diff -Nru firefox-3.6.3+nobinonly/debian/changelog firefox-3.6.4+build1+nobinonly/debian/changelog --- firefox-3.6.3+nobinonly/debian/changelog 2010-04-21 01:12:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/debian/changelog 2010-04-21 01:12:45.000000000 +0100 @@ -1,13 +1,40 @@ -firefox (3.6.3+nobinonly-0ubuntu3) lucid; urgency=low +firefox (3.6.4+build1+nobinonly-0ubuntu1~armel1) lucid; urgency=low + + * New upstream release v3.6.4 (FIREFOX_3_6_4_BUILD1) + - fix LP: #469752 - KDE/Gnome startup notification not disappearing + when app window is up + + [ Micah Gersten ] + * Rebase patch after upstream landing of Lorentz branch + - update debian/patches/bz460917_att350845_reload_new_plugins.patch + * Drop patch after upstream landing of (bmo: 544481) aka + Build fails on Ubuntu Lucid Lynx using 'dash' shell + - drop debian/patches/fix-build-glitch.patch + - update debian/patches/series [ Jamie Strandboge ] - * AppArmor: add read access to /etc/xul-ext/**, now needed by adblock + * AppArmor: + - add read access to /etc/xul-ext/**, now needed by adblock [ Chris Coulson ] * Create checksums for NSS libraries to make FIPS mode work (LP: #559881) - update debian/rules + * Build with --enable-ipc on amd64, i386 and armel. These are the only + architectures where OOPP is supported. Build with --disable-ipc on all + other architectures + - update debian/rules + * Fix LP: #513887 - Install the plugin-container binary for OOPP support + when building with --enable-ipc + - update debian/rules + + [ Alexander Sack ] + * fix LP: #443147 - Firefox on ARM inappropriately adds scroll bars to many + frames and images; this is a workaround that forces -O2 for the whole + mozilla tree build on armel; the scroll bar only manifests on lucid toolchain + when using the upstream optimization flag mix + - update debian/rules - -- Chris Coulson Tue, 13 Apr 2010 22:20:28 +0100 + -- Chris Coulson Fri, 16 Apr 2010 23:08:25 +0100 firefox (3.6.3+nobinonly-0ubuntu2) lucid; urgency=low diff -Nru firefox-3.6.3+nobinonly/debian/patches/bz460917_att350845_reload_new_plugins.patch firefox-3.6.4+build1+nobinonly/debian/patches/bz460917_att350845_reload_new_plugins.patch --- firefox-3.6.3+nobinonly/debian/patches/bz460917_att350845_reload_new_plugins.patch 2010-04-21 01:12:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/debian/patches/bz460917_att350845_reload_new_plugins.patch 2010-04-21 01:12:45.000000000 +0100 @@ -1,91 +1,89 @@ --- - browser/base/content/browser.js | 21 +++++++++++++++++++++ - 1 file changed, 21 insertions(+) + browser/base/content/browser.js | 22 ++++++++++++++++++++++ + 1 file changed, 22 insertions(+) Index: mozilla/browser/base/content/browser.js =================================================================== --- mozilla.orig/browser/base/content/browser.js +++ mozilla/browser/base/content/browser.js -@@ -5900,22 +5900,30 @@ - } +@@ -6003,19 +6003,28 @@ - missingPluginInstaller.prototype.installSinglePlugin = function(aEvent){ - var missingPluginsArray = {}; + // Callback for user clicking on a missing (unsupported) plugin. + installSinglePlugin: function (aEvent) { + var missingPluginsArray = {}; - var pluginInfo = getPluginInfo(aEvent.target); - missingPluginsArray[pluginInfo.mimetype] = pluginInfo; + var pluginInfo = getPluginInfo(aEvent.target); + missingPluginsArray[pluginInfo.mimetype] = pluginInfo; -+ gBrowser.selectedBrowser.addEventListener("NewPluginInstalled", -+ gMissingPluginInstaller.refreshBrowser, -+ false); -+ - if (missingPluginsArray) { - window.openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul", - "PFSWindow", "chrome,centerscreen,resizable=yes", - {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser}); - } - -+ gBrowser.selectedBrowser.removeEventListener("NewPluginInstalled", -+ gMissingPluginInstaller.refreshBrowser, -+ false); -+ - aEvent.stopPropagation(); - } - - missingPluginInstaller.prototype.managePlugins = function(aEvent){ - BrowserOpenAddonsMgr("plugins"); - aEvent.stopPropagation(); - } - -@@ -5981,21 +5989,27 @@ - var url = formatter.formatURLPref("plugins.update.url"); - gBrowser.loadOneTab(url, {inBackground: false}); - return true; - } - - function showPluginsMissing() { - // get the urls of missing plugins - var missingPluginsArray = gBrowser.selectedBrowser.missingPlugins; + gBrowser.selectedBrowser.addEventListener("NewPluginInstalled", + gMissingPluginInstaller.refreshBrowser, + false); - if (missingPluginsArray) { - window.openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul", - "PFSWindow", "chrome,centerscreen,resizable=yes", - {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser}); - } ++ + openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul", + "PFSWindow", "chrome,centerscreen,resizable=yes", + {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser}); ++ + gBrowser.selectedBrowser.removeEventListener("NewPluginInstalled", + gMissingPluginInstaller.refreshBrowser, + false); - } ++ + }, - if (aEvent.type == "PluginBlocklisted") { - if (blockedNotification || missingNotification) - return; - - let iconURL = "chrome://mozapps/skin/plugins/pluginBlocked-16.png"; - let messageString = gNavigatorBundle.getString("blockedpluginsMessage.title"); -@@ -6074,16 +6088,23 @@ - - // clear the plugin list, now that at least one plugin has been installed - browser.missingPlugins = null; - if (notification) { - // reset UI - notificationBox.removeNotification(notification); - } - // reload the browser to make the new plugin show. + // Callback for user clicking on a disabled plugin + managePlugins: function (aEvent) { + BrowserOpenAddonsMgr("plugins"); + }, + + // Callback for user clicking "submit a report" link +@@ -6101,21 +6110,27 @@ + var url = formatter.formatURLPref("plugins.update.url"); + gBrowser.loadOneTab(url, {inBackground: false}); + return true; + } + + function showPluginsMissing() { + // get the urls of missing plugins + var missingPluginsArray = gBrowser.selectedBrowser.missingPlugins; ++ gBrowser.selectedBrowser.addEventListener("NewPluginInstalled", ++ gMissingPluginInstaller.refreshBrowser, ++ false); + if (missingPluginsArray) { + window.openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul", + "PFSWindow", "chrome,centerscreen,resizable=yes", + {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser}); + } ++ gBrowser.selectedBrowser.removeEventListener("NewPluginInstalled", ++ gMissingPluginInstaller.refreshBrowser, ++ false); + } + + if (aEvent.type == "PluginBlocklisted") { + if (blockedNotification || missingNotification) + return; + + let iconURL = "chrome://mozapps/skin/plugins/pluginBlocked-16.png"; + let messageString = gNavigatorBundle.getString("blockedpluginsMessage.title"); +@@ -6432,16 +6447,23 @@ + + // clear the plugin list, now that at least one plugin has been installed + browser.missingPlugins = null; + if (notification) { + // reset UI + notificationBox.removeNotification(notification); + } + // reload the browser to make the new plugin show. + -+ // reload plugins -+ var pm = Components.classes["@mozilla.org/plugin/manager;1"] -+ .getService(Components.interfaces.nsIPluginManager); -+ pm.reloadPlugins(false); ++ // reload plugins ++ var pm = Components.classes["@mozilla.org/plugin/manager;1"] ++ .getService(Components.interfaces.nsIPluginManager); ++ pm.reloadPlugins(false); + -+ // ... and reload the browser to activate new plugins available - browser.reload(); - } - - var gMissingPluginInstaller = new missingPluginInstaller(); ++ // ... and reload the browser to activate new plugins available + browser.reload(); + } + }; function convertFromUnicode(charset, str) { try { + var unicodeConverter = Components diff -Nru firefox-3.6.3+nobinonly/debian/patches/fix-build-glitch.patch firefox-3.6.4+build1+nobinonly/debian/patches/fix-build-glitch.patch --- firefox-3.6.3+nobinonly/debian/patches/fix-build-glitch.patch 2010-04-21 01:12:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/debian/patches/fix-build-glitch.patch 1970-01-01 01:00:00.000000000 +0100 @@ -1,15 +0,0 @@ ---- mozilla/modules/libpr0n/build/Makefile.in~ 2010-01-14 10:21:02.274721280 -0800 -+++ mozilla/modules/libpr0n/build/Makefile.in 2010-01-14 10:21:39.793471208 -0800 -@@ -104,10 +104,8 @@ GARBAGE += _img_list nsImgBuildDefines.h - - export:: - @{ \ -- $(foreach d,$(filter-out icon,$(MOZ_IMG_DECODERS)), \ -- echo "#define IMG_BUILD_DECODER_${d}";) \ -- $(foreach d,$(MOZ_IMG_ENCODERS), \ -- echo "#define IMG_BUILD_ENCODER_${d}";) \ -+ $(foreach d,$(filter-out icon,$(MOZ_IMG_DECODERS)), echo "#define IMG_BUILD_DECODER_${d}";) \ -+ $(foreach d,$(MOZ_IMG_ENCODERS), echo "#define IMG_BUILD_ENCODER_${d}";) \ - } > nsImgBuildDefines.tmp - @if `cmp -s nsImgBuildDefines.h nsImgBuildDefines.tmp`; then \ - rm -f nsImgBuildDefines.tmp; \ diff -Nru firefox-3.6.3+nobinonly/debian/patches/series firefox-3.6.4+build1+nobinonly/debian/patches/series --- firefox-3.6.3+nobinonly/debian/patches/series 2010-04-21 01:12:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/debian/patches/series 2010-04-21 01:12:45.000000000 +0100 @@ -9,7 +9,6 @@ bz515232_att399338_distro_locale_searchplugins.patch bzXXX_libxul_sdk_nspr.patch bz532198_lp488354_ns_invokebyindex_not_thumb2_safe.patch -fix-build-glitch.patch abrowser_run_mozilla.patch add_syspref_dir.patch bz534663_attXXX_normalize_distribution_searchplugins.patch diff -Nru firefox-3.6.3+nobinonly/debian/rules firefox-3.6.4+build1+nobinonly/debian/rules --- firefox-3.6.3+nobinonly/debian/rules 2010-04-21 01:12:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/debian/rules 2010-04-21 01:12:45.000000000 +0100 @@ -80,10 +80,32 @@ export DEB_BUILD_HARDENING=1 -ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) +ifeq (,$(findstring noopt,$(DEB_BUILD_OPTIONS))) +ifneq (armel, $(DEB_BUILD_ARCH)) + EXTRA_SYSTEM_CONFIGURE_FLAGS += --enable-optimize +else + EXTRA_SYSTEM_CONFIGURE_FLAGS += --enable-optimize=-O2 +endif +else EXTRA_SYSTEM_CONFIGURE_FLAGS += --disable-optimize endif +# enable IPC only on i386, amd64 and armel +ENABLE_IPC ?= 1 +ifeq (,$(findstring i386,$(DEB_BUILD_ARCH))) +ifeq (,$(findstring amd64,$(DEB_BUILD_ARCH))) +ifeq (,$(findstring armel,$(DEB_BUILD_ARCH))) + ENABLE_IPC=0 +endif +endif +endif + +ifeq (1,$(ENABLE_IPC)) + EXTRA_SYSTEM_CONFIGURE_FLAGS += --enable-ipc +else + EXTRA_SYSTEM_CONFIGURE_FLAGS += --disable-ipc +endif + # disable jit on sparc until bmo 502369 is fixable ifneq (,$(findstring sparc,$(DEB_BUILD_ARCH))) EXTRA_SYSTEM_CONFIGURE_FLAGS += --disable-jit @@ -92,8 +114,14 @@ $(info WANT_UNIT_TESTS=$(WANT_UNIT_TESTS)) ifeq (1,$(WANT_UNIT_TESTS)) EXTRA_SYSTEM_CONFIGURE_FLAGS += --enable-tests --enable-mochitest +# Uncomment when the IPC test-suite works +#ifeq (1,$(ENABLE_IPC)) +# EXTRA_SYSTEM_CONFIGURE_FLAGS += --enable-ipdl-tests +#else +# EXTRA_SYSTEM_CONFIGURE_FLAGS += --disable-ipdl-tests +#endif else - EXTRA_SYSTEM_CONFIGURE_FLAGS += --disable-tests --disable-mochitest + EXTRA_SYSTEM_CONFIGURE_FLAGS += --disable-tests --disable-mochitest --disable-ipdl-tests endif ifneq (1, $(DEB_MIN_SYSDEPS)) @@ -175,7 +203,6 @@ --enable-canvas \ --enable-default-toolkit=cairo-gtk2 \ --enable-gnomevfs \ - --enable-optimize \ --enable-pango \ --enable-postscript \ --enable-svg \ @@ -302,6 +329,10 @@ dh_install -p$(DEBIAN_NAME) \ debian/tmp/usr/lib/firefox-*/defaults/preferences endif +ifeq (1,$(ENABLE_IPC)) + dh_install -p$(DEBIAN_NAME) \ + debian/tmp/usr/lib/firefox-*/plugin-container +endif touch debian/$(DEBIAN_NAME)/$(DEBIAN_FF3_DIR)/.autoreg if [ -d debian/$(DEBIAN_NAME)/usr/lib/xulrunner-addons/extensions ] ; then \ touch $(foreach dir,$(wildcard debian/$(DEBIAN_NAME)/usr/lib/xulrunner-addons/extensions/*),$(dir)/chrome.manifest) ; \ diff -Nru firefox-3.6.3+nobinonly/mozilla/accessible/src/base/nsDocAccessible.h firefox-3.6.4+build1+nobinonly/mozilla/accessible/src/base/nsDocAccessible.h --- firefox-3.6.3+nobinonly/mozilla/accessible/src/base/nsDocAccessible.h 2010-04-02 16:57:45.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/accessible/src/base/nsDocAccessible.h 2010-04-16 17:31:29.000000000 +0100 @@ -201,7 +201,7 @@ /** * For any accessibles in this subtree, invalidate their knowledge of - * their children. Only weak refrences are destroyed, not accessibles. + * their children. Only weak references are destroyed, not accessibles. * @param aStartNode The root of the subrtee to invalidate accessible child refs in */ void InvalidateChildrenInSubtree(nsIDOMNode *aStartNode); diff -Nru firefox-3.6.3+nobinonly/mozilla/accessible/src/html/nsHTMLTableAccessible.cpp firefox-3.6.4+build1+nobinonly/mozilla/accessible/src/html/nsHTMLTableAccessible.cpp --- firefox-3.6.3+nobinonly/mozilla/accessible/src/html/nsHTMLTableAccessible.cpp 2010-04-02 16:57:45.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/accessible/src/html/nsHTMLTableAccessible.cpp 2010-04-16 17:31:29.000000000 +0100 @@ -433,6 +433,10 @@ } } + // No elements in siblings what means the table has one column only. Therefore + // it should be column header. + *aRole = nsIAccessibleRole::ROLE_COLUMNHEADER; + return NS_OK; } diff -Nru firefox-3.6.3+nobinonly/mozilla/accessible/tests/mochitest/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/accessible/tests/mochitest/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/accessible/tests/mochitest/Makefile.in 2010-04-02 16:57:45.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/accessible/tests/mochitest/Makefile.in 2010-04-16 17:31:29.000000000 +0100 @@ -42,6 +42,8 @@ VPATH = @srcdir@ relativesrcdir = accessible +DIRS = tree + include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk diff -Nru firefox-3.6.3+nobinonly/mozilla/accessible/tests/mochitest/test_table_headers.html firefox-3.6.4+build1+nobinonly/mozilla/accessible/tests/mochitest/test_table_headers.html --- firefox-3.6.3+nobinonly/mozilla/accessible/tests/mochitest/test_table_headers.html 2010-04-02 16:57:45.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/accessible/tests/mochitest/test_table_headers.html 2010-04-16 17:31:29.000000000 +0100 @@ -119,6 +119,32 @@ testHeaderCells(headerInfoMap); + ////////////////////////////////////////////////////////////////////////// + // table consisted of one column + + headerInfoMap = [ + { + cell: "table4_cell", + rowHeaderCells: [], + columnHeaderCells: [ "table4_ch" ] + } + ]; + + testHeaderCells(headerInfoMap); + + ////////////////////////////////////////////////////////////////////////// + // table consisted of one row + + headerInfoMap = [ + { + cell: "table5_cell", + rowHeaderCells: [ "table5_rh" ], + columnHeaderCells: [ ] + } + ]; + + testHeaderCells(headerInfoMap); + SimpleTest.finish(); } @@ -200,5 +226,25 @@ col2 + + + + + + + + + + + + +
colheader
bla
+ + + + + + +
rowheadercell
diff -Nru firefox-3.6.3+nobinonly/mozilla/accessible/tests/mochitest/tree/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/accessible/tests/mochitest/tree/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/accessible/tests/mochitest/tree/Makefile.in 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/accessible/tests/mochitest/tree/Makefile.in 2010-04-16 17:31:29.000000000 +0100 @@ -0,0 +1,53 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (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.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Mozilla Corporation. +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Alexander Surkov (original author) +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ +relativesrcdir = accessible/tree + +include $(DEPTH)/config/autoconf.mk +include $(topsrcdir)/config/rules.mk + +_TEST_FILES =\ + test_table.html \ + $(NULL) + +libs:: $(_TEST_FILES) + $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir) diff -Nru firefox-3.6.3+nobinonly/mozilla/accessible/tests/mochitest/tree/test_table.html firefox-3.6.4+build1+nobinonly/mozilla/accessible/tests/mochitest/tree/test_table.html --- firefox-3.6.3+nobinonly/mozilla/accessible/tests/mochitest/tree/test_table.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/accessible/tests/mochitest/tree/test_table.html 2010-04-16 17:31:29.000000000 +0100 @@ -0,0 +1,195 @@ + + + + HTML table tests + + + + + + + + + + + + + + Mozilla Bug 529621 + +

+ +
+  
+ + + + + + + + + + + + + + + + + + + + + + +
col1col2
caption
cell1cell2
cell3cell4
caption2
cell5cell6
+ + + + + + + + + + + + +
colheader
bla
+ + + + + + +
rowheadercell
+ + diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/app/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/browser/app/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/browser/app/Makefile.in 2010-04-02 16:57:45.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/app/Makefile.in 2010-04-16 17:31:29.000000000 +0100 @@ -55,7 +55,11 @@ # hardcode en-US for the moment AB_CD = en-US -DEFINES += -DAB_CD=$(AB_CD) +DEFINES += \ + -DAB_CD=$(AB_CD) \ + -DDLL_PREFIX=$(DLL_PREFIX) \ + -DDLL_SUFFIX=$(DLL_SUFFIX) \ + $(NULL) APP_VERSION = $(shell cat $(srcdir)/../config/version.txt) DEFINES += -DAPP_VERSION="$(APP_VERSION)" diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/app/profile/firefox.js firefox-3.6.4+build1+nobinonly/mozilla/browser/app/profile/firefox.js --- firefox-3.6.3+nobinonly/mozilla/browser/app/profile/firefox.js 2010-04-02 16:57:45.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/app/profile/firefox.js 2010-04-16 17:31:29.000000000 +0100 @@ -909,6 +909,20 @@ pref("toolbar.customization.usesheet", false); #endif +// Whitelist the test plugin, Flash, Silverlight, and QuickTime + +pref("dom.ipc.plugins.enabled.@DLL_PREFIX@nptest@DLL_SUFFIX@", true); +#ifdef XP_WIN +pref("dom.ipc.plugins.enabled.npswf32.dll", true); +pref("dom.ipc.plugins.enabled.npctrl.dll", true); +pref("dom.ipc.plugins.enabled.npqtplugin.dll", true); +#endif +#ifdef XP_UNIX +pref("dom.ipc.plugins.enabled.libflashplayer.so", true); +#endif + +pref("dom.ipc.plugins.enabled", false); + #ifdef XP_WIN #ifndef WINCE pref("browser.taskbar.previews.enable", false); diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/base/content/browser.js firefox-3.6.4+build1+nobinonly/mozilla/browser/base/content/browser.js --- firefox-3.6.3+nobinonly/mozilla/browser/base/content/browser.js 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/base/content/browser.js 2010-04-16 17:31:29.000000000 +0100 @@ -49,6 +49,7 @@ # Nils Maier # Rob Arnold # Dietrich Ayala +# Justin Dolske # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or @@ -134,7 +135,7 @@ __defineGetter__("gCrashReporter", function() { delete this.gCrashReporter; return this.gCrashReporter = Cc["@mozilla.org/xre/app-info;1"]. - getService(Ci.nsICrashReporter); + getService(Ci.nsICrashReporter_MOZILLA_1_9_2_BRANCH); }); #endif @@ -438,6 +439,8 @@ if (gPrivateBrowsingUI.privateBrowsingEnabled) blockedPopupAllowSite.setAttribute("disabled", "true"); + else + blockedPopupAllowSite.removeAttribute("disabled"); var item = aEvent.target.lastChild; while (item && item.getAttribute("observes") != "blockedPopupsSeparator") { @@ -1098,14 +1101,18 @@ } function prepareForStartup() { + var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService); + gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver.onUpdatePageReport, false); // Note: we need to listen to untrusted events, because the pluginfinder XBL // binding can't fire trusted ones (runs with page privileges). gBrowser.addEventListener("PluginNotFound", gMissingPluginInstaller.newMissingPlugin, true, true); + gBrowser.addEventListener("PluginCrashed", gMissingPluginInstaller.pluginInstanceCrashed, true, true); gBrowser.addEventListener("PluginBlocklisted", gMissingPluginInstaller.newMissingPlugin, true, true); gBrowser.addEventListener("PluginOutdated", gMissingPluginInstaller.newMissingPlugin, true, true); gBrowser.addEventListener("PluginDisabled", gMissingPluginInstaller.newDisabledPlugin, true, true); gBrowser.addEventListener("NewPluginInstalled", gMissingPluginInstaller.refreshBrowser, false); + os.addObserver(gMissingPluginInstaller, "plugin-crashed", false); gBrowser.addEventListener("NewTab", BrowserOpenTab, false); window.addEventListener("AppCommand", HandleAppCommandEvent, true); @@ -1150,7 +1157,6 @@ // progress notifications for back/forward button updating webNavigation.sessionHistory = Components.classes["@mozilla.org/browser/shistory;1"] .createInstance(Components.interfaces.nsISHistory); - var os = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService); os.addObserver(gBrowser.browsers[0], "browser:purge-session-history", false); // remove the disablehistory attribute so the browser cleans up, as @@ -1403,6 +1409,7 @@ .getService(Components.interfaces.nsIObserverService); os.removeObserver(gSessionHistoryObserver, "browser:purge-session-history"); os.removeObserver(gXPInstallObserver, "xpinstall-install-blocked"); + os.removeObserver(gMissingPluginInstaller, "plugin-crashed"); try { gBrowser.removeProgressListener(window.XULBrowserWindow); @@ -4368,7 +4375,8 @@ #ifdef MOZ_CRASHREPORTER if (aRequest instanceof Ci.nsIChannel && aStateFlags & Ci.nsIWebProgressListener.STATE_START && - aStateFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT) { + aStateFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT && + gCrashReporter.enabled) { gCrashReporter.annotateCrashReport("URL", aRequest.URI.spec); } #endif @@ -4449,7 +4457,7 @@ throw Components.results.NS_NOINTERFACE; }, - openURI : function(aURI, aOpener, aWhere, aContext) + openURI : function (aURI, aOpener, aWhere, aContext) { var newWindow = null; var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL); @@ -4520,7 +4528,7 @@ return newWindow; }, - isTabContentWindow : function(aWindow) + isTabContentWindow : function (aWindow) { return gBrowser.browsers.some(function (browser) browser.contentWindow == aWindow); } @@ -5937,194 +5945,502 @@ return {mimetype: tagMimetype, pluginsPage: pluginsPage}; } -function missingPluginInstaller(){ -} +var gMissingPluginInstaller = { -missingPluginInstaller.prototype.installSinglePlugin = function(aEvent){ - var missingPluginsArray = {}; + get CrashSubmit() { + delete this.CrashSubmit; + Cu.import("resource://gre/modules/CrashSubmit.jsm", this); + return this.CrashSubmit; + }, + + get crashReportHelpURL() { + delete this.crashReportHelpURL; + let url = formatURL("app.support.baseURL", true); + url += "plugin-crashed"; + this.crashReportHelpURL = url; + return this.crashReportHelpURL; + }, + + // Map the plugin's name to a filtered version more suitable for user UI. + makeNicePluginName : function (aName, aFilename) { + if (aName == "Shockwave Flash") + return "Adobe Flash"; + + // Clean up the plugin name by stripping off any trailing version numbers + // or "plugin". EG, "Foo Bar Plugin 1.23_02" --> "Foo Bar" + let newName = aName.replace(/\bplug-?in\b/i, "").replace(/[\s\d\.\-\_\(\)]+$/, ""); + return newName; + }, + + addLinkClickCallback: function (linkNode, callbackName /*callbackArgs...*/) { + // XXX just doing (callback)(arg) was giving a same-origin error. bug? + let self = this; + let callbackArgs = Array.prototype.slice.call(arguments).slice(2); + linkNode.addEventListener("click", + function(evt) { + if (!evt.isTrusted) + return; + evt.preventDefault(); + if (callbackArgs.length == 0) + callbackArgs = [ evt ]; + (self[callbackName]).apply(self, callbackArgs); + }, + true); + + linkNode.addEventListener("keydown", + function(evt) { + if (!evt.isTrusted) + return; + if (evt.keyCode == evt.DOM_VK_RETURN) { + evt.preventDefault(); + if (callbackArgs.length == 0) + callbackArgs = [ evt ]; + evt.preventDefault(); + (self[callbackName]).apply(self, callbackArgs); + } + }, + true); + }, + + // Callback for user clicking on a missing (unsupported) plugin. + installSinglePlugin: function (aEvent) { + var missingPluginsArray = {}; + + var pluginInfo = getPluginInfo(aEvent.target); + missingPluginsArray[pluginInfo.mimetype] = pluginInfo; + + openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul", + "PFSWindow", "chrome,centerscreen,resizable=yes", + {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser}); + }, + + // Callback for user clicking on a disabled plugin + managePlugins: function (aEvent) { + BrowserOpenAddonsMgr("plugins"); + }, + + // Callback for user clicking "submit a report" link + submitReport : function(pluginDumpID, browserDumpID) { + // The crash reporter wants a DOM element it can append an IFRAME to, + // which it uses to submit a form. Let's just give it gBrowser. + this.CrashSubmit.submit(pluginDumpID, gBrowser, null, null); + if (browserDumpID) + this.CrashSubmit.submit(browserDumpID, gBrowser, null, null); + }, + + // Callback for user clicking a "reload page" link + reloadPage: function (browser) { + browser.reload(); + }, + + // Callback for user clicking the help icon + openHelpPage: function () { + openHelpLink("plugin-crashed", false); + }, + + + + // event listener for missing/blocklisted/outdated plugins. + newMissingPlugin: function (aEvent) { + // Since we are expecting also untrusted events, make sure + // that the target is a plugin + if (!(aEvent.target instanceof Ci.nsIObjectLoadingContent)) + return; - var pluginInfo = getPluginInfo(aEvent.target); - missingPluginsArray[pluginInfo.mimetype] = pluginInfo; + // For broken non-object plugin tags, register a click handler so + // that the user can click the plugin replacement to get the new + // plugin. Object tags can, and often do, deal with that themselves, + // so don't stomp on the page developers toes. + + if (aEvent.type != "PluginBlocklisted" && + aEvent.type != "PluginOutdated" && + !(aEvent.target instanceof HTMLObjectElement)) { + gMissingPluginInstaller.addLinkClickCallback(aEvent.target, "installSinglePlugin"); + } - if (missingPluginsArray) { - window.openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul", - "PFSWindow", "chrome,centerscreen,resizable=yes", - {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser}); - } + let hideBarPrefName = aEvent.type == "PluginOutdated" ? + "plugins.hide_infobar_for_outdated_plugin" : + "plugins.hide_infobar_for_missing_plugin"; + try { + if (gPrefService.getBoolPref(hideBarPrefName)) + return; + } catch (ex) {} // if the pref is missing, treat it as false, which shows the infobar - aEvent.stopPropagation(); -} + var browser = gBrowser.getBrowserForDocument(aEvent.target.ownerDocument + .defaultView.top.document); + if (!browser.missingPlugins) + browser.missingPlugins = {}; -missingPluginInstaller.prototype.managePlugins = function(aEvent){ - BrowserOpenAddonsMgr("plugins"); - aEvent.stopPropagation(); -} + var pluginInfo = getPluginInfo(aEvent.target); -missingPluginInstaller.prototype.newMissingPlugin = function(aEvent){ - // Since we are expecting also untrusted events, make sure - // that the target is a plugin - if (!(aEvent.target instanceof Components.interfaces.nsIObjectLoadingContent)) - return; + browser.missingPlugins[pluginInfo.mimetype] = pluginInfo; - // For broken non-object plugin tags, register a click handler so - // that the user can click the plugin replacement to get the new - // plugin. Object tags can, and often do, deal with that themselves, - // so don't stomp on the page developers toes. - - if (aEvent.type != "PluginBlocklisted" && - aEvent.type != "PluginOutdated" && - !(aEvent.target instanceof HTMLObjectElement)) { - aEvent.target.addEventListener("click", - gMissingPluginInstaller.installSinglePlugin, - true); - } - - let hideBarPrefName = aEvent.type == "PluginOutdated" ? - "plugins.hide_infobar_for_outdated_plugin" : - "plugins.hide_infobar_for_missing_plugin"; - try { - if (gPrefService.getBoolPref(hideBarPrefName)) + var notificationBox = gBrowser.getNotificationBox(browser); + + // Should only display one of these warnings per page. + // In order of priority, they are: outdated > missing > blocklisted + + // If there is already an outdated plugin notification then do nothing + if (notificationBox.getNotificationWithValue("outdated-plugins")) return; - } catch (ex) {} // if the pref is missing, treat it as false, which shows the infobar + var blockedNotification = notificationBox.getNotificationWithValue("blocked-plugins"); + var missingNotification = notificationBox.getNotificationWithValue("missing-plugins"); + var priority = notificationBox.PRIORITY_WARNING_MEDIUM; + + function showBlocklistInfo() { + var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. + getService(Ci.nsIURLFormatter); + var url = formatter.formatURLPref("extensions.blocklist.detailsURL"); + gBrowser.loadOneTab(url, {inBackground: false}); + return true; + } + + function showOutdatedPluginsInfo() { + gPrefService.setBoolPref("plugins.update.notifyUser", false); + var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. + getService(Ci.nsIURLFormatter); + var url = formatter.formatURLPref("plugins.update.url"); + gBrowser.loadOneTab(url, {inBackground: false}); + return true; + } + + function showPluginsMissing() { + // get the urls of missing plugins + var missingPluginsArray = gBrowser.selectedBrowser.missingPlugins; + if (missingPluginsArray) { + window.openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul", + "PFSWindow", "chrome,centerscreen,resizable=yes", + {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser}); + } + } - var browser = gBrowser.getBrowserForDocument(aEvent.target.ownerDocument - .defaultView.top.document); - if (!browser.missingPlugins) - browser.missingPlugins = {}; + if (aEvent.type == "PluginBlocklisted") { + if (blockedNotification || missingNotification) + return; - var pluginInfo = getPluginInfo(aEvent.target); + let iconURL = "chrome://mozapps/skin/plugins/pluginBlocked-16.png"; + let messageString = gNavigatorBundle.getString("blockedpluginsMessage.title"); + let buttons = [{ + label: gNavigatorBundle.getString("blockedpluginsMessage.infoButton.label"), + accessKey: gNavigatorBundle.getString("blockedpluginsMessage.infoButton.accesskey"), + popup: null, + callback: showBlocklistInfo + }, { + label: gNavigatorBundle.getString("blockedpluginsMessage.searchButton.label"), + accessKey: gNavigatorBundle.getString("blockedpluginsMessage.searchButton.accesskey"), + popup: null, + callback: showOutdatedPluginsInfo + }]; - browser.missingPlugins[pluginInfo.mimetype] = pluginInfo; + notificationBox.appendNotification(messageString, "blocked-plugins", + iconURL, priority, buttons); + } + else if (aEvent.type == "PluginOutdated") { + // Cancel any notification about blocklisting/missing plugins + if (blockedNotification) + blockedNotification.close(); + if (missingNotification) + missingNotification.close(); + + let iconURL = "chrome://mozapps/skin/plugins/pluginOutdated-16.png"; + let messageString = gNavigatorBundle.getString("outdatedpluginsMessage.title"); + let buttons = [{ + label: gNavigatorBundle.getString("outdatedpluginsMessage.updateButton.label"), + accessKey: gNavigatorBundle.getString("outdatedpluginsMessage.updateButton.accesskey"), + popup: null, + callback: showOutdatedPluginsInfo + }]; - var notificationBox = gBrowser.getNotificationBox(browser); + notificationBox.appendNotification(messageString, "outdated-plugins", + iconURL, priority, buttons); + } + else if (aEvent.type == "PluginNotFound") { + if (missingNotification) + return; - // Should only display one of these warnings per page. - // In order of priority, they are: outdated > missing > blocklisted + // Cancel any notification about blocklisting plugins + if (blockedNotification) + blockedNotification.close(); + + let iconURL = "chrome://mozapps/skin/plugins/pluginGeneric-16.png"; + let messageString = gNavigatorBundle.getString("missingpluginsMessage.title"); + let buttons = [{ + label: gNavigatorBundle.getString("missingpluginsMessage.button.label"), + accessKey: gNavigatorBundle.getString("missingpluginsMessage.button.accesskey"), + popup: null, + callback: showPluginsMissing + }]; - // If there is already an outdated plugin notification then do nothing - if (notificationBox.getNotificationWithValue("outdated-plugins")) - return; - var blockedNotification = notificationBox.getNotificationWithValue("blocked-plugins"); - var missingNotification = notificationBox.getNotificationWithValue("missing-plugins"); - var priority = notificationBox.PRIORITY_WARNING_MEDIUM; - - function showBlocklistInfo() { - var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. - getService(Ci.nsIURLFormatter); - var url = formatter.formatURLPref("extensions.blocklist.detailsURL"); - gBrowser.loadOneTab(url, {inBackground: false}); - return true; - } - - function showOutdatedPluginsInfo() { - gPrefService.setBoolPref("plugins.update.notifyUser", false); - var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]. - getService(Ci.nsIURLFormatter); - var url = formatter.formatURLPref("plugins.update.url"); - gBrowser.loadOneTab(url, {inBackground: false}); - return true; - } - - function showPluginsMissing() { - // get the urls of missing plugins - var missingPluginsArray = gBrowser.selectedBrowser.missingPlugins; - if (missingPluginsArray) { - window.openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul", - "PFSWindow", "chrome,centerscreen,resizable=yes", - {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser}); + notificationBox.appendNotification(messageString, "missing-plugins", + iconURL, priority, buttons); } - } + }, - if (aEvent.type == "PluginBlocklisted") { - if (blockedNotification || missingNotification) - return; - - let iconURL = "chrome://mozapps/skin/plugins/pluginBlocked-16.png"; - let messageString = gNavigatorBundle.getString("blockedpluginsMessage.title"); - let buttons = [{ - label: gNavigatorBundle.getString("blockedpluginsMessage.infoButton.label"), - accessKey: gNavigatorBundle.getString("blockedpluginsMessage.infoButton.accesskey"), - popup: null, - callback: showBlocklistInfo - }, { - label: gNavigatorBundle.getString("blockedpluginsMessage.searchButton.label"), - accessKey: gNavigatorBundle.getString("blockedpluginsMessage.searchButton.accesskey"), - popup: null, - callback: showOutdatedPluginsInfo - }]; + newDisabledPlugin: function (aEvent) { + // Since we are expecting also untrusted events, make sure + // that the target is a plugin + if (!(aEvent.target instanceof Components.interfaces.nsIObjectLoadingContent)) + return; - notificationBox.appendNotification(messageString, "blocked-plugins", - iconURL, priority, buttons); - } - else if (aEvent.type == "PluginOutdated") { - // Cancel any notification about blocklisting/missing plugins - if (blockedNotification) - blockedNotification.close(); - if (missingNotification) - missingNotification.close(); - - let iconURL = "chrome://mozapps/skin/plugins/pluginOutdated-16.png"; - let messageString = gNavigatorBundle.getString("outdatedpluginsMessage.title"); - let buttons = [{ - label: gNavigatorBundle.getString("outdatedpluginsMessage.updateButton.label"), - accessKey: gNavigatorBundle.getString("outdatedpluginsMessage.updateButton.accesskey"), - popup: null, - callback: showOutdatedPluginsInfo - }]; + gMissingPluginInstaller.addLinkClickCallback(aEvent.target, "managePlugins"); + }, - notificationBox.appendNotification(messageString, "outdated-plugins", - iconURL, priority, buttons); - } - else if (aEvent.type == "PluginNotFound") { - if (missingNotification) + // Crashed-plugin observer. Notified once per plugin crash, before events + // are dispatched to individual plugin instances. + observe: function(subject, topic, data) { + if (topic != "plugin-crashed") return; - // Cancel any notification about blocklisting plugins - if (blockedNotification) - blockedNotification.close(); - - let iconURL = "chrome://mozapps/skin/plugins/pluginGeneric-16.png"; - let messageString = gNavigatorBundle.getString("missingpluginsMessage.title"); - let buttons = [{ - label: gNavigatorBundle.getString("missingpluginsMessage.button.label"), - accessKey: gNavigatorBundle.getString("missingpluginsMessage.button.accesskey"), - popup: null, - callback: showPluginsMissing - }]; - - notificationBox.appendNotification(messageString, "missing-plugins", - iconURL, priority, buttons); - } -} + let propertyBag = subject; + if (!(propertyBag instanceof Ci.nsIPropertyBag2) || + !(propertyBag instanceof Ci.nsIWritablePropertyBag2)) + return; -missingPluginInstaller.prototype.newDisabledPlugin = function(aEvent){ - // Since we are expecting also untrusted events, make sure - // that the target is a plugin - if (!(aEvent.target instanceof Components.interfaces.nsIObjectLoadingContent)) - return; +#ifdef MOZ_CRASHREPORTER + let pluginDumpID = propertyBag.getPropertyAsAString("pluginDumpID"); + let browserDumpID= propertyBag.getPropertyAsAString("browserDumpID"); + let shouldSubmit = gCrashReporter.submitReports; + let doPrompt = true; // XXX followup to get via gCrashReporter + + // Submit automatically when appropriate. + if (pluginDumpID && shouldSubmit && !doPrompt) { + this.submitReport(pluginDumpID, browserDumpID); + // Submission is async, so we can't easily show failure UI. + propertyBag.setPropertyAsBool("submittedCrashReport", true); + } +#endif + }, - aEvent.target.addEventListener("click", - gMissingPluginInstaller.managePlugins, - true); -} + // Crashed-plugin event listener. Called for every instance of a + // plugin in content. + pluginInstanceCrashed: function (aEvent) { + let self = gMissingPluginInstaller; -missingPluginInstaller.prototype.refreshBrowser = function(aEvent) { - // browser elements are anonymous so we can't just use target. - var browser = aEvent.originalTarget; - var notificationBox = gBrowser.getNotificationBox(browser); - var notification = notificationBox.getNotificationWithValue("missing-plugins"); + // Evil content could fire a fake event at us, ignore them. + if (!aEvent.isTrusted) + return; - // clear the plugin list, now that at least one plugin has been installed - browser.missingPlugins = null; - if (notification) { - // reset UI - notificationBox.removeNotification(notification); - } - // reload the browser to make the new plugin show. - browser.reload(); -} + // Ensure the plugin and event are of the right type. + let plugin = aEvent.target; + if (!(aEvent instanceof Ci.nsIDOMDataContainerEvent) || + !(plugin instanceof Ci.nsIObjectLoadingContent)) + return; + + let submittedReport = aEvent.getData("submittedCrashReport"); + let doPrompt = true; // XXX followup for .getData("doPrompt"); + let submitReports = true; // XXX followup for .getData("submitReports"); + let pluginName = aEvent.getData("pluginName"); + let pluginFilename = aEvent.getData("pluginFilename"); + let pluginDumpID = aEvent.getData("pluginDumpID"); + let browserDumpID = aEvent.getData("browserDumpID"); + + // Remap the plugin name to a more user-presentable form. + pluginName = self.makeNicePluginName(pluginName, pluginFilename); + + // Force a style flush, so that we ensure our binding is attached. + plugin.clientTop; + + let messageString; + try { + messageString = gNavigatorBundle.getFormattedString("crashedpluginsMessage.title", [pluginName]); + } catch (e) { + messageString = "The " + pluginName + " plugin has crashed."; + } + + // + // Configure the crashed-plugin placeholder. + // + let doc = plugin.ownerDocument; + let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox"); + + // The binding has role="link" here, since missing/disabled/blocked + // plugin UI has a onclick handler on the whole thing. This isn't needed + // for the plugin-crashed UI, because we use actual HTML links in the text. + overlay.removeAttribute("role"); + + let statusDiv = doc.getAnonymousElementByAttribute(plugin, "class", "submitStatus"); +#ifdef MOZ_CRASHREPORTER + let status; + + // Determine which message to show regarding crash reports. + if (submittedReport) { // submitReports && !doPrompt, handled in observer + status = "submitted"; + } + else if (!submitReports && !doPrompt) { + status = "noSubmit"; + } + else { // doPrompt + status = "please"; + // XXX can we make the link target actually be blank? + let pleaseLink = doc.getAnonymousElementByAttribute( + plugin, "class", "pleaseSubmitLink"); + self.addLinkClickCallback(pleaseLink, "submitReport", + pluginDumpID, browserDumpID); + } + + // If we don't have a minidumpID, we can't (or didn't) submit anything. + // This can happen if the plugin is killed from the task manager. + if (!pluginDumpID) { + status = "noReport"; + } + + statusDiv.setAttribute("status", status); + + let bottomLinks = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgBottomLinks"); + bottomLinks.style.display = "block"; + let helpIcon = doc.getAnonymousElementByAttribute(plugin, "class", "helpIcon"); + self.addLinkClickCallback(helpIcon, "openHelpPage"); + + // If we're showing the link to manually trigger report submission, we'll + // want to be able to update all the instances of the UI for this crash to + // show an updated message when a report is submitted. + if (doPrompt) { + let observer = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, + Ci.nsISupportsWeakReference]), + observe : function(subject, topic, data) { + let propertyBag = subject; + if (!(propertyBag instanceof Ci.nsIPropertyBag2)) + return; + // Ignore notifications for other crashes. + if (propertyBag.get("minidumpID") != pluginDumpID) + return; + statusDiv.setAttribute("status", data); + }, + + handleEvent : function(event) { + // Not expected to be called, just here for the closure. + } + } + + let obs = Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService); + // Use a weak reference, so we don't have to remove it... + obs.addObserver(observer, "crash-report-status", true); + // ...alas, now we need something to hold a strong reference to prevent + // it from being GC. But I don't want to manually manage the reference's + // lifetime (which should be no greater than the page). + // Clever solution? Use a closue with an event listener on the document. + // When the doc goes away, so do the listener references and the closure. + doc.addEventListener("mozCleverClosureHack", observer, false); + } +#endif + + let crashText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgCrashed"); + crashText.textContent = messageString; -var gMissingPluginInstaller = new missingPluginInstaller(); + let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document); + + let link = doc.getAnonymousElementByAttribute(plugin, "class", "reloadLink"); + self.addLinkClickCallback(link, "reloadPage", browser); + + let notificationBox = gBrowser.getNotificationBox(browser); + + // Is the 's size too small to hold what we want to show? + let pluginRect = plugin.getBoundingClientRect(); + // XXX bug 446693. The text-shadow on the submitted-report text at + // the bottom causes scrollHeight to be larger than it should be. + let isObjectTooSmall = (overlay.scrollWidth > pluginRect.width) || + (overlay.scrollHeight - 5 > pluginRect.height); + if (isObjectTooSmall) { + // Hide the overlay's contents. Use visibility style, so that it + // doesn't collapse down to 0x0. + overlay.style.visibility = "hidden"; + // If another plugin on the page was large enough to show our UI, we + // don't want to show a notification bar. + if (!doc.mozNoPluginCrashedNotification) + showNotificationBar(pluginDumpID, browserDumpID); + } else { + // If a previous plugin on the page was too small and resulted in + // adding a notification bar, then remove it because this plugin + // instance it big enough to serve as in-content notification. + hideNotificationBar(); + doc.mozNoPluginCrashedNotification = true; + } + + function hideNotificationBar() { + let notification = notificationBox.getNotificationWithValue("plugin-crashed"); + if (notification) + notificationBox.removeNotification(notification, true); + } + + function showNotificationBar(pluginDumpID, browserDumpID) { + // If there's already an existing notification bar, don't do anything. + let notification = notificationBox.getNotificationWithValue("plugin-crashed"); + if (notification) + return; + + // Configure the notification bar + let priority = notificationBox.PRIORITY_WARNING_MEDIUM; + let iconURL = "chrome://mozapps/skin/plugins/pluginGeneric-16.png"; + + let reloadLabel, reloadKey, submitLabel, submitKey; + try { + reloadLabel = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.label"); + reloadKey = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.accesskey"); + submitLabel = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.label"); + submitKey = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.accesskey"); + } catch (e) { + reloadLabel = "Reload page"; + reloadKey = "R"; + submitLabel = "Submit a crash report"; + submitKey = "S"; + } + + let buttons = [{ + label: reloadLabel, + accessKey: reloadKey, + popup: null, + callback: function() { browser.reload(); }, + }]; +#ifdef MOZ_CRASHREPORTER + let submitButton = { + label: submitLabel, + accessKey: submitKey, + popup: null, + callback: function() { gMissingPluginInstaller.submitReport(pluginDumpID, browserDumpID); }, + }; + if (pluginDumpID) + buttons.push(submitButton); +#endif + + let notification = notificationBox.appendNotification(messageString, "plugin-crashed", + iconURL, priority, buttons); + + // Add the "learn more" link. + let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + let link = notification.ownerDocument.createElementNS(XULNS, "label"); + link.className = "text-link"; + let learnMore; + try { + learnMore = gNavigatorBundle.getString("crashedpluginsMessage.learnMore"); + } catch (e) { + learnMore = "Learn More\u2026"; + } + link.setAttribute("value", learnMore); + link.href = gMissingPluginInstaller.crashReportHelpURL; + let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText"); + description.appendChild(link); + } + + }, + + refreshBrowser: function (aEvent) { + // browser elements are anonymous so we can't just use target. + var browser = aEvent.originalTarget; + var notificationBox = gBrowser.getNotificationBox(browser); + var notification = notificationBox.getNotificationWithValue("missing-plugins"); + + // clear the plugin list, now that at least one plugin has been installed + browser.missingPlugins = null; + if (notification) { + // reset UI + notificationBox.removeNotification(notification); + } + // reload the browser to make the new plugin show. + browser.reload(); + } +}; function convertFromUnicode(charset, str) { @@ -6136,7 +6452,7 @@ str = unicodeConverter.ConvertFromUnicode(str); return str + unicodeConverter.Finish(); } catch(ex) { - return null; + return null; } } @@ -7113,6 +7429,10 @@ } } + if (gURLBar) { + gURLBar.editor.transactionManager.clear(); + } + document.getElementById("menu_import").removeAttribute("disabled"); // Re-enable the Clear Recent History... menu item on exit of PB mode diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/base/content/openLocationLastURL.jsm firefox-3.6.4+build1+nobinonly/mozilla/browser/base/content/openLocationLastURL.jsm --- firefox-3.6.3+nobinonly/mozilla/browser/base/content/openLocationLastURL.jsm 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/base/content/openLocationLastURL.jsm 2010-04-16 17:31:29.000000000 +0100 @@ -53,13 +53,21 @@ throw Components.results.NS_NOINTERFACE; }, observe: function (aSubject, aTopic, aData) { - gOpenLocationLastURLData = ""; + switch (aTopic) { + case "private-browsing": + gOpenLocationLastURLData = ""; + break; + case "browser:purge-session-history": + gOpenLocationLastURL.reset(); + break; + } } }; -Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService) - .addObserver(observer, "private-browsing", true); +let os = Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); +os.addObserver(observer, "private-browsing", true); +os.addObserver(observer, "browser:purge-session-history", true); let gOpenLocationLastURLData = ""; let gOpenLocationLastURL = { diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/base/content/tabbrowser.xml firefox-3.6.4+build1+nobinonly/mozilla/browser/base/content/tabbrowser.xml --- firefox-3.6.3+nobinonly/mozilla/browser/base/content/tabbrowser.xml 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/base/content/tabbrowser.xml 2010-04-16 17:31:29.000000000 +0100 @@ -1473,7 +1473,7 @@ if (!aTabWillBeMoved) { let ds = browser.docShell; - if (ds.contentViewer && !ds.contentViewer.permitUnload()) + if (ds && ds.contentViewer && !ds.contentViewer.permitUnload()) return null; } diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/components/preferences/advanced.js firefox-3.6.4+build1+nobinonly/mozilla/browser/components/preferences/advanced.js --- firefox-3.6.3+nobinonly/mozilla/browser/components/preferences/advanced.js 2010-04-02 16:57:46.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/components/preferences/advanced.js 2010-04-16 17:31:30.000000000 +0100 @@ -66,6 +66,9 @@ this.updateModeItems(); #endif this.updateOfflineApps(); +#ifdef MOZ_CRASHREPORTER + this.initSubmitCrashes(); +#endif }, /** @@ -139,6 +142,35 @@ return checkbox.checked ? (this._storedSpellCheck == 2 ? 2 : 1) : 0; }, + /** + * + */ + initSubmitCrashes: function () + { + var checkbox = document.getElementById("submitCrashesBox"); + try { + var cr = Components.classes["@mozilla.org/toolkit/crash-reporter;1"]. + getService(Components.interfaces.nsICrashReporter_MOZILLA_1_9_2_BRANCH); + checkbox.checked = cr.submitReports; + } catch (e) { + checkbox.style.display = "none"; + } + }, + + /** + * + */ + updateSubmitCrashes: function () + { + var checkbox = document.getElementById("submitCrashesBox"); + try { + var cr = Components.classes["@mozilla.org/toolkit/crash-reporter;1"]. + getService(Components.interfaces.nsICrashReporter); + cr.submitReports = checkbox.checked; + } catch (e) { } + }, + + // NETWORK TAB /* diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/components/preferences/advanced.xul firefox-3.6.4+build1+nobinonly/mozilla/browser/components/preferences/advanced.xul --- firefox-3.6.3+nobinonly/mozilla/browser/components/preferences/advanced.xul 2010-04-02 16:57:46.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/components/preferences/advanced.xul 2010-04-16 17:31:30.000000000 +0100 @@ -47,6 +47,8 @@ %advancedDTD; %privacyDTD; + + ]> - + @@ -196,6 +198,11 @@ oncommand="gAdvancedPane.checkNow()" preference="pref.general.disable_button.default_browser"/> +#ifdef MOZ_CRASHREPORTER + +#endif #endif diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js firefox-3.6.4+build1+nobinonly/mozilla/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js --- firefox-3.6.3+nobinonly/mozilla/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js 2010-04-16 17:31:30.000000000 +0100 @@ -88,10 +88,14 @@ gBrowser.selectedTab = tab; } - pb.privateBrowsingEnabled = true; - testPopupBlockerMenuItem(true, function() { - pb.privateBrowsingEnabled = false; - gPrefService.setBoolPref("dom.disable_open_during_load", oldPopupPolicy); - finish(); + testPopupBlockerMenuItem(false, function() { + pb.privateBrowsingEnabled = true; + testPopupBlockerMenuItem(true, function() { + pb.privateBrowsingEnabled = false; + testPopupBlockerMenuItem(false, function() { + gPrefService.setBoolPref("dom.disable_open_during_load", oldPopupPolicy); + finish(); + }); + }); }); } diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_urlbarundo.js firefox-3.6.4+build1+nobinonly/mozilla/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_urlbarundo.js --- firefox-3.6.3+nobinonly/mozilla/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_urlbarundo.js 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_urlbarundo.js 2010-04-16 17:31:30.000000000 +0100 @@ -0,0 +1,66 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Private Browsing Tests. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ehsan Akhgari (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// This test makes sure that the undo history of the URL bar is cleared when +// leaving the private browsing mode. + +function test() { + // initialization + let prefBranch = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefBranch); + prefBranch.setBoolPref("browser.privatebrowsing.keep_current_session", true); + let pb = Cc["@mozilla.org/privatebrowsing;1"]. + getService(Ci.nsIPrivateBrowsingService); + + // enter private browsing mode + pb.privateBrowsingEnabled = true; + + // fill in the URL bar with something + gURLBar.value = "some test value"; + + ok(gURLBar.editor.transactionManager.numberOfUndoItems > 0, + "The undo history for the URL bar should not be empty"); + + // leave private browsing mode + pb.privateBrowsingEnabled = false; + + is(gURLBar.editor.transactionManager.numberOfUndoItems, 0, + "The undo history of the URL bar should be cleared after leaving the private browsing mode"); + + // cleanup + prefBranch.clearUserPref("browser.privatebrowsing.keep_current_session"); +} + diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/components/privatebrowsing/test/browser/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/browser/components/privatebrowsing/test/browser/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/browser/components/privatebrowsing/test/browser/Makefile.in 2010-04-02 16:57:46.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/components/privatebrowsing/test/browser/Makefile.in 2010-04-16 17:31:30.000000000 +0100 @@ -69,6 +69,7 @@ browser_privatebrowsing_transition.js \ browser_privatebrowsing_ui.js \ browser_privatebrowsing_urlbarfocus.js \ + browser_privatebrowsing_urlbarundo.js \ browser_privatebrowsing_viewsource.js \ browser_privatebrowsing_windowtitle.js \ browser_privatebrowsing_windowtitle_page.html \ diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/components/privatebrowsing/test/unit/test_openLocationLastURL.js firefox-3.6.4+build1+nobinonly/mozilla/browser/components/privatebrowsing/test/unit/test_openLocationLastURL.js --- firefox-3.6.3+nobinonly/mozilla/browser/components/privatebrowsing/test/unit/test_openLocationLastURL.js 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/components/privatebrowsing/test/unit/test_openLocationLastURL.js 2010-04-16 17:31:30.000000000 +0100 @@ -43,6 +43,13 @@ let Cu = Components.utils; Cu.import("resource:///modules/openLocationLastURL.jsm"); + function clearHistory() { + // simulate clearing the private data + Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService). + notifyObservers(null, "browser:purge-session-history", ""); + } + let pb = Cc[PRIVATEBROWSING_CONTRACT_ID]. getService(Ci.nsIPrivateBrowsingService); let pref = Cc["@mozilla.org/preferences-service;1"]. @@ -64,6 +71,10 @@ gOpenLocationLastURL.value = url2; do_check_eq(gOpenLocationLastURL.value, url2); + clearHistory(); + do_check_eq(gOpenLocationLastURL.value, ""); + gOpenLocationLastURL.value = url2; + pb.privateBrowsingEnabled = true; do_check_eq(gOpenLocationLastURL.value, ""); @@ -76,6 +87,15 @@ pb.privateBrowsingEnabled = false; do_check_eq(gOpenLocationLastURL.value, url2); + + pb.privateBrowsingEnabled = true; + gOpenLocationLastURL.value = url1; + do_check_neq(gOpenLocationLastURL.value, ""); + clearHistory(); + do_check_eq(gOpenLocationLastURL.value, ""); + + pb.privateBrowsingEnabled = false; + do_check_eq(gOpenLocationLastURL.value, ""); } // Support running tests on both the service itself and its wrapper diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/components/sessionstore/src/nsSessionStore.js firefox-3.6.4+build1+nobinonly/mozilla/browser/components/sessionstore/src/nsSessionStore.js --- firefox-3.6.3+nobinonly/mozilla/browser/components/sessionstore/src/nsSessionStore.js 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/components/sessionstore/src/nsSessionStore.js 2010-04-16 17:31:30.000000000 +0100 @@ -685,7 +685,7 @@ if (!isFullyLoaded) { if (!aWindow.__SSi) aWindow.__SSi = "window" + Date.now(); - this._window[aWindow.__SSi] = this._statesToRestore[aWindow.__SS_restoreID]; + this._windows[aWindow.__SSi] = this._statesToRestore[aWindow.__SS_restoreID]; delete this._statesToRestore[aWindow.__SS_restoreID]; delete aWindow.__SS_restoreID; } diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/components/shell/test/browser_420786.js firefox-3.6.4+build1+nobinonly/mozilla/browser/components/shell/test/browser_420786.js --- firefox-3.6.3+nobinonly/mozilla/browser/components/shell/test/browser_420786.js 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/components/shell/test/browser_420786.js 2010-04-16 17:31:30.000000000 +0100 @@ -69,10 +69,10 @@ function test() { var osString = Cc["@mozilla.org/xre/app-info;1"]. getService(Ci.nsIXULRuntime).OS; - - // This test is Linux specific for now - if (osString != "Linux") + if (osString != "Linux") { + todo(false, "This test is Linux specific for now."); return; + } gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedBrowser.addEventListener("load", onPageLoad, true); diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/config/version.txt firefox-3.6.4+build1+nobinonly/mozilla/browser/config/version.txt --- firefox-3.6.3+nobinonly/mozilla/browser/config/version.txt 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/config/version.txt 2010-04-16 17:33:03.000000000 +0100 @@ -1 +1 @@ -3.6.3 +3.6.4 diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/installer/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/browser/installer/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/browser/installer/Makefile.in 2010-04-02 16:57:47.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/installer/Makefile.in 2010-04-16 17:31:30.000000000 +0100 @@ -98,6 +98,11 @@ DEFINES += -DMOZ_UPDATER=1 endif +ifdef MOZ_IPC +include $(topsrcdir)/ipc/app/defs.mk +DEFINES += -DMOZ_CHILD_PROCESS_NAME=$(MOZ_CHILD_PROCESS_NAME) +endif + ifdef MOZ_PKG_MANIFEST_P MOZ_PKG_MANIFEST = package-manifest diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/installer/package-manifest.in firefox-3.6.4+build1+nobinonly/mozilla/browser/installer/package-manifest.in --- firefox-3.6.3+nobinonly/mozilla/browser/installer/package-manifest.in 2010-04-02 16:57:47.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/installer/package-manifest.in 2010-04-16 17:31:30.000000000 +0100 @@ -54,6 +54,9 @@ #else @BINPATH@/@DLL_PREFIX@xul@DLL_SUFFIX@ #endif +#ifdef MOZ_IPC +@BINPATH@/@MOZ_CHILD_PROCESS_NAME@ +#endif #ifdef WINCE @BINPATH@/mozce_shunt.dll #elifdef XP_WIN32 @@ -64,6 +67,7 @@ @BINPATH@/msvcr80.dll #else @BINPATH@/mozcrt19.dll +@BINPATH@/mozcpp19.dll #endif #endif @@ -350,6 +354,7 @@ #ifdef MOZ_ENABLE_DBUS @BINPATH@/components/@DLL_PREFIX@dbusservice@DLL_SUFFIX@ #endif +@BINPATH@/components/nsINIProcessor.js ; Modules @BINPATH@/modules/* diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/installer/removed-files.in firefox-3.6.4+build1+nobinonly/mozilla/browser/installer/removed-files.in --- firefox-3.6.3+nobinonly/mozilla/browser/installer/removed-files.in 2010-04-02 16:57:47.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/installer/removed-files.in 2010-04-16 17:31:30.000000000 +0100 @@ -813,3 +813,4 @@ components/nsUpdateServiceStub.js #endif old-homepage-default.properties +mozilla-runtime@BIN_SUFFIX@ diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/locales/en-US/chrome/browser/browser.properties firefox-3.6.4+build1+nobinonly/mozilla/browser/locales/en-US/chrome/browser/browser.properties --- firefox-3.6.3+nobinonly/mozilla/browser/locales/en-US/chrome/browser/browser.properties 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/locales/en-US/chrome/browser/browser.properties 2010-04-16 17:31:31.000000000 +0100 @@ -74,6 +74,12 @@ blockedpluginsMessage.infoButton.accesskey=D blockedpluginsMessage.searchButton.label=Update Plugins… blockedpluginsMessage.searchButton.accesskey=U +crashedpluginsMessage.title=The %S plugin has crashed. +crashedpluginsMessage.reloadButton.label=Reload page +crashedpluginsMessage.reloadButton.accesskey=R +crashedpluginsMessage.submitButton.label=Submit a crash report +crashedpluginsMessage.submitButton.accesskey=S +crashedpluginsMessage.learnMore=Learn More… # Sanitize sanitizeWithPromptLabel2=Clear Recent History… diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/locales/en-US/chrome/browser/preferences/advanced.dtd firefox-3.6.4+build1+nobinonly/mozilla/browser/locales/en-US/chrome/browser/preferences/advanced.dtd --- firefox-3.6.3+nobinonly/mozilla/browser/locales/en-US/chrome/browser/preferences/advanced.dtd 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/locales/en-US/chrome/browser/preferences/advanced.dtd 2010-04-16 17:31:31.000000000 +0100 @@ -25,6 +25,8 @@ + + diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/locales/filter.py firefox-3.6.4+build1+nobinonly/mozilla/browser/locales/filter.py --- firefox-3.6.3+nobinonly/mozilla/browser/locales/filter.py 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/locales/filter.py 2010-04-16 17:31:31.000000000 +0100 @@ -1,30 +1,41 @@ def test(mod, path, entity = None): import re - # ignore anyhting but Firefox + # ignore anything but Firefox if mod not in ("netwerk", "dom", "toolkit", "security/manager", "browser", "extensions/reporter", "extensions/spellcheck", "other-licenses/branding/firefox"): - return False + return "ignore" + if mod != "browser" and mod != "extensions/spellcheck": # we only have exceptions for browser and extensions/spellcheck - return True + return "error" if not entity: if mod == "extensions/spellcheck": - return False + return "ignore" # browser - return not (re.match(r"searchplugins\/.+\.xml", path) or - re.match(r"chrome\/help\/images\/[A-Za-z-_]+\.png", path)) + return (re.match(r"searchplugins\/.+\.xml", path) and + "ignore" or "error") + if mod == "extensions/spellcheck": # l10n ships en-US dictionary or something, do compare - return True + return "error" + + # just browser remains if path == "defines.inc": - return entity != "MOZ_LANGPACK_CONTRIBUTORS" + return (entity == "MOZ_LANGPACK_CONTRIBUTORS") and "ignore" or "error" + + # Ignore Lorentz strings, at least temporarily + if path == 'chrome/browser/browser.properties': + if entity.startswith('crashedpluginsMessage.'): return "report" + if path == 'chrome/browser/preferences/advanced.dtd': + if entity.startswith('submitCrashes'): return "report" if path != "chrome/browser-region/region.properties": # only region.properties exceptions remain, compare all others - return True + return "error" - return not (re.match(r"browser\.search\.order\.[1-9]", entity) or + return ((re.match(r"browser\.search\.order\.[1-9]", entity) or re.match(r"browser\.contentHandlers\.types\.[0-5]", entity) or re.match(r"gecko\.handlerService\.schemes\.", entity) or re.match(r"gecko\.handlerService\.defaultHandlersVersion", entity)) + and "ignore" or "report") diff -Nru firefox-3.6.3+nobinonly/mozilla/browser/locales/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/browser/locales/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/browser/locales/Makefile.in 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/browser/locales/Makefile.in 2010-04-16 17:31:31.000000000 +0100 @@ -344,7 +344,7 @@ l10n-upload-%: $(PYTHON) $(topsrcdir)/build/upload.py --base-path $(DIST) "$(DIST)/$(PACKAGE)" $(DIST)/$(LANGPACK) ifdef MOZ_MAKE_COMPLETE_MAR - $(PYTHON) $(topsrcdir)/build/upload.py --base-path $(DIST) $(DIST)/$(COMPLETE_MAR) + $(PYTHON) $(topsrcdir)/build/upload.py --base-path $(DIST) $(DIST)/$(COMPLETE_MAR) $(call QUOTED_WILDCARD,$(wildcard $(DIST)/$(PARTIAL_MAR))) endif ifneq (,$(filter WINNT WINCE,$(OS_ARCH))) $(PYTHON) $(topsrcdir)/build/upload.py --base-path $(DIST) "$(INSTALLER_PACKAGE)" diff -Nru firefox-3.6.3+nobinonly/mozilla/build/automation-build.mk firefox-3.6.4+build1+nobinonly/mozilla/build/automation-build.mk --- firefox-3.6.3+nobinonly/mozilla/build/automation-build.mk 2010-04-02 16:57:47.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/build/automation-build.mk 2010-04-16 17:31:32.000000000 +0100 @@ -1,5 +1,4 @@ - -ifeq ($(USE_SHORT_LIBNAME), 1) +ifneq (,$(filter OS2 WINCE WINNT,$(OS_ARCH))) PROGRAM = $(MOZ_APP_NAME)$(BIN_SUFFIX) else PROGRAM = $(MOZ_APP_NAME)-bin$(BIN_SUFFIX) @@ -22,7 +21,6 @@ endif _PROFILE_DIR = $(TARGET_DEPTH)/_profile/pgo -_SYMBOLS_PATH = $(TARGET_DIST)/crashreporter-symbols ABSOLUTE_TOPSRCDIR = $(call core_abspath,$(MOZILLA_DIR)) _CERTS_SRC_DIR = $(ABSOLUTE_TOPSRCDIR)/build/pgo/certs @@ -33,8 +31,7 @@ -DBIN_SUFFIX=\"$(BIN_SUFFIX)\" \ -DPROFILE_DIR=\"$(_PROFILE_DIR)\" \ -DCERTS_SRC_DIR=\"$(_CERTS_SRC_DIR)\" \ - -DSYMBOLS_PATH=\"$(_SYMBOLS_PATH)\" \ - -DPERL=\"$(PERL)\" \ + -DPERL="\"$(PERL)\"" \ $(NULL) ifeq ($(OS_ARCH),Darwin) @@ -71,6 +68,14 @@ AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=0 endif +ifdef MOZ_CRASHREPORTER +AUTOMATION_PPARGS += -DCRASHREPORTER=1 +else +AUTOMATION_PPARGS += -DCRASHREPORTER=0 +endif + automation.py: $(MOZILLA_DIR)/build/automation.py.in $(MOZILLA_DIR)/build/automation-build.mk $(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py \ $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< > $@ + +GARBAGE += automation.py diff -Nru firefox-3.6.3+nobinonly/mozilla/build/automation.py.in firefox-3.6.4+build1+nobinonly/mozilla/build/automation.py.in --- firefox-3.6.3+nobinonly/mozilla/build/automation.py.in 2010-04-02 16:57:47.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/build/automation.py.in 2010-04-16 17:31:32.000000000 +0100 @@ -38,104 +38,38 @@ # ***** END LICENSE BLOCK ***** import codecs -from datetime import datetime +from datetime import datetime, timedelta import itertools import logging import os import re +import select import shutil import signal import subprocess import sys import threading +import tempfile -""" -Runs the browser from a script, and provides useful utilities -for setting up the browser environment. -""" - -SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0]))) -sys.path.insert(0, SCRIPT_DIR); -from automationutils import checkForCrashes -__all__ = [ - "UNIXISH", - "IS_WIN32", - "IS_MAC", - "log", - "runApp", - "Process", - "initializeProfile", - "DIST_BIN", - "DEFAULT_APP", - "CERTS_SRC_DIR", - "environment", - "IS_TEST_BUILD", - "IS_DEBUG_BUILD", - "SYMBOLS_PATH", - ] - -# These are generated in mozilla/build/Makefile.in -#expand DIST_BIN = __XPC_BIN_PATH__ -#expand IS_WIN32 = len("__WIN32__") != 0 -#expand IS_MAC = __IS_MAC__ != 0 -#expand IS_LINUX = __IS_LINUX__ != 0 +#expand _DIST_BIN = __XPC_BIN_PATH__ +#expand _IS_WIN32 = len("__WIN32__") != 0 +#expand _IS_MAC = __IS_MAC__ != 0 +#expand _IS_LINUX = __IS_LINUX__ != 0 #ifdef IS_CYGWIN -#expand IS_CYGWIN = __IS_CYGWIN__ == 1 +#expand _IS_CYGWIN = __IS_CYGWIN__ == 1 #else -IS_CYGWIN = False +_IS_CYGWIN = False #endif -#expand IS_CAMINO = __IS_CAMINO__ != 0 -#expand BIN_SUFFIX = __BIN_SUFFIX__ -#expand PERL = __PERL__ - -UNIXISH = not IS_WIN32 and not IS_MAC - -#expand DEFAULT_APP = "./" + __BROWSER_PATH__ -#expand CERTS_SRC_DIR = __CERTS_SRC_DIR__ -#expand IS_TEST_BUILD = __IS_TEST_BUILD__ -#expand IS_DEBUG_BUILD = __IS_DEBUG_BUILD__ -#expand SYMBOLS_PATH = __SYMBOLS_PATH__ - -########### -# LOGGING # -########### - -# We use the logging system here primarily because it'll handle multiple -# threads, which is needed to process the output of the server and application -# processes simultaneously. -log = logging.getLogger() -handler = logging.StreamHandler(sys.stdout) -log.setLevel(logging.INFO) -log.addHandler(handler) - - -################# -# SUBPROCESSING # -################# - -class Process(subprocess.Popen): - """ - Represents our view of a subprocess. - It adds a kill() method which allows it to be stopped explicitly. - """ - - def kill(self): - if IS_WIN32: - import platform - pid = "%i" % self.pid - if platform.release() == "2000": - # Windows 2000 needs 'kill.exe' from the 'Windows 2000 Resource Kit tools'. (See bug 475455.) - try: - subprocess.Popen(["kill", "-f", pid]).wait() - except: - log.info("TEST-UNEXPECTED-FAIL | automation.py | Missing 'kill' utility to kill process with pid=%s. Kill it manually!", pid) - else: - # Windows XP and later. - subprocess.Popen(["taskkill", "/F", "/PID", pid]).wait() - else: - os.kill(self.pid, signal.SIGKILL) - +#expand _IS_CAMINO = __IS_CAMINO__ != 0 +#expand _BIN_SUFFIX = __BIN_SUFFIX__ +#expand _PERL = __PERL__ + +#expand _DEFAULT_APP = "./" + __BROWSER_PATH__ +#expand _CERTS_SRC_DIR = __CERTS_SRC_DIR__ +#expand _IS_TEST_BUILD = __IS_TEST_BUILD__ +#expand _IS_DEBUG_BUILD = __IS_DEBUG_BUILD__ +#expand _CRASHREPORTER = __CRASHREPORTER__ == 1 ################# # PROFILE SETUP # @@ -166,19 +100,102 @@ self.port = port self.options = options - -def readLocations(locationsPath = "server-locations.txt"): +class Automation(object): """ - Reads the locations at which the Mochitest HTTP server is available from - server-locations.txt. + Runs the browser from a script, and provides useful utilities + for setting up the browser environment. """ - locationFile = codecs.open(locationsPath, "r", "UTF-8") + DIST_BIN = _DIST_BIN + IS_WIN32 = _IS_WIN32 + IS_MAC = _IS_MAC + IS_LINUX = _IS_LINUX + IS_CYGWIN = _IS_CYGWIN + IS_CAMINO = _IS_CAMINO + BIN_SUFFIX = _BIN_SUFFIX + PERL = _PERL + + UNIXISH = not IS_WIN32 and not IS_MAC + + DEFAULT_APP = _DEFAULT_APP + CERTS_SRC_DIR = _CERTS_SRC_DIR + IS_TEST_BUILD = _IS_TEST_BUILD + IS_DEBUG_BUILD = _IS_DEBUG_BUILD + CRASHREPORTER = _CRASHREPORTER + + SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0]))) + sys.path.insert(0, SCRIPT_DIR) + automationutils = __import__('automationutils') + + # timeout, in seconds + DEFAULT_TIMEOUT = 60.0 + + log = logging.getLogger() + + def __init__(self): + + # We use the logging system here primarily because it'll handle multiple + # threads, which is needed to process the output of the server and application + # processes simultaneously. + handler = logging.StreamHandler(sys.stdout) + self.log.setLevel(logging.INFO) + self.log.addHandler(handler) + + @property + def __all__(self): + return [ + "UNIXISH", + "IS_WIN32", + "IS_MAC", + "log", + "runApp", + "Process", + "addCommonOptions", + "initializeProfile", + "DIST_BIN", + "DEFAULT_APP", + "CERTS_SRC_DIR", + "environment", + "IS_TEST_BUILD", + "IS_DEBUG_BUILD", + "DEFAULT_TIMEOUT", + ] + + class Process(subprocess.Popen): + """ + Represents our view of a subprocess. + It adds a kill() method which allows it to be stopped explicitly. + """ + + def kill(self): + if Automation().IS_WIN32: + import platform + pid = "%i" % self.pid + if platform.release() == "2000": + # Windows 2000 needs 'kill.exe' from the + #'Windows 2000 Resource Kit tools'. (See bug 475455.) + try: + subprocess.Popen(["kill", "-f", pid]).wait() + except: + self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Missing 'kill' utility to kill process with pid=%s. Kill it manually!", pid) + else: + # Windows XP and later. + subprocess.Popen(["taskkill", "/F", "/PID", pid]).wait() + else: + os.kill(self.pid, signal.SIGKILL) - # Perhaps more detail than necessary, but it's the easiest way to make sure - # we get exactly the format we want. See server-locations.txt for the exact - # format guaranteed here. - lineRe = re.compile(r"^(?P[a-z][-a-z0-9+.]*)" + def readLocations(self, locationsPath = "server-locations.txt"): + """ + Reads the locations at which the Mochitest HTTP server is available from + server-locations.txt. + """ + + locationFile = codecs.open(locationsPath, "r", "UTF-8") + + # Perhaps more detail than necessary, but it's the easiest way to make sure + # we get exactly the format we want. See server-locations.txt for the exact + # format guaranteed here. + lineRe = re.compile(r"^(?P[a-z][-a-z0-9+.]*)" r"://" r"(?P" r"\d+\.\d+\.\d+\.\d+" @@ -192,52 +209,53 @@ r"\s+" r"(?P\S+(?:,\S+)*)" r")?$") - locations = [] - lineno = 0 - seenPrimary = False - for line in locationFile: - lineno += 1 - if line.startswith("#") or line == "\n": - continue + locations = [] + lineno = 0 + seenPrimary = False + for line in locationFile: + lineno += 1 + if line.startswith("#") or line == "\n": + continue - match = lineRe.match(line) - if not match: - raise SyntaxError(lineno) - - options = match.group("options") - if options: - options = options.split(",") - if "primary" in options: - if seenPrimary: - raise SyntaxError(lineno, "multiple primary locations") - seenPrimary = True - else: - options = [] + match = lineRe.match(line) + if not match: + raise SyntaxError(lineno) + + options = match.group("options") + if options: + options = options.split(",") + if "primary" in options: + if seenPrimary: + raise SyntaxError(lineno, "multiple primary locations") + seenPrimary = True + else: + options = [] - locations.append(Location(match.group("scheme"), match.group("host"), - match.group("port"), options)) + locations.append(Location(match.group("scheme"), match.group("host"), + match.group("port"), options)) - if not seenPrimary: - raise SyntaxError(lineno + 1, "missing primary location") + if not seenPrimary: + raise SyntaxError(lineno + 1, "missing primary location") - return locations + return locations -def initializeProfile(profileDir): - "Sets up the standard testing profile." + def initializeProfile(self, profileDir, extraPrefs = []): + "Sets up the standard testing profile." - # Start with a clean slate. - shutil.rmtree(profileDir, True) - os.mkdir(profileDir) + # Start with a clean slate. + shutil.rmtree(profileDir, True) + os.mkdir(profileDir) - prefs = [] + prefs = [] - part = """\ + part = """\ user_pref("browser.dom.window.dump.enabled", true); user_pref("dom.allow_scripts_to_close_windows", true); user_pref("dom.disable_open_during_load", false); user_pref("dom.max_script_run_time", 0); // no slow script dialogs user_pref("dom.max_chrome_script_run_time", 0); +user_pref("dom.popup_maximum", -1); user_pref("signed.applets.codebase_principal_support", true); user_pref("security.warn_submit_insecure", false); user_pref("browser.shell.checkDefaultBrowser", false); @@ -253,7 +271,6 @@ user_pref("test.mousescroll", true); user_pref("security.default_personal_cert", "Select Automatically"); // Need to client auth test be w/o any dialogs user_pref("network.http.prompt-temp-redirect", false); -user_pref("svg.smil.enabled", true); // Needed for SMIL mochitests until bug 482402 lands user_pref("media.cache_size", 100); user_pref("security.warn_viewing_mixed", false); @@ -271,14 +288,14 @@ user_pref("browser.safebrowsing.provider.0.updateURL", "http://localhost:8888/safebrowsing-dummy/update"); """ - prefs.append(part) + prefs.append(part) - locations = readLocations() + locations = self.readLocations() - # Grant God-power to all the privileged servers on which tests run. - privileged = filter(lambda loc: "privileged" in loc.options, locations) - for (i, l) in itertools.izip(itertools.count(1), privileged): - part = """ + # Grant God-power to all the privileged servers on which tests run. + privileged = filter(lambda loc: "privileged" in loc.options, locations) + for (i, l) in itertools.izip(itertools.count(1), privileged): + part = """ user_pref("capability.principal.codebase.p%(i)d.granted", "UniversalXPConnect UniversalBrowserRead UniversalBrowserWrite \ UniversalPreferencesRead UniversalPreferencesWrite \ @@ -287,14 +304,14 @@ user_pref("capability.principal.codebase.p%(i)d.subjectName", ""); """ % { "i": i, "origin": (l.scheme + "://" + l.host + ":" + l.port) } - prefs.append(part) + prefs.append(part) - # We need to proxy every server but the primary one. - origins = ["'%s://%s:%s'" % (l.scheme, l.host, l.port) - for l in filter(lambda l: "primary" not in l.options, locations)] - origins = ", ".join(origins) + # We need to proxy every server but the primary one. + origins = ["'%s://%s:%s'" % (l.scheme, l.host, l.port) + for l in filter(lambda l: "primary" not in l.options, locations)] + origins = ", ".join(origins) - pacURL = """data:text/plain, + pacURL = """data:text/plain, function FindProxyForURL(url, host) { var origins = [%(origins)s]; @@ -323,208 +340,411 @@ return 'PROXY 127.0.0.1:4443'; return 'DIRECT'; }""" % { "origins": origins } - pacURL = "".join(pacURL.splitlines()) + pacURL = "".join(pacURL.splitlines()) - part = """ + part = """ user_pref("network.proxy.type", 2); user_pref("network.proxy.autoconfig_url", "%(pacURL)s"); user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless to others """ % {"pacURL": pacURL} - prefs.append(part) + prefs.append(part) - # write the preferences - prefsFile = open(profileDir + "/" + "user.js", "a") - prefsFile.write("".join(prefs)) - prefsFile.close() + for v in extraPrefs: + thispref = v.split("=") + if len(thispref) < 2: + print "Error: syntax error in --setpref=" + v + sys.exit(1) + part = 'user_pref("%s", %s);\n' % (thispref[0], thispref[1]) + prefs.append(part) + + # write the preferences + prefsFile = open(profileDir + "/" + "user.js", "a") + prefsFile.write("".join(prefs)) + prefsFile.close() + + def addCommonOptions(self, parser): + "Adds command-line options which are common to mochitest and reftest." + + parser.add_option("--setpref", + action = "append", type = "string", + default = [], + dest = "extraPrefs", metavar = "PREF=VALUE", + help = "defines an extra user preference") -def fillCertificateDB(profileDir, certPath, utilityPath, xrePath): - pwfilePath = os.path.join(profileDir, ".crtdbpw") + def fillCertificateDB(self, profileDir, certPath, utilityPath, xrePath): + pwfilePath = os.path.join(profileDir, ".crtdbpw") - pwfile = open(pwfilePath, "w") - pwfile.write("\n") - pwfile.close() - - # Create head of the ssltunnel configuration file - sslTunnelConfigPath = os.path.join(profileDir, "ssltunnel.cfg") - sslTunnelConfig = open(sslTunnelConfigPath, "w") + pwfile = open(pwfilePath, "w") + pwfile.write("\n") + pwfile.close() + + # Create head of the ssltunnel configuration file + sslTunnelConfigPath = os.path.join(profileDir, "ssltunnel.cfg") + sslTunnelConfig = open(sslTunnelConfigPath, "w") - sslTunnelConfig.write("httpproxy:1\n") - sslTunnelConfig.write("certdbdir:%s\n" % certPath) - sslTunnelConfig.write("forward:127.0.0.1:8888\n") - sslTunnelConfig.write("listen:*:4443:pgo server certificate\n") - - # Configure automatic certificate and bind custom certificates, client authentication - locations = readLocations() - locations.pop(0) - for loc in locations: - if loc.scheme == "https" and "nocert" not in loc.options: - customCertRE = re.compile("^cert=(?P[0-9a-zA-Z_ ]+)") - clientAuthRE = re.compile("^clientauth=(?P[a-z]+)") - for option in loc.options: - match = customCertRE.match(option) - if match: - customcert = match.group("nickname"); - sslTunnelConfig.write("listen:%s:%s:4443:%s\n" % - (loc.host, loc.port, customcert)) - - match = clientAuthRE.match(option) - if match: - clientauth = match.group("clientauth"); - sslTunnelConfig.write("clientauth:%s:%s:4443:%s\n" % - (loc.host, loc.port, clientauth)) - - sslTunnelConfig.close() - - # Pre-create the certification database for the profile - env = environment(xrePath = xrePath) - certutil = os.path.join(utilityPath, "certutil" + BIN_SUFFIX) - pk12util = os.path.join(utilityPath, "pk12util" + BIN_SUFFIX) + sslTunnelConfig.write("httpproxy:1\n") + sslTunnelConfig.write("certdbdir:%s\n" % certPath) + sslTunnelConfig.write("forward:127.0.0.1:8888\n") + sslTunnelConfig.write("listen:*:4443:pgo server certificate\n") + + # Configure automatic certificate and bind custom certificates, client authentication + locations = self.readLocations() + locations.pop(0) + for loc in locations: + if loc.scheme == "https" and "nocert" not in loc.options: + customCertRE = re.compile("^cert=(?P[0-9a-zA-Z_ ]+)") + clientAuthRE = re.compile("^clientauth=(?P[a-z]+)") + for option in loc.options: + match = customCertRE.match(option) + if match: + customcert = match.group("nickname"); + sslTunnelConfig.write("listen:%s:%s:4443:%s\n" % + (loc.host, loc.port, customcert)) + + match = clientAuthRE.match(option) + if match: + clientauth = match.group("clientauth"); + sslTunnelConfig.write("clientauth:%s:%s:4443:%s\n" % + (loc.host, loc.port, clientauth)) + + sslTunnelConfig.close() + + # Pre-create the certification database for the profile + env = self.environment(xrePath = xrePath) + certutil = os.path.join(utilityPath, "certutil" + self.BIN_SUFFIX) + pk12util = os.path.join(utilityPath, "pk12util" + self.BIN_SUFFIX) - status = Process([certutil, "-N", "-d", profileDir, "-f", pwfilePath], env = env).wait() - if status != 0: - return status + status = self.Process([certutil, "-N", "-d", profileDir, "-f", pwfilePath], env = env).wait() + if status != 0: + return status - # Walk the cert directory and add custom CAs and client certs - files = os.listdir(certPath) - for item in files: - root, ext = os.path.splitext(item) - if ext == ".ca": - trustBits = "CT,," - if root.endswith("-object"): - trustBits = "CT,,CT" - Process([certutil, "-A", "-i", os.path.join(certPath, item), - "-d", profileDir, "-f", pwfilePath, "-n", root, "-t", trustBits], - env = env).wait() - if ext == ".client": - Process([pk12util, "-i", os.path.join(certPath, item), "-w", - pwfilePath, "-d", profileDir], - env = env).wait() - - os.unlink(pwfilePath) - return 0 - -def environment(env = None, xrePath = DIST_BIN, crashreporter = True): - if env == None: - env = dict(os.environ) - - ldLibraryPath = os.path.abspath(os.path.join(SCRIPT_DIR, xrePath)) - if UNIXISH or IS_MAC: - envVar = "LD_LIBRARY_PATH" - if IS_MAC: - envVar = "DYLD_LIBRARY_PATH" - if envVar in env: - ldLibraryPath = ldLibraryPath + ":" + env[envVar] - env[envVar] = ldLibraryPath - elif IS_WIN32: - env["PATH"] = env["PATH"] + ";" + ldLibraryPath - - if crashreporter: - env['MOZ_CRASHREPORTER_NO_REPORT'] = '1' - env['MOZ_CRASHREPORTER'] = '1' - - return env - - -############### -# RUN THE APP # -############### - -def runApp(testURL, env, app, profileDir, extraArgs, - runSSLTunnel = False, utilityPath = DIST_BIN, - xrePath = DIST_BIN, certPath = CERTS_SRC_DIR, - debuggerInfo = None, symbolsPath = SYMBOLS_PATH): - "Run the app, log the duration it took to execute, return the status code." - - if IS_TEST_BUILD and runSSLTunnel: - # create certificate database for the profile - certificateStatus = fillCertificateDB(profileDir, certPath, utilityPath, xrePath) - if certificateStatus != 0: - log.info("TEST-UNEXPECTED FAIL | automation.py | Certificate integration failed") - return certificateStatus - - # start ssltunnel to provide https:// URLs capability - ssltunnel = os.path.join(utilityPath, "ssltunnel" + BIN_SUFFIX) - ssltunnelProcess = Process([ssltunnel, os.path.join(profileDir, "ssltunnel.cfg")], env = environment(xrePath = xrePath)) - log.info("INFO | automation.py | SSL tunnel pid: %d", ssltunnelProcess.pid) - - # now run with the profile we created - cmd = app - if IS_MAC and not IS_CAMINO and not cmd.endswith("-bin"): - cmd += "-bin" - cmd = os.path.abspath(cmd) - - args = [] - - if debuggerInfo: - args.extend(debuggerInfo["args"]) - args.append(cmd) - cmd = os.path.abspath(debuggerInfo["path"]) + # Walk the cert directory and add custom CAs and client certs + files = os.listdir(certPath) + for item in files: + root, ext = os.path.splitext(item) + if ext == ".ca": + trustBits = "CT,," + if root.endswith("-object"): + trustBits = "CT,,CT" + self.Process([certutil, "-A", "-i", os.path.join(certPath, item), + "-d", profileDir, "-f", pwfilePath, "-n", root, "-t", trustBits], + env = env).wait() + if ext == ".client": + self.Process([pk12util, "-i", os.path.join(certPath, item), "-w", + pwfilePath, "-d", profileDir], + env = env).wait() + + os.unlink(pwfilePath) + return 0 + + def environment(self, env = None, xrePath = None, crashreporter = True): + if xrePath == None: + xrePath = self.DIST_BIN + if env == None: + env = dict(os.environ) + + ldLibraryPath = os.path.abspath(os.path.join(self.SCRIPT_DIR, xrePath)) + if self.UNIXISH or self.IS_MAC: + envVar = "LD_LIBRARY_PATH" + if self.IS_MAC: + envVar = "DYLD_LIBRARY_PATH" + else: # unixish + env['MOZILLA_FIVE_HOME'] = xrePath + if envVar in env: + ldLibraryPath = ldLibraryPath + ":" + env[envVar] + env[envVar] = ldLibraryPath + elif self.IS_WIN32: + env["PATH"] = env["PATH"] + ";" + ldLibraryPath + + if crashreporter: + env['MOZ_CRASHREPORTER_NO_REPORT'] = '1' + env['MOZ_CRASHREPORTER'] = '1' + else: + env['MOZ_CRASHREPORTER_DISABLE'] = '1' + + env['GNOME_DISABLE_CRASH_DIALOG'] = '1' + env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1' + return env + + if IS_WIN32: + ctypes = __import__('ctypes') + wintypes = __import__('ctypes.wintypes') + time = __import__('time') + msvcrt = __import__('msvcrt') + PeekNamedPipe = ctypes.windll.kernel32.PeekNamedPipe + GetLastError = ctypes.windll.kernel32.GetLastError + + def readWithTimeout(self, f, timeout): + """Try to read a line of output from the file object |f|. + |f| must be a pipe, like the |stdout| member of a subprocess.Popen + object created with stdout=PIPE. If no output + is received within |timeout| seconds, return a blank line. + Returns a tuple (line, did_timeout), where |did_timeout| is True + if the read timed out, and False otherwise.""" + if timeout is None: + # shortcut to allow callers to pass in "None" for no timeout. + return (f.readline(), False) + x = self.msvcrt.get_osfhandle(f.fileno()) + l = self.ctypes.c_long() + done = self.time.time() + timeout + while self.time.time() < done: + if self.PeekNamedPipe(x, None, 0, None, self.ctypes.byref(l), None) == 0: + err = self.GetLastError() + if err == 38 or err == 109: # ERROR_HANDLE_EOF || ERROR_BROKEN_PIPE + return ('', False) + else: + log.error("readWithTimeout got error: %d", err) + if l.value > 0: + # we're assuming that the output is line-buffered, + # which is not unreasonable + return (f.readline(), False) + self.time.sleep(0.01) + return ('', True) + + def isPidAlive(self, pid): + STILL_ACTIVE = 259 + PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 + pHandle = self.ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid) + if not pHandle: + return False + pExitCode = ctypes.wintypes.DWORD() + self.ctypes.windll.kernel32.GetExitCodeProcess(pHandle, self.ctypes.byref(pExitCode)) + self.ctypes.windll.kernel32.CloseHandle(pHandle) + if (pExitCode.value == STILL_ACTIVE): + return True + else: + return False - if IS_MAC: - args.append("-foreground") + def killPid(self, pid): + PROCESS_TERMINATE = 0x0001 + pHandle = self.ctypes.windll.kernel32.OpenProcess(PROCESS_TERMINATE, 0, pid) + if not pHandle: + return + success = self.ctypes.windll.kernel32.TerminateProcess(pHandle, 1) + self.ctypes.windll.kernel32.CloseHandle(pHandle) - if IS_CYGWIN: - profileDirectory = commands.getoutput("cygpath -w \"" + profileDir + "/\"") else: - profileDirectory = profileDir + "/" + errno = __import__('errno') - args.extend(("-no-remote", "-profile", profileDirectory)) - if testURL is not None: - if IS_CAMINO: - args.extend(("-url", testURL)) + def readWithTimeout(self, f, timeout): + """Try to read a line of output from the file object |f|. If no output + is received within |timeout| seconds, return a blank line. + Returns a tuple (line, did_timeout), where |did_timeout| is True + if the read timed out, and False otherwise.""" + (r, w, e) = select.select([f], [], [], timeout) + if len(r) == 0: + return ('', True) + return (f.readline(), False) + + def isPidAlive(self, pid): + try: + # kill(pid, 0) checks for a valid PID without actually sending a signal + # The method throws OSError if the PID is invalid, which we catch below. + os.kill(pid, 0) + + # Wait on it to see if it's a zombie. This can throw OSError.ECHILD if + # the process terminates before we get to this point. + wpid, wstatus = os.waitpid(pid, os.WNOHANG) + if wpid == 0: + return True + + return False + except OSError, err: + # Catch the errors we might expect from os.kill/os.waitpid, + # and re-raise any others + if err.errno == self.errno.ESRCH or err.errno == self.errno.ECHILD: + return False + raise + + def killPid(self, pid): + os.kill(pid, signal.SIGKILL) + + def triggerBreakpad(self, proc, utilityPath): + """Attempt to kill this process in a way that triggers Breakpad crash + reporting, if we know how for this platform. Otherwise just .kill() it.""" + if self.CRASHREPORTER: + if self.UNIXISH: + # ABRT will get picked up by Breakpad's signal handler + os.kill(proc.pid, signal.SIGABRT) + return + elif self.IS_WIN32: + # We should have a "crashinject" program in our utility path + crashinject = os.path.normpath(os.path.join(utilityPath, "crashinject.exe")) + if os.path.exists(crashinject) and subprocess.Popen([crashinject, str(proc.pid)]).wait() == 0: + return + #TODO: kill the process such that it triggers Breakpad on OS X (bug 525296) + self.log.info("Can't trigger Breakpad, just killing process") + proc.kill() + + def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime): + """ Look for timeout or crashes and return the status after the process terminates """ + stackFixerProcess = None + stackFixerModule = None + didTimeout = False + if proc.stdout is None: + self.log.info("TEST-INFO: Not logging stdout or stderr due to debugger connection") else: - args.append((testURL)) - args.extend(extraArgs) + logsource = proc.stdout + if self.IS_DEBUG_BUILD and self.IS_LINUX: + # Run logsource through fix-linux-stack.pl + stackFixerProcess = self.Process([self.PERL, os.path.join(utilityPath, "fix-linux-stack.pl")], + stdin=logsource, + stdout=subprocess.PIPE) + logsource = stackFixerProcess.stdout - startTime = datetime.now() + if self.IS_DEBUG_BUILD and self.IS_MAC: + # Import fix_macosx_stack.py from utilityPath + sys.path.insert(0, utilityPath) + import fix_macosx_stack as stackFixerModule + del sys.path[0] + + (line, didTimeout) = self.readWithTimeout(logsource, timeout) + hitMaxTime = False + while line != "" and not didTimeout: + if stackFixerModule: + line = stackFixerModule.fixSymbols(line) + self.log.info(line.rstrip()) + (line, didTimeout) = self.readWithTimeout(logsource, timeout) + if not hitMaxTime and maxTime and datetime.now() - startTime > timedelta(seconds = maxTime): + # Kill the application, but continue reading from stack fixer so as not to deadlock on stackFixerProcess.wait(). + hitMaxTime = True + self.log.info("TEST-UNEXPECTED-FAIL | automation.py | application ran for longer than allowed maximum time of %d seconds", int(maxTime)) + self.triggerBreakpad(proc, utilityPath) + if didTimeout: + self.log.info("TEST-UNEXPECTED-FAIL | automation.py | application timed out after %d seconds with no output", int(timeout)) + self.triggerBreakpad(proc, utilityPath) + + status = proc.wait() + if status != 0 and not didTimeout and not hitMaxTime: + self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Exited with code %d during test run", status) + if stackFixerProcess is not None: + fixerStatus = stackFixerProcess.wait() + if fixerStatus != 0 and not didTimeout and not hitMaxTime: + self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Stack fixer process exited with code %d during test run", fixerStatus) + return status - # Don't redirect stdout and stderr if an interactive debugger is attached - if debuggerInfo and debuggerInfo["interactive"]: - outputPipe = None - else: - outputPipe = subprocess.PIPE + def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs): + """ build the application command line """ + + cmd = app + if self.IS_MAC and not self.IS_CAMINO and not cmd.endswith("-bin"): + cmd += "-bin" + cmd = os.path.abspath(cmd) + + args = [] + + if debuggerInfo: + args.extend(debuggerInfo["args"]) + args.append(cmd) + cmd = os.path.abspath(debuggerInfo["path"]) + + if self.IS_MAC: + args.append("-foreground") + + if self.IS_CYGWIN: + profileDirectory = commands.getoutput("cygpath -w \"" + profileDir + "/\"") + else: + profileDirectory = profileDir + "/" + + args.extend(("-no-remote", "-profile", profileDirectory)) + if testURL is not None: + if self.IS_CAMINO: + args.extend(("-url", testURL)) + else: + args.append((testURL)) + args.extend(extraArgs) + return cmd, args + + def checkForZombies(self, processLog): + """ Look for hung processes """ + if not os.path.exists(processLog): + self.log.info('INFO | automation.py | PID log not found: %s', processLog) + else: + self.log.info('INFO | automation.py | Reading PID log: %s', processLog) + processList = [] + pidRE = re.compile(r'launched child process (\d+)$') + processLogFD = open(processLog) + for line in processLogFD: + self.log.info(line.rstrip()) + m = pidRE.search(line) + if m: + processList.append(int(m.group(1))) + processLogFD.close() + + for processPID in processList: + self.log.info("INFO | automation.py | Checking for orphan process with PID: %d", processPID) + if self.isPidAlive(processPID): + self.log.info("TEST-UNEXPECTED-FAIL | automation.py | child process %d still alive after shutdown", processPID) + self.killPid(processPID) + + def runApp(self, testURL, env, app, profileDir, extraArgs, + runSSLTunnel = False, utilityPath = None, + xrePath = None, certPath = None, + debuggerInfo = None, symbolsPath = None, + timeout = -1, maxTime = None): + """ + Run the app, log the duration it took to execute, return the status code. + Kills the app if it runs for longer than |maxTime| seconds, or outputs nothing for |timeout| seconds. + """ + + if utilityPath == None: + utilityPath = self.DIST_BIN + if xrePath == None: + xrePath = self.DIST_BIN + if certPath == None: + certPath = self.CERTS_SRC_DIR + if timeout == -1: + timeout = self.DEFAULT_TIMEOUT + + # copy env so we don't munge the caller's environment + env = dict(env); + env["NO_EM_RESTART"] = "1" + tmpfd, processLog = tempfile.mkstemp(suffix='pidlog') + os.close(tmpfd) + env["MOZ_PROCESS_LOG"] = processLog + + if self.IS_TEST_BUILD and runSSLTunnel: + # create certificate database for the profile + certificateStatus = self.fillCertificateDB(profileDir, certPath, utilityPath, xrePath) + if certificateStatus != 0: + self.log.info("TEST-UNEXPECTED FAIL | automation.py | Certificate integration failed") + return certificateStatus + + # start ssltunnel to provide https:// URLs capability + ssltunnel = os.path.join(utilityPath, "ssltunnel" + self.BIN_SUFFIX) + ssltunnelProcess = self.Process([ssltunnel, + os.path.join(profileDir, "ssltunnel.cfg")], + env = self.environment(xrePath = xrePath)) + self.log.info("INFO | automation.py | SSL tunnel pid: %d", ssltunnelProcess.pid) + + cmd, args = self.buildCommandLine(app, debuggerInfo, profileDir, testURL, extraArgs) + startTime = datetime.now() + + # Don't redirect stdout and stderr if an interactive debugger is attached + if debuggerInfo and debuggerInfo["interactive"]: + outputPipe = None + else: + outputPipe = subprocess.PIPE - proc = Process([cmd] + args, - env = environment(env, xrePath = xrePath, + proc = self.Process([cmd] + args, + env = self.environment(env, xrePath = xrePath, crashreporter = not debuggerInfo), stdout = outputPipe, stderr = subprocess.STDOUT) - log.info("INFO | automation.py | Application pid: %d", proc.pid) + self.log.info("INFO | automation.py | Application pid: %d", proc.pid) - stackFixerProcess = None - if outputPipe is None: - log.info("TEST-INFO: Not logging stdout or stderr due to debugger connection") - else: - logsource = proc.stdout - if IS_DEBUG_BUILD: - stackFixerCommand = None - if IS_MAC: - stackFixerCommand = "fix-macosx-stack.pl" - elif IS_LINUX: - stackFixerCommand = "fix-linux-stack.pl" - if stackFixerCommand is not None: - stackFixerProcess = Process([PERL, os.path.join(utilityPath, stackFixerCommand)], stdin=logsource, stdout=subprocess.PIPE) - logsource = stackFixerProcess.stdout + status = self.waitForFinish(proc, utilityPath, timeout, maxTime, startTime) + self.log.info("INFO | automation.py | Application ran for: %s", str(datetime.now() - startTime)) - line = logsource.readline() - while line != "": - log.info(line.rstrip()) - line = logsource.readline() - - status = proc.wait() - if status != 0: - log.info("TEST-UNEXPECTED-FAIL | automation.py | Exited with code %d during test run", status) - if stackFixerProcess is not None: - status = stackFixerProcess.wait() - if status != 0: - log.info("TEST-UNEXPECTED-FAIL | automation.py | Stack fixer process exited with code %d during test run", status) - log.info("INFO | automation.py | Application ran for: %s", str(datetime.now() - startTime)) + # Do a final check for zombie child processes. + self.checkForZombies(processLog) + self.automationutils.checkForCrashes(os.path.join(profileDir, "minidumps"), symbolsPath) - if checkForCrashes(os.path.join(profileDir, "minidumps"), symbolsPath): - status = -1 + if os.path.exists(processLog): + os.unlink(processLog) - if IS_TEST_BUILD and runSSLTunnel: - ssltunnelProcess.kill() + if self.IS_TEST_BUILD and runSSLTunnel: + ssltunnelProcess.kill() - return status + return status diff -Nru firefox-3.6.3+nobinonly/mozilla/build/automationutils.py firefox-3.6.4+build1+nobinonly/mozilla/build/automationutils.py --- firefox-3.6.3+nobinonly/mozilla/build/automationutils.py 2010-04-02 16:57:47.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/build/automationutils.py 2010-04-16 17:31:32.000000000 +0100 @@ -36,7 +36,7 @@ # # ***** END LICENSE BLOCK ***** */ -import glob, logging, os, subprocess, sys +import glob, logging, os, shutil, subprocess, sys import re __all__ = [ @@ -44,8 +44,28 @@ "checkForCrashes", "dumpLeakLog", "processLeakLog", + "getDebuggerInfo", + "DEBUGGER_INFO", ] +# Map of debugging programs to information about them, like default arguments +# and whether or not they are interactive. +DEBUGGER_INFO = { + # gdb requires that you supply the '--args' flag in order to pass arguments + # after the executable name to the executable. + "gdb": { + "interactive": True, + "args": "-q --args" + }, + + # valgrind doesn't explain much about leaks unless you set the + # '--leak-check=full' flag. + "valgrind": { + "interactive": False, + "args": "--leak-check=full" + } +} + log = logging.getLogger() def addCommonOptions(parser, defaults={}): @@ -60,6 +80,17 @@ action = "store", type = "string", dest = "symbolsPath", default = defaults['SYMBOLS_PATH'], help = "absolute path to directory containing breakpad symbols") + parser.add_option("--debugger", + action = "store", dest = "debugger", + help = "use the given debugger to launch the application") + parser.add_option("--debugger-args", + action = "store", dest = "debuggerArgs", + help = "pass the given args to the debugger _before_ " + "the application on the command line") + parser.add_option("--debugger-interactive", + action = "store_true", dest = "debuggerInteractive", + help = "prevents the test harness from redirecting " + "stdout and stderr for interactive debuggers") def checkForCrashes(dumpDir, symbolsPath, testName=None): stackwalkPath = os.environ.get('MINIDUMP_STACKWALK', None) @@ -73,7 +104,7 @@ foundCrash = False dumps = glob.glob(os.path.join(dumpDir, '*.dmp')) for d in dumps: - log.info("TEST-UNEXPECTED-FAIL | %s | application crashed (minidump found)", testName) + log.info("PROCESS-CRASH | %s | application crashed (minidump found)", testName) if symbolsPath and stackwalkPath and os.path.exists(stackwalkPath): nullfd = open(os.devnull, 'w') # eat minidump_stackwalk errors @@ -87,13 +118,70 @@ else: if not os.path.exists(stackwalkPath): print "MINIDUMP_STACKWALK binary not found: %s" % stackwalkPath - os.remove(d) + dumpSavePath = os.environ.get('MINIDUMP_SAVE_PATH', None) + if dumpSavePath: + shutil.move(d, dumpSavePath) + print "Saved dump as %s" % os.path.join(dumpSavePath, + os.path.basename(d)) + else: + os.remove(d) extra = os.path.splitext(d)[0] + ".extra" if os.path.exists(extra): os.remove(extra) foundCrash = True return foundCrash + +def getFullPath(directory, path): + "Get an absolute path relative to 'directory'." + return os.path.normpath(os.path.join(directory, os.path.expanduser(path))) + +def searchPath(directory, path): + "Go one step beyond getFullPath and try the various folders in PATH" + # Try looking in the current working directory first. + newpath = getFullPath(directory, path) + if os.path.isfile(newpath): + return newpath + + # At this point we have to fail if a directory was given (to prevent cases + # like './gdb' from matching '/usr/bin/./gdb'). + if not os.path.dirname(path): + for dir in os.environ['PATH'].split(os.pathsep): + newpath = os.path.join(dir, path) + if os.path.isfile(newpath): + return newpath + return None + +def getDebuggerInfo(directory, debugger, debuggerArgs, debuggerInteractive = False): + + debuggerInfo = None + + if debugger: + debuggerPath = searchPath(directory, debugger) + if not debuggerPath: + print "Error: Path %s doesn't exist." % debugger + sys.exit(1) + + debuggerName = os.path.basename(debuggerPath).lower() + + def getDebuggerInfo(type, default): + if debuggerName in DEBUGGER_INFO and type in DEBUGGER_INFO[debuggerName]: + return DEBUGGER_INFO[debuggerName][type] + return default + + debuggerInfo = { + "path": debuggerPath, + "interactive" : getDebuggerInfo("interactive", False), + "args": getDebuggerInfo("args", "").split() + } + + if debuggerArgs: + debuggerInfo["args"] = debuggerArgs.split() + if debuggerInteractive: + debuggerInfo["interactive"] = debuggerInteractive + + return debuggerInfo + def dumpLeakLog(leakLogFile, filter = False): """Process the leak log, without parsing it. @@ -118,17 +206,11 @@ # Simply copy the log. log.info(leakReport.rstrip("\n")) -def processLeakLog(leakLogFile, leakThreshold = 0): - """Process the leak log, parsing it. - - Use this function if you want an additional PASS/FAIL summary. - It must be used with the |XPCOM_MEM_BLOAT_LOG| environment variable. +def processSingleLeakFile(leakLogFileName, PID, processType, leakThreshold): + """Process a single leak log, corresponding to the specified + process PID and type. """ - if not os.path.exists(leakLogFile): - log.info("WARNING | automationutils.processLeakLog() | refcount logging is off, so leaks can't be detected!") - return - # Per-Inst Leaked Total Rem ... # 0 TOTAL 17 192 419115886 2 ... # 833 nsTimerImpl 60 120 24726 2 ... @@ -136,7 +218,10 @@ r"(?P-?\d+)\s+(?P-?\d+)\s+" r"-?\d+\s+(?P-?\d+)") - leaks = open(leakLogFile, "r") + processString = "" + if PID and processType: + processString = "| %s process %s " % (processType, PID) + leaks = open(leakLogFileName, "r") for line in leaks: matches = lineRe.match(line) if (matches and @@ -146,10 +231,13 @@ log.info(line.rstrip()) leaks.close() - leaks = open(leakLogFile, "r") + leaks = open(leakLogFileName, "r") seenTotal = False + crashedOnPurpose = False prefix = "TEST-PASS" for line in leaks: + if line.find("purposefully crash") > -1: + crashedOnPurpose = True matches = lineRe.match(line) if not matches: continue @@ -158,7 +246,8 @@ bytesLeaked = int(matches.group("bytesLeaked")) numLeaked = int(matches.group("numLeaked")) if size < 0 or bytesLeaked < 0 or numLeaked < 0: - log.info("TEST-UNEXPECTED-FAIL | automationutils.processLeakLog() | negative leaks caught!") + log.info("TEST-UNEXPECTED-FAIL %s| automationutils.processLeakLog() | negative leaks caught!" % + processString) if name == "TOTAL": seenTotal = True elif name == "TOTAL": @@ -166,13 +255,14 @@ # Check for leaks. if bytesLeaked < 0 or bytesLeaked > leakThreshold: prefix = "TEST-UNEXPECTED-FAIL" - leakLog = "TEST-UNEXPECTED-FAIL | automationutils.processLeakLog() | leaked" \ - " %d bytes during test execution" % bytesLeaked + leakLog = "TEST-UNEXPECTED-FAIL %s| automationutils.processLeakLog() | leaked" \ + " %d bytes during test execution" % (processString, bytesLeaked) elif bytesLeaked > 0: - leakLog = "TEST-PASS | automationutils.processLeakLog() | WARNING leaked" \ - " %d bytes during test execution" % bytesLeaked + leakLog = "TEST-PASS %s| automationutils.processLeakLog() | WARNING leaked" \ + " %d bytes during test execution" % (processString, bytesLeaked) else: - leakLog = "TEST-PASS | automationutils.processLeakLog() | no leaks detected!" + leakLog = "TEST-PASS %s| automationutils.processLeakLog() | no leaks detected!" \ + % processString # Remind the threshold if it is not 0, which is the default/goal. if leakThreshold != 0: leakLog += " (threshold set at %d bytes)" % leakThreshold @@ -186,14 +276,50 @@ else: instance = "instance" rest = "" - log.info("%(prefix)s | automationutils.processLeakLog() | leaked %(numLeaked)d %(instance)s of %(name)s " + log.info("%(prefix)s %(process)s| automationutils.processLeakLog() | leaked %(numLeaked)d %(instance)s of %(name)s " "with size %(size)s bytes%(rest)s" % { "prefix": prefix, + "process": processString, "numLeaked": numLeaked, "instance": instance, "name": name, "size": matches.group("size"), "rest": rest }) if not seenTotal: - log.info("TEST-UNEXPECTED-FAIL | automationutils.processLeakLog() | missing output line for total leaks!") + if crashedOnPurpose: + log.info("INFO | automationutils.processLeakLog() | process %s was " \ + "deliberately crashed and thus has no leak log" % PID) + else: + log.info("TEST-UNEXPECTED-FAIL %s| automationutils.processLeakLog() | missing output line for total leaks!" % + processString) leaks.close() + + +def processLeakLog(leakLogFile, leakThreshold = 0): + """Process the leak log, including separate leak logs created + by child processes. + + Use this function if you want an additional PASS/FAIL summary. + It must be used with the |XPCOM_MEM_BLOAT_LOG| environment variable. + """ + + if not os.path.exists(leakLogFile): + log.info("WARNING | automationutils.processLeakLog() | refcount logging is off, so leaks can't be detected!") + return + + (leakLogFileDir, leakFileBase) = os.path.split(leakLogFile) + pidRegExp = re.compile(r".*?_([a-z]*)_pid(\d*)$") + if leakFileBase[-4:] == ".log": + leakFileBase = leakFileBase[:-4] + pidRegExp = re.compile(r".*?_([a-z]*)_pid(\d*).log$") + + for fileName in os.listdir(leakLogFileDir): + if fileName.find(leakFileBase) != -1: + thisFile = os.path.join(leakLogFileDir, fileName) + processPID = 0 + processType = None + m = pidRegExp.search(fileName) + if m: + processType = m.group(1) + processPID = m.group(2) + processSingleLeakFile(thisFile, processPID, processType, leakThreshold) diff -Nru firefox-3.6.3+nobinonly/mozilla/build/leaktest.py.in firefox-3.6.4+build1+nobinonly/mozilla/build/leaktest.py.in --- firefox-3.6.3+nobinonly/mozilla/build/leaktest.py.in 2010-04-02 16:57:47.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/build/leaktest.py.in 2010-04-16 17:31:32.000000000 +0100 @@ -46,18 +46,19 @@ import sys import logging from getopt import getopt -import automation +from automation import Automation PORT = 8888 SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0]))) PROFILE_DIRECTORY = os.path.abspath(os.path.join(SCRIPT_DIR, "./leakprofile")) -DIST_BIN = os.path.join(SCRIPT_DIR, automation.DIST_BIN) os.chdir(SCRIPT_DIR) class EasyServer(SocketServer.TCPServer): allow_reuse_address = True if __name__ == '__main__': + automation = Automation() + DIST_BIN = os.path.join(SCRIPT_DIR, automation.DIST_BIN) opts, extraArgs = getopt(sys.argv[1:], 'l:') if len(opts) > 0: try: @@ -72,18 +73,14 @@ t.start() automation.initializeProfile(PROFILE_DIRECTORY) - browserEnv = dict(os.environ) + browserEnv = automation.environment() - browserEnv["NO_EM_RESTART"] = "1" if not "XPCOM_DEBUG_BREAK" in browserEnv: browserEnv["XPCOM_DEBUG_BREAK"] = "stack" - if automation.UNIXISH: - browserEnv["LD_LIBRARY_PATH"] = os.path.join(SCRIPT_DIR, DIST_BIN) - browserEnv["MOZILLA_FIVE_HOME"] = os.path.join(SCRIPT_DIR, DIST_BIN) - browserEnv["GNOME_DISABLE_CRASH_DIALOG"] = "1" - url = "http://localhost:%d/bloatcycle.html" % PORT appPath = os.path.join(SCRIPT_DIR, automation.DEFAULT_APP) status = automation.runApp(url, browserEnv, appPath, PROFILE_DIRECTORY, - extraArgs) + # leaktest builds are slow, give up and + # don't use a timeout. + extraArgs, timeout=None) sys.exit(status) diff -Nru firefox-3.6.3+nobinonly/mozilla/build/macosx/universal/flight.mk firefox-3.6.4+build1+nobinonly/mozilla/build/macosx/universal/flight.mk --- firefox-3.6.3+nobinonly/mozilla/build/macosx/universal/flight.mk 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/build/macosx/universal/flight.mk 2010-04-16 17:31:32.000000000 +0100 @@ -67,7 +67,7 @@ INSTALLER_DIR = camino/installer MOZ_PKG_APPNAME = camino APPNAME = Camino.app -BUILDCONFIG_JAR = Contents/MacOS/chrome/embed.jar +BUILDCONFIG_JAR = Contents/MacOS/chrome/toolkit.jar else # } { MOZ_PKG_APPNAME = $(MOZ_APP_NAME) APPNAME = $(MOZ_APP_DISPLAYNAME)$(DBGTAG).app @@ -125,6 +125,8 @@ cp $(DIST_PPC)/test-package-stage/reftest/automation.py \ $(DIST_X86)/test-package-stage/reftest/; \ $(TOPSRCDIR)/build/macosx/universal/unify \ + --unify-with-sort "all-test-dirs\.list$$" \ $(DIST_PPC)/test-package-stage \ $(DIST_X86)/test-package-stage \ - $(DIST_UNI)/test-package-stage; fi + $(DIST_UNI)/test-package-stage; \ + fi diff -Nru firefox-3.6.3+nobinonly/mozilla/build/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/build/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/build/Makefile.in 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/build/Makefile.in 2010-04-16 17:31:31.000000000 +0100 @@ -99,8 +99,8 @@ $(INSTALL) $< $(DIST)/bin/res ifeq ($(OS_ARCH),Darwin) -libs:: $(topsrcdir)/tools/rb/fix-macosx-stack.pl - $(INSTALL) $< $(DIST)/bin +libs:: $(topsrcdir)/tools/rb/fix-macosx-stack.pl $(topsrcdir)/tools/rb/fix_macosx_stack.py + $(INSTALL) $^ $(DIST)/bin # Basic unit tests for some stuff in the unify script check:: @@ -121,7 +121,7 @@ echo "TEST-UNEXPECTED-FAIL | build/ | unify failed to produce a universal binary!"; \ false; \ else \ - echo "TEST-PASS | build/ | unify produced a universal binary"; \ + echo "TEST-PASS | build/ | unify produced a universal binary!"; \ fi # try unifying two identical Java class files rm -f unifytesta.class unifytestb.class unifytestc.class @@ -140,7 +140,7 @@ echo "TEST-UNEXPECTED-FAIL | build/ | unify failed to unify a Java class file!"; \ false; \ else \ - echo "TEST-PASS | build/ | unify unified a Java class file"; \ + echo "TEST-PASS | build/ | unify unified a Java class file!"; \ fi # try unifying some files that differ only in line ordering rm -rf unify-sort-test @@ -151,14 +151,14 @@ @if ! $(srcdir)/macosx/universal/unify --unify-with-sort "\.foo$$" \ ./unify-sort-test/a ./unify-sort-test/b \ ./unify-sort-test/c; then \ - echo "TEST-UNEXPECTED-FAIL | build/ | unify failed to unify files wit differing line ordering!"; \ + echo "TEST-UNEXPECTED-FAIL | build/ | unify failed to unify files with differing line ordering!"; \ false; \ fi @if ! diff -q ./unify-sort-test/expected-result ./unify-sort-test/c/file.foo; then \ - echo "TEST-UNEXPECTED-FAIL | build/ | unify failed to unify files wit differing line ordering!"; \ + echo "TEST-UNEXPECTED-FAIL | build/ | unify failed to unify files with differing line ordering!"; \ false; \ else \ - echo "TEST-PASS | build/ | unify unified files with differing line orering!"; \ + echo "TEST-PASS | build/ | unify unified files with differing line ordering!"; \ fi endif diff -Nru firefox-3.6.3+nobinonly/mozilla/build/pgo/genpgocert.py.in firefox-3.6.4+build1+nobinonly/mozilla/build/pgo/genpgocert.py.in --- firefox-3.6.3+nobinonly/mozilla/build/pgo/genpgocert.py.in 2010-04-02 16:57:48.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/build/pgo/genpgocert.py.in 2010-04-16 17:31:32.000000000 +0100 @@ -36,7 +36,7 @@ # # ***** END LICENSE BLOCK ***** -import automation +from automation import Automation import os import re import shutil @@ -47,6 +47,8 @@ #expand PROFILE_DIR = __PROFILE_DIR__ #expand CERTS_SRC_DIR = __CERTS_SRC_DIR__ +automation = Automation() + dbFiles = [ re.compile("^cert[0-9]+\.db$"), re.compile("^key[0-9]+\.db$"), diff -Nru firefox-3.6.3+nobinonly/mozilla/build/pgo/profileserver.py.in firefox-3.6.4+build1+nobinonly/mozilla/build/pgo/profileserver.py.in --- firefox-3.6.3+nobinonly/mozilla/build/pgo/profileserver.py.in 2010-04-02 16:57:48.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/build/pgo/profileserver.py.in 2010-04-16 17:31:32.000000000 +0100 @@ -46,7 +46,7 @@ import sys import shutil from datetime import datetime -import automation +from automation import Automation PORT = 8888 SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0]))) @@ -57,23 +57,20 @@ allow_reuse_address = True if __name__ == '__main__': + automation = Automation() httpd = EasyServer(("", PORT), SimpleHTTPServer.SimpleHTTPRequestHandler) t = threading.Thread(target=httpd.serve_forever) t.setDaemon(True) # don't hang on exit t.start() automation.initializeProfile(PROFILE_DIRECTORY) - browserEnv = dict(os.environ) - - # These variables are necessary for correct application startup; change - # via the commandline at your own risk. - browserEnv["NO_EM_RESTART"] = "1" + browserEnv = automation.environment() browserEnv["XPCOM_DEBUG_BREAK"] = "warn" - if automation.UNIXISH: - browserEnv["LD_LIBRARY_PATH"] = os.path.join(SCRIPT_DIR, automation.DIST_BIN) - browserEnv["MOZILLA_FIVE_HOME"] = os.path.join(SCRIPT_DIR, automation.DIST_BIN) url = "http://localhost:%d/index.html" % PORT appPath = os.path.join(SCRIPT_DIR, automation.DEFAULT_APP) - status = automation.runApp(url, browserEnv, appPath, PROFILE_DIRECTORY, {}) + status = automation.runApp(url, browserEnv, appPath, PROFILE_DIRECTORY, {}, + # the profiling HTML doesn't output anything, + # so let's just run this without a timeout + timeout = None) sys.exit(status) diff -Nru firefox-3.6.3+nobinonly/mozilla/build/win32/crashinject.cpp firefox-3.6.4+build1+nobinonly/mozilla/build/win32/crashinject.cpp --- firefox-3.6.3+nobinonly/mozilla/build/win32/crashinject.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/build/win32/crashinject.cpp 2010-04-16 17:31:32.000000000 +0100 @@ -0,0 +1,128 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Crash Injection Utility + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ted Mielczarek + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Given a PID, this program attempts to inject a DLL into the process + * with that PID. The DLL it attempts to inject, "crashinjectdll.dll", + * must exist alongside this exe. The DLL will then crash the process. + */ +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: crashinject \n"); + return 1; + } + + int pid = atoi(argv[1]); + if (pid <= 0) { + fprintf(stderr, "Usage: crashinject \n"); + return 1; + } + + // find our DLL to inject + wchar_t filename[_MAX_PATH]; + if (GetModuleFileNameW(NULL, filename, sizeof(filename) / sizeof(wchar_t)) == 0) + return 1; + + wchar_t* slash = wcsrchr(filename, L'\\'); + if (slash == NULL) + return 1; + + slash++; + wcscpy(slash, L"crashinjectdll.dll"); + + // now find our target process + HANDLE targetProc = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD, + FALSE, + pid); + if (targetProc == NULL) { + fprintf(stderr, "Error %d opening target process\n", GetLastError()); + return 1; + } + + /* + * This is sort of insane, but we're implementing a technique described here: + * http://www.codeproject.com/KB/threads/winspy.aspx#section_2 + * + * The gist is to use CreateRemoteThread to create a thread in the other + * process, but cheat and make the thread function kernel32!LoadLibrary, + * so that the only remote data we have to pass to the other process + * is the path to the library we want to load. The library we're loading + * will then do its dirty work inside the other process. + */ + HMODULE hKernel32 = GetModuleHandleW(L"Kernel32"); + // allocate some memory to hold the path in the remote process + void* pLibRemote = VirtualAllocEx(targetProc, NULL, sizeof(filename), + MEM_COMMIT, PAGE_READWRITE); + if (pLibRemote == NULL) { + fprintf(stderr, "Error %d in VirtualAllocEx\n", GetLastError()); + CloseHandle(targetProc); + return 1; + } + + if (!WriteProcessMemory(targetProc, pLibRemote, (void*)filename, + sizeof(filename), NULL)) { + fprintf(stderr, "Error %d in WriteProcessMemory\n", GetLastError()); + VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); + CloseHandle(targetProc); + return 1; + } + // Now create a thread in the target process that will load our DLL + HANDLE hThread = CreateRemoteThread( + targetProc, NULL, 0, + (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, + "LoadLibraryW"), + pLibRemote, 0, NULL); + if (hThread == NULL) { + fprintf(stderr, "Error %d in CreateRemoteThread\n", GetLastError()); + VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); + CloseHandle(targetProc); + return 1; + } + WaitForSingleObject(hThread, INFINITE); + // Cleanup, not that it's going to matter at this point + CloseHandle(hThread); + VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); + CloseHandle(targetProc); + + return 0; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/build/win32/crashinjectdll/crashinjectdll.cpp firefox-3.6.4+build1+nobinonly/mozilla/build/win32/crashinjectdll/crashinjectdll.cpp --- firefox-3.6.3+nobinonly/mozilla/build/win32/crashinjectdll/crashinjectdll.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/build/win32/crashinjectdll/crashinjectdll.cpp 2010-04-16 17:31:32.000000000 +0100 @@ -0,0 +1,34 @@ +#include +#include + +// make sure we only ever spawn one thread +DWORD tid = -1; + +DWORD WINAPI CrashingThread( + LPVOID lpParameter +) +{ + // not a very friendly DLL + volatile int* x = (int *)0x0; + *x = 1; + return 0; +} + +BOOL WINAPI DllMain( + HANDLE hinstDLL, + DWORD dwReason, + LPVOID lpvReserved +) +{ + if (tid == -1) + // we have to crash on another thread because LoadLibrary() will + // catch memory access errors and return failure to the calling process + CreateThread( + NULL, // default security attributes + 0, // use default stack size + CrashingThread , // thread function name + NULL, // argument to thread function + 0, // use default creation flags + &tid); // returns the thread identifier + return TRUE; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/build/win32/crashinjectdll/crashinjectdll.def firefox-3.6.4+build1+nobinonly/mozilla/build/win32/crashinjectdll/crashinjectdll.def --- firefox-3.6.3+nobinonly/mozilla/build/win32/crashinjectdll/crashinjectdll.def 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/build/win32/crashinjectdll/crashinjectdll.def 2010-04-16 17:31:32.000000000 +0100 @@ -0,0 +1,3 @@ +LIBRARY crashinjectdll +EXPORTS + DllMain diff -Nru firefox-3.6.3+nobinonly/mozilla/build/win32/crashinjectdll/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/build/win32/crashinjectdll/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/build/win32/crashinjectdll/Makefile.in 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/build/win32/crashinjectdll/Makefile.in 2010-04-16 17:31:32.000000000 +0100 @@ -0,0 +1,53 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (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.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla core build scripts. +# +# The Initial Developer of the Original Code is +# The Mozilla Foundation +# +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Mozilla Foundation . All Rights Reserved. +# +# Contributor(s): +# Ted Mielczarek +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +LIBRARY_NAME = crashinjectdll +DEFFILE = $(srcdir)/crashinjectdll.def +FORCE_SHARED_LIB = 1 +USE_STATIC_LIBS = 1 + +CPPSRCS = crashinjectdll.cpp + +include $(topsrcdir)/config/rules.mk diff -Nru firefox-3.6.3+nobinonly/mozilla/build/win32/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/build/win32/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/build/win32/Makefile.in 2010-04-02 16:57:48.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/build/win32/Makefile.in 2010-04-16 17:31:32.000000000 +0100 @@ -42,6 +42,15 @@ include $(DEPTH)/config/autoconf.mk +ifdef ENABLE_TESTS +DIRS += crashinjectdll + +PROGRAM = crashinject$(BIN_SUFFIX) +USE_STATIC_LIBS = 1 +CPPSRCS = crashinject.cpp + +endif + include $(topsrcdir)/config/rules.mk ifdef WIN32_REDIST_DIR @@ -64,13 +73,18 @@ $(NULL) endif +ifeq (1600,$(_MSC_VER)) +REDIST_FILES = \ + msvcp100.dll \ + msvcr100.dll \ + $(NULL) +endif + endif ifdef REDIST_FILES libs:: mkdir -p $(FINAL_TARGET) - for file in $(REDIST_FILES) ; do \ - install --preserve-timestamps "$(WIN32_REDIST_DIR)"/$$file $(FINAL_TARGET) ; \ - done + install --preserve-timestamps $(foreach f,$(REDIST_FILES),"$(WIN32_REDIST_DIR)"/$(f)) $(FINAL_TARGET) endif diff -Nru firefox-3.6.3+nobinonly/mozilla/config/autoconf.mk.in firefox-3.6.4+build1+nobinonly/mozilla/config/autoconf.mk.in --- firefox-3.6.3+nobinonly/mozilla/config/autoconf.mk.in 2010-04-02 16:57:48.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/config/autoconf.mk.in 2010-04-16 17:31:32.000000000 +0100 @@ -106,6 +106,8 @@ MOZ_IMG_DECODERS= @MOZ_IMG_DECODERS@ MOZ_IMG_ENCODERS= @MOZ_IMG_ENCODERS@ MOZ_JSDEBUGGER = @MOZ_JSDEBUGGER@ +MOZ_IPC = @MOZ_IPC@ +MOZ_IPDL_TESTS = @MOZ_IPDL_TESTS@ MOZ_PERF_METRICS = @MOZ_PERF_METRICS@ MOZ_LEAKY = @MOZ_LEAKY@ MOZ_MEMORY = @MOZ_MEMORY@ diff -Nru firefox-3.6.3+nobinonly/mozilla/config/milestone.txt firefox-3.6.4+build1+nobinonly/mozilla/config/milestone.txt --- firefox-3.6.3+nobinonly/mozilla/config/milestone.txt 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/config/milestone.txt 2010-04-16 17:33:03.000000000 +0100 @@ -10,4 +10,4 @@ # hardcoded milestones in the tree from these two files. #-------------------------------------------------------- -1.9.2.3 +1.9.2.4 diff -Nru firefox-3.6.3+nobinonly/mozilla/config/rules.mk firefox-3.6.4+build1+nobinonly/mozilla/config/rules.mk --- firefox-3.6.3+nobinonly/mozilla/config/rules.mk 2010-04-02 16:57:48.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/config/rules.mk 2010-04-16 17:31:32.000000000 +0100 @@ -973,6 +973,7 @@ # creates OBJS, links with LIBS to create Foo # $(PROGRAM): $(PROGOBJS) $(LIBS_DEPS) $(EXTRA_DEPS) $(EXE_DEF_FILE) $(RESFILE) $(GLOBAL_DEPS) + @rm -f $@.manifest ifeq (WINCE,$(OS_ARCH)) $(LD) -NOLOGO -OUT:$@ $(WIN32_EXE_LDFLAGS) $(LDFLAGS) $(PROGOBJS) $(RESFILE) $(LIBS) $(EXTRA_LIBS) $(OS_LIBS) else @@ -981,11 +982,15 @@ ifdef MSMANIFEST_TOOL @if test -f $@.manifest; then \ if test -f "$(srcdir)/$@.manifest"; then \ + echo "Embedding manifest from $(srcdir)/$@.manifest and $@.manifest"; \ mt.exe -NOLOGO -MANIFEST "$(win_srcdir)/$@.manifest" $@.manifest -OUTPUTRESOURCE:$@\;1; \ else \ + echo "Embedding manifest from $@.manifest"; \ mt.exe -NOLOGO -MANIFEST $@.manifest -OUTPUTRESOURCE:$@\;1; \ fi; \ - rm -f $@.manifest; \ + elif test -f "$(srcdir)/$@.manifest"; then \ + echo "Embedding manifest from $(srcdir)/$@.manifest"; \ + mt.exe -NOLOGO -MANIFEST "$(win_srcdir)/$@.manifest" -OUTPUTRESOURCE:$@\;1; \ fi endif # MSVC with manifest tool else diff -Nru firefox-3.6.3+nobinonly/mozilla/config/static-checking-config.mk firefox-3.6.4+build1+nobinonly/mozilla/config/static-checking-config.mk --- firefox-3.6.3+nobinonly/mozilla/config/static-checking-config.mk 2010-04-02 16:57:48.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/config/static-checking-config.mk 2010-04-16 17:31:32.000000000 +0100 @@ -5,6 +5,8 @@ DEHYDRA_MODULES = \ $(topsrcdir)/xpcom/analysis/final.js \ + $(topsrcdir)/xpcom/analysis/override.js \ + $(topsrcdir)/xpcom/analysis/must-override.js \ $(NULL) TREEHYDRA_MODULES = \ diff -Nru firefox-3.6.3+nobinonly/mozilla/config/static-checking.js firefox-3.6.4+build1+nobinonly/mozilla/config/static-checking.js --- firefox-3.6.3+nobinonly/mozilla/config/static-checking.js 2010-04-02 16:57:48.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/config/static-checking.js 2010-04-16 17:31:32.000000000 +0100 @@ -57,6 +57,31 @@ return false; } +// This is useful for detecting method overrides +function signaturesMatch(m1, m2) +{ + if (m1.shortName != m2.shortName) + return false; + + if (m1.isVirtual != m2.isVirtual) + return false; + + if (m1.isStatic != m2.isStatic) + return false; + + let p1 = m1.type.parameters; + let p2 = m2.type.parameters; + + if (p1.length != p2.length) + return false; + + for (let i = 0; i < p1.length; ++i) + if (p1[i] !== p2[i]) + return false; + + return true; +} + const forward_functions = [ 'process_type', 'process_tree_type', diff -Nru firefox-3.6.3+nobinonly/mozilla/config/system-headers firefox-3.6.4+build1+nobinonly/mozilla/config/system-headers --- firefox-3.6.3+nobinonly/mozilla/config/system-headers 2010-04-02 16:57:48.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/config/system-headers 2010-04-16 17:31:32.000000000 +0100 @@ -76,6 +76,7 @@ #define WRAP_CAIRO_HEADERS #endif #ifdef WRAP_CAIRO_HEADERS +pixman.h cairo.h cairo-atsui.h cairo-beos.h diff -Nru firefox-3.6.3+nobinonly/mozilla/configure.in firefox-3.6.4+build1+nobinonly/mozilla/configure.in --- firefox-3.6.3+nobinonly/mozilla/configure.in 2010-04-02 16:57:48.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/configure.in 2010-04-16 17:31:32.000000000 +0100 @@ -130,7 +130,7 @@ LIBGNOME_VERSION=2.0 STARTUP_NOTIFICATION_VERSION=0.8 DBUS_VERSION=0.60 -SQLITE_VERSION=3.6.16.1 +SQLITE_VERSION=3.6.22 LIBNOTIFY_VERSION=0.4 MSMANIFEST_TOOL= @@ -461,11 +461,7 @@ WINVER=500 ;; *) - if test -n "$GNU_CC"; then - WINVER=501 - else - WINVER=500 - fi + WINVER=502 ;; esac @@ -1130,6 +1126,9 @@ cygwin*|mingw*|mks*|msvc*) HOST_OS_ARCH=WINNT ;; +darwin*) + HOST_OS_ARCH=Darwin + ;; linux*) HOST_OS_ARCH=Linux ;; @@ -4179,7 +4178,7 @@ dnl ======================================================== MOZ_ARG_HEADER(External Packages) -MOZ_ENABLE_LIBXUL= +MOZ_ENABLE_LIBXUL=1 MOZ_ARG_WITH_STRING(libxul-sdk, [ --with-libxul-sdk=PFX Use the libXUL SDK at ], @@ -4508,6 +4507,7 @@ MOZ_FEEDS=1 MOZ_IMG_DECODERS_DEFAULT="png gif jpeg bmp icon" MOZ_IMG_ENCODERS_DEFAULT="png jpeg" +MOZ_IPC=1 MOZ_JAVAXPCOM= MOZ_JSDEBUGGER=1 MOZ_JSLOADER=1 @@ -5344,6 +5344,49 @@ dnl ======================================================== +dnl = Disable IPC support for tabs and plugins +dnl ======================================================== +case "${target}" in +*-apple-darwin*) + MOZ_IPC= + ;; +*-wince*) + MOZ_IPC= + ;; +esac + +MOZ_ARG_DISABLE_BOOL(ipc, +[ --disable-ipc Disable IPC supports for tabs and plugins], + MOZ_IPC=, + MOZ_IPC=1) + +if test -n "$MOZ_IPC"; then + AC_DEFINE(MOZ_IPC) +fi + +AC_SUBST(MOZ_IPC) + +dnl ======================================================== +dnl = Enable IPDL's "expensive" unit tests +dnl ======================================================== +MOZ_IPDL_TESTS= + +MOZ_ARG_ENABLE_BOOL(ipdl-tests, +[ --enable-ipdl-tests Enable expensive IPDL tests], + MOZ_IPDL_TESTS=1, + MOZ_IPDL_TESTS=) + +if test -z "$MOZ_IPC" -a -n "$MOZ_IPDL_TESTS"; then + AC_MSG_ERROR([--enable-ipdl-tests requires --enable-ipc]) +fi + +if test -n "$MOZ_IPDL_TESTS"; then + AC_DEFINE(MOZ_IPDL_TESTS) +fi + +AC_SUBST(MOZ_IPDL_TESTS) + +dnl ======================================================== dnl = Disable plugin support dnl ======================================================== MOZ_ARG_DISABLE_BOOL(plugins, @@ -6397,6 +6440,13 @@ if test -z "$_LIB_FOUND"; then AC_MSG_ERROR([Hildon FM-2 is required when building for Maemo]) fi + + PKG_CHECK_MODULES(LIBLOCATION,liblocation, _LIB_FOUND=1, _LIB_FOUND=) + MOZ_PLATFORM_MAEMO_LIBS="$MOZ_PLATFORM_MAEMO_LIBS $LIBLOCATION_LIBS" + MOZ_PLATFORM_MAEMO_CFLAGS="$MOZ_PLATFORM_MAEMO_CFLAGS $LIBLOCATION_CFLAGS" + if test -z "$_LIB_FOUND"; then + AC_MSG_ERROR([liblocation is required when building for Maemo]) + fi fi AC_SUBST(MOZ_PLATFORM_MAEMO_LIBS) @@ -6715,9 +6765,7 @@ WIN32_CRT_SRC_DIR=`cd "$WIN32_CRT_SRC_DIR" && pwd -W` _objdir_win=`pwd -W` WIN32_CUSTOM_CRT_DIR="$_objdir_win/memory/jemalloc/crtsrc/build/intel" - dnl Statically link the C++ stdlib. We only use this for Breakpad anyway. - AC_DEFINE(_STATIC_CPPLIB) - MOZ_MEMORY_LDFLAGS="-MANIFEST:NO -LIBPATH:\"$WIN32_CUSTOM_CRT_DIR\" -NODEFAULTLIB:msvcrt -NODEFAULTLIB:msvcrtd -DEFAULTLIB:mozcrt19" + MOZ_MEMORY_LDFLAGS="-MANIFEST:NO -LIBPATH:\"$WIN32_CUSTOM_CRT_DIR\" -NODEFAULTLIB:msvcrt -NODEFAULTLIB:msvcrtd -NODEFAULTLIB:msvcprt -NODEFAULTLIB:msvcprtd -DEFAULTLIB:mozcrt19 -DEFAULTLIB:mozcpp19" dnl Also pass this to NSPR/NSS DLLFLAGS="$DLLFLAGS $MOZ_MEMORY_LDFLAGS" export DLLFLAGS @@ -7378,6 +7426,7 @@ dnl C++ rtti dnl Should be smarter and check that the compiler does indeed have rtti dnl ======================================================== + MOZ_ARG_ENABLE_BOOL(cpp-rtti, [ --enable-cpp-rtti Enable C++ RTTI ], [ _MOZ_USE_RTTI=1 ], @@ -7486,11 +7535,6 @@ BUILD_STATIC_LIBS=1, BUILD_STATIC_LIBS=) -dnl Disable libxul in debug builds, but not for xulrunner. -if test -n "$MOZ_DEBUG" -a "$MOZ_BUILD_APP" != "xulrunner"; then - MOZ_ENABLE_LIBXUL= -fi - MOZ_ARG_ENABLE_BOOL(libxul, [ --enable-libxul Enable building of libxul], MOZ_ENABLE_LIBXUL=1, @@ -7504,6 +7548,10 @@ AC_MSG_ERROR([--enable-libxul is not compatible with --enable-static]) fi +if test -n "$MOZ_IPC" -a -z "$MOZ_ENABLE_LIBXUL"; then + AC_MSG_ERROR([--enable-ipc requires --enable-libxul]) +fi + AC_SUBST(LIBXUL_LIBS) if test -n "$MOZ_ENABLE_LIBXUL"; then @@ -7803,7 +7851,7 @@ mv -f $CAIRO_FEATURES_H "$CAIRO_FEATURES_H".orig 2> /dev/null else - PKG_CHECK_MODULES(CAIRO, cairo >= $CAIRO_VERSION freetype2 fontconfig) + PKG_CHECK_MODULES(CAIRO, cairo >= $CAIRO_VERSION pixman-1 freetype2 fontconfig) MOZ_CAIRO_CFLAGS=$CAIRO_CFLAGS MOZ_CAIRO_LIBS=$CAIRO_LIBS if test "$MOZ_X11"; then diff -Nru firefox-3.6.3+nobinonly/mozilla/content/base/public/nsIObjectLoadingContent.idl firefox-3.6.4+build1+nobinonly/mozilla/content/base/public/nsIObjectLoadingContent.idl --- firefox-3.6.3+nobinonly/mozilla/content/base/public/nsIObjectLoadingContent.idl 2010-04-02 16:57:48.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/content/base/public/nsIObjectLoadingContent.idl 2010-04-16 17:31:33.000000000 +0100 @@ -39,6 +39,7 @@ interface nsIObjectFrame; interface nsIPluginInstance; +interface nsIPluginTag; interface nsIDOMElement; interface nsIDOMClientRect; @@ -80,7 +81,6 @@ */ [noscript] readonly attribute nsIPluginInstance pluginInstance; - /** * Makes sure that a frame for this object exists, and that the plugin is * instantiated. This method does nothing if the type is not #TYPE_PLUGIN. @@ -112,7 +112,7 @@ /** * This interface extends the nsIObjectLoadingContent for the 1.9.2 branch */ -[scriptable, uuid(f91247a2-fb8b-42d3-a9b6-8f1f49685c43)] +[scriptable, uuid(2725a137-db4b-4e43-a096-a084aeaa8b0b)] interface nsIObjectLoadingContent_MOZILLA_1_9_2_BRANCH : nsISupports { /** @@ -122,4 +122,9 @@ void setAbsoluteScreenPosition(in nsIDOMElement element, in nsIDOMClientRect position, in nsIDOMClientRect clip); + + [noscript] void pluginCrashed(in nsIPluginTag pluginTag, + in AString pluginDumpID, + in AString browserDumpID, + in boolean submittedCrashReport); }; diff -Nru firefox-3.6.3+nobinonly/mozilla/content/base/src/nsDocument.cpp firefox-3.6.4+build1+nobinonly/mozilla/content/base/src/nsDocument.cpp --- firefox-3.6.3+nobinonly/mozilla/content/base/src/nsDocument.cpp 2010-04-02 16:57:48.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/content/base/src/nsDocument.cpp 2010-04-16 17:33:04.000000000 +0100 @@ -46,6 +46,11 @@ * Base class for all our document implementations. */ +#ifdef MOZ_LOGGING +// so we can get logging even in release builds +#define FORCE_PR_LOG 1 +#endif +#include "prlog.h" #include "plstr.h" #include "prprf.h" @@ -179,13 +184,6 @@ #include "nsSVGUtils.h" #endif // MOZ_SMIL - -#ifdef MOZ_LOGGING -// so we can get logging even in release builds -#define FORCE_PR_LOG 1 -#endif -#include "prlog.h" - #ifdef PR_LOGGING static PRLogModuleInfo* gDocumentLeakPRLog; #endif diff -Nru firefox-3.6.3+nobinonly/mozilla/content/base/src/nsObjectLoadingContent.cpp firefox-3.6.4+build1+nobinonly/mozilla/content/base/src/nsObjectLoadingContent.cpp --- firefox-3.6.3+nobinonly/mozilla/content/base/src/nsObjectLoadingContent.cpp 2010-04-02 16:57:48.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/content/base/src/nsObjectLoadingContent.cpp 2010-04-16 17:31:33.000000000 +0100 @@ -21,6 +21,7 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): + * Justin Dolske * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -44,9 +45,13 @@ // Interface headers #include "imgILoader.h" +#include "nsEventDispatcher.h" #include "nsIContent.h" #include "nsIDocShell.h" #include "nsIDocument.h" +#include "nsIDOMDataContainerEvent.h" +#include "nsIDOMDocumentEvent.h" +#include "nsIDOMEventTarget.h" #include "nsIExternalProtocolHandler.h" #include "nsIEventStateManager.h" #include "nsIObjectFrame.h" @@ -54,6 +59,7 @@ #include "nsIPluginHost.h" #include "nsIPluginInstance.h" #include "nsIPresShell.h" +#include "nsIPrivateDOMEvent.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptSecurityManager.h" #include "nsIStreamConverterService.h" @@ -80,6 +86,7 @@ #include "nsPresShellIterator.h" #include "nsMimeTypes.h" #include "nsStyleUtil.h" +#include "nsGUIEvent.h" // Concrete classes #include "nsFrameLoader.h" @@ -210,6 +217,115 @@ return NS_OK; } +/** + * A task for firing PluginCrashed DOM Events. + */ +class nsPluginCrashedEvent : public nsRunnable { +public: + nsCOMPtr mContent; + nsString mPluginDumpID; + nsString mBrowserDumpID; + nsString mPluginName; + nsString mPluginFilename; + PRBool mSubmittedCrashReport; + + nsPluginCrashedEvent(nsIContent* aContent, + const nsAString& aPluginDumpID, + const nsAString& aBrowserDumpID, + const nsAString& aPluginName, + const nsAString& aPluginFilename, + PRBool submittedCrashReport) + : mContent(aContent), + mPluginDumpID(aPluginDumpID), + mBrowserDumpID(aBrowserDumpID), + mPluginName(aPluginName), + mPluginFilename(aPluginFilename), + mSubmittedCrashReport(submittedCrashReport) + {} + + ~nsPluginCrashedEvent() {} + + NS_IMETHOD Run(); +}; + +NS_IMETHODIMP +nsPluginCrashedEvent::Run() +{ + LOG(("OBJLC []: Firing plugin crashed event for content %p\n", + mContent.get())); + + nsCOMPtr domEventDoc = + do_QueryInterface(mContent->GetDocument()); + if (!domEventDoc) { + NS_WARNING("Couldn't get document for PluginCrashed event!"); + return NS_OK; + } + + nsCOMPtr event; + domEventDoc->CreateEvent(NS_LITERAL_STRING("datacontainerevents"), + getter_AddRefs(event)); + nsCOMPtr privateEvent(do_QueryInterface(event)); + nsCOMPtr containerEvent(do_QueryInterface(event)); + if (!privateEvent || !containerEvent) { + NS_WARNING("Couldn't QI event for PluginCrashed event!"); + return NS_OK; + } + + event->InitEvent(NS_LITERAL_STRING("PluginCrashed"), PR_TRUE, PR_TRUE); + privateEvent->SetTrusted(PR_TRUE); + privateEvent->GetInternalNSEvent()->flags |= NS_EVENT_FLAG_ONLY_CHROME_DISPATCH; + + nsCOMPtr variant; + + // add a "pluginDumpID" property to this event + variant = do_CreateInstance("@mozilla.org/variant;1"); + if (!variant) { + NS_WARNING("Couldn't create pluginDumpID variant for PluginCrashed event!"); + return NS_OK; + } + variant->SetAsAString(mPluginDumpID); + containerEvent->SetData(NS_LITERAL_STRING("pluginDumpID"), variant); + + // add a "browserDumpID" property to this event + variant = do_CreateInstance("@mozilla.org/variant;1"); + if (!variant) { + NS_WARNING("Couldn't create browserDumpID variant for PluginCrashed event!"); + return NS_OK; + } + variant->SetAsAString(mBrowserDumpID); + containerEvent->SetData(NS_LITERAL_STRING("browserDumpID"), variant); + + // add a "pluginName" property to this event + variant = do_CreateInstance("@mozilla.org/variant;1"); + if (!variant) { + NS_WARNING("Couldn't create pluginName variant for PluginCrashed event!"); + return NS_OK; + } + variant->SetAsAString(mPluginName); + containerEvent->SetData(NS_LITERAL_STRING("pluginName"), variant); + + // add a "pluginFilename" property to this event + variant = do_CreateInstance("@mozilla.org/variant;1"); + if (!variant) { + NS_WARNING("Couldn't create pluginFilename variant for PluginCrashed event!"); + return NS_OK; + } + variant->SetAsAString(mPluginFilename); + containerEvent->SetData(NS_LITERAL_STRING("pluginFilename"), variant); + + // add a "submittedCrashReport" property to this event + variant = do_CreateInstance("@mozilla.org/variant;1"); + if (!variant) { + NS_WARNING("Couldn't create crashSubmit variant for PluginCrashed event!"); + return NS_OK; + } + variant->SetAsBool(mSubmittedCrashReport); + containerEvent->SetData(NS_LITERAL_STRING("submittedCrashReport"), variant); + + nsEventDispatcher::DispatchDOMEvent(mContent, nsnull, event, nsnull, nsnull); + return NS_OK; +} + class AutoNotifier { public: AutoNotifier(nsObjectLoadingContent* aContent, PRBool aNotify) : @@ -256,7 +372,7 @@ LOG(("OBJLC [%p]: rv=%08x, falling back\n", mContent, *mResult)); mContent->Fallback(PR_FALSE); if (mPluginState != ePluginOtherState) { - mContent->mPluginState = mPluginState; + mContent->mFallbackReason = mPluginState; } } } @@ -359,7 +475,7 @@ , mInstantiating(PR_FALSE) , mUserDisabled(PR_FALSE) , mSuppressed(PR_FALSE) - , mPluginState(ePluginOtherState) + , mFallbackReason(ePluginOtherState) { } @@ -598,7 +714,7 @@ case eType_Null: LOG(("OBJLC [%p]: Unsupported type, falling back\n", this)); // Need to fallback here (instead of using the case below), so that we can - // set mPluginState without it being overwritten. This is also why we + // set mFallbackReason without it being overwritten. This is also why we // return early. Fallback(PR_FALSE); @@ -606,8 +722,8 @@ mContentType); // Do nothing, but fire the plugin not found event if needed if (pluginState != ePluginOtherState) { + mFallbackReason = pluginState; FirePluginError(thisContent, pluginState); - mPluginState = pluginState; } return NS_BINDING_ABORTED; } @@ -931,13 +1047,16 @@ // Otherwise, broken PRInt32 state = NS_EVENT_STATE_BROKEN; - switch (mPluginState) { + switch (mFallbackReason) { case ePluginDisabled: state |= NS_EVENT_STATE_HANDLER_DISABLED; break; case ePluginBlocklisted: state |= NS_EVENT_STATE_HANDLER_BLOCKED; break; + case ePluginCrashed: + state |= NS_EVENT_STATE_HANDLER_CRASHED; + break; case ePluginUnsupported: state |= NS_EVENT_STATE_TYPE_UNSUPPORTED; break; @@ -1486,7 +1605,7 @@ } mType = eType_Null; mUserDisabled = mSuppressed = PR_FALSE; - mPluginState = ePluginOtherState; + mFallbackReason = ePluginOtherState; } void @@ -1885,4 +2004,33 @@ return frame->SetAbsoluteScreenPosition(element, position, clip); } +NS_IMETHODIMP +nsObjectLoadingContent::PluginCrashed(nsIPluginTag* aPluginTag, + const nsAString& pluginDumpID, + const nsAString& browserDumpID, + PRBool submittedCrashReport) +{ + AutoNotifier notifier(this, PR_TRUE); + UnloadContent(); + mFallbackReason = ePluginCrashed; + nsCOMPtr thisContent = do_QueryInterface(static_cast(this)); + // Note that aPluginTag in invalidated after we're called, so copy + // out any data we need now. + nsCAutoString pluginName; + aPluginTag->GetName(pluginName); + nsCAutoString pluginFilename; + aPluginTag->GetFilename(pluginFilename); + + nsCOMPtr ev = new nsPluginCrashedEvent(thisContent, + pluginDumpID, + browserDumpID, + NS_ConvertUTF8toUTF16(pluginName), + NS_ConvertUTF8toUTF16(pluginFilename), + submittedCrashReport); + nsresult rv = NS_DispatchToCurrentThread(ev); + if (NS_FAILED(rv)) { + NS_WARNING("failed to dispatch nsPluginCrashedEvent"); + } + return NS_OK; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/content/base/src/nsObjectLoadingContent.h firefox-3.6.4+build1+nobinonly/mozilla/content/base/src/nsObjectLoadingContent.h --- firefox-3.6.3+nobinonly/mozilla/content/base/src/nsObjectLoadingContent.h 2010-04-02 16:57:48.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/content/base/src/nsObjectLoadingContent.h 2010-04-16 17:31:33.000000000 +0100 @@ -60,13 +60,12 @@ class AutoSetInstantiatingToFalse; enum PluginSupportState { - ePluginUnsupported, // The plugin is not supported (not installed, say) - ePluginDisabled, // The plugin has been explicitly disabled by the - // user. + ePluginUnsupported, // The plugin is not supported (e.g. not installed) + ePluginDisabled, // The plugin has been explicitly disabled by the user ePluginBlocklisted, // The plugin is blocklisted and disabled ePluginOutdated, // The plugin is considered outdated, but not disabled - ePluginOtherState // Something else (e.g. not a plugin at all as far - // as we can tell). + ePluginOtherState, // Something else (e.g. uninitialized or not a plugin) + ePluginCrashed }; /** @@ -422,7 +421,7 @@ PRBool mUserDisabled : 1; PRBool mSuppressed : 1; // A specific state that caused us to fallback - PluginSupportState mPluginState; + PluginSupportState mFallbackReason; friend class nsAsyncInstantiateEvent; }; diff -Nru firefox-3.6.3+nobinonly/mozilla/content/events/public/nsIEventStateManager.h firefox-3.6.4+build1+nobinonly/mozilla/content/events/public/nsIEventStateManager.h --- firefox-3.6.3+nobinonly/mozilla/content/events/public/nsIEventStateManager.h 2010-04-02 16:57:50.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/content/events/public/nsIEventStateManager.h 2010-04-16 17:31:34.000000000 +0100 @@ -197,4 +197,8 @@ #define NS_EVENT_STATE_INDETERMINATE 0x04000000 // CSS3-Selectors +// Handler for the content has crashed +#define NS_EVENT_STATE_HANDLER_CRASHED \ + 0x08000000 + #endif // nsIEventStateManager_h__ diff -Nru firefox-3.6.3+nobinonly/mozilla/content/events/src/nsContentEventHandler.cpp firefox-3.6.4+build1+nobinonly/mozilla/content/events/src/nsContentEventHandler.cpp --- firefox-3.6.3+nobinonly/mozilla/content/events/src/nsContentEventHandler.cpp 2010-04-02 16:57:50.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/content/events/src/nsContentEventHandler.cpp 2010-04-16 17:31:34.000000000 +0100 @@ -744,7 +744,10 @@ nsPoint ptInRoot = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, rootFrame); nsIFrame* targetFrame = nsLayoutUtils::GetFrameForPoint(rootFrame, ptInRoot); - if (!targetFrame || targetFrame->GetType() != nsGkAtoms::textFrame) { + if (!targetFrame || targetFrame->GetType() != nsGkAtoms::textFrame || + !targetFrame->GetContent() || + !nsContentUtils::ContentIsDescendantOf(targetFrame->GetContent(), + mRootContent)) { // there is no character at the point. aEvent->mReply.mOffset = nsQueryContentEvent::NOT_FOUND; aEvent->mSucceeded = PR_TRUE; diff -Nru firefox-3.6.3+nobinonly/mozilla/content/events/src/nsDOMDataTransfer.cpp firefox-3.6.4+build1+nobinonly/mozilla/content/events/src/nsDOMDataTransfer.cpp --- firefox-3.6.3+nobinonly/mozilla/content/events/src/nsDOMDataTransfer.cpp 2010-04-02 16:57:50.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/content/events/src/nsDOMDataTransfer.cpp 2010-04-16 17:31:34.000000000 +0100 @@ -49,6 +49,7 @@ #include "nsIDragService.h" #include "nsIScriptableRegion.h" #include "nsContentUtils.h" +#include "nsIContent.h" NS_IMPL_CYCLE_COLLECTION_2(nsDOMDataTransfer, mDragTarget, mDragImage) @@ -530,6 +531,10 @@ if (mReadOnly) return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; + if (aImage) { + nsCOMPtr content = do_QueryInterface(aImage); + NS_ENSURE_TRUE(content, NS_ERROR_INVALID_ARG); + } mDragImage = aImage; mDragImageX = aX; mDragImageY = aY; diff -Nru firefox-3.6.3+nobinonly/mozilla/content/events/src/nsDOMMouseEvent.cpp firefox-3.6.4+build1+nobinonly/mozilla/content/events/src/nsDOMMouseEvent.cpp --- firefox-3.6.3+nobinonly/mozilla/content/events/src/nsDOMMouseEvent.cpp 2010-04-02 16:57:50.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/content/events/src/nsDOMMouseEvent.cpp 2010-04-16 17:31:34.000000000 +0100 @@ -54,6 +54,9 @@ // DOM event. if (aEvent) { + NS_ASSERTION(static_cast(mEvent)->reason + != nsMouseEvent::eSynthesized, + "Don't dispatch DOM events from synthesized mouse events"); mEventIsInternal = PR_FALSE; } else { diff -Nru firefox-3.6.3+nobinonly/mozilla/content/events/src/nsEventStateManager.cpp firefox-3.6.4+build1+nobinonly/mozilla/content/events/src/nsEventStateManager.cpp --- firefox-3.6.3+nobinonly/mozilla/content/events/src/nsEventStateManager.cpp 2010-04-02 16:57:50.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/content/events/src/nsEventStateManager.cpp 2010-04-16 17:31:34.000000000 +0100 @@ -188,6 +188,15 @@ static nscoord GetScrollableViewLineHeight(nsPresContext* aPresContext, nsIFrame* aTargetFrame); +static inline PRBool +IsMouseEventReal(nsEvent* aEvent) +{ + NS_ABORT_IF_FALSE(aEvent->eventStructType == NS_MOUSE_EVENT, + "Not a mouse event"); + // Return true if not synthesized. + return static_cast(aEvent)->reason == nsMouseEvent::eReal; +} + #ifdef DEBUG_DOCSHELL_FOCUS static void PrintDocTree(nsIDocShellTreeItem* aParentItem, int aLevel) @@ -507,7 +516,7 @@ return; case NS_MOUSE_MOVE: case NS_DRAGDROP_OVER: - if (((nsMouseEvent*)aEvent)->reason == nsMouseEvent::eReal) { + if (IsMouseEventReal(aEvent)) { // If the cursor is moving to be outside the frame, // terminate the scrollwheel transaction. nsIntPoint pt = GetScreenPoint((nsGUIEvent*)aEvent); @@ -1029,7 +1038,7 @@ // when user is not active doesn't change the state to active. if (NS_IS_TRUSTED_EVENT(aEvent) && ((aEvent->eventStructType == NS_MOUSE_EVENT && - static_cast(aEvent)->reason == nsMouseEvent::eReal && + IsMouseEventReal(aEvent) && aEvent->message != NS_MOUSE_ENTER && aEvent->message != NS_MOUSE_EXIT) || aEvent->eventStructType == NS_MOUSE_SCROLL_EVENT || @@ -2928,13 +2937,15 @@ case NS_MOUSE_BUTTON_UP: { SetContentState(nsnull, NS_EVENT_STATE_ACTIVE); - if (!mCurrentTarget) { - nsIFrame* targ; - GetEventTarget(&targ); - } - if (mCurrentTarget) { - ret = - CheckForAndDispatchClick(presContext, (nsMouseEvent*)aEvent, aStatus); + if (IsMouseEventReal(aEvent)) { + if (!mCurrentTarget) { + nsIFrame* targ; + GetEventTarget(&targ); + } + if (mCurrentTarget) { + ret = CheckForAndDispatchClick(presContext, (nsMouseEvent*)aEvent, + aStatus); + } } nsIPresShell *shell = presContext->GetPresShell(); if (shell) { diff -Nru firefox-3.6.3+nobinonly/mozilla/content/svg/content/src/nsSVGTextContentElement.cpp firefox-3.6.4+build1+nobinonly/mozilla/content/svg/content/src/nsSVGTextContentElement.cpp --- firefox-3.6.3+nobinonly/mozilla/content/svg/content/src/nsSVGTextContentElement.cpp 2010-04-02 16:57:52.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/content/svg/content/src/nsSVGTextContentElement.cpp 2010-04-16 17:31:37.000000000 +0100 @@ -58,10 +58,8 @@ *_retval = 0; nsSVGTextContainerFrame* metrics = GetTextContainerFrame(); - if (!metrics) - return NS_ERROR_FAILURE; - - *_retval = metrics->GetNumberOfChars(); + if (metrics) + *_retval = metrics->GetNumberOfChars(); return NS_OK; } @@ -72,10 +70,8 @@ *_retval = 0.0; nsSVGTextContainerFrame* metrics = GetTextContainerFrame(); - if (!metrics) - return NS_ERROR_FAILURE; - - *_retval = metrics->GetComputedTextLength(); + if (metrics) + *_retval = metrics->GetComputedTextLength(); return NS_OK; } @@ -86,7 +82,7 @@ *_retval = 0.0f; nsSVGTextContainerFrame* metrics = GetTextContainerFrame(); if (!metrics) - return NS_ERROR_FAILURE; + return NS_OK; PRUint32 charcount = metrics->GetNumberOfChars(); if (charcount <= charnum || nchars > charcount - charnum) @@ -154,10 +150,8 @@ *_retval = -1; nsSVGTextContainerFrame* metrics = GetTextContainerFrame(); - if (!metrics) - return NS_ERROR_FAILURE; - - *_retval = metrics->GetCharNumAtPosition(point); + if (metrics) + *_retval = metrics->GetCharNumAtPosition(point); return NS_OK; } diff -Nru firefox-3.6.3+nobinonly/mozilla/content/svg/content/test/test_text.html firefox-3.6.4+build1+nobinonly/mozilla/content/svg/content/test/test_text.html --- firefox-3.6.3+nobinonly/mozilla/content/svg/content/test/test_text.html 2010-04-02 16:57:52.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/content/svg/content/test/test_text.html 2010-04-16 17:31:37.000000000 +0100 @@ -26,6 +26,7 @@ var text1 = doc.getElementById("text1"); var text2 = doc.getElementById("text2"); var text3 = doc.getElementById("text3"); + var text4 = doc.getElementById("text4"); var charWidth = text1.getSubStringLength(0, 1); @@ -160,6 +161,14 @@ // character 25 should be beyond the end of the path // Not sure what should happen here. Currently we throw, which seems wrong // is(text3.getStartPositionOfChar(25).x, 0, "text3 char 25 start offset"); + + // Display:none string + + is(text4.getNumberOfChars(), 0, "text4 length"); + is(text4.getComputedTextLength(), 0, "text4 measured length"); + is(text4.getSubStringLength(0, 3), 0, "text4 substring length"); + p = text1.getStartPositionOfChar(0); + is(text4.getCharNumAtPosition(p), -1, "text4 shouldn't find rendered char"); } function runTests() { diff -Nru firefox-3.6.3+nobinonly/mozilla/content/svg/content/test/text-helper.svg firefox-3.6.4+build1+nobinonly/mozilla/content/svg/content/test/text-helper.svg --- firefox-3.6.3+nobinonly/mozilla/content/svg/content/test/text-helper.svg 2010-04-02 16:57:52.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/content/svg/content/test/text-helper.svg 2010-04-16 17:31:37.000000000 +0100 @@ -13,5 +13,7 @@ abcdefghijklmnopqrstuvwxyz + + abc diff -Nru firefox-3.6.3+nobinonly/mozilla/content/xslt/src/xslt/txNodeSorter.cpp firefox-3.6.4+build1+nobinonly/mozilla/content/xslt/src/xslt/txNodeSorter.cpp --- firefox-3.6.3+nobinonly/mozilla/content/xslt/src/xslt/txNodeSorter.cpp 2010-04-02 16:57:52.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/content/xslt/src/xslt/txNodeSorter.cpp 2010-04-16 17:31:37.000000000 +0100 @@ -178,7 +178,15 @@ // Create and set up memoryblock for sort-values and indexarray PRUint32 len = static_cast(aNodes->size()); - void* mem = PR_Malloc(len * (sizeof(PRUint32) + mNKeys * sizeof(TxObject*))); + + // Don't overflow when calculating the length of the sort buffer. + PRUint32 itemSize = sizeof(PRUint32) + mNKeys * sizeof(TxObject*); + if (mNKeys > (PR_UINT32_MAX - sizeof(PRUint32)) / sizeof(TxObject*) || + len >= PR_UINT32_MAX / itemSize) { + return NS_ERROR_OUT_OF_MEMORY; + } + + void* mem = PR_Malloc(len * itemSize); NS_ENSURE_TRUE(mem, NS_ERROR_OUT_OF_MEMORY); PRUint32* indexes = static_cast(mem); diff -Nru firefox-3.6.3+nobinonly/mozilla/content/xul/document/src/nsXULDocument.cpp firefox-3.6.4+build1+nobinonly/mozilla/content/xul/document/src/nsXULDocument.cpp --- firefox-3.6.3+nobinonly/mozilla/content/xul/document/src/nsXULDocument.cpp 2010-04-02 16:57:52.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/content/xul/document/src/nsXULDocument.cpp 2010-04-16 17:31:38.000000000 +0100 @@ -1030,36 +1030,35 @@ = do_QueryReferent(bl->mListener); nsCOMPtr l = do_QueryInterface(listenerEl); if (l) { - PRBool possibleCycle = PR_FALSE; - for (PRUint32 j = 0; j < mDelayedAttrChangeBroadcasts.Length(); ++j) { - if (mDelayedAttrChangeBroadcasts[j].mListener == listenerEl && - mDelayedAttrChangeBroadcasts[j].mAttrName == aAttribute) { - possibleCycle = PR_TRUE; - break; + nsAutoString currentValue; + PRBool hasAttr = l->GetAttr(kNameSpaceID_None, + aAttribute, + currentValue); + // We need to update listener only if we're + // (1) removing an existing attribute, + // (2) adding a new attribute or + // (3) changing the value of an attribute. + PRBool needsAttrChange = + attrSet != hasAttr || !value.Equals(currentValue); + nsDelayedBroadcastUpdate delayedUpdate(domele, + listenerEl, + aAttribute, + value, + attrSet, + needsAttrChange); + + PRUint32 index = + mDelayedAttrChangeBroadcasts.IndexOf(delayedUpdate, + 0, nsDelayedBroadcastUpdate::Comparator()); + if (index != mDelayedAttrChangeBroadcasts.NoIndex) { + if (mHandlingDelayedAttrChange) { + NS_WARNING("Broadcasting loop!"); + continue; } + mDelayedAttrChangeBroadcasts.RemoveElementAt(index); } - if (possibleCycle) { - NS_WARNING("Broadcasting loop!"); - } else { - nsAutoString currentValue; - PRBool hasAttr = l->GetAttr(kNameSpaceID_None, - aAttribute, - currentValue); - // We need to update listener only if we're - // (1) removing an existing attribute, - // (2) adding a new attribute or - // (3) changing the value of an attribute. - PRBool needsAttrChange = - attrSet != hasAttr || !value.Equals(currentValue); - nsDelayedBroadcastUpdate delayedUpdate(domele, - listenerEl, - aAttribute, - value, - attrSet, - needsAttrChange); - mDelayedAttrChangeBroadcasts.AppendElement(delayedUpdate); - } + mDelayedAttrChangeBroadcasts.AppendElement(delayedUpdate); } } } diff -Nru firefox-3.6.3+nobinonly/mozilla/content/xul/document/src/nsXULDocument.h firefox-3.6.4+build1+nobinonly/mozilla/content/xul/document/src/nsXULDocument.h --- firefox-3.6.3+nobinonly/mozilla/content/xul/document/src/nsXULDocument.h 2010-04-02 16:57:52.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/content/xul/document/src/nsXULDocument.h 2010-04-16 17:31:39.000000000 +0100 @@ -753,6 +753,13 @@ nsCOMPtr mAttrName; PRPackedBool mSetAttr; PRPackedBool mNeedsAttrChange; + + class Comparator { + public: + static PRBool Equals(const nsDelayedBroadcastUpdate& a, const nsDelayedBroadcastUpdate& b) { + return a.mBroadcaster == b.mBroadcaster && a.mListener == b.mListener && a.mAttrName == b.mAttrName; + } + }; }; nsTArray mDelayedBroadcasters; diff -Nru firefox-3.6.3+nobinonly/mozilla/db/sqlite3/README.MOZILLA firefox-3.6.4+build1+nobinonly/mozilla/db/sqlite3/README.MOZILLA --- firefox-3.6.3+nobinonly/mozilla/db/sqlite3/README.MOZILLA 2010-04-02 16:57:53.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/db/sqlite3/README.MOZILLA 2010-04-16 17:31:40.000000000 +0100 @@ -1,6 +1,6 @@ -This is sqlite 3.6.16.1 +This is sqlite 3.6.22 --- Shawn Wilsher , 10/2009 +-- Andrew Sutherland , 04/2010 See http://www.sqlite.org/ for more info. diff -Nru firefox-3.6.3+nobinonly/mozilla/db/sqlite3/src/sqlite3.c firefox-3.6.4+build1+nobinonly/mozilla/db/sqlite3/src/sqlite3.c --- firefox-3.6.3+nobinonly/mozilla/db/sqlite3/src/sqlite3.c 2010-04-02 16:57:53.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/db/sqlite3/src/sqlite3.c 2010-04-16 17:31:40.000000000 +0100 @@ -1,6 +1,6 @@ /****************************************************************************** ** This file is an amalgamation of many separate C source files from SQLite -** version 3.6.16.1. By combining all the individual C code files into this +** version 3.6.22. By combining all the individual C code files into this ** single large file, the entire code can be compiled as a one translation ** unit. This allows many compilers to do optimizations that would not be ** possible if the files were compiled separately. Performance improvements @@ -10,14 +10,12 @@ ** This file is all you need to compile SQLite. To use SQLite in other ** programs, you need this file and the "sqlite3.h" header file that defines ** the programming interface to the SQLite library. (If you do not have -** the "sqlite3.h" header file at hand, you will find a copy in the first -** 5626 lines past this header comment.) Additional code files may be -** needed if you want a wrapper to interface SQLite with your choice of -** programming language. The code for the "sqlite3" command-line shell -** is also in a separate file. This file contains only code for the core -** SQLite library. -** -** This amalgamation was generated on 2009-10-30 13:28:17 UTC. +** the "sqlite3.h" header file at hand, you will find a copy embedded within +** the text of this file. Search for "Begin file sqlite3.h" to find the start +** of the embedded sqlite3.h header file.) Additional code files may be needed +** if you want a wrapper to interface SQLite with your choice of programming +** language. The code for the "sqlite3" command-line shell is also in a +** separate file. This file contains only code for the core SQLite library. */ #define SQLITE_CORE 1 #define SQLITE_AMALGAMATION 1 @@ -41,12 +39,38 @@ ************************************************************************* ** Internal interface definitions for SQLite. ** -** @(#) $Id: sqliteInt.h,v 1.890 2009/06/26 15:14:55 drh Exp $ */ #ifndef _SQLITEINT_H_ #define _SQLITEINT_H_ /* +** These #defines should enable >2GB file support on POSIX if the +** underlying operating system supports it. If the OS lacks +** large file support, or if the OS is windows, these should be no-ops. +** +** Ticket #2739: The _LARGEFILE_SOURCE macro must appear before any +** system #includes. Hence, this block of code must be the very first +** code in all source files. +** +** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch +** on the compiler command line. This is necessary if you are compiling +** on a recent machine (ex: Red Hat 7.2) but you want your code to work +** on an older machine (ex: Red Hat 6.0). If you compile on Red Hat 7.2 +** without this option, LFS is enable. But LFS does not exist in the kernel +** in Red Hat 6.0, so the code won't work. Hence, for maximum binary +** portability you should omit LFS. +** +** Similar is true for Mac OS X. LFS is only supported on Mac OS X 9 and later. +*/ +#ifndef SQLITE_DISABLE_LFS +# define _LARGE_FILE 1 +# ifndef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 +# endif +# define _LARGEFILE_SOURCE 1 +#endif + +/* ** Include the configuration header output by 'configure' if we're using the ** autoconf-based build */ @@ -69,8 +93,6 @@ ************************************************************************* ** ** This file defines various limits of what SQLite can process. -** -** @(#) $Id: sqliteLimit.h,v 1.10 2009/01/10 16:15:09 danielk1977 Exp $ */ /* @@ -247,6 +269,17 @@ # define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000 #endif +/* +** Maximum depth of recursion for triggers. +** +** A value of 1 means that a trigger program will not be able to itself +** fire any triggers. A value of 0 means that no trigger programs at all +** may be executed. +*/ +#ifndef SQLITE_MAX_TRIGGER_DEPTH +# define SQLITE_MAX_TRIGGER_DEPTH 1000 +#endif + /************** End of sqliteLimit.h *****************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ @@ -274,6 +307,8 @@ #include #endif +#define SQLITE_INDEX_SAMPLES 10 + /* ** This macro is used to "hide" some ugliness in casting an int ** value to a ptr value under the MSVC 64-bit compiler. Casting @@ -306,33 +341,6 @@ # define SQLITE_PTR_TO_INT(X) ((int)(((char*)X)-(char*)0)) #endif -/* -** These #defines should enable >2GB file support on POSIX if the -** underlying operating system supports it. If the OS lacks -** large file support, or if the OS is windows, these should be no-ops. -** -** Ticket #2739: The _LARGEFILE_SOURCE macro must appear before any -** system #includes. Hence, this block of code must be the very first -** code in all source files. -** -** Large file support can be disabled using the -DSQLITE_DISABLE_LFS switch -** on the compiler command line. This is necessary if you are compiling -** on a recent machine (ex: Red Hat 7.2) but you want your code to work -** on an older machine (ex: Red Hat 6.0). If you compile on Red Hat 7.2 -** without this option, LFS is enable. But LFS does not exist in the kernel -** in Red Hat 6.0, so the code won't work. Hence, for maximum binary -** portability you should omit LFS. -** -** Similar is true for Mac OS X. LFS is only supported on Mac OS X 9 and later. -*/ -#ifndef SQLITE_DISABLE_LFS -# define _LARGE_FILE 1 -# ifndef _FILE_OFFSET_BITS -# define _FILE_OFFSET_BITS 64 -# endif -# define _LARGEFILE_SOURCE 1 -#endif - /* ** The SQLITE_THREADSAFE macro must be defined as either 0 or 1. @@ -534,8 +542,8 @@ ** Some of the definitions that are in this file are marked as ** "experimental". Experimental interfaces are normally new ** features recently added to SQLite. We do not anticipate changes -** to experimental interfaces but reserve to make minor changes if -** experience from use "in the wild" suggest such changes are prudent. +** to experimental interfaces but reserve the right to make minor changes +** if experience from use "in the wild" suggest such changes are prudent. ** ** The official C-language API documentation for SQLite is derived ** from comments in this file. This file is the authoritative source @@ -545,8 +553,6 @@ ** The makefile makes some minor changes to this file (such as inserting ** the version number) and changes its name to "sqlite3.h" as ** part of the build process. -** -** @(#) $Id: sqlite.h.in,v 1.458 2009/06/19 22:50:31 drh Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -567,10 +573,15 @@ # define SQLITE_EXTERN extern #endif +#ifndef SQLITE_API +# define SQLITE_API +#endif + + /* ** These no-op macros are used in front of interfaces to mark those ** interfaces as either deprecated or experimental. New applications -** should not use deprecated intrfaces - they are support for backwards +** should not use deprecated interfaces - they are support for backwards ** compatibility only. Application writers should be aware that ** experimental interfaces are subject to change in point releases. ** @@ -594,57 +605,80 @@ #endif /* -** CAPI3REF: Compile-Time Library Version Numbers {H10010} +** CAPI3REF: Compile-Time Library Version Numbers ** -** The SQLITE_VERSION and SQLITE_VERSION_NUMBER #defines in -** the sqlite3.h file specify the version of SQLite with which -** that header file is associated. -** -** The "version" of SQLite is a string of the form "X.Y.Z". -** The phrase "alpha" or "beta" might be appended after the Z. -** The X value is major version number always 3 in SQLite3. -** The X value only changes when backwards compatibility is -** broken and we intend to never break backwards compatibility. -** The Y value is the minor version number and only changes when -** there are major feature enhancements that are forwards compatible -** but not backwards compatible. -** The Z value is the release number and is incremented with -** each release but resets back to 0 whenever Y is incremented. -** -** See also: [sqlite3_libversion()] and [sqlite3_libversion_number()]. -** -** Requirements: [H10011] [H10014] -*/ -#define SQLITE_VERSION "3.6.16.1" -#define SQLITE_VERSION_NUMBER 3006016 +** ^(The [SQLITE_VERSION] C preprocessor macro in the sqlite3.h header +** evaluates to a string literal that is the SQLite version in the +** format "X.Y.Z" where X is the major version number (always 3 for +** SQLite3) and Y is the minor version number and Z is the release number.)^ +** ^(The [SQLITE_VERSION_NUMBER] C preprocessor macro resolves to an integer +** with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same +** numbers used in [SQLITE_VERSION].)^ +** The SQLITE_VERSION_NUMBER for any given release of SQLite will also +** be larger than the release from which it is derived. Either Y will +** be held constant and Z will be incremented or else Y will be incremented +** and Z will be reset to zero. +** +** Since version 3.6.18, SQLite source code has been stored in the +** Fossil configuration management +** system. ^The SQLITE_SOURCE_ID macro evalutes to +** a string which identifies a particular check-in of SQLite +** within its configuration management system. ^The SQLITE_SOURCE_ID +** string contains the date and time of the check-in (UTC) and an SHA1 +** hash of the entire source tree. +** +** See also: [sqlite3_libversion()], +** [sqlite3_libversion_number()], [sqlite3_sourceid()], +** [sqlite_version()] and [sqlite_source_id()]. +*/ +#define SQLITE_VERSION "3.6.22" +#define SQLITE_VERSION_NUMBER 3006022 +#define SQLITE_SOURCE_ID "2010-01-05 15:30:36 28d0d7710761114a44a1a3a425a6883c661f06e7" /* -** CAPI3REF: Run-Time Library Version Numbers {H10020} +** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version ** -** These features provide the same information as the [SQLITE_VERSION] -** and [SQLITE_VERSION_NUMBER] #defines in the header, but are associated -** with the library instead of the header file. Cautious programmers might -** include a check in their application to verify that -** sqlite3_libversion_number() always returns the value -** [SQLITE_VERSION_NUMBER]. -** -** The sqlite3_libversion() function returns the same information as is -** in the sqlite3_version[] string constant. The function is provided -** for use in DLLs since DLL users usually do not have direct access to string -** constants within the DLL. +** These interfaces provide the same information as the [SQLITE_VERSION], +** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros +** but are associated with the library instead of the header file. ^(Cautious +** programmers might include assert() statements in their application to +** verify that values returned by these interfaces match the macros in +** the header, and thus insure that the application is +** compiled with matching library and header files. ** -** Requirements: [H10021] [H10022] [H10023] +**
+** assert( sqlite3_libversion_number()==SQLITE_VERSION_NUMBER );
+** assert( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)==0 );
+** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 );
+** 
)^ +** +** ^The sqlite3_version[] string constant contains the text of [SQLITE_VERSION] +** macro. ^The sqlite3_libversion() function returns a pointer to the +** to the sqlite3_version[] string constant. The sqlite3_libversion() +** function is provided for use in DLLs since DLL users usually do not have +** direct access to string constants within the DLL. ^The +** sqlite3_libversion_number() function returns an integer equal to +** [SQLITE_VERSION_NUMBER]. ^The sqlite3_sourceid() function a pointer +** to a string constant whose value is the same as the [SQLITE_SOURCE_ID] +** C preprocessor macro. +** +** See also: [sqlite_version()] and [sqlite_source_id()]. */ SQLITE_API const char sqlite3_version[] = SQLITE_VERSION; SQLITE_API const char *sqlite3_libversion(void); +SQLITE_API const char *sqlite3_sourceid(void); SQLITE_API int sqlite3_libversion_number(void); /* -** CAPI3REF: Test To See If The Library Is Threadsafe {H10100} +** CAPI3REF: Test To See If The Library Is Threadsafe +** +** ^The sqlite3_threadsafe() function returns zero if and only if +** SQLite was compiled mutexing code omitted due to the +** [SQLITE_THREADSAFE] compile-time option being set to 0. ** ** SQLite can be compiled with or without mutexes. When -** the [SQLITE_THREADSAFE] C preprocessor macro 1 or 2, mutexes +** the [SQLITE_THREADSAFE] C preprocessor macro is 1 or 2, mutexes ** are enabled and SQLite is threadsafe. When the ** [SQLITE_THREADSAFE] macro is 0, ** the mutexes are omitted. Without the mutexes, it is not safe @@ -653,29 +687,29 @@ ** Enabling mutexes incurs a measurable performance penalty. ** So if speed is of utmost importance, it makes sense to disable ** the mutexes. But for maximum safety, mutexes should be enabled. -** The default behavior is for mutexes to be enabled. +** ^The default behavior is for mutexes to be enabled. ** -** This interface can be used by a program to make sure that the +** This interface can be used by an application to make sure that the ** version of SQLite that it is linking against was compiled with ** the desired setting of the [SQLITE_THREADSAFE] macro. ** ** This interface only reports on the compile-time mutex setting ** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with -** SQLITE_THREADSAFE=1 then mutexes are enabled by default but +** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but ** can be fully or partially disabled using a call to [sqlite3_config()] ** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], -** or [SQLITE_CONFIG_MUTEX]. The return value of this function shows -** only the default compile-time setting, not any run-time changes -** to that setting. +** or [SQLITE_CONFIG_MUTEX]. ^(The return value of the +** sqlite3_threadsafe() function shows only the compile-time setting of +** thread safety, not any run-time changes to that setting made by +** sqlite3_config(). In other words, the return value from sqlite3_threadsafe() +** is unchanged by calls to sqlite3_config().)^ ** ** See the [threading mode] documentation for additional information. -** -** Requirements: [H10101] [H10102] */ SQLITE_API int sqlite3_threadsafe(void); /* -** CAPI3REF: Database Connection Handle {H12000} +** CAPI3REF: Database Connection Handle ** KEYWORDS: {database connection} {database connections} ** ** Each open SQLite database is represented by a pointer to an instance of @@ -690,7 +724,7 @@ typedef struct sqlite3 sqlite3; /* -** CAPI3REF: 64-Bit Integer Types {H10200} +** CAPI3REF: 64-Bit Integer Types ** KEYWORDS: sqlite_int64 sqlite_uint64 ** ** Because there is no cross-platform way to specify 64-bit integer types @@ -700,7 +734,10 @@ ** The sqlite_int64 and sqlite_uint64 types are supported for backwards ** compatibility only. ** -** Requirements: [H10201] [H10202] +** ^The sqlite3_int64 and sqlite_int64 types can store integer values +** between -9223372036854775808 and +9223372036854775807 inclusive. ^The +** sqlite3_uint64 and sqlite_uint64 types can store integer values +** between 0 and +18446744073709551615 inclusive. */ #ifdef SQLITE_INT64_TYPE typedef SQLITE_INT64_TYPE sqlite_int64; @@ -724,34 +761,28 @@ #endif /* -** CAPI3REF: Closing A Database Connection {H12010} +** CAPI3REF: Closing A Database Connection ** -** This routine is the destructor for the [sqlite3] object. +** ^The sqlite3_close() routine is the destructor for the [sqlite3] object. +** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is +** successfullly destroyed and all associated resources are deallocated. ** -** Applications should [sqlite3_finalize | finalize] all [prepared statements] +** Applications must [sqlite3_finalize | finalize] all [prepared statements] ** and [sqlite3_blob_close | close] all [BLOB handles] associated with -** the [sqlite3] object prior to attempting to close the object. -** The [sqlite3_next_stmt()] interface can be used to locate all -** [prepared statements] associated with a [database connection] if desired. -** Typical code might look like this: -** -**
-** sqlite3_stmt *pStmt;
-** while( (pStmt = sqlite3_next_stmt(db, 0))!=0 ){
-**     sqlite3_finalize(pStmt);
-** }
-** 
+** the [sqlite3] object prior to attempting to close the object. ^If +** sqlite3_close() is called on a [database connection] that still has +** outstanding [prepared statements] or [BLOB handles], then it returns +** SQLITE_BUSY. ** -** If [sqlite3_close()] is invoked while a transaction is open, +** ^If [sqlite3_close()] is invoked while a transaction is open, ** the transaction is automatically rolled back. ** ** The C parameter to [sqlite3_close(C)] must be either a NULL ** pointer or an [sqlite3] object pointer obtained ** from [sqlite3_open()], [sqlite3_open16()], or ** [sqlite3_open_v2()], and not previously closed. -** -** Requirements: -** [H12011] [H12012] [H12013] [H12014] [H12015] [H12019] +** ^Calling sqlite3_close() with a NULL pointer argument is a +** harmless no-op. */ SQLITE_API int sqlite3_close(sqlite3 *); @@ -763,48 +794,65 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); /* -** CAPI3REF: One-Step Query Execution Interface {H12100} +** CAPI3REF: One-Step Query Execution Interface +** +** The sqlite3_exec() interface is a convenience wrapper around +** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()], +** that allows an application to run multiple statements of SQL +** without having to use a lot of C code. +** +** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded, +** semicolon-separate SQL statements passed into its 2nd argument, +** in the context of the [database connection] passed in as its 1st +** argument. ^If the callback function of the 3rd argument to +** sqlite3_exec() is not NULL, then it is invoked for each result row +** coming out of the evaluated SQL statements. ^The 4th argument to +** to sqlite3_exec() is relayed through to the 1st argument of each +** callback invocation. ^If the callback pointer to sqlite3_exec() +** is NULL, then no callback is ever invoked and result rows are +** ignored. +** +** ^If an error occurs while evaluating the SQL statements passed into +** sqlite3_exec(), then execution of the current statement stops and +** subsequent statements are skipped. ^If the 5th parameter to sqlite3_exec() +** is not NULL then any error message is written into memory obtained +** from [sqlite3_malloc()] and passed back through the 5th parameter. +** To avoid memory leaks, the application should invoke [sqlite3_free()] +** on error message strings returned through the 5th parameter of +** of sqlite3_exec() after the error message string is no longer needed. +** ^If the 5th parameter to sqlite3_exec() is not NULL and no errors +** occur, then sqlite3_exec() sets the pointer in its 5th parameter to +** NULL before returning. +** +** ^If an sqlite3_exec() callback returns non-zero, the sqlite3_exec() +** routine returns SQLITE_ABORT without invoking the callback again and +** without running any subsequent SQL statements. +** +** ^The 2nd argument to the sqlite3_exec() callback function is the +** number of columns in the result. ^The 3rd argument to the sqlite3_exec() +** callback is an array of pointers to strings obtained as if from +** [sqlite3_column_text()], one for each column. ^If an element of a +** result row is NULL then the corresponding string pointer for the +** sqlite3_exec() callback is a NULL pointer. ^The 4th argument to the +** sqlite3_exec() callback is an array of pointers to strings where each +** entry represents the name of corresponding result column as obtained +** from [sqlite3_column_name()]. +** +** ^If the 2nd parameter to sqlite3_exec() is a NULL pointer, a pointer +** to an empty string, or a pointer that contains only whitespace and/or +** SQL comments, then no SQL statements are evaluated and the database +** is not changed. ** -** The sqlite3_exec() interface is a convenient way of running one or more -** SQL statements without having to write a lot of C code. The UTF-8 encoded -** SQL statements are passed in as the second parameter to sqlite3_exec(). -** The statements are evaluated one by one until either an error or -** an interrupt is encountered, or until they are all done. The 3rd parameter -** is an optional callback that is invoked once for each row of any query -** results produced by the SQL statements. The 5th parameter tells where -** to write any error messages. -** -** The error message passed back through the 5th parameter is held -** in memory obtained from [sqlite3_malloc()]. To avoid a memory leak, -** the calling application should call [sqlite3_free()] on any error -** message returned through the 5th parameter when it has finished using -** the error message. -** -** If the SQL statement in the 2nd parameter is NULL or an empty string -** or a string containing only whitespace and comments, then no SQL -** statements are evaluated and the database is not changed. -** -** The sqlite3_exec() interface is implemented in terms of -** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()]. -** The sqlite3_exec() routine does nothing to the database that cannot be done -** by [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()]. -** -** The first parameter to [sqlite3_exec()] must be an valid and open -** [database connection]. -** -** The database connection must not be closed while -** [sqlite3_exec()] is running. -** -** The calling function should use [sqlite3_free()] to free -** the memory that *errmsg is left pointing at once the error -** message is no longer needed. -** -** The SQL statement text in the 2nd parameter to [sqlite3_exec()] -** must remain unchanged while [sqlite3_exec()] is running. -** -** Requirements: -** [H12101] [H12102] [H12104] [H12105] [H12107] [H12110] [H12113] [H12116] -** [H12119] [H12122] [H12125] [H12131] [H12134] [H12137] [H12138] +** Restrictions: +** +**
    +**
  • The application must insure that the 1st parameter to sqlite3_exec() +** is a valid and open [database connection]. +**
  • The application must not close [database connection] specified by +** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. +**
  • The application must not modify the SQL statement text passed into +** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. +**
*/ SQLITE_API int sqlite3_exec( sqlite3*, /* An open database */ @@ -815,7 +863,7 @@ ); /* -** CAPI3REF: Result Codes {H10210} +** CAPI3REF: Result Codes ** KEYWORDS: SQLITE_OK {error code} {error codes} ** KEYWORDS: {result code} {result codes} ** @@ -859,7 +907,7 @@ /* end-of-error-codes */ /* -** CAPI3REF: Extended Result Codes {H10220} +** CAPI3REF: Extended Result Codes ** KEYWORDS: {extended error code} {extended error codes} ** KEYWORDS: {extended result code} {extended result codes} ** @@ -901,7 +949,7 @@ #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8) ) /* -** CAPI3REF: Flags For File Open Operations {H10230} +** CAPI3REF: Flags For File Open Operations ** ** These bit values are intended for use in the ** 3rd parameter to the [sqlite3_open_v2()] interface and @@ -922,9 +970,11 @@ #define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */ #define SQLITE_OPEN_NOMUTEX 0x00008000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ /* -** CAPI3REF: Device Characteristics {H10240} +** CAPI3REF: Device Characteristics ** ** The xDeviceCapabilities method of the [sqlite3_io_methods] ** object returns an integer which is a vector of the these @@ -956,7 +1006,7 @@ #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 /* -** CAPI3REF: File Locking Levels {H10250} +** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods @@ -969,7 +1019,7 @@ #define SQLITE_LOCK_EXCLUSIVE 4 /* -** CAPI3REF: Synchronization Type Flags {H10260} +** CAPI3REF: Synchronization Type Flags ** ** When SQLite invokes the xSync() method of an ** [sqlite3_io_methods] object it uses a combination of @@ -987,10 +1037,11 @@ #define SQLITE_SYNC_DATAONLY 0x00010 /* -** CAPI3REF: OS Interface Open File Handle {H11110} +** CAPI3REF: OS Interface Open File Handle ** -** An [sqlite3_file] object represents an open file in the OS -** interface layer. Individual OS interface implementations will +** An [sqlite3_file] object represents an open file in the +** [sqlite3_vfs | OS interface layer]. Individual OS interface +** implementations will ** want to subclass this object by appending additional fields ** for their own use. The pMethods entry is a pointer to an ** [sqlite3_io_methods] object that defines methods for performing @@ -1002,7 +1053,7 @@ }; /* -** CAPI3REF: OS Interface File Virtual Methods Object {H11120} +** CAPI3REF: OS Interface File Virtual Methods Object ** ** Every file opened by the [sqlite3_vfs] xOpen method populates an ** [sqlite3_file] object (or, more commonly, a subclass of the @@ -1107,7 +1158,7 @@ }; /* -** CAPI3REF: Standard File Control Opcodes {H11310} +** CAPI3REF: Standard File Control Opcodes ** ** These integer constants are opcodes for the xFileControl method ** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()] @@ -1127,7 +1178,7 @@ #define SQLITE_LAST_ERRNO 4 /* -** CAPI3REF: Mutex Handle {H17110} +** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks @@ -1139,7 +1190,7 @@ typedef struct sqlite3_mutex sqlite3_mutex; /* -** CAPI3REF: OS Interface Object {H11140} +** CAPI3REF: OS Interface Object ** ** An instance of the sqlite3_vfs object defines the interface between ** the SQLite core and the underlying operating system. The "vfs" @@ -1293,10 +1344,10 @@ }; /* -** CAPI3REF: Flags for the xAccess VFS method {H11190} +** CAPI3REF: Flags for the xAccess VFS method ** ** These integer constants can be used as the third parameter to -** the xAccess method of an [sqlite3_vfs] object. {END} They determine +** the xAccess method of an [sqlite3_vfs] object. They determine ** what kind of permissions the xAccess method is looking for. ** With SQLITE_ACCESS_EXISTS, the xAccess method ** simply checks whether the file exists. @@ -1310,39 +1361,48 @@ #define SQLITE_ACCESS_READ 2 /* -** CAPI3REF: Initialize The SQLite Library {H10130} +** CAPI3REF: Initialize The SQLite Library ** -** The sqlite3_initialize() routine initializes the -** SQLite library. The sqlite3_shutdown() routine +** ^The sqlite3_initialize() routine initializes the +** SQLite library. ^The sqlite3_shutdown() routine ** deallocates any resources that were allocated by sqlite3_initialize(). +** These routines are designed to aid in process initialization and +** shutdown on embedded systems. Workstation applications using +** SQLite normally do not need to invoke either of these routines. ** ** A call to sqlite3_initialize() is an "effective" call if it is ** the first time sqlite3_initialize() is invoked during the lifetime of ** the process, or if it is the first time sqlite3_initialize() is invoked -** following a call to sqlite3_shutdown(). Only an effective call +** following a call to sqlite3_shutdown(). ^(Only an effective call ** of sqlite3_initialize() does any initialization. All other calls -** are harmless no-ops. +** are harmless no-ops.)^ ** ** A call to sqlite3_shutdown() is an "effective" call if it is the first -** call to sqlite3_shutdown() since the last sqlite3_initialize(). Only +** call to sqlite3_shutdown() since the last sqlite3_initialize(). ^(Only ** an effective call to sqlite3_shutdown() does any deinitialization. -** All other calls to sqlite3_shutdown() are harmless no-ops. +** All other valid calls to sqlite3_shutdown() are harmless no-ops.)^ ** -** Among other things, sqlite3_initialize() shall invoke -** sqlite3_os_init(). Similarly, sqlite3_shutdown() -** shall invoke sqlite3_os_end(). +** The sqlite3_initialize() interface is threadsafe, but sqlite3_shutdown() +** is not. The sqlite3_shutdown() interface must only be called from a +** single thread. All open [database connections] must be closed and all +** other SQLite resources must be deallocated prior to invoking +** sqlite3_shutdown(). +** +** Among other things, ^sqlite3_initialize() will invoke +** sqlite3_os_init(). Similarly, ^sqlite3_shutdown() +** will invoke sqlite3_os_end(). ** -** The sqlite3_initialize() routine returns [SQLITE_OK] on success. -** If for some reason, sqlite3_initialize() is unable to initialize +** ^The sqlite3_initialize() routine returns [SQLITE_OK] on success. +** ^If for some reason, sqlite3_initialize() is unable to initialize ** the library (perhaps it is unable to allocate a needed resource such ** as a mutex) it returns an [error code] other than [SQLITE_OK]. ** -** The sqlite3_initialize() routine is called internally by many other +** ^The sqlite3_initialize() routine is called internally by many other ** SQLite interfaces so that an application usually does not need to ** invoke sqlite3_initialize() directly. For example, [sqlite3_open()] ** calls sqlite3_initialize() so the SQLite library will be automatically ** initialized when [sqlite3_open()] is called if it has not be initialized -** already. However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT] +** already. ^However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT] ** compile-time option, then the automatic calls to sqlite3_initialize() ** are omitted and the application must call sqlite3_initialize() directly ** prior to using any other SQLite interface. For maximum portability, @@ -1366,8 +1426,9 @@ ** interface is called automatically by sqlite3_initialize() and ** sqlite3_os_end() is called by sqlite3_shutdown(). Appropriate ** implementations for sqlite3_os_init() and sqlite3_os_end() -** are built into SQLite when it is compiled for unix, windows, or os/2. -** When built for other platforms (using the [SQLITE_OS_OTHER=1] compile-time +** are built into SQLite when it is compiled for Unix, Windows, or OS/2. +** When [custom builds | built for other platforms] +** (using the [SQLITE_OS_OTHER=1] compile-time ** option) the application must supply a suitable implementation for ** sqlite3_os_init() and sqlite3_os_end(). An application-supplied ** implementation of sqlite3_os_init() or sqlite3_os_end() @@ -1380,7 +1441,7 @@ SQLITE_API int sqlite3_os_end(void); /* -** CAPI3REF: Configuring The SQLite Library {H14100} +** CAPI3REF: Configuring The SQLite Library ** EXPERIMENTAL ** ** The sqlite3_config() interface is used to make global configuration @@ -1394,7 +1455,9 @@ ** threads while sqlite3_config() is running. Furthermore, sqlite3_config() ** may only be invoked prior to library initialization using ** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. -** Note, however, that sqlite3_config() can be called as part of the +** ^If sqlite3_config() is called after [sqlite3_initialize()] and before +** [sqlite3_shutdown()] then it will return SQLITE_MISUSE. +** Note, however, that ^sqlite3_config() can be called as part of the ** implementation of an application-defined [sqlite3_os_init()]. ** ** The first argument to sqlite3_config() is an integer @@ -1403,26 +1466,21 @@ ** vary depending on the [SQLITE_CONFIG_SINGLETHREAD | configuration option] ** in the first argument. ** -** When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. -** If the option is unknown or SQLite is unable to set the option +** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. +** ^If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. -** -** Requirements: -** [H14103] [H14106] [H14120] [H14123] [H14126] [H14129] [H14132] [H14135] -** [H14138] [H14141] [H14144] [H14147] [H14150] [H14153] [H14156] [H14159] -** [H14162] [H14165] [H14168] */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_config(int, ...); /* -** CAPI3REF: Configure database connections {H14200} +** CAPI3REF: Configure database connections ** EXPERIMENTAL ** ** The sqlite3_db_config() interface is used to make configuration ** changes to a [database connection]. The interface is similar to ** [sqlite3_config()] except that the changes apply to a single ** [database connection] (specified in the first argument). The -** sqlite3_db_config() interface can only be used immediately after +** sqlite3_db_config() interface should only be used immediately after ** the database connection is created using [sqlite3_open()], ** [sqlite3_open16()], or [sqlite3_open_v2()]. ** @@ -1433,13 +1491,13 @@ ** New verbs are likely to be added in future releases of SQLite. ** Additional arguments depend on the verb. ** -** Requirements: -** [H14203] [H14206] [H14209] [H14212] [H14215] +** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if +** the call is considered successful. */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_db_config(sqlite3*, int op, ...); /* -** CAPI3REF: Memory Allocation Routines {H10155} +** CAPI3REF: Memory Allocation Routines ** EXPERIMENTAL ** ** An instance of this object defines the interface between SQLite @@ -1448,13 +1506,15 @@ ** This object is used in only one place in the SQLite interface. ** A pointer to an instance of this object is the argument to ** [sqlite3_config()] when the configuration option is -** [SQLITE_CONFIG_MALLOC]. By creating an instance of this object -** and passing it to [sqlite3_config()] during configuration, an -** application can specify an alternative memory allocation subsystem -** for SQLite to use for all of its dynamic memory needs. +** [SQLITE_CONFIG_MALLOC] or [SQLITE_CONFIG_GETMALLOC]. +** By creating an instance of this object +** and passing it to [sqlite3_config]([SQLITE_CONFIG_MALLOC]) +** during configuration, an application can specify an alternative +** memory allocation subsystem for SQLite to use for all of its +** dynamic memory needs. ** -** Note that SQLite comes with a built-in memory allocator that is -** perfectly adequate for the overwhelming majority of applications +** Note that SQLite comes with several [built-in memory allocators] +** that are perfectly adequate for the overwhelming majority of applications ** and that this object is only useful to a tiny minority of applications ** with specialized memory allocation requirements. This object is ** also used during testing of SQLite in order to specify an alternative @@ -1462,8 +1522,16 @@ ** order to verify that SQLite recovers gracefully from such ** conditions. ** -** The xMalloc, xFree, and xRealloc methods must work like the -** malloc(), free(), and realloc() functions from the standard library. +** The xMalloc and xFree methods must work like the +** malloc() and free() functions from the standard C library. +** The xRealloc method must work like realloc() from the standard C library +** with the exception that if the second argument to xRealloc is zero, +** xRealloc must be a no-op - it must not perform any allocation or +** deallocation. ^SQLite guarantees that the second argument to +** xRealloc is always a value returned by a prior call to xRoundup. +** And so in cases where xRoundup always returns a positive number, +** xRealloc can perform exactly as the standard library realloc() and +** still be in compliance with this specification. ** ** xSize should return the allocated size of a memory allocation ** previously obtained from xMalloc or xRealloc. The allocated size @@ -1473,6 +1541,9 @@ ** a memory allocation given a particular requested size. Most memory ** allocators round up memory allocations at least to the next multiple ** of 8. Some allocators round up to a larger multiple or to a power of 2. +** Every memory allocation request coming in through [sqlite3_malloc()] +** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0, +** that causes the corresponding memory allocation to fail. ** ** The xInit method initializes the memory allocator. (For example, ** it might allocate any require mutexes or initialize internal data @@ -1480,6 +1551,20 @@ ** [sqlite3_shutdown()] and should deallocate any resources acquired ** by xInit. The pAppData pointer is used as the only parameter to ** xInit and xShutdown. +** +** SQLite holds the [SQLITE_MUTEX_STATIC_MASTER] mutex when it invokes +** the xInit method, so the xInit method need not be threadsafe. The +** xShutdown method is only called from [sqlite3_shutdown()] so it does +** not need to be threadsafe either. For all other methods, SQLite +** holds the [SQLITE_MUTEX_STATIC_MEM] mutex as long as the +** [SQLITE_CONFIG_MEMSTATUS] configuration option is turned on (which +** it is by default) and so the methods are automatically serialized. +** However, if [SQLITE_CONFIG_MEMSTATUS] is disabled, then the other +** methods must be threadsafe or else make their own arrangements for +** serialization. +** +** SQLite will never invoke xInit() more than once without an intervening +** call to xShutdown(). */ typedef struct sqlite3_mem_methods sqlite3_mem_methods; struct sqlite3_mem_methods { @@ -1494,7 +1579,7 @@ }; /* -** CAPI3REF: Configuration Options {H10160} +** CAPI3REF: Configuration Options ** EXPERIMENTAL ** ** These constants are the available integer configuration options that @@ -1509,22 +1594,33 @@ ** **
**
SQLITE_CONFIG_SINGLETHREAD
-**
There are no arguments to this option. This option disables +**
There are no arguments to this option. ^This option sets the +** [threading mode] to Single-thread. In other words, it disables ** all mutexing and puts SQLite into a mode where it can only be used -** by a single thread.
+** by a single thread. ^If SQLite is compiled with +** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then +** it is not possible to change the [threading mode] from its default +** value of Single-thread and so [sqlite3_config()] will return +** [SQLITE_ERROR] if called with the SQLITE_CONFIG_SINGLETHREAD +** configuration option. ** **
SQLITE_CONFIG_MULTITHREAD
-**
There are no arguments to this option. This option disables +**
There are no arguments to this option. ^This option sets the +** [threading mode] to Multi-thread. In other words, it disables ** mutexing on [database connection] and [prepared statement] objects. ** The application is responsible for serializing access to ** [database connections] and [prepared statements]. But other mutexes ** are enabled so that SQLite will be safe to use in a multi-threaded ** environment as long as no two threads attempt to use the same -** [database connection] at the same time. See the [threading mode] -** documentation for additional information.
+** [database connection] at the same time. ^If SQLite is compiled with +** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then +** it is not possible to set the Multi-thread [threading mode] and +** [sqlite3_config()] will return [SQLITE_ERROR] if called with the +** SQLITE_CONFIG_MULTITHREAD configuration option. ** **
SQLITE_CONFIG_SERIALIZED
-**
There are no arguments to this option. This option enables +**
There are no arguments to this option. ^This option sets the +** [threading mode] to Serialized. In other words, this option enables ** all mutexes including the recursive ** mutexes on [database connection] and [prepared statement] objects. ** In this mode (which is the default when SQLite is compiled with @@ -1532,55 +1628,63 @@ ** to [database connections] and [prepared statements] so that the ** application is free to use the same [database connection] or the ** same [prepared statement] in different threads at the same time. -** See the [threading mode] documentation for additional information.
+** ^If SQLite is compiled with +** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then +** it is not possible to set the Serialized [threading mode] and +** [sqlite3_config()] will return [SQLITE_ERROR] if called with the +** SQLITE_CONFIG_SERIALIZED configuration option. ** **
SQLITE_CONFIG_MALLOC
-**
This option takes a single argument which is a pointer to an +**
^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mem_methods] structure. The argument specifies ** alternative low-level memory allocation routines to be used in place of -** the memory allocation routines built into SQLite.
+** the memory allocation routines built into SQLite.)^ ^SQLite makes +** its own private copy of the content of the [sqlite3_mem_methods] structure +** before the [sqlite3_config()] call returns. ** **
SQLITE_CONFIG_GETMALLOC
-**
This option takes a single argument which is a pointer to an +**
^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mem_methods] structure. The [sqlite3_mem_methods] -** structure is filled with the currently defined memory allocation routines. +** structure is filled with the currently defined memory allocation routines.)^ ** This option can be used to overload the default memory allocation ** routines with a wrapper that simulations memory allocation failure or -** tracks memory usage, for example.
+** tracks memory usage, for example. ** **
SQLITE_CONFIG_MEMSTATUS
-**
This option takes single argument of type int, interpreted as a +**
^This option takes single argument of type int, interpreted as a ** boolean, which enables or disables the collection of memory allocation -** statistics. When disabled, the following SQLite interfaces become -** non-operational: +** statistics. ^(When memory allocation statistics are disabled, the +** following SQLite interfaces become non-operational: **
    **
  • [sqlite3_memory_used()] **
  • [sqlite3_memory_highwater()] **
  • [sqlite3_soft_heap_limit()] **
  • [sqlite3_status()] -**
+** )^ +** ^Memory allocation statistics are enabled by default unless SQLite is +** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory +** allocation statistics are disabled by default. **
** **
SQLITE_CONFIG_SCRATCH
-**
This option specifies a static memory buffer that SQLite can use for +**
^This option specifies a static memory buffer that SQLite can use for ** scratch memory. There are three arguments: A pointer an 8-byte ** aligned memory buffer from which the scrach allocations will be ** drawn, the size of each scratch allocation (sz), ** and the maximum number of scratch allocations (N). The sz ** argument must be a multiple of 16. The sz parameter should be a few bytes ** larger than the actual scratch space required due to internal overhead. -** The first argument should pointer to an 8-byte aligned buffer +** The first argument must be a pointer to an 8-byte aligned buffer ** of at least sz*N bytes of memory. -** SQLite will use no more than one scratch buffer at once per thread, so -** N should be set to the expected maximum number of threads. The sz -** parameter should be 6 times the size of the largest database page size. -** Scratch buffers are used as part of the btree balance operation. If -** The btree balancer needs additional memory beyond what is provided by -** scratch buffers or if no scratch buffer space is specified, then SQLite -** goes to [sqlite3_malloc()] to obtain the memory it needs.
+** ^SQLite will use no more than one scratch buffer per thread. So +** N should be set to the expected maximum number of threads. ^SQLite will +** never require a scratch buffer that is more than 6 times the database +** page size. ^If SQLite needs needs additional scratch memory beyond +** what is provided by this configuration option, then +** [sqlite3_malloc()] will be used to obtain the memory needed. ** **
SQLITE_CONFIG_PAGECACHE
-**
This option specifies a static memory buffer that SQLite can use for +**
^This option specifies a static memory buffer that SQLite can use for ** the database page cache with the default page cache implemenation. ** This configuration should not be used if an application-define page ** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option. @@ -1588,28 +1692,28 @@ ** memory, the size of each page buffer (sz), and the number of pages (N). ** The sz argument should be the size of the largest database page ** (a power of two between 512 and 32768) plus a little extra for each -** page header. The page header size is 20 to 40 bytes depending on -** the host architecture. It is harmless, apart from the wasted memory, +** page header. ^The page header size is 20 to 40 bytes depending on +** the host architecture. ^It is harmless, apart from the wasted memory, ** to make sz a little too large. The first ** argument should point to an allocation of at least sz*N bytes of memory. -** SQLite will use the memory provided by the first argument to satisfy its -** memory needs for the first N pages that it adds to cache. If additional +** ^SQLite will use the memory provided by the first argument to satisfy its +** memory needs for the first N pages that it adds to cache. ^If additional ** page cache memory is needed beyond what is provided by this option, then ** SQLite goes to [sqlite3_malloc()] for the additional storage space. -** The implementation might use one or more of the N buffers to hold +** ^The implementation might use one or more of the N buffers to hold ** memory accounting information. The pointer in the first argument must ** be aligned to an 8-byte boundary or subsequent behavior of SQLite ** will be undefined.
** **
SQLITE_CONFIG_HEAP
-**
This option specifies a static memory buffer that SQLite will use +**
^This option specifies a static memory buffer that SQLite will use ** for all of its dynamic memory allocation needs beyond those provided ** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE]. ** There are three arguments: An 8-byte aligned pointer to the memory, ** the number of bytes in the memory buffer, and the minimum allocation size. -** If the first pointer (the memory pointer) is NULL, then SQLite reverts +** ^If the first pointer (the memory pointer) is NULL, then SQLite reverts ** to using its default memory allocator (the system malloc() implementation), -** undoing any prior invocation of [SQLITE_CONFIG_MALLOC]. If the +** undoing any prior invocation of [SQLITE_CONFIG_MALLOC]. ^If the ** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory ** allocator is engaged to handle all of SQLites memory allocation needs. @@ -1617,36 +1721,50 @@ ** boundary or subsequent behavior of SQLite will be undefined.
** **
SQLITE_CONFIG_MUTEX
-**
This option takes a single argument which is a pointer to an +**
^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mutex_methods] structure. The argument specifies ** alternative low-level mutex routines to be used in place -** the mutex routines built into SQLite.
+** the mutex routines built into SQLite.)^ ^SQLite makes a copy of the +** content of the [sqlite3_mutex_methods] structure before the call to +** [sqlite3_config()] returns. ^If SQLite is compiled with +** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then +** the entire mutexing subsystem is omitted from the build and hence calls to +** [sqlite3_config()] with the SQLITE_CONFIG_MUTEX configuration option will +** return [SQLITE_ERROR]. ** **
SQLITE_CONFIG_GETMUTEX
-**
This option takes a single argument which is a pointer to an +**
^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mutex_methods] structure. The ** [sqlite3_mutex_methods] -** structure is filled with the currently defined mutex routines. +** structure is filled with the currently defined mutex routines.)^ ** This option can be used to overload the default mutex allocation ** routines with a wrapper used to track mutex usage for performance -** profiling or testing, for example.
+** profiling or testing, for example. ^If SQLite is compiled with +** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then +** the entire mutexing subsystem is omitted from the build and hence calls to +** [sqlite3_config()] with the SQLITE_CONFIG_GETMUTEX configuration option will +** return [SQLITE_ERROR]. ** **
SQLITE_CONFIG_LOOKASIDE
-**
This option takes two arguments that determine the default -** memory allcation lookaside optimization. The first argument is the +**
^(This option takes two arguments that determine the default +** memory allocation for the lookaside memory allocator on each +** [database connection]. The first argument is the ** size of each lookaside buffer slot and the second is the number of -** slots allocated to each database connection.
+** slots allocated to each database connection.)^ ^(This option sets the +** default lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE] +** verb to [sqlite3_db_config()] can be used to change the lookaside +** configuration on individual connections.)^ ** **
SQLITE_CONFIG_PCACHE
-**
This option takes a single argument which is a pointer to +**
^(This option takes a single argument which is a pointer to ** an [sqlite3_pcache_methods] object. This object specifies the interface -** to a custom page cache implementation. SQLite makes a copy of the +** to a custom page cache implementation.)^ ^SQLite makes a copy of the ** object and uses it for page cache memory allocations.
** **
SQLITE_CONFIG_GETPCACHE
-**
This option takes a single argument which is a pointer to an +**
^(This option takes a single argument which is a pointer to an ** [sqlite3_pcache_methods] object. SQLite copies of the current -** page cache implementation into that object.
+** page cache implementation into that object.)^ ** **
*/ @@ -1667,7 +1785,7 @@ #define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */ /* -** CAPI3REF: Configuration Options {H10170} +** CAPI3REF: Configuration Options ** EXPERIMENTAL ** ** These constants are the available integer configuration options that @@ -1676,21 +1794,26 @@ ** New configuration options may be added in future releases of SQLite. ** Existing configuration options might be discontinued. Applications ** should check the return code from [sqlite3_db_config()] to make sure that -** the call worked. The [sqlite3_db_config()] interface will return a +** the call worked. ^The [sqlite3_db_config()] interface will return a ** non-zero [error code] if a discontinued or unsupported configuration option ** is invoked. ** **
**
SQLITE_DBCONFIG_LOOKASIDE
-**
This option takes three additional arguments that determine the +**
^This option takes three additional arguments that determine the ** [lookaside memory allocator] configuration for the [database connection]. -** The first argument (the third parameter to [sqlite3_db_config()] is a -** pointer to an 8-byte aligned memory buffer to use for lookaside memory. -** The first argument may be NULL in which case SQLite will allocate the -** lookaside buffer itself using [sqlite3_malloc()]. The second argument is the -** size of each lookaside buffer slot and the third argument is the number of +** ^The first argument (the third parameter to [sqlite3_db_config()] is a +** pointer to an memory buffer to use for lookaside memory. +** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb +** may be NULL in which case SQLite will allocate the +** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the +** size of each lookaside buffer slot. ^The third argument is the number of ** slots. The size of the buffer in the first argument must be greater than -** or equal to the product of the second and third arguments.
+** or equal to the product of the second and third arguments. The buffer +** must be aligned to an 8-byte boundary. ^If the second argument to +** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally +** rounded down to the next smaller +** multiple of 8. See also: [SQLITE_CONFIG_LOOKASIDE] ** **
*/ @@ -1698,52 +1821,49 @@ /* -** CAPI3REF: Enable Or Disable Extended Result Codes {H12200} +** CAPI3REF: Enable Or Disable Extended Result Codes ** -** The sqlite3_extended_result_codes() routine enables or disables the -** [extended result codes] feature of SQLite. The extended result -** codes are disabled by default for historical compatibility considerations. -** -** Requirements: -** [H12201] [H12202] +** ^The sqlite3_extended_result_codes() routine enables or disables the +** [extended result codes] feature of SQLite. ^The extended result +** codes are disabled by default for historical compatibility. */ SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); /* -** CAPI3REF: Last Insert Rowid {H12220} +** CAPI3REF: Last Insert Rowid ** -** Each entry in an SQLite table has a unique 64-bit signed -** integer key called the [ROWID | "rowid"]. The rowid is always available +** ^Each entry in an SQLite table has a unique 64-bit signed +** integer key called the [ROWID | "rowid"]. ^The rowid is always available ** as an undeclared column named ROWID, OID, or _ROWID_ as long as those -** names are not also used by explicitly declared columns. If +** names are not also used by explicitly declared columns. ^If ** the table has a column of type [INTEGER PRIMARY KEY] then that column ** is another alias for the rowid. ** -** This routine returns the [rowid] of the most recent +** ^This routine returns the [rowid] of the most recent ** successful [INSERT] into the database from the [database connection] -** in the first argument. If no successful [INSERT]s +** in the first argument. ^If no successful [INSERT]s ** have ever occurred on that database connection, zero is returned. ** -** If an [INSERT] occurs within a trigger, then the [rowid] of the inserted +** ^(If an [INSERT] occurs within a trigger, then the [rowid] of the inserted ** row is returned by this routine as long as the trigger is running. ** But once the trigger terminates, the value returned by this routine -** reverts to the last value inserted before the trigger fired. +** reverts to the last value inserted before the trigger fired.)^ ** -** An [INSERT] that fails due to a constraint violation is not a +** ^An [INSERT] that fails due to a constraint violation is not a ** successful [INSERT] and does not change the value returned by this -** routine. Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK, +** routine. ^Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK, ** and INSERT OR ABORT make no changes to the return value of this -** routine when their insertion fails. When INSERT OR REPLACE +** routine when their insertion fails. ^(When INSERT OR REPLACE ** encounters a constraint violation, it does not fail. The ** INSERT continues to completion after deleting rows that caused ** the constraint problem so INSERT OR REPLACE will always change -** the return value of this interface. +** the return value of this interface.)^ ** -** For the purposes of this routine, an [INSERT] is considered to +** ^For the purposes of this routine, an [INSERT] is considered to ** be successful even if it is subsequently rolled back. ** -** Requirements: -** [H12221] [H12223] +** This function is accessible to SQL statements via the +** [last_insert_rowid() SQL function]. ** ** If a separate thread performs a new [INSERT] on the same ** database connection while the [sqlite3_last_insert_rowid()] @@ -1755,24 +1875,25 @@ SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); /* -** CAPI3REF: Count The Number Of Rows Modified {H12240} +** CAPI3REF: Count The Number Of Rows Modified ** -** This function returns the number of database rows that were changed +** ^This function returns the number of database rows that were changed ** or inserted or deleted by the most recently completed SQL statement ** on the [database connection] specified by the first parameter. -** Only changes that are directly specified by the [INSERT], [UPDATE], +** ^(Only changes that are directly specified by the [INSERT], [UPDATE], ** or [DELETE] statement are counted. Auxiliary changes caused by -** triggers are not counted. Use the [sqlite3_total_changes()] function -** to find the total number of changes including changes caused by triggers. +** triggers or [foreign key actions] are not counted.)^ Use the +** [sqlite3_total_changes()] function to find the total number of changes +** including changes caused by triggers and foreign key actions. ** -** Changes to a view that are simulated by an [INSTEAD OF trigger] +** ^Changes to a view that are simulated by an [INSTEAD OF trigger] ** are not counted. Only real table changes are counted. ** -** A "row change" is a change to a single row of a single table +** ^(A "row change" is a change to a single row of a single table ** caused by an INSERT, DELETE, or UPDATE statement. Rows that ** are changed as side effects of [REPLACE] constraint resolution, ** rollback, ABORT processing, [DROP TABLE], or by any other -** mechanisms do not count as direct row changes. +** mechanisms do not count as direct row changes.)^ ** ** A "trigger context" is a scope of execution that begins and ** ends with the script of a [CREATE TRIGGER | trigger]. @@ -1782,27 +1903,24 @@ ** new trigger context is entered for the duration of that one ** trigger. Subtriggers create subcontexts for their duration. ** -** Calling [sqlite3_exec()] or [sqlite3_step()] recursively does +** ^Calling [sqlite3_exec()] or [sqlite3_step()] recursively does ** not create a new trigger context. ** -** This function returns the number of direct row changes in the +** ^This function returns the number of direct row changes in the ** most recent INSERT, UPDATE, or DELETE statement within the same ** trigger context. ** -** Thus, when called from the top level, this function returns the +** ^Thus, when called from the top level, this function returns the ** number of changes in the most recent INSERT, UPDATE, or DELETE -** that also occurred at the top level. Within the body of a trigger, +** that also occurred at the top level. ^(Within the body of a trigger, ** the sqlite3_changes() interface can be called to find the number of ** changes in the most recently completed INSERT, UPDATE, or DELETE ** statement within the body of the same trigger. ** However, the number returned does not include changes -** caused by subtriggers since those have their own context. -** -** See also the [sqlite3_total_changes()] interface and the -** [count_changes pragma]. +** caused by subtriggers since those have their own context.)^ ** -** Requirements: -** [H12241] [H12243] +** See also the [sqlite3_total_changes()] interface, the +** [count_changes pragma], and the [changes() SQL function]. ** ** If a separate thread makes changes on the same database connection ** while [sqlite3_changes()] is running then the value returned @@ -1811,26 +1929,24 @@ SQLITE_API int sqlite3_changes(sqlite3*); /* -** CAPI3REF: Total Number Of Rows Modified {H12260} +** CAPI3REF: Total Number Of Rows Modified ** -** This function returns the number of row changes caused by [INSERT], +** ^This function returns the number of row changes caused by [INSERT], ** [UPDATE] or [DELETE] statements since the [database connection] was opened. -** The count includes all changes from all -** [CREATE TRIGGER | trigger] contexts. However, +** ^(The count returned by sqlite3_total_changes() includes all changes +** from all [CREATE TRIGGER | trigger] contexts and changes made by +** [foreign key actions]. However, ** the count does not include changes used to implement [REPLACE] constraints, ** do rollbacks or ABORT processing, or [DROP TABLE] processing. The ** count does not include rows of views that fire an [INSTEAD OF trigger], ** though if the INSTEAD OF trigger makes changes of its own, those changes -** are counted. -** The changes are counted as soon as the statement that makes them is -** completed (when the statement handle is passed to [sqlite3_reset()] or -** [sqlite3_finalize()]). +** are counted.)^ +** ^The sqlite3_total_changes() function counts the changes as soon as +** the statement that makes them is completed (when the statement handle +** is passed to [sqlite3_reset()] or [sqlite3_finalize()]). ** -** See also the [sqlite3_changes()] interface and the -** [count_changes pragma]. -** -** Requirements: -** [H12261] [H12263] +** See also the [sqlite3_changes()] interface, the +** [count_changes pragma], and the [total_changes() SQL function]. ** ** If a separate thread makes changes on the same database connection ** while [sqlite3_total_changes()] is running then the value @@ -1839,75 +1955,70 @@ SQLITE_API int sqlite3_total_changes(sqlite3*); /* -** CAPI3REF: Interrupt A Long-Running Query {H12270} +** CAPI3REF: Interrupt A Long-Running Query ** -** This function causes any pending database operation to abort and +** ^This function causes any pending database operation to abort and ** return at its earliest opportunity. This routine is typically ** called in response to a user action such as pressing "Cancel" ** or Ctrl-C where the user wants a long query operation to halt ** immediately. ** -** It is safe to call this routine from a thread different from the +** ^It is safe to call this routine from a thread different from the ** thread that is currently running the database operation. But it ** is not safe to call this routine with a [database connection] that ** is closed or might close before sqlite3_interrupt() returns. ** -** If an SQL operation is very nearly finished at the time when +** ^If an SQL operation is very nearly finished at the time when ** sqlite3_interrupt() is called, then it might not have an opportunity ** to be interrupted and might continue to completion. ** -** An SQL operation that is interrupted will return [SQLITE_INTERRUPT]. -** If the interrupted SQL operation is an INSERT, UPDATE, or DELETE +** ^An SQL operation that is interrupted will return [SQLITE_INTERRUPT]. +** ^If the interrupted SQL operation is an INSERT, UPDATE, or DELETE ** that is inside an explicit transaction, then the entire transaction ** will be rolled back automatically. ** -** The sqlite3_interrupt(D) call is in effect until all currently running -** SQL statements on [database connection] D complete. Any new SQL statements +** ^The sqlite3_interrupt(D) call is in effect until all currently running +** SQL statements on [database connection] D complete. ^Any new SQL statements ** that are started after the sqlite3_interrupt() call and before the ** running statements reaches zero are interrupted as if they had been -** running prior to the sqlite3_interrupt() call. New SQL statements +** running prior to the sqlite3_interrupt() call. ^New SQL statements ** that are started after the running statement count reaches zero are ** not effected by the sqlite3_interrupt(). -** A call to sqlite3_interrupt(D) that occurs when there are no running +** ^A call to sqlite3_interrupt(D) that occurs when there are no running ** SQL statements is a no-op and has no effect on SQL statements ** that are started after the sqlite3_interrupt() call returns. ** -** Requirements: -** [H12271] [H12272] -** ** If the database connection closes while [sqlite3_interrupt()] ** is running then bad things will likely happen. */ SQLITE_API void sqlite3_interrupt(sqlite3*); /* -** CAPI3REF: Determine If An SQL Statement Is Complete {H10510} +** CAPI3REF: Determine If An SQL Statement Is Complete ** ** These routines are useful during command-line input to determine if the ** currently entered text seems to form a complete SQL statement or ** if additional input is needed before sending the text into -** SQLite for parsing. These routines return 1 if the input string -** appears to be a complete SQL statement. A statement is judged to be +** SQLite for parsing. ^These routines return 1 if the input string +** appears to be a complete SQL statement. ^A statement is judged to be ** complete if it ends with a semicolon token and is not a prefix of a -** well-formed CREATE TRIGGER statement. Semicolons that are embedded within +** well-formed CREATE TRIGGER statement. ^Semicolons that are embedded within ** string literals or quoted identifier names or comments are not ** independent tokens (they are part of the token in which they are -** embedded) and thus do not count as a statement terminator. Whitespace +** embedded) and thus do not count as a statement terminator. ^Whitespace ** and comments that follow the final semicolon are ignored. ** -** These routines return 0 if the statement is incomplete. If a +** ^These routines return 0 if the statement is incomplete. ^If a ** memory allocation fails, then SQLITE_NOMEM is returned. ** -** These routines do not parse the SQL statements thus +** ^These routines do not parse the SQL statements thus ** will not detect syntactically incorrect SQL. ** -** If SQLite has not been initialized using [sqlite3_initialize()] prior +** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior ** to invoking sqlite3_complete16() then sqlite3_initialize() is invoked ** automatically by sqlite3_complete16(). If that initialization fails, ** then the return value from sqlite3_complete16() will be non-zero -** regardless of whether or not the input SQL is complete. -** -** Requirements: [H10511] [H10512] +** regardless of whether or not the input SQL is complete.)^ ** ** The input to [sqlite3_complete()] must be a zero-terminated ** UTF-8 string. @@ -1919,27 +2030,27 @@ SQLITE_API int sqlite3_complete16(const void *sql); /* -** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors {H12310} +** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors ** -** This routine sets a callback function that might be invoked whenever +** ^This routine sets a callback function that might be invoked whenever ** an attempt is made to open a database table that another thread ** or process has locked. ** -** If the busy callback is NULL, then [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] -** is returned immediately upon encountering the lock. If the busy callback -** is not NULL, then the callback will be invoked with two arguments. -** -** The first argument to the handler is a copy of the void* pointer which -** is the third argument to sqlite3_busy_handler(). The second argument to -** the handler callback is the number of times that the busy handler has -** been invoked for this locking event. If the +** ^If the busy callback is NULL, then [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] +** is returned immediately upon encountering the lock. ^If the busy callback +** is not NULL, then the callback might be invoked with two arguments. +** +** ^The first argument to the busy handler is a copy of the void* pointer which +** is the third argument to sqlite3_busy_handler(). ^The second argument to +** the busy handler callback is the number of times that the busy handler has +** been invoked for this locking event. ^If the ** busy callback returns 0, then no additional attempts are made to ** access the database and [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] is returned. -** If the callback returns non-zero, then another attempt +** ^If the callback returns non-zero, then another attempt ** is made to open the database for reading and the cycle repeats. ** ** The presence of a busy handler does not guarantee that it will be invoked -** when there is lock contention. If SQLite determines that invoking the busy +** when there is lock contention. ^If SQLite determines that invoking the busy ** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY] ** or [SQLITE_IOERR_BLOCKED] instead of invoking the busy handler. ** Consider a scenario where one process is holding a read lock that @@ -1953,65 +2064,59 @@ ** will induce the first process to release its read lock and allow ** the second process to proceed. ** -** The default busy callback is NULL. +** ^The default busy callback is NULL. ** -** The [SQLITE_BUSY] error is converted to [SQLITE_IOERR_BLOCKED] +** ^The [SQLITE_BUSY] error is converted to [SQLITE_IOERR_BLOCKED] ** when SQLite is in the middle of a large transaction where all the ** changes will not fit into the in-memory cache. SQLite will ** already hold a RESERVED lock on the database file, but it needs ** to promote this lock to EXCLUSIVE so that it can spill cache ** pages into the database file without harm to concurrent -** readers. If it is unable to promote the lock, then the in-memory +** readers. ^If it is unable to promote the lock, then the in-memory ** cache will be left in an inconsistent state and so the error ** code is promoted from the relatively benign [SQLITE_BUSY] to -** the more severe [SQLITE_IOERR_BLOCKED]. This error code promotion +** the more severe [SQLITE_IOERR_BLOCKED]. ^This error code promotion ** forces an automatic rollback of the changes. See the ** ** CorruptionFollowingBusyError wiki page for a discussion of why ** this is important. ** -** There can only be a single busy handler defined for each +** ^(There can only be a single busy handler defined for each ** [database connection]. Setting a new busy handler clears any -** previously set handler. Note that calling [sqlite3_busy_timeout()] +** previously set handler.)^ ^Note that calling [sqlite3_busy_timeout()] ** will also set or clear the busy handler. ** ** The busy callback should not take any actions which modify the ** database connection that invoked the busy handler. Any such actions ** result in undefined behavior. ** -** Requirements: -** [H12311] [H12312] [H12314] [H12316] [H12318] -** ** A busy handler must not close the database connection ** or [prepared statement] that invoked the busy handler. */ SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); /* -** CAPI3REF: Set A Busy Timeout {H12340} +** CAPI3REF: Set A Busy Timeout ** -** This routine sets a [sqlite3_busy_handler | busy handler] that sleeps -** for a specified amount of time when a table is locked. The handler +** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps +** for a specified amount of time when a table is locked. ^The handler ** will sleep multiple times until at least "ms" milliseconds of sleeping -** have accumulated. {H12343} After "ms" milliseconds of sleeping, +** have accumulated. ^After at least "ms" milliseconds of sleeping, ** the handler returns 0 which causes [sqlite3_step()] to return ** [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED]. ** -** Calling this routine with an argument less than or equal to zero +** ^Calling this routine with an argument less than or equal to zero ** turns off all busy handlers. ** -** There can only be a single busy handler for a particular +** ^(There can only be a single busy handler for a particular ** [database connection] any any given moment. If another busy handler ** was defined (using [sqlite3_busy_handler()]) prior to calling -** this routine, that other busy handler is cleared. -** -** Requirements: -** [H12341] [H12343] [H12344] +** this routine, that other busy handler is cleared.)^ */ SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); /* -** CAPI3REF: Convenience Routines For Running Queries {H12370} +** CAPI3REF: Convenience Routines For Running Queries ** ** Definition: A result table is memory data structure created by the ** [sqlite3_get_table()] interface. A result table records the @@ -2059,27 +2164,25 @@ ** azResult[7] = "21"; ** ** -** The sqlite3_get_table() function evaluates one or more +** ^The sqlite3_get_table() function evaluates one or more ** semicolon-separated SQL statements in the zero-terminated UTF-8 -** string of its 2nd parameter. It returns a result table to the +** string of its 2nd parameter and returns a result table to the ** pointer given in its 3rd parameter. ** -** After the calling function has finished using the result, it should -** pass the pointer to the result table to sqlite3_free_table() in order to +** After the application has finished with the result from sqlite3_get_table(), +** it should pass the result table pointer to sqlite3_free_table() in order to ** release the memory that was malloced. Because of the way the ** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling ** function must not try to call [sqlite3_free()] directly. Only ** [sqlite3_free_table()] is able to release the memory properly and safely. ** -** The sqlite3_get_table() interface is implemented as a wrapper around +** ^(The sqlite3_get_table() interface is implemented as a wrapper around ** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access ** to any internal data structures of SQLite. It uses only the public ** interface defined here. As a consequence, errors that occur in the ** wrapper layer outside of the internal [sqlite3_exec()] call are not -** reflected in subsequent calls to [sqlite3_errcode()] or [sqlite3_errmsg()]. -** -** Requirements: -** [H12371] [H12373] [H12374] [H12376] [H12379] [H12382] +** reflected in subsequent calls to [sqlite3_errcode()] or +** [sqlite3_errmsg()].)^ */ SQLITE_API int sqlite3_get_table( sqlite3 *db, /* An open database */ @@ -2092,33 +2195,33 @@ SQLITE_API void sqlite3_free_table(char **result); /* -** CAPI3REF: Formatted String Printing Functions {H17400} +** CAPI3REF: Formatted String Printing Functions ** -** These routines are workalikes of the "printf()" family of functions +** These routines are work-alikes of the "printf()" family of functions ** from the standard C library. ** -** The sqlite3_mprintf() and sqlite3_vmprintf() routines write their +** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their ** results into memory obtained from [sqlite3_malloc()]. ** The strings returned by these two routines should be -** released by [sqlite3_free()]. Both routines return a +** released by [sqlite3_free()]. ^Both routines return a ** NULL pointer if [sqlite3_malloc()] is unable to allocate enough ** memory to hold the resulting string. ** -** In sqlite3_snprintf() routine is similar to "snprintf()" from +** ^(In sqlite3_snprintf() routine is similar to "snprintf()" from ** the standard C library. The result is written into the ** buffer supplied as the second parameter whose size is given by ** the first parameter. Note that the order of the -** first two parameters is reversed from snprintf(). This is an +** first two parameters is reversed from snprintf().)^ This is an ** historical accident that cannot be fixed without breaking -** backwards compatibility. Note also that sqlite3_snprintf() +** backwards compatibility. ^(Note also that sqlite3_snprintf() ** returns a pointer to its buffer instead of the number of -** characters actually written into the buffer. We admit that +** characters actually written into the buffer.)^ We admit that ** the number of characters written would be a more useful return ** value but we cannot change the implementation of sqlite3_snprintf() ** now without breaking compatibility. ** -** As long as the buffer size is greater than zero, sqlite3_snprintf() -** guarantees that the buffer is always zero-terminated. The first +** ^As long as the buffer size is greater than zero, sqlite3_snprintf() +** guarantees that the buffer is always zero-terminated. ^The first ** parameter "n" is the total size of the buffer, including space for ** the zero terminator. So the longest string that can be completely ** written will be n-1 characters. @@ -2128,9 +2231,9 @@ ** All of the usual printf() formatting options apply. In addition, there ** is are "%q", "%Q", and "%z" options. ** -** The %q option works like %s in that it substitutes a null-terminated +** ^(The %q option works like %s in that it substitutes a null-terminated ** string from the argument list. But %q also doubles every '\'' character. -** %q is designed for use inside a string literal. By doubling each '\'' +** %q is designed for use inside a string literal.)^ By doubling each '\'' ** character it escapes that character and allows it to be inserted into ** the string. ** @@ -2165,10 +2268,10 @@ ** This second example is an SQL syntax error. As a general rule you should ** always use %q instead of %s when inserting text into a string literal. ** -** The %Q option works like %q except it also adds single quotes around +** ^(The %Q option works like %q except it also adds single quotes around ** the outside of the total string. Additionally, if the parameter in the ** argument list is a NULL pointer, %Q substitutes the text "NULL" (without -** single quotes) in place of the %Q option. So, for example, one could say: +** single quotes).)^ So, for example, one could say: ** **
 **  char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText);
@@ -2179,35 +2282,32 @@
 ** The code above will render a correct SQL statement in the zSQL
 ** variable even if the zText variable is a NULL pointer.
 **
-** The "%z" formatting option works exactly like "%s" with the
+** ^(The "%z" formatting option works like "%s" but with the
 ** addition that after the string has been read and copied into
-** the result, [sqlite3_free()] is called on the input string. {END}
-**
-** Requirements:
-** [H17403] [H17406] [H17407]
+** the result, [sqlite3_free()] is called on the input string.)^
 */
 SQLITE_API char *sqlite3_mprintf(const char*,...);
 SQLITE_API char *sqlite3_vmprintf(const char*, va_list);
 SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...);
 
 /*
-** CAPI3REF: Memory Allocation Subsystem {H17300} 
+** CAPI3REF: Memory Allocation Subsystem
 **
-** The SQLite core  uses these three routines for all of its own
+** The SQLite core uses these three routines for all of its own
 ** internal memory allocation needs. "Core" in the previous sentence
 ** does not include operating-system specific VFS implementation.  The
 ** Windows VFS uses native malloc() and free() for some operations.
 **
-** The sqlite3_malloc() routine returns a pointer to a block
+** ^The sqlite3_malloc() routine returns a pointer to a block
 ** of memory at least N bytes in length, where N is the parameter.
-** If sqlite3_malloc() is unable to obtain sufficient free
-** memory, it returns a NULL pointer.  If the parameter N to
+** ^If sqlite3_malloc() is unable to obtain sufficient free
+** memory, it returns a NULL pointer.  ^If the parameter N to
 ** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns
 ** a NULL pointer.
 **
-** Calling sqlite3_free() with a pointer previously returned
+** ^Calling sqlite3_free() with a pointer previously returned
 ** by sqlite3_malloc() or sqlite3_realloc() releases that memory so
-** that it might be reused.  The sqlite3_free() routine is
+** that it might be reused.  ^The sqlite3_free() routine is
 ** a no-op if is called with a NULL pointer.  Passing a NULL pointer
 ** to sqlite3_free() is harmless.  After being freed, memory
 ** should neither be read nor written.  Even reading previously freed
@@ -2216,34 +2316,25 @@
 ** might result if sqlite3_free() is called with a non-NULL pointer that
 ** was not obtained from sqlite3_malloc() or sqlite3_realloc().
 **
-** The sqlite3_realloc() interface attempts to resize a
+** ^(The sqlite3_realloc() interface attempts to resize a
 ** prior memory allocation to be at least N bytes, where N is the
 ** second parameter.  The memory allocation to be resized is the first
-** parameter.  If the first parameter to sqlite3_realloc()
+** parameter.)^ ^ If the first parameter to sqlite3_realloc()
 ** is a NULL pointer then its behavior is identical to calling
 ** sqlite3_malloc(N) where N is the second parameter to sqlite3_realloc().
-** If the second parameter to sqlite3_realloc() is zero or
+** ^If the second parameter to sqlite3_realloc() is zero or
 ** negative then the behavior is exactly the same as calling
 ** sqlite3_free(P) where P is the first parameter to sqlite3_realloc().
-** sqlite3_realloc() returns a pointer to a memory allocation
+** ^sqlite3_realloc() returns a pointer to a memory allocation
 ** of at least N bytes in size or NULL if sufficient memory is unavailable.
-** If M is the size of the prior allocation, then min(N,M) bytes
+** ^If M is the size of the prior allocation, then min(N,M) bytes
 ** of the prior allocation are copied into the beginning of buffer returned
 ** by sqlite3_realloc() and the prior allocation is freed.
-** If sqlite3_realloc() returns NULL, then the prior allocation
+** ^If sqlite3_realloc() returns NULL, then the prior allocation
 ** is not freed.
 **
-** The memory returned by sqlite3_malloc() and sqlite3_realloc()
-** is always aligned to at least an 8 byte boundary. {END}
-**
-** The default implementation of the memory allocation subsystem uses
-** the malloc(), realloc() and free() provided by the standard C library.
-** {H17382} However, if SQLite is compiled with the
-** SQLITE_MEMORY_SIZE=NNN C preprocessor macro (where NNN
-** is an integer), then SQLite create a static array of at least
-** NNN bytes in size and uses that array for all of its dynamic
-** memory allocation needs. {END}  Additional memory allocator options
-** may be added in future releases.
+** ^The memory returned by sqlite3_malloc() and sqlite3_realloc()
+** is always aligned to at least an 8 byte boundary.
 **
 ** In SQLite version 3.5.0 and 3.5.1, it was possible to define
 ** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in
@@ -2258,10 +2349,6 @@
 ** they are reported back as [SQLITE_CANTOPEN] or
 ** [SQLITE_IOERR] rather than [SQLITE_NOMEM].
 **
-** Requirements:
-** [H17303] [H17304] [H17305] [H17306] [H17310] [H17312] [H17315] [H17318]
-** [H17321] [H17322] [H17323]
-**
 ** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()]
 ** must be either NULL or else pointers obtained from a prior
 ** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have
@@ -2276,20 +2363,33 @@
 SQLITE_API void sqlite3_free(void*);
 
 /*
-** CAPI3REF: Memory Allocator Statistics {H17370} 
+** CAPI3REF: Memory Allocator Statistics
 **
 ** SQLite provides these two interfaces for reporting on the status
 ** of the [sqlite3_malloc()], [sqlite3_free()], and [sqlite3_realloc()]
 ** routines, which form the built-in memory allocation subsystem.
 **
-** Requirements:
-** [H17371] [H17373] [H17374] [H17375]
+** ^The [sqlite3_memory_used()] routine returns the number of bytes
+** of memory currently outstanding (malloced but not freed).
+** ^The [sqlite3_memory_highwater()] routine returns the maximum
+** value of [sqlite3_memory_used()] since the high-water mark
+** was last reset.  ^The values returned by [sqlite3_memory_used()] and
+** [sqlite3_memory_highwater()] include any overhead
+** added by SQLite in its implementation of [sqlite3_malloc()],
+** but not overhead added by the any underlying system library
+** routines that [sqlite3_malloc()] may call.
+**
+** ^The memory high-water mark is reset to the current value of
+** [sqlite3_memory_used()] if and only if the parameter to
+** [sqlite3_memory_highwater()] is true.  ^The value returned
+** by [sqlite3_memory_highwater(1)] is the high-water mark
+** prior to the reset.
 */
 SQLITE_API sqlite3_int64 sqlite3_memory_used(void);
 SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag);
 
 /*
-** CAPI3REF: Pseudo-Random Number Generator {H17390} 
+** CAPI3REF: Pseudo-Random Number Generator
 **
 ** SQLite contains a high-quality pseudo-random number generator (PRNG) used to
 ** select random [ROWID | ROWIDs] when inserting new records into a table that
@@ -2297,60 +2397,57 @@
 ** the build-in random() and randomblob() SQL functions.  This interface allows
 ** applications to access the same PRNG for other purposes.
 **
-** A call to this routine stores N bytes of randomness into buffer P.
+** ^A call to this routine stores N bytes of randomness into buffer P.
 **
-** The first time this routine is invoked (either internally or by
+** ^The first time this routine is invoked (either internally or by
 ** the application) the PRNG is seeded using randomness obtained
 ** from the xRandomness method of the default [sqlite3_vfs] object.
-** On all subsequent invocations, the pseudo-randomness is generated
+** ^On all subsequent invocations, the pseudo-randomness is generated
 ** internally and without recourse to the [sqlite3_vfs] xRandomness
 ** method.
-**
-** Requirements:
-** [H17392]
 */
 SQLITE_API void sqlite3_randomness(int N, void *P);
 
 /*
-** CAPI3REF: Compile-Time Authorization Callbacks {H12500} 
+** CAPI3REF: Compile-Time Authorization Callbacks
 **
-** This routine registers a authorizer callback with a particular
+** ^This routine registers a authorizer callback with a particular
 ** [database connection], supplied in the first argument.
-** The authorizer callback is invoked as SQL statements are being compiled
+** ^The authorizer callback is invoked as SQL statements are being compiled
 ** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()],
-** [sqlite3_prepare16()] and [sqlite3_prepare16_v2()].  At various
+** [sqlite3_prepare16()] and [sqlite3_prepare16_v2()].  ^At various
 ** points during the compilation process, as logic is being created
 ** to perform various actions, the authorizer callback is invoked to
-** see if those actions are allowed.  The authorizer callback should
+** see if those actions are allowed.  ^The authorizer callback should
 ** return [SQLITE_OK] to allow the action, [SQLITE_IGNORE] to disallow the
 ** specific action but allow the SQL statement to continue to be
 ** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be
-** rejected with an error.  If the authorizer callback returns
+** rejected with an error.  ^If the authorizer callback returns
 ** any value other than [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY]
 ** then the [sqlite3_prepare_v2()] or equivalent call that triggered
 ** the authorizer will fail with an error message.
 **
 ** When the callback returns [SQLITE_OK], that means the operation
-** requested is ok.  When the callback returns [SQLITE_DENY], the
+** requested is ok.  ^When the callback returns [SQLITE_DENY], the
 ** [sqlite3_prepare_v2()] or equivalent call that triggered the
 ** authorizer will fail with an error message explaining that
 ** access is denied. 
 **
-** The first parameter to the authorizer callback is a copy of the third
-** parameter to the sqlite3_set_authorizer() interface. The second parameter
+** ^The first parameter to the authorizer callback is a copy of the third
+** parameter to the sqlite3_set_authorizer() interface. ^The second parameter
 ** to the callback is an integer [SQLITE_COPY | action code] that specifies
-** the particular action to be authorized. The third through sixth parameters
+** the particular action to be authorized. ^The third through sixth parameters
 ** to the callback are zero-terminated strings that contain additional
 ** details about the action to be authorized.
 **
-** If the action code is [SQLITE_READ]
+** ^If the action code is [SQLITE_READ]
 ** and the callback returns [SQLITE_IGNORE] then the
 ** [prepared statement] statement is constructed to substitute
 ** a NULL value in place of the table column that would have
 ** been read if [SQLITE_OK] had been returned.  The [SQLITE_IGNORE]
 ** return can be used to deny an untrusted user access to individual
 ** columns of a table.
-** If the action code is [SQLITE_DELETE] and the callback returns
+** ^If the action code is [SQLITE_DELETE] and the callback returns
 ** [SQLITE_IGNORE] then the [DELETE] operation proceeds but the
 ** [truncate optimization] is disabled and all rows are deleted individually.
 **
@@ -2370,9 +2467,9 @@
 ** and limiting database size using the [max_page_count] [PRAGMA]
 ** in addition to using an authorizer.
 **
-** Only a single authorizer can be in place on a database connection
+** ^(Only a single authorizer can be in place on a database connection
 ** at a time.  Each call to sqlite3_set_authorizer overrides the
-** previous call.  Disable the authorizer by installing a NULL callback.
+** previous call.)^  ^Disable the authorizer by installing a NULL callback.
 ** The authorizer is disabled by default.
 **
 ** The authorizer callback must not do anything that will modify
@@ -2380,20 +2477,16 @@
 ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
 ** database connections for the meaning of "modify" in this paragraph.
 **
-** When [sqlite3_prepare_v2()] is used to prepare a statement, the
-** statement might be reprepared during [sqlite3_step()] due to a 
+** ^When [sqlite3_prepare_v2()] is used to prepare a statement, the
+** statement might be re-prepared during [sqlite3_step()] due to a 
 ** schema change.  Hence, the application should ensure that the
 ** correct authorizer callback remains in place during the [sqlite3_step()].
 **
-** Note that the authorizer callback is invoked only during
+** ^Note that the authorizer callback is invoked only during
 ** [sqlite3_prepare()] or its variants.  Authorization is not
 ** performed during statement evaluation in [sqlite3_step()], unless
 ** as stated in the previous paragraph, sqlite3_step() invokes
 ** sqlite3_prepare_v2() to reprepare a statement after a schema change.
-**
-** Requirements:
-** [H12501] [H12502] [H12503] [H12504] [H12505] [H12506] [H12507] [H12510]
-** [H12511] [H12512] [H12520] [H12521] [H12522]
 */
 SQLITE_API int sqlite3_set_authorizer(
   sqlite3*,
@@ -2402,7 +2495,7 @@
 );
 
 /*
-** CAPI3REF: Authorizer Return Codes {H12590} 
+** CAPI3REF: Authorizer Return Codes
 **
 ** The [sqlite3_set_authorizer | authorizer callback function] must
 ** return either [SQLITE_OK] or one of these two constants in order
@@ -2414,7 +2507,7 @@
 #define SQLITE_IGNORE 2   /* Don't allow access, but don't generate an error */
 
 /*
-** CAPI3REF: Authorizer Action Codes {H12550} 
+** CAPI3REF: Authorizer Action Codes
 **
 ** The [sqlite3_set_authorizer()] interface registers a callback function
 ** that is invoked to authorize certain SQL statement actions.  The
@@ -2425,15 +2518,12 @@
 ** These action code values signify what kind of operation is to be
 ** authorized.  The 3rd and 4th parameters to the authorization
 ** callback function will be parameters or NULL depending on which of these
-** codes is used as the second parameter.  The 5th parameter to the
+** codes is used as the second parameter.  ^(The 5th parameter to the
 ** authorizer callback is the name of the database ("main", "temp",
-** etc.) if applicable.  The 6th parameter to the authorizer callback
+** etc.) if applicable.)^  ^The 6th parameter to the authorizer callback
 ** is the name of the inner-most trigger or view that is responsible for
 ** the access attempt or NULL if this access attempt is directly from
 ** top-level SQL code.
-**
-** Requirements:
-** [H12551] [H12552] [H12553] [H12554]
 */
 /******************************************* 3rd ************ 4th ***********/
 #define SQLITE_CREATE_INDEX          1   /* Index Name      Table Name      */
@@ -2471,42 +2561,39 @@
 #define SQLITE_COPY                  0   /* No longer used */
 
 /*
-** CAPI3REF: Tracing And Profiling Functions {H12280} 
+** CAPI3REF: Tracing And Profiling Functions
 ** EXPERIMENTAL
 **
 ** These routines register callback functions that can be used for
 ** tracing and profiling the execution of SQL statements.
 **
-** The callback function registered by sqlite3_trace() is invoked at
+** ^The callback function registered by sqlite3_trace() is invoked at
 ** various times when an SQL statement is being run by [sqlite3_step()].
-** The callback returns a UTF-8 rendering of the SQL statement text
-** as the statement first begins executing.  Additional callbacks occur
+** ^The sqlite3_trace() callback is invoked with a UTF-8 rendering of the
+** SQL statement text as the statement first begins executing.
+** ^(Additional sqlite3_trace() callbacks might occur
 ** as each triggered subprogram is entered.  The callbacks for triggers
-** contain a UTF-8 SQL comment that identifies the trigger.
+** contain a UTF-8 SQL comment that identifies the trigger.)^
 **
-** The callback function registered by sqlite3_profile() is invoked
-** as each SQL statement finishes.  The profile callback contains
+** ^The callback function registered by sqlite3_profile() is invoked
+** as each SQL statement finishes.  ^The profile callback contains
 ** the original statement text and an estimate of wall-clock time
 ** of how long that statement took to run.
-**
-** Requirements:
-** [H12281] [H12282] [H12283] [H12284] [H12285] [H12287] [H12288] [H12289]
-** [H12290]
 */
 SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
 SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,
    void(*xProfile)(void*,const char*,sqlite3_uint64), void*);
 
 /*
-** CAPI3REF: Query Progress Callbacks {H12910} 
+** CAPI3REF: Query Progress Callbacks
 **
-** This routine configures a callback function - the
+** ^This routine configures a callback function - the
 ** progress callback - that is invoked periodically during long
 ** running calls to [sqlite3_exec()], [sqlite3_step()] and
 ** [sqlite3_get_table()].  An example use for this
 ** interface is to keep a GUI updated during a large query.
 **
-** If the progress callback returns non-zero, the operation is
+** ^If the progress callback returns non-zero, the operation is
 ** interrupted.  This feature can be used to implement a
 ** "Cancel" button on a GUI progress dialog box.
 **
@@ -2515,28 +2602,26 @@
 ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
 ** database connections for the meaning of "modify" in this paragraph.
 **
-** Requirements:
-** [H12911] [H12912] [H12913] [H12914] [H12915] [H12916] [H12917] [H12918]
-**
 */
 SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
 
 /*
-** CAPI3REF: Opening A New Database Connection {H12700} 
+** CAPI3REF: Opening A New Database Connection
 **
-** These routines open an SQLite database file whose name is given by the
-** filename argument. The filename argument is interpreted as UTF-8 for
+** ^These routines open an SQLite database file whose name is given by the
+** filename argument. ^The filename argument is interpreted as UTF-8 for
 ** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte
-** order for sqlite3_open16(). A [database connection] handle is usually
+** order for sqlite3_open16(). ^(A [database connection] handle is usually
 ** returned in *ppDb, even if an error occurs.  The only exception is that
 ** if SQLite is unable to allocate memory to hold the [sqlite3] object,
 ** a NULL will be written into *ppDb instead of a pointer to the [sqlite3]
-** object. If the database is opened (and/or created) successfully, then
-** [SQLITE_OK] is returned.  Otherwise an [error code] is returned.  The
+** object.)^ ^(If the database is opened (and/or created) successfully, then
+** [SQLITE_OK] is returned.  Otherwise an [error code] is returned.)^ ^The
 ** [sqlite3_errmsg()] or [sqlite3_errmsg16()] routines can be used to obtain
-** an English language description of the error.
+** an English language description of the error following a failure of any
+** of the sqlite3_open() routines.
 **
-** The default encoding for the database will be UTF-8 if
+** ^The default encoding for the database will be UTF-8 if
 ** sqlite3_open() or sqlite3_open_v2() is called and
 ** UTF-16 in the native byte order if sqlite3_open16() is used.
 **
@@ -2546,53 +2631,61 @@
 **
 ** The sqlite3_open_v2() interface works like sqlite3_open()
 ** except that it accepts two additional parameters for additional control
-** over the new database connection.  The flags parameter can take one of
+** over the new database connection.  ^(The flags parameter to
+** sqlite3_open_v2() can take one of
 ** the following three values, optionally combined with the 
-** [SQLITE_OPEN_NOMUTEX] or [SQLITE_OPEN_FULLMUTEX] flags:
+** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE],
+** and/or [SQLITE_OPEN_PRIVATECACHE] flags:)^
 **
 ** 
-**
[SQLITE_OPEN_READONLY]
+** ^(
[SQLITE_OPEN_READONLY]
**
The database is opened in read-only mode. If the database does not -** already exist, an error is returned.
+** already exist, an error is returned.)^ ** -**
[SQLITE_OPEN_READWRITE]
+** ^(
[SQLITE_OPEN_READWRITE]
**
The database is opened for reading and writing if possible, or reading ** only if the file is write protected by the operating system. In either -** case the database must already exist, otherwise an error is returned.
+** case the database must already exist, otherwise an error is returned.)^ ** -**
[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
+** ^(
[SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
**
The database is opened for reading and writing, and is creates it if ** it does not already exist. This is the behavior that is always used for -** sqlite3_open() and sqlite3_open16().
+** sqlite3_open() and sqlite3_open16().)^ **
** ** If the 3rd parameter to sqlite3_open_v2() is not one of the ** combinations shown above or one of the combinations shown above combined -** with the [SQLITE_OPEN_NOMUTEX] or [SQLITE_OPEN_FULLMUTEX] flags, +** with the [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], +** [SQLITE_OPEN_SHAREDCACHE] and/or [SQLITE_OPEN_SHAREDCACHE] flags, ** then the behavior is undefined. ** -** If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection +** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection ** opens in the multi-thread [threading mode] as long as the single-thread -** mode has not been set at compile-time or start-time. If the +** mode has not been set at compile-time or start-time. ^If the ** [SQLITE_OPEN_FULLMUTEX] flag is set then the database connection opens ** in the serialized [threading mode] unless single-thread was ** previously selected at compile-time or start-time. +** ^The [SQLITE_OPEN_SHAREDCACHE] flag causes the database connection to be +** eligible to use [shared cache mode], regardless of whether or not shared +** cache is enabled using [sqlite3_enable_shared_cache()]. ^The +** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not +** participate in [shared cache mode] even if it is enabled. ** -** If the filename is ":memory:", then a private, temporary in-memory database -** is created for the connection. This in-memory database will vanish when +** ^If the filename is ":memory:", then a private, temporary in-memory database +** is created for the connection. ^This in-memory database will vanish when ** the database connection is closed. Future versions of SQLite might ** make use of additional special filenames that begin with the ":" character. ** It is recommended that when a database filename actually does begin with ** a ":" character you should prefix the filename with a pathname such as ** "./" to avoid ambiguity. ** -** If the filename is an empty string, then a private, temporary -** on-disk database will be created. This private database will be +** ^If the filename is an empty string, then a private, temporary +** on-disk database will be created. ^This private database will be ** automatically deleted as soon as the database connection is closed. ** -** The fourth parameter to sqlite3_open_v2() is the name of the +** ^The fourth parameter to sqlite3_open_v2() is the name of the ** [sqlite3_vfs] object that defines the operating system interface that -** the new database connection should use. If the fourth parameter is +** the new database connection should use. ^If the fourth parameter is ** a NULL pointer then the default [sqlite3_vfs] object is used. ** ** Note to Windows users: The encoding used for the filename argument @@ -2600,10 +2693,6 @@ ** codepage is currently defined. Filenames containing international ** characters must be converted to UTF-8 prior to passing them into ** sqlite3_open() or sqlite3_open_v2(). -** -** Requirements: -** [H12701] [H12702] [H12703] [H12704] [H12706] [H12707] [H12709] [H12711] -** [H12712] [H12713] [H12714] [H12717] [H12719] [H12721] [H12723] */ SQLITE_API int sqlite3_open( const char *filename, /* Database filename (UTF-8) */ @@ -2621,23 +2710,23 @@ ); /* -** CAPI3REF: Error Codes And Messages {H12800} +** CAPI3REF: Error Codes And Messages ** -** The sqlite3_errcode() interface returns the numeric [result code] or +** ^The sqlite3_errcode() interface returns the numeric [result code] or ** [extended result code] for the most recent failed sqlite3_* API call ** associated with a [database connection]. If a prior API call failed ** but the most recent API call succeeded, the return value from -** sqlite3_errcode() is undefined. The sqlite3_extended_errcode() +** sqlite3_errcode() is undefined. ^The sqlite3_extended_errcode() ** interface is the same except that it always returns the ** [extended result code] even when extended result codes are ** disabled. ** -** The sqlite3_errmsg() and sqlite3_errmsg16() return English-language +** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language ** text that describes the error, as either UTF-8 or UTF-16 respectively. -** Memory to hold the error message string is managed internally. +** ^(Memory to hold the error message string is managed internally. ** The application does not need to worry about freeing the result. ** However, the error string might be overwritten or deallocated by -** subsequent calls to other SQLite interface functions. +** subsequent calls to other SQLite interface functions.)^ ** ** When the serialized [threading mode] is in use, it might be the ** case that a second error occurs on a separate thread in between @@ -2652,9 +2741,6 @@ ** If an interface fails with SQLITE_MISUSE, that means the interface ** was invoked incorrectly by the application. In that case, the ** error code and message may or may not be set. -** -** Requirements: -** [H12801] [H12802] [H12803] [H12807] [H12808] [H12809] */ SQLITE_API int sqlite3_errcode(sqlite3 *db); SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); @@ -2662,7 +2748,7 @@ SQLITE_API const void *sqlite3_errmsg16(sqlite3*); /* -** CAPI3REF: SQL Statement Object {H13000} +** CAPI3REF: SQL Statement Object ** KEYWORDS: {prepared statement} {prepared statements} ** ** An instance of this object represents a single SQL statement. @@ -2688,25 +2774,25 @@ typedef struct sqlite3_stmt sqlite3_stmt; /* -** CAPI3REF: Run-time Limits {H12760} +** CAPI3REF: Run-time Limits ** -** This interface allows the size of various constructs to be limited +** ^(This interface allows the size of various constructs to be limited ** on a connection by connection basis. The first parameter is the ** [database connection] whose limit is to be set or queried. The ** second parameter is one of the [limit categories] that define a ** class of constructs to be size limited. The third parameter is the -** new limit for that construct. The function returns the old limit. +** new limit for that construct. The function returns the old limit.)^ ** -** If the new limit is a negative number, the limit is unchanged. -** For the limit category of SQLITE_LIMIT_XYZ there is a +** ^If the new limit is a negative number, the limit is unchanged. +** ^(For the limit category of SQLITE_LIMIT_XYZ there is a ** [limits | hard upper bound] ** set by a compile-time C preprocessor macro named ** [limits | SQLITE_MAX_XYZ]. -** (The "_LIMIT_" in the name is changed to "_MAX_".) -** Attempts to increase a limit above its hard upper bound are -** silently truncated to the hard upper limit. +** (The "_LIMIT_" in the name is changed to "_MAX_".))^ +** ^Attempts to increase a limit above its hard upper bound are +** silently truncated to the hard upper bound. ** -** Run time limits are intended for use in applications that manage +** Run-time limits are intended for use in applications that manage ** both their own internal database and also databases that are controlled ** by untrusted external sources. An example application might be a ** web browser that has its own databases for storing history and @@ -2720,15 +2806,12 @@ ** [max_page_count] [PRAGMA]. ** ** New run-time limit categories may be added in future releases. -** -** Requirements: -** [H12762] [H12766] [H12769] */ SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); /* -** CAPI3REF: Run-Time Limit Categories {H12790} -** KEYWORDS: {limit category} {limit categories} +** CAPI3REF: Run-Time Limit Categories +** KEYWORDS: {limit category} {*limit categories} ** ** These constants define various performance limits ** that can be lowered at run-time using [sqlite3_limit()]. @@ -2736,40 +2819,43 @@ ** Additional information is available at [limits | Limits in SQLite]. ** **
-**
SQLITE_LIMIT_LENGTH
-**
The maximum size of any string or BLOB or table row.
+** ^(
SQLITE_LIMIT_LENGTH
+**
The maximum size of any string or BLOB or table row.
)^ ** -**
SQLITE_LIMIT_SQL_LENGTH
-**
The maximum length of an SQL statement.
+** ^(
SQLITE_LIMIT_SQL_LENGTH
+**
The maximum length of an SQL statement, in bytes.
)^ ** -**
SQLITE_LIMIT_COLUMN
+** ^(
SQLITE_LIMIT_COLUMN
**
The maximum number of columns in a table definition or in the ** result set of a [SELECT] or the maximum number of columns in an index -** or in an ORDER BY or GROUP BY clause.
+** or in an ORDER BY or GROUP BY clause.)^ ** -**
SQLITE_LIMIT_EXPR_DEPTH
-**
The maximum depth of the parse tree on any expression.
+** ^(
SQLITE_LIMIT_EXPR_DEPTH
+**
The maximum depth of the parse tree on any expression.
)^ ** -**
SQLITE_LIMIT_COMPOUND_SELECT
-**
The maximum number of terms in a compound SELECT statement.
+** ^(
SQLITE_LIMIT_COMPOUND_SELECT
+**
The maximum number of terms in a compound SELECT statement.
)^ ** -**
SQLITE_LIMIT_VDBE_OP
+** ^(
SQLITE_LIMIT_VDBE_OP
**
The maximum number of instructions in a virtual machine program -** used to implement an SQL statement.
+** used to implement an SQL statement.)^ ** -**
SQLITE_LIMIT_FUNCTION_ARG
-**
The maximum number of arguments on a function.
+** ^(
SQLITE_LIMIT_FUNCTION_ARG
+**
The maximum number of arguments on a function.
)^ ** -**
SQLITE_LIMIT_ATTACHED
-**
The maximum number of [ATTACH | attached databases].
+** ^(
SQLITE_LIMIT_ATTACHED
+**
The maximum number of [ATTACH | attached databases].)^
** -**
SQLITE_LIMIT_LIKE_PATTERN_LENGTH
+** ^(
SQLITE_LIMIT_LIKE_PATTERN_LENGTH
**
The maximum length of the pattern argument to the [LIKE] or -** [GLOB] operators.
+** [GLOB] operators.)^ ** -**
SQLITE_LIMIT_VARIABLE_NUMBER
+** ^(
SQLITE_LIMIT_VARIABLE_NUMBER
**
The maximum number of variables in an SQL statement that can -** be bound.
+** be bound.)^ +** +** ^(
SQLITE_LIMIT_TRIGGER_DEPTH
+**
The maximum depth of recursion for triggers.
)^ **
*/ #define SQLITE_LIMIT_LENGTH 0 @@ -2782,9 +2868,10 @@ #define SQLITE_LIMIT_ATTACHED 7 #define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 #define SQLITE_LIMIT_VARIABLE_NUMBER 9 +#define SQLITE_LIMIT_TRIGGER_DEPTH 10 /* -** CAPI3REF: Compiling An SQL Statement {H13010} +** CAPI3REF: Compiling An SQL Statement ** KEYWORDS: {SQL statement compiler} ** ** To execute an SQL query, it must first be compiled into a byte-code @@ -2799,9 +2886,9 @@ ** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2() ** use UTF-16. ** -** If the nByte argument is less than zero, then zSql is read up to the -** first zero terminator. If nByte is non-negative, then it is the maximum -** number of bytes read from zSql. When nByte is non-negative, the +** ^If the nByte argument is less than zero, then zSql is read up to the +** first zero terminator. ^If nByte is non-negative, then it is the maximum +** number of bytes read from zSql. ^When nByte is non-negative, the ** zSql string ends at either the first '\000' or '\u0000' character or ** the nByte-th byte, whichever comes first. If the caller knows ** that the supplied string is nul-terminated, then there is a small @@ -2809,34 +2896,35 @@ ** is equal to the number of bytes in the input string including ** the nul-terminator bytes. ** -** If pzTail is not NULL then *pzTail is made to point to the first byte +** ^If pzTail is not NULL then *pzTail is made to point to the first byte ** past the end of the first SQL statement in zSql. These routines only ** compile the first statement in zSql, so *pzTail is left pointing to ** what remains uncompiled. ** -** *ppStmt is left pointing to a compiled [prepared statement] that can be -** executed using [sqlite3_step()]. If there is an error, *ppStmt is set -** to NULL. If the input text contains no SQL (if the input is an empty +** ^*ppStmt is left pointing to a compiled [prepared statement] that can be +** executed using [sqlite3_step()]. ^If there is an error, *ppStmt is set +** to NULL. ^If the input text contains no SQL (if the input is an empty ** string or a comment) then *ppStmt is set to NULL. ** The calling procedure is responsible for deleting the compiled ** SQL statement using [sqlite3_finalize()] after it has finished with it. ** ppStmt may not be NULL. ** -** On success, [SQLITE_OK] is returned, otherwise an [error code] is returned. +** ^On success, the sqlite3_prepare() family of routines return [SQLITE_OK]; +** otherwise an [error code] is returned. ** ** The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are ** recommended for all new programs. The two older interfaces are retained ** for backwards compatibility, but their use is discouraged. -** In the "v2" interfaces, the prepared statement +** ^In the "v2" interfaces, the prepared statement ** that is returned (the [sqlite3_stmt] object) contains a copy of the ** original SQL text. This causes the [sqlite3_step()] interface to -** behave a differently in two ways: +** behave differently in three ways: ** **
    **
  1. -** If the database schema changes, instead of returning [SQLITE_SCHEMA] as it +** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it ** always used to do, [sqlite3_step()] will automatically recompile the SQL -** statement and try to run it again. If the schema has changed in +** statement and try to run it again. ^If the schema has changed in ** a way that makes the statement no longer valid, [sqlite3_step()] will still ** return [SQLITE_SCHEMA]. But unlike the legacy behavior, [SQLITE_SCHEMA] is ** now a fatal error. Calling [sqlite3_prepare_v2()] again will not make the @@ -2845,18 +2933,22 @@ **
  2. ** **
  3. -** When an error occurs, [sqlite3_step()] will return one of the detailed -** [error codes] or [extended error codes]. The legacy behavior was that +** ^When an error occurs, [sqlite3_step()] will return one of the detailed +** [error codes] or [extended error codes]. ^The legacy behavior was that ** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code -** and you would have to make a second call to [sqlite3_reset()] in order -** to find the underlying cause of the problem. With the "v2" prepare +** and the application would have to make a second call to [sqlite3_reset()] +** in order to find the underlying cause of the problem. With the "v2" prepare ** interfaces, the underlying reason for the error is returned immediately. **
  4. -**
-** -** Requirements: -** [H13011] [H13012] [H13013] [H13014] [H13015] [H13016] [H13019] [H13021] ** +**
  • +** ^If the value of a [parameter | host parameter] in the WHERE clause might +** change the query plan for a statement, then the statement may be +** automatically recompiled (as if there had been a schema change) on the first +** [sqlite3_step()] call following any change to the +** [sqlite3_bind_text | bindings] of the [parameter]. +**
  • +** */ SQLITE_API int sqlite3_prepare( sqlite3 *db, /* Database handle */ @@ -2888,24 +2980,21 @@ ); /* -** CAPI3REF: Retrieving Statement SQL {H13100} +** CAPI3REF: Retrieving Statement SQL ** -** This interface can be used to retrieve a saved copy of the original +** ^This interface can be used to retrieve a saved copy of the original ** SQL text used to create a [prepared statement] if that statement was ** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. -** -** Requirements: -** [H13101] [H13102] [H13103] */ SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); /* -** CAPI3REF: Dynamically Typed Value Object {H15000} +** CAPI3REF: Dynamically Typed Value Object ** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} ** ** SQLite uses the sqlite3_value object to represent all values ** that can be stored in a database table. SQLite uses dynamic typing -** for the values it stores. Values stored in sqlite3_value objects +** for the values it stores. ^Values stored in sqlite3_value objects ** can be integers, floating point values, strings, BLOBs, or NULL. ** ** An sqlite3_value object may be either "protected" or "unprotected". @@ -2927,9 +3016,9 @@ ** still make the distinction between between protected and unprotected ** sqlite3_value objects even when not strictly required. ** -** The sqlite3_value objects that are passed as parameters into the +** ^The sqlite3_value objects that are passed as parameters into the ** implementation of [application-defined SQL functions] are protected. -** The sqlite3_value object returned by +** ^The sqlite3_value object returned by ** [sqlite3_column_value()] is unprotected. ** Unprotected sqlite3_value objects may only be used with ** [sqlite3_result_value()] and [sqlite3_bind_value()]. @@ -2939,10 +3028,10 @@ typedef struct Mem sqlite3_value; /* -** CAPI3REF: SQL Function Context Object {H16001} +** CAPI3REF: SQL Function Context Object ** ** The context in which an SQL function executes is stored in an -** sqlite3_context object. A pointer to an sqlite3_context object +** sqlite3_context object. ^A pointer to an sqlite3_context object ** is always first parameter to [application-defined SQL functions]. ** The application-defined SQL function implementation will pass this ** pointer through into calls to [sqlite3_result_int | sqlite3_result()], @@ -2953,12 +3042,13 @@ typedef struct sqlite3_context sqlite3_context; /* -** CAPI3REF: Binding Values To Prepared Statements {H13500} +** CAPI3REF: Binding Values To Prepared Statements ** KEYWORDS: {host parameter} {host parameters} {host parameter name} ** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding} ** -** In the SQL strings input to [sqlite3_prepare_v2()] and its variants, -** literals may be replaced by a [parameter] in one of these forms: +** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants, +** literals may be replaced by a [parameter] that matches one of following +** templates: ** **
      **
    • ? @@ -2968,73 +3058,67 @@ **
    • $VVV **
    ** -** In the parameter forms shown above NNN is an integer literal, -** and VVV is an alpha-numeric parameter name. The values of these +** In the templates above, NNN represents an integer literal, +** and VVV represents an alphanumeric identifer.)^ ^The values of these ** parameters (also called "host parameter names" or "SQL parameters") ** can be set using the sqlite3_bind_*() routines defined here. ** -** The first argument to the sqlite3_bind_*() routines is always +** ^The first argument to the sqlite3_bind_*() routines is always ** a pointer to the [sqlite3_stmt] object returned from ** [sqlite3_prepare_v2()] or its variants. ** -** The second argument is the index of the SQL parameter to be set. -** The leftmost SQL parameter has an index of 1. When the same named +** ^The second argument is the index of the SQL parameter to be set. +** ^The leftmost SQL parameter has an index of 1. ^When the same named ** SQL parameter is used more than once, second and subsequent ** occurrences have the same index as the first occurrence. -** The index for named parameters can be looked up using the -** [sqlite3_bind_parameter_index()] API if desired. The index +** ^The index for named parameters can be looked up using the +** [sqlite3_bind_parameter_index()] API if desired. ^The index ** for "?NNN" parameters is the value of NNN. -** The NNN value must be between 1 and the [sqlite3_limit()] +** ^The NNN value must be between 1 and the [sqlite3_limit()] ** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999). ** -** The third argument is the value to bind to the parameter. +** ^The third argument is the value to bind to the parameter. ** -** In those routines that have a fourth argument, its value is the +** ^(In those routines that have a fourth argument, its value is the ** number of bytes in the parameter. To be clear: the value is the -** number of bytes in the value, not the number of characters. -** If the fourth parameter is negative, the length of the string is +** number of bytes in the value, not the number of characters.)^ +** ^If the fourth parameter is negative, the length of the string is ** the number of bytes up to the first zero terminator. ** -** The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and +** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and ** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or -** string after SQLite has finished with it. If the fifth argument is +** string after SQLite has finished with it. ^If the fifth argument is ** the special value [SQLITE_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. -** If the fifth argument has the value [SQLITE_TRANSIENT], then +** ^If the fifth argument has the value [SQLITE_TRANSIENT], then ** SQLite makes its own private copy of the data immediately, before ** the sqlite3_bind_*() routine returns. ** -** The sqlite3_bind_zeroblob() routine binds a BLOB of length N that -** is filled with zeroes. A zeroblob uses a fixed amount of memory +** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that +** is filled with zeroes. ^A zeroblob uses a fixed amount of memory ** (just an integer to hold its size) while it is being processed. ** Zeroblobs are intended to serve as placeholders for BLOBs whose ** content is later written using ** [sqlite3_blob_open | incremental BLOB I/O] routines. -** A negative value for the zeroblob results in a zero-length BLOB. +** ^A negative value for the zeroblob results in a zero-length BLOB. ** -** The sqlite3_bind_*() routines must be called after -** [sqlite3_prepare_v2()] (and its variants) or [sqlite3_reset()] and -** before [sqlite3_step()]. -** Bindings are not cleared by the [sqlite3_reset()] routine. -** Unbound parameters are interpreted as NULL. -** -** These routines return [SQLITE_OK] on success or an error code if -** anything goes wrong. [SQLITE_RANGE] is returned if the parameter -** index is out of range. [SQLITE_NOMEM] is returned if malloc() fails. -** [SQLITE_MISUSE] might be returned if these routines are called on a -** virtual machine that is the wrong state or which has already been finalized. -** Detection of misuse is unreliable. Applications should not depend -** on SQLITE_MISUSE returns. SQLITE_MISUSE is intended to indicate a -** a logic error in the application. Future versions of SQLite might -** panic rather than return SQLITE_MISUSE. +** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer +** for the [prepared statement] or with a prepared statement for which +** [sqlite3_step()] has been called more recently than [sqlite3_reset()], +** then the call will return [SQLITE_MISUSE]. If any sqlite3_bind_() +** routine is passed a [prepared statement] that has been finalized, the +** result is undefined and probably harmful. +** +** ^Bindings are not cleared by the [sqlite3_reset()] routine. +** ^Unbound parameters are interpreted as NULL. +** +** ^The sqlite3_bind_* routines return [SQLITE_OK] on success or an +** [error code] if anything goes wrong. +** ^[SQLITE_RANGE] is returned if the parameter +** index is out of range. ^[SQLITE_NOMEM] is returned if malloc() fails. ** ** See also: [sqlite3_bind_parameter_count()], ** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()]. -** -** Requirements: -** [H13506] [H13509] [H13512] [H13515] [H13518] [H13521] [H13524] [H13527] -** [H13530] [H13533] [H13536] [H13539] [H13542] [H13545] [H13548] [H13551] -** */ SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double); @@ -3047,45 +3131,42 @@ SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); /* -** CAPI3REF: Number Of SQL Parameters {H13600} +** CAPI3REF: Number Of SQL Parameters ** -** This routine can be used to find the number of [SQL parameters] +** ^This routine can be used to find the number of [SQL parameters] ** in a [prepared statement]. SQL parameters are tokens of the ** form "?", "?NNN", ":AAA", "$AAA", or "@AAA" that serve as ** placeholders for values that are [sqlite3_bind_blob | bound] ** to the parameters at a later time. ** -** This routine actually returns the index of the largest (rightmost) +** ^(This routine actually returns the index of the largest (rightmost) ** parameter. For all forms except ?NNN, this will correspond to the -** number of unique parameters. If parameters of the ?NNN are used, -** there may be gaps in the list. +** number of unique parameters. If parameters of the ?NNN form are used, +** there may be gaps in the list.)^ ** ** See also: [sqlite3_bind_blob|sqlite3_bind()], ** [sqlite3_bind_parameter_name()], and ** [sqlite3_bind_parameter_index()]. -** -** Requirements: -** [H13601] */ SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); /* -** CAPI3REF: Name Of A Host Parameter {H13620} +** CAPI3REF: Name Of A Host Parameter ** -** This routine returns a pointer to the name of the n-th -** [SQL parameter] in a [prepared statement]. -** SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA" +** ^The sqlite3_bind_parameter_name(P,N) interface returns +** the name of the N-th [SQL parameter] in the [prepared statement] P. +** ^(SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA" ** have a name which is the string "?NNN" or ":AAA" or "@AAA" or "$AAA" ** respectively. ** In other words, the initial ":" or "$" or "@" or "?" -** is included as part of the name. -** Parameters of the form "?" without a following integer have no name -** and are also referred to as "anonymous parameters". +** is included as part of the name.)^ +** ^Parameters of the form "?" without a following integer have no name +** and are referred to as "nameless" or "anonymous parameters". ** -** The first host parameter has an index of 1, not 0. +** ^The first host parameter has an index of 1, not 0. ** -** If the value n is out of range or if the n-th parameter is -** nameless, then NULL is returned. The returned string is +** ^If the value N is out of range or if the N-th parameter is +** nameless, then NULL is returned. ^The returned string is ** always in UTF-8 encoding even if the named parameter was ** originally specified as UTF-16 in [sqlite3_prepare16()] or ** [sqlite3_prepare16_v2()]. @@ -3093,125 +3174,108 @@ ** See also: [sqlite3_bind_blob|sqlite3_bind()], ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. -** -** Requirements: -** [H13621] */ SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); /* -** CAPI3REF: Index Of A Parameter With A Given Name {H13640} +** CAPI3REF: Index Of A Parameter With A Given Name ** -** Return the index of an SQL parameter given its name. The +** ^Return the index of an SQL parameter given its name. ^The ** index value returned is suitable for use as the second -** parameter to [sqlite3_bind_blob|sqlite3_bind()]. A zero -** is returned if no matching parameter is found. The parameter +** parameter to [sqlite3_bind_blob|sqlite3_bind()]. ^A zero +** is returned if no matching parameter is found. ^The parameter ** name must be given in UTF-8 even if the original statement ** was prepared from UTF-16 text using [sqlite3_prepare16_v2()]. ** ** See also: [sqlite3_bind_blob|sqlite3_bind()], ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. -** -** Requirements: -** [H13641] */ SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); /* -** CAPI3REF: Reset All Bindings On A Prepared Statement {H13660} +** CAPI3REF: Reset All Bindings On A Prepared Statement ** -** Contrary to the intuition of many, [sqlite3_reset()] does not reset +** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset ** the [sqlite3_bind_blob | bindings] on a [prepared statement]. -** Use this routine to reset all host parameters to NULL. -** -** Requirements: -** [H13661] +** ^Use this routine to reset all host parameters to NULL. */ SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); /* -** CAPI3REF: Number Of Columns In A Result Set {H13710} +** CAPI3REF: Number Of Columns In A Result Set ** -** Return the number of columns in the result set returned by the -** [prepared statement]. This routine returns 0 if pStmt is an SQL +** ^Return the number of columns in the result set returned by the +** [prepared statement]. ^This routine returns 0 if pStmt is an SQL ** statement that does not return data (for example an [UPDATE]). -** -** Requirements: -** [H13711] */ SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); /* -** CAPI3REF: Column Names In A Result Set {H13720} +** CAPI3REF: Column Names In A Result Set ** -** These routines return the name assigned to a particular column -** in the result set of a [SELECT] statement. The sqlite3_column_name() +** ^These routines return the name assigned to a particular column +** in the result set of a [SELECT] statement. ^The sqlite3_column_name() ** interface returns a pointer to a zero-terminated UTF-8 string ** and sqlite3_column_name16() returns a pointer to a zero-terminated -** UTF-16 string. The first parameter is the [prepared statement] -** that implements the [SELECT] statement. The second parameter is the -** column number. The leftmost column is number 0. +** UTF-16 string. ^The first parameter is the [prepared statement] +** that implements the [SELECT] statement. ^The second parameter is the +** column number. ^The leftmost column is number 0. ** -** The returned string pointer is valid until either the [prepared statement] +** ^The returned string pointer is valid until either the [prepared statement] ** is destroyed by [sqlite3_finalize()] or until the next call to ** sqlite3_column_name() or sqlite3_column_name16() on the same column. ** -** If sqlite3_malloc() fails during the processing of either routine +** ^If sqlite3_malloc() fails during the processing of either routine ** (for example during a conversion from UTF-8 to UTF-16) then a ** NULL pointer is returned. ** -** The name of a result column is the value of the "AS" clause for +** ^The name of a result column is the value of the "AS" clause for ** that column, if there is an AS clause. If there is no AS clause ** then the name of the column is unspecified and may change from ** one release of SQLite to the next. -** -** Requirements: -** [H13721] [H13723] [H13724] [H13725] [H13726] [H13727] */ SQLITE_API const char *sqlite3_column_name(sqlite3_stmt*, int N); SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); /* -** CAPI3REF: Source Of Data In A Query Result {H13740} +** CAPI3REF: Source Of Data In A Query Result ** -** These routines provide a means to determine what column of what -** table in which database a result of a [SELECT] statement comes from. -** The name of the database or table or column can be returned as -** either a UTF-8 or UTF-16 string. The _database_ routines return +** ^These routines provide a means to determine the database, table, and +** table column that is the origin of a particular result column in +** [SELECT] statement. +** ^The name of the database or table or column can be returned as +** either a UTF-8 or UTF-16 string. ^The _database_ routines return ** the database name, the _table_ routines return the table name, and ** the origin_ routines return the column name. -** The returned string is valid until the [prepared statement] is destroyed +** ^The returned string is valid until the [prepared statement] is destroyed ** using [sqlite3_finalize()] or until the same information is requested ** again in a different encoding. ** -** The names returned are the original un-aliased names of the +** ^The names returned are the original un-aliased names of the ** database, table, and column. ** -** The first argument to the following calls is a [prepared statement]. -** These functions return information about the Nth column returned by +** ^The first argument to these interfaces is a [prepared statement]. +** ^These functions return information about the Nth result column returned by ** the statement, where N is the second function argument. +** ^The left-most column is column 0 for these routines. ** -** If the Nth column returned by the statement is an expression or +** ^If the Nth column returned by the statement is an expression or ** subquery and is not a column value, then all of these functions return -** NULL. These routine might also return NULL if a memory allocation error -** occurs. Otherwise, they return the name of the attached database, table -** and column that query result column was extracted from. +** NULL. ^These routine might also return NULL if a memory allocation error +** occurs. ^Otherwise, they return the name of the attached database, table, +** or column that query result column was extracted from. ** -** As with all other SQLite APIs, those postfixed with "16" return -** UTF-16 encoded strings, the other functions return UTF-8. {END} +** ^As with all other SQLite APIs, those whose names end with "16" return +** UTF-16 encoded strings and the other functions return UTF-8. ** -** These APIs are only available if the library was compiled with the -** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined. +** ^These APIs are only available if the library was compiled with the +** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol. ** -** {A13751} ** If two or more threads call one or more of these routines against the same ** prepared statement and column at the same time then the results are ** undefined. ** -** Requirements: -** [H13741] [H13742] [H13743] [H13744] [H13745] [H13746] [H13748] -** ** If two or more threads call one or more ** [sqlite3_column_database_name | column metadata interfaces] ** for the same [prepared statement] and result column @@ -3225,17 +3289,17 @@ SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); /* -** CAPI3REF: Declared Datatype Of A Query Result {H13760} +** CAPI3REF: Declared Datatype Of A Query Result ** -** The first parameter is a [prepared statement]. +** ^(The first parameter is a [prepared statement]. ** If this statement is a [SELECT] statement and the Nth column of the ** returned result set of that [SELECT] is a table column (not an ** expression or subquery) then the declared type of the table -** column is returned. If the Nth column of the result set is an +** column is returned.)^ ^If the Nth column of the result set is an ** expression or subquery, then a NULL pointer is returned. -** The returned string is always UTF-8 encoded. {END} +** ^The returned string is always UTF-8 encoded. ** -** For example, given the database schema: +** ^(For example, given the database schema: ** ** CREATE TABLE t1(c1 VARIANT); ** @@ -3244,23 +3308,20 @@ ** SELECT c1 + 1, c1 FROM t1; ** ** this routine would return the string "VARIANT" for the second result -** column (i==1), and a NULL pointer for the first result column (i==0). +** column (i==1), and a NULL pointer for the first result column (i==0).)^ ** -** SQLite uses dynamic run-time typing. So just because a column +** ^SQLite uses dynamic run-time typing. ^So just because a column ** is declared to contain a particular type does not mean that the ** data stored in that column is of the declared type. SQLite is -** strongly typed, but the typing is dynamic not static. Type +** strongly typed, but the typing is dynamic not static. ^Type ** is associated with individual values, not with the containers ** used to hold those values. -** -** Requirements: -** [H13761] [H13762] [H13763] */ SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt*,int); SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); /* -** CAPI3REF: Evaluate An SQL Statement {H13200} +** CAPI3REF: Evaluate An SQL Statement ** ** After a [prepared statement] has been prepared using either ** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy @@ -3274,35 +3335,35 @@ ** new "v2" interface is recommended for new applications but the legacy ** interface will continue to be supported. ** -** In the legacy interface, the return value will be either [SQLITE_BUSY], +** ^In the legacy interface, the return value will be either [SQLITE_BUSY], ** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE]. -** With the "v2" interface, any of the other [result codes] or +** ^With the "v2" interface, any of the other [result codes] or ** [extended result codes] might be returned as well. ** -** [SQLITE_BUSY] means that the database engine was unable to acquire the -** database locks it needs to do its job. If the statement is a [COMMIT] +** ^[SQLITE_BUSY] means that the database engine was unable to acquire the +** database locks it needs to do its job. ^If the statement is a [COMMIT] ** or occurs outside of an explicit transaction, then you can retry the ** statement. If the statement is not a [COMMIT] and occurs within a ** explicit transaction then you should rollback the transaction before ** continuing. ** -** [SQLITE_DONE] means that the statement has finished executing +** ^[SQLITE_DONE] means that the statement has finished executing ** successfully. sqlite3_step() should not be called again on this virtual ** machine without first calling [sqlite3_reset()] to reset the virtual ** machine back to its initial state. ** -** If the SQL statement being executed returns any data, then [SQLITE_ROW] +** ^If the SQL statement being executed returns any data, then [SQLITE_ROW] ** is returned each time a new row of data is ready for processing by the ** caller. The values may be accessed using the [column access functions]. ** sqlite3_step() is called again to retrieve the next row of data. ** -** [SQLITE_ERROR] means that a run-time error (such as a constraint +** ^[SQLITE_ERROR] means that a run-time error (such as a constraint ** violation) has occurred. sqlite3_step() should not be called again on ** the VM. More information may be found by calling [sqlite3_errmsg()]. -** With the legacy interface, a more specific error code (for example, +** ^With the legacy interface, a more specific error code (for example, ** [SQLITE_INTERRUPT], [SQLITE_SCHEMA], [SQLITE_CORRUPT], and so forth) ** can be obtained by calling [sqlite3_reset()] on the -** [prepared statement]. In the "v2" interface, +** [prepared statement]. ^In the "v2" interface, ** the more specific error code is returned directly by sqlite3_step(). ** ** [SQLITE_MISUSE] means that the this routine was called inappropriately. @@ -3323,27 +3384,22 @@ ** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces, ** then the more specific [error codes] are returned directly ** by sqlite3_step(). The use of the "v2" interface is recommended. -** -** Requirements: -** [H13202] [H15304] [H15306] [H15308] [H15310] */ SQLITE_API int sqlite3_step(sqlite3_stmt*); /* -** CAPI3REF: Number of columns in a result set {H13770} +** CAPI3REF: Number of columns in a result set ** -** Returns the number of values in the current row of the result set. -** -** Requirements: -** [H13771] [H13772] +** ^The sqlite3_data_count(P) the number of columns in the +** of the result set of [prepared statement] P. */ SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); /* -** CAPI3REF: Fundamental Datatypes {H10265} +** CAPI3REF: Fundamental Datatypes ** KEYWORDS: SQLITE_TEXT ** -** {H10266} Every value in SQLite has one of five fundamental datatypes: +** ^(Every value in SQLite has one of five fundamental datatypes: ** **
      **
    • 64-bit signed integer @@ -3351,7 +3407,7 @@ **
    • string **
    • BLOB **
    • NULL -**
    {END} +** )^ ** ** These constants are codes for each of those types. ** @@ -3372,17 +3428,19 @@ #define SQLITE3_TEXT 3 /* -** CAPI3REF: Result Values From A Query {H13800} +** CAPI3REF: Result Values From A Query ** KEYWORDS: {column access functions} ** -** These routines form the "result set query" interface. +** These routines form the "result set" interface. ** -** These routines return information about a single column of the current -** result row of a query. In every case the first argument is a pointer +** ^These routines return information about a single column of the current +** result row of a query. ^In every case the first argument is a pointer ** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*] ** that was returned from [sqlite3_prepare_v2()] or one of its variants) ** and the second argument is the index of the column for which information -** should be returned. The leftmost column of the result set has the index 0. +** should be returned. ^The leftmost column of the result set has the index 0. +** ^The number of columns in the result can be determined using +** [sqlite3_column_count()]. ** ** If the SQL statement does not currently point to a valid row, or if the ** column index is out of range, the result is undefined. @@ -3396,9 +3454,9 @@ ** are called from a different thread while any of these routines ** are pending, then the results are undefined. ** -** The sqlite3_column_type() routine returns the +** ^The sqlite3_column_type() routine returns the ** [SQLITE_INTEGER | datatype code] for the initial data type -** of the result column. The returned value is one of [SQLITE_INTEGER], +** of the result column. ^The returned value is one of [SQLITE_INTEGER], ** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL]. The value ** returned by sqlite3_column_type() is only meaningful if no type ** conversions have occurred as described below. After a type conversion, @@ -3406,27 +3464,27 @@ ** versions of SQLite may change the behavior of sqlite3_column_type() ** following a type conversion. ** -** If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() +** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() ** routine returns the number of bytes in that BLOB or string. -** If the result is a UTF-16 string, then sqlite3_column_bytes() converts +** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts ** the string to UTF-8 and then returns the number of bytes. -** If the result is a numeric value then sqlite3_column_bytes() uses +** ^If the result is a numeric value then sqlite3_column_bytes() uses ** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns ** the number of bytes in that string. -** The value returned does not include the zero terminator at the end -** of the string. For clarity: the value returned is the number of +** ^The value returned does not include the zero terminator at the end +** of the string. ^For clarity: the value returned is the number of ** bytes in the string, not the number of characters. ** -** Strings returned by sqlite3_column_text() and sqlite3_column_text16(), -** even empty strings, are always zero terminated. The return +** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(), +** even empty strings, are always zero terminated. ^The return ** value from sqlite3_column_blob() for a zero-length BLOB is an arbitrary ** pointer, possibly even a NULL pointer. ** -** The sqlite3_column_bytes16() routine is similar to sqlite3_column_bytes() +** ^The sqlite3_column_bytes16() routine is similar to sqlite3_column_bytes() ** but leaves the result in UTF-16 in native byte order instead of UTF-8. -** The zero terminator is not included in this count. +** ^The zero terminator is not included in this count. ** -** The object returned by [sqlite3_column_value()] is an +** ^The object returned by [sqlite3_column_value()] is an ** [unprotected sqlite3_value] object. An unprotected sqlite3_value object ** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()]. ** If the [unprotected sqlite3_value] object returned by @@ -3434,10 +3492,10 @@ ** to routines like [sqlite3_value_int()], [sqlite3_value_text()], ** or [sqlite3_value_bytes()], then the behavior is undefined. ** -** These routines attempt to convert the value where appropriate. For +** These routines attempt to convert the value where appropriate. ^For ** example, if the internal representation is FLOAT and a text result ** is requested, [sqlite3_snprintf()] is used internally to perform the -** conversion automatically. The following table details the conversions +** conversion automatically. ^(The following table details the conversions ** that are applied: ** **
    @@ -3461,7 +3519,7 @@ ** BLOB FLOAT Convert to TEXT then use atof() ** BLOB TEXT Add a zero terminator if needed ** -**
    +**
    )^ ** ** The table above makes reference to standard C library functions atoi() ** and atof(). SQLite does not really use these functions. It has its @@ -3469,10 +3527,10 @@ ** used in the table for brevity and because they are familiar to most ** C programmers. ** -** Note that when type conversions occur, pointers returned by prior +** ^Note that when type conversions occur, pointers returned by prior ** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or ** sqlite3_column_text16() may be invalidated. -** Type conversions and pointer invalidations might occur +** ^(Type conversions and pointer invalidations might occur ** in the following cases: ** **
      @@ -3485,22 +3543,22 @@ **
    • The initial content is UTF-16 text and sqlite3_column_bytes() or ** sqlite3_column_text() is called. The content must be converted ** to UTF-8.
    • -**
    +** )^ ** -** Conversions between UTF-16be and UTF-16le are always done in place and do +** ^Conversions between UTF-16be and UTF-16le are always done in place and do ** not invalidate a prior pointer, though of course the content of the buffer ** that the prior pointer points to will have been modified. Other kinds ** of conversion are done in place when it is possible, but sometimes they ** are not possible and in those cases prior pointers are invalidated. ** -** The safest and easiest to remember policy is to invoke these routines +** ^(The safest and easiest to remember policy is to invoke these routines ** in one of the following ways: ** **
      **
    • sqlite3_column_text() followed by sqlite3_column_bytes()
    • **
    • sqlite3_column_blob() followed by sqlite3_column_bytes()
    • **
    • sqlite3_column_text16() followed by sqlite3_column_bytes16()
    • -**
    +** )^ ** ** In other words, you should call sqlite3_column_text(), ** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result @@ -3510,22 +3568,18 @@ ** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() ** with calls to sqlite3_column_bytes(). ** -** The pointers returned are valid until a type conversion occurs as +** ^The pointers returned are valid until a type conversion occurs as ** described above, or until [sqlite3_step()] or [sqlite3_reset()] or -** [sqlite3_finalize()] is called. The memory space used to hold strings +** [sqlite3_finalize()] is called. ^The memory space used to hold strings ** and BLOBs is freed automatically. Do not pass the pointers returned ** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into ** [sqlite3_free()]. ** -** If a memory allocation error occurs during the evaluation of any +** ^(If a memory allocation error occurs during the evaluation of any ** of these routines, a default value is returned. The default value ** is either the integer 0, the floating point number 0.0, or a NULL ** pointer. Subsequent calls to [sqlite3_errcode()] will return -** [SQLITE_NOMEM]. -** -** Requirements: -** [H13803] [H13806] [H13809] [H13812] [H13815] [H13818] [H13821] [H13824] -** [H13827] [H13830] +** [SQLITE_NOMEM].)^ */ SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol); @@ -3539,79 +3593,76 @@ SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); /* -** CAPI3REF: Destroy A Prepared Statement Object {H13300} +** CAPI3REF: Destroy A Prepared Statement Object ** -** The sqlite3_finalize() function is called to delete a [prepared statement]. -** If the statement was executed successfully or not executed at all, then -** SQLITE_OK is returned. If execution of the statement failed then an +** ^The sqlite3_finalize() function is called to delete a [prepared statement]. +** ^If the statement was executed successfully or not executed at all, then +** SQLITE_OK is returned. ^If execution of the statement failed then an ** [error code] or [extended error code] is returned. ** -** This routine can be called at any point during the execution of the -** [prepared statement]. If the virtual machine has not +** ^This routine can be called at any point during the execution of the +** [prepared statement]. ^If the virtual machine has not ** completed execution when this routine is called, that is like ** encountering an error or an [sqlite3_interrupt | interrupt]. -** Incomplete updates may be rolled back and transactions canceled, +** ^Incomplete updates may be rolled back and transactions canceled, ** depending on the circumstances, and the ** [error code] returned will be [SQLITE_ABORT]. -** -** Requirements: -** [H11302] [H11304] */ SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); /* -** CAPI3REF: Reset A Prepared Statement Object {H13330} +** CAPI3REF: Reset A Prepared Statement Object ** ** The sqlite3_reset() function is called to reset a [prepared statement] ** object back to its initial state, ready to be re-executed. -** Any SQL statement variables that had values bound to them using +** ^Any SQL statement variables that had values bound to them using ** the [sqlite3_bind_blob | sqlite3_bind_*() API] retain their values. ** Use [sqlite3_clear_bindings()] to reset the bindings. ** -** {H11332} The [sqlite3_reset(S)] interface resets the [prepared statement] S -** back to the beginning of its program. +** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S +** back to the beginning of its program. ** -** {H11334} If the most recent call to [sqlite3_step(S)] for the -** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], -** or if [sqlite3_step(S)] has never before been called on S, -** then [sqlite3_reset(S)] returns [SQLITE_OK]. -** -** {H11336} If the most recent call to [sqlite3_step(S)] for the -** [prepared statement] S indicated an error, then -** [sqlite3_reset(S)] returns an appropriate [error code]. +** ^If the most recent call to [sqlite3_step(S)] for the +** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], +** or if [sqlite3_step(S)] has never before been called on S, +** then [sqlite3_reset(S)] returns [SQLITE_OK]. +** +** ^If the most recent call to [sqlite3_step(S)] for the +** [prepared statement] S indicated an error, then +** [sqlite3_reset(S)] returns an appropriate [error code]. ** -** {H11338} The [sqlite3_reset(S)] interface does not change the values -** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. +** ^The [sqlite3_reset(S)] interface does not change the values +** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. */ SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); /* -** CAPI3REF: Create Or Redefine SQL Functions {H16100} +** CAPI3REF: Create Or Redefine SQL Functions ** KEYWORDS: {function creation routines} ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} ** -** These two functions (collectively known as "function creation routines") +** ^These two functions (collectively known as "function creation routines") ** are used to add SQL functions or aggregates or to redefine the behavior ** of existing SQL functions or aggregates. The only difference between the ** two is that the second parameter, the name of the (scalar) function or ** aggregate, is encoded in UTF-8 for sqlite3_create_function() and UTF-16 ** for sqlite3_create_function16(). ** -** The first parameter is the [database connection] to which the SQL -** function is to be added. If a single program uses more than one database -** connection internally, then SQL functions must be added individually to -** each database connection. +** ^The first parameter is the [database connection] to which the SQL +** function is to be added. ^If an application uses more than one database +** connection then application-defined SQL functions must be added +** to each database connection separately. ** ** The second parameter is the name of the SQL function to be created or -** redefined. The length of the name is limited to 255 bytes, exclusive of +** redefined. ^The length of the name is limited to 255 bytes, exclusive of ** the zero-terminator. Note that the name length limit is in bytes, not -** characters. Any attempt to create a function with a longer name +** characters. ^Any attempt to create a function with a longer name ** will result in [SQLITE_ERROR] being returned. ** -** The third parameter (nArg) +** ^The third parameter (nArg) ** is the number of arguments that the SQL function or -** aggregate takes. If this parameter is -1, then the SQL function or +** aggregate takes. ^If this parameter is -1, then the SQL function or ** aggregate may take any number of arguments between 0 and the limit ** set by [sqlite3_limit]([SQLITE_LIMIT_FUNCTION_ARG]). If the third ** parameter is less than -1 or greater than 127 then the behavior is @@ -3621,53 +3672,49 @@ ** [SQLITE_UTF8 | text encoding] this SQL function prefers for ** its parameters. Any SQL function implementation should be able to work ** work with UTF-8, UTF-16le, or UTF-16be. But some implementations may be -** more efficient with one encoding than another. It is allowed to +** more efficient with one encoding than another. ^An application may ** invoke sqlite3_create_function() or sqlite3_create_function16() multiple ** times with the same function but with different values of eTextRep. -** When multiple implementations of the same function are available, SQLite +** ^When multiple implementations of the same function are available, SQLite ** will pick the one that involves the least amount of data conversion. ** If there is only a single implementation which does not care what text ** encoding is used, then the fourth argument should be [SQLITE_ANY]. ** -** The fifth parameter is an arbitrary pointer. The implementation of the -** function can gain access to this pointer using [sqlite3_user_data()]. +** ^(The fifth parameter is an arbitrary pointer. The implementation of the +** function can gain access to this pointer using [sqlite3_user_data()].)^ ** ** The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are ** pointers to C-language functions that implement the SQL function or -** aggregate. A scalar SQL function requires an implementation of the xFunc -** callback only, NULL pointers should be passed as the xStep and xFinal -** parameters. An aggregate SQL function requires an implementation of xStep -** and xFinal and NULL should be passed for xFunc. To delete an existing +** aggregate. ^A scalar SQL function requires an implementation of the xFunc +** callback only; NULL pointers should be passed as the xStep and xFinal +** parameters. ^An aggregate SQL function requires an implementation of xStep +** and xFinal and NULL should be passed for xFunc. ^To delete an existing ** SQL function or aggregate, pass NULL for all three function callbacks. ** -** It is permitted to register multiple implementations of the same +** ^It is permitted to register multiple implementations of the same ** functions with the same name but with either differing numbers of -** arguments or differing preferred text encodings. SQLite will use -** the implementation most closely matches the way in which the -** SQL function is used. A function implementation with a non-negative +** arguments or differing preferred text encodings. ^SQLite will use +** the implementation that most closely matches the way in which the +** SQL function is used. ^A function implementation with a non-negative ** nArg parameter is a better match than a function implementation with -** a negative nArg. A function where the preferred text encoding +** a negative nArg. ^A function where the preferred text encoding ** matches the database encoding is a better ** match than a function where the encoding is different. -** A function where the encoding difference is between UTF16le and UTF16be +** ^A function where the encoding difference is between UTF16le and UTF16be ** is a closer match than a function where the encoding difference is ** between UTF8 and UTF16. ** -** Built-in functions may be overloaded by new application-defined functions. -** The first application-defined function with a given name overrides all +** ^Built-in functions may be overloaded by new application-defined functions. +** ^The first application-defined function with a given name overrides all ** built-in functions in the same [database connection] with the same name. -** Subsequent application-defined functions of the same name only override +** ^Subsequent application-defined functions of the same name only override ** prior application-defined functions that are an exact match for the ** number of parameters and preferred encoding. ** -** An application-defined function is permitted to call other +** ^An application-defined function is permitted to call other ** SQLite interfaces. However, such calls must not ** close the database connection nor finalize or reset the prepared ** statement in which the function is running. -** -** Requirements: -** [H16103] [H16106] [H16109] [H16112] [H16118] [H16121] [H16127] -** [H16130] [H16133] [H16136] [H16139] [H16142] */ SQLITE_API int sqlite3_create_function( sqlite3 *db, @@ -3691,7 +3738,7 @@ ); /* -** CAPI3REF: Text Encodings {H10267} +** CAPI3REF: Text Encodings ** ** These constant define integer codes that represent the various ** text encodings supported by SQLite. @@ -3723,7 +3770,7 @@ #endif /* -** CAPI3REF: Obtaining SQL Function Parameter Values {H15100} +** CAPI3REF: Obtaining SQL Function Parameter Values ** ** The C-language implementation of SQL functions and aggregates uses ** this set of interface routines to access the parameter values on @@ -3741,22 +3788,22 @@ ** Any attempt to use these routines on an [unprotected sqlite3_value] ** object results in undefined behavior. ** -** These routines work just like the corresponding [column access functions] +** ^These routines work just like the corresponding [column access functions] ** except that these routines take a single [protected sqlite3_value] object ** pointer instead of a [sqlite3_stmt*] pointer and an integer column number. ** -** The sqlite3_value_text16() interface extracts a UTF-16 string -** in the native byte-order of the host machine. The +** ^The sqlite3_value_text16() interface extracts a UTF-16 string +** in the native byte-order of the host machine. ^The ** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces ** extract UTF-16 strings as big-endian and little-endian respectively. ** -** The sqlite3_value_numeric_type() interface attempts to apply +** ^(The sqlite3_value_numeric_type() interface attempts to apply ** numeric affinity to the value. This means that an attempt is ** made to convert the value to an integer or floating point. If ** such a conversion is possible without loss of information (in other ** words, if the value is a string that looks like a number) ** then the conversion is performed. Otherwise no conversion occurs. -** The [SQLITE_INTEGER | datatype] after conversion is returned. +** The [SQLITE_INTEGER | datatype] after conversion is returned.)^ ** ** Please pay particular attention to the fact that the pointer returned ** from [sqlite3_value_blob()], [sqlite3_value_text()], or @@ -3766,10 +3813,6 @@ ** ** These routines must be called from the same thread as ** the SQL function that supplied the [sqlite3_value*] parameters. -** -** Requirements: -** [H15103] [H15106] [H15109] [H15112] [H15115] [H15118] [H15121] [H15124] -** [H15127] [H15130] [H15133] [H15136] */ SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); SQLITE_API int sqlite3_value_bytes(sqlite3_value*); @@ -3785,66 +3828,73 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); /* -** CAPI3REF: Obtain Aggregate Function Context {H16210} +** CAPI3REF: Obtain Aggregate Function Context ** -** The implementation of aggregate SQL functions use this routine to allocate -** a structure for storing their state. +** Implementions of aggregate SQL functions use this +** routine to allocate memory for storing their state. ** -** The first time the sqlite3_aggregate_context() routine is called for a -** particular aggregate, SQLite allocates nBytes of memory, zeroes out that -** memory, and returns a pointer to it. On second and subsequent calls to -** sqlite3_aggregate_context() for the same aggregate function index, -** the same buffer is returned. The implementation of the aggregate can use -** the returned buffer to accumulate data. +** ^The first time the sqlite3_aggregate_context(C,N) routine is called +** for a particular aggregate function, SQLite +** allocates N of memory, zeroes out that memory, and returns a pointer +** to the new memory. ^On second and subsequent calls to +** sqlite3_aggregate_context() for the same aggregate function instance, +** the same buffer is returned. Sqlite3_aggregate_context() is normally +** called once for each invocation of the xStep callback and then one +** last time when the xFinal callback is invoked. ^(When no rows match +** an aggregate query, the xStep() callback of the aggregate function +** implementation is never called and xFinal() is called exactly once. +** In those cases, sqlite3_aggregate_context() might be called for the +** first time from within xFinal().)^ +** +** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer if N is +** less than or equal to zero or if a memory allocate error occurs. +** +** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is +** determined by the N parameter on first successful call. Changing the +** value of N in subsequent call to sqlite3_aggregate_context() within +** the same aggregate function instance will not resize the memory +** allocation.)^ ** -** SQLite automatically frees the allocated buffer when the aggregate -** query concludes. +** ^SQLite automatically frees the memory allocated by +** sqlite3_aggregate_context() when the aggregate query concludes. ** -** The first parameter should be a copy of the +** The first parameter must be a copy of the ** [sqlite3_context | SQL function context] that is the first parameter -** to the callback routine that implements the aggregate function. +** to the xStep or xFinal callback routine that implements the aggregate +** function. ** ** This routine must be called from the same thread in which ** the aggregate SQL function is running. -** -** Requirements: -** [H16211] [H16213] [H16215] [H16217] */ SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); /* -** CAPI3REF: User Data For Functions {H16240} +** CAPI3REF: User Data For Functions ** -** The sqlite3_user_data() interface returns a copy of +** ^The sqlite3_user_data() interface returns a copy of ** the pointer that was the pUserData parameter (the 5th parameter) ** of the [sqlite3_create_function()] ** and [sqlite3_create_function16()] routines that originally -** registered the application defined function. {END} +** registered the application defined function. ** ** This routine must be called from the same thread in which ** the application-defined function is running. -** -** Requirements: -** [H16243] */ SQLITE_API void *sqlite3_user_data(sqlite3_context*); /* -** CAPI3REF: Database Connection For Functions {H16250} +** CAPI3REF: Database Connection For Functions ** -** The sqlite3_context_db_handle() interface returns a copy of +** ^The sqlite3_context_db_handle() interface returns a copy of ** the pointer to the [database connection] (the 1st parameter) ** of the [sqlite3_create_function()] ** and [sqlite3_create_function16()] routines that originally ** registered the application defined function. -** -** Requirements: -** [H16253] */ SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); /* -** CAPI3REF: Function Auxiliary Data {H16270} +** CAPI3REF: Function Auxiliary Data ** ** The following two functions may be used by scalar SQL functions to ** associate metadata with argument values. If the same value is passed to @@ -3857,48 +3907,45 @@ ** invocations of the same function so that the original pattern string ** does not need to be recompiled on each invocation. ** -** The sqlite3_get_auxdata() interface returns a pointer to the metadata +** ^The sqlite3_get_auxdata() interface returns a pointer to the metadata ** associated by the sqlite3_set_auxdata() function with the Nth argument -** value to the application-defined function. If no metadata has been ever +** value to the application-defined function. ^If no metadata has been ever ** been set for the Nth argument of the function, or if the corresponding ** function parameter has changed since the meta-data was set, ** then sqlite3_get_auxdata() returns a NULL pointer. ** -** The sqlite3_set_auxdata() interface saves the metadata +** ^The sqlite3_set_auxdata() interface saves the metadata ** pointed to by its 3rd parameter as the metadata for the N-th ** argument of the application-defined function. Subsequent ** calls to sqlite3_get_auxdata() might return this data, if it has ** not been destroyed. -** If it is not NULL, SQLite will invoke the destructor +** ^If it is not NULL, SQLite will invoke the destructor ** function given by the 4th parameter to sqlite3_set_auxdata() on ** the metadata when the corresponding function parameter changes ** or when the SQL statement completes, whichever comes first. ** ** SQLite is free to call the destructor and drop metadata on any -** parameter of any function at any time. The only guarantee is that +** parameter of any function at any time. ^The only guarantee is that ** the destructor will be called before the metadata is dropped. ** -** In practice, metadata is preserved between function calls for +** ^(In practice, metadata is preserved between function calls for ** expressions that are constant at compile time. This includes literal -** values and SQL variables. +** values and [parameters].)^ ** ** These routines must be called from the same thread in which ** the SQL function is running. -** -** Requirements: -** [H16272] [H16274] [H16276] [H16277] [H16278] [H16279] */ SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); /* -** CAPI3REF: Constants Defining Special Destructor Behavior {H10280} +** CAPI3REF: Constants Defining Special Destructor Behavior ** ** These are special values for the destructor that is passed in as the -** final argument to routines like [sqlite3_result_blob()]. If the destructor +** final argument to routines like [sqlite3_result_blob()]. ^If the destructor ** argument is SQLITE_STATIC, it means that the content pointer is constant -** and will never change. It does not need to be destroyed. The +** and will never change. It does not need to be destroyed. ^The ** SQLITE_TRANSIENT value means that the content will likely change in ** the near future and that SQLite should make its own private copy of ** the content before returning. @@ -3911,7 +3958,7 @@ #define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1) /* -** CAPI3REF: Setting The Result Of An SQL Function {H16400} +** CAPI3REF: Setting The Result Of An SQL Function ** ** These routines are used by the xFunc or xFinal callbacks that ** implement SQL functions and aggregates. See @@ -3922,102 +3969,98 @@ ** functions used to bind values to host parameters in prepared statements. ** Refer to the [SQL parameter] documentation for additional information. ** -** The sqlite3_result_blob() interface sets the result from +** ^The sqlite3_result_blob() interface sets the result from ** an application-defined function to be the BLOB whose content is pointed ** to by the second parameter and which is N bytes long where N is the ** third parameter. ** -** The sqlite3_result_zeroblob() interfaces set the result of +** ^The sqlite3_result_zeroblob() interfaces set the result of ** the application-defined function to be a BLOB containing all zero ** bytes and N bytes in size, where N is the value of the 2nd parameter. ** -** The sqlite3_result_double() interface sets the result from +** ^The sqlite3_result_double() interface sets the result from ** an application-defined function to be a floating point value specified ** by its 2nd argument. ** -** The sqlite3_result_error() and sqlite3_result_error16() functions +** ^The sqlite3_result_error() and sqlite3_result_error16() functions ** cause the implemented SQL function to throw an exception. -** SQLite uses the string pointed to by the +** ^SQLite uses the string pointed to by the ** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() -** as the text of an error message. SQLite interprets the error -** message string from sqlite3_result_error() as UTF-8. SQLite +** as the text of an error message. ^SQLite interprets the error +** message string from sqlite3_result_error() as UTF-8. ^SQLite ** interprets the string from sqlite3_result_error16() as UTF-16 in native -** byte order. If the third parameter to sqlite3_result_error() +** byte order. ^If the third parameter to sqlite3_result_error() ** or sqlite3_result_error16() is negative then SQLite takes as the error ** message all text up through the first zero character. -** If the third parameter to sqlite3_result_error() or +** ^If the third parameter to sqlite3_result_error() or ** sqlite3_result_error16() is non-negative then SQLite takes that many ** bytes (not characters) from the 2nd parameter as the error message. -** The sqlite3_result_error() and sqlite3_result_error16() +** ^The sqlite3_result_error() and sqlite3_result_error16() ** routines make a private copy of the error message text before ** they return. Hence, the calling function can deallocate or ** modify the text after they return without harm. -** The sqlite3_result_error_code() function changes the error code -** returned by SQLite as a result of an error in a function. By default, -** the error code is SQLITE_ERROR. A subsequent call to sqlite3_result_error() +** ^The sqlite3_result_error_code() function changes the error code +** returned by SQLite as a result of an error in a function. ^By default, +** the error code is SQLITE_ERROR. ^A subsequent call to sqlite3_result_error() ** or sqlite3_result_error16() resets the error code to SQLITE_ERROR. ** -** The sqlite3_result_toobig() interface causes SQLite to throw an error -** indicating that a string or BLOB is to long to represent. +** ^The sqlite3_result_toobig() interface causes SQLite to throw an error +** indicating that a string or BLOB is too long to represent. ** -** The sqlite3_result_nomem() interface causes SQLite to throw an error +** ^The sqlite3_result_nomem() interface causes SQLite to throw an error ** indicating that a memory allocation failed. ** -** The sqlite3_result_int() interface sets the return value +** ^The sqlite3_result_int() interface sets the return value ** of the application-defined function to be the 32-bit signed integer ** value given in the 2nd argument. -** The sqlite3_result_int64() interface sets the return value +** ^The sqlite3_result_int64() interface sets the return value ** of the application-defined function to be the 64-bit signed integer ** value given in the 2nd argument. ** -** The sqlite3_result_null() interface sets the return value +** ^The sqlite3_result_null() interface sets the return value ** of the application-defined function to be NULL. ** -** The sqlite3_result_text(), sqlite3_result_text16(), +** ^The sqlite3_result_text(), sqlite3_result_text16(), ** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces ** set the return value of the application-defined function to be ** a text string which is represented as UTF-8, UTF-16 native byte order, ** UTF-16 little endian, or UTF-16 big endian, respectively. -** SQLite takes the text result from the application from +** ^SQLite takes the text result from the application from ** the 2nd parameter of the sqlite3_result_text* interfaces. -** If the 3rd parameter to the sqlite3_result_text* interfaces +** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** is negative, then SQLite takes result text from the 2nd parameter ** through the first zero character. -** If the 3rd parameter to the sqlite3_result_text* interfaces +** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** is non-negative, then as many bytes (not characters) of the text ** pointed to by the 2nd parameter are taken as the application-defined ** function result. -** If the 4th parameter to the sqlite3_result_text* interfaces +** ^If the 4th parameter to the sqlite3_result_text* interfaces ** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that ** function as the destructor on the text or BLOB result when it has ** finished using that result. -** If the 4th parameter to the sqlite3_result_text* interfaces or +** ^If the 4th parameter to the sqlite3_result_text* interfaces or to ** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite ** assumes that the text or BLOB result is in constant space and does not -** copy the it or call a destructor when it has finished using that result. -** If the 4th parameter to the sqlite3_result_text* interfaces +** copy the content of the parameter nor call a destructor on the content +** when it has finished using that result. +** ^If the 4th parameter to the sqlite3_result_text* interfaces ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT ** then SQLite makes a copy of the result into space obtained from ** from [sqlite3_malloc()] before it returns. ** -** The sqlite3_result_value() interface sets the result of +** ^The sqlite3_result_value() interface sets the result of ** the application-defined function to be a copy the -** [unprotected sqlite3_value] object specified by the 2nd parameter. The +** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The ** sqlite3_result_value() interface makes a copy of the [sqlite3_value] ** so that the [sqlite3_value] specified in the parameter may change or ** be deallocated after sqlite3_result_value() returns without harm. -** A [protected sqlite3_value] object may always be used where an +** ^A [protected sqlite3_value] object may always be used where an ** [unprotected sqlite3_value] object is required, so either ** kind of [sqlite3_value] object can be used with this interface. ** ** If these routines are called from within the different thread ** than the one containing the application-defined function that received ** the [sqlite3_context] pointer, the results are undefined. -** -** Requirements: -** [H16403] [H16406] [H16409] [H16412] [H16415] [H16418] [H16421] [H16424] -** [H16427] [H16430] [H16433] [H16436] [H16439] [H16442] [H16445] [H16448] -** [H16451] [H16454] [H16457] [H16460] [H16463] */ SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); SQLITE_API void sqlite3_result_double(sqlite3_context*, double); @@ -4037,20 +4080,20 @@ SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); /* -** CAPI3REF: Define New Collating Sequences {H16600} +** CAPI3REF: Define New Collating Sequences ** ** These functions are used to add new collation sequences to the ** [database connection] specified as the first argument. ** -** The name of the new collation sequence is specified as a UTF-8 string +** ^The name of the new collation sequence is specified as a UTF-8 string ** for sqlite3_create_collation() and sqlite3_create_collation_v2() -** and a UTF-16 string for sqlite3_create_collation16(). In all cases +** and a UTF-16 string for sqlite3_create_collation16(). ^In all cases ** the name is passed as the second function argument. ** -** The third argument may be one of the constants [SQLITE_UTF8], +** ^The third argument may be one of the constants [SQLITE_UTF8], ** [SQLITE_UTF16LE], or [SQLITE_UTF16BE], indicating that the user-supplied ** routine expects to be passed pointers to strings encoded using UTF-8, -** UTF-16 little-endian, or UTF-16 big-endian, respectively. The +** UTF-16 little-endian, or UTF-16 big-endian, respectively. ^The ** third argument might also be [SQLITE_UTF16] to indicate that the routine ** expects pointers to be UTF-16 strings in the native byte order, or the ** argument can be [SQLITE_UTF16_ALIGNED] if the @@ -4058,33 +4101,29 @@ ** of UTF-16 in the native byte order. ** ** A pointer to the user supplied routine must be passed as the fifth -** argument. If it is NULL, this is the same as deleting the collation +** argument. ^If it is NULL, this is the same as deleting the collation ** sequence (so that SQLite cannot call it anymore). -** Each time the application supplied function is invoked, it is passed +** ^Each time the application supplied function is invoked, it is passed ** as its first parameter a copy of the void* passed as the fourth argument ** to sqlite3_create_collation() or sqlite3_create_collation16(). ** -** The remaining arguments to the application-supplied routine are two strings, +** ^The remaining arguments to the application-supplied routine are two strings, ** each represented by a (length, data) pair and encoded in the encoding ** that was passed as the third argument when the collation sequence was -** registered. {END} The application defined collation routine should +** registered. The application defined collation routine should ** return negative, zero or positive if the first string is less than, ** equal to, or greater than the second string. i.e. (STRING1 - STRING2). ** -** The sqlite3_create_collation_v2() works like sqlite3_create_collation() +** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() ** except that it takes an extra argument which is a destructor for -** the collation. The destructor is called when the collation is +** the collation. ^The destructor is called when the collation is ** destroyed and is passed a copy of the fourth parameter void* pointer ** of the sqlite3_create_collation_v2(). -** Collations are destroyed when they are overridden by later calls to the +** ^Collations are destroyed when they are overridden by later calls to the ** collation creation functions or when the [database connection] is closed ** using [sqlite3_close()]. ** ** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. -** -** Requirements: -** [H16603] [H16604] [H16606] [H16609] [H16612] [H16615] [H16618] [H16621] -** [H16624] [H16627] [H16630] */ SQLITE_API int sqlite3_create_collation( sqlite3*, @@ -4110,33 +4149,30 @@ ); /* -** CAPI3REF: Collation Needed Callbacks {H16700} +** CAPI3REF: Collation Needed Callbacks ** -** To avoid having to register all collation sequences before a database +** ^To avoid having to register all collation sequences before a database ** can be used, a single callback function may be registered with the -** [database connection] to be called whenever an undefined collation +** [database connection] to be invoked whenever an undefined collation ** sequence is required. ** -** If the function is registered using the sqlite3_collation_needed() API, +** ^If the function is registered using the sqlite3_collation_needed() API, ** then it is passed the names of undefined collation sequences as strings -** encoded in UTF-8. {H16703} If sqlite3_collation_needed16() is used, +** encoded in UTF-8. ^If sqlite3_collation_needed16() is used, ** the names are passed as UTF-16 in machine native byte order. -** A call to either function replaces any existing callback. +** ^A call to either function replaces the existing collation-needed callback. ** -** When the callback is invoked, the first argument passed is a copy +** ^(When the callback is invoked, the first argument passed is a copy ** of the second argument to sqlite3_collation_needed() or ** sqlite3_collation_needed16(). The second argument is the database ** connection. The third argument is one of [SQLITE_UTF8], [SQLITE_UTF16BE], ** or [SQLITE_UTF16LE], indicating the most desirable form of the collation ** sequence function required. The fourth parameter is the name of the -** required collation sequence. +** required collation sequence.)^ ** ** The callback function should register the desired collation using ** [sqlite3_create_collation()], [sqlite3_create_collation16()], or ** [sqlite3_create_collation_v2()]. -** -** Requirements: -** [H16702] [H16704] [H16706] */ SQLITE_API int sqlite3_collation_needed( sqlite3*, @@ -4175,29 +4211,28 @@ ); /* -** CAPI3REF: Suspend Execution For A Short Time {H10530} +** CAPI3REF: Suspend Execution For A Short Time ** -** The sqlite3_sleep() function causes the current thread to suspend execution +** ^The sqlite3_sleep() function causes the current thread to suspend execution ** for at least a number of milliseconds specified in its parameter. ** -** If the operating system does not support sleep requests with +** ^If the operating system does not support sleep requests with ** millisecond time resolution, then the time will be rounded up to -** the nearest second. The number of milliseconds of sleep actually +** the nearest second. ^The number of milliseconds of sleep actually ** requested from the operating system is returned. ** -** SQLite implements this interface by calling the xSleep() +** ^SQLite implements this interface by calling the xSleep() ** method of the default [sqlite3_vfs] object. -** -** Requirements: [H10533] [H10536] */ SQLITE_API int sqlite3_sleep(int); /* -** CAPI3REF: Name Of The Folder Holding Temporary Files {H10310} +** CAPI3REF: Name Of The Folder Holding Temporary Files ** -** If this global variable is made to point to a string which is +** ^(If this global variable is made to point to a string which is ** the name of a folder (a.k.a. directory), then all temporary files -** created by SQLite will be placed in that directory. If this variable +** created by SQLite when using a built-in [sqlite3_vfs | VFS] +** will be placed in that directory.)^ ^If this variable ** is a NULL pointer, then SQLite performs a search for an appropriate ** temporary file directory. ** @@ -4210,8 +4245,8 @@ ** routines have been called and that this variable remain unchanged ** thereafter. ** -** The [temp_store_directory pragma] may modify this variable and cause -** it to point to memory obtained from [sqlite3_malloc]. Furthermore, +** ^The [temp_store_directory pragma] may modify this variable and cause +** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, ** the [temp_store_directory pragma] always assumes that any string ** that this variable points to is held in memory obtained from ** [sqlite3_malloc] and the pragma may attempt to free that memory @@ -4223,14 +4258,14 @@ SQLITE_API char *sqlite3_temp_directory; /* -** CAPI3REF: Test For Auto-Commit Mode {H12930} +** CAPI3REF: Test For Auto-Commit Mode ** KEYWORDS: {autocommit mode} ** -** The sqlite3_get_autocommit() interface returns non-zero or +** ^The sqlite3_get_autocommit() interface returns non-zero or ** zero if the given database connection is or is not in autocommit mode, -** respectively. Autocommit mode is on by default. -** Autocommit mode is disabled by a [BEGIN] statement. -** Autocommit mode is re-enabled by a [COMMIT] or [ROLLBACK]. +** respectively. ^Autocommit mode is on by default. +** ^Autocommit mode is disabled by a [BEGIN] statement. +** ^Autocommit mode is re-enabled by a [COMMIT] or [ROLLBACK]. ** ** If certain kinds of errors occur on a statement within a multi-statement ** transaction (errors including [SQLITE_FULL], [SQLITE_IOERR], @@ -4242,58 +4277,55 @@ ** If another thread changes the autocommit status of the database ** connection while this routine is running, then the return value ** is undefined. -** -** Requirements: [H12931] [H12932] [H12933] [H12934] */ SQLITE_API int sqlite3_get_autocommit(sqlite3*); /* -** CAPI3REF: Find The Database Handle Of A Prepared Statement {H13120} +** CAPI3REF: Find The Database Handle Of A Prepared Statement ** -** The sqlite3_db_handle interface returns the [database connection] handle -** to which a [prepared statement] belongs. The [database connection] -** returned by sqlite3_db_handle is the same [database connection] that was the first argument +** ^The sqlite3_db_handle interface returns the [database connection] handle +** to which a [prepared statement] belongs. ^The [database connection] +** returned by sqlite3_db_handle is the same [database connection] +** that was the first argument ** to the [sqlite3_prepare_v2()] call (or its variants) that was used to ** create the statement in the first place. -** -** Requirements: [H13123] */ SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); /* -** CAPI3REF: Find the next prepared statement {H13140} +** CAPI3REF: Find the next prepared statement ** -** This interface returns a pointer to the next [prepared statement] after -** pStmt associated with the [database connection] pDb. If pStmt is NULL +** ^This interface returns a pointer to the next [prepared statement] after +** pStmt associated with the [database connection] pDb. ^If pStmt is NULL ** then this interface returns a pointer to the first prepared statement -** associated with the database connection pDb. If no prepared statement +** associated with the database connection pDb. ^If no prepared statement ** satisfies the conditions of this routine, it returns NULL. ** ** The [database connection] pointer D in a call to ** [sqlite3_next_stmt(D,S)] must refer to an open database ** connection and in particular must not be a NULL pointer. -** -** Requirements: [H13143] [H13146] [H13149] [H13152] */ SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); /* -** CAPI3REF: Commit And Rollback Notification Callbacks {H12950} +** CAPI3REF: Commit And Rollback Notification Callbacks ** -** The sqlite3_commit_hook() interface registers a callback +** ^The sqlite3_commit_hook() interface registers a callback ** function to be invoked whenever a transaction is [COMMIT | committed]. -** Any callback set by a previous call to sqlite3_commit_hook() +** ^Any callback set by a previous call to sqlite3_commit_hook() ** for the same database connection is overridden. -** The sqlite3_rollback_hook() interface registers a callback +** ^The sqlite3_rollback_hook() interface registers a callback ** function to be invoked whenever a transaction is [ROLLBACK | rolled back]. -** Any callback set by a previous call to sqlite3_commit_hook() +** ^Any callback set by a previous call to sqlite3_rollback_hook() ** for the same database connection is overridden. -** The pArg argument is passed through to the callback. -** If the callback on a commit hook function returns non-zero, +** ^The pArg argument is passed through to the callback. +** ^If the callback on a commit hook function returns non-zero, ** then the commit is converted into a rollback. ** -** If another function was previously registered, its -** pArg value is returned. Otherwise NULL is returned. +** ^The sqlite3_commit_hook(D,C,P) and sqlite3_rollback_hook(D,C,P) functions +** return the P argument from the previous call of the same function +** on the same [database connection] D, or NULL for +** the first call for each function on D. ** ** The callback implementation must not do anything that will modify ** the database connection that invoked the callback. Any actions @@ -4303,59 +4335,54 @@ ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** -** Registering a NULL function disables the callback. +** ^Registering a NULL function disables the callback. ** -** When the commit hook callback routine returns zero, the [COMMIT] -** operation is allowed to continue normally. If the commit hook +** ^When the commit hook callback routine returns zero, the [COMMIT] +** operation is allowed to continue normally. ^If the commit hook ** returns non-zero, then the [COMMIT] is converted into a [ROLLBACK]. -** The rollback hook is invoked on a rollback that results from a commit +** ^The rollback hook is invoked on a rollback that results from a commit ** hook returning non-zero, just as it would be with any other rollback. ** -** For the purposes of this API, a transaction is said to have been +** ^For the purposes of this API, a transaction is said to have been ** rolled back if an explicit "ROLLBACK" statement is executed, or ** an error or constraint causes an implicit rollback to occur. -** The rollback callback is not invoked if a transaction is +** ^The rollback callback is not invoked if a transaction is ** automatically rolled back because the database connection is closed. -** The rollback callback is not invoked if a transaction is +** ^The rollback callback is not invoked if a transaction is ** rolled back because a commit callback returned non-zero. -** Check on this ** ** See also the [sqlite3_update_hook()] interface. -** -** Requirements: -** [H12951] [H12952] [H12953] [H12954] [H12955] -** [H12961] [H12962] [H12963] [H12964] */ SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); /* -** CAPI3REF: Data Change Notification Callbacks {H12970} +** CAPI3REF: Data Change Notification Callbacks ** -** The sqlite3_update_hook() interface registers a callback function +** ^The sqlite3_update_hook() interface registers a callback function ** with the [database connection] identified by the first argument ** to be invoked whenever a row is updated, inserted or deleted. -** Any callback set by a previous call to this function +** ^Any callback set by a previous call to this function ** for the same database connection is overridden. ** -** The second argument is a pointer to the function to invoke when a +** ^The second argument is a pointer to the function to invoke when a ** row is updated, inserted or deleted. -** The first argument to the callback is a copy of the third argument +** ^The first argument to the callback is a copy of the third argument ** to sqlite3_update_hook(). -** The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], +** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], ** or [SQLITE_UPDATE], depending on the operation that caused the callback ** to be invoked. -** The third and fourth arguments to the callback contain pointers to the +** ^The third and fourth arguments to the callback contain pointers to the ** database and table name containing the affected row. -** The final callback parameter is the [rowid] of the row. -** In the case of an update, this is the [rowid] after the update takes place. +** ^The final callback parameter is the [rowid] of the row. +** ^In the case of an update, this is the [rowid] after the update takes place. ** -** The update hook is not invoked when internal system tables are -** modified (i.e. sqlite_master and sqlite_sequence). +** ^(The update hook is not invoked when internal system tables are +** modified (i.e. sqlite_master and sqlite_sequence).)^ ** -** In the current implementation, the update hook +** ^In the current implementation, the update hook ** is not invoked when duplication rows are deleted because of an -** [ON CONFLICT | ON CONFLICT REPLACE] clause. Nor is the update hook +** [ON CONFLICT | ON CONFLICT REPLACE] clause. ^Nor is the update hook ** invoked when rows are deleted using the [truncate optimization]. ** The exceptions defined in this paragraph might change in a future ** release of SQLite. @@ -4367,14 +4394,13 @@ ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** -** If another function was previously registered, its pArg value -** is returned. Otherwise NULL is returned. +** ^The sqlite3_update_hook(D,C,P) function +** returns the P argument from the previous call +** on the same [database connection] D, or NULL for +** the first call on D. ** ** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()] ** interfaces. -** -** Requirements: -** [H12971] [H12973] [H12975] [H12977] [H12979] [H12981] [H12983] [H12986] */ SQLITE_API void *sqlite3_update_hook( sqlite3*, @@ -4383,74 +4409,66 @@ ); /* -** CAPI3REF: Enable Or Disable Shared Pager Cache {H10330} -** KEYWORDS: {shared cache} {shared cache mode} +** CAPI3REF: Enable Or Disable Shared Pager Cache +** KEYWORDS: {shared cache} ** -** This routine enables or disables the sharing of the database cache +** ^(This routine enables or disables the sharing of the database cache ** and schema data structures between [database connection | connections] ** to the same database. Sharing is enabled if the argument is true -** and disabled if the argument is false. +** and disabled if the argument is false.)^ ** -** Cache sharing is enabled and disabled for an entire process. +** ^Cache sharing is enabled and disabled for an entire process. ** This is a change as of SQLite version 3.5.0. In prior versions of SQLite, ** sharing was enabled or disabled for each thread separately. ** -** The cache sharing mode set by this interface effects all subsequent +** ^(The cache sharing mode set by this interface effects all subsequent ** calls to [sqlite3_open()], [sqlite3_open_v2()], and [sqlite3_open16()]. ** Existing database connections continue use the sharing mode -** that was in effect at the time they were opened. +** that was in effect at the time they were opened.)^ ** -** Virtual tables cannot be used with a shared cache. When shared -** cache is enabled, the [sqlite3_create_module()] API used to register -** virtual tables will always return an error. +** ^(This routine returns [SQLITE_OK] if shared cache was enabled or disabled +** successfully. An [error code] is returned otherwise.)^ ** -** This routine returns [SQLITE_OK] if shared cache was enabled or disabled -** successfully. An [error code] is returned otherwise. -** -** Shared cache is disabled by default. But this might change in +** ^Shared cache is disabled by default. But this might change in ** future releases of SQLite. Applications that care about shared ** cache setting should set it explicitly. ** ** See Also: [SQLite Shared-Cache Mode] -** -** Requirements: [H10331] [H10336] [H10337] [H10339] */ SQLITE_API int sqlite3_enable_shared_cache(int); /* -** CAPI3REF: Attempt To Free Heap Memory {H17340} +** CAPI3REF: Attempt To Free Heap Memory ** -** The sqlite3_release_memory() interface attempts to free N bytes +** ^The sqlite3_release_memory() interface attempts to free N bytes ** of heap memory by deallocating non-essential memory allocations -** held by the database library. {END} Memory used to cache database +** held by the database library. Memory used to cache database ** pages to improve performance is an example of non-essential memory. -** sqlite3_release_memory() returns the number of bytes actually freed, +** ^sqlite3_release_memory() returns the number of bytes actually freed, ** which might be more or less than the amount requested. -** -** Requirements: [H17341] [H17342] */ SQLITE_API int sqlite3_release_memory(int); /* -** CAPI3REF: Impose A Limit On Heap Size {H17350} +** CAPI3REF: Impose A Limit On Heap Size ** -** The sqlite3_soft_heap_limit() interface places a "soft" limit +** ^The sqlite3_soft_heap_limit() interface places a "soft" limit ** on the amount of heap memory that may be allocated by SQLite. -** If an internal allocation is requested that would exceed the +** ^If an internal allocation is requested that would exceed the ** soft heap limit, [sqlite3_release_memory()] is invoked one or ** more times to free up some space before the allocation is performed. ** -** The limit is called "soft", because if [sqlite3_release_memory()] +** ^The limit is called "soft" because if [sqlite3_release_memory()] ** cannot free sufficient memory to prevent the limit from being exceeded, ** the memory is allocated anyway and the current operation proceeds. ** -** A negative or zero value for N means that there is no soft heap limit and +** ^A negative or zero value for N means that there is no soft heap limit and ** [sqlite3_release_memory()] will only be called when memory is exhausted. -** The default value for the soft heap limit is zero. +** ^The default value for the soft heap limit is zero. ** -** SQLite makes a best effort to honor the soft heap limit. +** ^(SQLite makes a best effort to honor the soft heap limit. ** But if the soft heap limit cannot be honored, execution will -** continue without error or notification. This is why the limit is +** continue without error or notification.)^ This is why the limit is ** called a "soft" limit. It is advisory only. ** ** Prior to SQLite version 3.5.0, this routine only constrained the memory @@ -4460,35 +4478,32 @@ ** is an upper bound on the total memory allocation for all threads. In ** version 3.5.0 there is no mechanism for limiting the heap usage for ** individual threads. -** -** Requirements: -** [H16351] [H16352] [H16353] [H16354] [H16355] [H16358] */ SQLITE_API void sqlite3_soft_heap_limit(int); /* -** CAPI3REF: Extract Metadata About A Column Of A Table {H12850} +** CAPI3REF: Extract Metadata About A Column Of A Table ** -** This routine returns metadata about a specific column of a specific +** ^This routine returns metadata about a specific column of a specific ** database table accessible using the [database connection] handle ** passed as the first function argument. ** -** The column is identified by the second, third and fourth parameters to -** this function. The second parameter is either the name of the database -** (i.e. "main", "temp" or an attached database) containing the specified -** table or NULL. If it is NULL, then all attached databases are searched +** ^The column is identified by the second, third and fourth parameters to +** this function. ^The second parameter is either the name of the database +** (i.e. "main", "temp", or an attached database) containing the specified +** table or NULL. ^If it is NULL, then all attached databases are searched ** for the table using the same algorithm used by the database engine to ** resolve unqualified table references. ** -** The third and fourth parameters to this function are the table and column +** ^The third and fourth parameters to this function are the table and column ** name of the desired column, respectively. Neither of these parameters ** may be NULL. ** -** Metadata is returned by writing to the memory locations passed as the 5th -** and subsequent parameters to this function. Any of these arguments may be +** ^Metadata is returned by writing to the memory locations passed as the 5th +** and subsequent parameters to this function. ^Any of these arguments may be ** NULL, in which case the corresponding element of metadata is omitted. ** -**
    +** ^(
    ** **
    Parameter Output
    Type
    Description ** @@ -4498,17 +4513,17 @@ **
    8th int True if column is part of the PRIMARY KEY **
    9th int True if column is [AUTOINCREMENT] **
    -**
    +**
    )^ ** -** The memory pointed to by the character pointers returned for the +** ^The memory pointed to by the character pointers returned for the ** declaration type and collation sequence is valid only until the next ** call to any SQLite API function. ** -** If the specified table is actually a view, an [error code] is returned. +** ^If the specified table is actually a view, an [error code] is returned. ** -** If the specified column is "rowid", "oid" or "_rowid_" and an +** ^If the specified column is "rowid", "oid" or "_rowid_" and an ** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output -** parameters are set for the explicitly declared column. If there is no +** parameters are set for the explicitly declared column. ^(If there is no ** explicitly declared [INTEGER PRIMARY KEY] column, then the output ** parameters are set as follows: ** @@ -4518,14 +4533,14 @@ ** not null: 0 ** primary key: 1 ** auto increment: 0 -** +** )^ ** -** This function may load one or more schemas from database files. If an +** ^(This function may load one or more schemas from database files. If an ** error occurs during this process, or if the requested table or column ** cannot be found, an [error code] is returned and an error message left -** in the [database connection] (to be retrieved using sqlite3_errmsg()). +** in the [database connection] (to be retrieved using sqlite3_errmsg()).)^ ** -** This API is only available if the library was compiled with the +** ^This API is only available if the library was compiled with the ** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined. */ SQLITE_API int sqlite3_table_column_metadata( @@ -4541,30 +4556,29 @@ ); /* -** CAPI3REF: Load An Extension {H12600} -** -** This interface loads an SQLite extension library from the named file. -** -** {H12601} The sqlite3_load_extension() interface attempts to load an -** SQLite extension library contained in the file zFile. -** -** {H12602} The entry point is zProc. +** CAPI3REF: Load An Extension ** -** {H12603} zProc may be 0, in which case the name of the entry point -** defaults to "sqlite3_extension_init". +** ^This interface loads an SQLite extension library from the named file. ** -** {H12604} The sqlite3_load_extension() interface shall return -** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. +** ^The sqlite3_load_extension() interface attempts to load an +** SQLite extension library contained in the file zFile. ** -** {H12605} If an error occurs and pzErrMsg is not 0, then the -** [sqlite3_load_extension()] interface shall attempt to -** fill *pzErrMsg with error message text stored in memory -** obtained from [sqlite3_malloc()]. {END} The calling function -** should free this memory by calling [sqlite3_free()]. +** ^The entry point is zProc. +** ^zProc may be 0, in which case the name of the entry point +** defaults to "sqlite3_extension_init". +** ^The sqlite3_load_extension() interface returns +** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. +** ^If an error occurs and pzErrMsg is not 0, then the +** [sqlite3_load_extension()] interface shall attempt to +** fill *pzErrMsg with error message text stored in memory +** obtained from [sqlite3_malloc()]. The calling function +** should free this memory by calling [sqlite3_free()]. +** +** ^Extension loading must be enabled using +** [sqlite3_enable_load_extension()] prior to calling this API, +** otherwise an error will be returned. ** -** {H12606} Extension loading must be enabled using -** [sqlite3_enable_load_extension()] prior to calling this API, -** otherwise an error will be returned. +** See also the [load_extension() SQL function]. */ SQLITE_API int sqlite3_load_extension( sqlite3 *db, /* Load the extension into this database connection */ @@ -4574,61 +4588,49 @@ ); /* -** CAPI3REF: Enable Or Disable Extension Loading {H12620} +** CAPI3REF: Enable Or Disable Extension Loading ** -** So as not to open security holes in older applications that are +** ^So as not to open security holes in older applications that are ** unprepared to deal with extension loading, and as a means of disabling ** extension loading while evaluating user-entered SQL, the following API ** is provided to turn the [sqlite3_load_extension()] mechanism on and off. ** -** Extension loading is off by default. See ticket #1863. -** -** {H12621} Call the sqlite3_enable_load_extension() routine with onoff==1 -** to turn extension loading on and call it with onoff==0 to turn -** it back off again. -** -** {H12622} Extension loading is off by default. +** ^Extension loading is off by default. See ticket #1863. +** ^Call the sqlite3_enable_load_extension() routine with onoff==1 +** to turn extension loading on and call it with onoff==0 to turn +** it back off again. */ SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); /* -** CAPI3REF: Automatically Load An Extensions {H12640} +** CAPI3REF: Automatically Load An Extensions ** -** This API can be invoked at program startup in order to register +** ^This API can be invoked at program startup in order to register ** one or more statically linked extensions that will be available -** to all new [database connections]. {END} -** -** This routine stores a pointer to the extension in an array that is -** obtained from [sqlite3_malloc()]. If you run a memory leak checker -** on your program and it reports a leak because of this array, invoke -** [sqlite3_reset_auto_extension()] prior to shutdown to free the memory. -** -** {H12641} This function registers an extension entry point that is -** automatically invoked whenever a new [database connection] -** is opened using [sqlite3_open()], [sqlite3_open16()], -** or [sqlite3_open_v2()]. +** to all new [database connections]. ** -** {H12642} Duplicate extensions are detected so calling this routine -** multiple times with the same extension is harmless. -** -** {H12643} This routine stores a pointer to the extension in an array -** that is obtained from [sqlite3_malloc()]. -** -** {H12644} Automatic extensions apply across all threads. +** ^(This routine stores a pointer to the extension entry point +** in an array that is obtained from [sqlite3_malloc()]. That memory +** is deallocated by [sqlite3_reset_auto_extension()].)^ +** +** ^This function registers an extension entry point that is +** automatically invoked whenever a new [database connection] +** is opened using [sqlite3_open()], [sqlite3_open16()], +** or [sqlite3_open_v2()]. +** ^Duplicate extensions are detected so calling this routine +** multiple times with the same extension is harmless. +** ^Automatic extensions apply across all threads. */ SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); /* -** CAPI3REF: Reset Automatic Extension Loading {H12660} +** CAPI3REF: Reset Automatic Extension Loading ** -** This function disables all previously registered automatic -** extensions. {END} It undoes the effect of all prior -** [sqlite3_auto_extension()] calls. +** ^(This function disables all previously registered automatic +** extensions. It undoes the effect of all prior +** [sqlite3_auto_extension()] calls.)^ ** -** {H12661} This function disables all previously registered -** automatic extensions. -** -** {H12662} This function disables automatic extensions in all threads. +** ^This function disables automatic extensions in all threads. */ SQLITE_API void sqlite3_reset_auto_extension(void); @@ -4652,7 +4654,7 @@ typedef struct sqlite3_module sqlite3_module; /* -** CAPI3REF: Virtual Table Object {H18000} +** CAPI3REF: Virtual Table Object ** KEYWORDS: sqlite3_module {virtual table module} ** EXPERIMENTAL ** @@ -4660,10 +4662,10 @@ ** defines the implementation of a [virtual tables]. ** This structure consists mostly of methods for the module. ** -** A virtual table module is created by filling in a persistent +** ^A virtual table module is created by filling in a persistent ** instance of this structure and passing a pointer to that instance ** to [sqlite3_create_module()] or [sqlite3_create_module_v2()]. -** The registration remains valid until it is replaced by a different +** ^The registration remains valid until it is replaced by a different ** module or until the [database connection] closes. The content ** of this structure must not change while it is registered with ** any database connection. @@ -4699,7 +4701,7 @@ }; /* -** CAPI3REF: Virtual Table Indexing Information {H18100} +** CAPI3REF: Virtual Table Indexing Information ** KEYWORDS: sqlite3_index_info ** EXPERIMENTAL ** @@ -4709,42 +4711,42 @@ ** inputs to xBestIndex and are read-only. xBestIndex inserts its ** results into the **Outputs** fields. ** -** The aConstraint[] array records WHERE clause constraints of the form: +** ^(The aConstraint[] array records WHERE clause constraints of the form: ** **
    column OP expr
    ** -** where OP is =, <, <=, >, or >=. The particular operator is -** stored in aConstraint[].op. The index of the column is stored in -** aConstraint[].iColumn. aConstraint[].usable is TRUE if the +** where OP is =, <, <=, >, or >=.)^ ^(The particular operator is +** stored in aConstraint[].op.)^ ^(The index of the column is stored in +** aConstraint[].iColumn.)^ ^(aConstraint[].usable is TRUE if the ** expr on the right-hand side can be evaluated (and thus the constraint -** is usable) and false if it cannot. +** is usable) and false if it cannot.)^ ** -** The optimizer automatically inverts terms of the form "expr OP column" +** ^The optimizer automatically inverts terms of the form "expr OP column" ** and makes other simplifications to the WHERE clause in an attempt to ** get as many WHERE clause terms into the form shown above as possible. -** The aConstraint[] array only reports WHERE clause terms in the correct -** form that refer to the particular virtual table being queried. +** ^The aConstraint[] array only reports WHERE clause terms that are +** relevant to the particular virtual table being queried. ** -** Information about the ORDER BY clause is stored in aOrderBy[]. -** Each term of aOrderBy records a column of the ORDER BY clause. +** ^Information about the ORDER BY clause is stored in aOrderBy[]. +** ^Each term of aOrderBy records a column of the ORDER BY clause. ** ** The [xBestIndex] method must fill aConstraintUsage[] with information -** about what parameters to pass to xFilter. If argvIndex>0 then +** about what parameters to pass to xFilter. ^If argvIndex>0 then ** the right-hand side of the corresponding aConstraint[] is evaluated -** and becomes the argvIndex-th entry in argv. If aConstraintUsage[].omit +** and becomes the argvIndex-th entry in argv. ^(If aConstraintUsage[].omit ** is true, then the constraint is assumed to be fully handled by the -** virtual table and is not checked again by SQLite. +** virtual table and is not checked again by SQLite.)^ ** -** The idxNum and idxPtr values are recorded and passed into the +** ^The idxNum and idxPtr values are recorded and passed into the ** [xFilter] method. -** [sqlite3_free()] is used to free idxPtr if and only iff +** ^[sqlite3_free()] is used to free idxPtr if and only if ** needToFreeIdxPtr is true. ** -** The orderByConsumed means that output from [xFilter]/[xNext] will occur in +** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in ** the correct order to satisfy the ORDER BY clause so that no separate ** sorting step is required. ** -** The estimatedCost value is an estimate of the cost of doing the +** ^The estimatedCost value is an estimate of the cost of doing the ** particular lookup. A full scan of a table with N entries should have ** a cost of N. A binary search of a table of N entries should have a ** cost of approximately log(N). @@ -4782,24 +4784,28 @@ #define SQLITE_INDEX_CONSTRAINT_MATCH 64 /* -** CAPI3REF: Register A Virtual Table Implementation {H18200} +** CAPI3REF: Register A Virtual Table Implementation ** EXPERIMENTAL ** -** This routine is used to register a new [virtual table module] name. -** Module names must be registered before -** creating a new [virtual table] using the module, or before using a +** ^These routines are used to register a new [virtual table module] name. +** ^Module names must be registered before +** creating a new [virtual table] using the module and before using a ** preexisting [virtual table] for the module. ** -** The module name is registered on the [database connection] specified -** by the first parameter. The name of the module is given by the -** second parameter. The third parameter is a pointer to -** the implementation of the [virtual table module]. The fourth +** ^The module name is registered on the [database connection] specified +** by the first parameter. ^The name of the module is given by the +** second parameter. ^The third parameter is a pointer to +** the implementation of the [virtual table module]. ^The fourth ** parameter is an arbitrary client data pointer that is passed through ** into the [xCreate] and [xConnect] methods of the virtual table module ** when a new virtual table is be being created or reinitialized. ** -** This interface has exactly the same effect as calling -** [sqlite3_create_module_v2()] with a NULL client data destructor. +** ^The sqlite3_create_module_v2() interface has a fifth parameter which +** is a pointer to a destructor for the pClientData. ^SQLite will +** invoke the destructor function (if it is not NULL) when SQLite +** no longer needs the pClientData pointer. ^The sqlite3_create_module() +** interface is equivalent to sqlite3_create_module_v2() with a NULL +** destructor. */ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_create_module( sqlite3 *db, /* SQLite connection to register module with */ @@ -4807,17 +4813,6 @@ const sqlite3_module *p, /* Methods for the module */ void *pClientData /* Client data for xCreate/xConnect */ ); - -/* -** CAPI3REF: Register A Virtual Table Implementation {H18210} -** EXPERIMENTAL -** -** This routine is identical to the [sqlite3_create_module()] method, -** except that it has an extra parameter to specify -** a destructor function for the client data pointer. SQLite will -** invoke the destructor function (if it is not NULL) when SQLite -** no longer needs the pClientData pointer. -*/ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_create_module_v2( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ @@ -4827,33 +4822,33 @@ ); /* -** CAPI3REF: Virtual Table Instance Object {H18010} +** CAPI3REF: Virtual Table Instance Object ** KEYWORDS: sqlite3_vtab ** EXPERIMENTAL ** ** Every [virtual table module] implementation uses a subclass -** of the following structure to describe a particular instance +** of this object to describe a particular instance ** of the [virtual table]. Each subclass will ** be tailored to the specific needs of the module implementation. ** The purpose of this superclass is to define certain fields that are ** common to all module implementations. ** -** Virtual tables methods can set an error message by assigning a +** ^Virtual tables methods can set an error message by assigning a ** string obtained from [sqlite3_mprintf()] to zErrMsg. The method should ** take care that any prior string is freed by a call to [sqlite3_free()] -** prior to assigning a new string to zErrMsg. After the error message +** prior to assigning a new string to zErrMsg. ^After the error message ** is delivered up to the client application, the string will be automatically ** freed by sqlite3_free() and the zErrMsg field will be zeroed. */ struct sqlite3_vtab { const sqlite3_module *pModule; /* The module for this virtual table */ - int nRef; /* Used internally */ + int nRef; /* NO LONGER USED */ char *zErrMsg; /* Error message from sqlite3_mprintf() */ /* Virtual table implementations will typically add additional fields */ }; /* -** CAPI3REF: Virtual Table Cursor Object {H18020} +** CAPI3REF: Virtual Table Cursor Object ** KEYWORDS: sqlite3_vtab_cursor {virtual table cursor} ** EXPERIMENTAL ** @@ -4862,7 +4857,7 @@ ** [virtual table] and are used ** to loop through the virtual table. Cursors are created using the ** [sqlite3_module.xOpen | xOpen] method of the module and are destroyed -** by the [sqlite3_module.xClose | xClose] method. Cussors are used +** by the [sqlite3_module.xClose | xClose] method. Cursors are used ** by the [xFilter], [xNext], [xEof], [xColumn], and [xRowid] methods ** of the module. Each module implementation will define ** the content of a cursor structure to suit its own needs. @@ -4876,10 +4871,10 @@ }; /* -** CAPI3REF: Declare The Schema Of A Virtual Table {H18280} +** CAPI3REF: Declare The Schema Of A Virtual Table ** EXPERIMENTAL ** -** The [xCreate] and [xConnect] methods of a +** ^The [xCreate] and [xConnect] methods of a ** [virtual table module] call this interface ** to declare the format (the names and datatypes of the columns) of ** the virtual tables they implement. @@ -4887,17 +4882,17 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_declare_vtab(sqlite3*, const char *zSQL); /* -** CAPI3REF: Overload A Function For A Virtual Table {H18300} +** CAPI3REF: Overload A Function For A Virtual Table ** EXPERIMENTAL ** -** Virtual tables can provide alternative implementations of functions +** ^(Virtual tables can provide alternative implementations of functions ** using the [xFindFunction] method of the [virtual table module]. ** But global versions of those functions -** must exist in order to be overloaded. +** must exist in order to be overloaded.)^ ** -** This API makes sure a global version of a function with a particular +** ^(This API makes sure a global version of a function with a particular ** name and number of parameters exists. If no such function exists -** before this API is called, a new function is created. The implementation +** before this API is called, a new function is created.)^ ^The implementation ** of the new function always causes an exception to be thrown. So ** the new function is not good for anything by itself. Its only ** purpose is to be a placeholder function that can be overloaded @@ -4918,74 +4913,74 @@ */ /* -** CAPI3REF: A Handle To An Open BLOB {H17800} +** CAPI3REF: A Handle To An Open BLOB ** KEYWORDS: {BLOB handle} {BLOB handles} ** ** An instance of this object represents an open BLOB on which ** [sqlite3_blob_open | incremental BLOB I/O] can be performed. -** Objects of this type are created by [sqlite3_blob_open()] +** ^Objects of this type are created by [sqlite3_blob_open()] ** and destroyed by [sqlite3_blob_close()]. -** The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces +** ^The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces ** can be used to read or write small subsections of the BLOB. -** The [sqlite3_blob_bytes()] interface returns the size of the BLOB in bytes. +** ^The [sqlite3_blob_bytes()] interface returns the size of the BLOB in bytes. */ typedef struct sqlite3_blob sqlite3_blob; /* -** CAPI3REF: Open A BLOB For Incremental I/O {H17810} +** CAPI3REF: Open A BLOB For Incremental I/O ** -** This interfaces opens a [BLOB handle | handle] to the BLOB located +** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located ** in row iRow, column zColumn, table zTable in database zDb; ** in other words, the same BLOB that would be selected by: ** **
     **     SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
    -** 
    {END} +** )^ ** -** If the flags parameter is non-zero, then the BLOB is opened for read -** and write access. If it is zero, the BLOB is opened for read access. +** ^If the flags parameter is non-zero, then the BLOB is opened for read +** and write access. ^If it is zero, the BLOB is opened for read access. +** ^It is not possible to open a column that is part of an index or primary +** key for writing. ^If [foreign key constraints] are enabled, it is +** not possible to open a column that is part of a [child key] for writing. ** -** Note that the database name is not the filename that contains +** ^Note that the database name is not the filename that contains ** the database but rather the symbolic name of the database that -** is assigned when the database is connected using [ATTACH]. -** For the main database file, the database name is "main". -** For TEMP tables, the database name is "temp". +** appears after the AS keyword when the database is connected using [ATTACH]. +** ^For the main database file, the database name is "main". +** ^For TEMP tables, the database name is "temp". ** -** On success, [SQLITE_OK] is returned and the new [BLOB handle] is written +** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is written ** to *ppBlob. Otherwise an [error code] is returned and *ppBlob is set -** to be a null pointer. -** This function sets the [database connection] error code and message +** to be a null pointer.)^ +** ^This function sets the [database connection] error code and message ** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()] and related -** functions. Note that the *ppBlob variable is always initialized in a +** functions. ^Note that the *ppBlob variable is always initialized in a ** way that makes it safe to invoke [sqlite3_blob_close()] on *ppBlob ** regardless of the success or failure of this routine. ** -** If the row that a BLOB handle points to is modified by an +** ^(If the row that a BLOB handle points to is modified by an ** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects ** then the BLOB handle is marked as "expired". ** This is true if any column of the row is changed, even a column -** other than the one the BLOB handle is open on. -** Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for +** other than the one the BLOB handle is open on.)^ +** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for ** a expired BLOB handle fail with an return code of [SQLITE_ABORT]. -** Changes written into a BLOB prior to the BLOB expiring are not -** rollback by the expiration of the BLOB. Such changes will eventually -** commit if the transaction continues to completion. -** -** Use the [sqlite3_blob_bytes()] interface to determine the size of -** the opened blob. The size of a blob may not be changed by this -** underface. Use the [UPDATE] SQL command to change the size of a +** ^(Changes written into a BLOB prior to the BLOB expiring are not +** rolled back by the expiration of the BLOB. Such changes will eventually +** commit if the transaction continues to completion.)^ +** +** ^Use the [sqlite3_blob_bytes()] interface to determine the size of +** the opened blob. ^The size of a blob may not be changed by this +** interface. Use the [UPDATE] SQL command to change the size of a ** blob. ** -** The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces +** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces ** and the built-in [zeroblob] SQL function can be used, if desired, ** to create an empty, zero-filled blob in which to read or write using ** this interface. ** ** To avoid a resource leak, every open [BLOB handle] should eventually ** be released by a call to [sqlite3_blob_close()]. -** -** Requirements: -** [H17813] [H17814] [H17816] [H17819] [H17821] [H17824] */ SQLITE_API int sqlite3_blob_open( sqlite3*, @@ -4998,37 +4993,34 @@ ); /* -** CAPI3REF: Close A BLOB Handle {H17830} +** CAPI3REF: Close A BLOB Handle ** -** Closes an open [BLOB handle]. +** ^Closes an open [BLOB handle]. ** -** Closing a BLOB shall cause the current transaction to commit +** ^Closing a BLOB shall cause the current transaction to commit ** if there are no other BLOBs, no pending prepared statements, and the ** database connection is in [autocommit mode]. -** If any writes were made to the BLOB, they might be held in cache +** ^If any writes were made to the BLOB, they might be held in cache ** until the close operation if they will fit. ** -** Closing the BLOB often forces the changes +** ^(Closing the BLOB often forces the changes ** out to disk and so if any I/O errors occur, they will likely occur ** at the time when the BLOB is closed. Any errors that occur during -** closing are reported as a non-zero return value. -** -** The BLOB is closed unconditionally. Even if this routine returns -** an error code, the BLOB is still closed. +** closing are reported as a non-zero return value.)^ ** -** Calling this routine with a null pointer (which as would be returned -** by failed call to [sqlite3_blob_open()]) is a harmless no-op. +** ^(The BLOB is closed unconditionally. Even if this routine returns +** an error code, the BLOB is still closed.)^ ** -** Requirements: -** [H17833] [H17836] [H17839] +** ^Calling this routine with a null pointer (such as would be returned +** by a failed call to [sqlite3_blob_open()]) is a harmless no-op. */ SQLITE_API int sqlite3_blob_close(sqlite3_blob *); /* -** CAPI3REF: Return The Size Of An Open BLOB {H17840} +** CAPI3REF: Return The Size Of An Open BLOB ** -** Returns the size in bytes of the BLOB accessible via the -** successfully opened [BLOB handle] in its only argument. The +** ^Returns the size in bytes of the BLOB accessible via the +** successfully opened [BLOB handle] in its only argument. ^The ** incremental blob I/O routines can only read or overwriting existing ** blob content; they cannot change the size of a blob. ** @@ -5036,30 +5028,27 @@ ** by a prior successful call to [sqlite3_blob_open()] and which has not ** been closed by [sqlite3_blob_close()]. Passing any other pointer in ** to this routine results in undefined and probably undesirable behavior. -** -** Requirements: -** [H17843] */ SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); /* -** CAPI3REF: Read Data From A BLOB Incrementally {H17850} +** CAPI3REF: Read Data From A BLOB Incrementally ** -** This function is used to read data from an open [BLOB handle] into a +** ^(This function is used to read data from an open [BLOB handle] into a ** caller-supplied buffer. N bytes of data are copied into buffer Z -** from the open BLOB, starting at offset iOffset. +** from the open BLOB, starting at offset iOffset.)^ ** -** If offset iOffset is less than N bytes from the end of the BLOB, -** [SQLITE_ERROR] is returned and no data is read. If N or iOffset is +** ^If offset iOffset is less than N bytes from the end of the BLOB, +** [SQLITE_ERROR] is returned and no data is read. ^If N or iOffset is ** less than zero, [SQLITE_ERROR] is returned and no data is read. -** The size of the blob (and hence the maximum value of N+iOffset) +** ^The size of the blob (and hence the maximum value of N+iOffset) ** can be determined using the [sqlite3_blob_bytes()] interface. ** -** An attempt to read from an expired [BLOB handle] fails with an +** ^An attempt to read from an expired [BLOB handle] fails with an ** error code of [SQLITE_ABORT]. ** -** On success, SQLITE_OK is returned. -** Otherwise, an [error code] or an [extended error code] is returned. +** ^(On success, sqlite3_blob_read() returns SQLITE_OK. +** Otherwise, an [error code] or an [extended error code] is returned.)^ ** ** This routine only works on a [BLOB handle] which has been created ** by a prior successful call to [sqlite3_blob_open()] and which has not @@ -5067,40 +5056,37 @@ ** to this routine results in undefined and probably undesirable behavior. ** ** See also: [sqlite3_blob_write()]. -** -** Requirements: -** [H17853] [H17856] [H17859] [H17862] [H17863] [H17865] [H17868] */ SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); /* -** CAPI3REF: Write Data Into A BLOB Incrementally {H17870} +** CAPI3REF: Write Data Into A BLOB Incrementally ** -** This function is used to write data into an open [BLOB handle] from a -** caller-supplied buffer. N bytes of data are copied from the buffer Z +** ^This function is used to write data into an open [BLOB handle] from a +** caller-supplied buffer. ^N bytes of data are copied from the buffer Z ** into the open BLOB, starting at offset iOffset. ** -** If the [BLOB handle] passed as the first argument was not opened for +** ^If the [BLOB handle] passed as the first argument was not opened for ** writing (the flags parameter to [sqlite3_blob_open()] was zero), ** this function returns [SQLITE_READONLY]. ** -** This function may only modify the contents of the BLOB; it is +** ^This function may only modify the contents of the BLOB; it is ** not possible to increase the size of a BLOB using this API. -** If offset iOffset is less than N bytes from the end of the BLOB, -** [SQLITE_ERROR] is returned and no data is written. If N is +** ^If offset iOffset is less than N bytes from the end of the BLOB, +** [SQLITE_ERROR] is returned and no data is written. ^If N is ** less than zero [SQLITE_ERROR] is returned and no data is written. ** The size of the BLOB (and hence the maximum value of N+iOffset) ** can be determined using the [sqlite3_blob_bytes()] interface. ** -** An attempt to write to an expired [BLOB handle] fails with an -** error code of [SQLITE_ABORT]. Writes to the BLOB that occurred +** ^An attempt to write to an expired [BLOB handle] fails with an +** error code of [SQLITE_ABORT]. ^Writes to the BLOB that occurred ** before the [BLOB handle] expired are not rolled back by the ** expiration of the handle, though of course those changes might ** have been overwritten by the statement that expired the BLOB handle ** or by other independent statements. ** -** On success, SQLITE_OK is returned. -** Otherwise, an [error code] or an [extended error code] is returned. +** ^(On success, sqlite3_blob_write() returns SQLITE_OK. +** Otherwise, an [error code] or an [extended error code] is returned.)^ ** ** This routine only works on a [BLOB handle] which has been created ** by a prior successful call to [sqlite3_blob_open()] and which has not @@ -5108,15 +5094,11 @@ ** to this routine results in undefined and probably undesirable behavior. ** ** See also: [sqlite3_blob_read()]. -** -** Requirements: -** [H17873] [H17874] [H17875] [H17876] [H17877] [H17879] [H17882] [H17885] -** [H17888] */ SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); /* -** CAPI3REF: Virtual File System Objects {H11200} +** CAPI3REF: Virtual File System Objects ** ** A virtual filesystem (VFS) is an [sqlite3_vfs] object ** that SQLite uses to interact @@ -5125,34 +5107,31 @@ ** New VFSes can be registered and existing VFSes can be unregistered. ** The following interfaces are provided. ** -** The sqlite3_vfs_find() interface returns a pointer to a VFS given its name. -** Names are case sensitive. -** Names are zero-terminated UTF-8 strings. -** If there is no match, a NULL pointer is returned. -** If zVfsName is NULL then the default VFS is returned. -** -** New VFSes are registered with sqlite3_vfs_register(). -** Each new VFS becomes the default VFS if the makeDflt flag is set. -** The same VFS can be registered multiple times without injury. -** To make an existing VFS into the default VFS, register it again +** ^The sqlite3_vfs_find() interface returns a pointer to a VFS given its name. +** ^Names are case sensitive. +** ^Names are zero-terminated UTF-8 strings. +** ^If there is no match, a NULL pointer is returned. +** ^If zVfsName is NULL then the default VFS is returned. +** +** ^New VFSes are registered with sqlite3_vfs_register(). +** ^Each new VFS becomes the default VFS if the makeDflt flag is set. +** ^The same VFS can be registered multiple times without injury. +** ^To make an existing VFS into the default VFS, register it again ** with the makeDflt flag set. If two different VFSes with the ** same name are registered, the behavior is undefined. If a ** VFS is registered with a name that is NULL or an empty string, ** then the behavior is undefined. ** -** Unregister a VFS with the sqlite3_vfs_unregister() interface. -** If the default VFS is unregistered, another VFS is chosen as -** the default. The choice for the new VFS is arbitrary. -** -** Requirements: -** [H11203] [H11206] [H11209] [H11212] [H11215] [H11218] +** ^Unregister a VFS with the sqlite3_vfs_unregister() interface. +** ^(If the default VFS is unregistered, another VFS is chosen as +** the default. The choice for the new VFS is arbitrary.)^ */ SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); SQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); /* -** CAPI3REF: Mutexes {H17000} +** CAPI3REF: Mutexes ** ** The SQLite core uses these routines for thread ** synchronization. Though they are intended for internal @@ -5161,7 +5140,7 @@ ** ** The SQLite source code contains multiple implementations ** of these mutex routines. An appropriate implementation -** is selected automatically at compile-time. The following +** is selected automatically at compile-time. ^(The following ** implementations are available in the SQLite core: ** **
      @@ -5169,26 +5148,26 @@ **
    • SQLITE_MUTEX_PTHREAD **
    • SQLITE_MUTEX_W32 **
    • SQLITE_MUTEX_NOOP -**
    +** )^ ** -** The SQLITE_MUTEX_NOOP implementation is a set of routines +** ^The SQLITE_MUTEX_NOOP implementation is a set of routines ** that does no real locking and is appropriate for use in -** a single-threaded application. The SQLITE_MUTEX_OS2, +** a single-threaded application. ^The SQLITE_MUTEX_OS2, ** SQLITE_MUTEX_PTHREAD, and SQLITE_MUTEX_W32 implementations ** are appropriate for use on OS/2, Unix, and Windows. ** -** If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor +** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor ** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex ** implementation is included with the library. In this case the ** application must supply a custom mutex implementation using the ** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function ** before calling sqlite3_initialize() or any other public sqlite3_ -** function that calls sqlite3_initialize(). +** function that calls sqlite3_initialize().)^ ** -** {H17011} The sqlite3_mutex_alloc() routine allocates a new -** mutex and returns a pointer to it. {H17012} If it returns NULL -** that means that a mutex could not be allocated. {H17013} SQLite -** will unwind its stack and return an error. {H17014} The argument +** ^The sqlite3_mutex_alloc() routine allocates a new +** mutex and returns a pointer to it. ^If it returns NULL +** that means that a mutex could not be allocated. ^SQLite +** will unwind its stack and return an error. ^(The argument ** to sqlite3_mutex_alloc() is one of these integer constants: ** **
      @@ -5200,64 +5179,66 @@ **
    • SQLITE_MUTEX_STATIC_PRNG **
    • SQLITE_MUTEX_STATIC_LRU **
    • SQLITE_MUTEX_STATIC_LRU2 -**
    +** )^ ** -** {H17015} The first two constants cause sqlite3_mutex_alloc() to create -** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE -** is used but not necessarily so when SQLITE_MUTEX_FAST is used. {END} +** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) +** cause sqlite3_mutex_alloc() to create +** a new mutex. ^The new mutex is recursive when SQLITE_MUTEX_RECURSIVE +** is used but not necessarily so when SQLITE_MUTEX_FAST is used. ** The mutex implementation does not need to make a distinction ** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does -** not want to. {H17016} But SQLite will only request a recursive mutex in -** cases where it really needs one. {END} If a faster non-recursive mutex +** not want to. ^SQLite will only request a recursive mutex in +** cases where it really needs one. ^If a faster non-recursive mutex ** implementation is available on the host platform, the mutex subsystem ** might return such a mutex in response to SQLITE_MUTEX_FAST. ** -** {H17017} The other allowed parameters to sqlite3_mutex_alloc() each return -** a pointer to a static preexisting mutex. {END} Four static mutexes are +** ^The other allowed parameters to sqlite3_mutex_alloc() (anything other +** than SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) each return +** a pointer to a static preexisting mutex. ^Six static mutexes are ** used by the current version of SQLite. Future versions of SQLite ** may add additional static mutexes. Static mutexes are for internal ** use by SQLite only. Applications that use SQLite mutexes should ** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or ** SQLITE_MUTEX_RECURSIVE. ** -** {H17018} Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST +** ^Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST ** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() -** returns a different mutex on every call. {H17034} But for the static +** returns a different mutex on every call. ^But for the static ** mutex types, the same mutex is returned on every call that has ** the same type number. ** -** {H17019} The sqlite3_mutex_free() routine deallocates a previously -** allocated dynamic mutex. {H17020} SQLite is careful to deallocate every -** dynamic mutex that it allocates. {A17021} The dynamic mutexes must not be in -** use when they are deallocated. {A17022} Attempting to deallocate a static -** mutex results in undefined behavior. {H17023} SQLite never deallocates -** a static mutex. {END} +** ^The sqlite3_mutex_free() routine deallocates a previously +** allocated dynamic mutex. ^SQLite is careful to deallocate every +** dynamic mutex that it allocates. The dynamic mutexes must not be in +** use when they are deallocated. Attempting to deallocate a static +** mutex results in undefined behavior. ^SQLite never deallocates +** a static mutex. ** -** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt -** to enter a mutex. {H17024} If another thread is already within the mutex, +** ^The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt +** to enter a mutex. ^If another thread is already within the mutex, ** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return -** SQLITE_BUSY. {H17025} The sqlite3_mutex_try() interface returns [SQLITE_OK] -** upon successful entry. {H17026} Mutexes created using +** SQLITE_BUSY. ^The sqlite3_mutex_try() interface returns [SQLITE_OK] +** upon successful entry. ^(Mutexes created using ** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread. -** {H17027} In such cases the, +** In such cases the, ** mutex must be exited an equal number of times before another thread -** can enter. {A17028} If the same thread tries to enter any other +** can enter.)^ ^(If the same thread tries to enter any other ** kind of mutex more than once, the behavior is undefined. -** {H17029} SQLite will never exhibit -** such behavior in its own use of mutexes. +** SQLite will never exhibit +** such behavior in its own use of mutexes.)^ ** -** Some systems (for example, Windows 95) do not support the operation +** ^(Some systems (for example, Windows 95) do not support the operation ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() -** will always return SQLITE_BUSY. {H17030} The SQLite core only ever uses -** sqlite3_mutex_try() as an optimization so this is acceptable behavior. +** will always return SQLITE_BUSY. The SQLite core only ever uses +** sqlite3_mutex_try() as an optimization so this is acceptable behavior.)^ ** -** {H17031} The sqlite3_mutex_leave() routine exits a mutex that was -** previously entered by the same thread. {A17032} The behavior +** ^The sqlite3_mutex_leave() routine exits a mutex that was +** previously entered by the same thread. ^(The behavior ** is undefined if the mutex is not currently entered by the -** calling thread or is not currently allocated. {H17033} SQLite will -** never do either. {END} +** calling thread or is not currently allocated. SQLite will +** never do either.)^ ** -** If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or +** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or ** sqlite3_mutex_leave() is a NULL pointer, then all three routines ** behave as no-ops. ** @@ -5270,7 +5251,7 @@ SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); /* -** CAPI3REF: Mutex Methods Object {H17120} +** CAPI3REF: Mutex Methods Object ** EXPERIMENTAL ** ** An instance of this structure defines the low-level routines @@ -5286,19 +5267,19 @@ ** output variable when querying the system for the current mutex ** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. ** -** The xMutexInit method defined by this structure is invoked as +** ^The xMutexInit method defined by this structure is invoked as ** part of system initialization by the sqlite3_initialize() function. -** {H17001} The xMutexInit routine shall be called by SQLite once for each +** ^The xMutexInit routine is calle by SQLite exactly once for each ** effective call to [sqlite3_initialize()]. ** -** The xMutexEnd method defined by this structure is invoked as +** ^The xMutexEnd method defined by this structure is invoked as ** part of system shutdown by the sqlite3_shutdown() function. The ** implementation of this method is expected to release all outstanding ** resources obtained by the mutex methods implementation, especially -** those obtained by the xMutexInit method. {H17003} The xMutexEnd() -** interface shall be invoked once for each call to [sqlite3_shutdown()]. +** those obtained by the xMutexInit method. ^The xMutexEnd() +** interface is invoked exactly once for each call to [sqlite3_shutdown()]. ** -** The remaining seven methods defined by this structure (xMutexAlloc, +** ^(The remaining seven methods defined by this structure (xMutexAlloc, ** xMutexFree, xMutexEnter, xMutexTry, xMutexLeave, xMutexHeld and ** xMutexNotheld) implement the following interfaces (respectively): ** @@ -5310,7 +5291,7 @@ **
  • [sqlite3_mutex_leave()]
  • **
  • [sqlite3_mutex_held()]
  • **
  • [sqlite3_mutex_notheld()]
  • -** +** )^ ** ** The only difference is that the public sqlite3_XXX functions enumerated ** above silently ignore any invocations that pass a NULL pointer instead @@ -5319,6 +5300,21 @@ ** of passing a NULL pointer instead of a valid mutex handle are undefined ** (i.e. it is acceptable to provide an implementation that segfaults if ** it is passed a NULL pointer). +** +** The xMutexInit() method must be threadsafe. ^It must be harmless to +** invoke xMutexInit() mutiple times within the same process and without +** intervening calls to xMutexEnd(). Second and subsequent calls to +** xMutexInit() must be no-ops. +** +** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()] +** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory +** allocation for a static mutex. ^However xMutexAlloc() may use SQLite +** memory allocation for a fast or recursive mutex. +** +** ^SQLite will invoke the xMutexEnd() method when [sqlite3_shutdown()] is +** called, but only if the prior call to xMutexInit returned SQLITE_OK. +** If xMutexInit fails in any way, it is expected to clean up after itself +** prior to returning. */ typedef struct sqlite3_mutex_methods sqlite3_mutex_methods; struct sqlite3_mutex_methods { @@ -5334,39 +5330,41 @@ }; /* -** CAPI3REF: Mutex Verification Routines {H17080} +** CAPI3REF: Mutex Verification Routines ** ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines -** are intended for use inside assert() statements. {H17081} The SQLite core +** are intended for use inside assert() statements. ^The SQLite core ** never uses these routines except inside an assert() and applications -** are advised to follow the lead of the core. {H17082} The core only +** are advised to follow the lead of the core. ^The SQLite core only ** provides implementations for these routines when it is compiled -** with the SQLITE_DEBUG flag. {A17087} External mutex implementations +** with the SQLITE_DEBUG flag. ^External mutex implementations ** are only required to provide these routines if SQLITE_DEBUG is ** defined and if NDEBUG is not defined. ** -** {H17083} These routines should return true if the mutex in their argument +** ^These routines should return true if the mutex in their argument ** is held or not held, respectively, by the calling thread. ** -** {X17084} The implementation is not required to provided versions of these +** ^The implementation is not required to provided versions of these ** routines that actually work. If the implementation does not provide working ** versions of these routines, it should at least provide stubs that always ** return true so that one does not get spurious assertion failures. ** -** {H17085} If the argument to sqlite3_mutex_held() is a NULL pointer then -** the routine should return 1. {END} This seems counter-intuitive since +** ^If the argument to sqlite3_mutex_held() is a NULL pointer then +** the routine should return 1. This seems counter-intuitive since ** clearly the mutex cannot be held if it does not exist. But the ** the reason the mutex does not exist is because the build is not ** using mutexes. And we do not want the assert() containing the ** call to sqlite3_mutex_held() to fail, so a non-zero return is -** the appropriate thing to do. {H17086} The sqlite3_mutex_notheld() +** the appropriate thing to do. ^The sqlite3_mutex_notheld() ** interface should also return 1 when given a NULL pointer. */ +#ifndef NDEBUG SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); +#endif /* -** CAPI3REF: Mutex Types {H17001} +** CAPI3REF: Mutex Types ** ** The [sqlite3_mutex_alloc()] interface takes a single argument ** which is one of these integer constants. @@ -5386,48 +5384,50 @@ #define SQLITE_MUTEX_STATIC_LRU2 7 /* lru page list */ /* -** CAPI3REF: Retrieve the mutex for a database connection {H17002} +** CAPI3REF: Retrieve the mutex for a database connection ** -** This interface returns a pointer the [sqlite3_mutex] object that +** ^This interface returns a pointer the [sqlite3_mutex] object that ** serializes access to the [database connection] given in the argument ** when the [threading mode] is Serialized. -** If the [threading mode] is Single-thread or Multi-thread then this +** ^If the [threading mode] is Single-thread or Multi-thread then this ** routine returns a NULL pointer. */ SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); /* -** CAPI3REF: Low-Level Control Of Database Files {H11300} +** CAPI3REF: Low-Level Control Of Database Files ** -** {H11301} The [sqlite3_file_control()] interface makes a direct call to the +** ^The [sqlite3_file_control()] interface makes a direct call to the ** xFileControl method for the [sqlite3_io_methods] object associated -** with a particular database identified by the second argument. {H11302} The -** name of the database is the name assigned to the database by the -** ATTACH SQL command that opened the -** database. {H11303} To control the main database file, use the name "main" -** or a NULL pointer. {H11304} The third and fourth parameters to this routine +** with a particular database identified by the second argument. ^The +** name of the database "main" for the main database or "temp" for the +** TEMP database, or the name that appears after the AS keyword for +** databases that are added using the [ATTACH] SQL command. +** ^A NULL pointer can be used in place of "main" to refer to the +** main database file. +** ^The third and fourth parameters to this routine ** are passed directly through to the second and third parameters of -** the xFileControl method. {H11305} The return value of the xFileControl +** the xFileControl method. ^The return value of the xFileControl ** method becomes the return value of this routine. ** -** {H11306} If the second parameter (zDbName) does not match the name of any -** open database file, then SQLITE_ERROR is returned. {H11307} This error +** ^If the second parameter (zDbName) does not match the name of any +** open database file, then SQLITE_ERROR is returned. ^This error ** code is not remembered and will not be recalled by [sqlite3_errcode()] -** or [sqlite3_errmsg()]. {A11308} The underlying xFileControl method might -** also return SQLITE_ERROR. {A11309} There is no way to distinguish between +** or [sqlite3_errmsg()]. The underlying xFileControl method might +** also return SQLITE_ERROR. There is no way to distinguish between ** an incorrect zDbName and an SQLITE_ERROR return from the underlying -** xFileControl method. {END} +** xFileControl method. ** ** See also: [SQLITE_FCNTL_LOCKSTATE] */ SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); /* -** CAPI3REF: Testing Interface {H11400} +** CAPI3REF: Testing Interface ** -** The sqlite3_test_control() interface is used to read out internal +** ^The sqlite3_test_control() interface is used to read out internal ** state of SQLite and to inject faults into SQLite for testing -** purposes. The first parameter is an operation code that determines +** purposes. ^The first parameter is an operation code that determines ** the number, meaning, and operation of all subsequent parameters. ** ** This interface is not for use by applications. It exists solely @@ -5442,7 +5442,7 @@ SQLITE_API int sqlite3_test_control(int op, ...); /* -** CAPI3REF: Testing Interface Operation Codes {H11410} +** CAPI3REF: Testing Interface Operation Codes ** ** These constants are the valid operation code parameters used ** as the first argument to [sqlite3_test_control()]. @@ -5452,6 +5452,7 @@ ** Applications should not use any of these parameters or the ** [sqlite3_test_control()] interface. */ +#define SQLITE_TESTCTRL_FIRST 5 #define SQLITE_TESTCTRL_PRNG_SAVE 5 #define SQLITE_TESTCTRL_PRNG_RESTORE 6 #define SQLITE_TESTCTRL_PRNG_RESET 7 @@ -5461,29 +5462,33 @@ #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 +#define SQLITE_TESTCTRL_RESERVE 14 +#define SQLITE_TESTCTRL_OPTIMIZATIONS 15 +#define SQLITE_TESTCTRL_ISKEYWORD 16 +#define SQLITE_TESTCTRL_LAST 16 /* -** CAPI3REF: SQLite Runtime Status {H17200} +** CAPI3REF: SQLite Runtime Status ** EXPERIMENTAL ** -** This interface is used to retrieve runtime status information +** ^This interface is used to retrieve runtime status information ** about the preformance of SQLite, and optionally to reset various -** highwater marks. The first argument is an integer code for -** the specific parameter to measure. Recognized integer codes -** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...]. -** The current value of the parameter is returned into *pCurrent. -** The highest recorded value is returned in *pHighwater. If the +** highwater marks. ^The first argument is an integer code for +** the specific parameter to measure. ^(Recognized integer codes +** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^ +** ^The current value of the parameter is returned into *pCurrent. +** ^The highest recorded value is returned in *pHighwater. ^If the ** resetFlag is true, then the highest record value is reset after -** *pHighwater is written. Some parameters do not record the highest +** *pHighwater is written. ^(Some parameters do not record the highest ** value. For those parameters -** nothing is written into *pHighwater and the resetFlag is ignored. -** Other parameters record only the highwater mark and not the current -** value. For these latter parameters nothing is written into *pCurrent. +** nothing is written into *pHighwater and the resetFlag is ignored.)^ +** ^(Other parameters record only the highwater mark and not the current +** value. For these latter parameters nothing is written into *pCurrent.)^ ** -** This routine returns SQLITE_OK on success and a non-zero -** [error code] on failure. +** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a +** non-zero [error code] on failure. ** -** This routine is threadsafe but is not atomic. This routine can +** This routine is threadsafe but is not atomic. This routine can be ** called while other threads are running the same or different SQLite ** interfaces. However the values returned in *pCurrent and ** *pHighwater reflect the status of SQLite at different points in time @@ -5496,14 +5501,14 @@ /* -** CAPI3REF: Status Parameters {H17250} +** CAPI3REF: Status Parameters ** EXPERIMENTAL ** ** These integer constants designate various run-time status parameters ** that can be returned by [sqlite3_status()]. ** **
    -**
    SQLITE_STATUS_MEMORY_USED
    +** ^(
    SQLITE_STATUS_MEMORY_USED
    **
    This parameter is the current amount of memory checked out ** using [sqlite3_malloc()], either directly or indirectly. The ** figure includes calls made to [sqlite3_malloc()] by the application @@ -5511,45 +5516,45 @@ ** controlled by [SQLITE_CONFIG_SCRATCH] and auxiliary page-cache ** memory controlled by [SQLITE_CONFIG_PAGECACHE] is not included in ** this parameter. The amount returned is the sum of the allocation -** sizes as reported by the xSize method in [sqlite3_mem_methods].
    +** sizes as reported by the xSize method in [sqlite3_mem_methods].)^ ** -**
    SQLITE_STATUS_MALLOC_SIZE
    +** ^(
    SQLITE_STATUS_MALLOC_SIZE
    **
    This parameter records the largest memory allocation request ** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their ** internal equivalents). Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. -** The value written into the *pCurrent parameter is undefined.
    +** The value written into the *pCurrent parameter is undefined.)^ ** -**
    SQLITE_STATUS_PAGECACHE_USED
    +** ^(
    SQLITE_STATUS_PAGECACHE_USED
    **
    This parameter returns the number of pages used out of the ** [pagecache memory allocator] that was configured using ** [SQLITE_CONFIG_PAGECACHE]. The -** value returned is in pages, not in bytes.
    +** value returned is in pages, not in bytes.)^ ** -**
    SQLITE_STATUS_PAGECACHE_OVERFLOW
    +** ^(
    SQLITE_STATUS_PAGECACHE_OVERFLOW
    **
    This parameter returns the number of bytes of page cache ** allocation which could not be statisfied by the [SQLITE_CONFIG_PAGECACHE] ** buffer and where forced to overflow to [sqlite3_malloc()]. The ** returned value includes allocations that overflowed because they ** where too large (they were larger than the "sz" parameter to ** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because -** no space was left in the page cache.
    +** no space was left in the page cache.)^ ** -**
    SQLITE_STATUS_PAGECACHE_SIZE
    +** ^(
    SQLITE_STATUS_PAGECACHE_SIZE
    **
    This parameter records the largest memory allocation request ** handed to [pagecache memory allocator]. Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. -** The value written into the *pCurrent parameter is undefined.
    +** The value written into the *pCurrent parameter is undefined.)^ ** -**
    SQLITE_STATUS_SCRATCH_USED
    +** ^(
    SQLITE_STATUS_SCRATCH_USED
    **
    This parameter returns the number of allocations used out of the ** [scratch memory allocator] configured using ** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not ** in bytes. Since a single thread may only have one scratch allocation ** outstanding at time, this parameter also reports the number of threads -** using scratch memory at the same time.
    +** using scratch memory at the same time.)^ ** -**
    SQLITE_STATUS_SCRATCH_OVERFLOW
    +** ^(
    SQLITE_STATUS_SCRATCH_OVERFLOW
    **
    This parameter returns the number of bytes of scratch memory ** allocation which could not be statisfied by the [SQLITE_CONFIG_SCRATCH] ** buffer and where forced to overflow to [sqlite3_malloc()]. The values @@ -5557,17 +5562,17 @@ ** larger (that is, because the requested allocation was larger than the ** "sz" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer ** slots were available. -**
    +** )^ ** -**
    SQLITE_STATUS_SCRATCH_SIZE
    +** ^(
    SQLITE_STATUS_SCRATCH_SIZE
    **
    This parameter records the largest memory allocation request ** handed to [scratch memory allocator]. Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. -** The value written into the *pCurrent parameter is undefined.
    +** The value written into the *pCurrent parameter is undefined.)^ ** -**
    SQLITE_STATUS_PARSER_STACK
    +** ^(
    SQLITE_STATUS_PARSER_STACK
    **
    This parameter records the deepest parser stack. It is only -** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].
    +** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].)^ **
    ** ** New status parameters may be added from time to time. @@ -5583,18 +5588,18 @@ #define SQLITE_STATUS_SCRATCH_SIZE 8 /* -** CAPI3REF: Database Connection Status {H17500} +** CAPI3REF: Database Connection Status ** EXPERIMENTAL ** -** This interface is used to retrieve runtime status information -** about a single [database connection]. The first argument is the -** database connection object to be interrogated. The second argument -** is the parameter to interrogate. Currently, the only allowed value +** ^This interface is used to retrieve runtime status information +** about a single [database connection]. ^The first argument is the +** database connection object to be interrogated. ^The second argument +** is the parameter to interrogate. ^Currently, the only allowed value ** for the second parameter is [SQLITE_DBSTATUS_LOOKASIDE_USED]. ** Additional options will likely appear in future releases of SQLite. ** -** The current value of the requested parameter is written into *pCur -** and the highest instantaneous value is written into *pHiwtr. If +** ^The current value of the requested parameter is written into *pCur +** and the highest instantaneous value is written into *pHiwtr. ^If ** the resetFlg is true, then the highest instantaneous value is ** reset back down to the current value. ** @@ -5603,40 +5608,47 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); /* -** CAPI3REF: Status Parameters for database connections {H17520} +** CAPI3REF: Status Parameters for database connections ** EXPERIMENTAL ** -** Status verbs for [sqlite3_db_status()]. +** These constants are the available integer "verbs" that can be passed as +** the second argument to the [sqlite3_db_status()] interface. +** +** New verbs may be added in future releases of SQLite. Existing verbs +** might be discontinued. Applications should check the return code from +** [sqlite3_db_status()] to make sure that the call worked. +** The [sqlite3_db_status()] interface will return a non-zero error code +** if a discontinued or unsupported verb is invoked. ** **
    -**
    SQLITE_DBSTATUS_LOOKASIDE_USED
    +** ^(
    SQLITE_DBSTATUS_LOOKASIDE_USED
    **
    This parameter returns the number of lookaside memory slots currently -** checked out.
    +** checked out.)^ **
    */ #define SQLITE_DBSTATUS_LOOKASIDE_USED 0 /* -** CAPI3REF: Prepared Statement Status {H17550} +** CAPI3REF: Prepared Statement Status ** EXPERIMENTAL ** -** Each prepared statement maintains various +** ^(Each prepared statement maintains various ** [SQLITE_STMTSTATUS_SORT | counters] that measure the number -** of times it has performed specific operations. These counters can +** of times it has performed specific operations.)^ These counters can ** be used to monitor the performance characteristics of the prepared ** statements. For example, if the number of table steps greatly exceeds ** the number of table searches or result rows, that would tend to indicate ** that the prepared statement is using a full table scan rather than ** an index. ** -** This interface is used to retrieve and reset counter values from +** ^(This interface is used to retrieve and reset counter values from ** a [prepared statement]. The first argument is the prepared statement ** object to be interrogated. The second argument ** is an integer code for a specific [SQLITE_STMTSTATUS_SORT | counter] -** to be interrogated. -** The current value of the requested counter is returned. -** If the resetFlg is true, then the counter is reset to zero after this +** to be interrogated.)^ +** ^The current value of the requested counter is returned. +** ^If the resetFlg is true, then the counter is reset to zero after this ** interface call returns. ** ** See also: [sqlite3_status()] and [sqlite3_db_status()]. @@ -5644,7 +5656,7 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); /* -** CAPI3REF: Status Parameters for prepared statements {H17570} +** CAPI3REF: Status Parameters for prepared statements ** EXPERIMENTAL ** ** These preprocessor macros define integer codes that name counter @@ -5653,13 +5665,13 @@ ** **
    **
    SQLITE_STMTSTATUS_FULLSCAN_STEP
    -**
    This is the number of times that SQLite has stepped forward in +**
    ^This is the number of times that SQLite has stepped forward in ** a table as part of a full table scan. Large numbers for this counter ** may indicate opportunities for performance improvement through ** careful use of indices.
    ** **
    SQLITE_STMTSTATUS_SORT
    -**
    This is the number of sort operations that have occurred. +**
    ^This is the number of sort operations that have occurred. ** A non-zero value in this counter may indicate an opportunity to ** improvement performance through careful use of indices.
    ** @@ -5684,114 +5696,128 @@ /* ** CAPI3REF: Application Defined Page Cache. +** KEYWORDS: {page cache} ** EXPERIMENTAL ** -** The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can +** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can ** register an alternative page cache implementation by passing in an -** instance of the sqlite3_pcache_methods structure. The majority of the -** heap memory used by sqlite is used by the page cache to cache data read +** instance of the sqlite3_pcache_methods structure.)^ The majority of the +** heap memory used by SQLite is used by the page cache to cache data read ** from, or ready to be written to, the database file. By implementing a ** custom page cache using this API, an application can control more -** precisely the amount of memory consumed by sqlite, the way in which -** said memory is allocated and released, and the policies used to +** precisely the amount of memory consumed by SQLite, the way in which +** that memory is allocated and released, and the policies used to ** determine exactly which parts of a database file are cached and for ** how long. ** -** The contents of the structure are copied to an internal buffer by sqlite -** within the call to [sqlite3_config]. -** -** The xInit() method is called once for each call to [sqlite3_initialize()] -** (usually only once during the lifetime of the process). It is passed -** a copy of the sqlite3_pcache_methods.pArg value. It can be used to set -** up global structures and mutexes required by the custom page cache -** implementation. The xShutdown() method is called from within -** [sqlite3_shutdown()], if the application invokes this API. It can be used -** to clean up any outstanding resources before process shutdown, if required. -** -** The xCreate() method is used to construct a new cache instance. The +** ^(The contents of the sqlite3_pcache_methods structure are copied to an +** internal buffer by SQLite within the call to [sqlite3_config]. Hence +** the application may discard the parameter after the call to +** [sqlite3_config()] returns.)^ +** +** ^The xInit() method is called once for each call to [sqlite3_initialize()] +** (usually only once during the lifetime of the process). ^(The xInit() +** method is passed a copy of the sqlite3_pcache_methods.pArg value.)^ +** ^The xInit() method can set up up global structures and/or any mutexes +** required by the custom page cache implementation. +** +** ^The xShutdown() method is called from within [sqlite3_shutdown()], +** if the application invokes this API. It can be used to clean up +** any outstanding resources before process shutdown, if required. +** +** ^SQLite holds a [SQLITE_MUTEX_RECURSIVE] mutex when it invokes +** the xInit method, so the xInit method need not be threadsafe. ^The +** xShutdown method is only called from [sqlite3_shutdown()] so it does +** not need to be threadsafe either. All other methods must be threadsafe +** in multithreaded applications. +** +** ^SQLite will never invoke xInit() more than once without an intervening +** call to xShutdown(). +** +** ^The xCreate() method is used to construct a new cache instance. SQLite +** will typically create one cache instance for each open database file, +** though this is not guaranteed. ^The ** first parameter, szPage, is the size in bytes of the pages that must -** be allocated by the cache. szPage will not be a power of two. The -** second argument, bPurgeable, is true if the cache being created will -** be used to cache database pages read from a file stored on disk, or -** false if it is used for an in-memory database. The cache implementation -** does not have to do anything special based on the value of bPurgeable, -** it is purely advisory. +** be allocated by the cache. ^szPage will not be a power of two. ^szPage +** will the page size of the database file that is to be cached plus an +** increment (here called "R") of about 100 or 200. ^SQLite will use the +** extra R bytes on each page to store metadata about the underlying +** database page on disk. The value of R depends +** on the SQLite version, the target platform, and how SQLite was compiled. +** ^R is constant for a particular build of SQLite. ^The second argument to +** xCreate(), bPurgeable, is true if the cache being created will +** be used to cache database pages of a file stored on disk, or +** false if it is used for an in-memory database. ^The cache implementation +** does not have to do anything special based with the value of bPurgeable; +** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will +** never invoke xUnpin() except to deliberately delete a page. +** ^In other words, a cache created with bPurgeable set to false will +** never contain any unpinned pages. ** -** The xCachesize() method may be called at any time by SQLite to set the +** ^(The xCachesize() method may be called at any time by SQLite to set the ** suggested maximum cache-size (number of pages stored by) the cache ** instance passed as the first argument. This is the value configured using -** the SQLite "[PRAGMA cache_size]" command. As with the bPurgeable parameter, -** the implementation is not required to do anything special with this -** value, it is advisory only. +** the SQLite "[PRAGMA cache_size]" command.)^ ^As with the bPurgeable +** parameter, the implementation is not required to do anything with this +** value; it is advisory only. ** -** The xPagecount() method should return the number of pages currently -** stored in the cache supplied as an argument. +** ^The xPagecount() method should return the number of pages currently +** stored in the cache. ** -** The xFetch() method is used to fetch a page and return a pointer to it. -** A 'page', in this context, is a buffer of szPage bytes aligned at an -** 8-byte boundary. The page to be fetched is determined by the key. The +** ^The xFetch() method is used to fetch a page and return a pointer to it. +** ^A 'page', in this context, is a buffer of szPage bytes aligned at an +** 8-byte boundary. ^The page to be fetched is determined by the key. ^The ** mimimum key value is 1. After it has been retrieved using xFetch, the page -** is considered to be pinned. +** is considered to be "pinned". ** -** If the requested page is already in the page cache, then a pointer to -** the cached buffer should be returned with its contents intact. If the -** page is not already in the cache, then the expected behaviour of the -** cache is determined by the value of the createFlag parameter passed -** to xFetch, according to the following table: +** ^If the requested page is already in the page cache, then the page cache +** implementation must return a pointer to the page buffer with its content +** intact. ^(If the requested page is not already in the cache, then the +** behavior of the cache implementation is determined by the value of the +** createFlag parameter passed to xFetch, according to the following table: ** ** -**
    createFlagExpected Behaviour -**
    0NULL should be returned. No new cache entry is created. -**
    1If createFlag is set to 1, this indicates that -** SQLite is holding pinned pages that can be unpinned -** by writing their contents to the database file (a -** relatively expensive operation). In this situation the -** cache implementation has two choices: it can return NULL, -** in which case SQLite will attempt to unpin one or more -** pages before re-requesting the same page, or it can -** allocate a new page and return a pointer to it. If a new -** page is allocated, then the first sizeof(void*) bytes of -** it (at least) must be zeroed before it is returned. -**
    2If createFlag is set to 2, then SQLite is not holding any -** pinned pages associated with the specific cache passed -** as the first argument to xFetch() that can be unpinned. The -** cache implementation should attempt to allocate a new -** cache entry and return a pointer to it. Again, the first -** sizeof(void*) bytes of the page should be zeroed before -** it is returned. If the xFetch() method returns NULL when -** createFlag==2, SQLite assumes that a memory allocation -** failed and returns SQLITE_NOMEM to the user. -**
    +** createFlag Behaviour when page is not already in cache +** 0 Do not allocate a new page. Return NULL. +** 1 Allocate a new page if it easy and convenient to do so. +** Otherwise return NULL. +** 2 Make every effort to allocate a new page. Only return +** NULL if allocating a new page is effectively impossible. +** )^ +** +** SQLite will normally invoke xFetch() with a createFlag of 0 or 1. If +** a call to xFetch() with createFlag==1 returns NULL, then SQLite will +** attempt to unpin one or more cache pages by spilling the content of +** pinned pages to disk and synching the operating system disk cache. After +** attempting to unpin pages, the xFetch() method will be invoked again with +** a createFlag of 2. ** -** xUnpin() is called by SQLite with a pointer to a currently pinned page -** as its second argument. If the third parameter, discard, is non-zero, +** ^xUnpin() is called by SQLite with a pointer to a currently pinned page +** as its second argument. ^(If the third parameter, discard, is non-zero, ** then the page should be evicted from the cache. In this case SQLite ** assumes that the next time the page is retrieved from the cache using -** the xFetch() method, it will be zeroed. If the discard parameter is -** zero, then the page is considered to be unpinned. The cache implementation -** may choose to reclaim (free or recycle) unpinned pages at any time. -** SQLite assumes that next time the page is retrieved from the cache -** it will either be zeroed, or contain the same data that it did when it -** was unpinned. +** the xFetch() method, it will be zeroed.)^ ^If the discard parameter is +** zero, then the page is considered to be unpinned. ^The cache implementation +** may choose to evict unpinned pages at any time. ** -** The cache is not required to perform any reference counting. A single +** ^(The cache is not required to perform any reference counting. A single ** call to xUnpin() unpins the page regardless of the number of prior calls -** to xFetch(). +** to xFetch().)^ ** -** The xRekey() method is used to change the key value associated with the -** page passed as the second argument from oldKey to newKey. If the cache +** ^The xRekey() method is used to change the key value associated with the +** page passed as the second argument from oldKey to newKey. ^If the cache ** previously contains an entry associated with newKey, it should be -** discarded. Any prior cache entry associated with newKey is guaranteed not +** discarded. ^Any prior cache entry associated with newKey is guaranteed not ** to be pinned. ** -** When SQLite calls the xTruncate() method, the cache must discard all +** ^When SQLite calls the xTruncate() method, the cache must discard all ** existing cache entries with page numbers (keys) greater than or equal -** to the value of the iLimit parameter passed to xTruncate(). If any +** to the value of the iLimit parameter passed to xTruncate(). ^If any ** of these pages are pinned, they are implicitly unpinned, meaning that ** they can be safely discarded. ** -** The xDestroy() method is used to delete a cache allocated by xCreate(). -** All resources associated with the specified cache should be freed. After +** ^The xDestroy() method is used to delete a cache allocated by xCreate(). +** All resources associated with the specified cache should be freed. ^After ** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] ** handle invalid, and will not use it with any other sqlite3_pcache_methods ** functions. @@ -5816,7 +5842,7 @@ ** EXPERIMENTAL ** ** The sqlite3_backup object records state information about an ongoing -** online backup operation. The sqlite3_backup object is created by +** online backup operation. ^The sqlite3_backup object is created by ** a call to [sqlite3_backup_init()] and is destroyed by a call to ** [sqlite3_backup_finish()]. ** @@ -5828,20 +5854,20 @@ ** CAPI3REF: Online Backup API. ** EXPERIMENTAL ** -** This API is used to overwrite the contents of one database with that -** of another. It is useful either for creating backups of databases or +** The backup API copies the content of one database into another. +** It is useful either for creating backups of databases or ** for copying in-memory databases to or from persistent files. ** ** See Also: [Using the SQLite Online Backup API] ** -** Exclusive access is required to the destination database for the -** duration of the operation. However the source database is only -** read-locked while it is actually being read, it is not locked -** continuously for the entire operation. Thus, the backup may be -** performed on a live database without preventing other users from -** writing to the database for an extended period of time. +** ^Exclusive access is required to the destination database for the +** duration of the operation. ^However the source database is only +** read-locked while it is actually being read; it is not locked +** continuously for the entire backup operation. ^Thus, the backup may be +** performed on a live source database without preventing other users from +** reading or writing to the source database while the backup is underway. ** -** To perform a backup operation: +** ^(To perform a backup operation: **
      **
    1. sqlite3_backup_init() is called once to initialize the ** backup, @@ -5849,143 +5875,148 @@ ** the data between the two databases, and finally **
    2. sqlite3_backup_finish() is called to release all resources ** associated with the backup operation. -**
    +** )^ ** There should be exactly one call to sqlite3_backup_finish() for each ** successful call to sqlite3_backup_init(). ** ** sqlite3_backup_init() ** -** The first two arguments passed to [sqlite3_backup_init()] are the database -** handle associated with the destination database and the database name -** used to attach the destination database to the handle. The database name -** is "main" for the main database, "temp" for the temporary database, or -** the name specified as part of the [ATTACH] statement if the destination is -** an attached database. The third and fourth arguments passed to -** sqlite3_backup_init() identify the [database connection] -** and database name used -** to access the source database. The values passed for the source and -** destination [database connection] parameters must not be the same. -** -** If an error occurs within sqlite3_backup_init(), then NULL is returned -** and an error code and error message written into the [database connection] -** passed as the first argument. They may be retrieved using the -** [sqlite3_errcode()], [sqlite3_errmsg()], and [sqlite3_errmsg16()] functions. -** Otherwise, if successful, a pointer to an [sqlite3_backup] object is -** returned. This pointer may be used with the sqlite3_backup_step() and +** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the +** [database connection] associated with the destination database +** and the database name, respectively. +** ^The database name is "main" for the main database, "temp" for the +** temporary database, or the name specified after the AS keyword in +** an [ATTACH] statement for an attached database. +** ^The S and M arguments passed to +** sqlite3_backup_init(D,N,S,M) identify the [database connection] +** and database name of the source database, respectively. +** ^The source and destination [database connections] (parameters S and D) +** must be different or else sqlite3_backup_init(D,N,S,M) will file with +** an error. +** +** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is +** returned and an error code and error message are store3d in the +** destination [database connection] D. +** ^The error code and message for the failed call to sqlite3_backup_init() +** can be retrieved using the [sqlite3_errcode()], [sqlite3_errmsg()], and/or +** [sqlite3_errmsg16()] functions. +** ^A successful call to sqlite3_backup_init() returns a pointer to an +** [sqlite3_backup] object. +** ^The [sqlite3_backup] object may be used with the sqlite3_backup_step() and ** sqlite3_backup_finish() functions to perform the specified backup ** operation. ** ** sqlite3_backup_step() ** -** Function [sqlite3_backup_step()] is used to copy up to nPage pages between -** the source and destination databases, where nPage is the value of the -** second parameter passed to sqlite3_backup_step(). If nPage is a negative -** value, all remaining source pages are copied. If the required pages are -** succesfully copied, but there are still more pages to copy before the -** backup is complete, it returns [SQLITE_OK]. If no error occured and there -** are no more pages to copy, then [SQLITE_DONE] is returned. If an error -** occurs, then an SQLite error code is returned. As well as [SQLITE_OK] and +** ^Function sqlite3_backup_step(B,N) will copy up to N pages between +** the source and destination databases specified by [sqlite3_backup] object B. +** ^If N is negative, all remaining source pages are copied. +** ^If sqlite3_backup_step(B,N) successfully copies N pages and there +** are still more pages to be copied, then the function resturns [SQLITE_OK]. +** ^If sqlite3_backup_step(B,N) successfully finishes copying all pages +** from source to destination, then it returns [SQLITE_DONE]. +** ^If an error occurs while running sqlite3_backup_step(B,N), +** then an [error code] is returned. ^As well as [SQLITE_OK] and ** [SQLITE_DONE], a call to sqlite3_backup_step() may return [SQLITE_READONLY], ** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an ** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code. ** -** As well as the case where the destination database file was opened for -** read-only access, sqlite3_backup_step() may return [SQLITE_READONLY] if +** ^The sqlite3_backup_step() might return [SQLITE_READONLY] if the destination +** database was opened read-only or if ** the destination is an in-memory database with a different page size ** from the source database. ** -** If sqlite3_backup_step() cannot obtain a required file-system lock, then +** ^If sqlite3_backup_step() cannot obtain a required file-system lock, then ** the [sqlite3_busy_handler | busy-handler function] -** is invoked (if one is specified). If the +** is invoked (if one is specified). ^If the ** busy-handler returns non-zero before the lock is available, then -** [SQLITE_BUSY] is returned to the caller. In this case the call to -** sqlite3_backup_step() can be retried later. If the source +** [SQLITE_BUSY] is returned to the caller. ^In this case the call to +** sqlite3_backup_step() can be retried later. ^If the source ** [database connection] ** is being used to write to the source database when sqlite3_backup_step() -** is called, then [SQLITE_LOCKED] is returned immediately. Again, in this -** case the call to sqlite3_backup_step() can be retried later on. If +** is called, then [SQLITE_LOCKED] is returned immediately. ^Again, in this +** case the call to sqlite3_backup_step() can be retried later on. ^(If ** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX], [SQLITE_NOMEM], or ** [SQLITE_READONLY] is returned, then ** there is no point in retrying the call to sqlite3_backup_step(). These -** errors are considered fatal. At this point the application must accept +** errors are considered fatal.)^ The application must accept ** that the backup operation has failed and pass the backup operation handle ** to the sqlite3_backup_finish() to release associated resources. ** -** Following the first call to sqlite3_backup_step(), an exclusive lock is -** obtained on the destination file. It is not released until either +** ^The first call to sqlite3_backup_step() obtains an exclusive lock +** on the destination file. ^The exclusive lock is not released until either ** sqlite3_backup_finish() is called or the backup operation is complete -** and sqlite3_backup_step() returns [SQLITE_DONE]. Additionally, each time -** a call to sqlite3_backup_step() is made a [shared lock] is obtained on -** the source database file. This lock is released before the -** sqlite3_backup_step() call returns. Because the source database is not -** locked between calls to sqlite3_backup_step(), it may be modified mid-way -** through the backup procedure. If the source database is modified by an +** and sqlite3_backup_step() returns [SQLITE_DONE]. ^Every call to +** sqlite3_backup_step() obtains a [shared lock] on the source database that +** lasts for the duration of the sqlite3_backup_step() call. +** ^Because the source database is not locked between calls to +** sqlite3_backup_step(), the source database may be modified mid-way +** through the backup process. ^If the source database is modified by an ** external process or via a database connection other than the one being -** used by the backup operation, then the backup will be transparently -** restarted by the next call to sqlite3_backup_step(). If the source +** used by the backup operation, then the backup will be automatically +** restarted by the next call to sqlite3_backup_step(). ^If the source ** database is modified by the using the same database connection as is used -** by the backup operation, then the backup database is transparently +** by the backup operation, then the backup database is automatically ** updated at the same time. ** ** sqlite3_backup_finish() ** -** Once sqlite3_backup_step() has returned [SQLITE_DONE], or when the -** application wishes to abandon the backup operation, the [sqlite3_backup] -** object should be passed to sqlite3_backup_finish(). This releases all -** resources associated with the backup operation. If sqlite3_backup_step() -** has not yet returned [SQLITE_DONE], then any active write-transaction on the -** destination database is rolled back. The [sqlite3_backup] object is invalid +** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the +** application wishes to abandon the backup operation, the application +** should destroy the [sqlite3_backup] by passing it to sqlite3_backup_finish(). +** ^The sqlite3_backup_finish() interfaces releases all +** resources associated with the [sqlite3_backup] object. +** ^If sqlite3_backup_step() has not yet returned [SQLITE_DONE], then any +** active write-transaction on the destination database is rolled back. +** The [sqlite3_backup] object is invalid ** and may not be used following a call to sqlite3_backup_finish(). ** -** The value returned by sqlite3_backup_finish is [SQLITE_OK] if no error -** occurred, regardless or whether or not sqlite3_backup_step() was called -** a sufficient number of times to complete the backup operation. Or, if -** an out-of-memory condition or IO error occured during a call to -** sqlite3_backup_step() then [SQLITE_NOMEM] or an -** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] error code -** is returned. In this case the error code and an error message are -** written to the destination [database connection]. +** ^The value returned by sqlite3_backup_finish is [SQLITE_OK] if no +** sqlite3_backup_step() errors occurred, regardless or whether or not +** sqlite3_backup_step() completed. +** ^If an out-of-memory condition or IO error occurred during any prior +** sqlite3_backup_step() call on the same [sqlite3_backup] object, then +** sqlite3_backup_finish() returns the corresponding [error code]. ** -** A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step() is -** not a permanent error and does not affect the return value of +** ^A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step() +** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** ** sqlite3_backup_remaining(), sqlite3_backup_pagecount() ** -** Each call to sqlite3_backup_step() sets two values stored internally -** by an [sqlite3_backup] object. The number of pages still to be backed -** up, which may be queried by sqlite3_backup_remaining(), and the total -** number of pages in the source database file, which may be queried by -** sqlite3_backup_pagecount(). +** ^Each call to sqlite3_backup_step() sets two values inside +** the [sqlite3_backup] object: the number of pages still to be backed +** up and the total number of pages in the source databae file. +** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces +** retrieve these two values, respectively. ** -** The values returned by these functions are only updated by -** sqlite3_backup_step(). If the source database is modified during a backup +** ^The values returned by these functions are only updated by +** sqlite3_backup_step(). ^If the source database is modified during a backup ** operation, then the values are not updated to account for any extra ** pages that need to be updated or the size of the source database file ** changing. ** ** Concurrent Usage of Database Handles ** -** The source [database connection] may be used by the application for other +** ^The source [database connection] may be used by the application for other ** purposes while a backup operation is underway or being initialized. -** If SQLite is compiled and configured to support threadsafe database +** ^If SQLite is compiled and configured to support threadsafe database ** connections, then the source database connection may be used concurrently ** from within other threads. ** -** However, the application must guarantee that the destination database -** connection handle is not passed to any other API (by any thread) after +** However, the application must guarantee that the destination +** [database connection] is not passed to any other API (by any thread) after ** sqlite3_backup_init() is called and before the corresponding call to -** sqlite3_backup_finish(). Unfortunately SQLite does not currently check -** for this, if the application does use the destination [database connection] -** for some other purpose during a backup operation, things may appear to -** work correctly but in fact be subtly malfunctioning. Use of the -** destination database connection while a backup is in progress might -** also cause a mutex deadlock. +** sqlite3_backup_finish(). SQLite does not currently check to see +** if the application incorrectly accesses the destination [database connection] +** and so no error code is reported, but the operations may malfunction +** nevertheless. Use of the destination database connection while a +** backup is in progress might also also cause a mutex deadlock. ** -** Furthermore, if running in [shared cache mode], the application must +** If running in [shared cache mode], the application must ** guarantee that the shared cache used by the destination database ** is not accessed while the backup is running. In practice this means -** that the application must guarantee that the file-system file being +** that the application must guarantee that the disk file being ** backed up to is not accessed by any connection within the process, ** not just the specific connection that was passed to sqlite3_backup_init(). ** @@ -6011,48 +6042,48 @@ ** CAPI3REF: Unlock Notification ** EXPERIMENTAL ** -** When running in shared-cache mode, a database operation may fail with +** ^When running in shared-cache mode, a database operation may fail with ** an [SQLITE_LOCKED] error if the required locks on the shared-cache or ** individual tables within the shared-cache cannot be obtained. See ** [SQLite Shared-Cache Mode] for a description of shared-cache locking. -** This API may be used to register a callback that SQLite will invoke +** ^This API may be used to register a callback that SQLite will invoke ** when the connection currently holding the required lock relinquishes it. -** This API is only available if the library was compiled with the +** ^This API is only available if the library was compiled with the ** [SQLITE_ENABLE_UNLOCK_NOTIFY] C-preprocessor symbol defined. ** ** See Also: [Using the SQLite Unlock Notification Feature]. ** -** Shared-cache locks are released when a database connection concludes +** ^Shared-cache locks are released when a database connection concludes ** its current transaction, either by committing it or rolling it back. ** -** When a connection (known as the blocked connection) fails to obtain a +** ^When a connection (known as the blocked connection) fails to obtain a ** shared-cache lock and SQLITE_LOCKED is returned to the caller, the ** identity of the database connection (the blocking connection) that -** has locked the required resource is stored internally. After an +** has locked the required resource is stored internally. ^After an ** application receives an SQLITE_LOCKED error, it may call the ** sqlite3_unlock_notify() method with the blocked connection handle as ** the first argument to register for a callback that will be invoked -** when the blocking connections current transaction is concluded. The +** when the blocking connections current transaction is concluded. ^The ** callback is invoked from within the [sqlite3_step] or [sqlite3_close] ** call that concludes the blocking connections transaction. ** -** If sqlite3_unlock_notify() is called in a multi-threaded application, +** ^(If sqlite3_unlock_notify() is called in a multi-threaded application, ** there is a chance that the blocking connection will have already ** concluded its transaction by the time sqlite3_unlock_notify() is invoked. ** If this happens, then the specified callback is invoked immediately, -** from within the call to sqlite3_unlock_notify(). +** from within the call to sqlite3_unlock_notify().)^ ** -** If the blocked connection is attempting to obtain a write-lock on a +** ^If the blocked connection is attempting to obtain a write-lock on a ** shared-cache table, and more than one other connection currently holds ** a read-lock on the same table, then SQLite arbitrarily selects one of ** the other connections to use as the blocking connection. ** -** There may be at most one unlock-notify callback registered by a +** ^(There may be at most one unlock-notify callback registered by a ** blocked connection. If sqlite3_unlock_notify() is called when the ** blocked connection already has a registered unlock-notify callback, -** then the new callback replaces the old. If sqlite3_unlock_notify() is +** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing -** unlock-notify callback is cancelled. The blocked connections +** unlock-notify callback is cancelled. ^The blocked connections ** unlock-notify callback may also be canceled by closing the blocked ** connection using [sqlite3_close()]. ** @@ -6060,7 +6091,7 @@ ** any sqlite3_xxx API functions from within an unlock-notify callback, a ** crash or deadlock may be the result. ** -** Unless deadlock is detected (see below), sqlite3_unlock_notify() always +** ^Unless deadlock is detected (see below), sqlite3_unlock_notify() always ** returns SQLITE_OK. ** ** Callback Invocation Details @@ -6074,7 +6105,7 @@ ** ** When a blocking connections transaction is concluded, there may be ** more than one blocked connection that has registered for an unlock-notify -** callback. If two or more such blocked connections have specified the +** callback. ^If two or more such blocked connections have specified the ** same callback function, then instead of invoking the callback function ** multiple times, it is invoked once with the set of void* context pointers ** specified by the blocked connections bundled together into an array. @@ -6092,16 +6123,16 @@ ** will proceed and the system may remain deadlocked indefinitely. ** ** To avoid this scenario, the sqlite3_unlock_notify() performs deadlock -** detection. If a given call to sqlite3_unlock_notify() would put the +** detection. ^If a given call to sqlite3_unlock_notify() would put the ** system in a deadlocked state, then SQLITE_LOCKED is returned and no ** unlock-notify callback is registered. The system is said to be in ** a deadlocked state if connection A has registered for an unlock-notify ** callback on the conclusion of connection B's transaction, and connection ** B has itself registered for an unlock-notify callback when connection -** A's transaction is concluded. Indirect deadlock is also detected, so +** A's transaction is concluded. ^Indirect deadlock is also detected, so ** the system is also considered to be deadlocked if connection B has ** registered for an unlock-notify callback on the conclusion of connection -** C's transaction, where connection C is waiting on connection A. Any +** C's transaction, where connection C is waiting on connection A. ^Any ** number of levels of indirection are allowed. ** ** The "DROP TABLE" Exception @@ -6117,10 +6148,10 @@ ** or "DROP INDEX" query, an infinite loop might be the result. ** ** One way around this problem is to check the extended error code returned -** by an sqlite3_step() call. If there is a blocking connection, then the +** by an sqlite3_step() call. ^(If there is a blocking connection, then the ** extended error code is set to SQLITE_LOCKED_SHAREDCACHE. Otherwise, in ** the special "DROP TABLE/INDEX" case, the extended error code is just -** SQLITE_LOCKED. +** SQLITE_LOCKED.)^ */ SQLITE_API int sqlite3_unlock_notify( sqlite3 *pBlocked, /* Waiting connection */ @@ -6128,6 +6159,18 @@ void *pNotifyArg /* Argument to pass to xNotify */ ); + +/* +** CAPI3REF: String Comparison +** EXPERIMENTAL +** +** ^The [sqlite3_strnicmp()] API allows applications and extensions to +** compare the contents of two buffers containing UTF-8 strings in a +** case-indendent fashion, using the same definition of case independence +** that SQLite uses internally when comparing identifiers. +*/ +SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); + /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. @@ -6141,6 +6184,7 @@ #endif #endif + /************** End of sqlite3.h *********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ /************** Include hash.h in the middle of sqliteInt.h ******************/ @@ -6158,8 +6202,6 @@ ************************************************************************* ** This is the header file for the generic hash-table implemenation ** used in SQLite. -** -** $Id: hash.h,v 1.15 2009/05/02 13:29:38 drh Exp $ */ #ifndef _SQLITE_HASH_H_ #define _SQLITE_HASH_H_ @@ -6276,70 +6318,70 @@ #define TK_ID 26 #define TK_INDEXED 27 #define TK_ABORT 28 -#define TK_AFTER 29 -#define TK_ANALYZE 30 -#define TK_ASC 31 -#define TK_ATTACH 32 -#define TK_BEFORE 33 -#define TK_BY 34 -#define TK_CASCADE 35 -#define TK_CAST 36 -#define TK_COLUMNKW 37 -#define TK_CONFLICT 38 -#define TK_DATABASE 39 -#define TK_DESC 40 -#define TK_DETACH 41 -#define TK_EACH 42 -#define TK_FAIL 43 -#define TK_FOR 44 -#define TK_IGNORE 45 -#define TK_INITIALLY 46 -#define TK_INSTEAD 47 -#define TK_LIKE_KW 48 -#define TK_MATCH 49 -#define TK_KEY 50 -#define TK_OF 51 -#define TK_OFFSET 52 -#define TK_PRAGMA 53 -#define TK_RAISE 54 -#define TK_REPLACE 55 -#define TK_RESTRICT 56 -#define TK_ROW 57 -#define TK_TRIGGER 58 -#define TK_VACUUM 59 -#define TK_VIEW 60 -#define TK_VIRTUAL 61 -#define TK_REINDEX 62 -#define TK_RENAME 63 -#define TK_CTIME_KW 64 -#define TK_ANY 65 -#define TK_OR 66 -#define TK_AND 67 -#define TK_IS 68 -#define TK_BETWEEN 69 -#define TK_IN 70 -#define TK_ISNULL 71 -#define TK_NOTNULL 72 -#define TK_NE 73 -#define TK_EQ 74 -#define TK_GT 75 -#define TK_LE 76 -#define TK_LT 77 -#define TK_GE 78 -#define TK_ESCAPE 79 -#define TK_BITAND 80 -#define TK_BITOR 81 -#define TK_LSHIFT 82 -#define TK_RSHIFT 83 -#define TK_PLUS 84 -#define TK_MINUS 85 -#define TK_STAR 86 -#define TK_SLASH 87 -#define TK_REM 88 -#define TK_CONCAT 89 -#define TK_COLLATE 90 -#define TK_UMINUS 91 -#define TK_UPLUS 92 +#define TK_ACTION 29 +#define TK_AFTER 30 +#define TK_ANALYZE 31 +#define TK_ASC 32 +#define TK_ATTACH 33 +#define TK_BEFORE 34 +#define TK_BY 35 +#define TK_CASCADE 36 +#define TK_CAST 37 +#define TK_COLUMNKW 38 +#define TK_CONFLICT 39 +#define TK_DATABASE 40 +#define TK_DESC 41 +#define TK_DETACH 42 +#define TK_EACH 43 +#define TK_FAIL 44 +#define TK_FOR 45 +#define TK_IGNORE 46 +#define TK_INITIALLY 47 +#define TK_INSTEAD 48 +#define TK_LIKE_KW 49 +#define TK_MATCH 50 +#define TK_NO 51 +#define TK_KEY 52 +#define TK_OF 53 +#define TK_OFFSET 54 +#define TK_PRAGMA 55 +#define TK_RAISE 56 +#define TK_REPLACE 57 +#define TK_RESTRICT 58 +#define TK_ROW 59 +#define TK_TRIGGER 60 +#define TK_VACUUM 61 +#define TK_VIEW 62 +#define TK_VIRTUAL 63 +#define TK_REINDEX 64 +#define TK_RENAME 65 +#define TK_CTIME_KW 66 +#define TK_ANY 67 +#define TK_OR 68 +#define TK_AND 69 +#define TK_IS 70 +#define TK_BETWEEN 71 +#define TK_IN 72 +#define TK_ISNULL 73 +#define TK_NOTNULL 74 +#define TK_NE 75 +#define TK_EQ 76 +#define TK_GT 77 +#define TK_LE 78 +#define TK_LT 79 +#define TK_GE 80 +#define TK_ESCAPE 81 +#define TK_BITAND 82 +#define TK_BITOR 83 +#define TK_LSHIFT 84 +#define TK_RSHIFT 85 +#define TK_PLUS 86 +#define TK_MINUS 87 +#define TK_STAR 88 +#define TK_SLASH 89 +#define TK_REM 90 +#define TK_CONCAT 91 +#define TK_COLLATE 92 #define TK_BITNOT 93 #define TK_STRING 94 #define TK_JOIN_KW 95 @@ -6352,9 +6394,9 @@ #define TK_REFERENCES 102 #define TK_AUTOINCR 103 #define TK_ON 104 -#define TK_DELETE 105 -#define TK_UPDATE 106 -#define TK_INSERT 107 +#define TK_INSERT 105 +#define TK_DELETE 106 +#define TK_UPDATE 107 #define TK_SET 108 #define TK_DEFERRABLE 109 #define TK_FOREIGN 110 @@ -6393,15 +6435,18 @@ #define TK_TO_NUMERIC 143 #define TK_TO_INT 144 #define TK_TO_REAL 145 -#define TK_END_OF_FILE 146 -#define TK_ILLEGAL 147 -#define TK_SPACE 148 -#define TK_UNCLOSED_STRING 149 -#define TK_FUNCTION 150 -#define TK_COLUMN 151 -#define TK_AGG_FUNCTION 152 -#define TK_AGG_COLUMN 153 -#define TK_CONST_FUNC 154 +#define TK_ISNOT 146 +#define TK_END_OF_FILE 147 +#define TK_ILLEGAL 148 +#define TK_SPACE 149 +#define TK_UNCLOSED_STRING 150 +#define TK_FUNCTION 151 +#define TK_COLUMN 152 +#define TK_AGG_FUNCTION 153 +#define TK_AGG_COLUMN 154 +#define TK_CONST_FUNC 155 +#define TK_UMINUS 156 +#define TK_UPLUS 157 /************** End of parse.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ @@ -6419,7 +6464,7 @@ # define double sqlite_int64 # define LONGDOUBLE_TYPE sqlite_int64 # ifndef SQLITE_BIG_DBL -# define SQLITE_BIG_DBL (((sqlite3_int64)1)<<60) +# define SQLITE_BIG_DBL (((sqlite3_int64)1)<<50) # endif # define SQLITE_OMIT_DATETIME_FUNCS 1 # define SQLITE_OMIT_TRACE 1 @@ -6466,6 +6511,10 @@ # define SQLITE_DEFAULT_FILE_FORMAT 1 #endif +#ifndef SQLITE_DEFAULT_RECURSIVE_TRIGGERS +# define SQLITE_DEFAULT_RECURSIVE_TRIGGERS 0 +#endif + /* ** Provide a default value for SQLITE_TEMP_STORE in case it is not specified ** on the command-line @@ -6593,9 +6642,19 @@ #define ROUNDDOWN8(x) ((x)&~7) /* -** Assert that the pointer X is aligned to an 8-byte boundary. +** Assert that the pointer X is aligned to an 8-byte boundary. This +** macro is used only within assert() to verify that the code gets +** all alignment restrictions correct. +** +** Except, if SQLITE_4_BYTE_ALIGNED_MALLOC is defined, then the +** underlying malloc() implemention might return us 4-byte aligned +** pointers. In that case, only verify 4-byte alignment. */ -#define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&7)==0) +#ifdef SQLITE_4_BYTE_ALIGNED_MALLOC +# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&3)==0) +#else +# define EIGHT_BYTE_ALIGNMENT(X) ((((char*)(X) - (char*)0)&7)==0) +#endif /* @@ -6709,6 +6768,7 @@ typedef struct FuncDefHash FuncDefHash; typedef struct IdList IdList; typedef struct Index Index; +typedef struct IndexSample IndexSample; typedef struct KeyClass KeyClass; typedef struct KeyInfo KeyInfo; typedef struct Lookaside Lookaside; @@ -6723,10 +6783,11 @@ typedef struct Table Table; typedef struct TableLock TableLock; typedef struct Token Token; -typedef struct TriggerStack TriggerStack; +typedef struct TriggerPrg TriggerPrg; typedef struct TriggerStep TriggerStep; typedef struct Trigger Trigger; typedef struct UnpackedRecord UnpackedRecord; +typedef struct VTable VTable; typedef struct Walker Walker; typedef struct WherePlan WherePlan; typedef struct WhereInfo WhereInfo; @@ -6753,8 +6814,6 @@ ** This header file defines the interface that the sqlite B-Tree file ** subsystem. See comments in the source code for a detailed description ** of what each interface routine does. -** -** @(#) $Id: btree.h,v 1.116 2009/06/03 11:25:07 danielk1977 Exp $ */ #ifndef _BTREE_H_ #define _BTREE_H_ @@ -6838,8 +6897,8 @@ SQLITE_PRIVATE int sqlite3BtreeIsInReadTrans(Btree*); SQLITE_PRIVATE int sqlite3BtreeIsInBackup(Btree*); SQLITE_PRIVATE void *sqlite3BtreeSchema(Btree *, int, void(*)(void *)); -SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *); -SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *, int, u8); +SQLITE_PRIVATE int sqlite3BtreeSchemaLocked(Btree *pBtree); +SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *pBtree, int iTab, u8 isWriteLock); SQLITE_PRIVATE int sqlite3BtreeSavepoint(Btree *, int, int); SQLITE_PRIVATE const char *sqlite3BtreeGetFilename(Btree *); @@ -6859,7 +6918,7 @@ SQLITE_PRIVATE int sqlite3BtreeClearTable(Btree*, int, int*); SQLITE_PRIVATE void sqlite3BtreeTripAllCursors(Btree*, int); -SQLITE_PRIVATE int sqlite3BtreeGetMeta(Btree*, int idx, u32 *pValue); +SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *pBtree, int idx, u32 *pValue); SQLITE_PRIVATE int sqlite3BtreeUpdateMeta(Btree*, int idx, u32 value); /* @@ -6891,15 +6950,9 @@ BtCursor *pCursor /* Space to write cursor structure */ ); SQLITE_PRIVATE int sqlite3BtreeCursorSize(void); +SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor*); SQLITE_PRIVATE int sqlite3BtreeCloseCursor(BtCursor*); -SQLITE_PRIVATE int sqlite3BtreeMoveto( - BtCursor*, - const void *pKey, - i64 nKey, - int bias, - int *pRes -); SQLITE_PRIVATE int sqlite3BtreeMovetoUnpacked( BtCursor*, UnpackedRecord *pUnKey, @@ -6916,7 +6969,6 @@ SQLITE_PRIVATE int sqlite3BtreeLast(BtCursor*, int *pRes); SQLITE_PRIVATE int sqlite3BtreeNext(BtCursor*, int *pRes); SQLITE_PRIVATE int sqlite3BtreeEof(BtCursor*); -SQLITE_PRIVATE int sqlite3BtreeFlags(BtCursor*); SQLITE_PRIVATE int sqlite3BtreePrevious(BtCursor*, int *pRes); SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor*, i64 *pSize); SQLITE_PRIVATE int sqlite3BtreeKey(BtCursor*, u32 offset, u32 amt, void*); @@ -6934,6 +6986,10 @@ SQLITE_PRIVATE void sqlite3BtreeCacheOverflow(BtCursor *); SQLITE_PRIVATE void sqlite3BtreeClearCursor(BtCursor *); +#ifndef NDEBUG +SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor*); +#endif + #ifndef SQLITE_OMIT_BTREECOUNT SQLITE_PRIVATE int sqlite3BtreeCount(BtCursor *, i64 *); #endif @@ -7006,8 +7062,6 @@ ** This header defines the interface to the virtual database engine ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. -** -** $Id: vdbe.h,v 1.141 2009/04/10 00:56:29 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -7025,6 +7079,7 @@ */ typedef struct VdbeFunc VdbeFunc; typedef struct Mem Mem; +typedef struct SubProgram SubProgram; /* ** A single instruction of the virtual machine has an opcode @@ -7034,12 +7089,12 @@ struct VdbeOp { u8 opcode; /* What operation to perform */ signed char p4type; /* One of the P4_xxx constants for p4 */ - u8 opflags; /* Not currently used */ + u8 opflags; /* Mask of the OPFLG_* flags in opcodes.h */ u8 p5; /* Fifth parameter is an unsigned character */ int p1; /* First operand */ int p2; /* Second parameter (often the jump destination) */ int p3; /* The third parameter */ - union { /* forth parameter */ + union { /* fourth parameter */ int i; /* Integer value if p4type==P4_INT32 */ void *p; /* Generic pointer */ char *z; /* Pointer to data for string (char array) types */ @@ -7049,9 +7104,10 @@ VdbeFunc *pVdbeFunc; /* Used when p4type is P4_VDBEFUNC */ CollSeq *pColl; /* Used when p4type is P4_COLLSEQ */ Mem *pMem; /* Used when p4type is P4_MEM */ - sqlite3_vtab *pVtab; /* Used when p4type is P4_VTAB */ + VTable *pVtab; /* Used when p4type is P4_VTAB */ KeyInfo *pKeyInfo; /* Used when p4type is P4_KEYINFO */ int *ai; /* Used when p4type is P4_INTARRAY */ + SubProgram *pProgram; /* Used when p4type is P4_SUBPROGRAM */ } p4; #ifdef SQLITE_DEBUG char *zComment; /* Comment to improve readability */ @@ -7063,6 +7119,19 @@ }; typedef struct VdbeOp VdbeOp; + +/* +** A sub-routine used to implement a trigger program. +*/ +struct SubProgram { + VdbeOp *aOp; /* Array of opcodes for sub-program */ + int nOp; /* Elements in aOp[] */ + int nMem; /* Number of memory cells required */ + int nCsr; /* Number of cursors required */ + int nRef; /* Number of pointers to this structure */ + void *token; /* id that may be used to recursive triggers */ +}; + /* ** A smaller version of VdbeOp used for the VdbeAddOpList() function because ** it takes up less space. @@ -7076,7 +7145,7 @@ typedef struct VdbeOpList VdbeOpList; /* -** Allowed values of VdbeOp.p3type +** Allowed values of VdbeOp.p4type */ #define P4_NOTUSED 0 /* The P4 parameter is not used */ #define P4_DYNAMIC (-1) /* Pointer to a string obtained from sqliteMalloc() */ @@ -7093,6 +7162,7 @@ #define P4_INT64 (-13) /* P4 is a 64-bit signed integer */ #define P4_INT32 (-14) /* P4 is a 32-bit signed integer */ #define P4_INTARRAY (-15) /* P4 is a vector of 32-bit integers */ +#define P4_SUBPROGRAM (-18) /* P4 is a pointer to a SubProgram structure */ /* When adding a P4 argument using P4_KEYINFO, a copy of the KeyInfo structure ** is made. That copy is freed when the Vdbe is finalized. But if the @@ -7139,147 +7209,147 @@ /************** Begin file opcodes.h *****************************************/ /* Automatically generated. Do not edit */ /* See the mkopcodeh.awk script for details */ -#define OP_VNext 1 -#define OP_Affinity 2 -#define OP_Column 3 -#define OP_SetCookie 4 -#define OP_Seek 5 +#define OP_Goto 1 +#define OP_Gosub 2 +#define OP_Return 3 +#define OP_Yield 4 +#define OP_HaltIfNull 5 +#define OP_Halt 6 +#define OP_Integer 7 +#define OP_Int64 8 #define OP_Real 130 /* same as TK_FLOAT */ -#define OP_Sequence 6 -#define OP_Savepoint 7 -#define OP_Ge 78 /* same as TK_GE */ -#define OP_RowKey 8 -#define OP_SCopy 9 -#define OP_Eq 74 /* same as TK_EQ */ -#define OP_OpenWrite 10 -#define OP_NotNull 72 /* same as TK_NOTNULL */ -#define OP_If 11 -#define OP_ToInt 144 /* same as TK_TO_INT */ #define OP_String8 94 /* same as TK_STRING */ -#define OP_CollSeq 12 -#define OP_OpenRead 13 -#define OP_Expire 14 -#define OP_AutoCommit 15 -#define OP_Gt 75 /* same as TK_GT */ -#define OP_Pagecount 16 -#define OP_IntegrityCk 17 -#define OP_Sort 18 -#define OP_Copy 20 -#define OP_Trace 21 -#define OP_Function 22 -#define OP_IfNeg 23 -#define OP_And 67 /* same as TK_AND */ -#define OP_Subtract 85 /* same as TK_MINUS */ -#define OP_Noop 24 -#define OP_Return 25 -#define OP_Remainder 88 /* same as TK_REM */ -#define OP_NewRowid 26 -#define OP_Multiply 86 /* same as TK_STAR */ -#define OP_Variable 27 -#define OP_String 28 -#define OP_RealAffinity 29 -#define OP_VRename 30 -#define OP_ParseSchema 31 -#define OP_VOpen 32 -#define OP_Close 33 -#define OP_CreateIndex 34 -#define OP_IsUnique 35 -#define OP_NotFound 36 -#define OP_Int64 37 -#define OP_MustBeInt 38 -#define OP_Halt 39 -#define OP_Rowid 40 -#define OP_IdxLT 41 -#define OP_AddImm 42 -#define OP_Statement 43 -#define OP_RowData 44 -#define OP_MemMax 45 -#define OP_Or 66 /* same as TK_OR */ -#define OP_NotExists 46 -#define OP_Gosub 47 -#define OP_Divide 87 /* same as TK_SLASH */ -#define OP_Integer 48 -#define OP_ToNumeric 143 /* same as TK_TO_NUMERIC*/ -#define OP_Prev 49 -#define OP_RowSetRead 50 -#define OP_Concat 89 /* same as TK_CONCAT */ -#define OP_RowSetAdd 51 -#define OP_BitAnd 80 /* same as TK_BITAND */ -#define OP_VColumn 52 -#define OP_CreateTable 53 -#define OP_Last 54 -#define OP_SeekLe 55 -#define OP_IsNull 71 /* same as TK_ISNULL */ -#define OP_IncrVacuum 56 -#define OP_IdxRowid 57 -#define OP_ShiftRight 83 /* same as TK_RSHIFT */ -#define OP_ResetCount 58 -#define OP_ContextPush 59 -#define OP_Yield 60 -#define OP_DropTrigger 61 -#define OP_DropIndex 62 -#define OP_IdxGE 63 -#define OP_IdxDelete 64 -#define OP_Vacuum 65 -#define OP_IfNot 68 -#define OP_DropTable 69 -#define OP_SeekLt 70 -#define OP_MakeRecord 79 -#define OP_ToBlob 142 /* same as TK_TO_BLOB */ -#define OP_ResultRow 90 -#define OP_Delete 91 -#define OP_AggFinal 92 -#define OP_Compare 95 -#define OP_ShiftLeft 82 /* same as TK_LSHIFT */ -#define OP_Goto 96 -#define OP_TableLock 97 -#define OP_Clear 98 -#define OP_Le 76 /* same as TK_LE */ -#define OP_VerifyCookie 99 -#define OP_AggStep 100 +#define OP_String 9 +#define OP_Null 10 +#define OP_Blob 11 +#define OP_Variable 12 +#define OP_Move 13 +#define OP_Copy 14 +#define OP_SCopy 15 +#define OP_ResultRow 16 +#define OP_Concat 91 /* same as TK_CONCAT */ +#define OP_Add 86 /* same as TK_PLUS */ +#define OP_Subtract 87 /* same as TK_MINUS */ +#define OP_Multiply 88 /* same as TK_STAR */ +#define OP_Divide 89 /* same as TK_SLASH */ +#define OP_Remainder 90 /* same as TK_REM */ +#define OP_CollSeq 17 +#define OP_Function 18 +#define OP_BitAnd 82 /* same as TK_BITAND */ +#define OP_BitOr 83 /* same as TK_BITOR */ +#define OP_ShiftLeft 84 /* same as TK_LSHIFT */ +#define OP_ShiftRight 85 /* same as TK_RSHIFT */ +#define OP_AddImm 20 +#define OP_MustBeInt 21 +#define OP_RealAffinity 22 #define OP_ToText 141 /* same as TK_TO_TEXT */ -#define OP_Not 19 /* same as TK_NOT */ +#define OP_ToBlob 142 /* same as TK_TO_BLOB */ +#define OP_ToNumeric 143 /* same as TK_TO_NUMERIC*/ +#define OP_ToInt 144 /* same as TK_TO_INT */ #define OP_ToReal 145 /* same as TK_TO_REAL */ -#define OP_SetNumColumns 101 -#define OP_Transaction 102 -#define OP_VFilter 103 -#define OP_Ne 73 /* same as TK_NE */ -#define OP_VDestroy 104 -#define OP_ContextPop 105 -#define OP_BitOr 81 /* same as TK_BITOR */ -#define OP_Next 106 -#define OP_Count 107 -#define OP_IdxInsert 108 -#define OP_Lt 77 /* same as TK_LT */ -#define OP_SeekGe 109 -#define OP_Insert 110 -#define OP_Destroy 111 -#define OP_ReadCookie 112 -#define OP_RowSetTest 113 -#define OP_LoadAnalysis 114 -#define OP_Explain 115 -#define OP_HaltIfNull 116 -#define OP_OpenPseudo 117 -#define OP_OpenEphemeral 118 -#define OP_Null 119 -#define OP_Move 120 -#define OP_Blob 121 -#define OP_Add 84 /* same as TK_PLUS */ -#define OP_Rewind 122 -#define OP_SeekGt 123 -#define OP_VBegin 124 -#define OP_VUpdate 125 -#define OP_IfZero 126 +#define OP_Eq 76 /* same as TK_EQ */ +#define OP_Ne 75 /* same as TK_NE */ +#define OP_Lt 79 /* same as TK_LT */ +#define OP_Le 78 /* same as TK_LE */ +#define OP_Gt 77 /* same as TK_GT */ +#define OP_Ge 80 /* same as TK_GE */ +#define OP_Permutation 23 +#define OP_Compare 24 +#define OP_Jump 25 +#define OP_And 69 /* same as TK_AND */ +#define OP_Or 68 /* same as TK_OR */ +#define OP_Not 19 /* same as TK_NOT */ #define OP_BitNot 93 /* same as TK_BITNOT */ -#define OP_VCreate 127 -#define OP_Found 128 -#define OP_IfPos 129 -#define OP_NullRow 131 -#define OP_Jump 132 -#define OP_Permutation 133 +#define OP_If 26 +#define OP_IfNot 27 +#define OP_IsNull 73 /* same as TK_ISNULL */ +#define OP_NotNull 74 /* same as TK_NOTNULL */ +#define OP_Column 28 +#define OP_Affinity 29 +#define OP_MakeRecord 30 +#define OP_Count 31 +#define OP_Savepoint 32 +#define OP_AutoCommit 33 +#define OP_Transaction 34 +#define OP_ReadCookie 35 +#define OP_SetCookie 36 +#define OP_VerifyCookie 37 +#define OP_OpenRead 38 +#define OP_OpenWrite 39 +#define OP_OpenEphemeral 40 +#define OP_OpenPseudo 41 +#define OP_Close 42 +#define OP_SeekLt 43 +#define OP_SeekLe 44 +#define OP_SeekGe 45 +#define OP_SeekGt 46 +#define OP_Seek 47 +#define OP_NotFound 48 +#define OP_Found 49 +#define OP_IsUnique 50 +#define OP_NotExists 51 +#define OP_Sequence 52 +#define OP_NewRowid 53 +#define OP_Insert 54 +#define OP_InsertInt 55 +#define OP_Delete 56 +#define OP_ResetCount 57 +#define OP_RowKey 58 +#define OP_RowData 59 +#define OP_Rowid 60 +#define OP_NullRow 61 +#define OP_Last 62 +#define OP_Sort 63 +#define OP_Rewind 64 +#define OP_Prev 65 +#define OP_Next 66 +#define OP_IdxInsert 67 +#define OP_IdxDelete 70 +#define OP_IdxRowid 71 +#define OP_IdxLT 72 +#define OP_IdxGE 81 +#define OP_Destroy 92 +#define OP_Clear 95 +#define OP_CreateIndex 96 +#define OP_CreateTable 97 +#define OP_ParseSchema 98 +#define OP_LoadAnalysis 99 +#define OP_DropTable 100 +#define OP_DropIndex 101 +#define OP_DropTrigger 102 +#define OP_IntegrityCk 103 +#define OP_RowSetAdd 104 +#define OP_RowSetRead 105 +#define OP_RowSetTest 106 +#define OP_Program 107 +#define OP_Param 108 +#define OP_FkCounter 109 +#define OP_FkIfZero 110 +#define OP_MemMax 111 +#define OP_IfPos 112 +#define OP_IfNeg 113 +#define OP_IfZero 114 +#define OP_AggStep 115 +#define OP_AggFinal 116 +#define OP_Vacuum 117 +#define OP_IncrVacuum 118 +#define OP_Expire 119 +#define OP_TableLock 120 +#define OP_VBegin 121 +#define OP_VCreate 122 +#define OP_VDestroy 123 +#define OP_VOpen 124 +#define OP_VFilter 125 +#define OP_VColumn 126 +#define OP_VNext 127 +#define OP_VRename 128 +#define OP_VUpdate 129 +#define OP_Pagecount 131 +#define OP_Trace 132 +#define OP_Noop 133 +#define OP_Explain 134 /* The following opcode values are never used */ -#define OP_NotUsed_134 134 #define OP_NotUsed_135 135 #define OP_NotUsed_136 136 #define OP_NotUsed_137 137 @@ -7297,25 +7367,26 @@ #define OPFLG_IN1 0x0004 /* in1: P1 is an input */ #define OPFLG_IN2 0x0008 /* in2: P2 is an input */ #define OPFLG_IN3 0x0010 /* in3: P3 is an input */ -#define OPFLG_OUT3 0x0020 /* out3: P3 is an output */ +#define OPFLG_OUT2 0x0020 /* out2: P2 is an output */ +#define OPFLG_OUT3 0x0040 /* out3: P3 is an output */ #define OPFLG_INITIALIZER {\ -/* 0 */ 0x00, 0x01, 0x00, 0x00, 0x10, 0x08, 0x02, 0x00,\ -/* 8 */ 0x00, 0x04, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00,\ -/* 16 */ 0x02, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x05,\ -/* 24 */ 0x00, 0x04, 0x02, 0x00, 0x02, 0x04, 0x00, 0x00,\ -/* 32 */ 0x00, 0x00, 0x02, 0x11, 0x11, 0x02, 0x05, 0x00,\ -/* 40 */ 0x02, 0x11, 0x04, 0x00, 0x00, 0x0c, 0x11, 0x01,\ -/* 48 */ 0x02, 0x01, 0x21, 0x08, 0x00, 0x02, 0x01, 0x11,\ -/* 56 */ 0x01, 0x02, 0x00, 0x00, 0x04, 0x00, 0x00, 0x11,\ -/* 64 */ 0x00, 0x00, 0x2c, 0x2c, 0x05, 0x00, 0x11, 0x05,\ -/* 72 */ 0x05, 0x15, 0x15, 0x15, 0x15, 0x15, 0x15, 0x00,\ -/* 80 */ 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c, 0x2c,\ -/* 88 */ 0x2c, 0x2c, 0x00, 0x00, 0x00, 0x04, 0x02, 0x00,\ -/* 96 */ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,\ -/* 104 */ 0x00, 0x00, 0x01, 0x02, 0x08, 0x11, 0x00, 0x02,\ -/* 112 */ 0x02, 0x15, 0x00, 0x00, 0x10, 0x00, 0x00, 0x02,\ -/* 120 */ 0x00, 0x02, 0x01, 0x11, 0x00, 0x00, 0x05, 0x00,\ -/* 128 */ 0x11, 0x05, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00,\ +/* 0 */ 0x00, 0x01, 0x05, 0x04, 0x04, 0x10, 0x00, 0x02,\ +/* 8 */ 0x02, 0x02, 0x02, 0x02, 0x00, 0x00, 0x24, 0x24,\ +/* 16 */ 0x00, 0x00, 0x00, 0x24, 0x04, 0x05, 0x04, 0x00,\ +/* 24 */ 0x00, 0x01, 0x05, 0x05, 0x00, 0x00, 0x00, 0x02,\ +/* 32 */ 0x00, 0x00, 0x00, 0x02, 0x10, 0x00, 0x00, 0x00,\ +/* 40 */ 0x00, 0x00, 0x00, 0x11, 0x11, 0x11, 0x11, 0x08,\ +/* 48 */ 0x11, 0x11, 0x11, 0x11, 0x02, 0x02, 0x00, 0x00,\ +/* 56 */ 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x01,\ +/* 64 */ 0x01, 0x01, 0x01, 0x08, 0x4c, 0x4c, 0x00, 0x02,\ +/* 72 */ 0x01, 0x05, 0x05, 0x15, 0x15, 0x15, 0x15, 0x15,\ +/* 80 */ 0x15, 0x01, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c, 0x4c,\ +/* 88 */ 0x4c, 0x4c, 0x4c, 0x4c, 0x02, 0x24, 0x02, 0x00,\ +/* 96 */ 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\ +/* 104 */ 0x0c, 0x45, 0x15, 0x01, 0x02, 0x00, 0x01, 0x08,\ +/* 112 */ 0x05, 0x05, 0x05, 0x00, 0x00, 0x00, 0x01, 0x00,\ +/* 120 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01,\ +/* 128 */ 0x00, 0x00, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00,\ /* 136 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x04, 0x04,\ /* 144 */ 0x04, 0x04,} @@ -7332,6 +7403,7 @@ SQLITE_PRIVATE int sqlite3VdbeAddOp2(Vdbe*,int,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOp3(Vdbe*,int,int,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOp4(Vdbe*,int,int,int,int,const char *zP4,int); +SQLITE_PRIVATE int sqlite3VdbeAddOp4Int(Vdbe*,int,int,int,int,int); SQLITE_PRIVATE int sqlite3VdbeAddOpList(Vdbe*, int nOp, VdbeOpList const *aOp); SQLITE_PRIVATE void sqlite3VdbeChangeP1(Vdbe*, int addr, int P1); SQLITE_PRIVATE void sqlite3VdbeChangeP2(Vdbe*, int addr, int P2); @@ -7344,11 +7416,12 @@ SQLITE_PRIVATE VdbeOp *sqlite3VdbeGetOp(Vdbe*, int); SQLITE_PRIVATE int sqlite3VdbeMakeLabel(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe*); -SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int); +SQLITE_PRIVATE void sqlite3VdbeMakeReady(Vdbe*,int,int,int,int,int,int); SQLITE_PRIVATE int sqlite3VdbeFinalize(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeResolveLabel(Vdbe*, int); SQLITE_PRIVATE int sqlite3VdbeCurrentAddr(Vdbe*); #ifdef SQLITE_DEBUG +SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *, int); SQLITE_PRIVATE void sqlite3VdbeTrace(Vdbe*,FILE*); #endif SQLITE_PRIVATE void sqlite3VdbeResetStepResult(Vdbe*); @@ -7359,10 +7432,14 @@ SQLITE_PRIVATE sqlite3 *sqlite3VdbeDb(Vdbe*); SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe*, const char *z, int n, int); SQLITE_PRIVATE void sqlite3VdbeSwap(Vdbe*,Vdbe*); - -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT -SQLITE_PRIVATE int sqlite3VdbeReleaseMemory(int); +SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe*, int*, int*); +SQLITE_PRIVATE void sqlite3VdbeProgramDelete(sqlite3 *, SubProgram *, int); +SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetValue(Vdbe*, int, u8); +SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe*, int); +#ifndef SQLITE_OMIT_TRACE +SQLITE_PRIVATE char *sqlite3VdbeExpandSql(Vdbe*, const char*); #endif + SQLITE_PRIVATE UnpackedRecord *sqlite3VdbeRecordUnpack(KeyInfo*,int,const void*,char*,int); SQLITE_PRIVATE void sqlite3VdbeDeleteUnpackedRecord(UnpackedRecord*); SQLITE_PRIVATE int sqlite3VdbeRecordCompare(int,const void*,UnpackedRecord*); @@ -7398,8 +7475,6 @@ ** This header file defines the interface that the sqlite page cache ** subsystem. The page cache subsystem reads and writes a file a page ** at a time and provides a journal for rollback. -** -** @(#) $Id: pager.h,v 1.102 2009/06/18 17:22:39 drh Exp $ */ #ifndef _PAGER_H_ @@ -7472,13 +7547,20 @@ */ /* Open and close a Pager connection. */ -SQLITE_PRIVATE int sqlite3PagerOpen(sqlite3_vfs *, Pager **ppPager, const char*, int,int,int); +SQLITE_PRIVATE int sqlite3PagerOpen( + sqlite3_vfs*, + Pager **ppPager, + const char*, + int, + int, + int, + void(*)(DbPage*) +); SQLITE_PRIVATE int sqlite3PagerClose(Pager *pPager); SQLITE_PRIVATE int sqlite3PagerReadFileheader(Pager*, int, unsigned char*); /* Functions used to configure a Pager object. */ SQLITE_PRIVATE void sqlite3PagerSetBusyhandler(Pager*, int(*)(void *), void *); -SQLITE_PRIVATE void sqlite3PagerSetReiniter(Pager*, void(*)(DbPage*)); SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager*, u16*, int); SQLITE_PRIVATE int sqlite3PagerMaxPageCount(Pager*, int); SQLITE_PRIVATE void sqlite3PagerSetCachesize(Pager*, int); @@ -7512,6 +7594,7 @@ SQLITE_PRIVATE int sqlite3PagerRollback(Pager*); SQLITE_PRIVATE int sqlite3PagerOpenSavepoint(Pager *pPager, int n); SQLITE_PRIVATE int sqlite3PagerSavepoint(Pager *pPager, int op, int iSavepoint); +SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager); /* Functions used to query pager state and configuration. */ SQLITE_PRIVATE u8 sqlite3PagerIsreadonly(Pager*); @@ -7561,8 +7644,6 @@ ************************************************************************* ** This header file defines the interface that the sqlite page cache ** subsystem. -** -** @(#) $Id: pcache.h,v 1.19 2009/01/20 17:06:27 danielk1977 Exp $ */ #ifndef _PCACHE_H_ @@ -7674,7 +7755,7 @@ /* Return the total number of pages stored in the cache */ SQLITE_PRIVATE int sqlite3PcachePagecount(PCache*); -#ifdef SQLITE_CHECK_PAGES +#if defined(SQLITE_CHECK_PAGES) || defined(SQLITE_DEBUG) /* Iterate through all dirty pages currently stored in the cache. This ** interface is only available if SQLITE_CHECK_PAGES is defined when the ** library is built. @@ -7729,8 +7810,6 @@ ** ** This header file is #include-ed by sqliteInt.h and thus ends up ** being included by every source file. -** -** $Id: os.h,v 1.108 2009/02/05 16:31:46 drh Exp $ */ #ifndef _SQLITE_OS_H_ #define _SQLITE_OS_H_ @@ -7937,6 +8016,11 @@ #define SHARED_FIRST (PENDING_BYTE+2) #define SHARED_SIZE 510 +/* +** Wrapper around OS specific sqlite3_os_init() function. +*/ +SQLITE_PRIVATE int sqlite3OsInit(void); + /* ** Functions for accessing sqlite3_file methods */ @@ -8004,8 +8088,6 @@ ** NOTE: source files should *not* #include this header file directly. ** Source files should #include the sqliteInt.h file and let that file ** include this one indirectly. -** -** $Id: mutex.h,v 1.9 2008/10/07 15:25:48 drh Exp $ */ @@ -8056,7 +8138,7 @@ #define sqlite3MutexAlloc(X) ((sqlite3_mutex*)8) #define sqlite3MutexInit() SQLITE_OK #define sqlite3MutexEnd() -#endif /* defined(SQLITE_OMIT_MUTEX) */ +#endif /* defined(SQLITE_MUTEX_OMIT) */ /************** End of mutex.h ***********************************************/ /************** Continuing where we left off in sqliteInt.h ******************/ @@ -8093,6 +8175,7 @@ Hash tblHash; /* All tables indexed by name */ Hash idxHash; /* All (named) indices indexed by name */ Hash trigHash; /* All triggers indexed by name */ + Hash fkeyHash; /* All foreign keys by referenced table name */ Table *pSeqTab; /* The sqlite_sequence table used by AUTOINCREMENT */ u8 file_format; /* Schema format version for this file */ u8 enc; /* Text encoding used by this database */ @@ -8130,7 +8213,7 @@ ** The number of different kinds of things that can be limited ** using the sqlite3_limit() interface. */ -#define SQLITE_N_LIMIT (SQLITE_LIMIT_VARIABLE_NUMBER+1) +#define SQLITE_N_LIMIT (SQLITE_LIMIT_TRIGGER_DEPTH+1) /* ** Lookaside malloc is a set of fixed-size buffers that can be used @@ -8229,6 +8312,7 @@ int iDb; /* When back is being initialized */ int newTnum; /* Rootpage of table being initialized */ u8 busy; /* TRUE if currently initializing */ + u8 orphanTrigger; /* Last statement is orphaned TEMP trigger */ } init; int nExtension; /* Number of loaded extensions */ void **aExtension; /* Array of shared library handles */ @@ -8269,8 +8353,9 @@ #ifndef SQLITE_OMIT_VIRTUALTABLE Hash aModule; /* populated by sqlite3_create_module() */ Table *pVTab; /* vtab with active Connect/Create method */ - sqlite3_vtab **aVTrans; /* Virtual tables with open transactions */ + VTable **aVTrans; /* Virtual tables with open transactions */ int nVTrans; /* Allocated size of aVTrans */ + VTable *pDisconnect; /* Disconnect these in next sqlite3_prepare() */ #endif FuncDefHash aFunc; /* Hash table of connection functions */ Hash aCollSeq; /* All collating sequences */ @@ -8281,6 +8366,7 @@ int nSavepoint; /* Number of non-transaction savepoints */ int nStatement; /* Number of nested statement-transactions */ u8 isTransactionSavepoint; /* True if the outermost savepoint is a TS */ + i64 nDeferredCons; /* Net deferred constraints this transaction. */ #ifdef SQLITE_ENABLE_UNLOCK_NOTIFY /* The following variables are all protected by the STATIC_MASTER @@ -8307,37 +8393,43 @@ #define ENC(db) ((db)->aDb[0].pSchema->enc) /* -** Possible values for the sqlite.flags and or Db.flags fields. -** -** On sqlite.flags, the SQLITE_InTrans value means that we have -** executed a BEGIN. On Db.flags, SQLITE_InTrans means a statement -** transaction is active on that particular database file. -*/ -#define SQLITE_VdbeTrace 0x00000001 /* True to trace VDBE execution */ -#define SQLITE_InTrans 0x00000008 /* True if in a transaction */ -#define SQLITE_InternChanges 0x00000010 /* Uncommitted Hash table changes */ -#define SQLITE_FullColNames 0x00000020 /* Show full column names on SELECT */ -#define SQLITE_ShortColNames 0x00000040 /* Show short columns names */ -#define SQLITE_CountRows 0x00000080 /* Count rows changed by INSERT, */ +** Possible values for the sqlite3.flags. +*/ +#define SQLITE_VdbeTrace 0x00000100 /* True to trace VDBE execution */ +#define SQLITE_InternChanges 0x00000200 /* Uncommitted Hash table changes */ +#define SQLITE_FullColNames 0x00000400 /* Show full column names on SELECT */ +#define SQLITE_ShortColNames 0x00000800 /* Show short columns names */ +#define SQLITE_CountRows 0x00001000 /* Count rows changed by INSERT, */ /* DELETE, or UPDATE and return */ /* the count using a callback. */ -#define SQLITE_NullCallback 0x00000100 /* Invoke the callback once if the */ +#define SQLITE_NullCallback 0x00002000 /* Invoke the callback once if the */ /* result set is empty */ -#define SQLITE_SqlTrace 0x00000200 /* Debug print SQL as it executes */ -#define SQLITE_VdbeListing 0x00000400 /* Debug listings of VDBE programs */ -#define SQLITE_WriteSchema 0x00000800 /* OK to update SQLITE_MASTER */ -#define SQLITE_NoReadlock 0x00001000 /* Readlocks are omitted when +#define SQLITE_SqlTrace 0x00004000 /* Debug print SQL as it executes */ +#define SQLITE_VdbeListing 0x00008000 /* Debug listings of VDBE programs */ +#define SQLITE_WriteSchema 0x00010000 /* OK to update SQLITE_MASTER */ +#define SQLITE_NoReadlock 0x00020000 /* Readlocks are omitted when ** accessing read-only databases */ -#define SQLITE_IgnoreChecks 0x00002000 /* Do not enforce check constraints */ -#define SQLITE_ReadUncommitted 0x00004000 /* For shared-cache mode */ -#define SQLITE_LegacyFileFmt 0x00008000 /* Create new databases in format 1 */ -#define SQLITE_FullFSync 0x00010000 /* Use full fsync on the backend */ -#define SQLITE_LoadExtension 0x00020000 /* Enable load_extension */ - -#define SQLITE_RecoveryMode 0x00040000 /* Ignore schema errors */ -#define SQLITE_SharedCache 0x00080000 /* Cache sharing is enabled */ -#define SQLITE_CommitBusy 0x00200000 /* In the process of committing */ -#define SQLITE_ReverseOrder 0x00400000 /* Reverse unordered SELECTs */ +#define SQLITE_IgnoreChecks 0x00040000 /* Do not enforce check constraints */ +#define SQLITE_ReadUncommitted 0x0080000 /* For shared-cache mode */ +#define SQLITE_LegacyFileFmt 0x00100000 /* Create new databases in format 1 */ +#define SQLITE_FullFSync 0x00200000 /* Use full fsync on the backend */ +#define SQLITE_LoadExtension 0x00400000 /* Enable load_extension */ +#define SQLITE_RecoveryMode 0x00800000 /* Ignore schema errors */ +#define SQLITE_ReverseOrder 0x01000000 /* Reverse unordered SELECTs */ +#define SQLITE_RecTriggers 0x02000000 /* Enable recursive triggers */ +#define SQLITE_ForeignKeys 0x04000000 /* Enforce foreign key constraints */ + +/* +** Bits of the sqlite3.flags field that are used by the +** sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS,...) interface. +** These must be the low-order bits of the flags field. +*/ +#define SQLITE_QueryFlattener 0x01 /* Disable query flattening */ +#define SQLITE_ColumnCache 0x02 /* Disable the column cache */ +#define SQLITE_IndexSort 0x04 /* Disable indexes for sorting */ +#define SQLITE_IndexSearch 0x08 /* Disable indexes for searching */ +#define SQLITE_IndexCover 0x10 /* Disable index covering table */ +#define SQLITE_OptMask 0x1f /* Mask of all disablable opts */ /* ** Possible values for the sqlite.magic field. @@ -8378,6 +8470,7 @@ #define SQLITE_FUNC_NEEDCOLL 0x08 /* sqlite3GetFuncCollSeq() might be called */ #define SQLITE_FUNC_PRIVATE 0x10 /* Allowed for internal use only */ #define SQLITE_FUNC_COUNT 0x20 /* Built-in count(*) aggregate */ +#define SQLITE_FUNC_COALESCE 0x40 /* Built-in coalesce() or ifnull() function */ /* ** The following three macros, FUNCTION(), LIKEFUNC() and AGGREGATE() are @@ -8424,6 +8517,7 @@ */ struct Savepoint { char *zName; /* Savepoint name (nul-terminated) */ + i64 nDeferredCons; /* Number of deferred fk violations */ Savepoint *pNext; /* Parent savepoint (if any) */ }; @@ -8544,6 +8638,57 @@ */ #define SQLITE_JUMPIFNULL 0x08 /* jumps if either operand is NULL */ #define SQLITE_STOREP2 0x10 /* Store result in reg[P2] rather than jump */ +#define SQLITE_NULLEQ 0x80 /* NULL=NULL */ + +/* +** An object of this type is created for each virtual table present in +** the database schema. +** +** If the database schema is shared, then there is one instance of this +** structure for each database connection (sqlite3*) that uses the shared +** schema. This is because each database connection requires its own unique +** instance of the sqlite3_vtab* handle used to access the virtual table +** implementation. sqlite3_vtab* handles can not be shared between +** database connections, even when the rest of the in-memory database +** schema is shared, as the implementation often stores the database +** connection handle passed to it via the xConnect() or xCreate() method +** during initialization internally. This database connection handle may +** then used by the virtual table implementation to access real tables +** within the database. So that they appear as part of the callers +** transaction, these accesses need to be made via the same database +** connection as that used to execute SQL operations on the virtual table. +** +** All VTable objects that correspond to a single table in a shared +** database schema are initially stored in a linked-list pointed to by +** the Table.pVTable member variable of the corresponding Table object. +** When an sqlite3_prepare() operation is required to access the virtual +** table, it searches the list for the VTable that corresponds to the +** database connection doing the preparing so as to use the correct +** sqlite3_vtab* handle in the compiled query. +** +** When an in-memory Table object is deleted (for example when the +** schema is being reloaded for some reason), the VTable objects are not +** deleted and the sqlite3_vtab* handles are not xDisconnect()ed +** immediately. Instead, they are moved from the Table.pVTable list to +** another linked list headed by the sqlite3.pDisconnect member of the +** corresponding sqlite3 structure. They are then deleted/xDisconnected +** next time a statement is prepared using said sqlite3*. This is done +** to avoid deadlock issues involving multiple sqlite3.mutex mutexes. +** Refer to comments above function sqlite3VtabUnlockList() for an +** explanation as to why it is safe to add an entry to an sqlite3.pDisconnect +** list without holding the corresponding sqlite3.mutex mutex. +** +** The memory for objects of this type is always allocated by +** sqlite3DbMalloc(), using the connection handle stored in VTable.db as +** the first argument. +*/ +struct VTable { + sqlite3 *db; /* Database connection associated with this table */ + Module *pMod; /* Pointer to module implementation */ + sqlite3_vtab *pVtab; /* Pointer to vtab instance */ + int nRef; /* Number of pointers to this structure */ + VTable *pNext; /* Next in linked list (see above) */ +}; /* ** Each SQL table is represented in memory by an instance of the @@ -8596,8 +8741,7 @@ int addColOffset; /* Offset in CREATE TABLE stmt to add a new column */ #endif #ifndef SQLITE_OMIT_VIRTUALTABLE - Module *pMod; /* Pointer to the implementation of the module */ - sqlite3_vtab *pVtab; /* Pointer to the module instance */ + VTable *pVTable; /* List of VTable objects. */ int nModuleArg; /* Number of arguments to the module */ char **azModuleArg; /* Text of all module args. [0] is module name */ #endif @@ -8651,14 +8795,16 @@ ** the from-table is created. The existence of the to-table is not checked. */ struct FKey { - Table *pFrom; /* The table that contains the REFERENCES clause */ + Table *pFrom; /* Table containing the REFERENCES clause (aka: Child) */ FKey *pNextFrom; /* Next foreign key in pFrom */ - char *zTo; /* Name of table that the key points to */ + char *zTo; /* Name of table that the key points to (aka: Parent) */ + FKey *pNextTo; /* Next foreign key on table named zTo */ + FKey *pPrevTo; /* Previous foreign key on table named zTo */ int nCol; /* Number of columns in this key */ + /* EV: R-30323-21917 */ u8 isDeferred; /* True if constraint checking is deferred till COMMIT */ - u8 updateConf; /* How to resolve conflicts that occur on UPDATE */ - u8 deleteConf; /* How to resolve conflicts that occur on DELETE */ - u8 insertConf; /* How to resolve conflicts that occur on INSERT */ + u8 aAction[2]; /* ON DELETE and ON UPDATE actions, respectively */ + Trigger *apTrigger[2]; /* Triggers for aAction[] actions */ struct sColMap { /* Mapping of columns in pFrom to columns in zTo */ int iFrom; /* Index of column in pFrom */ char *zCol; /* Name of column in zTo. If 0 use PRIMARY KEY */ @@ -8790,6 +8936,20 @@ Schema *pSchema; /* Schema containing this index */ u8 *aSortOrder; /* Array of size Index.nColumn. True==DESC, False==ASC */ char **azColl; /* Array of collation sequence names for index */ + IndexSample *aSample; /* Array of SQLITE_INDEX_SAMPLES samples */ +}; + +/* +** Each sample stored in the sqlite_stat2 table is represented in memory +** using a structure of this type. +*/ +struct IndexSample { + union { + char *z; /* Value if eType is SQLITE_TEXT or SQLITE_BLOB */ + double r; /* Value if eType is SQLITE_FLOAT or SQLITE_INTEGER */ + } u; + u8 eType; /* SQLITE_NULL, SQLITE_INTEGER ... etc. */ + u8 nByte; /* Size in byte of text or blob. */ }; /* @@ -8850,6 +9010,22 @@ }; /* +** The datatype ynVar is a signed integer, either 16-bit or 32-bit. +** Usually it is 16-bits. But if SQLITE_MAX_VARIABLE_NUMBER is greater +** than 32767 we have to make it 32-bit. 16-bit is preferred because +** it uses less memory in the Expr object, which is a big memory user +** in systems with lots of prepared statements. And few applications +** need more than about 10 or 20 variables. But some extreme users want +** to have prepared statements with over 32767 variables, and for them +** the option is available (at compile-time). +*/ +#if SQLITE_MAX_VARIABLE_NUMBER<=32767 +typedef i16 ynVar; +#else +typedef int ynVar; +#endif + +/* ** Each node of an expression in the parse tree is an instance ** of this structure. ** @@ -8940,11 +9116,14 @@ *********************************************************************/ int iTable; /* TK_COLUMN: cursor number of table holding column - ** TK_REGISTER: register number */ - int iColumn; /* TK_COLUMN: column index. -1 for rowid */ + ** TK_REGISTER: register number + ** TK_TRIGGER: 1 -> new, 0 -> old */ + ynVar iColumn; /* TK_COLUMN: column index. -1 for rowid. + ** TK_VARIABLE: variable number (always >= 1). */ i16 iAgg; /* Which entry in pAggInfo->aCol[] or ->aFunc[] */ i16 iRightJoinTable; /* If EP_FromJoin, the right table of the join */ - u16 flags2; /* Second set of flags. EP2_... */ + u8 flags2; /* Second set of flags. EP2_... */ + u8 op2; /* If a TK_REGISTER, the original value of Expr.op */ AggInfo *pAggInfo; /* Used by TK_AGG_COLUMN and TK_AGG_FUNCTION */ Table *pTab; /* Table for TK_COLUMN expressions. */ #if SQLITE_MAX_EXPR_DEPTH>0 @@ -8964,14 +9143,13 @@ #define EP_DblQuoted 0x0040 /* token.z was originally in "..." */ #define EP_InfixFunc 0x0080 /* True for an infix function: LIKE, GLOB, etc */ #define EP_ExpCollate 0x0100 /* Collating sequence specified explicitly */ -#define EP_AnyAff 0x0200 /* Can take a cached column of any affinity */ -#define EP_FixedDest 0x0400 /* Result needed in a specific register */ -#define EP_IntValue 0x0800 /* Integer value contained in u.iValue */ -#define EP_xIsSelect 0x1000 /* x.pSelect is valid (otherwise x.pList is) */ - -#define EP_Reduced 0x2000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */ -#define EP_TokenOnly 0x4000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */ -#define EP_Static 0x8000 /* Held in memory not obtained from malloc() */ +#define EP_FixedDest 0x0200 /* Result needed in a specific register */ +#define EP_IntValue 0x0400 /* Integer value contained in u.iValue */ +#define EP_xIsSelect 0x0800 /* x.pSelect is valid (otherwise x.pList is) */ + +#define EP_Reduced 0x1000 /* Expr struct is EXPR_REDUCEDSIZE bytes only */ +#define EP_TokenOnly 0x2000 /* Expr struct is EXPR_TOKENONLYSIZE bytes only */ +#define EP_Static 0x4000 /* Held in memory not obtained from malloc() */ /* ** The following are the meanings of bits in the Expr.flags2 field. @@ -9216,6 +9394,7 @@ #define WHERE_OMIT_OPEN 0x0010 /* Table cursor are already open */ #define WHERE_OMIT_CLOSE 0x0020 /* Omit close of table & index cursors */ #define WHERE_FORCE_TABLE 0x0040 /* Do not use an index-only search */ +#define WHERE_ONETABLE_ONLY 0x0080 /* Only code the 1st table in pTabList */ /* ** The WHERE clause processing routine has two halves. The @@ -9228,6 +9407,7 @@ Parse *pParse; /* Parsing and code generating context */ u16 wctrlFlags; /* Flags originally passed to sqlite3WhereBegin() */ u8 okOnePass; /* Ok to use one-pass algorithm for UPDATE or DELETE */ + u8 untestedTerms; /* Not all WHERE terms resolved by outer loop */ SrcList *pTabList; /* List of tables in the join */ int iTop; /* The very beginning of the WHERE loop */ int iContinue; /* Jump here to continue with next record */ @@ -9379,6 +9559,32 @@ #endif /* +** At least one instance of the following structure is created for each +** trigger that may be fired while parsing an INSERT, UPDATE or DELETE +** statement. All such objects are stored in the linked list headed at +** Parse.pTriggerPrg and deleted once statement compilation has been +** completed. +** +** A Vdbe sub-program that implements the body and WHEN clause of trigger +** TriggerPrg.pTrigger, assuming a default ON CONFLICT clause of +** TriggerPrg.orconf, is stored in the TriggerPrg.pProgram variable. +** The Parse.pTriggerPrg list never contains two entries with the same +** values for both pTrigger and orconf. +** +** The TriggerPrg.aColmask[0] variable is set to a mask of old.* columns +** accessed (or set to 0 for triggers fired as a result of INSERT +** statements). Similarly, the TriggerPrg.aColmask[1] variable is set to +** a mask of new.* columns used by the program. +*/ +struct TriggerPrg { + Trigger *pTrigger; /* Trigger this program was coded from */ + int orconf; /* Default ON CONFLICT policy */ + SubProgram *pProgram; /* Program implementing pTrigger/orconf */ + u32 aColmask[2]; /* Masks of old.*, new.* columns accessed */ + TriggerPrg *pNext; /* Next entry in Parse.pTriggerPrg list */ +}; + +/* ** An SQL parser context. A copy of this structure is passed through ** the parser and down into all the parser action routine in order to ** carry around information that is global to the entire parse. @@ -9421,7 +9627,6 @@ struct yColCache { int iTable; /* Table cursor number */ int iColumn; /* Table column number */ - u8 affChange; /* True if this register has had an affinity change */ u8 tempReg; /* iReg is a temp register that needs to be freed */ int iLevel; /* Nesting level */ int iReg; /* Reg with value of this column. 0 means none. */ @@ -9429,6 +9634,8 @@ } aColCache[SQLITE_N_COLCACHE]; /* One for each column cache entry */ u32 writeMask; /* Start a write transaction on these databases */ u32 cookieMask; /* Bitmask of schema verified databases */ + u8 isMultiWrite; /* True if statement may affect/insert multiple rows */ + u8 mayAbort; /* True if statement may throw an ABORT exception */ int cookieGoto; /* Address of OP_Goto to cookie verifier subroutine */ int cookieValue[SQLITE_MAX_ATTACHED+2]; /* Values of cookies to verify */ #ifndef SQLITE_OMIT_SHARED_CACHE @@ -9438,6 +9645,16 @@ int regRowid; /* Register holding rowid of CREATE TABLE entry */ int regRoot; /* Register holding root page number for new objects */ AutoincInfo *pAinc; /* Information about AUTOINCREMENT counters */ + int nMaxArg; /* Max args passed to user function by sub-program */ + + /* Information used while coding trigger programs. */ + Parse *pToplevel; /* Parse structure for main program (or NULL) */ + Table *pTriggerTab; /* Table triggers are being coded for */ + u32 oldmask; /* Mask of old.* columns referenced */ + u32 newmask; /* Mask of new.* columns referenced */ + u8 eTriggerOp; /* TK_UPDATE, TK_INSERT or TK_DELETE */ + u8 eOrconf; /* Default ON CONFLICT policy for trigger steps */ + u8 disableTriggers; /* True to disable triggers */ /* Above is constant between recursions. Below is reset before and after ** each recursion */ @@ -9446,18 +9663,16 @@ int nVarExpr; /* Number of used slots in apVarExpr[] */ int nVarExprAlloc; /* Number of allocated slots in apVarExpr[] */ Expr **apVarExpr; /* Pointers to :aaa and $aaaa wildcard expressions */ + Vdbe *pReprepare; /* VM being reprepared (sqlite3Reprepare()) */ int nAlias; /* Number of aliased result set columns */ int nAliasAlloc; /* Number of allocated slots for aAlias[] */ int *aAlias; /* Register used to hold aliased result */ u8 explain; /* True if the EXPLAIN flag is found on the query */ - Token sErrToken; /* The token at which the error occurred */ Token sNameToken; /* Token with unqualified schema object name */ Token sLastToken; /* The last token parsed */ - const char *zSql; /* All SQL text */ const char *zTail; /* All SQL text past the last semicolon parsed */ Table *pNewTable; /* A table being constructed by CREATE TABLE */ Trigger *pNewTrigger; /* Trigger under construct by a CREATE TRIGGER */ - TriggerStack *trigStack; /* Trigger actions being coded */ const char *zAuthContext; /* The 6th parameter to db->xAuth callbacks */ #ifndef SQLITE_OMIT_VIRTUALTABLE Token sArg; /* Complete text of a module argument */ @@ -9467,6 +9682,7 @@ #endif int nHeight; /* Expression tree height of current sub-select */ Table *pZombieTab; /* List of Table objects to delete after code gen */ + TriggerPrg *pTriggerPrg; /* Linked list of coded triggers */ }; #ifdef SQLITE_OMIT_VIRTUALTABLE @@ -9487,11 +9703,12 @@ /* ** Bitfield flags for P5 value in OP_Insert and OP_Delete */ -#define OPFLAG_NCHANGE 1 /* Set to update db->nChange */ -#define OPFLAG_LASTROWID 2 /* Set to update db->lastRowid */ -#define OPFLAG_ISUPDATE 4 /* This OP_Insert is an sql UPDATE */ -#define OPFLAG_APPEND 8 /* This is likely to be an append */ -#define OPFLAG_USESEEKRESULT 16 /* Try to avoid a seek in BtreeInsert() */ +#define OPFLAG_NCHANGE 0x01 /* Set to update db->nChange */ +#define OPFLAG_LASTROWID 0x02 /* Set to update db->lastRowid */ +#define OPFLAG_ISUPDATE 0x04 /* This OP_Insert is an sql UPDATE */ +#define OPFLAG_APPEND 0x08 /* This is likely to be an append */ +#define OPFLAG_USESEEKRESULT 0x10 /* Try to avoid a seek in BtreeInsert() */ +#define OPFLAG_CLEARCACHE 0x20 /* Clear pseudo-table cache in OP_Column */ /* * Each trigger present in the database schema is stored as an instance of @@ -9509,7 +9726,7 @@ * containing the SQL statements specified as the trigger program. */ struct Trigger { - char *name; /* The name of the trigger */ + char *zName; /* The name of the trigger */ char *table; /* The table or view to which the trigger applies */ u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT */ u8 tr_tm; /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ @@ -9571,61 +9788,19 @@ * */ struct TriggerStep { - int op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ - int orconf; /* OE_Rollback etc. */ + u8 op; /* One of TK_DELETE, TK_UPDATE, TK_INSERT, TK_SELECT */ + u8 orconf; /* OE_Rollback etc. */ Trigger *pTrig; /* The trigger that this step is a part of */ - - Select *pSelect; /* Valid for SELECT and sometimes - INSERT steps (when pExprList == 0) */ - Token target; /* Target table for DELETE, UPDATE, INSERT. Quoted */ - Expr *pWhere; /* Valid for DELETE, UPDATE steps */ - ExprList *pExprList; /* Valid for UPDATE statements and sometimes - INSERT steps (when pSelect == 0) */ - IdList *pIdList; /* Valid for INSERT statements only */ + Select *pSelect; /* SELECT statment or RHS of INSERT INTO .. SELECT ... */ + Token target; /* Target table for DELETE, UPDATE, INSERT */ + Expr *pWhere; /* The WHERE clause for DELETE or UPDATE steps */ + ExprList *pExprList; /* SET clause for UPDATE. VALUES clause for INSERT */ + IdList *pIdList; /* Column names for INSERT */ TriggerStep *pNext; /* Next in the link-list */ TriggerStep *pLast; /* Last element in link-list. Valid for 1st elem only */ }; /* - * An instance of struct TriggerStack stores information required during code - * generation of a single trigger program. While the trigger program is being - * coded, its associated TriggerStack instance is pointed to by the - * "pTriggerStack" member of the Parse structure. - * - * The pTab member points to the table that triggers are being coded on. The - * newIdx member contains the index of the vdbe cursor that points at the temp - * table that stores the new.* references. If new.* references are not valid - * for the trigger being coded (for example an ON DELETE trigger), then newIdx - * is set to -1. The oldIdx member is analogous to newIdx, for old.* references. - * - * The ON CONFLICT policy to be used for the trigger program steps is stored - * as the orconf member. If this is OE_Default, then the ON CONFLICT clause - * specified for individual triggers steps is used. - * - * struct TriggerStack has a "pNext" member, to allow linked lists to be - * constructed. When coding nested triggers (triggers fired by other triggers) - * each nested trigger stores its parent trigger's TriggerStack as the "pNext" - * pointer. Once the nested trigger has been coded, the pNext value is restored - * to the pTriggerStack member of the Parse stucture and coding of the parent - * trigger continues. - * - * Before a nested trigger is coded, the linked list pointed to by the - * pTriggerStack is scanned to ensure that the trigger is not about to be coded - * recursively. If this condition is detected, the nested trigger is not coded. - */ -struct TriggerStack { - Table *pTab; /* Table that triggers are currently being coded on */ - int newIdx; /* Index of vdbe cursor to "new" temp table */ - int oldIdx; /* Index of vdbe cursor to "old" temp table */ - u32 newColMask; - u32 oldColMask; - int orconf; /* Current orconf policy */ - int ignoreJump; /* where to jump to for a RAISE(IGNORE) */ - Trigger *pTrigger; /* The trigger currently being coded */ - TriggerStack *pNext; /* Next trigger down on the trigger stack */ -}; - -/* ** The following structure contains information used by the sqliteFix... ** routines as they walk the parse tree to make database references ** explicit. @@ -9695,7 +9870,9 @@ ** initially be zero, however. */ int isInit; /* True after initialization has finished */ int inProgress; /* True while initialization in progress */ + int isMutexInit; /* True after mutexes are initialized */ int isMallocInit; /* True after malloc is initialized */ + int isPCacheInit; /* True after malloc is initialized */ sqlite3_mutex *pInitMutex; /* Mutex used by sqlite3_initialize() */ int nRefInitMutex; /* Number of users of pInitMutex */ }; @@ -9787,9 +9964,9 @@ ** Internal function prototypes */ SQLITE_PRIVATE int sqlite3StrICmp(const char *, const char *); -SQLITE_PRIVATE int sqlite3StrNICmp(const char *, const char *, int); SQLITE_PRIVATE int sqlite3IsNumber(const char*, int*, u8); SQLITE_PRIVATE int sqlite3Strlen30(const char*); +#define sqlite3StrNICmp sqlite3_strnicmp SQLITE_PRIVATE int sqlite3MallocInit(void); SQLITE_PRIVATE void sqlite3MallocEnd(void); @@ -9853,6 +10030,9 @@ SQLITE_PRIVATE int sqlite3IsNaN(double); SQLITE_PRIVATE void sqlite3VXPrintf(StrAccum*, int, const char*, va_list); +#ifndef SQLITE_OMIT_TRACE +SQLITE_PRIVATE void sqlite3XPrintf(StrAccum*, const char*, ...); +#endif SQLITE_PRIVATE char *sqlite3MPrintf(sqlite3*,const char*, ...); SQLITE_PRIVATE char *sqlite3VMPrintf(sqlite3*,const char*, va_list); SQLITE_PRIVATE char *sqlite3MAppendf(sqlite3*,char*,const char*,...); @@ -9880,7 +10060,6 @@ SQLITE_PRIVATE Expr *sqlite3ExprAnd(sqlite3*,Expr*, Expr*); SQLITE_PRIVATE Expr *sqlite3ExprFunction(Parse*,ExprList*, Token*); SQLITE_PRIVATE void sqlite3ExprAssignVarNumber(Parse*, Expr*); -SQLITE_PRIVATE void sqlite3ExprClear(sqlite3*, Expr*); SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3*, Expr*); SQLITE_PRIVATE ExprList *sqlite3ExprListAppend(Parse*,ExprList*,Expr*); SQLITE_PRIVATE void sqlite3ExprListSetName(Parse*,ExprList*,Token*,int); @@ -9949,7 +10128,7 @@ SQLITE_PRIVATE void sqlite3SrcListAssignCursors(Parse*, SrcList*); SQLITE_PRIVATE void sqlite3IdListDelete(sqlite3*, IdList*); SQLITE_PRIVATE void sqlite3SrcListDelete(sqlite3*, SrcList*); -SQLITE_PRIVATE void sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, +SQLITE_PRIVATE Index *sqlite3CreateIndex(Parse*,Token*,Token*,SrcList*,ExprList*,int,Token*, Token*, int, int); SQLITE_PRIVATE void sqlite3DropIndex(Parse*, SrcList*, int); SQLITE_PRIVATE int sqlite3Select(Parse*, Select*, SelectDest*); @@ -9966,13 +10145,13 @@ SQLITE_PRIVATE void sqlite3Update(Parse*, SrcList*, ExprList*, Expr*, int); SQLITE_PRIVATE WhereInfo *sqlite3WhereBegin(Parse*, SrcList*, Expr*, ExprList**, u16); SQLITE_PRIVATE void sqlite3WhereEnd(WhereInfo*); -SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int, int); +SQLITE_PRIVATE int sqlite3ExprCodeGetColumn(Parse*, Table*, int, int, int); SQLITE_PRIVATE void sqlite3ExprCodeMove(Parse*, int, int, int); SQLITE_PRIVATE void sqlite3ExprCodeCopy(Parse*, int, int, int); SQLITE_PRIVATE void sqlite3ExprCacheStore(Parse*, int, int, int); SQLITE_PRIVATE void sqlite3ExprCachePush(Parse*); SQLITE_PRIVATE void sqlite3ExprCachePop(Parse*, int); -SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse*, int); +SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse*, int, int); SQLITE_PRIVATE void sqlite3ExprCacheClear(Parse*); SQLITE_PRIVATE void sqlite3ExprCacheAffinityChange(Parse*, int, int); SQLITE_PRIVATE void sqlite3ExprHardCopy(Parse*,int,int); @@ -9996,7 +10175,6 @@ SQLITE_PRIVATE void sqlite3ExprAnalyzeAggregates(NameContext*, Expr*); SQLITE_PRIVATE void sqlite3ExprAnalyzeAggList(NameContext*,ExprList*); SQLITE_PRIVATE Vdbe *sqlite3GetVdbe(Parse*); -SQLITE_PRIVATE Expr *sqlite3CreateIdExpr(Parse *, const char*); SQLITE_PRIVATE void sqlite3PrngSaveState(void); SQLITE_PRIVATE void sqlite3PrngRestoreState(void); SQLITE_PRIVATE void sqlite3PrngResetState(void); @@ -10011,15 +10189,21 @@ SQLITE_PRIVATE int sqlite3ExprIsConstantNotJoin(Expr*); SQLITE_PRIVATE int sqlite3ExprIsConstantOrFunction(Expr*); SQLITE_PRIVATE int sqlite3ExprIsInteger(Expr*, int*); +SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr*); +SQLITE_PRIVATE void sqlite3ExprCodeIsNullJump(Vdbe*, const Expr*, int, int); +SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr*, char); SQLITE_PRIVATE int sqlite3IsRowid(const char*); -SQLITE_PRIVATE void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int); +SQLITE_PRIVATE void sqlite3GenerateRowDelete(Parse*, Table*, int, int, int, Trigger *, int); SQLITE_PRIVATE void sqlite3GenerateRowIndexDelete(Parse*, Table*, int, int*); SQLITE_PRIVATE int sqlite3GenerateIndexKey(Parse*, Index*, int, int, int); SQLITE_PRIVATE void sqlite3GenerateConstraintChecks(Parse*,Table*,int,int, int*,int,int,int,int,int*); -SQLITE_PRIVATE void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int,int,int); +SQLITE_PRIVATE void sqlite3CompleteInsertion(Parse*, Table*, int, int, int*, int, int, int); SQLITE_PRIVATE int sqlite3OpenTableAndIndices(Parse*, Table*, int, int); SQLITE_PRIVATE void sqlite3BeginWriteOperation(Parse*, int, int); +SQLITE_PRIVATE void sqlite3MultiWrite(Parse*); +SQLITE_PRIVATE void sqlite3MayAbort(Parse*); +SQLITE_PRIVATE void sqlite3HaltConstraint(Parse*, int, char*, int); SQLITE_PRIVATE Expr *sqlite3ExprDup(sqlite3*,Expr*,int); SQLITE_PRIVATE ExprList *sqlite3ExprListDup(sqlite3*,ExprList*,int); SQLITE_PRIVATE SrcList *sqlite3SrcListDup(sqlite3*,SrcList*,int); @@ -10053,24 +10237,30 @@ SQLITE_PRIVATE void sqlite3DropTriggerPtr(Parse*, Trigger*); SQLITE_PRIVATE Trigger *sqlite3TriggersExist(Parse *, Table*, int, ExprList*, int *pMask); SQLITE_PRIVATE Trigger *sqlite3TriggerList(Parse *, Table *); -SQLITE_PRIVATE int sqlite3CodeRowTrigger(Parse*, Trigger *, int, ExprList*, int, Table *, - int, int, int, int, u32*, u32*); +SQLITE_PRIVATE void sqlite3CodeRowTrigger(Parse*, Trigger *, int, ExprList*, int, Table *, + int, int, int); +SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect(Parse *, Trigger *, Table *, int, int, int); void sqliteViewTriggers(Parse*, Table*, Expr*, int, ExprList*); SQLITE_PRIVATE void sqlite3DeleteTriggerStep(sqlite3*, TriggerStep*); SQLITE_PRIVATE TriggerStep *sqlite3TriggerSelectStep(sqlite3*,Select*); SQLITE_PRIVATE TriggerStep *sqlite3TriggerInsertStep(sqlite3*,Token*, IdList*, - ExprList*,Select*,int); -SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, int); + ExprList*,Select*,u8); +SQLITE_PRIVATE TriggerStep *sqlite3TriggerUpdateStep(sqlite3*,Token*,ExprList*, Expr*, u8); SQLITE_PRIVATE TriggerStep *sqlite3TriggerDeleteStep(sqlite3*,Token*, Expr*); SQLITE_PRIVATE void sqlite3DeleteTrigger(sqlite3*, Trigger*); SQLITE_PRIVATE void sqlite3UnlinkAndDeleteTrigger(sqlite3*,int,const char*); +SQLITE_PRIVATE u32 sqlite3TriggerColmask(Parse*,Trigger*,ExprList*,int,int,Table*,int); +# define sqlite3ParseToplevel(p) ((p)->pToplevel ? (p)->pToplevel : (p)) #else # define sqlite3TriggersExist(B,C,D,E,F) 0 # define sqlite3DeleteTrigger(A,B) # define sqlite3DropTriggerPtr(A,B) # define sqlite3UnlinkAndDeleteTrigger(A,B,C) -# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I,J,K,L) 0 +# define sqlite3CodeRowTrigger(A,B,C,D,E,F,G,H,I) +# define sqlite3CodeRowTriggerDirect(A,B,C,D,E,F) # define sqlite3TriggerList(X, Y) 0 +# define sqlite3ParseToplevel(p) p +# define sqlite3TriggerColmask(A,B,C,D,E,F,G) 0 #endif SQLITE_PRIVATE int sqlite3JoinType(Parse*, Token*, Token*, Token*); @@ -10081,6 +10271,7 @@ SQLITE_PRIVATE int sqlite3AuthCheck(Parse*,int, const char*, const char*, const char*); SQLITE_PRIVATE void sqlite3AuthContextPush(Parse*, AuthContext*, const char*); SQLITE_PRIVATE void sqlite3AuthContextPop(AuthContext*); +SQLITE_PRIVATE int sqlite3AuthReadCol(Parse*, const char *, const char *, int); #else # define sqlite3AuthRead(a,b,c,d) # define sqlite3AuthCheck(a,b,c,d,e) SQLITE_OK @@ -10089,7 +10280,7 @@ #endif SQLITE_PRIVATE void sqlite3Attach(Parse*, Expr*, Expr*, Expr*); SQLITE_PRIVATE void sqlite3Detach(Parse*, Expr*); -SQLITE_PRIVATE int sqlite3BtreeFactory(const sqlite3 *db, const char *zFilename, +SQLITE_PRIVATE int sqlite3BtreeFactory(sqlite3 *db, const char *zFilename, int omitJournal, int nCache, int flags, Btree **ppBtree); SQLITE_PRIVATE int sqlite3FixInit(DbFixer*, Parse*, int, const char*, const Token*); SQLITE_PRIVATE int sqlite3FixSrcList(DbFixer*, SrcList*); @@ -10140,7 +10331,7 @@ #define putVarint sqlite3PutVarint -SQLITE_PRIVATE void sqlite3IndexAffinityStr(Vdbe *, Index *); +SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *, Index *); SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *, Table *); SQLITE_PRIVATE char sqlite3CompareAffinity(Expr *pExpr, char aff2); SQLITE_PRIVATE int sqlite3IndexAffinityOk(Expr *pExpr, char idx_affinity); @@ -10166,9 +10357,13 @@ SQLITE_PRIVATE void sqlite3ValueFree(sqlite3_value*); SQLITE_PRIVATE sqlite3_value *sqlite3ValueNew(sqlite3 *); SQLITE_PRIVATE char *sqlite3Utf16to8(sqlite3 *, const void*, int); +#ifdef SQLITE_ENABLE_STAT2 +SQLITE_PRIVATE char *sqlite3Utf8to16(sqlite3 *, u8, char *, int, int *); +#endif SQLITE_PRIVATE int sqlite3ValueFromExpr(sqlite3 *, Expr *, u8, u8, sqlite3_value **); SQLITE_PRIVATE void sqlite3ValueApplyAffinity(sqlite3_value *, u8, u8); #ifndef SQLITE_AMALGAMATION +SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[]; SQLITE_PRIVATE const unsigned char sqlite3UpperToLower[]; SQLITE_PRIVATE const unsigned char sqlite3CtypeMap[]; SQLITE_PRIVATE SQLITE_WSD struct Sqlite3Config sqlite3Config; @@ -10182,21 +10377,22 @@ SQLITE_PRIVATE int sqlite3GetToken(const unsigned char *, int *); SQLITE_PRIVATE void sqlite3NestedParse(Parse*, const char*, ...); SQLITE_PRIVATE void sqlite3ExpirePreparedStatements(sqlite3*); -SQLITE_PRIVATE void sqlite3CodeSubselect(Parse *, Expr *, int, int); +SQLITE_PRIVATE int sqlite3CodeSubselect(Parse *, Expr *, int, int); SQLITE_PRIVATE void sqlite3SelectPrep(Parse*, Select*, NameContext*); SQLITE_PRIVATE int sqlite3ResolveExprNames(NameContext*, Expr*); SQLITE_PRIVATE void sqlite3ResolveSelectNames(Parse*, Select*, NameContext*); SQLITE_PRIVATE int sqlite3ResolveOrderGroupBy(Parse*, Select*, ExprList*, const char*); -SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int); +SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *, Table *, int, int); SQLITE_PRIVATE void sqlite3AlterFinishAddColumn(Parse *, Token *); SQLITE_PRIVATE void sqlite3AlterBeginAddColumn(Parse *, SrcList *); -SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(sqlite3*, CollSeq *, const char*); +SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq(sqlite3*, u8, CollSeq *, const char*); SQLITE_PRIVATE char sqlite3AffinityType(const char*); SQLITE_PRIVATE void sqlite3Analyze(Parse*, Token*, Token*); SQLITE_PRIVATE int sqlite3InvokeBusyHandler(BusyHandler*); SQLITE_PRIVATE int sqlite3FindDb(sqlite3*, Token*); SQLITE_PRIVATE int sqlite3FindDbName(sqlite3 *, const char *); SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3*,int iDB); +SQLITE_PRIVATE void sqlite3DeleteIndexSamples(Index*); SQLITE_PRIVATE void sqlite3DefaultRowEst(Index*); SQLITE_PRIVATE void sqlite3RegisterLikeFunctions(sqlite3*, int); SQLITE_PRIVATE int sqlite3IsLikeFunction(sqlite3*,Expr*,int*,char*); @@ -10216,6 +10412,7 @@ SQLITE_PRIVATE char *sqlite3StrAccumFinish(StrAccum*); SQLITE_PRIVATE void sqlite3StrAccumReset(StrAccum*); SQLITE_PRIVATE void sqlite3SelectDestInit(SelectDest*,int,int); +SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *, SrcList *, int, int); SQLITE_PRIVATE void sqlite3BackupRestart(sqlite3_backup *); SQLITE_PRIVATE void sqlite3BackupUpdate(sqlite3_backup *, Pgno, const u8 *); @@ -10248,21 +10445,25 @@ #endif #ifdef SQLITE_OMIT_VIRTUALTABLE -# define sqlite3VtabClear(X) +# define sqlite3VtabClear(Y) # define sqlite3VtabSync(X,Y) SQLITE_OK # define sqlite3VtabRollback(X) # define sqlite3VtabCommit(X) # define sqlite3VtabInSync(db) 0 +# define sqlite3VtabLock(X) +# define sqlite3VtabUnlock(X) +# define sqlite3VtabUnlockList(X) #else SQLITE_PRIVATE void sqlite3VtabClear(Table*); SQLITE_PRIVATE int sqlite3VtabSync(sqlite3 *db, char **); SQLITE_PRIVATE int sqlite3VtabRollback(sqlite3 *db); SQLITE_PRIVATE int sqlite3VtabCommit(sqlite3 *db); +SQLITE_PRIVATE void sqlite3VtabLock(VTable *); +SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *); +SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3*); # define sqlite3VtabInSync(db) ((db)->nVTrans>0 && (db)->aVTrans==0) #endif SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse*,Table*); -SQLITE_PRIVATE void sqlite3VtabLock(sqlite3_vtab*); -SQLITE_PRIVATE void sqlite3VtabUnlock(sqlite3*, sqlite3_vtab*); SQLITE_PRIVATE void sqlite3VtabBeginParse(Parse*, Token*, Token*, Token*); SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse*, Token*); SQLITE_PRIVATE void sqlite3VtabArgInit(Parse*); @@ -10270,15 +10471,43 @@ SQLITE_PRIVATE int sqlite3VtabCallCreate(sqlite3*, int, const char *, char **); SQLITE_PRIVATE int sqlite3VtabCallConnect(Parse*, Table*); SQLITE_PRIVATE int sqlite3VtabCallDestroy(sqlite3*, int, const char *); -SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, sqlite3_vtab *); +SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *, VTable *); SQLITE_PRIVATE FuncDef *sqlite3VtabOverloadFunction(sqlite3 *,FuncDef*, int nArg, Expr*); SQLITE_PRIVATE void sqlite3InvalidFunction(sqlite3_context*,int,sqlite3_value**); +SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe*, const char*, int); SQLITE_PRIVATE int sqlite3TransferBindings(sqlite3_stmt *, sqlite3_stmt *); SQLITE_PRIVATE int sqlite3Reprepare(Vdbe*); SQLITE_PRIVATE void sqlite3ExprListCheckLength(Parse*, ExprList*, const char*); SQLITE_PRIVATE CollSeq *sqlite3BinaryCompareCollSeq(Parse *, Expr *, Expr *); SQLITE_PRIVATE int sqlite3TempInMemory(const sqlite3*); +SQLITE_PRIVATE VTable *sqlite3GetVTable(sqlite3*, Table*); +/* Declarations for functions in fkey.c. All of these are replaced by +** no-op macros if OMIT_FOREIGN_KEY is defined. In this case no foreign +** key functionality is available. If OMIT_TRIGGER is defined but +** OMIT_FOREIGN_KEY is not, only some of the functions are no-oped. In +** this case foreign keys are parsed, but no other functionality is +** provided (enforcement of FK constraints requires the triggers sub-system). +*/ +#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) +SQLITE_PRIVATE void sqlite3FkCheck(Parse*, Table*, int, int); +SQLITE_PRIVATE void sqlite3FkDropTable(Parse*, SrcList *, Table*); +SQLITE_PRIVATE void sqlite3FkActions(Parse*, Table*, ExprList*, int); +SQLITE_PRIVATE int sqlite3FkRequired(Parse*, Table*, int*, int); +SQLITE_PRIVATE u32 sqlite3FkOldmask(Parse*, Table*); +SQLITE_PRIVATE FKey *sqlite3FkReferences(Table *); +#else + #define sqlite3FkActions(a,b,c,d) + #define sqlite3FkCheck(a,b,c,d) + #define sqlite3FkDropTable(a,b,c) + #define sqlite3FkOldmask(a,b) 0 + #define sqlite3FkRequired(a,b,c,d) 0 +#endif +#ifndef SQLITE_OMIT_FOREIGN_KEY +SQLITE_PRIVATE void sqlite3FkDelete(Table*); +#else + #define sqlite3FkDelete(a) +#endif /* @@ -10375,11 +10604,8 @@ ************************************************************************* ** ** This file contains definitions of global variables and contants. -** -** $Id: global.c,v 1.12 2009/02/05 16:31:46 drh Exp $ */ - /* An array to map all upper-case characters into their corresponding ** lower-case character. ** @@ -10435,6 +10661,7 @@ ** isalnum() 0x06 ** isxdigit() 0x08 ** toupper() 0x20 +** SQLite identifier character 0x40 ** ** Bit 0x20 is set if the mapped character requires translation to upper ** case. i.e. if the character is a lower-case ASCII character. @@ -10446,6 +10673,11 @@ ** Standard function tolower() is implemented using the sqlite3UpperToLower[] ** array. tolower() is used more often than toupper() by SQLite. ** +** Bit 0x40 is set if the character non-alphanumeric and can be used in an +** SQLite identifier. Identifiers are alphanumerics, "_", "$", and any +** non-ASCII UTF character. Hence the test for whether or not a character is +** part of an identifier is 0x46. +** ** SQLite's versions are identical to the standard versions assuming a ** locale of "C". They are implemented as macros in sqliteInt.h. */ @@ -10455,7 +10687,7 @@ 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, /* 08..0f ........ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10..17 ........ */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18..1f ........ */ - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 20..27 !"#$%&' */ + 0x01, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, /* 20..27 !"#$%&' */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28..2f ()*+,-./ */ 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, /* 30..37 01234567 */ 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 38..3f 89:;<=>? */ @@ -10463,29 +10695,29 @@ 0x00, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x0a, 0x02, /* 40..47 @ABCDEFG */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 48..4f HIJKLMNO */ 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, /* 50..57 PQRSTUVW */ - 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, /* 58..5f XYZ[\]^_ */ + 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x40, /* 58..5f XYZ[\]^_ */ 0x00, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x2a, 0x22, /* 60..67 `abcdefg */ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 68..6f hijklmno */ 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, 0x22, /* 70..77 pqrstuvw */ 0x22, 0x22, 0x22, 0x00, 0x00, 0x00, 0x00, 0x00, /* 78..7f xyz{|}~. */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 80..87 ........ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 88..8f ........ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 90..97 ........ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 98..9f ........ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a0..a7 ........ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* a8..af ........ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b0..b7 ........ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* b8..bf ........ */ - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c0..c7 ........ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* c8..cf ........ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d0..d7 ........ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* d8..df ........ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* e0..e7 ........ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* e8..ef ........ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* f0..f7 ........ */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 /* f8..ff ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 80..87 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 88..8f ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 90..97 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* 98..9f ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a0..a7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* a8..af ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b0..b7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* b8..bf ........ */ + + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c0..c7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* c8..cf ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d0..d7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* d8..df ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e0..e7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* e8..ef ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, /* f0..f7 ........ */ + 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 /* f8..ff ........ */ }; #endif @@ -10516,10 +10748,12 @@ 0, /* nPage */ 0, /* mxParserStack */ 0, /* sharedCacheEnabled */ - /* All the rest need to always be zero */ + /* All the rest should always be initialized to zero */ 0, /* isInit */ 0, /* inProgress */ + 0, /* isMutexInit */ 0, /* isMallocInit */ + 0, /* isPCacheInit */ 0, /* pInitMutex */ 0, /* nRefInitMutex */ }; @@ -10552,6 +10786,14 @@ */ SQLITE_PRIVATE int sqlite3PendingByte = 0x40000000; +/* +** Properties of opcodes. The OPFLG_INITIALIZER macro is +** created by mkopcodeh.awk during compilation. Data is obtained +** from the comments following the "case OP_xxxx:" statements in +** the vdbe.c file. +*/ +SQLITE_PRIVATE const unsigned char sqlite3OpcodeProperty[] = OPFLG_INITIALIZER; + /************** End of global.c **********************************************/ /************** Begin file status.c ******************************************/ /* @@ -10568,8 +10810,6 @@ ** ** This module implements the sqlite3_status() interface and related ** functionality. -** -** $Id: status.c,v 1.9 2008/09/02 00:52:52 drh Exp $ */ /* @@ -10696,8 +10936,6 @@ ** sqlite3RegisterDateTimeFunctions() found at the bottom of the file. ** All other code has file scope. ** -** $Id: date.c,v 1.107 2009/05/03 20:23:53 drh Exp $ -** ** SQLite processes all times and dates as Julian Day numbers. The ** dates and times are stored as the number of days since noon ** in Greenwich on November 24, 4714 B.C. according to the Gregorian @@ -11125,7 +11363,7 @@ x.tz = 0; x.validJD = 0; computeJD(&x); - t = x.iJD/1000 - 21086676*(i64)10000; + t = (time_t)(x.iJD/1000 - 21086676*(i64)10000); #ifdef HAVE_LOCALTIME_R { struct tm sLocal; @@ -11137,7 +11375,7 @@ y.m = sLocal.tm_min; y.s = sLocal.tm_sec; } -#elif defined(HAVE_LOCALTIME_S) +#elif defined(HAVE_LOCALTIME_S) && HAVE_LOCALTIME_S { struct tm sLocal; localtime_s(&sLocal, &t); @@ -11800,8 +12038,6 @@ ** ** This file contains OS interface code that is common to all ** architectures. -** -** $Id: os.c,v 1.126 2009/03/25 14:24:42 drh Exp $ */ #define _SQLITE_OS_C_ 1 #undef _SQLITE_OS_C_ @@ -11824,13 +12060,13 @@ ** */ #if defined(SQLITE_TEST) && (SQLITE_OS_WIN==0) - #define DO_OS_MALLOC_TEST if (1) { \ - void *pTstAlloc = sqlite3Malloc(10); \ - if (!pTstAlloc) return SQLITE_IOERR_NOMEM; \ - sqlite3_free(pTstAlloc); \ + #define DO_OS_MALLOC_TEST(x) if (!x || !sqlite3IsMemJournal(x)) { \ + void *pTstAlloc = sqlite3Malloc(10); \ + if (!pTstAlloc) return SQLITE_IOERR_NOMEM; \ + sqlite3_free(pTstAlloc); \ } #else - #define DO_OS_MALLOC_TEST + #define DO_OS_MALLOC_TEST(x) #endif /* @@ -11848,33 +12084,33 @@ return rc; } SQLITE_PRIVATE int sqlite3OsRead(sqlite3_file *id, void *pBuf, int amt, i64 offset){ - DO_OS_MALLOC_TEST; + DO_OS_MALLOC_TEST(id); return id->pMethods->xRead(id, pBuf, amt, offset); } SQLITE_PRIVATE int sqlite3OsWrite(sqlite3_file *id, const void *pBuf, int amt, i64 offset){ - DO_OS_MALLOC_TEST; + DO_OS_MALLOC_TEST(id); return id->pMethods->xWrite(id, pBuf, amt, offset); } SQLITE_PRIVATE int sqlite3OsTruncate(sqlite3_file *id, i64 size){ return id->pMethods->xTruncate(id, size); } SQLITE_PRIVATE int sqlite3OsSync(sqlite3_file *id, int flags){ - DO_OS_MALLOC_TEST; + DO_OS_MALLOC_TEST(id); return id->pMethods->xSync(id, flags); } SQLITE_PRIVATE int sqlite3OsFileSize(sqlite3_file *id, i64 *pSize){ - DO_OS_MALLOC_TEST; + DO_OS_MALLOC_TEST(id); return id->pMethods->xFileSize(id, pSize); } SQLITE_PRIVATE int sqlite3OsLock(sqlite3_file *id, int lockType){ - DO_OS_MALLOC_TEST; + DO_OS_MALLOC_TEST(id); return id->pMethods->xLock(id, lockType); } SQLITE_PRIVATE int sqlite3OsUnlock(sqlite3_file *id, int lockType){ return id->pMethods->xUnlock(id, lockType); } SQLITE_PRIVATE int sqlite3OsCheckReservedLock(sqlite3_file *id, int *pResOut){ - DO_OS_MALLOC_TEST; + DO_OS_MALLOC_TEST(id); return id->pMethods->xCheckReservedLock(id, pResOut); } SQLITE_PRIVATE int sqlite3OsFileControl(sqlite3_file *id, int op, void *pArg){ @@ -11900,8 +12136,12 @@ int *pFlagsOut ){ int rc; - DO_OS_MALLOC_TEST; - rc = pVfs->xOpen(pVfs, zPath, pFile, flags, pFlagsOut); + DO_OS_MALLOC_TEST(0); + /* 0x7f1f is a mask of SQLITE_OPEN_ flags that are valid to be passed + ** down into the VFS layer. Some SQLITE_OPEN_ flags (for example, + ** SQLITE_OPEN_FULLMUTEX or SQLITE_OPEN_SHAREDCACHE) are blocked before + ** reaching the VFS. */ + rc = pVfs->xOpen(pVfs, zPath, pFile, flags & 0x7f1f, pFlagsOut); assert( rc==SQLITE_OK || pFile->pMethods==0 ); return rc; } @@ -11914,7 +12154,7 @@ int flags, int *pResOut ){ - DO_OS_MALLOC_TEST; + DO_OS_MALLOC_TEST(0); return pVfs->xAccess(pVfs, zPath, flags, pResOut); } SQLITE_PRIVATE int sqlite3OsFullPathname( @@ -11923,6 +12163,7 @@ int nPathOut, char *zPathOut ){ + zPathOut[0] = 0; return pVfs->xFullPathname(pVfs, zPath, nPathOut, zPathOut); } #ifndef SQLITE_OMIT_LOAD_EXTENSION @@ -11978,6 +12219,19 @@ } /* +** This function is a wrapper around the OS specific implementation of +** sqlite3_os_init(). The purpose of the wrapper is to provide the +** ability to simulate a malloc failure, so that the handling of an +** error in sqlite3_os_init() by the upper layers can be tested. +*/ +SQLITE_PRIVATE int sqlite3OsInit(void){ + void *p = sqlite3_malloc(10); + if( p==0 ) return SQLITE_NOMEM; + sqlite3_free(p); + return sqlite3_os_init(); +} + +/* ** The list of all registered VFS implementations. */ static sqlite3_vfs * SQLITE_WSD vfsList = 0; @@ -12081,10 +12335,6 @@ ** ************************************************************************* ** -** $Id: fault.c,v 1.11 2008/09/02 00:52:52 drh Exp $ -*/ - -/* ** This file contains code to support the concept of "benign" ** malloc failures (when the xMalloc() or xRealloc() method of the ** sqlite3_mem_methods structure fails to allocate a block of memory @@ -12179,8 +12429,6 @@ ** here always fail. SQLite will not operate with these drivers. These ** are merely placeholders. Real drivers must be substituted using ** sqlite3_config() before SQLite will operate. -** -** $Id: mem0.c,v 1.1 2008/10/28 18:58:20 drh Exp $ */ /* @@ -12243,8 +12491,6 @@ ** ** This file contains implementations of the low-level memory allocation ** routines specified in the sqlite3_mem_methods object. -** -** $Id: mem1.c,v 1.30 2009/03/23 04:33:33 danielk1977 Exp $ */ /* @@ -12392,8 +12638,6 @@ ** ** This file contains implementations of the low-level memory allocation ** routines specified in the sqlite3_mem_methods object. -** -** $Id: mem2.c,v 1.45 2009/03/23 04:33:33 danielk1977 Exp $ */ /* @@ -12585,6 +12829,31 @@ } /* +** Fill a buffer with pseudo-random bytes. This is used to preset +** the content of a new memory allocation to unpredictable values and +** to clear the content of a freed allocation to unpredictable values. +*/ +static void randomFill(char *pBuf, int nByte){ + unsigned int x, y, r; + x = SQLITE_PTR_TO_INT(pBuf); + y = nByte | 1; + while( nByte >= 4 ){ + x = (x>>1) ^ (-(x&1) & 0xd0000001); + y = y*1103515245 + 12345; + r = x ^ y; + *(int*)pBuf = r; + pBuf += 4; + nByte -= 4; + } + while( nByte-- > 0 ){ + x = (x>>1) ^ (-(x&1) & 0xd0000001); + y = y*1103515245 + 12345; + r = x ^ y; + *(pBuf++) = r & 0xff; + } +} + +/* ** Allocate nByte bytes of memory. */ static void *sqlite3MemMalloc(int nByte){ @@ -12634,7 +12903,8 @@ adjustStats(nByte, +1); pInt = (int*)&pHdr[1]; pInt[nReserve/sizeof(int)] = REARGUARD; - memset(pInt, 0x65, nReserve); + randomFill((char*)pInt, nByte); + memset(((char*)pInt)+nByte, 0x65, nReserve-nByte); p = (void*)pInt; } sqlite3_mutex_leave(mem.mutex); @@ -12670,8 +12940,8 @@ z = (char*)pBt; z -= pHdr->nTitle; adjustStats(pHdr->iSize, -1); - memset(z, 0x2b, sizeof(void*)*pHdr->nBacktraceSlots + sizeof(*pHdr) + - pHdr->iSize + sizeof(int) + pHdr->nTitle); + randomFill(z, sizeof(void*)*pHdr->nBacktraceSlots + sizeof(*pHdr) + + pHdr->iSize + sizeof(int) + pHdr->nTitle); free(z); sqlite3_mutex_leave(mem.mutex); } @@ -12694,7 +12964,7 @@ if( pNew ){ memcpy(pNew, pPrior, nByteiSize ? nByte : pOldHdr->iSize); if( nByte>pOldHdr->iSize ){ - memset(&((char*)pNew)[pOldHdr->iSize], 0x2b, nByte - pOldHdr->iSize); + randomFill(&((char*)pNew)[pOldHdr->iSize], nByte - pOldHdr->iSize); } sqlite3MemFree(pPrior); } @@ -12841,8 +13111,6 @@ ** ** This version of the memory allocation subsystem is included ** in the build only if SQLITE_ENABLE_MEMSYS3 is defined. -** -** $Id: mem3.c,v 1.25 2008/11/19 16:52:44 danielk1977 Exp $ */ /* @@ -13396,6 +13664,7 @@ */ static void memsys3Shutdown(void *NotUsed){ UNUSED_PARAMETER(NotUsed); + mem3.mutex = 0; return; } @@ -13522,7 +13791,7 @@ ** allocation subsystem for use by SQLite. ** ** This version of the memory allocation subsystem omits all -** use of malloc(). The SQLite user supplies a block of memory +** use of malloc(). The application gives SQLite a block of memory ** before calling sqlite3_initialize() from which allocations ** are made and returned by the xMalloc() and xRealloc() ** implementations. Once sqlite3_initialize() has been called, @@ -13532,7 +13801,30 @@ ** This version of the memory allocation subsystem is included ** in the build only if SQLITE_ENABLE_MEMSYS5 is defined. ** -** $Id: mem5.c,v 1.19 2008/11/19 16:52:44 danielk1977 Exp $ +** This memory allocator uses the following algorithm: +** +** 1. All memory allocations sizes are rounded up to a power of 2. +** +** 2. If two adjacent free blocks are the halves of a larger block, +** then the two blocks are coalesed into the single larger block. +** +** 3. New memory is allocated from the first available free block. +** +** This algorithm is described in: J. M. Robson. "Bounds for Some Functions +** Concerning Dynamic Storage Allocation". Journal of the Association for +** Computing Machinery, Volume 21, Number 8, July 1974, pages 491-499. +** +** Let n be the size of the largest allocation divided by the minimum +** allocation size (after rounding all sizes up to a power of 2.) Let M +** be the maximum amount of memory ever outstanding at one time. Let +** N be the total amount of memory available for allocation. Robson +** proved that this memory allocator will never breakdown due to +** fragmentation as long as the following constraint holds: +** +** N >= M*(1 + log2(n)/2) - n + 1 +** +** The sqlite3_status() logic tracks the maximum values of n and M so +** that an application can, at any time, verify this constraint. */ /* @@ -13545,6 +13837,9 @@ ** A minimum allocation is an instance of the following structure. ** Larger allocations are an array of these structures where the ** size of the array is a power of 2. +** +** The size of this object must be a power of two. That fact is +** verified in memsys5Init(). */ typedef struct Mem5Link Mem5Link; struct Mem5Link { @@ -13553,16 +13848,16 @@ }; /* -** Maximum size of any allocation is ((1<=0 && i0 ); + /* Keep track of the maximum allocation request. Even unfulfilled ** requests are counted */ if( (u32)nByte>mem5.maxRequest ){ mem5.maxRequest = nByte; } + /* Abort if the requested allocation size is larger than the largest + ** power of two that we can represent using 32-bit signed integers. + */ + if( nByte > 0x40000000 ){ + return 0; + } + /* Round nByte up to the next valid power of two */ - for(iFullSz=mem5.nAtom, iLogsize=0; iFullSz=0 && iBlock0 ); - assert( mem5.currentOut>=(size*mem5.nAtom) ); + assert( mem5.currentOut>=(size*mem5.szAtom) ); mem5.currentCount--; - mem5.currentOut -= size*mem5.nAtom; + mem5.currentOut -= size*mem5.szAtom; assert( mem5.currentOut>0 || mem5.currentCount==0 ); assert( mem5.currentCount>0 || mem5.currentOut==0 ); mem5.aCtrl[iBlock] = CTRL_FREE | iLogsize; - while( iLogsize>iLogsize) & 1 ){ iBuddy = iBlock - size; @@ -13824,28 +14141,36 @@ /* ** Free memory. +** +** The outer layer memory allocator prevents this routine from +** being called with pPrior==0. */ static void memsys5Free(void *pPrior){ - if( pPrior==0 ){ -assert(0); - return; - } + assert( pPrior!=0 ); memsys5Enter(); memsys5FreeUnsafe(pPrior); memsys5Leave(); } /* -** Change the size of an existing memory allocation +** Change the size of an existing memory allocation. +** +** The outer layer memory allocator prevents this routine from +** being called with pPrior==0. +** +** nBytes is always a value obtained from a prior call to +** memsys5Round(). Hence nBytes is always a non-negative power +** of two. If nBytes==0 that means that an oversize allocation +** (an allocation larger than 0x40000000) was requested and this +** routine should return 0 without freeing pPrior. */ static void *memsys5Realloc(void *pPrior, int nBytes){ int nOld; void *p; - if( pPrior==0 ){ - return memsys5Malloc(nBytes); - } - if( nBytes<=0 ){ - memsys5Free(pPrior); + assert( pPrior!=0 ); + assert( (nBytes&(nBytes-1))==0 ); + assert( nBytes>=0 ); + if( nBytes==0 ){ return 0; } nOld = memsys5Size(pPrior); @@ -13863,14 +14188,31 @@ } /* -** Round up a request size to the next valid allocation size. +** Round up a request size to the next valid allocation size. If +** the allocation is too large to be handled by this allocation system, +** return 0. +** +** All allocations must be a power of two and must be expressed by a +** 32-bit signed integer. Hence the largest allocation is 0x40000000 +** or 1073741824 bytes. */ static int memsys5Roundup(int n){ int iFullSz; - for(iFullSz=mem5.nAtom; iFullSz 0x40000000 ) return 0; + for(iFullSz=mem5.szAtom; iFullSz 0 +** memsys5Log(2) -> 1 +** memsys5Log(4) -> 2 +** memsys5Log(5) -> 3 +** memsys5Log(8) -> 3 +** memsys5Log(9) -> 4 +*/ static int memsys5Log(int iValue){ int iLog; for(iLog=0; (1<mem5.nAtom ){ - mem5.nAtom = mem5.nAtom << 1; + mem5.szAtom = (1<mem5.szAtom ){ + mem5.szAtom = mem5.szAtom << 1; } - mem5.nBlock = (nByte / (mem5.nAtom+sizeof(u8))); + mem5.nBlock = (nByte / (mem5.szAtom+sizeof(u8))); mem5.zPool = zByte; - mem5.aCtrl = (u8 *)&mem5.zPool[mem5.nBlock*mem5.nAtom]; + mem5.aCtrl = (u8 *)&mem5.zPool[mem5.nBlock*mem5.szAtom]; for(ii=0; ii<=LOGMAX; ii++){ mem5.aiFreelist[ii] = -1; @@ -13918,6 +14271,11 @@ assert((iOffset+nAlloc)>mem5.nBlock); } + /* If a mutex is required for normal operation, allocate one */ + if( sqlite3GlobalConfig.bMemstat==0 ){ + mem5.mutex = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MEM); + } + return SQLITE_OK; } @@ -13926,15 +14284,16 @@ */ static void memsys5Shutdown(void *NotUsed){ UNUSED_PARAMETER(NotUsed); + mem5.mutex = 0; return; } +#ifdef SQLITE_TEST /* ** Open the file indicated and write a log of all unfreed memory ** allocations into that log. */ SQLITE_PRIVATE void sqlite3Memsys5Dump(const char *zFilename){ -#ifdef SQLITE_DEBUG FILE *out; int i, j, n; int nMinLog; @@ -13950,10 +14309,10 @@ } } memsys5Enter(); - nMinLog = memsys5Log(mem5.nAtom); + nMinLog = memsys5Log(mem5.szAtom); for(i=0; i<=LOGMAX && i+nMinLog<32; i++){ for(n=0, j=mem5.aiFreelist[i]; j>=0; j = MEM5LINK(j)->next, n++){} - fprintf(out, "freelist items of size %d: %d\n", mem5.nAtom << i, n); + fprintf(out, "freelist items of size %d: %d\n", mem5.szAtom << i, n); } fprintf(out, "mem5.nAlloc = %llu\n", mem5.nAlloc); fprintf(out, "mem5.totalAlloc = %llu\n", mem5.totalAlloc); @@ -13969,10 +14328,8 @@ }else{ fclose(out); } -#else - UNUSED_PARAMETER(zFilename); -#endif } +#endif /* ** This routine is the only routine in this file with external @@ -14011,10 +14368,17 @@ ** This file contains the C functions that implement mutexes. ** ** This file contains code that is common across all mutex implementations. +*/ -** -** $Id: mutex.c,v 1.30 2009/02/17 16:29:11 danielk1977 Exp $ +#if defined(SQLITE_DEBUG) && !defined(SQLITE_MUTEX_OMIT) +/* +** For debugging purposes, record when the mutex subsystem is initialized +** and uninitialized so that we can assert() if there is an attempt to +** allocate a mutex while the system is uninitialized. */ +static SQLITE_WSD int mutexIsInit = 0; +#endif /* SQLITE_DEBUG */ + #ifndef SQLITE_MUTEX_OMIT /* @@ -14028,34 +14392,22 @@ ** install a mutex implementation via sqlite3_config() prior to ** sqlite3_initialize() being called. This block copies pointers to ** the default implementation into the sqlite3GlobalConfig structure. - ** - ** The danger is that although sqlite3_config() is not a threadsafe - ** API, sqlite3_initialize() is, and so multiple threads may be - ** attempting to run this function simultaneously. To guard write - ** access to the sqlite3GlobalConfig structure, the 'MASTER' static mutex - ** is obtained before modifying it. */ - sqlite3_mutex_methods *p = sqlite3DefaultMutex(); - sqlite3_mutex *pMaster = 0; - - rc = p->xMutexInit(); - if( rc==SQLITE_OK ){ - pMaster = p->xMutexAlloc(SQLITE_MUTEX_STATIC_MASTER); - assert(pMaster); - p->xMutexEnter(pMaster); - assert( sqlite3GlobalConfig.mutex.xMutexAlloc==0 - || sqlite3GlobalConfig.mutex.xMutexAlloc==p->xMutexAlloc - ); - if( !sqlite3GlobalConfig.mutex.xMutexAlloc ){ - sqlite3GlobalConfig.mutex = *p; - } - p->xMutexLeave(pMaster); - } - }else{ - rc = sqlite3GlobalConfig.mutex.xMutexInit(); + sqlite3_mutex_methods *pFrom = sqlite3DefaultMutex(); + sqlite3_mutex_methods *pTo = &sqlite3GlobalConfig.mutex; + + memcpy(pTo, pFrom, offsetof(sqlite3_mutex_methods, xMutexAlloc)); + memcpy(&pTo->xMutexFree, &pFrom->xMutexFree, + sizeof(*pTo) - offsetof(sqlite3_mutex_methods, xMutexFree)); + pTo->xMutexAlloc = pFrom->xMutexAlloc; } + rc = sqlite3GlobalConfig.mutex.xMutexInit(); } +#ifdef SQLITE_DEBUG + GLOBAL(int, mutexIsInit) = 1; +#endif + return rc; } @@ -14068,6 +14420,11 @@ if( sqlite3GlobalConfig.mutex.xMutexEnd ){ rc = sqlite3GlobalConfig.mutex.xMutexEnd(); } + +#ifdef SQLITE_DEBUG + GLOBAL(int, mutexIsInit) = 0; +#endif + return rc; } @@ -14085,6 +14442,7 @@ if( !sqlite3GlobalConfig.bCoreMutex ){ return 0; } + assert( GLOBAL(int, mutexIsInit) ); return sqlite3GlobalConfig.mutex.xMutexAlloc(id); } @@ -14144,7 +14502,7 @@ } #endif -#endif /* SQLITE_OMIT_MUTEX */ +#endif /* SQLITE_MUTEX_OMIT */ /************** End of mutex.c ***********************************************/ /************** Begin file mutex_noop.c **************************************/ @@ -14174,8 +14532,6 @@ ** If compiled with SQLITE_DEBUG, then additional logic is inserted ** that does error checking on mutexes to make sure they are being ** called correctly. -** -** $Id: mutex_noop.c,v 1.3 2008/12/05 17:17:08 drh Exp $ */ @@ -14348,8 +14704,6 @@ ** ************************************************************************* ** This file contains the C functions that implement mutexes for OS/2 -** -** $Id: mutex_os2.c,v 1.11 2008/11/22 19:50:54 pweilbacher Exp $ */ /* @@ -14497,6 +14851,39 @@ sqlite3_free( p ); } +#ifdef SQLITE_DEBUG +/* +** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are +** intended for use inside assert() statements. +*/ +static int os2MutexHeld(sqlite3_mutex *p){ + TID tid; + PID pid; + ULONG ulCount; + PTIB ptib; + if( p!=0 ) { + DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); + } else { + DosGetInfoBlocks(&ptib, NULL); + tid = ptib->tib_ptib2->tib2_ultid; + } + return p==0 || (p->nRef!=0 && p->owner==tid); +} +static int os2MutexNotheld(sqlite3_mutex *p){ + TID tid; + PID pid; + ULONG ulCount; + PTIB ptib; + if( p!= 0 ) { + DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); + } else { + DosGetInfoBlocks(&ptib, NULL); + tid = ptib->tib_ptib2->tib2_ultid; + } + return p==0 || p->nRef==0 || p->owner!=tid; +} +#endif + /* ** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt ** to enter a mutex. If another thread is already within the mutex, @@ -14557,39 +14944,6 @@ DosReleaseMutexSem(p->mutex); } -#ifdef SQLITE_DEBUG -/* -** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routine are -** intended for use inside assert() statements. -*/ -static int os2MutexHeld(sqlite3_mutex *p){ - TID tid; - PID pid; - ULONG ulCount; - PTIB ptib; - if( p!=0 ) { - DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); - } else { - DosGetInfoBlocks(&ptib, NULL); - tid = ptib->tib_ptib2->tib2_ultid; - } - return p==0 || (p->nRef!=0 && p->owner==tid); -} -static int os2MutexNotheld(sqlite3_mutex *p){ - TID tid; - PID pid; - ULONG ulCount; - PTIB ptib; - if( p!= 0 ) { - DosQueryMutexSem(p->mutex, &pid, &tid, &ulCount); - } else { - DosGetInfoBlocks(&ptib, NULL); - tid = ptib->tib_ptib2->tib2_ultid; - } - return p==0 || p->nRef==0 || p->owner!=tid; -} -#endif - SQLITE_PRIVATE sqlite3_mutex_methods *sqlite3DefaultMutex(void){ static sqlite3_mutex_methods sMutex = { os2MutexInit, @@ -14623,8 +14977,6 @@ ** ************************************************************************* ** This file contains the C functions that implement mutexes for pthreads -** -** $Id: mutex_unix.c,v 1.16 2008/12/08 18:19:18 drh Exp $ */ /* @@ -14703,6 +15055,7 @@ **
  • SQLITE_MUTEX_STATIC_MEM2 **
  • SQLITE_MUTEX_STATIC_PRNG **
  • SQLITE_MUTEX_STATIC_LRU +**
  • SQLITE_MUTEX_STATIC_LRU2 ** ** ** The first two constants cause sqlite3_mutex_alloc() to create @@ -14716,7 +15069,7 @@ ** might return such a mutex in response to SQLITE_MUTEX_FAST. ** ** The other allowed parameters to sqlite3_mutex_alloc() each return -** a pointer to a static preexisting mutex. Three static mutexes are +** a pointer to a static preexisting mutex. Six static mutexes are ** used by the current version of SQLite. Future versions of SQLite ** may add additional static mutexes. Static mutexes are for internal ** use by SQLite only. Applications that use SQLite mutexes should @@ -14953,8 +15306,6 @@ ** ************************************************************************* ** This file contains the C functions that implement mutexes for win32 -** -** $Id: mutex_w32.c,v 1.17 2009/06/01 17:10:22 shane Exp $ */ /* @@ -15035,13 +15386,14 @@ static int winMutexInit(void){ /* The first to increment to 1 does actual initialization */ - if( InterlockedIncrement(&winMutex_lock)==1 ){ + if( InterlockedCompareExchange(&winMutex_lock, 1, 0)==0 ){ int i; - for(i=0; i -**
  • SQLITE_MUTEX_FAST 0 -**
  • SQLITE_MUTEX_RECURSIVE 1 -**
  • SQLITE_MUTEX_STATIC_MASTER 2 -**
  • SQLITE_MUTEX_STATIC_MEM 3 -**
  • SQLITE_MUTEX_STATIC_PRNG 4 +**
  • SQLITE_MUTEX_FAST +**
  • SQLITE_MUTEX_RECURSIVE +**
  • SQLITE_MUTEX_STATIC_MASTER +**
  • SQLITE_MUTEX_STATIC_MEM +**
  • SQLITE_MUTEX_STATIC_MEM2 +**
  • SQLITE_MUTEX_STATIC_PRNG +**
  • SQLITE_MUTEX_STATIC_LRU +**
  • SQLITE_MUTEX_STATIC_LRU2 ** ** ** The first two constants cause sqlite3_mutex_alloc() to create @@ -15090,7 +15445,7 @@ ** might return such a mutex in response to SQLITE_MUTEX_FAST. ** ** The other allowed parameters to sqlite3_mutex_alloc() each return -** a pointer to a static preexisting mutex. Three static mutexes are +** a pointer to a static preexisting mutex. Six static mutexes are ** used by the current version of SQLite. Future versions of SQLite ** may add additional static mutexes. Static mutexes are for internal ** use by SQLite only. Applications that use SQLite mutexes should @@ -15119,7 +15474,7 @@ default: { assert( winMutex_isInit==1 ); assert( iType-2 >= 0 ); - assert( iType-2 < sizeof(winMutex_staticMutexes)/sizeof(winMutex_staticMutexes[0]) ); + assert( iType-2 < ArraySize(winMutex_staticMutexes) ); p = &winMutex_staticMutexes[iType-2]; p->id = iType; break; @@ -15236,8 +15591,6 @@ ************************************************************************* ** ** Memory allocation functions used throughout sqlite. -** -** $Id: malloc.c,v 1.64 2009/06/27 00:48:33 drh Exp $ */ /* @@ -15266,7 +15619,9 @@ }else{ iLimit = n; } +#ifndef SQLITE_OMIT_AUTOINIT sqlite3_initialize(); +#endif if( iLimit>0 ){ sqlite3MemoryAlarm(softHeapLimitEnforcer, 0, iLimit); }else{ @@ -15286,9 +15641,6 @@ SQLITE_API int sqlite3_release_memory(int n){ #ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT int nRet = 0; -#if 0 - nRet += sqlite3VdbeReleaseMemory(n); -#endif nRet += sqlite3PcacheReleaseMemory(n-nRet); return nRet; #else @@ -15311,13 +15663,11 @@ ** The alarm callback and its arguments. The mem0.mutex lock will ** be held while the callback is running. Recursive calls into ** the memory subsystem are allowed, but no new callbacks will be - ** issued. The alarmBusy variable is set to prevent recursive - ** callbacks. + ** issued. */ sqlite3_int64 alarmThreshold; void (*alarmCallback)(void*, sqlite3_int64,int); void *alarmArg; - int alarmBusy; /* ** Pointers to the end of sqlite3GlobalConfig.pScratch and @@ -15326,7 +15676,7 @@ */ u32 *aScratchFree; u32 *aPageFree; -} mem0 = { 62560955, 0, 0, 0, 0, 0, 0, 0, 0 }; +} mem0 = { 0, 0, 0, 0, 0, 0, 0, 0 }; #define mem0 GLOBAL(struct Mem0Global, mem0) @@ -15443,15 +15793,16 @@ void (*xCallback)(void*,sqlite3_int64,int); sqlite3_int64 nowUsed; void *pArg; - if( mem0.alarmCallback==0 || mem0.alarmBusy ) return; - mem0.alarmBusy = 1; + if( mem0.alarmCallback==0 ) return; xCallback = mem0.alarmCallback; nowUsed = sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED); pArg = mem0.alarmArg; + mem0.alarmCallback = 0; sqlite3_mutex_leave(mem0.mutex); xCallback(pArg, nowUsed, nByte); sqlite3_mutex_enter(mem0.mutex); - mem0.alarmBusy = 0; + mem0.alarmCallback = xCallback; + mem0.alarmArg = pArg; } /* @@ -15647,9 +15998,7 @@ } SQLITE_PRIVATE int sqlite3DbMallocSize(sqlite3 *db, void *p){ assert( db==0 || sqlite3_mutex_held(db->mutex) ); - if( p==0 ){ - return 0; - }else if( isLookaside(db, p) ){ + if( isLookaside(db, p) ){ return db->lookaside.sz; }else{ return sqlite3GlobalConfig.m.xSize(p); @@ -15705,30 +16054,28 @@ return 0; } nOld = sqlite3MallocSize(pOld); - if( sqlite3GlobalConfig.bMemstat ){ + nNew = sqlite3GlobalConfig.m.xRoundup(nBytes); + if( nOld==nNew ){ + pNew = pOld; + }else if( sqlite3GlobalConfig.bMemstat ){ sqlite3_mutex_enter(mem0.mutex); sqlite3StatusSet(SQLITE_STATUS_MALLOC_SIZE, nBytes); - nNew = sqlite3GlobalConfig.m.xRoundup(nBytes); - if( nOld==nNew ){ - pNew = pOld; - }else{ - if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nNew-nOld >= - mem0.alarmThreshold ){ - sqlite3MallocAlarm(nNew-nOld); - } + if( sqlite3StatusValue(SQLITE_STATUS_MEMORY_USED)+nNew-nOld >= + mem0.alarmThreshold ){ + sqlite3MallocAlarm(nNew-nOld); + } + pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); + if( pNew==0 && mem0.alarmCallback ){ + sqlite3MallocAlarm(nBytes); pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); - if( pNew==0 && mem0.alarmCallback ){ - sqlite3MallocAlarm(nBytes); - pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); - } - if( pNew ){ - nNew = sqlite3MallocSize(pNew); - sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nNew-nOld); - } + } + if( pNew ){ + nNew = sqlite3MallocSize(pNew); + sqlite3StatusAdd(SQLITE_STATUS_MEMORY_USED, nNew-nOld); } sqlite3_mutex_leave(mem0.mutex); }else{ - pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nBytes); + pNew = sqlite3GlobalConfig.m.xRealloc(pOld, nNew); } return pNew; } @@ -15949,8 +16296,6 @@ ** an historical reference. Most of the "enhancements" have been backed ** out so that the functionality is now the same as standard printf(). ** -** $Id: printf.c,v 1.104 2009/06/03 01:24:54 drh Exp $ -** ************************************************************************** ** ** The following modules is an enhanced replacement for the "printf" subroutines @@ -16590,14 +16935,15 @@ case etSQLESCAPE: case etSQLESCAPE2: case etSQLESCAPE3: { - int i, j, n, isnull; + int i, j, k, n, isnull; int needQuote; char ch; char q = ((xtype==etSQLESCAPE3)?'"':'\''); /* Quote character */ char *escarg = va_arg(ap,char*); isnull = escarg==0; if( isnull ) escarg = (xtype==etSQLESCAPE2 ? "NULL" : "(NULL)"); - for(i=n=0; (ch=escarg[i])!=0; i++){ + k = precision; + for(i=n=0; (ch=escarg[i])!=0 && k!=0; i++, k--){ if( ch==q ) n++; } needQuote = !isnull && xtype==etSQLESCAPE2; @@ -16613,15 +16959,17 @@ } j = 0; if( needQuote ) bufpt[j++] = q; - for(i=0; (ch=escarg[i])!=0; i++){ - bufpt[j++] = ch; + k = i; + for(i=0; i=0 && precision=0 && precision=0xD800 && c<0xE000 ){ \ + if( c>=0xD800 && c<0xE000 && TERM ){ \ int c2 = (*zIn++); \ c2 += ((*zIn++)<<8); \ c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \ } \ } -#define READ_UTF16BE(zIn, c){ \ +#define READ_UTF16BE(zIn, TERM, c){ \ c = ((*zIn++)<<8); \ c += (*zIn++); \ - if( c>=0xD800 && c<0xE000 ){ \ + if( c>=0xD800 && c<0xE000 && TERM ){ \ int c2 = ((*zIn++)<<8); \ c2 += (*zIn++); \ c = (c2&0x03FF) + ((c&0x003F)<<10) + (((c&0x03C0)+0x0040)<<10); \ @@ -17748,13 +18129,13 @@ if( pMem->enc==SQLITE_UTF16LE ){ /* UTF-16 Little-endian -> UTF-8 */ while( zIn UTF-8 */ while( zInmallocFailed ); + return 0; + } + assert( m.z==m.zMalloc ); + *pnOut = m.n; + return m.z; +} +#endif + +/* +** zIn is a UTF-16 encoded unicode string at least nChar characters long. ** Return the number of bytes in the first nChar unicode characters ** in pZ. nChar must be non-negative. */ @@ -17906,23 +18313,15 @@ int c; unsigned char const *z = zIn; int n = 0; + if( SQLITE_UTF16NATIVE==SQLITE_UTF16BE ){ - /* Using an "if (SQLITE_UTF16NATIVE==SQLITE_UTF16BE)" construct here - ** and in other parts of this file means that at one branch will - ** not be covered by coverage testing on any single host. But coverage - ** will be complete if the tests are run on both a little-endian and - ** big-endian host. Because both the UTF16NATIVE and SQLITE_UTF16BE - ** macros are constant at compile time the compiler can determine - ** which branch will be followed. It is therefore assumed that no runtime - ** penalty is paid for this "if" statement. - */ while( n0 && n<=4 ); z[0] = 0; z = zBuf; - READ_UTF16LE(z, c); + READ_UTF16LE(z, 1, c); assert( c==i ); assert( (z-zBuf)==n ); } @@ -17976,7 +18375,7 @@ assert( n>0 && n<=4 ); z[0] = 0; z = zBuf; - READ_UTF16BE(z, c); + READ_UTF16BE(z, 1, c); assert( c==i ); assert( (z-zBuf)==n ); } @@ -18002,7 +18401,6 @@ ** This file contains functions for allocating memory, comparing ** strings, and stuff like that. ** -** $Id: util.c,v 1.261 2009/06/24 10:26:33 drh Exp $ */ #ifdef SQLITE_HAVE_ISNAN # include @@ -18211,7 +18609,7 @@ while( *a!=0 && UpperToLower[*a]==UpperToLower[*b]){ a++; b++; } return UpperToLower[*a] - UpperToLower[*b]; } -SQLITE_PRIVATE int sqlite3StrNICmp(const char *zLeft, const char *zRight, int N){ +SQLITE_API int sqlite3_strnicmp(const char *zLeft, const char *zRight, int N){ register unsigned char *a, *b; a = (unsigned char *)zLeft; b = (unsigned char *)zRight; @@ -18259,7 +18657,7 @@ } /* -** The string z[] is an ascii representation of a real number. +** The string z[] is an ASCII representation of a real number. ** Convert this string to a double. ** ** This routine assumes that z[] really is a valid number. If it @@ -18272,70 +18670,126 @@ */ SQLITE_PRIVATE int sqlite3AtoF(const char *z, double *pResult){ #ifndef SQLITE_OMIT_FLOATING_POINT - int sign = 1; const char *zBegin = z; - LONGDOUBLE_TYPE v1 = 0.0; - int nSignificant = 0; + /* sign * significand * (10 ^ (esign * exponent)) */ + int sign = 1; /* sign of significand */ + i64 s = 0; /* significand */ + int d = 0; /* adjust exponent for shifting decimal point */ + int esign = 1; /* sign of exponent */ + int e = 0; /* exponent */ + double result; + int nDigits = 0; + + /* skip leading spaces */ while( sqlite3Isspace(*z) ) z++; + /* get sign of significand */ if( *z=='-' ){ sign = -1; z++; }else if( *z=='+' ){ z++; } - while( z[0]=='0' ){ - z++; - } - while( sqlite3Isdigit(*z) ){ - v1 = v1*10.0 + (*z - '0'); - z++; - nSignificant++; - } + /* skip leading zeroes */ + while( z[0]=='0' ) z++, nDigits++; + + /* copy max significant digits to significand */ + while( sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ + s = s*10 + (*z - '0'); + z++, nDigits++; + } + /* skip non-significant significand digits + ** (increase exponent by d to shift decimal left) */ + while( sqlite3Isdigit(*z) ) z++, nDigits++, d++; + + /* if decimal point is present */ if( *z=='.' ){ - LONGDOUBLE_TYPE divisor = 1.0; z++; - if( nSignificant==0 ){ - while( z[0]=='0' ){ - divisor *= 10.0; - z++; - } - } - while( sqlite3Isdigit(*z) ){ - if( nSignificant<18 ){ - v1 = v1*10.0 + (*z - '0'); - divisor *= 10.0; - nSignificant++; - } - z++; + /* copy digits from after decimal to significand + ** (decrease exponent by d to shift decimal right) */ + while( sqlite3Isdigit(*z) && s<((LARGEST_INT64-9)/10) ){ + s = s*10 + (*z - '0'); + z++, nDigits++, d--; } - v1 /= divisor; + /* skip non-significant digits */ + while( sqlite3Isdigit(*z) ) z++, nDigits++; } + + /* if exponent is present */ if( *z=='e' || *z=='E' ){ - int esign = 1; - int eval = 0; - LONGDOUBLE_TYPE scale = 1.0; z++; + /* get sign of exponent */ if( *z=='-' ){ esign = -1; z++; }else if( *z=='+' ){ z++; } + /* copy digits to exponent */ while( sqlite3Isdigit(*z) ){ - eval = eval*10 + *z - '0'; + e = e*10 + (*z - '0'); z++; } - while( eval>=64 ){ scale *= 1.0e+64; eval -= 64; } - while( eval>=16 ){ scale *= 1.0e+16; eval -= 16; } - while( eval>=4 ){ scale *= 1.0e+4; eval -= 4; } - while( eval>=1 ){ scale *= 1.0e+1; eval -= 1; } - if( esign<0 ){ - v1 /= scale; - }else{ - v1 *= scale; + } + + /* adjust exponent by d, and update sign */ + e = (e*esign) + d; + if( e<0 ) { + esign = -1; + e *= -1; + } else { + esign = 1; + } + + /* if 0 significand */ + if( !s ) { + /* In the IEEE 754 standard, zero is signed. + ** Add the sign if we've seen at least one digit */ + result = (sign<0 && nDigits) ? -(double)0 : (double)0; + } else { + /* attempt to reduce exponent */ + if( esign>0 ){ + while( s<(LARGEST_INT64/10) && e>0 ) e--,s*=10; + }else{ + while( !(s%10) && e>0 ) e--,s/=10; + } + + /* adjust the sign of significand */ + s = sign<0 ? -s : s; + + /* if exponent, scale significand as appropriate + ** and store in result. */ + if( e ){ + double scale = 1.0; + /* attempt to handle extremely small/large numbers better */ + if( e>307 && e<342 ){ + while( e%308 ) { scale *= 1.0e+1; e -= 1; } + if( esign<0 ){ + result = s / scale; + result /= 1.0e+308; + }else{ + result = s * scale; + result *= 1.0e+308; + } + }else{ + /* 1.0e+22 is the largest power of 10 than can be + ** represented exactly. */ + while( e%22 ) { scale *= 1.0e+1; e -= 1; } + while( e>0 ) { scale *= 1.0e+22; e -= 22; } + if( esign<0 ){ + result = s / scale; + }else{ + result = s * scale; + } + } + } else { + result = (double)s; } } - *pResult = (double)(sign<0 ? -v1 : v1); + + /* store the result */ + *pResult = result; + + /* return number of characters used */ return (int)(z - zBegin); #else return sqlite3Atoi64(z, pResult); @@ -18890,7 +19344,7 @@ #if !defined(SQLITE_OMIT_BLOB_LITERAL) || defined(SQLITE_HAS_CODEC) /* ** Translate a single byte of Hex into an integer. -** This routinen only works if h really is a valid hexadecimal +** This routine only works if h really is a valid hexadecimal ** character: 0..9a..fA..F */ static u8 hexToInt(int h){ @@ -19039,8 +19493,6 @@ ************************************************************************* ** This is the implementation of generic hash-tables ** used in SQLite. -** -** $Id: hash.c,v 1.38 2009/05/09 23:29:12 drh Exp $ */ /* Turn bulk memory into a hash table object by initializing the @@ -19311,140 +19763,140 @@ #if !defined(SQLITE_OMIT_EXPLAIN) || !defined(NDEBUG) || defined(VDBE_PROFILE) || defined(SQLITE_DEBUG) SQLITE_PRIVATE const char *sqlite3OpcodeName(int i){ static const char *const azName[] = { "?", - /* 1 */ "VNext", - /* 2 */ "Affinity", - /* 3 */ "Column", - /* 4 */ "SetCookie", - /* 5 */ "Seek", - /* 6 */ "Sequence", - /* 7 */ "Savepoint", - /* 8 */ "RowKey", - /* 9 */ "SCopy", - /* 10 */ "OpenWrite", - /* 11 */ "If", - /* 12 */ "CollSeq", - /* 13 */ "OpenRead", - /* 14 */ "Expire", - /* 15 */ "AutoCommit", - /* 16 */ "Pagecount", - /* 17 */ "IntegrityCk", - /* 18 */ "Sort", + /* 1 */ "Goto", + /* 2 */ "Gosub", + /* 3 */ "Return", + /* 4 */ "Yield", + /* 5 */ "HaltIfNull", + /* 6 */ "Halt", + /* 7 */ "Integer", + /* 8 */ "Int64", + /* 9 */ "String", + /* 10 */ "Null", + /* 11 */ "Blob", + /* 12 */ "Variable", + /* 13 */ "Move", + /* 14 */ "Copy", + /* 15 */ "SCopy", + /* 16 */ "ResultRow", + /* 17 */ "CollSeq", + /* 18 */ "Function", /* 19 */ "Not", - /* 20 */ "Copy", - /* 21 */ "Trace", - /* 22 */ "Function", - /* 23 */ "IfNeg", - /* 24 */ "Noop", - /* 25 */ "Return", - /* 26 */ "NewRowid", - /* 27 */ "Variable", - /* 28 */ "String", - /* 29 */ "RealAffinity", - /* 30 */ "VRename", - /* 31 */ "ParseSchema", - /* 32 */ "VOpen", - /* 33 */ "Close", - /* 34 */ "CreateIndex", - /* 35 */ "IsUnique", - /* 36 */ "NotFound", - /* 37 */ "Int64", - /* 38 */ "MustBeInt", - /* 39 */ "Halt", - /* 40 */ "Rowid", - /* 41 */ "IdxLT", - /* 42 */ "AddImm", - /* 43 */ "Statement", - /* 44 */ "RowData", - /* 45 */ "MemMax", - /* 46 */ "NotExists", - /* 47 */ "Gosub", - /* 48 */ "Integer", - /* 49 */ "Prev", - /* 50 */ "RowSetRead", - /* 51 */ "RowSetAdd", - /* 52 */ "VColumn", - /* 53 */ "CreateTable", - /* 54 */ "Last", - /* 55 */ "SeekLe", - /* 56 */ "IncrVacuum", - /* 57 */ "IdxRowid", - /* 58 */ "ResetCount", - /* 59 */ "ContextPush", - /* 60 */ "Yield", - /* 61 */ "DropTrigger", - /* 62 */ "DropIndex", - /* 63 */ "IdxGE", - /* 64 */ "IdxDelete", - /* 65 */ "Vacuum", - /* 66 */ "Or", - /* 67 */ "And", - /* 68 */ "IfNot", - /* 69 */ "DropTable", - /* 70 */ "SeekLt", - /* 71 */ "IsNull", - /* 72 */ "NotNull", - /* 73 */ "Ne", - /* 74 */ "Eq", - /* 75 */ "Gt", - /* 76 */ "Le", - /* 77 */ "Lt", - /* 78 */ "Ge", - /* 79 */ "MakeRecord", - /* 80 */ "BitAnd", - /* 81 */ "BitOr", - /* 82 */ "ShiftLeft", - /* 83 */ "ShiftRight", - /* 84 */ "Add", - /* 85 */ "Subtract", - /* 86 */ "Multiply", - /* 87 */ "Divide", - /* 88 */ "Remainder", - /* 89 */ "Concat", - /* 90 */ "ResultRow", - /* 91 */ "Delete", - /* 92 */ "AggFinal", + /* 20 */ "AddImm", + /* 21 */ "MustBeInt", + /* 22 */ "RealAffinity", + /* 23 */ "Permutation", + /* 24 */ "Compare", + /* 25 */ "Jump", + /* 26 */ "If", + /* 27 */ "IfNot", + /* 28 */ "Column", + /* 29 */ "Affinity", + /* 30 */ "MakeRecord", + /* 31 */ "Count", + /* 32 */ "Savepoint", + /* 33 */ "AutoCommit", + /* 34 */ "Transaction", + /* 35 */ "ReadCookie", + /* 36 */ "SetCookie", + /* 37 */ "VerifyCookie", + /* 38 */ "OpenRead", + /* 39 */ "OpenWrite", + /* 40 */ "OpenEphemeral", + /* 41 */ "OpenPseudo", + /* 42 */ "Close", + /* 43 */ "SeekLt", + /* 44 */ "SeekLe", + /* 45 */ "SeekGe", + /* 46 */ "SeekGt", + /* 47 */ "Seek", + /* 48 */ "NotFound", + /* 49 */ "Found", + /* 50 */ "IsUnique", + /* 51 */ "NotExists", + /* 52 */ "Sequence", + /* 53 */ "NewRowid", + /* 54 */ "Insert", + /* 55 */ "InsertInt", + /* 56 */ "Delete", + /* 57 */ "ResetCount", + /* 58 */ "RowKey", + /* 59 */ "RowData", + /* 60 */ "Rowid", + /* 61 */ "NullRow", + /* 62 */ "Last", + /* 63 */ "Sort", + /* 64 */ "Rewind", + /* 65 */ "Prev", + /* 66 */ "Next", + /* 67 */ "IdxInsert", + /* 68 */ "Or", + /* 69 */ "And", + /* 70 */ "IdxDelete", + /* 71 */ "IdxRowid", + /* 72 */ "IdxLT", + /* 73 */ "IsNull", + /* 74 */ "NotNull", + /* 75 */ "Ne", + /* 76 */ "Eq", + /* 77 */ "Gt", + /* 78 */ "Le", + /* 79 */ "Lt", + /* 80 */ "Ge", + /* 81 */ "IdxGE", + /* 82 */ "BitAnd", + /* 83 */ "BitOr", + /* 84 */ "ShiftLeft", + /* 85 */ "ShiftRight", + /* 86 */ "Add", + /* 87 */ "Subtract", + /* 88 */ "Multiply", + /* 89 */ "Divide", + /* 90 */ "Remainder", + /* 91 */ "Concat", + /* 92 */ "Destroy", /* 93 */ "BitNot", /* 94 */ "String8", - /* 95 */ "Compare", - /* 96 */ "Goto", - /* 97 */ "TableLock", - /* 98 */ "Clear", - /* 99 */ "VerifyCookie", - /* 100 */ "AggStep", - /* 101 */ "SetNumColumns", - /* 102 */ "Transaction", - /* 103 */ "VFilter", - /* 104 */ "VDestroy", - /* 105 */ "ContextPop", - /* 106 */ "Next", - /* 107 */ "Count", - /* 108 */ "IdxInsert", - /* 109 */ "SeekGe", - /* 110 */ "Insert", - /* 111 */ "Destroy", - /* 112 */ "ReadCookie", - /* 113 */ "RowSetTest", - /* 114 */ "LoadAnalysis", - /* 115 */ "Explain", - /* 116 */ "HaltIfNull", - /* 117 */ "OpenPseudo", - /* 118 */ "OpenEphemeral", - /* 119 */ "Null", - /* 120 */ "Move", - /* 121 */ "Blob", - /* 122 */ "Rewind", - /* 123 */ "SeekGt", - /* 124 */ "VBegin", - /* 125 */ "VUpdate", - /* 126 */ "IfZero", - /* 127 */ "VCreate", - /* 128 */ "Found", - /* 129 */ "IfPos", + /* 95 */ "Clear", + /* 96 */ "CreateIndex", + /* 97 */ "CreateTable", + /* 98 */ "ParseSchema", + /* 99 */ "LoadAnalysis", + /* 100 */ "DropTable", + /* 101 */ "DropIndex", + /* 102 */ "DropTrigger", + /* 103 */ "IntegrityCk", + /* 104 */ "RowSetAdd", + /* 105 */ "RowSetRead", + /* 106 */ "RowSetTest", + /* 107 */ "Program", + /* 108 */ "Param", + /* 109 */ "FkCounter", + /* 110 */ "FkIfZero", + /* 111 */ "MemMax", + /* 112 */ "IfPos", + /* 113 */ "IfNeg", + /* 114 */ "IfZero", + /* 115 */ "AggStep", + /* 116 */ "AggFinal", + /* 117 */ "Vacuum", + /* 118 */ "IncrVacuum", + /* 119 */ "Expire", + /* 120 */ "TableLock", + /* 121 */ "VBegin", + /* 122 */ "VCreate", + /* 123 */ "VDestroy", + /* 124 */ "VOpen", + /* 125 */ "VFilter", + /* 126 */ "VColumn", + /* 127 */ "VNext", + /* 128 */ "VRename", + /* 129 */ "VUpdate", /* 130 */ "Real", - /* 131 */ "NullRow", - /* 132 */ "Jump", - /* 133 */ "Permutation", - /* 134 */ "NotUsed_134", + /* 131 */ "Pagecount", + /* 132 */ "Trace", + /* 133 */ "Noop", + /* 134 */ "Explain", /* 135 */ "NotUsed_135", /* 136 */ "NotUsed_136", /* 137 */ "NotUsed_137", @@ -19476,8 +19928,6 @@ ****************************************************************************** ** ** This file contains code that is specific to OS/2. -** -** $Id: os_os2.c,v 1.63 2008/12/10 19:26:24 drh Exp $ */ @@ -19539,8 +19989,6 @@ ** ** This file should be #included by the os_*.c files only. It is not a ** general purpose header file. -** -** $Id: os_common.h,v 1.38 2009/02/24 18:40:50 danielk1977 Exp $ */ #ifndef _OS_COMMON_H_ #define _OS_COMMON_H_ @@ -19601,8 +20049,6 @@ ** ** This file contains inline asm code for retrieving "high-performance" ** counters for x86 class CPUs. -** -** $Id: hwtime.h,v 1.3 2008/08/01 14:33:15 shane Exp $ */ #ifndef _HWTIME_H_ #define _HWTIME_H_ @@ -20859,8 +21305,6 @@ ** * Locking primitives for the proxy uber-locking-method. (MacOSX only) ** * Definitions of sqlite3_vfs objects for all locking methods ** plus implementations of sqlite3_os_init() and sqlite3_os_end(). -** -** $Id: os_unix.c,v 1.253 2009/06/17 13:09:39 drh Exp $ */ #if SQLITE_OS_UNIX /* This file is used on unix only */ @@ -20984,6 +21428,19 @@ /* +** Sometimes, after a file handle is closed by SQLite, the file descriptor +** cannot be closed immediately. In these cases, instances of the following +** structure are used to store the file descriptor while waiting for an +** opportunity to either close or reuse it. +*/ +typedef struct UnixUnusedFd UnixUnusedFd; +struct UnixUnusedFd { + int fd; /* File descriptor to close */ + int flags; /* Flags this file descriptor was opened with */ + UnixUnusedFd *pNext; /* Next unused file descriptor on same file */ +}; + +/* ** The unixFile structure is subclass of sqlite3_file specific to the unix ** VFS implementations. */ @@ -20997,6 +21454,8 @@ unsigned char locktype; /* The type of lock held on this fd */ int lastErrno; /* The unix errno from the last I/O error */ void *lockingContext; /* Locking style specific state */ + UnixUnusedFd *pUnused; /* Pre-allocated UnixUnusedFd */ + int fileFlags; /* Miscellanous flags */ #if SQLITE_ENABLE_LOCKING_STYLE int openFlags; /* The flags specified at open() */ #endif @@ -21018,11 +21477,6 @@ unsigned char transCntrChng; /* True if the transaction counter changed */ unsigned char dbUpdate; /* True if any part of database file changed */ unsigned char inNormalWrite; /* True if in a normal write operation */ - - /* If true, that means we are dealing with a database file that has - ** a range of locking bytes from PENDING_BYTE through PENDING_BYTE+511 - ** which should never be read or written. Asserts() will verify this */ - unsigned char isLockable; /* True if file might be locked */ #endif #ifdef SQLITE_TEST /* In test mode, increase the size of this structure a bit so that @@ -21033,6 +21487,11 @@ }; /* +** The following macros define bits in unixFile.fileFlags +*/ +#define SQLITE_WHOLE_FILE_LOCKING 0x0001 /* Use whole-file locking */ + +/* ** Include code that is common to all os_*.c files */ /************** Include os_common.h in the middle of os_unix.c ***************/ @@ -21055,8 +21514,6 @@ ** ** This file should be #included by the os_*.c files only. It is not a ** general purpose header file. -** -** $Id: os_common.h,v 1.38 2009/02/24 18:40:50 danielk1977 Exp $ */ #ifndef _OS_COMMON_H_ #define _OS_COMMON_H_ @@ -21117,8 +21574,6 @@ ** ** This file contains inline asm code for retrieving "high-performance" ** counters for x86 class CPUs. -** -** $Id: hwtime.h,v 1.3 2008/08/01 14:33:15 shane Exp $ */ #ifndef _HWTIME_H_ #define _HWTIME_H_ @@ -21299,7 +21754,18 @@ /* -** Helper functions to obtain and relinquish the global mutex. +** Helper functions to obtain and relinquish the global mutex. The +** global mutex is used to protect the unixOpenCnt, unixLockInfo and +** vxworksFileId objects used by this file, all of which may be +** shared by multiple threads. +** +** Function unixMutexHeld() is used to assert() that the global mutex +** is held when required. This function is only used as part of assert() +** statements. e.g. +** +** unixEnterMutex() +** assert( unixMutexHeld() ); +** unixEnterLeave() */ static void unixEnterMutex(void){ sqlite3_mutex_enter(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); @@ -21307,6 +21773,11 @@ static void unixLeaveMutex(void){ sqlite3_mutex_leave(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); } +#ifdef SQLITE_DEBUG +static int unixMutexHeld(void) { + return sqlite3_mutex_held(sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER)); +} +#endif #ifdef SQLITE_DEBUG @@ -21317,11 +21788,11 @@ */ static const char *locktypeName(int locktype){ switch( locktype ){ - case NO_LOCK: return "NONE"; - case SHARED_LOCK: return "SHARED"; - case RESERVED_LOCK: return "RESERVED"; - case PENDING_LOCK: return "PENDING"; - case EXCLUSIVE_LOCK: return "EXCLUSIVE"; + case NO_LOCK: return "NONE"; + case SHARED_LOCK: return "SHARED"; + case RESERVED_LOCK: return "RESERVED"; + case PENDING_LOCK: return "PENDING"; + case EXCLUSIVE_LOCK: return "EXCLUSIVE"; } return "ERROR"; } @@ -21775,11 +22246,10 @@ struct unixFileId fileId; /* The lookup key */ int nRef; /* Number of pointers to this structure */ int nLock; /* Number of outstanding locks */ - int nPending; /* Number of pending close() operations */ - int *aPending; /* Malloced space holding fd's awaiting a close() */ + UnixUnusedFd *pUnused; /* Unused file descriptors to close */ #if OS_VXWORKS sem_t *pSem; /* Named POSIX semaphore */ - char aSemName[MAX_PATHNAME+1]; /* Name of that semaphore */ + char aSemName[MAX_PATHNAME+2]; /* Name of that semaphore */ #endif struct unixOpenCnt *pNext, *pPrev; /* List of all unixOpenCnt objects */ }; @@ -21876,18 +22346,23 @@ d.fd = fd; d.lock = l; d.lock.l_type = F_WRLCK; - pthread_create(&t, 0, threadLockingTest, &d); - pthread_join(t, 0); + if( pthread_create(&t, 0, threadLockingTest, &d)==0 ){ + pthread_join(t, 0); + } close(fd); if( d.result!=0 ) return; threadsOverrideEachOthersLocks = (d.lock.l_type==F_UNLCK); } -#endif /* SQLITE_THERADSAFE && defined(__linux__) */ +#endif /* SQLITE_THREADSAFE && defined(__linux__) */ /* ** Release a unixLockInfo structure previously allocated by findLockInfo(). +** +** The mutex entered using the unixEnterMutex() function must be held +** when this function is called. */ static void releaseLockInfo(struct unixLockInfo *pLock){ + assert( unixMutexHeld() ); if( pLock ){ pLock->nRef--; if( pLock->nRef==0 ){ @@ -21909,8 +22384,12 @@ /* ** Release a unixOpenCnt structure previously allocated by findLockInfo(). +** +** The mutex entered using the unixEnterMutex() function must be held +** when this function is called. */ static void releaseOpenCnt(struct unixOpenCnt *pOpen){ + assert( unixMutexHeld() ); if( pOpen ){ pOpen->nRef--; if( pOpen->nRef==0 ){ @@ -21925,7 +22404,17 @@ assert( pOpen->pNext->pPrev==pOpen ); pOpen->pNext->pPrev = pOpen->pPrev; } - sqlite3_free(pOpen->aPending); +#if SQLITE_THREADSAFE && defined(__linux__) + assert( !pOpen->pUnused || threadsOverrideEachOthersLocks==0 ); +#endif + + /* If pOpen->pUnused is not null, then memory and file-descriptors + ** are leaked. + ** + ** This will only happen if, under Linuxthreads, the user has opened + ** a transaction in one thread, then attempts to close the database + ** handle from another thread (without first unlocking the db file). + ** This is a misuse. */ sqlite3_free(pOpen); } } @@ -21936,6 +22425,9 @@ ** describes that file descriptor. Create new ones if necessary. The ** return values might be uninitialized if an error occurs. ** +** The mutex entered using the unixEnterMutex() function must be held +** when this function is called. +** ** Return an appropriate error code. */ static int findLockInfo( @@ -21951,6 +22443,8 @@ struct unixLockInfo *pLock = 0;/* Candidate unixLockInfo object */ struct unixOpenCnt *pOpen; /* Candidate unixOpenCnt object */ + assert( unixMutexHeld() ); + /* Get low-level information about the file that we can used to ** create a unique name for the file. */ @@ -22013,7 +22507,7 @@ rc = SQLITE_NOMEM; goto exit_findlockinfo; } - pLock->lockKey = lockKey; + memcpy(&pLock->lockKey,&lockKey,sizeof(lockKey)); pLock->nRef = 1; pLock->cnt = 0; pLock->locktype = 0; @@ -22038,19 +22532,12 @@ rc = SQLITE_NOMEM; goto exit_findlockinfo; } + memset(pOpen, 0, sizeof(*pOpen)); pOpen->fileId = fileId; pOpen->nRef = 1; - pOpen->nLock = 0; - pOpen->nPending = 0; - pOpen->aPending = 0; pOpen->pNext = openList; - pOpen->pPrev = 0; if( openList ) openList->pPrev = pOpen; openList = pOpen; -#if OS_VXWORKS - pOpen->pSem = NULL; - pOpen->aSemName[0] = '\0'; -#endif }else{ pOpen->nRef++; } @@ -22151,13 +22638,69 @@ #endif unixLeaveMutex(); - OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved); + OSTRACE4("TEST WR-LOCK %d %d %d (unix)\n", pFile->h, rc, reserved); *pResOut = reserved; return rc; } /* +** Perform a file locking operation on a range of bytes in a file. +** The "op" parameter should be one of F_RDLCK, F_WRLCK, or F_UNLCK. +** Return 0 on success or -1 for failure. On failure, write the error +** code into *pErrcode. +** +** If the SQLITE_WHOLE_FILE_LOCKING bit is clear, then only lock +** the range of bytes on the locking page between SHARED_FIRST and +** SHARED_SIZE. If SQLITE_WHOLE_FILE_LOCKING is set, then lock all +** bytes from 0 up to but not including PENDING_BYTE, and all bytes +** that follow SHARED_FIRST. +** +** In other words, of SQLITE_WHOLE_FILE_LOCKING if false (the historical +** default case) then only lock a small range of bytes from SHARED_FIRST +** through SHARED_FIRST+SHARED_SIZE-1. But if SQLITE_WHOLE_FILE_LOCKING is +** true then lock every byte in the file except for PENDING_BYTE and +** RESERVED_BYTE. +** +** SQLITE_WHOLE_FILE_LOCKING=true overlaps SQLITE_WHOLE_FILE_LOCKING=false +** and so the locking schemes are compatible. One type of lock will +** effectively exclude the other type. The reason for using the +** SQLITE_WHOLE_FILE_LOCKING=true is that by indicating the full range +** of bytes to be read or written, we give hints to NFS to help it +** maintain cache coherency. On the other hand, whole file locking +** is slower, so we don't want to use it except for NFS. +*/ +static int rangeLock(unixFile *pFile, int op, int *pErrcode){ + struct flock lock; + int rc; + lock.l_type = op; + lock.l_start = SHARED_FIRST; + lock.l_whence = SEEK_SET; + if( (pFile->fileFlags & SQLITE_WHOLE_FILE_LOCKING)==0 ){ + lock.l_len = SHARED_SIZE; + rc = fcntl(pFile->h, F_SETLK, &lock); + *pErrcode = errno; + }else{ + lock.l_len = 0; + rc = fcntl(pFile->h, F_SETLK, &lock); + *pErrcode = errno; + if( NEVER(op==F_UNLCK) || rc!=(-1) ){ + lock.l_start = 0; + lock.l_len = PENDING_BYTE; + rc = fcntl(pFile->h, F_SETLK, &lock); + if( ALWAYS(op!=F_UNLCK) && rc==(-1) ){ + *pErrcode = errno; + lock.l_type = F_UNLCK; + lock.l_start = SHARED_FIRST; + lock.l_len = 0; + fcntl(pFile->h, F_SETLK, &lock); + } + } + } + return rc; +} + +/* ** Lock the file with the lock specified by parameter locktype - one ** of the following: ** @@ -22224,10 +22767,11 @@ unixFile *pFile = (unixFile*)id; struct unixLockInfo *pLock = pFile->pLock; struct flock lock; - int s; + int s = 0; + int tErrno; assert( pFile ); - OSTRACE7("LOCK %d %s was %s(%s,%d) pid=%d\n", pFile->h, + OSTRACE7("LOCK %d %s was %s(%s,%d) pid=%d (unix)\n", pFile->h, locktypeName(locktype), locktypeName(pFile->locktype), locktypeName(pLock->locktype), pLock->cnt , getpid()); @@ -22236,12 +22780,15 @@ ** unixEnterMutex() hasn't been called yet. */ if( pFile->locktype>=locktype ){ - OSTRACE3("LOCK %d %s ok (already held)\n", pFile->h, + OSTRACE3("LOCK %d %s ok (already held) (unix)\n", pFile->h, locktypeName(locktype)); return SQLITE_OK; } - /* Make sure the locking sequence is correct + /* Make sure the locking sequence is correct. + ** (1) We never move from unlocked to anything higher than shared lock. + ** (2) SQLite never explicitly requests a pendig lock. + ** (3) A shared lock is always held when a reserve lock is requested. */ assert( pFile->locktype!=NO_LOCK || locktype==SHARED_LOCK ); assert( locktype!=PENDING_LOCK ); @@ -22285,14 +22832,13 @@ goto end_lock; } - lock.l_len = 1L; - - lock.l_whence = SEEK_SET; /* A PENDING lock is needed before acquiring a SHARED lock and before ** acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will ** be released. */ + lock.l_len = 1L; + lock.l_whence = SEEK_SET; if( locktype==SHARED_LOCK || (locktype==EXCLUSIVE_LOCK && pFile->locktypeh, F_SETLK, &lock); if( s==(-1) ){ - int tErrno = errno; + tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; @@ -22314,16 +22860,12 @@ ** operating system calls for the specified lock. */ if( locktype==SHARED_LOCK ){ - int tErrno = 0; assert( pLock->cnt==0 ); assert( pLock->locktype==0 ); /* Now get the read-lock */ - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - if( (s = fcntl(pFile->h, F_SETLK, &lock))==(-1) ){ - tErrno = errno; - } + s = rangeLock(pFile, F_RDLCK, &tErrno); + /* Drop the temporary PENDING lock */ lock.l_start = PENDING_BYTE; lock.l_len = 1L; @@ -22363,17 +22905,16 @@ switch( locktype ){ case RESERVED_LOCK: lock.l_start = RESERVED_BYTE; + s = fcntl(pFile->h, F_SETLK, &lock); + tErrno = errno; break; case EXCLUSIVE_LOCK: - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; + s = rangeLock(pFile, F_WRLCK, &tErrno); break; default: assert(0); } - s = fcntl(pFile->h, F_SETLK, &lock); if( s==(-1) ){ - int tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; @@ -22409,12 +22950,55 @@ end_lock: unixLeaveMutex(); - OSTRACE4("LOCK %d %s %s\n", pFile->h, locktypeName(locktype), + OSTRACE4("LOCK %d %s %s (unix)\n", pFile->h, locktypeName(locktype), rc==SQLITE_OK ? "ok" : "failed"); return rc; } /* +** Close all file descriptors accumuated in the unixOpenCnt->pUnused list. +** If all such file descriptors are closed without error, the list is +** cleared and SQLITE_OK returned. +** +** Otherwise, if an error occurs, then successfully closed file descriptor +** entries are removed from the list, and SQLITE_IOERR_CLOSE returned. +** not deleted and SQLITE_IOERR_CLOSE returned. +*/ +static int closePendingFds(unixFile *pFile){ + int rc = SQLITE_OK; + struct unixOpenCnt *pOpen = pFile->pOpen; + UnixUnusedFd *pError = 0; + UnixUnusedFd *p; + UnixUnusedFd *pNext; + for(p=pOpen->pUnused; p; p=pNext){ + pNext = p->pNext; + if( close(p->fd) ){ + pFile->lastErrno = errno; + rc = SQLITE_IOERR_CLOSE; + p->pNext = pError; + pError = p; + }else{ + sqlite3_free(p); + } + } + pOpen->pUnused = pError; + return rc; +} + +/* +** Add the file descriptor used by file handle pFile to the corresponding +** pUnused list. +*/ +static void setPendingFd(unixFile *pFile){ + struct unixOpenCnt *pOpen = pFile->pOpen; + UnixUnusedFd *p = pFile->pUnused; + p->pNext = pOpen->pUnused; + pOpen->pUnused = p; + pFile->h = -1; + pFile->pUnused = 0; +} + +/* ** Lower the locking level on file descriptor pFile to locktype. locktype ** must be either NO_LOCK or SHARED_LOCK. ** @@ -22422,14 +23006,15 @@ ** the requested locking level, this routine is a no-op. */ static int unixUnlock(sqlite3_file *id, int locktype){ - struct unixLockInfo *pLock; - struct flock lock; - int rc = SQLITE_OK; - unixFile *pFile = (unixFile*)id; - int h; + unixFile *pFile = (unixFile*)id; /* The open file */ + struct unixLockInfo *pLock; /* Structure describing current lock state */ + struct flock lock; /* Information passed into fcntl() */ + int rc = SQLITE_OK; /* Return code from this interface */ + int h; /* The underlying file descriptor */ + int tErrno; /* Error code from system call errors */ assert( pFile ); - OSTRACE7("UNLOCK %d %d was %d(%d,%d) pid=%d\n", pFile->h, locktype, + OSTRACE7("UNLOCK %d %d was %d(%d,%d) pid=%d (unix)\n", pFile->h, locktype, pFile->locktype, pFile->pLock->locktype, pFile->pLock->cnt, getpid()); assert( locktype<=SHARED_LOCK ); @@ -22466,12 +23051,7 @@ if( locktype==SHARED_LOCK ){ - lock.l_type = F_RDLCK; - lock.l_whence = SEEK_SET; - lock.l_start = SHARED_FIRST; - lock.l_len = SHARED_SIZE; - if( fcntl(h, F_SETLK, &lock)==(-1) ){ - int tErrno = errno; + if( rangeLock(pFile, F_RDLCK, &tErrno)==(-1) ){ rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; @@ -22486,7 +23066,7 @@ if( fcntl(h, F_SETLK, &lock)!=(-1) ){ pLock->locktype = SHARED_LOCK; }else{ - int tErrno = errno; + tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; @@ -22496,7 +23076,6 @@ } if( locktype==NO_LOCK ){ struct unixOpenCnt *pOpen; - int rc2 = SQLITE_OK; /* Decrement the shared lock counter. Release the lock using an ** OS call only when all threads in this same process have released @@ -22513,7 +23092,7 @@ if( fcntl(h, F_SETLK, &lock)!=(-1) ){ pLock->locktype = NO_LOCK; }else{ - int tErrno = errno; + tErrno = errno; rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); if( IS_LOCK_ERROR(rc) ){ pFile->lastErrno = tErrno; @@ -22530,29 +23109,12 @@ pOpen = pFile->pOpen; pOpen->nLock--; assert( pOpen->nLock>=0 ); - if( pOpen->nLock==0 && pOpen->nPending>0 ){ - int i; - for(i=0; inPending; i++){ - /* close pending fds, but if closing fails don't free the array - ** assign -1 to the successfully closed descriptors and record the - ** error. The next attempt to unlock will try again. */ - if( pOpen->aPending[i] < 0 ) continue; - if( close(pOpen->aPending[i]) ){ - pFile->lastErrno = errno; - rc2 = SQLITE_IOERR_CLOSE; - }else{ - pOpen->aPending[i] = -1; - } - } - if( rc2==SQLITE_OK ){ - sqlite3_free(pOpen->aPending); - pOpen->nPending = 0; - pOpen->aPending = 0; + if( pOpen->nLock==0 ){ + int rc2 = closePendingFds(pFile); + if( rc==SQLITE_OK ){ + rc = rc2; } } - if( rc==SQLITE_OK ){ - rc = rc2; - } } end_unlock: @@ -22601,6 +23163,7 @@ #endif OSTRACE2("CLOSE %-3d\n", pFile->h); OpenCounter(-1); + sqlite3_free(pFile->pUnused); memset(pFile, 0, sizeof(unixFile)); } return SQLITE_OK; @@ -22618,20 +23181,10 @@ if( pFile->pOpen && pFile->pOpen->nLock ){ /* If there are outstanding locks, do not actually close the file just ** yet because that would clear those locks. Instead, add the file - ** descriptor to pOpen->aPending. It will be automatically closed when - ** the last lock is cleared. + ** descriptor to pOpen->pUnused list. It will be automatically closed + ** when the last lock is cleared. */ - int *aNew; - struct unixOpenCnt *pOpen = pFile->pOpen; - aNew = sqlite3_realloc(pOpen->aPending, (pOpen->nPending+1)*sizeof(int) ); - if( aNew==0 ){ - /* If a malloc fails, just leak the file descriptor */ - }else{ - pOpen->aPending = aNew; - pOpen->aPending[pOpen->nPending] = pFile->h; - pOpen->nPending++; - pFile->h = -1; - } + setPendingFd(pFile); } releaseLockInfo(pFile->pLock); releaseOpenCnt(pFile->pOpen); @@ -22688,7 +23241,7 @@ /****************************************************************************** ************************* Begin dot-file Locking ****************************** ** -** The dotfile locking implementation uses the existing of separate lock +** The dotfile locking implementation uses the existance of separate lock ** files in order to control access to the database. This works on just ** about every filesystem imaginable. But there are serious downsides: ** @@ -22742,7 +23295,7 @@ const char *zLockFile = (const char*)pFile->lockingContext; reserved = access(zLockFile, 0)==0; } - OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved); + OSTRACE4("TEST WR-LOCK %d %d %d (dotlock)\n", pFile->h, rc, reserved); *pResOut = reserved; return rc; } @@ -22832,7 +23385,7 @@ char *zLockFile = (char *)pFile->lockingContext; assert( pFile ); - OSTRACE5("UNLOCK %d %d was %d pid=%d\n", pFile->h, locktype, + OSTRACE5("UNLOCK %d %d was %d pid=%d (dotlock)\n", pFile->h, locktype, pFile->locktype, getpid()); assert( locktype<=SHARED_LOCK ); @@ -22946,7 +23499,7 @@ } } } - OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved); + OSTRACE4("TEST WR-LOCK %d %d %d (flock)\n", pFile->h, rc, reserved); #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){ @@ -23013,7 +23566,7 @@ /* got it, set the type and return ok */ pFile->locktype = locktype; } - OSTRACE4("LOCK %d %s %s\n", pFile->h, locktypeName(locktype), + OSTRACE4("LOCK %d %s %s (flock)\n", pFile->h, locktypeName(locktype), rc==SQLITE_OK ? "ok" : "failed"); #ifdef SQLITE_IGNORE_FLOCK_LOCK_ERRORS if( (rc & SQLITE_IOERR) == SQLITE_IOERR ){ @@ -23035,7 +23588,7 @@ unixFile *pFile = (unixFile*)id; assert( pFile ); - OSTRACE5("UNLOCK %d %d was %d pid=%d\n", pFile->h, locktype, + OSTRACE5("UNLOCK %d %d was %d pid=%d (flock)\n", pFile->h, locktype, pFile->locktype, getpid()); assert( locktype<=SHARED_LOCK ); @@ -23137,7 +23690,7 @@ sem_post(pSem); } } - OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved); + OSTRACE4("TEST WR-LOCK %d %d %d (sem)\n", pFile->h, rc, reserved); *pResOut = reserved; return rc; @@ -23212,7 +23765,7 @@ assert( pFile ); assert( pSem ); - OSTRACE5("UNLOCK %d %d was %d pid=%d\n", pFile->h, locktype, + OSTRACE5("UNLOCK %d %d was %d pid=%d (sem)\n", pFile->h, locktype, pFile->locktype, getpid()); assert( locktype<=SHARED_LOCK ); @@ -23382,7 +23935,7 @@ } } - OSTRACE4("TEST WR-LOCK %d %d %d\n", pFile->h, rc, reserved); + OSTRACE4("TEST WR-LOCK %d %d %d (afp)\n", pFile->h, rc, reserved); *pResOut = reserved; return rc; @@ -23418,7 +23971,7 @@ afpLockingContext *context = (afpLockingContext *) pFile->lockingContext; assert( pFile ); - OSTRACE5("LOCK %d %s was %s pid=%d\n", pFile->h, + OSTRACE5("LOCK %d %s was %s pid=%d (afp)\n", pFile->h, locktypeName(locktype), locktypeName(pFile->locktype), getpid()); /* If there is already a lock of this type or more restrictive on the @@ -23426,7 +23979,7 @@ ** unixEnterMutex() hasn't been called yet. */ if( pFile->locktype>=locktype ){ - OSTRACE3("LOCK %d %s ok (already held)\n", pFile->h, + OSTRACE3("LOCK %d %s ok (already held) (afp)\n", pFile->h, locktypeName(locktype)); return SQLITE_OK; } @@ -23468,7 +24021,8 @@ ** operating system calls for the specified lock. */ if( locktype==SHARED_LOCK ){ - int lk, lrc1, lrc2, lrc1Errno; + int lk, lrc1, lrc2; + int lrc1Errno = 0; /* Now get the read-lock SHARED_LOCK */ /* note that the quality of the randomness doesn't matter that much */ @@ -23544,7 +24098,7 @@ afp_end_lock: unixLeaveMutex(); - OSTRACE4("LOCK %d %s %s\n", pFile->h, locktypeName(locktype), + OSTRACE4("LOCK %d %s %s (afp)\n", pFile->h, locktypeName(locktype), rc==SQLITE_OK ? "ok" : "failed"); return rc; } @@ -23562,7 +24116,7 @@ afpLockingContext *pCtx = (afpLockingContext *) pFile->lockingContext; assert( pFile ); - OSTRACE5("UNLOCK %d %d was %d pid=%d\n", pFile->h, locktype, + OSTRACE5("UNLOCK %d %d was %d pid=%d (afp)\n", pFile->h, locktype, pFile->locktype, getpid()); assert( locktype<=SHARED_LOCK ); @@ -23600,27 +24154,15 @@ struct unixOpenCnt *pOpen = pFile->pOpen; pOpen->nLock--; assert( pOpen->nLock>=0 ); - if( pOpen->nLock==0 && pOpen->nPending>0 ){ - int i; - for(i=0; inPending; i++){ - if( pOpen->aPending[i] < 0 ) continue; - if( close(pOpen->aPending[i]) ){ - pFile->lastErrno = errno; - rc = SQLITE_IOERR_CLOSE; - }else{ - pOpen->aPending[i] = -1; - } - } - if( rc==SQLITE_OK ){ - sqlite3_free(pOpen->aPending); - pOpen->nPending = 0; - pOpen->aPending = 0; - } + if( pOpen->nLock==0 ){ + rc = closePendingFds(pFile); } } } unixLeaveMutex(); - if( rc==SQLITE_OK ) pFile->locktype = locktype; + if( rc==SQLITE_OK ){ + pFile->locktype = locktype; + } return rc; } @@ -23638,17 +24180,7 @@ ** descriptor to pOpen->aPending. It will be automatically closed when ** the last lock is cleared. */ - int *aNew; - struct unixOpenCnt *pOpen = pFile->pOpen; - aNew = sqlite3_realloc(pOpen->aPending, (pOpen->nPending+1)*sizeof(int) ); - if( aNew==0 ){ - /* If a malloc fails, just leak the file descriptor */ - }else{ - pOpen->aPending = aNew; - pOpen->aPending[pOpen->nPending] = pFile->h; - pOpen->nPending++; - pFile->h = -1; - } + setPendingFd(pFile); } releaseOpenCnt(pFile->pOpen); sqlite3_free(pFile->lockingContext); @@ -23734,22 +24266,25 @@ int amt, sqlite3_int64 offset ){ + unixFile *pFile = (unixFile *)id; int got; assert( id ); - /* Never read or write any of the bytes in the locking range */ - assert( ((unixFile*)id)->isLockable==0 - || offset>=PENDING_BYTE+512 - || offset+amt<=PENDING_BYTE ); + /* If this is a database file (not a journal, master-journal or temp + ** file), the bytes in the locking range should never be read or written. */ + assert( pFile->pUnused==0 + || offset>=PENDING_BYTE+512 + || offset+amt<=PENDING_BYTE + ); - got = seekAndRead((unixFile*)id, offset, pBuf, amt); + got = seekAndRead(pFile, offset, pBuf, amt); if( got==amt ){ return SQLITE_OK; }else if( got<0 ){ /* lastErrno set by seekAndRead */ return SQLITE_IOERR_READ; }else{ - ((unixFile*)id)->lastErrno = 0; /* not a system error */ + pFile->lastErrno = 0; /* not a system error */ /* Unread parts of the buffer must be zero-filled */ memset(&((char*)pBuf)[got], 0, amt-got); return SQLITE_IOERR_SHORT_READ; @@ -23803,14 +24338,17 @@ int amt, sqlite3_int64 offset ){ + unixFile *pFile = (unixFile*)id; int wrote = 0; assert( id ); assert( amt>0 ); - /* Never read or write any of the bytes in the locking range */ - assert( ((unixFile*)id)->isLockable==0 - || offset>=PENDING_BYTE+512 - || offset+amt<=PENDING_BYTE ); + /* If this is a database file (not a journal, master-journal or temp + ** file), the bytes in the locking range should never be read or written. */ + assert( pFile->pUnused==0 + || offset>=PENDING_BYTE+512 + || offset+amt<=PENDING_BYTE + ); #ifndef NDEBUG /* If we are doing a normal write to a database file (as opposed to @@ -23819,8 +24357,7 @@ ** has changed. If the transaction counter is modified, record that ** fact too. */ - if( ((unixFile*)id)->inNormalWrite ){ - unixFile *pFile = (unixFile*)id; + if( pFile->inNormalWrite ){ pFile->dbUpdate = 1; /* The database has been modified */ if( offset<=24 && offset+amt>=27 ){ int rc; @@ -23835,7 +24372,7 @@ } #endif - while( amt>0 && (wrote = seekAndWrite((unixFile*)id, offset, pBuf, amt))>0 ){ + while( amt>0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt))>0 ){ amt -= wrote; offset += wrote; pBuf = &((char*)pBuf)[wrote]; @@ -23847,7 +24384,7 @@ /* lastErrno set by seekAndWrite */ return SQLITE_IOERR_WRITE; }else{ - ((unixFile*)id)->lastErrno = 0; /* not a system error */ + pFile->lastErrno = 0; /* not a system error */ return SQLITE_FULL; } } @@ -24055,6 +24592,19 @@ ((unixFile*)id)->lastErrno = errno; return SQLITE_IOERR_TRUNCATE; }else{ +#ifndef NDEBUG + /* If we are doing a normal write to a database file (as opposed to + ** doing a hot-journal rollback or a write to some file other than a + ** normal database file) and we truncate the file to zero length, + ** that effectively updates the change counter. This might happen + ** when restoring a database using the backup API from a zero-length + ** source. + */ + if( ((unixFile*)id)->inNormalWrite && nByte==0 ){ + ((unixFile*)id)->transCntrChng = 1; + } +#endif + return SQLITE_OK; } } @@ -24175,7 +24725,7 @@ ** ** (1) The real finder-function named "FImpt()". ** -** (2) A constant pointer to this functio named just "F". +** (2) A constant pointer to this function named just "F". ** ** ** A pointer to the F pointer is used as the pAppData value for VFS @@ -24208,11 +24758,11 @@ unixSectorSize, /* xSectorSize */ \ unixDeviceCharacteristics /* xDeviceCapabilities */ \ }; \ -static const sqlite3_io_methods *FINDER##Impl(const char *z, int h){ \ - UNUSED_PARAMETER(z); UNUSED_PARAMETER(h); \ +static const sqlite3_io_methods *FINDER##Impl(const char *z, unixFile *p){ \ + UNUSED_PARAMETER(z); UNUSED_PARAMETER(p); \ return &METHOD; \ } \ -static const sqlite3_io_methods *(*const FINDER)(const char*,int) \ +static const sqlite3_io_methods *(*const FINDER)(const char*,unixFile *p) \ = FINDER##Impl; /* @@ -24279,6 +24829,23 @@ #endif /* +** The "Whole File Locking" finder returns the same set of methods as +** the posix locking finder. But it also sets the SQLITE_WHOLE_FILE_LOCKING +** flag to force the posix advisory locks to cover the whole file instead +** of just a small span of bytes near the 1GiB boundary. Whole File Locking +** is useful on NFS-mounted files since it helps NFS to maintain cache +** coherency. But it is a detriment to other filesystems since it runs +** slower. +*/ +static const sqlite3_io_methods *posixWflIoFinderImpl(const char*z, unixFile*p){ + UNUSED_PARAMETER(z); + p->fileFlags = SQLITE_WHOLE_FILE_LOCKING; + return &posixIoMethods; +} +static const sqlite3_io_methods + *(*const posixWflIoFinder)(const char*,unixFile *p) = posixWflIoFinderImpl; + +/* ** The proxy locking method is a "super-method" in the sense that it ** opens secondary file descriptors for the conch and lock files and ** it uses proxy, dot-file, AFP, and flock() locking methods on those @@ -24313,7 +24880,7 @@ */ static const sqlite3_io_methods *autolockIoFinderImpl( const char *filePath, /* name of the database file */ - int fd /* file descriptor open on the database file */ + unixFile *pNew /* open file object for the database file */ ){ static const struct Mapping { const char *zFilesystem; /* Filesystem type name */ @@ -24358,14 +24925,15 @@ lockInfo.l_start = 0; lockInfo.l_whence = SEEK_SET; lockInfo.l_type = F_RDLCK; - if( fcntl(fd, F_GETLK, &lockInfo)!=-1 ) { + if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { + pNew->fileFlags = SQLITE_WHOLE_FILE_LOCKING; return &posixIoMethods; }else{ return &dotlockIoMethods; } } -static const sqlite3_io_methods *(*const autolockIoFinder)(const char*,int) - = autolockIoFinderImpl; +static const sqlite3_io_methods + *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl; #endif /* defined(__APPLE__) && SQLITE_ENABLE_LOCKING_STYLE */ @@ -24379,7 +24947,7 @@ */ static const sqlite3_io_methods *autolockIoFinderImpl( const char *filePath, /* name of the database file */ - int fd /* file descriptor open on the database file */ + unixFile *pNew /* the open file object */ ){ struct flock lockInfo; @@ -24396,21 +24964,21 @@ lockInfo.l_start = 0; lockInfo.l_whence = SEEK_SET; lockInfo.l_type = F_RDLCK; - if( fcntl(fd, F_GETLK, &lockInfo)!=-1 ) { + if( fcntl(pNew->h, F_GETLK, &lockInfo)!=-1 ) { return &posixIoMethods; }else{ return &semIoMethods; } } -static const sqlite3_io_methods *(*const autolockIoFinder)(const char*,int) - = autolockIoFinderImpl; +static const sqlite3_io_methods + *(*const autolockIoFinder)(const char*,unixFile*) = autolockIoFinderImpl; #endif /* OS_VXWORKS && SQLITE_ENABLE_LOCKING_STYLE */ /* ** An abstract type for a pointer to a IO method finder function: */ -typedef const sqlite3_io_methods *(*finder_type)(const char*,int); +typedef const sqlite3_io_methods *(*finder_type)(const char*,unixFile*); /**************************************************************************** @@ -24439,18 +25007,16 @@ assert( pNew->pLock==NULL ); assert( pNew->pOpen==NULL ); - /* Parameter isDelete is only used on vxworks. - ** Express this explicitly here to prevent compiler warnings - ** about unused parameters. + /* Parameter isDelete is only used on vxworks. Express this explicitly + ** here to prevent compiler warnings about unused parameters. */ -#if !OS_VXWORKS UNUSED_PARAMETER(isDelete); -#endif OSTRACE3("OPEN %-3d %s\n", h, zFilename); pNew->h = h; pNew->dirfd = dirfd; SET_THREADID(pNew); + pNew->fileFlags = 0; #if OS_VXWORKS pNew->pId = vxworksFindFileId(zFilename); @@ -24463,7 +25029,7 @@ if( noLock ){ pLockingStyle = &nolockIoMethods; }else{ - pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, h); + pLockingStyle = (**(finder_type*)pVfs->pAppData)(zFilename, pNew); #if SQLITE_ENABLE_LOCKING_STYLE /* Cache zFilename in the locking context (AFP and dotlock override) for ** proxyLock activation is possible (remote proxy is based on db name) @@ -24475,6 +25041,28 @@ if( pLockingStyle == &posixIoMethods ){ unixEnterMutex(); rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); + if( rc!=SQLITE_OK ){ + /* If an error occured in findLockInfo(), close the file descriptor + ** immediately, before releasing the mutex. findLockInfo() may fail + ** in two scenarios: + ** + ** (a) A call to fstat() failed. + ** (b) A malloc failed. + ** + ** Scenario (b) may only occur if the process is holding no other + ** file descriptors open on the same file. If there were other file + ** descriptors on this file, then no malloc would be required by + ** findLockInfo(). If this is the case, it is quite safe to close + ** handle h - as it is guaranteed that no posix locks will be released + ** by doing so. + ** + ** If scenario (a) caused the error then things are not so safe. The + ** implicit assumption here is that if fstat() fails, things are in + ** such bad shape that dropping a lock or two doesn't matter much. + */ + close(h); + h = -1; + } unixLeaveMutex(); } @@ -24526,9 +25114,9 @@ if( (rc==SQLITE_OK) && (pNew->pOpen->pSem==NULL) ){ char *zSemName = pNew->pOpen->aSemName; int n; - sqlite3_snprintf(MAX_PATHNAME, zSemName, "%s.sem", + sqlite3_snprintf(MAX_PATHNAME, zSemName, "/%s.sem", pNew->pId->zCanonicalName); - for( n=0; zSemName[n]; n++ ) + for( n=1; zSemName[n]; n++ ) if( zSemName[n]=='/' ) zSemName[n] = '_'; pNew->pOpen->pSem = sem_open(zSemName, O_CREAT, 0666, 1); if( pNew->pOpen->pSem == SEM_FAILED ){ @@ -24550,7 +25138,7 @@ #endif if( rc!=SQLITE_OK ){ if( dirfd>=0 ) close(dirfd); /* silent leak if fail, already in error */ - close(h); + if( h>=0 ) close(h); }else{ pNew->pMethod = pLockingStyle; OpenCounter(+1); @@ -24659,6 +25247,63 @@ static int proxyTransformUnixFile(unixFile*, const char*); #endif +/* +** Search for an unused file descriptor that was opened on the database +** file (not a journal or master-journal file) identified by pathname +** zPath with SQLITE_OPEN_XXX flags matching those passed as the second +** argument to this function. +** +** Such a file descriptor may exist if a database connection was closed +** but the associated file descriptor could not be closed because some +** other file descriptor open on the same file is holding a file-lock. +** Refer to comments in the unixClose() function and the lengthy comment +** describing "Posix Advisory Locking" at the start of this file for +** further details. Also, ticket #4018. +** +** If a suitable file descriptor is found, then it is returned. If no +** such file descriptor is located, -1 is returned. +*/ +static UnixUnusedFd *findReusableFd(const char *zPath, int flags){ + UnixUnusedFd *pUnused = 0; + + /* Do not search for an unused file descriptor on vxworks. Not because + ** vxworks would not benefit from the change (it might, we're not sure), + ** but because no way to test it is currently available. It is better + ** not to risk breaking vxworks support for the sake of such an obscure + ** feature. */ +#if !OS_VXWORKS + struct stat sStat; /* Results of stat() call */ + + /* A stat() call may fail for various reasons. If this happens, it is + ** almost certain that an open() call on the same path will also fail. + ** For this reason, if an error occurs in the stat() call here, it is + ** ignored and -1 is returned. The caller will try to open a new file + ** descriptor on the same path, fail, and return an error to SQLite. + ** + ** Even if a subsequent open() call does succeed, the consequences of + ** not searching for a resusable file descriptor are not dire. */ + if( 0==stat(zPath, &sStat) ){ + struct unixOpenCnt *pOpen; + + unixEnterMutex(); + pOpen = openList; + while( pOpen && (pOpen->fileId.dev!=sStat.st_dev + || pOpen->fileId.ino!=sStat.st_ino) ){ + pOpen = pOpen->pNext; + } + if( pOpen ){ + UnixUnusedFd **pp; + for(pp=&pOpen->pUnused; *pp && (*pp)->flags!=flags; pp=&((*pp)->pNext)); + pUnused = *pp; + if( pUnused ){ + *pp = pUnused->pNext; + } + } + unixLeaveMutex(); + } +#endif /* if !OS_VXWORKS */ + return pUnused; +} /* ** Open the file zPath. @@ -24689,12 +25334,13 @@ int flags, /* Input flags to control the opening */ int *pOutFlags /* Output flags returned to SQLite core */ ){ - int fd = -1; /* File descriptor returned by open() */ + unixFile *p = (unixFile *)pFile; + int fd = -1; /* File descriptor returned by open() */ int dirfd = -1; /* Directory file descriptor */ int openFlags = 0; /* Flags to pass to open() */ int eType = flags&0xFFFFFF00; /* Type of file to open */ int noLock; /* True to omit locking primitives */ - int rc = SQLITE_OK; + int rc = SQLITE_OK; /* Function Return Code */ int isExclusive = (flags & SQLITE_OPEN_EXCLUSIVE); int isDelete = (flags & SQLITE_OPEN_DELETEONCLOSE); @@ -24729,11 +25375,10 @@ assert(isDelete==0 || isCreate); /* The main DB, main journal, and master journal are never automatically - ** deleted - */ - assert( eType!=SQLITE_OPEN_MAIN_DB || !isDelete ); - assert( eType!=SQLITE_OPEN_MAIN_JOURNAL || !isDelete ); - assert( eType!=SQLITE_OPEN_MASTER_JOURNAL || !isDelete ); + ** deleted. Nor are they ever temporary files. */ + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_DB ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MAIN_JOURNAL ); + assert( (!isDelete && zName) || eType!=SQLITE_OPEN_MASTER_JOURNAL ); /* Assert that the upper layer has set one of the "file-type" flags. */ assert( eType==SQLITE_OPEN_MAIN_DB || eType==SQLITE_OPEN_TEMP_DB @@ -24742,9 +25387,22 @@ || eType==SQLITE_OPEN_TRANSIENT_DB ); - memset(pFile, 0, sizeof(unixFile)); + memset(p, 0, sizeof(unixFile)); - if( !zName ){ + if( eType==SQLITE_OPEN_MAIN_DB ){ + UnixUnusedFd *pUnused; + pUnused = findReusableFd(zName, flags); + if( pUnused ){ + fd = pUnused->fd; + }else{ + pUnused = sqlite3_malloc(sizeof(*pUnused)); + if( !pUnused ){ + return SQLITE_NOMEM; + } + } + p->pUnused = pUnused; + }else if( !zName ){ + /* If zName is NULL, the upper layer is requesting a temp file. */ assert(isDelete && !isOpenDirectory); rc = getTempname(MAX_PATHNAME+1, zTmpname); if( rc!=SQLITE_OK ){ @@ -24753,23 +25411,43 @@ zName = zTmpname; } + /* Determine the value of the flags parameter passed to POSIX function + ** open(). These must be calculated even if open() is not called, as + ** they may be stored as part of the file handle and used by the + ** 'conch file' locking functions later on. */ if( isReadonly ) openFlags |= O_RDONLY; if( isReadWrite ) openFlags |= O_RDWR; if( isCreate ) openFlags |= O_CREAT; if( isExclusive ) openFlags |= (O_EXCL|O_NOFOLLOW); openFlags |= (O_LARGEFILE|O_BINARY); - fd = open(zName, openFlags, isDelete?0600:SQLITE_DEFAULT_FILE_PERMISSIONS); - OSTRACE4("OPENX %-3d %s 0%o\n", fd, zName, openFlags); - if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){ - /* Failed to open the file for read/write access. Try read-only. */ - flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); - flags |= SQLITE_OPEN_READONLY; - return unixOpen(pVfs, zPath, pFile, flags, pOutFlags); - } if( fd<0 ){ - return SQLITE_CANTOPEN; + mode_t openMode = (isDelete?0600:SQLITE_DEFAULT_FILE_PERMISSIONS); + fd = open(zName, openFlags, openMode); + OSTRACE4("OPENX %-3d %s 0%o\n", fd, zName, openFlags); + if( fd<0 && errno!=EISDIR && isReadWrite && !isExclusive ){ + /* Failed to open the file for read/write access. Try read-only. */ + flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE); + openFlags &= ~(O_RDWR|O_CREAT); + flags |= SQLITE_OPEN_READONLY; + openFlags |= O_RDONLY; + fd = open(zName, openFlags, openMode); + } + if( fd<0 ){ + rc = SQLITE_CANTOPEN; + goto open_finished; + } + } + assert( fd>=0 ); + if( pOutFlags ){ + *pOutFlags = flags; } + + if( p->pUnused ){ + p->pUnused->fd = fd; + p->pUnused->flags = flags; + } + if( isDelete ){ #if OS_VXWORKS zPath = zName; @@ -24779,25 +25457,20 @@ } #if SQLITE_ENABLE_LOCKING_STYLE else{ - ((unixFile*)pFile)->openFlags = openFlags; - } -#endif - if( pOutFlags ){ - *pOutFlags = flags; - } - -#ifndef NDEBUG - if( (flags & SQLITE_OPEN_MAIN_DB)!=0 ){ - ((unixFile*)pFile)->isLockable = 1; + p->openFlags = openFlags; } #endif - assert( fd>=0 ); if( isOpenDirectory ){ rc = openDirectory(zPath, &dirfd); if( rc!=SQLITE_OK ){ - close(fd); /* silently leak if fail, already in error */ - return rc; + /* It is safe to close fd at this point, because it is guaranteed not + ** to be open on a database file. If it were open on a database file, + ** it would not be safe to close as this would release any locks held + ** on the file by this process. */ + assert( eType!=SQLITE_OPEN_MAIN_DB ); + close(fd); /* silently leak if fail, already in error */ + goto open_finished; } } @@ -24808,23 +25481,31 @@ noLock = eType!=SQLITE_OPEN_MAIN_DB; #if SQLITE_PREFER_PROXY_LOCKING - if( zPath!=NULL && !noLock ){ + if( zPath!=NULL && !noLock && pVfs->xOpen ){ char *envforce = getenv("SQLITE_FORCE_PROXY_LOCKING"); int useProxy = 0; - /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, - ** 0 means never use proxy, NULL means use proxy for non-local files only - */ + /* SQLITE_FORCE_PROXY_LOCKING==1 means force always use proxy, 0 means + ** never use proxy, NULL means use proxy for non-local files only. */ if( envforce!=NULL ){ useProxy = atoi(envforce)>0; }else{ struct statfs fsInfo; - if( statfs(zPath, &fsInfo) == -1 ){ - ((unixFile*)pFile)->lastErrno = errno; - if( dirfd>=0 ) close(dirfd); /* silently leak if fail, in error */ + /* In theory, the close(fd) call is sub-optimal. If the file opened + ** with fd is a database file, and there are other connections open + ** on that file that are currently holding advisory locks on it, + ** then the call to close() will cancel those locks. In practice, + ** we're assuming that statfs() doesn't fail very often. At least + ** not while other file descriptors opened by the same process on + ** the same file are working. */ + p->lastErrno = errno; + if( dirfd>=0 ){ + close(dirfd); /* silently leak if fail, in error */ + } close(fd); /* silently leak if fail, in error */ - return SQLITE_IOERR_ACCESS; + rc = SQLITE_IOERR_ACCESS; + goto open_finished; } useProxy = !(fsInfo.f_flags&MNT_LOCAL); } @@ -24833,14 +25514,20 @@ if( rc==SQLITE_OK ){ rc = proxyTransformUnixFile((unixFile*)pFile, ":auto:"); } - return rc; + goto open_finished; } } #endif - return fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); + rc = fillInUnixFile(pVfs, fd, dirfd, pFile, zPath, noLock, isDelete); +open_finished: + if( rc!=SQLITE_OK ){ + sqlite3_free(p->pUnused); + } + return rc; } + /* ** Delete the file at zPath. If the dirSync argument is true, fsync() ** the directory after deleting the file. @@ -25466,7 +26153,7 @@ # ifdef _CS_DARWIN_USER_TEMP_DIR { confstr(_CS_DARWIN_USER_TEMP_DIR, lPath, maxLen); - len = strlcat(lPath, "sqliteplocks", maxLen); + len = strlcat(lPath, "sqliteplocks", maxLen); if( mkdir(lPath, SQLITE_DEFAULT_PROXYDIR_PERMISSIONS) ){ /* if mkdir fails, handle as lock file creation failure */ # ifdef SQLITE_DEBUG @@ -25509,33 +26196,43 @@ ** but also for freeing the memory associated with the file descriptor. */ static int proxyCreateUnixFile(const char *path, unixFile **ppFile) { - int fd; - int dirfd = -1; unixFile *pNew; + int flags = SQLITE_OPEN_MAIN_DB|SQLITE_OPEN_CREATE|SQLITE_OPEN_READWRITE; int rc = SQLITE_OK; sqlite3_vfs dummyVfs; - fd = open(path, O_RDWR | O_CREAT, SQLITE_DEFAULT_FILE_PERMISSIONS); - if( fd<0 ){ - return SQLITE_CANTOPEN; - } - pNew = (unixFile *)sqlite3_malloc(sizeof(unixFile)); - if( pNew==NULL ){ - rc = SQLITE_NOMEM; - goto end_create_proxy; + if( !pNew ){ + return SQLITE_NOMEM; } memset(pNew, 0, sizeof(unixFile)); + /* Call unixOpen() to open the proxy file. The flags passed to unixOpen() + ** suggest that the file being opened is a "main database". This is + ** necessary as other file types do not necessarily support locking. It + ** is better to use unixOpen() instead of opening the file directly with + ** open(), as unixOpen() sets up the various mechanisms required to + ** make sure a call to close() does not cause the system to discard + ** POSIX locks prematurely. + ** + ** It is important that the xOpen member of the VFS object passed to + ** unixOpen() is NULL. This tells unixOpen() may try to open a proxy-file + ** for the proxy-file (creating a potential infinite loop). + */ dummyVfs.pAppData = (void*)&autolockIoFinder; - rc = fillInUnixFile(&dummyVfs, fd, dirfd, (sqlite3_file*)pNew, path, 0, 0); - if( rc==SQLITE_OK ){ - *ppFile = pNew; - return SQLITE_OK; + dummyVfs.xOpen = 0; + rc = unixOpen(&dummyVfs, path, (sqlite3_file *)pNew, flags, &flags); + if( rc==SQLITE_OK && (flags&SQLITE_OPEN_READONLY) ){ + pNew->pMethod->xClose((sqlite3_file *)pNew); + rc = SQLITE_CANTOPEN; + } + + if( rc!=SQLITE_OK ){ + sqlite3_free(pNew); + pNew = 0; } -end_create_proxy: - close(fd); /* silently leak fd if error, we're already in error */ - sqlite3_free(pNew); + + *ppFile = pNew; return rc; } @@ -26148,6 +26845,7 @@ #endif UNIXVFS("unix-none", nolockIoFinder ), UNIXVFS("unix-dotfile", dotlockIoFinder ), + UNIXVFS("unix-wfl", posixWflIoFinder ), #if OS_VXWORKS UNIXVFS("unix-namedsem", semIoFinder ), #endif @@ -26199,8 +26897,6 @@ ****************************************************************************** ** ** This file contains code that is specific to windows. -** -** $Id: os_win.c,v 1.156 2009/04/23 19:08:33 shane Exp $ */ #if SQLITE_OS_WIN /* This file is used for windows only */ @@ -26267,8 +26963,6 @@ ** ** This file should be #included by the os_*.c files only. It is not a ** general purpose header file. -** -** $Id: os_common.h,v 1.38 2009/02/24 18:40:50 danielk1977 Exp $ */ #ifndef _OS_COMMON_H_ #define _OS_COMMON_H_ @@ -26329,8 +27023,6 @@ ** ** This file contains inline asm code for retrieving "high-performance" ** counters for x86 class CPUs. -** -** $Id: hwtime.h,v 1.3 2008/08/01 14:33:15 shane Exp $ */ #ifndef _HWTIME_H_ #define _HWTIME_H_ @@ -26485,7 +27177,7 @@ */ #if SQLITE_OS_WINCE # define AreFileApisANSI() 1 -# define GetDiskFreeSpaceW() 0 +# define FormatMessageW(a,b,c,d,e,f,g) 0 #endif /* @@ -26718,8 +27410,8 @@ sqlite3_int64 t64; t64 = *t; t64 = (t64 + 11644473600)*10000000; - uTm.dwLowDateTime = t64 & 0xFFFFFFFF; - uTm.dwHighDateTime= t64 >> 32; + uTm.dwLowDateTime = (DWORD)(t64 & 0xFFFFFFFF); + uTm.dwHighDateTime= (DWORD)(t64 >> 32); FileTimeToLocalFileTime(&uTm,&lTm); FileTimeToSystemTime(&lTm,&pTm); y.tm_year = pTm.wYear - 1900; @@ -26739,7 +27431,7 @@ #define UnlockFile(a,b,c,d,e) winceUnlockFile(&a, b, c, d, e) #define LockFileEx(a,b,c,d,e,f) winceLockFileEx(&a, b, c, d, e, f) -#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-offsetof(winFile,h)] +#define HANDLE_TO_WINFILE(a) (winFile*)&((char*)a)[-(int)offsetof(winFile,h)] /* ** Acquire a lock on the handle h @@ -26878,12 +27570,15 @@ winFile *pFile = HANDLE_TO_WINFILE(phFile); BOOL bReturn = FALSE; + UNUSED_PARAMETER(dwFileOffsetHigh); + UNUSED_PARAMETER(nNumberOfBytesToLockHigh); + if (!pFile->hMutex) return TRUE; winceMutexAcquire(pFile->hMutex); /* Wanting an exclusive lock? */ - if (dwFileOffsetLow == SHARED_FIRST - && nNumberOfBytesToLockLow == SHARED_SIZE){ + if (dwFileOffsetLow == (DWORD)SHARED_FIRST + && nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE){ if (pFile->shared->nReaders == 0 && pFile->shared->bExclusive == 0){ pFile->shared->bExclusive = TRUE; pFile->local.bExclusive = TRUE; @@ -26892,9 +27587,8 @@ } /* Want a read-only lock? */ - else if ((dwFileOffsetLow >= SHARED_FIRST && - dwFileOffsetLow < SHARED_FIRST + SHARED_SIZE) && - nNumberOfBytesToLockLow == 1){ + else if (dwFileOffsetLow == (DWORD)SHARED_FIRST && + nNumberOfBytesToLockLow == 1){ if (pFile->shared->bExclusive == 0){ pFile->local.nReaders ++; if (pFile->local.nReaders == 1){ @@ -26905,7 +27599,7 @@ } /* Want a pending lock? */ - else if (dwFileOffsetLow == PENDING_BYTE && nNumberOfBytesToLockLow == 1){ + else if (dwFileOffsetLow == (DWORD)PENDING_BYTE && nNumberOfBytesToLockLow == 1){ /* If no pending lock has been acquired, then acquire it */ if (pFile->shared->bPending == 0) { pFile->shared->bPending = TRUE; @@ -26913,8 +27607,9 @@ bReturn = TRUE; } } + /* Want a reserved lock? */ - else if (dwFileOffsetLow == RESERVED_BYTE && nNumberOfBytesToLockLow == 1){ + else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE && nNumberOfBytesToLockLow == 1){ if (pFile->shared->bReserved == 0) { pFile->shared->bReserved = TRUE; pFile->local.bReserved = TRUE; @@ -26939,14 +27634,17 @@ winFile *pFile = HANDLE_TO_WINFILE(phFile); BOOL bReturn = FALSE; + UNUSED_PARAMETER(dwFileOffsetHigh); + UNUSED_PARAMETER(nNumberOfBytesToUnlockHigh); + if (!pFile->hMutex) return TRUE; winceMutexAcquire(pFile->hMutex); /* Releasing a reader lock or an exclusive lock */ - if (dwFileOffsetLow >= SHARED_FIRST && - dwFileOffsetLow < SHARED_FIRST + SHARED_SIZE){ + if (dwFileOffsetLow == (DWORD)SHARED_FIRST){ /* Did we have an exclusive lock? */ if (pFile->local.bExclusive){ + assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE); pFile->local.bExclusive = FALSE; pFile->shared->bExclusive = FALSE; bReturn = TRUE; @@ -26954,6 +27652,7 @@ /* Did we just have a reader lock? */ else if (pFile->local.nReaders){ + assert(nNumberOfBytesToUnlockLow == (DWORD)SHARED_SIZE || nNumberOfBytesToUnlockLow == 1); pFile->local.nReaders --; if (pFile->local.nReaders == 0) { @@ -26964,7 +27663,7 @@ } /* Releasing a pending lock */ - else if (dwFileOffsetLow == PENDING_BYTE && nNumberOfBytesToUnlockLow == 1){ + else if (dwFileOffsetLow == (DWORD)PENDING_BYTE && nNumberOfBytesToUnlockLow == 1){ if (pFile->local.bPending){ pFile->local.bPending = FALSE; pFile->shared->bPending = FALSE; @@ -26972,7 +27671,7 @@ } } /* Releasing a reserved lock */ - else if (dwFileOffsetLow == RESERVED_BYTE && nNumberOfBytesToUnlockLow == 1){ + else if (dwFileOffsetLow == (DWORD)RESERVED_BYTE && nNumberOfBytesToUnlockLow == 1){ if (pFile->local.bReserved) { pFile->local.bReserved = FALSE; pFile->shared->bReserved = FALSE; @@ -26995,11 +27694,14 @@ DWORD nNumberOfBytesToLockHigh, LPOVERLAPPED lpOverlapped ){ + UNUSED_PARAMETER(dwReserved); + UNUSED_PARAMETER(nNumberOfBytesToLockHigh); + /* If the caller wants a shared read lock, forward this call ** to winceLockFile */ - if (lpOverlapped->Offset == SHARED_FIRST && + if (lpOverlapped->Offset == (DWORD)SHARED_FIRST && dwFlags == 1 && - nNumberOfBytesToLockLow == SHARED_SIZE){ + nNumberOfBytesToLockLow == (DWORD)SHARED_SIZE){ return winceLockFile(phFile, SHARED_FIRST, 0, 1, 0); } return FALSE; @@ -27649,27 +28351,59 @@ ** otherwise (if the message was truncated). */ static int getLastErrorMsg(int nBuf, char *zBuf){ - DWORD error = GetLastError(); - -#if SQLITE_OS_WINCE - sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error); -#else /* FormatMessage returns 0 on failure. Otherwise it ** returns the number of TCHARs written to the output ** buffer, excluding the terminating null char. */ - if (!FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - error, - 0, - zBuf, - nBuf-1, - 0)) - { + DWORD error = GetLastError(); + DWORD dwLen = 0; + char *zOut = 0; + + if( isNT() ){ + WCHAR *zTempWide = NULL; + dwLen = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + error, + 0, + (LPWSTR) &zTempWide, + 0, + 0); + if( dwLen > 0 ){ + /* allocate a buffer and convert to UTF8 */ + zOut = unicodeToUtf8(zTempWide); + /* free the system buffer allocated by FormatMessage */ + LocalFree(zTempWide); + } +/* isNT() is 1 if SQLITE_OS_WINCE==1, so this else is never executed. +** Since the ASCII version of these Windows API do not exist for WINCE, +** it's important to not reference them for WINCE builds. +*/ +#if SQLITE_OS_WINCE==0 + }else{ + char *zTemp = NULL; + dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + error, + 0, + (LPSTR) &zTemp, + 0, + 0); + if( dwLen > 0 ){ + /* allocate a buffer and convert to UTF8 */ + zOut = sqlite3_win32_mbcs_to_utf8(zTemp); + /* free the system buffer allocated by FormatMessage */ + LocalFree(zTemp); + } +#endif + } + if( 0 == dwLen ){ sqlite3_snprintf(nBuf, zBuf, "OsError 0x%x (%u)", error, error); + }else{ + /* copy a maximum of nBuf chars to output buffer */ + sqlite3_snprintf(nBuf, zBuf, "%s", zOut); + /* free the UTF8 buffer */ + free(zOut); } -#endif - return 0; } @@ -28001,9 +28735,15 @@ const char *zRelative /* UTF-8 file name */ ){ DWORD bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE; + /* GetDiskFreeSpace is not supported under WINCE */ +#if SQLITE_OS_WINCE + UNUSED_PARAMETER(pVfs); + UNUSED_PARAMETER(zRelative); +#else char zFullpath[MAX_PATH+1]; int rc; - DWORD dwRet = 0, dwDummy; + DWORD dwRet = 0; + DWORD dwDummy; /* ** We need to get the full path name of the file @@ -28029,22 +28769,20 @@ &bytesPerSector, &dwDummy, &dwDummy); -#if SQLITE_OS_WINCE==0 }else{ /* trim path to just drive reference */ - CHAR *p = (CHAR *)zConverted; + char *p = (char *)zConverted; for(;*p;p++){ if( *p == '\\' ){ *p = '\0'; break; } } - dwRet = GetDiskFreeSpaceA((CHAR*)zConverted, + dwRet = GetDiskFreeSpaceA((char*)zConverted, &dwDummy, &bytesPerSector, &dwDummy, &dwDummy); -#endif } free(zConverted); } @@ -28052,6 +28790,7 @@ bytesPerSector = SQLITE_DEFAULT_SECTOR_SIZE; } } +#endif return (int) bytesPerSector; } @@ -28278,6 +29017,7 @@ winCurrentTime, /* xCurrentTime */ winGetLastError /* xGetLastError */ }; + sqlite3_vfs_register(&winVfs, 1); return SQLITE_OK; } @@ -28324,12 +29064,10 @@ ** Bitvec object is the number of pages in the database file at the ** start of a transaction, and is thus usually less than a few thousand, ** but can be as large as 2 billion for a really big database. -** -** @(#) $Id: bitvec.c,v 1.15 2009/06/02 21:31:39 drh Exp $ */ /* Size of the Bitvec structure in bytes. */ -#define BITVEC_SZ 512 +#define BITVEC_SZ (sizeof(void*)*128) /* 512 on 32bit. 1024 on 64bit */ /* Round the union size down to the nearest pointer boundary, since that's how ** it will be aligned within the Bitvec struct. */ @@ -28436,8 +29174,7 @@ u32 h = BITVEC_HASH(i++); while( p->u.aHash[h] ){ if( p->u.aHash[h]==i ) return 1; - h++; - if( h>=BITVEC_NINT ) h = 0; + h = (h+1) % BITVEC_NINT; } return 0; } @@ -28457,7 +29194,7 @@ */ SQLITE_PRIVATE int sqlite3BitvecSet(Bitvec *p, u32 i){ u32 h; - assert( p!=0 ); + if( p==0 ) return SQLITE_OK; assert( i>0 ); assert( i<=p->iSize ); i--; @@ -28527,7 +29264,7 @@ ** that BitvecClear can use to rebuilt its hash table. */ SQLITE_PRIVATE void sqlite3BitvecClear(Bitvec *p, u32 i, void *pBuf){ - assert( p!=0 ); + if( p==0 ) return; assert( i>0 ); i--; while( p->iDivisor ){ @@ -28638,6 +29375,10 @@ if( pBitvec==0 || pV==0 || pTmpSpace==0 ) goto bitvec_end; memset(pV, 0, (sz+7)/8 + 1); + /* NULL pBitvec tests */ + sqlite3BitvecSet(0, 1); + sqlite3BitvecClear(0, 1, pTmpSpace); + /* Run the program */ pc = 0; while( (op = aOp[pc])!=0 ){ @@ -28710,8 +29451,6 @@ ** ************************************************************************* ** This file implements that page cache. -** -** @(#) $Id: pcache.c,v 1.44 2009/03/31 01:32:18 drh Exp $ */ /* @@ -28907,6 +29646,7 @@ int eCreate; assert( pCache!=0 ); + assert( createFlag==1 || createFlag==0 ); assert( pgno>0 ); /* If the pluggable cache (sqlite3_pcache*) has not been allocated, @@ -28924,10 +29664,7 @@ pCache->pCache = p; } - eCreate = createFlag ? 1 : 0; - if( eCreate && (!pCache->bPurgeable || !pCache->pDirty) ){ - eCreate = 2; - } + eCreate = createFlag * (1 + (!pCache->bPurgeable || !pCache->pDirty)); if( pCache->pCache ){ pPage = sqlite3GlobalConfig.pcache.xFetch(pCache->pCache, pgno, eCreate); } @@ -29169,24 +29906,22 @@ ** Sort the list of pages in accending order by pgno. Pages are ** connected by pDirty pointers. The pDirtyPrev pointers are ** corrupted by this sort. +** +** Since there cannot be more than 2^31 distinct pages in a database, +** there cannot be more than 31 buckets required by the merge sorter. +** One extra bucket is added to catch overflow in case something +** ever changes to make the previous sentence incorrect. */ -#define N_SORT_BUCKET_ALLOC 25 -#define N_SORT_BUCKET 25 -#ifdef SQLITE_TEST - int sqlite3_pager_n_sort_bucket = 0; - #undef N_SORT_BUCKET - #define N_SORT_BUCKET \ - (sqlite3_pager_n_sort_bucket?sqlite3_pager_n_sort_bucket:N_SORT_BUCKET_ALLOC) -#endif +#define N_SORT_BUCKET 32 static PgHdr *pcacheSortDirtyList(PgHdr *pIn){ - PgHdr *a[N_SORT_BUCKET_ALLOC], *p; + PgHdr *a[N_SORT_BUCKET], *p; int i; memset(a, 0, sizeof(a)); while( pIn ){ p = pIn; pIn = p->pDirty; p->pDirty = 0; - for(i=0; ipCache->bPurgeable ){ pcache1.nCurrentPage--; } @@ -29691,6 +30426,8 @@ /* ** Implementation of the sqlite3_pcache.xShutdown method. +** Note that the static mutex allocated in xInit does +** not need to be freed. */ static void pcache1Shutdown(void *NotUsed){ UNUSED_PARAMETER(NotUsed); @@ -29754,7 +30491,14 @@ ** Fetch a page by key value. ** ** Whether or not a new page may be allocated by this function depends on -** the value of the createFlag argument. +** the value of the createFlag argument. 0 means do not allocate a new +** page. 1 means allocate a new page if space is easily available. 2 +** means to try really hard to allocate a new page. +** +** For a non-purgeable cache (a cache used as the storage for an in-memory +** database) there is really no difference between createFlag 1 and 2. So +** the calling function (pcache.c) will never have a createFlag of 1 on +** a non-purgable cache. ** ** There are three different approaches to obtaining space for a page, ** depending on the value of parameter createFlag (which may be 0, 1 or 2). @@ -29765,9 +30509,8 @@ ** 2. If createFlag==0 and the page is not already in the cache, NULL is ** returned. ** -** 3. If createFlag is 1, the cache is marked as purgeable and the page is -** not already in the cache, and if either of the following are true, -** return NULL: +** 3. If createFlag is 1, and the page is not already in the cache, +** and if either of the following are true, return NULL: ** ** (a) the number of pages pinned by the cache is greater than ** PCache1.nMax, or @@ -29796,6 +30539,7 @@ PCache1 *pCache = (PCache1 *)p; PgHdr1 *pPage = 0; + assert( pCache->bPurgeable || createFlag!=1 ); pcache1EnterMutex(); if( createFlag==1 ) sqlite3BeginBenignMalloc(); @@ -29812,7 +30556,7 @@ /* Step 3 of header comment. */ nPinned = pCache->nPage - pCache->nRecyclable; - if( createFlag==1 && pCache->bPurgeable && ( + if( createFlag==1 && ( nPinned>=(pcache1.nMaxPage+pCache->nMin-pcache1.nMinPage) || nPinned>=(pCache->nMax * 9 / 10) )){ @@ -29937,7 +30681,6 @@ pPage->iKey = iNew; pPage->pNext = pCache->apHash[h]; pCache->apHash[h] = pPage; - if( iNew>pCache->iMaxKey ){ pCache->iMaxKey = iNew; } @@ -30114,8 +30857,6 @@ ** ** There is an added cost of O(N) when switching between TEST and ** SMALLEST primitives. -** -** $Id: rowset.c,v 1.7 2009/05/22 01:00:13 drh Exp $ */ @@ -30498,8 +31239,6 @@ ** locking to prevent two processes from writing the same database ** file simultaneously, or one process from reading the database while ** another is writing. -** -** @(#) $Id: pager.c,v 1.603 2009/06/26 12:15:23 drh Exp $ */ #ifndef SQLITE_OMIT_DISKIO @@ -30594,12 +31333,12 @@ #endif /* -** The maximum allowed sector size. 16MB. If the xSectorsize() method +** The maximum allowed sector size. 64KiB. If the xSectorsize() method ** returns a value larger than this, then MAX_SECTOR_SIZE is used instead. ** This could conceivably cause corruption following a power failure on ** such a system. This is currently an undocumented limit. */ -#define MAX_SECTOR_SIZE 0x0100000 +#define MAX_SECTOR_SIZE 0x10000 /* ** An instance of the following structure is allocated for each active @@ -31265,8 +32004,7 @@ memcpy(zHeader, aJournalMagic, sizeof(aJournalMagic)); put32bits(&zHeader[sizeof(aJournalMagic)], 0xffffffff); }else{ - zHeader[0] = '\0'; - put32bits(&zHeader[sizeof(aJournalMagic)], 0); + memset(zHeader, 0, sizeof(aJournalMagic)+4); } /* The random check-hash initialiser */ @@ -31394,10 +32132,10 @@ /* Check that the values read from the page-size and sector-size fields ** are within range. To be 'in range', both values need to be a power - ** of two greater than or equal to 512, and not greater than their + ** of two greater than or equal to 512 or 32, and not greater than their ** respective compile time maximum limits. */ - if( iPageSize<512 || iSectorSize<512 + if( iPageSize<512 || iSectorSize<32 || iPageSize>SQLITE_MAX_PAGE_SIZE || iSectorSize>MAX_SECTOR_SIZE || ((iPageSize-1)&iPageSize)!=0 || ((iSectorSize-1)&iSectorSize)!=0 ){ @@ -31607,7 +32345,7 @@ /* If the file is unlocked, somebody else might change it. The ** values stored in Pager.dbSize etc. might become invalid if ** this happens. TODO: Really, this doesn't need to be cleared - ** until the change-counter check fails in pagerSharedLock(). + ** until the change-counter check fails in PagerSharedLock(). */ pPager->dbSizeValid = 0; @@ -31630,6 +32368,7 @@ pPager->changeCountDone = 0; pPager->state = PAGER_UNLOCK; + pPager->dbModified = 0; } } @@ -31654,26 +32393,14 @@ */ static int pager_error(Pager *pPager, int rc){ int rc2 = rc & 0xff; + assert( rc==SQLITE_OK || !MEMDB ); assert( pPager->errCode==SQLITE_FULL || pPager->errCode==SQLITE_OK || (pPager->errCode & 0xff)==SQLITE_IOERR ); - if( - rc2==SQLITE_FULL || - rc2==SQLITE_IOERR || - rc2==SQLITE_CORRUPT - ){ + if( rc2==SQLITE_FULL || rc2==SQLITE_IOERR ){ pPager->errCode = rc; - if( pPager->state==PAGER_UNLOCK - && sqlite3PcacheRefCount(pPager->pPCache)==0 - ){ - /* If the pager is already unlocked, call pager_unlock() now to - ** clear the error state and ensure that the pager-cache is - ** completely empty. - */ - pager_unlock(pPager); - } } return rc; } @@ -31772,21 +32499,10 @@ assert( isOpen(pPager->jfd) || pPager->pInJournal==0 ); if( isOpen(pPager->jfd) ){ - /* TODO: There's a problem here if a journal-file was opened in MEMORY - ** mode and then the journal-mode is changed to TRUNCATE or PERSIST - ** during the transaction. This code should be changed to assume - ** that the journal mode has not changed since the transaction was - ** started. And the sqlite3PagerJournalMode() function should be - ** changed to make sure that this is the case too. - */ - /* Finalize the journal file. */ - if( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ){ - int isMemoryJournal = sqlite3IsMemJournal(pPager->jfd); + if( sqlite3IsMemJournal(pPager->jfd) ){ + assert( pPager->journalMode==PAGER_JOURNALMODE_MEMORY ); sqlite3OsClose(pPager->jfd); - if( !isMemoryJournal ){ - rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); - } }else if( pPager->journalMode==PAGER_JOURNALMODE_TRUNCATE ){ if( pPager->journalOff==0 ){ rc = SQLITE_OK; @@ -31803,9 +32519,15 @@ pPager->journalOff = 0; pPager->journalStarted = 0; }else{ - assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE || rc ); + /* This branch may be executed with Pager.journalMode==MEMORY if + ** a hot-journal was just rolled back. In this case the journal + ** file should be closed and deleted. If this connection writes to + ** the database file, it will do so using an in-memory journal. */ + assert( pPager->journalMode==PAGER_JOURNALMODE_DELETE + || pPager->journalMode==PAGER_JOURNALMODE_MEMORY + ); sqlite3OsClose(pPager->jfd); - if( rc==SQLITE_OK && !pPager->tempFile ){ + if( !pPager->tempFile ){ rc = sqlite3OsDelete(pPager->pVfs, pPager->zJournal, 0); } } @@ -31921,7 +32643,7 @@ PgHdr *pPg; /* An existing page in the cache */ Pgno pgno; /* The page number of a page in journal */ u32 cksum; /* Checksum used for sanity checking */ - u8 *aData; /* Temporary storage for the page */ + char *aData; /* Temporary storage for the page */ sqlite3_file *jfd; /* The file descriptor for the journal file */ assert( (isMainJrnl&~1)==0 ); /* isMainJrnl is 0 or 1 */ @@ -31929,7 +32651,7 @@ assert( isMainJrnl || pDone ); /* pDone always used on sub-journals */ assert( isSavepnt || pDone==0 ); /* pDone never used on non-savepoint */ - aData = (u8*)pPager->pTmpSpace; + aData = pPager->pTmpSpace; assert( aData ); /* Temp storage must have already been allocated */ /* Read the page number and page data from the journal or sub-journal @@ -31938,7 +32660,7 @@ jfd = isMainJrnl ? pPager->jfd : pPager->sjfd; rc = read32bits(jfd, *pOffset, &pgno); if( rc!=SQLITE_OK ) return rc; - rc = sqlite3OsRead(jfd, aData, pPager->pageSize, (*pOffset)+4); + rc = sqlite3OsRead(jfd, (u8*)aData, pPager->pageSize, (*pOffset)+4); if( rc!=SQLITE_OK ) return rc; *pOffset += pPager->pageSize + 4 + isMainJrnl*4; @@ -31957,7 +32679,7 @@ if( isMainJrnl ){ rc = read32bits(jfd, (*pOffset)-4, &cksum); if( rc ) return rc; - if( !isSavepnt && pager_cksum(pPager, aData)!=cksum ){ + if( !isSavepnt && pager_cksum(pPager, (u8*)aData)!=cksum ){ return SQLITE_DONE; } } @@ -32003,8 +32725,8 @@ pPg = pager_lookup(pPager, pgno); assert( pPg || !MEMDB ); PAGERTRACE(("PLAYBACK %d page %d hash(%08x) %s\n", - PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, aData), - (isMainJrnl?"main-journal":"sub-journal") + PAGERID(pPager), pgno, pager_datahash(pPager->pageSize, (u8*)aData), + (isMainJrnl?"main-journal":"sub-journal") )); if( (pPager->state>=PAGER_EXCLUSIVE) && (pPg==0 || 0==(pPg->flags&PGHDR_NEED_SYNC)) @@ -32012,14 +32734,14 @@ && !isUnsync ){ i64 ofst = (pgno-1)*(i64)pPager->pageSize; - rc = sqlite3OsWrite(pPager->fd, aData, pPager->pageSize, ofst); + rc = sqlite3OsWrite(pPager->fd, (u8*)aData, pPager->pageSize, ofst); if( pgno>pPager->dbFileSize ){ pPager->dbFileSize = pgno; } if( pPager->pBackup ){ CODEC1(pPager, aData, pgno, 3, rc=SQLITE_NOMEM); - sqlite3BackupUpdate(pPager->pBackup, pgno, aData); - CODEC1(pPager, aData, pgno, 0, rc=SQLITE_NOMEM); + sqlite3BackupUpdate(pPager->pBackup, pgno, (u8*)aData); + CODEC2(pPager, aData, pgno, 7, rc=SQLITE_NOMEM, aData); } }else if( !isMainJrnl && pPg==0 ){ /* If this is a rollback of a savepoint and data was not written to @@ -32054,10 +32776,8 @@ */ void *pData; pData = pPg->pData; - memcpy(pData, aData, pPager->pageSize); - if( pPager->xReiniter ){ - pPager->xReiniter(pPg); - } + memcpy(pData, (u8*)aData, pPager->pageSize); + pPager->xReiniter(pPg); if( isMainJrnl && (!isSavepnt || *pOffset<=pPager->journalHdr) ){ /* If the contents of this page were just restored from the main ** journal file, then its content must be as they were when the @@ -32095,46 +32815,6 @@ return rc; } -#if !defined(NDEBUG) || defined(SQLITE_COVERAGE_TEST) -/* -** This routine looks ahead into the main journal file and determines -** whether or not the next record (the record that begins at file -** offset pPager->journalOff) is a well-formed page record consisting -** of a valid page number, pPage->pageSize bytes of content, followed -** by a valid checksum. -** -** The pager never needs to know this in order to do its job. This -** routine is only used from with assert() and testcase() macros. -*/ -static int pagerNextJournalPageIsValid(Pager *pPager){ - Pgno pgno; /* The page number of the page */ - u32 cksum; /* The page checksum */ - int rc; /* Return code from read operations */ - sqlite3_file *fd; /* The file descriptor from which we are reading */ - u8 *aData; /* Content of the page */ - - /* Read the page number header */ - fd = pPager->jfd; - rc = read32bits(fd, pPager->journalOff, &pgno); - if( rc!=SQLITE_OK ){ return 0; } /*NO_TEST*/ - if( pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){ return 0; } /*NO_TEST*/ - if( pgno>(Pgno)pPager->dbSize ){ return 0; } /*NO_TEST*/ - - /* Read the checksum */ - rc = read32bits(fd, pPager->journalOff+pPager->pageSize+4, &cksum); - if( rc!=SQLITE_OK ){ return 0; } /*NO_TEST*/ - - /* Read the data and verify the checksum */ - aData = (u8*)pPager->pTmpSpace; - rc = sqlite3OsRead(fd, aData, pPager->pageSize, pPager->journalOff+4); - if( rc!=SQLITE_OK ){ return 0; } /*NO_TEST*/ - if( pager_cksum(pPager, aData)!=cksum ){ return 0; } /*NO_TEST*/ - - /* Reach this point only if the page is valid */ - return 1; -} -#endif /* !defined(NDEBUG) || defined(SQLITE_COVERAGE_TEST) */ - /* ** Parameter zMaster is the name of a master journal file. A single journal ** file that referred to the master journal file has just been rolled back. @@ -32210,14 +32890,15 @@ /* Load the entire master journal file into space obtained from ** sqlite3_malloc() and pointed to by zMasterJournal. */ - zMasterJournal = (char *)sqlite3Malloc((int)nMasterJournal + nMasterPtr); + zMasterJournal = sqlite3Malloc((int)nMasterJournal + nMasterPtr + 1); if( !zMasterJournal ){ rc = SQLITE_NOMEM; goto delmaster_out; } - zMasterPtr = &zMasterJournal[nMasterJournal]; + zMasterPtr = &zMasterJournal[nMasterJournal+1]; rc = sqlite3OsRead(pMaster, zMasterJournal, (int)nMasterJournal, 0); if( rc!=SQLITE_OK ) goto delmaster_out; + zMasterJournal[nMasterJournal] = 0; zJournal = zMasterJournal; while( (zJournal-zMasterJournal)sectorSize = sqlite3OsSectorSize(pPager->fd); } - if( pPager->sectorSize<512 ){ + if( pPager->sectorSize<32 ){ pPager->sectorSize = 512; } if( pPager->sectorSize>MAX_SECTOR_SIZE ){ @@ -32359,21 +33040,15 @@ ** database to during a rollback. ** (5) 4 byte big-endian integer which is the sector size. The header ** is this many bytes in size. -** (6) 4 byte big-endian integer which is the page case. -** (7) 4 byte integer which is the number of bytes in the master journal -** name. The value may be zero (indicate that there is no master -** journal.) -** (8) N bytes of the master journal name. The name will be nul-terminated -** and might be shorter than the value read from (5). If the first byte -** of the name is \000 then there is no master journal. The master -** journal name is stored in UTF-8. -** (9) Zero or more pages instances, each as follows: +** (6) 4 byte big-endian integer which is the page size. +** (7) zero padding out to the next sector size. +** (8) Zero or more pages instances, each as follows: ** + 4 byte page number. ** + pPager->pageSize bytes of data. ** + 4 byte checksum ** -** When we speak of the journal header, we mean the first 8 items above. -** Each entry in the journal is an instance of the 9th item. +** When we speak of the journal header, we mean the first 7 items above. +** Each entry in the journal is an instance of the 8th item. ** ** Call the value from the second bullet "nRec". nRec is the number of ** valid page entries in the journal. In most cases, you can compute the @@ -32493,11 +33168,6 @@ ** pages that need to be rolled back and that the number of pages ** should be computed based on the journal file size. */ - testcase( nRec==0 && !isHot - && pPager->journalHdr+JOURNAL_HDR_SZ(pPager)!=pPager->journalOff - && ((szJ - pPager->journalOff) / JOURNAL_PG_SZ(pPager))>0 - && pagerNextJournalPageIsValid(pPager) - ); if( nRec==0 && !isHot && pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff ){ nRec = (int)((szJ - pPager->journalOff) / JOURNAL_PG_SZ(pPager)); @@ -32689,11 +33359,6 @@ ** test is related to ticket #2565. See the discussion in the ** pager_playback() function for additional information. */ - assert( !(nJRec==0 - && pPager->journalHdr+JOURNAL_HDR_SZ(pPager)!=pPager->journalOff - && ((szJ - pPager->journalOff) / JOURNAL_PG_SZ(pPager))>0 - && pagerNextJournalPageIsValid(pPager)) - ); if( nJRec==0 && pPager->journalHdr+JOURNAL_HDR_SZ(pPager)==pPager->journalOff ){ @@ -32842,17 +33507,6 @@ } /* -** Set the reinitializer for this pager. If not NULL, the reinitializer -** is called when the content of a page in cache is modified (restored) -** as part of a transaction or savepoint rollback. The callback gives -** higher-level code an opportunity to restore the EXTRA section to -** agree with the restored page data. -*/ -SQLITE_PRIVATE void sqlite3PagerSetReiniter(Pager *pPager, void (*xReinit)(DbPage*)){ - pPager->xReiniter = xReinit; -} - -/* ** Report the current page size and number of reserved bytes back ** to the codec. */ @@ -32899,12 +33553,13 @@ */ SQLITE_PRIVATE int sqlite3PagerSetPagesize(Pager *pPager, u16 *pPageSize, int nReserve){ int rc = pPager->errCode; + if( rc==SQLITE_OK ){ u16 pageSize = *pPageSize; assert( pageSize==0 || (pageSize>=512 && pageSize<=SQLITE_MAX_PAGE_SIZE) ); - if( pageSize && pageSize!=pPager->pageSize - && (pPager->memDb==0 || pPager->dbSize==0) + if( (pPager->memDb==0 || pPager->dbSize==0) && sqlite3PcacheRefCount(pPager->pPCache)==0 + && pageSize && pageSize!=pPager->pageSize ){ char *pNew = (char *)sqlite3PageMalloc(pageSize); if( !pNew ){ @@ -33090,8 +33745,11 @@ assert( PAGER_RESERVED==RESERVED_LOCK ); assert( PAGER_EXCLUSIVE==EXCLUSIVE_LOCK ); - /* If the file is currently unlocked then the size must be unknown */ + /* If the file is currently unlocked then the size must be unknown. It + ** must not have been modified at this point. + */ assert( pPager->state>=PAGER_SHARED || pPager->dbSizeValid==0 ); + assert( pPager->state>=PAGER_SHARED || pPager->dbModified==0 ); /* Check that this is either a no-op (because the requested lock is ** already held, or one of the transistions that the busy-handler @@ -33118,6 +33776,40 @@ } /* +** Function assertTruncateConstraint(pPager) checks that one of the +** following is true for all dirty pages currently in the page-cache: +** +** a) The page number is less than or equal to the size of the +** current database image, in pages, OR +** +** b) if the page content were written at this time, it would not +** be necessary to write the current content out to the sub-journal +** (as determined by function subjRequiresPage()). +** +** If the condition asserted by this function were not true, and the +** dirty page were to be discarded from the cache via the pagerStress() +** routine, pagerStress() would not write the current page content to +** the database file. If a savepoint transaction were rolled back after +** this happened, the correct behaviour would be to restore the current +** content of the page. However, since this content is not present in either +** the database file or the portion of the rollback journal and +** sub-journal rolled back the content could not be restored and the +** database image would become corrupt. It is therefore fortunate that +** this circumstance cannot arise. +*/ +#if defined(SQLITE_DEBUG) +static void assertTruncateConstraintCb(PgHdr *pPg){ + assert( pPg->flags&PGHDR_DIRTY ); + assert( !subjRequiresPage(pPg) || pPg->pgno<=pPg->pPager->dbSize ); +} +static void assertTruncateConstraint(Pager *pPager){ + sqlite3PcacheIterateDirty(pPager->pPCache, assertTruncateConstraintCb); +} +#else +# define assertTruncateConstraint(pPager) +#endif + +/* ** Truncate the in-memory database file image to nPage pages. This ** function does not actually modify the database file on disk. It ** just sets the internal state of the pager object so that the @@ -33128,6 +33820,7 @@ assert( pPager->dbSize>=nPage ); assert( pPager->state>=PAGER_RESERVED ); pPager->dbSize = nPage; + assertTruncateConstraint(pPager); } /* @@ -33363,7 +34056,7 @@ Pager *pPager; /* Pager object */ int rc; /* Return code */ - if( pList==0 ) return SQLITE_OK; + if( NEVER(pList==0) ) return SQLITE_OK; pPager = pList->pPager; /* At this point there may be either a RESERVED or EXCLUSIVE lock on the @@ -33403,7 +34096,9 @@ ** any such pages to the file. ** ** Also, do not write out any page that has the PGHDR_DONT_WRITE flag - ** set (set by sqlite3PagerDontWrite()). + ** set (set by sqlite3PagerDontWrite()). Note that if compiled with + ** SQLITE_SECURE_DELETE the PGHDR_DONT_WRITE bit is never set and so + ** the second test is always true. */ if( pgno<=pPager->dbSize && 0==(pList->flags&PGHDR_DONT_WRITE) ){ i64 offset = (pgno-1)*(i64)pPager->pageSize; /* Offset to write */ @@ -33480,7 +34175,6 @@ pPager->nSubRec++; assert( pPager->nSavepoint>0 ); rc = addToSavepointBitvecs(pPager, pPg->pgno); - testcase( rc!=SQLITE_OK ); } return rc; } @@ -33525,7 +34219,9 @@ ** Similarly, if the pager has already entered the error state, do not ** try to write the contents of pPg to disk. */ - if( pPager->errCode || (pPager->doNotSync && pPg->flags&PGHDR_NEED_SYNC) ){ + if( NEVER(pPager->errCode) + || (pPager->doNotSync && pPg->flags&PGHDR_NEED_SYNC) + ){ return SQLITE_OK; } @@ -33568,7 +34264,9 @@ ** be restored to its current value when the "ROLLBACK TO sp" is ** executed. */ - if( rc==SQLITE_OK && pPg->pgno>pPager->dbSize && subjRequiresPage(pPg) ){ + if( NEVER( + rc==SQLITE_OK && pPg->pgno>pPager->dbSize && subjRequiresPage(pPg) + ) ){ rc = subjournalPage(pPg); } @@ -33624,7 +34322,8 @@ const char *zFilename, /* Name of the database file to open */ int nExtra, /* Extra bytes append to each in-memory page */ int flags, /* flags controlling this file */ - int vfsFlags /* flags passed through to sqlite3_vfs.xOpen() */ + int vfsFlags, /* flags passed through to sqlite3_vfs.xOpen() */ + void (*xReinit)(DbPage*) /* Function to reinitialize pages */ ){ u8 *pPtr; Pager *pPager = 0; /* Pager object to allocate and return */ @@ -33733,6 +34432,7 @@ memcpy(pPager->zFilename, zPathname, nPathname); memcpy(pPager->zJournal, zPathname, nPathname); memcpy(&pPager->zJournal[nPathname], "-journal", 8); + if( pPager->zFilename[0]==0 ) pPager->zJournal[0] = 0; sqlite3_free(zPathname); } pPager->pVfs = pVfs; @@ -33842,7 +34542,8 @@ pPager->memDb = (u8)memDb; pPager->readOnly = (u8)readOnly; /* pPager->needSync = 0; */ - pPager->noSync = (pPager->tempFile || !useJournal) ?1:0; + assert( useJournal || pPager->tempFile ); + pPager->noSync = pPager->tempFile; pPager->fullSync = pPager->noSync ?0:1; pPager->sync_flags = SQLITE_SYNC_NORMAL; /* pPager->pFirst = 0; */ @@ -33852,11 +34553,14 @@ pPager->journalSizeLimit = SQLITE_DEFAULT_JOURNAL_SIZE_LIMIT; assert( isOpen(pPager->fd) || tempFile ); setSectorSize(pPager); - if( memDb ){ + if( !useJournal ){ + pPager->journalMode = PAGER_JOURNALMODE_OFF; + }else if( memDb ){ pPager->journalMode = PAGER_JOURNALMODE_MEMORY; } /* pPager->xBusyHandler = 0; */ /* pPager->pBusyHandlerArg = 0; */ + pPager->xReiniter = xReinit; /* memset(pPager->aHash, 0, sizeof(pPager->aHash)); */ *ppPager = pPager; return SQLITE_OK; @@ -33904,6 +34608,7 @@ assert( pPager->useJournal ); assert( isOpen(pPager->fd) ); assert( !isOpen(pPager->jfd) ); + assert( pPager->state <= PAGER_SHARED ); *pExists = 0; rc = sqlite3OsAccess(pVfs, pPager->zJournal, SQLITE_ACCESS_EXISTS, &exists); @@ -33932,13 +34637,9 @@ if( rc==SQLITE_OK ){ if( nPage==0 ){ sqlite3BeginBenignMalloc(); - if( pPager->state>=PAGER_RESERVED - || sqlite3OsLock(pPager->fd, RESERVED_LOCK)==SQLITE_OK ){ + if( sqlite3OsLock(pPager->fd, RESERVED_LOCK)==SQLITE_OK ){ sqlite3OsDelete(pVfs, pPager->zJournal, 0); - assert( pPager->state>=PAGER_SHARED ); - if( pPager->state==PAGER_SHARED ){ - sqlite3OsUnlock(pPager->fd, SHARED_LOCK); - } + sqlite3OsUnlock(pPager->fd, SHARED_LOCK); } sqlite3EndBenignMalloc(); }else{ @@ -33997,8 +34698,9 @@ i64 iOffset; /* Byte offset of file to read from */ assert( pPager->state>=PAGER_SHARED && !MEMDB ); + assert( isOpen(pPager->fd) ); - if( !isOpen(pPager->fd) ){ + if( NEVER(!isOpen(pPager->fd)) ){ assert( pPager->tempFile ); memset(pPg->pData, 0, pPager->pageSize); return SQLITE_OK; @@ -34024,10 +34726,12 @@ } /* -** This function is called whenever the upper layer requests a database -** page is requested, before the cache is checked for a suitable page -** or any data is read from the database. It performs the following -** two functions: +** This function is called to obtain a shared lock on the database file. +** It is illegal to call sqlite3PagerAcquire() until after this function +** has been successfully called. If a shared-lock is already held when +** this function is called, it is a no-op. +** +** The following operations are also performed by this function. ** ** 1) If the pager is currently in PAGER_UNLOCK state (no lock held ** on the database file), then an attempt is made to obtain a @@ -34053,16 +34757,20 @@ ** IO error occurs while locking the database, checking for a hot-journal ** file or rolling back a journal file, the IO error code is returned. */ -static int pagerSharedLock(Pager *pPager){ +SQLITE_PRIVATE int sqlite3PagerSharedLock(Pager *pPager){ int rc = SQLITE_OK; /* Return code */ int isErrorReset = 0; /* True if recovering from error state */ - /* If this database has no outstanding page references and is in an - ** error-state, this is a chance to clear the error. Discard the - ** contents of the pager-cache and rollback any hot journal in the - ** file-system. + /* This routine is only called from b-tree and only when there are no + ** outstanding pages */ + assert( sqlite3PcacheRefCount(pPager->pPCache)==0 ); + if( NEVER(MEMDB && pPager->errCode) ){ return pPager->errCode; } + + /* If this database is in an error-state, now is a chance to clear + ** the error. Discard the contents of the pager-cache and rollback + ** any hot journal in the file-system. */ - if( !MEMDB && sqlite3PcacheRefCount(pPager->pPCache)==0 && pPager->errCode ){ + if( pPager->errCode ){ if( isOpen(pPager->jfd) || pPager->zJournal ){ isErrorReset = 1; } @@ -34070,27 +34778,20 @@ pager_reset(pPager); } - /* If the pager is still in an error state, do not proceed. The error - ** state will be cleared at some point in the future when all page - ** references are dropped and the cache can be discarded. - */ - if( pPager->errCode && pPager->errCode!=SQLITE_FULL ){ - return pPager->errCode; - } - if( pPager->state==PAGER_UNLOCK || isErrorReset ){ sqlite3_vfs * const pVfs = pPager->pVfs; int isHotJournal = 0; assert( !MEMDB ); assert( sqlite3PcacheRefCount(pPager->pPCache)==0 ); - if( !pPager->noReadlock ){ + if( pPager->noReadlock ){ + assert( pPager->readOnly ); + pPager->state = PAGER_SHARED; + }else{ rc = pager_wait_on_lock(pPager, SHARED_LOCK); if( rc!=SQLITE_OK ){ assert( pPager->state==PAGER_UNLOCK ); return pager_error(pPager, rc); } - }else if( pPager->state==PAGER_UNLOCK ){ - pPager->state = PAGER_SHARED; } assert( pPager->state>=SHARED_LOCK ); @@ -34098,6 +34799,7 @@ ** database file, then it either needs to be played back or deleted. */ if( !isErrorReset ){ + assert( pPager->state <= PAGER_SHARED ); rc = hasHotJournal(pPager, &isHotJournal); if( rc!=SQLITE_OK ){ goto failed; @@ -34250,27 +34952,10 @@ } /* -** Drop a page from the cache using sqlite3PcacheDrop(). -** -** If this means there are now no pages with references to them, a rollback -** occurs and the lock on the database is removed. -*/ -static void pagerDropPage(DbPage *pPg){ - Pager *pPager = pPg->pPager; - sqlite3PcacheDrop(pPg); - pagerUnlockIfUnused(pPager); -} - -/* ** Acquire a reference to page number pgno in pager pPager (a page ** reference has type DbPage*). If the requested reference is ** successfully obtained, it is copied to *ppPage and SQLITE_OK returned. ** -** This function calls pagerSharedLock() to obtain a SHARED lock on -** the database file if such a lock or greater is not already held. -** This may cause hot-journal rollback or a cache purge. See comments -** above function pagerSharedLock() for details. -** ** If the requested page is already in the cache, it is returned. ** Otherwise, a new page object is allocated and populated with data ** read from the database file. In some cases, the pcache module may @@ -34322,62 +35007,66 @@ DbPage **ppPage, /* Write a pointer to the page here */ int noContent /* Do not bother reading content from disk if true */ ){ - PgHdr *pPg = 0; int rc; + PgHdr *pPg; assert( assert_pager_state(pPager) ); - assert( pPager->state==PAGER_UNLOCK - || sqlite3PcacheRefCount(pPager->pPCache)>0 - || pgno==1 - ); + assert( pPager->state>PAGER_UNLOCK ); - /* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page - ** number greater than this, or zero, is requested. - */ - if( pgno>PAGER_MAX_PGNO || pgno==0 || pgno==PAGER_MJ_PGNO(pPager) ){ + if( pgno==0 ){ return SQLITE_CORRUPT_BKPT; } - /* Make sure we have not hit any critical errors. - */ - assert( pPager!=0 ); - *ppPage = 0; - - /* If this is the first page accessed, then get a SHARED lock - ** on the database file. pagerSharedLock() is a no-op if - ** a database lock is already held. - */ - rc = pagerSharedLock(pPager); - if( rc!=SQLITE_OK ){ - return rc; + /* If the pager is in the error state, return an error immediately. + ** Otherwise, request the page from the PCache layer. */ + if( pPager->errCode!=SQLITE_OK && pPager->errCode!=SQLITE_FULL ){ + rc = pPager->errCode; + }else{ + rc = sqlite3PcacheFetch(pPager->pPCache, pgno, 1, ppPage); } - assert( pPager->state!=PAGER_UNLOCK ); - rc = sqlite3PcacheFetch(pPager->pPCache, pgno, 1, &pPg); if( rc!=SQLITE_OK ){ - pagerUnlockIfUnused(pPager); - return rc; - } - assert( pPg->pgno==pgno ); - assert( pPg->pPager==pPager || pPg->pPager==0 ); - if( pPg->pPager==0 ){ + /* Either the call to sqlite3PcacheFetch() returned an error or the + ** pager was already in the error-state when this function was called. + ** Set pPg to 0 and jump to the exception handler. */ + pPg = 0; + goto pager_acquire_err; + } + assert( (*ppPage)->pgno==pgno ); + assert( (*ppPage)->pPager==pPager || (*ppPage)->pPager==0 ); + + if( (*ppPage)->pPager ){ + /* In this case the pcache already contains an initialized copy of + ** the page. Return without further ado. */ + assert( pgno<=PAGER_MAX_PGNO && pgno!=PAGER_MJ_PGNO(pPager) ); + PAGER_INCR(pPager->nHit); + return SQLITE_OK; + + }else{ /* The pager cache has created a new page. Its content needs to - ** be initialized. - */ + ** be initialized. */ int nMax; + PAGER_INCR(pPager->nMiss); + pPg = *ppPage; pPg->pPager = pPager; + /* The maximum page number is 2^31. Return SQLITE_CORRUPT if a page + ** number greater than this, or the unused locking-page, is requested. */ + if( pgno>PAGER_MAX_PGNO || pgno==PAGER_MJ_PGNO(pPager) ){ + rc = SQLITE_CORRUPT_BKPT; + goto pager_acquire_err; + } + rc = sqlite3PagerPagecount(pPager, &nMax); if( rc!=SQLITE_OK ){ - sqlite3PagerUnref(pPg); - return rc; + goto pager_acquire_err; } - if( nMax<(int)pgno || MEMDB || noContent ){ + if( MEMDB || nMax<(int)pgno || noContent ){ if( pgno>pPager->mxPgno ){ - sqlite3PagerUnref(pPg); - return SQLITE_FULL; + rc = SQLITE_FULL; + goto pager_acquire_err; } if( noContent ){ /* Failure to set the bits in the InJournal bit-vectors is benign. @@ -34394,28 +35083,32 @@ TESTONLY( rc = ) addToSavepointBitvecs(pPager, pgno); testcase( rc==SQLITE_NOMEM ); sqlite3EndBenignMalloc(); - }else{ - memset(pPg->pData, 0, pPager->pageSize); } + memset(pPg->pData, 0, pPager->pageSize); IOTRACE(("ZERO %p %d\n", pPager, pgno)); }else{ assert( pPg->pPager==pPager ); rc = readDbPage(pPg); if( rc!=SQLITE_OK ){ - pagerDropPage(pPg); - return rc; + goto pager_acquire_err; } } #ifdef SQLITE_CHECK_PAGES pPg->pageHash = pager_pagehash(pPg); #endif - }else{ - /* The requested page is in the page cache. */ - PAGER_INCR(pPager->nHit); } - *ppPage = pPg; return SQLITE_OK; + +pager_acquire_err: + assert( rc!=SQLITE_OK ); + if( pPg ){ + sqlite3PcacheDrop(pPg); + } + pagerUnlockIfUnused(pPager); + + *ppPage = 0; + return rc; } /* @@ -34435,13 +35128,9 @@ PgHdr *pPg = 0; assert( pPager!=0 ); assert( pgno!=0 ); - - if( (pPager->state!=PAGER_UNLOCK) - && (pPager->errCode==SQLITE_OK || pPager->errCode==SQLITE_FULL) - ){ - sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg); - } - + assert( pPager->pPCache!=0 ); + assert( pPager->state > PAGER_UNLOCK ); + sqlite3PcacheFetch(pPager->pPCache, pgno, 0, &pPg); return pPg; } @@ -34510,12 +35199,13 @@ assert( pPager->state>=PAGER_RESERVED ); assert( pPager->useJournal ); + assert( pPager->journalMode!=PAGER_JOURNALMODE_OFF ); assert( pPager->pInJournal==0 ); - /* If already in the error state, this function is a no-op. */ - if( pPager->errCode ){ - return pPager->errCode; - } + /* If already in the error state, this function is a no-op. But on + ** the other hand, this routine is never called if we are already in + ** an error state. */ + if( NEVER(pPager->errCode) ) return pPager->errCode; /* TODO: Is it really possible to get here with dbSizeValid==0? If not, ** the call to PagerPagecount() can be removed. @@ -34625,9 +35315,7 @@ /* If the required locks were successfully obtained, open the journal ** file and write the first journal-header to it. */ - if( rc==SQLITE_OK && pPager->useJournal - && pPager->journalMode!=PAGER_JOURNALMODE_OFF - ){ + if( rc==SQLITE_OK && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ rc = pager_open_journal(pPager); } }else if( isOpen(pPager->jfd) && pPager->journalOff==0 ){ @@ -34645,6 +35333,15 @@ PAGERTRACE(("TRANSACTION %d\n", PAGERID(pPager))); assert( !isOpen(pPager->jfd) || pPager->journalOff>0 || rc!=SQLITE_OK ); + if( rc!=SQLITE_OK ){ + assert( !pPager->dbModified ); + /* Ignore any IO error that occurs within pager_end_transaction(). The + ** purpose of this call is to reset the internal state of the pager + ** sub-system. It doesn't matter if the journal-file is not properly + ** finalized at this point (since it is not a valid journal file anyway). + */ + pager_end_transaction(pPager, 0); + } return rc; } @@ -34660,14 +35357,19 @@ Pager *pPager = pPg->pPager; int rc = SQLITE_OK; - /* Check for errors + /* This routine is not called unless a transaction has already been + ** started. */ - if( pPager->errCode ){ - return pPager->errCode; - } - if( pPager->readOnly ){ - return SQLITE_PERM; - } + assert( pPager->state>=PAGER_RESERVED ); + + /* If an error has been previously detected, we should not be + ** calling this routine. Repeat the error for robustness. + */ + if( NEVER(pPager->errCode) ) return pPager->errCode; + + /* Higher-level routines never call this function if database is not + ** writable. But check anyway, just for robustness. */ + if( NEVER(pPager->readOnly) ) return SQLITE_PERM; assert( !pPager->setMaster ); @@ -34685,17 +35387,16 @@ ** written to the transaction journal or the ckeckpoint journal ** or both. ** - ** First check to see that the transaction journal exists and - ** create it if it does not. + ** Higher level routines should have already started a transaction, + ** which means they have acquired the necessary locks and opened + ** a rollback journal. Double-check to makes sure this is the case. */ - assert( pPager->state!=PAGER_UNLOCK ); rc = sqlite3PagerBegin(pPager, 0, pPager->subjInMemory); - if( rc!=SQLITE_OK ){ + if( NEVER(rc!=SQLITE_OK) ){ return rc; } - assert( pPager->state>=PAGER_RESERVED ); - if( !isOpen(pPager->jfd) && pPager->useJournal - && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ + if( !isOpen(pPager->jfd) && pPager->journalMode!=PAGER_JOURNALMODE_OFF ){ + assert( pPager->useJournal ); rc = pager_open_journal(pPager); if( rc!=SQLITE_OK ) return rc; } @@ -34874,9 +35575,9 @@ ** journal file must contain sync()ed copies of all of them ** before any of them can be written out to the database file. */ - if( needSync ){ + if( rc==SQLITE_OK && needSync ){ assert( !MEMDB && pPager->noSync==0 ); - for(ii=0; iiflags |= PGHDR_NEED_SYNC; @@ -34905,6 +35606,7 @@ } #endif +#ifndef SQLITE_SECURE_DELETE /* ** A call to this routine tells the pager that it is not necessary to ** write the information on page pPg back to the disk, even though @@ -34930,18 +35632,19 @@ #endif } } +#endif /* !defined(SQLITE_SECURE_DELETE) */ /* ** This routine is called to increment the value of the database file ** change-counter, stored as a 4-byte big-endian integer starting at ** byte offset 24 of the pager file. ** -** If the isDirect flag is zero, then this is done by calling +** If the isDirectMode flag is zero, then this is done by calling ** sqlite3PagerWrite() on page 1, then modifying the contents of the ** page data. In this case the file will be updated when the current ** transaction is committed. ** -** The isDirect flag may only be non-zero if the library was compiled +** The isDirectMode flag may only be non-zero if the library was compiled ** with the SQLITE_ENABLE_ATOMIC_WRITE macro defined. In this case, ** if isDirect is non-zero, then the database file is updated directly ** by writing an updated version of page 1 using a call to the @@ -34961,11 +35664,11 @@ ** "if( isDirect )" condition. */ #ifndef SQLITE_ENABLE_ATOMIC_WRITE - const int isDirect = 0; +# define DIRECT_MODE 0 assert( isDirectMode==0 ); UNUSED_PARAMETER(isDirectMode); #else - const int isDirect = isDirectMode; +# define DIRECT_MODE isDirectMode #endif assert( pPager->state>=PAGER_RESERVED ); @@ -34980,9 +35683,11 @@ assert( pPgHdr==0 || rc==SQLITE_OK ); /* If page one was fetched successfully, and this function is not - ** operating in direct-mode, make page 1 writable. + ** operating in direct-mode, make page 1 writable. When not in + ** direct mode, page 1 is always held in cache and hence the PagerGet() + ** above is always successful - hence the ALWAYS on rc==SQLITE_OK. */ - if( rc==SQLITE_OK && !isDirect ){ + if( !DIRECT_MODE && ALWAYS(rc==SQLITE_OK) ){ rc = sqlite3PagerWrite(pPgHdr); } @@ -34993,14 +35698,14 @@ put32bits(((char*)pPgHdr->pData)+24, change_counter); /* If running in direct mode, write the contents of page 1 to the file. */ - if( isDirect ){ + if( DIRECT_MODE ){ const void *zBuf = pPgHdr->pData; assert( pPager->dbFileSize>0 ); rc = sqlite3OsWrite(pPager->fd, zBuf, pPager->pageSize, 0); - } - - /* If everything worked, set the changeCountDone flag. */ - if( rc==SQLITE_OK ){ + if( rc==SQLITE_OK ){ + pPager->changeCountDone = 1; + } + }else{ pPager->changeCountDone = 1; } } @@ -35020,7 +35725,8 @@ */ SQLITE_PRIVATE int sqlite3PagerSync(Pager *pPager){ int rc; /* Return code */ - if( MEMDB || pPager->noSync ){ + assert( !MEMDB ); + if( pPager->noSync ){ rc = SQLITE_OK; }else{ rc = sqlite3OsSync(pPager->fd, pPager->sync_flags); @@ -35061,17 +35767,22 @@ ){ int rc = SQLITE_OK; /* Return code */ - if( pPager->errCode ){ - return pPager->errCode; - } + /* The dbOrigSize is never set if journal_mode=OFF */ + assert( pPager->journalMode!=PAGER_JOURNALMODE_OFF || pPager->dbOrigSize==0 ); + + /* If a prior error occurred, this routine should not be called. ROLLBACK + ** is the appropriate response to an error, not COMMIT. Guard against + ** coding errors by repeating the prior error. */ + if( NEVER(pPager->errCode) ) return pPager->errCode; PAGERTRACE(("DATABASE SYNC: File=%s zMaster=%s nSize=%d\n", pPager->zFilename, zMaster, pPager->dbSize)); - /* If this is an in-memory db, or no pages have been written to, or this - ** function has already been called, it is a no-op. - */ if( MEMDB && pPager->dbModified ){ + /* If this is an in-memory db, or no pages have been written to, or this + ** function has already been called, it is mostly a no-op. However, any + ** backup in progress needs to be restarted. + */ sqlite3BackupRestart(pPager->pBackup); }else if( pPager->state!=PAGER_SYNCED && pPager->dbModified ){ @@ -35133,10 +35844,13 @@ ** that it took at the start of the transaction. Otherwise, the ** calls to sqlite3PagerGet() return zeroed pages instead of ** reading data from the database file. + ** + ** When journal_mode==OFF the dbOrigSize is always zero, so this + ** block never runs if journal_mode=OFF. */ #ifndef SQLITE_OMIT_AUTOVACUUM - if( pPager->dbSizedbOrigSize - && pPager->journalMode!=PAGER_JOURNALMODE_OFF + if( pPager->dbSizedbOrigSize + && ALWAYS(pPager->journalMode!=PAGER_JOURNALMODE_OFF) ){ Pgno i; /* Iterator variable */ const Pgno iSkip = PAGER_MJ_PGNO(pPager); /* Pending lock page */ @@ -35198,14 +35912,6 @@ } commit_phase_one_exit: - if( rc==SQLITE_IOERR_BLOCKED ){ - /* pager_incr_changecounter() may attempt to obtain an exclusive - ** lock to spill the cache and return IOERR_BLOCKED. But since - ** there is no chance the cache is inconsistent, it is - ** better to return SQLITE_BUSY. - **/ - rc = SQLITE_BUSY; - } return rc; } @@ -35228,18 +35934,16 @@ SQLITE_PRIVATE int sqlite3PagerCommitPhaseTwo(Pager *pPager){ int rc = SQLITE_OK; /* Return code */ - /* Do not proceed if the pager is already in the error state. */ - if( pPager->errCode ){ - return pPager->errCode; - } + /* This routine should not be called if a prior error has occurred. + ** But if (due to a coding error elsewhere in the system) it does get + ** called, just return the same error code without doing anything. */ + if( NEVER(pPager->errCode) ) return pPager->errCode; /* This function should not be called if the pager is not in at least ** PAGER_RESERVED state. And indeed SQLite never does this. But it is - ** nice to have this defensive block here anyway. + ** nice to have this defensive test here anyway. */ - if( NEVER(pPager->statestatedbSizeValid ); aNew[ii].nOrig = pPager->dbSize; - if( isOpen(pPager->jfd) && pPager->journalOff>0 ){ + if( isOpen(pPager->jfd) && ALWAYS(pPager->journalOff>0) ){ aNew[ii].iOffset = pPager->journalOff; }else{ aNew[ii].iOffset = JOURNAL_HDR_SZ(pPager); @@ -35448,6 +36152,7 @@ /* Open the sub-journal, if it is not already opened. */ rc = openSubJournal(pPager); + assertTruncateConstraint(pPager); } return rc; @@ -35575,7 +36280,7 @@ void *pCodec ){ if( pPager->xCodecFree ) pPager->xCodecFree(pPager->pCodec); - pPager->xCodec = xCodec; + pPager->xCodec = pPager->memDb ? 0 : xCodec; pPager->xCodecSizeChng = xCodecSizeChng; pPager->xCodecFree = xCodecFree; pPager->pCodec = pCodec; @@ -35620,6 +36325,14 @@ assert( pPg->nRef>0 ); + /* In order to be able to rollback, an in-memory database must journal + ** the page we are moving from. + */ + if( MEMDB ){ + rc = sqlite3PagerWrite(pPg); + if( rc ) return rc; + } + /* If the page being moved is dirty and has not been saved by the latest ** savepoint, then save the current contents of the page into the ** sub-journal now. This is required to handle the following scenario: @@ -35638,7 +36351,7 @@ ** one or more savepoint bitvecs. This is the reason this function ** may return SQLITE_NOMEM. */ - if( pPg->flags&PGHDR_DIRTY + if( pPg->flags&PGHDR_DIRTY && subjRequiresPage(pPg) && SQLITE_OK!=(rc = subjournalPage(pPg)) ){ @@ -35673,7 +36386,14 @@ assert( !pPgOld || pPgOld->nRef==1 ); if( pPgOld ){ pPg->flags |= (pPgOld->flags&PGHDR_NEED_SYNC); - sqlite3PcacheDrop(pPgOld); + if( MEMDB ){ + /* Do not discard pages from an in-memory database since we might + ** need to rollback later. Just move the page out of the way. */ + assert( pPager->dbSizeValid ); + sqlite3PcacheMove(pPgOld, pPager->dbSize+1); + }else{ + sqlite3PcacheDrop(pPgOld); + } } origPgno = pPg->pgno; @@ -35703,7 +36423,7 @@ assert( pPager->needSync ); rc = sqlite3PagerGet(pPager, needSyncPgno, &pPgHdr); if( rc!=SQLITE_OK ){ - if( pPager->pInJournal && needSyncPgno<=pPager->dbOrigSize ){ + if( needSyncPgno<=pPager->dbOrigSize ){ assert( pPager->pTmpSpace!=0 ); sqlite3BitvecClear(pPager->pInJournal, needSyncPgno, pPager->pTmpSpace); } @@ -35718,15 +36438,12 @@ /* ** For an in-memory database, make sure the original page continues - ** to exist, in case the transaction needs to roll back. We allocate - ** the page now, instead of at rollback, because we can better deal - ** with an out-of-memory error now. Ticket #3761. + ** to exist, in case the transaction needs to roll back. Use pPgOld + ** as the original page since it has already been allocated. */ if( MEMDB ){ - DbPage *pNew; - rc = sqlite3PagerAcquire(pPager, origPgno, &pNew, 1); - if( rc!=SQLITE_OK ) return rc; - sqlite3PagerUnref(pNew); + sqlite3PcacheMove(pPgOld, origPgno); + sqlite3PagerUnref(pPgOld); } return SQLITE_OK; @@ -35746,8 +36463,7 @@ ** allocated along with the specified page. */ SQLITE_PRIVATE void *sqlite3PagerGetExtra(DbPage *pPg){ - Pager *pPager = pPg->pPager; - return (pPager?pPg->pExtra:0); + return pPg->pExtra; } /* @@ -35854,8 +36570,6 @@ ** ************************************************************************* ** -** $Id: btmutex.c,v 1.15 2009/04/10 12:55:17 danielk1977 Exp $ -** ** This file contains code used to implement mutexes on Btree objects. ** This code really belongs in btree.c. But btree.c is getting too ** big and we want to break it down some. This packaged seemed like @@ -35874,8 +36588,6 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btreeInt.h,v 1.49 2009/06/24 05:40:34 danielk1977 Exp $ -** ** This file implements a external (disk-based) database using BTrees. ** For a detailed discussion of BTrees, refer to ** @@ -35913,9 +36625,9 @@ ** ** The file is divided into pages. The first page is called page 1, ** the second is page 2, and so forth. A page number of zero indicates -** "no such page". The page size can be anything between 512 and 65536. -** Each page can be either a btree page, a freelist page or an overflow -** page. +** "no such page". The page size can be any power of 2 between 512 and 32768. +** Each page can be either a btree page, a freelist page, an overflow +** page, or a pointer-map page. ** ** The first page is always a btree page. The first 100 bytes of the first ** page contain a special header (the "file header") that describes the file. @@ -36166,6 +36878,24 @@ */ #define EXTRA_SIZE sizeof(MemPage) +/* +** A linked list of the following structures is stored at BtShared.pLock. +** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor +** is opened on the table with root page BtShared.iTable. Locks are removed +** from this list when a transaction is committed or rolled back, or when +** a btree handle is closed. +*/ +struct BtLock { + Btree *pBtree; /* Btree handle holding this lock */ + Pgno iTable; /* Root page of table */ + u8 eLock; /* READ_LOCK or WRITE_LOCK */ + BtLock *pNext; /* Next in BtShared.pLock list */ +}; + +/* Candidate values for BtLock.eLock */ +#define READ_LOCK 1 +#define WRITE_LOCK 2 + /* A Btree handle ** ** A database connection contains a pointer to an instance of @@ -36175,8 +36905,8 @@ ** this structure. ** ** For some database files, the same underlying database cache might be -** shared between multiple connections. In that case, each contection -** has it own pointer to this object. But each instance of this object +** shared between multiple connections. In that case, each connection +** has it own instance of this object. But each instance of this object ** points to the same BtShared object. The database cache and the ** schema associated with the database file are all contained within ** the BtShared object. @@ -36197,6 +36927,9 @@ int nBackup; /* Number of backup operations reading this btree */ Btree *pNext; /* List of other sharable Btrees from the same db */ Btree *pPrev; /* Back pointer of the same list */ +#ifndef SQLITE_OMIT_SHARED_CACHE + BtLock lock; /* Object used to lock page 1 */ +#endif }; /* @@ -36314,7 +37047,7 @@ ** The entry is identified by its MemPage and the index in ** MemPage.aCell[] of the entry. ** -** When a single database file can shared by two more database connections, +** A single database file can shared by two more database connections, ** but cursors cannot be shared. Each cursor is associated with a ** particular database connection identified BtCursor.pBtree.db. ** @@ -36335,7 +37068,7 @@ u8 eState; /* One of the CURSOR_XXX constants (see below) */ void *pKey; /* Saved key that was cursor's last known position */ i64 nKey; /* Size of pKey, or last integer key */ - int skip; /* (skip<0) -> Prev() is a no-op. (skip>0) -> Next() is */ + int skipNext; /* Prev() is noop if negative. Next() is noop if positive */ #ifndef SQLITE_OMIT_INCRBLOB u8 isIncrblobHandle; /* True if this cursor is an incr. io handle */ Pgno *aOverflow; /* Cache of overflow page locations */ @@ -36381,24 +37114,6 @@ # define PENDING_BYTE_PAGE(pBt) PAGER_MJ_PGNO(pBt) /* -** A linked list of the following structures is stored at BtShared.pLock. -** Locks are added (or upgraded from READ_LOCK to WRITE_LOCK) when a cursor -** is opened on the table with root page BtShared.iTable. Locks are removed -** from this list when a transaction is committed or rolled back, or when -** a btree handle is closed. -*/ -struct BtLock { - Btree *pBtree; /* Btree handle holding this lock */ - Pgno iTable; /* Root page of table */ - u8 eLock; /* READ_LOCK or WRITE_LOCK */ - BtLock *pNext; /* Next in BtShared.pLock list */ -}; - -/* Candidate values for BtLock.eLock */ -#define READ_LOCK 1 -#define WRITE_LOCK 2 - -/* ** These macros define the location of the pointer-map entry for a ** database page. The first argument to each is the number of usable ** bytes on each page of the database (often 1024). The second is the @@ -36500,21 +37215,6 @@ #define get4byte sqlite3Get4byte #define put4byte sqlite3Put4byte -/* -** Internal routines that should be accessed by the btree layer only. -*/ -SQLITE_PRIVATE int sqlite3BtreeGetPage(BtShared*, Pgno, MemPage**, int); -SQLITE_PRIVATE int sqlite3BtreeInitPage(MemPage *pPage); -SQLITE_PRIVATE void sqlite3BtreeParseCellPtr(MemPage*, u8*, CellInfo*); -SQLITE_PRIVATE void sqlite3BtreeParseCell(MemPage*, int, CellInfo*); -SQLITE_PRIVATE int sqlite3BtreeRestoreCursorPosition(BtCursor *pCur); -SQLITE_PRIVATE void sqlite3BtreeMoveToParent(BtCursor *pCur); - -#ifdef SQLITE_TEST -SQLITE_PRIVATE void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur); -SQLITE_PRIVATE void sqlite3BtreeReleaseTempCursor(BtCursor *pCur); -#endif - /************** End of btreeInt.h ********************************************/ /************** Continuing where we left off in btmutex.c ********************/ #ifndef SQLITE_OMIT_SHARED_CACHE @@ -36696,7 +37396,9 @@ if( !p->locked ){ assert( p->wantToLock==1 ); while( p->pPrev ) p = p->pPrev; - while( p->locked && p->pNext ) p = p->pNext; + /* Reason for ALWAYS: There must be at least on unlocked Btree in + ** the chain. Otherwise the !p->locked test above would have failed */ + while( p->locked && ALWAYS(p->pNext) ) p = p->pNext; for(pLater = p->pNext; pLater; pLater=pLater->pNext){ if( pLater->locked ){ unlockBtreeMutex(pLater); @@ -36807,8 +37509,12 @@ /* We should already hold a lock on the database connection */ assert( sqlite3_mutex_held(p->db->mutex) ); + /* The Btree is sharable because only sharable Btrees are entered + ** into the array in the first place. */ + assert( p->sharable ); + p->wantToLock++; - if( !p->locked && p->sharable ){ + if( !p->locked ){ lockBtreeMutex(p); } } @@ -36823,14 +37529,14 @@ Btree *p = pArray->aBtree[i]; /* Some basic sanity checking */ assert( i==0 || pArray->aBtree[i-1]->pBtpBt ); - assert( p->locked || !p->sharable ); + assert( p->locked ); assert( p->wantToLock>0 ); /* We should already hold a lock on the database connection */ assert( sqlite3_mutex_held(p->db->mutex) ); p->wantToLock--; - if( p->wantToLock==0 && p->locked ){ + if( p->wantToLock==0 ){ unlockBtreeMutex(p); } } @@ -36865,8 +37571,6 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** $Id: btree.c,v 1.645 2009/06/26 16:32:13 shane Exp $ -** ** This file implements a external (disk-based) database using BTrees. ** See the header comment on "btreeInt.h" for additional information. ** Including a description of file format and an overview of operation. @@ -36922,11 +37626,6 @@ #endif -/* -** Forward declaration -*/ -static int checkForReadConflicts(Btree*, Pgno, BtCursor*, i64); - #ifdef SQLITE_OMIT_SHARED_CACHE /* @@ -36941,15 +37640,137 @@ #define querySharedCacheTableLock(a,b,c) SQLITE_OK #define setSharedCacheTableLock(a,b,c) SQLITE_OK #define clearAllSharedCacheTableLocks(a) + #define downgradeAllSharedCacheTableLocks(a) + #define hasSharedCacheTableLock(a,b,c,d) 1 + #define hasReadConflicts(a, b) 0 #endif #ifndef SQLITE_OMIT_SHARED_CACHE + +#ifdef SQLITE_DEBUG /* -** Query to see if btree handle p may obtain a lock of type eLock -** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return -** SQLITE_OK if the lock may be obtained (by calling -** setSharedCacheTableLock()), or SQLITE_LOCKED if not. -*/ +**** This function is only used as part of an assert() statement. *** +** +** Check to see if pBtree holds the required locks to read or write to the +** table with root page iRoot. Return 1 if it does and 0 if not. +** +** For example, when writing to a table with root-page iRoot via +** Btree connection pBtree: +** +** assert( hasSharedCacheTableLock(pBtree, iRoot, 0, WRITE_LOCK) ); +** +** When writing to an index that resides in a sharable database, the +** caller should have first obtained a lock specifying the root page of +** the corresponding table. This makes things a bit more complicated, +** as this module treats each table as a separate structure. To determine +** the table corresponding to the index being written, this +** function has to search through the database schema. +** +** Instead of a lock on the table/index rooted at page iRoot, the caller may +** hold a write-lock on the schema table (root page 1). This is also +** acceptable. +*/ +static int hasSharedCacheTableLock( + Btree *pBtree, /* Handle that must hold lock */ + Pgno iRoot, /* Root page of b-tree */ + int isIndex, /* True if iRoot is the root of an index b-tree */ + int eLockType /* Required lock type (READ_LOCK or WRITE_LOCK) */ +){ + Schema *pSchema = (Schema *)pBtree->pBt->pSchema; + Pgno iTab = 0; + BtLock *pLock; + + /* If this database is not shareable, or if the client is reading + ** and has the read-uncommitted flag set, then no lock is required. + ** Return true immediately. + */ + if( (pBtree->sharable==0) + || (eLockType==READ_LOCK && (pBtree->db->flags & SQLITE_ReadUncommitted)) + ){ + return 1; + } + + /* If the client is reading or writing an index and the schema is + ** not loaded, then it is too difficult to actually check to see if + ** the correct locks are held. So do not bother - just return true. + ** This case does not come up very often anyhow. + */ + if( isIndex && (!pSchema || (pSchema->flags&DB_SchemaLoaded)==0) ){ + return 1; + } + + /* Figure out the root-page that the lock should be held on. For table + ** b-trees, this is just the root page of the b-tree being read or + ** written. For index b-trees, it is the root page of the associated + ** table. */ + if( isIndex ){ + HashElem *p; + for(p=sqliteHashFirst(&pSchema->idxHash); p; p=sqliteHashNext(p)){ + Index *pIdx = (Index *)sqliteHashData(p); + if( pIdx->tnum==(int)iRoot ){ + iTab = pIdx->pTable->tnum; + } + } + }else{ + iTab = iRoot; + } + + /* Search for the required lock. Either a write-lock on root-page iTab, a + ** write-lock on the schema table, or (if the client is reading) a + ** read-lock on iTab will suffice. Return 1 if any of these are found. */ + for(pLock=pBtree->pBt->pLock; pLock; pLock=pLock->pNext){ + if( pLock->pBtree==pBtree + && (pLock->iTable==iTab || (pLock->eLock==WRITE_LOCK && pLock->iTable==1)) + && pLock->eLock>=eLockType + ){ + return 1; + } + } + + /* Failed to find the required lock. */ + return 0; +} +#endif /* SQLITE_DEBUG */ + +#ifdef SQLITE_DEBUG +/* +**** This function may be used as part of assert() statements only. **** +** +** Return true if it would be illegal for pBtree to write into the +** table or index rooted at iRoot because other shared connections are +** simultaneously reading that same table or index. +** +** It is illegal for pBtree to write if some other Btree object that +** shares the same BtShared object is currently reading or writing +** the iRoot table. Except, if the other Btree object has the +** read-uncommitted flag set, then it is OK for the other object to +** have a read cursor. +** +** For example, before writing to any part of the table or index +** rooted at page iRoot, one should call: +** +** assert( !hasReadConflicts(pBtree, iRoot) ); +*/ +static int hasReadConflicts(Btree *pBtree, Pgno iRoot){ + BtCursor *p; + for(p=pBtree->pBt->pCursor; p; p=p->pNext){ + if( p->pgnoRoot==iRoot + && p->pBtree!=pBtree + && 0==(p->pBtree->db->flags & SQLITE_ReadUncommitted) + ){ + return 1; + } + } + return 0; +} +#endif /* #ifdef SQLITE_DEBUG */ + +/* +** Query to see if Btree handle p may obtain a lock of type eLock +** (READ_LOCK or WRITE_LOCK) on the table with root-page iTab. Return +** SQLITE_OK if the lock may be obtained (by calling +** setSharedCacheTableLock()), or SQLITE_LOCKED if not. +*/ static int querySharedCacheTableLock(Btree *p, Pgno iTab, u8 eLock){ BtShared *pBt = p->pBt; BtLock *pIter; @@ -36957,6 +37778,7 @@ assert( sqlite3BtreeHoldsMutex(p) ); assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); assert( p->db!=0 ); + assert( !(p->db->flags&SQLITE_ReadUncommitted)||eLock==WRITE_LOCK||iTab==1 ); /* If requesting a write-lock, then the Btree must have an open write ** transaction on this file. And, obviously, for this to be so there @@ -36965,7 +37787,7 @@ assert( eLock==READ_LOCK || (p==pBt->pWriter && p->inTrans==TRANS_WRITE) ); assert( eLock==READ_LOCK || pBt->inTransaction==TRANS_WRITE ); - /* This is a no-op if the shared-cache is not enabled */ + /* This routine is a no-op if the shared-cache is not enabled */ if( !p->sharable ){ return SQLITE_OK; } @@ -36978,47 +37800,25 @@ return SQLITE_LOCKED_SHAREDCACHE; } - /* This (along with setSharedCacheTableLock()) is where - ** the ReadUncommitted flag is dealt with. - ** If the caller is querying for a read-lock on any table - ** other than the sqlite_master table (table 1) and if the ReadUncommitted - ** flag is set, then the lock granted even if there are write-locks - ** on the table. If a write-lock is requested, the ReadUncommitted flag - ** is not considered. - ** - ** In function setSharedCacheTableLock(), if a read-lock is demanded and the - ** ReadUncommitted flag is set, no entry is added to the locks list - ** (BtShared.pLock). - ** - ** To summarize: If the ReadUncommitted flag is set, then read cursors - ** on non-schema tables do not create or respect table locks. The locking - ** procedure for a write-cursor does not change. - */ - if( - 0==(p->db->flags&SQLITE_ReadUncommitted) || - eLock==WRITE_LOCK || - iTab==MASTER_ROOT - ){ - for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ - /* The condition (pIter->eLock!=eLock) in the following if(...) - ** statement is a simplification of: - ** - ** (eLock==WRITE_LOCK || pIter->eLock==WRITE_LOCK) - ** - ** since we know that if eLock==WRITE_LOCK, then no other connection - ** may hold a WRITE_LOCK on any table in this file (since there can - ** only be a single writer). - */ - assert( pIter->eLock==READ_LOCK || pIter->eLock==WRITE_LOCK ); - assert( eLock==READ_LOCK || pIter->pBtree==p || pIter->eLock==READ_LOCK); - if( pIter->pBtree!=p && pIter->iTable==iTab && pIter->eLock!=eLock ){ - sqlite3ConnectionBlocked(p->db, pIter->pBtree->db); - if( eLock==WRITE_LOCK ){ - assert( p==pBt->pWriter ); - pBt->isPending = 1; - } - return SQLITE_LOCKED_SHAREDCACHE; + for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ + /* The condition (pIter->eLock!=eLock) in the following if(...) + ** statement is a simplification of: + ** + ** (eLock==WRITE_LOCK || pIter->eLock==WRITE_LOCK) + ** + ** since we know that if eLock==WRITE_LOCK, then no other connection + ** may hold a WRITE_LOCK on any table in this file (since there can + ** only be a single writer). + */ + assert( pIter->eLock==READ_LOCK || pIter->eLock==WRITE_LOCK ); + assert( eLock==READ_LOCK || pIter->pBtree==p || pIter->eLock==READ_LOCK); + if( pIter->pBtree!=p && pIter->iTable==iTab && pIter->eLock!=eLock ){ + sqlite3ConnectionBlocked(p->db, pIter->pBtree->db); + if( eLock==WRITE_LOCK ){ + assert( p==pBt->pWriter ); + pBt->isPending = 1; } + return SQLITE_LOCKED_SHAREDCACHE; } } return SQLITE_OK; @@ -37031,8 +37831,17 @@ ** by Btree handle p. Parameter eLock must be either READ_LOCK or ** WRITE_LOCK. ** -** SQLITE_OK is returned if the lock is added successfully. SQLITE_BUSY and -** SQLITE_NOMEM may also be returned. +** This function assumes the following: +** +** (a) The specified Btree object p is connected to a sharable +** database (one with the BtShared.sharable flag set), and +** +** (b) No other Btree objects hold a lock that conflicts +** with the requested lock (i.e. querySharedCacheTableLock() has +** already been called and returned SQLITE_OK). +** +** SQLITE_OK is returned if the lock is added successfully. SQLITE_NOMEM +** is returned if a malloc attempt fails. */ static int setSharedCacheTableLock(Btree *p, Pgno iTable, u8 eLock){ BtShared *pBt = p->pBt; @@ -37043,27 +37852,17 @@ assert( eLock==READ_LOCK || eLock==WRITE_LOCK ); assert( p->db!=0 ); - /* This is a no-op if the shared-cache is not enabled */ - if( !p->sharable ){ - return SQLITE_OK; - } - + /* A connection with the read-uncommitted flag set will never try to + ** obtain a read-lock using this function. The only read-lock obtained + ** by a connection in read-uncommitted mode is on the sqlite_master + ** table, and that lock is obtained in BtreeBeginTrans(). */ + assert( 0==(p->db->flags&SQLITE_ReadUncommitted) || eLock==WRITE_LOCK ); + + /* This function should only be called on a sharable b-tree after it + ** has been determined that no other b-tree holds a conflicting lock. */ + assert( p->sharable ); assert( SQLITE_OK==querySharedCacheTableLock(p, iTable, eLock) ); - /* If the read-uncommitted flag is set and a read-lock is requested on - ** a non-schema table, then the lock is always granted. Return early - ** without adding an entry to the BtShared.pLock list. See - ** comment in function querySharedCacheTableLock() for more info - ** on handling the ReadUncommitted flag. - */ - if( - (p->db->flags&SQLITE_ReadUncommitted) && - (eLock==READ_LOCK) && - iTable!=MASTER_ROOT - ){ - return SQLITE_OK; - } - /* First search the list for an existing lock on this table. */ for(pIter=pBt->pLock; pIter; pIter=pIter->pNext){ if( pIter->iTable==iTable && pIter->pBtree==p ){ @@ -37102,9 +37901,9 @@ #ifndef SQLITE_OMIT_SHARED_CACHE /* ** Release all the table locks (locks obtained via calls to -** the setSharedCacheTableLock() procedure) held by Btree handle p. +** the setSharedCacheTableLock() procedure) held by Btree object p. ** -** This function assumes that handle p has an open read or write +** This function assumes that Btree p has an open read or write ** transaction. If it does not, then the BtShared.isPending variable ** may be incorrectly cleared. */ @@ -37122,7 +37921,10 @@ assert( pLock->pBtree->inTrans>=pLock->eLock ); if( pLock->pBtree==p ){ *ppIter = pLock->pNext; - sqlite3_free(pLock); + assert( pLock->iTable!=1 || pLock==&p->lock ); + if( pLock->iTable!=1 ){ + sqlite3_free(pLock); + } }else{ ppIter = &pLock->pNext; } @@ -37134,7 +37936,7 @@ pBt->isExclusive = 0; pBt->isPending = 0; }else if( pBt->nTransaction==2 ){ - /* This function is called when connection p is concluding its + /* This function is called when Btree p is concluding its ** transaction. If there currently exists a writer, and p is not ** that writer, then the number of locks held by connections other ** than the writer must be about to drop to zero. In this case @@ -37146,14 +37948,34 @@ pBt->isPending = 0; } } + +/* +** This function changes all write-locks held by Btree p into read-locks. +*/ +static void downgradeAllSharedCacheTableLocks(Btree *p){ + BtShared *pBt = p->pBt; + if( pBt->pWriter==p ){ + BtLock *pLock; + pBt->pWriter = 0; + pBt->isExclusive = 0; + pBt->isPending = 0; + for(pLock=pBt->pLock; pLock; pLock=pLock->pNext){ + assert( pLock->eLock==READ_LOCK || pLock->pBtree==p ); + pLock->eLock = READ_LOCK; + } + } +} + #endif /* SQLITE_OMIT_SHARED_CACHE */ static void releasePage(MemPage *pPage); /* Forward reference */ /* -** Verify that the cursor holds a mutex on the BtShared +***** This routine is used inside of assert() only **** +** +** Verify that the cursor holds the mutex on its BtShared */ -#ifndef NDEBUG +#ifdef SQLITE_DEBUG static int cursorHoldsMutex(BtCursor *p){ return sqlite3_mutex_held(p->pBt->mutex); } @@ -37181,10 +38003,41 @@ invalidateOverflowCache(p); } } + +/* +** This function is called before modifying the contents of a table +** to invalidate any incrblob cursors that are open on the +** row or one of the rows being modified. +** +** If argument isClearTable is true, then the entire contents of the +** table is about to be deleted. In this case invalidate all incrblob +** cursors open on any row within the table with root-page pgnoRoot. +** +** Otherwise, if argument isClearTable is false, then the row with +** rowid iRow is being replaced or deleted. In this case invalidate +** only those incrblob cursors open on that specific row. +*/ +static void invalidateIncrblobCursors( + Btree *pBtree, /* The database file to check */ + i64 iRow, /* The rowid that might be changing */ + int isClearTable /* True if all rows are being deleted */ +){ + BtCursor *p; + BtShared *pBt = pBtree->pBt; + assert( sqlite3BtreeHoldsMutex(pBtree) ); + for(p=pBt->pCursor; p; p=p->pNext){ + if( p->isIncrblobHandle && (isClearTable || p->info.nKey==iRow) ){ + p->eState = CURSOR_INVALID; + } + } +} + #else + /* Stub functions when INCRBLOB is omitted */ #define invalidateOverflowCache(x) #define invalidateAllOverflowCache(x) -#endif + #define invalidateIncrblobCursors(x,y,z) +#endif /* SQLITE_OMIT_INCRBLOB */ /* ** Set bit pgno of the BtShared.pHasContent bitvec. This is called @@ -37217,20 +38070,20 @@ ** The solution is the BtShared.pHasContent bitvec. Whenever a page is ** moved to become a free-list leaf page, the corresponding bit is ** set in the bitvec. Whenever a leaf page is extracted from the free-list, -** optimization 2 above is ommitted if the corresponding bit is already +** optimization 2 above is omitted if the corresponding bit is already ** set in BtShared.pHasContent. The contents of the bitvec are cleared ** at the end of every transaction. */ static int btreeSetHasContent(BtShared *pBt, Pgno pgno){ int rc = SQLITE_OK; if( !pBt->pHasContent ){ - int nPage; - rc = sqlite3PagerPagecount(pBt->pPager, &nPage); - if( rc==SQLITE_OK ){ - pBt->pHasContent = sqlite3BitvecCreate((u32)nPage); - if( !pBt->pHasContent ){ - rc = SQLITE_NOMEM; - } + int nPage = 100; + sqlite3PagerPagecount(pBt->pPager, &nPage); + /* If sqlite3PagerPagecount() fails there is no harm because the + ** nPage variable is unchanged from its default value of 100 */ + pBt->pHasContent = sqlite3BitvecCreate((u32)nPage); + if( !pBt->pHasContent ){ + rc = SQLITE_NOMEM; } } if( rc==SQLITE_OK && pgno<=sqlite3BitvecSize(pBt->pHasContent) ){ @@ -37263,6 +38116,9 @@ /* ** Save the current cursor position in the variables BtCursor.nKey ** and BtCursor.pKey. The cursor's state is set to CURSOR_REQUIRESEEK. +** +** The caller must ensure that the cursor is valid (has eState==CURSOR_VALID) +** prior to calling this routine. */ static int saveCursorPosition(BtCursor *pCur){ int rc; @@ -37272,6 +38128,7 @@ assert( cursorHoldsMutex(pCur) ); rc = sqlite3BtreeKeySize(pCur, &pCur->nKey); + assert( rc==SQLITE_OK ); /* KeySize() cannot fail */ /* If this is an intKey table, then the above call to BtreeKeySize() ** stores the integer key in pCur->nKey. In this case this value is @@ -37279,7 +38136,7 @@ ** table, then malloc space for and store the pCur->nKey bytes of key ** data. */ - if( rc==SQLITE_OK && 0==pCur->apPage[0]->intKey){ + if( 0==pCur->apPage[0]->intKey ){ void *pKey = sqlite3Malloc( (int)pCur->nKey ); if( pKey ){ rc = sqlite3BtreeKey(pCur, 0, (int)pCur->nKey, pKey); @@ -37309,8 +38166,8 @@ } /* -** Save the positions of all cursors except pExcept open on the table -** with root-page iRoot. Usually, this is called just before cursor +** Save the positions of all cursors (except pExcept) that are open on +** the table with root-page iRoot. Usually, this is called just before cursor ** pExcept is used to modify the table (BtreeDelete() or BtreeInsert()). */ static int saveAllCursors(BtShared *pBt, Pgno iRoot, BtCursor *pExcept){ @@ -37340,21 +38197,52 @@ } /* +** In this version of BtreeMoveto, pKey is a packed index record +** such as is generated by the OP_MakeRecord opcode. Unpack the +** record and then call BtreeMovetoUnpacked() to do the work. +*/ +static int btreeMoveto( + BtCursor *pCur, /* Cursor open on the btree to be searched */ + const void *pKey, /* Packed key if the btree is an index */ + i64 nKey, /* Integer key for tables. Size of pKey for indices */ + int bias, /* Bias search to the high end */ + int *pRes /* Write search results here */ +){ + int rc; /* Status code */ + UnpackedRecord *pIdxKey; /* Unpacked index key */ + char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */ + + if( pKey ){ + assert( nKey==(i64)(int)nKey ); + pIdxKey = sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, + aSpace, sizeof(aSpace)); + if( pIdxKey==0 ) return SQLITE_NOMEM; + }else{ + pIdxKey = 0; + } + rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes); + if( pKey ){ + sqlite3VdbeDeleteUnpackedRecord(pIdxKey); + } + return rc; +} + +/* ** Restore the cursor to the position it was in (or as close to as possible) ** when saveCursorPosition() was called. Note that this call deletes the ** saved position info stored by saveCursorPosition(), so there can be ** at most one effective restoreCursorPosition() call after each ** saveCursorPosition(). */ -SQLITE_PRIVATE int sqlite3BtreeRestoreCursorPosition(BtCursor *pCur){ +static int btreeRestoreCursorPosition(BtCursor *pCur){ int rc; assert( cursorHoldsMutex(pCur) ); assert( pCur->eState>=CURSOR_REQUIRESEEK ); if( pCur->eState==CURSOR_FAULT ){ - return pCur->skip; + return pCur->skipNext; } pCur->eState = CURSOR_INVALID; - rc = sqlite3BtreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &pCur->skip); + rc = btreeMoveto(pCur, pCur->pKey, pCur->nKey, 0, &pCur->skipNext); if( rc==SQLITE_OK ){ sqlite3_free(pCur->pKey); pCur->pKey = 0; @@ -37365,7 +38253,7 @@ #define restoreCursorPosition(p) \ (p->eState>=CURSOR_REQUIRESEEK ? \ - sqlite3BtreeRestoreCursorPosition(p) : \ + btreeRestoreCursorPosition(p) : \ SQLITE_OK) /* @@ -37384,7 +38272,7 @@ *pHasMoved = 1; return rc; } - if( pCur->eState!=CURSOR_VALID || pCur->skip!=0 ){ + if( pCur->eState!=CURSOR_VALID || pCur->skipNext!=0 ){ *pHasMoved = 1; }else{ *pHasMoved = 0; @@ -37416,14 +38304,19 @@ ** ** This routine updates the pointer map entry for page number 'key' ** so that it maps to type 'eType' and parent page number 'pgno'. -** An error code is returned if something goes wrong, otherwise SQLITE_OK. +** +** If *pRC is initially non-zero (non-SQLITE_OK) then this routine is +** a no-op. If an error occurs, the appropriate error code is written +** into *pRC. */ -static int ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent){ +static void ptrmapPut(BtShared *pBt, Pgno key, u8 eType, Pgno parent, int *pRC){ DbPage *pDbPage; /* The pointer map page */ u8 *pPtrmap; /* The pointer map data */ Pgno iPtrmap; /* The pointer map page number */ int offset; /* Offset in pointer map page */ - int rc; + int rc; /* Return code from subfunctions */ + + if( *pRC ) return; assert( sqlite3_mutex_held(pBt->mutex) ); /* The master-journal page number must never be used as a pointer map page */ @@ -37431,30 +38324,33 @@ assert( pBt->autoVacuum ); if( key==0 ){ - return SQLITE_CORRUPT_BKPT; + *pRC = SQLITE_CORRUPT_BKPT; + return; } iPtrmap = PTRMAP_PAGENO(pBt, key); rc = sqlite3PagerGet(pBt->pPager, iPtrmap, &pDbPage); if( rc!=SQLITE_OK ){ - return rc; + *pRC = rc; + return; } offset = PTRMAP_PTROFFSET(iPtrmap, key); if( offset<0 ){ - return SQLITE_CORRUPT_BKPT; + *pRC = SQLITE_CORRUPT_BKPT; + goto ptrmap_exit; } pPtrmap = (u8 *)sqlite3PagerGetData(pDbPage); if( eType!=pPtrmap[offset] || get4byte(&pPtrmap[offset+1])!=parent ){ TRACE(("PTRMAP_UPDATE: %d->(%d,%d)\n", key, eType, parent)); - rc = sqlite3PagerWrite(pDbPage); + *pRC= rc = sqlite3PagerWrite(pDbPage); if( rc==SQLITE_OK ){ pPtrmap[offset] = eType; put4byte(&pPtrmap[offset+1], parent); } } +ptrmap_exit: sqlite3PagerUnref(pDbPage); - return rc; } /* @@ -37491,8 +38387,9 @@ } #else /* if defined SQLITE_OMIT_AUTOVACUUM */ - #define ptrmapPut(w,x,y,z) SQLITE_OK + #define ptrmapPut(w,x,y,z,rc) #define ptrmapGet(w,x,y,z) SQLITE_OK + #define ptrmapPutOvflPtr(x, y, rc) #endif /* @@ -37507,7 +38404,7 @@ /* ** This a more complex version of findCell() that works for -** pages that do contain overflow cells. See insert +** pages that do contain overflow cells. */ static u8 *findOverflowCell(MemPage *pPage, int iCell){ int i; @@ -37529,14 +38426,14 @@ /* ** Parse a cell content block and fill in the CellInfo structure. There -** are two versions of this function. sqlite3BtreeParseCell() takes a -** cell index as the second argument and sqlite3BtreeParseCellPtr() +** are two versions of this function. btreeParseCell() takes a +** cell index as the second argument and btreeParseCellPtr() ** takes a pointer to the body of the cell as its second argument. ** ** Within this file, the parseCell() macro can be called instead of -** sqlite3BtreeParseCellPtr(). Using some compilers, this will be faster. +** btreeParseCellPtr(). Using some compilers, this will be faster. */ -SQLITE_PRIVATE void sqlite3BtreeParseCellPtr( +static void btreeParseCellPtr( MemPage *pPage, /* Page containing the cell */ u8 *pCell, /* Pointer to the cell text. */ CellInfo *pInfo /* Fill in this structure */ @@ -37565,6 +38462,8 @@ } pInfo->nPayload = nPayload; pInfo->nHeader = n; + testcase( nPayload==pPage->maxLocal ); + testcase( nPayload==pPage->maxLocal+1 ); if( likely(nPayload<=pPage->maxLocal) ){ /* This is the (easy) common case where the entire payload fits ** on the local page. No overflow is required. @@ -37594,6 +38493,8 @@ minLocal = pPage->minLocal; maxLocal = pPage->maxLocal; surplus = minLocal + (nPayload - minLocal)%(pPage->pBt->usableSize - 4); + testcase( surplus==maxLocal ); + testcase( surplus==maxLocal+1 ); if( surplus <= maxLocal ){ pInfo->nLocal = (u16)surplus; }else{ @@ -37604,8 +38505,8 @@ } } #define parseCell(pPage, iCell, pInfo) \ - sqlite3BtreeParseCellPtr((pPage), findCell((pPage), (iCell)), (pInfo)) -SQLITE_PRIVATE void sqlite3BtreeParseCell( + btreeParseCellPtr((pPage), findCell((pPage), (iCell)), (pInfo)) +static void btreeParseCell( MemPage *pPage, /* Page containing the cell */ int iCell, /* The cell index. First cell is 0 */ CellInfo *pInfo /* Fill in this structure */ @@ -37629,7 +38530,7 @@ ** cell. If SQLITE_DEBUG is defined, an assert() at the bottom of ** this function verifies that this invariant is not violated. */ CellInfo debuginfo; - sqlite3BtreeParseCellPtr(pPage, pCell, &debuginfo); + btreeParseCellPtr(pPage, pCell, &debuginfo); #endif if( pPage->intKey ){ @@ -37649,9 +38550,13 @@ pIter += getVarint32(pIter, nSize); } + testcase( nSize==pPage->maxLocal ); + testcase( nSize==pPage->maxLocal+1 ); if( nSize>pPage->maxLocal ){ int minLocal = pPage->minLocal; nSize = minLocal + (nSize - minLocal) % (pPage->pBt->usableSize - 4); + testcase( nSize==pPage->maxLocal ); + testcase( nSize==pPage->maxLocal+1 ); if( nSize>pPage->maxLocal ){ nSize = minLocal; } @@ -37667,7 +38572,10 @@ assert( nSize==debuginfo.nSize ); return (u16)nSize; } -#ifndef NDEBUG + +#ifdef SQLITE_DEBUG +/* This variation on cellSizePtr() is used inside of assert() statements +** only. */ static u16 cellSize(MemPage *pPage, int iCell){ return cellSizePtr(pPage, findCell(pPage, iCell)); } @@ -37679,16 +38587,16 @@ ** to an overflow page, insert an entry into the pointer-map ** for the overflow page. */ -static int ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell){ +static void ptrmapPutOvflPtr(MemPage *pPage, u8 *pCell, int *pRC){ CellInfo info; + if( *pRC ) return; assert( pCell!=0 ); - sqlite3BtreeParseCellPtr(pPage, pCell, &info); + btreeParseCellPtr(pPage, pCell, &info); assert( (info.nData+(pPage->intKey?0:info.nKey))==info.nPayload ); if( info.iOverflow ){ Pgno ovfl = get4byte(&pCell[info.iOverflow]); - return ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno); + ptrmapPut(pPage->pBt, ovfl, PTRMAP_OVERFLOW1, pPage->pgno, pRC); } - return SQLITE_OK; } #endif @@ -37702,7 +38610,6 @@ static int defragmentPage(MemPage *pPage){ int i; /* Loop counter */ int pc; /* Address of a i-th cell */ - int addr; /* Offset of first byte after cell pointer array */ int hdr; /* Offset to the page header */ int size; /* Size of a cell */ int usableSize; /* Number of usable bytes on a page */ @@ -37711,6 +38618,9 @@ int nCell; /* Number of cells on the page */ unsigned char *data; /* The page data */ unsigned char *temp; /* Temp area for cell content */ + int iCellFirst; /* First allowable cell index */ + int iCellLast; /* Last possible cell index */ + assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( pPage->pBt!=0 ); @@ -37727,31 +38637,48 @@ cbrk = get2byte(&data[hdr+5]); memcpy(&temp[cbrk], &data[cbrk], usableSize - cbrk); cbrk = usableSize; + iCellFirst = cellOffset + 2*nCell; + iCellLast = usableSize - 4; for(i=0; i=usableSize ){ + testcase( pc==iCellFirst ); + testcase( pc==iCellLast ); +#if !defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK) + /* These conditions have already been verified in btreeInitPage() + ** if SQLITE_ENABLE_OVERSIZE_CELL_CHECK is defined + */ + if( pciCellLast ){ return SQLITE_CORRUPT_BKPT; } +#endif + assert( pc>=iCellFirst && pc<=iCellLast ); size = cellSizePtr(pPage, &temp[pc]); cbrk -= size; - if( cbrkusableSize ){ +#if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK) + if( cbrkusableSize ){ return SQLITE_CORRUPT_BKPT; } - assert( cbrk+size<=usableSize && cbrk>=0 ); +#endif + assert( cbrk+size<=usableSize && cbrk>=iCellFirst ); + testcase( cbrk+size==usableSize ); + testcase( pc+size==usableSize ); memcpy(&data[cbrk], &temp[pc], size); put2byte(pAddr, cbrk); } - assert( cbrk>=cellOffset+2*nCell ); + assert( cbrk>=iCellFirst ); put2byte(&data[hdr+5], cbrk); data[hdr+1] = 0; data[hdr+2] = 0; data[hdr+7] = 0; - addr = cellOffset+2*nCell; - memset(&data[addr], 0, cbrk-addr); + memset(&data[iCellFirst], 0, cbrk-iCellFirst); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - if( cbrk-addr!=pPage->nFree ){ + if( cbrk-iCellFirst!=pPage->nFree ){ return SQLITE_CORRUPT_BKPT; } return SQLITE_OK; @@ -37759,24 +38686,25 @@ /* ** Allocate nByte bytes of space from within the B-Tree page passed -** as the first argument. Return the index into pPage->aData[] of the -** first byte of allocated space. -** -** The caller guarantees that the space between the end of the cell-offset -** array and the start of the cell-content area is at least nByte bytes -** in size. So this routine can never fail. -** -** If there are already 60 or more bytes of fragments within the page, -** the page is defragmented before returning. If this were not done there -** is a chance that the number of fragmented bytes could eventually -** overflow the single-byte field of the page-header in which this value -** is stored. +** as the first argument. Write into *pIdx the index into pPage->aData[] +** of the first byte of allocated space. Return either SQLITE_OK or +** an error code (usually SQLITE_CORRUPT). +** +** The caller guarantees that there is sufficient space to make the +** allocation. This routine might need to defragment in order to bring +** all the space together, however. This routine will avoid using +** the first two bytes past the cell pointer area since presumably this +** allocation is being made in order to insert a new cell, so we will +** also end up needing a new cell pointer. */ -static int allocateSpace(MemPage *pPage, int nByte){ +static int allocateSpace(MemPage *pPage, int nByte, int *pIdx){ const int hdr = pPage->hdrOffset; /* Local cache of pPage->hdrOffset */ u8 * const data = pPage->aData; /* Local cache of pPage->aData */ int nFrag; /* Number of fragmented bytes on pPage */ - int top; + int top; /* First byte of cell content area */ + int gap; /* First byte of gap between cell pointers and cell content */ + int rc; /* Integer return code */ + int usableSize; /* Usable size of the page */ assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( pPage->pBt ); @@ -37784,48 +38712,80 @@ assert( nByte>=0 ); /* Minimum cell size is 4 */ assert( pPage->nFree>=nByte ); assert( pPage->nOverflow==0 ); - - /* Assert that the space between the cell-offset array and the - ** cell-content area is greater than nByte bytes. - */ - assert( nByte <= ( - get2byte(&data[hdr+5])-(hdr+8+(pPage->leaf?0:4)+2*get2byte(&data[hdr+3])) - )); + usableSize = pPage->pBt->usableSize; + assert( nByte < usableSize-8 ); nFrag = data[hdr+7]; + assert( pPage->cellOffset == hdr + 12 - 4*pPage->leaf ); + gap = pPage->cellOffset + 2*pPage->nCell; + top = get2byte(&data[hdr+5]); + if( gap>top ) return SQLITE_CORRUPT_BKPT; + testcase( gap+2==top ); + testcase( gap+1==top ); + testcase( gap==top ); + if( nFrag>=60 ){ - defragmentPage(pPage); - }else{ + /* Always defragment highly fragmented pages */ + rc = defragmentPage(pPage); + if( rc ) return rc; + top = get2byte(&data[hdr+5]); + }else if( gap+2<=top ){ /* Search the freelist looking for a free slot big enough to satisfy ** the request. The allocation is made from the first free slot in ** the list that is large enough to accomadate it. */ int pc, addr; for(addr=hdr+1; (pc = get2byte(&data[addr]))>0; addr=pc){ - int size = get2byte(&data[pc+2]); /* Size of free slot */ + int size; /* Size of the free slot */ + if( pc>usableSize-4 || pc=nByte ){ int x = size - nByte; + testcase( x==4 ); + testcase( x==3 ); if( x<4 ){ /* Remove the slot from the free-list. Update the number of ** fragmented bytes within the page. */ memcpy(&data[addr], &data[pc], 2); data[hdr+7] = (u8)(nFrag + x); + }else if( size+pc > usableSize ){ + return SQLITE_CORRUPT_BKPT; }else{ /* The slot remains on the free-list. Reduce its size to account ** for the portion used by the new allocation. */ put2byte(&data[pc+2], x); } - return pc + x; + *pIdx = pc + x; + return SQLITE_OK; } } } + /* Check to make sure there is enough space in the gap to satisfy + ** the allocation. If not, defragment. + */ + testcase( gap+2+nByte==top ); + if( gap+2+nByte>top ){ + rc = defragmentPage(pPage); + if( rc ) return rc; + top = get2byte(&data[hdr+5]); + assert( gap+nByte<=top ); + } + + /* Allocate memory from the gap in between the cell pointer array - ** and the cell content area. + ** and the cell content area. The btreeInitPage() call has already + ** validated the freelist. Given that the freelist is valid, there + ** is no way that the allocation can extend off the end of the page. + ** The assert() below verifies the previous sentence. */ - top = get2byte(&data[hdr+5]) - nByte; + top -= nByte; put2byte(&data[hdr+5], top); - return top; + assert( top+nByte <= pPage->pBt->usableSize ); + *pIdx = top; + return SQLITE_OK; } /* @@ -37838,11 +38798,12 @@ */ static int freeSpace(MemPage *pPage, int start, int size){ int addr, pbegin, hdr; + int iLast; /* Largest possible freeblock offset */ unsigned char *data = pPage->aData; assert( pPage->pBt!=0 ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); - assert( start>=pPage->hdrOffset+6+(pPage->leaf?0:4) ); + assert( start>=pPage->hdrOffset+6+pPage->childPtrSize ); assert( (start + size)<=pPage->pBt->usableSize ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); assert( size>=0 ); /* Minimum cell size is 4 */ @@ -37853,17 +38814,26 @@ memset(&data[start], 0, size); #endif - /* Add the space back into the linked list of freeblocks */ + /* Add the space back into the linked list of freeblocks. Note that + ** even though the freeblock list was checked by btreeInitPage(), + ** btreeInitPage() did not detect overlapping cells or + ** freeblocks that overlapped cells. Nor does it detect when the + ** cell content area exceeds the value in the page header. If these + ** situations arise, then subsequent insert operations might corrupt + ** the freelist. So we do need to check for corruption while scanning + ** the freelist. + */ hdr = pPage->hdrOffset; addr = hdr + 1; + iLast = pPage->pBt->usableSize - 4; + assert( start<=iLast ); while( (pbegin = get2byte(&data[addr]))0 ){ - assert( pbegin<=pPage->pBt->usableSize-4 ); - if( pbegin<=addr ) { + if( pbeginpPage->pBt->usableSize-4 ) { + if( pbegin>iLast ){ return SQLITE_CORRUPT_BKPT; } assert( pbegin>addr || pbegin==0 ); @@ -37873,7 +38843,7 @@ pPage->nFree = pPage->nFree + (u16)size; /* Coalesce adjacent free blocks */ - addr = pPage->hdrOffset + 1; + addr = hdr + 1; while( (pbegin = get2byte(&data[addr]))>0 ){ int pnext, psize, x; assert( pbegin>addr ); @@ -37882,10 +38852,10 @@ psize = get2byte(&data[pbegin+2]); if( pbegin + psize + 3 >= pnext && pnext>0 ){ int frag = pnext - (pbegin+psize); - if( (frag<0) || (frag>(int)data[pPage->hdrOffset+7]) ){ + if( (frag<0) || (frag>(int)data[hdr+7]) ){ return SQLITE_CORRUPT_BKPT; } - data[pPage->hdrOffset+7] -= (u8)frag; + data[hdr+7] -= (u8)frag; x = get2byte(&data[pnext]); put2byte(&data[pbegin], x); x = pnext + get2byte(&data[pnext+2]) - pbegin; @@ -37953,7 +38923,7 @@ ** guarantee that the page is well-formed. It only shows that ** we failed to detect any corruption. */ -SQLITE_PRIVATE int sqlite3BtreeInitPage(MemPage *pPage){ +static int btreeInitPage(MemPage *pPage){ assert( pPage->pBt!=0 ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); @@ -37970,6 +38940,8 @@ u16 cellOffset; /* Offset from start of page to first cell pointer */ u16 nFree; /* Number of unused bytes on the page */ u16 top; /* First byte of the cell content area */ + int iCellFirst; /* First allowable cell or freeblock offset */ + int iCellLast; /* Last possible cell or freeblock offset */ pBt = pPage->pBt; @@ -37987,34 +38959,37 @@ /* To many cells for a single page. The page must be corrupt */ return SQLITE_CORRUPT_BKPT; } + testcase( pPage->nCell==MX_CELL(pBt) ); - /* A malformed database page might cause use to read past the end + /* A malformed database page might cause us to read past the end ** of page when parsing a cell. ** ** The following block of code checks early to see if a cell extends ** past the end of a page boundary and causes SQLITE_CORRUPT to be ** returned if it does. */ + iCellFirst = cellOffset + 2*pPage->nCell; + iCellLast = usableSize - 4; #if defined(SQLITE_ENABLE_OVERSIZE_CELL_CHECK) { - int iCellFirst; /* First allowable cell index */ - int iCellLast; /* Last possible cell index */ int i; /* Index into the cell pointer array */ int sz; /* Size of a cell */ - iCellFirst = cellOffset + 2*pPage->nCell; - iCellLast = usableSize - 4; if( !pPage->leaf ) iCellLast--; for(i=0; inCell; i++){ pc = get2byte(&data[cellOffset+i*2]); + testcase( pc==iCellFirst ); + testcase( pc==iCellLast ); if( pciCellLast ){ return SQLITE_CORRUPT_BKPT; } sz = cellSizePtr(pPage, &data[pc]); + testcase( pc+sz==usableSize ); if( pc+sz>usableSize ){ return SQLITE_CORRUPT_BKPT; } } + if( !pPage->leaf ) iCellLast++; } #endif @@ -38023,14 +38998,15 @@ nFree = data[hdr+7] + top; while( pc>0 ){ u16 next, size; - if( pc>usableSize-4 ){ - /* Free block is off the page */ + if( pciCellLast ){ + /* Start of free block is off the page */ return SQLITE_CORRUPT_BKPT; } next = get2byte(&data[pc]); size = get2byte(&data[pc+2]); - if( next>0 && next<=pc+size+3 ){ - /* Free blocks must be in accending order */ + if( (next>0 && next<=pc+size+3) || pc+size>usableSize ){ + /* Free blocks must be in ascending order. And the last byte of + ** the free-block must lie on the database page. */ return SQLITE_CORRUPT_BKPT; } nFree = nFree + size; @@ -38047,28 +39023,7 @@ if( nFree>usableSize ){ return SQLITE_CORRUPT_BKPT; } - pPage->nFree = nFree - (cellOffset + 2*pPage->nCell); - -#if 0 - /* Check that all the offsets in the cell offset array are within range. - ** - ** Omitting this consistency check and using the pPage->maskPage mask - ** to prevent overrunning the page buffer in findCell() results in a - ** 2.5% performance gain. - */ - { - u8 *pOff; /* Iterator used to check all cell offsets are in range */ - u8 *pEnd; /* Pointer to end of cell offset array */ - u8 mask; /* Mask of bits that must be zero in MSB of cell offsets */ - mask = ~(((u8)(pBt->pageSize>>8))-1); - pEnd = &data[cellOffset + pPage->nCell*2]; - for(pOff=&data[cellOffset]; pOff!=pEnd && !((*pOff)&mask); pOff+=2); - if( pOff!=pEnd ){ - return SQLITE_CORRUPT_BKPT; - } - } -#endif - + pPage->nFree = (u16)(nFree - iCellFirst); pPage->isInit = 1; } return SQLITE_OK; @@ -38089,7 +39044,9 @@ assert( sqlite3PagerGetData(pPage->pDbPage) == data ); assert( sqlite3PagerIswriteable(pPage->pDbPage) ); assert( sqlite3_mutex_held(pBt->mutex) ); - /*memset(&data[hdr], 0, pBt->usableSize - hdr);*/ +#ifdef SQLITE_SECURE_DELETE + memset(&data[hdr], 0, pBt->usableSize - hdr); +#endif data[hdr] = (char)flags; first = hdr + 8 + 4*((flags&PTF_LEAF)==0 ?1:0); memset(&data[hdr+1], 0, 4); @@ -38132,7 +39089,7 @@ ** means we have started to be concerned about content and the disk ** read should occur at that point. */ -SQLITE_PRIVATE int sqlite3BtreeGetPage( +static int btreeGetPage( BtShared *pBt, /* The btree */ Pgno pgno, /* Number of the page to fetch */ MemPage **ppPage, /* Return the page in this parameter */ @@ -38177,9 +39134,12 @@ } /* -** Get a page from the pager and initialize it. This routine -** is just a convenience wrapper around separate calls to -** sqlite3BtreeGetPage() and sqlite3BtreeInitPage(). +** Get a page from the pager and initialize it. This routine is just a +** convenience wrapper around separate calls to btreeGetPage() and +** btreeInitPage(). +** +** If an error occurs, then the value *ppPage is set to is undefined. It +** may remain unchanged, or it may be set to an invalid value. */ static int getAndInitPage( BtShared *pBt, /* The database file */ @@ -38187,48 +39147,34 @@ MemPage **ppPage /* Write the page pointer here */ ){ int rc; - MemPage *pPage; - + TESTONLY( Pgno iLastPg = pagerPagecount(pBt); ) assert( sqlite3_mutex_held(pBt->mutex) ); - if( pgno==0 ){ - return SQLITE_CORRUPT_BKPT; - } - /* It is often the case that the page we want is already in cache. - ** If so, get it directly. This saves us from having to call - ** pagerPagecount() to make sure pgno is within limits, which results - ** in a measureable performance improvements. - */ - *ppPage = pPage = btreePageLookup(pBt, pgno); - if( pPage ){ - /* Page is already in cache */ - rc = SQLITE_OK; - }else{ - /* Page not in cache. Acquire it. */ - if( pgno>pagerPagecount(pBt) ){ - return SQLITE_CORRUPT_BKPT; + rc = btreeGetPage(pBt, pgno, ppPage, 0); + if( rc==SQLITE_OK ){ + rc = btreeInitPage(*ppPage); + if( rc!=SQLITE_OK ){ + releasePage(*ppPage); } - rc = sqlite3BtreeGetPage(pBt, pgno, ppPage, 0); - if( rc ) return rc; - pPage = *ppPage; - } - if( !pPage->isInit ){ - rc = sqlite3BtreeInitPage(pPage); - } - if( rc!=SQLITE_OK ){ - releasePage(pPage); - *ppPage = 0; } + + /* If the requested page number was either 0 or greater than the page + ** number of the last page in the database, this function should return + ** SQLITE_CORRUPT or some other error (i.e. SQLITE_FULL). Check that this + ** is the case. */ + assert( (pgno>0 && pgno<=iLastPg) || rc!=SQLITE_OK ); + testcase( pgno==0 ); + testcase( pgno==iLastPg ); + return rc; } /* ** Release a MemPage. This should be called once for each prior -** call to sqlite3BtreeGetPage. +** call to btreeGetPage. */ static void releasePage(MemPage *pPage){ if( pPage ){ - assert( pPage->nOverflow==0 || sqlite3PagerPageRefcount(pPage->pDbPage)>1 ); assert( pPage->aData ); assert( pPage->pBt ); assert( sqlite3PagerGetExtra(pPage->pDbPage) == (void*)pPage ); @@ -38256,11 +39202,11 @@ if( sqlite3PagerPageRefcount(pData)>1 ){ /* pPage might not be a btree page; it might be an overflow page ** or ptrmap page or a free page. In those cases, the following - ** call to sqlite3BtreeInitPage() will likely return SQLITE_CORRUPT. + ** call to btreeInitPage() will likely return SQLITE_CORRUPT. ** But no harm is done by this. And it is very important that - ** sqlite3BtreeInitPage() be called on every btree page so we make + ** btreeInitPage() be called on every btree page so we make ** the call for every page that comes in for re-initing. */ - sqlite3BtreeInitPage(pPage); + btreeInitPage(pPage); } } } @@ -38328,6 +39274,10 @@ } p->inTrans = TRANS_NONE; p->db = db; +#ifndef SQLITE_OMIT_SHARED_CACHE + p->lock.pBtree = p; + p->lock.iTable = 1; +#endif #if !defined(SQLITE_OMIT_SHARED_CACHE) && !defined(SQLITE_OMIT_DISKIO) /* @@ -38335,12 +39285,11 @@ ** existing BtShared object that we can share with */ if( isMemdb==0 && zFilename && zFilename[0] ){ - if( sqlite3GlobalConfig.sharedCacheEnabled ){ + if( vfsFlags & SQLITE_OPEN_SHAREDCACHE ){ int nFullPathname = pVfs->mxPathname+1; char *zFullPathname = sqlite3Malloc(nFullPathname); sqlite3_mutex *mutexShared; p->sharable = 1; - db->flags |= SQLITE_SharedCache; if( !zFullPathname ){ sqlite3_free(p); return SQLITE_NOMEM; @@ -38403,7 +39352,7 @@ goto btree_open_out; } rc = sqlite3PagerOpen(pVfs, &pBt->pPager, zFilename, - EXTRA_SIZE, flags, vfsFlags); + EXTRA_SIZE, flags, vfsFlags, pageReinit); if( rc==SQLITE_OK ){ rc = sqlite3PagerReadFileheader(pBt->pPager,sizeof(zDbHeader),zDbHeader); } @@ -38414,7 +39363,6 @@ sqlite3PagerSetBusyhandler(pBt->pPager, btreeInvokeBusyHandler, pBt); p->pBt = pBt; - sqlite3PagerSetReiniter(pBt->pPager, pageReinit); pBt->pCursor = 0; pBt->pPage1 = 0; pBt->readOnly = sqlite3PagerIsreadonly(pBt->pPager); @@ -38839,7 +39787,9 @@ assert( sqlite3_mutex_held(pBt->mutex) ); assert( pBt->pPage1==0 ); - rc = sqlite3BtreeGetPage(pBt, 1, &pPage1, 0); + rc = sqlite3PagerSharedLock(pBt->pPager); + if( rc!=SQLITE_OK ) return rc; + rc = btreeGetPage(pBt, 1, &pPage1, 0); if( rc!=SQLITE_OK ) return rc; /* Do some checking to help insure the file we opened really is @@ -38892,8 +39842,7 @@ freeTempSpace(pBt); rc = sqlite3PagerSetPagesize(pBt->pPager, &pBt->pageSize, pageSize-usableSize); - if( rc ) goto page1_init_failed; - return SQLITE_OK; + return rc; } if( usableSize<480 ){ goto page1_init_failed; @@ -38934,41 +39883,17 @@ } /* -** This routine works like lockBtree() except that it also invokes the -** busy callback if there is lock contention. -*/ -static int lockBtreeWithRetry(Btree *pRef){ - int rc = SQLITE_OK; - - assert( sqlite3BtreeHoldsMutex(pRef) ); - if( pRef->inTrans==TRANS_NONE ){ - u8 inTransaction = pRef->pBt->inTransaction; - btreeIntegrity(pRef); - rc = sqlite3BtreeBeginTrans(pRef, 0); - pRef->pBt->inTransaction = inTransaction; - pRef->inTrans = TRANS_NONE; - if( rc==SQLITE_OK ){ - pRef->pBt->nTransaction--; - } - btreeIntegrity(pRef); - } - return rc; -} - - -/* ** If there are no outstanding cursors and we are not in the middle ** of a transaction but there is a read lock on the database, then ** this routine unrefs the first page of the database file which ** has the effect of releasing the read lock. ** -** If there are any outstanding cursors, this routine is a no-op. -** ** If there is a transaction in progress, this routine is a no-op. */ static void unlockBtreeIfUnused(BtShared *pBt){ assert( sqlite3_mutex_held(pBt->mutex) ); - if( pBt->inTransaction==TRANS_NONE && pBt->pCursor==0 && pBt->pPage1!=0 ){ + assert( pBt->pCursor==0 || pBt->inTransaction>TRANS_NONE ); + if( pBt->inTransaction==TRANS_NONE && pBt->pPage1!=0 ){ assert( pBt->pPage1->aData ); assert( sqlite3PagerRefcount(pBt->pPager)==1 ); assert( pBt->pPage1->aData ); @@ -38978,8 +39903,9 @@ } /* -** Create a new database by initializing the first page of the -** file. +** If pBt points to an empty file then convert that empty file +** into a new empty database by initializing the first page of +** the database. */ static int newDatabase(BtShared *pBt){ MemPage *pP1; @@ -39099,6 +40025,12 @@ } #endif + /* Any read-only or read-write transaction implies a read-lock on + ** page 1. So if some other shared-cache client already has a write-lock + ** on page 1, the transaction cannot be opened. */ + rc = querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK); + if( SQLITE_OK!=rc ) goto trans_begun; + do { /* Call lockBtree() until either pBt->pPage1 is populated or ** lockBtree() returns something other than SQLITE_OK. lockBtree() @@ -39129,6 +40061,14 @@ if( rc==SQLITE_OK ){ if( p->inTrans==TRANS_NONE ){ pBt->nTransaction++; +#ifndef SQLITE_OMIT_SHARED_CACHE + if( p->sharable ){ + assert( p->lock.pBtree==p && p->lock.iTable==1 ); + p->lock.eLock = READ_LOCK; + p->lock.pNext = pBt->pLock; + pBt->pLock = &p->lock; + } +#endif } p->inTrans = (wrflag?TRANS_WRITE:TRANS_READ); if( p->inTrans>pBt->inTransaction ){ @@ -39174,7 +40114,7 @@ Pgno pgno = pPage->pgno; assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - rc = sqlite3BtreeInitPage(pPage); + rc = btreeInitPage(pPage); if( rc!=SQLITE_OK ){ goto set_child_ptrmaps_out; } @@ -39183,21 +40123,17 @@ for(i=0; ileaf ){ Pgno childPgno = get4byte(pCell); - rc = ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno); - if( rc!=SQLITE_OK ) goto set_child_ptrmaps_out; + ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno, &rc); } } if( !pPage->leaf ){ Pgno childPgno = get4byte(&pPage->aData[pPage->hdrOffset+8]); - rc = ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno); + ptrmapPut(pBt, childPgno, PTRMAP_BTREE, pgno, &rc); } set_child_ptrmaps_out: @@ -39206,10 +40142,9 @@ } /* -** Somewhere on pPage, which is guaranteed to be a btree page, not an overflow -** page, is a pointer to page iFrom. Modify this pointer so that it points to -** iTo. Parameter eType describes the type of pointer to be modified, as -** follows: +** Somewhere on pPage is a pointer to page iFrom. Modify this pointer so +** that it points to iTo. Parameter eType describes the type of pointer to +** be modified, as follows: ** ** PTRMAP_BTREE: pPage is a btree-page. The pointer points at a child ** page of pPage. @@ -39234,14 +40169,14 @@ int i; int nCell; - sqlite3BtreeInitPage(pPage); + btreeInitPage(pPage); nCell = pPage->nCell; for(i=0; ipgno +** can be written to. The caller has already promised not to write to that +** page. */ static int relocatePage( BtShared *pBt, /* Btree */ @@ -39280,7 +40220,7 @@ u8 eType, /* Pointer map 'type' entry for pDbPage */ Pgno iPtrPage, /* Pointer map 'page-no' entry for pDbPage */ Pgno iFreePage, /* The location to move pDbPage to */ - int isCommit + int isCommit /* isCommit flag passed to sqlite3PagerMovepage */ ){ MemPage *pPtrPage; /* The page that contains a pointer to pDbPage */ Pgno iDbPage = pDbPage->pgno; @@ -39317,7 +40257,7 @@ }else{ Pgno nextOvfl = get4byte(pDbPage->aData); if( nextOvfl!=0 ){ - rc = ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage); + ptrmapPut(pBt, nextOvfl, PTRMAP_OVERFLOW2, iFreePage, &rc); if( rc!=SQLITE_OK ){ return rc; } @@ -39329,7 +40269,7 @@ ** iPtrPage. */ if( eType!=PTRMAP_ROOTPAGE ){ - rc = sqlite3BtreeGetPage(pBt, iPtrPage, &pPtrPage, 0); + rc = btreeGetPage(pBt, iPtrPage, &pPtrPage, 0); if( rc!=SQLITE_OK ){ return rc; } @@ -39341,7 +40281,7 @@ rc = modifyPagePointer(pPtrPage, iDbPage, iFreePage, eType); releasePage(pPtrPage); if( rc==SQLITE_OK ){ - rc = ptrmapPut(pBt, iFreePage, eType, iPtrPage); + ptrmapPut(pBt, iFreePage, eType, iPtrPage, &rc); } } return rc; @@ -39359,11 +40299,14 @@ ** database so that the last page of the file currently in use ** is no longer in use. ** -** If the nFin parameter is non-zero, the implementation assumes +** If the nFin parameter is non-zero, this function assumes ** that the caller will keep calling incrVacuumStep() until ** it returns SQLITE_DONE or an error, and that nFin is the ** number of pages the database file will contain after this -** process is complete. +** process is complete. If nFin is zero, it is assumed that +** incrVacuumStep() will be called a finite amount of times +** which may or may not empty the freelist. A full autovacuum +** has nFin>0. A "PRAGMA incremental_vacuum" has nFin==0. */ static int incrVacuumStep(BtShared *pBt, Pgno nFin, Pgno iLastPg){ Pgno nFreeList; /* Number of pages still on the free-list */ @@ -39409,7 +40352,7 @@ Pgno iFreePg; /* Index of free page to move pLastPg to */ MemPage *pLastPg; - rc = sqlite3BtreeGetPage(pBt, iLastPg, &pLastPg, 0); + rc = btreeGetPage(pBt, iLastPg, &pLastPg, 0); if( rc!=SQLITE_OK ){ return rc; } @@ -39448,7 +40391,7 @@ while( iLastPg==PENDING_BYTE_PAGE(pBt)||PTRMAP_ISPAGE(pBt, iLastPg) ){ if( PTRMAP_ISPAGE(pBt, iLastPg) ){ MemPage *pPg; - int rc = sqlite3BtreeGetPage(pBt, iLastPg, &pPg, 0); + int rc = btreeGetPage(pBt, iLastPg, &pPg, 0); if( rc!=SQLITE_OK ){ return rc; } @@ -39507,13 +40450,14 @@ invalidateAllOverflowCache(pBt); assert(pBt->autoVacuum); if( !pBt->incrVacuum ){ - Pgno nFin; - Pgno nFree; - Pgno nPtrmap; - Pgno iFree; - const int pgsz = pBt->pageSize; - Pgno nOrig = pagerPagecount(pBt); + Pgno nFin; /* Number of pages in database after autovacuuming */ + Pgno nFree; /* Number of pages on the freelist initially */ + Pgno nPtrmap; /* Number of PtrMap pages to be freed */ + Pgno iFree; /* The next page to be freed */ + int nEntry; /* Number of entries on one ptrmap page */ + Pgno nOrig; /* Database size before freeing */ + nOrig = pagerPagecount(pBt); if( PTRMAP_ISPAGE(pBt, nOrig) || nOrig==PENDING_BYTE_PAGE(pBt) ){ /* It is not possible to create a database for which the final page ** is either a pointer-map page or the pending-byte page. If one @@ -39523,7 +40467,8 @@ } nFree = get4byte(&pBt->pPage1->aData[36]); - nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+pgsz/5)/(pgsz/5); + nEntry = pBt->usableSize/5; + nPtrmap = (nFree-nOrig+PTRMAP_PAGENO(pBt, nOrig)+nEntry)/nEntry; nFin = nOrig - nFree - nPtrmap; if( nOrig>PENDING_BYTE_PAGE(pBt) && nFinpBt; + assert( sqlite3BtreeHoldsMutex(p) ); + + btreeClearHasContent(pBt); + if( p->inTrans>TRANS_NONE && p->db->activeVdbeCnt>1 ){ + /* If there are other active statements that belong to this database + ** handle, downgrade to a read-only transaction. The other statements + ** may still be reading from the database. */ + downgradeAllSharedCacheTableLocks(p); + p->inTrans = TRANS_READ; + }else{ + /* If the handle had any kind of transaction open, decrement the + ** transaction count of the shared btree. If the transaction count + ** reaches 0, set the shared state to TRANS_NONE. The unlockBtreeIfUnused() + ** call below will unlock the pager. */ + if( p->inTrans!=TRANS_NONE ){ + clearAllSharedCacheTableLocks(p); + pBt->nTransaction--; + if( 0==pBt->nTransaction ){ + pBt->inTransaction = TRANS_NONE; + } + } + + /* Set the current transaction state to TRANS_NONE and unlock the + ** pager if this call closed the only read or write transaction. */ + p->inTrans = TRANS_NONE; + unlockBtreeIfUnused(pBt); + } + + btreeIntegrity(p); +} + +/* ** Commit the transaction currently in progress. ** ** This routine implements the second phase of a 2-phase commit. The @@ -39638,27 +40620,7 @@ pBt->inTransaction = TRANS_READ; } - /* If the handle has any kind of transaction open, decrement the transaction - ** count of the shared btree. If the transaction count reaches 0, set - ** the shared state to TRANS_NONE. The unlockBtreeIfUnused() call below - ** will unlock the pager. - */ - if( p->inTrans!=TRANS_NONE ){ - clearAllSharedCacheTableLocks(p); - pBt->nTransaction--; - if( 0==pBt->nTransaction ){ - pBt->inTransaction = TRANS_NONE; - } - } - - /* Set the current transaction state to TRANS_NONE and unlock - ** the pager if this call closed the only read or write transaction. - */ - btreeClearHasContent(pBt); - p->inTrans = TRANS_NONE; - unlockBtreeIfUnused(pBt); - - btreeIntegrity(p); + btreeEndTransaction(p); sqlite3BtreeLeave(p); return SQLITE_OK; } @@ -39722,7 +40684,7 @@ int i; sqlite3BtreeClearCursor(p); p->eState = CURSOR_FAULT; - p->skip = errCode; + p->skipNext = errCode; for(i=0; i<=p->iPage; i++){ releasePage(p->apPage[i]); p->apPage[i] = 0; @@ -39771,29 +40733,16 @@ } /* The rollback may have destroyed the pPage1->aData value. So - ** call sqlite3BtreeGetPage() on page 1 again to make + ** call btreeGetPage() on page 1 again to make ** sure pPage1->aData is set correctly. */ - if( sqlite3BtreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){ + if( btreeGetPage(pBt, 1, &pPage1, 0)==SQLITE_OK ){ releasePage(pPage1); } assert( countWriteCursors(pBt)==0 ); pBt->inTransaction = TRANS_READ; } - if( p->inTrans!=TRANS_NONE ){ - clearAllSharedCacheTableLocks(p); - assert( pBt->nTransaction>0 ); - pBt->nTransaction--; - if( 0==pBt->nTransaction ){ - pBt->inTransaction = TRANS_NONE; - } - } - - btreeClearHasContent(pBt); - p->inTrans = TRANS_NONE; - unlockBtreeIfUnused(pBt); - - btreeIntegrity(p); + btreeEndTransaction(p); sqlite3BtreeLeave(p); return rc; } @@ -39869,8 +40818,10 @@ /* ** Create a new cursor for the BTree whose root is on the page -** iTable. The act of acquiring a cursor gets a read lock on -** the database file. +** iTable. If a read-only cursor is requested, it is assumed that +** the caller already has at least a read-only transaction open +** on the database already. If a write-cursor is requested, then +** the caller is assumed to have an open write transaction. ** ** If wrFlag==0, then the cursor can only be used for reading. ** If wrFlag==1, then the cursor can be used for reading or for @@ -39894,8 +40845,8 @@ ** root page of a b-tree. If it is not, then the cursor acquired ** will not work correctly. ** -** It is assumed that the sqlite3BtreeCursorSize() bytes of memory -** pointed to by pCur have been zeroed by the caller. +** It is assumed that the sqlite3BtreeCursorZero() has been called +** on pCur to initialize the memory space prior to invoking this routine. */ static int btreeCursor( Btree *p, /* The btree */ @@ -39904,48 +40855,34 @@ struct KeyInfo *pKeyInfo, /* First arg to comparison function */ BtCursor *pCur /* Space for new cursor */ ){ - int rc; - Pgno nPage; - BtShared *pBt = p->pBt; + BtShared *pBt = p->pBt; /* Shared b-tree handle */ assert( sqlite3BtreeHoldsMutex(p) ); assert( wrFlag==0 || wrFlag==1 ); - if( wrFlag ){ - assert( !pBt->readOnly ); - if( NEVER(pBt->readOnly) ){ - return SQLITE_READONLY; - } - rc = checkForReadConflicts(p, iTable, 0, 0); - if( rc!=SQLITE_OK ){ - assert( rc==SQLITE_LOCKED_SHAREDCACHE ); - return rc; - } - } - if( pBt->pPage1==0 ){ - rc = lockBtreeWithRetry(p); - if( rc!=SQLITE_OK ){ - return rc; - } - } - pCur->pgnoRoot = (Pgno)iTable; - rc = sqlite3PagerPagecount(pBt->pPager, (int *)&nPage); - if( rc!=SQLITE_OK ){ - return rc; - } - if( iTable==1 && nPage==0 ){ - rc = SQLITE_EMPTY; - goto create_cursor_exception; + /* The following assert statements verify that if this is a sharable + ** b-tree database, the connection is holding the required table locks, + ** and that no other connection has any open cursor that conflicts with + ** this lock. */ + assert( hasSharedCacheTableLock(p, iTable, pKeyInfo!=0, wrFlag+1) ); + assert( wrFlag==0 || !hasReadConflicts(p, iTable) ); + + /* Assert that the caller has opened the required transaction. */ + assert( p->inTrans>TRANS_NONE ); + assert( wrFlag==0 || p->inTrans==TRANS_WRITE ); + assert( pBt->pPage1 && pBt->pPage1->aData ); + + if( NEVER(wrFlag && pBt->readOnly) ){ + return SQLITE_READONLY; } - rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0]); - if( rc!=SQLITE_OK ){ - goto create_cursor_exception; + if( iTable==1 && pagerPagecount(pBt)==0 ){ + return SQLITE_EMPTY; } /* Now that no other errors can occur, finish filling in the BtCursor - ** variables, link the cursor into the BtShared list and set *ppCur (the - ** output argument to this function). - */ + ** variables and link the cursor into the BtShared list. */ + pCur->pgnoRoot = (Pgno)iTable; + pCur->iPage = -1; pCur->pKeyInfo = pKeyInfo; pCur->pBtree = p; pCur->pBt = pBt; @@ -39957,13 +40894,7 @@ pBt->pCursor = pCur; pCur->eState = CURSOR_INVALID; pCur->cachedRowid = 0; - return SQLITE_OK; - -create_cursor_exception: - releasePage(pCur->apPage[0]); - unlockBtreeIfUnused(pBt); - return rc; } SQLITE_PRIVATE int sqlite3BtreeCursor( Btree *p, /* The btree */ @@ -39988,7 +40919,19 @@ ** this routine. */ SQLITE_PRIVATE int sqlite3BtreeCursorSize(void){ - return sizeof(BtCursor); + return ROUND8(sizeof(BtCursor)); +} + +/* +** Initialize memory that will be converted into a BtCursor object. +** +** The simple approach here would be to memset() the entire object +** to zero. But it turns out that the apPage[] and aiIdx[] arrays +** do not need to be zeroed and they are large, so we can save a lot +** of run-time by skipping the initialization of those elements. +*/ +SQLITE_PRIVATE void sqlite3BtreeCursorZero(BtCursor *p){ + memset(p, 0, offsetof(BtCursor, iPage)); } /* @@ -40051,46 +40994,13 @@ return SQLITE_OK; } -#ifdef SQLITE_TEST -/* -** Make a temporary cursor by filling in the fields of pTempCur. -** The temporary cursor is not on the cursor list for the Btree. -*/ -SQLITE_PRIVATE void sqlite3BtreeGetTempCursor(BtCursor *pCur, BtCursor *pTempCur){ - int i; - assert( cursorHoldsMutex(pCur) ); - memcpy(pTempCur, pCur, sizeof(BtCursor)); - pTempCur->pNext = 0; - pTempCur->pPrev = 0; - for(i=0; i<=pTempCur->iPage; i++){ - sqlite3PagerRef(pTempCur->apPage[i]->pDbPage); - } - assert( pTempCur->pKey==0 ); -} -#endif /* SQLITE_TEST */ - -#ifdef SQLITE_TEST -/* -** Delete a temporary cursor such as was made by the CreateTemporaryCursor() -** function above. -*/ -SQLITE_PRIVATE void sqlite3BtreeReleaseTempCursor(BtCursor *pCur){ - int i; - assert( cursorHoldsMutex(pCur) ); - for(i=0; i<=pCur->iPage; i++){ - sqlite3PagerUnref(pCur->apPage[i]->pDbPage); - } - sqlite3_free(pCur->pKey); -} -#endif /* SQLITE_TEST */ - /* ** Make sure the BtCursor* given in the argument has a valid ** BtCursor.info structure. If it is not already valid, call -** sqlite3BtreeParseCell() to fill it in. +** btreeParseCell() to fill it in. ** ** BtCursor.info is a cache of the information in the current cell. -** Using this cache reduces the number of calls to sqlite3BtreeParseCell(). +** Using this cache reduces the number of calls to btreeParseCell(). ** ** 2007-06-25: There is a bug in some versions of MSVC that cause the ** compiler to crash when getCellInfo() is implemented as a macro. @@ -40104,7 +41014,7 @@ CellInfo info; int iPage = pCur->iPage; memset(&info, 0, sizeof(info)); - sqlite3BtreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info); + btreeParseCell(pCur->apPage[iPage], pCur->aiIdx[iPage], &info); assert( memcmp(&info, &pCur->info, sizeof(info))==0 ); } #else @@ -40115,7 +41025,7 @@ static void getCellInfo(BtCursor *pCur){ if( pCur->info.nSize==0 ){ int iPage = pCur->iPage; - sqlite3BtreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); + btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); pCur->validNKey = 1; }else{ assertCellInfo(pCur); @@ -40126,13 +41036,24 @@ #define getCellInfo(pCur) \ if( pCur->info.nSize==0 ){ \ int iPage = pCur->iPage; \ - sqlite3BtreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \ + btreeParseCell(pCur->apPage[iPage],pCur->aiIdx[iPage],&pCur->info); \ pCur->validNKey = 1; \ }else{ \ assertCellInfo(pCur); \ } #endif /* _MSC_VER */ +#ifndef NDEBUG /* The next routine used only within assert() statements */ +/* +** Return true if the given BtCursor is valid. A valid cursor is one +** that is currently pointing to a row in a (non-empty) table. +** This is a verification routine is used only within assert() statements. +*/ +SQLITE_PRIVATE int sqlite3BtreeCursorIsValid(BtCursor *pCur){ + return pCur && pCur->eState==CURSOR_VALID; +} +#endif /* NDEBUG */ + /* ** Set *pSize to the size of the buffer needed to hold the value of ** the key for the current entry. If the cursor is not pointing @@ -40140,47 +41061,41 @@ ** ** For a table with the INTKEY flag set, this routine returns the key ** itself, not the number of bytes in the key. +** +** The caller must position the cursor prior to invoking this routine. +** +** This routine cannot fail. It always returns SQLITE_OK. */ SQLITE_PRIVATE int sqlite3BtreeKeySize(BtCursor *pCur, i64 *pSize){ - int rc; - assert( cursorHoldsMutex(pCur) ); - rc = restoreCursorPosition(pCur); - if( rc==SQLITE_OK ){ - assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID ); - if( pCur->eState==CURSOR_INVALID ){ - *pSize = 0; - }else{ - getCellInfo(pCur); - *pSize = pCur->info.nKey; - } + assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID ); + if( pCur->eState!=CURSOR_VALID ){ + *pSize = 0; + }else{ + getCellInfo(pCur); + *pSize = pCur->info.nKey; } - return rc; + return SQLITE_OK; } /* ** Set *pSize to the number of bytes of data in the entry the -** cursor currently points to. Always return SQLITE_OK. -** Failure is not possible. If the cursor is not currently -** pointing to an entry (which can happen, for example, if -** the database is empty) then *pSize is set to 0. +** cursor currently points to. +** +** The caller must guarantee that the cursor is pointing to a non-NULL +** valid entry. In other words, the calling procedure must guarantee +** that the cursor has Cursor.eState==CURSOR_VALID. +** +** Failure is not possible. This function always returns SQLITE_OK. +** It might just as well be a procedure (returning void) but we continue +** to return an integer result code for historical reasons. */ SQLITE_PRIVATE int sqlite3BtreeDataSize(BtCursor *pCur, u32 *pSize){ - int rc; - assert( cursorHoldsMutex(pCur) ); - rc = restoreCursorPosition(pCur); - if( rc==SQLITE_OK ){ - assert( pCur->eState==CURSOR_INVALID || pCur->eState==CURSOR_VALID ); - if( pCur->eState==CURSOR_INVALID ){ - /* Not pointing at a valid entry - set *pSize to 0. */ - *pSize = 0; - }else{ - getCellInfo(pCur); - *pSize = pCur->info.nData; - } - } - return rc; + assert( pCur->eState==CURSOR_VALID ); + getCellInfo(pCur); + *pSize = pCur->info.nData; + return SQLITE_OK; } /* @@ -40203,8 +41118,8 @@ ** *ppPage is set to zero. */ static int getOverflowPage( - BtShared *pBt, - Pgno ovfl, /* Overflow page */ + BtShared *pBt, /* The database file */ + Pgno ovfl, /* Current overflow page number */ MemPage **ppPage, /* OUT: MemPage handle (may be NULL) */ Pgno *pPgnoNext /* OUT: Next overflow page number */ ){ @@ -40241,10 +41156,11 @@ } #endif + assert( next==0 || rc==SQLITE_DONE ); if( rc==SQLITE_OK ){ - rc = sqlite3BtreeGetPage(pBt, ovfl, &pPage, 0); - assert(rc==SQLITE_OK || pPage==0); - if( next==0 && rc==SQLITE_OK ){ + rc = btreeGetPage(pBt, ovfl, &pPage, 0); + assert( rc==SQLITE_OK || pPage==0 ); + if( rc==SQLITE_OK ){ next = get4byte(pPage->aData); } } @@ -40300,10 +41216,8 @@ ** A total of "amt" bytes are read or written beginning at "offset". ** Data is read to or from the buffer pBuf. ** -** This routine does not make a distinction between key and data. -** It just reads or writes bytes from the payload area. Data might -** appear on the main page or be scattered out on multiple overflow -** pages. +** The content being read or written might appear on the main page +** or be scattered out on multiple overflow pages. ** ** If the BtCursor.isIncrblobHandle flag is set, and the current ** cursor entry uses one or more overflow pages, this function @@ -40325,7 +41239,6 @@ u32 offset, /* Begin reading this far into payload */ u32 amt, /* Read this many bytes */ unsigned char *pBuf, /* Write the bytes into this buffer */ - int skipKey, /* offset begins at data if this is true */ int eOp /* zero to read. non-zero to write. */ ){ unsigned char *aPayload; @@ -40344,10 +41257,7 @@ aPayload = pCur->info.pCell + pCur->info.nHeader; nKey = (pPage->intKey ? 0 : (int)pCur->info.nKey); - if( skipKey ){ - offset += nKey; - } - if( offset+amt > nKey+pCur->info.nData + if( NEVER(offset+amt > nKey+pCur->info.nData) || &aPayload[pCur->info.nLocal] > &pPage->aData[pBt->usableSize] ){ /* Trying to read or write past the end of the data is an error */ @@ -40385,7 +41295,9 @@ if( pCur->isIncrblobHandle && !pCur->aOverflow ){ int nOvfl = (pCur->info.nPayload-pCur->info.nLocal+ovflSize-1)/ovflSize; pCur->aOverflow = (Pgno *)sqlite3MallocZero(sizeof(Pgno)*nOvfl); - if( nOvfl && !pCur->aOverflow ){ + /* nOvfl is always positive. If it were zero, fetchPayload would have + ** been used instead of this routine. */ + if( ALWAYS(nOvfl) && !pCur->aOverflow ){ rc = SQLITE_NOMEM; } } @@ -40459,25 +41371,19 @@ ** "amt" bytes will be transfered into pBuf[]. The transfer ** begins at "offset". ** +** The caller must ensure that pCur is pointing to a valid row +** in the table. +** ** Return SQLITE_OK on success or an error code if anything goes ** wrong. An error is returned if "offset+amt" is larger than ** the available payload. */ SQLITE_PRIVATE int sqlite3BtreeKey(BtCursor *pCur, u32 offset, u32 amt, void *pBuf){ - int rc; - assert( cursorHoldsMutex(pCur) ); - rc = restoreCursorPosition(pCur); - if( rc==SQLITE_OK ){ - assert( pCur->eState==CURSOR_VALID ); - assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] ); - if( pCur->apPage[0]->intKey ){ - return SQLITE_CORRUPT_BKPT; - } - assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); - rc = accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0, 0); - } - return rc; + assert( pCur->eState==CURSOR_VALID ); + assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] ); + assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); + return accessPayload(pCur, offset, amt, (unsigned char*)pBuf, 0); } /* @@ -40504,7 +41410,7 @@ assert( pCur->eState==CURSOR_VALID ); assert( pCur->iPage>=0 && pCur->apPage[pCur->iPage] ); assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); - rc = accessPayload(pCur, offset, amt, pBuf, 1, 0); + rc = accessPayload(pCur, offset, amt, pBuf, 0); } return rc; } @@ -40543,7 +41449,10 @@ assert( cursorHoldsMutex(pCur) ); pPage = pCur->apPage[pCur->iPage]; assert( pCur->aiIdx[pCur->iPage]nCell ); - getCellInfo(pCur); + if( NEVER(pCur->info.nSize==0) ){ + btreeParseCell(pCur->apPage[pCur->iPage], pCur->aiIdx[pCur->iPage], + &pCur->info); + } aPayload = pCur->info.pCell; aPayload += pCur->info.nHeader; if( pPage->intKey ){ @@ -40556,9 +41465,7 @@ nLocal = pCur->info.nLocal - nKey; }else{ nLocal = pCur->info.nLocal; - if( nLocal>nKey ){ - nLocal = nKey; - } + assert( nLocal<=nKey ); } *pAmt = nLocal; return aPayload; @@ -40580,26 +41487,33 @@ ** in the common case where no overflow pages are used. */ SQLITE_PRIVATE const void *sqlite3BtreeKeyFetch(BtCursor *pCur, int *pAmt){ + const void *p = 0; assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert( cursorHoldsMutex(pCur) ); - if( pCur->eState==CURSOR_VALID ){ - return (const void*)fetchPayload(pCur, pAmt, 0); + if( ALWAYS(pCur->eState==CURSOR_VALID) ){ + p = (const void*)fetchPayload(pCur, pAmt, 0); } - return 0; + return p; } SQLITE_PRIVATE const void *sqlite3BtreeDataFetch(BtCursor *pCur, int *pAmt){ + const void *p = 0; assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); assert( cursorHoldsMutex(pCur) ); - if( pCur->eState==CURSOR_VALID ){ - return (const void*)fetchPayload(pCur, pAmt, 1); + if( ALWAYS(pCur->eState==CURSOR_VALID) ){ + p = (const void*)fetchPayload(pCur, pAmt, 1); } - return 0; + return p; } /* ** Move the cursor down to a new child page. The newPgno argument is the ** page number of the child page to move to. +** +** This function returns SQLITE_CORRUPT if the page-header flags field of +** the new child page does not match the flags field of the parent (i.e. +** if an intkey page appears to be the parent of a non-intkey page, or +** vice-versa). */ static int moveToChild(BtCursor *pCur, u32 newPgno){ int rc; @@ -40621,7 +41535,7 @@ pCur->info.nSize = 0; pCur->validNKey = 0; - if( pNewPage->nCell<1 ){ + if( pNewPage->nCell<1 || pNewPage->intKey!=pCur->apPage[i]->intKey ){ return SQLITE_CORRUPT_BKPT; } return SQLITE_OK; @@ -40655,7 +41569,7 @@ ** right-most child page then pCur->idx is set to one more than ** the largest cell index. */ -SQLITE_PRIVATE void sqlite3BtreeMoveToParent(BtCursor *pCur){ +static void moveToParent(BtCursor *pCur){ assert( cursorHoldsMutex(pCur) ); assert( pCur->eState==CURSOR_VALID ); assert( pCur->iPage>0 ); @@ -40672,7 +41586,25 @@ } /* -** Move the cursor to the root page +** Move the cursor to point to the root page of its b-tree structure. +** +** If the table has a virtual root page, then the cursor is moved to point +** to the virtual root page instead of the actual root page. A table has a +** virtual root page when the actual root page contains no cells and a +** single child page. This can only happen with the table rooted at page 1. +** +** If the b-tree structure is empty, the cursor state is set to +** CURSOR_INVALID. Otherwise, the cursor is set to point to the first +** cell located on the root (or virtual root) page and the cursor state +** is set to CURSOR_VALID. +** +** If this function returns successfully, it may be assumed that the +** page-header flags indicate that the [virtual] root-page is the expected +** kind of b-tree page (i.e. if when opening the cursor the caller did not +** specify a KeyInfo structure the flags byte is set to 0x05 or 0x0D, +** indicating a table b-tree, or if the caller did specify a KeyInfo +** structure the flags byte is set to 0x02 or 0x0A, indicating an index +** b-tree). */ static int moveToRoot(BtCursor *pCur){ MemPage *pRoot; @@ -40686,7 +41618,8 @@ assert( CURSOR_FAULT > CURSOR_REQUIRESEEK ); if( pCur->eState>=CURSOR_REQUIRESEEK ){ if( pCur->eState==CURSOR_FAULT ){ - return pCur->skip; + assert( pCur->skipNext!=SQLITE_OK ); + return pCur->skipNext; } sqlite3BtreeClearCursor(pCur); } @@ -40696,18 +41629,35 @@ for(i=1; i<=pCur->iPage; i++){ releasePage(pCur->apPage[i]); } + pCur->iPage = 0; }else{ - if( - SQLITE_OK!=(rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0])) - ){ + rc = getAndInitPage(pBt, pCur->pgnoRoot, &pCur->apPage[0]); + if( rc!=SQLITE_OK ){ pCur->eState = CURSOR_INVALID; return rc; } + pCur->iPage = 0; + + /* If pCur->pKeyInfo is not NULL, then the caller that opened this cursor + ** expected to open it on an index b-tree. Otherwise, if pKeyInfo is + ** NULL, the caller expects a table b-tree. If this is not the case, + ** return an SQLITE_CORRUPT error. */ + assert( pCur->apPage[0]->intKey==1 || pCur->apPage[0]->intKey==0 ); + if( (pCur->pKeyInfo==0)!=pCur->apPage[0]->intKey ){ + return SQLITE_CORRUPT_BKPT; + } } + /* Assert that the root page is of the correct type. This must be the + ** case as the call to this function that loaded the root-page (either + ** this call or a previous invocation) would have detected corruption + ** if the assumption were not true, and it is not possible for the flags + ** byte to have been modified while this cursor is holding a reference + ** to the page. */ pRoot = pCur->apPage[0]; assert( pRoot->pgno==pCur->pgnoRoot ); - pCur->iPage = 0; + assert( pRoot->isInit && (pCur->pKeyInfo==0)==pRoot->intKey ); + pCur->aiIdx[0] = 0; pCur->info.nSize = 0; pCur->atLast = 0; @@ -40716,9 +41666,7 @@ if( pRoot->nCell==0 && !pRoot->leaf ){ Pgno subpage; if( pRoot->pgno!=1 ) return SQLITE_CORRUPT_BKPT; - assert( pRoot->pgno==1 ); subpage = get4byte(&pRoot->aData[pRoot->hdrOffset+8]); - assert( subpage>0 ); pCur->eState = CURSOR_VALID; rc = moveToChild(pCur, subpage); }else{ @@ -40882,6 +41830,8 @@ assert( cursorHoldsMutex(pCur) ); assert( sqlite3_mutex_held(pCur->pBtree->db->mutex) ); + assert( pRes ); + assert( (pIdxKey==0)==(pCur->pKeyInfo==0) ); /* If the cursor is already positioned at the point we are trying ** to move to, then just return without doing any work */ @@ -40904,6 +41854,7 @@ } assert( pCur->apPage[pCur->iPage] ); assert( pCur->apPage[pCur->iPage]->isInit ); + assert( pCur->apPage[pCur->iPage]->nCell>0 || pCur->eState==CURSOR_INVALID ); if( pCur->eState==CURSOR_INVALID ){ *pRes = -1; assert( pCur->apPage[pCur->iPage]->nCell==0 ); @@ -40914,13 +41865,18 @@ int lwr, upr; Pgno chldPg; MemPage *pPage = pCur->apPage[pCur->iPage]; - int c = -1; /* pRes return if table is empty must be -1 */ + int c; + + /* pPage->nCell must be greater than zero. If this is the root-page + ** the cursor would have been INVALID above and this for(;;) loop + ** not run. If this is not the root-page, then the moveToChild() routine + ** would have already detected db corruption. Similarly, pPage must + ** be the right kind (index or table) of b-tree page. Otherwise + ** a moveToChild() or moveToRoot() call would have detected corruption. */ + assert( pPage->nCell>0 ); + assert( pPage->intKey==(pIdxKey==0) ); lwr = 0; upr = pPage->nCell-1; - if( (!pPage->intKey && pIdxKey==0) || upr<0 ){ - rc = SQLITE_CORRUPT_BKPT; - goto moveto_finish; - } if( biasRight ){ pCur->aiIdx[pCur->iPage] = (u16)upr; }else{ @@ -40977,17 +41933,20 @@ ** buffer before VdbeRecordCompare() can be called. */ void *pCellKey; u8 * const pCellBody = pCell - pPage->childPtrSize; - sqlite3BtreeParseCellPtr(pPage, pCellBody, &pCur->info); + btreeParseCellPtr(pPage, pCellBody, &pCur->info); nCell = (int)pCur->info.nKey; pCellKey = sqlite3Malloc( nCell ); if( pCellKey==0 ){ rc = SQLITE_NOMEM; goto moveto_finish; } - rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0, 0); + rc = accessPayload(pCur, 0, nCell, (unsigned char*)pCellKey, 0); + if( rc ){ + sqlite3_free(pCellKey); + goto moveto_finish; + } c = sqlite3VdbeRecordCompare(nCell, pCellKey, pIdxKey); sqlite3_free(pCellKey); - if( rc ) goto moveto_finish; } } if( c==0 ){ @@ -41022,7 +41981,7 @@ } if( chldPg==0 ){ assert( pCur->aiIdx[pCur->iPage]apPage[pCur->iPage]->nCell ); - if( pRes ) *pRes = c; + *pRes = c; rc = SQLITE_OK; goto moveto_finish; } @@ -41036,38 +41995,6 @@ return rc; } -/* -** In this version of BtreeMoveto, pKey is a packed index record -** such as is generated by the OP_MakeRecord opcode. Unpack the -** record and then call BtreeMovetoUnpacked() to do the work. -*/ -SQLITE_PRIVATE int sqlite3BtreeMoveto( - BtCursor *pCur, /* Cursor open on the btree to be searched */ - const void *pKey, /* Packed key if the btree is an index */ - i64 nKey, /* Integer key for tables. Size of pKey for indices */ - int bias, /* Bias search to the high end */ - int *pRes /* Write search results here */ -){ - int rc; /* Status code */ - UnpackedRecord *pIdxKey; /* Unpacked index key */ - char aSpace[150]; /* Temp space for pIdxKey - to avoid a malloc */ - - - if( pKey ){ - assert( nKey==(i64)(int)nKey ); - pIdxKey = sqlite3VdbeRecordUnpack(pCur->pKeyInfo, (int)nKey, pKey, - aSpace, sizeof(aSpace)); - if( pIdxKey==0 ) return SQLITE_NOMEM; - }else{ - pIdxKey = 0; - } - rc = sqlite3BtreeMovetoUnpacked(pCur, pIdxKey, nKey, bias, pRes); - if( pKey ){ - sqlite3VdbeDeleteUnpackedRecord(pIdxKey); - } - return rc; -} - /* ** Return TRUE if the cursor is not pointing at an entry of the table. @@ -41105,12 +42032,12 @@ *pRes = 1; return SQLITE_OK; } - if( pCur->skip>0 ){ - pCur->skip = 0; + if( pCur->skipNext>0 ){ + pCur->skipNext = 0; *pRes = 0; return SQLITE_OK; } - pCur->skip = 0; + pCur->skipNext = 0; pPage = pCur->apPage[pCur->iPage]; idx = ++pCur->aiIdx[pCur->iPage]; @@ -41133,7 +42060,7 @@ pCur->eState = CURSOR_INVALID; return SQLITE_OK; } - sqlite3BtreeMoveToParent(pCur); + moveToParent(pCur); pPage = pCur->apPage[pCur->iPage]; }while( pCur->aiIdx[pCur->iPage]>=pPage->nCell ); *pRes = 0; @@ -41173,12 +42100,12 @@ *pRes = 1; return SQLITE_OK; } - if( pCur->skip<0 ){ - pCur->skip = 0; + if( pCur->skipNext<0 ){ + pCur->skipNext = 0; *pRes = 0; return SQLITE_OK; } - pCur->skip = 0; + pCur->skipNext = 0; pPage = pCur->apPage[pCur->iPage]; assert( pPage->isInit ); @@ -41196,7 +42123,7 @@ *pRes = 1; return SQLITE_OK; } - sqlite3BtreeMoveToParent(pCur); + moveToParent(pCur); } pCur->info.nSize = 0; pCur->validNKey = 0; @@ -41253,7 +42180,8 @@ pPage1 = pBt->pPage1; mxPage = pagerPagecount(pBt); n = get4byte(&pPage1->aData[36]); - if( n>mxPage ){ + testcase( n==mxPage-1 ); + if( n>=mxPage ){ return SQLITE_CORRUPT_BKPT; } if( n>0 ){ @@ -41297,10 +42225,11 @@ }else{ iTrunk = get4byte(&pPage1->aData[32]); } + testcase( iTrunk==mxPage ); if( iTrunk>mxPage ){ rc = SQLITE_CORRUPT_BKPT; }else{ - rc = sqlite3BtreeGetPage(pBt, iTrunk, &pTrunk, 0); + rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); } if( rc ){ pTrunk = 0; @@ -41355,7 +42284,8 @@ rc = SQLITE_CORRUPT_BKPT; goto end_allocate_page; } - rc = sqlite3BtreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0); + testcase( iNewTrunk==mxPage ); + rc = btreeGetPage(pBt, iNewTrunk, &pNewTrunk, 0); if( rc!=SQLITE_OK ){ goto end_allocate_page; } @@ -41410,20 +42340,15 @@ } iPage = get4byte(&aData[8+closest*4]); + testcase( iPage==mxPage ); if( iPage>mxPage ){ rc = SQLITE_CORRUPT_BKPT; goto end_allocate_page; } + testcase( iPage==mxPage ); if( !searchList || iPage==nearby ){ int noContent; - Pgno nPage; *pPgno = iPage; - nPage = pagerPagecount(pBt); - if( iPage>nPage ){ - /* Free page off the end of the file */ - rc = SQLITE_CORRUPT_BKPT; - goto end_allocate_page; - } TRACE(("ALLOCATE: %d was leaf %d of %d on trunk %d" ": %d more free pages\n", *pPgno, closest+1, k, pTrunk->pgno, n-1)); @@ -41433,7 +42358,7 @@ put4byte(&aData[4], k-1); assert( sqlite3PagerIswriteable(pTrunk->pDbPage) ); noContent = !btreeGetHasContent(pBt, *pPgno); - rc = sqlite3BtreeGetPage(pBt, *pPgno, ppPage, noContent); + rc = btreeGetPage(pBt, *pPgno, ppPage, noContent); if( rc==SQLITE_OK ){ rc = sqlite3PagerWrite((*ppPage)->pDbPage); if( rc!=SQLITE_OK ){ @@ -41465,7 +42390,7 @@ MemPage *pPg = 0; TRACE(("ALLOCATE: %d from end of file (pointer-map page)\n", *pPgno)); assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); - rc = sqlite3BtreeGetPage(pBt, *pPgno, &pPg, 0); + rc = btreeGetPage(pBt, *pPgno, &pPg, 0); if( rc==SQLITE_OK ){ rc = sqlite3PagerWrite(pPg->pDbPage); releasePage(pPg); @@ -41477,7 +42402,7 @@ #endif assert( *pPgno!=PENDING_BYTE_PAGE(pBt) ); - rc = sqlite3BtreeGetPage(pBt, *pPgno, ppPage, 0); + rc = btreeGetPage(pBt, *pPgno, ppPage, 0); if( rc ) return rc; rc = sqlite3PagerWrite((*ppPage)->pDbPage); if( rc!=SQLITE_OK ){ @@ -41544,7 +42469,7 @@ /* If the SQLITE_SECURE_DELETE compile-time option is enabled, then ** always fully overwrite deleted information with zeros. */ - if( (!pPage && (rc = sqlite3BtreeGetPage(pBt, iPage, &pPage, 0))) + if( (!pPage && (rc = btreeGetPage(pBt, iPage, &pPage, 0))) || (rc = sqlite3PagerWrite(pPage->pDbPage)) ){ goto freepage_out; @@ -41556,7 +42481,7 @@ ** to indicate that the page is free. */ if( ISAUTOVACUUM ){ - rc = ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0); + ptrmapPut(pBt, iPage, PTRMAP_FREEPAGE, 0, &rc); if( rc ) goto freepage_out; } @@ -41568,20 +42493,21 @@ ** is possible to add the page as a new free-list leaf. */ if( nFree!=0 ){ - int nLeaf; /* Initial number of leaf cells on trunk page */ + u32 nLeaf; /* Initial number of leaf cells on trunk page */ iTrunk = get4byte(&pPage1->aData[32]); - rc = sqlite3BtreeGetPage(pBt, iTrunk, &pTrunk, 0); + rc = btreeGetPage(pBt, iTrunk, &pTrunk, 0); if( rc!=SQLITE_OK ){ goto freepage_out; } nLeaf = get4byte(&pTrunk->aData[4]); - if( nLeaf<0 ){ + assert( pBt->usableSize>32 ); + if( nLeaf > (u32)pBt->usableSize/4 - 2 ){ rc = SQLITE_CORRUPT_BKPT; goto freepage_out; } - if( nLeafusableSize/4 - 8 ){ + if( nLeaf < (u32)pBt->usableSize/4 - 8 ){ /* In this case there is room on the trunk page to insert the page ** being freed as a new leaf. ** @@ -41591,7 +42517,7 @@ ** 3.6.0, databases with freelist trunk pages holding more than ** usableSize/4 - 8 entries will be reported as corrupt. In order ** to maintain backwards compatibility with older versions of SQLite, - ** we will contain to restrict the number of entries to usableSize/4 - 8 + ** we will continue to restrict the number of entries to usableSize/4 - 8 ** for now. At some point in the future (once everyone has upgraded ** to 3.6.0 or later) we should consider fixing the conditional above ** to read "usableSize/4-2" instead of "usableSize/4-8". @@ -41618,9 +42544,11 @@ ** first trunk in the free-list is full. Either way, the page being freed ** will become the new first trunk page in the free-list. */ - if( ((!pPage) && (0 != (rc = sqlite3BtreeGetPage(pBt, iPage, &pPage, 0)))) - || (0 != (rc = sqlite3PagerWrite(pPage->pDbPage))) - ){ + if( pPage==0 && SQLITE_OK!=(rc = btreeGetPage(pBt, iPage, &pPage, 0)) ){ + goto freepage_out; + } + rc = sqlite3PagerWrite(pPage->pDbPage); + if( rc!=SQLITE_OK ){ goto freepage_out; } put4byte(pPage->aData, iTrunk); @@ -41636,8 +42564,10 @@ releasePage(pTrunk); return rc; } -static int freePage(MemPage *pPage){ - return freePage2(pPage->pBt, pPage, pPage->pgno); +static void freePage(MemPage *pPage, int *pRC){ + if( (*pRC)==SQLITE_OK ){ + *pRC = freePage2(pPage->pBt, pPage, pPage->pgno); + } } /* @@ -41652,7 +42582,7 @@ u16 ovflPageSize; assert( sqlite3_mutex_held(pPage->pBt->mutex) ); - sqlite3BtreeParseCellPtr(pPage, pCell, &info); + btreeParseCellPtr(pPage, pCell, &info); if( info.iOverflow==0 ){ return SQLITE_OK; /* No overflow pages. Return without doing anything */ } @@ -41735,7 +42665,7 @@ nData = nZero = 0; } nHeader += putVarint(&pCell[nHeader], *(u64*)&nKey); - sqlite3BtreeParseCellPtr(pPage, pCell, &info); + btreeParseCellPtr(pPage, pCell, &info); assert( info.nHeader==nHeader ); assert( info.nKey==nKey ); assert( info.nData==(u32)(nData+nZero) ); @@ -41747,8 +42677,8 @@ nSrc = nData; nData = 0; }else{ - if( nKey>0x7fffffff || pKey==0 ){ - return SQLITE_CORRUPT; + if( NEVER(nKey>0x7fffffff || pKey==0) ){ + return SQLITE_CORRUPT_BKPT; } nPayload += (int)nKey; pSrc = pKey; @@ -41785,7 +42715,7 @@ */ if( pBt->autoVacuum && rc==SQLITE_OK ){ u8 eType = (pgnoPtrmap?PTRMAP_OVERFLOW2:PTRMAP_OVERFLOW1); - rc = ptrmapPut(pBt, pgnoOvfl, eType, pgnoPtrmap); + ptrmapPut(pBt, pgnoOvfl, eType, pgnoPtrmap, &rc); if( rc ){ releasePage(pOvfl); } @@ -41854,12 +42784,15 @@ ** ** "sz" must be the number of bytes in the cell. */ -static int dropCell(MemPage *pPage, int idx, int sz){ +static void dropCell(MemPage *pPage, int idx, int sz, int *pRC){ int i; /* Loop counter */ int pc; /* Offset to cell content of cell being deleted */ u8 *data; /* pPage->aData */ u8 *ptr; /* Used to move bytes around within data[] */ int rc; /* The return code */ + int hdr; /* Beginning of the header. 0 most pages. 100 page 1 */ + + if( *pRC ) return; assert( idx>=0 && idxnCell ); assert( sz==cellSize(pPage, idx) ); @@ -41868,22 +42801,25 @@ data = pPage->aData; ptr = &data[pPage->cellOffset + 2*idx]; pc = get2byte(ptr); - if( (pchdrOffset+6+(pPage->leaf?0:4)) - || (pc+sz>pPage->pBt->usableSize) ){ - return SQLITE_CORRUPT_BKPT; + hdr = pPage->hdrOffset; + testcase( pc==get2byte(&data[hdr+5]) ); + testcase( pc+sz==pPage->pBt->usableSize ); + if( pc < get2byte(&data[hdr+5]) || pc+sz > pPage->pBt->usableSize ){ + *pRC = SQLITE_CORRUPT_BKPT; + return; } rc = freeSpace(pPage, pc, sz); - if( rc!=SQLITE_OK ){ - return rc; + if( rc ){ + *pRC = rc; + return; } for(i=idx+1; inCell; i++, ptr+=2){ ptr[0] = ptr[2]; ptr[1] = ptr[3]; } pPage->nCell--; - put2byte(&data[pPage->hdrOffset+3], pPage->nCell); + put2byte(&data[hdr+3], pPage->nCell); pPage->nFree += 2; - return SQLITE_OK; } /* @@ -41903,31 +42839,37 @@ ** nSkip is non-zero, then pCell may not point to an invalid memory location ** (but pCell+nSkip is always valid). */ -static int insertCell( +static void insertCell( MemPage *pPage, /* Page into which we are copying */ int i, /* New cell becomes the i-th cell of the page */ u8 *pCell, /* Content of the new cell */ int sz, /* Bytes of content in pCell */ u8 *pTemp, /* Temp storage space for pCell, if needed */ - Pgno iChild /* If non-zero, replace first 4 bytes with this value */ + Pgno iChild, /* If non-zero, replace first 4 bytes with this value */ + int *pRC /* Read and write return code from here */ ){ int idx; /* Where to write new cell content in data[] */ int j; /* Loop counter */ - int top; /* First byte of content for any cell in data[] */ int end; /* First byte past the last cell pointer in data[] */ int ins; /* Index in data[] where new cell pointer is inserted */ - int hdr; /* Offset into data[] of the page header */ int cellOffset; /* Address of first cell pointer in data[] */ u8 *data; /* The content of the whole page */ u8 *ptr; /* Used for moving information around in data[] */ int nSkip = (iChild ? 4 : 0); + if( *pRC ) return; + assert( i>=0 && i<=pPage->nCell+pPage->nOverflow ); assert( pPage->nCell<=MX_CELL(pPage->pBt) && MX_CELL(pPage->pBt)<=5460 ); assert( pPage->nOverflow<=ArraySize(pPage->aOvfl) ); - assert( sz==cellSizePtr(pPage, pCell) ); assert( sqlite3_mutex_held(pPage->pBt->mutex) ); + /* The cell should normally be sized correctly. However, when moving a + ** malformed cell from a leaf page to an interior page, if the cell size + ** wanted to be less than 4 but got rounded up to 4 on the leaf, then size + ** might be less than 8 (leaf-size + pointer) on the interior node. Hence + ** the term after the || in the following assert(). */ + assert( sz==cellSizePtr(pPage, pCell) || (sz==8 && iChild>0) ); if( pPage->nOverflow || sz+2>pPage->nFree ){ if( pTemp ){ memcpy(pTemp+nSkip, pCell+nSkip, sz-nSkip); @@ -41943,52 +42885,41 @@ }else{ int rc = sqlite3PagerWrite(pPage->pDbPage); if( rc!=SQLITE_OK ){ - return rc; + *pRC = rc; + return; } assert( sqlite3PagerIswriteable(pPage->pDbPage) ); data = pPage->aData; - hdr = pPage->hdrOffset; - top = get2byte(&data[hdr+5]); cellOffset = pPage->cellOffset; - end = cellOffset + 2*pPage->nCell + 2; + end = cellOffset + 2*pPage->nCell; ins = cellOffset + 2*i; - if( end > top - sz ){ - rc = defragmentPage(pPage); - if( rc!=SQLITE_OK ){ - return rc; - } - top = get2byte(&data[hdr+5]); - assert( end + sz <= top ); - } - idx = allocateSpace(pPage, sz); - assert( idx>0 ); - assert( end <= get2byte(&data[hdr+5]) ); - if (idx+sz > pPage->pBt->usableSize) { - return SQLITE_CORRUPT_BKPT; - } + rc = allocateSpace(pPage, sz, &idx); + if( rc ){ *pRC = rc; return; } + /* The allocateSpace() routine guarantees the following two properties + ** if it returns success */ + assert( idx >= end+2 ); + assert( idx+sz <= pPage->pBt->usableSize ); pPage->nCell++; - pPage->nFree = pPage->nFree - (u16)(2 + sz); + pPage->nFree -= (u16)(2 + sz); memcpy(&data[idx+nSkip], pCell+nSkip, sz-nSkip); if( iChild ){ put4byte(&data[idx], iChild); } - for(j=end-2, ptr=&data[j]; j>ins; j-=2, ptr-=2){ + for(j=end, ptr=&data[j]; j>ins; j-=2, ptr-=2){ ptr[0] = ptr[-2]; ptr[1] = ptr[-1]; } put2byte(&data[ins], idx); - put2byte(&data[hdr+3], pPage->nCell); + put2byte(&data[pPage->hdrOffset+3], pPage->nCell); #ifndef SQLITE_OMIT_AUTOVACUUM if( pPage->pBt->autoVacuum ){ /* The cell may contain a pointer to an overflow page. If so, write ** the entry for the overflow page into the pointer map. */ - return ptrmapPutOvflPtr(pPage, pCell); + ptrmapPutOvflPtr(pPage, pCell, pRC); } #endif } - - return SQLITE_OK; } /* @@ -42054,7 +42985,7 @@ ** tree, in other words, when the new entry will become the largest ** entry in the tree. ** -** Instead of trying balance the 3 right-most leaf pages, just add +** Instead of trying to balance the 3 right-most leaf pages, just add ** a new page to the right-hand side and put the one new entry in ** that page. This leaves the right side of the tree somewhat ** unbalanced. But odds are that we will be inserting new entries @@ -42111,9 +43042,9 @@ ** rollback, undoing any changes made to the parent page. */ if( ISAUTOVACUUM ){ - rc = ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno); - if( szCell>pNew->minLocal && rc==SQLITE_OK ){ - rc = ptrmapPutOvflPtr(pNew, pCell); + ptrmapPut(pBt, pgnoNew, PTRMAP_BTREE, pParent->pgno, &rc); + if( szCell>pNew->minLocal ){ + ptrmapPutOvflPtr(pNew, pCell, &rc); } } @@ -42137,7 +43068,8 @@ while( ((*(pOut++) = *(pCell++))&0x80) && pCellnCell,pSpace,(int)(pOut-pSpace),0,pPage->pgno); + insertCell(pParent, pParent->nCell, pSpace, (int)(pOut-pSpace), + 0, pPage->pgno, &rc); /* Set the right-child pointer of pParent to point to the new page. */ put4byte(&pParent->aData[pParent->hdrOffset+8], pgnoNew); @@ -42170,7 +43102,7 @@ u8 *z; z = findCell(pPage, j); - sqlite3BtreeParseCellPtr(pPage, z, &info); + btreeParseCellPtr(pPage, z, &info); if( info.iOverflow ){ Pgno ovfl = get4byte(&z[info.iOverflow]); ptrmapGet(pBt, ovfl, &e, &n); @@ -42203,43 +43135,51 @@ ** If pFrom is currently carrying any overflow cells (entries in the ** MemPage.aOvfl[] array), they are not copied to pTo. ** -** Before returning, page pTo is reinitialized using sqlite3BtreeInitPage(). +** Before returning, page pTo is reinitialized using btreeInitPage(). ** ** The performance of this function is not critical. It is only used by ** the balance_shallower() and balance_deeper() procedures, neither of ** which are called often under normal circumstances. */ -static int copyNodeContent(MemPage *pFrom, MemPage *pTo){ - BtShared * const pBt = pFrom->pBt; - u8 * const aFrom = pFrom->aData; - u8 * const aTo = pTo->aData; - int const iFromHdr = pFrom->hdrOffset; - int const iToHdr = ((pTo->pgno==1) ? 100 : 0); - int rc = SQLITE_OK; - int iData; - - assert( pFrom->isInit ); - assert( pFrom->nFree>=iToHdr ); - assert( get2byte(&aFrom[iFromHdr+5])<=pBt->usableSize ); - - /* Copy the b-tree node content from page pFrom to page pTo. */ - iData = get2byte(&aFrom[iFromHdr+5]); - memcpy(&aTo[iData], &aFrom[iData], pBt->usableSize-iData); - memcpy(&aTo[iToHdr], &aFrom[iFromHdr], pFrom->cellOffset + 2*pFrom->nCell); - - /* Reinitialize page pTo so that the contents of the MemPage structure - ** match the new data. The initialization of pTo "cannot" fail, as the - ** data copied from pFrom is known to be valid. */ - pTo->isInit = 0; - TESTONLY(rc = ) sqlite3BtreeInitPage(pTo); - assert( rc==SQLITE_OK ); - - /* If this is an auto-vacuum database, update the pointer-map entries - ** for any b-tree or overflow pages that pTo now contains the pointers to. */ - if( ISAUTOVACUUM ){ - rc = setChildPtrmaps(pTo); +static void copyNodeContent(MemPage *pFrom, MemPage *pTo, int *pRC){ + if( (*pRC)==SQLITE_OK ){ + BtShared * const pBt = pFrom->pBt; + u8 * const aFrom = pFrom->aData; + u8 * const aTo = pTo->aData; + int const iFromHdr = pFrom->hdrOffset; + int const iToHdr = ((pTo->pgno==1) ? 100 : 0); + int rc; + int iData; + + + assert( pFrom->isInit ); + assert( pFrom->nFree>=iToHdr ); + assert( get2byte(&aFrom[iFromHdr+5])<=pBt->usableSize ); + + /* Copy the b-tree node content from page pFrom to page pTo. */ + iData = get2byte(&aFrom[iFromHdr+5]); + memcpy(&aTo[iData], &aFrom[iData], pBt->usableSize-iData); + memcpy(&aTo[iToHdr], &aFrom[iFromHdr], pFrom->cellOffset + 2*pFrom->nCell); + + /* Reinitialize page pTo so that the contents of the MemPage structure + ** match the new data. The initialization of pTo can actually fail under + ** fairly obscure circumstances, even though it is a copy of initialized + ** page pFrom. + */ + pTo->isInit = 0; + rc = btreeInitPage(pTo); + if( rc!=SQLITE_OK ){ + *pRC = rc; + return; + } + + /* If this is an auto-vacuum database, update the pointer-map entries + ** for any b-tree or overflow pages that pTo now contains the pointers to. + */ + if( ISAUTOVACUUM ){ + *pRC = setChildPtrmaps(pTo); + } } - return rc; } /* @@ -42271,9 +43211,9 @@ ** be rolled back. ** ** The third argument to this function, aOvflSpace, is a pointer to a -** buffer page-size bytes in size. If, in inserting cells into the parent -** page (pParent), the parent page becomes overfull, this buffer is -** used to store the parents overflow cells. Because this function inserts +** buffer big enough to hold one page. If while inserting cells into the parent +** page (pParent) the parent page becomes overfull, this buffer is +** used to store the parent's overflow cells. Because this function inserts ** a maximum of four divider cells into the parent page, and the maximum ** size of a cell stored within an internal node is always less than 1/4 ** of the page-size, the aOvflSpace[] buffer is guaranteed to be large @@ -42327,7 +43267,8 @@ /* At this point pParent may have at most one overflow cell. And if ** this overflow cell is present, it must be the cell with ** index iParentIdx. This scenario comes about when this function - ** is called (indirectly) from sqlite3BtreeDelete(). */ + ** is called (indirectly) from sqlite3BtreeDelete(). + */ assert( pParent->nOverflow==0 || pParent->nOverflow==1 ); assert( pParent->nOverflow==0 || pParent->aOvfl[0].idx==iParentIdx ); @@ -42343,8 +43284,9 @@ ** ** This loop also drops the divider cells from the parent page. This ** way, the remainder of the function does not have to deal with any - ** overflow cells in the parent page, as if one existed it has already - ** been removed. */ + ** overflow cells in the parent page, since if any existed they will + ** have already been removed. + */ i = pParent->nOverflow + pParent->nCell; if( i<2 ){ nxDiv = 0; @@ -42369,13 +43311,13 @@ while( 1 ){ rc = getAndInitPage(pBt, pgno, &apOld[i]); if( rc ){ - memset(apOld, 0, i*sizeof(MemPage*)); + memset(apOld, 0, (i+1)*sizeof(MemPage*)); goto balance_cleanup; } nMaxCells += 1+apOld[i]->nCell+apOld[i]->nOverflow; if( (i--)==0 ) break; - if( pParent->nOverflow && i+nxDiv==pParent->aOvfl[0].idx ){ + if( i+nxDiv==pParent->aOvfl[0].idx && pParent->nOverflow ){ apDiv[i] = pParent->aOvfl[0].pCell; pgno = get4byte(apDiv[i]); szNew[i] = cellSizePtr(pParent, apDiv[i]); @@ -42401,7 +43343,7 @@ memcpy(&aOvflSpace[apDiv[i]-pParent->aData], apDiv[i], szNew[i]); apDiv[i] = &aOvflSpace[apDiv[i]-pParent->aData]; #endif - dropCell(pParent, i+nxDiv-pParent->nOverflow, szNew[i]); + dropCell(pParent, i+nxDiv-pParent->nOverflow, szNew[i], &rc); } } @@ -42595,7 +43537,7 @@ /* Set the pointer-map entry for the new sibling page. */ if( ISAUTOVACUUM ){ - rc = ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno); + ptrmapPut(pBt, pNew->pgno, PTRMAP_BTREE, pParent->pgno, &rc); if( rc!=SQLITE_OK ){ goto balance_cleanup; } @@ -42606,7 +43548,7 @@ /* Free any old pages that were not reused as new pages. */ while( ipageSize/4 ); assert( iOvflSpace<=pBt->pageSize ); - rc = insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew->pgno); + insertCell(pParent, nxDiv, pCell, sz, pTemp, pNew->pgno, &rc); if( rc!=SQLITE_OK ) goto balance_cleanup; assert( sqlite3PagerIswriteable(pParent->pDbPage) ); @@ -42754,9 +43696,8 @@ assert( apNew[0]->nFree == (get2byte(&apNew[0]->aData[5])-apNew[0]->cellOffset-apNew[0]->nCell*2) ); - if( SQLITE_OK==(rc = copyNodeContent(apNew[0], pParent)) ){ - rc = freePage(apNew[0]); - } + copyNodeContent(apNew[0], pParent, &rc); + freePage(apNew[0], &rc); }else if( ISAUTOVACUUM ){ /* Fix the pointer-map entries for all the cells that were shifted around. ** There are several different types of pointer-map entries that need to @@ -42796,7 +43737,7 @@ int iOverflow = (nOverflow ? pOld->aOvfl[0].idx : -1); j = 0; /* Current 'old' sibling page */ k = 0; /* Current 'new' sibling page */ - for(i=0; ipgno!=pNew->pgno ){ if( !leafCorrection ){ - rc = ptrmapPut(pBt, get4byte(apCell[i]), PTRMAP_BTREE, pNew->pgno); + ptrmapPut(pBt, get4byte(apCell[i]), PTRMAP_BTREE, pNew->pgno, &rc); } - if( szCell[i]>pNew->minLocal && rc==SQLITE_OK ){ - rc = ptrmapPutOvflPtr(pNew, apCell[i]); + if( szCell[i]>pNew->minLocal ){ + ptrmapPutOvflPtr(pNew, apCell[i], &rc); } } } if( !leafCorrection ){ - for(i=0; rc==SQLITE_OK && iaData[8]), PTRMAP_BTREE, apNew[i]->pgno); + for(i=0; iaData[8]); + ptrmapPut(pBt, key, PTRMAP_BTREE, apNew[i]->pgno, &rc); } } @@ -42905,7 +43845,7 @@ static int balance_deeper(MemPage *pRoot, MemPage **ppChild){ int rc; /* Return value from subprocedures */ MemPage *pChild = 0; /* Pointer to a new child page */ - Pgno pgnoChild; /* Page number of the new child page */ + Pgno pgnoChild = 0; /* Page number of the new child page */ BtShared *pBt = pRoot->pBt; /* The BTree */ assert( pRoot->nOverflow>0 ); @@ -42915,12 +43855,15 @@ ** page that will become the new right-child of pPage. Copy the contents ** of the node stored on pRoot into the new child page. */ - if( SQLITE_OK!=(rc = sqlite3PagerWrite(pRoot->pDbPage)) - || SQLITE_OK!=(rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0)) - || SQLITE_OK!=(rc = copyNodeContent(pRoot, pChild)) - || (ISAUTOVACUUM && - SQLITE_OK!=(rc = ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot->pgno))) - ){ + rc = sqlite3PagerWrite(pRoot->pDbPage); + if( rc==SQLITE_OK ){ + rc = allocateBtreePage(pBt,&pChild,&pgnoChild,pRoot->pgno,0); + copyNodeContent(pRoot, pChild, &rc); + if( ISAUTOVACUUM ){ + ptrmapPut(pBt, pgnoChild, PTRMAP_BTREE, pRoot->pgno, &rc); + } + } + if( rc ){ *ppChild = 0; releasePage(pChild); return rc; @@ -43065,75 +44008,6 @@ return rc; } -/* -** This routine checks all cursors that point to table pgnoRoot. -** If any of those cursors were opened with wrFlag==0 in a different -** database connection (a database connection that shares the pager -** cache with the current connection) and that other connection -** is not in the ReadUncommmitted state, then this routine returns -** SQLITE_LOCKED. -** -** As well as cursors with wrFlag==0, cursors with -** isIncrblobHandle==1 are also considered 'read' cursors because -** incremental blob cursors are used for both reading and writing. -** -** When pgnoRoot is the root page of an intkey table, this function is also -** responsible for invalidating incremental blob cursors when the table row -** on which they are opened is deleted or modified. Cursors are invalidated -** according to the following rules: -** -** 1) When BtreeClearTable() is called to completely delete the contents -** of a B-Tree table, pExclude is set to zero and parameter iRow is -** set to non-zero. In this case all incremental blob cursors open -** on the table rooted at pgnoRoot are invalidated. -** -** 2) When BtreeInsert(), BtreeDelete() or BtreePutData() is called to -** modify a table row via an SQL statement, pExclude is set to the -** write cursor used to do the modification and parameter iRow is set -** to the integer row id of the B-Tree entry being modified. Unless -** pExclude is itself an incremental blob cursor, then all incremental -** blob cursors open on row iRow of the B-Tree are invalidated. -** -** 3) If both pExclude and iRow are set to zero, no incremental blob -** cursors are invalidated. -*/ -static int checkForReadConflicts( - Btree *pBtree, /* The database file to check */ - Pgno pgnoRoot, /* Look for read cursors on this btree */ - BtCursor *pExclude, /* Ignore this cursor */ - i64 iRow /* The rowid that might be changing */ -){ - BtCursor *p; - BtShared *pBt = pBtree->pBt; - sqlite3 *db = pBtree->db; - assert( sqlite3BtreeHoldsMutex(pBtree) ); - for(p=pBt->pCursor; p; p=p->pNext){ - if( p==pExclude ) continue; - if( p->pgnoRoot!=pgnoRoot ) continue; -#ifndef SQLITE_OMIT_INCRBLOB - if( p->isIncrblobHandle && ( - (!pExclude && iRow) - || (pExclude && !pExclude->isIncrblobHandle && p->info.nKey==iRow) - )){ - p->eState = CURSOR_INVALID; - } -#endif - if( p->eState!=CURSOR_VALID ) continue; - if( p->wrFlag==0 -#ifndef SQLITE_OMIT_INCRBLOB - || p->isIncrblobHandle -#endif - ){ - sqlite3 *dbOther = p->pBtree->db; - assert(dbOther); - if( dbOther!=db && (dbOther->flags & SQLITE_ReadUncommitted)==0 ){ - sqlite3ConnectionBlocked(db, dbOther); - return SQLITE_LOCKED_SHAREDCACHE; - } - } - } - return SQLITE_OK; -} /* ** Insert a new record into the BTree. The key is given by (pKey,nKey) @@ -43145,14 +44019,16 @@ ** ignored. For a ZERODATA table, the pData and nData are both ignored. ** ** If the seekResult parameter is non-zero, then a successful call to -** sqlite3BtreeMoveto() to seek cursor pCur to (pKey, nKey) has already +** MovetoUnpacked() to seek cursor pCur to (pKey, nKey) has already ** been performed. seekResult is the search result returned (a negative ** number if pCur points at an entry that is smaller than (pKey, nKey), or ** a positive value if pCur points at an etry that is larger than ** (pKey, nKey)). ** -** If the seekResult parameter is 0, then cursor pCur may point to any -** entry or to no entry at all. In this case this function has to seek +** If the seekResult parameter is non-zero, then the caller guarantees that +** cursor pCur is pointing at the existing copy of a row that is to be +** overwritten. If the seekResult parameter is 0, then cursor pCur may +** point to any entry or to no entry at all and so this function has to seek ** the cursor before the new key can be inserted. */ SQLITE_PRIVATE int sqlite3BtreeInsert( @@ -43161,11 +44037,11 @@ const void *pData, int nData, /* The data of the new record */ int nZero, /* Number of extra 0 bytes to append to data */ int appendBias, /* True if this is likely an append */ - int seekResult /* Result of prior sqlite3BtreeMoveto() call */ + int seekResult /* Result of prior MovetoUnpacked() call */ ){ int rc; - int loc = seekResult; - int szNew; + int loc = seekResult; /* -1: before desired location +1: after */ + int szNew = 0; int idx; MemPage *pPage; Btree *p = pCur->pBtree; @@ -43173,42 +44049,52 @@ unsigned char *oldCell; unsigned char *newCell = 0; + if( pCur->eState==CURSOR_FAULT ){ + assert( pCur->skipNext!=SQLITE_OK ); + return pCur->skipNext; + } + assert( cursorHoldsMutex(pCur) ); - assert( pBt->inTransaction==TRANS_WRITE ); - assert( !pBt->readOnly ); - assert( pCur->wrFlag ); - rc = checkForReadConflicts(pCur->pBtree, pCur->pgnoRoot, pCur, nKey); - if( rc ){ - /* The table pCur points to has a read lock */ - assert( rc==SQLITE_LOCKED_SHAREDCACHE ); - return rc; - } - if( pCur->eState==CURSOR_FAULT ){ - return pCur->skip; + assert( pCur->wrFlag && pBt->inTransaction==TRANS_WRITE && !pBt->readOnly ); + assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); + + /* Assert that the caller has been consistent. If this cursor was opened + ** expecting an index b-tree, then the caller should be inserting blob + ** keys with no associated data. If the cursor was opened expecting an + ** intkey table, the caller should be inserting integer keys with a + ** blob of associated data. */ + assert( (pKey==0)==(pCur->pKeyInfo==0) ); + + /* If this is an insert into a table b-tree, invalidate any incrblob + ** cursors open on the row being replaced (assuming this is a replace + ** operation - if it is not, the following is a no-op). */ + if( pCur->pKeyInfo==0 ){ + invalidateIncrblobCursors(p, nKey, 0); } /* Save the positions of any other cursors open on this table. ** - ** In some cases, the call to sqlite3BtreeMoveto() below is a no-op. For + ** In some cases, the call to btreeMoveto() below is a no-op. For ** example, when inserting data into a table with auto-generated integer ** keys, the VDBE layer invokes sqlite3BtreeLast() to figure out the ** integer key to use. It then calls this function to actually insert the - ** data into the intkey B-Tree. In this case sqlite3BtreeMoveto() recognizes + ** data into the intkey B-Tree. In this case btreeMoveto() recognizes ** that the cursor is already where it needs to be and returns without ** doing any work. To avoid thwarting these optimizations, it is important ** not to clear the cursor here. */ - if( - SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) || (!loc && - SQLITE_OK!=(rc = sqlite3BtreeMoveto(pCur, pKey, nKey, appendBias, &loc)) - )){ - return rc; + rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); + if( rc ) return rc; + if( !loc ){ + rc = btreeMoveto(pCur, pKey, nKey, appendBias, &loc); + if( rc ) return rc; } assert( pCur->eState==CURSOR_VALID || (pCur->eState==CURSOR_INVALID && loc) ); pPage = pCur->apPage[pCur->iPage]; assert( pPage->intKey || nKey>=0 ); assert( pPage->leaf || !pPage->intKey ); + TRACE(("INSERT: table=%d nkey=%lld ndata=%d page=%d %s\n", pCur->pgnoRoot, nKey, nData, pPage->pgno, loc==0 ? "overwrite" : "new entry")); @@ -43234,18 +44120,15 @@ } szOld = cellSizePtr(pPage, oldCell); rc = clearCell(pPage, oldCell); + dropCell(pPage, idx, szOld, &rc); if( rc ) goto end_insert; - rc = dropCell(pPage, idx, szOld); - if( rc!=SQLITE_OK ) { - goto end_insert; - } }else if( loc<0 && pPage->nCell>0 ){ assert( pPage->leaf ); idx = ++pCur->aiIdx[pCur->iPage]; }else{ assert( pPage->leaf ); } - rc = insertCell(pPage, idx, newCell, szNew, 0, 0); + insertCell(pPage, idx, newCell, szNew, 0, 0, &rc); assert( rc!=SQLITE_OK || pPage->nCell>0 || pPage->nOverflow>0 ); /* If no error has occured and pPage has an overflow cell, call balance() @@ -43303,16 +44186,19 @@ assert( pBt->inTransaction==TRANS_WRITE ); assert( !pBt->readOnly ); assert( pCur->wrFlag ); + assert( hasSharedCacheTableLock(p, pCur->pgnoRoot, pCur->pKeyInfo!=0, 2) ); + assert( !hasReadConflicts(p, pCur->pgnoRoot) ); + if( NEVER(pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell) || NEVER(pCur->eState!=CURSOR_VALID) ){ return SQLITE_ERROR; /* Something has gone awry. */ } - rc = checkForReadConflicts(p, pCur->pgnoRoot, pCur, pCur->info.nKey); - if( rc!=SQLITE_OK ){ - assert( rc==SQLITE_LOCKED_SHAREDCACHE ); - return rc; /* The table pCur points to has a read lock */ + /* If this is a delete operation to remove a row from a table b-tree, + ** invalidate any incrblob cursors open on the row being deleted. */ + if( pCur->pKeyInfo==0 ){ + invalidateIncrblobCursors(p, pCur->info.nKey, 0); } iCellDepth = pCur->iPage; @@ -43329,22 +44215,22 @@ ** balancing the tree following the delete operation easier. */ if( !pPage->leaf ){ int notUsed; - if( SQLITE_OK!=(rc = sqlite3BtreePrevious(pCur, ¬Used)) ){ - return rc; - } + rc = sqlite3BtreePrevious(pCur, ¬Used); + if( rc ) return rc; } /* Save the positions of any other cursors open on this table before ** making any modifications. Make the page containing the entry to be ** deleted writable. Then free any overflow pages associated with the - ** entry and finally remove the cell itself from within the page. */ - if( SQLITE_OK!=(rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur)) - || SQLITE_OK!=(rc = sqlite3PagerWrite(pPage->pDbPage)) - || SQLITE_OK!=(rc = clearCell(pPage, pCell)) - || SQLITE_OK!=(rc = dropCell(pPage, iCellIdx, cellSizePtr(pPage, pCell))) - ){ - return rc; - } + ** entry and finally remove the cell itself from within the page. + */ + rc = saveAllCursors(pBt, pCur->pgnoRoot, pCur); + if( rc ) return rc; + rc = sqlite3PagerWrite(pPage->pDbPage); + if( rc ) return rc; + rc = clearCell(pPage, pCell); + dropCell(pPage, iCellIdx, cellSizePtr(pPage, pCell), &rc); + if( rc ) return rc; /* If the cell deleted was not located on a leaf page, then the cursor ** is currently pointing to the largest entry in the sub-tree headed @@ -43364,12 +44250,10 @@ allocateTempSpace(pBt); pTmp = pBt->pTmpSpace; - if( SQLITE_OK!=(rc = sqlite3PagerWrite(pLeaf->pDbPage)) - || SQLITE_OK!=(rc = insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n)) - || SQLITE_OK!=(rc = dropCell(pLeaf, pLeaf->nCell-1, nCell)) - ){ - return rc; - } + rc = sqlite3PagerWrite(pLeaf->pDbPage); + insertCell(pPage, iCellIdx, pCell-4, nCell+4, pTmp, n, &rc); + dropCell(pLeaf, pLeaf->nCell-1, nCell, &rc); + if( rc ) return rc; } /* Balance the tree. If the entry deleted was located on a leaf page, @@ -43443,10 +44327,7 @@ ** root page of the new table should go. meta[3] is the largest root-page ** created so far, so the new root-page is (meta[3]+1). */ - rc = sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); - if( rc!=SQLITE_OK ){ - return rc; - } + sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &pgnoRoot); pgnoRoot++; /* The new root-page may not be allocated on a pointer-map page, or the @@ -43474,13 +44355,13 @@ ** by extending the file), the current page at position pgnoMove ** is already journaled. */ - u8 eType; - Pgno iPtrPage; + u8 eType = 0; + Pgno iPtrPage = 0; releasePage(pPageMove); /* Move the page currently at pgnoRoot to pgnoMove. */ - rc = sqlite3BtreeGetPage(pBt, pgnoRoot, &pRoot, 0); + rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); if( rc!=SQLITE_OK ){ return rc; } @@ -43501,7 +44382,7 @@ if( rc!=SQLITE_OK ){ return rc; } - rc = sqlite3BtreeGetPage(pBt, pgnoRoot, &pRoot, 0); + rc = btreeGetPage(pBt, pgnoRoot, &pRoot, 0); if( rc!=SQLITE_OK ){ return rc; } @@ -43515,7 +44396,7 @@ } /* Update the pointer-map and meta-data with the new root-page number. */ - rc = ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0); + ptrmapPut(pBt, pgnoRoot, PTRMAP_ROOTPAGE, 0, &rc); if( rc ){ releasePage(pRoot); return rc; @@ -43551,11 +44432,11 @@ */ static int clearDatabasePage( BtShared *pBt, /* The BTree that contains the table */ - Pgno pgno, /* Page number to clear */ - int freePageFlag, /* Deallocate page if true */ - int *pnChange + Pgno pgno, /* Page number to clear */ + int freePageFlag, /* Deallocate page if true */ + int *pnChange /* Add number of Cells freed to this counter */ ){ - MemPage *pPage = 0; + MemPage *pPage; int rc; unsigned char *pCell; int i; @@ -43566,7 +44447,7 @@ } rc = getAndInitPage(pBt, pgno, &pPage); - if( rc ) goto cleardatabasepage_out; + if( rc ) return rc; for(i=0; inCell; i++){ pCell = findCell(pPage, i); if( !pPage->leaf ){ @@ -43584,7 +44465,7 @@ *pnChange += pPage->nCell; } if( freePageFlag ){ - rc = freePage(pPage); + freePage(pPage, &rc); }else if( (rc = sqlite3PagerWrite(pPage->pDbPage))==0 ){ zeroPage(pPage, pPage->aData[0] | PTF_LEAF); } @@ -43612,11 +44493,14 @@ BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); assert( p->inTrans==TRANS_WRITE ); - if( (rc = checkForReadConflicts(p, iTable, 0, 1))!=SQLITE_OK ){ - /* nothing to do */ - }else if( SQLITE_OK!=(rc = saveAllCursors(pBt, iTable, 0)) ){ - /* nothing to do */ - }else{ + + /* Invalidate all incrblob cursors open on table iTable (assuming iTable + ** is the root of a table b-tree - if it is not, the following call is + ** a no-op). */ + invalidateIncrblobCursors(p, 0, 1); + + rc = saveAllCursors(pBt, (Pgno)iTable, 0); + if( SQLITE_OK==rc ){ rc = clearDatabasePage(pBt, (Pgno)iTable, 0, pnChange); } sqlite3BtreeLeave(p); @@ -43656,13 +44540,15 @@ ** need to move another root-page to fill a gap left by the deleted ** root page. If an open cursor was using this page a problem would ** occur. + ** + ** This error is caught long before control reaches this point. */ - if( pBt->pCursor ){ + if( NEVER(pBt->pCursor) ){ sqlite3ConnectionBlocked(p->db, pBt->pCursor->pBtree->db); return SQLITE_LOCKED_SHAREDCACHE; } - rc = sqlite3BtreeGetPage(pBt, (Pgno)iTable, &pPage, 0); + rc = btreeGetPage(pBt, (Pgno)iTable, &pPage, 0); if( rc ) return rc; rc = sqlite3BtreeClearTable(p, iTable, 0); if( rc ){ @@ -43674,22 +44560,18 @@ if( iTable>1 ){ #ifdef SQLITE_OMIT_AUTOVACUUM - rc = freePage(pPage); + freePage(pPage, &rc); releasePage(pPage); #else if( pBt->autoVacuum ){ Pgno maxRootPgno; - rc = sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno); - if( rc!=SQLITE_OK ){ - releasePage(pPage); - return rc; - } + sqlite3BtreeGetMeta(p, BTREE_LARGEST_ROOT_PAGE, &maxRootPgno); if( iTable==maxRootPgno ){ /* If the table being dropped is the table with the largest root-page ** number in the database, put the root page on the free list. */ - rc = freePage(pPage); + freePage(pPage, &rc); releasePage(pPage); if( rc!=SQLITE_OK ){ return rc; @@ -43701,7 +44583,7 @@ */ MemPage *pMove; releasePage(pPage); - rc = sqlite3BtreeGetPage(pBt, maxRootPgno, &pMove, 0); + rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); if( rc!=SQLITE_OK ){ return rc; } @@ -43710,11 +44592,9 @@ if( rc!=SQLITE_OK ){ return rc; } - rc = sqlite3BtreeGetPage(pBt, maxRootPgno, &pMove, 0); - if( rc!=SQLITE_OK ){ - return rc; - } - rc = freePage(pMove); + pMove = 0; + rc = btreeGetPage(pBt, maxRootPgno, &pMove, 0); + freePage(pMove, &rc); releasePage(pMove); if( rc!=SQLITE_OK ){ return rc; @@ -43728,22 +44608,23 @@ ** PENDING_BYTE_PAGE. */ maxRootPgno--; - if( maxRootPgno==PENDING_BYTE_PAGE(pBt) ){ - maxRootPgno--; - } - if( maxRootPgno==PTRMAP_PAGENO(pBt, maxRootPgno) ){ + while( maxRootPgno==PENDING_BYTE_PAGE(pBt) + || PTRMAP_ISPAGE(pBt, maxRootPgno) ){ maxRootPgno--; } assert( maxRootPgno!=PENDING_BYTE_PAGE(pBt) ); rc = sqlite3BtreeUpdateMeta(p, 4, maxRootPgno); }else{ - rc = freePage(pPage); + freePage(pPage, &rc); releasePage(pPage); } #endif }else{ - /* If sqlite3BtreeDropTable was called on page 1. */ + /* If sqlite3BtreeDropTable was called on page 1. + ** This really never should happen except in a corrupt + ** database. + */ zeroPage(pPage, PTF_INTKEY|PTF_LEAF ); releasePage(pPage); } @@ -43759,6 +44640,9 @@ /* +** This function may only be called if the b-tree connection already +** has a read or write transaction open on the database. +** ** Read the meta-information out of a database file. Meta[0] ** is the number of free pages currently in the database. Meta[1] ** through meta[15] are available for use by higher layers. Meta[0] @@ -43768,71 +44652,24 @@ ** layer (and the SetCookie and ReadCookie opcodes) the number of ** free pages is not visible. So Cookie[0] is the same as Meta[1]. */ -SQLITE_PRIVATE int sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ - DbPage *pDbPage = 0; - int rc; - unsigned char *pP1; +SQLITE_PRIVATE void sqlite3BtreeGetMeta(Btree *p, int idx, u32 *pMeta){ BtShared *pBt = p->pBt; sqlite3BtreeEnter(p); - - /* Reading a meta-data value requires a read-lock on page 1 (and hence - ** the sqlite_master table. We grab this lock regardless of whether or - ** not the SQLITE_ReadUncommitted flag is set (the table rooted at page - ** 1 is treated as a special case by querySharedCacheTableLock() - ** and setSharedCacheTableLock()). - */ - rc = querySharedCacheTableLock(p, 1, READ_LOCK); - if( rc!=SQLITE_OK ){ - sqlite3BtreeLeave(p); - return rc; - } - + assert( p->inTrans>TRANS_NONE ); + assert( SQLITE_OK==querySharedCacheTableLock(p, MASTER_ROOT, READ_LOCK) ); + assert( pBt->pPage1 ); assert( idx>=0 && idx<=15 ); - if( pBt->pPage1 ){ - /* The b-tree is already holding a reference to page 1 of the database - ** file. In this case the required meta-data value can be read directly - ** from the page data of this reference. This is slightly faster than - ** requesting a new reference from the pager layer. - */ - pP1 = (unsigned char *)pBt->pPage1->aData; - }else{ - /* The b-tree does not have a reference to page 1 of the database file. - ** Obtain one from the pager layer. - */ - rc = sqlite3PagerGet(pBt->pPager, 1, &pDbPage); - if( rc ){ - sqlite3BtreeLeave(p); - return rc; - } - pP1 = (unsigned char *)sqlite3PagerGetData(pDbPage); - } - *pMeta = get4byte(&pP1[36 + idx*4]); - /* If the b-tree is not holding a reference to page 1, then one was - ** requested from the pager layer in the above block. Release it now. - */ - if( !pBt->pPage1 ){ - sqlite3PagerUnref(pDbPage); - } + *pMeta = get4byte(&pBt->pPage1->aData[36 + idx*4]); - /* If autovacuumed is disabled in this build but we are trying to - ** access an autovacuumed database, then make the database readonly. - */ + /* If auto-vacuum is disabled in this build and this is an auto-vacuum + ** database, mark the database as read-only. */ #ifdef SQLITE_OMIT_AUTOVACUUM if( idx==BTREE_LARGEST_ROOT_PAGE && *pMeta>0 ) pBt->readOnly = 1; #endif - /* If there is currently an open transaction, grab a read-lock - ** on page 1 of the database file. This is done to make sure that - ** no other connection can modify the meta value just read from - ** the database until the transaction is concluded. - */ - if( p->inTrans>0 ){ - rc = setSharedCacheTableLock(p, 1, READ_LOCK); - } sqlite3BtreeLeave(p); - return rc; } /* @@ -43863,23 +44700,6 @@ return rc; } -/* -** Return the flag byte at the beginning of the page that the cursor -** is currently pointing to. -*/ -SQLITE_PRIVATE int sqlite3BtreeFlags(BtCursor *pCur){ - /* TODO: What about CURSOR_REQUIRESEEK state? Probably need to call - ** restoreCursorPosition() here. - */ - MemPage *pPage; - restoreCursorPosition(pCur); - pPage = pCur->apPage[pCur->iPage]; - assert( cursorHoldsMutex(pCur) ); - assert( pPage!=0 ); - assert( pPage->pBt==pCur->pBt ); - return pPage->aData[pPage->hdrOffset]; -} - #ifndef SQLITE_OMIT_BTREECOUNT /* ** The first argument, pCur, is a cursor opened on some b-tree. Count the @@ -43927,7 +44747,7 @@ *pnEntry = nEntry; return SQLITE_OK; } - sqlite3BtreeMoveToParent(pCur); + moveToParent(pCur); }while ( pCur->aiIdx[pCur->iPage]>=pCur->apPage[pCur->iPage]->nCell ); pCur->aiIdx[pCur->iPage]++; @@ -44154,16 +44974,19 @@ usableSize = pBt->usableSize; if( iPage==0 ) return 0; if( checkRef(pCheck, iPage, zParentContext) ) return 0; - if( (rc = sqlite3BtreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){ - if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ) pCheck->mallocFailed = 1; + if( (rc = btreeGetPage(pBt, (Pgno)iPage, &pPage, 0))!=0 ){ checkAppendMsg(pCheck, zContext, "unable to get the page. error code=%d", rc); return 0; } - if( (rc = sqlite3BtreeInitPage(pPage))!=0 ){ + + /* Clear MemPage.isInit to make sure the corruption detection code in + ** btreeInitPage() is executed. */ + pPage->isInit = 0; + if( (rc = btreeInitPage(pPage))!=0 ){ assert( rc==SQLITE_CORRUPT ); /* The only possible error from InitPage */ checkAppendMsg(pCheck, zContext, - "sqlite3BtreeInitPage() returns error code %d", rc); + "btreeInitPage() returns error code %d", rc); releasePage(pPage); return 0; } @@ -44181,7 +45004,7 @@ sqlite3_snprintf(sizeof(zContext), zContext, "On tree page %d cell %d: ", iPage, i); pCell = findCell(pPage,i); - sqlite3BtreeParseCellPtr(pPage, pCell, &info); + btreeParseCellPtr(pPage, pCell, &info); sz = info.nData; if( !pPage->intKey ) sz += (int)info.nKey; assert( sz==info.nPayload ); @@ -44235,11 +45058,7 @@ pCheck->mallocFailed = 1; }else{ u16 contentOffset = get2byte(&data[hdr+5]); - if (contentOffset > usableSize) { - checkAppendMsg(pCheck, 0, - "Corruption detected in header on page %d",iPage,0); - goto check_page_abort; - } + assert( contentOffset<=usableSize ); /* Enforced by btreeInitPage() */ memset(hit+contentOffset, 0, usableSize-contentOffset); memset(hit, 1, contentOffset); nCell = get2byte(&data[hdr+3]); @@ -44248,27 +45067,27 @@ int pc = get2byte(&data[cellStart+i*2]); u16 size = 1024; int j; - if( pc<=usableSize ){ + if( pc<=usableSize-4 ){ size = cellSizePtr(pPage, &data[pc]); } - if( (pc+size-1)>=usableSize || pc<0 ){ + if( (pc+size-1)>=usableSize ){ checkAppendMsg(pCheck, 0, "Corruption detected in cell %d on page %d",i,iPage,0); }else{ for(j=pc+size-1; j>=pc; j--) hit[j]++; } } - for(cnt=0, i=get2byte(&data[hdr+1]); i>0 && i=usableSize || i<0 ){ - checkAppendMsg(pCheck, 0, - "Corruption detected in cell %d on page %d",i,iPage,0); - }else{ - for(j=i+size-1; j>=i; j--) hit[j]++; - } - i = get2byte(&data[i]); + i = get2byte(&data[hdr+1]); + while( i>0 ){ + int size, j; + assert( i<=usableSize-4 ); /* Enforced by btreeInitPage() */ + size = get2byte(&data[i+2]); + assert( i+size<=usableSize ); /* Enforced by btreeInitPage() */ + for(j=i+size-1; j>=i; j--) hit[j]++; + j = get2byte(&data[i]); + assert( j==0 || j>i+size ); /* Enforced by btreeInitPage() */ + assert( j<=usableSize-4 ); /* Enforced by btreeInitPage() */ + i = j; } for(i=cnt=0; iinTrans>TRANS_NONE && pBt->inTransaction>TRANS_NONE ); nRef = sqlite3PagerRefcount(pBt->pPager); - if( lockBtreeWithRetry(p)!=SQLITE_OK ){ - *pnErr = 1; - sqlite3BtreeLeave(p); - return sqlite3DbStrDup(0, "cannot acquire a read lock on the database"); - } sCheck.pBt = pBt; sCheck.pPager = pBt->pPager; sCheck.nPage = pagerPagecount(sCheck.pBt); @@ -44332,13 +45148,11 @@ sCheck.mallocFailed = 0; *pnErr = 0; if( sCheck.nPage==0 ){ - unlockBtreeIfUnused(pBt); sqlite3BtreeLeave(p); return 0; } sCheck.anRef = sqlite3Malloc( (sCheck.nPage+1)*sizeof(sCheck.anRef[0]) ); if( !sCheck.anRef ){ - unlockBtreeIfUnused(pBt); *pnErr = 1; sqlite3BtreeLeave(p); return 0; @@ -44393,7 +45207,6 @@ ** This is an internal consistency check; an integrity check ** of the integrity check. */ - unlockBtreeIfUnused(pBt); if( NEVER(nRef != sqlite3PagerRefcount(pBt->pPager)) ){ checkAppendMsg(&sCheck, 0, "Outstanding page count goes from %d to %d during this analysis", @@ -44518,10 +45331,12 @@ */ SQLITE_PRIVATE int sqlite3BtreeLockTable(Btree *p, int iTab, u8 isWriteLock){ int rc = SQLITE_OK; + assert( p->inTrans!=TRANS_NONE ); if( p->sharable ){ u8 lockType = READ_LOCK + isWriteLock; assert( READ_LOCK+1==WRITE_LOCK ); assert( isWriteLock==0 || isWriteLock==1 ); + sqlite3BtreeEnter(p); rc = querySharedCacheTableLock(p, iTab, lockType); if( rc==SQLITE_OK ){ @@ -44538,43 +45353,43 @@ ** Argument pCsr must be a cursor opened for writing on an ** INTKEY table currently pointing at a valid table entry. ** This function modifies the data stored as part of that entry. -** Only the data content may only be modified, it is not possible -** to change the length of the data stored. +** +** Only the data content may only be modified, it is not possible to +** change the length of the data stored. If this function is called with +** parameters that attempt to write past the end of the existing data, +** no modifications are made and SQLITE_CORRUPT is returned. */ SQLITE_PRIVATE int sqlite3BtreePutData(BtCursor *pCsr, u32 offset, u32 amt, void *z){ int rc; - assert( cursorHoldsMutex(pCsr) ); assert( sqlite3_mutex_held(pCsr->pBtree->db->mutex) ); - assert(pCsr->isIncrblobHandle); + assert( pCsr->isIncrblobHandle ); - restoreCursorPosition(pCsr); + rc = restoreCursorPosition(pCsr); + if( rc!=SQLITE_OK ){ + return rc; + } assert( pCsr->eState!=CURSOR_REQUIRESEEK ); if( pCsr->eState!=CURSOR_VALID ){ return SQLITE_ABORT; } - /* Check some preconditions: + /* Check some assumptions: ** (a) the cursor is open for writing, - ** (b) there is no read-lock on the table being modified and - ** (c) the cursor points at a valid row of an intKey table. + ** (b) there is a read/write transaction open, + ** (c) the connection holds a write-lock on the table (if required), + ** (d) there are no conflicting read-locks, and + ** (e) the cursor points at a valid row of an intKey table. */ if( !pCsr->wrFlag ){ return SQLITE_READONLY; } - assert( !pCsr->pBt->readOnly - && pCsr->pBt->inTransaction==TRANS_WRITE ); - rc = checkForReadConflicts(pCsr->pBtree, pCsr->pgnoRoot, pCsr, 0); - if( rc!=SQLITE_OK ){ - /* The table pCur points to has a read lock */ - assert( rc==SQLITE_LOCKED_SHAREDCACHE ); - return rc; - } - if( pCsr->eState==CURSOR_INVALID || !pCsr->apPage[pCsr->iPage]->intKey ){ - return SQLITE_ERROR; - } + assert( !pCsr->pBt->readOnly && pCsr->pBt->inTransaction==TRANS_WRITE ); + assert( hasSharedCacheTableLock(pCsr->pBtree, pCsr->pgnoRoot, 0, 2) ); + assert( !hasReadConflicts(pCsr->pBtree, pCsr->pgnoRoot) ); + assert( pCsr->apPage[pCsr->iPage]->intKey ); - return accessPayload(pCsr, offset, amt, (unsigned char *)z, 0, 1); + return accessPayload(pCsr, offset, amt, (unsigned char *)z, 1); } /* @@ -44611,8 +45426,6 @@ ************************************************************************* ** This file contains the implementation of the sqlite3_backup_XXX() ** API functions and the related features. -** -** $Id: backup.c,v 1.17 2009/06/03 11:25:07 danielk1977 Exp $ */ /* Macro to find the minimum of two numeric values. @@ -44916,7 +45729,7 @@ && SQLITE_OK==(rc = sqlite3BtreeBeginTrans(p->pDest, 2)) ){ p->bDestLocked = 1; - rc = sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema); + sqlite3BtreeGetMeta(p->pDest, BTREE_SCHEMA_VERSION, &p->iDestSchema); } /* If there is no open read-transaction on the source database, open @@ -44956,17 +45769,18 @@ } } - if( rc==SQLITE_DONE ){ + /* Update the schema version field in the destination database. This + ** is to make sure that the schema-version really does change in + ** the case where the source and destination databases have the + ** same schema version. + */ + if( rc==SQLITE_DONE + && (rc = sqlite3BtreeUpdateMeta(p->pDest,1,p->iDestSchema+1))==SQLITE_OK + ){ const int nSrcPagesize = sqlite3BtreeGetPageSize(p->pSrc); const int nDestPagesize = sqlite3BtreeGetPageSize(p->pDest); int nDestTruncate; - /* Update the schema version field in the destination database. This - ** is to make sure that the schema-version really does change in - ** the case where the source and destination databases have the - ** same schema version. - */ - sqlite3BtreeUpdateMeta(p->pDest, 1, p->iDestSchema+1); if( p->pDestDb ){ sqlite3ResetInternalSchema(p->pDestDb, 0); } @@ -45244,8 +46058,6 @@ ** stores a single value in the VDBE. Mem is an opaque structure visible ** only within the VDBE. Interface routines refer to a Mem using the ** name sqlite_value -** -** $Id: vdbemem.c,v 1.150 2009/06/25 01:47:12 drh Exp $ */ /* @@ -45498,7 +46310,11 @@ */ SQLITE_PRIVATE void sqlite3VdbeMemReleaseExternal(Mem *p){ assert( p->db==0 || sqlite3_mutex_held(p->db->mutex) ); - if( p->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet) ){ + testcase( p->flags & MEM_Agg ); + testcase( p->flags & MEM_Dyn ); + testcase( p->flags & MEM_RowSet ); + testcase( p->flags & MEM_Frame ); + if( p->flags&(MEM_Agg|MEM_Dyn|MEM_RowSet|MEM_Frame) ){ if( p->flags&MEM_Agg ){ sqlite3VdbeMemFinalize(p, p->u.pDef); assert( (p->flags & MEM_Agg)==0 ); @@ -45509,6 +46325,8 @@ p->xDel = 0; }else if( p->flags&MEM_RowSet ){ sqlite3RowSetClear(p->u.pRowSet); + }else if( p->flags&MEM_Frame ){ + sqlite3VdbeMemSetNull(p); } } } @@ -45646,11 +46464,14 @@ ** (2) The integer is neither the largest nor the smallest ** possible integer (ticket #3922) ** - ** The second term in the following conditional enforces the second - ** condition under the assumption that additional overflow causes - ** values to wrap around. + ** The second and third terms in the following conditional enforces + ** the second condition under the assumption that addition overflow causes + ** values to wrap around. On x86 hardware, the third term is always + ** true and could be omitted. But we leave it in because other + ** architectures might behave differently. */ - if( pMem->r==(double)pMem->u.i && (pMem->u.i-1) < (pMem->u.i+1) ){ + if( pMem->r==(double)pMem->u.i && pMem->u.i>SMALLEST_INT64 + && ALWAYS(pMem->u.iflags |= MEM_Int; } } @@ -45707,6 +46528,9 @@ ** Delete any previous value and set the value stored in *pMem to NULL. */ SQLITE_PRIVATE void sqlite3VdbeMemSetNull(Mem *pMem){ + if( pMem->flags & MEM_Frame ){ + sqlite3VdbeFrameDelete(pMem->u.pFrame); + } if( pMem->flags & MEM_RowSet ){ sqlite3RowSetClear(pMem->u.pRowSet); } @@ -45726,6 +46550,14 @@ if( n<0 ) n = 0; pMem->u.nZero = n; pMem->enc = SQLITE_UTF8; + +#ifdef SQLITE_OMIT_INCRBLOB + sqlite3VdbeMemGrow(pMem, n, 0); + if( pMem->z ){ + pMem->n = n; + memset(pMem->z, 0, n); + } +#endif } /* @@ -45964,9 +46796,6 @@ int f1, f2; int combined_flags; - /* Interchange pMem1 and pMem2 if the collating sequence specifies - ** DESC order. - */ f1 = pMem1->flags; f2 = pMem2->flags; combined_flags = f1|f2; @@ -46095,6 +46924,8 @@ int available = 0; /* Number of bytes available on the local btree page */ int rc = SQLITE_OK; /* Return code */ + assert( sqlite3BtreeCursorIsValid(pCur) ); + /* Note: the calls to BtreeKeyFetch() and DataFetch() below assert() ** that both the BtShared and database handle mutexes are held. */ assert( (pMem->flags & MEM_RowSet)==0 ); @@ -46214,6 +47045,9 @@ return SQLITE_OK; } op = pExpr->op; + if( op==TK_REGISTER ){ + op = pExpr->op2; /* This only happens with SQLITE_ENABLE_STAT2 */ + } if( op==TK_STRING || op==TK_FLOAT || op==TK_INTEGER ){ pVal = sqlite3ValueNew(db); @@ -46224,6 +47058,7 @@ zVal = sqlite3DbStrDup(db, pExpr->u.zToken); if( zVal==0 ) goto no_mem; sqlite3ValueSetStr(pVal, -1, zVal, SQLITE_UTF8, SQLITE_DYNAMIC); + if( op==TK_FLOAT ) pVal->type = SQLITE_FLOAT; } if( (op==TK_INTEGER || op==TK_FLOAT ) && affinity==SQLITE_AFF_NONE ){ sqlite3ValueApplyAffinity(pVal, SQLITE_AFF_NUMERIC, SQLITE_UTF8); @@ -46255,6 +47090,9 @@ } #endif + if( pVal ){ + sqlite3VdbeMemStoreType(pVal); + } *ppVal = pVal; return SQLITE_OK; @@ -46321,8 +47159,6 @@ ** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.) Prior ** to version 2.8.7, all this code was combined into the vdbe.c source file. ** But that file was getting too big so this subroutines were split out. -** -** $Id: vdbeaux.c,v 1.467 2009/06/26 16:32:13 shane Exp $ */ @@ -46359,13 +47195,14 @@ ** Remember the SQL string for a prepared statement. */ SQLITE_PRIVATE void sqlite3VdbeSetSql(Vdbe *p, const char *z, int n, int isPrepareV2){ + assert( isPrepareV2==1 || isPrepareV2==0 ); if( p==0 ) return; #ifdef SQLITE_OMIT_TRACE if( !isPrepareV2 ) return; #endif assert( p->zSql==0 ); p->zSql = sqlite3DbStrNDup(p->db, z, n); - p->isPrepareV2 = isPrepareV2 ? 1 : 0; + p->isPrepareV2 = (u8)isPrepareV2; } /* @@ -46503,6 +47340,22 @@ } /* +** Add an opcode that includes the p4 value as an integer. +*/ +SQLITE_PRIVATE int sqlite3VdbeAddOp4Int( + Vdbe *p, /* Add the opcode to this VM */ + int op, /* The new opcode */ + int p1, /* The P1 operand */ + int p2, /* The P2 operand */ + int p3, /* The P3 operand */ + int p4 /* The P4 operand as an integer */ +){ + int addr = sqlite3VdbeAddOp3(p, op, p1, p2, p3); + sqlite3VdbeChangeP4(p, addr, SQLITE_INT_TO_PTR(p4), P4_INT32); + return addr; +} + +/* ** Create a new symbolic label for an instruction that has yet to be ** coded. The symbolic label is really just a negative number. The ** label can be used as the P2 value of an operation. Later, when @@ -46546,6 +47399,127 @@ } } +#ifdef SQLITE_DEBUG /* sqlite3AssertMayAbort() logic */ + +/* +** The following type and function are used to iterate through all opcodes +** in a Vdbe main program and each of the sub-programs (triggers) it may +** invoke directly or indirectly. It should be used as follows: +** +** Op *pOp; +** VdbeOpIter sIter; +** +** memset(&sIter, 0, sizeof(sIter)); +** sIter.v = v; // v is of type Vdbe* +** while( (pOp = opIterNext(&sIter)) ){ +** // Do something with pOp +** } +** sqlite3DbFree(v->db, sIter.apSub); +** +*/ +typedef struct VdbeOpIter VdbeOpIter; +struct VdbeOpIter { + Vdbe *v; /* Vdbe to iterate through the opcodes of */ + SubProgram **apSub; /* Array of subprograms */ + int nSub; /* Number of entries in apSub */ + int iAddr; /* Address of next instruction to return */ + int iSub; /* 0 = main program, 1 = first sub-program etc. */ +}; +static Op *opIterNext(VdbeOpIter *p){ + Vdbe *v = p->v; + Op *pRet = 0; + Op *aOp; + int nOp; + + if( p->iSub<=p->nSub ){ + + if( p->iSub==0 ){ + aOp = v->aOp; + nOp = v->nOp; + }else{ + aOp = p->apSub[p->iSub-1]->aOp; + nOp = p->apSub[p->iSub-1]->nOp; + } + assert( p->iAddriAddr]; + p->iAddr++; + if( p->iAddr==nOp ){ + p->iSub++; + p->iAddr = 0; + } + + if( pRet->p4type==P4_SUBPROGRAM ){ + int nByte = (p->nSub+1)*sizeof(SubProgram*); + int j; + for(j=0; jnSub; j++){ + if( p->apSub[j]==pRet->p4.pProgram ) break; + } + if( j==p->nSub ){ + p->apSub = sqlite3DbReallocOrFree(v->db, p->apSub, nByte); + if( !p->apSub ){ + pRet = 0; + }else{ + p->apSub[p->nSub++] = pRet->p4.pProgram; + } + } + } + } + + return pRet; +} + +/* +** Check if the program stored in the VM associated with pParse may +** throw an ABORT exception (causing the statement, but not entire transaction +** to be rolled back). This condition is true if the main program or any +** sub-programs contains any of the following: +** +** * OP_Halt with P1=SQLITE_CONSTRAINT and P2=OE_Abort. +** * OP_HaltIfNull with P1=SQLITE_CONSTRAINT and P2=OE_Abort. +** * OP_Destroy +** * OP_VUpdate +** * OP_VRename +** * OP_FkCounter with P2==0 (immediate foreign key constraint) +** +** Then check that the value of Parse.mayAbort is true if an +** ABORT may be thrown, or false otherwise. Return true if it does +** match, or false otherwise. This function is intended to be used as +** part of an assert statement in the compiler. Similar to: +** +** assert( sqlite3VdbeAssertMayAbort(pParse->pVdbe, pParse->mayAbort) ); +*/ +SQLITE_PRIVATE int sqlite3VdbeAssertMayAbort(Vdbe *v, int mayAbort){ + int hasAbort = 0; + Op *pOp; + VdbeOpIter sIter; + memset(&sIter, 0, sizeof(sIter)); + sIter.v = v; + + while( (pOp = opIterNext(&sIter))!=0 ){ + int opcode = pOp->opcode; + if( opcode==OP_Destroy || opcode==OP_VUpdate || opcode==OP_VRename +#ifndef SQLITE_OMIT_FOREIGN_KEY + || (opcode==OP_FkCounter && pOp->p1==0 && pOp->p2==1) +#endif + || ((opcode==OP_Halt || opcode==OP_HaltIfNull) + && (pOp->p1==SQLITE_CONSTRAINT && pOp->p2==OE_Abort)) + ){ + hasAbort = 1; + break; + } + } + sqlite3DbFree(v->db, sIter.apSub); + + /* Return true if hasAbort==mayAbort. Or if a malloc failure occured. + ** If malloc failed, then the while() loop above may not have iterated + ** through all opcodes and hasAbort may be set incorrectly. Return + ** true for this case to prevent the assert() in the callers frame + ** from failing. */ + return ( v->db->mallocFailed || hasAbort==mayAbort ); +} +#endif /* SQLITE_DEBUG - the sqlite3AssertMayAbort() function */ + /* ** Loop through the program looking for P2 values that are negative ** on jump instructions. Each such value is a label. Resolve the @@ -46557,52 +47531,25 @@ ** to an OP_Function, OP_AggStep or OP_VFilter opcode. This is used by ** sqlite3VdbeMakeReady() to size the Vdbe.apArg[] array. ** -** This routine also does the following optimization: It scans for -** instructions that might cause a statement rollback. Such instructions -** are: -** -** * OP_Halt with P1=SQLITE_CONSTRAINT and P2=OE_Abort. -** * OP_Destroy -** * OP_VUpdate -** * OP_VRename -** -** If no such instruction is found, then every Statement instruction -** is changed to a Noop. In this way, we avoid creating the statement -** journal file unnecessarily. +** The Op.opflags field is set on all opcodes. */ static void resolveP2Values(Vdbe *p, int *pMaxFuncArgs){ int i; - int nMaxArgs = 0; + int nMaxArgs = *pMaxFuncArgs; Op *pOp; int *aLabel = p->aLabel; - int doesStatementRollback = 0; - int hasStatementBegin = 0; p->readOnly = 1; - p->usesStmtJournal = 0; for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){ u8 opcode = pOp->opcode; + pOp->opflags = sqlite3OpcodeProperty[opcode]; if( opcode==OP_Function || opcode==OP_AggStep ){ if( pOp->p5>nMaxArgs ) nMaxArgs = pOp->p5; -#ifndef SQLITE_OMIT_VIRTUALTABLE - }else if( opcode==OP_VUpdate ){ - if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2; -#endif - } - if( opcode==OP_Halt ){ - if( pOp->p1==SQLITE_CONSTRAINT && pOp->p2==OE_Abort ){ - doesStatementRollback = 1; - } - }else if( opcode==OP_Statement ){ - hasStatementBegin = 1; - p->usesStmtJournal = 1; - }else if( opcode==OP_Destroy ){ - doesStatementRollback = 1; }else if( opcode==OP_Transaction && pOp->p2!=0 ){ p->readOnly = 0; #ifndef SQLITE_OMIT_VIRTUALTABLE - }else if( opcode==OP_VUpdate || opcode==OP_VRename ){ - doesStatementRollback = 1; + }else if( opcode==OP_VUpdate ){ + if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2; }else if( opcode==OP_VFilter ){ int n; assert( p->nOp - i >= 3 ); @@ -46612,7 +47559,7 @@ #endif } - if( sqlite3VdbeOpcodeHasProperty(opcode, OPFLG_JUMP) && pOp->p2<0 ){ + if( (pOp->opflags & OPFLG_JUMP)!=0 && pOp->p2<0 ){ assert( -1-pOp->p2nLabel ); pOp->p2 = aLabel[-1-pOp->p2]; } @@ -46621,20 +47568,6 @@ p->aLabel = 0; *pMaxFuncArgs = nMaxArgs; - - /* If we never rollback a statement transaction, then statement - ** transactions are not needed. So change every OP_Statement - ** opcode into an OP_Noop. This avoid a call to sqlite3OsOpenExclusive() - ** which can be expensive on some platforms. - */ - if( hasStatementBegin && !doesStatementRollback ){ - p->usesStmtJournal = 0; - for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++){ - if( pOp->opcode==OP_Statement ){ - pOp->opcode = OP_Noop; - } - } - } } /* @@ -46646,6 +47579,30 @@ } /* +** This function returns a pointer to the array of opcodes associated with +** the Vdbe passed as the first argument. It is the callers responsibility +** to arrange for the returned array to be eventually freed using the +** vdbeFreeOpArray() function. +** +** Before returning, *pnOp is set to the number of entries in the returned +** array. Also, *pnMaxArg is set to the larger of its current value and +** the number of entries in the Vdbe.apArg[] array required to execute the +** returned program. +*/ +SQLITE_PRIVATE VdbeOp *sqlite3VdbeTakeOpArray(Vdbe *p, int *pnOp, int *pnMaxArg){ + VdbeOp *aOp = p->aOp; + assert( aOp && !p->db->mallocFailed ); + + /* Check that sqlite3VdbeUsesBtree() was not called on this VM */ + assert( p->aMutex.nMutex==0 ); + + resolveP2Values(p, pnMaxArg); + *pnOp = p->nOp; + p->aOp = 0; + return aOp; +} + +/* ** Add a whole list of operations to the operation stack. Return the ** address of the first operation added. */ @@ -46664,7 +47621,7 @@ VdbeOp *pOut = &p->aOp[i+addr]; pOut->opcode = pIn->opcode; pOut->p1 = pIn->p1; - if( p2<0 && sqlite3VdbeOpcodeHasProperty(pOut->opcode, OPFLG_JUMP) ){ + if( p2<0 && (sqlite3OpcodeProperty[pOut->opcode] & OPFLG_JUMP)!=0 ){ pOut->p2 = addr + ADDR(p2); }else{ pOut->p2 = p2; @@ -46784,6 +47741,61 @@ sqlite3ValueFree((sqlite3_value*)p4); break; } + case P4_VTAB : { + sqlite3VtabUnlock((VTable *)p4); + break; + } + case P4_SUBPROGRAM : { + sqlite3VdbeProgramDelete(db, (SubProgram *)p4, 1); + break; + } + } + } +} + +/* +** Free the space allocated for aOp and any p4 values allocated for the +** opcodes contained within. If aOp is not NULL it is assumed to contain +** nOp entries. +*/ +static void vdbeFreeOpArray(sqlite3 *db, Op *aOp, int nOp){ + if( aOp ){ + Op *pOp; + for(pOp=aOp; pOp<&aOp[nOp]; pOp++){ + freeP4(db, pOp->p4type, pOp->p4.p); +#ifdef SQLITE_DEBUG + sqlite3DbFree(db, pOp->zComment); +#endif + } + } + sqlite3DbFree(db, aOp); +} + +/* +** Decrement the ref-count on the SubProgram structure passed as the +** second argument. If the ref-count reaches zero, free the structure. +** +** The array of VDBE opcodes stored as SubProgram.aOp is freed if +** either the ref-count reaches zero or parameter freeop is non-zero. +** +** Since the array of opcodes pointed to by SubProgram.aOp may directly +** or indirectly contain a reference to the SubProgram structure itself. +** By passing a non-zero freeop parameter, the caller may ensure that all +** SubProgram structures and their aOp arrays are freed, even when there +** are such circular references. +*/ +SQLITE_PRIVATE void sqlite3VdbeProgramDelete(sqlite3 *db, SubProgram *p, int freeop){ + if( p ){ + assert( p->nRef>0 ); + if( freeop || p->nRef==1 ){ + Op *aOp = p->aOp; + p->aOp = 0; + vdbeFreeOpArray(db, aOp, p->nOp); + p->nOp = 0; + } + p->nRef--; + if( p->nRef==0 ){ + sqlite3DbFree(db, p); } } } @@ -46837,7 +47849,7 @@ db = p->db; assert( p->magic==VDBE_MAGIC_INIT ); if( p->aOp==0 || db->mallocFailed ){ - if (n != P4_KEYINFO) { + if ( n!=P4_KEYINFO && n!=P4_VTAB ) { freeP4(db, n, (void*)*(char**)&zP4); } return; @@ -46882,6 +47894,11 @@ }else if( n==P4_KEYINFO_HANDOFF ){ pOp->p4.p = (void*)zP4; pOp->p4type = P4_KEYINFO; + }else if( n==P4_VTAB ){ + pOp->p4.p = (void*)zP4; + pOp->p4type = P4_VTAB; + sqlite3VtabLock((VTable *)zP4); + assert( ((VTable *)zP4)->db==p->db ); }else if( n<0 ){ pOp->p4.p = (void*)zP4; pOp->p4type = (signed char)n; @@ -46901,6 +47918,7 @@ */ SQLITE_PRIVATE void sqlite3VdbeComment(Vdbe *p, const char *zFormat, ...){ va_list ap; + if( !p ) return; assert( p->nOp>0 || p->aOp==0 ); assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed ); if( p->nOp ){ @@ -46913,6 +47931,7 @@ } SQLITE_PRIVATE void sqlite3VdbeNoopComment(Vdbe *p, const char *zFormat, ...){ va_list ap; + if( !p ) return; sqlite3VdbeAddOp0(p, OP_Noop); assert( p->nOp>0 || p->aOp==0 ); assert( p->aOp==0 || p->aOp[p->nOp-1].zComment==0 || p->db->mallocFailed ); @@ -47032,12 +48051,15 @@ sqlite3_snprintf(nTemp, zTemp, "%lld", pMem->u.i); }else if( pMem->flags & MEM_Real ){ sqlite3_snprintf(nTemp, zTemp, "%.16g", pMem->r); + }else{ + assert( pMem->flags & MEM_Blob ); + zP4 = "(blob)"; } break; } #ifndef SQLITE_OMIT_VIRTUALTABLE case P4_VTAB: { - sqlite3_vtab *pVtab = pOp->p4.pVtab; + sqlite3_vtab *pVtab = pOp->p4.pVtab->pVtab; sqlite3_snprintf(nTemp, zTemp, "vtab:%p:%p", pVtab, pVtab->pModule); break; } @@ -47046,6 +48068,10 @@ sqlite3_snprintf(nTemp, zTemp, "intarray"); break; } + case P4_SUBPROGRAM: { + sqlite3_snprintf(nTemp, zTemp, "program"); + break; + } default: { zP4 = pOp->p4.z; if( zP4==0 ){ @@ -47061,7 +48087,6 @@ /* ** Declare to the Vdbe that the BTree object at db->aDb[i] is used. -** */ SQLITE_PRIVATE void sqlite3VdbeUsesBtree(Vdbe *p, int i){ int mask; @@ -47120,7 +48145,7 @@ ** with no indexes using a single prepared INSERT statement, bind() ** and reset(). Inserts are grouped into a transaction. */ - if( p->flags&(MEM_Agg|MEM_Dyn) ){ + if( p->flags&(MEM_Agg|MEM_Dyn|MEM_Frame|MEM_RowSet) ){ sqlite3VdbeMemRelease(p); }else if( p->zMalloc ){ sqlite3DbFree(db, p->zMalloc); @@ -47133,25 +48158,20 @@ } } -#ifdef SQLITE_ENABLE_MEMORY_MANAGEMENT -SQLITE_PRIVATE int sqlite3VdbeReleaseBuffers(Vdbe *p){ - int ii; - int nFree = 0; - assert( sqlite3_mutex_held(p->db->mutex) ); - for(ii=1; ii<=p->nMem; ii++){ - Mem *pMem = &p->aMem[ii]; - if( pMem->flags & MEM_RowSet ){ - sqlite3RowSetClear(pMem->u.pRowSet); - } - if( pMem->z && pMem->flags&MEM_Dyn ){ - assert( !pMem->xDel ); - nFree += sqlite3DbMallocSize(pMem->db, pMem->z); - sqlite3VdbeMemRelease(pMem); - } +/* +** Delete a VdbeFrame object and its contents. VdbeFrame objects are +** allocated by the OP_Program opcode in sqlite3VdbeExec(). +*/ +SQLITE_PRIVATE void sqlite3VdbeFrameDelete(VdbeFrame *p){ + int i; + Mem *aMem = VdbeFrameMem(p); + VdbeCursor **apCsr = (VdbeCursor **)&aMem[p->nChildMem]; + for(i=0; inChildCsr; i++){ + sqlite3VdbeFreeCursor(p->v, apCsr[i]); } - return nFree; + releaseMemArray(aMem, p->nChildMem); + sqlite3DbFree(p->v->db, p); } -#endif #ifndef SQLITE_OMIT_EXPLAIN /* @@ -47165,17 +48185,24 @@ ** p->explain==2, only OP_Explain instructions are listed and these ** are shown in a different format. p->explain==2 is used to implement ** EXPLAIN QUERY PLAN. +** +** When p->explain==1, first the main program is listed, then each of +** the trigger subprograms are listed one by one. */ SQLITE_PRIVATE int sqlite3VdbeList( Vdbe *p /* The VDBE */ ){ - sqlite3 *db = p->db; - int i; - int rc = SQLITE_OK; - Mem *pMem = p->pResultSet = &p->aMem[1]; + int nRow; /* Stop when row count reaches this */ + int nSub = 0; /* Number of sub-vdbes seen so far */ + SubProgram **apSub = 0; /* Array of sub-vdbes */ + Mem *pSub = 0; /* Memory cell hold array of subprogs */ + sqlite3 *db = p->db; /* The database connection */ + int i; /* Loop counter */ + int rc = SQLITE_OK; /* Return code */ + Mem *pMem = p->pResultSet = &p->aMem[1]; /* First Mem of result set */ assert( p->explain ); - if( p->magic!=VDBE_MAGIC_RUN ) return SQLITE_MISUSE; + assert( p->magic==VDBE_MAGIC_RUN ); assert( db->magic==SQLITE_MAGIC_BUSY ); assert( p->rc==SQLITE_OK || p->rc==SQLITE_BUSY || p->rc==SQLITE_NOMEM ); @@ -47183,7 +48210,7 @@ ** the result, result columns may become dynamic if the user calls ** sqlite3_column_text16(), causing a translation to UTF-16 encoding. */ - releaseMemArray(pMem, p->nMem); + releaseMemArray(pMem, 8); if( p->rc==SQLITE_NOMEM ){ /* This happens if a malloc() inside a call to sqlite3_column_text() or @@ -47192,10 +48219,36 @@ return SQLITE_ERROR; } + /* When the number of output rows reaches nRow, that means the + ** listing has finished and sqlite3_step() should return SQLITE_DONE. + ** nRow is the sum of the number of rows in the main program, plus + ** the sum of the number of rows in all trigger subprograms encountered + ** so far. The nRow value will increase as new trigger subprograms are + ** encountered, but p->pc will eventually catch up to nRow. + */ + nRow = p->nOp; + if( p->explain==1 ){ + /* The first 8 memory cells are used for the result set. So we will + ** commandeer the 9th cell to use as storage for an array of pointers + ** to trigger subprograms. The VDBE is guaranteed to have at least 9 + ** cells. */ + assert( p->nMem>9 ); + pSub = &p->aMem[9]; + if( pSub->flags&MEM_Blob ){ + /* On the first call to sqlite3_step(), pSub will hold a NULL. It is + ** initialized to a BLOB by the P4_SUBPROGRAM processing logic below */ + nSub = pSub->n/sizeof(Vdbe*); + apSub = (SubProgram **)pSub->z; + } + for(i=0; inOp; + } + } + do{ i = p->pc++; - }while( inOp && p->explain==2 && p->aOp[i].opcode!=OP_Explain ); - if( i>=p->nOp ){ + }while( iexplain==2 && p->aOp[i].opcode!=OP_Explain ); + if( i>=nRow ){ p->rc = SQLITE_OK; rc = SQLITE_DONE; }else if( db->u1.isInterrupted ){ @@ -47204,7 +48257,21 @@ sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3ErrStr(p->rc)); }else{ char *z; - Op *pOp = &p->aOp[i]; + Op *pOp; + if( inOp ){ + /* The output line number is small enough that we are still in the + ** main program. */ + pOp = &p->aOp[i]; + }else{ + /* We are currently listing subprograms. Figure out which one and + ** pick up the appropriate opcode. */ + int j; + i -= p->nOp; + for(j=0; i>=apSub[j]->nOp; j++){ + i -= apSub[j]->nOp; + } + pOp = &apSub[j]->aOp[i]; + } if( p->explain==1 ){ pMem->flags = MEM_Int; pMem->type = SQLITE_INTEGER; @@ -47218,6 +48285,25 @@ pMem->type = SQLITE_TEXT; pMem->enc = SQLITE_UTF8; pMem++; + + /* When an OP_Program opcode is encounter (the only opcode that has + ** a P4_SUBPROGRAM argument), expand the size of the array of subprograms + ** kept in p->aMem[9].z to hold the new program - assuming this subprogram + ** has not already been seen. + */ + if( pOp->p4type==P4_SUBPROGRAM ){ + int nByte = (nSub+1)*sizeof(SubProgram*); + int j; + for(j=0; jp4.pProgram ) break; + } + if( j==nSub && SQLITE_OK==sqlite3VdbeMemGrow(pSub, nByte, 1) ){ + apSub = (SubProgram **)pSub->z; + apSub[nSub++] = pOp->p4.pProgram; + pSub->flags |= MEM_Blob; + pSub->n = nSub*sizeof(SubProgram*); + } + } } pMem->flags = MEM_Int; @@ -47336,38 +48422,43 @@ #endif /* !SQLITE_OMIT_TRACE && SQLITE_ENABLE_IOTRACE */ /* -** Allocate space from a fixed size buffer. Make *pp point to the -** allocated space. (Note: pp is a char* rather than a void** to -** work around the pointer aliasing rules of C.) *pp should initially -** be zero. If *pp is not zero, that means that the space has already -** been allocated and this routine is a noop. +** Allocate space from a fixed size buffer and return a pointer to +** that space. If insufficient space is available, return NULL. +** +** The pBuf parameter is the initial value of a pointer which will +** receive the new memory. pBuf is normally NULL. If pBuf is not +** NULL, it means that memory space has already been allocated and that +** this routine should not allocate any new memory. When pBuf is not +** NULL simply return pBuf. Only allocate new memory space when pBuf +** is NULL. ** ** nByte is the number of bytes of space needed. ** -** *ppFrom point to available space and pEnd points to the end of the -** available space. +** *ppFrom points to available space and pEnd points to the end of the +** available space. When space is allocated, *ppFrom is advanced past +** the end of the allocated space. ** ** *pnByte is a counter of the number of bytes of space that have failed ** to allocate. If there is insufficient space in *ppFrom to satisfy the ** request, then increment *pnByte by the amount of the request. */ -static void allocSpace( - char *pp, /* IN/OUT: Set *pp to point to allocated buffer */ +static void *allocSpace( + void *pBuf, /* Where return pointer will be stored */ int nByte, /* Number of bytes to allocate */ u8 **ppFrom, /* IN/OUT: Allocate from *ppFrom */ u8 *pEnd, /* Pointer to 1 byte past the end of *ppFrom buffer */ int *pnByte /* If allocation cannot be made, increment *pnByte */ ){ assert( EIGHT_BYTE_ALIGNMENT(*ppFrom) ); - if( (*(void**)pp)==0 ){ - nByte = ROUND8(nByte); - if( (pEnd - *ppFrom)>=nByte ){ - *(void**)pp = (void *)*ppFrom; - *ppFrom += nByte; - }else{ - *pnByte += nByte; - } + if( pBuf ) return pBuf; + nByte = ROUND8(nByte); + if( &(*ppFrom)[nByte] <= pEnd ){ + pBuf = (void*)*ppFrom; + *ppFrom += nByte; + }else{ + *pnByte += nByte; } + return pBuf; } /* @@ -47392,7 +48483,9 @@ int nVar, /* Number of '?' see in the SQL statement */ int nMem, /* Number of memory cells to allocate */ int nCursor, /* Number of cursors to allocate */ - int isExplain /* True if the EXPLAIN keywords is present */ + int nArg, /* Maximum number of args in SubPrograms */ + int isExplain, /* True if the EXPLAIN keywords is present */ + int usesStmtJournal /* True to set Vdbe.usesStmtJournal */ ){ int n; sqlite3 *db = p->db; @@ -47423,31 +48516,40 @@ ** first time this function is called for a given VDBE, not when it is ** being called from sqlite3_reset() to reset the virtual machine. */ - if( nVar>=0 && !db->mallocFailed ){ - u8 *zCsr = (u8 *)&p->aOp[p->nOp]; - u8 *zEnd = (u8 *)&p->aOp[p->nOpAlloc]; - int nByte; - int nArg; /* Maximum number of args passed to a user function. */ + if( nVar>=0 && ALWAYS(db->mallocFailed==0) ){ + u8 *zCsr = (u8 *)&p->aOp[p->nOp]; /* Memory avaliable for alloation */ + u8 *zEnd = (u8 *)&p->aOp[p->nOpAlloc]; /* First byte past available mem */ + int nByte; /* How much extra memory needed */ + resolveP2Values(p, &nArg); + p->usesStmtJournal = (u8)usesStmtJournal; if( isExplain && nMem<10 ){ nMem = 10; } + memset(zCsr, 0, zEnd-zCsr); zCsr += (zCsr - (u8*)0)&7; assert( EIGHT_BYTE_ALIGNMENT(zCsr) ); - if( zEndaMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte); - allocSpace((char*)&p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte); - allocSpace((char*)&p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte); - allocSpace((char*)&p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte); - allocSpace((char*)&p->apCsr, - nCursor*sizeof(VdbeCursor*), &zCsr, zEnd, &nByte - ); + p->aMem = allocSpace(p->aMem, nMem*sizeof(Mem), &zCsr, zEnd, &nByte); + p->aVar = allocSpace(p->aVar, nVar*sizeof(Mem), &zCsr, zEnd, &nByte); + p->apArg = allocSpace(p->apArg, nArg*sizeof(Mem*), &zCsr, zEnd, &nByte); + p->azVar = allocSpace(p->azVar, nVar*sizeof(char*), &zCsr, zEnd, &nByte); + p->apCsr = allocSpace(p->apCsr, nCursor*sizeof(VdbeCursor*), + &zCsr, zEnd, &nByte); if( nByte ){ - p->pFree = sqlite3DbMallocRaw(db, nByte); + p->pFree = sqlite3DbMallocZero(db, nByte); } zCsr = p->pFree; zEnd = &zCsr[nByte]; @@ -47455,7 +48557,7 @@ p->nCursor = (u16)nCursor; if( p->aVar ){ - p->nVar = (u16)nVar; + p->nVar = (ynVar)nVar; for(n=0; naVar[n].flags = MEM_Null; p->aVar[n].db = db; @@ -47522,25 +48624,56 @@ p->inVtabMethod = 0; } #endif - if( !pCx->ephemPseudoTable ){ - sqlite3DbFree(p->db, pCx->pData); - } } /* -** Close all cursors except for VTab cursors that are currently -** in use. -*/ -static void closeAllCursorsExceptActiveVtabs(Vdbe *p){ - int i; - if( p->apCsr==0 ) return; - for(i=0; inCursor; i++){ - VdbeCursor *pC = p->apCsr[i]; - if( pC && (!p->inVtabMethod || !pC->pVtabCursor) ){ - sqlite3VdbeFreeCursor(p, pC); - p->apCsr[i] = 0; +** Copy the values stored in the VdbeFrame structure to its Vdbe. This +** is used, for example, when a trigger sub-program is halted to restore +** control to the main program. +*/ +SQLITE_PRIVATE int sqlite3VdbeFrameRestore(VdbeFrame *pFrame){ + Vdbe *v = pFrame->v; + v->aOp = pFrame->aOp; + v->nOp = pFrame->nOp; + v->aMem = pFrame->aMem; + v->nMem = pFrame->nMem; + v->apCsr = pFrame->apCsr; + v->nCursor = pFrame->nCursor; + v->db->lastRowid = pFrame->lastRowid; + v->nChange = pFrame->nChange; + return pFrame->pc; +} + +/* +** Close all cursors. +** +** Also release any dynamic memory held by the VM in the Vdbe.aMem memory +** cell array. This is necessary as the memory cell array may contain +** pointers to VdbeFrame objects, which may in turn contain pointers to +** open cursors. +*/ +static void closeAllCursors(Vdbe *p){ + if( p->pFrame ){ + VdbeFrame *pFrame = p->pFrame; + for(pFrame=p->pFrame; pFrame->pParent; pFrame=pFrame->pParent); + sqlite3VdbeFrameRestore(pFrame); + } + p->pFrame = 0; + p->nFrame = 0; + + if( p->apCsr ){ + int i; + for(i=0; inCursor; i++){ + VdbeCursor *pC = p->apCsr[i]; + if( pC ){ + sqlite3VdbeFreeCursor(p, pC); + p->apCsr[i] = 0; + } } } + if( p->aMem ){ + releaseMemArray(&p->aMem[1], p->nMem); + } } /* @@ -47551,23 +48684,16 @@ ** variables in the aVar[] array. */ static void Cleanup(Vdbe *p){ - int i; sqlite3 *db = p->db; - Mem *pMem; - closeAllCursorsExceptActiveVtabs(p); - for(pMem=&p->aMem[1], i=1; i<=p->nMem; i++, pMem++){ - if( pMem->flags & MEM_RowSet ){ - sqlite3RowSetClear(pMem->u.pRowSet); - } - MemSetTypeFlag(pMem, MEM_Null); - } - releaseMemArray(&p->aMem[1], p->nMem); - if( p->contextStack ){ - sqlite3DbFree(db, p->contextStack); - } - p->contextStack = 0; - p->contextStackDepth = 0; - p->contextStackTop = 0; + +#ifdef SQLITE_DEBUG + /* Execute assert() statements to ensure that the Vdbe.apCsr[] and + ** Vdbe.aMem[] arrays have already been cleaned up. */ + int i; + for(i=0; inCursor; i++) assert( p->apCsr==0 || p->apCsr[i]==0 ); + for(i=1; i<=p->nMem; i++) assert( p->aMem==0 || p->aMem[i].flags==MEM_Null ); +#endif + sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = 0; p->pResultSet = 0; @@ -47675,12 +48801,9 @@ /* If there are any write-transactions at all, invoke the commit hook */ if( needXcommit && db->xCommitCallback ){ - assert( (db->flags & SQLITE_CommitBusy)==0 ); - db->flags |= SQLITE_CommitBusy; (void)sqlite3SafetyOff(db); rc = db->xCommitCallback(db->pCommitArg); (void)sqlite3SafetyOn(db); - db->flags &= ~SQLITE_CommitBusy; if( rc ){ return SQLITE_CONSTRAINT; } @@ -47766,10 +48889,11 @@ */ for(i=0; inDb; i++){ Btree *pBt = db->aDb[i].pBt; - if( i==1 ) continue; /* Ignore the TEMP database */ if( sqlite3BtreeIsInTrans(pBt) ){ char const *zFile = sqlite3BtreeGetJournalname(pBt); - if( zFile[0]==0 ) continue; /* Ignore :memory: databases */ + if( zFile==0 || zFile[0]==0 ){ + continue; /* Ignore TEMP and :memory: databases */ + } if( !needSync && !sqlite3BtreeSyncDisabled(pBt) ){ needSync = 1; } @@ -47923,7 +49047,13 @@ SQLITE_PRIVATE int sqlite3VdbeCloseStatement(Vdbe *p, int eOp){ sqlite3 *const db = p->db; int rc = SQLITE_OK; - if( p->iStatement && db->nStatement ){ + + /* If p->iStatement is greater than zero, then this Vdbe opened a + ** statement transaction that should be closed here. The only exception + ** is that an IO error may have occured, causing an emergency rollback. + ** In this case (db->nStatement==0), and there is nothing to do. + */ + if( db->nStatement && p->iStatement ){ int i; const int iSavepoint = p->iStatement-1; @@ -47948,6 +49078,13 @@ } db->nStatement--; p->iStatement = 0; + + /* If the statement transaction is being rolled back, also restore the + ** database handles deferred constraint counter to the value it had when + ** the statement transaction was opened. */ + if( eOp==SAVEPOINT_ROLLBACK ){ + db->nDeferredCons = p->nStmtDefCons; + } } return rc; } @@ -47980,6 +49117,29 @@ #endif /* +** This function is called when a transaction opened by the database +** handle associated with the VM passed as an argument is about to be +** committed. If there are outstanding deferred foreign key constraint +** violations, return SQLITE_ERROR. Otherwise, SQLITE_OK. +** +** If there are outstanding FK violations and this function returns +** SQLITE_ERROR, set the result of the VM to SQLITE_CONSTRAINT and write +** an error message to it. Then return SQLITE_ERROR. +*/ +#ifndef SQLITE_OMIT_FOREIGN_KEY +SQLITE_PRIVATE int sqlite3VdbeCheckFk(Vdbe *p, int deferred){ + sqlite3 *db = p->db; + if( (deferred && db->nDeferredCons>0) || (!deferred && p->nFkConstraint>0) ){ + p->rc = SQLITE_CONSTRAINT; + p->errorAction = OE_Abort; + sqlite3SetString(&p->zErrMsg, db, "foreign key constraint failed"); + return SQLITE_ERROR; + } + return SQLITE_OK; +} +#endif + +/* ** This routine is called the when a VDBE tries to halt. If the VDBE ** has made changes and is in autocommit mode, then commit those ** changes. If a rollback is needed, then do the rollback. @@ -48015,7 +49175,7 @@ if( p->db->mallocFailed ){ p->rc = SQLITE_NOMEM; } - closeAllCursorsExceptActiveVtabs(p); + closeAllCursors(p); if( p->magic!=VDBE_MAGIC_RUN ){ return SQLITE_OK; } @@ -48032,6 +49192,7 @@ /* Check for one of the special errors */ mrc = p->rc & 0xff; + assert( p->rc!=SQLITE_IOERR_BLOCKED ); /* This error no longer exists */ isSpecialError = mrc==SQLITE_NOMEM || mrc==SQLITE_IOERR || mrc==SQLITE_INTERRUPT || mrc==SQLITE_FULL; if( isSpecialError ){ @@ -48039,11 +49200,7 @@ ** proceed with the special handling. */ if( !p->readOnly || mrc!=SQLITE_INTERRUPT ){ - if( p->rc==SQLITE_IOERR_BLOCKED && p->usesStmtJournal ){ - eStatementOp = SAVEPOINT_ROLLBACK; - p->rc = SQLITE_BUSY; - }else if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) - && p->usesStmtJournal ){ + if( (mrc==SQLITE_NOMEM || mrc==SQLITE_FULL) && p->usesStmtJournal ){ eStatementOp = SAVEPOINT_ROLLBACK; }else{ /* We are forced to roll back the active transaction. Before doing @@ -48056,6 +49213,11 @@ } } } + + /* Check for immediate foreign key violations. */ + if( p->rc==SQLITE_OK ){ + sqlite3VdbeCheckFk(p, 0); + } /* If the auto-commit flag is set and this is the only active writer ** VM, then we do either a commit or rollback of the current transaction. @@ -48066,13 +49228,16 @@ if( !sqlite3VtabInSync(db) && db->autoCommit && db->writeVdbeCnt==(p->readOnly==0) - && (db->flags & SQLITE_CommitBusy)==0 ){ if( p->rc==SQLITE_OK || (p->errorAction==OE_Fail && !isSpecialError) ){ - /* The auto-commit flag is true, and the vdbe program was - ** successful or hit an 'OR FAIL' constraint. This means a commit - ** is required. - */ + if( sqlite3VdbeCheckFk(p, 1) ){ + sqlite3BtreeMutexArrayLeave(&p->aMutex); + return SQLITE_ERROR; + } + /* The auto-commit flag is true, the vdbe program was successful + ** or hit an 'OR FAIL' constraint and there are no deferred foreign + ** key constraints to hold up the transaction. This means a commit + ** is required. */ rc = vdbeCommit(db, p); if( rc==SQLITE_BUSY ){ sqlite3BtreeMutexArrayLeave(&p->aMutex); @@ -48081,6 +49246,7 @@ p->rc = rc; sqlite3RollbackAll(db); }else{ + db->nDeferredCons = 0; sqlite3CommitInternalChanges(db); } }else{ @@ -48118,7 +49284,7 @@ /* If this was an INSERT, UPDATE or DELETE and no statement transaction ** has been rolled back, update the database connection change-counter. */ - if( p->changeCntOn && p->pc>=0 ){ + if( p->changeCntOn ){ if( eStatementOp!=SAVEPOINT_ROLLBACK ){ sqlite3VdbeSetChanges(db, p->nChange); }else{ @@ -48265,8 +49431,6 @@ if( p->magic==VDBE_MAGIC_RUN || p->magic==VDBE_MAGIC_HALT ){ rc = sqlite3VdbeReset(p); assert( (rc & p->db->errMask)==rc ); - }else if( p->magic!=VDBE_MAGIC_INIT ){ - return SQLITE_MISUSE; } sqlite3VdbeDelete(p); return rc; @@ -48295,10 +49459,9 @@ ** Delete an entire VDBE. */ SQLITE_PRIVATE void sqlite3VdbeDelete(Vdbe *p){ - int i; sqlite3 *db; - if( p==0 ) return; + if( NEVER(p==0) ) return; db = p->db; if( p->pPrev ){ p->pPrev->pNext = p->pNext; @@ -48309,22 +49472,13 @@ if( p->pNext ){ p->pNext->pPrev = p->pPrev; } - if( p->aOp ){ - Op *pOp = p->aOp; - for(i=0; inOp; i++, pOp++){ - freeP4(db, pOp->p4type, pOp->p4.p); -#ifdef SQLITE_DEBUG - sqlite3DbFree(db, pOp->zComment); -#endif - } - } releaseMemArray(p->aVar, p->nVar); - sqlite3DbFree(db, p->aLabel); releaseMemArray(p->aColName, p->nResColumn*COLNAME_N); + vdbeFreeOpArray(db, p->aOp, p->nOp); + sqlite3DbFree(db, p->aLabel); sqlite3DbFree(db, p->aColName); sqlite3DbFree(db, p->zSql); p->magic = VDBE_MAGIC_DEAD; - sqlite3DbFree(db, p->aOp); sqlite3DbFree(db, p->pFree); sqlite3DbFree(db, p); } @@ -48362,7 +49516,7 @@ #endif p->deferredMoveto = 0; p->cacheStatus = CACHE_STALE; - }else if( p->pCursor ){ + }else if( ALWAYS(p->pCursor) ){ int hasMoved; int rc = sqlite3BtreeCursorHasMoved(p->pCursor, &hasMoved); if( rc ) return rc; @@ -48735,11 +49889,10 @@ idx = getVarint32(aKey, szHdr); d = szHdr; u = 0; - while( idxnField ){ + while( idxnField && d<=nKey ){ u32 serial_type; idx += getVarint32(&aKey[idx], serial_type); - if( d>=nKey && sqlite3VdbeSerialTypeLen(serial_type)>0 ) break; pMem->enc = pKeyInfo->enc; pMem->db = pKeyInfo->db; pMem->flags = 0; @@ -48763,9 +49916,12 @@ assert( p!=0 ); assert( p->flags & UNPACKED_NEED_DESTROY ); for(i=0, pMem=p->aMem; inField; i++, pMem++){ - if( pMem->zMalloc ){ - sqlite3VdbeMemRelease(pMem); - } + /* The unpacked record is always constructed by the + ** sqlite3VdbeUnpackRecord() function above, which makes all + ** strings and blobs static. And none of the elements are + ** ever transformed, so there is never anything to delete. + */ + if( NEVER(pMem->zMalloc) ) sqlite3VdbeMemRelease(pMem); } if( p->flags & UNPACKED_NEED_FREE ){ sqlite3DbFree(p->pKeyInfo->db, p); @@ -48815,9 +49971,17 @@ pKeyInfo = pPKey2->pKeyInfo; mem1.enc = pKeyInfo->enc; mem1.db = pKeyInfo->db; - mem1.flags = 0; - mem1.u.i = 0; /* not needed, here to silence compiler warning */ - mem1.zMalloc = 0; + /* mem1.flags = 0; // Will be initialized by sqlite3VdbeSerialGet() */ + VVA_ONLY( mem1.zMalloc = 0; ) /* Only needed by assert() statements */ + + /* Compilers may complain that mem1.u.i is potentially uninitialized. + ** We could initialize it, as shown here, to silence those complaints. + ** But in fact, mem1.u.i will never actually be used initialized, and doing + ** the unnecessary initialization has a measurable negative performance + ** impact, since this routine is a very high runner. And so, we choose + ** to ignore the compiler warnings and leave this variable uninitialized. + */ + /* mem1.u.i = 0; // not needed, here to silence compiler warning */ idx1 = getVarint32(aKey1, szHdr1); d1 = szHdr1; @@ -48841,45 +50005,52 @@ rc = sqlite3MemCompare(&mem1, &pPKey2->aMem[i], iaColl[i] : 0); if( rc!=0 ){ - break; + assert( mem1.zMalloc==0 ); /* See comment below */ + + /* Invert the result if we are using DESC sort order. */ + if( pKeyInfo->aSortOrder && iaSortOrder[i] ){ + rc = -rc; + } + + /* If the PREFIX_SEARCH flag is set and all fields except the final + ** rowid field were equal, then clear the PREFIX_SEARCH flag and set + ** pPKey2->rowid to the value of the rowid field in (pKey1, nKey1). + ** This is used by the OP_IsUnique opcode. + */ + if( (pPKey2->flags & UNPACKED_PREFIX_SEARCH) && i==(pPKey2->nField-1) ){ + assert( idx1==szHdr1 && rc ); + assert( mem1.flags & MEM_Int ); + pPKey2->flags &= ~UNPACKED_PREFIX_SEARCH; + pPKey2->rowid = mem1.u.i; + } + + return rc; } i++; } - if( mem1.zMalloc ) sqlite3VdbeMemRelease(&mem1); - - /* If the PREFIX_SEARCH flag is set and all fields except the final - ** rowid field were equal, then clear the PREFIX_SEARCH flag and set - ** pPKey2->rowid to the value of the rowid field in (pKey1, nKey1). - ** This is used by the OP_IsUnique opcode. - */ - if( (pPKey2->flags & UNPACKED_PREFIX_SEARCH) && i==(pPKey2->nField-1) ){ - assert( idx1==szHdr1 && rc ); - assert( mem1.flags & MEM_Int ); - pPKey2->flags &= ~UNPACKED_PREFIX_SEARCH; - pPKey2->rowid = mem1.u.i; - } - if( rc==0 ){ - /* rc==0 here means that one of the keys ran out of fields and - ** all the fields up to that point were equal. If the UNPACKED_INCRKEY - ** flag is set, then break the tie by treating key2 as larger. - ** If the UPACKED_PREFIX_MATCH flag is set, then keys with common prefixes - ** are considered to be equal. Otherwise, the longer key is the - ** larger. As it happens, the pPKey2 will always be the longer - ** if there is a difference. - */ - if( pPKey2->flags & UNPACKED_INCRKEY ){ - rc = -1; - }else if( pPKey2->flags & UNPACKED_PREFIX_MATCH ){ - /* Leave rc==0 */ - }else if( idx1aSortOrder && inField - && pKeyInfo->aSortOrder[i] ){ - rc = -rc; + /* No memory allocation is ever used on mem1. Prove this using + ** the following assert(). If the assert() fails, it indicates a + ** memory leak and a need to call sqlite3VdbeMemRelease(&mem1). + */ + assert( mem1.zMalloc==0 ); + + /* rc==0 here means that one of the keys ran out of fields and + ** all the fields up to that point were equal. If the UNPACKED_INCRKEY + ** flag is set, then break the tie by treating key2 as larger. + ** If the UPACKED_PREFIX_MATCH flag is set, then keys with common prefixes + ** are considered to be equal. Otherwise, the longer key is the + ** larger. As it happens, the pPKey2 will always be the longer + ** if there is a difference. + */ + assert( rc==0 ); + if( pPKey2->flags & UNPACKED_INCRKEY ){ + rc = -1; + }else if( pPKey2->flags & UNPACKED_PREFIX_MATCH ){ + /* Leave rc==0 */ + }else if( idx1pCursor; Mem m; - sqlite3BtreeKeySize(pCur, &nCellKey); + assert( sqlite3BtreeCursorIsValid(pCur) ); + rc = sqlite3BtreeKeySize(pCur, &nCellKey); + assert( rc==SQLITE_OK ); /* pCur is always valid so KeySize cannot fail */ + /* nCellKey will always be between 0 and 0xffffffff because of the say + ** that btreeParseCellPtr() and sqlite3GetVarint32() are implemented */ if( nCellKey<=0 || nCellKey>0x7fffffff ){ *res = 0; - return SQLITE_OK; + return SQLITE_CORRUPT; } - m.db = 0; - m.flags = 0; - m.zMalloc = 0; + memset(&m, 0, sizeof(m)); rc = sqlite3VdbeMemFromBtree(pC->pCursor, 0, (int)nCellKey, 1, &m); if( rc ){ return rc; @@ -49042,6 +50215,45 @@ return v->db; } +/* +** Return a pointer to an sqlite3_value structure containing the value bound +** parameter iVar of VM v. Except, if the value is an SQL NULL, return +** 0 instead. Unless it is NULL, apply affinity aff (one of the SQLITE_AFF_* +** constants) to the value before returning it. +** +** The returned value must be freed by the caller using sqlite3ValueFree(). +*/ +SQLITE_PRIVATE sqlite3_value *sqlite3VdbeGetValue(Vdbe *v, int iVar, u8 aff){ + assert( iVar>0 ); + if( v ){ + Mem *pMem = &v->aVar[iVar-1]; + if( 0==(pMem->flags & MEM_Null) ){ + sqlite3_value *pRet = sqlite3ValueNew(v->db); + if( pRet ){ + sqlite3VdbeMemCopy((Mem *)pRet, pMem); + sqlite3ValueApplyAffinity(pRet, aff, SQLITE_UTF8); + sqlite3VdbeMemStoreType((Mem *)pRet); + } + return pRet; + } + } + return 0; +} + +/* +** Configure SQL variable iVar so that binding a new value to it signals +** to sqlite3_reoptimize() that re-preparing the statement may result +** in a better query plan. +*/ +SQLITE_PRIVATE void sqlite3VdbeSetVarmask(Vdbe *v, int iVar){ + assert( iVar>0 ); + if( iVar>32 ){ + v->expmask = 0xffffffff; + }else{ + v->expmask |= ((u32)1 << (iVar-1)); + } +} + /************** End of vdbeaux.c *********************************************/ /************** Begin file vdbeapi.c *****************************************/ /* @@ -49058,8 +50270,6 @@ ** ** This file contains code use to implement APIs that are part of the ** VDBE. -** -** $Id: vdbeapi.c,v 1.167 2009/06/25 01:47:12 drh Exp $ */ #ifndef SQLITE_OMIT_DEPRECATED @@ -49120,7 +50330,7 @@ Vdbe *v = (Vdbe*)pStmt; sqlite3_mutex_enter(v->db->mutex); rc = sqlite3VdbeReset(v); - sqlite3VdbeMakeReady(v, -1, 0, 0, 0); + sqlite3VdbeMakeReady(v, -1, 0, 0, 0, 0, 0); assert( (rc & (v->db->errMask))==rc ); rc = sqlite3ApiExit(v->db, rc); sqlite3_mutex_leave(v->db->mutex); @@ -49143,6 +50353,9 @@ sqlite3VdbeMemRelease(&p->aVar[i]); p->aVar[i].flags = MEM_Null; } + if( p->isPrepareV2 && p->expmask ){ + p->expired = 1; + } sqlite3_mutex_leave(mutex); return rc; } @@ -49348,7 +50561,7 @@ } if( p->pc<=0 && p->expired ){ - if( ALWAYS(p->rc==SQLITE_OK) ){ + if( ALWAYS(p->rc==SQLITE_OK || p->rc==SQLITE_SCHEMA) ){ p->rc = SQLITE_SCHEMA; } rc = SQLITE_ERROR; @@ -49367,6 +50580,8 @@ db->u1.isInterrupted = 0; } + assert( db->writeVdbeCnt>0 || db->autoCommit==0 || db->nDeferredCons==0 ); + #ifndef SQLITE_OMIT_TRACE if( db->xProfile && !db->init.busy ){ double rNow; @@ -49524,8 +50739,9 @@ assert( p && p->pFunc && p->pFunc->xStep ); assert( sqlite3_mutex_held(p->s.db->mutex) ); pMem = p->pMem; + testcase( nByte<0 ); if( (pMem->flags & MEM_Agg)==0 ){ - if( nByte==0 ){ + if( nByte<=0 ){ sqlite3VdbeMemReleaseExternal(pMem); pMem->flags = MEM_Null; pMem->z = 0; @@ -49956,6 +51172,15 @@ sqlite3VdbeMemRelease(pVar); pVar->flags = MEM_Null; sqlite3Error(p->db, SQLITE_OK, 0); + + /* If the bit corresponding to this variable in Vdbe.expmask is set, then + ** binding a new value to this variable invalidates the current query plan. + */ + if( p->isPrepareV2 && + ((i<32 && p->expmask & ((u32)1 << i)) || p->expmask==0xffffffff) + ){ + p->expired = 1; + } return SQLITE_OK; } @@ -50152,8 +51377,7 @@ ** with that name. If there is no variable with the given name, ** return 0. */ -SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName){ - Vdbe *p = (Vdbe*)pStmt; +SQLITE_PRIVATE int sqlite3VdbeParameterIndex(Vdbe *p, const char *zName, int nName){ int i; if( p==0 ){ return 0; @@ -50162,13 +51386,16 @@ if( zName ){ for(i=0; inVar; i++){ const char *z = p->azVar[i]; - if( z && strcmp(z,zName)==0 ){ + if( z && memcmp(z,zName,nName)==0 && z[nName]==0 ){ return i+1; } } } return 0; } +SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt *pStmt, const char *zName){ + return sqlite3VdbeParameterIndex((Vdbe*)pStmt, zName, sqlite3Strlen30(zName)); +} /* ** Transfer all bindings from the first statement over to the second. @@ -50206,6 +51433,12 @@ if( pFrom->nVar!=pTo->nVar ){ return SQLITE_ERROR; } + if( pTo->isPrepareV2 && pTo->expmask ){ + pTo->expired = 1; + } + if( pFrom->isPrepareV2 && pFrom->expmask ){ + pFrom->expired = 1; + } return sqlite3TransferBindings(pFromStmt, pToStmt); } #endif @@ -50249,6 +51482,149 @@ } /************** End of vdbeapi.c *********************************************/ +/************** Begin file vdbetrace.c ***************************************/ +/* +** 2009 November 25 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** +** This file contains code used to insert the values of host parameters +** (aka "wildcards") into the SQL text output by sqlite3_trace(). +*/ + +#ifndef SQLITE_OMIT_TRACE + +/* +** zSql is a zero-terminated string of UTF-8 SQL text. Return the number of +** bytes in this text up to but excluding the first character in +** a host parameter. If the text contains no host parameters, return +** the total number of bytes in the text. +*/ +static int findNextHostParameter(const char *zSql, int *pnToken){ + int tokenType; + int nTotal = 0; + int n; + + *pnToken = 0; + while( zSql[0] ){ + n = sqlite3GetToken((u8*)zSql, &tokenType); + assert( n>0 && tokenType!=TK_ILLEGAL ); + if( tokenType==TK_VARIABLE ){ + *pnToken = n; + break; + } + nTotal += n; + zSql += n; + } + return nTotal; +} + +/* +** Return a pointer to a string in memory obtained form sqlite3DbMalloc() which +** holds a copy of zRawSql but with host parameters expanded to their +** current bindings. +** +** The calling function is responsible for making sure the memory returned +** is eventually freed. +** +** ALGORITHM: Scan the input string looking for host parameters in any of +** these forms: ?, ?N, $A, @A, :A. Take care to avoid text within +** string literals, quoted identifier names, and comments. For text forms, +** the host parameter index is found by scanning the perpared +** statement for the corresponding OP_Variable opcode. Once the host +** parameter index is known, locate the value in p->aVar[]. Then render +** the value as a literal in place of the host parameter name. +*/ +SQLITE_PRIVATE char *sqlite3VdbeExpandSql( + Vdbe *p, /* The prepared statement being evaluated */ + const char *zRawSql /* Raw text of the SQL statement */ +){ + sqlite3 *db; /* The database connection */ + int idx = 0; /* Index of a host parameter */ + int nextIndex = 1; /* Index of next ? host parameter */ + int n; /* Length of a token prefix */ + int nToken; /* Length of the parameter token */ + int i; /* Loop counter */ + Mem *pVar; /* Value of a host parameter */ + StrAccum out; /* Accumulate the output here */ + char zBase[100]; /* Initial working space */ + + db = p->db; + sqlite3StrAccumInit(&out, zBase, sizeof(zBase), + db->aLimit[SQLITE_LIMIT_LENGTH]); + out.db = db; + while( zRawSql[0] ){ + n = findNextHostParameter(zRawSql, &nToken); + assert( n>0 ); + sqlite3StrAccumAppend(&out, zRawSql, n); + zRawSql += n; + assert( zRawSql[0] || nToken==0 ); + if( nToken==0 ) break; + if( zRawSql[0]=='?' ){ + if( nToken>1 ){ + assert( sqlite3Isdigit(zRawSql[1]) ); + sqlite3GetInt32(&zRawSql[1], &idx); + }else{ + idx = nextIndex; + } + }else{ + assert( zRawSql[0]==':' || zRawSql[0]=='$' || zRawSql[0]=='@' ); + testcase( zRawSql[0]==':' ); + testcase( zRawSql[0]=='$' ); + testcase( zRawSql[0]=='@' ); + idx = sqlite3VdbeParameterIndex(p, zRawSql, nToken); + assert( idx>0 ); + } + zRawSql += nToken; + nextIndex = idx + 1; + assert( idx>0 && idx<=p->nVar ); + pVar = &p->aVar[idx-1]; + if( pVar->flags & MEM_Null ){ + sqlite3StrAccumAppend(&out, "NULL", 4); + }else if( pVar->flags & MEM_Int ){ + sqlite3XPrintf(&out, "%lld", pVar->u.i); + }else if( pVar->flags & MEM_Real ){ + sqlite3XPrintf(&out, "%!.15g", pVar->r); + }else if( pVar->flags & MEM_Str ){ +#ifndef SQLITE_OMIT_UTF16 + u8 enc = ENC(db); + if( enc!=SQLITE_UTF8 ){ + Mem utf8; + memset(&utf8, 0, sizeof(utf8)); + utf8.db = db; + sqlite3VdbeMemSetStr(&utf8, pVar->z, pVar->n, enc, SQLITE_STATIC); + sqlite3VdbeChangeEncoding(&utf8, SQLITE_UTF8); + sqlite3XPrintf(&out, "'%.*q'", utf8.n, utf8.z); + sqlite3VdbeMemRelease(&utf8); + }else +#endif + { + sqlite3XPrintf(&out, "'%.*q'", pVar->n, pVar->z); + } + }else if( pVar->flags & MEM_Zero ){ + sqlite3XPrintf(&out, "zeroblob(%d)", pVar->u.nZero); + }else{ + assert( pVar->flags & MEM_Blob ); + sqlite3StrAccumAppend(&out, "x'", 2); + for(i=0; in; i++){ + sqlite3XPrintf(&out, "%02x", pVar->z[i]&0xff); + } + sqlite3StrAccumAppend(&out, "'", 1); + } + } + return sqlite3StrAccumFinish(&out); +} + +#endif /* #ifndef SQLITE_OMIT_TRACE */ + +/************** End of vdbetrace.c *******************************************/ /************** Begin file vdbe.c ********************************************/ /* ** 2001 September 15 @@ -50294,8 +51670,6 @@ ** of the code in this file is, therefore, important. See other comments ** in this file for details. If in doubt, do not deviate from existing ** commenting and indentation practices when changing or adding code. -** -** $Id: vdbe.c,v 1.866 2009/06/26 16:32:13 shane Exp $ */ /* @@ -50349,6 +51723,17 @@ #endif /* +** The next global variable is incremented each type the OP_Found opcode +** is executed. This is used to test whether or not the foreign key +** operation implemented using OP_FkIsZero is working. This variable +** has no function other than to help verify the correct operation of the +** library. +*/ +#ifdef SQLITE_TEST +SQLITE_API int sqlite3_found_count = 0; +#endif + +/* ** Test a register to see if it exceeds the current maximum blob size. ** If it does, record the new maximum blob size. */ @@ -50390,12 +51775,10 @@ /* ** Argument pMem points at a register that will be passed to a ** user-defined function or returned to the user as the result of a query. -** The second argument, 'db_enc' is the text encoding used by the vdbe for -** register variables. This routine sets the pMem->enc and pMem->type -** variables used by the sqlite3_value_*() routines. +** This routine sets the pMem->type variable used by the sqlite3_value_*() +** routines. */ -#define storeTypeInfo(A,B) _storeTypeInfo(A) -static void _storeTypeInfo(Mem *pMem){ +SQLITE_PRIVATE void sqlite3VdbeMemStoreType(Mem *pMem){ int flags = pMem->flags; if( flags & MEM_Null ){ pMem->type = SQLITE_NULL; @@ -50414,23 +51797,6 @@ } /* -** Properties of opcodes. The OPFLG_INITIALIZER macro is -** created by mkopcodeh.awk during compilation. Data is obtained -** from the comments following the "case OP_xxxx:" statements in -** this file. -*/ -static const unsigned char opcodeProperty[] = OPFLG_INITIALIZER; - -/* -** Return true if an opcode has any of the OPFLG_xxx properties -** specified by mask. -*/ -SQLITE_PRIVATE int sqlite3VdbeOpcodeHasProperty(int opcode, int mask){ - assert( opcode>0 && opcode<(int)sizeof(opcodeProperty) ); - return (opcodeProperty[opcode]&mask)!=0; -} - -/* ** Allocate VdbeCursor number iCur. Return a pointer to it. Return NULL ** if we run out of memory. */ @@ -50439,7 +51805,7 @@ int iCur, /* Index of the new VdbeCursor */ int nField, /* Number of fields in the table or index */ int iDb, /* When database the cursor belongs to, or -1 */ - int isBtreeCursor /* True for B-Tree vs. pseudo-table or vtab */ + int isBtreeCursor /* True for B-Tree. False for pseudo-table or vtab */ ){ /* Find the memory cell that will be used to store the blob of memory ** required for this VdbeCursor structure. It is convenient to use a @@ -50464,7 +51830,7 @@ int nByte; VdbeCursor *pCx = 0; nByte = - sizeof(VdbeCursor) + + ROUND8(sizeof(VdbeCursor)) + (isBtreeCursor?sqlite3BtreeCursorSize():0) + 2*nField*sizeof(u32); @@ -50475,15 +51841,16 @@ } if( SQLITE_OK==sqlite3VdbeMemGrow(pMem, nByte, 0) ){ p->apCsr[iCur] = pCx = (VdbeCursor*)pMem->z; - memset(pMem->z, 0, nByte); + memset(pCx, 0, sizeof(VdbeCursor)); pCx->iDb = iDb; pCx->nField = nField; if( nField ){ - pCx->aType = (u32 *)&pMem->z[sizeof(VdbeCursor)]; + pCx->aType = (u32 *)&pMem->z[ROUND8(sizeof(VdbeCursor))]; } if( isBtreeCursor ){ pCx->pCursor = (BtCursor*) - &pMem->z[sizeof(VdbeCursor)+2*nField*sizeof(u32)]; + &pMem->z[ROUND8(sizeof(VdbeCursor))+2*nField*sizeof(u32)]; + sqlite3BtreeCursorZero(pCx->pCursor); } } return pCx; @@ -50566,7 +51933,7 @@ SQLITE_API int sqlite3_value_numeric_type(sqlite3_value *pVal){ Mem *pMem = (Mem*)pVal; applyNumericAffinity(pMem); - storeTypeInfo(pMem, 0); + sqlite3VdbeMemStoreType(pMem); return pMem->type; } @@ -50725,8 +52092,6 @@ ** ** This file contains inline asm code for retrieving "high-performance" ** counters for x86 class CPUs. -** -** $Id: hwtime.h,v 1.3 2008/08/01 14:33:15 shane Exp $ */ #ifndef _HWTIME_H_ #define _HWTIME_H_ @@ -50889,24 +52254,27 @@ Vdbe *p /* The VDBE */ ){ int pc; /* The program counter */ + Op *aOp = p->aOp; /* Copy of p->aOp */ Op *pOp; /* Current operation */ int rc = SQLITE_OK; /* Value to return */ sqlite3 *db = p->db; /* The database */ + u8 resetSchemaOnFault = 0; /* Reset schema after an error if true */ u8 encoding = ENC(db); /* The database encoding */ +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + int checkProgress; /* True if progress callbacks are enabled */ + int nProgressOps = 0; /* Opcodes executed since progress callback. */ +#endif + Mem *aMem = p->aMem; /* Copy of p->aMem */ Mem *pIn1 = 0; /* 1st input operand */ Mem *pIn2 = 0; /* 2nd input operand */ Mem *pIn3 = 0; /* 3rd input operand */ Mem *pOut = 0; /* Output operand */ - u8 opProperty; int iCompare = 0; /* Result of last OP_Compare operation */ int *aPermute = 0; /* Permutation of columns for OP_Compare */ #ifdef VDBE_PROFILE u64 start; /* CPU clock count at start of opcode */ int origPc; /* Program counter at start of opcode */ #endif -#ifndef SQLITE_OMIT_PROGRESS_CALLBACK - int nProgressOps = 0; /* Opcodes executed since progress callback. */ -#endif /******************************************************************** ** Automatically generated code ** @@ -50957,9 +52325,8 @@ i64 b; } ah; struct OP_Ge_stack_vars { - int flags; - int res; - char affinity; + int res; /* Result of the comparison of pIn1 against pIn3 */ + char affinity; /* Affinity to use for comparison */ } ai; struct OP_Compare_stack_vars { int n; @@ -51000,12 +52367,11 @@ u64 offset64; /* 64-bit offset. 64 bits needed to catch overflow */ int szHdr; /* Size of the header size field at start of record */ int avail; /* Number of bytes of available data */ + Mem *pReg; /* PseudoTable input register */ } am; struct OP_Affinity_stack_vars { - char *zAffinity; /* The affinity to be applied */ - Mem *pData0; /* First register to which to apply affinity */ - Mem *pLast; /* Last register to which to apply affinity */ - Mem *pRec; /* Current register */ + const char *zAffinity; /* The affinity to be applied */ + char cAff; /* A single character of affinity */ } an; struct OP_MakeRecord_stack_vars { u8 *zNewRecord; /* A buffer to hold the data for the new record */ @@ -51028,9 +52394,6 @@ i64 nEntry; BtCursor *pCrsr; } ap; - struct OP_Statement_stack_vars { - Btree *pBt; - } aq; struct OP_Savepoint_stack_vars { int p1; /* Value of P1 operand */ char *zName; /* Name of savepoint */ @@ -51040,27 +52403,27 @@ Savepoint *pTmp; int iSavepoint; int ii; - } ar; + } aq; struct OP_AutoCommit_stack_vars { int desiredAutoCommit; int iRollback; int turnOnAC; - } as; + } ar; struct OP_Transaction_stack_vars { Btree *pBt; - } at; + } as; struct OP_ReadCookie_stack_vars { int iMeta; int iDb; int iCookie; - } au; + } at; struct OP_SetCookie_stack_vars { Db *pDb; - } av; + } au; struct OP_VerifyCookie_stack_vars { int iMeta; Btree *pBt; - } aw; + } av; struct OP_OpenWrite_stack_vars { int nField; KeyInfo *pKeyInfo; @@ -51070,14 +52433,13 @@ Btree *pX; VdbeCursor *pCur; Db *pDb; - int flags; - } ax; + } aw; struct OP_OpenEphemeral_stack_vars { VdbeCursor *pCx; - } ay; + } ax; struct OP_OpenPseudo_stack_vars { VdbeCursor *pCx; - } az; + } ay; struct OP_SeekGt_stack_vars { int res; int oc; @@ -51085,126 +52447,128 @@ UnpackedRecord r; int nField; i64 iKey; /* The rowid we are to seek to */ - } ba; + } az; struct OP_Seek_stack_vars { VdbeCursor *pC; - } bb; + } ba; struct OP_Found_stack_vars { int alreadyExists; VdbeCursor *pC; int res; UnpackedRecord *pIdxKey; + UnpackedRecord r; char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7]; - } bc; + } bb; struct OP_IsUnique_stack_vars { u16 ii; VdbeCursor *pCx; BtCursor *pCrsr; u16 nField; - Mem *aMem; + Mem *aMx; UnpackedRecord r; /* B-Tree index search key */ i64 R; /* Rowid stored in register P3 */ - } bd; + } bc; struct OP_NotExists_stack_vars { VdbeCursor *pC; BtCursor *pCrsr; int res; u64 iKey; - } be; + } bd; struct OP_NewRowid_stack_vars { i64 v; /* The new rowid */ VdbeCursor *pC; /* Cursor of table to get the new rowid */ int res; /* Result of an sqlite3BtreeLast() */ int cnt; /* Counter to limit the number of searches */ Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */ + VdbeFrame *pFrame; /* Root frame of VDBE */ + } be; + struct OP_InsertInt_stack_vars { + Mem *pData; /* MEM cell holding data for the record to be inserted */ + Mem *pKey; /* MEM cell holding key for the record */ + i64 iKey; /* The integer ROWID or key for the record to be inserted */ + VdbeCursor *pC; /* Cursor to table into which insert is written */ + int nZero; /* Number of zero-bytes to append */ + int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */ + const char *zDb; /* database name - used by the update hook */ + const char *zTbl; /* Table name - used by the opdate hook */ + int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */ } bf; - struct OP_Insert_stack_vars { - Mem *pData; - Mem *pKey; - i64 iKey; /* The integer ROWID or key for the record to be inserted */ - VdbeCursor *pC; - int nZero; - int seekResult; - const char *zDb; - const char *zTbl; - int op; - } bg; struct OP_Delete_stack_vars { i64 iKey; VdbeCursor *pC; - } bh; + } bg; struct OP_RowData_stack_vars { VdbeCursor *pC; BtCursor *pCrsr; u32 n; i64 n64; - } bi; + } bh; struct OP_Rowid_stack_vars { VdbeCursor *pC; i64 v; sqlite3_vtab *pVtab; const sqlite3_module *pModule; - } bj; + } bi; struct OP_NullRow_stack_vars { VdbeCursor *pC; - } bk; + } bj; struct OP_Last_stack_vars { VdbeCursor *pC; BtCursor *pCrsr; int res; - } bl; + } bk; struct OP_Rewind_stack_vars { VdbeCursor *pC; BtCursor *pCrsr; int res; - } bm; + } bl; struct OP_Next_stack_vars { VdbeCursor *pC; BtCursor *pCrsr; int res; - } bn; + } bm; struct OP_IdxInsert_stack_vars { VdbeCursor *pC; BtCursor *pCrsr; int nKey; const char *zKey; - } bo; + } bn; struct OP_IdxDelete_stack_vars { VdbeCursor *pC; BtCursor *pCrsr; int res; UnpackedRecord r; - } bp; + } bo; struct OP_IdxRowid_stack_vars { BtCursor *pCrsr; VdbeCursor *pC; i64 rowid; - } bq; + } bp; struct OP_IdxGE_stack_vars { VdbeCursor *pC; int res; UnpackedRecord r; - } br; + } bq; struct OP_Destroy_stack_vars { int iMoved; int iCnt; Vdbe *pVdbe; int iDb; - } bs; + } br; struct OP_Clear_stack_vars { int nChange; - } bt; + } bs; struct OP_CreateTable_stack_vars { int pgno; int flags; Db *pDb; - } bu; + } bt; struct OP_ParseSchema_stack_vars { int iDb; const char *zMaster; char *zSql; InitData initData; - } bv; + } bu; struct OP_IntegrityCk_stack_vars { int nRoot; /* Number of tables to check. (Number of root pages.) */ int *aRoot; /* Array of rootpage numbers for tables to be checked */ @@ -51212,26 +52576,32 @@ int nErr; /* Number of errors reported */ char *z; /* Text of the error report */ Mem *pnErr; /* Register keeping track of errors remaining */ - } bw; - struct OP_RowSetAdd_stack_vars { - Mem *pIdx; - Mem *pVal; - } bx; + } bv; struct OP_RowSetRead_stack_vars { - Mem *pIdx; i64 val; - } by; + } bw; struct OP_RowSetTest_stack_vars { int iSet; int exists; + } bx; + struct OP_Program_stack_vars { + int nMem; /* Number of memory registers for sub-program */ + int nByte; /* Bytes of runtime space required for sub-program */ + Mem *pRt; /* Register to allocate runtime space */ + Mem *pMem; /* Used to iterate through memory cells */ + Mem *pEnd; /* Last memory cell in new array */ + VdbeFrame *pFrame; /* New vdbe frame to execute in */ + SubProgram *pProgram; /* Sub-program to execute */ + void *t; /* Token identifying trigger */ + } by; + struct OP_Param_stack_vars { + VdbeFrame *pFrame; + Mem *pIn; } bz; - struct OP_ContextPush_stack_vars { - int i; - Context *pContext; + struct OP_MemMax_stack_vars { + Mem *pIn1; + VdbeFrame *pFrame; } ca; - struct OP_ContextPop_stack_vars { - Context *pContext; - } cb; struct OP_AggStep_stack_vars { int n; int i; @@ -51239,26 +52609,22 @@ Mem *pRec; sqlite3_context ctx; sqlite3_value **apVal; - } cc; + } cb; struct OP_AggFinal_stack_vars { Mem *pMem; - } cd; + } cc; struct OP_IncrVacuum_stack_vars { Btree *pBt; - } ce; - struct OP_TableLock_stack_vars { - int p1; - u8 isWriteLock; - } cf; + } cd; struct OP_VBegin_stack_vars { - sqlite3_vtab *pVtab; - } cg; + VTable *pVTab; + } ce; struct OP_VOpen_stack_vars { VdbeCursor *pCur; sqlite3_vtab_cursor *pVtabCursor; sqlite3_vtab *pVtab; sqlite3_module *pModule; - } ch; + } cf; struct OP_VFilter_stack_vars { int nArg; int iQuery; @@ -51271,23 +52637,23 @@ int res; int i; Mem **apArg; - } ci; + } cg; struct OP_VColumn_stack_vars { sqlite3_vtab *pVtab; const sqlite3_module *pModule; Mem *pDest; sqlite3_context sContext; - } cj; + } ch; struct OP_VNext_stack_vars { sqlite3_vtab *pVtab; const sqlite3_module *pModule; int res; VdbeCursor *pCur; - } ck; + } ci; struct OP_VRename_stack_vars { sqlite3_vtab *pVtab; Mem *pName; - } cl; + } cj; struct OP_VUpdate_stack_vars { sqlite3_vtab *pVtab; sqlite3_module *pModule; @@ -51296,15 +52662,15 @@ sqlite_int64 rowid; Mem **apArg; Mem *pX; - } cm; + } ck; struct OP_Pagecount_stack_vars { int p1; int nPage; Pager *pPager; - } cn; + } cl; struct OP_Trace_stack_vars { char *zTrace; - } co; + } cm; } u; /* End automatically generated code ********************************************************************/ @@ -51324,6 +52690,9 @@ db->busyHandler.nBusy = 0; CHECK_FOR_INTERRUPT; sqlite3VdbeIOTraceSql(p); +#ifndef SQLITE_OMIT_PROGRESS_CALLBACK + checkProgress = db->xProgress!=0; +#endif #ifdef SQLITE_DEBUG sqlite3BeginBenignMalloc(); if( p->pc==0 @@ -51333,7 +52702,7 @@ printf("VDBE Program Listing:\n"); sqlite3VdbePrintSql(p); for(i=0; inOp; i++){ - sqlite3VdbePrintOp(stdout, i, &p->aOp[i]); + sqlite3VdbePrintOp(stdout, i, &aOp[i]); } } if( fileExists(db, "vdbe_trace") ){ @@ -51348,7 +52717,7 @@ origPc = pc; start = sqlite3Hwtime(); #endif - pOp = &p->aOp[pc]; + pOp = &aOp[pc]; /* Only allow tracing if SQLITE_DEBUG is defined. */ @@ -51389,7 +52758,7 @@ ** If the progress callback returns non-zero, exit the virtual machine with ** a return code SQLITE_ABORT. */ - if( db->xProgress ){ + if( checkProgress ){ if( db->nProgressOps==nProgressOps ){ int prc; if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; @@ -51405,65 +52774,47 @@ } #endif - /* Do common setup processing for any opcode that is marked - ** with the "out2-prerelease" tag. Such opcodes have a single - ** output which is specified by the P2 parameter. The P2 register - ** is initialized to a NULL. + /* On any opcode with the "out2-prerelase" tag, free any + ** external allocations out of mem[p2] and set mem[p2] to be + ** an undefined integer. Opcodes will either fill in the integer + ** value or convert mem[p2] to a different type. */ - opProperty = opcodeProperty[pOp->opcode]; - if( (opProperty & OPFLG_OUT2_PRERELEASE)!=0 ){ + assert( pOp->opflags==sqlite3OpcodeProperty[pOp->opcode] ); + if( pOp->opflags & OPFLG_OUT2_PRERELEASE ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); - pOut = &p->aMem[pOp->p2]; + pOut = &aMem[pOp->p2]; sqlite3VdbeMemReleaseExternal(pOut); - pOut->flags = MEM_Null; - pOut->n = 0; - }else - - /* Do common setup for opcodes marked with one of the following - ** combinations of properties. - ** - ** in1 - ** in1 in2 - ** in1 in2 out3 - ** in1 in3 - ** - ** Variables pIn1, pIn2, and pIn3 are made to point to appropriate - ** registers for inputs. Variable pOut points to the output register. - */ - if( (opProperty & OPFLG_IN1)!=0 ){ + pOut->flags = MEM_Int; + } + + /* Sanity checking on other operands */ +#ifdef SQLITE_DEBUG + if( (pOp->opflags & OPFLG_IN1)!=0 ){ assert( pOp->p1>0 ); assert( pOp->p1<=p->nMem ); - pIn1 = &p->aMem[pOp->p1]; - REGISTER_TRACE(pOp->p1, pIn1); - if( (opProperty & OPFLG_IN2)!=0 ){ - assert( pOp->p2>0 ); - assert( pOp->p2<=p->nMem ); - pIn2 = &p->aMem[pOp->p2]; - REGISTER_TRACE(pOp->p2, pIn2); - if( (opProperty & OPFLG_OUT3)!=0 ){ - assert( pOp->p3>0 ); - assert( pOp->p3<=p->nMem ); - pOut = &p->aMem[pOp->p3]; - } - }else if( (opProperty & OPFLG_IN3)!=0 ){ - assert( pOp->p3>0 ); - assert( pOp->p3<=p->nMem ); - pIn3 = &p->aMem[pOp->p3]; - REGISTER_TRACE(pOp->p3, pIn3); - } - }else if( (opProperty & OPFLG_IN2)!=0 ){ + REGISTER_TRACE(pOp->p1, &aMem[pOp->p1]); + } + if( (pOp->opflags & OPFLG_IN2)!=0 ){ + assert( pOp->p2>0 ); + assert( pOp->p2<=p->nMem ); + REGISTER_TRACE(pOp->p2, &aMem[pOp->p2]); + } + if( (pOp->opflags & OPFLG_IN3)!=0 ){ + assert( pOp->p3>0 ); + assert( pOp->p3<=p->nMem ); + REGISTER_TRACE(pOp->p3, &aMem[pOp->p3]); + } + if( (pOp->opflags & OPFLG_OUT2)!=0 ){ assert( pOp->p2>0 ); assert( pOp->p2<=p->nMem ); - pIn2 = &p->aMem[pOp->p2]; - REGISTER_TRACE(pOp->p2, pIn2); - }else if( (opProperty & OPFLG_IN3)!=0 ){ + } + if( (pOp->opflags & OPFLG_OUT3)!=0 ){ assert( pOp->p3>0 ); assert( pOp->p3<=p->nMem ); - pIn3 = &p->aMem[pOp->p3]; - REGISTER_TRACE(pOp->p3, pIn3); } - +#endif + switch( pOp->opcode ){ /***************************************************************************** @@ -51519,10 +52870,8 @@ ** Write the current address onto register P1 ** and then jump to address P2. */ -case OP_Gosub: { /* jump */ - assert( pOp->p1>0 ); - assert( pOp->p1<=p->nMem ); - pIn1 = &p->aMem[pOp->p1]; +case OP_Gosub: { /* jump, in1 */ + pIn1 = &aMem[pOp->p1]; assert( (pIn1->flags & MEM_Dyn)==0 ); pIn1->flags = MEM_Int; pIn1->u.i = pc; @@ -51536,6 +52885,7 @@ ** Jump to the next instruction after the address in register P1. */ case OP_Return: { /* in1 */ + pIn1 = &aMem[pOp->p1]; assert( pIn1->flags & MEM_Int ); pc = (int)pIn1->u.i; break; @@ -51549,6 +52899,7 @@ #if 0 /* local variables moved into u.aa */ int pcDest; #endif /* local variables moved into u.aa */ + pIn1 = &aMem[pOp->p1]; assert( (pIn1->flags & MEM_Dyn)==0 ); pIn1->flags = MEM_Int; u.aa.pcDest = (int)pIn1->u.i; @@ -51565,6 +52916,7 @@ ** value in register P3 is not NULL, then this routine is a no-op. */ case OP_HaltIfNull: { /* in3 */ + pIn3 = &aMem[pOp->p3]; if( (pIn3->flags & MEM_Null)==0 ) break; /* Fall through into OP_Halt */ } @@ -51589,17 +52941,39 @@ ** is the same as executing Halt. */ case OP_Halt: { + if( pOp->p1==SQLITE_OK && p->pFrame ){ + /* Halt the sub-program. Return control to the parent frame. */ + VdbeFrame *pFrame = p->pFrame; + p->pFrame = pFrame->pParent; + p->nFrame--; + sqlite3VdbeSetChanges(db, p->nChange); + pc = sqlite3VdbeFrameRestore(pFrame); + if( pOp->p2==OE_Ignore ){ + /* Instruction pc is the OP_Program that invoked the sub-program + ** currently being halted. If the p2 instruction of this OP_Halt + ** instruction is set to OE_Ignore, then the sub-program is throwing + ** an IGNORE exception. In this case jump to the address specified + ** as the p2 of the calling OP_Program. */ + pc = p->aOp[pc].p2-1; + } + aOp = p->aOp; + aMem = p->aMem; + break; + } + p->rc = pOp->p1; - p->pc = pc; p->errorAction = (u8)pOp->p2; + p->pc = pc; if( pOp->p4.z ){ sqlite3SetString(&p->zErrMsg, db, "%s", pOp->p4.z); } rc = sqlite3VdbeHalt(p); - assert( rc==SQLITE_BUSY || rc==SQLITE_OK ); + assert( rc==SQLITE_BUSY || rc==SQLITE_OK || rc==SQLITE_ERROR ); if( rc==SQLITE_BUSY ){ p->rc = rc = SQLITE_BUSY; }else{ + assert( rc==SQLITE_OK || p->rc==SQLITE_CONSTRAINT ); + assert( rc==SQLITE_OK || db->nDeferredCons>0 ); rc = p->rc ? SQLITE_ERROR : SQLITE_DONE; } goto vdbe_return; @@ -51610,7 +52984,6 @@ ** The 32-bit integer value P1 is written into register P2. */ case OP_Integer: { /* out2-prerelease */ - pOut->flags = MEM_Int; pOut->u.i = pOp->p1; break; } @@ -51622,7 +52995,6 @@ */ case OP_Int64: { /* out2-prerelease */ assert( pOp->p4.pI64!=0 ); - pOut->flags = MEM_Int; pOut->u.i = *pOp->p4.pI64; break; } @@ -51692,6 +53064,7 @@ ** Write a NULL into register P2. */ case OP_Null: { /* out2-prerelease */ + pOut->flags = MEM_Null; break; } @@ -51734,14 +53107,14 @@ u.ab.n = pOp->p3; assert( u.ab.p1>=0 && u.ab.p1+u.ab.n<=p->nVar ); assert( u.ab.p2>=1 && u.ab.p2+u.ab.n-1<=p->nMem ); - assert( pOp->p4.z==0 || pOp->p3==1 ); + assert( pOp->p4.z==0 || pOp->p3==1 || pOp->p3==0 ); while( u.ab.n-- > 0 ){ u.ab.pVar = &p->aVar[u.ab.p1++]; if( sqlite3VdbeMemTooBig(u.ab.pVar) ){ goto too_big; } - pOut = &p->aMem[u.ab.p2++]; + pOut = &aMem[u.ab.p2++]; sqlite3VdbeMemReleaseExternal(pOut); pOut->flags = MEM_Null; sqlite3VdbeMemShallowCopy(pOut, u.ab.pVar, MEM_Static); @@ -51771,11 +53144,11 @@ assert( u.ac.n>0 && u.ac.p1>0 && u.ac.p2>0 ); assert( u.ac.p1+u.ac.n<=u.ac.p2 || u.ac.p2+u.ac.n<=u.ac.p1 ); - pIn1 = &p->aMem[u.ac.p1]; - pOut = &p->aMem[u.ac.p2]; + pIn1 = &aMem[u.ac.p1]; + pOut = &aMem[u.ac.p2]; while( u.ac.n-- ){ - assert( pOut<=&p->aMem[p->nMem] ); - assert( pIn1<=&p->aMem[p->nMem] ); + assert( pOut<=&aMem[p->nMem] ); + assert( pIn1<=&aMem[p->nMem] ); u.ac.zMalloc = pOut->zMalloc; pOut->zMalloc = 0; sqlite3VdbeMemMove(pOut, pIn1); @@ -51794,10 +53167,9 @@ ** This instruction makes a deep copy of the value. A duplicate ** is made of any string or blob constant. See also OP_SCopy. */ -case OP_Copy: { /* in1 */ - assert( pOp->p2>0 ); - assert( pOp->p2<=p->nMem ); - pOut = &p->aMem[pOp->p2]; +case OP_Copy: { /* in1, out2 */ + pIn1 = &aMem[pOp->p1]; + pOut = &aMem[pOp->p2]; assert( pOut!=pIn1 ); sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); Deephemeralize(pOut); @@ -51817,11 +53189,9 @@ ** during the lifetime of the copy. Use OP_Copy to make a complete ** copy. */ -case OP_SCopy: { /* in1 */ - REGISTER_TRACE(pOp->p1, pIn1); - assert( pOp->p2>0 ); - assert( pOp->p2<=p->nMem ); - pOut = &p->aMem[pOp->p2]; +case OP_SCopy: { /* in1, out2 */ + pIn1 = &aMem[pOp->p1]; + pOut = &aMem[pOp->p2]; assert( pOut!=pIn1 ); sqlite3VdbeMemShallowCopy(pOut, pIn1, MEM_Ephem); REGISTER_TRACE(pOp->p2, pOut); @@ -51845,6 +53215,15 @@ assert( pOp->p1>0 ); assert( pOp->p1+pOp->p2<=p->nMem+1 ); + /* If this statement has violated immediate foreign key constraints, do + ** not return the number of rows modified. And do not RELEASE the statement + ** transaction. It needs to be rolled back. */ + if( SQLITE_OK!=(rc = sqlite3VdbeCheckFk(p, 0)) ){ + assert( db->flags&SQLITE_CountRows ); + assert( p->usesStmtJournal ); + break; + } + /* If the SQLITE_CountRows flag is set in sqlite3.flags mask, then ** DML statements invoke this opcode to return the number of rows ** modified to the user. This is the only way that a VM that @@ -51873,10 +53252,10 @@ ** and have an assigned type. The results are de-ephemeralized as ** as side effect. */ - u.ad.pMem = p->pResultSet = &p->aMem[pOp->p1]; + u.ad.pMem = p->pResultSet = &aMem[pOp->p1]; for(u.ad.i=0; u.ad.ip2; u.ad.i++){ sqlite3VdbeMemNulTerminate(&u.ad.pMem[u.ad.i]); - storeTypeInfo(&u.ad.pMem[u.ad.i], encoding); + sqlite3VdbeMemStoreType(&u.ad.pMem[u.ad.i]); REGISTER_TRACE(pOp->p1+u.ad.i, &u.ad.pMem[u.ad.i]); } if( db->mallocFailed ) goto no_mem; @@ -51905,6 +53284,9 @@ i64 nByte; #endif /* local variables moved into u.ae */ + pIn1 = &aMem[pOp->p1]; + pIn2 = &aMem[pOp->p2]; + pOut = &aMem[pOp->p3]; assert( pIn1!=pOut ); if( (pIn1->flags | pIn2->flags) & MEM_Null ){ sqlite3VdbeMemSetNull(pOut); @@ -51956,9 +53338,9 @@ /* Opcode: Divide P1 P2 P3 * * ** ** Divide the value in register P1 by the value in register P2 -** and store the result in register P3. If the value in register P2 -** is zero, then the result is NULL. -** If either input is NULL, the result is NULL. +** and store the result in register P3 (P3=P2/P1). If the value in +** register P1 is zero, then the result is NULL. If either input is +** NULL, the result is NULL. */ /* Opcode: Remainder P1 P2 P3 * * ** @@ -51980,8 +53362,11 @@ double rB; /* Real value of right operand */ #endif /* local variables moved into u.af */ + pIn1 = &aMem[pOp->p1]; applyNumericAffinity(pIn1); + pIn2 = &aMem[pOp->p2]; applyNumericAffinity(pIn2); + pOut = &aMem[pOp->p3]; u.af.flags = pIn1->flags | pIn2->flags; if( (u.af.flags & MEM_Null)!=0 ) goto arithmetic_result_is_null; if( (pIn1->flags & pIn2->flags & MEM_Int)==MEM_Int ){ @@ -52098,10 +53483,10 @@ assert( u.ag.n==0 || (pOp->p2>0 && pOp->p2+u.ag.n<=p->nMem+1) ); assert( pOp->p3p2 || pOp->p3>=pOp->p2+u.ag.n ); - u.ag.pArg = &p->aMem[pOp->p2]; + u.ag.pArg = &aMem[pOp->p2]; for(u.ag.i=0; u.ag.ip2, u.ag.pArg); } @@ -52115,7 +53500,7 @@ } assert( pOp->p3>0 && pOp->p3<=p->nMem ); - pOut = &p->aMem[pOp->p3]; + pOut = &aMem[pOp->p3]; u.ag.ctx.s.flags = MEM_Null; u.ag.ctx.s.db = db; u.ag.ctx.s.xDel = 0; @@ -52130,7 +53515,7 @@ u.ag.ctx.isError = 0; if( u.ag.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ - assert( pOp>p->aOp ); + assert( pOp>aOp ); assert( pOp[-1].p4type==P4_COLLSEQ ); assert( pOp[-1].opcode==OP_CollSeq ); u.ag.ctx.pColl = pOp[-1].p4.pColl; @@ -52216,6 +53601,9 @@ i64 b; #endif /* local variables moved into u.ah */ + pIn1 = &aMem[pOp->p1]; + pIn2 = &aMem[pOp->p2]; + pOut = &aMem[pOp->p3]; if( (pIn1->flags | pIn2->flags) & MEM_Null ){ sqlite3VdbeMemSetNull(pOut); break; @@ -52242,6 +53630,7 @@ ** To force any register to be an integer, just add 0. */ case OP_AddImm: { /* in1 */ + pIn1 = &aMem[pOp->p1]; sqlite3VdbeMemIntegerify(pIn1); pIn1->u.i += pOp->p2; break; @@ -52255,6 +53644,7 @@ ** raise an SQLITE_MISMATCH exception. */ case OP_MustBeInt: { /* jump, in1 */ + pIn1 = &aMem[pOp->p1]; applyAffinity(pIn1, SQLITE_AFF_NUMERIC, encoding); if( (pIn1->flags & MEM_Int)==0 ){ if( pOp->p2==0 ){ @@ -52279,6 +53669,7 @@ ** to have only a real value. */ case OP_RealAffinity: { /* in1 */ + pIn1 = &aMem[pOp->p1]; if( pIn1->flags & MEM_Int ){ sqlite3VdbeMemRealify(pIn1); } @@ -52296,6 +53687,7 @@ ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToText: { /* same as TK_TO_TEXT, in1 */ + pIn1 = &aMem[pOp->p1]; if( pIn1->flags & MEM_Null ) break; assert( MEM_Str==(MEM_Blob>>3) ); pIn1->flags |= (pIn1->flags&MEM_Blob)>>3; @@ -52317,6 +53709,7 @@ ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToBlob: { /* same as TK_TO_BLOB, in1 */ + pIn1 = &aMem[pOp->p1]; if( pIn1->flags & MEM_Null ) break; if( (pIn1->flags & MEM_Blob)==0 ){ applyAffinity(pIn1, SQLITE_AFF_TEXT, encoding); @@ -52340,6 +53733,7 @@ ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToNumeric: { /* same as TK_TO_NUMERIC, in1 */ + pIn1 = &aMem[pOp->p1]; if( (pIn1->flags & (MEM_Null|MEM_Int|MEM_Real))==0 ){ sqlite3VdbeMemNumerify(pIn1); } @@ -52357,6 +53751,7 @@ ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToInt: { /* same as TK_TO_INT, in1 */ + pIn1 = &aMem[pOp->p1]; if( (pIn1->flags & MEM_Null)==0 ){ sqlite3VdbeMemIntegerify(pIn1); } @@ -52374,6 +53769,7 @@ ** A NULL value is not changed by this routine. It remains NULL. */ case OP_ToReal: { /* same as TK_TO_REAL, in1 */ + pIn1 = &aMem[pOp->p1]; if( (pIn1->flags & MEM_Null)==0 ){ sqlite3VdbeMemRealify(pIn1); } @@ -52416,12 +53812,24 @@ ** This works just like the Lt opcode except that the jump is taken if ** the operands in registers P1 and P3 are not equal. See the Lt opcode for ** additional information. +** +** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either +** true or false and is never NULL. If both operands are NULL then the result +** of comparison is false. If either operand is NULL then the result is true. +** If neither operand is NULL the the result is the same as it would be if +** the SQLITE_NULLEQ flag were omitted from P5. */ /* Opcode: Eq P1 P2 P3 P4 P5 ** ** This works just like the Lt opcode except that the jump is taken if ** the operands in registers P1 and P3 are equal. ** See the Lt opcode for additional information. +** +** If SQLITE_NULLEQ is set in P5 then the result of comparison is always either +** true or false and is never NULL. If both operands are NULL then the result +** of comparison is true. If either operand is NULL then the result is false. +** If neither operand is NULL the the result is the same as it would be if +** the SQLITE_NULLEQ flag were omitted from P5. */ /* Opcode: Le P1 P2 P3 P4 P5 ** @@ -52448,38 +53856,49 @@ case OP_Gt: /* same as TK_GT, jump, in1, in3 */ case OP_Ge: { /* same as TK_GE, jump, in1, in3 */ #if 0 /* local variables moved into u.ai */ - int flags; - int res; - char affinity; + int res; /* Result of the comparison of pIn1 against pIn3 */ + char affinity; /* Affinity to use for comparison */ #endif /* local variables moved into u.ai */ - u.ai.flags = pIn1->flags|pIn3->flags; - - if( u.ai.flags&MEM_Null ){ - /* If either operand is NULL then the result is always NULL. - ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. - */ - if( pOp->p5 & SQLITE_STOREP2 ){ - pOut = &p->aMem[pOp->p2]; - MemSetTypeFlag(pOut, MEM_Null); - REGISTER_TRACE(pOp->p2, pOut); - }else if( pOp->p5 & SQLITE_JUMPIFNULL ){ - pc = pOp->p2-1; + pIn1 = &aMem[pOp->p1]; + pIn3 = &aMem[pOp->p3]; + if( (pIn1->flags | pIn3->flags)&MEM_Null ){ + /* One or both operands are NULL */ + if( pOp->p5 & SQLITE_NULLEQ ){ + /* If SQLITE_NULLEQ is set (which will only happen if the operator is + ** OP_Eq or OP_Ne) then take the jump or not depending on whether + ** or not both operands are null. + */ + assert( pOp->opcode==OP_Eq || pOp->opcode==OP_Ne ); + u.ai.res = (pIn1->flags & pIn3->flags & MEM_Null)==0; + }else{ + /* SQLITE_NULLEQ is clear and at least one operand is NULL, + ** then the result is always NULL. + ** The jump is taken if the SQLITE_JUMPIFNULL bit is set. + */ + if( pOp->p5 & SQLITE_STOREP2 ){ + pOut = &aMem[pOp->p2]; + MemSetTypeFlag(pOut, MEM_Null); + REGISTER_TRACE(pOp->p2, pOut); + }else if( pOp->p5 & SQLITE_JUMPIFNULL ){ + pc = pOp->p2-1; + } + break; + } + }else{ + /* Neither operand is NULL. Do a comparison. */ + u.ai.affinity = pOp->p5 & SQLITE_AFF_MASK; + if( u.ai.affinity ){ + applyAffinity(pIn1, u.ai.affinity, encoding); + applyAffinity(pIn3, u.ai.affinity, encoding); + if( db->mallocFailed ) goto no_mem; } - break; - } - u.ai.affinity = pOp->p5 & SQLITE_AFF_MASK; - if( u.ai.affinity ){ - applyAffinity(pIn1, u.ai.affinity, encoding); - applyAffinity(pIn3, u.ai.affinity, encoding); - if( db->mallocFailed ) goto no_mem; + assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); + ExpandBlob(pIn1); + ExpandBlob(pIn3); + u.ai.res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); } - - assert( pOp->p4type==P4_COLLSEQ || pOp->p4.pColl==0 ); - ExpandBlob(pIn1); - ExpandBlob(pIn3); - u.ai.res = sqlite3MemCompare(pIn3, pIn1, pOp->p4.pColl); switch( pOp->opcode ){ case OP_Eq: u.ai.res = u.ai.res==0; break; case OP_Ne: u.ai.res = u.ai.res!=0; break; @@ -52490,7 +53909,7 @@ } if( pOp->p5 & SQLITE_STOREP2 ){ - pOut = &p->aMem[pOp->p2]; + pOut = &aMem[pOp->p2]; MemSetTypeFlag(pOut, MEM_Int); pOut->u.i = u.ai.res; REGISTER_TRACE(pOp->p2, pOut); @@ -52547,17 +53966,26 @@ assert( u.aj.n>0 ); assert( u.aj.pKeyInfo!=0 ); u.aj.p1 = pOp->p1; - assert( u.aj.p1>0 && u.aj.p1+u.aj.n<=p->nMem+1 ); u.aj.p2 = pOp->p2; - assert( u.aj.p2>0 && u.aj.p2+u.aj.n<=p->nMem+1 ); +#if SQLITE_DEBUG + if( aPermute ){ + int k, mx = 0; + for(k=0; kmx ) mx = aPermute[k]; + assert( u.aj.p1>0 && u.aj.p1+mx<=p->nMem+1 ); + assert( u.aj.p2>0 && u.aj.p2+mx<=p->nMem+1 ); + }else{ + assert( u.aj.p1>0 && u.aj.p1+u.aj.n<=p->nMem+1 ); + assert( u.aj.p2>0 && u.aj.p2+u.aj.n<=p->nMem+1 ); + } +#endif /* SQLITE_DEBUG */ for(u.aj.i=0; u.aj.iaMem[u.aj.p1+u.aj.idx]); - REGISTER_TRACE(u.aj.p2+u.aj.idx, &p->aMem[u.aj.p2+u.aj.idx]); + REGISTER_TRACE(u.aj.p1+u.aj.idx, &aMem[u.aj.p1+u.aj.idx]); + REGISTER_TRACE(u.aj.p2+u.aj.idx, &aMem[u.aj.p2+u.aj.idx]); assert( u.aj.inField ); u.aj.pColl = u.aj.pKeyInfo->aColl[u.aj.i]; u.aj.bRev = u.aj.pKeyInfo->aSortOrder[u.aj.i]; - iCompare = sqlite3MemCompare(&p->aMem[u.aj.p1+u.aj.idx], &p->aMem[u.aj.p2+u.aj.idx], u.aj.pColl); + iCompare = sqlite3MemCompare(&aMem[u.aj.p1+u.aj.idx], &aMem[u.aj.p2+u.aj.idx], u.aj.pColl); if( iCompare ){ if( u.aj.bRev ) iCompare = -iCompare; break; @@ -52609,11 +54037,13 @@ int v2; /* Right operand: 0==FALSE, 1==TRUE, 2==UNKNOWN or NULL */ #endif /* local variables moved into u.ak */ + pIn1 = &aMem[pOp->p1]; if( pIn1->flags & MEM_Null ){ u.ak.v1 = 2; }else{ u.ak.v1 = sqlite3VdbeIntValue(pIn1)!=0; } + pIn2 = &aMem[pOp->p2]; if( pIn2->flags & MEM_Null ){ u.ak.v2 = 2; }else{ @@ -52626,6 +54056,7 @@ static const unsigned char or_logic[] = { 0, 1, 2, 1, 1, 1, 2, 1, 2 }; u.ak.v1 = or_logic[u.ak.v1*3+u.ak.v2]; } + pOut = &aMem[pOp->p3]; if( u.ak.v1==2 ){ MemSetTypeFlag(pOut, MEM_Null); }else{ @@ -52641,8 +54072,9 @@ ** boolean complement in register P2. If the value in register P1 is ** NULL, then a NULL is stored in P2. */ -case OP_Not: { /* same as TK_NOT, in1 */ - pOut = &p->aMem[pOp->p2]; +case OP_Not: { /* same as TK_NOT, in1, out2 */ + pIn1 = &aMem[pOp->p1]; + pOut = &aMem[pOp->p2]; if( pIn1->flags & MEM_Null ){ sqlite3VdbeMemSetNull(pOut); }else{ @@ -52657,8 +54089,9 @@ ** ones-complement of the P1 value into register P2. If P1 holds ** a NULL then store a NULL in P2. */ -case OP_BitNot: { /* same as TK_BITNOT, in1 */ - pOut = &p->aMem[pOp->p2]; +case OP_BitNot: { /* same as TK_BITNOT, in1, out2 */ + pIn1 = &aMem[pOp->p1]; + pOut = &aMem[pOp->p2]; if( pIn1->flags & MEM_Null ){ sqlite3VdbeMemSetNull(pOut); }else{ @@ -52684,6 +54117,7 @@ #if 0 /* local variables moved into u.al */ int c; #endif /* local variables moved into u.al */ + pIn1 = &aMem[pOp->p1]; if( pIn1->flags & MEM_Null ){ u.al.c = pOp->p3; }else{ @@ -52705,6 +54139,7 @@ ** Jump to P2 if the value in register P1 is NULL. */ case OP_IsNull: { /* same as TK_ISNULL, jump, in1 */ + pIn1 = &aMem[pOp->p1]; if( (pIn1->flags & MEM_Null)!=0 ){ pc = pOp->p2 - 1; } @@ -52716,35 +54151,14 @@ ** Jump to P2 if the value in register P1 is not NULL. */ case OP_NotNull: { /* same as TK_NOTNULL, jump, in1 */ + pIn1 = &aMem[pOp->p1]; if( (pIn1->flags & MEM_Null)==0 ){ pc = pOp->p2 - 1; } break; } -/* Opcode: SetNumColumns * P2 * * * -** -** This opcode sets the number of columns for the cursor opened by the -** following instruction to P2. -** -** An OP_SetNumColumns is only useful if it occurs immediately before -** one of the following opcodes: -** -** OpenRead -** OpenWrite -** OpenPseudo -** -** If the OP_Column opcode is to be executed on a cursor, then -** this opcode must be present immediately before the opcode that -** opens the cursor. -*/ -#if 0 -case OP_SetNumColumns: { - break; -} -#endif - -/* Opcode: Column P1 P2 P3 P4 * +/* Opcode: Column P1 P2 P3 P4 P5 ** ** Interpret the data that cursor P1 points to as a structure built using ** the MakeRecord instruction. (See the MakeRecord opcode for additional @@ -52757,6 +54171,11 @@ ** If the column contains fewer than P2 fields, then extract a NULL. Or, ** if the P4 argument is a P4_MEM use the value of the P4 argument as ** the result. +** +** If the OPFLAG_CLEARCACHE bit is set on P5 and P1 is a pseudo-table cursor, +** then the cache of the cursor is reset prior to extracting the column. +** The first OP_Column against a pseudo-table after the value of the content +** register has changed should have this bit set. */ case OP_Column: { #if 0 /* local variables moved into u.am */ @@ -52781,6 +54200,7 @@ u64 offset64; /* 64-bit offset. 64 bits needed to catch overflow */ int szHdr; /* Size of the header size field at start of record */ int avail; /* Number of bytes of available data */ + Mem *pReg; /* PseudoTable input register */ #endif /* local variables moved into u.am */ @@ -52790,7 +54210,7 @@ memset(&u.am.sMem, 0, sizeof(u.am.sMem)); assert( u.am.p1nCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); - u.am.pDest = &p->aMem[pOp->p3]; + u.am.pDest = &aMem[pOp->p3]; MemSetTypeFlag(u.am.pDest, MEM_Null); u.am.zRec = 0; @@ -52822,20 +54242,25 @@ u.am.payloadSize = u.am.pC->payloadSize; u.am.zRec = (char*)u.am.pC->aRow; }else if( u.am.pC->isIndex ){ - sqlite3BtreeKeySize(u.am.pCrsr, &u.am.payloadSize64); + assert( sqlite3BtreeCursorIsValid(u.am.pCrsr) ); + rc = sqlite3BtreeKeySize(u.am.pCrsr, &u.am.payloadSize64); + assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ /* sqlite3BtreeParseCellPtr() uses getVarint32() to extract the ** payload size, so it is impossible for u.am.payloadSize64 to be ** larger than 32 bits. */ assert( (u.am.payloadSize64 & SQLITE_MAX_U32)==(u64)u.am.payloadSize64 ); u.am.payloadSize = (u32)u.am.payloadSize64; }else{ - sqlite3BtreeDataSize(u.am.pCrsr, &u.am.payloadSize); - } - }else if( u.am.pC->pseudoTable ){ - /* The record is the sole entry of a pseudo-table */ - u.am.payloadSize = u.am.pC->nData; - u.am.zRec = u.am.pC->pData; - u.am.pC->cacheStatus = CACHE_STALE; + assert( sqlite3BtreeCursorIsValid(u.am.pCrsr) ); + rc = sqlite3BtreeDataSize(u.am.pCrsr, &u.am.payloadSize); + assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ + } + }else if( u.am.pC->pseudoTableReg>0 ){ + u.am.pReg = &aMem[u.am.pC->pseudoTableReg]; + assert( u.am.pReg->flags & MEM_Blob ); + u.am.payloadSize = u.am.pReg->n; + u.am.zRec = u.am.pReg->z; + u.am.pC->cacheStatus = (pOp->p5&OPFLAG_CLEARCACHE) ? CACHE_STALE : p->cacheCtr; assert( u.am.payloadSize==0 || u.am.zRec!=0 ); }else{ /* Consider the row to be NULL */ @@ -53043,18 +54468,19 @@ */ case OP_Affinity: { #if 0 /* local variables moved into u.an */ - char *zAffinity; /* The affinity to be applied */ - Mem *pData0; /* First register to which to apply affinity */ - Mem *pLast; /* Last register to which to apply affinity */ - Mem *pRec; /* Current register */ + const char *zAffinity; /* The affinity to be applied */ + char cAff; /* A single character of affinity */ #endif /* local variables moved into u.an */ u.an.zAffinity = pOp->p4.z; - u.an.pData0 = &p->aMem[pOp->p1]; - u.an.pLast = &u.an.pData0[pOp->p2-1]; - for(u.an.pRec=u.an.pData0; u.an.pRec<=u.an.pLast; u.an.pRec++){ - ExpandBlob(u.an.pRec); - applyAffinity(u.an.pRec, u.an.zAffinity[u.an.pRec-u.an.pData0], encoding); + assert( u.an.zAffinity!=0 ); + assert( u.an.zAffinity[pOp->p2]==0 ); + pIn1 = &aMem[pOp->p1]; + while( (u.an.cAff = *(u.an.zAffinity++))!=0 ){ + assert( pIn1 <= &p->aMem[p->nMem] ); + ExpandBlob(pIn1); + applyAffinity(pIn1, u.an.cAff, encoding); + pIn1++; } break; } @@ -53118,7 +54544,7 @@ u.ao.nField = pOp->p1; u.ao.zAffinity = pOp->p4.z; assert( u.ao.nField>0 && pOp->p2>0 && pOp->p2+u.ao.nField<=p->nMem+1 ); - u.ao.pData0 = &p->aMem[u.ao.nField]; + u.ao.pData0 = &aMem[u.ao.nField]; u.ao.nField = pOp->p2; u.ao.pLast = &u.ao.pData0[u.ao.nField-1]; u.ao.file_format = p->minWriteFileFormat; @@ -53162,7 +54588,7 @@ ** sqlite3VdbeMemGrow() could clobber the value before it is used). */ assert( pOp->p3p1 || pOp->p3>=pOp->p1+pOp->p2 ); - pOut = &p->aMem[pOp->p3]; + pOut = &aMem[pOp->p3]; if( sqlite3VdbeMemGrow(pOut, (int)u.ao.nByte, 0) ){ goto no_mem; } @@ -53211,54 +54637,11 @@ }else{ u.ap.nEntry = 0; } - pOut->flags = MEM_Int; pOut->u.i = u.ap.nEntry; break; } #endif -/* Opcode: Statement P1 * * * * -** -** Begin an individual statement transaction which is part of a larger -** transaction. This is needed so that the statement -** can be rolled back after an error without having to roll back the -** entire transaction. The statement transaction will automatically -** commit when the VDBE halts. -** -** If the database connection is currently in autocommit mode (that -** is to say, if it is in between BEGIN and COMMIT) -** and if there are no other active statements on the same database -** connection, then this operation is a no-op. No statement transaction -** is needed since any error can use the normal ROLLBACK process to -** undo changes. -** -** If a statement transaction is started, then a statement journal file -** will be allocated and initialized. -** -** The statement is begun on the database file with index P1. The main -** database file has an index of 0 and the file used for temporary tables -** has an index of 1. -*/ -case OP_Statement: { -#if 0 /* local variables moved into u.aq */ - Btree *pBt; -#endif /* local variables moved into u.aq */ - if( db->autoCommit==0 || db->activeVdbeCnt>1 ){ - assert( pOp->p1>=0 && pOp->p1nDb ); - assert( db->aDb[pOp->p1].pBt!=0 ); - u.aq.pBt = db->aDb[pOp->p1].pBt; - assert( sqlite3BtreeIsInTrans(u.aq.pBt) ); - assert( (p->btreeMask & (1<p1))!=0 ); - if( p->iStatement==0 ){ - assert( db->nStatement>=0 && db->nSavepoint>=0 ); - db->nStatement++; - p->iStatement = db->nSavepoint + db->nStatement; - } - rc = sqlite3BtreeBeginStmt(u.aq.pBt, p->iStatement); - } - break; -} - /* Opcode: Savepoint P1 * * P4 * ** ** Open, release or rollback the savepoint named by parameter P4, depending @@ -53266,7 +54649,7 @@ ** existing savepoint, P1==1, or to rollback an existing savepoint P1==2. */ case OP_Savepoint: { -#if 0 /* local variables moved into u.ar */ +#if 0 /* local variables moved into u.aq */ int p1; /* Value of P1 operand */ char *zName; /* Name of savepoint */ int nName; @@ -53275,20 +54658,20 @@ Savepoint *pTmp; int iSavepoint; int ii; -#endif /* local variables moved into u.ar */ +#endif /* local variables moved into u.aq */ - u.ar.p1 = pOp->p1; - u.ar.zName = pOp->p4.z; + u.aq.p1 = pOp->p1; + u.aq.zName = pOp->p4.z; - /* Assert that the u.ar.p1 parameter is valid. Also that if there is no open + /* Assert that the u.aq.p1 parameter is valid. Also that if there is no open ** transaction, then there cannot be any savepoints. */ assert( db->pSavepoint==0 || db->autoCommit==0 ); - assert( u.ar.p1==SAVEPOINT_BEGIN||u.ar.p1==SAVEPOINT_RELEASE||u.ar.p1==SAVEPOINT_ROLLBACK ); + assert( u.aq.p1==SAVEPOINT_BEGIN||u.aq.p1==SAVEPOINT_RELEASE||u.aq.p1==SAVEPOINT_ROLLBACK ); assert( db->pSavepoint || db->isTransactionSavepoint==0 ); assert( checkSavepointCount(db) ); - if( u.ar.p1==SAVEPOINT_BEGIN ){ + if( u.aq.p1==SAVEPOINT_BEGIN ){ if( db->writeVdbeCnt>0 ){ /* A new savepoint cannot be created if there are active write ** statements (i.e. open read/write incremental blob handles). @@ -53297,13 +54680,13 @@ "SQL statements in progress"); rc = SQLITE_BUSY; }else{ - u.ar.nName = sqlite3Strlen30(u.ar.zName); + u.aq.nName = sqlite3Strlen30(u.aq.zName); /* Create a new savepoint structure. */ - u.ar.pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+u.ar.nName+1); - if( u.ar.pNew ){ - u.ar.pNew->zName = (char *)&u.ar.pNew[1]; - memcpy(u.ar.pNew->zName, u.ar.zName, u.ar.nName+1); + u.aq.pNew = sqlite3DbMallocRaw(db, sizeof(Savepoint)+u.aq.nName+1); + if( u.aq.pNew ){ + u.aq.pNew->zName = (char *)&u.aq.pNew[1]; + memcpy(u.aq.pNew->zName, u.aq.zName, u.aq.nName+1); /* If there is no open transaction, then mark this as a special ** "transaction savepoint". */ @@ -53315,27 +54698,28 @@ } /* Link the new savepoint into the database handle's list. */ - u.ar.pNew->pNext = db->pSavepoint; - db->pSavepoint = u.ar.pNew; + u.aq.pNew->pNext = db->pSavepoint; + db->pSavepoint = u.aq.pNew; + u.aq.pNew->nDeferredCons = db->nDeferredCons; } } }else{ - u.ar.iSavepoint = 0; + u.aq.iSavepoint = 0; /* Find the named savepoint. If there is no such savepoint, then an ** an error is returned to the user. */ for( - u.ar.pSavepoint = db->pSavepoint; - u.ar.pSavepoint && sqlite3StrICmp(u.ar.pSavepoint->zName, u.ar.zName); - u.ar.pSavepoint = u.ar.pSavepoint->pNext + u.aq.pSavepoint = db->pSavepoint; + u.aq.pSavepoint && sqlite3StrICmp(u.aq.pSavepoint->zName, u.aq.zName); + u.aq.pSavepoint = u.aq.pSavepoint->pNext ){ - u.ar.iSavepoint++; + u.aq.iSavepoint++; } - if( !u.ar.pSavepoint ){ - sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", u.ar.zName); + if( !u.aq.pSavepoint ){ + sqlite3SetString(&p->zErrMsg, db, "no such savepoint: %s", u.aq.zName); rc = SQLITE_ERROR; }else if( - db->writeVdbeCnt>0 || (u.ar.p1==SAVEPOINT_ROLLBACK && db->activeVdbeCnt>1) + db->writeVdbeCnt>0 || (u.aq.p1==SAVEPOINT_ROLLBACK && db->activeVdbeCnt>1) ){ /* It is not possible to release (commit) a savepoint if there are ** active write statements. It is not possible to rollback a savepoint @@ -53343,7 +54727,7 @@ */ sqlite3SetString(&p->zErrMsg, db, "cannot %s savepoint - SQL statements in progress", - (u.ar.p1==SAVEPOINT_ROLLBACK ? "rollback": "release") + (u.aq.p1==SAVEPOINT_ROLLBACK ? "rollback": "release") ); rc = SQLITE_BUSY; }else{ @@ -53352,8 +54736,11 @@ ** and this is a RELEASE command, then the current transaction ** is committed. */ - int isTransaction = u.ar.pSavepoint->pNext==0 && db->isTransactionSavepoint; - if( isTransaction && u.ar.p1==SAVEPOINT_RELEASE ){ + int isTransaction = u.aq.pSavepoint->pNext==0 && db->isTransactionSavepoint; + if( isTransaction && u.aq.p1==SAVEPOINT_RELEASE ){ + if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ + goto vdbe_return; + } db->autoCommit = 1; if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ p->pc = pc; @@ -53364,14 +54751,14 @@ db->isTransactionSavepoint = 0; rc = p->rc; }else{ - u.ar.iSavepoint = db->nSavepoint - u.ar.iSavepoint - 1; - for(u.ar.ii=0; u.ar.iinDb; u.ar.ii++){ - rc = sqlite3BtreeSavepoint(db->aDb[u.ar.ii].pBt, u.ar.p1, u.ar.iSavepoint); + u.aq.iSavepoint = db->nSavepoint - u.aq.iSavepoint - 1; + for(u.aq.ii=0; u.aq.iinDb; u.aq.ii++){ + rc = sqlite3BtreeSavepoint(db->aDb[u.aq.ii].pBt, u.aq.p1, u.aq.iSavepoint); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } } - if( u.ar.p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ + if( u.aq.p1==SAVEPOINT_ROLLBACK && (db->flags&SQLITE_InternChanges)!=0 ){ sqlite3ExpirePreparedStatements(db); sqlite3ResetInternalSchema(db, 0); } @@ -53379,21 +54766,26 @@ /* Regardless of whether this is a RELEASE or ROLLBACK, destroy all ** savepoints nested inside of the savepoint being operated on. */ - while( db->pSavepoint!=u.ar.pSavepoint ){ - u.ar.pTmp = db->pSavepoint; - db->pSavepoint = u.ar.pTmp->pNext; - sqlite3DbFree(db, u.ar.pTmp); + while( db->pSavepoint!=u.aq.pSavepoint ){ + u.aq.pTmp = db->pSavepoint; + db->pSavepoint = u.aq.pTmp->pNext; + sqlite3DbFree(db, u.aq.pTmp); db->nSavepoint--; } - /* If it is a RELEASE, then destroy the savepoint being operated on too */ - if( u.ar.p1==SAVEPOINT_RELEASE ){ - assert( u.ar.pSavepoint==db->pSavepoint ); - db->pSavepoint = u.ar.pSavepoint->pNext; - sqlite3DbFree(db, u.ar.pSavepoint); + /* If it is a RELEASE, then destroy the savepoint being operated on + ** too. If it is a ROLLBACK TO, then set the number of deferred + ** constraint violations present in the database to the value stored + ** when the savepoint was created. */ + if( u.aq.p1==SAVEPOINT_RELEASE ){ + assert( u.aq.pSavepoint==db->pSavepoint ); + db->pSavepoint = u.aq.pSavepoint->pNext; + sqlite3DbFree(db, u.aq.pSavepoint); if( !isTransaction ){ db->nSavepoint--; } + }else{ + db->nDeferredCons = u.aq.pSavepoint->nDeferredCons; } } } @@ -53411,20 +54803,20 @@ ** This instruction causes the VM to halt. */ case OP_AutoCommit: { -#if 0 /* local variables moved into u.as */ +#if 0 /* local variables moved into u.ar */ int desiredAutoCommit; int iRollback; int turnOnAC; -#endif /* local variables moved into u.as */ +#endif /* local variables moved into u.ar */ - u.as.desiredAutoCommit = pOp->p1; - u.as.iRollback = pOp->p2; - u.as.turnOnAC = u.as.desiredAutoCommit && !db->autoCommit; - assert( u.as.desiredAutoCommit==1 || u.as.desiredAutoCommit==0 ); - assert( u.as.desiredAutoCommit==1 || u.as.iRollback==0 ); + u.ar.desiredAutoCommit = pOp->p1; + u.ar.iRollback = pOp->p2; + u.ar.turnOnAC = u.ar.desiredAutoCommit && !db->autoCommit; + assert( u.ar.desiredAutoCommit==1 || u.ar.desiredAutoCommit==0 ); + assert( u.ar.desiredAutoCommit==1 || u.ar.iRollback==0 ); assert( db->activeVdbeCnt>0 ); /* At least this one VM is active */ - if( u.as.turnOnAC && u.as.iRollback && db->activeVdbeCnt>1 ){ + if( u.ar.turnOnAC && u.ar.iRollback && db->activeVdbeCnt>1 ){ /* If this instruction implements a ROLLBACK and other VMs are ** still running, and a transaction is active, return an error indicating ** that the other VMs must complete first. @@ -53432,23 +54824,25 @@ sqlite3SetString(&p->zErrMsg, db, "cannot rollback transaction - " "SQL statements in progress"); rc = SQLITE_BUSY; - }else if( u.as.turnOnAC && !u.as.iRollback && db->writeVdbeCnt>0 ){ + }else if( u.ar.turnOnAC && !u.ar.iRollback && db->writeVdbeCnt>0 ){ /* If this instruction implements a COMMIT and other VMs are writing ** return an error indicating that the other VMs must complete first. */ sqlite3SetString(&p->zErrMsg, db, "cannot commit transaction - " "SQL statements in progress"); rc = SQLITE_BUSY; - }else if( u.as.desiredAutoCommit!=db->autoCommit ){ - if( u.as.iRollback ){ - assert( u.as.desiredAutoCommit==1 ); + }else if( u.ar.desiredAutoCommit!=db->autoCommit ){ + if( u.ar.iRollback ){ + assert( u.ar.desiredAutoCommit==1 ); sqlite3RollbackAll(db); db->autoCommit = 1; + }else if( (rc = sqlite3VdbeCheckFk(p, 1))!=SQLITE_OK ){ + goto vdbe_return; }else{ - db->autoCommit = (u8)u.as.desiredAutoCommit; + db->autoCommit = (u8)u.ar.desiredAutoCommit; if( sqlite3VdbeHalt(p)==SQLITE_BUSY ){ p->pc = pc; - db->autoCommit = (u8)(1-u.as.desiredAutoCommit); + db->autoCommit = (u8)(1-u.ar.desiredAutoCommit); p->rc = rc = SQLITE_BUSY; goto vdbe_return; } @@ -53463,8 +54857,8 @@ goto vdbe_return; }else{ sqlite3SetString(&p->zErrMsg, db, - (!u.as.desiredAutoCommit)?"cannot start a transaction within a transaction":( - (u.as.iRollback)?"cannot rollback - no transaction is active": + (!u.ar.desiredAutoCommit)?"cannot start a transaction within a transaction":( + (u.ar.iRollback)?"cannot rollback - no transaction is active": "cannot commit - no transaction is active")); rc = SQLITE_ERROR; @@ -53491,27 +54885,54 @@ ** database. If P2 is 2 or greater then an EXCLUSIVE lock is also obtained ** on the file. ** +** If a write-transaction is started and the Vdbe.usesStmtJournal flag is +** true (this flag is set if the Vdbe may modify more than one row and may +** throw an ABORT exception), a statement transaction may also be opened. +** More specifically, a statement transaction is opened iff the database +** connection is currently not in autocommit mode, or if there are other +** active statements. A statement transaction allows the affects of this +** VDBE to be rolled back after an error without having to roll back the +** entire transaction. If no error is encountered, the statement transaction +** will automatically commit when the VDBE halts. +** ** If P2 is zero, then a read-lock is obtained on the database file. */ case OP_Transaction: { -#if 0 /* local variables moved into u.at */ +#if 0 /* local variables moved into u.as */ Btree *pBt; -#endif /* local variables moved into u.at */ +#endif /* local variables moved into u.as */ assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (1<p1))!=0 ); - u.at.pBt = db->aDb[pOp->p1].pBt; + u.as.pBt = db->aDb[pOp->p1].pBt; - if( u.at.pBt ){ - rc = sqlite3BtreeBeginTrans(u.at.pBt, pOp->p2); + if( u.as.pBt ){ + rc = sqlite3BtreeBeginTrans(u.as.pBt, pOp->p2); if( rc==SQLITE_BUSY ){ p->pc = pc; p->rc = rc = SQLITE_BUSY; goto vdbe_return; } - if( rc!=SQLITE_OK && rc!=SQLITE_READONLY /* && rc!=SQLITE_BUSY */ ){ + if( rc!=SQLITE_OK ){ goto abort_due_to_error; } + + if( pOp->p2 && p->usesStmtJournal + && (db->autoCommit==0 || db->activeVdbeCnt>1) + ){ + assert( sqlite3BtreeIsInTrans(u.as.pBt) ); + if( p->iStatement==0 ){ + assert( db->nStatement>=0 && db->nSavepoint>=0 ); + db->nStatement++; + p->iStatement = db->nSavepoint + db->nStatement; + } + rc = sqlite3BtreeBeginStmt(u.as.pBt, p->iStatement); + + /* Store the current value of the database handles deferred constraint + ** counter. If the statement transaction needs to be rolled back, + ** the value of this counter needs to be restored too. */ + p->nStmtDefCons = db->nDeferredCons; + } } break; } @@ -53529,22 +54950,21 @@ ** executing this instruction. */ case OP_ReadCookie: { /* out2-prerelease */ -#if 0 /* local variables moved into u.au */ +#if 0 /* local variables moved into u.at */ int iMeta; int iDb; int iCookie; -#endif /* local variables moved into u.au */ +#endif /* local variables moved into u.at */ - u.au.iDb = pOp->p1; - u.au.iCookie = pOp->p3; + u.at.iDb = pOp->p1; + u.at.iCookie = pOp->p3; assert( pOp->p3=0 && u.au.iDbnDb ); - assert( db->aDb[u.au.iDb].pBt!=0 ); - assert( (p->btreeMask & (1<=0 && u.at.iDbnDb ); + assert( db->aDb[u.at.iDb].pBt!=0 ); + assert( (p->btreeMask & (1<aDb[u.au.iDb].pBt, u.au.iCookie, (u32 *)&u.au.iMeta); - pOut->u.i = u.au.iMeta; - MemSetTypeFlag(pOut, MEM_Int); + sqlite3BtreeGetMeta(db->aDb[u.at.iDb].pBt, u.at.iCookie, (u32 *)&u.at.iMeta); + pOut->u.i = u.at.iMeta; break; } @@ -53559,29 +54979,31 @@ ** A transaction must be started before executing this opcode. */ case OP_SetCookie: { /* in3 */ -#if 0 /* local variables moved into u.av */ +#if 0 /* local variables moved into u.au */ Db *pDb; -#endif /* local variables moved into u.av */ +#endif /* local variables moved into u.au */ assert( pOp->p2p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (1<p1))!=0 ); - u.av.pDb = &db->aDb[pOp->p1]; - assert( u.av.pDb->pBt!=0 ); + u.au.pDb = &db->aDb[pOp->p1]; + assert( u.au.pDb->pBt!=0 ); + pIn3 = &aMem[pOp->p3]; sqlite3VdbeMemIntegerify(pIn3); /* See note about index shifting on OP_ReadCookie */ - rc = sqlite3BtreeUpdateMeta(u.av.pDb->pBt, pOp->p2, (int)pIn3->u.i); + rc = sqlite3BtreeUpdateMeta(u.au.pDb->pBt, pOp->p2, (int)pIn3->u.i); if( pOp->p2==BTREE_SCHEMA_VERSION ){ /* When the schema cookie changes, record the new cookie internally */ - u.av.pDb->pSchema->schema_cookie = (int)pIn3->u.i; + u.au.pDb->pSchema->schema_cookie = (int)pIn3->u.i; db->flags |= SQLITE_InternChanges; }else if( pOp->p2==BTREE_FILE_FORMAT ){ /* Record changes in the file format */ - u.av.pDb->pSchema->file_format = (u8)pIn3->u.i; + u.au.pDb->pSchema->file_format = (u8)pIn3->u.i; } if( pOp->p1==1 ){ /* Invalidate all prepared statements whenever the TEMP database ** schema is changed. Ticket #1644 */ sqlite3ExpirePreparedStatements(db); + p->expired = 0; } break; } @@ -53603,20 +55025,19 @@ ** invoked. */ case OP_VerifyCookie: { -#if 0 /* local variables moved into u.aw */ +#if 0 /* local variables moved into u.av */ int iMeta; Btree *pBt; -#endif /* local variables moved into u.aw */ +#endif /* local variables moved into u.av */ assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (1<p1))!=0 ); - u.aw.pBt = db->aDb[pOp->p1].pBt; - if( u.aw.pBt ){ - rc = sqlite3BtreeGetMeta(u.aw.pBt, BTREE_SCHEMA_VERSION, (u32 *)&u.aw.iMeta); + u.av.pBt = db->aDb[pOp->p1].pBt; + if( u.av.pBt ){ + sqlite3BtreeGetMeta(u.av.pBt, BTREE_SCHEMA_VERSION, (u32 *)&u.av.iMeta); }else{ - rc = SQLITE_OK; - u.aw.iMeta = 0; + u.av.iMeta = 0; } - if( rc==SQLITE_OK && u.aw.iMeta!=pOp->p2 ){ + if( u.av.iMeta!=pOp->p2 ){ sqlite3DbFree(db, p->zErrMsg); p->zErrMsg = sqlite3DbStrDup(db, "database schema has changed"); /* If the schema-cookie from the database file matches the cookie @@ -53632,7 +55053,7 @@ ** to be invalidated whenever sqlite3_step() is called from within ** a v-table method. */ - if( db->aDb[pOp->p1].pSchema->schema_cookie!=u.aw.iMeta ){ + if( db->aDb[pOp->p1].pSchema->schema_cookie!=u.av.iMeta ){ sqlite3ResetInternalSchema(db, pOp->p1); } @@ -53693,7 +55114,7 @@ */ case OP_OpenRead: case OP_OpenWrite: { -#if 0 /* local variables moved into u.ax */ +#if 0 /* local variables moved into u.aw */ int nField; KeyInfo *pKeyInfo; int p2; @@ -53702,94 +55123,75 @@ Btree *pX; VdbeCursor *pCur; Db *pDb; - int flags; -#endif /* local variables moved into u.ax */ +#endif /* local variables moved into u.aw */ + + if( p->expired ){ + rc = SQLITE_ABORT; + break; + } - u.ax.nField = 0; - u.ax.pKeyInfo = 0; - u.ax.p2 = pOp->p2; - u.ax.iDb = pOp->p3; - assert( u.ax.iDb>=0 && u.ax.iDbnDb ); - assert( (p->btreeMask & (1<aDb[u.ax.iDb]; - u.ax.pX = u.ax.pDb->pBt; - assert( u.ax.pX!=0 ); + u.aw.nField = 0; + u.aw.pKeyInfo = 0; + u.aw.p2 = pOp->p2; + u.aw.iDb = pOp->p3; + assert( u.aw.iDb>=0 && u.aw.iDbnDb ); + assert( (p->btreeMask & (1<aDb[u.aw.iDb]; + u.aw.pX = u.aw.pDb->pBt; + assert( u.aw.pX!=0 ); if( pOp->opcode==OP_OpenWrite ){ - u.ax.wrFlag = 1; - if( u.ax.pDb->pSchema->file_format < p->minWriteFileFormat ){ - p->minWriteFileFormat = u.ax.pDb->pSchema->file_format; + u.aw.wrFlag = 1; + if( u.aw.pDb->pSchema->file_format < p->minWriteFileFormat ){ + p->minWriteFileFormat = u.aw.pDb->pSchema->file_format; } }else{ - u.ax.wrFlag = 0; + u.aw.wrFlag = 0; } if( pOp->p5 ){ - assert( u.ax.p2>0 ); - assert( u.ax.p2<=p->nMem ); - pIn2 = &p->aMem[u.ax.p2]; + assert( u.aw.p2>0 ); + assert( u.aw.p2<=p->nMem ); + pIn2 = &aMem[u.aw.p2]; sqlite3VdbeMemIntegerify(pIn2); - u.ax.p2 = (int)pIn2->u.i; - /* The u.ax.p2 value always comes from a prior OP_CreateTable opcode and - ** that opcode will always set the u.ax.p2 value to 2 or more or else fail. + u.aw.p2 = (int)pIn2->u.i; + /* The u.aw.p2 value always comes from a prior OP_CreateTable opcode and + ** that opcode will always set the u.aw.p2 value to 2 or more or else fail. ** If there were a failure, the prepared statement would have halted ** before reaching this instruction. */ - if( NEVER(u.ax.p2<2) ) { + if( NEVER(u.aw.p2<2) ) { rc = SQLITE_CORRUPT_BKPT; goto abort_due_to_error; } } if( pOp->p4type==P4_KEYINFO ){ - u.ax.pKeyInfo = pOp->p4.pKeyInfo; - u.ax.pKeyInfo->enc = ENC(p->db); - u.ax.nField = u.ax.pKeyInfo->nField+1; + u.aw.pKeyInfo = pOp->p4.pKeyInfo; + u.aw.pKeyInfo->enc = ENC(p->db); + u.aw.nField = u.aw.pKeyInfo->nField+1; }else if( pOp->p4type==P4_INT32 ){ - u.ax.nField = pOp->p4.i; + u.aw.nField = pOp->p4.i; } assert( pOp->p1>=0 ); - u.ax.pCur = allocateCursor(p, pOp->p1, u.ax.nField, u.ax.iDb, 1); - if( u.ax.pCur==0 ) goto no_mem; - u.ax.pCur->nullRow = 1; - rc = sqlite3BtreeCursor(u.ax.pX, u.ax.p2, u.ax.wrFlag, u.ax.pKeyInfo, u.ax.pCur->pCursor); - u.ax.pCur->pKeyInfo = u.ax.pKeyInfo; - - switch( rc ){ - case SQLITE_OK: { - u.ax.flags = sqlite3BtreeFlags(u.ax.pCur->pCursor); - - /* Sanity checking. Only the lower four bits of the u.ax.flags byte should - ** be used. Bit 3 (mask 0x08) is unpredictable. The lower 3 bits - ** (mask 0x07) should be either 5 (intkey+leafdata for tables) or - ** 2 (zerodata for indices). If these conditions are not met it can - ** only mean that we are dealing with a corrupt database file. - ** Note: All of the above is checked already in sqlite3BtreeCursor(). - */ - assert( (u.ax.flags & 0xf0)==0 ); - assert( (u.ax.flags & 0x07)==5 || (u.ax.flags & 0x07)==2 ); - - u.ax.pCur->isTable = (u.ax.flags & BTREE_INTKEY)!=0 ?1:0; - u.ax.pCur->isIndex = (u.ax.flags & BTREE_ZERODATA)!=0 ?1:0; - /* If P4==0 it means we are expected to open a table. If P4!=0 then - ** we expect to be opening an index. If this is not what happened, - ** then the database is corrupt - */ - if( (u.ax.pCur->isTable && pOp->p4type==P4_KEYINFO) - || (u.ax.pCur->isIndex && pOp->p4type!=P4_KEYINFO) ){ - rc = SQLITE_CORRUPT_BKPT; - goto abort_due_to_error; - } - break; - } - case SQLITE_EMPTY: { - u.ax.pCur->isTable = pOp->p4type!=P4_KEYINFO; - u.ax.pCur->isIndex = !u.ax.pCur->isTable; - u.ax.pCur->pCursor = 0; - rc = SQLITE_OK; - break; - } - default: { - assert( rc!=SQLITE_BUSY ); /* Busy conditions detected earlier */ - goto abort_due_to_error; - } + u.aw.pCur = allocateCursor(p, pOp->p1, u.aw.nField, u.aw.iDb, 1); + if( u.aw.pCur==0 ) goto no_mem; + u.aw.pCur->nullRow = 1; + rc = sqlite3BtreeCursor(u.aw.pX, u.aw.p2, u.aw.wrFlag, u.aw.pKeyInfo, u.aw.pCur->pCursor); + u.aw.pCur->pKeyInfo = u.aw.pKeyInfo; + + /* Since it performs no memory allocation or IO, the only values that + ** sqlite3BtreeCursor() may return are SQLITE_EMPTY and SQLITE_OK. + ** SQLITE_EMPTY is only returned when attempting to open the table + ** rooted at page 1 of a zero-byte database. */ + assert( rc==SQLITE_EMPTY || rc==SQLITE_OK ); + if( rc==SQLITE_EMPTY ){ + u.aw.pCur->pCursor = 0; + rc = SQLITE_OK; } + + /* Set the VdbeCursor.isTable and isIndex variables. Previous versions of + ** SQLite used to check if the root-page flags were sane at this point + ** and report database corruption if they were not, but this check has + ** since moved into the btree layer. */ + u.aw.pCur->isTable = pOp->p4type!=P4_KEYINFO; + u.aw.pCur->isIndex = !u.aw.pCur->isTable; break; } @@ -53812,9 +55214,9 @@ ** that created confusion with the whole virtual-table idea. */ case OP_OpenEphemeral: { -#if 0 /* local variables moved into u.ay */ +#if 0 /* local variables moved into u.ax */ VdbeCursor *pCx; -#endif /* local variables moved into u.ay */ +#endif /* local variables moved into u.ax */ static const int openFlags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | @@ -53823,13 +55225,13 @@ SQLITE_OPEN_TRANSIENT_DB; assert( pOp->p1>=0 ); - u.ay.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); - if( u.ay.pCx==0 ) goto no_mem; - u.ay.pCx->nullRow = 1; + u.ax.pCx = allocateCursor(p, pOp->p1, pOp->p2, -1, 1); + if( u.ax.pCx==0 ) goto no_mem; + u.ax.pCx->nullRow = 1; rc = sqlite3BtreeFactory(db, 0, 1, SQLITE_DEFAULT_TEMP_CACHE_SIZE, openFlags, - &u.ay.pCx->pBt); + &u.ax.pCx->pBt); if( rc==SQLITE_OK ){ - rc = sqlite3BtreeBeginTrans(u.ay.pCx->pBt, 1); + rc = sqlite3BtreeBeginTrans(u.ax.pCx->pBt, 1); } if( rc==SQLITE_OK ){ /* If a transient index is required, create it by calling @@ -53840,60 +55242,51 @@ if( pOp->p4.pKeyInfo ){ int pgno; assert( pOp->p4type==P4_KEYINFO ); - rc = sqlite3BtreeCreateTable(u.ay.pCx->pBt, &pgno, BTREE_ZERODATA); + rc = sqlite3BtreeCreateTable(u.ax.pCx->pBt, &pgno, BTREE_ZERODATA); if( rc==SQLITE_OK ){ assert( pgno==MASTER_ROOT+1 ); - rc = sqlite3BtreeCursor(u.ay.pCx->pBt, pgno, 1, - (KeyInfo*)pOp->p4.z, u.ay.pCx->pCursor); - u.ay.pCx->pKeyInfo = pOp->p4.pKeyInfo; - u.ay.pCx->pKeyInfo->enc = ENC(p->db); + rc = sqlite3BtreeCursor(u.ax.pCx->pBt, pgno, 1, + (KeyInfo*)pOp->p4.z, u.ax.pCx->pCursor); + u.ax.pCx->pKeyInfo = pOp->p4.pKeyInfo; + u.ax.pCx->pKeyInfo->enc = ENC(p->db); } - u.ay.pCx->isTable = 0; + u.ax.pCx->isTable = 0; }else{ - rc = sqlite3BtreeCursor(u.ay.pCx->pBt, MASTER_ROOT, 1, 0, u.ay.pCx->pCursor); - u.ay.pCx->isTable = 1; + rc = sqlite3BtreeCursor(u.ax.pCx->pBt, MASTER_ROOT, 1, 0, u.ax.pCx->pCursor); + u.ax.pCx->isTable = 1; } } - u.ay.pCx->isIndex = !u.ay.pCx->isTable; + u.ax.pCx->isIndex = !u.ax.pCx->isTable; break; } /* Opcode: OpenPseudo P1 P2 P3 * * ** ** Open a new cursor that points to a fake table that contains a single -** row of data. Any attempt to write a second row of data causes the -** first row to be deleted. All data is deleted when the cursor is -** closed. +** row of data. The content of that one row in the content of memory +** register P2. In other words, cursor P1 becomes an alias for the +** MEM_Blob content contained in register P2. ** -** A pseudo-table created by this opcode is useful for holding the -** NEW or OLD tables in a trigger. Also used to hold the a single +** A pseudo-table created by this opcode is used to hold the a single ** row output from the sorter so that the row can be decomposed into -** individual columns using the OP_Column opcode. -** -** When OP_Insert is executed to insert a row in to the pseudo table, -** the pseudo-table cursor may or may not make it's own copy of the -** original row data. If P2 is 0, then the pseudo-table will copy the -** original row data. Otherwise, a pointer to the original memory cell -** is stored. In this case, the vdbe program must ensure that the -** memory cell containing the row data is not overwritten until the -** pseudo table is closed (or a new row is inserted into it). +** individual columns using the OP_Column opcode. The OP_Column opcode +** is the only cursor opcode that works with a pseudo-table. ** ** P3 is the number of fields in the records that will be stored by ** the pseudo-table. */ case OP_OpenPseudo: { -#if 0 /* local variables moved into u.az */ +#if 0 /* local variables moved into u.ay */ VdbeCursor *pCx; -#endif /* local variables moved into u.az */ +#endif /* local variables moved into u.ay */ assert( pOp->p1>=0 ); - u.az.pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0); - if( u.az.pCx==0 ) goto no_mem; - u.az.pCx->nullRow = 1; - u.az.pCx->pseudoTable = 1; - u.az.pCx->ephemPseudoTable = (u8)pOp->p2; - u.az.pCx->isTable = 1; - u.az.pCx->isIndex = 0; + u.ay.pCx = allocateCursor(p, pOp->p1, pOp->p3, -1, 0); + if( u.ay.pCx==0 ) goto no_mem; + u.ay.pCx->nullRow = 1; + u.ay.pCx->pseudoTableReg = pOp->p2; + u.ay.pCx->isTable = 1; + u.ay.pCx->isIndex = 0; break; } @@ -53965,29 +55358,34 @@ case OP_SeekLe: /* jump, in3 */ case OP_SeekGe: /* jump, in3 */ case OP_SeekGt: { /* jump, in3 */ -#if 0 /* local variables moved into u.ba */ +#if 0 /* local variables moved into u.az */ int res; int oc; VdbeCursor *pC; UnpackedRecord r; int nField; i64 iKey; /* The rowid we are to seek to */ -#endif /* local variables moved into u.ba */ +#endif /* local variables moved into u.az */ assert( pOp->p1>=0 && pOp->p1nCursor ); assert( pOp->p2!=0 ); - u.ba.pC = p->apCsr[pOp->p1]; - assert( u.ba.pC!=0 ); - if( u.ba.pC->pCursor!=0 ){ - u.ba.oc = pOp->opcode; - u.ba.pC->nullRow = 0; - if( u.ba.pC->isTable ){ + u.az.pC = p->apCsr[pOp->p1]; + assert( u.az.pC!=0 ); + assert( u.az.pC->pseudoTableReg==0 ); + assert( OP_SeekLe == OP_SeekLt+1 ); + assert( OP_SeekGe == OP_SeekLt+2 ); + assert( OP_SeekGt == OP_SeekLt+3 ); + if( u.az.pC->pCursor!=0 ){ + u.az.oc = pOp->opcode; + u.az.pC->nullRow = 0; + if( u.az.pC->isTable ){ /* The input value in P3 might be of any type: integer, real, string, ** blob, or NULL. But it needs to be an integer before we can do ** the seek, so covert it. */ + pIn3 = &aMem[pOp->p3]; applyNumericAffinity(pIn3); - u.ba.iKey = sqlite3VdbeIntValue(pIn3); - u.ba.pC->rowidIsValid = 0; + u.az.iKey = sqlite3VdbeIntValue(pIn3); + u.az.pC->rowidIsValid = 0; /* If the P3 value could not be converted into an integer without ** loss of information, then special processing is required... */ @@ -54002,88 +55400,98 @@ ** point number. */ assert( (pIn3->flags & MEM_Real)!=0 ); - if( u.ba.iKey==SMALLEST_INT64 && (pIn3->r<(double)u.ba.iKey || pIn3->r>0) ){ + if( u.az.iKey==SMALLEST_INT64 && (pIn3->r<(double)u.az.iKey || pIn3->r>0) ){ /* The P3 value is too large in magnitude to be expressed as an ** integer. */ - u.ba.res = 1; + u.az.res = 1; if( pIn3->r<0 ){ - if( u.ba.oc==OP_SeekGt || u.ba.oc==OP_SeekGe ){ - rc = sqlite3BtreeFirst(u.ba.pC->pCursor, &u.ba.res); + if( u.az.oc>=OP_SeekGe ){ assert( u.az.oc==OP_SeekGe || u.az.oc==OP_SeekGt ); + rc = sqlite3BtreeFirst(u.az.pC->pCursor, &u.az.res); if( rc!=SQLITE_OK ) goto abort_due_to_error; } }else{ - if( u.ba.oc==OP_SeekLt || u.ba.oc==OP_SeekLe ){ - rc = sqlite3BtreeLast(u.ba.pC->pCursor, &u.ba.res); + if( u.az.oc<=OP_SeekLe ){ assert( u.az.oc==OP_SeekLt || u.az.oc==OP_SeekLe ); + rc = sqlite3BtreeLast(u.az.pC->pCursor, &u.az.res); if( rc!=SQLITE_OK ) goto abort_due_to_error; } } - if( u.ba.res ){ + if( u.az.res ){ pc = pOp->p2 - 1; } break; - }else if( u.ba.oc==OP_SeekLt || u.ba.oc==OP_SeekGe ){ + }else if( u.az.oc==OP_SeekLt || u.az.oc==OP_SeekGe ){ /* Use the ceiling() function to convert real->int */ - if( pIn3->r > (double)u.ba.iKey ) u.ba.iKey++; + if( pIn3->r > (double)u.az.iKey ) u.az.iKey++; }else{ /* Use the floor() function to convert real->int */ - assert( u.ba.oc==OP_SeekLe || u.ba.oc==OP_SeekGt ); - if( pIn3->r < (double)u.ba.iKey ) u.ba.iKey--; + assert( u.az.oc==OP_SeekLe || u.az.oc==OP_SeekGt ); + if( pIn3->r < (double)u.az.iKey ) u.az.iKey--; } } - rc = sqlite3BtreeMovetoUnpacked(u.ba.pC->pCursor, 0, (u64)u.ba.iKey, 0, &u.ba.res); + rc = sqlite3BtreeMovetoUnpacked(u.az.pC->pCursor, 0, (u64)u.az.iKey, 0, &u.az.res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } - if( u.ba.res==0 ){ - u.ba.pC->rowidIsValid = 1; - u.ba.pC->lastRowid = u.ba.iKey; + if( u.az.res==0 ){ + u.az.pC->rowidIsValid = 1; + u.az.pC->lastRowid = u.az.iKey; } }else{ - u.ba.nField = pOp->p4.i; + u.az.nField = pOp->p4.i; assert( pOp->p4type==P4_INT32 ); - assert( u.ba.nField>0 ); - u.ba.r.pKeyInfo = u.ba.pC->pKeyInfo; - u.ba.r.nField = (u16)u.ba.nField; - if( u.ba.oc==OP_SeekGt || u.ba.oc==OP_SeekLe ){ - u.ba.r.flags = UNPACKED_INCRKEY; - }else{ - u.ba.r.flags = 0; - } - u.ba.r.aMem = &p->aMem[pOp->p3]; - rc = sqlite3BtreeMovetoUnpacked(u.ba.pC->pCursor, &u.ba.r, 0, 0, &u.ba.res); + assert( u.az.nField>0 ); + u.az.r.pKeyInfo = u.az.pC->pKeyInfo; + u.az.r.nField = (u16)u.az.nField; + + /* The next line of code computes as follows, only faster: + ** if( u.az.oc==OP_SeekGt || u.az.oc==OP_SeekLe ){ + ** u.az.r.flags = UNPACKED_INCRKEY; + ** }else{ + ** u.az.r.flags = 0; + ** } + */ + u.az.r.flags = (u16)(UNPACKED_INCRKEY * (1 & (u.az.oc - OP_SeekLt))); + assert( u.az.oc!=OP_SeekGt || u.az.r.flags==UNPACKED_INCRKEY ); + assert( u.az.oc!=OP_SeekLe || u.az.r.flags==UNPACKED_INCRKEY ); + assert( u.az.oc!=OP_SeekGe || u.az.r.flags==0 ); + assert( u.az.oc!=OP_SeekLt || u.az.r.flags==0 ); + + u.az.r.aMem = &aMem[pOp->p3]; + ExpandBlob(u.az.r.aMem); + rc = sqlite3BtreeMovetoUnpacked(u.az.pC->pCursor, &u.az.r, 0, 0, &u.az.res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } - u.ba.pC->rowidIsValid = 0; + u.az.pC->rowidIsValid = 0; } - u.ba.pC->deferredMoveto = 0; - u.ba.pC->cacheStatus = CACHE_STALE; + u.az.pC->deferredMoveto = 0; + u.az.pC->cacheStatus = CACHE_STALE; #ifdef SQLITE_TEST sqlite3_search_count++; #endif - if( u.ba.oc==OP_SeekGe || u.ba.oc==OP_SeekGt ){ - if( u.ba.res<0 || (u.ba.res==0 && u.ba.oc==OP_SeekGt) ){ - rc = sqlite3BtreeNext(u.ba.pC->pCursor, &u.ba.res); + if( u.az.oc>=OP_SeekGe ){ assert( u.az.oc==OP_SeekGe || u.az.oc==OP_SeekGt ); + if( u.az.res<0 || (u.az.res==0 && u.az.oc==OP_SeekGt) ){ + rc = sqlite3BtreeNext(u.az.pC->pCursor, &u.az.res); if( rc!=SQLITE_OK ) goto abort_due_to_error; - u.ba.pC->rowidIsValid = 0; + u.az.pC->rowidIsValid = 0; }else{ - u.ba.res = 0; + u.az.res = 0; } }else{ - assert( u.ba.oc==OP_SeekLt || u.ba.oc==OP_SeekLe ); - if( u.ba.res>0 || (u.ba.res==0 && u.ba.oc==OP_SeekLt) ){ - rc = sqlite3BtreePrevious(u.ba.pC->pCursor, &u.ba.res); + assert( u.az.oc==OP_SeekLt || u.az.oc==OP_SeekLe ); + if( u.az.res>0 || (u.az.res==0 && u.az.oc==OP_SeekLt) ){ + rc = sqlite3BtreePrevious(u.az.pC->pCursor, &u.az.res); if( rc!=SQLITE_OK ) goto abort_due_to_error; - u.ba.pC->rowidIsValid = 0; + u.az.pC->rowidIsValid = 0; }else{ - /* u.ba.res might be negative because the table is empty. Check to + /* u.az.res might be negative because the table is empty. Check to ** see if this is the case. */ - u.ba.res = sqlite3BtreeEof(u.ba.pC->pCursor); + u.az.res = sqlite3BtreeEof(u.az.pC->pCursor); } } assert( pOp->p2>0 ); - if( u.ba.res ){ + if( u.az.res ){ pc = pOp->p2 - 1; } }else{ @@ -54091,7 +55499,6 @@ ** for read access returns SQLITE_EMPTY. In this case always ** take the jump (since there are no records in the table). */ - assert( u.ba.pC->pseudoTable==0 ); pc = pOp->p2 - 1; } break; @@ -54107,102 +55514,114 @@ ** occur, no unnecessary I/O happens. */ case OP_Seek: { /* in2 */ -#if 0 /* local variables moved into u.bb */ +#if 0 /* local variables moved into u.ba */ VdbeCursor *pC; -#endif /* local variables moved into u.bb */ +#endif /* local variables moved into u.ba */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bb.pC = p->apCsr[pOp->p1]; - assert( u.bb.pC!=0 ); - if( ALWAYS(u.bb.pC->pCursor!=0) ){ - assert( u.bb.pC->isTable ); - u.bb.pC->nullRow = 0; - u.bb.pC->movetoTarget = sqlite3VdbeIntValue(pIn2); - u.bb.pC->rowidIsValid = 0; - u.bb.pC->deferredMoveto = 1; + u.ba.pC = p->apCsr[pOp->p1]; + assert( u.ba.pC!=0 ); + if( ALWAYS(u.ba.pC->pCursor!=0) ){ + assert( u.ba.pC->isTable ); + u.ba.pC->nullRow = 0; + pIn2 = &aMem[pOp->p2]; + u.ba.pC->movetoTarget = sqlite3VdbeIntValue(pIn2); + u.ba.pC->rowidIsValid = 0; + u.ba.pC->deferredMoveto = 1; } break; } -/* Opcode: Found P1 P2 P3 * * +/* Opcode: Found P1 P2 P3 P4 * +** +** If P4==0 then register P3 holds a blob constructed by MakeRecord. If +** P4>0 then register P3 is the first of P4 registers that form an unpacked +** record. ** -** Register P3 holds a blob constructed by MakeRecord. P1 is an index. -** If an entry that matches the value in register p3 exists in P1 then -** jump to P2. If the P3 value does not match any entry in P1 -** then fall thru. The P1 cursor is left pointing at the matching entry -** if it exists. -** -** This instruction is used to implement the IN operator where the -** left-hand side is a SELECT statement. P1 may be a true index, or it -** may be a temporary index that holds the results of the SELECT -** statement. This instruction is also used to implement the -** DISTINCT keyword in SELECT statements. -** -** This instruction checks if index P1 contains a record for which -** the first N serialized values exactly match the N serialized values -** in the record in register P3, where N is the total number of values in -** the P3 record (the P3 record is a prefix of the P1 record). -** -** See also: NotFound, IsUnique, NotExists -*/ -/* Opcode: NotFound P1 P2 P3 * * -** -** Register P3 holds a blob constructed by MakeRecord. P1 is -** an index. If no entry exists in P1 that matches the blob then jump -** to P2. If an entry does existing, fall through. The cursor is left -** pointing to the entry that matches. +** Cursor P1 is on an index btree. If the record identified by P3 and P4 +** is a prefix of any entry in P1 then a jump is made to P2 and +** P1 is left pointing at the matching entry. +*/ +/* Opcode: NotFound P1 P2 P3 P4 * +** +** If P4==0 then register P3 holds a blob constructed by MakeRecord. If +** P4>0 then register P3 is the first of P4 registers that form an unpacked +** record. +** +** Cursor P1 is on an index btree. If the record identified by P3 and P4 +** is not the prefix of any entry in P1 then a jump is made to P2. If P1 +** does contain an entry whose prefix matches the P3/P4 record then control +** falls through to the next instruction and P1 is left pointing at the +** matching entry. ** ** See also: Found, NotExists, IsUnique */ case OP_NotFound: /* jump, in3 */ case OP_Found: { /* jump, in3 */ -#if 0 /* local variables moved into u.bc */ +#if 0 /* local variables moved into u.bb */ int alreadyExists; VdbeCursor *pC; int res; UnpackedRecord *pIdxKey; + UnpackedRecord r; char aTempRec[ROUND8(sizeof(UnpackedRecord)) + sizeof(Mem)*3 + 7]; -#endif /* local variables moved into u.bc */ +#endif /* local variables moved into u.bb */ + +#ifdef SQLITE_TEST + sqlite3_found_count++; +#endif - u.bc.alreadyExists = 0; + u.bb.alreadyExists = 0; assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bc.pC = p->apCsr[pOp->p1]; - assert( u.bc.pC!=0 ); - if( ALWAYS(u.bc.pC->pCursor!=0) ){ - - assert( u.bc.pC->isTable==0 ); - assert( pIn3->flags & MEM_Blob ); - u.bc.pIdxKey = sqlite3VdbeRecordUnpack(u.bc.pC->pKeyInfo, pIn3->n, pIn3->z, - u.bc.aTempRec, sizeof(u.bc.aTempRec)); - if( u.bc.pIdxKey==0 ){ - goto no_mem; - } - if( pOp->opcode==OP_Found ){ - u.bc.pIdxKey->flags |= UNPACKED_PREFIX_MATCH; + assert( pOp->p4type==P4_INT32 ); + u.bb.pC = p->apCsr[pOp->p1]; + assert( u.bb.pC!=0 ); + pIn3 = &aMem[pOp->p3]; + if( ALWAYS(u.bb.pC->pCursor!=0) ){ + + assert( u.bb.pC->isTable==0 ); + if( pOp->p4.i>0 ){ + u.bb.r.pKeyInfo = u.bb.pC->pKeyInfo; + u.bb.r.nField = (u16)pOp->p4.i; + u.bb.r.aMem = pIn3; + u.bb.r.flags = UNPACKED_PREFIX_MATCH; + u.bb.pIdxKey = &u.bb.r; + }else{ + assert( pIn3->flags & MEM_Blob ); + ExpandBlob(pIn3); + u.bb.pIdxKey = sqlite3VdbeRecordUnpack(u.bb.pC->pKeyInfo, pIn3->n, pIn3->z, + u.bb.aTempRec, sizeof(u.bb.aTempRec)); + if( u.bb.pIdxKey==0 ){ + goto no_mem; + } + u.bb.pIdxKey->flags |= UNPACKED_PREFIX_MATCH; + } + rc = sqlite3BtreeMovetoUnpacked(u.bb.pC->pCursor, u.bb.pIdxKey, 0, 0, &u.bb.res); + if( pOp->p4.i==0 ){ + sqlite3VdbeDeleteUnpackedRecord(u.bb.pIdxKey); } - rc = sqlite3BtreeMovetoUnpacked(u.bc.pC->pCursor, u.bc.pIdxKey, 0, 0, &u.bc.res); - sqlite3VdbeDeleteUnpackedRecord(u.bc.pIdxKey); if( rc!=SQLITE_OK ){ break; } - u.bc.alreadyExists = (u.bc.res==0); - u.bc.pC->deferredMoveto = 0; - u.bc.pC->cacheStatus = CACHE_STALE; + u.bb.alreadyExists = (u.bb.res==0); + u.bb.pC->deferredMoveto = 0; + u.bb.pC->cacheStatus = CACHE_STALE; } if( pOp->opcode==OP_Found ){ - if( u.bc.alreadyExists ) pc = pOp->p2 - 1; + if( u.bb.alreadyExists ) pc = pOp->p2 - 1; }else{ - if( !u.bc.alreadyExists ) pc = pOp->p2 - 1; + if( !u.bb.alreadyExists ) pc = pOp->p2 - 1; } break; } /* Opcode: IsUnique P1 P2 P3 P4 * ** -** Cursor P1 is open on an index. So it has no data and its key consists -** of a record generated by OP_MakeRecord where the last field is the -** rowid of the entry that the index refers to. +** Cursor P1 is open on an index b-tree - that is to say, a btree which +** no data and where the key are records generated by OP_MakeRecord with +** the list field being the integer ROWID of the entry that the index +** entry refers to. ** ** The P3 register contains an integer record number. Call this record ** number R. Register P4 is the first in a set of N contiguous registers @@ -54224,59 +55643,60 @@ ** See also: NotFound, NotExists, Found */ case OP_IsUnique: { /* jump, in3 */ -#if 0 /* local variables moved into u.bd */ +#if 0 /* local variables moved into u.bc */ u16 ii; VdbeCursor *pCx; BtCursor *pCrsr; u16 nField; - Mem *aMem; + Mem *aMx; UnpackedRecord r; /* B-Tree index search key */ i64 R; /* Rowid stored in register P3 */ -#endif /* local variables moved into u.bd */ +#endif /* local variables moved into u.bc */ - u.bd.aMem = &p->aMem[pOp->p4.i]; + pIn3 = &aMem[pOp->p3]; + u.bc.aMx = &aMem[pOp->p4.i]; /* Assert that the values of parameters P1 and P4 are in range. */ assert( pOp->p4type==P4_INT32 ); assert( pOp->p4.i>0 && pOp->p4.i<=p->nMem ); assert( pOp->p1>=0 && pOp->p1nCursor ); /* Find the index cursor. */ - u.bd.pCx = p->apCsr[pOp->p1]; - assert( u.bd.pCx->deferredMoveto==0 ); - u.bd.pCx->seekResult = 0; - u.bd.pCx->cacheStatus = CACHE_STALE; - u.bd.pCrsr = u.bd.pCx->pCursor; + u.bc.pCx = p->apCsr[pOp->p1]; + assert( u.bc.pCx->deferredMoveto==0 ); + u.bc.pCx->seekResult = 0; + u.bc.pCx->cacheStatus = CACHE_STALE; + u.bc.pCrsr = u.bc.pCx->pCursor; /* If any of the values are NULL, take the jump. */ - u.bd.nField = u.bd.pCx->pKeyInfo->nField; - for(u.bd.ii=0; u.bd.iipKeyInfo->nField; + for(u.bc.ii=0; u.bc.iip2 - 1; - u.bd.pCrsr = 0; + u.bc.pCrsr = 0; break; } } - assert( (u.bd.aMem[u.bd.nField].flags & MEM_Null)==0 ); + assert( (u.bc.aMx[u.bc.nField].flags & MEM_Null)==0 ); - if( u.bd.pCrsr!=0 ){ + if( u.bc.pCrsr!=0 ){ /* Populate the index search key. */ - u.bd.r.pKeyInfo = u.bd.pCx->pKeyInfo; - u.bd.r.nField = u.bd.nField + 1; - u.bd.r.flags = UNPACKED_PREFIX_SEARCH; - u.bd.r.aMem = u.bd.aMem; + u.bc.r.pKeyInfo = u.bc.pCx->pKeyInfo; + u.bc.r.nField = u.bc.nField + 1; + u.bc.r.flags = UNPACKED_PREFIX_SEARCH; + u.bc.r.aMem = u.bc.aMx; - /* Extract the value of u.bd.R from register P3. */ + /* Extract the value of u.bc.R from register P3. */ sqlite3VdbeMemIntegerify(pIn3); - u.bd.R = pIn3->u.i; + u.bc.R = pIn3->u.i; /* Search the B-Tree index. If no conflicting record is found, jump ** to P2. Otherwise, copy the rowid of the conflicting record to ** register P3 and fall through to the next instruction. */ - rc = sqlite3BtreeMovetoUnpacked(u.bd.pCrsr, &u.bd.r, 0, 0, &u.bd.pCx->seekResult); - if( (u.bd.r.flags & UNPACKED_PREFIX_SEARCH) || u.bd.r.rowid==u.bd.R ){ + rc = sqlite3BtreeMovetoUnpacked(u.bc.pCrsr, &u.bc.r, 0, 0, &u.bc.pCx->seekResult); + if( (u.bc.r.flags & UNPACKED_PREFIX_SEARCH) || u.bc.r.rowid==u.bc.R ){ pc = pOp->p2 - 1; }else{ - pIn3->u.i = u.bd.r.rowid; + pIn3->u.i = u.bc.r.rowid; } } break; @@ -54297,42 +55717,42 @@ ** See also: Found, NotFound, IsUnique */ case OP_NotExists: { /* jump, in3 */ -#if 0 /* local variables moved into u.be */ +#if 0 /* local variables moved into u.bd */ VdbeCursor *pC; BtCursor *pCrsr; int res; u64 iKey; -#endif /* local variables moved into u.be */ +#endif /* local variables moved into u.bd */ + pIn3 = &aMem[pOp->p3]; assert( pIn3->flags & MEM_Int ); assert( pOp->p1>=0 && pOp->p1nCursor ); - u.be.pC = p->apCsr[pOp->p1]; - assert( u.be.pC!=0 ); - assert( u.be.pC->isTable ); - u.be.pCrsr = u.be.pC->pCursor; - if( u.be.pCrsr!=0 ){ - u.be.res = 0; - u.be.iKey = pIn3->u.i; - rc = sqlite3BtreeMovetoUnpacked(u.be.pCrsr, 0, u.be.iKey, 0, &u.be.res); - u.be.pC->lastRowid = pIn3->u.i; - u.be.pC->rowidIsValid = u.be.res==0 ?1:0; - u.be.pC->nullRow = 0; - u.be.pC->cacheStatus = CACHE_STALE; - u.be.pC->deferredMoveto = 0; - if( u.be.res!=0 ){ + u.bd.pC = p->apCsr[pOp->p1]; + assert( u.bd.pC!=0 ); + assert( u.bd.pC->isTable ); + assert( u.bd.pC->pseudoTableReg==0 ); + u.bd.pCrsr = u.bd.pC->pCursor; + if( u.bd.pCrsr!=0 ){ + u.bd.res = 0; + u.bd.iKey = pIn3->u.i; + rc = sqlite3BtreeMovetoUnpacked(u.bd.pCrsr, 0, u.bd.iKey, 0, &u.bd.res); + u.bd.pC->lastRowid = pIn3->u.i; + u.bd.pC->rowidIsValid = u.bd.res==0 ?1:0; + u.bd.pC->nullRow = 0; + u.bd.pC->cacheStatus = CACHE_STALE; + u.bd.pC->deferredMoveto = 0; + if( u.bd.res!=0 ){ pc = pOp->p2 - 1; - assert( u.be.pC->rowidIsValid==0 ); + assert( u.bd.pC->rowidIsValid==0 ); } - u.be.pC->seekResult = u.be.res; + u.bd.pC->seekResult = u.bd.res; }else{ /* This happens when an attempt to open a read cursor on the ** sqlite_master table returns SQLITE_EMPTY. */ - assert( !u.be.pC->pseudoTable ); - assert( u.be.pC->isTable ); pc = pOp->p2 - 1; - assert( u.be.pC->rowidIsValid==0 ); - u.be.pC->seekResult = 0; + assert( u.bd.pC->rowidIsValid==0 ); + u.bd.pC->seekResult = 0; } break; } @@ -54348,7 +55768,6 @@ assert( pOp->p1>=0 && pOp->p1nCursor ); assert( p->apCsr[pOp->p1]!=0 ); pOut->u.i = p->apCsr[pOp->p1]->seqCount++; - MemSetTypeFlag(pOut, MEM_Int); break; } @@ -54360,28 +55779,29 @@ ** table that cursor P1 points to. The new record number is written ** written to register P2. ** -** If P3>0 then P3 is a register that holds the largest previously -** generated record number. No new record numbers are allowed to be less -** than this value. When this value reaches its maximum, a SQLITE_FULL -** error is generated. The P3 register is updated with the generated -** record number. This P3 mechanism is used to help implement the +** If P3>0 then P3 is a register in the root frame of this VDBE that holds +** the largest previously generated record number. No new record numbers are +** allowed to be less than this value. When this value reaches its maximum, +** a SQLITE_FULL error is generated. The P3 register is updated with the ' +** generated record number. This P3 mechanism is used to help implement the ** AUTOINCREMENT feature. */ case OP_NewRowid: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bf */ +#if 0 /* local variables moved into u.be */ i64 v; /* The new rowid */ VdbeCursor *pC; /* Cursor of table to get the new rowid */ int res; /* Result of an sqlite3BtreeLast() */ int cnt; /* Counter to limit the number of searches */ Mem *pMem; /* Register holding largest rowid for AUTOINCREMENT */ -#endif /* local variables moved into u.bf */ + VdbeFrame *pFrame; /* Root frame of VDBE */ +#endif /* local variables moved into u.be */ - u.bf.v = 0; - u.bf.res = 0; + u.be.v = 0; + u.be.res = 0; assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bf.pC = p->apCsr[pOp->p1]; - assert( u.bf.pC!=0 ); - if( NEVER(u.bf.pC->pCursor==0) ){ + u.be.pC = p->apCsr[pOp->p1]; + assert( u.be.pC!=0 ); + if( NEVER(u.be.pC->pCursor==0) ){ /* The zero initialization above is all that is needed */ }else{ /* The next rowid or record number (different terms for the same @@ -54397,8 +55817,8 @@ ** succeeded. If the random rowid does exist, we select a new one ** and try again, up to 100 times. */ - assert( u.bf.pC->isTable ); - u.bf.cnt = 0; + assert( u.be.pC->isTable ); + u.be.cnt = 0; #ifdef SQLITE_32BIT_ROWID # define MAX_ROWID 0x7fffffff @@ -54410,71 +55830,88 @@ # define MAX_ROWID (i64)( (((u64)0x7fffffff)<<32) | (u64)0xffffffff ) #endif - if( !u.bf.pC->useRandomRowid ){ - u.bf.v = sqlite3BtreeGetCachedRowid(u.bf.pC->pCursor); - if( u.bf.v==0 ){ - rc = sqlite3BtreeLast(u.bf.pC->pCursor, &u.bf.res); + if( !u.be.pC->useRandomRowid ){ + u.be.v = sqlite3BtreeGetCachedRowid(u.be.pC->pCursor); + if( u.be.v==0 ){ + rc = sqlite3BtreeLast(u.be.pC->pCursor, &u.be.res); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } - if( u.bf.res ){ - u.bf.v = 1; + if( u.be.res ){ + u.be.v = 1; /* IMP: R-61914-48074 */ }else{ - sqlite3BtreeKeySize(u.bf.pC->pCursor, &u.bf.v); - if( u.bf.v==MAX_ROWID ){ - u.bf.pC->useRandomRowid = 1; + assert( sqlite3BtreeCursorIsValid(u.be.pC->pCursor) ); + rc = sqlite3BtreeKeySize(u.be.pC->pCursor, &u.be.v); + assert( rc==SQLITE_OK ); /* Cannot fail following BtreeLast() */ + if( u.be.v==MAX_ROWID ){ + u.be.pC->useRandomRowid = 1; }else{ - u.bf.v++; + u.be.v++; /* IMP: R-29538-34987 */ } } } #ifndef SQLITE_OMIT_AUTOINCREMENT if( pOp->p3 ){ - assert( pOp->p3>0 && pOp->p3<=p->nMem ); /* P3 is a valid memory cell */ - u.bf.pMem = &p->aMem[pOp->p3]; - REGISTER_TRACE(pOp->p3, u.bf.pMem); - sqlite3VdbeMemIntegerify(u.bf.pMem); - assert( (u.bf.pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ - if( u.bf.pMem->u.i==MAX_ROWID || u.bf.pC->useRandomRowid ){ - rc = SQLITE_FULL; + /* Assert that P3 is a valid memory cell. */ + assert( pOp->p3>0 ); + if( p->pFrame ){ + for(u.be.pFrame=p->pFrame; u.be.pFrame->pParent; u.be.pFrame=u.be.pFrame->pParent); + /* Assert that P3 is a valid memory cell. */ + assert( pOp->p3<=u.be.pFrame->nMem ); + u.be.pMem = &u.be.pFrame->aMem[pOp->p3]; + }else{ + /* Assert that P3 is a valid memory cell. */ + assert( pOp->p3<=p->nMem ); + u.be.pMem = &aMem[pOp->p3]; + } + + REGISTER_TRACE(pOp->p3, u.be.pMem); + sqlite3VdbeMemIntegerify(u.be.pMem); + assert( (u.be.pMem->flags & MEM_Int)!=0 ); /* mem(P3) holds an integer */ + if( u.be.pMem->u.i==MAX_ROWID || u.be.pC->useRandomRowid ){ + rc = SQLITE_FULL; /* IMP: R-12275-61338 */ goto abort_due_to_error; } - if( u.bf.vu.i+1 ){ - u.bf.v = u.bf.pMem->u.i + 1; + if( u.be.vu.i+1 ){ + u.be.v = u.be.pMem->u.i + 1; } - u.bf.pMem->u.i = u.bf.v; + u.be.pMem->u.i = u.be.v; } #endif - sqlite3BtreeSetCachedRowid(u.bf.pC->pCursor, u.bf.vpCursor, u.be.vuseRandomRowid ){ + if( u.be.pC->useRandomRowid ){ + /* IMPLEMENTATION-OF: R-48598-02938 If the largest ROWID is equal to the + ** largest possible integer (9223372036854775807) then the database + ** engine starts picking candidate ROWIDs at random until it finds one + ** that is not previously used. + */ assert( pOp->p3==0 ); /* We cannot be in random rowid mode if this is ** an AUTOINCREMENT table. */ - u.bf.v = db->lastRowid; - u.bf.cnt = 0; + u.be.v = db->lastRowid; + u.be.cnt = 0; do{ - if( u.bf.cnt==0 && (u.bf.v&0xffffff)==u.bf.v ){ - u.bf.v++; + if( u.be.cnt==0 && (u.be.v&0xffffff)==u.be.v ){ + u.be.v++; }else{ - sqlite3_randomness(sizeof(u.bf.v), &u.bf.v); - if( u.bf.cnt<5 ) u.bf.v &= 0xffffff; + sqlite3_randomness(sizeof(u.be.v), &u.be.v); + if( u.be.cnt<5 ) u.be.v &= 0xffffff; } - rc = sqlite3BtreeMovetoUnpacked(u.bf.pC->pCursor, 0, (u64)u.bf.v, 0, &u.bf.res); - u.bf.cnt++; - }while( u.bf.cnt<100 && rc==SQLITE_OK && u.bf.res==0 ); - if( rc==SQLITE_OK && u.bf.res==0 ){ - rc = SQLITE_FULL; + rc = sqlite3BtreeMovetoUnpacked(u.be.pC->pCursor, 0, (u64)u.be.v, 0, &u.be.res); + u.be.cnt++; + }while( u.be.cnt<100 && rc==SQLITE_OK && u.be.res==0 ); + if( rc==SQLITE_OK && u.be.res==0 ){ + rc = SQLITE_FULL; /* IMP: R-38219-53002 */ goto abort_due_to_error; } } - u.bf.pC->rowidIsValid = 0; - u.bf.pC->deferredMoveto = 0; - u.bf.pC->cacheStatus = CACHE_STALE; + u.be.pC->rowidIsValid = 0; + u.be.pC->deferredMoveto = 0; + u.be.pC->cacheStatus = CACHE_STALE; } - MemSetTypeFlag(pOut, MEM_Int); - pOut->u.i = u.bf.v; + pOut->u.i = u.be.v; break; } @@ -54482,15 +55919,28 @@ ** ** Write an entry into the table of cursor P1. A new entry is ** created if it doesn't already exist or the data for an existing -** entry is overwritten. The data is the value stored register +** entry is overwritten. The data is the value MEM_Blob stored in register ** number P2. The key is stored in register P3. The key must -** be an integer. +** be a MEM_Int. ** ** If the OPFLAG_NCHANGE flag of P5 is set, then the row change count is ** incremented (otherwise not). If the OPFLAG_LASTROWID flag of P5 is set, ** then rowid is stored for subsequent return by the ** sqlite3_last_insert_rowid() function (otherwise it is unmodified). ** +** If the OPFLAG_USESEEKRESULT flag of P5 is set and if the result of +** the last seek operation (OP_NotExists) was a success, then this +** operation will not attempt to find the appropriate row before doing +** the insert but will instead overwrite the row that the cursor is +** currently pointing to. Presumably, the prior OP_NotExists opcode +** has already positioned the cursor correctly. This is an optimization +** that boosts performance by avoiding redundant seeks. +** +** If the OPFLAG_ISUPDATE flag is set, then this opcode is part of an +** UPDATE operation. Otherwise (if the flag is clear) then this opcode +** is part of an INSERT operation. The difference is only important to +** the update hook. +** ** Parameter P4 may point to a string containing the table-name, or ** may be NULL. If it is not NULL, then the update-hook ** (sqlite3.xUpdateCallback) is invoked following a successful insert. @@ -54504,86 +55954,75 @@ ** This instruction only works on tables. The equivalent instruction ** for indices is OP_IdxInsert. */ -case OP_Insert: { -#if 0 /* local variables moved into u.bg */ - Mem *pData; - Mem *pKey; - i64 iKey; /* The integer ROWID or key for the record to be inserted */ - VdbeCursor *pC; - int nZero; - int seekResult; - const char *zDb; - const char *zTbl; - int op; -#endif /* local variables moved into u.bg */ +/* Opcode: InsertInt P1 P2 P3 P4 P5 +** +** This works exactly like OP_Insert except that the key is the +** integer value P3, not the value of the integer stored in register P3. +*/ +case OP_Insert: +case OP_InsertInt: { +#if 0 /* local variables moved into u.bf */ + Mem *pData; /* MEM cell holding data for the record to be inserted */ + Mem *pKey; /* MEM cell holding key for the record */ + i64 iKey; /* The integer ROWID or key for the record to be inserted */ + VdbeCursor *pC; /* Cursor to table into which insert is written */ + int nZero; /* Number of zero-bytes to append */ + int seekResult; /* Result of prior seek or 0 if no USESEEKRESULT flag */ + const char *zDb; /* database name - used by the update hook */ + const char *zTbl; /* Table name - used by the opdate hook */ + int op; /* Opcode for update hook: SQLITE_UPDATE or SQLITE_INSERT */ +#endif /* local variables moved into u.bf */ - u.bg.pData = &p->aMem[pOp->p2]; - u.bg.pKey = &p->aMem[pOp->p3]; + u.bf.pData = &aMem[pOp->p2]; assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bg.pC = p->apCsr[pOp->p1]; - assert( u.bg.pC!=0 ); - assert( u.bg.pC->pCursor!=0 || u.bg.pC->pseudoTable ); - assert( u.bg.pKey->flags & MEM_Int ); - assert( u.bg.pC->isTable ); - REGISTER_TRACE(pOp->p2, u.bg.pData); - REGISTER_TRACE(pOp->p3, u.bg.pKey); - - u.bg.iKey = u.bg.pKey->u.i; - if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; - if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = u.bg.pKey->u.i; - if( u.bg.pData->flags & MEM_Null ){ - u.bg.pData->z = 0; - u.bg.pData->n = 0; - }else{ - assert( u.bg.pData->flags & (MEM_Blob|MEM_Str) ); - } - if( u.bg.pC->pseudoTable ){ - if( !u.bg.pC->ephemPseudoTable ){ - sqlite3DbFree(db, u.bg.pC->pData); - } - u.bg.pC->iKey = u.bg.iKey; - u.bg.pC->nData = u.bg.pData->n; - if( u.bg.pC->ephemPseudoTable || u.bg.pData->z==u.bg.pData->zMalloc ){ - u.bg.pC->pData = u.bg.pData->z; - if( !u.bg.pC->ephemPseudoTable ){ - u.bg.pData->flags &= ~MEM_Dyn; - u.bg.pData->flags |= MEM_Ephem; - u.bg.pData->zMalloc = 0; - } - }else{ - u.bg.pC->pData = sqlite3Malloc( u.bg.pC->nData+2 ); - if( !u.bg.pC->pData ) goto no_mem; - memcpy(u.bg.pC->pData, u.bg.pData->z, u.bg.pC->nData); - u.bg.pC->pData[u.bg.pC->nData] = 0; - u.bg.pC->pData[u.bg.pC->nData+1] = 0; - } - u.bg.pC->nullRow = 0; - }else{ - u.bg.seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bg.pC->seekResult : 0); - if( u.bg.pData->flags & MEM_Zero ){ - u.bg.nZero = u.bg.pData->u.nZero; - }else{ - u.bg.nZero = 0; - } - sqlite3BtreeSetCachedRowid(u.bg.pC->pCursor, 0); - rc = sqlite3BtreeInsert(u.bg.pC->pCursor, 0, u.bg.iKey, - u.bg.pData->z, u.bg.pData->n, u.bg.nZero, - pOp->p5 & OPFLAG_APPEND, u.bg.seekResult - ); + u.bf.pC = p->apCsr[pOp->p1]; + assert( u.bf.pC!=0 ); + assert( u.bf.pC->pCursor!=0 ); + assert( u.bf.pC->pseudoTableReg==0 ); + assert( u.bf.pC->isTable ); + REGISTER_TRACE(pOp->p2, u.bf.pData); + + if( pOp->opcode==OP_Insert ){ + u.bf.pKey = &aMem[pOp->p3]; + assert( u.bf.pKey->flags & MEM_Int ); + REGISTER_TRACE(pOp->p3, u.bf.pKey); + u.bf.iKey = u.bf.pKey->u.i; + }else{ + assert( pOp->opcode==OP_InsertInt ); + u.bf.iKey = pOp->p3; } - u.bg.pC->rowidIsValid = 0; - u.bg.pC->deferredMoveto = 0; - u.bg.pC->cacheStatus = CACHE_STALE; + if( pOp->p5 & OPFLAG_NCHANGE ) p->nChange++; + if( pOp->p5 & OPFLAG_LASTROWID ) db->lastRowid = u.bf.iKey; + if( u.bf.pData->flags & MEM_Null ){ + u.bf.pData->z = 0; + u.bf.pData->n = 0; + }else{ + assert( u.bf.pData->flags & (MEM_Blob|MEM_Str) ); + } + u.bf.seekResult = ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bf.pC->seekResult : 0); + if( u.bf.pData->flags & MEM_Zero ){ + u.bf.nZero = u.bf.pData->u.nZero; + }else{ + u.bf.nZero = 0; + } + sqlite3BtreeSetCachedRowid(u.bf.pC->pCursor, 0); + rc = sqlite3BtreeInsert(u.bf.pC->pCursor, 0, u.bf.iKey, + u.bf.pData->z, u.bf.pData->n, u.bf.nZero, + pOp->p5 & OPFLAG_APPEND, u.bf.seekResult + ); + u.bf.pC->rowidIsValid = 0; + u.bf.pC->deferredMoveto = 0; + u.bf.pC->cacheStatus = CACHE_STALE; /* Invoke the update-hook if required. */ if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){ - u.bg.zDb = db->aDb[u.bg.pC->iDb].zName; - u.bg.zTbl = pOp->p4.z; - u.bg.op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT); - assert( u.bg.pC->isTable ); - db->xUpdateCallback(db->pUpdateArg, u.bg.op, u.bg.zDb, u.bg.zTbl, u.bg.iKey); - assert( u.bg.pC->iDb>=0 ); + u.bf.zDb = db->aDb[u.bf.pC->iDb].zName; + u.bf.zTbl = pOp->p4.z; + u.bf.op = ((pOp->p5 & OPFLAG_ISUPDATE) ? SQLITE_UPDATE : SQLITE_INSERT); + assert( u.bf.pC->isTable ); + db->xUpdateCallback(db->pUpdateArg, u.bf.op, u.bf.zDb, u.bf.zTbl, u.bf.iKey); + assert( u.bf.pC->iDb>=0 ); } break; } @@ -54609,63 +56048,60 @@ ** using OP_NotFound prior to invoking this opcode. */ case OP_Delete: { -#if 0 /* local variables moved into u.bh */ +#if 0 /* local variables moved into u.bg */ i64 iKey; VdbeCursor *pC; -#endif /* local variables moved into u.bh */ +#endif /* local variables moved into u.bg */ - u.bh.iKey = 0; + u.bg.iKey = 0; assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bh.pC = p->apCsr[pOp->p1]; - assert( u.bh.pC!=0 ); - assert( u.bh.pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */ + u.bg.pC = p->apCsr[pOp->p1]; + assert( u.bg.pC!=0 ); + assert( u.bg.pC->pCursor!=0 ); /* Only valid for real tables, no pseudotables */ - /* If the update-hook will be invoked, set u.bh.iKey to the rowid of the + /* If the update-hook will be invoked, set u.bg.iKey to the rowid of the ** row being deleted. */ if( db->xUpdateCallback && pOp->p4.z ){ - assert( u.bh.pC->isTable ); - assert( u.bh.pC->rowidIsValid ); /* lastRowid set by previous OP_NotFound */ - u.bh.iKey = u.bh.pC->lastRowid; + assert( u.bg.pC->isTable ); + assert( u.bg.pC->rowidIsValid ); /* lastRowid set by previous OP_NotFound */ + u.bg.iKey = u.bg.pC->lastRowid; } /* The OP_Delete opcode always follows an OP_NotExists or OP_Last or ** OP_Column on the same table without any intervening operations that - ** might move or invalidate the cursor. Hence cursor u.bh.pC is always pointing + ** might move or invalidate the cursor. Hence cursor u.bg.pC is always pointing ** to the row to be deleted and the sqlite3VdbeCursorMoveto() operation ** below is always a no-op and cannot fail. We will run it anyhow, though, ** to guard against future changes to the code generator. **/ - assert( u.bh.pC->deferredMoveto==0 ); - rc = sqlite3VdbeCursorMoveto(u.bh.pC); + assert( u.bg.pC->deferredMoveto==0 ); + rc = sqlite3VdbeCursorMoveto(u.bg.pC); if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; - sqlite3BtreeSetCachedRowid(u.bh.pC->pCursor, 0); - rc = sqlite3BtreeDelete(u.bh.pC->pCursor); - u.bh.pC->cacheStatus = CACHE_STALE; + sqlite3BtreeSetCachedRowid(u.bg.pC->pCursor, 0); + rc = sqlite3BtreeDelete(u.bg.pC->pCursor); + u.bg.pC->cacheStatus = CACHE_STALE; /* Invoke the update-hook if required. */ if( rc==SQLITE_OK && db->xUpdateCallback && pOp->p4.z ){ - const char *zDb = db->aDb[u.bh.pC->iDb].zName; + const char *zDb = db->aDb[u.bg.pC->iDb].zName; const char *zTbl = pOp->p4.z; - db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, zTbl, u.bh.iKey); - assert( u.bh.pC->iDb>=0 ); + db->xUpdateCallback(db->pUpdateArg, SQLITE_DELETE, zDb, zTbl, u.bg.iKey); + assert( u.bg.pC->iDb>=0 ); } if( pOp->p2 & OPFLAG_NCHANGE ) p->nChange++; break; } - -/* Opcode: ResetCount P1 * * +/* Opcode: ResetCount * * * * * ** -** This opcode resets the VMs internal change counter to 0. If P1 is true, -** then the value of the change counter is copied to the database handle -** change counter (returned by subsequent calls to sqlite3_changes()) -** before it is reset. This is used by trigger programs. +** The value of the change counter is copied to the database handle +** change counter (returned by subsequent calls to sqlite3_changes()). +** Then the VMs internal change counter resets to 0. +** This is used by trigger programs. */ case OP_ResetCount: { - if( pOp->p1 ){ - sqlite3VdbeSetChanges(db, p->nChange); - } + sqlite3VdbeSetChanges(db, p->nChange); p->nChange = 0; break; } @@ -54692,57 +56128,60 @@ */ case OP_RowKey: case OP_RowData: { -#if 0 /* local variables moved into u.bi */ +#if 0 /* local variables moved into u.bh */ VdbeCursor *pC; BtCursor *pCrsr; u32 n; i64 n64; -#endif /* local variables moved into u.bi */ +#endif /* local variables moved into u.bh */ - pOut = &p->aMem[pOp->p2]; + pOut = &aMem[pOp->p2]; /* Note that RowKey and RowData are really exactly the same instruction */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bi.pC = p->apCsr[pOp->p1]; - assert( u.bi.pC->isTable || pOp->opcode==OP_RowKey ); - assert( u.bi.pC->isIndex || pOp->opcode==OP_RowData ); - assert( u.bi.pC!=0 ); - assert( u.bi.pC->nullRow==0 ); - assert( u.bi.pC->pseudoTable==0 ); - assert( u.bi.pC->pCursor!=0 ); - u.bi.pCrsr = u.bi.pC->pCursor; + u.bh.pC = p->apCsr[pOp->p1]; + assert( u.bh.pC->isTable || pOp->opcode==OP_RowKey ); + assert( u.bh.pC->isIndex || pOp->opcode==OP_RowData ); + assert( u.bh.pC!=0 ); + assert( u.bh.pC->nullRow==0 ); + assert( u.bh.pC->pseudoTableReg==0 ); + assert( u.bh.pC->pCursor!=0 ); + u.bh.pCrsr = u.bh.pC->pCursor; + assert( sqlite3BtreeCursorIsValid(u.bh.pCrsr) ); /* The OP_RowKey and OP_RowData opcodes always follow OP_NotExists or ** OP_Rewind/Op_Next with no intervening instructions that might invalidate ** the cursor. Hence the following sqlite3VdbeCursorMoveto() call is always ** a no-op and can never fail. But we leave it in place as a safety. */ - assert( u.bi.pC->deferredMoveto==0 ); - rc = sqlite3VdbeCursorMoveto(u.bi.pC); + assert( u.bh.pC->deferredMoveto==0 ); + rc = sqlite3VdbeCursorMoveto(u.bh.pC); if( NEVER(rc!=SQLITE_OK) ) goto abort_due_to_error; - if( u.bi.pC->isIndex ){ - assert( !u.bi.pC->isTable ); - sqlite3BtreeKeySize(u.bi.pCrsr, &u.bi.n64); - if( u.bi.n64>db->aLimit[SQLITE_LIMIT_LENGTH] ){ + if( u.bh.pC->isIndex ){ + assert( !u.bh.pC->isTable ); + rc = sqlite3BtreeKeySize(u.bh.pCrsr, &u.bh.n64); + assert( rc==SQLITE_OK ); /* True because of CursorMoveto() call above */ + if( u.bh.n64>db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } - u.bi.n = (u32)u.bi.n64; + u.bh.n = (u32)u.bh.n64; }else{ - sqlite3BtreeDataSize(u.bi.pCrsr, &u.bi.n); - if( u.bi.n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ + rc = sqlite3BtreeDataSize(u.bh.pCrsr, &u.bh.n); + assert( rc==SQLITE_OK ); /* DataSize() cannot fail */ + if( u.bh.n>(u32)db->aLimit[SQLITE_LIMIT_LENGTH] ){ goto too_big; } } - if( sqlite3VdbeMemGrow(pOut, u.bi.n, 0) ){ + if( sqlite3VdbeMemGrow(pOut, u.bh.n, 0) ){ goto no_mem; } - pOut->n = u.bi.n; + pOut->n = u.bh.n; MemSetTypeFlag(pOut, MEM_Blob); - if( u.bi.pC->isIndex ){ - rc = sqlite3BtreeKey(u.bi.pCrsr, 0, u.bi.n, pOut->z); + if( u.bh.pC->isIndex ){ + rc = sqlite3BtreeKey(u.bh.pCrsr, 0, u.bh.n, pOut->z); }else{ - rc = sqlite3BtreeData(u.bi.pCrsr, 0, u.bi.n, pOut->z); + rc = sqlite3BtreeData(u.bh.pCrsr, 0, u.bh.n, pOut->z); } pOut->enc = SQLITE_UTF8; /* In case the blob is ever cast to text */ UPDATE_MAX_BLOBSIZE(pOut); @@ -54759,47 +56198,46 @@ ** one opcode now works for both table types. */ case OP_Rowid: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bj */ +#if 0 /* local variables moved into u.bi */ VdbeCursor *pC; i64 v; sqlite3_vtab *pVtab; const sqlite3_module *pModule; -#endif /* local variables moved into u.bj */ +#endif /* local variables moved into u.bi */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bj.pC = p->apCsr[pOp->p1]; - assert( u.bj.pC!=0 ); - if( u.bj.pC->nullRow ){ - /* Do nothing so that reg[P2] remains NULL */ + u.bi.pC = p->apCsr[pOp->p1]; + assert( u.bi.pC!=0 ); + assert( u.bi.pC->pseudoTableReg==0 ); + if( u.bi.pC->nullRow ){ + pOut->flags = MEM_Null; break; - }else if( u.bj.pC->deferredMoveto ){ - u.bj.v = u.bj.pC->movetoTarget; - }else if( u.bj.pC->pseudoTable ){ - u.bj.v = u.bj.pC->iKey; + }else if( u.bi.pC->deferredMoveto ){ + u.bi.v = u.bi.pC->movetoTarget; #ifndef SQLITE_OMIT_VIRTUALTABLE - }else if( u.bj.pC->pVtabCursor ){ - u.bj.pVtab = u.bj.pC->pVtabCursor->pVtab; - u.bj.pModule = u.bj.pVtab->pModule; - assert( u.bj.pModule->xRowid ); + }else if( u.bi.pC->pVtabCursor ){ + u.bi.pVtab = u.bi.pC->pVtabCursor->pVtab; + u.bi.pModule = u.bi.pVtab->pModule; + assert( u.bi.pModule->xRowid ); if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; - rc = u.bj.pModule->xRowid(u.bj.pC->pVtabCursor, &u.bj.v); + rc = u.bi.pModule->xRowid(u.bi.pC->pVtabCursor, &u.bi.v); sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = u.bj.pVtab->zErrMsg; - u.bj.pVtab->zErrMsg = 0; + p->zErrMsg = u.bi.pVtab->zErrMsg; + u.bi.pVtab->zErrMsg = 0; if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; #endif /* SQLITE_OMIT_VIRTUALTABLE */ }else{ - rc = sqlite3VdbeCursorMoveto(u.bj.pC); + assert( u.bi.pC->pCursor!=0 ); + rc = sqlite3VdbeCursorMoveto(u.bi.pC); if( rc ) goto abort_due_to_error; - if( u.bj.pC->rowidIsValid ){ - u.bj.v = u.bj.pC->lastRowid; + if( u.bi.pC->rowidIsValid ){ + u.bi.v = u.bi.pC->lastRowid; }else{ - assert( u.bj.pC->pCursor!=0 ); - sqlite3BtreeKeySize(u.bj.pC->pCursor, &u.bj.v); + rc = sqlite3BtreeKeySize(u.bi.pC->pCursor, &u.bi.v); + assert( rc==SQLITE_OK ); /* Always so because of CursorMoveto() above */ } } - pOut->u.i = u.bj.v; - MemSetTypeFlag(pOut, MEM_Int); + pOut->u.i = u.bi.v; break; } @@ -54810,17 +56248,17 @@ ** write a NULL. */ case OP_NullRow: { -#if 0 /* local variables moved into u.bk */ +#if 0 /* local variables moved into u.bj */ VdbeCursor *pC; -#endif /* local variables moved into u.bk */ +#endif /* local variables moved into u.bj */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bk.pC = p->apCsr[pOp->p1]; - assert( u.bk.pC!=0 ); - u.bk.pC->nullRow = 1; - u.bk.pC->rowidIsValid = 0; - if( u.bk.pC->pCursor ){ - sqlite3BtreeClearCursor(u.bk.pC->pCursor); + u.bj.pC = p->apCsr[pOp->p1]; + assert( u.bj.pC!=0 ); + u.bj.pC->nullRow = 1; + u.bj.pC->rowidIsValid = 0; + if( u.bj.pC->pCursor ){ + sqlite3BtreeClearCursor(u.bj.pC->pCursor); } break; } @@ -54834,26 +56272,26 @@ ** to the following instruction. */ case OP_Last: { /* jump */ -#if 0 /* local variables moved into u.bl */ +#if 0 /* local variables moved into u.bk */ VdbeCursor *pC; BtCursor *pCrsr; int res; -#endif /* local variables moved into u.bl */ +#endif /* local variables moved into u.bk */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bl.pC = p->apCsr[pOp->p1]; - assert( u.bl.pC!=0 ); - u.bl.pCrsr = u.bl.pC->pCursor; - if( u.bl.pCrsr==0 ){ - u.bl.res = 1; + u.bk.pC = p->apCsr[pOp->p1]; + assert( u.bk.pC!=0 ); + u.bk.pCrsr = u.bk.pC->pCursor; + if( u.bk.pCrsr==0 ){ + u.bk.res = 1; }else{ - rc = sqlite3BtreeLast(u.bl.pCrsr, &u.bl.res); + rc = sqlite3BtreeLast(u.bk.pCrsr, &u.bk.res); } - u.bl.pC->nullRow = (u8)u.bl.res; - u.bl.pC->deferredMoveto = 0; - u.bl.pC->rowidIsValid = 0; - u.bl.pC->cacheStatus = CACHE_STALE; - if( pOp->p2>0 && u.bl.res ){ + u.bk.pC->nullRow = (u8)u.bk.res; + u.bk.pC->deferredMoveto = 0; + u.bk.pC->rowidIsValid = 0; + u.bk.pC->cacheStatus = CACHE_STALE; + if( pOp->p2>0 && u.bk.res ){ pc = pOp->p2 - 1; } break; @@ -54889,27 +56327,27 @@ ** to the following instruction. */ case OP_Rewind: { /* jump */ -#if 0 /* local variables moved into u.bm */ +#if 0 /* local variables moved into u.bl */ VdbeCursor *pC; BtCursor *pCrsr; int res; -#endif /* local variables moved into u.bm */ +#endif /* local variables moved into u.bl */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bm.pC = p->apCsr[pOp->p1]; - assert( u.bm.pC!=0 ); - if( (u.bm.pCrsr = u.bm.pC->pCursor)!=0 ){ - rc = sqlite3BtreeFirst(u.bm.pCrsr, &u.bm.res); - u.bm.pC->atFirst = u.bm.res==0 ?1:0; - u.bm.pC->deferredMoveto = 0; - u.bm.pC->cacheStatus = CACHE_STALE; - u.bm.pC->rowidIsValid = 0; + u.bl.pC = p->apCsr[pOp->p1]; + assert( u.bl.pC!=0 ); + if( (u.bl.pCrsr = u.bl.pC->pCursor)!=0 ){ + rc = sqlite3BtreeFirst(u.bl.pCrsr, &u.bl.res); + u.bl.pC->atFirst = u.bl.res==0 ?1:0; + u.bl.pC->deferredMoveto = 0; + u.bl.pC->cacheStatus = CACHE_STALE; + u.bl.pC->rowidIsValid = 0; }else{ - u.bm.res = 1; + u.bl.res = 1; } - u.bm.pC->nullRow = (u8)u.bm.res; + u.bl.pC->nullRow = (u8)u.bl.res; assert( pOp->p2>0 && pOp->p2nOp ); - if( u.bm.res ){ + if( u.bl.res ){ pc = pOp->p2 - 1; } break; @@ -54937,37 +56375,37 @@ */ case OP_Prev: /* jump */ case OP_Next: { /* jump */ -#if 0 /* local variables moved into u.bn */ +#if 0 /* local variables moved into u.bm */ VdbeCursor *pC; BtCursor *pCrsr; int res; -#endif /* local variables moved into u.bn */ +#endif /* local variables moved into u.bm */ CHECK_FOR_INTERRUPT; assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bn.pC = p->apCsr[pOp->p1]; - if( u.bn.pC==0 ){ + u.bm.pC = p->apCsr[pOp->p1]; + if( u.bm.pC==0 ){ break; /* See ticket #2273 */ } - u.bn.pCrsr = u.bn.pC->pCursor; - if( u.bn.pCrsr==0 ){ - u.bn.pC->nullRow = 1; + u.bm.pCrsr = u.bm.pC->pCursor; + if( u.bm.pCrsr==0 ){ + u.bm.pC->nullRow = 1; break; } - u.bn.res = 1; - assert( u.bn.pC->deferredMoveto==0 ); - rc = pOp->opcode==OP_Next ? sqlite3BtreeNext(u.bn.pCrsr, &u.bn.res) : - sqlite3BtreePrevious(u.bn.pCrsr, &u.bn.res); - u.bn.pC->nullRow = (u8)u.bn.res; - u.bn.pC->cacheStatus = CACHE_STALE; - if( u.bn.res==0 ){ + u.bm.res = 1; + assert( u.bm.pC->deferredMoveto==0 ); + rc = pOp->opcode==OP_Next ? sqlite3BtreeNext(u.bm.pCrsr, &u.bm.res) : + sqlite3BtreePrevious(u.bm.pCrsr, &u.bm.res); + u.bm.pC->nullRow = (u8)u.bm.res; + u.bm.pC->cacheStatus = CACHE_STALE; + if( u.bm.res==0 ){ pc = pOp->p2 - 1; if( pOp->p5 ) p->aCounter[pOp->p5-1]++; #ifdef SQLITE_TEST sqlite3_search_count++; #endif } - u.bn.pC->rowidIsValid = 0; + u.bm.pC->rowidIsValid = 0; break; } @@ -54984,29 +56422,30 @@ ** for tables is OP_Insert. */ case OP_IdxInsert: { /* in2 */ -#if 0 /* local variables moved into u.bo */ +#if 0 /* local variables moved into u.bn */ VdbeCursor *pC; BtCursor *pCrsr; int nKey; const char *zKey; -#endif /* local variables moved into u.bo */ +#endif /* local variables moved into u.bn */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bo.pC = p->apCsr[pOp->p1]; - assert( u.bo.pC!=0 ); + u.bn.pC = p->apCsr[pOp->p1]; + assert( u.bn.pC!=0 ); + pIn2 = &aMem[pOp->p2]; assert( pIn2->flags & MEM_Blob ); - u.bo.pCrsr = u.bo.pC->pCursor; - if( ALWAYS(u.bo.pCrsr!=0) ){ - assert( u.bo.pC->isTable==0 ); + u.bn.pCrsr = u.bn.pC->pCursor; + if( ALWAYS(u.bn.pCrsr!=0) ){ + assert( u.bn.pC->isTable==0 ); rc = ExpandBlob(pIn2); if( rc==SQLITE_OK ){ - u.bo.nKey = pIn2->n; - u.bo.zKey = pIn2->z; - rc = sqlite3BtreeInsert(u.bo.pCrsr, u.bo.zKey, u.bo.nKey, "", 0, 0, pOp->p3, - ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bo.pC->seekResult : 0) + u.bn.nKey = pIn2->n; + u.bn.zKey = pIn2->z; + rc = sqlite3BtreeInsert(u.bn.pCrsr, u.bn.zKey, u.bn.nKey, "", 0, 0, pOp->p3, + ((pOp->p5 & OPFLAG_USESEEKRESULT) ? u.bn.pC->seekResult : 0) ); - assert( u.bo.pC->deferredMoveto==0 ); - u.bo.pC->cacheStatus = CACHE_STALE; + assert( u.bn.pC->deferredMoveto==0 ); + u.bn.pC->cacheStatus = CACHE_STALE; } } break; @@ -55019,30 +56458,30 @@ ** index opened by cursor P1. */ case OP_IdxDelete: { -#if 0 /* local variables moved into u.bp */ +#if 0 /* local variables moved into u.bo */ VdbeCursor *pC; BtCursor *pCrsr; int res; UnpackedRecord r; -#endif /* local variables moved into u.bp */ +#endif /* local variables moved into u.bo */ assert( pOp->p3>0 ); assert( pOp->p2>0 && pOp->p2+pOp->p3<=p->nMem+1 ); assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bp.pC = p->apCsr[pOp->p1]; - assert( u.bp.pC!=0 ); - u.bp.pCrsr = u.bp.pC->pCursor; - if( ALWAYS(u.bp.pCrsr!=0) ){ - u.bp.r.pKeyInfo = u.bp.pC->pKeyInfo; - u.bp.r.nField = (u16)pOp->p3; - u.bp.r.flags = 0; - u.bp.r.aMem = &p->aMem[pOp->p2]; - rc = sqlite3BtreeMovetoUnpacked(u.bp.pCrsr, &u.bp.r, 0, 0, &u.bp.res); - if( rc==SQLITE_OK && u.bp.res==0 ){ - rc = sqlite3BtreeDelete(u.bp.pCrsr); + u.bo.pC = p->apCsr[pOp->p1]; + assert( u.bo.pC!=0 ); + u.bo.pCrsr = u.bo.pC->pCursor; + if( ALWAYS(u.bo.pCrsr!=0) ){ + u.bo.r.pKeyInfo = u.bo.pC->pKeyInfo; + u.bo.r.nField = (u16)pOp->p3; + u.bo.r.flags = 0; + u.bo.r.aMem = &aMem[pOp->p2]; + rc = sqlite3BtreeMovetoUnpacked(u.bo.pCrsr, &u.bo.r, 0, 0, &u.bo.res); + if( rc==SQLITE_OK && u.bo.res==0 ){ + rc = sqlite3BtreeDelete(u.bo.pCrsr); } - assert( u.bp.pC->deferredMoveto==0 ); - u.bp.pC->cacheStatus = CACHE_STALE; + assert( u.bo.pC->deferredMoveto==0 ); + u.bo.pC->cacheStatus = CACHE_STALE; } break; } @@ -55056,28 +56495,29 @@ ** See also: Rowid, MakeRecord. */ case OP_IdxRowid: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bq */ +#if 0 /* local variables moved into u.bp */ BtCursor *pCrsr; VdbeCursor *pC; i64 rowid; -#endif /* local variables moved into u.bq */ +#endif /* local variables moved into u.bp */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.bq.pC = p->apCsr[pOp->p1]; - assert( u.bq.pC!=0 ); - u.bq.pCrsr = u.bq.pC->pCursor; - if( ALWAYS(u.bq.pCrsr!=0) ){ - rc = sqlite3VdbeCursorMoveto(u.bq.pC); + u.bp.pC = p->apCsr[pOp->p1]; + assert( u.bp.pC!=0 ); + u.bp.pCrsr = u.bp.pC->pCursor; + pOut->flags = MEM_Null; + if( ALWAYS(u.bp.pCrsr!=0) ){ + rc = sqlite3VdbeCursorMoveto(u.bp.pC); if( NEVER(rc) ) goto abort_due_to_error; - assert( u.bq.pC->deferredMoveto==0 ); - assert( u.bq.pC->isTable==0 ); - if( !u.bq.pC->nullRow ){ - rc = sqlite3VdbeIdxRowid(db, u.bq.pCrsr, &u.bq.rowid); + assert( u.bp.pC->deferredMoveto==0 ); + assert( u.bp.pC->isTable==0 ); + if( !u.bp.pC->nullRow ){ + rc = sqlite3VdbeIdxRowid(db, u.bp.pCrsr, &u.bp.rowid); if( rc!=SQLITE_OK ){ goto abort_due_to_error; } - MemSetTypeFlag(pOut, MEM_Int); - pOut->u.i = u.bq.rowid; + pOut->u.i = u.bp.rowid; + pOut->flags = MEM_Int; } } break; @@ -55109,37 +56549,37 @@ ** If P5 is non-zero then the key value is increased by an epsilon prior ** to the comparison. This makes the opcode work like IdxLE. */ -case OP_IdxLT: /* jump, in3 */ -case OP_IdxGE: { /* jump, in3 */ -#if 0 /* local variables moved into u.br */ +case OP_IdxLT: /* jump */ +case OP_IdxGE: { /* jump */ +#if 0 /* local variables moved into u.bq */ VdbeCursor *pC; int res; UnpackedRecord r; -#endif /* local variables moved into u.br */ +#endif /* local variables moved into u.bq */ assert( pOp->p1>=0 && pOp->p1nCursor ); - u.br.pC = p->apCsr[pOp->p1]; - assert( u.br.pC!=0 ); - if( ALWAYS(u.br.pC->pCursor!=0) ){ - assert( u.br.pC->deferredMoveto==0 ); + u.bq.pC = p->apCsr[pOp->p1]; + assert( u.bq.pC!=0 ); + if( ALWAYS(u.bq.pC->pCursor!=0) ){ + assert( u.bq.pC->deferredMoveto==0 ); assert( pOp->p5==0 || pOp->p5==1 ); assert( pOp->p4type==P4_INT32 ); - u.br.r.pKeyInfo = u.br.pC->pKeyInfo; - u.br.r.nField = (u16)pOp->p4.i; + u.bq.r.pKeyInfo = u.bq.pC->pKeyInfo; + u.bq.r.nField = (u16)pOp->p4.i; if( pOp->p5 ){ - u.br.r.flags = UNPACKED_INCRKEY | UNPACKED_IGNORE_ROWID; + u.bq.r.flags = UNPACKED_INCRKEY | UNPACKED_IGNORE_ROWID; }else{ - u.br.r.flags = UNPACKED_IGNORE_ROWID; + u.bq.r.flags = UNPACKED_IGNORE_ROWID; } - u.br.r.aMem = &p->aMem[pOp->p3]; - rc = sqlite3VdbeIdxKeyCompare(u.br.pC, &u.br.r, &u.br.res); + u.bq.r.aMem = &aMem[pOp->p3]; + rc = sqlite3VdbeIdxKeyCompare(u.bq.pC, &u.bq.r, &u.bq.res); if( pOp->opcode==OP_IdxLT ){ - u.br.res = -u.br.res; + u.bq.res = -u.bq.res; }else{ assert( pOp->opcode==OP_IdxGE ); - u.br.res++; + u.bq.res++; } - if( u.br.res>0 ){ + if( u.bq.res>0 ){ pc = pOp->p2 - 1 ; } } @@ -55167,35 +56607,37 @@ ** See also: Clear */ case OP_Destroy: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bs */ +#if 0 /* local variables moved into u.br */ int iMoved; int iCnt; Vdbe *pVdbe; int iDb; -#endif /* local variables moved into u.bs */ +#endif /* local variables moved into u.br */ #ifndef SQLITE_OMIT_VIRTUALTABLE - u.bs.iCnt = 0; - for(u.bs.pVdbe=db->pVdbe; u.bs.pVdbe; u.bs.pVdbe = u.bs.pVdbe->pNext){ - if( u.bs.pVdbe->magic==VDBE_MAGIC_RUN && u.bs.pVdbe->inVtabMethod<2 && u.bs.pVdbe->pc>=0 ){ - u.bs.iCnt++; + u.br.iCnt = 0; + for(u.br.pVdbe=db->pVdbe; u.br.pVdbe; u.br.pVdbe = u.br.pVdbe->pNext){ + if( u.br.pVdbe->magic==VDBE_MAGIC_RUN && u.br.pVdbe->inVtabMethod<2 && u.br.pVdbe->pc>=0 ){ + u.br.iCnt++; } } #else - u.bs.iCnt = db->activeVdbeCnt; + u.br.iCnt = db->activeVdbeCnt; #endif - if( u.bs.iCnt>1 ){ + pOut->flags = MEM_Null; + if( u.br.iCnt>1 ){ rc = SQLITE_LOCKED; p->errorAction = OE_Abort; }else{ - u.bs.iDb = pOp->p3; - assert( u.bs.iCnt==1 ); - assert( (p->btreeMask & (1<aDb[u.bs.iDb].pBt, pOp->p1, &u.bs.iMoved); - MemSetTypeFlag(pOut, MEM_Int); - pOut->u.i = u.bs.iMoved; + u.br.iDb = pOp->p3; + assert( u.br.iCnt==1 ); + assert( (p->btreeMask & (1<aDb[u.br.iDb].pBt, pOp->p1, &u.br.iMoved); + pOut->flags = MEM_Int; + pOut->u.i = u.br.iMoved; #ifndef SQLITE_OMIT_AUTOVACUUM - if( rc==SQLITE_OK && u.bs.iMoved!=0 ){ - sqlite3RootPageMoved(&db->aDb[u.bs.iDb], u.bs.iMoved, pOp->p1); + if( rc==SQLITE_OK && u.br.iMoved!=0 ){ + sqlite3RootPageMoved(&db->aDb[u.br.iDb], u.br.iMoved, pOp->p1); + resetSchemaOnFault = 1; } #endif } @@ -55221,19 +56663,19 @@ ** See also: Destroy */ case OP_Clear: { -#if 0 /* local variables moved into u.bt */ +#if 0 /* local variables moved into u.bs */ int nChange; -#endif /* local variables moved into u.bt */ +#endif /* local variables moved into u.bs */ - u.bt.nChange = 0; + u.bs.nChange = 0; assert( (p->btreeMask & (1<p2))!=0 ); rc = sqlite3BtreeClearTable( - db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &u.bt.nChange : 0) + db->aDb[pOp->p2].pBt, pOp->p1, (pOp->p3 ? &u.bs.nChange : 0) ); if( pOp->p3 ){ - p->nChange += u.bt.nChange; + p->nChange += u.bs.nChange; if( pOp->p3>0 ){ - p->aMem[pOp->p3].u.i += u.bt.nChange; + aMem[pOp->p3].u.i += u.bs.nChange; } } break; @@ -55263,26 +56705,25 @@ */ case OP_CreateIndex: /* out2-prerelease */ case OP_CreateTable: { /* out2-prerelease */ -#if 0 /* local variables moved into u.bu */ +#if 0 /* local variables moved into u.bt */ int pgno; int flags; Db *pDb; -#endif /* local variables moved into u.bu */ +#endif /* local variables moved into u.bt */ - u.bu.pgno = 0; + u.bt.pgno = 0; assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (1<p1))!=0 ); - u.bu.pDb = &db->aDb[pOp->p1]; - assert( u.bu.pDb->pBt!=0 ); + u.bt.pDb = &db->aDb[pOp->p1]; + assert( u.bt.pDb->pBt!=0 ); if( pOp->opcode==OP_CreateTable ){ - /* u.bu.flags = BTREE_INTKEY; */ - u.bu.flags = BTREE_LEAFDATA|BTREE_INTKEY; + /* u.bt.flags = BTREE_INTKEY; */ + u.bt.flags = BTREE_LEAFDATA|BTREE_INTKEY; }else{ - u.bu.flags = BTREE_ZERODATA; + u.bt.flags = BTREE_ZERODATA; } - rc = sqlite3BtreeCreateTable(u.bu.pDb->pBt, &u.bu.pgno, u.bu.flags); - pOut->u.i = u.bu.pgno; - MemSetTypeFlag(pOut, MEM_Int); + rc = sqlite3BtreeCreateTable(u.bt.pDb->pBt, &u.bt.pgno, u.bt.flags); + pOut->u.i = u.bt.pgno; break; } @@ -55299,15 +56740,15 @@ ** then runs the new virtual machine. It is thus a re-entrant opcode. */ case OP_ParseSchema: { -#if 0 /* local variables moved into u.bv */ +#if 0 /* local variables moved into u.bu */ int iDb; const char *zMaster; char *zSql; InitData initData; -#endif /* local variables moved into u.bv */ +#endif /* local variables moved into u.bu */ - u.bv.iDb = pOp->p1; - assert( u.bv.iDb>=0 && u.bv.iDbnDb ); + u.bu.iDb = pOp->p1; + assert( u.bu.iDb>=0 && u.bu.iDbnDb ); /* If pOp->p2 is 0, then this opcode is being executed to read a ** single row, for example the row corresponding to a new index @@ -55317,40 +56758,40 @@ ** with the rest of the schema when it is required. ** ** Although the mutex on the BtShared object that corresponds to - ** database u.bv.iDb (the database containing the sqlite_master table + ** database u.bu.iDb (the database containing the sqlite_master table ** read by this instruction) is currently held, it is necessary to ** obtain the mutexes on all attached databases before checking if - ** the schema of u.bv.iDb is loaded. This is because, at the start of + ** the schema of u.bu.iDb is loaded. This is because, at the start of ** the sqlite3_exec() call below, SQLite will invoke ** sqlite3BtreeEnterAll(). If all mutexes are not already held, the - ** u.bv.iDb mutex may be temporarily released to avoid deadlock. If + ** u.bu.iDb mutex may be temporarily released to avoid deadlock. If ** this happens, then some other thread may delete the in-memory - ** schema of database u.bv.iDb before the SQL statement runs. The schema + ** schema of database u.bu.iDb before the SQL statement runs. The schema ** will not be reloaded becuase the db->init.busy flag is set. This ** can result in a "no such table: sqlite_master" or "malformed ** database schema" error being returned to the user. */ - assert( sqlite3BtreeHoldsMutex(db->aDb[u.bv.iDb].pBt) ); + assert( sqlite3BtreeHoldsMutex(db->aDb[u.bu.iDb].pBt) ); sqlite3BtreeEnterAll(db); - if( pOp->p2 || DbHasProperty(db, u.bv.iDb, DB_SchemaLoaded) ){ - u.bv.zMaster = SCHEMA_TABLE(u.bv.iDb); - u.bv.initData.db = db; - u.bv.initData.iDb = pOp->p1; - u.bv.initData.pzErrMsg = &p->zErrMsg; - u.bv.zSql = sqlite3MPrintf(db, + if( pOp->p2 || DbHasProperty(db, u.bu.iDb, DB_SchemaLoaded) ){ + u.bu.zMaster = SCHEMA_TABLE(u.bu.iDb); + u.bu.initData.db = db; + u.bu.initData.iDb = pOp->p1; + u.bu.initData.pzErrMsg = &p->zErrMsg; + u.bu.zSql = sqlite3MPrintf(db, "SELECT name, rootpage, sql FROM '%q'.%s WHERE %s", - db->aDb[u.bv.iDb].zName, u.bv.zMaster, pOp->p4.z); - if( u.bv.zSql==0 ){ + db->aDb[u.bu.iDb].zName, u.bu.zMaster, pOp->p4.z); + if( u.bu.zSql==0 ){ rc = SQLITE_NOMEM; }else{ (void)sqlite3SafetyOff(db); assert( db->init.busy==0 ); db->init.busy = 1; - u.bv.initData.rc = SQLITE_OK; + u.bu.initData.rc = SQLITE_OK; assert( !db->mallocFailed ); - rc = sqlite3_exec(db, u.bv.zSql, sqlite3InitCallback, &u.bv.initData, 0); - if( rc==SQLITE_OK ) rc = u.bv.initData.rc; - sqlite3DbFree(db, u.bv.zSql); + rc = sqlite3_exec(db, u.bu.zSql, sqlite3InitCallback, &u.bu.initData, 0); + if( rc==SQLITE_OK ) rc = u.bu.initData.rc; + sqlite3DbFree(db, u.bu.zSql); db->init.busy = 0; (void)sqlite3SafetyOn(db); } @@ -55435,41 +56876,41 @@ ** This opcode is used to implement the integrity_check pragma. */ case OP_IntegrityCk: { -#if 0 /* local variables moved into u.bw */ +#if 0 /* local variables moved into u.bv */ int nRoot; /* Number of tables to check. (Number of root pages.) */ int *aRoot; /* Array of rootpage numbers for tables to be checked */ int j; /* Loop counter */ int nErr; /* Number of errors reported */ char *z; /* Text of the error report */ Mem *pnErr; /* Register keeping track of errors remaining */ -#endif /* local variables moved into u.bw */ +#endif /* local variables moved into u.bv */ - u.bw.nRoot = pOp->p2; - assert( u.bw.nRoot>0 ); - u.bw.aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(u.bw.nRoot+1) ); - if( u.bw.aRoot==0 ) goto no_mem; + u.bv.nRoot = pOp->p2; + assert( u.bv.nRoot>0 ); + u.bv.aRoot = sqlite3DbMallocRaw(db, sizeof(int)*(u.bv.nRoot+1) ); + if( u.bv.aRoot==0 ) goto no_mem; assert( pOp->p3>0 && pOp->p3<=p->nMem ); - u.bw.pnErr = &p->aMem[pOp->p3]; - assert( (u.bw.pnErr->flags & MEM_Int)!=0 ); - assert( (u.bw.pnErr->flags & (MEM_Str|MEM_Blob))==0 ); - pIn1 = &p->aMem[pOp->p1]; - for(u.bw.j=0; u.bw.jp3]; + assert( (u.bv.pnErr->flags & MEM_Int)!=0 ); + assert( (u.bv.pnErr->flags & (MEM_Str|MEM_Blob))==0 ); + pIn1 = &aMem[pOp->p1]; + for(u.bv.j=0; u.bv.jp5nDb ); assert( (p->btreeMask & (1<p5))!=0 ); - u.bw.z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, u.bw.aRoot, u.bw.nRoot, - (int)u.bw.pnErr->u.i, &u.bw.nErr); - sqlite3DbFree(db, u.bw.aRoot); - u.bw.pnErr->u.i -= u.bw.nErr; + u.bv.z = sqlite3BtreeIntegrityCheck(db->aDb[pOp->p5].pBt, u.bv.aRoot, u.bv.nRoot, + (int)u.bv.pnErr->u.i, &u.bv.nErr); + sqlite3DbFree(db, u.bv.aRoot); + u.bv.pnErr->u.i -= u.bv.nErr; sqlite3VdbeMemSetNull(pIn1); - if( u.bw.nErr==0 ){ - assert( u.bw.z==0 ); - }else if( u.bw.z==0 ){ + if( u.bv.nErr==0 ){ + assert( u.bv.z==0 ); + }else if( u.bv.z==0 ){ goto no_mem; }else{ - sqlite3VdbeMemSetStr(pIn1, u.bw.z, -1, SQLITE_UTF8, sqlite3_free); + sqlite3VdbeMemSetStr(pIn1, u.bv.z, -1, SQLITE_UTF8, sqlite3_free); } UPDATE_MAX_BLOBSIZE(pIn1); sqlite3VdbeChangeEncoding(pIn1, encoding); @@ -55484,21 +56925,15 @@ ** ** An assertion fails if P2 is not an integer. */ -case OP_RowSetAdd: { /* in2 */ -#if 0 /* local variables moved into u.bx */ - Mem *pIdx; - Mem *pVal; -#endif /* local variables moved into u.bx */ - assert( pOp->p1>0 && pOp->p1<=p->nMem ); - u.bx.pIdx = &p->aMem[pOp->p1]; - assert( pOp->p2>0 && pOp->p2<=p->nMem ); - u.bx.pVal = &p->aMem[pOp->p2]; - assert( (u.bx.pVal->flags & MEM_Int)!=0 ); - if( (u.bx.pIdx->flags & MEM_RowSet)==0 ){ - sqlite3VdbeMemSetRowSet(u.bx.pIdx); - if( (u.bx.pIdx->flags & MEM_RowSet)==0 ) goto no_mem; +case OP_RowSetAdd: { /* in1, in2 */ + pIn1 = &aMem[pOp->p1]; + pIn2 = &aMem[pOp->p2]; + assert( (pIn2->flags & MEM_Int)!=0 ); + if( (pIn1->flags & MEM_RowSet)==0 ){ + sqlite3VdbeMemSetRowSet(pIn1); + if( (pIn1->flags & MEM_RowSet)==0 ) goto no_mem; } - sqlite3RowSetInsert(u.bx.pIdx->u.pRowSet, u.bx.pVal->u.i); + sqlite3RowSetInsert(pIn1->u.pRowSet, pIn2->u.i); break; } @@ -55508,25 +56943,21 @@ ** register P3. Or, if boolean index P1 is initially empty, leave P3 ** unchanged and jump to instruction P2. */ -case OP_RowSetRead: { /* jump, out3 */ -#if 0 /* local variables moved into u.by */ - Mem *pIdx; +case OP_RowSetRead: { /* jump, in1, out3 */ +#if 0 /* local variables moved into u.bw */ i64 val; -#endif /* local variables moved into u.by */ - assert( pOp->p1>0 && pOp->p1<=p->nMem ); +#endif /* local variables moved into u.bw */ CHECK_FOR_INTERRUPT; - u.by.pIdx = &p->aMem[pOp->p1]; - pOut = &p->aMem[pOp->p3]; - if( (u.by.pIdx->flags & MEM_RowSet)==0 - || sqlite3RowSetNext(u.by.pIdx->u.pRowSet, &u.by.val)==0 + pIn1 = &aMem[pOp->p1]; + if( (pIn1->flags & MEM_RowSet)==0 + || sqlite3RowSetNext(pIn1->u.pRowSet, &u.bw.val)==0 ){ /* The boolean index is empty */ - sqlite3VdbeMemSetNull(u.by.pIdx); + sqlite3VdbeMemSetNull(pIn1); pc = pOp->p2 - 1; }else{ /* A value was pulled from the index */ - assert( pOp->p3>0 && pOp->p3<=p->nMem ); - sqlite3VdbeMemSetInt64(pOut, u.by.val); + sqlite3VdbeMemSetInt64(&aMem[pOp->p3], u.bw.val); } break; } @@ -55555,12 +56986,14 @@ ** inserted as part of some other set). */ case OP_RowSetTest: { /* jump, in1, in3 */ -#if 0 /* local variables moved into u.bz */ +#if 0 /* local variables moved into u.bx */ int iSet; int exists; -#endif /* local variables moved into u.bz */ +#endif /* local variables moved into u.bx */ - u.bz.iSet = pOp->p4.i; + pIn1 = &aMem[pOp->p1]; + pIn3 = &aMem[pOp->p3]; + u.bx.iSet = pOp->p4.i; assert( pIn3->flags&MEM_Int ); /* If there is anything other than a rowset object in memory cell P1, @@ -55572,17 +57005,17 @@ } assert( pOp->p4type==P4_INT32 ); - assert( u.bz.iSet==-1 || u.bz.iSet>=0 ); - if( u.bz.iSet ){ - u.bz.exists = sqlite3RowSetTest(pIn1->u.pRowSet, - (u8)(u.bz.iSet>=0 ? u.bz.iSet & 0xf : 0xff), + assert( u.bx.iSet==-1 || u.bx.iSet>=0 ); + if( u.bx.iSet ){ + u.bx.exists = sqlite3RowSetTest(pIn1->u.pRowSet, + (u8)(u.bx.iSet>=0 ? u.bx.iSet & 0xf : 0xff), pIn3->u.i); - if( u.bz.exists ){ + if( u.bx.exists ){ pc = pOp->p2 - 1; break; } } - if( u.bz.iSet>=0 ){ + if( u.bx.iSet>=0 ){ sqlite3RowSetInsert(pIn1->u.pRowSet, pIn3->u.i); } break; @@ -55590,65 +57023,212 @@ #ifndef SQLITE_OMIT_TRIGGER -/* Opcode: ContextPush * * * + +/* Opcode: Program P1 P2 P3 P4 * +** +** Execute the trigger program passed as P4 (type P4_SUBPROGRAM). ** -** Save the current Vdbe context such that it can be restored by a ContextPop -** opcode. The context stores the last insert row id, the last statement change -** count, and the current statement change count. +** P1 contains the address of the memory cell that contains the first memory +** cell in an array of values used as arguments to the sub-program. P2 +** contains the address to jump to if the sub-program throws an IGNORE +** exception using the RAISE() function. Register P3 contains the address +** of a memory cell in this (the parent) VM that is used to allocate the +** memory required by the sub-vdbe at runtime. +** +** P4 is a pointer to the VM containing the trigger program. */ -case OP_ContextPush: { -#if 0 /* local variables moved into u.ca */ - int i; - Context *pContext; -#endif /* local variables moved into u.ca */ +case OP_Program: { /* jump */ +#if 0 /* local variables moved into u.by */ + int nMem; /* Number of memory registers for sub-program */ + int nByte; /* Bytes of runtime space required for sub-program */ + Mem *pRt; /* Register to allocate runtime space */ + Mem *pMem; /* Used to iterate through memory cells */ + Mem *pEnd; /* Last memory cell in new array */ + VdbeFrame *pFrame; /* New vdbe frame to execute in */ + SubProgram *pProgram; /* Sub-program to execute */ + void *t; /* Token identifying trigger */ +#endif /* local variables moved into u.by */ + + u.by.pProgram = pOp->p4.pProgram; + u.by.pRt = &aMem[pOp->p3]; + assert( u.by.pProgram->nOp>0 ); + + /* If the p5 flag is clear, then recursive invocation of triggers is + ** disabled for backwards compatibility (p5 is set if this sub-program + ** is really a trigger, not a foreign key action, and the flag set + ** and cleared by the "PRAGMA recursive_triggers" command is clear). + ** + ** It is recursive invocation of triggers, at the SQL level, that is + ** disabled. In some cases a single trigger may generate more than one + ** SubProgram (if the trigger may be executed with more than one different + ** ON CONFLICT algorithm). SubProgram structures associated with a + ** single trigger all have the same value for the SubProgram.token + ** variable. */ + if( pOp->p5 ){ + u.by.t = u.by.pProgram->token; + for(u.by.pFrame=p->pFrame; u.by.pFrame && u.by.pFrame->token!=u.by.t; u.by.pFrame=u.by.pFrame->pParent); + if( u.by.pFrame ) break; + } + + if( p->nFrame>=db->aLimit[SQLITE_LIMIT_TRIGGER_DEPTH] ){ + rc = SQLITE_ERROR; + sqlite3SetString(&p->zErrMsg, db, "too many levels of trigger recursion"); + break; + } + + /* Register u.by.pRt is used to store the memory required to save the state + ** of the current program, and the memory required at runtime to execute + ** the trigger program. If this trigger has been fired before, then u.by.pRt + ** is already allocated. Otherwise, it must be initialized. */ + if( (u.by.pRt->flags&MEM_Frame)==0 ){ + /* SubProgram.nMem is set to the number of memory cells used by the + ** program stored in SubProgram.aOp. As well as these, one memory + ** cell is required for each cursor used by the program. Set local + ** variable u.by.nMem (and later, VdbeFrame.nChildMem) to this value. + */ + u.by.nMem = u.by.pProgram->nMem + u.by.pProgram->nCsr; + u.by.nByte = ROUND8(sizeof(VdbeFrame)) + + u.by.nMem * sizeof(Mem) + + u.by.pProgram->nCsr * sizeof(VdbeCursor *); + u.by.pFrame = sqlite3DbMallocZero(db, u.by.nByte); + if( !u.by.pFrame ){ + goto no_mem; + } + sqlite3VdbeMemRelease(u.by.pRt); + u.by.pRt->flags = MEM_Frame; + u.by.pRt->u.pFrame = u.by.pFrame; + + u.by.pFrame->v = p; + u.by.pFrame->nChildMem = u.by.nMem; + u.by.pFrame->nChildCsr = u.by.pProgram->nCsr; + u.by.pFrame->pc = pc; + u.by.pFrame->aMem = p->aMem; + u.by.pFrame->nMem = p->nMem; + u.by.pFrame->apCsr = p->apCsr; + u.by.pFrame->nCursor = p->nCursor; + u.by.pFrame->aOp = p->aOp; + u.by.pFrame->nOp = p->nOp; + u.by.pFrame->token = u.by.pProgram->token; + + u.by.pEnd = &VdbeFrameMem(u.by.pFrame)[u.by.pFrame->nChildMem]; + for(u.by.pMem=VdbeFrameMem(u.by.pFrame); u.by.pMem!=u.by.pEnd; u.by.pMem++){ + u.by.pMem->flags = MEM_Null; + u.by.pMem->db = db; + } + }else{ + u.by.pFrame = u.by.pRt->u.pFrame; + assert( u.by.pProgram->nMem+u.by.pProgram->nCsr==u.by.pFrame->nChildMem ); + assert( u.by.pProgram->nCsr==u.by.pFrame->nChildCsr ); + assert( pc==u.by.pFrame->pc ); + } + + p->nFrame++; + u.by.pFrame->pParent = p->pFrame; + u.by.pFrame->lastRowid = db->lastRowid; + u.by.pFrame->nChange = p->nChange; + p->nChange = 0; + p->pFrame = u.by.pFrame; + p->aMem = aMem = &VdbeFrameMem(u.by.pFrame)[-1]; + p->nMem = u.by.pFrame->nChildMem; + p->nCursor = (u16)u.by.pFrame->nChildCsr; + p->apCsr = (VdbeCursor **)&aMem[p->nMem+1]; + p->aOp = aOp = u.by.pProgram->aOp; + p->nOp = u.by.pProgram->nOp; + pc = -1; - u.ca.i = p->contextStackTop++; - assert( u.ca.i>=0 ); - /* FIX ME: This should be allocated as part of the vdbe at compile-time */ - if( u.ca.i>=p->contextStackDepth ){ - p->contextStackDepth = u.ca.i+1; - p->contextStack = sqlite3DbReallocOrFree(db, p->contextStack, - sizeof(Context)*(u.ca.i+1)); - if( p->contextStack==0 ) goto no_mem; - } - u.ca.pContext = &p->contextStack[u.ca.i]; - u.ca.pContext->lastRowid = db->lastRowid; - u.ca.pContext->nChange = p->nChange; break; } -/* Opcode: ContextPop * * * +/* Opcode: Param P1 P2 * * * ** -** Restore the Vdbe context to the state it was in when contextPush was last -** executed. The context stores the last insert row id, the last statement -** change count, and the current statement change count. +** This opcode is only ever present in sub-programs called via the +** OP_Program instruction. Copy a value currently stored in a memory +** cell of the calling (parent) frame to cell P2 in the current frames +** address space. This is used by trigger programs to access the new.* +** and old.* values. +** +** The address of the cell in the parent frame is determined by adding +** the value of the P1 argument to the value of the P1 argument to the +** calling OP_Program instruction. */ -case OP_ContextPop: { -#if 0 /* local variables moved into u.cb */ - Context *pContext; -#endif /* local variables moved into u.cb */ - u.cb.pContext = &p->contextStack[--p->contextStackTop]; - assert( p->contextStackTop>=0 ); - db->lastRowid = u.cb.pContext->lastRowid; - p->nChange = u.cb.pContext->nChange; +case OP_Param: { /* out2-prerelease */ +#if 0 /* local variables moved into u.bz */ + VdbeFrame *pFrame; + Mem *pIn; +#endif /* local variables moved into u.bz */ + u.bz.pFrame = p->pFrame; + u.bz.pIn = &u.bz.pFrame->aMem[pOp->p1 + u.bz.pFrame->aOp[u.bz.pFrame->pc].p1]; + sqlite3VdbeMemShallowCopy(pOut, u.bz.pIn, MEM_Ephem); break; } + #endif /* #ifndef SQLITE_OMIT_TRIGGER */ +#ifndef SQLITE_OMIT_FOREIGN_KEY +/* Opcode: FkCounter P1 P2 * * * +** +** Increment a "constraint counter" by P2 (P2 may be negative or positive). +** If P1 is non-zero, the database constraint counter is incremented +** (deferred foreign key constraints). Otherwise, if P1 is zero, the +** statement counter is incremented (immediate foreign key constraints). +*/ +case OP_FkCounter: { + if( pOp->p1 ){ + db->nDeferredCons += pOp->p2; + }else{ + p->nFkConstraint += pOp->p2; + } + break; +} + +/* Opcode: FkIfZero P1 P2 * * * +** +** This opcode tests if a foreign key constraint-counter is currently zero. +** If so, jump to instruction P2. Otherwise, fall through to the next +** instruction. +** +** If P1 is non-zero, then the jump is taken if the database constraint-counter +** is zero (the one that counts deferred constraint violations). If P1 is +** zero, the jump is taken if the statement constraint-counter is zero +** (immediate foreign key constraint violations). +*/ +case OP_FkIfZero: { /* jump */ + if( pOp->p1 ){ + if( db->nDeferredCons==0 ) pc = pOp->p2-1; + }else{ + if( p->nFkConstraint==0 ) pc = pOp->p2-1; + } + break; +} +#endif /* #ifndef SQLITE_OMIT_FOREIGN_KEY */ + #ifndef SQLITE_OMIT_AUTOINCREMENT /* Opcode: MemMax P1 P2 * * * ** -** Set the value of register P1 to the maximum of its current value -** and the value in register P2. +** P1 is a register in the root frame of this VM (the root frame is +** different from the current frame if this instruction is being executed +** within a sub-program). Set the value of register P1 to the maximum of +** its current value and the value in register P2. ** ** This instruction throws an error if the memory cell is not initially ** an integer. */ -case OP_MemMax: { /* in1, in2 */ - sqlite3VdbeMemIntegerify(pIn1); +case OP_MemMax: { /* in2 */ +#if 0 /* local variables moved into u.ca */ + Mem *pIn1; + VdbeFrame *pFrame; +#endif /* local variables moved into u.ca */ + if( p->pFrame ){ + for(u.ca.pFrame=p->pFrame; u.ca.pFrame->pParent; u.ca.pFrame=u.ca.pFrame->pParent); + u.ca.pIn1 = &u.ca.pFrame->aMem[pOp->p1]; + }else{ + u.ca.pIn1 = &aMem[pOp->p1]; + } + sqlite3VdbeMemIntegerify(u.ca.pIn1); + pIn2 = &aMem[pOp->p2]; sqlite3VdbeMemIntegerify(pIn2); - if( pIn1->u.iu.i){ - pIn1->u.i = pIn2->u.i; + if( u.ca.pIn1->u.iu.i){ + u.ca.pIn1->u.i = pIn2->u.i; } break; } @@ -55662,6 +57242,7 @@ ** not contain an integer. An assertion fault will result if you try. */ case OP_IfPos: { /* jump, in1 */ + pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); if( pIn1->u.i>0 ){ pc = pOp->p2 - 1; @@ -55677,6 +57258,7 @@ ** not contain an integer. An assertion fault will result if you try. */ case OP_IfNeg: { /* jump, in1 */ + pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); if( pIn1->u.i<0 ){ pc = pOp->p2 - 1; @@ -55684,15 +57266,18 @@ break; } -/* Opcode: IfZero P1 P2 * * * +/* Opcode: IfZero P1 P2 P3 * * ** -** If the value of register P1 is exactly 0, jump to P2. +** The register P1 must contain an integer. Add literal P3 to the +** value in register P1. If the result is exactly 0, jump to P2. ** ** It is illegal to use this instruction on a register that does ** not contain an integer. An assertion fault will result if you try. */ case OP_IfZero: { /* jump, in1 */ + pIn1 = &aMem[pOp->p1]; assert( pIn1->flags&MEM_Int ); + pIn1->u.i += pOp->p3; if( pIn1->u.i==0 ){ pc = pOp->p2 - 1; } @@ -55710,47 +57295,47 @@ ** successors. */ case OP_AggStep: { -#if 0 /* local variables moved into u.cc */ +#if 0 /* local variables moved into u.cb */ int n; int i; Mem *pMem; Mem *pRec; sqlite3_context ctx; sqlite3_value **apVal; -#endif /* local variables moved into u.cc */ +#endif /* local variables moved into u.cb */ - u.cc.n = pOp->p5; - assert( u.cc.n>=0 ); - u.cc.pRec = &p->aMem[pOp->p2]; - u.cc.apVal = p->apArg; - assert( u.cc.apVal || u.cc.n==0 ); - for(u.cc.i=0; u.cc.ip5; + assert( u.cb.n>=0 ); + u.cb.pRec = &aMem[pOp->p2]; + u.cb.apVal = p->apArg; + assert( u.cb.apVal || u.cb.n==0 ); + for(u.cb.i=0; u.cb.ip4.pFunc; + u.cb.ctx.pFunc = pOp->p4.pFunc; assert( pOp->p3>0 && pOp->p3<=p->nMem ); - u.cc.ctx.pMem = u.cc.pMem = &p->aMem[pOp->p3]; - u.cc.pMem->n++; - u.cc.ctx.s.flags = MEM_Null; - u.cc.ctx.s.z = 0; - u.cc.ctx.s.zMalloc = 0; - u.cc.ctx.s.xDel = 0; - u.cc.ctx.s.db = db; - u.cc.ctx.isError = 0; - u.cc.ctx.pColl = 0; - if( u.cc.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ + u.cb.ctx.pMem = u.cb.pMem = &aMem[pOp->p3]; + u.cb.pMem->n++; + u.cb.ctx.s.flags = MEM_Null; + u.cb.ctx.s.z = 0; + u.cb.ctx.s.zMalloc = 0; + u.cb.ctx.s.xDel = 0; + u.cb.ctx.s.db = db; + u.cb.ctx.isError = 0; + u.cb.ctx.pColl = 0; + if( u.cb.ctx.pFunc->flags & SQLITE_FUNC_NEEDCOLL ){ assert( pOp>p->aOp ); assert( pOp[-1].p4type==P4_COLLSEQ ); assert( pOp[-1].opcode==OP_CollSeq ); - u.cc.ctx.pColl = pOp[-1].p4.pColl; + u.cb.ctx.pColl = pOp[-1].p4.pColl; } - (u.cc.ctx.pFunc->xStep)(&u.cc.ctx, u.cc.n, u.cc.apVal); - if( u.cc.ctx.isError ){ - sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.cc.ctx.s)); - rc = u.cc.ctx.isError; + (u.cb.ctx.pFunc->xStep)(&u.cb.ctx, u.cb.n, u.cb.apVal); + if( u.cb.ctx.isError ){ + sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(&u.cb.ctx.s)); + rc = u.cb.ctx.isError; } - sqlite3VdbeMemRelease(&u.cc.ctx.s); + sqlite3VdbeMemRelease(&u.cb.ctx.s); break; } @@ -55767,19 +57352,19 @@ ** the step function was not previously called. */ case OP_AggFinal: { -#if 0 /* local variables moved into u.cd */ +#if 0 /* local variables moved into u.cc */ Mem *pMem; -#endif /* local variables moved into u.cd */ +#endif /* local variables moved into u.cc */ assert( pOp->p1>0 && pOp->p1<=p->nMem ); - u.cd.pMem = &p->aMem[pOp->p1]; - assert( (u.cd.pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); - rc = sqlite3VdbeMemFinalize(u.cd.pMem, pOp->p4.pFunc); + u.cc.pMem = &aMem[pOp->p1]; + assert( (u.cc.pMem->flags & ~(MEM_Null|MEM_Agg))==0 ); + rc = sqlite3VdbeMemFinalize(u.cc.pMem, pOp->p4.pFunc); if( rc ){ - sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(u.cd.pMem)); + sqlite3SetString(&p->zErrMsg, db, "%s", sqlite3_value_text(u.cc.pMem)); } - sqlite3VdbeChangeEncoding(u.cd.pMem, encoding); - UPDATE_MAX_BLOBSIZE(u.cd.pMem); - if( sqlite3VdbeMemTooBig(u.cd.pMem) ){ + sqlite3VdbeChangeEncoding(u.cc.pMem, encoding); + UPDATE_MAX_BLOBSIZE(u.cc.pMem); + if( sqlite3VdbeMemTooBig(u.cc.pMem) ){ goto too_big; } break; @@ -55809,14 +57394,14 @@ ** P2. Otherwise, fall through to the next instruction. */ case OP_IncrVacuum: { /* jump */ -#if 0 /* local variables moved into u.ce */ +#if 0 /* local variables moved into u.cd */ Btree *pBt; -#endif /* local variables moved into u.ce */ +#endif /* local variables moved into u.cd */ assert( pOp->p1>=0 && pOp->p1nDb ); assert( (p->btreeMask & (1<p1))!=0 ); - u.ce.pBt = db->aDb[pOp->p1].pBt; - rc = sqlite3BtreeIncrVacuum(u.ce.pBt); + u.cd.pBt = db->aDb[pOp->p1].pBt; + rc = sqlite3BtreeIncrVacuum(u.cd.pBt); if( rc==SQLITE_DONE ){ pc = pOp->p2 - 1; rc = SQLITE_OK; @@ -55849,7 +57434,7 @@ ** Obtain a lock on a particular table. This instruction is only used when ** the shared-cache feature is enabled. ** -** If P1 is the index of the database in sqlite3.aDb[] of the database +** P1 is the index of the database in sqlite3.aDb[] of the database ** on which the lock is acquired. A readlock is obtained if P3==0 or ** a write lock if P3==1. ** @@ -55859,20 +57444,17 @@ ** used to generate an error message if the lock cannot be obtained. */ case OP_TableLock: { -#if 0 /* local variables moved into u.cf */ - int p1; - u8 isWriteLock; -#endif /* local variables moved into u.cf */ - - u.cf.p1 = pOp->p1; - u.cf.isWriteLock = (u8)pOp->p3; - assert( u.cf.p1>=0 && u.cf.p1nDb ); - assert( (p->btreeMask & (1<aDb[u.cf.p1].pBt, pOp->p2, u.cf.isWriteLock); - if( (rc&0xFF)==SQLITE_LOCKED ){ - const char *z = pOp->p4.z; - sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z); + u8 isWriteLock = (u8)pOp->p3; + if( isWriteLock || 0==(db->flags&SQLITE_ReadUncommitted) ){ + int p1 = pOp->p1; + assert( p1>=0 && p1nDb ); + assert( (p->btreeMask & (1<aDb[p1].pBt, pOp->p2, isWriteLock); + if( (rc&0xFF)==SQLITE_LOCKED ){ + const char *z = pOp->p4.z; + sqlite3SetString(&p->zErrMsg, db, "database table is locked: %s", z); + } } break; } @@ -55889,15 +57471,15 @@ ** code will be set to SQLITE_LOCKED. */ case OP_VBegin: { -#if 0 /* local variables moved into u.cg */ - sqlite3_vtab *pVtab; -#endif /* local variables moved into u.cg */ - u.cg.pVtab = pOp->p4.pVtab; - rc = sqlite3VtabBegin(db, u.cg.pVtab); - if( u.cg.pVtab ){ +#if 0 /* local variables moved into u.ce */ + VTable *pVTab; +#endif /* local variables moved into u.ce */ + u.ce.pVTab = pOp->p4.pVtab; + rc = sqlite3VtabBegin(db, u.ce.pVTab); + if( u.ce.pVTab ){ sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = u.cg.pVtab->zErrMsg; - u.cg.pVtab->zErrMsg = 0; + p->zErrMsg = u.ce.pVTab->pVtab->zErrMsg; + u.ce.pVTab->pVtab->zErrMsg = 0; } break; } @@ -55937,36 +57519,36 @@ ** table and stores that cursor in P1. */ case OP_VOpen: { -#if 0 /* local variables moved into u.ch */ +#if 0 /* local variables moved into u.cf */ VdbeCursor *pCur; sqlite3_vtab_cursor *pVtabCursor; sqlite3_vtab *pVtab; sqlite3_module *pModule; -#endif /* local variables moved into u.ch */ +#endif /* local variables moved into u.cf */ - u.ch.pCur = 0; - u.ch.pVtabCursor = 0; - u.ch.pVtab = pOp->p4.pVtab; - u.ch.pModule = (sqlite3_module *)u.ch.pVtab->pModule; - assert(u.ch.pVtab && u.ch.pModule); + u.cf.pCur = 0; + u.cf.pVtabCursor = 0; + u.cf.pVtab = pOp->p4.pVtab->pVtab; + u.cf.pModule = (sqlite3_module *)u.cf.pVtab->pModule; + assert(u.cf.pVtab && u.cf.pModule); if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; - rc = u.ch.pModule->xOpen(u.ch.pVtab, &u.ch.pVtabCursor); + rc = u.cf.pModule->xOpen(u.cf.pVtab, &u.cf.pVtabCursor); sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = u.ch.pVtab->zErrMsg; - u.ch.pVtab->zErrMsg = 0; + p->zErrMsg = u.cf.pVtab->zErrMsg; + u.cf.pVtab->zErrMsg = 0; if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; if( SQLITE_OK==rc ){ /* Initialize sqlite3_vtab_cursor base class */ - u.ch.pVtabCursor->pVtab = u.ch.pVtab; + u.cf.pVtabCursor->pVtab = u.cf.pVtab; /* Initialise vdbe cursor object */ - u.ch.pCur = allocateCursor(p, pOp->p1, 0, -1, 0); - if( u.ch.pCur ){ - u.ch.pCur->pVtabCursor = u.ch.pVtabCursor; - u.ch.pCur->pModule = u.ch.pVtabCursor->pVtab->pModule; + u.cf.pCur = allocateCursor(p, pOp->p1, 0, -1, 0); + if( u.cf.pCur ){ + u.cf.pCur->pVtabCursor = u.cf.pVtabCursor; + u.cf.pCur->pModule = u.cf.pVtabCursor->pVtab->pModule; }else{ db->mallocFailed = 1; - u.ch.pModule->xClose(u.ch.pVtabCursor); + u.cf.pModule->xClose(u.cf.pVtabCursor); } } break; @@ -55993,7 +57575,7 @@ ** A jump is made to P2 if the result set after filtering would be empty. */ case OP_VFilter: { /* jump */ -#if 0 /* local variables moved into u.ci */ +#if 0 /* local variables moved into u.cg */ int nArg; int iQuery; const sqlite3_module *pModule; @@ -56005,50 +57587,48 @@ int res; int i; Mem **apArg; -#endif /* local variables moved into u.ci */ +#endif /* local variables moved into u.cg */ - u.ci.pQuery = &p->aMem[pOp->p3]; - u.ci.pArgc = &u.ci.pQuery[1]; - u.ci.pCur = p->apCsr[pOp->p1]; - REGISTER_TRACE(pOp->p3, u.ci.pQuery); - assert( u.ci.pCur->pVtabCursor ); - u.ci.pVtabCursor = u.ci.pCur->pVtabCursor; - u.ci.pVtab = u.ci.pVtabCursor->pVtab; - u.ci.pModule = u.ci.pVtab->pModule; + u.cg.pQuery = &aMem[pOp->p3]; + u.cg.pArgc = &u.cg.pQuery[1]; + u.cg.pCur = p->apCsr[pOp->p1]; + REGISTER_TRACE(pOp->p3, u.cg.pQuery); + assert( u.cg.pCur->pVtabCursor ); + u.cg.pVtabCursor = u.cg.pCur->pVtabCursor; + u.cg.pVtab = u.cg.pVtabCursor->pVtab; + u.cg.pModule = u.cg.pVtab->pModule; /* Grab the index number and argc parameters */ - assert( (u.ci.pQuery->flags&MEM_Int)!=0 && u.ci.pArgc->flags==MEM_Int ); - u.ci.nArg = (int)u.ci.pArgc->u.i; - u.ci.iQuery = (int)u.ci.pQuery->u.i; + assert( (u.cg.pQuery->flags&MEM_Int)!=0 && u.cg.pArgc->flags==MEM_Int ); + u.cg.nArg = (int)u.cg.pArgc->u.i; + u.cg.iQuery = (int)u.cg.pQuery->u.i; /* Invoke the xFilter method */ { - u.ci.res = 0; - u.ci.apArg = p->apArg; - for(u.ci.i = 0; u.ci.iapArg; + for(u.cg.i = 0; u.cg.iinVtabMethod = 1; - rc = u.ci.pModule->xFilter(u.ci.pVtabCursor, u.ci.iQuery, pOp->p4.z, u.ci.nArg, u.ci.apArg); + rc = u.cg.pModule->xFilter(u.cg.pVtabCursor, u.cg.iQuery, pOp->p4.z, u.cg.nArg, u.cg.apArg); p->inVtabMethod = 0; sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = u.ci.pVtab->zErrMsg; - u.ci.pVtab->zErrMsg = 0; - sqlite3VtabUnlock(db, u.ci.pVtab); + p->zErrMsg = u.cg.pVtab->zErrMsg; + u.cg.pVtab->zErrMsg = 0; if( rc==SQLITE_OK ){ - u.ci.res = u.ci.pModule->xEof(u.ci.pVtabCursor); + u.cg.res = u.cg.pModule->xEof(u.cg.pVtabCursor); } if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; - if( u.ci.res ){ + if( u.cg.res ){ pc = pOp->p2 - 1; } } - u.ci.pCur->nullRow = 0; + u.cg.pCur->nullRow = 0; break; } @@ -56062,56 +57642,56 @@ ** P1 cursor is pointing to into register P3. */ case OP_VColumn: { -#if 0 /* local variables moved into u.cj */ +#if 0 /* local variables moved into u.ch */ sqlite3_vtab *pVtab; const sqlite3_module *pModule; Mem *pDest; sqlite3_context sContext; -#endif /* local variables moved into u.cj */ +#endif /* local variables moved into u.ch */ VdbeCursor *pCur = p->apCsr[pOp->p1]; assert( pCur->pVtabCursor ); assert( pOp->p3>0 && pOp->p3<=p->nMem ); - u.cj.pDest = &p->aMem[pOp->p3]; + u.ch.pDest = &aMem[pOp->p3]; if( pCur->nullRow ){ - sqlite3VdbeMemSetNull(u.cj.pDest); + sqlite3VdbeMemSetNull(u.ch.pDest); break; } - u.cj.pVtab = pCur->pVtabCursor->pVtab; - u.cj.pModule = u.cj.pVtab->pModule; - assert( u.cj.pModule->xColumn ); - memset(&u.cj.sContext, 0, sizeof(u.cj.sContext)); + u.ch.pVtab = pCur->pVtabCursor->pVtab; + u.ch.pModule = u.ch.pVtab->pModule; + assert( u.ch.pModule->xColumn ); + memset(&u.ch.sContext, 0, sizeof(u.ch.sContext)); /* The output cell may already have a buffer allocated. Move - ** the current contents to u.cj.sContext.s so in case the user-function + ** the current contents to u.ch.sContext.s so in case the user-function ** can use the already allocated buffer instead of allocating a ** new one. */ - sqlite3VdbeMemMove(&u.cj.sContext.s, u.cj.pDest); - MemSetTypeFlag(&u.cj.sContext.s, MEM_Null); + sqlite3VdbeMemMove(&u.ch.sContext.s, u.ch.pDest); + MemSetTypeFlag(&u.ch.sContext.s, MEM_Null); if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; - rc = u.cj.pModule->xColumn(pCur->pVtabCursor, &u.cj.sContext, pOp->p2); + rc = u.ch.pModule->xColumn(pCur->pVtabCursor, &u.ch.sContext, pOp->p2); sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = u.cj.pVtab->zErrMsg; - u.cj.pVtab->zErrMsg = 0; - if( u.cj.sContext.isError ){ - rc = u.cj.sContext.isError; + p->zErrMsg = u.ch.pVtab->zErrMsg; + u.ch.pVtab->zErrMsg = 0; + if( u.ch.sContext.isError ){ + rc = u.ch.sContext.isError; } /* Copy the result of the function to the P3 register. We ** do this regardless of whether or not an error occurred to ensure any - ** dynamic allocation in u.cj.sContext.s (a Mem struct) is released. + ** dynamic allocation in u.ch.sContext.s (a Mem struct) is released. */ - sqlite3VdbeChangeEncoding(&u.cj.sContext.s, encoding); - REGISTER_TRACE(pOp->p3, u.cj.pDest); - sqlite3VdbeMemMove(u.cj.pDest, &u.cj.sContext.s); - UPDATE_MAX_BLOBSIZE(u.cj.pDest); + sqlite3VdbeChangeEncoding(&u.ch.sContext.s, encoding); + sqlite3VdbeMemMove(u.ch.pDest, &u.ch.sContext.s); + REGISTER_TRACE(pOp->p3, u.ch.pDest); + UPDATE_MAX_BLOBSIZE(u.ch.pDest); if( sqlite3SafetyOn(db) ){ goto abort_due_to_misuse; } - if( sqlite3VdbeMemTooBig(u.cj.pDest) ){ + if( sqlite3VdbeMemTooBig(u.ch.pDest) ){ goto too_big; } break; @@ -56126,22 +57706,22 @@ ** the end of its result set, then fall through to the next instruction. */ case OP_VNext: { /* jump */ -#if 0 /* local variables moved into u.ck */ +#if 0 /* local variables moved into u.ci */ sqlite3_vtab *pVtab; const sqlite3_module *pModule; int res; VdbeCursor *pCur; -#endif /* local variables moved into u.ck */ +#endif /* local variables moved into u.ci */ - u.ck.res = 0; - u.ck.pCur = p->apCsr[pOp->p1]; - assert( u.ck.pCur->pVtabCursor ); - if( u.ck.pCur->nullRow ){ + u.ci.res = 0; + u.ci.pCur = p->apCsr[pOp->p1]; + assert( u.ci.pCur->pVtabCursor ); + if( u.ci.pCur->nullRow ){ break; } - u.ck.pVtab = u.ck.pCur->pVtabCursor->pVtab; - u.ck.pModule = u.ck.pVtab->pModule; - assert( u.ck.pModule->xNext ); + u.ci.pVtab = u.ci.pCur->pVtabCursor->pVtab; + u.ci.pModule = u.ci.pVtab->pModule; + assert( u.ci.pModule->xNext ); /* Invoke the xNext() method of the module. There is no way for the ** underlying implementation to return an error if one occurs during @@ -56150,20 +57730,18 @@ ** some other method is next invoked on the save virtual table cursor. */ if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; - sqlite3VtabLock(u.ck.pVtab); p->inVtabMethod = 1; - rc = u.ck.pModule->xNext(u.ck.pCur->pVtabCursor); + rc = u.ci.pModule->xNext(u.ci.pCur->pVtabCursor); p->inVtabMethod = 0; sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = u.ck.pVtab->zErrMsg; - u.ck.pVtab->zErrMsg = 0; - sqlite3VtabUnlock(db, u.ck.pVtab); + p->zErrMsg = u.ci.pVtab->zErrMsg; + u.ci.pVtab->zErrMsg = 0; if( rc==SQLITE_OK ){ - u.ck.res = u.ck.pModule->xEof(u.ck.pCur->pVtabCursor); + u.ci.res = u.ci.pModule->xEof(u.ci.pCur->pVtabCursor); } if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; - if( !u.ck.res ){ + if( !u.ci.res ){ /* If there is data, jump to P2 */ pc = pOp->p2 - 1; } @@ -56179,23 +57757,21 @@ ** in register P1 is passed as the zName argument to the xRename method. */ case OP_VRename: { -#if 0 /* local variables moved into u.cl */ +#if 0 /* local variables moved into u.cj */ sqlite3_vtab *pVtab; Mem *pName; -#endif /* local variables moved into u.cl */ +#endif /* local variables moved into u.cj */ - u.cl.pVtab = pOp->p4.pVtab; - u.cl.pName = &p->aMem[pOp->p1]; - assert( u.cl.pVtab->pModule->xRename ); - REGISTER_TRACE(pOp->p1, u.cl.pName); - assert( u.cl.pName->flags & MEM_Str ); + u.cj.pVtab = pOp->p4.pVtab->pVtab; + u.cj.pName = &aMem[pOp->p1]; + assert( u.cj.pVtab->pModule->xRename ); + REGISTER_TRACE(pOp->p1, u.cj.pName); + assert( u.cj.pName->flags & MEM_Str ); if( sqlite3SafetyOff(db) ) goto abort_due_to_misuse; - sqlite3VtabLock(u.cl.pVtab); - rc = u.cl.pVtab->pModule->xRename(u.cl.pVtab, u.cl.pName->z); + rc = u.cj.pVtab->pModule->xRename(u.cj.pVtab, u.cj.pName->z); sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = u.cl.pVtab->zErrMsg; - u.cl.pVtab->zErrMsg = 0; - sqlite3VtabUnlock(db, u.cl.pVtab); + p->zErrMsg = u.cj.pVtab->zErrMsg; + u.cj.pVtab->zErrMsg = 0; if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; break; @@ -56227,7 +57803,7 @@ ** is set to the value of the rowid for the row just inserted. */ case OP_VUpdate: { -#if 0 /* local variables moved into u.cm */ +#if 0 /* local variables moved into u.ck */ sqlite3_vtab *pVtab; sqlite3_module *pModule; int nArg; @@ -56235,31 +57811,29 @@ sqlite_int64 rowid; Mem **apArg; Mem *pX; -#endif /* local variables moved into u.cm */ +#endif /* local variables moved into u.ck */ - u.cm.pVtab = pOp->p4.pVtab; - u.cm.pModule = (sqlite3_module *)u.cm.pVtab->pModule; - u.cm.nArg = pOp->p2; + u.ck.pVtab = pOp->p4.pVtab->pVtab; + u.ck.pModule = (sqlite3_module *)u.ck.pVtab->pModule; + u.ck.nArg = pOp->p2; assert( pOp->p4type==P4_VTAB ); - if( ALWAYS(u.cm.pModule->xUpdate) ){ - u.cm.apArg = p->apArg; - u.cm.pX = &p->aMem[pOp->p3]; - for(u.cm.i=0; u.cm.ixUpdate) ){ + u.ck.apArg = p->apArg; + u.ck.pX = &aMem[pOp->p3]; + for(u.ck.i=0; u.ck.ixUpdate(u.cm.pVtab, u.cm.nArg, u.cm.apArg, &u.cm.rowid); + rc = u.ck.pModule->xUpdate(u.ck.pVtab, u.ck.nArg, u.ck.apArg, &u.ck.rowid); sqlite3DbFree(db, p->zErrMsg); - p->zErrMsg = u.cm.pVtab->zErrMsg; - u.cm.pVtab->zErrMsg = 0; - sqlite3VtabUnlock(db, u.cm.pVtab); + p->zErrMsg = u.ck.pVtab->zErrMsg; + u.ck.pVtab->zErrMsg = 0; if( sqlite3SafetyOn(db) ) goto abort_due_to_misuse; if( rc==SQLITE_OK && pOp->p1 ){ - assert( u.cm.nArg>1 && u.cm.apArg[0] && (u.cm.apArg[0]->flags&MEM_Null) ); - db->lastRowid = u.cm.rowid; + assert( u.ck.nArg>1 && u.ck.apArg[0] && (u.ck.apArg[0]->flags&MEM_Null) ); + db->lastRowid = u.ck.rowid; } p->nChange++; } @@ -56273,21 +57847,20 @@ ** Write the current number of pages in database P1 to memory cell P2. */ case OP_Pagecount: { /* out2-prerelease */ -#if 0 /* local variables moved into u.cn */ +#if 0 /* local variables moved into u.cl */ int p1; int nPage; Pager *pPager; -#endif /* local variables moved into u.cn */ +#endif /* local variables moved into u.cl */ - u.cn.p1 = pOp->p1; - u.cn.pPager = sqlite3BtreePager(db->aDb[u.cn.p1].pBt); - rc = sqlite3PagerPagecount(u.cn.pPager, &u.cn.nPage); + u.cl.p1 = pOp->p1; + u.cl.pPager = sqlite3BtreePager(db->aDb[u.cl.p1].pBt); + rc = sqlite3PagerPagecount(u.cl.pPager, &u.cl.nPage); /* OP_Pagecount is always called from within a read transaction. The ** page count has already been successfully read and cached. So the ** sqlite3PagerPagecount() call above cannot fail. */ if( ALWAYS(rc==SQLITE_OK) ){ - pOut->flags = MEM_Int; - pOut->u.i = u.cn.nPage; + pOut->u.i = u.cl.nPage; } break; } @@ -56300,18 +57873,20 @@ ** the UTF-8 string contained in P4 is emitted on the trace callback. */ case OP_Trace: { -#if 0 /* local variables moved into u.co */ +#if 0 /* local variables moved into u.cm */ char *zTrace; -#endif /* local variables moved into u.co */ +#endif /* local variables moved into u.cm */ - u.co.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql); - if( u.co.zTrace ){ + u.cm.zTrace = (pOp->p4.z ? pOp->p4.z : p->zSql); + if( u.cm.zTrace ){ if( db->xTrace ){ - db->xTrace(db->pTraceArg, u.co.zTrace); + char *z = sqlite3VdbeExpandSql(p, u.cm.zTrace); + db->xTrace(db->pTraceArg, z); + sqlite3DbFree(db, z); } #ifdef SQLITE_DEBUG if( (db->flags & SQLITE_SqlTrace)!=0 ){ - sqlite3DebugPrintf("SQL-trace: %s\n", u.co.zTrace); + sqlite3DebugPrintf("SQL-trace: %s\n", u.cm.zTrace); } #endif /* SQLITE_DEBUG */ } @@ -56350,7 +57925,7 @@ pOp->cnt++; #if 0 fprintf(stdout, "%10llu ", elapsed); - sqlite3VdbePrintOp(stdout, origPc, &p->aOp[origPc]); + sqlite3VdbePrintOp(stdout, origPc, &aOp[origPc]); #endif } #endif @@ -56366,11 +57941,11 @@ #ifdef SQLITE_DEBUG if( p->trace ){ if( rc!=0 ) fprintf(p->trace,"rc=%d\n",rc); - if( opProperty & OPFLG_OUT2_PRERELEASE ){ - registerTrace(p->trace, pOp->p2, pOut); + if( pOp->opflags & (OPFLG_OUT2_PRERELEASE|OPFLG_OUT2) ){ + registerTrace(p->trace, pOp->p2, &aMem[pOp->p2]); } - if( opProperty & OPFLG_OUT3 ){ - registerTrace(p->trace, pOp->p3, pOut); + if( pOp->opflags & OPFLG_OUT3 ){ + registerTrace(p->trace, pOp->p3, &aMem[pOp->p3]); } } #endif /* SQLITE_DEBUG */ @@ -56386,6 +57961,7 @@ sqlite3VdbeHalt(p); if( rc==SQLITE_IOERR_NOMEM ) db->mallocFailed = 1; rc = SQLITE_ERROR; + if( resetSchemaOnFault ) sqlite3ResetInternalSchema(db, 0); /* This is the only way out of this procedure. We have to ** release the mutexes on btrees that were acquired at the @@ -56453,8 +58029,6 @@ ************************************************************************* ** ** This file contains code used to implement incremental BLOB I/O. -** -** $Id: vdbeblob.c,v 1.33 2009/06/01 19:53:31 drh Exp $ */ @@ -56506,19 +58080,18 @@ static const VdbeOpList openBlob[] = { {OP_Transaction, 0, 0, 0}, /* 0: Start a transaction */ {OP_VerifyCookie, 0, 0, 0}, /* 1: Check the schema cookie */ + {OP_TableLock, 0, 0, 0}, /* 2: Acquire a read or write lock */ - /* One of the following two instructions is replaced by an - ** OP_Noop before exection. - */ - {OP_OpenRead, 0, 0, 0}, /* 2: Open cursor 0 for reading */ - {OP_OpenWrite, 0, 0, 0}, /* 3: Open cursor 0 for read/write */ - - {OP_Variable, 1, 1, 1}, /* 4: Push the rowid to the stack */ - {OP_NotExists, 0, 8, 1}, /* 5: Seek the cursor */ - {OP_Column, 0, 0, 1}, /* 6 */ - {OP_ResultRow, 1, 0, 0}, /* 7 */ - {OP_Close, 0, 0, 0}, /* 8 */ - {OP_Halt, 0, 0, 0}, /* 9 */ + /* One of the following two instructions is replaced by an OP_Noop. */ + {OP_OpenRead, 0, 0, 0}, /* 3: Open cursor 0 for reading */ + {OP_OpenWrite, 0, 0, 0}, /* 4: Open cursor 0 for read/write */ + + {OP_Variable, 1, 1, 1}, /* 5: Push the rowid to the stack */ + {OP_NotExists, 0, 9, 1}, /* 6: Seek the cursor */ + {OP_Column, 0, 0, 1}, /* 7 */ + {OP_ResultRow, 1, 0, 0}, /* 8 */ + {OP_Close, 0, 0, 0}, /* 9 */ + {OP_Halt, 0, 0, 0}, /* 10 */ }; Vdbe *v = 0; @@ -56585,35 +58158,56 @@ } /* If the value is being opened for writing, check that the - ** column is not indexed. It is against the rules to open an - ** indexed column for writing. - */ + ** column is not indexed, and that it is not part of a foreign key. + ** It is against the rules to open a column to which either of these + ** descriptions applies for writing. */ if( flags ){ + const char *zFault = 0; Index *pIdx; +#ifndef SQLITE_OMIT_FOREIGN_KEY + if( db->flags&SQLITE_ForeignKeys ){ + /* Check that the column is not part of an FK child key definition. It + ** is not necessary to check if it is part of a parent key, as parent + ** key columns must be indexed. The check below will pick up this + ** case. */ + FKey *pFKey; + for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ + int j; + for(j=0; jnCol; j++){ + if( pFKey->aCol[j].iFrom==iCol ){ + zFault = "foreign key"; + } + } + } + } +#endif for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ int j; for(j=0; jnColumn; j++){ if( pIdx->aiColumn[j]==iCol ){ - sqlite3DbFree(db, zErr); - zErr = sqlite3MPrintf(db, - "cannot open indexed column for writing"); - rc = SQLITE_ERROR; - (void)sqlite3SafetyOff(db); - sqlite3BtreeLeaveAll(db); - goto blob_open_out; + zFault = "indexed"; } } } + if( zFault ){ + sqlite3DbFree(db, zErr); + zErr = sqlite3MPrintf(db, "cannot open %s column for writing", zFault); + rc = SQLITE_ERROR; + (void)sqlite3SafetyOff(db); + sqlite3BtreeLeaveAll(db); + goto blob_open_out; + } } v = sqlite3VdbeCreate(db); if( v ){ int iDb = sqlite3SchemaToIndex(db, pTab->pSchema); sqlite3VdbeAddOpList(v, sizeof(openBlob)/sizeof(VdbeOpList), openBlob); + flags = !!flags; /* flags = (flags ? 1 : 0); */ /* Configure the OP_Transaction */ sqlite3VdbeChangeP1(v, 0, iDb); - sqlite3VdbeChangeP2(v, 0, (flags ? 1 : 0)); + sqlite3VdbeChangeP2(v, 0, flags); /* Configure the OP_VerifyCookie */ sqlite3VdbeChangeP1(v, 1, iDb); @@ -56622,13 +58216,17 @@ /* Make sure a mutex is held on the table to be accessed */ sqlite3VdbeUsesBtree(v, iDb); + /* Configure the OP_TableLock instruction */ + sqlite3VdbeChangeP1(v, 2, iDb); + sqlite3VdbeChangeP2(v, 2, pTab->tnum); + sqlite3VdbeChangeP3(v, 2, flags); + sqlite3VdbeChangeP4(v, 2, pTab->zName, P4_TRANSIENT); + /* Remove either the OP_OpenWrite or OpenRead. Set the P2 - ** parameter of the other to pTab->tnum. - */ - flags = !!flags; - sqlite3VdbeChangeToNoop(v, 3 - flags, 1); - sqlite3VdbeChangeP2(v, 2 + flags, pTab->tnum); - sqlite3VdbeChangeP3(v, 2 + flags, iDb); + ** parameter of the other to pTab->tnum. */ + sqlite3VdbeChangeToNoop(v, 4 - flags, 1); + sqlite3VdbeChangeP2(v, 3 + flags, pTab->tnum); + sqlite3VdbeChangeP3(v, 3 + flags, iDb); /* Configure the number of columns. Configure the cursor to ** think that the table has one more column than it really @@ -56637,10 +58235,10 @@ ** we can invoke OP_Column to fill in the vdbe cursors type ** and offset cache without causing any IO. */ - sqlite3VdbeChangeP4(v, 2+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); - sqlite3VdbeChangeP2(v, 6, pTab->nCol); + sqlite3VdbeChangeP4(v, 3+flags, SQLITE_INT_TO_PTR(pTab->nCol+1),P4_INT32); + sqlite3VdbeChangeP2(v, 7, pTab->nCol); if( !db->mallocFailed ){ - sqlite3VdbeMakeReady(v, 1, 1, 1, 0); + sqlite3VdbeMakeReady(v, 1, 1, 1, 0, 0, 0); } } @@ -56823,12 +58421,6 @@ ** ************************************************************************* ** -** @(#) $Id: journal.c,v 1.9 2009/01/20 17:06:27 danielk1977 Exp $ -*/ - -#ifdef SQLITE_ENABLE_ATOMIC_WRITE - -/* ** This file implements a special kind of sqlite3_file object used ** by SQLite to create journal files if the atomic-write optimization ** is enabled. @@ -56843,7 +58435,7 @@ ** buffer, or ** 2) The sqlite3JournalCreate() function is called. */ - +#ifdef SQLITE_ENABLE_ATOMIC_WRITE /* @@ -57068,8 +58660,6 @@ ** This file contains code use to implement an in-memory rollback journal. ** The in-memory rollback journal is used to journal transactions for ** ":memory:" databases and when the journal_mode=MEMORY pragma is used. -** -** @(#) $Id: memjournal.c,v 1.12 2009/05/04 11:42:30 danielk1977 Exp $ */ /* Forward references to internal structures */ @@ -57327,8 +58917,6 @@ ************************************************************************* ** This file contains routines used for walking the parser tree for ** an SQL statement. -** -** $Id: walker.c,v 1.7 2009/06/15 23:15:59 drh Exp $ */ @@ -57467,8 +59055,6 @@ ** This file contains routines used for walking the parser tree and ** resolve all identifiers by associating them with a particular ** table and column. -** -** $Id: resolve.c,v 1.30 2009/06/15 23:15:59 drh Exp $ */ /* @@ -57540,7 +59126,13 @@ pDup->pColl = pExpr->pColl; pDup->flags |= EP_ExpCollate; } - sqlite3ExprClear(db, pExpr); + + /* Before calling sqlite3ExprDelete(), set the EP_Static flag. This + ** prevents ExprDelete() from deleting the Expr structure itself, + ** allowing it to be repopulated by the memcpy() on the following line. + */ + ExprSetProperty(pExpr, EP_Static); + sqlite3ExprDelete(db, pExpr); memcpy(pExpr, pDup, sizeof(*pExpr)); sqlite3DbFree(db, pDup); } @@ -57588,6 +59180,7 @@ struct SrcList_item *pMatch = 0; /* The matching pSrcList item */ NameContext *pTopNC = pNC; /* First namecontext in the list */ Schema *pSchema = 0; /* Schema of the expression */ + int isTrigger = 0; assert( pNC ); /* the name context cannot be NULL. */ assert( zCol ); /* The Z in X.Y.Z cannot be NULL */ @@ -57673,43 +59266,51 @@ /* If we have not already resolved the name, then maybe ** it is a new.* or old.* trigger argument reference */ - if( zDb==0 && zTab!=0 && cnt==0 && pParse->trigStack!=0 ){ - TriggerStack *pTriggerStack = pParse->trigStack; + if( zDb==0 && zTab!=0 && cnt==0 && pParse->pTriggerTab!=0 ){ + int op = pParse->eTriggerOp; Table *pTab = 0; - u32 *piColMask = 0; - if( pTriggerStack->newIdx != -1 && sqlite3StrICmp("new", zTab) == 0 ){ - pExpr->iTable = pTriggerStack->newIdx; - assert( pTriggerStack->pTab ); - pTab = pTriggerStack->pTab; - piColMask = &(pTriggerStack->newColMask); - }else if( pTriggerStack->oldIdx != -1 && sqlite3StrICmp("old", zTab)==0 ){ - pExpr->iTable = pTriggerStack->oldIdx; - assert( pTriggerStack->pTab ); - pTab = pTriggerStack->pTab; - piColMask = &(pTriggerStack->oldColMask); + assert( op==TK_DELETE || op==TK_UPDATE || op==TK_INSERT ); + if( op!=TK_DELETE && sqlite3StrICmp("new",zTab) == 0 ){ + pExpr->iTable = 1; + pTab = pParse->pTriggerTab; + }else if( op!=TK_INSERT && sqlite3StrICmp("old",zTab)==0 ){ + pExpr->iTable = 0; + pTab = pParse->pTriggerTab; } if( pTab ){ int iCol; - Column *pCol = pTab->aCol; - pSchema = pTab->pSchema; cntTab++; - for(iCol=0; iCol < pTab->nCol; iCol++, pCol++) { + for(iCol=0; iColnCol; iCol++){ + Column *pCol = &pTab->aCol[iCol]; if( sqlite3StrICmp(pCol->zName, zCol)==0 ){ - cnt++; - pExpr->iColumn = iCol==pTab->iPKey ? -1 : (i16)iCol; - pExpr->pTab = pTab; - testcase( iCol==31 ); - testcase( iCol==32 ); - if( iCol>=32 ){ - *piColMask = 0xffffffff; - }else{ - *piColMask |= ((u32)1)<iPKey ){ + iCol = -1; } break; } } + if( iCol>=pTab->nCol && sqlite3IsRowid(zCol) ){ + iCol = -1; /* IMP: R-44911-55124 */ + } + if( iColnCol ){ + cnt++; + if( iCol<0 ){ + pExpr->affinity = SQLITE_AFF_INTEGER; + }else if( pExpr->iTable==0 ){ + testcase( iCol==31 ); + testcase( iCol==32 ); + pParse->oldmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<newmask |= (iCol>=32 ? 0xffffffff : (((u32)1)<iColumn = (i16)iCol; + pExpr->pTab = pTab; + isTrigger = 1; + } } } #endif /* !defined(SQLITE_OMIT_TRIGGER) */ @@ -57719,7 +59320,7 @@ */ if( cnt==0 && cntTab==1 && sqlite3IsRowid(zCol) ){ cnt = 1; - pExpr->iColumn = -1; + pExpr->iColumn = -1; /* IMP: R-44911-55124 */ pExpr->affinity = SQLITE_AFF_INTEGER; } @@ -57820,7 +59421,7 @@ pExpr->pLeft = 0; sqlite3ExprDelete(db, pExpr->pRight); pExpr->pRight = 0; - pExpr->op = TK_COLUMN; + pExpr->op = (isTrigger ? TK_TRIGGER : TK_COLUMN); lookupname_end: if( cnt==1 ){ assert( pNC!=0 ); @@ -57840,6 +59441,27 @@ } /* +** Allocate and return a pointer to an expression to load the column iCol +** from datasource iSrc datasource in SrcList pSrc. +*/ +SQLITE_PRIVATE Expr *sqlite3CreateColumnExpr(sqlite3 *db, SrcList *pSrc, int iSrc, int iCol){ + Expr *p = sqlite3ExprAlloc(db, TK_COLUMN, 0, 0); + if( p ){ + struct SrcList_item *pItem = &pSrc->a[iSrc]; + p->pTab = pItem->pTab; + p->iTable = pItem->iCursor; + if( p->pTab->iPKey==iCol ){ + p->iColumn = -1; + }else{ + p->iColumn = (ynVar)iCol; + pItem->colUsed |= ((Bitmask)1)<<(iCol>=BMS ? BMS-1 : iCol); + } + ExprSetProperty(p, EP_Resolved); + } + return p; +} + +/* ** This routine is callback for sqlite3WalkExpr(). ** ** Resolve symbolic names into TK_COLUMN operators for the current @@ -58627,8 +60249,6 @@ ************************************************************************* ** This file contains routines used for analyzing expressions and ** for generating VDBE code that evaluates expressions in SQLite. -** -** $Id: expr.c,v 1.446 2009/06/19 18:32:55 drh Exp $ */ /* @@ -58707,7 +60327,9 @@ pColl = p->pColl; if( pColl ) break; op = p->op; - if( (op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER) && p->pTab!=0 ){ + if( p->pTab!=0 && ( + op==TK_AGG_COLUMN || op==TK_COLUMN || op==TK_REGISTER || op==TK_TRIGGER + )){ /* op==TK_REGISTER && p->pTab!=0 happens when pExpr was originally ** a TK_COLUMN but was previously evaluated and cached in a register */ const char *zColl; @@ -58767,7 +60389,7 @@ char aff; assert( pExpr->op==TK_EQ || pExpr->op==TK_IN || pExpr->op==TK_LT || pExpr->op==TK_GT || pExpr->op==TK_GE || pExpr->op==TK_LE || - pExpr->op==TK_NE ); + pExpr->op==TK_NE || pExpr->op==TK_IS || pExpr->op==TK_ISNOT ); assert( pExpr->pLeft ); aff = sqlite3ExprAffinity(pExpr->pLeft); if( pExpr->pRight ){ @@ -58843,30 +60465,6 @@ } /* -** Generate the operands for a comparison operation. Before -** generating the code for each operand, set the EP_AnyAff -** flag on the expression so that it will be able to used a -** cached column value that has previously undergone an -** affinity change. -*/ -static void codeCompareOperands( - Parse *pParse, /* Parsing and code generating context */ - Expr *pLeft, /* The left operand */ - int *pRegLeft, /* Register where left operand is stored */ - int *pFreeLeft, /* Free this register when done */ - Expr *pRight, /* The right operand */ - int *pRegRight, /* Register where right operand is stored */ - int *pFreeRight /* Write temp register for right operand there */ -){ - while( pLeft->op==TK_UPLUS ) pLeft = pLeft->pLeft; - pLeft->flags |= EP_AnyAff; - *pRegLeft = sqlite3ExprCodeTemp(pParse, pLeft, pFreeLeft); - while( pRight->op==TK_UPLUS ) pRight = pRight->pLeft; - pRight->flags |= EP_AnyAff; - *pRegRight = sqlite3ExprCodeTemp(pParse, pRight, pFreeRight); -} - -/* ** Generate code for a comparison operator. */ static int codeCompare( @@ -59186,12 +60784,12 @@ if( z[1]==0 ){ /* Wildcard of the form "?". Assign the next variable number */ assert( z[0]=='?' ); - pExpr->iTable = ++pParse->nVar; + pExpr->iColumn = (ynVar)(++pParse->nVar); }else if( z[0]=='?' ){ /* Wildcard of the form "?nnn". Convert "nnn" to an integer and ** use it as the variable number */ - int i; - pExpr->iTable = i = atoi((char*)&z[1]); + int i = atoi((char*)&z[1]); + pExpr->iColumn = (ynVar)i; testcase( i==0 ); testcase( i==1 ); testcase( i==db->aLimit[SQLITE_LIMIT_VARIABLE_NUMBER]-1 ); @@ -59215,12 +60813,12 @@ Expr *pE = pParse->apVarExpr[i]; assert( pE!=0 ); if( memcmp(pE->u.zToken, z, n)==0 && pE->u.zToken[n]==0 ){ - pExpr->iTable = pE->iTable; + pExpr->iColumn = pE->iColumn; break; } } if( i>=pParse->nVarExpr ){ - pExpr->iTable = ++pParse->nVar; + pExpr->iColumn = (ynVar)(++pParse->nVar); if( pParse->nVarExpr>=pParse->nVarExprAlloc-1 ){ pParse->nVarExprAlloc += pParse->nVarExprAlloc + 10; pParse->apVarExpr = @@ -59242,11 +60840,10 @@ } /* -** Clear an expression structure without deleting the structure itself. -** Substructure is deleted. +** Recursively delete an expression tree. */ -SQLITE_PRIVATE void sqlite3ExprClear(sqlite3 *db, Expr *p){ - assert( p!=0 ); +SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){ + if( p==0 ) return; if( !ExprHasAnyProperty(p, EP_TokenOnly) ){ sqlite3ExprDelete(db, p->pLeft); sqlite3ExprDelete(db, p->pRight); @@ -59259,14 +60856,6 @@ sqlite3ExprListDelete(db, p->x.pList); } } -} - -/* -** Recursively delete an expression tree. -*/ -SQLITE_PRIVATE void sqlite3ExprDelete(sqlite3 *db, Expr *p){ - if( p==0 ) return; - sqlite3ExprClear(db, p); if( !ExprHasProperty(p, EP_Static) ){ sqlite3DbFree(db, p); } @@ -59501,9 +61090,8 @@ } pOldItem = p->a; for(i=0; inExpr; i++, pItem++, pOldItem++){ - Expr *pNewExpr; Expr *pOldExpr = pOldItem->pExpr; - pItem->pExpr = pNewExpr = sqlite3ExprDup(db, pOldExpr, flags); + pItem->pExpr = sqlite3ExprDup(db, pOldExpr, flags); pItem->zName = sqlite3DbStrDup(db, pOldItem->zName); pItem->zSpan = sqlite3DbStrDup(db, pOldItem->zSpan); pItem->sortOrder = pOldItem->sortOrder; @@ -59875,6 +61463,94 @@ } /* +** Return FALSE if there is no chance that the expression can be NULL. +** +** If the expression might be NULL or if the expression is too complex +** to tell return TRUE. +** +** This routine is used as an optimization, to skip OP_IsNull opcodes +** when we know that a value cannot be NULL. Hence, a false positive +** (returning TRUE when in fact the expression can never be NULL) might +** be a small performance hit but is otherwise harmless. On the other +** hand, a false negative (returning FALSE when the result could be NULL) +** will likely result in an incorrect answer. So when in doubt, return +** TRUE. +*/ +SQLITE_PRIVATE int sqlite3ExprCanBeNull(const Expr *p){ + u8 op; + while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ p = p->pLeft; } + op = p->op; + if( op==TK_REGISTER ) op = p->op2; + switch( op ){ + case TK_INTEGER: + case TK_STRING: + case TK_FLOAT: + case TK_BLOB: + return 0; + default: + return 1; + } +} + +/* +** Generate an OP_IsNull instruction that tests register iReg and jumps +** to location iDest if the value in iReg is NULL. The value in iReg +** was computed by pExpr. If we can look at pExpr at compile-time and +** determine that it can never generate a NULL, then the OP_IsNull operation +** can be omitted. +*/ +SQLITE_PRIVATE void sqlite3ExprCodeIsNullJump( + Vdbe *v, /* The VDBE under construction */ + const Expr *pExpr, /* Only generate OP_IsNull if this expr can be NULL */ + int iReg, /* Test the value in this register for NULL */ + int iDest /* Jump here if the value is null */ +){ + if( sqlite3ExprCanBeNull(pExpr) ){ + sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iDest); + } +} + +/* +** Return TRUE if the given expression is a constant which would be +** unchanged by OP_Affinity with the affinity given in the second +** argument. +** +** This routine is used to determine if the OP_Affinity operation +** can be omitted. When in doubt return FALSE. A false negative +** is harmless. A false positive, however, can result in the wrong +** answer. +*/ +SQLITE_PRIVATE int sqlite3ExprNeedsNoAffinityChange(const Expr *p, char aff){ + u8 op; + if( aff==SQLITE_AFF_NONE ) return 1; + while( p->op==TK_UPLUS || p->op==TK_UMINUS ){ p = p->pLeft; } + op = p->op; + if( op==TK_REGISTER ) op = p->op2; + switch( op ){ + case TK_INTEGER: { + return aff==SQLITE_AFF_INTEGER || aff==SQLITE_AFF_NUMERIC; + } + case TK_FLOAT: { + return aff==SQLITE_AFF_REAL || aff==SQLITE_AFF_NUMERIC; + } + case TK_STRING: { + return aff==SQLITE_AFF_TEXT; + } + case TK_BLOB: { + return 1; + } + case TK_COLUMN: { + assert( p->iTable>=0 ); /* p cannot be part of a CHECK constraint */ + return p->iColumn<0 + && (aff==SQLITE_AFF_INTEGER || aff==SQLITE_AFF_NUMERIC); + } + default: { + return 0; + } + } +} + +/* ** Return TRUE if the given string is a row-id column name. */ SQLITE_PRIVATE int sqlite3IsRowid(const char *z){ @@ -59961,16 +61637,16 @@ ** When the b-tree is being used for membership tests, the calling function ** needs to know whether or not the structure contains an SQL NULL ** value in order to correctly evaluate expressions like "X IN (Y, Z)". -** If there is a chance that the b-tree might contain a NULL value at +** If there is any chance that the (...) might contain a NULL value at ** runtime, then a register is allocated and the register number written -** to *prNotFound. If there is no chance that the b-tree contains a +** to *prNotFound. If there is no chance that the (...) contains a ** NULL value, then *prNotFound is left unchanged. ** ** If a register is allocated and its location stored in *prNotFound, then -** its initial value is NULL. If the b-tree does not remain constant -** for the duration of the query (i.e. the SELECT that generates the b-tree +** its initial value is NULL. If the (...) does not remain constant +** for the duration of the query (i.e. the SELECT within the (...) ** is a correlated subquery) then the value of the allocated register is -** reset to NULL each time the b-tree is repopulated. This allows the +** reset to NULL each time the subquery is rerun. This allows the ** caller to use vdbe code equivalent to the following: ** ** if( register==NULL ){ @@ -59988,6 +61664,8 @@ int iTab = pParse->nTab++; /* Cursor of the RHS table */ int mustBeUnique = (prNotFound==0); /* True if RHS must be unique */ + assert( pX->op==TK_IN ); + /* Check to see if an existing table or index can be used to ** satisfy the query. This is preferable to generating a new ** ephemeral table. @@ -60014,7 +61692,6 @@ if( iCol<0 ){ int iMem = ++pParse->nMem; int iAddr; - sqlite3VdbeUsesBtree(v, iDb); iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem); sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem); @@ -60048,9 +61725,6 @@ char *pKey; pKey = (char *)sqlite3IndexKeyinfo(pParse, pIdx); - iDb = sqlite3SchemaToIndex(db, pIdx->pSchema); - sqlite3VdbeUsesBtree(v, iDb); - iAddr = sqlite3VdbeAddOp1(v, OP_If, iMem); sqlite3VdbeAddOp2(v, OP_Integer, 1, iMem); @@ -60069,7 +61743,7 @@ } if( eType==0 ){ - /* Could not found an existing able or index to use as the RHS b-tree. + /* Could not found an existing table or index to use as the RHS b-tree. ** We will have to generate an ephemeral table to do the job. */ int rMayHaveNull = 0; @@ -60116,17 +61790,21 @@ ** If rMayHaveNull is zero, that means that the subquery is being used ** for membership testing only. There is no need to initialize any ** registers to indicate the presense or absence of NULLs on the RHS. +** +** For a SELECT or EXISTS operator, return the register that holds the +** result. For IN operators or if an error occurs, the return value is 0. */ #ifndef SQLITE_OMIT_SUBQUERY -SQLITE_PRIVATE void sqlite3CodeSubselect( +SQLITE_PRIVATE int sqlite3CodeSubselect( Parse *pParse, /* Parsing context */ Expr *pExpr, /* The IN, SELECT, or EXISTS operator */ int rMayHaveNull, /* Register that records whether NULLs exist in RHS */ int isRowid /* If true, LHS of IN operator is a rowid */ ){ int testAddr = 0; /* One-time test address */ + int rReg = 0; /* Register storing resulting */ Vdbe *v = sqlite3GetVdbe(pParse); - if( NEVER(v==0) ) return; + if( NEVER(v==0) ) return 0; sqlite3ExprCachePush(pParse); /* This code must be run in its entirety every time it is encountered @@ -60139,7 +61817,7 @@ ** If all of the above are false, then we can run this code just once ** save the results, and reuse the same result on subsequent invocations. */ - if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->trigStack ){ + if( !ExprHasAnyProperty(pExpr, EP_VarSelect) && !pParse->pTriggerTab ){ int mem = ++pParse->nMem; sqlite3VdbeAddOp1(v, OP_If, mem); testAddr = sqlite3VdbeAddOp2(v, OP_Integer, 1, mem); @@ -60160,7 +61838,7 @@ affinity = sqlite3ExprAffinity(pLeft); /* Whether this is an 'x IN(SELECT...)' or an 'x IN()' - ** expression it is handled the same way. A virtual table is + ** expression it is handled the same way. An ephemeral table is ** filled with single-field index keys representing the results ** from the SELECT or the . ** @@ -60191,7 +61869,7 @@ dest.affinity = (u8)affinity; assert( (pExpr->iTable&0x0000FFFF)==pExpr->iTable ); if( sqlite3Select(pParse, pExpr->x.pSelect, &dest) ){ - return; + return 0; } pEList = pExpr->x.pSelect->pEList; if( ALWAYS(pEList!=0 && pEList->nExpr>0) ){ @@ -60222,6 +61900,7 @@ sqlite3VdbeAddOp2(v, OP_Null, 0, r2); for(i=pList->nExpr, pItem=pList->a; i>0; i--, pItem++){ Expr *pE2 = pItem->pExpr; + int iValToIns; /* If the expression is not constant then we will need to ** disable the test that was generated above that makes sure @@ -60234,14 +61913,19 @@ } /* Evaluate the expression and insert it into the temp table */ - r3 = sqlite3ExprCodeTarget(pParse, pE2, r1); - if( isRowid ){ - sqlite3VdbeAddOp2(v, OP_MustBeInt, r3, sqlite3VdbeCurrentAddr(v)+2); - sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3); + if( isRowid && sqlite3ExprIsInteger(pE2, &iValToIns) ){ + sqlite3VdbeAddOp3(v, OP_InsertInt, pExpr->iTable, r2, iValToIns); }else{ - sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1); - sqlite3ExprCacheAffinityChange(pParse, r3, 1); - sqlite3VdbeAddOp2(v, OP_IdxInsert, pExpr->iTable, r2); + r3 = sqlite3ExprCodeTarget(pParse, pE2, r1); + if( isRowid ){ + sqlite3VdbeAddOp2(v, OP_MustBeInt, r3, + sqlite3VdbeCurrentAddr(v)+2); + sqlite3VdbeAddOp3(v, OP_Insert, pExpr->iTable, r2, r3); + }else{ + sqlite3VdbeAddOp4(v, OP_MakeRecord, r3, 1, r2, &affinity, 1); + sqlite3ExprCacheAffinityChange(pParse, r3, 1); + sqlite3VdbeAddOp2(v, OP_IdxInsert, pExpr->iTable, r2); + } } } sqlite3ReleaseTempReg(pParse, r1); @@ -60285,9 +61969,9 @@ sqlite3ExprDelete(pParse->db, pSel->pLimit); pSel->pLimit = sqlite3PExpr(pParse, TK_INTEGER, 0, 0, &one); if( sqlite3Select(pParse, pSel, &dest) ){ - return; + return 0; } - pExpr->iColumn = dest.iParm; + rReg = dest.iParm; ExprSetIrreducible(pExpr); break; } @@ -60298,7 +61982,129 @@ } sqlite3ExprCachePop(pParse, 1); - return; + return rReg; +} +#endif /* SQLITE_OMIT_SUBQUERY */ + +#ifndef SQLITE_OMIT_SUBQUERY +/* +** Generate code for an IN expression. +** +** x IN (SELECT ...) +** x IN (value, value, ...) +** +** The left-hand side (LHS) is a scalar expression. The right-hand side (RHS) +** is an array of zero or more values. The expression is true if the LHS is +** contained within the RHS. The value of the expression is unknown (NULL) +** if the LHS is NULL or if the LHS is not contained within the RHS and the +** RHS contains one or more NULL values. +** +** This routine generates code will jump to destIfFalse if the LHS is not +** contained within the RHS. If due to NULLs we cannot determine if the LHS +** is contained in the RHS then jump to destIfNull. If the LHS is contained +** within the RHS then fall through. +*/ +static void sqlite3ExprCodeIN( + Parse *pParse, /* Parsing and code generating context */ + Expr *pExpr, /* The IN expression */ + int destIfFalse, /* Jump here if LHS is not contained in the RHS */ + int destIfNull /* Jump here if the results are unknown due to NULLs */ +){ + int rRhsHasNull = 0; /* Register that is true if RHS contains NULL values */ + char affinity; /* Comparison affinity to use */ + int eType; /* Type of the RHS */ + int r1; /* Temporary use register */ + Vdbe *v; /* Statement under construction */ + + /* Compute the RHS. After this step, the table with cursor + ** pExpr->iTable will contains the values that make up the RHS. + */ + v = pParse->pVdbe; + assert( v!=0 ); /* OOM detected prior to this routine */ + VdbeNoopComment((v, "begin IN expr")); + eType = sqlite3FindInIndex(pParse, pExpr, &rRhsHasNull); + + /* Figure out the affinity to use to create a key from the results + ** of the expression. affinityStr stores a static string suitable for + ** P4 of OP_MakeRecord. + */ + affinity = comparisonAffinity(pExpr); + + /* Code the LHS, the from " IN (...)". + */ + sqlite3ExprCachePush(pParse); + r1 = sqlite3GetTempReg(pParse); + sqlite3ExprCode(pParse, pExpr->pLeft, r1); + sqlite3VdbeAddOp2(v, OP_IsNull, r1, destIfNull); + + + if( eType==IN_INDEX_ROWID ){ + /* In this case, the RHS is the ROWID of table b-tree + */ + sqlite3VdbeAddOp2(v, OP_MustBeInt, r1, destIfFalse); + sqlite3VdbeAddOp3(v, OP_NotExists, pExpr->iTable, destIfFalse, r1); + }else{ + /* In this case, the RHS is an index b-tree. + */ + sqlite3VdbeAddOp4(v, OP_Affinity, r1, 1, 0, &affinity, 1); + + /* If the set membership test fails, then the result of the + ** "x IN (...)" expression must be either 0 or NULL. If the set + ** contains no NULL values, then the result is 0. If the set + ** contains one or more NULL values, then the result of the + ** expression is also NULL. + */ + if( rRhsHasNull==0 || destIfFalse==destIfNull ){ + /* This branch runs if it is known at compile time that the RHS + ** cannot contain NULL values. This happens as the result + ** of a "NOT NULL" constraint in the database schema. + ** + ** Also run this branch if NULL is equivalent to FALSE + ** for this particular IN operator. + */ + sqlite3VdbeAddOp4Int(v, OP_NotFound, pExpr->iTable, destIfFalse, r1, 1); + + }else{ + /* In this branch, the RHS of the IN might contain a NULL and + ** the presence of a NULL on the RHS makes a difference in the + ** outcome. + */ + int j1, j2, j3; + + /* First check to see if the LHS is contained in the RHS. If so, + ** then the presence of NULLs in the RHS does not matter, so jump + ** over all of the code that follows. + */ + j1 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, r1, 1); + + /* Here we begin generating code that runs if the LHS is not + ** contained within the RHS. Generate additional code that + ** tests the RHS for NULLs. If the RHS contains a NULL then + ** jump to destIfNull. If there are no NULLs in the RHS then + ** jump to destIfFalse. + */ + j2 = sqlite3VdbeAddOp1(v, OP_NotNull, rRhsHasNull); + j3 = sqlite3VdbeAddOp4Int(v, OP_Found, pExpr->iTable, 0, rRhsHasNull, 1); + sqlite3VdbeAddOp2(v, OP_Integer, -1, rRhsHasNull); + sqlite3VdbeJumpHere(v, j3); + sqlite3VdbeAddOp2(v, OP_AddImm, rRhsHasNull, 1); + sqlite3VdbeJumpHere(v, j2); + + /* Jump to the appropriate target depending on whether or not + ** the RHS contains a NULL + */ + sqlite3VdbeAddOp2(v, OP_If, rRhsHasNull, destIfNull); + sqlite3VdbeAddOp2(v, OP_Goto, 0, destIfFalse); + + /* The OP_Found at the top of this branch jumps here when true, + ** causing the overall IN expression evaluation to fall through. + */ + sqlite3VdbeJumpHere(v, j1); + } + } + sqlite3ReleaseTempReg(pParse, r1); + sqlite3ExprCachePop(pParse, 1); + VdbeComment((v, "end IN expr")); } #endif /* SQLITE_OMIT_SUBQUERY */ @@ -60326,13 +62132,10 @@ double value; char *zV; sqlite3AtoF(z, &value); - if( sqlite3IsNaN(value) ){ - sqlite3VdbeAddOp2(v, OP_Null, 0, iMem); - }else{ - if( negateFlag ) value = -value; - zV = dup8bytes(v, (char*)&value); - sqlite3VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL); - } + assert( !sqlite3IsNaN(value) ); /* The new AtoF never returns NaN */ + if( negateFlag ) value = -value; + zV = dup8bytes(v, (char*)&value); + sqlite3VdbeAddOp4(v, OP_Real, 0, iMem, 0, zV, P4_REAL); } } @@ -60392,17 +62195,31 @@ assert( iReg>0 ); /* Register numbers are always positive */ assert( iCol>=-1 && iCol<32768 ); /* Finite column numbers */ - /* First replace any existing entry */ + /* The SQLITE_ColumnCache flag disables the column cache. This is used + ** for testing only - to verify that SQLite always gets the same answer + ** with and without the column cache. + */ + if( pParse->db->flags & SQLITE_ColumnCache ) return; + + /* First replace any existing entry. + ** + ** Actually, the way the column cache is currently used, we are guaranteed + ** that the object will never already be in cache. Verify this guarantee. + */ +#ifndef NDEBUG for(i=0, p=pParse->aColCache; iiReg && p->iTable==iTab && p->iColumn==iCol ){ cacheEntryClear(pParse, p); p->iLevel = pParse->iCacheLevel; p->iReg = iReg; - p->affChange = 0; p->lru = pParse->iCacheCnt++; return; } +#endif + assert( p->iReg==0 || p->iTable!=iTab || p->iColumn!=iCol ); } +#endif /* Find an empty slot and replace it */ for(i=0, p=pParse->aColCache; iiTable = iTab; p->iColumn = iCol; p->iReg = iReg; - p->affChange = 0; p->tempReg = 0; p->lru = pParse->iCacheCnt++; return; @@ -60433,7 +62249,6 @@ p->iTable = iTab; p->iColumn = iCol; p->iReg = iReg; - p->affChange = 0; p->tempReg = 0; p->lru = pParse->iCacheCnt++; return; @@ -60441,14 +62256,16 @@ } /* -** Indicate that a register is being overwritten. Purge the register -** from the column cache. +** Indicate that registers between iReg..iReg+nReg-1 are being overwritten. +** Purge the range of registers from the column cache. */ -SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse *pParse, int iReg){ +SQLITE_PRIVATE void sqlite3ExprCacheRemove(Parse *pParse, int iReg, int nReg){ int i; + int iLast = iReg + nReg - 1; struct yColCache *p; for(i=0, p=pParse->aColCache; iiReg==iReg ){ + int r = p->iReg; + if( r>=iReg && r<=iLast ){ cacheEntryClear(pParse, p); p->iReg = 0; } @@ -60507,28 +62324,20 @@ ** ** There must be an open cursor to pTab in iTable when this routine ** is called. If iColumn<0 then code is generated that extracts the rowid. -** -** This routine might attempt to reuse the value of the column that -** has already been loaded into a register. The value will always -** be used if it has not undergone any affinity changes. But if -** an affinity change has occurred, then the cached value will only be -** used if allowAffChng is true. */ SQLITE_PRIVATE int sqlite3ExprCodeGetColumn( Parse *pParse, /* Parsing and code generating context */ Table *pTab, /* Description of the table we are reading from */ int iColumn, /* Index of the table column */ int iTable, /* The cursor pointing to the table */ - int iReg, /* Store results here */ - int allowAffChng /* True if prior affinity changes are OK */ + int iReg /* Store results here */ ){ Vdbe *v = pParse->pVdbe; int i; struct yColCache *p; for(i=0, p=pParse->aColCache; iiReg>0 && p->iTable==iTable && p->iColumn==iColumn - && (!p->affChange || allowAffChng) ){ + if( p->iReg>0 && p->iTable==iTable && p->iColumn==iColumn ){ p->lru = pParse->iCacheCnt++; sqlite3ExprCachePinRegister(pParse, p->iReg); return p->iReg; @@ -60540,12 +62349,7 @@ }else if( ALWAYS(pTab!=0) ){ int op = IsVirtual(pTab) ? OP_VColumn : OP_Column; sqlite3VdbeAddOp3(v, op, iTable, iColumn, iReg); - sqlite3ColumnDefault(v, pTab, iColumn); -#ifndef SQLITE_OMIT_FLOATING_POINT - if( pTab->aCol[iColumn].affinity==SQLITE_AFF_REAL ){ - sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); - } -#endif + sqlite3ColumnDefault(v, pTab, iColumn, iReg); } sqlite3ExprCacheStore(pParse, iTable, iColumn, iReg); return iReg; @@ -60571,15 +62375,7 @@ ** registers starting with iStart. */ SQLITE_PRIVATE void sqlite3ExprCacheAffinityChange(Parse *pParse, int iStart, int iCount){ - int iEnd = iStart + iCount - 1; - int i; - struct yColCache *p; - for(i=0, p=pParse->aColCache; iiReg; - if( r>=iStart && r<=iEnd ){ - p->affChange = 1; - } - } + sqlite3ExprCacheRemove(pParse, iStart, iCount); } /* @@ -60611,19 +62407,24 @@ } } +#if defined(SQLITE_DEBUG) || defined(SQLITE_COVERAGE_TEST) /* ** Return true if any register in the range iFrom..iTo (inclusive) ** is used as part of the column cache. +** +** This routine is used within assert() and testcase() macros only +** and does not appear in a normal build. */ static int usedAsColumnCache(Parse *pParse, int iFrom, int iTo){ int i; struct yColCache *p; for(i=0, p=pParse->aColCache; iiReg; - if( r>=iFrom && r<=iTo ) return 1; + if( r>=iFrom && r<=iTo ) return 1; /*NO_TEST*/ } return 0; } +#endif /* SQLITE_DEBUG || SQLITE_COVERAGE_TEST */ /* ** If the last instruction coded is an ephemeral copy of any of @@ -60744,10 +62545,8 @@ assert( pParse->ckBase>0 ); inReg = pExpr->iColumn + pParse->ckBase; }else{ - testcase( (pExpr->flags & EP_AnyAff)!=0 ); inReg = sqlite3ExprCodeGetColumn(pParse, pExpr->pTab, - pExpr->iColumn, pExpr->iTable, target, - pExpr->flags & EP_AnyAff); + pExpr->iColumn, pExpr->iTable, target); } break; } @@ -60792,7 +62591,7 @@ assert( pExpr->u.zToken[0]!=0 ); if( pExpr->u.zToken[1]==0 && (pOp = sqlite3VdbeGetOp(v, -1))->opcode==OP_Variable - && pOp->p1+pOp->p3==pExpr->iTable + && pOp->p1+pOp->p3==pExpr->iColumn && pOp->p2+pOp->p3==target && pOp->p4.z==0 ){ @@ -60803,7 +62602,7 @@ */ pOp->p3++; }else{ - sqlite3VdbeAddOp3(v, OP_Variable, pExpr->iTable, target, 1); + sqlite3VdbeAddOp3(v, OP_Variable, pExpr->iColumn, target, 1); if( pExpr->u.zToken[1]!=0 ){ sqlite3VdbeChangeP4(v, -1, pExpr->u.zToken, 0); } @@ -60864,14 +62663,27 @@ testcase( op==TK_GE ); testcase( op==TK_EQ ); testcase( op==TK_NE ); - codeCompareOperands(pParse, pExpr->pLeft, &r1, ®Free1, - pExpr->pRight, &r2, ®Free2); + r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); + r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, inReg, SQLITE_STOREP2); testcase( regFree1==0 ); testcase( regFree2==0 ); break; } + case TK_IS: + case TK_ISNOT: { + testcase( op==TK_IS ); + testcase( op==TK_ISNOT ); + r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); + r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); + op = (op==TK_IS) ? TK_EQ : TK_NE; + codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, + r1, r2, inReg, SQLITE_STOREP2 | SQLITE_NULLEQ); + testcase( regFree1==0 ); + testcase( regFree2==0 ); + break; + } case TK_AND: case TK_OR: case TK_PLUS: @@ -60993,10 +62805,36 @@ zId = pExpr->u.zToken; nId = sqlite3Strlen30(zId); pDef = sqlite3FindFunction(db, zId, nId, nFarg, enc, 0); - assert( pDef!=0 ); + if( pDef==0 ){ + sqlite3ErrorMsg(pParse, "unknown function: %.*s()", nId, zId); + break; + } + + /* Attempt a direct implementation of the built-in COALESCE() and + ** IFNULL() functions. This avoids unnecessary evalation of + ** arguments past the first non-NULL argument. + */ + if( pDef->flags & SQLITE_FUNC_COALESCE ){ + int endCoalesce = sqlite3VdbeMakeLabel(v); + assert( nFarg>=2 ); + sqlite3ExprCode(pParse, pFarg->a[0].pExpr, target); + for(i=1; ia[i].pExpr, target); + sqlite3ExprCachePop(pParse, 1); + } + sqlite3VdbeResolveLabel(v, endCoalesce); + break; + } + + if( pFarg ){ r1 = sqlite3GetTempRange(pParse, nFarg); + sqlite3ExprCachePush(pParse); /* Ticket 2ea2425d34be */ sqlite3ExprCodeExprList(pParse, pFarg, r1, 1); + sqlite3ExprCachePop(pParse, 1); /* Ticket 2ea2425d34be */ }else{ r1 = 0; } @@ -61037,7 +62875,6 @@ if( nFarg ){ sqlite3ReleaseTempRange(pParse, r1, nFarg); } - sqlite3ExprCacheAffinityChange(pParse, r1, nFarg); break; } #ifndef SQLITE_OMIT_SUBQUERY @@ -61045,100 +62882,23 @@ case TK_SELECT: { testcase( op==TK_EXISTS ); testcase( op==TK_SELECT ); - sqlite3CodeSubselect(pParse, pExpr, 0, 0); - inReg = pExpr->iColumn; + inReg = sqlite3CodeSubselect(pParse, pExpr, 0, 0); break; } case TK_IN: { - int rNotFound = 0; - int rMayHaveNull = 0; - int j2, j3, j4, j5; - char affinity; - int eType; - - VdbeNoopComment((v, "begin IN expr r%d", target)); - eType = sqlite3FindInIndex(pParse, pExpr, &rMayHaveNull); - if( rMayHaveNull ){ - rNotFound = ++pParse->nMem; - } - - /* Figure out the affinity to use to create a key from the results - ** of the expression. affinityStr stores a static string suitable for - ** P4 of OP_MakeRecord. - */ - affinity = comparisonAffinity(pExpr); - - - /* Code the from " IN (...)". The temporary table - ** pExpr->iTable contains the values that make up the (...) set. - */ - sqlite3ExprCachePush(pParse); - sqlite3ExprCode(pParse, pExpr->pLeft, target); - j2 = sqlite3VdbeAddOp1(v, OP_IsNull, target); - if( eType==IN_INDEX_ROWID ){ - j3 = sqlite3VdbeAddOp1(v, OP_MustBeInt, target); - j4 = sqlite3VdbeAddOp3(v, OP_NotExists, pExpr->iTable, 0, target); - sqlite3VdbeAddOp2(v, OP_Integer, 1, target); - j5 = sqlite3VdbeAddOp0(v, OP_Goto); - sqlite3VdbeJumpHere(v, j3); - sqlite3VdbeJumpHere(v, j4); - sqlite3VdbeAddOp2(v, OP_Integer, 0, target); - }else{ - r2 = regFree2 = sqlite3GetTempReg(pParse); - - /* Create a record and test for set membership. If the set contains - ** the value, then jump to the end of the test code. The target - ** register still contains the true (1) value written to it earlier. - */ - sqlite3VdbeAddOp4(v, OP_MakeRecord, target, 1, r2, &affinity, 1); - sqlite3VdbeAddOp2(v, OP_Integer, 1, target); - j5 = sqlite3VdbeAddOp3(v, OP_Found, pExpr->iTable, 0, r2); - - /* If the set membership test fails, then the result of the - ** "x IN (...)" expression must be either 0 or NULL. If the set - ** contains no NULL values, then the result is 0. If the set - ** contains one or more NULL values, then the result of the - ** expression is also NULL. - */ - if( rNotFound==0 ){ - /* This branch runs if it is known at compile time (now) that - ** the set contains no NULL values. This happens as the result - ** of a "NOT NULL" constraint in the database schema. No need - ** to test the data structure at runtime in this case. - */ - sqlite3VdbeAddOp2(v, OP_Integer, 0, target); - }else{ - /* This block populates the rNotFound register with either NULL - ** or 0 (an integer value). If the data structure contains one - ** or more NULLs, then set rNotFound to NULL. Otherwise, set it - ** to 0. If register rMayHaveNull is already set to some value - ** other than NULL, then the test has already been run and - ** rNotFound is already populated. - */ - static const char nullRecord[] = { 0x02, 0x00 }; - j3 = sqlite3VdbeAddOp1(v, OP_NotNull, rMayHaveNull); - sqlite3VdbeAddOp2(v, OP_Null, 0, rNotFound); - sqlite3VdbeAddOp4(v, OP_Blob, 2, rMayHaveNull, 0, - nullRecord, P4_STATIC); - j4 = sqlite3VdbeAddOp3(v, OP_Found, pExpr->iTable, 0, rMayHaveNull); - sqlite3VdbeAddOp2(v, OP_Integer, 0, rNotFound); - sqlite3VdbeJumpHere(v, j4); - sqlite3VdbeJumpHere(v, j3); - - /* Copy the value of register rNotFound (which is either NULL or 0) - ** into the target register. This will be the result of the - ** expression. - */ - sqlite3VdbeAddOp2(v, OP_Copy, rNotFound, target); - } - } - sqlite3VdbeJumpHere(v, j2); - sqlite3VdbeJumpHere(v, j5); - sqlite3ExprCachePop(pParse, 1); - VdbeComment((v, "end IN expr r%d", target)); + int destIfFalse = sqlite3VdbeMakeLabel(v); + int destIfNull = sqlite3VdbeMakeLabel(v); + sqlite3VdbeAddOp2(v, OP_Null, 0, target); + sqlite3ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull); + sqlite3VdbeAddOp2(v, OP_Integer, 1, target); + sqlite3VdbeResolveLabel(v, destIfFalse); + sqlite3VdbeAddOp2(v, OP_AddImm, target, 0); + sqlite3VdbeResolveLabel(v, destIfNull); break; } -#endif +#endif /* SQLITE_OMIT_SUBQUERY */ + + /* ** x BETWEEN y AND z ** @@ -61155,8 +62915,8 @@ struct ExprList_item *pLItem = pExpr->x.pList->a; Expr *pRight = pLItem->pExpr; - codeCompareOperands(pParse, pLeft, &r1, ®Free1, - pRight, &r2, ®Free2); + r1 = sqlite3ExprCodeTemp(pParse, pLeft, ®Free1); + r2 = sqlite3ExprCodeTemp(pParse, pRight, ®Free2); testcase( regFree1==0 ); testcase( regFree2==0 ); r3 = sqlite3GetTempReg(pParse); @@ -61179,6 +62939,58 @@ break; } + case TK_TRIGGER: { + /* If the opcode is TK_TRIGGER, then the expression is a reference + ** to a column in the new.* or old.* pseudo-tables available to + ** trigger programs. In this case Expr.iTable is set to 1 for the + ** new.* pseudo-table, or 0 for the old.* pseudo-table. Expr.iColumn + ** is set to the column of the pseudo-table to read, or to -1 to + ** read the rowid field. + ** + ** The expression is implemented using an OP_Param opcode. The p1 + ** parameter is set to 0 for an old.rowid reference, or to (i+1) + ** to reference another column of the old.* pseudo-table, where + ** i is the index of the column. For a new.rowid reference, p1 is + ** set to (n+1), where n is the number of columns in each pseudo-table. + ** For a reference to any other column in the new.* pseudo-table, p1 + ** is set to (n+2+i), where n and i are as defined previously. For + ** example, if the table on which triggers are being fired is + ** declared as: + ** + ** CREATE TABLE t1(a, b); + ** + ** Then p1 is interpreted as follows: + ** + ** p1==0 -> old.rowid p1==3 -> new.rowid + ** p1==1 -> old.a p1==4 -> new.a + ** p1==2 -> old.b p1==5 -> new.b + */ + Table *pTab = pExpr->pTab; + int p1 = pExpr->iTable * (pTab->nCol+1) + 1 + pExpr->iColumn; + + assert( pExpr->iTable==0 || pExpr->iTable==1 ); + assert( pExpr->iColumn>=-1 && pExpr->iColumnnCol ); + assert( pTab->iPKey<0 || pExpr->iColumn!=pTab->iPKey ); + assert( p1>=0 && p1<(pTab->nCol*2+2) ); + + sqlite3VdbeAddOp2(v, OP_Param, p1, target); + VdbeComment((v, "%s.%s -> $%d", + (pExpr->iTable ? "new" : "old"), + (pExpr->iColumn<0 ? "rowid" : pExpr->pTab->aCol[pExpr->iColumn].zName), + target + )); + + /* If the column has REAL affinity, it may currently be stored as an + ** integer. Use OP_RealAffinity to make sure it is really real. */ + if( pExpr->iColumn>=0 + && pTab->aCol[pExpr->iColumn].affinity==SQLITE_AFF_REAL + ){ + sqlite3VdbeAddOp1(v, OP_RealAffinity, target); + } + break; + } + + /* ** Form A: ** CASE x WHEN e1 THEN r1 WHEN e2 THEN r2 ... WHEN eN THEN rN ELSE y END @@ -61263,24 +63075,27 @@ } #ifndef SQLITE_OMIT_TRIGGER case TK_RAISE: { - if( !pParse->trigStack ){ + assert( pExpr->affinity==OE_Rollback + || pExpr->affinity==OE_Abort + || pExpr->affinity==OE_Fail + || pExpr->affinity==OE_Ignore + ); + if( !pParse->pTriggerTab ){ sqlite3ErrorMsg(pParse, "RAISE() may only be used within a trigger-program"); return 0; } - if( pExpr->affinity!=OE_Ignore ){ - assert( pExpr->affinity==OE_Rollback || - pExpr->affinity == OE_Abort || - pExpr->affinity == OE_Fail ); - assert( !ExprHasProperty(pExpr, EP_IntValue) ); - sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, pExpr->affinity, 0, - pExpr->u.zToken, 0); - } else { - assert( pExpr->affinity == OE_Ignore ); - sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0); - sqlite3VdbeAddOp2(v, OP_Goto, 0, pParse->trigStack->ignoreJump); - VdbeComment((v, "raise(IGNORE)")); + if( pExpr->affinity==OE_Abort ){ + sqlite3MayAbort(pParse); + } + assert( !ExprHasProperty(pExpr, EP_IntValue) ); + if( pExpr->affinity==OE_Ignore ){ + sqlite3VdbeAddOp4( + v, OP_Halt, SQLITE_OK, OE_Ignore, 0, pExpr->u.zToken,0); + }else{ + sqlite3HaltConstraint(pParse, pExpr->affinity, pExpr->u.zToken, 0); } + break; } #endif @@ -61356,6 +63171,7 @@ iMem = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Copy, inReg, iMem); pExpr->iTable = iMem; + pExpr->op2 = pExpr->op; pExpr->op = TK_REGISTER; } return inReg; @@ -61429,6 +63245,7 @@ static int evalConstExpr(Walker *pWalker, Expr *pExpr){ Parse *pParse = pWalker->pParse; switch( pExpr->op ){ + case TK_IN: case TK_REGISTER: { return WRC_Prune; } @@ -61456,6 +63273,7 @@ int r2; r2 = sqlite3ExprCodeTarget(pParse, pExpr, r1); if( NEVER(r1!=r2) ) sqlite3ReleaseTempReg(pParse, r1); + pExpr->op2 = pExpr->op; pExpr->op = TK_REGISTER; pExpr->iTable = r2; return WRC_Prune; @@ -61512,6 +63330,62 @@ } /* +** Generate code for a BETWEEN operator. +** +** x BETWEEN y AND z +** +** The above is equivalent to +** +** x>=y AND x<=z +** +** Code it as such, taking care to do the common subexpression +** elementation of x. +*/ +static void exprCodeBetween( + Parse *pParse, /* Parsing and code generating context */ + Expr *pExpr, /* The BETWEEN expression */ + int dest, /* Jump here if the jump is taken */ + int jumpIfTrue, /* Take the jump if the BETWEEN is true */ + int jumpIfNull /* Take the jump if the BETWEEN is NULL */ +){ + Expr exprAnd; /* The AND operator in x>=y AND x<=z */ + Expr compLeft; /* The x>=y term */ + Expr compRight; /* The x<=z term */ + Expr exprX; /* The x subexpression */ + int regFree1 = 0; /* Temporary use register */ + + assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); + exprX = *pExpr->pLeft; + exprAnd.op = TK_AND; + exprAnd.pLeft = &compLeft; + exprAnd.pRight = &compRight; + compLeft.op = TK_GE; + compLeft.pLeft = &exprX; + compLeft.pRight = pExpr->x.pList->a[0].pExpr; + compRight.op = TK_LE; + compRight.pLeft = &exprX; + compRight.pRight = pExpr->x.pList->a[1].pExpr; + exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, ®Free1); + exprX.op = TK_REGISTER; + if( jumpIfTrue ){ + sqlite3ExprIfTrue(pParse, &exprAnd, dest, jumpIfNull); + }else{ + sqlite3ExprIfFalse(pParse, &exprAnd, dest, jumpIfNull); + } + sqlite3ReleaseTempReg(pParse, regFree1); + + /* Ensure adequate test coverage */ + testcase( jumpIfTrue==0 && jumpIfNull==0 && regFree1==0 ); + testcase( jumpIfTrue==0 && jumpIfNull==0 && regFree1!=0 ); + testcase( jumpIfTrue==0 && jumpIfNull!=0 && regFree1==0 ); + testcase( jumpIfTrue==0 && jumpIfNull!=0 && regFree1!=0 ); + testcase( jumpIfTrue!=0 && jumpIfNull==0 && regFree1==0 ); + testcase( jumpIfTrue!=0 && jumpIfNull==0 && regFree1!=0 ); + testcase( jumpIfTrue!=0 && jumpIfNull!=0 && regFree1==0 ); + testcase( jumpIfTrue!=0 && jumpIfNull!=0 && regFree1!=0 ); +} + +/* ** Generate code for a boolean expression such that a jump is made ** to the label "dest" if the expression is true but execution ** continues straight thru if the expression is false. @@ -61577,14 +63451,27 @@ testcase( op==TK_EQ ); testcase( op==TK_NE ); testcase( jumpIfNull==0 ); - codeCompareOperands(pParse, pExpr->pLeft, &r1, ®Free1, - pExpr->pRight, &r2, ®Free2); + r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); + r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, dest, jumpIfNull); testcase( regFree1==0 ); testcase( regFree2==0 ); break; } + case TK_IS: + case TK_ISNOT: { + testcase( op==TK_IS ); + testcase( op==TK_ISNOT ); + r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); + r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); + op = (op==TK_IS) ? TK_EQ : TK_NE; + codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, + r1, r2, dest, SQLITE_NULLEQ); + testcase( regFree1==0 ); + testcase( regFree2==0 ); + break; + } case TK_ISNULL: case TK_NOTNULL: { assert( TK_ISNULL==OP_IsNull ); @@ -61597,36 +63484,16 @@ break; } case TK_BETWEEN: { - /* x BETWEEN y AND z - ** - ** Is equivalent to - ** - ** x>=y AND x<=z - ** - ** Code it as such, taking care to do the common subexpression - ** elementation of x. - */ - Expr exprAnd; - Expr compLeft; - Expr compRight; - Expr exprX; - - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - exprX = *pExpr->pLeft; - exprAnd.op = TK_AND; - exprAnd.pLeft = &compLeft; - exprAnd.pRight = &compRight; - compLeft.op = TK_GE; - compLeft.pLeft = &exprX; - compLeft.pRight = pExpr->x.pList->a[0].pExpr; - compRight.op = TK_LE; - compRight.pLeft = &exprX; - compRight.pRight = pExpr->x.pList->a[1].pExpr; - exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, ®Free1); - testcase( regFree1==0 ); - exprX.op = TK_REGISTER; testcase( jumpIfNull==0 ); - sqlite3ExprIfTrue(pParse, &exprAnd, dest, jumpIfNull); + exprCodeBetween(pParse, pExpr, dest, 1, jumpIfNull); + break; + } + case TK_IN: { + int destIfFalse = sqlite3VdbeMakeLabel(v); + int destIfNull = jumpIfNull ? dest : destIfFalse; + sqlite3ExprCodeIN(pParse, pExpr, destIfFalse, destIfNull); + sqlite3VdbeAddOp2(v, OP_Goto, 0, dest); + sqlite3VdbeResolveLabel(v, destIfFalse); break; } default: { @@ -61710,6 +63577,7 @@ break; } case TK_NOT: { + testcase( jumpIfNull==0 ); sqlite3ExprIfTrue(pParse, pExpr->pLeft, dest, jumpIfNull); break; } @@ -61726,14 +63594,27 @@ testcase( op==TK_EQ ); testcase( op==TK_NE ); testcase( jumpIfNull==0 ); - codeCompareOperands(pParse, pExpr->pLeft, &r1, ®Free1, - pExpr->pRight, &r2, ®Free2); + r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); + r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, r1, r2, dest, jumpIfNull); testcase( regFree1==0 ); testcase( regFree2==0 ); break; } + case TK_IS: + case TK_ISNOT: { + testcase( pExpr->op==TK_IS ); + testcase( pExpr->op==TK_ISNOT ); + r1 = sqlite3ExprCodeTemp(pParse, pExpr->pLeft, ®Free1); + r2 = sqlite3ExprCodeTemp(pParse, pExpr->pRight, ®Free2); + op = (pExpr->op==TK_IS) ? TK_NE : TK_EQ; + codeCompare(pParse, pExpr->pLeft, pExpr->pRight, op, + r1, r2, dest, SQLITE_NULLEQ); + testcase( regFree1==0 ); + testcase( regFree2==0 ); + break; + } case TK_ISNULL: case TK_NOTNULL: { testcase( op==TK_ISNULL ); @@ -61744,36 +63625,18 @@ break; } case TK_BETWEEN: { - /* x BETWEEN y AND z - ** - ** Is equivalent to - ** - ** x>=y AND x<=z - ** - ** Code it as such, taking care to do the common subexpression - ** elementation of x. - */ - Expr exprAnd; - Expr compLeft; - Expr compRight; - Expr exprX; - - assert( !ExprHasProperty(pExpr, EP_xIsSelect) ); - exprX = *pExpr->pLeft; - exprAnd.op = TK_AND; - exprAnd.pLeft = &compLeft; - exprAnd.pRight = &compRight; - compLeft.op = TK_GE; - compLeft.pLeft = &exprX; - compLeft.pRight = pExpr->x.pList->a[0].pExpr; - compRight.op = TK_LE; - compRight.pLeft = &exprX; - compRight.pRight = pExpr->x.pList->a[1].pExpr; - exprX.iTable = sqlite3ExprCodeTemp(pParse, &exprX, ®Free1); - testcase( regFree1==0 ); - exprX.op = TK_REGISTER; testcase( jumpIfNull==0 ); - sqlite3ExprIfFalse(pParse, &exprAnd, dest, jumpIfNull); + exprCodeBetween(pParse, pExpr, dest, 0, jumpIfNull); + break; + } + case TK_IN: { + if( jumpIfNull ){ + sqlite3ExprCodeIN(pParse, pExpr, dest, dest); + }else{ + int destIfNull = sqlite3VdbeMakeLabel(v); + sqlite3ExprCodeIN(pParse, pExpr, dest, destIfNull); + sqlite3VdbeResolveLabel(v, destIfNull); + } break; } default: { @@ -62091,7 +63954,8 @@ int i, n; i = pParse->iRangeReg; n = pParse->nRangeReg; - if( nReg<=n && !usedAsColumnCache(pParse, i, i+n-1) ){ + if( nReg<=n ){ + assert( !usedAsColumnCache(pParse, i, i+n-1) ); pParse->iRangeReg += nReg; pParse->nRangeReg -= nReg; }else{ @@ -62101,6 +63965,7 @@ return i; } SQLITE_PRIVATE void sqlite3ReleaseTempRange(Parse *pParse, int iReg, int nReg){ + sqlite3ExprCacheRemove(pParse, iReg, nReg); if( nReg>pParse->nRangeReg ){ pParse->nRangeReg = nReg; pParse->iRangeReg = iReg; @@ -62122,8 +63987,6 @@ ************************************************************************* ** This file contains C code routines that used to generate VDBE code ** that implements the ALTER TABLE command. -** -** $Id: alter.c,v 1.61 2009/06/03 11:25:07 danielk1977 Exp $ */ /* @@ -62195,6 +64058,69 @@ } } +/* +** This C function implements an SQL user function that is used by SQL code +** generated by the ALTER TABLE ... RENAME command to modify the definition +** of any foreign key constraints that use the table being renamed as the +** parent table. It is passed three arguments: +** +** 1) The complete text of the CREATE TABLE statement being modified, +** 2) The old name of the table being renamed, and +** 3) The new name of the table being renamed. +** +** It returns the new CREATE TABLE statement. For example: +** +** sqlite_rename_parent('CREATE TABLE t1(a REFERENCES t2)', 't2', 't3') +** -> 'CREATE TABLE t1(a REFERENCES t3)' +*/ +#ifndef SQLITE_OMIT_FOREIGN_KEY +static void renameParentFunc( + sqlite3_context *context, + int NotUsed, + sqlite3_value **argv +){ + sqlite3 *db = sqlite3_context_db_handle(context); + char *zOutput = 0; + char *zResult; + unsigned char const *zInput = sqlite3_value_text(argv[0]); + unsigned char const *zOld = sqlite3_value_text(argv[1]); + unsigned char const *zNew = sqlite3_value_text(argv[2]); + + unsigned const char *z; /* Pointer to token */ + int n; /* Length of token z */ + int token; /* Type of token */ + + UNUSED_PARAMETER(NotUsed); + for(z=zInput; *z; z=z+n){ + n = sqlite3GetToken(z, &token); + if( token==TK_REFERENCES ){ + char *zParent; + do { + z += n; + n = sqlite3GetToken(z, &token); + }while( token==TK_SPACE ); + + zParent = sqlite3DbStrNDup(db, (const char *)z, n); + if( zParent==0 ) break; + sqlite3Dequote(zParent); + if( 0==sqlite3StrICmp((const char *)zOld, zParent) ){ + char *zOut = sqlite3MPrintf(db, "%s%.*s\"%w\"", + (zOutput?zOutput:""), z-zInput, zInput, (const char *)zNew + ); + sqlite3DbFree(db, zOutput); + zOutput = zOut; + zInput = &z[n]; + } + sqlite3DbFree(db, zParent); + } + } + + zResult = sqlite3MPrintf(db, "%s%s", (zOutput?zOutput:""), zInput), + sqlite3_result_text(context, zResult, -1, SQLITE_DYNAMIC); + sqlite3DbFree(db, zOutput); +} +#endif + #ifndef SQLITE_OMIT_TRIGGER /* This function is used by SQL generated to implement the ** ALTER TABLE command. The first argument is the text of a CREATE TRIGGER @@ -62282,9 +64208,57 @@ sqlite3CreateFunc(db, "sqlite_rename_trigger", 2, SQLITE_UTF8, 0, renameTriggerFunc, 0, 0); #endif +#ifndef SQLITE_OMIT_FOREIGN_KEY + sqlite3CreateFunc(db, "sqlite_rename_parent", 3, SQLITE_UTF8, 0, + renameParentFunc, 0, 0); +#endif } /* +** This function is used to create the text of expressions of the form: +** +** name= OR name= OR ... +** +** If argument zWhere is NULL, then a pointer string containing the text +** "name=" is returned, where is the quoted version +** of the string passed as argument zConstant. The returned buffer is +** allocated using sqlite3DbMalloc(). It is the responsibility of the +** caller to ensure that it is eventually freed. +** +** If argument zWhere is not NULL, then the string returned is +** " OR name=", where is the contents of zWhere. +** In this case zWhere is passed to sqlite3DbFree() before returning. +** +*/ +static char *whereOrName(sqlite3 *db, char *zWhere, char *zConstant){ + char *zNew; + if( !zWhere ){ + zNew = sqlite3MPrintf(db, "name=%Q", zConstant); + }else{ + zNew = sqlite3MPrintf(db, "%s OR name=%Q", zWhere, zConstant); + sqlite3DbFree(db, zWhere); + } + return zNew; +} + +#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) +/* +** Generate the text of a WHERE expression which can be used to select all +** tables that have foreign key constraints that refer to table pTab (i.e. +** constraints for which pTab is the parent table) from the sqlite_master +** table. +*/ +static char *whereForeignKeys(Parse *pParse, Table *pTab){ + FKey *p; + char *zWhere = 0; + for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ + zWhere = whereOrName(pParse->db, zWhere, p->pFrom->zName); + } + return zWhere; +} +#endif + +/* ** Generate the text of a WHERE expression which can be used to select all ** temporary triggers on table pTab from the sqlite_temp_master table. If ** table pTab has no temporary triggers, or is itself stored in the @@ -62293,7 +64267,6 @@ static char *whereTempTriggers(Parse *pParse, Table *pTab){ Trigger *pTrig; char *zWhere = 0; - char *tmp = 0; const Schema *pTempSchema = pParse->db->aDb[1].pSchema; /* Temp db schema */ /* If the table is not located in the temp-db (in which case NULL is @@ -62305,13 +64278,7 @@ sqlite3 *db = pParse->db; for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ if( pTrig->pSchema==pTempSchema ){ - if( !zWhere ){ - zWhere = sqlite3MPrintf(db, "name=%Q", pTrig->name); - }else{ - tmp = zWhere; - zWhere = sqlite3MPrintf(db, "%s OR name=%Q", zWhere, pTrig->name); - sqlite3DbFree(db, tmp); - } + zWhere = whereOrName(db, zWhere, pTrig->zName); } } } @@ -62345,11 +64312,11 @@ for(pTrig=sqlite3TriggerList(pParse, pTab); pTrig; pTrig=pTrig->pNext){ int iTrigDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); assert( iTrigDb==iDb || iTrigDb==1 ); - sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->name, 0); + sqlite3VdbeAddOp4(v, OP_DropTrigger, iTrigDb, 0, 0, pTrig->zName, 0); } #endif - /* Drop the table and index from the internal schema */ + /* Drop the table and index from the internal schema. */ sqlite3VdbeAddOp4(v, OP_DropTable, iDb, 0, 0, pTab->zName, 0); /* Reload the table, index and permanent trigger schemas. */ @@ -62387,7 +64354,7 @@ #ifndef SQLITE_OMIT_TRIGGER char *zWhere = 0; /* Where clause to locate temp triggers */ #endif - int isVirtualRename = 0; /* True if this is a v-table with an xRename() */ + VTable *pVTab = 0; /* Non-zero if this is a v-tab with an xRename() */ if( NEVER(db->mallocFailed) ) goto exit_rename_table; assert( pSrc->nSrc==1 ); @@ -62442,8 +64409,11 @@ if( sqlite3ViewGetColumnNames(pParse, pTab) ){ goto exit_rename_table; } - if( IsVirtual(pTab) && pTab->pMod->pModule->xRename ){ - isVirtualRename = 1; + if( IsVirtual(pTab) ){ + pVTab = sqlite3GetVTable(db, pTab); + if( pVTab->pVtab->pModule->xRename==0 ){ + pVTab = 0; + } } #endif @@ -62456,7 +64426,7 @@ if( v==0 ){ goto exit_rename_table; } - sqlite3BeginWriteOperation(pParse, isVirtualRename, iDb); + sqlite3BeginWriteOperation(pParse, pVTab!=0, iDb); sqlite3ChangeCookie(pParse, iDb); /* If this is a virtual table, invoke the xRename() function if @@ -62465,10 +64435,11 @@ ** SQLite tables) that are identified by the name of the virtual table. */ #ifndef SQLITE_OMIT_VIRTUALTABLE - if( isVirtualRename ){ + if( pVTab ){ int i = ++pParse->nMem; sqlite3VdbeAddOp4(v, OP_String8, 0, i, 0, zName, 0); - sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pTab->pVtab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VRename, i, 0, 0,(const char*)pVTab, P4_VTAB); + sqlite3MayAbort(pParse); } #endif @@ -62476,6 +64447,21 @@ zTabName = pTab->zName; nTabName = sqlite3Utf8CharLen(zTabName, -1); +#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) + if( db->flags&SQLITE_ForeignKeys ){ + /* If foreign-key support is enabled, rewrite the CREATE TABLE + ** statements corresponding to all child tables of foreign key constraints + ** for which the renamed table is the parent table. */ + if( (zWhere=whereForeignKeys(pParse, pTab))!=0 ){ + sqlite3NestedParse(pParse, + "UPDATE sqlite_master SET " + "sql = sqlite_rename_parent(sql, %Q, %Q) " + "WHERE %s;", zTabName, zName, zWhere); + sqlite3DbFree(db, zWhere); + } + } +#endif + /* Modify the sqlite_master table to use the new table name. */ sqlite3NestedParse(pParse, "UPDATE %Q.%s SET " @@ -62527,6 +64513,18 @@ } #endif +#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) + if( db->flags&SQLITE_ForeignKeys ){ + FKey *p; + for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ + Table *pFrom = p->pFrom; + if( pFrom!=pTab ){ + reloadTableSchema(pParse, p->pFrom, pFrom->zName); + } + } + } +#endif + /* Drop and reload the internal table schema. */ reloadTableSchema(pParse, pTab, zName); @@ -62621,6 +64619,11 @@ sqlite3ErrorMsg(pParse, "Cannot add a UNIQUE column"); return; } + if( (db->flags&SQLITE_ForeignKeys) && pNew->pFKey && pDflt ){ + sqlite3ErrorMsg(pParse, + "Cannot add a REFERENCES column with non-NULL default value"); + return; + } if( pCol->notNull && !pDflt ){ sqlite3ErrorMsg(pParse, "Cannot add a NOT NULL column with default value NULL"); @@ -62778,18 +64781,24 @@ ** ************************************************************************* ** This file contains code associated with the ANALYZE command. -** -** @(#) $Id: analyze.c,v 1.52 2009/04/16 17:45:48 drh Exp $ */ #ifndef SQLITE_OMIT_ANALYZE /* -** This routine generates code that opens the sqlite_stat1 table on cursor -** iStatCur. +** This routine generates code that opens the sqlite_stat1 table for +** writing with cursor iStatCur. If the library was built with the +** SQLITE_ENABLE_STAT2 macro defined, then the sqlite_stat2 table is +** opened for writing using cursor (iStatCur+1) ** ** If the sqlite_stat1 tables does not previously exist, it is created. -** If it does previously exist, all entires associated with table zWhere -** are removed. If zWhere==0 then all entries are removed. +** Similarly, if the sqlite_stat2 table does not exist and the library +** is compiled with SQLITE_ENABLE_STAT2 defined, it is created. +** +** Argument zWhere may be a pointer to a buffer containing a table name, +** or it may be a NULL pointer. If it is not NULL, then all entries in +** the sqlite_stat1 and (if applicable) sqlite_stat2 tables associated +** with the named table are deleted. If zWhere==0, then code is generated +** to delete all stat table entries. */ static void openStatTable( Parse *pParse, /* Parsing context */ @@ -62797,53 +64806,64 @@ int iStatCur, /* Open the sqlite_stat1 table on this cursor */ const char *zWhere /* Delete entries associated with this table */ ){ + static struct { + const char *zName; + const char *zCols; + } aTable[] = { + { "sqlite_stat1", "tbl,idx,stat" }, +#ifdef SQLITE_ENABLE_STAT2 + { "sqlite_stat2", "tbl,idx,sampleno,sample" }, +#endif + }; + + int aRoot[] = {0, 0}; + u8 aCreateTbl[] = {0, 0}; + + int i; sqlite3 *db = pParse->db; Db *pDb; - int iRootPage; - u8 createStat1 = 0; - Table *pStat; Vdbe *v = sqlite3GetVdbe(pParse); - if( v==0 ) return; assert( sqlite3BtreeHoldsAllMutexes(db) ); assert( sqlite3VdbeDb(v)==db ); pDb = &db->aDb[iDb]; - if( (pStat = sqlite3FindTable(db, "sqlite_stat1", pDb->zName))==0 ){ - /* The sqlite_stat1 tables does not exist. Create it. - ** Note that a side-effect of the CREATE TABLE statement is to leave - ** the rootpage of the new table in register pParse->regRoot. This is - ** important because the OpenWrite opcode below will be needing it. */ - sqlite3NestedParse(pParse, - "CREATE TABLE %Q.sqlite_stat1(tbl,idx,stat)", - pDb->zName - ); - iRootPage = pParse->regRoot; - createStat1 = 1; /* Cause rootpage to be taken from top of stack */ - }else if( zWhere ){ - /* The sqlite_stat1 table exists. Delete all entries associated with - ** the table zWhere. */ - sqlite3NestedParse(pParse, - "DELETE FROM %Q.sqlite_stat1 WHERE tbl=%Q", - pDb->zName, zWhere - ); - iRootPage = pStat->tnum; - }else{ - /* The sqlite_stat1 table already exists. Delete all rows. */ - iRootPage = pStat->tnum; - sqlite3VdbeAddOp2(v, OP_Clear, pStat->tnum, iDb); + + for(i=0; izName))==0 ){ + /* The sqlite_stat[12] table does not exist. Create it. Note that a + ** side-effect of the CREATE TABLE statement is to leave the rootpage + ** of the new table in register pParse->regRoot. This is important + ** because the OpenWrite opcode below will be needing it. */ + sqlite3NestedParse(pParse, + "CREATE TABLE %Q.%s(%s)", pDb->zName, zTab, aTable[i].zCols + ); + aRoot[i] = pParse->regRoot; + aCreateTbl[i] = 1; + }else{ + /* The table already exists. If zWhere is not NULL, delete all entries + ** associated with the table zWhere. If zWhere is NULL, delete the + ** entire contents of the table. */ + aRoot[i] = pStat->tnum; + sqlite3TableLock(pParse, iDb, aRoot[i], 1, zTab); + if( zWhere ){ + sqlite3NestedParse(pParse, + "DELETE FROM %Q.%s WHERE tbl=%Q", pDb->zName, zTab, zWhere + ); + }else{ + /* The sqlite_stat[12] table already exists. Delete all rows. */ + sqlite3VdbeAddOp2(v, OP_Clear, aRoot[i], iDb); + } + } } - /* Open the sqlite_stat1 table for writing. Unless it was created - ** by this vdbe program, lock it for writing at the shared-cache level. - ** If this vdbe did create the sqlite_stat1 table, then it must have - ** already obtained a schema-lock, making the write-lock redundant. - */ - if( !createStat1 ){ - sqlite3TableLock(pParse, iDb, iRootPage, 1, "sqlite_stat1"); + /* Open the sqlite_stat[12] tables for writing. */ + for(i=0; idb; /* Database handle */ + Index *pIdx; /* An index to being analyzed */ + int iIdxCur; /* Cursor open on index being analyzed */ + Vdbe *v; /* The virtual machine being built up */ + int i; /* Loop counter */ + int topOfLoop; /* The top of the loop */ + int endOfLoop; /* The end of the loop */ + int addr; /* The address of an instruction */ + int iDb; /* Index of database containing pTab */ + int regTabname = iMem++; /* Register containing table name */ + int regIdxname = iMem++; /* Register containing index name */ + int regSampleno = iMem++; /* Register containing next sample number */ + int regCol = iMem++; /* Content of a column analyzed table */ + int regRec = iMem++; /* Register holding completed record */ + int regTemp = iMem++; /* Temporary use register */ + int regRowid = iMem++; /* Rowid for the inserted record */ + +#ifdef SQLITE_ENABLE_STAT2 + int regTemp2 = iMem++; /* Temporary use register */ + int regSamplerecno = iMem++; /* Index of next sample to record */ + int regRecno = iMem++; /* Current sample index */ + int regLast = iMem++; /* Index of last sample to record */ + int regFirst = iMem++; /* Index of first sample to record */ +#endif v = sqlite3GetVdbe(pParse); if( v==0 || NEVER(pTab==0) || pTab->pIndex==0 ){ /* Do no analysis for tables that have no indices */ return; } - assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); - iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + assert( sqlite3BtreeHoldsAllMutexes(db) ); + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); assert( iDb>=0 ); #ifndef SQLITE_OMIT_AUTHORIZATION if( sqlite3AuthCheck(pParse, SQLITE_ANALYZE, pTab->zName, 0, - pParse->db->aDb[iDb].zName ) ){ + db->aDb[iDb].zName ) ){ return; } #endif @@ -62886,40 +64921,66 @@ iIdxCur = pParse->nTab++; for(pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext){ + int nCol = pIdx->nColumn; KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); - int regFields; /* Register block for building records */ - int regRec; /* Register holding completed record */ - int regTemp; /* Temporary use register */ - int regCol; /* Content of a column from the table being analyzed */ - int regRowid; /* Rowid for the inserted record */ - int regF2; - /* Open a cursor to the index to be analyzed - */ - assert( iDb==sqlite3SchemaToIndex(pParse->db, pIdx->pSchema) ); - nCol = pIdx->nColumn; + if( iMem+1+(nCol*2)>pParse->nMem ){ + pParse->nMem = iMem+1+(nCol*2); + } + + /* Open a cursor to the index to be analyzed. */ + assert( iDb==sqlite3SchemaToIndex(db, pIdx->pSchema) ); sqlite3VdbeAddOp4(v, OP_OpenRead, iIdxCur, pIdx->tnum, iDb, (char *)pKey, P4_KEYINFO_HANDOFF); VdbeComment((v, "%s", pIdx->zName)); - regFields = iMem+nCol*2; - regTemp = regRowid = regCol = regFields+3; - regRec = regCol+1; - if( regRec>pParse->nMem ){ - pParse->nMem = regRec; + + /* Populate the registers containing the table and index names. */ + if( pTab->pIndex==pIdx ){ + sqlite3VdbeAddOp4(v, OP_String8, 0, regTabname, 0, pTab->zName, 0); + } + sqlite3VdbeAddOp4(v, OP_String8, 0, regIdxname, 0, pIdx->zName, 0); + +#ifdef SQLITE_ENABLE_STAT2 + + /* If this iteration of the loop is generating code to analyze the + ** first index in the pTab->pIndex list, then register regLast has + ** not been populated. In this case populate it now. */ + if( pTab->pIndex==pIdx ){ + sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES, regSamplerecno); + sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES*2-1, regTemp); + sqlite3VdbeAddOp2(v, OP_Integer, SQLITE_INDEX_SAMPLES*2, regTemp2); + + sqlite3VdbeAddOp2(v, OP_Count, iIdxCur, regLast); + sqlite3VdbeAddOp2(v, OP_Null, 0, regFirst); + addr = sqlite3VdbeAddOp3(v, OP_Lt, regSamplerecno, 0, regLast); + sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regLast, regFirst); + sqlite3VdbeAddOp3(v, OP_Multiply, regLast, regTemp, regLast); + sqlite3VdbeAddOp2(v, OP_AddImm, regLast, SQLITE_INDEX_SAMPLES*2-2); + sqlite3VdbeAddOp3(v, OP_Divide, regTemp2, regLast, regLast); + sqlite3VdbeJumpHere(v, addr); } - /* Memory cells are used as follows: + /* Zero the regSampleno and regRecno registers. */ + sqlite3VdbeAddOp2(v, OP_Integer, 0, regSampleno); + sqlite3VdbeAddOp2(v, OP_Integer, 0, regRecno); + sqlite3VdbeAddOp2(v, OP_Copy, regFirst, regSamplerecno); +#endif + + /* The block of memory cells initialized here is used as follows. + ** + ** iMem: + ** The total number of rows in the table. + ** + ** iMem+1 .. iMem+nCol: + ** Number of distinct entries in index considering the + ** left-most N columns only, where N is between 1 and nCol, + ** inclusive. ** - ** mem[iMem]: The total number of rows in the table. - ** mem[iMem+1]: Number of distinct values in column 1 - ** ... - ** mem[iMem+nCol]: Number of distinct values in column N - ** mem[iMem+nCol+1] Last observed value of column 1 - ** ... - ** mem[iMem+nCol+nCol]: Last observed value of column N + ** iMem+nCol+1 .. Mem+2*nCol: + ** Previous value of indexed columns, from left to right. ** - ** Cells iMem through iMem+nCol are initialized to 0. The others - ** are initialized to NULL. + ** Cells iMem through iMem+nCol are initialized to 0. The others are + ** initialized to contain an SQL NULL. */ for(i=0; i<=nCol; i++){ sqlite3VdbeAddOp2(v, OP_Integer, 0, iMem+i); @@ -62928,29 +64989,72 @@ sqlite3VdbeAddOp2(v, OP_Null, 0, iMem+nCol+i+1); } - /* Do the analysis. - */ + /* Start the analysis loop. This loop runs through all the entries in + ** the index b-tree. */ endOfLoop = sqlite3VdbeMakeLabel(v); sqlite3VdbeAddOp2(v, OP_Rewind, iIdxCur, endOfLoop); topOfLoop = sqlite3VdbeCurrentAddr(v); sqlite3VdbeAddOp2(v, OP_AddImm, iMem, 1); + for(i=0; imallocFailed ){ + /* If a malloc failure has occurred, then the result of the expression + ** passed as the second argument to the call to sqlite3VdbeJumpHere() + ** below may be negative. Which causes an assert() to fail (or an + ** out-of-bounds write if SQLITE_DEBUG is not defined). */ + return; + } sqlite3VdbeAddOp2(v, OP_Goto, 0, endOfLoop); for(i=0; izName, 0); - sqlite3VdbeAddOp4(v, OP_String8, 0, regFields+1, 0, pIdx->zName, 0); - regF2 = regFields+2; - sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regF2); + sqlite3VdbeAddOp2(v, OP_SCopy, iMem, regSampleno); for(i=0; inTab++; + iStatCur = pParse->nTab; + pParse->nTab += 2; openStatTable(pParse, iDb, iStatCur, 0); iMem = pParse->nMem+1; for(k=sqliteHashFirst(&pSchema->tblHash); k; k=sqliteHashNext(k)){ @@ -63034,7 +65136,8 @@ assert( sqlite3BtreeHoldsAllMutexes(pParse->db) ); iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); sqlite3BeginWriteOperation(pParse, 0, iDb); - iStatCur = pParse->nTab++; + iStatCur = pParse->nTab; + pParse->nTab += 2; openStatTable(pParse, iDb, iStatCur, pTab->zName); analyzeOneTable(pParse, pTab, iStatCur, pParse->nMem+1); loadAnalysis(pParse, iDb); @@ -63154,14 +65257,54 @@ } /* -** Load the content of the sqlite_stat1 table into the index hash tables. +** If the Index.aSample variable is not NULL, delete the aSample[] array +** and its contents. */ -SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ - analysisInfo sInfo; - HashElem *i; - char *zSql; - int rc; - +SQLITE_PRIVATE void sqlite3DeleteIndexSamples(Index *pIdx){ +#ifdef SQLITE_ENABLE_STAT2 + if( pIdx->aSample ){ + int j; + sqlite3 *dbMem = pIdx->pTable->dbMem; + for(j=0; jaSample[j]; + if( p->eType==SQLITE_TEXT || p->eType==SQLITE_BLOB ){ + sqlite3DbFree(pIdx->pTable->dbMem, p->u.z); + } + } + sqlite3DbFree(dbMem, pIdx->aSample); + pIdx->aSample = 0; + } +#else + UNUSED_PARAMETER(pIdx); +#endif +} + +/* +** Load the content of the sqlite_stat1 and sqlite_stat2 tables. The +** contents of sqlite_stat1 are used to populate the Index.aiRowEst[] +** arrays. The contents of sqlite_stat2 are used to populate the +** Index.aSample[] arrays. +** +** If the sqlite_stat1 table is not present in the database, SQLITE_ERROR +** is returned. In this case, even if SQLITE_ENABLE_STAT2 was defined +** during compilation and the sqlite_stat2 table is present, no data is +** read from it. +** +** If SQLITE_ENABLE_STAT2 was defined during compilation and the +** sqlite_stat2 table is not present in the database, SQLITE_ERROR is +** returned. However, in this case, data is read from the sqlite_stat1 +** table (if it is present) before returning. +** +** If an OOM error occurs, this function always sets db->mallocFailed. +** This means if the caller does not care about other errors, the return +** code may be ignored. +*/ +SQLITE_PRIVATE int sqlite3AnalysisLoad(sqlite3 *db, int iDb){ + analysisInfo sInfo; + HashElem *i; + char *zSql; + int rc; + assert( iDb>=0 && iDbnDb ); assert( db->aDb[iDb].pBt!=0 ); assert( sqlite3BtreeHoldsMutex(db->aDb[iDb].pBt) ); @@ -63170,19 +65313,19 @@ for(i=sqliteHashFirst(&db->aDb[iDb].pSchema->idxHash);i;i=sqliteHashNext(i)){ Index *pIdx = sqliteHashData(i); sqlite3DefaultRowEst(pIdx); + sqlite3DeleteIndexSamples(pIdx); } - /* Check to make sure the sqlite_stat1 table existss */ + /* Check to make sure the sqlite_stat1 table exists */ sInfo.db = db; sInfo.zDatabase = db->aDb[iDb].zName; if( sqlite3FindTable(db, "sqlite_stat1", sInfo.zDatabase)==0 ){ - return SQLITE_ERROR; + return SQLITE_ERROR; } - /* Load new statistics out of the sqlite_stat1 table */ - zSql = sqlite3MPrintf(db, "SELECT idx, stat FROM %Q.sqlite_stat1", - sInfo.zDatabase); + zSql = sqlite3MPrintf(db, + "SELECT idx, stat FROM %Q.sqlite_stat1", sInfo.zDatabase); if( zSql==0 ){ rc = SQLITE_NOMEM; }else{ @@ -63190,7 +65333,86 @@ rc = sqlite3_exec(db, zSql, analysisLoader, &sInfo, 0); (void)sqlite3SafetyOn(db); sqlite3DbFree(db, zSql); - if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; + } + + + /* Load the statistics from the sqlite_stat2 table. */ +#ifdef SQLITE_ENABLE_STAT2 + if( rc==SQLITE_OK && !sqlite3FindTable(db, "sqlite_stat2", sInfo.zDatabase) ){ + rc = SQLITE_ERROR; + } + if( rc==SQLITE_OK ){ + sqlite3_stmt *pStmt = 0; + + zSql = sqlite3MPrintf(db, + "SELECT idx,sampleno,sample FROM %Q.sqlite_stat2", sInfo.zDatabase); + if( !zSql ){ + rc = SQLITE_NOMEM; + }else{ + (void)sqlite3SafetyOff(db); + rc = sqlite3_prepare(db, zSql, -1, &pStmt, 0); + (void)sqlite3SafetyOn(db); + sqlite3DbFree(db, zSql); + } + + if( rc==SQLITE_OK ){ + (void)sqlite3SafetyOff(db); + while( sqlite3_step(pStmt)==SQLITE_ROW ){ + char *zIndex = (char *)sqlite3_column_text(pStmt, 0); + Index *pIdx = sqlite3FindIndex(db, zIndex, sInfo.zDatabase); + if( pIdx ){ + int iSample = sqlite3_column_int(pStmt, 1); + sqlite3 *dbMem = pIdx->pTable->dbMem; + assert( dbMem==db || dbMem==0 ); + if( iSample=0 ){ + int eType = sqlite3_column_type(pStmt, 2); + + if( pIdx->aSample==0 ){ + static const int sz = sizeof(IndexSample)*SQLITE_INDEX_SAMPLES; + pIdx->aSample = (IndexSample *)sqlite3DbMallocZero(dbMem, sz); + if( pIdx->aSample==0 ){ + db->mallocFailed = 1; + break; + } + } + + assert( pIdx->aSample ); + { + IndexSample *pSample = &pIdx->aSample[iSample]; + pSample->eType = (u8)eType; + if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ + pSample->u.r = sqlite3_column_double(pStmt, 2); + }else if( eType==SQLITE_TEXT || eType==SQLITE_BLOB ){ + const char *z = (const char *)( + (eType==SQLITE_BLOB) ? + sqlite3_column_blob(pStmt, 2): + sqlite3_column_text(pStmt, 2) + ); + int n = sqlite3_column_bytes(pStmt, 2); + if( n>24 ){ + n = 24; + } + pSample->nByte = (u8)n; + pSample->u.z = sqlite3DbMallocRaw(dbMem, n); + if( pSample->u.z ){ + memcpy(pSample->u.z, z, n); + }else{ + db->mallocFailed = 1; + break; + } + } + } + } + } + } + rc = sqlite3_finalize(pStmt); + (void)sqlite3SafetyOn(db); + } + } +#endif + + if( rc==SQLITE_NOMEM ){ + db->mallocFailed = 1; } return rc; } @@ -63212,8 +65434,6 @@ ** ************************************************************************* ** This file contains code used to implement the ATTACH and DETACH commands. -** -** $Id: attach.c,v 1.93 2009/05/31 21:21:41 drh Exp $ */ #ifndef SQLITE_OMIT_ATTACH @@ -63352,7 +65572,7 @@ aNew->safety_level = 3; #if SQLITE_HAS_CODEC - { + if( rc==SQLITE_OK ){ extern int sqlite3CodecAttach(sqlite3*, int, const void*, int); extern void sqlite3CodecGetKey(sqlite3*, int, void**, int*); int nKey; @@ -63369,13 +65589,13 @@ case SQLITE_BLOB: nKey = sqlite3_value_bytes(argv[2]); zKey = (char *)sqlite3_value_blob(argv[2]); - sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); break; case SQLITE_NULL: /* No key specified. Use the key from the main database */ sqlite3CodecGetKey(db, 0, (void**)&zKey, &nKey); - sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); + rc = sqlite3CodecAttach(db, db->nDb-1, zKey, nKey); break; } } @@ -63754,8 +65974,6 @@ ** API. This facility is an optional feature of the library. Embedded ** systems that do not need this facility may omit it by recompiling ** the library with -DSQLITE_OMIT_AUTHORIZATION=1 -** -** $Id: auth.c,v 1.31 2009/05/04 18:01:40 drh Exp $ */ /* @@ -63832,6 +66050,39 @@ } /* +** Invoke the authorization callback for permission to read column zCol from +** table zTab in database zDb. This function assumes that an authorization +** callback has been registered (i.e. that sqlite3.xAuth is not NULL). +** +** If SQLITE_IGNORE is returned and pExpr is not NULL, then pExpr is changed +** to an SQL NULL expression. Otherwise, if pExpr is NULL, then SQLITE_IGNORE +** is treated as SQLITE_DENY. In this case an error is left in pParse. +*/ +SQLITE_PRIVATE int sqlite3AuthReadCol( + Parse *pParse, /* The parser context */ + const char *zTab, /* Table name */ + const char *zCol, /* Column name */ + int iDb /* Index of containing database. */ +){ + sqlite3 *db = pParse->db; /* Database handle */ + char *zDb = db->aDb[iDb].zName; /* Name of attached database */ + int rc; /* Auth callback return code */ + + rc = db->xAuth(db->pAuthArg, SQLITE_READ, zTab,zCol,zDb,pParse->zAuthContext); + if( rc==SQLITE_DENY ){ + if( db->nDb>2 || iDb!=0 ){ + sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited",zDb,zTab,zCol); + }else{ + sqlite3ErrorMsg(pParse, "access to %s.%s is prohibited", zTab, zCol); + } + pParse->rc = SQLITE_AUTH; + }else if( rc!=SQLITE_IGNORE && rc!=SQLITE_OK ){ + sqliteAuthBadReturnCode(pParse); + } + return rc; +} + +/* ** The pExpr should be a TK_COLUMN expression. The table referred to ** is in pTabList or else it is the NEW or OLD table of a trigger. ** Check to see if it is OK to read this particular column. @@ -63847,42 +66098,38 @@ SrcList *pTabList /* All table that pExpr might refer to */ ){ sqlite3 *db = pParse->db; - int rc; Table *pTab = 0; /* The table being read */ const char *zCol; /* Name of the column of the table */ int iSrc; /* Index in pTabList->a[] of table being read */ - const char *zDBase; /* Name of database being accessed */ - TriggerStack *pStack; /* The stack of current triggers */ int iDb; /* The index of the database the expression refers to */ + int iCol; /* Index of column in table */ if( db->xAuth==0 ) return; - assert( pExpr->op==TK_COLUMN ); iDb = sqlite3SchemaToIndex(pParse->db, pSchema); if( iDb<0 ){ /* An attempt to read a column out of a subquery or other ** temporary table. */ return; } - if( pTabList ){ - for(iSrc=0; ALWAYS(iSrcnSrc); iSrc++){ - if( pExpr->iTable==pTabList->a[iSrc].iCursor ) break; - } - assert( iSrcnSrc ); - pTab = pTabList->a[iSrc].pTab; + + assert( pExpr->op==TK_COLUMN || pExpr->op==TK_TRIGGER ); + if( pExpr->op==TK_TRIGGER ){ + pTab = pParse->pTriggerTab; }else{ - pStack = pParse->trigStack; - if( ALWAYS(pStack) ){ - /* This must be an attempt to read the NEW or OLD pseudo-tables - ** of a trigger. - */ - assert( pExpr->iTable==pStack->newIdx || pExpr->iTable==pStack->oldIdx ); - pTab = pStack->pTab; + assert( pTabList ); + for(iSrc=0; ALWAYS(iSrcnSrc); iSrc++){ + if( pExpr->iTable==pTabList->a[iSrc].iCursor ){ + pTab = pTabList->a[iSrc].pTab; + break; + } } } + iCol = pExpr->iColumn; if( NEVER(pTab==0) ) return; - if( pExpr->iColumn>=0 ){ - assert( pExpr->iColumnnCol ); - zCol = pTab->aCol[pExpr->iColumn].zName; + + if( iCol>=0 ){ + assert( iColnCol ); + zCol = pTab->aCol[iCol].zName; }else if( pTab->iPKey>=0 ){ assert( pTab->iPKeynCol ); zCol = pTab->aCol[pTab->iPKey].zName; @@ -63890,21 +66137,8 @@ zCol = "ROWID"; } assert( iDb>=0 && iDbnDb ); - zDBase = db->aDb[iDb].zName; - rc = db->xAuth(db->pAuthArg, SQLITE_READ, pTab->zName, zCol, zDBase, - pParse->zAuthContext); - if( rc==SQLITE_IGNORE ){ + if( SQLITE_IGNORE==sqlite3AuthReadCol(pParse, pTab->zName, zCol, iDb) ){ pExpr->op = TK_NULL; - }else if( rc==SQLITE_DENY ){ - if( db->nDb>2 || iDb!=0 ){ - sqlite3ErrorMsg(pParse, "access to %s.%s.%s is prohibited", - zDBase, pTab->zName, zCol); - }else{ - sqlite3ErrorMsg(pParse, "access to %s.%s is prohibited",pTab->zName,zCol); - } - pParse->rc = SQLITE_AUTH; - }else if( rc!=SQLITE_OK ){ - sqliteAuthBadReturnCode(pParse); } } @@ -63999,8 +66233,6 @@ ** BEGIN TRANSACTION ** COMMIT ** ROLLBACK -** -** $Id: build.c,v 1.554 2009/06/25 11:50:21 drh Exp $ */ /* @@ -64041,31 +66273,32 @@ u8 isWriteLock, /* True for a write lock */ const char *zName /* Name of the table to be locked */ ){ + Parse *pToplevel = sqlite3ParseToplevel(pParse); int i; int nBytes; TableLock *p; - assert( iDb>=0 ); - for(i=0; inTableLock; i++){ - p = &pParse->aTableLock[i]; + + for(i=0; inTableLock; i++){ + p = &pToplevel->aTableLock[i]; if( p->iDb==iDb && p->iTab==iTab ){ p->isWriteLock = (p->isWriteLock || isWriteLock); return; } } - nBytes = sizeof(TableLock) * (pParse->nTableLock+1); - pParse->aTableLock = - sqlite3DbReallocOrFree(pParse->db, pParse->aTableLock, nBytes); - if( pParse->aTableLock ){ - p = &pParse->aTableLock[pParse->nTableLock++]; + nBytes = sizeof(TableLock) * (pToplevel->nTableLock+1); + pToplevel->aTableLock = + sqlite3DbReallocOrFree(pToplevel->db, pToplevel->aTableLock, nBytes); + if( pToplevel->aTableLock ){ + p = &pToplevel->aTableLock[pToplevel->nTableLock++]; p->iDb = iDb; p->iTab = iTab; p->isWriteLock = isWriteLock; p->zName = zName; }else{ - pParse->nTableLock = 0; - pParse->db->mallocFailed = 1; + pToplevel->nTableLock = 0; + pToplevel->db->mallocFailed = 1; } } @@ -64114,6 +66347,8 @@ ** vdbe program */ v = sqlite3GetVdbe(pParse); + assert( !pParse->isMultiWrite + || sqlite3VdbeAssertMayAbort(v, pParse->mayAbort)); if( v ){ sqlite3VdbeAddOp0(v, OP_Halt); @@ -64139,7 +66374,7 @@ { int i; for(i=0; inVtabLock; i++){ - char *vtab = (char *)pParse->apVtabLock[i]->pVtab; + char *vtab = (char *)sqlite3GetVTable(db, pParse->apVtabLock[i]); sqlite3VdbeAddOp4(v, OP_VBegin, 0, 0, 0, vtab, P4_VTAB); } pParse->nVtabLock = 0; @@ -64170,8 +66405,12 @@ sqlite3VdbeTrace(v, trace); #endif assert( pParse->iCacheLevel==0 ); /* Disables and re-enables match */ + /* A minimum of one cursor is required if autoincrement is used + * See ticket [a696379c1f08866] */ + if( pParse->pAinc!=0 && pParse->nTab==0 ) pParse->nTab = 1; sqlite3VdbeMakeReady(v, pParse->nVar, pParse->nMem, - pParse->nTab, pParse->explain); + pParse->nTab, pParse->nMaxArg, pParse->explain, + pParse->isMultiWrite && pParse->mayAbort); pParse->rc = SQLITE_DONE; pParse->colNamesSet = 0; }else if( pParse->rc==SQLITE_OK ){ @@ -64319,7 +66558,9 @@ */ static void freeIndex(Index *p){ sqlite3 *db = p->pTable->dbMem; - /* testcase( db==0 ); */ +#ifndef SQLITE_OMIT_ANALYZE + sqlite3DeleteIndexSamples(p); +#endif sqlite3DbFree(db, p->zColAff); sqlite3DbFree(db, p); } @@ -64401,6 +66642,7 @@ } assert( iDb==0 ); db->flags &= ~SQLITE_InternChanges; + sqlite3VtabUnlockList(db); sqlite3BtreeLeaveAll(db); /* If one or more of the auxiliary database files has been closed, @@ -64471,7 +66713,6 @@ */ SQLITE_PRIVATE void sqlite3DeleteTable(Table *pTable){ Index *pIndex, *pNext; - FKey *pFKey, *pNextFKey; sqlite3 *db; if( pTable==0 ) return; @@ -64493,13 +66734,8 @@ sqlite3DeleteIndex(pIndex); } -#ifndef SQLITE_OMIT_FOREIGN_KEY - /* Delete all foreign keys associated with this table. */ - for(pFKey=pTable->pFKey; pFKey; pFKey=pNextFKey){ - pNextFKey = pFKey->pNextFrom; - sqlite3DbFree(db, pFKey); - } -#endif + /* Delete any foreign keys attached to this table. */ + sqlite3FkDelete(pTable); /* Delete the Table structure itself. */ @@ -64524,7 +66760,8 @@ assert( db!=0 ); assert( iDb>=0 && iDbnDb ); - assert( zTabName && zTabName[0] ); + assert( zTabName ); + testcase( zTabName[0]==0 ); /* Zero-length table names are allowed */ pDb = &db->aDb[iDb]; p = sqlite3HashInsert(&pDb->pSchema->tblHash, zTabName, sqlite3Strlen30(zTabName),0); @@ -65141,7 +67378,11 @@ "INTEGER PRIMARY KEY"); #endif }else{ - sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0); + Index *p; + p = sqlite3CreateIndex(pParse, 0, 0, 0, pList, onError, 0, 0, sortOrder, 0); + if( p ){ + p->autoIndex = 2; + } pList = 0; } @@ -65232,7 +67473,7 @@ pColl = sqlite3FindCollSeq(db, enc, zName, initbusy); if( !initbusy && (!pColl || !pColl->xCmp) ){ - pColl = sqlite3GetCollSeq(db, pColl, zName); + pColl = sqlite3GetCollSeq(db, enc, pColl, zName); if( !pColl ){ sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName); } @@ -65852,6 +68093,7 @@ Vdbe *v = sqlite3GetVdbe(pParse); int r1 = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp3(v, OP_Destroy, iTable, r1, iDb); + sqlite3MayAbort(pParse); #ifndef SQLITE_OMIT_AUTOVACUUM /* OP_Destroy stores an in integer r1. If this integer ** is non-zero, then it is the root page number of a table moved to @@ -65979,7 +68221,7 @@ #ifndef SQLITE_OMIT_VIRTUALTABLE }else if( IsVirtual(pTab) ){ code = SQLITE_DROP_VTABLE; - zArg2 = pTab->pMod->zName; + zArg2 = sqlite3GetVTable(db, pTab)->pMod->zName; #endif }else{ if( !OMIT_TEMPDB && iDb==1 ){ @@ -66029,6 +68271,7 @@ sqlite3VdbeAddOp0(v, OP_VBegin); } #endif + sqlite3FkDropTable(pParse, pName, pTab); /* Drop all triggers associated with the table being dropped. Code ** is generated to remove entries from sqlite_master and/or @@ -66119,6 +68362,7 @@ sqlite3 *db = pParse->db; #ifndef SQLITE_OMIT_FOREIGN_KEY FKey *pFKey = 0; + FKey *pNextTo; Table *p = pParse->pNewTable; int nByte; int i; @@ -66193,9 +68437,21 @@ } } pFKey->isDeferred = 0; - pFKey->deleteConf = (u8)(flags & 0xff); - pFKey->updateConf = (u8)((flags >> 8 ) & 0xff); - pFKey->insertConf = (u8)((flags >> 16 ) & 0xff); + pFKey->aAction[0] = (u8)(flags & 0xff); /* ON DELETE action */ + pFKey->aAction[1] = (u8)((flags >> 8 ) & 0xff); /* ON UPDATE action */ + + pNextTo = (FKey *)sqlite3HashInsert(&p->pSchema->fkeyHash, + pFKey->zTo, sqlite3Strlen30(pFKey->zTo), (void *)pFKey + ); + if( pNextTo==pFKey ){ + db->mallocFailed = 1; + goto fk_end; + } + if( pNextTo ){ + assert( pNextTo->pPrevTo==0 ); + pFKey->pNextTo = pNextTo; + pNextTo->pPrevTo = pFKey; + } /* Link the foreign key to the table as the last step. */ @@ -66221,7 +68477,7 @@ Table *pTab; FKey *pFKey; if( (pTab = pParse->pNewTable)==0 || (pFKey = pTab->pFKey)==0 ) return; - assert( isDeferred==0 || isDeferred==1 ); + assert( isDeferred==0 || isDeferred==1 ); /* EV: R-30323-21917 */ pFKey->isDeferred = (u8)isDeferred; #endif } @@ -66293,8 +68549,8 @@ ** since sqlite3ReleaseTempRange() was called, it is safe to do so. */ sqlite3VdbeAddOp4(v, OP_IsUnique, iIdx, j2, regRowid, pRegKey, P4_INT32); - sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, OE_Abort, 0, - "indexed columns are not unique", P4_STATIC); + sqlite3HaltConstraint( + pParse, OE_Abort, "indexed columns are not unique", P4_STATIC); } sqlite3VdbeAddOp2(v, OP_IdxInsert, iIdx, regRecord); sqlite3VdbeChangeP5(v, OPFLAG_USESEEKRESULT); @@ -66316,8 +68572,12 @@ ** pList is a list of columns to be indexed. pList will be NULL if this ** is a primary key or unique-constraint on the most recent column added ** to the table currently under construction. +** +** If the index is created successfully, return a pointer to the new Index +** structure. This is used by sqlite3AddPrimaryKey() to mark the index +** as the tables primary key (Index.autoIndex==2). */ -SQLITE_PRIVATE void sqlite3CreateIndex( +SQLITE_PRIVATE Index *sqlite3CreateIndex( Parse *pParse, /* All information about this parse */ Token *pName1, /* First part of index name. May be NULL */ Token *pName2, /* Second part of index name. May be NULL */ @@ -66329,6 +68589,7 @@ int sortOrder, /* Sort order of primary key when pList==NULL */ int ifNotExist /* Omit error if index already exists */ ){ + Index *pRet = 0; /* Pointer to return */ Table *pTab = 0; /* Table to be indexed */ Index *pIndex = 0; /* The index to be created */ char *zName = 0; /* Name of the index */ @@ -66764,6 +69025,7 @@ pIndex->pNext = pOther->pNext; pOther->pNext = pIndex; } + pRet = pIndex; pIndex = 0; } @@ -66776,7 +69038,7 @@ sqlite3ExprListDelete(db, pList); sqlite3SrcListDelete(db, pTblName); sqlite3DbFree(db, zName); - return; + return pRet; } /* @@ -67185,12 +69447,15 @@ ){ struct SrcList_item *pItem; sqlite3 *db = pParse->db; + if( !p && (pOn || pUsing) ){ + sqlite3ErrorMsg(pParse, "a JOIN clause is required before %s", + (pOn ? "ON" : "USING") + ); + goto append_from_error; + } p = sqlite3SrcListAppend(db, p, pTable, pDatabase); if( p==0 || NEVER(p->nSrc==0) ){ - sqlite3ExprDelete(db, pOn); - sqlite3IdListDelete(db, pUsing); - sqlite3SelectDelete(db, pSubquery); - return p; + goto append_from_error; } pItem = &p->a[p->nSrc-1]; assert( pAlias!=0 ); @@ -67201,6 +69466,13 @@ pItem->pOn = pOn; pItem->pUsing = pUsing; return p; + + append_from_error: + assert( p==0 ); + sqlite3ExprDelete(db, pOn); + sqlite3IdListDelete(db, pUsing); + sqlite3SelectDelete(db, pSubquery); + return 0; } /* @@ -67356,7 +69628,6 @@ pParse->rc = rc; return 1; } - assert( (db->flags & SQLITE_InTrans)==0 || db->autoCommit ); assert( db->aDb[1].pSchema ); sqlite3PagerJournalMode(sqlite3BtreePager(db->aDb[1].pBt), db->dfltJournalMode); @@ -67387,26 +69658,26 @@ ** early in the code, before we know if any database tables will be used. */ SQLITE_PRIVATE void sqlite3CodeVerifySchema(Parse *pParse, int iDb){ - sqlite3 *db; - Vdbe *v; - int mask; + Parse *pToplevel = sqlite3ParseToplevel(pParse); - v = sqlite3GetVdbe(pParse); - if( v==0 ) return; /* This only happens if there was a prior error */ - db = pParse->db; - if( pParse->cookieGoto==0 ){ - pParse->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1; + if( pToplevel->cookieGoto==0 ){ + Vdbe *v = sqlite3GetVdbe(pToplevel); + if( v==0 ) return; /* This only happens if there was a prior error */ + pToplevel->cookieGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0)+1; } if( iDb>=0 ){ + sqlite3 *db = pToplevel->db; + int mask; + assert( iDbnDb ); assert( db->aDb[iDb].pBt!=0 || iDb==1 ); assert( iDbcookieMask & mask)==0 ){ - pParse->cookieMask |= mask; - pParse->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie; + if( (pToplevel->cookieMask & mask)==0 ){ + pToplevel->cookieMask |= mask; + pToplevel->cookieValue[iDb] = db->aDb[iDb].pSchema->schema_cookie; if( !OMIT_TEMPDB && iDb==1 ){ - sqlite3OpenTempDatabase(pParse); + sqlite3OpenTempDatabase(pToplevel); } } } @@ -67426,14 +69697,56 @@ ** necessary to undo a write and the checkpoint should not be set. */ SQLITE_PRIVATE void sqlite3BeginWriteOperation(Parse *pParse, int setStatement, int iDb){ + Parse *pToplevel = sqlite3ParseToplevel(pParse); sqlite3CodeVerifySchema(pParse, iDb); - pParse->writeMask |= 1<nested==0 ){ - /* Every place where this routine is called with setStatement!=0 has - ** already successfully created a VDBE. */ - assert( pParse->pVdbe ); - sqlite3VdbeAddOp1(pParse->pVdbe, OP_Statement, iDb); + pToplevel->writeMask |= 1<isMultiWrite |= setStatement; +} + +/* +** Indicate that the statement currently under construction might write +** more than one entry (example: deleting one row then inserting another, +** inserting multiple rows in a table, or inserting a row and index entries.) +** If an abort occurs after some of these writes have completed, then it will +** be necessary to undo the completed writes. +*/ +SQLITE_PRIVATE void sqlite3MultiWrite(Parse *pParse){ + Parse *pToplevel = sqlite3ParseToplevel(pParse); + pToplevel->isMultiWrite = 1; +} + +/* +** The code generator calls this routine if is discovers that it is +** possible to abort a statement prior to completion. In order to +** perform this abort without corrupting the database, we need to make +** sure that the statement is protected by a statement transaction. +** +** Technically, we only need to set the mayAbort flag if the +** isMultiWrite flag was previously set. There is a time dependency +** such that the abort must occur after the multiwrite. This makes +** some statements involving the REPLACE conflict resolution algorithm +** go a little faster. But taking advantage of this time dependency +** makes it more difficult to prove that the code is correct (in +** particular, it prevents us from writing an effective +** implementation of sqlite3AssertMayAbort()) and so we have chosen +** to take the safe route and skip the optimization. +*/ +SQLITE_PRIVATE void sqlite3MayAbort(Parse *pParse){ + Parse *pToplevel = sqlite3ParseToplevel(pParse); + pToplevel->mayAbort = 1; +} + +/* +** Code an OP_Halt that causes the vdbe to return an SQLITE_CONSTRAINT +** error. The onError parameter determines which (if any) of the statement +** and/or current transaction is rolled back. +*/ +SQLITE_PRIVATE void sqlite3HaltConstraint(Parse *pParse, int onError, char *p4, int p4type){ + Vdbe *v = sqlite3GetVdbe(pParse); + if( onError==OE_Abort ){ + sqlite3MayAbort(pParse); } + sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, p4, p4type); } /* @@ -67617,22 +69930,19 @@ ** ** This file contains functions used to access the internal hash tables ** of user defined functions and collation sequences. -** -** $Id: callback.c,v 1.42 2009/06/17 00:35:31 drh Exp $ */ /* ** Invoke the 'collation needed' callback to request a collation sequence -** in the database text encoding of name zName, length nName. -** If the collation sequence +** in the encoding enc of name zName, length nName. */ -static void callCollNeeded(sqlite3 *db, const char *zName){ +static void callCollNeeded(sqlite3 *db, int enc, const char *zName){ assert( !db->xCollNeeded || !db->xCollNeeded16 ); if( db->xCollNeeded ){ char *zExternal = sqlite3DbStrDup(db, zName); if( !zExternal ) return; - db->xCollNeeded(db->pCollNeededArg, db, (int)ENC(db), zExternal); + db->xCollNeeded(db->pCollNeededArg, db, enc, zExternal); sqlite3DbFree(db, zExternal); } #ifndef SQLITE_OMIT_UTF16 @@ -67675,8 +69985,7 @@ /* ** This function is responsible for invoking the collation factory callback ** or substituting a collation sequence of a different encoding when the -** requested collation sequence is not available in the database native -** encoding. +** requested collation sequence is not available in the desired encoding. ** ** If it is not NULL, then pColl must point to the database native encoding ** collation sequence with name zName, length nName. @@ -67689,6 +69998,7 @@ */ SQLITE_PRIVATE CollSeq *sqlite3GetCollSeq( sqlite3* db, /* The database connection */ + u8 enc, /* The desired encoding for the collating sequence */ CollSeq *pColl, /* Collating sequence with native encoding, or NULL */ const char *zName /* Collating sequence name */ ){ @@ -67696,14 +70006,14 @@ p = pColl; if( !p ){ - p = sqlite3FindCollSeq(db, ENC(db), zName, 0); + p = sqlite3FindCollSeq(db, enc, zName, 0); } if( !p || !p->xCmp ){ /* No collation sequence of this type for this encoding is registered. ** Call the collation factory to see if it can supply us with one. */ - callCollNeeded(db, zName); - p = sqlite3FindCollSeq(db, ENC(db), zName, 0); + callCollNeeded(db, enc, zName); + p = sqlite3FindCollSeq(db, enc, zName, 0); } if( p && !p->xCmp && synthCollSeq(db, p) ){ p = 0; @@ -67726,7 +70036,8 @@ SQLITE_PRIVATE int sqlite3CheckCollSeq(Parse *pParse, CollSeq *pColl){ if( pColl ){ const char *zName = pColl->zName; - CollSeq *p = sqlite3GetCollSeq(pParse->db, pColl, zName); + sqlite3 *db = pParse->db; + CollSeq *p = sqlite3GetCollSeq(db, ENC(db), pColl, zName); if( !p ){ sqlite3ErrorMsg(pParse, "no such collation sequence: %s", zName); pParse->nErr++; @@ -68027,6 +70338,7 @@ sqlite3DeleteTable(pTab); } sqlite3HashClear(&temp1); + sqlite3HashClear(&pSchema->fkeyHash); pSchema->pSeqTab = 0; pSchema->flags &= ~DB_SchemaLoaded; } @@ -68048,6 +70360,7 @@ sqlite3HashInit(&p->tblHash); sqlite3HashInit(&p->idxHash); sqlite3HashInit(&p->trigHash); + sqlite3HashInit(&p->fkeyHash); p->enc = SQLITE_UTF8; } return p; @@ -68068,8 +70381,6 @@ ************************************************************************* ** This file contains C code routines that are called by the parser ** in order to generate code for DELETE FROM statements. -** -** $Id: delete.c,v 1.204 2009/06/23 20:28:54 drh Exp $ */ /* @@ -68099,16 +70410,26 @@ ** writable return 0; */ SQLITE_PRIVATE int sqlite3IsReadOnly(Parse *pParse, Table *pTab, int viewOk){ - if( ((pTab->tabFlags & TF_Readonly)!=0 - && (pParse->db->flags & SQLITE_WriteSchema)==0 - && pParse->nested==0) -#ifndef SQLITE_OMIT_VIRTUALTABLE - || (pTab->pMod && pTab->pMod->pModule->xUpdate==0) -#endif + /* A table is not writable under the following circumstances: + ** + ** 1) It is a virtual table and no implementation of the xUpdate method + ** has been provided, or + ** 2) It is a system table (i.e. sqlite_master), this call is not + ** part of a nested parse and writable_schema pragma has not + ** been specified. + ** + ** In either case leave an error message in pParse and return non-zero. + */ + if( ( IsVirtual(pTab) + && sqlite3GetVTable(pParse->db, pTab)->pMod->pModule->xUpdate==0 ) + || ( (pTab->tabFlags & TF_Readonly)!=0 + && (pParse->db->flags & SQLITE_WriteSchema)==0 + && pParse->nested==0 ) ){ sqlite3ErrorMsg(pParse, "table %s may not be modified", pTab->zName); return 1; } + #ifndef SQLITE_OMIT_VIEW if( !viewOk && pTab->pSelect ){ sqlite3ErrorMsg(pParse,"cannot modify %s because it is a view",pTab->zName); @@ -68274,7 +70595,6 @@ int iCur; /* VDBE Cursor number for pTab */ sqlite3 *db; /* Main database structure */ AuthContext sContext; /* Authorization context */ - int oldIdx = -1; /* Cursor for the OLD table of AFTER triggers */ NameContext sNC; /* Name context to resolve expressions in */ int iDb; /* Database number */ int memCnt = -1; /* Memory cell used for change counting */ @@ -68284,13 +70604,8 @@ int isView; /* True if attempting to delete from a view */ Trigger *pTrigger; /* List of table triggers, if required */ #endif - int iBeginAfterTrigger = 0; /* Address of after trigger program */ - int iEndAfterTrigger = 0; /* Exit of after trigger program */ - int iBeginBeforeTrigger = 0; /* Address of before trigger program */ - int iEndBeforeTrigger = 0; /* Exit of before trigger program */ - u32 old_col_mask = 0; /* Mask of OLD.* columns in use */ - sContext.pParse = 0; + memset(&sContext, 0, sizeof(sContext)); db = pParse->db; if( pParse->nErr || db->mallocFailed ){ goto delete_from_cleanup; @@ -68320,6 +70635,12 @@ # define isView 0 #endif + /* If pTab is really a view, make sure it has been initialized. + */ + if( sqlite3ViewGetColumnNames(pParse, pTab) ){ + goto delete_from_cleanup; + } + if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ goto delete_from_cleanup; } @@ -68333,18 +70654,6 @@ } assert(!isView || pTrigger); - /* If pTab is really a view, make sure it has been initialized. - */ - if( sqlite3ViewGetColumnNames(pParse, pTab) ){ - goto delete_from_cleanup; - } - - /* Allocate a cursor used to store the old.* data for a trigger. - */ - if( pTrigger ){ - oldIdx = pParse->nTab++; - } - /* Assign cursor number to the table and all its indices. */ assert( pTabList->nSrc==1 ); @@ -68366,25 +70675,7 @@ goto delete_from_cleanup; } if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); - sqlite3BeginWriteOperation(pParse, (pTrigger?1:0), iDb); - - if( pTrigger ){ - int orconf = ((pParse->trigStack)?pParse->trigStack->orconf:OE_Default); - int iGoto = sqlite3VdbeAddOp0(v, OP_Goto); - addr = sqlite3VdbeMakeLabel(v); - - iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v); - (void)sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0, - TRIGGER_BEFORE, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0); - iEndBeforeTrigger = sqlite3VdbeAddOp0(v, OP_Goto); - - iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v); - (void)sqlite3CodeRowTrigger(pParse, pTrigger, TK_DELETE, 0, - TRIGGER_AFTER, pTab, -1, oldIdx, orconf, addr, &old_col_mask, 0); - iEndAfterTrigger = sqlite3VdbeAddOp0(v, OP_Goto); - - sqlite3VdbeJumpHere(v, iGoto); - } + sqlite3BeginWriteOperation(pParse, 1, iDb); /* If we are trying to delete from a view, realize that view into ** a ephemeral table. @@ -68414,10 +70705,12 @@ #ifndef SQLITE_OMIT_TRUNCATE_OPTIMIZATION /* Special case: A DELETE without a WHERE clause deletes everything. - ** It is easier just to erase the whole table. Note, however, that - ** this means that the row change count will be incorrect. - */ - if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) ){ + ** It is easier just to erase the whole table. Prior to version 3.6.5, + ** this optimization caused the row change count (the value returned by + ** API function sqlite3_count_changes) to be set incorrectly. */ + if( rcauth==SQLITE_OK && pWhere==0 && !pTrigger && !IsVirtual(pTab) + && 0==sqlite3FkRequired(pParse, pTab, 0, 0) + ){ assert( !isView ); sqlite3VdbeAddOp4(v, OP_Clear, pTab->tnum, iDb, memCnt, pTab->zName, P4_STATIC); @@ -68431,8 +70724,8 @@ ** the table and pick which records to delete. */ { - int iRowid = ++pParse->nMem; /* Used for storing rowid values. */ int iRowSet = ++pParse->nMem; /* Register for rowset of rows to delete */ + int iRowid = ++pParse->nMem; /* Used for storing rowid values. */ int regRowid; /* Actual register containing rowids */ /* Collect rowids of every row to be deleted. @@ -68440,90 +70733,48 @@ sqlite3VdbeAddOp2(v, OP_Null, 0, iRowSet); pWInfo = sqlite3WhereBegin(pParse, pTabList, pWhere,0,WHERE_DUPLICATES_OK); if( pWInfo==0 ) goto delete_from_cleanup; - regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid, 0); + regRowid = sqlite3ExprCodeGetColumn(pParse, pTab, -1, iCur, iRowid); sqlite3VdbeAddOp2(v, OP_RowSetAdd, iRowSet, regRowid); if( db->flags & SQLITE_CountRows ){ sqlite3VdbeAddOp2(v, OP_AddImm, memCnt, 1); } sqlite3WhereEnd(pWInfo); - /* Open the pseudo-table used to store OLD if there are triggers. - */ - if( pTrigger ){ - sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol); - } - /* Delete every item whose key was written to the list during the ** database scan. We have to delete items after the scan is complete - ** because deleting an item can change the scan order. - */ + ** because deleting an item can change the scan order. */ end = sqlite3VdbeMakeLabel(v); + /* Unless this is a view, open cursors for the table we are + ** deleting from and all its indices. If this is a view, then the + ** only effect this statement has is to fire the INSTEAD OF + ** triggers. */ if( !isView ){ - /* Open cursors for the table we are deleting from and - ** all its indices. - */ sqlite3OpenTableAndIndices(pParse, pTab, iCur, OP_OpenWrite); } - /* This is the beginning of the delete loop. If a trigger encounters - ** an IGNORE constraint, it jumps back to here. - */ - if( pTrigger ){ - sqlite3VdbeResolveLabel(v, addr); - } addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, iRowSet, end, iRowid); - if( pTrigger ){ - int iData = ++pParse->nMem; /* For storing row data of OLD table */ - - /* If the record is no longer present in the table, jump to the - ** next iteration of the loop through the contents of the fifo. - */ - sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, iRowid); - - /* Populate the OLD.* pseudo-table */ - if( old_col_mask ){ - sqlite3VdbeAddOp2(v, OP_RowData, iCur, iData); - }else{ - sqlite3VdbeAddOp2(v, OP_Null, 0, iData); - } - sqlite3VdbeAddOp3(v, OP_Insert, oldIdx, iData, iRowid); - - /* Jump back and run the BEFORE triggers */ - sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginBeforeTrigger); - sqlite3VdbeJumpHere(v, iEndBeforeTrigger); - } - - if( !isView ){ - /* Delete the row */ + /* Delete the row */ #ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pTab) ){ - const char *pVtab = (const char *)pTab->pVtab; - sqlite3VtabMakeWritable(pParse, pTab); - sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVtab, P4_VTAB); - }else + if( IsVirtual(pTab) ){ + const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); + sqlite3VtabMakeWritable(pParse, pTab); + sqlite3VdbeAddOp4(v, OP_VUpdate, 0, 1, iRowid, pVTab, P4_VTAB); + sqlite3MayAbort(pParse); + }else #endif - { - sqlite3GenerateRowDelete(pParse, pTab, iCur, iRowid, pParse->nested==0); - } - } - - /* If there are row triggers, close all cursors then invoke - ** the AFTER triggers - */ - if( pTrigger ){ - /* Jump back and run the AFTER triggers */ - sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger); - sqlite3VdbeJumpHere(v, iEndAfterTrigger); + { + int count = (pParse->nested==0); /* True to count changes */ + sqlite3GenerateRowDelete(pParse, pTab, iCur, iRowid, count, pTrigger, OE_Default); } /* End of the delete loop */ sqlite3VdbeAddOp2(v, OP_Goto, 0, addr); sqlite3VdbeResolveLabel(v, end); - /* Close the cursors after the loop if there are no row triggers */ - if( !isView && !IsVirtual(pTab) ){ + /* Close the cursors open on the table and its indexes. */ + if( !isView && !IsVirtual(pTab) ){ for(i=1, pIdx=pTab->pIndex; pIdx; i++, pIdx=pIdx->pNext){ sqlite3VdbeAddOp2(v, OP_Close, iCur + i, pIdx->tnum); } @@ -68535,16 +70786,15 @@ ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ - if( pParse->nested==0 && pParse->trigStack==0 ){ + if( pParse->nested==0 && pParse->pTriggerTab==0 ){ sqlite3AutoincrementEnd(pParse); } - /* - ** Return the number of rows that were deleted. If this routine is + /* Return the number of rows that were deleted. If this routine is ** generating code because of a call to sqlite3NestedParse(), do not ** invoke the callback function. */ - if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){ + if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){ sqlite3VdbeAddOp2(v, OP_ResultRow, memCnt, 1); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows deleted", SQLITE_STATIC); @@ -68556,6 +70806,15 @@ sqlite3ExprDelete(db, pWhere); return; } +/* Make sure "isView" and other macros defined above are undefined. Otherwise +** thely may interfere with compilation of other functions in this file +** (or in another file, if this file becomes part of the amalgamation). */ +#ifdef isView + #undef isView +#endif +#ifdef pTrigger + #undef pTrigger +#endif /* ** This routine generates VDBE code that causes a single row of a @@ -68565,7 +70824,7 @@ ** These are the requirements: ** ** 1. A read/write cursor pointing to pTab, the table containing the row -** to be deleted, must be opened as cursor number "base". +** to be deleted, must be opened as cursor number $iCur. ** ** 2. Read/write cursors for all indices of pTab must be open as ** cursor number base+i for the i-th index. @@ -68573,28 +70832,99 @@ ** 3. The record number of the row to be deleted must be stored in ** memory cell iRowid. ** -** This routine pops the top of the stack to remove the record number -** and then generates code to remove both the table record and all index -** entries that point to that record. +** This routine generates code to remove both the table record and all +** index entries that point to that record. */ SQLITE_PRIVATE void sqlite3GenerateRowDelete( Parse *pParse, /* Parsing context */ Table *pTab, /* Table containing the row to be deleted */ int iCur, /* Cursor number for the table */ int iRowid, /* Memory cell that contains the rowid to delete */ - int count /* Increment the row change counter */ -){ - int addr; - Vdbe *v; + int count, /* If non-zero, increment the row change counter */ + Trigger *pTrigger, /* List of triggers to (potentially) fire */ + int onconf /* Default ON CONFLICT policy for triggers */ +){ + Vdbe *v = pParse->pVdbe; /* Vdbe */ + int iOld = 0; /* First register in OLD.* array */ + int iLabel; /* Label resolved to end of generated code */ - v = pParse->pVdbe; - addr = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, iRowid); - sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0); - sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0)); - if( count ){ - sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC); + /* Vdbe is guaranteed to have been allocated by this stage. */ + assert( v ); + + /* Seek cursor iCur to the row to delete. If this row no longer exists + ** (this can happen if a trigger program has already deleted it), do + ** not attempt to delete it or fire any DELETE triggers. */ + iLabel = sqlite3VdbeMakeLabel(v); + sqlite3VdbeAddOp3(v, OP_NotExists, iCur, iLabel, iRowid); + + /* If there are any triggers to fire, allocate a range of registers to + ** use for the old.* references in the triggers. */ + if( sqlite3FkRequired(pParse, pTab, 0, 0) || pTrigger ){ + u32 mask; /* Mask of OLD.* columns in use */ + int iCol; /* Iterator used while populating OLD.* */ + + /* TODO: Could use temporary registers here. Also could attempt to + ** avoid copying the contents of the rowid register. */ + mask = sqlite3TriggerColmask( + pParse, pTrigger, 0, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onconf + ); + mask |= sqlite3FkOldmask(pParse, pTab); + iOld = pParse->nMem+1; + pParse->nMem += (1 + pTab->nCol); + + /* Populate the OLD.* pseudo-table register array. These values will be + ** used by any BEFORE and AFTER triggers that exist. */ + sqlite3VdbeAddOp2(v, OP_Copy, iRowid, iOld); + for(iCol=0; iColnCol; iCol++){ + if( mask==0xffffffff || mask&(1<pSelect==0 ){ + sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, 0); + sqlite3VdbeAddOp2(v, OP_Delete, iCur, (count?OPFLAG_NCHANGE:0)); + if( count ){ + sqlite3VdbeChangeP4(v, -1, pTab->zName, P4_STATIC); + } } - sqlite3VdbeJumpHere(v, addr); + + /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to + ** handle rows (possibly in other tables) that refer via a foreign key + ** to the row just deleted. */ + sqlite3FkActions(pParse, pTab, 0, iOld); + + /* Invoke AFTER DELETE trigger programs. */ + sqlite3CodeRowTrigger(pParse, pTrigger, + TK_DELETE, 0, TRIGGER_AFTER, pTab, iOld, onconf, iLabel + ); + + /* Jump here if the row had already been deleted before any BEFORE + ** trigger programs were invoked. Or if a trigger program throws a + ** RAISE(IGNORE) exception. */ + sqlite3VdbeResolveLabel(v, iLabel); } /* @@ -68663,23 +70993,17 @@ sqlite3VdbeAddOp2(v, OP_SCopy, regBase+nCol, regBase+j); }else{ sqlite3VdbeAddOp3(v, OP_Column, iCur, idx, regBase+j); - sqlite3ColumnDefault(v, pTab, idx); + sqlite3ColumnDefault(v, pTab, idx, -1); } } if( doMakeRec ){ sqlite3VdbeAddOp3(v, OP_MakeRecord, regBase, nCol+1, regOut); - sqlite3IndexAffinityStr(v, pIdx); - sqlite3ExprCacheAffinityChange(pParse, regBase, nCol+1); + sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); } sqlite3ReleaseTempRange(pParse, regBase, nCol+1); return regBase; } -/* Make sure "isView" gets undefined in case this file becomes part of -** the amalgamation - so that subsequent files do not see isView as a -** macro. */ -#undef isView - /************** End of delete.c **********************************************/ /************** Begin file func.c ********************************************/ /* @@ -68699,8 +71023,6 @@ ** There is only one exported symbol in this file - the function ** sqliteRegisterBuildinFunctions() found at the bottom of the file. ** All other code has file scope. -** -** $Id: func.c,v 1.239 2009/06/19 16:44:41 drh Exp $ */ /* @@ -68799,7 +71121,10 @@ } /* -** Implementation of the abs() function +** Implementation of the abs() function. +** +** IMP: R-23979-26855 The abs(X) function returns the absolute value of +** the numeric argument X. */ static void absFunc(sqlite3_context *context, int argc, sqlite3_value **argv){ assert( argc==1 ); @@ -68809,6 +71134,9 @@ i64 iVal = sqlite3_value_int64(argv[0]); if( iVal<0 ){ if( (iVal<<1)==0 ){ + /* IMP: R-35460-15084 If X is the integer -9223372036854775807 then + ** abs(X) throws an integer overflow error since there is no + ** equivalent positive 64-bit two complement value. */ sqlite3_result_error(context, "integer overflow", -1); return; } @@ -68818,10 +71146,16 @@ break; } case SQLITE_NULL: { + /* IMP: R-37434-19929 Abs(X) returns NULL if X is NULL. */ sqlite3_result_null(context); break; } default: { + /* Because sqlite3_value_double() returns 0.0 if the argument is not + ** something that can be converted into a number, we have: + ** IMP: R-57326-31541 Abs(X) return 0.0 if X is a string or blob that + ** cannot be converted to a numeric value. + */ double rVal = sqlite3_value_double(argv[0]); if( rVal<0 ) rVal = -rVal; sqlite3_result_double(context, rVal); @@ -68839,6 +71173,8 @@ ** If x is a blob, then we count bytes. ** ** If p1 is negative, then we begin abs(p1) from the end of x[]. +** +** If p2 is negative, return the p2 characters preceeding p1. */ static void substrFunc( sqlite3_context *context, @@ -68859,6 +71195,7 @@ return; } p0type = sqlite3_value_type(argv[0]); + p1 = sqlite3_value_int(argv[1]); if( p0type==SQLITE_BLOB ){ len = sqlite3_value_bytes(argv[0]); z = sqlite3_value_blob(argv[0]); @@ -68868,11 +71205,12 @@ z = sqlite3_value_text(argv[0]); if( z==0 ) return; len = 0; - for(z2=z; *z2; len++){ - SQLITE_SKIP_UTF8(z2); + if( p1<0 ){ + for(z2=z; *z2; len++){ + SQLITE_SKIP_UTF8(z2); + } } } - p1 = sqlite3_value_int(argv[1]); if( argc==3 ){ p2 = sqlite3_value_int(argv[2]); if( p2<0 ){ @@ -68902,10 +71240,6 @@ } } assert( p1>=0 && p2>=0 ); - if( p1+p2>len ){ - p2 = len-p1; - if( p2<0 ) p2 = 0; - } if( p0type!=SQLITE_BLOB ){ while( *z && p1 ){ SQLITE_SKIP_UTF8(z); @@ -68916,6 +71250,10 @@ } sqlite3_result_text(context, (char*)z, (int)(z2-z), SQLITE_TRANSIENT); }else{ + if( p1+p2>len ){ + p2 = len-p1; + if( p2<0 ) p2 = 0; + } sqlite3_result_blob(context, (char*)&z[p1], (int)p2, SQLITE_TRANSIENT); } } @@ -69017,6 +71355,14 @@ } } + +#if 0 /* This function is never used. */ +/* +** The COALESCE() and IFNULL() functions used to be implemented as shown +** here. But now they are implemented as VDBE code so that unused arguments +** do not have to be computed. This legacy implementation is retained as +** comment. +*/ /* ** Implementation of the IFNULL(), NVL(), and COALESCE() functions. ** All three do the same thing. They return the first non-NULL @@ -69035,6 +71381,8 @@ } } } +#endif /* NOT USED */ +#define ifnullFunc versionFunc /* Substitute function - never called */ /* ** Implementation of random(). Return a random integer. @@ -69382,7 +71730,7 @@ } /* -** Implementation of the VERSION(*) function. The result is the version +** Implementation of the sqlite_version() function. The result is the version ** of the SQLite library that is running. */ static void versionFunc( @@ -69394,6 +71742,20 @@ sqlite3_result_text(context, sqlite3_version, -1, SQLITE_STATIC); } +/* +** Implementation of the sqlite_source_id() function. The result is a string +** that identifies the particular version of the source code used to build +** SQLite. +*/ +static void sourceidFunc( + sqlite3_context *context, + int NotUsed, + sqlite3_value **NotUsed2 +){ + UNUSED_PARAMETER2(NotUsed, NotUsed2); + sqlite3_result_text(context, SQLITE_SOURCE_ID, -1, SQLITE_STATIC); +} + /* Array for converting from half-bytes (nybbles) into ASCII hex ** digits. */ static const char hexdigits[] = { @@ -69695,9 +72057,16 @@ } +/* IMP: R-25361-16150 This function is omitted from SQLite by default. It +** is only available if the SQLITE_SOUNDEX compile-time option is used +** when SQLite is built. +*/ #ifdef SQLITE_SOUNDEX /* ** Compute the soundex encoding of a word. +** +** IMP: R-59782-00072 The soundex(X) function returns a string that is the +** soundex encoding of the string X. */ static void soundexFunc( sqlite3_context *context, @@ -69741,10 +72110,12 @@ zResult[j] = 0; sqlite3_result_text(context, zResult, 4, SQLITE_TRANSIENT); }else{ + /* IMP: R-64894-50321 The string "?000" is returned if the argument + ** is NULL or contains no ASCII alphabetic characters. */ sqlite3_result_text(context, "?000", 4, SQLITE_STATIC); } } -#endif +#endif /* SQLITE_SOUNDEX */ #ifndef SQLITE_OMIT_LOAD_EXTENSION /* @@ -70105,14 +72476,17 @@ FUNCTION(upper, 1, 0, 0, upperFunc ), FUNCTION(lower, 1, 0, 0, lowerFunc ), FUNCTION(coalesce, 1, 0, 0, 0 ), - FUNCTION(coalesce, -1, 0, 0, ifnullFunc ), FUNCTION(coalesce, 0, 0, 0, 0 ), +/* FUNCTION(coalesce, -1, 0, 0, ifnullFunc ), */ + {-1,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"coalesce",0}, FUNCTION(hex, 1, 0, 0, hexFunc ), - FUNCTION(ifnull, 2, 0, 1, ifnullFunc ), +/* FUNCTION(ifnull, 2, 0, 0, ifnullFunc ), */ + {2,SQLITE_UTF8,SQLITE_FUNC_COALESCE,0,0,ifnullFunc,0,0,"ifnull",0}, FUNCTION(random, 0, 0, 0, randomFunc ), FUNCTION(randomblob, 1, 0, 0, randomBlob ), FUNCTION(nullif, 2, 0, 1, nullifFunc ), FUNCTION(sqlite_version, 0, 0, 0, versionFunc ), + FUNCTION(sqlite_source_id, 0, 0, 0, sourceidFunc ), FUNCTION(quote, 1, 0, 0, quoteFunc ), FUNCTION(last_insert_rowid, 0, 0, 0, last_insert_rowid), FUNCTION(changes, 0, 0, 0, changes ), @@ -70156,9 +72530,8 @@ } /************** End of func.c ************************************************/ -/************** Begin file insert.c ******************************************/ +/************** Begin file fkey.c ********************************************/ /* -** 2001 September 15 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -70168,240 +72541,1447 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** This file contains C code routines that are called by the parser -** to handle INSERT statements in SQLite. -** -** $Id: insert.c,v 1.269 2009/06/23 20:28:54 drh Exp $ +** This file contains code used by the compiler to add foreign key +** support to compiled SQL statements. */ +#ifndef SQLITE_OMIT_FOREIGN_KEY +#ifndef SQLITE_OMIT_TRIGGER + /* -** Generate code that will open a table for reading. +** Deferred and Immediate FKs +** -------------------------- +** +** Foreign keys in SQLite come in two flavours: deferred and immediate. +** If an immediate foreign key constraint is violated, SQLITE_CONSTRAINT +** is returned and the current statement transaction rolled back. If a +** deferred foreign key constraint is violated, no action is taken +** immediately. However if the application attempts to commit the +** transaction before fixing the constraint violation, the attempt fails. +** +** Deferred constraints are implemented using a simple counter associated +** with the database handle. The counter is set to zero each time a +** database transaction is opened. Each time a statement is executed +** that causes a foreign key violation, the counter is incremented. Each +** time a statement is executed that removes an existing violation from +** the database, the counter is decremented. When the transaction is +** committed, the commit fails if the current value of the counter is +** greater than zero. This scheme has two big drawbacks: +** +** * When a commit fails due to a deferred foreign key constraint, +** there is no way to tell which foreign constraint is not satisfied, +** or which row it is not satisfied for. +** +** * If the database contains foreign key violations when the +** transaction is opened, this may cause the mechanism to malfunction. +** +** Despite these problems, this approach is adopted as it seems simpler +** than the alternatives. +** +** INSERT operations: +** +** I.1) For each FK for which the table is the child table, search +** the parent table for a match. If none is found increment the +** constraint counter. +** +** I.2) For each FK for which the table is the parent table, +** search the child table for rows that correspond to the new +** row in the parent table. Decrement the counter for each row +** found (as the constraint is now satisfied). +** +** DELETE operations: +** +** D.1) For each FK for which the table is the child table, +** search the parent table for a row that corresponds to the +** deleted row in the child table. If such a row is not found, +** decrement the counter. +** +** D.2) For each FK for which the table is the parent table, search +** the child table for rows that correspond to the deleted row +** in the parent table. For each found increment the counter. +** +** UPDATE operations: +** +** An UPDATE command requires that all 4 steps above are taken, but only +** for FK constraints for which the affected columns are actually +** modified (values must be compared at runtime). +** +** Note that I.1 and D.1 are very similar operations, as are I.2 and D.2. +** This simplifies the implementation a bit. +** +** For the purposes of immediate FK constraints, the OR REPLACE conflict +** resolution is considered to delete rows before the new row is inserted. +** If a delete caused by OR REPLACE violates an FK constraint, an exception +** is thrown, even if the FK constraint would be satisfied after the new +** row is inserted. +** +** Immediate constraints are usually handled similarly. The only difference +** is that the counter used is stored as part of each individual statement +** object (struct Vdbe). If, after the statement has run, its immediate +** constraint counter is greater than zero, it returns SQLITE_CONSTRAINT +** and the statement transaction is rolled back. An exception is an INSERT +** statement that inserts a single row only (no triggers). In this case, +** instead of using a counter, an exception is thrown immediately if the +** INSERT violates a foreign key constraint. This is necessary as such +** an INSERT does not open a statement transaction. +** +** TODO: How should dropping a table be handled? How should renaming a +** table be handled? +** +** +** Query API Notes +** --------------- +** +** Before coding an UPDATE or DELETE row operation, the code-generator +** for those two operations needs to know whether or not the operation +** requires any FK processing and, if so, which columns of the original +** row are required by the FK processing VDBE code (i.e. if FKs were +** implemented using triggers, which of the old.* columns would be +** accessed). No information is required by the code-generator before +** coding an INSERT operation. The functions used by the UPDATE/DELETE +** generation code to query for this information are: +** +** sqlite3FkRequired() - Test to see if FK processing is required. +** sqlite3FkOldmask() - Query for the set of required old.* columns. +** +** +** Externally accessible module functions +** -------------------------------------- +** +** sqlite3FkCheck() - Check for foreign key violations. +** sqlite3FkActions() - Code triggers for ON UPDATE/ON DELETE actions. +** sqlite3FkDelete() - Delete an FKey structure. */ -SQLITE_PRIVATE void sqlite3OpenTable( - Parse *p, /* Generate code into this VDBE */ - int iCur, /* The cursor number of the table */ - int iDb, /* The database index in sqlite3.aDb[] */ - Table *pTab, /* The table to be opened */ - int opcode /* OP_OpenRead or OP_OpenWrite */ -){ - Vdbe *v; - if( IsVirtual(pTab) ) return; - v = sqlite3GetVdbe(p); - assert( opcode==OP_OpenWrite || opcode==OP_OpenRead ); - sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite)?1:0, pTab->zName); - sqlite3VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb); - sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(pTab->nCol), P4_INT32); - VdbeComment((v, "%s", pTab->zName)); -} /* -** Set P4 of the most recently inserted opcode to a column affinity -** string for index pIdx. A column affinity string has one character -** for each column in the table, according to the affinity of the column: +** VDBE Calling Convention +** ----------------------- ** -** Character Column affinity -** ------------------------------ -** 'a' TEXT -** 'b' NONE -** 'c' NUMERIC -** 'd' INTEGER -** 'e' REAL +** Example: ** -** An extra 'b' is appended to the end of the string to cover the -** rowid that appears as the last column in every index. +** For the following INSERT statement: +** +** CREATE TABLE t1(a, b INTEGER PRIMARY KEY, c); +** INSERT INTO t1 VALUES(1, 2, 3.1); +** +** Register (x): 2 (type integer) +** Register (x+1): 1 (type integer) +** Register (x+2): NULL (type NULL) +** Register (x+3): 3.1 (type real) */ -SQLITE_PRIVATE void sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){ - if( !pIdx->zColAff ){ - /* The first time a column affinity string for a particular index is - ** required, it is allocated and populated here. It is then stored as - ** a member of the Index structure for subsequent use. - ** - ** The column affinity string will eventually be deleted by - ** sqliteDeleteIndex() when the Index structure itself is cleaned - ** up. - */ - int n; - Table *pTab = pIdx->pTable; - sqlite3 *db = sqlite3VdbeDb(v); - pIdx->zColAff = (char *)sqlite3Malloc(pIdx->nColumn+2); - if( !pIdx->zColAff ){ - db->mallocFailed = 1; - return; - } - for(n=0; nnColumn; n++){ - pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity; - } - pIdx->zColAff[n++] = SQLITE_AFF_NONE; - pIdx->zColAff[n] = 0; - } - - sqlite3VdbeChangeP4(v, -1, pIdx->zColAff, 0); -} /* -** Set P4 of the most recently inserted opcode to a column affinity -** string for table pTab. A column affinity string has one character -** for each column indexed by the index, according to the affinity of the -** column: +** A foreign key constraint requires that the key columns in the parent +** table are collectively subject to a UNIQUE or PRIMARY KEY constraint. +** Given that pParent is the parent table for foreign key constraint pFKey, +** search the schema a unique index on the parent key columns. ** -** Character Column affinity -** ------------------------------ -** 'a' TEXT -** 'b' NONE -** 'c' NUMERIC -** 'd' INTEGER -** 'e' REAL -*/ -SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){ - /* The first time a column affinity string for a particular table - ** is required, it is allocated and populated here. It is then - ** stored as a member of the Table structure for subsequent use. - ** - ** The column affinity string will eventually be deleted by - ** sqlite3DeleteTable() when the Table structure itself is cleaned up. +** If successful, zero is returned. If the parent key is an INTEGER PRIMARY +** KEY column, then output variable *ppIdx is set to NULL. Otherwise, *ppIdx +** is set to point to the unique index. +** +** If the parent key consists of a single column (the foreign key constraint +** is not a composite foreign key), output variable *paiCol is set to NULL. +** Otherwise, it is set to point to an allocated array of size N, where +** N is the number of columns in the parent key. The first element of the +** array is the index of the child table column that is mapped by the FK +** constraint to the parent table column stored in the left-most column +** of index *ppIdx. The second element of the array is the index of the +** child table column that corresponds to the second left-most column of +** *ppIdx, and so on. +** +** If the required index cannot be found, either because: +** +** 1) The named parent key columns do not exist, or +** +** 2) The named parent key columns do exist, but are not subject to a +** UNIQUE or PRIMARY KEY constraint, or +** +** 3) No parent key columns were provided explicitly as part of the +** foreign key definition, and the parent table does not have a +** PRIMARY KEY, or +** +** 4) No parent key columns were provided explicitly as part of the +** foreign key definition, and the PRIMARY KEY of the parent table +** consists of a a different number of columns to the child key in +** the child table. +** +** then non-zero is returned, and a "foreign key mismatch" error loaded +** into pParse. If an OOM error occurs, non-zero is returned and the +** pParse->db->mallocFailed flag is set. +*/ +static int locateFkeyIndex( + Parse *pParse, /* Parse context to store any error in */ + Table *pParent, /* Parent table of FK constraint pFKey */ + FKey *pFKey, /* Foreign key to find index for */ + Index **ppIdx, /* OUT: Unique index on parent table */ + int **paiCol /* OUT: Map of index columns in pFKey */ +){ + Index *pIdx = 0; /* Value to return via *ppIdx */ + int *aiCol = 0; /* Value to return via *paiCol */ + int nCol = pFKey->nCol; /* Number of columns in parent key */ + char *zKey = pFKey->aCol[0].zCol; /* Name of left-most parent key column */ + + /* The caller is responsible for zeroing output parameters. */ + assert( ppIdx && *ppIdx==0 ); + assert( !paiCol || *paiCol==0 ); + assert( pParse ); + + /* If this is a non-composite (single column) foreign key, check if it + ** maps to the INTEGER PRIMARY KEY of table pParent. If so, leave *ppIdx + ** and *paiCol set to zero and return early. + ** + ** Otherwise, for a composite foreign key (more than one column), allocate + ** space for the aiCol array (returned via output parameter *paiCol). + ** Non-composite foreign keys do not require the aiCol array. */ - if( !pTab->zColAff ){ - char *zColAff; - int i; - sqlite3 *db = sqlite3VdbeDb(v); + if( nCol==1 ){ + /* The FK maps to the IPK if any of the following are true: + ** + ** 1) There is an INTEGER PRIMARY KEY column and the FK is implicitly + ** mapped to the primary key of table pParent, or + ** 2) The FK is explicitly mapped to a column declared as INTEGER + ** PRIMARY KEY. + */ + if( pParent->iPKey>=0 ){ + if( !zKey ) return 0; + if( !sqlite3StrICmp(pParent->aCol[pParent->iPKey].zName, zKey) ) return 0; + } + }else if( paiCol ){ + assert( nCol>1 ); + aiCol = (int *)sqlite3DbMallocRaw(pParse->db, nCol*sizeof(int)); + if( !aiCol ) return 1; + *paiCol = aiCol; + } + + for(pIdx=pParent->pIndex; pIdx; pIdx=pIdx->pNext){ + if( pIdx->nColumn==nCol && pIdx->onError!=OE_None ){ + /* pIdx is a UNIQUE index (or a PRIMARY KEY) and has the right number + ** of columns. If each indexed column corresponds to a foreign key + ** column of pFKey, then this index is a winner. */ + + if( zKey==0 ){ + /* If zKey is NULL, then this foreign key is implicitly mapped to + ** the PRIMARY KEY of table pParent. The PRIMARY KEY index may be + ** identified by the test (Index.autoIndex==2). */ + if( pIdx->autoIndex==2 ){ + if( aiCol ){ + int i; + for(i=0; iaCol[i].iFrom; + } + break; + } + }else{ + /* If zKey is non-NULL, then this foreign key was declared to + ** map to an explicit list of columns in table pParent. Check if this + ** index matches those columns. Also, check that the index uses + ** the default collation sequences for each column. */ + int i, j; + for(i=0; iaiColumn[i]; /* Index of column in parent tbl */ + char *zDfltColl; /* Def. collation for column */ + char *zIdxCol; /* Name of indexed column */ + + /* If the index uses a collation sequence that is different from + ** the default collation sequence for the column, this index is + ** unusable. Bail out early in this case. */ + zDfltColl = pParent->aCol[iCol].zColl; + if( !zDfltColl ){ + zDfltColl = "BINARY"; + } + if( sqlite3StrICmp(pIdx->azColl[i], zDfltColl) ) break; - zColAff = (char *)sqlite3Malloc(pTab->nCol+1); - if( !zColAff ){ - db->mallocFailed = 1; - return; + zIdxCol = pParent->aCol[iCol].zName; + for(j=0; jaCol[j].zCol, zIdxCol)==0 ){ + if( aiCol ) aiCol[i] = pFKey->aCol[j].iFrom; + break; + } + } + if( j==nCol ) break; + } + if( i==nCol ) break; /* pIdx is usable */ + } } + } - for(i=0; inCol; i++){ - zColAff[i] = pTab->aCol[i].affinity; + if( !pIdx ){ + if( !pParse->disableTriggers ){ + sqlite3ErrorMsg(pParse, "foreign key mismatch"); } - zColAff[pTab->nCol] = '\0'; - - pTab->zColAff = zColAff; + sqlite3DbFree(pParse->db, aiCol); + return 1; } - sqlite3VdbeChangeP4(v, -1, pTab->zColAff, 0); + *ppIdx = pIdx; + return 0; } /* -** Return non-zero if the table pTab in database iDb or any of its indices -** have been opened at any point in the VDBE program beginning at location -** iStartAddr throught the end of the program. This is used to see if -** a statement of the form "INSERT INTO SELECT ..." can -** run without using temporary table for the results of the SELECT. -*/ -static int readsTable(Vdbe *v, int iStartAddr, int iDb, Table *pTab){ - int i; - int iEnd = sqlite3VdbeCurrentAddr(v); - for(i=iStartAddr; iopcode==OP_OpenRead && pOp->p3==iDb ){ - Index *pIndex; - int tnum = pOp->p2; - if( tnum==pTab->tnum ){ - return 1; +** This function is called when a row is inserted into or deleted from the +** child table of foreign key constraint pFKey. If an SQL UPDATE is executed +** on the child table of pFKey, this function is invoked twice for each row +** affected - once to "delete" the old row, and then again to "insert" the +** new row. +** +** Each time it is called, this function generates VDBE code to locate the +** row in the parent table that corresponds to the row being inserted into +** or deleted from the child table. If the parent row can be found, no +** special action is taken. Otherwise, if the parent row can *not* be +** found in the parent table: +** +** Operation | FK type | Action taken +** -------------------------------------------------------------------------- +** INSERT immediate Increment the "immediate constraint counter". +** +** DELETE immediate Decrement the "immediate constraint counter". +** +** INSERT deferred Increment the "deferred constraint counter". +** +** DELETE deferred Decrement the "deferred constraint counter". +** +** These operations are identified in the comment at the top of this file +** (fkey.c) as "I.1" and "D.1". +*/ +static void fkLookupParent( + Parse *pParse, /* Parse context */ + int iDb, /* Index of database housing pTab */ + Table *pTab, /* Parent table of FK pFKey */ + Index *pIdx, /* Unique index on parent key columns in pTab */ + FKey *pFKey, /* Foreign key constraint */ + int *aiCol, /* Map from parent key columns to child table columns */ + int regData, /* Address of array containing child table row */ + int nIncr, /* Increment constraint counter by this */ + int isIgnore /* If true, pretend pTab contains all NULL values */ +){ + int i; /* Iterator variable */ + Vdbe *v = sqlite3GetVdbe(pParse); /* Vdbe to add code to */ + int iCur = pParse->nTab - 1; /* Cursor number to use */ + int iOk = sqlite3VdbeMakeLabel(v); /* jump here if parent key found */ + + /* If nIncr is less than zero, then check at runtime if there are any + ** outstanding constraints to resolve. If there are not, there is no need + ** to check if deleting this row resolves any outstanding violations. + ** + ** Check if any of the key columns in the child table row are NULL. If + ** any are, then the constraint is considered satisfied. No need to + ** search for a matching row in the parent table. */ + if( nIncr<0 ){ + sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, iOk); + } + for(i=0; inCol; i++){ + int iReg = aiCol[i] + regData + 1; + sqlite3VdbeAddOp2(v, OP_IsNull, iReg, iOk); + } + + if( isIgnore==0 ){ + if( pIdx==0 ){ + /* If pIdx is NULL, then the parent key is the INTEGER PRIMARY KEY + ** column of the parent table (table pTab). */ + int iMustBeInt; /* Address of MustBeInt instruction */ + int regTemp = sqlite3GetTempReg(pParse); + + /* Invoke MustBeInt to coerce the child key value to an integer (i.e. + ** apply the affinity of the parent key). If this fails, then there + ** is no matching parent key. Before using MustBeInt, make a copy of + ** the value. Otherwise, the value inserted into the child key column + ** will have INTEGER affinity applied to it, which may not be correct. */ + sqlite3VdbeAddOp2(v, OP_SCopy, aiCol[0]+1+regData, regTemp); + iMustBeInt = sqlite3VdbeAddOp2(v, OP_MustBeInt, regTemp, 0); + + /* If the parent table is the same as the child table, and we are about + ** to increment the constraint-counter (i.e. this is an INSERT operation), + ** then check if the row being inserted matches itself. If so, do not + ** increment the constraint-counter. */ + if( pTab==pFKey->pFrom && nIncr==1 ){ + sqlite3VdbeAddOp3(v, OP_Eq, regData, iOk, regTemp); } - for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ - if( tnum==pIndex->tnum ){ - return 1; + + sqlite3OpenTable(pParse, iCur, iDb, pTab, OP_OpenRead); + sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regTemp); + sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk); + sqlite3VdbeJumpHere(v, sqlite3VdbeCurrentAddr(v)-2); + sqlite3VdbeJumpHere(v, iMustBeInt); + sqlite3ReleaseTempReg(pParse, regTemp); + }else{ + int nCol = pFKey->nCol; + int regTemp = sqlite3GetTempRange(pParse, nCol); + int regRec = sqlite3GetTempReg(pParse); + KeyInfo *pKey = sqlite3IndexKeyinfo(pParse, pIdx); + + sqlite3VdbeAddOp3(v, OP_OpenRead, iCur, pIdx->tnum, iDb); + sqlite3VdbeChangeP4(v, -1, (char*)pKey, P4_KEYINFO_HANDOFF); + for(i=0; ipFrom && nIncr==1 ){ + int iJump = sqlite3VdbeCurrentAddr(v) + nCol + 1; + for(i=0; iaiColumn[i]+1+regData; + sqlite3VdbeAddOp3(v, OP_Ne, iChild, iJump, iParent); } + sqlite3VdbeAddOp2(v, OP_Goto, 0, iOk); } + + sqlite3VdbeAddOp3(v, OP_MakeRecord, regTemp, nCol, regRec); + sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); + sqlite3VdbeAddOp4Int(v, OP_Found, iCur, iOk, regRec, 0); + + sqlite3ReleaseTempReg(pParse, regRec); + sqlite3ReleaseTempRange(pParse, regTemp, nCol); } -#ifndef SQLITE_OMIT_VIRTUALTABLE - if( pOp->opcode==OP_VOpen && pOp->p4.pVtab==pTab->pVtab ){ - assert( pOp->p4.pVtab!=0 ); - assert( pOp->p4type==P4_VTAB ); - return 1; + } + + if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){ + /* Special case: If this is an INSERT statement that will insert exactly + ** one row into the table, raise a constraint immediately instead of + ** incrementing a counter. This is necessary as the VM code is being + ** generated for will not open a statement transaction. */ + assert( nIncr==1 ); + sqlite3HaltConstraint( + pParse, OE_Abort, "foreign key constraint failed", P4_STATIC + ); + }else{ + if( nIncr>0 && pFKey->isDeferred==0 ){ + sqlite3ParseToplevel(pParse)->mayAbort = 1; } -#endif + sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); } - return 0; + + sqlite3VdbeResolveLabel(v, iOk); + sqlite3VdbeAddOp1(v, OP_Close, iCur); } -#ifndef SQLITE_OMIT_AUTOINCREMENT /* -** Locate or create an AutoincInfo structure associated with table pTab -** which is in database iDb. Return the register number for the register -** that holds the maximum rowid. +** This function is called to generate code executed when a row is deleted +** from the parent table of foreign key constraint pFKey and, if pFKey is +** deferred, when a row is inserted into the same table. When generating +** code for an SQL UPDATE operation, this function may be called twice - +** once to "delete" the old row and once to "insert" the new row. ** -** There is at most one AutoincInfo structure per table even if the -** same table is autoincremented multiple times due to inserts within -** triggers. A new AutoincInfo structure is created if this is the -** first use of table pTab. On 2nd and subsequent uses, the original -** AutoincInfo structure is used. +** The code generated by this function scans through the rows in the child +** table that correspond to the parent table row being deleted or inserted. +** For each child row found, one of the following actions is taken: ** -** Three memory locations are allocated: +** Operation | FK type | Action taken +** -------------------------------------------------------------------------- +** DELETE immediate Increment the "immediate constraint counter". +** Or, if the ON (UPDATE|DELETE) action is RESTRICT, +** throw a "foreign key constraint failed" exception. ** -** (1) Register to hold the name of the pTab table. -** (2) Register to hold the maximum ROWID of pTab. -** (3) Register to hold the rowid in sqlite_sequence of pTab +** INSERT immediate Decrement the "immediate constraint counter". ** -** The 2nd register is the one that is returned. That is all the -** insert routine needs to know about. +** DELETE deferred Increment the "deferred constraint counter". +** Or, if the ON (UPDATE|DELETE) action is RESTRICT, +** throw a "foreign key constraint failed" exception. +** +** INSERT deferred Decrement the "deferred constraint counter". +** +** These operations are identified in the comment at the top of this file +** (fkey.c) as "I.2" and "D.2". */ -static int autoIncBegin( - Parse *pParse, /* Parsing context */ - int iDb, /* Index of the database holding pTab */ - Table *pTab /* The table we are writing to */ -){ - int memId = 0; /* Register holding maximum rowid */ - if( pTab->tabFlags & TF_Autoincrement ){ - AutoincInfo *pInfo; +static void fkScanChildren( + Parse *pParse, /* Parse context */ + SrcList *pSrc, /* SrcList containing the table to scan */ + Table *pTab, + Index *pIdx, /* Foreign key index */ + FKey *pFKey, /* Foreign key relationship */ + int *aiCol, /* Map from pIdx cols to child table cols */ + int regData, /* Referenced table data starts here */ + int nIncr /* Amount to increment deferred counter by */ +){ + sqlite3 *db = pParse->db; /* Database handle */ + int i; /* Iterator variable */ + Expr *pWhere = 0; /* WHERE clause to scan with */ + NameContext sNameContext; /* Context used to resolve WHERE clause */ + WhereInfo *pWInfo; /* Context used by sqlite3WhereXXX() */ + int iFkIfZero = 0; /* Address of OP_FkIfZero */ + Vdbe *v = sqlite3GetVdbe(pParse); - pInfo = pParse->pAinc; - while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; } - if( pInfo==0 ){ - pInfo = sqlite3DbMallocRaw(pParse->db, sizeof(*pInfo)); - if( pInfo==0 ) return 0; - pInfo->pNext = pParse->pAinc; - pParse->pAinc = pInfo; - pInfo->pTab = pTab; - pInfo->iDb = iDb; - pParse->nMem++; /* Register to hold name of table */ - pInfo->regCtr = ++pParse->nMem; /* Max rowid register */ - pParse->nMem++; /* Rowid in sqlite_sequence */ + assert( !pIdx || pIdx->pTable==pTab ); + + if( nIncr<0 ){ + iFkIfZero = sqlite3VdbeAddOp2(v, OP_FkIfZero, pFKey->isDeferred, 0); + } + + /* Create an Expr object representing an SQL expression like: + ** + ** = AND = ... + ** + ** The collation sequence used for the comparison should be that of + ** the parent key columns. The affinity of the parent key column should + ** be applied to each child key value before the comparison takes place. + */ + for(i=0; inCol; i++){ + Expr *pLeft; /* Value from parent table row */ + Expr *pRight; /* Column ref to child table */ + Expr *pEq; /* Expression (pLeft = pRight) */ + int iCol; /* Index of column in child table */ + const char *zCol; /* Name of column in child table */ + + pLeft = sqlite3Expr(db, TK_REGISTER, 0); + if( pLeft ){ + /* Set the collation sequence and affinity of the LHS of each TK_EQ + ** expression to the parent key column defaults. */ + if( pIdx ){ + Column *pCol; + iCol = pIdx->aiColumn[i]; + pCol = &pIdx->pTable->aCol[iCol]; + pLeft->iTable = regData+iCol+1; + pLeft->affinity = pCol->affinity; + pLeft->pColl = sqlite3LocateCollSeq(pParse, pCol->zColl); + }else{ + pLeft->iTable = regData; + pLeft->affinity = SQLITE_AFF_INTEGER; + } } - memId = pInfo->regCtr; + iCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; + assert( iCol>=0 ); + zCol = pFKey->pFrom->aCol[iCol].zName; + pRight = sqlite3Expr(db, TK_ID, zCol); + pEq = sqlite3PExpr(pParse, TK_EQ, pLeft, pRight, 0); + pWhere = sqlite3ExprAnd(db, pWhere, pEq); + } + + /* If the child table is the same as the parent table, and this scan + ** is taking place as part of a DELETE operation (operation D.2), omit the + ** row being deleted from the scan by adding ($rowid != rowid) to the WHERE + ** clause, where $rowid is the rowid of the row being deleted. */ + if( pTab==pFKey->pFrom && nIncr>0 ){ + Expr *pEq; /* Expression (pLeft = pRight) */ + Expr *pLeft; /* Value from parent table row */ + Expr *pRight; /* Column ref to child table */ + pLeft = sqlite3Expr(db, TK_REGISTER, 0); + pRight = sqlite3Expr(db, TK_COLUMN, 0); + if( pLeft && pRight ){ + pLeft->iTable = regData; + pLeft->affinity = SQLITE_AFF_INTEGER; + pRight->iTable = pSrc->a[0].iCursor; + pRight->iColumn = -1; + } + pEq = sqlite3PExpr(pParse, TK_NE, pLeft, pRight, 0); + pWhere = sqlite3ExprAnd(db, pWhere, pEq); + } + + /* Resolve the references in the WHERE clause. */ + memset(&sNameContext, 0, sizeof(NameContext)); + sNameContext.pSrcList = pSrc; + sNameContext.pParse = pParse; + sqlite3ResolveExprNames(&sNameContext, pWhere); + + /* Create VDBE to loop through the entries in pSrc that match the WHERE + ** clause. If the constraint is not deferred, throw an exception for + ** each row found. Otherwise, for deferred constraints, increment the + ** deferred constraint counter by nIncr for each row selected. */ + pWInfo = sqlite3WhereBegin(pParse, pSrc, pWhere, 0, 0); + if( nIncr>0 && pFKey->isDeferred==0 ){ + sqlite3ParseToplevel(pParse)->mayAbort = 1; + } + sqlite3VdbeAddOp2(v, OP_FkCounter, pFKey->isDeferred, nIncr); + if( pWInfo ){ + sqlite3WhereEnd(pWInfo); + } + + /* Clean up the WHERE clause constructed above. */ + sqlite3ExprDelete(db, pWhere); + if( iFkIfZero ){ + sqlite3VdbeJumpHere(v, iFkIfZero); } - return memId; } /* -** This routine generates code that will initialize all of the -** register used by the autoincrement tracker. +** This function returns a pointer to the head of a linked list of FK +** constraints for which table pTab is the parent table. For example, +** given the following schema: +** +** CREATE TABLE t1(a PRIMARY KEY); +** CREATE TABLE t2(b REFERENCES t1(a); +** +** Calling this function with table "t1" as an argument returns a pointer +** to the FKey structure representing the foreign key constraint on table +** "t2". Calling this function with "t2" as the argument would return a +** NULL pointer (as there are no FK constraints for which t2 is the parent +** table). */ -SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse){ - AutoincInfo *p; /* Information about an AUTOINCREMENT */ - sqlite3 *db = pParse->db; /* The database connection */ - Db *pDb; /* Database only autoinc table */ - int memId; /* Register holding max rowid */ - int addr; /* A VDBE address */ - Vdbe *v = pParse->pVdbe; /* VDBE under construction */ +SQLITE_PRIVATE FKey *sqlite3FkReferences(Table *pTab){ + int nName = sqlite3Strlen30(pTab->zName); + return (FKey *)sqlite3HashFind(&pTab->pSchema->fkeyHash, pTab->zName, nName); +} - assert( v ); /* We failed long ago if this is not so */ - for(p = pParse->pAinc; p; p = p->pNext){ - pDb = &db->aDb[p->iDb]; - memId = p->regCtr; - sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead); - addr = sqlite3VdbeCurrentAddr(v); - sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0); - sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9); - sqlite3VdbeAddOp3(v, OP_Column, 0, 0, memId); - sqlite3VdbeAddOp3(v, OP_Ne, memId-1, addr+7, memId); - sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL); - sqlite3VdbeAddOp2(v, OP_Rowid, 0, memId+1); - sqlite3VdbeAddOp3(v, OP_Column, 0, 1, memId); - sqlite3VdbeAddOp2(v, OP_Goto, 0, addr+9); - sqlite3VdbeAddOp2(v, OP_Next, 0, addr+2); - sqlite3VdbeAddOp2(v, OP_Integer, 0, memId); - sqlite3VdbeAddOp0(v, OP_Close); +/* +** The second argument is a Trigger structure allocated by the +** fkActionTrigger() routine. This function deletes the Trigger structure +** and all of its sub-components. +** +** The Trigger structure or any of its sub-components may be allocated from +** the lookaside buffer belonging to database handle dbMem. +*/ +static void fkTriggerDelete(sqlite3 *dbMem, Trigger *p){ + if( p ){ + TriggerStep *pStep = p->step_list; + sqlite3ExprDelete(dbMem, pStep->pWhere); + sqlite3ExprListDelete(dbMem, pStep->pExprList); + sqlite3SelectDelete(dbMem, pStep->pSelect); + sqlite3ExprDelete(dbMem, p->pWhen); + sqlite3DbFree(dbMem, p); } } /* -** Update the maximum rowid for an autoincrement calculation. +** This function is called to generate code that runs when table pTab is +** being dropped from the database. The SrcList passed as the second argument +** to this function contains a single entry guaranteed to resolve to +** table pTab. ** -** This routine should be called when the top of the stack holds a +** Normally, no code is required. However, if either +** +** (a) The table is the parent table of a FK constraint, or +** (b) The table is the child table of a deferred FK constraint and it is +** determined at runtime that there are outstanding deferred FK +** constraint violations in the database, +** +** then the equivalent of "DELETE FROM " is executed before dropping +** the table from the database. Triggers are disabled while running this +** DELETE, but foreign key actions are not. +*/ +SQLITE_PRIVATE void sqlite3FkDropTable(Parse *pParse, SrcList *pName, Table *pTab){ + sqlite3 *db = pParse->db; + if( (db->flags&SQLITE_ForeignKeys) && !IsVirtual(pTab) && !pTab->pSelect ){ + int iSkip = 0; + Vdbe *v = sqlite3GetVdbe(pParse); + + assert( v ); /* VDBE has already been allocated */ + if( sqlite3FkReferences(pTab)==0 ){ + /* Search for a deferred foreign key constraint for which this table + ** is the child table. If one cannot be found, return without + ** generating any VDBE code. If one can be found, then jump over + ** the entire DELETE if there are no outstanding deferred constraints + ** when this statement is run. */ + FKey *p; + for(p=pTab->pFKey; p; p=p->pNextFrom){ + if( p->isDeferred ) break; + } + if( !p ) return; + iSkip = sqlite3VdbeMakeLabel(v); + sqlite3VdbeAddOp2(v, OP_FkIfZero, 1, iSkip); + } + + pParse->disableTriggers = 1; + sqlite3DeleteFrom(pParse, sqlite3SrcListDup(db, pName, 0), 0); + pParse->disableTriggers = 0; + + /* If the DELETE has generated immediate foreign key constraint + ** violations, halt the VDBE and return an error at this point, before + ** any modifications to the schema are made. This is because statement + ** transactions are not able to rollback schema changes. */ + sqlite3VdbeAddOp2(v, OP_FkIfZero, 0, sqlite3VdbeCurrentAddr(v)+2); + sqlite3HaltConstraint( + pParse, OE_Abort, "foreign key constraint failed", P4_STATIC + ); + + if( iSkip ){ + sqlite3VdbeResolveLabel(v, iSkip); + } + } +} + +/* +** This function is called when inserting, deleting or updating a row of +** table pTab to generate VDBE code to perform foreign key constraint +** processing for the operation. +** +** For a DELETE operation, parameter regOld is passed the index of the +** first register in an array of (pTab->nCol+1) registers containing the +** rowid of the row being deleted, followed by each of the column values +** of the row being deleted, from left to right. Parameter regNew is passed +** zero in this case. +** +** For an INSERT operation, regOld is passed zero and regNew is passed the +** first register of an array of (pTab->nCol+1) registers containing the new +** row data. +** +** For an UPDATE operation, this function is called twice. Once before +** the original record is deleted from the table using the calling convention +** described for DELETE. Then again after the original record is deleted +** but before the new record is inserted using the INSERT convention. +*/ +SQLITE_PRIVATE void sqlite3FkCheck( + Parse *pParse, /* Parse context */ + Table *pTab, /* Row is being deleted from this table */ + int regOld, /* Previous row data is stored here */ + int regNew /* New row data is stored here */ +){ + sqlite3 *db = pParse->db; /* Database handle */ + Vdbe *v; /* VM to write code to */ + FKey *pFKey; /* Used to iterate through FKs */ + int iDb; /* Index of database containing pTab */ + const char *zDb; /* Name of database containing pTab */ + int isIgnoreErrors = pParse->disableTriggers; + + /* Exactly one of regOld and regNew should be non-zero. */ + assert( (regOld==0)!=(regNew==0) ); + + /* If foreign-keys are disabled, this function is a no-op. */ + if( (db->flags&SQLITE_ForeignKeys)==0 ) return; + + v = sqlite3GetVdbe(pParse); + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); + zDb = db->aDb[iDb].zName; + + /* Loop through all the foreign key constraints for which pTab is the + ** child table (the table that the foreign key definition is part of). */ + for(pFKey=pTab->pFKey; pFKey; pFKey=pFKey->pNextFrom){ + Table *pTo; /* Parent table of foreign key pFKey */ + Index *pIdx = 0; /* Index on key columns in pTo */ + int *aiFree = 0; + int *aiCol; + int iCol; + int i; + int isIgnore = 0; + + /* Find the parent table of this foreign key. Also find a unique index + ** on the parent key columns in the parent table. If either of these + ** schema items cannot be located, set an error in pParse and return + ** early. */ + if( pParse->disableTriggers ){ + pTo = sqlite3FindTable(db, pFKey->zTo, zDb); + }else{ + pTo = sqlite3LocateTable(pParse, 0, pFKey->zTo, zDb); + } + if( !pTo || locateFkeyIndex(pParse, pTo, pFKey, &pIdx, &aiFree) ){ + if( !isIgnoreErrors || db->mallocFailed ) return; + continue; + } + assert( pFKey->nCol==1 || (aiFree && pIdx) ); + + if( aiFree ){ + aiCol = aiFree; + }else{ + iCol = pFKey->aCol[0].iFrom; + aiCol = &iCol; + } + for(i=0; inCol; i++){ + if( aiCol[i]==pTab->iPKey ){ + aiCol[i] = -1; + } +#ifndef SQLITE_OMIT_AUTHORIZATION + /* Request permission to read the parent key columns. If the + ** authorization callback returns SQLITE_IGNORE, behave as if any + ** values read from the parent table are NULL. */ + if( db->xAuth ){ + int rcauth; + char *zCol = pTo->aCol[pIdx ? pIdx->aiColumn[i] : pTo->iPKey].zName; + rcauth = sqlite3AuthReadCol(pParse, pTo->zName, zCol, iDb); + isIgnore = (rcauth==SQLITE_IGNORE); + } +#endif + } + + /* Take a shared-cache advisory read-lock on the parent table. Allocate + ** a cursor to use to search the unique index on the parent key columns + ** in the parent table. */ + sqlite3TableLock(pParse, iDb, pTo->tnum, 0, pTo->zName); + pParse->nTab++; + + if( regOld!=0 ){ + /* A row is being removed from the child table. Search for the parent. + ** If the parent does not exist, removing the child row resolves an + ** outstanding foreign key constraint violation. */ + fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regOld, -1,isIgnore); + } + if( regNew!=0 ){ + /* A row is being added to the child table. If a parent row cannot + ** be found, adding the child row has violated the FK constraint. */ + fkLookupParent(pParse, iDb, pTo, pIdx, pFKey, aiCol, regNew, +1,isIgnore); + } + + sqlite3DbFree(db, aiFree); + } + + /* Loop through all the foreign key constraints that refer to this table */ + for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){ + Index *pIdx = 0; /* Foreign key index for pFKey */ + SrcList *pSrc; + int *aiCol = 0; + + if( !pFKey->isDeferred && !pParse->pToplevel && !pParse->isMultiWrite ){ + assert( regOld==0 && regNew!=0 ); + /* Inserting a single row into a parent table cannot cause an immediate + ** foreign key violation. So do nothing in this case. */ + continue; + } + + if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ){ + if( !isIgnoreErrors || db->mallocFailed ) return; + continue; + } + assert( aiCol || pFKey->nCol==1 ); + + /* Create a SrcList structure containing a single table (the table + ** the foreign key that refers to this table is attached to). This + ** is required for the sqlite3WhereXXX() interface. */ + pSrc = sqlite3SrcListAppend(db, 0, 0, 0); + if( pSrc ){ + struct SrcList_item *pItem = pSrc->a; + pItem->pTab = pFKey->pFrom; + pItem->zName = pFKey->pFrom->zName; + pItem->pTab->nRef++; + pItem->iCursor = pParse->nTab++; + + if( regNew!=0 ){ + fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regNew, -1); + } + if( regOld!=0 ){ + /* If there is a RESTRICT action configured for the current operation + ** on the parent table of this FK, then throw an exception + ** immediately if the FK constraint is violated, even if this is a + ** deferred trigger. That's what RESTRICT means. To defer checking + ** the constraint, the FK should specify NO ACTION (represented + ** using OE_None). NO ACTION is the default. */ + fkScanChildren(pParse, pSrc, pTab, pIdx, pFKey, aiCol, regOld, 1); + } + pItem->zName = 0; + sqlite3SrcListDelete(db, pSrc); + } + sqlite3DbFree(db, aiCol); + } +} + +#define COLUMN_MASK(x) (((x)>31) ? 0xffffffff : ((u32)1<<(x))) + +/* +** This function is called before generating code to update or delete a +** row contained in table pTab. +*/ +SQLITE_PRIVATE u32 sqlite3FkOldmask( + Parse *pParse, /* Parse context */ + Table *pTab /* Table being modified */ +){ + u32 mask = 0; + if( pParse->db->flags&SQLITE_ForeignKeys ){ + FKey *p; + int i; + for(p=pTab->pFKey; p; p=p->pNextFrom){ + for(i=0; inCol; i++) mask |= COLUMN_MASK(p->aCol[i].iFrom); + } + for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ + Index *pIdx = 0; + locateFkeyIndex(pParse, pTab, p, &pIdx, 0); + if( pIdx ){ + for(i=0; inColumn; i++) mask |= COLUMN_MASK(pIdx->aiColumn[i]); + } + } + } + return mask; +} + +/* +** This function is called before generating code to update or delete a +** row contained in table pTab. If the operation is a DELETE, then +** parameter aChange is passed a NULL value. For an UPDATE, aChange points +** to an array of size N, where N is the number of columns in table pTab. +** If the i'th column is not modified by the UPDATE, then the corresponding +** entry in the aChange[] array is set to -1. If the column is modified, +** the value is 0 or greater. Parameter chngRowid is set to true if the +** UPDATE statement modifies the rowid fields of the table. +** +** If any foreign key processing will be required, this function returns +** true. If there is no foreign key related processing, this function +** returns false. +*/ +SQLITE_PRIVATE int sqlite3FkRequired( + Parse *pParse, /* Parse context */ + Table *pTab, /* Table being modified */ + int *aChange, /* Non-NULL for UPDATE operations */ + int chngRowid /* True for UPDATE that affects rowid */ +){ + if( pParse->db->flags&SQLITE_ForeignKeys ){ + if( !aChange ){ + /* A DELETE operation. Foreign key processing is required if the + ** table in question is either the child or parent table for any + ** foreign key constraint. */ + return (sqlite3FkReferences(pTab) || pTab->pFKey); + }else{ + /* This is an UPDATE. Foreign key processing is only required if the + ** operation modifies one or more child or parent key columns. */ + int i; + FKey *p; + + /* Check if any child key columns are being modified. */ + for(p=pTab->pFKey; p; p=p->pNextFrom){ + for(i=0; inCol; i++){ + int iChildKey = p->aCol[i].iFrom; + if( aChange[iChildKey]>=0 ) return 1; + if( iChildKey==pTab->iPKey && chngRowid ) return 1; + } + } + + /* Check if any parent key columns are being modified. */ + for(p=sqlite3FkReferences(pTab); p; p=p->pNextTo){ + for(i=0; inCol; i++){ + char *zKey = p->aCol[i].zCol; + int iKey; + for(iKey=0; iKeynCol; iKey++){ + Column *pCol = &pTab->aCol[iKey]; + if( (zKey ? !sqlite3StrICmp(pCol->zName, zKey) : pCol->isPrimKey) ){ + if( aChange[iKey]>=0 ) return 1; + if( iKey==pTab->iPKey && chngRowid ) return 1; + } + } + } + } + } + } + return 0; +} + +/* +** This function is called when an UPDATE or DELETE operation is being +** compiled on table pTab, which is the parent table of foreign-key pFKey. +** If the current operation is an UPDATE, then the pChanges parameter is +** passed a pointer to the list of columns being modified. If it is a +** DELETE, pChanges is passed a NULL pointer. +** +** It returns a pointer to a Trigger structure containing a trigger +** equivalent to the ON UPDATE or ON DELETE action specified by pFKey. +** If the action is "NO ACTION" or "RESTRICT", then a NULL pointer is +** returned (these actions require no special handling by the triggers +** sub-system, code for them is created by fkScanChildren()). +** +** For example, if pFKey is the foreign key and pTab is table "p" in +** the following schema: +** +** CREATE TABLE p(pk PRIMARY KEY); +** CREATE TABLE c(ck REFERENCES p ON DELETE CASCADE); +** +** then the returned trigger structure is equivalent to: +** +** CREATE TRIGGER ... DELETE ON p BEGIN +** DELETE FROM c WHERE ck = old.pk; +** END; +** +** The returned pointer is cached as part of the foreign key object. It +** is eventually freed along with the rest of the foreign key object by +** sqlite3FkDelete(). +*/ +static Trigger *fkActionTrigger( + Parse *pParse, /* Parse context */ + Table *pTab, /* Table being updated or deleted from */ + FKey *pFKey, /* Foreign key to get action for */ + ExprList *pChanges /* Change-list for UPDATE, NULL for DELETE */ +){ + sqlite3 *db = pParse->db; /* Database handle */ + int action; /* One of OE_None, OE_Cascade etc. */ + Trigger *pTrigger; /* Trigger definition to return */ + int iAction = (pChanges!=0); /* 1 for UPDATE, 0 for DELETE */ + + action = pFKey->aAction[iAction]; + pTrigger = pFKey->apTrigger[iAction]; + + if( action!=OE_None && !pTrigger ){ + u8 enableLookaside; /* Copy of db->lookaside.bEnabled */ + char const *zFrom; /* Name of child table */ + int nFrom; /* Length in bytes of zFrom */ + Index *pIdx = 0; /* Parent key index for this FK */ + int *aiCol = 0; /* child table cols -> parent key cols */ + TriggerStep *pStep = 0; /* First (only) step of trigger program */ + Expr *pWhere = 0; /* WHERE clause of trigger step */ + ExprList *pList = 0; /* Changes list if ON UPDATE CASCADE */ + Select *pSelect = 0; /* If RESTRICT, "SELECT RAISE(...)" */ + int i; /* Iterator variable */ + Expr *pWhen = 0; /* WHEN clause for the trigger */ + + if( locateFkeyIndex(pParse, pTab, pFKey, &pIdx, &aiCol) ) return 0; + assert( aiCol || pFKey->nCol==1 ); + + for(i=0; inCol; i++){ + Token tOld = { "old", 3 }; /* Literal "old" token */ + Token tNew = { "new", 3 }; /* Literal "new" token */ + Token tFromCol; /* Name of column in child table */ + Token tToCol; /* Name of column in parent table */ + int iFromCol; /* Idx of column in child table */ + Expr *pEq; /* tFromCol = OLD.tToCol */ + + iFromCol = aiCol ? aiCol[i] : pFKey->aCol[0].iFrom; + assert( iFromCol>=0 ); + tToCol.z = pIdx ? pTab->aCol[pIdx->aiColumn[i]].zName : "oid"; + tFromCol.z = pFKey->pFrom->aCol[iFromCol].zName; + + tToCol.n = sqlite3Strlen30(tToCol.z); + tFromCol.n = sqlite3Strlen30(tFromCol.z); + + /* Create the expression "OLD.zToCol = zFromCol". It is important + ** that the "OLD.zToCol" term is on the LHS of the = operator, so + ** that the affinity and collation sequence associated with the + ** parent table are used for the comparison. */ + pEq = sqlite3PExpr(pParse, TK_EQ, + sqlite3PExpr(pParse, TK_DOT, + sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld), + sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol) + , 0), + sqlite3PExpr(pParse, TK_ID, 0, 0, &tFromCol) + , 0); + pWhere = sqlite3ExprAnd(db, pWhere, pEq); + + /* For ON UPDATE, construct the next term of the WHEN clause. + ** The final WHEN clause will be like this: + ** + ** WHEN NOT(old.col1 IS new.col1 AND ... AND old.colN IS new.colN) + */ + if( pChanges ){ + pEq = sqlite3PExpr(pParse, TK_IS, + sqlite3PExpr(pParse, TK_DOT, + sqlite3PExpr(pParse, TK_ID, 0, 0, &tOld), + sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol), + 0), + sqlite3PExpr(pParse, TK_DOT, + sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew), + sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol), + 0), + 0); + pWhen = sqlite3ExprAnd(db, pWhen, pEq); + } + + if( action!=OE_Restrict && (action!=OE_Cascade || pChanges) ){ + Expr *pNew; + if( action==OE_Cascade ){ + pNew = sqlite3PExpr(pParse, TK_DOT, + sqlite3PExpr(pParse, TK_ID, 0, 0, &tNew), + sqlite3PExpr(pParse, TK_ID, 0, 0, &tToCol) + , 0); + }else if( action==OE_SetDflt ){ + Expr *pDflt = pFKey->pFrom->aCol[iFromCol].pDflt; + if( pDflt ){ + pNew = sqlite3ExprDup(db, pDflt, 0); + }else{ + pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0); + } + }else{ + pNew = sqlite3PExpr(pParse, TK_NULL, 0, 0, 0); + } + pList = sqlite3ExprListAppend(pParse, pList, pNew); + sqlite3ExprListSetName(pParse, pList, &tFromCol, 0); + } + } + sqlite3DbFree(db, aiCol); + + zFrom = pFKey->pFrom->zName; + nFrom = sqlite3Strlen30(zFrom); + + if( action==OE_Restrict ){ + Token tFrom; + Expr *pRaise; + + tFrom.z = zFrom; + tFrom.n = nFrom; + pRaise = sqlite3Expr(db, TK_RAISE, "foreign key constraint failed"); + if( pRaise ){ + pRaise->affinity = OE_Abort; + } + pSelect = sqlite3SelectNew(pParse, + sqlite3ExprListAppend(pParse, 0, pRaise), + sqlite3SrcListAppend(db, 0, &tFrom, 0), + pWhere, + 0, 0, 0, 0, 0, 0 + ); + pWhere = 0; + } + + /* In the current implementation, pTab->dbMem==0 for all tables except + ** for temporary tables used to describe subqueries. And temporary + ** tables do not have foreign key constraints. Hence, pTab->dbMem + ** should always be 0 there. + */ + enableLookaside = db->lookaside.bEnabled; + db->lookaside.bEnabled = 0; + + pTrigger = (Trigger *)sqlite3DbMallocZero(db, + sizeof(Trigger) + /* struct Trigger */ + sizeof(TriggerStep) + /* Single step in trigger program */ + nFrom + 1 /* Space for pStep->target.z */ + ); + if( pTrigger ){ + pStep = pTrigger->step_list = (TriggerStep *)&pTrigger[1]; + pStep->target.z = (char *)&pStep[1]; + pStep->target.n = nFrom; + memcpy((char *)pStep->target.z, zFrom, nFrom); + + pStep->pWhere = sqlite3ExprDup(db, pWhere, EXPRDUP_REDUCE); + pStep->pExprList = sqlite3ExprListDup(db, pList, EXPRDUP_REDUCE); + pStep->pSelect = sqlite3SelectDup(db, pSelect, EXPRDUP_REDUCE); + if( pWhen ){ + pWhen = sqlite3PExpr(pParse, TK_NOT, pWhen, 0, 0); + pTrigger->pWhen = sqlite3ExprDup(db, pWhen, EXPRDUP_REDUCE); + } + } + + /* Re-enable the lookaside buffer, if it was disabled earlier. */ + db->lookaside.bEnabled = enableLookaside; + + sqlite3ExprDelete(db, pWhere); + sqlite3ExprDelete(db, pWhen); + sqlite3ExprListDelete(db, pList); + sqlite3SelectDelete(db, pSelect); + if( db->mallocFailed==1 ){ + fkTriggerDelete(db, pTrigger); + return 0; + } + + switch( action ){ + case OE_Restrict: + pStep->op = TK_SELECT; + break; + case OE_Cascade: + if( !pChanges ){ + pStep->op = TK_DELETE; + break; + } + default: + pStep->op = TK_UPDATE; + } + pStep->pTrig = pTrigger; + pTrigger->pSchema = pTab->pSchema; + pTrigger->pTabSchema = pTab->pSchema; + pFKey->apTrigger[iAction] = pTrigger; + pTrigger->op = (pChanges ? TK_UPDATE : TK_DELETE); + } + + return pTrigger; +} + +/* +** This function is called when deleting or updating a row to implement +** any required CASCADE, SET NULL or SET DEFAULT actions. +*/ +SQLITE_PRIVATE void sqlite3FkActions( + Parse *pParse, /* Parse context */ + Table *pTab, /* Table being updated or deleted from */ + ExprList *pChanges, /* Change-list for UPDATE, NULL for DELETE */ + int regOld /* Address of array containing old row */ +){ + /* If foreign-key support is enabled, iterate through all FKs that + ** refer to table pTab. If there is an action associated with the FK + ** for this operation (either update or delete), invoke the associated + ** trigger sub-program. */ + if( pParse->db->flags&SQLITE_ForeignKeys ){ + FKey *pFKey; /* Iterator variable */ + for(pFKey = sqlite3FkReferences(pTab); pFKey; pFKey=pFKey->pNextTo){ + Trigger *pAction = fkActionTrigger(pParse, pTab, pFKey, pChanges); + if( pAction ){ + sqlite3CodeRowTriggerDirect(pParse, pAction, pTab, regOld, OE_Abort, 0); + } + } + } +} + +#endif /* ifndef SQLITE_OMIT_TRIGGER */ + +/* +** Free all memory associated with foreign key definitions attached to +** table pTab. Remove the deleted foreign keys from the Schema.fkeyHash +** hash table. +*/ +SQLITE_PRIVATE void sqlite3FkDelete(Table *pTab){ + FKey *pFKey; /* Iterator variable */ + FKey *pNext; /* Copy of pFKey->pNextFrom */ + + for(pFKey=pTab->pFKey; pFKey; pFKey=pNext){ + + /* Remove the FK from the fkeyHash hash table. */ + if( pFKey->pPrevTo ){ + pFKey->pPrevTo->pNextTo = pFKey->pNextTo; + }else{ + void *data = (void *)pFKey->pNextTo; + const char *z = (data ? pFKey->pNextTo->zTo : pFKey->zTo); + sqlite3HashInsert(&pTab->pSchema->fkeyHash, z, sqlite3Strlen30(z), data); + } + if( pFKey->pNextTo ){ + pFKey->pNextTo->pPrevTo = pFKey->pPrevTo; + } + + /* Delete any triggers created to implement actions for this FK. */ +#ifndef SQLITE_OMIT_TRIGGER + fkTriggerDelete(pTab->dbMem, pFKey->apTrigger[0]); + fkTriggerDelete(pTab->dbMem, pFKey->apTrigger[1]); +#endif + + /* EV: R-30323-21917 Each foreign key constraint in SQLite is + ** classified as either immediate or deferred. + */ + assert( pFKey->isDeferred==0 || pFKey->isDeferred==1 ); + + pNext = pFKey->pNextFrom; + sqlite3DbFree(pTab->dbMem, pFKey); + } +} +#endif /* ifndef SQLITE_OMIT_FOREIGN_KEY */ + +/************** End of fkey.c ************************************************/ +/************** Begin file insert.c ******************************************/ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** This file contains C code routines that are called by the parser +** to handle INSERT statements in SQLite. +*/ + +/* +** Generate code that will open a table for reading. +*/ +SQLITE_PRIVATE void sqlite3OpenTable( + Parse *p, /* Generate code into this VDBE */ + int iCur, /* The cursor number of the table */ + int iDb, /* The database index in sqlite3.aDb[] */ + Table *pTab, /* The table to be opened */ + int opcode /* OP_OpenRead or OP_OpenWrite */ +){ + Vdbe *v; + if( IsVirtual(pTab) ) return; + v = sqlite3GetVdbe(p); + assert( opcode==OP_OpenWrite || opcode==OP_OpenRead ); + sqlite3TableLock(p, iDb, pTab->tnum, (opcode==OP_OpenWrite)?1:0, pTab->zName); + sqlite3VdbeAddOp3(v, opcode, iCur, pTab->tnum, iDb); + sqlite3VdbeChangeP4(v, -1, SQLITE_INT_TO_PTR(pTab->nCol), P4_INT32); + VdbeComment((v, "%s", pTab->zName)); +} + +/* +** Return a pointer to the column affinity string associated with index +** pIdx. A column affinity string has one character for each column in +** the table, according to the affinity of the column: +** +** Character Column affinity +** ------------------------------ +** 'a' TEXT +** 'b' NONE +** 'c' NUMERIC +** 'd' INTEGER +** 'e' REAL +** +** An extra 'b' is appended to the end of the string to cover the +** rowid that appears as the last column in every index. +** +** Memory for the buffer containing the column index affinity string +** is managed along with the rest of the Index structure. It will be +** released when sqlite3DeleteIndex() is called. +*/ +SQLITE_PRIVATE const char *sqlite3IndexAffinityStr(Vdbe *v, Index *pIdx){ + if( !pIdx->zColAff ){ + /* The first time a column affinity string for a particular index is + ** required, it is allocated and populated here. It is then stored as + ** a member of the Index structure for subsequent use. + ** + ** The column affinity string will eventually be deleted by + ** sqliteDeleteIndex() when the Index structure itself is cleaned + ** up. + */ + int n; + Table *pTab = pIdx->pTable; + sqlite3 *db = sqlite3VdbeDb(v); + pIdx->zColAff = (char *)sqlite3Malloc(pIdx->nColumn+2); + if( !pIdx->zColAff ){ + db->mallocFailed = 1; + return 0; + } + for(n=0; nnColumn; n++){ + pIdx->zColAff[n] = pTab->aCol[pIdx->aiColumn[n]].affinity; + } + pIdx->zColAff[n++] = SQLITE_AFF_NONE; + pIdx->zColAff[n] = 0; + } + + return pIdx->zColAff; +} + +/* +** Set P4 of the most recently inserted opcode to a column affinity +** string for table pTab. A column affinity string has one character +** for each column indexed by the index, according to the affinity of the +** column: +** +** Character Column affinity +** ------------------------------ +** 'a' TEXT +** 'b' NONE +** 'c' NUMERIC +** 'd' INTEGER +** 'e' REAL +*/ +SQLITE_PRIVATE void sqlite3TableAffinityStr(Vdbe *v, Table *pTab){ + /* The first time a column affinity string for a particular table + ** is required, it is allocated and populated here. It is then + ** stored as a member of the Table structure for subsequent use. + ** + ** The column affinity string will eventually be deleted by + ** sqlite3DeleteTable() when the Table structure itself is cleaned up. + */ + if( !pTab->zColAff ){ + char *zColAff; + int i; + sqlite3 *db = sqlite3VdbeDb(v); + + zColAff = (char *)sqlite3Malloc(pTab->nCol+1); + if( !zColAff ){ + db->mallocFailed = 1; + return; + } + + for(i=0; inCol; i++){ + zColAff[i] = pTab->aCol[i].affinity; + } + zColAff[pTab->nCol] = '\0'; + + pTab->zColAff = zColAff; + } + + sqlite3VdbeChangeP4(v, -1, pTab->zColAff, 0); +} + +/* +** Return non-zero if the table pTab in database iDb or any of its indices +** have been opened at any point in the VDBE program beginning at location +** iStartAddr throught the end of the program. This is used to see if +** a statement of the form "INSERT INTO SELECT ..." can +** run without using temporary table for the results of the SELECT. +*/ +static int readsTable(Parse *p, int iStartAddr, int iDb, Table *pTab){ + Vdbe *v = sqlite3GetVdbe(p); + int i; + int iEnd = sqlite3VdbeCurrentAddr(v); +#ifndef SQLITE_OMIT_VIRTUALTABLE + VTable *pVTab = IsVirtual(pTab) ? sqlite3GetVTable(p->db, pTab) : 0; +#endif + + for(i=iStartAddr; iopcode==OP_OpenRead && pOp->p3==iDb ){ + Index *pIndex; + int tnum = pOp->p2; + if( tnum==pTab->tnum ){ + return 1; + } + for(pIndex=pTab->pIndex; pIndex; pIndex=pIndex->pNext){ + if( tnum==pIndex->tnum ){ + return 1; + } + } + } +#ifndef SQLITE_OMIT_VIRTUALTABLE + if( pOp->opcode==OP_VOpen && pOp->p4.pVtab==pVTab ){ + assert( pOp->p4.pVtab!=0 ); + assert( pOp->p4type==P4_VTAB ); + return 1; + } +#endif + } + return 0; +} + +#ifndef SQLITE_OMIT_AUTOINCREMENT +/* +** Locate or create an AutoincInfo structure associated with table pTab +** which is in database iDb. Return the register number for the register +** that holds the maximum rowid. +** +** There is at most one AutoincInfo structure per table even if the +** same table is autoincremented multiple times due to inserts within +** triggers. A new AutoincInfo structure is created if this is the +** first use of table pTab. On 2nd and subsequent uses, the original +** AutoincInfo structure is used. +** +** Three memory locations are allocated: +** +** (1) Register to hold the name of the pTab table. +** (2) Register to hold the maximum ROWID of pTab. +** (3) Register to hold the rowid in sqlite_sequence of pTab +** +** The 2nd register is the one that is returned. That is all the +** insert routine needs to know about. +*/ +static int autoIncBegin( + Parse *pParse, /* Parsing context */ + int iDb, /* Index of the database holding pTab */ + Table *pTab /* The table we are writing to */ +){ + int memId = 0; /* Register holding maximum rowid */ + if( pTab->tabFlags & TF_Autoincrement ){ + Parse *pToplevel = sqlite3ParseToplevel(pParse); + AutoincInfo *pInfo; + + pInfo = pToplevel->pAinc; + while( pInfo && pInfo->pTab!=pTab ){ pInfo = pInfo->pNext; } + if( pInfo==0 ){ + pInfo = sqlite3DbMallocRaw(pParse->db, sizeof(*pInfo)); + if( pInfo==0 ) return 0; + pInfo->pNext = pToplevel->pAinc; + pToplevel->pAinc = pInfo; + pInfo->pTab = pTab; + pInfo->iDb = iDb; + pToplevel->nMem++; /* Register to hold name of table */ + pInfo->regCtr = ++pToplevel->nMem; /* Max rowid register */ + pToplevel->nMem++; /* Rowid in sqlite_sequence */ + } + memId = pInfo->regCtr; + } + return memId; +} + +/* +** This routine generates code that will initialize all of the +** register used by the autoincrement tracker. +*/ +SQLITE_PRIVATE void sqlite3AutoincrementBegin(Parse *pParse){ + AutoincInfo *p; /* Information about an AUTOINCREMENT */ + sqlite3 *db = pParse->db; /* The database connection */ + Db *pDb; /* Database only autoinc table */ + int memId; /* Register holding max rowid */ + int addr; /* A VDBE address */ + Vdbe *v = pParse->pVdbe; /* VDBE under construction */ + + /* This routine is never called during trigger-generation. It is + ** only called from the top-level */ + assert( pParse->pTriggerTab==0 ); + assert( pParse==sqlite3ParseToplevel(pParse) ); + + assert( v ); /* We failed long ago if this is not so */ + for(p = pParse->pAinc; p; p = p->pNext){ + pDb = &db->aDb[p->iDb]; + memId = p->regCtr; + sqlite3OpenTable(pParse, 0, p->iDb, pDb->pSchema->pSeqTab, OP_OpenRead); + addr = sqlite3VdbeCurrentAddr(v); + sqlite3VdbeAddOp4(v, OP_String8, 0, memId-1, 0, p->pTab->zName, 0); + sqlite3VdbeAddOp2(v, OP_Rewind, 0, addr+9); + sqlite3VdbeAddOp3(v, OP_Column, 0, 0, memId); + sqlite3VdbeAddOp3(v, OP_Ne, memId-1, addr+7, memId); + sqlite3VdbeChangeP5(v, SQLITE_JUMPIFNULL); + sqlite3VdbeAddOp2(v, OP_Rowid, 0, memId+1); + sqlite3VdbeAddOp3(v, OP_Column, 0, 1, memId); + sqlite3VdbeAddOp2(v, OP_Goto, 0, addr+9); + sqlite3VdbeAddOp2(v, OP_Next, 0, addr+2); + sqlite3VdbeAddOp2(v, OP_Integer, 0, memId); + sqlite3VdbeAddOp0(v, OP_Close); + } +} + +/* +** Update the maximum rowid for an autoincrement calculation. +** +** This routine should be called when the top of the stack holds a ** new rowid that is about to be inserted. If that new rowid is ** larger than the maximum rowid in the memId memory cell, then the ** memory cell is updated. The stack is unchanged. @@ -70599,7 +74179,6 @@ int addrCont = 0; /* Top of insert loop. Label "C" in templates 3 and 4 */ int addrSelect = 0; /* Address of coroutine that implements the SELECT */ SelectDest dest; /* Destination for SELECT on rhs of INSERT */ - int newIdx = -1; /* Cursor for the NEW pseudo-table */ int iDb; /* Index of database holding TABLE */ Db *pDb; /* The database containing table being inserted into */ int appendFlag = 0; /* True if the insert is likely to be an append */ @@ -70615,7 +74194,6 @@ int regEof = 0; /* Register recording end of SELECT data */ int *aRegIdx = 0; /* One register allocated to each index */ - #ifndef SQLITE_OMIT_TRIGGER int isView; /* True if attempting to insert into a view */ Trigger *pTrigger; /* List of triggers on pTab, if required */ @@ -70662,15 +74240,6 @@ #endif assert( (pTrigger && tmask) || (pTrigger==0 && tmask==0) ); - /* Ensure that: - * (a) the table is not read-only, - * (b) that if it is a view then ON INSERT triggers exist - */ - if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ - goto insert_cleanup; - } - assert( pTab!=0 ); - /* If pTab is really a view, make sure it has been initialized. ** ViewGetColumnNames() is a no-op if pTab is not a view (or virtual ** module table). @@ -70679,6 +74248,14 @@ goto insert_cleanup; } + /* Ensure that: + * (a) the table is not read-only, + * (b) that if it is a view then ON INSERT triggers exist + */ + if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ + goto insert_cleanup; + } + /* Allocate a VDBE */ v = sqlite3GetVdbe(pParse); @@ -70686,11 +74263,6 @@ if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); sqlite3BeginWriteOperation(pParse, pSelect || pTrigger, iDb); - /* if there are row triggers, allocate a temp table for new.* references. */ - if( pTrigger ){ - newIdx = pParse->nTab++; - } - #ifndef SQLITE_OMIT_XFER_OPT /* If the statement is of the form ** @@ -70778,7 +74350,7 @@ ** of the tables being read by the SELECT statement. Also use a ** temp table in the case of row triggers. */ - if( pTrigger || readsTable(v, addrSelect, iDb, pTab) ){ + if( pTrigger || readsTable(pParse, addrSelect, iDb, pTab) ){ useTempTable = 1; } @@ -70894,12 +74466,6 @@ if( pColumn==0 && nColumn>0 ){ keyColumn = pTab->iPKey; } - - /* Open the temp table for FOR EACH ROW triggers - */ - if( pTrigger ){ - sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol); - } /* Initialize the count of rows to be inserted */ @@ -70966,9 +74532,7 @@ */ endOfLoop = sqlite3VdbeMakeLabel(v); if( tmask & TRIGGER_BEFORE ){ - int regTrigRowid; - int regCols; - int regRec; + int regCols = sqlite3GetTempRange(pParse, pTab->nCol+1); /* build the NEW.* reference row. Note that if there is an INTEGER ** PRIMARY KEY into which a NULL is being inserted, that NULL will be @@ -70976,31 +74540,29 @@ ** we do not know what the unique ID will be (because the insert has ** not happened yet) so we substitute a rowid of -1 */ - regTrigRowid = sqlite3GetTempReg(pParse); if( keyColumn<0 ){ - sqlite3VdbeAddOp2(v, OP_Integer, -1, regTrigRowid); + sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols); }else{ int j1; if( useTempTable ){ - sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regTrigRowid); + sqlite3VdbeAddOp3(v, OP_Column, srcTab, keyColumn, regCols); }else{ assert( pSelect==0 ); /* Otherwise useTempTable is true */ - sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regTrigRowid); + sqlite3ExprCode(pParse, pList->a[keyColumn].pExpr, regCols); } - j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regTrigRowid); - sqlite3VdbeAddOp2(v, OP_Integer, -1, regTrigRowid); + j1 = sqlite3VdbeAddOp1(v, OP_NotNull, regCols); + sqlite3VdbeAddOp2(v, OP_Integer, -1, regCols); sqlite3VdbeJumpHere(v, j1); - sqlite3VdbeAddOp1(v, OP_MustBeInt, regTrigRowid); + sqlite3VdbeAddOp1(v, OP_MustBeInt, regCols); } /* Cannot have triggers on a virtual table. If it were possible, ** this block would have to account for hidden column. */ - assert(!IsVirtual(pTab)); + assert( !IsVirtual(pTab) ); /* Create the new column data */ - regCols = sqlite3GetTempRange(pParse, pTab->nCol); for(i=0; inCol; i++){ if( pColumn==0 ){ j = i; @@ -71010,16 +74572,14 @@ } } if( pColumn && j>=pColumn->nId ){ - sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regCols+i); + sqlite3ExprCode(pParse, pTab->aCol[i].pDflt, regCols+i+1); }else if( useTempTable ){ - sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, regCols+i); + sqlite3VdbeAddOp3(v, OP_Column, srcTab, j, regCols+i+1); }else{ assert( pSelect==0 ); /* Otherwise useTempTable is true */ - sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr, regCols+i); + sqlite3ExprCodeAndCache(pParse, pList->a[j].pExpr, regCols+i+1); } } - regRec = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp3(v, OP_MakeRecord, regCols, pTab->nCol, regRec); /* If this is an INSERT on a view with an INSTEAD OF INSERT trigger, ** do not attempt any conversions before assembling the record. @@ -71027,18 +74587,15 @@ ** table column affinities. */ if( !isView ){ + sqlite3VdbeAddOp2(v, OP_Affinity, regCols+1, pTab->nCol); sqlite3TableAffinityStr(v, pTab); } - sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRec, regTrigRowid); - sqlite3ReleaseTempReg(pParse, regRec); - sqlite3ReleaseTempReg(pParse, regTrigRowid); - sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol); /* Fire BEFORE or INSTEAD OF triggers */ - if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE, - pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){ - goto insert_cleanup; - } + sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_BEFORE, + pTab, regCols-pTab->nCol-1, onError, endOfLoop); + + sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol+1); } /* Push the record number for the new entry onto the stack. The @@ -71134,9 +74691,10 @@ */ #ifndef SQLITE_OMIT_VIRTUALTABLE if( IsVirtual(pTab) ){ + const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); sqlite3VtabMakeWritable(pParse, pTab); - sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, - (const char*)pTab->pVtab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VUpdate, 1, pTab->nCol+2, regIns, pVTab, P4_VTAB); + sqlite3MayAbort(pParse); }else #endif { @@ -71144,9 +74702,9 @@ sqlite3GenerateConstraintChecks(pParse, pTab, baseCur, regIns, aRegIdx, keyColumn>=0, 0, onError, endOfLoop, &isReplace ); + sqlite3FkCheck(pParse, pTab, 0, regIns); sqlite3CompleteInsertion( - pParse, pTab, baseCur, regIns, aRegIdx, 0, - (tmask&TRIGGER_AFTER) ? newIdx : -1, appendFlag, isReplace==0 + pParse, pTab, baseCur, regIns, aRegIdx, 0, appendFlag, isReplace==0 ); } } @@ -71159,10 +74717,8 @@ if( pTrigger ){ /* Code AFTER triggers */ - if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER, - pTab, newIdx, -1, onError, endOfLoop, 0, 0) ){ - goto insert_cleanup; - } + sqlite3CodeRowTrigger(pParse, pTrigger, TK_INSERT, 0, TRIGGER_AFTER, + pTab, regData-2-pTab->nCol, onError, endOfLoop); } /* The bottom of the main insertion loop, if the data source @@ -71191,7 +74747,7 @@ ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ - if( pParse->nested==0 && pParse->trigStack==0 ){ + if( pParse->nested==0 && pParse->pTriggerTab==0 ){ sqlite3AutoincrementEnd(pParse); } @@ -71200,7 +74756,7 @@ ** generating code because of a call to sqlite3NestedParse(), do not ** invoke the callback function. */ - if( db->flags & SQLITE_CountRows && pParse->nested==0 && !pParse->trigStack ){ + if( (db->flags&SQLITE_CountRows) && !pParse->nested && !pParse->pTriggerTab ){ sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows inserted", SQLITE_STATIC); @@ -71214,31 +74770,43 @@ sqlite3DbFree(db, aRegIdx); } +/* Make sure "isView" and other macros defined above are undefined. Otherwise +** thely may interfere with compilation of other functions in this file +** (or in another file, if this file becomes part of the amalgamation). */ +#ifdef isView + #undef isView +#endif +#ifdef pTrigger + #undef pTrigger +#endif +#ifdef tmask + #undef tmask +#endif + + /* ** Generate code to do constraint checks prior to an INSERT or an UPDATE. ** ** The input is a range of consecutive registers as follows: ** -** 1. The rowid of the row to be updated before the update. This -** value is omitted unless we are doing an UPDATE that involves a -** change to the record number or writing to a virtual table. -** -** 2. The rowid of the row after the update. +** 1. The rowid of the row after the update. ** -** 3. The data in the first column of the entry after the update. +** 2. The data in the first column of the entry after the update. ** ** i. Data from middle columns... ** ** N. The data in the last column of the entry after the update. ** -** The regRowid parameter is the index of the register containing (2). +** The regRowid parameter is the index of the register containing (1). ** -** The old rowid shown as entry (1) above is omitted unless both isUpdate -** and rowidChng are 1. isUpdate is true for UPDATEs and false for -** INSERTs. RowidChng means that the new rowid is explicitly specified by -** the update or insert statement. If rowidChng is false, it means that -** the rowid is computed automatically in an insert or that the rowid value -** is not modified by the update. +** If isUpdate is true and rowidChng is non-zero, then rowidChng contains +** the address of a register containing the rowid before the update takes +** place. isUpdate is true for UPDATEs and false for INSERTs. If isUpdate +** is false, indicating an INSERT statement, then a non-zero rowidChng +** indicates that the rowid was explicitly specified as part of the +** INSERT statement. If rowidChng is false, it means that the rowid is +** computed automatically in an insert or that the rowid value is not +** modified by an update. ** ** The code generated by this routine store new index entries into ** registers identified by aRegIdx[]. No index entry is created for @@ -71313,7 +74881,7 @@ int iCur; /* Table cursor number */ Index *pIdx; /* Pointer to one of the indices */ int seenReplace = 0; /* True if REPLACE is used to resolve INT PK conflict */ - int hasTwoRowids = (isUpdate && rowidChng); + int regOldRowid = (rowidChng && isUpdate) ? rowidChng : regRowid; v = sqlite3GetVdbe(pParse); assert( v!=0 ); @@ -71321,7 +74889,6 @@ nCol = pTab->nCol; regData = regRowid + 1; - /* Test all NOT NULL constraints. */ for(i=0; ipIndex ){ - if( isUpdate ){ - j2 = sqlite3VdbeAddOp3(v, OP_Eq, regRowid, 0, regRowid-1); - } - j3 = sqlite3VdbeAddOp3(v, OP_NotExists, baseCur, 0, regRowid); - switch( onError ){ - default: { - onError = OE_Abort; - /* Fall thru into the next case */ - } - case OE_Rollback: - case OE_Abort: - case OE_Fail: { - sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, - "PRIMARY KEY must be unique", P4_STATIC); - break; - } - case OE_Replace: { + if( isUpdate ){ + j2 = sqlite3VdbeAddOp3(v, OP_Eq, regRowid, 0, rowidChng); + } + j3 = sqlite3VdbeAddOp3(v, OP_NotExists, baseCur, 0, regRowid); + switch( onError ){ + default: { + onError = OE_Abort; + /* Fall thru into the next case */ + } + case OE_Rollback: + case OE_Abort: + case OE_Fail: { + sqlite3HaltConstraint( + pParse, onError, "PRIMARY KEY must be unique", P4_STATIC); + break; + } + case OE_Replace: { + /* If there are DELETE triggers on this table and the + ** recursive-triggers flag is set, call GenerateRowDelete() to + ** remove the conflicting row from the the table. This will fire + ** the triggers and remove both the table and index b-tree entries. + ** + ** Otherwise, if there are no triggers or the recursive-triggers + ** flag is not set, call GenerateRowIndexDelete(). This removes + ** the index b-tree entries only. The table b-tree entry will be + ** replaced by the new entry when it is inserted. */ + Trigger *pTrigger = 0; + if( pParse->db->flags&SQLITE_RecTriggers ){ + pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); + } + sqlite3MultiWrite(pParse); + if( pTrigger || sqlite3FkRequired(pParse, pTab, 0, 0) ){ + sqlite3GenerateRowDelete( + pParse, pTab, baseCur, regRowid, 0, pTrigger, OE_Replace + ); + }else{ sqlite3GenerateRowIndexDelete(pParse, pTab, baseCur, 0); - seenReplace = 1; - break; - } - case OE_Ignore: { - assert( seenReplace==0 ); - sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); - break; } + seenReplace = 1; + break; } - sqlite3VdbeJumpHere(v, j3); - if( isUpdate ){ - sqlite3VdbeJumpHere(v, j2); + case OE_Ignore: { + assert( seenReplace==0 ); + sqlite3VdbeAddOp2(v, OP_Goto, 0, ignoreDest); + break; } } + sqlite3VdbeJumpHere(v, j3); + if( isUpdate ){ + sqlite3VdbeJumpHere(v, j2); + } } /* Test all UNIQUE constraints by creating entries for each UNIQUE @@ -71452,7 +75038,7 @@ } sqlite3VdbeAddOp2(v, OP_SCopy, regRowid, regIdx+i); sqlite3VdbeAddOp3(v, OP_MakeRecord, regIdx, pIdx->nColumn+1, aRegIdx[iCur]); - sqlite3IndexAffinityStr(v, pIdx); + sqlite3VdbeChangeP4(v, -1, sqlite3IndexAffinityStr(v, pIdx), 0); sqlite3ExprCacheAffinityChange(pParse, regIdx, pIdx->nColumn+1); /* Find out what action to take in case there is an indexing conflict */ @@ -71471,10 +75057,9 @@ else if( onError==OE_Fail ) onError = OE_Abort; } - /* Check to see if the new index entry will be unique */ regR = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_SCopy, regRowid-hasTwoRowids, regR); + sqlite3VdbeAddOp2(v, OP_SCopy, regOldRowid, regR); j3 = sqlite3VdbeAddOp4(v, OP_IsUnique, baseCur+iCur+1, 0, regR, SQLITE_INT_TO_PTR(regIdx), P4_INT32); @@ -71504,7 +75089,7 @@ sqlite3StrAccumAppend(&errMsg, pIdx->nColumn>1 ? " are not unique" : " is not unique", -1); zErr = sqlite3StrAccumFinish(&errMsg); - sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, zErr, 0); + sqlite3HaltConstraint(pParse, onError, zErr, 0); sqlite3DbFree(errMsg.db, zErr); break; } @@ -71514,8 +75099,15 @@ break; } default: { + Trigger *pTrigger = 0; assert( onError==OE_Replace ); - sqlite3GenerateRowDelete(pParse, pTab, baseCur, regR, 0); + sqlite3MultiWrite(pParse); + if( pParse->db->flags&SQLITE_RecTriggers ){ + pTrigger = sqlite3TriggersExist(pParse, pTab, TK_DELETE, 0, 0); + } + sqlite3GenerateRowDelete( + pParse, pTab, baseCur, regR, 0, pTrigger, OE_Replace + ); seenReplace = 1; break; } @@ -71523,7 +75115,7 @@ sqlite3VdbeJumpHere(v, j3); sqlite3ReleaseTempReg(pParse, regR); } - + if( pbMayReplace ){ *pbMayReplace = seenReplace; } @@ -71545,7 +75137,6 @@ int regRowid, /* Range of content */ int *aRegIdx, /* Register used by each index. 0 for unused indices */ int isUpdate, /* True for UPDATE, False for INSERT */ - int newIdx, /* Index of NEW table for triggers. -1 if none */ int appendBias, /* True if this is likely to be an append */ int useSeekResult /* True to set the USESEEKRESULT flag on OP_[Idx]Insert */ ){ @@ -71573,11 +75164,6 @@ sqlite3VdbeAddOp3(v, OP_MakeRecord, regData, pTab->nCol, regRec); sqlite3TableAffinityStr(v, pTab); sqlite3ExprCacheAffinityChange(pParse, regData, pTab->nCol); -#ifndef SQLITE_OMIT_TRIGGER - if( newIdx>=0 ){ - sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRec, regRowid); - } -#endif if( pParse->nested ){ pik_flags = 0; }else{ @@ -71901,8 +75487,8 @@ if( pDest->iPKey>=0 ){ addr1 = sqlite3VdbeAddOp2(v, OP_Rowid, iSrc, regRowid); addr2 = sqlite3VdbeAddOp3(v, OP_NotExists, iDest, 0, regRowid); - sqlite3VdbeAddOp4(v, OP_Halt, SQLITE_CONSTRAINT, onError, 0, - "PRIMARY KEY must be unique", P4_STATIC); + sqlite3HaltConstraint( + pParse, onError, "PRIMARY KEY must be unique", P4_STATIC); sqlite3VdbeJumpHere(v, addr2); autoIncStep(pParse, regAutoinc, regRowid); }else if( pDest->pIndex==0 ){ @@ -71953,11 +75539,6 @@ } #endif /* SQLITE_OMIT_XFER_OPT */ -/* Make sure "isView" gets undefined in case this file becomes part of -** the amalgamation - so that subsequent files do not see isView as a -** macro. */ -#undef isView - /************** End of insert.c **********************************************/ /************** Begin file legacy.c ******************************************/ /* @@ -71975,8 +75556,6 @@ ** implement the programmer interface to the library. Routines in ** other files are for internal use by SQLite and should not be ** accessed by users of the library. -** -** $Id: legacy.c,v 1.33 2009/05/05 20:02:48 drh Exp $ */ @@ -72093,6 +75672,9 @@ *pzErrMsg = sqlite3Malloc(nErrMsg); if( *pzErrMsg ){ memcpy(*pzErrMsg, sqlite3_errmsg(db), nErrMsg); + }else{ + rc = SQLITE_NOMEM; + sqlite3Error(db, SQLITE_NOMEM, 0); } }else if( pzErrMsg ){ *pzErrMsg = 0; @@ -72118,8 +75700,6 @@ ************************************************************************* ** This file contains code used to dynamically load extensions into ** the SQLite library. -** -** $Id: loadext.c,v 1.60 2009/06/03 01:24:54 drh Exp $ */ #ifndef SQLITE_CORE @@ -72143,8 +75723,6 @@ ** an SQLite instance. Shared libraries that intend to be loaded ** as extensions by SQLite should #include this file instead of ** sqlite3.h. -** -** @(#) $Id: sqlite3ext.h,v 1.25 2008/10/12 00:27:54 shane Exp $ */ #ifndef _SQLITE3EXT_H_ #define _SQLITE3EXT_H_ @@ -73112,8 +76690,6 @@ ** ************************************************************************* ** This file contains code used to implement the PRAGMA command. -** -** $Id: pragma.c,v 1.213 2009/06/19 14:06:03 drh Exp $ */ /* Ignore this whole file if pragmas are disabled @@ -73291,6 +76867,13 @@ /* TODO: Maybe it shouldn't be possible to change the ReadUncommitted ** flag if there are any active statements. */ { "read_uncommitted", SQLITE_ReadUncommitted }, + { "recursive_triggers", SQLITE_RecTriggers }, + + /* This flag may only be set if both foreign-key and trigger support + ** are present in the build. */ +#if !defined(SQLITE_OMIT_FOREIGN_KEY) && !defined(SQLITE_OMIT_TRIGGER) + { "foreign_keys", SQLITE_ForeignKeys }, +#endif }; int i; const struct sPragmaType *p; @@ -73304,10 +76887,17 @@ if( zRight==0 ){ returnSingleInt(pParse, p->zName, (db->flags & p->mask)!=0 ); }else{ + int mask = p->mask; /* Mask of bits to set or clear. */ + if( db->autoCommit==0 ){ + /* Foreign key support may not be enabled or disabled while not + ** in auto-commit mode. */ + mask &= ~(SQLITE_ForeignKeys); + } + if( getBoolean(zRight) ){ - db->flags |= p->mask; + db->flags |= mask; }else{ - db->flags &= ~p->mask; + db->flags &= ~mask; } /* Many of the flag-pragmas modify the code generated by the SQL @@ -73328,17 +76918,20 @@ /* ** Return a human-readable name for a constraint resolution action. */ +#ifndef SQLITE_OMIT_FOREIGN_KEY static const char *actionName(u8 action){ const char *zName; switch( action ){ - case OE_SetNull: zName = "SET NULL"; break; - case OE_SetDflt: zName = "SET DEFAULT"; break; - case OE_Cascade: zName = "CASCADE"; break; - default: zName = "RESTRICT"; - assert( action==OE_Restrict ); break; + case OE_SetNull: zName = "SET NULL"; break; + case OE_SetDflt: zName = "SET DEFAULT"; break; + case OE_Cascade: zName = "CASCADE"; break; + case OE_Restrict: zName = "RESTRICT"; break; + default: zName = "NO ACTION"; + assert( action==OE_None ); break; } return zName; } +#endif /* ** Process a pragma statement. @@ -73419,12 +77012,13 @@ */ if( sqlite3StrICmp(zLeft,"default_cache_size")==0 ){ static const VdbeOpList getCacheSize[] = { - { OP_ReadCookie, 0, 1, BTREE_DEFAULT_CACHE_SIZE}, /* 0 */ - { OP_IfPos, 1, 6, 0}, + { OP_Transaction, 0, 0, 0}, /* 0 */ + { OP_ReadCookie, 0, 1, BTREE_DEFAULT_CACHE_SIZE}, /* 1 */ + { OP_IfPos, 1, 7, 0}, { OP_Integer, 0, 2, 0}, { OP_Subtract, 1, 2, 1}, - { OP_IfPos, 1, 6, 0}, - { OP_Integer, 0, 1, 0}, /* 5 */ + { OP_IfPos, 1, 7, 0}, + { OP_Integer, 0, 1, 0}, /* 6 */ { OP_ResultRow, 1, 1, 0}, }; int addr; @@ -73436,7 +77030,8 @@ pParse->nMem += 2; addr = sqlite3VdbeAddOpList(v, ArraySize(getCacheSize), getCacheSize); sqlite3VdbeChangeP1(v, addr, iDb); - sqlite3VdbeChangeP1(v, addr+5, SQLITE_DEFAULT_CACHE_SIZE); + sqlite3VdbeChangeP1(v, addr+1, iDb); + sqlite3VdbeChangeP1(v, addr+6, SQLITE_DEFAULT_CACHE_SIZE); }else{ int size = atoi(zRight); if( size<0 ) size = -size; @@ -74063,8 +77658,8 @@ int j; for(j=0; jnCol; j++){ char *zCol = pFK->aCol[j].zCol; - char *zOnUpdate = (char *)actionName(pFK->updateConf); - char *zOnDelete = (char *)actionName(pFK->deleteConf); + char *zOnDelete = (char *)actionName(pFK->aAction[0]); + char *zOnUpdate = (char *)actionName(pFK->aAction[1]); sqlite3VdbeAddOp2(v, OP_Integer, i, 1); sqlite3VdbeAddOp2(v, OP_Integer, j, 2); sqlite3VdbeAddOp4(v, OP_String8, 0, 3, 0, pFK->zTo, 0); @@ -74212,6 +77807,7 @@ sqlite3VdbeAddOp2(v, OP_AddImm, 2, 1); /* increment entry count */ for(j=0, pIdx=pTab->pIndex; pIdx; pIdx=pIdx->pNext, j++){ int jmp2; + int r1; static const VdbeOpList idxErr[] = { { OP_AddImm, 1, -1, 0}, { OP_String8, 0, 3, 0}, /* 1 */ @@ -74225,8 +77821,8 @@ { OP_IfPos, 1, 0, 0}, /* 9 */ { OP_Halt, 0, 0, 0}, }; - sqlite3GenerateIndexKey(pParse, pIdx, 1, 3, 1); - jmp2 = sqlite3VdbeAddOp3(v, OP_Found, j+2, 0, 3); + r1 = sqlite3GenerateIndexKey(pParse, pIdx, 1, 3, 0); + jmp2 = sqlite3VdbeAddOp4Int(v, OP_Found, j+2, 0, r1, pIdx->nColumn+1); addr = sqlite3VdbeAddOpList(v, ArraySize(idxErr), idxErr); sqlite3VdbeChangeP4(v, addr+1, "rowid ", P4_STATIC); sqlite3VdbeChangeP4(v, addr+3, " missing from index ", P4_STATIC); @@ -74403,12 +77999,14 @@ }else{ /* Read the specified cookie value */ static const VdbeOpList readCookie[] = { - { OP_ReadCookie, 0, 1, 0}, /* 0 */ + { OP_Transaction, 0, 0, 0}, /* 0 */ + { OP_ReadCookie, 0, 1, 0}, /* 1 */ { OP_ResultRow, 1, 1, 0} }; int addr = sqlite3VdbeAddOpList(v, ArraySize(readCookie), readCookie); sqlite3VdbeChangeP1(v, addr, iDb); - sqlite3VdbeChangeP3(v, addr, iCookie); + sqlite3VdbeChangeP1(v, addr+1, iDb); + sqlite3VdbeChangeP3(v, addr+1, iCookie); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, zLeft, SQLITE_TRANSIENT); } @@ -74531,8 +78129,6 @@ ** This file contains the implementation of the sqlite3_prepare() ** interface, and routines that contribute to loading the database schema ** from disk. -** -** $Id: prepare.c,v 1.125 2009/06/25 11:50:21 drh Exp $ */ /* @@ -74547,11 +78143,11 @@ sqlite3 *db = pData->db; if( !db->mallocFailed && (db->flags & SQLITE_RecoveryMode)==0 ){ if( zObj==0 ) zObj = "?"; - sqlite3SetString(pData->pzErrMsg, pData->db, - "malformed database schema (%s)", zObj); + sqlite3SetString(pData->pzErrMsg, db, + "malformed database schema (%s)", zObj); if( zExtra ){ - *pData->pzErrMsg = sqlite3MAppendf(pData->db, *pData->pzErrMsg, "%s - %s", - *pData->pzErrMsg, zExtra); + *pData->pzErrMsg = sqlite3MAppendf(db, *pData->pzErrMsg, + "%s - %s", *pData->pzErrMsg, zExtra); } } pData->rc = db->mallocFailed ? SQLITE_NOMEM : SQLITE_CORRUPT; @@ -74598,15 +78194,20 @@ assert( db->init.busy ); db->init.iDb = iDb; db->init.newTnum = atoi(argv[1]); + db->init.orphanTrigger = 0; rc = sqlite3_exec(db, argv[2], 0, 0, &zErr); db->init.iDb = 0; assert( rc!=SQLITE_OK || zErr==0 ); if( SQLITE_OK!=rc ){ - pData->rc = rc; - if( rc==SQLITE_NOMEM ){ - db->mallocFailed = 1; - }else if( rc!=SQLITE_INTERRUPT && rc!=SQLITE_LOCKED ){ - corruptSchema(pData, argv[0], zErr); + if( db->init.orphanTrigger ){ + assert( iDb==1 ); + }else{ + pData->rc = rc; + if( rc==SQLITE_NOMEM ){ + db->mallocFailed = 1; + }else if( rc!=SQLITE_INTERRUPT && rc!=SQLITE_LOCKED ){ + corruptSchema(pData, argv[0], zErr); + } } sqlite3DbFree(db, zErr); } @@ -74646,7 +78247,6 @@ static int sqlite3InitOne(sqlite3 *db, int iDb, char **pzErrMsg){ int rc; int i; - BtCursor *curMain; int size; Table *pTab; Db *pDb; @@ -74655,6 +78255,7 @@ InitData initData; char const *zMasterSchema; char const *zMasterName = SCHEMA_TABLE(iDb); + int openedTransaction = 0; /* ** The master database table has a structure like this @@ -74728,14 +78329,19 @@ } return SQLITE_OK; } - curMain = sqlite3MallocZero(sqlite3BtreeCursorSize()); - if( !curMain ){ - rc = SQLITE_NOMEM; - goto error_out; - } + + /* If there is not already a read-only (or read-write) transaction opened + ** on the b-tree database, open one now. If a transaction is opened, it + ** will be closed before this function returns. */ sqlite3BtreeEnter(pDb->pBt); - rc = sqlite3BtreeCursor(pDb->pBt, MASTER_ROOT, 0, 0, curMain); - if( rc==SQLITE_EMPTY ) rc = SQLITE_OK; + if( !sqlite3BtreeIsInReadTrans(pDb->pBt) ){ + rc = sqlite3BtreeBeginTrans(pDb->pBt, 0); + if( rc!=SQLITE_OK ){ + sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc)); + goto initone_error_out; + } + openedTransaction = 1; + } /* Get the database meta information. ** @@ -74754,12 +78360,8 @@ ** Note: The #defined SQLITE_UTF* symbols in sqliteInt.h correspond to ** the possible values of meta[4]. */ - for(i=0; rc==SQLITE_OK && ipBt, i+1, (u32 *)&meta[i]); - } - if( rc ){ - sqlite3SetString(pzErrMsg, db, "%s", sqlite3ErrStr(rc)); - goto initone_error_out; + for(i=0; ipBt, i+1, (u32 *)&meta[i]); } pDb->pSchema->schema_cookie = meta[BTREE_SCHEMA_VERSION-1]; @@ -74874,8 +78476,9 @@ ** before that point, jump to error_out. */ initone_error_out: - sqlite3BtreeCloseCursor(curMain); - sqlite3_free(curMain); + if( openedTransaction ){ + sqlite3BtreeCommit(pDb->pBt); + } sqlite3BtreeLeave(pDb->pBt); error_out: @@ -74953,43 +78556,47 @@ /* ** Check schema cookies in all databases. If any cookie is out -** of date, return 0. If all schema cookies are current, return 1. +** of date set pParse->rc to SQLITE_SCHEMA. If all schema cookies +** make no changes to pParse->rc. */ -static int schemaIsValid(sqlite3 *db){ +static void schemaIsValid(Parse *pParse){ + sqlite3 *db = pParse->db; int iDb; int rc; - BtCursor *curTemp; int cookie; - int allOk = 1; - curTemp = (BtCursor *)sqlite3Malloc(sqlite3BtreeCursorSize()); - if( curTemp ){ - assert( sqlite3_mutex_held(db->mutex) ); - for(iDb=0; allOk && iDbnDb; iDb++){ - Btree *pBt; - pBt = db->aDb[iDb].pBt; - if( pBt==0 ) continue; - memset(curTemp, 0, sqlite3BtreeCursorSize()); - rc = sqlite3BtreeCursor(pBt, MASTER_ROOT, 0, 0, curTemp); - if( rc==SQLITE_OK ){ - rc = sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie); - if( ALWAYS(rc==SQLITE_OK) - && cookie!=db->aDb[iDb].pSchema->schema_cookie ){ - allOk = 0; - } - sqlite3BtreeCloseCursor(curTemp); - } - if( NEVER(rc==SQLITE_NOMEM) || rc==SQLITE_IOERR_NOMEM ){ + assert( pParse->checkSchema ); + assert( sqlite3_mutex_held(db->mutex) ); + for(iDb=0; iDbnDb; iDb++){ + int openedTransaction = 0; /* True if a transaction is opened */ + Btree *pBt = db->aDb[iDb].pBt; /* Btree database to read cookie from */ + if( pBt==0 ) continue; + + /* If there is not already a read-only (or read-write) transaction opened + ** on the b-tree database, open one now. If a transaction is opened, it + ** will be closed immediately after reading the meta-value. */ + if( !sqlite3BtreeIsInReadTrans(pBt) ){ + rc = sqlite3BtreeBeginTrans(pBt, 0); + if( rc==SQLITE_NOMEM || rc==SQLITE_IOERR_NOMEM ){ db->mallocFailed = 1; } + if( rc!=SQLITE_OK ) return; + openedTransaction = 1; } - sqlite3_free(curTemp); - }else{ - allOk = 0; - db->mallocFailed = 1; - } - return allOk; + /* Read the schema cookie from the database. If it does not match the + ** value stored as part of the in-memory schema representation, + ** set Parse.rc to SQLITE_SCHEMA. */ + sqlite3BtreeGetMeta(pBt, BTREE_SCHEMA_VERSION, (u32 *)&cookie); + if( cookie!=db->aDb[iDb].pSchema->schema_cookie ){ + pParse->rc = SQLITE_SCHEMA; + } + + /* Close the transaction, if one was opened. */ + if( openedTransaction ){ + sqlite3BtreeCommit(pBt); + } + } } /* @@ -75032,6 +78639,7 @@ const char *zSql, /* UTF-8 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ int saveSqlFlag, /* True to copy SQL text into the sqlite3_stmt */ + Vdbe *pReprepare, /* VM being reprepared */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const char **pzTail /* OUT: End of parsed string */ ){ @@ -75046,6 +78654,7 @@ rc = SQLITE_NOMEM; goto end_prepare; } + pParse->pReprepare = pReprepare; if( sqlite3SafetyOn(db) ){ rc = SQLITE_MISUSE; @@ -75093,6 +78702,7 @@ } } + sqlite3VtabUnlockList(db); pParse->db = db; if( nBytes>=0 && (nBytes==0 || zSql[nBytes-1]!=0) ){ @@ -75122,8 +78732,8 @@ pParse->rc = SQLITE_NOMEM; } if( pParse->rc==SQLITE_DONE ) pParse->rc = SQLITE_OK; - if( pParse->checkSchema && !schemaIsValid(db) ){ - pParse->rc = SQLITE_SCHEMA; + if( pParse->checkSchema ){ + schemaIsValid(pParse); } if( pParse->rc==SQLITE_SCHEMA ){ sqlite3ResetInternalSchema(db, 0); @@ -75182,6 +78792,14 @@ sqlite3Error(db, rc, 0); } + /* Delete any TriggerPrg structures allocated while parsing this statement. */ + while( pParse->pTriggerPrg ){ + TriggerPrg *pT = pParse->pTriggerPrg; + pParse->pTriggerPrg = pT->pNext; + sqlite3VdbeProgramDelete(db, pT->pProgram, 0); + sqlite3DbFree(db, pT); + } + end_prepare: sqlite3StackFree(db, pParse); @@ -75194,6 +78812,7 @@ const char *zSql, /* UTF-8 encoded SQL statement. */ int nBytes, /* Length of zSql in bytes. */ int saveSqlFlag, /* True to copy SQL text into the sqlite3_stmt */ + Vdbe *pOld, /* VM being reprepared */ sqlite3_stmt **ppStmt, /* OUT: A pointer to the prepared statement */ const char **pzTail /* OUT: End of parsed string */ ){ @@ -75205,10 +78824,10 @@ } sqlite3_mutex_enter(db->mutex); sqlite3BtreeEnterAll(db); - rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, ppStmt, pzTail); + rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, pOld, ppStmt, pzTail); if( rc==SQLITE_SCHEMA ){ sqlite3_finalize(*ppStmt); - rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, ppStmt, pzTail); + rc = sqlite3Prepare(db, zSql, nBytes, saveSqlFlag, pOld, ppStmt, pzTail); } sqlite3BtreeLeaveAll(db); sqlite3_mutex_leave(db->mutex); @@ -75234,7 +78853,7 @@ assert( zSql!=0 ); /* Reprepare only called for prepare_v2() statements */ db = sqlite3VdbeDb(p); assert( sqlite3_mutex_held(db->mutex) ); - rc = sqlite3LockAndPrepare(db, zSql, -1, 0, &pNew, 0); + rc = sqlite3LockAndPrepare(db, zSql, -1, 0, p, &pNew, 0); if( rc ){ if( rc==SQLITE_NOMEM ){ db->mallocFailed = 1; @@ -75268,7 +78887,7 @@ const char **pzTail /* OUT: End of parsed string */ ){ int rc; - rc = sqlite3LockAndPrepare(db,zSql,nBytes,0,ppStmt,pzTail); + rc = sqlite3LockAndPrepare(db,zSql,nBytes,0,0,ppStmt,pzTail); assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ return rc; } @@ -75280,7 +78899,7 @@ const char **pzTail /* OUT: End of parsed string */ ){ int rc; - rc = sqlite3LockAndPrepare(db,zSql,nBytes,1,ppStmt,pzTail); + rc = sqlite3LockAndPrepare(db,zSql,nBytes,1,0,ppStmt,pzTail); assert( rc==SQLITE_OK || ppStmt==0 || *ppStmt==0 ); /* VERIFY: F13021 */ return rc; } @@ -75314,7 +78933,7 @@ sqlite3_mutex_enter(db->mutex); zSql8 = sqlite3Utf16to8(db, zSql, nBytes); if( zSql8 ){ - rc = sqlite3LockAndPrepare(db, zSql8, -1, saveSqlFlag, ppStmt, &zTail8); + rc = sqlite3LockAndPrepare(db, zSql8, -1, saveSqlFlag, 0, ppStmt, &zTail8); } if( zTail8 && pzTail ){ @@ -75382,8 +79001,6 @@ ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle SELECT statements in SQLite. -** -** $Id: select.c,v 1.524 2009/06/12 03:27:27 drh Exp $ */ @@ -75562,51 +79179,80 @@ } /* -** Create an expression node for an identifier with the name of zName -*/ -SQLITE_PRIVATE Expr *sqlite3CreateIdExpr(Parse *pParse, const char *zName){ - return sqlite3Expr(pParse->db, TK_ID, zName); +** Search the first N tables in pSrc, from left to right, looking for a +** table that has a column named zCol. +** +** When found, set *piTab and *piCol to the table index and column index +** of the matching column and return TRUE. +** +** If not found, return FALSE. +*/ +static int tableAndColumnIndex( + SrcList *pSrc, /* Array of tables to search */ + int N, /* Number of tables in pSrc->a[] to search */ + const char *zCol, /* Name of the column we are looking for */ + int *piTab, /* Write index of pSrc->a[] here */ + int *piCol /* Write index of pSrc->a[*piTab].pTab->aCol[] here */ +){ + int i; /* For looping over tables in pSrc */ + int iCol; /* Index of column matching zCol */ + + assert( (piTab==0)==(piCol==0) ); /* Both or neither are NULL */ + for(i=0; ia[i].pTab, zCol); + if( iCol>=0 ){ + if( piTab ){ + *piTab = i; + *piCol = iCol; + } + return 1; + } + } + return 0; } /* -** Add a term to the WHERE expression in *ppExpr that requires the -** zCol column to be equal in the two tables pTab1 and pTab2. +** This function is used to add terms implied by JOIN syntax to the +** WHERE clause expression of a SELECT statement. The new term, which +** is ANDed with the existing WHERE clause, is of the form: +** +** (tab1.col1 = tab2.col2) +** +** where tab1 is the iSrc'th table in SrcList pSrc and tab2 is the +** (iSrc+1)'th. Column col1 is column iColLeft of tab1, and col2 is +** column iColRight of tab2. */ static void addWhereTerm( - Parse *pParse, /* Parsing context */ - const char *zCol, /* Name of the column */ - const Table *pTab1, /* First table */ - const char *zAlias1, /* Alias for first table. May be NULL */ - const Table *pTab2, /* Second table */ - const char *zAlias2, /* Alias for second table. May be NULL */ - int iRightJoinTable, /* VDBE cursor for the right table */ - Expr **ppExpr, /* Add the equality term to this expression */ - int isOuterJoin /* True if dealing with an OUTER join */ -){ - Expr *pE1a, *pE1b, *pE1c; - Expr *pE2a, *pE2b, *pE2c; - Expr *pE; - - pE1a = sqlite3CreateIdExpr(pParse, zCol); - pE2a = sqlite3CreateIdExpr(pParse, zCol); - if( zAlias1==0 ){ - zAlias1 = pTab1->zName; - } - pE1b = sqlite3CreateIdExpr(pParse, zAlias1); - if( zAlias2==0 ){ - zAlias2 = pTab2->zName; - } - pE2b = sqlite3CreateIdExpr(pParse, zAlias2); - pE1c = sqlite3PExpr(pParse, TK_DOT, pE1b, pE1a, 0); - pE2c = sqlite3PExpr(pParse, TK_DOT, pE2b, pE2a, 0); - pE = sqlite3PExpr(pParse, TK_EQ, pE1c, pE2c, 0); - if( pE && isOuterJoin ){ - ExprSetProperty(pE, EP_FromJoin); - assert( !ExprHasAnyProperty(pE, EP_TokenOnly|EP_Reduced) ); - ExprSetIrreducible(pE); - pE->iRightJoinTable = (i16)iRightJoinTable; + Parse *pParse, /* Parsing context */ + SrcList *pSrc, /* List of tables in FROM clause */ + int iLeft, /* Index of first table to join in pSrc */ + int iColLeft, /* Index of column in first table */ + int iRight, /* Index of second table in pSrc */ + int iColRight, /* Index of column in second table */ + int isOuterJoin, /* True if this is an OUTER join */ + Expr **ppWhere /* IN/OUT: The WHERE clause to add to */ +){ + sqlite3 *db = pParse->db; + Expr *pE1; + Expr *pE2; + Expr *pEq; + + assert( iLeftnSrc>iRight ); + assert( pSrc->a[iLeft].pTab ); + assert( pSrc->a[iRight].pTab ); + + pE1 = sqlite3CreateColumnExpr(db, pSrc, iLeft, iColLeft); + pE2 = sqlite3CreateColumnExpr(db, pSrc, iRight, iColRight); + + pEq = sqlite3PExpr(pParse, TK_EQ, pE1, pE2, 0); + if( pEq && isOuterJoin ){ + ExprSetProperty(pEq, EP_FromJoin); + assert( !ExprHasAnyProperty(pEq, EP_TokenOnly|EP_Reduced) ); + ExprSetIrreducible(pEq); + pEq->iRightJoinTable = (i16)pE2->iTable; } - *ppExpr = sqlite3ExprAnd(pParse->db,*ppExpr, pE); + *ppWhere = sqlite3ExprAnd(db, *ppWhere, pEq); } /* @@ -75686,13 +79332,15 @@ "an ON or USING clause", 0); return 1; } - for(j=0; jnCol; j++){ - char *zName = pLeftTab->aCol[j].zName; - if( columnIndex(pRightTab, zName)>=0 ){ - addWhereTerm(pParse, zName, pLeftTab, pLeft->zAlias, - pRightTab, pRight->zAlias, - pRight->iCursor, &p->pWhere, isOuter); - + for(j=0; jnCol; j++){ + char *zName; /* Name of column in the right table */ + int iLeft; /* Matching left table */ + int iLeftCol; /* Matching column in the left table */ + + zName = pRightTab->aCol[j].zName; + if( tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol) ){ + addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, j, + isOuter, &p->pWhere); } } } @@ -75724,15 +79372,22 @@ if( pRight->pUsing ){ IdList *pList = pRight->pUsing; for(j=0; jnId; j++){ - char *zName = pList->a[j].zName; - if( columnIndex(pLeftTab, zName)<0 || columnIndex(pRightTab, zName)<0 ){ + char *zName; /* Name of the term in the USING clause */ + int iLeft; /* Table on the left with matching column name */ + int iLeftCol; /* Column number of matching column on the left */ + int iRightCol; /* Column number of matching column on the right */ + + zName = pList->a[j].zName; + iRightCol = columnIndex(pRightTab, zName); + if( iRightCol<0 + || !tableAndColumnIndex(pSrc, i+1, zName, &iLeft, &iLeftCol) + ){ sqlite3ErrorMsg(pParse, "cannot join using column %s - column " "not present in both tables", zName); return 1; } - addWhereTerm(pParse, zName, pLeftTab, pLeft->zAlias, - pRightTab, pRight->zAlias, - pRight->iCursor, &p->pWhere, isOuter); + addWhereTerm(pParse, pSrc, iLeft, iLeftCol, i+1, iRightCol, + isOuter, &p->pWhere); } } } @@ -75819,8 +79474,8 @@ v = pParse->pVdbe; r1 = sqlite3GetTempReg(pParse); + sqlite3VdbeAddOp4Int(v, OP_Found, iTab, addrRepeat, iMem, N); sqlite3VdbeAddOp3(v, OP_MakeRecord, iMem, N, r1); - sqlite3VdbeAddOp3(v, OP_Found, iTab, addrRepeat, r1); sqlite3VdbeAddOp2(v, OP_IdxInsert, iTab, r1); sqlite3ReleaseTempReg(pParse, r1); } @@ -76061,8 +79716,7 @@ if( p->iLimit ){ assert( pOrderBy==0 ); /* If there is an ORDER BY, the call to ** pushOntoSorter() would have cleared p->iLimit */ - sqlite3VdbeAddOp2(v, OP_AddImm, p->iLimit, -1); - sqlite3VdbeAddOp2(v, OP_IfZero, p->iLimit, iBreak); + sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); } } @@ -76136,14 +79790,16 @@ int regRowid; iTab = pOrderBy->iECursor; + regRow = sqlite3GetTempReg(pParse); if( eDest==SRT_Output || eDest==SRT_Coroutine ){ pseudoTab = pParse->nTab++; - sqlite3VdbeAddOp3(v, OP_OpenPseudo, pseudoTab, eDest==SRT_Output, nColumn); + sqlite3VdbeAddOp3(v, OP_OpenPseudo, pseudoTab, regRow, nColumn); + regRowid = 0; + }else{ + regRowid = sqlite3GetTempReg(pParse); } addr = 1 + sqlite3VdbeAddOp2(v, OP_Sort, iTab, addrBreak); codeOffset(v, p, addrContinue); - regRow = sqlite3GetTempReg(pParse); - regRowid = sqlite3GetTempReg(pParse); sqlite3VdbeAddOp3(v, OP_Column, iTab, pOrderBy->nExpr + 1, regRow); switch( eDest ){ case SRT_Table: @@ -76175,11 +79831,12 @@ assert( eDest==SRT_Output || eDest==SRT_Coroutine ); testcase( eDest==SRT_Output ); testcase( eDest==SRT_Coroutine ); - sqlite3VdbeAddOp2(v, OP_Integer, 1, regRowid); - sqlite3VdbeAddOp3(v, OP_Insert, pseudoTab, regRow, regRowid); for(i=0; iiMem+i ); sqlite3VdbeAddOp3(v, OP_Column, pseudoTab, i, pDest->iMem+i); + if( i==0 ){ + sqlite3VdbeChangeP5(v, OPFLAG_CLEARCACHE); + } } if( eDest==SRT_Output ){ sqlite3VdbeAddOp2(v, OP_ResultRow, pDest->iMem, nColumn); @@ -76263,27 +79920,33 @@ } if( pTab==0 ){ - /* FIX ME: - ** This can occurs if you have something like "SELECT new.x;" inside - ** a trigger. In other words, if you reference the special "new" - ** table in the result set of a select. We do not have a good way - ** to find the actual table type, so call it "TEXT". This is really - ** something of a bug, but I do not know how to fix it. + /* At one time, code such as "SELECT new.x" within a trigger would + ** cause this condition to run. Since then, we have restructured how + ** trigger code is generated and so this condition is no longer + ** possible. However, it can still be true for statements like + ** the following: ** - ** This code does not produce the correct answer - it just prevents - ** a segfault. See ticket #1229. - */ - zType = "TEXT"; + ** CREATE TABLE t1(col INTEGER); + ** SELECT (SELECT t1.col) FROM FROM t1; + ** + ** when columnType() is called on the expression "t1.col" in the + ** sub-select. In this case, set the column type to NULL, even + ** though it should really be "INTEGER". + ** + ** This is not a problem, as the column type of "t1.col" is never + ** used. When columnType() is called on the expression + ** "(SELECT t1.col)", the correct type is returned (see the TK_SELECT + ** branch below. */ break; } - assert( pTab ); + assert( pTab && pExpr->pTab==pTab ); if( pS ){ /* The "table" is actually a sub-select or a view in the FROM clause ** of the SELECT statement. Return the declaration type and origin ** data for the result-set column of the sub-select. */ - if( ALWAYS(iCol>=0 && iColpEList->nExpr) ){ + if( iCol>=0 && ALWAYS(iColpEList->nExpr) ){ /* If iCol is less than zero, then the expression requests the ** rowid of the sub-select or view. This expression is legal (see ** test case misc2.2.2) - it always evaluates to NULL. @@ -76291,7 +79954,7 @@ NameContext sNC; Expr *p = pS->pEList->a[iCol].pExpr; sNC.pSrcList = pS->pSrc; - sNC.pNext = 0; + sNC.pNext = pNC; sNC.pParse = pNC->pParse; zType = columnType(&sNC, p, &zOriginDb, &zOriginTab, &zOriginCol); } @@ -76679,7 +80342,7 @@ Vdbe *v = 0; int iLimit = 0; int iOffset; - int addr1; + int addr1, n; if( p->iLimit ) return; /* @@ -76694,10 +80357,18 @@ p->iLimit = iLimit = ++pParse->nMem; v = sqlite3GetVdbe(pParse); if( NEVER(v==0) ) return; /* VDBE should have already been allocated */ - sqlite3ExprCode(pParse, p->pLimit, iLimit); - sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit); - VdbeComment((v, "LIMIT counter")); - sqlite3VdbeAddOp2(v, OP_IfZero, iLimit, iBreak); + if( sqlite3ExprIsInteger(p->pLimit, &n) ){ + sqlite3VdbeAddOp2(v, OP_Integer, n, iLimit); + VdbeComment((v, "LIMIT counter")); + if( n==0 ){ + sqlite3VdbeAddOp2(v, OP_Goto, 0, iBreak); + } + }else{ + sqlite3ExprCode(pParse, p->pLimit, iLimit); + sqlite3VdbeAddOp1(v, OP_MustBeInt, iLimit); + VdbeComment((v, "LIMIT counter")); + sqlite3VdbeAddOp2(v, OP_IfZero, iLimit, iBreak); + } if( p->pOffset ){ p->iOffset = iOffset = ++pParse->nMem; pParse->nMem++; /* Allocate an extra register for limit+offset */ @@ -77033,7 +80704,7 @@ sqlite3VdbeAddOp2(v, OP_Rewind, tab1, iBreak); r1 = sqlite3GetTempReg(pParse); iStart = sqlite3VdbeAddOp2(v, OP_RowKey, tab1, r1); - sqlite3VdbeAddOp3(v, OP_NotFound, tab2, iCont, r1); + sqlite3VdbeAddOp4Int(v, OP_NotFound, tab2, iCont, r1, 0); sqlite3ReleaseTempReg(pParse, r1); selectInnerLoop(pParse, p, p->pEList, tab1, p->pEList->nExpr, 0, -1, &dest, iCont, iBreak); @@ -77252,8 +80923,7 @@ /* Jump to the end of the loop if the LIMIT is reached. */ if( p->iLimit ){ - sqlite3VdbeAddOp2(v, OP_AddImm, p->iLimit, -1); - sqlite3VdbeAddOp2(v, OP_IfZero, p->iLimit, iBreak); + sqlite3VdbeAddOp3(v, OP_IfZero, p->iLimit, iBreak, -1); } /* Generate the subroutine return @@ -77725,6 +81395,9 @@ assert( pEList!=0 && pExpr->iColumnnExpr ); assert( pExpr->pLeft==0 && pExpr->pRight==0 ); pNew = sqlite3ExprDup(db, pEList->a[pExpr->iColumn].pExpr, 0); + if( pNew && pExpr->pColl ){ + pNew->pColl = pExpr->pColl; + } sqlite3ExprDelete(db, pExpr); pExpr = pNew; } @@ -77834,7 +81507,7 @@ ** ** (11) The subquery and the outer query do not both have ORDER BY clauses. ** -** (12) Not implemented. Subsumed into restriction (3). Was previously +** (**) Not implemented. Subsumed into restriction (3). Was previously ** a separate restriction deriving from ticket #350. ** ** (13) The subquery and outer query do not both use LIMIT @@ -77908,6 +81581,7 @@ */ assert( p!=0 ); assert( p->pPrior==0 ); /* Unable to flatten compound queries */ + if( db->flags & SQLITE_QueryFlattener ) return 0; pSrc = p->pSrc; assert( pSrc && iFrom>=0 && iFromnSrc ); pSubitem = &pSrc->a[iFrom]; @@ -78101,8 +81775,9 @@ if( ALWAYS(pSubitem->pTab!=0) ){ Table *pTabToDel = pSubitem->pTab; if( pTabToDel->nRef==1 ){ - pTabToDel->pNextZombie = pParse->pZombieTab; - pParse->pZombieTab = pTabToDel; + Parse *pToplevel = sqlite3ParseToplevel(pParse); + pTabToDel->pNextZombie = pToplevel->pZombieTab; + pToplevel->pZombieTab = pTabToDel; }else{ pTabToDel->nRef--; } @@ -78530,14 +82205,14 @@ } if( i>0 && zTName==0 ){ - struct SrcList_item *pLeft = &pTabList->a[i-1]; - if( (pLeft[1].jointype & JT_NATURAL)!=0 && - columnIndex(pLeft->pTab, zName)>=0 ){ + if( (pFrom->jointype & JT_NATURAL)!=0 + && tableAndColumnIndex(pTabList, i, zName, 0, 0) + ){ /* In a NATURAL join, omit the join columns from the - ** table on the right */ + ** table to the right of the join */ continue; } - if( sqlite3IdListIndex(pLeft[1].pUsing, zName)>=0 ){ + if( sqlite3IdListIndex(pFrom->pUsing, zName)>=0 ){ /* In a join with a USING clause, omit columns in the ** using clause from the table on the right. */ continue; @@ -78804,8 +82479,8 @@ sqlite3VdbeAddOp4(v, OP_AggStep, 0, regAgg, pF->iMem, (void*)pF->pFunc, P4_FUNCDEF); sqlite3VdbeChangeP5(v, (u8)nArg); - sqlite3ReleaseTempRange(pParse, regAgg, nArg); sqlite3ExprCacheAffinityChange(pParse, regAgg, nArg); + sqlite3ReleaseTempRange(pParse, regAgg, nArg); if( addrNext ){ sqlite3VdbeResolveLabel(v, addrNext); sqlite3ExprCacheClear(pParse); @@ -79231,7 +82906,7 @@ int r2; r2 = sqlite3ExprCodeGetColumn(pParse, - pCol->pTab, pCol->iColumn, pCol->iTable, r1, 0); + pCol->pTab, pCol->iColumn, pCol->iTable, r1); if( r1!=r2 ){ sqlite3VdbeAddOp2(v, OP_SCopy, r2, r1); } @@ -79617,8 +83292,6 @@ ** ** These routines are in a separate files so that they will not be linked ** if they are not used. -** -** $Id: table.c,v 1.40 2009/04/10 14:28:00 drh Exp $ */ #ifndef SQLITE_OMIT_GET_TABLE @@ -79809,9 +83482,7 @@ ** May you share freely, never taking more than you give. ** ************************************************************************* -** -** -** $Id: trigger.c,v 1.141 2009/05/28 01:00:55 drh Exp $ +** This file contains the implementation for TRIGGERs */ #ifndef SQLITE_OMIT_TRIGGER @@ -79850,6 +83521,10 @@ Schema * const pTmpSchema = pParse->db->aDb[1].pSchema; Trigger *pList = 0; /* List of triggers to return */ + if( pParse->disableTriggers ){ + return 0; + } + if( pTmpSchema!=pTab->pSchema ){ HashElem *p; for(p=sqliteHashFirst(&pTmpSchema->trigHash); p; p=sqliteHashNext(p)){ @@ -79886,14 +83561,14 @@ int isTemp, /* True if the TEMPORARY keyword is present */ int noErr /* Suppress errors if the trigger already exists */ ){ - Trigger *pTrigger = 0; - Table *pTab; + Trigger *pTrigger = 0; /* The new trigger */ + Table *pTab; /* Table that the trigger fires off of */ char *zName = 0; /* Name of the trigger */ - sqlite3 *db = pParse->db; + sqlite3 *db = pParse->db; /* The database connection */ int iDb; /* The database to store the trigger in */ Token *pName; /* The unqualified db name */ - DbFixer sFix; - int iTabDb; + DbFixer sFix; /* State vector for the DB fixer */ + int iTabDb; /* Index of the database holding pTab */ assert( pName1!=0 ); /* pName1->z might be NULL, but not pName1 itself */ assert( pName2!=0 ); @@ -79938,6 +83613,17 @@ pTab = sqlite3SrcListLookup(pParse, pTableName); if( !pTab ){ /* The table does not exist. */ + if( db->init.iDb==1 ){ + /* Ticket #3810. + ** Normally, whenever a table is dropped, all associated triggers are + ** dropped too. But if a TEMP trigger is created on a non-TEMP table + ** and the table is dropped by a different database connection, the + ** trigger is not visible to the database connection that does the + ** drop so the trigger cannot be dropped. This results in an + ** "orphaned trigger" - a trigger whose associated table is missing. + */ + db->init.orphanTrigger = 1; + } goto trigger_cleanup; } if( IsVirtual(pTab) ){ @@ -80008,7 +83694,7 @@ /* Build the Trigger object */ pTrigger = (Trigger*)sqlite3DbMallocZero(db, sizeof(Trigger)); if( pTrigger==0 ) goto trigger_cleanup; - pTrigger->name = zName; + pTrigger->zName = zName; zName = 0; pTrigger->table = sqlite3DbStrDup(db, pTableName->a[0].zName); pTrigger->pSchema = db->aDb[iDb].pSchema; @@ -80051,14 +83737,14 @@ pTrig = pParse->pNewTrigger; pParse->pNewTrigger = 0; if( NEVER(pParse->nErr) || !pTrig ) goto triggerfinish_cleanup; - zName = pTrig->name; + zName = pTrig->zName; iDb = sqlite3SchemaToIndex(pParse->db, pTrig->pSchema); pTrig->step_list = pStepList; while( pStepList ){ pStepList->pTrig = pTrig; pStepList = pStepList->pNext; } - nameToken.z = pTrig->name; + nameToken.z = pTrig->zName; nameToken.n = sqlite3Strlen30(nameToken.z); if( sqlite3FixInit(&sFix, pParse, iDb, "trigger", &nameToken) && sqlite3FixTriggerStep(&sFix, pTrig->step_list) ){ @@ -80137,7 +83823,7 @@ */ static TriggerStep *triggerStepAllocate( sqlite3 *db, /* Database connection */ - int op, /* Trigger opcode */ + u8 op, /* Trigger opcode */ Token *pName /* The target name */ ){ TriggerStep *pTriggerStep; @@ -80166,7 +83852,7 @@ IdList *pColumn, /* List of columns in pTableName to insert into */ ExprList *pEList, /* The VALUE clause: a list of values to be inserted */ Select *pSelect, /* A SELECT statement that supplies values */ - int orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */ + u8 orconf /* The conflict algorithm (OE_Abort, OE_Replace, etc.) */ ){ TriggerStep *pTriggerStep; @@ -80198,7 +83884,7 @@ Token *pTableName, /* Name of the table to be updated */ ExprList *pEList, /* The SET clause: list of column and new values */ Expr *pWhere, /* The WHERE clause */ - int orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */ + u8 orconf /* The conflict algorithm. (OE_Abort, OE_Ignore, etc) */ ){ TriggerStep *pTriggerStep; @@ -80240,7 +83926,7 @@ SQLITE_PRIVATE void sqlite3DeleteTrigger(sqlite3 *db, Trigger *pTrigger){ if( pTrigger==0 ) return; sqlite3DeleteTriggerStep(db, pTrigger->step_list); - sqlite3DbFree(db, pTrigger->name); + sqlite3DbFree(db, pTrigger->zName); sqlite3DbFree(db, pTrigger->table); sqlite3ExprDelete(db, pTrigger->pWhen); sqlite3IdListDelete(db, pTrigger->pColumns); @@ -80320,7 +84006,7 @@ const char *zDb = db->aDb[iDb].zName; const char *zTab = SCHEMA_TABLE(iDb); if( iDb==1 ) code = SQLITE_DROP_TEMP_TRIGGER; - if( sqlite3AuthCheck(pParse, code, pTrigger->name, pTable->zName, zDb) || + if( sqlite3AuthCheck(pParse, code, pTrigger->zName, pTable->zName, zDb) || sqlite3AuthCheck(pParse, SQLITE_DELETE, zTab, 0, zDb) ){ return; } @@ -80347,11 +84033,11 @@ sqlite3BeginWriteOperation(pParse, 0, iDb); sqlite3OpenMasterTable(pParse, iDb); base = sqlite3VdbeAddOpList(v, ArraySize(dropTrigger), dropTrigger); - sqlite3VdbeChangeP4(v, base+1, pTrigger->name, 0); + sqlite3VdbeChangeP4(v, base+1, pTrigger->zName, 0); sqlite3VdbeChangeP4(v, base+4, "trigger", P4_STATIC); sqlite3ChangeCookie(pParse, iDb); sqlite3VdbeAddOp2(v, OP_Close, 0, 0); - sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->name, 0); + sqlite3VdbeAddOp4(v, OP_DropTrigger, iDb, 0, 0, pTrigger->zName, 0); if( pParse->nMem<3 ){ pParse->nMem = 3; } @@ -80455,209 +84141,425 @@ } /* -** Generate VDBE code for zero or more statements inside the body of a -** trigger. +** Generate VDBE code for the statements inside the body of a single +** trigger. */ static int codeTriggerProgram( Parse *pParse, /* The parser context */ TriggerStep *pStepList, /* List of statements inside the trigger body */ - int orconfin /* Conflict algorithm. (OE_Abort, etc) */ + int orconf /* Conflict algorithm. (OE_Abort, etc) */ ){ - TriggerStep * pTriggerStep = pStepList; - int orconf; + TriggerStep *pStep; Vdbe *v = pParse->pVdbe; sqlite3 *db = pParse->db; - assert( pTriggerStep!=0 ); + assert( pParse->pTriggerTab && pParse->pToplevel ); + assert( pStepList ); assert( v!=0 ); - sqlite3VdbeAddOp2(v, OP_ContextPush, 0, 0); - VdbeComment((v, "begin trigger %s", pStepList->pTrig->name)); - while( pTriggerStep ){ - sqlite3ExprCacheClear(pParse); - orconf = (orconfin == OE_Default)?pTriggerStep->orconf:orconfin; - pParse->trigStack->orconf = orconf; - switch( pTriggerStep->op ){ + for(pStep=pStepList; pStep; pStep=pStep->pNext){ + /* Figure out the ON CONFLICT policy that will be used for this step + ** of the trigger program. If the statement that caused this trigger + ** to fire had an explicit ON CONFLICT, then use it. Otherwise, use + ** the ON CONFLICT policy that was specified as part of the trigger + ** step statement. Example: + ** + ** CREATE TRIGGER AFTER INSERT ON t1 BEGIN; + ** INSERT OR REPLACE INTO t2 VALUES(new.a, new.b); + ** END; + ** + ** INSERT INTO t1 ... ; -- insert into t2 uses REPLACE policy + ** INSERT OR IGNORE INTO t1 ... ; -- insert into t2 uses IGNORE policy + */ + pParse->eOrconf = (orconf==OE_Default)?pStep->orconf:(u8)orconf; + + switch( pStep->op ){ case TK_UPDATE: { - SrcList *pSrc; - pSrc = targetSrcList(pParse, pTriggerStep); - sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0); - sqlite3Update(pParse, pSrc, - sqlite3ExprListDup(db, pTriggerStep->pExprList, 0), - sqlite3ExprDup(db, pTriggerStep->pWhere, 0), orconf); - sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0); + sqlite3Update(pParse, + targetSrcList(pParse, pStep), + sqlite3ExprListDup(db, pStep->pExprList, 0), + sqlite3ExprDup(db, pStep->pWhere, 0), + pParse->eOrconf + ); break; } case TK_INSERT: { - SrcList *pSrc; - pSrc = targetSrcList(pParse, pTriggerStep); - sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0); - sqlite3Insert(pParse, pSrc, - sqlite3ExprListDup(db, pTriggerStep->pExprList, 0), - sqlite3SelectDup(db, pTriggerStep->pSelect, 0), - sqlite3IdListDup(db, pTriggerStep->pIdList), orconf); - sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0); + sqlite3Insert(pParse, + targetSrcList(pParse, pStep), + sqlite3ExprListDup(db, pStep->pExprList, 0), + sqlite3SelectDup(db, pStep->pSelect, 0), + sqlite3IdListDup(db, pStep->pIdList), + pParse->eOrconf + ); break; } case TK_DELETE: { - SrcList *pSrc; - sqlite3VdbeAddOp2(v, OP_ResetCount, 0, 0); - pSrc = targetSrcList(pParse, pTriggerStep); - sqlite3DeleteFrom(pParse, pSrc, - sqlite3ExprDup(db, pTriggerStep->pWhere, 0)); - sqlite3VdbeAddOp2(v, OP_ResetCount, 1, 0); - break; - } - default: assert( pTriggerStep->op==TK_SELECT ); { - Select *ss = sqlite3SelectDup(db, pTriggerStep->pSelect, 0); - if( ss ){ - SelectDest dest; - - sqlite3SelectDestInit(&dest, SRT_Discard, 0); - sqlite3Select(pParse, ss, &dest); - sqlite3SelectDelete(db, ss); - } + sqlite3DeleteFrom(pParse, + targetSrcList(pParse, pStep), + sqlite3ExprDup(db, pStep->pWhere, 0) + ); + break; + } + default: assert( pStep->op==TK_SELECT ); { + SelectDest sDest; + Select *pSelect = sqlite3SelectDup(db, pStep->pSelect, 0); + sqlite3SelectDestInit(&sDest, SRT_Discard, 0); + sqlite3Select(pParse, pSelect, &sDest); + sqlite3SelectDelete(db, pSelect); break; } } - pTriggerStep = pTriggerStep->pNext; + if( pStep->op!=TK_SELECT ){ + sqlite3VdbeAddOp0(v, OP_ResetCount); + } } - sqlite3VdbeAddOp2(v, OP_ContextPop, 0, 0); - VdbeComment((v, "end trigger %s", pStepList->pTrig->name)); return 0; } +#ifdef SQLITE_DEBUG /* -** This is called to code FOR EACH ROW triggers. -** -** When the code that this function generates is executed, the following -** must be true: -** -** 1. No cursors may be open in the main database. (But newIdx and oldIdx -** can be indices of cursors in temporary tables. See below.) -** -** 2. If the triggers being coded are ON INSERT or ON UPDATE triggers, then -** a temporary vdbe cursor (index newIdx) must be open and pointing at -** a row containing values to be substituted for new.* expressions in the -** trigger program(s). -** -** 3. If the triggers being coded are ON DELETE or ON UPDATE triggers, then -** a temporary vdbe cursor (index oldIdx) must be open and pointing at -** a row containing values to be substituted for old.* expressions in the -** trigger program(s). -** -** If they are not NULL, the piOldColMask and piNewColMask output variables -** are set to values that describe the columns used by the trigger program -** in the OLD.* and NEW.* tables respectively. If column N of the -** pseudo-table is read at least once, the corresponding bit of the output -** mask is set. If a column with an index greater than 32 is read, the -** output mask is set to the special value 0xffffffff. -** +** This function is used to add VdbeComment() annotations to a VDBE +** program. It is not used in production code, only for debugging. +*/ +static const char *onErrorText(int onError){ + switch( onError ){ + case OE_Abort: return "abort"; + case OE_Rollback: return "rollback"; + case OE_Fail: return "fail"; + case OE_Replace: return "replace"; + case OE_Ignore: return "ignore"; + case OE_Default: return "default"; + } + return "n/a"; +} +#endif + +/* +** Parse context structure pFrom has just been used to create a sub-vdbe +** (trigger program). If an error has occurred, transfer error information +** from pFrom to pTo. +*/ +static void transferParseError(Parse *pTo, Parse *pFrom){ + assert( pFrom->zErrMsg==0 || pFrom->nErr ); + assert( pTo->zErrMsg==0 || pTo->nErr ); + if( pTo->nErr==0 ){ + pTo->zErrMsg = pFrom->zErrMsg; + pTo->nErr = pFrom->nErr; + }else{ + sqlite3DbFree(pFrom->db, pFrom->zErrMsg); + } +} + +/* +** Create and populate a new TriggerPrg object with a sub-program +** implementing trigger pTrigger with ON CONFLICT policy orconf. +*/ +static TriggerPrg *codeRowTrigger( + Parse *pParse, /* Current parse context */ + Trigger *pTrigger, /* Trigger to code */ + Table *pTab, /* The table pTrigger is attached to */ + int orconf /* ON CONFLICT policy to code trigger program with */ +){ + Parse *pTop = sqlite3ParseToplevel(pParse); + sqlite3 *db = pParse->db; /* Database handle */ + TriggerPrg *pPrg; /* Value to return */ + Expr *pWhen = 0; /* Duplicate of trigger WHEN expression */ + Vdbe *v; /* Temporary VM */ + NameContext sNC; /* Name context for sub-vdbe */ + SubProgram *pProgram = 0; /* Sub-vdbe for trigger program */ + Parse *pSubParse; /* Parse context for sub-vdbe */ + int iEndTrigger = 0; /* Label to jump to if WHEN is false */ + + assert( pTrigger->zName==0 || pTab==tableOfTrigger(pTrigger) ); + + /* Allocate the TriggerPrg and SubProgram objects. To ensure that they + ** are freed if an error occurs, link them into the Parse.pTriggerPrg + ** list of the top-level Parse object sooner rather than later. */ + pPrg = sqlite3DbMallocZero(db, sizeof(TriggerPrg)); + if( !pPrg ) return 0; + pPrg->pNext = pTop->pTriggerPrg; + pTop->pTriggerPrg = pPrg; + pPrg->pProgram = pProgram = sqlite3DbMallocZero(db, sizeof(SubProgram)); + if( !pProgram ) return 0; + pProgram->nRef = 1; + pPrg->pTrigger = pTrigger; + pPrg->orconf = orconf; + pPrg->aColmask[0] = 0xffffffff; + pPrg->aColmask[1] = 0xffffffff; + + /* Allocate and populate a new Parse context to use for coding the + ** trigger sub-program. */ + pSubParse = sqlite3StackAllocZero(db, sizeof(Parse)); + if( !pSubParse ) return 0; + memset(&sNC, 0, sizeof(sNC)); + sNC.pParse = pSubParse; + pSubParse->db = db; + pSubParse->pTriggerTab = pTab; + pSubParse->pToplevel = pTop; + pSubParse->zAuthContext = pTrigger->zName; + pSubParse->eTriggerOp = pTrigger->op; + + v = sqlite3GetVdbe(pSubParse); + if( v ){ + VdbeComment((v, "Start: %s.%s (%s %s%s%s ON %s)", + pTrigger->zName, onErrorText(orconf), + (pTrigger->tr_tm==TRIGGER_BEFORE ? "BEFORE" : "AFTER"), + (pTrigger->op==TK_UPDATE ? "UPDATE" : ""), + (pTrigger->op==TK_INSERT ? "INSERT" : ""), + (pTrigger->op==TK_DELETE ? "DELETE" : ""), + pTab->zName + )); +#ifndef SQLITE_OMIT_TRACE + sqlite3VdbeChangeP4(v, -1, + sqlite3MPrintf(db, "-- TRIGGER %s", pTrigger->zName), P4_DYNAMIC + ); +#endif + + /* If one was specified, code the WHEN clause. If it evaluates to false + ** (or NULL) the sub-vdbe is immediately halted by jumping to the + ** OP_Halt inserted at the end of the program. */ + if( pTrigger->pWhen ){ + pWhen = sqlite3ExprDup(db, pTrigger->pWhen, 0); + if( SQLITE_OK==sqlite3ResolveExprNames(&sNC, pWhen) + && db->mallocFailed==0 + ){ + iEndTrigger = sqlite3VdbeMakeLabel(v); + sqlite3ExprIfFalse(pSubParse, pWhen, iEndTrigger, SQLITE_JUMPIFNULL); + } + sqlite3ExprDelete(db, pWhen); + } + + /* Code the trigger program into the sub-vdbe. */ + codeTriggerProgram(pSubParse, pTrigger->step_list, orconf); + + /* Insert an OP_Halt at the end of the sub-program. */ + if( iEndTrigger ){ + sqlite3VdbeResolveLabel(v, iEndTrigger); + } + sqlite3VdbeAddOp0(v, OP_Halt); + VdbeComment((v, "End: %s.%s", pTrigger->zName, onErrorText(orconf))); + + transferParseError(pParse, pSubParse); + if( db->mallocFailed==0 ){ + pProgram->aOp = sqlite3VdbeTakeOpArray(v, &pProgram->nOp, &pTop->nMaxArg); + } + pProgram->nMem = pSubParse->nMem; + pProgram->nCsr = pSubParse->nTab; + pProgram->token = (void *)pTrigger; + pPrg->aColmask[0] = pSubParse->oldmask; + pPrg->aColmask[1] = pSubParse->newmask; + sqlite3VdbeDelete(v); + } + + assert( !pSubParse->pAinc && !pSubParse->pZombieTab ); + assert( !pSubParse->pTriggerPrg && !pSubParse->nMaxArg ); + sqlite3StackFree(db, pSubParse); + + return pPrg; +} + +/* +** Return a pointer to a TriggerPrg object containing the sub-program for +** trigger pTrigger with default ON CONFLICT algorithm orconf. If no such +** TriggerPrg object exists, a new object is allocated and populated before +** being returned. +*/ +static TriggerPrg *getRowTrigger( + Parse *pParse, /* Current parse context */ + Trigger *pTrigger, /* Trigger to code */ + Table *pTab, /* The table trigger pTrigger is attached to */ + int orconf /* ON CONFLICT algorithm. */ +){ + Parse *pRoot = sqlite3ParseToplevel(pParse); + TriggerPrg *pPrg; + + assert( pTrigger->zName==0 || pTab==tableOfTrigger(pTrigger) ); + + /* It may be that this trigger has already been coded (or is in the + ** process of being coded). If this is the case, then an entry with + ** a matching TriggerPrg.pTrigger field will be present somewhere + ** in the Parse.pTriggerPrg list. Search for such an entry. */ + for(pPrg=pRoot->pTriggerPrg; + pPrg && (pPrg->pTrigger!=pTrigger || pPrg->orconf!=orconf); + pPrg=pPrg->pNext + ); + + /* If an existing TriggerPrg could not be located, create a new one. */ + if( !pPrg ){ + pPrg = codeRowTrigger(pParse, pTrigger, pTab, orconf); + } + + return pPrg; +} + +/* +** Generate code for the trigger program associated with trigger p on +** table pTab. The reg, orconf and ignoreJump parameters passed to this +** function are the same as those described in the header function for +** sqlite3CodeRowTrigger() +*/ +SQLITE_PRIVATE void sqlite3CodeRowTriggerDirect( + Parse *pParse, /* Parse context */ + Trigger *p, /* Trigger to code */ + Table *pTab, /* The table to code triggers from */ + int reg, /* Reg array containing OLD.* and NEW.* values */ + int orconf, /* ON CONFLICT policy */ + int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */ +){ + Vdbe *v = sqlite3GetVdbe(pParse); /* Main VM */ + TriggerPrg *pPrg; + pPrg = getRowTrigger(pParse, p, pTab, orconf); + assert( pPrg || pParse->nErr || pParse->db->mallocFailed ); + + /* Code the OP_Program opcode in the parent VDBE. P4 of the OP_Program + ** is a pointer to the sub-vdbe containing the trigger program. */ + if( pPrg ){ + sqlite3VdbeAddOp3(v, OP_Program, reg, ignoreJump, ++pParse->nMem); + pPrg->pProgram->nRef++; + sqlite3VdbeChangeP4(v, -1, (const char *)pPrg->pProgram, P4_SUBPROGRAM); + VdbeComment( + (v, "Call: %s.%s", (p->zName?p->zName:"fkey"), onErrorText(orconf))); + + /* Set the P5 operand of the OP_Program instruction to non-zero if + ** recursive invocation of this trigger program is disallowed. Recursive + ** invocation is disallowed if (a) the sub-program is really a trigger, + ** not a foreign key action, and (b) the flag to enable recursive triggers + ** is clear. */ + sqlite3VdbeChangeP5(v, (u8)(p->zName && !(pParse->db->flags&SQLITE_RecTriggers))); + } +} + +/* +** This is called to code the required FOR EACH ROW triggers for an operation +** on table pTab. The operation to code triggers for (INSERT, UPDATE or DELETE) +** is given by the op paramater. The tr_tm parameter determines whether the +** BEFORE or AFTER triggers are coded. If the operation is an UPDATE, then +** parameter pChanges is passed the list of columns being modified. +** +** If there are no triggers that fire at the specified time for the specified +** operation on pTab, this function is a no-op. +** +** The reg argument is the address of the first in an array of registers +** that contain the values substituted for the new.* and old.* references +** in the trigger program. If N is the number of columns in table pTab +** (a copy of pTab->nCol), then registers are populated as follows: +** +** Register Contains +** ------------------------------------------------------ +** reg+0 OLD.rowid +** reg+1 OLD.* value of left-most column of pTab +** ... ... +** reg+N OLD.* value of right-most column of pTab +** reg+N+1 NEW.rowid +** reg+N+2 OLD.* value of left-most column of pTab +** ... ... +** reg+N+N+1 NEW.* value of right-most column of pTab +** +** For ON DELETE triggers, the registers containing the NEW.* values will +** never be accessed by the trigger program, so they are not allocated or +** populated by the caller (there is no data to populate them with anyway). +** Similarly, for ON INSERT triggers the values stored in the OLD.* registers +** are never accessed, and so are not allocated by the caller. So, for an +** ON INSERT trigger, the value passed to this function as parameter reg +** is not a readable register, although registers (reg+N) through +** (reg+N+N+1) are. +** +** Parameter orconf is the default conflict resolution algorithm for the +** trigger program to use (REPLACE, IGNORE etc.). Parameter ignoreJump +** is the instruction that control should jump to if a trigger program +** raises an IGNORE exception. */ -SQLITE_PRIVATE int sqlite3CodeRowTrigger( +SQLITE_PRIVATE void sqlite3CodeRowTrigger( Parse *pParse, /* Parse context */ Trigger *pTrigger, /* List of triggers on table pTab */ int op, /* One of TK_UPDATE, TK_INSERT, TK_DELETE */ ExprList *pChanges, /* Changes list for any UPDATE OF triggers */ int tr_tm, /* One of TRIGGER_BEFORE, TRIGGER_AFTER */ Table *pTab, /* The table to code triggers from */ - int newIdx, /* The indice of the "new" row to access */ - int oldIdx, /* The indice of the "old" row to access */ + int reg, /* The first in an array of registers (see above) */ int orconf, /* ON CONFLICT policy */ - int ignoreJump, /* Instruction to jump to for RAISE(IGNORE) */ - u32 *piOldColMask, /* OUT: Mask of columns used from the OLD.* table */ - u32 *piNewColMask /* OUT: Mask of columns used from the NEW.* table */ + int ignoreJump /* Instruction to jump to for RAISE(IGNORE) */ ){ - Trigger *p; - sqlite3 *db = pParse->db; - TriggerStack trigStackEntry; - - trigStackEntry.oldColMask = 0; - trigStackEntry.newColMask = 0; + Trigger *p; /* Used to iterate through pTrigger list */ - assert(op == TK_UPDATE || op == TK_INSERT || op == TK_DELETE); - assert(tr_tm == TRIGGER_BEFORE || tr_tm == TRIGGER_AFTER ); - - assert(newIdx != -1 || oldIdx != -1); + assert( op==TK_UPDATE || op==TK_INSERT || op==TK_DELETE ); + assert( tr_tm==TRIGGER_BEFORE || tr_tm==TRIGGER_AFTER ); + assert( (op==TK_UPDATE)==(pChanges!=0) ); for(p=pTrigger; p; p=p->pNext){ - int fire_this = 0; /* Sanity checking: The schema for the trigger and for the table are ** always defined. The trigger must be in the same schema as the table ** or else it must be a TEMP trigger. */ assert( p->pSchema!=0 ); assert( p->pTabSchema!=0 ); - assert( p->pSchema==p->pTabSchema || p->pSchema==db->aDb[1].pSchema ); + assert( p->pSchema==p->pTabSchema + || p->pSchema==pParse->db->aDb[1].pSchema ); /* Determine whether we should code this trigger */ - if( - p->op==op && - p->tr_tm==tr_tm && - checkColumnOverlap(p->pColumns,pChanges) + if( p->op==op + && p->tr_tm==tr_tm + && checkColumnOverlap(p->pColumns, pChanges) ){ - TriggerStack *pS; /* Pointer to trigger-stack entry */ - for(pS=pParse->trigStack; pS && p!=pS->pTrigger; pS=pS->pNext){} - if( !pS ){ - fire_this = 1; - } -#if 0 /* Give no warning for recursive triggers. Just do not do them */ - else{ - sqlite3ErrorMsg(pParse, "recursive triggers not supported (%s)", - p->name); - return SQLITE_ERROR; - } -#endif + sqlite3CodeRowTriggerDirect(pParse, p, pTab, reg, orconf, ignoreJump); } - - if( fire_this ){ - int endTrigger; - Expr * whenExpr; - AuthContext sContext; - NameContext sNC; - -#ifndef SQLITE_OMIT_TRACE - sqlite3VdbeAddOp4(pParse->pVdbe, OP_Trace, 0, 0, 0, - sqlite3MPrintf(db, "-- TRIGGER %s", p->name), - P4_DYNAMIC); -#endif - memset(&sNC, 0, sizeof(sNC)); - sNC.pParse = pParse; - - /* Push an entry on to the trigger stack */ - trigStackEntry.pTrigger = p; - trigStackEntry.newIdx = newIdx; - trigStackEntry.oldIdx = oldIdx; - trigStackEntry.pTab = pTab; - trigStackEntry.pNext = pParse->trigStack; - trigStackEntry.ignoreJump = ignoreJump; - pParse->trigStack = &trigStackEntry; - sqlite3AuthContextPush(pParse, &sContext, p->name); - - /* code the WHEN clause */ - endTrigger = sqlite3VdbeMakeLabel(pParse->pVdbe); - whenExpr = sqlite3ExprDup(db, p->pWhen, 0); - if( db->mallocFailed || sqlite3ResolveExprNames(&sNC, whenExpr) ){ - pParse->trigStack = trigStackEntry.pNext; - sqlite3ExprDelete(db, whenExpr); - return 1; - } - sqlite3ExprIfFalse(pParse, whenExpr, endTrigger, SQLITE_JUMPIFNULL); - sqlite3ExprDelete(db, whenExpr); - - codeTriggerProgram(pParse, p->step_list, orconf); + } +} - /* Pop the entry off the trigger stack */ - pParse->trigStack = trigStackEntry.pNext; - sqlite3AuthContextPop(&sContext); +/* +** Triggers may access values stored in the old.* or new.* pseudo-table. +** This function returns a 32-bit bitmask indicating which columns of the +** old.* or new.* tables actually are used by triggers. This information +** may be used by the caller, for example, to avoid having to load the entire +** old.* record into memory when executing an UPDATE or DELETE command. +** +** Bit 0 of the returned mask is set if the left-most column of the +** table may be accessed using an [old|new]. reference. Bit 1 is set if +** the second leftmost column value is required, and so on. If there +** are more than 32 columns in the table, and at least one of the columns +** with an index greater than 32 may be accessed, 0xffffffff is returned. +** +** It is not possible to determine if the old.rowid or new.rowid column is +** accessed by triggers. The caller must always assume that it is. +** +** Parameter isNew must be either 1 or 0. If it is 0, then the mask returned +** applies to the old.* table. If 1, the new.* table. +** +** Parameter tr_tm must be a mask with one or both of the TRIGGER_BEFORE +** and TRIGGER_AFTER bits set. Values accessed by BEFORE triggers are only +** included in the returned mask if the TRIGGER_BEFORE bit is set in the +** tr_tm parameter. Similarly, values accessed by AFTER triggers are only +** included in the returned mask if the TRIGGER_AFTER bit is set in tr_tm. +*/ +SQLITE_PRIVATE u32 sqlite3TriggerColmask( + Parse *pParse, /* Parse context */ + Trigger *pTrigger, /* List of triggers on table pTab */ + ExprList *pChanges, /* Changes list for any UPDATE OF triggers */ + int isNew, /* 1 for new.* ref mask, 0 for old.* ref mask */ + int tr_tm, /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ + Table *pTab, /* The table to code triggers from */ + int orconf /* Default ON CONFLICT policy for trigger steps */ +){ + const int op = pChanges ? TK_UPDATE : TK_DELETE; + u32 mask = 0; + Trigger *p; - sqlite3VdbeResolveLabel(pParse->pVdbe, endTrigger); + assert( isNew==1 || isNew==0 ); + for(p=pTrigger; p; p=p->pNext){ + if( p->op==op && (tr_tm&p->tr_tm) + && checkColumnOverlap(p->pColumns,pChanges) + ){ + TriggerPrg *pPrg; + pPrg = getRowTrigger(pParse, p, pTab, orconf); + if( pPrg ){ + mask |= pPrg->aColmask[isNew]; + } } } - if( piOldColMask ) *piOldColMask |= trigStackEntry.oldColMask; - if( piNewColMask ) *piNewColMask |= trigStackEntry.newColMask; - return 0; + + return mask; } + #endif /* !defined(SQLITE_OMIT_TRIGGER) */ /************** End of trigger.c *********************************************/ @@ -80675,8 +84577,6 @@ ************************************************************************* ** This file contains C code routines that are called by the parser ** to handle UPDATE statements. -** -** $Id: update.c,v 1.204 2009/06/27 11:17:35 drh Exp $ */ #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -80716,8 +84616,13 @@ ** the column is a literal number, string or null. The sqlite3ValueFromExpr() ** function is capable of transforming these types of expressions into ** sqlite3_value objects. +** +** If parameter iReg is not negative, code an OP_RealAffinity instruction +** on register iReg. This is used when an equivalent integer value is +** stored in place of an 8-byte floating point value in order to save +** space. */ -SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i){ +SQLITE_PRIVATE void sqlite3ColumnDefault(Vdbe *v, Table *pTab, int i, int iReg){ assert( pTab!=0 ); if( !pTab->pSelect ){ sqlite3_value *pValue; @@ -80730,6 +84635,11 @@ if( pValue ){ sqlite3VdbeChangeP4(v, -1, (const char *)pValue, P4_MEM); } +#ifndef SQLITE_OMIT_FLOATING_POINT + if( iReg>=0 && pTab->aCol[i].affinity==SQLITE_AFF_REAL ){ + sqlite3VdbeAddOp1(v, OP_RealAffinity, iReg); + } +#endif } } @@ -80766,31 +84676,26 @@ AuthContext sContext; /* The authorization context */ NameContext sNC; /* The name-context to resolve expressions in */ int iDb; /* Database containing the table being updated */ - int j1; /* Addresses of jump instructions */ int okOnePass; /* True for one-pass algorithm without the FIFO */ + int hasFK; /* True if foreign key processing is required */ #ifndef SQLITE_OMIT_TRIGGER - int isView; /* Trying to update a view */ - Trigger *pTrigger; /* List of triggers on pTab, if required */ + int isView; /* True when updating a view (INSTEAD OF trigger) */ + Trigger *pTrigger; /* List of triggers on pTab, if required */ + int tmask; /* Mask of TRIGGER_BEFORE|TRIGGER_AFTER */ #endif - int iBeginAfterTrigger = 0; /* Address of after trigger program */ - int iEndAfterTrigger = 0; /* Exit of after trigger program */ - int iBeginBeforeTrigger = 0; /* Address of before trigger program */ - int iEndBeforeTrigger = 0; /* Exit of before trigger program */ - u32 old_col_mask = 0; /* Mask of OLD.* columns in use */ - u32 new_col_mask = 0; /* Mask of NEW.* columns in use */ - - int newIdx = -1; /* index of trigger "new" temp table */ - int oldIdx = -1; /* index of trigger "old" temp table */ + int newmask; /* Mask of NEW.* columns accessed by BEFORE triggers */ /* Register Allocations */ int regRowCount = 0; /* A count of rows changed */ int regOldRowid; /* The old rowid */ int regNewRowid; /* The new rowid */ - int regData; /* New data for the row */ + int regNew; + int regOld = 0; int regRowSet = 0; /* Rowset of rows to be updated */ + int regRec; /* Register used for new table record to insert */ - sContext.pParse = 0; + memset(&sContext, 0, sizeof(sContext)); db = pParse->db; if( pParse->nErr || db->mallocFailed ){ goto update_cleanup; @@ -80804,38 +84709,32 @@ iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); /* Figure out if we have any triggers and if the table being - ** updated is a view + ** updated is a view. */ #ifndef SQLITE_OMIT_TRIGGER - pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, 0); + pTrigger = sqlite3TriggersExist(pParse, pTab, TK_UPDATE, pChanges, &tmask); isView = pTab->pSelect!=0; + assert( pTrigger || tmask==0 ); #else # define pTrigger 0 # define isView 0 +# define tmask 0 #endif #ifdef SQLITE_OMIT_VIEW # undef isView # define isView 0 #endif - if( sqlite3IsReadOnly(pParse, pTab, (pTrigger?1:0)) ){ + if( sqlite3ViewGetColumnNames(pParse, pTab) ){ goto update_cleanup; } - if( sqlite3ViewGetColumnNames(pParse, pTab) ){ + if( sqlite3IsReadOnly(pParse, pTab, tmask) ){ goto update_cleanup; } aXRef = sqlite3DbMallocRaw(db, sizeof(int) * pTab->nCol ); if( aXRef==0 ) goto update_cleanup; for(i=0; inCol; i++) aXRef[i] = -1; - /* If there are FOR EACH ROW triggers, allocate cursors for the - ** special OLD and NEW tables - */ - if( pTrigger ){ - newIdx = pParse->nTab++; - oldIdx = pParse->nTab++; - } - /* Allocate a cursors for the main database table and for all indices. ** The index cursors might not be used, but if they are used they ** need to occur right after the database cursor. So go ahead and @@ -80895,6 +84794,8 @@ #endif } + hasFK = sqlite3FkRequired(pParse, pTab, aXRef, chngRowid); + /* Allocate memory for the array aRegIdx[]. There is one entry in the ** array for each index associated with table being updated. Fill in ** the value with a register number for indices that are to be used @@ -80921,24 +84822,7 @@ aRegIdx[j] = reg; } - /* Allocate a block of register used to store the change record - ** sent to sqlite3GenerateConstraintChecks(). There are either - ** one or two registers for holding the rowid. One rowid register - ** is used if chngRowid is false and two are used if chngRowid is - ** true. Following these are pTab->nCol register holding column - ** data. - */ - regOldRowid = regNewRowid = pParse->nMem + 1; - pParse->nMem += pTab->nCol + 1; - if( chngRowid ){ - regNewRowid++; - pParse->nMem++; - } - regData = regNewRowid+1; - - - /* Begin generating code. - */ + /* Begin generating code. */ v = sqlite3GetVdbe(pParse); if( v==0 ) goto update_cleanup; if( pParse->nested==0 ) sqlite3VdbeCountChanges(v); @@ -80955,41 +84839,24 @@ } #endif - /* Start the view context - */ + /* Allocate required registers. */ + regOldRowid = regNewRowid = ++pParse->nMem; + if( pTrigger || hasFK ){ + regOld = pParse->nMem + 1; + pParse->nMem += pTab->nCol; + } + if( chngRowid || pTrigger || hasFK ){ + regNewRowid = ++pParse->nMem; + } + regNew = pParse->nMem + 1; + pParse->nMem += pTab->nCol; + regRec = ++pParse->nMem; + + /* Start the view context. */ if( isView ){ sqlite3AuthContextPush(pParse, &sContext, pTab->zName); } - /* Generate the code for triggers. - */ - if( pTrigger ){ - int iGoto; - - /* Create pseudo-tables for NEW and OLD - */ - sqlite3VdbeAddOp3(v, OP_OpenPseudo, oldIdx, 0, pTab->nCol); - sqlite3VdbeAddOp3(v, OP_OpenPseudo, newIdx, 0, pTab->nCol); - - iGoto = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); - addr = sqlite3VdbeMakeLabel(v); - iBeginBeforeTrigger = sqlite3VdbeCurrentAddr(v); - if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, - TRIGGER_BEFORE, pTab, newIdx, oldIdx, onError, addr, - &old_col_mask, &new_col_mask) ){ - goto update_cleanup; - } - iEndBeforeTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); - iBeginAfterTrigger = sqlite3VdbeCurrentAddr(v); - if( sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, - TRIGGER_AFTER, pTab, newIdx, oldIdx, onError, addr, - &old_col_mask, &new_col_mask) ){ - goto update_cleanup; - } - iEndAfterTrigger = sqlite3VdbeAddOp2(v, OP_Goto, 0, 0); - sqlite3VdbeJumpHere(v, iGoto); - } - /* If we are trying to update a view, realize that view into ** a ephemeral table. */ @@ -81027,7 +84894,7 @@ /* Initialize the count of updated rows */ - if( db->flags & SQLITE_CountRows && !pParse->trigStack ){ + if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab ){ regRowCount = ++pParse->nMem; sqlite3VdbeAddOp2(v, OP_Integer, 0, regRowCount); } @@ -81060,11 +84927,6 @@ } } } - - /* Jump back to this point if a trigger encounters an IGNORE constraint. */ - if( pTrigger ){ - sqlite3VdbeResolveLabel(v, addr); - } /* Top of the update loop */ if( okOnePass ){ @@ -81075,139 +84937,153 @@ addr = sqlite3VdbeAddOp3(v, OP_RowSetRead, regRowSet, 0, regOldRowid); } - if( pTrigger ){ - int regRowid; - int regRow; - int regCols; - - /* Make cursor iCur point to the record that is being updated. - */ - sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); - - /* Generate the OLD table - */ - regRowid = sqlite3GetTempReg(pParse); - regRow = sqlite3GetTempReg(pParse); - sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regRowid); - if( !old_col_mask ){ - sqlite3VdbeAddOp2(v, OP_Null, 0, regRow); - }else{ - sqlite3VdbeAddOp2(v, OP_RowData, iCur, regRow); - } - sqlite3VdbeAddOp3(v, OP_Insert, oldIdx, regRow, regRowid); + /* Make cursor iCur point to the record that is being updated. If + ** this record does not exist for some reason (deleted by a trigger, + ** for example, then jump to the next iteration of the RowSet loop. */ + sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); + + /* If the record number will change, set register regNewRowid to + ** contain the new value. If the record number is not being modified, + ** then regNewRowid is the same register as regOldRowid, which is + ** already populated. */ + assert( chngRowid || pTrigger || hasFK || regOldRowid==regNewRowid ); + if( chngRowid ){ + sqlite3ExprCode(pParse, pRowidExpr, regNewRowid); + sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); + } - /* Generate the NEW table - */ - if( chngRowid ){ - sqlite3ExprCodeAndCache(pParse, pRowidExpr, regRowid); - sqlite3VdbeAddOp1(v, OP_MustBeInt, regRowid); - }else{ - sqlite3VdbeAddOp2(v, OP_Rowid, iCur, regRowid); - } - regCols = sqlite3GetTempRange(pParse, pTab->nCol); + /* If there are triggers on this table, populate an array of registers + ** with the required old.* column data. */ + if( hasFK || pTrigger ){ + u32 oldmask = (hasFK ? sqlite3FkOldmask(pParse, pTab) : 0); + oldmask |= sqlite3TriggerColmask(pParse, + pTrigger, pChanges, 0, TRIGGER_BEFORE|TRIGGER_AFTER, pTab, onError + ); for(i=0; inCol; i++){ - if( i==pTab->iPKey ){ - sqlite3VdbeAddOp2(v, OP_Null, 0, regCols+i); - continue; - } - j = aXRef[i]; - if( (i<32 && (new_col_mask&((u32)1<a[j].pExpr, regCols+i); - } + if( aXRef[i]<0 || oldmask==0xffffffff || (oldmask & (1<nCol, regRow); - if( !isView ){ - sqlite3TableAffinityStr(v, pTab); - sqlite3ExprCacheAffinityChange(pParse, regCols, pTab->nCol); + if( chngRowid==0 ){ + sqlite3VdbeAddOp2(v, OP_Copy, regOldRowid, regNewRowid); } - sqlite3ReleaseTempRange(pParse, regCols, pTab->nCol); - /* if( pParse->nErr ) goto update_cleanup; */ - sqlite3VdbeAddOp3(v, OP_Insert, newIdx, regRow, regRowid); - sqlite3ReleaseTempReg(pParse, regRowid); - sqlite3ReleaseTempReg(pParse, regRow); + } - sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginBeforeTrigger); - sqlite3VdbeJumpHere(v, iEndBeforeTrigger); + /* Populate the array of registers beginning at regNew with the new + ** row data. This array is used to check constaints, create the new + ** table and index records, and as the values for any new.* references + ** made by triggers. + ** + ** If there are one or more BEFORE triggers, then do not populate the + ** registers associated with columns that are (a) not modified by + ** this UPDATE statement and (b) not accessed by new.* references. The + ** values for registers not modified by the UPDATE must be reloaded from + ** the database after the BEFORE triggers are fired anyway (as the trigger + ** may have modified them). So not loading those that are not going to + ** be used eliminates some redundant opcodes. + */ + newmask = sqlite3TriggerColmask( + pParse, pTrigger, pChanges, 1, TRIGGER_BEFORE, pTab, onError + ); + for(i=0; inCol; i++){ + if( i==pTab->iPKey ){ + sqlite3VdbeAddOp2(v, OP_Null, 0, regNew+i); + }else{ + j = aXRef[i]; + if( j>=0 ){ + sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regNew+i); + }else if( 0==(tmask&TRIGGER_BEFORE) || i>31 || (newmask&(1<nCol); + sqlite3TableAffinityStr(v, pTab); + sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, + TRIGGER_BEFORE, pTab, regOldRowid, onError, addr); - /* If the record number will change, push the record number as it - ** will be after the update. (The old record number is currently - ** on top of the stack.) + /* The row-trigger may have deleted the row being updated. In this + ** case, jump to the next row. No updates or AFTER triggers are + ** required. This behaviour - what happens when the row being updated + ** is deleted or renamed by a BEFORE trigger - is left undefined in the + ** documentation. */ - if( chngRowid ){ - sqlite3ExprCode(pParse, pRowidExpr, regNewRowid); - sqlite3VdbeAddOp1(v, OP_MustBeInt, regNewRowid); - } + sqlite3VdbeAddOp3(v, OP_NotExists, iCur, addr, regOldRowid); - /* Compute new data for this record. + /* If it did not delete it, the row-trigger may still have modified + ** some of the columns of the row being updated. Load the values for + ** all columns not modified by the update statement into their + ** registers in case this has happened. */ for(i=0; inCol; i++){ - if( i==pTab->iPKey ){ - sqlite3VdbeAddOp2(v, OP_Null, 0, regData+i); - continue; - } - j = aXRef[i]; - if( j<0 ){ - sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regData+i); - sqlite3ColumnDefault(v, pTab, i); - }else{ - sqlite3ExprCode(pParse, pChanges->a[j].pExpr, regData+i); + if( aXRef[i]<0 && i!=pTab->iPKey ){ + sqlite3VdbeAddOp3(v, OP_Column, iCur, i, regNew+i); + sqlite3ColumnDefault(v, pTab, i, regNew+i); } } + } - /* Do constraint checks - */ + if( !isView ){ + int j1; /* Address of jump instruction */ + + /* Do constraint checks. */ sqlite3GenerateConstraintChecks(pParse, pTab, iCur, regNewRowid, - aRegIdx, chngRowid, 1, - onError, addr, 0); + aRegIdx, (chngRowid?regOldRowid:0), 1, onError, addr, 0); - /* Delete the old indices for the current record. - */ + /* Do FK constraint checks. */ + if( hasFK ){ + sqlite3FkCheck(pParse, pTab, regOldRowid, 0); + } + + /* Delete the index entries associated with the current record. */ j1 = sqlite3VdbeAddOp3(v, OP_NotExists, iCur, 0, regOldRowid); sqlite3GenerateRowIndexDelete(pParse, pTab, iCur, aRegIdx); - - /* If changing the record number, delete the old record. - */ - if( chngRowid ){ + + /* If changing the record number, delete the old record. */ + if( hasFK || chngRowid ){ sqlite3VdbeAddOp2(v, OP_Delete, iCur, 0); } sqlite3VdbeJumpHere(v, j1); - /* Create the new index entries and the new record. - */ - sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, - aRegIdx, 1, -1, 0, 0); + if( hasFK ){ + sqlite3FkCheck(pParse, pTab, 0, regNewRowid); + } + + /* Insert the new index entries and the new record. */ + sqlite3CompleteInsertion(pParse, pTab, iCur, regNewRowid, aRegIdx, 1, 0, 0); + + /* Do any ON CASCADE, SET NULL or SET DEFAULT operations required to + ** handle rows (possibly in other tables) that refer via a foreign key + ** to the row just updated. */ + if( hasFK ){ + sqlite3FkActions(pParse, pTab, pChanges, regOldRowid); + } } /* Increment the row counter */ - if( db->flags & SQLITE_CountRows && !pParse->trigStack){ + if( (db->flags & SQLITE_CountRows) && !pParse->pTriggerTab){ sqlite3VdbeAddOp2(v, OP_AddImm, regRowCount, 1); } - /* If there are triggers, close all the cursors after each iteration - ** through the loop. The fire the after triggers. - */ - if( pTrigger ){ - sqlite3VdbeAddOp2(v, OP_Goto, 0, iBeginAfterTrigger); - sqlite3VdbeJumpHere(v, iEndAfterTrigger); - } + sqlite3CodeRowTrigger(pParse, pTrigger, TK_UPDATE, pChanges, + TRIGGER_AFTER, pTab, regOldRowid, onError, addr); /* Repeat the above with the next record to be updated, until ** all record selected by the WHERE clause have been updated. @@ -81222,16 +85098,12 @@ } } sqlite3VdbeAddOp2(v, OP_Close, iCur, 0); - if( pTrigger ){ - sqlite3VdbeAddOp2(v, OP_Close, newIdx, 0); - sqlite3VdbeAddOp2(v, OP_Close, oldIdx, 0); - } /* Update the sqlite_sequence table by storing the content of the ** maximum rowid counter values recorded while inserting into ** autoincrement tables. */ - if( pParse->nested==0 && pParse->trigStack==0 ){ + if( pParse->nested==0 && pParse->pTriggerTab==0 ){ sqlite3AutoincrementEnd(pParse); } @@ -81240,7 +85112,7 @@ ** generating code because of a call to sqlite3NestedParse(), do not ** invoke the callback function. */ - if( db->flags & SQLITE_CountRows && !pParse->trigStack && pParse->nested==0 ){ + if( (db->flags&SQLITE_CountRows) && !pParse->pTriggerTab && !pParse->nested ){ sqlite3VdbeAddOp2(v, OP_ResultRow, regRowCount, 1); sqlite3VdbeSetNumCols(v, 1); sqlite3VdbeSetColName(v, 0, COLNAME_NAME, "rows updated", SQLITE_STATIC); @@ -81255,6 +85127,15 @@ sqlite3ExprDelete(db, pWhere); return; } +/* Make sure "isView" and other macros defined above are undefined. Otherwise +** thely may interfere with compilation of other functions in this file +** (or in another file, if this file becomes part of the amalgamation). */ +#ifdef isView + #undef isView +#endif +#ifdef pTrigger + #undef pTrigger +#endif #ifndef SQLITE_OMIT_VIRTUALTABLE /* @@ -81294,14 +85175,13 @@ int addr; /* Address of top of loop */ int iReg; /* First register in set passed to OP_VUpdate */ sqlite3 *db = pParse->db; /* Database connection */ - const char *pVtab = (const char*)pTab->pVtab; + const char *pVTab = (const char*)sqlite3GetVTable(db, pTab); SelectDest dest; /* Construct the SELECT statement that will find the new values for ** all updated rows. */ - pEList = sqlite3ExprListAppend(pParse, 0, - sqlite3CreateIdExpr(pParse, "_rowid_")); + pEList = sqlite3ExprListAppend(pParse, 0, sqlite3Expr(db, TK_ID, "_rowid_")); if( pRowid ){ pEList = sqlite3ExprListAppend(pParse, pEList, sqlite3ExprDup(db, pRowid, 0)); @@ -81311,7 +85191,7 @@ if( aXRef[i]>=0 ){ pExpr = sqlite3ExprDup(db, pChanges->a[aXRef[i]].pExpr, 0); }else{ - pExpr = sqlite3CreateIdExpr(pParse, pTab->aCol[i].zName); + pExpr = sqlite3Expr(db, TK_ID, pTab->aCol[i].zName); } pEList = sqlite3ExprListAppend(pParse, pEList, pExpr); } @@ -81339,7 +85219,8 @@ sqlite3VdbeAddOp3(v, OP_Column, ephemTab, i+1+(pRowid!=0), iReg+2+i); } sqlite3VtabMakeWritable(pParse, pTab); - sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVtab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VUpdate, 0, pTab->nCol+2, iReg, pVTab, P4_VTAB); + sqlite3MayAbort(pParse); sqlite3VdbeAddOp2(v, OP_Next, ephemTab, addr+1); sqlite3VdbeJumpHere(v, addr); sqlite3VdbeAddOp2(v, OP_Close, ephemTab, 0); @@ -81349,11 +85230,6 @@ } #endif /* SQLITE_OMIT_VIRTUALTABLE */ -/* Make sure "isView" gets undefined in case this file becomes part of -** the amalgamation - so that subsequent files do not see isView as a -** macro. */ -#undef isView - /************** End of update.c **********************************************/ /************** Begin file vacuum.c ******************************************/ /* @@ -81371,8 +85247,6 @@ ** ** Most of the code in this file may be omitted by defining the ** SQLITE_OMIT_VACUUM macro. -** -** $Id: vacuum.c,v 1.90 2009/06/03 11:25:07 danielk1977 Exp $ */ #if !defined(SQLITE_OMIT_VACUUM) && !defined(SQLITE_OMIT_ATTACH) @@ -81444,6 +85318,7 @@ int saved_flags; /* Saved value of the db->flags */ int saved_nChange; /* Saved value of db->nChange */ int saved_nTotalChange; /* Saved value of db->nTotalChange */ + void (*saved_xTrace)(void*,const char*); /* Saved db->xTrace */ Db *pDb = 0; /* Database to detach at end of vacuum */ int isMemDb; /* True if vacuuming a :memory: database */ int nRes; @@ -81453,11 +85328,16 @@ return SQLITE_ERROR; } - /* Save the current value of the write-schema flag before setting it. */ + /* Save the current value of the database flags so that it can be + ** restored before returning. Then set the writable-schema flag, and + ** disable CHECK and foreign key constraints. */ saved_flags = db->flags; saved_nChange = db->nChange; saved_nTotalChange = db->nTotalChange; + saved_xTrace = db->xTrace; db->flags |= SQLITE_WriteSchema | SQLITE_IgnoreChecks; + db->flags &= ~SQLITE_ForeignKeys; + db->xTrace = 0; pMain = db->aDb[0].pBt; isMemDb = sqlite3PagerIsMemdb(sqlite3BtreePager(pMain)); @@ -81483,6 +85363,12 @@ assert( strcmp(db->aDb[db->nDb-1].zName,"vacuum_db")==0 ); pTemp = db->aDb[db->nDb-1].pBt; + /* The call to execSql() to attach the temp database has left the file + ** locked (as there was more than one active statement when the transaction + ** to read the schema was concluded. Unlock it here so that this doesn't + ** cause problems for the call to BtreeSetPageSize() below. */ + sqlite3BtreeCommit(pTemp); + nRes = sqlite3BtreeGetReserve(pMain); /* A VACUUM cannot change the pagesize of an encrypted database. */ @@ -81536,13 +85422,13 @@ if( rc!=SQLITE_OK ) goto end_of_vacuum; /* Loop through the tables in the main database. For each, do - ** an "INSERT INTO vacuum_db.xxx SELECT * FROM xxx;" to copy + ** an "INSERT INTO vacuum_db.xxx SELECT * FROM main.xxx;" to copy ** the contents to the temporary database. */ rc = execExecSql(db, "SELECT 'INSERT INTO vacuum_db.' || quote(name) " - "|| ' SELECT * FROM ' || quote(name) || ';'" - "FROM sqlite_master " + "|| ' SELECT * FROM main.' || quote(name) || ';'" + "FROM main.sqlite_master " "WHERE type = 'table' AND name!='sqlite_sequence' " " AND rootpage>0" @@ -81558,7 +85444,7 @@ if( rc!=SQLITE_OK ) goto end_of_vacuum; rc = execExecSql(db, "SELECT 'INSERT INTO vacuum_db.' || quote(name) " - "|| ' SELECT * FROM ' || quote(name) || ';' " + "|| ' SELECT * FROM main.' || quote(name) || ';' " "FROM vacuum_db.sqlite_master WHERE name=='sqlite_sequence';" ); if( rc!=SQLITE_OK ) goto end_of_vacuum; @@ -81572,7 +85458,7 @@ rc = execSql(db, "INSERT INTO vacuum_db.sqlite_master " " SELECT type, name, tbl_name, rootpage, sql" - " FROM sqlite_master" + " FROM main.sqlite_master" " WHERE type='view' OR type='trigger'" " OR (type='table' AND rootpage=0)" ); @@ -81610,8 +85496,7 @@ for(i=0; iflags = saved_flags; db->nChange = saved_nChange; db->nTotalChange = saved_nTotalChange; + db->xTrace = saved_xTrace; /* Currently there is an SQL level transaction open on the vacuum ** database. No locks are held on any other files (since the main file @@ -81669,8 +85555,6 @@ ** ************************************************************************* ** This file contains code used to help implement virtual tables. -** -** $Id: vtab.c,v 1.91 2009/06/15 16:27:08 shane Exp $ */ #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -81685,7 +85569,7 @@ const sqlite3_module *pModule, /* The definition of the module */ void *pAux, /* Context pointer for xCreate/xConnect */ void (*xDestroy)(void *) /* Module destructor function */ -) { +){ int rc, nName; Module *pMod; @@ -81751,33 +85635,128 @@ ** If a disconnect is attempted while a virtual table is locked, ** the disconnect is deferred until all locks have been removed. */ -SQLITE_PRIVATE void sqlite3VtabLock(sqlite3_vtab *pVtab){ - pVtab->nRef++; +SQLITE_PRIVATE void sqlite3VtabLock(VTable *pVTab){ + pVTab->nRef++; } + /* -** Unlock a virtual table. When the last lock is removed, -** disconnect the virtual table. +** pTab is a pointer to a Table structure representing a virtual-table. +** Return a pointer to the VTable object used by connection db to access +** this virtual-table, if one has been created, or NULL otherwise. */ -SQLITE_PRIVATE void sqlite3VtabUnlock(sqlite3 *db, sqlite3_vtab *pVtab){ -#ifndef SQLITE_DEBUG - UNUSED_PARAMETER(db); -#endif - assert( pVtab->nRef>0 ); - pVtab->nRef--; - assert(db); +SQLITE_PRIVATE VTable *sqlite3GetVTable(sqlite3 *db, Table *pTab){ + VTable *pVtab; + assert( IsVirtual(pTab) ); + for(pVtab=pTab->pVTable; pVtab && pVtab->db!=db; pVtab=pVtab->pNext); + return pVtab; +} + +/* +** Decrement the ref-count on a virtual table object. When the ref-count +** reaches zero, call the xDisconnect() method to delete the object. +*/ +SQLITE_PRIVATE void sqlite3VtabUnlock(VTable *pVTab){ + sqlite3 *db = pVTab->db; + + assert( db ); + assert( pVTab->nRef>0 ); assert( sqlite3SafetyCheckOk(db) ); - if( pVtab->nRef==0 ){ + + pVTab->nRef--; + if( pVTab->nRef==0 ){ + sqlite3_vtab *p = pVTab->pVtab; + if( p ){ #ifdef SQLITE_DEBUG - if( db->magic==SQLITE_MAGIC_BUSY ){ - (void)sqlite3SafetyOff(db); - pVtab->pModule->xDisconnect(pVtab); - (void)sqlite3SafetyOn(db); - } else + if( pVTab->db->magic==SQLITE_MAGIC_BUSY ){ + (void)sqlite3SafetyOff(db); + p->pModule->xDisconnect(p); + (void)sqlite3SafetyOn(db); + } else #endif - { - pVtab->pModule->xDisconnect(pVtab); + { + p->pModule->xDisconnect(p); + } + } + sqlite3DbFree(db, pVTab); + } +} + +/* +** Table p is a virtual table. This function moves all elements in the +** p->pVTable list to the sqlite3.pDisconnect lists of their associated +** database connections to be disconnected at the next opportunity. +** Except, if argument db is not NULL, then the entry associated with +** connection db is left in the p->pVTable list. +*/ +static VTable *vtabDisconnectAll(sqlite3 *db, Table *p){ + VTable *pRet = 0; + VTable *pVTable = p->pVTable; + p->pVTable = 0; + + /* Assert that the mutex (if any) associated with the BtShared database + ** that contains table p is held by the caller. See header comments + ** above function sqlite3VtabUnlockList() for an explanation of why + ** this makes it safe to access the sqlite3.pDisconnect list of any + ** database connection that may have an entry in the p->pVTable list. */ + assert( db==0 || + sqlite3BtreeHoldsMutex(db->aDb[sqlite3SchemaToIndex(db, p->pSchema)].pBt) + ); + + while( pVTable ){ + sqlite3 *db2 = pVTable->db; + VTable *pNext = pVTable->pNext; + assert( db2 ); + if( db2==db ){ + pRet = pVTable; + p->pVTable = pRet; + pRet->pNext = 0; + }else{ + pVTable->pNext = db2->pDisconnect; + db2->pDisconnect = pVTable; } + pVTable = pNext; + } + + assert( !db || pRet ); + return pRet; +} + + +/* +** Disconnect all the virtual table objects in the sqlite3.pDisconnect list. +** +** This function may only be called when the mutexes associated with all +** shared b-tree databases opened using connection db are held by the +** caller. This is done to protect the sqlite3.pDisconnect list. The +** sqlite3.pDisconnect list is accessed only as follows: +** +** 1) By this function. In this case, all BtShared mutexes and the mutex +** associated with the database handle itself must be held. +** +** 2) By function vtabDisconnectAll(), when it adds a VTable entry to +** the sqlite3.pDisconnect list. In this case either the BtShared mutex +** associated with the database the virtual table is stored in is held +** or, if the virtual table is stored in a non-sharable database, then +** the database handle mutex is held. +** +** As a result, a sqlite3.pDisconnect cannot be accessed simultaneously +** by multiple threads. It is thread-safe. +*/ +SQLITE_PRIVATE void sqlite3VtabUnlockList(sqlite3 *db){ + VTable *p = db->pDisconnect; + db->pDisconnect = 0; + + assert( sqlite3BtreeHoldsAllMutexes(db) ); + assert( sqlite3_mutex_held(db->mutex) ); + + if( p ){ + sqlite3ExpirePreparedStatements(db); + do { + VTable *pNext = p->pNext; + sqlite3VtabUnlock(p); + p = pNext; + }while( p ); } } @@ -81785,22 +85764,24 @@ ** Clear any and all virtual-table information from the Table record. ** This routine is called, for example, just before deleting the Table ** record. +** +** Since it is a virtual-table, the Table structure contains a pointer +** to the head of a linked list of VTable structures. Each VTable +** structure is associated with a single sqlite3* user of the schema. +** The reference count of the VTable structure associated with database +** connection db is decremented immediately (which may lead to the +** structure being xDisconnected and free). Any other VTable structures +** in the list are moved to the sqlite3.pDisconnect list of the associated +** database connection. */ SQLITE_PRIVATE void sqlite3VtabClear(Table *p){ - sqlite3_vtab *pVtab = p->pVtab; - Schema *pSchema = p->pSchema; - sqlite3 *db = pSchema ? pSchema->db : 0; - if( pVtab ){ - assert( p->pMod && p->pMod->pModule ); - sqlite3VtabUnlock(db, pVtab); - p->pVtab = 0; - } + vtabDisconnectAll(0, p); if( p->azModuleArg ){ int i; for(i=0; inModuleArg; i++){ - sqlite3DbFree(db, p->azModuleArg[i]); + sqlite3DbFree(p->dbMem, p->azModuleArg[i]); } - sqlite3DbFree(db, p->azModuleArg); + sqlite3DbFree(p->dbMem, p->azModuleArg); } } @@ -81845,11 +85826,6 @@ Table *pTable; /* The new virtual table */ sqlite3 *db; /* Database connection */ - if( pParse->db->flags & SQLITE_SharedCache ){ - sqlite3ErrorMsg(pParse, "Cannot use virtual tables in shared-cache mode"); - return; - } - sqlite3StartTable(pParse, pName1, pName2, 0, 0, 1, 0); pTable = pParse->pNewTable; if( pTable==0 ) return; @@ -81898,23 +85874,13 @@ ** has been completely parsed. */ SQLITE_PRIVATE void sqlite3VtabFinishParse(Parse *pParse, Token *pEnd){ - Table *pTab; /* The table being constructed */ - sqlite3 *db; /* The database connection */ - char *zModule; /* The module name of the table: USING modulename */ - Module *pMod = 0; + Table *pTab = pParse->pNewTable; /* The table being constructed */ + sqlite3 *db = pParse->db; /* The database connection */ + if( pTab==0 ) return; addArgumentToVtab(pParse); pParse->sArg.z = 0; - - /* Lookup the module name. */ - pTab = pParse->pNewTable; - if( pTab==0 ) return; - db = pParse->db; if( pTab->nModuleArg<1 ) return; - zModule = pTab->azModuleArg[0]; - pMod = (Module*)sqlite3HashFind(&db->aModule, zModule, - sqlite3Strlen30(zModule)); - pTab->pMod = pMod; /* If the CREATE VIRTUAL TABLE statement is being entered for the ** first time (in other words if the virtual table is actually being @@ -81965,9 +85931,10 @@ } /* If we are rereading the sqlite_master table create the in-memory - ** record of the table. If the module has already been registered, - ** also call the xConnect method here. - */ + ** record of the table. The xConnect() method is not called until + ** the first time the virtual table is used in an SQL statement. This + ** allows a schema that contains virtual tables to be loaded before + ** the required virtual table implementations are registered. */ else { Table *pOld; Schema *pSchema = pTab->pSchema; @@ -82021,9 +85988,8 @@ int (*xConstruct)(sqlite3*,void*,int,const char*const*,sqlite3_vtab**,char**), char **pzErr ){ + VTable *pVTable; int rc; - int rc2; - sqlite3_vtab *pVtab = 0; const char *const*azArg = (const char *const*)pTab->azModuleArg; int nArg = pTab->nModuleArg; char *zErr = 0; @@ -82033,22 +85999,23 @@ return SQLITE_NOMEM; } + pVTable = sqlite3DbMallocZero(db, sizeof(VTable)); + if( !pVTable ){ + sqlite3DbFree(db, zModuleName); + return SQLITE_NOMEM; + } + pVTable->db = db; + pVTable->pMod = pMod; + assert( !db->pVTab ); assert( xConstruct ); - db->pVTab = pTab; - rc = sqlite3SafetyOff(db); - assert( rc==SQLITE_OK ); - rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVtab, &zErr); - rc2 = sqlite3SafetyOn(db); + + /* Invoke the virtual table constructor */ + (void)sqlite3SafetyOff(db); + rc = xConstruct(db, pMod->pAux, nArg, azArg, &pVTable->pVtab, &zErr); + (void)sqlite3SafetyOn(db); if( rc==SQLITE_NOMEM ) db->mallocFailed = 1; - /* Justification of ALWAYS(): A correct vtab constructor must allocate - ** the sqlite3_vtab object if successful. */ - if( rc==SQLITE_OK && ALWAYS(pVtab) ){ - pVtab->pModule = pMod->pModule; - pVtab->nRef = 1; - pTab->pVtab = pVtab; - } if( SQLITE_OK!=rc ){ if( zErr==0 ){ @@ -82057,54 +86024,61 @@ *pzErr = sqlite3MPrintf(db, "%s", zErr); sqlite3DbFree(db, zErr); } - }else if( db->pVTab ){ - const char *zFormat = "vtable constructor did not declare schema: %s"; - *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); - rc = SQLITE_ERROR; - } - if( rc==SQLITE_OK ){ - rc = rc2; - } - db->pVTab = 0; - sqlite3DbFree(db, zModuleName); + sqlite3DbFree(db, pVTable); + }else if( ALWAYS(pVTable->pVtab) ){ + /* Justification of ALWAYS(): A correct vtab constructor must allocate + ** the sqlite3_vtab object if successful. */ + pVTable->pVtab->pModule = pMod->pModule; + pVTable->nRef = 1; + if( db->pVTab ){ + const char *zFormat = "vtable constructor did not declare schema: %s"; + *pzErr = sqlite3MPrintf(db, zFormat, pTab->zName); + sqlite3VtabUnlock(pVTable); + rc = SQLITE_ERROR; + }else{ + int iCol; + /* If everything went according to plan, link the new VTable structure + ** into the linked list headed by pTab->pVTable. Then loop through the + ** columns of the table to see if any of them contain the token "hidden". + ** If so, set the Column.isHidden flag and remove the token from + ** the type string. */ + pVTable->pNext = pTab->pVTable; + pTab->pVTable = pVTable; - /* If everything went according to plan, loop through the columns - ** of the table to see if any of them contain the token "hidden". - ** If so, set the Column.isHidden flag and remove the token from - ** the type string. - */ - if( rc==SQLITE_OK ){ - int iCol; - for(iCol=0; iColnCol; iCol++){ - char *zType = pTab->aCol[iCol].zType; - int nType; - int i = 0; - if( !zType ) continue; - nType = sqlite3Strlen30(zType); - if( sqlite3StrNICmp("hidden", zType, 6) || (zType[6] && zType[6]!=' ') ){ - for(i=0; inCol; iCol++){ + char *zType = pTab->aCol[iCol].zType; + int nType; + int i = 0; + if( !zType ) continue; + nType = sqlite3Strlen30(zType); + if( sqlite3StrNICmp("hidden", zType, 6)||(zType[6] && zType[6]!=' ') ){ + for(i=0; i0 ){ - assert(zType[i-1]==' '); - zType[i-1] = '\0'; + if( i0 ){ + assert(zType[i-1]==' '); + zType[i-1] = '\0'; + } + pTab->aCol[iCol].isHidden = 1; } - pTab->aCol[iCol].isHidden = 1; } } } + + sqlite3DbFree(db, zModuleName); + db->pVTab = 0; return rc; } @@ -82116,22 +86090,26 @@ ** This call is a no-op if table pTab is not a virtual table. */ SQLITE_PRIVATE int sqlite3VtabCallConnect(Parse *pParse, Table *pTab){ + sqlite3 *db = pParse->db; + const char *zMod; Module *pMod; - int rc = SQLITE_OK; + int rc; assert( pTab ); - if( (pTab->tabFlags & TF_Virtual)==0 || pTab->pVtab ){ + if( (pTab->tabFlags & TF_Virtual)==0 || sqlite3GetVTable(db, pTab) ){ return SQLITE_OK; } - pMod = pTab->pMod; + /* Locate the required virtual table module */ + zMod = pTab->azModuleArg[0]; + pMod = (Module*)sqlite3HashFind(&db->aModule, zMod, sqlite3Strlen30(zMod)); + if( !pMod ){ const char *zModule = pTab->azModuleArg[0]; sqlite3ErrorMsg(pParse, "no such module: %s", zModule); rc = SQLITE_ERROR; - } else { + }else{ char *zErr = 0; - sqlite3 *db = pParse->db; rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xConnect, &zErr); if( rc!=SQLITE_OK ){ sqlite3ErrorMsg(pParse, "%s", zErr); @@ -82143,14 +86121,14 @@ } /* -** Add the virtual table pVtab to the array sqlite3.aVTrans[]. +** Add the virtual table pVTab to the array sqlite3.aVTrans[]. */ -static int addToVTrans(sqlite3 *db, sqlite3_vtab *pVtab){ +static int addToVTrans(sqlite3 *db, VTable *pVTab){ const int ARRAY_INCR = 5; /* Grow the sqlite3.aVTrans array if required */ if( (db->nVTrans%ARRAY_INCR)==0 ){ - sqlite3_vtab **aVTrans; + VTable **aVTrans; int nBytes = sizeof(sqlite3_vtab *) * (db->nVTrans + ARRAY_INCR); aVTrans = sqlite3DbRealloc(db, (void *)db->aVTrans, nBytes); if( !aVTrans ){ @@ -82161,8 +86139,8 @@ } /* Add pVtab to the end of sqlite3.aVTrans */ - db->aVTrans[db->nVTrans++] = pVtab; - sqlite3VtabLock(pVtab); + db->aVTrans[db->nVTrans++] = pVTab; + sqlite3VtabLock(pVTab); return SQLITE_OK; } @@ -82178,19 +86156,21 @@ int rc = SQLITE_OK; Table *pTab; Module *pMod; - const char *zModule; + const char *zMod; pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName); - assert(pTab && (pTab->tabFlags & TF_Virtual)!=0 && !pTab->pVtab); - pMod = pTab->pMod; - zModule = pTab->azModuleArg[0]; + assert( pTab && (pTab->tabFlags & TF_Virtual)!=0 && !pTab->pVTable ); + + /* Locate the required virtual table module */ + zMod = pTab->azModuleArg[0]; + pMod = (Module*)sqlite3HashFind(&db->aModule, zMod, sqlite3Strlen30(zMod)); /* If the module has been registered and includes a Create method, ** invoke it now. If the module has not been registered, return an ** error. Otherwise, do nothing. */ if( !pMod ){ - *pzErr = sqlite3MPrintf(db, "no such module: %s", zModule); + *pzErr = sqlite3MPrintf(db, "no such module: %s", zMod); rc = SQLITE_ERROR; }else{ rc = vtabCallConstructor(db, pTab, pMod, pMod->pModule->xCreate, pzErr); @@ -82198,8 +86178,8 @@ /* Justification of ALWAYS(): The xConstructor method is required to ** create a valid sqlite3_vtab if it returns SQLITE_OK. */ - if( rc==SQLITE_OK && ALWAYS(pTab->pVtab) ){ - rc = addToVTrans(db, pTab->pVtab); + if( rc==SQLITE_OK && ALWAYS(sqlite3GetVTable(db, pTab)) ){ + rc = addToVTrans(db, sqlite3GetVTable(db, pTab)); } return rc; @@ -82224,7 +86204,7 @@ sqlite3_mutex_leave(db->mutex); return SQLITE_MISUSE; } - assert((pTab->tabFlags & TF_Virtual)!=0 && pTab->nCol==0 && pTab->aCol==0); + assert( (pTab->tabFlags & TF_Virtual)!=0 ); pParse = sqlite3StackAllocZero(db, sizeof(*pParse)); if( pParse==0 ){ @@ -82233,18 +86213,20 @@ pParse->declareVtab = 1; pParse->db = db; - if( - SQLITE_OK == sqlite3RunParser(pParse, zCreateTable, &zErr) && - pParse->pNewTable && - !pParse->pNewTable->pSelect && - (pParse->pNewTable->tabFlags & TF_Virtual)==0 + if( SQLITE_OK==sqlite3RunParser(pParse, zCreateTable, &zErr) + && pParse->pNewTable + && !db->mallocFailed + && !pParse->pNewTable->pSelect + && (pParse->pNewTable->tabFlags & TF_Virtual)==0 ){ - pTab->aCol = pParse->pNewTable->aCol; - pTab->nCol = pParse->pNewTable->nCol; - pParse->pNewTable->nCol = 0; - pParse->pNewTable->aCol = 0; + if( !pTab->aCol ){ + pTab->aCol = pParse->pNewTable->aCol; + pTab->nCol = pParse->pNewTable->nCol; + pParse->pNewTable->nCol = 0; + pParse->pNewTable->aCol = 0; + } db->pVTab = 0; - } else { + }else{ sqlite3Error(db, SQLITE_ERROR, zErr); sqlite3DbFree(db, zErr); rc = SQLITE_ERROR; @@ -82276,21 +86258,20 @@ Table *pTab; pTab = sqlite3FindTable(db, zTab, db->aDb[iDb].zName); - if( ALWAYS(pTab!=0 && pTab->pVtab!=0) ){ - int (*xDestroy)(sqlite3_vtab *pVTab) = pTab->pMod->pModule->xDestroy; + if( ALWAYS(pTab!=0 && pTab->pVTable!=0) ){ + VTable *p = vtabDisconnectAll(db, pTab); + rc = sqlite3SafetyOff(db); assert( rc==SQLITE_OK ); - rc = xDestroy(pTab->pVtab); + rc = p->pMod->pModule->xDestroy(p->pVtab); (void)sqlite3SafetyOn(db); + + /* Remove the sqlite3_vtab* from the aVTrans[] array, if applicable */ if( rc==SQLITE_OK ){ - int i; - for(i=0; inVTrans; i++){ - if( db->aVTrans[i]==pTab->pVtab ){ - db->aVTrans[i] = db->aVTrans[--db->nVTrans]; - break; - } - } - pTab->pVtab = 0; + assert( pTab->pVTable==p && p->pNext==0 ); + p->pVtab = 0; + pTab->pVTable = 0; + sqlite3VtabUnlock(p); } } @@ -82309,13 +86290,14 @@ int i; if( db->aVTrans ){ for(i=0; inVTrans; i++){ - sqlite3_vtab *pVtab = db->aVTrans[i]; - int (*x)(sqlite3_vtab *); - - assert( pVtab!=0 ); - x = *(int (**)(sqlite3_vtab *))((char *)pVtab->pModule + offset); - if( x ) x(pVtab); - sqlite3VtabUnlock(db, pVtab); + VTable *pVTab = db->aVTrans[i]; + sqlite3_vtab *p = pVTab->pVtab; + if( p ){ + int (*x)(sqlite3_vtab *); + x = *(int (**)(sqlite3_vtab *))((char *)p->pModule + offset); + if( x ) x(p); + } + sqlite3VtabUnlock(pVTab); } sqlite3DbFree(db, db->aVTrans); db->nVTrans = 0; @@ -82335,16 +86317,14 @@ int i; int rc = SQLITE_OK; int rcsafety; - sqlite3_vtab **aVTrans = db->aVTrans; + VTable **aVTrans = db->aVTrans; rc = sqlite3SafetyOff(db); db->aVTrans = 0; for(i=0; rc==SQLITE_OK && inVTrans; i++){ - sqlite3_vtab *pVtab = aVTrans[i]; int (*x)(sqlite3_vtab *); - assert( pVtab!=0 ); - x = pVtab->pModule->xSync; - if( x ){ + sqlite3_vtab *pVtab = aVTrans[i]->pVtab; + if( pVtab && (x = pVtab->pModule->xSync)!=0 ){ rc = x(pVtab); sqlite3DbFree(db, *pzErrmsg); *pzErrmsg = pVtab->zErrMsg; @@ -82386,7 +86366,7 @@ ** If the xBegin call is successful, place the sqlite3_vtab pointer ** in the sqlite3.aVTrans array. */ -SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *db, sqlite3_vtab *pVtab){ +SQLITE_PRIVATE int sqlite3VtabBegin(sqlite3 *db, VTable *pVTab){ int rc = SQLITE_OK; const sqlite3_module *pModule; @@ -82398,10 +86378,10 @@ if( sqlite3VtabInSync(db) ){ return SQLITE_LOCKED; } - if( !pVtab ){ + if( !pVTab ){ return SQLITE_OK; } - pModule = pVtab->pModule; + pModule = pVTab->pVtab->pModule; if( pModule->xBegin ){ int i; @@ -82409,15 +86389,15 @@ /* If pVtab is already in the aVTrans array, return early */ for(i=0; inVTrans; i++){ - if( db->aVTrans[i]==pVtab ){ + if( db->aVTrans[i]==pVTab ){ return SQLITE_OK; } } /* Invoke the xBegin method */ - rc = pModule->xBegin(pVtab); + rc = pModule->xBegin(pVTab->pVtab); if( rc==SQLITE_OK ){ - rc = addToVTrans(db, pVtab); + rc = addToVTrans(db, pVTab); } } return rc; @@ -82459,7 +86439,7 @@ pTab = pExpr->pTab; if( NEVER(pTab==0) ) return pDef; if( (pTab->tabFlags & TF_Virtual)==0 ) return pDef; - pVtab = pTab->pVtab; + pVtab = sqlite3GetVTable(db, pTab)->pVtab; assert( pVtab!=0 ); assert( pVtab->pModule!=0 ); pMod = (sqlite3_module *)pVtab->pModule; @@ -82483,7 +86463,7 @@ /* Create a new ephemeral function definition for the overloaded ** function */ pNew = sqlite3DbMallocZero(db, sizeof(*pNew) - + sqlite3Strlen30(pDef->zName) ); + + sqlite3Strlen30(pDef->zName) + 1); if( pNew==0 ){ return pDef; } @@ -82503,20 +86483,21 @@ ** is a no-op. */ SQLITE_PRIVATE void sqlite3VtabMakeWritable(Parse *pParse, Table *pTab){ + Parse *pToplevel = sqlite3ParseToplevel(pParse); int i, n; Table **apVtabLock; assert( IsVirtual(pTab) ); - for(i=0; inVtabLock; i++){ - if( pTab==pParse->apVtabLock[i] ) return; + for(i=0; inVtabLock; i++){ + if( pTab==pToplevel->apVtabLock[i] ) return; } - n = (pParse->nVtabLock+1)*sizeof(pParse->apVtabLock[0]); - apVtabLock = sqlite3_realloc(pParse->apVtabLock, n); + n = (pToplevel->nVtabLock+1)*sizeof(pToplevel->apVtabLock[0]); + apVtabLock = sqlite3_realloc(pToplevel->apVtabLock, n); if( apVtabLock ){ - pParse->apVtabLock = apVtabLock; - pParse->apVtabLock[pParse->nVtabLock++] = pTab; + pToplevel->apVtabLock = apVtabLock; + pToplevel->apVtabLock[pToplevel->nVtabLock++] = pTab; }else{ - pParse->db->mallocFailed = 1; + pToplevel->db->mallocFailed = 1; } } @@ -82541,8 +86522,6 @@ ** rows. Indices are selected and used to speed the search when doing ** so is applicable. Because this module is responsible for selecting ** indices, you might also think of this module as the "query optimizer". -** -** $Id: where.c,v 1.408 2009/06/16 14:15:22 shane Exp $ */ /* @@ -82720,6 +86699,7 @@ WherePlan plan; /* The lookup strategy */ double rCost; /* Overall cost of pursuing this search strategy */ double nRow; /* Estimated number of output rows */ + Bitmask used; /* Bitmask of cursors used by this plan */ }; /* @@ -83149,11 +87129,11 @@ static int isLikeOrGlob( Parse *pParse, /* Parsing and code generating context */ Expr *pExpr, /* Test this expression */ - int *pnPattern, /* Number of non-wildcard prefix characters */ + Expr **ppPrefix, /* Pointer to TK_STRING expression with pattern prefix */ int *pisComplete, /* True if the only wildcard is % in the last character */ int *pnoCase /* True if uppercase is equivalent to lowercase */ ){ - const char *z; /* String on RHS of LIKE operator */ + const char *z = 0; /* String on RHS of LIKE operator */ Expr *pRight, *pLeft; /* Right and left size of LIKE operator */ ExprList *pList; /* List of operands to the LIKE operator */ int c; /* One character in z[] */ @@ -83161,6 +87141,8 @@ char wc[3]; /* Wildcard characters */ CollSeq *pColl; /* Collating sequence for LHS */ sqlite3 *db = pParse->db; /* Database connection */ + sqlite3_value *pVal = 0; + int op; /* Opcode of pRight */ if( !sqlite3IsLikeFunction(db, pExpr, pnoCase, wc) ){ return 0; @@ -83169,35 +87151,77 @@ if( *pnoCase ) return 0; #endif pList = pExpr->x.pList; - pRight = pList->a[0].pExpr; - if( pRight->op!=TK_STRING ){ - return 0; - } pLeft = pList->a[1].pExpr; - if( pLeft->op!=TK_COLUMN ){ + if( pLeft->op!=TK_COLUMN || sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT ){ + /* IMP: R-02065-49465 The left-hand side of the LIKE or GLOB operator must + ** be the name of an indexed column with TEXT affinity. */ return 0; } + assert( pLeft->iColumn!=(-1) ); /* Because IPK never has AFF_TEXT */ pColl = sqlite3ExprCollSeq(pParse, pLeft); - assert( pColl!=0 || pLeft->iColumn==-1 ); - if( pColl==0 ) return 0; + assert( pColl!=0 ); /* Every non-IPK column has a collating sequence */ if( (pColl->type!=SQLITE_COLL_BINARY || *pnoCase) && (pColl->type!=SQLITE_COLL_NOCASE || !*pnoCase) ){ + /* IMP: R-09003-32046 For the GLOB operator, the column must use the + ** default BINARY collating sequence. + ** IMP: R-41408-28306 For the LIKE operator, if case_sensitive_like mode + ** is enabled then the column must use the default BINARY collating + ** sequence, or if case_sensitive_like mode is disabled then the column + ** must use the built-in NOCASE collating sequence. + */ return 0; } - if( sqlite3ExprAffinity(pLeft)!=SQLITE_AFF_TEXT ) return 0; - z = pRight->u.zToken; - if( ALWAYS(z) ){ + + pRight = pList->a[0].pExpr; + op = pRight->op; + if( op==TK_REGISTER ){ + op = pRight->op2; + } + if( op==TK_VARIABLE ){ + Vdbe *pReprepare = pParse->pReprepare; + pVal = sqlite3VdbeGetValue(pReprepare, pRight->iColumn, SQLITE_AFF_NONE); + if( pVal && sqlite3_value_type(pVal)==SQLITE_TEXT ){ + z = (char *)sqlite3_value_text(pVal); + } + sqlite3VdbeSetVarmask(pParse->pVdbe, pRight->iColumn); + assert( pRight->op==TK_VARIABLE || pRight->op==TK_REGISTER ); + }else if( op==TK_STRING ){ + z = pRight->u.zToken; + } + if( z ){ cnt = 0; while( (c=z[cnt])!=0 && c!=wc[0] && c!=wc[1] && c!=wc[2] ){ cnt++; } if( cnt!=0 && c!=0 && 255!=(u8)z[cnt-1] ){ + Expr *pPrefix; *pisComplete = z[cnt]==wc[0] && z[cnt+1]==0; - *pnPattern = cnt; - return 1; + pPrefix = sqlite3Expr(db, TK_STRING, z); + if( pPrefix ) pPrefix->u.zToken[cnt] = 0; + *ppPrefix = pPrefix; + if( op==TK_VARIABLE ){ + Vdbe *v = pParse->pVdbe; + sqlite3VdbeSetVarmask(v, pRight->iColumn); + if( *pisComplete && pRight->u.zToken[1] ){ + /* If the rhs of the LIKE expression is a variable, and the current + ** value of the variable means there is no need to invoke the LIKE + ** function, then no OP_Variable will be added to the program. + ** This causes problems for the sqlite3_bind_parameter_name() + ** API. To workaround them, add a dummy OP_Variable here. + */ + int r1 = sqlite3GetTempReg(pParse); + sqlite3ExprCodeTarget(pParse, pRight, r1); + sqlite3VdbeChangeP3(v, sqlite3VdbeCurrentAddr(v)-1, 0); + sqlite3ReleaseTempReg(pParse, r1); + } + } + }else{ + z = 0; } } - return 0; + + sqlite3ValueFree(pVal); + return (z!=0); } #endif /* SQLITE_OMIT_LIKE_OPTIMIZATION */ @@ -83578,10 +87602,10 @@ Expr *pExpr; /* The expression to be analyzed */ Bitmask prereqLeft; /* Prerequesites of the pExpr->pLeft */ Bitmask prereqAll; /* Prerequesites of pExpr */ - Bitmask extraRight = 0; - int nPattern; - int isComplete; - int noCase; + Bitmask extraRight = 0; /* */ + Expr *pStr1 = 0; /* RHS of LIKE/GLOB operator */ + int isComplete = 0; /* RHS of LIKE/GLOB ends with wildcard */ + int noCase = 0; /* LIKE/GLOB distinguishes case */ int op; /* Top-level operator. pExpr->op */ Parse *pParse = pWC->pParse; /* Parsing context */ sqlite3 *db = pParse->db; /* Database connection */ @@ -83701,6 +87725,7 @@ else if( pExpr->op==TK_OR ){ assert( pWC->op==TK_AND ); exprAnalyzeOrTerm(pSrc, pWC, idxTerm); + pTerm = &pWC->a[idxTerm]; } #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ @@ -83715,21 +87740,21 @@ ** The last character of the prefix "abc" is incremented to form the ** termination condition "abd". */ - if( isLikeOrGlob(pParse, pExpr, &nPattern, &isComplete, &noCase) - && pWC->op==TK_AND ){ - Expr *pLeft, *pRight; - Expr *pStr1, *pStr2; - Expr *pNewExpr1, *pNewExpr2; - int idxNew1, idxNew2; + if( pWC->op==TK_AND + && isLikeOrGlob(pParse, pExpr, &pStr1, &isComplete, &noCase) + ){ + Expr *pLeft; /* LHS of LIKE/GLOB operator */ + Expr *pStr2; /* Copy of pStr1 - RHS of LIKE/GLOB operator */ + Expr *pNewExpr1; + Expr *pNewExpr2; + int idxNew1; + int idxNew2; pLeft = pExpr->x.pList->a[1].pExpr; - pRight = pExpr->x.pList->a[0].pExpr; - pStr1 = sqlite3Expr(db, TK_STRING, pRight->u.zToken); - if( pStr1 ) pStr1->u.zToken[nPattern] = 0; pStr2 = sqlite3ExprDup(db, pStr1, 0); if( !db->mallocFailed ){ u8 c, *pC; /* Last character before the first wildcard */ - pC = (u8*)&pStr2->u.zToken[nPattern-1]; + pC = (u8*)&pStr2->u.zToken[sqlite3Strlen30(pStr2->u.zToken)-1]; c = *pC; if( noCase ){ /* The point is to increment the last character before the first @@ -83862,6 +87887,11 @@ nTerm = pOrderBy->nExpr; assert( nTerm>0 ); + /* Argument pIdx must either point to a 'real' named index structure, + ** or an index structure allocated on the stack by bestBtreeIndex() to + ** represent the rowid index that is part of every table. */ + assert( pIdx->zName || (pIdx->nColumn==1 && pIdx->aiColumn[0]==-1) ); + /* Match terms of the ORDER BY clause against columns of ** the index. ** @@ -83888,7 +87918,7 @@ if( !pColl ){ pColl = db->pDfltColl; } - if( inColumn ){ + if( pIdx->zName && inColumn ){ iColumn = pIdx->aiColumn[i]; if( iColumn==pIdx->pTable->iPKey ){ iColumn = -1; @@ -83917,7 +87947,7 @@ return 0; } } - assert( pIdx->aSortOrder!=0 ); + assert( pIdx->aSortOrder!=0 || iColumn==-1 ); assert( pTerm->sortOrder==0 || pTerm->sortOrder==1 ); assert( iSortOrder==0 || iSortOrder==1 ); termSortOrder = iSortOrder ^ pTerm->sortOrder; @@ -83961,30 +87991,6 @@ } /* -** Check table to see if the ORDER BY clause in pOrderBy can be satisfied -** by sorting in order of ROWID. Return true if so and set *pbRev to be -** true for reverse ROWID and false for forward ROWID order. -*/ -static int sortableByRowid( - int base, /* Cursor number for table to be sorted */ - ExprList *pOrderBy, /* The ORDER BY clause */ - WhereMaskSet *pMaskSet, /* Mapping from table cursors to bitmaps */ - int *pbRev /* Set to 1 if ORDER BY is DESC */ -){ - Expr *p; - - assert( pOrderBy!=0 ); - assert( pOrderBy->nExpr>0 ); - p = pOrderBy->a[0].pExpr; - if( p->op==TK_COLUMN && p->iTable==base && p->iColumn==-1 - && !referencesOtherTables(pOrderBy, pMaskSet, 1, base) ){ - *pbRev = pOrderBy->a[0].sortOrder; - return 1; - } - return 0; -} - -/* ** Prepare a crude estimate of the logarithm of the input value. ** The results need not be exact. This is only used for estimating ** the total cost of performing operations with O(logN) or O(NlogN) @@ -84084,6 +88090,7 @@ int flags = WHERE_MULTI_OR; double rTotal = 0; double nRow = 0; + Bitmask used = 0; for(pOrTerm=pOrWC->a; pOrTerm=pCost->rCost ) break; } @@ -84123,6 +88131,7 @@ if( rTotalrCost ){ pCost->rCost = rTotal; pCost->nRow = nRow; + pCost->used = used; pCost->plan.wsFlags = flags; pCost->plan.u.pTerm = pTerm; } @@ -84251,7 +88260,7 @@ ** that this is required. */ static int vtabBestIndex(Parse *pParse, Table *pTab, sqlite3_index_info *p){ - sqlite3_vtab *pVtab = pTab->pVtab; + sqlite3_vtab *pVtab = sqlite3GetVTable(pParse->db, pTab)->pVtab; int i; int rc; @@ -84348,7 +88357,7 @@ ** sqlite3ViewGetColumnNames() would have picked up the error. */ assert( pTab->azModuleArg && pTab->azModuleArg[0] ); - assert( pTab->pVtab ); + assert( sqlite3GetVTable(pParse->db, pTab) ); /* Set the aConstraint[].usable fields and initialize all ** output variables to zero. @@ -84375,7 +88384,7 @@ for(i=0; inConstraint; i++, pIdxCons++){ j = pIdxCons->iTermOffset; pTerm = &pWC->a[j]; - pIdxCons->usable = (pTerm->prereqRight & notReady)==0 ?1:0; + pIdxCons->usable = (pTerm->prereqRight¬Ready) ? 0 : 1; } memset(pUsage, 0, sizeof(pUsage[0])*pIdxInfo->nConstraint); if( pIdxInfo->needToFreeIdxStr ){ @@ -84396,6 +88405,13 @@ return; } + pIdxCons = *(struct sqlite3_index_constraint**)&pIdxInfo->aConstraint; + for(i=0; inConstraint; i++){ + if( pUsage[i].argvIndex>0 ){ + pCost->used |= pWC->a[pIdxCons[i].iTermOffset].prereqRight; + } + } + /* The cost is not allowed to be larger than SQLITE_BIG_DBL (the ** inital value of lowestCost in this loop. If it is, then the ** (costaSample; + int i = 0; + int eType = sqlite3_value_type(pVal); + + if( eType==SQLITE_INTEGER || eType==SQLITE_FLOAT ){ + double r = sqlite3_value_double(pVal); + for(i=0; i=SQLITE_TEXT || aSample[i].u.r>r ) break; + } + }else{ + sqlite3 *db = pParse->db; + CollSeq *pColl; + const u8 *z; + int n; + + /* pVal comes from sqlite3ValueFromExpr() so the type cannot be NULL */ + assert( eType==SQLITE_TEXT || eType==SQLITE_BLOB ); + + if( eType==SQLITE_BLOB ){ + z = (const u8 *)sqlite3_value_blob(pVal); + pColl = db->pDfltColl; + assert( pColl->enc==SQLITE_UTF8 ); + }else{ + pColl = sqlite3GetCollSeq(db, SQLITE_UTF8, 0, *pIdx->azColl); + if( pColl==0 ){ + sqlite3ErrorMsg(pParse, "no such collation sequence: %s", + *pIdx->azColl); + return SQLITE_ERROR; + } + z = (const u8 *)sqlite3ValueText(pVal, pColl->enc); + if( !z ){ + return SQLITE_NOMEM; + } + assert( z && pColl && pColl->xCmp ); + } + n = sqlite3ValueBytes(pVal, pColl->enc); + + for(i=0; ienc!=SQLITE_UTF8 ){ + int nSample; + char *zSample = sqlite3Utf8to16( + db, pColl->enc, aSample[i].u.z, aSample[i].nByte, &nSample + ); + if( !zSample ){ + assert( db->mallocFailed ); + return SQLITE_NOMEM; + } + r = pColl->xCmp(pColl->pUser, nSample, zSample, n, z); + sqlite3DbFree(db, zSample); + }else +#endif + { + r = pColl->xCmp(pColl->pUser, aSample[i].nByte, aSample[i].u.z, n, z); + } + if( r>0 ) break; + } + } + + assert( i>=0 && i<=SQLITE_INDEX_SAMPLES ); + *piRegion = i; + } + return SQLITE_OK; +} +#endif /* #ifdef SQLITE_ENABLE_STAT2 */ + +/* +** If expression pExpr represents a literal value, set *pp to point to +** an sqlite3_value structure containing the same value, with affinity +** aff applied to it, before returning. It is the responsibility of the +** caller to eventually release this structure by passing it to +** sqlite3ValueFree(). +** +** If the current parse is a recompile (sqlite3Reprepare()) and pExpr +** is an SQL variable that currently has a non-NULL value bound to it, +** create an sqlite3_value structure containing this value, again with +** affinity aff applied to it, instead. +** +** If neither of the above apply, set *pp to NULL. +** +** If an error occurs, return an error code. Otherwise, SQLITE_OK. +*/ +#ifdef SQLITE_ENABLE_STAT2 +static int valueFromExpr( + Parse *pParse, + Expr *pExpr, + u8 aff, + sqlite3_value **pp +){ + /* The evalConstExpr() function will have already converted any TK_VARIABLE + ** expression involved in an comparison into a TK_REGISTER. */ + assert( pExpr->op!=TK_VARIABLE ); + if( pExpr->op==TK_REGISTER && pExpr->op2==TK_VARIABLE ){ + int iVar = pExpr->iColumn; + sqlite3VdbeSetVarmask(pParse->pVdbe, iVar); + *pp = sqlite3VdbeGetValue(pParse->pReprepare, iVar, aff); + return SQLITE_OK; + } + return sqlite3ValueFromExpr(pParse->db, pExpr, SQLITE_UTF8, aff, pp); +} +#endif + +/* +** This function is used to estimate the number of rows that will be visited +** by scanning an index for a range of values. The range may have an upper +** bound, a lower bound, or both. The WHERE clause terms that set the upper +** and lower bounds are represented by pLower and pUpper respectively. For +** example, assuming that index p is on t1(a): +** +** ... FROM t1 WHERE a > ? AND a < ? ... +** |_____| |_____| +** | | +** pLower pUpper +** +** If either of the upper or lower bound is not present, then NULL is passed in +** place of the corresponding WhereTerm. +** +** The nEq parameter is passed the index of the index column subject to the +** range constraint. Or, equivalently, the number of equality constraints +** optimized by the proposed index scan. For example, assuming index p is +** on t1(a, b), and the SQL query is: +** +** ... FROM t1 WHERE a = ? AND b > ? AND b < ? ... +** +** then nEq should be passed the value 1 (as the range restricted column, +** b, is the second left-most column of the index). Or, if the query is: +** +** ... FROM t1 WHERE a > ? AND a < ? ... +** +** then nEq should be passed 0. +** +** The returned value is an integer between 1 and 100, inclusive. A return +** value of 1 indicates that the proposed range scan is expected to visit +** approximately 1/100th (1%) of the rows selected by the nEq equality +** constraints (if any). A return value of 100 indicates that it is expected +** that the range scan will visit every row (100%) selected by the equality +** constraints. +** +** In the absence of sqlite_stat2 ANALYZE data, each range inequality +** reduces the search space by 2/3rds. Hence a single constraint (x>?) +** results in a return of 33 and a range constraint (x>? AND xaCol[] of the range-compared column */ + WhereTerm *pLower, /* Lower bound on the range. ex: "x>123" Might be NULL */ + WhereTerm *pUpper, /* Upper bound on the range. ex: "x<455" Might be NULL */ + int *piEst /* OUT: Return value */ +){ + int rc = SQLITE_OK; + +#ifdef SQLITE_ENABLE_STAT2 + + if( nEq==0 && p->aSample ){ + sqlite3_value *pLowerVal = 0; + sqlite3_value *pUpperVal = 0; + int iEst; + int iLower = 0; + int iUpper = SQLITE_INDEX_SAMPLES; + u8 aff = p->pTable->aCol[p->aiColumn[0]].affinity; + + if( pLower ){ + Expr *pExpr = pLower->pExpr->pRight; + rc = valueFromExpr(pParse, pExpr, aff, &pLowerVal); + } + if( rc==SQLITE_OK && pUpper ){ + Expr *pExpr = pUpper->pExpr->pRight; + rc = valueFromExpr(pParse, pExpr, aff, &pUpperVal); + } + + if( rc!=SQLITE_OK || (pLowerVal==0 && pUpperVal==0) ){ + sqlite3ValueFree(pLowerVal); + sqlite3ValueFree(pUpperVal); + goto range_est_fallback; + }else if( pLowerVal==0 ){ + rc = whereRangeRegion(pParse, p, pUpperVal, &iUpper); + if( pLower ) iLower = iUpper/2; + }else if( pUpperVal==0 ){ + rc = whereRangeRegion(pParse, p, pLowerVal, &iLower); + if( pUpper ) iUpper = (iLower + SQLITE_INDEX_SAMPLES + 1)/2; + }else{ + rc = whereRangeRegion(pParse, p, pUpperVal, &iUpper); + if( rc==SQLITE_OK ){ + rc = whereRangeRegion(pParse, p, pLowerVal, &iLower); + } + } + + iEst = iUpper - iLower; + testcase( iEst==SQLITE_INDEX_SAMPLES ); + assert( iEst<=SQLITE_INDEX_SAMPLES ); + if( iEst<1 ){ + iEst = 1; + } + + sqlite3ValueFree(pLowerVal); + sqlite3ValueFree(pUpperVal); + *piEst = (iEst * 100)/SQLITE_INDEX_SAMPLES; + return rc; + } +range_est_fallback: +#else + UNUSED_PARAMETER(pParse); + UNUSED_PARAMETER(p); + UNUSED_PARAMETER(nEq); +#endif + assert( pLower || pUpper ); + if( pLower && pUpper ){ + *piEst = 11; + }else{ + *piEst = 33; + } + return rc; +} + + +/* ** Find the query plan for accessing a particular table. Write the ** best query plan and its cost into the WhereCost object supplied as the ** last parameter. @@ -84458,290 +88719,314 @@ ExprList *pOrderBy, /* The ORDER BY clause */ WhereCost *pCost /* Lowest cost query plan */ ){ - WhereTerm *pTerm; /* A single term of the WHERE clause */ int iCur = pSrc->iCursor; /* The cursor of the table to be accessed */ Index *pProbe; /* An index we are evaluating */ - int rev; /* True to scan in reverse order */ - int wsFlags; /* Flags associated with pProbe */ - int nEq; /* Number of == or IN constraints */ - int eqTermMask; /* Mask of valid equality operators */ - double cost; /* Cost of using pProbe */ - double nRow; /* Estimated number of rows in result set */ - int i; /* Loop counter */ - - WHERETRACE(("bestIndex: tbl=%s notReady=%llx\n", pSrc->pTab->zName,notReady)); - pProbe = pSrc->pTab->pIndex; - if( pSrc->notIndexed ){ - pProbe = 0; - } - - /* If the table has no indices and there are no terms in the where - ** clause that refer to the ROWID, then we will never be able to do - ** anything other than a full table scan on this table. We might as - ** well put it first in the join order. That way, perhaps it can be - ** referenced by other tables in the join. - */ + Index *pIdx; /* Copy of pProbe, or zero for IPK index */ + int eqTermMask; /* Current mask of valid equality operators */ + int idxEqTermMask; /* Index mask of valid equality operators */ + Index sPk; /* A fake index object for the primary key */ + unsigned int aiRowEstPk[2]; /* The aiRowEst[] value for the sPk index */ + int aiColumnPk = -1; /* The aColumn[] value for the sPk index */ + int wsFlagMask; /* Allowed flags in pCost->plan.wsFlag */ + + /* Initialize the cost to a worst-case value */ memset(pCost, 0, sizeof(*pCost)); - if( pProbe==0 && - findTerm(pWC, iCur, -1, 0, WO_EQ|WO_IN|WO_LT|WO_LE|WO_GT|WO_GE,0)==0 && - (pOrderBy==0 || !sortableByRowid(iCur, pOrderBy, pWC->pMaskSet, &rev)) ){ - if( pParse->db->flags & SQLITE_ReverseOrder ){ - /* For application testing, randomly reverse the output order for - ** SELECT statements that omit the ORDER BY clause. This will help - ** to find cases where - */ - pCost->plan.wsFlags |= WHERE_REVERSE; - } - return; - } pCost->rCost = SQLITE_BIG_DBL; - /* Check for a rowid=EXPR or rowid IN (...) constraints. If there was - ** an INDEXED BY clause attached to this table, skip this step. - */ - if( !pSrc->pIndex ){ - pTerm = findTerm(pWC, iCur, -1, notReady, WO_EQ|WO_IN, 0); - if( pTerm ){ - Expr *pExpr; - pCost->plan.wsFlags = WHERE_ROWID_EQ; - if( pTerm->eOperator & WO_EQ ){ - /* Rowid== is always the best pick. Look no further. Because only - ** a single row is generated, output is always in sorted order */ - pCost->plan.wsFlags = WHERE_ROWID_EQ | WHERE_UNIQUE; - pCost->plan.nEq = 1; - WHERETRACE(("... best is rowid\n")); - pCost->rCost = 0; - pCost->nRow = 1; - return; - }else if( !ExprHasProperty((pExpr = pTerm->pExpr), EP_xIsSelect) - && pExpr->x.pList - ){ - /* Rowid IN (LIST): cost is NlogN where N is the number of list - ** elements. */ - pCost->rCost = pCost->nRow = pExpr->x.pList->nExpr; - pCost->rCost *= estLog(pCost->rCost); - }else{ - /* Rowid IN (SELECT): cost is NlogN where N is the number of rows - ** in the result of the inner select. We have no way to estimate - ** that value so make a wild guess. */ - pCost->nRow = 100; - pCost->rCost = 200; - } - WHERETRACE(("... rowid IN cost: %.9g\n", pCost->rCost)); - } - - /* Estimate the cost of a table scan. If we do not know how many - ** entries are in the table, use 1 million as a guess. - */ - cost = pProbe ? pProbe->aiRowEst[0] : 1000000; - WHERETRACE(("... table scan base cost: %.9g\n", cost)); - wsFlags = WHERE_ROWID_RANGE; - - /* Check for constraints on a range of rowids in a table scan. - */ - pTerm = findTerm(pWC, iCur, -1, notReady, WO_LT|WO_LE|WO_GT|WO_GE, 0); - if( pTerm ){ - if( findTerm(pWC, iCur, -1, notReady, WO_LT|WO_LE, 0) ){ - wsFlags |= WHERE_TOP_LIMIT; - cost /= 3; /* Guess that rowidEXPR eliminates two-thirds of rows */ - } - WHERETRACE(("... rowid range reduces cost to %.9g\n", cost)); - }else{ - wsFlags = 0; - } - nRow = cost; - - /* If the table scan does not satisfy the ORDER BY clause, increase - ** the cost by NlogN to cover the expense of sorting. */ - if( pOrderBy ){ - if( sortableByRowid(iCur, pOrderBy, pWC->pMaskSet, &rev) ){ - wsFlags |= WHERE_ORDERBY|WHERE_ROWID_RANGE; - if( rev ){ - wsFlags |= WHERE_REVERSE; - } - }else{ - cost += cost*estLog(cost); - WHERETRACE(("... sorting increases cost to %.9g\n", cost)); - } - }else if( pParse->db->flags & SQLITE_ReverseOrder ){ - /* For application testing, randomly reverse the output order for - ** SELECT statements that omit the ORDER BY clause. This will help - ** to find cases where - */ - wsFlags |= WHERE_REVERSE; - } - - /* Remember this case if it is the best so far */ - if( costrCost ){ - pCost->rCost = cost; - pCost->nRow = nRow; - pCost->plan.wsFlags = wsFlags; - } - } - - bestOrClauseIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost); - /* If the pSrc table is the right table of a LEFT JOIN then we may not ** use an index to satisfy IS NULL constraints on that table. This is ** because columns might end up being NULL if the table does not match - ** a circumstance which the index cannot help us discover. Ticket #2177. */ - if( (pSrc->jointype & JT_LEFT)!=0 ){ - eqTermMask = WO_EQ|WO_IN; + if( pSrc->jointype & JT_LEFT ){ + idxEqTermMask = WO_EQ|WO_IN; }else{ - eqTermMask = WO_EQ|WO_IN|WO_ISNULL; + idxEqTermMask = WO_EQ|WO_IN|WO_ISNULL; } - /* Look at each index. - */ if( pSrc->pIndex ){ - pProbe = pSrc->pIndex; - } - for(; pProbe; pProbe=(pSrc->pIndex ? 0 : pProbe->pNext)){ - double inMultiplier = 1; /* Number of equality look-ups needed */ - int inMultIsEst = 0; /* True if inMultiplier is an estimate */ - - WHERETRACE(("... index %s:\n", pProbe->zName)); - - /* Count the number of columns in the index that are satisfied - ** by x=EXPR or x IS NULL constraints or x IN (...) constraints. - ** For a term of the form x=EXPR or x IS NULL we only have to do - ** a single binary search. But for x IN (...) we have to do a - ** number of binary searched - ** equal to the number of entries on the RHS of the IN operator. - ** The inMultipler variable with try to estimate the number of - ** binary searches needed. - */ - wsFlags = 0; - for(i=0; inColumn; i++){ - int j = pProbe->aiColumn[i]; - pTerm = findTerm(pWC, iCur, j, notReady, eqTermMask, pProbe); + /* An INDEXED BY clause specifies a particular index to use */ + pIdx = pProbe = pSrc->pIndex; + wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE); + eqTermMask = idxEqTermMask; + }else{ + /* There is no INDEXED BY clause. Create a fake Index object to + ** represent the primary key */ + Index *pFirst; /* Any other index on the table */ + memset(&sPk, 0, sizeof(Index)); + sPk.nColumn = 1; + sPk.aiColumn = &aiColumnPk; + sPk.aiRowEst = aiRowEstPk; + aiRowEstPk[1] = 1; + sPk.onError = OE_Replace; + sPk.pTable = pSrc->pTab; + pFirst = pSrc->pTab->pIndex; + if( pSrc->notIndexed==0 ){ + sPk.pNext = pFirst; + } + /* The aiRowEstPk[0] is an estimate of the total number of rows in the + ** table. Get this information from the ANALYZE information if it is + ** available. If not available, assume the table 1 million rows in size. + */ + if( pFirst ){ + assert( pFirst->aiRowEst!=0 ); /* Allocated together with pFirst */ + aiRowEstPk[0] = pFirst->aiRowEst[0]; + }else{ + aiRowEstPk[0] = 1000000; + } + pProbe = &sPk; + wsFlagMask = ~( + WHERE_COLUMN_IN|WHERE_COLUMN_EQ|WHERE_COLUMN_NULL|WHERE_COLUMN_RANGE + ); + eqTermMask = WO_EQ|WO_IN; + pIdx = 0; + } + + /* Loop over all indices looking for the best one to use + */ + for(; pProbe; pIdx=pProbe=pProbe->pNext){ + const unsigned int * const aiRowEst = pProbe->aiRowEst; + double cost; /* Cost of using pProbe */ + double nRow; /* Estimated number of rows in result set */ + int rev; /* True to scan in reverse order */ + int wsFlags = 0; + Bitmask used = 0; + + /* The following variables are populated based on the properties of + ** scan being evaluated. They are then used to determine the expected + ** cost and number of rows returned. + ** + ** nEq: + ** Number of equality terms that can be implemented using the index. + ** + ** nInMul: + ** The "in-multiplier". This is an estimate of how many seek operations + ** SQLite must perform on the index in question. For example, if the + ** WHERE clause is: + ** + ** WHERE a IN (1, 2, 3) AND b IN (4, 5, 6) + ** + ** SQLite must perform 9 lookups on an index on (a, b), so nInMul is + ** set to 9. Given the same schema and either of the following WHERE + ** clauses: + ** + ** WHERE a = 1 + ** WHERE a >= 2 + ** + ** nInMul is set to 1. + ** + ** If there exists a WHERE term of the form "x IN (SELECT ...)", then + ** the sub-select is assumed to return 25 rows for the purposes of + ** determining nInMul. + ** + ** bInEst: + ** Set to true if there was at least one "x IN (SELECT ...)" term used + ** in determining the value of nInMul. + ** + ** nBound: + ** An estimate on the amount of the table that must be searched. A + ** value of 100 means the entire table is searched. Range constraints + ** might reduce this to a value less than 100 to indicate that only + ** a fraction of the table needs searching. In the absence of + ** sqlite_stat2 ANALYZE data, a single inequality reduces the search + ** space to 1/3rd its original size. So an x>? constraint reduces + ** nBound to 33. Two constraints (x>? AND xnColumn; nEq++){ + WhereTerm *pTerm; /* A single term of the WHERE clause */ + int j = pProbe->aiColumn[nEq]; + pTerm = findTerm(pWC, iCur, j, notReady, eqTermMask, pIdx); if( pTerm==0 ) break; - wsFlags |= WHERE_COLUMN_EQ; + wsFlags |= (WHERE_COLUMN_EQ|WHERE_ROWID_EQ); if( pTerm->eOperator & WO_IN ){ Expr *pExpr = pTerm->pExpr; wsFlags |= WHERE_COLUMN_IN; if( ExprHasProperty(pExpr, EP_xIsSelect) ){ - inMultiplier *= 25; - inMultIsEst = 1; + nInMul *= 25; + bInEst = 1; }else if( pExpr->x.pList ){ - inMultiplier *= pExpr->x.pList->nExpr + 1; + nInMul *= pExpr->x.pList->nExpr + 1; } }else if( pTerm->eOperator & WO_ISNULL ){ wsFlags |= WHERE_COLUMN_NULL; } + used |= pTerm->prereqRight; } - nRow = pProbe->aiRowEst[i] * inMultiplier; - /* If inMultiplier is an estimate and that estimate results in an - ** nRow it that is more than half number of rows in the table, - ** then reduce inMultipler */ - if( inMultIsEst && nRow*2 > pProbe->aiRowEst[0] ){ - nRow = pProbe->aiRowEst[0]/2; - inMultiplier = nRow/pProbe->aiRowEst[i]; - } - cost = nRow + inMultiplier*estLog(pProbe->aiRowEst[0]); - nEq = i; - if( pProbe->onError!=OE_None && nEq==pProbe->nColumn ){ - testcase( wsFlags & WHERE_COLUMN_IN ); - testcase( wsFlags & WHERE_COLUMN_NULL ); - if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){ - wsFlags |= WHERE_UNIQUE; - } - } - WHERETRACE(("...... nEq=%d inMult=%.9g nRow=%.9g cost=%.9g\n", - nEq, inMultiplier, nRow, cost)); - /* Look for range constraints. Assume that each range constraint - ** makes the search space 1/3rd smaller. - */ + /* Determine the value of nBound. */ if( nEqnColumn ){ int j = pProbe->aiColumn[nEq]; - pTerm = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pProbe); - if( pTerm ){ - wsFlags |= WHERE_COLUMN_RANGE; - if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pProbe) ){ + if( findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE|WO_GT|WO_GE, pIdx) ){ + WhereTerm *pTop = findTerm(pWC, iCur, j, notReady, WO_LT|WO_LE, pIdx); + WhereTerm *pBtm = findTerm(pWC, iCur, j, notReady, WO_GT|WO_GE, pIdx); + whereRangeScanEst(pParse, pProbe, nEq, pBtm, pTop, &nBound); + if( pTop ){ wsFlags |= WHERE_TOP_LIMIT; - cost /= 3; - nRow /= 3; + used |= pTop->prereqRight; } - if( findTerm(pWC, iCur, j, notReady, WO_GT|WO_GE, pProbe) ){ + if( pBtm ){ wsFlags |= WHERE_BTM_LIMIT; - cost /= 3; - nRow /= 3; + used |= pBtm->prereqRight; } - WHERETRACE(("...... range reduces nRow to %.9g and cost to %.9g\n", - nRow, cost)); + wsFlags |= (WHERE_COLUMN_RANGE|WHERE_ROWID_RANGE); + } + }else if( pProbe->onError!=OE_None ){ + testcase( wsFlags & WHERE_COLUMN_IN ); + testcase( wsFlags & WHERE_COLUMN_NULL ); + if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 ){ + wsFlags |= WHERE_UNIQUE; } } - /* Add the additional cost of sorting if that is a factor. - */ + /* If there is an ORDER BY clause and the index being considered will + ** naturally scan rows in the required order, set the appropriate flags + ** in wsFlags. Otherwise, if there is an ORDER BY clause but the index + ** will scan rows in a different order, set the bSort variable. */ if( pOrderBy ){ if( (wsFlags & (WHERE_COLUMN_IN|WHERE_COLUMN_NULL))==0 - && isSortingIndex(pParse,pWC->pMaskSet,pProbe,iCur,pOrderBy,nEq,&rev) + && isSortingIndex(pParse,pWC->pMaskSet,pProbe,iCur,pOrderBy,nEq,&rev) ){ - if( wsFlags==0 ){ - wsFlags = WHERE_COLUMN_RANGE; - } - wsFlags |= WHERE_ORDERBY; - if( rev ){ - wsFlags |= WHERE_REVERSE; - } + wsFlags |= WHERE_ROWID_RANGE|WHERE_COLUMN_RANGE|WHERE_ORDERBY; + wsFlags |= (rev ? WHERE_REVERSE : 0); }else{ - cost += cost*estLog(cost); - WHERETRACE(("...... orderby increases cost to %.9g\n", cost)); + bSort = 1; } - }else if( wsFlags!=0 && (pParse->db->flags & SQLITE_ReverseOrder)!=0 ){ - /* For application testing, randomly reverse the output order for - ** SELECT statements that omit the ORDER BY clause. This will help - ** to find cases where - */ - wsFlags |= WHERE_REVERSE; } - /* Check to see if we can get away with using just the index without - ** ever reading the table. If that is the case, then halve the - ** cost of this index. - */ - if( wsFlags && pSrc->colUsed < (((Bitmask)1)<<(BMS-1)) ){ + /* If currently calculating the cost of using an index (not the IPK + ** index), determine if all required column data may be obtained without + ** seeking to entries in the main table (i.e. if the index is a covering + ** index for this query). If it is, set the WHERE_IDX_ONLY flag in + ** wsFlags. Otherwise, set the bLookup variable to true. */ + if( pIdx && wsFlags ){ Bitmask m = pSrc->colUsed; int j; - for(j=0; jnColumn; j++){ - int x = pProbe->aiColumn[j]; + for(j=0; jnColumn; j++){ + int x = pIdx->aiColumn[j]; if( xaiRowEst[0] ){ + nRow = aiRowEst[0]/2; + nInMul = (int)(nRow / aiRowEst[nEq]); + } + + /* Assume constant cost to access a row and logarithmic cost to + ** do a binary search. Hence, the initial cost is the number of output + ** rows plus log2(table-size) times the number of binary searches. + */ + cost = nRow + nInMul*estLog(aiRowEst[0]); + + /* Adjust the number of rows and the cost downward to reflect rows + ** that are excluded by range constraints. + */ + nRow = (nRow * (double)nBound) / (double)100; + cost = (cost * (double)nBound) / (double)100; + + /* Add in the estimated cost of sorting the result + */ + if( bSort ){ + cost += cost*estLog(cost); + } + + /* If all information can be taken directly from the index, we avoid + ** doing table lookups. This reduces the cost by half. (Not really - + ** this needs to be fixed.) */ - if( wsFlags!=0 && cost < pCost->rCost ){ + if( pIdx && bLookup==0 ){ + cost /= (double)2; + } + /**** Cost of using this index has now been computed ****/ + + WHERETRACE(( + "tbl=%s idx=%s nEq=%d nInMul=%d nBound=%d bSort=%d bLookup=%d" + " wsFlags=%d (nRow=%.2f cost=%.2f)\n", + pSrc->pTab->zName, (pIdx ? pIdx->zName : "ipk"), + nEq, nInMul, nBound, bSort, bLookup, wsFlags, nRow, cost + )); + + /* If this index is the best we have seen so far, then record this + ** index and its cost in the pCost structure. + */ + if( (!pIdx || wsFlags) && costrCost ){ pCost->rCost = cost; pCost->nRow = nRow; - pCost->plan.wsFlags = wsFlags; + pCost->used = used; + pCost->plan.wsFlags = (wsFlags&wsFlagMask); pCost->plan.nEq = nEq; - assert( pCost->plan.wsFlags & WHERE_INDEXED ); - pCost->plan.u.pIdx = pProbe; + pCost->plan.u.pIdx = pIdx; } + + /* If there was an INDEXED BY clause, then only that one index is + ** considered. */ + if( pSrc->pIndex ) break; + + /* Reset masks for the next index in the loop */ + wsFlagMask = ~(WHERE_ROWID_EQ|WHERE_ROWID_RANGE); + eqTermMask = idxEqTermMask; } - /* Report the best result - */ + /* If there is no ORDER BY clause and the SQLITE_ReverseOrder flag + ** is set, then reverse the order that the index will be scanned + ** in. This is used for application testing, to help find cases + ** where application behaviour depends on the (undefined) order that + ** SQLite outputs rows in in the absence of an ORDER BY clause. */ + if( !pOrderBy && pParse->db->flags & SQLITE_ReverseOrder ){ + pCost->plan.wsFlags |= WHERE_REVERSE; + } + + assert( pOrderBy || (pCost->plan.wsFlags&WHERE_ORDERBY)==0 ); + assert( pCost->plan.u.pIdx==0 || (pCost->plan.wsFlags&WHERE_ROWID_EQ)==0 ); + assert( pSrc->pIndex==0 + || pCost->plan.u.pIdx==0 + || pCost->plan.u.pIdx==pSrc->pIndex + ); + + WHERETRACE(("best index is: %s\n", + (pCost->plan.u.pIdx ? pCost->plan.u.pIdx->zName : "ipk") + )); + + bestOrClauseIndex(pParse, pWC, pSrc, notReady, pOrderBy, pCost); pCost->plan.wsFlags |= eqTermMask; - WHERETRACE(("best index is %s, cost=%.9g, nrow=%.9g, wsFlags=%x, nEq=%d\n", - (pCost->plan.wsFlags & WHERE_INDEXED)!=0 ? - pCost->plan.u.pIdx->zName : "(none)", pCost->nRow, - pCost->rCost, pCost->plan.wsFlags, pCost->plan.nEq)); } /* @@ -84812,15 +89097,40 @@ } /* -** Apply the affinities associated with the first n columns of index -** pIdx to the values in the n registers starting at base. +** Code an OP_Affinity opcode to apply the column affinity string zAff +** to the n registers starting at base. +** +** As an optimization, SQLITE_AFF_NONE entries (which are no-ops) at the +** beginning and end of zAff are ignored. If all entries in zAff are +** SQLITE_AFF_NONE, then no code gets generated. +** +** This routine makes its own copy of zAff so that the caller is free +** to modify zAff after this routine returns. */ -static void codeApplyAffinity(Parse *pParse, int base, int n, Index *pIdx){ +static void codeApplyAffinity(Parse *pParse, int base, int n, char *zAff){ + Vdbe *v = pParse->pVdbe; + if( zAff==0 ){ + assert( pParse->db->mallocFailed ); + return; + } + assert( v!=0 ); + + /* Adjust base and n to skip over SQLITE_AFF_NONE entries at the beginning + ** and end of the affinity string. + */ + while( n>0 && zAff[0]==SQLITE_AFF_NONE ){ + n--; + base++; + zAff++; + } + while( n>1 && zAff[n-1]==SQLITE_AFF_NONE ){ + n--; + } + + /* Code the OP_Affinity opcode if there is anything left to do. */ if( n>0 ){ - Vdbe *v = pParse->pVdbe; - assert( v!=0 ); sqlite3VdbeAddOp2(v, OP_Affinity, base, n); - sqlite3IndexAffinityStr(v, pIdx); + sqlite3VdbeChangeP4(v, -1, zAff, n); sqlite3ExprCacheAffinityChange(pParse, base, n); } } @@ -84893,7 +89203,7 @@ /* ** Generate code that will evaluate all == and IN constraints for an -** index. The values for all constraints are left on the stack. +** index. ** ** For example, consider table t1(a,b,c,d,e,f) with index i1(a,b,c). ** Suppose the WHERE clause is this: a==5 AND b IN (1,2,3) AND c>5 AND c<10 @@ -84905,7 +89215,8 @@ ** ** In the example above nEq==2. But this subroutine works for any value ** of nEq including 0. If nEq==0, this routine is nearly a no-op. -** The only thing it does is allocate the pLevel->iMem memory cell. +** The only thing it does is allocate the pLevel->iMem memory cell and +** compute the affinity string. ** ** This routine always allocates at least one memory cell and returns ** the index of that memory cell. The code that @@ -84913,13 +89224,29 @@ ** key value of the loop. If one or more IN operators appear, then ** this routine allocates an additional nEq memory cells for internal ** use. +** +** Before returning, *pzAff is set to point to a buffer containing a +** copy of the column affinity string of the index allocated using +** sqlite3DbMalloc(). Except, entries in the copy of the string associated +** with equality constraints that use NONE affinity are set to +** SQLITE_AFF_NONE. This is to deal with SQL such as the following: +** +** CREATE TABLE t1(a TEXT PRIMARY KEY, b); +** SELECT ... FROM t1 AS t2, t1 WHERE t1.a = t2.b; +** +** In the example above, the index on t1(a) has TEXT affinity. But since +** the right hand side of the equality constraint (t2.b) has NONE affinity, +** no conversion should be attempted before using a t2.b value as part of +** a key to search the index. Hence the first byte in the returned affinity +** string in this example would be set to SQLITE_AFF_NONE. */ static int codeAllEqualityTerms( Parse *pParse, /* Parsing context */ WhereLevel *pLevel, /* Which nested loop of the FROM we are coding */ WhereClause *pWC, /* The WHERE clause */ Bitmask notReady, /* Which parts of FROM have not yet been coded */ - int nExtraReg /* Number of extra registers to allocate */ + int nExtraReg, /* Number of extra registers to allocate */ + char **pzAff /* OUT: Set to point to affinity string */ ){ int nEq = pLevel->plan.nEq; /* The number of == or IN constraints to code */ Vdbe *v = pParse->pVdbe; /* The vm under construction */ @@ -84929,6 +89256,7 @@ int j; /* Loop counter */ int regBase; /* Base register */ int nReg; /* Number of registers to allocate */ + char *zAff; /* Affinity string to return */ /* This module is only called on query plans that use an index. */ assert( pLevel->plan.wsFlags & WHERE_INDEXED ); @@ -84940,6 +89268,11 @@ nReg = pLevel->plan.nEq + nExtraReg; pParse->nMem += nReg; + zAff = sqlite3DbStrDup(pParse->db, sqlite3IndexAffinityStr(v, pIdx)); + if( !zAff ){ + pParse->db->mallocFailed = 1; + } + /* Evaluate the equality constraints */ assert( pIdx->nColumn>=nEq ); @@ -84961,9 +89294,19 @@ testcase( pTerm->eOperator & WO_ISNULL ); testcase( pTerm->eOperator & WO_IN ); if( (pTerm->eOperator & (WO_ISNULL|WO_IN))==0 ){ - sqlite3VdbeAddOp2(v, OP_IsNull, regBase+j, pLevel->addrBrk); + Expr *pRight = pTerm->pExpr->pRight; + sqlite3ExprCodeIsNullJump(v, pRight, regBase+j, pLevel->addrBrk); + if( zAff ){ + if( sqlite3CompareAffinity(pRight, zAff[j])==SQLITE_AFF_NONE ){ + zAff[j] = SQLITE_AFF_NONE; + } + if( sqlite3ExprNeedsNoAffinityChange(pRight, zAff[j]) ){ + zAff[j] = SQLITE_AFF_NONE; + } + } } } + *pzAff = zAff; return regBase; } @@ -85039,6 +89382,7 @@ const struct sqlite3_index_constraint *aConstraint = pVtabIdx->aConstraint; + sqlite3ExprCachePush(pParse); iReg = sqlite3GetTempRange(pParse, nConstraint+2); for(j=1; j<=nConstraint; j++){ for(k=0; kp1 = iCur; pLevel->p2 = sqlite3VdbeCurrentAddr(v); sqlite3ReleaseTempRange(pParse, iReg, nConstraint+2); + sqlite3ExprCachePop(pParse, 1); }else #endif /* SQLITE_OMIT_VIRTUALTABLE */ @@ -85219,6 +89564,7 @@ int iIdxCur; /* The VDBE cursor for the index */ int nExtraReg = 0; /* Number of extra registers needed */ int op; /* Instruction opcode */ + char *zAff; pIdx = pLevel->plan.u.pIdx; iIdxCur = pLevel->iIdxCur; @@ -85258,10 +89604,11 @@ ** and store the values of those terms in an array of registers ** starting at regBase. */ - regBase = codeAllEqualityTerms(pParse, pLevel, pWC, notReady, nExtraReg); + regBase = codeAllEqualityTerms( + pParse, pLevel, pWC, notReady, nExtraReg, &zAff + ); addrNxt = pLevel->addrNxt; - /* If we are doing a reverse order scan on an ascending index, or ** a forward order scan on a descending index, interchange the ** start and end terms (pRangeStart and pRangeEnd). @@ -85281,8 +89628,20 @@ /* Seek the index cursor to the start of the range. */ nConstraint = nEq; if( pRangeStart ){ - sqlite3ExprCode(pParse, pRangeStart->pExpr->pRight, regBase+nEq); - sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); + Expr *pRight = pRangeStart->pExpr->pRight; + sqlite3ExprCode(pParse, pRight, regBase+nEq); + sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + if( zAff ){ + if( sqlite3CompareAffinity(pRight, zAff[nConstraint])==SQLITE_AFF_NONE){ + /* Since the comparison is to be performed with no conversions + ** applied to the operands, set the affinity to apply to pRight to + ** SQLITE_AFF_NONE. */ + zAff[nConstraint] = SQLITE_AFF_NONE; + } + if( sqlite3ExprNeedsNoAffinityChange(pRight, zAff[nConstraint]) ){ + zAff[nConstraint] = SQLITE_AFF_NONE; + } + } nConstraint++; }else if( isMinQuery ){ sqlite3VdbeAddOp2(v, OP_Null, 0, regBase+nEq); @@ -85290,7 +89649,7 @@ startEq = 0; start_constraints = 1; } - codeApplyAffinity(pParse, regBase, nConstraint, pIdx); + codeApplyAffinity(pParse, regBase, nConstraint, zAff); op = aStartOp[(start_constraints<<2) + (startEq<<1) + bRev]; assert( op!=0 ); testcase( op==OP_Rewind ); @@ -85299,20 +89658,32 @@ testcase( op==OP_SeekGe ); testcase( op==OP_SeekLe ); testcase( op==OP_SeekLt ); - sqlite3VdbeAddOp4(v, op, iIdxCur, addrNxt, regBase, - SQLITE_INT_TO_PTR(nConstraint), P4_INT32); + sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); /* Load the value for the inequality constraint at the end of the ** range (if any). */ nConstraint = nEq; if( pRangeEnd ){ - sqlite3ExprCacheRemove(pParse, regBase+nEq); - sqlite3ExprCode(pParse, pRangeEnd->pExpr->pRight, regBase+nEq); - sqlite3VdbeAddOp2(v, OP_IsNull, regBase+nEq, addrNxt); - codeApplyAffinity(pParse, regBase, nEq+1, pIdx); + Expr *pRight = pRangeEnd->pExpr->pRight; + sqlite3ExprCacheRemove(pParse, regBase+nEq, 1); + sqlite3ExprCode(pParse, pRight, regBase+nEq); + sqlite3ExprCodeIsNullJump(v, pRight, regBase+nEq, addrNxt); + if( zAff ){ + if( sqlite3CompareAffinity(pRight, zAff[nConstraint])==SQLITE_AFF_NONE){ + /* Since the comparison is to be performed with no conversions + ** applied to the operands, set the affinity to apply to pRight to + ** SQLITE_AFF_NONE. */ + zAff[nConstraint] = SQLITE_AFF_NONE; + } + if( sqlite3ExprNeedsNoAffinityChange(pRight, zAff[nConstraint]) ){ + zAff[nConstraint] = SQLITE_AFF_NONE; + } + } + codeApplyAffinity(pParse, regBase, nEq+1, zAff); nConstraint++; } + sqlite3DbFree(pParse->db, zAff); /* Top of the loop body */ pLevel->p2 = sqlite3VdbeCurrentAddr(v); @@ -85323,8 +89694,7 @@ testcase( op==OP_IdxGE ); testcase( op==OP_IdxLT ); if( op!=OP_Noop ){ - sqlite3VdbeAddOp4(v, op, iIdxCur, addrNxt, regBase, - SQLITE_INT_TO_PTR(nConstraint), P4_INT32); + sqlite3VdbeAddOp4Int(v, op, iIdxCur, addrNxt, regBase, nConstraint); sqlite3VdbeChangeP5(v, endEq!=bRev ?1:0); } @@ -85401,13 +89771,14 @@ */ WhereClause *pOrWc; /* The OR-clause broken out into subterms */ WhereTerm *pFinal; /* Final subterm within the OR-clause. */ - SrcList oneTab; /* Shortened table list */ + SrcList *pOrTab; /* Shortened table list or OR-clause generation */ int regReturn = ++pParse->nMem; /* Register used with OP_Gosub */ int regRowset = 0; /* Register for RowSet object */ int regRowid = 0; /* Register holding rowid */ int iLoopBody = sqlite3VdbeMakeLabel(v); /* Start of loop body */ int iRetInit; /* Address of regReturn init */ + int untestedTerms = 0; /* Some terms not completely tested */ int ii; pTerm = pLevel->plan.u.pTerm; @@ -85416,11 +89787,30 @@ assert( (pTerm->wtFlags & TERM_ORINFO)!=0 ); pOrWc = &pTerm->u.pOrInfo->wc; pFinal = &pOrWc->a[pOrWc->nTerm-1]; + pLevel->op = OP_Return; + pLevel->p1 = regReturn; - /* Set up a SrcList containing just the table being scanned by this loop. */ - oneTab.nSrc = 1; - oneTab.nAlloc = 1; - oneTab.a[0] = *pTabItem; + /* Set up a new SrcList ni pOrTab containing the table being scanned + ** by this loop in the a[0] slot and all notReady tables in a[1..] slots. + ** This becomes the SrcList in the recursive call to sqlite3WhereBegin(). + */ + if( pWInfo->nLevel>1 ){ + int nNotReady; /* The number of notReady tables */ + struct SrcList_item *origSrc; /* Original list of tables */ + nNotReady = pWInfo->nLevel - iLevel - 1; + pOrTab = sqlite3StackAllocRaw(pParse->db, + sizeof(*pOrTab)+ nNotReady*sizeof(pOrTab->a[0])); + if( pOrTab==0 ) return notReady; + pOrTab->nAlloc = (i16)(nNotReady + 1); + pOrTab->nSrc = pOrTab->nAlloc; + memcpy(pOrTab->a, pTabItem, sizeof(*pTabItem)); + origSrc = pWInfo->pTabList->a; + for(k=1; k<=nNotReady; k++){ + memcpy(&pOrTab->a[k], &origSrc[pLevel[k].iFrom], sizeof(pOrTab->a[k])); + } + }else{ + pOrTab = pWInfo->pTabList; + } /* Initialize the rowset register to contain NULL. An SQL NULL is ** equivalent to an empty rowset. @@ -85445,33 +89835,38 @@ if( pOrTerm->leftCursor==iCur || pOrTerm->eOperator==WO_AND ){ WhereInfo *pSubWInfo; /* Info for single OR-term scan */ /* Loop through table entries that match term pOrTerm. */ - pSubWInfo = sqlite3WhereBegin(pParse, &oneTab, pOrTerm->pExpr, 0, - WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE | WHERE_FORCE_TABLE); + pSubWInfo = sqlite3WhereBegin(pParse, pOrTab, pOrTerm->pExpr, 0, + WHERE_OMIT_OPEN | WHERE_OMIT_CLOSE | + WHERE_FORCE_TABLE | WHERE_ONETABLE_ONLY); if( pSubWInfo ){ if( (wctrlFlags & WHERE_DUPLICATES_OK)==0 ){ int iSet = ((ii==pOrWc->nTerm-1)?-1:ii); int r; r = sqlite3ExprCodeGetColumn(pParse, pTabItem->pTab, -1, iCur, - regRowid, 0); - sqlite3VdbeAddOp4(v, OP_RowSetTest, regRowset, - sqlite3VdbeCurrentAddr(v)+2, - r, SQLITE_INT_TO_PTR(iSet), P4_INT32); + regRowid); + sqlite3VdbeAddOp4Int(v, OP_RowSetTest, regRowset, + sqlite3VdbeCurrentAddr(v)+2, r, iSet); } sqlite3VdbeAddOp2(v, OP_Gosub, regReturn, iLoopBody); + /* The pSubWInfo->untestedTerms flag means that this OR term + ** contained one or more AND term from a notReady table. The + ** terms from the notReady table could not be tested and will + ** need to be tested later. + */ + if( pSubWInfo->untestedTerms ) untestedTerms = 1; + /* Finish the loop through table entries that match term pOrTerm. */ sqlite3WhereEnd(pSubWInfo); } } } sqlite3VdbeChangeP1(v, iRetInit, sqlite3VdbeCurrentAddr(v)); - /* sqlite3VdbeAddOp2(v, OP_Null, 0, regRowset); */ sqlite3VdbeAddOp2(v, OP_Goto, 0, pLevel->addrBrk); sqlite3VdbeResolveLabel(v, iLoopBody); - pLevel->op = OP_Return; - pLevel->p1 = regReturn; - disableTerm(pLevel, pTerm); + if( pWInfo->nLevel>1 ) sqlite3StackFree(pParse->db, pOrTab); + if( !untestedTerms ) disableTerm(pLevel, pTerm); }else #endif /* SQLITE_OMIT_OR_OPTIMIZATION */ @@ -85499,7 +89894,12 @@ testcase( pTerm->wtFlags & TERM_VIRTUAL ); testcase( pTerm->wtFlags & TERM_CODED ); if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; - if( (pTerm->prereqAll & notReady)!=0 ) continue; + if( (pTerm->prereqAll & notReady)!=0 ){ + testcase( pWInfo->untestedTerms==0 + && (pWInfo->wctrlFlags & WHERE_ONETABLE_ONLY)!=0 ); + pWInfo->untestedTerms = 1; + continue; + } pE = pTerm->pExpr; assert( pE!=0 ); if( pLevel->iLeftJoin && !ExprHasProperty(pE, EP_FromJoin) ){ @@ -85522,7 +89922,10 @@ testcase( pTerm->wtFlags & TERM_VIRTUAL ); testcase( pTerm->wtFlags & TERM_CODED ); if( pTerm->wtFlags & (TERM_VIRTUAL|TERM_CODED) ) continue; - if( (pTerm->prereqAll & notReady)!=0 ) continue; + if( (pTerm->prereqAll & notReady)!=0 ){ + assert( pWInfo->untestedTerms ); + continue; + } assert( pTerm->pExpr ); sqlite3ExprIfFalse(pParse, pTerm->pExpr, addrCont, SQLITE_JUMPIFNULL); pTerm->wtFlags |= TERM_CODED; @@ -85665,6 +90068,7 @@ ){ int i; /* Loop counter */ int nByteWInfo; /* Num. bytes allocated for WhereInfo struct */ + int nTabList; /* Number of elements in pTabList */ WhereInfo *pWInfo; /* Will become the return value of this function */ Vdbe *v = pParse->pVdbe; /* The virtual database engine */ Bitmask notReady; /* Cursors that are not yet positioned */ @@ -85684,6 +90088,13 @@ return 0; } + /* This function normally generates a nested loop for all tables in + ** pTabList. But if the WHERE_ONETABLE_ONLY flag is set, then we should + ** only generate code for the first table in pTabList and assume that + ** any cursors associated with subsequent tables are uninitialized. + */ + nTabList = (wctrlFlags & WHERE_ONETABLE_ONLY) ? 1 : pTabList->nSrc; + /* Allocate and initialize the WhereInfo structure that will become the ** return value. A single allocation is used to store the WhereInfo ** struct, the contents of WhereInfo.a[], the WhereClause structure @@ -85692,7 +90103,7 @@ ** some architectures. Hence the ROUND8() below. */ db = pParse->db; - nByteWInfo = ROUND8(sizeof(WhereInfo)+(pTabList->nSrc-1)*sizeof(WhereLevel)); + nByteWInfo = ROUND8(sizeof(WhereInfo)+(nTabList-1)*sizeof(WhereLevel)); pWInfo = sqlite3DbMallocZero(db, nByteWInfo + sizeof(WhereClause) + @@ -85701,7 +90112,7 @@ if( db->mallocFailed ){ goto whereBeginError; } - pWInfo->nLevel = pTabList->nSrc; + pWInfo->nLevel = nTabList; pWInfo->pParse = pParse; pWInfo->pTabList = pTabList; pWInfo->iBreak = sqlite3VdbeMakeLabel(v); @@ -85720,7 +90131,7 @@ /* Special case: a WHERE clause that is constant. Evaluate the ** expression and either jump over all of the code or fall thru. */ - if( pWhere && (pTabList->nSrc==0 || sqlite3ExprIsConstantNotJoin(pWhere)) ){ + if( pWhere && (nTabList==0 || sqlite3ExprIsConstantNotJoin(pWhere)) ){ sqlite3ExprIfFalse(pParse, pWhere, pWInfo->iBreak, SQLITE_JUMPIFNULL); pWhere = 0; } @@ -85740,6 +90151,11 @@ ** to virtual table cursors are set. This is used to selectively disable ** the OR-to-IN transformation in exprAnalyzeOrTerm(). It is not helpful ** with virtual tables. + ** + ** Note that bitmasks are created for all pTabList->nSrc tables in + ** pTabList, not just the first nTabList tables. nTabList is normally + ** equal to pTabList->nSrc but might be shortened to 1 if the + ** WHERE_ONETABLE_ONLY flag is set. */ assert( pWC->vmask==0 && pMaskSet->n==0 ); for(i=0; inSrc; i++){ @@ -85791,48 +90207,88 @@ pLevel = pWInfo->a; andFlags = ~0; WHERETRACE(("*** Optimizer Start ***\n")); - for(i=iFrom=0, pLevel=pWInfo->a; inSrc; i++, pLevel++){ + for(i=iFrom=0, pLevel=pWInfo->a; ia[j]; jnSrc; j++, pTabItem++){ - int doNotReorder; /* True if this table should not be reordered */ - WhereCost sCost; /* Cost information from best[Virtual]Index() */ - ExprList *pOrderBy; /* ORDER BY clause for index to optimize */ - - doNotReorder = (pTabItem->jointype & (JT_LEFT|JT_CROSS))!=0; - if( once && doNotReorder ) break; - m = getMask(pMaskSet, pTabItem->iCursor); - if( (m & notReady)==0 ){ - if( j==iFrom ) iFrom++; - continue; - } - pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0); - assert( pTabItem->pTab ); + /* Loop through the remaining entries in the FROM clause to find the + ** next nested loop. The FROM clause entries may be iterated through + ** either once or twice. + ** + ** The first iteration, which is always performed, searches for the + ** FROM clause entry that permits the lowest-cost, "optimal" scan. In + ** this context an optimal scan is one that uses the same strategy + ** for the given FROM clause entry as would be selected if the entry + ** were used as the innermost nested loop. In other words, a table + ** is chosen such that the cost of running that table cannot be reduced + ** by waiting for other tables to run first. + ** + ** The second iteration is only performed if no optimal scan strategies + ** were found by the first. This iteration is used to search for the + ** lowest cost scan overall. + ** + ** Previous versions of SQLite performed only the second iteration - + ** the next outermost loop was always that with the lowest overall + ** cost. However, this meant that SQLite could select the wrong plan + ** for scripts such as the following: + ** + ** CREATE TABLE t1(a, b); + ** CREATE TABLE t2(c, d); + ** SELECT * FROM t2, t1 WHERE t2.rowid = t1.a; + ** + ** The best strategy is to iterate through table t1 first. However it + ** is not possible to determine this with a simple greedy algorithm. + ** However, since the cost of a linear scan through table t2 is the same + ** as the cost of a linear scan through table t1, a simple greedy + ** algorithm may choose to use t2 for the outer loop, which is a much + ** costlier approach. + */ + for(isOptimal=1; isOptimal>=0 && bestJ<0; isOptimal--){ + Bitmask mask = (isOptimal ? 0 : notReady); + assert( (nTabList-iFrom)>1 || isOptimal ); + for(j=iFrom, pTabItem=&pTabList->a[j]; jjointype & (JT_LEFT|JT_CROSS))!=0; + if( j!=iFrom && doNotReorder ) break; + m = getMask(pMaskSet, pTabItem->iCursor); + if( (m & notReady)==0 ){ + if( j==iFrom ) iFrom++; + continue; + } + pOrderBy = ((i==0 && ppOrderBy )?*ppOrderBy:0); + + assert( pTabItem->pTab ); #ifndef SQLITE_OMIT_VIRTUALTABLE - if( IsVirtual(pTabItem->pTab) ){ - sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo; - bestVirtualIndex(pParse, pWC, pTabItem, notReady, pOrderBy, &sCost, pp); - }else + if( IsVirtual(pTabItem->pTab) ){ + sqlite3_index_info **pp = &pWInfo->a[j].pIdxInfo; + bestVirtualIndex(pParse, pWC, pTabItem, mask, pOrderBy, &sCost, pp); + }else #endif - { - bestBtreeIndex(pParse, pWC, pTabItem, notReady, pOrderBy, &sCost); - } - if( once==0 || sCost.rCost=0 ); assert( notReady & getMask(pMaskSet, pTabList->a[bestJ].iCursor) ); WHERETRACE(("*** Optimizer selects table %d for loop %d\n", bestJ, pLevel-pWInfo->a)); @@ -85894,7 +90350,7 @@ ** searching those tables. */ sqlite3CodeVerifySchema(pParse, -1); /* Insert the cookie verifier Goto */ - for(i=0, pLevel=pWInfo->a; inSrc; i++, pLevel++){ + for(i=0, pLevel=pWInfo->a; ia[pLevel->iFrom]; pTab = pTabItem->pTab; - iDb = sqlite3SchemaToIndex(pParse->db, pTab->pSchema); + iDb = sqlite3SchemaToIndex(db, pTab->pSchema); if( (pTab->tabFlags & TF_Ephemeral)!=0 || pTab->pSelect ) continue; #ifndef SQLITE_OMIT_VIRTUALTABLE if( (pLevel->plan.wsFlags & WHERE_VIRTUALTABLE)!=0 ){ + const char *pVTab = (const char *)sqlite3GetVTable(db, pTab); int iCur = pTabItem->iCursor; - sqlite3VdbeAddOp4(v, OP_VOpen, iCur, 0, 0, - (const char*)pTab->pVtab, P4_VTAB); + sqlite3VdbeAddOp4(v, OP_VOpen, iCur, 0, 0, pVTab, P4_VTAB); }else #endif if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 @@ -85946,7 +90402,8 @@ Bitmask b = pTabItem->colUsed; int n = 0; for(; b; b=b>>1, n++){} - sqlite3VdbeChangeP4(v, sqlite3VdbeCurrentAddr(v)-1, SQLITE_INT_TO_PTR(n), P4_INT32); + sqlite3VdbeChangeP4(v, sqlite3VdbeCurrentAddr(v)-1, + SQLITE_INT_TO_PTR(n), P4_INT32); assert( n<=pTab->nCol ); } }else{ @@ -85972,7 +90429,7 @@ ** program. */ notReady = ~(Bitmask)0; - for(i=0; inSrc; i++){ + for(i=0; iiContinue = pWInfo->a[i].addrCont; } @@ -85984,7 +90441,7 @@ ** the index is listed as "{}". If the primary key is used the ** index name is '*'. */ - for(i=0; inSrc; i++){ + for(i=0; ia[i]; @@ -86052,7 +90509,7 @@ /* Generate loop termination code. */ sqlite3ExprCacheClear(pParse); - for(i=pTabList->nSrc-1; i>=0; i--){ + for(i=pWInfo->nLevel-1; i>=0; i--){ pLevel = &pWInfo->a[i]; sqlite3VdbeResolveLabel(v, pLevel->addrCont); if( pLevel->op!=OP_Noop ){ @@ -86074,7 +90531,11 @@ if( pLevel->iLeftJoin ){ int addr; addr = sqlite3VdbeAddOp1(v, OP_IfPos, pLevel->iLeftJoin); - sqlite3VdbeAddOp1(v, OP_NullRow, pTabList->a[i].iCursor); + assert( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 + || (pLevel->plan.wsFlags & WHERE_INDEXED)!=0 ); + if( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 ){ + sqlite3VdbeAddOp1(v, OP_NullRow, pTabList->a[i].iCursor); + } if( pLevel->iIdxCur>=0 ){ sqlite3VdbeAddOp1(v, OP_NullRow, pLevel->iIdxCur); } @@ -86094,7 +90555,8 @@ /* Close all of the cursors that were opened by sqlite3WhereBegin. */ - for(i=0, pLevel=pWInfo->a; inSrc; i++, pLevel++){ + assert( pWInfo->nLevel==1 || pWInfo->nLevel==pTabList->nSrc ); + for(i=0, pLevel=pWInfo->a; inLevel; i++, pLevel++){ struct SrcList_item *pTabItem = &pTabList->a[pLevel->iFrom]; Table *pTab = pTabItem->pTab; assert( pTab!=0 ); @@ -86125,7 +90587,6 @@ int k, j, last; VdbeOp *pOp; Index *pIdx = pLevel->plan.u.pIdx; - int useIndexOnly = pLevel->plan.wsFlags & WHERE_IDX_ONLY; assert( pIdx!=0 ); pOp = sqlite3VdbeGetOp(v, pWInfo->iTop); @@ -86140,12 +90601,11 @@ break; } } - assert(!useIndexOnly || jnColumn); + assert( (pLevel->plan.wsFlags & WHERE_IDX_ONLY)==0 + || jnColumn ); }else if( pOp->opcode==OP_Rowid ){ pOp->p1 = pLevel->iIdxCur; pOp->opcode = OP_IdxRowid; - }else if( pOp->opcode==OP_NullRow && useIndexOnly ){ - pOp->opcode = OP_Noop; } } } @@ -86161,6 +90621,12 @@ /************** Begin file parse.c *******************************************/ /* Driver template for the LEMON parser generator. ** The author disclaims copyright to this source code. +** +** This version of "lempar.c" is modified, slightly, for use by SQLite. +** The only modifications are the addition of a couple of NEVER() +** macros to disable tests that are needed in the case of a general +** LALR(1) grammar but which are always false in the +** specific grammar used by SQLite. */ /* First off, code is included that follows the "include" declaration ** in the input grammar file. */ @@ -86260,6 +90726,17 @@ pOut->zEnd = &pPostOp->z[pPostOp->n]; } + /* A routine to convert a binary TK_IS or TK_ISNOT expression into a + ** unary TK_ISNULL or TK_NOTNULL expression. */ + static void binaryToUnaryIfNull(Parse *pParse, Expr *pY, Expr *pA, int op){ + sqlite3 *db = pParse->db; + if( db->mallocFailed==0 && pY->op==TK_NULL ){ + pA->op = (u8)op; + sqlite3ExprDelete(db, pA->pRight); + pA->pRight = 0; + } + } + /* Construct an expression node for a unary prefix operator */ static void spanUnaryPrefix( @@ -86323,25 +90800,26 @@ ** defined, then do no error processing. */ #define YYCODETYPE unsigned char -#define YYNOCODE 252 +#define YYNOCODE 254 #define YYACTIONTYPE unsigned short int -#define YYWILDCARD 65 +#define YYWILDCARD 67 #define sqlite3ParserTOKENTYPE Token typedef union { int yyinit; sqlite3ParserTOKENTYPE yy0; - Expr* yy72; - TriggerStep* yy145; - ExprList* yy148; - SrcList* yy185; - ExprSpan yy190; - int yy194; - Select* yy243; - IdList* yy254; - struct TrigEvent yy332; - struct LimitVal yy354; - struct LikeOp yy392; - struct {int value; int mask;} yy497; + Select* yy3; + ExprList* yy14; + SrcList* yy65; + struct LikeOp yy96; + Expr* yy132; + u8 yy186; + int yy328; + ExprSpan yy346; + struct TrigEvent yy378; + IdList* yy408; + struct {int value; int mask;} yy429; + TriggerStep* yy473; + struct LimitVal yy476; } YYMINORTYPE; #ifndef YYSTACKDEPTH #define YYSTACKDEPTH 100 @@ -86350,8 +90828,8 @@ #define sqlite3ParserARG_PDECL ,Parse *pParse #define sqlite3ParserARG_FETCH Parse *pParse = yypParser->pParse #define sqlite3ParserARG_STORE yypParser->pParse = pParse -#define YYNSTATE 619 -#define YYNRULE 324 +#define YYNSTATE 631 +#define YYNRULE 330 #define YYFALLBACK 1 #define YY_NO_ACTION (YYNSTATE+YYNRULE+2) #define YY_ACCEPT_ACTION (YYNSTATE+YYNRULE+1) @@ -86421,457 +90899,473 @@ ** shifting non-terminals after a reduce. ** yy_default[] Default action for each state. */ +#define YY_ACTTAB_COUNT (1543) static const YYACTIONTYPE yy_action[] = { - /* 0 */ 305, 944, 176, 618, 2, 150, 214, 441, 24, 24, - /* 10 */ 24, 24, 490, 26, 26, 26, 26, 27, 27, 28, - /* 20 */ 28, 28, 29, 216, 415, 416, 212, 415, 416, 448, - /* 30 */ 454, 31, 26, 26, 26, 26, 27, 27, 28, 28, - /* 40 */ 28, 29, 216, 30, 485, 32, 134, 23, 22, 311, - /* 50 */ 458, 459, 455, 455, 25, 25, 24, 24, 24, 24, - /* 60 */ 438, 26, 26, 26, 26, 27, 27, 28, 28, 28, - /* 70 */ 29, 216, 305, 216, 314, 441, 514, 492, 45, 26, - /* 80 */ 26, 26, 26, 27, 27, 28, 28, 28, 29, 216, - /* 90 */ 415, 416, 418, 419, 156, 418, 419, 362, 365, 366, - /* 100 */ 314, 448, 454, 387, 516, 21, 186, 497, 367, 27, - /* 110 */ 27, 28, 28, 28, 29, 216, 415, 416, 417, 23, - /* 120 */ 22, 311, 458, 459, 455, 455, 25, 25, 24, 24, - /* 130 */ 24, 24, 557, 26, 26, 26, 26, 27, 27, 28, - /* 140 */ 28, 28, 29, 216, 305, 228, 506, 135, 470, 218, - /* 150 */ 550, 145, 132, 256, 360, 261, 361, 153, 418, 419, - /* 160 */ 241, 600, 333, 30, 265, 32, 134, 441, 598, 599, - /* 170 */ 230, 228, 492, 448, 454, 57, 508, 330, 132, 256, - /* 180 */ 360, 261, 361, 153, 418, 419, 437, 78, 410, 407, - /* 190 */ 265, 23, 22, 311, 458, 459, 455, 455, 25, 25, - /* 200 */ 24, 24, 24, 24, 344, 26, 26, 26, 26, 27, - /* 210 */ 27, 28, 28, 28, 29, 216, 305, 214, 536, 549, - /* 220 */ 308, 127, 491, 597, 30, 333, 32, 134, 347, 389, - /* 230 */ 431, 63, 333, 357, 417, 441, 509, 333, 417, 537, - /* 240 */ 330, 215, 193, 596, 595, 448, 454, 330, 18, 437, - /* 250 */ 85, 16, 330, 183, 190, 558, 437, 78, 312, 465, - /* 260 */ 466, 437, 85, 23, 22, 311, 458, 459, 455, 455, - /* 270 */ 25, 25, 24, 24, 24, 24, 438, 26, 26, 26, - /* 280 */ 26, 27, 27, 28, 28, 28, 29, 216, 305, 349, - /* 290 */ 221, 316, 597, 191, 380, 333, 474, 234, 347, 383, - /* 300 */ 326, 412, 220, 346, 594, 217, 213, 417, 112, 333, - /* 310 */ 330, 4, 596, 401, 211, 556, 531, 448, 454, 437, - /* 320 */ 79, 217, 555, 517, 330, 336, 515, 461, 461, 471, - /* 330 */ 443, 574, 434, 437, 78, 23, 22, 311, 458, 459, - /* 340 */ 455, 455, 25, 25, 24, 24, 24, 24, 438, 26, - /* 350 */ 26, 26, 26, 27, 27, 28, 28, 28, 29, 216, - /* 360 */ 305, 445, 445, 445, 156, 470, 218, 362, 365, 366, - /* 370 */ 333, 247, 397, 400, 217, 351, 333, 30, 367, 32, - /* 380 */ 134, 390, 282, 281, 39, 330, 41, 432, 547, 448, - /* 390 */ 454, 330, 214, 533, 437, 93, 544, 603, 1, 406, - /* 400 */ 437, 93, 415, 416, 497, 40, 538, 23, 22, 311, - /* 410 */ 458, 459, 455, 455, 25, 25, 24, 24, 24, 24, - /* 420 */ 575, 26, 26, 26, 26, 27, 27, 28, 28, 28, - /* 430 */ 29, 216, 305, 276, 333, 179, 510, 492, 210, 549, - /* 440 */ 322, 415, 416, 222, 192, 387, 323, 240, 417, 330, - /* 450 */ 559, 63, 415, 416, 417, 619, 410, 407, 437, 71, - /* 460 */ 417, 448, 454, 539, 574, 28, 28, 28, 29, 216, - /* 470 */ 418, 419, 438, 338, 465, 466, 403, 43, 438, 23, - /* 480 */ 22, 311, 458, 459, 455, 455, 25, 25, 24, 24, - /* 490 */ 24, 24, 497, 26, 26, 26, 26, 27, 27, 28, - /* 500 */ 28, 28, 29, 216, 305, 429, 209, 135, 513, 418, - /* 510 */ 419, 433, 233, 64, 390, 282, 281, 441, 66, 544, - /* 520 */ 418, 419, 415, 416, 156, 214, 405, 362, 365, 366, - /* 530 */ 549, 252, 492, 448, 454, 493, 217, 8, 367, 497, - /* 540 */ 438, 608, 63, 208, 299, 417, 494, 472, 548, 200, - /* 550 */ 196, 23, 22, 311, 458, 459, 455, 455, 25, 25, - /* 560 */ 24, 24, 24, 24, 388, 26, 26, 26, 26, 27, - /* 570 */ 27, 28, 28, 28, 29, 216, 305, 479, 254, 356, - /* 580 */ 530, 60, 519, 520, 438, 441, 391, 333, 358, 7, - /* 590 */ 418, 419, 333, 480, 330, 374, 197, 137, 462, 501, - /* 600 */ 449, 450, 330, 437, 9, 448, 454, 330, 481, 487, - /* 610 */ 521, 437, 72, 569, 417, 436, 437, 67, 488, 435, - /* 620 */ 522, 452, 453, 23, 22, 311, 458, 459, 455, 455, - /* 630 */ 25, 25, 24, 24, 24, 24, 333, 26, 26, 26, - /* 640 */ 26, 27, 27, 28, 28, 28, 29, 216, 305, 333, - /* 650 */ 451, 330, 268, 392, 463, 333, 65, 333, 370, 436, - /* 660 */ 437, 76, 313, 435, 330, 150, 185, 441, 475, 333, - /* 670 */ 330, 501, 330, 437, 97, 29, 216, 448, 454, 437, - /* 680 */ 96, 437, 101, 355, 330, 242, 417, 336, 154, 461, - /* 690 */ 461, 354, 571, 437, 99, 23, 22, 311, 458, 459, - /* 700 */ 455, 455, 25, 25, 24, 24, 24, 24, 333, 26, - /* 710 */ 26, 26, 26, 27, 27, 28, 28, 28, 29, 216, - /* 720 */ 305, 333, 248, 330, 264, 56, 336, 333, 461, 461, - /* 730 */ 864, 335, 437, 104, 378, 441, 330, 417, 333, 417, - /* 740 */ 567, 333, 330, 307, 566, 437, 105, 442, 265, 448, - /* 750 */ 454, 437, 126, 330, 572, 520, 330, 336, 379, 461, - /* 760 */ 461, 317, 437, 128, 194, 437, 59, 23, 22, 311, - /* 770 */ 458, 459, 455, 455, 25, 25, 24, 24, 24, 24, - /* 780 */ 333, 26, 26, 26, 26, 27, 27, 28, 28, 28, - /* 790 */ 29, 216, 305, 333, 136, 330, 467, 479, 438, 333, - /* 800 */ 352, 333, 611, 303, 437, 102, 201, 137, 330, 417, - /* 810 */ 456, 178, 333, 480, 330, 417, 330, 437, 77, 486, - /* 820 */ 249, 448, 454, 437, 100, 437, 68, 330, 481, 469, - /* 830 */ 343, 616, 934, 341, 934, 417, 437, 98, 489, 23, - /* 840 */ 22, 311, 458, 459, 455, 455, 25, 25, 24, 24, - /* 850 */ 24, 24, 333, 26, 26, 26, 26, 27, 27, 28, - /* 860 */ 28, 28, 29, 216, 305, 333, 399, 330, 164, 264, - /* 870 */ 205, 333, 264, 334, 612, 250, 437, 129, 409, 2, - /* 880 */ 330, 325, 175, 333, 417, 214, 330, 417, 417, 437, - /* 890 */ 130, 468, 468, 448, 454, 437, 131, 398, 330, 257, - /* 900 */ 336, 259, 461, 461, 438, 154, 229, 437, 69, 318, - /* 910 */ 258, 23, 33, 311, 458, 459, 455, 455, 25, 25, - /* 920 */ 24, 24, 24, 24, 333, 26, 26, 26, 26, 27, - /* 930 */ 27, 28, 28, 28, 29, 216, 305, 333, 155, 330, - /* 940 */ 531, 264, 414, 333, 264, 472, 339, 200, 437, 80, - /* 950 */ 542, 499, 330, 151, 541, 333, 417, 417, 330, 417, - /* 960 */ 307, 437, 81, 535, 534, 448, 454, 437, 70, 47, - /* 970 */ 330, 616, 933, 543, 933, 420, 421, 422, 319, 437, - /* 980 */ 82, 320, 304, 613, 22, 311, 458, 459, 455, 455, - /* 990 */ 25, 25, 24, 24, 24, 24, 333, 26, 26, 26, - /* 1000 */ 26, 27, 27, 28, 28, 28, 29, 216, 305, 333, - /* 1010 */ 209, 330, 364, 206, 612, 333, 528, 565, 377, 565, - /* 1020 */ 437, 83, 525, 526, 330, 615, 545, 333, 501, 577, - /* 1030 */ 330, 333, 290, 437, 84, 426, 396, 448, 454, 437, - /* 1040 */ 86, 590, 330, 417, 438, 141, 330, 438, 413, 423, - /* 1050 */ 417, 437, 87, 424, 327, 437, 88, 311, 458, 459, - /* 1060 */ 455, 455, 25, 25, 24, 24, 24, 24, 388, 26, - /* 1070 */ 26, 26, 26, 27, 27, 28, 28, 28, 29, 216, - /* 1080 */ 35, 340, 286, 3, 333, 270, 333, 329, 416, 142, - /* 1090 */ 384, 321, 276, 425, 144, 35, 340, 337, 3, 330, - /* 1100 */ 6, 330, 329, 416, 304, 614, 276, 417, 437, 73, - /* 1110 */ 437, 74, 337, 333, 328, 342, 427, 333, 439, 333, - /* 1120 */ 540, 417, 155, 47, 289, 474, 287, 274, 330, 272, - /* 1130 */ 342, 417, 330, 350, 330, 277, 276, 437, 89, 243, - /* 1140 */ 474, 437, 90, 437, 91, 38, 37, 615, 333, 584, - /* 1150 */ 244, 417, 428, 276, 36, 331, 332, 46, 245, 443, - /* 1160 */ 38, 37, 507, 330, 202, 203, 204, 417, 417, 36, - /* 1170 */ 331, 332, 437, 92, 443, 198, 267, 214, 155, 586, - /* 1180 */ 235, 236, 237, 143, 239, 348, 133, 583, 440, 246, - /* 1190 */ 445, 445, 445, 446, 447, 10, 587, 276, 20, 42, - /* 1200 */ 172, 417, 294, 333, 288, 445, 445, 445, 446, 447, - /* 1210 */ 10, 295, 417, 35, 340, 219, 3, 149, 330, 484, - /* 1220 */ 329, 416, 333, 170, 276, 574, 48, 437, 75, 169, - /* 1230 */ 337, 19, 171, 251, 444, 415, 416, 330, 333, 417, - /* 1240 */ 588, 345, 276, 177, 353, 498, 437, 17, 342, 417, - /* 1250 */ 483, 253, 255, 330, 276, 496, 417, 417, 474, 333, - /* 1260 */ 504, 505, 437, 94, 369, 417, 155, 231, 359, 417, - /* 1270 */ 417, 518, 523, 474, 330, 395, 291, 281, 38, 37, - /* 1280 */ 500, 306, 315, 437, 95, 232, 214, 36, 331, 332, - /* 1290 */ 524, 502, 443, 188, 189, 417, 262, 292, 532, 263, - /* 1300 */ 551, 260, 269, 515, 271, 273, 417, 443, 570, 402, - /* 1310 */ 155, 417, 527, 417, 417, 417, 275, 417, 280, 417, - /* 1320 */ 417, 382, 385, 445, 445, 445, 446, 447, 10, 528, - /* 1330 */ 386, 417, 283, 417, 284, 285, 417, 417, 445, 445, - /* 1340 */ 445, 582, 593, 293, 107, 417, 296, 417, 297, 417, - /* 1350 */ 417, 607, 578, 529, 151, 300, 417, 417, 417, 226, - /* 1360 */ 579, 417, 54, 417, 158, 591, 417, 54, 225, 610, - /* 1370 */ 227, 302, 546, 552, 301, 553, 554, 371, 560, 159, - /* 1380 */ 375, 373, 207, 160, 51, 562, 563, 161, 117, 278, - /* 1390 */ 381, 140, 573, 163, 181, 393, 394, 118, 119, 120, - /* 1400 */ 180, 580, 121, 123, 324, 605, 604, 606, 55, 609, - /* 1410 */ 589, 309, 224, 62, 58, 103, 411, 111, 238, 430, - /* 1420 */ 199, 174, 660, 661, 662, 146, 147, 460, 310, 457, - /* 1430 */ 34, 476, 464, 473, 182, 195, 148, 477, 5, 478, - /* 1440 */ 482, 12, 138, 44, 11, 106, 495, 511, 512, 503, - /* 1450 */ 223, 49, 363, 108, 109, 152, 266, 50, 110, 157, - /* 1460 */ 258, 372, 184, 561, 139, 113, 151, 162, 279, 115, - /* 1470 */ 376, 15, 576, 116, 165, 52, 13, 368, 581, 53, - /* 1480 */ 167, 166, 585, 122, 124, 114, 592, 564, 568, 168, - /* 1490 */ 14, 61, 601, 602, 173, 298, 125, 408, 187, 617, - /* 1500 */ 945, 945, 404, + /* 0 */ 313, 49, 556, 46, 147, 172, 628, 598, 55, 55, + /* 10 */ 55, 55, 302, 53, 53, 53, 53, 52, 52, 51, + /* 20 */ 51, 51, 50, 238, 603, 66, 624, 623, 604, 598, + /* 30 */ 591, 585, 48, 53, 53, 53, 53, 52, 52, 51, + /* 40 */ 51, 51, 50, 238, 51, 51, 51, 50, 238, 56, + /* 50 */ 57, 47, 583, 582, 584, 584, 54, 54, 55, 55, + /* 60 */ 55, 55, 609, 53, 53, 53, 53, 52, 52, 51, + /* 70 */ 51, 51, 50, 238, 313, 598, 672, 330, 411, 217, + /* 80 */ 32, 53, 53, 53, 53, 52, 52, 51, 51, 51, + /* 90 */ 50, 238, 330, 414, 621, 620, 166, 598, 673, 382, + /* 100 */ 379, 378, 602, 73, 591, 585, 307, 424, 166, 58, + /* 110 */ 377, 382, 379, 378, 516, 515, 624, 623, 254, 200, + /* 120 */ 199, 198, 377, 56, 57, 47, 583, 582, 584, 584, + /* 130 */ 54, 54, 55, 55, 55, 55, 581, 53, 53, 53, + /* 140 */ 53, 52, 52, 51, 51, 51, 50, 238, 313, 270, + /* 150 */ 226, 422, 283, 133, 177, 139, 284, 385, 279, 384, + /* 160 */ 169, 197, 251, 282, 253, 226, 411, 275, 440, 167, + /* 170 */ 139, 284, 385, 279, 384, 169, 571, 236, 591, 585, + /* 180 */ 240, 414, 275, 622, 621, 620, 674, 437, 441, 442, + /* 190 */ 602, 88, 352, 266, 439, 268, 438, 56, 57, 47, + /* 200 */ 583, 582, 584, 584, 54, 54, 55, 55, 55, 55, + /* 210 */ 465, 53, 53, 53, 53, 52, 52, 51, 51, 51, + /* 220 */ 50, 238, 313, 471, 52, 52, 51, 51, 51, 50, + /* 230 */ 238, 234, 166, 491, 567, 382, 379, 378, 1, 440, + /* 240 */ 252, 176, 624, 623, 608, 67, 377, 513, 622, 443, + /* 250 */ 237, 577, 591, 585, 622, 172, 466, 598, 554, 441, + /* 260 */ 340, 409, 526, 580, 580, 349, 596, 553, 194, 482, + /* 270 */ 175, 56, 57, 47, 583, 582, 584, 584, 54, 54, + /* 280 */ 55, 55, 55, 55, 562, 53, 53, 53, 53, 52, + /* 290 */ 52, 51, 51, 51, 50, 238, 313, 594, 594, 594, + /* 300 */ 561, 578, 469, 65, 259, 351, 258, 411, 624, 623, + /* 310 */ 621, 620, 332, 576, 575, 240, 560, 568, 520, 411, + /* 320 */ 341, 237, 414, 624, 623, 598, 591, 585, 542, 519, + /* 330 */ 171, 602, 95, 68, 414, 624, 623, 624, 623, 38, + /* 340 */ 877, 506, 507, 602, 88, 56, 57, 47, 583, 582, + /* 350 */ 584, 584, 54, 54, 55, 55, 55, 55, 532, 53, + /* 360 */ 53, 53, 53, 52, 52, 51, 51, 51, 50, 238, + /* 370 */ 313, 411, 579, 398, 531, 237, 621, 620, 388, 625, + /* 380 */ 500, 206, 167, 396, 233, 312, 414, 387, 569, 492, + /* 390 */ 216, 621, 620, 566, 622, 602, 74, 533, 210, 491, + /* 400 */ 591, 585, 548, 621, 620, 621, 620, 300, 598, 466, + /* 410 */ 481, 67, 603, 35, 622, 601, 604, 547, 6, 56, + /* 420 */ 57, 47, 583, 582, 584, 584, 54, 54, 55, 55, + /* 430 */ 55, 55, 601, 53, 53, 53, 53, 52, 52, 51, + /* 440 */ 51, 51, 50, 238, 313, 411, 184, 409, 528, 580, + /* 450 */ 580, 551, 962, 186, 419, 2, 353, 259, 351, 258, + /* 460 */ 414, 409, 411, 580, 580, 44, 411, 544, 240, 602, + /* 470 */ 94, 190, 7, 62, 591, 585, 598, 414, 350, 607, + /* 480 */ 493, 414, 409, 317, 580, 580, 602, 95, 496, 565, + /* 490 */ 602, 80, 203, 56, 57, 47, 583, 582, 584, 584, + /* 500 */ 54, 54, 55, 55, 55, 55, 535, 53, 53, 53, + /* 510 */ 53, 52, 52, 51, 51, 51, 50, 238, 313, 202, + /* 520 */ 564, 293, 511, 49, 562, 46, 147, 411, 394, 183, + /* 530 */ 563, 549, 505, 549, 174, 409, 322, 580, 580, 39, + /* 540 */ 561, 37, 414, 624, 623, 192, 473, 383, 591, 585, + /* 550 */ 474, 602, 80, 601, 504, 544, 560, 364, 402, 210, + /* 560 */ 421, 952, 361, 952, 365, 201, 144, 56, 57, 47, + /* 570 */ 583, 582, 584, 584, 54, 54, 55, 55, 55, 55, + /* 580 */ 559, 53, 53, 53, 53, 52, 52, 51, 51, 51, + /* 590 */ 50, 238, 313, 601, 232, 264, 272, 321, 374, 484, + /* 600 */ 510, 146, 342, 146, 328, 425, 485, 407, 576, 575, + /* 610 */ 622, 621, 620, 49, 168, 46, 147, 353, 546, 491, + /* 620 */ 204, 240, 591, 585, 421, 951, 549, 951, 549, 168, + /* 630 */ 429, 67, 390, 343, 622, 434, 307, 423, 338, 360, + /* 640 */ 391, 56, 57, 47, 583, 582, 584, 584, 54, 54, + /* 650 */ 55, 55, 55, 55, 601, 53, 53, 53, 53, 52, + /* 660 */ 52, 51, 51, 51, 50, 238, 313, 34, 318, 425, + /* 670 */ 237, 21, 359, 273, 411, 167, 411, 276, 411, 540, + /* 680 */ 411, 422, 13, 318, 619, 618, 617, 622, 275, 414, + /* 690 */ 336, 414, 622, 414, 622, 414, 591, 585, 602, 69, + /* 700 */ 602, 97, 602, 100, 602, 98, 631, 629, 334, 475, + /* 710 */ 475, 367, 319, 148, 327, 56, 57, 47, 583, 582, + /* 720 */ 584, 584, 54, 54, 55, 55, 55, 55, 411, 53, + /* 730 */ 53, 53, 53, 52, 52, 51, 51, 51, 50, 238, + /* 740 */ 313, 411, 331, 414, 411, 49, 276, 46, 147, 569, + /* 750 */ 406, 216, 602, 106, 573, 573, 414, 354, 524, 414, + /* 760 */ 411, 622, 411, 224, 4, 602, 104, 605, 602, 108, + /* 770 */ 591, 585, 622, 20, 375, 414, 167, 414, 215, 144, + /* 780 */ 470, 239, 167, 225, 602, 109, 602, 134, 18, 56, + /* 790 */ 57, 47, 583, 582, 584, 584, 54, 54, 55, 55, + /* 800 */ 55, 55, 411, 53, 53, 53, 53, 52, 52, 51, + /* 810 */ 51, 51, 50, 238, 313, 411, 276, 414, 12, 459, + /* 820 */ 276, 171, 411, 16, 223, 189, 602, 135, 354, 170, + /* 830 */ 414, 622, 630, 2, 411, 622, 540, 414, 143, 602, + /* 840 */ 61, 359, 132, 622, 591, 585, 602, 105, 458, 414, + /* 850 */ 23, 622, 446, 326, 23, 538, 622, 325, 602, 103, + /* 860 */ 427, 530, 309, 56, 57, 47, 583, 582, 584, 584, + /* 870 */ 54, 54, 55, 55, 55, 55, 411, 53, 53, 53, + /* 880 */ 53, 52, 52, 51, 51, 51, 50, 238, 313, 411, + /* 890 */ 264, 414, 411, 276, 359, 219, 157, 214, 357, 366, + /* 900 */ 602, 96, 522, 521, 414, 622, 358, 414, 622, 622, + /* 910 */ 411, 613, 612, 602, 102, 142, 602, 77, 591, 585, + /* 920 */ 529, 540, 231, 426, 308, 414, 622, 622, 468, 521, + /* 930 */ 324, 601, 257, 263, 602, 99, 622, 56, 45, 47, + /* 940 */ 583, 582, 584, 584, 54, 54, 55, 55, 55, 55, + /* 950 */ 411, 53, 53, 53, 53, 52, 52, 51, 51, 51, + /* 960 */ 50, 238, 313, 264, 264, 414, 411, 213, 209, 544, + /* 970 */ 544, 207, 611, 28, 602, 138, 50, 238, 622, 622, + /* 980 */ 381, 414, 503, 140, 323, 222, 274, 622, 590, 589, + /* 990 */ 602, 137, 591, 585, 629, 334, 606, 30, 622, 571, + /* 1000 */ 236, 601, 601, 130, 496, 601, 453, 451, 288, 286, + /* 1010 */ 587, 586, 57, 47, 583, 582, 584, 584, 54, 54, + /* 1020 */ 55, 55, 55, 55, 411, 53, 53, 53, 53, 52, + /* 1030 */ 52, 51, 51, 51, 50, 238, 313, 588, 411, 414, + /* 1040 */ 411, 264, 410, 129, 595, 400, 27, 376, 602, 136, + /* 1050 */ 128, 165, 479, 414, 282, 414, 622, 622, 411, 622, + /* 1060 */ 622, 411, 602, 76, 602, 93, 591, 585, 188, 372, + /* 1070 */ 368, 125, 476, 414, 261, 160, 414, 171, 124, 472, + /* 1080 */ 123, 15, 602, 92, 450, 602, 75, 47, 583, 582, + /* 1090 */ 584, 584, 54, 54, 55, 55, 55, 55, 464, 53, + /* 1100 */ 53, 53, 53, 52, 52, 51, 51, 51, 50, 238, + /* 1110 */ 43, 405, 264, 3, 558, 264, 545, 415, 623, 159, + /* 1120 */ 541, 158, 539, 278, 25, 461, 121, 622, 408, 622, + /* 1130 */ 622, 622, 24, 43, 405, 622, 3, 622, 622, 120, + /* 1140 */ 415, 623, 11, 456, 411, 156, 452, 403, 509, 277, + /* 1150 */ 118, 408, 489, 113, 205, 449, 271, 567, 221, 414, + /* 1160 */ 269, 267, 155, 622, 622, 111, 411, 622, 602, 95, + /* 1170 */ 403, 622, 411, 110, 10, 622, 622, 40, 41, 534, + /* 1180 */ 567, 414, 64, 264, 42, 413, 412, 414, 601, 596, + /* 1190 */ 602, 91, 445, 436, 150, 435, 602, 90, 622, 265, + /* 1200 */ 40, 41, 337, 242, 411, 191, 333, 42, 413, 412, + /* 1210 */ 398, 420, 596, 316, 622, 399, 260, 107, 230, 414, + /* 1220 */ 594, 594, 594, 593, 592, 14, 220, 411, 602, 101, + /* 1230 */ 240, 622, 43, 405, 362, 3, 149, 315, 626, 415, + /* 1240 */ 623, 127, 414, 594, 594, 594, 593, 592, 14, 622, + /* 1250 */ 408, 602, 89, 411, 181, 33, 405, 463, 3, 411, + /* 1260 */ 264, 462, 415, 623, 616, 615, 614, 355, 414, 403, + /* 1270 */ 417, 416, 622, 408, 414, 622, 622, 602, 87, 567, + /* 1280 */ 418, 627, 622, 602, 86, 8, 241, 180, 126, 255, + /* 1290 */ 600, 178, 403, 240, 208, 455, 395, 294, 444, 40, + /* 1300 */ 41, 297, 567, 248, 622, 296, 42, 413, 412, 247, + /* 1310 */ 622, 596, 244, 622, 30, 60, 31, 243, 430, 624, + /* 1320 */ 623, 292, 40, 41, 622, 295, 145, 622, 601, 42, + /* 1330 */ 413, 412, 622, 622, 596, 393, 622, 397, 599, 59, + /* 1340 */ 235, 622, 594, 594, 594, 593, 592, 14, 218, 291, + /* 1350 */ 622, 36, 344, 305, 304, 303, 179, 301, 411, 567, + /* 1360 */ 454, 557, 173, 185, 622, 594, 594, 594, 593, 592, + /* 1370 */ 14, 411, 29, 414, 151, 289, 246, 523, 411, 196, + /* 1380 */ 195, 335, 602, 85, 411, 245, 414, 526, 392, 543, + /* 1390 */ 411, 596, 287, 414, 285, 602, 72, 537, 153, 414, + /* 1400 */ 466, 411, 602, 71, 154, 414, 411, 152, 602, 84, + /* 1410 */ 386, 536, 329, 411, 602, 83, 414, 518, 280, 411, + /* 1420 */ 513, 414, 594, 594, 594, 602, 82, 517, 414, 311, + /* 1430 */ 602, 81, 411, 514, 414, 512, 131, 602, 70, 229, + /* 1440 */ 228, 227, 494, 602, 17, 411, 488, 414, 259, 346, + /* 1450 */ 249, 389, 487, 486, 314, 164, 602, 79, 310, 240, + /* 1460 */ 414, 373, 480, 163, 262, 371, 414, 162, 369, 602, + /* 1470 */ 78, 212, 478, 26, 477, 602, 9, 161, 467, 363, + /* 1480 */ 141, 122, 339, 187, 119, 457, 348, 117, 347, 116, + /* 1490 */ 115, 114, 448, 112, 182, 320, 22, 433, 19, 432, + /* 1500 */ 431, 63, 428, 610, 193, 298, 597, 574, 572, 404, + /* 1510 */ 555, 552, 290, 281, 510, 499, 498, 497, 495, 380, + /* 1520 */ 356, 460, 256, 250, 345, 447, 306, 5, 570, 550, + /* 1530 */ 299, 211, 370, 401, 550, 508, 502, 501, 490, 527, + /* 1540 */ 525, 483, 238, }; static const YYCODETYPE yy_lookahead[] = { - /* 0 */ 19, 142, 143, 144, 145, 24, 116, 26, 75, 76, - /* 10 */ 77, 78, 25, 80, 81, 82, 83, 84, 85, 86, - /* 20 */ 87, 88, 89, 90, 26, 27, 160, 26, 27, 48, - /* 30 */ 49, 79, 80, 81, 82, 83, 84, 85, 86, 87, - /* 40 */ 88, 89, 90, 222, 223, 224, 225, 66, 67, 68, + /* 0 */ 19, 222, 223, 224, 225, 24, 1, 26, 77, 78, + /* 10 */ 79, 80, 15, 82, 83, 84, 85, 86, 87, 88, + /* 20 */ 89, 90, 91, 92, 113, 22, 26, 27, 117, 26, + /* 30 */ 49, 50, 81, 82, 83, 84, 85, 86, 87, 88, + /* 40 */ 89, 90, 91, 92, 88, 89, 90, 91, 92, 68, /* 50 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, - /* 60 */ 194, 80, 81, 82, 83, 84, 85, 86, 87, 88, - /* 70 */ 89, 90, 19, 90, 19, 94, 174, 25, 25, 80, - /* 80 */ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, - /* 90 */ 26, 27, 94, 95, 96, 94, 95, 99, 100, 101, - /* 100 */ 19, 48, 49, 150, 174, 52, 119, 166, 110, 84, - /* 110 */ 85, 86, 87, 88, 89, 90, 26, 27, 165, 66, - /* 120 */ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, - /* 130 */ 77, 78, 186, 80, 81, 82, 83, 84, 85, 86, - /* 140 */ 87, 88, 89, 90, 19, 90, 205, 95, 84, 85, - /* 150 */ 186, 96, 97, 98, 99, 100, 101, 102, 94, 95, - /* 160 */ 195, 97, 150, 222, 109, 224, 225, 26, 104, 105, - /* 170 */ 217, 90, 120, 48, 49, 50, 86, 165, 97, 98, - /* 180 */ 99, 100, 101, 102, 94, 95, 174, 175, 1, 2, - /* 190 */ 109, 66, 67, 68, 69, 70, 71, 72, 73, 74, - /* 200 */ 75, 76, 77, 78, 191, 80, 81, 82, 83, 84, - /* 210 */ 85, 86, 87, 88, 89, 90, 19, 116, 35, 150, - /* 220 */ 155, 24, 208, 150, 222, 150, 224, 225, 216, 128, - /* 230 */ 161, 162, 150, 221, 165, 94, 23, 150, 165, 56, - /* 240 */ 165, 197, 160, 170, 171, 48, 49, 165, 204, 174, - /* 250 */ 175, 22, 165, 24, 185, 186, 174, 175, 169, 170, - /* 260 */ 171, 174, 175, 66, 67, 68, 69, 70, 71, 72, - /* 270 */ 73, 74, 75, 76, 77, 78, 194, 80, 81, 82, - /* 280 */ 83, 84, 85, 86, 87, 88, 89, 90, 19, 214, - /* 290 */ 215, 108, 150, 25, 229, 150, 64, 148, 216, 234, - /* 300 */ 146, 147, 215, 221, 231, 232, 152, 165, 154, 150, - /* 310 */ 165, 196, 170, 171, 160, 181, 182, 48, 49, 174, - /* 320 */ 175, 232, 188, 165, 165, 112, 94, 114, 115, 166, - /* 330 */ 98, 55, 174, 174, 175, 66, 67, 68, 69, 70, - /* 340 */ 71, 72, 73, 74, 75, 76, 77, 78, 194, 80, - /* 350 */ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, - /* 360 */ 19, 129, 130, 131, 96, 84, 85, 99, 100, 101, - /* 370 */ 150, 226, 218, 231, 232, 216, 150, 222, 110, 224, - /* 380 */ 225, 105, 106, 107, 135, 165, 137, 172, 173, 48, - /* 390 */ 49, 165, 116, 183, 174, 175, 181, 242, 22, 245, - /* 400 */ 174, 175, 26, 27, 166, 136, 183, 66, 67, 68, - /* 410 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, - /* 420 */ 11, 80, 81, 82, 83, 84, 85, 86, 87, 88, - /* 430 */ 89, 90, 19, 150, 150, 23, 23, 25, 160, 150, - /* 440 */ 220, 26, 27, 205, 160, 150, 220, 158, 165, 165, - /* 450 */ 161, 162, 26, 27, 165, 0, 1, 2, 174, 175, - /* 460 */ 165, 48, 49, 183, 55, 86, 87, 88, 89, 90, - /* 470 */ 94, 95, 194, 169, 170, 171, 193, 136, 194, 66, - /* 480 */ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, - /* 490 */ 77, 78, 166, 80, 81, 82, 83, 84, 85, 86, - /* 500 */ 87, 88, 89, 90, 19, 153, 160, 95, 23, 94, - /* 510 */ 95, 173, 217, 22, 105, 106, 107, 26, 22, 181, - /* 520 */ 94, 95, 26, 27, 96, 116, 243, 99, 100, 101, - /* 530 */ 150, 205, 120, 48, 49, 120, 232, 22, 110, 166, - /* 540 */ 194, 161, 162, 236, 163, 165, 120, 166, 167, 168, - /* 550 */ 160, 66, 67, 68, 69, 70, 71, 72, 73, 74, - /* 560 */ 75, 76, 77, 78, 218, 80, 81, 82, 83, 84, - /* 570 */ 85, 86, 87, 88, 89, 90, 19, 12, 205, 150, - /* 580 */ 23, 235, 190, 191, 194, 94, 240, 150, 86, 74, - /* 590 */ 94, 95, 150, 28, 165, 237, 206, 207, 23, 150, - /* 600 */ 48, 49, 165, 174, 175, 48, 49, 165, 43, 31, - /* 610 */ 45, 174, 175, 21, 165, 113, 174, 175, 40, 117, - /* 620 */ 55, 69, 70, 66, 67, 68, 69, 70, 71, 72, - /* 630 */ 73, 74, 75, 76, 77, 78, 150, 80, 81, 82, - /* 640 */ 83, 84, 85, 86, 87, 88, 89, 90, 19, 150, - /* 650 */ 98, 165, 23, 61, 23, 150, 25, 150, 19, 113, - /* 660 */ 174, 175, 213, 117, 165, 24, 196, 26, 23, 150, - /* 670 */ 165, 150, 165, 174, 175, 89, 90, 48, 49, 174, - /* 680 */ 175, 174, 175, 19, 165, 198, 165, 112, 49, 114, - /* 690 */ 115, 27, 100, 174, 175, 66, 67, 68, 69, 70, - /* 700 */ 71, 72, 73, 74, 75, 76, 77, 78, 150, 80, - /* 710 */ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, - /* 720 */ 19, 150, 150, 165, 150, 24, 112, 150, 114, 115, - /* 730 */ 138, 19, 174, 175, 213, 94, 165, 165, 150, 165, - /* 740 */ 29, 150, 165, 104, 33, 174, 175, 166, 109, 48, - /* 750 */ 49, 174, 175, 165, 190, 191, 165, 112, 47, 114, - /* 760 */ 115, 187, 174, 175, 160, 174, 175, 66, 67, 68, - /* 770 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, - /* 780 */ 150, 80, 81, 82, 83, 84, 85, 86, 87, 88, - /* 790 */ 89, 90, 19, 150, 150, 165, 233, 12, 194, 150, - /* 800 */ 150, 150, 248, 249, 174, 175, 206, 207, 165, 165, - /* 810 */ 98, 23, 150, 28, 165, 165, 165, 174, 175, 177, - /* 820 */ 150, 48, 49, 174, 175, 174, 175, 165, 43, 233, - /* 830 */ 45, 22, 23, 228, 25, 165, 174, 175, 177, 66, - /* 840 */ 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, - /* 850 */ 77, 78, 150, 80, 81, 82, 83, 84, 85, 86, - /* 860 */ 87, 88, 89, 90, 19, 150, 97, 165, 25, 150, - /* 870 */ 160, 150, 150, 150, 65, 209, 174, 175, 144, 145, - /* 880 */ 165, 246, 247, 150, 165, 116, 165, 165, 165, 174, - /* 890 */ 175, 129, 130, 48, 49, 174, 175, 128, 165, 98, - /* 900 */ 112, 177, 114, 115, 194, 49, 187, 174, 175, 187, - /* 910 */ 109, 66, 67, 68, 69, 70, 71, 72, 73, 74, - /* 920 */ 75, 76, 77, 78, 150, 80, 81, 82, 83, 84, - /* 930 */ 85, 86, 87, 88, 89, 90, 19, 150, 25, 165, - /* 940 */ 182, 150, 150, 150, 150, 166, 167, 168, 174, 175, - /* 950 */ 166, 23, 165, 25, 177, 150, 165, 165, 165, 165, - /* 960 */ 104, 174, 175, 97, 98, 48, 49, 174, 175, 126, - /* 970 */ 165, 22, 23, 177, 25, 7, 8, 9, 187, 174, - /* 980 */ 175, 187, 22, 23, 67, 68, 69, 70, 71, 72, - /* 990 */ 73, 74, 75, 76, 77, 78, 150, 80, 81, 82, - /* 1000 */ 83, 84, 85, 86, 87, 88, 89, 90, 19, 150, - /* 1010 */ 160, 165, 178, 160, 65, 150, 103, 105, 106, 107, - /* 1020 */ 174, 175, 7, 8, 165, 65, 166, 150, 150, 199, - /* 1030 */ 165, 150, 209, 174, 175, 150, 209, 48, 49, 174, - /* 1040 */ 175, 199, 165, 165, 194, 6, 165, 194, 149, 149, - /* 1050 */ 165, 174, 175, 149, 149, 174, 175, 68, 69, 70, - /* 1060 */ 71, 72, 73, 74, 75, 76, 77, 78, 218, 80, - /* 1070 */ 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, - /* 1080 */ 19, 20, 16, 22, 150, 16, 150, 26, 27, 151, - /* 1090 */ 240, 213, 150, 13, 151, 19, 20, 36, 22, 165, - /* 1100 */ 25, 165, 26, 27, 22, 23, 150, 165, 174, 175, - /* 1110 */ 174, 175, 36, 150, 159, 54, 150, 150, 194, 150, - /* 1120 */ 23, 165, 25, 126, 58, 64, 60, 58, 165, 60, - /* 1130 */ 54, 165, 165, 123, 165, 193, 150, 174, 175, 199, - /* 1140 */ 64, 174, 175, 174, 175, 84, 85, 65, 150, 193, - /* 1150 */ 200, 165, 150, 150, 93, 94, 95, 124, 201, 98, - /* 1160 */ 84, 85, 86, 165, 105, 106, 107, 165, 165, 93, - /* 1170 */ 94, 95, 174, 175, 98, 5, 23, 116, 25, 193, - /* 1180 */ 10, 11, 12, 13, 14, 122, 150, 17, 203, 202, - /* 1190 */ 129, 130, 131, 132, 133, 134, 193, 150, 125, 135, - /* 1200 */ 30, 165, 32, 150, 138, 129, 130, 131, 132, 133, - /* 1210 */ 134, 41, 165, 19, 20, 227, 22, 118, 165, 157, - /* 1220 */ 26, 27, 150, 53, 150, 55, 104, 174, 175, 59, - /* 1230 */ 36, 22, 62, 210, 150, 26, 27, 165, 150, 165, - /* 1240 */ 193, 150, 150, 157, 121, 211, 174, 175, 54, 165, - /* 1250 */ 150, 210, 210, 165, 150, 150, 165, 165, 64, 150, - /* 1260 */ 211, 211, 174, 175, 23, 165, 25, 193, 104, 165, - /* 1270 */ 165, 176, 176, 64, 165, 105, 106, 107, 84, 85, - /* 1280 */ 150, 111, 46, 174, 175, 193, 116, 93, 94, 95, - /* 1290 */ 184, 150, 98, 84, 85, 165, 150, 193, 150, 150, - /* 1300 */ 150, 176, 150, 94, 150, 150, 165, 98, 23, 139, - /* 1310 */ 25, 165, 178, 165, 165, 165, 150, 165, 150, 165, - /* 1320 */ 165, 150, 150, 129, 130, 131, 132, 133, 134, 103, - /* 1330 */ 150, 165, 150, 165, 150, 150, 165, 165, 129, 130, - /* 1340 */ 131, 150, 150, 150, 22, 165, 150, 165, 150, 165, - /* 1350 */ 165, 150, 23, 176, 25, 179, 165, 165, 165, 90, - /* 1360 */ 23, 165, 25, 165, 156, 23, 165, 25, 230, 23, - /* 1370 */ 230, 25, 184, 176, 179, 176, 176, 18, 157, 156, - /* 1380 */ 44, 157, 157, 156, 135, 157, 239, 156, 22, 238, - /* 1390 */ 157, 66, 189, 189, 219, 157, 18, 192, 192, 192, - /* 1400 */ 219, 199, 192, 189, 157, 157, 39, 157, 241, 37, - /* 1410 */ 199, 250, 180, 244, 241, 164, 1, 180, 15, 23, - /* 1420 */ 22, 247, 118, 118, 118, 118, 118, 113, 250, 98, - /* 1430 */ 22, 11, 23, 23, 22, 22, 25, 23, 34, 23, - /* 1440 */ 23, 34, 118, 25, 25, 22, 120, 23, 23, 27, - /* 1450 */ 50, 22, 50, 22, 22, 34, 23, 22, 22, 102, - /* 1460 */ 109, 19, 24, 20, 38, 104, 25, 104, 138, 22, - /* 1470 */ 42, 5, 1, 108, 127, 74, 22, 50, 1, 74, - /* 1480 */ 16, 119, 20, 119, 108, 51, 128, 57, 51, 121, - /* 1490 */ 22, 16, 23, 23, 15, 140, 127, 3, 22, 4, - /* 1500 */ 251, 251, 63, -}; -#define YY_SHIFT_USE_DFLT (-111) -#define YY_SHIFT_MAX 408 + /* 60 */ 79, 80, 23, 82, 83, 84, 85, 86, 87, 88, + /* 70 */ 89, 90, 91, 92, 19, 94, 118, 19, 150, 22, + /* 80 */ 25, 82, 83, 84, 85, 86, 87, 88, 89, 90, + /* 90 */ 91, 92, 19, 165, 94, 95, 96, 94, 118, 99, + /* 100 */ 100, 101, 174, 175, 49, 50, 22, 23, 96, 54, + /* 110 */ 110, 99, 100, 101, 7, 8, 26, 27, 16, 105, + /* 120 */ 106, 107, 110, 68, 69, 70, 71, 72, 73, 74, + /* 130 */ 75, 76, 77, 78, 79, 80, 113, 82, 83, 84, + /* 140 */ 85, 86, 87, 88, 89, 90, 91, 92, 19, 16, + /* 150 */ 92, 67, 98, 24, 96, 97, 98, 99, 100, 101, + /* 160 */ 102, 25, 60, 109, 62, 92, 150, 109, 150, 25, + /* 170 */ 97, 98, 99, 100, 101, 102, 86, 87, 49, 50, + /* 180 */ 116, 165, 109, 165, 94, 95, 118, 97, 170, 171, + /* 190 */ 174, 175, 128, 60, 104, 62, 106, 68, 69, 70, + /* 200 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + /* 210 */ 11, 82, 83, 84, 85, 86, 87, 88, 89, 90, + /* 220 */ 91, 92, 19, 21, 86, 87, 88, 89, 90, 91, + /* 230 */ 92, 215, 96, 150, 66, 99, 100, 101, 22, 150, + /* 240 */ 138, 118, 26, 27, 161, 162, 110, 103, 165, 231, + /* 250 */ 232, 23, 49, 50, 165, 24, 57, 26, 32, 170, + /* 260 */ 171, 112, 94, 114, 115, 63, 98, 41, 185, 186, + /* 270 */ 118, 68, 69, 70, 71, 72, 73, 74, 75, 76, + /* 280 */ 77, 78, 79, 80, 12, 82, 83, 84, 85, 86, + /* 290 */ 87, 88, 89, 90, 91, 92, 19, 129, 130, 131, + /* 300 */ 28, 23, 100, 25, 105, 106, 107, 150, 26, 27, + /* 310 */ 94, 95, 169, 170, 171, 116, 44, 23, 46, 150, + /* 320 */ 231, 232, 165, 26, 27, 94, 49, 50, 23, 57, + /* 330 */ 25, 174, 175, 22, 165, 26, 27, 26, 27, 136, + /* 340 */ 138, 97, 98, 174, 175, 68, 69, 70, 71, 72, + /* 350 */ 73, 74, 75, 76, 77, 78, 79, 80, 23, 82, + /* 360 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + /* 370 */ 19, 150, 23, 216, 23, 232, 94, 95, 221, 150, + /* 380 */ 23, 160, 25, 214, 215, 163, 165, 88, 166, 167, + /* 390 */ 168, 94, 95, 23, 165, 174, 175, 88, 160, 150, + /* 400 */ 49, 50, 120, 94, 95, 94, 95, 158, 26, 57, + /* 410 */ 161, 162, 113, 136, 165, 194, 117, 120, 22, 68, + /* 420 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + /* 430 */ 79, 80, 194, 82, 83, 84, 85, 86, 87, 88, + /* 440 */ 89, 90, 91, 92, 19, 150, 23, 112, 23, 114, + /* 450 */ 115, 25, 142, 143, 144, 145, 218, 105, 106, 107, + /* 460 */ 165, 112, 150, 114, 115, 22, 150, 166, 116, 174, + /* 470 */ 175, 22, 76, 235, 49, 50, 94, 165, 240, 172, + /* 480 */ 173, 165, 112, 155, 114, 115, 174, 175, 181, 11, + /* 490 */ 174, 175, 22, 68, 69, 70, 71, 72, 73, 74, + /* 500 */ 75, 76, 77, 78, 79, 80, 205, 82, 83, 84, + /* 510 */ 85, 86, 87, 88, 89, 90, 91, 92, 19, 160, + /* 520 */ 23, 226, 23, 222, 12, 224, 225, 150, 216, 23, + /* 530 */ 23, 25, 36, 25, 25, 112, 220, 114, 115, 135, + /* 540 */ 28, 137, 165, 26, 27, 119, 30, 51, 49, 50, + /* 550 */ 34, 174, 175, 194, 58, 166, 44, 229, 46, 160, + /* 560 */ 22, 23, 234, 25, 48, 206, 207, 68, 69, 70, + /* 570 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + /* 580 */ 23, 82, 83, 84, 85, 86, 87, 88, 89, 90, + /* 590 */ 91, 92, 19, 194, 205, 150, 23, 220, 19, 181, + /* 600 */ 182, 95, 97, 95, 108, 67, 188, 169, 170, 171, + /* 610 */ 165, 94, 95, 222, 50, 224, 225, 218, 120, 150, + /* 620 */ 160, 116, 49, 50, 22, 23, 120, 25, 120, 50, + /* 630 */ 161, 162, 19, 128, 165, 244, 22, 23, 193, 240, + /* 640 */ 27, 68, 69, 70, 71, 72, 73, 74, 75, 76, + /* 650 */ 77, 78, 79, 80, 194, 82, 83, 84, 85, 86, + /* 660 */ 87, 88, 89, 90, 91, 92, 19, 25, 104, 67, + /* 670 */ 232, 24, 150, 23, 150, 25, 150, 150, 150, 150, + /* 680 */ 150, 67, 25, 104, 7, 8, 9, 165, 109, 165, + /* 690 */ 245, 165, 165, 165, 165, 165, 49, 50, 174, 175, + /* 700 */ 174, 175, 174, 175, 174, 175, 0, 1, 2, 105, + /* 710 */ 106, 107, 248, 249, 187, 68, 69, 70, 71, 72, + /* 720 */ 73, 74, 75, 76, 77, 78, 79, 80, 150, 82, + /* 730 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + /* 740 */ 19, 150, 213, 165, 150, 222, 150, 224, 225, 166, + /* 750 */ 167, 168, 174, 175, 129, 130, 165, 150, 165, 165, + /* 760 */ 150, 165, 150, 241, 35, 174, 175, 174, 174, 175, + /* 770 */ 49, 50, 165, 52, 23, 165, 25, 165, 206, 207, + /* 780 */ 23, 197, 25, 187, 174, 175, 174, 175, 204, 68, + /* 790 */ 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, + /* 800 */ 79, 80, 150, 82, 83, 84, 85, 86, 87, 88, + /* 810 */ 89, 90, 91, 92, 19, 150, 150, 165, 35, 23, + /* 820 */ 150, 25, 150, 22, 217, 24, 174, 175, 150, 35, + /* 830 */ 165, 165, 144, 145, 150, 165, 150, 165, 118, 174, + /* 840 */ 175, 150, 22, 165, 49, 50, 174, 175, 23, 165, + /* 850 */ 25, 165, 23, 187, 25, 27, 165, 187, 174, 175, + /* 860 */ 23, 23, 25, 68, 69, 70, 71, 72, 73, 74, + /* 870 */ 75, 76, 77, 78, 79, 80, 150, 82, 83, 84, + /* 880 */ 85, 86, 87, 88, 89, 90, 91, 92, 19, 150, + /* 890 */ 150, 165, 150, 150, 150, 217, 25, 160, 19, 213, + /* 900 */ 174, 175, 190, 191, 165, 165, 27, 165, 165, 165, + /* 910 */ 150, 150, 150, 174, 175, 39, 174, 175, 49, 50, + /* 920 */ 23, 150, 52, 250, 251, 165, 165, 165, 190, 191, + /* 930 */ 187, 194, 241, 193, 174, 175, 165, 68, 69, 70, + /* 940 */ 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, + /* 950 */ 150, 82, 83, 84, 85, 86, 87, 88, 89, 90, + /* 960 */ 91, 92, 19, 150, 150, 165, 150, 160, 160, 166, + /* 970 */ 166, 160, 150, 22, 174, 175, 91, 92, 165, 165, + /* 980 */ 52, 165, 29, 150, 213, 241, 23, 165, 49, 50, + /* 990 */ 174, 175, 49, 50, 1, 2, 173, 126, 165, 86, + /* 1000 */ 87, 194, 194, 22, 181, 194, 193, 193, 205, 205, + /* 1010 */ 71, 72, 69, 70, 71, 72, 73, 74, 75, 76, + /* 1020 */ 77, 78, 79, 80, 150, 82, 83, 84, 85, 86, + /* 1030 */ 87, 88, 89, 90, 91, 92, 19, 98, 150, 165, + /* 1040 */ 150, 150, 150, 22, 150, 150, 22, 52, 174, 175, + /* 1050 */ 22, 102, 20, 165, 109, 165, 165, 165, 150, 165, + /* 1060 */ 165, 150, 174, 175, 174, 175, 49, 50, 24, 19, + /* 1070 */ 43, 104, 59, 165, 138, 104, 165, 25, 53, 53, + /* 1080 */ 22, 5, 174, 175, 193, 174, 175, 70, 71, 72, + /* 1090 */ 73, 74, 75, 76, 77, 78, 79, 80, 1, 82, + /* 1100 */ 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, + /* 1110 */ 19, 20, 150, 22, 150, 150, 150, 26, 27, 118, + /* 1120 */ 150, 35, 150, 150, 76, 27, 108, 165, 37, 165, + /* 1130 */ 165, 165, 76, 19, 20, 165, 22, 165, 165, 127, + /* 1140 */ 26, 27, 22, 1, 150, 16, 20, 56, 150, 150, + /* 1150 */ 119, 37, 150, 119, 160, 193, 150, 66, 193, 165, + /* 1160 */ 150, 150, 121, 165, 165, 108, 150, 165, 174, 175, + /* 1170 */ 56, 165, 150, 127, 22, 165, 165, 86, 87, 88, + /* 1180 */ 66, 165, 16, 150, 93, 94, 95, 165, 194, 98, + /* 1190 */ 174, 175, 128, 23, 15, 23, 174, 175, 165, 150, + /* 1200 */ 86, 87, 65, 140, 150, 22, 3, 93, 94, 95, + /* 1210 */ 216, 4, 98, 252, 165, 221, 150, 164, 180, 165, + /* 1220 */ 129, 130, 131, 132, 133, 134, 193, 150, 174, 175, + /* 1230 */ 116, 165, 19, 20, 150, 22, 249, 252, 149, 26, + /* 1240 */ 27, 180, 165, 129, 130, 131, 132, 133, 134, 165, + /* 1250 */ 37, 174, 175, 150, 6, 19, 20, 150, 22, 150, + /* 1260 */ 150, 150, 26, 27, 149, 149, 13, 150, 165, 56, + /* 1270 */ 149, 159, 165, 37, 165, 165, 165, 174, 175, 66, + /* 1280 */ 146, 147, 165, 174, 175, 25, 152, 151, 154, 150, + /* 1290 */ 194, 151, 56, 116, 160, 150, 123, 202, 150, 86, + /* 1300 */ 87, 199, 66, 193, 165, 200, 93, 94, 95, 150, + /* 1310 */ 165, 98, 150, 165, 126, 22, 124, 150, 150, 26, + /* 1320 */ 27, 150, 86, 87, 165, 201, 150, 165, 194, 93, + /* 1330 */ 94, 95, 165, 165, 98, 150, 165, 122, 203, 125, + /* 1340 */ 227, 165, 129, 130, 131, 132, 133, 134, 5, 150, + /* 1350 */ 165, 135, 218, 10, 11, 12, 13, 14, 150, 66, + /* 1360 */ 17, 157, 118, 157, 165, 129, 130, 131, 132, 133, + /* 1370 */ 134, 150, 104, 165, 31, 210, 33, 176, 150, 86, + /* 1380 */ 87, 247, 174, 175, 150, 42, 165, 94, 121, 211, + /* 1390 */ 150, 98, 210, 165, 210, 174, 175, 211, 55, 165, + /* 1400 */ 57, 150, 174, 175, 61, 165, 150, 64, 174, 175, + /* 1410 */ 104, 211, 47, 150, 174, 175, 165, 176, 176, 150, + /* 1420 */ 103, 165, 129, 130, 131, 174, 175, 184, 165, 179, + /* 1430 */ 174, 175, 150, 178, 165, 176, 22, 174, 175, 230, + /* 1440 */ 92, 230, 184, 174, 175, 150, 176, 165, 105, 106, + /* 1450 */ 107, 150, 176, 176, 111, 156, 174, 175, 179, 116, + /* 1460 */ 165, 18, 157, 156, 238, 157, 165, 156, 45, 174, + /* 1470 */ 175, 157, 157, 135, 239, 174, 175, 156, 189, 157, + /* 1480 */ 68, 189, 139, 219, 22, 199, 157, 192, 18, 192, + /* 1490 */ 192, 192, 199, 189, 219, 157, 243, 40, 243, 157, + /* 1500 */ 157, 246, 38, 153, 196, 198, 166, 233, 233, 228, + /* 1510 */ 177, 177, 209, 177, 182, 177, 166, 177, 166, 178, + /* 1520 */ 242, 199, 242, 209, 209, 199, 148, 196, 166, 208, + /* 1530 */ 195, 236, 237, 191, 208, 183, 183, 183, 186, 174, + /* 1540 */ 174, 186, 92, +}; +#define YY_SHIFT_USE_DFLT (-90) +#define YY_SHIFT_COUNT (418) +#define YY_SHIFT_MIN (-89) +#define YY_SHIFT_MAX (1470) static const short yy_shift_ofst[] = { - /* 0 */ 187, 1061, 1170, 1061, 1194, 1194, -2, 64, 64, -19, - /* 10 */ 1194, 1194, 1194, 1194, 1194, 276, 1, 125, 1076, 1194, - /* 20 */ 1194, 1194, 1194, 1194, 1194, 1194, 1194, 1194, 1194, 1194, - /* 30 */ 1194, 1194, 1194, 1194, 1194, 1194, 1194, 1194, 1194, 1194, - /* 40 */ 1194, 1194, 1194, 1194, 1194, 1194, 1194, 1194, 1194, 1194, - /* 50 */ 1194, 1194, 1194, 1194, 1194, 1194, 1194, 1194, 1194, -48, - /* 60 */ 409, 1, 1, 141, 281, 281, -110, 53, 197, 269, - /* 70 */ 341, 413, 485, 557, 629, 701, 773, 845, 773, 773, - /* 80 */ 773, 773, 773, 773, 773, 773, 773, 773, 773, 773, - /* 90 */ 773, 773, 773, 773, 773, 773, 917, 989, 989, -67, - /* 100 */ -67, -1, -1, 55, 25, 379, 1, 1, 1, 1, - /* 110 */ 1, 639, 592, 1, 1, 1, 1, 1, 1, 1, - /* 120 */ 1, 1, 1, 1, 1, 1, 586, 141, -17, -111, - /* 130 */ -111, -111, 1209, 81, 376, 415, 426, 496, 90, 565, - /* 140 */ 565, 1, 1, 1, 1, 1, 1, 1, 1, 1, - /* 150 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - /* 160 */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - /* 170 */ 1, 1, 1, 1, 809, 949, 455, 641, 641, 641, - /* 180 */ 769, 101, -110, -110, -110, -111, -111, -111, 232, 232, - /* 190 */ 268, 428, 213, 575, 645, 785, 788, 412, 968, 502, - /* 200 */ 491, 52, 183, 183, 183, 614, 614, 711, 912, 614, - /* 210 */ 614, 614, 614, 229, 546, -13, 141, 762, 762, 249, - /* 220 */ 578, 578, 664, 578, 856, 578, 141, 578, 141, 913, - /* 230 */ 843, 664, 664, 843, 1039, 1039, 1039, 1039, 1080, 1080, - /* 240 */ 1075, -110, 997, 1010, 1033, 1063, 1073, 1064, 1099, 1099, - /* 250 */ 1122, 1123, 1122, 1123, 1122, 1123, 1164, 1164, 1236, 1164, - /* 260 */ 1226, 1164, 1322, 1269, 1269, 1236, 1164, 1164, 1164, 1322, - /* 270 */ 1359, 1099, 1359, 1099, 1359, 1099, 1099, 1336, 1249, 1359, - /* 280 */ 1099, 1325, 1325, 1366, 997, 1099, 1378, 1378, 1378, 1378, - /* 290 */ 997, 1325, 1366, 1099, 1367, 1367, 1099, 1099, 1372, -111, - /* 300 */ -111, -111, -111, -111, -111, 552, 1066, 1059, 1069, 960, - /* 310 */ 1082, 712, 631, 928, 801, 1015, 866, 1097, 1153, 1241, - /* 320 */ 1285, 1329, 1337, 1342, 515, 1346, 1415, 1403, 1396, 1398, - /* 330 */ 1304, 1305, 1306, 1307, 1308, 1331, 1314, 1408, 1409, 1410, - /* 340 */ 1412, 1420, 1413, 1414, 1411, 1416, 1417, 1418, 1404, 1419, - /* 350 */ 1407, 1418, 1326, 1423, 1421, 1422, 1324, 1424, 1425, 1426, - /* 360 */ 1400, 1429, 1402, 1431, 1433, 1432, 1435, 1427, 1436, 1357, - /* 370 */ 1351, 1442, 1443, 1438, 1361, 1428, 1430, 1434, 1441, 1437, - /* 380 */ 1330, 1363, 1447, 1466, 1471, 1365, 1401, 1405, 1347, 1454, - /* 390 */ 1362, 1477, 1464, 1368, 1462, 1364, 1376, 1369, 1468, 1358, - /* 400 */ 1469, 1470, 1475, 1439, 1479, 1355, 1476, 1494, 1495, -}; -#define YY_REDUCE_USE_DFLT (-180) -#define YY_REDUCE_MAX 304 + /* 0 */ 993, 1114, 1343, 1114, 1213, 1213, 90, 90, 0, -19, + /* 10 */ 1213, 1213, 1213, 1213, 1213, 352, 517, 721, 1091, 1213, + /* 20 */ 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213, + /* 30 */ 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213, + /* 40 */ 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1236, 1213, 1213, + /* 50 */ 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213, 1213, + /* 60 */ 1213, -49, 199, 517, 517, 913, 913, 382, 1177, 55, + /* 70 */ 647, 573, 499, 425, 351, 277, 203, 129, 795, 795, + /* 80 */ 795, 795, 795, 795, 795, 795, 795, 795, 795, 795, + /* 90 */ 795, 795, 795, 795, 795, 795, 869, 795, 943, 1017, + /* 100 */ 1017, -69, -69, -69, -69, -1, -1, 58, 138, -44, + /* 110 */ 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + /* 120 */ 517, 517, 517, 517, 517, 517, 202, 579, 517, 517, + /* 130 */ 517, 517, 517, 382, 885, 1450, -90, -90, -90, 1293, + /* 140 */ 73, 272, 272, 309, 311, 297, 282, 216, 602, 538, + /* 150 */ 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + /* 160 */ 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + /* 170 */ 517, 517, 517, 517, 517, 517, 517, 517, 517, 517, + /* 180 */ 517, 517, 505, 231, 231, 231, 706, 64, 1177, 1177, + /* 190 */ 1177, -90, -90, -90, 136, 168, 168, 12, 496, 496, + /* 200 */ 496, 506, 423, 512, 370, 349, 335, 149, 149, 149, + /* 210 */ 149, 604, 516, 149, 149, 508, 3, 299, 677, 871, + /* 220 */ 613, 613, 879, 871, 879, 144, 382, 226, 382, 226, + /* 230 */ 564, 226, 613, 226, 226, 404, 625, 625, 382, 426, + /* 240 */ -89, 801, 1464, 1244, 1244, 1457, 1457, 1244, 1462, 1412, + /* 250 */ 1188, 1470, 1470, 1470, 1470, 1244, 1188, 1462, 1412, 1412, + /* 260 */ 1244, 1443, 1338, 1423, 1244, 1244, 1443, 1244, 1443, 1244, + /* 270 */ 1443, 1414, 1306, 1306, 1306, 1365, 1348, 1348, 1414, 1306, + /* 280 */ 1317, 1306, 1365, 1306, 1306, 1267, 1268, 1267, 1268, 1267, + /* 290 */ 1268, 1244, 1244, 1216, 1214, 1215, 1192, 1173, 1188, 1177, + /* 300 */ 1260, 1253, 1253, 1248, 1248, 1248, 1248, -90, -90, -90, + /* 310 */ -90, -90, -90, 939, 102, 614, 84, 133, 14, 837, + /* 320 */ 396, 829, 825, 796, 757, 751, 650, 357, 244, 107, + /* 330 */ 54, 305, 278, 1207, 1203, 1183, 1063, 1179, 1137, 1166, + /* 340 */ 1172, 1170, 1064, 1152, 1046, 1057, 1034, 1126, 1041, 1129, + /* 350 */ 1142, 1031, 1120, 1012, 1056, 1048, 1018, 1098, 1086, 1001, + /* 360 */ 1097, 1076, 1058, 971, 936, 1026, 1052, 1025, 1013, 1027, + /* 370 */ 967, 1044, 1032, 1050, 945, 949, 1028, 995, 1024, 1021, + /* 380 */ 963, 981, 928, 953, 951, 870, 876, 897, 838, 720, + /* 390 */ 828, 794, 820, 498, 642, 783, 657, 729, 642, 557, + /* 400 */ 507, 509, 497, 470, 478, 449, 294, 228, 443, 23, + /* 410 */ 152, 123, 68, -20, -42, 57, 39, -3, 5, +}; +#define YY_REDUCE_USE_DFLT (-222) +#define YY_REDUCE_COUNT (312) +#define YY_REDUCE_MIN (-221) +#define YY_REDUCE_MAX (1378) static const short yy_reduce_ofst[] = { - /* 0 */ -141, 82, 154, 284, 12, 75, 69, 73, 142, -59, - /* 10 */ 145, 87, 159, 220, 226, 346, 289, 155, 429, 437, - /* 20 */ 442, 486, 499, 505, 507, 519, 558, 571, 577, 588, - /* 30 */ 591, 630, 643, 649, 651, 662, 702, 715, 721, 733, - /* 40 */ 774, 787, 793, 805, 846, 859, 865, 877, 881, 934, - /* 50 */ 936, 963, 967, 969, 998, 1053, 1072, 1088, 1109, -179, - /* 60 */ 850, 283, 380, 381, 89, 304, 390, 2, 2, 2, - /* 70 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - /* 80 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - /* 90 */ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - /* 100 */ 2, 2, 2, 215, 2, 2, 449, 574, 719, 722, - /* 110 */ 791, 134, 65, 942, 521, 794, -47, 878, 956, 986, - /* 120 */ 1003, 1047, 1074, 1092, 295, 1104, 2, 779, 2, 2, - /* 130 */ 2, 2, 158, 338, 572, 644, 650, 670, 723, 392, - /* 140 */ 564, 792, 885, 966, 1002, 1036, 723, 1084, 1091, 1100, - /* 150 */ 1105, 1130, 1141, 1146, 1148, 1149, 1150, 1152, 1154, 1155, - /* 160 */ 1166, 1168, 1171, 1172, 1180, 1182, 1184, 1185, 1191, 1192, - /* 170 */ 1193, 1196, 1198, 1201, 554, 554, 734, 238, 326, 373, - /* 180 */ -134, 278, 604, 710, 853, 44, 600, 635, -98, -70, - /* 190 */ -54, -36, -35, -35, -35, 13, -35, 14, 149, 115, - /* 200 */ 163, 14, 210, 223, 280, -35, -35, 307, 358, -35, - /* 210 */ -35, -35, -35, 352, 470, 487, 581, 563, 596, 605, - /* 220 */ 642, 661, 666, 724, 758, 777, 784, 796, 860, 834, - /* 230 */ 830, 823, 827, 842, 899, 900, 904, 905, 938, 943, - /* 240 */ 955, 924, 940, 950, 957, 987, 985, 988, 1062, 1086, - /* 250 */ 1023, 1034, 1041, 1049, 1042, 1050, 1095, 1096, 1106, 1125, - /* 260 */ 1134, 1177, 1176, 1138, 1140, 1188, 1197, 1199, 1200, 1195, - /* 270 */ 1208, 1221, 1223, 1224, 1227, 1225, 1228, 1151, 1147, 1231, - /* 280 */ 1233, 1203, 1204, 1175, 1202, 1238, 1205, 1206, 1207, 1210, - /* 290 */ 1211, 1214, 1181, 1247, 1167, 1173, 1248, 1250, 1169, 1251, - /* 300 */ 1232, 1237, 1174, 1161, 1178, + /* 0 */ 310, 994, 1134, 221, 169, 157, 89, 18, 83, 301, + /* 10 */ 377, 316, 312, 16, 295, 238, 249, 391, 1301, 1295, + /* 20 */ 1282, 1269, 1263, 1256, 1251, 1240, 1234, 1228, 1221, 1208, + /* 30 */ 1109, 1103, 1077, 1054, 1022, 1016, 911, 908, 890, 888, + /* 40 */ 874, 816, 800, 760, 742, 739, 726, 684, 672, 665, + /* 50 */ 652, 612, 610, 594, 591, 578, 530, 528, 526, 524, + /* 60 */ -72, -221, 399, 469, 445, 438, 143, 222, 359, 523, + /* 70 */ 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, + /* 80 */ 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, + /* 90 */ 523, 523, 523, 523, 523, 523, 523, 523, 523, 523, + /* 100 */ 523, 523, 523, 523, 523, 523, 523, 307, 523, 523, + /* 110 */ 1110, 678, 1033, 965, 962, 891, 814, 813, 744, 771, + /* 120 */ 691, 607, 522, 743, 686, 740, 328, 418, 670, 666, + /* 130 */ 596, 527, 529, 583, 523, 523, 523, 523, 523, 593, + /* 140 */ 823, 738, 712, 892, 1199, 1185, 1176, 1171, 673, 673, + /* 150 */ 1168, 1167, 1162, 1159, 1148, 1145, 1139, 1117, 1111, 1107, + /* 160 */ 1084, 1066, 1049, 1011, 1010, 1006, 1002, 999, 998, 973, + /* 170 */ 972, 970, 966, 964, 895, 894, 892, 833, 822, 762, + /* 180 */ 761, 229, 811, 804, 803, 389, 688, 808, 807, 737, + /* 190 */ 460, 464, 572, 584, 1355, 1366, 1365, 1352, 1354, 1353, + /* 200 */ 1352, 1326, 1335, 1342, 1335, 1335, 1335, 1335, 1335, 1335, + /* 210 */ 1335, 1295, 1295, 1335, 1335, 1321, 1362, 1331, 1378, 1326, + /* 220 */ 1315, 1314, 1280, 1322, 1278, 1341, 1352, 1340, 1350, 1338, + /* 230 */ 1332, 1336, 1303, 1334, 1333, 1281, 1275, 1274, 1340, 1307, + /* 240 */ 1308, 1350, 1255, 1343, 1342, 1255, 1253, 1338, 1275, 1304, + /* 250 */ 1293, 1299, 1298, 1297, 1295, 1329, 1286, 1264, 1292, 1289, + /* 260 */ 1322, 1321, 1235, 1226, 1315, 1314, 1311, 1308, 1307, 1305, + /* 270 */ 1299, 1279, 1277, 1276, 1270, 1258, 1211, 1209, 1250, 1259, + /* 280 */ 1255, 1242, 1243, 1241, 1201, 1200, 1184, 1186, 1182, 1178, + /* 290 */ 1165, 1206, 1204, 1113, 1135, 1095, 1124, 1105, 1102, 1096, + /* 300 */ 1112, 1140, 1136, 1121, 1116, 1115, 1089, 985, 961, 987, + /* 310 */ 1061, 1038, 1053, }; static const YYACTIONTYPE yy_default[] = { - /* 0 */ 624, 859, 943, 943, 859, 943, 943, 888, 888, 747, - /* 10 */ 857, 943, 943, 943, 943, 943, 943, 917, 943, 943, - /* 20 */ 943, 943, 943, 943, 943, 943, 943, 943, 943, 943, - /* 30 */ 943, 943, 943, 943, 943, 943, 943, 943, 943, 943, - /* 40 */ 943, 943, 943, 943, 943, 943, 943, 943, 943, 943, - /* 50 */ 943, 943, 943, 943, 943, 943, 943, 943, 943, 831, - /* 60 */ 943, 943, 943, 663, 888, 888, 751, 782, 943, 943, - /* 70 */ 943, 943, 943, 943, 943, 943, 783, 943, 861, 856, - /* 80 */ 852, 854, 853, 860, 784, 773, 780, 787, 762, 901, - /* 90 */ 789, 790, 796, 797, 918, 916, 819, 818, 837, 821, - /* 100 */ 843, 820, 830, 655, 822, 823, 943, 943, 943, 943, - /* 110 */ 943, 716, 650, 943, 943, 943, 943, 943, 943, 943, - /* 120 */ 943, 943, 943, 943, 943, 943, 824, 943, 825, 838, - /* 130 */ 839, 840, 943, 943, 943, 943, 943, 943, 943, 943, - /* 140 */ 943, 630, 943, 943, 943, 943, 943, 943, 943, 943, - /* 150 */ 943, 943, 943, 943, 943, 943, 943, 943, 943, 943, - /* 160 */ 943, 943, 943, 943, 943, 943, 943, 943, 943, 872, - /* 170 */ 943, 921, 923, 943, 943, 943, 624, 747, 747, 747, - /* 180 */ 943, 943, 943, 943, 943, 741, 751, 935, 943, 943, - /* 190 */ 707, 943, 943, 943, 943, 943, 943, 943, 632, 739, - /* 200 */ 665, 749, 943, 943, 943, 652, 728, 894, 943, 908, - /* 210 */ 906, 730, 792, 943, 739, 748, 943, 943, 943, 855, - /* 220 */ 776, 776, 764, 776, 686, 776, 943, 776, 943, 689, - /* 230 */ 786, 764, 764, 786, 629, 629, 629, 629, 640, 640, - /* 240 */ 706, 943, 786, 777, 779, 769, 781, 943, 755, 755, - /* 250 */ 763, 768, 763, 768, 763, 768, 718, 718, 703, 718, - /* 260 */ 689, 718, 865, 869, 869, 703, 718, 718, 718, 865, - /* 270 */ 647, 755, 647, 755, 647, 755, 755, 898, 900, 647, - /* 280 */ 755, 720, 720, 798, 786, 755, 727, 727, 727, 727, - /* 290 */ 786, 720, 798, 755, 920, 920, 755, 755, 928, 673, - /* 300 */ 691, 691, 935, 940, 940, 943, 943, 943, 943, 943, - /* 310 */ 943, 943, 943, 943, 943, 943, 943, 943, 943, 943, - /* 320 */ 943, 943, 943, 943, 874, 943, 943, 638, 943, 657, - /* 330 */ 805, 810, 806, 943, 807, 943, 733, 943, 943, 943, - /* 340 */ 943, 943, 943, 943, 943, 943, 943, 858, 943, 770, - /* 350 */ 943, 778, 943, 943, 943, 943, 943, 943, 943, 943, - /* 360 */ 943, 943, 943, 943, 943, 943, 943, 943, 943, 943, - /* 370 */ 943, 943, 943, 943, 943, 943, 943, 896, 897, 943, - /* 380 */ 943, 943, 943, 943, 943, 943, 943, 943, 943, 943, - /* 390 */ 943, 943, 943, 943, 943, 943, 943, 943, 943, 943, - /* 400 */ 943, 943, 943, 927, 943, 943, 930, 625, 943, 620, - /* 410 */ 622, 623, 627, 628, 631, 657, 658, 660, 661, 662, - /* 420 */ 633, 634, 635, 636, 637, 639, 643, 641, 642, 644, - /* 430 */ 651, 653, 672, 674, 676, 737, 738, 802, 731, 732, - /* 440 */ 736, 659, 813, 804, 808, 809, 811, 812, 826, 827, - /* 450 */ 829, 835, 842, 845, 828, 833, 834, 836, 841, 844, - /* 460 */ 734, 735, 848, 666, 667, 670, 671, 884, 886, 885, - /* 470 */ 887, 669, 668, 814, 817, 850, 851, 909, 910, 911, - /* 480 */ 912, 913, 846, 756, 849, 832, 771, 774, 775, 772, - /* 490 */ 740, 750, 758, 759, 760, 761, 745, 746, 752, 767, - /* 500 */ 800, 801, 765, 766, 753, 754, 742, 743, 744, 847, - /* 510 */ 803, 815, 816, 677, 678, 810, 679, 680, 681, 719, - /* 520 */ 722, 723, 724, 682, 701, 704, 705, 683, 690, 684, - /* 530 */ 685, 692, 693, 694, 697, 698, 699, 700, 695, 696, - /* 540 */ 866, 867, 870, 868, 687, 688, 702, 675, 664, 656, - /* 550 */ 708, 711, 712, 713, 714, 715, 717, 709, 710, 654, - /* 560 */ 645, 648, 757, 890, 899, 895, 891, 892, 893, 649, - /* 570 */ 862, 863, 721, 794, 795, 889, 902, 904, 799, 905, - /* 580 */ 907, 903, 932, 646, 725, 726, 729, 871, 914, 785, - /* 590 */ 788, 791, 793, 873, 875, 877, 879, 880, 881, 882, - /* 600 */ 883, 876, 878, 915, 919, 922, 924, 925, 926, 929, - /* 610 */ 931, 936, 937, 938, 941, 942, 939, 626, 621, + /* 0 */ 636, 872, 961, 961, 961, 872, 901, 901, 961, 760, + /* 10 */ 961, 961, 961, 961, 870, 961, 961, 935, 961, 961, + /* 20 */ 961, 961, 961, 961, 961, 961, 961, 961, 961, 961, + /* 30 */ 961, 961, 961, 961, 961, 961, 961, 961, 961, 961, + /* 40 */ 961, 961, 961, 961, 961, 961, 961, 961, 961, 961, + /* 50 */ 961, 961, 961, 961, 961, 961, 961, 961, 961, 961, + /* 60 */ 961, 844, 961, 961, 961, 901, 901, 675, 764, 795, + /* 70 */ 961, 961, 961, 961, 961, 961, 961, 961, 934, 936, + /* 80 */ 810, 809, 803, 802, 914, 775, 800, 793, 786, 797, + /* 90 */ 873, 866, 867, 865, 869, 874, 961, 796, 832, 850, + /* 100 */ 831, 849, 856, 848, 834, 843, 833, 667, 835, 836, + /* 110 */ 961, 961, 961, 961, 961, 961, 961, 961, 961, 961, + /* 120 */ 961, 961, 961, 961, 961, 961, 662, 729, 961, 961, + /* 130 */ 961, 961, 961, 961, 837, 838, 853, 852, 851, 961, + /* 140 */ 961, 961, 961, 961, 961, 961, 961, 961, 961, 961, + /* 150 */ 961, 941, 939, 961, 885, 961, 961, 961, 961, 961, + /* 160 */ 961, 961, 961, 961, 961, 961, 961, 961, 961, 961, + /* 170 */ 961, 961, 961, 961, 961, 961, 961, 961, 961, 961, + /* 180 */ 961, 642, 961, 760, 760, 760, 636, 961, 961, 961, + /* 190 */ 961, 953, 764, 754, 720, 961, 961, 961, 961, 961, + /* 200 */ 961, 961, 961, 961, 961, 961, 961, 805, 743, 924, + /* 210 */ 926, 961, 907, 741, 664, 762, 677, 752, 644, 799, + /* 220 */ 777, 777, 919, 799, 919, 701, 961, 789, 961, 789, + /* 230 */ 698, 789, 777, 789, 789, 868, 961, 961, 961, 761, + /* 240 */ 752, 961, 946, 768, 768, 938, 938, 768, 811, 733, + /* 250 */ 799, 740, 740, 740, 740, 768, 799, 811, 733, 733, + /* 260 */ 768, 659, 913, 911, 768, 768, 659, 768, 659, 768, + /* 270 */ 659, 878, 731, 731, 731, 716, 882, 882, 878, 731, + /* 280 */ 701, 731, 716, 731, 731, 781, 776, 781, 776, 781, + /* 290 */ 776, 768, 768, 961, 794, 782, 792, 790, 799, 961, + /* 300 */ 719, 652, 652, 641, 641, 641, 641, 958, 958, 953, + /* 310 */ 703, 703, 685, 961, 961, 961, 961, 961, 961, 961, + /* 320 */ 887, 961, 961, 961, 961, 961, 961, 961, 961, 961, + /* 330 */ 961, 961, 961, 961, 637, 948, 961, 961, 945, 961, + /* 340 */ 961, 961, 961, 961, 961, 961, 961, 961, 961, 961, + /* 350 */ 961, 961, 961, 961, 961, 961, 961, 961, 961, 917, + /* 360 */ 961, 961, 961, 961, 961, 961, 910, 909, 961, 961, + /* 370 */ 961, 961, 961, 961, 961, 961, 961, 961, 961, 961, + /* 380 */ 961, 961, 961, 961, 961, 961, 961, 961, 961, 961, + /* 390 */ 961, 961, 961, 961, 791, 961, 783, 961, 871, 961, + /* 400 */ 961, 961, 961, 961, 961, 961, 961, 961, 961, 746, + /* 410 */ 820, 961, 819, 823, 818, 669, 961, 650, 961, 633, + /* 420 */ 638, 957, 960, 959, 956, 955, 954, 949, 947, 944, + /* 430 */ 943, 942, 940, 937, 933, 891, 889, 896, 895, 894, + /* 440 */ 893, 892, 890, 888, 886, 806, 804, 801, 798, 932, + /* 450 */ 884, 742, 739, 738, 658, 950, 916, 925, 923, 812, + /* 460 */ 922, 921, 920, 918, 915, 902, 808, 807, 734, 876, + /* 470 */ 875, 661, 906, 905, 904, 908, 912, 903, 770, 660, + /* 480 */ 657, 666, 723, 722, 730, 728, 727, 726, 725, 724, + /* 490 */ 721, 668, 676, 687, 715, 700, 699, 881, 883, 880, + /* 500 */ 879, 708, 707, 713, 712, 711, 710, 709, 706, 705, + /* 510 */ 704, 697, 696, 702, 695, 718, 717, 714, 694, 737, + /* 520 */ 736, 735, 732, 693, 692, 691, 823, 690, 689, 829, + /* 530 */ 828, 816, 860, 757, 756, 755, 767, 766, 779, 778, + /* 540 */ 814, 813, 780, 765, 759, 758, 774, 773, 772, 771, + /* 550 */ 763, 753, 785, 788, 787, 784, 845, 862, 769, 859, + /* 560 */ 931, 930, 929, 928, 927, 864, 863, 830, 827, 680, + /* 570 */ 681, 900, 898, 899, 897, 683, 682, 679, 678, 861, + /* 580 */ 748, 747, 857, 854, 846, 841, 858, 855, 847, 842, + /* 590 */ 840, 839, 825, 824, 822, 821, 817, 826, 671, 749, + /* 600 */ 745, 744, 815, 751, 750, 688, 686, 684, 665, 663, + /* 610 */ 656, 654, 653, 655, 651, 649, 648, 647, 646, 645, + /* 620 */ 674, 673, 672, 670, 669, 643, 640, 639, 635, 634, + /* 630 */ 632, }; -#define YY_SZ_ACTTAB (int)(sizeof(yy_action)/sizeof(yy_action[0])) /* The next table maps tokens into fallback tokens. If a construct ** like the following: @@ -86914,6 +91408,7 @@ 0, /* ID => nothing */ 0, /* INDEXED => nothing */ 26, /* ABORT => ID */ + 26, /* ACTION => ID */ 26, /* AFTER => ID */ 26, /* ANALYZE => ID */ 26, /* ASC => ID */ @@ -86935,6 +91430,7 @@ 26, /* INSTEAD => ID */ 26, /* LIKE_KW => ID */ 26, /* MATCH => ID */ + 26, /* NO => ID */ 26, /* KEY => ID */ 26, /* OF => ID */ 26, /* OFFSET => ID */ @@ -87034,26 +91530,26 @@ "TABLE", "CREATE", "IF", "NOT", "EXISTS", "TEMP", "LP", "RP", "AS", "COMMA", "ID", "INDEXED", - "ABORT", "AFTER", "ANALYZE", "ASC", - "ATTACH", "BEFORE", "BY", "CASCADE", - "CAST", "COLUMNKW", "CONFLICT", "DATABASE", - "DESC", "DETACH", "EACH", "FAIL", - "FOR", "IGNORE", "INITIALLY", "INSTEAD", - "LIKE_KW", "MATCH", "KEY", "OF", - "OFFSET", "PRAGMA", "RAISE", "REPLACE", - "RESTRICT", "ROW", "TRIGGER", "VACUUM", - "VIEW", "VIRTUAL", "REINDEX", "RENAME", - "CTIME_KW", "ANY", "OR", "AND", - "IS", "BETWEEN", "IN", "ISNULL", - "NOTNULL", "NE", "EQ", "GT", - "LE", "LT", "GE", "ESCAPE", - "BITAND", "BITOR", "LSHIFT", "RSHIFT", - "PLUS", "MINUS", "STAR", "SLASH", - "REM", "CONCAT", "COLLATE", "UMINUS", - "UPLUS", "BITNOT", "STRING", "JOIN_KW", + "ABORT", "ACTION", "AFTER", "ANALYZE", + "ASC", "ATTACH", "BEFORE", "BY", + "CASCADE", "CAST", "COLUMNKW", "CONFLICT", + "DATABASE", "DESC", "DETACH", "EACH", + "FAIL", "FOR", "IGNORE", "INITIALLY", + "INSTEAD", "LIKE_KW", "MATCH", "NO", + "KEY", "OF", "OFFSET", "PRAGMA", + "RAISE", "REPLACE", "RESTRICT", "ROW", + "TRIGGER", "VACUUM", "VIEW", "VIRTUAL", + "REINDEX", "RENAME", "CTIME_KW", "ANY", + "OR", "AND", "IS", "BETWEEN", + "IN", "ISNULL", "NOTNULL", "NE", + "EQ", "GT", "LE", "LT", + "GE", "ESCAPE", "BITAND", "BITOR", + "LSHIFT", "RSHIFT", "PLUS", "MINUS", + "STAR", "SLASH", "REM", "CONCAT", + "COLLATE", "BITNOT", "STRING", "JOIN_KW", "CONSTRAINT", "DEFAULT", "NULL", "PRIMARY", "UNIQUE", "CHECK", "REFERENCES", "AUTOINCR", - "ON", "DELETE", "UPDATE", "INSERT", + "ON", "INSERT", "DELETE", "UPDATE", "SET", "DEFERRABLE", "FOREIGN", "DROP", "UNION", "ALL", "EXCEPT", "INTERSECT", "SELECT", "DISTINCT", "DOT", "FROM", @@ -87087,9 +91583,10 @@ "case_else", "uniqueflag", "collate", "nmnum", "plus_opt", "number", "trigger_decl", "trigger_cmd_list", "trigger_time", "trigger_event", "foreach_clause", "when_clause", - "trigger_cmd", "database_kw_opt", "key_opt", "add_column_fullname", - "kwcolumn_opt", "create_vtab", "vtabarglist", "vtabarg", - "vtabargtoken", "lp", "anylist", + "trigger_cmd", "trnm", "tridxby", "database_kw_opt", + "key_opt", "add_column_fullname", "kwcolumn_opt", "create_vtab", + "vtabarglist", "vtabarg", "vtabargtoken", "lp", + "anylist", }; #endif /* NDEBUG */ @@ -87172,255 +91669,261 @@ /* 72 */ "refargs ::=", /* 73 */ "refargs ::= refargs refarg", /* 74 */ "refarg ::= MATCH nm", - /* 75 */ "refarg ::= ON DELETE refact", - /* 76 */ "refarg ::= ON UPDATE refact", - /* 77 */ "refarg ::= ON INSERT refact", + /* 75 */ "refarg ::= ON INSERT refact", + /* 76 */ "refarg ::= ON DELETE refact", + /* 77 */ "refarg ::= ON UPDATE refact", /* 78 */ "refact ::= SET NULL", /* 79 */ "refact ::= SET DEFAULT", /* 80 */ "refact ::= CASCADE", /* 81 */ "refact ::= RESTRICT", - /* 82 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", - /* 83 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", - /* 84 */ "init_deferred_pred_opt ::=", - /* 85 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", - /* 86 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", - /* 87 */ "conslist_opt ::=", - /* 88 */ "conslist_opt ::= COMMA conslist", - /* 89 */ "conslist ::= conslist COMMA tcons", - /* 90 */ "conslist ::= conslist tcons", - /* 91 */ "conslist ::= tcons", - /* 92 */ "tcons ::= CONSTRAINT nm", - /* 93 */ "tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf", - /* 94 */ "tcons ::= UNIQUE LP idxlist RP onconf", - /* 95 */ "tcons ::= CHECK LP expr RP onconf", - /* 96 */ "tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt", - /* 97 */ "defer_subclause_opt ::=", - /* 98 */ "defer_subclause_opt ::= defer_subclause", - /* 99 */ "onconf ::=", - /* 100 */ "onconf ::= ON CONFLICT resolvetype", - /* 101 */ "orconf ::=", - /* 102 */ "orconf ::= OR resolvetype", - /* 103 */ "resolvetype ::= raisetype", - /* 104 */ "resolvetype ::= IGNORE", - /* 105 */ "resolvetype ::= REPLACE", - /* 106 */ "cmd ::= DROP TABLE ifexists fullname", - /* 107 */ "ifexists ::= IF EXISTS", - /* 108 */ "ifexists ::=", - /* 109 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm AS select", - /* 110 */ "cmd ::= DROP VIEW ifexists fullname", - /* 111 */ "cmd ::= select", - /* 112 */ "select ::= oneselect", - /* 113 */ "select ::= select multiselect_op oneselect", - /* 114 */ "multiselect_op ::= UNION", - /* 115 */ "multiselect_op ::= UNION ALL", - /* 116 */ "multiselect_op ::= EXCEPT|INTERSECT", - /* 117 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", - /* 118 */ "distinct ::= DISTINCT", - /* 119 */ "distinct ::= ALL", - /* 120 */ "distinct ::=", - /* 121 */ "sclp ::= selcollist COMMA", - /* 122 */ "sclp ::=", - /* 123 */ "selcollist ::= sclp expr as", - /* 124 */ "selcollist ::= sclp STAR", - /* 125 */ "selcollist ::= sclp nm DOT STAR", - /* 126 */ "as ::= AS nm", - /* 127 */ "as ::= ids", - /* 128 */ "as ::=", - /* 129 */ "from ::=", - /* 130 */ "from ::= FROM seltablist", - /* 131 */ "stl_prefix ::= seltablist joinop", - /* 132 */ "stl_prefix ::=", - /* 133 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt", - /* 134 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt", - /* 135 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt", - /* 136 */ "dbnm ::=", - /* 137 */ "dbnm ::= DOT nm", - /* 138 */ "fullname ::= nm dbnm", - /* 139 */ "joinop ::= COMMA|JOIN", - /* 140 */ "joinop ::= JOIN_KW JOIN", - /* 141 */ "joinop ::= JOIN_KW nm JOIN", - /* 142 */ "joinop ::= JOIN_KW nm nm JOIN", - /* 143 */ "on_opt ::= ON expr", - /* 144 */ "on_opt ::=", - /* 145 */ "indexed_opt ::=", - /* 146 */ "indexed_opt ::= INDEXED BY nm", - /* 147 */ "indexed_opt ::= NOT INDEXED", - /* 148 */ "using_opt ::= USING LP inscollist RP", - /* 149 */ "using_opt ::=", - /* 150 */ "orderby_opt ::=", - /* 151 */ "orderby_opt ::= ORDER BY sortlist", - /* 152 */ "sortlist ::= sortlist COMMA sortitem sortorder", - /* 153 */ "sortlist ::= sortitem sortorder", - /* 154 */ "sortitem ::= expr", - /* 155 */ "sortorder ::= ASC", - /* 156 */ "sortorder ::= DESC", - /* 157 */ "sortorder ::=", - /* 158 */ "groupby_opt ::=", - /* 159 */ "groupby_opt ::= GROUP BY nexprlist", - /* 160 */ "having_opt ::=", - /* 161 */ "having_opt ::= HAVING expr", - /* 162 */ "limit_opt ::=", - /* 163 */ "limit_opt ::= LIMIT expr", - /* 164 */ "limit_opt ::= LIMIT expr OFFSET expr", - /* 165 */ "limit_opt ::= LIMIT expr COMMA expr", - /* 166 */ "cmd ::= DELETE FROM fullname indexed_opt where_opt", - /* 167 */ "where_opt ::=", - /* 168 */ "where_opt ::= WHERE expr", - /* 169 */ "cmd ::= UPDATE orconf fullname indexed_opt SET setlist where_opt", - /* 170 */ "setlist ::= setlist COMMA nm EQ expr", - /* 171 */ "setlist ::= nm EQ expr", - /* 172 */ "cmd ::= insert_cmd INTO fullname inscollist_opt VALUES LP itemlist RP", - /* 173 */ "cmd ::= insert_cmd INTO fullname inscollist_opt select", - /* 174 */ "cmd ::= insert_cmd INTO fullname inscollist_opt DEFAULT VALUES", - /* 175 */ "insert_cmd ::= INSERT orconf", - /* 176 */ "insert_cmd ::= REPLACE", - /* 177 */ "itemlist ::= itemlist COMMA expr", - /* 178 */ "itemlist ::= expr", - /* 179 */ "inscollist_opt ::=", - /* 180 */ "inscollist_opt ::= LP inscollist RP", - /* 181 */ "inscollist ::= inscollist COMMA nm", - /* 182 */ "inscollist ::= nm", - /* 183 */ "expr ::= term", - /* 184 */ "expr ::= LP expr RP", - /* 185 */ "term ::= NULL", - /* 186 */ "expr ::= id", - /* 187 */ "expr ::= JOIN_KW", - /* 188 */ "expr ::= nm DOT nm", - /* 189 */ "expr ::= nm DOT nm DOT nm", - /* 190 */ "term ::= INTEGER|FLOAT|BLOB", - /* 191 */ "term ::= STRING", - /* 192 */ "expr ::= REGISTER", - /* 193 */ "expr ::= VARIABLE", - /* 194 */ "expr ::= expr COLLATE ids", - /* 195 */ "expr ::= CAST LP expr AS typetoken RP", - /* 196 */ "expr ::= ID LP distinct exprlist RP", - /* 197 */ "expr ::= ID LP STAR RP", - /* 198 */ "term ::= CTIME_KW", - /* 199 */ "expr ::= expr AND expr", - /* 200 */ "expr ::= expr OR expr", - /* 201 */ "expr ::= expr LT|GT|GE|LE expr", - /* 202 */ "expr ::= expr EQ|NE expr", - /* 203 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", - /* 204 */ "expr ::= expr PLUS|MINUS expr", - /* 205 */ "expr ::= expr STAR|SLASH|REM expr", - /* 206 */ "expr ::= expr CONCAT expr", - /* 207 */ "likeop ::= LIKE_KW", - /* 208 */ "likeop ::= NOT LIKE_KW", - /* 209 */ "likeop ::= MATCH", - /* 210 */ "likeop ::= NOT MATCH", - /* 211 */ "escape ::= ESCAPE expr", - /* 212 */ "escape ::=", - /* 213 */ "expr ::= expr likeop expr escape", - /* 214 */ "expr ::= expr ISNULL|NOTNULL", - /* 215 */ "expr ::= expr IS NULL", + /* 82 */ "refact ::= NO ACTION", + /* 83 */ "defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt", + /* 84 */ "defer_subclause ::= DEFERRABLE init_deferred_pred_opt", + /* 85 */ "init_deferred_pred_opt ::=", + /* 86 */ "init_deferred_pred_opt ::= INITIALLY DEFERRED", + /* 87 */ "init_deferred_pred_opt ::= INITIALLY IMMEDIATE", + /* 88 */ "conslist_opt ::=", + /* 89 */ "conslist_opt ::= COMMA conslist", + /* 90 */ "conslist ::= conslist COMMA tcons", + /* 91 */ "conslist ::= conslist tcons", + /* 92 */ "conslist ::= tcons", + /* 93 */ "tcons ::= CONSTRAINT nm", + /* 94 */ "tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf", + /* 95 */ "tcons ::= UNIQUE LP idxlist RP onconf", + /* 96 */ "tcons ::= CHECK LP expr RP onconf", + /* 97 */ "tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt", + /* 98 */ "defer_subclause_opt ::=", + /* 99 */ "defer_subclause_opt ::= defer_subclause", + /* 100 */ "onconf ::=", + /* 101 */ "onconf ::= ON CONFLICT resolvetype", + /* 102 */ "orconf ::=", + /* 103 */ "orconf ::= OR resolvetype", + /* 104 */ "resolvetype ::= raisetype", + /* 105 */ "resolvetype ::= IGNORE", + /* 106 */ "resolvetype ::= REPLACE", + /* 107 */ "cmd ::= DROP TABLE ifexists fullname", + /* 108 */ "ifexists ::= IF EXISTS", + /* 109 */ "ifexists ::=", + /* 110 */ "cmd ::= createkw temp VIEW ifnotexists nm dbnm AS select", + /* 111 */ "cmd ::= DROP VIEW ifexists fullname", + /* 112 */ "cmd ::= select", + /* 113 */ "select ::= oneselect", + /* 114 */ "select ::= select multiselect_op oneselect", + /* 115 */ "multiselect_op ::= UNION", + /* 116 */ "multiselect_op ::= UNION ALL", + /* 117 */ "multiselect_op ::= EXCEPT|INTERSECT", + /* 118 */ "oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt", + /* 119 */ "distinct ::= DISTINCT", + /* 120 */ "distinct ::= ALL", + /* 121 */ "distinct ::=", + /* 122 */ "sclp ::= selcollist COMMA", + /* 123 */ "sclp ::=", + /* 124 */ "selcollist ::= sclp expr as", + /* 125 */ "selcollist ::= sclp STAR", + /* 126 */ "selcollist ::= sclp nm DOT STAR", + /* 127 */ "as ::= AS nm", + /* 128 */ "as ::= ids", + /* 129 */ "as ::=", + /* 130 */ "from ::=", + /* 131 */ "from ::= FROM seltablist", + /* 132 */ "stl_prefix ::= seltablist joinop", + /* 133 */ "stl_prefix ::=", + /* 134 */ "seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt", + /* 135 */ "seltablist ::= stl_prefix LP select RP as on_opt using_opt", + /* 136 */ "seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt", + /* 137 */ "dbnm ::=", + /* 138 */ "dbnm ::= DOT nm", + /* 139 */ "fullname ::= nm dbnm", + /* 140 */ "joinop ::= COMMA|JOIN", + /* 141 */ "joinop ::= JOIN_KW JOIN", + /* 142 */ "joinop ::= JOIN_KW nm JOIN", + /* 143 */ "joinop ::= JOIN_KW nm nm JOIN", + /* 144 */ "on_opt ::= ON expr", + /* 145 */ "on_opt ::=", + /* 146 */ "indexed_opt ::=", + /* 147 */ "indexed_opt ::= INDEXED BY nm", + /* 148 */ "indexed_opt ::= NOT INDEXED", + /* 149 */ "using_opt ::= USING LP inscollist RP", + /* 150 */ "using_opt ::=", + /* 151 */ "orderby_opt ::=", + /* 152 */ "orderby_opt ::= ORDER BY sortlist", + /* 153 */ "sortlist ::= sortlist COMMA sortitem sortorder", + /* 154 */ "sortlist ::= sortitem sortorder", + /* 155 */ "sortitem ::= expr", + /* 156 */ "sortorder ::= ASC", + /* 157 */ "sortorder ::= DESC", + /* 158 */ "sortorder ::=", + /* 159 */ "groupby_opt ::=", + /* 160 */ "groupby_opt ::= GROUP BY nexprlist", + /* 161 */ "having_opt ::=", + /* 162 */ "having_opt ::= HAVING expr", + /* 163 */ "limit_opt ::=", + /* 164 */ "limit_opt ::= LIMIT expr", + /* 165 */ "limit_opt ::= LIMIT expr OFFSET expr", + /* 166 */ "limit_opt ::= LIMIT expr COMMA expr", + /* 167 */ "cmd ::= DELETE FROM fullname indexed_opt where_opt", + /* 168 */ "where_opt ::=", + /* 169 */ "where_opt ::= WHERE expr", + /* 170 */ "cmd ::= UPDATE orconf fullname indexed_opt SET setlist where_opt", + /* 171 */ "setlist ::= setlist COMMA nm EQ expr", + /* 172 */ "setlist ::= nm EQ expr", + /* 173 */ "cmd ::= insert_cmd INTO fullname inscollist_opt VALUES LP itemlist RP", + /* 174 */ "cmd ::= insert_cmd INTO fullname inscollist_opt select", + /* 175 */ "cmd ::= insert_cmd INTO fullname inscollist_opt DEFAULT VALUES", + /* 176 */ "insert_cmd ::= INSERT orconf", + /* 177 */ "insert_cmd ::= REPLACE", + /* 178 */ "itemlist ::= itemlist COMMA expr", + /* 179 */ "itemlist ::= expr", + /* 180 */ "inscollist_opt ::=", + /* 181 */ "inscollist_opt ::= LP inscollist RP", + /* 182 */ "inscollist ::= inscollist COMMA nm", + /* 183 */ "inscollist ::= nm", + /* 184 */ "expr ::= term", + /* 185 */ "expr ::= LP expr RP", + /* 186 */ "term ::= NULL", + /* 187 */ "expr ::= id", + /* 188 */ "expr ::= JOIN_KW", + /* 189 */ "expr ::= nm DOT nm", + /* 190 */ "expr ::= nm DOT nm DOT nm", + /* 191 */ "term ::= INTEGER|FLOAT|BLOB", + /* 192 */ "term ::= STRING", + /* 193 */ "expr ::= REGISTER", + /* 194 */ "expr ::= VARIABLE", + /* 195 */ "expr ::= expr COLLATE ids", + /* 196 */ "expr ::= CAST LP expr AS typetoken RP", + /* 197 */ "expr ::= ID LP distinct exprlist RP", + /* 198 */ "expr ::= ID LP STAR RP", + /* 199 */ "term ::= CTIME_KW", + /* 200 */ "expr ::= expr AND expr", + /* 201 */ "expr ::= expr OR expr", + /* 202 */ "expr ::= expr LT|GT|GE|LE expr", + /* 203 */ "expr ::= expr EQ|NE expr", + /* 204 */ "expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr", + /* 205 */ "expr ::= expr PLUS|MINUS expr", + /* 206 */ "expr ::= expr STAR|SLASH|REM expr", + /* 207 */ "expr ::= expr CONCAT expr", + /* 208 */ "likeop ::= LIKE_KW", + /* 209 */ "likeop ::= NOT LIKE_KW", + /* 210 */ "likeop ::= MATCH", + /* 211 */ "likeop ::= NOT MATCH", + /* 212 */ "escape ::= ESCAPE expr", + /* 213 */ "escape ::=", + /* 214 */ "expr ::= expr likeop expr escape", + /* 215 */ "expr ::= expr ISNULL|NOTNULL", /* 216 */ "expr ::= expr NOT NULL", - /* 217 */ "expr ::= expr IS NOT NULL", - /* 218 */ "expr ::= NOT expr", - /* 219 */ "expr ::= BITNOT expr", - /* 220 */ "expr ::= MINUS expr", - /* 221 */ "expr ::= PLUS expr", - /* 222 */ "between_op ::= BETWEEN", - /* 223 */ "between_op ::= NOT BETWEEN", - /* 224 */ "expr ::= expr between_op expr AND expr", - /* 225 */ "in_op ::= IN", - /* 226 */ "in_op ::= NOT IN", - /* 227 */ "expr ::= expr in_op LP exprlist RP", - /* 228 */ "expr ::= LP select RP", - /* 229 */ "expr ::= expr in_op LP select RP", - /* 230 */ "expr ::= expr in_op nm dbnm", - /* 231 */ "expr ::= EXISTS LP select RP", - /* 232 */ "expr ::= CASE case_operand case_exprlist case_else END", - /* 233 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", - /* 234 */ "case_exprlist ::= WHEN expr THEN expr", - /* 235 */ "case_else ::= ELSE expr", - /* 236 */ "case_else ::=", - /* 237 */ "case_operand ::= expr", - /* 238 */ "case_operand ::=", - /* 239 */ "exprlist ::= nexprlist", - /* 240 */ "exprlist ::=", - /* 241 */ "nexprlist ::= nexprlist COMMA expr", - /* 242 */ "nexprlist ::= expr", - /* 243 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP", - /* 244 */ "uniqueflag ::= UNIQUE", - /* 245 */ "uniqueflag ::=", - /* 246 */ "idxlist_opt ::=", - /* 247 */ "idxlist_opt ::= LP idxlist RP", - /* 248 */ "idxlist ::= idxlist COMMA nm collate sortorder", - /* 249 */ "idxlist ::= nm collate sortorder", - /* 250 */ "collate ::=", - /* 251 */ "collate ::= COLLATE ids", - /* 252 */ "cmd ::= DROP INDEX ifexists fullname", - /* 253 */ "cmd ::= VACUUM", - /* 254 */ "cmd ::= VACUUM nm", - /* 255 */ "cmd ::= PRAGMA nm dbnm", - /* 256 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", - /* 257 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", - /* 258 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", - /* 259 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", - /* 260 */ "nmnum ::= plus_num", - /* 261 */ "nmnum ::= nm", - /* 262 */ "nmnum ::= ON", - /* 263 */ "nmnum ::= DELETE", - /* 264 */ "nmnum ::= DEFAULT", - /* 265 */ "plus_num ::= plus_opt number", - /* 266 */ "minus_num ::= MINUS number", - /* 267 */ "number ::= INTEGER|FLOAT", - /* 268 */ "plus_opt ::= PLUS", - /* 269 */ "plus_opt ::=", - /* 270 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", - /* 271 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", - /* 272 */ "trigger_time ::= BEFORE", - /* 273 */ "trigger_time ::= AFTER", - /* 274 */ "trigger_time ::= INSTEAD OF", - /* 275 */ "trigger_time ::=", - /* 276 */ "trigger_event ::= DELETE|INSERT", - /* 277 */ "trigger_event ::= UPDATE", - /* 278 */ "trigger_event ::= UPDATE OF inscollist", - /* 279 */ "foreach_clause ::=", - /* 280 */ "foreach_clause ::= FOR EACH ROW", - /* 281 */ "when_clause ::=", - /* 282 */ "when_clause ::= WHEN expr", - /* 283 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", - /* 284 */ "trigger_cmd_list ::= trigger_cmd SEMI", - /* 285 */ "trigger_cmd ::= UPDATE orconf nm SET setlist where_opt", - /* 286 */ "trigger_cmd ::= insert_cmd INTO nm inscollist_opt VALUES LP itemlist RP", - /* 287 */ "trigger_cmd ::= insert_cmd INTO nm inscollist_opt select", - /* 288 */ "trigger_cmd ::= DELETE FROM nm where_opt", - /* 289 */ "trigger_cmd ::= select", - /* 290 */ "expr ::= RAISE LP IGNORE RP", - /* 291 */ "expr ::= RAISE LP raisetype COMMA nm RP", - /* 292 */ "raisetype ::= ROLLBACK", - /* 293 */ "raisetype ::= ABORT", - /* 294 */ "raisetype ::= FAIL", - /* 295 */ "cmd ::= DROP TRIGGER ifexists fullname", - /* 296 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", - /* 297 */ "cmd ::= DETACH database_kw_opt expr", - /* 298 */ "key_opt ::=", - /* 299 */ "key_opt ::= KEY expr", - /* 300 */ "database_kw_opt ::= DATABASE", - /* 301 */ "database_kw_opt ::=", - /* 302 */ "cmd ::= REINDEX", - /* 303 */ "cmd ::= REINDEX nm dbnm", - /* 304 */ "cmd ::= ANALYZE", - /* 305 */ "cmd ::= ANALYZE nm dbnm", - /* 306 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", - /* 307 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column", - /* 308 */ "add_column_fullname ::= fullname", - /* 309 */ "kwcolumn_opt ::=", - /* 310 */ "kwcolumn_opt ::= COLUMNKW", - /* 311 */ "cmd ::= create_vtab", - /* 312 */ "cmd ::= create_vtab LP vtabarglist RP", - /* 313 */ "create_vtab ::= createkw VIRTUAL TABLE nm dbnm USING nm", - /* 314 */ "vtabarglist ::= vtabarg", - /* 315 */ "vtabarglist ::= vtabarglist COMMA vtabarg", - /* 316 */ "vtabarg ::=", - /* 317 */ "vtabarg ::= vtabarg vtabargtoken", - /* 318 */ "vtabargtoken ::= ANY", - /* 319 */ "vtabargtoken ::= lp anylist RP", - /* 320 */ "lp ::= LP", - /* 321 */ "anylist ::=", - /* 322 */ "anylist ::= anylist LP anylist RP", - /* 323 */ "anylist ::= anylist ANY", + /* 217 */ "expr ::= expr IS expr", + /* 218 */ "expr ::= expr IS NOT expr", + /* 219 */ "expr ::= NOT expr", + /* 220 */ "expr ::= BITNOT expr", + /* 221 */ "expr ::= MINUS expr", + /* 222 */ "expr ::= PLUS expr", + /* 223 */ "between_op ::= BETWEEN", + /* 224 */ "between_op ::= NOT BETWEEN", + /* 225 */ "expr ::= expr between_op expr AND expr", + /* 226 */ "in_op ::= IN", + /* 227 */ "in_op ::= NOT IN", + /* 228 */ "expr ::= expr in_op LP exprlist RP", + /* 229 */ "expr ::= LP select RP", + /* 230 */ "expr ::= expr in_op LP select RP", + /* 231 */ "expr ::= expr in_op nm dbnm", + /* 232 */ "expr ::= EXISTS LP select RP", + /* 233 */ "expr ::= CASE case_operand case_exprlist case_else END", + /* 234 */ "case_exprlist ::= case_exprlist WHEN expr THEN expr", + /* 235 */ "case_exprlist ::= WHEN expr THEN expr", + /* 236 */ "case_else ::= ELSE expr", + /* 237 */ "case_else ::=", + /* 238 */ "case_operand ::= expr", + /* 239 */ "case_operand ::=", + /* 240 */ "exprlist ::= nexprlist", + /* 241 */ "exprlist ::=", + /* 242 */ "nexprlist ::= nexprlist COMMA expr", + /* 243 */ "nexprlist ::= expr", + /* 244 */ "cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP", + /* 245 */ "uniqueflag ::= UNIQUE", + /* 246 */ "uniqueflag ::=", + /* 247 */ "idxlist_opt ::=", + /* 248 */ "idxlist_opt ::= LP idxlist RP", + /* 249 */ "idxlist ::= idxlist COMMA nm collate sortorder", + /* 250 */ "idxlist ::= nm collate sortorder", + /* 251 */ "collate ::=", + /* 252 */ "collate ::= COLLATE ids", + /* 253 */ "cmd ::= DROP INDEX ifexists fullname", + /* 254 */ "cmd ::= VACUUM", + /* 255 */ "cmd ::= VACUUM nm", + /* 256 */ "cmd ::= PRAGMA nm dbnm", + /* 257 */ "cmd ::= PRAGMA nm dbnm EQ nmnum", + /* 258 */ "cmd ::= PRAGMA nm dbnm LP nmnum RP", + /* 259 */ "cmd ::= PRAGMA nm dbnm EQ minus_num", + /* 260 */ "cmd ::= PRAGMA nm dbnm LP minus_num RP", + /* 261 */ "nmnum ::= plus_num", + /* 262 */ "nmnum ::= nm", + /* 263 */ "nmnum ::= ON", + /* 264 */ "nmnum ::= DELETE", + /* 265 */ "nmnum ::= DEFAULT", + /* 266 */ "plus_num ::= plus_opt number", + /* 267 */ "minus_num ::= MINUS number", + /* 268 */ "number ::= INTEGER|FLOAT", + /* 269 */ "plus_opt ::= PLUS", + /* 270 */ "plus_opt ::=", + /* 271 */ "cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END", + /* 272 */ "trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause", + /* 273 */ "trigger_time ::= BEFORE", + /* 274 */ "trigger_time ::= AFTER", + /* 275 */ "trigger_time ::= INSTEAD OF", + /* 276 */ "trigger_time ::=", + /* 277 */ "trigger_event ::= DELETE|INSERT", + /* 278 */ "trigger_event ::= UPDATE", + /* 279 */ "trigger_event ::= UPDATE OF inscollist", + /* 280 */ "foreach_clause ::=", + /* 281 */ "foreach_clause ::= FOR EACH ROW", + /* 282 */ "when_clause ::=", + /* 283 */ "when_clause ::= WHEN expr", + /* 284 */ "trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI", + /* 285 */ "trigger_cmd_list ::= trigger_cmd SEMI", + /* 286 */ "trnm ::= nm", + /* 287 */ "trnm ::= nm DOT nm", + /* 288 */ "tridxby ::=", + /* 289 */ "tridxby ::= INDEXED BY nm", + /* 290 */ "tridxby ::= NOT INDEXED", + /* 291 */ "trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt", + /* 292 */ "trigger_cmd ::= insert_cmd INTO trnm inscollist_opt VALUES LP itemlist RP", + /* 293 */ "trigger_cmd ::= insert_cmd INTO trnm inscollist_opt select", + /* 294 */ "trigger_cmd ::= DELETE FROM trnm tridxby where_opt", + /* 295 */ "trigger_cmd ::= select", + /* 296 */ "expr ::= RAISE LP IGNORE RP", + /* 297 */ "expr ::= RAISE LP raisetype COMMA nm RP", + /* 298 */ "raisetype ::= ROLLBACK", + /* 299 */ "raisetype ::= ABORT", + /* 300 */ "raisetype ::= FAIL", + /* 301 */ "cmd ::= DROP TRIGGER ifexists fullname", + /* 302 */ "cmd ::= ATTACH database_kw_opt expr AS expr key_opt", + /* 303 */ "cmd ::= DETACH database_kw_opt expr", + /* 304 */ "key_opt ::=", + /* 305 */ "key_opt ::= KEY expr", + /* 306 */ "database_kw_opt ::= DATABASE", + /* 307 */ "database_kw_opt ::=", + /* 308 */ "cmd ::= REINDEX", + /* 309 */ "cmd ::= REINDEX nm dbnm", + /* 310 */ "cmd ::= ANALYZE", + /* 311 */ "cmd ::= ANALYZE nm dbnm", + /* 312 */ "cmd ::= ALTER TABLE fullname RENAME TO nm", + /* 313 */ "cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column", + /* 314 */ "add_column_fullname ::= fullname", + /* 315 */ "kwcolumn_opt ::=", + /* 316 */ "kwcolumn_opt ::= COLUMNKW", + /* 317 */ "cmd ::= create_vtab", + /* 318 */ "cmd ::= create_vtab LP vtabarglist RP", + /* 319 */ "create_vtab ::= createkw VIRTUAL TABLE nm dbnm USING nm", + /* 320 */ "vtabarglist ::= vtabarg", + /* 321 */ "vtabarglist ::= vtabarglist COMMA vtabarg", + /* 322 */ "vtabarg ::=", + /* 323 */ "vtabarg ::= vtabarg vtabargtoken", + /* 324 */ "vtabargtoken ::= ANY", + /* 325 */ "vtabargtoken ::= lp anylist RP", + /* 326 */ "lp ::= LP", + /* 327 */ "anylist ::=", + /* 328 */ "anylist ::= anylist LP anylist RP", + /* 329 */ "anylist ::= anylist ANY", }; #endif /* NDEBUG */ @@ -87502,14 +92005,14 @@ case 160: /* select */ case 194: /* oneselect */ { -sqlite3SelectDelete(pParse->db, (yypminor->yy243)); +sqlite3SelectDelete(pParse->db, (yypminor->yy3)); } break; case 174: /* term */ case 175: /* expr */ case 223: /* escape */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy190).pExpr); +sqlite3ExprDelete(pParse->db, (yypminor->yy346).pExpr); } break; case 179: /* idxlist_opt */ @@ -87525,7 +92028,7 @@ case 221: /* exprlist */ case 227: /* case_exprlist */ { -sqlite3ExprListDelete(pParse->db, (yypminor->yy148)); +sqlite3ExprListDelete(pParse->db, (yypminor->yy14)); } break; case 193: /* fullname */ @@ -87533,7 +92036,7 @@ case 206: /* seltablist */ case 207: /* stl_prefix */ { -sqlite3SrcListDelete(pParse->db, (yypminor->yy185)); +sqlite3SrcListDelete(pParse->db, (yypminor->yy65)); } break; case 199: /* where_opt */ @@ -87543,27 +92046,27 @@ case 226: /* case_operand */ case 228: /* case_else */ case 239: /* when_clause */ - case 242: /* key_opt */ + case 244: /* key_opt */ { -sqlite3ExprDelete(pParse->db, (yypminor->yy72)); +sqlite3ExprDelete(pParse->db, (yypminor->yy132)); } break; case 211: /* using_opt */ case 213: /* inscollist */ case 219: /* inscollist_opt */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy254)); +sqlite3IdListDelete(pParse->db, (yypminor->yy408)); } break; case 235: /* trigger_cmd_list */ case 240: /* trigger_cmd */ { -sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy145)); +sqlite3DeleteTriggerStep(pParse->db, (yypminor->yy473)); } break; case 237: /* trigger_event */ { -sqlite3IdListDelete(pParse->db, (yypminor->yy332).b); +sqlite3IdListDelete(pParse->db, (yypminor->yy378).b); } break; default: break; /* If no destructor action specified: do nothing */ @@ -87582,7 +92085,9 @@ YYCODETYPE yymajor; yyStackEntry *yytos = &pParser->yystack[pParser->yyidx]; - if( pParser->yyidx<0 ) return 0; + /* There is no mechanism by which the parser stack can be popped below + ** empty in SQLite. */ + if( NEVER(pParser->yyidx<0) ) return 0; #ifndef NDEBUG if( yyTraceFILE && pParser->yyidx>=0 ){ fprintf(yyTraceFILE,"%sPopping %s\n", @@ -87613,7 +92118,9 @@ void (*freeProc)(void*) /* Function used to reclaim memory */ ){ yyParser *pParser = (yyParser*)p; - if( pParser==0 ) return; + /* In SQLite, we never try to destroy a parser that was not successfully + ** created in the first place. */ + if( NEVER(pParser==0) ) return; while( pParser->yyidx>=0 ) yy_pop_parser_stack(pParser); #if YYSTACKDEPTH<=0 free(pParser->yystack); @@ -87646,12 +92153,13 @@ int i; int stateno = pParser->yystack[pParser->yyidx].stateno; - if( stateno>YY_SHIFT_MAX || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ + if( stateno>YY_SHIFT_COUNT + || (i = yy_shift_ofst[stateno])==YY_SHIFT_USE_DFLT ){ return yy_default[stateno]; } assert( iLookAhead!=YYNOCODE ); i += iLookAhead; - if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ if( iLookAhead>0 ){ #ifdef YYFALLBACK YYCODETYPE iFallback; /* Fallback token */ @@ -87669,7 +92177,15 @@ #ifdef YYWILDCARD { int j = i - iLookAhead + YYWILDCARD; - if( j>=0 && j=0 && +#endif +#if YY_SHIFT_MAX+YYWILDCARD>=YY_ACTTAB_COUNT + j %s\n", @@ -87701,22 +92217,22 @@ ){ int i; #ifdef YYERRORSYMBOL - if( stateno>YY_REDUCE_MAX ){ + if( stateno>YY_REDUCE_COUNT ){ return yy_default[stateno]; } #else - assert( stateno<=YY_REDUCE_MAX ); + assert( stateno<=YY_REDUCE_COUNT ); #endif i = yy_reduce_ofst[stateno]; assert( i!=YY_REDUCE_USE_DFLT ); assert( iLookAhead!=YYNOCODE ); i += iLookAhead; #ifdef YYERRORSYMBOL - if( i<0 || i>=YY_SZ_ACTTAB || yy_lookahead[i]!=iLookAhead ){ + if( i<0 || i>=YY_ACTTAB_COUNT || yy_lookahead[i]!=iLookAhead ){ return yy_default[stateno]; } #else - assert( i>=0 && i=0 && idb, yymsp[0].minor.yy243); + sqlite3EndTable(pParse,0,0,yymsp[0].minor.yy3); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy3); } break; case 36: /* column ::= columnid type carglist */ @@ -88286,19 +92809,20 @@ case 43: /* nm ::= JOIN_KW */ yytestcase(yyruleno==43); case 46: /* typetoken ::= typename */ yytestcase(yyruleno==46); case 49: /* typename ::= ids */ yytestcase(yyruleno==49); - case 126: /* as ::= AS nm */ yytestcase(yyruleno==126); - case 127: /* as ::= ids */ yytestcase(yyruleno==127); - case 137: /* dbnm ::= DOT nm */ yytestcase(yyruleno==137); - case 146: /* indexed_opt ::= INDEXED BY nm */ yytestcase(yyruleno==146); - case 251: /* collate ::= COLLATE ids */ yytestcase(yyruleno==251); - case 260: /* nmnum ::= plus_num */ yytestcase(yyruleno==260); - case 261: /* nmnum ::= nm */ yytestcase(yyruleno==261); - case 262: /* nmnum ::= ON */ yytestcase(yyruleno==262); - case 263: /* nmnum ::= DELETE */ yytestcase(yyruleno==263); - case 264: /* nmnum ::= DEFAULT */ yytestcase(yyruleno==264); - case 265: /* plus_num ::= plus_opt number */ yytestcase(yyruleno==265); - case 266: /* minus_num ::= MINUS number */ yytestcase(yyruleno==266); - case 267: /* number ::= INTEGER|FLOAT */ yytestcase(yyruleno==267); + case 127: /* as ::= AS nm */ yytestcase(yyruleno==127); + case 128: /* as ::= ids */ yytestcase(yyruleno==128); + case 138: /* dbnm ::= DOT nm */ yytestcase(yyruleno==138); + case 147: /* indexed_opt ::= INDEXED BY nm */ yytestcase(yyruleno==147); + case 252: /* collate ::= COLLATE ids */ yytestcase(yyruleno==252); + case 261: /* nmnum ::= plus_num */ yytestcase(yyruleno==261); + case 262: /* nmnum ::= nm */ yytestcase(yyruleno==262); + case 263: /* nmnum ::= ON */ yytestcase(yyruleno==263); + case 264: /* nmnum ::= DELETE */ yytestcase(yyruleno==264); + case 265: /* nmnum ::= DEFAULT */ yytestcase(yyruleno==265); + case 266: /* plus_num ::= plus_opt number */ yytestcase(yyruleno==266); + case 267: /* minus_num ::= MINUS number */ yytestcase(yyruleno==267); + case 268: /* number ::= INTEGER|FLOAT */ yytestcase(yyruleno==268); + case 286: /* trnm ::= nm */ yytestcase(yyruleno==286); {yygotominor.yy0 = yymsp[0].minor.yy0;} break; case 45: /* type ::= typetoken */ @@ -88321,17 +92845,17 @@ break; case 57: /* ccons ::= DEFAULT term */ case 59: /* ccons ::= DEFAULT PLUS term */ yytestcase(yyruleno==59); -{sqlite3AddDefaultValue(pParse,&yymsp[0].minor.yy190);} +{sqlite3AddDefaultValue(pParse,&yymsp[0].minor.yy346);} break; case 58: /* ccons ::= DEFAULT LP expr RP */ -{sqlite3AddDefaultValue(pParse,&yymsp[-1].minor.yy190);} +{sqlite3AddDefaultValue(pParse,&yymsp[-1].minor.yy346);} break; case 60: /* ccons ::= DEFAULT MINUS term */ { ExprSpan v; - v.pExpr = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy190.pExpr, 0, 0); + v.pExpr = sqlite3PExpr(pParse, TK_UMINUS, yymsp[0].minor.yy346.pExpr, 0, 0); v.zStart = yymsp[-1].minor.yy0.z; - v.zEnd = yymsp[0].minor.yy190.zEnd; + v.zEnd = yymsp[0].minor.yy346.zEnd; sqlite3AddDefaultValue(pParse,&v); } break; @@ -88343,837 +92867,871 @@ } break; case 63: /* ccons ::= NOT NULL onconf */ -{sqlite3AddNotNull(pParse, yymsp[0].minor.yy194);} +{sqlite3AddNotNull(pParse, yymsp[0].minor.yy328);} break; case 64: /* ccons ::= PRIMARY KEY sortorder onconf autoinc */ -{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy194,yymsp[0].minor.yy194,yymsp[-2].minor.yy194);} +{sqlite3AddPrimaryKey(pParse,0,yymsp[-1].minor.yy328,yymsp[0].minor.yy328,yymsp[-2].minor.yy328);} break; case 65: /* ccons ::= UNIQUE onconf */ -{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy194,0,0,0,0);} +{sqlite3CreateIndex(pParse,0,0,0,0,yymsp[0].minor.yy328,0,0,0,0);} break; case 66: /* ccons ::= CHECK LP expr RP */ -{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy190.pExpr);} +{sqlite3AddCheckConstraint(pParse,yymsp[-1].minor.yy346.pExpr);} break; case 67: /* ccons ::= REFERENCES nm idxlist_opt refargs */ -{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy148,yymsp[0].minor.yy194);} +{sqlite3CreateForeignKey(pParse,0,&yymsp[-2].minor.yy0,yymsp[-1].minor.yy14,yymsp[0].minor.yy328);} break; case 68: /* ccons ::= defer_subclause */ -{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy194);} +{sqlite3DeferForeignKey(pParse,yymsp[0].minor.yy328);} break; case 69: /* ccons ::= COLLATE ids */ {sqlite3AddCollateType(pParse, &yymsp[0].minor.yy0);} break; case 72: /* refargs ::= */ -{ yygotominor.yy194 = OE_Restrict * 0x010101; } +{ yygotominor.yy328 = OE_None*0x0101; /* EV: R-19803-45884 */} break; case 73: /* refargs ::= refargs refarg */ -{ yygotominor.yy194 = (yymsp[-1].minor.yy194 & ~yymsp[0].minor.yy497.mask) | yymsp[0].minor.yy497.value; } +{ yygotominor.yy328 = (yymsp[-1].minor.yy328 & ~yymsp[0].minor.yy429.mask) | yymsp[0].minor.yy429.value; } break; case 74: /* refarg ::= MATCH nm */ -{ yygotominor.yy497.value = 0; yygotominor.yy497.mask = 0x000000; } - break; - case 75: /* refarg ::= ON DELETE refact */ -{ yygotominor.yy497.value = yymsp[0].minor.yy194; yygotominor.yy497.mask = 0x0000ff; } + case 75: /* refarg ::= ON INSERT refact */ yytestcase(yyruleno==75); +{ yygotominor.yy429.value = 0; yygotominor.yy429.mask = 0x000000; } break; - case 76: /* refarg ::= ON UPDATE refact */ -{ yygotominor.yy497.value = yymsp[0].minor.yy194<<8; yygotominor.yy497.mask = 0x00ff00; } + case 76: /* refarg ::= ON DELETE refact */ +{ yygotominor.yy429.value = yymsp[0].minor.yy328; yygotominor.yy429.mask = 0x0000ff; } break; - case 77: /* refarg ::= ON INSERT refact */ -{ yygotominor.yy497.value = yymsp[0].minor.yy194<<16; yygotominor.yy497.mask = 0xff0000; } + case 77: /* refarg ::= ON UPDATE refact */ +{ yygotominor.yy429.value = yymsp[0].minor.yy328<<8; yygotominor.yy429.mask = 0x00ff00; } break; case 78: /* refact ::= SET NULL */ -{ yygotominor.yy194 = OE_SetNull; } +{ yygotominor.yy328 = OE_SetNull; /* EV: R-33326-45252 */} break; case 79: /* refact ::= SET DEFAULT */ -{ yygotominor.yy194 = OE_SetDflt; } +{ yygotominor.yy328 = OE_SetDflt; /* EV: R-33326-45252 */} break; case 80: /* refact ::= CASCADE */ -{ yygotominor.yy194 = OE_Cascade; } +{ yygotominor.yy328 = OE_Cascade; /* EV: R-33326-45252 */} break; case 81: /* refact ::= RESTRICT */ -{ yygotominor.yy194 = OE_Restrict; } +{ yygotominor.yy328 = OE_Restrict; /* EV: R-33326-45252 */} break; - case 82: /* defer_subclause ::= NOT DEFERRABLE init_deferred_pred_opt */ - case 83: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ yytestcase(yyruleno==83); - case 98: /* defer_subclause_opt ::= defer_subclause */ yytestcase(yyruleno==98); - case 100: /* onconf ::= ON CONFLICT resolvetype */ yytestcase(yyruleno==100); - case 102: /* orconf ::= OR resolvetype */ yytestcase(yyruleno==102); - case 103: /* resolvetype ::= raisetype */ yytestcase(yyruleno==103); - case 175: /* insert_cmd ::= INSERT orconf */ yytestcase(yyruleno==175); -{yygotominor.yy194 = yymsp[0].minor.yy194;} + case 82: /* refact ::= NO ACTION */ +{ yygotominor.yy328 = OE_None; /* EV: R-33326-45252 */} break; - case 87: /* conslist_opt ::= */ + case 84: /* defer_subclause ::= DEFERRABLE init_deferred_pred_opt */ + case 99: /* defer_subclause_opt ::= defer_subclause */ yytestcase(yyruleno==99); + case 101: /* onconf ::= ON CONFLICT resolvetype */ yytestcase(yyruleno==101); + case 104: /* resolvetype ::= raisetype */ yytestcase(yyruleno==104); +{yygotominor.yy328 = yymsp[0].minor.yy328;} + break; + case 88: /* conslist_opt ::= */ {yygotominor.yy0.n = 0; yygotominor.yy0.z = 0;} break; - case 88: /* conslist_opt ::= COMMA conslist */ + case 89: /* conslist_opt ::= COMMA conslist */ {yygotominor.yy0 = yymsp[-1].minor.yy0;} break; - case 93: /* tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf */ -{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy148,yymsp[0].minor.yy194,yymsp[-2].minor.yy194,0);} + case 94: /* tcons ::= PRIMARY KEY LP idxlist autoinc RP onconf */ +{sqlite3AddPrimaryKey(pParse,yymsp[-3].minor.yy14,yymsp[0].minor.yy328,yymsp[-2].minor.yy328,0);} break; - case 94: /* tcons ::= UNIQUE LP idxlist RP onconf */ -{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy148,yymsp[0].minor.yy194,0,0,0,0);} + case 95: /* tcons ::= UNIQUE LP idxlist RP onconf */ +{sqlite3CreateIndex(pParse,0,0,0,yymsp[-2].minor.yy14,yymsp[0].minor.yy328,0,0,0,0);} break; - case 95: /* tcons ::= CHECK LP expr RP onconf */ -{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy190.pExpr);} + case 96: /* tcons ::= CHECK LP expr RP onconf */ +{sqlite3AddCheckConstraint(pParse,yymsp[-2].minor.yy346.pExpr);} break; - case 96: /* tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt */ + case 97: /* tcons ::= FOREIGN KEY LP idxlist RP REFERENCES nm idxlist_opt refargs defer_subclause_opt */ { - sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy148, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy148, yymsp[-1].minor.yy194); - sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy194); + sqlite3CreateForeignKey(pParse, yymsp[-6].minor.yy14, &yymsp[-3].minor.yy0, yymsp[-2].minor.yy14, yymsp[-1].minor.yy328); + sqlite3DeferForeignKey(pParse, yymsp[0].minor.yy328); } break; - case 99: /* onconf ::= */ - case 101: /* orconf ::= */ yytestcase(yyruleno==101); -{yygotominor.yy194 = OE_Default;} + case 100: /* onconf ::= */ +{yygotominor.yy328 = OE_Default;} + break; + case 102: /* orconf ::= */ +{yygotominor.yy186 = OE_Default;} break; - case 104: /* resolvetype ::= IGNORE */ -{yygotominor.yy194 = OE_Ignore;} + case 103: /* orconf ::= OR resolvetype */ +{yygotominor.yy186 = (u8)yymsp[0].minor.yy328;} break; - case 105: /* resolvetype ::= REPLACE */ - case 176: /* insert_cmd ::= REPLACE */ yytestcase(yyruleno==176); -{yygotominor.yy194 = OE_Replace;} + case 105: /* resolvetype ::= IGNORE */ +{yygotominor.yy328 = OE_Ignore;} break; - case 106: /* cmd ::= DROP TABLE ifexists fullname */ + case 106: /* resolvetype ::= REPLACE */ +{yygotominor.yy328 = OE_Replace;} + break; + case 107: /* cmd ::= DROP TABLE ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy185, 0, yymsp[-1].minor.yy194); + sqlite3DropTable(pParse, yymsp[0].minor.yy65, 0, yymsp[-1].minor.yy328); } break; - case 109: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm AS select */ + case 110: /* cmd ::= createkw temp VIEW ifnotexists nm dbnm AS select */ { - sqlite3CreateView(pParse, &yymsp[-7].minor.yy0, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, yymsp[0].minor.yy243, yymsp[-6].minor.yy194, yymsp[-4].minor.yy194); + sqlite3CreateView(pParse, &yymsp[-7].minor.yy0, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, yymsp[0].minor.yy3, yymsp[-6].minor.yy328, yymsp[-4].minor.yy328); } break; - case 110: /* cmd ::= DROP VIEW ifexists fullname */ + case 111: /* cmd ::= DROP VIEW ifexists fullname */ { - sqlite3DropTable(pParse, yymsp[0].minor.yy185, 1, yymsp[-1].minor.yy194); + sqlite3DropTable(pParse, yymsp[0].minor.yy65, 1, yymsp[-1].minor.yy328); } break; - case 111: /* cmd ::= select */ + case 112: /* cmd ::= select */ { SelectDest dest = {SRT_Output, 0, 0, 0, 0}; - sqlite3Select(pParse, yymsp[0].minor.yy243, &dest); - sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy243); + sqlite3Select(pParse, yymsp[0].minor.yy3, &dest); + sqlite3SelectDelete(pParse->db, yymsp[0].minor.yy3); } break; - case 112: /* select ::= oneselect */ -{yygotominor.yy243 = yymsp[0].minor.yy243;} + case 113: /* select ::= oneselect */ +{yygotominor.yy3 = yymsp[0].minor.yy3;} break; - case 113: /* select ::= select multiselect_op oneselect */ + case 114: /* select ::= select multiselect_op oneselect */ { - if( yymsp[0].minor.yy243 ){ - yymsp[0].minor.yy243->op = (u8)yymsp[-1].minor.yy194; - yymsp[0].minor.yy243->pPrior = yymsp[-2].minor.yy243; + if( yymsp[0].minor.yy3 ){ + yymsp[0].minor.yy3->op = (u8)yymsp[-1].minor.yy328; + yymsp[0].minor.yy3->pPrior = yymsp[-2].minor.yy3; }else{ - sqlite3SelectDelete(pParse->db, yymsp[-2].minor.yy243); + sqlite3SelectDelete(pParse->db, yymsp[-2].minor.yy3); } - yygotominor.yy243 = yymsp[0].minor.yy243; + yygotominor.yy3 = yymsp[0].minor.yy3; } break; - case 115: /* multiselect_op ::= UNION ALL */ -{yygotominor.yy194 = TK_ALL;} + case 116: /* multiselect_op ::= UNION ALL */ +{yygotominor.yy328 = TK_ALL;} break; - case 117: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ + case 118: /* oneselect ::= SELECT distinct selcollist from where_opt groupby_opt having_opt orderby_opt limit_opt */ { - yygotominor.yy243 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy148,yymsp[-5].minor.yy185,yymsp[-4].minor.yy72,yymsp[-3].minor.yy148,yymsp[-2].minor.yy72,yymsp[-1].minor.yy148,yymsp[-7].minor.yy194,yymsp[0].minor.yy354.pLimit,yymsp[0].minor.yy354.pOffset); + yygotominor.yy3 = sqlite3SelectNew(pParse,yymsp[-6].minor.yy14,yymsp[-5].minor.yy65,yymsp[-4].minor.yy132,yymsp[-3].minor.yy14,yymsp[-2].minor.yy132,yymsp[-1].minor.yy14,yymsp[-7].minor.yy328,yymsp[0].minor.yy476.pLimit,yymsp[0].minor.yy476.pOffset); } break; - case 121: /* sclp ::= selcollist COMMA */ - case 247: /* idxlist_opt ::= LP idxlist RP */ yytestcase(yyruleno==247); -{yygotominor.yy148 = yymsp[-1].minor.yy148;} + case 122: /* sclp ::= selcollist COMMA */ + case 248: /* idxlist_opt ::= LP idxlist RP */ yytestcase(yyruleno==248); +{yygotominor.yy14 = yymsp[-1].minor.yy14;} break; - case 122: /* sclp ::= */ - case 150: /* orderby_opt ::= */ yytestcase(yyruleno==150); - case 158: /* groupby_opt ::= */ yytestcase(yyruleno==158); - case 240: /* exprlist ::= */ yytestcase(yyruleno==240); - case 246: /* idxlist_opt ::= */ yytestcase(yyruleno==246); -{yygotominor.yy148 = 0;} + case 123: /* sclp ::= */ + case 151: /* orderby_opt ::= */ yytestcase(yyruleno==151); + case 159: /* groupby_opt ::= */ yytestcase(yyruleno==159); + case 241: /* exprlist ::= */ yytestcase(yyruleno==241); + case 247: /* idxlist_opt ::= */ yytestcase(yyruleno==247); +{yygotominor.yy14 = 0;} break; - case 123: /* selcollist ::= sclp expr as */ + case 124: /* selcollist ::= sclp expr as */ { - yygotominor.yy148 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy148, yymsp[-1].minor.yy190.pExpr); - if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yygotominor.yy148, &yymsp[0].minor.yy0, 1); - sqlite3ExprListSetSpan(pParse,yygotominor.yy148,&yymsp[-1].minor.yy190); + yygotominor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-2].minor.yy14, yymsp[-1].minor.yy346.pExpr); + if( yymsp[0].minor.yy0.n>0 ) sqlite3ExprListSetName(pParse, yygotominor.yy14, &yymsp[0].minor.yy0, 1); + sqlite3ExprListSetSpan(pParse,yygotominor.yy14,&yymsp[-1].minor.yy346); } break; - case 124: /* selcollist ::= sclp STAR */ + case 125: /* selcollist ::= sclp STAR */ { Expr *p = sqlite3Expr(pParse->db, TK_ALL, 0); - yygotominor.yy148 = sqlite3ExprListAppend(pParse, yymsp[-1].minor.yy148, p); + yygotominor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-1].minor.yy14, p); } break; - case 125: /* selcollist ::= sclp nm DOT STAR */ + case 126: /* selcollist ::= sclp nm DOT STAR */ { Expr *pRight = sqlite3PExpr(pParse, TK_ALL, 0, 0, &yymsp[0].minor.yy0); Expr *pLeft = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0); Expr *pDot = sqlite3PExpr(pParse, TK_DOT, pLeft, pRight, 0); - yygotominor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy148, pDot); + yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy14, pDot); } break; - case 128: /* as ::= */ + case 129: /* as ::= */ {yygotominor.yy0.n = 0;} break; - case 129: /* from ::= */ -{yygotominor.yy185 = sqlite3DbMallocZero(pParse->db, sizeof(*yygotominor.yy185));} + case 130: /* from ::= */ +{yygotominor.yy65 = sqlite3DbMallocZero(pParse->db, sizeof(*yygotominor.yy65));} break; - case 130: /* from ::= FROM seltablist */ + case 131: /* from ::= FROM seltablist */ { - yygotominor.yy185 = yymsp[0].minor.yy185; - sqlite3SrcListShiftJoinType(yygotominor.yy185); + yygotominor.yy65 = yymsp[0].minor.yy65; + sqlite3SrcListShiftJoinType(yygotominor.yy65); } break; - case 131: /* stl_prefix ::= seltablist joinop */ + case 132: /* stl_prefix ::= seltablist joinop */ { - yygotominor.yy185 = yymsp[-1].minor.yy185; - if( ALWAYS(yygotominor.yy185 && yygotominor.yy185->nSrc>0) ) yygotominor.yy185->a[yygotominor.yy185->nSrc-1].jointype = (u8)yymsp[0].minor.yy194; + yygotominor.yy65 = yymsp[-1].minor.yy65; + if( ALWAYS(yygotominor.yy65 && yygotominor.yy65->nSrc>0) ) yygotominor.yy65->a[yygotominor.yy65->nSrc-1].jointype = (u8)yymsp[0].minor.yy328; } break; - case 132: /* stl_prefix ::= */ -{yygotominor.yy185 = 0;} + case 133: /* stl_prefix ::= */ +{yygotominor.yy65 = 0;} break; - case 133: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ + case 134: /* seltablist ::= stl_prefix nm dbnm as indexed_opt on_opt using_opt */ { - yygotominor.yy185 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy185,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy72,yymsp[0].minor.yy254); - sqlite3SrcListIndexedBy(pParse, yygotominor.yy185, &yymsp[-2].minor.yy0); + yygotominor.yy65 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy65,&yymsp[-5].minor.yy0,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,0,yymsp[-1].minor.yy132,yymsp[0].minor.yy408); + sqlite3SrcListIndexedBy(pParse, yygotominor.yy65, &yymsp[-2].minor.yy0); } break; - case 134: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */ + case 135: /* seltablist ::= stl_prefix LP select RP as on_opt using_opt */ { - yygotominor.yy185 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy185,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy243,yymsp[-1].minor.yy72,yymsp[0].minor.yy254); + yygotominor.yy65 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy65,0,0,&yymsp[-2].minor.yy0,yymsp[-4].minor.yy3,yymsp[-1].minor.yy132,yymsp[0].minor.yy408); } break; - case 135: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ + case 136: /* seltablist ::= stl_prefix LP seltablist RP as on_opt using_opt */ { - if( yymsp[-6].minor.yy185==0 ){ - sqlite3ExprDelete(pParse->db, yymsp[-1].minor.yy72); - sqlite3IdListDelete(pParse->db, yymsp[0].minor.yy254); - yygotominor.yy185 = yymsp[-4].minor.yy185; + if( yymsp[-6].minor.yy65==0 && yymsp[-2].minor.yy0.n==0 && yymsp[-1].minor.yy132==0 && yymsp[0].minor.yy408==0 ){ + yygotominor.yy65 = yymsp[-4].minor.yy65; }else{ Select *pSubquery; - sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy185); - pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy185,0,0,0,0,0,0,0); - yygotominor.yy185 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy185,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy72,yymsp[0].minor.yy254); + sqlite3SrcListShiftJoinType(yymsp[-4].minor.yy65); + pSubquery = sqlite3SelectNew(pParse,0,yymsp[-4].minor.yy65,0,0,0,0,0,0,0); + yygotominor.yy65 = sqlite3SrcListAppendFromTerm(pParse,yymsp[-6].minor.yy65,0,0,&yymsp[-2].minor.yy0,pSubquery,yymsp[-1].minor.yy132,yymsp[0].minor.yy408); } } break; - case 136: /* dbnm ::= */ - case 145: /* indexed_opt ::= */ yytestcase(yyruleno==145); + case 137: /* dbnm ::= */ + case 146: /* indexed_opt ::= */ yytestcase(yyruleno==146); {yygotominor.yy0.z=0; yygotominor.yy0.n=0;} break; - case 138: /* fullname ::= nm dbnm */ -{yygotominor.yy185 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} + case 139: /* fullname ::= nm dbnm */ +{yygotominor.yy65 = sqlite3SrcListAppend(pParse->db,0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0);} break; - case 139: /* joinop ::= COMMA|JOIN */ -{ yygotominor.yy194 = JT_INNER; } + case 140: /* joinop ::= COMMA|JOIN */ +{ yygotominor.yy328 = JT_INNER; } break; - case 140: /* joinop ::= JOIN_KW JOIN */ -{ yygotominor.yy194 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); } + case 141: /* joinop ::= JOIN_KW JOIN */ +{ yygotominor.yy328 = sqlite3JoinType(pParse,&yymsp[-1].minor.yy0,0,0); } break; - case 141: /* joinop ::= JOIN_KW nm JOIN */ -{ yygotominor.yy194 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); } - break; - case 142: /* joinop ::= JOIN_KW nm nm JOIN */ -{ yygotominor.yy194 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); } - break; - case 143: /* on_opt ::= ON expr */ - case 154: /* sortitem ::= expr */ yytestcase(yyruleno==154); - case 161: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==161); - case 168: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==168); - case 235: /* case_else ::= ELSE expr */ yytestcase(yyruleno==235); - case 237: /* case_operand ::= expr */ yytestcase(yyruleno==237); -{yygotominor.yy72 = yymsp[0].minor.yy190.pExpr;} - break; - case 144: /* on_opt ::= */ - case 160: /* having_opt ::= */ yytestcase(yyruleno==160); - case 167: /* where_opt ::= */ yytestcase(yyruleno==167); - case 236: /* case_else ::= */ yytestcase(yyruleno==236); - case 238: /* case_operand ::= */ yytestcase(yyruleno==238); -{yygotominor.yy72 = 0;} + case 142: /* joinop ::= JOIN_KW nm JOIN */ +{ yygotominor.yy328 = sqlite3JoinType(pParse,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0,0); } + break; + case 143: /* joinop ::= JOIN_KW nm nm JOIN */ +{ yygotominor.yy328 = sqlite3JoinType(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[-1].minor.yy0); } + break; + case 144: /* on_opt ::= ON expr */ + case 155: /* sortitem ::= expr */ yytestcase(yyruleno==155); + case 162: /* having_opt ::= HAVING expr */ yytestcase(yyruleno==162); + case 169: /* where_opt ::= WHERE expr */ yytestcase(yyruleno==169); + case 236: /* case_else ::= ELSE expr */ yytestcase(yyruleno==236); + case 238: /* case_operand ::= expr */ yytestcase(yyruleno==238); +{yygotominor.yy132 = yymsp[0].minor.yy346.pExpr;} + break; + case 145: /* on_opt ::= */ + case 161: /* having_opt ::= */ yytestcase(yyruleno==161); + case 168: /* where_opt ::= */ yytestcase(yyruleno==168); + case 237: /* case_else ::= */ yytestcase(yyruleno==237); + case 239: /* case_operand ::= */ yytestcase(yyruleno==239); +{yygotominor.yy132 = 0;} break; - case 147: /* indexed_opt ::= NOT INDEXED */ + case 148: /* indexed_opt ::= NOT INDEXED */ {yygotominor.yy0.z=0; yygotominor.yy0.n=1;} break; - case 148: /* using_opt ::= USING LP inscollist RP */ - case 180: /* inscollist_opt ::= LP inscollist RP */ yytestcase(yyruleno==180); -{yygotominor.yy254 = yymsp[-1].minor.yy254;} - break; - case 149: /* using_opt ::= */ - case 179: /* inscollist_opt ::= */ yytestcase(yyruleno==179); -{yygotominor.yy254 = 0;} - break; - case 151: /* orderby_opt ::= ORDER BY sortlist */ - case 159: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==159); - case 239: /* exprlist ::= nexprlist */ yytestcase(yyruleno==239); -{yygotominor.yy148 = yymsp[0].minor.yy148;} + case 149: /* using_opt ::= USING LP inscollist RP */ + case 181: /* inscollist_opt ::= LP inscollist RP */ yytestcase(yyruleno==181); +{yygotominor.yy408 = yymsp[-1].minor.yy408;} + break; + case 150: /* using_opt ::= */ + case 180: /* inscollist_opt ::= */ yytestcase(yyruleno==180); +{yygotominor.yy408 = 0;} + break; + case 152: /* orderby_opt ::= ORDER BY sortlist */ + case 160: /* groupby_opt ::= GROUP BY nexprlist */ yytestcase(yyruleno==160); + case 240: /* exprlist ::= nexprlist */ yytestcase(yyruleno==240); +{yygotominor.yy14 = yymsp[0].minor.yy14;} break; - case 152: /* sortlist ::= sortlist COMMA sortitem sortorder */ + case 153: /* sortlist ::= sortlist COMMA sortitem sortorder */ { - yygotominor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy148,yymsp[-1].minor.yy72); - if( yygotominor.yy148 ) yygotominor.yy148->a[yygotominor.yy148->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy194; + yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-3].minor.yy14,yymsp[-1].minor.yy132); + if( yygotominor.yy14 ) yygotominor.yy14->a[yygotominor.yy14->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy328; } break; - case 153: /* sortlist ::= sortitem sortorder */ + case 154: /* sortlist ::= sortitem sortorder */ { - yygotominor.yy148 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy72); - if( yygotominor.yy148 && ALWAYS(yygotominor.yy148->a) ) yygotominor.yy148->a[0].sortOrder = (u8)yymsp[0].minor.yy194; + yygotominor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[-1].minor.yy132); + if( yygotominor.yy14 && ALWAYS(yygotominor.yy14->a) ) yygotominor.yy14->a[0].sortOrder = (u8)yymsp[0].minor.yy328; } break; - case 155: /* sortorder ::= ASC */ - case 157: /* sortorder ::= */ yytestcase(yyruleno==157); -{yygotominor.yy194 = SQLITE_SO_ASC;} + case 156: /* sortorder ::= ASC */ + case 158: /* sortorder ::= */ yytestcase(yyruleno==158); +{yygotominor.yy328 = SQLITE_SO_ASC;} break; - case 156: /* sortorder ::= DESC */ -{yygotominor.yy194 = SQLITE_SO_DESC;} + case 157: /* sortorder ::= DESC */ +{yygotominor.yy328 = SQLITE_SO_DESC;} break; - case 162: /* limit_opt ::= */ -{yygotominor.yy354.pLimit = 0; yygotominor.yy354.pOffset = 0;} + case 163: /* limit_opt ::= */ +{yygotominor.yy476.pLimit = 0; yygotominor.yy476.pOffset = 0;} break; - case 163: /* limit_opt ::= LIMIT expr */ -{yygotominor.yy354.pLimit = yymsp[0].minor.yy190.pExpr; yygotominor.yy354.pOffset = 0;} + case 164: /* limit_opt ::= LIMIT expr */ +{yygotominor.yy476.pLimit = yymsp[0].minor.yy346.pExpr; yygotominor.yy476.pOffset = 0;} break; - case 164: /* limit_opt ::= LIMIT expr OFFSET expr */ -{yygotominor.yy354.pLimit = yymsp[-2].minor.yy190.pExpr; yygotominor.yy354.pOffset = yymsp[0].minor.yy190.pExpr;} + case 165: /* limit_opt ::= LIMIT expr OFFSET expr */ +{yygotominor.yy476.pLimit = yymsp[-2].minor.yy346.pExpr; yygotominor.yy476.pOffset = yymsp[0].minor.yy346.pExpr;} break; - case 165: /* limit_opt ::= LIMIT expr COMMA expr */ -{yygotominor.yy354.pOffset = yymsp[-2].minor.yy190.pExpr; yygotominor.yy354.pLimit = yymsp[0].minor.yy190.pExpr;} + case 166: /* limit_opt ::= LIMIT expr COMMA expr */ +{yygotominor.yy476.pOffset = yymsp[-2].minor.yy346.pExpr; yygotominor.yy476.pLimit = yymsp[0].minor.yy346.pExpr;} break; - case 166: /* cmd ::= DELETE FROM fullname indexed_opt where_opt */ + case 167: /* cmd ::= DELETE FROM fullname indexed_opt where_opt */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy185, &yymsp[-1].minor.yy0); - sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy185,yymsp[0].minor.yy72); + sqlite3SrcListIndexedBy(pParse, yymsp[-2].minor.yy65, &yymsp[-1].minor.yy0); + sqlite3DeleteFrom(pParse,yymsp[-2].minor.yy65,yymsp[0].minor.yy132); } break; - case 169: /* cmd ::= UPDATE orconf fullname indexed_opt SET setlist where_opt */ + case 170: /* cmd ::= UPDATE orconf fullname indexed_opt SET setlist where_opt */ { - sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy185, &yymsp[-3].minor.yy0); - sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy148,"set list"); - sqlite3Update(pParse,yymsp[-4].minor.yy185,yymsp[-1].minor.yy148,yymsp[0].minor.yy72,yymsp[-5].minor.yy194); + sqlite3SrcListIndexedBy(pParse, yymsp[-4].minor.yy65, &yymsp[-3].minor.yy0); + sqlite3ExprListCheckLength(pParse,yymsp[-1].minor.yy14,"set list"); + sqlite3Update(pParse,yymsp[-4].minor.yy65,yymsp[-1].minor.yy14,yymsp[0].minor.yy132,yymsp[-5].minor.yy186); } break; - case 170: /* setlist ::= setlist COMMA nm EQ expr */ + case 171: /* setlist ::= setlist COMMA nm EQ expr */ { - yygotominor.yy148 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy148, yymsp[0].minor.yy190.pExpr); - sqlite3ExprListSetName(pParse, yygotominor.yy148, &yymsp[-2].minor.yy0, 1); + yygotominor.yy14 = sqlite3ExprListAppend(pParse, yymsp[-4].minor.yy14, yymsp[0].minor.yy346.pExpr); + sqlite3ExprListSetName(pParse, yygotominor.yy14, &yymsp[-2].minor.yy0, 1); } break; - case 171: /* setlist ::= nm EQ expr */ + case 172: /* setlist ::= nm EQ expr */ { - yygotominor.yy148 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy190.pExpr); - sqlite3ExprListSetName(pParse, yygotominor.yy148, &yymsp[-2].minor.yy0, 1); + yygotominor.yy14 = sqlite3ExprListAppend(pParse, 0, yymsp[0].minor.yy346.pExpr); + sqlite3ExprListSetName(pParse, yygotominor.yy14, &yymsp[-2].minor.yy0, 1); } break; - case 172: /* cmd ::= insert_cmd INTO fullname inscollist_opt VALUES LP itemlist RP */ -{sqlite3Insert(pParse, yymsp[-5].minor.yy185, yymsp[-1].minor.yy148, 0, yymsp[-4].minor.yy254, yymsp[-7].minor.yy194);} + case 173: /* cmd ::= insert_cmd INTO fullname inscollist_opt VALUES LP itemlist RP */ +{sqlite3Insert(pParse, yymsp[-5].minor.yy65, yymsp[-1].minor.yy14, 0, yymsp[-4].minor.yy408, yymsp[-7].minor.yy186);} + break; + case 174: /* cmd ::= insert_cmd INTO fullname inscollist_opt select */ +{sqlite3Insert(pParse, yymsp[-2].minor.yy65, 0, yymsp[0].minor.yy3, yymsp[-1].minor.yy408, yymsp[-4].minor.yy186);} break; - case 173: /* cmd ::= insert_cmd INTO fullname inscollist_opt select */ -{sqlite3Insert(pParse, yymsp[-2].minor.yy185, 0, yymsp[0].minor.yy243, yymsp[-1].minor.yy254, yymsp[-4].minor.yy194);} + case 175: /* cmd ::= insert_cmd INTO fullname inscollist_opt DEFAULT VALUES */ +{sqlite3Insert(pParse, yymsp[-3].minor.yy65, 0, 0, yymsp[-2].minor.yy408, yymsp[-5].minor.yy186);} break; - case 174: /* cmd ::= insert_cmd INTO fullname inscollist_opt DEFAULT VALUES */ -{sqlite3Insert(pParse, yymsp[-3].minor.yy185, 0, 0, yymsp[-2].minor.yy254, yymsp[-5].minor.yy194);} + case 176: /* insert_cmd ::= INSERT orconf */ +{yygotominor.yy186 = yymsp[0].minor.yy186;} break; - case 177: /* itemlist ::= itemlist COMMA expr */ - case 241: /* nexprlist ::= nexprlist COMMA expr */ yytestcase(yyruleno==241); -{yygotominor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy148,yymsp[0].minor.yy190.pExpr);} + case 177: /* insert_cmd ::= REPLACE */ +{yygotominor.yy186 = OE_Replace;} break; - case 178: /* itemlist ::= expr */ - case 242: /* nexprlist ::= expr */ yytestcase(yyruleno==242); -{yygotominor.yy148 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy190.pExpr);} + case 178: /* itemlist ::= itemlist COMMA expr */ + case 242: /* nexprlist ::= nexprlist COMMA expr */ yytestcase(yyruleno==242); +{yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-2].minor.yy14,yymsp[0].minor.yy346.pExpr);} break; - case 181: /* inscollist ::= inscollist COMMA nm */ -{yygotominor.yy254 = sqlite3IdListAppend(pParse->db,yymsp[-2].minor.yy254,&yymsp[0].minor.yy0);} + case 179: /* itemlist ::= expr */ + case 243: /* nexprlist ::= expr */ yytestcase(yyruleno==243); +{yygotominor.yy14 = sqlite3ExprListAppend(pParse,0,yymsp[0].minor.yy346.pExpr);} break; - case 182: /* inscollist ::= nm */ -{yygotominor.yy254 = sqlite3IdListAppend(pParse->db,0,&yymsp[0].minor.yy0);} + case 182: /* inscollist ::= inscollist COMMA nm */ +{yygotominor.yy408 = sqlite3IdListAppend(pParse->db,yymsp[-2].minor.yy408,&yymsp[0].minor.yy0);} break; - case 183: /* expr ::= term */ - case 211: /* escape ::= ESCAPE expr */ yytestcase(yyruleno==211); -{yygotominor.yy190 = yymsp[0].minor.yy190;} + case 183: /* inscollist ::= nm */ +{yygotominor.yy408 = sqlite3IdListAppend(pParse->db,0,&yymsp[0].minor.yy0);} break; - case 184: /* expr ::= LP expr RP */ -{yygotominor.yy190.pExpr = yymsp[-1].minor.yy190.pExpr; spanSet(&yygotominor.yy190,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);} + case 184: /* expr ::= term */ + case 212: /* escape ::= ESCAPE expr */ yytestcase(yyruleno==212); +{yygotominor.yy346 = yymsp[0].minor.yy346;} break; - case 185: /* term ::= NULL */ - case 190: /* term ::= INTEGER|FLOAT|BLOB */ yytestcase(yyruleno==190); - case 191: /* term ::= STRING */ yytestcase(yyruleno==191); -{spanExpr(&yygotominor.yy190, pParse, yymsp[0].major, &yymsp[0].minor.yy0);} + case 185: /* expr ::= LP expr RP */ +{yygotominor.yy346.pExpr = yymsp[-1].minor.yy346.pExpr; spanSet(&yygotominor.yy346,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0);} break; - case 186: /* expr ::= id */ - case 187: /* expr ::= JOIN_KW */ yytestcase(yyruleno==187); -{spanExpr(&yygotominor.yy190, pParse, TK_ID, &yymsp[0].minor.yy0);} + case 186: /* term ::= NULL */ + case 191: /* term ::= INTEGER|FLOAT|BLOB */ yytestcase(yyruleno==191); + case 192: /* term ::= STRING */ yytestcase(yyruleno==192); +{spanExpr(&yygotominor.yy346, pParse, yymsp[0].major, &yymsp[0].minor.yy0);} break; - case 188: /* expr ::= nm DOT nm */ + case 187: /* expr ::= id */ + case 188: /* expr ::= JOIN_KW */ yytestcase(yyruleno==188); +{spanExpr(&yygotominor.yy346, pParse, TK_ID, &yymsp[0].minor.yy0);} + break; + case 189: /* expr ::= nm DOT nm */ { Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0); Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0); - yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0); - spanSet(&yygotominor.yy190,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp2, 0); + spanSet(&yygotominor.yy346,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0); } break; - case 189: /* expr ::= nm DOT nm DOT nm */ + case 190: /* expr ::= nm DOT nm DOT nm */ { Expr *temp1 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-4].minor.yy0); Expr *temp2 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[-2].minor.yy0); Expr *temp3 = sqlite3PExpr(pParse, TK_ID, 0, 0, &yymsp[0].minor.yy0); Expr *temp4 = sqlite3PExpr(pParse, TK_DOT, temp2, temp3, 0); - yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0); - spanSet(&yygotominor.yy190,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_DOT, temp1, temp4, 0); + spanSet(&yygotominor.yy346,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); } break; - case 192: /* expr ::= REGISTER */ + case 193: /* expr ::= REGISTER */ { /* When doing a nested parse, one can include terms in an expression ** that look like this: #1 #2 ... These terms refer to registers ** in the virtual machine. #N is the N-th register. */ if( pParse->nested==0 ){ sqlite3ErrorMsg(pParse, "near \"%T\": syntax error", &yymsp[0].minor.yy0); - yygotominor.yy190.pExpr = 0; + yygotominor.yy346.pExpr = 0; }else{ - yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &yymsp[0].minor.yy0); - if( yygotominor.yy190.pExpr ) sqlite3GetInt32(&yymsp[0].minor.yy0.z[1], &yygotominor.yy190.pExpr->iTable); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_REGISTER, 0, 0, &yymsp[0].minor.yy0); + if( yygotominor.yy346.pExpr ) sqlite3GetInt32(&yymsp[0].minor.yy0.z[1], &yygotominor.yy346.pExpr->iTable); } - spanSet(&yygotominor.yy190, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0); + spanSet(&yygotominor.yy346, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0); } break; - case 193: /* expr ::= VARIABLE */ + case 194: /* expr ::= VARIABLE */ { - spanExpr(&yygotominor.yy190, pParse, TK_VARIABLE, &yymsp[0].minor.yy0); - sqlite3ExprAssignVarNumber(pParse, yygotominor.yy190.pExpr); - spanSet(&yygotominor.yy190, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0); + spanExpr(&yygotominor.yy346, pParse, TK_VARIABLE, &yymsp[0].minor.yy0); + sqlite3ExprAssignVarNumber(pParse, yygotominor.yy346.pExpr); + spanSet(&yygotominor.yy346, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0); } break; - case 194: /* expr ::= expr COLLATE ids */ + case 195: /* expr ::= expr COLLATE ids */ { - yygotominor.yy190.pExpr = sqlite3ExprSetColl(pParse, yymsp[-2].minor.yy190.pExpr, &yymsp[0].minor.yy0); - yygotominor.yy190.zStart = yymsp[-2].minor.yy190.zStart; - yygotominor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; + yygotominor.yy346.pExpr = sqlite3ExprSetColl(pParse, yymsp[-2].minor.yy346.pExpr, &yymsp[0].minor.yy0); + yygotominor.yy346.zStart = yymsp[-2].minor.yy346.zStart; + yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } break; - case 195: /* expr ::= CAST LP expr AS typetoken RP */ + case 196: /* expr ::= CAST LP expr AS typetoken RP */ { - yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_CAST, yymsp[-3].minor.yy190.pExpr, 0, &yymsp[-1].minor.yy0); - spanSet(&yygotominor.yy190,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_CAST, yymsp[-3].minor.yy346.pExpr, 0, &yymsp[-1].minor.yy0); + spanSet(&yygotominor.yy346,&yymsp[-5].minor.yy0,&yymsp[0].minor.yy0); } break; - case 196: /* expr ::= ID LP distinct exprlist RP */ + case 197: /* expr ::= ID LP distinct exprlist RP */ { - if( yymsp[-1].minor.yy148 && yymsp[-1].minor.yy148->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){ + if( yymsp[-1].minor.yy14 && yymsp[-1].minor.yy14->nExpr>pParse->db->aLimit[SQLITE_LIMIT_FUNCTION_ARG] ){ sqlite3ErrorMsg(pParse, "too many arguments on function %T", &yymsp[-4].minor.yy0); } - yygotominor.yy190.pExpr = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy148, &yymsp[-4].minor.yy0); - spanSet(&yygotominor.yy190,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); - if( yymsp[-2].minor.yy194 && yygotominor.yy190.pExpr ){ - yygotominor.yy190.pExpr->flags |= EP_Distinct; + yygotominor.yy346.pExpr = sqlite3ExprFunction(pParse, yymsp[-1].minor.yy14, &yymsp[-4].minor.yy0); + spanSet(&yygotominor.yy346,&yymsp[-4].minor.yy0,&yymsp[0].minor.yy0); + if( yymsp[-2].minor.yy328 && yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->flags |= EP_Distinct; } } break; - case 197: /* expr ::= ID LP STAR RP */ + case 198: /* expr ::= ID LP STAR RP */ { - yygotominor.yy190.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0); - spanSet(&yygotominor.yy190,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); + yygotominor.yy346.pExpr = sqlite3ExprFunction(pParse, 0, &yymsp[-3].minor.yy0); + spanSet(&yygotominor.yy346,&yymsp[-3].minor.yy0,&yymsp[0].minor.yy0); } break; - case 198: /* term ::= CTIME_KW */ + case 199: /* term ::= CTIME_KW */ { /* The CURRENT_TIME, CURRENT_DATE, and CURRENT_TIMESTAMP values are ** treated as functions that return constants */ - yygotominor.yy190.pExpr = sqlite3ExprFunction(pParse, 0,&yymsp[0].minor.yy0); - if( yygotominor.yy190.pExpr ){ - yygotominor.yy190.pExpr->op = TK_CONST_FUNC; - } - spanSet(&yygotominor.yy190, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0); -} - break; - case 199: /* expr ::= expr AND expr */ - case 200: /* expr ::= expr OR expr */ yytestcase(yyruleno==200); - case 201: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==201); - case 202: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==202); - case 203: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==203); - case 204: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==204); - case 205: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==205); - case 206: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==206); -{spanBinaryExpr(&yygotominor.yy190,pParse,yymsp[-1].major,&yymsp[-2].minor.yy190,&yymsp[0].minor.yy190);} - break; - case 207: /* likeop ::= LIKE_KW */ - case 209: /* likeop ::= MATCH */ yytestcase(yyruleno==209); -{yygotominor.yy392.eOperator = yymsp[0].minor.yy0; yygotominor.yy392.not = 0;} - break; - case 208: /* likeop ::= NOT LIKE_KW */ - case 210: /* likeop ::= NOT MATCH */ yytestcase(yyruleno==210); -{yygotominor.yy392.eOperator = yymsp[0].minor.yy0; yygotominor.yy392.not = 1;} + yygotominor.yy346.pExpr = sqlite3ExprFunction(pParse, 0,&yymsp[0].minor.yy0); + if( yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->op = TK_CONST_FUNC; + } + spanSet(&yygotominor.yy346, &yymsp[0].minor.yy0, &yymsp[0].minor.yy0); +} + break; + case 200: /* expr ::= expr AND expr */ + case 201: /* expr ::= expr OR expr */ yytestcase(yyruleno==201); + case 202: /* expr ::= expr LT|GT|GE|LE expr */ yytestcase(yyruleno==202); + case 203: /* expr ::= expr EQ|NE expr */ yytestcase(yyruleno==203); + case 204: /* expr ::= expr BITAND|BITOR|LSHIFT|RSHIFT expr */ yytestcase(yyruleno==204); + case 205: /* expr ::= expr PLUS|MINUS expr */ yytestcase(yyruleno==205); + case 206: /* expr ::= expr STAR|SLASH|REM expr */ yytestcase(yyruleno==206); + case 207: /* expr ::= expr CONCAT expr */ yytestcase(yyruleno==207); +{spanBinaryExpr(&yygotominor.yy346,pParse,yymsp[-1].major,&yymsp[-2].minor.yy346,&yymsp[0].minor.yy346);} + break; + case 208: /* likeop ::= LIKE_KW */ + case 210: /* likeop ::= MATCH */ yytestcase(yyruleno==210); +{yygotominor.yy96.eOperator = yymsp[0].minor.yy0; yygotominor.yy96.not = 0;} + break; + case 209: /* likeop ::= NOT LIKE_KW */ + case 211: /* likeop ::= NOT MATCH */ yytestcase(yyruleno==211); +{yygotominor.yy96.eOperator = yymsp[0].minor.yy0; yygotominor.yy96.not = 1;} break; - case 212: /* escape ::= */ -{memset(&yygotominor.yy190,0,sizeof(yygotominor.yy190));} + case 213: /* escape ::= */ +{memset(&yygotominor.yy346,0,sizeof(yygotominor.yy346));} break; - case 213: /* expr ::= expr likeop expr escape */ + case 214: /* expr ::= expr likeop expr escape */ { ExprList *pList; - pList = sqlite3ExprListAppend(pParse,0, yymsp[-1].minor.yy190.pExpr); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[-3].minor.yy190.pExpr); - if( yymsp[0].minor.yy190.pExpr ){ - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy190.pExpr); - } - yygotominor.yy190.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-2].minor.yy392.eOperator); - if( yymsp[-2].minor.yy392.not ) yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy190.pExpr, 0, 0); - yygotominor.yy190.zStart = yymsp[-3].minor.yy190.zStart; - yygotominor.yy190.zEnd = yymsp[-1].minor.yy190.zEnd; - if( yygotominor.yy190.pExpr ) yygotominor.yy190.pExpr->flags |= EP_InfixFunc; + pList = sqlite3ExprListAppend(pParse,0, yymsp[-1].minor.yy346.pExpr); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[-3].minor.yy346.pExpr); + if( yymsp[0].minor.yy346.pExpr ){ + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy346.pExpr); + } + yygotominor.yy346.pExpr = sqlite3ExprFunction(pParse, pList, &yymsp[-2].minor.yy96.eOperator); + if( yymsp[-2].minor.yy96.not ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0); + yygotominor.yy346.zStart = yymsp[-3].minor.yy346.zStart; + yygotominor.yy346.zEnd = yymsp[-1].minor.yy346.zEnd; + if( yygotominor.yy346.pExpr ) yygotominor.yy346.pExpr->flags |= EP_InfixFunc; } break; - case 214: /* expr ::= expr ISNULL|NOTNULL */ -{spanUnaryPostfix(&yygotominor.yy190,pParse,yymsp[0].major,&yymsp[-1].minor.yy190,&yymsp[0].minor.yy0);} - break; - case 215: /* expr ::= expr IS NULL */ -{spanUnaryPostfix(&yygotominor.yy190,pParse,TK_ISNULL,&yymsp[-2].minor.yy190,&yymsp[0].minor.yy0);} + case 215: /* expr ::= expr ISNULL|NOTNULL */ +{spanUnaryPostfix(&yygotominor.yy346,pParse,yymsp[0].major,&yymsp[-1].minor.yy346,&yymsp[0].minor.yy0);} break; case 216: /* expr ::= expr NOT NULL */ -{spanUnaryPostfix(&yygotominor.yy190,pParse,TK_NOTNULL,&yymsp[-2].minor.yy190,&yymsp[0].minor.yy0);} +{spanUnaryPostfix(&yygotominor.yy346,pParse,TK_NOTNULL,&yymsp[-2].minor.yy346,&yymsp[0].minor.yy0);} + break; + case 217: /* expr ::= expr IS expr */ +{ + spanBinaryExpr(&yygotominor.yy346,pParse,TK_IS,&yymsp[-2].minor.yy346,&yymsp[0].minor.yy346); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy346.pExpr, yygotominor.yy346.pExpr, TK_ISNULL); +} break; - case 217: /* expr ::= expr IS NOT NULL */ -{spanUnaryPostfix(&yygotominor.yy190,pParse,TK_NOTNULL,&yymsp[-3].minor.yy190,&yymsp[0].minor.yy0);} + case 218: /* expr ::= expr IS NOT expr */ +{ + spanBinaryExpr(&yygotominor.yy346,pParse,TK_ISNOT,&yymsp[-3].minor.yy346,&yymsp[0].minor.yy346); + binaryToUnaryIfNull(pParse, yymsp[0].minor.yy346.pExpr, yygotominor.yy346.pExpr, TK_NOTNULL); +} break; - case 218: /* expr ::= NOT expr */ - case 219: /* expr ::= BITNOT expr */ yytestcase(yyruleno==219); -{spanUnaryPrefix(&yygotominor.yy190,pParse,yymsp[-1].major,&yymsp[0].minor.yy190,&yymsp[-1].minor.yy0);} + case 219: /* expr ::= NOT expr */ + case 220: /* expr ::= BITNOT expr */ yytestcase(yyruleno==220); +{spanUnaryPrefix(&yygotominor.yy346,pParse,yymsp[-1].major,&yymsp[0].minor.yy346,&yymsp[-1].minor.yy0);} break; - case 220: /* expr ::= MINUS expr */ -{spanUnaryPrefix(&yygotominor.yy190,pParse,TK_UMINUS,&yymsp[0].minor.yy190,&yymsp[-1].minor.yy0);} + case 221: /* expr ::= MINUS expr */ +{spanUnaryPrefix(&yygotominor.yy346,pParse,TK_UMINUS,&yymsp[0].minor.yy346,&yymsp[-1].minor.yy0);} break; - case 221: /* expr ::= PLUS expr */ -{spanUnaryPrefix(&yygotominor.yy190,pParse,TK_UPLUS,&yymsp[0].minor.yy190,&yymsp[-1].minor.yy0);} + case 222: /* expr ::= PLUS expr */ +{spanUnaryPrefix(&yygotominor.yy346,pParse,TK_UPLUS,&yymsp[0].minor.yy346,&yymsp[-1].minor.yy0);} break; - case 224: /* expr ::= expr between_op expr AND expr */ + case 225: /* expr ::= expr between_op expr AND expr */ { - ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy190.pExpr); - pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy190.pExpr); - yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy190.pExpr, 0, 0); - if( yygotominor.yy190.pExpr ){ - yygotominor.yy190.pExpr->x.pList = pList; + ExprList *pList = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy346.pExpr); + pList = sqlite3ExprListAppend(pParse,pList, yymsp[0].minor.yy346.pExpr); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_BETWEEN, yymsp[-4].minor.yy346.pExpr, 0, 0); + if( yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->x.pList = pList; }else{ sqlite3ExprListDelete(pParse->db, pList); } - if( yymsp[-3].minor.yy194 ) yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy190.pExpr, 0, 0); - yygotominor.yy190.zStart = yymsp[-4].minor.yy190.zStart; - yygotominor.yy190.zEnd = yymsp[0].minor.yy190.zEnd; + if( yymsp[-3].minor.yy328 ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0); + yygotominor.yy346.zStart = yymsp[-4].minor.yy346.zStart; + yygotominor.yy346.zEnd = yymsp[0].minor.yy346.zEnd; } break; - case 227: /* expr ::= expr in_op LP exprlist RP */ + case 228: /* expr ::= expr in_op LP exprlist RP */ { - yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy190.pExpr, 0, 0); - if( yygotominor.yy190.pExpr ){ - yygotominor.yy190.pExpr->x.pList = yymsp[-1].minor.yy148; - sqlite3ExprSetHeight(pParse, yygotominor.yy190.pExpr); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy346.pExpr, 0, 0); + if( yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->x.pList = yymsp[-1].minor.yy14; + sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy148); + sqlite3ExprListDelete(pParse->db, yymsp[-1].minor.yy14); } - if( yymsp[-3].minor.yy194 ) yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy190.pExpr, 0, 0); - yygotominor.yy190.zStart = yymsp[-4].minor.yy190.zStart; - yygotominor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; + if( yymsp[-3].minor.yy328 ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0); + yygotominor.yy346.zStart = yymsp[-4].minor.yy346.zStart; + yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } break; - case 228: /* expr ::= LP select RP */ + case 229: /* expr ::= LP select RP */ { - yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0); - if( yygotominor.yy190.pExpr ){ - yygotominor.yy190.pExpr->x.pSelect = yymsp[-1].minor.yy243; - ExprSetProperty(yygotominor.yy190.pExpr, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, yygotominor.yy190.pExpr); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_SELECT, 0, 0, 0); + if( yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->x.pSelect = yymsp[-1].minor.yy3; + ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect); + sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); }else{ - sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy243); + sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy3); } - yygotominor.yy190.zStart = yymsp[-2].minor.yy0.z; - yygotominor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; + yygotominor.yy346.zStart = yymsp[-2].minor.yy0.z; + yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } break; - case 229: /* expr ::= expr in_op LP select RP */ + case 230: /* expr ::= expr in_op LP select RP */ { - yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy190.pExpr, 0, 0); - if( yygotominor.yy190.pExpr ){ - yygotominor.yy190.pExpr->x.pSelect = yymsp[-1].minor.yy243; - ExprSetProperty(yygotominor.yy190.pExpr, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, yygotominor.yy190.pExpr); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-4].minor.yy346.pExpr, 0, 0); + if( yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->x.pSelect = yymsp[-1].minor.yy3; + ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect); + sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); }else{ - sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy243); + sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy3); } - if( yymsp[-3].minor.yy194 ) yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy190.pExpr, 0, 0); - yygotominor.yy190.zStart = yymsp[-4].minor.yy190.zStart; - yygotominor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; + if( yymsp[-3].minor.yy328 ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0); + yygotominor.yy346.zStart = yymsp[-4].minor.yy346.zStart; + yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } break; - case 230: /* expr ::= expr in_op nm dbnm */ + case 231: /* expr ::= expr in_op nm dbnm */ { SrcList *pSrc = sqlite3SrcListAppend(pParse->db, 0,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0); - yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-3].minor.yy190.pExpr, 0, 0); - if( yygotominor.yy190.pExpr ){ - yygotominor.yy190.pExpr->x.pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0); - ExprSetProperty(yygotominor.yy190.pExpr, EP_xIsSelect); - sqlite3ExprSetHeight(pParse, yygotominor.yy190.pExpr); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_IN, yymsp[-3].minor.yy346.pExpr, 0, 0); + if( yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->x.pSelect = sqlite3SelectNew(pParse, 0,pSrc,0,0,0,0,0,0,0); + ExprSetProperty(yygotominor.yy346.pExpr, EP_xIsSelect); + sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); }else{ sqlite3SrcListDelete(pParse->db, pSrc); } - if( yymsp[-2].minor.yy194 ) yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy190.pExpr, 0, 0); - yygotominor.yy190.zStart = yymsp[-3].minor.yy190.zStart; - yygotominor.yy190.zEnd = yymsp[0].minor.yy0.z ? &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] : &yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]; + if( yymsp[-2].minor.yy328 ) yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_NOT, yygotominor.yy346.pExpr, 0, 0); + yygotominor.yy346.zStart = yymsp[-3].minor.yy346.zStart; + yygotominor.yy346.zEnd = yymsp[0].minor.yy0.z ? &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n] : &yymsp[-1].minor.yy0.z[yymsp[-1].minor.yy0.n]; } break; - case 231: /* expr ::= EXISTS LP select RP */ + case 232: /* expr ::= EXISTS LP select RP */ { - Expr *p = yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0); + Expr *p = yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_EXISTS, 0, 0, 0); if( p ){ - p->x.pSelect = yymsp[-1].minor.yy243; + p->x.pSelect = yymsp[-1].minor.yy3; ExprSetProperty(p, EP_xIsSelect); sqlite3ExprSetHeight(pParse, p); }else{ - sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy243); + sqlite3SelectDelete(pParse->db, yymsp[-1].minor.yy3); } - yygotominor.yy190.zStart = yymsp[-3].minor.yy0.z; - yygotominor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; + yygotominor.yy346.zStart = yymsp[-3].minor.yy0.z; + yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } break; - case 232: /* expr ::= CASE case_operand case_exprlist case_else END */ + case 233: /* expr ::= CASE case_operand case_exprlist case_else END */ { - yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy72, yymsp[-1].minor.yy72, 0); - if( yygotominor.yy190.pExpr ){ - yygotominor.yy190.pExpr->x.pList = yymsp[-2].minor.yy148; - sqlite3ExprSetHeight(pParse, yygotominor.yy190.pExpr); + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_CASE, yymsp[-3].minor.yy132, yymsp[-1].minor.yy132, 0); + if( yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->x.pList = yymsp[-2].minor.yy14; + sqlite3ExprSetHeight(pParse, yygotominor.yy346.pExpr); }else{ - sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy148); + sqlite3ExprListDelete(pParse->db, yymsp[-2].minor.yy14); } - yygotominor.yy190.zStart = yymsp[-4].minor.yy0.z; - yygotominor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; + yygotominor.yy346.zStart = yymsp[-4].minor.yy0.z; + yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } break; - case 233: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ + case 234: /* case_exprlist ::= case_exprlist WHEN expr THEN expr */ { - yygotominor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy148, yymsp[-2].minor.yy190.pExpr); - yygotominor.yy148 = sqlite3ExprListAppend(pParse,yygotominor.yy148, yymsp[0].minor.yy190.pExpr); + yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, yymsp[-2].minor.yy346.pExpr); + yygotominor.yy14 = sqlite3ExprListAppend(pParse,yygotominor.yy14, yymsp[0].minor.yy346.pExpr); } break; - case 234: /* case_exprlist ::= WHEN expr THEN expr */ + case 235: /* case_exprlist ::= WHEN expr THEN expr */ { - yygotominor.yy148 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy190.pExpr); - yygotominor.yy148 = sqlite3ExprListAppend(pParse,yygotominor.yy148, yymsp[0].minor.yy190.pExpr); + yygotominor.yy14 = sqlite3ExprListAppend(pParse,0, yymsp[-2].minor.yy346.pExpr); + yygotominor.yy14 = sqlite3ExprListAppend(pParse,yygotominor.yy14, yymsp[0].minor.yy346.pExpr); } break; - case 243: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP */ + case 244: /* cmd ::= createkw uniqueflag INDEX ifnotexists nm dbnm ON nm LP idxlist RP */ { sqlite3CreateIndex(pParse, &yymsp[-6].minor.yy0, &yymsp[-5].minor.yy0, - sqlite3SrcListAppend(pParse->db,0,&yymsp[-3].minor.yy0,0), yymsp[-1].minor.yy148, yymsp[-9].minor.yy194, - &yymsp[-10].minor.yy0, &yymsp[0].minor.yy0, SQLITE_SO_ASC, yymsp[-7].minor.yy194); + sqlite3SrcListAppend(pParse->db,0,&yymsp[-3].minor.yy0,0), yymsp[-1].minor.yy14, yymsp[-9].minor.yy328, + &yymsp[-10].minor.yy0, &yymsp[0].minor.yy0, SQLITE_SO_ASC, yymsp[-7].minor.yy328); } break; - case 244: /* uniqueflag ::= UNIQUE */ - case 293: /* raisetype ::= ABORT */ yytestcase(yyruleno==293); -{yygotominor.yy194 = OE_Abort;} + case 245: /* uniqueflag ::= UNIQUE */ + case 299: /* raisetype ::= ABORT */ yytestcase(yyruleno==299); +{yygotominor.yy328 = OE_Abort;} break; - case 245: /* uniqueflag ::= */ -{yygotominor.yy194 = OE_None;} + case 246: /* uniqueflag ::= */ +{yygotominor.yy328 = OE_None;} break; - case 248: /* idxlist ::= idxlist COMMA nm collate sortorder */ + case 249: /* idxlist ::= idxlist COMMA nm collate sortorder */ { Expr *p = 0; if( yymsp[-1].minor.yy0.n>0 ){ p = sqlite3Expr(pParse->db, TK_COLUMN, 0); sqlite3ExprSetColl(pParse, p, &yymsp[-1].minor.yy0); } - yygotominor.yy148 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy148, p); - sqlite3ExprListSetName(pParse,yygotominor.yy148,&yymsp[-2].minor.yy0,1); - sqlite3ExprListCheckLength(pParse, yygotominor.yy148, "index"); - if( yygotominor.yy148 ) yygotominor.yy148->a[yygotominor.yy148->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy194; + yygotominor.yy14 = sqlite3ExprListAppend(pParse,yymsp[-4].minor.yy14, p); + sqlite3ExprListSetName(pParse,yygotominor.yy14,&yymsp[-2].minor.yy0,1); + sqlite3ExprListCheckLength(pParse, yygotominor.yy14, "index"); + if( yygotominor.yy14 ) yygotominor.yy14->a[yygotominor.yy14->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy328; } break; - case 249: /* idxlist ::= nm collate sortorder */ + case 250: /* idxlist ::= nm collate sortorder */ { Expr *p = 0; if( yymsp[-1].minor.yy0.n>0 ){ p = sqlite3PExpr(pParse, TK_COLUMN, 0, 0, 0); sqlite3ExprSetColl(pParse, p, &yymsp[-1].minor.yy0); } - yygotominor.yy148 = sqlite3ExprListAppend(pParse,0, p); - sqlite3ExprListSetName(pParse, yygotominor.yy148, &yymsp[-2].minor.yy0, 1); - sqlite3ExprListCheckLength(pParse, yygotominor.yy148, "index"); - if( yygotominor.yy148 ) yygotominor.yy148->a[yygotominor.yy148->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy194; + yygotominor.yy14 = sqlite3ExprListAppend(pParse,0, p); + sqlite3ExprListSetName(pParse, yygotominor.yy14, &yymsp[-2].minor.yy0, 1); + sqlite3ExprListCheckLength(pParse, yygotominor.yy14, "index"); + if( yygotominor.yy14 ) yygotominor.yy14->a[yygotominor.yy14->nExpr-1].sortOrder = (u8)yymsp[0].minor.yy328; } break; - case 250: /* collate ::= */ + case 251: /* collate ::= */ {yygotominor.yy0.z = 0; yygotominor.yy0.n = 0;} break; - case 252: /* cmd ::= DROP INDEX ifexists fullname */ -{sqlite3DropIndex(pParse, yymsp[0].minor.yy185, yymsp[-1].minor.yy194);} + case 253: /* cmd ::= DROP INDEX ifexists fullname */ +{sqlite3DropIndex(pParse, yymsp[0].minor.yy65, yymsp[-1].minor.yy328);} break; - case 253: /* cmd ::= VACUUM */ - case 254: /* cmd ::= VACUUM nm */ yytestcase(yyruleno==254); + case 254: /* cmd ::= VACUUM */ + case 255: /* cmd ::= VACUUM nm */ yytestcase(yyruleno==255); {sqlite3Vacuum(pParse);} break; - case 255: /* cmd ::= PRAGMA nm dbnm */ + case 256: /* cmd ::= PRAGMA nm dbnm */ {sqlite3Pragma(pParse,&yymsp[-1].minor.yy0,&yymsp[0].minor.yy0,0,0);} break; - case 256: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ + case 257: /* cmd ::= PRAGMA nm dbnm EQ nmnum */ {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,0);} break; - case 257: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ + case 258: /* cmd ::= PRAGMA nm dbnm LP nmnum RP */ {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,0);} break; - case 258: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ + case 259: /* cmd ::= PRAGMA nm dbnm EQ minus_num */ {sqlite3Pragma(pParse,&yymsp[-3].minor.yy0,&yymsp[-2].minor.yy0,&yymsp[0].minor.yy0,1);} break; - case 259: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ + case 260: /* cmd ::= PRAGMA nm dbnm LP minus_num RP */ {sqlite3Pragma(pParse,&yymsp[-4].minor.yy0,&yymsp[-3].minor.yy0,&yymsp[-1].minor.yy0,1);} break; - case 270: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ + case 271: /* cmd ::= createkw trigger_decl BEGIN trigger_cmd_list END */ { Token all; all.z = yymsp[-3].minor.yy0.z; all.n = (int)(yymsp[0].minor.yy0.z - yymsp[-3].minor.yy0.z) + yymsp[0].minor.yy0.n; - sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy145, &all); + sqlite3FinishTrigger(pParse, yymsp[-1].minor.yy473, &all); } break; - case 271: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ + case 272: /* trigger_decl ::= temp TRIGGER ifnotexists nm dbnm trigger_time trigger_event ON fullname foreach_clause when_clause */ { - sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy194, yymsp[-4].minor.yy332.a, yymsp[-4].minor.yy332.b, yymsp[-2].minor.yy185, yymsp[0].minor.yy72, yymsp[-10].minor.yy194, yymsp[-8].minor.yy194); + sqlite3BeginTrigger(pParse, &yymsp[-7].minor.yy0, &yymsp[-6].minor.yy0, yymsp[-5].minor.yy328, yymsp[-4].minor.yy378.a, yymsp[-4].minor.yy378.b, yymsp[-2].minor.yy65, yymsp[0].minor.yy132, yymsp[-10].minor.yy328, yymsp[-8].minor.yy328); yygotominor.yy0 = (yymsp[-6].minor.yy0.n==0?yymsp[-7].minor.yy0:yymsp[-6].minor.yy0); } break; - case 272: /* trigger_time ::= BEFORE */ - case 275: /* trigger_time ::= */ yytestcase(yyruleno==275); -{ yygotominor.yy194 = TK_BEFORE; } + case 273: /* trigger_time ::= BEFORE */ + case 276: /* trigger_time ::= */ yytestcase(yyruleno==276); +{ yygotominor.yy328 = TK_BEFORE; } break; - case 273: /* trigger_time ::= AFTER */ -{ yygotominor.yy194 = TK_AFTER; } + case 274: /* trigger_time ::= AFTER */ +{ yygotominor.yy328 = TK_AFTER; } break; - case 274: /* trigger_time ::= INSTEAD OF */ -{ yygotominor.yy194 = TK_INSTEAD;} + case 275: /* trigger_time ::= INSTEAD OF */ +{ yygotominor.yy328 = TK_INSTEAD;} break; - case 276: /* trigger_event ::= DELETE|INSERT */ - case 277: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==277); -{yygotominor.yy332.a = yymsp[0].major; yygotominor.yy332.b = 0;} + case 277: /* trigger_event ::= DELETE|INSERT */ + case 278: /* trigger_event ::= UPDATE */ yytestcase(yyruleno==278); +{yygotominor.yy378.a = yymsp[0].major; yygotominor.yy378.b = 0;} break; - case 278: /* trigger_event ::= UPDATE OF inscollist */ -{yygotominor.yy332.a = TK_UPDATE; yygotominor.yy332.b = yymsp[0].minor.yy254;} + case 279: /* trigger_event ::= UPDATE OF inscollist */ +{yygotominor.yy378.a = TK_UPDATE; yygotominor.yy378.b = yymsp[0].minor.yy408;} break; - case 281: /* when_clause ::= */ - case 298: /* key_opt ::= */ yytestcase(yyruleno==298); -{ yygotominor.yy72 = 0; } + case 282: /* when_clause ::= */ + case 304: /* key_opt ::= */ yytestcase(yyruleno==304); +{ yygotominor.yy132 = 0; } break; - case 282: /* when_clause ::= WHEN expr */ - case 299: /* key_opt ::= KEY expr */ yytestcase(yyruleno==299); -{ yygotominor.yy72 = yymsp[0].minor.yy190.pExpr; } + case 283: /* when_clause ::= WHEN expr */ + case 305: /* key_opt ::= KEY expr */ yytestcase(yyruleno==305); +{ yygotominor.yy132 = yymsp[0].minor.yy346.pExpr; } break; - case 283: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ + case 284: /* trigger_cmd_list ::= trigger_cmd_list trigger_cmd SEMI */ { - assert( yymsp[-2].minor.yy145!=0 ); - yymsp[-2].minor.yy145->pLast->pNext = yymsp[-1].minor.yy145; - yymsp[-2].minor.yy145->pLast = yymsp[-1].minor.yy145; - yygotominor.yy145 = yymsp[-2].minor.yy145; + assert( yymsp[-2].minor.yy473!=0 ); + yymsp[-2].minor.yy473->pLast->pNext = yymsp[-1].minor.yy473; + yymsp[-2].minor.yy473->pLast = yymsp[-1].minor.yy473; + yygotominor.yy473 = yymsp[-2].minor.yy473; } break; - case 284: /* trigger_cmd_list ::= trigger_cmd SEMI */ + case 285: /* trigger_cmd_list ::= trigger_cmd SEMI */ { - assert( yymsp[-1].minor.yy145!=0 ); - yymsp[-1].minor.yy145->pLast = yymsp[-1].minor.yy145; - yygotominor.yy145 = yymsp[-1].minor.yy145; + assert( yymsp[-1].minor.yy473!=0 ); + yymsp[-1].minor.yy473->pLast = yymsp[-1].minor.yy473; + yygotominor.yy473 = yymsp[-1].minor.yy473; +} + break; + case 287: /* trnm ::= nm DOT nm */ +{ + yygotominor.yy0 = yymsp[0].minor.yy0; + sqlite3ErrorMsg(pParse, + "qualified table names are not allowed on INSERT, UPDATE, and DELETE " + "statements within triggers"); } break; - case 285: /* trigger_cmd ::= UPDATE orconf nm SET setlist where_opt */ -{ yygotominor.yy145 = sqlite3TriggerUpdateStep(pParse->db, &yymsp[-3].minor.yy0, yymsp[-1].minor.yy148, yymsp[0].minor.yy72, yymsp[-4].minor.yy194); } + case 289: /* tridxby ::= INDEXED BY nm */ +{ + sqlite3ErrorMsg(pParse, + "the INDEXED BY clause is not allowed on UPDATE or DELETE statements " + "within triggers"); +} + break; + case 290: /* tridxby ::= NOT INDEXED */ +{ + sqlite3ErrorMsg(pParse, + "the NOT INDEXED clause is not allowed on UPDATE or DELETE statements " + "within triggers"); +} + break; + case 291: /* trigger_cmd ::= UPDATE orconf trnm tridxby SET setlist where_opt */ +{ yygotominor.yy473 = sqlite3TriggerUpdateStep(pParse->db, &yymsp[-4].minor.yy0, yymsp[-1].minor.yy14, yymsp[0].minor.yy132, yymsp[-5].minor.yy186); } break; - case 286: /* trigger_cmd ::= insert_cmd INTO nm inscollist_opt VALUES LP itemlist RP */ -{yygotominor.yy145 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy254, yymsp[-1].minor.yy148, 0, yymsp[-7].minor.yy194);} + case 292: /* trigger_cmd ::= insert_cmd INTO trnm inscollist_opt VALUES LP itemlist RP */ +{yygotominor.yy473 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-5].minor.yy0, yymsp[-4].minor.yy408, yymsp[-1].minor.yy14, 0, yymsp[-7].minor.yy186);} break; - case 287: /* trigger_cmd ::= insert_cmd INTO nm inscollist_opt select */ -{yygotominor.yy145 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy254, 0, yymsp[0].minor.yy243, yymsp[-4].minor.yy194);} + case 293: /* trigger_cmd ::= insert_cmd INTO trnm inscollist_opt select */ +{yygotominor.yy473 = sqlite3TriggerInsertStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[-1].minor.yy408, 0, yymsp[0].minor.yy3, yymsp[-4].minor.yy186);} break; - case 288: /* trigger_cmd ::= DELETE FROM nm where_opt */ -{yygotominor.yy145 = sqlite3TriggerDeleteStep(pParse->db, &yymsp[-1].minor.yy0, yymsp[0].minor.yy72);} + case 294: /* trigger_cmd ::= DELETE FROM trnm tridxby where_opt */ +{yygotominor.yy473 = sqlite3TriggerDeleteStep(pParse->db, &yymsp[-2].minor.yy0, yymsp[0].minor.yy132);} break; - case 289: /* trigger_cmd ::= select */ -{yygotominor.yy145 = sqlite3TriggerSelectStep(pParse->db, yymsp[0].minor.yy243); } + case 295: /* trigger_cmd ::= select */ +{yygotominor.yy473 = sqlite3TriggerSelectStep(pParse->db, yymsp[0].minor.yy3); } break; - case 290: /* expr ::= RAISE LP IGNORE RP */ + case 296: /* expr ::= RAISE LP IGNORE RP */ { - yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0); - if( yygotominor.yy190.pExpr ){ - yygotominor.yy190.pExpr->affinity = OE_Ignore; + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, 0); + if( yygotominor.yy346.pExpr ){ + yygotominor.yy346.pExpr->affinity = OE_Ignore; } - yygotominor.yy190.zStart = yymsp[-3].minor.yy0.z; - yygotominor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; + yygotominor.yy346.zStart = yymsp[-3].minor.yy0.z; + yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } break; - case 291: /* expr ::= RAISE LP raisetype COMMA nm RP */ + case 297: /* expr ::= RAISE LP raisetype COMMA nm RP */ { - yygotominor.yy190.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &yymsp[-1].minor.yy0); - if( yygotominor.yy190.pExpr ) { - yygotominor.yy190.pExpr->affinity = (char)yymsp[-3].minor.yy194; + yygotominor.yy346.pExpr = sqlite3PExpr(pParse, TK_RAISE, 0, 0, &yymsp[-1].minor.yy0); + if( yygotominor.yy346.pExpr ) { + yygotominor.yy346.pExpr->affinity = (char)yymsp[-3].minor.yy328; } - yygotominor.yy190.zStart = yymsp[-5].minor.yy0.z; - yygotominor.yy190.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; + yygotominor.yy346.zStart = yymsp[-5].minor.yy0.z; + yygotominor.yy346.zEnd = &yymsp[0].minor.yy0.z[yymsp[0].minor.yy0.n]; } break; - case 292: /* raisetype ::= ROLLBACK */ -{yygotominor.yy194 = OE_Rollback;} + case 298: /* raisetype ::= ROLLBACK */ +{yygotominor.yy328 = OE_Rollback;} break; - case 294: /* raisetype ::= FAIL */ -{yygotominor.yy194 = OE_Fail;} + case 300: /* raisetype ::= FAIL */ +{yygotominor.yy328 = OE_Fail;} break; - case 295: /* cmd ::= DROP TRIGGER ifexists fullname */ + case 301: /* cmd ::= DROP TRIGGER ifexists fullname */ { - sqlite3DropTrigger(pParse,yymsp[0].minor.yy185,yymsp[-1].minor.yy194); + sqlite3DropTrigger(pParse,yymsp[0].minor.yy65,yymsp[-1].minor.yy328); } break; - case 296: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ + case 302: /* cmd ::= ATTACH database_kw_opt expr AS expr key_opt */ { - sqlite3Attach(pParse, yymsp[-3].minor.yy190.pExpr, yymsp[-1].minor.yy190.pExpr, yymsp[0].minor.yy72); + sqlite3Attach(pParse, yymsp[-3].minor.yy346.pExpr, yymsp[-1].minor.yy346.pExpr, yymsp[0].minor.yy132); } break; - case 297: /* cmd ::= DETACH database_kw_opt expr */ + case 303: /* cmd ::= DETACH database_kw_opt expr */ { - sqlite3Detach(pParse, yymsp[0].minor.yy190.pExpr); + sqlite3Detach(pParse, yymsp[0].minor.yy346.pExpr); } break; - case 302: /* cmd ::= REINDEX */ + case 308: /* cmd ::= REINDEX */ {sqlite3Reindex(pParse, 0, 0);} break; - case 303: /* cmd ::= REINDEX nm dbnm */ + case 309: /* cmd ::= REINDEX nm dbnm */ {sqlite3Reindex(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 304: /* cmd ::= ANALYZE */ + case 310: /* cmd ::= ANALYZE */ {sqlite3Analyze(pParse, 0, 0);} break; - case 305: /* cmd ::= ANALYZE nm dbnm */ + case 311: /* cmd ::= ANALYZE nm dbnm */ {sqlite3Analyze(pParse, &yymsp[-1].minor.yy0, &yymsp[0].minor.yy0);} break; - case 306: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ + case 312: /* cmd ::= ALTER TABLE fullname RENAME TO nm */ { - sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy185,&yymsp[0].minor.yy0); + sqlite3AlterRenameTable(pParse,yymsp[-3].minor.yy65,&yymsp[0].minor.yy0); } break; - case 307: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column */ + case 313: /* cmd ::= ALTER TABLE add_column_fullname ADD kwcolumn_opt column */ { sqlite3AlterFinishAddColumn(pParse, &yymsp[0].minor.yy0); } break; - case 308: /* add_column_fullname ::= fullname */ + case 314: /* add_column_fullname ::= fullname */ { pParse->db->lookaside.bEnabled = 0; - sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy185); + sqlite3AlterBeginAddColumn(pParse, yymsp[0].minor.yy65); } break; - case 311: /* cmd ::= create_vtab */ + case 317: /* cmd ::= create_vtab */ {sqlite3VtabFinishParse(pParse,0);} break; - case 312: /* cmd ::= create_vtab LP vtabarglist RP */ + case 318: /* cmd ::= create_vtab LP vtabarglist RP */ {sqlite3VtabFinishParse(pParse,&yymsp[0].minor.yy0);} break; - case 313: /* create_vtab ::= createkw VIRTUAL TABLE nm dbnm USING nm */ + case 319: /* create_vtab ::= createkw VIRTUAL TABLE nm dbnm USING nm */ { sqlite3VtabBeginParse(pParse, &yymsp[-3].minor.yy0, &yymsp[-2].minor.yy0, &yymsp[0].minor.yy0); } break; - case 316: /* vtabarg ::= */ + case 322: /* vtabarg ::= */ {sqlite3VtabArgInit(pParse);} break; - case 318: /* vtabargtoken ::= ANY */ - case 319: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==319); - case 320: /* lp ::= LP */ yytestcase(yyruleno==320); + case 324: /* vtabargtoken ::= ANY */ + case 325: /* vtabargtoken ::= lp anylist RP */ yytestcase(yyruleno==325); + case 326: /* lp ::= LP */ yytestcase(yyruleno==326); {sqlite3VtabArgExtend(pParse,&yymsp[0].minor.yy0);} break; default: @@ -89198,24 +93756,25 @@ /* (55) carg ::= CONSTRAINT nm ccons */ yytestcase(yyruleno==55); /* (56) carg ::= ccons */ yytestcase(yyruleno==56); /* (62) ccons ::= NULL onconf */ yytestcase(yyruleno==62); - /* (89) conslist ::= conslist COMMA tcons */ yytestcase(yyruleno==89); - /* (90) conslist ::= conslist tcons */ yytestcase(yyruleno==90); - /* (91) conslist ::= tcons */ yytestcase(yyruleno==91); - /* (92) tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==92); - /* (268) plus_opt ::= PLUS */ yytestcase(yyruleno==268); - /* (269) plus_opt ::= */ yytestcase(yyruleno==269); - /* (279) foreach_clause ::= */ yytestcase(yyruleno==279); - /* (280) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==280); - /* (300) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==300); - /* (301) database_kw_opt ::= */ yytestcase(yyruleno==301); - /* (309) kwcolumn_opt ::= */ yytestcase(yyruleno==309); - /* (310) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==310); - /* (314) vtabarglist ::= vtabarg */ yytestcase(yyruleno==314); - /* (315) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==315); - /* (317) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==317); - /* (321) anylist ::= */ yytestcase(yyruleno==321); - /* (322) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==322); - /* (323) anylist ::= anylist ANY */ yytestcase(yyruleno==323); + /* (90) conslist ::= conslist COMMA tcons */ yytestcase(yyruleno==90); + /* (91) conslist ::= conslist tcons */ yytestcase(yyruleno==91); + /* (92) conslist ::= tcons */ yytestcase(yyruleno==92); + /* (93) tcons ::= CONSTRAINT nm */ yytestcase(yyruleno==93); + /* (269) plus_opt ::= PLUS */ yytestcase(yyruleno==269); + /* (270) plus_opt ::= */ yytestcase(yyruleno==270); + /* (280) foreach_clause ::= */ yytestcase(yyruleno==280); + /* (281) foreach_clause ::= FOR EACH ROW */ yytestcase(yyruleno==281); + /* (288) tridxby ::= */ yytestcase(yyruleno==288); + /* (306) database_kw_opt ::= DATABASE */ yytestcase(yyruleno==306); + /* (307) database_kw_opt ::= */ yytestcase(yyruleno==307); + /* (315) kwcolumn_opt ::= */ yytestcase(yyruleno==315); + /* (316) kwcolumn_opt ::= COLUMNKW */ yytestcase(yyruleno==316); + /* (320) vtabarglist ::= vtabarg */ yytestcase(yyruleno==320); + /* (321) vtabarglist ::= vtabarglist COMMA vtabarg */ yytestcase(yyruleno==321); + /* (323) vtabarg ::= vtabarg vtabargtoken */ yytestcase(yyruleno==323); + /* (327) anylist ::= */ yytestcase(yyruleno==327); + /* (328) anylist ::= anylist LP anylist RP */ yytestcase(yyruleno==328); + /* (329) anylist ::= anylist ANY */ yytestcase(yyruleno==329); break; }; yygoto = yyRuleInfo[yyruleno].lhs; @@ -89489,8 +94048,6 @@ ** This file contains C code that splits an SQL input string up into ** individual tokens and sends those tokens one-by-one over to the ** parser for analysis. -** -** $Id: tokenize.c,v 1.162 2009/06/23 20:28:54 drh Exp $ */ /* @@ -89543,7 +94100,7 @@ ** ** The code in this file has been automatically generated by ** -** $Header: /home/drh/sqlite/trans/cvs/sqlite/sqlite/tool/mkkeywordhash.c,v 1.38 2009/06/09 14:27:41 drh Exp $ +** sqlite/tool/mkkeywordhash.c ** ** The code in this file implements a function that determines whether ** or not a given identifier is really an SQL keyword. The same thing @@ -89552,9 +94109,9 @@ ** is substantially reduced. This is important for embedded applications ** on platforms with limited memory. */ -/* Hash score: 171 */ +/* Hash score: 175 */ static int keywordCode(const char *z, int n){ - /* zText[] encodes 801 bytes of keywords in 541 bytes */ + /* zText[] encodes 811 bytes of keywords in 541 bytes */ /* REINDEXEDESCAPEACHECKEYBEFOREIGNOREGEXPLAINSTEADDATABASELECT */ /* ABLEFTHENDEFERRABLELSEXCEPTRANSACTIONATURALTERAISEXCLUSIVE */ /* XISTSAVEPOINTERSECTRIGGEREFERENCESCONSTRAINTOFFSETEMPORARY */ @@ -89598,78 +94155,79 @@ 'A','C','U','U','M','V','I','E','W','I','N','I','T','I','A','L','L','Y', }; static const unsigned char aHash[127] = { - 70, 99, 112, 68, 0, 43, 0, 0, 76, 0, 71, 0, 0, - 41, 12, 72, 15, 0, 111, 79, 49, 106, 0, 19, 0, 0, - 116, 0, 114, 109, 0, 22, 87, 0, 9, 0, 0, 64, 65, - 0, 63, 6, 0, 47, 84, 96, 0, 113, 95, 0, 0, 44, - 0, 97, 24, 0, 17, 0, 117, 48, 23, 0, 5, 104, 25, - 90, 0, 0, 119, 100, 55, 118, 52, 7, 50, 0, 85, 0, - 94, 26, 0, 93, 0, 0, 0, 89, 86, 91, 82, 103, 14, - 38, 102, 0, 75, 0, 18, 83, 105, 31, 0, 115, 74, 107, - 57, 45, 78, 0, 0, 88, 39, 0, 110, 0, 35, 0, 0, - 28, 0, 80, 53, 58, 0, 20, 56, 0, 51, + 72, 101, 114, 70, 0, 45, 0, 0, 78, 0, 73, 0, 0, + 42, 12, 74, 15, 0, 113, 81, 50, 108, 0, 19, 0, 0, + 118, 0, 116, 111, 0, 22, 89, 0, 9, 0, 0, 66, 67, + 0, 65, 6, 0, 48, 86, 98, 0, 115, 97, 0, 0, 44, + 0, 99, 24, 0, 17, 0, 119, 49, 23, 0, 5, 106, 25, + 92, 0, 0, 121, 102, 56, 120, 53, 28, 51, 0, 87, 0, + 96, 26, 0, 95, 0, 0, 0, 91, 88, 93, 84, 105, 14, + 39, 104, 0, 77, 0, 18, 85, 107, 32, 0, 117, 76, 109, + 58, 46, 80, 0, 0, 90, 40, 0, 112, 0, 36, 0, 0, + 29, 0, 82, 59, 60, 0, 20, 57, 0, 52, }; - static const unsigned char aNext[119] = { + static const unsigned char aNext[121] = { 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 13, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 32, 21, 0, 0, 0, 42, 3, 46, 0, - 0, 0, 0, 29, 0, 0, 37, 0, 0, 0, 1, 60, 0, - 0, 61, 0, 40, 0, 0, 0, 0, 0, 0, 0, 59, 0, - 0, 0, 0, 30, 54, 16, 33, 10, 0, 0, 0, 0, 0, - 0, 0, 11, 66, 73, 0, 8, 0, 98, 92, 0, 101, 0, - 81, 0, 69, 0, 0, 108, 27, 36, 67, 77, 0, 34, 62, - 0, 0, + 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 33, 0, 21, 0, 0, 0, 43, 3, 47, + 0, 0, 0, 0, 30, 0, 54, 0, 38, 0, 0, 0, 1, + 62, 0, 0, 63, 0, 41, 0, 0, 0, 0, 0, 0, 0, + 61, 0, 0, 0, 0, 31, 55, 16, 34, 10, 0, 0, 0, + 0, 0, 0, 0, 11, 68, 75, 0, 8, 0, 100, 94, 0, + 103, 0, 83, 0, 71, 0, 0, 110, 27, 37, 69, 79, 0, + 35, 64, 0, 0, }; - static const unsigned char aLen[119] = { + static const unsigned char aLen[121] = { 7, 7, 5, 4, 6, 4, 5, 3, 6, 7, 3, 6, 6, 7, 7, 3, 8, 2, 6, 5, 4, 4, 3, 10, 4, 6, - 11, 2, 7, 5, 5, 9, 6, 9, 9, 7, 10, 10, 4, - 6, 2, 3, 4, 9, 2, 6, 5, 6, 6, 5, 6, 5, - 5, 7, 7, 7, 3, 4, 4, 7, 3, 6, 4, 7, 6, - 12, 6, 9, 4, 6, 5, 4, 7, 6, 5, 6, 7, 5, - 4, 5, 6, 5, 7, 3, 7, 13, 2, 2, 4, 6, 6, - 8, 5, 17, 12, 7, 8, 8, 2, 4, 4, 4, 4, 4, - 2, 2, 6, 5, 8, 5, 5, 8, 3, 5, 5, 6, 4, - 9, 3, + 11, 6, 2, 7, 5, 5, 9, 6, 9, 9, 7, 10, 10, + 4, 6, 2, 3, 9, 4, 2, 6, 5, 6, 6, 5, 6, + 5, 5, 7, 7, 7, 3, 2, 4, 4, 7, 3, 6, 4, + 7, 6, 12, 6, 9, 4, 6, 5, 4, 7, 6, 5, 6, + 7, 5, 4, 5, 6, 5, 7, 3, 7, 13, 2, 2, 4, + 6, 6, 8, 5, 17, 12, 7, 8, 8, 2, 4, 4, 4, + 4, 4, 2, 2, 6, 5, 8, 5, 5, 8, 3, 5, 5, + 6, 4, 9, 3, }; - static const unsigned short int aOffset[119] = { + static const unsigned short int aOffset[121] = { 0, 2, 2, 8, 9, 14, 16, 20, 23, 25, 25, 29, 33, 36, 41, 46, 48, 53, 54, 59, 62, 65, 67, 69, 78, 81, - 86, 95, 96, 101, 105, 109, 117, 122, 128, 136, 142, 152, 159, - 162, 162, 165, 167, 167, 171, 176, 179, 184, 189, 194, 197, 203, - 206, 210, 217, 223, 223, 226, 229, 233, 234, 238, 244, 248, 255, - 261, 273, 279, 288, 290, 296, 301, 303, 310, 315, 320, 326, 332, - 337, 341, 344, 350, 354, 361, 363, 370, 372, 374, 383, 387, 393, - 399, 407, 412, 412, 428, 435, 442, 443, 450, 454, 458, 462, 466, - 469, 471, 473, 479, 483, 491, 495, 500, 508, 511, 516, 521, 527, - 531, 536, + 86, 91, 95, 96, 101, 105, 109, 117, 122, 128, 136, 142, 152, + 159, 162, 162, 165, 167, 167, 171, 176, 179, 184, 189, 194, 197, + 203, 206, 210, 217, 223, 223, 223, 226, 229, 233, 234, 238, 244, + 248, 255, 261, 273, 279, 288, 290, 296, 301, 303, 310, 315, 320, + 326, 332, 337, 341, 344, 350, 354, 361, 363, 370, 372, 374, 383, + 387, 393, 399, 407, 412, 412, 428, 435, 442, 443, 450, 454, 458, + 462, 466, 469, 471, 473, 479, 483, 491, 495, 500, 508, 511, 516, + 521, 527, 531, 536, }; - static const unsigned char aCode[119] = { + static const unsigned char aCode[121] = { TK_REINDEX, TK_INDEXED, TK_INDEX, TK_DESC, TK_ESCAPE, TK_EACH, TK_CHECK, TK_KEY, TK_BEFORE, TK_FOREIGN, TK_FOR, TK_IGNORE, TK_LIKE_KW, TK_EXPLAIN, TK_INSTEAD, TK_ADD, TK_DATABASE, TK_AS, TK_SELECT, TK_TABLE, TK_JOIN_KW, TK_THEN, TK_END, TK_DEFERRABLE, TK_ELSE, - TK_EXCEPT, TK_TRANSACTION,TK_ON, TK_JOIN_KW, TK_ALTER, - TK_RAISE, TK_EXCLUSIVE, TK_EXISTS, TK_SAVEPOINT, TK_INTERSECT, - TK_TRIGGER, TK_REFERENCES, TK_CONSTRAINT, TK_INTO, TK_OFFSET, - TK_OF, TK_SET, TK_TEMP, TK_TEMP, TK_OR, - TK_UNIQUE, TK_QUERY, TK_ATTACH, TK_HAVING, TK_GROUP, - TK_UPDATE, TK_BEGIN, TK_JOIN_KW, TK_RELEASE, TK_BETWEEN, - TK_NOTNULL, TK_NOT, TK_NULL, TK_LIKE_KW, TK_CASCADE, - TK_ASC, TK_DELETE, TK_CASE, TK_COLLATE, TK_CREATE, - TK_CTIME_KW, TK_DETACH, TK_IMMEDIATE, TK_JOIN, TK_INSERT, - TK_MATCH, TK_PLAN, TK_ANALYZE, TK_PRAGMA, TK_ABORT, - TK_VALUES, TK_VIRTUAL, TK_LIMIT, TK_WHEN, TK_WHERE, - TK_RENAME, TK_AFTER, TK_REPLACE, TK_AND, TK_DEFAULT, - TK_AUTOINCR, TK_TO, TK_IN, TK_CAST, TK_COLUMNKW, - TK_COMMIT, TK_CONFLICT, TK_JOIN_KW, TK_CTIME_KW, TK_CTIME_KW, - TK_PRIMARY, TK_DEFERRED, TK_DISTINCT, TK_IS, TK_DROP, - TK_FAIL, TK_FROM, TK_JOIN_KW, TK_LIKE_KW, TK_BY, - TK_IF, TK_ISNULL, TK_ORDER, TK_RESTRICT, TK_JOIN_KW, - TK_JOIN_KW, TK_ROLLBACK, TK_ROW, TK_UNION, TK_USING, - TK_VACUUM, TK_VIEW, TK_INITIALLY, TK_ALL, + TK_EXCEPT, TK_TRANSACTION,TK_ACTION, TK_ON, TK_JOIN_KW, + TK_ALTER, TK_RAISE, TK_EXCLUSIVE, TK_EXISTS, TK_SAVEPOINT, + TK_INTERSECT, TK_TRIGGER, TK_REFERENCES, TK_CONSTRAINT, TK_INTO, + TK_OFFSET, TK_OF, TK_SET, TK_TEMP, TK_TEMP, + TK_OR, TK_UNIQUE, TK_QUERY, TK_ATTACH, TK_HAVING, + TK_GROUP, TK_UPDATE, TK_BEGIN, TK_JOIN_KW, TK_RELEASE, + TK_BETWEEN, TK_NOTNULL, TK_NOT, TK_NO, TK_NULL, + TK_LIKE_KW, TK_CASCADE, TK_ASC, TK_DELETE, TK_CASE, + TK_COLLATE, TK_CREATE, TK_CTIME_KW, TK_DETACH, TK_IMMEDIATE, + TK_JOIN, TK_INSERT, TK_MATCH, TK_PLAN, TK_ANALYZE, + TK_PRAGMA, TK_ABORT, TK_VALUES, TK_VIRTUAL, TK_LIMIT, + TK_WHEN, TK_WHERE, TK_RENAME, TK_AFTER, TK_REPLACE, + TK_AND, TK_DEFAULT, TK_AUTOINCR, TK_TO, TK_IN, + TK_CAST, TK_COLUMNKW, TK_COMMIT, TK_CONFLICT, TK_JOIN_KW, + TK_CTIME_KW, TK_CTIME_KW, TK_PRIMARY, TK_DEFERRED, TK_DISTINCT, + TK_IS, TK_DROP, TK_FAIL, TK_FROM, TK_JOIN_KW, + TK_LIKE_KW, TK_BY, TK_IF, TK_ISNULL, TK_ORDER, + TK_RESTRICT, TK_JOIN_KW, TK_JOIN_KW, TK_ROLLBACK, TK_ROW, + TK_UNION, TK_USING, TK_VACUUM, TK_VIEW, TK_INITIALLY, + TK_ALL, }; int h, i; if( n<2 ) return TK_ID; @@ -89705,98 +94263,100 @@ testcase( i==24 ); /* ELSE */ testcase( i==25 ); /* EXCEPT */ testcase( i==26 ); /* TRANSACTION */ - testcase( i==27 ); /* ON */ - testcase( i==28 ); /* NATURAL */ - testcase( i==29 ); /* ALTER */ - testcase( i==30 ); /* RAISE */ - testcase( i==31 ); /* EXCLUSIVE */ - testcase( i==32 ); /* EXISTS */ - testcase( i==33 ); /* SAVEPOINT */ - testcase( i==34 ); /* INTERSECT */ - testcase( i==35 ); /* TRIGGER */ - testcase( i==36 ); /* REFERENCES */ - testcase( i==37 ); /* CONSTRAINT */ - testcase( i==38 ); /* INTO */ - testcase( i==39 ); /* OFFSET */ - testcase( i==40 ); /* OF */ - testcase( i==41 ); /* SET */ - testcase( i==42 ); /* TEMP */ + testcase( i==27 ); /* ACTION */ + testcase( i==28 ); /* ON */ + testcase( i==29 ); /* NATURAL */ + testcase( i==30 ); /* ALTER */ + testcase( i==31 ); /* RAISE */ + testcase( i==32 ); /* EXCLUSIVE */ + testcase( i==33 ); /* EXISTS */ + testcase( i==34 ); /* SAVEPOINT */ + testcase( i==35 ); /* INTERSECT */ + testcase( i==36 ); /* TRIGGER */ + testcase( i==37 ); /* REFERENCES */ + testcase( i==38 ); /* CONSTRAINT */ + testcase( i==39 ); /* INTO */ + testcase( i==40 ); /* OFFSET */ + testcase( i==41 ); /* OF */ + testcase( i==42 ); /* SET */ testcase( i==43 ); /* TEMPORARY */ - testcase( i==44 ); /* OR */ - testcase( i==45 ); /* UNIQUE */ - testcase( i==46 ); /* QUERY */ - testcase( i==47 ); /* ATTACH */ - testcase( i==48 ); /* HAVING */ - testcase( i==49 ); /* GROUP */ - testcase( i==50 ); /* UPDATE */ - testcase( i==51 ); /* BEGIN */ - testcase( i==52 ); /* INNER */ - testcase( i==53 ); /* RELEASE */ - testcase( i==54 ); /* BETWEEN */ - testcase( i==55 ); /* NOTNULL */ - testcase( i==56 ); /* NOT */ - testcase( i==57 ); /* NULL */ - testcase( i==58 ); /* LIKE */ - testcase( i==59 ); /* CASCADE */ - testcase( i==60 ); /* ASC */ - testcase( i==61 ); /* DELETE */ - testcase( i==62 ); /* CASE */ - testcase( i==63 ); /* COLLATE */ - testcase( i==64 ); /* CREATE */ - testcase( i==65 ); /* CURRENT_DATE */ - testcase( i==66 ); /* DETACH */ - testcase( i==67 ); /* IMMEDIATE */ - testcase( i==68 ); /* JOIN */ - testcase( i==69 ); /* INSERT */ - testcase( i==70 ); /* MATCH */ - testcase( i==71 ); /* PLAN */ - testcase( i==72 ); /* ANALYZE */ - testcase( i==73 ); /* PRAGMA */ - testcase( i==74 ); /* ABORT */ - testcase( i==75 ); /* VALUES */ - testcase( i==76 ); /* VIRTUAL */ - testcase( i==77 ); /* LIMIT */ - testcase( i==78 ); /* WHEN */ - testcase( i==79 ); /* WHERE */ - testcase( i==80 ); /* RENAME */ - testcase( i==81 ); /* AFTER */ - testcase( i==82 ); /* REPLACE */ - testcase( i==83 ); /* AND */ - testcase( i==84 ); /* DEFAULT */ - testcase( i==85 ); /* AUTOINCREMENT */ - testcase( i==86 ); /* TO */ - testcase( i==87 ); /* IN */ - testcase( i==88 ); /* CAST */ - testcase( i==89 ); /* COLUMN */ - testcase( i==90 ); /* COMMIT */ - testcase( i==91 ); /* CONFLICT */ - testcase( i==92 ); /* CROSS */ - testcase( i==93 ); /* CURRENT_TIMESTAMP */ - testcase( i==94 ); /* CURRENT_TIME */ - testcase( i==95 ); /* PRIMARY */ - testcase( i==96 ); /* DEFERRED */ - testcase( i==97 ); /* DISTINCT */ - testcase( i==98 ); /* IS */ - testcase( i==99 ); /* DROP */ - testcase( i==100 ); /* FAIL */ - testcase( i==101 ); /* FROM */ - testcase( i==102 ); /* FULL */ - testcase( i==103 ); /* GLOB */ - testcase( i==104 ); /* BY */ - testcase( i==105 ); /* IF */ - testcase( i==106 ); /* ISNULL */ - testcase( i==107 ); /* ORDER */ - testcase( i==108 ); /* RESTRICT */ - testcase( i==109 ); /* OUTER */ - testcase( i==110 ); /* RIGHT */ - testcase( i==111 ); /* ROLLBACK */ - testcase( i==112 ); /* ROW */ - testcase( i==113 ); /* UNION */ - testcase( i==114 ); /* USING */ - testcase( i==115 ); /* VACUUM */ - testcase( i==116 ); /* VIEW */ - testcase( i==117 ); /* INITIALLY */ - testcase( i==118 ); /* ALL */ + testcase( i==44 ); /* TEMP */ + testcase( i==45 ); /* OR */ + testcase( i==46 ); /* UNIQUE */ + testcase( i==47 ); /* QUERY */ + testcase( i==48 ); /* ATTACH */ + testcase( i==49 ); /* HAVING */ + testcase( i==50 ); /* GROUP */ + testcase( i==51 ); /* UPDATE */ + testcase( i==52 ); /* BEGIN */ + testcase( i==53 ); /* INNER */ + testcase( i==54 ); /* RELEASE */ + testcase( i==55 ); /* BETWEEN */ + testcase( i==56 ); /* NOTNULL */ + testcase( i==57 ); /* NOT */ + testcase( i==58 ); /* NO */ + testcase( i==59 ); /* NULL */ + testcase( i==60 ); /* LIKE */ + testcase( i==61 ); /* CASCADE */ + testcase( i==62 ); /* ASC */ + testcase( i==63 ); /* DELETE */ + testcase( i==64 ); /* CASE */ + testcase( i==65 ); /* COLLATE */ + testcase( i==66 ); /* CREATE */ + testcase( i==67 ); /* CURRENT_DATE */ + testcase( i==68 ); /* DETACH */ + testcase( i==69 ); /* IMMEDIATE */ + testcase( i==70 ); /* JOIN */ + testcase( i==71 ); /* INSERT */ + testcase( i==72 ); /* MATCH */ + testcase( i==73 ); /* PLAN */ + testcase( i==74 ); /* ANALYZE */ + testcase( i==75 ); /* PRAGMA */ + testcase( i==76 ); /* ABORT */ + testcase( i==77 ); /* VALUES */ + testcase( i==78 ); /* VIRTUAL */ + testcase( i==79 ); /* LIMIT */ + testcase( i==80 ); /* WHEN */ + testcase( i==81 ); /* WHERE */ + testcase( i==82 ); /* RENAME */ + testcase( i==83 ); /* AFTER */ + testcase( i==84 ); /* REPLACE */ + testcase( i==85 ); /* AND */ + testcase( i==86 ); /* DEFAULT */ + testcase( i==87 ); /* AUTOINCREMENT */ + testcase( i==88 ); /* TO */ + testcase( i==89 ); /* IN */ + testcase( i==90 ); /* CAST */ + testcase( i==91 ); /* COLUMN */ + testcase( i==92 ); /* COMMIT */ + testcase( i==93 ); /* CONFLICT */ + testcase( i==94 ); /* CROSS */ + testcase( i==95 ); /* CURRENT_TIMESTAMP */ + testcase( i==96 ); /* CURRENT_TIME */ + testcase( i==97 ); /* PRIMARY */ + testcase( i==98 ); /* DEFERRED */ + testcase( i==99 ); /* DISTINCT */ + testcase( i==100 ); /* IS */ + testcase( i==101 ); /* DROP */ + testcase( i==102 ); /* FAIL */ + testcase( i==103 ); /* FROM */ + testcase( i==104 ); /* FULL */ + testcase( i==105 ); /* GLOB */ + testcase( i==106 ); /* BY */ + testcase( i==107 ); /* IF */ + testcase( i==108 ); /* ISNULL */ + testcase( i==109 ); /* ORDER */ + testcase( i==110 ); /* RESTRICT */ + testcase( i==111 ); /* OUTER */ + testcase( i==112 ); /* RIGHT */ + testcase( i==113 ); /* ROLLBACK */ + testcase( i==114 ); /* ROW */ + testcase( i==115 ); /* UNION */ + testcase( i==116 ); /* USING */ + testcase( i==117 ); /* VACUUM */ + testcase( i==118 ); /* VIEW */ + testcase( i==119 ); /* INITIALLY */ + testcase( i==120 ); /* ALL */ return aCode[i]; } } @@ -89805,6 +94365,7 @@ SQLITE_PRIVATE int sqlite3KeywordCode(const unsigned char *z, int n){ return keywordCode((char*)z, n); } +#define SQLITE_N_KEYWORD 121 /************** End of keywordhash.h *****************************************/ /************** Continuing where we left off in tokenize.c *******************/ @@ -89827,16 +94388,7 @@ ** But the feature is undocumented. */ #ifdef SQLITE_ASCII -SQLITE_PRIVATE const char sqlite3IsAsciiIdChar[] = { -/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ - 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */ -}; -#define IdChar(C) (((c=C)&0x80)!=0 || (c>0x1f && sqlite3IsAsciiIdChar[c-0x20])) +#define IdChar(C) ((sqlite3CtypeMap[(unsigned char)C]&0x46)!=0) #endif #ifdef SQLITE_EBCDIC SQLITE_PRIVATE const char sqlite3IsEbcdicIdChar[] = { @@ -89877,8 +94429,9 @@ } case '-': { if( z[1]=='-' ){ + /* IMP: R-15891-05542 -- syntax diagram for comments */ for(i=2; (c=z[i])!=0 && c!='\n'; i++){} - *tokenType = TK_SPACE; + *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ return i; } *tokenType = TK_MINUS; @@ -89909,9 +94462,10 @@ *tokenType = TK_SLASH; return 1; } + /* IMP: R-15891-05542 -- syntax diagram for comments */ for(i=3, c=z[2]; (c!='*' || z[i]!='/') && (c=z[i])!=0; i++){} if( c ) i++; - *tokenType = TK_SPACE; + *tokenType = TK_SPACE; /* IMP: R-22934-25134 */ return i; } case '%': { @@ -90153,7 +94707,7 @@ db->u1.isInterrupted = 0; } pParse->rc = SQLITE_OK; - pParse->zTail = pParse->zSql = zSql; + pParse->zTail = zSql; i = 0; assert( pzErrMsg!=0 ); pEngine = sqlite3ParserAlloc((void*(*)(size_t))sqlite3Malloc); @@ -90296,8 +94850,6 @@ ** This code used to be part of the tokenizer.c source file. But by ** separating it out, the code will be automatically omitted from ** static links that do not use it. -** -** $Id: complete.c,v 1.8 2009/04/28 04:46:42 drh Exp $ */ #ifndef SQLITE_OMIT_COMPLETE @@ -90306,8 +94858,7 @@ */ #ifndef SQLITE_AMALGAMATION #ifdef SQLITE_ASCII -SQLITE_PRIVATE const char sqlite3IsAsciiIdChar[]; -#define IdChar(C) (((c=C)&0x80)!=0 || (c>0x1f && sqlite3IsAsciiIdChar[c-0x20])) +#define IdChar(C) ((sqlite3CtypeMap[(unsigned char)C]&0x46)!=0) #endif #ifdef SQLITE_EBCDIC SQLITE_PRIVATE const char sqlite3IsEbcdicIdChar[]; @@ -90323,11 +94874,13 @@ #define tkSEMI 0 #define tkWS 1 #define tkOTHER 2 +#ifndef SQLITE_OMIT_TRIGGER #define tkEXPLAIN 3 #define tkCREATE 4 #define tkTEMP 5 #define tkTRIGGER 6 #define tkEND 7 +#endif /* ** Return TRUE if the given SQL string ends in a semicolon. @@ -90336,36 +94889,38 @@ ** Whenever the CREATE TRIGGER keywords are seen, the statement ** must end with ";END;". ** -** This implementation uses a state machine with 7 states: +** This implementation uses a state machine with 8 states: ** -** (0) START At the beginning or end of an SQL statement. This routine +** (0) INVALID We have not yet seen a non-whitespace character. +** +** (1) START At the beginning or end of an SQL statement. This routine ** returns 1 if it ends in the START state and 0 if it ends ** in any other state. ** -** (1) NORMAL We are in the middle of statement which ends with a single +** (2) NORMAL We are in the middle of statement which ends with a single ** semicolon. ** -** (2) EXPLAIN The keyword EXPLAIN has been seen at the beginning of +** (3) EXPLAIN The keyword EXPLAIN has been seen at the beginning of ** a statement. ** -** (3) CREATE The keyword CREATE has been seen at the beginning of a +** (4) CREATE The keyword CREATE has been seen at the beginning of a ** statement, possibly preceeded by EXPLAIN and/or followed by ** TEMP or TEMPORARY ** -** (4) TRIGGER We are in the middle of a trigger definition that must be +** (5) TRIGGER We are in the middle of a trigger definition that must be ** ended by a semicolon, the keyword END, and another semicolon. ** -** (5) SEMI We've seen the first semicolon in the ";END;" that occurs at +** (6) SEMI We've seen the first semicolon in the ";END;" that occurs at ** the end of a trigger definition. ** -** (6) END We've seen the ";END" of the ";END;" that occurs at the end +** (7) END We've seen the ";END" of the ";END;" that occurs at the end ** of a trigger difinition. ** ** Transitions between states above are determined by tokens extracted ** from the input. The following tokens are significant: ** ** (0) tkSEMI A semicolon. -** (1) tkWS Whitespace +** (1) tkWS Whitespace. ** (2) tkOTHER Any other SQL token. ** (3) tkEXPLAIN The "explain" keyword. ** (4) tkCREATE The "create" keyword. @@ -90374,6 +94929,7 @@ ** (7) tkEND The "end" keyword. ** ** Whitespace never causes a state transition and is always ignored. +** This means that a SQL string of all whitespace is invalid. ** ** If we compile with SQLITE_OMIT_TRIGGER, all of the computation needed ** to recognize the end of a trigger can be omitted. All we have to do @@ -90387,26 +94943,28 @@ /* A complex statement machine used to detect the end of a CREATE TRIGGER ** statement. This is the normal case. */ - static const u8 trans[7][8] = { + static const u8 trans[8][8] = { /* Token: */ - /* State: ** SEMI WS OTHER EXPLAIN CREATE TEMP TRIGGER END */ - /* 0 START: */ { 0, 0, 1, 2, 3, 1, 1, 1, }, - /* 1 NORMAL: */ { 0, 1, 1, 1, 1, 1, 1, 1, }, - /* 2 EXPLAIN: */ { 0, 2, 2, 1, 3, 1, 1, 1, }, - /* 3 CREATE: */ { 0, 3, 1, 1, 1, 3, 4, 1, }, - /* 4 TRIGGER: */ { 5, 4, 4, 4, 4, 4, 4, 4, }, - /* 5 SEMI: */ { 5, 5, 4, 4, 4, 4, 4, 6, }, - /* 6 END: */ { 0, 6, 4, 4, 4, 4, 4, 4, }, + /* State: ** SEMI WS OTHER EXPLAIN CREATE TEMP TRIGGER END */ + /* 0 INVALID: */ { 1, 0, 2, 3, 4, 2, 2, 2, }, + /* 1 START: */ { 1, 1, 2, 3, 4, 2, 2, 2, }, + /* 2 NORMAL: */ { 1, 2, 2, 2, 2, 2, 2, 2, }, + /* 3 EXPLAIN: */ { 1, 3, 3, 2, 4, 2, 2, 2, }, + /* 4 CREATE: */ { 1, 4, 2, 2, 2, 4, 5, 2, }, + /* 5 TRIGGER: */ { 6, 5, 5, 5, 5, 5, 5, 5, }, + /* 6 SEMI: */ { 6, 6, 5, 5, 5, 5, 5, 7, }, + /* 7 END: */ { 1, 7, 5, 5, 5, 5, 5, 5, }, }; #else - /* If triggers are not suppored by this compile then the statement machine + /* If triggers are not supported by this compile then the statement machine ** used to detect the end of a statement is much simplier */ - static const u8 trans[2][3] = { + static const u8 trans[3][3] = { /* Token: */ /* State: ** SEMI WS OTHER */ - /* 0 START: */ { 0, 0, 1, }, - /* 1 NORMAL: */ { 0, 1, 1, }, + /* 0 INVALID: */ { 1, 0, 2, }, + /* 1 START: */ { 1, 1, 2, }, + /* 2 NORMAL: */ { 1, 2, 2, }, }; #endif /* SQLITE_OMIT_TRIGGER */ @@ -90442,7 +95000,7 @@ break; } while( *zSql && *zSql!='\n' ){ zSql++; } - if( *zSql==0 ) return state==0; + if( *zSql==0 ) return state==1; token = tkWS; break; } @@ -90464,7 +95022,9 @@ break; } default: { - int c; +#ifdef SQLITE_EBCDIC + unsigned char c; +#endif if( IdChar((u8)*zSql) ){ /* Keywords and unquoted identifiers */ int nId; @@ -90524,7 +95084,7 @@ state = trans[state][token]; zSql++; } - return state==0; + return state==1; } #ifndef SQLITE_OMIT_UTF16 @@ -90573,8 +95133,6 @@ ** implement the programmer interface to the library. Routines in ** other files are for internal use by SQLite and should not be ** accessed by users of the library. -** -** $Id: main.c,v 1.560 2009/06/26 15:14:55 drh Exp $ */ #ifdef SQLITE_ENABLE_FTS3 @@ -90682,6 +95240,7 @@ SQLITE_API const char sqlite3_version[] = SQLITE_VERSION; #endif SQLITE_API const char *sqlite3_libversion(void){ return sqlite3_version; } +SQLITE_API const char *sqlite3_sourceid(void){ return SQLITE_SOURCE_ID; } SQLITE_API int sqlite3_libversion_number(void){ return SQLITE_VERSION_NUMBER; } SQLITE_API int sqlite3_threadsafe(void){ return SQLITE_THREADSAFE; } @@ -90772,13 +95331,15 @@ */ pMaster = sqlite3MutexAlloc(SQLITE_MUTEX_STATIC_MASTER); sqlite3_mutex_enter(pMaster); + sqlite3GlobalConfig.isMutexInit = 1; if( !sqlite3GlobalConfig.isMallocInit ){ rc = sqlite3MallocInit(); } if( rc==SQLITE_OK ){ sqlite3GlobalConfig.isMallocInit = 1; if( !sqlite3GlobalConfig.pInitMutex ){ - sqlite3GlobalConfig.pInitMutex = sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); + sqlite3GlobalConfig.pInitMutex = + sqlite3MutexAlloc(SQLITE_MUTEX_RECURSIVE); if( sqlite3GlobalConfig.bCoreMutex && !sqlite3GlobalConfig.pInitMutex ){ rc = SQLITE_NOMEM; } @@ -90789,10 +95350,9 @@ } sqlite3_mutex_leave(pMaster); - /* If unable to initialize the malloc subsystem, then return early. - ** There is little hope of getting SQLite to run if the malloc - ** subsystem cannot be initialized. - */ + /* If rc is not SQLITE_OK at this point, then either the malloc + ** subsystem could not be initialized or the system failed to allocate + ** the pInitMutex mutex. Return an error in either case. */ if( rc!=SQLITE_OK ){ return rc; } @@ -90809,9 +95369,12 @@ sqlite3GlobalConfig.inProgress = 1; memset(pHash, 0, sizeof(sqlite3GlobalFunctions)); sqlite3RegisterGlobalFunctions(); - rc = sqlite3PcacheInitialize(); + if( sqlite3GlobalConfig.isPCacheInit==0 ){ + rc = sqlite3PcacheInitialize(); + } if( rc==SQLITE_OK ){ - rc = sqlite3_os_init(); + sqlite3GlobalConfig.isPCacheInit = 1; + rc = sqlite3OsInit(); } if( rc==SQLITE_OK ){ sqlite3PCacheBufferSetup( sqlite3GlobalConfig.pPage, @@ -90866,14 +95429,23 @@ */ SQLITE_API int sqlite3_shutdown(void){ if( sqlite3GlobalConfig.isInit ){ - sqlite3GlobalConfig.isMallocInit = 0; - sqlite3PcacheShutdown(); sqlite3_os_end(); sqlite3_reset_auto_extension(); + sqlite3GlobalConfig.isInit = 0; + } + if( sqlite3GlobalConfig.isPCacheInit ){ + sqlite3PcacheShutdown(); + sqlite3GlobalConfig.isPCacheInit = 0; + } + if( sqlite3GlobalConfig.isMallocInit ){ sqlite3MallocEnd(); + sqlite3GlobalConfig.isMallocInit = 0; + } + if( sqlite3GlobalConfig.isMutexInit ){ sqlite3MutexEnd(); - sqlite3GlobalConfig.isInit = 0; + sqlite3GlobalConfig.isMutexInit = 0; } + return SQLITE_OK; } @@ -90900,7 +95472,7 @@ /* Mutex configuration options are only available in a threadsafe ** compile. */ -#if SQLITE_THREADSAFE +#if defined(SQLITE_THREADSAFE) && SQLITE_THREADSAFE>0 case SQLITE_CONFIG_SINGLETHREAD: { /* Disable all mutexing */ sqlite3GlobalConfig.bCoreMutex = 0; @@ -91366,6 +95938,9 @@ sqlite3ResetInternalSchema(db, 0); } + /* Any deferred constraint violations have now been resolved. */ + db->nDeferredCons = 0; + /* If one has been configured, invoke the rollback-hook callback */ if( db->xRollbackCallback && (inTrans || !db->autoCommit) ){ db->xRollbackCallback(db->pRollbackArg); @@ -91851,7 +96426,7 @@ ** The sqlite3TempInMemory() function is used to determine which. */ SQLITE_PRIVATE int sqlite3BtreeFactory( - const sqlite3 *db, /* Main database when opening aux otherwise 0 */ + sqlite3 *db, /* Main database when opening aux otherwise 0 */ const char *zFilename, /* Name of the file containing the BTree database */ int omitJournal, /* if TRUE then do not journal this file */ int nCache, /* How many pages in the page cache */ @@ -91992,9 +96567,10 @@ ** and the encoding is enc. */ static int createCollation( - sqlite3* db, + sqlite3* db, const char *zName, - int enc, + u8 enc, + u8 collType, void* pCtx, int(*xCompare)(void*,int,const void*,int,const void*), void(*xDel)(void*) @@ -92059,6 +96635,7 @@ pColl->pUser = pCtx; pColl->xDel = xDel; pColl->enc = (u8)(enc2 | (enc & SQLITE_UTF16_ALIGNED)); + pColl->type = collType; } sqlite3Error(db, SQLITE_OK, 0); return SQLITE_OK; @@ -92081,6 +96658,7 @@ SQLITE_MAX_ATTACHED, SQLITE_MAX_LIKE_PATTERN_LENGTH, SQLITE_MAX_VARIABLE_NUMBER, + SQLITE_MAX_TRIGGER_DEPTH, }; /* @@ -92110,12 +96688,12 @@ #if SQLITE_MAX_LIKE_PATTERN_LENGTH<1 # error SQLITE_MAX_LIKE_PATTERN_LENGTH must be at least 1 #endif -#if SQLITE_MAX_VARIABLE_NUMBER<1 -# error SQLITE_MAX_VARIABLE_NUMBER must be at least 1 -#endif #if SQLITE_MAX_COLUMN>32767 # error SQLITE_MAX_COLUMN must not exceed 32767 #endif +#if SQLITE_MAX_TRIGGER_DEPTH<1 +# error SQLITE_MAX_TRIGGER_DEPTH must be at least 1 +#endif /* @@ -92156,7 +96734,6 @@ ){ sqlite3 *db; int rc; - CollSeq *pColl; int isThreadsafe; *ppDb = 0; @@ -92174,6 +96751,11 @@ }else{ isThreadsafe = sqlite3GlobalConfig.bFullMutex; } + if( flags & SQLITE_OPEN_PRIVATECACHE ){ + flags &= ~SQLITE_OPEN_SHAREDCACHE; + }else if( sqlite3GlobalConfig.sharedCacheEnabled ){ + flags |= SQLITE_OPEN_SHAREDCACHE; + } /* Remove harmful bits from the flags parameter ** @@ -92225,6 +96807,9 @@ #ifdef SQLITE_ENABLE_LOAD_EXTENSION | SQLITE_LoadExtension #endif +#if SQLITE_DEFAULT_RECURSIVE_TRIGGERS + | SQLITE_RecTriggers +#endif ; sqlite3HashInit(&db->aCollSeq); #ifndef SQLITE_OMIT_VIRTUALTABLE @@ -92242,10 +96827,14 @@ ** and UTF-16, so add a version for each to avoid any unnecessary ** conversions. The only error that can occur here is a malloc() failure. */ - createCollation(db, "BINARY", SQLITE_UTF8, 0, binCollFunc, 0); - createCollation(db, "BINARY", SQLITE_UTF16BE, 0, binCollFunc, 0); - createCollation(db, "BINARY", SQLITE_UTF16LE, 0, binCollFunc, 0); - createCollation(db, "RTRIM", SQLITE_UTF8, (void*)1, binCollFunc, 0); + createCollation(db, "BINARY", SQLITE_UTF8, SQLITE_COLL_BINARY, 0, + binCollFunc, 0); + createCollation(db, "BINARY", SQLITE_UTF16BE, SQLITE_COLL_BINARY, 0, + binCollFunc, 0); + createCollation(db, "BINARY", SQLITE_UTF16LE, SQLITE_COLL_BINARY, 0, + binCollFunc, 0); + createCollation(db, "RTRIM", SQLITE_UTF8, SQLITE_COLL_USER, (void*)1, + binCollFunc, 0); if( db->mallocFailed ){ goto opendb_out; } @@ -92253,14 +96842,8 @@ assert( db->pDfltColl!=0 ); /* Also add a UTF-8 case-insensitive collation sequence. */ - createCollation(db, "NOCASE", SQLITE_UTF8, 0, nocaseCollatingFunc, 0); - - /* Set flags on the built-in collating sequences */ - db->pDfltColl->type = SQLITE_COLL_BINARY; - pColl = sqlite3FindCollSeq(db, SQLITE_UTF8, "NOCASE", 0); - if( pColl ){ - pColl->type = SQLITE_COLL_NOCASE; - } + createCollation(db, "NOCASE", SQLITE_UTF8, SQLITE_COLL_NOCASE, 0, + nocaseCollatingFunc, 0); /* Open the backend database driver */ db->openFlags = flags; @@ -92283,10 +96866,8 @@ */ db->aDb[0].zName = "main"; db->aDb[0].safety_level = 3; -#ifndef SQLITE_OMIT_TEMPDB db->aDb[1].zName = "temp"; db->aDb[1].safety_level = 1; -#endif db->magic = SQLITE_MAGIC_OPEN; if( db->mallocFailed ){ @@ -92443,7 +97024,7 @@ int rc; sqlite3_mutex_enter(db->mutex); assert( !db->mallocFailed ); - rc = createCollation(db, zName, enc, pCtx, xCompare, 0); + rc = createCollation(db, zName, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, 0); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; @@ -92463,7 +97044,7 @@ int rc; sqlite3_mutex_enter(db->mutex); assert( !db->mallocFailed ); - rc = createCollation(db, zName, enc, pCtx, xCompare, xDel); + rc = createCollation(db, zName, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, xDel); rc = sqlite3ApiExit(db, rc); sqlite3_mutex_leave(db->mutex); return rc; @@ -92486,7 +97067,7 @@ assert( !db->mallocFailed ); zName8 = sqlite3Utf16to8(db, zName, -1); if( zName8 ){ - rc = createCollation(db, zName8, enc, pCtx, xCompare, 0); + rc = createCollation(db, zName8, (u8)enc, SQLITE_COLL_USER, pCtx, xCompare, 0); sqlite3DbFree(db, zName8); } rc = sqlite3ApiExit(db, rc); @@ -92892,6 +97473,55 @@ rc = ALWAYS(x); break; } + + /* sqlite3_test_control(SQLITE_TESTCTRL_RESERVE, sqlite3 *db, int N) + ** + ** Set the nReserve size to N for the main database on the database + ** connection db. + */ + case SQLITE_TESTCTRL_RESERVE: { + sqlite3 *db = va_arg(ap, sqlite3*); + int x = va_arg(ap,int); + sqlite3_mutex_enter(db->mutex); + sqlite3BtreeSetPageSize(db->aDb[0].pBt, 0, x, 0); + sqlite3_mutex_leave(db->mutex); + break; + } + + /* sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, sqlite3 *db, int N) + ** + ** Enable or disable various optimizations for testing purposes. The + ** argument N is a bitmask of optimizations to be disabled. For normal + ** operation N should be 0. The idea is that a test program (like the + ** SQL Logic Test or SLT test module) can run the same SQL multiple times + ** with various optimizations disabled to verify that the same answer + ** is obtained in every case. + */ + case SQLITE_TESTCTRL_OPTIMIZATIONS: { + sqlite3 *db = va_arg(ap, sqlite3*); + int x = va_arg(ap,int); + db->flags = (x & SQLITE_OptMask) | (db->flags & ~SQLITE_OptMask); + break; + } + +#ifdef SQLITE_N_KEYWORD + /* sqlite3_test_control(SQLITE_TESTCTRL_ISKEYWORD, const char *zWord) + ** + ** If zWord is a keyword recognized by the parser, then return the + ** number of keywords. Or if zWord is not a keyword, return 0. + ** + ** This test feature is only available in the amalgamation since + ** the SQLITE_N_KEYWORD macro is not defined in this file if SQLite + ** is built using separate source files. + */ + case SQLITE_TESTCTRL_ISKEYWORD: { + const char *zWord = va_arg(ap, const char*); + int n = sqlite3Strlen30(zWord); + rc = (sqlite3KeywordCode((u8*)zWord, n)!=TK_ID) ? SQLITE_N_KEYWORD : 0; + break; + } +#endif + } va_end(ap); #endif /* SQLITE_OMIT_BUILTIN_TEST */ @@ -92914,8 +97544,6 @@ ** ** This file contains the implementation of the sqlite3_unlock_notify() ** API method and its associated functionality. -** -** $Id: notify.c,v 1.4 2009/04/07 22:06:57 drh Exp $ */ /* Omit this entire file if SQLITE_ENABLE_UNLOCK_NOTIFY is not defined. */ @@ -93514,11 +98142,10 @@ # define SQLITE_CORE 1 #endif - -/************** Include fts3_expr.h in the middle of fts3.c ******************/ -/************** Begin file fts3_expr.h ***************************************/ +/************** Include fts3Int.h in the middle of fts3.c ********************/ +/************** Begin file fts3Int.h *****************************************/ /* -** 2008 Nov 28 +** 2009 Nov 12 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -93531,7 +98158,14 @@ ** */ -/************** Include fts3_tokenizer.h in the middle of fts3_expr.h ********/ +#ifndef _FTSINT_H +#define _FTSINT_H + +#if !defined(NDEBUG) && !defined(SQLITE_DEBUG) +# define NDEBUG 1 +#endif + +/************** Include fts3_tokenizer.h in the middle of fts3Int.h **********/ /************** Begin file fts3_tokenizer.h **********************************/ /* ** 2006 July 10 @@ -93679,94 +98313,15 @@ /* Tokenizer implementations will typically add additional fields */ }; -#endif /* _FTS3_TOKENIZER_H_ */ - -/************** End of fts3_tokenizer.h **************************************/ -/************** Continuing where we left off in fts3_expr.h ******************/ - -/* -** The following describes the syntax supported by the fts3 MATCH -** operator in a similar format to that used by the lemon parser -** generator. This module does not use actually lemon, it uses a -** custom parser. -** -** query ::= andexpr (OR andexpr)*. -** -** andexpr ::= notexpr (AND? notexpr)*. -** -** notexpr ::= nearexpr (NOT nearexpr|-TOKEN)*. -** notexpr ::= LP query RP. -** -** nearexpr ::= phrase (NEAR distance_opt nearexpr)*. -** -** distance_opt ::= . -** distance_opt ::= / INTEGER. -** -** phrase ::= TOKEN. -** phrase ::= COLUMN:TOKEN. -** phrase ::= "TOKEN TOKEN TOKEN...". -*/ - -typedef struct Fts3Expr Fts3Expr; -typedef struct Fts3Phrase Fts3Phrase; - -/* -** A "phrase" is a sequence of one or more tokens that must match in -** sequence. A single token is the base case and the most common case. -** For a sequence of tokens contained in "...", nToken will be the number -** of tokens in the string. -*/ -struct Fts3Phrase { - int nToken; /* Number of tokens in the phrase */ - int iColumn; /* Index of column this phrase must match */ - int isNot; /* Phrase prefixed by unary not (-) operator */ - struct PhraseToken { - char *z; /* Text of the token */ - int n; /* Number of bytes in buffer pointed to by z */ - int isPrefix; /* True if token ends in with a "*" character */ - } aToken[1]; /* One entry for each token in the phrase */ -}; - -/* -** A tree of these objects forms the RHS of a MATCH operator. -*/ -struct Fts3Expr { - int eType; /* One of the FTSQUERY_XXX values defined below */ - int nNear; /* Valid if eType==FTSQUERY_NEAR */ - Fts3Expr *pParent; /* pParent->pLeft==this or pParent->pRight==this */ - Fts3Expr *pLeft; /* Left operand */ - Fts3Expr *pRight; /* Right operand */ - Fts3Phrase *pPhrase; /* Valid if eType==FTSQUERY_PHRASE */ -}; - -SQLITE_PRIVATE int sqlite3Fts3ExprParse(sqlite3_tokenizer *, char **, int, int, - const char *, int, Fts3Expr **); -SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *); +int fts3_global_term_cnt(int iTerm, int iCol); +int fts3_term_cnt(int iTerm, int iCol); -/* -** Candidate values for Fts3Query.eType. Note that the order of the first -** four values is in order of precedence when parsing expressions. For -** example, the following: -** -** "a OR b AND c NOT d NEAR e" -** -** is equivalent to: -** -** "a OR (b AND (c NOT (d NEAR e)))" -*/ -#define FTSQUERY_NEAR 1 -#define FTSQUERY_NOT 2 -#define FTSQUERY_AND 3 -#define FTSQUERY_OR 4 -#define FTSQUERY_PHRASE 5 -#ifdef SQLITE_TEST -SQLITE_PRIVATE void sqlite3Fts3ExprInitTestInterface(sqlite3 *db); -#endif +#endif /* _FTS3_TOKENIZER_H_ */ -/************** End of fts3_expr.h *******************************************/ -/************** Continuing where we left off in fts3.c ***********************/ -/************** Include fts3_hash.h in the middle of fts3.c ******************/ +/************** End of fts3_tokenizer.h **************************************/ +/************** Continuing where we left off in fts3Int.h ********************/ +/************** Include fts3_hash.h in the middle of fts3Int.h ***************/ /************** Begin file fts3_hash.h ***************************************/ /* ** 2001 September 22 @@ -93788,8 +98343,8 @@ #define _FTS3_HASH_H_ /* Forward declarations of structures. */ -typedef struct fts3Hash fts3Hash; -typedef struct fts3HashElem fts3HashElem; +typedef struct Fts3Hash Fts3Hash; +typedef struct Fts3HashElem Fts3HashElem; /* A complete hash table is an instance of the following structure. ** The internals of this structure are intended to be opaque -- client @@ -93799,15 +98354,15 @@ ** accessing this structure are really macros, so we can't really make ** this structure opaque. */ -struct fts3Hash { +struct Fts3Hash { char keyClass; /* HASH_INT, _POINTER, _STRING, _BINARY */ char copyKey; /* True if copy of key made on insert */ int count; /* Number of entries in this table */ - fts3HashElem *first; /* The first element of the array */ + Fts3HashElem *first; /* The first element of the array */ int htsize; /* Number of buckets in the hash table */ struct _fts3ht { /* the hash table */ int count; /* Number of entries with this hash */ - fts3HashElem *chain; /* Pointer to first entry with this hash */ + Fts3HashElem *chain; /* Pointer to first entry with this hash */ } *ht; }; @@ -93817,8 +98372,8 @@ ** Again, this structure is intended to be opaque, but it can't really ** be opaque because it is used by macros. */ -struct fts3HashElem { - fts3HashElem *next, *prev; /* Next and previous elements in the table */ +struct Fts3HashElem { + Fts3HashElem *next, *prev; /* Next and previous elements in the table */ void *data; /* Data associated with this element */ void *pKey; int nKey; /* Key associated with this element */ }; @@ -93841,25 +98396,27 @@ /* ** Access routines. To delete, insert a NULL pointer. */ -SQLITE_PRIVATE void sqlite3Fts3HashInit(fts3Hash*, int keytype, int copyKey); -SQLITE_PRIVATE void *sqlite3Fts3HashInsert(fts3Hash*, const void *pKey, int nKey, void *pData); -SQLITE_PRIVATE void *sqlite3Fts3HashFind(const fts3Hash*, const void *pKey, int nKey); -SQLITE_PRIVATE void sqlite3Fts3HashClear(fts3Hash*); +SQLITE_PRIVATE void sqlite3Fts3HashInit(Fts3Hash *pNew, char keyClass, char copyKey); +SQLITE_PRIVATE void *sqlite3Fts3HashInsert(Fts3Hash*, const void *pKey, int nKey, void *pData); +SQLITE_PRIVATE void *sqlite3Fts3HashFind(const Fts3Hash*, const void *pKey, int nKey); +SQLITE_PRIVATE void sqlite3Fts3HashClear(Fts3Hash*); +SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem(const Fts3Hash *, const void *, int); /* ** Shorthand for the functions above */ -#define fts3HashInit sqlite3Fts3HashInit -#define fts3HashInsert sqlite3Fts3HashInsert -#define fts3HashFind sqlite3Fts3HashFind -#define fts3HashClear sqlite3Fts3HashClear +#define fts3HashInit sqlite3Fts3HashInit +#define fts3HashInsert sqlite3Fts3HashInsert +#define fts3HashFind sqlite3Fts3HashFind +#define fts3HashClear sqlite3Fts3HashClear +#define fts3HashFindElem sqlite3Fts3HashFindElem /* ** Macros for looping over all elements of a hash table. The idiom is ** like this: ** -** fts3Hash h; -** fts3HashElem *p; +** Fts3Hash h; +** Fts3HashElem *p; ** ... ** for(p=fts3HashFirst(&h); p; p=fts3HashNext(p)){ ** SomeStructure *pData = fts3HashData(p); @@ -93880,105 +98437,307 @@ #endif /* _FTS3_HASH_H_ */ /************** End of fts3_hash.h *******************************************/ -/************** Continuing where we left off in fts3.c ***********************/ -#ifndef SQLITE_CORE - SQLITE_EXTENSION_INIT1 -#endif +/************** Continuing where we left off in fts3Int.h ********************/ +/* +** This constant controls how often segments are merged. Once there are +** FTS3_MERGE_COUNT segments of level N, they are merged into a single +** segment of level N+1. +*/ +#define FTS3_MERGE_COUNT 16 -/* TODO(shess) MAN, this thing needs some refactoring. At minimum, it -** would be nice to order the file better, perhaps something along the -** lines of: -** -** - utility functions -** - table setup functions -** - table update functions -** - table query functions -** -** Put the query functions last because they're likely to reference -** typedefs or functions from the table update section. +/* +** This is the maximum amount of data (in bytes) to store in the +** Fts3Table.pendingTerms hash table. Normally, the hash table is +** populated as documents are inserted/updated/deleted in a transaction +** and used to create a new segment when the transaction is committed. +** However if this limit is reached midway through a transaction, a new +** segment is created and the hash table cleared immediately. */ +#define FTS3_MAX_PENDING_DATA (1*1024*1024) -#if 0 -# define FTSTRACE(A) printf A; fflush(stdout) -#else -# define FTSTRACE(A) -#endif +/* +** Macro to return the number of elements in an array. SQLite has a +** similar macro called ArraySize(). Use a different name to avoid +** a collision when building an amalgamation with built-in FTS3. +*/ +#define SizeofArray(X) ((int)(sizeof(X)/sizeof(X[0]))) -/* It is not safe to call isspace(), tolower(), or isalnum() on -** hi-bit-set characters. This is the same solution used in the -** tokenizer. +/* +** Maximum length of a varint encoded integer. The varint format is different +** from that used by SQLite, so the maximum length is 10, not 9. */ -/* TODO(shess) The snippet-generation code should be using the -** tokenizer-generated tokens rather than doing its own local -** tokenization. +#define FTS3_VARINT_MAX 10 + +/* +** This section provides definitions to allow the +** FTS3 extension to be compiled outside of the +** amalgamation. */ -/* TODO(shess) Is __isascii() a portable version of (c&0x80)==0? */ -static int safe_isspace(char c){ - return (c&0x80)==0 ? isspace(c) : 0; -} -static int safe_tolower(char c){ - return (c&0x80)==0 ? tolower(c) : c; -} -static int safe_isalnum(char c){ - return (c&0x80)==0 ? isalnum(c) : 0; -} +#ifndef SQLITE_AMALGAMATION +/* +** Macros indicating that conditional expressions are always true or +** false. +*/ +# define ALWAYS(x) (x) +# define NEVER(X) (x) +/* +** Internal types used by SQLite. +*/ +typedef unsigned char u8; /* 1-byte (or larger) unsigned integer */ +typedef short int i16; /* 2-byte (or larger) signed integer */ +typedef unsigned int u32; /* 4-byte unsigned integer */ +typedef sqlite3_uint64 u64; /* 8-byte unsigned integer */ +/* +** Macro used to suppress compiler warnings for unused parameters. +*/ +#define UNUSED_PARAMETER(x) (void)(x) +#endif -typedef enum DocListType { - DL_DOCIDS, /* docids only */ - DL_POSITIONS, /* docids + positions */ - DL_POSITIONS_OFFSETS /* docids + positions + offsets */ -} DocListType; +typedef struct Fts3Table Fts3Table; +typedef struct Fts3Cursor Fts3Cursor; +typedef struct Fts3Expr Fts3Expr; +typedef struct Fts3Phrase Fts3Phrase; +typedef struct Fts3SegReader Fts3SegReader; +typedef struct Fts3SegFilter Fts3SegFilter; /* -** By default, only positions and not offsets are stored in the doclists. -** To change this so that offsets are stored too, compile with -** -** -DDL_DEFAULT=DL_POSITIONS_OFFSETS -** -** If DL_DEFAULT is set to DL_DOCIDS, your table can only be inserted -** into (no deletes or updates). +** A connection to a fulltext index is an instance of the following +** structure. The xCreate and xConnect methods create an instance +** of this structure and xDestroy and xDisconnect free that instance. +** All other methods receive a pointer to the structure as one of their +** arguments. */ -#ifndef DL_DEFAULT -# define DL_DEFAULT DL_POSITIONS -#endif +struct Fts3Table { + sqlite3_vtab base; /* Base class used by SQLite core */ + sqlite3 *db; /* The database connection */ + const char *zDb; /* logical database name */ + const char *zName; /* virtual table name */ + int nColumn; /* number of named columns in virtual table */ + char **azColumn; /* column names. malloced */ + sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */ + + /* Precompiled statements used by the implementation. Each of these + ** statements is run and reset within a single virtual table API call. + */ + sqlite3_stmt *aStmt[18]; + + /* Pointer to string containing the SQL: + ** + ** "SELECT block FROM %_segments WHERE blockid BETWEEN ? AND ? + ** ORDER BY blockid" + */ + char *zSelectLeaves; + int nLeavesStmt; /* Valid statements in aLeavesStmt */ + int nLeavesTotal; /* Total number of prepared leaves stmts */ + int nLeavesAlloc; /* Allocated size of aLeavesStmt */ + sqlite3_stmt **aLeavesStmt; /* Array of prepared zSelectLeaves stmts */ + + int nNodeSize; /* Soft limit for node size */ + + /* The following hash table is used to buffer pending index updates during + ** transactions. Variable nPendingData estimates the memory size of the + ** pending data, including hash table overhead, but not malloc overhead. + ** When nPendingData exceeds nMaxPendingData, the buffer is flushed + ** automatically. Variable iPrevDocid is the docid of the most recently + ** inserted record. + */ + int nMaxPendingData; + int nPendingData; + sqlite_int64 iPrevDocid; + Fts3Hash pendingTerms; +}; + +/* +** When the core wants to read from the virtual table, it creates a +** virtual table cursor (an instance of the following structure) using +** the xOpen method. Cursors are destroyed using the xClose method. +*/ +struct Fts3Cursor { + sqlite3_vtab_cursor base; /* Base class used by SQLite core */ + i16 eSearch; /* Search strategy (see below) */ + u8 isEof; /* True if at End Of Results */ + u8 isRequireSeek; /* True if must seek pStmt to %_content row */ + sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */ + Fts3Expr *pExpr; /* Parsed MATCH query string */ + sqlite3_int64 iPrevId; /* Previous id read from aDoclist */ + char *pNextId; /* Pointer into the body of aDoclist */ + char *aDoclist; /* List of docids for full-text queries */ + int nDoclist; /* Size of buffer at aDoclist */ + int isMatchinfoOk; /* True when aMatchinfo[] matches iPrevId */ + u32 *aMatchinfo; +}; + +/* +** The Fts3Cursor.eSearch member is always set to one of the following. +** Actualy, Fts3Cursor.eSearch can be greater than or equal to +** FTS3_FULLTEXT_SEARCH. If so, then Fts3Cursor.eSearch - 2 is the index +** of the column to be searched. For example, in +** +** CREATE VIRTUAL TABLE ex1 USING fts3(a,b,c,d); +** SELECT docid FROM ex1 WHERE b MATCH 'one two three'; +** +** Because the LHS of the MATCH operator is 2nd column "b", +** Fts3Cursor.eSearch will be set to FTS3_FULLTEXT_SEARCH+1. (+0 for a, +** +1 for b, +2 for c, +3 for d.) If the LHS of MATCH were "ex1" +** indicating that all columns should be searched, +** then eSearch would be set to FTS3_FULLTEXT_SEARCH+4. +*/ +#define FTS3_FULLSCAN_SEARCH 0 /* Linear scan of %_content table */ +#define FTS3_DOCID_SEARCH 1 /* Lookup by rowid on %_content table */ +#define FTS3_FULLTEXT_SEARCH 2 /* Full-text index search */ -enum { - POS_END = 0, /* end of this position list */ - POS_COLUMN, /* followed by new column number */ - POS_BASE +/* +** A "phrase" is a sequence of one or more tokens that must match in +** sequence. A single token is the base case and the most common case. +** For a sequence of tokens contained in "...", nToken will be the number +** of tokens in the string. +*/ +struct Fts3Phrase { + int nToken; /* Number of tokens in the phrase */ + int iColumn; /* Index of column this phrase must match */ + int isNot; /* Phrase prefixed by unary not (-) operator */ + struct PhraseToken { + char *z; /* Text of the token */ + int n; /* Number of bytes in buffer pointed to by z */ + int isPrefix; /* True if token ends in with a "*" character */ + } aToken[1]; /* One entry for each token in the phrase */ }; -/* MERGE_COUNT controls how often we merge segments (see comment at -** top of file). +/* +** A tree of these objects forms the RHS of a MATCH operator. +** +** If Fts3Expr.eType is either FTSQUERY_NEAR or FTSQUERY_PHRASE and isLoaded +** is true, then aDoclist points to a malloced buffer, size nDoclist bytes, +** containing the results of the NEAR or phrase query in FTS3 doclist +** format. As usual, the initial "Length" field found in doclists stored +** on disk is omitted from this buffer. +** +** Variable pCurrent always points to the start of a docid field within +** aDoclist. Since the doclist is usually scanned in docid order, this can +** be used to accelerate seeking to the required docid within the doclist. */ -#define MERGE_COUNT 16 +struct Fts3Expr { + int eType; /* One of the FTSQUERY_XXX values defined below */ + int nNear; /* Valid if eType==FTSQUERY_NEAR */ + Fts3Expr *pParent; /* pParent->pLeft==this or pParent->pRight==this */ + Fts3Expr *pLeft; /* Left operand */ + Fts3Expr *pRight; /* Right operand */ + Fts3Phrase *pPhrase; /* Valid if eType==FTSQUERY_PHRASE */ + + int isLoaded; /* True if aDoclist/nDoclist are initialized. */ + char *aDoclist; /* Buffer containing doclist */ + int nDoclist; /* Size of aDoclist in bytes */ -/* utility functions */ + sqlite3_int64 iCurrent; + char *pCurrent; +}; -/* CLEAR() and SCRAMBLE() abstract memset() on a pointer to a single -** record to prevent errors of the form: +/* +** Candidate values for Fts3Query.eType. Note that the order of the first +** four values is in order of precedence when parsing expressions. For +** example, the following: ** -** my_function(SomeType *b){ -** memset(b, '\0', sizeof(b)); // sizeof(b)!=sizeof(*b) -** } +** "a OR b AND c NOT d NEAR e" +** +** is equivalent to: +** +** "a OR (b AND (c NOT (d NEAR e)))" */ -/* TODO(shess) Obvious candidates for a header file. */ -#define CLEAR(b) memset(b, '\0', sizeof(*(b))) +#define FTSQUERY_NEAR 1 +#define FTSQUERY_NOT 2 +#define FTSQUERY_AND 3 +#define FTSQUERY_OR 4 +#define FTSQUERY_PHRASE 5 -#ifndef NDEBUG -# define SCRAMBLE(b) memset(b, 0x55, sizeof(*(b))) -#else -# define SCRAMBLE(b) + +/* fts3_init.c */ +SQLITE_PRIVATE int sqlite3Fts3DeleteVtab(int, sqlite3_vtab *); +SQLITE_PRIVATE int sqlite3Fts3InitVtab(int, sqlite3*, void*, int, const char*const*, + sqlite3_vtab **, char **); + +/* fts3_write.c */ +SQLITE_PRIVATE int sqlite3Fts3UpdateMethod(sqlite3_vtab*,int,sqlite3_value**,sqlite3_int64*); +SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *); +SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *); +SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *); +SQLITE_PRIVATE int sqlite3Fts3SegReaderNew(Fts3Table *,int, sqlite3_int64, + sqlite3_int64, sqlite3_int64, const char *, int, Fts3SegReader**); +SQLITE_PRIVATE int sqlite3Fts3SegReaderPending(Fts3Table*,const char*,int,int,Fts3SegReader**); +SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3Table *, Fts3SegReader *); +SQLITE_PRIVATE int sqlite3Fts3SegReaderIterate( + Fts3Table *, Fts3SegReader **, int, Fts3SegFilter *, + int (*)(Fts3Table *, void *, char *, int, char *, int), void * +); +SQLITE_PRIVATE int sqlite3Fts3ReadBlock(Fts3Table*, sqlite3_int64, char const**, int*); +SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table*, sqlite3_stmt **); + +/* Flags allowed as part of the 4th argument to SegmentReaderIterate() */ +#define FTS3_SEGMENT_REQUIRE_POS 0x00000001 +#define FTS3_SEGMENT_IGNORE_EMPTY 0x00000002 +#define FTS3_SEGMENT_COLUMN_FILTER 0x00000004 +#define FTS3_SEGMENT_PREFIX 0x00000008 + +/* Type passed as 4th argument to SegmentReaderIterate() */ +struct Fts3SegFilter { + const char *zTerm; + int nTerm; + int iCol; + int flags; +}; + +/* fts3.c */ +SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *, sqlite3_int64); +SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *, sqlite_int64 *); +SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *, int *); +SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64); +SQLITE_PRIVATE void sqlite3Fts3Dequote(char *); + +SQLITE_PRIVATE char *sqlite3Fts3FindPositions(Fts3Expr *, sqlite3_int64, int); +SQLITE_PRIVATE int sqlite3Fts3ExprLoadDoclist(Fts3Table *, Fts3Expr *); + +/* fts3_tokenizer.c */ +SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *, int *); +SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, Fts3Hash *, const char *); +SQLITE_PRIVATE int sqlite3Fts3InitTokenizer(Fts3Hash *pHash, + const char *, sqlite3_tokenizer **, const char **, char ** +); + +/* fts3_snippet.c */ +SQLITE_PRIVATE void sqlite3Fts3Offsets(sqlite3_context*, Fts3Cursor*); +SQLITE_PRIVATE void sqlite3Fts3Snippet(sqlite3_context*, Fts3Cursor*, + const char *, const char *, const char * +); +SQLITE_PRIVATE void sqlite3Fts3Snippet2(sqlite3_context *, Fts3Cursor *, const char *, + const char *, const char *, int, int +); +SQLITE_PRIVATE void sqlite3Fts3Matchinfo(sqlite3_context *, Fts3Cursor *); + +/* fts3_expr.c */ +SQLITE_PRIVATE int sqlite3Fts3ExprParse(sqlite3_tokenizer *, + char **, int, int, const char *, int, Fts3Expr ** +); +SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *); +#ifdef SQLITE_TEST +SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3 *db); #endif -/* We may need up to VARINT_MAX bytes to store an encoded 64-bit integer. */ -#define VARINT_MAX 10 +#endif /* _FTSINT_H */ + +/************** End of fts3Int.h *********************************************/ +/************** Continuing where we left off in fts3.c ***********************/ + + +#ifndef SQLITE_CORE + SQLITE_EXTENSION_INIT1 +#endif -/* Write a 64-bit variable-length integer to memory starting at p[0]. - * The length of data written will be between 1 and VARINT_MAX bytes. - * The number of bytes written is returned. */ -static int fts3PutVarint(char *p, sqlite_int64 v){ +/* +** Write a 64-bit variable-length integer to memory starting at p[0]. +** The length of data written will be between 1 and FTS3_VARINT_MAX bytes. +** The number of bytes written is returned. +*/ +SQLITE_PRIVATE int sqlite3Fts3PutVarint(char *p, sqlite_int64 v){ unsigned char *q = (unsigned char *) p; sqlite_uint64 vu = v; do{ @@ -93986,7530 +98745,7161 @@ vu >>= 7; }while( vu!=0 ); q[-1] &= 0x7f; /* turn off high bit in final byte */ - assert( q - (unsigned char *)p <= VARINT_MAX ); + assert( q - (unsigned char *)p <= FTS3_VARINT_MAX ); return (int) (q - (unsigned char *)p); } -/* Read a 64-bit variable-length integer from memory starting at p[0]. - * Return the number of bytes read, or 0 on error. - * The value is stored in *v. */ -static int fts3GetVarint(const char *p, sqlite_int64 *v){ +/* +** Read a 64-bit variable-length integer from memory starting at p[0]. +** Return the number of bytes read, or 0 on error. +** The value is stored in *v. +*/ +SQLITE_PRIVATE int sqlite3Fts3GetVarint(const char *p, sqlite_int64 *v){ const unsigned char *q = (const unsigned char *) p; sqlite_uint64 x = 0, y = 1; - while( (*q & 0x80) == 0x80 ){ + while( (*q&0x80)==0x80 && q-(unsigned char *)p= VARINT_MAX ){ /* bad data */ - assert( 0 ); - return 0; - } } x += y * (*q++); *v = (sqlite_int64) x; return (int) (q - (unsigned char *)p); } -static int fts3GetVarint32(const char *p, int *pi){ +/* +** Similar to sqlite3Fts3GetVarint(), except that the output is truncated to a +** 32-bit integer before it is returned. +*/ +SQLITE_PRIVATE int sqlite3Fts3GetVarint32(const char *p, int *pi){ sqlite_int64 i; - int ret = fts3GetVarint(p, &i); + int ret = sqlite3Fts3GetVarint(p, &i); *pi = (int) i; - assert( *pi==i ); return ret; } -/*******************************************************************/ -/* DataBuffer is used to collect data into a buffer in piecemeal -** fashion. It implements the usual distinction between amount of -** data currently stored (nData) and buffer capacity (nCapacity). -** -** dataBufferInit - create a buffer with given initial capacity. -** dataBufferReset - forget buffer's data, retaining capacity. -** dataBufferDestroy - free buffer's data. -** dataBufferSwap - swap contents of two buffers. -** dataBufferExpand - expand capacity without adding data. -** dataBufferAppend - append data. -** dataBufferAppend2 - append two pieces of data at once. -** dataBufferReplace - replace buffer's data. -*/ -typedef struct DataBuffer { - char *pData; /* Pointer to malloc'ed buffer. */ - int nCapacity; /* Size of pData buffer. */ - int nData; /* End of data loaded into pData. */ -} DataBuffer; - -static void dataBufferInit(DataBuffer *pBuffer, int nCapacity){ - assert( nCapacity>=0 ); - pBuffer->nData = 0; - pBuffer->nCapacity = nCapacity; - pBuffer->pData = nCapacity==0 ? NULL : sqlite3_malloc(nCapacity); -} -static void dataBufferReset(DataBuffer *pBuffer){ - pBuffer->nData = 0; -} -static void dataBufferDestroy(DataBuffer *pBuffer){ - if( pBuffer->pData!=NULL ) sqlite3_free(pBuffer->pData); - SCRAMBLE(pBuffer); -} -static void dataBufferSwap(DataBuffer *pBuffer1, DataBuffer *pBuffer2){ - DataBuffer tmp = *pBuffer1; - *pBuffer1 = *pBuffer2; - *pBuffer2 = tmp; -} -static void dataBufferExpand(DataBuffer *pBuffer, int nAddCapacity){ - assert( nAddCapacity>0 ); - /* TODO(shess) Consider expanding more aggressively. Note that the - ** underlying malloc implementation may take care of such things for - ** us already. - */ - if( pBuffer->nData+nAddCapacity>pBuffer->nCapacity ){ - pBuffer->nCapacity = pBuffer->nData+nAddCapacity; - pBuffer->pData = sqlite3_realloc(pBuffer->pData, pBuffer->nCapacity); - } -} -static void dataBufferAppend(DataBuffer *pBuffer, - const char *pSource, int nSource){ - assert( nSource>0 && pSource!=NULL ); - dataBufferExpand(pBuffer, nSource); - memcpy(pBuffer->pData+pBuffer->nData, pSource, nSource); - pBuffer->nData += nSource; -} -static void dataBufferAppend2(DataBuffer *pBuffer, - const char *pSource1, int nSource1, - const char *pSource2, int nSource2){ - assert( nSource1>0 && pSource1!=NULL ); - assert( nSource2>0 && pSource2!=NULL ); - dataBufferExpand(pBuffer, nSource1+nSource2); - memcpy(pBuffer->pData+pBuffer->nData, pSource1, nSource1); - memcpy(pBuffer->pData+pBuffer->nData+nSource1, pSource2, nSource2); - pBuffer->nData += nSource1+nSource2; -} -static void dataBufferReplace(DataBuffer *pBuffer, - const char *pSource, int nSource){ - dataBufferReset(pBuffer); - dataBufferAppend(pBuffer, pSource, nSource); +/* +** Return the number of bytes required to store the value passed as the +** first argument in varint form. +*/ +SQLITE_PRIVATE int sqlite3Fts3VarintLen(sqlite3_uint64 v){ + int i = 0; + do{ + i++; + v >>= 7; + }while( v!=0 ); + return i; } -/* StringBuffer is a null-terminated version of DataBuffer. */ -typedef struct StringBuffer { - DataBuffer b; /* Includes null terminator. */ -} StringBuffer; +/* +** Convert an SQL-style quoted string into a normal string by removing +** the quote characters. The conversion is done in-place. If the +** input does not begin with a quote character, then this routine +** is a no-op. +** +** Examples: +** +** "abc" becomes abc +** 'xyz' becomes xyz +** [pqr] becomes pqr +** `mno` becomes mno +** +*/ +SQLITE_PRIVATE void sqlite3Fts3Dequote(char *z){ + char quote; /* Quote character (if any ) */ -static void initStringBuffer(StringBuffer *sb){ - dataBufferInit(&sb->b, 100); - dataBufferReplace(&sb->b, "", 1); -} -static int stringBufferLength(StringBuffer *sb){ - return sb->b.nData-1; -} -static char *stringBufferData(StringBuffer *sb){ - return sb->b.pData; + quote = z[0]; + if( quote=='[' || quote=='\'' || quote=='"' || quote=='`' ){ + int iIn = 1; /* Index of next byte to read from input */ + int iOut = 0; /* Index of next byte to write to output */ + + /* If the first byte was a '[', then the close-quote character is a ']' */ + if( quote=='[' ) quote = ']'; + + while( ALWAYS(z[iIn]) ){ + if( z[iIn]==quote ){ + if( z[iIn+1]!=quote ) break; + z[iOut++] = quote; + iIn += 2; + }else{ + z[iOut++] = z[iIn++]; + } + } + z[iOut] = '\0'; + } } -static void stringBufferDestroy(StringBuffer *sb){ - dataBufferDestroy(&sb->b); + +static void fts3GetDeltaVarint(char **pp, sqlite3_int64 *pVal){ + sqlite3_int64 iVal; + *pp += sqlite3Fts3GetVarint(*pp, &iVal); + *pVal += iVal; } -static void nappend(StringBuffer *sb, const char *zFrom, int nFrom){ - assert( sb->b.nData>0 ); - if( nFrom>0 ){ - sb->b.nData--; - dataBufferAppend2(&sb->b, zFrom, nFrom, "", 1); +static void fts3GetDeltaVarint2(char **pp, char *pEnd, sqlite3_int64 *pVal){ + if( *pp>=pEnd ){ + *pp = 0; + }else{ + fts3GetDeltaVarint(pp, pVal); } } -static void append(StringBuffer *sb, const char *zFrom){ - nappend(sb, zFrom, strlen(zFrom)); -} -/* Append a list of strings separated by commas. */ -static void appendList(StringBuffer *sb, int nString, char **azString){ +/* +** The xDisconnect() virtual table method. +*/ +static int fts3DisconnectMethod(sqlite3_vtab *pVtab){ + Fts3Table *p = (Fts3Table *)pVtab; int i; - for(i=0; i0 ) append(sb, ", "); - append(sb, azString[i]); + + assert( p->nPendingData==0 ); + + /* Free any prepared statements held */ + for(i=0; iaStmt); i++){ + sqlite3_finalize(p->aStmt[i]); } -} + for(i=0; inLeavesStmt; i++){ + sqlite3_finalize(p->aLeavesStmt[i]); + } + sqlite3_free(p->zSelectLeaves); + sqlite3_free(p->aLeavesStmt); -static int endsInWhiteSpace(StringBuffer *p){ - return stringBufferLength(p)>0 && - safe_isspace(stringBufferData(p)[stringBufferLength(p)-1]); + /* Invoke the tokenizer destructor to free the tokenizer. */ + p->pTokenizer->pModule->xDestroy(p->pTokenizer); + + sqlite3_free(p); + return SQLITE_OK; } -/* If the StringBuffer ends in something other than white space, add a -** single space character to the end. +/* +** The xDestroy() virtual table method. */ -static void appendWhiteSpace(StringBuffer *p){ - if( stringBufferLength(p)==0 ) return; - if( !endsInWhiteSpace(p) ) append(p, " "); -} +static int fts3DestroyMethod(sqlite3_vtab *pVtab){ + int rc; /* Return code */ + Fts3Table *p = (Fts3Table *)pVtab; -/* Remove white space from the end of the StringBuffer */ -static void trimWhiteSpace(StringBuffer *p){ - while( endsInWhiteSpace(p) ){ - p->b.pData[--p->b.nData-1] = '\0'; + /* Create a script to drop the underlying three storage tables. */ + char *zSql = sqlite3_mprintf( + "DROP TABLE IF EXISTS %Q.'%q_content';" + "DROP TABLE IF EXISTS %Q.'%q_segments';" + "DROP TABLE IF EXISTS %Q.'%q_segdir';", + p->zDb, p->zName, p->zDb, p->zName, p->zDb, p->zName + ); + + /* If malloc has failed, set rc to SQLITE_NOMEM. Otherwise, try to + ** execute the SQL script created above. + */ + if( zSql ){ + rc = sqlite3_exec(p->db, zSql, 0, 0, 0); + sqlite3_free(zSql); + }else{ + rc = SQLITE_NOMEM; } + + /* If everything has worked, invoke fts3DisconnectMethod() to free the + ** memory associated with the Fts3Table structure and return SQLITE_OK. + ** Otherwise, return an SQLite error code. + */ + return (rc==SQLITE_OK ? fts3DisconnectMethod(pVtab) : rc); } -/*******************************************************************/ -/* DLReader is used to read document elements from a doclist. The -** current docid is cached, so dlrDocid() is fast. DLReader does not -** own the doclist buffer. -** -** dlrAtEnd - true if there's no more data to read. -** dlrDocid - docid of current document. -** dlrDocData - doclist data for current document (including docid). -** dlrDocDataBytes - length of same. -** dlrAllDataBytes - length of all remaining data. -** dlrPosData - position data for current document. -** dlrPosDataLen - length of pos data for current document (incl POS_END). -** dlrStep - step to current document. -** dlrInit - initial for doclist of given type against given data. -** dlrDestroy - clean up. -** -** Expected usage is something like: -** -** DLReader reader; -** dlrInit(&reader, pData, nData); -** while( !dlrAtEnd(&reader) ){ -** // calls to dlrDocid() and kin. -** dlrStep(&reader); -** } -** dlrDestroy(&reader); + +/* +** Invoke sqlite3_declare_vtab() to declare the schema for the FTS3 table +** passed as the first argument. This is done as part of the xConnect() +** and xCreate() methods. */ -typedef struct DLReader { - DocListType iType; - const char *pData; - int nData; +static int fts3DeclareVtab(Fts3Table *p){ + int i; /* Iterator variable */ + int rc; /* Return code */ + char *zSql; /* SQL statement passed to declare_vtab() */ + char *zCols; /* List of user defined columns */ - sqlite_int64 iDocid; - int nElement; -} DLReader; - -static int dlrAtEnd(DLReader *pReader){ - assert( pReader->nData>=0 ); - return pReader->nData==0; -} -static sqlite_int64 dlrDocid(DLReader *pReader){ - assert( !dlrAtEnd(pReader) ); - return pReader->iDocid; -} -static const char *dlrDocData(DLReader *pReader){ - assert( !dlrAtEnd(pReader) ); - return pReader->pData; -} -static int dlrDocDataBytes(DLReader *pReader){ - assert( !dlrAtEnd(pReader) ); - return pReader->nElement; -} -static int dlrAllDataBytes(DLReader *pReader){ - assert( !dlrAtEnd(pReader) ); - return pReader->nData; -} -/* TODO(shess) Consider adding a field to track iDocid varint length -** to make these two functions faster. This might matter (a tiny bit) -** for queries. -*/ -static const char *dlrPosData(DLReader *pReader){ - sqlite_int64 iDummy; - int n = fts3GetVarint(pReader->pData, &iDummy); - assert( !dlrAtEnd(pReader) ); - return pReader->pData+n; -} -static int dlrPosDataLen(DLReader *pReader){ - sqlite_int64 iDummy; - int n = fts3GetVarint(pReader->pData, &iDummy); - assert( !dlrAtEnd(pReader) ); - return pReader->nElement-n; -} -static void dlrStep(DLReader *pReader){ - assert( !dlrAtEnd(pReader) ); - - /* Skip past current doclist element. */ - assert( pReader->nElement<=pReader->nData ); - pReader->pData += pReader->nElement; - pReader->nData -= pReader->nElement; - - /* If there is more data, read the next doclist element. */ - if( pReader->nData!=0 ){ - sqlite_int64 iDocidDelta; - int iDummy, n = fts3GetVarint(pReader->pData, &iDocidDelta); - pReader->iDocid += iDocidDelta; - if( pReader->iType>=DL_POSITIONS ){ - assert( nnData ); - while( 1 ){ - n += fts3GetVarint32(pReader->pData+n, &iDummy); - assert( n<=pReader->nData ); - if( iDummy==POS_END ) break; - if( iDummy==POS_COLUMN ){ - n += fts3GetVarint32(pReader->pData+n, &iDummy); - assert( nnData ); - }else if( pReader->iType==DL_POSITIONS_OFFSETS ){ - n += fts3GetVarint32(pReader->pData+n, &iDummy); - n += fts3GetVarint32(pReader->pData+n, &iDummy); - assert( nnData ); - } - } - } - pReader->nElement = n; - assert( pReader->nElement<=pReader->nData ); - } -} -static void dlrInit(DLReader *pReader, DocListType iType, - const char *pData, int nData){ - assert( pData!=NULL && nData!=0 ); - pReader->iType = iType; - pReader->pData = pData; - pReader->nData = nData; - pReader->nElement = 0; - pReader->iDocid = 0; + /* Create a list of user columns for the virtual table */ + zCols = sqlite3_mprintf("%Q, ", p->azColumn[0]); + for(i=1; zCols && inColumn; i++){ + zCols = sqlite3_mprintf("%z%Q, ", zCols, p->azColumn[i]); + } - /* Load the first element's data. There must be a first element. */ - dlrStep(pReader); -} -static void dlrDestroy(DLReader *pReader){ - SCRAMBLE(pReader); -} + /* Create the whole "CREATE TABLE" statement to pass to SQLite */ + zSql = sqlite3_mprintf( + "CREATE TABLE x(%s %Q HIDDEN, docid HIDDEN)", zCols, p->zName + ); -#ifndef NDEBUG -/* Verify that the doclist can be validly decoded. Also returns the -** last docid found because it is convenient in other assertions for -** DLWriter. -*/ -static void docListValidate(DocListType iType, const char *pData, int nData, - sqlite_int64 *pLastDocid){ - sqlite_int64 iPrevDocid = 0; - assert( nData>0 ); - assert( pData!=0 ); - assert( pData+nData>pData ); - while( nData!=0 ){ - sqlite_int64 iDocidDelta; - int n = fts3GetVarint(pData, &iDocidDelta); - iPrevDocid += iDocidDelta; - if( iType>DL_DOCIDS ){ - int iDummy; - while( 1 ){ - n += fts3GetVarint32(pData+n, &iDummy); - if( iDummy==POS_END ) break; - if( iDummy==POS_COLUMN ){ - n += fts3GetVarint32(pData+n, &iDummy); - }else if( iType>DL_POSITIONS ){ - n += fts3GetVarint32(pData+n, &iDummy); - n += fts3GetVarint32(pData+n, &iDummy); - } - assert( n<=nData ); - } - } - assert( n<=nData ); - pData += n; - nData -= n; + if( !zCols || !zSql ){ + rc = SQLITE_NOMEM; + }else{ + rc = sqlite3_declare_vtab(p->db, zSql); } - if( pLastDocid ) *pLastDocid = iPrevDocid; -} -#define ASSERT_VALID_DOCLIST(i, p, n, o) docListValidate(i, p, n, o) -#else -#define ASSERT_VALID_DOCLIST(i, p, n, o) assert( 1 ) -#endif - -/*******************************************************************/ -/* DLWriter is used to write doclist data to a DataBuffer. DLWriter -** always appends to the buffer and does not own it. -** -** dlwInit - initialize to write a given type doclistto a buffer. -** dlwDestroy - clear the writer's memory. Does not free buffer. -** dlwAppend - append raw doclist data to buffer. -** dlwCopy - copy next doclist from reader to writer. -** dlwAdd - construct doclist element and append to buffer. -** Only apply dlwAdd() to DL_DOCIDS doclists (else use PLWriter). -*/ -typedef struct DLWriter { - DocListType iType; - DataBuffer *b; - sqlite_int64 iPrevDocid; -#ifndef NDEBUG - int has_iPrevDocid; -#endif -} DLWriter; -static void dlwInit(DLWriter *pWriter, DocListType iType, DataBuffer *b){ - pWriter->b = b; - pWriter->iType = iType; - pWriter->iPrevDocid = 0; -#ifndef NDEBUG - pWriter->has_iPrevDocid = 0; -#endif -} -static void dlwDestroy(DLWriter *pWriter){ - SCRAMBLE(pWriter); + sqlite3_free(zSql); + sqlite3_free(zCols); + return rc; } -/* iFirstDocid is the first docid in the doclist in pData. It is -** needed because pData may point within a larger doclist, in which -** case the first item would be delta-encoded. -** -** iLastDocid is the final docid in the doclist in pData. It is -** needed to create the new iPrevDocid for future delta-encoding. The -** code could decode the passed doclist to recreate iLastDocid, but -** the only current user (docListMerge) already has decoded this -** information. -*/ -/* TODO(shess) This has become just a helper for docListMerge. -** Consider a refactor to make this cleaner. -*/ -static void dlwAppend(DLWriter *pWriter, - const char *pData, int nData, - sqlite_int64 iFirstDocid, sqlite_int64 iLastDocid){ - sqlite_int64 iDocid = 0; - char c[VARINT_MAX]; - int nFirstOld, nFirstNew; /* Old and new varint len of first docid. */ -#ifndef NDEBUG - sqlite_int64 iLastDocidDelta; -#endif - /* Recode the initial docid as delta from iPrevDocid. */ - nFirstOld = fts3GetVarint(pData, &iDocid); - assert( nFirstOldiType==DL_DOCIDS) ); - nFirstNew = fts3PutVarint(c, iFirstDocid-pWriter->iPrevDocid); - - /* Verify that the incoming doclist is valid AND that it ends with - ** the expected docid. This is essential because we'll trust this - ** docid in future delta-encoding. - */ - ASSERT_VALID_DOCLIST(pWriter->iType, pData, nData, &iLastDocidDelta); - assert( iLastDocid==iFirstDocid-iDocid+iLastDocidDelta ); +/* +** Create the backing store tables (%_content, %_segments and %_segdir) +** required by the FTS3 table passed as the only argument. This is done +** as part of the vtab xCreate() method. +*/ +static int fts3CreateTables(Fts3Table *p){ + int rc; /* Return code */ + int i; /* Iterator variable */ + char *zContentCols; /* Columns of %_content table */ + char *zSql; /* SQL script to create required tables */ + + /* Create a list of user columns for the content table */ + zContentCols = sqlite3_mprintf("docid INTEGER PRIMARY KEY"); + for(i=0; zContentCols && inColumn; i++){ + char *z = p->azColumn[i]; + zContentCols = sqlite3_mprintf("%z, 'c%d%q'", zContentCols, i, z); + } + + /* Create the whole SQL script */ + zSql = sqlite3_mprintf( + "CREATE TABLE %Q.'%q_content'(%s);" + "CREATE TABLE %Q.'%q_segments'(blockid INTEGER PRIMARY KEY, block BLOB);" + "CREATE TABLE %Q.'%q_segdir'(" + "level INTEGER," + "idx INTEGER," + "start_block INTEGER," + "leaves_end_block INTEGER," + "end_block INTEGER," + "root BLOB," + "PRIMARY KEY(level, idx)" + ");", + p->zDb, p->zName, zContentCols, p->zDb, p->zName, p->zDb, p->zName + ); - /* Append recoded initial docid and everything else. Rest of docids - ** should have been delta-encoded from previous initial docid. + /* Unless a malloc() failure has occurred, execute the SQL script to + ** create the tables used to store data for this FTS3 virtual table. */ - if( nFirstOldb, c, nFirstNew, - pData+nFirstOld, nData-nFirstOld); + if( zContentCols==0 || zSql==0 ){ + rc = SQLITE_NOMEM; }else{ - dataBufferAppend(pWriter->b, c, nFirstNew); + rc = sqlite3_exec(p->db, zSql, 0, 0, 0); } - pWriter->iPrevDocid = iLastDocid; -} -static void dlwCopy(DLWriter *pWriter, DLReader *pReader){ - dlwAppend(pWriter, dlrDocData(pReader), dlrDocDataBytes(pReader), - dlrDocid(pReader), dlrDocid(pReader)); -} -static void dlwAdd(DLWriter *pWriter, sqlite_int64 iDocid){ - char c[VARINT_MAX]; - int n = fts3PutVarint(c, iDocid-pWriter->iPrevDocid); - /* Docids must ascend. */ - assert( !pWriter->has_iPrevDocid || iDocid>pWriter->iPrevDocid ); - assert( pWriter->iType==DL_DOCIDS ); - - dataBufferAppend(pWriter->b, c, n); - pWriter->iPrevDocid = iDocid; -#ifndef NDEBUG - pWriter->has_iPrevDocid = 1; -#endif + sqlite3_free(zSql); + sqlite3_free(zContentCols); + return rc; } -/*******************************************************************/ -/* PLReader is used to read data from a document's position list. As -** the caller steps through the list, data is cached so that varints -** only need to be decoded once. +/* +** This function is the implementation of both the xConnect and xCreate +** methods of the FTS3 virtual table. +** +** The argv[] array contains the following: ** -** plrInit, plrDestroy - create/destroy a reader. -** plrColumn, plrPosition, plrStartOffset, plrEndOffset - accessors -** plrAtEnd - at end of stream, only call plrDestroy once true. -** plrStep - step to the next element. +** argv[0] -> module name +** argv[1] -> database name +** argv[2] -> table name +** argv[...] -> "column name" and other module argument fields. */ -typedef struct PLReader { - /* These refer to the next position's data. nData will reach 0 when - ** reading the last position, so plrStep() signals EOF by setting - ** pData to NULL. - */ - const char *pData; - int nData; +static int fts3InitVtab( + int isCreate, /* True for xCreate, false for xConnect */ + sqlite3 *db, /* The SQLite database connection */ + void *pAux, /* Hash table containing tokenizers */ + int argc, /* Number of elements in argv array */ + const char * const *argv, /* xCreate/xConnect argument array */ + sqlite3_vtab **ppVTab, /* Write the resulting vtab structure here */ + char **pzErr /* Write any error message here */ +){ + Fts3Hash *pHash = (Fts3Hash *)pAux; + Fts3Table *p; /* Pointer to allocated vtab */ + int rc; /* Return code */ + int i; /* Iterator variable */ + int nByte; /* Size of allocation used for *p */ + int iCol; + int nString = 0; + int nCol = 0; + char *zCsr; + int nDb; + int nName; - DocListType iType; - int iColumn; /* the last column read */ - int iPosition; /* the last position read */ - int iStartOffset; /* the last start offset read */ - int iEndOffset; /* the last end offset read */ -} PLReader; - -static int plrAtEnd(PLReader *pReader){ - return pReader->pData==NULL; -} -static int plrColumn(PLReader *pReader){ - assert( !plrAtEnd(pReader) ); - return pReader->iColumn; -} -static int plrPosition(PLReader *pReader){ - assert( !plrAtEnd(pReader) ); - return pReader->iPosition; -} -static int plrStartOffset(PLReader *pReader){ - assert( !plrAtEnd(pReader) ); - return pReader->iStartOffset; -} -static int plrEndOffset(PLReader *pReader){ - assert( !plrAtEnd(pReader) ); - return pReader->iEndOffset; -} -static void plrStep(PLReader *pReader){ - int i, n; + const char *zTokenizer = 0; /* Name of tokenizer to use */ + sqlite3_tokenizer *pTokenizer = 0; /* Tokenizer for this table */ + + nDb = (int)strlen(argv[1]) + 1; + nName = (int)strlen(argv[2]) + 1; + for(i=3; inData==0 ){ - pReader->pData = NULL; - return; + /* Allocate and populate the Fts3Table structure. */ + nByte = sizeof(Fts3Table) + /* Fts3Table */ + nCol * sizeof(char *) + /* azColumn */ + nName + /* zName */ + nDb + /* zDb */ + nString; /* Space for azColumn strings */ + p = (Fts3Table*)sqlite3_malloc(nByte); + if( p==0 ){ + rc = SQLITE_NOMEM; + goto fts3_init_out; } + memset(p, 0, nByte); - n = fts3GetVarint32(pReader->pData, &i); - if( i==POS_COLUMN ){ - n += fts3GetVarint32(pReader->pData+n, &pReader->iColumn); - pReader->iPosition = 0; - pReader->iStartOffset = 0; - n += fts3GetVarint32(pReader->pData+n, &i); - } - /* Should never see adjacent column changes. */ - assert( i!=POS_COLUMN ); - - if( i==POS_END ){ - pReader->nData = 0; - pReader->pData = NULL; - return; + p->db = db; + p->nColumn = nCol; + p->nPendingData = 0; + p->azColumn = (char **)&p[1]; + p->pTokenizer = pTokenizer; + p->nNodeSize = 1000; + p->nMaxPendingData = FTS3_MAX_PENDING_DATA; + zCsr = (char *)&p->azColumn[nCol]; + + fts3HashInit(&p->pendingTerms, FTS3_HASH_STRING, 1); + + /* Fill in the zName and zDb fields of the vtab structure. */ + p->zName = zCsr; + memcpy(zCsr, argv[2], nName); + zCsr += nName; + p->zDb = zCsr; + memcpy(zCsr, argv[1], nDb); + zCsr += nDb; + + /* Fill in the azColumn array */ + iCol = 0; + for(i=3; iazColumn[iCol++] = zCsr; + zCsr += n+1; + assert( zCsr <= &((char *)p)[nByte] ); + } + } + if( iCol==0 ){ + assert( nCol==1 ); + p->azColumn[0] = "content"; } - pReader->iPosition += i-POS_BASE; - if( pReader->iType==DL_POSITIONS_OFFSETS ){ - n += fts3GetVarint32(pReader->pData+n, &i); - pReader->iStartOffset += i; - n += fts3GetVarint32(pReader->pData+n, &i); - pReader->iEndOffset = pReader->iStartOffset+i; - } - assert( n<=pReader->nData ); - pReader->pData += n; - pReader->nData -= n; -} - -static void plrInit(PLReader *pReader, DLReader *pDLReader){ - pReader->pData = dlrPosData(pDLReader); - pReader->nData = dlrPosDataLen(pDLReader); - pReader->iType = pDLReader->iType; - pReader->iColumn = 0; - pReader->iPosition = 0; - pReader->iStartOffset = 0; - pReader->iEndOffset = 0; - plrStep(pReader); -} -static void plrDestroy(PLReader *pReader){ - SCRAMBLE(pReader); -} - -/*******************************************************************/ -/* PLWriter is used in constructing a document's position list. As a -** convenience, if iType is DL_DOCIDS, PLWriter becomes a no-op. -** PLWriter writes to the associated DLWriter's buffer. -** -** plwInit - init for writing a document's poslist. -** plwDestroy - clear a writer. -** plwAdd - append position and offset information. -** plwCopy - copy next position's data from reader to writer. -** plwTerminate - add any necessary doclist terminator. -** -** Calling plwAdd() after plwTerminate() may result in a corrupt -** doclist. -*/ -/* TODO(shess) Until we've written the second item, we can cache the -** first item's information. Then we'd have three states: -** -** - initialized with docid, no positions. -** - docid and one position. -** - docid and multiple positions. -** -** Only the last state needs to actually write to dlw->b, which would -** be an improvement in the DLCollector case. -*/ -typedef struct PLWriter { - DLWriter *dlw; - - int iColumn; /* the last column written */ - int iPos; /* the last position written */ - int iOffset; /* the last start offset written */ -} PLWriter; - -/* TODO(shess) In the case where the parent is reading these values -** from a PLReader, we could optimize to a copy if that PLReader has -** the same type as pWriter. -*/ -static void plwAdd(PLWriter *pWriter, int iColumn, int iPos, - int iStartOffset, int iEndOffset){ - /* Worst-case space for POS_COLUMN, iColumn, iPosDelta, - ** iStartOffsetDelta, and iEndOffsetDelta. + /* If this is an xCreate call, create the underlying tables in the + ** database. TODO: For xConnect(), it could verify that said tables exist. */ - char c[5*VARINT_MAX]; - int n = 0; + if( isCreate ){ + rc = fts3CreateTables(p); + if( rc!=SQLITE_OK ) goto fts3_init_out; + } - /* Ban plwAdd() after plwTerminate(). */ - assert( pWriter->iPos!=-1 ); + rc = fts3DeclareVtab(p); + if( rc!=SQLITE_OK ) goto fts3_init_out; - if( pWriter->dlw->iType==DL_DOCIDS ) return; + *ppVTab = &p->base; - if( iColumn!=pWriter->iColumn ){ - n += fts3PutVarint(c+n, POS_COLUMN); - n += fts3PutVarint(c+n, iColumn); - pWriter->iColumn = iColumn; - pWriter->iPos = 0; - pWriter->iOffset = 0; - } - assert( iPos>=pWriter->iPos ); - n += fts3PutVarint(c+n, POS_BASE+(iPos-pWriter->iPos)); - pWriter->iPos = iPos; - if( pWriter->dlw->iType==DL_POSITIONS_OFFSETS ){ - assert( iStartOffset>=pWriter->iOffset ); - n += fts3PutVarint(c+n, iStartOffset-pWriter->iOffset); - pWriter->iOffset = iStartOffset; - assert( iEndOffset>=iStartOffset ); - n += fts3PutVarint(c+n, iEndOffset-iStartOffset); - } - dataBufferAppend(pWriter->dlw->b, c, n); -} -static void plwCopy(PLWriter *pWriter, PLReader *pReader){ - plwAdd(pWriter, plrColumn(pReader), plrPosition(pReader), - plrStartOffset(pReader), plrEndOffset(pReader)); +fts3_init_out: + assert( p || (pTokenizer && rc!=SQLITE_OK) ); + if( rc!=SQLITE_OK ){ + if( p ){ + fts3DisconnectMethod((sqlite3_vtab *)p); + }else{ + pTokenizer->pModule->xDestroy(pTokenizer); + } + } + return rc; } -static void plwInit(PLWriter *pWriter, DLWriter *dlw, sqlite_int64 iDocid){ - char c[VARINT_MAX]; - int n; - pWriter->dlw = dlw; +/* +** The xConnect() and xCreate() methods for the virtual table. All the +** work is done in function fts3InitVtab(). +*/ +static int fts3ConnectMethod( + sqlite3 *db, /* Database connection */ + void *pAux, /* Pointer to tokenizer hash table */ + int argc, /* Number of elements in argv array */ + const char * const *argv, /* xCreate/xConnect argument array */ + sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ + char **pzErr /* OUT: sqlite3_malloc'd error message */ +){ + return fts3InitVtab(0, db, pAux, argc, argv, ppVtab, pzErr); +} +static int fts3CreateMethod( + sqlite3 *db, /* Database connection */ + void *pAux, /* Pointer to tokenizer hash table */ + int argc, /* Number of elements in argv array */ + const char * const *argv, /* xCreate/xConnect argument array */ + sqlite3_vtab **ppVtab, /* OUT: New sqlite3_vtab object */ + char **pzErr /* OUT: sqlite3_malloc'd error message */ +){ + return fts3InitVtab(1, db, pAux, argc, argv, ppVtab, pzErr); +} - /* Docids must ascend. */ - assert( !pWriter->dlw->has_iPrevDocid || iDocid>pWriter->dlw->iPrevDocid ); - n = fts3PutVarint(c, iDocid-pWriter->dlw->iPrevDocid); - dataBufferAppend(pWriter->dlw->b, c, n); - pWriter->dlw->iPrevDocid = iDocid; -#ifndef NDEBUG - pWriter->dlw->has_iPrevDocid = 1; -#endif +/* +** Implementation of the xBestIndex method for FTS3 tables. There +** are three possible strategies, in order of preference: +** +** 1. Direct lookup by rowid or docid. +** 2. Full-text search using a MATCH operator on a non-docid column. +** 3. Linear scan of %_content table. +*/ +static int fts3BestIndexMethod(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ + Fts3Table *p = (Fts3Table *)pVTab; + int i; /* Iterator variable */ + int iCons = -1; /* Index of constraint to use */ + + /* By default use a full table scan. This is an expensive option, + ** so search through the constraints to see if a more efficient + ** strategy is possible. + */ + pInfo->idxNum = FTS3_FULLSCAN_SEARCH; + pInfo->estimatedCost = 500000; + for(i=0; inConstraint; i++){ + struct sqlite3_index_constraint *pCons = &pInfo->aConstraint[i]; + if( pCons->usable==0 ) continue; + + /* A direct lookup on the rowid or docid column. Assign a cost of 1.0. */ + if( pCons->op==SQLITE_INDEX_CONSTRAINT_EQ + && (pCons->iColumn<0 || pCons->iColumn==p->nColumn+1 ) + ){ + pInfo->idxNum = FTS3_DOCID_SEARCH; + pInfo->estimatedCost = 1.0; + iCons = i; + } - pWriter->iColumn = 0; - pWriter->iPos = 0; - pWriter->iOffset = 0; -} -/* TODO(shess) Should plwDestroy() also terminate the doclist? But -** then plwDestroy() would no longer be just a destructor, it would -** also be doing work, which isn't consistent with the overall idiom. -** Another option would be for plwAdd() to always append any necessary -** terminator, so that the output is always correct. But that would -** add incremental work to the common case with the only benefit being -** API elegance. Punt for now. -*/ -static void plwTerminate(PLWriter *pWriter){ - if( pWriter->dlw->iType>DL_DOCIDS ){ - char c[VARINT_MAX]; - int n = fts3PutVarint(c, POS_END); - dataBufferAppend(pWriter->dlw->b, c, n); + /* A MATCH constraint. Use a full-text search. + ** + ** If there is more than one MATCH constraint available, use the first + ** one encountered. If there is both a MATCH constraint and a direct + ** rowid/docid lookup, prefer the MATCH strategy. This is done even + ** though the rowid/docid lookup is faster than a MATCH query, selecting + ** it would lead to an "unable to use function MATCH in the requested + ** context" error. + */ + if( pCons->op==SQLITE_INDEX_CONSTRAINT_MATCH + && pCons->iColumn>=0 && pCons->iColumn<=p->nColumn + ){ + pInfo->idxNum = FTS3_FULLTEXT_SEARCH + pCons->iColumn; + pInfo->estimatedCost = 2.0; + iCons = i; + break; + } } -#ifndef NDEBUG - /* Mark as terminated for assert in plwAdd(). */ - pWriter->iPos = -1; -#endif -} -static void plwDestroy(PLWriter *pWriter){ - SCRAMBLE(pWriter); + + if( iCons>=0 ){ + pInfo->aConstraintUsage[iCons].argvIndex = 1; + pInfo->aConstraintUsage[iCons].omit = 1; + } + return SQLITE_OK; } -/*******************************************************************/ -/* DLCollector wraps PLWriter and DLWriter to provide a -** dynamically-allocated doclist area to use during tokenization. -** -** dlcNew - malloc up and initialize a collector. -** dlcDelete - destroy a collector and all contained items. -** dlcAddPos - append position and offset information. -** dlcAddDoclist - add the collected doclist to the given buffer. -** dlcNext - terminate the current document and open another. +/* +** Implementation of xOpen method. */ -typedef struct DLCollector { - DataBuffer b; - DLWriter dlw; - PLWriter plw; -} DLCollector; +static int fts3OpenMethod(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCsr){ + sqlite3_vtab_cursor *pCsr; /* Allocated cursor */ -/* TODO(shess) This could also be done by calling plwTerminate() and -** dataBufferAppend(). I tried that, expecting nominal performance -** differences, but it seemed to pretty reliably be worth 1% to code -** it this way. I suspect it is the incremental malloc overhead (some -** percentage of the plwTerminate() calls will cause a realloc), so -** this might be worth revisiting if the DataBuffer implementation -** changes. -*/ -static void dlcAddDoclist(DLCollector *pCollector, DataBuffer *b){ - if( pCollector->dlw.iType>DL_DOCIDS ){ - char c[VARINT_MAX]; - int n = fts3PutVarint(c, POS_END); - dataBufferAppend2(b, pCollector->b.pData, pCollector->b.nData, c, n); - }else{ - dataBufferAppend(b, pCollector->b.pData, pCollector->b.nData); + UNUSED_PARAMETER(pVTab); + + /* Allocate a buffer large enough for an Fts3Cursor structure. If the + ** allocation succeeds, zero it and return SQLITE_OK. Otherwise, + ** if the allocation fails, return SQLITE_NOMEM. + */ + *ppCsr = pCsr = (sqlite3_vtab_cursor *)sqlite3_malloc(sizeof(Fts3Cursor)); + if( !pCsr ){ + return SQLITE_NOMEM; } + memset(pCsr, 0, sizeof(Fts3Cursor)); + return SQLITE_OK; } -static void dlcNext(DLCollector *pCollector, sqlite_int64 iDocid){ - plwTerminate(&pCollector->plw); - plwDestroy(&pCollector->plw); - plwInit(&pCollector->plw, &pCollector->dlw, iDocid); -} -static void dlcAddPos(DLCollector *pCollector, int iColumn, int iPos, - int iStartOffset, int iEndOffset){ - plwAdd(&pCollector->plw, iColumn, iPos, iStartOffset, iEndOffset); -} -static DLCollector *dlcNew(sqlite_int64 iDocid, DocListType iType){ - DLCollector *pCollector = sqlite3_malloc(sizeof(DLCollector)); - dataBufferInit(&pCollector->b, 0); - dlwInit(&pCollector->dlw, iType, &pCollector->b); - plwInit(&pCollector->plw, &pCollector->dlw, iDocid); - return pCollector; +/****************************************************************/ +/****************************************************************/ +/****************************************************************/ +/****************************************************************/ + + +/* +** Close the cursor. For additional information see the documentation +** on the xClose method of the virtual table interface. +*/ +static int fulltextClose(sqlite3_vtab_cursor *pCursor){ + Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; + sqlite3_finalize(pCsr->pStmt); + sqlite3Fts3ExprFree(pCsr->pExpr); + sqlite3_free(pCsr->aDoclist); + sqlite3_free(pCsr->aMatchinfo); + sqlite3_free(pCsr); + return SQLITE_OK; } -static void dlcDelete(DLCollector *pCollector){ - plwDestroy(&pCollector->plw); - dlwDestroy(&pCollector->dlw); - dataBufferDestroy(&pCollector->b); - SCRAMBLE(pCollector); - sqlite3_free(pCollector); + +static int fts3CursorSeek(sqlite3_context *pContext, Fts3Cursor *pCsr){ + if( pCsr->isRequireSeek ){ + pCsr->isRequireSeek = 0; + sqlite3_bind_int64(pCsr->pStmt, 1, pCsr->iPrevId); + if( SQLITE_ROW==sqlite3_step(pCsr->pStmt) ){ + return SQLITE_OK; + }else{ + int rc = sqlite3_reset(pCsr->pStmt); + if( rc==SQLITE_OK ){ + /* If no row was found and no error has occured, then the %_content + ** table is missing a row that is present in the full-text index. + ** The data structures are corrupt. + */ + rc = SQLITE_CORRUPT; + } + pCsr->isEof = 1; + if( pContext ){ + sqlite3_result_error_code(pContext, rc); + } + return rc; + } + }else{ + return SQLITE_OK; + } } +static int fts3NextMethod(sqlite3_vtab_cursor *pCursor){ + int rc = SQLITE_OK; /* Return code */ + Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; -/* Copy the doclist data of iType in pData/nData into *out, trimming -** unnecessary data as we go. Only columns matching iColumn are -** copied, all columns copied if iColumn is -1. Elements with no -** matching columns are dropped. The output is an iOutType doclist. -*/ -/* NOTE(shess) This code is only valid after all doclists are merged. -** If this is run before merges, then doclist items which represent -** deletion will be trimmed, and will thus not effect a deletion -** during the merge. -*/ -static void docListTrim(DocListType iType, const char *pData, int nData, - int iColumn, DocListType iOutType, DataBuffer *out){ - DLReader dlReader; - DLWriter dlWriter; + if( pCsr->aDoclist==0 ){ + if( SQLITE_ROW!=sqlite3_step(pCsr->pStmt) ){ + pCsr->isEof = 1; + rc = sqlite3_reset(pCsr->pStmt); + } + }else if( pCsr->pNextId>=&pCsr->aDoclist[pCsr->nDoclist] ){ + pCsr->isEof = 1; + }else{ + sqlite3_reset(pCsr->pStmt); + fts3GetDeltaVarint(&pCsr->pNextId, &pCsr->iPrevId); + pCsr->isRequireSeek = 1; + pCsr->isMatchinfoOk = 1; + } + return rc; +} - assert( iOutType<=iType ); - dlrInit(&dlReader, iType, pData, nData); - dlwInit(&dlWriter, iOutType, out); +/* +** The buffer pointed to by argument zNode (size nNode bytes) contains the +** root node of a b-tree segment. The segment is guaranteed to be at least +** one level high (i.e. the root node is not also a leaf). If successful, +** this function locates the leaf node of the segment that may contain the +** term specified by arguments zTerm and nTerm and writes its block number +** to *piLeaf. +** +** It is possible that the returned leaf node does not contain the specified +** term. However, if the segment does contain said term, it is stored on +** the identified leaf node. Because this function only inspects interior +** segment nodes (and never loads leaf nodes into memory), it is not possible +** to be sure. +** +** If an error occurs, an error code other than SQLITE_OK is returned. +*/ +static int fts3SelectLeaf( + Fts3Table *p, /* Virtual table handle */ + const char *zTerm, /* Term to select leaves for */ + int nTerm, /* Size of term zTerm in bytes */ + const char *zNode, /* Buffer containing segment interior node */ + int nNode, /* Size of buffer at zNode */ + sqlite3_int64 *piLeaf /* Selected leaf node */ +){ + int rc = SQLITE_OK; /* Return code */ + const char *zCsr = zNode; /* Cursor to iterate through node */ + const char *zEnd = &zCsr[nNode];/* End of interior node buffer */ + char *zBuffer = 0; /* Buffer to load terms into */ + int nAlloc = 0; /* Size of allocated buffer */ - while( !dlrAtEnd(&dlReader) ){ - PLReader plReader; - PLWriter plWriter; - int match = 0; + while( 1 ){ + int isFirstTerm = 1; /* True when processing first term on page */ + int iHeight; /* Height of this node in tree */ + sqlite3_int64 iChild; /* Block id of child node to descend to */ + int nBlock; /* Size of child node in bytes */ - plrInit(&plReader, &dlReader); + zCsr += sqlite3Fts3GetVarint32(zCsr, &iHeight); + zCsr += sqlite3Fts3GetVarint(zCsr, &iChild); + + while( zCsrnAlloc ){ + char *zNew; + nAlloc = (nPrefix+nSuffix) * 2; + zNew = (char *)sqlite3_realloc(zBuffer, nAlloc); + if( !zNew ){ + sqlite3_free(zBuffer); + return SQLITE_NOMEM; + } + zBuffer = zNew; + } + memcpy(&zBuffer[nPrefix], zCsr, nSuffix); + nBuffer = nPrefix + nSuffix; + zCsr += nSuffix; + + /* Compare the term we are searching for with the term just loaded from + ** the interior node. If the specified term is greater than or equal + ** to the term from the interior node, then all terms on the sub-tree + ** headed by node iChild are smaller than zTerm. No need to search + ** iChild. + ** + ** If the interior node term is larger than the specified term, then + ** the tree headed by iChild may contain the specified term. + */ + cmp = memcmp(zTerm, zBuffer, (nBuffer>nTerm ? nTerm : nBuffer)); + if( cmp<0 || (cmp==0 && nBuffer>nTerm) ) break; + iChild++; + }; - while( !plrAtEnd(&plReader) ){ - if( iColumn==-1 || plrColumn(&plReader)==iColumn ){ - if( !match ){ - plwInit(&plWriter, &dlWriter, dlrDocid(&dlReader)); - match = 1; - } - plwAdd(&plWriter, plrColumn(&plReader), plrPosition(&plReader), - plrStartOffset(&plReader), plrEndOffset(&plReader)); - } - plrStep(&plReader); - } - if( match ){ - plwTerminate(&plWriter); - plwDestroy(&plWriter); + /* If (iHeight==1), the children of this interior node are leaves. The + ** specified term may be present on leaf node iChild. + */ + if( iHeight==1 ){ + *piLeaf = iChild; + break; } - plrDestroy(&plReader); - dlrStep(&dlReader); + /* Descend to interior node iChild. */ + rc = sqlite3Fts3ReadBlock(p, iChild, &zCsr, &nBlock); + if( rc!=SQLITE_OK ) break; + zEnd = &zCsr[nBlock]; } - dlwDestroy(&dlWriter); - dlrDestroy(&dlReader); + sqlite3_free(zBuffer); + return rc; +} + +/* +** This function is used to create delta-encoded serialized lists of FTS3 +** varints. Each call to this function appends a single varint to a list. +*/ +static void fts3PutDeltaVarint( + char **pp, /* IN/OUT: Output pointer */ + sqlite3_int64 *piPrev, /* IN/OUT: Previous value written to list */ + sqlite3_int64 iVal /* Write this value to the list */ +){ + assert( iVal-*piPrev > 0 || (*piPrev==0 && iVal==0) ); + *pp += sqlite3Fts3PutVarint(*pp, iVal-*piPrev); + *piPrev = iVal; } -/* Used by docListMerge() to keep doclists in the ascending order by -** docid, then ascending order by age (so the newest comes first). +/* +** When this function is called, *ppPoslist is assumed to point to the +** start of a position-list. */ -typedef struct OrderedDLReader { - DLReader *pReader; +static void fts3PoslistCopy(char **pp, char **ppPoslist){ + char *pEnd = *ppPoslist; + char c = 0; - /* TODO(shess) If we assume that docListMerge pReaders is ordered by - ** age (which we do), then we could use pReader comparisons to break - ** ties. + /* The end of a position list is marked by a zero encoded as an FTS3 + ** varint. A single 0x00 byte. Except, if the 0x00 byte is preceded by + ** a byte with the 0x80 bit set, then it is not a varint 0, but the tail + ** of some other, multi-byte, value. + ** + ** The following block moves pEnd to point to the first byte that is not + ** immediately preceded by a byte with the 0x80 bit set. Then increments + ** pEnd once more so that it points to the byte immediately following the + ** last byte in the position-list. */ - int idx; -} OrderedDLReader; + while( *pEnd | c ) c = *pEnd++ & 0x80; + pEnd++; -/* Order eof to end, then by docid asc, idx desc. */ -static int orderedDLReaderCmp(OrderedDLReader *r1, OrderedDLReader *r2){ - if( dlrAtEnd(r1->pReader) ){ - if( dlrAtEnd(r2->pReader) ) return 0; /* Both atEnd(). */ - return 1; /* Only r1 atEnd(). */ + if( pp ){ + int n = (int)(pEnd - *ppPoslist); + char *p = *pp; + memcpy(p, *ppPoslist, n); + p += n; + *pp = p; } - if( dlrAtEnd(r2->pReader) ) return -1; /* Only r2 atEnd(). */ + *ppPoslist = pEnd; +} - if( dlrDocid(r1->pReader)pReader) ) return -1; - if( dlrDocid(r1->pReader)>dlrDocid(r2->pReader) ) return 1; +static void fts3ColumnlistCopy(char **pp, char **ppPoslist){ + char *pEnd = *ppPoslist; + char c = 0; - /* Descending on idx. */ - return r2->idx-r1->idx; + /* A column-list is terminated by either a 0x01 or 0x00. */ + while( 0xFE & (*pEnd | c) ) c = *pEnd++ & 0x80; + if( pp ){ + int n = (int)(pEnd - *ppPoslist); + char *p = *pp; + memcpy(p, *ppPoslist, n); + p += n; + *pp = p; + } + *ppPoslist = pEnd; } -/* Bubble p[0] to appropriate place in p[1..n-1]. Assumes that -** p[1..n-1] is already sorted. +/* +** Value used to signify the end of an offset-list. This is safe because +** it is not possible to have a document with 2^31 terms. */ -/* TODO(shess) Is this frequent enough to warrant a binary search? -** Before implementing that, instrument the code to check. In most -** current usage, I expect that p[0] will be less than p[1] a very -** high proportion of the time. +#define OFFSET_LIST_END 0x7fffffff + +/* +** This function is used to help parse offset-lists. When this function is +** called, *pp may point to the start of the next varint in the offset-list +** being parsed, or it may point to 1 byte past the end of the offset-list +** (in which case **pp will be 0x00 or 0x01). +** +** If *pp points past the end of the current offset list, set *pi to +** OFFSET_LIST_END and return. Otherwise, read the next varint from *pp, +** increment the current value of *pi by the value read, and set *pp to +** point to the next value before returning. */ -static void orderedDLReaderReorder(OrderedDLReader *p, int n){ - while( n>1 && orderedDLReaderCmp(p, p+1)>0 ){ - OrderedDLReader tmp = p[0]; - p[0] = p[1]; - p[1] = tmp; - n--; - p++; +static void fts3ReadNextPos( + char **pp, /* IN/OUT: Pointer into offset-list buffer */ + sqlite3_int64 *pi /* IN/OUT: Value read from offset-list */ +){ + if( **pp&0xFE ){ + fts3GetDeltaVarint(pp, pi); + *pi -= 2; + }else{ + *pi = OFFSET_LIST_END; } } -/* Given an array of doclist readers, merge their doclist elements -** into out in sorted order (by docid), dropping elements from older -** readers when there is a duplicate docid. pReaders is assumed to be -** ordered by age, oldest first. -*/ -/* TODO(shess) nReaders must be <= MERGE_COUNT. This should probably -** be fixed. -*/ -static void docListMerge(DataBuffer *out, - DLReader *pReaders, int nReaders){ - OrderedDLReader readers[MERGE_COUNT]; - DLWriter writer; - int i, n; - const char *pStart = 0; - int nStart = 0; - sqlite_int64 iFirstDocid = 0, iLastDocid = 0; - - assert( nReaders>0 ); - if( nReaders==1 ){ - dataBufferAppend(out, dlrDocData(pReaders), dlrAllDataBytes(pReaders)); - return; +/* +** If parameter iCol is not 0, write an 0x01 byte followed by the value of +** iCol encoded as a varint to *pp. +** +** Set *pp to point to the byte just after the last byte written before +** returning (do not modify it if iCol==0). Return the total number of bytes +** written (0 if iCol==0). +*/ +static int fts3PutColNumber(char **pp, int iCol){ + int n = 0; /* Number of bytes written */ + if( iCol ){ + char *p = *pp; /* Output pointer */ + n = 1 + sqlite3Fts3PutVarint(&p[1], iCol); + *p = 0x01; + *pp = &p[n]; } + return n; +} - assert( nReaders<=MERGE_COUNT ); - n = 0; - for(i=0; i0 ){ - orderedDLReaderReorder(readers+i, nReaders-i); + *p++ = '\0'; + *pp = p; + *pp1 = p1 + 1; + *pp2 = p2 + 1; +} + +/* +** nToken==1 searches for adjacent positions. +*/ +static int fts3PoslistPhraseMerge( + char **pp, /* Output buffer */ + int nToken, /* Maximum difference in token positions */ + int isSaveLeft, /* Save the left position */ + char **pp1, /* Left input list */ + char **pp2 /* Right input list */ +){ + char *p = (pp ? *pp : 0); + char *p1 = *pp1; + char *p2 = *pp2; + + int iCol1 = 0; + int iCol2 = 0; + assert( *p1!=0 && *p2!=0 ); + if( *p1==0x01 ){ + p1++; + p1 += sqlite3Fts3GetVarint32(p1, &iCol1); + } + if( *p2==0x01 ){ + p2++; + p2 += sqlite3Fts3GetVarint32(p2, &iCol2); } - dlwInit(&writer, pReaders[0].iType, out); - while( !dlrAtEnd(readers[0].pReader) ){ - sqlite_int64 iDocid = dlrDocid(readers[0].pReader); + while( 1 ){ + if( iCol1==iCol2 ){ + char *pSave = p; + sqlite3_int64 iPrev = 0; + sqlite3_int64 iPos1 = 0; + sqlite3_int64 iPos2 = 0; + + if( pp && iCol1 ){ + *p++ = 0x01; + p += sqlite3Fts3PutVarint(p, iCol1); + } + + assert( *p1!=0x00 && *p2!=0x00 && *p1!=0x01 && *p2!=0x01 ); + fts3GetDeltaVarint(&p1, &iPos1); iPos1 -= 2; + fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2; - /* If this is a continuation of the current buffer to copy, extend - ** that buffer. memcpy() seems to be more efficient if it has a - ** lots of data to copy. - */ - if( dlrDocData(readers[0].pReader)==pStart+nStart ){ - nStart += dlrDocDataBytes(readers[0].pReader); - }else{ - if( pStart!=0 ){ - dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid); + while( 1 ){ + if( iPos2>iPos1 && iPos2<=iPos1+nToken ){ + sqlite3_int64 iSave; + if( !pp ){ + fts3PoslistCopy(0, &p2); + fts3PoslistCopy(0, &p1); + *pp1 = p1; + *pp2 = p2; + return 1; + } + iSave = isSaveLeft ? iPos1 : iPos2; + fts3PutDeltaVarint(&p, &iPrev, iSave+2); iPrev -= 2; + pSave = 0; + } + if( (!isSaveLeft && iPos2<=(iPos1+nToken)) || iPos2<=iPos1 ){ + if( (*p2&0xFE)==0 ) break; + fts3GetDeltaVarint(&p2, &iPos2); iPos2 -= 2; + }else{ + if( (*p1&0xFE)==0 ) break; + fts3GetDeltaVarint(&p1, &iPos1); iPos1 -= 2; + } } - pStart = dlrDocData(readers[0].pReader); - nStart = dlrDocDataBytes(readers[0].pReader); - iFirstDocid = iDocid; - } - iLastDocid = iDocid; - dlrStep(readers[0].pReader); - /* Drop all of the older elements with the same docid. */ - for(i=1; i0 ){ - orderedDLReaderReorder(readers+i, nReaders-i); + /* Advance pointer p1 or p2 (whichever corresponds to the smaller of + ** iCol1 and iCol2) so that it points to either the 0x00 that marks the + ** end of the position list, or the 0x01 that precedes the next + ** column-number in the position list. + */ + else if( iCol10 ) dlwAppend(&writer, pStart, nStart, iFirstDocid, iLastDocid); - dlwDestroy(&writer); + fts3PoslistCopy(0, &p2); + fts3PoslistCopy(0, &p1); + *pp1 = p1; + *pp2 = p2; + if( !pp || *pp==p ){ + return 0; + } + *p++ = 0x00; + *pp = p; + return 1; } -/* Helper function for posListUnion(). Compares the current position -** between left and right, returning as standard C idiom of <0 if -** left0 if left>right, and 0 if left==right. "End" always -** compares greater. +/* +** Merge two position-lists as required by the NEAR operator. */ -static int posListCmp(PLReader *pLeft, PLReader *pRight){ - assert( pLeft->iType==pRight->iType ); - if( pLeft->iType==DL_DOCIDS ) return 0; +static int fts3PoslistNearMerge( + char **pp, /* Output buffer */ + char *aTmp, /* Temporary buffer space */ + int nRight, /* Maximum difference in token positions */ + int nLeft, /* Maximum difference in token positions */ + char **pp1, /* IN/OUT: Left input list */ + char **pp2 /* IN/OUT: Right input list */ +){ + char *p1 = *pp1; + char *p2 = *pp2; - if( plrAtEnd(pLeft) ) return plrAtEnd(pRight) ? 0 : 1; - if( plrAtEnd(pRight) ) return -1; + if( !pp ){ + if( fts3PoslistPhraseMerge(0, nRight, 0, pp1, pp2) ) return 1; + *pp1 = p1; + *pp2 = p2; + return fts3PoslistPhraseMerge(0, nLeft, 0, pp2, pp1); + }else{ + char *pTmp1 = aTmp; + char *pTmp2; + char *aTmp2; + int res = 1; + + fts3PoslistPhraseMerge(&pTmp1, nRight, 0, pp1, pp2); + aTmp2 = pTmp2 = pTmp1; + *pp1 = p1; + *pp2 = p2; + fts3PoslistPhraseMerge(&pTmp2, nLeft, 1, pp2, pp1); + if( pTmp1!=aTmp && pTmp2!=aTmp2 ){ + fts3PoslistMerge(pp, &aTmp, &aTmp2); + }else if( pTmp1!=aTmp ){ + fts3PoslistCopy(pp, &aTmp); + }else if( pTmp2!=aTmp2 ){ + fts3PoslistCopy(pp, &aTmp2); + }else{ + res = 0; + } - if( plrColumn(pLeft)plrColumn(pRight) ) return 1; + return res; + } +} + +/* +** Values that may be used as the first parameter to fts3DoclistMerge(). +*/ +#define MERGE_NOT 2 /* D + D -> D */ +#define MERGE_AND 3 /* D + D -> D */ +#define MERGE_OR 4 /* D + D -> D */ +#define MERGE_POS_OR 5 /* P + P -> P */ +#define MERGE_PHRASE 6 /* P + P -> D */ +#define MERGE_POS_PHRASE 7 /* P + P -> P */ +#define MERGE_NEAR 8 /* P + P -> D */ +#define MERGE_POS_NEAR 9 /* P + P -> P */ - if( plrPosition(pLeft)plrPosition(pRight) ) return 1; - if( pLeft->iType==DL_POSITIONS ) return 0; +/* +** Merge the two doclists passed in buffer a1 (size n1 bytes) and a2 +** (size n2 bytes). The output is written to pre-allocated buffer aBuffer, +** which is guaranteed to be large enough to hold the results. The number +** of bytes written to aBuffer is stored in *pnBuffer before returning. +** +** If successful, SQLITE_OK is returned. Otherwise, if a malloc error +** occurs while allocating a temporary buffer as part of the merge operation, +** SQLITE_NOMEM is returned. +*/ +static int fts3DoclistMerge( + int mergetype, /* One of the MERGE_XXX constants */ + int nParam1, /* Used by MERGE_NEAR and MERGE_POS_NEAR */ + int nParam2, /* Used by MERGE_NEAR and MERGE_POS_NEAR */ + char *aBuffer, /* Pre-allocated output buffer */ + int *pnBuffer, /* OUT: Bytes written to aBuffer */ + char *a1, /* Buffer containing first doclist */ + int n1, /* Size of buffer a1 */ + char *a2, /* Buffer containing second doclist */ + int n2 /* Size of buffer a2 */ +){ + sqlite3_int64 i1 = 0; + sqlite3_int64 i2 = 0; + sqlite3_int64 iPrev = 0; + + char *p = aBuffer; + char *p1 = a1; + char *p2 = a2; + char *pEnd1 = &a1[n1]; + char *pEnd2 = &a2[n2]; + + assert( mergetype==MERGE_OR || mergetype==MERGE_POS_OR + || mergetype==MERGE_AND || mergetype==MERGE_NOT + || mergetype==MERGE_PHRASE || mergetype==MERGE_POS_PHRASE + || mergetype==MERGE_NEAR || mergetype==MERGE_POS_NEAR + ); - if( plrStartOffset(pLeft)plrStartOffset(pRight) ) return 1; + if( !aBuffer ){ + *pnBuffer = 0; + return SQLITE_NOMEM; + } - if( plrEndOffset(pLeft)plrEndOffset(pRight) ) return 1; + /* Read the first docid from each doclist */ + fts3GetDeltaVarint2(&p1, pEnd1, &i1); + fts3GetDeltaVarint2(&p2, pEnd2, &i2); + + switch( mergetype ){ + case MERGE_OR: + case MERGE_POS_OR: + while( p1 || p2 ){ + if( p2 && p1 && i1==i2 ){ + fts3PutDeltaVarint(&p, &iPrev, i1); + if( mergetype==MERGE_POS_OR ) fts3PoslistMerge(&p, &p1, &p2); + fts3GetDeltaVarint2(&p1, pEnd1, &i1); + fts3GetDeltaVarint2(&p2, pEnd2, &i2); + }else if( !p2 || (p1 && i1iType==pRight->iType ); - assert( pLeft->iType==pOut->iType ); - - plrInit(&left, pLeft); - plrInit(&right, pRight); - plwInit(&writer, pOut, dlrDocid(pLeft)); - - while( !plrAtEnd(&left) || !plrAtEnd(&right) ){ - int c = posListCmp(&left, &right); - if( c<0 ){ - plwCopy(&writer, &left); - plrStep(&left); - }else if( c>0 ){ - plwCopy(&writer, &right); - plrStep(&right); - }else{ - plwCopy(&writer, &left); - plrStep(&left); - plrStep(&right); + case MERGE_POS_PHRASE: + case MERGE_PHRASE: { + char **ppPos = (mergetype==MERGE_PHRASE ? 0 : &p); + while( p1 && p2 ){ + if( i1==i2 ){ + char *pSave = p; + sqlite3_int64 iPrevSave = iPrev; + fts3PutDeltaVarint(&p, &iPrev, i1); + if( 0==fts3PoslistPhraseMerge(ppPos, 1, 0, &p1, &p2) ){ + p = pSave; + iPrev = iPrevSave; + } + fts3GetDeltaVarint2(&p1, pEnd1, &i1); + fts3GetDeltaVarint2(&p2, pEnd2, &i2); + }else if( i1dlrDocid(&right) ){ - dlwCopy(&writer, &right); - dlrStep(&right); - }else{ - posListUnion(&left, &right, &writer); - dlrStep(&left); - dlrStep(&right); + if( !fts3PoslistNearMerge(ppPos, aTmp, nParam1, nParam2, &p1, &p2) ){ + iPrev = iPrevSave; + p = pSave; + } + + fts3GetDeltaVarint2(&p1, pEnd1, &i1); + fts3GetDeltaVarint2(&p2, pEnd2, &i2); + }else if( i1iType!=DL_POSITIONS_OFFSETS ); +/* +** This function is used as the sqlite3Fts3SegReaderIterate() callback when +** querying the full-text index for a doclist associated with a term or +** term-prefix. +*/ +static int fts3TermSelectCb( + Fts3Table *p, /* Virtual table object */ + void *pContext, /* Pointer to TermSelect structure */ + char *zTerm, + int nTerm, + char *aDoclist, + int nDoclist +){ + TermSelect *pTS = (TermSelect *)pContext; + int nNew = pTS->nOutput + nDoclist; + char *aNew = sqlite3_malloc(nNew); - plrInit(&left, pLeft); - plrInit(&right, pRight); + UNUSED_PARAMETER(p); + UNUSED_PARAMETER(zTerm); + UNUSED_PARAMETER(nTerm); - while( !plrAtEnd(&left) && !plrAtEnd(&right) ){ - if( plrColumn(&left)plrColumn(&right) ){ - plrStep(&right); - }else if( plrPosition(&left)>=plrPosition(&right) ){ - plrStep(&right); - }else{ - if( (plrPosition(&right)-plrPosition(&left))<=(nNear+1) ){ - if( !match ){ - plwInit(&writer, pOut, dlrDocid(pLeft)); - match = 1; - } - if( !isSaveLeft ){ - plwAdd(&writer, plrColumn(&right), plrPosition(&right), 0, 0); - }else{ - plwAdd(&writer, plrColumn(&left), plrPosition(&left), 0, 0); - } - plrStep(&right); - }else{ - plrStep(&left); - } - } + if( !aNew ){ + return SQLITE_NOMEM; } - if( match ){ - plwTerminate(&writer); - plwDestroy(&writer); + if( pTS->nOutput==0 ){ + /* If this is the first term selected, copy the doclist to the output + ** buffer using memcpy(). TODO: Add a way to transfer control of the + ** aDoclist buffer from the caller so as to avoid the memcpy(). + */ + memcpy(aNew, aDoclist, nDoclist); + }else{ + /* The output buffer is not empty. Merge doclist aDoclist with the + ** existing output. This can only happen with prefix-searches (as + ** searches for exact terms return exactly one doclist). + */ + int mergetype = (pTS->isReqPos ? MERGE_POS_OR : MERGE_OR); + fts3DoclistMerge(mergetype, 0, 0, + aNew, &nNew, pTS->aOutput, pTS->nOutput, aDoclist, nDoclist + ); } - plrDestroy(&left); - plrDestroy(&right); + sqlite3_free(pTS->aOutput); + pTS->aOutput = aNew; + pTS->nOutput = nNew; + + return SQLITE_OK; } /* -** Compare the values pointed to by the PLReaders passed as arguments. -** Return -1 if the value pointed to by pLeft is considered less than -** the value pointed to by pRight, +1 if it is considered greater -** than it, or 0 if it is equal. i.e. +** This function retreives the doclist for the specified term (or term +** prefix) from the database. ** -** (*pLeft - *pRight) -** -** A PLReader that is in the EOF condition is considered greater than -** any other. If neither argument is in EOF state, the return value of -** plrColumn() is used. If the plrColumn() values are equal, the -** comparison is on the basis of plrPosition(). +** The returned doclist may be in one of two formats, depending on the +** value of parameter isReqPos. If isReqPos is zero, then the doclist is +** a sorted list of delta-compressed docids. If isReqPos is non-zero, +** then the returned list is in the same format as is stored in the +** database without the found length specifier at the start of on-disk +** doclists. */ -static int plrCompare(PLReader *pLeft, PLReader *pRight){ - assert(!plrAtEnd(pLeft) || !plrAtEnd(pRight)); +static int fts3TermSelect( + Fts3Table *p, /* Virtual table handle */ + int iColumn, /* Column to query (or -ve for all columns) */ + const char *zTerm, /* Term to query for */ + int nTerm, /* Size of zTerm in bytes */ + int isPrefix, /* True for a prefix search */ + int isReqPos, /* True to include position lists in output */ + int *pnOut, /* OUT: Size of buffer at *ppOut */ + char **ppOut /* OUT: Malloced result buffer */ +){ + int i; + TermSelect tsc; + Fts3SegFilter filter; /* Segment term filter configuration */ + Fts3SegReader **apSegment; /* Array of segments to read data from */ + int nSegment = 0; /* Size of apSegment array */ + int nAlloc = 16; /* Allocated size of segment array */ + int rc; /* Return code */ + sqlite3_stmt *pStmt = 0; /* SQL statement to scan %_segdir table */ + int iAge = 0; /* Used to assign ages to segments */ + + apSegment = (Fts3SegReader **)sqlite3_malloc(sizeof(Fts3SegReader*)*nAlloc); + if( !apSegment ) return SQLITE_NOMEM; + rc = sqlite3Fts3SegReaderPending(p, zTerm, nTerm, isPrefix, &apSegment[0]); + if( rc!=SQLITE_OK ) goto finished; + if( apSegment[0] ){ + nSegment = 1; + } + + /* Loop through the entire %_segdir table. For each segment, create a + ** Fts3SegReader to iterate through the subset of the segment leaves + ** that may contain a term that matches zTerm/nTerm. For non-prefix + ** searches, this is always a single leaf. For prefix searches, this + ** may be a contiguous block of leaves. + ** + ** The code in this loop does not actually load any leaves into memory + ** (unless the root node happens to be a leaf). It simply examines the + ** b-tree structure to determine which leaves need to be inspected. + */ + rc = sqlite3Fts3AllSegdirs(p, &pStmt); + while( rc==SQLITE_OK && SQLITE_ROW==(rc = sqlite3_step(pStmt)) ){ + Fts3SegReader *pNew = 0; + int nRoot = sqlite3_column_bytes(pStmt, 4); + char const *zRoot = sqlite3_column_blob(pStmt, 4); + if( sqlite3_column_int64(pStmt, 1)==0 ){ + /* The entire segment is stored on the root node (which must be a + ** leaf). Do not bother inspecting any data in this case, just + ** create a Fts3SegReader to scan the single leaf. + */ + rc = sqlite3Fts3SegReaderNew(p, iAge, 0, 0, 0, zRoot, nRoot, &pNew); + }else{ + int rc2; /* Return value of sqlite3Fts3ReadBlock() */ + sqlite3_int64 i1; /* Blockid of leaf that may contain zTerm */ + rc = fts3SelectLeaf(p, zTerm, nTerm, zRoot, nRoot, &i1); + if( rc==SQLITE_OK ){ + sqlite3_int64 i2 = sqlite3_column_int64(pStmt, 2); + rc = sqlite3Fts3SegReaderNew(p, iAge, i1, i2, 0, 0, 0, &pNew); + } - if( plrAtEnd(pRight) || plrAtEnd(pLeft) ){ - return (plrAtEnd(pRight) ? -1 : 1); - } - if( plrColumn(pLeft)!=plrColumn(pRight) ){ - return ((plrColumn(pLeft)0) -** and write the results into pOut. -** -** A phrase intersection means that two documents only match -** if pLeft.iPos+1==pRight.iPos. -** -** A NEAR intersection means that two documents only match if -** (abs(pLeft.iPos-pRight.iPos)nColumn ? FTS3_SEGMENT_COLUMN_FILTER : 0); + filter.iCol = iColumn; + filter.zTerm = zTerm; + filter.nTerm = nTerm; + + rc = sqlite3Fts3SegReaderIterate(p, apSegment, nSegment, &filter, + fts3TermSelectCb, (void *)&tsc + ); + + if( rc==SQLITE_OK ){ + *ppOut = tsc.aOutput; + *pnOut = tsc.nOutput; + }else{ + sqlite3_free(tsc.aOutput); + } - dlrDestroy(&left); - dlrDestroy(&right); - dlwDestroy(&writer); +finished: + sqlite3_reset(pStmt); + for(i=0; iiColumn; + int isTermPos = (pPhrase->nToken>1 || isReqPos); - if( nLeft==0 || nRight==0 ) return; + for(ii=0; iinToken; ii++){ + struct PhraseToken *pTok = &pPhrase->aToken[ii]; + char *z = pTok->z; /* Next token of the phrase */ + int n = pTok->n; /* Size of z in bytes */ + int isPrefix = pTok->isPrefix;/* True if token is a prefix */ + char *pList; /* Pointer to token doclist */ + int nList; /* Size of buffer at pList */ - dlrInit(&left, DL_DOCIDS, pLeft, nLeft); - dlrInit(&right, DL_DOCIDS, pRight, nRight); - dlwInit(&writer, DL_DOCIDS, pOut); + rc = fts3TermSelect(p, iCol, z, n, isPrefix, isTermPos, &nList, &pList); + if( rc!=SQLITE_OK ) break; - while( !dlrAtEnd(&left) && !dlrAtEnd(&right) ){ - if( dlrDocid(&left)nToken-1 && !isReqPos ){ + mergetype = MERGE_PHRASE; + } + fts3DoclistMerge(mergetype, 0, 0, pList, &nOut, pOut, nOut, pList, nList); + sqlite3_free(pOut); + pOut = pList; } + assert( nOut==0 || pOut!=0 ); } - dlrDestroy(&left); - dlrDestroy(&right); - dlwDestroy(&writer); + if( rc==SQLITE_OK ){ + *paOut = pOut; + *pnOut = nOut; + }else{ + sqlite3_free(pOut); + } + return rc; } -/* We have two DL_DOCIDS doclists: pLeft and pRight. -** Write the union of these two doclists into pOut as a -** DL_DOCIDS doclist. +/* +** Evaluate the full-text expression pExpr against fts3 table pTab. Store +** the resulting doclist in *paOut and *pnOut. */ -static void docListOrMerge( - const char *pLeft, int nLeft, - const char *pRight, int nRight, - DataBuffer *pOut /* Write the combined doclist here */ +static int evalFts3Expr( + Fts3Table *p, /* Virtual table handle */ + Fts3Expr *pExpr, /* Parsed fts3 expression */ + char **paOut, /* OUT: Pointer to malloc'd result buffer */ + int *pnOut, /* OUT: Size of buffer at *paOut */ + int isReqPos /* Require positions in output buffer */ ){ - DLReader left, right; - DLWriter writer; + int rc = SQLITE_OK; /* Return code */ - if( nLeft==0 ){ - if( nRight!=0 ) dataBufferAppend(pOut, pRight, nRight); - return; - } - if( nRight==0 ){ - dataBufferAppend(pOut, pLeft, nLeft); - return; - } + /* Zero the output parameters. */ + *paOut = 0; + *pnOut = 0; - dlrInit(&left, DL_DOCIDS, pLeft, nLeft); - dlrInit(&right, DL_DOCIDS, pRight, nRight); - dlwInit(&writer, DL_DOCIDS, pOut); - - while( !dlrAtEnd(&left) || !dlrAtEnd(&right) ){ - if( dlrAtEnd(&right) ){ - dlwAdd(&writer, dlrDocid(&left)); - dlrStep(&left); - }else if( dlrAtEnd(&left) ){ - dlwAdd(&writer, dlrDocid(&right)); - dlrStep(&right); - }else if( dlrDocid(&left)eType==FTSQUERY_PHRASE + || pExpr->eType==FTSQUERY_NEAR + || isReqPos==0 + ); + if( pExpr->eType==FTSQUERY_PHRASE ){ + rc = fts3PhraseSelect(p, pExpr->pPhrase, + isReqPos || (pExpr->pParent && pExpr->pParent->eType==FTSQUERY_NEAR), + paOut, pnOut + ); + }else{ + char *aLeft; + char *aRight; + int nLeft; + int nRight; - dlrDestroy(&left); - dlrDestroy(&right); - dlwDestroy(&writer); -} - -/* We have two DL_DOCIDS doclists: pLeft and pRight. -** Write into pOut as DL_DOCIDS doclist containing all documents that -** occur in pLeft but not in pRight. -*/ -static void docListExceptMerge( - const char *pLeft, int nLeft, - const char *pRight, int nRight, - DataBuffer *pOut /* Write the combined doclist here */ -){ - DLReader left, right; - DLWriter writer; - - if( nLeft==0 ) return; - if( nRight==0 ){ - dataBufferAppend(pOut, pLeft, nLeft); - return; - } + if( 0==(rc = evalFts3Expr(p, pExpr->pRight, &aRight, &nRight, isReqPos)) + && 0==(rc = evalFts3Expr(p, pExpr->pLeft, &aLeft, &nLeft, isReqPos)) + ){ + assert( pExpr->eType==FTSQUERY_NEAR || pExpr->eType==FTSQUERY_OR + || pExpr->eType==FTSQUERY_AND || pExpr->eType==FTSQUERY_NOT + ); + switch( pExpr->eType ){ + case FTSQUERY_NEAR: { + Fts3Expr *pLeft; + Fts3Expr *pRight; + int mergetype = isReqPos ? MERGE_POS_NEAR : MERGE_NEAR; + int nParam1; + int nParam2; + char *aBuffer; + + if( pExpr->pParent && pExpr->pParent->eType==FTSQUERY_NEAR ){ + mergetype = MERGE_POS_NEAR; + } + pLeft = pExpr->pLeft; + while( pLeft->eType==FTSQUERY_NEAR ){ + pLeft=pLeft->pRight; + } + pRight = pExpr->pRight; + assert( pRight->eType==FTSQUERY_PHRASE ); + assert( pLeft->eType==FTSQUERY_PHRASE ); - dlrInit(&left, DL_DOCIDS, pLeft, nLeft); - dlrInit(&right, DL_DOCIDS, pRight, nRight); - dlwInit(&writer, DL_DOCIDS, pOut); - - while( !dlrAtEnd(&left) ){ - while( !dlrAtEnd(&right) && dlrDocid(&right)nNear+1; + nParam2 = nParam1+pLeft->pPhrase->nToken+pRight->pPhrase->nToken-2; + aBuffer = sqlite3_malloc(nLeft+nRight+1); + rc = fts3DoclistMerge(mergetype, nParam1, nParam2, aBuffer, + pnOut, aLeft, nLeft, aRight, nRight + ); + if( rc!=SQLITE_OK ){ + sqlite3_free(aBuffer); + }else{ + *paOut = aBuffer; + } + sqlite3_free(aLeft); + break; + } + + case FTSQUERY_OR: { + /* Allocate a buffer for the output. The maximum size is the + ** sum of the sizes of the two input buffers. The +1 term is + ** so that a buffer of zero bytes is never allocated - this can + ** cause fts3DoclistMerge() to incorrectly return SQLITE_NOMEM. + */ + char *aBuffer = sqlite3_malloc(nRight+nLeft+1); + rc = fts3DoclistMerge(MERGE_OR, 0, 0, aBuffer, pnOut, + aLeft, nLeft, aRight, nRight + ); + *paOut = aBuffer; + sqlite3_free(aLeft); + break; + } + + default: { + assert( FTSQUERY_NOT==MERGE_NOT && FTSQUERY_AND==MERGE_AND ); + fts3DoclistMerge(pExpr->eType, 0, 0, aLeft, pnOut, + aLeft, nLeft, aRight, nRight + ); + *paOut = aLeft; + break; + } + } + } + sqlite3_free(aRight); } } - *r++ = '\0'; - assert( r == result + len ); - return result; -} -static int sql_exec(sqlite3 *db, const char *zDb, const char *zName, - const char *zFormat){ - char *zCommand = string_format(zFormat, zDb, zName); - int rc; - FTSTRACE(("FTS3 sql: %s\n", zCommand)); - rc = sqlite3_exec(db, zCommand, NULL, 0, NULL); - sqlite3_free(zCommand); - return rc; -} - -static int sql_prepare(sqlite3 *db, const char *zDb, const char *zName, - sqlite3_stmt **ppStmt, const char *zFormat){ - char *zCommand = string_format(zFormat, zDb, zName); - int rc; - FTSTRACE(("FTS3 prepare: %s\n", zCommand)); - rc = sqlite3_prepare_v2(db, zCommand, -1, ppStmt, NULL); - sqlite3_free(zCommand); return rc; } -/* end utility functions */ - -/* Forward reference */ -typedef struct fulltext_vtab fulltext_vtab; - /* -** An instance of the following structure keeps track of generated -** matching-word offset information and snippets. +** This is the xFilter interface for the virtual table. See +** the virtual table xFilter method documentation for additional +** information. +** +** If idxNum==FTS3_FULLSCAN_SEARCH then do a full table scan against +** the %_content table. +** +** If idxNum==FTS3_DOCID_SEARCH then do a docid lookup for a single entry +** in the %_content table. +** +** If idxNum>=FTS3_FULLTEXT_SEARCH then use the full text index. The +** column on the left-hand side of the MATCH operator is column +** number idxNum-FTS3_FULLTEXT_SEARCH, 0 indexed. argv[0] is the right-hand +** side of the MATCH operator. */ -typedef struct Snippet { - int nMatch; /* Total number of matches */ - int nAlloc; /* Space allocated for aMatch[] */ - struct snippetMatch { /* One entry for each matching term */ - char snStatus; /* Status flag for use while constructing snippets */ - short int iCol; /* The column that contains the match */ - short int iTerm; /* The index in Query.pTerms[] of the matching term */ - int iToken; /* The index of the matching document token */ - short int nByte; /* Number of bytes in the term */ - int iStart; /* The offset to the first character of the term */ - } *aMatch; /* Points to space obtained from malloc */ - char *zOffset; /* Text rendering of aMatch[] */ - int nOffset; /* strlen(zOffset) */ - char *zSnippet; /* Snippet text */ - int nSnippet; /* strlen(zSnippet) */ -} Snippet; - - -typedef enum QueryType { - QUERY_GENERIC, /* table scan */ - QUERY_DOCID, /* lookup by docid */ - QUERY_FULLTEXT /* QUERY_FULLTEXT + [i] is a full-text search for column i*/ -} QueryType; - -typedef enum fulltext_statement { - CONTENT_INSERT_STMT, - CONTENT_SELECT_STMT, - CONTENT_UPDATE_STMT, - CONTENT_DELETE_STMT, - CONTENT_EXISTS_STMT, - - BLOCK_INSERT_STMT, - BLOCK_SELECT_STMT, - BLOCK_DELETE_STMT, - BLOCK_DELETE_ALL_STMT, - - SEGDIR_MAX_INDEX_STMT, - SEGDIR_SET_STMT, - SEGDIR_SELECT_LEVEL_STMT, - SEGDIR_SPAN_STMT, - SEGDIR_DELETE_STMT, - SEGDIR_SELECT_SEGMENT_STMT, - SEGDIR_SELECT_ALL_STMT, - SEGDIR_DELETE_ALL_STMT, - SEGDIR_COUNT_STMT, - - MAX_STMT /* Always at end! */ -} fulltext_statement; - -/* These must exactly match the enum above. */ -/* TODO(shess): Is there some risk that a statement will be used in two -** cursors at once, e.g. if a query joins a virtual table to itself? -** If so perhaps we should move some of these to the cursor object. -*/ -static const char *const fulltext_zStatement[MAX_STMT] = { - /* CONTENT_INSERT */ NULL, /* generated in contentInsertStatement() */ - /* CONTENT_SELECT */ NULL, /* generated in contentSelectStatement() */ - /* CONTENT_UPDATE */ NULL, /* generated in contentUpdateStatement() */ - /* CONTENT_DELETE */ "delete from %_content where docid = ?", - /* CONTENT_EXISTS */ "select docid from %_content limit 1", - - /* BLOCK_INSERT */ - "insert into %_segments (blockid, block) values (null, ?)", - /* BLOCK_SELECT */ "select block from %_segments where blockid = ?", - /* BLOCK_DELETE */ "delete from %_segments where blockid between ? and ?", - /* BLOCK_DELETE_ALL */ "delete from %_segments", - - /* SEGDIR_MAX_INDEX */ "select max(idx) from %_segdir where level = ?", - /* SEGDIR_SET */ "insert into %_segdir values (?, ?, ?, ?, ?, ?)", - /* SEGDIR_SELECT_LEVEL */ - "select start_block, leaves_end_block, root from %_segdir " - " where level = ? order by idx", - /* SEGDIR_SPAN */ - "select min(start_block), max(end_block) from %_segdir " - " where level = ? and start_block <> 0", - /* SEGDIR_DELETE */ "delete from %_segdir where level = ?", - - /* NOTE(shess): The first three results of the following two - ** statements must match. - */ - /* SEGDIR_SELECT_SEGMENT */ - "select start_block, leaves_end_block, root from %_segdir " - " where level = ? and idx = ?", - /* SEGDIR_SELECT_ALL */ - "select start_block, leaves_end_block, root from %_segdir " - " order by level desc, idx asc", - /* SEGDIR_DELETE_ALL */ "delete from %_segdir", - /* SEGDIR_COUNT */ "select count(*), ifnull(max(level),0) from %_segdir", -}; - -/* -** A connection to a fulltext index is an instance of the following -** structure. The xCreate and xConnect methods create an instance -** of this structure and xDestroy and xDisconnect free that instance. -** All other methods receive a pointer to the structure as one of their -** arguments. +/* TODO(shess) Upgrade the cursor initialization and destruction to +** account for fts3FilterMethod() being called multiple times on the +** same cursor. The current solution is very fragile. Apply fix to +** fts3 as appropriate. */ -struct fulltext_vtab { - sqlite3_vtab base; /* Base class used by SQLite core */ - sqlite3 *db; /* The database connection */ - const char *zDb; /* logical database name */ - const char *zName; /* virtual table name */ - int nColumn; /* number of columns in virtual table */ - char **azColumn; /* column names. malloced */ - char **azContentColumn; /* column names in content table; malloced */ - sqlite3_tokenizer *pTokenizer; /* tokenizer for inserts and queries */ - - /* Precompiled statements which we keep as long as the table is - ** open. - */ - sqlite3_stmt *pFulltextStatements[MAX_STMT]; - - /* Precompiled statements used for segment merges. We run a - ** separate select across the leaf level of each tree being merged. - */ - sqlite3_stmt *pLeafSelectStmts[MERGE_COUNT]; - /* The statement used to prepare pLeafSelectStmts. */ -#define LEAF_SELECT \ - "select block from %_segments where blockid between ? and ? order by blockid" - - /* These buffer pending index updates during transactions. - ** nPendingData estimates the memory size of the pending data. It - ** doesn't include the hash-bucket overhead, nor any malloc - ** overhead. When nPendingData exceeds kPendingThreshold, the - ** buffer is flushed even before the transaction closes. - ** pendingTerms stores the data, and is only valid when nPendingData - ** is >=0 (nPendingData<0 means pendingTerms has not been - ** initialized). iPrevDocid is the last docid written, used to make - ** certain we're inserting in sorted order. +static int fts3FilterMethod( + sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ + int idxNum, /* Strategy index */ + const char *idxStr, /* Unused */ + int nVal, /* Number of elements in apVal */ + sqlite3_value **apVal /* Arguments for the indexing scheme */ +){ + const char *azSql[] = { + "SELECT * FROM %Q.'%q_content' WHERE docid = ?", /* non-full-table-scan */ + "SELECT * FROM %Q.'%q_content'", /* full-table-scan */ + }; + int rc; /* Return code */ + char *zSql; /* SQL statement used to access %_content */ + Fts3Table *p = (Fts3Table *)pCursor->pVtab; + Fts3Cursor *pCsr = (Fts3Cursor *)pCursor; + + UNUSED_PARAMETER(idxStr); + UNUSED_PARAMETER(nVal); + + assert( idxNum>=0 && idxNum<=(FTS3_FULLTEXT_SEARCH+p->nColumn) ); + assert( nVal==0 || nVal==1 ); + assert( (nVal==0)==(idxNum==FTS3_FULLSCAN_SEARCH) ); + + /* In case the cursor has been used before, clear it now. */ + sqlite3_finalize(pCsr->pStmt); + sqlite3_free(pCsr->aDoclist); + sqlite3Fts3ExprFree(pCsr->pExpr); + memset(&pCursor[1], 0, sizeof(Fts3Cursor)-sizeof(sqlite3_vtab_cursor)); + + /* Compile a SELECT statement for this cursor. For a full-table-scan, the + ** statement loops through all rows of the %_content table. For a + ** full-text query or docid lookup, the statement retrieves a single + ** row by docid. */ - int nPendingData; -#define kPendingThreshold (1*1024*1024) - sqlite_int64 iPrevDocid; - fts3Hash pendingTerms; -}; + zSql = sqlite3_mprintf(azSql[idxNum==FTS3_FULLSCAN_SEARCH], p->zDb, p->zName); + if( !zSql ){ + rc = SQLITE_NOMEM; + }else{ + rc = sqlite3_prepare_v2(p->db, zSql, -1, &pCsr->pStmt, 0); + sqlite3_free(zSql); + } + if( rc!=SQLITE_OK ) return rc; + pCsr->eSearch = (i16)idxNum; -/* -** When the core wants to do a query, it create a cursor using a -** call to xOpen. This structure is an instance of a cursor. It -** is destroyed by xClose. -*/ -typedef struct fulltext_cursor { - sqlite3_vtab_cursor base; /* Base class used by SQLite core */ - QueryType iCursorType; /* Copy of sqlite3_index_info.idxNum */ - sqlite3_stmt *pStmt; /* Prepared statement in use by the cursor */ - int eof; /* True if at End Of Results */ - Fts3Expr *pExpr; /* Parsed MATCH query string */ - Snippet snippet; /* Cached snippet for the current row */ - int iColumn; /* Column being searched */ - DataBuffer result; /* Doclist results from fulltextQuery */ - DLReader reader; /* Result reader if result not empty */ -} fulltext_cursor; + if( idxNum==FTS3_DOCID_SEARCH ){ + rc = sqlite3_bind_value(pCsr->pStmt, 1, apVal[0]); + }else if( idxNum!=FTS3_FULLSCAN_SEARCH ){ + int iCol = idxNum-FTS3_FULLTEXT_SEARCH; + const char *zQuery = (const char *)sqlite3_value_text(apVal[0]); -static fulltext_vtab *cursor_vtab(fulltext_cursor *c){ - return (fulltext_vtab *) c->base.pVtab; -} + if( zQuery==0 && sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ + return SQLITE_NOMEM; + } -static const sqlite3_module fts3Module; /* forward declaration */ + rc = sqlite3Fts3ExprParse(p->pTokenizer, p->azColumn, p->nColumn, + iCol, zQuery, -1, &pCsr->pExpr + ); + if( rc!=SQLITE_OK ) return rc; -/* Return a dynamically generated statement of the form - * insert into %_content (docid, ...) values (?, ...) - */ -static const char *contentInsertStatement(fulltext_vtab *v){ - StringBuffer sb; - int i; + rc = evalFts3Expr(p, pCsr->pExpr, &pCsr->aDoclist, &pCsr->nDoclist, 0); + pCsr->pNextId = pCsr->aDoclist; + pCsr->iPrevId = 0; + } - initStringBuffer(&sb); - append(&sb, "insert into %_content (docid, "); - appendList(&sb, v->nColumn, v->azContentColumn); - append(&sb, ") values (?"); - for(i=0; inColumn; ++i) - append(&sb, ", ?"); - append(&sb, ")"); - return stringBufferData(&sb); + if( rc!=SQLITE_OK ) return rc; + return fts3NextMethod(pCursor); } -/* Return a dynamically generated statement of the form - * select from %_content where docid = ? - */ -static const char *contentSelectStatement(fulltext_vtab *v){ - StringBuffer sb; - initStringBuffer(&sb); - append(&sb, "SELECT "); - appendList(&sb, v->nColumn, v->azContentColumn); - append(&sb, " FROM %_content WHERE docid = ?"); - return stringBufferData(&sb); +/* +** This is the xEof method of the virtual table. SQLite calls this +** routine to find out if it has reached the end of a result set. +*/ +static int fts3EofMethod(sqlite3_vtab_cursor *pCursor){ + return ((Fts3Cursor *)pCursor)->isEof; } -/* Return a dynamically generated statement of the form - * update %_content set [col_0] = ?, [col_1] = ?, ... - * where docid = ? - */ -static const char *contentUpdateStatement(fulltext_vtab *v){ - StringBuffer sb; - int i; - - initStringBuffer(&sb); - append(&sb, "update %_content set "); - for(i=0; inColumn; ++i) { - if( i>0 ){ - append(&sb, ", "); - } - append(&sb, v->azContentColumn[i]); - append(&sb, " = ?"); +/* +** This is the xRowid method. The SQLite core calls this routine to +** retrieve the rowid for the current row of the result set. fts3 +** exposes %_content.docid as the rowid for the virtual table. The +** rowid should be written to *pRowid. +*/ +static int fts3RowidMethod(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ + Fts3Cursor *pCsr = (Fts3Cursor *) pCursor; + if( pCsr->aDoclist ){ + *pRowid = pCsr->iPrevId; + }else{ + *pRowid = sqlite3_column_int64(pCsr->pStmt, 0); } - append(&sb, " where docid = ?"); - return stringBufferData(&sb); + return SQLITE_OK; } -/* Puts a freshly-prepared statement determined by iStmt in *ppStmt. -** If the indicated statement has never been prepared, it is prepared -** and cached, otherwise the cached version is reset. +/* +** This is the xColumn method, called by SQLite to request a value from +** the row that the supplied cursor currently points to. */ -static int sql_get_statement(fulltext_vtab *v, fulltext_statement iStmt, - sqlite3_stmt **ppStmt){ - assert( iStmtpFulltextStatements[iStmt]==NULL ){ - const char *zStmt; - int rc; - switch( iStmt ){ - case CONTENT_INSERT_STMT: - zStmt = contentInsertStatement(v); break; - case CONTENT_SELECT_STMT: - zStmt = contentSelectStatement(v); break; - case CONTENT_UPDATE_STMT: - zStmt = contentUpdateStatement(v); break; - default: - zStmt = fulltext_zStatement[iStmt]; +static int fts3ColumnMethod( + sqlite3_vtab_cursor *pCursor, /* Cursor to retrieve value from */ + sqlite3_context *pContext, /* Context for sqlite3_result_xxx() calls */ + int iCol /* Index of column to read value from */ +){ + int rc; /* Return Code */ + Fts3Cursor *pCsr = (Fts3Cursor *) pCursor; + Fts3Table *p = (Fts3Table *)pCursor->pVtab; + + /* The column value supplied by SQLite must be in range. */ + assert( iCol>=0 && iCol<=p->nColumn+1 ); + + if( iCol==p->nColumn+1 ){ + /* This call is a request for the "docid" column. Since "docid" is an + ** alias for "rowid", use the xRowid() method to obtain the value. + */ + sqlite3_int64 iRowid; + rc = fts3RowidMethod(pCursor, &iRowid); + sqlite3_result_int64(pContext, iRowid); + }else if( iCol==p->nColumn ){ + /* The extra column whose name is the same as the table. + ** Return a blob which is a pointer to the cursor. + */ + sqlite3_result_blob(pContext, &pCsr, sizeof(pCsr), SQLITE_TRANSIENT); + rc = SQLITE_OK; + }else{ + rc = fts3CursorSeek(0, pCsr); + if( rc==SQLITE_OK ){ + sqlite3_result_value(pContext, sqlite3_column_value(pCsr->pStmt, iCol+1)); } - rc = sql_prepare(v->db, v->zDb, v->zName, &v->pFulltextStatements[iStmt], - zStmt); - if( zStmt != fulltext_zStatement[iStmt]) sqlite3_free((void *) zStmt); - if( rc!=SQLITE_OK ) return rc; - } else { - int rc = sqlite3_reset(v->pFulltextStatements[iStmt]); - if( rc!=SQLITE_OK ) return rc; } - - *ppStmt = v->pFulltextStatements[iStmt]; - return SQLITE_OK; + return rc; } -/* Like sqlite3_step(), but convert SQLITE_DONE to SQLITE_OK and -** SQLITE_ROW to SQLITE_ERROR. Useful for statements like UPDATE, -** where we expect no results. +/* +** This function is the implementation of the xUpdate callback used by +** FTS3 virtual tables. It is invoked by SQLite each time a row is to be +** inserted, updated or deleted. */ -static int sql_single_step(sqlite3_stmt *s){ - int rc = sqlite3_step(s); - return (rc==SQLITE_DONE) ? SQLITE_OK : rc; +static int fts3UpdateMethod( + sqlite3_vtab *pVtab, /* Virtual table handle */ + int nArg, /* Size of argument array */ + sqlite3_value **apVal, /* Array of arguments */ + sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */ +){ + return sqlite3Fts3UpdateMethod(pVtab, nArg, apVal, pRowid); } -/* Like sql_get_statement(), but for special replicated LEAF_SELECT -** statements. idx -1 is a special case for an uncached version of -** the statement (used in the optimize implementation). -*/ -/* TODO(shess) Write version for generic statements and then share -** that between the cached-statement functions. +/* +** Implementation of xSync() method. Flush the contents of the pending-terms +** hash-table to the database. */ -static int sql_get_leaf_statement(fulltext_vtab *v, int idx, - sqlite3_stmt **ppStmt){ - assert( idx>=-1 && idxdb, v->zDb, v->zName, ppStmt, LEAF_SELECT); - }else if( v->pLeafSelectStmts[idx]==NULL ){ - int rc = sql_prepare(v->db, v->zDb, v->zName, &v->pLeafSelectStmts[idx], - LEAF_SELECT); - if( rc!=SQLITE_OK ) return rc; - }else{ - int rc = sqlite3_reset(v->pLeafSelectStmts[idx]); - if( rc!=SQLITE_OK ) return rc; - } +static int fts3SyncMethod(sqlite3_vtab *pVtab){ + return sqlite3Fts3PendingTermsFlush((Fts3Table *)pVtab); +} - *ppStmt = v->pLeafSelectStmts[idx]; +/* +** Implementation of xBegin() method. This is a no-op. +*/ +static int fts3BeginMethod(sqlite3_vtab *pVtab){ + UNUSED_PARAMETER(pVtab); + assert( ((Fts3Table *)pVtab)->nPendingData==0 ); return SQLITE_OK; } -/* insert into %_content (docid, ...) values ([docid], [pValues]) -** If the docid contains SQL NULL, then a unique docid will be -** generated. +/* +** Implementation of xCommit() method. This is a no-op. The contents of +** the pending-terms hash-table have already been flushed into the database +** by fts3SyncMethod(). */ -static int content_insert(fulltext_vtab *v, sqlite3_value *docid, - sqlite3_value **pValues){ - sqlite3_stmt *s; - int i; - int rc = sql_get_statement(v, CONTENT_INSERT_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_value(s, 1, docid); - if( rc!=SQLITE_OK ) return rc; - - for(i=0; inColumn; ++i){ - rc = sqlite3_bind_value(s, 2+i, pValues[i]); - if( rc!=SQLITE_OK ) return rc; - } +static int fts3CommitMethod(sqlite3_vtab *pVtab){ + UNUSED_PARAMETER(pVtab); + assert( ((Fts3Table *)pVtab)->nPendingData==0 ); + return SQLITE_OK; +} - return sql_single_step(s); +/* +** Implementation of xRollback(). Discard the contents of the pending-terms +** hash-table. Any changes made to the database are reverted by SQLite. +*/ +static int fts3RollbackMethod(sqlite3_vtab *pVtab){ + sqlite3Fts3PendingTermsClear((Fts3Table *)pVtab); + return SQLITE_OK; } -/* update %_content set col0 = pValues[0], col1 = pValues[1], ... - * where docid = [iDocid] */ -static int content_update(fulltext_vtab *v, sqlite3_value **pValues, - sqlite_int64 iDocid){ - sqlite3_stmt *s; - int i; - int rc = sql_get_statement(v, CONTENT_UPDATE_STMT, &s); - if( rc!=SQLITE_OK ) return rc; +/* +** Load the doclist associated with expression pExpr to pExpr->aDoclist. +** The loaded doclist contains positions as well as the document ids. +** This is used by the matchinfo(), snippet() and offsets() auxillary +** functions. +*/ +SQLITE_PRIVATE int sqlite3Fts3ExprLoadDoclist(Fts3Table *pTab, Fts3Expr *pExpr){ + return evalFts3Expr(pTab, pExpr, &pExpr->aDoclist, &pExpr->nDoclist, 1); +} - for(i=0; inColumn; ++i){ - rc = sqlite3_bind_value(s, 1+i, pValues[i]); - if( rc!=SQLITE_OK ) return rc; +/* +** After ExprLoadDoclist() (see above) has been called, this function is +** used to iterate through the position lists that make up the doclist +** stored in pExpr->aDoclist. +*/ +SQLITE_PRIVATE char *sqlite3Fts3FindPositions( + Fts3Expr *pExpr, /* Access this expressions doclist */ + sqlite3_int64 iDocid, /* Docid associated with requested pos-list */ + int iCol /* Column of requested pos-list */ +){ + assert( pExpr->isLoaded ); + if( pExpr->aDoclist ){ + char *pEnd = &pExpr->aDoclist[pExpr->nDoclist]; + char *pCsr = pExpr->pCurrent; + + assert( pCsr ); + while( pCsriCurrentiCurrent); + pExpr->pCurrent = pCsr; + }else{ + if( pExpr->iCurrent==iDocid ){ + int iThis = 0; + if( iCol<0 ){ + /* If iCol is negative, return a pointer to the start of the + ** position-list (instead of a pointer to the start of a list + ** of offsets associated with a specific column). + */ + return pCsr; + } + while( iThisnColumn, iDocid); - if( rc!=SQLITE_OK ) return rc; - - return sql_single_step(s); + return 0; } -static void freeStringArray(int nString, const char **pString){ - int i; - - for (i=0 ; i < nString ; ++i) { - if( pString[i]!=NULL ) sqlite3_free((void *) pString[i]); +/* +** Helper function used by the implementation of the overloaded snippet(), +** offsets() and optimize() SQL functions. +** +** If the value passed as the third argument is a blob of size +** sizeof(Fts3Cursor*), then the blob contents are copied to the +** output variable *ppCsr and SQLITE_OK is returned. Otherwise, an error +** message is written to context pContext and SQLITE_ERROR returned. The +** string passed via zFunc is used as part of the error message. +*/ +static int fts3FunctionArg( + sqlite3_context *pContext, /* SQL function call context */ + const char *zFunc, /* Function name */ + sqlite3_value *pVal, /* argv[0] passed to function */ + Fts3Cursor **ppCsr /* OUT: Store cursor handle here */ +){ + Fts3Cursor *pRet; + if( sqlite3_value_type(pVal)!=SQLITE_BLOB + || sqlite3_value_bytes(pVal)!=sizeof(Fts3Cursor *) + ){ + char *zErr = sqlite3_mprintf("illegal first argument to %s", zFunc); + sqlite3_result_error(pContext, zErr, -1); + sqlite3_free(zErr); + return SQLITE_ERROR; } - sqlite3_free((void *) pString); + memcpy(&pRet, sqlite3_value_blob(pVal), sizeof(Fts3Cursor *)); + *ppCsr = pRet; + return SQLITE_OK; } -/* select * from %_content where docid = [iDocid] - * The caller must delete the returned array and all strings in it. - * null fields will be NULL in the returned array. - * - * TODO: Perhaps we should return pointer/length strings here for consistency - * with other code which uses pointer/length. */ -static int content_select(fulltext_vtab *v, sqlite_int64 iDocid, - const char ***pValues){ - sqlite3_stmt *s; - const char **values; - int i; - int rc; - - *pValues = NULL; - - rc = sql_get_statement(v, CONTENT_SELECT_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 1, iDocid); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_step(s); - if( rc!=SQLITE_ROW ) return rc; - - values = (const char **) sqlite3_malloc(v->nColumn * sizeof(const char *)); - for(i=0; inColumn; ++i){ - if( sqlite3_column_type(s, i)==SQLITE_NULL ){ - values[i] = NULL; - }else{ - values[i] = string_dup((char*)sqlite3_column_text(s, i)); - } +/* +** Implementation of the snippet() function for FTS3 +*/ +static void fts3SnippetFunc( + sqlite3_context *pContext, /* SQLite function call context */ + int nVal, /* Size of apVal[] array */ + sqlite3_value **apVal /* Array of arguments */ +){ + Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */ + const char *zStart = ""; + const char *zEnd = ""; + const char *zEllipsis = "..."; + + /* There must be at least one argument passed to this function (otherwise + ** the non-overloaded version would have been called instead of this one). + */ + assert( nVal>=1 ); + + if( nVal>4 ){ + sqlite3_result_error(pContext, + "wrong number of arguments to function snippet()", -1); + return; } + if( fts3FunctionArg(pContext, "snippet", apVal[0], &pCsr) ) return; - /* We expect only one row. We must execute another sqlite3_step() - * to complete the iteration; otherwise the table will remain locked. */ - rc = sqlite3_step(s); - if( rc==SQLITE_DONE ){ - *pValues = values; - return SQLITE_OK; + switch( nVal ){ + case 4: zEllipsis = (const char*)sqlite3_value_text(apVal[3]); + case 3: zEnd = (const char*)sqlite3_value_text(apVal[2]); + case 2: zStart = (const char*)sqlite3_value_text(apVal[1]); + } + if( !zEllipsis || !zEnd || !zStart ){ + sqlite3_result_error_nomem(pContext); + }else if( SQLITE_OK==fts3CursorSeek(pContext, pCsr) ){ + sqlite3Fts3Snippet(pContext, pCsr, zStart, zEnd, zEllipsis); } - - freeStringArray(v->nColumn, values); - return rc; -} - -/* delete from %_content where docid = [iDocid ] */ -static int content_delete(fulltext_vtab *v, sqlite_int64 iDocid){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, CONTENT_DELETE_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 1, iDocid); - if( rc!=SQLITE_OK ) return rc; - - return sql_single_step(s); } -/* Returns SQLITE_ROW if any rows exist in %_content, SQLITE_DONE if -** no rows exist, and any error in case of failure. +/* +** Implementation of the snippet2() function for FTS3 */ -static int content_exists(fulltext_vtab *v){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, CONTENT_EXISTS_STMT, &s); - if( rc!=SQLITE_OK ) return rc; +static void fts3Snippet2Func( + sqlite3_context *pContext, /* SQLite function call context */ + int nVal, /* Size of apVal[] array */ + sqlite3_value **apVal /* Array of arguments */ +){ + Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */ + const char *zStart = ""; + const char *zEnd = ""; + const char *zEllipsis = "..."; + int iCol = -1; + int nToken = 10; + + /* There must be at least one argument passed to this function (otherwise + ** the non-overloaded version would have been called instead of this one). + */ + assert( nVal>=1 ); - rc = sqlite3_step(s); - if( rc!=SQLITE_ROW ) return rc; + if( nVal>6 ){ + sqlite3_result_error(pContext, + "wrong number of arguments to function snippet()", -1); + return; + } + if( fts3FunctionArg(pContext, "snippet", apVal[0], &pCsr) ) return; - /* We expect only one row. We must execute another sqlite3_step() - * to complete the iteration; otherwise the table will remain locked. */ - rc = sqlite3_step(s); - if( rc==SQLITE_DONE ) return SQLITE_ROW; - if( rc==SQLITE_ROW ) return SQLITE_ERROR; - return rc; + switch( nVal ){ + case 6: nToken = sqlite3_value_int(apVal[5]); + case 5: iCol = sqlite3_value_int(apVal[4]); + case 4: zEllipsis = (const char*)sqlite3_value_text(apVal[3]); + case 3: zEnd = (const char*)sqlite3_value_text(apVal[2]); + case 2: zStart = (const char*)sqlite3_value_text(apVal[1]); + } + if( !zEllipsis || !zEnd || !zStart ){ + sqlite3_result_error_nomem(pContext); + }else if( SQLITE_OK==fts3CursorSeek(pContext, pCsr) ){ + sqlite3Fts3Snippet2(pContext, pCsr, zStart, zEnd, zEllipsis, iCol, nToken); + } } -/* insert into %_segments values ([pData]) -** returns assigned blockid in *piBlockid +/* +** Implementation of the offsets() function for FTS3 */ -static int block_insert(fulltext_vtab *v, const char *pData, int nData, - sqlite_int64 *piBlockid){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, BLOCK_INSERT_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_blob(s, 1, pData, nData, SQLITE_STATIC); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_step(s); - if( rc==SQLITE_ROW ) return SQLITE_ERROR; - if( rc!=SQLITE_DONE ) return rc; - - /* blockid column is an alias for rowid. */ - *piBlockid = sqlite3_last_insert_rowid(v->db); - return SQLITE_OK; +static void fts3OffsetsFunc( + sqlite3_context *pContext, /* SQLite function call context */ + int nVal, /* Size of argument array */ + sqlite3_value **apVal /* Array of arguments */ +){ + Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */ + + UNUSED_PARAMETER(nVal); + + assert( nVal==1 ); + if( fts3FunctionArg(pContext, "offsets", apVal[0], &pCsr) ) return; + assert( pCsr ); + if( SQLITE_OK==fts3CursorSeek(pContext, pCsr) ){ + sqlite3Fts3Offsets(pContext, pCsr); + } } -/* delete from %_segments -** where blockid between [iStartBlockid] and [iEndBlockid] +/* +** Implementation of the special optimize() function for FTS3. This +** function merges all segments in the database to a single segment. +** Example usage is: +** +** SELECT optimize(t) FROM t LIMIT 1; ** -** Deletes the range of blocks, inclusive, used to delete the blocks -** which form a segment. +** where 't' is the name of an FTS3 table. */ -static int block_delete(fulltext_vtab *v, - sqlite_int64 iStartBlockid, sqlite_int64 iEndBlockid){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, BLOCK_DELETE_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 1, iStartBlockid); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 2, iEndBlockid); - if( rc!=SQLITE_OK ) return rc; +static void fts3OptimizeFunc( + sqlite3_context *pContext, /* SQLite function call context */ + int nVal, /* Size of argument array */ + sqlite3_value **apVal /* Array of arguments */ +){ + int rc; /* Return code */ + Fts3Table *p; /* Virtual table handle */ + Fts3Cursor *pCursor; /* Cursor handle passed through apVal[0] */ - return sql_single_step(s); -} + UNUSED_PARAMETER(nVal); -/* Returns SQLITE_ROW with *pidx set to the maximum segment idx found -** at iLevel. Returns SQLITE_DONE if there are no segments at -** iLevel. Otherwise returns an error. -*/ -static int segdir_max_index(fulltext_vtab *v, int iLevel, int *pidx){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, SEGDIR_MAX_INDEX_STMT, &s); - if( rc!=SQLITE_OK ) return rc; + assert( nVal==1 ); + if( fts3FunctionArg(pContext, "optimize", apVal[0], &pCursor) ) return; + p = (Fts3Table *)pCursor->base.pVtab; + assert( p ); - rc = sqlite3_bind_int(s, 1, iLevel); - if( rc!=SQLITE_OK ) return rc; + rc = sqlite3Fts3Optimize(p); - rc = sqlite3_step(s); - /* Should always get at least one row due to how max() works. */ - if( rc==SQLITE_DONE ) return SQLITE_DONE; - if( rc!=SQLITE_ROW ) return rc; - - /* NULL means that there were no inputs to max(). */ - if( SQLITE_NULL==sqlite3_column_type(s, 0) ){ - rc = sqlite3_step(s); - if( rc==SQLITE_ROW ) return SQLITE_ERROR; - return rc; + switch( rc ){ + case SQLITE_OK: + sqlite3_result_text(pContext, "Index optimized", -1, SQLITE_STATIC); + break; + case SQLITE_DONE: + sqlite3_result_text(pContext, "Index already optimal", -1, SQLITE_STATIC); + break; + default: + sqlite3_result_error_code(pContext, rc); + break; } - - *pidx = sqlite3_column_int(s, 0); - - /* We expect only one row. We must execute another sqlite3_step() - * to complete the iteration; otherwise the table will remain locked. */ - rc = sqlite3_step(s); - if( rc==SQLITE_ROW ) return SQLITE_ERROR; - if( rc!=SQLITE_DONE ) return rc; - return SQLITE_ROW; -} - -/* insert into %_segdir values ( -** [iLevel], [idx], -** [iStartBlockid], [iLeavesEndBlockid], [iEndBlockid], -** [pRootData] -** ) -*/ -static int segdir_set(fulltext_vtab *v, int iLevel, int idx, - sqlite_int64 iStartBlockid, - sqlite_int64 iLeavesEndBlockid, - sqlite_int64 iEndBlockid, - const char *pRootData, int nRootData){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, SEGDIR_SET_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int(s, 1, iLevel); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int(s, 2, idx); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 3, iStartBlockid); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 4, iLeavesEndBlockid); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 5, iEndBlockid); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_blob(s, 6, pRootData, nRootData, SQLITE_STATIC); - if( rc!=SQLITE_OK ) return rc; - - return sql_single_step(s); } -/* Queries %_segdir for the block span of the segments in level -** iLevel. Returns SQLITE_DONE if there are no blocks for iLevel, -** SQLITE_ROW if there are blocks, else an error. -*/ -static int segdir_span(fulltext_vtab *v, int iLevel, - sqlite_int64 *piStartBlockid, - sqlite_int64 *piEndBlockid){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, SEGDIR_SPAN_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int(s, 1, iLevel); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_step(s); - if( rc==SQLITE_DONE ) return SQLITE_DONE; /* Should never happen */ - if( rc!=SQLITE_ROW ) return rc; - - /* This happens if all segments at this level are entirely inline. */ - if( SQLITE_NULL==sqlite3_column_type(s, 0) ){ - /* We expect only one row. We must execute another sqlite3_step() - * to complete the iteration; otherwise the table will remain locked. */ - int rc2 = sqlite3_step(s); - if( rc2==SQLITE_ROW ) return SQLITE_ERROR; - return rc2; - } - - *piStartBlockid = sqlite3_column_int64(s, 0); - *piEndBlockid = sqlite3_column_int64(s, 1); - - /* We expect only one row. We must execute another sqlite3_step() - * to complete the iteration; otherwise the table will remain locked. */ - rc = sqlite3_step(s); - if( rc==SQLITE_ROW ) return SQLITE_ERROR; - if( rc!=SQLITE_DONE ) return rc; - return SQLITE_ROW; -} - -/* Delete the segment blocks and segment directory records for all -** segments at iLevel. -*/ -static int segdir_delete(fulltext_vtab *v, int iLevel){ - sqlite3_stmt *s; - sqlite_int64 iStartBlockid, iEndBlockid; - int rc = segdir_span(v, iLevel, &iStartBlockid, &iEndBlockid); - if( rc!=SQLITE_ROW && rc!=SQLITE_DONE ) return rc; +/* +** Implementation of the matchinfo() function for FTS3 +*/ +static void fts3MatchinfoFunc( + sqlite3_context *pContext, /* SQLite function call context */ + int nVal, /* Size of argument array */ + sqlite3_value **apVal /* Array of arguments */ +){ + Fts3Cursor *pCsr; /* Cursor handle passed through apVal[0] */ - if( rc==SQLITE_ROW ){ - rc = block_delete(v, iStartBlockid, iEndBlockid); - if( rc!=SQLITE_OK ) return rc; + if( nVal!=1 ){ + sqlite3_result_error(pContext, + "wrong number of arguments to function matchinfo()", -1); + return; } - /* Delete the segment directory itself. */ - rc = sql_get_statement(v, SEGDIR_DELETE_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 1, iLevel); - if( rc!=SQLITE_OK ) return rc; - - return sql_single_step(s); + if( SQLITE_OK==fts3FunctionArg(pContext, "matchinfo", apVal[0], &pCsr) ){ + sqlite3Fts3Matchinfo(pContext, pCsr); + } } -/* Delete entire fts index, SQLITE_OK on success, relevant error on -** failure. +/* +** This routine implements the xFindFunction method for the FTS3 +** virtual table. */ -static int segdir_delete_all(fulltext_vtab *v){ - sqlite3_stmt *s; - int rc = sql_get_statement(v, SEGDIR_DELETE_ALL_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sql_single_step(s); - if( rc!=SQLITE_OK ) return rc; +static int fts3FindFunctionMethod( + sqlite3_vtab *pVtab, /* Virtual table handle */ + int nArg, /* Number of SQL function arguments */ + const char *zName, /* Name of SQL function */ + void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), /* OUT: Result */ + void **ppArg /* Unused */ +){ + struct Overloaded { + const char *zName; + void (*xFunc)(sqlite3_context*,int,sqlite3_value**); + } aOverload[] = { + { "snippet", fts3SnippetFunc }, + { "snippet2", fts3Snippet2Func }, + { "offsets", fts3OffsetsFunc }, + { "optimize", fts3OptimizeFunc }, + { "matchinfo", fts3MatchinfoFunc }, + }; + int i; /* Iterator variable */ - rc = sql_get_statement(v, BLOCK_DELETE_ALL_STMT, &s); - if( rc!=SQLITE_OK ) return rc; + UNUSED_PARAMETER(pVtab); + UNUSED_PARAMETER(nArg); + UNUSED_PARAMETER(ppArg); + + for(i=0; izDb, p->zName, zName + , p->zDb, p->zName, zName + , p->zDb, p->zName, zName + ); + if( zSql ){ + rc = sqlite3_exec(p->db, zSql, 0, 0, 0); + sqlite3_free(zSql); } - if( rc!=SQLITE_ROW ) return rc; + return rc; +} - *pnSegments = sqlite3_column_int(s, 0); - *piMaxLevel = sqlite3_column_int(s, 1); +static const sqlite3_module fts3Module = { + /* iVersion */ 0, + /* xCreate */ fts3CreateMethod, + /* xConnect */ fts3ConnectMethod, + /* xBestIndex */ fts3BestIndexMethod, + /* xDisconnect */ fts3DisconnectMethod, + /* xDestroy */ fts3DestroyMethod, + /* xOpen */ fts3OpenMethod, + /* xClose */ fulltextClose, + /* xFilter */ fts3FilterMethod, + /* xNext */ fts3NextMethod, + /* xEof */ fts3EofMethod, + /* xColumn */ fts3ColumnMethod, + /* xRowid */ fts3RowidMethod, + /* xUpdate */ fts3UpdateMethod, + /* xBegin */ fts3BeginMethod, + /* xSync */ fts3SyncMethod, + /* xCommit */ fts3CommitMethod, + /* xRollback */ fts3RollbackMethod, + /* xFindFunction */ fts3FindFunctionMethod, + /* xRename */ fts3RenameMethod, +}; - /* We expect only one row. We must execute another sqlite3_step() - * to complete the iteration; otherwise the table will remain locked. */ - rc = sqlite3_step(s); - if( rc==SQLITE_DONE ) return SQLITE_OK; - if( rc==SQLITE_ROW ) return SQLITE_ERROR; - return rc; +/* +** This function is registered as the module destructor (called when an +** FTS3 enabled database connection is closed). It frees the memory +** allocated for the tokenizer hash table. +*/ +static void hashDestroy(void *p){ + Fts3Hash *pHash = (Fts3Hash *)p; + sqlite3Fts3HashClear(pHash); + sqlite3_free(pHash); } -/* TODO(shess) clearPendingTerms() is far down the file because -** writeZeroSegment() is far down the file because LeafWriter is far -** down the file. Consider refactoring the code to move the non-vtab -** code above the vtab code so that we don't need this forward -** reference. +/* +** The fts3 built-in tokenizers - "simple" and "porter" - are implemented +** in files fts3_tokenizer1.c and fts3_porter.c respectively. The following +** two forward declarations are for functions declared in these files +** used to retrieve the respective implementations. +** +** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed +** to by the argument to point a the "simple" tokenizer implementation. +** Function ...PorterTokenizerModule() sets *pModule to point to the +** porter tokenizer/stemmer implementation. */ -static int clearPendingTerms(fulltext_vtab *v); +SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule); +SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule); +SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule); /* -** Free the memory used to contain a fulltext_vtab structure. +** Initialise the fts3 extension. If this extension is built as part +** of the sqlite library, then this function is called directly by +** SQLite. If fts3 is built as a dynamically loadable extension, this +** function is called by the sqlite3_extension_init() entry point. */ -static void fulltext_vtab_destroy(fulltext_vtab *v){ - int iStmt, i; +SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){ + int rc = SQLITE_OK; + Fts3Hash *pHash = 0; + const sqlite3_tokenizer_module *pSimple = 0; + const sqlite3_tokenizer_module *pPorter = 0; - FTSTRACE(("FTS3 Destroy %p\n", v)); - for( iStmt=0; iStmtpFulltextStatements[iStmt]!=NULL ){ - sqlite3_finalize(v->pFulltextStatements[iStmt]); - v->pFulltextStatements[iStmt] = NULL; - } +#ifdef SQLITE_ENABLE_ICU + const sqlite3_tokenizer_module *pIcu = 0; + sqlite3Fts3IcuTokenizerModule(&pIcu); +#endif + + sqlite3Fts3SimpleTokenizerModule(&pSimple); + sqlite3Fts3PorterTokenizerModule(&pPorter); + + /* Allocate and initialise the hash-table used to store tokenizers. */ + pHash = sqlite3_malloc(sizeof(Fts3Hash)); + if( !pHash ){ + rc = SQLITE_NOMEM; + }else{ + sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1); } - for( i=0; ipLeafSelectStmts[i]!=NULL ){ - sqlite3_finalize(v->pLeafSelectStmts[i]); - v->pLeafSelectStmts[i] = NULL; + /* Load the built-in tokenizers into the hash table */ + if( rc==SQLITE_OK ){ + if( sqlite3Fts3HashInsert(pHash, "simple", 7, (void *)pSimple) + || sqlite3Fts3HashInsert(pHash, "porter", 7, (void *)pPorter) +#ifdef SQLITE_ENABLE_ICU + || (pIcu && sqlite3Fts3HashInsert(pHash, "icu", 4, (void *)pIcu)) +#endif + ){ + rc = SQLITE_NOMEM; } } - if( v->pTokenizer!=NULL ){ - v->pTokenizer->pModule->xDestroy(v->pTokenizer); - v->pTokenizer = NULL; +#ifdef SQLITE_TEST + if( rc==SQLITE_OK ){ + rc = sqlite3Fts3ExprInitTestInterface(db); } +#endif - clearPendingTerms(v); + /* Create the virtual table wrapper around the hash-table and overload + ** the two scalar functions. If this is successful, register the + ** module with sqlite. + */ + if( SQLITE_OK==rc + && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) + && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1)) + && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet2", -1)) + && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", 1)) + && SQLITE_OK==(rc = sqlite3_overload_function(db, "matchinfo", -1)) + && SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", 1)) + ){ + return sqlite3_create_module_v2( + db, "fts3", &fts3Module, (void *)pHash, hashDestroy + ); + } - sqlite3_free(v->azColumn); - for(i = 0; i < v->nColumn; ++i) { - sqlite3_free(v->azContentColumn[i]); + /* An error has occurred. Delete the hash table and return the error code. */ + assert( rc!=SQLITE_OK ); + if( pHash ){ + sqlite3Fts3HashClear(pHash); + sqlite3_free(pHash); } - sqlite3_free(v->azContentColumn); - sqlite3_free(v); + return rc; +} + +#if !SQLITE_CORE +SQLITE_API int sqlite3_extension_init( + sqlite3 *db, + char **pzErrMsg, + const sqlite3_api_routines *pApi +){ + SQLITE_EXTENSION_INIT2(pApi) + return sqlite3Fts3Init(db); } +#endif +#endif + +/************** End of fts3.c ************************************************/ +/************** Begin file fts3_expr.c ***************************************/ /* -** Token types for parsing the arguments to xConnect or xCreate. +** 2008 Nov 28 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This module contains code that implements a parser for fts3 query strings +** (the right-hand argument to the MATCH operator). Because the supported +** syntax is relatively simple, the whole tokenizer/parser system is +** hand-coded. */ -#define TOKEN_EOF 0 /* End of file */ -#define TOKEN_SPACE 1 /* Any kind of whitespace */ -#define TOKEN_ID 2 /* An identifier */ -#define TOKEN_STRING 3 /* A string literal */ -#define TOKEN_PUNCT 4 /* A single punctuation character */ +#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) /* -** If X is a character that can be used in an identifier then -** ftsIdChar(X) will be true. Otherwise it is false. +** By default, this module parses the legacy syntax that has been +** traditionally used by fts3. Or, if SQLITE_ENABLE_FTS3_PARENTHESIS +** is defined, then it uses the new syntax. The differences between +** the new and the old syntaxes are: ** -** For ASCII, any character with the high-order bit set is -** allowed in an identifier. For 7-bit characters, -** isFtsIdChar[X] must be 1. +** a) The new syntax supports parenthesis. The old does not. ** -** Ticket #1066. the SQL standard does not allow '$' in the -** middle of identfiers. But many SQL implementations do. -** SQLite will allow '$' in identifiers for compatibility. -** But the feature is undocumented. +** b) The new syntax supports the AND and NOT operators. The old does not. +** +** c) The old syntax supports the "-" token qualifier. This is not +** supported by the new syntax (it is replaced by the NOT operator). +** +** d) When using the old syntax, the OR operator has a greater precedence +** than an implicit AND. When using the new, both implicity and explicit +** AND operators have a higher precedence than OR. +** +** If compiled with SQLITE_TEST defined, then this module exports the +** symbol "int sqlite3_fts3_enable_parentheses". Setting this variable +** to zero causes the module to use the old syntax. If it is set to +** non-zero the new syntax is activated. This is so both syntaxes can +** be tested using a single build of testfixture. +** +** The following describes the syntax supported by the fts3 MATCH +** operator in a similar format to that used by the lemon parser +** generator. This module does not use actually lemon, it uses a +** custom parser. +** +** query ::= andexpr (OR andexpr)*. +** +** andexpr ::= notexpr (AND? notexpr)*. +** +** notexpr ::= nearexpr (NOT nearexpr|-TOKEN)*. +** notexpr ::= LP query RP. +** +** nearexpr ::= phrase (NEAR distance_opt nearexpr)*. +** +** distance_opt ::= . +** distance_opt ::= / INTEGER. +** +** phrase ::= TOKEN. +** phrase ::= COLUMN:TOKEN. +** phrase ::= "TOKEN TOKEN TOKEN...". */ -static const char isFtsIdChar[] = { -/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ - 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */ -}; -#define ftsIdChar(C) (((c=C)&0x80)!=0 || (c>0x1f && isFtsIdChar[c-0x20])) +#ifdef SQLITE_TEST +SQLITE_API int sqlite3_fts3_enable_parentheses = 0; +#else +# ifdef SQLITE_ENABLE_FTS3_PARENTHESIS +# define sqlite3_fts3_enable_parentheses 1 +# else +# define sqlite3_fts3_enable_parentheses 0 +# endif +#endif /* -** Return the length of the token that begins at z[0]. -** Store the token type in *tokenType before returning. +** Default span for NEAR operators. */ -static int ftsGetToken(const char *z, int *tokenType){ - int i, c; - switch( *z ){ - case 0: { - *tokenType = TOKEN_EOF; - return 0; - } - case ' ': case '\t': case '\n': case '\f': case '\r': { - for(i=1; safe_isspace(z[i]); i++){} - *tokenType = TOKEN_SPACE; - return i; - } - case '`': - case '\'': - case '"': { - int delim = z[0]; - for(i=1; (c=z[i])!=0; i++){ - if( c==delim ){ - if( z[i+1]==delim ){ - i++; - }else{ - break; - } - } - } - *tokenType = TOKEN_STRING; - return i + (c!=0); - } - case '[': { - for(i=1, c=z[0]; c!=']' && (c=z[i])!=0; i++){} - *tokenType = TOKEN_ID; - return i; - } - default: { - if( !ftsIdChar(*z) ){ - break; - } - for(i=1; ftsIdChar(z[i]); i++){} - *tokenType = TOKEN_ID; - return i; - } - } - *tokenType = TOKEN_PUNCT; - return 1; -} +#define SQLITE_FTS3_DEFAULT_NEAR_PARAM 10 -/* -** A token extracted from a string is an instance of the following -** structure. -*/ -typedef struct FtsToken { - const char *z; /* Pointer to token text. Not '\000' terminated */ - short int n; /* Length of the token text in bytes. */ -} FtsToken; - -/* -** Given a input string (which is really one of the argv[] parameters -** passed into xConnect or xCreate) split the string up into tokens. -** Return an array of pointers to '\000' terminated strings, one string -** for each non-whitespace token. -** -** The returned array is terminated by a single NULL pointer. -** -** Space to hold the returned array is obtained from a single -** malloc and should be freed by passing the return value to free(). -** The individual strings within the token list are all a part of -** the single memory allocation and will all be freed at once. -*/ -static char **tokenizeString(const char *z, int *pnToken){ - int nToken = 0; - FtsToken *aToken = sqlite3_malloc( strlen(z) * sizeof(aToken[0]) ); - int n = 1; - int e, i; - int totalSize = 0; - char **azToken; - char *zCopy; - while( n>0 ){ - n = ftsGetToken(z, &e); - if( e!=TOKEN_SPACE ){ - aToken[nToken].z = z; - aToken[nToken].n = n; - nToken++; - totalSize += n+1; - } - z += n; - } - azToken = (char**)sqlite3_malloc( nToken*sizeof(char*) + totalSize ); - zCopy = (char*)&azToken[nToken]; - nToken--; - for(i=0; i=0 ){ - azIn[j] = azIn[i]; +static int getNextToken( + ParseContext *pParse, /* fts3 query parse context */ + int iCol, /* Value for Fts3Phrase.iColumn */ + const char *z, int n, /* Input string */ + Fts3Expr **ppExpr, /* OUT: expression */ + int *pnConsumed /* OUT: Number of bytes consumed */ +){ + sqlite3_tokenizer *pTokenizer = pParse->pTokenizer; + sqlite3_tokenizer_module const *pModule = pTokenizer->pModule; + int rc; + sqlite3_tokenizer_cursor *pCursor; + Fts3Expr *pRet = 0; + int nConsumed = 0; + + rc = pModule->xOpen(pTokenizer, z, n, &pCursor); + if( rc==SQLITE_OK ){ + const char *zToken; + int nToken, iStart, iEnd, iPosition; + int nByte; /* total space to allocate */ + + pCursor->pTokenizer = pTokenizer; + rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); + + if( rc==SQLITE_OK ){ + nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; + pRet = (Fts3Expr *)sqlite3_malloc(nByte); + if( !pRet ){ + rc = SQLITE_NOMEM; + }else{ + memset(pRet, 0, nByte); + pRet->eType = FTSQUERY_PHRASE; + pRet->pPhrase = (Fts3Phrase *)&pRet[1]; + pRet->pPhrase->nToken = 1; + pRet->pPhrase->iColumn = iCol; + pRet->pPhrase->aToken[0].n = nToken; + pRet->pPhrase->aToken[0].z = (char *)&pRet->pPhrase[1]; + memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken); + + if( iEndpPhrase->aToken[0].isPrefix = 1; + iEnd++; + } + if( !sqlite3_fts3_enable_parentheses && iStart>0 && z[iStart-1]=='-' ){ + pRet->pPhrase->isNot = 1; } - j++; } + nConsumed = iEnd; } - azIn[j] = 0; + + pModule->xClose(pCursor); } + + *pnConsumed = nConsumed; + *ppExpr = pRet; + return rc; } /* -** Find the first alphanumeric token in the string zIn. Null-terminate -** this token. Remove any quotation marks. And return a pointer to -** the result. -*/ -static char *firstToken(char *zIn, char **pzTail){ - int n, ttype; - while(1){ - n = ftsGetToken(zIn, &ttype); - if( ttype==TOKEN_SPACE ){ - zIn += n; - }else if( ttype==TOKEN_EOF ){ - *pzTail = zIn; - return 0; - }else{ - zIn[n] = 0; - *pzTail = &zIn[1]; - dequoteString(zIn); - return zIn; - } - } - /*NOTREACHED*/ -} - -/* Return true if... -** -** * s begins with the string t, ignoring case -** * s is longer than t -** * The first character of s beyond t is not a alphanumeric -** -** Ignore leading space in *s. -** -** To put it another way, return true if the first token of -** s[] is t[]. +** Enlarge a memory allocation. If an out-of-memory allocation occurs, +** then free the old allocation. */ -static int startsWith(const char *s, const char *t){ - while( safe_isspace(*s) ){ s++; } - while( *t ){ - if( safe_tolower(*s++)!=safe_tolower(*t++) ) return 0; +static void *fts3ReallocOrFree(void *pOrig, int nNew){ + void *pRet = sqlite3_realloc(pOrig, nNew); + if( !pRet ){ + sqlite3_free(pOrig); } - return *s!='_' && !safe_isalnum(*s); + return pRet; } /* -** An instance of this structure defines the "spec" of a -** full text index. This structure is populated by parseSpec -** and use by fulltextConnect and fulltextCreate. -*/ -typedef struct TableSpec { - const char *zDb; /* Logical database name */ - const char *zName; /* Name of the full-text index */ - int nColumn; /* Number of columns to be indexed */ - char **azColumn; /* Original names of columns to be indexed */ - char **azContentColumn; /* Column names for %_content */ - char **azTokenizer; /* Name of tokenizer and its arguments */ -} TableSpec; - -/* -** Reclaim all of the memory used by a TableSpec +** Buffer zInput, length nInput, contains the contents of a quoted string +** that appeared as part of an fts3 query expression. Neither quote character +** is included in the buffer. This function attempts to tokenize the entire +** input buffer and create an Fts3Expr structure of type FTSQUERY_PHRASE +** containing the results. +** +** If successful, SQLITE_OK is returned and *ppExpr set to point at the +** allocated Fts3Expr structure. Otherwise, either SQLITE_NOMEM (out of memory +** error) or SQLITE_ERROR (tokenization error) is returned and *ppExpr set +** to 0. */ -static void clearTableSpec(TableSpec *p) { - sqlite3_free(p->azColumn); - sqlite3_free(p->azContentColumn); - sqlite3_free(p->azTokenizer); -} - -/* Parse a CREATE VIRTUAL TABLE statement, which looks like this: - * - * CREATE VIRTUAL TABLE email - * USING fts3(subject, body, tokenize mytokenizer(myarg)) - * - * We return parsed information in a TableSpec structure. - * - */ -static int parseSpec(TableSpec *pSpec, int argc, const char *const*argv, - char**pzErr){ - int i, n; - char *z, *zDummy; - char **azArg; - const char *zTokenizer = 0; /* argv[] entry describing the tokenizer */ - - assert( argc>=3 ); - /* Current interface: - ** argv[0] - module name - ** argv[1] - database name - ** argv[2] - table name - ** argv[3..] - columns, optionally followed by tokenizer specification - ** and snippet delimiters specification. - */ - - /* Make a copy of the complete argv[][] array in a single allocation. - ** The argv[][] array is read-only and transient. We can write to the - ** copy in order to modify things and the copy is persistent. - */ - CLEAR(pSpec); - for(i=n=0; izDb = azArg[1]; - pSpec->zName = azArg[2]; - pSpec->nColumn = 0; - pSpec->azColumn = azArg; - zTokenizer = "tokenize simple"; - for(i=3; inColumn] = firstToken(azArg[i], &zDummy); - pSpec->nColumn++; - } - } - if( pSpec->nColumn==0 ){ - azArg[0] = "content"; - pSpec->nColumn = 1; - } +static int getNextString( + ParseContext *pParse, /* fts3 query parse context */ + const char *zInput, int nInput, /* Input string */ + Fts3Expr **ppExpr /* OUT: expression */ +){ + sqlite3_tokenizer *pTokenizer = pParse->pTokenizer; + sqlite3_tokenizer_module const *pModule = pTokenizer->pModule; + int rc; + Fts3Expr *p = 0; + sqlite3_tokenizer_cursor *pCursor = 0; + char *zTemp = 0; + int nTemp = 0; - /* - ** Construct the list of content column names. - ** - ** Each content column name will be of the form cNNAAAA - ** where NN is the column number and AAAA is the sanitized - ** column name. "sanitized" means that special characters are - ** converted to "_". The cNN prefix guarantees that all column - ** names are unique. - ** - ** The AAAA suffix is not strictly necessary. It is included - ** for the convenience of people who might examine the generated - ** %_content table and wonder what the columns are used for. - */ - pSpec->azContentColumn = sqlite3_malloc( pSpec->nColumn * sizeof(char *) ); - if( pSpec->azContentColumn==0 ){ - clearTableSpec(pSpec); - return SQLITE_NOMEM; + rc = pModule->xOpen(pTokenizer, zInput, nInput, &pCursor); + if( rc==SQLITE_OK ){ + int ii; + pCursor->pTokenizer = pTokenizer; + for(ii=0; rc==SQLITE_OK; ii++){ + const char *zToken; + int nToken, iBegin, iEnd, iPos; + rc = pModule->xNext(pCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos); + if( rc==SQLITE_OK ){ + int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); + p = fts3ReallocOrFree(p, nByte+ii*sizeof(struct PhraseToken)); + zTemp = fts3ReallocOrFree(zTemp, nTemp + nToken); + if( !p || !zTemp ){ + goto no_mem; + } + if( ii==0 ){ + memset(p, 0, nByte); + p->pPhrase = (Fts3Phrase *)&p[1]; + } + p->pPhrase = (Fts3Phrase *)&p[1]; + p->pPhrase->nToken = ii+1; + p->pPhrase->aToken[ii].n = nToken; + memcpy(&zTemp[nTemp], zToken, nToken); + nTemp += nToken; + if( iEndpPhrase->aToken[ii].isPrefix = 1; + }else{ + p->pPhrase->aToken[ii].isPrefix = 0; + } + } + } + + pModule->xClose(pCursor); + pCursor = 0; } - for(i=0; inColumn; i++){ - char *p; - pSpec->azContentColumn[i] = sqlite3_mprintf("c%d%s", i, azArg[i]); - for (p = pSpec->azContentColumn[i]; *p ; ++p) { - if( !safe_isalnum(*p) ) *p = '_'; + + if( rc==SQLITE_DONE ){ + int jj; + char *zNew = NULL; + int nNew = 0; + int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); + nByte += (p?(p->pPhrase->nToken-1):0) * sizeof(struct PhraseToken); + p = fts3ReallocOrFree(p, nByte + nTemp); + if( !p ){ + goto no_mem; + } + if( zTemp ){ + zNew = &(((char *)p)[nByte]); + memcpy(zNew, zTemp, nTemp); + }else{ + memset(p, 0, nByte+nTemp); + } + p->pPhrase = (Fts3Phrase *)&p[1]; + for(jj=0; jjpPhrase->nToken; jj++){ + p->pPhrase->aToken[jj].z = &zNew[nNew]; + nNew += p->pPhrase->aToken[jj].n; } + sqlite3_free(zTemp); + p->eType = FTSQUERY_PHRASE; + p->pPhrase->iColumn = pParse->iDefaultCol; + rc = SQLITE_OK; } - /* - ** Parse the tokenizer specification string. - */ - pSpec->azTokenizer = tokenizeString(zTokenizer, &n); - tokenListToIdList(pSpec->azTokenizer); + *ppExpr = p; + return rc; +no_mem: - return SQLITE_OK; + if( pCursor ){ + pModule->xClose(pCursor); + } + sqlite3_free(zTemp); + sqlite3_free(p); + *ppExpr = 0; + return SQLITE_NOMEM; } /* -** Generate a CREATE TABLE statement that describes the schema of -** the virtual table. Return a pointer to this schema string. -** -** Space is obtained from sqlite3_mprintf() and should be freed -** using sqlite3_free(). +** Function getNextNode(), which is called by fts3ExprParse(), may itself +** call fts3ExprParse(). So this forward declaration is required. */ -static char *fulltextSchema( - int nColumn, /* Number of columns */ - const char *const* azColumn, /* List of columns */ - const char *zTableName /* Name of the table */ -){ - int i; - char *zSchema, *zNext; - const char *zSep = "("; - zSchema = sqlite3_mprintf("CREATE TABLE x"); - for(i=0; ibase */ - v->db = db; - v->zDb = spec->zDb; /* Freed when azColumn is freed */ - v->zName = spec->zName; /* Freed when azColumn is freed */ - v->nColumn = spec->nColumn; - v->azContentColumn = spec->azContentColumn; - spec->azContentColumn = 0; - v->azColumn = spec->azColumn; - spec->azColumn = 0; - - if( spec->azTokenizer==0 ){ - return SQLITE_NOMEM; - } + Fts3Expr *pRet = 0; - zTok = spec->azTokenizer[0]; - if( !zTok ){ - zTok = "simple"; - } - nTok = strlen(zTok)+1; + const char *zInput = z; + int nInput = n; - m = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, zTok, nTok); - if( !m ){ - *pzErr = sqlite3_mprintf("unknown tokenizer: %s", spec->azTokenizer[0]); - rc = SQLITE_ERROR; - goto err; + /* Skip over any whitespace before checking for a keyword, an open or + ** close bracket, or a quoted string. + */ + while( nInput>0 && fts3isspace(*zInput) ){ + nInput--; + zInput++; } - - for(n=0; spec->azTokenizer[n]; n++){} - if( n ){ - rc = m->xCreate(n-1, (const char*const*)&spec->azTokenizer[1], - &v->pTokenizer); - }else{ - rc = m->xCreate(0, 0, &v->pTokenizer); + if( nInput==0 ){ + return SQLITE_DONE; } - if( rc!=SQLITE_OK ) goto err; - v->pTokenizer->pModule = m; - - /* TODO: verify the existence of backing tables foo_content, foo_term */ - - schema = fulltextSchema(v->nColumn, (const char*const*)v->azColumn, - spec->zName); - rc = sqlite3_declare_vtab(db, schema); - sqlite3_free(schema); - if( rc!=SQLITE_OK ) goto err; - - memset(v->pFulltextStatements, 0, sizeof(v->pFulltextStatements)); - - /* Indicate that the buffer is not live. */ - v->nPendingData = -1; - - *ppVTab = &v->base; - FTSTRACE(("FTS3 Connect %p\n", v)); - - return rc; - -err: - fulltext_vtab_destroy(v); - return rc; -} - -static int fulltextConnect( - sqlite3 *db, - void *pAux, - int argc, const char *const*argv, - sqlite3_vtab **ppVTab, - char **pzErr -){ - TableSpec spec; - int rc = parseSpec(&spec, argc, argv, pzErr); - if( rc!=SQLITE_OK ) return rc; - - rc = constructVtab(db, (fts3Hash *)pAux, &spec, ppVTab, pzErr); - clearTableSpec(&spec); - return rc; -} - -/* The %_content table holds the text of each document, with -** the docid column exposed as the SQLite rowid for the table. -*/ -/* TODO(shess) This comment needs elaboration to match the updated -** code. Work it into the top-of-file comment at that time. -*/ -static int fulltextCreate(sqlite3 *db, void *pAux, - int argc, const char * const *argv, - sqlite3_vtab **ppVTab, char **pzErr){ - int rc; - TableSpec spec; - StringBuffer schema; - FTSTRACE(("FTS3 Create\n")); - rc = parseSpec(&spec, argc, argv, pzErr); - if( rc!=SQLITE_OK ) return rc; + /* See if we are dealing with a keyword. */ + for(ii=0; ii<(int)(sizeof(aKeyword)/sizeof(struct Fts3Keyword)); ii++){ + const struct Fts3Keyword *pKey = &aKeyword[ii]; - initStringBuffer(&schema); - append(&schema, "CREATE TABLE %_content("); - append(&schema, " docid INTEGER PRIMARY KEY,"); - appendList(&schema, spec.nColumn, spec.azContentColumn); - append(&schema, ")"); - rc = sql_exec(db, spec.zDb, spec.zName, stringBufferData(&schema)); - stringBufferDestroy(&schema); - if( rc!=SQLITE_OK ) goto out; - - rc = sql_exec(db, spec.zDb, spec.zName, - "create table %_segments(" - " blockid INTEGER PRIMARY KEY," - " block blob" - ");" - ); - if( rc!=SQLITE_OK ) goto out; + if( (pKey->parenOnly & ~sqlite3_fts3_enable_parentheses)!=0 ){ + continue; + } - rc = sql_exec(db, spec.zDb, spec.zName, - "create table %_segdir(" - " level integer," - " idx integer," - " start_block integer," - " leaves_end_block integer," - " end_block integer," - " root blob," - " primary key(level, idx)" - ");"); - if( rc!=SQLITE_OK ) goto out; - - rc = constructVtab(db, (fts3Hash *)pAux, &spec, ppVTab, pzErr); - -out: - clearTableSpec(&spec); - return rc; -} + if( nInput>=pKey->n && 0==memcmp(zInput, pKey->z, pKey->n) ){ + int nNear = SQLITE_FTS3_DEFAULT_NEAR_PARAM; + int nKey = pKey->n; + char cNext; -/* Decide how to handle an SQL query. */ -static int fulltextBestIndex(sqlite3_vtab *pVTab, sqlite3_index_info *pInfo){ - fulltext_vtab *v = (fulltext_vtab *)pVTab; - int i; - FTSTRACE(("FTS3 BestIndex\n")); + /* If this is a "NEAR" keyword, check for an explicit nearness. */ + if( pKey->eType==FTSQUERY_NEAR ){ + assert( nKey==4 ); + if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){ + nNear = 0; + for(nKey=5; zInput[nKey]>='0' && zInput[nKey]<='9'; nKey++){ + nNear = nNear * 10 + (zInput[nKey] - '0'); + } + } + } - for(i=0; inConstraint; ++i){ - const struct sqlite3_index_constraint *pConstraint; - pConstraint = &pInfo->aConstraint[i]; - if( pConstraint->usable ) { - if( (pConstraint->iColumn==-1 || pConstraint->iColumn==v->nColumn+1) && - pConstraint->op==SQLITE_INDEX_CONSTRAINT_EQ ){ - pInfo->idxNum = QUERY_DOCID; /* lookup by docid */ - FTSTRACE(("FTS3 QUERY_DOCID\n")); - } else if( pConstraint->iColumn>=0 && pConstraint->iColumn<=v->nColumn && - pConstraint->op==SQLITE_INDEX_CONSTRAINT_MATCH ){ - /* full-text search */ - pInfo->idxNum = QUERY_FULLTEXT + pConstraint->iColumn; - FTSTRACE(("FTS3 QUERY_FULLTEXT %d\n", pConstraint->iColumn)); - } else continue; - - pInfo->aConstraintUsage[i].argvIndex = 1; - pInfo->aConstraintUsage[i].omit = 1; - - /* An arbitrary value for now. - * TODO: Perhaps docid matches should be considered cheaper than - * full-text searches. */ - pInfo->estimatedCost = 1.0; + /* At this point this is probably a keyword. But for that to be true, + ** the next byte must contain either whitespace, an open or close + ** parenthesis, a quote character, or EOF. + */ + cNext = zInput[nKey]; + if( fts3isspace(cNext) + || cNext=='"' || cNext=='(' || cNext==')' || cNext==0 + ){ + pRet = (Fts3Expr *)sqlite3_malloc(sizeof(Fts3Expr)); + if( !pRet ){ + return SQLITE_NOMEM; + } + memset(pRet, 0, sizeof(Fts3Expr)); + pRet->eType = pKey->eType; + pRet->nNear = nNear; + *ppExpr = pRet; + *pnConsumed = (int)((zInput - z) + nKey); + return SQLITE_OK; + } - return SQLITE_OK; + /* Turns out that wasn't a keyword after all. This happens if the + ** user has supplied a token such as "ORacle". Continue. + */ } } - pInfo->idxNum = QUERY_GENERIC; - return SQLITE_OK; -} - -static int fulltextDisconnect(sqlite3_vtab *pVTab){ - FTSTRACE(("FTS3 Disconnect %p\n", pVTab)); - fulltext_vtab_destroy((fulltext_vtab *)pVTab); - return SQLITE_OK; -} - -static int fulltextDestroy(sqlite3_vtab *pVTab){ - fulltext_vtab *v = (fulltext_vtab *)pVTab; - int rc; - - FTSTRACE(("FTS3 Destroy %p\n", pVTab)); - rc = sql_exec(v->db, v->zDb, v->zName, - "drop table if exists %_content;" - "drop table if exists %_segments;" - "drop table if exists %_segdir;" - ); - if( rc!=SQLITE_OK ) return rc; - - fulltext_vtab_destroy((fulltext_vtab *)pVTab); - return SQLITE_OK; -} - -static int fulltextOpen(sqlite3_vtab *pVTab, sqlite3_vtab_cursor **ppCursor){ - fulltext_cursor *c; - c = (fulltext_cursor *) sqlite3_malloc(sizeof(fulltext_cursor)); - if( c ){ - memset(c, 0, sizeof(fulltext_cursor)); - /* sqlite will initialize c->base */ - *ppCursor = &c->base; - FTSTRACE(("FTS3 Open %p: %p\n", pVTab, c)); - return SQLITE_OK; - }else{ - return SQLITE_NOMEM; + /* Check for an open bracket. */ + if( sqlite3_fts3_enable_parentheses ){ + if( *zInput=='(' ){ + int nConsumed; + int rc; + pParse->nNest++; + rc = fts3ExprParse(pParse, &zInput[1], nInput-1, ppExpr, &nConsumed); + if( rc==SQLITE_OK && !*ppExpr ){ + rc = SQLITE_DONE; + } + *pnConsumed = (int)((zInput - z) + 1 + nConsumed); + return rc; + } + + /* Check for a close bracket. */ + if( *zInput==')' ){ + pParse->nNest--; + *pnConsumed = (int)((zInput - z) + 1); + return SQLITE_DONE; + } } -} - -/* Free all of the dynamically allocated memory held by the -** Snippet -*/ -static void snippetClear(Snippet *p){ - sqlite3_free(p->aMatch); - sqlite3_free(p->zOffset); - sqlite3_free(p->zSnippet); - CLEAR(p); -} -/* -** Append a single entry to the p->aMatch[] log. -*/ -static void snippetAppendMatch( - Snippet *p, /* Append the entry to this snippet */ - int iCol, int iTerm, /* The column and query term */ - int iToken, /* Matching token in document */ - int iStart, int nByte /* Offset and size of the match */ -){ - int i; - struct snippetMatch *pMatch; - if( p->nMatch+1>=p->nAlloc ){ - p->nAlloc = p->nAlloc*2 + 10; - p->aMatch = sqlite3_realloc(p->aMatch, p->nAlloc*sizeof(p->aMatch[0]) ); - if( p->aMatch==0 ){ - p->nMatch = 0; - p->nAlloc = 0; - return; + /* See if we are dealing with a quoted phrase. If this is the case, then + ** search for the closing quote and pass the whole string to getNextString() + ** for processing. This is easy to do, as fts3 has no syntax for escaping + ** a quote character embedded in a string. + */ + if( *zInput=='"' ){ + for(ii=1; iinMatch++; - pMatch = &p->aMatch[i]; - pMatch->iCol = iCol; - pMatch->iTerm = iTerm; - pMatch->iToken = iToken; - pMatch->iStart = iStart; - pMatch->nByte = nByte; -} -/* -** Sizing information for the circular buffer used in snippetOffsetsOfColumn() -*/ -#define FTS3_ROTOR_SZ (32) -#define FTS3_ROTOR_MASK (FTS3_ROTOR_SZ-1) -/* -** Function to iterate through the tokens of a compiled expression. -** -** Except, skip all tokens on the right-hand side of a NOT operator. -** This function is used to find tokens as part of snippet and offset -** generation and we do nt want snippets and offsets to report matches -** for tokens on the RHS of a NOT. -*/ -static int fts3NextExprToken(Fts3Expr **ppExpr, int *piToken){ - Fts3Expr *p = *ppExpr; - int iToken = *piToken; - if( iToken<0 ){ - /* In this case the expression p is the root of an expression tree. - ** Move to the first token in the expression tree. - */ - while( p->pLeft ){ - p = p->pLeft; - } - iToken = 0; - }else{ - assert(p && p->eType==FTSQUERY_PHRASE ); - if( iToken<(p->pPhrase->nToken-1) ){ - iToken++; - }else{ - iToken = 0; - while( p->pParent && p->pParent->pLeft!=p ){ - assert( p->pParent->pRight==p ); - p = p->pParent; - } - p = p->pParent; - if( p ){ - assert( p->pRight!=0 ); - p = p->pRight; - while( p->pLeft ){ - p = p->pLeft; - } - } + /* If control flows to this point, this must be a regular token, or + ** the end of the input. Read a regular token using the sqlite3_tokenizer + ** interface. Before doing so, figure out if there is an explicit + ** column specifier for the token. + ** + ** TODO: Strangely, it is not possible to associate a column specifier + ** with a quoted phrase, only with a single token. Not sure if this was + ** an implementation artifact or an intentional decision when fts3 was + ** first implemented. Whichever it was, this module duplicates the + ** limitation. + */ + iCol = pParse->iDefaultCol; + iColLen = 0; + for(ii=0; iinCol; ii++){ + const char *zStr = pParse->azCol[ii]; + int nStr = (int)strlen(zStr); + if( nInput>nStr && zInput[nStr]==':' + && sqlite3_strnicmp(zStr, zInput, nStr)==0 + ){ + iCol = ii; + iColLen = (int)((zInput - z) + nStr + 1); + break; } } - - *ppExpr = p; - *piToken = iToken; - return p?1:0; + rc = getNextToken(pParse, iCol, &z[iColLen], n-iColLen, ppExpr, pnConsumed); + *pnConsumed += iColLen; + return rc; } /* -** Return TRUE if the expression node pExpr is located beneath the -** RHS of a NOT operator. +** The argument is an Fts3Expr structure for a binary operator (any type +** except an FTSQUERY_PHRASE). Return an integer value representing the +** precedence of the operator. Lower values have a higher precedence (i.e. +** group more tightly). For example, in the C language, the == operator +** groups more tightly than ||, and would therefore have a higher precedence. +** +** When using the new fts3 query syntax (when SQLITE_ENABLE_FTS3_PARENTHESIS +** is defined), the order of the operators in precedence from highest to +** lowest is: +** +** NEAR +** NOT +** AND (including implicit ANDs) +** OR +** +** Note that when using the old query syntax, the OR operator has a higher +** precedence than the AND operator. */ -static int fts3ExprBeneathNot(Fts3Expr *p){ - Fts3Expr *pParent; - while( p ){ - pParent = p->pParent; - if( pParent && pParent->eType==FTSQUERY_NOT && pParent->pRight==p ){ - return 1; - } - p = pParent; +static int opPrecedence(Fts3Expr *p){ + assert( p->eType!=FTSQUERY_PHRASE ); + if( sqlite3_fts3_enable_parentheses ){ + return p->eType; + }else if( p->eType==FTSQUERY_NEAR ){ + return 1; + }else if( p->eType==FTSQUERY_OR ){ + return 2; } - return 0; + assert( p->eType==FTSQUERY_AND ); + return 3; } /* -** Add entries to pSnippet->aMatch[] for every match that occurs against -** document zDoc[0..nDoc-1] which is stored in column iColumn. +** Argument ppHead contains a pointer to the current head of a query +** expression tree being parsed. pPrev is the expression node most recently +** inserted into the tree. This function adds pNew, which is always a binary +** operator node, into the expression tree based on the relative precedence +** of pNew and the existing nodes of the tree. This may result in the head +** of the tree changing, in which case *ppHead is set to the new root node. */ -static void snippetOffsetsOfColumn( - fulltext_cursor *pCur, /* The fulltest search cursor */ - Snippet *pSnippet, /* The Snippet object to be filled in */ - int iColumn, /* Index of fulltext table column */ - const char *zDoc, /* Text of the fulltext table column */ - int nDoc /* Length of zDoc in bytes */ +static void insertBinaryOperator( + Fts3Expr **ppHead, /* Pointer to the root node of a tree */ + Fts3Expr *pPrev, /* Node most recently inserted into the tree */ + Fts3Expr *pNew /* New binary node to insert into expression tree */ ){ - const sqlite3_tokenizer_module *pTModule; /* The tokenizer module */ - sqlite3_tokenizer *pTokenizer; /* The specific tokenizer */ - sqlite3_tokenizer_cursor *pTCursor; /* Tokenizer cursor */ - fulltext_vtab *pVtab; /* The full text index */ - int nColumn; /* Number of columns in the index */ - int i, j; /* Loop counters */ - int rc; /* Return code */ - unsigned int match, prevMatch; /* Phrase search bitmasks */ - const char *zToken; /* Next token from the tokenizer */ - int nToken; /* Size of zToken */ - int iBegin, iEnd, iPos; /* Offsets of beginning and end */ - - /* The following variables keep a circular buffer of the last - ** few tokens */ - unsigned int iRotor = 0; /* Index of current token */ - int iRotorBegin[FTS3_ROTOR_SZ]; /* Beginning offset of token */ - int iRotorLen[FTS3_ROTOR_SZ]; /* Length of token */ - - pVtab = cursor_vtab(pCur); - nColumn = pVtab->nColumn; - pTokenizer = pVtab->pTokenizer; - pTModule = pTokenizer->pModule; - rc = pTModule->xOpen(pTokenizer, zDoc, nDoc, &pTCursor); - if( rc ) return; - pTCursor->pTokenizer = pTokenizer; - - prevMatch = 0; - while( !pTModule->xNext(pTCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos) ){ - Fts3Expr *pIter = pCur->pExpr; - int iIter = -1; - iRotorBegin[iRotor&FTS3_ROTOR_MASK] = iBegin; - iRotorLen[iRotor&FTS3_ROTOR_MASK] = iEnd-iBegin; - match = 0; - for(i=0; i<(FTS3_ROTOR_SZ-1) && fts3NextExprToken(&pIter, &iIter); i++){ - int nPhrase; /* Number of tokens in current phrase */ - struct PhraseToken *pToken; /* Current token */ - int iCol; /* Column index */ + Fts3Expr *pSplit = pPrev; + while( pSplit->pParent && opPrecedence(pSplit->pParent)<=opPrecedence(pNew) ){ + pSplit = pSplit->pParent; + } - if( fts3ExprBeneathNot(pIter) ) continue; - nPhrase = pIter->pPhrase->nToken; - pToken = &pIter->pPhrase->aToken[iIter]; - iCol = pIter->pPhrase->iColumn; - if( iCol>=0 && iColn>nToken ) continue; - if( !pToken->isPrefix && pToken->nn<=nToken ); - if( memcmp(pToken->z, zToken, pToken->n) ) continue; - if( iIter>0 && (prevMatch & (1<=0; j--){ - int k = (iRotor-j) & FTS3_ROTOR_MASK; - snippetAppendMatch(pSnippet, iColumn, i-j, iPos-j, - iRotorBegin[k], iRotorLen[k]); - } - } - } - prevMatch = match<<1; - iRotor++; + if( pSplit->pParent ){ + assert( pSplit->pParent->pRight==pSplit ); + pSplit->pParent->pRight = pNew; + pNew->pParent = pSplit->pParent; + }else{ + *ppHead = pNew; } - pTModule->xClose(pTCursor); + pNew->pLeft = pSplit; + pSplit->pParent = pNew; } /* -** Remove entries from the pSnippet structure to account for the NEAR -** operator. When this is called, pSnippet contains the list of token -** offsets produced by treating all NEAR operators as AND operators. -** This function removes any entries that should not be present after -** accounting for the NEAR restriction. For example, if the queried -** document is: -** -** "A B C D E A" -** -** and the query is: -** -** A NEAR/0 E -** -** then when this function is called the Snippet contains token offsets -** 0, 4 and 5. This function removes the "0" entry (because the first A -** is not near enough to an E). -** -** When this function is called, the value pointed to by parameter piLeft is -** the integer id of the left-most token in the expression tree headed by -** pExpr. This function increments *piLeft by the total number of tokens -** in the expression tree headed by pExpr. +** Parse the fts3 query expression found in buffer z, length n. This function +** returns either when the end of the buffer is reached or an unmatched +** closing bracket - ')' - is encountered. ** -** Return 1 if any trimming occurs. Return 0 if no trimming is required. +** If successful, SQLITE_OK is returned, *ppExpr is set to point to the +** parsed form of the expression and *pnConsumed is set to the number of +** bytes read from buffer z. Otherwise, *ppExpr is set to 0 and SQLITE_NOMEM +** (out of memory error) or SQLITE_ERROR (parse error) is returned. */ -static int trimSnippetOffsets( - Fts3Expr *pExpr, /* The search expression */ - Snippet *pSnippet, /* The set of snippet offsets to be trimmed */ - int *piLeft /* Index of left-most token in pExpr */ +static int fts3ExprParse( + ParseContext *pParse, /* fts3 query parse context */ + const char *z, int n, /* Text of MATCH query */ + Fts3Expr **ppExpr, /* OUT: Parsed query structure */ + int *pnConsumed /* OUT: Number of bytes consumed */ ){ - if( pExpr ){ - if( trimSnippetOffsets(pExpr->pLeft, pSnippet, piLeft) ){ - return 1; - } + Fts3Expr *pRet = 0; + Fts3Expr *pPrev = 0; + Fts3Expr *pNotBranch = 0; /* Only used in legacy parse mode */ + int nIn = n; + const char *zIn = z; + int rc = SQLITE_OK; + int isRequirePhrase = 1; - switch( pExpr->eType ){ - case FTSQUERY_PHRASE: - *piLeft += pExpr->pPhrase->nToken; - break; - case FTSQUERY_NEAR: { - /* The right-hand-side of a NEAR operator is always a phrase. The - ** left-hand-side is either a phrase or an expression tree that is - ** itself headed by a NEAR operator. The following initializations - ** set local variable iLeft to the token number of the left-most - ** token in the right-hand phrase, and iRight to the right most - ** token in the same phrase. For example, if we had: + while( rc==SQLITE_OK ){ + Fts3Expr *p = 0; + int nByte = 0; + rc = getNextNode(pParse, zIn, nIn, &p, &nByte); + if( rc==SQLITE_OK ){ + int isPhrase; + + if( !sqlite3_fts3_enable_parentheses + && p->eType==FTSQUERY_PHRASE && p->pPhrase->isNot + ){ + /* Create an implicit NOT operator. */ + Fts3Expr *pNot = sqlite3_malloc(sizeof(Fts3Expr)); + if( !pNot ){ + sqlite3Fts3ExprFree(p); + rc = SQLITE_NOMEM; + goto exprparse_out; + } + memset(pNot, 0, sizeof(Fts3Expr)); + pNot->eType = FTSQUERY_NOT; + pNot->pRight = p; + if( pNotBranch ){ + pNot->pLeft = pNotBranch; + } + pNotBranch = pNot; + p = pPrev; + }else{ + int eType = p->eType; + assert( eType!=FTSQUERY_PHRASE || !p->pPhrase->isNot ); + isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft); + + /* The isRequirePhrase variable is set to true if a phrase or + ** an expression contained in parenthesis is required. If a + ** binary operator (AND, OR, NOT or NEAR) is encounted when + ** isRequirePhrase is set, this is a syntax error. + */ + if( !isPhrase && isRequirePhrase ){ + sqlite3Fts3ExprFree(p); + rc = SQLITE_ERROR; + goto exprparse_out; + } + + if( isPhrase && !isRequirePhrase ){ + /* Insert an implicit AND operator. */ + Fts3Expr *pAnd; + assert( pRet && pPrev ); + pAnd = sqlite3_malloc(sizeof(Fts3Expr)); + if( !pAnd ){ + sqlite3Fts3ExprFree(p); + rc = SQLITE_NOMEM; + goto exprparse_out; + } + memset(pAnd, 0, sizeof(Fts3Expr)); + pAnd->eType = FTSQUERY_AND; + insertBinaryOperator(&pRet, pPrev, pAnd); + pPrev = pAnd; + } + + /* This test catches attempts to make either operand of a NEAR + ** operator something other than a phrase. For example, either of + ** the following: ** - ** MATCH '"abc def" NEAR/2 "ghi jkl"' + ** (bracketed expression) NEAR phrase + ** phrase NEAR (bracketed expression) ** - ** then iLeft will be set to 2 (token number of ghi) and nToken will - ** be set to 4. + ** Return an error in either case. */ - Fts3Expr *pLeft = pExpr->pLeft; - Fts3Expr *pRight = pExpr->pRight; - int iLeft = *piLeft; - int nNear = pExpr->nNear; - int nToken = pRight->pPhrase->nToken; - int jj, ii; - if( pLeft->eType==FTSQUERY_NEAR ){ - pLeft = pLeft->pRight; + if( pPrev && ( + (eType==FTSQUERY_NEAR && !isPhrase && pPrev->eType!=FTSQUERY_PHRASE) + || (eType!=FTSQUERY_PHRASE && isPhrase && pPrev->eType==FTSQUERY_NEAR) + )){ + sqlite3Fts3ExprFree(p); + rc = SQLITE_ERROR; + goto exprparse_out; } - assert( pRight->eType==FTSQUERY_PHRASE ); - assert( pLeft->eType==FTSQUERY_PHRASE ); - nToken += pLeft->pPhrase->nToken; - - for(ii=0; iinMatch; ii++){ - struct snippetMatch *p = &pSnippet->aMatch[ii]; - if( p->iTerm==iLeft ){ - int isOk = 0; - /* Snippet ii is an occurence of query term iLeft in the document. - ** It occurs at position (p->iToken) of the document. We now - ** search for an instance of token (iLeft-1) somewhere in the - ** range (p->iToken - nNear)...(p->iToken + nNear + nToken) within - ** the set of snippetMatch structures. If one is found, proceed. - ** If one cannot be found, then remove snippets ii..(ii+N-1) - ** from the matching snippets, where N is the number of tokens - ** in phrase pRight->pPhrase. - */ - for(jj=0; isOk==0 && jjnMatch; jj++){ - struct snippetMatch *p2 = &pSnippet->aMatch[jj]; - if( p2->iTerm==(iLeft-1) ){ - if( p2->iToken>=(p->iToken-nNear-1) - && p2->iToken<(p->iToken+nNear+nToken) - ){ - isOk = 1; - } - } - } - if( !isOk ){ - int kk; - for(kk=0; kkpPhrase->nToken; kk++){ - pSnippet->aMatch[kk+ii].iTerm = -2; - } - return 1; - } - } - if( p->iTerm==(iLeft-1) ){ - int isOk = 0; - for(jj=0; isOk==0 && jjnMatch; jj++){ - struct snippetMatch *p2 = &pSnippet->aMatch[jj]; - if( p2->iTerm==iLeft ){ - if( p2->iToken<=(p->iToken+nNear+1) - && p2->iToken>(p->iToken-nNear-nToken) - ){ - isOk = 1; - } - } - } - if( !isOk ){ - int kk; - for(kk=0; kkpPhrase->nToken; kk++){ - pSnippet->aMatch[ii-kk].iTerm = -2; - } - return 1; - } + + if( isPhrase ){ + if( pRet ){ + assert( pPrev && pPrev->pLeft && pPrev->pRight==0 ); + pPrev->pRight = p; + p->pParent = pPrev; + }else{ + pRet = p; } + }else{ + insertBinaryOperator(&pRet, pPrev, p); } - break; + isRequirePhrase = !isPhrase; } + assert( nByte>0 ); } - - if( trimSnippetOffsets(pExpr->pRight, pSnippet, piLeft) ){ - return 1; - } + assert( rc!=SQLITE_OK || (nByte>0 && nByte<=nIn) ); + nIn -= nByte; + zIn += nByte; + pPrev = p; } - return 0; -} - -/* -** Compute all offsets for the current row of the query. -** If the offsets have already been computed, this routine is a no-op. -*/ -static void snippetAllOffsets(fulltext_cursor *p){ - int nColumn; - int iColumn, i; - int iFirst, iLast; - int iTerm = 0; - fulltext_vtab *pFts = cursor_vtab(p); - if( p->snippet.nMatch || p->pExpr==0 ){ - return; - } - nColumn = pFts->nColumn; - iColumn = (p->iCursorType - QUERY_FULLTEXT); - if( iColumn<0 || iColumn>=nColumn ){ - /* Look for matches over all columns of the full-text index */ - iFirst = 0; - iLast = nColumn-1; - }else{ - /* Look for matches in the iColumn-th column of the index only */ - iFirst = iColumn; - iLast = iColumn; - } - for(i=iFirst; i<=iLast; i++){ - const char *zDoc; - int nDoc; - zDoc = (const char*)sqlite3_column_text(p->pStmt, i+1); - nDoc = sqlite3_column_bytes(p->pStmt, i+1); - snippetOffsetsOfColumn(p, &p->snippet, i, zDoc, nDoc); + if( rc==SQLITE_DONE && pRet && isRequirePhrase ){ + rc = SQLITE_ERROR; } - while( trimSnippetOffsets(p->pExpr, &p->snippet, &iTerm) ){ - iTerm = 0; + if( rc==SQLITE_DONE ){ + rc = SQLITE_OK; + if( !sqlite3_fts3_enable_parentheses && pNotBranch ){ + if( !pRet ){ + rc = SQLITE_ERROR; + }else{ + Fts3Expr *pIter = pNotBranch; + while( pIter->pLeft ){ + pIter = pIter->pLeft; + } + pIter->pLeft = pRet; + pRet = pNotBranch; + } + } } -} + *pnConsumed = n - nIn; -/* -** Convert the information in the aMatch[] array of the snippet -** into the string zOffset[0..nOffset-1]. This string is used as -** the return of the SQL offsets() function. -*/ -static void snippetOffsetText(Snippet *p){ - int i; - int cnt = 0; - StringBuffer sb; - char zBuf[200]; - if( p->zOffset ) return; - initStringBuffer(&sb); - for(i=0; inMatch; i++){ - struct snippetMatch *pMatch = &p->aMatch[i]; - if( pMatch->iTerm>=0 ){ - /* If snippetMatch.iTerm is less than 0, then the match was - ** discarded as part of processing the NEAR operator (see the - ** trimSnippetOffsetsForNear() function for details). Ignore - ** it in this case - */ - zBuf[0] = ' '; - sqlite3_snprintf(sizeof(zBuf)-1, &zBuf[cnt>0], "%d %d %d %d", - pMatch->iCol, pMatch->iTerm, pMatch->iStart, pMatch->nByte); - append(&sb, zBuf); - cnt++; - } +exprparse_out: + if( rc!=SQLITE_OK ){ + sqlite3Fts3ExprFree(pRet); + sqlite3Fts3ExprFree(pNotBranch); + pRet = 0; } - p->zOffset = stringBufferData(&sb); - p->nOffset = stringBufferLength(&sb); + *ppExpr = pRet; + return rc; } /* -** zDoc[0..nDoc-1] is phrase of text. aMatch[0..nMatch-1] are a set -** of matching words some of which might be in zDoc. zDoc is column -** number iCol. +** Parameters z and n contain a pointer to and length of a buffer containing +** an fts3 query expression, respectively. This function attempts to parse the +** query expression and create a tree of Fts3Expr structures representing the +** parsed expression. If successful, *ppExpr is set to point to the head +** of the parsed expression tree and SQLITE_OK is returned. If an error +** occurs, either SQLITE_NOMEM (out-of-memory error) or SQLITE_ERROR (parse +** error) is returned and *ppExpr is set to 0. ** -** iBreak is suggested spot in zDoc where we could begin or end an -** excerpt. Return a value similar to iBreak but possibly adjusted -** to be a little left or right so that the break point is better. +** If parameter n is a negative number, then z is assumed to point to a +** nul-terminated string and the length is determined using strlen(). +** +** The first parameter, pTokenizer, is passed the fts3 tokenizer module to +** use to normalize query tokens while parsing the expression. The azCol[] +** array, which is assumed to contain nCol entries, should contain the names +** of each column in the target fts3 table, in order from left to right. +** Column names must be nul-terminated strings. +** +** The iDefaultCol parameter should be passed the index of the table column +** that appears on the left-hand-side of the MATCH operator (the default +** column to match against for tokens for which a column name is not explicitly +** specified as part of the query string), or -1 if tokens may by default +** match any table column. */ -static int wordBoundary( - int iBreak, /* The suggested break point */ - const char *zDoc, /* Document text */ - int nDoc, /* Number of bytes in zDoc[] */ - struct snippetMatch *aMatch, /* Matching words */ - int nMatch, /* Number of entries in aMatch[] */ - int iCol /* The column number for zDoc[] */ +SQLITE_PRIVATE int sqlite3Fts3ExprParse( + sqlite3_tokenizer *pTokenizer, /* Tokenizer module */ + char **azCol, /* Array of column names for fts3 table */ + int nCol, /* Number of entries in azCol[] */ + int iDefaultCol, /* Default column to query */ + const char *z, int n, /* Text of MATCH query */ + Fts3Expr **ppExpr /* OUT: Parsed query structure */ ){ - int i; - if( iBreak<=10 ){ - return 0; + int nParsed; + int rc; + ParseContext sParse; + sParse.pTokenizer = pTokenizer; + sParse.azCol = (const char **)azCol; + sParse.nCol = nCol; + sParse.iDefaultCol = iDefaultCol; + sParse.nNest = 0; + if( z==0 ){ + *ppExpr = 0; + return SQLITE_OK; } - if( iBreak>=nDoc-10 ){ - return nDoc; + if( n<0 ){ + n = (int)strlen(z); } - for(i=0; i0 && aMatch[i-1].iStart+aMatch[i-1].nByte>=iBreak ){ - return aMatch[i-1].iStart; - } + rc = fts3ExprParse(&sParse, z, n, ppExpr, &nParsed); + + /* Check for mismatched parenthesis */ + if( rc==SQLITE_OK && sParse.nNest ){ + rc = SQLITE_ERROR; + sqlite3Fts3ExprFree(*ppExpr); + *ppExpr = 0; } - for(i=1; i<=10; i++){ - if( safe_isspace(zDoc[iBreak-i]) ){ - return iBreak - i + 1; - } - if( safe_isspace(zDoc[iBreak+i]) ){ - return iBreak + i + 1; - } + + return rc; +} + +/* +** Free a parsed fts3 query expression allocated by sqlite3Fts3ExprParse(). +*/ +SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *p){ + if( p ){ + sqlite3Fts3ExprFree(p->pLeft); + sqlite3Fts3ExprFree(p->pRight); + sqlite3_free(p->aDoclist); + sqlite3_free(p); } - return iBreak; } +/**************************************************************************** +***************************************************************************** +** Everything after this point is just test code. +*/ +#ifdef SQLITE_TEST -/* -** Allowed values for Snippet.aMatch[].snStatus -*/ -#define SNIPPET_IGNORE 0 /* It is ok to omit this match from the snippet */ -#define SNIPPET_DESIRED 1 /* We want to include this match in the snippet */ /* -** Generate the text of a snippet. +** Function to query the hash-table of tokenizers (see README.tokenizers). */ -static void snippetText( - fulltext_cursor *pCursor, /* The cursor we need the snippet for */ - const char *zStartMark, /* Markup to appear before each match */ - const char *zEndMark, /* Markup to appear after each match */ - const char *zEllipsis /* Ellipsis mark */ +static int queryTestTokenizer( + sqlite3 *db, + const char *zName, + const sqlite3_tokenizer_module **pp ){ - int i, j; - struct snippetMatch *aMatch; - int nMatch; - int nDesired; - StringBuffer sb; - int tailCol; - int tailOffset; - int iCol; - int nDoc; - const char *zDoc; - int iStart, iEnd; - int tailEllipsis = 0; - int iMatch; - - - sqlite3_free(pCursor->snippet.zSnippet); - pCursor->snippet.zSnippet = 0; - aMatch = pCursor->snippet.aMatch; - nMatch = pCursor->snippet.nMatch; - initStringBuffer(&sb); + int rc; + sqlite3_stmt *pStmt; + const char zSql[] = "SELECT fts3_tokenizer(?)"; - for(i=0; i0; i++){ - if( aMatch[i].snStatus!=SNIPPET_DESIRED ) continue; - nDesired--; - iCol = aMatch[i].iCol; - zDoc = (const char*)sqlite3_column_text(pCursor->pStmt, iCol+1); - nDoc = sqlite3_column_bytes(pCursor->pStmt, iCol+1); - iStart = aMatch[i].iStart - 40; - iStart = wordBoundary(iStart, zDoc, nDoc, aMatch, nMatch, iCol); - if( iStart<=10 ){ - iStart = 0; - } - if( iCol==tailCol && iStart<=tailOffset+20 ){ - iStart = tailOffset; - } - if( (iCol!=tailCol && tailCol>=0) || iStart!=tailOffset ){ - trimWhiteSpace(&sb); - appendWhiteSpace(&sb); - append(&sb, zEllipsis); - appendWhiteSpace(&sb); - } - iEnd = aMatch[i].iStart + aMatch[i].nByte + 40; - iEnd = wordBoundary(iEnd, zDoc, nDoc, aMatch, nMatch, iCol); - if( iEnd>=nDoc-10 ){ - iEnd = nDoc; - tailEllipsis = 0; - }else{ - tailEllipsis = 1; - } - while( iMatchsnippet.zSnippet = stringBufferData(&sb); - pCursor->snippet.nSnippet = stringBufferLength(&sb); -} + return sqlite3_finalize(pStmt); +} /* -** Close the cursor. For additional information see the documentation -** on the xClose method of the virtual table interface. +** This function is part of the test interface for the query parser. It +** writes a text representation of the query expression pExpr into the +** buffer pointed to by argument zBuf. It is assumed that zBuf is large +** enough to store the required text representation. */ -static int fulltextClose(sqlite3_vtab_cursor *pCursor){ - fulltext_cursor *c = (fulltext_cursor *) pCursor; - FTSTRACE(("FTS3 Close %p\n", c)); - sqlite3_finalize(c->pStmt); - sqlite3Fts3ExprFree(c->pExpr); - snippetClear(&c->snippet); - if( c->result.nData!=0 ){ - dlrDestroy(&c->reader); - } - dataBufferDestroy(&c->result); - sqlite3_free(c); - return SQLITE_OK; -} - -static int fulltextNext(sqlite3_vtab_cursor *pCursor){ - fulltext_cursor *c = (fulltext_cursor *) pCursor; - int rc; - - FTSTRACE(("FTS3 Next %p\n", pCursor)); - snippetClear(&c->snippet); - if( c->iCursorType < QUERY_FULLTEXT ){ - /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */ - rc = sqlite3_step(c->pStmt); - switch( rc ){ - case SQLITE_ROW: - c->eof = 0; - return SQLITE_OK; - case SQLITE_DONE: - c->eof = 1; - return SQLITE_OK; - default: - c->eof = 1; - return rc; +static void exprToString(Fts3Expr *pExpr, char *zBuf){ + switch( pExpr->eType ){ + case FTSQUERY_PHRASE: { + Fts3Phrase *pPhrase = pExpr->pPhrase; + int i; + zBuf += sprintf(zBuf, "PHRASE %d %d", pPhrase->iColumn, pPhrase->isNot); + for(i=0; inToken; i++){ + zBuf += sprintf(zBuf," %.*s",pPhrase->aToken[i].n,pPhrase->aToken[i].z); + zBuf += sprintf(zBuf,"%s", (pPhrase->aToken[i].isPrefix?"+":"")); + } + return; } - } else { /* full-text query */ - rc = sqlite3_reset(c->pStmt); - if( rc!=SQLITE_OK ) return rc; - if( c->result.nData==0 || dlrAtEnd(&c->reader) ){ - c->eof = 1; - return SQLITE_OK; - } - rc = sqlite3_bind_int64(c->pStmt, 1, dlrDocid(&c->reader)); - dlrStep(&c->reader); - if( rc!=SQLITE_OK ) return rc; - /* TODO(shess) Handle SQLITE_SCHEMA AND SQLITE_BUSY. */ - rc = sqlite3_step(c->pStmt); - if( rc==SQLITE_ROW ){ /* the case we expect */ - c->eof = 0; - return SQLITE_OK; - } - /* an error occurred; abort */ - return rc==SQLITE_DONE ? SQLITE_ERROR : rc; + case FTSQUERY_NEAR: + zBuf += sprintf(zBuf, "NEAR/%d ", pExpr->nNear); + break; + case FTSQUERY_NOT: + zBuf += sprintf(zBuf, "NOT "); + break; + case FTSQUERY_AND: + zBuf += sprintf(zBuf, "AND "); + break; + case FTSQUERY_OR: + zBuf += sprintf(zBuf, "OR "); + break; } -} + zBuf += sprintf(zBuf, "{"); + exprToString(pExpr->pLeft, zBuf); + zBuf += strlen(zBuf); + zBuf += sprintf(zBuf, "} "); -/* TODO(shess) If we pushed LeafReader to the top of the file, or to -** another file, term_select() could be pushed above -** docListOfTerm(). -*/ -static int termSelect(fulltext_vtab *v, int iColumn, - const char *pTerm, int nTerm, int isPrefix, - DocListType iType, DataBuffer *out); + zBuf += sprintf(zBuf, "{"); + exprToString(pExpr->pRight, zBuf); + zBuf += strlen(zBuf); + zBuf += sprintf(zBuf, "}"); +} -/* -** Return a DocList corresponding to the phrase *pPhrase. +/* +** This is the implementation of a scalar SQL function used to test the +** expression parser. It should be called as follows: ** -** The resulting DL_DOCIDS doclist is stored in pResult, which is -** overwritten. -*/ -static int docListOfPhrase( - fulltext_vtab *pTab, /* The full text index */ - Fts3Phrase *pPhrase, /* Phrase to return a doclist corresponding to */ - DocListType eListType, /* Either DL_DOCIDS or DL_POSITIONS */ - DataBuffer *pResult /* Write the result here */ -){ - int ii; - int rc = SQLITE_OK; - int iCol = pPhrase->iColumn; - DocListType eType = eListType; - assert( eType==DL_POSITIONS || eType==DL_DOCIDS ); - if( pPhrase->nToken>1 ){ - eType = DL_POSITIONS; - } - - /* This code should never be called with buffered updates. */ - assert( pTab->nPendingData<0 ); - - for(ii=0; rc==SQLITE_OK && iinToken; ii++){ - DataBuffer tmp; - struct PhraseToken *p = &pPhrase->aToken[ii]; - rc = termSelect(pTab, iCol, p->z, p->n, p->isPrefix, eType, &tmp); - if( rc==SQLITE_OK ){ - if( ii==0 ){ - *pResult = tmp; - }else{ - DataBuffer res = *pResult; - dataBufferInit(pResult, 0); - if( ii==(pPhrase->nToken-1) ){ - eType = eListType; - } - docListPhraseMerge( - res.pData, res.nData, tmp.pData, tmp.nData, 0, 0, eType, pResult - ); - dataBufferDestroy(&res); - dataBufferDestroy(&tmp); - } - } - } - - return rc; -} - -/* -** Evaluate the full-text expression pExpr against fts3 table pTab. Write -** the results into pRes. +** fts3_exprtest(, , , ...); +** +** The first argument, , is the name of the fts3 tokenizer used +** to parse the query expression (see README.tokenizers). The second argument +** is the query expression to parse. Each subsequent argument is the name +** of a column of the fts3 table that the query expression may refer to. +** For example: +** +** SELECT fts3_exprtest('simple', 'Bill col2:Bloggs', 'col1', 'col2'); */ -static int evalFts3Expr( - fulltext_vtab *pTab, /* Fts3 Virtual table object */ - Fts3Expr *pExpr, /* Parsed fts3 expression */ - DataBuffer *pRes /* OUT: Write results of the expression here */ +static void fts3ExprTest( + sqlite3_context *context, + int argc, + sqlite3_value **argv ){ - int rc = SQLITE_OK; - - /* Initialize the output buffer. If this is an empty query (pExpr==0), - ** this is all that needs to be done. Empty queries produce empty - ** result sets. - */ - dataBufferInit(pRes, 0); + sqlite3_tokenizer_module const *pModule = 0; + sqlite3_tokenizer *pTokenizer = 0; + int rc; + char **azCol = 0; + const char *zExpr; + int nExpr; + int nCol; + int ii; + Fts3Expr *pExpr; + sqlite3 *db = sqlite3_context_db_handle(context); - if( pExpr ){ - if( pExpr->eType==FTSQUERY_PHRASE ){ - DocListType eType = DL_DOCIDS; - if( pExpr->pParent && pExpr->pParent->eType==FTSQUERY_NEAR ){ - eType = DL_POSITIONS; - } - rc = docListOfPhrase(pTab, pExpr->pPhrase, eType, pRes); - }else{ - DataBuffer lhs; - DataBuffer rhs; - - dataBufferInit(&rhs, 0); - if( SQLITE_OK==(rc = evalFts3Expr(pTab, pExpr->pLeft, &lhs)) - && SQLITE_OK==(rc = evalFts3Expr(pTab, pExpr->pRight, &rhs)) - ){ - switch( pExpr->eType ){ - case FTSQUERY_NEAR: { - int nToken; - Fts3Expr *pLeft; - DocListType eType = DL_DOCIDS; - if( pExpr->pParent && pExpr->pParent->eType==FTSQUERY_NEAR ){ - eType = DL_POSITIONS; - } - pLeft = pExpr->pLeft; - while( pLeft->eType==FTSQUERY_NEAR ){ - pLeft=pLeft->pRight; - } - assert( pExpr->pRight->eType==FTSQUERY_PHRASE ); - assert( pLeft->eType==FTSQUERY_PHRASE ); - nToken = pLeft->pPhrase->nToken + pExpr->pRight->pPhrase->nToken; - docListPhraseMerge(lhs.pData, lhs.nData, rhs.pData, rhs.nData, - pExpr->nNear+1, nToken, eType, pRes - ); - break; - } - case FTSQUERY_NOT: { - docListExceptMerge(lhs.pData, lhs.nData, rhs.pData, rhs.nData,pRes); - break; - } - case FTSQUERY_AND: { - docListAndMerge(lhs.pData, lhs.nData, rhs.pData, rhs.nData, pRes); - break; - } - case FTSQUERY_OR: { - docListOrMerge(lhs.pData, lhs.nData, rhs.pData, rhs.nData, pRes); - break; - } - } - } - dataBufferDestroy(&lhs); - dataBufferDestroy(&rhs); - } + if( argc<3 ){ + sqlite3_result_error(context, + "Usage: fts3_exprtest(tokenizer, expr, col1, ...", -1 + ); + return; } - return rc; -} - -/* TODO(shess) Refactor the code to remove this forward decl. */ -static int flushPendingTerms(fulltext_vtab *v); - -/* Perform a full-text query using the search expression in -** zInput[0..nInput-1]. Return a list of matching documents -** in pResult. -** -** Queries must match column iColumn. Or if iColumn>=nColumn -** they are allowed to match against any column. -*/ -static int fulltextQuery( - fulltext_vtab *v, /* The full text index */ - int iColumn, /* Match against this column by default */ - const char *zInput, /* The query string */ - int nInput, /* Number of bytes in zInput[] */ - DataBuffer *pResult, /* Write the result doclist here */ - Fts3Expr **ppExpr /* Put parsed query string here */ -){ - int rc; + rc = queryTestTokenizer(db, + (const char *)sqlite3_value_text(argv[0]), &pModule); + if( rc==SQLITE_NOMEM ){ + sqlite3_result_error_nomem(context); + goto exprtest_out; + }else if( !pModule ){ + sqlite3_result_error(context, "No such tokenizer module", -1); + goto exprtest_out; + } - /* TODO(shess) Instead of flushing pendingTerms, we could query for - ** the relevant term and merge the doclist into what we receive from - ** the database. Wait and see if this is a common issue, first. - ** - ** A good reason not to flush is to not generate update-related - ** error codes from here. - */ + rc = pModule->xCreate(0, 0, &pTokenizer); + assert( rc==SQLITE_NOMEM || rc==SQLITE_OK ); + if( rc==SQLITE_NOMEM ){ + sqlite3_result_error_nomem(context); + goto exprtest_out; + } + pTokenizer->pModule = pModule; - /* Flush any buffered updates before executing the query. */ - rc = flushPendingTerms(v); - if( rc!=SQLITE_OK ){ - return rc; + zExpr = (const char *)sqlite3_value_text(argv[1]); + nExpr = sqlite3_value_bytes(argv[1]); + nCol = argc-2; + azCol = (char **)sqlite3_malloc(nCol*sizeof(char *)); + if( !azCol ){ + sqlite3_result_error_nomem(context); + goto exprtest_out; + } + for(ii=0; iipTokenizer, - v->azColumn, v->nColumn, iColumn, zInput, nInput, ppExpr + rc = sqlite3Fts3ExprParse( + pTokenizer, azCol, nCol, nCol, zExpr, nExpr, &pExpr ); - if( rc!=SQLITE_OK ){ - assert( 0==(*ppExpr) ); - return rc; + if( rc==SQLITE_NOMEM ){ + sqlite3_result_error_nomem(context); + goto exprtest_out; + }else if( rc==SQLITE_OK ){ + char zBuf[4096]; + exprToString(pExpr, zBuf); + sqlite3_result_text(context, zBuf, -1, SQLITE_TRANSIENT); + sqlite3Fts3ExprFree(pExpr); + }else{ + sqlite3_result_error(context, "Error parsing expression", -1); + } + +exprtest_out: + if( pModule && pTokenizer ){ + rc = pModule->xDestroy(pTokenizer); } + sqlite3_free(azCol); +} - return evalFts3Expr(v, *ppExpr, pResult); +/* +** Register the query expression parser test function fts3_exprtest() +** with database connection db. +*/ +SQLITE_PRIVATE int sqlite3Fts3ExprInitTestInterface(sqlite3* db){ + return sqlite3_create_function( + db, "fts3_exprtest", -1, SQLITE_UTF8, 0, fts3ExprTest, 0, 0 + ); } +#endif +#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ + +/************** End of fts3_expr.c *******************************************/ +/************** Begin file fts3_hash.c ***************************************/ /* -** This is the xFilter interface for the virtual table. See -** the virtual table xFilter method documentation for additional -** information. +** 2001 September 22 ** -** If idxNum==QUERY_GENERIC then do a full table scan against -** the %_content table. +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: ** -** If idxNum==QUERY_DOCID then do a docid lookup for a single entry -** in the %_content table. +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. ** -** If idxNum>=QUERY_FULLTEXT then use the full text index. The -** column on the left-hand side of the MATCH operator is column -** number idxNum-QUERY_FULLTEXT, 0 indexed. argv[0] is the right-hand -** side of the MATCH operator. -*/ -/* TODO(shess) Upgrade the cursor initialization and destruction to -** account for fulltextFilter() being called multiple times on the -** same cursor. The current solution is very fragile. Apply fix to -** fts3 as appropriate. +************************************************************************* +** This is the implementation of generic hash-tables used in SQLite. +** We've modified it slightly to serve as a standalone hash table +** implementation for the full-text indexing module. */ -static int fulltextFilter( - sqlite3_vtab_cursor *pCursor, /* The cursor used for this query */ - int idxNum, const char *idxStr, /* Which indexing scheme to use */ - int argc, sqlite3_value **argv /* Arguments for the indexing scheme */ -){ - fulltext_cursor *c = (fulltext_cursor *) pCursor; - fulltext_vtab *v = cursor_vtab(c); - int rc; - - FTSTRACE(("FTS3 Filter %p\n",pCursor)); - - /* If the cursor has a statement that was not prepared according to - ** idxNum, clear it. I believe all calls to fulltextFilter with a - ** given cursor will have the same idxNum , but in this case it's - ** easy to be safe. - */ - if( c->pStmt && c->iCursorType!=idxNum ){ - sqlite3_finalize(c->pStmt); - c->pStmt = NULL; - } - - /* Get a fresh statement appropriate to idxNum. */ - /* TODO(shess): Add a prepared-statement cache in the vt structure. - ** The cache must handle multiple open cursors. Easier to cache the - ** statement variants at the vt to reduce malloc/realloc/free here. - ** Or we could have a StringBuffer variant which allowed stack - ** construction for small values. - */ - if( !c->pStmt ){ - StringBuffer sb; - initStringBuffer(&sb); - append(&sb, "SELECT docid, "); - appendList(&sb, v->nColumn, v->azContentColumn); - append(&sb, " FROM %_content"); - if( idxNum!=QUERY_GENERIC ) append(&sb, " WHERE docid = ?"); - rc = sql_prepare(v->db, v->zDb, v->zName, &c->pStmt, - stringBufferData(&sb)); - stringBufferDestroy(&sb); - if( rc!=SQLITE_OK ) return rc; - c->iCursorType = idxNum; - }else{ - sqlite3_reset(c->pStmt); - assert( c->iCursorType==idxNum ); - } - - switch( idxNum ){ - case QUERY_GENERIC: - break; - case QUERY_DOCID: - rc = sqlite3_bind_int64(c->pStmt, 1, sqlite3_value_int64(argv[0])); - if( rc!=SQLITE_OK ) return rc; - break; - - default: /* full-text search */ - { - int iCol = idxNum-QUERY_FULLTEXT; - const char *zQuery = (const char *)sqlite3_value_text(argv[0]); - assert( idxNum<=QUERY_FULLTEXT+v->nColumn); - assert( argc==1 ); - if( c->result.nData!=0 ){ - /* This case happens if the same cursor is used repeatedly. */ - dlrDestroy(&c->reader); - dataBufferReset(&c->result); - }else{ - dataBufferInit(&c->result, 0); - } - rc = fulltextQuery(v, iCol, zQuery, -1, &c->result, &c->pExpr); - if( rc!=SQLITE_OK ) return rc; - if( c->result.nData!=0 ){ - dlrInit(&c->reader, DL_DOCIDS, c->result.pData, c->result.nData); - } - break; - } - } +/* +** The code in this file is only compiled if: +** +** * The FTS3 module is being built as an extension +** (in which case SQLITE_CORE is not defined), or +** +** * The FTS3 module is being built into the core of +** SQLite (in which case SQLITE_ENABLE_FTS3 is defined). +*/ +#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - return fulltextNext(pCursor); -} -/* This is the xEof method of the virtual table. The SQLite core -** calls this routine to find out if it has reached the end of -** a query's results set. -*/ -static int fulltextEof(sqlite3_vtab_cursor *pCursor){ - fulltext_cursor *c = (fulltext_cursor *) pCursor; - return c->eof; -} -/* This is the xColumn method of the virtual table. The SQLite -** core calls this method during a query when it needs the value -** of a column from the virtual table. This method needs to use -** one of the sqlite3_result_*() routines to store the requested -** value back in the pContext. +/* +** Malloc and Free functions */ -static int fulltextColumn(sqlite3_vtab_cursor *pCursor, - sqlite3_context *pContext, int idxCol){ - fulltext_cursor *c = (fulltext_cursor *) pCursor; - fulltext_vtab *v = cursor_vtab(c); - - if( idxColnColumn ){ - sqlite3_value *pVal = sqlite3_column_value(c->pStmt, idxCol+1); - sqlite3_result_value(pContext, pVal); - }else if( idxCol==v->nColumn ){ - /* The extra column whose name is the same as the table. - ** Return a blob which is a pointer to the cursor - */ - sqlite3_result_blob(pContext, &c, sizeof(c), SQLITE_TRANSIENT); - }else if( idxCol==v->nColumn+1 ){ - /* The docid column, which is an alias for rowid. */ - sqlite3_value *pVal = sqlite3_column_value(c->pStmt, 0); - sqlite3_result_value(pContext, pVal); +static void *fts3HashMalloc(int n){ + void *p = sqlite3_malloc(n); + if( p ){ + memset(p, 0, n); } - return SQLITE_OK; + return p; +} +static void fts3HashFree(void *p){ + sqlite3_free(p); } -/* This is the xRowid method. The SQLite core calls this routine to -** retrieve the rowid for the current row of the result set. fts3 -** exposes %_content.docid as the rowid for the virtual table. The -** rowid should be written to *pRowid. +/* Turn bulk memory into a hash table object by initializing the +** fields of the Hash structure. +** +** "pNew" is a pointer to the hash table that is to be initialized. +** keyClass is one of the constants +** FTS3_HASH_BINARY or FTS3_HASH_STRING. The value of keyClass +** determines what kind of key the hash table will use. "copyKey" is +** true if the hash table should make its own private copy of keys and +** false if it should just use the supplied pointer. */ -static int fulltextRowid(sqlite3_vtab_cursor *pCursor, sqlite_int64 *pRowid){ - fulltext_cursor *c = (fulltext_cursor *) pCursor; - - *pRowid = sqlite3_column_int64(c->pStmt, 0); - return SQLITE_OK; +SQLITE_PRIVATE void sqlite3Fts3HashInit(Fts3Hash *pNew, char keyClass, char copyKey){ + assert( pNew!=0 ); + assert( keyClass>=FTS3_HASH_STRING && keyClass<=FTS3_HASH_BINARY ); + pNew->keyClass = keyClass; + pNew->copyKey = copyKey; + pNew->first = 0; + pNew->count = 0; + pNew->htsize = 0; + pNew->ht = 0; } -/* Add all terms in [zText] to pendingTerms table. If [iColumn] > 0, -** we also store positions and offsets in the hash table using that -** column number. +/* Remove all entries from a hash table. Reclaim all memory. +** Call this routine to delete a hash table or to reset a hash table +** to the empty state. */ -static int buildTerms(fulltext_vtab *v, sqlite_int64 iDocid, - const char *zText, int iColumn){ - sqlite3_tokenizer *pTokenizer = v->pTokenizer; - sqlite3_tokenizer_cursor *pCursor; - const char *pToken; - int nTokenBytes; - int iStartOffset, iEndOffset, iPosition; - int rc; - - rc = pTokenizer->pModule->xOpen(pTokenizer, zText, -1, &pCursor); - if( rc!=SQLITE_OK ) return rc; - - pCursor->pTokenizer = pTokenizer; - while( SQLITE_OK==(rc=pTokenizer->pModule->xNext(pCursor, - &pToken, &nTokenBytes, - &iStartOffset, &iEndOffset, - &iPosition)) ){ - DLCollector *p; - int nData; /* Size of doclist before our update. */ - - /* Positions can't be negative; we use -1 as a terminator - * internally. Token can't be NULL or empty. */ - if( iPosition<0 || pToken == NULL || nTokenBytes == 0 ){ - rc = SQLITE_ERROR; - break; - } - - p = fts3HashFind(&v->pendingTerms, pToken, nTokenBytes); - if( p==NULL ){ - nData = 0; - p = dlcNew(iDocid, DL_DEFAULT); - fts3HashInsert(&v->pendingTerms, pToken, nTokenBytes, p); +SQLITE_PRIVATE void sqlite3Fts3HashClear(Fts3Hash *pH){ + Fts3HashElem *elem; /* For looping over all elements of the table */ - /* Overhead for our hash table entry, the key, and the value. */ - v->nPendingData += sizeof(struct fts3HashElem)+sizeof(*p)+nTokenBytes; - }else{ - nData = p->b.nData; - if( p->dlw.iPrevDocid!=iDocid ) dlcNext(p, iDocid); - } - if( iColumn>=0 ){ - dlcAddPos(p, iColumn, iPosition, iStartOffset, iEndOffset); + assert( pH!=0 ); + elem = pH->first; + pH->first = 0; + fts3HashFree(pH->ht); + pH->ht = 0; + pH->htsize = 0; + while( elem ){ + Fts3HashElem *next_elem = elem->next; + if( pH->copyKey && elem->pKey ){ + fts3HashFree(elem->pKey); } - - /* Accumulate data added by dlcNew or dlcNext, and dlcAddPos. */ - v->nPendingData += p->b.nData-nData; + fts3HashFree(elem); + elem = next_elem; } - - /* TODO(shess) Check return? Should this be able to cause errors at - ** this point? Actually, same question about sqlite3_finalize(), - ** though one could argue that failure there means that the data is - ** not durable. *ponder* - */ - pTokenizer->pModule->xClose(pCursor); - if( SQLITE_DONE == rc ) return SQLITE_OK; - return rc; + pH->count = 0; } -/* Add doclists for all terms in [pValues] to pendingTerms table. */ -static int insertTerms(fulltext_vtab *v, sqlite_int64 iDocid, - sqlite3_value **pValues){ - int i; - for(i = 0; i < v->nColumn ; ++i){ - char *zText = (char*)sqlite3_value_text(pValues[i]); - int rc = buildTerms(v, iDocid, zText, i); - if( rc!=SQLITE_OK ) return rc; +/* +** Hash and comparison functions when the mode is FTS3_HASH_STRING +*/ +static int fts3StrHash(const void *pKey, int nKey){ + const char *z = (const char *)pKey; + int h = 0; + if( nKey<=0 ) nKey = (int) strlen(z); + while( nKey > 0 ){ + h = (h<<3) ^ h ^ *z++; + nKey--; } - return SQLITE_OK; + return h & 0x7fffffff; +} +static int fts3StrCompare(const void *pKey1, int n1, const void *pKey2, int n2){ + if( n1!=n2 ) return 1; + return strncmp((const char*)pKey1,(const char*)pKey2,n1); } -/* Add empty doclists for all terms in the given row's content to -** pendingTerms. +/* +** Hash and comparison functions when the mode is FTS3_HASH_BINARY */ -static int deleteTerms(fulltext_vtab *v, sqlite_int64 iDocid){ - const char **pValues; - int i, rc; - - /* TODO(shess) Should we allow such tables at all? */ - if( DL_DEFAULT==DL_DOCIDS ) return SQLITE_ERROR; - - rc = content_select(v, iDocid, &pValues); - if( rc!=SQLITE_OK ) return rc; - - for(i = 0 ; i < v->nColumn; ++i) { - rc = buildTerms(v, iDocid, pValues[i], -1); - if( rc!=SQLITE_OK ) break; +static int fts3BinHash(const void *pKey, int nKey){ + int h = 0; + const char *z = (const char *)pKey; + while( nKey-- > 0 ){ + h = (h<<3) ^ h ^ *(z++); } - - freeStringArray(v->nColumn, pValues); - return SQLITE_OK; + return h & 0x7fffffff; } - -/* TODO(shess) Refactor the code to remove this forward decl. */ -static int initPendingTerms(fulltext_vtab *v, sqlite_int64 iDocid); - -/* Insert a row into the %_content table; set *piDocid to be the ID of the -** new row. Add doclists for terms to pendingTerms. -*/ -static int index_insert(fulltext_vtab *v, sqlite3_value *pRequestDocid, - sqlite3_value **pValues, sqlite_int64 *piDocid){ - int rc; - - rc = content_insert(v, pRequestDocid, pValues); /* execute an SQL INSERT */ - if( rc!=SQLITE_OK ) return rc; - - /* docid column is an alias for rowid. */ - *piDocid = sqlite3_last_insert_rowid(v->db); - rc = initPendingTerms(v, *piDocid); - if( rc!=SQLITE_OK ) return rc; - - return insertTerms(v, *piDocid, pValues); +static int fts3BinCompare(const void *pKey1, int n1, const void *pKey2, int n2){ + if( n1!=n2 ) return 1; + return memcmp(pKey1,pKey2,n1); } -/* Delete a row from the %_content table; add empty doclists for terms -** to pendingTerms. +/* +** Return a pointer to the appropriate hash function given the key class. +** +** The C syntax in this function definition may be unfamilar to some +** programmers, so we provide the following additional explanation: +** +** The name of the function is "ftsHashFunction". The function takes a +** single parameter "keyClass". The return value of ftsHashFunction() +** is a pointer to another function. Specifically, the return value +** of ftsHashFunction() is a pointer to a function that takes two parameters +** with types "const void*" and "int" and returns an "int". */ -static int index_delete(fulltext_vtab *v, sqlite_int64 iRow){ - int rc = initPendingTerms(v, iRow); - if( rc!=SQLITE_OK ) return rc; - - rc = deleteTerms(v, iRow); - if( rc!=SQLITE_OK ) return rc; - - return content_delete(v, iRow); /* execute an SQL DELETE */ +static int (*ftsHashFunction(int keyClass))(const void*,int){ + if( keyClass==FTS3_HASH_STRING ){ + return &fts3StrHash; + }else{ + assert( keyClass==FTS3_HASH_BINARY ); + return &fts3BinHash; + } } -/* Update a row in the %_content table; add delete doclists to -** pendingTerms for old terms not in the new data, add insert doclists -** to pendingTerms for terms in the new data. +/* +** Return a pointer to the appropriate hash function given the key class. +** +** For help in interpreted the obscure C code in the function definition, +** see the header comment on the previous function. */ -static int index_update(fulltext_vtab *v, sqlite_int64 iRow, - sqlite3_value **pValues){ - int rc = initPendingTerms(v, iRow); - if( rc!=SQLITE_OK ) return rc; - - /* Generate an empty doclist for each term that previously appeared in this - * row. */ - rc = deleteTerms(v, iRow); - if( rc!=SQLITE_OK ) return rc; - - rc = content_update(v, pValues, iRow); /* execute an SQL UPDATE */ - if( rc!=SQLITE_OK ) return rc; - - /* Now add positions for terms which appear in the updated row. */ - return insertTerms(v, iRow, pValues); +static int (*ftsCompareFunction(int keyClass))(const void*,int,const void*,int){ + if( keyClass==FTS3_HASH_STRING ){ + return &fts3StrCompare; + }else{ + assert( keyClass==FTS3_HASH_BINARY ); + return &fts3BinCompare; + } } -/*******************************************************************/ -/* InteriorWriter is used to collect terms and block references into -** interior nodes in %_segments. See commentary at top of file for -** format. +/* Link an element into the hash table */ +static void fts3HashInsertElement( + Fts3Hash *pH, /* The complete hash table */ + struct _fts3ht *pEntry, /* The entry into which pNew is inserted */ + Fts3HashElem *pNew /* The element to be inserted */ +){ + Fts3HashElem *pHead; /* First element already in pEntry */ + pHead = pEntry->chain; + if( pHead ){ + pNew->next = pHead; + pNew->prev = pHead->prev; + if( pHead->prev ){ pHead->prev->next = pNew; } + else { pH->first = pNew; } + pHead->prev = pNew; + }else{ + pNew->next = pH->first; + if( pH->first ){ pH->first->prev = pNew; } + pNew->prev = 0; + pH->first = pNew; + } + pEntry->count++; + pEntry->chain = pNew; +} -/* How large interior nodes can grow. */ -#define INTERIOR_MAX 2048 - -/* Minimum number of terms per interior node (except the root). This -** prevents large terms from making the tree too skinny - must be >0 -** so that the tree always makes progress. Note that the min tree -** fanout will be INTERIOR_MIN_TERMS+1. -*/ -#define INTERIOR_MIN_TERMS 7 -#if INTERIOR_MIN_TERMS<1 -# error INTERIOR_MIN_TERMS must be greater than 0. -#endif -/* ROOT_MAX controls how much data is stored inline in the segment -** directory. +/* Resize the hash table so that it cantains "new_size" buckets. +** "new_size" must be a power of 2. The hash table might fail +** to resize if sqliteMalloc() fails. +** +** Return non-zero if a memory allocation error occurs. */ -/* TODO(shess) Push ROOT_MAX down to whoever is writing things. It's -** only here so that interiorWriterRootInfo() and leafWriterRootInfo() -** can both see it, but if the caller passed it in, we wouldn't even -** need a define. -*/ -#define ROOT_MAX 1024 -#if ROOT_MAXterm, 0); - dataBufferReplace(&block->term, pTerm, nTerm); - - n = fts3PutVarint(c, iHeight); - n += fts3PutVarint(c+n, iChildBlock); - dataBufferInit(&block->data, INTERIOR_MAX); - dataBufferReplace(&block->data, c, n); + assert( (new_size & (new_size-1))==0 ); + new_ht = (struct _fts3ht *)fts3HashMalloc( new_size*sizeof(struct _fts3ht) ); + if( new_ht==0 ) return 1; + fts3HashFree(pH->ht); + pH->ht = new_ht; + pH->htsize = new_size; + xHash = ftsHashFunction(pH->keyClass); + for(elem=pH->first, pH->first=0; elem; elem = next_elem){ + int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1); + next_elem = elem->next; + fts3HashInsertElement(pH, &new_ht[h], elem); } - return block; + return 0; } -#ifndef NDEBUG -/* Verify that the data is readable as an interior node. */ -static void interiorBlockValidate(InteriorBlock *pBlock){ - const char *pData = pBlock->data.pData; - int nData = pBlock->data.nData; - int n, iDummy; - sqlite_int64 iBlockid; - - assert( nData>0 ); - assert( pData!=0 ); - assert( pData+nData>pData ); - - /* Must lead with height of node as a varint(n), n>0 */ - n = fts3GetVarint32(pData, &iDummy); - assert( n>0 ); - assert( iDummy>0 ); - assert( n0 ); - assert( n<=nData ); - pData += n; - nData -= n; - - /* Zero or more terms of positive length */ - if( nData!=0 ){ - /* First term is not delta-encoded. */ - n = fts3GetVarint32(pData, &iDummy); - assert( n>0 ); - assert( iDummy>0 ); - assert( n+iDummy>0); - assert( n+iDummy<=nData ); - pData += n+iDummy; - nData -= n+iDummy; - - /* Following terms delta-encoded. */ - while( nData!=0 ){ - /* Length of shared prefix. */ - n = fts3GetVarint32(pData, &iDummy); - assert( n>0 ); - assert( iDummy>=0 ); - assert( n0 ); - assert( iDummy>0 ); - assert( n+iDummy>0); - assert( n+iDummy<=nData ); - pData += n+iDummy; - nData -= n+iDummy; + if( pH->ht ){ + struct _fts3ht *pEntry = &pH->ht[h]; + elem = pEntry->chain; + count = pEntry->count; + xCompare = ftsCompareFunction(pH->keyClass); + while( count-- && elem ){ + if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){ + return elem; + } + elem = elem->next; } } -} -#define ASSERT_VALID_INTERIOR_BLOCK(x) interiorBlockValidate(x) -#else -#define ASSERT_VALID_INTERIOR_BLOCK(x) assert( 1 ) -#endif - -typedef struct InteriorWriter { - int iHeight; /* from 0 at leaves. */ - InteriorBlock *first, *last; - struct InteriorWriter *parentWriter; - - DataBuffer term; /* Last term written to block "last". */ - sqlite_int64 iOpeningChildBlock; /* First child block in block "last". */ -#ifndef NDEBUG - sqlite_int64 iLastChildBlock; /* for consistency checks. */ -#endif -} InteriorWriter; - -/* Initialize an interior node where pTerm[nTerm] marks the leftmost -** term in the tree. iChildBlock is the leftmost child block at the -** next level down the tree. -*/ -static void interiorWriterInit(int iHeight, const char *pTerm, int nTerm, - sqlite_int64 iChildBlock, - InteriorWriter *pWriter){ - InteriorBlock *block; - assert( iHeight>0 ); - CLEAR(pWriter); - - pWriter->iHeight = iHeight; - pWriter->iOpeningChildBlock = iChildBlock; -#ifndef NDEBUG - pWriter->iLastChildBlock = iChildBlock; -#endif - block = interiorBlockNew(iHeight, iChildBlock, pTerm, nTerm); - pWriter->last = pWriter->first = block; - ASSERT_VALID_INTERIOR_BLOCK(pWriter->last); - dataBufferInit(&pWriter->term, 0); + return 0; } -/* Append the child node rooted at iChildBlock to the interior node, -** with pTerm[nTerm] as the leftmost term in iChildBlock's subtree. +/* Remove a single entry from the hash table given a pointer to that +** element and a hash on the element's key. */ -static void interiorWriterAppend(InteriorWriter *pWriter, - const char *pTerm, int nTerm, - sqlite_int64 iChildBlock){ - char c[VARINT_MAX+VARINT_MAX]; - int n, nPrefix = 0; - - ASSERT_VALID_INTERIOR_BLOCK(pWriter->last); - - /* The first term written into an interior node is actually - ** associated with the second child added (the first child was added - ** in interiorWriterInit, or in the if clause at the bottom of this - ** function). That term gets encoded straight up, with nPrefix left - ** at 0. - */ - if( pWriter->term.nData==0 ){ - n = fts3PutVarint(c, nTerm); +static void fts3RemoveElementByHash( + Fts3Hash *pH, /* The pH containing "elem" */ + Fts3HashElem* elem, /* The element to be removed from the pH */ + int h /* Hash value for the element */ +){ + struct _fts3ht *pEntry; + if( elem->prev ){ + elem->prev->next = elem->next; }else{ - while( nPrefixterm.nData && - pTerm[nPrefix]==pWriter->term.pData[nPrefix] ){ - nPrefix++; - } - - n = fts3PutVarint(c, nPrefix); - n += fts3PutVarint(c+n, nTerm-nPrefix); + pH->first = elem->next; } - -#ifndef NDEBUG - pWriter->iLastChildBlock++; -#endif - assert( pWriter->iLastChildBlock==iChildBlock ); - - /* Overflow to a new block if the new term makes the current block - ** too big, and the current block already has enough terms. - */ - if( pWriter->last->data.nData+n+nTerm-nPrefix>INTERIOR_MAX && - iChildBlock-pWriter->iOpeningChildBlock>INTERIOR_MIN_TERMS ){ - pWriter->last->next = interiorBlockNew(pWriter->iHeight, iChildBlock, - pTerm, nTerm); - pWriter->last = pWriter->last->next; - pWriter->iOpeningChildBlock = iChildBlock; - dataBufferReset(&pWriter->term); - }else{ - dataBufferAppend2(&pWriter->last->data, c, n, - pTerm+nPrefix, nTerm-nPrefix); - dataBufferReplace(&pWriter->term, pTerm, nTerm); + if( elem->next ){ + elem->next->prev = elem->prev; } - ASSERT_VALID_INTERIOR_BLOCK(pWriter->last); -} - -/* Free the space used by pWriter, including the linked-list of -** InteriorBlocks, and parentWriter, if present. -*/ -static int interiorWriterDestroy(InteriorWriter *pWriter){ - InteriorBlock *block = pWriter->first; - - while( block!=NULL ){ - InteriorBlock *b = block; - block = block->next; - dataBufferDestroy(&b->term); - dataBufferDestroy(&b->data); - sqlite3_free(b); + pEntry = &pH->ht[h]; + if( pEntry->chain==elem ){ + pEntry->chain = elem->next; } - if( pWriter->parentWriter!=NULL ){ - interiorWriterDestroy(pWriter->parentWriter); - sqlite3_free(pWriter->parentWriter); + pEntry->count--; + if( pEntry->count<=0 ){ + pEntry->chain = 0; } - dataBufferDestroy(&pWriter->term); - SCRAMBLE(pWriter); - return SQLITE_OK; -} - -/* If pWriter can fit entirely in ROOT_MAX, return it as the root info -** directly, leaving *piEndBlockid unchanged. Otherwise, flush -** pWriter to %_segments, building a new layer of interior nodes, and -** recursively ask for their root into. -*/ -static int interiorWriterRootInfo(fulltext_vtab *v, InteriorWriter *pWriter, - char **ppRootInfo, int *pnRootInfo, - sqlite_int64 *piEndBlockid){ - InteriorBlock *block = pWriter->first; - sqlite_int64 iBlockid = 0; - int rc; - - /* If we can fit the segment inline */ - if( block==pWriter->last && block->data.nDatadata.pData; - *pnRootInfo = block->data.nData; - return SQLITE_OK; + if( pH->copyKey && elem->pKey ){ + fts3HashFree(elem->pKey); } - - /* Flush the first block to %_segments, and create a new level of - ** interior node. - */ - ASSERT_VALID_INTERIOR_BLOCK(block); - rc = block_insert(v, block->data.pData, block->data.nData, &iBlockid); - if( rc!=SQLITE_OK ) return rc; - *piEndBlockid = iBlockid; - - pWriter->parentWriter = sqlite3_malloc(sizeof(*pWriter->parentWriter)); - interiorWriterInit(pWriter->iHeight+1, - block->term.pData, block->term.nData, - iBlockid, pWriter->parentWriter); - - /* Flush additional blocks and append to the higher interior - ** node. - */ - for(block=block->next; block!=NULL; block=block->next){ - ASSERT_VALID_INTERIOR_BLOCK(block); - rc = block_insert(v, block->data.pData, block->data.nData, &iBlockid); - if( rc!=SQLITE_OK ) return rc; - *piEndBlockid = iBlockid; - - interiorWriterAppend(pWriter->parentWriter, - block->term.pData, block->term.nData, iBlockid); + fts3HashFree( elem ); + pH->count--; + if( pH->count<=0 ){ + assert( pH->first==0 ); + assert( pH->count==0 ); + fts3HashClear(pH); } - - /* Parent node gets the chance to be the root. */ - return interiorWriterRootInfo(v, pWriter->parentWriter, - ppRootInfo, pnRootInfo, piEndBlockid); } -/****************************************************************/ -/* InteriorReader is used to read off the data from an interior node -** (see comment at top of file for the format). -*/ -typedef struct InteriorReader { - const char *pData; - int nData; - - DataBuffer term; /* previous term, for decoding term delta. */ - - sqlite_int64 iBlockid; -} InteriorReader; +SQLITE_PRIVATE Fts3HashElem *sqlite3Fts3HashFindElem( + const Fts3Hash *pH, + const void *pKey, + int nKey +){ + int h; /* A hash on key */ + int (*xHash)(const void*,int); /* The hash function */ -static void interiorReaderDestroy(InteriorReader *pReader){ - dataBufferDestroy(&pReader->term); - SCRAMBLE(pReader); + if( pH==0 || pH->ht==0 ) return 0; + xHash = ftsHashFunction(pH->keyClass); + assert( xHash!=0 ); + h = (*xHash)(pKey,nKey); + assert( (pH->htsize & (pH->htsize-1))==0 ); + return fts3FindElementByHash(pH,pKey,nKey, h & (pH->htsize-1)); } -/* TODO(shess) The assertions are great, but what if we're in NDEBUG -** and the blob is empty or otherwise contains suspect data? +/* +** Attempt to locate an element of the hash table pH with a key +** that matches pKey,nKey. Return the data for this element if it is +** found, or NULL if there is no match. */ -static void interiorReaderInit(const char *pData, int nData, - InteriorReader *pReader){ - int n, nTerm; - - /* Require at least the leading flag byte */ - assert( nData>0 ); - assert( pData[0]!='\0' ); +SQLITE_PRIVATE void *sqlite3Fts3HashFind(const Fts3Hash *pH, const void *pKey, int nKey){ + Fts3HashElem *pElem; /* The element that matches key (if any) */ - CLEAR(pReader); - - /* Decode the base blockid, and set the cursor to the first term. */ - n = fts3GetVarint(pData+1, &pReader->iBlockid); - assert( 1+n<=nData ); - pReader->pData = pData+1+n; - pReader->nData = nData-(1+n); - - /* A single-child interior node (such as when a leaf node was too - ** large for the segment directory) won't have any terms. - ** Otherwise, decode the first term. - */ - if( pReader->nData==0 ){ - dataBufferInit(&pReader->term, 0); - }else{ - n = fts3GetVarint32(pReader->pData, &nTerm); - dataBufferInit(&pReader->term, nTerm); - dataBufferReplace(&pReader->term, pReader->pData+n, nTerm); - assert( n+nTerm<=pReader->nData ); - pReader->pData += n+nTerm; - pReader->nData -= n+nTerm; - } -} - -static int interiorReaderAtEnd(InteriorReader *pReader){ - return pReader->term.nData==0; -} - -static sqlite_int64 interiorReaderCurrentBlockid(InteriorReader *pReader){ - return pReader->iBlockid; -} - -static int interiorReaderTermBytes(InteriorReader *pReader){ - assert( !interiorReaderAtEnd(pReader) ); - return pReader->term.nData; -} -static const char *interiorReaderTerm(InteriorReader *pReader){ - assert( !interiorReaderAtEnd(pReader) ); - return pReader->term.pData; + pElem = sqlite3Fts3HashFindElem(pH, pKey, nKey); + return pElem ? pElem->data : 0; } -/* Step forward to the next term in the node. */ -static void interiorReaderStep(InteriorReader *pReader){ - assert( !interiorReaderAtEnd(pReader) ); - - /* If the last term has been read, signal eof, else construct the - ** next term. - */ - if( pReader->nData==0 ){ - dataBufferReset(&pReader->term); - }else{ - int n, nPrefix, nSuffix; - - n = fts3GetVarint32(pReader->pData, &nPrefix); - n += fts3GetVarint32(pReader->pData+n, &nSuffix); - - /* Truncate the current term and append suffix data. */ - pReader->term.nData = nPrefix; - dataBufferAppend(&pReader->term, pReader->pData+n, nSuffix); - - assert( n+nSuffix<=pReader->nData ); - pReader->pData += n+nSuffix; - pReader->nData -= n+nSuffix; - } - pReader->iBlockid++; -} - -/* Compare the current term to pTerm[nTerm], returning strcmp-style -** results. If isPrefix, equality means equal through nTerm bytes. -*/ -static int interiorReaderTermCmp(InteriorReader *pReader, - const char *pTerm, int nTerm, int isPrefix){ - const char *pReaderTerm = interiorReaderTerm(pReader); - int nReaderTerm = interiorReaderTermBytes(pReader); - int c, n = nReaderTerm0 ) return -1; - if( nTerm>0 ) return 1; - return 0; - } - - c = memcmp(pReaderTerm, pTerm, n); - if( c!=0 ) return c; - if( isPrefix && n==nTerm ) return 0; - return nReaderTerm - nTerm; -} - -/****************************************************************/ -/* LeafWriter is used to collect terms and associated doclist data -** into leaf blocks in %_segments (see top of file for format info). -** Expected usage is: -** -** LeafWriter writer; -** leafWriterInit(0, 0, &writer); -** while( sorted_terms_left_to_process ){ -** // data is doclist data for that term. -** rc = leafWriterStep(v, &writer, pTerm, nTerm, pData, nData); -** if( rc!=SQLITE_OK ) goto err; -** } -** rc = leafWriterFinalize(v, &writer); -**err: -** leafWriterDestroy(&writer); -** return rc; +/* Insert an element into the hash table pH. The key is pKey,nKey +** and the data is "data". ** -** leafWriterStep() may write a collected leaf out to %_segments. -** leafWriterFinalize() finishes writing any buffered data and stores -** a root node in %_segdir. leafWriterDestroy() frees all buffers and -** InteriorWriters allocated as part of writing this segment. +** If no element exists with a matching key, then a new +** element is created. A copy of the key is made if the copyKey +** flag is set. NULL is returned. +** +** If another element already exists with the same key, then the +** new data replaces the old data and the old data is returned. +** The key is not copied in this instance. If a malloc fails, then +** the new data is returned and the hash table is unchanged. ** -** TODO(shess) Document leafWriterStepMerge(). +** If the "data" parameter to this function is NULL, then the +** element corresponding to "key" is removed from the hash table. */ +SQLITE_PRIVATE void *sqlite3Fts3HashInsert( + Fts3Hash *pH, /* The hash table to insert into */ + const void *pKey, /* The key */ + int nKey, /* Number of bytes in the key */ + void *data /* The data */ +){ + int hraw; /* Raw hash value of the key */ + int h; /* the hash of the key modulo hash table size */ + Fts3HashElem *elem; /* Used to loop thru the element list */ + Fts3HashElem *new_elem; /* New element added to the pH */ + int (*xHash)(const void*,int); /* The hash function */ -/* Put terms with data this big in their own block. */ -#define STANDALONE_MIN 1024 - -/* Keep leaf blocks below this size. */ -#define LEAF_MAX 2048 - -typedef struct LeafWriter { - int iLevel; - int idx; - sqlite_int64 iStartBlockid; /* needed to create the root info */ - sqlite_int64 iEndBlockid; /* when we're done writing. */ - - DataBuffer term; /* previous encoded term */ - DataBuffer data; /* encoding buffer */ - - /* bytes of first term in the current node which distinguishes that - ** term from the last term of the previous node. - */ - int nTermDistinct; - - InteriorWriter parentWriter; /* if we overflow */ - int has_parent; -} LeafWriter; - -static void leafWriterInit(int iLevel, int idx, LeafWriter *pWriter){ - CLEAR(pWriter); - pWriter->iLevel = iLevel; - pWriter->idx = idx; - - dataBufferInit(&pWriter->term, 32); - - /* Start out with a reasonably sized block, though it can grow. */ - dataBufferInit(&pWriter->data, LEAF_MAX); -} - -#ifndef NDEBUG -/* Verify that the data is readable as a leaf node. */ -static void leafNodeValidate(const char *pData, int nData){ - int n, iDummy; - - if( nData==0 ) return; - assert( nData>0 ); - assert( pData!=0 ); - assert( pData+nData>pData ); - - /* Must lead with a varint(0) */ - n = fts3GetVarint32(pData, &iDummy); - assert( iDummy==0 ); - assert( n>0 ); - assert( n0 ); - assert( iDummy>0 ); - assert( n+iDummy>0 ); - assert( n+iDummy0 ); - assert( iDummy>0 ); - assert( n+iDummy>0 ); - assert( n+iDummy<=nData ); - ASSERT_VALID_DOCLIST(DL_DEFAULT, pData+n, iDummy, NULL); - pData += n+iDummy; - nData -= n+iDummy; - - /* Verify that trailing terms and doclists also are readable. */ - while( nData!=0 ){ - n = fts3GetVarint32(pData, &iDummy); - assert( n>0 ); - assert( iDummy>=0 ); - assert( n0 ); - assert( iDummy>0 ); - assert( n+iDummy>0 ); - assert( n+iDummy0 ); - assert( iDummy>0 ); - assert( n+iDummy>0 ); - assert( n+iDummy<=nData ); - ASSERT_VALID_DOCLIST(DL_DEFAULT, pData+n, iDummy, NULL); - pData += n+iDummy; - nData -= n+iDummy; + assert( pH!=0 ); + xHash = ftsHashFunction(pH->keyClass); + assert( xHash!=0 ); + hraw = (*xHash)(pKey, nKey); + assert( (pH->htsize & (pH->htsize-1))==0 ); + h = hraw & (pH->htsize-1); + elem = fts3FindElementByHash(pH,pKey,nKey,h); + if( elem ){ + void *old_data = elem->data; + if( data==0 ){ + fts3RemoveElementByHash(pH,elem,h); + }else{ + elem->data = data; + } + return old_data; } -} -#define ASSERT_VALID_LEAF_NODE(p, n) leafNodeValidate(p, n) -#else -#define ASSERT_VALID_LEAF_NODE(p, n) assert( 1 ) -#endif - -/* Flush the current leaf node to %_segments, and adding the resulting -** blockid and the starting term to the interior node which will -** contain it. -*/ -static int leafWriterInternalFlush(fulltext_vtab *v, LeafWriter *pWriter, - int iData, int nData){ - sqlite_int64 iBlockid = 0; - const char *pStartingTerm; - int nStartingTerm, rc, n; - - /* Must have the leading varint(0) flag, plus at least some - ** valid-looking data. - */ - assert( nData>2 ); - assert( iData>=0 ); - assert( iData+nData<=pWriter->data.nData ); - ASSERT_VALID_LEAF_NODE(pWriter->data.pData+iData, nData); - - rc = block_insert(v, pWriter->data.pData+iData, nData, &iBlockid); - if( rc!=SQLITE_OK ) return rc; - assert( iBlockid!=0 ); - - /* Reconstruct the first term in the leaf for purposes of building - ** the interior node. - */ - n = fts3GetVarint32(pWriter->data.pData+iData+1, &nStartingTerm); - pStartingTerm = pWriter->data.pData+iData+1+n; - assert( pWriter->data.nData>iData+1+n+nStartingTerm ); - assert( pWriter->nTermDistinct>0 ); - assert( pWriter->nTermDistinct<=nStartingTerm ); - nStartingTerm = pWriter->nTermDistinct; - - if( pWriter->has_parent ){ - interiorWriterAppend(&pWriter->parentWriter, - pStartingTerm, nStartingTerm, iBlockid); - }else{ - interiorWriterInit(1, pStartingTerm, nStartingTerm, iBlockid, - &pWriter->parentWriter); - pWriter->has_parent = 1; + if( data==0 ) return 0; + if( (pH->htsize==0 && fts3Rehash(pH,8)) + || (pH->count>=pH->htsize && fts3Rehash(pH, pH->htsize*2)) + ){ + pH->count = 0; + return data; } - - /* Track the span of this segment's leaf nodes. */ - if( pWriter->iEndBlockid==0 ){ - pWriter->iEndBlockid = pWriter->iStartBlockid = iBlockid; + assert( pH->htsize>0 ); + new_elem = (Fts3HashElem*)fts3HashMalloc( sizeof(Fts3HashElem) ); + if( new_elem==0 ) return data; + if( pH->copyKey && pKey!=0 ){ + new_elem->pKey = fts3HashMalloc( nKey ); + if( new_elem->pKey==0 ){ + fts3HashFree(new_elem); + return data; + } + memcpy((void*)new_elem->pKey, pKey, nKey); }else{ - pWriter->iEndBlockid++; - assert( iBlockid==pWriter->iEndBlockid ); + new_elem->pKey = (void*)pKey; } - - return SQLITE_OK; + new_elem->nKey = nKey; + pH->count++; + assert( pH->htsize>0 ); + assert( (pH->htsize & (pH->htsize-1))==0 ); + h = hraw & (pH->htsize-1); + fts3HashInsertElement(pH, &pH->ht[h], new_elem); + new_elem->data = data; + return 0; } -static int leafWriterFlush(fulltext_vtab *v, LeafWriter *pWriter){ - int rc = leafWriterInternalFlush(v, pWriter, 0, pWriter->data.nData); - if( rc!=SQLITE_OK ) return rc; - /* Re-initialize the output buffer. */ - dataBufferReset(&pWriter->data); - - return SQLITE_OK; -} +#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ -/* Fetch the root info for the segment. If the entire leaf fits -** within ROOT_MAX, then it will be returned directly, otherwise it -** will be flushed and the root info will be returned from the -** interior node. *piEndBlockid is set to the blockid of the last -** interior or leaf node written to disk (0 if none are written at -** all). -*/ -static int leafWriterRootInfo(fulltext_vtab *v, LeafWriter *pWriter, - char **ppRootInfo, int *pnRootInfo, - sqlite_int64 *piEndBlockid){ - /* we can fit the segment entirely inline */ - if( !pWriter->has_parent && pWriter->data.nDatadata.pData; - *pnRootInfo = pWriter->data.nData; - *piEndBlockid = 0; - return SQLITE_OK; - } +/************** End of fts3_hash.c *******************************************/ +/************** Begin file fts3_porter.c *************************************/ +/* +** 2006 September 30 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** Implementation of the full-text-search tokenizer that implements +** a Porter stemmer. +*/ - /* Flush remaining leaf data. */ - if( pWriter->data.nData>0 ){ - int rc = leafWriterFlush(v, pWriter); - if( rc!=SQLITE_OK ) return rc; - } +/* +** The code in this file is only compiled if: +** +** * The FTS3 module is being built as an extension +** (in which case SQLITE_CORE is not defined), or +** +** * The FTS3 module is being built into the core of +** SQLite (in which case SQLITE_ENABLE_FTS3 is defined). +*/ +#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - /* We must have flushed a leaf at some point. */ - assert( pWriter->has_parent ); - /* Tenatively set the end leaf blockid as the end blockid. If the - ** interior node can be returned inline, this will be the final - ** blockid, otherwise it will be overwritten by - ** interiorWriterRootInfo(). - */ - *piEndBlockid = pWriter->iEndBlockid; - return interiorWriterRootInfo(v, &pWriter->parentWriter, - ppRootInfo, pnRootInfo, piEndBlockid); -} -/* Collect the rootInfo data and store it into the segment directory. -** This has the effect of flushing the segment's leaf data to -** %_segments, and also flushing any interior nodes to %_segments. +/* +** Class derived from sqlite3_tokenizer */ -static int leafWriterFinalize(fulltext_vtab *v, LeafWriter *pWriter){ - sqlite_int64 iEndBlockid; - char *pRootInfo; - int rc, nRootInfo; - - rc = leafWriterRootInfo(v, pWriter, &pRootInfo, &nRootInfo, &iEndBlockid); - if( rc!=SQLITE_OK ) return rc; - - /* Don't bother storing an entirely empty segment. */ - if( iEndBlockid==0 && nRootInfo==0 ) return SQLITE_OK; +typedef struct porter_tokenizer { + sqlite3_tokenizer base; /* Base class */ +} porter_tokenizer; - return segdir_set(v, pWriter->iLevel, pWriter->idx, - pWriter->iStartBlockid, pWriter->iEndBlockid, - iEndBlockid, pRootInfo, nRootInfo); -} +/* +** Class derived from sqlit3_tokenizer_cursor +*/ +typedef struct porter_tokenizer_cursor { + sqlite3_tokenizer_cursor base; + const char *zInput; /* input we are tokenizing */ + int nInput; /* size of the input */ + int iOffset; /* current position in zInput */ + int iToken; /* index of next token to be returned */ + char *zToken; /* storage for current token */ + int nAllocated; /* space allocated to zToken buffer */ +} porter_tokenizer_cursor; -static void leafWriterDestroy(LeafWriter *pWriter){ - if( pWriter->has_parent ) interiorWriterDestroy(&pWriter->parentWriter); - dataBufferDestroy(&pWriter->term); - dataBufferDestroy(&pWriter->data); -} -/* Encode a term into the leafWriter, delta-encoding as appropriate. -** Returns the length of the new term which distinguishes it from the -** previous term, which can be used to set nTermDistinct when a node -** boundary is crossed. +/* +** Create a new tokenizer instance. */ -static int leafWriterEncodeTerm(LeafWriter *pWriter, - const char *pTerm, int nTerm){ - char c[VARINT_MAX+VARINT_MAX]; - int n, nPrefix = 0; - - assert( nTerm>0 ); - while( nPrefixterm.nData && - pTerm[nPrefix]==pWriter->term.pData[nPrefix] ){ - nPrefix++; - /* Failing this implies that the terms weren't in order. */ - assert( nPrefixdata.nData==0 ){ - /* Encode the node header and leading term as: - ** varint(0) - ** varint(nTerm) - ** char pTerm[nTerm] - */ - n = fts3PutVarint(c, '\0'); - n += fts3PutVarint(c+n, nTerm); - dataBufferAppend2(&pWriter->data, c, n, pTerm, nTerm); - }else{ - /* Delta-encode the term as: - ** varint(nPrefix) - ** varint(nSuffix) - ** char pTermSuffix[nSuffix] - */ - n = fts3PutVarint(c, nPrefix); - n += fts3PutVarint(c+n, nTerm-nPrefix); - dataBufferAppend2(&pWriter->data, c, n, pTerm+nPrefix, nTerm-nPrefix); - } - dataBufferReplace(&pWriter->term, pTerm, nTerm); + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); - return nPrefix+1; + t = (porter_tokenizer *) sqlite3_malloc(sizeof(*t)); + if( t==NULL ) return SQLITE_NOMEM; + memset(t, 0, sizeof(*t)); + *ppTokenizer = &t->base; + return SQLITE_OK; } -/* Used to avoid a memmove when a large amount of doclist data is in -** the buffer. This constructs a node and term header before -** iDoclistData and flushes the resulting complete node using -** leafWriterInternalFlush(). +/* +** Destroy a tokenizer */ -static int leafWriterInlineFlush(fulltext_vtab *v, LeafWriter *pWriter, - const char *pTerm, int nTerm, - int iDoclistData){ - char c[VARINT_MAX+VARINT_MAX]; - int iData, n = fts3PutVarint(c, 0); - n += fts3PutVarint(c+n, nTerm); - - /* There should always be room for the header. Even if pTerm shared - ** a substantial prefix with the previous term, the entire prefix - ** could be constructed from earlier data in the doclist, so there - ** should be room. - */ - assert( iDoclistData>=n+nTerm ); - - iData = iDoclistData-(n+nTerm); - memcpy(pWriter->data.pData+iData, c, n); - memcpy(pWriter->data.pData+iData+n, pTerm, nTerm); - - return leafWriterInternalFlush(v, pWriter, iData, pWriter->data.nData-iData); +static int porterDestroy(sqlite3_tokenizer *pTokenizer){ + sqlite3_free(pTokenizer); + return SQLITE_OK; } -/* Push pTerm[nTerm] along with the doclist data to the leaf layer of -** %_segments. +/* +** Prepare to begin tokenizing a particular string. The input +** string to be tokenized is zInput[0..nInput-1]. A cursor +** used to incrementally tokenize this string is returned in +** *ppCursor. */ -static int leafWriterStepMerge(fulltext_vtab *v, LeafWriter *pWriter, - const char *pTerm, int nTerm, - DLReader *pReaders, int nReaders){ - char c[VARINT_MAX+VARINT_MAX]; - int iTermData = pWriter->data.nData, iDoclistData; - int i, nData, n, nActualData, nActual, rc, nTermDistinct; - - ASSERT_VALID_LEAF_NODE(pWriter->data.pData, pWriter->data.nData); - nTermDistinct = leafWriterEncodeTerm(pWriter, pTerm, nTerm); - - /* Remember nTermDistinct if opening a new node. */ - if( iTermData==0 ) pWriter->nTermDistinct = nTermDistinct; - - iDoclistData = pWriter->data.nData; - - /* Estimate the length of the merged doclist so we can leave space - ** to encode it. - */ - for(i=0, nData=0; idata, c, n); - - docListMerge(&pWriter->data, pReaders, nReaders); - ASSERT_VALID_DOCLIST(DL_DEFAULT, - pWriter->data.pData+iDoclistData+n, - pWriter->data.nData-iDoclistData-n, NULL); - - /* The actual amount of doclist data at this point could be smaller - ** than the length we encoded. Additionally, the space required to - ** encode this length could be smaller. For small doclists, this is - ** not a big deal, we can just use memmove() to adjust things. - */ - nActualData = pWriter->data.nData-(iDoclistData+n); - nActual = fts3PutVarint(c, nActualData); - assert( nActualData<=nData ); - assert( nActual<=n ); - - /* If the new doclist is big enough for force a standalone leaf - ** node, we can immediately flush it inline without doing the - ** memmove(). - */ - /* TODO(shess) This test matches leafWriterStep(), which does this - ** test before it knows the cost to varint-encode the term and - ** doclist lengths. At some point, change to - ** pWriter->data.nData-iTermData>STANDALONE_MIN. - */ - if( nTerm+nActualData>STANDALONE_MIN ){ - /* Push leaf node from before this term. */ - if( iTermData>0 ){ - rc = leafWriterInternalFlush(v, pWriter, 0, iTermData); - if( rc!=SQLITE_OK ) return rc; - - pWriter->nTermDistinct = nTermDistinct; - } - - /* Fix the encoded doclist length. */ - iDoclistData += n - nActual; - memcpy(pWriter->data.pData+iDoclistData, c, nActual); - - /* Push the standalone leaf node. */ - rc = leafWriterInlineFlush(v, pWriter, pTerm, nTerm, iDoclistData); - if( rc!=SQLITE_OK ) return rc; - - /* Leave the node empty. */ - dataBufferReset(&pWriter->data); - - return rc; - } - - /* At this point, we know that the doclist was small, so do the - ** memmove if indicated. - */ - if( nActualdata.pData+iDoclistData+nActual, - pWriter->data.pData+iDoclistData+n, - pWriter->data.nData-(iDoclistData+n)); - pWriter->data.nData -= n-nActual; - } - - /* Replace written length with actual length. */ - memcpy(pWriter->data.pData+iDoclistData, c, nActual); - - /* If the node is too large, break things up. */ - /* TODO(shess) This test matches leafWriterStep(), which does this - ** test before it knows the cost to varint-encode the term and - ** doclist lengths. At some point, change to - ** pWriter->data.nData>LEAF_MAX. - */ - if( iTermData+nTerm+nActualData>LEAF_MAX ){ - /* Flush out the leading data as a node */ - rc = leafWriterInternalFlush(v, pWriter, 0, iTermData); - if( rc!=SQLITE_OK ) return rc; +static int porterOpen( + sqlite3_tokenizer *pTokenizer, /* The tokenizer */ + const char *zInput, int nInput, /* String to be tokenized */ + sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */ +){ + porter_tokenizer_cursor *c; - pWriter->nTermDistinct = nTermDistinct; + UNUSED_PARAMETER(pTokenizer); - /* Rebuild header using the current term */ - n = fts3PutVarint(pWriter->data.pData, 0); - n += fts3PutVarint(pWriter->data.pData+n, nTerm); - memcpy(pWriter->data.pData+n, pTerm, nTerm); - n += nTerm; + c = (porter_tokenizer_cursor *) sqlite3_malloc(sizeof(*c)); + if( c==NULL ) return SQLITE_NOMEM; - /* There should always be room, because the previous encoding - ** included all data necessary to construct the term. - */ - assert( ndata.nData-iDoclistDatadata.pData+n, - pWriter->data.pData+iDoclistData, - pWriter->data.nData-iDoclistData); - pWriter->data.nData -= iDoclistData-n; + c->zInput = zInput; + if( zInput==0 ){ + c->nInput = 0; + }else if( nInput<0 ){ + c->nInput = (int)strlen(zInput); + }else{ + c->nInput = nInput; } - ASSERT_VALID_LEAF_NODE(pWriter->data.pData, pWriter->data.nData); + c->iOffset = 0; /* start tokenizing at the beginning */ + c->iToken = 0; + c->zToken = NULL; /* no space allocated, yet. */ + c->nAllocated = 0; + *ppCursor = &c->base; return SQLITE_OK; } -/* Push pTerm[nTerm] along with the doclist data to the leaf layer of -** %_segments. -*/ -/* TODO(shess) Revise writeZeroSegment() so that doclists are -** constructed directly in pWriter->data. +/* +** Close a tokenization cursor previously opened by a call to +** porterOpen() above. */ -static int leafWriterStep(fulltext_vtab *v, LeafWriter *pWriter, - const char *pTerm, int nTerm, - const char *pData, int nData){ - int rc; - DLReader reader; - - dlrInit(&reader, DL_DEFAULT, pData, nData); - rc = leafWriterStepMerge(v, pWriter, pTerm, nTerm, &reader, 1); - dlrDestroy(&reader); - - return rc; -} - - -/****************************************************************/ -/* LeafReader is used to iterate over an individual leaf node. */ -typedef struct LeafReader { - DataBuffer term; /* copy of current term. */ - - const char *pData; /* data for current term. */ - int nData; -} LeafReader; - -static void leafReaderDestroy(LeafReader *pReader){ - dataBufferDestroy(&pReader->term); - SCRAMBLE(pReader); -} - -static int leafReaderAtEnd(LeafReader *pReader){ - return pReader->nData<=0; -} - -/* Access the current term. */ -static int leafReaderTermBytes(LeafReader *pReader){ - return pReader->term.nData; -} -static const char *leafReaderTerm(LeafReader *pReader){ - assert( pReader->term.nData>0 ); - return pReader->term.pData; -} - -/* Access the doclist data for the current term. */ -static int leafReaderDataBytes(LeafReader *pReader){ - int nData; - assert( pReader->term.nData>0 ); - fts3GetVarint32(pReader->pData, &nData); - return nData; -} -static const char *leafReaderData(LeafReader *pReader){ - int n, nData; - assert( pReader->term.nData>0 ); - n = fts3GetVarint32(pReader->pData, &nData); - return pReader->pData+n; -} - -static void leafReaderInit(const char *pData, int nData, - LeafReader *pReader){ - int nTerm, n; - - assert( nData>0 ); - assert( pData[0]=='\0' ); - - CLEAR(pReader); - - /* Read the first term, skipping the header byte. */ - n = fts3GetVarint32(pData+1, &nTerm); - dataBufferInit(&pReader->term, nTerm); - dataBufferReplace(&pReader->term, pData+1+n, nTerm); - - /* Position after the first term. */ - assert( 1+n+nTermpData = pData+1+n+nTerm; - pReader->nData = nData-1-n-nTerm; -} - -/* Step the reader forward to the next term. */ -static void leafReaderStep(LeafReader *pReader){ - int n, nData, nPrefix, nSuffix; - assert( !leafReaderAtEnd(pReader) ); - - /* Skip previous entry's data block. */ - n = fts3GetVarint32(pReader->pData, &nData); - assert( n+nData<=pReader->nData ); - pReader->pData += n+nData; - pReader->nData -= n+nData; - - if( !leafReaderAtEnd(pReader) ){ - /* Construct the new term using a prefix from the old term plus a - ** suffix from the leaf data. - */ - n = fts3GetVarint32(pReader->pData, &nPrefix); - n += fts3GetVarint32(pReader->pData+n, &nSuffix); - assert( n+nSuffixnData ); - pReader->term.nData = nPrefix; - dataBufferAppend(&pReader->term, pReader->pData+n, nSuffix); - - pReader->pData += n+nSuffix; - pReader->nData -= n+nSuffix; - } +static int porterClose(sqlite3_tokenizer_cursor *pCursor){ + porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor; + sqlite3_free(c->zToken); + sqlite3_free(c); + return SQLITE_OK; } - -/* strcmp-style comparison of pReader's current term against pTerm. -** If isPrefix, equality means equal through nTerm bytes. +/* +** Vowel or consonant */ -static int leafReaderTermCmp(LeafReader *pReader, - const char *pTerm, int nTerm, int isPrefix){ - int c, n = pReader->term.nDataterm.nData : nTerm; - if( n==0 ){ - if( pReader->term.nData>0 ) return -1; - if(nTerm>0 ) return 1; - return 0; - } - - c = memcmp(pReader->term.pData, pTerm, n); - if( c!=0 ) return c; - if( isPrefix && n==nTerm ) return 0; - return pReader->term.nData - nTerm; -} - +static const char cType[] = { + 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, + 1, 1, 1, 2, 1 +}; -/****************************************************************/ -/* LeavesReader wraps LeafReader to allow iterating over the entire -** leaf layer of the tree. +/* +** isConsonant() and isVowel() determine if their first character in +** the string they point to is a consonant or a vowel, according +** to Porter ruls. +** +** A consonate is any letter other than 'a', 'e', 'i', 'o', or 'u'. +** 'Y' is a consonant unless it follows another consonant, +** in which case it is a vowel. +** +** In these routine, the letters are in reverse order. So the 'y' rule +** is that 'y' is a consonant unless it is followed by another +** consonent. */ -typedef struct LeavesReader { - int idx; /* Index within the segment. */ - - sqlite3_stmt *pStmt; /* Statement we're streaming leaves from. */ - int eof; /* we've seen SQLITE_DONE from pStmt. */ - - LeafReader leafReader; /* reader for the current leaf. */ - DataBuffer rootData; /* root data for inline. */ -} LeavesReader; - -/* Access the current term. */ -static int leavesReaderTermBytes(LeavesReader *pReader){ - assert( !pReader->eof ); - return leafReaderTermBytes(&pReader->leafReader); -} -static const char *leavesReaderTerm(LeavesReader *pReader){ - assert( !pReader->eof ); - return leafReaderTerm(&pReader->leafReader); -} - -/* Access the doclist data for the current term. */ -static int leavesReaderDataBytes(LeavesReader *pReader){ - assert( !pReader->eof ); - return leafReaderDataBytes(&pReader->leafReader); -} -static const char *leavesReaderData(LeavesReader *pReader){ - assert( !pReader->eof ); - return leafReaderData(&pReader->leafReader); +static int isVowel(const char*); +static int isConsonant(const char *z){ + int j; + char x = *z; + if( x==0 ) return 0; + assert( x>='a' && x<='z' ); + j = cType[x-'a']; + if( j<2 ) return j; + return z[1]==0 || isVowel(z + 1); } - -static int leavesReaderAtEnd(LeavesReader *pReader){ - return pReader->eof; +static int isVowel(const char *z){ + int j; + char x = *z; + if( x==0 ) return 0; + assert( x>='a' && x<='z' ); + j = cType[x-'a']; + if( j<2 ) return 1-j; + return isConsonant(z + 1); } -/* loadSegmentLeaves() may not read all the way to SQLITE_DONE, thus -** leaving the statement handle open, which locks the table. -*/ -/* TODO(shess) This "solution" is not satisfactory. Really, there -** should be check-in function for all statement handles which -** arranges to call sqlite3_reset(). This most likely will require -** modification to control flow all over the place, though, so for now -** just punt. +/* +** Let any sequence of one or more vowels be represented by V and let +** C be sequence of one or more consonants. Then every word can be +** represented as: +** +** [C] (VC){m} [V] +** +** In prose: A word is an optional consonant followed by zero or +** vowel-consonant pairs followed by an optional vowel. "m" is the +** number of vowel consonant pairs. This routine computes the value +** of m for the first i bytes of a word. +** +** Return true if the m-value for z is 1 or more. In other words, +** return true if z contains at least one vowel that is followed +** by a consonant. ** -** Note the the current system assumes that segment merges will run to -** completion, which is why this particular probably hasn't arisen in -** this case. Probably a brittle assumption. +** In this routine z[] is in reverse order. So we are really looking +** for an instance of of a consonant followed by a vowel. */ -static int leavesReaderReset(LeavesReader *pReader){ - return sqlite3_reset(pReader->pStmt); +static int m_gt_0(const char *z){ + while( isVowel(z) ){ z++; } + if( *z==0 ) return 0; + while( isConsonant(z) ){ z++; } + return *z!=0; } -static void leavesReaderDestroy(LeavesReader *pReader){ - /* If idx is -1, that means we're using a non-cached statement - ** handle in the optimize() case, so we need to release it. - */ - if( pReader->pStmt!=NULL && pReader->idx==-1 ){ - sqlite3_finalize(pReader->pStmt); - } - leafReaderDestroy(&pReader->leafReader); - dataBufferDestroy(&pReader->rootData); - SCRAMBLE(pReader); +/* Like mgt0 above except we are looking for a value of m which is +** exactly 1 +*/ +static int m_eq_1(const char *z){ + while( isVowel(z) ){ z++; } + if( *z==0 ) return 0; + while( isConsonant(z) ){ z++; } + if( *z==0 ) return 0; + while( isVowel(z) ){ z++; } + if( *z==0 ) return 1; + while( isConsonant(z) ){ z++; } + return *z==0; } -/* Initialize pReader with the given root data (if iStartBlockid==0 -** the leaf data was entirely contained in the root), or from the -** stream of blocks between iStartBlockid and iEndBlockid, inclusive. +/* Like mgt0 above except we are looking for a value of m>1 instead +** or m>0 */ -static int leavesReaderInit(fulltext_vtab *v, - int idx, - sqlite_int64 iStartBlockid, - sqlite_int64 iEndBlockid, - const char *pRootData, int nRootData, - LeavesReader *pReader){ - CLEAR(pReader); - pReader->idx = idx; - - dataBufferInit(&pReader->rootData, 0); - if( iStartBlockid==0 ){ - /* Entire leaf level fit in root data. */ - dataBufferReplace(&pReader->rootData, pRootData, nRootData); - leafReaderInit(pReader->rootData.pData, pReader->rootData.nData, - &pReader->leafReader); - }else{ - sqlite3_stmt *s; - int rc = sql_get_leaf_statement(v, idx, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 1, iStartBlockid); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int64(s, 2, iEndBlockid); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_step(s); - if( rc==SQLITE_DONE ){ - pReader->eof = 1; - return SQLITE_OK; - } - if( rc!=SQLITE_ROW ) return rc; - - pReader->pStmt = s; - leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0), - sqlite3_column_bytes(pReader->pStmt, 0), - &pReader->leafReader); - } - return SQLITE_OK; +static int m_gt_1(const char *z){ + while( isVowel(z) ){ z++; } + if( *z==0 ) return 0; + while( isConsonant(z) ){ z++; } + if( *z==0 ) return 0; + while( isVowel(z) ){ z++; } + if( *z==0 ) return 0; + while( isConsonant(z) ){ z++; } + return *z!=0; } -/* Step the current leaf forward to the next term. If we reach the -** end of the current leaf, step forward to the next leaf block. +/* +** Return TRUE if there is a vowel anywhere within z[0..n-1] */ -static int leavesReaderStep(fulltext_vtab *v, LeavesReader *pReader){ - assert( !leavesReaderAtEnd(pReader) ); - leafReaderStep(&pReader->leafReader); - - if( leafReaderAtEnd(&pReader->leafReader) ){ - int rc; - if( pReader->rootData.pData ){ - pReader->eof = 1; - return SQLITE_OK; - } - rc = sqlite3_step(pReader->pStmt); - if( rc!=SQLITE_ROW ){ - pReader->eof = 1; - return rc==SQLITE_DONE ? SQLITE_OK : rc; - } - leafReaderDestroy(&pReader->leafReader); - leafReaderInit(sqlite3_column_blob(pReader->pStmt, 0), - sqlite3_column_bytes(pReader->pStmt, 0), - &pReader->leafReader); - } - return SQLITE_OK; +static int hasVowel(const char *z){ + while( isConsonant(z) ){ z++; } + return *z!=0; } -/* Order LeavesReaders by their term, ignoring idx. Readers at eof -** always sort to the end. +/* +** Return TRUE if the word ends in a double consonant. +** +** The text is reversed here. So we are really looking at +** the first two characters of z[]. */ -static int leavesReaderTermCmp(LeavesReader *lr1, LeavesReader *lr2){ - if( leavesReaderAtEnd(lr1) ){ - if( leavesReaderAtEnd(lr2) ) return 0; - return 1; - } - if( leavesReaderAtEnd(lr2) ) return -1; - - return leafReaderTermCmp(&lr1->leafReader, - leavesReaderTerm(lr2), leavesReaderTermBytes(lr2), - 0); +static int doubleConsonant(const char *z){ + return isConsonant(z) && z[0]==z[1]; } -/* Similar to leavesReaderTermCmp(), with additional ordering by idx -** so that older segments sort before newer segments. +/* +** Return TRUE if the word ends with three letters which +** are consonant-vowel-consonent and where the final consonant +** is not 'w', 'x', or 'y'. +** +** The word is reversed here. So we are really checking the +** first three letters and the first one cannot be in [wxy]. */ -static int leavesReaderCmp(LeavesReader *lr1, LeavesReader *lr2){ - int c = leavesReaderTermCmp(lr1, lr2); - if( c!=0 ) return c; - return lr1->idx-lr2->idx; +static int star_oh(const char *z){ + return + isConsonant(z) && + z[0]!='w' && z[0]!='x' && z[0]!='y' && + isVowel(z+1) && + isConsonant(z+2); } -/* Assume that pLr[1]..pLr[nLr] are sorted. Bubble pLr[0] into its -** sorted position. +/* +** If the word ends with zFrom and xCond() is true for the stem +** of the word that preceeds the zFrom ending, then change the +** ending to zTo. +** +** The input word *pz and zFrom are both in reverse order. zTo +** is in normal order. +** +** Return TRUE if zFrom matches. Return FALSE if zFrom does not +** match. Not that TRUE is returned even if xCond() fails and +** no substitution occurs. */ -static void leavesReaderReorder(LeavesReader *pLr, int nLr){ - while( nLr>1 && leavesReaderCmp(pLr, pLr+1)>0 ){ - LeavesReader tmp = pLr[0]; - pLr[0] = pLr[1]; - pLr[1] = tmp; - nLr--; - pLr++; +static int stem( + char **pz, /* The word being stemmed (Reversed) */ + const char *zFrom, /* If the ending matches this... (Reversed) */ + const char *zTo, /* ... change the ending to this (not reversed) */ + int (*xCond)(const char*) /* Condition that must be true */ +){ + char *z = *pz; + while( *zFrom && *zFrom==*z ){ z++; zFrom++; } + if( *zFrom!=0 ) return 0; + if( xCond && !xCond(z) ) return 1; + while( *zTo ){ + *(--z) = *(zTo++); } + *pz = z; + return 1; } -/* Initializes pReaders with the segments from level iLevel, returning -** the number of segments in *piReaders. Leaves pReaders in sorted -** order. +/* +** This is the fallback stemmer used when the porter stemmer is +** inappropriate. The input word is copied into the output with +** US-ASCII case folding. If the input word is too long (more +** than 20 bytes if it contains no digits or more than 6 bytes if +** it contains digits) then word is truncated to 20 or 6 bytes +** by taking 10 or 3 bytes from the beginning and end. */ -static int leavesReadersInit(fulltext_vtab *v, int iLevel, - LeavesReader *pReaders, int *piReaders){ - sqlite3_stmt *s; - int i, rc = sql_get_statement(v, SEGDIR_SELECT_LEVEL_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - rc = sqlite3_bind_int(s, 1, iLevel); - if( rc!=SQLITE_OK ) return rc; - - i = 0; - while( (rc = sqlite3_step(s))==SQLITE_ROW ){ - sqlite_int64 iStart = sqlite3_column_int64(s, 0); - sqlite_int64 iEnd = sqlite3_column_int64(s, 1); - const char *pRootData = sqlite3_column_blob(s, 2); - int nRootData = sqlite3_column_bytes(s, 2); - - assert( i0 ){ - leavesReaderDestroy(&pReaders[i]); +static void copy_stemmer(const char *zIn, int nIn, char *zOut, int *pnOut){ + int i, mx, j; + int hasDigit = 0; + for(i=0; i='A' && c<='Z' ){ + zOut[i] = c - 'A' + 'a'; + }else{ + if( c>='0' && c<='9' ) hasDigit = 1; + zOut[i] = c; } - return rc; - } - - *piReaders = i; - - /* Leave our results sorted by term, then age. */ - while( i-- ){ - leavesReaderReorder(pReaders+i, *piReaders-i); } - return SQLITE_OK; -} - -/* Merge doclists from pReaders[nReaders] into a single doclist, which -** is written to pWriter. Assumes pReaders is ordered oldest to -** newest. -*/ -/* TODO(shess) Consider putting this inline in segmentMerge(). */ -static int leavesReadersMerge(fulltext_vtab *v, - LeavesReader *pReaders, int nReaders, - LeafWriter *pWriter){ - DLReader dlReaders[MERGE_COUNT]; - const char *pTerm = leavesReaderTerm(pReaders); - int i, nTerm = leavesReaderTermBytes(pReaders); - - assert( nReaders<=MERGE_COUNT ); - - for(i=0; imx*2 ){ + for(j=mx, i=nIn-mx; i=sizeof(zReverse)-7 ){ + /* The word is too big or too small for the porter stemmer. + ** Fallback to the copy stemmer */ + copy_stemmer(zIn, nIn, zOut, pnOut); + return; } - return SQLITE_OK; -} - -/* Merge MERGE_COUNT segments at iLevel into a new segment at -** iLevel+1. If iLevel+1 is already full of segments, those will be -** merged to make room. -*/ -static int segmentMerge(fulltext_vtab *v, int iLevel){ - LeafWriter writer; - LeavesReader lrs[MERGE_COUNT]; - int i, rc, idx = 0; - - /* Determine the next available segment index at the next level, - ** merging as necessary. - */ - rc = segdirNextIndex(v, iLevel+1, &idx); - if( rc!=SQLITE_OK ) return rc; - - /* TODO(shess) This assumes that we'll always see exactly - ** MERGE_COUNT segments to merge at a given level. That will be - ** broken if we allow the developer to request preemptive or - ** deferred merging. - */ - memset(&lrs, '\0', sizeof(lrs)); - rc = leavesReadersInit(v, iLevel, lrs, &i); - if( rc!=SQLITE_OK ) return rc; - assert( i==MERGE_COUNT ); - - leafWriterInit(iLevel+1, idx, &writer); - - /* Since leavesReaderReorder() pushes readers at eof to the end, - ** when the first reader is empty, all will be empty. - */ - while( !leavesReaderAtEnd(lrs) ){ - /* Figure out how many readers share their next term. */ - for(i=1; i='A' && c<='Z' ){ + zReverse[j] = c + 'a' - 'A'; + }else if( c>='a' && c<='z' ){ + zReverse[j] = c; + }else{ + /* The use of a character not in [a-zA-Z] means that we fallback + ** to the copy stemmer */ + copy_stemmer(zIn, nIn, zOut, pnOut); + return; } + } + memset(&zReverse[sizeof(zReverse)-5], 0, 5); + z = &zReverse[j+1]; - rc = leavesReadersMerge(v, lrs, i, &writer); - if( rc!=SQLITE_OK ) goto err; - - /* Step forward those that were merged. */ - while( i-->0 ){ - rc = leavesReaderStep(v, lrs+i); - if( rc!=SQLITE_OK ) goto err; - /* Reorder by term, then by age. */ - leavesReaderReorder(lrs+i, MERGE_COUNT-i); + /* Step 1a */ + if( z[0]=='s' ){ + if( + !stem(&z, "sess", "ss", 0) && + !stem(&z, "sei", "i", 0) && + !stem(&z, "ss", "ss", 0) + ){ + z++; } } - for(i=0; i0 ); - - for(rc=SQLITE_OK; rc==SQLITE_OK && !leavesReaderAtEnd(pReader); - rc=leavesReaderStep(v, pReader)){ - /* TODO(shess) Really want leavesReaderTermCmp(), but that name is - ** already taken to compare the terms of two LeavesReaders. Think - ** on a better name. [Meanwhile, break encapsulation rather than - ** use a confusing name.] - */ - int c = leafReaderTermCmp(&pReader->leafReader, pTerm, nTerm, isPrefix); - if( c>0 ) break; /* Past any possible matches. */ - if( c==0 ){ - const char *pData = leavesReaderData(pReader); - int iBuffer, nData = leavesReaderDataBytes(pReader); - - /* Find the first empty buffer. */ - for(iBuffer=0; iBuffer0 ){ - assert(pBuffers!=NULL); - memcpy(p, pBuffers, nBuffers*sizeof(*pBuffers)); - sqlite3_free(pBuffers); - } - pBuffers = p; - } - dataBufferInit(&(pBuffers[nBuffers]), 0); - nBuffers++; - } - - /* At this point, must have an empty at iBuffer. */ - assert(iBufferpData, p->nData); + /* Step 3 */ + switch( z[0] ){ + case 'e': + stem(&z, "etaci", "ic", m_gt_0) || + stem(&z, "evita", "", m_gt_0) || + stem(&z, "ezila", "al", m_gt_0); + break; + case 'i': + stem(&z, "itici", "ic", m_gt_0); + break; + case 'l': + stem(&z, "laci", "ic", m_gt_0) || + stem(&z, "luf", "", m_gt_0); + break; + case 's': + stem(&z, "ssen", "", m_gt_0); + break; + } - /* dataBufferReset() could allow a large doclist to blow up - ** our memory requirements. - */ - if( p->nCapacity<1024 ){ - dataBufferReset(p); - }else{ - dataBufferDestroy(p); - dataBufferInit(p, 0); - } - } - } - } + /* Step 4 */ + switch( z[1] ){ + case 'a': + if( z[0]=='l' && m_gt_1(z+2) ){ + z += 2; + } + break; + case 'c': + if( z[0]=='e' && z[2]=='n' && (z[3]=='a' || z[3]=='e') && m_gt_1(z+4) ){ + z += 4; + } + break; + case 'e': + if( z[0]=='r' && m_gt_1(z+2) ){ + z += 2; + } + break; + case 'i': + if( z[0]=='c' && m_gt_1(z+2) ){ + z += 2; + } + break; + case 'l': + if( z[0]=='e' && z[2]=='b' && (z[3]=='a' || z[3]=='i') && m_gt_1(z+4) ){ + z += 4; + } + break; + case 'n': + if( z[0]=='t' ){ + if( z[2]=='a' ){ + if( m_gt_1(z+3) ){ + z += 3; + } + }else if( z[2]=='e' ){ + stem(&z, "tneme", "", m_gt_1) || + stem(&z, "tnem", "", m_gt_1) || + stem(&z, "tne", "", m_gt_1); + } + } + break; + case 'o': + if( z[0]=='u' ){ + if( m_gt_1(z+2) ){ + z += 2; + } + }else if( z[3]=='s' || z[3]=='t' ){ + stem(&z, "noi", "", m_gt_1); + } + break; + case 's': + if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){ + z += 3; + } + break; + case 't': + stem(&z, "eta", "", m_gt_1) || + stem(&z, "iti", "", m_gt_1); + break; + case 'u': + if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){ + z += 3; + } + break; + case 'v': + case 'z': + if( z[0]=='e' && z[2]=='i' && m_gt_1(z+3) ){ + z += 3; + } + break; } - /* Union all the doclists together into *out. */ - /* TODO(shess) What if *out is big? Sigh. */ - if( rc==SQLITE_OK && nBuffers>0 ){ - int iBuffer; - for(iBuffer=0; iBuffer0 ){ - if( out->nData==0 ){ - dataBufferSwap(out, &(pBuffers[iBuffer])); - }else{ - docListAccumulateUnion(out, pBuffers[iBuffer].pData, - pBuffers[iBuffer].nData); - } - } + /* Step 5a */ + if( z[0]=='e' ){ + if( m_gt_1(z+1) ){ + z++; + }else if( m_eq_1(z+1) && !star_oh(z+1) ){ + z++; } } - while( nBuffers-- ){ - dataBufferDestroy(&(pBuffers[nBuffers])); + /* Step 5b */ + if( m_gt_1(z) && z[0]=='l' && z[1]=='l' ){ + z++; } - if( pBuffers!=NULL ) sqlite3_free(pBuffers); - return rc; + /* z[] is now the stemmed word in reverse order. Flip it back + ** around into forward order and return. + */ + *pnOut = i = (int)strlen(z); + zOut[i] = 0; + while( *z ){ + zOut[--i] = *(z++); + } } -/* Call loadSegmentLeavesInt() with pData/nData as input. */ -static int loadSegmentLeaf(fulltext_vtab *v, const char *pData, int nData, - const char *pTerm, int nTerm, int isPrefix, - DataBuffer *out){ - LeavesReader reader; - int rc; +/* +** Characters that can be part of a token. We assume any character +** whose value is greater than 0x80 (any UTF character) can be +** part of a token. In other words, delimiters all must have +** values of 0x7f or lower. +*/ +static const char porterIdChar[] = { +/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */ +}; +#define isDelim(C) (((ch=C)&0x80)==0 && (ch<0x30 || !porterIdChar[ch-0x30])) - assert( nData>1 ); - assert( *pData=='\0' ); - rc = leavesReaderInit(v, 0, 0, 0, pData, nData, &reader); - if( rc!=SQLITE_OK ) return rc; +/* +** Extract the next token from a tokenization cursor. The cursor must +** have been opened by a prior call to porterOpen(). +*/ +static int porterNext( + sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by porterOpen */ + const char **pzToken, /* OUT: *pzToken is the token text */ + int *pnBytes, /* OUT: Number of bytes in token */ + int *piStartOffset, /* OUT: Starting offset of token */ + int *piEndOffset, /* OUT: Ending offset of token */ + int *piPosition /* OUT: Position integer of token */ +){ + porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor; + const char *z = c->zInput; - rc = loadSegmentLeavesInt(v, &reader, pTerm, nTerm, isPrefix, out); - leavesReaderReset(&reader); - leavesReaderDestroy(&reader); - return rc; -} + while( c->iOffsetnInput ){ + int iStartOffset, ch; -/* Call loadSegmentLeavesInt() with the leaf nodes from iStartLeaf to -** iEndLeaf (inclusive) as input, and merge the resulting doclist into -** out. -*/ -static int loadSegmentLeaves(fulltext_vtab *v, - sqlite_int64 iStartLeaf, sqlite_int64 iEndLeaf, - const char *pTerm, int nTerm, int isPrefix, - DataBuffer *out){ - int rc; - LeavesReader reader; + /* Scan past delimiter characters */ + while( c->iOffsetnInput && isDelim(z[c->iOffset]) ){ + c->iOffset++; + } - assert( iStartLeaf<=iEndLeaf ); - rc = leavesReaderInit(v, 0, iStartLeaf, iEndLeaf, NULL, 0, &reader); - if( rc!=SQLITE_OK ) return rc; + /* Count non-delimiter characters. */ + iStartOffset = c->iOffset; + while( c->iOffsetnInput && !isDelim(z[c->iOffset]) ){ + c->iOffset++; + } - rc = loadSegmentLeavesInt(v, &reader, pTerm, nTerm, isPrefix, out); - leavesReaderReset(&reader); - leavesReaderDestroy(&reader); - return rc; + if( c->iOffset>iStartOffset ){ + int n = c->iOffset-iStartOffset; + if( n>c->nAllocated ){ + c->nAllocated = n+20; + c->zToken = sqlite3_realloc(c->zToken, c->nAllocated); + if( c->zToken==NULL ) return SQLITE_NOMEM; + } + porter_stemmer(&z[iStartOffset], n, c->zToken, pnBytes); + *pzToken = c->zToken; + *piStartOffset = iStartOffset; + *piEndOffset = c->iOffset; + *piPosition = c->iToken++; + return SQLITE_OK; + } + } + return SQLITE_DONE; } -/* Taking pData/nData as an interior node, find the sequence of child -** nodes which could include pTerm/nTerm/isPrefix. Note that the -** interior node terms logically come between the blocks, so there is -** one more blockid than there are terms (that block contains terms >= -** the last interior-node term). +/* +** The set of routines that implement the porter-stemmer tokenizer */ -/* TODO(shess) The calling code may already know that the end child is -** not worth calculating, because the end may be in a later sibling -** node. Consider whether breaking symmetry is worthwhile. I suspect -** it is not worthwhile. +static const sqlite3_tokenizer_module porterTokenizerModule = { + 0, + porterCreate, + porterDestroy, + porterOpen, + porterClose, + porterNext, +}; + +/* +** Allocate a new porter tokenizer. Return a pointer to the new +** tokenizer in *ppModule */ -static void getChildrenContaining(const char *pData, int nData, - const char *pTerm, int nTerm, int isPrefix, - sqlite_int64 *piStartChild, - sqlite_int64 *piEndChild){ - InteriorReader reader; +SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule( + sqlite3_tokenizer_module const**ppModule +){ + *ppModule = &porterTokenizerModule; +} - assert( nData>1 ); - assert( *pData!='\0' ); - interiorReaderInit(pData, nData, &reader); +#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ - /* Scan for the first child which could contain pTerm/nTerm. */ - while( !interiorReaderAtEnd(&reader) ){ - if( interiorReaderTermCmp(&reader, pTerm, nTerm, 0)>0 ) break; - interiorReaderStep(&reader); - } - *piStartChild = interiorReaderCurrentBlockid(&reader); +/************** End of fts3_porter.c *****************************************/ +/************** Begin file fts3_tokenizer.c **********************************/ +/* +** 2007 June 22 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This is part of an SQLite module implementing full-text search. +** This particular file implements the generic tokenizer interface. +*/ - /* Keep scanning to find a term greater than our term, using prefix - ** comparison if indicated. If isPrefix is false, this will be the - ** same blockid as the starting block. - */ - while( !interiorReaderAtEnd(&reader) ){ - if( interiorReaderTermCmp(&reader, pTerm, nTerm, isPrefix)>0 ) break; - interiorReaderStep(&reader); - } - *piEndChild = interiorReaderCurrentBlockid(&reader); +/* +** The code in this file is only compiled if: +** +** * The FTS3 module is being built as an extension +** (in which case SQLITE_CORE is not defined), or +** +** * The FTS3 module is being built into the core of +** SQLite (in which case SQLITE_ENABLE_FTS3 is defined). +*/ +#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - interiorReaderDestroy(&reader); +#ifndef SQLITE_CORE + SQLITE_EXTENSION_INIT1 +#endif - /* Children must ascend, and if !prefix, both must be the same. */ - assert( *piEndChild>=*piStartChild ); - assert( isPrefix || *piStartChild==*piEndChild ); -} -/* Read block at iBlockid and pass it with other params to -** getChildrenContaining(). +/* +** Implementation of the SQL scalar function for accessing the underlying +** hash table. This function may be called as follows: +** +** SELECT (); +** SELECT (, ); +** +** where is the name passed as the second argument +** to the sqlite3Fts3InitHashTable() function (e.g. 'fts3_tokenizer'). +** +** If the argument is specified, it must be a blob value +** containing a pointer to be stored as the hash data corresponding +** to the string . If is not specified, then +** the string must already exist in the has table. Otherwise, +** an error is returned. +** +** Whether or not the argument is specified, the value returned +** is a blob containing the pointer stored as the hash data corresponding +** to string (after the hash-table is updated, if applicable). */ -static int loadAndGetChildrenContaining( - fulltext_vtab *v, - sqlite_int64 iBlockid, - const char *pTerm, int nTerm, int isPrefix, - sqlite_int64 *piStartChild, sqlite_int64 *piEndChild +static void scalarFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv ){ - sqlite3_stmt *s = NULL; - int rc; - - assert( iBlockid!=0 ); - assert( pTerm!=NULL ); - assert( nTerm!=0 ); /* TODO(shess) Why not allow this? */ - assert( piStartChild!=NULL ); - assert( piEndChild!=NULL ); - - rc = sql_get_statement(v, BLOCK_SELECT_STMT, &s); - if( rc!=SQLITE_OK ) return rc; + Fts3Hash *pHash; + void *pPtr = 0; + const unsigned char *zName; + int nName; - rc = sqlite3_bind_int64(s, 1, iBlockid); - if( rc!=SQLITE_OK ) return rc; + assert( argc==1 || argc==2 ); - rc = sqlite3_step(s); - if( rc==SQLITE_DONE ) return SQLITE_ERROR; - if( rc!=SQLITE_ROW ) return rc; - - getChildrenContaining(sqlite3_column_blob(s, 0), sqlite3_column_bytes(s, 0), - pTerm, nTerm, isPrefix, piStartChild, piEndChild); - - /* We expect only one row. We must execute another sqlite3_step() - * to complete the iteration; otherwise the table will remain - * locked. */ - rc = sqlite3_step(s); - if( rc==SQLITE_ROW ) return SQLITE_ERROR; - if( rc!=SQLITE_DONE ) return rc; + pHash = (Fts3Hash *)sqlite3_user_data(context); - return SQLITE_OK; -} + zName = sqlite3_value_text(argv[0]); + nName = sqlite3_value_bytes(argv[0])+1; -/* Traverse the tree represented by pData[nData] looking for -** pTerm[nTerm], placing its doclist into *out. This is internal to -** loadSegment() to make error-handling cleaner. -*/ -static int loadSegmentInt(fulltext_vtab *v, const char *pData, int nData, - sqlite_int64 iLeavesEnd, - const char *pTerm, int nTerm, int isPrefix, - DataBuffer *out){ - /* Special case where root is a leaf. */ - if( *pData=='\0' ){ - return loadSegmentLeaf(v, pData, nData, pTerm, nTerm, isPrefix, out); + if( argc==2 ){ + void *pOld; + int n = sqlite3_value_bytes(argv[1]); + if( n!=sizeof(pPtr) ){ + sqlite3_result_error(context, "argument type mismatch", -1); + return; + } + pPtr = *(void **)sqlite3_value_blob(argv[1]); + pOld = sqlite3Fts3HashInsert(pHash, (void *)zName, nName, pPtr); + if( pOld==pPtr ){ + sqlite3_result_error(context, "out of memory", -1); + return; + } }else{ - int rc; - sqlite_int64 iStartChild, iEndChild; - - /* Process pData as an interior node, then loop down the tree - ** until we find the set of leaf nodes to scan for the term. - */ - getChildrenContaining(pData, nData, pTerm, nTerm, isPrefix, - &iStartChild, &iEndChild); - while( iStartChild>iLeavesEnd ){ - sqlite_int64 iNextStart, iNextEnd; - rc = loadAndGetChildrenContaining(v, iStartChild, pTerm, nTerm, isPrefix, - &iNextStart, &iNextEnd); - if( rc!=SQLITE_OK ) return rc; - - /* If we've branched, follow the end branch, too. */ - if( iStartChild!=iEndChild ){ - sqlite_int64 iDummy; - rc = loadAndGetChildrenContaining(v, iEndChild, pTerm, nTerm, isPrefix, - &iDummy, &iNextEnd); - if( rc!=SQLITE_OK ) return rc; - } - - assert( iNextStart<=iNextEnd ); - iStartChild = iNextStart; - iEndChild = iNextEnd; - } - assert( iStartChild<=iLeavesEnd ); - assert( iEndChild<=iLeavesEnd ); - - /* Scan through the leaf segments for doclists. */ - return loadSegmentLeaves(v, iStartChild, iEndChild, - pTerm, nTerm, isPrefix, out); + pPtr = sqlite3Fts3HashFind(pHash, zName, nName); + if( !pPtr ){ + char *zErr = sqlite3_mprintf("unknown tokenizer: %s", zName); + sqlite3_result_error(context, zErr, -1); + sqlite3_free(zErr); + return; + } } -} - -/* Call loadSegmentInt() to collect the doclist for pTerm/nTerm, then -** merge its doclist over *out (any duplicate doclists read from the -** segment rooted at pData will overwrite those in *out). -*/ -/* TODO(shess) Consider changing this to determine the depth of the -** leaves using either the first characters of interior nodes (when -** ==1, we're one level above the leaves), or the first character of -** the root (which will describe the height of the tree directly). -** Either feels somewhat tricky to me. -*/ -/* TODO(shess) The current merge is likely to be slow for large -** doclists (though it should process from newest/smallest to -** oldest/largest, so it may not be that bad). It might be useful to -** modify things to allow for N-way merging. This could either be -** within a segment, with pairwise merges across segments, or across -** all segments at once. -*/ -static int loadSegment(fulltext_vtab *v, const char *pData, int nData, - sqlite_int64 iLeavesEnd, - const char *pTerm, int nTerm, int isPrefix, - DataBuffer *out){ - DataBuffer result; - int rc; - - assert( nData>1 ); - /* This code should never be called with buffered updates. */ - assert( v->nPendingData<0 ); - - dataBufferInit(&result, 0); - rc = loadSegmentInt(v, pData, nData, iLeavesEnd, - pTerm, nTerm, isPrefix, &result); - if( rc==SQLITE_OK && result.nData>0 ){ - if( out->nData==0 ){ - DataBuffer tmp = *out; - *out = result; - result = tmp; - }else{ - DataBuffer merged; - DLReader readers[2]; - - dlrInit(&readers[0], DL_DEFAULT, out->pData, out->nData); - dlrInit(&readers[1], DL_DEFAULT, result.pData, result.nData); - dataBufferInit(&merged, out->nData+result.nData); - docListMerge(&merged, readers, 2); - dataBufferDestroy(out); - *out = merged; - dlrDestroy(&readers[0]); - dlrDestroy(&readers[1]); - } - } - dataBufferDestroy(&result); - return rc; + sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT); } -/* Scan the database and merge together the posting lists for the term -** into *out. -*/ -static int termSelect( - fulltext_vtab *v, - int iColumn, - const char *pTerm, int nTerm, /* Term to query for */ - int isPrefix, /* True for a prefix search */ - DocListType iType, - DataBuffer *out /* Write results here */ -){ - DataBuffer doclist; - sqlite3_stmt *s; - int rc = sql_get_statement(v, SEGDIR_SELECT_ALL_STMT, &s); - if( rc!=SQLITE_OK ) return rc; - - /* This code should never be called with buffered updates. */ - assert( v->nPendingData<0 ); +static int fts3IsIdChar(char c){ + static const char isFtsIdChar[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 0x */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 1x */ + 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 2x */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */ + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */ + }; + return (c&0x80 || isFtsIdChar[(int)(c)]); +} - dataBufferInit(&doclist, 0); - dataBufferInit(out, 0); +SQLITE_PRIVATE const char *sqlite3Fts3NextToken(const char *zStr, int *pn){ + const char *z1; + const char *z2 = 0; + + /* Find the start of the next token. */ + z1 = zStr; + while( z2==0 ){ + char c = *z1; + switch( c ){ + case '\0': return 0; /* No more tokens here */ + case '\'': + case '"': + case '`': { + z2 = z1; + while( *++z2 && (*z2!=c || *++z2==c) ); + break; + } + case '[': + z2 = &z1[1]; + while( *z2 && z2[0]!=']' ) z2++; + if( *z2 ) z2++; + break; - /* Traverse the segments from oldest to newest so that newer doclist - ** elements for given docids overwrite older elements. - */ - while( (rc = sqlite3_step(s))==SQLITE_ROW ){ - const char *pData = sqlite3_column_blob(s, 2); - const int nData = sqlite3_column_bytes(s, 2); - const sqlite_int64 iLeavesEnd = sqlite3_column_int64(s, 1); - rc = loadSegment(v, pData, nData, iLeavesEnd, pTerm, nTerm, isPrefix, - &doclist); - if( rc!=SQLITE_OK ) goto err; - } - if( rc==SQLITE_DONE ){ - if( doclist.nData!=0 ){ - /* TODO(shess) The old term_select_all() code applied the column - ** restrict as we merged segments, leading to smaller buffers. - ** This is probably worthwhile to bring back, once the new storage - ** system is checked in. - */ - if( iColumn==v->nColumn) iColumn = -1; - docListTrim(DL_DEFAULT, doclist.pData, doclist.nData, - iColumn, iType, out); + default: + if( fts3IsIdChar(*z1) ){ + z2 = &z1[1]; + while( fts3IsIdChar(*z2) ) z2++; + }else{ + z1++; + } } - rc = SQLITE_OK; } - err: - dataBufferDestroy(&doclist); - return rc; + *pn = (int)(z2-z1); + return z1; } -/****************************************************************/ -/* Used to hold hashtable data for sorting. */ -typedef struct TermData { - const char *pTerm; - int nTerm; - DLCollector *pCollector; -} TermData; - -/* Orders TermData elements in strcmp fashion ( <0 for less-than, 0 -** for equal, >0 for greater-than). -*/ -static int termDataCmp(const void *av, const void *bv){ - const TermData *a = (const TermData *)av; - const TermData *b = (const TermData *)bv; - int n = a->nTermnTerm ? a->nTerm : b->nTerm; - int c = memcmp(a->pTerm, b->pTerm, n); - if( c!=0 ) return c; - return a->nTerm-b->nTerm; -} - -/* Order pTerms data by term, then write a new level 0 segment using -** LeafWriter. -*/ -static int writeZeroSegment(fulltext_vtab *v, fts3Hash *pTerms){ - fts3HashElem *e; - int idx, rc, i, n; - TermData *pData; - LeafWriter writer; - DataBuffer dl; - - /* Determine the next index at level 0, merging as necessary. */ - rc = segdirNextIndex(v, 0, &idx); - if( rc!=SQLITE_OK ) return rc; - - n = fts3HashCount(pTerms); - pData = sqlite3_malloc(n*sizeof(TermData)); +SQLITE_PRIVATE int sqlite3Fts3InitTokenizer( + Fts3Hash *pHash, /* Tokenizer hash table */ + const char *zArg, /* Possible tokenizer specification */ + sqlite3_tokenizer **ppTok, /* OUT: Tokenizer (if applicable) */ + const char **pzTokenizer, /* OUT: Set to zArg if is tokenizer */ + char **pzErr /* OUT: Set to malloced error message */ +){ + int rc; + char *z = (char *)zArg; + int n; + char *zCopy; + char *zEnd; /* Pointer to nul-term of zCopy */ + sqlite3_tokenizer_module *m; - for(i = 0, e = fts3HashFirst(pTerms); e; i++, e = fts3HashNext(e)){ - assert( i1 ) qsort(pData, n, sizeof(*pData), termDataCmp); + zEnd = &zCopy[strlen(zCopy)]; - /* TODO(shess) Refactor so that we can write directly to the segment - ** DataBuffer, as happens for segment merges. - */ - leafWriterInit(0, idx, &writer); - dataBufferInit(&dl, 0); - for(i=0; ixCreate(iArg, aArg, ppTok); + assert( rc!=SQLITE_OK || *ppTok ); + if( rc!=SQLITE_OK ){ + *pzErr = sqlite3_mprintf("unknown tokenizer"); + }else{ + (*ppTok)->pModule = m; + } + sqlite3_free((void *)aArg); } - rc = leafWriterFinalize(v, &writer); - err: - dataBufferDestroy(&dl); - sqlite3_free(pData); - leafWriterDestroy(&writer); + sqlite3_free(zCopy); return rc; } -/* If pendingTerms has data, free it. */ -static int clearPendingTerms(fulltext_vtab *v){ - if( v->nPendingData>=0 ){ - fts3HashElem *e; - for(e=fts3HashFirst(&v->pendingTerms); e; e=fts3HashNext(e)){ - dlcDelete(fts3HashData(e)); - } - fts3HashClear(&v->pendingTerms); - v->nPendingData = -1; - } - return SQLITE_OK; -} -/* If pendingTerms has data, flush it to a level-zero segment, and -** free it. -*/ -static int flushPendingTerms(fulltext_vtab *v){ - if( v->nPendingData>=0 ){ - int rc = writeZeroSegment(v, &v->pendingTerms); - if( rc==SQLITE_OK ) clearPendingTerms(v); - return rc; - } - return SQLITE_OK; -} +#ifdef SQLITE_TEST -/* If pendingTerms is "too big", or docid is out of order, flush it. -** Regardless, be certain that pendingTerms is initialized for use. -*/ -static int initPendingTerms(fulltext_vtab *v, sqlite_int64 iDocid){ - /* TODO(shess) Explore whether partially flushing the buffer on - ** forced-flush would provide better performance. I suspect that if - ** we ordered the doclists by size and flushed the largest until the - ** buffer was half empty, that would let the less frequent terms - ** generate longer doclists. - */ - if( iDocid<=v->iPrevDocid || v->nPendingData>kPendingThreshold ){ - int rc = flushPendingTerms(v); - if( rc!=SQLITE_OK ) return rc; - } - if( v->nPendingData<0 ){ - fts3HashInit(&v->pendingTerms, FTS3_HASH_STRING, 1); - v->nPendingData = 0; - } - v->iPrevDocid = iDocid; - return SQLITE_OK; -} -/* This function implements the xUpdate callback; it is the top-level entry - * point for inserting, deleting or updating a row in a full-text table. */ -static int fulltextUpdate(sqlite3_vtab *pVtab, int nArg, sqlite3_value **ppArg, - sqlite_int64 *pRowid){ - fulltext_vtab *v = (fulltext_vtab *) pVtab; - int rc; +/* +** Implementation of a special SQL scalar function for testing tokenizers +** designed to be used in concert with the Tcl testing framework. This +** function must be called with two arguments: +** +** SELECT (, ); +** SELECT (, ); +** +** where is the name passed as the second argument +** to the sqlite3Fts3InitHashTable() function (e.g. 'fts3_tokenizer') +** concatenated with the string '_test' (e.g. 'fts3_tokenizer_test'). +** +** The return value is a string that may be interpreted as a Tcl +** list. For each token in the , three elements are +** added to the returned list. The first is the token position, the +** second is the token text (folded, stemmed, etc.) and the third is the +** substring of associated with the token. For example, +** using the built-in "simple" tokenizer: +** +** SELECT fts_tokenizer_test('simple', 'I don't see how'); +** +** will return the string: +** +** "{0 i I 1 dont don't 2 see see 3 how how}" +** +*/ +static void testFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + Fts3Hash *pHash; + sqlite3_tokenizer_module *p; + sqlite3_tokenizer *pTokenizer = 0; + sqlite3_tokenizer_cursor *pCsr = 0; - FTSTRACE(("FTS3 Update %p\n", pVtab)); + const char *zErr = 0; - if( nArg<2 ){ - rc = index_delete(v, sqlite3_value_int64(ppArg[0])); - if( rc==SQLITE_OK ){ - /* If we just deleted the last row in the table, clear out the - ** index data. - */ - rc = content_exists(v); - if( rc==SQLITE_ROW ){ - rc = SQLITE_OK; - }else if( rc==SQLITE_DONE ){ - /* Clear the pending terms so we don't flush a useless level-0 - ** segment when the transaction closes. - */ - rc = clearPendingTerms(v); - if( rc==SQLITE_OK ){ - rc = segdir_delete_all(v); - } - } - } - } else if( sqlite3_value_type(ppArg[0]) != SQLITE_NULL ){ - /* An update: - * ppArg[0] = old rowid - * ppArg[1] = new rowid - * ppArg[2..2+v->nColumn-1] = values - * ppArg[2+v->nColumn] = value for magic column (we ignore this) - * ppArg[2+v->nColumn+1] = value for docid - */ - sqlite_int64 rowid = sqlite3_value_int64(ppArg[0]); - if( sqlite3_value_type(ppArg[1]) != SQLITE_INTEGER || - sqlite3_value_int64(ppArg[1]) != rowid ){ - rc = SQLITE_ERROR; /* we don't allow changing the rowid */ - }else if( sqlite3_value_type(ppArg[2+v->nColumn+1]) != SQLITE_INTEGER || - sqlite3_value_int64(ppArg[2+v->nColumn+1]) != rowid ){ - rc = SQLITE_ERROR; /* we don't allow changing the docid */ - }else{ - assert( nArg==2+v->nColumn+2); - rc = index_update(v, rowid, &ppArg[2]); - } - } else { - /* An insert: - * ppArg[1] = requested rowid - * ppArg[2..2+v->nColumn-1] = values - * ppArg[2+v->nColumn] = value for magic column (we ignore this) - * ppArg[2+v->nColumn+1] = value for docid - */ - sqlite3_value *pRequestDocid = ppArg[2+v->nColumn+1]; - assert( nArg==2+v->nColumn+2); - if( SQLITE_NULL != sqlite3_value_type(pRequestDocid) && - SQLITE_NULL != sqlite3_value_type(ppArg[1]) ){ - /* TODO(shess) Consider allowing this to work if the values are - ** identical. I'm inclined to discourage that usage, though, - ** given that both rowid and docid are special columns. Better - ** would be to define one or the other as the default winner, - ** but should it be fts3-centric (docid) or SQLite-centric - ** (rowid)? - */ - rc = SQLITE_ERROR; - }else{ - if( SQLITE_NULL == sqlite3_value_type(pRequestDocid) ){ - pRequestDocid = ppArg[1]; - } - rc = index_insert(v, pRequestDocid, &ppArg[2], pRowid); - } - } + const char *zName; + int nName; + const char *zInput; + int nInput; - return rc; -} + const char *zArg = 0; -static int fulltextSync(sqlite3_vtab *pVtab){ - FTSTRACE(("FTS3 xSync()\n")); - return flushPendingTerms((fulltext_vtab *)pVtab); -} + const char *zToken; + int nToken; + int iStart; + int iEnd; + int iPos; -static int fulltextBegin(sqlite3_vtab *pVtab){ - fulltext_vtab *v = (fulltext_vtab *) pVtab; - FTSTRACE(("FTS3 xBegin()\n")); + Tcl_Obj *pRet; - /* Any buffered updates should have been cleared by the previous - ** transaction. - */ - assert( v->nPendingData<0 ); - return clearPendingTerms(v); -} + assert( argc==2 || argc==3 ); -static int fulltextCommit(sqlite3_vtab *pVtab){ - fulltext_vtab *v = (fulltext_vtab *) pVtab; - FTSTRACE(("FTS3 xCommit()\n")); + nName = sqlite3_value_bytes(argv[0]); + zName = (const char *)sqlite3_value_text(argv[0]); + nInput = sqlite3_value_bytes(argv[argc-1]); + zInput = (const char *)sqlite3_value_text(argv[argc-1]); - /* Buffered updates should have been cleared by fulltextSync(). */ - assert( v->nPendingData<0 ); - return clearPendingTerms(v); -} + if( argc==3 ){ + zArg = (const char *)sqlite3_value_text(argv[1]); + } -static int fulltextRollback(sqlite3_vtab *pVtab){ - FTSTRACE(("FTS3 xRollback()\n")); - return clearPendingTerms((fulltext_vtab *)pVtab); -} + pHash = (Fts3Hash *)sqlite3_user_data(context); + p = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, zName, nName+1); -/* -** Implementation of the snippet() function for FTS3 -*/ -static void snippetFunc( - sqlite3_context *pContext, - int argc, - sqlite3_value **argv -){ - fulltext_cursor *pCursor; - if( argc<1 ) return; - if( sqlite3_value_type(argv[0])!=SQLITE_BLOB || - sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){ - sqlite3_result_error(pContext, "illegal first argument to html_snippet",-1); - }else{ - const char *zStart = ""; - const char *zEnd = ""; - const char *zEllipsis = "..."; - memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor)); - if( argc>=2 ){ - zStart = (const char*)sqlite3_value_text(argv[1]); - if( argc>=3 ){ - zEnd = (const char*)sqlite3_value_text(argv[2]); - if( argc>=4 ){ - zEllipsis = (const char*)sqlite3_value_text(argv[3]); - } - } - } - snippetAllOffsets(pCursor); - snippetText(pCursor, zStart, zEnd, zEllipsis); - sqlite3_result_text(pContext, pCursor->snippet.zSnippet, - pCursor->snippet.nSnippet, SQLITE_STATIC); + if( !p ){ + char *zErr = sqlite3_mprintf("unknown tokenizer: %s", zName); + sqlite3_result_error(context, zErr, -1); + sqlite3_free(zErr); + return; } -} -/* -** Implementation of the offsets() function for FTS3 -*/ -static void snippetOffsetsFunc( - sqlite3_context *pContext, - int argc, - sqlite3_value **argv -){ - fulltext_cursor *pCursor; - if( argc<1 ) return; - if( sqlite3_value_type(argv[0])!=SQLITE_BLOB || - sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){ - sqlite3_result_error(pContext, "illegal first argument to offsets",-1); - }else{ - memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor)); - snippetAllOffsets(pCursor); - snippetOffsetText(&pCursor->snippet); - sqlite3_result_text(pContext, - pCursor->snippet.zOffset, pCursor->snippet.nOffset, - SQLITE_STATIC); + pRet = Tcl_NewObj(); + Tcl_IncrRefCount(pRet); + + if( SQLITE_OK!=p->xCreate(zArg ? 1 : 0, &zArg, &pTokenizer) ){ + zErr = "error in xCreate()"; + goto finish; } -} + pTokenizer->pModule = p; + if( SQLITE_OK!=p->xOpen(pTokenizer, zInput, nInput, &pCsr) ){ + zErr = "error in xOpen()"; + goto finish; + } + pCsr->pTokenizer = pTokenizer; -/* OptLeavesReader is nearly identical to LeavesReader, except that -** where LeavesReader is geared towards the merging of complete -** segment levels (with exactly MERGE_COUNT segments), OptLeavesReader -** is geared towards implementation of the optimize() function, and -** can merge all segments simultaneously. This version may be -** somewhat less efficient than LeavesReader because it merges into an -** accumulator rather than doing an N-way merge, but since segment -** size grows exponentially (so segment count logrithmically) this is -** probably not an immediate problem. -*/ -/* TODO(shess): Prove that assertion, or extend the merge code to -** merge tree fashion (like the prefix-searching code does). -*/ -/* TODO(shess): OptLeavesReader and LeavesReader could probably be -** merged with little or no loss of performance for LeavesReader. The -** merged code would need to handle >MERGE_COUNT segments, and would -** also need to be able to optionally optimize away deletes. -*/ -typedef struct OptLeavesReader { - /* Segment number, to order readers by age. */ - int segment; - LeavesReader reader; -} OptLeavesReader; + while( SQLITE_OK==p->xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos) ){ + Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(iPos)); + Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zToken, nToken)); + zToken = &zInput[iStart]; + nToken = iEnd-iStart; + Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zToken, nToken)); + } -static int optLeavesReaderAtEnd(OptLeavesReader *pReader){ - return leavesReaderAtEnd(&pReader->reader); -} -static int optLeavesReaderTermBytes(OptLeavesReader *pReader){ - return leavesReaderTermBytes(&pReader->reader); -} -static const char *optLeavesReaderData(OptLeavesReader *pReader){ - return leavesReaderData(&pReader->reader); -} -static int optLeavesReaderDataBytes(OptLeavesReader *pReader){ - return leavesReaderDataBytes(&pReader->reader); -} -static const char *optLeavesReaderTerm(OptLeavesReader *pReader){ - return leavesReaderTerm(&pReader->reader); -} -static int optLeavesReaderStep(fulltext_vtab *v, OptLeavesReader *pReader){ - return leavesReaderStep(v, &pReader->reader); -} -static int optLeavesReaderTermCmp(OptLeavesReader *lr1, OptLeavesReader *lr2){ - return leavesReaderTermCmp(&lr1->reader, &lr2->reader); -} -/* Order by term ascending, segment ascending (oldest to newest), with -** exhausted readers to the end. -*/ -static int optLeavesReaderCmp(OptLeavesReader *lr1, OptLeavesReader *lr2){ - int c = optLeavesReaderTermCmp(lr1, lr2); - if( c!=0 ) return c; - return lr1->segment-lr2->segment; -} -/* Bubble pLr[0] to appropriate place in pLr[1..nLr-1]. Assumes that -** pLr[1..nLr-1] is already sorted. -*/ -static void optLeavesReaderReorder(OptLeavesReader *pLr, int nLr){ - while( nLr>1 && optLeavesReaderCmp(pLr, pLr+1)>0 ){ - OptLeavesReader tmp = pLr[0]; - pLr[0] = pLr[1]; - pLr[1] = tmp; - nLr--; - pLr++; + if( SQLITE_OK!=p->xClose(pCsr) ){ + zErr = "error in xClose()"; + goto finish; } + if( SQLITE_OK!=p->xDestroy(pTokenizer) ){ + zErr = "error in xDestroy()"; + goto finish; + } + +finish: + if( zErr ){ + sqlite3_result_error(context, zErr, -1); + }else{ + sqlite3_result_text(context, Tcl_GetString(pRet), -1, SQLITE_TRANSIENT); + } + Tcl_DecrRefCount(pRet); } -/* optimize() helper function. Put the readers in order and iterate -** through them, merging doclists for matching terms into pWriter. -** Returns SQLITE_OK on success, or the SQLite error code which -** prevented success. -*/ -static int optimizeInternal(fulltext_vtab *v, - OptLeavesReader *readers, int nReaders, - LeafWriter *pWriter){ - int i, rc = SQLITE_OK; - DataBuffer doclist, merged, tmp; +static +int registerTokenizer( + sqlite3 *db, + char *zName, + const sqlite3_tokenizer_module *p +){ + int rc; + sqlite3_stmt *pStmt; + const char zSql[] = "SELECT fts3_tokenizer(?, ?)"; - /* Order the readers. */ - i = nReaders; - while( i-- > 0 ){ - optLeavesReaderReorder(&readers[i], nReaders-i); + rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + if( rc!=SQLITE_OK ){ + return rc; } - dataBufferInit(&doclist, LEAF_MAX); - dataBufferInit(&merged, LEAF_MAX); + sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC); + sqlite3_bind_blob(pStmt, 2, &p, sizeof(p), SQLITE_STATIC); + sqlite3_step(pStmt); - /* Exhausted readers bubble to the end, so when the first reader is - ** at eof, all are at eof. - */ - while( !optLeavesReaderAtEnd(&readers[0]) ){ + return sqlite3_finalize(pStmt); +} - /* Figure out how many readers share the next term. */ - for(i=1; i 0 ){ - dlrDestroy(&dlReaders[nReaders]); - } +/* +** Implementation of the scalar function fts3_tokenizer_internal_test(). +** This function is used for testing only, it is not included in the +** build unless SQLITE_TEST is defined. +** +** The purpose of this is to test that the fts3_tokenizer() function +** can be used as designed by the C-code in the queryTokenizer and +** registerTokenizer() functions above. These two functions are repeated +** in the README.tokenizer file as an example, so it is important to +** test them. +** +** To run the tests, evaluate the fts3_tokenizer_internal_test() scalar +** function with no arguments. An assert() will fail if a problem is +** detected. i.e.: +** +** SELECT fts3_tokenizer_internal_test(); +** +*/ +static void intTestFunc( + sqlite3_context *context, + int argc, + sqlite3_value **argv +){ + int rc; + const sqlite3_tokenizer_module *p1; + const sqlite3_tokenizer_module *p2; + sqlite3 *db = (sqlite3 *)sqlite3_user_data(context); - /* Accumulated doclist to reader 0 for next pass. */ - dlrInit(&dlReaders[0], DL_DEFAULT, doclist.pData, doclist.nData); - } + UNUSED_PARAMETER(argc); + UNUSED_PARAMETER(argv); - /* Destroy reader that was left in the pipeline. */ - dlrDestroy(&dlReaders[0]); + /* Test the query function */ + sqlite3Fts3SimpleTokenizerModule(&p1); + rc = queryTokenizer(db, "simple", &p2); + assert( rc==SQLITE_OK ); + assert( p1==p2 ); + rc = queryTokenizer(db, "nosuchtokenizer", &p2); + assert( rc==SQLITE_ERROR ); + assert( p2==0 ); + assert( 0==strcmp(sqlite3_errmsg(db), "unknown tokenizer: nosuchtokenizer") ); - /* Trim deletions from the doclist. */ - dataBufferReset(&merged); - docListTrim(DL_DEFAULT, doclist.pData, doclist.nData, - -1, DL_DEFAULT, &merged); - } + /* Test the storage function */ + rc = registerTokenizer(db, "nosuchtokenizer", p1); + assert( rc==SQLITE_OK ); + rc = queryTokenizer(db, "nosuchtokenizer", &p2); + assert( rc==SQLITE_OK ); + assert( p2==p1 ); - /* Only pass doclists with hits (skip if all hits deleted). */ - if( merged.nData>0 ){ - rc = leafWriterStep(v, pWriter, - optLeavesReaderTerm(&readers[0]), - optLeavesReaderTermBytes(&readers[0]), - merged.pData, merged.nData); - if( rc!=SQLITE_OK ) goto err; - } + sqlite3_result_text(context, "ok", -1, SQLITE_STATIC); +} - /* Step merged readers to next term and reorder. */ - while( i-- > 0 ){ - rc = optLeavesReaderStep(v, &readers[i]); - if( rc!=SQLITE_OK ) goto err; +#endif - optLeavesReaderReorder(&readers[i], nReaders-i); - } - } +/* +** Set up SQL objects in database db used to access the contents of +** the hash table pointed to by argument pHash. The hash table must +** been initialised to use string keys, and to take a private copy +** of the key when a value is inserted. i.e. by a call similar to: +** +** sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1); +** +** This function adds a scalar function (see header comment above +** scalarFunc() in this file for details) and, if ENABLE_TABLE is +** defined at compilation time, a temporary virtual table (see header +** comment above struct HashTableVtab) to the database schema. Both +** provide read/write access to the contents of *pHash. +** +** The third argument to this function, zName, is used as the name +** of both the scalar and, if created, the virtual table. +*/ +SQLITE_PRIVATE int sqlite3Fts3InitHashTable( + sqlite3 *db, + Fts3Hash *pHash, + const char *zName +){ + int rc = SQLITE_OK; + void *p = (void *)pHash; + const int any = SQLITE_ANY; - err: - dataBufferDestroy(&doclist); - dataBufferDestroy(&merged); - return rc; -} +#ifdef SQLITE_TEST + char *zTest = 0; + char *zTest2 = 0; + void *pdb = (void *)db; + zTest = sqlite3_mprintf("%s_test", zName); + zTest2 = sqlite3_mprintf("%s_internal_test", zName); + if( !zTest || !zTest2 ){ + rc = SQLITE_NOMEM; + } +#endif -/* Implement optimize() function for FTS3. optimize(t) merges all -** segments in the fts index into a single segment. 't' is the magic -** table-named column. -*/ -static void optimizeFunc(sqlite3_context *pContext, - int argc, sqlite3_value **argv){ - fulltext_cursor *pCursor; - if( argc>1 ){ - sqlite3_result_error(pContext, "excess arguments to optimize()",-1); - }else if( sqlite3_value_type(argv[0])!=SQLITE_BLOB || - sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){ - sqlite3_result_error(pContext, "illegal first argument to optimize",-1); - }else{ - fulltext_vtab *v; - int i, rc, iMaxLevel; - OptLeavesReader *readers; - int nReaders; - LeafWriter writer; - sqlite3_stmt *s; - - memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor)); - v = cursor_vtab(pCursor); - - /* Flush any buffered updates before optimizing. */ - rc = flushPendingTerms(v); - if( rc!=SQLITE_OK ) goto err; - - rc = segdir_count(v, &nReaders, &iMaxLevel); - if( rc!=SQLITE_OK ) goto err; - if( nReaders==0 || nReaders==1 ){ - sqlite3_result_text(pContext, "Index already optimal", -1, - SQLITE_STATIC); - return; - } + if( SQLITE_OK!=rc + || SQLITE_OK!=(rc = sqlite3_create_function(db, zName, 1, any, p, scalarFunc, 0, 0)) + || SQLITE_OK!=(rc = sqlite3_create_function(db, zName, 2, any, p, scalarFunc, 0, 0)) +#ifdef SQLITE_TEST + || SQLITE_OK!=(rc = sqlite3_create_function(db, zTest, 2, any, p, testFunc, 0, 0)) + || SQLITE_OK!=(rc = sqlite3_create_function(db, zTest, 3, any, p, testFunc, 0, 0)) + || SQLITE_OK!=(rc = sqlite3_create_function(db, zTest2, 0, any, pdb, intTestFunc, 0, 0)) +#endif + ); - rc = sql_get_statement(v, SEGDIR_SELECT_ALL_STMT, &s); - if( rc!=SQLITE_OK ) goto err; +#ifdef SQLITE_TEST + sqlite3_free(zTest); + sqlite3_free(zTest2); +#endif - readers = sqlite3_malloc(nReaders*sizeof(readers[0])); - if( readers==NULL ) goto err; + return rc; +} - /* Note that there will already be a segment at this position - ** until we call segdir_delete() on iMaxLevel. - */ - leafWriterInit(iMaxLevel, 0, &writer); - - i = 0; - while( (rc = sqlite3_step(s))==SQLITE_ROW ){ - sqlite_int64 iStart = sqlite3_column_int64(s, 0); - sqlite_int64 iEnd = sqlite3_column_int64(s, 1); - const char *pRootData = sqlite3_column_blob(s, 2); - int nRootData = sqlite3_column_bytes(s, 2); - - assert( i 0 ){ - leavesReaderDestroy(&readers[i].reader); - } - sqlite3_free(readers); - /* If we've successfully gotten to here, delete the old segments - ** and flush the interior structure of the new segment. - */ - if( rc==SQLITE_OK ){ - for( i=0; i<=iMaxLevel; i++ ){ - rc = segdir_delete(v, i); - if( rc!=SQLITE_OK ) break; - } - if( rc==SQLITE_OK ) rc = leafWriterFinalize(v, &writer); - } - leafWriterDestroy(&writer); +typedef struct simple_tokenizer { + sqlite3_tokenizer base; + char delim[128]; /* flag ASCII delimiters */ +} simple_tokenizer; - if( rc!=SQLITE_OK ) goto err; +typedef struct simple_tokenizer_cursor { + sqlite3_tokenizer_cursor base; + const char *pInput; /* input we are tokenizing */ + int nBytes; /* size of the input */ + int iOffset; /* current position in pInput */ + int iToken; /* index of next token to be returned */ + char *pToken; /* storage for current token */ + int nTokenAllocated; /* space allocated to zToken buffer */ +} simple_tokenizer_cursor; - sqlite3_result_text(pContext, "Index optimized", -1, SQLITE_STATIC); - return; - /* TODO(shess): Error-handling needs to be improved along the - ** lines of the dump_ functions. - */ - err: - { - char buf[512]; - sqlite3_snprintf(sizeof(buf), buf, "Error in optimize: %s", - sqlite3_errmsg(sqlite3_context_db_handle(pContext))); - sqlite3_result_error(pContext, buf, -1); - } - } +static int simpleDelim(simple_tokenizer *t, unsigned char c){ + return c<0x80 && t->delim[c]; } -#ifdef SQLITE_TEST -/* Generate an error of the form ": ". If msg is NULL, -** pull the error from the context's db handle. +/* +** Create a new tokenizer instance. */ -static void generateError(sqlite3_context *pContext, - const char *prefix, const char *msg){ - char buf[512]; - if( msg==NULL ) msg = sqlite3_errmsg(sqlite3_context_db_handle(pContext)); - sqlite3_snprintf(sizeof(buf), buf, "%s: %s", prefix, msg); - sqlite3_result_error(pContext, buf, -1); -} - -/* Helper function to collect the set of terms in the segment into -** pTerms. The segment is defined by the leaf nodes between -** iStartBlockid and iEndBlockid, inclusive, or by the contents of -** pRootData if iStartBlockid is 0 (in which case the entire segment -** fit in a leaf). -*/ -static int collectSegmentTerms(fulltext_vtab *v, sqlite3_stmt *s, - fts3Hash *pTerms){ - const sqlite_int64 iStartBlockid = sqlite3_column_int64(s, 0); - const sqlite_int64 iEndBlockid = sqlite3_column_int64(s, 1); - const char *pRootData = sqlite3_column_blob(s, 2); - const int nRootData = sqlite3_column_bytes(s, 2); - LeavesReader reader; - int rc = leavesReaderInit(v, 0, iStartBlockid, iEndBlockid, - pRootData, nRootData, &reader); - if( rc!=SQLITE_OK ) return rc; +static int simpleCreate( + int argc, const char * const *argv, + sqlite3_tokenizer **ppTokenizer +){ + simple_tokenizer *t; - while( rc==SQLITE_OK && !leavesReaderAtEnd(&reader) ){ - const char *pTerm = leavesReaderTerm(&reader); - const int nTerm = leavesReaderTermBytes(&reader); - void *oldValue = sqlite3Fts3HashFind(pTerms, pTerm, nTerm); - void *newValue = (void *)((char *)oldValue+1); + t = (simple_tokenizer *) sqlite3_malloc(sizeof(*t)); + if( t==NULL ) return SQLITE_NOMEM; + memset(t, 0, sizeof(*t)); - /* From the comment before sqlite3Fts3HashInsert in fts3_hash.c, - ** the data value passed is returned in case of malloc failure. - */ - if( newValue==sqlite3Fts3HashInsert(pTerms, pTerm, nTerm, newValue) ){ - rc = SQLITE_NOMEM; - }else{ - rc = leavesReaderStep(v, &reader); + /* TODO(shess) Delimiters need to remain the same from run to run, + ** else we need to reindex. One solution would be a meta-table to + ** track such information in the database, then we'd only want this + ** information on the initial create. + */ + if( argc>1 ){ + int i, n = (int)strlen(argv[1]); + for(i=0; i=0x80 ){ + sqlite3_free(t); + return SQLITE_ERROR; + } + t->delim[ch] = 1; + } + } else { + /* Mark non-alphanumeric ASCII characters as delimiters */ + int i; + for(i=1; i<0x80; i++){ + t->delim[i] = !isalnum(i) ? -1 : 0; } } - leavesReaderDestroy(&reader); - return rc; + *ppTokenizer = &t->base; + return SQLITE_OK; } -/* Helper function to build the result string for dump_terms(). */ -static int generateTermsResult(sqlite3_context *pContext, fts3Hash *pTerms){ - int iTerm, nTerms, nResultBytes, iByte; - char *result; - TermData *pData; - fts3HashElem *e; +/* +** Destroy a tokenizer +*/ +static int simpleDestroy(sqlite3_tokenizer *pTokenizer){ + sqlite3_free(pTokenizer); + return SQLITE_OK; +} - /* Iterate pTerms to generate an array of terms in pData for - ** sorting. - */ - nTerms = fts3HashCount(pTerms); - assert( nTerms>0 ); - pData = sqlite3_malloc(nTerms*sizeof(TermData)); - if( pData==NULL ) return SQLITE_NOMEM; +/* +** Prepare to begin tokenizing a particular string. The input +** string to be tokenized is pInput[0..nBytes-1]. A cursor +** used to incrementally tokenize this string is returned in +** *ppCursor. +*/ +static int simpleOpen( + sqlite3_tokenizer *pTokenizer, /* The tokenizer */ + const char *pInput, int nBytes, /* String to be tokenized */ + sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */ +){ + simple_tokenizer_cursor *c; - nResultBytes = 0; - for(iTerm = 0, e = fts3HashFirst(pTerms); e; iTerm++, e = fts3HashNext(e)){ - nResultBytes += fts3HashKeysize(e)+1; /* Term plus trailing space */ - assert( iTerm0 ); /* nTerms>0, nResultsBytes must be, too. */ - result = sqlite3_malloc(nResultBytes); - if( result==NULL ){ - sqlite3_free(pData); - return SQLITE_NOMEM; + c = (simple_tokenizer_cursor *) sqlite3_malloc(sizeof(*c)); + if( c==NULL ) return SQLITE_NOMEM; + + c->pInput = pInput; + if( pInput==0 ){ + c->nBytes = 0; + }else if( nBytes<0 ){ + c->nBytes = (int)strlen(pInput); + }else{ + c->nBytes = nBytes; } + c->iOffset = 0; /* start tokenizing at the beginning */ + c->iToken = 0; + c->pToken = NULL; /* no space allocated, yet. */ + c->nTokenAllocated = 0; - if( nTerms>1 ) qsort(pData, nTerms, sizeof(*pData), termDataCmp); + *ppCursor = &c->base; + return SQLITE_OK; +} - /* Read the terms in order to build the result. */ - iByte = 0; - for(iTerm=0; iTermpToken); + sqlite3_free(c); return SQLITE_OK; } -/* Implements dump_terms() for use in inspecting the fts3 index from -** tests. TEXT result containing the ordered list of terms joined by -** spaces. dump_terms(t, level, idx) dumps the terms for the segment -** specified by level, idx (in %_segdir), while dump_terms(t) dumps -** all terms in the index. In both cases t is the fts table's magic -** table-named column. +/* +** Extract the next token from a tokenization cursor. The cursor must +** have been opened by a prior call to simpleOpen(). */ -static void dumpTermsFunc( - sqlite3_context *pContext, - int argc, sqlite3_value **argv +static int simpleNext( + sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by simpleOpen */ + const char **ppToken, /* OUT: *ppToken is the token text */ + int *pnBytes, /* OUT: Number of bytes in token */ + int *piStartOffset, /* OUT: Starting offset of token */ + int *piEndOffset, /* OUT: Ending offset of token */ + int *piPosition /* OUT: Position integer of token */ ){ - fulltext_cursor *pCursor; - if( argc!=3 && argc!=1 ){ - generateError(pContext, "dump_terms", "incorrect arguments"); - }else if( sqlite3_value_type(argv[0])!=SQLITE_BLOB || - sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){ - generateError(pContext, "dump_terms", "illegal first argument"); - }else{ - fulltext_vtab *v; - fts3Hash terms; - sqlite3_stmt *s = NULL; - int rc; - - memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor)); - v = cursor_vtab(pCursor); + simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor; + simple_tokenizer *t = (simple_tokenizer *) pCursor->pTokenizer; + unsigned char *p = (unsigned char *)c->pInput; - /* If passed only the cursor column, get all segments. Otherwise - ** get the segment described by the following two arguments. - */ - if( argc==1 ){ - rc = sql_get_statement(v, SEGDIR_SELECT_ALL_STMT, &s); - }else{ - rc = sql_get_statement(v, SEGDIR_SELECT_SEGMENT_STMT, &s); - if( rc==SQLITE_OK ){ - rc = sqlite3_bind_int(s, 1, sqlite3_value_int(argv[1])); - if( rc==SQLITE_OK ){ - rc = sqlite3_bind_int(s, 2, sqlite3_value_int(argv[2])); - } - } - } + while( c->iOffsetnBytes ){ + int iStartOffset; - if( rc!=SQLITE_OK ){ - generateError(pContext, "dump_terms", NULL); - return; + /* Scan past delimiter characters */ + while( c->iOffsetnBytes && simpleDelim(t, p[c->iOffset]) ){ + c->iOffset++; } - /* Collect the terms for each segment. */ - sqlite3Fts3HashInit(&terms, FTS3_HASH_STRING, 1); - while( (rc = sqlite3_step(s))==SQLITE_ROW ){ - rc = collectSegmentTerms(v, s, &terms); - if( rc!=SQLITE_OK ) break; + /* Count non-delimiter characters. */ + iStartOffset = c->iOffset; + while( c->iOffsetnBytes && !simpleDelim(t, p[c->iOffset]) ){ + c->iOffset++; } - if( rc!=SQLITE_DONE ){ - sqlite3_reset(s); - generateError(pContext, "dump_terms", NULL); - }else{ - const int nTerms = fts3HashCount(&terms); - if( nTerms>0 ){ - rc = generateTermsResult(pContext, &terms); - if( rc==SQLITE_NOMEM ){ - generateError(pContext, "dump_terms", "out of memory"); - }else{ - assert( rc==SQLITE_OK ); - } - }else if( argc==3 ){ - /* The specific segment asked for could not be found. */ - generateError(pContext, "dump_terms", "segment not found"); - }else{ - /* No segments found. */ - /* TODO(shess): It should be impossible to reach this. This - ** case can only happen for an empty table, in which case - ** SQLite has no rows to call this function on. + if( c->iOffset>iStartOffset ){ + int i, n = c->iOffset-iStartOffset; + if( n>c->nTokenAllocated ){ + c->nTokenAllocated = n+20; + c->pToken = sqlite3_realloc(c->pToken, c->nTokenAllocated); + if( c->pToken==NULL ) return SQLITE_NOMEM; + } + for(i=0; ipToken[i] = (char)(ch<0x80 ? tolower(ch) : ch); } + *ppToken = c->pToken; + *pnBytes = n; + *piStartOffset = iStartOffset; + *piEndOffset = c->iOffset; + *piPosition = c->iToken++; + + return SQLITE_OK; } - sqlite3Fts3HashClear(&terms); } + return SQLITE_DONE; } -/* Expand the DL_DEFAULT doclist in pData into a text result in -** pContext. +/* +** The set of routines that implement the simple tokenizer */ -static void createDoclistResult(sqlite3_context *pContext, - const char *pData, int nData){ - DataBuffer dump; - DLReader dlReader; - - assert( pData!=NULL && nData>0 ); - - dataBufferInit(&dump, 0); - dlrInit(&dlReader, DL_DEFAULT, pData, nData); - for( ; !dlrAtEnd(&dlReader); dlrStep(&dlReader) ){ - char buf[256]; - PLReader plReader; +static const sqlite3_tokenizer_module simpleTokenizerModule = { + 0, + simpleCreate, + simpleDestroy, + simpleOpen, + simpleClose, + simpleNext, +}; - plrInit(&plReader, &dlReader); - if( DL_DEFAULT==DL_DOCIDS || plrAtEnd(&plReader) ){ - sqlite3_snprintf(sizeof(buf), buf, "[%lld] ", dlrDocid(&dlReader)); - dataBufferAppend(&dump, buf, strlen(buf)); - }else{ - int iColumn = plrColumn(&plReader); +/* +** Allocate a new simple tokenizer. Return a pointer to the new +** tokenizer in *ppModule +*/ +SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule( + sqlite3_tokenizer_module const**ppModule +){ + *ppModule = &simpleTokenizerModule; +} - sqlite3_snprintf(sizeof(buf), buf, "[%lld %d[", - dlrDocid(&dlReader), iColumn); - dataBufferAppend(&dump, buf, strlen(buf)); +#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ - for( ; !plrAtEnd(&plReader); plrStep(&plReader) ){ - if( plrColumn(&plReader)!=iColumn ){ - iColumn = plrColumn(&plReader); - sqlite3_snprintf(sizeof(buf), buf, "] %d[", iColumn); - assert( dump.nData>0 ); - dump.nData--; /* Overwrite trailing space. */ - assert( dump.pData[dump.nData]==' '); - dataBufferAppend(&dump, buf, strlen(buf)); - } - if( DL_DEFAULT==DL_POSITIONS_OFFSETS ){ - sqlite3_snprintf(sizeof(buf), buf, "%d,%d,%d ", - plrPosition(&plReader), - plrStartOffset(&plReader), plrEndOffset(&plReader)); - }else if( DL_DEFAULT==DL_POSITIONS ){ - sqlite3_snprintf(sizeof(buf), buf, "%d ", plrPosition(&plReader)); - }else{ - assert( NULL=="Unhandled DL_DEFAULT value"); - } - dataBufferAppend(&dump, buf, strlen(buf)); - } - plrDestroy(&plReader); +/************** End of fts3_tokenizer1.c *************************************/ +/************** Begin file fts3_write.c **************************************/ +/* +** 2009 Oct 23 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +****************************************************************************** +** +** This file is part of the SQLite FTS3 extension module. Specifically, +** this file contains code to insert, update and delete rows from FTS3 +** tables. It also contains code to merge FTS3 b-tree segments. Some +** of the sub-routines used to merge segments are also used by the query +** code in fts3.c. +*/ + +#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) + + +typedef struct PendingList PendingList; +typedef struct SegmentNode SegmentNode; +typedef struct SegmentWriter SegmentWriter; - assert( dump.nData>0 ); - dump.nData--; /* Overwrite trailing space. */ - assert( dump.pData[dump.nData]==' '); - dataBufferAppend(&dump, "]] ", 3); +/* +** Data structure used while accumulating terms in the pending-terms hash +** table. The hash table entry maps from term (a string) to a malloc'd +** instance of this structure. +*/ +struct PendingList { + int nData; + char *aData; + int nSpace; + sqlite3_int64 iLastDocid; + sqlite3_int64 iLastCol; + sqlite3_int64 iLastPos; +}; + +/* +** An instance of this structure is used to iterate through the terms on +** a contiguous set of segment b-tree leaf nodes. Although the details of +** this structure are only manipulated by code in this file, opaque handles +** of type Fts3SegReader* are also used by code in fts3.c to iterate through +** terms when querying the full-text index. See functions: +** +** sqlite3Fts3SegReaderNew() +** sqlite3Fts3SegReaderFree() +** sqlite3Fts3SegReaderIterate() +** +** Methods used to manipulate Fts3SegReader structures: +** +** fts3SegReaderNext() +** fts3SegReaderFirstDocid() +** fts3SegReaderNextDocid() +*/ +struct Fts3SegReader { + int iIdx; /* Index within level, or 0x7FFFFFFF for PT */ + sqlite3_int64 iStartBlock; + sqlite3_int64 iEndBlock; + sqlite3_stmt *pStmt; /* SQL Statement to access leaf nodes */ + char *aNode; /* Pointer to node data (or NULL) */ + int nNode; /* Size of buffer at aNode (or 0) */ + int nTermAlloc; /* Allocated size of zTerm buffer */ + Fts3HashElem **ppNextElem; + + /* Variables set by fts3SegReaderNext(). These may be read directly + ** by the caller. They are valid from the time SegmentReaderNew() returns + ** until SegmentReaderNext() returns something other than SQLITE_OK + ** (i.e. SQLITE_DONE). + */ + int nTerm; /* Number of bytes in current term */ + char *zTerm; /* Pointer to current term */ + char *aDoclist; /* Pointer to doclist of current entry */ + int nDoclist; /* Size of doclist in current entry */ + + /* The following variables are used to iterate through the current doclist */ + char *pOffsetList; + sqlite3_int64 iDocid; +}; + +#define fts3SegReaderIsPending(p) ((p)->ppNextElem!=0) + +/* +** An instance of this structure is used to create a segment b-tree in the +** database. The internal details of this type are only accessed by the +** following functions: +** +** fts3SegWriterAdd() +** fts3SegWriterFlush() +** fts3SegWriterFree() +*/ +struct SegmentWriter { + SegmentNode *pTree; /* Pointer to interior tree structure */ + sqlite3_int64 iFirst; /* First slot in %_segments written */ + sqlite3_int64 iFree; /* Next free slot in %_segments */ + char *zTerm; /* Pointer to previous term buffer */ + int nTerm; /* Number of bytes in zTerm */ + int nMalloc; /* Size of malloc'd buffer at zMalloc */ + char *zMalloc; /* Malloc'd space (possibly) used for zTerm */ + int nSize; /* Size of allocation at aData */ + int nData; /* Bytes of data in aData */ + char *aData; /* Pointer to block from malloc() */ +}; + +/* +** Type SegmentNode is used by the following three functions to create +** the interior part of the segment b+-tree structures (everything except +** the leaf nodes). These functions and type are only ever used by code +** within the fts3SegWriterXXX() family of functions described above. +** +** fts3NodeAddTerm() +** fts3NodeWrite() +** fts3NodeFree() +*/ +struct SegmentNode { + SegmentNode *pParent; /* Parent node (or NULL for root node) */ + SegmentNode *pRight; /* Pointer to right-sibling */ + SegmentNode *pLeftmost; /* Pointer to left-most node of this depth */ + int nEntry; /* Number of terms written to node so far */ + char *zTerm; /* Pointer to previous term buffer */ + int nTerm; /* Number of bytes in zTerm */ + int nMalloc; /* Size of malloc'd buffer at zMalloc */ + char *zMalloc; /* Malloc'd space (possibly) used for zTerm */ + int nData; /* Bytes of valid data so far */ + char *aData; /* Node data */ +}; + +/* +** Valid values for the second argument to fts3SqlStmt(). +*/ +#define SQL_DELETE_CONTENT 0 +#define SQL_IS_EMPTY 1 +#define SQL_DELETE_ALL_CONTENT 2 +#define SQL_DELETE_ALL_SEGMENTS 3 +#define SQL_DELETE_ALL_SEGDIR 4 +#define SQL_SELECT_CONTENT_BY_ROWID 5 +#define SQL_NEXT_SEGMENT_INDEX 6 +#define SQL_INSERT_SEGMENTS 7 +#define SQL_NEXT_SEGMENTS_ID 8 +#define SQL_INSERT_SEGDIR 9 +#define SQL_SELECT_LEVEL 10 +#define SQL_SELECT_ALL_LEVEL 11 +#define SQL_SELECT_LEVEL_COUNT 12 +#define SQL_SELECT_SEGDIR_COUNT_MAX 13 +#define SQL_DELETE_SEGDIR_BY_LEVEL 14 +#define SQL_DELETE_SEGMENTS_RANGE 15 +#define SQL_CONTENT_INSERT 16 +#define SQL_GET_BLOCK 17 + +/* +** This function is used to obtain an SQLite prepared statement handle +** for the statement identified by the second argument. If successful, +** *pp is set to the requested statement handle and SQLITE_OK returned. +** Otherwise, an SQLite error code is returned and *pp is set to 0. +** +** If argument apVal is not NULL, then it must point to an array with +** at least as many entries as the requested statement has bound +** parameters. The values are bound to the statements parameters before +** returning. +*/ +static int fts3SqlStmt( + Fts3Table *p, /* Virtual table handle */ + int eStmt, /* One of the SQL_XXX constants above */ + sqlite3_stmt **pp, /* OUT: Statement handle */ + sqlite3_value **apVal /* Values to bind to statement */ +){ + const char *azSql[] = { +/* 0 */ "DELETE FROM %Q.'%q_content' WHERE rowid = ?", +/* 1 */ "SELECT NOT EXISTS(SELECT docid FROM %Q.'%q_content' WHERE rowid!=?)", +/* 2 */ "DELETE FROM %Q.'%q_content'", +/* 3 */ "DELETE FROM %Q.'%q_segments'", +/* 4 */ "DELETE FROM %Q.'%q_segdir'", +/* 5 */ "SELECT * FROM %Q.'%q_content' WHERE rowid=?", +/* 6 */ "SELECT coalesce(max(idx)+1, 0) FROM %Q.'%q_segdir' WHERE level=?", +/* 7 */ "INSERT INTO %Q.'%q_segments'(blockid, block) VALUES(?, ?)", +/* 8 */ "SELECT coalesce(max(blockid)+1, 1) FROM %Q.'%q_segments'", +/* 9 */ "INSERT INTO %Q.'%q_segdir' VALUES(?,?,?,?,?,?)", + + /* Return segments in order from oldest to newest.*/ +/* 10 */ "SELECT idx, start_block, leaves_end_block, end_block, root " + "FROM %Q.'%q_segdir' WHERE level = ? ORDER BY idx ASC", +/* 11 */ "SELECT idx, start_block, leaves_end_block, end_block, root " + "FROM %Q.'%q_segdir' ORDER BY level DESC, idx ASC", + +/* 12 */ "SELECT count(*) FROM %Q.'%q_segdir' WHERE level = ?", +/* 13 */ "SELECT count(*), max(level) FROM %Q.'%q_segdir'", + +/* 14 */ "DELETE FROM %Q.'%q_segdir' WHERE level = ?", +/* 15 */ "DELETE FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ?", +/* 16 */ "INSERT INTO %Q.'%q_content' VALUES(%z)", +/* 17 */ "SELECT block FROM %Q.'%q_segments' WHERE blockid = ?", + }; + int rc = SQLITE_OK; + sqlite3_stmt *pStmt; + + assert( SizeofArray(azSql)==SizeofArray(p->aStmt) ); + assert( eStmt=0 ); + + pStmt = p->aStmt[eStmt]; + if( !pStmt ){ + char *zSql; + if( eStmt==SQL_CONTENT_INSERT ){ + int i; /* Iterator variable */ + char *zVarlist; /* The "?, ?, ..." string */ + zVarlist = (char *)sqlite3_malloc(2*p->nColumn+2); + if( !zVarlist ){ + *pp = 0; + return SQLITE_NOMEM; + } + zVarlist[0] = '?'; + zVarlist[p->nColumn*2+1] = '\0'; + for(i=1; i<=p->nColumn; i++){ + zVarlist[i*2-1] = ','; + zVarlist[i*2] = '?'; + } + zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName, zVarlist); + }else{ + zSql = sqlite3_mprintf(azSql[eStmt], p->zDb, p->zName); + } + if( !zSql ){ + rc = SQLITE_NOMEM; + }else{ + rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, NULL); + sqlite3_free(zSql); + assert( rc==SQLITE_OK || pStmt==0 ); + p->aStmt[eStmt] = pStmt; } } - dlrDestroy(&dlReader); + if( apVal ){ + int i; + int nParam = sqlite3_bind_parameter_count(pStmt); + for(i=0; rc==SQLITE_OK && i0 ); - dump.nData--; /* Overwrite trailing space. */ - assert( dump.pData[dump.nData]==' '); - dump.pData[dump.nData] = '\0'; - assert( dump.nData>0 ); - /* Passes ownership of dump's buffer to pContext. */ - sqlite3_result_text(pContext, dump.pData, dump.nData, sqlite3_free); - dump.pData = NULL; - dump.nData = dump.nCapacity = 0; +/* +** Read a single block from the %_segments table. If the specified block +** does not exist, return SQLITE_CORRUPT. If some other error (malloc, IO +** etc.) occurs, return the appropriate SQLite error code. +** +** Otherwise, if successful, set *pzBlock to point to a buffer containing +** the block read from the database, and *pnBlock to the size of the read +** block in bytes. +** +** WARNING: The returned buffer is only valid until the next call to +** sqlite3Fts3ReadBlock(). +*/ +SQLITE_PRIVATE int sqlite3Fts3ReadBlock( + Fts3Table *p, + sqlite3_int64 iBlock, + char const **pzBlock, + int *pnBlock +){ + sqlite3_stmt *pStmt; + int rc = fts3SqlStmt(p, SQL_GET_BLOCK, &pStmt, 0); + if( rc!=SQLITE_OK ) return rc; + sqlite3_reset(pStmt); + + if( pzBlock ){ + sqlite3_bind_int64(pStmt, 1, iBlock); + rc = sqlite3_step(pStmt); + if( rc!=SQLITE_ROW ){ + return (rc==SQLITE_DONE ? SQLITE_CORRUPT : rc); + } + + *pnBlock = sqlite3_column_bytes(pStmt, 0); + *pzBlock = (char *)sqlite3_column_blob(pStmt, 0); + if( sqlite3_column_type(pStmt, 0)!=SQLITE_BLOB ){ + return SQLITE_CORRUPT; + } + } + return SQLITE_OK; } -/* Implements dump_doclist() for use in inspecting the fts3 index from -** tests. TEXT result containing a string representation of the -** doclist for the indicated term. dump_doclist(t, term, level, idx) -** dumps the doclist for term from the segment specified by level, idx -** (in %_segdir), while dump_doclist(t, term) dumps the logical -** doclist for the term across all segments. The per-segment doclist -** can contain deletions, while the full-index doclist will not -** (deletions are omitted). +/* +** Set *ppStmt to a statement handle that may be used to iterate through +** all rows in the %_segdir table, from oldest to newest. If successful, +** return SQLITE_OK. If an error occurs while preparing the statement, +** return an SQLite error code. ** -** Result formats differ with the setting of DL_DEFAULTS. Examples: +** There is only ever one instance of this SQL statement compiled for +** each FTS3 table. ** -** DL_DOCIDS: [1] [3] [7] -** DL_POSITIONS: [1 0[0 4] 1[17]] [3 1[5]] -** DL_POSITIONS_OFFSETS: [1 0[0,0,3 4,23,26] 1[17,102,105]] [3 1[5,20,23]] +** The statement returns the following columns from the %_segdir table: ** -** In each case the number after the outer '[' is the docid. In the -** latter two cases, the number before the inner '[' is the column -** associated with the values within. For DL_POSITIONS the numbers -** within are the positions, for DL_POSITIONS_OFFSETS they are the -** position, the start offset, and the end offset. +** 0: idx +** 1: start_block +** 2: leaves_end_block +** 3: end_block +** 4: root */ -static void dumpDoclistFunc( - sqlite3_context *pContext, - int argc, sqlite3_value **argv +SQLITE_PRIVATE int sqlite3Fts3AllSegdirs(Fts3Table *p, sqlite3_stmt **ppStmt){ + return fts3SqlStmt(p, SQL_SELECT_ALL_LEVEL, ppStmt, 0); +} + + +/* +** Append a single varint to a PendingList buffer. SQLITE_OK is returned +** if successful, or an SQLite error code otherwise. +** +** This function also serves to allocate the PendingList structure itself. +** For example, to create a new PendingList structure containing two +** varints: +** +** PendingList *p = 0; +** fts3PendingListAppendVarint(&p, 1); +** fts3PendingListAppendVarint(&p, 2); +*/ +static int fts3PendingListAppendVarint( + PendingList **pp, /* IN/OUT: Pointer to PendingList struct */ + sqlite3_int64 i /* Value to append to data */ ){ - fulltext_cursor *pCursor; - if( argc!=2 && argc!=4 ){ - generateError(pContext, "dump_doclist", "incorrect arguments"); - }else if( sqlite3_value_type(argv[0])!=SQLITE_BLOB || - sqlite3_value_bytes(argv[0])!=sizeof(pCursor) ){ - generateError(pContext, "dump_doclist", "illegal first argument"); - }else if( sqlite3_value_text(argv[1])==NULL || - sqlite3_value_text(argv[1])[0]=='\0' ){ - generateError(pContext, "dump_doclist", "empty second argument"); - }else{ - const char *pTerm = (const char *)sqlite3_value_text(argv[1]); - const int nTerm = strlen(pTerm); - fulltext_vtab *v; - int rc; - DataBuffer doclist; + PendingList *p = *pp; - memcpy(&pCursor, sqlite3_value_blob(argv[0]), sizeof(pCursor)); - v = cursor_vtab(pCursor); + /* Allocate or grow the PendingList as required. */ + if( !p ){ + p = sqlite3_malloc(sizeof(*p) + 100); + if( !p ){ + return SQLITE_NOMEM; + } + p->nSpace = 100; + p->aData = (char *)&p[1]; + p->nData = 0; + } + else if( p->nData+FTS3_VARINT_MAX+1>p->nSpace ){ + int nNew = p->nSpace * 2; + p = sqlite3_realloc(p, sizeof(*p) + nNew); + if( !p ){ + sqlite3_free(*pp); + *pp = 0; + return SQLITE_NOMEM; + } + p->nSpace = nNew; + p->aData = (char *)&p[1]; + } - dataBufferInit(&doclist, 0); + /* Append the new serialized varint to the end of the list. */ + p->nData += sqlite3Fts3PutVarint(&p->aData[p->nData], i); + p->aData[p->nData] = '\0'; + *pp = p; + return SQLITE_OK; +} - /* termSelect() yields the same logical doclist that queries are - ** run against. - */ - if( argc==2 ){ - rc = termSelect(v, v->nColumn, pTerm, nTerm, 0, DL_DEFAULT, &doclist); - }else{ - sqlite3_stmt *s = NULL; +/* +** Add a docid/column/position entry to a PendingList structure. Non-zero +** is returned if the structure is sqlite3_realloced as part of adding +** the entry. Otherwise, zero. +** +** If an OOM error occurs, *pRc is set to SQLITE_NOMEM before returning. +** Zero is always returned in this case. Otherwise, if no OOM error occurs, +** it is set to SQLITE_OK. +*/ +static int fts3PendingListAppend( + PendingList **pp, /* IN/OUT: PendingList structure */ + sqlite3_int64 iDocid, /* Docid for entry to add */ + sqlite3_int64 iCol, /* Column for entry to add */ + sqlite3_int64 iPos, /* Position of term for entry to add */ + int *pRc /* OUT: Return code */ +){ + PendingList *p = *pp; + int rc = SQLITE_OK; - /* Get our specific segment's information. */ - rc = sql_get_statement(v, SEGDIR_SELECT_SEGMENT_STMT, &s); - if( rc==SQLITE_OK ){ - rc = sqlite3_bind_int(s, 1, sqlite3_value_int(argv[2])); - if( rc==SQLITE_OK ){ - rc = sqlite3_bind_int(s, 2, sqlite3_value_int(argv[3])); - } - } + assert( !p || p->iLastDocid<=iDocid ); - if( rc==SQLITE_OK ){ - rc = sqlite3_step(s); + if( !p || p->iLastDocid!=iDocid ){ + sqlite3_int64 iDelta = iDocid - (p ? p->iLastDocid : 0); + if( p ){ + assert( p->nDatanSpace ); + assert( p->aData[p->nData]==0 ); + p->nData++; + } + if( SQLITE_OK!=(rc = fts3PendingListAppendVarint(&p, iDelta)) ){ + goto pendinglistappend_out; + } + p->iLastCol = -1; + p->iLastPos = 0; + p->iLastDocid = iDocid; + } + if( iCol>0 && p->iLastCol!=iCol ){ + if( SQLITE_OK!=(rc = fts3PendingListAppendVarint(&p, 1)) + || SQLITE_OK!=(rc = fts3PendingListAppendVarint(&p, iCol)) + ){ + goto pendinglistappend_out; + } + p->iLastCol = iCol; + p->iLastPos = 0; + } + if( iCol>=0 ){ + assert( iPos>p->iLastPos || (iPos==0 && p->iLastPos==0) ); + rc = fts3PendingListAppendVarint(&p, 2+iPos-p->iLastPos); + if( rc==SQLITE_OK ){ + p->iLastPos = iPos; + } + } - if( rc==SQLITE_DONE ){ - dataBufferDestroy(&doclist); - generateError(pContext, "dump_doclist", "segment not found"); - return; - } + pendinglistappend_out: + *pRc = rc; + if( p!=*pp ){ + *pp = p; + return 1; + } + return 0; +} - /* Found a segment, load it into doclist. */ - if( rc==SQLITE_ROW ){ - const sqlite_int64 iLeavesEnd = sqlite3_column_int64(s, 1); - const char *pData = sqlite3_column_blob(s, 2); - const int nData = sqlite3_column_bytes(s, 2); +/* +** Tokenize the nul-terminated string zText and add all tokens to the +** pending-terms hash-table. The docid used is that currently stored in +** p->iPrevDocid, and the column is specified by argument iCol. +** +** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code. +*/ +static int fts3PendingTermsAdd(Fts3Table *p, const char *zText, int iCol){ + int rc; + int iStart; + int iEnd; + int iPos; - /* loadSegment() is used by termSelect() to load each - ** segment's data. - */ - rc = loadSegment(v, pData, nData, iLeavesEnd, pTerm, nTerm, 0, - &doclist); - if( rc==SQLITE_OK ){ - rc = sqlite3_step(s); + char const *zToken; + int nToken; - /* Should not have more than one matching segment. */ - if( rc!=SQLITE_DONE ){ - sqlite3_reset(s); - dataBufferDestroy(&doclist); - generateError(pContext, "dump_doclist", "invalid segdir"); - return; - } - rc = SQLITE_OK; - } - } - } + sqlite3_tokenizer *pTokenizer = p->pTokenizer; + sqlite3_tokenizer_module const *pModule = pTokenizer->pModule; + sqlite3_tokenizer_cursor *pCsr; + int (*xNext)(sqlite3_tokenizer_cursor *pCursor, + const char**,int*,int*,int*,int*); + + assert( pTokenizer && pModule ); + + rc = pModule->xOpen(pTokenizer, zText, -1, &pCsr); + if( rc!=SQLITE_OK ){ + return rc; + } + pCsr->pTokenizer = pTokenizer; + + xNext = pModule->xNext; + while( SQLITE_OK==rc + && SQLITE_OK==(rc = xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos)) + ){ + PendingList *pList; - sqlite3_reset(s); + /* Positions cannot be negative; we use -1 as a terminator internally. + ** Tokens must have a non-zero length. + */ + if( iPos<0 || !zToken || nToken<=0 ){ + rc = SQLITE_ERROR; + break; } - if( rc==SQLITE_OK ){ - if( doclist.nData>0 ){ - createDoclistResult(pContext, doclist.pData, doclist.nData); - }else{ - /* TODO(shess): This can happen if the term is not present, or - ** if all instances of the term have been deleted and this is - ** an all-index dump. It may be interesting to distinguish - ** these cases. + pList = (PendingList *)fts3HashFind(&p->pendingTerms, zToken, nToken); + if( pList ){ + p->nPendingData -= (pList->nData + nToken + sizeof(Fts3HashElem)); + } + if( fts3PendingListAppend(&pList, p->iPrevDocid, iCol, iPos, &rc) ){ + if( pList==fts3HashInsert(&p->pendingTerms, zToken, nToken, pList) ){ + /* Malloc failed while inserting the new entry. This can only + ** happen if there was no previous entry for this token. */ - sqlite3_result_text(pContext, "", 0, SQLITE_STATIC); + assert( 0==fts3HashFind(&p->pendingTerms, zToken, nToken) ); + sqlite3_free(pList); + rc = SQLITE_NOMEM; } - }else if( rc==SQLITE_NOMEM ){ - /* Handle out-of-memory cases specially because if they are - ** generated in fts3 code they may not be reflected in the db - ** handle. - */ - /* TODO(shess): Handle this more comprehensively. - ** sqlite3ErrStr() has what I need, but is internal. - */ - generateError(pContext, "dump_doclist", "out of memory"); - }else{ - generateError(pContext, "dump_doclist", NULL); } + if( rc==SQLITE_OK ){ + p->nPendingData += (pList->nData + nToken + sizeof(Fts3HashElem)); + } + } + + pModule->xClose(pCsr); + return (rc==SQLITE_DONE ? SQLITE_OK : rc); +} - dataBufferDestroy(&doclist); +/* +** Calling this function indicates that subsequent calls to +** fts3PendingTermsAdd() are to add term/position-list pairs for the +** contents of the document with docid iDocid. +*/ +static int fts3PendingTermsDocid(Fts3Table *p, sqlite_int64 iDocid){ + /* TODO(shess) Explore whether partially flushing the buffer on + ** forced-flush would provide better performance. I suspect that if + ** we ordered the doclists by size and flushed the largest until the + ** buffer was half empty, that would let the less frequent terms + ** generate longer doclists. + */ + if( iDocid<=p->iPrevDocid || p->nPendingData>p->nMaxPendingData ){ + int rc = sqlite3Fts3PendingTermsFlush(p); + if( rc!=SQLITE_OK ) return rc; } + p->iPrevDocid = iDocid; + return SQLITE_OK; +} + +SQLITE_PRIVATE void sqlite3Fts3PendingTermsClear(Fts3Table *p){ + Fts3HashElem *pElem; + for(pElem=fts3HashFirst(&p->pendingTerms); pElem; pElem=fts3HashNext(pElem)){ + sqlite3_free(fts3HashData(pElem)); + } + fts3HashClear(&p->pendingTerms); + p->nPendingData = 0; } -#endif /* -** This routine implements the xFindFunction method for the FTS3 -** virtual table. +** This function is called by the xUpdate() method as part of an INSERT +** operation. It adds entries for each term in the new record to the +** pendingTerms hash table. +** +** Argument apVal is the same as the similarly named argument passed to +** fts3InsertData(). Parameter iDocid is the docid of the new row. */ -static int fulltextFindFunction( - sqlite3_vtab *pVtab, - int nArg, - const char *zName, - void (**pxFunc)(sqlite3_context*,int,sqlite3_value**), - void **ppArg -){ - if( strcmp(zName,"snippet")==0 ){ - *pxFunc = snippetFunc; - return 1; - }else if( strcmp(zName,"offsets")==0 ){ - *pxFunc = snippetOffsetsFunc; - return 1; - }else if( strcmp(zName,"optimize")==0 ){ - *pxFunc = optimizeFunc; - return 1; -#ifdef SQLITE_TEST - /* NOTE(shess): These functions are present only for testing - ** purposes. No particular effort is made to optimize their - ** execution or how they build their results. - */ - }else if( strcmp(zName,"dump_terms")==0 ){ - /* fprintf(stderr, "Found dump_terms\n"); */ - *pxFunc = dumpTermsFunc; - return 1; - }else if( strcmp(zName,"dump_doclist")==0 ){ - /* fprintf(stderr, "Found dump_doclist\n"); */ - *pxFunc = dumpDoclistFunc; - return 1; -#endif +static int fts3InsertTerms(Fts3Table *p, sqlite3_value **apVal){ + int i; /* Iterator variable */ + for(i=2; inColumn+2; i++){ + const char *zText = (const char *)sqlite3_value_text(apVal[i]); + if( zText ){ + int rc = fts3PendingTermsAdd(p, zText, i-2); + if( rc!=SQLITE_OK ){ + return rc; + } + } } - return 0; + return SQLITE_OK; } /* -** Rename an fts3 table. +** This function is called by the xUpdate() method for an INSERT operation. +** The apVal parameter is passed a copy of the apVal argument passed by +** SQLite to the xUpdate() method. i.e: +** +** apVal[0] Not used for INSERT. +** apVal[1] rowid +** apVal[2] Left-most user-defined column +** ... +** apVal[p->nColumn+1] Right-most user-defined column +** apVal[p->nColumn+2] Hidden column with same name as table +** apVal[p->nColumn+3] Hidden "docid" column (alias for rowid) */ -static int fulltextRename( - sqlite3_vtab *pVtab, - const char *zName +static int fts3InsertData( + Fts3Table *p, /* Full-text table */ + sqlite3_value **apVal, /* Array of values to insert */ + sqlite3_int64 *piDocid /* OUT: Docid for row just inserted */ ){ - fulltext_vtab *p = (fulltext_vtab *)pVtab; - int rc = SQLITE_NOMEM; - char *zSql = sqlite3_mprintf( - "ALTER TABLE %Q.'%q_content' RENAME TO '%q_content';" - "ALTER TABLE %Q.'%q_segments' RENAME TO '%q_segments';" - "ALTER TABLE %Q.'%q_segdir' RENAME TO '%q_segdir';" - , p->zDb, p->zName, zName - , p->zDb, p->zName, zName - , p->zDb, p->zName, zName - ); - if( zSql ){ - rc = sqlite3_exec(p->db, zSql, 0, 0, 0); - sqlite3_free(zSql); + int rc; /* Return code */ + sqlite3_stmt *pContentInsert; /* INSERT INTO %_content VALUES(...) */ + + /* Locate the statement handle used to insert data into the %_content + ** table. The SQL for this statement is: + ** + ** INSERT INTO %_content VALUES(?, ?, ?, ...) + ** + ** The statement features N '?' variables, where N is the number of user + ** defined columns in the FTS3 table, plus one for the docid field. + */ + rc = fts3SqlStmt(p, SQL_CONTENT_INSERT, &pContentInsert, &apVal[1]); + if( rc!=SQLITE_OK ){ + return rc; + } + + /* There is a quirk here. The users INSERT statement may have specified + ** a value for the "rowid" field, for the "docid" field, or for both. + ** Which is a problem, since "rowid" and "docid" are aliases for the + ** same value. For example: + ** + ** INSERT INTO fts3tbl(rowid, docid) VALUES(1, 2); + ** + ** In FTS3, this is an error. It is an error to specify non-NULL values + ** for both docid and some other rowid alias. + */ + if( SQLITE_NULL!=sqlite3_value_type(apVal[3+p->nColumn]) ){ + if( SQLITE_NULL==sqlite3_value_type(apVal[0]) + && SQLITE_NULL!=sqlite3_value_type(apVal[1]) + ){ + /* A rowid/docid conflict. */ + return SQLITE_ERROR; + } + rc = sqlite3_bind_value(pContentInsert, 1, apVal[3+p->nColumn]); + if( rc!=SQLITE_OK ) return rc; } + + /* Execute the statement to insert the record. Set *piDocid to the + ** new docid value. + */ + sqlite3_step(pContentInsert); + rc = sqlite3_reset(pContentInsert); + + *piDocid = sqlite3_last_insert_rowid(p->db); return rc; } -static const sqlite3_module fts3Module = { - /* iVersion */ 0, - /* xCreate */ fulltextCreate, - /* xConnect */ fulltextConnect, - /* xBestIndex */ fulltextBestIndex, - /* xDisconnect */ fulltextDisconnect, - /* xDestroy */ fulltextDestroy, - /* xOpen */ fulltextOpen, - /* xClose */ fulltextClose, - /* xFilter */ fulltextFilter, - /* xNext */ fulltextNext, - /* xEof */ fulltextEof, - /* xColumn */ fulltextColumn, - /* xRowid */ fulltextRowid, - /* xUpdate */ fulltextUpdate, - /* xBegin */ fulltextBegin, - /* xSync */ fulltextSync, - /* xCommit */ fulltextCommit, - /* xRollback */ fulltextRollback, - /* xFindFunction */ fulltextFindFunction, - /* xRename */ fulltextRename, -}; -static void hashDestroy(void *p){ - fts3Hash *pHash = (fts3Hash *)p; - sqlite3Fts3HashClear(pHash); - sqlite3_free(pHash); -} /* -** The fts3 built-in tokenizers - "simple" and "porter" - are implemented -** in files fts3_tokenizer1.c and fts3_porter.c respectively. The following -** two forward declarations are for functions declared in these files -** used to retrieve the respective implementations. -** -** Calling sqlite3Fts3SimpleTokenizerModule() sets the value pointed -** to by the argument to point a the "simple" tokenizer implementation. -** Function ...PorterTokenizerModule() sets *pModule to point to the -** porter tokenizer/stemmer implementation. +** Remove all data from the FTS3 table. Clear the hash table containing +** pending terms. */ -SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule); -SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule(sqlite3_tokenizer_module const**ppModule); -SQLITE_PRIVATE void sqlite3Fts3IcuTokenizerModule(sqlite3_tokenizer_module const**ppModule); +static int fts3DeleteAll(Fts3Table *p){ + int rc; /* Return code */ + + /* Discard the contents of the pending-terms hash table. */ + sqlite3Fts3PendingTermsClear(p); -SQLITE_PRIVATE int sqlite3Fts3InitHashTable(sqlite3 *, fts3Hash *, const char *); + /* Delete everything from the %_content, %_segments and %_segdir tables. */ + rc = fts3SqlExec(p, SQL_DELETE_ALL_CONTENT, 0); + if( rc==SQLITE_OK ){ + rc = fts3SqlExec(p, SQL_DELETE_ALL_SEGMENTS, 0); + } + if( rc==SQLITE_OK ){ + rc = fts3SqlExec(p, SQL_DELETE_ALL_SEGDIR, 0); + } + return rc; +} /* -** Initialise the fts3 extension. If this extension is built as part -** of the sqlite library, then this function is called directly by -** SQLite. If fts3 is built as a dynamically loadable extension, this -** function is called by the sqlite3_extension_init() entry point. +** The first element in the apVal[] array is assumed to contain the docid +** (an integer) of a row about to be deleted. Remove all terms from the +** full-text index. */ -SQLITE_PRIVATE int sqlite3Fts3Init(sqlite3 *db){ - int rc = SQLITE_OK; - fts3Hash *pHash = 0; - const sqlite3_tokenizer_module *pSimple = 0; - const sqlite3_tokenizer_module *pPorter = 0; - const sqlite3_tokenizer_module *pIcu = 0; - - sqlite3Fts3SimpleTokenizerModule(&pSimple); - sqlite3Fts3PorterTokenizerModule(&pPorter); -#ifdef SQLITE_ENABLE_ICU - sqlite3Fts3IcuTokenizerModule(&pIcu); -#endif +static int fts3DeleteTerms(Fts3Table *p, sqlite3_value **apVal){ + int rc; + sqlite3_stmt *pSelect; - /* Allocate and initialise the hash-table used to store tokenizers. */ - pHash = sqlite3_malloc(sizeof(fts3Hash)); - if( !pHash ){ - rc = SQLITE_NOMEM; + rc = fts3SqlStmt(p, SQL_SELECT_CONTENT_BY_ROWID, &pSelect, apVal); + if( rc==SQLITE_OK ){ + if( SQLITE_ROW==sqlite3_step(pSelect) ){ + int i; + for(i=1; i<=p->nColumn; i++){ + const char *zText = (const char *)sqlite3_column_text(pSelect, i); + rc = fts3PendingTermsAdd(p, zText, -1); + if( rc!=SQLITE_OK ){ + sqlite3_reset(pSelect); + return rc; + } + } + } + rc = sqlite3_reset(pSelect); }else{ - sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1); + sqlite3_reset(pSelect); } + return rc; +} - /* Load the built-in tokenizers into the hash table */ +/* +** Forward declaration to account for the circular dependency between +** functions fts3SegmentMerge() and fts3AllocateSegdirIdx(). +*/ +static int fts3SegmentMerge(Fts3Table *, int); + +/* +** This function allocates a new level iLevel index in the segdir table. +** Usually, indexes are allocated within a level sequentially starting +** with 0, so the allocated index is one greater than the value returned +** by: +** +** SELECT max(idx) FROM %_segdir WHERE level = :iLevel +** +** However, if there are already FTS3_MERGE_COUNT indexes at the requested +** level, they are merged into a single level (iLevel+1) segment and the +** allocated index is 0. +** +** If successful, *piIdx is set to the allocated index slot and SQLITE_OK +** returned. Otherwise, an SQLite error code is returned. +*/ +static int fts3AllocateSegdirIdx(Fts3Table *p, int iLevel, int *piIdx){ + int rc; /* Return Code */ + sqlite3_stmt *pNextIdx; /* Query for next idx at level iLevel */ + int iNext = 0; /* Result of query pNextIdx */ + + /* Set variable iNext to the next available segdir index at level iLevel. */ + rc = fts3SqlStmt(p, SQL_NEXT_SEGMENT_INDEX, &pNextIdx, 0); if( rc==SQLITE_OK ){ - if( sqlite3Fts3HashInsert(pHash, "simple", 7, (void *)pSimple) - || sqlite3Fts3HashInsert(pHash, "porter", 7, (void *)pPorter) - || (pIcu && sqlite3Fts3HashInsert(pHash, "icu", 4, (void *)pIcu)) - ){ - rc = SQLITE_NOMEM; + sqlite3_bind_int(pNextIdx, 1, iLevel); + if( SQLITE_ROW==sqlite3_step(pNextIdx) ){ + iNext = sqlite3_column_int(pNextIdx, 0); } + rc = sqlite3_reset(pNextIdx); } -#ifdef SQLITE_TEST - sqlite3Fts3ExprInitTestInterface(db); -#endif - - /* Create the virtual table wrapper around the hash-table and overload - ** the two scalar functions. If this is successful, register the - ** module with sqlite. - */ - if( SQLITE_OK==rc - && SQLITE_OK==(rc = sqlite3Fts3InitHashTable(db, pHash, "fts3_tokenizer")) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "snippet", -1)) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "offsets", -1)) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "optimize", -1)) -#ifdef SQLITE_TEST - && SQLITE_OK==(rc = sqlite3_overload_function(db, "dump_terms", -1)) - && SQLITE_OK==(rc = sqlite3_overload_function(db, "dump_doclist", -1)) -#endif - ){ - return sqlite3_create_module_v2( - db, "fts3", &fts3Module, (void *)pHash, hashDestroy - ); + if( rc==SQLITE_OK ){ + /* If iNext is FTS3_MERGE_COUNT, indicating that level iLevel is already + ** full, merge all segments in level iLevel into a single iLevel+1 + ** segment and allocate (newly freed) index 0 at level iLevel. Otherwise, + ** if iNext is less than FTS3_MERGE_COUNT, allocate index iNext. + */ + if( iNext>=FTS3_MERGE_COUNT ){ + rc = fts3SegmentMerge(p, iLevel); + *piIdx = 0; + }else{ + *piIdx = iNext; + } } - /* An error has occurred. Delete the hash table and return the error code. */ - assert( rc!=SQLITE_OK ); - if( pHash ){ - sqlite3Fts3HashClear(pHash); - sqlite3_free(pHash); - } return rc; } -#if !SQLITE_CORE -SQLITE_API int sqlite3_extension_init( - sqlite3 *db, - char **pzErrMsg, - const sqlite3_api_routines *pApi -){ - SQLITE_EXTENSION_INIT2(pApi) - return sqlite3Fts3Init(db); -} -#endif - -#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ - -/************** End of fts3.c ************************************************/ -/************** Begin file fts3_expr.c ***************************************/ /* -** 2008 Nov 28 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This module contains code that implements a parser for fts3 query strings -** (the right-hand argument to the MATCH operator). Because the supported -** syntax is relatively simple, the whole tokenizer/parser system is -** hand-coded. The public interface to this module is declared in source -** code file "fts3_expr.h". +** Move the iterator passed as the first argument to the next term in the +** segment. If successful, SQLITE_OK is returned. If there is no next term, +** SQLITE_DONE. Otherwise, an SQLite error code. */ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) +static int fts3SegReaderNext(Fts3SegReader *pReader){ + char *pNext; /* Cursor variable */ + int nPrefix; /* Number of bytes in term prefix */ + int nSuffix; /* Number of bytes in term suffix */ + + if( !pReader->aDoclist ){ + pNext = pReader->aNode; + }else{ + pNext = &pReader->aDoclist[pReader->nDoclist]; + } + + if( !pNext || pNext>=&pReader->aNode[pReader->nNode] ){ + int rc; + if( fts3SegReaderIsPending(pReader) ){ + Fts3HashElem *pElem = *(pReader->ppNextElem); + if( pElem==0 ){ + pReader->aNode = 0; + }else{ + PendingList *pList = (PendingList *)fts3HashData(pElem); + pReader->zTerm = (char *)fts3HashKey(pElem); + pReader->nTerm = fts3HashKeysize(pElem); + pReader->nNode = pReader->nDoclist = pList->nData + 1; + pReader->aNode = pReader->aDoclist = pList->aData; + pReader->ppNextElem++; + assert( pReader->aNode ); + } + return SQLITE_OK; + } + if( !pReader->pStmt ){ + pReader->aNode = 0; + return SQLITE_OK; + } + rc = sqlite3_step(pReader->pStmt); + if( rc!=SQLITE_ROW ){ + pReader->aNode = 0; + return (rc==SQLITE_DONE ? SQLITE_OK : rc); + } + pReader->nNode = sqlite3_column_bytes(pReader->pStmt, 0); + pReader->aNode = (char *)sqlite3_column_blob(pReader->pStmt, 0); + pNext = pReader->aNode; + } + + pNext += sqlite3Fts3GetVarint32(pNext, &nPrefix); + pNext += sqlite3Fts3GetVarint32(pNext, &nSuffix); + + if( nPrefix+nSuffix>pReader->nTermAlloc ){ + int nNew = (nPrefix+nSuffix)*2; + char *zNew = sqlite3_realloc(pReader->zTerm, nNew); + if( !zNew ){ + return SQLITE_NOMEM; + } + pReader->zTerm = zNew; + pReader->nTermAlloc = nNew; + } + memcpy(&pReader->zTerm[nPrefix], pNext, nSuffix); + pReader->nTerm = nPrefix+nSuffix; + pNext += nSuffix; + pNext += sqlite3Fts3GetVarint32(pNext, &pReader->nDoclist); + assert( pNext<&pReader->aNode[pReader->nNode] ); + pReader->aDoclist = pNext; + pReader->pOffsetList = 0; + return SQLITE_OK; +} /* -** By default, this module parses the legacy syntax that has been -** traditionally used by fts3. Or, if SQLITE_ENABLE_FTS3_PARENTHESIS -** is defined, then it uses the new syntax. The differences between -** the new and the old syntaxes are: -** -** a) The new syntax supports parenthesis. The old does not. -** -** b) The new syntax supports the AND and NOT operators. The old does not. -** -** c) The old syntax supports the "-" token qualifier. This is not -** supported by the new syntax (it is replaced by the NOT operator). -** -** d) When using the old syntax, the OR operator has a greater precedence -** than an implicit AND. When using the new, both implicity and explicit -** AND operators have a higher precedence than OR. -** -** If compiled with SQLITE_TEST defined, then this module exports the -** symbol "int sqlite3_fts3_enable_parentheses". Setting this variable -** to zero causes the module to use the old syntax. If it is set to -** non-zero the new syntax is activated. This is so both syntaxes can -** be tested using a single build of testfixture. +** Set the SegReader to point to the first docid in the doclist associated +** with the current term. */ -#ifdef SQLITE_TEST -SQLITE_API int sqlite3_fts3_enable_parentheses = 0; -#else -# ifdef SQLITE_ENABLE_FTS3_PARENTHESIS -# define sqlite3_fts3_enable_parentheses 1 -# else -# define sqlite3_fts3_enable_parentheses 0 -# endif -#endif +static void fts3SegReaderFirstDocid(Fts3SegReader *pReader){ + int n; + assert( pReader->aDoclist ); + assert( !pReader->pOffsetList ); + n = sqlite3Fts3GetVarint(pReader->aDoclist, &pReader->iDocid); + pReader->pOffsetList = &pReader->aDoclist[n]; +} /* -** Default span for NEAR operators. -*/ -#define SQLITE_FTS3_DEFAULT_NEAR_PARAM 10 +** Advance the SegReader to point to the next docid in the doclist +** associated with the current term. +** +** If arguments ppOffsetList and pnOffsetList are not NULL, then +** *ppOffsetList is set to point to the first column-offset list +** in the doclist entry (i.e. immediately past the docid varint). +** *pnOffsetList is set to the length of the set of column-offset +** lists, not including the nul-terminator byte. For example: +*/ +static void fts3SegReaderNextDocid( + Fts3SegReader *pReader, + char **ppOffsetList, + int *pnOffsetList +){ + char *p = pReader->pOffsetList; + char c = 0; + + /* Pointer p currently points at the first byte of an offset list. The + ** following two lines advance it to point one byte past the end of + ** the same offset list. + */ + while( *p | c ) c = *p++ & 0x80; + p++; + /* If required, populate the output variables with a pointer to and the + ** size of the previous offset-list. + */ + if( ppOffsetList ){ + *ppOffsetList = pReader->pOffsetList; + *pnOffsetList = (int)(p - pReader->pOffsetList - 1); + } -typedef struct ParseContext ParseContext; -struct ParseContext { - sqlite3_tokenizer *pTokenizer; /* Tokenizer module */ - const char **azCol; /* Array of column names for fts3 table */ - int nCol; /* Number of entries in azCol[] */ - int iDefaultCol; /* Default column to query */ - sqlite3_context *pCtx; /* Write error message here */ - int nNest; /* Number of nested brackets */ -}; + /* If there are no more entries in the doclist, set pOffsetList to + ** NULL. Otherwise, set Fts3SegReader.iDocid to the next docid and + ** Fts3SegReader.pOffsetList to point to the next offset list before + ** returning. + */ + if( p>=&pReader->aDoclist[pReader->nDoclist] ){ + pReader->pOffsetList = 0; + }else{ + sqlite3_int64 iDelta; + pReader->pOffsetList = p + sqlite3Fts3GetVarint(p, &iDelta); + pReader->iDocid += iDelta; + } +} /* -** This function is equivalent to the standard isspace() function. -** -** The standard isspace() can be awkward to use safely, because although it -** is defined to accept an argument of type int, its behaviour when passed -** an integer that falls outside of the range of the unsigned char type -** is undefined (and sometimes, "undefined" means segfault). This wrapper -** is defined to accept an argument of type char, and always returns 0 for -** any values that fall outside of the range of the unsigned char type (i.e. -** negative values). +** Free all allocations associated with the iterator passed as the +** second argument. */ -static int fts3isspace(char c){ - return (c&0x80)==0 ? isspace(c) : 0; +SQLITE_PRIVATE void sqlite3Fts3SegReaderFree(Fts3Table *p, Fts3SegReader *pReader){ + if( pReader ){ + if( pReader->pStmt ){ + /* Move the leaf-range SELECT statement to the aLeavesStmt[] array, + ** so that it can be reused when required by another query. + */ + assert( p->nLeavesStmtnLeavesTotal ); + sqlite3_reset(pReader->pStmt); + p->aLeavesStmt[p->nLeavesStmt++] = pReader->pStmt; + } + if( !fts3SegReaderIsPending(pReader) ){ + sqlite3_free(pReader->zTerm); + } + sqlite3_free(pReader); + } } /* -** Extract the next token from buffer z (length n) using the tokenizer -** and other information (column names etc.) in pParse. Create an Fts3Expr -** structure of type FTSQUERY_PHRASE containing a phrase consisting of this -** single token and set *ppExpr to point to it. If the end of the buffer is -** reached before a token is found, set *ppExpr to zero. It is the -** responsibility of the caller to eventually deallocate the allocated -** Fts3Expr structure (if any) by passing it to sqlite3_free(). -** -** Return SQLITE_OK if successful, or SQLITE_NOMEM if a memory allocation -** fails. +** Allocate a new SegReader object. */ -static int getNextToken( - ParseContext *pParse, /* fts3 query parse context */ - int iCol, /* Value for Fts3Phrase.iColumn */ - const char *z, int n, /* Input string */ - Fts3Expr **ppExpr, /* OUT: expression */ - int *pnConsumed /* OUT: Number of bytes consumed */ +SQLITE_PRIVATE int sqlite3Fts3SegReaderNew( + Fts3Table *p, /* Virtual table handle */ + int iAge, /* Segment "age". */ + sqlite3_int64 iStartLeaf, /* First leaf to traverse */ + sqlite3_int64 iEndLeaf, /* Final leaf to traverse */ + sqlite3_int64 iEndBlock, /* Final block of segment */ + const char *zRoot, /* Buffer containing root node */ + int nRoot, /* Size of buffer containing root node */ + Fts3SegReader **ppReader /* OUT: Allocated Fts3SegReader */ ){ - sqlite3_tokenizer *pTokenizer = pParse->pTokenizer; - sqlite3_tokenizer_module const *pModule = pTokenizer->pModule; - int rc; - sqlite3_tokenizer_cursor *pCursor; - Fts3Expr *pRet = 0; - int nConsumed = 0; - - rc = pModule->xOpen(pTokenizer, z, n, &pCursor); - if( rc==SQLITE_OK ){ - const char *zToken; - int nToken, iStart, iEnd, iPosition; - int nByte; /* total space to allocate */ + int rc = SQLITE_OK; /* Return code */ + Fts3SegReader *pReader; /* Newly allocated SegReader object */ + int nExtra = 0; /* Bytes to allocate segment root node */ - pCursor->pTokenizer = pTokenizer; - rc = pModule->xNext(pCursor, &zToken, &nToken, &iStart, &iEnd, &iPosition); + if( iStartLeaf==0 ){ + nExtra = nRoot; + } - if( rc==SQLITE_OK ){ - nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase) + nToken; - pRet = (Fts3Expr *)sqlite3_malloc(nByte); - if( !pRet ){ + pReader = (Fts3SegReader *)sqlite3_malloc(sizeof(Fts3SegReader) + nExtra); + if( !pReader ){ + return SQLITE_NOMEM; + } + memset(pReader, 0, sizeof(Fts3SegReader)); + pReader->iStartBlock = iStartLeaf; + pReader->iIdx = iAge; + pReader->iEndBlock = iEndBlock; + + if( nExtra ){ + /* The entire segment is stored in the root node. */ + pReader->aNode = (char *)&pReader[1]; + pReader->nNode = nRoot; + memcpy(pReader->aNode, zRoot, nRoot); + }else{ + /* If the text of the SQL statement to iterate through a contiguous + ** set of entries in the %_segments table has not yet been composed, + ** compose it now. + */ + if( !p->zSelectLeaves ){ + p->zSelectLeaves = sqlite3_mprintf( + "SELECT block FROM %Q.'%q_segments' WHERE blockid BETWEEN ? AND ? " + "ORDER BY blockid", p->zDb, p->zName + ); + if( !p->zSelectLeaves ){ rc = SQLITE_NOMEM; - }else{ - memset(pRet, 0, nByte); - pRet->eType = FTSQUERY_PHRASE; - pRet->pPhrase = (Fts3Phrase *)&pRet[1]; - pRet->pPhrase->nToken = 1; - pRet->pPhrase->iColumn = iCol; - pRet->pPhrase->aToken[0].n = nToken; - pRet->pPhrase->aToken[0].z = (char *)&pRet->pPhrase[1]; - memcpy(pRet->pPhrase->aToken[0].z, zToken, nToken); + goto finished; + } + } - if( iEndpPhrase->aToken[0].isPrefix = 1; - iEnd++; - } - if( !sqlite3_fts3_enable_parentheses && iStart>0 && z[iStart-1]=='-' ){ - pRet->pPhrase->isNot = 1; + /* If there are no free statements in the aLeavesStmt[] array, prepare + ** a new statement now. Otherwise, reuse a prepared statement from + ** aLeavesStmt[]. + */ + if( p->nLeavesStmt==0 ){ + if( p->nLeavesTotal==p->nLeavesAlloc ){ + int nNew = p->nLeavesAlloc + 16; + sqlite3_stmt **aNew = (sqlite3_stmt **)sqlite3_realloc( + p->aLeavesStmt, nNew*sizeof(sqlite3_stmt *) + ); + if( !aNew ){ + rc = SQLITE_NOMEM; + goto finished; } + p->nLeavesAlloc = nNew; + p->aLeavesStmt = aNew; } - nConsumed = iEnd; + rc = sqlite3_prepare_v2(p->db, p->zSelectLeaves, -1, &pReader->pStmt, 0); + if( rc!=SQLITE_OK ){ + goto finished; + } + p->nLeavesTotal++; + }else{ + pReader->pStmt = p->aLeavesStmt[--p->nLeavesStmt]; } - pModule->xClose(pCursor); + /* Bind the start and end leaf blockids to the prepared SQL statement. */ + sqlite3_bind_int64(pReader->pStmt, 1, iStartLeaf); + sqlite3_bind_int64(pReader->pStmt, 2, iEndLeaf); + } + rc = fts3SegReaderNext(pReader); + + finished: + if( rc==SQLITE_OK ){ + *ppReader = pReader; + }else{ + sqlite3Fts3SegReaderFree(p, pReader); } - - *pnConsumed = nConsumed; - *ppExpr = pRet; return rc; } - /* -** Enlarge a memory allocation. If an out-of-memory allocation occurs, -** then free the old allocation. +** This is a comparison function used as a qsort() callback when sorting +** an array of pending terms by term. This occurs as part of flushing +** the contents of the pending-terms hash table to the database. */ -void *fts3ReallocOrFree(void *pOrig, int nNew){ - void *pRet = sqlite3_realloc(pOrig, nNew); - if( !pRet ){ - sqlite3_free(pOrig); +static int fts3CompareElemByTerm(const void *lhs, const void *rhs){ + char *z1 = fts3HashKey(*(Fts3HashElem **)lhs); + char *z2 = fts3HashKey(*(Fts3HashElem **)rhs); + int n1 = fts3HashKeysize(*(Fts3HashElem **)lhs); + int n2 = fts3HashKeysize(*(Fts3HashElem **)rhs); + + int n = (n1pTokenizer; - sqlite3_tokenizer_module const *pModule = pTokenizer->pModule; - int rc; - Fts3Expr *p = 0; - sqlite3_tokenizer_cursor *pCursor = 0; - char *zTemp = 0; - int nTemp = 0; - - rc = pModule->xOpen(pTokenizer, zInput, nInput, &pCursor); - if( rc==SQLITE_OK ){ - int ii; - pCursor->pTokenizer = pTokenizer; - for(ii=0; rc==SQLITE_OK; ii++){ - const char *zToken; - int nToken, iBegin, iEnd, iPos; - rc = pModule->xNext(pCursor, &zToken, &nToken, &iBegin, &iEnd, &iPos); - if( rc==SQLITE_OK ){ - int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); - p = fts3ReallocOrFree(p, nByte+ii*sizeof(struct PhraseToken)); - zTemp = fts3ReallocOrFree(zTemp, nTemp + nToken); - if( !p || !zTemp ){ - goto no_mem; - } - if( ii==0 ){ - memset(p, 0, nByte); - p->pPhrase = (Fts3Phrase *)&p[1]; - } - p->pPhrase = (Fts3Phrase *)&p[1]; - p->pPhrase->nToken = ii+1; - p->pPhrase->aToken[ii].n = nToken; - memcpy(&zTemp[nTemp], zToken, nToken); - nTemp += nToken; - if( iEndpPhrase->aToken[ii].isPrefix = 1; - }else{ - p->pPhrase->aToken[ii].isPrefix = 0; +SQLITE_PRIVATE int sqlite3Fts3SegReaderPending( + Fts3Table *p, /* Virtual table handle */ + const char *zTerm, /* Term to search for */ + int nTerm, /* Size of buffer zTerm */ + int isPrefix, /* True for a term-prefix query */ + Fts3SegReader **ppReader /* OUT: SegReader for pending-terms */ +){ + Fts3SegReader *pReader = 0; /* Fts3SegReader object to return */ + Fts3HashElem **aElem = 0; /* Array of term hash entries to scan */ + int nElem = 0; /* Size of array at aElem */ + int rc = SQLITE_OK; /* Return Code */ + + if( isPrefix ){ + int nAlloc = 0; /* Size of allocated array at aElem */ + Fts3HashElem *pE = 0; /* Iterator variable */ + + for(pE=fts3HashFirst(&p->pendingTerms); pE; pE=fts3HashNext(pE)){ + char *zKey = (char *)fts3HashKey(pE); + int nKey = fts3HashKeysize(pE); + if( nTerm==0 || (nKey>=nTerm && 0==memcmp(zKey, zTerm, nTerm)) ){ + if( nElem==nAlloc ){ + Fts3HashElem **aElem2; + nAlloc += 16; + aElem2 = (Fts3HashElem **)sqlite3_realloc( + aElem, nAlloc*sizeof(Fts3HashElem *) + ); + if( !aElem2 ){ + rc = SQLITE_NOMEM; + nElem = 0; + break; + } + aElem = aElem2; } + aElem[nElem++] = pE; } } - pModule->xClose(pCursor); - pCursor = 0; + /* If more than one term matches the prefix, sort the Fts3HashElem + ** objects in term order using qsort(). This uses the same comparison + ** callback as is used when flushing terms to disk. + */ + if( nElem>1 ){ + qsort(aElem, nElem, sizeof(Fts3HashElem *), fts3CompareElemByTerm); + } + + }else{ + Fts3HashElem *pE = fts3HashFindElem(&p->pendingTerms, zTerm, nTerm); + if( pE ){ + aElem = &pE; + nElem = 1; + } } - if( rc==SQLITE_DONE ){ - int jj; - char *zNew; - int nNew = 0; - int nByte = sizeof(Fts3Expr) + sizeof(Fts3Phrase); - nByte += (p?(p->pPhrase->nToken-1):0) * sizeof(struct PhraseToken); - p = fts3ReallocOrFree(p, nByte + nTemp); - if( !p ){ - goto no_mem; + if( nElem>0 ){ + int nByte = sizeof(Fts3SegReader) + (nElem+1)*sizeof(Fts3HashElem *); + pReader = (Fts3SegReader *)sqlite3_malloc(nByte); + if( !pReader ){ + rc = SQLITE_NOMEM; + }else{ + memset(pReader, 0, nByte); + pReader->iIdx = 0x7FFFFFFF; + pReader->ppNextElem = (Fts3HashElem **)&pReader[1]; + memcpy(pReader->ppNextElem, aElem, nElem*sizeof(Fts3HashElem *)); + fts3SegReaderNext(pReader); } - if( zTemp ){ - zNew = &(((char *)p)[nByte]); - memcpy(zNew, zTemp, nTemp); + } + + if( isPrefix ){ + sqlite3_free(aElem); + } + *ppReader = pReader; + return rc; +} + + +/* +** The second argument to this function is expected to be a statement of +** the form: +** +** SELECT +** idx, -- col 0 +** start_block, -- col 1 +** leaves_end_block, -- col 2 +** end_block, -- col 3 +** root -- col 4 +** FROM %_segdir ... +** +** This function allocates and initializes a Fts3SegReader structure to +** iterate through the terms stored in the segment identified by the +** current row that pStmt is pointing to. +** +** If successful, the Fts3SegReader is left pointing to the first term +** in the segment and SQLITE_OK is returned. Otherwise, an SQLite error +** code is returned. +*/ +static int fts3SegReaderNew( + Fts3Table *p, /* Virtual table handle */ + sqlite3_stmt *pStmt, /* See above */ + int iAge, /* Segment "age". */ + Fts3SegReader **ppReader /* OUT: Allocated Fts3SegReader */ +){ + return sqlite3Fts3SegReaderNew(p, iAge, + sqlite3_column_int64(pStmt, 1), + sqlite3_column_int64(pStmt, 2), + sqlite3_column_int64(pStmt, 3), + sqlite3_column_blob(pStmt, 4), + sqlite3_column_bytes(pStmt, 4), + ppReader + ); +} + +/* +** Compare the entries pointed to by two Fts3SegReader structures. +** Comparison is as follows: +** +** 1) EOF is greater than not EOF. +** +** 2) The current terms (if any) are compared using memcmp(). If one +** term is a prefix of another, the longer term is considered the +** larger. +** +** 3) By segment age. An older segment is considered larger. +*/ +static int fts3SegReaderCmp(Fts3SegReader *pLhs, Fts3SegReader *pRhs){ + int rc; + if( pLhs->aNode && pRhs->aNode ){ + int rc2 = pLhs->nTerm - pRhs->nTerm; + if( rc2<0 ){ + rc = memcmp(pLhs->zTerm, pRhs->zTerm, pLhs->nTerm); }else{ - memset(p, 0, nByte+nTemp); + rc = memcmp(pLhs->zTerm, pRhs->zTerm, pRhs->nTerm); } - p->pPhrase = (Fts3Phrase *)&p[1]; - for(jj=0; jjpPhrase->nToken; jj++){ - p->pPhrase->aToken[jj].z = &zNew[nNew]; - nNew += p->pPhrase->aToken[jj].n; + if( rc==0 ){ + rc = rc2; } - sqlite3_free(zTemp); - p->eType = FTSQUERY_PHRASE; - p->pPhrase->iColumn = pParse->iDefaultCol; - rc = SQLITE_OK; + }else{ + rc = (pLhs->aNode==0) - (pRhs->aNode==0); + } + if( rc==0 ){ + rc = pRhs->iIdx - pLhs->iIdx; } + assert( rc!=0 ); + return rc; +} - *ppExpr = p; +/* +** A different comparison function for SegReader structures. In this +** version, it is assumed that each SegReader points to an entry in +** a doclist for identical terms. Comparison is made as follows: +** +** 1) EOF (end of doclist in this case) is greater than not EOF. +** +** 2) By current docid. +** +** 3) By segment age. An older segment is considered larger. +*/ +static int fts3SegReaderDoclistCmp(Fts3SegReader *pLhs, Fts3SegReader *pRhs){ + int rc = (pLhs->pOffsetList==0)-(pRhs->pOffsetList==0); + if( rc==0 ){ + if( pLhs->iDocid==pRhs->iDocid ){ + rc = pRhs->iIdx - pLhs->iIdx; + }else{ + rc = (pLhs->iDocid > pRhs->iDocid) ? 1 : -1; + } + } + assert( pLhs->aNode && pRhs->aNode ); return rc; -no_mem: +} - if( pCursor ){ - pModule->xClose(pCursor); +/* +** Compare the term that the Fts3SegReader object passed as the first argument +** points to with the term specified by arguments zTerm and nTerm. +** +** If the pSeg iterator is already at EOF, return 0. Otherwise, return +** -ve if the pSeg term is less than zTerm/nTerm, 0 if the two terms are +** equal, or +ve if the pSeg term is greater than zTerm/nTerm. +*/ +static int fts3SegReaderTermCmp( + Fts3SegReader *pSeg, /* Segment reader object */ + const char *zTerm, /* Term to compare to */ + int nTerm /* Size of term zTerm in bytes */ +){ + int res = 0; + if( pSeg->aNode ){ + if( pSeg->nTerm>nTerm ){ + res = memcmp(pSeg->zTerm, zTerm, nTerm); + }else{ + res = memcmp(pSeg->zTerm, zTerm, pSeg->nTerm); + } + if( res==0 ){ + res = pSeg->nTerm-nTerm; + } } - sqlite3_free(zTemp); - sqlite3_free(p); - *ppExpr = 0; - return SQLITE_NOMEM; + return res; } /* -** Function getNextNode(), which is called by fts3ExprParse(), may itself -** call fts3ExprParse(). So this forward declaration is required. +** Argument apSegment is an array of nSegment elements. It is known that +** the final (nSegment-nSuspect) members are already in sorted order +** (according to the comparison function provided). This function shuffles +** the array around until all entries are in sorted order. +*/ +static void fts3SegReaderSort( + Fts3SegReader **apSegment, /* Array to sort entries of */ + int nSegment, /* Size of apSegment array */ + int nSuspect, /* Unsorted entry count */ + int (*xCmp)(Fts3SegReader *, Fts3SegReader *) /* Comparison function */ +){ + int i; /* Iterator variable */ + + assert( nSuspect<=nSegment ); + + if( nSuspect==nSegment ) nSuspect--; + for(i=nSuspect-1; i>=0; i--){ + int j; + for(j=i; j<(nSegment-1); j++){ + Fts3SegReader *pTmp; + if( xCmp(apSegment[j], apSegment[j+1])<0 ) break; + pTmp = apSegment[j+1]; + apSegment[j+1] = apSegment[j]; + apSegment[j] = pTmp; + } + } + +#ifndef NDEBUG + /* Check that the list really is sorted now. */ + for(i=0; i<(nSuspect-1); i++){ + assert( xCmp(apSegment[i], apSegment[i+1])<0 ); + } +#endif +} + +/* +** Insert a record into the %_segments table. */ -static int fts3ExprParse(ParseContext *, const char *, int, Fts3Expr **, int *); +static int fts3WriteSegment( + Fts3Table *p, /* Virtual table handle */ + sqlite3_int64 iBlock, /* Block id for new block */ + char *z, /* Pointer to buffer containing block data */ + int n /* Size of buffer z in bytes */ +){ + sqlite3_stmt *pStmt; + int rc = fts3SqlStmt(p, SQL_INSERT_SEGMENTS, &pStmt, 0); + if( rc==SQLITE_OK ){ + sqlite3_bind_int64(pStmt, 1, iBlock); + sqlite3_bind_blob(pStmt, 2, z, n, SQLITE_STATIC); + sqlite3_step(pStmt); + rc = sqlite3_reset(pStmt); + } + return rc; +} + +/* +** Insert a record into the %_segdir table. +*/ +static int fts3WriteSegdir( + Fts3Table *p, /* Virtual table handle */ + int iLevel, /* Value for "level" field */ + int iIdx, /* Value for "idx" field */ + sqlite3_int64 iStartBlock, /* Value for "start_block" field */ + sqlite3_int64 iLeafEndBlock, /* Value for "leaves_end_block" field */ + sqlite3_int64 iEndBlock, /* Value for "end_block" field */ + char *zRoot, /* Blob value for "root" field */ + int nRoot /* Number of bytes in buffer zRoot */ +){ + sqlite3_stmt *pStmt; + int rc = fts3SqlStmt(p, SQL_INSERT_SEGDIR, &pStmt, 0); + if( rc==SQLITE_OK ){ + sqlite3_bind_int(pStmt, 1, iLevel); + sqlite3_bind_int(pStmt, 2, iIdx); + sqlite3_bind_int64(pStmt, 3, iStartBlock); + sqlite3_bind_int64(pStmt, 4, iLeafEndBlock); + sqlite3_bind_int64(pStmt, 5, iEndBlock); + sqlite3_bind_blob(pStmt, 6, zRoot, nRoot, SQLITE_STATIC); + sqlite3_step(pStmt); + rc = sqlite3_reset(pStmt); + } + return rc; +} /* -** The output variable *ppExpr is populated with an allocated Fts3Expr -** structure, or set to 0 if the end of the input buffer is reached. +** Return the size of the common prefix (if any) shared by zPrev and +** zNext, in bytes. For example, ** -** Returns an SQLite error code. SQLITE_OK if everything works, SQLITE_NOMEM -** if a malloc failure occurs, or SQLITE_ERROR if a parse error is encountered. -** If SQLITE_ERROR is returned, pContext is populated with an error message. +** fts3PrefixCompress("abc", 3, "abcdef", 6) // returns 3 +** fts3PrefixCompress("abX", 3, "abcdef", 6) // returns 2 +** fts3PrefixCompress("abX", 3, "Xbcdef", 6) // returns 0 */ -static int getNextNode( - ParseContext *pParse, /* fts3 query parse context */ - const char *z, int n, /* Input string */ - Fts3Expr **ppExpr, /* OUT: expression */ - int *pnConsumed /* OUT: Number of bytes consumed */ +static int fts3PrefixCompress( + const char *zPrev, /* Buffer containing previous term */ + int nPrev, /* Size of buffer zPrev in bytes */ + const char *zNext, /* Buffer containing next term */ + int nNext /* Size of buffer zNext in bytes */ ){ - static const struct Fts3Keyword { - char z[4]; /* Keyword text */ - unsigned char n; /* Length of the keyword */ - unsigned char parenOnly; /* Only valid in paren mode */ - unsigned char eType; /* Keyword code */ - } aKeyword[] = { - { "OR" , 2, 0, FTSQUERY_OR }, - { "AND", 3, 1, FTSQUERY_AND }, - { "NOT", 3, 1, FTSQUERY_NOT }, - { "NEAR", 4, 0, FTSQUERY_NEAR } - }; - int ii; - int iCol; - int iColLen; + int n; + UNUSED_PARAMETER(nNext); + for(n=0; nnData; /* Current size of node in bytes */ + int nReq = nData; /* Required space after adding zTerm */ + int nPrefix; /* Number of bytes of prefix compression */ + int nSuffix; /* Suffix length */ + + nPrefix = fts3PrefixCompress(pTree->zTerm, pTree->nTerm, zTerm, nTerm); + nSuffix = nTerm-nPrefix; + + nReq += sqlite3Fts3VarintLen(nPrefix)+sqlite3Fts3VarintLen(nSuffix)+nSuffix; + if( nReq<=p->nNodeSize || !pTree->zTerm ){ + + if( nReq>p->nNodeSize ){ + /* An unusual case: this is the first term to be added to the node + ** and the static node buffer (p->nNodeSize bytes) is not large + ** enough. Use a separately malloced buffer instead This wastes + ** p->nNodeSize bytes, but since this scenario only comes about when + ** the database contain two terms that share a prefix of almost 2KB, + ** this is not expected to be a serious problem. + */ + assert( pTree->aData==(char *)&pTree[1] ); + pTree->aData = (char *)sqlite3_malloc(nReq); + if( !pTree->aData ){ + return SQLITE_NOMEM; + } + } - /* Skip over any whitespace before checking for a keyword, an open or - ** close bracket, or a quoted string. + if( pTree->zTerm ){ + /* There is no prefix-length field for first term in a node */ + nData += sqlite3Fts3PutVarint(&pTree->aData[nData], nPrefix); + } + + nData += sqlite3Fts3PutVarint(&pTree->aData[nData], nSuffix); + memcpy(&pTree->aData[nData], &zTerm[nPrefix], nSuffix); + pTree->nData = nData + nSuffix; + pTree->nEntry++; + + if( isCopyTerm ){ + if( pTree->nMalloczMalloc, nTerm*2); + if( !zNew ){ + return SQLITE_NOMEM; + } + pTree->nMalloc = nTerm*2; + pTree->zMalloc = zNew; + } + pTree->zTerm = pTree->zMalloc; + memcpy(pTree->zTerm, zTerm, nTerm); + pTree->nTerm = nTerm; + }else{ + pTree->zTerm = (char *)zTerm; + pTree->nTerm = nTerm; + } + return SQLITE_OK; + } + } + + /* If control flows to here, it was not possible to append zTerm to the + ** current node. Create a new node (a right-sibling of the current node). + ** If this is the first node in the tree, the term is added to it. + ** + ** Otherwise, the term is not added to the new node, it is left empty for + ** now. Instead, the term is inserted into the parent of pTree. If pTree + ** has no parent, one is created here. */ - while( nInput>0 && fts3isspace(*zInput) ){ - nInput--; - zInput++; + pNew = (SegmentNode *)sqlite3_malloc(sizeof(SegmentNode) + p->nNodeSize); + if( !pNew ){ + return SQLITE_NOMEM; } - if( nInput==0 ){ - return SQLITE_DONE; + memset(pNew, 0, sizeof(SegmentNode)); + pNew->nData = 1 + FTS3_VARINT_MAX; + pNew->aData = (char *)&pNew[1]; + + if( pTree ){ + SegmentNode *pParent = pTree->pParent; + rc = fts3NodeAddTerm(p, &pParent, isCopyTerm, zTerm, nTerm); + if( pTree->pParent==0 ){ + pTree->pParent = pParent; + } + pTree->pRight = pNew; + pNew->pLeftmost = pTree->pLeftmost; + pNew->pParent = pParent; + pNew->zMalloc = pTree->zMalloc; + pNew->nMalloc = pTree->nMalloc; + pTree->zMalloc = 0; + }else{ + pNew->pLeftmost = pNew; + rc = fts3NodeAddTerm(p, &pNew, isCopyTerm, zTerm, nTerm); } - /* See if we are dealing with a keyword. */ - for(ii=0; ii<(int)(sizeof(aKeyword)/sizeof(struct Fts3Keyword)); ii++){ - const struct Fts3Keyword *pKey = &aKeyword[ii]; + *ppTree = pNew; + return rc; +} - if( (pKey->parenOnly & ~sqlite3_fts3_enable_parentheses)!=0 ){ - continue; +/* +** Helper function for fts3NodeWrite(). +*/ +static int fts3TreeFinishNode( + SegmentNode *pTree, + int iHeight, + sqlite3_int64 iLeftChild +){ + int nStart; + assert( iHeight>=1 && iHeight<128 ); + nStart = FTS3_VARINT_MAX - sqlite3Fts3VarintLen(iLeftChild); + pTree->aData[nStart] = (char)iHeight; + sqlite3Fts3PutVarint(&pTree->aData[nStart+1], iLeftChild); + return nStart; +} + +/* +** Write the buffer for the segment node pTree and all of its peers to the +** database. Then call this function recursively to write the parent of +** pTree and its peers to the database. +** +** Except, if pTree is a root node, do not write it to the database. Instead, +** set output variables *paRoot and *pnRoot to contain the root node. +** +** If successful, SQLITE_OK is returned and output variable *piLast is +** set to the largest blockid written to the database (or zero if no +** blocks were written to the db). Otherwise, an SQLite error code is +** returned. +*/ +static int fts3NodeWrite( + Fts3Table *p, /* Virtual table handle */ + SegmentNode *pTree, /* SegmentNode handle */ + int iHeight, /* Height of this node in tree */ + sqlite3_int64 iLeaf, /* Block id of first leaf node */ + sqlite3_int64 iFree, /* Block id of next free slot in %_segments */ + sqlite3_int64 *piLast, /* OUT: Block id of last entry written */ + char **paRoot, /* OUT: Data for root node */ + int *pnRoot /* OUT: Size of root node in bytes */ +){ + int rc = SQLITE_OK; + + if( !pTree->pParent ){ + /* Root node of the tree. */ + int nStart = fts3TreeFinishNode(pTree, iHeight, iLeaf); + *piLast = iFree-1; + *pnRoot = pTree->nData - nStart; + *paRoot = &pTree->aData[nStart]; + }else{ + SegmentNode *pIter; + sqlite3_int64 iNextFree = iFree; + sqlite3_int64 iNextLeaf = iLeaf; + for(pIter=pTree->pLeftmost; pIter && rc==SQLITE_OK; pIter=pIter->pRight){ + int nStart = fts3TreeFinishNode(pIter, iHeight, iNextLeaf); + int nWrite = pIter->nData - nStart; + + rc = fts3WriteSegment(p, iNextFree, &pIter->aData[nStart], nWrite); + iNextFree++; + iNextLeaf += (pIter->nEntry+1); } + if( rc==SQLITE_OK ){ + assert( iNextLeaf==iFree ); + rc = fts3NodeWrite( + p, pTree->pParent, iHeight+1, iFree, iNextFree, piLast, paRoot, pnRoot + ); + } + } - if( nInput>=pKey->n && 0==memcmp(zInput, pKey->z, pKey->n) ){ - int nNear = SQLITE_FTS3_DEFAULT_NEAR_PARAM; - int nKey = pKey->n; - char cNext; + return rc; +} - /* If this is a "NEAR" keyword, check for an explicit nearness. */ - if( pKey->eType==FTSQUERY_NEAR ){ - assert( nKey==4 ); - if( zInput[4]=='/' && zInput[5]>='0' && zInput[5]<='9' ){ - nNear = 0; - for(nKey=5; zInput[nKey]>='0' && zInput[nKey]<='9'; nKey++){ - nNear = nNear * 10 + (zInput[nKey] - '0'); - } - } +/* +** Free all memory allocations associated with the tree pTree. +*/ +static void fts3NodeFree(SegmentNode *pTree){ + if( pTree ){ + SegmentNode *p = pTree->pLeftmost; + fts3NodeFree(p->pParent); + while( p ){ + SegmentNode *pRight = p->pRight; + if( p->aData!=(char *)&p[1] ){ + sqlite3_free(p->aData); } + assert( pRight==0 || p->zMalloc==0 ); + sqlite3_free(p->zMalloc); + sqlite3_free(p); + p = pRight; + } + } +} - /* At this point this is probably a keyword. But for that to be true, - ** the next byte must contain either whitespace, an open or close - ** parenthesis, a quote character, or EOF. - */ - cNext = zInput[nKey]; - if( fts3isspace(cNext) - || cNext=='"' || cNext=='(' || cNext==')' || cNext==0 - ){ - pRet = (Fts3Expr *)sqlite3_malloc(sizeof(Fts3Expr)); - memset(pRet, 0, sizeof(Fts3Expr)); - pRet->eType = pKey->eType; - pRet->nNear = nNear; - *ppExpr = pRet; - *pnConsumed = (zInput - z) + nKey; - return SQLITE_OK; - } +/* +** Add a term to the segment being constructed by the SegmentWriter object +** *ppWriter. When adding the first term to a segment, *ppWriter should +** be passed NULL. This function will allocate a new SegmentWriter object +** and return it via the input/output variable *ppWriter in this case. +** +** If successful, SQLITE_OK is returned. Otherwise, an SQLite error code. +*/ +static int fts3SegWriterAdd( + Fts3Table *p, /* Virtual table handle */ + SegmentWriter **ppWriter, /* IN/OUT: SegmentWriter handle */ + int isCopyTerm, /* True if buffer zTerm must be copied */ + const char *zTerm, /* Pointer to buffer containing term */ + int nTerm, /* Size of term in bytes */ + const char *aDoclist, /* Pointer to buffer containing doclist */ + int nDoclist /* Size of doclist in bytes */ +){ + int nPrefix; /* Size of term prefix in bytes */ + int nSuffix; /* Size of term suffix in bytes */ + int nReq; /* Number of bytes required on leaf page */ + int nData; + SegmentWriter *pWriter = *ppWriter; - /* Turns out that wasn't a keyword after all. This happens if the - ** user has supplied a token such as "ORacle". Continue. - */ + if( !pWriter ){ + int rc; + sqlite3_stmt *pStmt; + + /* Allocate the SegmentWriter structure */ + pWriter = (SegmentWriter *)sqlite3_malloc(sizeof(SegmentWriter)); + if( !pWriter ) return SQLITE_NOMEM; + memset(pWriter, 0, sizeof(SegmentWriter)); + *ppWriter = pWriter; + + /* Allocate a buffer in which to accumulate data */ + pWriter->aData = (char *)sqlite3_malloc(p->nNodeSize); + if( !pWriter->aData ) return SQLITE_NOMEM; + pWriter->nSize = p->nNodeSize; + + /* Find the next free blockid in the %_segments table */ + rc = fts3SqlStmt(p, SQL_NEXT_SEGMENTS_ID, &pStmt, 0); + if( rc!=SQLITE_OK ) return rc; + if( SQLITE_ROW==sqlite3_step(pStmt) ){ + pWriter->iFree = sqlite3_column_int64(pStmt, 0); + pWriter->iFirst = pWriter->iFree; } + rc = sqlite3_reset(pStmt); + if( rc!=SQLITE_OK ) return rc; } + nData = pWriter->nData; - /* Check for an open bracket. */ - if( sqlite3_fts3_enable_parentheses ){ - if( *zInput=='(' ){ - int nConsumed; - int rc; - pParse->nNest++; - rc = fts3ExprParse(pParse, &zInput[1], nInput-1, ppExpr, &nConsumed); - if( rc==SQLITE_OK && !*ppExpr ){ - rc = SQLITE_DONE; + nPrefix = fts3PrefixCompress(pWriter->zTerm, pWriter->nTerm, zTerm, nTerm); + nSuffix = nTerm-nPrefix; + + /* Figure out how many bytes are required by this new entry */ + nReq = sqlite3Fts3VarintLen(nPrefix) + /* varint containing prefix size */ + sqlite3Fts3VarintLen(nSuffix) + /* varint containing suffix size */ + nSuffix + /* Term suffix */ + sqlite3Fts3VarintLen(nDoclist) + /* Size of doclist */ + nDoclist; /* Doclist data */ + + if( nData>0 && nData+nReq>p->nNodeSize ){ + int rc; + + /* The current leaf node is full. Write it out to the database. */ + rc = fts3WriteSegment(p, pWriter->iFree++, pWriter->aData, nData); + if( rc!=SQLITE_OK ) return rc; + + /* Add the current term to the interior node tree. The term added to + ** the interior tree must: + ** + ** a) be greater than the largest term on the leaf node just written + ** to the database (still available in pWriter->zTerm), and + ** + ** b) be less than or equal to the term about to be added to the new + ** leaf node (zTerm/nTerm). + ** + ** In other words, it must be the prefix of zTerm 1 byte longer than + ** the common prefix (if any) of zTerm and pWriter->zTerm. + */ + assert( nPrefixpTree, isCopyTerm, zTerm, nPrefix+1); + if( rc!=SQLITE_OK ) return rc; + + nData = 0; + pWriter->nTerm = 0; + + nPrefix = 0; + nSuffix = nTerm; + nReq = 1 + /* varint containing prefix size */ + sqlite3Fts3VarintLen(nTerm) + /* varint containing suffix size */ + nTerm + /* Term suffix */ + sqlite3Fts3VarintLen(nDoclist) + /* Size of doclist */ + nDoclist; /* Doclist data */ + } + + /* If the buffer currently allocated is too small for this entry, realloc + ** the buffer to make it large enough. + */ + if( nReq>pWriter->nSize ){ + char *aNew = sqlite3_realloc(pWriter->aData, nReq); + if( !aNew ) return SQLITE_NOMEM; + pWriter->aData = aNew; + pWriter->nSize = nReq; + } + assert( nData+nReq<=pWriter->nSize ); + + /* Append the prefix-compressed term and doclist to the buffer. */ + nData += sqlite3Fts3PutVarint(&pWriter->aData[nData], nPrefix); + nData += sqlite3Fts3PutVarint(&pWriter->aData[nData], nSuffix); + memcpy(&pWriter->aData[nData], &zTerm[nPrefix], nSuffix); + nData += nSuffix; + nData += sqlite3Fts3PutVarint(&pWriter->aData[nData], nDoclist); + memcpy(&pWriter->aData[nData], aDoclist, nDoclist); + pWriter->nData = nData + nDoclist; + + /* Save the current term so that it can be used to prefix-compress the next. + ** If the isCopyTerm parameter is true, then the buffer pointed to by + ** zTerm is transient, so take a copy of the term data. Otherwise, just + ** store a copy of the pointer. + */ + if( isCopyTerm ){ + if( nTerm>pWriter->nMalloc ){ + char *zNew = sqlite3_realloc(pWriter->zMalloc, nTerm*2); + if( !zNew ){ + return SQLITE_NOMEM; } - *pnConsumed = (zInput - z) + 1 + nConsumed; - return rc; - } - - /* Check for a close bracket. */ - if( *zInput==')' ){ - pParse->nNest--; - *pnConsumed = (zInput - z) + 1; - return SQLITE_DONE; + pWriter->nMalloc = nTerm*2; + pWriter->zMalloc = zNew; + pWriter->zTerm = zNew; } + assert( pWriter->zTerm==pWriter->zMalloc ); + memcpy(pWriter->zTerm, zTerm, nTerm); + }else{ + pWriter->zTerm = (char *)zTerm; } + pWriter->nTerm = nTerm; - /* See if we are dealing with a quoted phrase. If this is the case, then - ** search for the closing quote and pass the whole string to getNextString() - ** for processing. This is easy to do, as fts3 has no syntax for escaping - ** a quote character embedded in a string. - */ - if( *zInput=='"' ){ - for(ii=1; iipTree ){ + sqlite3_int64 iLast = 0; /* Largest block id written to database */ + sqlite3_int64 iLastLeaf; /* Largest leaf block id written to db */ + char *zRoot = NULL; /* Pointer to buffer containing root node */ + int nRoot = 0; /* Size of buffer zRoot */ + + iLastLeaf = pWriter->iFree; + rc = fts3WriteSegment(p, pWriter->iFree++, pWriter->aData, pWriter->nData); + if( rc==SQLITE_OK ){ + rc = fts3NodeWrite(p, pWriter->pTree, 1, + pWriter->iFirst, pWriter->iFree, &iLast, &zRoot, &nRoot); } - return getNextString(pParse, &zInput[1], ii-1, ppExpr); + if( rc==SQLITE_OK ){ + rc = fts3WriteSegdir( + p, iLevel, iIdx, pWriter->iFirst, iLastLeaf, iLast, zRoot, nRoot); + } + }else{ + /* The entire tree fits on the root node. Write it to the segdir table. */ + rc = fts3WriteSegdir( + p, iLevel, iIdx, 0, 0, 0, pWriter->aData, pWriter->nData); } + return rc; +} +/* +** Release all memory held by the SegmentWriter object passed as the +** first argument. +*/ +static void fts3SegWriterFree(SegmentWriter *pWriter){ + if( pWriter ){ + sqlite3_free(pWriter->aData); + sqlite3_free(pWriter->zMalloc); + fts3NodeFree(pWriter->pTree); + sqlite3_free(pWriter); + } +} - /* If control flows to this point, this must be a regular token, or - ** the end of the input. Read a regular token using the sqlite3_tokenizer - ** interface. Before doing so, figure out if there is an explicit - ** column specifier for the token. - ** - ** TODO: Strangely, it is not possible to associate a column specifier - ** with a quoted phrase, only with a single token. Not sure if this was - ** an implementation artifact or an intentional decision when fts3 was - ** first implemented. Whichever it was, this module duplicates the - ** limitation. - */ - iCol = pParse->iDefaultCol; - iColLen = 0; - for(ii=0; iinCol; ii++){ - const char *zStr = pParse->azCol[ii]; - int nStr = strlen(zStr); - if( nInput>nStr && zInput[nStr]==':' && memcmp(zStr, zInput, nStr)==0 ){ - iCol = ii; - iColLen = ((zInput - z) + nStr + 1); - break; +/* +** The first value in the apVal[] array is assumed to contain an integer. +** This function tests if there exist any documents with docid values that +** are different from that integer. i.e. if deleting the document with docid +** apVal[0] would mean the FTS3 table were empty. +** +** If successful, *pisEmpty is set to true if the table is empty except for +** document apVal[0], or false otherwise, and SQLITE_OK is returned. If an +** error occurs, an SQLite error code is returned. +*/ +static int fts3IsEmpty(Fts3Table *p, sqlite3_value **apVal, int *pisEmpty){ + sqlite3_stmt *pStmt; + int rc; + rc = fts3SqlStmt(p, SQL_IS_EMPTY, &pStmt, apVal); + if( rc==SQLITE_OK ){ + if( SQLITE_ROW==sqlite3_step(pStmt) ){ + *pisEmpty = sqlite3_column_int(pStmt, 0); } + rc = sqlite3_reset(pStmt); } - rc = getNextToken(pParse, iCol, &z[iColLen], n-iColLen, ppExpr, pnConsumed); - *pnConsumed += iColLen; return rc; } /* -** The argument is an Fts3Expr structure for a binary operator (any type -** except an FTSQUERY_PHRASE). Return an integer value representing the -** precedence of the operator. Lower values have a higher precedence (i.e. -** group more tightly). For example, in the C language, the == operator -** groups more tightly than ||, and would therefore have a higher precedence. -** -** When using the new fts3 query syntax (when SQLITE_ENABLE_FTS3_PARENTHESIS -** is defined), the order of the operators in precedence from highest to -** lowest is: -** -** NEAR -** NOT -** AND (including implicit ANDs) -** OR +** Set *pnSegment to the number of segments of level iLevel in the database. ** -** Note that when using the old query syntax, the OR operator has a higher -** precedence than the AND operator. +** Return SQLITE_OK if successful, or an SQLite error code if not. */ -static int opPrecedence(Fts3Expr *p){ - assert( p->eType!=FTSQUERY_PHRASE ); - if( sqlite3_fts3_enable_parentheses ){ - return p->eType; - }else if( p->eType==FTSQUERY_NEAR ){ - return 1; - }else if( p->eType==FTSQUERY_OR ){ - return 2; +static int fts3SegmentCount(Fts3Table *p, int iLevel, int *pnSegment){ + sqlite3_stmt *pStmt; + int rc; + + assert( iLevel>=0 ); + rc = fts3SqlStmt(p, SQL_SELECT_LEVEL_COUNT, &pStmt, 0); + if( rc!=SQLITE_OK ) return rc; + sqlite3_bind_int(pStmt, 1, iLevel); + if( SQLITE_ROW==sqlite3_step(pStmt) ){ + *pnSegment = sqlite3_column_int(pStmt, 0); } - assert( p->eType==FTSQUERY_AND ); - return 3; + return sqlite3_reset(pStmt); } /* -** Argument ppHead contains a pointer to the current head of a query -** expression tree being parsed. pPrev is the expression node most recently -** inserted into the tree. This function adds pNew, which is always a binary -** operator node, into the expression tree based on the relative precedence -** of pNew and the existing nodes of the tree. This may result in the head -** of the tree changing, in which case *ppHead is set to the new root node. +** Set *pnSegment to the total number of segments in the database. Set +** *pnMax to the largest segment level in the database (segment levels +** are stored in the 'level' column of the %_segdir table). +** +** Return SQLITE_OK if successful, or an SQLite error code if not. */ -static void insertBinaryOperator( - Fts3Expr **ppHead, /* Pointer to the root node of a tree */ - Fts3Expr *pPrev, /* Node most recently inserted into the tree */ - Fts3Expr *pNew /* New binary node to insert into expression tree */ -){ - Fts3Expr *pSplit = pPrev; - while( pSplit->pParent && opPrecedence(pSplit->pParent)<=opPrecedence(pNew) ){ - pSplit = pSplit->pParent; +static int fts3SegmentCountMax(Fts3Table *p, int *pnSegment, int *pnMax){ + sqlite3_stmt *pStmt; + int rc; + + rc = fts3SqlStmt(p, SQL_SELECT_SEGDIR_COUNT_MAX, &pStmt, 0); + if( rc!=SQLITE_OK ) return rc; + if( SQLITE_ROW==sqlite3_step(pStmt) ){ + *pnSegment = sqlite3_column_int(pStmt, 0); + *pnMax = sqlite3_column_int(pStmt, 1); } + return sqlite3_reset(pStmt); +} - if( pSplit->pParent ){ - assert( pSplit->pParent->pRight==pSplit ); - pSplit->pParent->pRight = pNew; - pNew->pParent = pSplit->pParent; +/* +** This function is used after merging multiple segments into a single large +** segment to delete the old, now redundant, segment b-trees. Specifically, +** it: +** +** 1) Deletes all %_segments entries for the segments associated with +** each of the SegReader objects in the array passed as the third +** argument, and +** +** 2) deletes all %_segdir entries with level iLevel, or all %_segdir +** entries regardless of level if (iLevel<0). +** +** SQLITE_OK is returned if successful, otherwise an SQLite error code. +*/ +static int fts3DeleteSegdir( + Fts3Table *p, /* Virtual table handle */ + int iLevel, /* Level of %_segdir entries to delete */ + Fts3SegReader **apSegment, /* Array of SegReader objects */ + int nReader /* Size of array apSegment */ +){ + int rc; /* Return Code */ + int i; /* Iterator variable */ + sqlite3_stmt *pDelete; /* SQL statement to delete rows */ + + rc = fts3SqlStmt(p, SQL_DELETE_SEGMENTS_RANGE, &pDelete, 0); + for(i=0; rc==SQLITE_OK && iiStartBlock ){ + sqlite3_bind_int64(pDelete, 1, pSegment->iStartBlock); + sqlite3_bind_int64(pDelete, 2, pSegment->iEndBlock); + sqlite3_step(pDelete); + rc = sqlite3_reset(pDelete); + } + } + if( rc!=SQLITE_OK ){ + return rc; + } + + if( iLevel>=0 ){ + rc = fts3SqlStmt(p, SQL_DELETE_SEGDIR_BY_LEVEL, &pDelete, 0); + if( rc==SQLITE_OK ){ + sqlite3_bind_int(pDelete, 1, iLevel); + sqlite3_step(pDelete); + rc = sqlite3_reset(pDelete); + } }else{ - *ppHead = pNew; + rc = fts3SqlExec(p, SQL_DELETE_ALL_SEGDIR, 0); } - pNew->pLeft = pSplit; - pSplit->pParent = pNew; + + return rc; } /* -** Parse the fts3 query expression found in buffer z, length n. This function -** returns either when the end of the buffer is reached or an unmatched -** closing bracket - ')' - is encountered. +** When this function is called, buffer *ppList (size *pnList bytes) contains +** a position list that may (or may not) feature multiple columns. This +** function adjusts the pointer *ppList and the length *pnList so that they +** identify the subset of the position list that corresponds to column iCol. ** -** If successful, SQLITE_OK is returned, *ppExpr is set to point to the -** parsed form of the expression and *pnConsumed is set to the number of -** bytes read from buffer z. Otherwise, *ppExpr is set to 0 and SQLITE_NOMEM -** (out of memory error) or SQLITE_ERROR (parse error) is returned. +** If there are no entries in the input position list for column iCol, then +** *pnList is set to zero before returning. */ -static int fts3ExprParse( - ParseContext *pParse, /* fts3 query parse context */ - const char *z, int n, /* Text of MATCH query */ - Fts3Expr **ppExpr, /* OUT: Parsed query structure */ - int *pnConsumed /* OUT: Number of bytes consumed */ +static void fts3ColumnFilter( + int iCol, /* Column to filter on */ + char **ppList, /* IN/OUT: Pointer to position list */ + int *pnList /* IN/OUT: Size of buffer *ppList in bytes */ ){ - Fts3Expr *pRet = 0; - Fts3Expr *pPrev = 0; - Fts3Expr *pNotBranch = 0; /* Only used in legacy parse mode */ - int nIn = n; - const char *zIn = z; - int rc = SQLITE_OK; - int isRequirePhrase = 1; + char *pList = *ppList; + int nList = *pnList; + char *pEnd = &pList[nList]; + int iCurrent = 0; + char *p = pList; - while( rc==SQLITE_OK ){ - Fts3Expr *p = 0; - int nByte = 0; - rc = getNextNode(pParse, zIn, nIn, &p, &nByte); - if( rc==SQLITE_OK ){ - int isPhrase; + assert( iCol>=0 ); + while( 1 ){ + char c = 0; + while( peType==FTSQUERY_PHRASE && p->pPhrase->isNot - ){ - /* Create an implicit NOT operator. */ - Fts3Expr *pNot = sqlite3_malloc(sizeof(Fts3Expr)); - if( !pNot ){ - sqlite3Fts3ExprFree(p); - rc = SQLITE_NOMEM; - goto exprparse_out; - } - memset(pNot, 0, sizeof(Fts3Expr)); - pNot->eType = FTSQUERY_NOT; - pNot->pRight = p; - if( pNotBranch ){ - pNotBranch->pLeft = p; - pNot->pRight = pNotBranch; - } - pNotBranch = pNot; - }else{ - int eType = p->eType; - assert( eType!=FTSQUERY_PHRASE || !p->pPhrase->isNot ); - isPhrase = (eType==FTSQUERY_PHRASE || p->pLeft); - - /* The isRequirePhrase variable is set to true if a phrase or - ** an expression contained in parenthesis is required. If a - ** binary operator (AND, OR, NOT or NEAR) is encounted when - ** isRequirePhrase is set, this is a syntax error. - */ - if( !isPhrase && isRequirePhrase ){ - sqlite3Fts3ExprFree(p); - rc = SQLITE_ERROR; - goto exprparse_out; - } - - if( isPhrase && !isRequirePhrase ){ - /* Insert an implicit AND operator. */ - Fts3Expr *pAnd; - assert( pRet && pPrev ); - pAnd = sqlite3_malloc(sizeof(Fts3Expr)); - if( !pAnd ){ - sqlite3Fts3ExprFree(p); - rc = SQLITE_NOMEM; - goto exprparse_out; - } - memset(pAnd, 0, sizeof(Fts3Expr)); - pAnd->eType = FTSQUERY_AND; - insertBinaryOperator(&pRet, pPrev, pAnd); - pPrev = pAnd; - } - - /* This test catches attempts to make either operand of a NEAR - ** operator something other than a phrase. For example, either of - ** the following: - ** - ** (bracketed expression) NEAR phrase - ** phrase NEAR (bracketed expression) - ** - ** Return an error in either case. - */ - if( pPrev && ( - (eType==FTSQUERY_NEAR && !isPhrase && pPrev->eType!=FTSQUERY_PHRASE) - || (eType!=FTSQUERY_PHRASE && isPhrase && pPrev->eType==FTSQUERY_NEAR) - )){ - sqlite3Fts3ExprFree(p); - rc = SQLITE_ERROR; - goto exprparse_out; - } - - if( isPhrase ){ - if( pRet ){ - assert( pPrev && pPrev->pLeft && pPrev->pRight==0 ); - pPrev->pRight = p; - p->pParent = pPrev; - }else{ - pRet = p; - } - }else{ - insertBinaryOperator(&pRet, pPrev, p); - } - isRequirePhrase = !isPhrase; - } - assert( nByte>0 ); + nList -= (int)(p - pList); + pList = p; + if( nList==0 ){ + break; } - assert( rc!=SQLITE_OK || (nByte>0 && nByte<=nIn) ); - nIn -= nByte; - zIn += nByte; - pPrev = p; + p = &pList[1]; + p += sqlite3Fts3GetVarint32(p, &iCurrent); } - if( rc==SQLITE_DONE && pRet && isRequirePhrase ){ - rc = SQLITE_ERROR; - } + *ppList = pList; + *pnList = nList; +} - if( rc==SQLITE_DONE ){ - rc = SQLITE_OK; - if( !sqlite3_fts3_enable_parentheses && pNotBranch ){ - if( !pRet ){ - rc = SQLITE_ERROR; - }else{ - pNotBranch->pLeft = pRet; - pRet = pNotBranch; - } - } - } - *pnConsumed = n - nIn; +/* +** sqlite3Fts3SegReaderIterate() callback used when merging multiple +** segments to create a single, larger segment. +*/ +static int fts3MergeCallback( + Fts3Table *p, /* FTS3 Virtual table handle */ + void *pContext, /* Pointer to SegmentWriter* to write with */ + char *zTerm, /* Term to write to the db */ + int nTerm, /* Number of bytes in zTerm */ + char *aDoclist, /* Doclist associated with zTerm */ + int nDoclist /* Number of bytes in doclist */ +){ + SegmentWriter **ppW = (SegmentWriter **)pContext; + return fts3SegWriterAdd(p, ppW, 1, zTerm, nTerm, aDoclist, nDoclist); +} -exprparse_out: - if( rc!=SQLITE_OK ){ - sqlite3Fts3ExprFree(pRet); - sqlite3Fts3ExprFree(pNotBranch); - pRet = 0; - } - *ppExpr = pRet; - return rc; +/* +** sqlite3Fts3SegReaderIterate() callback used when flushing the contents +** of the pending-terms hash table to the database. +*/ +static int fts3FlushCallback( + Fts3Table *p, /* FTS3 Virtual table handle */ + void *pContext, /* Pointer to SegmentWriter* to write with */ + char *zTerm, /* Term to write to the db */ + int nTerm, /* Number of bytes in zTerm */ + char *aDoclist, /* Doclist associated with zTerm */ + int nDoclist /* Number of bytes in doclist */ +){ + SegmentWriter **ppW = (SegmentWriter **)pContext; + return fts3SegWriterAdd(p, ppW, 0, zTerm, nTerm, aDoclist, nDoclist); } /* -** Parameters z and n contain a pointer to and length of a buffer containing -** an fts3 query expression, respectively. This function attempts to parse the -** query expression and create a tree of Fts3Expr structures representing the -** parsed expression. If successful, *ppExpr is set to point to the head -** of the parsed expression tree and SQLITE_OK is returned. If an error -** occurs, either SQLITE_NOMEM (out-of-memory error) or SQLITE_ERROR (parse -** error) is returned and *ppExpr is set to 0. +** This function is used to iterate through a contiguous set of terms +** stored in the full-text index. It merges data contained in one or +** more segments to support this. ** -** If parameter n is a negative number, then z is assumed to point to a -** nul-terminated string and the length is determined using strlen(). +** The second argument is passed an array of pointers to SegReader objects +** allocated with sqlite3Fts3SegReaderNew(). This function merges the range +** of terms selected by each SegReader. If a single term is present in +** more than one segment, the associated doclists are merged. For each +** term and (possibly merged) doclist in the merged range, the callback +** function xFunc is invoked with its arguments set as follows. ** -** The first parameter, pTokenizer, is passed the fts3 tokenizer module to -** use to normalize query tokens while parsing the expression. The azCol[] -** array, which is assumed to contain nCol entries, should contain the names -** of each column in the target fts3 table, in order from left to right. -** Column names must be nul-terminated strings. +** arg 0: Copy of 'p' parameter passed to this function +** arg 1: Copy of 'pContext' parameter passed to this function +** arg 2: Pointer to buffer containing term +** arg 3: Size of arg 2 buffer in bytes +** arg 4: Pointer to buffer containing doclist +** arg 5: Size of arg 2 buffer in bytes ** -** The iDefaultCol parameter should be passed the index of the table column -** that appears on the left-hand-side of the MATCH operator (the default -** column to match against for tokens for which a column name is not explicitly -** specified as part of the query string), or -1 if tokens may by default -** match any table column. +** The 4th argument to this function is a pointer to a structure of type +** Fts3SegFilter, defined in fts3Int.h. The contents of this structure +** further restrict the range of terms that callbacks are made for and +** modify the behaviour of this function. See comments above structure +** definition for details. */ -SQLITE_PRIVATE int sqlite3Fts3ExprParse( - sqlite3_tokenizer *pTokenizer, /* Tokenizer module */ - char **azCol, /* Array of column names for fts3 table */ - int nCol, /* Number of entries in azCol[] */ - int iDefaultCol, /* Default column to query */ - const char *z, int n, /* Text of MATCH query */ - Fts3Expr **ppExpr /* OUT: Parsed query structure */ +SQLITE_PRIVATE int sqlite3Fts3SegReaderIterate( + Fts3Table *p, /* Virtual table handle */ + Fts3SegReader **apSegment, /* Array of Fts3SegReader objects */ + int nSegment, /* Size of apSegment array */ + Fts3SegFilter *pFilter, /* Restrictions on range of iteration */ + int (*xFunc)(Fts3Table *, void *, char *, int, char *, int), /* Callback */ + void *pContext /* Callback context (2nd argument) */ ){ - int nParsed; - int rc; - ParseContext sParse; - sParse.pTokenizer = pTokenizer; - sParse.azCol = (const char **)azCol; - sParse.nCol = nCol; - sParse.iDefaultCol = iDefaultCol; - sParse.nNest = 0; - if( z==0 ){ - *ppExpr = 0; - return SQLITE_OK; - } - if( n<0 ){ - n = strlen(z); - } - rc = fts3ExprParse(&sParse, z, n, ppExpr, &nParsed); + int i; /* Iterator variable */ + char *aBuffer = 0; /* Buffer to merge doclists in */ + int nAlloc = 0; /* Allocated size of aBuffer buffer */ + int rc = SQLITE_OK; /* Return code */ - /* Check for mismatched parenthesis */ - if( rc==SQLITE_OK && sParse.nNest ){ - rc = SQLITE_ERROR; - sqlite3Fts3ExprFree(*ppExpr); - *ppExpr = 0; + int isIgnoreEmpty = (pFilter->flags & FTS3_SEGMENT_IGNORE_EMPTY); + int isRequirePos = (pFilter->flags & FTS3_SEGMENT_REQUIRE_POS); + int isColFilter = (pFilter->flags & FTS3_SEGMENT_COLUMN_FILTER); + int isPrefix = (pFilter->flags & FTS3_SEGMENT_PREFIX); + + /* If there are zero segments, this function is a no-op. This scenario + ** comes about only when reading from an empty database. + */ + if( nSegment==0 ) goto finished; + + /* If the Fts3SegFilter defines a specific term (or term prefix) to search + ** for, then advance each segment iterator until it points to a term of + ** equal or greater value than the specified term. This prevents many + ** unnecessary merge/sort operations for the case where single segment + ** b-tree leaf nodes contain more than one term. + */ + if( pFilter->zTerm ){ + int nTerm = pFilter->nTerm; + const char *zTerm = pFilter->zTerm; + for(i=0; iaNode ){ + int nTerm = apSegment[0]->nTerm; + char *zTerm = apSegment[0]->zTerm; + int nMerge = 1; + + /* If this is a prefix-search, and if the term that apSegment[0] points + ** to does not share a suffix with pFilter->zTerm/nTerm, then all + ** required callbacks have been made. In this case exit early. + ** + ** Similarly, if this is a search for an exact match, and the first term + ** of segment apSegment[0] is not a match, exit early. + */ + if( pFilter->zTerm ){ + if( nTermnTerm + || (!isPrefix && nTerm>pFilter->nTerm) + || memcmp(zTerm, pFilter->zTerm, pFilter->nTerm) + ){ + goto finished; + } + } -/* -** Free a parsed fts3 query expression allocated by sqlite3Fts3ExprParse(). -*/ -SQLITE_PRIVATE void sqlite3Fts3ExprFree(Fts3Expr *p){ - if( p ){ - sqlite3Fts3ExprFree(p->pLeft); - sqlite3Fts3ExprFree(p->pRight); - sqlite3_free(p); - } -} + while( nMergeaNode + && apSegment[nMerge]->nTerm==nTerm + && 0==memcmp(zTerm, apSegment[nMerge]->zTerm, nTerm) + ){ + nMerge++; + } -/**************************************************************************** -***************************************************************************** -** Everything after this point is just test code. -*/ + assert( isIgnoreEmpty || (isRequirePos && !isColFilter) ); + if( nMerge==1 && !isIgnoreEmpty ){ + Fts3SegReader *p0 = apSegment[0]; + rc = xFunc(p, pContext, zTerm, nTerm, p0->aDoclist, p0->nDoclist); + if( rc!=SQLITE_OK ) goto finished; + }else{ + int nDoclist = 0; /* Size of doclist */ + sqlite3_int64 iPrev = 0; /* Previous docid stored in doclist */ -#ifdef SQLITE_TEST + /* The current term of the first nMerge entries in the array + ** of Fts3SegReader objects is the same. The doclists must be merged + ** and a single term added to the new segment. + */ + for(i=0; ipOffsetList ){ + int j; /* Number of segments that share a docid */ + char *pList; + int nList; + int nByte; + sqlite3_int64 iDocid = apSegment[0]->iDocid; + fts3SegReaderNextDocid(apSegment[0], &pList, &nList); + j = 1; + while( jpOffsetList + && apSegment[j]->iDocid==iDocid + ){ + fts3SegReaderNextDocid(apSegment[j], 0, 0); + j++; + } + if( isColFilter ){ + fts3ColumnFilter(pFilter->iCol, &pList, &nList); + } -/* -** Function to query the hash-table of tokenizers (see README.tokenizers). -*/ -static int queryTestTokenizer( - sqlite3 *db, - const char *zName, - const sqlite3_tokenizer_module **pp -){ - int rc; - sqlite3_stmt *pStmt; - const char zSql[] = "SELECT fts3_tokenizer(?)"; + if( !isIgnoreEmpty || nList>0 ){ + nByte = sqlite3Fts3VarintLen(iDocid-iPrev) + (isRequirePos?nList+1:0); + if( nDoclist+nByte>nAlloc ){ + char *aNew; + nAlloc = nDoclist+nByte*2; + aNew = sqlite3_realloc(aBuffer, nAlloc); + if( !aNew ){ + rc = SQLITE_NOMEM; + goto finished; + } + aBuffer = aNew; + } + nDoclist += sqlite3Fts3PutVarint(&aBuffer[nDoclist], iDocid-iPrev); + iPrev = iDocid; + if( isRequirePos ){ + memcpy(&aBuffer[nDoclist], pList, nList); + nDoclist += nList; + aBuffer[nDoclist++] = '\0'; + } + } - *pp = 0; - rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); - if( rc!=SQLITE_OK ){ - return rc; - } + fts3SegReaderSort(apSegment, nMerge, j, fts3SegReaderDoclistCmp); + } - sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC); - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){ - memcpy(pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp)); + if( nDoclist>0 ){ + rc = xFunc(p, pContext, zTerm, nTerm, aBuffer, nDoclist); + if( rc!=SQLITE_OK ) goto finished; + } } + + /* If there is a term specified to filter on, and this is not a prefix + ** search, return now. The callback that corresponds to the required + ** term (if such a term exists in the index) has already been made. + */ + if( pFilter->zTerm && !isPrefix ){ + goto finished; + } + + for(i=0; ieType ){ - case FTSQUERY_PHRASE: { - Fts3Phrase *pPhrase = pExpr->pPhrase; - int i; - zBuf += sprintf(zBuf, "PHRASE %d %d", pPhrase->iColumn, pPhrase->isNot); - for(i=0; inToken; i++){ - zBuf += sprintf(zBuf," %.*s",pPhrase->aToken[i].n,pPhrase->aToken[i].z); - zBuf += sprintf(zBuf,"%s", (pPhrase->aToken[i].isPrefix?"+":"")); - } - return; +static int fts3SegmentMerge(Fts3Table *p, int iLevel){ + int i; /* Iterator variable */ + int rc; /* Return code */ + int iIdx; /* Index of new segment */ + int iNewLevel; /* Level to create new segment at */ + sqlite3_stmt *pStmt = 0; + SegmentWriter *pWriter = 0; + int nSegment = 0; /* Number of segments being merged */ + Fts3SegReader **apSegment = 0; /* Array of Segment iterators */ + Fts3SegReader *pPending = 0; /* Iterator for pending-terms */ + Fts3SegFilter filter; /* Segment term filter condition */ + + if( iLevel<0 ){ + /* This call is to merge all segments in the database to a single + ** segment. The level of the new segment is equal to the the numerically + ** greatest segment level currently present in the database. The index + ** of the new segment is always 0. + */ + iIdx = 0; + rc = sqlite3Fts3SegReaderPending(p, 0, 0, 1, &pPending); + if( rc!=SQLITE_OK ) goto finished; + rc = fts3SegmentCountMax(p, &nSegment, &iNewLevel); + if( rc!=SQLITE_OK ) goto finished; + nSegment += (pPending!=0); + if( nSegment<=1 ){ + return SQLITE_DONE; } + }else{ + /* This call is to merge all segments at level iLevel. Find the next + ** available segment index at level iLevel+1. The call to + ** fts3AllocateSegdirIdx() will merge the segments at level iLevel+1 to + ** a single iLevel+2 segment if necessary. + */ + iNewLevel = iLevel+1; + rc = fts3AllocateSegdirIdx(p, iNewLevel, &iIdx); + if( rc!=SQLITE_OK ) goto finished; + rc = fts3SegmentCount(p, iLevel, &nSegment); + if( rc!=SQLITE_OK ) goto finished; + } + assert( nSegment>0 ); + assert( iNewLevel>=0 ); + + /* Allocate space for an array of pointers to segment iterators. */ + apSegment = (Fts3SegReader**)sqlite3_malloc(sizeof(Fts3SegReader *)*nSegment); + if( !apSegment ){ + rc = SQLITE_NOMEM; + goto finished; + } + memset(apSegment, 0, sizeof(Fts3SegReader *)*nSegment); - case FTSQUERY_NEAR: - zBuf += sprintf(zBuf, "NEAR/%d ", pExpr->nNear); - break; - case FTSQUERY_NOT: - zBuf += sprintf(zBuf, "NOT "); - break; - case FTSQUERY_AND: - zBuf += sprintf(zBuf, "AND "); - break; - case FTSQUERY_OR: - zBuf += sprintf(zBuf, "OR "); - break; + /* Allocate a Fts3SegReader structure for each segment being merged. A + ** Fts3SegReader stores the state data required to iterate through all + ** entries on all leaves of a single segment. + */ + assert( SQL_SELECT_LEVEL+1==SQL_SELECT_ALL_LEVEL); + rc = fts3SqlStmt(p, SQL_SELECT_LEVEL+(iLevel<0), &pStmt, 0); + if( rc!=SQLITE_OK ) goto finished; + sqlite3_bind_int(pStmt, 1, iLevel); + for(i=0; SQLITE_ROW==(sqlite3_step(pStmt)); i++){ + rc = fts3SegReaderNew(p, pStmt, i, &apSegment[i]); + if( rc!=SQLITE_OK ){ + goto finished; + } } + rc = sqlite3_reset(pStmt); + if( pPending ){ + apSegment[i] = pPending; + pPending = 0; + } + pStmt = 0; + if( rc!=SQLITE_OK ) goto finished; + + memset(&filter, 0, sizeof(Fts3SegFilter)); + filter.flags = FTS3_SEGMENT_REQUIRE_POS; + filter.flags |= (iLevel<0 ? FTS3_SEGMENT_IGNORE_EMPTY : 0); + rc = sqlite3Fts3SegReaderIterate(p, apSegment, nSegment, + &filter, fts3MergeCallback, (void *)&pWriter + ); + if( rc!=SQLITE_OK ) goto finished; - zBuf += sprintf(zBuf, "{"); - exprToString(pExpr->pLeft, zBuf); - zBuf += strlen(zBuf); - zBuf += sprintf(zBuf, "} "); + rc = fts3DeleteSegdir(p, iLevel, apSegment, nSegment); + if( rc==SQLITE_OK ){ + rc = fts3SegWriterFlush(p, pWriter, iNewLevel, iIdx); + } - zBuf += sprintf(zBuf, "{"); - exprToString(pExpr->pRight, zBuf); - zBuf += strlen(zBuf); - zBuf += sprintf(zBuf, "}"); + finished: + fts3SegWriterFree(pWriter); + if( apSegment ){ + for(i=0; i, , , ...); -** -** The first argument, , is the name of the fts3 tokenizer used -** to parse the query expression (see README.tokenizers). The second argument -** is the query expression to parse. Each subsequent argument is the name -** of a column of the fts3 table that the query expression may refer to. -** For example: -** -** SELECT fts3_exprtest('simple', 'Bill col2:Bloggs', 'col1', 'col2'); -*/ -static void fts3ExprTest( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - sqlite3_tokenizer_module const *pModule = 0; - sqlite3_tokenizer *pTokenizer = 0; - int rc; - char **azCol = 0; - const char *zExpr; - int nExpr; - int nCol; - int ii; - Fts3Expr *pExpr; - sqlite3 *db = sqlite3_context_db_handle(context); - if( argc<3 ){ - sqlite3_result_error(context, - "Usage: fts3_exprtest(tokenizer, expr, col1, ...", -1 - ); - return; +/* +** Flush the contents of pendingTerms to a level 0 segment. +*/ +SQLITE_PRIVATE int sqlite3Fts3PendingTermsFlush(Fts3Table *p){ + int rc; /* Return Code */ + int idx; /* Index of new segment created */ + SegmentWriter *pWriter = 0; /* Used to write the segment */ + Fts3SegReader *pReader = 0; /* Used to iterate through the hash table */ + + /* Allocate a SegReader object to iterate through the contents of the + ** pending-terms table. If an error occurs, or if there are no terms + ** in the pending-terms table, return immediately. + */ + rc = sqlite3Fts3SegReaderPending(p, 0, 0, 1, &pReader); + if( rc!=SQLITE_OK || pReader==0 ){ + return rc; } - rc = queryTestTokenizer(db, - (const char *)sqlite3_value_text(argv[0]), &pModule); - if( rc==SQLITE_NOMEM ){ - sqlite3_result_error_nomem(context); - goto exprtest_out; - }else if( !pModule ){ - sqlite3_result_error(context, "No such tokenizer module", -1); - goto exprtest_out; - } + /* Determine the next index at level 0. If level 0 is already full, this + ** call may merge all existing level 0 segments into a single level 1 + ** segment. + */ + rc = fts3AllocateSegdirIdx(p, 0, &idx); + + /* If no errors have occured, iterate through the contents of the + ** pending-terms hash table using the Fts3SegReader iterator. The callback + ** writes each term (along with its doclist) to the database via the + ** SegmentWriter handle pWriter. + */ + if( rc==SQLITE_OK ){ + void *c = (void *)&pWriter; /* SegReaderIterate() callback context */ + Fts3SegFilter f; /* SegReaderIterate() parameters */ - rc = pModule->xCreate(0, 0, &pTokenizer); - assert( rc==SQLITE_NOMEM || rc==SQLITE_OK ); - if( rc==SQLITE_NOMEM ){ - sqlite3_result_error_nomem(context); - goto exprtest_out; + memset(&f, 0, sizeof(Fts3SegFilter)); + f.flags = FTS3_SEGMENT_REQUIRE_POS; + rc = sqlite3Fts3SegReaderIterate(p, &pReader, 1, &f, fts3FlushCallback, c); + } + assert( pWriter || rc!=SQLITE_OK ); + + /* If no errors have occured, flush the SegmentWriter object to the + ** database. Then delete the SegmentWriter and Fts3SegReader objects + ** allocated by this function. + */ + if( rc==SQLITE_OK ){ + rc = fts3SegWriterFlush(p, pWriter, 0, idx); } - pTokenizer->pModule = pModule; + fts3SegWriterFree(pWriter); + sqlite3Fts3SegReaderFree(p, pReader); - zExpr = (const char *)sqlite3_value_text(argv[1]); - nExpr = sqlite3_value_bytes(argv[1]); - nCol = argc-2; - azCol = (char **)sqlite3_malloc(nCol*sizeof(char *)); - if( !azCol ){ - sqlite3_result_error_nomem(context); - goto exprtest_out; - } - for(ii=0; ii)" +** +** Argument pVal contains the result of . Currently the only +** meaningful value to insert is the text 'optimize'. +*/ +static int fts3SpecialInsert(Fts3Table *p, sqlite3_value *pVal){ + int rc; /* Return Code */ + const char *zVal = (const char *)sqlite3_value_text(pVal); + int nVal = sqlite3_value_bytes(pVal); + + if( !zVal ){ + return SQLITE_NOMEM; + }else if( nVal==8 && 0==sqlite3_strnicmp(zVal, "optimize", 8) ){ + rc = fts3SegmentMerge(p, -1); + if( rc==SQLITE_DONE ){ + rc = SQLITE_OK; + }else{ + sqlite3Fts3PendingTermsClear(p); + } +#ifdef SQLITE_TEST + }else if( nVal>9 && 0==sqlite3_strnicmp(zVal, "nodesize=", 9) ){ + p->nNodeSize = atoi(&zVal[9]); + rc = SQLITE_OK; + }else if( nVal>11 && 0==sqlite3_strnicmp(zVal, "maxpending=", 9) ){ + p->nMaxPendingData = atoi(&zVal[11]); + rc = SQLITE_OK; +#endif }else{ - sqlite3_result_error(context, "Error parsing expression", -1); + rc = SQLITE_ERROR; } -exprtest_out: - if( pModule && pTokenizer ){ - rc = pModule->xDestroy(pTokenizer); - } - sqlite3_free(azCol); + return rc; } /* -** Register the query expression parser test function fts3_exprtest() -** with database connection db. +** This function does the work for the xUpdate method of FTS3 virtual +** tables. */ -SQLITE_PRIVATE void sqlite3Fts3ExprInitTestInterface(sqlite3* db){ - sqlite3_create_function( - db, "fts3_exprtest", -1, SQLITE_UTF8, 0, fts3ExprTest, 0, 0 - ); +SQLITE_PRIVATE int sqlite3Fts3UpdateMethod( + sqlite3_vtab *pVtab, /* FTS3 vtab object */ + int nArg, /* Size of argument array */ + sqlite3_value **apVal, /* Array of arguments */ + sqlite_int64 *pRowid /* OUT: The affected (or effected) rowid */ +){ + Fts3Table *p = (Fts3Table *)pVtab; + int rc = SQLITE_OK; /* Return Code */ + int isRemove = 0; /* True for an UPDATE or DELETE */ + sqlite3_int64 iRemove = 0; /* Rowid removed by UPDATE or DELETE */ + + + /* If this is a DELETE or UPDATE operation, remove the old record. */ + if( sqlite3_value_type(apVal[0])!=SQLITE_NULL ){ + int isEmpty; + rc = fts3IsEmpty(p, apVal, &isEmpty); + if( rc==SQLITE_OK ){ + if( isEmpty ){ + /* Deleting this row means the whole table is empty. In this case + ** delete the contents of all three tables and throw away any + ** data in the pendingTerms hash table. + */ + rc = fts3DeleteAll(p); + }else{ + isRemove = 1; + iRemove = sqlite3_value_int64(apVal[0]); + rc = fts3PendingTermsDocid(p, iRemove); + if( rc==SQLITE_OK ){ + rc = fts3DeleteTerms(p, apVal); + if( rc==SQLITE_OK ){ + rc = fts3SqlExec(p, SQL_DELETE_CONTENT, apVal); + } + } + } + } + }else if( sqlite3_value_type(apVal[p->nColumn+2])!=SQLITE_NULL ){ + return fts3SpecialInsert(p, apVal[p->nColumn+2]); + } + + /* If this is an INSERT or UPDATE operation, insert the new record. */ + if( nArg>1 && rc==SQLITE_OK ){ + rc = fts3InsertData(p, apVal, pRowid); + if( rc==SQLITE_OK && (!isRemove || *pRowid!=iRemove) ){ + rc = fts3PendingTermsDocid(p, *pRowid); + } + if( rc==SQLITE_OK ){ + rc = fts3InsertTerms(p, apVal); + } + } + + return rc; +} + +/* +** Flush any data in the pending-terms hash table to disk. If successful, +** merge all segments in the database (including the new segment, if +** there was any data to flush) into a single segment. +*/ +SQLITE_PRIVATE int sqlite3Fts3Optimize(Fts3Table *p){ + int rc; + rc = sqlite3_exec(p->db, "SAVEPOINT fts3", 0, 0, 0); + if( rc==SQLITE_OK ){ + rc = fts3SegmentMerge(p, -1); + if( rc==SQLITE_OK ){ + rc = sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0); + if( rc==SQLITE_OK ){ + sqlite3Fts3PendingTermsClear(p); + } + }else{ + sqlite3_exec(p->db, "ROLLBACK TO fts3", 0, 0, 0); + sqlite3_exec(p->db, "RELEASE fts3", 0, 0, 0); + } + } + return rc; } #endif -#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ -/************** End of fts3_expr.c *******************************************/ -/************** Begin file fts3_hash.c ***************************************/ +/************** End of fts3_write.c ******************************************/ +/************** Begin file fts3_snippet.c ************************************/ /* -** 2001 September 22 +** 2009 Oct 23 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: @@ -101518,1600 +105908,1338 @@ ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** -************************************************************************* -** This is the implementation of generic hash-tables used in SQLite. -** We've modified it slightly to serve as a standalone hash table -** implementation for the full-text indexing module. +****************************************************************************** */ -/* -** The code in this file is only compiled if: -** -** * The FTS3 module is being built as an extension -** (in which case SQLITE_CORE is not defined), or -** -** * The FTS3 module is being built into the core of -** SQLite (in which case SQLITE_ENABLE_FTS3 is defined). -*/ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) +typedef struct Snippet Snippet; /* -** Malloc and Free functions +** An instance of the following structure keeps track of generated +** matching-word offset information and snippets. */ -static void *fts3HashMalloc(int n){ - void *p = sqlite3_malloc(n); - if( p ){ - memset(p, 0, n); - } - return p; -} -static void fts3HashFree(void *p){ - sqlite3_free(p); +struct Snippet { + int nMatch; /* Total number of matches */ + int nAlloc; /* Space allocated for aMatch[] */ + struct snippetMatch { /* One entry for each matching term */ + char snStatus; /* Status flag for use while constructing snippets */ + short int nByte; /* Number of bytes in the term */ + short int iCol; /* The column that contains the match */ + short int iTerm; /* The index in Query.pTerms[] of the matching term */ + int iToken; /* The index of the matching document token */ + int iStart; /* The offset to the first character of the term */ + } *aMatch; /* Points to space obtained from malloc */ + char *zOffset; /* Text rendering of aMatch[] */ + int nOffset; /* strlen(zOffset) */ + char *zSnippet; /* Snippet text */ + int nSnippet; /* strlen(zSnippet) */ +}; + + +/* It is not safe to call isspace(), tolower(), or isalnum() on +** hi-bit-set characters. This is the same solution used in the +** tokenizer. +*/ +static int fts3snippetIsspace(char c){ + return (c&0x80)==0 ? isspace(c) : 0; } -/* Turn bulk memory into a hash table object by initializing the -** fields of the Hash structure. -** -** "pNew" is a pointer to the hash table that is to be initialized. -** keyClass is one of the constants -** FTS3_HASH_BINARY or FTS3_HASH_STRING. The value of keyClass -** determines what kind of key the hash table will use. "copyKey" is -** true if the hash table should make its own private copy of keys and -** false if it should just use the supplied pointer. + +/* +** A StringBuffer object holds a zero-terminated string that grows +** arbitrarily by appending. Space to hold the string is obtained +** from sqlite3_malloc(). After any memory allocation failure, +** StringBuffer.z is set to NULL and no further allocation is attempted. */ -SQLITE_PRIVATE void sqlite3Fts3HashInit(fts3Hash *pNew, int keyClass, int copyKey){ - assert( pNew!=0 ); - assert( keyClass>=FTS3_HASH_STRING && keyClass<=FTS3_HASH_BINARY ); - pNew->keyClass = keyClass; - pNew->copyKey = copyKey; - pNew->first = 0; - pNew->count = 0; - pNew->htsize = 0; - pNew->ht = 0; +typedef struct StringBuffer { + char *z; /* Text of the string. Space from malloc. */ + int nUsed; /* Number bytes of z[] used, not counting \000 terminator */ + int nAlloc; /* Bytes allocated for z[] */ +} StringBuffer; + + +/* +** Initialize a new StringBuffer. +*/ +static void fts3SnippetSbInit(StringBuffer *p){ + p->nAlloc = 100; + p->nUsed = 0; + p->z = sqlite3_malloc( p->nAlloc ); } -/* Remove all entries from a hash table. Reclaim all memory. -** Call this routine to delete a hash table or to reset a hash table -** to the empty state. +/* +** Append text to the string buffer. */ -SQLITE_PRIVATE void sqlite3Fts3HashClear(fts3Hash *pH){ - fts3HashElem *elem; /* For looping over all elements of the table */ +static void fts3SnippetAppend(StringBuffer *p, const char *zNew, int nNew){ + if( p->z==0 ) return; + if( nNew<0 ) nNew = (int)strlen(zNew); + if( p->nUsed + nNew >= p->nAlloc ){ + int nAlloc; + char *zNew; - assert( pH!=0 ); - elem = pH->first; - pH->first = 0; - fts3HashFree(pH->ht); - pH->ht = 0; - pH->htsize = 0; - while( elem ){ - fts3HashElem *next_elem = elem->next; - if( pH->copyKey && elem->pKey ){ - fts3HashFree(elem->pKey); + nAlloc = p->nUsed + nNew + p->nAlloc; + zNew = sqlite3_realloc(p->z, nAlloc); + if( zNew==0 ){ + sqlite3_free(p->z); + p->z = 0; + return; } - fts3HashFree(elem); - elem = next_elem; + p->z = zNew; + p->nAlloc = nAlloc; } - pH->count = 0; + memcpy(&p->z[p->nUsed], zNew, nNew); + p->nUsed += nNew; + p->z[p->nUsed] = 0; } -/* -** Hash and comparison functions when the mode is FTS3_HASH_STRING +/* If the StringBuffer ends in something other than white space, add a +** single space character to the end. */ -static int fts3StrHash(const void *pKey, int nKey){ - const char *z = (const char *)pKey; - int h = 0; - if( nKey<=0 ) nKey = (int) strlen(z); - while( nKey > 0 ){ - h = (h<<3) ^ h ^ *z++; - nKey--; +static void fts3SnippetAppendWhiteSpace(StringBuffer *p){ + if( p->z && p->nUsed && !fts3snippetIsspace(p->z[p->nUsed-1]) ){ + fts3SnippetAppend(p, " ", 1); } - return h & 0x7fffffff; } -static int fts3StrCompare(const void *pKey1, int n1, const void *pKey2, int n2){ - if( n1!=n2 ) return 1; - return strncmp((const char*)pKey1,(const char*)pKey2,n1); + +/* Remove white space from the end of the StringBuffer */ +static void fts3SnippetTrimWhiteSpace(StringBuffer *p){ + if( p->z ){ + while( p->nUsed && fts3snippetIsspace(p->z[p->nUsed-1]) ){ + p->nUsed--; + } + p->z[p->nUsed] = 0; + } } -/* -** Hash and comparison functions when the mode is FTS3_HASH_BINARY +/* +** Release all memory associated with the Snippet structure passed as +** an argument. */ -static int fts3BinHash(const void *pKey, int nKey){ - int h = 0; - const char *z = (const char *)pKey; - while( nKey-- > 0 ){ - h = (h<<3) ^ h ^ *(z++); +static void fts3SnippetFree(Snippet *p){ + if( p ){ + sqlite3_free(p->aMatch); + sqlite3_free(p->zOffset); + sqlite3_free(p->zSnippet); + sqlite3_free(p); } - return h & 0x7fffffff; -} -static int fts3BinCompare(const void *pKey1, int n1, const void *pKey2, int n2){ - if( n1!=n2 ) return 1; - return memcmp(pKey1,pKey2,n1); } /* -** Return a pointer to the appropriate hash function given the key class. -** -** The C syntax in this function definition may be unfamilar to some -** programmers, so we provide the following additional explanation: -** -** The name of the function is "ftsHashFunction". The function takes a -** single parameter "keyClass". The return value of ftsHashFunction() -** is a pointer to another function. Specifically, the return value -** of ftsHashFunction() is a pointer to a function that takes two parameters -** with types "const void*" and "int" and returns an "int". +** Append a single entry to the p->aMatch[] log. */ -static int (*ftsHashFunction(int keyClass))(const void*,int){ - if( keyClass==FTS3_HASH_STRING ){ - return &fts3StrHash; - }else{ - assert( keyClass==FTS3_HASH_BINARY ); - return &fts3BinHash; +static int snippetAppendMatch( + Snippet *p, /* Append the entry to this snippet */ + int iCol, int iTerm, /* The column and query term */ + int iToken, /* Matching token in document */ + int iStart, int nByte /* Offset and size of the match */ +){ + int i; + struct snippetMatch *pMatch; + if( p->nMatch+1>=p->nAlloc ){ + struct snippetMatch *pNew; + p->nAlloc = p->nAlloc*2 + 10; + pNew = sqlite3_realloc(p->aMatch, p->nAlloc*sizeof(p->aMatch[0]) ); + if( pNew==0 ){ + p->aMatch = 0; + p->nMatch = 0; + p->nAlloc = 0; + return SQLITE_NOMEM; + } + p->aMatch = pNew; } + i = p->nMatch++; + pMatch = &p->aMatch[i]; + pMatch->iCol = (short)iCol; + pMatch->iTerm = (short)iTerm; + pMatch->iToken = iToken; + pMatch->iStart = iStart; + pMatch->nByte = (short)nByte; + return SQLITE_OK; } /* -** Return a pointer to the appropriate hash function given the key class. +** Sizing information for the circular buffer used in snippetOffsetsOfColumn() +*/ +#define FTS3_ROTOR_SZ (32) +#define FTS3_ROTOR_MASK (FTS3_ROTOR_SZ-1) + +/* +** Function to iterate through the tokens of a compiled expression. ** -** For help in interpreted the obscure C code in the function definition, -** see the header comment on the previous function. +** Except, skip all tokens on the right-hand side of a NOT operator. +** This function is used to find tokens as part of snippet and offset +** generation and we do nt want snippets and offsets to report matches +** for tokens on the RHS of a NOT. */ -static int (*ftsCompareFunction(int keyClass))(const void*,int,const void*,int){ - if( keyClass==FTS3_HASH_STRING ){ - return &fts3StrCompare; +static int fts3NextExprToken(Fts3Expr **ppExpr, int *piToken){ + Fts3Expr *p = *ppExpr; + int iToken = *piToken; + if( iToken<0 ){ + /* In this case the expression p is the root of an expression tree. + ** Move to the first token in the expression tree. + */ + while( p->pLeft ){ + p = p->pLeft; + } + iToken = 0; }else{ - assert( keyClass==FTS3_HASH_BINARY ); - return &fts3BinCompare; + assert(p && p->eType==FTSQUERY_PHRASE ); + if( iToken<(p->pPhrase->nToken-1) ){ + iToken++; + }else{ + iToken = 0; + while( p->pParent && p->pParent->pLeft!=p ){ + assert( p->pParent->pRight==p ); + p = p->pParent; + } + p = p->pParent; + if( p ){ + assert( p->pRight!=0 ); + p = p->pRight; + while( p->pLeft ){ + p = p->pLeft; + } + } + } } + + *ppExpr = p; + *piToken = iToken; + return p?1:0; } -/* Link an element into the hash table +/* +** Return TRUE if the expression node pExpr is located beneath the +** RHS of a NOT operator. */ -static void fts3HashInsertElement( - fts3Hash *pH, /* The complete hash table */ - struct _fts3ht *pEntry, /* The entry into which pNew is inserted */ - fts3HashElem *pNew /* The element to be inserted */ -){ - fts3HashElem *pHead; /* First element already in pEntry */ - pHead = pEntry->chain; - if( pHead ){ - pNew->next = pHead; - pNew->prev = pHead->prev; - if( pHead->prev ){ pHead->prev->next = pNew; } - else { pH->first = pNew; } - pHead->prev = pNew; - }else{ - pNew->next = pH->first; - if( pH->first ){ pH->first->prev = pNew; } - pNew->prev = 0; - pH->first = pNew; +static int fts3ExprBeneathNot(Fts3Expr *p){ + Fts3Expr *pParent; + while( p ){ + pParent = p->pParent; + if( pParent && pParent->eType==FTSQUERY_NOT && pParent->pRight==p ){ + return 1; + } + p = pParent; } - pEntry->count++; - pEntry->chain = pNew; + return 0; } - -/* Resize the hash table so that it cantains "new_size" buckets. -** "new_size" must be a power of 2. The hash table might fail -** to resize if sqliteMalloc() fails. +/* +** Add entries to pSnippet->aMatch[] for every match that occurs against +** document zDoc[0..nDoc-1] which is stored in column iColumn. */ -static void fts3Rehash(fts3Hash *pH, int new_size){ - struct _fts3ht *new_ht; /* The new hash table */ - fts3HashElem *elem, *next_elem; /* For looping over existing elements */ - int (*xHash)(const void*,int); /* The hash function */ +static int snippetOffsetsOfColumn( + Fts3Cursor *pCur, /* The fulltest search cursor */ + Snippet *pSnippet, /* The Snippet object to be filled in */ + int iColumn, /* Index of fulltext table column */ + const char *zDoc, /* Text of the fulltext table column */ + int nDoc /* Length of zDoc in bytes */ +){ + const sqlite3_tokenizer_module *pTModule; /* The tokenizer module */ + sqlite3_tokenizer *pTokenizer; /* The specific tokenizer */ + sqlite3_tokenizer_cursor *pTCursor; /* Tokenizer cursor */ + Fts3Table *pVtab; /* The full text index */ + int nColumn; /* Number of columns in the index */ + int i, j; /* Loop counters */ + int rc; /* Return code */ + unsigned int match, prevMatch; /* Phrase search bitmasks */ + const char *zToken; /* Next token from the tokenizer */ + int nToken; /* Size of zToken */ + int iBegin, iEnd, iPos; /* Offsets of beginning and end */ - assert( (new_size & (new_size-1))==0 ); - new_ht = (struct _fts3ht *)fts3HashMalloc( new_size*sizeof(struct _fts3ht) ); - if( new_ht==0 ) return; - fts3HashFree(pH->ht); - pH->ht = new_ht; - pH->htsize = new_size; - xHash = ftsHashFunction(pH->keyClass); - for(elem=pH->first, pH->first=0; elem; elem = next_elem){ - int h = (*xHash)(elem->pKey, elem->nKey) & (new_size-1); - next_elem = elem->next; - fts3HashInsertElement(pH, &new_ht[h], elem); + /* The following variables keep a circular buffer of the last + ** few tokens */ + unsigned int iRotor = 0; /* Index of current token */ + int iRotorBegin[FTS3_ROTOR_SZ]; /* Beginning offset of token */ + int iRotorLen[FTS3_ROTOR_SZ]; /* Length of token */ + + pVtab = (Fts3Table *)pCur->base.pVtab; + nColumn = pVtab->nColumn; + pTokenizer = pVtab->pTokenizer; + pTModule = pTokenizer->pModule; + rc = pTModule->xOpen(pTokenizer, zDoc, nDoc, &pTCursor); + if( rc ) return rc; + pTCursor->pTokenizer = pTokenizer; + + prevMatch = 0; + while( (rc = pTModule->xNext(pTCursor, &zToken, &nToken, + &iBegin, &iEnd, &iPos))==SQLITE_OK ){ + Fts3Expr *pIter = pCur->pExpr; + int iIter = -1; + iRotorBegin[iRotor&FTS3_ROTOR_MASK] = iBegin; + iRotorLen[iRotor&FTS3_ROTOR_MASK] = iEnd-iBegin; + match = 0; + for(i=0; i<(FTS3_ROTOR_SZ-1) && fts3NextExprToken(&pIter, &iIter); i++){ + int nPhrase; /* Number of tokens in current phrase */ + struct PhraseToken *pToken; /* Current token */ + int iCol; /* Column index */ + + if( fts3ExprBeneathNot(pIter) ) continue; + nPhrase = pIter->pPhrase->nToken; + pToken = &pIter->pPhrase->aToken[iIter]; + iCol = pIter->pPhrase->iColumn; + if( iCol>=0 && iColn>nToken ) continue; + if( !pToken->isPrefix && pToken->nn<=nToken ); + if( memcmp(pToken->z, zToken, pToken->n) ) continue; + if( iIter>0 && (prevMatch & (1<=0; j--){ + int k = (iRotor-j) & FTS3_ROTOR_MASK; + rc = snippetAppendMatch(pSnippet, iColumn, i-j, iPos-j, + iRotorBegin[k], iRotorLen[k]); + if( rc ) goto end_offsets_of_column; + } + } + } + prevMatch = match<<1; + iRotor++; } +end_offsets_of_column: + pTModule->xClose(pTCursor); + return rc==SQLITE_DONE ? SQLITE_OK : rc; } -/* This function (for internal use only) locates an element in an -** hash table that matches the given key. The hash for this key has -** already been computed and is passed as the 4th parameter. +/* +** Remove entries from the pSnippet structure to account for the NEAR +** operator. When this is called, pSnippet contains the list of token +** offsets produced by treating all NEAR operators as AND operators. +** This function removes any entries that should not be present after +** accounting for the NEAR restriction. For example, if the queried +** document is: +** +** "A B C D E A" +** +** and the query is: +** +** A NEAR/0 E +** +** then when this function is called the Snippet contains token offsets +** 0, 4 and 5. This function removes the "0" entry (because the first A +** is not near enough to an E). +** +** When this function is called, the value pointed to by parameter piLeft is +** the integer id of the left-most token in the expression tree headed by +** pExpr. This function increments *piLeft by the total number of tokens +** in the expression tree headed by pExpr. +** +** Return 1 if any trimming occurs. Return 0 if no trimming is required. */ -static fts3HashElem *fts3FindElementByHash( - const fts3Hash *pH, /* The pH to be searched */ - const void *pKey, /* The key we are searching for */ - int nKey, - int h /* The hash for this key. */ +static int trimSnippetOffsets( + Fts3Expr *pExpr, /* The search expression */ + Snippet *pSnippet, /* The set of snippet offsets to be trimmed */ + int *piLeft /* Index of left-most token in pExpr */ ){ - fts3HashElem *elem; /* Used to loop thru the element list */ - int count; /* Number of elements left to test */ - int (*xCompare)(const void*,int,const void*,int); /* comparison function */ + if( pExpr ){ + if( trimSnippetOffsets(pExpr->pLeft, pSnippet, piLeft) ){ + return 1; + } - if( pH->ht ){ - struct _fts3ht *pEntry = &pH->ht[h]; - elem = pEntry->chain; - count = pEntry->count; - xCompare = ftsCompareFunction(pH->keyClass); - while( count-- && elem ){ - if( (*xCompare)(elem->pKey,elem->nKey,pKey,nKey)==0 ){ - return elem; + switch( pExpr->eType ){ + case FTSQUERY_PHRASE: + *piLeft += pExpr->pPhrase->nToken; + break; + case FTSQUERY_NEAR: { + /* The right-hand-side of a NEAR operator is always a phrase. The + ** left-hand-side is either a phrase or an expression tree that is + ** itself headed by a NEAR operator. The following initializations + ** set local variable iLeft to the token number of the left-most + ** token in the right-hand phrase, and iRight to the right most + ** token in the same phrase. For example, if we had: + ** + ** MATCH '"abc def" NEAR/2 "ghi jkl"' + ** + ** then iLeft will be set to 2 (token number of ghi) and nToken will + ** be set to 4. + */ + Fts3Expr *pLeft = pExpr->pLeft; + Fts3Expr *pRight = pExpr->pRight; + int iLeft = *piLeft; + int nNear = pExpr->nNear; + int nToken = pRight->pPhrase->nToken; + int jj, ii; + if( pLeft->eType==FTSQUERY_NEAR ){ + pLeft = pLeft->pRight; + } + assert( pRight->eType==FTSQUERY_PHRASE ); + assert( pLeft->eType==FTSQUERY_PHRASE ); + nToken += pLeft->pPhrase->nToken; + + for(ii=0; iinMatch; ii++){ + struct snippetMatch *p = &pSnippet->aMatch[ii]; + if( p->iTerm==iLeft ){ + int isOk = 0; + /* Snippet ii is an occurence of query term iLeft in the document. + ** It occurs at position (p->iToken) of the document. We now + ** search for an instance of token (iLeft-1) somewhere in the + ** range (p->iToken - nNear)...(p->iToken + nNear + nToken) within + ** the set of snippetMatch structures. If one is found, proceed. + ** If one cannot be found, then remove snippets ii..(ii+N-1) + ** from the matching snippets, where N is the number of tokens + ** in phrase pRight->pPhrase. + */ + for(jj=0; isOk==0 && jjnMatch; jj++){ + struct snippetMatch *p2 = &pSnippet->aMatch[jj]; + if( p2->iTerm==(iLeft-1) ){ + if( p2->iToken>=(p->iToken-nNear-1) + && p2->iToken<(p->iToken+nNear+nToken) + ){ + isOk = 1; + } + } + } + if( !isOk ){ + int kk; + for(kk=0; kkpPhrase->nToken; kk++){ + pSnippet->aMatch[kk+ii].iTerm = -2; + } + return 1; + } + } + if( p->iTerm==(iLeft-1) ){ + int isOk = 0; + for(jj=0; isOk==0 && jjnMatch; jj++){ + struct snippetMatch *p2 = &pSnippet->aMatch[jj]; + if( p2->iTerm==iLeft ){ + if( p2->iToken<=(p->iToken+nNear+1) + && p2->iToken>(p->iToken-nNear-nToken) + ){ + isOk = 1; + } + } + } + if( !isOk ){ + int kk; + for(kk=0; kkpPhrase->nToken; kk++){ + pSnippet->aMatch[ii-kk].iTerm = -2; + } + return 1; + } + } + } + break; } - elem = elem->next; + } + + if( trimSnippetOffsets(pExpr->pRight, pSnippet, piLeft) ){ + return 1; } } return 0; } -/* Remove a single entry from the hash table given a pointer to that -** element and a hash on the element's key. +/* +** Compute all offsets for the current row of the query. +** If the offsets have already been computed, this routine is a no-op. */ -static void fts3RemoveElementByHash( - fts3Hash *pH, /* The pH containing "elem" */ - fts3HashElem* elem, /* The element to be removed from the pH */ - int h /* Hash value for the element */ -){ - struct _fts3ht *pEntry; - if( elem->prev ){ - elem->prev->next = elem->next; - }else{ - pH->first = elem->next; - } - if( elem->next ){ - elem->next->prev = elem->prev; +static int snippetAllOffsets(Fts3Cursor *pCsr, Snippet **ppSnippet){ + Fts3Table *p = (Fts3Table *)pCsr->base.pVtab; /* The FTS3 virtual table */ + int nColumn; /* Number of columns. Docid does count */ + int iColumn; /* Index of of a column */ + int i; /* Loop index */ + int iFirst; /* First column to search */ + int iLast; /* Last coumn to search */ + int iTerm = 0; + Snippet *pSnippet; + int rc = SQLITE_OK; + + if( pCsr->pExpr==0 ){ + return SQLITE_OK; } - pEntry = &pH->ht[h]; - if( pEntry->chain==elem ){ - pEntry->chain = elem->next; + + pSnippet = (Snippet *)sqlite3_malloc(sizeof(Snippet)); + *ppSnippet = pSnippet; + if( !pSnippet ){ + return SQLITE_NOMEM; } - pEntry->count--; - if( pEntry->count<=0 ){ - pEntry->chain = 0; + memset(pSnippet, 0, sizeof(Snippet)); + + nColumn = p->nColumn; + iColumn = (pCsr->eSearch - 2); + if( iColumn<0 || iColumn>=nColumn ){ + /* Look for matches over all columns of the full-text index */ + iFirst = 0; + iLast = nColumn-1; + }else{ + /* Look for matches in the iColumn-th column of the index only */ + iFirst = iColumn; + iLast = iColumn; } - if( pH->copyKey && elem->pKey ){ - fts3HashFree(elem->pKey); + for(i=iFirst; rc==SQLITE_OK && i<=iLast; i++){ + const char *zDoc; + int nDoc; + zDoc = (const char*)sqlite3_column_text(pCsr->pStmt, i+1); + nDoc = sqlite3_column_bytes(pCsr->pStmt, i+1); + if( zDoc==0 && sqlite3_column_type(pCsr->pStmt, i+1)!=SQLITE_NULL ){ + rc = SQLITE_NOMEM; + }else{ + rc = snippetOffsetsOfColumn(pCsr, pSnippet, i, zDoc, nDoc); + } } - fts3HashFree( elem ); - pH->count--; - if( pH->count<=0 ){ - assert( pH->first==0 ); - assert( pH->count==0 ); - fts3HashClear(pH); + + while( trimSnippetOffsets(pCsr->pExpr, pSnippet, &iTerm) ){ + iTerm = 0; } + + return rc; } -/* Attempt to locate an element of the hash table pH with a key -** that matches pKey,nKey. Return the data for this element if it is -** found, or NULL if there is no match. +/* +** Convert the information in the aMatch[] array of the snippet +** into the string zOffset[0..nOffset-1]. This string is used as +** the return of the SQL offsets() function. */ -SQLITE_PRIVATE void *sqlite3Fts3HashFind(const fts3Hash *pH, const void *pKey, int nKey){ - int h; /* A hash on key */ - fts3HashElem *elem; /* The element that matches key */ - int (*xHash)(const void*,int); /* The hash function */ - - if( pH==0 || pH->ht==0 ) return 0; - xHash = ftsHashFunction(pH->keyClass); - assert( xHash!=0 ); - h = (*xHash)(pKey,nKey); - assert( (pH->htsize & (pH->htsize-1))==0 ); - elem = fts3FindElementByHash(pH,pKey,nKey, h & (pH->htsize-1)); - return elem ? elem->data : 0; +static void snippetOffsetText(Snippet *p){ + int i; + int cnt = 0; + StringBuffer sb; + char zBuf[200]; + if( p->zOffset ) return; + fts3SnippetSbInit(&sb); + for(i=0; inMatch; i++){ + struct snippetMatch *pMatch = &p->aMatch[i]; + if( pMatch->iTerm>=0 ){ + /* If snippetMatch.iTerm is less than 0, then the match was + ** discarded as part of processing the NEAR operator (see the + ** trimSnippetOffsetsForNear() function for details). Ignore + ** it in this case + */ + zBuf[0] = ' '; + sqlite3_snprintf(sizeof(zBuf)-1, &zBuf[cnt>0], "%d %d %d %d", + pMatch->iCol, pMatch->iTerm, pMatch->iStart, pMatch->nByte); + fts3SnippetAppend(&sb, zBuf, -1); + cnt++; + } + } + p->zOffset = sb.z; + p->nOffset = sb.z ? sb.nUsed : 0; } -/* Insert an element into the hash table pH. The key is pKey,nKey -** and the data is "data". -** -** If no element exists with a matching key, then a new -** element is created. A copy of the key is made if the copyKey -** flag is set. NULL is returned. -** -** If another element already exists with the same key, then the -** new data replaces the old data and the old data is returned. -** The key is not copied in this instance. If a malloc fails, then -** the new data is returned and the hash table is unchanged. +/* +** zDoc[0..nDoc-1] is phrase of text. aMatch[0..nMatch-1] are a set +** of matching words some of which might be in zDoc. zDoc is column +** number iCol. ** -** If the "data" parameter to this function is NULL, then the -** element corresponding to "key" is removed from the hash table. +** iBreak is suggested spot in zDoc where we could begin or end an +** excerpt. Return a value similar to iBreak but possibly adjusted +** to be a little left or right so that the break point is better. */ -SQLITE_PRIVATE void *sqlite3Fts3HashInsert( - fts3Hash *pH, /* The hash table to insert into */ - const void *pKey, /* The key */ - int nKey, /* Number of bytes in the key */ - void *data /* The data */ +static int wordBoundary( + int iBreak, /* The suggested break point */ + const char *zDoc, /* Document text */ + int nDoc, /* Number of bytes in zDoc[] */ + struct snippetMatch *aMatch, /* Matching words */ + int nMatch, /* Number of entries in aMatch[] */ + int iCol /* The column number for zDoc[] */ ){ - int hraw; /* Raw hash value of the key */ - int h; /* the hash of the key modulo hash table size */ - fts3HashElem *elem; /* Used to loop thru the element list */ - fts3HashElem *new_elem; /* New element added to the pH */ - int (*xHash)(const void*,int); /* The hash function */ - - assert( pH!=0 ); - xHash = ftsHashFunction(pH->keyClass); - assert( xHash!=0 ); - hraw = (*xHash)(pKey, nKey); - assert( (pH->htsize & (pH->htsize-1))==0 ); - h = hraw & (pH->htsize-1); - elem = fts3FindElementByHash(pH,pKey,nKey,h); - if( elem ){ - void *old_data = elem->data; - if( data==0 ){ - fts3RemoveElementByHash(pH,elem,h); - }else{ - elem->data = data; - } - return old_data; + int i; + if( iBreak<=10 ){ + return 0; } - if( data==0 ) return 0; - if( pH->htsize==0 ){ - fts3Rehash(pH,8); - if( pH->htsize==0 ){ - pH->count = 0; - return data; - } + if( iBreak>=nDoc-10 ){ + return nDoc; } - new_elem = (fts3HashElem*)fts3HashMalloc( sizeof(fts3HashElem) ); - if( new_elem==0 ) return data; - if( pH->copyKey && pKey!=0 ){ - new_elem->pKey = fts3HashMalloc( nKey ); - if( new_elem->pKey==0 ){ - fts3HashFree(new_elem); - return data; + for(i=0; ALWAYS(i0 && aMatch[i-1].iStart+aMatch[i-1].nByte>=iBreak ){ + return aMatch[i-1].iStart; } - memcpy((void*)new_elem->pKey, pKey, nKey); - }else{ - new_elem->pKey = (void*)pKey; } - new_elem->nKey = nKey; - pH->count++; - if( pH->count > pH->htsize ){ - fts3Rehash(pH,pH->htsize*2); + for(i=1; i<=10; i++){ + if( fts3snippetIsspace(zDoc[iBreak-i]) ){ + return iBreak - i + 1; + } + if( fts3snippetIsspace(zDoc[iBreak+i]) ){ + return iBreak + i + 1; + } } - assert( pH->htsize>0 ); - assert( (pH->htsize & (pH->htsize-1))==0 ); - h = hraw & (pH->htsize-1); - fts3HashInsertElement(pH, &pH->ht[h], new_elem); - new_elem->data = data; - return 0; + return iBreak; } -#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ -/************** End of fts3_hash.c *******************************************/ -/************** Begin file fts3_porter.c *************************************/ -/* -** 2006 September 30 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -************************************************************************* -** Implementation of the full-text-search tokenizer that implements -** a Porter stemmer. -*/ /* -** The code in this file is only compiled if: -** -** * The FTS3 module is being built as an extension -** (in which case SQLITE_CORE is not defined), or -** -** * The FTS3 module is being built into the core of -** SQLite (in which case SQLITE_ENABLE_FTS3 is defined). -*/ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - - - - -/* -** Class derived from sqlite3_tokenizer -*/ -typedef struct porter_tokenizer { - sqlite3_tokenizer base; /* Base class */ -} porter_tokenizer; - -/* -** Class derived from sqlit3_tokenizer_cursor -*/ -typedef struct porter_tokenizer_cursor { - sqlite3_tokenizer_cursor base; - const char *zInput; /* input we are tokenizing */ - int nInput; /* size of the input */ - int iOffset; /* current position in zInput */ - int iToken; /* index of next token to be returned */ - char *zToken; /* storage for current token */ - int nAllocated; /* space allocated to zToken buffer */ -} porter_tokenizer_cursor; - - -/* Forward declaration */ -static const sqlite3_tokenizer_module porterTokenizerModule; - - -/* -** Create a new tokenizer instance. -*/ -static int porterCreate( - int argc, const char * const *argv, - sqlite3_tokenizer **ppTokenizer -){ - porter_tokenizer *t; - t = (porter_tokenizer *) sqlite3_malloc(sizeof(*t)); - if( t==NULL ) return SQLITE_NOMEM; - memset(t, 0, sizeof(*t)); - *ppTokenizer = &t->base; - return SQLITE_OK; -} - -/* -** Destroy a tokenizer +** Allowed values for Snippet.aMatch[].snStatus */ -static int porterDestroy(sqlite3_tokenizer *pTokenizer){ - sqlite3_free(pTokenizer); - return SQLITE_OK; -} +#define SNIPPET_IGNORE 0 /* It is ok to omit this match from the snippet */ +#define SNIPPET_DESIRED 1 /* We want to include this match in the snippet */ /* -** Prepare to begin tokenizing a particular string. The input -** string to be tokenized is zInput[0..nInput-1]. A cursor -** used to incrementally tokenize this string is returned in -** *ppCursor. +** Generate the text of a snippet. */ -static int porterOpen( - sqlite3_tokenizer *pTokenizer, /* The tokenizer */ - const char *zInput, int nInput, /* String to be tokenized */ - sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */ +static void snippetText( + Fts3Cursor *pCursor, /* The cursor we need the snippet for */ + Snippet *pSnippet, + const char *zStartMark, /* Markup to appear before each match */ + const char *zEndMark, /* Markup to appear after each match */ + const char *zEllipsis /* Ellipsis mark */ ){ - porter_tokenizer_cursor *c; + int i, j; + struct snippetMatch *aMatch; + int nMatch; + int nDesired; + StringBuffer sb; + int tailCol; + int tailOffset; + int iCol; + int nDoc; + const char *zDoc; + int iStart, iEnd; + int tailEllipsis = 0; + int iMatch; + - c = (porter_tokenizer_cursor *) sqlite3_malloc(sizeof(*c)); - if( c==NULL ) return SQLITE_NOMEM; + sqlite3_free(pSnippet->zSnippet); + pSnippet->zSnippet = 0; + aMatch = pSnippet->aMatch; + nMatch = pSnippet->nMatch; + fts3SnippetSbInit(&sb); - c->zInput = zInput; - if( zInput==0 ){ - c->nInput = 0; - }else if( nInput<0 ){ - c->nInput = (int)strlen(zInput); - }else{ - c->nInput = nInput; + for(i=0; iiOffset = 0; /* start tokenizing at the beginning */ - c->iToken = 0; - c->zToken = NULL; /* no space allocated, yet. */ - c->nAllocated = 0; - - *ppCursor = &c->base; - return SQLITE_OK; -} - -/* -** Close a tokenization cursor previously opened by a call to -** porterOpen() above. -*/ -static int porterClose(sqlite3_tokenizer_cursor *pCursor){ - porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor; - sqlite3_free(c->zToken); - sqlite3_free(c); - return SQLITE_OK; -} -/* -** Vowel or consonant -*/ -static const char cType[] = { - 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, - 1, 1, 1, 2, 1 -}; - -/* -** isConsonant() and isVowel() determine if their first character in -** the string they point to is a consonant or a vowel, according -** to Porter ruls. -** -** A consonate is any letter other than 'a', 'e', 'i', 'o', or 'u'. -** 'Y' is a consonant unless it follows another consonant, -** in which case it is a vowel. -** -** In these routine, the letters are in reverse order. So the 'y' rule -** is that 'y' is a consonant unless it is followed by another -** consonent. -*/ -static int isVowel(const char*); -static int isConsonant(const char *z){ - int j; - char x = *z; - if( x==0 ) return 0; - assert( x>='a' && x<='z' ); - j = cType[x-'a']; - if( j<2 ) return j; - return z[1]==0 || isVowel(z + 1); -} -static int isVowel(const char *z){ - int j; - char x = *z; - if( x==0 ) return 0; - assert( x>='a' && x<='z' ); - j = cType[x-'a']; - if( j<2 ) return 1-j; - return isConsonant(z + 1); -} - -/* -** Let any sequence of one or more vowels be represented by V and let -** C be sequence of one or more consonants. Then every word can be -** represented as: -** -** [C] (VC){m} [V] -** -** In prose: A word is an optional consonant followed by zero or -** vowel-consonant pairs followed by an optional vowel. "m" is the -** number of vowel consonant pairs. This routine computes the value -** of m for the first i bytes of a word. -** -** Return true if the m-value for z is 1 or more. In other words, -** return true if z contains at least one vowel that is followed -** by a consonant. -** -** In this routine z[] is in reverse order. So we are really looking -** for an instance of of a consonant followed by a vowel. -*/ -static int m_gt_0(const char *z){ - while( isVowel(z) ){ z++; } - if( *z==0 ) return 0; - while( isConsonant(z) ){ z++; } - return *z!=0; -} - -/* Like mgt0 above except we are looking for a value of m which is -** exactly 1 -*/ -static int m_eq_1(const char *z){ - while( isVowel(z) ){ z++; } - if( *z==0 ) return 0; - while( isConsonant(z) ){ z++; } - if( *z==0 ) return 0; - while( isVowel(z) ){ z++; } - if( *z==0 ) return 1; - while( isConsonant(z) ){ z++; } - return *z==0; -} - -/* Like mgt0 above except we are looking for a value of m>1 instead -** or m>0 -*/ -static int m_gt_1(const char *z){ - while( isVowel(z) ){ z++; } - if( *z==0 ) return 0; - while( isConsonant(z) ){ z++; } - if( *z==0 ) return 0; - while( isVowel(z) ){ z++; } - if( *z==0 ) return 0; - while( isConsonant(z) ){ z++; } - return *z!=0; -} - -/* -** Return TRUE if there is a vowel anywhere within z[0..n-1] -*/ -static int hasVowel(const char *z){ - while( isConsonant(z) ){ z++; } - return *z!=0; -} - -/* -** Return TRUE if the word ends in a double consonant. -** -** The text is reversed here. So we are really looking at -** the first two characters of z[]. -*/ -static int doubleConsonant(const char *z){ - return isConsonant(z) && z[0]==z[1] && isConsonant(z+1); -} - -/* -** Return TRUE if the word ends with three letters which -** are consonant-vowel-consonent and where the final consonant -** is not 'w', 'x', or 'y'. -** -** The word is reversed here. So we are really checking the -** first three letters and the first one cannot be in [wxy]. -*/ -static int star_oh(const char *z){ - return - z[0]!=0 && isConsonant(z) && - z[0]!='w' && z[0]!='x' && z[0]!='y' && - z[1]!=0 && isVowel(z+1) && - z[2]!=0 && isConsonant(z+2); -} - -/* -** If the word ends with zFrom and xCond() is true for the stem -** of the word that preceeds the zFrom ending, then change the -** ending to zTo. -** -** The input word *pz and zFrom are both in reverse order. zTo -** is in normal order. -** -** Return TRUE if zFrom matches. Return FALSE if zFrom does not -** match. Not that TRUE is returned even if xCond() fails and -** no substitution occurs. -*/ -static int stem( - char **pz, /* The word being stemmed (Reversed) */ - const char *zFrom, /* If the ending matches this... (Reversed) */ - const char *zTo, /* ... change the ending to this (not reversed) */ - int (*xCond)(const char*) /* Condition that must be true */ -){ - char *z = *pz; - while( *zFrom && *zFrom==*z ){ z++; zFrom++; } - if( *zFrom!=0 ) return 0; - if( xCond && !xCond(z) ) return 1; - while( *zTo ){ - *(--z) = *(zTo++); + nDesired = 0; + for(i=0; i='A' && c<='Z' ){ - zOut[i] = c - 'A' + 'a'; - }else{ - if( c>='0' && c<='9' ) hasDigit = 1; - zOut[i] = c; + iMatch = 0; + tailCol = -1; + tailOffset = 0; + for(i=0; i0; i++){ + if( aMatch[i].snStatus!=SNIPPET_DESIRED ) continue; + nDesired--; + iCol = aMatch[i].iCol; + zDoc = (const char*)sqlite3_column_text(pCursor->pStmt, iCol+1); + nDoc = sqlite3_column_bytes(pCursor->pStmt, iCol+1); + iStart = aMatch[i].iStart - 40; + iStart = wordBoundary(iStart, zDoc, nDoc, aMatch, nMatch, iCol); + if( iStart<=10 ){ + iStart = 0; } - } - mx = hasDigit ? 3 : 10; - if( nIn>mx*2 ){ - for(j=mx, i=nIn-mx; i=sizeof(zReverse)-7 ){ - /* The word is too big or too small for the porter stemmer. - ** Fallback to the copy stemmer */ - copy_stemmer(zIn, nIn, zOut, pnOut); - return; - } - for(i=0, j=sizeof(zReverse)-6; i='A' && c<='Z' ){ - zReverse[j] = c + 'a' - 'A'; - }else if( c>='a' && c<='z' ){ - zReverse[j] = c; - }else{ - /* The use of a character not in [a-zA-Z] means that we fallback - ** to the copy stemmer */ - copy_stemmer(zIn, nIn, zOut, pnOut); - return; + if( (iCol!=tailCol && tailCol>=0) || iStart!=tailOffset ){ + fts3SnippetTrimWhiteSpace(&sb); + fts3SnippetAppendWhiteSpace(&sb); + fts3SnippetAppend(&sb, zEllipsis, -1); + fts3SnippetAppendWhiteSpace(&sb); } - } - memset(&zReverse[sizeof(zReverse)-5], 0, 5); - z = &zReverse[j+1]; - - - /* Step 1a */ - if( z[0]=='s' ){ - if( - !stem(&z, "sess", "ss", 0) && - !stem(&z, "sei", "i", 0) && - !stem(&z, "ss", "ss", 0) - ){ - z++; + iEnd = aMatch[i].iStart + aMatch[i].nByte + 40; + iEnd = wordBoundary(iEnd, zDoc, nDoc, aMatch, nMatch, iCol); + if( iEnd>=nDoc-10 ){ + iEnd = nDoc; + tailEllipsis = 0; + }else{ + tailEllipsis = 1; } - } - - /* Step 1b */ - z2 = z; - if( stem(&z, "dee", "ee", m_gt_0) ){ - /* Do nothing. The work was all in the test */ - }else if( - (stem(&z, "gni", "", hasVowel) || stem(&z, "de", "", hasVowel)) - && z!=z2 - ){ - if( stem(&z, "ta", "ate", 0) || - stem(&z, "lb", "ble", 0) || - stem(&z, "zi", "ize", 0) ){ - /* Do nothing. The work was all in the test */ - }else if( doubleConsonant(z) && (*z!='l' && *z!='s' && *z!='z') ){ - z++; - }else if( m_eq_1(z) && star_oh(z) ){ - *(--z) = 'e'; - } - } - - /* Step 1c */ - if( z[0]=='y' && hasVowel(z+1) ){ - z[0] = 'i'; - } - - /* Step 2 */ - switch( z[1] ){ - case 'a': - stem(&z, "lanoita", "ate", m_gt_0) || - stem(&z, "lanoit", "tion", m_gt_0); - break; - case 'c': - stem(&z, "icne", "ence", m_gt_0) || - stem(&z, "icna", "ance", m_gt_0); - break; - case 'e': - stem(&z, "rezi", "ize", m_gt_0); - break; - case 'g': - stem(&z, "igol", "log", m_gt_0); - break; - case 'l': - stem(&z, "ilb", "ble", m_gt_0) || - stem(&z, "illa", "al", m_gt_0) || - stem(&z, "iltne", "ent", m_gt_0) || - stem(&z, "ile", "e", m_gt_0) || - stem(&z, "ilsuo", "ous", m_gt_0); - break; - case 'o': - stem(&z, "noitazi", "ize", m_gt_0) || - stem(&z, "noita", "ate", m_gt_0) || - stem(&z, "rota", "ate", m_gt_0); - break; - case 's': - stem(&z, "msila", "al", m_gt_0) || - stem(&z, "ssenevi", "ive", m_gt_0) || - stem(&z, "ssenluf", "ful", m_gt_0) || - stem(&z, "ssensuo", "ous", m_gt_0); - break; - case 't': - stem(&z, "itila", "al", m_gt_0) || - stem(&z, "itivi", "ive", m_gt_0) || - stem(&z, "itilib", "ble", m_gt_0); - break; - } - - /* Step 3 */ - switch( z[0] ){ - case 'e': - stem(&z, "etaci", "ic", m_gt_0) || - stem(&z, "evita", "", m_gt_0) || - stem(&z, "ezila", "al", m_gt_0); - break; - case 'i': - stem(&z, "itici", "ic", m_gt_0); - break; - case 'l': - stem(&z, "laci", "ic", m_gt_0) || - stem(&z, "luf", "", m_gt_0); - break; - case 's': - stem(&z, "ssen", "", m_gt_0); - break; - } - - /* Step 4 */ - switch( z[1] ){ - case 'a': - if( z[0]=='l' && m_gt_1(z+2) ){ - z += 2; - } - break; - case 'c': - if( z[0]=='e' && z[2]=='n' && (z[3]=='a' || z[3]=='e') && m_gt_1(z+4) ){ - z += 4; - } - break; - case 'e': - if( z[0]=='r' && m_gt_1(z+2) ){ - z += 2; - } - break; - case 'i': - if( z[0]=='c' && m_gt_1(z+2) ){ - z += 2; - } - break; - case 'l': - if( z[0]=='e' && z[2]=='b' && (z[3]=='a' || z[3]=='i') && m_gt_1(z+4) ){ - z += 4; - } - break; - case 'n': - if( z[0]=='t' ){ - if( z[2]=='a' ){ - if( m_gt_1(z+3) ){ - z += 3; - } - }else if( z[2]=='e' ){ - stem(&z, "tneme", "", m_gt_1) || - stem(&z, "tnem", "", m_gt_1) || - stem(&z, "tne", "", m_gt_1); - } - } - break; - case 'o': - if( z[0]=='u' ){ - if( m_gt_1(z+2) ){ - z += 2; - } - }else if( z[3]=='s' || z[3]=='t' ){ - stem(&z, "noi", "", m_gt_1); - } - break; - case 's': - if( z[0]=='m' && z[2]=='i' && m_gt_1(z+3) ){ - z += 3; - } - break; - case 't': - stem(&z, "eta", "", m_gt_1) || - stem(&z, "iti", "", m_gt_1); - break; - case 'u': - if( z[0]=='s' && z[2]=='o' && m_gt_1(z+3) ){ - z += 3; - } - break; - case 'v': - case 'z': - if( z[0]=='e' && z[2]=='i' && m_gt_1(z+3) ){ - z += 3; - } - break; - } - - /* Step 5a */ - if( z[0]=='e' ){ - if( m_gt_1(z+1) ){ - z++; - }else if( m_eq_1(z+1) && !star_oh(z+1) ){ - z++; + while( iMatchzSnippet = sb.z; + pSnippet->nSnippet = sb.z ? sb.nUsed : 0; } -/* -** Characters that can be part of a token. We assume any character -** whose value is greater than 0x80 (any UTF character) can be -** part of a token. In other words, delimiters all must have -** values of 0x7f or lower. -*/ -static const char porterIdChar[] = { -/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, /* 3x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 4x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, /* 5x */ - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* 6x */ - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, /* 7x */ -}; -#define isDelim(C) (((ch=C)&0x80)==0 && (ch<0x30 || !porterIdChar[ch-0x30])) - -/* -** Extract the next token from a tokenization cursor. The cursor must -** have been opened by a prior call to porterOpen(). -*/ -static int porterNext( - sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by porterOpen */ - const char **pzToken, /* OUT: *pzToken is the token text */ - int *pnBytes, /* OUT: Number of bytes in token */ - int *piStartOffset, /* OUT: Starting offset of token */ - int *piEndOffset, /* OUT: Ending offset of token */ - int *piPosition /* OUT: Position integer of token */ +SQLITE_PRIVATE void sqlite3Fts3Offsets( + sqlite3_context *pCtx, /* SQLite function call context */ + Fts3Cursor *pCsr /* Cursor object */ ){ - porter_tokenizer_cursor *c = (porter_tokenizer_cursor *) pCursor; - const char *z = c->zInput; - - while( c->iOffsetnInput ){ - int iStartOffset, ch; - - /* Scan past delimiter characters */ - while( c->iOffsetnInput && isDelim(z[c->iOffset]) ){ - c->iOffset++; - } - - /* Count non-delimiter characters. */ - iStartOffset = c->iOffset; - while( c->iOffsetnInput && !isDelim(z[c->iOffset]) ){ - c->iOffset++; - } - - if( c->iOffset>iStartOffset ){ - int n = c->iOffset-iStartOffset; - if( n>c->nAllocated ){ - c->nAllocated = n+20; - c->zToken = sqlite3_realloc(c->zToken, c->nAllocated); - if( c->zToken==NULL ) return SQLITE_NOMEM; - } - porter_stemmer(&z[iStartOffset], n, c->zToken, pnBytes); - *pzToken = c->zToken; - *piStartOffset = iStartOffset; - *piEndOffset = c->iOffset; - *piPosition = c->iToken++; - return SQLITE_OK; + Snippet *p; /* Snippet structure */ + int rc = snippetAllOffsets(pCsr, &p); + if( rc==SQLITE_OK ){ + snippetOffsetText(p); + if( p->zOffset ){ + sqlite3_result_text(pCtx, p->zOffset, p->nOffset, SQLITE_TRANSIENT); + }else{ + sqlite3_result_error_nomem(pCtx); } + }else{ + sqlite3_result_error_nomem(pCtx); } - return SQLITE_DONE; + fts3SnippetFree(p); } -/* -** The set of routines that implement the porter-stemmer tokenizer -*/ -static const sqlite3_tokenizer_module porterTokenizerModule = { - 0, - porterCreate, - porterDestroy, - porterOpen, - porterClose, - porterNext, -}; - -/* -** Allocate a new porter tokenizer. Return a pointer to the new -** tokenizer in *ppModule -*/ -SQLITE_PRIVATE void sqlite3Fts3PorterTokenizerModule( - sqlite3_tokenizer_module const**ppModule +SQLITE_PRIVATE void sqlite3Fts3Snippet( + sqlite3_context *pCtx, /* SQLite function call context */ + Fts3Cursor *pCsr, /* Cursor object */ + const char *zStart, /* Snippet start text - "" */ + const char *zEnd, /* Snippet end text - "" */ + const char *zEllipsis /* Snippet ellipsis text - "..." */ ){ - *ppModule = &porterTokenizerModule; + Snippet *p; /* Snippet structure */ + int rc = snippetAllOffsets(pCsr, &p); + if( rc==SQLITE_OK ){ + snippetText(pCsr, p, zStart, zEnd, zEllipsis); + if( p->zSnippet ){ + sqlite3_result_text(pCtx, p->zSnippet, p->nSnippet, SQLITE_TRANSIENT); + }else{ + sqlite3_result_error_nomem(pCtx); + } + }else{ + sqlite3_result_error_nomem(pCtx); + } + fts3SnippetFree(p); } -#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ - -/************** End of fts3_porter.c *****************************************/ -/************** Begin file fts3_tokenizer.c **********************************/ -/* -** 2007 June 22 -** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: -** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. -** -****************************************************************************** -** -** This is part of an SQLite module implementing full-text search. -** This particular file implements the generic tokenizer interface. -*/ - -/* -** The code in this file is only compiled if: -** -** * The FTS3 module is being built as an extension -** (in which case SQLITE_CORE is not defined), or -** -** * The FTS3 module is being built into the core of -** SQLite (in which case SQLITE_ENABLE_FTS3 is defined). +/************************************************************************* +** Below this point is the alternative, experimental snippet() implementation. */ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) -#ifndef SQLITE_CORE - SQLITE_EXTENSION_INIT1 -#endif +#define SNIPPET_BUFFER_CHUNK 64 +#define SNIPPET_BUFFER_SIZE SNIPPET_BUFFER_CHUNK*4 +#define SNIPPET_BUFFER_MASK (SNIPPET_BUFFER_SIZE-1) +static void fts3GetDeltaPosition(char **pp, int *piPos){ + int iVal; + *pp += sqlite3Fts3GetVarint32(*pp, &iVal); + *piPos += (iVal-2); +} /* -** Implementation of the SQL scalar function for accessing the underlying -** hash table. This function may be called as follows: -** -** SELECT (); -** SELECT (, ); -** -** where is the name passed as the second argument -** to the sqlite3Fts3InitHashTable() function (e.g. 'fts3_tokenizer'). +** Iterate through all phrase nodes in an FTS3 query, except those that +** are part of a sub-tree that is the right-hand-side of a NOT operator. +** For each phrase node found, the supplied callback function is invoked. ** -** If the argument is specified, it must be a blob value -** containing a pointer to be stored as the hash data corresponding -** to the string . If is not specified, then -** the string must already exist in the has table. Otherwise, -** an error is returned. -** -** Whether or not the argument is specified, the value returned -** is a blob containing the pointer stored as the hash data corresponding -** to string (after the hash-table is updated, if applicable). +** If the callback function returns anything other than SQLITE_OK, +** the iteration is abandoned and the error code returned immediately. +** Otherwise, SQLITE_OK is returned after a callback has been made for +** all eligible phrase nodes. */ -static void scalarFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - fts3Hash *pHash; - void *pPtr = 0; - const unsigned char *zName; - int nName; - - assert( argc==1 || argc==2 ); - - pHash = (fts3Hash *)sqlite3_user_data(context); - - zName = sqlite3_value_text(argv[0]); - nName = sqlite3_value_bytes(argv[0])+1; - - if( argc==2 ){ - void *pOld; - int n = sqlite3_value_bytes(argv[1]); - if( n!=sizeof(pPtr) ){ - sqlite3_result_error(context, "argument type mismatch", -1); - return; - } - pPtr = *(void **)sqlite3_value_blob(argv[1]); - pOld = sqlite3Fts3HashInsert(pHash, (void *)zName, nName, pPtr); - if( pOld==pPtr ){ - sqlite3_result_error(context, "out of memory", -1); - return; +static int fts3ExprIterate( + Fts3Expr *pExpr, /* Expression to iterate phrases of */ + int (*x)(Fts3Expr *, void *), /* Callback function to invoke for phrases */ + void *pCtx /* Second argument to pass to callback */ +){ + int rc; + int eType = pExpr->eType; + if( eType==FTSQUERY_NOT ){ + rc = SQLITE_OK; + }else if( eType!=FTSQUERY_PHRASE ){ + assert( pExpr->pLeft && pExpr->pRight ); + rc = fts3ExprIterate(pExpr->pLeft, x, pCtx); + if( rc==SQLITE_OK ){ + rc = fts3ExprIterate(pExpr->pRight, x, pCtx); } }else{ - pPtr = sqlite3Fts3HashFind(pHash, zName, nName); - if( !pPtr ){ - char *zErr = sqlite3_mprintf("unknown tokenizer: %s", zName); - sqlite3_result_error(context, zErr, -1); - sqlite3_free(zErr); - return; - } + rc = x(pExpr, pCtx); } - - sqlite3_result_blob(context, (void *)&pPtr, sizeof(pPtr), SQLITE_TRANSIENT); + return rc; } -#ifdef SQLITE_TEST +typedef struct LoadDoclistCtx LoadDoclistCtx; +struct LoadDoclistCtx { + Fts3Table *pTab; /* FTS3 Table */ + int nPhrase; /* Number of phrases so far */ +}; +static int fts3ExprLoadDoclistsCb(Fts3Expr *pExpr, void *ctx){ + int rc = SQLITE_OK; + LoadDoclistCtx *p = (LoadDoclistCtx *)ctx; + p->nPhrase++; + if( pExpr->isLoaded==0 ){ + rc = sqlite3Fts3ExprLoadDoclist(p->pTab, pExpr); + pExpr->isLoaded = 1; + if( rc==SQLITE_OK && pExpr->aDoclist ){ + pExpr->pCurrent = pExpr->aDoclist; + pExpr->pCurrent += sqlite3Fts3GetVarint(pExpr->pCurrent,&pExpr->iCurrent); + } + } + return rc; +} + +static int fts3ExprLoadDoclists(Fts3Cursor *pCsr, int *pnPhrase){ + int rc; + LoadDoclistCtx sCtx = {0, 0}; + sCtx.pTab = (Fts3Table *)pCsr->base.pVtab; + rc = fts3ExprIterate(pCsr->pExpr, fts3ExprLoadDoclistsCb, (void *)&sCtx); + *pnPhrase = sCtx.nPhrase; + return rc; +} /* -** Implementation of a special SQL scalar function for testing tokenizers -** designed to be used in concert with the Tcl testing framework. This -** function must be called with two arguments: -** -** SELECT (, ); -** SELECT (, ); -** -** where is the name passed as the second argument -** to the sqlite3Fts3InitHashTable() function (e.g. 'fts3_tokenizer') -** concatenated with the string '_test' (e.g. 'fts3_tokenizer_test'). -** -** The return value is a string that may be interpreted as a Tcl -** list. For each token in the , three elements are -** added to the returned list. The first is the token position, the -** second is the token text (folded, stemmed, etc.) and the third is the -** substring of associated with the token. For example, -** using the built-in "simple" tokenizer: -** -** SELECT fts_tokenizer_test('simple', 'I don't see how'); +** Each call to this function populates a chunk of a snippet-buffer +** SNIPPET_BUFFER_CHUNK bytes in size. ** -** will return the string: -** -** "{0 i I 1 dont don't 2 see see 3 how how}" -** +** Return true if the end of the data has been reached (and all subsequent +** calls to fts3LoadSnippetBuffer() with the same arguments will be no-ops), +** or false otherwise. */ -static void testFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv +static int fts3LoadSnippetBuffer( + int iPos, /* Document token offset to load data for */ + u8 *aBuffer, /* Circular snippet buffer to populate */ + int nList, /* Number of position lists in appList */ + char **apList, /* IN/OUT: nList position list pointers */ + int *aiPrev /* IN/OUT: Previous positions read */ ){ - fts3Hash *pHash; - sqlite3_tokenizer_module *p; - sqlite3_tokenizer *pTokenizer = 0; - sqlite3_tokenizer_cursor *pCsr = 0; - - const char *zErr = 0; - - const char *zName; - int nName; - const char *zInput; - int nInput; + int i; + int nFin = 0; - const char *zArg = 0; + assert( (iPos&(SNIPPET_BUFFER_CHUNK-1))==0 ); - const char *zToken; - int nToken; - int iStart; - int iEnd; - int iPos; + memset(&aBuffer[iPos&SNIPPET_BUFFER_MASK], 0, SNIPPET_BUFFER_CHUNK); - Tcl_Obj *pRet; + for(i=0; i=iPos ){ + aBuffer[iPrev&SNIPPET_BUFFER_MASK] = (u8)(i+1); + } + if( 0==((*pList)&0xFE) ){ + nFin++; + break; + } + fts3GetDeltaPosition(&pList, &iPrev); + } - if( argc==3 ){ - zArg = (const char *)sqlite3_value_text(argv[1]); + aiPrev[i] = iPrev; + apList[i] = pList; } - pHash = (fts3Hash *)sqlite3_user_data(context); - p = (sqlite3_tokenizer_module *)sqlite3Fts3HashFind(pHash, zName, nName+1); + return (nFin==nList); +} - if( !p ){ - char *zErr = sqlite3_mprintf("unknown tokenizer: %s", zName); - sqlite3_result_error(context, zErr, -1); - sqlite3_free(zErr); - return; - } +typedef struct SnippetCtx SnippetCtx; +struct SnippetCtx { + Fts3Cursor *pCsr; + int iCol; + int iPhrase; + int *aiPrev; + int *anToken; + char **apList; +}; - pRet = Tcl_NewObj(); - Tcl_IncrRefCount(pRet); +static int fts3SnippetFindPositions(Fts3Expr *pExpr, void *ctx){ + SnippetCtx *p = (SnippetCtx *)ctx; + int iPhrase = p->iPhrase++; + char *pCsr; - if( SQLITE_OK!=p->xCreate(zArg ? 1 : 0, &zArg, &pTokenizer) ){ - zErr = "error in xCreate()"; - goto finish; - } - pTokenizer->pModule = p; - if( SQLITE_OK!=p->xOpen(pTokenizer, zInput, nInput, &pCsr) ){ - zErr = "error in xOpen()"; - goto finish; - } - pCsr->pTokenizer = pTokenizer; + p->anToken[iPhrase] = pExpr->pPhrase->nToken; + pCsr = sqlite3Fts3FindPositions(pExpr, p->pCsr->iPrevId, p->iCol); - while( SQLITE_OK==p->xNext(pCsr, &zToken, &nToken, &iStart, &iEnd, &iPos) ){ - Tcl_ListObjAppendElement(0, pRet, Tcl_NewIntObj(iPos)); - Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zToken, nToken)); - zToken = &zInput[iStart]; - nToken = iEnd-iStart; - Tcl_ListObjAppendElement(0, pRet, Tcl_NewStringObj(zToken, nToken)); + if( pCsr ){ + int iVal; + pCsr += sqlite3Fts3GetVarint32(pCsr, &iVal); + p->apList[iPhrase] = pCsr; + p->aiPrev[iPhrase] = iVal-2; } + return SQLITE_OK; +} - if( SQLITE_OK!=p->xClose(pCsr) ){ - zErr = "error in xClose()"; - goto finish; - } - if( SQLITE_OK!=p->xDestroy(pTokenizer) ){ - zErr = "error in xDestroy()"; - goto finish; - } +static void fts3SnippetCnt( + int iIdx, + int nSnippet, + int *anCnt, + u8 *aBuffer, + int *anToken, + u64 *pHlmask +){ + int iSub = (iIdx-1)&SNIPPET_BUFFER_MASK; + int iAdd = (iIdx+nSnippet-1)&SNIPPET_BUFFER_MASK; + int iSub2 = (iIdx+(nSnippet/3)-1)&SNIPPET_BUFFER_MASK; + int iAdd2 = (iIdx+(nSnippet*2/3)-1)&SNIPPET_BUFFER_MASK; -finish: - if( zErr ){ - sqlite3_result_error(context, zErr, -1); - }else{ - sqlite3_result_text(context, Tcl_GetString(pRet), -1, SQLITE_TRANSIENT); + u64 h = *pHlmask; + + anCnt[ aBuffer[iSub] ]--; + anCnt[ aBuffer[iSub2] ]--; + anCnt[ aBuffer[iAdd] ]++; + anCnt[ aBuffer[iAdd2] ]++; + + h = h >> 1; + if( aBuffer[iAdd] ){ + int j; + for(j=anToken[aBuffer[iAdd]-1]; j>=1; j--){ + h |= (u64)1 << (nSnippet-j); + } } - Tcl_DecrRefCount(pRet); + *pHlmask = h; } -static -int registerTokenizer( - sqlite3 *db, - char *zName, - const sqlite3_tokenizer_module *p -){ - int rc; - sqlite3_stmt *pStmt; - const char zSql[] = "SELECT fts3_tokenizer(?, ?)"; +static int fts3SnippetScore(int n, int *anCnt){ + int j; + int iScore = 0; + for(j=1; j<=n; j++){ + int nCnt = anCnt[j]; + iScore += nCnt + (nCnt ? 1000 : 0); + } + return iScore; +} + +static int fts3BestSnippet( + int nSnippet, /* Desired snippet length */ + Fts3Cursor *pCsr, /* Cursor to create snippet for */ + int iCol, /* Index of column to create snippet from */ + int *piPos, /* OUT: Starting token for best snippet */ + u64 *pHlmask /* OUT: Highlight mask for best snippet */ +){ + int rc; /* Return Code */ + u8 aBuffer[SNIPPET_BUFFER_SIZE];/* Circular snippet buffer */ + int *aiPrev; /* Used by fts3LoadSnippetBuffer() */ + int *anToken; /* Number of tokens in each phrase */ + char **apList; /* Array of position lists */ + int *anCnt; /* Running totals of phrase occurences */ + int nList; - rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); + int i; + + u64 hlmask = 0; /* Current mask of highlighted terms */ + u64 besthlmask = 0; /* Mask of highlighted terms for iBestPos */ + int iBestPos = 0; /* Starting position of 'best' snippet */ + int iBestScore = 0; /* Score of best snippet higher->better */ + SnippetCtx sCtx; + + /* Iterate through the phrases in the expression to count them. The same + ** callback makes sure the doclists are loaded for each phrase. + */ + rc = fts3ExprLoadDoclists(pCsr, &nList); if( rc!=SQLITE_OK ){ return rc; } - sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC); - sqlite3_bind_blob(pStmt, 2, &p, sizeof(p), SQLITE_STATIC); - sqlite3_step(pStmt); + /* Now that it is known how many phrases there are, allocate and zero + ** the required arrays using malloc(). + */ + apList = sqlite3_malloc( + sizeof(u8*)*nList + /* apList */ + sizeof(int)*(nList) + /* anToken */ + sizeof(int)*nList + /* aiPrev */ + sizeof(int)*(nList+1) /* anCnt */ + ); + if( !apList ){ + return SQLITE_NOMEM; + } + memset(apList, 0, sizeof(u8*)*nList+sizeof(int)*nList+sizeof(int)*nList); + anToken = (int *)&apList[nList]; + aiPrev = &anToken[nList]; + anCnt = &aiPrev[nList]; - return sqlite3_finalize(pStmt); -} + /* Initialize the contents of the aiPrev and aiList arrays. */ + sCtx.pCsr = pCsr; + sCtx.iCol = iCol; + sCtx.apList = apList; + sCtx.aiPrev = aiPrev; + sCtx.anToken = anToken; + sCtx.iPhrase = 0; + (void)fts3ExprIterate(pCsr->pExpr, fts3SnippetFindPositions, (void *)&sCtx); -static -int queryTokenizer( - sqlite3 *db, - char *zName, - const sqlite3_tokenizer_module **pp -){ - int rc; - sqlite3_stmt *pStmt; - const char zSql[] = "SELECT fts3_tokenizer(?)"; + /* Load the first two chunks of data into the buffer. */ + memset(aBuffer, 0, SNIPPET_BUFFER_SIZE); + fts3LoadSnippetBuffer(0, aBuffer, nList, apList, aiPrev); + fts3LoadSnippetBuffer(SNIPPET_BUFFER_CHUNK, aBuffer, nList, apList, aiPrev); - *pp = 0; - rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, 0); - if( rc!=SQLITE_OK ){ - return rc; + /* Set the initial contents of the highlight-mask and anCnt[] array. */ + for(i=1-nSnippet; i<=0; i++){ + fts3SnippetCnt(i, nSnippet, anCnt, aBuffer, anToken, &hlmask); } + iBestScore = fts3SnippetScore(nList, anCnt); + besthlmask = hlmask; + iBestPos = 0; - sqlite3_bind_text(pStmt, 1, zName, -1, SQLITE_STATIC); - if( SQLITE_ROW==sqlite3_step(pStmt) ){ - if( sqlite3_column_type(pStmt, 0)==SQLITE_BLOB ){ - memcpy(pp, sqlite3_column_blob(pStmt, 0), sizeof(*pp)); + for(i=1; 1; i++){ + int iScore; + + if( 0==(i&(SNIPPET_BUFFER_CHUNK-1)) ){ + int iLoad = i + SNIPPET_BUFFER_CHUNK; + if( fts3LoadSnippetBuffer(iLoad, aBuffer, nList, apList, aiPrev) ) break; + } + + /* Figure out how highly a snippet starting at token offset i scores + ** according to fts3SnippetScore(). If it is higher than any previously + ** considered position, save the current position, score and hlmask as + ** the best snippet candidate found so far. + */ + fts3SnippetCnt(i, nSnippet, anCnt, aBuffer, anToken, &hlmask); + iScore = fts3SnippetScore(nList, anCnt); + if( iScore>iBestScore ){ + iBestPos = i; + iBestScore = iScore; + besthlmask = hlmask; } } - return sqlite3_finalize(pStmt); + sqlite3_free(apList); + *piPos = iBestPos; + *pHlmask = besthlmask; + return SQLITE_OK; } -SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule(sqlite3_tokenizer_module const**ppModule); +typedef struct StrBuffer StrBuffer; +struct StrBuffer { + char *z; + int n; + int nAlloc; +}; -/* -** Implementation of the scalar function fts3_tokenizer_internal_test(). -** This function is used for testing only, it is not included in the -** build unless SQLITE_TEST is defined. -** -** The purpose of this is to test that the fts3_tokenizer() function -** can be used as designed by the C-code in the queryTokenizer and -** registerTokenizer() functions above. These two functions are repeated -** in the README.tokenizer file as an example, so it is important to -** test them. -** -** To run the tests, evaluate the fts3_tokenizer_internal_test() scalar -** function with no arguments. An assert() will fail if a problem is -** detected. i.e.: -** -** SELECT fts3_tokenizer_internal_test(); -** -*/ -static void intTestFunc( - sqlite3_context *context, - int argc, - sqlite3_value **argv -){ - int rc; - const sqlite3_tokenizer_module *p1; - const sqlite3_tokenizer_module *p2; - sqlite3 *db = (sqlite3 *)sqlite3_user_data(context); +static int fts3StringAppend( + StrBuffer *pStr, + const char *zAppend, + int nAppend +){ + if( nAppend<0 ){ + nAppend = (int)strlen(zAppend); + } - /* Test the query function */ - sqlite3Fts3SimpleTokenizerModule(&p1); - rc = queryTokenizer(db, "simple", &p2); - assert( rc==SQLITE_OK ); - assert( p1==p2 ); - rc = queryTokenizer(db, "nosuchtokenizer", &p2); - assert( rc==SQLITE_ERROR ); - assert( p2==0 ); - assert( 0==strcmp(sqlite3_errmsg(db), "unknown tokenizer: nosuchtokenizer") ); + if( pStr->n+nAppend+1>=pStr->nAlloc ){ + int nAlloc = pStr->nAlloc+nAppend+100; + char *zNew = sqlite3_realloc(pStr->z, nAlloc); + if( !zNew ){ + return SQLITE_NOMEM; + } + pStr->z = zNew; + pStr->nAlloc = nAlloc; + } - /* Test the storage function */ - rc = registerTokenizer(db, "nosuchtokenizer", p1); - assert( rc==SQLITE_OK ); - rc = queryTokenizer(db, "nosuchtokenizer", &p2); - assert( rc==SQLITE_OK ); - assert( p2==p1 ); + memcpy(&pStr->z[pStr->n], zAppend, nAppend); + pStr->n += nAppend; + pStr->z[pStr->n] = '\0'; - sqlite3_result_text(context, "ok", -1, SQLITE_STATIC); + return SQLITE_OK; } -#endif - -/* -** Set up SQL objects in database db used to access the contents of -** the hash table pointed to by argument pHash. The hash table must -** been initialised to use string keys, and to take a private copy -** of the key when a value is inserted. i.e. by a call similar to: -** -** sqlite3Fts3HashInit(pHash, FTS3_HASH_STRING, 1); -** -** This function adds a scalar function (see header comment above -** scalarFunc() in this file for details) and, if ENABLE_TABLE is -** defined at compilation time, a temporary virtual table (see header -** comment above struct HashTableVtab) to the database schema. Both -** provide read/write access to the contents of *pHash. -** -** The third argument to this function, zName, is used as the name -** of both the scalar and, if created, the virtual table. -*/ -SQLITE_PRIVATE int sqlite3Fts3InitHashTable( - sqlite3 *db, - fts3Hash *pHash, - const char *zName +static int fts3SnippetText( + Fts3Cursor *pCsr, /* FTS3 Cursor */ + const char *zDoc, /* Document to extract snippet from */ + int nDoc, /* Size of zDoc in bytes */ + int nSnippet, /* Number of tokens in extracted snippet */ + int iPos, /* Index of first document token in snippet */ + u64 hlmask, /* Bitmask of terms to highlight in snippet */ + const char *zOpen, /* String inserted before highlighted term */ + const char *zClose, /* String inserted after highlighted term */ + const char *zEllipsis, + char **pzSnippet /* OUT: Snippet text */ ){ - int rc = SQLITE_OK; - void *p = (void *)pHash; - const int any = SQLITE_ANY; - char *zTest = 0; - char *zTest2 = 0; + Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; + int rc; /* Return code */ + int iCurrent = 0; + int iStart = 0; + int iEnd; -#ifdef SQLITE_TEST - void *pdb = (void *)db; - zTest = sqlite3_mprintf("%s_test", zName); - zTest2 = sqlite3_mprintf("%s_internal_test", zName); - if( !zTest || !zTest2 ){ - rc = SQLITE_NOMEM; + sqlite3_tokenizer_module *pMod; /* Tokenizer module methods object */ + sqlite3_tokenizer_cursor *pC; /* Tokenizer cursor open on zDoc/nDoc */ + const char *ZDUMMY; /* Dummy arguments used with tokenizer */ + int DUMMY1, DUMMY2, DUMMY3; /* Dummy arguments used with tokenizer */ + + StrBuffer res = {0, 0, 0}; /* Result string */ + + /* Open a token cursor on the document. Read all tokens up to and + ** including token iPos (the first token of the snippet). Set variable + ** iStart to the byte offset in zDoc of the start of token iPos. + */ + pMod = (sqlite3_tokenizer_module *)pTab->pTokenizer->pModule; + rc = pMod->xOpen(pTab->pTokenizer, zDoc, nDoc, &pC); + while( rc==SQLITE_OK && iCurrentxNext(pC, &ZDUMMY, &DUMMY1, &iStart, &DUMMY2, &iCurrent); } -#endif + iEnd = iStart; - if( rc!=SQLITE_OK - || (rc = sqlite3_create_function(db, zName, 1, any, p, scalarFunc, 0, 0)) - || (rc = sqlite3_create_function(db, zName, 2, any, p, scalarFunc, 0, 0)) -#ifdef SQLITE_TEST - || (rc = sqlite3_create_function(db, zTest, 2, any, p, testFunc, 0, 0)) - || (rc = sqlite3_create_function(db, zTest, 3, any, p, testFunc, 0, 0)) - || (rc = sqlite3_create_function(db, zTest2, 0, any, pdb, intTestFunc, 0, 0)) -#endif - ); + if( rc==SQLITE_OK && iStart>0 ){ + rc = fts3StringAppend(&res, zEllipsis, -1); + } - sqlite3_free(zTest); - sqlite3_free(zTest2); + while( rc==SQLITE_OK ){ + int iBegin; + int iFin; + rc = pMod->xNext(pC, &ZDUMMY, &DUMMY1, &iBegin, &iFin, &iCurrent); + + if( rc==SQLITE_OK ){ + if( iCurrent>=(iPos+nSnippet) ){ + rc = SQLITE_DONE; + }else{ + iEnd = iFin; + if( hlmask & ((u64)1 << (iCurrent-iPos)) ){ + if( fts3StringAppend(&res, &zDoc[iStart], iBegin-iStart) + || fts3StringAppend(&res, zOpen, -1) + || fts3StringAppend(&res, &zDoc[iBegin], iEnd-iBegin) + || fts3StringAppend(&res, zClose, -1) + ){ + rc = SQLITE_NOMEM; + } + iStart = iEnd; + } + } + } + } + assert( rc!=SQLITE_OK ); + if( rc==SQLITE_DONE ){ + rc = fts3StringAppend(&res, &zDoc[iStart], iEnd-iStart); + if( rc==SQLITE_OK ){ + rc = pMod->xNext(pC, &ZDUMMY, &DUMMY1, &DUMMY2, &DUMMY3, &iCurrent); + if( rc==SQLITE_OK ){ + rc = fts3StringAppend(&res, zEllipsis, -1); + }else if( rc==SQLITE_DONE ){ + rc = fts3StringAppend(&res, &zDoc[iEnd], -1); + } + } + } + + pMod->xClose(pC); + if( rc!=SQLITE_OK ){ + sqlite3_free(res.z); + }else{ + *pzSnippet = res.z; + } return rc; } -#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ -/************** End of fts3_tokenizer.c **************************************/ -/************** Begin file fts3_tokenizer1.c *********************************/ /* -** 2006 Oct 10 +** An instance of this structure is used to collect the 'global' part of +** the matchinfo statistics. The 'global' part consists of the following: ** -** The author disclaims copyright to this source code. In place of -** a legal notice, here is a blessing: +** 1. The number of phrases in the query (nPhrase). ** -** May you do good and not evil. -** May you find forgiveness for yourself and forgive others. -** May you share freely, never taking more than you give. +** 2. The number of columns in the FTS3 table (nCol). ** -****************************************************************************** +** 3. A matrix of (nPhrase*nCol) integers containing the sum of the +** number of hits for each phrase in each column across all rows +** of the table. ** -** Implementation of the "simple" full-text-search tokenizer. -*/ - -/* -** The code in this file is only compiled if: +** The total size of the global matchinfo array, assuming the number of +** columns is N and the number of phrases is P is: ** -** * The FTS3 module is being built as an extension -** (in which case SQLITE_CORE is not defined), or +** 2 + P*(N+1) ** -** * The FTS3 module is being built into the core of -** SQLite (in which case SQLITE_ENABLE_FTS3 is defined). +** The number of hits for the 3rd phrase in the second column is found +** using the expression: +** +** aGlobal[2 + P*(1+2) + 1] */ -#if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) - - - - -typedef struct simple_tokenizer { - sqlite3_tokenizer base; - char delim[128]; /* flag ASCII delimiters */ -} simple_tokenizer; +typedef struct MatchInfo MatchInfo; +struct MatchInfo { + Fts3Table *pTab; /* FTS3 Table */ + Fts3Cursor *pCursor; /* FTS3 Cursor */ + int iPhrase; /* Number of phrases so far */ + int nCol; /* Number of columns in table */ + u32 *aGlobal; /* Pre-allocated buffer */ +}; -typedef struct simple_tokenizer_cursor { - sqlite3_tokenizer_cursor base; - const char *pInput; /* input we are tokenizing */ - int nBytes; /* size of the input */ - int iOffset; /* current position in pInput */ - int iToken; /* index of next token to be returned */ - char *pToken; /* storage for current token */ - int nTokenAllocated; /* space allocated to zToken buffer */ -} simple_tokenizer_cursor; +/* +** This function is used to count the entries in a column-list (delta-encoded +** list of term offsets within a single column of a single row). +*/ +static int fts3ColumnlistCount(char **ppCollist){ + char *pEnd = *ppCollist; + char c = 0; + int nEntry = 0; + /* A column-list is terminated by either a 0x01 or 0x00. */ + while( 0xFE & (*pEnd | c) ){ + c = *pEnd++ & 0x80; + if( !c ) nEntry++; + } -/* Forward declaration */ -static const sqlite3_tokenizer_module simpleTokenizerModule; + *ppCollist = pEnd; + return nEntry; +} -static int simpleDelim(simple_tokenizer *t, unsigned char c){ - return c<0x80 && t->delim[c]; +static void fts3LoadColumnlistCounts(char **pp, u32 *aOut){ + char *pCsr = *pp; + while( *pCsr ){ + sqlite3_int64 iCol = 0; + if( *pCsr==0x01 ){ + pCsr++; + pCsr += sqlite3Fts3GetVarint(pCsr, &iCol); + } + aOut[iCol] += fts3ColumnlistCount(&pCsr); + } + pCsr++; + *pp = pCsr; } /* -** Create a new tokenizer instance. +** fts3ExprIterate() callback used to collect the "global" matchinfo stats +** for a single query. */ -static int simpleCreate( - int argc, const char * const *argv, - sqlite3_tokenizer **ppTokenizer +static int fts3ExprGlobalMatchinfoCb( + Fts3Expr *pExpr, /* Phrase expression node */ + void *pCtx /* Pointer to MatchInfo structure */ ){ - simple_tokenizer *t; + MatchInfo *p = (MatchInfo *)pCtx; + char *pCsr; + char *pEnd; + const int iStart = 2 + p->nCol*p->iPhrase; - t = (simple_tokenizer *) sqlite3_malloc(sizeof(*t)); - if( t==NULL ) return SQLITE_NOMEM; - memset(t, 0, sizeof(*t)); + assert( pExpr->isLoaded ); - /* TODO(shess) Delimiters need to remain the same from run to run, - ** else we need to reindex. One solution would be a meta-table to - ** track such information in the database, then we'd only want this - ** information on the initial create. - */ - if( argc>1 ){ - int i, n = strlen(argv[1]); - for(i=0; i=0x80 ){ - sqlite3_free(t); - return SQLITE_ERROR; - } - t->delim[ch] = 1; - } - } else { - /* Mark non-alphanumeric ASCII characters as delimiters */ - int i; - for(i=1; i<0x80; i++){ - t->delim[i] = !isalnum(i); - } + /* Fill in the global hit count matrix row for this phrase. */ + pCsr = pExpr->aDoclist; + pEnd = &pExpr->aDoclist[pExpr->nDoclist]; + while( pCsraGlobal[iStart]); } - *ppTokenizer = &t->base; - return SQLITE_OK; -} - -/* -** Destroy a tokenizer -*/ -static int simpleDestroy(sqlite3_tokenizer *pTokenizer){ - sqlite3_free(pTokenizer); + p->iPhrase++; return SQLITE_OK; } -/* -** Prepare to begin tokenizing a particular string. The input -** string to be tokenized is pInput[0..nBytes-1]. A cursor -** used to incrementally tokenize this string is returned in -** *ppCursor. -*/ -static int simpleOpen( - sqlite3_tokenizer *pTokenizer, /* The tokenizer */ - const char *pInput, int nBytes, /* String to be tokenized */ - sqlite3_tokenizer_cursor **ppCursor /* OUT: Tokenization cursor */ +static int fts3ExprLocalMatchinfoCb( + Fts3Expr *pExpr, /* Phrase expression node */ + void *pCtx /* Pointer to MatchInfo structure */ ){ - simple_tokenizer_cursor *c; + MatchInfo *p = (MatchInfo *)pCtx; + int iPhrase = p->iPhrase++; - c = (simple_tokenizer_cursor *) sqlite3_malloc(sizeof(*c)); - if( c==NULL ) return SQLITE_NOMEM; + if( pExpr->aDoclist ){ + char *pCsr; + int iOffset = 2 + p->nCol*(p->aGlobal[0]+iPhrase); - c->pInput = pInput; - if( pInput==0 ){ - c->nBytes = 0; - }else if( nBytes<0 ){ - c->nBytes = (int)strlen(pInput); - }else{ - c->nBytes = nBytes; + memset(&p->aGlobal[iOffset], 0, p->nCol*sizeof(u32)); + pCsr = sqlite3Fts3FindPositions(pExpr, p->pCursor->iPrevId, -1); + if( pCsr ) fts3LoadColumnlistCounts(&pCsr, &p->aGlobal[iOffset]); } - c->iOffset = 0; /* start tokenizing at the beginning */ - c->iToken = 0; - c->pToken = NULL; /* no space allocated, yet. */ - c->nTokenAllocated = 0; - *ppCursor = &c->base; return SQLITE_OK; } /* -** Close a tokenization cursor previously opened by a call to -** simpleOpen() above. +** Populate pCsr->aMatchinfo[] with data for the current row. The 'matchinfo' +** data is an array of 32-bit unsigned integers (C type u32). */ -static int simpleClose(sqlite3_tokenizer_cursor *pCursor){ - simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor; - sqlite3_free(c->pToken); - sqlite3_free(c); - return SQLITE_OK; -} +static int fts3GetMatchinfo(Fts3Cursor *pCsr){ + MatchInfo g; + Fts3Table *pTab = (Fts3Table *)pCsr->base.pVtab; + if( pCsr->aMatchinfo==0 ){ + int rc; + int nPhrase; + int nMatchinfo; -/* -** Extract the next token from a tokenization cursor. The cursor must -** have been opened by a prior call to simpleOpen(). -*/ -static int simpleNext( - sqlite3_tokenizer_cursor *pCursor, /* Cursor returned by simpleOpen */ - const char **ppToken, /* OUT: *ppToken is the token text */ - int *pnBytes, /* OUT: Number of bytes in token */ - int *piStartOffset, /* OUT: Starting offset of token */ - int *piEndOffset, /* OUT: Ending offset of token */ - int *piPosition /* OUT: Position integer of token */ -){ - simple_tokenizer_cursor *c = (simple_tokenizer_cursor *) pCursor; - simple_tokenizer *t = (simple_tokenizer *) pCursor->pTokenizer; - unsigned char *p = (unsigned char *)c->pInput; + g.pTab = pTab; + g.nCol = pTab->nColumn; + g.iPhrase = 0; + rc = fts3ExprLoadDoclists(pCsr, &nPhrase); + if( rc!=SQLITE_OK ){ + return rc; + } - while( c->iOffsetnBytes ){ - int iStartOffset; + nMatchinfo = 2 + 2*g.nCol*nPhrase; - /* Scan past delimiter characters */ - while( c->iOffsetnBytes && simpleDelim(t, p[c->iOffset]) ){ - c->iOffset++; + g.iPhrase = 0; + g.aGlobal = (u32 *)sqlite3_malloc(sizeof(u32)*nMatchinfo); + if( !g.aGlobal ){ + return SQLITE_NOMEM; } + memset(g.aGlobal, 0, sizeof(u32)*nMatchinfo); - /* Count non-delimiter characters. */ - iStartOffset = c->iOffset; - while( c->iOffsetnBytes && !simpleDelim(t, p[c->iOffset]) ){ - c->iOffset++; - } + g.aGlobal[0] = nPhrase; + g.aGlobal[1] = g.nCol; + (void)fts3ExprIterate(pCsr->pExpr, fts3ExprGlobalMatchinfoCb, (void *)&g); - if( c->iOffset>iStartOffset ){ - int i, n = c->iOffset-iStartOffset; - if( n>c->nTokenAllocated ){ - c->nTokenAllocated = n+20; - c->pToken = sqlite3_realloc(c->pToken, c->nTokenAllocated); - if( c->pToken==NULL ) return SQLITE_NOMEM; - } - for(i=0; ipToken[i] = ch<0x80 ? tolower(ch) : ch; - } - *ppToken = c->pToken; - *pnBytes = n; - *piStartOffset = iStartOffset; - *piEndOffset = c->iOffset; - *piPosition = c->iToken++; + pCsr->aMatchinfo = g.aGlobal; + } - return SQLITE_OK; - } + g.pTab = pTab; + g.pCursor = pCsr; + g.nCol = pTab->nColumn; + g.iPhrase = 0; + g.aGlobal = pCsr->aMatchinfo; + + if( pCsr->isMatchinfoOk ){ + (void)fts3ExprIterate(pCsr->pExpr, fts3ExprLocalMatchinfoCb, (void *)&g); + pCsr->isMatchinfoOk = 0; } - return SQLITE_DONE; -} -/* -** The set of routines that implement the simple tokenizer -*/ -static const sqlite3_tokenizer_module simpleTokenizerModule = { - 0, - simpleCreate, - simpleDestroy, - simpleOpen, - simpleClose, - simpleNext, -}; + return SQLITE_OK; +} -/* -** Allocate a new simple tokenizer. Return a pointer to the new -** tokenizer in *ppModule -*/ -SQLITE_PRIVATE void sqlite3Fts3SimpleTokenizerModule( - sqlite3_tokenizer_module const**ppModule +SQLITE_PRIVATE void sqlite3Fts3Snippet2( + sqlite3_context *pCtx, /* SQLite function call context */ + Fts3Cursor *pCsr, /* Cursor object */ + const char *zStart, /* Snippet start text - "" */ + const char *zEnd, /* Snippet end text - "" */ + const char *zEllipsis, /* Snippet ellipsis text - "..." */ + int iCol, /* Extract snippet from this column */ + int nToken /* Approximate number of tokens in snippet */ ){ - *ppModule = &simpleTokenizerModule; + int rc; + int iPos = 0; + u64 hlmask = 0; + char *z = 0; + int nDoc; + const char *zDoc; + + rc = fts3BestSnippet(nToken, pCsr, iCol, &iPos, &hlmask); + + nDoc = sqlite3_column_bytes(pCsr->pStmt, iCol+1); + zDoc = (const char *)sqlite3_column_text(pCsr->pStmt, iCol+1); + + if( rc==SQLITE_OK ){ + rc = fts3SnippetText( + pCsr, zDoc, nDoc, nToken, iPos, hlmask, zStart, zEnd, zEllipsis, &z); + } + if( rc!=SQLITE_OK ){ + sqlite3_result_error_code(pCtx, rc); + }else{ + sqlite3_result_text(pCtx, z, -1, sqlite3_free); + } } -#endif /* !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_FTS3) */ +SQLITE_PRIVATE void sqlite3Fts3Matchinfo(sqlite3_context *pContext, Fts3Cursor *pCsr){ + int rc = fts3GetMatchinfo(pCsr); + if( rc!=SQLITE_OK ){ + sqlite3_result_error_code(pContext, rc); + }else{ + int n = sizeof(u32)*(2+pCsr->aMatchinfo[0]*pCsr->aMatchinfo[1]*2); + sqlite3_result_blob(pContext, pCsr->aMatchinfo, n, SQLITE_TRANSIENT); + } +} -/************** End of fts3_tokenizer1.c *************************************/ +#endif + +/************** End of fts3_snippet.c ****************************************/ /************** Begin file rtree.c *******************************************/ /* ** 2001 September 15 @@ -103126,8 +107254,6 @@ ************************************************************************* ** This file contains code for implementations of the r-tree and r*-tree ** algorithms packaged as an SQLite virtual table module. -** -** $Id: rtree.c,v 1.12 2008/12/22 15:04:32 danielk1977 Exp $ */ #if !defined(SQLITE_CORE) || defined(SQLITE_ENABLE_RTREE) @@ -104597,8 +108723,8 @@ ** variables iLeftSeek and iRightSeed. */ for(i=0; inDim; i++){ - float x1 = aCell[0].aCoord[i*2]; - float x2 = aCell[0].aCoord[i*2+1]; + float x1 = DCOORD(aCell[0].aCoord[i*2]); + float x2 = DCOORD(aCell[0].aCoord[i*2+1]); float x3 = x1; float x4 = x2; int jj; @@ -104607,8 +108733,8 @@ int iCellRight = 0; for(jj=1; jjx4 ) x4 = right; @@ -104966,6 +109092,9 @@ int i; aiUsed = sqlite3_malloc(sizeof(int)*nCell); + if( !aiUsed ){ + return SQLITE_NOMEM; + } memset(aiUsed, 0, sizeof(int)*nCell); PickSeeds(pRtree, aCell, nCell, &iLeftSeed, &iRightSeed); @@ -105453,7 +109582,7 @@ /* ** The xUpdate method for rtree module virtual tables. */ -int rtreeUpdate( +static int rtreeUpdate( sqlite3_vtab *pVtab, int nData, sqlite3_value **azData, @@ -105848,8 +109977,10 @@ zSql = sqlite3_mprintf("%s);", zTmp); sqlite3_free(zTmp); } - if( !zSql || sqlite3_declare_vtab(db, zSql) ){ + if( !zSql ){ rc = SQLITE_NOMEM; + }else if( SQLITE_OK!=(rc = sqlite3_declare_vtab(db, zSql)) ){ + *pzErr = sqlite3_mprintf("%s", sqlite3_errmsg(db)); } sqlite3_free(zSql); } @@ -106424,7 +110555,7 @@ void *pContext; /* sqlite3_user_data() context */ void (*xFunc)(sqlite3_context*,int,sqlite3_value**); } scalars[] = { - {"regexp",-1, SQLITE_ANY, 0, icuRegexpFunc}, + {"regexp", 2, SQLITE_ANY, 0, icuRegexpFunc}, {"lower", 1, SQLITE_UTF16, 0, icuCaseFunc16}, {"lower", 2, SQLITE_UTF16, 0, icuCaseFunc16}, diff -Nru firefox-3.6.3+nobinonly/mozilla/db/sqlite3/src/sqlite3.h firefox-3.6.4+build1+nobinonly/mozilla/db/sqlite3/src/sqlite3.h --- firefox-3.6.3+nobinonly/mozilla/db/sqlite3/src/sqlite3.h 2010-04-02 16:57:53.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/db/sqlite3/src/sqlite3.h 2010-04-16 17:31:41.000000000 +0100 @@ -18,8 +18,8 @@ ** Some of the definitions that are in this file are marked as ** "experimental". Experimental interfaces are normally new ** features recently added to SQLite. We do not anticipate changes -** to experimental interfaces but reserve to make minor changes if -** experience from use "in the wild" suggest such changes are prudent. +** to experimental interfaces but reserve the right to make minor changes +** if experience from use "in the wild" suggest such changes are prudent. ** ** The official C-language API documentation for SQLite is derived ** from comments in this file. This file is the authoritative source @@ -29,8 +29,6 @@ ** The makefile makes some minor changes to this file (such as inserting ** the version number) and changes its name to "sqlite3.h" as ** part of the build process. -** -** @(#) $Id: sqlite.h.in,v 1.458 2009/06/19 22:50:31 drh Exp $ */ #ifndef _SQLITE3_H_ #define _SQLITE3_H_ @@ -51,10 +49,15 @@ # define SQLITE_EXTERN extern #endif +#ifndef SQLITE_API +# define SQLITE_API +#endif + + /* ** These no-op macros are used in front of interfaces to mark those ** interfaces as either deprecated or experimental. New applications -** should not use deprecated intrfaces - they are support for backwards +** should not use deprecated interfaces - they are support for backwards ** compatibility only. Application writers should be aware that ** experimental interfaces are subject to change in point releases. ** @@ -78,57 +81,80 @@ #endif /* -** CAPI3REF: Compile-Time Library Version Numbers {H10010} -** -** The SQLITE_VERSION and SQLITE_VERSION_NUMBER #defines in -** the sqlite3.h file specify the version of SQLite with which -** that header file is associated. +** CAPI3REF: Compile-Time Library Version Numbers ** -** The "version" of SQLite is a string of the form "X.Y.Z". -** The phrase "alpha" or "beta" might be appended after the Z. -** The X value is major version number always 3 in SQLite3. -** The X value only changes when backwards compatibility is -** broken and we intend to never break backwards compatibility. -** The Y value is the minor version number and only changes when -** there are major feature enhancements that are forwards compatible -** but not backwards compatible. -** The Z value is the release number and is incremented with -** each release but resets back to 0 whenever Y is incremented. -** -** See also: [sqlite3_libversion()] and [sqlite3_libversion_number()]. -** -** Requirements: [H10011] [H10014] -*/ -#define SQLITE_VERSION "3.6.16.1" -#define SQLITE_VERSION_NUMBER 3006016 +** ^(The [SQLITE_VERSION] C preprocessor macro in the sqlite3.h header +** evaluates to a string literal that is the SQLite version in the +** format "X.Y.Z" where X is the major version number (always 3 for +** SQLite3) and Y is the minor version number and Z is the release number.)^ +** ^(The [SQLITE_VERSION_NUMBER] C preprocessor macro resolves to an integer +** with the value (X*1000000 + Y*1000 + Z) where X, Y, and Z are the same +** numbers used in [SQLITE_VERSION].)^ +** The SQLITE_VERSION_NUMBER for any given release of SQLite will also +** be larger than the release from which it is derived. Either Y will +** be held constant and Z will be incremented or else Y will be incremented +** and Z will be reset to zero. +** +** Since version 3.6.18, SQLite source code has been stored in the +** Fossil configuration management +** system. ^The SQLITE_SOURCE_ID macro evalutes to +** a string which identifies a particular check-in of SQLite +** within its configuration management system. ^The SQLITE_SOURCE_ID +** string contains the date and time of the check-in (UTC) and an SHA1 +** hash of the entire source tree. +** +** See also: [sqlite3_libversion()], +** [sqlite3_libversion_number()], [sqlite3_sourceid()], +** [sqlite_version()] and [sqlite_source_id()]. +*/ +#define SQLITE_VERSION "3.6.22" +#define SQLITE_VERSION_NUMBER 3006022 +#define SQLITE_SOURCE_ID "2010-01-05 15:30:36 28d0d7710761114a44a1a3a425a6883c661f06e7" /* -** CAPI3REF: Run-Time Library Version Numbers {H10020} +** CAPI3REF: Run-Time Library Version Numbers ** KEYWORDS: sqlite3_version ** -** These features provide the same information as the [SQLITE_VERSION] -** and [SQLITE_VERSION_NUMBER] #defines in the header, but are associated -** with the library instead of the header file. Cautious programmers might -** include a check in their application to verify that -** sqlite3_libversion_number() always returns the value -** [SQLITE_VERSION_NUMBER]. -** -** The sqlite3_libversion() function returns the same information as is -** in the sqlite3_version[] string constant. The function is provided -** for use in DLLs since DLL users usually do not have direct access to string -** constants within the DLL. -** -** Requirements: [H10021] [H10022] [H10023] -*/ -SQLITE_EXTERN const char sqlite3_version[]; -const char *sqlite3_libversion(void); -int sqlite3_libversion_number(void); - -/* -** CAPI3REF: Test To See If The Library Is Threadsafe {H10100} +** These interfaces provide the same information as the [SQLITE_VERSION], +** [SQLITE_VERSION_NUMBER], and [SQLITE_SOURCE_ID] C preprocessor macros +** but are associated with the library instead of the header file. ^(Cautious +** programmers might include assert() statements in their application to +** verify that values returned by these interfaces match the macros in +** the header, and thus insure that the application is +** compiled with matching library and header files. +** +**
    +** assert( sqlite3_libversion_number()==SQLITE_VERSION_NUMBER );
    +** assert( strcmp(sqlite3_sourceid(),SQLITE_SOURCE_ID)==0 );
    +** assert( strcmp(sqlite3_libversion(),SQLITE_VERSION)==0 );
    +** 
    )^ +** +** ^The sqlite3_version[] string constant contains the text of [SQLITE_VERSION] +** macro. ^The sqlite3_libversion() function returns a pointer to the +** to the sqlite3_version[] string constant. The sqlite3_libversion() +** function is provided for use in DLLs since DLL users usually do not have +** direct access to string constants within the DLL. ^The +** sqlite3_libversion_number() function returns an integer equal to +** [SQLITE_VERSION_NUMBER]. ^The sqlite3_sourceid() function a pointer +** to a string constant whose value is the same as the [SQLITE_SOURCE_ID] +** C preprocessor macro. +** +** See also: [sqlite_version()] and [sqlite_source_id()]. +*/ +SQLITE_API SQLITE_EXTERN const char sqlite3_version[]; +SQLITE_API const char *sqlite3_libversion(void); +SQLITE_API const char *sqlite3_sourceid(void); +SQLITE_API int sqlite3_libversion_number(void); + +/* +** CAPI3REF: Test To See If The Library Is Threadsafe +** +** ^The sqlite3_threadsafe() function returns zero if and only if +** SQLite was compiled mutexing code omitted due to the +** [SQLITE_THREADSAFE] compile-time option being set to 0. ** ** SQLite can be compiled with or without mutexes. When -** the [SQLITE_THREADSAFE] C preprocessor macro 1 or 2, mutexes +** the [SQLITE_THREADSAFE] C preprocessor macro is 1 or 2, mutexes ** are enabled and SQLite is threadsafe. When the ** [SQLITE_THREADSAFE] macro is 0, ** the mutexes are omitted. Without the mutexes, it is not safe @@ -137,29 +163,29 @@ ** Enabling mutexes incurs a measurable performance penalty. ** So if speed is of utmost importance, it makes sense to disable ** the mutexes. But for maximum safety, mutexes should be enabled. -** The default behavior is for mutexes to be enabled. +** ^The default behavior is for mutexes to be enabled. ** -** This interface can be used by a program to make sure that the +** This interface can be used by an application to make sure that the ** version of SQLite that it is linking against was compiled with ** the desired setting of the [SQLITE_THREADSAFE] macro. ** ** This interface only reports on the compile-time mutex setting ** of the [SQLITE_THREADSAFE] flag. If SQLite is compiled with -** SQLITE_THREADSAFE=1 then mutexes are enabled by default but +** SQLITE_THREADSAFE=1 or =2 then mutexes are enabled by default but ** can be fully or partially disabled using a call to [sqlite3_config()] ** with the verbs [SQLITE_CONFIG_SINGLETHREAD], [SQLITE_CONFIG_MULTITHREAD], -** or [SQLITE_CONFIG_MUTEX]. The return value of this function shows -** only the default compile-time setting, not any run-time changes -** to that setting. +** or [SQLITE_CONFIG_MUTEX]. ^(The return value of the +** sqlite3_threadsafe() function shows only the compile-time setting of +** thread safety, not any run-time changes to that setting made by +** sqlite3_config(). In other words, the return value from sqlite3_threadsafe() +** is unchanged by calls to sqlite3_config().)^ ** ** See the [threading mode] documentation for additional information. -** -** Requirements: [H10101] [H10102] */ -int sqlite3_threadsafe(void); +SQLITE_API int sqlite3_threadsafe(void); /* -** CAPI3REF: Database Connection Handle {H12000} +** CAPI3REF: Database Connection Handle ** KEYWORDS: {database connection} {database connections} ** ** Each open SQLite database is represented by a pointer to an instance of @@ -174,7 +200,7 @@ typedef struct sqlite3 sqlite3; /* -** CAPI3REF: 64-Bit Integer Types {H10200} +** CAPI3REF: 64-Bit Integer Types ** KEYWORDS: sqlite_int64 sqlite_uint64 ** ** Because there is no cross-platform way to specify 64-bit integer types @@ -184,7 +210,10 @@ ** The sqlite_int64 and sqlite_uint64 types are supported for backwards ** compatibility only. ** -** Requirements: [H10201] [H10202] +** ^The sqlite3_int64 and sqlite_int64 types can store integer values +** between -9223372036854775808 and +9223372036854775807 inclusive. ^The +** sqlite3_uint64 and sqlite_uint64 types can store integer values +** between 0 and +18446744073709551615 inclusive. */ #ifdef SQLITE_INT64_TYPE typedef SQLITE_INT64_TYPE sqlite_int64; @@ -208,36 +237,30 @@ #endif /* -** CAPI3REF: Closing A Database Connection {H12010} +** CAPI3REF: Closing A Database Connection ** -** This routine is the destructor for the [sqlite3] object. +** ^The sqlite3_close() routine is the destructor for the [sqlite3] object. +** ^Calls to sqlite3_close() return SQLITE_OK if the [sqlite3] object is +** successfullly destroyed and all associated resources are deallocated. ** -** Applications should [sqlite3_finalize | finalize] all [prepared statements] +** Applications must [sqlite3_finalize | finalize] all [prepared statements] ** and [sqlite3_blob_close | close] all [BLOB handles] associated with -** the [sqlite3] object prior to attempting to close the object. -** The [sqlite3_next_stmt()] interface can be used to locate all -** [prepared statements] associated with a [database connection] if desired. -** Typical code might look like this: +** the [sqlite3] object prior to attempting to close the object. ^If +** sqlite3_close() is called on a [database connection] that still has +** outstanding [prepared statements] or [BLOB handles], then it returns +** SQLITE_BUSY. ** -**
    -** sqlite3_stmt *pStmt;
    -** while( (pStmt = sqlite3_next_stmt(db, 0))!=0 ){
    -**     sqlite3_finalize(pStmt);
    -** }
    -** 
    -** -** If [sqlite3_close()] is invoked while a transaction is open, +** ^If [sqlite3_close()] is invoked while a transaction is open, ** the transaction is automatically rolled back. ** ** The C parameter to [sqlite3_close(C)] must be either a NULL ** pointer or an [sqlite3] object pointer obtained ** from [sqlite3_open()], [sqlite3_open16()], or ** [sqlite3_open_v2()], and not previously closed. -** -** Requirements: -** [H12011] [H12012] [H12013] [H12014] [H12015] [H12019] +** ^Calling sqlite3_close() with a NULL pointer argument is a +** harmless no-op. */ -int sqlite3_close(sqlite3 *); +SQLITE_API int sqlite3_close(sqlite3 *); /* ** The type for a callback function. @@ -247,50 +270,67 @@ typedef int (*sqlite3_callback)(void*,int,char**, char**); /* -** CAPI3REF: One-Step Query Execution Interface {H12100} +** CAPI3REF: One-Step Query Execution Interface ** -** The sqlite3_exec() interface is a convenient way of running one or more -** SQL statements without having to write a lot of C code. The UTF-8 encoded -** SQL statements are passed in as the second parameter to sqlite3_exec(). -** The statements are evaluated one by one until either an error or -** an interrupt is encountered, or until they are all done. The 3rd parameter -** is an optional callback that is invoked once for each row of any query -** results produced by the SQL statements. The 5th parameter tells where -** to write any error messages. -** -** The error message passed back through the 5th parameter is held -** in memory obtained from [sqlite3_malloc()]. To avoid a memory leak, -** the calling application should call [sqlite3_free()] on any error -** message returned through the 5th parameter when it has finished using -** the error message. -** -** If the SQL statement in the 2nd parameter is NULL or an empty string -** or a string containing only whitespace and comments, then no SQL -** statements are evaluated and the database is not changed. -** -** The sqlite3_exec() interface is implemented in terms of -** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()]. -** The sqlite3_exec() routine does nothing to the database that cannot be done -** by [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()]. -** -** The first parameter to [sqlite3_exec()] must be an valid and open -** [database connection]. -** -** The database connection must not be closed while -** [sqlite3_exec()] is running. -** -** The calling function should use [sqlite3_free()] to free -** the memory that *errmsg is left pointing at once the error -** message is no longer needed. -** -** The SQL statement text in the 2nd parameter to [sqlite3_exec()] -** must remain unchanged while [sqlite3_exec()] is running. -** -** Requirements: -** [H12101] [H12102] [H12104] [H12105] [H12107] [H12110] [H12113] [H12116] -** [H12119] [H12122] [H12125] [H12131] [H12134] [H12137] [H12138] +** The sqlite3_exec() interface is a convenience wrapper around +** [sqlite3_prepare_v2()], [sqlite3_step()], and [sqlite3_finalize()], +** that allows an application to run multiple statements of SQL +** without having to use a lot of C code. +** +** ^The sqlite3_exec() interface runs zero or more UTF-8 encoded, +** semicolon-separate SQL statements passed into its 2nd argument, +** in the context of the [database connection] passed in as its 1st +** argument. ^If the callback function of the 3rd argument to +** sqlite3_exec() is not NULL, then it is invoked for each result row +** coming out of the evaluated SQL statements. ^The 4th argument to +** to sqlite3_exec() is relayed through to the 1st argument of each +** callback invocation. ^If the callback pointer to sqlite3_exec() +** is NULL, then no callback is ever invoked and result rows are +** ignored. +** +** ^If an error occurs while evaluating the SQL statements passed into +** sqlite3_exec(), then execution of the current statement stops and +** subsequent statements are skipped. ^If the 5th parameter to sqlite3_exec() +** is not NULL then any error message is written into memory obtained +** from [sqlite3_malloc()] and passed back through the 5th parameter. +** To avoid memory leaks, the application should invoke [sqlite3_free()] +** on error message strings returned through the 5th parameter of +** of sqlite3_exec() after the error message string is no longer needed. +** ^If the 5th parameter to sqlite3_exec() is not NULL and no errors +** occur, then sqlite3_exec() sets the pointer in its 5th parameter to +** NULL before returning. +** +** ^If an sqlite3_exec() callback returns non-zero, the sqlite3_exec() +** routine returns SQLITE_ABORT without invoking the callback again and +** without running any subsequent SQL statements. +** +** ^The 2nd argument to the sqlite3_exec() callback function is the +** number of columns in the result. ^The 3rd argument to the sqlite3_exec() +** callback is an array of pointers to strings obtained as if from +** [sqlite3_column_text()], one for each column. ^If an element of a +** result row is NULL then the corresponding string pointer for the +** sqlite3_exec() callback is a NULL pointer. ^The 4th argument to the +** sqlite3_exec() callback is an array of pointers to strings where each +** entry represents the name of corresponding result column as obtained +** from [sqlite3_column_name()]. +** +** ^If the 2nd parameter to sqlite3_exec() is a NULL pointer, a pointer +** to an empty string, or a pointer that contains only whitespace and/or +** SQL comments, then no SQL statements are evaluated and the database +** is not changed. +** +** Restrictions: +** +**
      +**
    • The application must insure that the 1st parameter to sqlite3_exec() +** is a valid and open [database connection]. +**
    • The application must not close [database connection] specified by +** the 1st parameter to sqlite3_exec() while sqlite3_exec() is running. +**
    • The application must not modify the SQL statement text passed into +** the 2nd parameter of sqlite3_exec() while sqlite3_exec() is running. +**
    */ -int sqlite3_exec( +SQLITE_API int sqlite3_exec( sqlite3*, /* An open database */ const char *sql, /* SQL to be evaluated */ int (*callback)(void*,int,char**,char**), /* Callback function */ @@ -299,7 +339,7 @@ ); /* -** CAPI3REF: Result Codes {H10210} +** CAPI3REF: Result Codes ** KEYWORDS: SQLITE_OK {error code} {error codes} ** KEYWORDS: {result code} {result codes} ** @@ -343,7 +383,7 @@ /* end-of-error-codes */ /* -** CAPI3REF: Extended Result Codes {H10220} +** CAPI3REF: Extended Result Codes ** KEYWORDS: {extended error code} {extended error codes} ** KEYWORDS: {extended result code} {extended result codes} ** @@ -385,7 +425,7 @@ #define SQLITE_LOCKED_SHAREDCACHE (SQLITE_LOCKED | (1<<8) ) /* -** CAPI3REF: Flags For File Open Operations {H10230} +** CAPI3REF: Flags For File Open Operations ** ** These bit values are intended for use in the ** 3rd parameter to the [sqlite3_open_v2()] interface and @@ -406,9 +446,11 @@ #define SQLITE_OPEN_MASTER_JOURNAL 0x00004000 /* VFS only */ #define SQLITE_OPEN_NOMUTEX 0x00008000 /* Ok for sqlite3_open_v2() */ #define SQLITE_OPEN_FULLMUTEX 0x00010000 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_SHAREDCACHE 0x00020000 /* Ok for sqlite3_open_v2() */ +#define SQLITE_OPEN_PRIVATECACHE 0x00040000 /* Ok for sqlite3_open_v2() */ /* -** CAPI3REF: Device Characteristics {H10240} +** CAPI3REF: Device Characteristics ** ** The xDeviceCapabilities method of the [sqlite3_io_methods] ** object returns an integer which is a vector of the these @@ -440,7 +482,7 @@ #define SQLITE_IOCAP_SEQUENTIAL 0x00000400 /* -** CAPI3REF: File Locking Levels {H10250} +** CAPI3REF: File Locking Levels ** ** SQLite uses one of these integer values as the second ** argument to calls it makes to the xLock() and xUnlock() methods @@ -453,7 +495,7 @@ #define SQLITE_LOCK_EXCLUSIVE 4 /* -** CAPI3REF: Synchronization Type Flags {H10260} +** CAPI3REF: Synchronization Type Flags ** ** When SQLite invokes the xSync() method of an ** [sqlite3_io_methods] object it uses a combination of @@ -471,10 +513,11 @@ #define SQLITE_SYNC_DATAONLY 0x00010 /* -** CAPI3REF: OS Interface Open File Handle {H11110} +** CAPI3REF: OS Interface Open File Handle ** -** An [sqlite3_file] object represents an open file in the OS -** interface layer. Individual OS interface implementations will +** An [sqlite3_file] object represents an open file in the +** [sqlite3_vfs | OS interface layer]. Individual OS interface +** implementations will ** want to subclass this object by appending additional fields ** for their own use. The pMethods entry is a pointer to an ** [sqlite3_io_methods] object that defines methods for performing @@ -486,7 +529,7 @@ }; /* -** CAPI3REF: OS Interface File Virtual Methods Object {H11120} +** CAPI3REF: OS Interface File Virtual Methods Object ** ** Every file opened by the [sqlite3_vfs] xOpen method populates an ** [sqlite3_file] object (or, more commonly, a subclass of the @@ -591,7 +634,7 @@ }; /* -** CAPI3REF: Standard File Control Opcodes {H11310} +** CAPI3REF: Standard File Control Opcodes ** ** These integer constants are opcodes for the xFileControl method ** of the [sqlite3_io_methods] object and for the [sqlite3_file_control()] @@ -611,7 +654,7 @@ #define SQLITE_LAST_ERRNO 4 /* -** CAPI3REF: Mutex Handle {H17110} +** CAPI3REF: Mutex Handle ** ** The mutex module within SQLite defines [sqlite3_mutex] to be an ** abstract type for a mutex object. The SQLite core never looks @@ -623,7 +666,7 @@ typedef struct sqlite3_mutex sqlite3_mutex; /* -** CAPI3REF: OS Interface Object {H11140} +** CAPI3REF: OS Interface Object ** ** An instance of the sqlite3_vfs object defines the interface between ** the SQLite core and the underlying operating system. The "vfs" @@ -777,10 +820,10 @@ }; /* -** CAPI3REF: Flags for the xAccess VFS method {H11190} +** CAPI3REF: Flags for the xAccess VFS method ** ** These integer constants can be used as the third parameter to -** the xAccess method of an [sqlite3_vfs] object. {END} They determine +** the xAccess method of an [sqlite3_vfs] object. They determine ** what kind of permissions the xAccess method is looking for. ** With SQLITE_ACCESS_EXISTS, the xAccess method ** simply checks whether the file exists. @@ -794,39 +837,48 @@ #define SQLITE_ACCESS_READ 2 /* -** CAPI3REF: Initialize The SQLite Library {H10130} +** CAPI3REF: Initialize The SQLite Library ** -** The sqlite3_initialize() routine initializes the -** SQLite library. The sqlite3_shutdown() routine +** ^The sqlite3_initialize() routine initializes the +** SQLite library. ^The sqlite3_shutdown() routine ** deallocates any resources that were allocated by sqlite3_initialize(). +** These routines are designed to aid in process initialization and +** shutdown on embedded systems. Workstation applications using +** SQLite normally do not need to invoke either of these routines. ** ** A call to sqlite3_initialize() is an "effective" call if it is ** the first time sqlite3_initialize() is invoked during the lifetime of ** the process, or if it is the first time sqlite3_initialize() is invoked -** following a call to sqlite3_shutdown(). Only an effective call +** following a call to sqlite3_shutdown(). ^(Only an effective call ** of sqlite3_initialize() does any initialization. All other calls -** are harmless no-ops. +** are harmless no-ops.)^ ** ** A call to sqlite3_shutdown() is an "effective" call if it is the first -** call to sqlite3_shutdown() since the last sqlite3_initialize(). Only +** call to sqlite3_shutdown() since the last sqlite3_initialize(). ^(Only ** an effective call to sqlite3_shutdown() does any deinitialization. -** All other calls to sqlite3_shutdown() are harmless no-ops. +** All other valid calls to sqlite3_shutdown() are harmless no-ops.)^ ** -** Among other things, sqlite3_initialize() shall invoke -** sqlite3_os_init(). Similarly, sqlite3_shutdown() -** shall invoke sqlite3_os_end(). +** The sqlite3_initialize() interface is threadsafe, but sqlite3_shutdown() +** is not. The sqlite3_shutdown() interface must only be called from a +** single thread. All open [database connections] must be closed and all +** other SQLite resources must be deallocated prior to invoking +** sqlite3_shutdown(). +** +** Among other things, ^sqlite3_initialize() will invoke +** sqlite3_os_init(). Similarly, ^sqlite3_shutdown() +** will invoke sqlite3_os_end(). ** -** The sqlite3_initialize() routine returns [SQLITE_OK] on success. -** If for some reason, sqlite3_initialize() is unable to initialize +** ^The sqlite3_initialize() routine returns [SQLITE_OK] on success. +** ^If for some reason, sqlite3_initialize() is unable to initialize ** the library (perhaps it is unable to allocate a needed resource such ** as a mutex) it returns an [error code] other than [SQLITE_OK]. ** -** The sqlite3_initialize() routine is called internally by many other +** ^The sqlite3_initialize() routine is called internally by many other ** SQLite interfaces so that an application usually does not need to ** invoke sqlite3_initialize() directly. For example, [sqlite3_open()] ** calls sqlite3_initialize() so the SQLite library will be automatically ** initialized when [sqlite3_open()] is called if it has not be initialized -** already. However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT] +** already. ^However, if SQLite is compiled with the [SQLITE_OMIT_AUTOINIT] ** compile-time option, then the automatic calls to sqlite3_initialize() ** are omitted and the application must call sqlite3_initialize() directly ** prior to using any other SQLite interface. For maximum portability, @@ -850,21 +902,22 @@ ** interface is called automatically by sqlite3_initialize() and ** sqlite3_os_end() is called by sqlite3_shutdown(). Appropriate ** implementations for sqlite3_os_init() and sqlite3_os_end() -** are built into SQLite when it is compiled for unix, windows, or os/2. -** When built for other platforms (using the [SQLITE_OS_OTHER=1] compile-time +** are built into SQLite when it is compiled for Unix, Windows, or OS/2. +** When [custom builds | built for other platforms] +** (using the [SQLITE_OS_OTHER=1] compile-time ** option) the application must supply a suitable implementation for ** sqlite3_os_init() and sqlite3_os_end(). An application-supplied ** implementation of sqlite3_os_init() or sqlite3_os_end() ** must return [SQLITE_OK] on success and some other [error code] upon ** failure. */ -int sqlite3_initialize(void); -int sqlite3_shutdown(void); -int sqlite3_os_init(void); -int sqlite3_os_end(void); +SQLITE_API int sqlite3_initialize(void); +SQLITE_API int sqlite3_shutdown(void); +SQLITE_API int sqlite3_os_init(void); +SQLITE_API int sqlite3_os_end(void); /* -** CAPI3REF: Configuring The SQLite Library {H14100} +** CAPI3REF: Configuring The SQLite Library ** EXPERIMENTAL ** ** The sqlite3_config() interface is used to make global configuration @@ -878,7 +931,9 @@ ** threads while sqlite3_config() is running. Furthermore, sqlite3_config() ** may only be invoked prior to library initialization using ** [sqlite3_initialize()] or after shutdown by [sqlite3_shutdown()]. -** Note, however, that sqlite3_config() can be called as part of the +** ^If sqlite3_config() is called after [sqlite3_initialize()] and before +** [sqlite3_shutdown()] then it will return SQLITE_MISUSE. +** Note, however, that ^sqlite3_config() can be called as part of the ** implementation of an application-defined [sqlite3_os_init()]. ** ** The first argument to sqlite3_config() is an integer @@ -887,26 +942,21 @@ ** vary depending on the [SQLITE_CONFIG_SINGLETHREAD | configuration option] ** in the first argument. ** -** When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. -** If the option is unknown or SQLite is unable to set the option +** ^When a configuration option is set, sqlite3_config() returns [SQLITE_OK]. +** ^If the option is unknown or SQLite is unable to set the option ** then this routine returns a non-zero [error code]. -** -** Requirements: -** [H14103] [H14106] [H14120] [H14123] [H14126] [H14129] [H14132] [H14135] -** [H14138] [H14141] [H14144] [H14147] [H14150] [H14153] [H14156] [H14159] -** [H14162] [H14165] [H14168] */ -SQLITE_EXPERIMENTAL int sqlite3_config(int, ...); +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_config(int, ...); /* -** CAPI3REF: Configure database connections {H14200} +** CAPI3REF: Configure database connections ** EXPERIMENTAL ** ** The sqlite3_db_config() interface is used to make configuration ** changes to a [database connection]. The interface is similar to ** [sqlite3_config()] except that the changes apply to a single ** [database connection] (specified in the first argument). The -** sqlite3_db_config() interface can only be used immediately after +** sqlite3_db_config() interface should only be used immediately after ** the database connection is created using [sqlite3_open()], ** [sqlite3_open16()], or [sqlite3_open_v2()]. ** @@ -917,13 +967,13 @@ ** New verbs are likely to be added in future releases of SQLite. ** Additional arguments depend on the verb. ** -** Requirements: -** [H14203] [H14206] [H14209] [H14212] [H14215] +** ^Calls to sqlite3_db_config() return SQLITE_OK if and only if +** the call is considered successful. */ -SQLITE_EXPERIMENTAL int sqlite3_db_config(sqlite3*, int op, ...); +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_db_config(sqlite3*, int op, ...); /* -** CAPI3REF: Memory Allocation Routines {H10155} +** CAPI3REF: Memory Allocation Routines ** EXPERIMENTAL ** ** An instance of this object defines the interface between SQLite @@ -932,13 +982,15 @@ ** This object is used in only one place in the SQLite interface. ** A pointer to an instance of this object is the argument to ** [sqlite3_config()] when the configuration option is -** [SQLITE_CONFIG_MALLOC]. By creating an instance of this object -** and passing it to [sqlite3_config()] during configuration, an -** application can specify an alternative memory allocation subsystem -** for SQLite to use for all of its dynamic memory needs. +** [SQLITE_CONFIG_MALLOC] or [SQLITE_CONFIG_GETMALLOC]. +** By creating an instance of this object +** and passing it to [sqlite3_config]([SQLITE_CONFIG_MALLOC]) +** during configuration, an application can specify an alternative +** memory allocation subsystem for SQLite to use for all of its +** dynamic memory needs. ** -** Note that SQLite comes with a built-in memory allocator that is -** perfectly adequate for the overwhelming majority of applications +** Note that SQLite comes with several [built-in memory allocators] +** that are perfectly adequate for the overwhelming majority of applications ** and that this object is only useful to a tiny minority of applications ** with specialized memory allocation requirements. This object is ** also used during testing of SQLite in order to specify an alternative @@ -946,8 +998,16 @@ ** order to verify that SQLite recovers gracefully from such ** conditions. ** -** The xMalloc, xFree, and xRealloc methods must work like the -** malloc(), free(), and realloc() functions from the standard library. +** The xMalloc and xFree methods must work like the +** malloc() and free() functions from the standard C library. +** The xRealloc method must work like realloc() from the standard C library +** with the exception that if the second argument to xRealloc is zero, +** xRealloc must be a no-op - it must not perform any allocation or +** deallocation. ^SQLite guarantees that the second argument to +** xRealloc is always a value returned by a prior call to xRoundup. +** And so in cases where xRoundup always returns a positive number, +** xRealloc can perform exactly as the standard library realloc() and +** still be in compliance with this specification. ** ** xSize should return the allocated size of a memory allocation ** previously obtained from xMalloc or xRealloc. The allocated size @@ -957,6 +1017,9 @@ ** a memory allocation given a particular requested size. Most memory ** allocators round up memory allocations at least to the next multiple ** of 8. Some allocators round up to a larger multiple or to a power of 2. +** Every memory allocation request coming in through [sqlite3_malloc()] +** or [sqlite3_realloc()] first calls xRoundup. If xRoundup returns 0, +** that causes the corresponding memory allocation to fail. ** ** The xInit method initializes the memory allocator. (For example, ** it might allocate any require mutexes or initialize internal data @@ -964,6 +1027,20 @@ ** [sqlite3_shutdown()] and should deallocate any resources acquired ** by xInit. The pAppData pointer is used as the only parameter to ** xInit and xShutdown. +** +** SQLite holds the [SQLITE_MUTEX_STATIC_MASTER] mutex when it invokes +** the xInit method, so the xInit method need not be threadsafe. The +** xShutdown method is only called from [sqlite3_shutdown()] so it does +** not need to be threadsafe either. For all other methods, SQLite +** holds the [SQLITE_MUTEX_STATIC_MEM] mutex as long as the +** [SQLITE_CONFIG_MEMSTATUS] configuration option is turned on (which +** it is by default) and so the methods are automatically serialized. +** However, if [SQLITE_CONFIG_MEMSTATUS] is disabled, then the other +** methods must be threadsafe or else make their own arrangements for +** serialization. +** +** SQLite will never invoke xInit() more than once without an intervening +** call to xShutdown(). */ typedef struct sqlite3_mem_methods sqlite3_mem_methods; struct sqlite3_mem_methods { @@ -978,7 +1055,7 @@ }; /* -** CAPI3REF: Configuration Options {H10160} +** CAPI3REF: Configuration Options ** EXPERIMENTAL ** ** These constants are the available integer configuration options that @@ -993,22 +1070,33 @@ ** **
    **
    SQLITE_CONFIG_SINGLETHREAD
    -**
    There are no arguments to this option. This option disables +**
    There are no arguments to this option. ^This option sets the +** [threading mode] to Single-thread. In other words, it disables ** all mutexing and puts SQLite into a mode where it can only be used -** by a single thread.
    +** by a single thread. ^If SQLite is compiled with +** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then +** it is not possible to change the [threading mode] from its default +** value of Single-thread and so [sqlite3_config()] will return +** [SQLITE_ERROR] if called with the SQLITE_CONFIG_SINGLETHREAD +** configuration option. ** **
    SQLITE_CONFIG_MULTITHREAD
    -**
    There are no arguments to this option. This option disables +**
    There are no arguments to this option. ^This option sets the +** [threading mode] to Multi-thread. In other words, it disables ** mutexing on [database connection] and [prepared statement] objects. ** The application is responsible for serializing access to ** [database connections] and [prepared statements]. But other mutexes ** are enabled so that SQLite will be safe to use in a multi-threaded ** environment as long as no two threads attempt to use the same -** [database connection] at the same time. See the [threading mode] -** documentation for additional information.
    +** [database connection] at the same time. ^If SQLite is compiled with +** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then +** it is not possible to set the Multi-thread [threading mode] and +** [sqlite3_config()] will return [SQLITE_ERROR] if called with the +** SQLITE_CONFIG_MULTITHREAD configuration option. ** **
    SQLITE_CONFIG_SERIALIZED
    -**
    There are no arguments to this option. This option enables +**
    There are no arguments to this option. ^This option sets the +** [threading mode] to Serialized. In other words, this option enables ** all mutexes including the recursive ** mutexes on [database connection] and [prepared statement] objects. ** In this mode (which is the default when SQLite is compiled with @@ -1016,55 +1104,63 @@ ** to [database connections] and [prepared statements] so that the ** application is free to use the same [database connection] or the ** same [prepared statement] in different threads at the same time. -** See the [threading mode] documentation for additional information.
    +** ^If SQLite is compiled with +** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then +** it is not possible to set the Serialized [threading mode] and +** [sqlite3_config()] will return [SQLITE_ERROR] if called with the +** SQLITE_CONFIG_SERIALIZED configuration option. ** **
    SQLITE_CONFIG_MALLOC
    -**
    This option takes a single argument which is a pointer to an +**
    ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mem_methods] structure. The argument specifies ** alternative low-level memory allocation routines to be used in place of -** the memory allocation routines built into SQLite.
    +** the memory allocation routines built into SQLite.)^ ^SQLite makes +** its own private copy of the content of the [sqlite3_mem_methods] structure +** before the [sqlite3_config()] call returns. ** **
    SQLITE_CONFIG_GETMALLOC
    -**
    This option takes a single argument which is a pointer to an +**
    ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mem_methods] structure. The [sqlite3_mem_methods] -** structure is filled with the currently defined memory allocation routines. +** structure is filled with the currently defined memory allocation routines.)^ ** This option can be used to overload the default memory allocation ** routines with a wrapper that simulations memory allocation failure or -** tracks memory usage, for example.
    +** tracks memory usage, for example. ** **
    SQLITE_CONFIG_MEMSTATUS
    -**
    This option takes single argument of type int, interpreted as a +**
    ^This option takes single argument of type int, interpreted as a ** boolean, which enables or disables the collection of memory allocation -** statistics. When disabled, the following SQLite interfaces become -** non-operational: +** statistics. ^(When memory allocation statistics are disabled, the +** following SQLite interfaces become non-operational: **
      **
    • [sqlite3_memory_used()] **
    • [sqlite3_memory_highwater()] **
    • [sqlite3_soft_heap_limit()] **
    • [sqlite3_status()] -**
    +** )^ +** ^Memory allocation statistics are enabled by default unless SQLite is +** compiled with [SQLITE_DEFAULT_MEMSTATUS]=0 in which case memory +** allocation statistics are disabled by default. **
    ** **
    SQLITE_CONFIG_SCRATCH
    -**
    This option specifies a static memory buffer that SQLite can use for +**
    ^This option specifies a static memory buffer that SQLite can use for ** scratch memory. There are three arguments: A pointer an 8-byte ** aligned memory buffer from which the scrach allocations will be ** drawn, the size of each scratch allocation (sz), ** and the maximum number of scratch allocations (N). The sz ** argument must be a multiple of 16. The sz parameter should be a few bytes ** larger than the actual scratch space required due to internal overhead. -** The first argument should pointer to an 8-byte aligned buffer +** The first argument must be a pointer to an 8-byte aligned buffer ** of at least sz*N bytes of memory. -** SQLite will use no more than one scratch buffer at once per thread, so -** N should be set to the expected maximum number of threads. The sz -** parameter should be 6 times the size of the largest database page size. -** Scratch buffers are used as part of the btree balance operation. If -** The btree balancer needs additional memory beyond what is provided by -** scratch buffers or if no scratch buffer space is specified, then SQLite -** goes to [sqlite3_malloc()] to obtain the memory it needs.
    +** ^SQLite will use no more than one scratch buffer per thread. So +** N should be set to the expected maximum number of threads. ^SQLite will +** never require a scratch buffer that is more than 6 times the database +** page size. ^If SQLite needs needs additional scratch memory beyond +** what is provided by this configuration option, then +** [sqlite3_malloc()] will be used to obtain the memory needed. ** **
    SQLITE_CONFIG_PAGECACHE
    -**
    This option specifies a static memory buffer that SQLite can use for +**
    ^This option specifies a static memory buffer that SQLite can use for ** the database page cache with the default page cache implemenation. ** This configuration should not be used if an application-define page ** cache implementation is loaded using the SQLITE_CONFIG_PCACHE option. @@ -1072,28 +1168,28 @@ ** memory, the size of each page buffer (sz), and the number of pages (N). ** The sz argument should be the size of the largest database page ** (a power of two between 512 and 32768) plus a little extra for each -** page header. The page header size is 20 to 40 bytes depending on -** the host architecture. It is harmless, apart from the wasted memory, +** page header. ^The page header size is 20 to 40 bytes depending on +** the host architecture. ^It is harmless, apart from the wasted memory, ** to make sz a little too large. The first ** argument should point to an allocation of at least sz*N bytes of memory. -** SQLite will use the memory provided by the first argument to satisfy its -** memory needs for the first N pages that it adds to cache. If additional +** ^SQLite will use the memory provided by the first argument to satisfy its +** memory needs for the first N pages that it adds to cache. ^If additional ** page cache memory is needed beyond what is provided by this option, then ** SQLite goes to [sqlite3_malloc()] for the additional storage space. -** The implementation might use one or more of the N buffers to hold +** ^The implementation might use one or more of the N buffers to hold ** memory accounting information. The pointer in the first argument must ** be aligned to an 8-byte boundary or subsequent behavior of SQLite ** will be undefined.
    ** **
    SQLITE_CONFIG_HEAP
    -**
    This option specifies a static memory buffer that SQLite will use +**
    ^This option specifies a static memory buffer that SQLite will use ** for all of its dynamic memory allocation needs beyond those provided ** for by [SQLITE_CONFIG_SCRATCH] and [SQLITE_CONFIG_PAGECACHE]. ** There are three arguments: An 8-byte aligned pointer to the memory, ** the number of bytes in the memory buffer, and the minimum allocation size. -** If the first pointer (the memory pointer) is NULL, then SQLite reverts +** ^If the first pointer (the memory pointer) is NULL, then SQLite reverts ** to using its default memory allocator (the system malloc() implementation), -** undoing any prior invocation of [SQLITE_CONFIG_MALLOC]. If the +** undoing any prior invocation of [SQLITE_CONFIG_MALLOC]. ^If the ** memory pointer is not NULL and either [SQLITE_ENABLE_MEMSYS3] or ** [SQLITE_ENABLE_MEMSYS5] are defined, then the alternative memory ** allocator is engaged to handle all of SQLites memory allocation needs. @@ -1101,36 +1197,50 @@ ** boundary or subsequent behavior of SQLite will be undefined.
    ** **
    SQLITE_CONFIG_MUTEX
    -**
    This option takes a single argument which is a pointer to an +**
    ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mutex_methods] structure. The argument specifies ** alternative low-level mutex routines to be used in place -** the mutex routines built into SQLite.
    +** the mutex routines built into SQLite.)^ ^SQLite makes a copy of the +** content of the [sqlite3_mutex_methods] structure before the call to +** [sqlite3_config()] returns. ^If SQLite is compiled with +** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then +** the entire mutexing subsystem is omitted from the build and hence calls to +** [sqlite3_config()] with the SQLITE_CONFIG_MUTEX configuration option will +** return [SQLITE_ERROR]. ** **
    SQLITE_CONFIG_GETMUTEX
    -**
    This option takes a single argument which is a pointer to an +**
    ^(This option takes a single argument which is a pointer to an ** instance of the [sqlite3_mutex_methods] structure. The ** [sqlite3_mutex_methods] -** structure is filled with the currently defined mutex routines. +** structure is filled with the currently defined mutex routines.)^ ** This option can be used to overload the default mutex allocation ** routines with a wrapper used to track mutex usage for performance -** profiling or testing, for example.
    +** profiling or testing, for example. ^If SQLite is compiled with +** the [SQLITE_THREADSAFE | SQLITE_THREADSAFE=0] compile-time option then +** the entire mutexing subsystem is omitted from the build and hence calls to +** [sqlite3_config()] with the SQLITE_CONFIG_GETMUTEX configuration option will +** return [SQLITE_ERROR]. ** **
    SQLITE_CONFIG_LOOKASIDE
    -**
    This option takes two arguments that determine the default -** memory allcation lookaside optimization. The first argument is the +**
    ^(This option takes two arguments that determine the default +** memory allocation for the lookaside memory allocator on each +** [database connection]. The first argument is the ** size of each lookaside buffer slot and the second is the number of -** slots allocated to each database connection.
    +** slots allocated to each database connection.)^ ^(This option sets the +** default lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE] +** verb to [sqlite3_db_config()] can be used to change the lookaside +** configuration on individual connections.)^ ** **
    SQLITE_CONFIG_PCACHE
    -**
    This option takes a single argument which is a pointer to +**
    ^(This option takes a single argument which is a pointer to ** an [sqlite3_pcache_methods] object. This object specifies the interface -** to a custom page cache implementation. SQLite makes a copy of the +** to a custom page cache implementation.)^ ^SQLite makes a copy of the ** object and uses it for page cache memory allocations.
    ** **
    SQLITE_CONFIG_GETPCACHE
    -**
    This option takes a single argument which is a pointer to an +**
    ^(This option takes a single argument which is a pointer to an ** [sqlite3_pcache_methods] object. SQLite copies of the current -** page cache implementation into that object.
    +** page cache implementation into that object.)^ ** **
    */ @@ -1151,7 +1261,7 @@ #define SQLITE_CONFIG_GETPCACHE 15 /* sqlite3_pcache_methods* */ /* -** CAPI3REF: Configuration Options {H10170} +** CAPI3REF: Configuration Options ** EXPERIMENTAL ** ** These constants are the available integer configuration options that @@ -1160,21 +1270,26 @@ ** New configuration options may be added in future releases of SQLite. ** Existing configuration options might be discontinued. Applications ** should check the return code from [sqlite3_db_config()] to make sure that -** the call worked. The [sqlite3_db_config()] interface will return a +** the call worked. ^The [sqlite3_db_config()] interface will return a ** non-zero [error code] if a discontinued or unsupported configuration option ** is invoked. ** **
    **
    SQLITE_DBCONFIG_LOOKASIDE
    -**
    This option takes three additional arguments that determine the +**
    ^This option takes three additional arguments that determine the ** [lookaside memory allocator] configuration for the [database connection]. -** The first argument (the third parameter to [sqlite3_db_config()] is a -** pointer to an 8-byte aligned memory buffer to use for lookaside memory. -** The first argument may be NULL in which case SQLite will allocate the -** lookaside buffer itself using [sqlite3_malloc()]. The second argument is the -** size of each lookaside buffer slot and the third argument is the number of +** ^The first argument (the third parameter to [sqlite3_db_config()] is a +** pointer to an memory buffer to use for lookaside memory. +** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb +** may be NULL in which case SQLite will allocate the +** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the +** size of each lookaside buffer slot. ^The third argument is the number of ** slots. The size of the buffer in the first argument must be greater than -** or equal to the product of the second and third arguments.
    +** or equal to the product of the second and third arguments. The buffer +** must be aligned to an 8-byte boundary. ^If the second argument to +** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally +** rounded down to the next smaller +** multiple of 8. See also: [SQLITE_CONFIG_LOOKASIDE] ** **
    */ @@ -1182,52 +1297,49 @@ /* -** CAPI3REF: Enable Or Disable Extended Result Codes {H12200} -** -** The sqlite3_extended_result_codes() routine enables or disables the -** [extended result codes] feature of SQLite. The extended result -** codes are disabled by default for historical compatibility considerations. +** CAPI3REF: Enable Or Disable Extended Result Codes ** -** Requirements: -** [H12201] [H12202] +** ^The sqlite3_extended_result_codes() routine enables or disables the +** [extended result codes] feature of SQLite. ^The extended result +** codes are disabled by default for historical compatibility. */ -int sqlite3_extended_result_codes(sqlite3*, int onoff); +SQLITE_API int sqlite3_extended_result_codes(sqlite3*, int onoff); /* -** CAPI3REF: Last Insert Rowid {H12220} +** CAPI3REF: Last Insert Rowid ** -** Each entry in an SQLite table has a unique 64-bit signed -** integer key called the [ROWID | "rowid"]. The rowid is always available +** ^Each entry in an SQLite table has a unique 64-bit signed +** integer key called the [ROWID | "rowid"]. ^The rowid is always available ** as an undeclared column named ROWID, OID, or _ROWID_ as long as those -** names are not also used by explicitly declared columns. If +** names are not also used by explicitly declared columns. ^If ** the table has a column of type [INTEGER PRIMARY KEY] then that column ** is another alias for the rowid. ** -** This routine returns the [rowid] of the most recent +** ^This routine returns the [rowid] of the most recent ** successful [INSERT] into the database from the [database connection] -** in the first argument. If no successful [INSERT]s +** in the first argument. ^If no successful [INSERT]s ** have ever occurred on that database connection, zero is returned. ** -** If an [INSERT] occurs within a trigger, then the [rowid] of the inserted +** ^(If an [INSERT] occurs within a trigger, then the [rowid] of the inserted ** row is returned by this routine as long as the trigger is running. ** But once the trigger terminates, the value returned by this routine -** reverts to the last value inserted before the trigger fired. +** reverts to the last value inserted before the trigger fired.)^ ** -** An [INSERT] that fails due to a constraint violation is not a +** ^An [INSERT] that fails due to a constraint violation is not a ** successful [INSERT] and does not change the value returned by this -** routine. Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK, +** routine. ^Thus INSERT OR FAIL, INSERT OR IGNORE, INSERT OR ROLLBACK, ** and INSERT OR ABORT make no changes to the return value of this -** routine when their insertion fails. When INSERT OR REPLACE +** routine when their insertion fails. ^(When INSERT OR REPLACE ** encounters a constraint violation, it does not fail. The ** INSERT continues to completion after deleting rows that caused ** the constraint problem so INSERT OR REPLACE will always change -** the return value of this interface. +** the return value of this interface.)^ ** -** For the purposes of this routine, an [INSERT] is considered to +** ^For the purposes of this routine, an [INSERT] is considered to ** be successful even if it is subsequently rolled back. ** -** Requirements: -** [H12221] [H12223] +** This function is accessible to SQL statements via the +** [last_insert_rowid() SQL function]. ** ** If a separate thread performs a new [INSERT] on the same ** database connection while the [sqlite3_last_insert_rowid()] @@ -1236,27 +1348,28 @@ ** unpredictable and might not equal either the old or the new ** last insert [rowid]. */ -sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); +SQLITE_API sqlite3_int64 sqlite3_last_insert_rowid(sqlite3*); /* -** CAPI3REF: Count The Number Of Rows Modified {H12240} +** CAPI3REF: Count The Number Of Rows Modified ** -** This function returns the number of database rows that were changed +** ^This function returns the number of database rows that were changed ** or inserted or deleted by the most recently completed SQL statement ** on the [database connection] specified by the first parameter. -** Only changes that are directly specified by the [INSERT], [UPDATE], +** ^(Only changes that are directly specified by the [INSERT], [UPDATE], ** or [DELETE] statement are counted. Auxiliary changes caused by -** triggers are not counted. Use the [sqlite3_total_changes()] function -** to find the total number of changes including changes caused by triggers. +** triggers or [foreign key actions] are not counted.)^ Use the +** [sqlite3_total_changes()] function to find the total number of changes +** including changes caused by triggers and foreign key actions. ** -** Changes to a view that are simulated by an [INSTEAD OF trigger] +** ^Changes to a view that are simulated by an [INSTEAD OF trigger] ** are not counted. Only real table changes are counted. ** -** A "row change" is a change to a single row of a single table +** ^(A "row change" is a change to a single row of a single table ** caused by an INSERT, DELETE, or UPDATE statement. Rows that ** are changed as side effects of [REPLACE] constraint resolution, ** rollback, ABORT processing, [DROP TABLE], or by any other -** mechanisms do not count as direct row changes. +** mechanisms do not count as direct row changes.)^ ** ** A "trigger context" is a scope of execution that begins and ** ends with the script of a [CREATE TRIGGER | trigger]. @@ -1266,132 +1379,122 @@ ** new trigger context is entered for the duration of that one ** trigger. Subtriggers create subcontexts for their duration. ** -** Calling [sqlite3_exec()] or [sqlite3_step()] recursively does +** ^Calling [sqlite3_exec()] or [sqlite3_step()] recursively does ** not create a new trigger context. ** -** This function returns the number of direct row changes in the +** ^This function returns the number of direct row changes in the ** most recent INSERT, UPDATE, or DELETE statement within the same ** trigger context. ** -** Thus, when called from the top level, this function returns the +** ^Thus, when called from the top level, this function returns the ** number of changes in the most recent INSERT, UPDATE, or DELETE -** that also occurred at the top level. Within the body of a trigger, +** that also occurred at the top level. ^(Within the body of a trigger, ** the sqlite3_changes() interface can be called to find the number of ** changes in the most recently completed INSERT, UPDATE, or DELETE ** statement within the body of the same trigger. ** However, the number returned does not include changes -** caused by subtriggers since those have their own context. +** caused by subtriggers since those have their own context.)^ ** -** See also the [sqlite3_total_changes()] interface and the -** [count_changes pragma]. -** -** Requirements: -** [H12241] [H12243] +** See also the [sqlite3_total_changes()] interface, the +** [count_changes pragma], and the [changes() SQL function]. ** ** If a separate thread makes changes on the same database connection ** while [sqlite3_changes()] is running then the value returned ** is unpredictable and not meaningful. */ -int sqlite3_changes(sqlite3*); +SQLITE_API int sqlite3_changes(sqlite3*); /* -** CAPI3REF: Total Number Of Rows Modified {H12260} +** CAPI3REF: Total Number Of Rows Modified ** -** This function returns the number of row changes caused by [INSERT], +** ^This function returns the number of row changes caused by [INSERT], ** [UPDATE] or [DELETE] statements since the [database connection] was opened. -** The count includes all changes from all -** [CREATE TRIGGER | trigger] contexts. However, +** ^(The count returned by sqlite3_total_changes() includes all changes +** from all [CREATE TRIGGER | trigger] contexts and changes made by +** [foreign key actions]. However, ** the count does not include changes used to implement [REPLACE] constraints, ** do rollbacks or ABORT processing, or [DROP TABLE] processing. The ** count does not include rows of views that fire an [INSTEAD OF trigger], ** though if the INSTEAD OF trigger makes changes of its own, those changes -** are counted. -** The changes are counted as soon as the statement that makes them is -** completed (when the statement handle is passed to [sqlite3_reset()] or -** [sqlite3_finalize()]). -** -** See also the [sqlite3_changes()] interface and the -** [count_changes pragma]. +** are counted.)^ +** ^The sqlite3_total_changes() function counts the changes as soon as +** the statement that makes them is completed (when the statement handle +** is passed to [sqlite3_reset()] or [sqlite3_finalize()]). ** -** Requirements: -** [H12261] [H12263] +** See also the [sqlite3_changes()] interface, the +** [count_changes pragma], and the [total_changes() SQL function]. ** ** If a separate thread makes changes on the same database connection ** while [sqlite3_total_changes()] is running then the value ** returned is unpredictable and not meaningful. */ -int sqlite3_total_changes(sqlite3*); +SQLITE_API int sqlite3_total_changes(sqlite3*); /* -** CAPI3REF: Interrupt A Long-Running Query {H12270} +** CAPI3REF: Interrupt A Long-Running Query ** -** This function causes any pending database operation to abort and +** ^This function causes any pending database operation to abort and ** return at its earliest opportunity. This routine is typically ** called in response to a user action such as pressing "Cancel" ** or Ctrl-C where the user wants a long query operation to halt ** immediately. ** -** It is safe to call this routine from a thread different from the +** ^It is safe to call this routine from a thread different from the ** thread that is currently running the database operation. But it ** is not safe to call this routine with a [database connection] that ** is closed or might close before sqlite3_interrupt() returns. ** -** If an SQL operation is very nearly finished at the time when +** ^If an SQL operation is very nearly finished at the time when ** sqlite3_interrupt() is called, then it might not have an opportunity ** to be interrupted and might continue to completion. ** -** An SQL operation that is interrupted will return [SQLITE_INTERRUPT]. -** If the interrupted SQL operation is an INSERT, UPDATE, or DELETE +** ^An SQL operation that is interrupted will return [SQLITE_INTERRUPT]. +** ^If the interrupted SQL operation is an INSERT, UPDATE, or DELETE ** that is inside an explicit transaction, then the entire transaction ** will be rolled back automatically. ** -** The sqlite3_interrupt(D) call is in effect until all currently running -** SQL statements on [database connection] D complete. Any new SQL statements +** ^The sqlite3_interrupt(D) call is in effect until all currently running +** SQL statements on [database connection] D complete. ^Any new SQL statements ** that are started after the sqlite3_interrupt() call and before the ** running statements reaches zero are interrupted as if they had been -** running prior to the sqlite3_interrupt() call. New SQL statements +** running prior to the sqlite3_interrupt() call. ^New SQL statements ** that are started after the running statement count reaches zero are ** not effected by the sqlite3_interrupt(). -** A call to sqlite3_interrupt(D) that occurs when there are no running +** ^A call to sqlite3_interrupt(D) that occurs when there are no running ** SQL statements is a no-op and has no effect on SQL statements ** that are started after the sqlite3_interrupt() call returns. ** -** Requirements: -** [H12271] [H12272] -** ** If the database connection closes while [sqlite3_interrupt()] ** is running then bad things will likely happen. */ -void sqlite3_interrupt(sqlite3*); +SQLITE_API void sqlite3_interrupt(sqlite3*); /* -** CAPI3REF: Determine If An SQL Statement Is Complete {H10510} +** CAPI3REF: Determine If An SQL Statement Is Complete ** ** These routines are useful during command-line input to determine if the ** currently entered text seems to form a complete SQL statement or ** if additional input is needed before sending the text into -** SQLite for parsing. These routines return 1 if the input string -** appears to be a complete SQL statement. A statement is judged to be +** SQLite for parsing. ^These routines return 1 if the input string +** appears to be a complete SQL statement. ^A statement is judged to be ** complete if it ends with a semicolon token and is not a prefix of a -** well-formed CREATE TRIGGER statement. Semicolons that are embedded within +** well-formed CREATE TRIGGER statement. ^Semicolons that are embedded within ** string literals or quoted identifier names or comments are not ** independent tokens (they are part of the token in which they are -** embedded) and thus do not count as a statement terminator. Whitespace +** embedded) and thus do not count as a statement terminator. ^Whitespace ** and comments that follow the final semicolon are ignored. ** -** These routines return 0 if the statement is incomplete. If a +** ^These routines return 0 if the statement is incomplete. ^If a ** memory allocation fails, then SQLITE_NOMEM is returned. ** -** These routines do not parse the SQL statements thus +** ^These routines do not parse the SQL statements thus ** will not detect syntactically incorrect SQL. ** -** If SQLite has not been initialized using [sqlite3_initialize()] prior +** ^(If SQLite has not been initialized using [sqlite3_initialize()] prior ** to invoking sqlite3_complete16() then sqlite3_initialize() is invoked ** automatically by sqlite3_complete16(). If that initialization fails, ** then the return value from sqlite3_complete16() will be non-zero -** regardless of whether or not the input SQL is complete. -** -** Requirements: [H10511] [H10512] +** regardless of whether or not the input SQL is complete.)^ ** ** The input to [sqlite3_complete()] must be a zero-terminated ** UTF-8 string. @@ -1399,31 +1502,31 @@ ** The input to [sqlite3_complete16()] must be a zero-terminated ** UTF-16 string in native byte order. */ -int sqlite3_complete(const char *sql); -int sqlite3_complete16(const void *sql); +SQLITE_API int sqlite3_complete(const char *sql); +SQLITE_API int sqlite3_complete16(const void *sql); /* -** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors {H12310} +** CAPI3REF: Register A Callback To Handle SQLITE_BUSY Errors ** -** This routine sets a callback function that might be invoked whenever +** ^This routine sets a callback function that might be invoked whenever ** an attempt is made to open a database table that another thread ** or process has locked. ** -** If the busy callback is NULL, then [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] -** is returned immediately upon encountering the lock. If the busy callback -** is not NULL, then the callback will be invoked with two arguments. -** -** The first argument to the handler is a copy of the void* pointer which -** is the third argument to sqlite3_busy_handler(). The second argument to -** the handler callback is the number of times that the busy handler has -** been invoked for this locking event. If the +** ^If the busy callback is NULL, then [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] +** is returned immediately upon encountering the lock. ^If the busy callback +** is not NULL, then the callback might be invoked with two arguments. +** +** ^The first argument to the busy handler is a copy of the void* pointer which +** is the third argument to sqlite3_busy_handler(). ^The second argument to +** the busy handler callback is the number of times that the busy handler has +** been invoked for this locking event. ^If the ** busy callback returns 0, then no additional attempts are made to ** access the database and [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED] is returned. -** If the callback returns non-zero, then another attempt +** ^If the callback returns non-zero, then another attempt ** is made to open the database for reading and the cycle repeats. ** ** The presence of a busy handler does not guarantee that it will be invoked -** when there is lock contention. If SQLite determines that invoking the busy +** when there is lock contention. ^If SQLite determines that invoking the busy ** handler could result in a deadlock, it will go ahead and return [SQLITE_BUSY] ** or [SQLITE_IOERR_BLOCKED] instead of invoking the busy handler. ** Consider a scenario where one process is holding a read lock that @@ -1437,65 +1540,59 @@ ** will induce the first process to release its read lock and allow ** the second process to proceed. ** -** The default busy callback is NULL. +** ^The default busy callback is NULL. ** -** The [SQLITE_BUSY] error is converted to [SQLITE_IOERR_BLOCKED] +** ^The [SQLITE_BUSY] error is converted to [SQLITE_IOERR_BLOCKED] ** when SQLite is in the middle of a large transaction where all the ** changes will not fit into the in-memory cache. SQLite will ** already hold a RESERVED lock on the database file, but it needs ** to promote this lock to EXCLUSIVE so that it can spill cache ** pages into the database file without harm to concurrent -** readers. If it is unable to promote the lock, then the in-memory +** readers. ^If it is unable to promote the lock, then the in-memory ** cache will be left in an inconsistent state and so the error ** code is promoted from the relatively benign [SQLITE_BUSY] to -** the more severe [SQLITE_IOERR_BLOCKED]. This error code promotion +** the more severe [SQLITE_IOERR_BLOCKED]. ^This error code promotion ** forces an automatic rollback of the changes. See the ** ** CorruptionFollowingBusyError wiki page for a discussion of why ** this is important. ** -** There can only be a single busy handler defined for each +** ^(There can only be a single busy handler defined for each ** [database connection]. Setting a new busy handler clears any -** previously set handler. Note that calling [sqlite3_busy_timeout()] +** previously set handler.)^ ^Note that calling [sqlite3_busy_timeout()] ** will also set or clear the busy handler. ** ** The busy callback should not take any actions which modify the ** database connection that invoked the busy handler. Any such actions ** result in undefined behavior. ** -** Requirements: -** [H12311] [H12312] [H12314] [H12316] [H12318] -** ** A busy handler must not close the database connection ** or [prepared statement] that invoked the busy handler. */ -int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); +SQLITE_API int sqlite3_busy_handler(sqlite3*, int(*)(void*,int), void*); /* -** CAPI3REF: Set A Busy Timeout {H12340} +** CAPI3REF: Set A Busy Timeout ** -** This routine sets a [sqlite3_busy_handler | busy handler] that sleeps -** for a specified amount of time when a table is locked. The handler +** ^This routine sets a [sqlite3_busy_handler | busy handler] that sleeps +** for a specified amount of time when a table is locked. ^The handler ** will sleep multiple times until at least "ms" milliseconds of sleeping -** have accumulated. {H12343} After "ms" milliseconds of sleeping, +** have accumulated. ^After at least "ms" milliseconds of sleeping, ** the handler returns 0 which causes [sqlite3_step()] to return ** [SQLITE_BUSY] or [SQLITE_IOERR_BLOCKED]. ** -** Calling this routine with an argument less than or equal to zero +** ^Calling this routine with an argument less than or equal to zero ** turns off all busy handlers. ** -** There can only be a single busy handler for a particular +** ^(There can only be a single busy handler for a particular ** [database connection] any any given moment. If another busy handler ** was defined (using [sqlite3_busy_handler()]) prior to calling -** this routine, that other busy handler is cleared. -** -** Requirements: -** [H12341] [H12343] [H12344] +** this routine, that other busy handler is cleared.)^ */ -int sqlite3_busy_timeout(sqlite3*, int ms); +SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms); /* -** CAPI3REF: Convenience Routines For Running Queries {H12370} +** CAPI3REF: Convenience Routines For Running Queries ** ** Definition: A result table is memory data structure created by the ** [sqlite3_get_table()] interface. A result table records the @@ -1543,29 +1640,27 @@ ** azResult[7] = "21"; ** ** -** The sqlite3_get_table() function evaluates one or more +** ^The sqlite3_get_table() function evaluates one or more ** semicolon-separated SQL statements in the zero-terminated UTF-8 -** string of its 2nd parameter. It returns a result table to the +** string of its 2nd parameter and returns a result table to the ** pointer given in its 3rd parameter. ** -** After the calling function has finished using the result, it should -** pass the pointer to the result table to sqlite3_free_table() in order to +** After the application has finished with the result from sqlite3_get_table(), +** it should pass the result table pointer to sqlite3_free_table() in order to ** release the memory that was malloced. Because of the way the ** [sqlite3_malloc()] happens within sqlite3_get_table(), the calling ** function must not try to call [sqlite3_free()] directly. Only ** [sqlite3_free_table()] is able to release the memory properly and safely. ** -** The sqlite3_get_table() interface is implemented as a wrapper around +** ^(The sqlite3_get_table() interface is implemented as a wrapper around ** [sqlite3_exec()]. The sqlite3_get_table() routine does not have access ** to any internal data structures of SQLite. It uses only the public ** interface defined here. As a consequence, errors that occur in the ** wrapper layer outside of the internal [sqlite3_exec()] call are not -** reflected in subsequent calls to [sqlite3_errcode()] or [sqlite3_errmsg()]. -** -** Requirements: -** [H12371] [H12373] [H12374] [H12376] [H12379] [H12382] +** reflected in subsequent calls to [sqlite3_errcode()] or +** [sqlite3_errmsg()].)^ */ -int sqlite3_get_table( +SQLITE_API int sqlite3_get_table( sqlite3 *db, /* An open database */ const char *zSql, /* SQL to be evaluated */ char ***pazResult, /* Results of the query */ @@ -1573,36 +1668,36 @@ int *pnColumn, /* Number of result columns written here */ char **pzErrmsg /* Error msg written here */ ); -void sqlite3_free_table(char **result); +SQLITE_API void sqlite3_free_table(char **result); /* -** CAPI3REF: Formatted String Printing Functions {H17400} +** CAPI3REF: Formatted String Printing Functions ** -** These routines are workalikes of the "printf()" family of functions +** These routines are work-alikes of the "printf()" family of functions ** from the standard C library. ** -** The sqlite3_mprintf() and sqlite3_vmprintf() routines write their +** ^The sqlite3_mprintf() and sqlite3_vmprintf() routines write their ** results into memory obtained from [sqlite3_malloc()]. ** The strings returned by these two routines should be -** released by [sqlite3_free()]. Both routines return a +** released by [sqlite3_free()]. ^Both routines return a ** NULL pointer if [sqlite3_malloc()] is unable to allocate enough ** memory to hold the resulting string. ** -** In sqlite3_snprintf() routine is similar to "snprintf()" from +** ^(In sqlite3_snprintf() routine is similar to "snprintf()" from ** the standard C library. The result is written into the ** buffer supplied as the second parameter whose size is given by ** the first parameter. Note that the order of the -** first two parameters is reversed from snprintf(). This is an +** first two parameters is reversed from snprintf().)^ This is an ** historical accident that cannot be fixed without breaking -** backwards compatibility. Note also that sqlite3_snprintf() +** backwards compatibility. ^(Note also that sqlite3_snprintf() ** returns a pointer to its buffer instead of the number of -** characters actually written into the buffer. We admit that +** characters actually written into the buffer.)^ We admit that ** the number of characters written would be a more useful return ** value but we cannot change the implementation of sqlite3_snprintf() ** now without breaking compatibility. ** -** As long as the buffer size is greater than zero, sqlite3_snprintf() -** guarantees that the buffer is always zero-terminated. The first +** ^As long as the buffer size is greater than zero, sqlite3_snprintf() +** guarantees that the buffer is always zero-terminated. ^The first ** parameter "n" is the total size of the buffer, including space for ** the zero terminator. So the longest string that can be completely ** written will be n-1 characters. @@ -1612,9 +1707,9 @@ ** All of the usual printf() formatting options apply. In addition, there ** is are "%q", "%Q", and "%z" options. ** -** The %q option works like %s in that it substitutes a null-terminated +** ^(The %q option works like %s in that it substitutes a null-terminated ** string from the argument list. But %q also doubles every '\'' character. -** %q is designed for use inside a string literal. By doubling each '\'' +** %q is designed for use inside a string literal.)^ By doubling each '\'' ** character it escapes that character and allows it to be inserted into ** the string. ** @@ -1649,10 +1744,10 @@ ** This second example is an SQL syntax error. As a general rule you should ** always use %q instead of %s when inserting text into a string literal. ** -** The %Q option works like %q except it also adds single quotes around +** ^(The %Q option works like %q except it also adds single quotes around ** the outside of the total string. Additionally, if the parameter in the ** argument list is a NULL pointer, %Q substitutes the text "NULL" (without -** single quotes) in place of the %Q option. So, for example, one could say: +** single quotes).)^ So, for example, one could say: ** **
     **  char *zSQL = sqlite3_mprintf("INSERT INTO table VALUES(%Q)", zText);
    @@ -1663,35 +1758,32 @@
     ** The code above will render a correct SQL statement in the zSQL
     ** variable even if the zText variable is a NULL pointer.
     **
    -** The "%z" formatting option works exactly like "%s" with the
    +** ^(The "%z" formatting option works like "%s" but with the
     ** addition that after the string has been read and copied into
    -** the result, [sqlite3_free()] is called on the input string. {END}
    -**
    -** Requirements:
    -** [H17403] [H17406] [H17407]
    +** the result, [sqlite3_free()] is called on the input string.)^
     */
    -char *sqlite3_mprintf(const char*,...);
    -char *sqlite3_vmprintf(const char*, va_list);
    -char *sqlite3_snprintf(int,char*,const char*, ...);
    +SQLITE_API char *sqlite3_mprintf(const char*,...);
    +SQLITE_API char *sqlite3_vmprintf(const char*, va_list);
    +SQLITE_API char *sqlite3_snprintf(int,char*,const char*, ...);
     
     /*
    -** CAPI3REF: Memory Allocation Subsystem {H17300} 
    +** CAPI3REF: Memory Allocation Subsystem
     **
    -** The SQLite core  uses these three routines for all of its own
    +** The SQLite core uses these three routines for all of its own
     ** internal memory allocation needs. "Core" in the previous sentence
     ** does not include operating-system specific VFS implementation.  The
     ** Windows VFS uses native malloc() and free() for some operations.
     **
    -** The sqlite3_malloc() routine returns a pointer to a block
    +** ^The sqlite3_malloc() routine returns a pointer to a block
     ** of memory at least N bytes in length, where N is the parameter.
    -** If sqlite3_malloc() is unable to obtain sufficient free
    -** memory, it returns a NULL pointer.  If the parameter N to
    +** ^If sqlite3_malloc() is unable to obtain sufficient free
    +** memory, it returns a NULL pointer.  ^If the parameter N to
     ** sqlite3_malloc() is zero or negative then sqlite3_malloc() returns
     ** a NULL pointer.
     **
    -** Calling sqlite3_free() with a pointer previously returned
    +** ^Calling sqlite3_free() with a pointer previously returned
     ** by sqlite3_malloc() or sqlite3_realloc() releases that memory so
    -** that it might be reused.  The sqlite3_free() routine is
    +** that it might be reused.  ^The sqlite3_free() routine is
     ** a no-op if is called with a NULL pointer.  Passing a NULL pointer
     ** to sqlite3_free() is harmless.  After being freed, memory
     ** should neither be read nor written.  Even reading previously freed
    @@ -1700,34 +1792,25 @@
     ** might result if sqlite3_free() is called with a non-NULL pointer that
     ** was not obtained from sqlite3_malloc() or sqlite3_realloc().
     **
    -** The sqlite3_realloc() interface attempts to resize a
    +** ^(The sqlite3_realloc() interface attempts to resize a
     ** prior memory allocation to be at least N bytes, where N is the
     ** second parameter.  The memory allocation to be resized is the first
    -** parameter.  If the first parameter to sqlite3_realloc()
    +** parameter.)^ ^ If the first parameter to sqlite3_realloc()
     ** is a NULL pointer then its behavior is identical to calling
     ** sqlite3_malloc(N) where N is the second parameter to sqlite3_realloc().
    -** If the second parameter to sqlite3_realloc() is zero or
    +** ^If the second parameter to sqlite3_realloc() is zero or
     ** negative then the behavior is exactly the same as calling
     ** sqlite3_free(P) where P is the first parameter to sqlite3_realloc().
    -** sqlite3_realloc() returns a pointer to a memory allocation
    +** ^sqlite3_realloc() returns a pointer to a memory allocation
     ** of at least N bytes in size or NULL if sufficient memory is unavailable.
    -** If M is the size of the prior allocation, then min(N,M) bytes
    +** ^If M is the size of the prior allocation, then min(N,M) bytes
     ** of the prior allocation are copied into the beginning of buffer returned
     ** by sqlite3_realloc() and the prior allocation is freed.
    -** If sqlite3_realloc() returns NULL, then the prior allocation
    +** ^If sqlite3_realloc() returns NULL, then the prior allocation
     ** is not freed.
     **
    -** The memory returned by sqlite3_malloc() and sqlite3_realloc()
    -** is always aligned to at least an 8 byte boundary. {END}
    -**
    -** The default implementation of the memory allocation subsystem uses
    -** the malloc(), realloc() and free() provided by the standard C library.
    -** {H17382} However, if SQLite is compiled with the
    -** SQLITE_MEMORY_SIZE=NNN C preprocessor macro (where NNN
    -** is an integer), then SQLite create a static array of at least
    -** NNN bytes in size and uses that array for all of its dynamic
    -** memory allocation needs. {END}  Additional memory allocator options
    -** may be added in future releases.
    +** ^The memory returned by sqlite3_malloc() and sqlite3_realloc()
    +** is always aligned to at least an 8 byte boundary.
     **
     ** In SQLite version 3.5.0 and 3.5.1, it was possible to define
     ** the SQLITE_OMIT_MEMORY_ALLOCATION which would cause the built-in
    @@ -1742,10 +1825,6 @@
     ** they are reported back as [SQLITE_CANTOPEN] or
     ** [SQLITE_IOERR] rather than [SQLITE_NOMEM].
     **
    -** Requirements:
    -** [H17303] [H17304] [H17305] [H17306] [H17310] [H17312] [H17315] [H17318]
    -** [H17321] [H17322] [H17323]
    -**
     ** The pointer arguments to [sqlite3_free()] and [sqlite3_realloc()]
     ** must be either NULL or else pointers obtained from a prior
     ** invocation of [sqlite3_malloc()] or [sqlite3_realloc()] that have
    @@ -1755,25 +1834,38 @@
     ** a block of memory after it has been released using
     ** [sqlite3_free()] or [sqlite3_realloc()].
     */
    -void *sqlite3_malloc(int);
    -void *sqlite3_realloc(void*, int);
    -void sqlite3_free(void*);
    +SQLITE_API void *sqlite3_malloc(int);
    +SQLITE_API void *sqlite3_realloc(void*, int);
    +SQLITE_API void sqlite3_free(void*);
     
     /*
    -** CAPI3REF: Memory Allocator Statistics {H17370} 
    +** CAPI3REF: Memory Allocator Statistics
     **
     ** SQLite provides these two interfaces for reporting on the status
     ** of the [sqlite3_malloc()], [sqlite3_free()], and [sqlite3_realloc()]
     ** routines, which form the built-in memory allocation subsystem.
     **
    -** Requirements:
    -** [H17371] [H17373] [H17374] [H17375]
    +** ^The [sqlite3_memory_used()] routine returns the number of bytes
    +** of memory currently outstanding (malloced but not freed).
    +** ^The [sqlite3_memory_highwater()] routine returns the maximum
    +** value of [sqlite3_memory_used()] since the high-water mark
    +** was last reset.  ^The values returned by [sqlite3_memory_used()] and
    +** [sqlite3_memory_highwater()] include any overhead
    +** added by SQLite in its implementation of [sqlite3_malloc()],
    +** but not overhead added by the any underlying system library
    +** routines that [sqlite3_malloc()] may call.
    +**
    +** ^The memory high-water mark is reset to the current value of
    +** [sqlite3_memory_used()] if and only if the parameter to
    +** [sqlite3_memory_highwater()] is true.  ^The value returned
    +** by [sqlite3_memory_highwater(1)] is the high-water mark
    +** prior to the reset.
     */
    -sqlite3_int64 sqlite3_memory_used(void);
    -sqlite3_int64 sqlite3_memory_highwater(int resetFlag);
    +SQLITE_API sqlite3_int64 sqlite3_memory_used(void);
    +SQLITE_API sqlite3_int64 sqlite3_memory_highwater(int resetFlag);
     
     /*
    -** CAPI3REF: Pseudo-Random Number Generator {H17390} 
    +** CAPI3REF: Pseudo-Random Number Generator
     **
     ** SQLite contains a high-quality pseudo-random number generator (PRNG) used to
     ** select random [ROWID | ROWIDs] when inserting new records into a table that
    @@ -1781,60 +1873,57 @@
     ** the build-in random() and randomblob() SQL functions.  This interface allows
     ** applications to access the same PRNG for other purposes.
     **
    -** A call to this routine stores N bytes of randomness into buffer P.
    +** ^A call to this routine stores N bytes of randomness into buffer P.
     **
    -** The first time this routine is invoked (either internally or by
    +** ^The first time this routine is invoked (either internally or by
     ** the application) the PRNG is seeded using randomness obtained
     ** from the xRandomness method of the default [sqlite3_vfs] object.
    -** On all subsequent invocations, the pseudo-randomness is generated
    +** ^On all subsequent invocations, the pseudo-randomness is generated
     ** internally and without recourse to the [sqlite3_vfs] xRandomness
     ** method.
    -**
    -** Requirements:
    -** [H17392]
     */
    -void sqlite3_randomness(int N, void *P);
    +SQLITE_API void sqlite3_randomness(int N, void *P);
     
     /*
    -** CAPI3REF: Compile-Time Authorization Callbacks {H12500} 
    +** CAPI3REF: Compile-Time Authorization Callbacks
     **
    -** This routine registers a authorizer callback with a particular
    +** ^This routine registers a authorizer callback with a particular
     ** [database connection], supplied in the first argument.
    -** The authorizer callback is invoked as SQL statements are being compiled
    +** ^The authorizer callback is invoked as SQL statements are being compiled
     ** by [sqlite3_prepare()] or its variants [sqlite3_prepare_v2()],
    -** [sqlite3_prepare16()] and [sqlite3_prepare16_v2()].  At various
    +** [sqlite3_prepare16()] and [sqlite3_prepare16_v2()].  ^At various
     ** points during the compilation process, as logic is being created
     ** to perform various actions, the authorizer callback is invoked to
    -** see if those actions are allowed.  The authorizer callback should
    +** see if those actions are allowed.  ^The authorizer callback should
     ** return [SQLITE_OK] to allow the action, [SQLITE_IGNORE] to disallow the
     ** specific action but allow the SQL statement to continue to be
     ** compiled, or [SQLITE_DENY] to cause the entire SQL statement to be
    -** rejected with an error.  If the authorizer callback returns
    +** rejected with an error.  ^If the authorizer callback returns
     ** any value other than [SQLITE_IGNORE], [SQLITE_OK], or [SQLITE_DENY]
     ** then the [sqlite3_prepare_v2()] or equivalent call that triggered
     ** the authorizer will fail with an error message.
     **
     ** When the callback returns [SQLITE_OK], that means the operation
    -** requested is ok.  When the callback returns [SQLITE_DENY], the
    +** requested is ok.  ^When the callback returns [SQLITE_DENY], the
     ** [sqlite3_prepare_v2()] or equivalent call that triggered the
     ** authorizer will fail with an error message explaining that
     ** access is denied. 
     **
    -** The first parameter to the authorizer callback is a copy of the third
    -** parameter to the sqlite3_set_authorizer() interface. The second parameter
    +** ^The first parameter to the authorizer callback is a copy of the third
    +** parameter to the sqlite3_set_authorizer() interface. ^The second parameter
     ** to the callback is an integer [SQLITE_COPY | action code] that specifies
    -** the particular action to be authorized. The third through sixth parameters
    +** the particular action to be authorized. ^The third through sixth parameters
     ** to the callback are zero-terminated strings that contain additional
     ** details about the action to be authorized.
     **
    -** If the action code is [SQLITE_READ]
    +** ^If the action code is [SQLITE_READ]
     ** and the callback returns [SQLITE_IGNORE] then the
     ** [prepared statement] statement is constructed to substitute
     ** a NULL value in place of the table column that would have
     ** been read if [SQLITE_OK] had been returned.  The [SQLITE_IGNORE]
     ** return can be used to deny an untrusted user access to individual
     ** columns of a table.
    -** If the action code is [SQLITE_DELETE] and the callback returns
    +** ^If the action code is [SQLITE_DELETE] and the callback returns
     ** [SQLITE_IGNORE] then the [DELETE] operation proceeds but the
     ** [truncate optimization] is disabled and all rows are deleted individually.
     **
    @@ -1854,9 +1943,9 @@
     ** and limiting database size using the [max_page_count] [PRAGMA]
     ** in addition to using an authorizer.
     **
    -** Only a single authorizer can be in place on a database connection
    +** ^(Only a single authorizer can be in place on a database connection
     ** at a time.  Each call to sqlite3_set_authorizer overrides the
    -** previous call.  Disable the authorizer by installing a NULL callback.
    +** previous call.)^  ^Disable the authorizer by installing a NULL callback.
     ** The authorizer is disabled by default.
     **
     ** The authorizer callback must not do anything that will modify
    @@ -1864,29 +1953,25 @@
     ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
     ** database connections for the meaning of "modify" in this paragraph.
     **
    -** When [sqlite3_prepare_v2()] is used to prepare a statement, the
    -** statement might be reprepared during [sqlite3_step()] due to a 
    +** ^When [sqlite3_prepare_v2()] is used to prepare a statement, the
    +** statement might be re-prepared during [sqlite3_step()] due to a 
     ** schema change.  Hence, the application should ensure that the
     ** correct authorizer callback remains in place during the [sqlite3_step()].
     **
    -** Note that the authorizer callback is invoked only during
    +** ^Note that the authorizer callback is invoked only during
     ** [sqlite3_prepare()] or its variants.  Authorization is not
     ** performed during statement evaluation in [sqlite3_step()], unless
     ** as stated in the previous paragraph, sqlite3_step() invokes
     ** sqlite3_prepare_v2() to reprepare a statement after a schema change.
    -**
    -** Requirements:
    -** [H12501] [H12502] [H12503] [H12504] [H12505] [H12506] [H12507] [H12510]
    -** [H12511] [H12512] [H12520] [H12521] [H12522]
     */
    -int sqlite3_set_authorizer(
    +SQLITE_API int sqlite3_set_authorizer(
       sqlite3*,
       int (*xAuth)(void*,int,const char*,const char*,const char*,const char*),
       void *pUserData
     );
     
     /*
    -** CAPI3REF: Authorizer Return Codes {H12590} 
    +** CAPI3REF: Authorizer Return Codes
     **
     ** The [sqlite3_set_authorizer | authorizer callback function] must
     ** return either [SQLITE_OK] or one of these two constants in order
    @@ -1898,7 +1983,7 @@
     #define SQLITE_IGNORE 2   /* Don't allow access, but don't generate an error */
     
     /*
    -** CAPI3REF: Authorizer Action Codes {H12550} 
    +** CAPI3REF: Authorizer Action Codes
     **
     ** The [sqlite3_set_authorizer()] interface registers a callback function
     ** that is invoked to authorize certain SQL statement actions.  The
    @@ -1909,15 +1994,12 @@
     ** These action code values signify what kind of operation is to be
     ** authorized.  The 3rd and 4th parameters to the authorization
     ** callback function will be parameters or NULL depending on which of these
    -** codes is used as the second parameter.  The 5th parameter to the
    +** codes is used as the second parameter.  ^(The 5th parameter to the
     ** authorizer callback is the name of the database ("main", "temp",
    -** etc.) if applicable.  The 6th parameter to the authorizer callback
    +** etc.) if applicable.)^  ^The 6th parameter to the authorizer callback
     ** is the name of the inner-most trigger or view that is responsible for
     ** the access attempt or NULL if this access attempt is directly from
     ** top-level SQL code.
    -**
    -** Requirements:
    -** [H12551] [H12552] [H12553] [H12554]
     */
     /******************************************* 3rd ************ 4th ***********/
     #define SQLITE_CREATE_INDEX          1   /* Index Name      Table Name      */
    @@ -1955,42 +2037,39 @@
     #define SQLITE_COPY                  0   /* No longer used */
     
     /*
    -** CAPI3REF: Tracing And Profiling Functions {H12280} 
    +** CAPI3REF: Tracing And Profiling Functions
     ** EXPERIMENTAL
     **
     ** These routines register callback functions that can be used for
     ** tracing and profiling the execution of SQL statements.
     **
    -** The callback function registered by sqlite3_trace() is invoked at
    +** ^The callback function registered by sqlite3_trace() is invoked at
     ** various times when an SQL statement is being run by [sqlite3_step()].
    -** The callback returns a UTF-8 rendering of the SQL statement text
    -** as the statement first begins executing.  Additional callbacks occur
    +** ^The sqlite3_trace() callback is invoked with a UTF-8 rendering of the
    +** SQL statement text as the statement first begins executing.
    +** ^(Additional sqlite3_trace() callbacks might occur
     ** as each triggered subprogram is entered.  The callbacks for triggers
    -** contain a UTF-8 SQL comment that identifies the trigger.
    +** contain a UTF-8 SQL comment that identifies the trigger.)^
     **
    -** The callback function registered by sqlite3_profile() is invoked
    -** as each SQL statement finishes.  The profile callback contains
    +** ^The callback function registered by sqlite3_profile() is invoked
    +** as each SQL statement finishes.  ^The profile callback contains
     ** the original statement text and an estimate of wall-clock time
     ** of how long that statement took to run.
    -**
    -** Requirements:
    -** [H12281] [H12282] [H12283] [H12284] [H12285] [H12287] [H12288] [H12289]
    -** [H12290]
     */
    -SQLITE_EXPERIMENTAL void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
    -SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,
    +SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_trace(sqlite3*, void(*xTrace)(void*,const char*), void*);
    +SQLITE_API SQLITE_EXPERIMENTAL void *sqlite3_profile(sqlite3*,
        void(*xProfile)(void*,const char*,sqlite3_uint64), void*);
     
     /*
    -** CAPI3REF: Query Progress Callbacks {H12910} 
    +** CAPI3REF: Query Progress Callbacks
     **
    -** This routine configures a callback function - the
    +** ^This routine configures a callback function - the
     ** progress callback - that is invoked periodically during long
     ** running calls to [sqlite3_exec()], [sqlite3_step()] and
     ** [sqlite3_get_table()].  An example use for this
     ** interface is to keep a GUI updated during a large query.
     **
    -** If the progress callback returns non-zero, the operation is
    +** ^If the progress callback returns non-zero, the operation is
     ** interrupted.  This feature can be used to implement a
     ** "Cancel" button on a GUI progress dialog box.
     **
    @@ -1999,28 +2078,26 @@
     ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their
     ** database connections for the meaning of "modify" in this paragraph.
     **
    -** Requirements:
    -** [H12911] [H12912] [H12913] [H12914] [H12915] [H12916] [H12917] [H12918]
    -**
     */
    -void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
    +SQLITE_API void sqlite3_progress_handler(sqlite3*, int, int(*)(void*), void*);
     
     /*
    -** CAPI3REF: Opening A New Database Connection {H12700} 
    +** CAPI3REF: Opening A New Database Connection
     **
    -** These routines open an SQLite database file whose name is given by the
    -** filename argument. The filename argument is interpreted as UTF-8 for
    +** ^These routines open an SQLite database file whose name is given by the
    +** filename argument. ^The filename argument is interpreted as UTF-8 for
     ** sqlite3_open() and sqlite3_open_v2() and as UTF-16 in the native byte
    -** order for sqlite3_open16(). A [database connection] handle is usually
    +** order for sqlite3_open16(). ^(A [database connection] handle is usually
     ** returned in *ppDb, even if an error occurs.  The only exception is that
     ** if SQLite is unable to allocate memory to hold the [sqlite3] object,
     ** a NULL will be written into *ppDb instead of a pointer to the [sqlite3]
    -** object. If the database is opened (and/or created) successfully, then
    -** [SQLITE_OK] is returned.  Otherwise an [error code] is returned.  The
    +** object.)^ ^(If the database is opened (and/or created) successfully, then
    +** [SQLITE_OK] is returned.  Otherwise an [error code] is returned.)^ ^The
     ** [sqlite3_errmsg()] or [sqlite3_errmsg16()] routines can be used to obtain
    -** an English language description of the error.
    +** an English language description of the error following a failure of any
    +** of the sqlite3_open() routines.
     **
    -** The default encoding for the database will be UTF-8 if
    +** ^The default encoding for the database will be UTF-8 if
     ** sqlite3_open() or sqlite3_open_v2() is called and
     ** UTF-16 in the native byte order if sqlite3_open16() is used.
     **
    @@ -2030,53 +2107,61 @@
     **
     ** The sqlite3_open_v2() interface works like sqlite3_open()
     ** except that it accepts two additional parameters for additional control
    -** over the new database connection.  The flags parameter can take one of
    +** over the new database connection.  ^(The flags parameter to
    +** sqlite3_open_v2() can take one of
     ** the following three values, optionally combined with the 
    -** [SQLITE_OPEN_NOMUTEX] or [SQLITE_OPEN_FULLMUTEX] flags:
    +** [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], [SQLITE_OPEN_SHAREDCACHE],
    +** and/or [SQLITE_OPEN_PRIVATECACHE] flags:)^
     **
     ** 
    -**
    [SQLITE_OPEN_READONLY]
    +** ^(
    [SQLITE_OPEN_READONLY]
    **
    The database is opened in read-only mode. If the database does not -** already exist, an error is returned.
    +** already exist, an error is returned.)^ ** -**
    [SQLITE_OPEN_READWRITE]
    +** ^(
    [SQLITE_OPEN_READWRITE]
    **
    The database is opened for reading and writing if possible, or reading ** only if the file is write protected by the operating system. In either -** case the database must already exist, otherwise an error is returned.
    +** case the database must already exist, otherwise an error is returned.)^ ** -**
    [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
    +** ^(
    [SQLITE_OPEN_READWRITE] | [SQLITE_OPEN_CREATE]
    **
    The database is opened for reading and writing, and is creates it if ** it does not already exist. This is the behavior that is always used for -** sqlite3_open() and sqlite3_open16().
    +** sqlite3_open() and sqlite3_open16().)^ **
    ** ** If the 3rd parameter to sqlite3_open_v2() is not one of the ** combinations shown above or one of the combinations shown above combined -** with the [SQLITE_OPEN_NOMUTEX] or [SQLITE_OPEN_FULLMUTEX] flags, +** with the [SQLITE_OPEN_NOMUTEX], [SQLITE_OPEN_FULLMUTEX], +** [SQLITE_OPEN_SHAREDCACHE] and/or [SQLITE_OPEN_SHAREDCACHE] flags, ** then the behavior is undefined. ** -** If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection +** ^If the [SQLITE_OPEN_NOMUTEX] flag is set, then the database connection ** opens in the multi-thread [threading mode] as long as the single-thread -** mode has not been set at compile-time or start-time. If the +** mode has not been set at compile-time or start-time. ^If the ** [SQLITE_OPEN_FULLMUTEX] flag is set then the database connection opens ** in the serialized [threading mode] unless single-thread was ** previously selected at compile-time or start-time. +** ^The [SQLITE_OPEN_SHAREDCACHE] flag causes the database connection to be +** eligible to use [shared cache mode], regardless of whether or not shared +** cache is enabled using [sqlite3_enable_shared_cache()]. ^The +** [SQLITE_OPEN_PRIVATECACHE] flag causes the database connection to not +** participate in [shared cache mode] even if it is enabled. ** -** If the filename is ":memory:", then a private, temporary in-memory database -** is created for the connection. This in-memory database will vanish when +** ^If the filename is ":memory:", then a private, temporary in-memory database +** is created for the connection. ^This in-memory database will vanish when ** the database connection is closed. Future versions of SQLite might ** make use of additional special filenames that begin with the ":" character. ** It is recommended that when a database filename actually does begin with ** a ":" character you should prefix the filename with a pathname such as ** "./" to avoid ambiguity. ** -** If the filename is an empty string, then a private, temporary -** on-disk database will be created. This private database will be +** ^If the filename is an empty string, then a private, temporary +** on-disk database will be created. ^This private database will be ** automatically deleted as soon as the database connection is closed. ** -** The fourth parameter to sqlite3_open_v2() is the name of the +** ^The fourth parameter to sqlite3_open_v2() is the name of the ** [sqlite3_vfs] object that defines the operating system interface that -** the new database connection should use. If the fourth parameter is +** the new database connection should use. ^If the fourth parameter is ** a NULL pointer then the default [sqlite3_vfs] object is used. ** ** Note to Windows users: The encoding used for the filename argument @@ -2084,20 +2169,16 @@ ** codepage is currently defined. Filenames containing international ** characters must be converted to UTF-8 prior to passing them into ** sqlite3_open() or sqlite3_open_v2(). -** -** Requirements: -** [H12701] [H12702] [H12703] [H12704] [H12706] [H12707] [H12709] [H12711] -** [H12712] [H12713] [H12714] [H12717] [H12719] [H12721] [H12723] */ -int sqlite3_open( +SQLITE_API int sqlite3_open( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); -int sqlite3_open16( +SQLITE_API int sqlite3_open16( const void *filename, /* Database filename (UTF-16) */ sqlite3 **ppDb /* OUT: SQLite db handle */ ); -int sqlite3_open_v2( +SQLITE_API int sqlite3_open_v2( const char *filename, /* Database filename (UTF-8) */ sqlite3 **ppDb, /* OUT: SQLite db handle */ int flags, /* Flags */ @@ -2105,23 +2186,23 @@ ); /* -** CAPI3REF: Error Codes And Messages {H12800} +** CAPI3REF: Error Codes And Messages ** -** The sqlite3_errcode() interface returns the numeric [result code] or +** ^The sqlite3_errcode() interface returns the numeric [result code] or ** [extended result code] for the most recent failed sqlite3_* API call ** associated with a [database connection]. If a prior API call failed ** but the most recent API call succeeded, the return value from -** sqlite3_errcode() is undefined. The sqlite3_extended_errcode() +** sqlite3_errcode() is undefined. ^The sqlite3_extended_errcode() ** interface is the same except that it always returns the ** [extended result code] even when extended result codes are ** disabled. ** -** The sqlite3_errmsg() and sqlite3_errmsg16() return English-language +** ^The sqlite3_errmsg() and sqlite3_errmsg16() return English-language ** text that describes the error, as either UTF-8 or UTF-16 respectively. -** Memory to hold the error message string is managed internally. +** ^(Memory to hold the error message string is managed internally. ** The application does not need to worry about freeing the result. ** However, the error string might be overwritten or deallocated by -** subsequent calls to other SQLite interface functions. +** subsequent calls to other SQLite interface functions.)^ ** ** When the serialized [threading mode] is in use, it might be the ** case that a second error occurs on a separate thread in between @@ -2136,17 +2217,14 @@ ** If an interface fails with SQLITE_MISUSE, that means the interface ** was invoked incorrectly by the application. In that case, the ** error code and message may or may not be set. -** -** Requirements: -** [H12801] [H12802] [H12803] [H12807] [H12808] [H12809] */ -int sqlite3_errcode(sqlite3 *db); -int sqlite3_extended_errcode(sqlite3 *db); -const char *sqlite3_errmsg(sqlite3*); -const void *sqlite3_errmsg16(sqlite3*); +SQLITE_API int sqlite3_errcode(sqlite3 *db); +SQLITE_API int sqlite3_extended_errcode(sqlite3 *db); +SQLITE_API const char *sqlite3_errmsg(sqlite3*); +SQLITE_API const void *sqlite3_errmsg16(sqlite3*); /* -** CAPI3REF: SQL Statement Object {H13000} +** CAPI3REF: SQL Statement Object ** KEYWORDS: {prepared statement} {prepared statements} ** ** An instance of this object represents a single SQL statement. @@ -2172,25 +2250,25 @@ typedef struct sqlite3_stmt sqlite3_stmt; /* -** CAPI3REF: Run-time Limits {H12760} +** CAPI3REF: Run-time Limits ** -** This interface allows the size of various constructs to be limited +** ^(This interface allows the size of various constructs to be limited ** on a connection by connection basis. The first parameter is the ** [database connection] whose limit is to be set or queried. The ** second parameter is one of the [limit categories] that define a ** class of constructs to be size limited. The third parameter is the -** new limit for that construct. The function returns the old limit. +** new limit for that construct. The function returns the old limit.)^ ** -** If the new limit is a negative number, the limit is unchanged. -** For the limit category of SQLITE_LIMIT_XYZ there is a +** ^If the new limit is a negative number, the limit is unchanged. +** ^(For the limit category of SQLITE_LIMIT_XYZ there is a ** [limits | hard upper bound] ** set by a compile-time C preprocessor macro named ** [limits | SQLITE_MAX_XYZ]. -** (The "_LIMIT_" in the name is changed to "_MAX_".) -** Attempts to increase a limit above its hard upper bound are -** silently truncated to the hard upper limit. +** (The "_LIMIT_" in the name is changed to "_MAX_".))^ +** ^Attempts to increase a limit above its hard upper bound are +** silently truncated to the hard upper bound. ** -** Run time limits are intended for use in applications that manage +** Run-time limits are intended for use in applications that manage ** both their own internal database and also databases that are controlled ** by untrusted external sources. An example application might be a ** web browser that has its own databases for storing history and @@ -2204,15 +2282,12 @@ ** [max_page_count] [PRAGMA]. ** ** New run-time limit categories may be added in future releases. -** -** Requirements: -** [H12762] [H12766] [H12769] */ -int sqlite3_limit(sqlite3*, int id, int newVal); +SQLITE_API int sqlite3_limit(sqlite3*, int id, int newVal); /* -** CAPI3REF: Run-Time Limit Categories {H12790} -** KEYWORDS: {limit category} {limit categories} +** CAPI3REF: Run-Time Limit Categories +** KEYWORDS: {limit category} {*limit categories} ** ** These constants define various performance limits ** that can be lowered at run-time using [sqlite3_limit()]. @@ -2220,40 +2295,43 @@ ** Additional information is available at [limits | Limits in SQLite]. ** **
    -**
    SQLITE_LIMIT_LENGTH
    -**
    The maximum size of any string or BLOB or table row.
    +** ^(
    SQLITE_LIMIT_LENGTH
    +**
    The maximum size of any string or BLOB or table row.
    )^ ** -**
    SQLITE_LIMIT_SQL_LENGTH
    -**
    The maximum length of an SQL statement.
    +** ^(
    SQLITE_LIMIT_SQL_LENGTH
    +**
    The maximum length of an SQL statement, in bytes.
    )^ ** -**
    SQLITE_LIMIT_COLUMN
    +** ^(
    SQLITE_LIMIT_COLUMN
    **
    The maximum number of columns in a table definition or in the ** result set of a [SELECT] or the maximum number of columns in an index -** or in an ORDER BY or GROUP BY clause.
    +** or in an ORDER BY or GROUP BY clause.)^ ** -**
    SQLITE_LIMIT_EXPR_DEPTH
    -**
    The maximum depth of the parse tree on any expression.
    +** ^(
    SQLITE_LIMIT_EXPR_DEPTH
    +**
    The maximum depth of the parse tree on any expression.
    )^ ** -**
    SQLITE_LIMIT_COMPOUND_SELECT
    -**
    The maximum number of terms in a compound SELECT statement.
    +** ^(
    SQLITE_LIMIT_COMPOUND_SELECT
    +**
    The maximum number of terms in a compound SELECT statement.
    )^ ** -**
    SQLITE_LIMIT_VDBE_OP
    +** ^(
    SQLITE_LIMIT_VDBE_OP
    **
    The maximum number of instructions in a virtual machine program -** used to implement an SQL statement.
    +** used to implement an SQL statement.)^ ** -**
    SQLITE_LIMIT_FUNCTION_ARG
    -**
    The maximum number of arguments on a function.
    +** ^(
    SQLITE_LIMIT_FUNCTION_ARG
    +**
    The maximum number of arguments on a function.
    )^ ** -**
    SQLITE_LIMIT_ATTACHED
    -**
    The maximum number of [ATTACH | attached databases].
    +** ^(
    SQLITE_LIMIT_ATTACHED
    +**
    The maximum number of [ATTACH | attached databases].)^
    ** -**
    SQLITE_LIMIT_LIKE_PATTERN_LENGTH
    +** ^(
    SQLITE_LIMIT_LIKE_PATTERN_LENGTH
    **
    The maximum length of the pattern argument to the [LIKE] or -** [GLOB] operators.
    +** [GLOB] operators.)^ ** -**
    SQLITE_LIMIT_VARIABLE_NUMBER
    +** ^(
    SQLITE_LIMIT_VARIABLE_NUMBER
    **
    The maximum number of variables in an SQL statement that can -** be bound.
    +** be bound.)^ +** +** ^(
    SQLITE_LIMIT_TRIGGER_DEPTH
    +**
    The maximum depth of recursion for triggers.
    )^ **
    */ #define SQLITE_LIMIT_LENGTH 0 @@ -2266,9 +2344,10 @@ #define SQLITE_LIMIT_ATTACHED 7 #define SQLITE_LIMIT_LIKE_PATTERN_LENGTH 8 #define SQLITE_LIMIT_VARIABLE_NUMBER 9 +#define SQLITE_LIMIT_TRIGGER_DEPTH 10 /* -** CAPI3REF: Compiling An SQL Statement {H13010} +** CAPI3REF: Compiling An SQL Statement ** KEYWORDS: {SQL statement compiler} ** ** To execute an SQL query, it must first be compiled into a byte-code @@ -2283,9 +2362,9 @@ ** interfaces use UTF-8, and sqlite3_prepare16() and sqlite3_prepare16_v2() ** use UTF-16. ** -** If the nByte argument is less than zero, then zSql is read up to the -** first zero terminator. If nByte is non-negative, then it is the maximum -** number of bytes read from zSql. When nByte is non-negative, the +** ^If the nByte argument is less than zero, then zSql is read up to the +** first zero terminator. ^If nByte is non-negative, then it is the maximum +** number of bytes read from zSql. ^When nByte is non-negative, the ** zSql string ends at either the first '\000' or '\u0000' character or ** the nByte-th byte, whichever comes first. If the caller knows ** that the supplied string is nul-terminated, then there is a small @@ -2293,34 +2372,35 @@ ** is equal to the number of bytes in the input string including ** the nul-terminator bytes. ** -** If pzTail is not NULL then *pzTail is made to point to the first byte +** ^If pzTail is not NULL then *pzTail is made to point to the first byte ** past the end of the first SQL statement in zSql. These routines only ** compile the first statement in zSql, so *pzTail is left pointing to ** what remains uncompiled. ** -** *ppStmt is left pointing to a compiled [prepared statement] that can be -** executed using [sqlite3_step()]. If there is an error, *ppStmt is set -** to NULL. If the input text contains no SQL (if the input is an empty +** ^*ppStmt is left pointing to a compiled [prepared statement] that can be +** executed using [sqlite3_step()]. ^If there is an error, *ppStmt is set +** to NULL. ^If the input text contains no SQL (if the input is an empty ** string or a comment) then *ppStmt is set to NULL. ** The calling procedure is responsible for deleting the compiled ** SQL statement using [sqlite3_finalize()] after it has finished with it. ** ppStmt may not be NULL. ** -** On success, [SQLITE_OK] is returned, otherwise an [error code] is returned. +** ^On success, the sqlite3_prepare() family of routines return [SQLITE_OK]; +** otherwise an [error code] is returned. ** ** The sqlite3_prepare_v2() and sqlite3_prepare16_v2() interfaces are ** recommended for all new programs. The two older interfaces are retained ** for backwards compatibility, but their use is discouraged. -** In the "v2" interfaces, the prepared statement +** ^In the "v2" interfaces, the prepared statement ** that is returned (the [sqlite3_stmt] object) contains a copy of the ** original SQL text. This causes the [sqlite3_step()] interface to -** behave a differently in two ways: +** behave differently in three ways: ** **
      **
    1. -** If the database schema changes, instead of returning [SQLITE_SCHEMA] as it +** ^If the database schema changes, instead of returning [SQLITE_SCHEMA] as it ** always used to do, [sqlite3_step()] will automatically recompile the SQL -** statement and try to run it again. If the schema has changed in +** statement and try to run it again. ^If the schema has changed in ** a way that makes the statement no longer valid, [sqlite3_step()] will still ** return [SQLITE_SCHEMA]. But unlike the legacy behavior, [SQLITE_SCHEMA] is ** now a fatal error. Calling [sqlite3_prepare_v2()] again will not make the @@ -2329,41 +2409,45 @@ **
    2. ** **
    3. -** When an error occurs, [sqlite3_step()] will return one of the detailed -** [error codes] or [extended error codes]. The legacy behavior was that +** ^When an error occurs, [sqlite3_step()] will return one of the detailed +** [error codes] or [extended error codes]. ^The legacy behavior was that ** [sqlite3_step()] would only return a generic [SQLITE_ERROR] result code -** and you would have to make a second call to [sqlite3_reset()] in order -** to find the underlying cause of the problem. With the "v2" prepare +** and the application would have to make a second call to [sqlite3_reset()] +** in order to find the underlying cause of the problem. With the "v2" prepare ** interfaces, the underlying reason for the error is returned immediately. **
    4. -**
    -** -** Requirements: -** [H13011] [H13012] [H13013] [H13014] [H13015] [H13016] [H13019] [H13021] ** +**
  • +** ^If the value of a [parameter | host parameter] in the WHERE clause might +** change the query plan for a statement, then the statement may be +** automatically recompiled (as if there had been a schema change) on the first +** [sqlite3_step()] call following any change to the +** [sqlite3_bind_text | bindings] of the [parameter]. +**
  • +** */ -int sqlite3_prepare( +SQLITE_API int sqlite3_prepare( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); -int sqlite3_prepare_v2( +SQLITE_API int sqlite3_prepare_v2( sqlite3 *db, /* Database handle */ const char *zSql, /* SQL statement, UTF-8 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const char **pzTail /* OUT: Pointer to unused portion of zSql */ ); -int sqlite3_prepare16( +SQLITE_API int sqlite3_prepare16( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ sqlite3_stmt **ppStmt, /* OUT: Statement handle */ const void **pzTail /* OUT: Pointer to unused portion of zSql */ ); -int sqlite3_prepare16_v2( +SQLITE_API int sqlite3_prepare16_v2( sqlite3 *db, /* Database handle */ const void *zSql, /* SQL statement, UTF-16 encoded */ int nByte, /* Maximum length of zSql in bytes. */ @@ -2372,24 +2456,21 @@ ); /* -** CAPI3REF: Retrieving Statement SQL {H13100} +** CAPI3REF: Retrieving Statement SQL ** -** This interface can be used to retrieve a saved copy of the original +** ^This interface can be used to retrieve a saved copy of the original ** SQL text used to create a [prepared statement] if that statement was ** compiled using either [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()]. -** -** Requirements: -** [H13101] [H13102] [H13103] */ -const char *sqlite3_sql(sqlite3_stmt *pStmt); +SQLITE_API const char *sqlite3_sql(sqlite3_stmt *pStmt); /* -** CAPI3REF: Dynamically Typed Value Object {H15000} +** CAPI3REF: Dynamically Typed Value Object ** KEYWORDS: {protected sqlite3_value} {unprotected sqlite3_value} ** ** SQLite uses the sqlite3_value object to represent all values ** that can be stored in a database table. SQLite uses dynamic typing -** for the values it stores. Values stored in sqlite3_value objects +** for the values it stores. ^Values stored in sqlite3_value objects ** can be integers, floating point values, strings, BLOBs, or NULL. ** ** An sqlite3_value object may be either "protected" or "unprotected". @@ -2411,9 +2492,9 @@ ** still make the distinction between between protected and unprotected ** sqlite3_value objects even when not strictly required. ** -** The sqlite3_value objects that are passed as parameters into the +** ^The sqlite3_value objects that are passed as parameters into the ** implementation of [application-defined SQL functions] are protected. -** The sqlite3_value object returned by +** ^The sqlite3_value object returned by ** [sqlite3_column_value()] is unprotected. ** Unprotected sqlite3_value objects may only be used with ** [sqlite3_result_value()] and [sqlite3_bind_value()]. @@ -2423,10 +2504,10 @@ typedef struct Mem sqlite3_value; /* -** CAPI3REF: SQL Function Context Object {H16001} +** CAPI3REF: SQL Function Context Object ** ** The context in which an SQL function executes is stored in an -** sqlite3_context object. A pointer to an sqlite3_context object +** sqlite3_context object. ^A pointer to an sqlite3_context object ** is always first parameter to [application-defined SQL functions]. ** The application-defined SQL function implementation will pass this ** pointer through into calls to [sqlite3_result_int | sqlite3_result()], @@ -2437,12 +2518,13 @@ typedef struct sqlite3_context sqlite3_context; /* -** CAPI3REF: Binding Values To Prepared Statements {H13500} +** CAPI3REF: Binding Values To Prepared Statements ** KEYWORDS: {host parameter} {host parameters} {host parameter name} ** KEYWORDS: {SQL parameter} {SQL parameters} {parameter binding} ** -** In the SQL strings input to [sqlite3_prepare_v2()] and its variants, -** literals may be replaced by a [parameter] in one of these forms: +** ^(In the SQL statement text input to [sqlite3_prepare_v2()] and its variants, +** literals may be replaced by a [parameter] that matches one of following +** templates: ** **
      **
    • ? @@ -2452,124 +2534,115 @@ **
    • $VVV **
    ** -** In the parameter forms shown above NNN is an integer literal, -** and VVV is an alpha-numeric parameter name. The values of these +** In the templates above, NNN represents an integer literal, +** and VVV represents an alphanumeric identifer.)^ ^The values of these ** parameters (also called "host parameter names" or "SQL parameters") ** can be set using the sqlite3_bind_*() routines defined here. ** -** The first argument to the sqlite3_bind_*() routines is always +** ^The first argument to the sqlite3_bind_*() routines is always ** a pointer to the [sqlite3_stmt] object returned from ** [sqlite3_prepare_v2()] or its variants. ** -** The second argument is the index of the SQL parameter to be set. -** The leftmost SQL parameter has an index of 1. When the same named +** ^The second argument is the index of the SQL parameter to be set. +** ^The leftmost SQL parameter has an index of 1. ^When the same named ** SQL parameter is used more than once, second and subsequent ** occurrences have the same index as the first occurrence. -** The index for named parameters can be looked up using the -** [sqlite3_bind_parameter_index()] API if desired. The index +** ^The index for named parameters can be looked up using the +** [sqlite3_bind_parameter_index()] API if desired. ^The index ** for "?NNN" parameters is the value of NNN. -** The NNN value must be between 1 and the [sqlite3_limit()] +** ^The NNN value must be between 1 and the [sqlite3_limit()] ** parameter [SQLITE_LIMIT_VARIABLE_NUMBER] (default value: 999). ** -** The third argument is the value to bind to the parameter. +** ^The third argument is the value to bind to the parameter. ** -** In those routines that have a fourth argument, its value is the +** ^(In those routines that have a fourth argument, its value is the ** number of bytes in the parameter. To be clear: the value is the -** number of bytes in the value, not the number of characters. -** If the fourth parameter is negative, the length of the string is +** number of bytes in the value, not the number of characters.)^ +** ^If the fourth parameter is negative, the length of the string is ** the number of bytes up to the first zero terminator. ** -** The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and +** ^The fifth argument to sqlite3_bind_blob(), sqlite3_bind_text(), and ** sqlite3_bind_text16() is a destructor used to dispose of the BLOB or -** string after SQLite has finished with it. If the fifth argument is +** string after SQLite has finished with it. ^If the fifth argument is ** the special value [SQLITE_STATIC], then SQLite assumes that the ** information is in static, unmanaged space and does not need to be freed. -** If the fifth argument has the value [SQLITE_TRANSIENT], then +** ^If the fifth argument has the value [SQLITE_TRANSIENT], then ** SQLite makes its own private copy of the data immediately, before ** the sqlite3_bind_*() routine returns. ** -** The sqlite3_bind_zeroblob() routine binds a BLOB of length N that -** is filled with zeroes. A zeroblob uses a fixed amount of memory +** ^The sqlite3_bind_zeroblob() routine binds a BLOB of length N that +** is filled with zeroes. ^A zeroblob uses a fixed amount of memory ** (just an integer to hold its size) while it is being processed. ** Zeroblobs are intended to serve as placeholders for BLOBs whose ** content is later written using ** [sqlite3_blob_open | incremental BLOB I/O] routines. -** A negative value for the zeroblob results in a zero-length BLOB. +** ^A negative value for the zeroblob results in a zero-length BLOB. ** -** The sqlite3_bind_*() routines must be called after -** [sqlite3_prepare_v2()] (and its variants) or [sqlite3_reset()] and -** before [sqlite3_step()]. -** Bindings are not cleared by the [sqlite3_reset()] routine. -** Unbound parameters are interpreted as NULL. -** -** These routines return [SQLITE_OK] on success or an error code if -** anything goes wrong. [SQLITE_RANGE] is returned if the parameter -** index is out of range. [SQLITE_NOMEM] is returned if malloc() fails. -** [SQLITE_MISUSE] might be returned if these routines are called on a -** virtual machine that is the wrong state or which has already been finalized. -** Detection of misuse is unreliable. Applications should not depend -** on SQLITE_MISUSE returns. SQLITE_MISUSE is intended to indicate a -** a logic error in the application. Future versions of SQLite might -** panic rather than return SQLITE_MISUSE. +** ^If any of the sqlite3_bind_*() routines are called with a NULL pointer +** for the [prepared statement] or with a prepared statement for which +** [sqlite3_step()] has been called more recently than [sqlite3_reset()], +** then the call will return [SQLITE_MISUSE]. If any sqlite3_bind_() +** routine is passed a [prepared statement] that has been finalized, the +** result is undefined and probably harmful. +** +** ^Bindings are not cleared by the [sqlite3_reset()] routine. +** ^Unbound parameters are interpreted as NULL. +** +** ^The sqlite3_bind_* routines return [SQLITE_OK] on success or an +** [error code] if anything goes wrong. +** ^[SQLITE_RANGE] is returned if the parameter +** index is out of range. ^[SQLITE_NOMEM] is returned if malloc() fails. ** ** See also: [sqlite3_bind_parameter_count()], ** [sqlite3_bind_parameter_name()], and [sqlite3_bind_parameter_index()]. -** -** Requirements: -** [H13506] [H13509] [H13512] [H13515] [H13518] [H13521] [H13524] [H13527] -** [H13530] [H13533] [H13536] [H13539] [H13542] [H13545] [H13548] [H13551] -** */ -int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); -int sqlite3_bind_double(sqlite3_stmt*, int, double); -int sqlite3_bind_int(sqlite3_stmt*, int, int); -int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); -int sqlite3_bind_null(sqlite3_stmt*, int); -int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*)); -int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); -int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); -int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); +SQLITE_API int sqlite3_bind_blob(sqlite3_stmt*, int, const void*, int n, void(*)(void*)); +SQLITE_API int sqlite3_bind_double(sqlite3_stmt*, int, double); +SQLITE_API int sqlite3_bind_int(sqlite3_stmt*, int, int); +SQLITE_API int sqlite3_bind_int64(sqlite3_stmt*, int, sqlite3_int64); +SQLITE_API int sqlite3_bind_null(sqlite3_stmt*, int); +SQLITE_API int sqlite3_bind_text(sqlite3_stmt*, int, const char*, int n, void(*)(void*)); +SQLITE_API int sqlite3_bind_text16(sqlite3_stmt*, int, const void*, int, void(*)(void*)); +SQLITE_API int sqlite3_bind_value(sqlite3_stmt*, int, const sqlite3_value*); +SQLITE_API int sqlite3_bind_zeroblob(sqlite3_stmt*, int, int n); /* -** CAPI3REF: Number Of SQL Parameters {H13600} +** CAPI3REF: Number Of SQL Parameters ** -** This routine can be used to find the number of [SQL parameters] +** ^This routine can be used to find the number of [SQL parameters] ** in a [prepared statement]. SQL parameters are tokens of the ** form "?", "?NNN", ":AAA", "$AAA", or "@AAA" that serve as ** placeholders for values that are [sqlite3_bind_blob | bound] ** to the parameters at a later time. ** -** This routine actually returns the index of the largest (rightmost) +** ^(This routine actually returns the index of the largest (rightmost) ** parameter. For all forms except ?NNN, this will correspond to the -** number of unique parameters. If parameters of the ?NNN are used, -** there may be gaps in the list. +** number of unique parameters. If parameters of the ?NNN form are used, +** there may be gaps in the list.)^ ** ** See also: [sqlite3_bind_blob|sqlite3_bind()], ** [sqlite3_bind_parameter_name()], and ** [sqlite3_bind_parameter_index()]. -** -** Requirements: -** [H13601] */ -int sqlite3_bind_parameter_count(sqlite3_stmt*); +SQLITE_API int sqlite3_bind_parameter_count(sqlite3_stmt*); /* -** CAPI3REF: Name Of A Host Parameter {H13620} +** CAPI3REF: Name Of A Host Parameter ** -** This routine returns a pointer to the name of the n-th -** [SQL parameter] in a [prepared statement]. -** SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA" +** ^The sqlite3_bind_parameter_name(P,N) interface returns +** the name of the N-th [SQL parameter] in the [prepared statement] P. +** ^(SQL parameters of the form "?NNN" or ":AAA" or "@AAA" or "$AAA" ** have a name which is the string "?NNN" or ":AAA" or "@AAA" or "$AAA" ** respectively. ** In other words, the initial ":" or "$" or "@" or "?" -** is included as part of the name. -** Parameters of the form "?" without a following integer have no name -** and are also referred to as "anonymous parameters". +** is included as part of the name.)^ +** ^Parameters of the form "?" without a following integer have no name +** and are referred to as "nameless" or "anonymous parameters". ** -** The first host parameter has an index of 1, not 0. +** ^The first host parameter has an index of 1, not 0. ** -** If the value n is out of range or if the n-th parameter is -** nameless, then NULL is returned. The returned string is +** ^If the value N is out of range or if the N-th parameter is +** nameless, then NULL is returned. ^The returned string is ** always in UTF-8 encoding even if the named parameter was ** originally specified as UTF-16 in [sqlite3_prepare16()] or ** [sqlite3_prepare16_v2()]. @@ -2577,149 +2650,132 @@ ** See also: [sqlite3_bind_blob|sqlite3_bind()], ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. -** -** Requirements: -** [H13621] */ -const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); +SQLITE_API const char *sqlite3_bind_parameter_name(sqlite3_stmt*, int); /* -** CAPI3REF: Index Of A Parameter With A Given Name {H13640} +** CAPI3REF: Index Of A Parameter With A Given Name ** -** Return the index of an SQL parameter given its name. The +** ^Return the index of an SQL parameter given its name. ^The ** index value returned is suitable for use as the second -** parameter to [sqlite3_bind_blob|sqlite3_bind()]. A zero -** is returned if no matching parameter is found. The parameter +** parameter to [sqlite3_bind_blob|sqlite3_bind()]. ^A zero +** is returned if no matching parameter is found. ^The parameter ** name must be given in UTF-8 even if the original statement ** was prepared from UTF-16 text using [sqlite3_prepare16_v2()]. ** ** See also: [sqlite3_bind_blob|sqlite3_bind()], ** [sqlite3_bind_parameter_count()], and ** [sqlite3_bind_parameter_index()]. -** -** Requirements: -** [H13641] */ -int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); +SQLITE_API int sqlite3_bind_parameter_index(sqlite3_stmt*, const char *zName); /* -** CAPI3REF: Reset All Bindings On A Prepared Statement {H13660} +** CAPI3REF: Reset All Bindings On A Prepared Statement ** -** Contrary to the intuition of many, [sqlite3_reset()] does not reset +** ^Contrary to the intuition of many, [sqlite3_reset()] does not reset ** the [sqlite3_bind_blob | bindings] on a [prepared statement]. -** Use this routine to reset all host parameters to NULL. -** -** Requirements: -** [H13661] +** ^Use this routine to reset all host parameters to NULL. */ -int sqlite3_clear_bindings(sqlite3_stmt*); +SQLITE_API int sqlite3_clear_bindings(sqlite3_stmt*); /* -** CAPI3REF: Number Of Columns In A Result Set {H13710} +** CAPI3REF: Number Of Columns In A Result Set ** -** Return the number of columns in the result set returned by the -** [prepared statement]. This routine returns 0 if pStmt is an SQL +** ^Return the number of columns in the result set returned by the +** [prepared statement]. ^This routine returns 0 if pStmt is an SQL ** statement that does not return data (for example an [UPDATE]). -** -** Requirements: -** [H13711] */ -int sqlite3_column_count(sqlite3_stmt *pStmt); +SQLITE_API int sqlite3_column_count(sqlite3_stmt *pStmt); /* -** CAPI3REF: Column Names In A Result Set {H13720} +** CAPI3REF: Column Names In A Result Set ** -** These routines return the name assigned to a particular column -** in the result set of a [SELECT] statement. The sqlite3_column_name() +** ^These routines return the name assigned to a particular column +** in the result set of a [SELECT] statement. ^The sqlite3_column_name() ** interface returns a pointer to a zero-terminated UTF-8 string ** and sqlite3_column_name16() returns a pointer to a zero-terminated -** UTF-16 string. The first parameter is the [prepared statement] -** that implements the [SELECT] statement. The second parameter is the -** column number. The leftmost column is number 0. +** UTF-16 string. ^The first parameter is the [prepared statement] +** that implements the [SELECT] statement. ^The second parameter is the +** column number. ^The leftmost column is number 0. ** -** The returned string pointer is valid until either the [prepared statement] +** ^The returned string pointer is valid until either the [prepared statement] ** is destroyed by [sqlite3_finalize()] or until the next call to ** sqlite3_column_name() or sqlite3_column_name16() on the same column. ** -** If sqlite3_malloc() fails during the processing of either routine +** ^If sqlite3_malloc() fails during the processing of either routine ** (for example during a conversion from UTF-8 to UTF-16) then a ** NULL pointer is returned. ** -** The name of a result column is the value of the "AS" clause for +** ^The name of a result column is the value of the "AS" clause for ** that column, if there is an AS clause. If there is no AS clause ** then the name of the column is unspecified and may change from ** one release of SQLite to the next. -** -** Requirements: -** [H13721] [H13723] [H13724] [H13725] [H13726] [H13727] */ -const char *sqlite3_column_name(sqlite3_stmt*, int N); -const void *sqlite3_column_name16(sqlite3_stmt*, int N); +SQLITE_API const char *sqlite3_column_name(sqlite3_stmt*, int N); +SQLITE_API const void *sqlite3_column_name16(sqlite3_stmt*, int N); /* -** CAPI3REF: Source Of Data In A Query Result {H13740} +** CAPI3REF: Source Of Data In A Query Result ** -** These routines provide a means to determine what column of what -** table in which database a result of a [SELECT] statement comes from. -** The name of the database or table or column can be returned as -** either a UTF-8 or UTF-16 string. The _database_ routines return +** ^These routines provide a means to determine the database, table, and +** table column that is the origin of a particular result column in +** [SELECT] statement. +** ^The name of the database or table or column can be returned as +** either a UTF-8 or UTF-16 string. ^The _database_ routines return ** the database name, the _table_ routines return the table name, and ** the origin_ routines return the column name. -** The returned string is valid until the [prepared statement] is destroyed +** ^The returned string is valid until the [prepared statement] is destroyed ** using [sqlite3_finalize()] or until the same information is requested ** again in a different encoding. ** -** The names returned are the original un-aliased names of the +** ^The names returned are the original un-aliased names of the ** database, table, and column. ** -** The first argument to the following calls is a [prepared statement]. -** These functions return information about the Nth column returned by +** ^The first argument to these interfaces is a [prepared statement]. +** ^These functions return information about the Nth result column returned by ** the statement, where N is the second function argument. +** ^The left-most column is column 0 for these routines. ** -** If the Nth column returned by the statement is an expression or +** ^If the Nth column returned by the statement is an expression or ** subquery and is not a column value, then all of these functions return -** NULL. These routine might also return NULL if a memory allocation error -** occurs. Otherwise, they return the name of the attached database, table -** and column that query result column was extracted from. +** NULL. ^These routine might also return NULL if a memory allocation error +** occurs. ^Otherwise, they return the name of the attached database, table, +** or column that query result column was extracted from. ** -** As with all other SQLite APIs, those postfixed with "16" return -** UTF-16 encoded strings, the other functions return UTF-8. {END} +** ^As with all other SQLite APIs, those whose names end with "16" return +** UTF-16 encoded strings and the other functions return UTF-8. ** -** These APIs are only available if the library was compiled with the -** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined. +** ^These APIs are only available if the library was compiled with the +** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol. ** -** {A13751} ** If two or more threads call one or more of these routines against the same ** prepared statement and column at the same time then the results are ** undefined. ** -** Requirements: -** [H13741] [H13742] [H13743] [H13744] [H13745] [H13746] [H13748] -** ** If two or more threads call one or more ** [sqlite3_column_database_name | column metadata interfaces] ** for the same [prepared statement] and result column ** at the same time then the results are undefined. */ -const char *sqlite3_column_database_name(sqlite3_stmt*,int); -const void *sqlite3_column_database_name16(sqlite3_stmt*,int); -const char *sqlite3_column_table_name(sqlite3_stmt*,int); -const void *sqlite3_column_table_name16(sqlite3_stmt*,int); -const char *sqlite3_column_origin_name(sqlite3_stmt*,int); -const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); +SQLITE_API const char *sqlite3_column_database_name(sqlite3_stmt*,int); +SQLITE_API const void *sqlite3_column_database_name16(sqlite3_stmt*,int); +SQLITE_API const char *sqlite3_column_table_name(sqlite3_stmt*,int); +SQLITE_API const void *sqlite3_column_table_name16(sqlite3_stmt*,int); +SQLITE_API const char *sqlite3_column_origin_name(sqlite3_stmt*,int); +SQLITE_API const void *sqlite3_column_origin_name16(sqlite3_stmt*,int); /* -** CAPI3REF: Declared Datatype Of A Query Result {H13760} +** CAPI3REF: Declared Datatype Of A Query Result ** -** The first parameter is a [prepared statement]. +** ^(The first parameter is a [prepared statement]. ** If this statement is a [SELECT] statement and the Nth column of the ** returned result set of that [SELECT] is a table column (not an ** expression or subquery) then the declared type of the table -** column is returned. If the Nth column of the result set is an +** column is returned.)^ ^If the Nth column of the result set is an ** expression or subquery, then a NULL pointer is returned. -** The returned string is always UTF-8 encoded. {END} +** ^The returned string is always UTF-8 encoded. ** -** For example, given the database schema: +** ^(For example, given the database schema: ** ** CREATE TABLE t1(c1 VARIANT); ** @@ -2728,23 +2784,20 @@ ** SELECT c1 + 1, c1 FROM t1; ** ** this routine would return the string "VARIANT" for the second result -** column (i==1), and a NULL pointer for the first result column (i==0). +** column (i==1), and a NULL pointer for the first result column (i==0).)^ ** -** SQLite uses dynamic run-time typing. So just because a column +** ^SQLite uses dynamic run-time typing. ^So just because a column ** is declared to contain a particular type does not mean that the ** data stored in that column is of the declared type. SQLite is -** strongly typed, but the typing is dynamic not static. Type +** strongly typed, but the typing is dynamic not static. ^Type ** is associated with individual values, not with the containers ** used to hold those values. -** -** Requirements: -** [H13761] [H13762] [H13763] */ -const char *sqlite3_column_decltype(sqlite3_stmt*,int); -const void *sqlite3_column_decltype16(sqlite3_stmt*,int); +SQLITE_API const char *sqlite3_column_decltype(sqlite3_stmt*,int); +SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int); /* -** CAPI3REF: Evaluate An SQL Statement {H13200} +** CAPI3REF: Evaluate An SQL Statement ** ** After a [prepared statement] has been prepared using either ** [sqlite3_prepare_v2()] or [sqlite3_prepare16_v2()] or one of the legacy @@ -2758,35 +2811,35 @@ ** new "v2" interface is recommended for new applications but the legacy ** interface will continue to be supported. ** -** In the legacy interface, the return value will be either [SQLITE_BUSY], +** ^In the legacy interface, the return value will be either [SQLITE_BUSY], ** [SQLITE_DONE], [SQLITE_ROW], [SQLITE_ERROR], or [SQLITE_MISUSE]. -** With the "v2" interface, any of the other [result codes] or +** ^With the "v2" interface, any of the other [result codes] or ** [extended result codes] might be returned as well. ** -** [SQLITE_BUSY] means that the database engine was unable to acquire the -** database locks it needs to do its job. If the statement is a [COMMIT] +** ^[SQLITE_BUSY] means that the database engine was unable to acquire the +** database locks it needs to do its job. ^If the statement is a [COMMIT] ** or occurs outside of an explicit transaction, then you can retry the ** statement. If the statement is not a [COMMIT] and occurs within a ** explicit transaction then you should rollback the transaction before ** continuing. ** -** [SQLITE_DONE] means that the statement has finished executing +** ^[SQLITE_DONE] means that the statement has finished executing ** successfully. sqlite3_step() should not be called again on this virtual ** machine without first calling [sqlite3_reset()] to reset the virtual ** machine back to its initial state. ** -** If the SQL statement being executed returns any data, then [SQLITE_ROW] +** ^If the SQL statement being executed returns any data, then [SQLITE_ROW] ** is returned each time a new row of data is ready for processing by the ** caller. The values may be accessed using the [column access functions]. ** sqlite3_step() is called again to retrieve the next row of data. ** -** [SQLITE_ERROR] means that a run-time error (such as a constraint +** ^[SQLITE_ERROR] means that a run-time error (such as a constraint ** violation) has occurred. sqlite3_step() should not be called again on ** the VM. More information may be found by calling [sqlite3_errmsg()]. -** With the legacy interface, a more specific error code (for example, +** ^With the legacy interface, a more specific error code (for example, ** [SQLITE_INTERRUPT], [SQLITE_SCHEMA], [SQLITE_CORRUPT], and so forth) ** can be obtained by calling [sqlite3_reset()] on the -** [prepared statement]. In the "v2" interface, +** [prepared statement]. ^In the "v2" interface, ** the more specific error code is returned directly by sqlite3_step(). ** ** [SQLITE_MISUSE] means that the this routine was called inappropriately. @@ -2807,27 +2860,22 @@ ** of the legacy [sqlite3_prepare()] and [sqlite3_prepare16()] interfaces, ** then the more specific [error codes] are returned directly ** by sqlite3_step(). The use of the "v2" interface is recommended. -** -** Requirements: -** [H13202] [H15304] [H15306] [H15308] [H15310] */ -int sqlite3_step(sqlite3_stmt*); +SQLITE_API int sqlite3_step(sqlite3_stmt*); /* -** CAPI3REF: Number of columns in a result set {H13770} -** -** Returns the number of values in the current row of the result set. +** CAPI3REF: Number of columns in a result set ** -** Requirements: -** [H13771] [H13772] +** ^The sqlite3_data_count(P) the number of columns in the +** of the result set of [prepared statement] P. */ -int sqlite3_data_count(sqlite3_stmt *pStmt); +SQLITE_API int sqlite3_data_count(sqlite3_stmt *pStmt); /* -** CAPI3REF: Fundamental Datatypes {H10265} +** CAPI3REF: Fundamental Datatypes ** KEYWORDS: SQLITE_TEXT ** -** {H10266} Every value in SQLite has one of five fundamental datatypes: +** ^(Every value in SQLite has one of five fundamental datatypes: ** **
      **
    • 64-bit signed integer @@ -2835,7 +2883,7 @@ **
    • string **
    • BLOB **
    • NULL -**
    {END} +** )^ ** ** These constants are codes for each of those types. ** @@ -2856,17 +2904,19 @@ #define SQLITE3_TEXT 3 /* -** CAPI3REF: Result Values From A Query {H13800} +** CAPI3REF: Result Values From A Query ** KEYWORDS: {column access functions} ** -** These routines form the "result set query" interface. +** These routines form the "result set" interface. ** -** These routines return information about a single column of the current -** result row of a query. In every case the first argument is a pointer +** ^These routines return information about a single column of the current +** result row of a query. ^In every case the first argument is a pointer ** to the [prepared statement] that is being evaluated (the [sqlite3_stmt*] ** that was returned from [sqlite3_prepare_v2()] or one of its variants) ** and the second argument is the index of the column for which information -** should be returned. The leftmost column of the result set has the index 0. +** should be returned. ^The leftmost column of the result set has the index 0. +** ^The number of columns in the result can be determined using +** [sqlite3_column_count()]. ** ** If the SQL statement does not currently point to a valid row, or if the ** column index is out of range, the result is undefined. @@ -2880,9 +2930,9 @@ ** are called from a different thread while any of these routines ** are pending, then the results are undefined. ** -** The sqlite3_column_type() routine returns the +** ^The sqlite3_column_type() routine returns the ** [SQLITE_INTEGER | datatype code] for the initial data type -** of the result column. The returned value is one of [SQLITE_INTEGER], +** of the result column. ^The returned value is one of [SQLITE_INTEGER], ** [SQLITE_FLOAT], [SQLITE_TEXT], [SQLITE_BLOB], or [SQLITE_NULL]. The value ** returned by sqlite3_column_type() is only meaningful if no type ** conversions have occurred as described below. After a type conversion, @@ -2890,27 +2940,27 @@ ** versions of SQLite may change the behavior of sqlite3_column_type() ** following a type conversion. ** -** If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() +** ^If the result is a BLOB or UTF-8 string then the sqlite3_column_bytes() ** routine returns the number of bytes in that BLOB or string. -** If the result is a UTF-16 string, then sqlite3_column_bytes() converts +** ^If the result is a UTF-16 string, then sqlite3_column_bytes() converts ** the string to UTF-8 and then returns the number of bytes. -** If the result is a numeric value then sqlite3_column_bytes() uses +** ^If the result is a numeric value then sqlite3_column_bytes() uses ** [sqlite3_snprintf()] to convert that value to a UTF-8 string and returns ** the number of bytes in that string. -** The value returned does not include the zero terminator at the end -** of the string. For clarity: the value returned is the number of +** ^The value returned does not include the zero terminator at the end +** of the string. ^For clarity: the value returned is the number of ** bytes in the string, not the number of characters. ** -** Strings returned by sqlite3_column_text() and sqlite3_column_text16(), -** even empty strings, are always zero terminated. The return +** ^Strings returned by sqlite3_column_text() and sqlite3_column_text16(), +** even empty strings, are always zero terminated. ^The return ** value from sqlite3_column_blob() for a zero-length BLOB is an arbitrary ** pointer, possibly even a NULL pointer. ** -** The sqlite3_column_bytes16() routine is similar to sqlite3_column_bytes() +** ^The sqlite3_column_bytes16() routine is similar to sqlite3_column_bytes() ** but leaves the result in UTF-16 in native byte order instead of UTF-8. -** The zero terminator is not included in this count. +** ^The zero terminator is not included in this count. ** -** The object returned by [sqlite3_column_value()] is an +** ^The object returned by [sqlite3_column_value()] is an ** [unprotected sqlite3_value] object. An unprotected sqlite3_value object ** may only be used with [sqlite3_bind_value()] and [sqlite3_result_value()]. ** If the [unprotected sqlite3_value] object returned by @@ -2918,10 +2968,10 @@ ** to routines like [sqlite3_value_int()], [sqlite3_value_text()], ** or [sqlite3_value_bytes()], then the behavior is undefined. ** -** These routines attempt to convert the value where appropriate. For +** These routines attempt to convert the value where appropriate. ^For ** example, if the internal representation is FLOAT and a text result ** is requested, [sqlite3_snprintf()] is used internally to perform the -** conversion automatically. The following table details the conversions +** conversion automatically. ^(The following table details the conversions ** that are applied: ** **
    @@ -2945,7 +2995,7 @@ ** BLOB FLOAT Convert to TEXT then use atof() ** BLOB TEXT Add a zero terminator if needed ** -**
    +**
    )^ ** ** The table above makes reference to standard C library functions atoi() ** and atof(). SQLite does not really use these functions. It has its @@ -2953,10 +3003,10 @@ ** used in the table for brevity and because they are familiar to most ** C programmers. ** -** Note that when type conversions occur, pointers returned by prior +** ^Note that when type conversions occur, pointers returned by prior ** calls to sqlite3_column_blob(), sqlite3_column_text(), and/or ** sqlite3_column_text16() may be invalidated. -** Type conversions and pointer invalidations might occur +** ^(Type conversions and pointer invalidations might occur ** in the following cases: ** **
      @@ -2969,22 +3019,22 @@ **
    • The initial content is UTF-16 text and sqlite3_column_bytes() or ** sqlite3_column_text() is called. The content must be converted ** to UTF-8.
    • -**
    +** )^ ** -** Conversions between UTF-16be and UTF-16le are always done in place and do +** ^Conversions between UTF-16be and UTF-16le are always done in place and do ** not invalidate a prior pointer, though of course the content of the buffer ** that the prior pointer points to will have been modified. Other kinds ** of conversion are done in place when it is possible, but sometimes they ** are not possible and in those cases prior pointers are invalidated. ** -** The safest and easiest to remember policy is to invoke these routines +** ^(The safest and easiest to remember policy is to invoke these routines ** in one of the following ways: ** **
      **
    • sqlite3_column_text() followed by sqlite3_column_bytes()
    • **
    • sqlite3_column_blob() followed by sqlite3_column_bytes()
    • **
    • sqlite3_column_text16() followed by sqlite3_column_bytes16()
    • -**
    +** )^ ** ** In other words, you should call sqlite3_column_text(), ** sqlite3_column_blob(), or sqlite3_column_text16() first to force the result @@ -2994,108 +3044,101 @@ ** sqlite3_column_bytes16(), and do not mix calls to sqlite3_column_text16() ** with calls to sqlite3_column_bytes(). ** -** The pointers returned are valid until a type conversion occurs as +** ^The pointers returned are valid until a type conversion occurs as ** described above, or until [sqlite3_step()] or [sqlite3_reset()] or -** [sqlite3_finalize()] is called. The memory space used to hold strings +** [sqlite3_finalize()] is called. ^The memory space used to hold strings ** and BLOBs is freed automatically. Do not pass the pointers returned ** [sqlite3_column_blob()], [sqlite3_column_text()], etc. into ** [sqlite3_free()]. ** -** If a memory allocation error occurs during the evaluation of any +** ^(If a memory allocation error occurs during the evaluation of any ** of these routines, a default value is returned. The default value ** is either the integer 0, the floating point number 0.0, or a NULL ** pointer. Subsequent calls to [sqlite3_errcode()] will return -** [SQLITE_NOMEM]. -** -** Requirements: -** [H13803] [H13806] [H13809] [H13812] [H13815] [H13818] [H13821] [H13824] -** [H13827] [H13830] -*/ -const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); -int sqlite3_column_bytes(sqlite3_stmt*, int iCol); -int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); -double sqlite3_column_double(sqlite3_stmt*, int iCol); -int sqlite3_column_int(sqlite3_stmt*, int iCol); -sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); -const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); -const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); -int sqlite3_column_type(sqlite3_stmt*, int iCol); -sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); - -/* -** CAPI3REF: Destroy A Prepared Statement Object {H13300} -** -** The sqlite3_finalize() function is called to delete a [prepared statement]. -** If the statement was executed successfully or not executed at all, then -** SQLITE_OK is returned. If execution of the statement failed then an +** [SQLITE_NOMEM].)^ +*/ +SQLITE_API const void *sqlite3_column_blob(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_bytes(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_bytes16(sqlite3_stmt*, int iCol); +SQLITE_API double sqlite3_column_double(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_int(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_int64 sqlite3_column_int64(sqlite3_stmt*, int iCol); +SQLITE_API const unsigned char *sqlite3_column_text(sqlite3_stmt*, int iCol); +SQLITE_API const void *sqlite3_column_text16(sqlite3_stmt*, int iCol); +SQLITE_API int sqlite3_column_type(sqlite3_stmt*, int iCol); +SQLITE_API sqlite3_value *sqlite3_column_value(sqlite3_stmt*, int iCol); + +/* +** CAPI3REF: Destroy A Prepared Statement Object +** +** ^The sqlite3_finalize() function is called to delete a [prepared statement]. +** ^If the statement was executed successfully or not executed at all, then +** SQLITE_OK is returned. ^If execution of the statement failed then an ** [error code] or [extended error code] is returned. ** -** This routine can be called at any point during the execution of the -** [prepared statement]. If the virtual machine has not +** ^This routine can be called at any point during the execution of the +** [prepared statement]. ^If the virtual machine has not ** completed execution when this routine is called, that is like ** encountering an error or an [sqlite3_interrupt | interrupt]. -** Incomplete updates may be rolled back and transactions canceled, +** ^Incomplete updates may be rolled back and transactions canceled, ** depending on the circumstances, and the ** [error code] returned will be [SQLITE_ABORT]. -** -** Requirements: -** [H11302] [H11304] */ -int sqlite3_finalize(sqlite3_stmt *pStmt); +SQLITE_API int sqlite3_finalize(sqlite3_stmt *pStmt); /* -** CAPI3REF: Reset A Prepared Statement Object {H13330} +** CAPI3REF: Reset A Prepared Statement Object ** ** The sqlite3_reset() function is called to reset a [prepared statement] ** object back to its initial state, ready to be re-executed. -** Any SQL statement variables that had values bound to them using +** ^Any SQL statement variables that had values bound to them using ** the [sqlite3_bind_blob | sqlite3_bind_*() API] retain their values. ** Use [sqlite3_clear_bindings()] to reset the bindings. ** -** {H11332} The [sqlite3_reset(S)] interface resets the [prepared statement] S -** back to the beginning of its program. +** ^The [sqlite3_reset(S)] interface resets the [prepared statement] S +** back to the beginning of its program. ** -** {H11334} If the most recent call to [sqlite3_step(S)] for the -** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], -** or if [sqlite3_step(S)] has never before been called on S, -** then [sqlite3_reset(S)] returns [SQLITE_OK]. +** ^If the most recent call to [sqlite3_step(S)] for the +** [prepared statement] S returned [SQLITE_ROW] or [SQLITE_DONE], +** or if [sqlite3_step(S)] has never before been called on S, +** then [sqlite3_reset(S)] returns [SQLITE_OK]. ** -** {H11336} If the most recent call to [sqlite3_step(S)] for the -** [prepared statement] S indicated an error, then -** [sqlite3_reset(S)] returns an appropriate [error code]. +** ^If the most recent call to [sqlite3_step(S)] for the +** [prepared statement] S indicated an error, then +** [sqlite3_reset(S)] returns an appropriate [error code]. ** -** {H11338} The [sqlite3_reset(S)] interface does not change the values -** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. +** ^The [sqlite3_reset(S)] interface does not change the values +** of any [sqlite3_bind_blob|bindings] on the [prepared statement] S. */ -int sqlite3_reset(sqlite3_stmt *pStmt); +SQLITE_API int sqlite3_reset(sqlite3_stmt *pStmt); /* -** CAPI3REF: Create Or Redefine SQL Functions {H16100} +** CAPI3REF: Create Or Redefine SQL Functions ** KEYWORDS: {function creation routines} ** KEYWORDS: {application-defined SQL function} ** KEYWORDS: {application-defined SQL functions} ** -** These two functions (collectively known as "function creation routines") +** ^These two functions (collectively known as "function creation routines") ** are used to add SQL functions or aggregates or to redefine the behavior ** of existing SQL functions or aggregates. The only difference between the ** two is that the second parameter, the name of the (scalar) function or ** aggregate, is encoded in UTF-8 for sqlite3_create_function() and UTF-16 ** for sqlite3_create_function16(). ** -** The first parameter is the [database connection] to which the SQL -** function is to be added. If a single program uses more than one database -** connection internally, then SQL functions must be added individually to -** each database connection. +** ^The first parameter is the [database connection] to which the SQL +** function is to be added. ^If an application uses more than one database +** connection then application-defined SQL functions must be added +** to each database connection separately. ** ** The second parameter is the name of the SQL function to be created or -** redefined. The length of the name is limited to 255 bytes, exclusive of +** redefined. ^The length of the name is limited to 255 bytes, exclusive of ** the zero-terminator. Note that the name length limit is in bytes, not -** characters. Any attempt to create a function with a longer name +** characters. ^Any attempt to create a function with a longer name ** will result in [SQLITE_ERROR] being returned. ** -** The third parameter (nArg) +** ^The third parameter (nArg) ** is the number of arguments that the SQL function or -** aggregate takes. If this parameter is -1, then the SQL function or +** aggregate takes. ^If this parameter is -1, then the SQL function or ** aggregate may take any number of arguments between 0 and the limit ** set by [sqlite3_limit]([SQLITE_LIMIT_FUNCTION_ARG]). If the third ** parameter is less than -1 or greater than 127 then the behavior is @@ -3105,55 +3148,51 @@ ** [SQLITE_UTF8 | text encoding] this SQL function prefers for ** its parameters. Any SQL function implementation should be able to work ** work with UTF-8, UTF-16le, or UTF-16be. But some implementations may be -** more efficient with one encoding than another. It is allowed to +** more efficient with one encoding than another. ^An application may ** invoke sqlite3_create_function() or sqlite3_create_function16() multiple ** times with the same function but with different values of eTextRep. -** When multiple implementations of the same function are available, SQLite +** ^When multiple implementations of the same function are available, SQLite ** will pick the one that involves the least amount of data conversion. ** If there is only a single implementation which does not care what text ** encoding is used, then the fourth argument should be [SQLITE_ANY]. ** -** The fifth parameter is an arbitrary pointer. The implementation of the -** function can gain access to this pointer using [sqlite3_user_data()]. +** ^(The fifth parameter is an arbitrary pointer. The implementation of the +** function can gain access to this pointer using [sqlite3_user_data()].)^ ** ** The seventh, eighth and ninth parameters, xFunc, xStep and xFinal, are ** pointers to C-language functions that implement the SQL function or -** aggregate. A scalar SQL function requires an implementation of the xFunc -** callback only, NULL pointers should be passed as the xStep and xFinal -** parameters. An aggregate SQL function requires an implementation of xStep -** and xFinal and NULL should be passed for xFunc. To delete an existing +** aggregate. ^A scalar SQL function requires an implementation of the xFunc +** callback only; NULL pointers should be passed as the xStep and xFinal +** parameters. ^An aggregate SQL function requires an implementation of xStep +** and xFinal and NULL should be passed for xFunc. ^To delete an existing ** SQL function or aggregate, pass NULL for all three function callbacks. ** -** It is permitted to register multiple implementations of the same +** ^It is permitted to register multiple implementations of the same ** functions with the same name but with either differing numbers of -** arguments or differing preferred text encodings. SQLite will use -** the implementation most closely matches the way in which the -** SQL function is used. A function implementation with a non-negative +** arguments or differing preferred text encodings. ^SQLite will use +** the implementation that most closely matches the way in which the +** SQL function is used. ^A function implementation with a non-negative ** nArg parameter is a better match than a function implementation with -** a negative nArg. A function where the preferred text encoding +** a negative nArg. ^A function where the preferred text encoding ** matches the database encoding is a better ** match than a function where the encoding is different. -** A function where the encoding difference is between UTF16le and UTF16be +** ^A function where the encoding difference is between UTF16le and UTF16be ** is a closer match than a function where the encoding difference is ** between UTF8 and UTF16. ** -** Built-in functions may be overloaded by new application-defined functions. -** The first application-defined function with a given name overrides all +** ^Built-in functions may be overloaded by new application-defined functions. +** ^The first application-defined function with a given name overrides all ** built-in functions in the same [database connection] with the same name. -** Subsequent application-defined functions of the same name only override +** ^Subsequent application-defined functions of the same name only override ** prior application-defined functions that are an exact match for the ** number of parameters and preferred encoding. ** -** An application-defined function is permitted to call other +** ^An application-defined function is permitted to call other ** SQLite interfaces. However, such calls must not ** close the database connection nor finalize or reset the prepared ** statement in which the function is running. -** -** Requirements: -** [H16103] [H16106] [H16109] [H16112] [H16118] [H16121] [H16127] -** [H16130] [H16133] [H16136] [H16139] [H16142] */ -int sqlite3_create_function( +SQLITE_API int sqlite3_create_function( sqlite3 *db, const char *zFunctionName, int nArg, @@ -3163,7 +3202,7 @@ void (*xStep)(sqlite3_context*,int,sqlite3_value**), void (*xFinal)(sqlite3_context*) ); -int sqlite3_create_function16( +SQLITE_API int sqlite3_create_function16( sqlite3 *db, const void *zFunctionName, int nArg, @@ -3175,7 +3214,7 @@ ); /* -** CAPI3REF: Text Encodings {H10267} +** CAPI3REF: Text Encodings ** ** These constant define integer codes that represent the various ** text encodings supported by SQLite. @@ -3198,16 +3237,16 @@ ** using these functions, we are not going to tell you what they do. */ #ifndef SQLITE_OMIT_DEPRECATED -SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*); -SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*); -SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); -SQLITE_DEPRECATED int sqlite3_global_recover(void); -SQLITE_DEPRECATED void sqlite3_thread_cleanup(void); -SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void*,sqlite3_int64); +SQLITE_API SQLITE_DEPRECATED int sqlite3_aggregate_count(sqlite3_context*); +SQLITE_API SQLITE_DEPRECATED int sqlite3_expired(sqlite3_stmt*); +SQLITE_API SQLITE_DEPRECATED int sqlite3_transfer_bindings(sqlite3_stmt*, sqlite3_stmt*); +SQLITE_API SQLITE_DEPRECATED int sqlite3_global_recover(void); +SQLITE_API SQLITE_DEPRECATED void sqlite3_thread_cleanup(void); +SQLITE_API SQLITE_DEPRECATED int sqlite3_memory_alarm(void(*)(void*,sqlite3_int64,int),void*,sqlite3_int64); #endif /* -** CAPI3REF: Obtaining SQL Function Parameter Values {H15100} +** CAPI3REF: Obtaining SQL Function Parameter Values ** ** The C-language implementation of SQL functions and aggregates uses ** this set of interface routines to access the parameter values on @@ -3225,22 +3264,22 @@ ** Any attempt to use these routines on an [unprotected sqlite3_value] ** object results in undefined behavior. ** -** These routines work just like the corresponding [column access functions] +** ^These routines work just like the corresponding [column access functions] ** except that these routines take a single [protected sqlite3_value] object ** pointer instead of a [sqlite3_stmt*] pointer and an integer column number. ** -** The sqlite3_value_text16() interface extracts a UTF-16 string -** in the native byte-order of the host machine. The +** ^The sqlite3_value_text16() interface extracts a UTF-16 string +** in the native byte-order of the host machine. ^The ** sqlite3_value_text16be() and sqlite3_value_text16le() interfaces ** extract UTF-16 strings as big-endian and little-endian respectively. ** -** The sqlite3_value_numeric_type() interface attempts to apply +** ^(The sqlite3_value_numeric_type() interface attempts to apply ** numeric affinity to the value. This means that an attempt is ** made to convert the value to an integer or floating point. If ** such a conversion is possible without loss of information (in other ** words, if the value is a string that looks like a number) ** then the conversion is performed. Otherwise no conversion occurs. -** The [SQLITE_INTEGER | datatype] after conversion is returned. +** The [SQLITE_INTEGER | datatype] after conversion is returned.)^ ** ** Please pay particular attention to the fact that the pointer returned ** from [sqlite3_value_blob()], [sqlite3_value_text()], or @@ -3250,85 +3289,88 @@ ** ** These routines must be called from the same thread as ** the SQL function that supplied the [sqlite3_value*] parameters. +*/ +SQLITE_API const void *sqlite3_value_blob(sqlite3_value*); +SQLITE_API int sqlite3_value_bytes(sqlite3_value*); +SQLITE_API int sqlite3_value_bytes16(sqlite3_value*); +SQLITE_API double sqlite3_value_double(sqlite3_value*); +SQLITE_API int sqlite3_value_int(sqlite3_value*); +SQLITE_API sqlite3_int64 sqlite3_value_int64(sqlite3_value*); +SQLITE_API const unsigned char *sqlite3_value_text(sqlite3_value*); +SQLITE_API const void *sqlite3_value_text16(sqlite3_value*); +SQLITE_API const void *sqlite3_value_text16le(sqlite3_value*); +SQLITE_API const void *sqlite3_value_text16be(sqlite3_value*); +SQLITE_API int sqlite3_value_type(sqlite3_value*); +SQLITE_API int sqlite3_value_numeric_type(sqlite3_value*); + +/* +** CAPI3REF: Obtain Aggregate Function Context +** +** Implementions of aggregate SQL functions use this +** routine to allocate memory for storing their state. +** +** ^The first time the sqlite3_aggregate_context(C,N) routine is called +** for a particular aggregate function, SQLite +** allocates N of memory, zeroes out that memory, and returns a pointer +** to the new memory. ^On second and subsequent calls to +** sqlite3_aggregate_context() for the same aggregate function instance, +** the same buffer is returned. Sqlite3_aggregate_context() is normally +** called once for each invocation of the xStep callback and then one +** last time when the xFinal callback is invoked. ^(When no rows match +** an aggregate query, the xStep() callback of the aggregate function +** implementation is never called and xFinal() is called exactly once. +** In those cases, sqlite3_aggregate_context() might be called for the +** first time from within xFinal().)^ +** +** ^The sqlite3_aggregate_context(C,N) routine returns a NULL pointer if N is +** less than or equal to zero or if a memory allocate error occurs. +** +** ^(The amount of space allocated by sqlite3_aggregate_context(C,N) is +** determined by the N parameter on first successful call. Changing the +** value of N in subsequent call to sqlite3_aggregate_context() within +** the same aggregate function instance will not resize the memory +** allocation.)^ ** -** Requirements: -** [H15103] [H15106] [H15109] [H15112] [H15115] [H15118] [H15121] [H15124] -** [H15127] [H15130] [H15133] [H15136] -*/ -const void *sqlite3_value_blob(sqlite3_value*); -int sqlite3_value_bytes(sqlite3_value*); -int sqlite3_value_bytes16(sqlite3_value*); -double sqlite3_value_double(sqlite3_value*); -int sqlite3_value_int(sqlite3_value*); -sqlite3_int64 sqlite3_value_int64(sqlite3_value*); -const unsigned char *sqlite3_value_text(sqlite3_value*); -const void *sqlite3_value_text16(sqlite3_value*); -const void *sqlite3_value_text16le(sqlite3_value*); -const void *sqlite3_value_text16be(sqlite3_value*); -int sqlite3_value_type(sqlite3_value*); -int sqlite3_value_numeric_type(sqlite3_value*); - -/* -** CAPI3REF: Obtain Aggregate Function Context {H16210} -** -** The implementation of aggregate SQL functions use this routine to allocate -** a structure for storing their state. -** -** The first time the sqlite3_aggregate_context() routine is called for a -** particular aggregate, SQLite allocates nBytes of memory, zeroes out that -** memory, and returns a pointer to it. On second and subsequent calls to -** sqlite3_aggregate_context() for the same aggregate function index, -** the same buffer is returned. The implementation of the aggregate can use -** the returned buffer to accumulate data. -** -** SQLite automatically frees the allocated buffer when the aggregate -** query concludes. +** ^SQLite automatically frees the memory allocated by +** sqlite3_aggregate_context() when the aggregate query concludes. ** -** The first parameter should be a copy of the +** The first parameter must be a copy of the ** [sqlite3_context | SQL function context] that is the first parameter -** to the callback routine that implements the aggregate function. +** to the xStep or xFinal callback routine that implements the aggregate +** function. ** ** This routine must be called from the same thread in which ** the aggregate SQL function is running. -** -** Requirements: -** [H16211] [H16213] [H16215] [H16217] */ -void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); +SQLITE_API void *sqlite3_aggregate_context(sqlite3_context*, int nBytes); /* -** CAPI3REF: User Data For Functions {H16240} +** CAPI3REF: User Data For Functions ** -** The sqlite3_user_data() interface returns a copy of +** ^The sqlite3_user_data() interface returns a copy of ** the pointer that was the pUserData parameter (the 5th parameter) ** of the [sqlite3_create_function()] ** and [sqlite3_create_function16()] routines that originally -** registered the application defined function. {END} +** registered the application defined function. ** ** This routine must be called from the same thread in which ** the application-defined function is running. -** -** Requirements: -** [H16243] */ -void *sqlite3_user_data(sqlite3_context*); +SQLITE_API void *sqlite3_user_data(sqlite3_context*); /* -** CAPI3REF: Database Connection For Functions {H16250} +** CAPI3REF: Database Connection For Functions ** -** The sqlite3_context_db_handle() interface returns a copy of +** ^The sqlite3_context_db_handle() interface returns a copy of ** the pointer to the [database connection] (the 1st parameter) ** of the [sqlite3_create_function()] ** and [sqlite3_create_function16()] routines that originally ** registered the application defined function. -** -** Requirements: -** [H16253] */ -sqlite3 *sqlite3_context_db_handle(sqlite3_context*); +SQLITE_API sqlite3 *sqlite3_context_db_handle(sqlite3_context*); /* -** CAPI3REF: Function Auxiliary Data {H16270} +** CAPI3REF: Function Auxiliary Data ** ** The following two functions may be used by scalar SQL functions to ** associate metadata with argument values. If the same value is passed to @@ -3341,48 +3383,45 @@ ** invocations of the same function so that the original pattern string ** does not need to be recompiled on each invocation. ** -** The sqlite3_get_auxdata() interface returns a pointer to the metadata +** ^The sqlite3_get_auxdata() interface returns a pointer to the metadata ** associated by the sqlite3_set_auxdata() function with the Nth argument -** value to the application-defined function. If no metadata has been ever +** value to the application-defined function. ^If no metadata has been ever ** been set for the Nth argument of the function, or if the corresponding ** function parameter has changed since the meta-data was set, ** then sqlite3_get_auxdata() returns a NULL pointer. ** -** The sqlite3_set_auxdata() interface saves the metadata +** ^The sqlite3_set_auxdata() interface saves the metadata ** pointed to by its 3rd parameter as the metadata for the N-th ** argument of the application-defined function. Subsequent ** calls to sqlite3_get_auxdata() might return this data, if it has ** not been destroyed. -** If it is not NULL, SQLite will invoke the destructor +** ^If it is not NULL, SQLite will invoke the destructor ** function given by the 4th parameter to sqlite3_set_auxdata() on ** the metadata when the corresponding function parameter changes ** or when the SQL statement completes, whichever comes first. ** ** SQLite is free to call the destructor and drop metadata on any -** parameter of any function at any time. The only guarantee is that +** parameter of any function at any time. ^The only guarantee is that ** the destructor will be called before the metadata is dropped. ** -** In practice, metadata is preserved between function calls for +** ^(In practice, metadata is preserved between function calls for ** expressions that are constant at compile time. This includes literal -** values and SQL variables. +** values and [parameters].)^ ** ** These routines must be called from the same thread in which ** the SQL function is running. -** -** Requirements: -** [H16272] [H16274] [H16276] [H16277] [H16278] [H16279] */ -void *sqlite3_get_auxdata(sqlite3_context*, int N); -void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); +SQLITE_API void *sqlite3_get_auxdata(sqlite3_context*, int N); +SQLITE_API void sqlite3_set_auxdata(sqlite3_context*, int N, void*, void (*)(void*)); /* -** CAPI3REF: Constants Defining Special Destructor Behavior {H10280} +** CAPI3REF: Constants Defining Special Destructor Behavior ** ** These are special values for the destructor that is passed in as the -** final argument to routines like [sqlite3_result_blob()]. If the destructor +** final argument to routines like [sqlite3_result_blob()]. ^If the destructor ** argument is SQLITE_STATIC, it means that the content pointer is constant -** and will never change. It does not need to be destroyed. The +** and will never change. It does not need to be destroyed. ^The ** SQLITE_TRANSIENT value means that the content will likely change in ** the near future and that SQLite should make its own private copy of ** the content before returning. @@ -3395,7 +3434,7 @@ #define SQLITE_TRANSIENT ((sqlite3_destructor_type)-1) /* -** CAPI3REF: Setting The Result Of An SQL Function {H16400} +** CAPI3REF: Setting The Result Of An SQL Function ** ** These routines are used by the xFunc or xFinal callbacks that ** implement SQL functions and aggregates. See @@ -3406,135 +3445,131 @@ ** functions used to bind values to host parameters in prepared statements. ** Refer to the [SQL parameter] documentation for additional information. ** -** The sqlite3_result_blob() interface sets the result from +** ^The sqlite3_result_blob() interface sets the result from ** an application-defined function to be the BLOB whose content is pointed ** to by the second parameter and which is N bytes long where N is the ** third parameter. ** -** The sqlite3_result_zeroblob() interfaces set the result of +** ^The sqlite3_result_zeroblob() interfaces set the result of ** the application-defined function to be a BLOB containing all zero ** bytes and N bytes in size, where N is the value of the 2nd parameter. ** -** The sqlite3_result_double() interface sets the result from +** ^The sqlite3_result_double() interface sets the result from ** an application-defined function to be a floating point value specified ** by its 2nd argument. ** -** The sqlite3_result_error() and sqlite3_result_error16() functions +** ^The sqlite3_result_error() and sqlite3_result_error16() functions ** cause the implemented SQL function to throw an exception. -** SQLite uses the string pointed to by the +** ^SQLite uses the string pointed to by the ** 2nd parameter of sqlite3_result_error() or sqlite3_result_error16() -** as the text of an error message. SQLite interprets the error -** message string from sqlite3_result_error() as UTF-8. SQLite +** as the text of an error message. ^SQLite interprets the error +** message string from sqlite3_result_error() as UTF-8. ^SQLite ** interprets the string from sqlite3_result_error16() as UTF-16 in native -** byte order. If the third parameter to sqlite3_result_error() +** byte order. ^If the third parameter to sqlite3_result_error() ** or sqlite3_result_error16() is negative then SQLite takes as the error ** message all text up through the first zero character. -** If the third parameter to sqlite3_result_error() or +** ^If the third parameter to sqlite3_result_error() or ** sqlite3_result_error16() is non-negative then SQLite takes that many ** bytes (not characters) from the 2nd parameter as the error message. -** The sqlite3_result_error() and sqlite3_result_error16() +** ^The sqlite3_result_error() and sqlite3_result_error16() ** routines make a private copy of the error message text before ** they return. Hence, the calling function can deallocate or ** modify the text after they return without harm. -** The sqlite3_result_error_code() function changes the error code -** returned by SQLite as a result of an error in a function. By default, -** the error code is SQLITE_ERROR. A subsequent call to sqlite3_result_error() +** ^The sqlite3_result_error_code() function changes the error code +** returned by SQLite as a result of an error in a function. ^By default, +** the error code is SQLITE_ERROR. ^A subsequent call to sqlite3_result_error() ** or sqlite3_result_error16() resets the error code to SQLITE_ERROR. ** -** The sqlite3_result_toobig() interface causes SQLite to throw an error -** indicating that a string or BLOB is to long to represent. +** ^The sqlite3_result_toobig() interface causes SQLite to throw an error +** indicating that a string or BLOB is too long to represent. ** -** The sqlite3_result_nomem() interface causes SQLite to throw an error +** ^The sqlite3_result_nomem() interface causes SQLite to throw an error ** indicating that a memory allocation failed. ** -** The sqlite3_result_int() interface sets the return value +** ^The sqlite3_result_int() interface sets the return value ** of the application-defined function to be the 32-bit signed integer ** value given in the 2nd argument. -** The sqlite3_result_int64() interface sets the return value +** ^The sqlite3_result_int64() interface sets the return value ** of the application-defined function to be the 64-bit signed integer ** value given in the 2nd argument. ** -** The sqlite3_result_null() interface sets the return value +** ^The sqlite3_result_null() interface sets the return value ** of the application-defined function to be NULL. ** -** The sqlite3_result_text(), sqlite3_result_text16(), +** ^The sqlite3_result_text(), sqlite3_result_text16(), ** sqlite3_result_text16le(), and sqlite3_result_text16be() interfaces ** set the return value of the application-defined function to be ** a text string which is represented as UTF-8, UTF-16 native byte order, ** UTF-16 little endian, or UTF-16 big endian, respectively. -** SQLite takes the text result from the application from +** ^SQLite takes the text result from the application from ** the 2nd parameter of the sqlite3_result_text* interfaces. -** If the 3rd parameter to the sqlite3_result_text* interfaces +** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** is negative, then SQLite takes result text from the 2nd parameter ** through the first zero character. -** If the 3rd parameter to the sqlite3_result_text* interfaces +** ^If the 3rd parameter to the sqlite3_result_text* interfaces ** is non-negative, then as many bytes (not characters) of the text ** pointed to by the 2nd parameter are taken as the application-defined ** function result. -** If the 4th parameter to the sqlite3_result_text* interfaces +** ^If the 4th parameter to the sqlite3_result_text* interfaces ** or sqlite3_result_blob is a non-NULL pointer, then SQLite calls that ** function as the destructor on the text or BLOB result when it has ** finished using that result. -** If the 4th parameter to the sqlite3_result_text* interfaces or +** ^If the 4th parameter to the sqlite3_result_text* interfaces or to ** sqlite3_result_blob is the special constant SQLITE_STATIC, then SQLite ** assumes that the text or BLOB result is in constant space and does not -** copy the it or call a destructor when it has finished using that result. -** If the 4th parameter to the sqlite3_result_text* interfaces +** copy the content of the parameter nor call a destructor on the content +** when it has finished using that result. +** ^If the 4th parameter to the sqlite3_result_text* interfaces ** or sqlite3_result_blob is the special constant SQLITE_TRANSIENT ** then SQLite makes a copy of the result into space obtained from ** from [sqlite3_malloc()] before it returns. ** -** The sqlite3_result_value() interface sets the result of +** ^The sqlite3_result_value() interface sets the result of ** the application-defined function to be a copy the -** [unprotected sqlite3_value] object specified by the 2nd parameter. The +** [unprotected sqlite3_value] object specified by the 2nd parameter. ^The ** sqlite3_result_value() interface makes a copy of the [sqlite3_value] ** so that the [sqlite3_value] specified in the parameter may change or ** be deallocated after sqlite3_result_value() returns without harm. -** A [protected sqlite3_value] object may always be used where an +** ^A [protected sqlite3_value] object may always be used where an ** [unprotected sqlite3_value] object is required, so either ** kind of [sqlite3_value] object can be used with this interface. ** ** If these routines are called from within the different thread ** than the one containing the application-defined function that received ** the [sqlite3_context] pointer, the results are undefined. -** -** Requirements: -** [H16403] [H16406] [H16409] [H16412] [H16415] [H16418] [H16421] [H16424] -** [H16427] [H16430] [H16433] [H16436] [H16439] [H16442] [H16445] [H16448] -** [H16451] [H16454] [H16457] [H16460] [H16463] -*/ -void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); -void sqlite3_result_double(sqlite3_context*, double); -void sqlite3_result_error(sqlite3_context*, const char*, int); -void sqlite3_result_error16(sqlite3_context*, const void*, int); -void sqlite3_result_error_toobig(sqlite3_context*); -void sqlite3_result_error_nomem(sqlite3_context*); -void sqlite3_result_error_code(sqlite3_context*, int); -void sqlite3_result_int(sqlite3_context*, int); -void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); -void sqlite3_result_null(sqlite3_context*); -void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); -void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); -void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); -void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); -void sqlite3_result_value(sqlite3_context*, sqlite3_value*); -void sqlite3_result_zeroblob(sqlite3_context*, int n); +*/ +SQLITE_API void sqlite3_result_blob(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void sqlite3_result_double(sqlite3_context*, double); +SQLITE_API void sqlite3_result_error(sqlite3_context*, const char*, int); +SQLITE_API void sqlite3_result_error16(sqlite3_context*, const void*, int); +SQLITE_API void sqlite3_result_error_toobig(sqlite3_context*); +SQLITE_API void sqlite3_result_error_nomem(sqlite3_context*); +SQLITE_API void sqlite3_result_error_code(sqlite3_context*, int); +SQLITE_API void sqlite3_result_int(sqlite3_context*, int); +SQLITE_API void sqlite3_result_int64(sqlite3_context*, sqlite3_int64); +SQLITE_API void sqlite3_result_null(sqlite3_context*); +SQLITE_API void sqlite3_result_text(sqlite3_context*, const char*, int, void(*)(void*)); +SQLITE_API void sqlite3_result_text16(sqlite3_context*, const void*, int, void(*)(void*)); +SQLITE_API void sqlite3_result_text16le(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void sqlite3_result_text16be(sqlite3_context*, const void*, int,void(*)(void*)); +SQLITE_API void sqlite3_result_value(sqlite3_context*, sqlite3_value*); +SQLITE_API void sqlite3_result_zeroblob(sqlite3_context*, int n); /* -** CAPI3REF: Define New Collating Sequences {H16600} +** CAPI3REF: Define New Collating Sequences ** ** These functions are used to add new collation sequences to the ** [database connection] specified as the first argument. ** -** The name of the new collation sequence is specified as a UTF-8 string +** ^The name of the new collation sequence is specified as a UTF-8 string ** for sqlite3_create_collation() and sqlite3_create_collation_v2() -** and a UTF-16 string for sqlite3_create_collation16(). In all cases +** and a UTF-16 string for sqlite3_create_collation16(). ^In all cases ** the name is passed as the second function argument. ** -** The third argument may be one of the constants [SQLITE_UTF8], +** ^The third argument may be one of the constants [SQLITE_UTF8], ** [SQLITE_UTF16LE], or [SQLITE_UTF16BE], indicating that the user-supplied ** routine expects to be passed pointers to strings encoded using UTF-8, -** UTF-16 little-endian, or UTF-16 big-endian, respectively. The +** UTF-16 little-endian, or UTF-16 big-endian, respectively. ^The ** third argument might also be [SQLITE_UTF16] to indicate that the routine ** expects pointers to be UTF-16 strings in the native byte order, or the ** argument can be [SQLITE_UTF16_ALIGNED] if the @@ -3542,42 +3577,38 @@ ** of UTF-16 in the native byte order. ** ** A pointer to the user supplied routine must be passed as the fifth -** argument. If it is NULL, this is the same as deleting the collation +** argument. ^If it is NULL, this is the same as deleting the collation ** sequence (so that SQLite cannot call it anymore). -** Each time the application supplied function is invoked, it is passed +** ^Each time the application supplied function is invoked, it is passed ** as its first parameter a copy of the void* passed as the fourth argument ** to sqlite3_create_collation() or sqlite3_create_collation16(). ** -** The remaining arguments to the application-supplied routine are two strings, +** ^The remaining arguments to the application-supplied routine are two strings, ** each represented by a (length, data) pair and encoded in the encoding ** that was passed as the third argument when the collation sequence was -** registered. {END} The application defined collation routine should +** registered. The application defined collation routine should ** return negative, zero or positive if the first string is less than, ** equal to, or greater than the second string. i.e. (STRING1 - STRING2). ** -** The sqlite3_create_collation_v2() works like sqlite3_create_collation() +** ^The sqlite3_create_collation_v2() works like sqlite3_create_collation() ** except that it takes an extra argument which is a destructor for -** the collation. The destructor is called when the collation is +** the collation. ^The destructor is called when the collation is ** destroyed and is passed a copy of the fourth parameter void* pointer ** of the sqlite3_create_collation_v2(). -** Collations are destroyed when they are overridden by later calls to the +** ^Collations are destroyed when they are overridden by later calls to the ** collation creation functions or when the [database connection] is closed ** using [sqlite3_close()]. ** ** See also: [sqlite3_collation_needed()] and [sqlite3_collation_needed16()]. -** -** Requirements: -** [H16603] [H16604] [H16606] [H16609] [H16612] [H16615] [H16618] [H16621] -** [H16624] [H16627] [H16630] */ -int sqlite3_create_collation( +SQLITE_API int sqlite3_create_collation( sqlite3*, const char *zName, int eTextRep, void*, int(*xCompare)(void*,int,const void*,int,const void*) ); -int sqlite3_create_collation_v2( +SQLITE_API int sqlite3_create_collation_v2( sqlite3*, const char *zName, int eTextRep, @@ -3585,7 +3616,7 @@ int(*xCompare)(void*,int,const void*,int,const void*), void(*xDestroy)(void*) ); -int sqlite3_create_collation16( +SQLITE_API int sqlite3_create_collation16( sqlite3*, const void *zName, int eTextRep, @@ -3594,40 +3625,37 @@ ); /* -** CAPI3REF: Collation Needed Callbacks {H16700} +** CAPI3REF: Collation Needed Callbacks ** -** To avoid having to register all collation sequences before a database +** ^To avoid having to register all collation sequences before a database ** can be used, a single callback function may be registered with the -** [database connection] to be called whenever an undefined collation +** [database connection] to be invoked whenever an undefined collation ** sequence is required. ** -** If the function is registered using the sqlite3_collation_needed() API, +** ^If the function is registered using the sqlite3_collation_needed() API, ** then it is passed the names of undefined collation sequences as strings -** encoded in UTF-8. {H16703} If sqlite3_collation_needed16() is used, +** encoded in UTF-8. ^If sqlite3_collation_needed16() is used, ** the names are passed as UTF-16 in machine native byte order. -** A call to either function replaces any existing callback. +** ^A call to either function replaces the existing collation-needed callback. ** -** When the callback is invoked, the first argument passed is a copy +** ^(When the callback is invoked, the first argument passed is a copy ** of the second argument to sqlite3_collation_needed() or ** sqlite3_collation_needed16(). The second argument is the database ** connection. The third argument is one of [SQLITE_UTF8], [SQLITE_UTF16BE], ** or [SQLITE_UTF16LE], indicating the most desirable form of the collation ** sequence function required. The fourth parameter is the name of the -** required collation sequence. +** required collation sequence.)^ ** ** The callback function should register the desired collation using ** [sqlite3_create_collation()], [sqlite3_create_collation16()], or ** [sqlite3_create_collation_v2()]. -** -** Requirements: -** [H16702] [H16704] [H16706] */ -int sqlite3_collation_needed( +SQLITE_API int sqlite3_collation_needed( sqlite3*, void*, void(*)(void*,sqlite3*,int eTextRep,const char*) ); -int sqlite3_collation_needed16( +SQLITE_API int sqlite3_collation_needed16( sqlite3*, void*, void(*)(void*,sqlite3*,int eTextRep,const void*) @@ -3640,7 +3668,7 @@ ** The code to implement this API is not available in the public release ** of SQLite. */ -int sqlite3_key( +SQLITE_API int sqlite3_key( sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The key */ ); @@ -3653,35 +3681,34 @@ ** The code to implement this API is not available in the public release ** of SQLite. */ -int sqlite3_rekey( +SQLITE_API int sqlite3_rekey( sqlite3 *db, /* Database to be rekeyed */ const void *pKey, int nKey /* The new key */ ); /* -** CAPI3REF: Suspend Execution For A Short Time {H10530} +** CAPI3REF: Suspend Execution For A Short Time ** -** The sqlite3_sleep() function causes the current thread to suspend execution +** ^The sqlite3_sleep() function causes the current thread to suspend execution ** for at least a number of milliseconds specified in its parameter. ** -** If the operating system does not support sleep requests with +** ^If the operating system does not support sleep requests with ** millisecond time resolution, then the time will be rounded up to -** the nearest second. The number of milliseconds of sleep actually +** the nearest second. ^The number of milliseconds of sleep actually ** requested from the operating system is returned. ** -** SQLite implements this interface by calling the xSleep() +** ^SQLite implements this interface by calling the xSleep() ** method of the default [sqlite3_vfs] object. -** -** Requirements: [H10533] [H10536] */ -int sqlite3_sleep(int); +SQLITE_API int sqlite3_sleep(int); /* -** CAPI3REF: Name Of The Folder Holding Temporary Files {H10310} +** CAPI3REF: Name Of The Folder Holding Temporary Files ** -** If this global variable is made to point to a string which is +** ^(If this global variable is made to point to a string which is ** the name of a folder (a.k.a. directory), then all temporary files -** created by SQLite will be placed in that directory. If this variable +** created by SQLite when using a built-in [sqlite3_vfs | VFS] +** will be placed in that directory.)^ ^If this variable ** is a NULL pointer, then SQLite performs a search for an appropriate ** temporary file directory. ** @@ -3694,8 +3721,8 @@ ** routines have been called and that this variable remain unchanged ** thereafter. ** -** The [temp_store_directory pragma] may modify this variable and cause -** it to point to memory obtained from [sqlite3_malloc]. Furthermore, +** ^The [temp_store_directory pragma] may modify this variable and cause +** it to point to memory obtained from [sqlite3_malloc]. ^Furthermore, ** the [temp_store_directory pragma] always assumes that any string ** that this variable points to is held in memory obtained from ** [sqlite3_malloc] and the pragma may attempt to free that memory @@ -3704,17 +3731,17 @@ ** made NULL or made to point to memory obtained from [sqlite3_malloc] ** or else the use of the [temp_store_directory pragma] should be avoided. */ -SQLITE_EXTERN char *sqlite3_temp_directory; +SQLITE_API SQLITE_EXTERN char *sqlite3_temp_directory; /* -** CAPI3REF: Test For Auto-Commit Mode {H12930} +** CAPI3REF: Test For Auto-Commit Mode ** KEYWORDS: {autocommit mode} ** -** The sqlite3_get_autocommit() interface returns non-zero or +** ^The sqlite3_get_autocommit() interface returns non-zero or ** zero if the given database connection is or is not in autocommit mode, -** respectively. Autocommit mode is on by default. -** Autocommit mode is disabled by a [BEGIN] statement. -** Autocommit mode is re-enabled by a [COMMIT] or [ROLLBACK]. +** respectively. ^Autocommit mode is on by default. +** ^Autocommit mode is disabled by a [BEGIN] statement. +** ^Autocommit mode is re-enabled by a [COMMIT] or [ROLLBACK]. ** ** If certain kinds of errors occur on a statement within a multi-statement ** transaction (errors including [SQLITE_FULL], [SQLITE_IOERR], @@ -3726,58 +3753,55 @@ ** If another thread changes the autocommit status of the database ** connection while this routine is running, then the return value ** is undefined. -** -** Requirements: [H12931] [H12932] [H12933] [H12934] */ -int sqlite3_get_autocommit(sqlite3*); +SQLITE_API int sqlite3_get_autocommit(sqlite3*); /* -** CAPI3REF: Find The Database Handle Of A Prepared Statement {H13120} +** CAPI3REF: Find The Database Handle Of A Prepared Statement ** -** The sqlite3_db_handle interface returns the [database connection] handle -** to which a [prepared statement] belongs. The [database connection] -** returned by sqlite3_db_handle is the same [database connection] that was the first argument +** ^The sqlite3_db_handle interface returns the [database connection] handle +** to which a [prepared statement] belongs. ^The [database connection] +** returned by sqlite3_db_handle is the same [database connection] +** that was the first argument ** to the [sqlite3_prepare_v2()] call (or its variants) that was used to ** create the statement in the first place. -** -** Requirements: [H13123] */ -sqlite3 *sqlite3_db_handle(sqlite3_stmt*); +SQLITE_API sqlite3 *sqlite3_db_handle(sqlite3_stmt*); /* -** CAPI3REF: Find the next prepared statement {H13140} +** CAPI3REF: Find the next prepared statement ** -** This interface returns a pointer to the next [prepared statement] after -** pStmt associated with the [database connection] pDb. If pStmt is NULL +** ^This interface returns a pointer to the next [prepared statement] after +** pStmt associated with the [database connection] pDb. ^If pStmt is NULL ** then this interface returns a pointer to the first prepared statement -** associated with the database connection pDb. If no prepared statement +** associated with the database connection pDb. ^If no prepared statement ** satisfies the conditions of this routine, it returns NULL. ** ** The [database connection] pointer D in a call to ** [sqlite3_next_stmt(D,S)] must refer to an open database ** connection and in particular must not be a NULL pointer. -** -** Requirements: [H13143] [H13146] [H13149] [H13152] */ -sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); +SQLITE_API sqlite3_stmt *sqlite3_next_stmt(sqlite3 *pDb, sqlite3_stmt *pStmt); /* -** CAPI3REF: Commit And Rollback Notification Callbacks {H12950} +** CAPI3REF: Commit And Rollback Notification Callbacks ** -** The sqlite3_commit_hook() interface registers a callback +** ^The sqlite3_commit_hook() interface registers a callback ** function to be invoked whenever a transaction is [COMMIT | committed]. -** Any callback set by a previous call to sqlite3_commit_hook() +** ^Any callback set by a previous call to sqlite3_commit_hook() ** for the same database connection is overridden. -** The sqlite3_rollback_hook() interface registers a callback +** ^The sqlite3_rollback_hook() interface registers a callback ** function to be invoked whenever a transaction is [ROLLBACK | rolled back]. -** Any callback set by a previous call to sqlite3_commit_hook() +** ^Any callback set by a previous call to sqlite3_rollback_hook() ** for the same database connection is overridden. -** The pArg argument is passed through to the callback. -** If the callback on a commit hook function returns non-zero, +** ^The pArg argument is passed through to the callback. +** ^If the callback on a commit hook function returns non-zero, ** then the commit is converted into a rollback. ** -** If another function was previously registered, its -** pArg value is returned. Otherwise NULL is returned. +** ^The sqlite3_commit_hook(D,C,P) and sqlite3_rollback_hook(D,C,P) functions +** return the P argument from the previous call of the same function +** on the same [database connection] D, or NULL for +** the first call for each function on D. ** ** The callback implementation must not do anything that will modify ** the database connection that invoked the callback. Any actions @@ -3787,59 +3811,54 @@ ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** -** Registering a NULL function disables the callback. +** ^Registering a NULL function disables the callback. ** -** When the commit hook callback routine returns zero, the [COMMIT] -** operation is allowed to continue normally. If the commit hook +** ^When the commit hook callback routine returns zero, the [COMMIT] +** operation is allowed to continue normally. ^If the commit hook ** returns non-zero, then the [COMMIT] is converted into a [ROLLBACK]. -** The rollback hook is invoked on a rollback that results from a commit +** ^The rollback hook is invoked on a rollback that results from a commit ** hook returning non-zero, just as it would be with any other rollback. ** -** For the purposes of this API, a transaction is said to have been +** ^For the purposes of this API, a transaction is said to have been ** rolled back if an explicit "ROLLBACK" statement is executed, or ** an error or constraint causes an implicit rollback to occur. -** The rollback callback is not invoked if a transaction is +** ^The rollback callback is not invoked if a transaction is ** automatically rolled back because the database connection is closed. -** The rollback callback is not invoked if a transaction is +** ^The rollback callback is not invoked if a transaction is ** rolled back because a commit callback returned non-zero. -** Check on this ** ** See also the [sqlite3_update_hook()] interface. -** -** Requirements: -** [H12951] [H12952] [H12953] [H12954] [H12955] -** [H12961] [H12962] [H12963] [H12964] */ -void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); -void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); +SQLITE_API void *sqlite3_commit_hook(sqlite3*, int(*)(void*), void*); +SQLITE_API void *sqlite3_rollback_hook(sqlite3*, void(*)(void *), void*); /* -** CAPI3REF: Data Change Notification Callbacks {H12970} +** CAPI3REF: Data Change Notification Callbacks ** -** The sqlite3_update_hook() interface registers a callback function +** ^The sqlite3_update_hook() interface registers a callback function ** with the [database connection] identified by the first argument ** to be invoked whenever a row is updated, inserted or deleted. -** Any callback set by a previous call to this function +** ^Any callback set by a previous call to this function ** for the same database connection is overridden. ** -** The second argument is a pointer to the function to invoke when a +** ^The second argument is a pointer to the function to invoke when a ** row is updated, inserted or deleted. -** The first argument to the callback is a copy of the third argument +** ^The first argument to the callback is a copy of the third argument ** to sqlite3_update_hook(). -** The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], +** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE], ** or [SQLITE_UPDATE], depending on the operation that caused the callback ** to be invoked. -** The third and fourth arguments to the callback contain pointers to the +** ^The third and fourth arguments to the callback contain pointers to the ** database and table name containing the affected row. -** The final callback parameter is the [rowid] of the row. -** In the case of an update, this is the [rowid] after the update takes place. +** ^The final callback parameter is the [rowid] of the row. +** ^In the case of an update, this is the [rowid] after the update takes place. ** -** The update hook is not invoked when internal system tables are -** modified (i.e. sqlite_master and sqlite_sequence). +** ^(The update hook is not invoked when internal system tables are +** modified (i.e. sqlite_master and sqlite_sequence).)^ ** -** In the current implementation, the update hook +** ^In the current implementation, the update hook ** is not invoked when duplication rows are deleted because of an -** [ON CONFLICT | ON CONFLICT REPLACE] clause. Nor is the update hook +** [ON CONFLICT | ON CONFLICT REPLACE] clause. ^Nor is the update hook ** invoked when rows are deleted using the [truncate optimization]. ** The exceptions defined in this paragraph might change in a future ** release of SQLite. @@ -3851,90 +3870,81 @@ ** Note that [sqlite3_prepare_v2()] and [sqlite3_step()] both modify their ** database connections for the meaning of "modify" in this paragraph. ** -** If another function was previously registered, its pArg value -** is returned. Otherwise NULL is returned. +** ^The sqlite3_update_hook(D,C,P) function +** returns the P argument from the previous call +** on the same [database connection] D, or NULL for +** the first call on D. ** ** See also the [sqlite3_commit_hook()] and [sqlite3_rollback_hook()] ** interfaces. -** -** Requirements: -** [H12971] [H12973] [H12975] [H12977] [H12979] [H12981] [H12983] [H12986] */ -void *sqlite3_update_hook( +SQLITE_API void *sqlite3_update_hook( sqlite3*, void(*)(void *,int ,char const *,char const *,sqlite3_int64), void* ); /* -** CAPI3REF: Enable Or Disable Shared Pager Cache {H10330} -** KEYWORDS: {shared cache} {shared cache mode} +** CAPI3REF: Enable Or Disable Shared Pager Cache +** KEYWORDS: {shared cache} ** -** This routine enables or disables the sharing of the database cache +** ^(This routine enables or disables the sharing of the database cache ** and schema data structures between [database connection | connections] ** to the same database. Sharing is enabled if the argument is true -** and disabled if the argument is false. +** and disabled if the argument is false.)^ ** -** Cache sharing is enabled and disabled for an entire process. +** ^Cache sharing is enabled and disabled for an entire process. ** This is a change as of SQLite version 3.5.0. In prior versions of SQLite, ** sharing was enabled or disabled for each thread separately. ** -** The cache sharing mode set by this interface effects all subsequent +** ^(The cache sharing mode set by this interface effects all subsequent ** calls to [sqlite3_open()], [sqlite3_open_v2()], and [sqlite3_open16()]. ** Existing database connections continue use the sharing mode -** that was in effect at the time they were opened. +** that was in effect at the time they were opened.)^ ** -** Virtual tables cannot be used with a shared cache. When shared -** cache is enabled, the [sqlite3_create_module()] API used to register -** virtual tables will always return an error. +** ^(This routine returns [SQLITE_OK] if shared cache was enabled or disabled +** successfully. An [error code] is returned otherwise.)^ ** -** This routine returns [SQLITE_OK] if shared cache was enabled or disabled -** successfully. An [error code] is returned otherwise. -** -** Shared cache is disabled by default. But this might change in +** ^Shared cache is disabled by default. But this might change in ** future releases of SQLite. Applications that care about shared ** cache setting should set it explicitly. ** ** See Also: [SQLite Shared-Cache Mode] -** -** Requirements: [H10331] [H10336] [H10337] [H10339] */ -int sqlite3_enable_shared_cache(int); +SQLITE_API int sqlite3_enable_shared_cache(int); /* -** CAPI3REF: Attempt To Free Heap Memory {H17340} +** CAPI3REF: Attempt To Free Heap Memory ** -** The sqlite3_release_memory() interface attempts to free N bytes +** ^The sqlite3_release_memory() interface attempts to free N bytes ** of heap memory by deallocating non-essential memory allocations -** held by the database library. {END} Memory used to cache database +** held by the database library. Memory used to cache database ** pages to improve performance is an example of non-essential memory. -** sqlite3_release_memory() returns the number of bytes actually freed, +** ^sqlite3_release_memory() returns the number of bytes actually freed, ** which might be more or less than the amount requested. -** -** Requirements: [H17341] [H17342] */ -int sqlite3_release_memory(int); +SQLITE_API int sqlite3_release_memory(int); /* -** CAPI3REF: Impose A Limit On Heap Size {H17350} +** CAPI3REF: Impose A Limit On Heap Size ** -** The sqlite3_soft_heap_limit() interface places a "soft" limit +** ^The sqlite3_soft_heap_limit() interface places a "soft" limit ** on the amount of heap memory that may be allocated by SQLite. -** If an internal allocation is requested that would exceed the +** ^If an internal allocation is requested that would exceed the ** soft heap limit, [sqlite3_release_memory()] is invoked one or ** more times to free up some space before the allocation is performed. ** -** The limit is called "soft", because if [sqlite3_release_memory()] +** ^The limit is called "soft" because if [sqlite3_release_memory()] ** cannot free sufficient memory to prevent the limit from being exceeded, ** the memory is allocated anyway and the current operation proceeds. ** -** A negative or zero value for N means that there is no soft heap limit and +** ^A negative or zero value for N means that there is no soft heap limit and ** [sqlite3_release_memory()] will only be called when memory is exhausted. -** The default value for the soft heap limit is zero. +** ^The default value for the soft heap limit is zero. ** -** SQLite makes a best effort to honor the soft heap limit. +** ^(SQLite makes a best effort to honor the soft heap limit. ** But if the soft heap limit cannot be honored, execution will -** continue without error or notification. This is why the limit is +** continue without error or notification.)^ This is why the limit is ** called a "soft" limit. It is advisory only. ** ** Prior to SQLite version 3.5.0, this routine only constrained the memory @@ -3944,35 +3954,32 @@ ** is an upper bound on the total memory allocation for all threads. In ** version 3.5.0 there is no mechanism for limiting the heap usage for ** individual threads. -** -** Requirements: -** [H16351] [H16352] [H16353] [H16354] [H16355] [H16358] */ -void sqlite3_soft_heap_limit(int); +SQLITE_API void sqlite3_soft_heap_limit(int); /* -** CAPI3REF: Extract Metadata About A Column Of A Table {H12850} +** CAPI3REF: Extract Metadata About A Column Of A Table ** -** This routine returns metadata about a specific column of a specific +** ^This routine returns metadata about a specific column of a specific ** database table accessible using the [database connection] handle ** passed as the first function argument. ** -** The column is identified by the second, third and fourth parameters to -** this function. The second parameter is either the name of the database -** (i.e. "main", "temp" or an attached database) containing the specified -** table or NULL. If it is NULL, then all attached databases are searched +** ^The column is identified by the second, third and fourth parameters to +** this function. ^The second parameter is either the name of the database +** (i.e. "main", "temp", or an attached database) containing the specified +** table or NULL. ^If it is NULL, then all attached databases are searched ** for the table using the same algorithm used by the database engine to ** resolve unqualified table references. ** -** The third and fourth parameters to this function are the table and column +** ^The third and fourth parameters to this function are the table and column ** name of the desired column, respectively. Neither of these parameters ** may be NULL. ** -** Metadata is returned by writing to the memory locations passed as the 5th -** and subsequent parameters to this function. Any of these arguments may be +** ^Metadata is returned by writing to the memory locations passed as the 5th +** and subsequent parameters to this function. ^Any of these arguments may be ** NULL, in which case the corresponding element of metadata is omitted. ** -**
    +** ^(
    ** **
    Parameter Output
    Type
    Description ** @@ -3982,17 +3989,17 @@ **
    8th int True if column is part of the PRIMARY KEY **
    9th int True if column is [AUTOINCREMENT] **
    -**
    +**
    )^ ** -** The memory pointed to by the character pointers returned for the +** ^The memory pointed to by the character pointers returned for the ** declaration type and collation sequence is valid only until the next ** call to any SQLite API function. ** -** If the specified table is actually a view, an [error code] is returned. +** ^If the specified table is actually a view, an [error code] is returned. ** -** If the specified column is "rowid", "oid" or "_rowid_" and an +** ^If the specified column is "rowid", "oid" or "_rowid_" and an ** [INTEGER PRIMARY KEY] column has been explicitly declared, then the output -** parameters are set for the explicitly declared column. If there is no +** parameters are set for the explicitly declared column. ^(If there is no ** explicitly declared [INTEGER PRIMARY KEY] column, then the output ** parameters are set as follows: ** @@ -4002,17 +4009,17 @@ ** not null: 0 ** primary key: 1 ** auto increment: 0 -** +** )^ ** -** This function may load one or more schemas from database files. If an +** ^(This function may load one or more schemas from database files. If an ** error occurs during this process, or if the requested table or column ** cannot be found, an [error code] is returned and an error message left -** in the [database connection] (to be retrieved using sqlite3_errmsg()). +** in the [database connection] (to be retrieved using sqlite3_errmsg()).)^ ** -** This API is only available if the library was compiled with the +** ^This API is only available if the library was compiled with the ** [SQLITE_ENABLE_COLUMN_METADATA] C-preprocessor symbol defined. */ -int sqlite3_table_column_metadata( +SQLITE_API int sqlite3_table_column_metadata( sqlite3 *db, /* Connection handle */ const char *zDbName, /* Database name or NULL */ const char *zTableName, /* Table name */ @@ -4025,32 +4032,31 @@ ); /* -** CAPI3REF: Load An Extension {H12600} -** -** This interface loads an SQLite extension library from the named file. -** -** {H12601} The sqlite3_load_extension() interface attempts to load an -** SQLite extension library contained in the file zFile. -** -** {H12602} The entry point is zProc. +** CAPI3REF: Load An Extension ** -** {H12603} zProc may be 0, in which case the name of the entry point -** defaults to "sqlite3_extension_init". +** ^This interface loads an SQLite extension library from the named file. ** -** {H12604} The sqlite3_load_extension() interface shall return -** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. +** ^The sqlite3_load_extension() interface attempts to load an +** SQLite extension library contained in the file zFile. ** -** {H12605} If an error occurs and pzErrMsg is not 0, then the -** [sqlite3_load_extension()] interface shall attempt to -** fill *pzErrMsg with error message text stored in memory -** obtained from [sqlite3_malloc()]. {END} The calling function -** should free this memory by calling [sqlite3_free()]. +** ^The entry point is zProc. +** ^zProc may be 0, in which case the name of the entry point +** defaults to "sqlite3_extension_init". +** ^The sqlite3_load_extension() interface returns +** [SQLITE_OK] on success and [SQLITE_ERROR] if something goes wrong. +** ^If an error occurs and pzErrMsg is not 0, then the +** [sqlite3_load_extension()] interface shall attempt to +** fill *pzErrMsg with error message text stored in memory +** obtained from [sqlite3_malloc()]. The calling function +** should free this memory by calling [sqlite3_free()]. +** +** ^Extension loading must be enabled using +** [sqlite3_enable_load_extension()] prior to calling this API, +** otherwise an error will be returned. ** -** {H12606} Extension loading must be enabled using -** [sqlite3_enable_load_extension()] prior to calling this API, -** otherwise an error will be returned. +** See also the [load_extension() SQL function]. */ -int sqlite3_load_extension( +SQLITE_API int sqlite3_load_extension( sqlite3 *db, /* Load the extension into this database connection */ const char *zFile, /* Name of the shared library containing extension */ const char *zProc, /* Entry point. Derived from zFile if 0 */ @@ -4058,63 +4064,51 @@ ); /* -** CAPI3REF: Enable Or Disable Extension Loading {H12620} +** CAPI3REF: Enable Or Disable Extension Loading ** -** So as not to open security holes in older applications that are +** ^So as not to open security holes in older applications that are ** unprepared to deal with extension loading, and as a means of disabling ** extension loading while evaluating user-entered SQL, the following API ** is provided to turn the [sqlite3_load_extension()] mechanism on and off. ** -** Extension loading is off by default. See ticket #1863. -** -** {H12621} Call the sqlite3_enable_load_extension() routine with onoff==1 -** to turn extension loading on and call it with onoff==0 to turn -** it back off again. -** -** {H12622} Extension loading is off by default. +** ^Extension loading is off by default. See ticket #1863. +** ^Call the sqlite3_enable_load_extension() routine with onoff==1 +** to turn extension loading on and call it with onoff==0 to turn +** it back off again. */ -int sqlite3_enable_load_extension(sqlite3 *db, int onoff); +SQLITE_API int sqlite3_enable_load_extension(sqlite3 *db, int onoff); /* -** CAPI3REF: Automatically Load An Extensions {H12640} +** CAPI3REF: Automatically Load An Extensions ** -** This API can be invoked at program startup in order to register +** ^This API can be invoked at program startup in order to register ** one or more statically linked extensions that will be available -** to all new [database connections]. {END} -** -** This routine stores a pointer to the extension in an array that is -** obtained from [sqlite3_malloc()]. If you run a memory leak checker -** on your program and it reports a leak because of this array, invoke -** [sqlite3_reset_auto_extension()] prior to shutdown to free the memory. -** -** {H12641} This function registers an extension entry point that is -** automatically invoked whenever a new [database connection] -** is opened using [sqlite3_open()], [sqlite3_open16()], -** or [sqlite3_open_v2()]. +** to all new [database connections]. ** -** {H12642} Duplicate extensions are detected so calling this routine -** multiple times with the same extension is harmless. +** ^(This routine stores a pointer to the extension entry point +** in an array that is obtained from [sqlite3_malloc()]. That memory +** is deallocated by [sqlite3_reset_auto_extension()].)^ ** -** {H12643} This routine stores a pointer to the extension in an array -** that is obtained from [sqlite3_malloc()]. -** -** {H12644} Automatic extensions apply across all threads. +** ^This function registers an extension entry point that is +** automatically invoked whenever a new [database connection] +** is opened using [sqlite3_open()], [sqlite3_open16()], +** or [sqlite3_open_v2()]. +** ^Duplicate extensions are detected so calling this routine +** multiple times with the same extension is harmless. +** ^Automatic extensions apply across all threads. */ -int sqlite3_auto_extension(void (*xEntryPoint)(void)); +SQLITE_API int sqlite3_auto_extension(void (*xEntryPoint)(void)); /* -** CAPI3REF: Reset Automatic Extension Loading {H12660} -** -** This function disables all previously registered automatic -** extensions. {END} It undoes the effect of all prior -** [sqlite3_auto_extension()] calls. +** CAPI3REF: Reset Automatic Extension Loading ** -** {H12661} This function disables all previously registered -** automatic extensions. +** ^(This function disables all previously registered automatic +** extensions. It undoes the effect of all prior +** [sqlite3_auto_extension()] calls.)^ ** -** {H12662} This function disables automatic extensions in all threads. +** ^This function disables automatic extensions in all threads. */ -void sqlite3_reset_auto_extension(void); +SQLITE_API void sqlite3_reset_auto_extension(void); /* ****** EXPERIMENTAL - subject to change without notice ************** @@ -4136,7 +4130,7 @@ typedef struct sqlite3_module sqlite3_module; /* -** CAPI3REF: Virtual Table Object {H18000} +** CAPI3REF: Virtual Table Object ** KEYWORDS: sqlite3_module {virtual table module} ** EXPERIMENTAL ** @@ -4144,10 +4138,10 @@ ** defines the implementation of a [virtual tables]. ** This structure consists mostly of methods for the module. ** -** A virtual table module is created by filling in a persistent +** ^A virtual table module is created by filling in a persistent ** instance of this structure and passing a pointer to that instance ** to [sqlite3_create_module()] or [sqlite3_create_module_v2()]. -** The registration remains valid until it is replaced by a different +** ^The registration remains valid until it is replaced by a different ** module or until the [database connection] closes. The content ** of this structure must not change while it is registered with ** any database connection. @@ -4183,7 +4177,7 @@ }; /* -** CAPI3REF: Virtual Table Indexing Information {H18100} +** CAPI3REF: Virtual Table Indexing Information ** KEYWORDS: sqlite3_index_info ** EXPERIMENTAL ** @@ -4193,42 +4187,42 @@ ** inputs to xBestIndex and are read-only. xBestIndex inserts its ** results into the **Outputs** fields. ** -** The aConstraint[] array records WHERE clause constraints of the form: +** ^(The aConstraint[] array records WHERE clause constraints of the form: ** **
    column OP expr
    ** -** where OP is =, <, <=, >, or >=. The particular operator is -** stored in aConstraint[].op. The index of the column is stored in -** aConstraint[].iColumn. aConstraint[].usable is TRUE if the +** where OP is =, <, <=, >, or >=.)^ ^(The particular operator is +** stored in aConstraint[].op.)^ ^(The index of the column is stored in +** aConstraint[].iColumn.)^ ^(aConstraint[].usable is TRUE if the ** expr on the right-hand side can be evaluated (and thus the constraint -** is usable) and false if it cannot. +** is usable) and false if it cannot.)^ ** -** The optimizer automatically inverts terms of the form "expr OP column" +** ^The optimizer automatically inverts terms of the form "expr OP column" ** and makes other simplifications to the WHERE clause in an attempt to ** get as many WHERE clause terms into the form shown above as possible. -** The aConstraint[] array only reports WHERE clause terms in the correct -** form that refer to the particular virtual table being queried. +** ^The aConstraint[] array only reports WHERE clause terms that are +** relevant to the particular virtual table being queried. ** -** Information about the ORDER BY clause is stored in aOrderBy[]. -** Each term of aOrderBy records a column of the ORDER BY clause. +** ^Information about the ORDER BY clause is stored in aOrderBy[]. +** ^Each term of aOrderBy records a column of the ORDER BY clause. ** ** The [xBestIndex] method must fill aConstraintUsage[] with information -** about what parameters to pass to xFilter. If argvIndex>0 then +** about what parameters to pass to xFilter. ^If argvIndex>0 then ** the right-hand side of the corresponding aConstraint[] is evaluated -** and becomes the argvIndex-th entry in argv. If aConstraintUsage[].omit +** and becomes the argvIndex-th entry in argv. ^(If aConstraintUsage[].omit ** is true, then the constraint is assumed to be fully handled by the -** virtual table and is not checked again by SQLite. +** virtual table and is not checked again by SQLite.)^ ** -** The idxNum and idxPtr values are recorded and passed into the +** ^The idxNum and idxPtr values are recorded and passed into the ** [xFilter] method. -** [sqlite3_free()] is used to free idxPtr if and only iff +** ^[sqlite3_free()] is used to free idxPtr if and only if ** needToFreeIdxPtr is true. ** -** The orderByConsumed means that output from [xFilter]/[xNext] will occur in +** ^The orderByConsumed means that output from [xFilter]/[xNext] will occur in ** the correct order to satisfy the ORDER BY clause so that no separate ** sorting step is required. ** -** The estimatedCost value is an estimate of the cost of doing the +** ^The estimatedCost value is an estimate of the cost of doing the ** particular lookup. A full scan of a table with N entries should have ** a cost of N. A binary search of a table of N entries should have a ** cost of approximately log(N). @@ -4266,43 +4260,36 @@ #define SQLITE_INDEX_CONSTRAINT_MATCH 64 /* -** CAPI3REF: Register A Virtual Table Implementation {H18200} +** CAPI3REF: Register A Virtual Table Implementation ** EXPERIMENTAL ** -** This routine is used to register a new [virtual table module] name. -** Module names must be registered before -** creating a new [virtual table] using the module, or before using a +** ^These routines are used to register a new [virtual table module] name. +** ^Module names must be registered before +** creating a new [virtual table] using the module and before using a ** preexisting [virtual table] for the module. ** -** The module name is registered on the [database connection] specified -** by the first parameter. The name of the module is given by the -** second parameter. The third parameter is a pointer to -** the implementation of the [virtual table module]. The fourth +** ^The module name is registered on the [database connection] specified +** by the first parameter. ^The name of the module is given by the +** second parameter. ^The third parameter is a pointer to +** the implementation of the [virtual table module]. ^The fourth ** parameter is an arbitrary client data pointer that is passed through ** into the [xCreate] and [xConnect] methods of the virtual table module ** when a new virtual table is be being created or reinitialized. ** -** This interface has exactly the same effect as calling -** [sqlite3_create_module_v2()] with a NULL client data destructor. +** ^The sqlite3_create_module_v2() interface has a fifth parameter which +** is a pointer to a destructor for the pClientData. ^SQLite will +** invoke the destructor function (if it is not NULL) when SQLite +** no longer needs the pClientData pointer. ^The sqlite3_create_module() +** interface is equivalent to sqlite3_create_module_v2() with a NULL +** destructor. */ -SQLITE_EXPERIMENTAL int sqlite3_create_module( +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_create_module( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ void *pClientData /* Client data for xCreate/xConnect */ ); - -/* -** CAPI3REF: Register A Virtual Table Implementation {H18210} -** EXPERIMENTAL -** -** This routine is identical to the [sqlite3_create_module()] method, -** except that it has an extra parameter to specify -** a destructor function for the client data pointer. SQLite will -** invoke the destructor function (if it is not NULL) when SQLite -** no longer needs the pClientData pointer. -*/ -SQLITE_EXPERIMENTAL int sqlite3_create_module_v2( +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_create_module_v2( sqlite3 *db, /* SQLite connection to register module with */ const char *zName, /* Name of the module */ const sqlite3_module *p, /* Methods for the module */ @@ -4311,33 +4298,33 @@ ); /* -** CAPI3REF: Virtual Table Instance Object {H18010} +** CAPI3REF: Virtual Table Instance Object ** KEYWORDS: sqlite3_vtab ** EXPERIMENTAL ** ** Every [virtual table module] implementation uses a subclass -** of the following structure to describe a particular instance +** of this object to describe a particular instance ** of the [virtual table]. Each subclass will ** be tailored to the specific needs of the module implementation. ** The purpose of this superclass is to define certain fields that are ** common to all module implementations. ** -** Virtual tables methods can set an error message by assigning a +** ^Virtual tables methods can set an error message by assigning a ** string obtained from [sqlite3_mprintf()] to zErrMsg. The method should ** take care that any prior string is freed by a call to [sqlite3_free()] -** prior to assigning a new string to zErrMsg. After the error message +** prior to assigning a new string to zErrMsg. ^After the error message ** is delivered up to the client application, the string will be automatically ** freed by sqlite3_free() and the zErrMsg field will be zeroed. */ struct sqlite3_vtab { const sqlite3_module *pModule; /* The module for this virtual table */ - int nRef; /* Used internally */ + int nRef; /* NO LONGER USED */ char *zErrMsg; /* Error message from sqlite3_mprintf() */ /* Virtual table implementations will typically add additional fields */ }; /* -** CAPI3REF: Virtual Table Cursor Object {H18020} +** CAPI3REF: Virtual Table Cursor Object ** KEYWORDS: sqlite3_vtab_cursor {virtual table cursor} ** EXPERIMENTAL ** @@ -4346,7 +4333,7 @@ ** [virtual table] and are used ** to loop through the virtual table. Cursors are created using the ** [sqlite3_module.xOpen | xOpen] method of the module and are destroyed -** by the [sqlite3_module.xClose | xClose] method. Cussors are used +** by the [sqlite3_module.xClose | xClose] method. Cursors are used ** by the [xFilter], [xNext], [xEof], [xColumn], and [xRowid] methods ** of the module. Each module implementation will define ** the content of a cursor structure to suit its own needs. @@ -4360,34 +4347,34 @@ }; /* -** CAPI3REF: Declare The Schema Of A Virtual Table {H18280} +** CAPI3REF: Declare The Schema Of A Virtual Table ** EXPERIMENTAL ** -** The [xCreate] and [xConnect] methods of a +** ^The [xCreate] and [xConnect] methods of a ** [virtual table module] call this interface ** to declare the format (the names and datatypes of the columns) of ** the virtual tables they implement. */ -SQLITE_EXPERIMENTAL int sqlite3_declare_vtab(sqlite3*, const char *zSQL); +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_declare_vtab(sqlite3*, const char *zSQL); /* -** CAPI3REF: Overload A Function For A Virtual Table {H18300} +** CAPI3REF: Overload A Function For A Virtual Table ** EXPERIMENTAL ** -** Virtual tables can provide alternative implementations of functions +** ^(Virtual tables can provide alternative implementations of functions ** using the [xFindFunction] method of the [virtual table module]. ** But global versions of those functions -** must exist in order to be overloaded. +** must exist in order to be overloaded.)^ ** -** This API makes sure a global version of a function with a particular +** ^(This API makes sure a global version of a function with a particular ** name and number of parameters exists. If no such function exists -** before this API is called, a new function is created. The implementation +** before this API is called, a new function is created.)^ ^The implementation ** of the new function always causes an exception to be thrown. So ** the new function is not good for anything by itself. Its only ** purpose is to be a placeholder function that can be overloaded ** by a [virtual table]. */ -SQLITE_EXPERIMENTAL int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_overload_function(sqlite3*, const char *zFuncName, int nArg); /* ** The interface to the virtual-table mechanism defined above (back up @@ -4402,76 +4389,76 @@ */ /* -** CAPI3REF: A Handle To An Open BLOB {H17800} +** CAPI3REF: A Handle To An Open BLOB ** KEYWORDS: {BLOB handle} {BLOB handles} ** ** An instance of this object represents an open BLOB on which ** [sqlite3_blob_open | incremental BLOB I/O] can be performed. -** Objects of this type are created by [sqlite3_blob_open()] +** ^Objects of this type are created by [sqlite3_blob_open()] ** and destroyed by [sqlite3_blob_close()]. -** The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces +** ^The [sqlite3_blob_read()] and [sqlite3_blob_write()] interfaces ** can be used to read or write small subsections of the BLOB. -** The [sqlite3_blob_bytes()] interface returns the size of the BLOB in bytes. +** ^The [sqlite3_blob_bytes()] interface returns the size of the BLOB in bytes. */ typedef struct sqlite3_blob sqlite3_blob; /* -** CAPI3REF: Open A BLOB For Incremental I/O {H17810} +** CAPI3REF: Open A BLOB For Incremental I/O ** -** This interfaces opens a [BLOB handle | handle] to the BLOB located +** ^(This interfaces opens a [BLOB handle | handle] to the BLOB located ** in row iRow, column zColumn, table zTable in database zDb; ** in other words, the same BLOB that would be selected by: ** **
     **     SELECT zColumn FROM zDb.zTable WHERE [rowid] = iRow;
    -** 
    {END} +** )^ ** -** If the flags parameter is non-zero, then the BLOB is opened for read -** and write access. If it is zero, the BLOB is opened for read access. +** ^If the flags parameter is non-zero, then the BLOB is opened for read +** and write access. ^If it is zero, the BLOB is opened for read access. +** ^It is not possible to open a column that is part of an index or primary +** key for writing. ^If [foreign key constraints] are enabled, it is +** not possible to open a column that is part of a [child key] for writing. ** -** Note that the database name is not the filename that contains +** ^Note that the database name is not the filename that contains ** the database but rather the symbolic name of the database that -** is assigned when the database is connected using [ATTACH]. -** For the main database file, the database name is "main". -** For TEMP tables, the database name is "temp". +** appears after the AS keyword when the database is connected using [ATTACH]. +** ^For the main database file, the database name is "main". +** ^For TEMP tables, the database name is "temp". ** -** On success, [SQLITE_OK] is returned and the new [BLOB handle] is written +** ^(On success, [SQLITE_OK] is returned and the new [BLOB handle] is written ** to *ppBlob. Otherwise an [error code] is returned and *ppBlob is set -** to be a null pointer. -** This function sets the [database connection] error code and message +** to be a null pointer.)^ +** ^This function sets the [database connection] error code and message ** accessible via [sqlite3_errcode()] and [sqlite3_errmsg()] and related -** functions. Note that the *ppBlob variable is always initialized in a +** functions. ^Note that the *ppBlob variable is always initialized in a ** way that makes it safe to invoke [sqlite3_blob_close()] on *ppBlob ** regardless of the success or failure of this routine. ** -** If the row that a BLOB handle points to is modified by an +** ^(If the row that a BLOB handle points to is modified by an ** [UPDATE], [DELETE], or by [ON CONFLICT] side-effects ** then the BLOB handle is marked as "expired". ** This is true if any column of the row is changed, even a column -** other than the one the BLOB handle is open on. -** Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for +** other than the one the BLOB handle is open on.)^ +** ^Calls to [sqlite3_blob_read()] and [sqlite3_blob_write()] for ** a expired BLOB handle fail with an return code of [SQLITE_ABORT]. -** Changes written into a BLOB prior to the BLOB expiring are not -** rollback by the expiration of the BLOB. Such changes will eventually -** commit if the transaction continues to completion. -** -** Use the [sqlite3_blob_bytes()] interface to determine the size of -** the opened blob. The size of a blob may not be changed by this -** underface. Use the [UPDATE] SQL command to change the size of a +** ^(Changes written into a BLOB prior to the BLOB expiring are not +** rolled back by the expiration of the BLOB. Such changes will eventually +** commit if the transaction continues to completion.)^ +** +** ^Use the [sqlite3_blob_bytes()] interface to determine the size of +** the opened blob. ^The size of a blob may not be changed by this +** interface. Use the [UPDATE] SQL command to change the size of a ** blob. ** -** The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces +** ^The [sqlite3_bind_zeroblob()] and [sqlite3_result_zeroblob()] interfaces ** and the built-in [zeroblob] SQL function can be used, if desired, ** to create an empty, zero-filled blob in which to read or write using ** this interface. ** ** To avoid a resource leak, every open [BLOB handle] should eventually ** be released by a call to [sqlite3_blob_close()]. -** -** Requirements: -** [H17813] [H17814] [H17816] [H17819] [H17821] [H17824] */ -int sqlite3_blob_open( +SQLITE_API int sqlite3_blob_open( sqlite3*, const char *zDb, const char *zTable, @@ -4482,37 +4469,34 @@ ); /* -** CAPI3REF: Close A BLOB Handle {H17830} +** CAPI3REF: Close A BLOB Handle ** -** Closes an open [BLOB handle]. +** ^Closes an open [BLOB handle]. ** -** Closing a BLOB shall cause the current transaction to commit +** ^Closing a BLOB shall cause the current transaction to commit ** if there are no other BLOBs, no pending prepared statements, and the ** database connection is in [autocommit mode]. -** If any writes were made to the BLOB, they might be held in cache +** ^If any writes were made to the BLOB, they might be held in cache ** until the close operation if they will fit. ** -** Closing the BLOB often forces the changes +** ^(Closing the BLOB often forces the changes ** out to disk and so if any I/O errors occur, they will likely occur ** at the time when the BLOB is closed. Any errors that occur during -** closing are reported as a non-zero return value. +** closing are reported as a non-zero return value.)^ ** -** The BLOB is closed unconditionally. Even if this routine returns -** an error code, the BLOB is still closed. +** ^(The BLOB is closed unconditionally. Even if this routine returns +** an error code, the BLOB is still closed.)^ ** -** Calling this routine with a null pointer (which as would be returned -** by failed call to [sqlite3_blob_open()]) is a harmless no-op. -** -** Requirements: -** [H17833] [H17836] [H17839] +** ^Calling this routine with a null pointer (such as would be returned +** by a failed call to [sqlite3_blob_open()]) is a harmless no-op. */ -int sqlite3_blob_close(sqlite3_blob *); +SQLITE_API int sqlite3_blob_close(sqlite3_blob *); /* -** CAPI3REF: Return The Size Of An Open BLOB {H17840} +** CAPI3REF: Return The Size Of An Open BLOB ** -** Returns the size in bytes of the BLOB accessible via the -** successfully opened [BLOB handle] in its only argument. The +** ^Returns the size in bytes of the BLOB accessible via the +** successfully opened [BLOB handle] in its only argument. ^The ** incremental blob I/O routines can only read or overwriting existing ** blob content; they cannot change the size of a blob. ** @@ -4520,30 +4504,27 @@ ** by a prior successful call to [sqlite3_blob_open()] and which has not ** been closed by [sqlite3_blob_close()]. Passing any other pointer in ** to this routine results in undefined and probably undesirable behavior. -** -** Requirements: -** [H17843] */ -int sqlite3_blob_bytes(sqlite3_blob *); +SQLITE_API int sqlite3_blob_bytes(sqlite3_blob *); /* -** CAPI3REF: Read Data From A BLOB Incrementally {H17850} +** CAPI3REF: Read Data From A BLOB Incrementally ** -** This function is used to read data from an open [BLOB handle] into a +** ^(This function is used to read data from an open [BLOB handle] into a ** caller-supplied buffer. N bytes of data are copied into buffer Z -** from the open BLOB, starting at offset iOffset. +** from the open BLOB, starting at offset iOffset.)^ ** -** If offset iOffset is less than N bytes from the end of the BLOB, -** [SQLITE_ERROR] is returned and no data is read. If N or iOffset is +** ^If offset iOffset is less than N bytes from the end of the BLOB, +** [SQLITE_ERROR] is returned and no data is read. ^If N or iOffset is ** less than zero, [SQLITE_ERROR] is returned and no data is read. -** The size of the blob (and hence the maximum value of N+iOffset) +** ^The size of the blob (and hence the maximum value of N+iOffset) ** can be determined using the [sqlite3_blob_bytes()] interface. ** -** An attempt to read from an expired [BLOB handle] fails with an +** ^An attempt to read from an expired [BLOB handle] fails with an ** error code of [SQLITE_ABORT]. ** -** On success, SQLITE_OK is returned. -** Otherwise, an [error code] or an [extended error code] is returned. +** ^(On success, sqlite3_blob_read() returns SQLITE_OK. +** Otherwise, an [error code] or an [extended error code] is returned.)^ ** ** This routine only works on a [BLOB handle] which has been created ** by a prior successful call to [sqlite3_blob_open()] and which has not @@ -4551,40 +4532,37 @@ ** to this routine results in undefined and probably undesirable behavior. ** ** See also: [sqlite3_blob_write()]. -** -** Requirements: -** [H17853] [H17856] [H17859] [H17862] [H17863] [H17865] [H17868] */ -int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); +SQLITE_API int sqlite3_blob_read(sqlite3_blob *, void *Z, int N, int iOffset); /* -** CAPI3REF: Write Data Into A BLOB Incrementally {H17870} +** CAPI3REF: Write Data Into A BLOB Incrementally ** -** This function is used to write data into an open [BLOB handle] from a -** caller-supplied buffer. N bytes of data are copied from the buffer Z +** ^This function is used to write data into an open [BLOB handle] from a +** caller-supplied buffer. ^N bytes of data are copied from the buffer Z ** into the open BLOB, starting at offset iOffset. ** -** If the [BLOB handle] passed as the first argument was not opened for +** ^If the [BLOB handle] passed as the first argument was not opened for ** writing (the flags parameter to [sqlite3_blob_open()] was zero), ** this function returns [SQLITE_READONLY]. ** -** This function may only modify the contents of the BLOB; it is +** ^This function may only modify the contents of the BLOB; it is ** not possible to increase the size of a BLOB using this API. -** If offset iOffset is less than N bytes from the end of the BLOB, -** [SQLITE_ERROR] is returned and no data is written. If N is +** ^If offset iOffset is less than N bytes from the end of the BLOB, +** [SQLITE_ERROR] is returned and no data is written. ^If N is ** less than zero [SQLITE_ERROR] is returned and no data is written. ** The size of the BLOB (and hence the maximum value of N+iOffset) ** can be determined using the [sqlite3_blob_bytes()] interface. ** -** An attempt to write to an expired [BLOB handle] fails with an -** error code of [SQLITE_ABORT]. Writes to the BLOB that occurred +** ^An attempt to write to an expired [BLOB handle] fails with an +** error code of [SQLITE_ABORT]. ^Writes to the BLOB that occurred ** before the [BLOB handle] expired are not rolled back by the ** expiration of the handle, though of course those changes might ** have been overwritten by the statement that expired the BLOB handle ** or by other independent statements. ** -** On success, SQLITE_OK is returned. -** Otherwise, an [error code] or an [extended error code] is returned. +** ^(On success, sqlite3_blob_write() returns SQLITE_OK. +** Otherwise, an [error code] or an [extended error code] is returned.)^ ** ** This routine only works on a [BLOB handle] which has been created ** by a prior successful call to [sqlite3_blob_open()] and which has not @@ -4592,15 +4570,11 @@ ** to this routine results in undefined and probably undesirable behavior. ** ** See also: [sqlite3_blob_read()]. -** -** Requirements: -** [H17873] [H17874] [H17875] [H17876] [H17877] [H17879] [H17882] [H17885] -** [H17888] */ -int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); +SQLITE_API int sqlite3_blob_write(sqlite3_blob *, const void *z, int n, int iOffset); /* -** CAPI3REF: Virtual File System Objects {H11200} +** CAPI3REF: Virtual File System Objects ** ** A virtual filesystem (VFS) is an [sqlite3_vfs] object ** that SQLite uses to interact @@ -4609,34 +4583,31 @@ ** New VFSes can be registered and existing VFSes can be unregistered. ** The following interfaces are provided. ** -** The sqlite3_vfs_find() interface returns a pointer to a VFS given its name. -** Names are case sensitive. -** Names are zero-terminated UTF-8 strings. -** If there is no match, a NULL pointer is returned. -** If zVfsName is NULL then the default VFS is returned. -** -** New VFSes are registered with sqlite3_vfs_register(). -** Each new VFS becomes the default VFS if the makeDflt flag is set. -** The same VFS can be registered multiple times without injury. -** To make an existing VFS into the default VFS, register it again +** ^The sqlite3_vfs_find() interface returns a pointer to a VFS given its name. +** ^Names are case sensitive. +** ^Names are zero-terminated UTF-8 strings. +** ^If there is no match, a NULL pointer is returned. +** ^If zVfsName is NULL then the default VFS is returned. +** +** ^New VFSes are registered with sqlite3_vfs_register(). +** ^Each new VFS becomes the default VFS if the makeDflt flag is set. +** ^The same VFS can be registered multiple times without injury. +** ^To make an existing VFS into the default VFS, register it again ** with the makeDflt flag set. If two different VFSes with the ** same name are registered, the behavior is undefined. If a ** VFS is registered with a name that is NULL or an empty string, ** then the behavior is undefined. ** -** Unregister a VFS with the sqlite3_vfs_unregister() interface. -** If the default VFS is unregistered, another VFS is chosen as -** the default. The choice for the new VFS is arbitrary. -** -** Requirements: -** [H11203] [H11206] [H11209] [H11212] [H11215] [H11218] +** ^Unregister a VFS with the sqlite3_vfs_unregister() interface. +** ^(If the default VFS is unregistered, another VFS is chosen as +** the default. The choice for the new VFS is arbitrary.)^ */ -sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); -int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); -int sqlite3_vfs_unregister(sqlite3_vfs*); +SQLITE_API sqlite3_vfs *sqlite3_vfs_find(const char *zVfsName); +SQLITE_API int sqlite3_vfs_register(sqlite3_vfs*, int makeDflt); +SQLITE_API int sqlite3_vfs_unregister(sqlite3_vfs*); /* -** CAPI3REF: Mutexes {H17000} +** CAPI3REF: Mutexes ** ** The SQLite core uses these routines for thread ** synchronization. Though they are intended for internal @@ -4645,7 +4616,7 @@ ** ** The SQLite source code contains multiple implementations ** of these mutex routines. An appropriate implementation -** is selected automatically at compile-time. The following +** is selected automatically at compile-time. ^(The following ** implementations are available in the SQLite core: ** **
      @@ -4653,26 +4624,26 @@ **
    • SQLITE_MUTEX_PTHREAD **
    • SQLITE_MUTEX_W32 **
    • SQLITE_MUTEX_NOOP -**
    +** )^ ** -** The SQLITE_MUTEX_NOOP implementation is a set of routines +** ^The SQLITE_MUTEX_NOOP implementation is a set of routines ** that does no real locking and is appropriate for use in -** a single-threaded application. The SQLITE_MUTEX_OS2, +** a single-threaded application. ^The SQLITE_MUTEX_OS2, ** SQLITE_MUTEX_PTHREAD, and SQLITE_MUTEX_W32 implementations ** are appropriate for use on OS/2, Unix, and Windows. ** -** If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor +** ^(If SQLite is compiled with the SQLITE_MUTEX_APPDEF preprocessor ** macro defined (with "-DSQLITE_MUTEX_APPDEF=1"), then no mutex ** implementation is included with the library. In this case the ** application must supply a custom mutex implementation using the ** [SQLITE_CONFIG_MUTEX] option of the sqlite3_config() function ** before calling sqlite3_initialize() or any other public sqlite3_ -** function that calls sqlite3_initialize(). +** function that calls sqlite3_initialize().)^ ** -** {H17011} The sqlite3_mutex_alloc() routine allocates a new -** mutex and returns a pointer to it. {H17012} If it returns NULL -** that means that a mutex could not be allocated. {H17013} SQLite -** will unwind its stack and return an error. {H17014} The argument +** ^The sqlite3_mutex_alloc() routine allocates a new +** mutex and returns a pointer to it. ^If it returns NULL +** that means that a mutex could not be allocated. ^SQLite +** will unwind its stack and return an error. ^(The argument ** to sqlite3_mutex_alloc() is one of these integer constants: ** **
      @@ -4684,77 +4655,79 @@ **
    • SQLITE_MUTEX_STATIC_PRNG **
    • SQLITE_MUTEX_STATIC_LRU **
    • SQLITE_MUTEX_STATIC_LRU2 -**
    +** )^ ** -** {H17015} The first two constants cause sqlite3_mutex_alloc() to create -** a new mutex. The new mutex is recursive when SQLITE_MUTEX_RECURSIVE -** is used but not necessarily so when SQLITE_MUTEX_FAST is used. {END} +** ^The first two constants (SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) +** cause sqlite3_mutex_alloc() to create +** a new mutex. ^The new mutex is recursive when SQLITE_MUTEX_RECURSIVE +** is used but not necessarily so when SQLITE_MUTEX_FAST is used. ** The mutex implementation does not need to make a distinction ** between SQLITE_MUTEX_RECURSIVE and SQLITE_MUTEX_FAST if it does -** not want to. {H17016} But SQLite will only request a recursive mutex in -** cases where it really needs one. {END} If a faster non-recursive mutex +** not want to. ^SQLite will only request a recursive mutex in +** cases where it really needs one. ^If a faster non-recursive mutex ** implementation is available on the host platform, the mutex subsystem ** might return such a mutex in response to SQLITE_MUTEX_FAST. ** -** {H17017} The other allowed parameters to sqlite3_mutex_alloc() each return -** a pointer to a static preexisting mutex. {END} Four static mutexes are +** ^The other allowed parameters to sqlite3_mutex_alloc() (anything other +** than SQLITE_MUTEX_FAST and SQLITE_MUTEX_RECURSIVE) each return +** a pointer to a static preexisting mutex. ^Six static mutexes are ** used by the current version of SQLite. Future versions of SQLite ** may add additional static mutexes. Static mutexes are for internal ** use by SQLite only. Applications that use SQLite mutexes should ** use only the dynamic mutexes returned by SQLITE_MUTEX_FAST or ** SQLITE_MUTEX_RECURSIVE. ** -** {H17018} Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST +** ^Note that if one of the dynamic mutex parameters (SQLITE_MUTEX_FAST ** or SQLITE_MUTEX_RECURSIVE) is used then sqlite3_mutex_alloc() -** returns a different mutex on every call. {H17034} But for the static +** returns a different mutex on every call. ^But for the static ** mutex types, the same mutex is returned on every call that has ** the same type number. ** -** {H17019} The sqlite3_mutex_free() routine deallocates a previously -** allocated dynamic mutex. {H17020} SQLite is careful to deallocate every -** dynamic mutex that it allocates. {A17021} The dynamic mutexes must not be in -** use when they are deallocated. {A17022} Attempting to deallocate a static -** mutex results in undefined behavior. {H17023} SQLite never deallocates -** a static mutex. {END} +** ^The sqlite3_mutex_free() routine deallocates a previously +** allocated dynamic mutex. ^SQLite is careful to deallocate every +** dynamic mutex that it allocates. The dynamic mutexes must not be in +** use when they are deallocated. Attempting to deallocate a static +** mutex results in undefined behavior. ^SQLite never deallocates +** a static mutex. ** -** The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt -** to enter a mutex. {H17024} If another thread is already within the mutex, +** ^The sqlite3_mutex_enter() and sqlite3_mutex_try() routines attempt +** to enter a mutex. ^If another thread is already within the mutex, ** sqlite3_mutex_enter() will block and sqlite3_mutex_try() will return -** SQLITE_BUSY. {H17025} The sqlite3_mutex_try() interface returns [SQLITE_OK] -** upon successful entry. {H17026} Mutexes created using +** SQLITE_BUSY. ^The sqlite3_mutex_try() interface returns [SQLITE_OK] +** upon successful entry. ^(Mutexes created using ** SQLITE_MUTEX_RECURSIVE can be entered multiple times by the same thread. -** {H17027} In such cases the, +** In such cases the, ** mutex must be exited an equal number of times before another thread -** can enter. {A17028} If the same thread tries to enter any other +** can enter.)^ ^(If the same thread tries to enter any other ** kind of mutex more than once, the behavior is undefined. -** {H17029} SQLite will never exhibit -** such behavior in its own use of mutexes. +** SQLite will never exhibit +** such behavior in its own use of mutexes.)^ ** -** Some systems (for example, Windows 95) do not support the operation +** ^(Some systems (for example, Windows 95) do not support the operation ** implemented by sqlite3_mutex_try(). On those systems, sqlite3_mutex_try() -** will always return SQLITE_BUSY. {H17030} The SQLite core only ever uses -** sqlite3_mutex_try() as an optimization so this is acceptable behavior. +** will always return SQLITE_BUSY. The SQLite core only ever uses +** sqlite3_mutex_try() as an optimization so this is acceptable behavior.)^ ** -** {H17031} The sqlite3_mutex_leave() routine exits a mutex that was -** previously entered by the same thread. {A17032} The behavior +** ^The sqlite3_mutex_leave() routine exits a mutex that was +** previously entered by the same thread. ^(The behavior ** is undefined if the mutex is not currently entered by the -** calling thread or is not currently allocated. {H17033} SQLite will -** never do either. {END} +** calling thread or is not currently allocated. SQLite will +** never do either.)^ ** -** If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or +** ^If the argument to sqlite3_mutex_enter(), sqlite3_mutex_try(), or ** sqlite3_mutex_leave() is a NULL pointer, then all three routines ** behave as no-ops. ** ** See also: [sqlite3_mutex_held()] and [sqlite3_mutex_notheld()]. */ -sqlite3_mutex *sqlite3_mutex_alloc(int); -void sqlite3_mutex_free(sqlite3_mutex*); -void sqlite3_mutex_enter(sqlite3_mutex*); -int sqlite3_mutex_try(sqlite3_mutex*); -void sqlite3_mutex_leave(sqlite3_mutex*); +SQLITE_API sqlite3_mutex *sqlite3_mutex_alloc(int); +SQLITE_API void sqlite3_mutex_free(sqlite3_mutex*); +SQLITE_API void sqlite3_mutex_enter(sqlite3_mutex*); +SQLITE_API int sqlite3_mutex_try(sqlite3_mutex*); +SQLITE_API void sqlite3_mutex_leave(sqlite3_mutex*); /* -** CAPI3REF: Mutex Methods Object {H17120} +** CAPI3REF: Mutex Methods Object ** EXPERIMENTAL ** ** An instance of this structure defines the low-level routines @@ -4770,19 +4743,19 @@ ** output variable when querying the system for the current mutex ** implementation, using the [SQLITE_CONFIG_GETMUTEX] option. ** -** The xMutexInit method defined by this structure is invoked as +** ^The xMutexInit method defined by this structure is invoked as ** part of system initialization by the sqlite3_initialize() function. -** {H17001} The xMutexInit routine shall be called by SQLite once for each +** ^The xMutexInit routine is calle by SQLite exactly once for each ** effective call to [sqlite3_initialize()]. ** -** The xMutexEnd method defined by this structure is invoked as +** ^The xMutexEnd method defined by this structure is invoked as ** part of system shutdown by the sqlite3_shutdown() function. The ** implementation of this method is expected to release all outstanding ** resources obtained by the mutex methods implementation, especially -** those obtained by the xMutexInit method. {H17003} The xMutexEnd() -** interface shall be invoked once for each call to [sqlite3_shutdown()]. +** those obtained by the xMutexInit method. ^The xMutexEnd() +** interface is invoked exactly once for each call to [sqlite3_shutdown()]. ** -** The remaining seven methods defined by this structure (xMutexAlloc, +** ^(The remaining seven methods defined by this structure (xMutexAlloc, ** xMutexFree, xMutexEnter, xMutexTry, xMutexLeave, xMutexHeld and ** xMutexNotheld) implement the following interfaces (respectively): ** @@ -4794,7 +4767,7 @@ **
  • [sqlite3_mutex_leave()]
  • **
  • [sqlite3_mutex_held()]
  • **
  • [sqlite3_mutex_notheld()]
  • -** +** )^ ** ** The only difference is that the public sqlite3_XXX functions enumerated ** above silently ignore any invocations that pass a NULL pointer instead @@ -4803,6 +4776,21 @@ ** of passing a NULL pointer instead of a valid mutex handle are undefined ** (i.e. it is acceptable to provide an implementation that segfaults if ** it is passed a NULL pointer). +** +** The xMutexInit() method must be threadsafe. ^It must be harmless to +** invoke xMutexInit() mutiple times within the same process and without +** intervening calls to xMutexEnd(). Second and subsequent calls to +** xMutexInit() must be no-ops. +** +** ^xMutexInit() must not use SQLite memory allocation ([sqlite3_malloc()] +** and its associates). ^Similarly, xMutexAlloc() must not use SQLite memory +** allocation for a static mutex. ^However xMutexAlloc() may use SQLite +** memory allocation for a fast or recursive mutex. +** +** ^SQLite will invoke the xMutexEnd() method when [sqlite3_shutdown()] is +** called, but only if the prior call to xMutexInit returned SQLITE_OK. +** If xMutexInit fails in any way, it is expected to clean up after itself +** prior to returning. */ typedef struct sqlite3_mutex_methods sqlite3_mutex_methods; struct sqlite3_mutex_methods { @@ -4818,39 +4806,41 @@ }; /* -** CAPI3REF: Mutex Verification Routines {H17080} +** CAPI3REF: Mutex Verification Routines ** ** The sqlite3_mutex_held() and sqlite3_mutex_notheld() routines -** are intended for use inside assert() statements. {H17081} The SQLite core +** are intended for use inside assert() statements. ^The SQLite core ** never uses these routines except inside an assert() and applications -** are advised to follow the lead of the core. {H17082} The core only +** are advised to follow the lead of the core. ^The SQLite core only ** provides implementations for these routines when it is compiled -** with the SQLITE_DEBUG flag. {A17087} External mutex implementations +** with the SQLITE_DEBUG flag. ^External mutex implementations ** are only required to provide these routines if SQLITE_DEBUG is ** defined and if NDEBUG is not defined. ** -** {H17083} These routines should return true if the mutex in their argument +** ^These routines should return true if the mutex in their argument ** is held or not held, respectively, by the calling thread. ** -** {X17084} The implementation is not required to provided versions of these +** ^The implementation is not required to provided versions of these ** routines that actually work. If the implementation does not provide working ** versions of these routines, it should at least provide stubs that always ** return true so that one does not get spurious assertion failures. ** -** {H17085} If the argument to sqlite3_mutex_held() is a NULL pointer then -** the routine should return 1. {END} This seems counter-intuitive since +** ^If the argument to sqlite3_mutex_held() is a NULL pointer then +** the routine should return 1. This seems counter-intuitive since ** clearly the mutex cannot be held if it does not exist. But the ** the reason the mutex does not exist is because the build is not ** using mutexes. And we do not want the assert() containing the ** call to sqlite3_mutex_held() to fail, so a non-zero return is -** the appropriate thing to do. {H17086} The sqlite3_mutex_notheld() +** the appropriate thing to do. ^The sqlite3_mutex_notheld() ** interface should also return 1 when given a NULL pointer. */ -int sqlite3_mutex_held(sqlite3_mutex*); -int sqlite3_mutex_notheld(sqlite3_mutex*); +#ifndef NDEBUG +SQLITE_API int sqlite3_mutex_held(sqlite3_mutex*); +SQLITE_API int sqlite3_mutex_notheld(sqlite3_mutex*); +#endif /* -** CAPI3REF: Mutex Types {H17001} +** CAPI3REF: Mutex Types ** ** The [sqlite3_mutex_alloc()] interface takes a single argument ** which is one of these integer constants. @@ -4870,48 +4860,50 @@ #define SQLITE_MUTEX_STATIC_LRU2 7 /* lru page list */ /* -** CAPI3REF: Retrieve the mutex for a database connection {H17002} +** CAPI3REF: Retrieve the mutex for a database connection ** -** This interface returns a pointer the [sqlite3_mutex] object that +** ^This interface returns a pointer the [sqlite3_mutex] object that ** serializes access to the [database connection] given in the argument ** when the [threading mode] is Serialized. -** If the [threading mode] is Single-thread or Multi-thread then this +** ^If the [threading mode] is Single-thread or Multi-thread then this ** routine returns a NULL pointer. */ -sqlite3_mutex *sqlite3_db_mutex(sqlite3*); +SQLITE_API sqlite3_mutex *sqlite3_db_mutex(sqlite3*); /* -** CAPI3REF: Low-Level Control Of Database Files {H11300} +** CAPI3REF: Low-Level Control Of Database Files ** -** {H11301} The [sqlite3_file_control()] interface makes a direct call to the +** ^The [sqlite3_file_control()] interface makes a direct call to the ** xFileControl method for the [sqlite3_io_methods] object associated -** with a particular database identified by the second argument. {H11302} The -** name of the database is the name assigned to the database by the -** ATTACH SQL command that opened the -** database. {H11303} To control the main database file, use the name "main" -** or a NULL pointer. {H11304} The third and fourth parameters to this routine +** with a particular database identified by the second argument. ^The +** name of the database "main" for the main database or "temp" for the +** TEMP database, or the name that appears after the AS keyword for +** databases that are added using the [ATTACH] SQL command. +** ^A NULL pointer can be used in place of "main" to refer to the +** main database file. +** ^The third and fourth parameters to this routine ** are passed directly through to the second and third parameters of -** the xFileControl method. {H11305} The return value of the xFileControl +** the xFileControl method. ^The return value of the xFileControl ** method becomes the return value of this routine. ** -** {H11306} If the second parameter (zDbName) does not match the name of any -** open database file, then SQLITE_ERROR is returned. {H11307} This error +** ^If the second parameter (zDbName) does not match the name of any +** open database file, then SQLITE_ERROR is returned. ^This error ** code is not remembered and will not be recalled by [sqlite3_errcode()] -** or [sqlite3_errmsg()]. {A11308} The underlying xFileControl method might -** also return SQLITE_ERROR. {A11309} There is no way to distinguish between +** or [sqlite3_errmsg()]. The underlying xFileControl method might +** also return SQLITE_ERROR. There is no way to distinguish between ** an incorrect zDbName and an SQLITE_ERROR return from the underlying -** xFileControl method. {END} +** xFileControl method. ** ** See also: [SQLITE_FCNTL_LOCKSTATE] */ -int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); +SQLITE_API int sqlite3_file_control(sqlite3*, const char *zDbName, int op, void*); /* -** CAPI3REF: Testing Interface {H11400} +** CAPI3REF: Testing Interface ** -** The sqlite3_test_control() interface is used to read out internal +** ^The sqlite3_test_control() interface is used to read out internal ** state of SQLite and to inject faults into SQLite for testing -** purposes. The first parameter is an operation code that determines +** purposes. ^The first parameter is an operation code that determines ** the number, meaning, and operation of all subsequent parameters. ** ** This interface is not for use by applications. It exists solely @@ -4923,10 +4915,10 @@ ** Unlike most of the SQLite API, this function is not guaranteed to ** operate consistently from one release to the next. */ -int sqlite3_test_control(int op, ...); +SQLITE_API int sqlite3_test_control(int op, ...); /* -** CAPI3REF: Testing Interface Operation Codes {H11410} +** CAPI3REF: Testing Interface Operation Codes ** ** These constants are the valid operation code parameters used ** as the first argument to [sqlite3_test_control()]. @@ -4936,6 +4928,7 @@ ** Applications should not use any of these parameters or the ** [sqlite3_test_control()] interface. */ +#define SQLITE_TESTCTRL_FIRST 5 #define SQLITE_TESTCTRL_PRNG_SAVE 5 #define SQLITE_TESTCTRL_PRNG_RESTORE 6 #define SQLITE_TESTCTRL_PRNG_RESET 7 @@ -4945,29 +4938,33 @@ #define SQLITE_TESTCTRL_PENDING_BYTE 11 #define SQLITE_TESTCTRL_ASSERT 12 #define SQLITE_TESTCTRL_ALWAYS 13 +#define SQLITE_TESTCTRL_RESERVE 14 +#define SQLITE_TESTCTRL_OPTIMIZATIONS 15 +#define SQLITE_TESTCTRL_ISKEYWORD 16 +#define SQLITE_TESTCTRL_LAST 16 /* -** CAPI3REF: SQLite Runtime Status {H17200} +** CAPI3REF: SQLite Runtime Status ** EXPERIMENTAL ** -** This interface is used to retrieve runtime status information +** ^This interface is used to retrieve runtime status information ** about the preformance of SQLite, and optionally to reset various -** highwater marks. The first argument is an integer code for -** the specific parameter to measure. Recognized integer codes -** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...]. -** The current value of the parameter is returned into *pCurrent. -** The highest recorded value is returned in *pHighwater. If the +** highwater marks. ^The first argument is an integer code for +** the specific parameter to measure. ^(Recognized integer codes +** are of the form [SQLITE_STATUS_MEMORY_USED | SQLITE_STATUS_...].)^ +** ^The current value of the parameter is returned into *pCurrent. +** ^The highest recorded value is returned in *pHighwater. ^If the ** resetFlag is true, then the highest record value is reset after -** *pHighwater is written. Some parameters do not record the highest +** *pHighwater is written. ^(Some parameters do not record the highest ** value. For those parameters -** nothing is written into *pHighwater and the resetFlag is ignored. -** Other parameters record only the highwater mark and not the current -** value. For these latter parameters nothing is written into *pCurrent. +** nothing is written into *pHighwater and the resetFlag is ignored.)^ +** ^(Other parameters record only the highwater mark and not the current +** value. For these latter parameters nothing is written into *pCurrent.)^ ** -** This routine returns SQLITE_OK on success and a non-zero -** [error code] on failure. +** ^The sqlite3_db_status() routine returns SQLITE_OK on success and a +** non-zero [error code] on failure. ** -** This routine is threadsafe but is not atomic. This routine can +** This routine is threadsafe but is not atomic. This routine can be ** called while other threads are running the same or different SQLite ** interfaces. However the values returned in *pCurrent and ** *pHighwater reflect the status of SQLite at different points in time @@ -4976,18 +4973,18 @@ ** ** See also: [sqlite3_db_status()] */ -SQLITE_EXPERIMENTAL int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_status(int op, int *pCurrent, int *pHighwater, int resetFlag); /* -** CAPI3REF: Status Parameters {H17250} +** CAPI3REF: Status Parameters ** EXPERIMENTAL ** ** These integer constants designate various run-time status parameters ** that can be returned by [sqlite3_status()]. ** **
    -**
    SQLITE_STATUS_MEMORY_USED
    +** ^(
    SQLITE_STATUS_MEMORY_USED
    **
    This parameter is the current amount of memory checked out ** using [sqlite3_malloc()], either directly or indirectly. The ** figure includes calls made to [sqlite3_malloc()] by the application @@ -4995,45 +4992,45 @@ ** controlled by [SQLITE_CONFIG_SCRATCH] and auxiliary page-cache ** memory controlled by [SQLITE_CONFIG_PAGECACHE] is not included in ** this parameter. The amount returned is the sum of the allocation -** sizes as reported by the xSize method in [sqlite3_mem_methods].
    +** sizes as reported by the xSize method in [sqlite3_mem_methods].)^ ** -**
    SQLITE_STATUS_MALLOC_SIZE
    +** ^(
    SQLITE_STATUS_MALLOC_SIZE
    **
    This parameter records the largest memory allocation request ** handed to [sqlite3_malloc()] or [sqlite3_realloc()] (or their ** internal equivalents). Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. -** The value written into the *pCurrent parameter is undefined.
    +** The value written into the *pCurrent parameter is undefined.)^ ** -**
    SQLITE_STATUS_PAGECACHE_USED
    +** ^(
    SQLITE_STATUS_PAGECACHE_USED
    **
    This parameter returns the number of pages used out of the ** [pagecache memory allocator] that was configured using ** [SQLITE_CONFIG_PAGECACHE]. The -** value returned is in pages, not in bytes.
    +** value returned is in pages, not in bytes.)^ ** -**
    SQLITE_STATUS_PAGECACHE_OVERFLOW
    +** ^(
    SQLITE_STATUS_PAGECACHE_OVERFLOW
    **
    This parameter returns the number of bytes of page cache ** allocation which could not be statisfied by the [SQLITE_CONFIG_PAGECACHE] ** buffer and where forced to overflow to [sqlite3_malloc()]. The ** returned value includes allocations that overflowed because they ** where too large (they were larger than the "sz" parameter to ** [SQLITE_CONFIG_PAGECACHE]) and allocations that overflowed because -** no space was left in the page cache.
    +** no space was left in the page cache.)^ ** -**
    SQLITE_STATUS_PAGECACHE_SIZE
    +** ^(
    SQLITE_STATUS_PAGECACHE_SIZE
    **
    This parameter records the largest memory allocation request ** handed to [pagecache memory allocator]. Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. -** The value written into the *pCurrent parameter is undefined.
    +** The value written into the *pCurrent parameter is undefined.)^ ** -**
    SQLITE_STATUS_SCRATCH_USED
    +** ^(
    SQLITE_STATUS_SCRATCH_USED
    **
    This parameter returns the number of allocations used out of the ** [scratch memory allocator] configured using ** [SQLITE_CONFIG_SCRATCH]. The value returned is in allocations, not ** in bytes. Since a single thread may only have one scratch allocation ** outstanding at time, this parameter also reports the number of threads -** using scratch memory at the same time.
    +** using scratch memory at the same time.)^ ** -**
    SQLITE_STATUS_SCRATCH_OVERFLOW
    +** ^(
    SQLITE_STATUS_SCRATCH_OVERFLOW
    **
    This parameter returns the number of bytes of scratch memory ** allocation which could not be statisfied by the [SQLITE_CONFIG_SCRATCH] ** buffer and where forced to overflow to [sqlite3_malloc()]. The values @@ -5041,17 +5038,17 @@ ** larger (that is, because the requested allocation was larger than the ** "sz" parameter to [SQLITE_CONFIG_SCRATCH]) and because no scratch buffer ** slots were available. -**
    +** )^ ** -**
    SQLITE_STATUS_SCRATCH_SIZE
    +** ^(
    SQLITE_STATUS_SCRATCH_SIZE
    **
    This parameter records the largest memory allocation request ** handed to [scratch memory allocator]. Only the value returned in the ** *pHighwater parameter to [sqlite3_status()] is of interest. -** The value written into the *pCurrent parameter is undefined.
    +** The value written into the *pCurrent parameter is undefined.)^ ** -**
    SQLITE_STATUS_PARSER_STACK
    +** ^(
    SQLITE_STATUS_PARSER_STACK
    **
    This parameter records the deepest parser stack. It is only -** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].
    +** meaningful if SQLite is compiled with [YYTRACKMAXSTACKDEPTH].)^ **
    ** ** New status parameters may be added from time to time. @@ -5067,68 +5064,75 @@ #define SQLITE_STATUS_SCRATCH_SIZE 8 /* -** CAPI3REF: Database Connection Status {H17500} +** CAPI3REF: Database Connection Status ** EXPERIMENTAL ** -** This interface is used to retrieve runtime status information -** about a single [database connection]. The first argument is the -** database connection object to be interrogated. The second argument -** is the parameter to interrogate. Currently, the only allowed value +** ^This interface is used to retrieve runtime status information +** about a single [database connection]. ^The first argument is the +** database connection object to be interrogated. ^The second argument +** is the parameter to interrogate. ^Currently, the only allowed value ** for the second parameter is [SQLITE_DBSTATUS_LOOKASIDE_USED]. ** Additional options will likely appear in future releases of SQLite. ** -** The current value of the requested parameter is written into *pCur -** and the highest instantaneous value is written into *pHiwtr. If +** ^The current value of the requested parameter is written into *pCur +** and the highest instantaneous value is written into *pHiwtr. ^If ** the resetFlg is true, then the highest instantaneous value is ** reset back down to the current value. ** ** See also: [sqlite3_status()] and [sqlite3_stmt_status()]. */ -SQLITE_EXPERIMENTAL int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_db_status(sqlite3*, int op, int *pCur, int *pHiwtr, int resetFlg); /* -** CAPI3REF: Status Parameters for database connections {H17520} +** CAPI3REF: Status Parameters for database connections ** EXPERIMENTAL ** -** Status verbs for [sqlite3_db_status()]. +** These constants are the available integer "verbs" that can be passed as +** the second argument to the [sqlite3_db_status()] interface. +** +** New verbs may be added in future releases of SQLite. Existing verbs +** might be discontinued. Applications should check the return code from +** [sqlite3_db_status()] to make sure that the call worked. +** The [sqlite3_db_status()] interface will return a non-zero error code +** if a discontinued or unsupported verb is invoked. ** **
    -**
    SQLITE_DBSTATUS_LOOKASIDE_USED
    +** ^(
    SQLITE_DBSTATUS_LOOKASIDE_USED
    **
    This parameter returns the number of lookaside memory slots currently -** checked out.
    +** checked out.)^ **
    */ #define SQLITE_DBSTATUS_LOOKASIDE_USED 0 /* -** CAPI3REF: Prepared Statement Status {H17550} +** CAPI3REF: Prepared Statement Status ** EXPERIMENTAL ** -** Each prepared statement maintains various +** ^(Each prepared statement maintains various ** [SQLITE_STMTSTATUS_SORT | counters] that measure the number -** of times it has performed specific operations. These counters can +** of times it has performed specific operations.)^ These counters can ** be used to monitor the performance characteristics of the prepared ** statements. For example, if the number of table steps greatly exceeds ** the number of table searches or result rows, that would tend to indicate ** that the prepared statement is using a full table scan rather than ** an index. ** -** This interface is used to retrieve and reset counter values from +** ^(This interface is used to retrieve and reset counter values from ** a [prepared statement]. The first argument is the prepared statement ** object to be interrogated. The second argument ** is an integer code for a specific [SQLITE_STMTSTATUS_SORT | counter] -** to be interrogated. -** The current value of the requested counter is returned. -** If the resetFlg is true, then the counter is reset to zero after this +** to be interrogated.)^ +** ^The current value of the requested counter is returned. +** ^If the resetFlg is true, then the counter is reset to zero after this ** interface call returns. ** ** See also: [sqlite3_status()] and [sqlite3_db_status()]. */ -SQLITE_EXPERIMENTAL int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); +SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_stmt_status(sqlite3_stmt*, int op,int resetFlg); /* -** CAPI3REF: Status Parameters for prepared statements {H17570} +** CAPI3REF: Status Parameters for prepared statements ** EXPERIMENTAL ** ** These preprocessor macros define integer codes that name counter @@ -5137,13 +5141,13 @@ ** **
    **
    SQLITE_STMTSTATUS_FULLSCAN_STEP
    -**
    This is the number of times that SQLite has stepped forward in +**
    ^This is the number of times that SQLite has stepped forward in ** a table as part of a full table scan. Large numbers for this counter ** may indicate opportunities for performance improvement through ** careful use of indices.
    ** **
    SQLITE_STMTSTATUS_SORT
    -**
    This is the number of sort operations that have occurred. +**
    ^This is the number of sort operations that have occurred. ** A non-zero value in this counter may indicate an opportunity to ** improvement performance through careful use of indices.
    ** @@ -5168,114 +5172,128 @@ /* ** CAPI3REF: Application Defined Page Cache. +** KEYWORDS: {page cache} ** EXPERIMENTAL ** -** The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can +** ^(The [sqlite3_config]([SQLITE_CONFIG_PCACHE], ...) interface can ** register an alternative page cache implementation by passing in an -** instance of the sqlite3_pcache_methods structure. The majority of the -** heap memory used by sqlite is used by the page cache to cache data read +** instance of the sqlite3_pcache_methods structure.)^ The majority of the +** heap memory used by SQLite is used by the page cache to cache data read ** from, or ready to be written to, the database file. By implementing a ** custom page cache using this API, an application can control more -** precisely the amount of memory consumed by sqlite, the way in which -** said memory is allocated and released, and the policies used to +** precisely the amount of memory consumed by SQLite, the way in which +** that memory is allocated and released, and the policies used to ** determine exactly which parts of a database file are cached and for ** how long. ** -** The contents of the structure are copied to an internal buffer by sqlite -** within the call to [sqlite3_config]. -** -** The xInit() method is called once for each call to [sqlite3_initialize()] -** (usually only once during the lifetime of the process). It is passed -** a copy of the sqlite3_pcache_methods.pArg value. It can be used to set -** up global structures and mutexes required by the custom page cache -** implementation. The xShutdown() method is called from within -** [sqlite3_shutdown()], if the application invokes this API. It can be used -** to clean up any outstanding resources before process shutdown, if required. -** -** The xCreate() method is used to construct a new cache instance. The +** ^(The contents of the sqlite3_pcache_methods structure are copied to an +** internal buffer by SQLite within the call to [sqlite3_config]. Hence +** the application may discard the parameter after the call to +** [sqlite3_config()] returns.)^ +** +** ^The xInit() method is called once for each call to [sqlite3_initialize()] +** (usually only once during the lifetime of the process). ^(The xInit() +** method is passed a copy of the sqlite3_pcache_methods.pArg value.)^ +** ^The xInit() method can set up up global structures and/or any mutexes +** required by the custom page cache implementation. +** +** ^The xShutdown() method is called from within [sqlite3_shutdown()], +** if the application invokes this API. It can be used to clean up +** any outstanding resources before process shutdown, if required. +** +** ^SQLite holds a [SQLITE_MUTEX_RECURSIVE] mutex when it invokes +** the xInit method, so the xInit method need not be threadsafe. ^The +** xShutdown method is only called from [sqlite3_shutdown()] so it does +** not need to be threadsafe either. All other methods must be threadsafe +** in multithreaded applications. +** +** ^SQLite will never invoke xInit() more than once without an intervening +** call to xShutdown(). +** +** ^The xCreate() method is used to construct a new cache instance. SQLite +** will typically create one cache instance for each open database file, +** though this is not guaranteed. ^The ** first parameter, szPage, is the size in bytes of the pages that must -** be allocated by the cache. szPage will not be a power of two. The -** second argument, bPurgeable, is true if the cache being created will -** be used to cache database pages read from a file stored on disk, or -** false if it is used for an in-memory database. The cache implementation -** does not have to do anything special based on the value of bPurgeable, -** it is purely advisory. +** be allocated by the cache. ^szPage will not be a power of two. ^szPage +** will the page size of the database file that is to be cached plus an +** increment (here called "R") of about 100 or 200. ^SQLite will use the +** extra R bytes on each page to store metadata about the underlying +** database page on disk. The value of R depends +** on the SQLite version, the target platform, and how SQLite was compiled. +** ^R is constant for a particular build of SQLite. ^The second argument to +** xCreate(), bPurgeable, is true if the cache being created will +** be used to cache database pages of a file stored on disk, or +** false if it is used for an in-memory database. ^The cache implementation +** does not have to do anything special based with the value of bPurgeable; +** it is purely advisory. ^On a cache where bPurgeable is false, SQLite will +** never invoke xUnpin() except to deliberately delete a page. +** ^In other words, a cache created with bPurgeable set to false will +** never contain any unpinned pages. ** -** The xCachesize() method may be called at any time by SQLite to set the +** ^(The xCachesize() method may be called at any time by SQLite to set the ** suggested maximum cache-size (number of pages stored by) the cache ** instance passed as the first argument. This is the value configured using -** the SQLite "[PRAGMA cache_size]" command. As with the bPurgeable parameter, -** the implementation is not required to do anything special with this -** value, it is advisory only. +** the SQLite "[PRAGMA cache_size]" command.)^ ^As with the bPurgeable +** parameter, the implementation is not required to do anything with this +** value; it is advisory only. ** -** The xPagecount() method should return the number of pages currently -** stored in the cache supplied as an argument. +** ^The xPagecount() method should return the number of pages currently +** stored in the cache. ** -** The xFetch() method is used to fetch a page and return a pointer to it. -** A 'page', in this context, is a buffer of szPage bytes aligned at an -** 8-byte boundary. The page to be fetched is determined by the key. The +** ^The xFetch() method is used to fetch a page and return a pointer to it. +** ^A 'page', in this context, is a buffer of szPage bytes aligned at an +** 8-byte boundary. ^The page to be fetched is determined by the key. ^The ** mimimum key value is 1. After it has been retrieved using xFetch, the page -** is considered to be pinned. +** is considered to be "pinned". ** -** If the requested page is already in the page cache, then a pointer to -** the cached buffer should be returned with its contents intact. If the -** page is not already in the cache, then the expected behaviour of the -** cache is determined by the value of the createFlag parameter passed -** to xFetch, according to the following table: +** ^If the requested page is already in the page cache, then the page cache +** implementation must return a pointer to the page buffer with its content +** intact. ^(If the requested page is not already in the cache, then the +** behavior of the cache implementation is determined by the value of the +** createFlag parameter passed to xFetch, according to the following table: ** ** -**
    createFlagExpected Behaviour -**
    0NULL should be returned. No new cache entry is created. -**
    1If createFlag is set to 1, this indicates that -** SQLite is holding pinned pages that can be unpinned -** by writing their contents to the database file (a -** relatively expensive operation). In this situation the -** cache implementation has two choices: it can return NULL, -** in which case SQLite will attempt to unpin one or more -** pages before re-requesting the same page, or it can -** allocate a new page and return a pointer to it. If a new -** page is allocated, then the first sizeof(void*) bytes of -** it (at least) must be zeroed before it is returned. -**
    2If createFlag is set to 2, then SQLite is not holding any -** pinned pages associated with the specific cache passed -** as the first argument to xFetch() that can be unpinned. The -** cache implementation should attempt to allocate a new -** cache entry and return a pointer to it. Again, the first -** sizeof(void*) bytes of the page should be zeroed before -** it is returned. If the xFetch() method returns NULL when -** createFlag==2, SQLite assumes that a memory allocation -** failed and returns SQLITE_NOMEM to the user. -**
    +** createFlag Behaviour when page is not already in cache +** 0 Do not allocate a new page. Return NULL. +** 1 Allocate a new page if it easy and convenient to do so. +** Otherwise return NULL. +** 2 Make every effort to allocate a new page. Only return +** NULL if allocating a new page is effectively impossible. +** )^ +** +** SQLite will normally invoke xFetch() with a createFlag of 0 or 1. If +** a call to xFetch() with createFlag==1 returns NULL, then SQLite will +** attempt to unpin one or more cache pages by spilling the content of +** pinned pages to disk and synching the operating system disk cache. After +** attempting to unpin pages, the xFetch() method will be invoked again with +** a createFlag of 2. ** -** xUnpin() is called by SQLite with a pointer to a currently pinned page -** as its second argument. If the third parameter, discard, is non-zero, +** ^xUnpin() is called by SQLite with a pointer to a currently pinned page +** as its second argument. ^(If the third parameter, discard, is non-zero, ** then the page should be evicted from the cache. In this case SQLite ** assumes that the next time the page is retrieved from the cache using -** the xFetch() method, it will be zeroed. If the discard parameter is -** zero, then the page is considered to be unpinned. The cache implementation -** may choose to reclaim (free or recycle) unpinned pages at any time. -** SQLite assumes that next time the page is retrieved from the cache -** it will either be zeroed, or contain the same data that it did when it -** was unpinned. +** the xFetch() method, it will be zeroed.)^ ^If the discard parameter is +** zero, then the page is considered to be unpinned. ^The cache implementation +** may choose to evict unpinned pages at any time. ** -** The cache is not required to perform any reference counting. A single +** ^(The cache is not required to perform any reference counting. A single ** call to xUnpin() unpins the page regardless of the number of prior calls -** to xFetch(). +** to xFetch().)^ ** -** The xRekey() method is used to change the key value associated with the -** page passed as the second argument from oldKey to newKey. If the cache +** ^The xRekey() method is used to change the key value associated with the +** page passed as the second argument from oldKey to newKey. ^If the cache ** previously contains an entry associated with newKey, it should be -** discarded. Any prior cache entry associated with newKey is guaranteed not +** discarded. ^Any prior cache entry associated with newKey is guaranteed not ** to be pinned. ** -** When SQLite calls the xTruncate() method, the cache must discard all +** ^When SQLite calls the xTruncate() method, the cache must discard all ** existing cache entries with page numbers (keys) greater than or equal -** to the value of the iLimit parameter passed to xTruncate(). If any +** to the value of the iLimit parameter passed to xTruncate(). ^If any ** of these pages are pinned, they are implicitly unpinned, meaning that ** they can be safely discarded. ** -** The xDestroy() method is used to delete a cache allocated by xCreate(). -** All resources associated with the specified cache should be freed. After +** ^The xDestroy() method is used to delete a cache allocated by xCreate(). +** All resources associated with the specified cache should be freed. ^After ** calling the xDestroy() method, SQLite considers the [sqlite3_pcache*] ** handle invalid, and will not use it with any other sqlite3_pcache_methods ** functions. @@ -5300,7 +5318,7 @@ ** EXPERIMENTAL ** ** The sqlite3_backup object records state information about an ongoing -** online backup operation. The sqlite3_backup object is created by +** online backup operation. ^The sqlite3_backup object is created by ** a call to [sqlite3_backup_init()] and is destroyed by a call to ** [sqlite3_backup_finish()]. ** @@ -5312,20 +5330,20 @@ ** CAPI3REF: Online Backup API. ** EXPERIMENTAL ** -** This API is used to overwrite the contents of one database with that -** of another. It is useful either for creating backups of databases or +** The backup API copies the content of one database into another. +** It is useful either for creating backups of databases or ** for copying in-memory databases to or from persistent files. ** ** See Also: [Using the SQLite Online Backup API] ** -** Exclusive access is required to the destination database for the -** duration of the operation. However the source database is only -** read-locked while it is actually being read, it is not locked -** continuously for the entire operation. Thus, the backup may be -** performed on a live database without preventing other users from -** writing to the database for an extended period of time. +** ^Exclusive access is required to the destination database for the +** duration of the operation. ^However the source database is only +** read-locked while it is actually being read; it is not locked +** continuously for the entire backup operation. ^Thus, the backup may be +** performed on a live source database without preventing other users from +** reading or writing to the source database while the backup is underway. ** -** To perform a backup operation: +** ^(To perform a backup operation: **
      **
    1. sqlite3_backup_init() is called once to initialize the ** backup, @@ -5333,143 +5351,148 @@ ** the data between the two databases, and finally **
    2. sqlite3_backup_finish() is called to release all resources ** associated with the backup operation. -**
    +** )^ ** There should be exactly one call to sqlite3_backup_finish() for each ** successful call to sqlite3_backup_init(). ** ** sqlite3_backup_init() ** -** The first two arguments passed to [sqlite3_backup_init()] are the database -** handle associated with the destination database and the database name -** used to attach the destination database to the handle. The database name -** is "main" for the main database, "temp" for the temporary database, or -** the name specified as part of the [ATTACH] statement if the destination is -** an attached database. The third and fourth arguments passed to -** sqlite3_backup_init() identify the [database connection] -** and database name used -** to access the source database. The values passed for the source and -** destination [database connection] parameters must not be the same. -** -** If an error occurs within sqlite3_backup_init(), then NULL is returned -** and an error code and error message written into the [database connection] -** passed as the first argument. They may be retrieved using the -** [sqlite3_errcode()], [sqlite3_errmsg()], and [sqlite3_errmsg16()] functions. -** Otherwise, if successful, a pointer to an [sqlite3_backup] object is -** returned. This pointer may be used with the sqlite3_backup_step() and +** ^The D and N arguments to sqlite3_backup_init(D,N,S,M) are the +** [database connection] associated with the destination database +** and the database name, respectively. +** ^The database name is "main" for the main database, "temp" for the +** temporary database, or the name specified after the AS keyword in +** an [ATTACH] statement for an attached database. +** ^The S and M arguments passed to +** sqlite3_backup_init(D,N,S,M) identify the [database connection] +** and database name of the source database, respectively. +** ^The source and destination [database connections] (parameters S and D) +** must be different or else sqlite3_backup_init(D,N,S,M) will file with +** an error. +** +** ^If an error occurs within sqlite3_backup_init(D,N,S,M), then NULL is +** returned and an error code and error message are store3d in the +** destination [database connection] D. +** ^The error code and message for the failed call to sqlite3_backup_init() +** can be retrieved using the [sqlite3_errcode()], [sqlite3_errmsg()], and/or +** [sqlite3_errmsg16()] functions. +** ^A successful call to sqlite3_backup_init() returns a pointer to an +** [sqlite3_backup] object. +** ^The [sqlite3_backup] object may be used with the sqlite3_backup_step() and ** sqlite3_backup_finish() functions to perform the specified backup ** operation. ** ** sqlite3_backup_step() ** -** Function [sqlite3_backup_step()] is used to copy up to nPage pages between -** the source and destination databases, where nPage is the value of the -** second parameter passed to sqlite3_backup_step(). If nPage is a negative -** value, all remaining source pages are copied. If the required pages are -** succesfully copied, but there are still more pages to copy before the -** backup is complete, it returns [SQLITE_OK]. If no error occured and there -** are no more pages to copy, then [SQLITE_DONE] is returned. If an error -** occurs, then an SQLite error code is returned. As well as [SQLITE_OK] and +** ^Function sqlite3_backup_step(B,N) will copy up to N pages between +** the source and destination databases specified by [sqlite3_backup] object B. +** ^If N is negative, all remaining source pages are copied. +** ^If sqlite3_backup_step(B,N) successfully copies N pages and there +** are still more pages to be copied, then the function resturns [SQLITE_OK]. +** ^If sqlite3_backup_step(B,N) successfully finishes copying all pages +** from source to destination, then it returns [SQLITE_DONE]. +** ^If an error occurs while running sqlite3_backup_step(B,N), +** then an [error code] is returned. ^As well as [SQLITE_OK] and ** [SQLITE_DONE], a call to sqlite3_backup_step() may return [SQLITE_READONLY], ** [SQLITE_NOMEM], [SQLITE_BUSY], [SQLITE_LOCKED], or an ** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] extended error code. ** -** As well as the case where the destination database file was opened for -** read-only access, sqlite3_backup_step() may return [SQLITE_READONLY] if +** ^The sqlite3_backup_step() might return [SQLITE_READONLY] if the destination +** database was opened read-only or if ** the destination is an in-memory database with a different page size ** from the source database. ** -** If sqlite3_backup_step() cannot obtain a required file-system lock, then +** ^If sqlite3_backup_step() cannot obtain a required file-system lock, then ** the [sqlite3_busy_handler | busy-handler function] -** is invoked (if one is specified). If the +** is invoked (if one is specified). ^If the ** busy-handler returns non-zero before the lock is available, then -** [SQLITE_BUSY] is returned to the caller. In this case the call to -** sqlite3_backup_step() can be retried later. If the source +** [SQLITE_BUSY] is returned to the caller. ^In this case the call to +** sqlite3_backup_step() can be retried later. ^If the source ** [database connection] ** is being used to write to the source database when sqlite3_backup_step() -** is called, then [SQLITE_LOCKED] is returned immediately. Again, in this -** case the call to sqlite3_backup_step() can be retried later on. If +** is called, then [SQLITE_LOCKED] is returned immediately. ^Again, in this +** case the call to sqlite3_backup_step() can be retried later on. ^(If ** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX], [SQLITE_NOMEM], or ** [SQLITE_READONLY] is returned, then ** there is no point in retrying the call to sqlite3_backup_step(). These -** errors are considered fatal. At this point the application must accept +** errors are considered fatal.)^ The application must accept ** that the backup operation has failed and pass the backup operation handle ** to the sqlite3_backup_finish() to release associated resources. ** -** Following the first call to sqlite3_backup_step(), an exclusive lock is -** obtained on the destination file. It is not released until either +** ^The first call to sqlite3_backup_step() obtains an exclusive lock +** on the destination file. ^The exclusive lock is not released until either ** sqlite3_backup_finish() is called or the backup operation is complete -** and sqlite3_backup_step() returns [SQLITE_DONE]. Additionally, each time -** a call to sqlite3_backup_step() is made a [shared lock] is obtained on -** the source database file. This lock is released before the -** sqlite3_backup_step() call returns. Because the source database is not -** locked between calls to sqlite3_backup_step(), it may be modified mid-way -** through the backup procedure. If the source database is modified by an +** and sqlite3_backup_step() returns [SQLITE_DONE]. ^Every call to +** sqlite3_backup_step() obtains a [shared lock] on the source database that +** lasts for the duration of the sqlite3_backup_step() call. +** ^Because the source database is not locked between calls to +** sqlite3_backup_step(), the source database may be modified mid-way +** through the backup process. ^If the source database is modified by an ** external process or via a database connection other than the one being -** used by the backup operation, then the backup will be transparently -** restarted by the next call to sqlite3_backup_step(). If the source +** used by the backup operation, then the backup will be automatically +** restarted by the next call to sqlite3_backup_step(). ^If the source ** database is modified by the using the same database connection as is used -** by the backup operation, then the backup database is transparently +** by the backup operation, then the backup database is automatically ** updated at the same time. ** ** sqlite3_backup_finish() ** -** Once sqlite3_backup_step() has returned [SQLITE_DONE], or when the -** application wishes to abandon the backup operation, the [sqlite3_backup] -** object should be passed to sqlite3_backup_finish(). This releases all -** resources associated with the backup operation. If sqlite3_backup_step() -** has not yet returned [SQLITE_DONE], then any active write-transaction on the -** destination database is rolled back. The [sqlite3_backup] object is invalid +** When sqlite3_backup_step() has returned [SQLITE_DONE], or when the +** application wishes to abandon the backup operation, the application +** should destroy the [sqlite3_backup] by passing it to sqlite3_backup_finish(). +** ^The sqlite3_backup_finish() interfaces releases all +** resources associated with the [sqlite3_backup] object. +** ^If sqlite3_backup_step() has not yet returned [SQLITE_DONE], then any +** active write-transaction on the destination database is rolled back. +** The [sqlite3_backup] object is invalid ** and may not be used following a call to sqlite3_backup_finish(). ** -** The value returned by sqlite3_backup_finish is [SQLITE_OK] if no error -** occurred, regardless or whether or not sqlite3_backup_step() was called -** a sufficient number of times to complete the backup operation. Or, if -** an out-of-memory condition or IO error occured during a call to -** sqlite3_backup_step() then [SQLITE_NOMEM] or an -** [SQLITE_IOERR_ACCESS | SQLITE_IOERR_XXX] error code -** is returned. In this case the error code and an error message are -** written to the destination [database connection]. +** ^The value returned by sqlite3_backup_finish is [SQLITE_OK] if no +** sqlite3_backup_step() errors occurred, regardless or whether or not +** sqlite3_backup_step() completed. +** ^If an out-of-memory condition or IO error occurred during any prior +** sqlite3_backup_step() call on the same [sqlite3_backup] object, then +** sqlite3_backup_finish() returns the corresponding [error code]. ** -** A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step() is -** not a permanent error and does not affect the return value of +** ^A return of [SQLITE_BUSY] or [SQLITE_LOCKED] from sqlite3_backup_step() +** is not a permanent error and does not affect the return value of ** sqlite3_backup_finish(). ** ** sqlite3_backup_remaining(), sqlite3_backup_pagecount() ** -** Each call to sqlite3_backup_step() sets two values stored internally -** by an [sqlite3_backup] object. The number of pages still to be backed -** up, which may be queried by sqlite3_backup_remaining(), and the total -** number of pages in the source database file, which may be queried by -** sqlite3_backup_pagecount(). +** ^Each call to sqlite3_backup_step() sets two values inside +** the [sqlite3_backup] object: the number of pages still to be backed +** up and the total number of pages in the source databae file. +** The sqlite3_backup_remaining() and sqlite3_backup_pagecount() interfaces +** retrieve these two values, respectively. ** -** The values returned by these functions are only updated by -** sqlite3_backup_step(). If the source database is modified during a backup +** ^The values returned by these functions are only updated by +** sqlite3_backup_step(). ^If the source database is modified during a backup ** operation, then the values are not updated to account for any extra ** pages that need to be updated or the size of the source database file ** changing. ** ** Concurrent Usage of Database Handles ** -** The source [database connection] may be used by the application for other +** ^The source [database connection] may be used by the application for other ** purposes while a backup operation is underway or being initialized. -** If SQLite is compiled and configured to support threadsafe database +** ^If SQLite is compiled and configured to support threadsafe database ** connections, then the source database connection may be used concurrently ** from within other threads. ** -** However, the application must guarantee that the destination database -** connection handle is not passed to any other API (by any thread) after +** However, the application must guarantee that the destination +** [database connection] is not passed to any other API (by any thread) after ** sqlite3_backup_init() is called and before the corresponding call to -** sqlite3_backup_finish(). Unfortunately SQLite does not currently check -** for this, if the application does use the destination [database connection] -** for some other purpose during a backup operation, things may appear to -** work correctly but in fact be subtly malfunctioning. Use of the -** destination database connection while a backup is in progress might -** also cause a mutex deadlock. +** sqlite3_backup_finish(). SQLite does not currently check to see +** if the application incorrectly accesses the destination [database connection] +** and so no error code is reported, but the operations may malfunction +** nevertheless. Use of the destination database connection while a +** backup is in progress might also also cause a mutex deadlock. ** -** Furthermore, if running in [shared cache mode], the application must +** If running in [shared cache mode], the application must ** guarantee that the shared cache used by the destination database ** is not accessed while the backup is running. In practice this means -** that the application must guarantee that the file-system file being +** that the application must guarantee that the disk file being ** backed up to is not accessed by any connection within the process, ** not just the specific connection that was passed to sqlite3_backup_init(). ** @@ -5480,63 +5503,63 @@ ** same time as another thread is invoking sqlite3_backup_step() it is ** possible that they return invalid values. */ -sqlite3_backup *sqlite3_backup_init( +SQLITE_API sqlite3_backup *sqlite3_backup_init( sqlite3 *pDest, /* Destination database handle */ const char *zDestName, /* Destination database name */ sqlite3 *pSource, /* Source database handle */ const char *zSourceName /* Source database name */ ); -int sqlite3_backup_step(sqlite3_backup *p, int nPage); -int sqlite3_backup_finish(sqlite3_backup *p); -int sqlite3_backup_remaining(sqlite3_backup *p); -int sqlite3_backup_pagecount(sqlite3_backup *p); +SQLITE_API int sqlite3_backup_step(sqlite3_backup *p, int nPage); +SQLITE_API int sqlite3_backup_finish(sqlite3_backup *p); +SQLITE_API int sqlite3_backup_remaining(sqlite3_backup *p); +SQLITE_API int sqlite3_backup_pagecount(sqlite3_backup *p); /* ** CAPI3REF: Unlock Notification ** EXPERIMENTAL ** -** When running in shared-cache mode, a database operation may fail with +** ^When running in shared-cache mode, a database operation may fail with ** an [SQLITE_LOCKED] error if the required locks on the shared-cache or ** individual tables within the shared-cache cannot be obtained. See ** [SQLite Shared-Cache Mode] for a description of shared-cache locking. -** This API may be used to register a callback that SQLite will invoke +** ^This API may be used to register a callback that SQLite will invoke ** when the connection currently holding the required lock relinquishes it. -** This API is only available if the library was compiled with the +** ^This API is only available if the library was compiled with the ** [SQLITE_ENABLE_UNLOCK_NOTIFY] C-preprocessor symbol defined. ** ** See Also: [Using the SQLite Unlock Notification Feature]. ** -** Shared-cache locks are released when a database connection concludes +** ^Shared-cache locks are released when a database connection concludes ** its current transaction, either by committing it or rolling it back. ** -** When a connection (known as the blocked connection) fails to obtain a +** ^When a connection (known as the blocked connection) fails to obtain a ** shared-cache lock and SQLITE_LOCKED is returned to the caller, the ** identity of the database connection (the blocking connection) that -** has locked the required resource is stored internally. After an +** has locked the required resource is stored internally. ^After an ** application receives an SQLITE_LOCKED error, it may call the ** sqlite3_unlock_notify() method with the blocked connection handle as ** the first argument to register for a callback that will be invoked -** when the blocking connections current transaction is concluded. The +** when the blocking connections current transaction is concluded. ^The ** callback is invoked from within the [sqlite3_step] or [sqlite3_close] ** call that concludes the blocking connections transaction. ** -** If sqlite3_unlock_notify() is called in a multi-threaded application, +** ^(If sqlite3_unlock_notify() is called in a multi-threaded application, ** there is a chance that the blocking connection will have already ** concluded its transaction by the time sqlite3_unlock_notify() is invoked. ** If this happens, then the specified callback is invoked immediately, -** from within the call to sqlite3_unlock_notify(). +** from within the call to sqlite3_unlock_notify().)^ ** -** If the blocked connection is attempting to obtain a write-lock on a +** ^If the blocked connection is attempting to obtain a write-lock on a ** shared-cache table, and more than one other connection currently holds ** a read-lock on the same table, then SQLite arbitrarily selects one of ** the other connections to use as the blocking connection. ** -** There may be at most one unlock-notify callback registered by a +** ^(There may be at most one unlock-notify callback registered by a ** blocked connection. If sqlite3_unlock_notify() is called when the ** blocked connection already has a registered unlock-notify callback, -** then the new callback replaces the old. If sqlite3_unlock_notify() is +** then the new callback replaces the old.)^ ^If sqlite3_unlock_notify() is ** called with a NULL pointer as its second argument, then any existing -** unlock-notify callback is cancelled. The blocked connections +** unlock-notify callback is cancelled. ^The blocked connections ** unlock-notify callback may also be canceled by closing the blocked ** connection using [sqlite3_close()]. ** @@ -5544,7 +5567,7 @@ ** any sqlite3_xxx API functions from within an unlock-notify callback, a ** crash or deadlock may be the result. ** -** Unless deadlock is detected (see below), sqlite3_unlock_notify() always +** ^Unless deadlock is detected (see below), sqlite3_unlock_notify() always ** returns SQLITE_OK. ** ** Callback Invocation Details @@ -5558,7 +5581,7 @@ ** ** When a blocking connections transaction is concluded, there may be ** more than one blocked connection that has registered for an unlock-notify -** callback. If two or more such blocked connections have specified the +** callback. ^If two or more such blocked connections have specified the ** same callback function, then instead of invoking the callback function ** multiple times, it is invoked once with the set of void* context pointers ** specified by the blocked connections bundled together into an array. @@ -5576,16 +5599,16 @@ ** will proceed and the system may remain deadlocked indefinitely. ** ** To avoid this scenario, the sqlite3_unlock_notify() performs deadlock -** detection. If a given call to sqlite3_unlock_notify() would put the +** detection. ^If a given call to sqlite3_unlock_notify() would put the ** system in a deadlocked state, then SQLITE_LOCKED is returned and no ** unlock-notify callback is registered. The system is said to be in ** a deadlocked state if connection A has registered for an unlock-notify ** callback on the conclusion of connection B's transaction, and connection ** B has itself registered for an unlock-notify callback when connection -** A's transaction is concluded. Indirect deadlock is also detected, so +** A's transaction is concluded. ^Indirect deadlock is also detected, so ** the system is also considered to be deadlocked if connection B has ** registered for an unlock-notify callback on the conclusion of connection -** C's transaction, where connection C is waiting on connection A. Any +** C's transaction, where connection C is waiting on connection A. ^Any ** number of levels of indirection are allowed. ** ** The "DROP TABLE" Exception @@ -5601,17 +5624,29 @@ ** or "DROP INDEX" query, an infinite loop might be the result. ** ** One way around this problem is to check the extended error code returned -** by an sqlite3_step() call. If there is a blocking connection, then the +** by an sqlite3_step() call. ^(If there is a blocking connection, then the ** extended error code is set to SQLITE_LOCKED_SHAREDCACHE. Otherwise, in ** the special "DROP TABLE/INDEX" case, the extended error code is just -** SQLITE_LOCKED. +** SQLITE_LOCKED.)^ */ -int sqlite3_unlock_notify( +SQLITE_API int sqlite3_unlock_notify( sqlite3 *pBlocked, /* Waiting connection */ void (*xNotify)(void **apArg, int nArg), /* Callback function to invoke */ void *pNotifyArg /* Argument to pass to xNotify */ ); + +/* +** CAPI3REF: String Comparison +** EXPERIMENTAL +** +** ^The [sqlite3_strnicmp()] API allows applications and extensions to +** compare the contents of two buffers containing UTF-8 strings in a +** case-indendent fashion, using the same definition of case independence +** that SQLite uses internally when comparing identifiers. +*/ +SQLITE_API int sqlite3_strnicmp(const char *, const char *, int); + /* ** Undo the hack that converts floating point types to integer for ** builds on processors without floating point support. @@ -5624,3 +5659,4 @@ } /* End of the 'extern "C"' block */ #endif #endif + diff -Nru firefox-3.6.3+nobinonly/mozilla/db/sqlite3/src/sqlite.def firefox-3.6.4+build1+nobinonly/mozilla/db/sqlite3/src/sqlite.def --- firefox-3.6.3+nobinonly/mozilla/db/sqlite3/src/sqlite.def 2010-04-02 16:57:53.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/db/sqlite3/src/sqlite.def 2010-04-16 17:31:40.000000000 +0100 @@ -83,6 +83,7 @@ sqlite3_create_module sqlite3_data_count sqlite3_db_handle + sqlite3_db_mutex sqlite3_declare_vtab sqlite3_enable_load_extension sqlite3_enable_shared_cache diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/base/nsDOMClassInfo.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/base/nsDOMClassInfo.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/base/nsDOMClassInfo.cpp 2010-04-02 16:57:54.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/base/nsDOMClassInfo.cpp 2010-04-16 17:31:41.000000000 +0100 @@ -3598,6 +3598,7 @@ DOM_CLASSINFO_MAP_BEGIN(DataContainerEvent, nsIDOMDataContainerEvent) DOM_CLASSINFO_MAP_ENTRY(nsIDOMDataContainerEvent) + DOM_CLASSINFO_EVENT_MAP_ENTRIES DOM_CLASSINFO_MAP_END DOM_CLASSINFO_MAP_BEGIN(MessageEvent, nsIDOMMessageEvent) @@ -9753,7 +9754,7 @@ // If obj is a native wrapper, or if there's no plugin around for // this object, throw. - if (!ObjectIsNativeWrapper(cx, obj) || !pi) { + if (ObjectIsNativeWrapper(cx, obj) || !pi) { return NS_ERROR_NOT_AVAILABLE; } diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/base/nsFocusManager.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/base/nsFocusManager.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/base/nsFocusManager.cpp 2010-04-02 16:57:54.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/base/nsFocusManager.cpp 2010-04-16 17:31:41.000000000 +0100 @@ -1630,6 +1630,26 @@ mFirstFocusEvent = nsnull; } +class FocusBlurEvent : public nsRunnable +{ +public: + FocusBlurEvent(nsISupports* aTarget, PRUint32 aType, + nsPresContext* aContext) + : mTarget(aTarget), mType(aType), mContext(aContext) + {} + + NS_IMETHOD Run() + { + nsEvent event(PR_TRUE, mType); + event.flags |= NS_EVENT_FLAG_CANT_BUBBLE; + return nsEventDispatcher::Dispatch(mTarget, mContext, &event); + } + + nsCOMPtr mTarget; + PRUint32 mType; + nsCOMPtr mContext; +}; + void nsFocusManager::SendFocusOrBlurEvent(PRUint32 aType, nsIPresShell* aPresShell, @@ -1661,13 +1681,8 @@ return; } - nsCOMPtr presContext = aPresShell->GetPresContext(); - - nsEventStatus status = nsEventStatus_eIgnore; - nsEvent event(PR_TRUE, aType); - event.flags |= NS_EVENT_FLAG_CANT_BUBBLE; - - nsEventDispatcher::Dispatch(aTarget, presContext, &event, nsnull, &status); + nsContentUtils::AddScriptRunner( + new FocusBlurEvent(aTarget, aType, aPresShell->GetPresContext())); } void diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/base/nsGlobalWindow.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/base/nsGlobalWindow.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/base/nsGlobalWindow.cpp 2010-04-02 16:57:54.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/base/nsGlobalWindow.cpp 2010-04-16 17:31:41.000000000 +0100 @@ -6263,6 +6263,22 @@ return NS_OK; } +class CommandDispatcher : public nsRunnable +{ +public: + CommandDispatcher(nsIDOMXULCommandDispatcher* aDispatcher, + const nsAString& aAction) + : mDispatcher(aDispatcher), mAction(aAction) {} + + NS_IMETHOD Run() + { + return mDispatcher->UpdateCommands(mAction); + } + + nsCOMPtr mDispatcher; + nsString mAction; +}; + NS_IMETHODIMP nsGlobalWindow::UpdateCommands(const nsAString& anAction) { @@ -6277,7 +6293,10 @@ // Retrieve the command dispatcher and call updateCommands on it. nsCOMPtr xulCommandDispatcher; xulDoc->GetCommandDispatcher(getter_AddRefs(xulCommandDispatcher)); - xulCommandDispatcher->UpdateCommands(anAction); + if (xulCommandDispatcher) { + nsContentUtils::AddScriptRunner(new CommandDispatcher(xulCommandDispatcher, + anAction)); + } } return NS_OK; diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/base/nsJSEnvironment.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/base/nsJSEnvironment.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/base/nsJSEnvironment.cpp 2010-04-02 16:57:54.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/base/nsJSEnvironment.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -418,6 +418,129 @@ return called; } +class ScriptErrorEvent : public nsRunnable +{ +public: + ScriptErrorEvent(nsIScriptGlobalObject* aScriptGlobal, + PRUint32 aLineNr, PRUint32 aColumn, PRUint32 aFlags, + const nsAString& aErrorMsg, + const nsAString& aFileName, + const nsAString& aSourceLine, + PRBool aDispatchEvent) + : mScriptGlobal(aScriptGlobal), mLineNr(aLineNr), mColumn(aColumn), + mFlags(aFlags), mErrorMsg(aErrorMsg), mFileName(aFileName), + mSourceLine(aSourceLine), mDispatchEvent(aDispatchEvent) {} + + NS_IMETHOD Run() + { + nsEventStatus status = nsEventStatus_eIgnore; + // First, notify the DOM that we have a script error. + if (mDispatchEvent) { + nsCOMPtr win(do_QueryInterface(mScriptGlobal)); + nsIDocShell* docShell = win ? win->GetDocShell() : nsnull; + if (docShell && + !JSREPORT_IS_WARNING(mFlags) && + !sHandlingScriptError) { + sHandlingScriptError = PR_TRUE; // Recursion prevention + + nsCOMPtr presContext; + docShell->GetPresContext(getter_AddRefs(presContext)); + + if (presContext) { + nsScriptErrorEvent errorevent(PR_TRUE, NS_LOAD_ERROR); + + errorevent.fileName = mFileName.get(); + + nsCOMPtr sop(do_QueryInterface(win)); + NS_ENSURE_STATE(sop); + nsIPrincipal* p = sop->GetPrincipal(); + NS_ENSURE_STATE(p); + + PRBool sameOrigin = mFileName.IsVoid(); + + if (p && !sameOrigin) { + nsCOMPtr errorURI; + NS_NewURI(getter_AddRefs(errorURI), mFileName); + if (errorURI) { + // FIXME: Once error reports contain the origin of the + // error (principals) we should change this to do the + // security check based on the principals and not + // URIs. See bug 387476. + sameOrigin = NS_SUCCEEDED(p->CheckMayLoad(errorURI, PR_FALSE)); + } + } + + NS_NAMED_LITERAL_STRING(xoriginMsg, "Script error."); + if (sameOrigin) { + errorevent.errorMsg = mErrorMsg.get(); + errorevent.lineNr = mLineNr; + } else { + NS_WARNING("Not same origin error!"); + errorevent.errorMsg = xoriginMsg.get(); + errorevent.lineNr = 0; + } + + nsEventDispatcher::Dispatch(win, presContext, &errorevent, nsnull, + &status); + } + + sHandlingScriptError = PR_FALSE; + } + } + + if (status != nsEventStatus_eConsumeNoDefault) { + // Make an nsIScriptError and populate it with information from + // this error. + nsCOMPtr errorObject = + do_CreateInstance("@mozilla.org/scripterror;1"); + + if (errorObject != nsnull) { + nsresult rv = NS_ERROR_NOT_AVAILABLE; + + // Set category to chrome or content + nsCOMPtr scriptPrincipal = + do_QueryInterface(mScriptGlobal); + NS_ASSERTION(scriptPrincipal, "Global objects must implement " + "nsIScriptObjectPrincipal"); + nsCOMPtr systemPrincipal; + sSecurityManager->GetSystemPrincipal(getter_AddRefs(systemPrincipal)); + const char * category = + scriptPrincipal->GetPrincipal() == systemPrincipal + ? "chrome javascript" + : "content javascript"; + + rv = errorObject->Init(mErrorMsg.get(), mFileName.get(), + mSourceLine.get(), + mLineNr, mColumn, mFlags, + category); + + if (NS_SUCCEEDED(rv)) { + nsCOMPtr consoleService = + do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) { + consoleService->LogMessage(errorObject); + } + } + } + } + return NS_OK; + } + + + nsCOMPtr mScriptGlobal; + PRUint32 mLineNr; + PRUint32 mColumn; + PRUint32 mFlags; + nsString mErrorMsg; + nsString mFileName; + nsString mSourceLine; + PRBool mDispatchEvent; + + static PRBool sHandlingScriptError; +}; + +PRBool ScriptErrorEvent::sHandlingScriptError = PR_FALSE; + // NOTE: This function could be refactored to use the above. The only reason // it has not been done is that the code below only fills the error event // after it has a good nsPresContext - whereas using the above function @@ -457,8 +580,6 @@ // XXX this means we are not going to get error reports on non DOM contexts nsIScriptContext *context = nsJSUtils::GetDynamicScriptContext(cx); - nsEventStatus status = nsEventStatus_eIgnore; - // Note: we must do this before running any more code on cx (if cx is the // dynamic script context). ::JS_ClearPendingException(cx); @@ -468,9 +589,11 @@ if (globalObject) { nsAutoString fileName, msg; - NS_NAMED_LITERAL_STRING(xoriginMsg, "Script error."); - - fileName.AssignWithConversion(report->filename); + if (!report->filename) { + fileName.SetIsVoid(PR_TRUE); + } else { + fileName.AssignWithConversion(report->filename); + } const PRUnichar *m = reinterpret_cast (report->ucmessage); @@ -482,107 +605,20 @@ msg.AssignWithConversion(message); } - // First, notify the DOM that we have a script error. + /* We do not try to report Out Of Memory via a dom * event because the dom event handler would encounter * an OOM exception trying to process the event, and * then we'd need to generate a new OOM event for that * new OOM instance -- this isn't pretty. */ - { - // Scope to make sure we're not using |win| in the rest of - // this function when we should be using |globalObject|. We - // only need |win| for the event dispatch. - nsCOMPtr win(do_QueryInterface(globalObject)); - nsIDocShell *docShell = win ? win->GetDocShell() : nsnull; - if (docShell && - (report->errorNumber != JSMSG_OUT_OF_MEMORY && - !JSREPORT_IS_WARNING(report->flags))) { - static PRInt32 errorDepth; // Recursion prevention - ++errorDepth; - - nsCOMPtr presContext; - docShell->GetPresContext(getter_AddRefs(presContext)); - - if (presContext && errorDepth < 2) { - nsScriptErrorEvent errorevent(PR_TRUE, NS_LOAD_ERROR); - - errorevent.fileName = fileName.get(); - - nsCOMPtr sop(do_QueryInterface(win)); - nsIPrincipal *p = sop->GetPrincipal(); - - PRBool sameOrigin = (report->filename == nsnull); - - if (p && !sameOrigin) { - nsCOMPtr errorURI; - NS_NewURI(getter_AddRefs(errorURI), report->filename); - - if (errorURI) { - // FIXME: Once error reports contain the origin of the - // error (principals) we should change this to do the - // security check based on the principals and not - // URIs. See bug 387476. - sameOrigin = NS_SUCCEEDED(p->CheckMayLoad(errorURI, PR_FALSE)); - } - } - - if (sameOrigin) { - errorevent.errorMsg = msg.get(); - errorevent.lineNr = report->lineno; - } else { - errorevent.errorMsg = xoriginMsg.get(); - errorevent.lineNr = 0; - } - - // Dispatch() must be synchronous for the recursion block - // (errorDepth) to work. - nsEventDispatcher::Dispatch(win, presContext, &errorevent, nsnull, - &status); - } - - --errorDepth; - } - } - - if (status != nsEventStatus_eConsumeNoDefault) { - // Make an nsIScriptError and populate it with information from - // this error. - nsCOMPtr errorObject = - do_CreateInstance("@mozilla.org/scripterror;1"); - - if (errorObject != nsnull) { - nsresult rv = NS_ERROR_NOT_AVAILABLE; - - // Set category to chrome or content - nsCOMPtr scriptPrincipal = - do_QueryInterface(globalObject); - NS_ASSERTION(scriptPrincipal, "Global objects must implement " - "nsIScriptObjectPrincipal"); - nsCOMPtr systemPrincipal; - sSecurityManager->GetSystemPrincipal(getter_AddRefs(systemPrincipal)); - const char * category = - scriptPrincipal->GetPrincipal() == systemPrincipal - ? "chrome javascript" - : "content javascript"; - - PRUint32 column = report->uctokenptr - report->uclinebuf; - - rv = errorObject->Init(msg.get(), fileName.get(), - reinterpret_cast - (report->uclinebuf), - report->lineno, column, report->flags, - category); - - if (NS_SUCCEEDED(rv)) { - nsCOMPtr consoleService = - do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv); - if (NS_SUCCEEDED(rv)) { - consoleService->LogMessage(errorObject); - } - } - } - } + nsAutoString sourceLine; + sourceLine.Assign(reinterpret_cast(report->uclinebuf)); + nsContentUtils::AddScriptRunner( + new ScriptErrorEvent(globalObject, report->lineno, + report->uctokenptr - report->uclinebuf, + report->flags, msg, fileName, sourceLine, + report->errorNumber != JSMSG_OUT_OF_MEMORY)); } } @@ -607,8 +643,7 @@ } else { error.Append(message); } - if (status != nsEventStatus_eIgnore && !JSREPORT_IS_WARNING(report->flags)) - error.Append(" Error was suppressed by event handler\n"); + fprintf(stderr, "%s\n", error.get()); fflush(stderr); #endif diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/dom/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/dom/Makefile.in 2010-04-02 16:57:54.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/Makefile.in 2010-04-16 17:31:41.000000000 +0100 @@ -79,6 +79,7 @@ base \ src \ locales \ + plugins \ $(NULL) ifdef ENABLE_TESTS diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/AStream.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/AStream.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/AStream.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/AStream.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,59 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugins. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_plugins_AStream_h +#define mozilla_plugins_AStream_h + +namespace mozilla { +namespace plugins { + +/** + * When we are passed NPStream->{ndata,pdata} in {NPN,NPP}_DestroyStream, we + * don't know whether it's a plugin stream or a browser stream. This abstract + * class lets us cast to the right type of object and send the appropriate + * message. + */ +class AStream +{ +public: + virtual bool IsBrowserStream() = 0; +}; + +} // namespace plugins +} // namespace mozilla + +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/BrowserStreamChild.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/BrowserStreamChild.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/BrowserStreamChild.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/BrowserStreamChild.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,319 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Benjamin Smedberg + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "BrowserStreamChild.h" +#include "PluginInstanceChild.h" +#include "StreamNotifyChild.h" + +namespace mozilla { +namespace plugins { + +BrowserStreamChild::BrowserStreamChild(PluginInstanceChild* instance, + const nsCString& url, + const uint32_t& length, + const uint32_t& lastmodified, + StreamNotifyChild* notifyData, + const nsCString& headers, + const nsCString& mimeType, + const bool& seekable, + NPError* rv, + uint16_t* stype) + : mInstance(instance) + , mStreamStatus(kStreamOpen) + , mDestroyPending(NOT_DESTROYED) + , mNotifyPending(false) + , mInstanceDying(false) + , mState(CONSTRUCTING) + , mURL(url) + , mHeaders(headers) + , mStreamNotify(notifyData) + , mDeliveryTracker(this) +{ + PLUGIN_LOG_DEBUG(("%s (%s, %i, %i, %p, %s, %s)", FULLFUNCTION, + url.get(), length, lastmodified, (void*) notifyData, + headers.get(), mimeType.get())); + + AssertPluginThread(); + + memset(&mStream, 0, sizeof(mStream)); + mStream.ndata = static_cast(this); + mStream.url = NullableStringGet(mURL); + mStream.end = length; + mStream.lastmodified = lastmodified; + mStream.headers = NullableStringGet(mHeaders); + if (notifyData) + mStream.notifyData = notifyData->mClosure; +} + +NPError +BrowserStreamChild::StreamConstructed( + const nsCString& mimeType, + const bool& seekable, + uint16_t* stype) +{ + NPError rv = NPERR_NO_ERROR; + + *stype = NP_NORMAL; + rv = mInstance->mPluginIface->newstream( + &mInstance->mData, const_cast(NullableStringGet(mimeType)), + &mStream, seekable, stype); + if (rv != NPERR_NO_ERROR) { + mState = DELETING; + mStreamNotify = NULL; + } + else { + mState = ALIVE; + + if (mStreamNotify) + mStreamNotify->SetAssociatedStream(this); + } + + return rv; +} + +BrowserStreamChild::~BrowserStreamChild() +{ + NS_ASSERTION(!mStreamNotify, "Should have nulled it by now!"); +} + +bool +BrowserStreamChild::RecvWrite(const int32_t& offset, + const Buffer& data, + const uint32_t& newlength) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + + AssertPluginThread(); + + if (ALIVE != mState) + NS_RUNTIMEABORT("Unexpected state: received data after NPP_DestroyStream?"); + + if (kStreamOpen != mStreamStatus) + return true; + + mStream.end = newlength; + + NS_ASSERTION(data.Length() > 0, "Empty data"); + + PendingData* newdata = mPendingData.AppendElement(); + newdata->offset = offset; + newdata->data = data; + newdata->curpos = 0; + + EnsureDeliveryPending(); + + return true; +} + +bool +BrowserStreamChild::AnswerNPP_StreamAsFile(const nsCString& fname) +{ + PLUGIN_LOG_DEBUG(("%s (fname=%s)", FULLFUNCTION, fname.get())); + + AssertPluginThread(); + + if (ALIVE != mState) + NS_RUNTIMEABORT("Unexpected state: received file after NPP_DestroyStream?"); + + if (kStreamOpen != mStreamStatus) + return true; + + mInstance->mPluginIface->asfile(&mInstance->mData, &mStream, + fname.get()); + return true; +} + +bool +BrowserStreamChild::RecvNPP_DestroyStream(const NPReason& reason) +{ + PLUGIN_LOG_DEBUG_METHOD; + + if (ALIVE != mState) + NS_RUNTIMEABORT("Unexpected state: recevied NPP_DestroyStream twice?"); + + mState = DYING; + mDestroyPending = DESTROY_PENDING; + if (NPRES_DONE != reason) + mStreamStatus = reason; + + EnsureDeliveryPending(); + return true; +} + +bool +BrowserStreamChild::Recv__delete__() +{ + AssertPluginThread(); + + if (DELETING != mState) + NS_RUNTIMEABORT("Bad state, not DELETING"); + + return true; +} + +NPError +BrowserStreamChild::NPN_RequestRead(NPByteRange* aRangeList) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + + AssertPluginThread(); + + if (ALIVE != mState || kStreamOpen != mStreamStatus) + return NPERR_GENERIC_ERROR; + + IPCByteRanges ranges; + for (; aRangeList; aRangeList = aRangeList->next) { + IPCByteRange br = {aRangeList->offset, aRangeList->length}; + ranges.push_back(br); + } + + NPError result; + CallNPN_RequestRead(ranges, &result); + return result; +} + +void +BrowserStreamChild::NPN_DestroyStream(NPReason reason) +{ + mStreamStatus = reason; + if (ALIVE == mState) + SendNPN_DestroyStream(reason); + + EnsureDeliveryPending(); +} + +void +BrowserStreamChild::EnsureDeliveryPending() +{ + MessageLoop::current()->PostTask(FROM_HERE, + mDeliveryTracker.NewRunnableMethod(&BrowserStreamChild::Deliver)); +} + +void +BrowserStreamChild::Deliver() +{ + while (kStreamOpen == mStreamStatus && mPendingData.Length()) { + if (DeliverPendingData() && kStreamOpen == mStreamStatus) { + SetSuspendedTimer(); + return; + } + } + ClearSuspendedTimer(); + + NS_ASSERTION(kStreamOpen != mStreamStatus || 0 == mPendingData.Length(), + "Exit out of the data-delivery loop with pending data"); + mPendingData.Clear(); + + if (DESTROY_PENDING == mDestroyPending) { + mDestroyPending = DESTROYED; + if (mState != DYING) + NS_RUNTIMEABORT("mDestroyPending but state not DYING"); + + NS_ASSERTION(NPRES_DONE != mStreamStatus, "Success status set too early!"); + if (kStreamOpen == mStreamStatus) + mStreamStatus = NPRES_DONE; + + (void) mInstance->mPluginIface + ->destroystream(&mInstance->mData, &mStream, mStreamStatus); + } + if (DESTROYED == mDestroyPending && mNotifyPending) { + NS_ASSERTION(mStreamNotify, "mDestroyPending but no mStreamNotify?"); + + mNotifyPending = false; + mStreamNotify->NPP_URLNotify(mStreamStatus); + delete mStreamNotify; + mStreamNotify = NULL; + } + if (DYING == mState && DESTROYED == mDestroyPending + && !mStreamNotify && !mInstanceDying) { + SendStreamDestroyed(); + mState = DELETING; + } +} + +bool +BrowserStreamChild::DeliverPendingData() +{ + if (mState != ALIVE && mState != DYING) + NS_RUNTIMEABORT("Unexpected state"); + + NS_ASSERTION(mPendingData.Length(), "Called from Deliver with empty pending"); + + while (mPendingData[0].curpos < mPendingData[0].data.Length()) { + int32_t r = mInstance->mPluginIface->writeready(&mInstance->mData, &mStream); + if (kStreamOpen != mStreamStatus) + return false; + if (0 == r) // plugin wants to suspend delivery + return true; + + r = mInstance->mPluginIface->write( + &mInstance->mData, &mStream, + mPendingData[0].offset + mPendingData[0].curpos, // offset + mPendingData[0].data.Length() - mPendingData[0].curpos, // length + const_cast(mPendingData[0].data.BeginReading() + mPendingData[0].curpos)); + if (kStreamOpen != mStreamStatus) + return false; + if (0 == r) + return true; + if (r < 0) { // error condition + NPN_DestroyStream(NPRES_NETWORK_ERR); + return false; + } + mPendingData[0].curpos += r; + } + mPendingData.RemoveElementAt(0); + return false; +} + +void +BrowserStreamChild::SetSuspendedTimer() +{ + if (mSuspendedTimer.IsRunning()) + return; + mSuspendedTimer.Start( + base::TimeDelta::FromMilliseconds(100), // 100ms copied from Mozilla plugin host + this, &BrowserStreamChild::Deliver); +} + +void +BrowserStreamChild::ClearSuspendedTimer() +{ + mSuspendedTimer.Stop(); +} + +} /* namespace plugins */ +} /* namespace mozilla */ diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/BrowserStreamChild.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/BrowserStreamChild.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/BrowserStreamChild.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/BrowserStreamChild.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,205 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Benjamin Smedberg + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_plugins_BrowserStreamChild_h +#define mozilla_plugins_BrowserStreamChild_h 1 + +#include "mozilla/plugins/PBrowserStreamChild.h" +#include "mozilla/plugins/AStream.h" + +namespace mozilla { +namespace plugins { + +class PluginInstanceChild; +class StreamNotifyChild; + +class BrowserStreamChild : public PBrowserStreamChild, public AStream +{ +public: + BrowserStreamChild(PluginInstanceChild* instance, + const nsCString& url, + const uint32_t& length, + const uint32_t& lastmodified, + StreamNotifyChild* notifyData, + const nsCString& headers, + const nsCString& mimeType, + const bool& seekable, + NPError* rv, + uint16_t* stype); + virtual ~BrowserStreamChild(); + + NS_OVERRIDE virtual bool IsBrowserStream() { return true; } + + NPError StreamConstructed( + const nsCString& mimeType, + const bool& seekable, + uint16_t* stype); + + virtual bool RecvWrite(const int32_t& offset, + const Buffer& data, + const uint32_t& newsize); + virtual bool AnswerNPP_StreamAsFile(const nsCString& fname); + virtual bool RecvNPP_DestroyStream(const NPReason& reason); + virtual bool Recv__delete__(); + + void EnsureCorrectInstance(PluginInstanceChild* i) + { + if (i != mInstance) + NS_RUNTIMEABORT("Incorrect stream instance"); + } + void EnsureCorrectStream(NPStream* s) + { + if (s != &mStream) + NS_RUNTIMEABORT("Incorrect stream data"); + } + + NPError NPN_RequestRead(NPByteRange* aRangeList); + void NPN_DestroyStream(NPReason reason); + + void NotifyPending() { + NS_ASSERTION(!mNotifyPending, "Pending twice?"); + mNotifyPending = true; + EnsureDeliveryPending(); + } + + /** + * During instance destruction, artificially cancel all outstanding streams. + * + * @return false if we are already in the DELETING state. + */ + bool InstanceDying() { + if (DELETING == mState) + return false; + + mInstanceDying = true; + return true; + } + + void FinishDelivery() { + NS_ASSERTION(mInstanceDying, "Should only be called after InstanceDying"); + NS_ASSERTION(DELETING != mState, "InstanceDying didn't work?"); + mStreamStatus = NPRES_USER_BREAK; + Deliver(); + NS_ASSERTION(!mStreamNotify, "Didn't deliver NPN_URLNotify?"); + } + +private: + friend class StreamNotifyChild; + using PBrowserStreamChild::SendNPN_DestroyStream; + + /** + * Post an event to ensure delivery of pending data/destroy/urlnotify events + * outside of the current RPC stack. + */ + void EnsureDeliveryPending(); + + /** + * Deliver data, destruction, notify scheduling + * or cancelling the suspended timer as needed. + */ + void Deliver(); + + /** + * Deliver one chunk of pending data. + * @return true if the plugin indicated a pause was necessary + */ + bool DeliverPendingData(); + + void SetSuspendedTimer(); + void ClearSuspendedTimer(); + + PluginInstanceChild* mInstance; + NPStream mStream; + + static const NPReason kStreamOpen = -1; + + /** + * The plugin's notion of whether a stream has been "closed" (no more + * data delivery) differs from the plugin host due to asynchronous delivery + * of data and NPN_DestroyStream. While the plugin-visible stream is open, + * mStreamStatus should be kStreamOpen (-1). mStreamStatus will be a + * failure code if either the parent or child indicates stream failure. + */ + NPReason mStreamStatus; + + /** + * Delivery of NPP_DestroyStream and NPP_URLNotify must be postponed until + * all data has been delivered. + */ + enum { + NOT_DESTROYED, // NPP_DestroyStream not yet received + DESTROY_PENDING, // NPP_DestroyStream received, not yet delivered + DESTROYED // NPP_DestroyStream delivered, NPP_URLNotify may still be pending + } mDestroyPending; + bool mNotifyPending; + + // When NPP_Destroy is called for our instance (manager), this flag is set + // cancels the stream and avoids sending StreamDestroyed. + bool mInstanceDying; + + enum { + CONSTRUCTING, + ALIVE, + DYING, + DELETING + } mState; + nsCString mURL; + nsCString mHeaders; + StreamNotifyChild* mStreamNotify; + + struct PendingData + { + int32_t offset; + Buffer data; + int32_t curpos; + }; + nsTArray mPendingData; + + /** + * Asynchronous RecvWrite messages are never delivered to the plugin + * immediately, because that may be in the midst of an unexpected RPC + * stack frame. It instead posts a runnable using this tracker to cancel + * in case we are destroyed. + */ + ScopedRunnableMethodFactory mDeliveryTracker; + base::RepeatingTimer mSuspendedTimer; +}; + +} // namespace plugins +} // namespace mozilla + +#endif /* mozilla_plugins_BrowserStreamChild_h */ diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/BrowserStreamParent.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/BrowserStreamParent.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/BrowserStreamParent.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/BrowserStreamParent.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,142 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ + +#include "BrowserStreamParent.h" +#include "PluginInstanceParent.h" + +// How much data are we willing to send across the wire +// in one chunk? +static const int32_t kSendDataChunk = 0x1000; + +namespace mozilla { +namespace plugins { + +BrowserStreamParent::BrowserStreamParent(PluginInstanceParent* npp, + NPStream* stream) + : mNPP(npp) + , mStream(stream) + , mState(ALIVE) +{ + mStream->pdata = static_cast(this); +} + +BrowserStreamParent::~BrowserStreamParent() +{ +} + +bool +BrowserStreamParent::AnswerNPN_RequestRead(const IPCByteRanges& ranges, + NPError* result) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + + switch (mState) { + case ALIVE: + break; + + case DYING: + *result = NPERR_GENERIC_ERROR; + return true; + + default: + NS_ERROR("Unexpected state"); + return false; + } + + if (!mStream) + return false; + + if (ranges.size() > PR_INT32_MAX) + return false; + + nsAutoArrayPtr rp(new NPByteRange[ranges.size()]); + for (PRUint32 i = 0; i < ranges.size(); ++i) { + rp[i].offset = ranges[i].offset; + rp[i].length = ranges[i].length; + rp[i].next = &rp[i + 1]; + } + rp[ranges.size() - 1].next = NULL; + + *result = mNPP->mNPNIface->requestread(mStream, rp); + return true; +} + +bool +BrowserStreamParent::RecvNPN_DestroyStream(const NPReason& reason) +{ + switch (mState) { + case ALIVE: + break; + + case DYING: + return true; + + default: + NS_ERROR("Unexpected state"); + return false; + }; + + mNPP->mNPNIface->destroystream(mNPP->mNPP, mStream, reason); + return true; +} + +void +BrowserStreamParent::NPP_DestroyStream(NPReason reason) +{ + NS_ASSERTION(ALIVE == mState, "NPP_DestroyStream called twice?"); + mState = DYING; + SendNPP_DestroyStream(reason); +} + +bool +BrowserStreamParent::RecvStreamDestroyed() +{ + if (DYING != mState) { + NS_ERROR("Unexpected state"); + return false; + } + + mState = DELETING; + Send__delete__(this); + return true; +} + +int32_t +BrowserStreamParent::WriteReady() +{ + return kSendDataChunk; +} + +int32_t +BrowserStreamParent::Write(int32_t offset, + int32_t len, + void* buffer) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + + NS_ASSERTION(ALIVE == mState, "Sending data after NPP_DestroyStream?"); + NS_ASSERTION(len > 0, "Non-positive length to NPP_Write"); + + if (len > kSendDataChunk) + len = kSendDataChunk; + + SendWrite(offset, + nsCString(static_cast(buffer), len), + mStream->end); + + return len; +} + +void +BrowserStreamParent::StreamAsFile(const char* fname) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + + NS_ASSERTION(ALIVE == mState, + "Calling streamasfile after NPP_DestroyStream?"); + + CallNPP_StreamAsFile(nsCString(fname)); + return; +} + +} // namespace plugins +} // namespace mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/BrowserStreamParent.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/BrowserStreamParent.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/BrowserStreamParent.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/BrowserStreamParent.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,90 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugins. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_plugins_BrowserStreamParent_h +#define mozilla_plugins_BrowserStreamParent_h + +#include "mozilla/plugins/PBrowserStreamParent.h" +#include "mozilla/plugins/AStream.h" + +namespace mozilla { +namespace plugins { + +class PluginInstanceParent; + +class BrowserStreamParent : public PBrowserStreamParent, public AStream +{ + friend class PluginModuleParent; + friend class PluginInstanceParent; + +public: + BrowserStreamParent(PluginInstanceParent* npp, + NPStream* stream); + virtual ~BrowserStreamParent(); + + NS_OVERRIDE virtual bool IsBrowserStream() { return true; } + + virtual bool AnswerNPN_RequestRead(const IPCByteRanges& ranges, + NPError* result); + + virtual bool RecvNPN_DestroyStream(const NPReason& reason); + + virtual bool RecvStreamDestroyed(); + + int32_t WriteReady(); + int32_t Write(int32_t offset, int32_t len, void* buffer); + void StreamAsFile(const char* fname); + + void NPP_DestroyStream(NPReason reason); + +private: + using PBrowserStreamParent::SendNPP_DestroyStream; + + PluginInstanceParent* mNPP; + NPStream* mStream; + + enum { + ALIVE, + DYING, + DELETING + } mState; +}; + +} // namespace plugins +} // namespace mozilla + +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/ChildAsyncCall.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/ChildAsyncCall.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/ChildAsyncCall.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/ChildAsyncCall.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Firefox. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation . + * + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "ChildAsyncCall.h" +#include "PluginInstanceChild.h" + +namespace mozilla { +namespace plugins { + +ChildAsyncCall::ChildAsyncCall(PluginInstanceChild* instance, + PluginThreadCallback aFunc, void* aUserData) + : mInstance(instance) + , mFunc(aFunc) + , mData(aUserData) +{ +} + +void +ChildAsyncCall::Cancel() +{ + mInstance = NULL; + mFunc = NULL; + mData = NULL; +} + +void +ChildAsyncCall::Run() +{ + if (mFunc) { + mInstance->mPendingAsyncCalls.RemoveElement(this); + mFunc(mData); + } +} + +} // namespace plugins +} // namespace mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/ChildAsyncCall.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/ChildAsyncCall.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/ChildAsyncCall.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/ChildAsyncCall.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=8 et : + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Firefox. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation . + * + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_plugins_ChildAsyncCall_h +#define mozilla_plugins_ChildAsyncCall_h + +#include "PluginMessageUtils.h" +#include "base/task.h" + +namespace mozilla { +namespace plugins { + +typedef void (*PluginThreadCallback)(void*); + +class PluginInstanceChild; + +class ChildAsyncCall : public CancelableTask +{ +public: + ChildAsyncCall(PluginInstanceChild* instance, + PluginThreadCallback aFunc, void* aUserData); + + NS_OVERRIDE void Run(); + NS_OVERRIDE void Cancel(); + +private: + PluginInstanceChild* mInstance; + PluginThreadCallback mFunc; + void* mData; +}; + +} // namespace plugins +} // namespace mozilla + +#endif // mozilla_plugins_ChildAsyncCall_h diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/ipdl.mk firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/ipdl.mk --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/ipdl.mk 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/ipdl.mk 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,45 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (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.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Plugins. +# +# The Initial Developer of the Original Code is +# Benjamin Smedberg. +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +IPDLSRCS = \ + PPluginModule.ipdl \ + PPluginIdentifier.ipdl \ + PPluginInstance.ipdl \ + PPluginScriptableObject.ipdl \ + PBrowserStream.ipdl \ + PPluginStream.ipdl \ + PStreamNotify.ipdl \ + $(NULL) diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/Makefile.in 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/Makefile.in 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,129 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (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.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Plugins. +# +# The Initial Developer of the Original Code is +# Ben Turner . +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Chris Jones +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = dom + +EXPORTS_NAMESPACES = mozilla + +EXPORTS_mozilla = \ + PluginLibrary.h \ + $(NULL) + +ifdef MOZ_IPC + +EXPORTS_NAMESPACES = mozilla mozilla/plugins + +EXPORTS_mozilla/plugins = \ + BrowserStreamChild.h \ + BrowserStreamParent.h \ + ChildAsyncCall.h \ + NPEventOSX.h \ + NPEventWindows.h \ + NPEventX11.h \ + PluginIdentifierChild.h \ + PluginIdentifierParent.h \ + PluginInstanceChild.h \ + PluginInstanceParent.h \ + PluginMessageUtils.h \ + PluginModuleChild.h \ + PluginModuleParent.h \ + PluginProcessParent.h \ + PluginScriptableObjectChild.h \ + PluginScriptableObjectParent.h \ + PluginScriptableObjectUtils.h \ + PluginScriptableObjectUtils-inl.h \ + PluginInstanceChild.h \ + PluginInstanceParent.h \ + AStream.h \ + BrowserStreamChild.h \ + BrowserStreamParent.h \ + PluginStreamChild.h \ + PluginStreamParent.h \ + PluginMessageUtils.h \ + PluginProcessParent.h \ + PluginThreadChild.h \ + StreamNotifyChild.h \ + StreamNotifyParent.h \ + $(NULL) + +MODULE = dom +LIBRARY_NAME = domplugins_s +LIBXUL_LIBRARY = 1 +FORCE_STATIC_LIB = 1 +EXPORT_LIBRARY = 1 +ENABLE_CXX_EXCEPTIONS = 1 + +CPPSRCS = \ + ChildAsyncCall.cpp \ + PluginMessageUtils.cpp \ + PluginInstanceChild.cpp \ + PluginInstanceParent.cpp \ + PluginModuleChild.cpp \ + PluginModuleParent.cpp \ + PluginProcessParent.cpp \ + PluginScriptableObjectChild.cpp \ + PluginScriptableObjectParent.cpp \ + BrowserStreamChild.cpp \ + BrowserStreamParent.cpp \ + PluginStreamChild.cpp \ + PluginStreamParent.cpp \ + PluginThreadChild.cpp \ + $(NULL) + +LOCAL_INCLUDES = \ + -I$(topsrcdir)/modules/plugin/base/public/ \ + -I$(topsrcdir)/modules/plugin/base/src/ \ + -I$(topsrcdir)/toolkit/xre \ + -I$(topsrcdir)/toolkit/crashreporter \ + $(NULL) + +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk +endif + +include $(topsrcdir)/config/rules.mk + +CXXFLAGS += $(TK_CFLAGS) + +DEFINES += -DFORCE_PR_LOG diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/NPEventOSX.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/NPEventOSX.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/NPEventOSX.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/NPEventOSX.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,79 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_dom_plugins_NPEventOSX_h +#define mozilla_dom_plugins_NPEventOSX_h 1 + + +#include "npapi.h" +#include "IPC/IPCMessageUtils.h" + +#warning This is only a stub implementation IMPLEMENT ME + +namespace mozilla { +namespace plugins { +struct NPRemoteEvent { + NPEvent event; +}; +} +} + +namespace IPC { + +template <> +struct ParamTraits +{ + typedef mozilla::plugins::NPRemoteEvent paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + return true; + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + } +}; + +} // namespace IPC + +#endif // ifndef mozilla_dom_plugins_NPEventOSX_h diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/NPEventWindows.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/NPEventWindows.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/NPEventWindows.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/NPEventWindows.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,172 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_dom_plugins_NPEventWindows_h +#define mozilla_dom_plugins_NPEventWindows_h 1 + + +#include "npapi.h" +namespace mozilla { + +namespace plugins { + +// We use an NPRemoteEvent struct so that we can store the extra data on +// the stack so that we don't need to worry about managing the memory. +struct NPRemoteEvent +{ + NPEvent event; + union { + RECT rect; + WINDOWPOS windowpos; + } lParamData; +}; + +} + +} + +namespace IPC { + +template <> +struct ParamTraits +{ + typedef mozilla::plugins::NPRemoteEvent paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + // Make a non-const copy of aParam so that we can muck with + // its insides for tranport + paramType paramCopy; + + paramCopy.event = aParam.event; + + // We can't blindly ipc events because they may sometimes contain + // pointers to memory in the sending process. For example, the + // WM_IME_CONTROL with the IMC_GETCOMPOSITIONFONT message has lParam + // set to a pointer to a LOGFONT structure. + switch (paramCopy.event.event) { + case WM_WINDOWPOSCHANGED: + // The lParam paramter of WM_WINDOWPOSCHANGED holds a pointer to + // a WINDOWPOS structure that contains information about the + // window's new size and position + paramCopy.lParamData.windowpos = *(reinterpret_cast(paramCopy.event.lParam)); + break; + case WM_PAINT: + // The lParam paramter of WM_PAINT holds a pointer to an RECT + // structure specifying the bounding box of the update area. + paramCopy.lParamData.rect = *(reinterpret_cast(paramCopy.event.lParam)); + break; + + // the white list of events that we will ipc to the client + case WM_CHAR: + case WM_SYSCHAR: + + case WM_KEYUP: + case WM_SYSKEYUP: + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + + case WM_DEADCHAR: + case WM_SYSDEADCHAR: + case WM_CONTEXTMENU: + + case WM_CUT: + case WM_COPY: + case WM_PASTE: + case WM_CLEAR: + case WM_UNDO: + + case WM_MOUSELEAVE: + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MBUTTONUP: + case WM_RBUTTONUP: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + + case WM_SETFOCUS: + case WM_KILLFOCUS: + break; + + default: + // RegisterWindowMessage events should be passed. + if (paramCopy.event.event >= 0xC000 && paramCopy.event.event <= 0xFFFF) + break; + + // ignore any events we don't expect + return; + } + + aMsg->WriteBytes(¶mCopy, sizeof(paramType)); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + const char* bytes = 0; + + if (!aMsg->ReadBytes(aIter, &bytes, sizeof(paramType))) { + return false; + } + memcpy(aResult, bytes, sizeof(paramType)); + + if (aResult->event.event == WM_PAINT) { + // restore the lParam to point at the RECT + aResult->event.lParam = reinterpret_cast(&aResult->lParamData.rect); + } else if (aResult->event.event == WM_WINDOWPOSCHANGED) { + // restore the lParam to point at the WINDOWPOS + aResult->event.lParam = reinterpret_cast(&aResult->lParamData.windowpos); + } + + return true; + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + aLog->append(L"(WINEvent)"); + } + +}; + +} // namespace IPC + +#endif // ifndef mozilla_dom_plugins_NPEventWindows_h diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/NPEventX11.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/NPEventX11.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/NPEventX11.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/NPEventX11.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,158 @@ +/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_dom_plugins_NPEventX11_h +#define mozilla_dom_plugins_NPEventX11_h 1 + +#if defined(MOZ_WIDGET_GTK2) +# include +#elif defined(MOZ_WIDGET_QT) +// X11/X.h has #define CursorShape 0, but Qt's qnamespace.h defines +// enum CursorShape { ... }. Good times! +# undef CursorShape +# include +#else +# error Implement me for your toolkit +#endif + +#include "npapi.h" + +namespace mozilla { + +namespace plugins { + +struct NPRemoteEvent { + NPEvent event; +}; + +} + +} + + +// +// XEvent is defined as a union of all more specific X*Events. +// Luckily, as of xorg 1.6.0 / X protocol 11 rev 0, the only pointer +// field contained in any of these specific X*Event structs is a +// |Display*|. So to simplify serializing these XEvents, we make the +// +// ********** XXX ASSUMPTION XXX ********** +// +// that the process to which the event is forwarded shares the same +// display as the process on which the event originated. +// +// With this simplification, serialization becomes a simple memcpy to +// the output stream. Deserialization starts as just a memcpy from +// the input stream, BUT we then have to write the correct |Display*| +// into the right field of each X*Event that contains one. +// + +namespace IPC { + +template <> +struct ParamTraits // synonym for XEvent +{ + typedef mozilla::plugins::NPRemoteEvent paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + aMsg->WriteBytes(&aParam, sizeof(paramType)); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + const char* bytes = 0; + + if (!aMsg->ReadBytes(aIter, &bytes, sizeof(paramType))) { + return false; + } + + memcpy(aResult, bytes, sizeof(paramType)); + SetXDisplay(aResult->event); + return true; + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + // TODO + aLog->append(L"(XEvent)"); + } + +private: + static Display* GetXDisplay(const XAnyEvent& ev) + { + // TODO: get Display* from Window in |ev| + + // FIXME: do this using Xlib +#if defined(MOZ_WIDGET_GTK2) + return GDK_DISPLAY(); +#elif defined(MOZ_WIDGET_QT) + return QX11Info::display(); +#endif + } + + static Display* GetXDisplay(const XErrorEvent& ev) + { + // TODO: get Display* from Window in |ev| + + // FIXME: do this using Xlib +#if defined(MOZ_WIDGET_GTK2) + return GDK_DISPLAY(); +#elif defined(MOZ_WIDGET_QT) + return QX11Info::display(); +#endif + } + + static void SetXDisplay(XEvent& ev) + { + if (ev.type >= KeyPress) { + ev.xany.display = GetXDisplay(ev.xany); + } + else { + // XXX assuming that this is an error event + // (type == 0? not clear from Xlib.h) + ev.xerror.display = GetXDisplay(ev.xerror); + } + } +}; + +} // namespace IPC + + +#endif // ifndef mozilla_dom_plugins_NPEventX11_h diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PBrowserStream.ipdl firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PBrowserStream.ipdl --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PBrowserStream.ipdl 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PBrowserStream.ipdl 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,101 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugins. + * + * The Initial Developer of the Original Code is + * Benjamin Smedberg + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +include protocol "PPluginInstance.ipdl"; + +include "mozilla/plugins/PluginMessageUtils.h"; + +using mozilla::plugins::Buffer; +using mozilla::plugins::IPCByteRanges; + +using NPError; +using NPReason; + +namespace mozilla { +namespace plugins { + +/** + * NPBrowserStream represents a NPStream sent from the browser to the plugin. + */ + +rpc protocol PBrowserStream +{ + manager PPluginInstance; + +child: + async Write(int32_t offset, Buffer data, + uint32_t newlength); + rpc NPP_StreamAsFile(nsCString fname); + + /** + * NPP_DestroyStream may race with other messages: the child acknowledges + * the message with StreamDestroyed before this actor is deleted. + */ + async NPP_DestroyStream(NPReason reason); + async __delete__(); + +parent: + rpc NPN_RequestRead(IPCByteRanges ranges) + returns (NPError result); + async NPN_DestroyStream(NPReason reason); + async StreamDestroyed(); + +/* + TODO: turn on state machine. + + // need configurable start state: if the constructor + // returns an error in result, start state should + // be DELETING. +start state ALIVE: + send Write goto ALIVE; + call NPP_StreamAsFile goto ALIVE; + send NPP_DestroyStream goto ALIVE; + answer NPN_RequestRead goto ALIVE; + recv NPN_DestroyStream goto DYING; + +state DYING: + answer NPN_RequestRead goto DYING; + recv NPN_DestroyStream goto DYING; + recv StreamDestroyed goto DELETING; + +state DELETING: + send __delete__; +*/ +}; + +} // namespace plugins +} // namespace mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginIdentifierChild.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginIdentifierChild.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginIdentifierChild.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginIdentifierChild.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,140 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugins. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Turner + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef dom_plugins_PluginIdentifierChild_h +#define dom_plugins_PluginIdentifierChild_h + +#include "mozilla/plugins/PPluginIdentifierChild.h" +#include "mozilla/plugins/PluginModuleChild.h" + +#include "nsStringGlue.h" + +namespace mozilla { +namespace plugins { + +class PluginIdentifierChild : public PPluginIdentifierChild +{ + friend class PluginModuleChild; +public: + bool IsString() + { + return reinterpret_cast(mCanonicalIdentifier) & 1; + } + + NPIdentifier ToNPIdentifier() + { + return reinterpret_cast( + reinterpret_cast(mCanonicalIdentifier) & ~1); + } + +protected: + PluginIdentifierChild(bool aIsString) + : ALLOW_THIS_IN_INITIALIZER_LIST(mCanonicalIdentifier(this)) + { + MOZ_COUNT_CTOR(PluginIdentifierChild); + if (aIsString) { + SetIsString(); + } + } + + virtual ~PluginIdentifierChild() + { + MOZ_COUNT_DTOR(PluginIdentifierChild); + } + + void SetCanonicalIdentifier(PluginIdentifierChild* aIdentifier) + { + NS_ASSERTION(ToNPIdentifier() == this, "Already got one!"); + bool isString = IsString(); + mCanonicalIdentifier = aIdentifier; + if (isString) { + SetIsString(); + } + } + +private: + void SetIsString() + { + mCanonicalIdentifier = reinterpret_cast( + reinterpret_cast(mCanonicalIdentifier) | 1); + } + + PluginIdentifierChild* mCanonicalIdentifier; +}; + +class PluginIdentifierChildString : public PluginIdentifierChild +{ + friend class PluginModuleChild; +public: + NPUTF8* ToString() + { + return ToNewCString(mString); + } + +protected: + PluginIdentifierChildString(const nsCString& aString) + : PluginIdentifierChild(true), + mString(aString) + { } + + nsCString mString; +}; + +class PluginIdentifierChildInt : public PluginIdentifierChild +{ + friend class PluginModuleChild; +public: + int32_t ToInt() + { + return mInt; + } + +protected: + PluginIdentifierChildInt(int32_t aInt) + : PluginIdentifierChild(false), + mInt(aInt) + { } + + int32_t mInt; +}; + +} // namespace plugins +} // namespace mozilla + +#endif // dom_plugins_PluginIdentifierChild_h diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginIdentifierParent.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginIdentifierParent.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginIdentifierParent.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginIdentifierParent.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugins. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Turner + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef dom_plugins_PluginIdentifierParent_h +#define dom_plugins_PluginIdentifierParent_h + +#include "mozilla/plugins/PPluginIdentifierParent.h" + +#include "npapi.h" +#include "npruntime.h" + +namespace mozilla { +namespace plugins { + +class PluginIdentifierParent : public PPluginIdentifierParent +{ + friend class PluginModuleParent; + +public: + NPIdentifier ToNPIdentifier() + { + return mIdentifier; + } + +protected: + PluginIdentifierParent(NPIdentifier aIdentifier) + : mIdentifier(aIdentifier) + { + MOZ_COUNT_CTOR(PluginIdentifierParent); + } + + virtual ~PluginIdentifierParent() + { + MOZ_COUNT_DTOR(PluginIdentifierParent); + } + +private: + NPIdentifier mIdentifier; +}; + +} // namespace plugins +} // namespace mozilla + +#endif // dom_plugins_PluginIdentifierParent_h diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginInstanceChild.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginInstanceChild.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginInstanceChild.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginInstanceChild.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,1662 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Chris Jones + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jim Mathies + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "PluginInstanceChild.h" +#include "PluginModuleChild.h" +#include "BrowserStreamChild.h" +#include "PluginStreamChild.h" +#include "StreamNotifyChild.h" + +#include "mozilla/ipc/SyncChannel.h" + +using namespace mozilla::plugins; + +#ifdef MOZ_WIDGET_GTK2 + +#include +#include +#include +#include "gtk2xtbin.h" + +#elif defined(MOZ_WIDGET_QT) +#include +#elif defined(OS_WIN) + +#include "nsWindowsDllInterceptor.h" + +typedef BOOL (WINAPI *User32TrackPopupMenu)(HMENU hMenu, + UINT uFlags, + int x, + int y, + int nReserved, + HWND hWnd, + CONST RECT *prcRect); +static WindowsDllInterceptor sUser32Intercept; +static HWND sWinlessPopupSurrogateHWND = NULL; +static User32TrackPopupMenu sUser32TrackPopupMenuStub = NULL; + +using mozilla::gfx::SharedDIB; + +#include +#include + +#define NS_OOPP_DOUBLEPASS_MSGID TEXT("MozDoublePassMsg") + +#endif // defined(OS_WIN) + +PluginInstanceChild::PluginInstanceChild(const NPPluginFuncs* aPluginIface, + const nsCString& aMimeType) + : mPluginIface(aPluginIface) + , mQuirks(0) + , mCachedWindowActor(nsnull) + , mCachedElementActor(nsnull) +#if defined(OS_WIN) + , mPluginWindowHWND(0) + , mPluginWndProc(0) + , mPluginParentHWND(0) + , mNestedEventHook(0) + , mNestedEventLevelDepth(0) + , mNestedEventState(false) + , mCachedWinlessPluginHWND(0) + , mWinlessPopupSurrogateHWND(0) +#endif // OS_WIN +{ + memset(&mWindow, 0, sizeof(mWindow)); + mData.ndata = (void*) this; + mData.pdata = nsnull; +#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX) + mWindow.ws_info = &mWsInfo; + memset(&mWsInfo, 0, sizeof(mWsInfo)); +#ifdef MOZ_WIDGET_GTK2 + mWsInfo.display = GDK_DISPLAY(); +#elif defined(MOZ_WIDGET_QT) + mWsInfo.display = QX11Info::display(); +#endif // MOZ_WIDGET_GTK2 +#endif // MOZ_X11 && XP_UNIX && !XP_MACOSX +#if defined(OS_WIN) + memset(&mAlphaExtract, 0, sizeof(mAlphaExtract)); + mAlphaExtract.doublePassEvent = ::RegisterWindowMessage(NS_OOPP_DOUBLEPASS_MSGID); +#endif // OS_WIN + InitQuirksModes(aMimeType); +#if defined(OS_WIN) + InitPopupMenuHook(); +#endif // OS_WIN +} + +PluginInstanceChild::~PluginInstanceChild() +{ +#if defined(OS_WIN) + DestroyPluginWindow(); +#endif +} + +void +PluginInstanceChild::InitQuirksModes(const nsCString& aMimeType) +{ +#ifdef OS_WIN + // application/x-silverlight + // application/x-silverlight-2 + NS_NAMED_LITERAL_CSTRING(silverlight, "application/x-silverlight"); + // application/x-shockwave-flash + NS_NAMED_LITERAL_CSTRING(flash, "application/x-shockwave-flash"); + if (FindInReadable(silverlight, aMimeType)) { + mQuirks |= QUIRK_SILVERLIGHT_WINLESS_INPUT_TRANSLATION; + mQuirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK; + } + else if (FindInReadable(flash, aMimeType)) { + mQuirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK; + } +#endif +} + +NPError +PluginInstanceChild::InternalGetNPObjectForValue(NPNVariable aValue, + NPObject** aObject) +{ + PluginScriptableObjectChild* actor; + NPError result = NPERR_NO_ERROR; + + switch (aValue) { + case NPNVWindowNPObject: + if (!(actor = mCachedWindowActor)) { + PPluginScriptableObjectChild* actorProtocol; + CallNPN_GetValue_NPNVWindowNPObject(&actorProtocol, &result); + if (result == NPERR_NO_ERROR) { + actor = mCachedWindowActor = + static_cast(actorProtocol); + NS_ASSERTION(actor, "Null actor!"); + PluginModuleChild::sBrowserFuncs.retainobject( + actor->GetObject(false)); + } + } + break; + + case NPNVPluginElementNPObject: + if (!(actor = mCachedElementActor)) { + PPluginScriptableObjectChild* actorProtocol; + CallNPN_GetValue_NPNVPluginElementNPObject(&actorProtocol, + &result); + if (result == NPERR_NO_ERROR) { + actor = mCachedElementActor = + static_cast(actorProtocol); + NS_ASSERTION(actor, "Null actor!"); + PluginModuleChild::sBrowserFuncs.retainobject( + actor->GetObject(false)); + } + } + break; + + default: + NS_NOTREACHED("Don't know what to do with this value type!"); + } + +#ifdef DEBUG + { + NPError currentResult; + PPluginScriptableObjectChild* currentActor; + + switch (aValue) { + case NPNVWindowNPObject: + CallNPN_GetValue_NPNVWindowNPObject(¤tActor, + ¤tResult); + break; + case NPNVPluginElementNPObject: + CallNPN_GetValue_NPNVPluginElementNPObject(¤tActor, + ¤tResult); + break; + default: + NS_NOTREACHED("Don't know what to do with this value type!"); + } + + // Make sure that the current actor returned by the parent matches our + // cached actor! + NS_ASSERTION(static_cast(currentActor) == + actor, "Cached actor is out of date!"); + NS_ASSERTION(currentResult == result, "Results don't match?!"); + } +#endif + + if (result != NPERR_NO_ERROR) { + return result; + } + + NPObject* object = actor->GetObject(false); + NS_ASSERTION(object, "Null object?!"); + + *aObject = PluginModuleChild::sBrowserFuncs.retainobject(object); + return NPERR_NO_ERROR; + +} + +NPError +PluginInstanceChild::NPN_GetValue(NPNVariable aVar, + void* aValue) +{ + PLUGIN_LOG_DEBUG(("%s (aVar=%i)", FULLFUNCTION, (int) aVar)); + AssertPluginThread(); + + switch(aVar) { + + case NPNVSupportsWindowless: +#if defined(OS_LINUX) || defined(OS_WIN) + *((NPBool*)aValue) = true; +#else + *((NPBool*)aValue) = false; +#endif + return NPERR_NO_ERROR; + +#if defined(OS_LINUX) + case NPNVSupportsXEmbedBool: + *((NPBool*)aValue) = true; + return NPERR_NO_ERROR; + + case NPNVToolkit: + *((NPNToolkitType*)aValue) = NPNVGtk2; + return NPERR_NO_ERROR; + +#elif defined(OS_WIN) + case NPNVToolkit: + return NPERR_GENERIC_ERROR; +#endif + case NPNVjavascriptEnabledBool: { + bool v = false; + NPError result; + if (!CallNPN_GetValue_NPNVjavascriptEnabledBool(&v, &result)) { + return NPERR_GENERIC_ERROR; + } + *static_cast(aValue) = v; + return result; + } + + case NPNVisOfflineBool: { + bool v = false; + NPError result; + if (!CallNPN_GetValue_NPNVisOfflineBool(&v, &result)) { + return NPERR_GENERIC_ERROR; + } + *static_cast(aValue) = v; + return result; + } + + case NPNVprivateModeBool: { + bool v = false; + NPError result; + if (!CallNPN_GetValue_NPNVprivateModeBool(&v, &result)) { + return NPERR_GENERIC_ERROR; + } + *static_cast(aValue) = v; + return result; + } + + case NPNVWindowNPObject: // Intentional fall-through + case NPNVPluginElementNPObject: { + NPObject* object; + NPError result = InternalGetNPObjectForValue(aVar, &object); + if (result == NPERR_NO_ERROR) { + *((NPObject**)aValue) = object; + } + return result; + } + + case NPNVnetscapeWindow: { +#ifdef XP_WIN + if (mWindow.type == NPWindowTypeDrawable) { + if (mCachedWinlessPluginHWND) { + *static_cast(aValue) = mCachedWinlessPluginHWND; + return NPERR_NO_ERROR; + } + NPError result; + if (!CallNPN_GetValue_NPNVnetscapeWindow(&mCachedWinlessPluginHWND, &result)) { + return NPERR_GENERIC_ERROR; + } + *static_cast(aValue) = mCachedWinlessPluginHWND; + return result; + } + else { + *static_cast(aValue) = mPluginWindowHWND; + return NPERR_NO_ERROR; + } +#elif defined(MOZ_X11) + NPError result; + CallNPN_GetValue_NPNVnetscapeWindow(static_cast(aValue), &result); + return result; +#else + return NPERR_GENERIC_ERROR; +#endif + } + + default: + PR_LOG(gPluginLog, PR_LOG_WARNING, + ("In PluginInstanceChild::NPN_GetValue: Unhandled NPNVariable %i (%s)", + (int) aVar, NPNVariableToString(aVar))); + return NPERR_GENERIC_ERROR; + } + +} + + +NPError +PluginInstanceChild::NPN_SetValue(NPPVariable aVar, void* aValue) +{ + PR_LOG(gPluginLog, PR_LOG_DEBUG, ("%s (aVar=%i, aValue=%p)", + FULLFUNCTION, (int) aVar, aValue)); + + AssertPluginThread(); + + switch (aVar) { + case NPPVpluginWindowBool: { + NPError rv; + bool windowed = (NPBool) (intptr_t) aValue; + + if (!CallNPN_SetValue_NPPVpluginWindow(windowed, &rv)) + return NPERR_GENERIC_ERROR; + + return rv; + } + + case NPPVpluginTransparentBool: { + NPError rv; + bool transparent = (NPBool) (intptr_t) aValue; + + if (!CallNPN_SetValue_NPPVpluginTransparent(transparent, &rv)) + return NPERR_GENERIC_ERROR; + + return rv; + } + + default: + PR_LOG(gPluginLog, PR_LOG_WARNING, + ("In PluginInstanceChild::NPN_SetValue: Unhandled NPPVariable %i (%s)", + (int) aVar, NPPVariableToString(aVar))); + return NPERR_GENERIC_ERROR; + } +} + +bool +PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginNeedsXEmbed( + bool* needs, NPError* rv) +{ + AssertPluginThread(); + +#ifdef MOZ_X11 + // The documentation on the types for many variables in NP(N|P)_GetValue + // is vague. Often boolean values are NPBool (1 byte), but + // https://developer.mozilla.org/en/XEmbed_Extension_for_Mozilla_Plugins + // treats NPPVpluginNeedsXEmbed as PRBool (int), and + // on x86/32-bit, flash stores to this using |movl 0x1,&needsXEmbed|. + // thus we can't use NPBool for needsXEmbed, or the three bytes above + // it on the stack would get clobbered. so protect with the larger PRBool. + PRBool needsXEmbed = 0; + if (!mPluginIface->getvalue) { + *rv = NPERR_GENERIC_ERROR; + } + else { + *rv = mPluginIface->getvalue(GetNPP(), NPPVpluginNeedsXEmbed, + &needsXEmbed); + } + *needs = needsXEmbed; + return true; + +#else + + NS_RUNTIMEABORT("shouldn't be called on non-X11 platforms"); + return false; // not reached + +#endif +} + +bool +PluginInstanceChild::AnswerNPP_GetValue_NPPVpluginScriptableNPObject( + PPluginScriptableObjectChild** aValue, + NPError* aResult) +{ + AssertPluginThread(); + + NPObject* object = nsnull; + NPError result = NPERR_GENERIC_ERROR; + if (mPluginIface->getvalue) { + result = mPluginIface->getvalue(GetNPP(), NPPVpluginScriptableNPObject, + &object); + } + if (result == NPERR_NO_ERROR && object) { + PluginScriptableObjectChild* actor = GetActorForNPObject(object); + + // If we get an actor then it has retained. Otherwise we don't need it + // any longer. + PluginModuleChild::sBrowserFuncs.releaseobject(object); + if (actor) { + *aValue = actor; + *aResult = NPERR_NO_ERROR; + return true; + } + + NS_ERROR("Failed to get actor!"); + result = NPERR_GENERIC_ERROR; + } + else { + result = NPERR_GENERIC_ERROR; + } + + *aValue = nsnull; + *aResult = result; + return true; +} + +bool +PluginInstanceChild::AnswerNPP_SetValue_NPNVprivateModeBool(const bool& value, + NPError* result) +{ + if (!mPluginIface->setvalue) { + *result = NPERR_GENERIC_ERROR; + return true; + } + + // Use `long` instead of NPBool because Flash and other plugins read + // this as a word-size value instead of the 1-byte NPBool that it is. + long v = value; + *result = mPluginIface->setvalue(GetNPP(), NPNVprivateModeBool, &v); + return true; +} + +bool +PluginInstanceChild::AnswerNPP_HandleEvent(const NPRemoteEvent& event, + int16_t* handled) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + AssertPluginThread(); + +#if defined(OS_LINUX) && defined(DEBUG) + if (GraphicsExpose == event.event.type) + PLUGIN_LOG_DEBUG((" received drawable 0x%lx\n", + event.event.xgraphicsexpose.drawable)); +#endif + + // Make a copy since we may modify values. + NPEvent evcopy = event.event; + +#ifdef OS_WIN + // Painting for win32. SharedSurfacePaint handles everything. + if (mWindow.type == NPWindowTypeDrawable) { + if (evcopy.event == WM_PAINT) { + *handled = SharedSurfacePaint(evcopy); + return true; + } + else if (evcopy.event == mAlphaExtract.doublePassEvent) { + // We'll render to mSharedSurfaceDib first, then render to a cached bitmap + // we store locally. The two passes are for alpha extraction, so the second + // pass must be to a flat white surface in order for things to work. + mAlphaExtract.doublePass = RENDER_BACK_ONE; + *handled = true; + return true; + } + } + *handled = WinlessHandleEvent(evcopy); + return true; +#endif + + if (!mPluginIface->event) + *handled = false; + else + *handled = mPluginIface->event(&mData, reinterpret_cast(&evcopy)); + +#ifdef MOZ_X11 + if (GraphicsExpose == event.event.type) { + // Make sure the X server completes the drawing before the parent + // draws on top and destroys the Drawable. + // + // XSync() waits for the X server to complete. Really this child + // process does not need to wait; the parent is the process that needs + // to wait. A possibly-slightly-better alternative would be to send + // an X event to the parent that the parent would wait for. + XSync(mWsInfo.display, False); + } +#endif + + return true; +} + +bool +PluginInstanceChild::RecvWindowPosChanged(const NPRemoteEvent& event) +{ +#ifdef OS_WIN + int16_t dontcare; + return AnswerNPP_HandleEvent(event, &dontcare); +#else + NS_RUNTIMEABORT("WindowPosChanged is a windows-only message"); + return false; +#endif +} + + +#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX) +static bool +XVisualIDToInfo(Display* aDisplay, VisualID aVisualID, + Visual** aVisual, unsigned int* aDepth) +{ + if (aVisualID == None) { + *aVisual = NULL; + *aDepth = 0; + return true; + } + + const Screen* screen = DefaultScreenOfDisplay(aDisplay); + + for (int d = 0; d < screen->ndepths; d++) { + Depth *d_info = &screen->depths[d]; + for (int v = 0; v < d_info->nvisuals; v++) { + Visual* visual = &d_info->visuals[v]; + if (visual->visualid == aVisualID) { + *aVisual = visual; + *aDepth = d_info->depth; + return true; + } + } + } + + NS_ERROR("VisualID not on Screen."); + return false; +} +#endif + +bool +PluginInstanceChild::AnswerNPP_SetWindow(const NPRemoteWindow& aWindow) +{ + PLUGIN_LOG_DEBUG(("%s (aWindow=)", + FULLFUNCTION, + aWindow.window, + aWindow.x, aWindow.y, + aWindow.width, aWindow.height)); + AssertPluginThread(); + +#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX) + // The minimum info is sent over IPC to allow this + // code to determine the rest. + + mWindow.window = reinterpret_cast(aWindow.window); + mWindow.x = aWindow.x; + mWindow.y = aWindow.y; + mWindow.width = aWindow.width; + mWindow.height = aWindow.height; + mWindow.clipRect = aWindow.clipRect; + mWindow.type = aWindow.type; + + mWsInfo.colormap = aWindow.colormap; + if (!XVisualIDToInfo(mWsInfo.display, aWindow.visualID, + &mWsInfo.visual, &mWsInfo.depth)) + return false; + + if (aWindow.type == NPWindowTypeWindow) { +#ifdef MOZ_WIDGET_GTK2 + if (GdkWindow* socket_window = gdk_window_lookup(aWindow.window)) { + // A GdkWindow for the socket already exists. Need to + // workaround https://bugzilla.gnome.org/show_bug.cgi?id=607061 + // See wrap_gtk_plug_embedded in PluginModuleChild.cpp. + g_object_set_data(G_OBJECT(socket_window), + "moz-existed-before-set-window", + GUINT_TO_POINTER(1)); + } +#endif + } + + if (mPluginIface->setwindow) + (void) mPluginIface->setwindow(&mData, &mWindow); + +#elif defined(OS_WIN) + switch (aWindow.type) { + case NPWindowTypeWindow: + { + if (!CreatePluginWindow()) + return false; + + ReparentPluginWindow((HWND)aWindow.window); + SizePluginWindow(aWindow.width, aWindow.height); + + mWindow.window = (void*)mPluginWindowHWND; + mWindow.x = aWindow.x; + mWindow.y = aWindow.y; + mWindow.width = aWindow.width; + mWindow.height = aWindow.height; + mWindow.type = aWindow.type; + + if (mPluginIface->setwindow) { + (void) mPluginIface->setwindow(&mData, &mWindow); + WNDPROC wndProc = reinterpret_cast( + GetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC)); + if (wndProc != PluginWindowProc) { + mPluginWndProc = reinterpret_cast( + SetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC, + reinterpret_cast(PluginWindowProc))); + } + } + } + break; + + case NPWindowTypeDrawable: + if (mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK) + CreateWinlessPopupSurrogate(); + return SharedSurfaceSetWindow(aWindow); + break; + + default: + NS_NOTREACHED("Bad plugin window type."); + return false; + break; + } + +#elif defined(OS_MACOSX) +# warning This is only a stub implementation IMPLEMENT ME + +#else +# error Implement me for your OS +#endif + + return true; +} + +bool +PluginInstanceChild::Initialize() +{ + return true; +} + +#if defined(OS_WIN) + +static const TCHAR kWindowClassName[] = TEXT("GeckoPluginWindow"); +static const TCHAR kPluginInstanceChildProperty[] = TEXT("PluginInstanceChildProperty"); + +// static +bool +PluginInstanceChild::RegisterWindowClass() +{ + static bool alreadyRegistered = false; + if (alreadyRegistered) + return true; + + alreadyRegistered = true; + + WNDCLASSEX wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_DBLCLKS; + wcex.lpfnWndProc = DummyWindowProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = GetModuleHandle(NULL); + wcex.hIcon = 0; + wcex.hCursor = 0; + wcex.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); + wcex.lpszMenuName = 0; + wcex.lpszClassName = kWindowClassName; + wcex.hIconSm = 0; + + return RegisterClassEx(&wcex) ? true : false; +} + +bool +PluginInstanceChild::CreatePluginWindow() +{ + // already initialized + if (mPluginWindowHWND) + return true; + + if (!RegisterWindowClass()) + return false; + + mPluginWindowHWND = + CreateWindowEx(WS_EX_LEFT | WS_EX_LTRREADING | + WS_EX_NOPARENTNOTIFY | // XXXbent Get rid of this! + WS_EX_RIGHTSCROLLBAR, + kWindowClassName, 0, + WS_POPUP | WS_CLIPCHILDREN | WS_CLIPSIBLINGS, 0, 0, + 0, 0, NULL, 0, GetModuleHandle(NULL), 0); + if (!mPluginWindowHWND) + return false; + if (!SetProp(mPluginWindowHWND, kPluginInstanceChildProperty, this)) + return false; + + // Apparently some plugins require an ASCII WndProc. + SetWindowLongPtrA(mPluginWindowHWND, GWLP_WNDPROC, + reinterpret_cast(DefWindowProcA)); + + return true; +} + +void +PluginInstanceChild::DestroyPluginWindow() +{ + if (mPluginWindowHWND) { + // Unsubclass the window. + WNDPROC wndProc = reinterpret_cast( + GetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC)); + if (wndProc == PluginWindowProc) { + NS_ASSERTION(mPluginWndProc, "Should have old proc here!"); + SetWindowLongPtr(mPluginWindowHWND, GWLP_WNDPROC, + reinterpret_cast(mPluginWndProc)); + mPluginWndProc = 0; + } + + RemoveProp(mPluginWindowHWND, kPluginInstanceChildProperty); + DestroyWindow(mPluginWindowHWND); + mPluginWindowHWND = 0; + } +} + +void +PluginInstanceChild::ReparentPluginWindow(HWND hWndParent) +{ + if (hWndParent != mPluginParentHWND && IsWindow(hWndParent)) { + // Fix the child window's style to be a child window. + LONG style = GetWindowLongPtr(mPluginWindowHWND, GWL_STYLE); + style |= WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS; + style &= ~WS_POPUP; + SetWindowLongPtr(mPluginWindowHWND, GWL_STYLE, style); + + // Do the reparenting. + SetParent(mPluginWindowHWND, hWndParent); + + // Make sure we're visible. + ShowWindow(mPluginWindowHWND, SW_SHOWNA); + } + mPluginParentHWND = hWndParent; +} + +void +PluginInstanceChild::SizePluginWindow(int width, + int height) +{ + if (mPluginWindowHWND) { + mPluginSize.x = width; + mPluginSize.y = height; + SetWindowPos(mPluginWindowHWND, NULL, 0, 0, width, height, + SWP_NOZORDER | SWP_NOREPOSITION); + } +} + +// See chromium's webplugin_delegate_impl.cc for explanation of this function. +// static +LRESULT CALLBACK +PluginInstanceChild::DummyWindowProc(HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + return CallWindowProc(DefWindowProc, hWnd, message, wParam, lParam); +} + +// static +LRESULT CALLBACK +PluginInstanceChild::PluginWindowProc(HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + NS_ASSERTION(!mozilla::ipc::SyncChannel::IsPumpingMessages(), + "Failed to prevent a nonqueued message from running!"); + + PluginInstanceChild* self = reinterpret_cast( + GetProp(hWnd, kPluginInstanceChildProperty)); + if (!self) { + NS_NOTREACHED("Badness!"); + return 0; + } + + NS_ASSERTION(self->mPluginWindowHWND == hWnd, "Wrong window!"); + + // Adobe's shockwave positions the plugin window relative to the browser + // frame when it initializes. With oopp disabled, this wouldn't have an + // effect. With oopp, GeckoPluginWindow is a child of the parent plugin + // window, so the move offsets the child within the parent. Generally + // we don't want plugins moving or sizing our window, so we prevent these + // changes here. + if (message == WM_WINDOWPOSCHANGING) { + WINDOWPOS* pos = reinterpret_cast(lParam); + if (pos && (!(pos->flags & SWP_NOMOVE) || !(pos->flags & SWP_NOSIZE))) { + pos->x = pos->y = 0; + pos->cx = self->mPluginSize.x; + pos->cy = self->mPluginSize.y; + LRESULT res = CallWindowProc(self->mPluginWndProc, hWnd, message, wParam, + lParam); + pos->x = pos->y = 0; + pos->cx = self->mPluginSize.x; + pos->cy = self->mPluginSize.y; + return res; + } + } + + // The plugin received keyboard focus, let the parent know so the dom is up to date. + if (message == WM_MOUSEACTIVATE) + self->CallPluginGotFocus(); + + // Prevent lockups due to plugins making rpc calls when the parent + // is making a synchronous SetFocus api call. (bug 541362) Add more + // windowing events as needed for other api. + if (message == WM_KILLFOCUS && + ((InSendMessageEx(NULL) & (ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND)) { + ReplyMessage(0); // Unblock the caller + } + + LRESULT res = CallWindowProc(self->mPluginWndProc, hWnd, message, wParam, + lParam); + + if (message == WM_CLOSE) + self->DestroyPluginWindow(); + + if (message == WM_NCDESTROY) + RemoveProp(hWnd, kPluginInstanceChildProperty); + + return res; +} + +/* winless modal ui loop logic */ + +// gTempChildPointer is only in use from the time we enter handle event, to the +// point where ui might be created by that call. If ui isn't created, there's +// no issue. If ui is created, the parent can't start processing messages in +// spin loop until InternalCallSetNestedEventState is set, at which point, +// gTempChildPointer is no longer needed. +static PluginInstanceChild* gTempChildPointer; + +LRESULT CALLBACK +PluginInstanceChild::NestedInputEventHook(int nCode, + WPARAM wParam, + LPARAM lParam) +{ + if (!gTempChildPointer) { + return CallNextHookEx(NULL, nCode, wParam, lParam); + } + + if (nCode >= 0) { + NS_ASSERTION(gTempChildPointer, "Never should be null here!"); + gTempChildPointer->ResetNestedEventHook(); + gTempChildPointer->InternalCallSetNestedEventState(true); + + gTempChildPointer = NULL; + } + return CallNextHookEx(NULL, nCode, wParam, lParam); +} + +void +PluginInstanceChild::SetNestedInputEventHook() +{ + NS_ASSERTION(!mNestedEventHook, + "mNestedEventHook already setup in call to SetNestedInputEventHook?"); + + PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION)); + + // WH_GETMESSAGE hooks are triggered by peek message calls in parent due to + // attached message queues, resulting in stomped in-process ipc calls. So + // we use a filter hook specific to dialogs, menus, and scroll bars to kick + // things off. + mNestedEventHook = SetWindowsHookEx(WH_MSGFILTER, + NestedInputEventHook, + NULL, + GetCurrentThreadId()); +} + +void +PluginInstanceChild::ResetNestedEventHook() +{ + PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION)); + if (mNestedEventHook) + UnhookWindowsHookEx(mNestedEventHook); + mNestedEventHook = NULL; +} + +void +PluginInstanceChild::InternalCallSetNestedEventState(bool aState) +{ + if (aState != mNestedEventState) { + PLUGIN_LOG_DEBUG( + ("PluginInstanceChild::InternalCallSetNestedEventState(%i)", + (int)aState)); + mNestedEventState = aState; + SendSetNestedEventState(mNestedEventState); + } +} + +/* windowless track popup menu helpers */ + +BOOL +WINAPI +PluginInstanceChild::TrackPopupHookProc(HMENU hMenu, + UINT uFlags, + int x, + int y, + int nReserved, + HWND hWnd, + CONST RECT *prcRect) +{ + if (!sUser32TrackPopupMenuStub) { + NS_ERROR("TrackPopupMenu stub isn't set! Badness!"); + return 0; + } + + // Only change the parent when we know this is a context on the plugin + // surface within the browser. Prevents resetting the parent on child ui + // displayed by plugins that have working parent-child relationships. + PRUnichar szClass[21]; + bool haveClass = GetClassNameW(hWnd, szClass, NS_ARRAY_LENGTH(szClass)); + if (!haveClass || + (wcscmp(szClass, L"MozillaWindowClass") && + wcscmp(szClass, L"SWFlash_Placeholder"))) { + // Unrecognized parent + return sUser32TrackPopupMenuStub(hMenu, uFlags, x, y, nReserved, + hWnd, prcRect); + } + + // Called on an unexpected event, warn. + if (!sWinlessPopupSurrogateHWND) { + NS_WARNING( + "Untraced TrackPopupHookProc call! Menu might not work right!"); + return sUser32TrackPopupMenuStub(hMenu, uFlags, x, y, nReserved, + hWnd, prcRect); + } + + HWND surrogateHwnd = sWinlessPopupSurrogateHWND; + sWinlessPopupSurrogateHWND = NULL; + + // Popups that don't use TPM_RETURNCMD expect a final command message + // when an item is selected and the context closes. Since we replace + // the parent, we need to forward this back to the real parent so it + // can act on the menu item selected. + bool isRetCmdCall = (uFlags & TPM_RETURNCMD); + + // A little trick scrounged from chromium's code - set the focus + // to our surrogate parent so keyboard nav events go to the menu. + HWND focusHwnd = SetFocus(surrogateHwnd); + DWORD res = sUser32TrackPopupMenuStub(hMenu, uFlags|TPM_RETURNCMD, x, y, + nReserved, surrogateHwnd, prcRect); + if (IsWindow(focusHwnd)) { + SetFocus(focusHwnd); + } + + if (!isRetCmdCall && res) { + SendMessage(hWnd, WM_COMMAND, MAKEWPARAM(res, 0), 0); + } + + return res; +} + +void +PluginInstanceChild::InitPopupMenuHook() +{ + if (!(mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK) || + sUser32TrackPopupMenuStub) + return; + + // Note, once WindowsDllInterceptor is initialized for a module, + // it remains initialized for that particular module for it's + // lifetime. Additional instances are needed if other modules need + // to be hooked. + sUser32Intercept.Init("user32.dll"); + sUser32Intercept.AddHook("TrackPopupMenu", TrackPopupHookProc, + (void**) &sUser32TrackPopupMenuStub); +} + +void +PluginInstanceChild::CreateWinlessPopupSurrogate() +{ + // already initialized + if (mWinlessPopupSurrogateHWND) + return; + + HWND hwnd = NULL; + NPError result; + if (!CallNPN_GetValue_NPNVnetscapeWindow(&hwnd, &result)) { + NS_ERROR("CallNPN_GetValue_NPNVnetscapeWindow failed."); + return; + } + + mWinlessPopupSurrogateHWND = + CreateWindowEx(WS_EX_NOPARENTNOTIFY, L"Static", NULL, WS_CHILD, 0, 0, + 0, 0, hwnd, 0, GetModuleHandle(NULL), 0); + if (!mWinlessPopupSurrogateHWND) { + NS_ERROR("CreateWindowEx failed for winless placeholder!"); + return; + } + return; +} + +void +PluginInstanceChild::DestroyWinlessPopupSurrogate() +{ + if (mWinlessPopupSurrogateHWND) + DestroyWindow(mWinlessPopupSurrogateHWND); + mWinlessPopupSurrogateHWND = NULL; +} + +/* windowless handle event helpers */ + +static bool +NeedsNestedEventCoverage(UINT msg) +{ + // Events we assume some sort of modal ui *might* be generated. + switch (msg) { + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_CONTEXTMENU: + return true; + } + return false; +} + +static bool +IsMouseInputEvent(UINT msg) +{ + switch (msg) { + case WM_MOUSEMOVE: + case WM_LBUTTONUP: + case WM_RBUTTONUP: + case WM_MBUTTONUP: + case WM_LBUTTONDOWN: + case WM_RBUTTONDOWN: + case WM_MBUTTONDOWN: + case WM_LBUTTONDBLCLK: + case WM_MBUTTONDBLCLK: + case WM_RBUTTONDBLCLK: + return true; + } + return false; +} + +int16_t +PluginInstanceChild::WinlessHandleEvent(NPEvent& event) +{ + if (!mPluginIface->event) + return false; + + // Winless Silverlight quirk: winposchanged events are not used in + // determining the position of the plugin within the parent window, + // NPP_SetWindow values are used instead. Due to shared memory dib + // rendering, the origin of NPP_SetWindow is 0x0, so we trap + // winposchanged events here and do the translation internally for + // mouse input events. + if (mQuirks & QUIRK_SILVERLIGHT_WINLESS_INPUT_TRANSLATION) { + if (event.event == WM_WINDOWPOSCHANGED && event.lParam) { + WINDOWPOS* pos = reinterpret_cast(event.lParam); + mPluginOffset.x = pos->x; + mPluginOffset.y = pos->y; + } + else if (IsMouseInputEvent(event.event)) { + event.lParam = + MAKELPARAM((GET_X_LPARAM(event.lParam) - mPluginOffset.x), + (GET_Y_LPARAM(event.lParam) - mPluginOffset.y)); + } + } + + if (!NeedsNestedEventCoverage(event.event)) { + return mPluginIface->event(&mData, reinterpret_cast(&event)); + } + + // Events that might generate nested event dispatch loops need + // special handling during delivery. + int16_t handled; + + mNestedEventLevelDepth++; + PLUGIN_LOG_DEBUG(("WinlessHandleEvent start depth: %i", mNestedEventLevelDepth)); + + // On the first, non-reentrant call, setup our modal ui detection hook. + if (mNestedEventLevelDepth == 1) { + NS_ASSERTION(!gTempChildPointer, "valid gTempChildPointer here?"); + gTempChildPointer = this; + SetNestedInputEventHook(); + } + + // TrackPopupMenu will fail if the parent window is not associated with + // our ui thread. So we hook TrackPopupMenu so we can hand in a surrogate + // parent created in the child process. + if ((mQuirks & QUIRK_WINLESS_TRACKPOPUP_HOOK) && // XXX turn on by default? + (event.event == WM_RBUTTONDOWN || // flash + event.event == WM_RBUTTONUP)) { // silverlight + sWinlessPopupSurrogateHWND = mWinlessPopupSurrogateHWND; + } + + bool old_state = MessageLoop::current()->NestableTasksAllowed(); + MessageLoop::current()->SetNestableTasksAllowed(true); + handled = mPluginIface->event(&mData, reinterpret_cast(&event)); + MessageLoop::current()->SetNestableTasksAllowed(old_state); + + gTempChildPointer = NULL; + sWinlessPopupSurrogateHWND = NULL; + + mNestedEventLevelDepth--; + PLUGIN_LOG_DEBUG(("WinlessHandleEvent end depth: %i", mNestedEventLevelDepth)); + + NS_ASSERTION(!(mNestedEventLevelDepth < 0), "mNestedEventLevelDepth < 0?"); + if (mNestedEventLevelDepth <= 0) { + ResetNestedEventHook(); + InternalCallSetNestedEventState(false); + } + return handled; +} + +/* windowless drawing helpers */ + +bool +PluginInstanceChild::SharedSurfaceSetWindow(const NPRemoteWindow& aWindow) +{ + // If the surfaceHandle is empty, parent is telling us we can reuse our cached + // memory surface and hdc. Otherwise, we need to reset, usually due to a + // expanding plugin port size. + if (!aWindow.surfaceHandle) { + if (!mSharedSurfaceDib.IsValid()) { + return false; + } + } + else { + // Attach to the new shared surface parent handed us. + if (NS_FAILED(mSharedSurfaceDib.Attach((SharedDIB::Handle)aWindow.surfaceHandle, + aWindow.width, aWindow.height, 32))) + return false; + // Free any alpha extraction resources if needed. This will be reset + // the next time it's used. + AlphaExtractCacheRelease(); + } + + // NPRemoteWindow's origin is the origin of our shared dib. + mWindow.x = 0; + mWindow.y = 0; + mWindow.width = aWindow.width; + mWindow.height = aWindow.height; + mWindow.type = aWindow.type; + + mWindow.window = reinterpret_cast(mSharedSurfaceDib.GetHDC()); + + if (mPluginIface->setwindow) + mPluginIface->setwindow(&mData, &mWindow); + + return true; +} + +void +PluginInstanceChild::SharedSurfaceRelease() +{ + mSharedSurfaceDib.Close(); + AlphaExtractCacheRelease(); +} + +/* double pass cache buffer - (rarely) used in cases where alpha extraction + * occurs for windowless plugins. */ + +bool +PluginInstanceChild::AlphaExtractCacheSetup() +{ + AlphaExtractCacheRelease(); + + mAlphaExtract.hdc = ::CreateCompatibleDC(NULL); + + if (!mAlphaExtract.hdc) + return false; + + BITMAPINFOHEADER bmih; + memset((void*)&bmih, 0, sizeof(BITMAPINFOHEADER)); + bmih.biSize = sizeof(BITMAPINFOHEADER); + bmih.biWidth = mWindow.width; + bmih.biHeight = mWindow.height; + bmih.biPlanes = 1; + bmih.biBitCount = 32; + bmih.biCompression = BI_RGB; + + void* ppvBits = nsnull; + mAlphaExtract.bmp = ::CreateDIBSection(mAlphaExtract.hdc, + (BITMAPINFO*)&bmih, + DIB_RGB_COLORS, + (void**)&ppvBits, + NULL, + (unsigned long)sizeof(BITMAPINFOHEADER)); + if (!mAlphaExtract.bmp) + return false; + + DeleteObject(::SelectObject(mAlphaExtract.hdc, mAlphaExtract.bmp)); + return true; +} + +void +PluginInstanceChild::AlphaExtractCacheRelease() +{ + if (mAlphaExtract.bmp) + ::DeleteObject(mAlphaExtract.bmp); + + if (mAlphaExtract.hdc) + ::DeleteObject(mAlphaExtract.hdc); + + mAlphaExtract.bmp = NULL; + mAlphaExtract.hdc = NULL; +} + +void +PluginInstanceChild::UpdatePaintClipRect(RECT* aRect) +{ + if (aRect) { + // Update the clip rect on our internal hdc + HRGN clip = ::CreateRectRgnIndirect(aRect); + ::SelectClipRgn(mSharedSurfaceDib.GetHDC(), clip); + ::DeleteObject(clip); + } +} + +int16_t +PluginInstanceChild::SharedSurfacePaint(NPEvent& evcopy) +{ + if (!mPluginIface->event) + return false; + + RECT* pRect = reinterpret_cast(evcopy.lParam); + + switch(mAlphaExtract.doublePass) { + case RENDER_NATIVE: + // pass the internal hdc to the plugin + UpdatePaintClipRect(pRect); + evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC()); + return mPluginIface->event(&mData, reinterpret_cast(&evcopy)); + break; + case RENDER_BACK_ONE: + // Handle a double pass render used in alpha extraction for transparent + // plugins. (See nsObjectFrame and gfxWindowsNativeDrawing for details.) + // We render twice, once to the shared dib, and once to a cache which + // we copy back on a second paint. These paints can't be spread across + // multiple rpc messages as delays cause animation frame changes. + if (!mAlphaExtract.bmp && !AlphaExtractCacheSetup()) { + mAlphaExtract.doublePass = RENDER_NATIVE; + return false; + } + + // See gfxWindowsNativeDrawing, color order doesn't have to match. + UpdatePaintClipRect(pRect); + ::FillRect(mSharedSurfaceDib.GetHDC(), pRect, (HBRUSH)GetStockObject(WHITE_BRUSH)); + evcopy.wParam = WPARAM(mSharedSurfaceDib.GetHDC()); + if (!mPluginIface->event(&mData, reinterpret_cast(&evcopy))) { + mAlphaExtract.doublePass = RENDER_NATIVE; + return false; + } + + // Copy to cache. We render to shared dib so we don't have to call + // setwindow between calls (flash issue). + ::BitBlt(mAlphaExtract.hdc, + pRect->left, + pRect->top, + pRect->right - pRect->left, + pRect->bottom - pRect->top, + mSharedSurfaceDib.GetHDC(), + pRect->left, + pRect->top, + SRCCOPY); + + ::FillRect(mSharedSurfaceDib.GetHDC(), pRect, (HBRUSH)GetStockObject(BLACK_BRUSH)); + if (!mPluginIface->event(&mData, reinterpret_cast(&evcopy))) { + mAlphaExtract.doublePass = RENDER_NATIVE; + return false; + } + mAlphaExtract.doublePass = RENDER_BACK_TWO; + return true; + break; + case RENDER_BACK_TWO: + // copy our cached surface back + UpdatePaintClipRect(pRect); + ::BitBlt(mSharedSurfaceDib.GetHDC(), + pRect->left, + pRect->top, + pRect->right - pRect->left, + pRect->bottom - pRect->top, + mAlphaExtract.hdc, + pRect->left, + pRect->top, + SRCCOPY); + mAlphaExtract.doublePass = RENDER_NATIVE; + return true; + break; + } + return false; +} + +#endif // OS_WIN + +bool +PluginInstanceChild::AnswerSetPluginFocus() +{ + PR_LOG(gPluginLog, PR_LOG_DEBUG, ("%s", FULLFUNCTION)); + +#if defined(OS_WIN) + // Parent is letting us know something set focus to the plugin. + if (::GetFocus() == mPluginWindowHWND) + return true; + ::SetFocus(mPluginWindowHWND); + return true; +#else + NS_NOTREACHED("PluginInstanceChild::AnswerSetPluginFocus not implemented!"); + return false; +#endif +} + +bool +PluginInstanceChild::AnswerUpdateWindow() +{ + PR_LOG(gPluginLog, PR_LOG_DEBUG, ("%s", FULLFUNCTION)); + +#if defined(OS_WIN) + if (mPluginWindowHWND) { + RECT rect; + if (GetUpdateRect(GetParent(mPluginWindowHWND), &rect, FALSE)) { + ::InvalidateRect(mPluginWindowHWND, &rect, FALSE); + } + UpdateWindow(mPluginWindowHWND); + } + return true; +#else + NS_NOTREACHED("PluginInstanceChild::AnswerUpdateWindow not implemented!"); + return false; +#endif +} + +PPluginScriptableObjectChild* +PluginInstanceChild::AllocPPluginScriptableObject() +{ + AssertPluginThread(); + return new PluginScriptableObjectChild(Proxy); +} + +bool +PluginInstanceChild::DeallocPPluginScriptableObject( + PPluginScriptableObjectChild* aObject) +{ + AssertPluginThread(); + delete aObject; + return true; +} + +bool +PluginInstanceChild::RecvPPluginScriptableObjectConstructor( + PPluginScriptableObjectChild* aActor) +{ + AssertPluginThread(); + + // This is only called in response to the parent process requesting the + // creation of an actor. This actor will represent an NPObject that is + // created by the browser and returned to the plugin. + PluginScriptableObjectChild* actor = + static_cast(aActor); + NS_ASSERTION(!actor->GetObject(false), "Actor already has an object?!"); + + actor->InitializeProxy(); + NS_ASSERTION(actor->GetObject(false), "Actor should have an object!"); + + return true; +} + +bool +PluginInstanceChild::AnswerPBrowserStreamConstructor( + PBrowserStreamChild* aActor, + const nsCString& url, + const uint32_t& length, + const uint32_t& lastmodified, + PStreamNotifyChild* notifyData, + const nsCString& headers, + const nsCString& mimeType, + const bool& seekable, + NPError* rv, + uint16_t* stype) +{ + AssertPluginThread(); + *rv = static_cast(aActor) + ->StreamConstructed(mimeType, seekable, stype); + return true; +} + +PBrowserStreamChild* +PluginInstanceChild::AllocPBrowserStream(const nsCString& url, + const uint32_t& length, + const uint32_t& lastmodified, + PStreamNotifyChild* notifyData, + const nsCString& headers, + const nsCString& mimeType, + const bool& seekable, + NPError* rv, + uint16_t *stype) +{ + AssertPluginThread(); + return new BrowserStreamChild(this, url, length, lastmodified, + static_cast(notifyData), + headers, mimeType, seekable, rv, stype); +} + +bool +PluginInstanceChild::DeallocPBrowserStream(PBrowserStreamChild* stream) +{ + AssertPluginThread(); + delete stream; + return true; +} + +PPluginStreamChild* +PluginInstanceChild::AllocPPluginStream(const nsCString& mimeType, + const nsCString& target, + NPError* result) +{ + NS_RUNTIMEABORT("not callable"); + return NULL; +} + +bool +PluginInstanceChild::DeallocPPluginStream(PPluginStreamChild* stream) +{ + AssertPluginThread(); + delete stream; + return true; +} + +PStreamNotifyChild* +PluginInstanceChild::AllocPStreamNotify(const nsCString& url, + const nsCString& target, + const bool& post, + const nsCString& buffer, + const bool& file, + NPError* result) +{ + AssertPluginThread(); + NS_RUNTIMEABORT("not reached"); + return NULL; +} + +void +StreamNotifyChild::ActorDestroy(ActorDestroyReason why) +{ + if (AncestorDeletion == why && mBrowserStream) { + NS_ERROR("Pending NPP_URLNotify not called when closing an instance."); + + // reclaim responsibility for deleting ourself + mBrowserStream->mStreamNotify = NULL; + mBrowserStream = NULL; + } +} + + +void +StreamNotifyChild::SetAssociatedStream(BrowserStreamChild* bs) +{ + NS_ASSERTION(bs, "Shouldn't be null"); + NS_ASSERTION(!mBrowserStream, "Two streams for one streamnotify?"); + + mBrowserStream = bs; +} + +bool +StreamNotifyChild::Recv__delete__(const NPReason& reason) +{ + AssertPluginThread(); + + if (mBrowserStream) + mBrowserStream->NotifyPending(); + else + NPP_URLNotify(reason); + + return true; +} + +void +StreamNotifyChild::NPP_URLNotify(NPReason reason) +{ + PluginInstanceChild* instance = static_cast(Manager()); + + if (mClosure) + instance->mPluginIface->urlnotify(instance->GetNPP(), mURL.get(), + reason, mClosure); +} + +bool +PluginInstanceChild::DeallocPStreamNotify(PStreamNotifyChild* notifyData) +{ + AssertPluginThread(); + + if (!static_cast(notifyData)->mBrowserStream) + delete notifyData; + return true; +} + +PluginScriptableObjectChild* +PluginInstanceChild::GetActorForNPObject(NPObject* aObject) +{ + AssertPluginThread(); + NS_ASSERTION(aObject, "Null pointer!"); + + if (aObject->_class == PluginScriptableObjectChild::GetClass()) { + // One of ours! It's a browser-provided object. + ChildNPObject* object = static_cast(aObject); + NS_ASSERTION(object->parent, "Null actor!"); + return object->parent; + } + + PluginScriptableObjectChild* actor = + PluginModuleChild::current()->GetActorForNPObject(aObject); + if (actor) { + // Plugin-provided object that we've previously wrapped. + return actor; + } + + actor = new PluginScriptableObjectChild(LocalObject); + if (!SendPPluginScriptableObjectConstructor(actor)) { + NS_ERROR("Failed to send constructor message!"); + return nsnull; + } + + actor->InitializeLocal(aObject); + return actor; +} + +NPError +PluginInstanceChild::NPN_NewStream(NPMIMEType aMIMEType, const char* aWindow, + NPStream** aStream) +{ + AssertPluginThread(); + + PluginStreamChild* ps = new PluginStreamChild(); + + NPError result; + CallPPluginStreamConstructor(ps, nsDependentCString(aMIMEType), + NullableString(aWindow), &result); + if (NPERR_NO_ERROR != result) { + *aStream = NULL; + PPluginStreamChild::Call__delete__(ps, NPERR_GENERIC_ERROR, true); + return result; + } + + *aStream = &ps->mStream; + return NPERR_NO_ERROR; +} + +void +PluginInstanceChild::InvalidateRect(NPRect* aInvalidRect) +{ + NS_ASSERTION(aInvalidRect, "Null pointer!"); + +#ifdef OS_WIN + // Invalidate and draw locally for windowed plugins. + if (mWindow.type == NPWindowTypeWindow) { + NS_ASSERTION(IsWindow(mPluginWindowHWND), "Bad window?!"); + RECT rect = { aInvalidRect->left, aInvalidRect->top, + aInvalidRect->right, aInvalidRect->bottom }; + ::InvalidateRect(mPluginWindowHWND, &rect, FALSE); + return; + } +#endif + + SendNPN_InvalidateRect(*aInvalidRect); +} + +static PLDHashOperator +InvalidateObject(DeletingObjectEntry* e, void* userArg) +{ + NPObject* o = e->GetKey(); + if (!e->mDeleted && o->_class && o->_class->invalidate) + o->_class->invalidate(o); + + return PL_DHASH_NEXT; +} + +static PLDHashOperator +DeleteObject(DeletingObjectEntry* e, void* userArg) +{ + NPObject* o = e->GetKey(); + if (!e->mDeleted) { + e->mDeleted = true; + +#ifdef NS_BUILD_REFCNT_LOGGING + { + int32_t refcnt = o->referenceCount; + while (refcnt) { + --refcnt; + NS_LOG_RELEASE(o, refcnt, "NPObject"); + } + } +#endif + + PluginModuleChild::DeallocNPObject(o); + } + + return PL_DHASH_NEXT; +} + +bool +PluginInstanceChild::AnswerNPP_Destroy(NPError* aResult) +{ + PLUGIN_LOG_DEBUG_METHOD; + AssertPluginThread(); + + nsTArray streams; + ManagedPBrowserStreamChild(streams); + + // First make sure none of these streams become deleted + for (PRUint32 i = 0; i < streams.Length(); ) { + if (static_cast(streams[i])->InstanceDying()) + ++i; + else + streams.RemoveElementAt(i); + } + for (PRUint32 i = 0; i < streams.Length(); ++i) + static_cast(streams[i])->FinishDelivery(); + + for (PRUint32 i = 0; i < mPendingAsyncCalls.Length(); ++i) + mPendingAsyncCalls[i]->Cancel(); + mPendingAsyncCalls.TruncateLength(0); + + PluginModuleChild::current()->NPP_Destroy(this); + mData.ndata = 0; + + mDeletingHash = new nsTHashtable; + mDeletingHash->Init(); + PluginModuleChild::current()->FindNPObjectsForInstance(this); + + mDeletingHash->EnumerateEntries(InvalidateObject, NULL); + mDeletingHash->EnumerateEntries(DeleteObject, NULL); + + // Null out our cached actors as they should have been killed in the + // PluginInstanceDestroyed call above. + mCachedWindowActor = nsnull; + mCachedElementActor = nsnull; + +#if defined(OS_WIN) + SharedSurfaceRelease(); + ResetNestedEventHook(); + DestroyWinlessPopupSurrogate(); +#endif + + return true; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginInstanceChild.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginInstanceChild.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginInstanceChild.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginInstanceChild.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,317 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Chris Jones + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef dom_plugins_PluginInstanceChild_h +#define dom_plugins_PluginInstanceChild_h 1 + +#include "mozilla/plugins/PPluginInstanceChild.h" +#include "mozilla/plugins/PluginScriptableObjectChild.h" +#include "mozilla/plugins/StreamNotifyChild.h" +#if defined(OS_WIN) +#include "mozilla/gfx/SharedDIBWin.h" +#endif + +#include "npfunctions.h" +#include "nsAutoPtr.h" +#include "nsTArray.h" +#include "ChildAsyncCall.h" +#include "nsRect.h" +#include "nsTHashtable.h" + +namespace mozilla { +namespace plugins { + +class PBrowserStreamChild; +class BrowserStreamChild; +class StreamNotifyChild; + +class PluginInstanceChild : public PPluginInstanceChild +{ + friend class BrowserStreamChild; + friend class PluginStreamChild; + friend class StreamNotifyChild; + +#ifdef OS_WIN + friend LRESULT CALLBACK PluginWindowProc(HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam); +#endif + +protected: + virtual bool AnswerNPP_SetWindow(const NPRemoteWindow& window); + + virtual bool + AnswerNPP_GetValue_NPPVpluginNeedsXEmbed(bool* needs, NPError* rv); + virtual bool + AnswerNPP_GetValue_NPPVpluginScriptableNPObject(PPluginScriptableObjectChild** value, + NPError* result); + + virtual bool + AnswerNPP_SetValue_NPNVprivateModeBool(const bool& value, NPError* result); + + virtual bool + AnswerNPP_HandleEvent(const NPRemoteEvent& event, int16_t* handled); + + NS_OVERRIDE + virtual bool + AnswerPaint(const NPRemoteEvent& event, int16_t* handled) + { + return AnswerNPP_HandleEvent(event, handled); + } + + NS_OVERRIDE + virtual bool + RecvWindowPosChanged(const NPRemoteEvent& event); + + virtual bool + AnswerNPP_Destroy(NPError* result); + + virtual PPluginScriptableObjectChild* + AllocPPluginScriptableObject(); + + virtual bool + DeallocPPluginScriptableObject(PPluginScriptableObjectChild* aObject); + + NS_OVERRIDE virtual bool + RecvPPluginScriptableObjectConstructor(PPluginScriptableObjectChild* aActor); + + virtual PBrowserStreamChild* + AllocPBrowserStream(const nsCString& url, + const uint32_t& length, + const uint32_t& lastmodified, + PStreamNotifyChild* notifyData, + const nsCString& headers, + const nsCString& mimeType, + const bool& seekable, + NPError* rv, + uint16_t *stype); + + virtual bool + AnswerPBrowserStreamConstructor( + PBrowserStreamChild* aActor, + const nsCString& url, + const uint32_t& length, + const uint32_t& lastmodified, + PStreamNotifyChild* notifyData, + const nsCString& headers, + const nsCString& mimeType, + const bool& seekable, + NPError* rv, + uint16_t* stype); + + virtual bool + DeallocPBrowserStream(PBrowserStreamChild* stream); + + virtual PPluginStreamChild* + AllocPPluginStream(const nsCString& mimeType, + const nsCString& target, + NPError* result); + + virtual bool + DeallocPPluginStream(PPluginStreamChild* stream); + + virtual PStreamNotifyChild* + AllocPStreamNotify(const nsCString& url, const nsCString& target, + const bool& post, const nsCString& buffer, + const bool& file, + NPError* result); + + NS_OVERRIDE virtual bool + DeallocPStreamNotify(PStreamNotifyChild* notifyData); + + virtual bool + AnswerSetPluginFocus(); + + virtual bool + AnswerUpdateWindow(); + +public: + PluginInstanceChild(const NPPluginFuncs* aPluginIface, const nsCString& aMimeType); + + virtual ~PluginInstanceChild(); + + bool Initialize(); + + NPP GetNPP() + { + return &mData; + } + + NPError + NPN_GetValue(NPNVariable aVariable, void* aValue); + + NPError + NPN_SetValue(NPPVariable aVariable, void* aValue); + + PluginScriptableObjectChild* + GetActorForNPObject(NPObject* aObject); + + NPError + NPN_NewStream(NPMIMEType aMIMEType, const char* aWindow, + NPStream** aStream); + + void InvalidateRect(NPRect* aInvalidRect); + +private: + friend class PluginModuleChild; + + // Quirks mode support for various plugin mime types + enum PluginQuirks { + // Win32: Translate mouse input based on WM_WINDOWPOSCHANGED + // windowing events due to winless shared dib rendering. See + // WinlessHandleEvent for details. + QUIRK_SILVERLIGHT_WINLESS_INPUT_TRANSLATION = 1, + // Win32: Hook TrackPopupMenu api so that we can swap out parent + // hwnds. The api will fail with parents not associated with our + // child ui thread. See WinlessHandleEvent for details. + QUIRK_WINLESS_TRACKPOPUP_HOOK = 2, + }; + + void InitQuirksModes(const nsCString& aMimeType); + + NPError + InternalGetNPObjectForValue(NPNVariable aValue, + NPObject** aObject); + +#if defined(OS_WIN) + static bool RegisterWindowClass(); + bool CreatePluginWindow(); + void DestroyPluginWindow(); + void ReparentPluginWindow(HWND hWndParent); + void SizePluginWindow(int width, int height); + int16_t WinlessHandleEvent(NPEvent& event); + void SetNestedInputEventHook(); + void ResetNestedEventHook(); + void SetNestedInputPumpHook(); + void ResetPumpHooks(); + void CreateWinlessPopupSurrogate(); + void DestroyWinlessPopupSurrogate(); + void InitPopupMenuHook(); + void InternalCallSetNestedEventState(bool aState); + static LRESULT CALLBACK DummyWindowProc(HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam); + static LRESULT CALLBACK PluginWindowProc(HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam); + static VOID CALLBACK PumpTimerProc(HWND hwnd, + UINT uMsg, + UINT_PTR idEvent, + DWORD dwTime); + static LRESULT CALLBACK NestedInputEventHook(int code, + WPARAM wParam, + LPARAM lParam); + static LRESULT CALLBACK NestedInputPumpHook(int code, + WPARAM wParam, + LPARAM lParam); + static BOOL WINAPI TrackPopupHookProc(HMENU hMenu, + UINT uFlags, + int x, + int y, + int nReserved, + HWND hWnd, + CONST RECT *prcRect); +#endif + + const NPPluginFuncs* mPluginIface; + NPP_t mData; + NPWindow mWindow; + int mQuirks; + + // Cached scriptable actors to avoid IPC churn + PluginScriptableObjectChild* mCachedWindowActor; + PluginScriptableObjectChild* mCachedElementActor; + +#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX) + NPSetWindowCallbackStruct mWsInfo; +#elif defined(OS_WIN) + HWND mPluginWindowHWND; + WNDPROC mPluginWndProc; + HWND mPluginParentHWND; + HHOOK mNestedEventHook; + int mNestedEventLevelDepth; + bool mNestedEventState; + HWND mCachedWinlessPluginHWND; + HWND mWinlessPopupSurrogateHWND; + nsIntPoint mPluginSize; + nsIntPoint mPluginOffset; +#endif + + friend class ChildAsyncCall; + nsTArray mPendingAsyncCalls; + + /** + * During destruction we enumerate all remaining scriptable objects and + * invalidate/delete them. Enumeration can re-enter, so maintain a + * hash separate from PluginModuleChild.mObjectMap. + */ + nsAutoPtr< nsTHashtable > mDeletingHash; + +#if defined(OS_WIN) +private: + // Shared dib rendering management for windowless plugins. + bool SharedSurfaceSetWindow(const NPRemoteWindow& aWindow); + int16_t SharedSurfacePaint(NPEvent& evcopy); + void SharedSurfaceRelease(); + bool AlphaExtractCacheSetup(); + void AlphaExtractCacheRelease(); + void UpdatePaintClipRect(RECT* aRect); + +private: + enum { + RENDER_NATIVE, + RENDER_BACK_ONE, + RENDER_BACK_TWO + }; + gfx::SharedDIBWin mSharedSurfaceDib; + struct { + PRUint32 doublePassEvent; + PRUint16 doublePass; + HDC hdc; + HBITMAP bmp; + } mAlphaExtract; +#endif // defined(OS_WIN) +}; + +} // namespace plugins +} // namespace mozilla + +#endif // ifndef dom_plugins_PluginInstanceChild_h diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginInstanceParent.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginInstanceParent.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginInstanceParent.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginInstanceParent.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,1136 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Chris Jones + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jim Mathies + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "PluginInstanceParent.h" + +#include "BrowserStreamParent.h" +#include "PluginModuleParent.h" +#include "PluginStreamParent.h" +#include "StreamNotifyParent.h" +#include "npfunctions.h" +#include "nsAutoPtr.h" + +#if defined(OS_WIN) +#include + +// Plugin focus event for widget. +extern const PRUnichar* kOOPPPluginFocusEventId; +UINT gOOPPPluginFocusEvent = + RegisterWindowMessage(kOOPPPluginFocusEventId); +UINT gOOPPSpinNativeLoopEvent = + RegisterWindowMessage(L"SyncChannel Spin Inner Loop Message"); +UINT gOOPPStopNativeLoopEvent = + RegisterWindowMessage(L"SyncChannel Stop Inner Loop Message"); +#elif defined(MOZ_WIDGET_GTK2) +#include +#endif + +using namespace mozilla::plugins; + +PluginInstanceParent::PluginInstanceParent(PluginModuleParent* parent, + NPP npp, + const NPNetscapeFuncs* npniface) + : mParent(parent) + , mNPP(npp) + , mNPNIface(npniface) + , mWindowType(NPWindowTypeWindow) +#if defined(OS_WIN) + , mPluginHWND(NULL) + , mPluginWndProc(NULL) + , mNestedEventState(false) +#endif // defined(XP_WIN) +{ +} + +PluginInstanceParent::~PluginInstanceParent() +{ + if (mNPP) + mNPP->pdata = NULL; + +#if defined(OS_WIN) + NS_ASSERTION(!(mPluginHWND || mPluginWndProc), + "Subclass was not reset correctly before the dtor was reached!"); +#endif +} + +bool +PluginInstanceParent::Init() +{ + return !!mScriptableObjects.Init(); +} + +namespace { + +PLDHashOperator +ActorCollect(const void* aKey, + PluginScriptableObjectParent* aData, + void* aUserData) +{ + nsTArray* objects = + reinterpret_cast*>(aUserData); + return objects->AppendElement(aData) ? PL_DHASH_NEXT : PL_DHASH_STOP; +} + +} // anonymous namespace + +void +PluginInstanceParent::ActorDestroy(ActorDestroyReason why) +{ +#if defined(OS_WIN) + if (why == AbnormalShutdown) { + // If the plugin process crashes, this is the only + // chance we get to destroy resources. + SharedSurfaceRelease(); + UnsubclassPluginWindow(); + // If we crashed in a modal loop in the child, reset + // the rpc event spin loop state. + if (mNestedEventState) { + mNestedEventState = false; + PostThreadMessage(GetCurrentThreadId(), + gOOPPStopNativeLoopEvent, + 0, 0); + } + } +#endif +} + +NPError +PluginInstanceParent::Destroy() +{ + NPError retval; + if (!CallNPP_Destroy(&retval)) + retval = NPERR_GENERIC_ERROR; + +#if defined(OS_WIN) + SharedSurfaceRelease(); + UnsubclassPluginWindow(); + if (mNestedEventState) { + mNestedEventState = false; + PostThreadMessage(GetCurrentThreadId(), + gOOPPStopNativeLoopEvent, + 0, 0); + } +#endif + + return retval; +} + +PBrowserStreamParent* +PluginInstanceParent::AllocPBrowserStream(const nsCString& url, + const uint32_t& length, + const uint32_t& lastmodified, + PStreamNotifyParent* notifyData, + const nsCString& headers, + const nsCString& mimeType, + const bool& seekable, + NPError* rv, + uint16_t *stype) +{ + NS_RUNTIMEABORT("Not reachable"); + return NULL; +} + +bool +PluginInstanceParent::DeallocPBrowserStream(PBrowserStreamParent* stream) +{ + delete stream; + return true; +} + +PPluginStreamParent* +PluginInstanceParent::AllocPPluginStream(const nsCString& mimeType, + const nsCString& target, + NPError* result) +{ + return new PluginStreamParent(this, mimeType, target, result); +} + +bool +PluginInstanceParent::DeallocPPluginStream(PPluginStreamParent* stream) +{ + delete stream; + return true; +} + +#ifdef MOZ_X11 +static Display* GetXDisplay() { +# ifdef MOZ_WIDGET_GTK2 + return GDK_DISPLAY(); +# elif defined(MOZ_WIDGET_QT) + return QX11Info::display(); +# endif +} +#endif + +bool +PluginInstanceParent::AnswerNPN_GetValue_NPNVjavascriptEnabledBool( + bool* value, + NPError* result) +{ + NPBool v; + *result = mNPNIface->getvalue(mNPP, NPNVjavascriptEnabledBool, &v); + *value = v; + return true; +} + +bool +PluginInstanceParent::AnswerNPN_GetValue_NPNVisOfflineBool(bool* value, + NPError* result) +{ + NPBool v; + *result = mNPNIface->getvalue(mNPP, NPNVisOfflineBool, &v); + *value = v; + return true; +} + +bool +PluginInstanceParent::AnswerNPN_GetValue_NPNVnetscapeWindow(NativeWindowHandle* value, + NPError* result) +{ +#ifdef XP_WIN + HWND id; +#elif defined(MOZ_X11) + XID id; +#else + return false; +#endif + + *result = mNPNIface->getvalue(mNPP, NPNVnetscapeWindow, &id); + *value = id; + return true; +} + +bool +PluginInstanceParent::InternalGetValueForNPObject( + NPNVariable aVariable, + PPluginScriptableObjectParent** aValue, + NPError* aResult) +{ + NPObject* npobject; + NPError result = mNPNIface->getvalue(mNPP, aVariable, (void*)&npobject); + if (result == NPERR_NO_ERROR) { + NS_ASSERTION(npobject, "Shouldn't return null and NPERR_NO_ERROR!"); + + PluginScriptableObjectParent* actor = GetActorForNPObject(npobject); + mNPNIface->releaseobject(npobject); + if (actor) { + *aValue = actor; + *aResult = NPERR_NO_ERROR; + return true; + } + + NS_ERROR("Failed to get actor!"); + result = NPERR_GENERIC_ERROR; + } + + *aValue = nsnull; + *aResult = result; + return true; +} + +bool +PluginInstanceParent::AnswerNPN_GetValue_NPNVWindowNPObject( + PPluginScriptableObjectParent** aValue, + NPError* aResult) +{ + return InternalGetValueForNPObject(NPNVWindowNPObject, aValue, aResult); +} + +bool +PluginInstanceParent::AnswerNPN_GetValue_NPNVPluginElementNPObject( + PPluginScriptableObjectParent** aValue, + NPError* aResult) +{ + return InternalGetValueForNPObject(NPNVPluginElementNPObject, aValue, + aResult); +} + +bool +PluginInstanceParent::AnswerNPN_GetValue_NPNVprivateModeBool(bool* value, + NPError* result) +{ + NPBool v; + *result = mNPNIface->getvalue(mNPP, NPNVprivateModeBool, &v); + *value = v; + return true; +} + + +bool +PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginWindow( + const bool& windowed, NPError* result) +{ + NPBool isWindowed = windowed; + *result = mNPNIface->setvalue(mNPP, NPPVpluginWindowBool, + (void*)isWindowed); + return true; +} + +bool +PluginInstanceParent::AnswerNPN_SetValue_NPPVpluginTransparent( + const bool& transparent, NPError* result) +{ + NPBool isTransparent = transparent; + *result = mNPNIface->setvalue(mNPP, NPPVpluginTransparentBool, + (void*)isTransparent); + return true; +} + + +bool +PluginInstanceParent::AnswerNPN_GetURL(const nsCString& url, + const nsCString& target, + NPError* result) +{ + *result = mNPNIface->geturl(mNPP, + NullableStringGet(url), + NullableStringGet(target)); + return true; +} + +bool +PluginInstanceParent::AnswerNPN_PostURL(const nsCString& url, + const nsCString& target, + const nsCString& buffer, + const bool& file, + NPError* result) +{ + *result = mNPNIface->posturl(mNPP, url.get(), NullableStringGet(target), + buffer.Length(), buffer.get(), file); + return true; +} + +PStreamNotifyParent* +PluginInstanceParent::AllocPStreamNotify(const nsCString& url, + const nsCString& target, + const bool& post, + const nsCString& buffer, + const bool& file, + NPError* result) +{ + return new StreamNotifyParent(); +} + +bool +PluginInstanceParent::AnswerPStreamNotifyConstructor(PStreamNotifyParent* actor, + const nsCString& url, + const nsCString& target, + const bool& post, + const nsCString& buffer, + const bool& file, + NPError* result) +{ + bool streamDestroyed = false; + static_cast(actor)-> + SetDestructionFlag(&streamDestroyed); + + if (!post) { + *result = mNPNIface->geturlnotify(mNPP, + NullableStringGet(url), + NullableStringGet(target), + actor); + } + else { + *result = mNPNIface->posturlnotify(mNPP, + NullableStringGet(url), + NullableStringGet(target), + buffer.Length(), + NullableStringGet(buffer), + file, actor); + } + + if (!streamDestroyed) { + static_cast(actor)->ClearDestructionFlag(); + if (*result != NPERR_NO_ERROR) + PStreamNotifyParent::Send__delete__(actor, NPERR_GENERIC_ERROR); + } + + return true; +} + +bool +PluginInstanceParent::DeallocPStreamNotify(PStreamNotifyParent* notifyData) +{ + delete notifyData; + return true; +} + +bool +PluginInstanceParent::RecvNPN_InvalidateRect(const NPRect& rect) +{ + mNPNIface->invalidaterect(mNPP, const_cast(&rect)); + return true; +} + +NPError +PluginInstanceParent::NPP_SetWindow(const NPWindow* aWindow) +{ + PLUGIN_LOG_DEBUG(("%s (aWindow=%p)", FULLFUNCTION, (void*) aWindow)); + + NS_ENSURE_TRUE(aWindow, NPERR_GENERIC_ERROR); + + NPRemoteWindow window; + mWindowType = aWindow->type; + +#if defined(OS_WIN) + // On windowless controls, reset the shared memory surface as needed. + if (mWindowType == NPWindowTypeDrawable) { + // SharedSurfaceSetWindow will take care of NPRemoteWindow. + if (!SharedSurfaceSetWindow(aWindow, window)) { + return NPERR_OUT_OF_MEMORY_ERROR; + } + } + else { + SubclassPluginWindow(reinterpret_cast(aWindow->window)); + + window.window = reinterpret_cast(aWindow->window); + window.x = aWindow->x; + window.y = aWindow->y; + window.width = aWindow->width; + window.height = aWindow->height; + window.type = aWindow->type; + } +#else + window.window = reinterpret_cast(aWindow->window); + window.x = aWindow->x; + window.y = aWindow->y; + window.width = aWindow->width; + window.height = aWindow->height; + window.clipRect = aWindow->clipRect; // MacOS specific + window.type = aWindow->type; +#endif + +#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX) + const NPSetWindowCallbackStruct* ws_info = + static_cast(aWindow->ws_info); + window.visualID = ws_info->visual ? ws_info->visual->visualid : None; + window.colormap = ws_info->colormap; +#endif + + if (!CallNPP_SetWindow(window)) + return NPERR_GENERIC_ERROR; + + return NPERR_NO_ERROR; +} + +NPError +PluginInstanceParent::NPP_GetValue(NPPVariable aVariable, + void* _retval) +{ + switch (aVariable) { + +#ifdef MOZ_X11 + case NPPVpluginNeedsXEmbed: { + bool needsXEmbed; + NPError rv; + + if (!CallNPP_GetValue_NPPVpluginNeedsXEmbed(&needsXEmbed, &rv)) { + return NPERR_GENERIC_ERROR; + } + + if (NPERR_NO_ERROR != rv) { + return rv; + } + + (*(NPBool*)_retval) = needsXEmbed; + return NPERR_NO_ERROR; + } +#endif + + case NPPVpluginScriptableNPObject: { + PPluginScriptableObjectParent* actor; + NPError rv; + if (!CallNPP_GetValue_NPPVpluginScriptableNPObject(&actor, &rv)) { + return NPERR_GENERIC_ERROR; + } + + if (NPERR_NO_ERROR != rv) { + return rv; + } + + if (!actor) { + NS_ERROR("NPPVpluginScriptableNPObject succeeded but null."); + return NPERR_GENERIC_ERROR; + } + + const NPNetscapeFuncs* npn = mParent->GetNetscapeFuncs(); + if (!npn) { + NS_WARNING("No netscape functions?!"); + return NPERR_GENERIC_ERROR; + } + + NPObject* object = + static_cast(actor)->GetObject(true); + NS_ASSERTION(object, "This shouldn't ever be null!"); + + (*(NPObject**)_retval) = npn->retainobject(object); + return NPERR_NO_ERROR; + } + + default: + PR_LOG(gPluginLog, PR_LOG_WARNING, + ("In PluginInstanceParent::NPP_GetValue: Unhandled NPPVariable %i (%s)", + (int) aVariable, NPPVariableToString(aVariable))); + return NPERR_GENERIC_ERROR; + } +} + +NPError +PluginInstanceParent::NPP_SetValue(NPNVariable variable, void* value) +{ + switch (variable) { + case NPNVprivateModeBool: + NPError result; + if (!CallNPP_SetValue_NPNVprivateModeBool(*static_cast(value), + &result)) + return NPERR_GENERIC_ERROR; + + return result; + + default: + NS_ERROR("Unhandled NPNVariable in NPP_SetValue"); + PR_LOG(gPluginLog, PR_LOG_WARNING, + ("In PluginInstanceParent::NPP_SetValue: Unhandled NPNVariable %i (%s)", + (int) variable, NPNVariableToString(variable))); + return NPERR_GENERIC_ERROR; + } +} + +int16_t +PluginInstanceParent::NPP_HandleEvent(void* event) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + + NPEvent* npevent = reinterpret_cast(event); + NPRemoteEvent npremoteevent; + npremoteevent.event = *npevent; + int16_t handled = 0; + +#if defined(OS_WIN) + if (mWindowType == NPWindowTypeDrawable) { + switch(npevent->event) { + case WM_PAINT: + { + RECT rect; + SharedSurfaceBeforePaint(rect, npremoteevent); + CallPaint(npremoteevent, &handled); + SharedSurfaceAfterPaint(npevent); + return handled; + } + break; + + case WM_KILLFOCUS: + { + // When the user selects fullscreen mode in Flash video players, + // WM_KILLFOCUS will be delayed by deferred event processing: + // WM_LBUTTONUP results in a call to CreateWindow within Flash, + // which fires WM_KILLFOCUS. Delayed delivery causes Flash to + // misinterpret the event, dropping back out of fullscreen. Trap + // this event and drop it. + PRUnichar szClass[26]; + HWND hwnd = GetForegroundWindow(); + if (hwnd && hwnd != mPluginHWND && + GetClassNameW(hwnd, szClass, + sizeof(szClass)/sizeof(PRUnichar)) && + !wcscmp(szClass, L"ShockwaveFlashFullScreen")) { + return 0; + } + } + break; + + case WM_WINDOWPOSCHANGED: + { + // We send this in nsObjectFrame just before painting + SendWindowPosChanged(npremoteevent); + // nsObjectFrame doesn't care whether we handle this + // or not, just returning 1 for good hygiene + return 1; + } + break; + } + } +#endif + +#if defined(MOZ_X11) + switch (npevent->type) { + case GraphicsExpose: + PLUGIN_LOG_DEBUG((" schlepping drawable 0x%lx across the pipe\n", + npevent->xgraphicsexpose.drawable)); + // Make sure the X server has created the Drawable and completes any + // drawing before the plugin draws on top. + // + // XSync() waits for the X server to complete. Really this parent + // process does not need to wait; the child is the process that needs + // to wait. A possibly-slightly-better alternative would be to send + // an X event to the child that the child would wait for. + XSync(GetXDisplay(), False); + break; + case ButtonPress: + // Release any active pointer grab so that the plugin X client can + // grab the pointer if it wishes. + Display *dpy = GetXDisplay(); +# ifdef MOZ_WIDGET_GTK2 + // GDK attempts to (asynchronously) track whether there is an active + // grab so ungrab through GDK. + gdk_pointer_ungrab(npevent->xbutton.time); +# else + XUngrabPointer(dpy, npevent->xbutton.time); +# endif + // Wait for the ungrab to complete. + XSync(dpy, False); + break; + + return CallPaint(npremoteevent, &handled) ? handled : 0; + } +#endif + + if (!CallNPP_HandleEvent(npremoteevent, &handled)) + return 0; // no good way to handle errors here... + + return handled; +} + +NPError +PluginInstanceParent::NPP_NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16_t* stype) +{ + PLUGIN_LOG_DEBUG(("%s (type=%s, stream=%p, seekable=%i)", + FULLFUNCTION, (char*) type, (void*) stream, (int) seekable)); + + BrowserStreamParent* bs = new BrowserStreamParent(this, stream); + + NPError err; + if (!CallPBrowserStreamConstructor(bs, + NullableString(stream->url), + stream->end, + stream->lastmodified, + static_cast(stream->notifyData), + NullableString(stream->headers), + NullableString(type), seekable, + &err, stype)) + return NPERR_GENERIC_ERROR; + + if (NPERR_NO_ERROR != err) + PBrowserStreamParent::Send__delete__(bs); + + return err; +} + +NPError +PluginInstanceParent::NPP_DestroyStream(NPStream* stream, NPReason reason) +{ + PLUGIN_LOG_DEBUG(("%s (stream=%p, reason=%i)", + FULLFUNCTION, (void*) stream, (int) reason)); + + AStream* s = static_cast(stream->pdata); + if (s->IsBrowserStream()) { + BrowserStreamParent* sp = + static_cast(s); + if (sp->mNPP != this) + NS_RUNTIMEABORT("Mismatched plugin data"); + + sp->NPP_DestroyStream(reason); + return NPERR_NO_ERROR; + } + else { + PluginStreamParent* sp = + static_cast(s); + if (sp->mInstance != this) + NS_RUNTIMEABORT("Mismatched plugin data"); + + PPluginStreamParent::Call__delete__(sp, reason, false); + return NPERR_NO_ERROR; + } +} + +void +PluginInstanceParent::NPP_Print(NPPrint* platformPrint) +{ + // TODO: implement me + NS_ERROR("Not implemented"); +} + +PPluginScriptableObjectParent* +PluginInstanceParent::AllocPPluginScriptableObject() +{ + return new PluginScriptableObjectParent(Proxy); +} + +#ifdef DEBUG +namespace { + +struct ActorSearchData +{ + PluginScriptableObjectParent* actor; + bool found; +}; + +PLDHashOperator +ActorSearch(const void* aKey, + PluginScriptableObjectParent* aData, + void* aUserData) +{ + ActorSearchData* asd = reinterpret_cast(aUserData); + if (asd->actor == aData) { + asd->found = true; + return PL_DHASH_STOP; + } + return PL_DHASH_NEXT; +} + +} // anonymous namespace +#endif // DEBUG + +bool +PluginInstanceParent::DeallocPPluginScriptableObject( + PPluginScriptableObjectParent* aObject) +{ + PluginScriptableObjectParent* actor = + static_cast(aObject); + + NPObject* object = actor->GetObject(false); + if (object) { + NS_ASSERTION(mScriptableObjects.Get(object, nsnull), + "NPObject not in the hash!"); + mScriptableObjects.Remove(object); + } +#ifdef DEBUG + else { + ActorSearchData asd = { actor, false }; + mScriptableObjects.EnumerateRead(ActorSearch, &asd); + NS_ASSERTION(!asd.found, "Actor in the hash with a null NPObject!"); + } +#endif + + delete actor; + return true; +} + +bool +PluginInstanceParent::RecvPPluginScriptableObjectConstructor( + PPluginScriptableObjectParent* aActor) +{ + // This is only called in response to the child process requesting the + // creation of an actor. This actor will represent an NPObject that is + // created by the plugin and returned to the browser. + PluginScriptableObjectParent* actor = + static_cast(aActor); + NS_ASSERTION(!actor->GetObject(false), "Actor already has an object?!"); + + actor->InitializeProxy(); + NS_ASSERTION(actor->GetObject(false), "Actor should have an object!"); + + return true; +} + +void +PluginInstanceParent::NPP_URLNotify(const char* url, NPReason reason, + void* notifyData) +{ + PLUGIN_LOG_DEBUG(("%s (%s, %i, %p)", + FULLFUNCTION, url, (int) reason, notifyData)); + + PStreamNotifyParent* streamNotify = + static_cast(notifyData); + PStreamNotifyParent::Send__delete__(streamNotify, reason); +} + +bool +PluginInstanceParent::RegisterNPObjectForActor( + NPObject* aObject, + PluginScriptableObjectParent* aActor) +{ + NS_ASSERTION(aObject && aActor, "Null pointers!"); + NS_ASSERTION(mScriptableObjects.IsInitialized(), "Hash not initialized!"); + NS_ASSERTION(!mScriptableObjects.Get(aObject, nsnull), "Duplicate entry!"); + return !!mScriptableObjects.Put(aObject, aActor); +} + +void +PluginInstanceParent::UnregisterNPObject(NPObject* aObject) +{ + NS_ASSERTION(aObject, "Null pointer!"); + NS_ASSERTION(mScriptableObjects.IsInitialized(), "Hash not initialized!"); + NS_ASSERTION(mScriptableObjects.Get(aObject, nsnull), "Unknown entry!"); + mScriptableObjects.Remove(aObject); +} + +PluginScriptableObjectParent* +PluginInstanceParent::GetActorForNPObject(NPObject* aObject) +{ + NS_ASSERTION(aObject, "Null pointer!"); + + if (aObject->_class == PluginScriptableObjectParent::GetClass()) { + // One of ours! + ParentNPObject* object = static_cast(aObject); + NS_ASSERTION(object->parent, "Null actor!"); + return object->parent; + } + + PluginScriptableObjectParent* actor; + if (mScriptableObjects.Get(aObject, &actor)) { + return actor; + } + + actor = new PluginScriptableObjectParent(LocalObject); + if (!actor) { + NS_ERROR("Out of memory!"); + return nsnull; + } + + if (!SendPPluginScriptableObjectConstructor(actor)) { + NS_WARNING("Failed to send constructor message!"); + return nsnull; + } + + actor->InitializeLocal(aObject); + return actor; +} + +bool +PluginInstanceParent::AnswerNPN_PushPopupsEnabledState(const bool& aState, + bool* aSuccess) +{ + *aSuccess = mNPNIface->pushpopupsenabledstate(mNPP, aState ? 1 : 0); + return true; +} + +bool +PluginInstanceParent::AnswerNPN_PopPopupsEnabledState(bool* aSuccess) +{ + *aSuccess = mNPNIface->poppopupsenabledstate(mNPP); + return true; +} + +bool +PluginInstanceParent::AnswerNPN_GetValueForURL(const NPNURLVariable& variable, + const nsCString& url, + nsCString* value, + NPError* result) +{ + char* v; + uint32_t len; + + *result = mNPNIface->getvalueforurl(mNPP, (NPNURLVariable) variable, + url.get(), &v, &len); + if (NPERR_NO_ERROR == *result) + value->Adopt(v, len); + + return true; +} + +bool +PluginInstanceParent::AnswerNPN_SetValueForURL(const NPNURLVariable& variable, + const nsCString& url, + const nsCString& value, + NPError* result) +{ + *result = mNPNIface->setvalueforurl(mNPP, (NPNURLVariable) variable, + url.get(), value.get(), + value.Length()); + return true; +} + +bool +PluginInstanceParent::AnswerNPN_GetAuthenticationInfo(const nsCString& protocol, + const nsCString& host, + const int32_t& port, + const nsCString& scheme, + const nsCString& realm, + nsCString* username, + nsCString* password, + NPError* result) +{ + char* u; + uint32_t ulen; + char* p; + uint32_t plen; + + *result = mNPNIface->getauthenticationinfo(mNPP, protocol.get(), + host.get(), port, + scheme.get(), realm.get(), + &u, &ulen, &p, &plen); + if (NPERR_NO_ERROR == *result) { + username->Adopt(u, ulen); + password->Adopt(p, plen); + } + return true; +} + +#if defined(OS_WIN) + +/* + plugin focus changes between processes + + focus from dom -> child: + Focs manager calls on widget to set the focus on the window. + We pick up the resulting wm_setfocus event here, and forward + that over ipc to the child which calls set focus on itself. + + focus from child -> focus manager: + Child picks up the local wm_setfocus and sends it via ipc over + here. We then post a custom event to widget/src/windows/nswindow + which fires off a gui event letting the browser know. +*/ + +static const PRUnichar kPluginInstanceParentProperty[] = + L"PluginInstanceParentProperty"; + +// static +LRESULT CALLBACK +PluginInstanceParent::PluginWindowHookProc(HWND hWnd, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + PluginInstanceParent* self = reinterpret_cast( + ::GetPropW(hWnd, kPluginInstanceParentProperty)); + if (!self) { + NS_NOTREACHED("PluginInstanceParent::PluginWindowHookProc null this ptr!"); + return DefWindowProc(hWnd, message, wParam, lParam); + } + + NS_ASSERTION(self->mPluginHWND == hWnd, "Wrong window!"); + + switch (message) { + case WM_SETFOCUS: + // Let the child plugin window know it should take focus. + self->CallSetPluginFocus(); + break; + + case WM_CLOSE: + self->UnsubclassPluginWindow(); + break; + } + + return ::CallWindowProc(self->mPluginWndProc, hWnd, message, wParam, + lParam); +} + +void +PluginInstanceParent::SubclassPluginWindow(HWND aWnd) +{ + NS_ASSERTION(!(mPluginHWND && aWnd != mPluginHWND), + "PluginInstanceParent::SubclassPluginWindow hwnd is not our window!"); + + if (!mPluginHWND) { + mPluginHWND = aWnd; + mPluginWndProc = + (WNDPROC)::SetWindowLongPtrA(mPluginHWND, GWLP_WNDPROC, + reinterpret_cast(PluginWindowHookProc)); + bool bRes = ::SetPropW(mPluginHWND, kPluginInstanceParentProperty, this); + NS_ASSERTION(mPluginWndProc, + "PluginInstanceParent::SubclassPluginWindow failed to set subclass!"); + NS_ASSERTION(bRes, + "PluginInstanceParent::SubclassPluginWindow failed to set prop!"); + } +} + +void +PluginInstanceParent::UnsubclassPluginWindow() +{ + if (mPluginHWND && mPluginWndProc) { + ::SetWindowLongPtrA(mPluginHWND, GWLP_WNDPROC, + reinterpret_cast(mPluginWndProc)); + + ::RemovePropW(mPluginHWND, kPluginInstanceParentProperty); + + mPluginWndProc = NULL; + mPluginHWND = NULL; + } +} + +/* windowless drawing helpers */ + +/* + * Origin info: + * + * windowless, offscreen: + * + * WM_WINDOWPOSCHANGED: origin is relative to container + * setwindow: origin is 0,0 + * WM_PAINT: origin is 0,0 + * + * windowless, native: + * + * WM_WINDOWPOSCHANGED: origin is relative to container + * setwindow: origin is relative to container + * WM_PAINT: origin is relative to container + * + * PluginInstanceParent: + * + * painting: mPluginPort (nsIntRect, saved in SetWindow) + */ + +void +PluginInstanceParent::SharedSurfaceRelease() +{ + mSharedSurfaceDib.Close(); +} + +bool +PluginInstanceParent::SharedSurfaceSetWindow(const NPWindow* aWindow, + NPRemoteWindow& aRemoteWindow) +{ + aRemoteWindow.window = nsnull; + aRemoteWindow.x = 0; + aRemoteWindow.y = 0; + aRemoteWindow.width = aWindow->width; + aRemoteWindow.height = aWindow->height; + aRemoteWindow.type = aWindow->type; + + nsIntRect newPort(aWindow->x, aWindow->y, aWindow->width, aWindow->height); + + // save the the rect location within the browser window. + mPluginPort = newPort; + + // move the port to our shared surface origin + newPort.MoveTo(0,0); + + // check to see if we have the room in shared surface + if (mSharedSurfaceDib.IsValid() && mSharedSize.Contains(newPort)) { + // ok to paint + aRemoteWindow.surfaceHandle = 0; + return true; + } + + // allocate a new shared surface + SharedSurfaceRelease(); + if (NS_FAILED(mSharedSurfaceDib.Create(reinterpret_cast(aWindow->window), + newPort.width, newPort.height, 32))) + return false; + + // save the new shared surface size we just allocated + mSharedSize = newPort; + + base::SharedMemoryHandle handle; + if (NS_FAILED(mSharedSurfaceDib.ShareToProcess(mParent->ChildProcessHandle(), &handle))) + return false; + + aRemoteWindow.surfaceHandle = handle; + + return true; +} + +void +PluginInstanceParent::SharedSurfaceBeforePaint(RECT& rect, + NPRemoteEvent& npremoteevent) +{ + RECT* dr = (RECT*)npremoteevent.event.lParam; + HDC parentHdc = (HDC)npremoteevent.event.wParam; + + nsIntRect dirtyRect(dr->left, dr->top, dr->right-dr->left, dr->bottom-dr->top); + dirtyRect.MoveBy(-mPluginPort.x, -mPluginPort.y); // should always be smaller than dirtyRect + + ::BitBlt(mSharedSurfaceDib.GetHDC(), + dirtyRect.x, + dirtyRect.y, + dirtyRect.width, + dirtyRect.height, + parentHdc, + dr->left, + dr->top, + SRCCOPY); + + // setup the translated dirty rect we'll send to the child + rect.left = dirtyRect.x; + rect.top = dirtyRect.y; + rect.right = dirtyRect.x + dirtyRect.width; + rect.bottom = dirtyRect.y + dirtyRect.height; + + npremoteevent.event.wParam = WPARAM(0); + npremoteevent.event.lParam = LPARAM(&rect); +} + +void +PluginInstanceParent::SharedSurfaceAfterPaint(NPEvent* npevent) +{ + RECT* dr = (RECT*)npevent->lParam; + HDC parentHdc = (HDC)npevent->wParam; + + nsIntRect dirtyRect(dr->left, dr->top, dr->right-dr->left, dr->bottom-dr->top); + dirtyRect.MoveBy(-mPluginPort.x, -mPluginPort.y); + + // src copy the shared dib into the parent surface we are handed. + ::BitBlt(parentHdc, + dr->left, + dr->top, + dirtyRect.width, + dirtyRect.height, + mSharedSurfaceDib.GetHDC(), + dirtyRect.x, + dirtyRect.y, + SRCCOPY); +} + +#endif // defined(OS_WIN) + +bool +PluginInstanceParent::AnswerPluginGotFocus() +{ + PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION)); + + // Currently only in use on windows - an rpc event we receive from the + // child when it's plugin window (or one of it's children) receives keyboard + // focus. We forward the event down to widget so the dom/focus manager can + // be updated. +#if defined(OS_WIN) + ::SendMessage(mPluginHWND, gOOPPPluginFocusEvent, 0, 0); + return true; +#else + NS_NOTREACHED("PluginInstanceParent::AnswerPluginGotFocus not implemented!"); + return false; +#endif +} + +bool +PluginInstanceParent::RecvSetNestedEventState(const bool& aState) +{ + PLUGIN_LOG_DEBUG(("%s state=%i", FULLFUNCTION, (int)aState)); +#if defined(OS_WIN) + PostThreadMessage(GetCurrentThreadId(), aState ? + gOOPPSpinNativeLoopEvent : gOOPPStopNativeLoopEvent, 0, 0); + mNestedEventState = aState; + return true; +#else + NS_NOTREACHED( + "PluginInstanceParent::AnswerSetNestedEventState not implemented!"); + return false; +#endif +} diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginInstanceParent.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginInstanceParent.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginInstanceParent.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginInstanceParent.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,273 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Chris Jones + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef dom_plugins_PluginInstanceParent_h +#define dom_plugins_PluginInstanceParent_h 1 + +#include "mozilla/plugins/PPluginInstanceParent.h" +#include "mozilla/plugins/PluginScriptableObjectParent.h" +#if defined(OS_WIN) +#include "mozilla/gfx/SharedDIBWin.h" +#endif + +#include "npfunctions.h" +#include "nsAutoPtr.h" +#include "nsDataHashtable.h" +#include "nsHashKeys.h" +#include "nsRect.h" + +namespace mozilla { +namespace plugins { + +class PBrowserStreamParent; +class PluginModuleParent; + +class PluginInstanceParent : public PPluginInstanceParent +{ + friend class PluginModuleParent; + friend class BrowserStreamParent; + friend class PluginStreamParent; + +public: + PluginInstanceParent(PluginModuleParent* parent, + NPP npp, + const NPNetscapeFuncs* npniface); + + virtual ~PluginInstanceParent(); + + bool Init(); + NPError Destroy(); + + NS_OVERRIDE virtual void ActorDestroy(ActorDestroyReason why); + + virtual PPluginScriptableObjectParent* + AllocPPluginScriptableObject(); + + NS_OVERRIDE virtual bool + RecvPPluginScriptableObjectConstructor(PPluginScriptableObjectParent* aActor); + + virtual bool + DeallocPPluginScriptableObject(PPluginScriptableObjectParent* aObject); + virtual PBrowserStreamParent* + AllocPBrowserStream(const nsCString& url, + const uint32_t& length, + const uint32_t& lastmodified, + PStreamNotifyParent* notifyData, + const nsCString& headers, + const nsCString& mimeType, + const bool& seekable, + NPError* rv, + uint16_t *stype); + virtual bool + DeallocPBrowserStream(PBrowserStreamParent* stream); + + virtual PPluginStreamParent* + AllocPPluginStream(const nsCString& mimeType, + const nsCString& target, + NPError* result); + virtual bool + DeallocPPluginStream(PPluginStreamParent* stream); + + virtual bool + AnswerNPN_GetValue_NPNVjavascriptEnabledBool(bool* value, NPError* result); + virtual bool + AnswerNPN_GetValue_NPNVisOfflineBool(bool* value, NPError* result); + virtual bool + AnswerNPN_GetValue_NPNVnetscapeWindow(NativeWindowHandle* value, + NPError* result); + virtual bool + AnswerNPN_GetValue_NPNVWindowNPObject( + PPluginScriptableObjectParent** value, + NPError* result); + virtual bool + AnswerNPN_GetValue_NPNVPluginElementNPObject( + PPluginScriptableObjectParent** value, + NPError* result); + virtual bool + AnswerNPN_GetValue_NPNVprivateModeBool(bool* value, NPError* result); + + virtual bool + AnswerNPN_SetValue_NPPVpluginWindow(const bool& windowed, NPError* result); + virtual bool + AnswerNPN_SetValue_NPPVpluginTransparent(const bool& transparent, + NPError* result); + + virtual bool + AnswerNPN_GetURL(const nsCString& url, const nsCString& target, + NPError *result); + + virtual bool + AnswerNPN_PostURL(const nsCString& url, const nsCString& target, + const nsCString& buffer, const bool& file, + NPError* result); + + virtual PStreamNotifyParent* + AllocPStreamNotify(const nsCString& url, const nsCString& target, + const bool& post, const nsCString& buffer, + const bool& file, + NPError* result); + + NS_OVERRIDE virtual bool + AnswerPStreamNotifyConstructor(PStreamNotifyParent* actor, + const nsCString& url, + const nsCString& target, + const bool& post, const nsCString& buffer, + const bool& file, + NPError* result); + + virtual bool + DeallocPStreamNotify(PStreamNotifyParent* notifyData); + + virtual bool + RecvNPN_InvalidateRect(const NPRect& rect); + + virtual bool + AnswerNPN_PushPopupsEnabledState(const bool& aState, + bool* aSuccess); + + virtual bool + AnswerNPN_PopPopupsEnabledState(bool* aSuccess); + + NS_OVERRIDE virtual bool + AnswerNPN_GetValueForURL(const NPNURLVariable& variable, + const nsCString& url, + nsCString* value, NPError* result); + + NS_OVERRIDE virtual bool + AnswerNPN_SetValueForURL(const NPNURLVariable& variable, + const nsCString& url, + const nsCString& value, NPError* result); + + NS_OVERRIDE virtual bool + AnswerNPN_GetAuthenticationInfo(const nsCString& protocol, + const nsCString& host, + const int32_t& port, + const nsCString& scheme, + const nsCString& realm, + nsCString* username, + nsCString* password, + NPError* result); + + NPError NPP_SetWindow(const NPWindow* aWindow); + + NPError NPP_GetValue(NPPVariable variable, void* retval); + NPError NPP_SetValue(NPNVariable variable, void* value); + + NPError NPP_NewStream(NPMIMEType type, NPStream* stream, + NPBool seekable, uint16_t* stype); + NPError NPP_DestroyStream(NPStream* stream, NPReason reason); + + void NPP_Print(NPPrint* platformPrint); + + int16_t NPP_HandleEvent(void* event); + + void NPP_URLNotify(const char* url, NPReason reason, void* notifyData); + + PluginModuleParent* Module() + { + return mParent; + } + + const NPNetscapeFuncs* GetNPNIface() + { + return mNPNIface; + } + + bool + RegisterNPObjectForActor(NPObject* aObject, + PluginScriptableObjectParent* aActor); + + void + UnregisterNPObject(NPObject* aObject); + + PluginScriptableObjectParent* + GetActorForNPObject(NPObject* aObject); + + NPP + GetNPP() + { + return mNPP; + } + + virtual bool + AnswerPluginGotFocus(); + + virtual bool + RecvSetNestedEventState(const bool& aState); + +private: + bool InternalGetValueForNPObject(NPNVariable aVariable, + PPluginScriptableObjectParent** aValue, + NPError* aResult); + +private: + PluginModuleParent* mParent; + NPP mNPP; + const NPNetscapeFuncs* mNPNIface; + NPWindowType mWindowType; + + nsDataHashtable mScriptableObjects; + +#if defined(OS_WIN) +private: + // Used in rendering windowless plugins in other processes. + bool SharedSurfaceSetWindow(const NPWindow* aWindow, NPRemoteWindow& aRemoteWindow); + void SharedSurfaceBeforePaint(RECT &rect, NPRemoteEvent& npremoteevent); + void SharedSurfaceAfterPaint(NPEvent* npevent); + void SharedSurfaceRelease(); + // Used in handling parent/child forwarding of events. + static LRESULT CALLBACK PluginWindowHookProc(HWND hWnd, UINT message, + WPARAM wParam, LPARAM lParam); + void SubclassPluginWindow(HWND aWnd); + void UnsubclassPluginWindow(); + +private: + gfx::SharedDIBWin mSharedSurfaceDib; + nsIntRect mPluginPort; + nsIntRect mSharedSize; + HWND mPluginHWND; + WNDPROC mPluginWndProc; + bool mNestedEventState; +#endif // defined(XP_WIN) +}; + + +} // namespace plugins +} // namespace mozilla + +#endif // ifndef dom_plugins_PluginInstanceParent_h diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginLibrary.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginLibrary.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginLibrary.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginLibrary.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=4 ts=4 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Foundation. + * + * The Initial Developer of the Original Code is + * Josh Aas + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_PluginLibrary_h +#define mozilla_PluginLibrary_h 1 + +#include "prlink.h" +#include "npapi.h" +#include "npfunctions.h" +#include "nscore.h" + +class nsNPAPIPlugin; + +namespace mozilla { + +class PluginLibrary +{ +public: + virtual ~PluginLibrary() { } + + /** + * Inform this library about the nsNPAPIPlugin which owns it. This + * object will hold a weak pointer to the plugin. + */ + virtual void SetPlugin(nsNPAPIPlugin* plugin) = 0; + + virtual bool HasRequiredFunctions() = 0; + +#if defined(XP_UNIX) && !defined(XP_MACOSX) + virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) = 0; +#else + virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) = 0; +#endif + virtual nsresult NP_Shutdown(NPError* error) = 0; + virtual nsresult NP_GetMIMEDescription(const char** mimeDesc) = 0; + virtual nsresult NP_GetValue(void *future, NPPVariable aVariable, + void *aValue, NPError* error) = 0; +#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_OS2) + virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) = 0; +#endif + virtual nsresult NPP_New(NPMIMEType pluginType, NPP instance, + uint16_t mode, int16_t argc, char* argn[], + char* argv[], NPSavedData* saved, + NPError* error) = 0; +}; + + +} // namespace mozilla + +#endif // ifndef mozilla_PluginLibrary_h diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginMessageUtils.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginMessageUtils.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginMessageUtils.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginMessageUtils.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,124 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8; -*- */ +/* vim: set sw=2 ts=8 et tw=80 : */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Content App. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "PluginMessageUtils.h" +#include "nsIRunnable.h" +#include "nsThreadUtils.h" + +#include "PluginInstanceParent.h" +#include "PluginInstanceChild.h" +#include "PluginScriptableObjectParent.h" +#include "PluginScriptableObjectChild.h" + +using mozilla::ipc::RPCChannel; + +namespace { + +class DeferNPObjectReleaseRunnable : public nsRunnable +{ +public: + DeferNPObjectReleaseRunnable(const NPNetscapeFuncs* f, NPObject* o) + : mFuncs(f) + , mObject(o) + { + NS_ASSERTION(o, "no release null objects"); + } + + NS_IMETHOD Run(); + +private: + const NPNetscapeFuncs* mFuncs; + NPObject* mObject; +}; + +NS_IMETHODIMP +DeferNPObjectReleaseRunnable::Run() +{ + mFuncs->releaseobject(mObject); + return NS_OK; +} + +} // anonymous namespace + +namespace mozilla { +namespace plugins { + +RPCChannel::RacyRPCPolicy +MediateRace(const RPCChannel::Message& parent, + const RPCChannel::Message& child) +{ + switch (parent.type()) { + case PPluginInstance::Msg_Paint__ID: + case PPluginInstance::Msg_NPP_SetWindow__ID: + // our code relies on the frame list not changing during paints and + // reflows + return RPCChannel::RRPParentWins; + + default: + return RPCChannel::RRPChildWins; + } +} + +PRLogModuleInfo* gPluginLog = PR_NewLogModule("IPCPlugins"); + +void +DeferNPObjectLastRelease(const NPNetscapeFuncs* f, NPObject* o) +{ + if (!o) + return; + + if (o->referenceCount > 1) { + f->releaseobject(o); + return; + } + + NS_DispatchToCurrentThread(new DeferNPObjectReleaseRunnable(f, o)); +} + +void DeferNPVariantLastRelease(const NPNetscapeFuncs* f, NPVariant* v) +{ + if (!NPVARIANT_IS_OBJECT(*v)) { + f->releasevariantvalue(v); + return; + } + DeferNPObjectLastRelease(f, v->value.objectValue); + VOID_TO_NPVARIANT(*v); +} + +} // namespace plugins +} // namespace mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginMessageUtils.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginMessageUtils.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginMessageUtils.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginMessageUtils.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,710 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=4 ts=4 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Chris Jones + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef DOM_PLUGINS_PLUGINMESSAGEUTILS_H +#define DOM_PLUGINS_PLUGINMESSAGEUTILS_H + +#include "IPC/IPCMessageUtils.h" +#include "base/message_loop.h" + +#include "mozilla/ipc/RPCChannel.h" + +#include "npapi.h" +#include "npruntime.h" +#include "npfunctions.h" +#include "nsAutoPtr.h" +#include "nsStringGlue.h" +#include "nsTArray.h" +#include "nsThreadUtils.h" +#include "prlog.h" +#include "nsHashKeys.h" +#ifdef MOZ_CRASHREPORTER +# include "nsExceptionHandler.h" +#endif + +namespace mozilla { + +// XXX might want to move these to nscore.h or something, they can be +// generally useful +struct void_t { }; +struct null_t { }; + +namespace plugins { + +enum ScriptableObjectType +{ + LocalObject, + Proxy +}; + +mozilla::ipc::RPCChannel::RacyRPCPolicy +MediateRace(const mozilla::ipc::RPCChannel::Message& parent, + const mozilla::ipc::RPCChannel::Message& child); + +extern PRLogModuleInfo* gPluginLog; + +#if defined(_MSC_VER) +#define FULLFUNCTION __FUNCSIG__ +#elif (__GNUC__ >= 4) +#define FULLFUNCTION __PRETTY_FUNCTION__ +#else +#define FULLFUNCTION __FUNCTION__ +#endif + +#define PLUGIN_LOG_DEBUG(args) PR_LOG(gPluginLog, PR_LOG_DEBUG, args) +#define PLUGIN_LOG_DEBUG_FUNCTION PR_LOG(gPluginLog, PR_LOG_DEBUG, ("%s", FULLFUNCTION)) +#define PLUGIN_LOG_DEBUG_METHOD PR_LOG(gPluginLog, PR_LOG_DEBUG, ("%s [%p]", FULLFUNCTION, (void*) this)) + +/** + * This is NPByteRange without the linked list. + */ +struct IPCByteRange +{ + int32_t offset; + uint32_t length; +}; + +typedef std::vector IPCByteRanges; + +typedef nsCString Buffer; + +struct NPRemoteWindow +{ + unsigned long window; + int32_t x; + int32_t y; + uint32_t width; + uint32_t height; + NPRect clipRect; + NPWindowType type; +#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX) + VisualID visualID; + Colormap colormap; +#endif /* XP_UNIX */ +#if defined(XP_WIN) + base::SharedMemoryHandle surfaceHandle; +#endif +}; + +#ifdef XP_WIN +typedef HWND NativeWindowHandle; +#elif defined(MOZ_X11) +typedef XID NativeWindowHandle; +#else +#error Need NativeWindowHandle for this platform +#endif + +#ifdef MOZ_CRASHREPORTER +typedef CrashReporter::ThreadId NativeThreadId; +#else +// unused in this case +typedef int32 NativeThreadId; +#endif + +// XXX maybe not the best place for these. better one? + +#define VARSTR(v_) case v_: return #v_ +inline const char* const +NPPVariableToString(NPPVariable aVar) +{ + switch (aVar) { + VARSTR(NPPVpluginNameString); + VARSTR(NPPVpluginDescriptionString); + VARSTR(NPPVpluginWindowBool); + VARSTR(NPPVpluginTransparentBool); + VARSTR(NPPVjavaClass); + VARSTR(NPPVpluginWindowSize); + VARSTR(NPPVpluginTimerInterval); + + VARSTR(NPPVpluginScriptableInstance); + VARSTR(NPPVpluginScriptableIID); + + VARSTR(NPPVjavascriptPushCallerBool); + + VARSTR(NPPVpluginKeepLibraryInMemory); + VARSTR(NPPVpluginNeedsXEmbed); + + VARSTR(NPPVpluginScriptableNPObject); + + VARSTR(NPPVformValue); + + VARSTR(NPPVpluginUrlRequestsDisplayedBool); + + VARSTR(NPPVpluginWantsAllNetworkStreams); + +#ifdef XP_MACOSX + VARSTR(NPPVpluginDrawingModel); + VARSTR(NPPVpluginEventModel); +#endif + + default: return "???"; + } +} + +inline const char* +NPNVariableToString(NPNVariable aVar) +{ + switch(aVar) { + VARSTR(NPNVxDisplay); + VARSTR(NPNVxtAppContext); + VARSTR(NPNVnetscapeWindow); + VARSTR(NPNVjavascriptEnabledBool); + VARSTR(NPNVasdEnabledBool); + VARSTR(NPNVisOfflineBool); + + VARSTR(NPNVserviceManager); + VARSTR(NPNVDOMElement); + VARSTR(NPNVDOMWindow); + VARSTR(NPNVToolkit); + VARSTR(NPNVSupportsXEmbedBool); + + VARSTR(NPNVWindowNPObject); + + VARSTR(NPNVPluginElementNPObject); + + VARSTR(NPNVSupportsWindowless); + + VARSTR(NPNVprivateModeBool); + + default: return "???"; + } +} +#undef VARSTR + +inline bool IsPluginThread() +{ + MessageLoop* loop = MessageLoop::current(); + if (!loop) + return false; + return (loop->type() == MessageLoop::TYPE_UI); +} + +inline void AssertPluginThread() +{ + NS_ASSERTION(IsPluginThread(), "Should be on the plugin's main thread!"); +} + +#define ENSURE_PLUGIN_THREAD(retval) \ + PR_BEGIN_MACRO \ + if (!IsPluginThread()) { \ + NS_WARNING("Not running on the plugin's main thread!"); \ + return (retval); \ + } \ + PR_END_MACRO + +#define ENSURE_PLUGIN_THREAD_VOID() \ + PR_BEGIN_MACRO \ + if (!IsPluginThread()) { \ + NS_WARNING("Not running on the plugin's main thread!"); \ + return; \ + } \ + PR_END_MACRO + +void DeferNPObjectLastRelease(const NPNetscapeFuncs* f, NPObject* o); +void DeferNPVariantLastRelease(const NPNetscapeFuncs* f, NPVariant* v); + +// in NPAPI, char* == NULL is sometimes meaningful. the following is +// helper code for dealing with nullable nsCString's +inline nsCString +NullableString(const char* aString) +{ + if (!aString) { + nsCString str; + str.SetIsVoid(PR_TRUE); + return str; + } + return nsCString(aString); +} + +inline const char* +NullableStringGet(const nsCString& str) +{ + if (str.IsVoid()) + return NULL; + + return str.get(); +} + +struct DeletingObjectEntry : public nsPtrHashKey +{ + DeletingObjectEntry(const NPObject* key) + : nsPtrHashKey(key) + , mDeleted(false) + { } + + bool mDeleted; +}; + +} /* namespace plugins */ + +} /* namespace mozilla */ + +namespace IPC { + +template <> +struct ParamTraits +{ + typedef NPRect paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.top); + WriteParam(aMsg, aParam.left); + WriteParam(aMsg, aParam.bottom); + WriteParam(aMsg, aParam.right); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + uint16_t top, left, bottom, right; + if (ReadParam(aMsg, aIter, &top) && + ReadParam(aMsg, aIter, &left) && + ReadParam(aMsg, aIter, &bottom) && + ReadParam(aMsg, aIter, &right)) { + aResult->top = top; + aResult->left = left; + aResult->bottom = bottom; + aResult->right = right; + return true; + } + return false; + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + aLog->append(StringPrintf(L"[%u, %u, %u, %u]", aParam.top, aParam.left, + aParam.bottom, aParam.right)); + } +}; + +template <> +struct ParamTraits +{ + typedef NPWindowType paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + aMsg->WriteInt16(int16(aParam)); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + int16 result; + if (aMsg->ReadInt16(aIter, &result)) { + *aResult = paramType(result); + return true; + } + return false; + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + aLog->append(StringPrintf(L"%d", int16(aParam))); + } +}; + +template <> +struct ParamTraits +{ + typedef mozilla::plugins::NPRemoteWindow paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + aMsg->WriteULong(aParam.window); + WriteParam(aMsg, aParam.x); + WriteParam(aMsg, aParam.y); + WriteParam(aMsg, aParam.width); + WriteParam(aMsg, aParam.height); + WriteParam(aMsg, aParam.clipRect); + WriteParam(aMsg, aParam.type); +#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX) + aMsg->WriteULong(aParam.visualID); + aMsg->WriteULong(aParam.colormap); +#endif +#if defined(XP_WIN) + WriteParam(aMsg, aParam.surfaceHandle); +#endif + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + unsigned long window; + int32_t x, y; + uint32_t width, height; + NPRect clipRect; + NPWindowType type; + if (!(aMsg->ReadULong(aIter, &window) && + ReadParam(aMsg, aIter, &x) && + ReadParam(aMsg, aIter, &y) && + ReadParam(aMsg, aIter, &width) && + ReadParam(aMsg, aIter, &height) && + ReadParam(aMsg, aIter, &clipRect) && + ReadParam(aMsg, aIter, &type))) + return false; + +#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX) + unsigned long visualID; + unsigned long colormap; + if (!(aMsg->ReadULong(aIter, &visualID) && + aMsg->ReadULong(aIter, &colormap))) + return false; +#endif + +#if defined(XP_WIN) + base::SharedMemoryHandle surfaceHandle; + if (!ReadParam(aMsg, aIter, &surfaceHandle)) + return false; +#endif + + aResult->window = window; + aResult->x = x; + aResult->y = y; + aResult->width = width; + aResult->height = height; + aResult->clipRect = clipRect; + aResult->type = type; +#if defined(MOZ_X11) && defined(XP_UNIX) && !defined(XP_MACOSX) + aResult->visualID = visualID; + aResult->colormap = colormap; +#endif +#if defined(XP_WIN) + aResult->surfaceHandle = surfaceHandle; +#endif + return true; + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + aLog->append(StringPrintf(L"[%u, %d, %d, %u, %u, %d", + (unsigned long)aParam.window, + aParam.x, aParam.y, aParam.width, + aParam.height, (long)aParam.type)); + } +}; + +template <> +struct ParamTraits +{ + typedef NPString paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.UTF8Length); + aMsg->WriteBytes(aParam.UTF8Characters, + aParam.UTF8Length * sizeof(NPUTF8)); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + if (ReadParam(aMsg, aIter, &aResult->UTF8Length)) { + int byteCount = aResult->UTF8Length * sizeof(NPUTF8); + if (!byteCount) { + aResult->UTF8Characters = "\0"; + return true; + } + + const char* messageBuffer = nsnull; + nsAutoArrayPtr newBuffer(new char[byteCount]); + if (newBuffer && aMsg->ReadBytes(aIter, &messageBuffer, byteCount )) { + memcpy((void*)messageBuffer, newBuffer.get(), byteCount); + aResult->UTF8Characters = newBuffer.forget(); + return true; + } + } + return false; + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + aLog->append(StringPrintf(L"%s", aParam.UTF8Characters)); + } +}; + +template <> +struct ParamTraits +{ + typedef NPVariant paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + if (NPVARIANT_IS_VOID(aParam)) { + aMsg->WriteInt(0); + return; + } + + if (NPVARIANT_IS_NULL(aParam)) { + aMsg->WriteInt(1); + return; + } + + if (NPVARIANT_IS_BOOLEAN(aParam)) { + aMsg->WriteInt(2); + WriteParam(aMsg, NPVARIANT_TO_BOOLEAN(aParam)); + return; + } + + if (NPVARIANT_IS_INT32(aParam)) { + aMsg->WriteInt(3); + WriteParam(aMsg, NPVARIANT_TO_INT32(aParam)); + return; + } + + if (NPVARIANT_IS_DOUBLE(aParam)) { + aMsg->WriteInt(4); + WriteParam(aMsg, NPVARIANT_TO_DOUBLE(aParam)); + return; + } + + if (NPVARIANT_IS_STRING(aParam)) { + aMsg->WriteInt(5); + WriteParam(aMsg, NPVARIANT_TO_STRING(aParam)); + return; + } + + NS_ERROR("Unsupported type!"); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + int type; + if (!aMsg->ReadInt(aIter, &type)) { + return false; + } + + switch (type) { + case 0: + VOID_TO_NPVARIANT(*aResult); + return true; + + case 1: + NULL_TO_NPVARIANT(*aResult); + return true; + + case 2: { + bool value; + if (ReadParam(aMsg, aIter, &value)) { + BOOLEAN_TO_NPVARIANT(value, *aResult); + return true; + } + } break; + + case 3: { + int32 value; + if (ReadParam(aMsg, aIter, &value)) { + INT32_TO_NPVARIANT(value, *aResult); + return true; + } + } break; + + case 4: { + double value; + if (ReadParam(aMsg, aIter, &value)) { + DOUBLE_TO_NPVARIANT(value, *aResult); + return true; + } + } break; + + case 5: { + NPString value; + if (ReadParam(aMsg, aIter, &value)) { + STRINGN_TO_NPVARIANT(value.UTF8Characters, value.UTF8Length, + *aResult); + return true; + } + } break; + + default: + NS_ERROR("Unsupported type!"); + } + + return false; + } + + static void Log(const paramType& aParam, std::wstring* aLog) + { + if (NPVARIANT_IS_VOID(aParam)) { + aLog->append(L"[void]"); + return; + } + + if (NPVARIANT_IS_NULL(aParam)) { + aLog->append(L"[null]"); + return; + } + + if (NPVARIANT_IS_BOOLEAN(aParam)) { + LogParam(NPVARIANT_TO_BOOLEAN(aParam), aLog); + return; + } + + if (NPVARIANT_IS_INT32(aParam)) { + LogParam(NPVARIANT_TO_INT32(aParam), aLog); + return; + } + + if (NPVARIANT_IS_DOUBLE(aParam)) { + LogParam(NPVARIANT_TO_DOUBLE(aParam), aLog); + return; + } + + if (NPVARIANT_IS_STRING(aParam)) { + LogParam(NPVARIANT_TO_STRING(aParam), aLog); + return; + } + + NS_ERROR("Unsupported type!"); + } +}; + +template<> +struct ParamTraits +{ + typedef mozilla::void_t paramType; + static void Write(Message* aMsg, const paramType& aParam) { } + static bool + Read(const Message* aMsg, void** aIter, paramType* aResult) + { + *aResult = paramType(); + return true; + } +}; + +template<> +struct ParamTraits +{ + typedef mozilla::null_t paramType; + static void Write(Message* aMsg, const paramType& aParam) { } + static bool + Read(const Message* aMsg, void** aIter, paramType* aResult) + { + *aResult = paramType(); + return true; + } +}; + +template <> +struct ParamTraits +{ + typedef mozilla::plugins::IPCByteRange paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, aParam.offset); + WriteParam(aMsg, aParam.length); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + paramType p; + if (ReadParam(aMsg, aIter, &p.offset) && + ReadParam(aMsg, aIter, &p.length)) { + *aResult = p; + return true; + } + return false; + } +}; + +template <> +struct ParamTraits +{ + typedef NPNVariable paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, int(aParam)); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + int intval; + if (ReadParam(aMsg, aIter, &intval)) { + *aResult = paramType(intval); + return true; + } + return false; + } +}; + +template<> +struct ParamTraits +{ + typedef NPNURLVariable paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + WriteParam(aMsg, int(aParam)); + } + + static bool Read(const Message* aMsg, void** aIter, paramType* aResult) + { + int intval; + if (ReadParam(aMsg, aIter, &intval)) { + switch (intval) { + case NPNURLVCookie: + case NPNURLVProxy: + *aResult = paramType(intval); + return true; + } + } + return false; + } +}; + +} /* namespace IPC */ + + +// Serializing NPEvents is completely platform-specific and can be rather +// intricate depending on the platform. So for readability we split it +// into separate files and have the only macro crud live here. +// +// NB: these guards are based on those where struct NPEvent is defined +// in npapi.h. They should be kept in sync. +#if defined(XP_MACOSX) +# include "mozilla/plugins/NPEventOSX.h" +#elif defined(XP_WIN) +# include "mozilla/plugins/NPEventWindows.h" +#elif defined(XP_OS2) +# error Sorry, OS/2 is not supported +#elif defined(XP_UNIX) && defined(MOZ_X11) +# include "mozilla/plugins/NPEventX11.h" +#else +# error Unsupported platform +#endif + +#endif /* DOM_PLUGINS_PLUGINMESSAGEUTILS_H */ diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginModuleChild.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginModuleChild.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginModuleChild.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginModuleChild.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,1786 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Ben Turner . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifdef MOZ_WIDGET_QT +#include +#endif + +#include "mozilla/plugins/PluginModuleChild.h" + +#ifdef MOZ_WIDGET_GTK2 +#include +#endif + +#include "nsILocalFile.h" + +#include "pratom.h" +#include "nsDebug.h" +#include "nsCOMPtr.h" +#include "nsPluginsDir.h" +#include "nsXULAppAPI.h" + +#include "mozilla/plugins/PluginInstanceChild.h" +#include "mozilla/plugins/StreamNotifyChild.h" +#include "mozilla/plugins/BrowserStreamChild.h" +#include "mozilla/plugins/PluginStreamChild.h" +#include "mozilla/plugins/PluginThreadChild.h" +#include "PluginIdentifierChild.h" + +#include "nsNPAPIPlugin.h" + +using namespace mozilla::plugins; + +namespace { +PluginModuleChild* gInstance = nsnull; +#ifdef MOZ_WIDGET_QT +static QApplication *gQApp = nsnull; +#endif +} + + +PluginModuleChild::PluginModuleChild() : + mLibrary(0), + mInitializeFunc(0), + mShutdownFunc(0) +#ifdef OS_WIN + , mGetEntryPointsFunc(0) +#elif defined(MOZ_WIDGET_GTK2) + , mNestedLoopTimerId(0) +#endif +{ + NS_ASSERTION(!gInstance, "Something terribly wrong here!"); + memset(&mFunctions, 0, sizeof(mFunctions)); + memset(&mSavedData, 0, sizeof(mSavedData)); + gInstance = this; +} + +PluginModuleChild::~PluginModuleChild() +{ + NS_ASSERTION(gInstance == this, "Something terribly wrong here!"); + if (mLibrary) { + PR_UnloadLibrary(mLibrary); + } +#ifdef MOZ_WIDGET_QT + if (gQApp) + delete gQApp; + gQApp = nsnull; +#endif + gInstance = nsnull; +} + +// static +PluginModuleChild* +PluginModuleChild::current() +{ + NS_ASSERTION(gInstance, "Null instance!"); + return gInstance; +} + +bool +PluginModuleChild::Init(const std::string& aPluginFilename, + base::ProcessHandle aParentProcessHandle, + MessageLoop* aIOLoop, + IPC::Channel* aChannel) +{ + PLUGIN_LOG_DEBUG_METHOD; + + NS_ASSERTION(aChannel, "need a channel"); + + if (!mObjectMap.Init()) { + NS_WARNING("Failed to initialize object hashtable!"); + return false; + } + + if (!mStringIdentifiers.Init()) { + NS_ERROR("Failed to initialize string identifier hashtable!"); + return false; + } + + if (!mIntIdentifiers.Init()) { + NS_ERROR("Failed to initialize int identifier hashtable!"); + return false; + } + + if (!InitGraphics()) + return false; + + nsCString filename; + filename = aPluginFilename.c_str(); + nsCOMPtr pluginFile; + NS_NewNativeLocalFile(filename, + PR_TRUE, + getter_AddRefs(pluginFile)); + + PRBool exists; + pluginFile->Exists(&exists); + NS_ASSERTION(exists, "plugin file ain't there"); + + nsCOMPtr pluginIfile; + pluginIfile = do_QueryInterface(pluginFile); + + nsPluginFile lib(pluginIfile); + + nsresult rv = lib.LoadPlugin(mLibrary); + NS_ASSERTION(NS_OK == rv, "trouble with mPluginFile"); + NS_ASSERTION(mLibrary, "couldn't open shared object"); + + if (!Open(aChannel, aParentProcessHandle, aIOLoop)) + return false; + + memset((void*) &mFunctions, 0, sizeof(mFunctions)); + mFunctions.size = sizeof(mFunctions); + + // TODO: use PluginPRLibrary here + +#if defined(OS_LINUX) + mShutdownFunc = + (NP_PLUGINSHUTDOWN) PR_FindFunctionSymbol(mLibrary, "NP_Shutdown"); + + // create the new plugin handler + + mInitializeFunc = + (NP_PLUGINUNIXINIT) PR_FindFunctionSymbol(mLibrary, "NP_Initialize"); + NS_ASSERTION(mInitializeFunc, "couldn't find NP_Initialize()"); + +#elif defined(OS_WIN) + mShutdownFunc = + (NP_PLUGINSHUTDOWN)PR_FindFunctionSymbol(mLibrary, "NP_Shutdown"); + + mGetEntryPointsFunc = + (NP_GETENTRYPOINTS)PR_FindSymbol(mLibrary, "NP_GetEntryPoints"); + NS_ENSURE_TRUE(mGetEntryPointsFunc, false); + + mInitializeFunc = + (NP_PLUGININIT)PR_FindFunctionSymbol(mLibrary, "NP_Initialize"); + NS_ENSURE_TRUE(mInitializeFunc, false); +#elif defined(OS_MACOSX) +# warning IMPLEMENT ME + +#else + +# error Please copy the initialization code from nsNPAPIPlugin.cpp + +#endif + return true; +} + +#if defined(MOZ_WIDGET_GTK2) +typedef void (*GObjectDisposeFn)(GObject*); +typedef gboolean (*GtkWidgetScrollEventFn)(GtkWidget*, GdkEventScroll*); +typedef void (*GtkPlugEmbeddedFn)(GtkPlug*); + +static GObjectDisposeFn real_gtk_plug_dispose; +static GtkPlugEmbeddedFn real_gtk_plug_embedded; + +static void +undo_bogus_unref(gpointer data, GObject* object, gboolean is_last_ref) { + if (!is_last_ref) // recursion in g_object_ref + return; + + g_object_ref(object); +} + +static void +wrap_gtk_plug_dispose(GObject* object) { + // Work around Flash Player bug described in bug 538914. + // + // This function is called during gtk_widget_destroy and/or before + // the object's last reference is removed. A reference to the + // object is held during the call so the ref count should not drop + // to zero. However, Flash Player tries to destroy the GtkPlug + // using g_object_unref instead of gtk_widget_destroy. The + // reference that Flash is removing actually belongs to the + // GtkPlug. During real_gtk_plug_dispose, the GtkPlug removes its + // reference. + // + // A toggle ref is added to prevent premature deletion of the object + // caused by Flash Player's extra unref, and to detect when there are + // unexpectedly no other references. + g_object_add_toggle_ref(object, undo_bogus_unref, NULL); + (*real_gtk_plug_dispose)(object); + g_object_remove_toggle_ref(object, undo_bogus_unref, NULL); +} + +static gboolean +gtk_plug_scroll_event(GtkWidget *widget, GdkEventScroll *gdk_event) +{ + if (!GTK_WIDGET_TOPLEVEL(widget)) // in same process as its GtkSocket + return FALSE; // event not handled; propagate to GtkSocket + + GdkWindow* socket_window = GTK_PLUG(widget)->socket_window; + if (!socket_window) + return FALSE; + + // Propagate the event to the embedder. + GdkScreen* screen = gdk_drawable_get_screen(socket_window); + GdkWindow* plug_window = widget->window; + GdkWindow* event_window = gdk_event->window; + gint x = gdk_event->x; + gint y = gdk_event->y; + unsigned int button; + unsigned int button_mask = 0; + XEvent xevent; + Display* dpy = GDK_WINDOW_XDISPLAY(socket_window); + + /* Translate the event coordinates to the plug window, + * which should be aligned with the socket window. + */ + while (event_window != plug_window) + { + gint dx, dy; + + gdk_window_get_position(event_window, &dx, &dy); + x += dx; + y += dy; + + event_window = gdk_window_get_parent(event_window); + if (!event_window) + return FALSE; + } + + switch (gdk_event->direction) { + case GDK_SCROLL_UP: + button = 4; + button_mask = Button4Mask; + break; + case GDK_SCROLL_DOWN: + button = 5; + button_mask = Button5Mask; + break; + case GDK_SCROLL_LEFT: + button = 6; + break; + case GDK_SCROLL_RIGHT: + button = 7; + break; + default: + return FALSE; // unknown GdkScrollDirection + } + + memset(&xevent, 0, sizeof(xevent)); + xevent.xbutton.type = ButtonPress; + xevent.xbutton.window = GDK_WINDOW_XWINDOW(socket_window); + xevent.xbutton.root = GDK_WINDOW_XWINDOW(gdk_screen_get_root_window(screen)); + xevent.xbutton.subwindow = GDK_WINDOW_XWINDOW(plug_window); + xevent.xbutton.time = gdk_event->time; + xevent.xbutton.x = x; + xevent.xbutton.y = y; + xevent.xbutton.x_root = gdk_event->x_root; + xevent.xbutton.y_root = gdk_event->y_root; + xevent.xbutton.state = gdk_event->state; + xevent.xbutton.button = button; + xevent.xbutton.same_screen = True; + + gdk_error_trap_push(); + + XSendEvent(dpy, xevent.xbutton.window, + True, ButtonPressMask, &xevent); + + xevent.xbutton.type = ButtonRelease; + xevent.xbutton.state |= button_mask; + XSendEvent(dpy, xevent.xbutton.window, + True, ButtonReleaseMask, &xevent); + + gdk_display_sync(gdk_screen_get_display(screen)); + gdk_error_trap_pop(); + + return TRUE; // event handled +} + +static void +wrap_gtk_plug_embedded(GtkPlug* plug) { + GdkWindow* socket_window = plug->socket_window; + if (socket_window && + g_object_get_data(G_OBJECT(socket_window), + "moz-existed-before-set-window")) { + // Add missing reference for + // https://bugzilla.gnome.org/show_bug.cgi?id=607061 + g_object_ref(socket_window); + } + + if (*real_gtk_plug_embedded) { + (*real_gtk_plug_embedded)(plug); + } +} + +// +// The next four constants are knobs that can be tuned. They trade +// off potential UI lag from delayed event processing with CPU time. +// +static const gint kNestedLoopDetectorPriority = G_PRIORITY_HIGH_IDLE; +// 90ms so that we can hopefully break livelocks before the user +// notices UI lag (100ms) +static const guint kNestedLoopDetectorIntervalMs = 90; + +static const gint kBrowserEventPriority = G_PRIORITY_HIGH_IDLE; +static const guint kBrowserEventIntervalMs = 10; + +// static +gboolean +PluginModuleChild::DetectNestedEventLoop(gpointer data) +{ + PluginModuleChild* pmc = static_cast(data); + + NS_ABORT_IF_FALSE(0 != pmc->mNestedLoopTimerId, + "callback after descheduling"); + NS_ABORT_IF_FALSE(pmc->mTopLoopDepth < g_main_depth(), + "not canceled before returning to main event loop!"); + + PLUGIN_LOG_DEBUG(("Detected nested glib event loop")); + + // just detected a nested loop; start a timer that will + // periodically rpc-call back into the browser and process some + // events + pmc->mNestedLoopTimerId = + g_timeout_add_full(kBrowserEventPriority, + kBrowserEventIntervalMs, + PluginModuleChild::ProcessBrowserEvents, + data, + NULL); + // cancel the nested-loop detection timer + return FALSE; +} + +// static +gboolean +PluginModuleChild::ProcessBrowserEvents(gpointer data) +{ + PluginModuleChild* pmc = static_cast(data); + + NS_ABORT_IF_FALSE(pmc->mTopLoopDepth < g_main_depth(), + "not canceled before returning to main event loop!"); + + pmc->CallProcessSomeEvents(); + + return TRUE; +} + +void +PluginModuleChild::EnteredCxxStack() +{ + NS_ABORT_IF_FALSE(0 == mNestedLoopTimerId, + "previous timer not descheduled"); + + mNestedLoopTimerId = + g_timeout_add_full(kNestedLoopDetectorPriority, + kNestedLoopDetectorIntervalMs, + PluginModuleChild::DetectNestedEventLoop, + this, + NULL); + +#ifdef DEBUG + mTopLoopDepth = g_main_depth(); +#endif +} + +void +PluginModuleChild::ExitedCxxStack() +{ + NS_ABORT_IF_FALSE(0 < mNestedLoopTimerId, + "nested loop timeout not scheduled"); + + g_source_remove(mNestedLoopTimerId); + mNestedLoopTimerId = 0; +} + +#endif + +bool +PluginModuleChild::InitGraphics() +{ +#if defined(MOZ_WIDGET_GTK2) + // Work around plugins that don't interact well with GDK + // client-side windows. + PR_SetEnv("GDK_NATIVE_WINDOWS=1"); + + gtk_init(0, 0); + + // GtkPlug is a static class so will leak anyway but this ref makes sure. + gpointer gtk_plug_class = g_type_class_ref(GTK_TYPE_PLUG); + + // The dispose method is a good place to hook into the destruction process + // because the reference count should be 1 the last time dispose is + // called. (Toggle references wouldn't detect if the reference count + // might be higher.) + GObjectDisposeFn* dispose = &G_OBJECT_CLASS(gtk_plug_class)->dispose; + NS_ABORT_IF_FALSE(*dispose != wrap_gtk_plug_dispose, + "InitGraphics called twice"); + real_gtk_plug_dispose = *dispose; + *dispose = wrap_gtk_plug_dispose; + + // If we ever stop setting GDK_NATIVE_WINDOWS, we'll also need to + // gtk_widget_add_events GDK_SCROLL_MASK or GDK client-side windows will + // not tell us about the scroll events that it intercepts. With native + // windows, this is called when GDK intercepts the events; if GDK doesn't + // intercept the events, then the X server will instead send them directly + // to an ancestor (embedder) window. + GtkWidgetScrollEventFn* scroll_event = + >K_WIDGET_CLASS(gtk_plug_class)->scroll_event; + if (!*scroll_event) { + *scroll_event = gtk_plug_scroll_event; + } + + if (gtk_check_version(2,18,7) != NULL) { // older + GtkPlugEmbeddedFn* embedded = >K_PLUG_CLASS(gtk_plug_class)->embedded; + real_gtk_plug_embedded = *embedded; + *embedded = wrap_gtk_plug_embedded; + } + +#elif defined(MOZ_WIDGET_QT) + if (!qApp) + gQApp = new QApplication(0, NULL); +#else + // may not be necessary on all platforms +#endif +#ifdef MOZ_X11 + // Do this after initializing GDK, or GDK will install its own handler. + XRE_InstallX11ErrorHandler(); +#endif + + return true; +} + +bool +PluginModuleChild::AnswerNP_Shutdown(NPError *rv) +{ + AssertPluginThread(); + + // the PluginModuleParent shuts down this process after this RPC + // call pops off its stack + + *rv = mShutdownFunc ? mShutdownFunc() : NPERR_NO_ERROR; + + // weakly guard against re-entry after NP_Shutdown + memset(&mFunctions, 0, sizeof(mFunctions)); + + return true; +} + +void +PluginModuleChild::ActorDestroy(ActorDestroyReason why) +{ + // doesn't matter why we're being destroyed; it's up to us to + // initiate (clean) shutdown + XRE_ShutdownChildProcess(); +} + +void +PluginModuleChild::CleanUp() +{ +} + +const char* +PluginModuleChild::GetUserAgent() +{ + if (!CallNPN_UserAgent(&mUserAgent)) + return NULL; + + return NullableStringGet(mUserAgent); +} + +bool +PluginModuleChild::RegisterActorForNPObject(NPObject* aObject, + PluginScriptableObjectChild* aActor) +{ + AssertPluginThread(); + NS_ASSERTION(mObjectMap.IsInitialized(), "Not initialized!"); + NS_ASSERTION(aObject && aActor, "Null pointer!"); + + NPObjectData* d = mObjectMap.GetEntry(aObject); + if (!d) { + NS_ERROR("NPObject not in object table"); + return false; + } + + d->actor = aActor; + return true; +} + +void +PluginModuleChild::UnregisterActorForNPObject(NPObject* aObject) +{ + AssertPluginThread(); + NS_ASSERTION(mObjectMap.IsInitialized(), "Not initialized!"); + NS_ASSERTION(aObject, "Null pointer!"); + + mObjectMap.GetEntry(aObject)->actor = NULL; +} + +PluginScriptableObjectChild* +PluginModuleChild::GetActorForNPObject(NPObject* aObject) +{ + AssertPluginThread(); + NS_ASSERTION(mObjectMap.IsInitialized(), "Not initialized!"); + NS_ASSERTION(aObject, "Null pointer!"); + + NPObjectData* d = mObjectMap.GetEntry(aObject); + if (!d) { + NS_ERROR("Plugin using object not created with NPN_CreateObject?"); + return NULL; + } + + return d->actor; +} + +#ifdef DEBUG +bool +PluginModuleChild::NPObjectIsRegistered(NPObject* aObject) +{ + return !!mObjectMap.GetEntry(aObject); +} +#endif + +//----------------------------------------------------------------------------- +// FIXME/cjones: just getting this out of the way for the moment ... + +namespace mozilla { +namespace plugins { +namespace child { + +static NPError NP_CALLBACK +_requestread(NPStream *pstream, NPByteRange *rangeList); + +static NPError NP_CALLBACK +_geturlnotify(NPP aNPP, const char* relativeURL, const char* target, + void* notifyData); + +static NPError NP_CALLBACK +_getvalue(NPP aNPP, NPNVariable variable, void *r_value); + +static NPError NP_CALLBACK +_setvalue(NPP aNPP, NPPVariable variable, void *r_value); + +static NPError NP_CALLBACK +_geturl(NPP aNPP, const char* relativeURL, const char* target); + +static NPError NP_CALLBACK +_posturlnotify(NPP aNPP, const char* relativeURL, const char *target, + uint32_t len, const char *buf, NPBool file, void* notifyData); + +static NPError NP_CALLBACK +_posturl(NPP aNPP, const char* relativeURL, const char *target, uint32_t len, + const char *buf, NPBool file); + +static NPError NP_CALLBACK +_newstream(NPP aNPP, NPMIMEType type, const char* window, NPStream** pstream); + +static int32_t NP_CALLBACK +_write(NPP aNPP, NPStream *pstream, int32_t len, void *buffer); + +static NPError NP_CALLBACK +_destroystream(NPP aNPP, NPStream *pstream, NPError reason); + +static void NP_CALLBACK +_status(NPP aNPP, const char *message); + +static void NP_CALLBACK +_memfree (void *ptr); + +static uint32_t NP_CALLBACK +_memflush(uint32_t size); + +static void NP_CALLBACK +_reloadplugins(NPBool reloadPages); + +static void NP_CALLBACK +_invalidaterect(NPP aNPP, NPRect *invalidRect); + +static void NP_CALLBACK +_invalidateregion(NPP aNPP, NPRegion invalidRegion); + +static void NP_CALLBACK +_forceredraw(NPP aNPP); + +static const char* NP_CALLBACK +_useragent(NPP aNPP); + +static void* NP_CALLBACK +_memalloc (uint32_t size); + +// Deprecated entry points for the old Java plugin. +static void* NP_CALLBACK /* OJI type: JRIEnv* */ +_getjavaenv(void); + +// Deprecated entry points for the old Java plugin. +static void* NP_CALLBACK /* OJI type: jref */ +_getjavapeer(NPP aNPP); + +static bool NP_CALLBACK +_invoke(NPP aNPP, NPObject* npobj, NPIdentifier method, const NPVariant *args, + uint32_t argCount, NPVariant *result); + +static bool NP_CALLBACK +_invokedefault(NPP aNPP, NPObject* npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result); + +static bool NP_CALLBACK +_evaluate(NPP aNPP, NPObject* npobj, NPString *script, NPVariant *result); + +static bool NP_CALLBACK +_getproperty(NPP aNPP, NPObject* npobj, NPIdentifier property, + NPVariant *result); + +static bool NP_CALLBACK +_setproperty(NPP aNPP, NPObject* npobj, NPIdentifier property, + const NPVariant *value); + +static bool NP_CALLBACK +_removeproperty(NPP aNPP, NPObject* npobj, NPIdentifier property); + +static bool NP_CALLBACK +_hasproperty(NPP aNPP, NPObject* npobj, NPIdentifier propertyName); + +static bool NP_CALLBACK +_hasmethod(NPP aNPP, NPObject* npobj, NPIdentifier methodName); + +static bool NP_CALLBACK +_enumerate(NPP aNPP, NPObject *npobj, NPIdentifier **identifier, + uint32_t *count); + +static bool NP_CALLBACK +_construct(NPP aNPP, NPObject* npobj, const NPVariant *args, + uint32_t argCount, NPVariant *result); + +static void NP_CALLBACK +_releasevariantvalue(NPVariant *variant); + +static void NP_CALLBACK +_setexception(NPObject* npobj, const NPUTF8 *message); + +static bool NP_CALLBACK +_pushpopupsenabledstate(NPP aNPP, NPBool enabled); + +static bool NP_CALLBACK +_poppopupsenabledstate(NPP aNPP); + +static void NP_CALLBACK +_pluginthreadasynccall(NPP instance, PluginThreadCallback func, + void *userData); + +static NPError NP_CALLBACK +_getvalueforurl(NPP npp, NPNURLVariable variable, const char *url, + char **value, uint32_t *len); + +static NPError NP_CALLBACK +_setvalueforurl(NPP npp, NPNURLVariable variable, const char *url, + const char *value, uint32_t len); + +static NPError NP_CALLBACK +_getauthenticationinfo(NPP npp, const char *protocol, + const char *host, int32_t port, + const char *scheme, const char *realm, + char **username, uint32_t *ulen, + char **password, uint32_t *plen); + +} /* namespace child */ +} /* namespace plugins */ +} /* namespace mozilla */ + +const NPNetscapeFuncs PluginModuleChild::sBrowserFuncs = { + sizeof(sBrowserFuncs), + (NP_VERSION_MAJOR << 8) + NP_VERSION_MINOR, + mozilla::plugins::child::_geturl, + mozilla::plugins::child::_posturl, + mozilla::plugins::child::_requestread, + mozilla::plugins::child::_newstream, + mozilla::plugins::child::_write, + mozilla::plugins::child::_destroystream, + mozilla::plugins::child::_status, + mozilla::plugins::child::_useragent, + mozilla::plugins::child::_memalloc, + mozilla::plugins::child::_memfree, + mozilla::plugins::child::_memflush, + mozilla::plugins::child::_reloadplugins, + mozilla::plugins::child::_getjavaenv, + mozilla::plugins::child::_getjavapeer, + mozilla::plugins::child::_geturlnotify, + mozilla::plugins::child::_posturlnotify, + mozilla::plugins::child::_getvalue, + mozilla::plugins::child::_setvalue, + mozilla::plugins::child::_invalidaterect, + mozilla::plugins::child::_invalidateregion, + mozilla::plugins::child::_forceredraw, + PluginModuleChild::NPN_GetStringIdentifier, + PluginModuleChild::NPN_GetStringIdentifiers, + PluginModuleChild::NPN_GetIntIdentifier, + PluginModuleChild::NPN_IdentifierIsString, + PluginModuleChild::NPN_UTF8FromIdentifier, + PluginModuleChild::NPN_IntFromIdentifier, + PluginModuleChild::NPN_CreateObject, + PluginModuleChild::NPN_RetainObject, + PluginModuleChild::NPN_ReleaseObject, + mozilla::plugins::child::_invoke, + mozilla::plugins::child::_invokedefault, + mozilla::plugins::child::_evaluate, + mozilla::plugins::child::_getproperty, + mozilla::plugins::child::_setproperty, + mozilla::plugins::child::_removeproperty, + mozilla::plugins::child::_hasproperty, + mozilla::plugins::child::_hasmethod, + mozilla::plugins::child::_releasevariantvalue, + mozilla::plugins::child::_setexception, + mozilla::plugins::child::_pushpopupsenabledstate, + mozilla::plugins::child::_poppopupsenabledstate, + mozilla::plugins::child::_enumerate, + mozilla::plugins::child::_pluginthreadasynccall, + mozilla::plugins::child::_construct, + mozilla::plugins::child::_getvalueforurl, + mozilla::plugins::child::_setvalueforurl, + mozilla::plugins::child::_getauthenticationinfo +}; + +PluginInstanceChild* +InstCast(NPP aNPP) +{ + NS_ABORT_IF_FALSE(!!(aNPP->ndata), "nil instance"); + return static_cast(aNPP->ndata); +} + +namespace mozilla { +namespace plugins { +namespace child { + +NPError NP_CALLBACK +_requestread(NPStream* aStream, + NPByteRange* aRangeList) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM); + + BrowserStreamChild* bs = + static_cast(static_cast(aStream->ndata)); + bs->EnsureCorrectStream(aStream); + return bs->NPN_RequestRead(aRangeList); +} + +NPError NP_CALLBACK +_geturlnotify(NPP aNPP, + const char* aRelativeURL, + const char* aTarget, + void* aNotifyData) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM); + + nsCString url = NullableString(aRelativeURL); + StreamNotifyChild* sn = new StreamNotifyChild(url); + + NPError err; + InstCast(aNPP)->CallPStreamNotifyConstructor( + sn, url, NullableString(aTarget), false, nsCString(), false, &err); + + if (NPERR_NO_ERROR == err) { + // If NPN_PostURLNotify fails, the parent will immediately send us + // a PStreamNotifyDestructor, which should not call NPP_URLNotify. + sn->SetValid(aNotifyData); + } + + return err; +} + +NPError NP_CALLBACK +_getvalue(NPP aNPP, + NPNVariable aVariable, + void* aValue) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM); + + switch (aVariable) { + // Copied from nsNPAPIPlugin.cpp + case NPNVToolkit: +#ifdef MOZ_WIDGET_GTK2 + *static_cast(aValue) = NPNVGtk2; + return NPERR_NO_ERROR; +#endif + return NPERR_GENERIC_ERROR; + + case NPNVjavascriptEnabledBool: // Intentional fall-through + case NPNVasdEnabledBool: // Intentional fall-through + case NPNVisOfflineBool: // Intentional fall-through + case NPNVSupportsXEmbedBool: // Intentional fall-through + case NPNVSupportsWindowless: // Intentional fall-through + case NPNVprivateModeBool: { + NPError result; + bool value; + PluginModuleChild::current()-> + CallNPN_GetValue_WithBoolReturn(aVariable, &result, &value); + *(NPBool*)aValue = value ? true : false; + return result; + } + + default: { + if (aNPP) { + return InstCast(aNPP)->NPN_GetValue(aVariable, aValue); + } + + NS_WARNING("Null NPP!"); + return NPERR_INVALID_INSTANCE_ERROR; + } + } + + NS_NOTREACHED("Shouldn't get here!"); + return NPERR_GENERIC_ERROR; +} + +NPError NP_CALLBACK +_setvalue(NPP aNPP, + NPPVariable aVariable, + void* aValue) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM); + return InstCast(aNPP)->NPN_SetValue(aVariable, aValue); +} + +NPError NP_CALLBACK +_geturl(NPP aNPP, + const char* aRelativeURL, + const char* aTarget) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM); + + NPError err; + InstCast(aNPP)->CallNPN_GetURL(NullableString(aRelativeURL), + NullableString(aTarget), &err); + return err; +} + +NPError NP_CALLBACK +_posturlnotify(NPP aNPP, + const char* aRelativeURL, + const char* aTarget, + uint32_t aLength, + const char* aBuffer, + NPBool aIsFile, + void* aNotifyData) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM); + + if (!aBuffer) + return NPERR_INVALID_PARAM; + + nsCString url = NullableString(aRelativeURL); + StreamNotifyChild* sn = new StreamNotifyChild(url); + + NPError err; + InstCast(aNPP)->CallPStreamNotifyConstructor( + sn, url, NullableString(aTarget), true, + nsCString(aBuffer, aLength), aIsFile, &err); + + if (NPERR_NO_ERROR == err) { + // If NPN_PostURLNotify fails, the parent will immediately send us + // a PStreamNotifyDestructor, which should not call NPP_URLNotify. + sn->SetValid(aNotifyData); + } + + return err; +} + +NPError NP_CALLBACK +_posturl(NPP aNPP, + const char* aRelativeURL, + const char* aTarget, + uint32_t aLength, + const char* aBuffer, + NPBool aIsFile) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM); + + NPError err; + // FIXME what should happen when |aBuffer| is null? + InstCast(aNPP)->CallNPN_PostURL(NullableString(aRelativeURL), + NullableString(aTarget), + nsDependentCString(aBuffer, aLength), + aIsFile, &err); + return err; +} + +NPError NP_CALLBACK +_newstream(NPP aNPP, + NPMIMEType aMIMEType, + const char* aWindow, + NPStream** aStream) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM); + return InstCast(aNPP)->NPN_NewStream(aMIMEType, aWindow, aStream); +} + +int32_t NP_CALLBACK +_write(NPP aNPP, + NPStream* aStream, + int32_t aLength, + void* aBuffer) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(0); + + PluginStreamChild* ps = + static_cast(static_cast(aStream->ndata)); + ps->EnsureCorrectInstance(InstCast(aNPP)); + ps->EnsureCorrectStream(aStream); + return ps->NPN_Write(aLength, aBuffer); +} + +NPError NP_CALLBACK +_destroystream(NPP aNPP, + NPStream* aStream, + NPError aReason) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(NPERR_INVALID_PARAM); + + PluginInstanceChild* p = InstCast(aNPP); + AStream* s = static_cast(aStream->ndata); + if (s->IsBrowserStream()) { + BrowserStreamChild* bs = static_cast(s); + bs->EnsureCorrectInstance(p); + bs->NPN_DestroyStream(aReason); + } + else { + PluginStreamChild* ps = static_cast(s); + ps->EnsureCorrectInstance(p); + PPluginStreamChild::Call__delete__(ps, aReason, false); + } + return NPERR_NO_ERROR; +} + +void NP_CALLBACK +_status(NPP aNPP, + const char* aMessage) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD_VOID(); + NS_WARNING("Not yet implemented!"); +} + +void NP_CALLBACK +_memfree(void* aPtr) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + // Only assert plugin thread here for consistency with in-process plugins. + AssertPluginThread(); + NS_Free(aPtr); +} + +uint32_t NP_CALLBACK +_memflush(uint32_t aSize) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + // Only assert plugin thread here for consistency with in-process plugins. + AssertPluginThread(); + return 0; +} + +void NP_CALLBACK +_reloadplugins(NPBool aReloadPages) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD_VOID(); + NS_WARNING("Not yet implemented!"); +} + +void NP_CALLBACK +_invalidaterect(NPP aNPP, + NPRect* aInvalidRect) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD_VOID(); + // NULL check for nspluginwrapper (bug 548434) + if (aNPP) { + InstCast(aNPP)->InvalidateRect(aInvalidRect); + } +} + +void NP_CALLBACK +_invalidateregion(NPP aNPP, + NPRegion aInvalidRegion) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD_VOID(); + NS_WARNING("Not yet implemented!"); +} + +void NP_CALLBACK +_forceredraw(NPP aNPP) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD_VOID(); + NS_WARNING("Not yet implemented!"); +} + +const char* NP_CALLBACK +_useragent(NPP aNPP) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(nsnull); + return PluginModuleChild::current()->GetUserAgent(); +} + +void* NP_CALLBACK +_memalloc(uint32_t aSize) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + // Only assert plugin thread here for consistency with in-process plugins. + AssertPluginThread(); + return NS_Alloc(aSize); +} + +// Deprecated entry points for the old Java plugin. +void* NP_CALLBACK /* OJI type: JRIEnv* */ +_getjavaenv(void) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + return 0; +} + +void* NP_CALLBACK /* OJI type: jref */ +_getjavapeer(NPP aNPP) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + return 0; +} + +bool NP_CALLBACK +_invoke(NPP aNPP, + NPObject* aNPObj, + NPIdentifier aMethod, + const NPVariant* aArgs, + uint32_t aArgCount, + NPVariant* aResult) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(false); + + if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->invoke) + return false; + + return aNPObj->_class->invoke(aNPObj, aMethod, aArgs, aArgCount, aResult); +} + +bool NP_CALLBACK +_invokedefault(NPP aNPP, + NPObject* aNPObj, + const NPVariant* aArgs, + uint32_t aArgCount, + NPVariant* aResult) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(false); + + if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->invokeDefault) + return false; + + return aNPObj->_class->invokeDefault(aNPObj, aArgs, aArgCount, aResult); +} + +bool NP_CALLBACK +_evaluate(NPP aNPP, + NPObject* aObject, + NPString* aScript, + NPVariant* aResult) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(false); + + if (!(aNPP && aObject && aScript && aResult)) { + NS_ERROR("Bad arguments!"); + return false; + } + + PluginScriptableObjectChild* actor = + InstCast(aNPP)->GetActorForNPObject(aObject); + if (!actor) { + NS_ERROR("Failed to create actor?!"); + return false; + } + + return actor->Evaluate(aScript, aResult); +} + +bool NP_CALLBACK +_getproperty(NPP aNPP, + NPObject* aNPObj, + NPIdentifier aPropertyName, + NPVariant* aResult) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(false); + + if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->getProperty) + return false; + + return aNPObj->_class->getProperty(aNPObj, aPropertyName, aResult); +} + +bool NP_CALLBACK +_setproperty(NPP aNPP, + NPObject* aNPObj, + NPIdentifier aPropertyName, + const NPVariant* aValue) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(false); + + if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->setProperty) + return false; + + return aNPObj->_class->setProperty(aNPObj, aPropertyName, aValue); +} + +bool NP_CALLBACK +_removeproperty(NPP aNPP, + NPObject* aNPObj, + NPIdentifier aPropertyName) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(false); + + if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->removeProperty) + return false; + + return aNPObj->_class->removeProperty(aNPObj, aPropertyName); +} + +bool NP_CALLBACK +_hasproperty(NPP aNPP, + NPObject* aNPObj, + NPIdentifier aPropertyName) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(false); + + if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->hasProperty) + return false; + + return aNPObj->_class->hasProperty(aNPObj, aPropertyName); +} + +bool NP_CALLBACK +_hasmethod(NPP aNPP, + NPObject* aNPObj, + NPIdentifier aMethodName) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(false); + + if (!aNPP || !aNPObj || !aNPObj->_class || !aNPObj->_class->hasMethod) + return false; + + return aNPObj->_class->hasMethod(aNPObj, aMethodName); +} + +bool NP_CALLBACK +_enumerate(NPP aNPP, + NPObject* aNPObj, + NPIdentifier** aIdentifiers, + uint32_t* aCount) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(false); + + if (!aNPP || !aNPObj || !aNPObj->_class) + return false; + + if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(aNPObj->_class) || + !aNPObj->_class->enumerate) { + *aIdentifiers = 0; + *aCount = 0; + return true; + } + + return aNPObj->_class->enumerate(aNPObj, aIdentifiers, aCount); +} + +bool NP_CALLBACK +_construct(NPP aNPP, + NPObject* aNPObj, + const NPVariant* aArgs, + uint32_t aArgCount, + NPVariant* aResult) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(false); + + if (!aNPP || !aNPObj || !aNPObj->_class || + !NP_CLASS_STRUCT_VERSION_HAS_CTOR(aNPObj->_class) || + !aNPObj->_class->construct) { + return false; + } + + return aNPObj->_class->construct(aNPObj, aArgs, aArgCount, aResult); +} + +void NP_CALLBACK +_releasevariantvalue(NPVariant* aVariant) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + // Only assert plugin thread here for consistency with in-process plugins. + AssertPluginThread(); + + if (NPVARIANT_IS_STRING(*aVariant)) { + NPString str = NPVARIANT_TO_STRING(*aVariant); + free(const_cast(str.UTF8Characters)); + } + else if (NPVARIANT_IS_OBJECT(*aVariant)) { + NPObject* object = NPVARIANT_TO_OBJECT(*aVariant); + if (object) { + PluginModuleChild::NPN_ReleaseObject(object); + } + } + VOID_TO_NPVARIANT(*aVariant); +} + +void NP_CALLBACK +_setexception(NPObject* aNPObj, + const NPUTF8* aMessage) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD_VOID(); + NS_WARNING("Not yet implemented!"); +} + +bool NP_CALLBACK +_pushpopupsenabledstate(NPP aNPP, + NPBool aEnabled) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + // XXXbent This is incorrect, followup to make this return void! + ENSURE_PLUGIN_THREAD(false); + + bool retval; + if (InstCast(aNPP)->CallNPN_PushPopupsEnabledState(aEnabled ? true : false, + &retval)) { + return retval; + } + return false; +} + +bool NP_CALLBACK +_poppopupsenabledstate(NPP aNPP) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + // XXXbent This is incorrect, followup to make this return void! + ENSURE_PLUGIN_THREAD(false); + + bool retval; + if (InstCast(aNPP)->CallNPN_PopPopupsEnabledState(&retval)) { + return retval; + } + return false; +} + +void NP_CALLBACK +_pluginthreadasynccall(NPP aNPP, + PluginThreadCallback aFunc, + void* aUserData) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + if (!aFunc) + return; + + PluginThreadChild::current()->message_loop() + ->PostTask(FROM_HERE, new ChildAsyncCall(InstCast(aNPP), aFunc, + aUserData)); +} + +NPError NP_CALLBACK +_getvalueforurl(NPP npp, NPNURLVariable variable, const char *url, + char **value, uint32_t *len) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + AssertPluginThread(); + + if (!url) + return NPERR_INVALID_URL; + + if (!npp || !value || !len) + return NPERR_INVALID_PARAM; + + switch (variable) { + case NPNURLVCookie: + case NPNURLVProxy: + nsCString v; + NPError result; + InstCast(npp)-> + CallNPN_GetValueForURL(variable, nsCString(url), &v, &result); + if (NPERR_NO_ERROR == result) { + *value = ToNewCString(v); + *len = v.Length(); + } + return result; + } + + return NPERR_INVALID_PARAM; +} + +NPError NP_CALLBACK +_setvalueforurl(NPP npp, NPNURLVariable variable, const char *url, + const char *value, uint32_t len) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + AssertPluginThread(); + + if (!value) + return NPERR_INVALID_PARAM; + + if (!url) + return NPERR_INVALID_URL; + + switch (variable) { + case NPNURLVCookie: + case NPNURLVProxy: + NPError result; + InstCast(npp)->CallNPN_SetValueForURL(variable, nsCString(url), + nsDependentCString(value, len), + &result); + return result; + } + + return NPERR_INVALID_PARAM; +} + +NPError NP_CALLBACK +_getauthenticationinfo(NPP npp, const char *protocol, + const char *host, int32_t port, + const char *scheme, const char *realm, + char **username, uint32_t *ulen, + char **password, uint32_t *plen) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + AssertPluginThread(); + + if (!protocol || !host || !scheme || !realm || !username || !ulen || + !password || !plen) + return NPERR_INVALID_PARAM; + + nsCString u; + nsCString p; + NPError result; + InstCast(npp)-> + CallNPN_GetAuthenticationInfo(nsDependentCString(protocol), + nsDependentCString(host), + port, + nsDependentCString(scheme), + nsDependentCString(realm), + &u, &p, &result); + if (NPERR_NO_ERROR == result) { + *username = ToNewCString(u); + *ulen = u.Length(); + *password = ToNewCString(p); + *plen = p.Length(); + } + return result; +} + +} /* namespace child */ +} /* namespace plugins */ +} /* namespace mozilla */ + +//----------------------------------------------------------------------------- + +bool +PluginModuleChild::AnswerNP_Initialize(NativeThreadId* tid, NPError* _retval) +{ + PLUGIN_LOG_DEBUG_METHOD; + AssertPluginThread(); + +#ifdef MOZ_CRASHREPORTER + *tid = CrashReporter::CurrentThreadId(); +#else + *tid = 0; +#endif + +#if defined(OS_LINUX) + *_retval = mInitializeFunc(&sBrowserFuncs, &mFunctions); + return true; + +#elif defined(OS_WIN) + nsresult rv = mGetEntryPointsFunc(&mFunctions); + if (NS_FAILED(rv)) { + return false; + } + + NS_ASSERTION(HIBYTE(mFunctions.version) >= NP_VERSION_MAJOR, + "callback version is less than NP version"); + + *_retval = mInitializeFunc(&sBrowserFuncs); + return true; +#elif defined(OS_MACOSX) +# warning IMPLEMENT ME + return false; + +#else +# error Please implement me for your platform +#endif +} + +PPluginIdentifierChild* +PluginModuleChild::AllocPPluginIdentifier(const nsCString& aString, + const int32_t& aInt) +{ + // There's a possibility that we already have an actor that wraps the same + // string or int because we do all this identifier construction + // asynchronously. Check to see if we've already wrapped here, and then set + // canonical actor of the new one to the actor already in our hash. + PluginIdentifierChild* newActor; + PluginIdentifierChild* existingActor; + + if (aString.IsVoid()) { + newActor = new PluginIdentifierChildInt(aInt); + if (mIntIdentifiers.Get(aInt, &existingActor)) { + newActor->SetCanonicalIdentifier(existingActor); + } + } + else { + newActor = new PluginIdentifierChildString(aString); + if (mStringIdentifiers.Get(aString, &existingActor)) { + newActor->SetCanonicalIdentifier(existingActor); + } + } + return newActor; +} + +bool +PluginModuleChild::DeallocPPluginIdentifier(PPluginIdentifierChild* aActor) +{ + delete aActor; + return true; +} + +PPluginInstanceChild* +PluginModuleChild::AllocPPluginInstance(const nsCString& aMimeType, + const uint16_t& aMode, + const nsTArray& aNames, + const nsTArray& aValues, + NPError* rv) +{ + PLUGIN_LOG_DEBUG_METHOD; + AssertPluginThread(); + + nsAutoPtr childInstance( + new PluginInstanceChild(&mFunctions, aMimeType)); + if (!childInstance->Initialize()) { + *rv = NPERR_GENERIC_ERROR; + return 0; + } + return childInstance.forget(); +} + +bool +PluginModuleChild::AnswerPPluginInstanceConstructor(PPluginInstanceChild* aActor, + const nsCString& aMimeType, + const uint16_t& aMode, + const nsTArray& aNames, + const nsTArray& aValues, + NPError* rv) +{ + PLUGIN_LOG_DEBUG_METHOD; + AssertPluginThread(); + + PluginInstanceChild* childInstance = + reinterpret_cast(aActor); + NS_ASSERTION(childInstance, "Null actor!"); + + // unpack the arguments into a C format + int argc = aNames.Length(); + NS_ASSERTION(argc == (int) aValues.Length(), + "argn.length != argv.length"); + + nsAutoArrayPtr argn(new char*[1 + argc]); + nsAutoArrayPtr argv(new char*[1 + argc]); + argn[argc] = 0; + argv[argc] = 0; + + for (int i = 0; i < argc; ++i) { + argn[i] = const_cast(NullableStringGet(aNames[i])); + argv[i] = const_cast(NullableStringGet(aValues[i])); + } + + NPP npp = childInstance->GetNPP(); + + // FIXME/cjones: use SAFE_CALL stuff + *rv = mFunctions.newp((char*)NullableStringGet(aMimeType), + npp, + aMode, + argc, + argn, + argv, + 0); + if (NPERR_NO_ERROR != *rv) { + return false; + } + + return true; +} + +bool +PluginModuleChild::DeallocPPluginInstance(PPluginInstanceChild* aActor) +{ + PLUGIN_LOG_DEBUG_METHOD; + AssertPluginThread(); + + delete aActor; + + return true; +} + +NPObject* NP_CALLBACK +PluginModuleChild::NPN_CreateObject(NPP aNPP, NPClass* aClass) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + ENSURE_PLUGIN_THREAD(nsnull); + + PluginInstanceChild* i = InstCast(aNPP); + if (i->mDeletingHash) { + NS_ERROR("Plugin used NPP after NPP_Destroy"); + return NULL; + } + + NPObject* newObject; + if (aClass && aClass->allocate) { + newObject = aClass->allocate(aNPP, aClass); + } + else { + newObject = reinterpret_cast(child::_memalloc(sizeof(NPObject))); + } + + if (newObject) { + newObject->_class = aClass; + newObject->referenceCount = 1; + NS_LOG_ADDREF(newObject, 1, "NPObject", sizeof(NPObject)); + } + + NPObjectData* d = static_cast(i->Manager()) + ->mObjectMap.PutEntry(newObject); + NS_ASSERTION(!d->instance, "New NPObject already mapped?"); + d->instance = i; + + return newObject; +} + +NPObject* NP_CALLBACK +PluginModuleChild::NPN_RetainObject(NPObject* aNPObj) +{ + AssertPluginThread(); + + int32_t refCnt = PR_AtomicIncrement((PRInt32*)&aNPObj->referenceCount); + NS_LOG_ADDREF(aNPObj, refCnt, "NPObject", sizeof(NPObject)); + + return aNPObj; +} + +void NP_CALLBACK +PluginModuleChild::NPN_ReleaseObject(NPObject* aNPObj) +{ + AssertPluginThread(); + + NPObjectData* d = current()->mObjectMap.GetEntry(aNPObj); + if (!d) { + NS_ERROR("Releasing object not in mObjectMap?"); + return; + } + + DeletingObjectEntry* doe = NULL; + if (d->instance->mDeletingHash) { + doe = d->instance->mDeletingHash->GetEntry(aNPObj); + if (!doe) { + NS_ERROR("An object for a destroyed instance isn't in the instance deletion hash"); + return; + } + if (doe->mDeleted) + return; + } + + int32_t refCnt = PR_AtomicDecrement((PRInt32*)&aNPObj->referenceCount); + NS_LOG_RELEASE(aNPObj, refCnt, "NPObject"); + + if (refCnt == 0) { + DeallocNPObject(aNPObj); + if (doe) + doe->mDeleted = true; + } + return; +} + +void +PluginModuleChild::DeallocNPObject(NPObject* aNPObj) +{ + if (aNPObj->_class && aNPObj->_class->deallocate) { + aNPObj->_class->deallocate(aNPObj); + } else { + child::_memfree(aNPObj); + } + + NPObjectData* d = current()->mObjectMap.GetEntry(aNPObj); + if (d->actor) + d->actor->NPObjectDestroyed(); + + current()->mObjectMap.RemoveEntry(aNPObj); +} + +void +PluginModuleChild::FindNPObjectsForInstance(PluginInstanceChild* instance) +{ + NS_ASSERTION(instance->mDeletingHash, "filling null mDeletingHash?"); + mObjectMap.EnumerateEntries(CollectForInstance, instance); +} + +PLDHashOperator +PluginModuleChild::CollectForInstance(NPObjectData* d, void* userArg) +{ + PluginInstanceChild* instance = static_cast(userArg); + if (d->instance == instance) { + NPObject* o = d->GetKey(); + instance->mDeletingHash->PutEntry(o); + } + return PL_DHASH_NEXT; +} + +NPIdentifier NP_CALLBACK +PluginModuleChild::NPN_GetStringIdentifier(const NPUTF8* aName) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + AssertPluginThread(); + + if (!aName) + return 0; + + PluginModuleChild* self = PluginModuleChild::current(); + nsDependentCString name(aName); + + PluginIdentifierChild* ident; + if (!self->mStringIdentifiers.Get(name, &ident)) { + nsCString nameCopy(name); + + ident = new PluginIdentifierChildString(nameCopy); + self->SendPPluginIdentifierConstructor(ident, nameCopy, -1); + self->mStringIdentifiers.Put(nameCopy, ident); + } + + return ident; +} + +void NP_CALLBACK +PluginModuleChild::NPN_GetStringIdentifiers(const NPUTF8** aNames, + int32_t aNameCount, + NPIdentifier* aIdentifiers) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + AssertPluginThread(); + + if (!(aNames && aNameCount > 0 && aIdentifiers)) { + NS_RUNTIMEABORT("Bad input! Headed for a crash!"); + } + + PluginModuleChild* self = PluginModuleChild::current(); + + for (int32_t index = 0; index < aNameCount; ++index) { + if (!aNames[index]) { + aIdentifiers[index] = 0; + continue; + } + nsDependentCString name(aNames[index]); + PluginIdentifierChild* ident; + if (!self->mStringIdentifiers.Get(name, &ident)) { + nsCString nameCopy(name); + + ident = new PluginIdentifierChildString(nameCopy); + self->SendPPluginIdentifierConstructor(ident, nameCopy, -1); + self->mStringIdentifiers.Put(nameCopy, ident); + } + aIdentifiers[index] = ident; + } +} + +bool NP_CALLBACK +PluginModuleChild::NPN_IdentifierIsString(NPIdentifier aIdentifier) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + + PluginIdentifierChild* ident = + static_cast(aIdentifier); + return ident->IsString(); +} + +NPIdentifier NP_CALLBACK +PluginModuleChild::NPN_GetIntIdentifier(int32_t aIntId) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + AssertPluginThread(); + + PluginModuleChild* self = PluginModuleChild::current(); + + PluginIdentifierChild* ident; + if (!self->mIntIdentifiers.Get(aIntId, &ident)) { + nsCString voidString; + voidString.SetIsVoid(PR_TRUE); + + ident = new PluginIdentifierChildInt(aIntId); + self->SendPPluginIdentifierConstructor(ident, voidString, aIntId); + self->mIntIdentifiers.Put(aIntId, ident); + } + return ident; +} + +NPUTF8* NP_CALLBACK +PluginModuleChild::NPN_UTF8FromIdentifier(NPIdentifier aIdentifier) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + + if (static_cast(aIdentifier)->IsString()) { + return static_cast(aIdentifier)->ToString(); + } + return nsnull; +} + +int32_t NP_CALLBACK +PluginModuleChild::NPN_IntFromIdentifier(NPIdentifier aIdentifier) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + + if (static_cast(aIdentifier)->IsString()) { + return static_cast(aIdentifier)->ToInt(); + } + return PR_INT32_MIN; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginModuleChild.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginModuleChild.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginModuleChild.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginModuleChild.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,306 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Ben Turner . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef dom_plugins_PluginModuleChild_h +#define dom_plugins_PluginModuleChild_h 1 + +#include +#include + +#include "base/basictypes.h" + +#include "prlink.h" + +#include "npapi.h" +#include "npfunctions.h" + +#include "nsAutoPtr.h" +#include "nsDataHashtable.h" +#include "nsTHashtable.h" +#include "nsHashKeys.h" + +#include "mozilla/plugins/PPluginModuleChild.h" +#include "mozilla/plugins/PluginInstanceChild.h" +#include "mozilla/plugins/PluginIdentifierChild.h" + +// NOTE: stolen from nsNPAPIPlugin.h + +/* + * Use this macro before each exported function + * (between the return address and the function + * itself), to ensure that the function has the + * right calling conventions on OS/2. + */ +#ifdef XP_OS2 +#define NP_CALLBACK _System +#else +#define NP_CALLBACK +#endif + +#if defined(XP_WIN) +#define NS_NPAPIPLUGIN_CALLBACK(_type, _name) _type (__stdcall * _name) +#elif defined(XP_OS2) +#define NS_NPAPIPLUGIN_CALLBACK(_type, _name) _type (_System * _name) +#else +#define NS_NPAPIPLUGIN_CALLBACK(_type, _name) _type (* _name) +#endif + +typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_GETENTRYPOINTS) (NPPluginFuncs* pCallbacks); +typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGININIT) (const NPNetscapeFuncs* pCallbacks); +typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINUNIXINIT) (const NPNetscapeFuncs* pCallbacks, NPPluginFuncs* fCallbacks); +typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINSHUTDOWN) (void); + +namespace mozilla { +namespace plugins { + +class PluginScriptableObjectChild; +class PluginInstanceChild; + +class PluginModuleChild : public PPluginModuleChild +{ +protected: + NS_OVERRIDE + virtual mozilla::ipc::RPCChannel::RacyRPCPolicy + MediateRPCRace(const Message& parent, const Message& child) + { + return MediateRace(parent, child); + } + + // Implement the PPluginModuleChild interface + virtual bool AnswerNP_Initialize(NativeThreadId* tid, NPError* rv); + + virtual PPluginIdentifierChild* + AllocPPluginIdentifier(const nsCString& aString, + const int32_t& aInt); + + virtual bool + DeallocPPluginIdentifier(PPluginIdentifierChild* aActor); + + virtual PPluginInstanceChild* + AllocPPluginInstance(const nsCString& aMimeType, + const uint16_t& aMode, + const nsTArray& aNames, + const nsTArray& aValues, + NPError* rv); + + virtual bool + DeallocPPluginInstance(PPluginInstanceChild* aActor); + + virtual bool + AnswerPPluginInstanceConstructor(PPluginInstanceChild* aActor, + const nsCString& aMimeType, + const uint16_t& aMode, + const nsTArray& aNames, + const nsTArray& aValues, + NPError* rv); + virtual bool + AnswerNP_Shutdown(NPError *rv); + + virtual void + ActorDestroy(ActorDestroyReason why); + +public: + PluginModuleChild(); + virtual ~PluginModuleChild(); + + bool Init(const std::string& aPluginFilename, + base::ProcessHandle aParentProcessHandle, + MessageLoop* aIOLoop, + IPC::Channel* aChannel); + + void CleanUp(); + + const char* GetUserAgent(); + + static const NPNetscapeFuncs sBrowserFuncs; + + static PluginModuleChild* current(); + + bool RegisterActorForNPObject(NPObject* aObject, + PluginScriptableObjectChild* aActor); + + void UnregisterActorForNPObject(NPObject* aObject); + + PluginScriptableObjectChild* GetActorForNPObject(NPObject* aObject); + +#ifdef DEBUG + bool NPObjectIsRegistered(NPObject* aObject); +#endif + + /** + * The child implementation of NPN_CreateObject. + */ + static NPObject* NP_CALLBACK NPN_CreateObject(NPP aNPP, NPClass* aClass); + /** + * The child implementation of NPN_RetainObject. + */ + static NPObject* NP_CALLBACK NPN_RetainObject(NPObject* aNPObj); + /** + * The child implementation of NPN_ReleaseObject. + */ + static void NP_CALLBACK NPN_ReleaseObject(NPObject* aNPObj); + + /** + * The child implementations of NPIdentifier-related functions. + */ + static NPIdentifier NP_CALLBACK NPN_GetStringIdentifier(const NPUTF8* aName); + static void NP_CALLBACK NPN_GetStringIdentifiers(const NPUTF8** aNames, + int32_t aNameCount, + NPIdentifier* aIdentifiers); + static NPIdentifier NP_CALLBACK NPN_GetIntIdentifier(int32_t aIntId); + static bool NP_CALLBACK NPN_IdentifierIsString(NPIdentifier aIdentifier); + static NPUTF8* NP_CALLBACK NPN_UTF8FromIdentifier(NPIdentifier aIdentifier); + static int32_t NP_CALLBACK NPN_IntFromIdentifier(NPIdentifier aIdentifier); + +private: + bool InitGraphics(); +#if defined(MOZ_WIDGET_GTK2) + static gboolean DetectNestedEventLoop(gpointer data); + static gboolean ProcessBrowserEvents(gpointer data); + + NS_OVERRIDE + virtual void EnteredCxxStack(); + NS_OVERRIDE + virtual void ExitedCxxStack(); +#endif + + std::string mPluginFilename; + PRLibrary* mLibrary; + nsCString mUserAgent; + + // we get this from the plugin +#ifdef OS_POSIX + NP_PLUGINUNIXINIT mInitializeFunc; +#elif OS_WIN + NP_PLUGININIT mInitializeFunc; + NP_GETENTRYPOINTS mGetEntryPointsFunc; +#endif + + NP_PLUGINSHUTDOWN mShutdownFunc; + NPPluginFuncs mFunctions; + NPSavedData mSavedData; + +#if defined(MOZ_WIDGET_GTK2) + // If a plugin spins a nested glib event loop in response to a + // synchronous IPC message from the browser, the loop might break + // only after the browser responds to a request sent by the + // plugin. This can happen if a plugin uses gtk's synchronous + // copy/paste, for example. But because the browser is blocked on + // a condvar, it can't respond to the request. This situation + // isn't technically a deadlock, but the symptoms are basically + // the same from the user's perspective. + // + // We take two steps to prevent this + // + // (1) Detect nested event loops spun by the plugin. This is + // done by scheduling a glib timer event in the plugin + // process whenever the browser might block on the plugin. + // If the plugin indeed spins a nested loop, this timer event + // will fire "soon" thereafter. + // + // (2) When a nested loop is detected, deschedule the + // nested-loop-detection timer and in its place, schedule + // another timer that periodically calls back into the + // browser and spins a mini event loop. This mini event loop + // processes a handful of pending native events. + // + // Because only timer (1) or (2) (or neither) may be active at any + // point in time, we use the same member variable + // |mNestedLoopTimerId| to refer to both. + // + // When the browser no longer might be blocked on a plugin's IPC + // response, we deschedule whichever of (1) or (2) is active. + guint mNestedLoopTimerId; +# ifdef DEBUG + // Depth of the stack of calls to g_main_context_dispatch before any + // nested loops are run. This is 1 when IPC calls are dispatched from + // g_main_context_iteration, or 0 when dispatched directly from + // MessagePumpForUI. + int mTopLoopDepth; +# endif +#endif + + struct NPObjectData : public nsPtrHashKey + { + NPObjectData(const NPObject* key) + : nsPtrHashKey(key) + , instance(NULL) + , actor(NULL) + { } + + // never NULL + PluginInstanceChild* instance; + + // sometimes NULL (no actor associated with an NPObject) + PluginScriptableObjectChild* actor; + }; + /** + * mObjectMap contains all the currently active NPObjects (from NPN_CreateObject until the + * final release/dealloc, whether or not an actor is currently associated with the object. + */ + nsTHashtable mObjectMap; + + nsDataHashtable mStringIdentifiers; + nsDataHashtable mIntIdentifiers; + +public: // called by PluginInstanceChild + /** + * Dealloc an NPObject after last-release or when the associated instance + * is destroyed. This function will remove the object from mObjectMap. + */ + static void DeallocNPObject(NPObject* o); + + NPError NPP_Destroy(PluginInstanceChild* instance) { + return mFunctions.destroy(instance->GetNPP(), 0); + } + + /** + * Fill PluginInstanceChild.mDeletingHash with all the remaining NPObjects + * associated with that instance. + */ + void FindNPObjectsForInstance(PluginInstanceChild* instance); + +private: + static PLDHashOperator CollectForInstance(NPObjectData* d, void* userArg); +}; + +} /* namespace plugins */ +} /* namespace mozilla */ + +#endif // ifndef dom_plugins_PluginModuleChild_h diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginModuleParent.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginModuleParent.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginModuleParent.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginModuleParent.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,784 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Chris Jones + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifdef MOZ_WIDGET_GTK2 +#include +#endif + +#include "base/process_util.h" + +#include "mozilla/ipc/SyncChannel.h" +#include "mozilla/plugins/PluginModuleParent.h" +#include "mozilla/plugins/BrowserStreamParent.h" +#include "PluginIdentifierParent.h" + +#include "nsContentUtils.h" +#include "nsCRT.h" +#ifdef MOZ_CRASHREPORTER +#include "nsExceptionHandler.h" +#endif +#include "nsNPAPIPlugin.h" + +using base::KillProcess; + +using mozilla::PluginLibrary; +using mozilla::ipc::SyncChannel; + +using namespace mozilla::plugins; + +static const char kTimeoutPref[] = "dom.ipc.plugins.timeoutSecs"; + +template<> +struct RunnableMethodTraits +{ + typedef mozilla::plugins::PluginModuleParent Class; + static void RetainCallee(Class* obj) { } + static void ReleaseCallee(Class* obj) { } +}; + +// static +PluginLibrary* +PluginModuleParent::LoadModule(const char* aFilePath) +{ + PLUGIN_LOG_DEBUG_FUNCTION; + + // Block on the child process being launched and initialized. + PluginModuleParent* parent = new PluginModuleParent(aFilePath); + parent->mSubprocess->Launch(); + parent->Open(parent->mSubprocess->GetChannel(), + parent->mSubprocess->GetChildProcessHandle()); + + TimeoutChanged(kTimeoutPref, parent); + + return parent; +} + + +PluginModuleParent::PluginModuleParent(const char* aFilePath) + : mSubprocess(new PluginProcessParent(aFilePath)) + , mPluginThread(0) + , mShutdown(false) + , mNPNIface(NULL) + , mPlugin(NULL) + , mProcessStartTime(time(NULL)) + , mTaskFactory(this) +{ + NS_ASSERTION(mSubprocess, "Out of memory!"); + + if (!mIdentifiers.Init()) { + NS_ERROR("Out of memory"); + } + + nsContentUtils::RegisterPrefCallback(kTimeoutPref, TimeoutChanged, this); +} + +PluginModuleParent::~PluginModuleParent() +{ + NS_ASSERTION(OkToCleanup(), "unsafe destruction"); + + if (!mShutdown) { + NS_WARNING("Plugin host deleted the module without shutting down."); + NPError err; + NP_Shutdown(&err); + } + NS_ASSERTION(mShutdown, "NP_Shutdown didn't"); + + if (mSubprocess) { + mSubprocess->Delete(); + mSubprocess = nsnull; + } + + nsContentUtils::UnregisterPrefCallback(kTimeoutPref, TimeoutChanged, this); +} + +#ifdef MOZ_CRASHREPORTER +void +PluginModuleParent::WritePluginExtraDataForMinidump(const nsAString& id) +{ + typedef nsDependentCString CS; + + CrashReporter::AnnotationTable notes; + if (!notes.Init(32)) + return; + + notes.Put(CS("ProcessType"), CS("plugin")); + + char startTime[32]; + sprintf(startTime, "%lld", static_cast(mProcessStartTime)); + notes.Put(CS("StartupTime"), CS(startTime)); + + // Get the plugin filename, try to get just the file leafname + const std::string& pluginFile = mSubprocess->GetPluginFilePath(); + size_t filePos = pluginFile.rfind(FILE_PATH_SEPARATOR); + if (filePos == std::string::npos) + filePos = 0; + else + filePos++; + notes.Put(CS("PluginFilename"), CS(pluginFile.substr(filePos).c_str())); + + //TODO: add plugin name and version: bug 539841 + // (as PluginName, PluginVersion) + notes.Put(CS("PluginName"), CS("")); + notes.Put(CS("PluginVersion"), CS("")); + + if (!mCrashNotes.IsEmpty()) + notes.Put(CS("Notes"), CS(mCrashNotes.get())); + + if (!mHangID.IsEmpty()) + notes.Put(CS("HangID"), NS_ConvertUTF16toUTF8(mHangID)); + + if (!CrashReporter::AppendExtraData(id, notes)) + NS_WARNING("problem appending plugin data to .extra"); +} + +void +PluginModuleParent::WriteExtraDataForHang() +{ + // this writes HangID + WritePluginExtraDataForMinidump(mPluginDumpID); + + CrashReporter::AnnotationTable notes; + if (!notes.Init(4)) + return; + + notes.Put(nsDependentCString("HangID"), NS_ConvertUTF16toUTF8(mHangID)); + if (!CrashReporter::AppendExtraData(mBrowserDumpID, notes)) + NS_WARNING("problem appending browser data to .extra"); +} +#endif // MOZ_CRASHREPORTER + +bool +PluginModuleParent::RecvAppendNotesToCrashReport(const nsCString& aNotes) +{ + mCrashNotes.Append(aNotes); + return true; +} + +int +PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thead!"); + NS_ABORT_IF_FALSE(!strcmp(aPref, kTimeoutPref), + "unexpected pref callback"); + + PRInt32 timeoutSecs = nsContentUtils::GetIntPref(kTimeoutPref, 0); + int32 timeoutMs = (timeoutSecs > 0) ? (1000 * timeoutSecs) : + SyncChannel::kNoTimeout; + + static_cast(aModule)->SetReplyTimeoutMs(timeoutMs); + return 0; +} + +void +PluginModuleParent::CleanupFromTimeout() +{ + if (!mShutdown) + Close(); +} + +bool +PluginModuleParent::ShouldContinueFromReplyTimeout() +{ +#ifdef MOZ_CRASHREPORTER + nsCOMPtr pluginDump; + nsCOMPtr browserDump; + if (CrashReporter::CreatePairedMinidumps(OtherProcess(), + mPluginThread, + &mHangID, + getter_AddRefs(pluginDump), + getter_AddRefs(browserDump)) && + CrashReporter::GetIDFromMinidump(pluginDump, mPluginDumpID) && + CrashReporter::GetIDFromMinidump(browserDump, mBrowserDumpID)) { + + PLUGIN_LOG_DEBUG( + ("generated paired browser/plugin minidumps: %s/%s (ID=%s)", + NS_ConvertUTF16toUTF8(mBrowserDumpID).get(), + NS_ConvertUTF16toUTF8(mPluginDumpID).get(), + NS_ConvertUTF16toUTF8(mHangID).get())); + } + else { + NS_WARNING("failed to capture paired minidumps from hang"); + } +#endif + + // this must run before the error notification from the channel, + // or not at all + MessageLoop::current()->PostTask( + FROM_HERE, + mTaskFactory.NewRunnableMethod( + &PluginModuleParent::CleanupFromTimeout)); + + if (!KillProcess(OtherProcess(), 1, false)) + NS_WARNING("failed to kill subprocess!"); + + return false; +} + +void +PluginModuleParent::ActorDestroy(ActorDestroyReason why) +{ + switch (why) { + case AbnormalShutdown: { +#ifdef MOZ_CRASHREPORTER + nsCOMPtr pluginDump; + if (TakeMinidump(getter_AddRefs(pluginDump)) && + CrashReporter::GetIDFromMinidump(pluginDump, mPluginDumpID)) { + PLUGIN_LOG_DEBUG(("got child minidump: %s", + NS_ConvertUTF16toUTF8(mPluginDumpID).get())); + WritePluginExtraDataForMinidump(mPluginDumpID); + } + else if (!mPluginDumpID.IsEmpty() && !mBrowserDumpID.IsEmpty()) { + WriteExtraDataForHang(); + } + else { + NS_WARNING("[PluginModuleParent::ActorDestroy] abnormal shutdown without minidump!"); + } +#endif + + mShutdown = true; + // Defer the PluginCrashed method so that we don't re-enter + // and potentially modify the actor child list while enumerating it. + if (mPlugin) + MessageLoop::current()->PostTask( + FROM_HERE, + mTaskFactory.NewRunnableMethod( + &PluginModuleParent::NotifyPluginCrashed)); + break; + } + case NormalShutdown: + mShutdown = true; + break; + + default: + NS_ERROR("Unexpected shutdown reason for toplevel actor."); + } +} + +void +PluginModuleParent::NotifyPluginCrashed() +{ + if (!OkToCleanup()) { + // there's still plugin code on the C++ stack. try again + MessageLoop::current()->PostDelayedTask( + FROM_HERE, + mTaskFactory.NewRunnableMethod( + &PluginModuleParent::NotifyPluginCrashed), 10); + return; + } + + if (mPlugin) + mPlugin->PluginCrashed(mPluginDumpID, mBrowserDumpID); +} + +PPluginIdentifierParent* +PluginModuleParent::AllocPPluginIdentifier(const nsCString& aString, + const int32_t& aInt) +{ + NPIdentifier npident = aString.IsVoid() ? + mozilla::plugins::parent::_getintidentifier(aInt) : + mozilla::plugins::parent::_getstringidentifier(aString.get()); + + if (!npident) { + NS_WARNING("Failed to get identifier!"); + return nsnull; + } + + PluginIdentifierParent* ident = new PluginIdentifierParent(npident); + mIdentifiers.Put(npident, ident); + return ident; +} + +bool +PluginModuleParent::DeallocPPluginIdentifier(PPluginIdentifierParent* aActor) +{ + delete aActor; + return true; +} + +PPluginInstanceParent* +PluginModuleParent::AllocPPluginInstance(const nsCString& aMimeType, + const uint16_t& aMode, + const nsTArray& aNames, + const nsTArray& aValues, + NPError* rv) +{ + NS_ERROR("Not reachable!"); + return NULL; +} + +bool +PluginModuleParent::DeallocPPluginInstance(PPluginInstanceParent* aActor) +{ + PLUGIN_LOG_DEBUG_METHOD; + delete aActor; + return true; +} + +void +PluginModuleParent::SetPluginFuncs(NPPluginFuncs* aFuncs) +{ + aFuncs->version = (NP_VERSION_MAJOR << 8) | NP_VERSION_MINOR; + aFuncs->javaClass = nsnull; + + aFuncs->newp = nsnull; // Gecko should always call this through a PluginLibrary object + aFuncs->destroy = NPP_Destroy; + aFuncs->setwindow = NPP_SetWindow; + aFuncs->newstream = NPP_NewStream; + aFuncs->destroystream = NPP_DestroyStream; + aFuncs->asfile = NPP_StreamAsFile; + aFuncs->writeready = NPP_WriteReady; + aFuncs->write = NPP_Write; + aFuncs->print = NPP_Print; + aFuncs->event = NPP_HandleEvent; + aFuncs->urlnotify = NPP_URLNotify; + aFuncs->getvalue = NPP_GetValue; + aFuncs->setvalue = NPP_SetValue; +} + +NPError +PluginModuleParent::NPP_Destroy(NPP instance, + NPSavedData** /*saved*/) +{ + // FIXME/cjones: + // (1) send a "destroy" message to the child + // (2) the child shuts down its instance + // (3) remove both parent and child IDs from map + // (4) free parent + PLUGIN_LOG_DEBUG_FUNCTION; + + PluginInstanceParent* parentInstance = + static_cast(instance->pdata); + + if (!parentInstance) + return NPERR_NO_ERROR; + + NPError retval = parentInstance->Destroy(); + instance->pdata = nsnull; + + (void) PluginInstanceParent::Call__delete__(parentInstance); + return retval; +} + +NPError +PluginModuleParent::NPP_NewStream(NPP instance, NPMIMEType type, + NPStream* stream, NPBool seekable, + uint16_t* stype) +{ + PluginInstanceParent* i = InstCast(instance); + if (!i) + return NPERR_GENERIC_ERROR; + + return i->NPP_NewStream(type, stream, seekable, + stype); +} + +NPError +PluginModuleParent::NPP_SetWindow(NPP instance, NPWindow* window) +{ + PluginInstanceParent* i = InstCast(instance); + if (!i) + return NPERR_GENERIC_ERROR; + + return i->NPP_SetWindow(window); +} + +NPError +PluginModuleParent::NPP_DestroyStream(NPP instance, + NPStream* stream, + NPReason reason) +{ + PluginInstanceParent* i = InstCast(instance); + if (!i) + return NPERR_GENERIC_ERROR; + + return i->NPP_DestroyStream(stream, reason); +} + +int32_t +PluginModuleParent::NPP_WriteReady(NPP instance, + NPStream* stream) +{ + BrowserStreamParent* s = StreamCast(instance, stream); + if (!s) + return -1; + + return s->WriteReady(); +} + +int32_t +PluginModuleParent::NPP_Write(NPP instance, + NPStream* stream, + int32_t offset, + int32_t len, + void* buffer) +{ + BrowserStreamParent* s = StreamCast(instance, stream); + if (!s) + return -1; + + return s->Write(offset, len, buffer); +} + +void +PluginModuleParent::NPP_StreamAsFile(NPP instance, + NPStream* stream, + const char* fname) +{ + BrowserStreamParent* s = StreamCast(instance, stream); + if (!s) + return; + + s->StreamAsFile(fname); +} + +void +PluginModuleParent::NPP_Print(NPP instance, NPPrint* platformPrint) +{ + PluginInstanceParent* i = InstCast(instance); + if (i) + i->NPP_Print(platformPrint); +} + +int16_t +PluginModuleParent::NPP_HandleEvent(NPP instance, void* event) +{ + PluginInstanceParent* i = InstCast(instance); + if (!i) + return false; + + return i->NPP_HandleEvent(event); +} + +void +PluginModuleParent::NPP_URLNotify(NPP instance, const char* url, + NPReason reason, void* notifyData) +{ + PluginInstanceParent* i = InstCast(instance); + if (!i) + return; + + i->NPP_URLNotify(url, reason, notifyData); +} + +NPError +PluginModuleParent::NPP_GetValue(NPP instance, + NPPVariable variable, void *ret_value) +{ + PluginInstanceParent* i = InstCast(instance); + if (!i) + return NPERR_GENERIC_ERROR; + + return i->NPP_GetValue(variable, ret_value); +} + +NPError +PluginModuleParent::NPP_SetValue(NPP instance, NPNVariable variable, + void *value) +{ + PluginInstanceParent* i = InstCast(instance); + if (!i) + return NPERR_GENERIC_ERROR; + + return i->NPP_SetValue(variable, value); +} + +bool +PluginModuleParent::AnswerNPN_UserAgent(nsCString* userAgent) +{ + *userAgent = NullableString(mNPNIface->uagent(nsnull)); + return true; +} + +PPluginIdentifierParent* +PluginModuleParent::GetIdentifierForNPIdentifier(NPIdentifier aIdentifier) +{ + PluginIdentifierParent* ident; + if (!mIdentifiers.Get(aIdentifier, &ident)) { + nsCString string; + int32_t intval = -1; + if (mozilla::plugins::parent::_identifierisstring(aIdentifier)) { + NPUTF8* chars = + mozilla::plugins::parent::_utf8fromidentifier(aIdentifier); + if (!chars) { + return nsnull; + } + string.Adopt(chars); + } + else { + intval = mozilla::plugins::parent::_intfromidentifier(aIdentifier); + if (intval == -1) { + return nsnull; + } + string.SetIsVoid(PR_TRUE); + } + ident = new PluginIdentifierParent(aIdentifier); + if (!SendPPluginIdentifierConstructor(ident, string, intval)) { + delete ident; + return nsnull; + } + mIdentifiers.Put(aIdentifier, ident); + } + return ident; +} + +PluginInstanceParent* +PluginModuleParent::InstCast(NPP instance) +{ + PluginInstanceParent* ip = + static_cast(instance->pdata); + + // If the plugin crashed and the PluginInstanceParent was deleted, + // instance->pdata will be NULL. + if (!ip) + return NULL; + + if (instance != ip->mNPP) { + NS_RUNTIMEABORT("Corrupted plugin data."); + } + return ip; +} + +BrowserStreamParent* +PluginModuleParent::StreamCast(NPP instance, + NPStream* s) +{ + PluginInstanceParent* ip = InstCast(instance); + if (!ip) + return NULL; + + BrowserStreamParent* sp = + static_cast(static_cast(s->pdata)); + if (sp->mNPP != ip || s != sp->mStream) { + NS_RUNTIMEABORT("Corrupted plugin stream data."); + } + return sp; +} + +bool +PluginModuleParent::HasRequiredFunctions() +{ + return true; +} + +#if defined(XP_UNIX) && !defined(XP_MACOSX) +nsresult +PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error) +{ + PLUGIN_LOG_DEBUG_METHOD; + + mNPNIface = bFuncs; + + if (mShutdown) { + *error = NPERR_GENERIC_ERROR; + return NS_ERROR_FAILURE; + } + + if (!CallNP_Initialize(&mPluginThread, error)) { + return NS_ERROR_FAILURE; + } + else if (*error != NPERR_NO_ERROR) { + return NS_OK; + } + + SetPluginFuncs(pFuncs); + return NS_OK; +} +#else +nsresult +PluginModuleParent::NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error) +{ + PLUGIN_LOG_DEBUG_METHOD; + + mNPNIface = bFuncs; + + if (mShutdown) { + *error = NPERR_GENERIC_ERROR; + return NS_ERROR_FAILURE; + } + + if (!CallNP_Initialize(&mPluginThread, error)) + return NS_ERROR_FAILURE; + + return NS_OK; +} +#endif + +nsresult +PluginModuleParent::NP_Shutdown(NPError* error) +{ + PLUGIN_LOG_DEBUG_METHOD; + + if (mShutdown) { + *error = NPERR_GENERIC_ERROR; + return NS_ERROR_FAILURE; + } + + bool ok = CallNP_Shutdown(error); + + // if NP_Shutdown() is nested within another RPC call, this will + // break things. but lord help us if we're doing that anyway; the + // plugin dso will have been unloaded on the other side by the + // CallNP_Shutdown() message + Close(); + + return ok ? NS_OK : NS_ERROR_FAILURE; +} + +nsresult +PluginModuleParent::NP_GetMIMEDescription(const char** mimeDesc) +{ + PLUGIN_LOG_DEBUG_METHOD; + + *mimeDesc = "application/x-foobar"; + return NS_OK; +} + +nsresult +PluginModuleParent::NP_GetValue(void *future, NPPVariable aVariable, + void *aValue, NPError* error) +{ + PR_LOG(gPluginLog, PR_LOG_WARNING, ("%s Not implemented, requested variable %i", __FUNCTION__, + (int) aVariable)); + + //TODO: implement this correctly + *error = NPERR_GENERIC_ERROR; + return NS_OK; +} + +#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_OS2) +nsresult +PluginModuleParent::NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error) +{ + NS_ASSERTION(pFuncs, "Null pointer!"); + + SetPluginFuncs(pFuncs); + *error = NPERR_NO_ERROR; + return NS_OK; +} +#endif + +nsresult +PluginModuleParent::NPP_New(NPMIMEType pluginType, NPP instance, + uint16_t mode, int16_t argc, char* argn[], + char* argv[], NPSavedData* saved, + NPError* error) +{ + PLUGIN_LOG_DEBUG_METHOD; + + if (mShutdown) { + *error = NPERR_GENERIC_ERROR; + return NS_ERROR_FAILURE; + } + + // create the instance on the other side + nsTArray names; + nsTArray values; + + for (int i = 0; i < argc; ++i) { + names.AppendElement(NullableString(argn[i])); + values.AppendElement(NullableString(argv[i])); + } + + PluginInstanceParent* parentInstance = + new PluginInstanceParent(this, instance, mNPNIface); + + if (!parentInstance->Init()) { + delete parentInstance; + return NS_ERROR_FAILURE; + } + + instance->pdata = parentInstance; + + if (!CallPPluginInstanceConstructor(parentInstance, + nsDependentCString(pluginType), mode, + names, values, error)) { + // |parentInstance| is automatically deleted. + instance->pdata = nsnull; + // if IPC is down, we'll get an immediate "failed" return, but + // without *error being set. So make sure that the error + // condition is signaled to nsNPAPIPluginInstance + if (NPERR_NO_ERROR == *error) + *error = NPERR_GENERIC_ERROR; + return NS_ERROR_FAILURE; + } + + if (*error != NPERR_NO_ERROR) { + NPP_Destroy(instance, 0); + return *error; + } + + return NS_OK; +} + +bool +PluginModuleParent::AnswerNPN_GetValue_WithBoolReturn(const NPNVariable& aVariable, + NPError* aError, + bool* aBoolVal) +{ + NPBool boolVal = false; + *aError = mozilla::plugins::parent::_getvalue(nsnull, aVariable, &boolVal); + *aBoolVal = boolVal ? true : false; + return true; +} + +#if !defined(MOZ_WIDGET_GTK2) +bool +PluginModuleParent::AnswerProcessSomeEvents() +{ + NS_RUNTIMEABORT("unreached"); + return false; +} + +#else +static const int kMaxChancesToProcessEvents = 20; + +bool +PluginModuleParent::AnswerProcessSomeEvents() +{ + PLUGIN_LOG_DEBUG(("Spinning mini nested loop ...")); + + int i = 0; + for (; i < kMaxChancesToProcessEvents; ++i) + if (!g_main_context_iteration(NULL, FALSE)) + break; + + PLUGIN_LOG_DEBUG(("... quitting mini nested loop; processed %i tasks", i)); + + return true; +} +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginModuleParent.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginModuleParent.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginModuleParent.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginModuleParent.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,246 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Chris Jones + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef dom_plugins_PluginModuleParent_h +#define dom_plugins_PluginModuleParent_h 1 + +#include + +#include "base/basictypes.h" + +#include "prlink.h" + +#include "npapi.h" +#include "npfunctions.h" + +#include "base/string_util.h" + +#include "mozilla/PluginLibrary.h" +#include "mozilla/plugins/PPluginModuleParent.h" +#include "mozilla/plugins/PluginInstanceParent.h" +#include "mozilla/plugins/PluginProcessParent.h" +#include "mozilla/plugins/PluginIdentifierParent.h" + +#include "nsAutoPtr.h" +#include "nsDataHashtable.h" +#include "nsHashKeys.h" +#include "nsIFileStreams.h" + +namespace mozilla { +namespace plugins { +//----------------------------------------------------------------------------- + +class BrowserStreamParent; + +/** + * PluginModuleParent + * + * This class implements the NPP API from the perspective of the rest + * of Gecko, forwarding NPP calls along to the child process that is + * actually running the plugin. + * + * This class /also/ implements a version of the NPN API, because the + * child process needs to make these calls back into Gecko proper. + * This class is responsible for "actually" making those function calls. + */ +class PluginModuleParent : public PPluginModuleParent, PluginLibrary +{ +private: + typedef mozilla::PluginLibrary PluginLibrary; + +protected: + + virtual PPluginIdentifierParent* + AllocPPluginIdentifier(const nsCString& aString, + const int32_t& aInt); + + virtual bool + DeallocPPluginIdentifier(PPluginIdentifierParent* aActor); + + PPluginInstanceParent* + AllocPPluginInstance(const nsCString& aMimeType, + const uint16_t& aMode, + const nsTArray& aNames, + const nsTArray& aValues, + NPError* rv); + + virtual bool + DeallocPPluginInstance(PPluginInstanceParent* aActor); + +public: + PluginModuleParent(const char* aFilePath); + virtual ~PluginModuleParent(); + + NS_OVERRIDE virtual void SetPlugin(nsNPAPIPlugin* plugin) + { + mPlugin = plugin; + } + + NS_OVERRIDE virtual void ActorDestroy(ActorDestroyReason why); + + /** + * LoadModule + * + * This may or may not launch a plugin child process, + * and may or may not be very expensive. + */ + static PluginLibrary* LoadModule(const char* aFilePath); + + const NPNetscapeFuncs* GetNetscapeFuncs() { + return mNPNIface; + } + + base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); } + + bool OkToCleanup() const { + return !IsOnCxxStack(); + } + + PPluginIdentifierParent* + GetIdentifierForNPIdentifier(NPIdentifier aIdentifier); + +protected: + NS_OVERRIDE + virtual mozilla::ipc::RPCChannel::RacyRPCPolicy + MediateRPCRace(const Message& parent, const Message& child) + { + return MediateRace(parent, child); + } + + NS_OVERRIDE + virtual bool ShouldContinueFromReplyTimeout(); + + virtual bool + AnswerNPN_UserAgent(nsCString* userAgent); + + virtual bool + AnswerNPN_GetValue_WithBoolReturn(const NPNVariable& aVariable, + NPError* aError, + bool* aBoolVal); + + NS_OVERRIDE + virtual bool AnswerProcessSomeEvents(); + + virtual bool + RecvAppendNotesToCrashReport(const nsCString& aNotes); + + static PluginInstanceParent* InstCast(NPP instance); + static BrowserStreamParent* StreamCast(NPP instance, NPStream* s); + +private: + void SetPluginFuncs(NPPluginFuncs* aFuncs); + + // Implement the module-level functions from NPAPI; these are + // normally resolved directly from the DSO. +#ifdef OS_LINUX + NPError NP_Initialize(const NPNetscapeFuncs* npnIface, + NPPluginFuncs* nppIface); +#else + NPError NP_Initialize(const NPNetscapeFuncs* npnIface); + NPError NP_GetEntryPoints(NPPluginFuncs* nppIface); +#endif + + // NPP-like API that Gecko calls are trampolined into. These + // messages then get forwarded along to the plugin instance, + // and then eventually the child process. + + static NPError NPP_Destroy(NPP instance, NPSavedData** save); + + static NPError NPP_SetWindow(NPP instance, NPWindow* window); + static NPError NPP_NewStream(NPP instance, NPMIMEType type, NPStream* stream, + NPBool seekable, uint16_t* stype); + static NPError NPP_DestroyStream(NPP instance, + NPStream* stream, NPReason reason); + static int32_t NPP_WriteReady(NPP instance, NPStream* stream); + static int32_t NPP_Write(NPP instance, NPStream* stream, + int32_t offset, int32_t len, void* buffer); + static void NPP_StreamAsFile(NPP instance, + NPStream* stream, const char* fname); + static void NPP_Print(NPP instance, NPPrint* platformPrint); + static int16_t NPP_HandleEvent(NPP instance, void* event); + static void NPP_URLNotify(NPP instance, const char* url, + NPReason reason, void* notifyData); + static NPError NPP_GetValue(NPP instance, + NPPVariable variable, void *ret_value); + static NPError NPP_SetValue(NPP instance, NPNVariable variable, + void *value); + + virtual bool HasRequiredFunctions(); + +#if defined(XP_UNIX) && !defined(XP_MACOSX) + virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPPluginFuncs* pFuncs, NPError* error); +#else + virtual nsresult NP_Initialize(NPNetscapeFuncs* bFuncs, NPError* error); +#endif + virtual nsresult NP_Shutdown(NPError* error); + virtual nsresult NP_GetMIMEDescription(const char** mimeDesc); + virtual nsresult NP_GetValue(void *future, NPPVariable aVariable, + void *aValue, NPError* error); +#if defined(XP_WIN) || defined(XP_MACOSX) || defined(XP_OS2) + virtual nsresult NP_GetEntryPoints(NPPluginFuncs* pFuncs, NPError* error); +#endif + virtual nsresult NPP_New(NPMIMEType pluginType, NPP instance, + uint16_t mode, int16_t argc, char* argn[], + char* argv[], NPSavedData* saved, + NPError* error); +private: + void WritePluginExtraDataForMinidump(const nsAString& id); + void WriteExtraDataForHang(); + void CleanupFromTimeout(); + static int TimeoutChanged(const char* aPref, void* aModule); + void NotifyPluginCrashed(); + + nsCString mCrashNotes; + PluginProcessParent* mSubprocess; + // the plugin thread in mSubprocess + NativeThreadId mPluginThread; + bool mShutdown; + const NPNetscapeFuncs* mNPNIface; + nsDataHashtable mIdentifiers; + nsNPAPIPlugin* mPlugin; + time_t mProcessStartTime; + ScopedRunnableMethodFactory mTaskFactory; + nsString mPluginDumpID; + nsString mBrowserDumpID; + nsString mHangID; +}; + +} // namespace plugins +} // namespace mozilla + +#endif // ifndef dom_plugins_PluginModuleParent_h diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginProcessParent.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginProcessParent.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginProcessParent.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginProcessParent.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,92 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Ben Turner . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "mozilla/plugins/PluginProcessParent.h" + +#include "base/string_util.h" +#include "mozilla/ipc/BrowserProcessSubThread.h" + +using mozilla::ipc::BrowserProcessSubThread; +using mozilla::ipc::GeckoChildProcessHost; +using mozilla::plugins::PluginProcessParent; + +template<> +struct RunnableMethodTraits +{ + static void RetainCallee(PluginProcessParent* obj) { } + static void ReleaseCallee(PluginProcessParent* obj) { } +}; + +PluginProcessParent::PluginProcessParent(const std::string& aPluginFilePath) : + GeckoChildProcessHost(GeckoProcessType_Plugin), + mPluginFilePath(aPluginFilePath) +{ +} + +PluginProcessParent::~PluginProcessParent() +{ +} + +bool +PluginProcessParent::Launch() +{ + std::vector args; +#if defined(XP_WIN) + args.push_back("\""+ mPluginFilePath +"\""); +#else + args.push_back(mPluginFilePath); +#endif + return SyncLaunch(args); +} + +void +PluginProcessParent::Delete() +{ + MessageLoop* currentLoop = MessageLoop::current(); + MessageLoop* ioLoop = + BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO); + + if (currentLoop == ioLoop) { + delete this; + return; + } + + ioLoop->PostTask(FROM_HERE, + NewRunnableMethod(this, &PluginProcessParent::Delete)); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginProcessParent.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginProcessParent.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginProcessParent.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginProcessParent.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Ben Turner . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef dom_plugins_PluginProcessParent_h +#define dom_plugins_PluginProcessParent_h 1 + +#include "base/basictypes.h" + +#include "base/file_path.h" +#include "base/scoped_ptr.h" +#include "base/thread.h" +#include "base/waitable_event.h" +#include "chrome/common/child_process_host.h" + +#include "mozilla/ipc/GeckoChildProcessHost.h" + +namespace mozilla { +namespace plugins { +//----------------------------------------------------------------------------- + +class PluginProcessParent : mozilla::ipc::GeckoChildProcessHost +{ +public: + PluginProcessParent(const std::string& aPluginFilePath); + ~PluginProcessParent(); + + /** + * Synchronously launch the plugin process. + */ + bool Launch(); + + void Delete(); + + virtual bool CanShutdown() + { + return true; + } + + const std::string& GetPluginFilePath() { return mPluginFilePath; } + + using mozilla::ipc::GeckoChildProcessHost::GetShutDownEvent; + using mozilla::ipc::GeckoChildProcessHost::GetChannel; + using mozilla::ipc::GeckoChildProcessHost::GetChildProcessHandle; + +private: + std::string mPluginFilePath; + + DISALLOW_EVIL_CONSTRUCTORS(PluginProcessParent); +}; + + +} // namespace plugins +} // namespace mozilla + +#endif // ifndef dom_plugins_PluginProcessParent_h diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginScriptableObjectChild.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginScriptableObjectChild.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginScriptableObjectChild.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginScriptableObjectChild.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,1099 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Ben Turner + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "PluginScriptableObjectChild.h" +#include "PluginScriptableObjectUtils.h" +#include "PluginIdentifierChild.h" + +using namespace mozilla::plugins; + +// static +NPObject* +PluginScriptableObjectChild::ScriptableAllocate(NPP aInstance, + NPClass* aClass) +{ + AssertPluginThread(); + + if (aClass != GetClass()) { + NS_RUNTIMEABORT("Huh?! Wrong class!"); + } + + return new ChildNPObject(); +} + +// static +void +PluginScriptableObjectChild::ScriptableInvalidate(NPObject* aObject) +{ + AssertPluginThread(); + + if (aObject->_class != GetClass()) { + NS_RUNTIMEABORT("Don't know what kind of object this is!"); + } + + ChildNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + // This can happen more than once, and is just fine. + return; + } + + object->invalidated = true; +} + +// static +void +PluginScriptableObjectChild::ScriptableDeallocate(NPObject* aObject) +{ + AssertPluginThread(); + + if (aObject->_class != GetClass()) { + NS_RUNTIMEABORT("Don't know what kind of object this is!"); + } + + ChildNPObject* object = reinterpret_cast(aObject); + PluginScriptableObjectChild* actor = object->parent; + if (actor) { + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + actor->DropNPObject(); + } + + delete object; +} + +// static +bool +PluginScriptableObjectChild::ScriptableHasMethod(NPObject* aObject, + NPIdentifier aName) +{ + AssertPluginThread(); + + if (aObject->_class != GetClass()) { + NS_RUNTIMEABORT("Don't know what kind of object this is!"); + } + + ChildNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + ProtectedActor actor(object->parent); + NS_ASSERTION(actor, "This shouldn't ever be null!"); + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + bool result; + actor->CallHasMethod(static_cast(aName), &result); + + return result; +} + +// static +bool +PluginScriptableObjectChild::ScriptableInvoke(NPObject* aObject, + NPIdentifier aName, + const NPVariant* aArgs, + uint32_t aArgCount, + NPVariant* aResult) +{ + AssertPluginThread(); + + if (aObject->_class != GetClass()) { + NS_RUNTIMEABORT("Don't know what kind of object this is!"); + } + + ChildNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + ProtectedActor actor(object->parent); + NS_ASSERTION(actor, "This shouldn't ever be null!"); + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance()); + if (!args.IsOk()) { + NS_ERROR("Failed to convert arguments!"); + return false; + } + + Variant remoteResult; + bool success; + actor->CallInvoke(static_cast(aName), args, + &remoteResult, &success); + + if (!success) { + return false; + } + + ConvertToVariant(remoteResult, *aResult); + return true; +} + +// static +bool +PluginScriptableObjectChild::ScriptableInvokeDefault(NPObject* aObject, + const NPVariant* aArgs, + uint32_t aArgCount, + NPVariant* aResult) +{ + AssertPluginThread(); + + if (aObject->_class != GetClass()) { + NS_RUNTIMEABORT("Don't know what kind of object this is!"); + } + + ChildNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + ProtectedActor actor(object->parent); + NS_ASSERTION(actor, "This shouldn't ever be null!"); + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance()); + if (!args.IsOk()) { + NS_ERROR("Failed to convert arguments!"); + return false; + } + + Variant remoteResult; + bool success; + actor->CallInvokeDefault(args, &remoteResult, &success); + + if (!success) { + return false; + } + + ConvertToVariant(remoteResult, *aResult); + return true; +} + +// static +bool +PluginScriptableObjectChild::ScriptableHasProperty(NPObject* aObject, + NPIdentifier aName) +{ + AssertPluginThread(); + + if (aObject->_class != GetClass()) { + NS_RUNTIMEABORT("Don't know what kind of object this is!"); + } + + ChildNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + ProtectedActor actor(object->parent); + NS_ASSERTION(actor, "This shouldn't ever be null!"); + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + bool result; + actor->CallHasProperty(static_cast(aName), &result); + + return result; +} + +// static +bool +PluginScriptableObjectChild::ScriptableGetProperty(NPObject* aObject, + NPIdentifier aName, + NPVariant* aResult) +{ + AssertPluginThread(); + + if (aObject->_class != GetClass()) { + NS_RUNTIMEABORT("Don't know what kind of object this is!"); + } + + ChildNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + ProtectedActor actor(object->parent); + NS_ASSERTION(actor, "This shouldn't ever be null!"); + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + Variant result; + bool success; + actor->CallGetParentProperty(static_cast(aName), + &result, &success); + + if (!success) { + return false; + } + + ConvertToVariant(result, *aResult); + return true; +} + +// static +bool +PluginScriptableObjectChild::ScriptableSetProperty(NPObject* aObject, + NPIdentifier aName, + const NPVariant* aValue) +{ + AssertPluginThread(); + + if (aObject->_class != GetClass()) { + NS_RUNTIMEABORT("Don't know what kind of object this is!"); + } + + ChildNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + ProtectedActor actor(object->parent); + NS_ASSERTION(actor, "This shouldn't ever be null!"); + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + ProtectedVariant value(*aValue, actor->GetInstance()); + if (!value.IsOk()) { + NS_WARNING("Failed to convert variant!"); + return false; + } + + bool success; + actor->CallSetProperty(static_cast(aName), value, + &success); + + return success; +} + +// static +bool +PluginScriptableObjectChild::ScriptableRemoveProperty(NPObject* aObject, + NPIdentifier aName) +{ + AssertPluginThread(); + + if (aObject->_class != GetClass()) { + NS_RUNTIMEABORT("Don't know what kind of object this is!"); + } + + ChildNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + ProtectedActor actor(object->parent); + NS_ASSERTION(actor, "This shouldn't ever be null!"); + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + bool success; + actor->CallRemoveProperty(static_cast(aName), + &success); + + return success; +} + +// static +bool +PluginScriptableObjectChild::ScriptableEnumerate(NPObject* aObject, + NPIdentifier** aIdentifiers, + uint32_t* aCount) +{ + AssertPluginThread(); + + if (aObject->_class != GetClass()) { + NS_RUNTIMEABORT("Don't know what kind of object this is!"); + } + + ChildNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + ProtectedActor actor(object->parent); + NS_ASSERTION(actor, "This shouldn't ever be null!"); + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + nsAutoTArray identifiers; + bool success; + actor->CallEnumerate(&identifiers, &success); + + if (!success) { + return false; + } + + *aCount = identifiers.Length(); + if (!*aCount) { + *aIdentifiers = nsnull; + return true; + } + + *aIdentifiers = reinterpret_cast( + PluginModuleChild::sBrowserFuncs.memalloc(*aCount * sizeof(NPIdentifier))); + if (!*aIdentifiers) { + NS_ERROR("Out of memory!"); + return false; + } + + for (PRUint32 index = 0; index < *aCount; index++) { + (*aIdentifiers)[index] = + static_cast(identifiers[index]); + } + return true; +} + +// static +bool +PluginScriptableObjectChild::ScriptableConstruct(NPObject* aObject, + const NPVariant* aArgs, + uint32_t aArgCount, + NPVariant* aResult) +{ + AssertPluginThread(); + + if (aObject->_class != GetClass()) { + NS_RUNTIMEABORT("Don't know what kind of object this is!"); + } + + ChildNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + ProtectedActor actor(object->parent); + NS_ASSERTION(actor, "This shouldn't ever be null!"); + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance()); + if (!args.IsOk()) { + NS_ERROR("Failed to convert arguments!"); + return false; + } + + Variant remoteResult; + bool success; + actor->CallConstruct(args, &remoteResult, &success); + + if (!success) { + return false; + } + + ConvertToVariant(remoteResult, *aResult); + return true; +} + +const NPClass PluginScriptableObjectChild::sNPClass = { + NP_CLASS_STRUCT_VERSION, + PluginScriptableObjectChild::ScriptableAllocate, + PluginScriptableObjectChild::ScriptableDeallocate, + PluginScriptableObjectChild::ScriptableInvalidate, + PluginScriptableObjectChild::ScriptableHasMethod, + PluginScriptableObjectChild::ScriptableInvoke, + PluginScriptableObjectChild::ScriptableInvokeDefault, + PluginScriptableObjectChild::ScriptableHasProperty, + PluginScriptableObjectChild::ScriptableGetProperty, + PluginScriptableObjectChild::ScriptableSetProperty, + PluginScriptableObjectChild::ScriptableRemoveProperty, + PluginScriptableObjectChild::ScriptableEnumerate, + PluginScriptableObjectChild::ScriptableConstruct +}; + +PluginScriptableObjectChild::PluginScriptableObjectChild( + ScriptableObjectType aType) +: mInstance(nsnull), + mObject(nsnull), + mInvalidated(false), + mProtectCount(0), + mType(aType) +{ + AssertPluginThread(); +} + +PluginScriptableObjectChild::~PluginScriptableObjectChild() +{ + AssertPluginThread(); + + if (mObject) { + PluginModuleChild::current()->UnregisterActorForNPObject(mObject); + + if (mObject->_class == GetClass()) { + NS_ASSERTION(mType == Proxy, "Wrong type!"); + static_cast(mObject)->parent = nsnull; + } + else { + NS_ASSERTION(mType == LocalObject, "Wrong type!"); + PluginModuleChild::sBrowserFuncs.releaseobject(mObject); + } + } +} + +void +PluginScriptableObjectChild::InitializeProxy() +{ + AssertPluginThread(); + NS_ASSERTION(mType == Proxy, "Bad type!"); + NS_ASSERTION(!mObject, "Calling Initialize more than once!"); + NS_ASSERTION(!mInvalidated, "Already invalidated?!"); + + mInstance = static_cast(Manager()); + NS_ASSERTION(mInstance, "Null manager?!"); + + NPObject* object = CreateProxyObject(); + NS_ASSERTION(object, "Failed to create object!"); + + if (!PluginModuleChild::current()->RegisterActorForNPObject(object, this)) { + NS_ERROR("Out of memory?"); + } + + mObject = object; +} + +void +PluginScriptableObjectChild::InitializeLocal(NPObject* aObject) +{ + AssertPluginThread(); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + NS_ASSERTION(!mObject, "Calling Initialize more than once!"); + NS_ASSERTION(!mInvalidated, "Already invalidated?!"); + + mInstance = static_cast(Manager()); + NS_ASSERTION(mInstance, "Null manager?!"); + + PluginModuleChild::sBrowserFuncs.retainobject(aObject); + + NS_ASSERTION(!mProtectCount, "Should be zero!"); + mProtectCount++; + + if (!PluginModuleChild::current()->RegisterActorForNPObject(aObject, this)) { + NS_ERROR("Out of memory?"); + } + + mObject = aObject; +} + +NPObject* +PluginScriptableObjectChild::CreateProxyObject() +{ + NS_ASSERTION(mInstance, "Must have an instance!"); + NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!"); + + NPClass* proxyClass = const_cast(GetClass()); + NPObject* npobject = + PluginModuleChild::sBrowserFuncs.createobject(mInstance->GetNPP(), + proxyClass); + NS_ASSERTION(npobject, "Failed to create object?!"); + NS_ASSERTION(npobject->_class == GetClass(), "Wrong kind of object!"); + NS_ASSERTION(npobject->referenceCount == 1, "Some kind of live object!"); + + ChildNPObject* object = static_cast(npobject); + NS_ASSERTION(!object->invalidated, "Bad object!"); + NS_ASSERTION(!object->parent, "Bad object!"); + + // We don't want to have the actor own this object but rather let the object + // own this actor. Set the reference count to 0 here so that when the object + // dies we will send the destructor message to the child. + object->referenceCount = 0; + NS_LOG_RELEASE(object, 0, "NPObject"); + + object->parent = const_cast(this); + return object; +} + +bool +PluginScriptableObjectChild::ResurrectProxyObject() +{ + NS_ASSERTION(mInstance, "Must have an instance already!"); + NS_ASSERTION(!mObject, "Should not have an object already!"); + NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!"); + + NPObject* object = CreateProxyObject(); + if (!object) { + NS_WARNING("Failed to create object!"); + return false; + } + + InitializeProxy(); + NS_ASSERTION(mObject, "Initialize failed!"); + + SendProtect(); + return true; +} + +NPObject* +PluginScriptableObjectChild::GetObject(bool aCanResurrect) +{ + if (!mObject && aCanResurrect && !ResurrectProxyObject()) { + NS_ERROR("Null object!"); + return nsnull; + } + return mObject; +} + +void +PluginScriptableObjectChild::Protect() +{ + NS_ASSERTION(mObject, "No object!"); + NS_ASSERTION(mProtectCount >= 0, "Negative retain count?!"); + + if (mType == LocalObject) { + ++mProtectCount; + } +} + +void +PluginScriptableObjectChild::Unprotect() +{ + NS_ASSERTION(mObject, "Bad state!"); + NS_ASSERTION(mProtectCount >= 0, "Negative retain count?!"); + + if (mType == LocalObject) { + if (--mProtectCount == 0) { + PluginScriptableObjectChild::Send__delete__(this); + } + } +} + +void +PluginScriptableObjectChild::DropNPObject() +{ + NS_ASSERTION(mObject, "Invalidated object!"); + NS_ASSERTION(mObject->_class == GetClass(), "Wrong type of object!"); + NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!"); + + // We think we're about to be deleted, but we could be racing with the other + // process. + PluginModuleChild::current()->UnregisterActorForNPObject(mObject); + mObject = nsnull; + + SendUnprotect(); +} + +void +PluginScriptableObjectChild::NPObjectDestroyed() +{ + NS_ASSERTION(LocalObject == mType, + "ScriptableDeallocate should have handled this for proxies"); + mInvalidated = true; + mObject = NULL; +} + +bool +PluginScriptableObjectChild::AnswerInvalidate() +{ + AssertPluginThread(); + + if (mInvalidated) { + return true; + } + + mInvalidated = true; + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + if (mObject->_class && mObject->_class->invalidate) { + mObject->_class->invalidate(mObject); + } + + Unprotect(); + + return true; +} + +bool +PluginScriptableObjectChild::AnswerHasMethod(PPluginIdentifierChild* aId, + bool* aHasMethod) +{ + AssertPluginThread(); + + if (mInvalidated) { + NS_WARNING("Calling AnswerHasMethod with an invalidated object!"); + *aHasMethod = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + if (!(mObject->_class && mObject->_class->hasMethod)) { + *aHasMethod = false; + return true; + } + + PluginIdentifierChild* id = static_cast(aId); + *aHasMethod = mObject->_class->hasMethod(mObject, id->ToNPIdentifier()); + return true; +} + +bool +PluginScriptableObjectChild::AnswerInvoke(PPluginIdentifierChild* aId, + const nsTArray& aArgs, + Variant* aResult, + bool* aSuccess) +{ + AssertPluginThread(); + + if (mInvalidated) { + NS_WARNING("Calling AnswerInvoke with an invalidated object!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + if (!(mObject->_class && mObject->_class->invoke)) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + nsAutoTArray convertedArgs; + PRUint32 argCount = aArgs.Length(); + + if (!convertedArgs.SetLength(argCount)) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + for (PRUint32 index = 0; index < argCount; index++) { + ConvertToVariant(aArgs[index], convertedArgs[index]); + } + + NPVariant result; + VOID_TO_NPVARIANT(result); + PluginIdentifierChild* id = static_cast(aId); + bool success = mObject->_class->invoke(mObject, id->ToNPIdentifier(), + convertedArgs.Elements(), argCount, + &result); + + for (PRUint32 index = 0; index < argCount; index++) { + PluginModuleChild::sBrowserFuncs.releasevariantvalue(&convertedArgs[index]); + } + + if (!success) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + Variant convertedResult; + success = ConvertToRemoteVariant(result, convertedResult, GetInstance(), + false); + + DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result); + + if (!success) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + *aSuccess = true; + *aResult = convertedResult; + return true; +} + +bool +PluginScriptableObjectChild::AnswerInvokeDefault(const nsTArray& aArgs, + Variant* aResult, + bool* aSuccess) +{ + AssertPluginThread(); + + if (mInvalidated) { + NS_WARNING("Calling AnswerInvokeDefault with an invalidated object!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + if (!(mObject->_class && mObject->_class->invokeDefault)) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + nsAutoTArray convertedArgs; + PRUint32 argCount = aArgs.Length(); + + if (!convertedArgs.SetLength(argCount)) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + for (PRUint32 index = 0; index < argCount; index++) { + ConvertToVariant(aArgs[index], convertedArgs[index]); + } + + NPVariant result; + VOID_TO_NPVARIANT(result); + bool success = mObject->_class->invokeDefault(mObject, + convertedArgs.Elements(), + argCount, &result); + + for (PRUint32 index = 0; index < argCount; index++) { + PluginModuleChild::sBrowserFuncs.releasevariantvalue(&convertedArgs[index]); + } + + if (!success) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + Variant convertedResult; + success = ConvertToRemoteVariant(result, convertedResult, GetInstance(), + false); + + DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result); + + if (!success) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + *aResult = convertedResult; + *aSuccess = true; + return true; +} + +bool +PluginScriptableObjectChild::AnswerHasProperty(PPluginIdentifierChild* aId, + bool* aHasProperty) +{ + AssertPluginThread(); + + if (mInvalidated) { + NS_WARNING("Calling AnswerHasProperty with an invalidated object!"); + *aHasProperty = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + if (!(mObject->_class && mObject->_class->hasProperty)) { + *aHasProperty = false; + return true; + } + + PluginIdentifierChild* id = static_cast(aId); + *aHasProperty = mObject->_class->hasProperty(mObject, id->ToNPIdentifier()); + return true; +} + +bool +PluginScriptableObjectChild::AnswerGetChildProperty(PPluginIdentifierChild* aId, + bool* aHasProperty, + bool* aHasMethod, + Variant* aResult, + bool* aSuccess) +{ + AssertPluginThread(); + + *aHasProperty = *aHasMethod = *aSuccess = false; + *aResult = void_t(); + + if (mInvalidated) { + NS_WARNING("Calling AnswerGetProperty with an invalidated object!"); + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + if (!(mObject->_class && mObject->_class->hasProperty && + mObject->_class->hasMethod && mObject->_class->getProperty)) { + return true; + } + + NPIdentifier id = static_cast(aId)->ToNPIdentifier(); + + *aHasProperty = mObject->_class->hasProperty(mObject, id); + *aHasMethod = mObject->_class->hasMethod(mObject, id); + + if (*aHasProperty) { + NPVariant result; + VOID_TO_NPVARIANT(result); + + if (!mObject->_class->getProperty(mObject, id, &result)) { + return true; + } + + Variant converted; + if ((*aSuccess = ConvertToRemoteVariant(result, converted, GetInstance(), + false))) { + DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result); + *aResult = converted; + } + } + + return true; +} + +bool +PluginScriptableObjectChild::AnswerSetProperty(PPluginIdentifierChild* aId, + const Variant& aValue, + bool* aSuccess) +{ + AssertPluginThread(); + + if (mInvalidated) { + NS_WARNING("Calling AnswerSetProperty with an invalidated object!"); + *aSuccess = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + if (!(mObject->_class && mObject->_class->hasProperty && + mObject->_class->setProperty)) { + *aSuccess = false; + return true; + } + + NPIdentifier id = static_cast(aId)->ToNPIdentifier(); + + if (!mObject->_class->hasProperty(mObject, id)) { + *aSuccess = false; + return true; + } + + NPVariant converted; + ConvertToVariant(aValue, converted); + + if ((*aSuccess = mObject->_class->setProperty(mObject, id, &converted))) { + PluginModuleChild::sBrowserFuncs.releasevariantvalue(&converted); + } + return true; +} + +bool +PluginScriptableObjectChild::AnswerRemoveProperty(PPluginIdentifierChild* aId, + bool* aSuccess) +{ + AssertPluginThread(); + + if (mInvalidated) { + NS_WARNING("Calling AnswerRemoveProperty with an invalidated object!"); + *aSuccess = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + if (!(mObject->_class && mObject->_class->hasProperty && + mObject->_class->removeProperty)) { + *aSuccess = false; + return true; + } + + NPIdentifier id = static_cast(aId)->ToNPIdentifier(); + *aSuccess = mObject->_class->hasProperty(mObject, id) ? + mObject->_class->removeProperty(mObject, id) : + true; + + return true; +} + +bool +PluginScriptableObjectChild::AnswerEnumerate(nsTArray* aProperties, + bool* aSuccess) +{ + AssertPluginThread(); + + if (mInvalidated) { + NS_WARNING("Calling AnswerEnumerate with an invalidated object!"); + *aSuccess = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + if (!(mObject->_class && mObject->_class->enumerate)) { + *aSuccess = false; + return true; + } + + NPIdentifier* ids; + uint32_t idCount; + if (!mObject->_class->enumerate(mObject, &ids, &idCount)) { + *aSuccess = false; + return true; + } + + if (!aProperties->SetCapacity(idCount)) { + PluginModuleChild::sBrowserFuncs.memfree(ids); + *aSuccess = false; + return true; + } + + for (uint32_t index = 0; index < idCount; index++) { + PluginIdentifierChild* id = static_cast(ids[index]); + aProperties->AppendElement(id); + } + + PluginModuleChild::sBrowserFuncs.memfree(ids); + *aSuccess = true; + return true; +} + +bool +PluginScriptableObjectChild::AnswerConstruct(const nsTArray& aArgs, + Variant* aResult, + bool* aSuccess) +{ + AssertPluginThread(); + + if (mInvalidated) { + NS_WARNING("Calling AnswerConstruct with an invalidated object!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + if (!(mObject->_class && mObject->_class->construct)) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + nsAutoTArray convertedArgs; + PRUint32 argCount = aArgs.Length(); + + if (!convertedArgs.SetLength(argCount)) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + for (PRUint32 index = 0; index < argCount; index++) { + ConvertToVariant(aArgs[index], convertedArgs[index]); + } + + NPVariant result; + VOID_TO_NPVARIANT(result); + bool success = mObject->_class->construct(mObject, convertedArgs.Elements(), + argCount, &result); + + for (PRUint32 index = 0; index < argCount; index++) { + PluginModuleChild::sBrowserFuncs.releasevariantvalue(&convertedArgs[index]); + } + + if (!success) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + Variant convertedResult; + success = ConvertToRemoteVariant(result, convertedResult, GetInstance(), + false); + + DeferNPVariantLastRelease(&PluginModuleChild::sBrowserFuncs, &result); + + if (!success) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + *aResult = convertedResult; + *aSuccess = true; + return true; +} + +bool +PluginScriptableObjectChild::RecvProtect() +{ + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + Protect(); + return true; +} + +bool +PluginScriptableObjectChild::RecvUnprotect() +{ + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + Unprotect(); + return true; +} + +bool +PluginScriptableObjectChild::Evaluate(NPString* aScript, + NPVariant* aResult) +{ + nsDependentCString script(""); + if (aScript->UTF8Characters && aScript->UTF8Length) { + script.Rebind(aScript->UTF8Characters, aScript->UTF8Length); + } + + bool success; + Variant result; + CallNPN_Evaluate(script, &result, &success); + + if (!success) { + return false; + } + + ConvertToVariant(result, *aResult); + return true; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginScriptableObjectChild.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginScriptableObjectChild.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginScriptableObjectChild.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginScriptableObjectChild.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,269 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Ben Turner + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef dom_plugins_PluginScriptableObjectChild_h +#define dom_plugins_PluginScriptableObjectChild_h 1 + +#include "mozilla/plugins/PPluginScriptableObjectChild.h" + +#include "npruntime.h" + +namespace mozilla { +namespace plugins { + +class PluginInstanceChild; +class PluginScriptableObjectChild; +class PPluginIdentifierChild; + +struct ChildNPObject : NPObject +{ + ChildNPObject() + : NPObject(), parent(NULL), invalidated(false) + { + MOZ_COUNT_CTOR(ChildNPObject); + } + + ~ChildNPObject() + { + MOZ_COUNT_DTOR(ChildNPObject); + } + + // |parent| is always valid as long as the actor is alive. Once the actor is + // destroyed this will be set to null. + PluginScriptableObjectChild* parent; + bool invalidated; +}; + +class PluginScriptableObjectChild : public PPluginScriptableObjectChild +{ + friend class PluginInstanceChild; + +public: + PluginScriptableObjectChild(ScriptableObjectType aType); + virtual ~PluginScriptableObjectChild(); + + void + InitializeProxy(); + + void + InitializeLocal(NPObject* aObject); + + + virtual bool + AnswerInvalidate(); + + virtual bool + AnswerHasMethod(PPluginIdentifierChild* aId, + bool* aHasMethod); + + virtual bool + AnswerInvoke(PPluginIdentifierChild* aId, + const nsTArray& aArgs, + Variant* aResult, + bool* aSuccess); + + virtual bool + AnswerInvokeDefault(const nsTArray& aArgs, + Variant* aResult, + bool* aSuccess); + + virtual bool + AnswerHasProperty(PPluginIdentifierChild* aId, + bool* aHasProperty); + + virtual bool + AnswerGetChildProperty(PPluginIdentifierChild* aId, + bool* aHasProperty, + bool* aHasMethod, + Variant* aResult, + bool* aSuccess); + + virtual bool + AnswerSetProperty(PPluginIdentifierChild* aId, + const Variant& aValue, + bool* aSuccess); + + virtual bool + AnswerRemoveProperty(PPluginIdentifierChild* aId, + bool* aSuccess); + + virtual bool + AnswerEnumerate(nsTArray* aProperties, + bool* aSuccess); + + virtual bool + AnswerConstruct(const nsTArray& aArgs, + Variant* aResult, + bool* aSuccess); + + virtual bool + RecvProtect(); + + virtual bool + RecvUnprotect(); + + NPObject* + GetObject(bool aCanResurrect); + + static const NPClass* + GetClass() + { + return &sNPClass; + } + + PluginInstanceChild* + GetInstance() const + { + return mInstance; + } + + // Protect only affects LocalObject actors. It is called by the + // ProtectedVariant/Actor helper classes before the actor is used as an + // argument to an IPC call and when the parent process resurrects a + // proxy object to the NPObject associated with this actor. + void Protect(); + + // Unprotect only affects LocalObject actors. It is called by the + // ProtectedVariant/Actor helper classes after the actor is used as an + // argument to an IPC call and when the parent process is no longer using + // this actor. + void Unprotect(); + + // DropNPObject is only used for Proxy actors and is called when the child + // process is no longer using the NPObject associated with this actor. The + // parent process may subsequently use this actor again in which case a new + // NPObject will be created and associated with this actor (see + // ResurrectProxyObject). + void DropNPObject(); + + /** + * After NPP_Destroy, all NPObjects associated with an instance are + * destroyed. We are informed of this destruction. This should only be called + * on Local actors. + */ + void NPObjectDestroyed(); + + bool + Evaluate(NPString* aScript, + NPVariant* aResult); + + ScriptableObjectType + Type() const { + return mType; + } + +private: + static NPObject* + ScriptableAllocate(NPP aInstance, + NPClass* aClass); + + static void + ScriptableInvalidate(NPObject* aObject); + + static void + ScriptableDeallocate(NPObject* aObject); + + static bool + ScriptableHasMethod(NPObject* aObject, + NPIdentifier aName); + + static bool + ScriptableInvoke(NPObject* aObject, + NPIdentifier aName, + const NPVariant* aArgs, + uint32_t aArgCount, + NPVariant* aResult); + + static bool + ScriptableInvokeDefault(NPObject* aObject, + const NPVariant* aArgs, + uint32_t aArgCount, + NPVariant* aResult); + + static bool + ScriptableHasProperty(NPObject* aObject, + NPIdentifier aName); + + static bool + ScriptableGetProperty(NPObject* aObject, + NPIdentifier aName, + NPVariant* aResult); + + static bool + ScriptableSetProperty(NPObject* aObject, + NPIdentifier aName, + const NPVariant* aValue); + + static bool + ScriptableRemoveProperty(NPObject* aObject, + NPIdentifier aName); + + static bool + ScriptableEnumerate(NPObject* aObject, + NPIdentifier** aIdentifiers, + uint32_t* aCount); + + static bool + ScriptableConstruct(NPObject* aObject, + const NPVariant* aArgs, + uint32_t aArgCount, + NPVariant* aResult); + + NPObject* + CreateProxyObject(); + + // ResurrectProxyObject is only used with Proxy actors. It is called when the + // parent process uses an actor whose NPObject was deleted by the child + // process. + bool ResurrectProxyObject(); + +private: + PluginInstanceChild* mInstance; + NPObject* mObject; + bool mInvalidated; + int mProtectCount; + + ScriptableObjectType mType; + + static const NPClass sNPClass; +}; + +} /* namespace plugins */ +} /* namespace mozilla */ + +#endif /* dom_plugins_PluginScriptableObjectChild_h */ diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginScriptableObjectParent.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginScriptableObjectParent.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginScriptableObjectParent.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginScriptableObjectParent.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,1293 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Ben Turner + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "PluginScriptableObjectParent.h" +#include "PluginScriptableObjectUtils.h" + +using namespace mozilla::plugins; + +namespace { + +inline void +ReleaseVariant(NPVariant& aVariant, + PluginInstanceParent* aInstance) +{ + const NPNetscapeFuncs* npn = GetNetscapeFuncs(aInstance); + if (npn) { + npn->releasevariantvalue(&aVariant); + } +} + +inline PPluginIdentifierParent* +GetIdentifier(PluginInstanceParent* aInstance, + NPIdentifier aIdentifier) +{ + PluginModuleParent* module = aInstance->Module(); + if (!module) { + NS_WARNING("Huh?!"); + return false; + } + + return module->GetIdentifierForNPIdentifier(aIdentifier); +} + +inline PPluginIdentifierParent* +GetIdentifier(NPObject* aObject, + NPIdentifier aIdentifier) +{ + PluginInstanceParent* instance = GetInstance(aObject); + if (!instance) { + NS_WARNING("Huh?!"); + return false; + } + + return GetIdentifier(instance, aIdentifier); +} + +} // anonymous namespace + +// static +NPObject* +PluginScriptableObjectParent::ScriptableAllocate(NPP aInstance, + NPClass* aClass) +{ + if (aClass != GetClass()) { + NS_ERROR("Huh?! Wrong class!"); + return nsnull; + } + + return new ParentNPObject(); +} + +// static +void +PluginScriptableObjectParent::ScriptableInvalidate(NPObject* aObject) +{ + if (aObject->_class != GetClass()) { + NS_ERROR("Don't know what kind of object this is!"); + return; + } + + ParentNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + // This can happen more than once, and is just fine. + return; + } + + object->invalidated = true; + + // |object->parent| may be null already if the instance has gone away. + if (object->parent && !object->parent->CallInvalidate()) { + NS_ERROR("Failed to send message!"); + } +} + +// static +void +PluginScriptableObjectParent::ScriptableDeallocate(NPObject* aObject) +{ + if (aObject->_class != GetClass()) { + NS_ERROR("Don't know what kind of object this is!"); + return; + } + + ParentNPObject* object = reinterpret_cast(aObject); + PluginScriptableObjectParent* actor = object->parent; + if (actor) { + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + actor->DropNPObject(); + } + + delete object; +} + +// static +bool +PluginScriptableObjectParent::ScriptableHasMethod(NPObject* aObject, + NPIdentifier aName) +{ + if (aObject->_class != GetClass()) { + NS_ERROR("Don't know what kind of object this is!"); + return false; + } + + ParentNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + PPluginIdentifierParent* identifier = GetIdentifier(aObject, aName); + if (!identifier) { + return false; + } + + ProtectedActor actor(object->parent); + if (!actor) { + return false; + } + + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + bool result; + if (!actor->CallHasMethod(identifier, &result)) { + NS_WARNING("Failed to send message!"); + return false; + } + + return result; +} + +// static +bool +PluginScriptableObjectParent::ScriptableInvoke(NPObject* aObject, + NPIdentifier aName, + const NPVariant* aArgs, + uint32_t aArgCount, + NPVariant* aResult) +{ + if (aObject->_class != GetClass()) { + NS_ERROR("Don't know what kind of object this is!"); + return false; + } + + ParentNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + PPluginIdentifierParent* identifier = GetIdentifier(aObject, aName); + if (!identifier) { + return false; + } + + ProtectedActor actor(object->parent); + if (!actor) { + return false; + } + + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance()); + if (!args.IsOk()) { + NS_ERROR("Failed to convert arguments!"); + return false; + } + + Variant remoteResult; + bool success; + if (!actor->CallInvoke(identifier, args, &remoteResult, + &success)) { + NS_WARNING("Failed to send message!"); + return false; + } + + if (!success) { + return false; + } + + if (!ConvertToVariant(remoteResult, *aResult, actor->GetInstance())) { + NS_WARNING("Failed to convert result!"); + return false; + } + return true; +} + +// static +bool +PluginScriptableObjectParent::ScriptableInvokeDefault(NPObject* aObject, + const NPVariant* aArgs, + uint32_t aArgCount, + NPVariant* aResult) +{ + if (aObject->_class != GetClass()) { + NS_ERROR("Don't know what kind of object this is!"); + return false; + } + + ParentNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + ProtectedActor actor(object->parent); + if (!actor) { + return false; + } + + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance()); + if (!args.IsOk()) { + NS_ERROR("Failed to convert arguments!"); + return false; + } + + Variant remoteResult; + bool success; + if (!actor->CallInvokeDefault(args, &remoteResult, &success)) { + NS_WARNING("Failed to send message!"); + return false; + } + + if (!success) { + return false; + } + + if (!ConvertToVariant(remoteResult, *aResult, actor->GetInstance())) { + NS_WARNING("Failed to convert result!"); + return false; + } + return true; +} + +// static +bool +PluginScriptableObjectParent::ScriptableHasProperty(NPObject* aObject, + NPIdentifier aName) +{ + if (aObject->_class != GetClass()) { + NS_ERROR("Don't know what kind of object this is!"); + return false; + } + + ParentNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + PPluginIdentifierParent* identifier = GetIdentifier(aObject, aName); + if (!identifier) { + return false; + } + + ProtectedActor actor(object->parent); + if (!actor) { + return false; + } + + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + bool result; + if (!actor->CallHasProperty(identifier, &result)) { + NS_WARNING("Failed to send message!"); + return false; + } + + return result; +} + +// static +bool +PluginScriptableObjectParent::ScriptableGetProperty(NPObject* aObject, + NPIdentifier aName, + NPVariant* aResult) +{ + // See GetPropertyHelper below. + NS_NOTREACHED("Shouldn't ever call this directly!"); + return false; +} + +// static +bool +PluginScriptableObjectParent::ScriptableSetProperty(NPObject* aObject, + NPIdentifier aName, + const NPVariant* aValue) +{ + if (aObject->_class != GetClass()) { + NS_ERROR("Don't know what kind of object this is!"); + return false; + } + + ParentNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + PPluginIdentifierParent* identifier = GetIdentifier(aObject, aName); + if (!identifier) { + return false; + } + + ProtectedActor actor(object->parent); + if (!actor) { + return false; + } + + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + ProtectedVariant value(*aValue, actor->GetInstance()); + if (!value.IsOk()) { + NS_WARNING("Failed to convert variant!"); + return false; + } + + bool success; + if (!actor->CallSetProperty(identifier, value, &success)) { + NS_WARNING("Failed to send message!"); + return false; + } + + return success; +} + +// static +bool +PluginScriptableObjectParent::ScriptableRemoveProperty(NPObject* aObject, + NPIdentifier aName) +{ + if (aObject->_class != GetClass()) { + NS_ERROR("Don't know what kind of object this is!"); + return false; + } + + ParentNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + PPluginIdentifierParent* identifier = GetIdentifier(aObject, aName); + if (!identifier) { + return false; + } + + ProtectedActor actor(object->parent); + if (!actor) { + return false; + } + + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + bool success; + if (!actor->CallRemoveProperty(identifier, &success)) { + NS_WARNING("Failed to send message!"); + return false; + } + + return success; +} + +// static +bool +PluginScriptableObjectParent::ScriptableEnumerate(NPObject* aObject, + NPIdentifier** aIdentifiers, + uint32_t* aCount) +{ + if (aObject->_class != GetClass()) { + NS_ERROR("Don't know what kind of object this is!"); + return false; + } + + ParentNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + ProtectedActor actor(object->parent); + if (!actor) { + return false; + } + + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + const NPNetscapeFuncs* npn = GetNetscapeFuncs(aObject); + if (!npn) { + NS_ERROR("No netscape funcs!"); + return false; + } + + nsAutoTArray identifiers; + bool success; + if (!actor->CallEnumerate(&identifiers, &success)) { + NS_WARNING("Failed to send message!"); + return false; + } + + if (!success) { + return false; + } + + *aCount = identifiers.Length(); + if (!*aCount) { + *aIdentifiers = nsnull; + return true; + } + + *aIdentifiers = (NPIdentifier*)npn->memalloc(*aCount * sizeof(NPIdentifier)); + if (!*aIdentifiers) { + NS_ERROR("Out of memory!"); + return false; + } + + for (PRUint32 index = 0; index < *aCount; index++) { + PluginIdentifierParent* id = + static_cast(identifiers[index]); + *aIdentifiers[index] = id->ToNPIdentifier(); + } + return true; +} + +// static +bool +PluginScriptableObjectParent::ScriptableConstruct(NPObject* aObject, + const NPVariant* aArgs, + uint32_t aArgCount, + NPVariant* aResult) +{ + if (aObject->_class != GetClass()) { + NS_ERROR("Don't know what kind of object this is!"); + return false; + } + + ParentNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return false; + } + + ProtectedActor actor(object->parent); + if (!actor) { + return false; + } + + NS_ASSERTION(actor->Type() == Proxy, "Bad type!"); + + ProtectedVariantArray args(aArgs, aArgCount, actor->GetInstance()); + if (!args.IsOk()) { + NS_ERROR("Failed to convert arguments!"); + return false; + } + + Variant remoteResult; + bool success; + if (!actor->CallConstruct(args, &remoteResult, &success)) { + NS_WARNING("Failed to send message!"); + return false; + } + + if (!success) { + return false; + } + + if (!ConvertToVariant(remoteResult, *aResult, actor->GetInstance())) { + NS_WARNING("Failed to convert result!"); + return false; + } + return true; +} + +const NPClass PluginScriptableObjectParent::sNPClass = { + NP_CLASS_STRUCT_VERSION, + PluginScriptableObjectParent::ScriptableAllocate, + PluginScriptableObjectParent::ScriptableDeallocate, + PluginScriptableObjectParent::ScriptableInvalidate, + PluginScriptableObjectParent::ScriptableHasMethod, + PluginScriptableObjectParent::ScriptableInvoke, + PluginScriptableObjectParent::ScriptableInvokeDefault, + PluginScriptableObjectParent::ScriptableHasProperty, + PluginScriptableObjectParent::ScriptableGetProperty, + PluginScriptableObjectParent::ScriptableSetProperty, + PluginScriptableObjectParent::ScriptableRemoveProperty, + PluginScriptableObjectParent::ScriptableEnumerate, + PluginScriptableObjectParent::ScriptableConstruct +}; + +PluginScriptableObjectParent::PluginScriptableObjectParent( + ScriptableObjectType aType) +: mInstance(nsnull), + mObject(nsnull), + mProtectCount(0), + mType(aType) +{ +} + +PluginScriptableObjectParent::~PluginScriptableObjectParent() +{ + if (mObject) { + if (mObject->_class == GetClass()) { + NS_ASSERTION(mType == Proxy, "Wrong type!"); + static_cast(mObject)->parent = nsnull; + } + else { + NS_ASSERTION(mType == LocalObject, "Wrong type!"); + GetInstance()->GetNPNIface()->releaseobject(mObject); + } + } +} + +void +PluginScriptableObjectParent::InitializeProxy() +{ + NS_ASSERTION(mType == Proxy, "Bad type!"); + NS_ASSERTION(!mObject, "Calling Initialize more than once!"); + + mInstance = static_cast(Manager()); + NS_ASSERTION(mInstance, "Null manager?!"); + + NPObject* object = CreateProxyObject(); + NS_ASSERTION(object, "Failed to create object!"); + + if (!mInstance->RegisterNPObjectForActor(object, this)) { + NS_ERROR("Out of memory?"); + } + + mObject = object; +} + +void +PluginScriptableObjectParent::InitializeLocal(NPObject* aObject) +{ + NS_ASSERTION(mType == LocalObject, "Bad type!"); + NS_ASSERTION(!(mInstance && mObject), "Calling Initialize more than once!"); + + mInstance = static_cast(Manager()); + NS_ASSERTION(mInstance, "Null manager?!"); + + mInstance->GetNPNIface()->retainobject(aObject); + + NS_ASSERTION(!mProtectCount, "Should be zero!"); + mProtectCount++; + + if (!mInstance->RegisterNPObjectForActor(aObject, this)) { + NS_ERROR("Out of memory?"); + } + + mObject = aObject; +} + +NPObject* +PluginScriptableObjectParent::CreateProxyObject() +{ + NS_ASSERTION(mInstance, "Must have an instance!"); + NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!"); + + const NPNetscapeFuncs* npn = GetNetscapeFuncs(mInstance); + + NPObject* npobject = npn->createobject(mInstance->GetNPP(), + const_cast(GetClass())); + NS_ASSERTION(npobject, "Failed to create object?!"); + NS_ASSERTION(npobject->_class == GetClass(), "Wrong kind of object!"); + NS_ASSERTION(npobject->referenceCount == 1, "Some kind of live object!"); + + ParentNPObject* object = static_cast(npobject); + NS_ASSERTION(!object->invalidated, "Bad object!"); + NS_ASSERTION(!object->parent, "Bad object!"); + + // We don't want to have the actor own this object but rather let the object + // own this actor. Set the reference count to 0 here so that when the object + // dies we will send the destructor message to the child. + object->referenceCount = 0; + NS_LOG_RELEASE(object, 0, "BrowserNPObject"); + + object->parent = const_cast(this); + return object; +} + +bool +PluginScriptableObjectParent::ResurrectProxyObject() +{ + NS_ASSERTION(mInstance, "Must have an instance already!"); + NS_ASSERTION(!mObject, "Should not have an object already!"); + NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!"); + + InitializeProxy(); + NS_ASSERTION(mObject, "Initialize failed!"); + + if (!SendProtect()) { + NS_WARNING("Failed to send message!"); + return false; + } + + return true; +} + +NPObject* +PluginScriptableObjectParent::GetObject(bool aCanResurrect) +{ + if (!mObject && aCanResurrect && !ResurrectProxyObject()) { + NS_ERROR("Null object!"); + return nsnull; + } + return mObject; +} + +void +PluginScriptableObjectParent::Protect() +{ + NS_ASSERTION(mObject, "No object!"); + NS_ASSERTION(mProtectCount >= 0, "Negative protect count?!"); + + if (mType == LocalObject) { + ++mProtectCount; + } +} + +void +PluginScriptableObjectParent::Unprotect() +{ + NS_ASSERTION(mObject, "No object!"); + NS_ASSERTION(mProtectCount >= 0, "Negative protect count?!"); + + if (mType == LocalObject) { + if (--mProtectCount == 0) { + PluginScriptableObjectParent::Send__delete__(this); + } + } +} + +void +PluginScriptableObjectParent::DropNPObject() +{ + NS_ASSERTION(mObject, "Invalidated object!"); + NS_ASSERTION(mObject->_class == GetClass(), "Wrong type of object!"); + NS_ASSERTION(mType == Proxy, "Shouldn't call this for non-proxy object!"); + + // We think we're about to be deleted, but we could be racing with the other + // process. + PluginInstanceParent* instance = GetInstance(); + NS_ASSERTION(instance, "Must have an instance!"); + + instance->UnregisterNPObject(mObject); + mObject = nsnull; + + (void) SendUnprotect(); +} + +bool +PluginScriptableObjectParent::AnswerHasMethod(PPluginIdentifierParent* aId, + bool* aHasMethod) +{ + if (!mObject) { + NS_WARNING("Calling AnswerHasMethod with an invalidated object!"); + *aHasMethod = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + PluginInstanceParent* instance = GetInstance(); + if (!instance) { + NS_ERROR("No instance?!"); + *aHasMethod = false; + return true; + } + + const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance); + if (!npn) { + NS_ERROR("No netscape funcs?!"); + *aHasMethod = false; + return true; + } + + PluginIdentifierParent* id = static_cast(aId); + *aHasMethod = npn->hasmethod(instance->GetNPP(), mObject, id->ToNPIdentifier()); + return true; +} + +bool +PluginScriptableObjectParent::AnswerInvoke(PPluginIdentifierParent* aId, + const nsTArray& aArgs, + Variant* aResult, + bool* aSuccess) +{ + if (!mObject) { + NS_WARNING("Calling AnswerInvoke with an invalidated object!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + PluginInstanceParent* instance = GetInstance(); + if (!instance) { + NS_ERROR("No instance?!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance); + if (!npn) { + NS_ERROR("No netscape funcs?!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + nsAutoTArray convertedArgs; + PRUint32 argCount = aArgs.Length(); + + if (!convertedArgs.SetLength(argCount)) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + for (PRUint32 index = 0; index < argCount; index++) { + if (!ConvertToVariant(aArgs[index], convertedArgs[index], instance)) { + // Don't leak things we've already converted! + while (index-- > 0) { + ReleaseVariant(convertedArgs[index], instance); + } + *aResult = void_t(); + *aSuccess = false; + return true; + } + } + + PluginIdentifierParent* id = static_cast(aId); + NPVariant result; + bool success = npn->invoke(instance->GetNPP(), mObject, id->ToNPIdentifier(), + convertedArgs.Elements(), argCount, &result); + + for (PRUint32 index = 0; index < argCount; index++) { + ReleaseVariant(convertedArgs[index], instance); + } + + if (!success) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + Variant convertedResult; + success = ConvertToRemoteVariant(result, convertedResult, GetInstance()); + + DeferNPVariantLastRelease(npn, &result); + + if (!success) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + *aResult = convertedResult; + *aSuccess = true; + return true; +} + +bool +PluginScriptableObjectParent::AnswerInvokeDefault(const nsTArray& aArgs, + Variant* aResult, + bool* aSuccess) +{ + if (!mObject) { + NS_WARNING("Calling AnswerInvoke with an invalidated object!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + PluginInstanceParent* instance = GetInstance(); + if (!instance) { + NS_ERROR("No instance?!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance); + if (!npn) { + NS_ERROR("No netscape funcs?!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + nsAutoTArray convertedArgs; + PRUint32 argCount = aArgs.Length(); + + if (!convertedArgs.SetLength(argCount)) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + for (PRUint32 index = 0; index < argCount; index++) { + if (!ConvertToVariant(aArgs[index], convertedArgs[index], instance)) { + // Don't leak things we've already converted! + while (index-- > 0) { + ReleaseVariant(convertedArgs[index], instance); + } + *aResult = void_t(); + *aSuccess = false; + return true; + } + } + + NPVariant result; + bool success = npn->invokeDefault(instance->GetNPP(), mObject, + convertedArgs.Elements(), argCount, + &result); + + for (PRUint32 index = 0; index < argCount; index++) { + ReleaseVariant(convertedArgs[index], instance); + } + + if (!success) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + Variant convertedResult; + success = ConvertToRemoteVariant(result, convertedResult, GetInstance()); + + DeferNPVariantLastRelease(npn, &result); + + if (!success) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + *aResult = convertedResult; + *aSuccess = true; + return true; +} + +bool +PluginScriptableObjectParent::AnswerHasProperty(PPluginIdentifierParent* aId, + bool* aHasProperty) +{ + if (!mObject) { + NS_WARNING("Calling AnswerHasProperty with an invalidated object!"); + *aHasProperty = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + PluginInstanceParent* instance = GetInstance(); + if (!instance) { + NS_ERROR("No instance?!"); + *aHasProperty = false; + return true; + } + + const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance); + if (!npn) { + NS_ERROR("No netscape funcs?!"); + *aHasProperty = false; + return true; + } + + PluginIdentifierParent* id = static_cast(aId); + *aHasProperty = npn->hasproperty(instance->GetNPP(), mObject, + id->ToNPIdentifier()); + return true; +} + +bool +PluginScriptableObjectParent::AnswerGetParentProperty( + PPluginIdentifierParent* aId, + Variant* aResult, + bool* aSuccess) +{ + if (!mObject) { + NS_WARNING("Calling AnswerGetProperty with an invalidated object!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + PluginInstanceParent* instance = GetInstance(); + if (!instance) { + NS_ERROR("No instance?!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance); + if (!npn) { + NS_ERROR("No netscape funcs?!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + PluginIdentifierParent* id = static_cast(aId); + NPVariant result; + if (!npn->getproperty(instance->GetNPP(), mObject, id->ToNPIdentifier(), + &result)) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + Variant converted; + if ((*aSuccess = ConvertToRemoteVariant(result, converted, instance))) { + DeferNPVariantLastRelease(npn, &result); + *aResult = converted; + } + else { + *aResult = void_t(); + } + + return true; +} + +bool +PluginScriptableObjectParent::AnswerSetProperty(PPluginIdentifierParent* aId, + const Variant& aValue, + bool* aSuccess) +{ + if (!mObject) { + NS_WARNING("Calling AnswerSetProperty with an invalidated object!"); + *aSuccess = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + PluginInstanceParent* instance = GetInstance(); + if (!instance) { + NS_ERROR("No instance?!"); + *aSuccess = false; + return true; + } + + const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance); + if (!npn) { + NS_ERROR("No netscape funcs?!"); + *aSuccess = false; + return true; + } + + NPVariant converted; + if (!ConvertToVariant(aValue, converted, instance)) { + *aSuccess = false; + return true; + } + + PluginIdentifierParent* id = static_cast(aId); + if ((*aSuccess = npn->setproperty(instance->GetNPP(), mObject, + id->ToNPIdentifier(), &converted))) { + ReleaseVariant(converted, instance); + } + return true; +} + +bool +PluginScriptableObjectParent::AnswerRemoveProperty(PPluginIdentifierParent* aId, + bool* aSuccess) +{ + if (!mObject) { + NS_WARNING("Calling AnswerRemoveProperty with an invalidated object!"); + *aSuccess = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + PluginInstanceParent* instance = GetInstance(); + if (!instance) { + NS_ERROR("No instance?!"); + *aSuccess = false; + return true; + } + + const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance); + if (!npn) { + NS_ERROR("No netscape funcs?!"); + *aSuccess = false; + return true; + } + + PluginIdentifierParent* id = static_cast(aId); + *aSuccess = npn->removeproperty(instance->GetNPP(), mObject, + id->ToNPIdentifier()); + return true; +} + +bool +PluginScriptableObjectParent::AnswerEnumerate(nsTArray* aProperties, + bool* aSuccess) +{ + if (!mObject) { + NS_WARNING("Calling AnswerEnumerate with an invalidated object!"); + *aSuccess = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + PluginInstanceParent* instance = GetInstance(); + if (!instance) { + NS_ERROR("No instance?!"); + *aSuccess = false; + return true; + } + + const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance); + if (!npn) { + NS_WARNING("No netscape funcs?!"); + *aSuccess = false; + return true; + } + + NPIdentifier* ids; + uint32_t idCount; + if (!npn->enumerate(instance->GetNPP(), mObject, &ids, &idCount)) { + *aSuccess = false; + return true; + } + + if (!aProperties->SetCapacity(idCount)) { + npn->memfree(ids); + *aSuccess = false; + return true; + } + + for (uint32_t index = 0; index < idCount; index++) { + aProperties->AppendElement(GetIdentifier(instance, ids[index])); + } + + npn->memfree(ids); + *aSuccess = true; + return true; +} + +bool +PluginScriptableObjectParent::AnswerConstruct(const nsTArray& aArgs, + Variant* aResult, + bool* aSuccess) +{ + if (!mObject) { + NS_WARNING("Calling AnswerConstruct with an invalidated object!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + PluginInstanceParent* instance = GetInstance(); + if (!instance) { + NS_ERROR("No instance?!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance); + if (!npn) { + NS_ERROR("No netscape funcs?!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + nsAutoTArray convertedArgs; + PRUint32 argCount = aArgs.Length(); + + if (!convertedArgs.SetLength(argCount)) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + for (PRUint32 index = 0; index < argCount; index++) { + if (!ConvertToVariant(aArgs[index], convertedArgs[index], instance)) { + // Don't leak things we've already converted! + while (index-- > 0) { + ReleaseVariant(convertedArgs[index], instance); + } + *aResult = void_t(); + *aSuccess = false; + return true; + } + } + + NPVariant result; + bool success = npn->construct(instance->GetNPP(), mObject, + convertedArgs.Elements(), argCount, &result); + + for (PRUint32 index = 0; index < argCount; index++) { + ReleaseVariant(convertedArgs[index], instance); + } + + if (!success) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + Variant convertedResult; + success = ConvertToRemoteVariant(result, convertedResult, instance); + + DeferNPVariantLastRelease(npn, &result); + + if (!success) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + *aSuccess = true; + *aResult = convertedResult; + return true; +} + +bool +PluginScriptableObjectParent::RecvProtect() +{ + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + Protect(); + return true; +} + +bool +PluginScriptableObjectParent::RecvUnprotect() +{ + NS_ASSERTION(mObject->_class != GetClass(), "Bad object type!"); + NS_ASSERTION(mType == LocalObject, "Bad type!"); + + Unprotect(); + return true; +} + +bool +PluginScriptableObjectParent::AnswerNPN_Evaluate(const nsCString& aScript, + Variant* aResult, + bool* aSuccess) +{ + PluginInstanceParent* instance = GetInstance(); + if (!instance) { + NS_ERROR("No instance?!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + const NPNetscapeFuncs* npn = GetNetscapeFuncs(instance); + if (!npn) { + NS_ERROR("No netscape funcs?!"); + *aResult = void_t(); + *aSuccess = false; + return true; + } + + NPString script = { aScript.get(), aScript.Length() }; + + NPVariant result; + bool success = npn->evaluate(instance->GetNPP(), mObject, &script, &result); + if (!success) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + Variant convertedResult; + success = ConvertToRemoteVariant(result, convertedResult, instance); + + DeferNPVariantLastRelease(npn, &result); + + if (!success) { + *aResult = void_t(); + *aSuccess = false; + return true; + } + + *aSuccess = true; + *aResult = convertedResult; + return true; +} + +JSBool +PluginScriptableObjectParent::GetPropertyHelper(NPIdentifier aName, + PRBool* aHasProperty, + PRBool* aHasMethod, + NPVariant* aResult) +{ + NS_ASSERTION(Type() == Proxy, "Bad type!"); + + ParentNPObject* object = static_cast(mObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return JS_FALSE; + } + + PPluginIdentifierParent* identifier = GetIdentifier(GetInstance(), aName); + if (!identifier) { + return JS_FALSE; + } + + bool hasProperty, hasMethod, success; + Variant result; + if (!CallGetChildProperty(identifier, &hasProperty, &hasMethod, &result, + &success)) { + return JS_FALSE; + } + + if (!success) { + return JS_FALSE; + } + + if (!ConvertToVariant(result, *aResult, GetInstance())) { + NS_WARNING("Failed to convert result!"); + return JS_FALSE; + } + + *aHasProperty = hasProperty; + *aHasMethod = hasMethod; + return JS_TRUE; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginScriptableObjectParent.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginScriptableObjectParent.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginScriptableObjectParent.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginScriptableObjectParent.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,258 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Ben Turner + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef dom_plugins_PluginScriptableObjectParent_h +#define dom_plugins_PluginScriptableObjectParent_h 1 + +#include "mozilla/plugins/PPluginScriptableObjectParent.h" + +#include "jsapi.h" +#include "npfunctions.h" +#include "npruntime.h" + +namespace mozilla { +namespace plugins { + +class PluginInstanceParent; +class PluginScriptableObjectParent; +class PPluginIdentifierParent; + +struct ParentNPObject : NPObject +{ + ParentNPObject() + : NPObject(), parent(NULL), invalidated(false) { } + + // |parent| is always valid as long as the actor is alive. Once the actor is + // destroyed this will be set to null. + PluginScriptableObjectParent* parent; + bool invalidated; +}; + +class PluginScriptableObjectParent : public PPluginScriptableObjectParent +{ + friend class PluginInstanceParent; + +public: + PluginScriptableObjectParent(ScriptableObjectType aType); + virtual ~PluginScriptableObjectParent(); + + void + InitializeProxy(); + + void + InitializeLocal(NPObject* aObject); + + virtual bool + AnswerHasMethod(PPluginIdentifierParent* aId, + bool* aHasMethod); + + virtual bool + AnswerInvoke(PPluginIdentifierParent* aId, + const nsTArray& aArgs, + Variant* aResult, + bool* aSuccess); + + virtual bool + AnswerInvokeDefault(const nsTArray& aArgs, + Variant* aResult, + bool* aSuccess); + + virtual bool + AnswerHasProperty(PPluginIdentifierParent* aId, + bool* aHasProperty); + + virtual bool + AnswerGetParentProperty(PPluginIdentifierParent* aId, + Variant* aResult, + bool* aSuccess); + + virtual bool + AnswerSetProperty(PPluginIdentifierParent* aId, + const Variant& aValue, + bool* aSuccess); + + virtual bool + AnswerRemoveProperty(PPluginIdentifierParent* aId, + bool* aSuccess); + + virtual bool + AnswerEnumerate(nsTArray* aProperties, + bool* aSuccess); + + virtual bool + AnswerConstruct(const nsTArray& aArgs, + Variant* aResult, + bool* aSuccess); + + virtual bool + AnswerNPN_Evaluate(const nsCString& aScript, + Variant* aResult, + bool* aSuccess); + + virtual bool + RecvProtect(); + + virtual bool + RecvUnprotect(); + + static const NPClass* + GetClass() + { + return &sNPClass; + } + + PluginInstanceParent* + GetInstance() const + { + return mInstance; + } + + NPObject* + GetObject(bool aCanResurrect); + + // Protect only affects LocalObject actors. It is called by the + // ProtectedVariant/Actor helper classes before the actor is used as an + // argument to an IPC call and when the child process resurrects a + // proxy object to the NPObject associated with this actor. + void Protect(); + + // Unprotect only affects LocalObject actors. It is called by the + // ProtectedVariant/Actor helper classes after the actor is used as an + // argument to an IPC call and when the child process is no longer using this + // actor. + void Unprotect(); + + // DropNPObject is only used for Proxy actors and is called when the parent + // process is no longer using the NPObject associated with this actor. The + // child process may subsequently use this actor again in which case a new + // NPObject will be created and associated with this actor (see + // ResurrectProxyObject). + void DropNPObject(); + + ScriptableObjectType + Type() const { + return mType; + } + + JSBool GetPropertyHelper(NPIdentifier aName, + PRBool* aHasProperty, + PRBool* aHasMethod, + NPVariant* aResult); + +private: + static NPObject* + ScriptableAllocate(NPP aInstance, + NPClass* aClass); + + static void + ScriptableInvalidate(NPObject* aObject); + + static void + ScriptableDeallocate(NPObject* aObject); + + static bool + ScriptableHasMethod(NPObject* aObject, + NPIdentifier aName); + + static bool + ScriptableInvoke(NPObject* aObject, + NPIdentifier aName, + const NPVariant* aArgs, + uint32_t aArgCount, + NPVariant* aResult); + + static bool + ScriptableInvokeDefault(NPObject* aObject, + const NPVariant* aArgs, + uint32_t aArgCount, + NPVariant* aResult); + + static bool + ScriptableHasProperty(NPObject* aObject, + NPIdentifier aName); + + static bool + ScriptableGetProperty(NPObject* aObject, + NPIdentifier aName, + NPVariant* aResult); + + static bool + ScriptableSetProperty(NPObject* aObject, + NPIdentifier aName, + const NPVariant* aValue); + + static bool + ScriptableRemoveProperty(NPObject* aObject, + NPIdentifier aName); + + static bool + ScriptableEnumerate(NPObject* aObject, + NPIdentifier** aIdentifiers, + uint32_t* aCount); + + static bool + ScriptableConstruct(NPObject* aObject, + const NPVariant* aArgs, + uint32_t aArgCount, + NPVariant* aResult); + + NPObject* + CreateProxyObject(); + + // ResurrectProxyObject is only used with Proxy actors. It is called when the + // child process uses an actor whose NPObject was deleted by the parent + // process. + bool ResurrectProxyObject(); + +private: + PluginInstanceParent* mInstance; + + // This may be a ParentNPObject or some other kind depending on who created + // it. Have to check its class to find out. + NPObject* mObject; + int mProtectCount; + + ScriptableObjectType mType; + + static const NPClass sNPClass; +}; + +} /* namespace plugins */ +} /* namespace mozilla */ + +#endif /* dom_plugins_PluginScriptableObjectParent_h */ diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginScriptableObjectUtils.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginScriptableObjectUtils.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginScriptableObjectUtils.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginScriptableObjectUtils.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,322 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Turner + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef dom_plugins_PluginScriptableObjectUtils_h +#define dom_plugins_PluginScriptableObjectUtils_h + +#include "PluginModuleParent.h" +#include "PluginModuleChild.h" +#include "PluginInstanceParent.h" +#include "PluginInstanceChild.h" +#include "PluginScriptableObjectParent.h" +#include "PluginScriptableObjectChild.h" + +#include "npapi.h" +#include "npfunctions.h" +#include "npruntime.h" + +#include "nsDebug.h" + +namespace mozilla { +namespace plugins { + +inline PluginInstanceParent* +GetInstance(NPObject* aObject) +{ + NS_ASSERTION(aObject->_class == PluginScriptableObjectParent::GetClass(), + "Bad class!"); + + ParentNPObject* object = reinterpret_cast(aObject); + if (object->invalidated) { + NS_WARNING("Calling method on an invalidated object!"); + return nsnull; + } + if (!object->parent) { + return nsnull; + } + return object->parent->GetInstance(); +} + +inline NPObject* +NPObjectFromVariant(const Variant& aRemoteVariant) +{ + switch (aRemoteVariant.type()) { + case Variant::TPPluginScriptableObjectParent: { + PluginScriptableObjectParent* actor = + const_cast( + reinterpret_cast( + aRemoteVariant.get_PPluginScriptableObjectParent())); + return actor->GetObject(true); + } + + case Variant::TPPluginScriptableObjectChild: { + PluginScriptableObjectChild* actor = + const_cast( + reinterpret_cast( + aRemoteVariant.get_PPluginScriptableObjectChild())); + return actor->GetObject(true); + } + + default: + NS_NOTREACHED("Shouldn't get here!"); + return nsnull; + } +} + +inline NPObject* +NPObjectFromVariant(const NPVariant& aVariant) +{ + NS_ASSERTION(NPVARIANT_IS_OBJECT(aVariant), "Wrong variant type!"); + return NPVARIANT_TO_OBJECT(aVariant); +} + +inline const NPNetscapeFuncs* +GetNetscapeFuncs(PluginInstanceParent* aInstance) +{ + PluginModuleParent* module = aInstance->Module(); + if (!module) { + NS_WARNING("Null module?!"); + return nsnull; + } + return module->GetNetscapeFuncs(); +} + +inline const NPNetscapeFuncs* +GetNetscapeFuncs(NPObject* aObject) +{ + NS_ASSERTION(aObject->_class == PluginScriptableObjectParent::GetClass(), + "Bad class!"); + + PluginInstanceParent* instance = GetInstance(aObject); + if (!instance) { + return nsnull; + } + + return GetNetscapeFuncs(instance); +} + +inline void +ReleaseRemoteVariant(Variant& aVariant) +{ + switch (aVariant.type()) { + case Variant::TPPluginScriptableObjectParent: { + PluginScriptableObjectParent* actor = + const_cast( + reinterpret_cast( + aVariant.get_PPluginScriptableObjectParent())); + actor->Unprotect(); + break; + } + + case Variant::TPPluginScriptableObjectChild: { + NS_ASSERTION(PluginModuleChild::current(), + "Should only be running in the child!"); + PluginScriptableObjectChild* actor = + const_cast( + reinterpret_cast( + aVariant.get_PPluginScriptableObjectChild())); + actor->Unprotect(); + break; + } + + default: + break; // Intentional fall-through for other variant types. + } + + aVariant = mozilla::void_t(); +} + +bool +ConvertToVariant(const Variant& aRemoteVariant, + NPVariant& aVariant, + PluginInstanceParent* aInstance = nsnull); + +template +bool +ConvertToRemoteVariant(const NPVariant& aVariant, + Variant& aRemoteVariant, + InstanceType* aInstance, + bool aProtectActors = false); + +class ProtectedVariant +{ +public: + ProtectedVariant(const NPVariant& aVariant, + PluginInstanceParent* aInstance) + { + mOk = ConvertToRemoteVariant(aVariant, mVariant, aInstance, true); + } + + ProtectedVariant(const NPVariant& aVariant, + PluginInstanceChild* aInstance) + { + mOk = ConvertToRemoteVariant(aVariant, mVariant, aInstance, true); + } + + ~ProtectedVariant() { + ReleaseRemoteVariant(mVariant); + } + + PRBool IsOk() { + return mOk; + } + + operator const Variant&() { + return mVariant; + } + +private: + Variant mVariant; + bool mOk; +}; + +class ProtectedVariantArray +{ +public: + ProtectedVariantArray(const NPVariant* aArgs, + PRUint32 aCount, + PluginInstanceParent* aInstance) + { + for (PRUint32 index = 0; index < aCount; index++) { + Variant* remoteVariant = mArray.AppendElement(); + if (!(remoteVariant && + ConvertToRemoteVariant(aArgs[index], *remoteVariant, aInstance, + true))) { + mOk = false; + return; + } + } + mOk = true; + } + + ProtectedVariantArray(const NPVariant* aArgs, + PRUint32 aCount, + PluginInstanceChild* aInstance) + { + for (PRUint32 index = 0; index < aCount; index++) { + Variant* remoteVariant = mArray.AppendElement(); + if (!(remoteVariant && + ConvertToRemoteVariant(aArgs[index], *remoteVariant, aInstance, + true))) { + mOk = false; + return; + } + } + mOk = true; + } + + ~ProtectedVariantArray() + { + PRUint32 count = mArray.Length(); + for (PRUint32 index = 0; index < count; index++) { + ReleaseRemoteVariant(mArray[index]); + } + } + + operator const nsTArray&() + { + return mArray; + } + + bool IsOk() + { + return mOk; + } + +private: + nsTArray mArray; + bool mOk; +}; + +template +struct ProtectedActorTraits +{ + static bool Nullable(); +}; + +template > +class ProtectedActor +{ +public: + ProtectedActor(ActorType* aActor) : mActor(aActor) + { + if (!Traits::Nullable()) { + NS_ASSERTION(mActor, "This should never be null!"); + } + } + + ~ProtectedActor() + { + if (Traits::Nullable() && !mActor) + return; + mActor->Unprotect(); + } + + ActorType* operator->() + { + return mActor; + } + + operator bool() + { + return !!mActor; + } + +private: + ActorType* mActor; +}; + +template<> +struct ProtectedActorTraits +{ + static bool Nullable() { return true; } +}; + +template<> +struct ProtectedActorTraits +{ + static bool Nullable() { return false; } +}; + +} /* namespace plugins */ +} /* namespace mozilla */ + +#include "PluginScriptableObjectUtils-inl.h" + +#endif /* dom_plugins_PluginScriptableObjectUtils_h */ diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginScriptableObjectUtils-inl.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginScriptableObjectUtils-inl.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginScriptableObjectUtils-inl.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginScriptableObjectUtils-inl.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,195 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Turner + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "PluginScriptableObjectUtils.h" + +namespace { + +template +class VariantTraits; + +template<> +class VariantTraits +{ +public: + typedef mozilla::plugins::PluginScriptableObjectParent ScriptableObjectType; +}; + +template<> +class VariantTraits +{ +public: + typedef mozilla::plugins::PluginScriptableObjectChild ScriptableObjectType; +}; + +} /* anonymous namespace */ + +inline bool +mozilla::plugins::ConvertToVariant(const Variant& aRemoteVariant, + NPVariant& aVariant, + PluginInstanceParent* aInstance) +{ + switch (aRemoteVariant.type()) { + case Variant::Tvoid_t: { + VOID_TO_NPVARIANT(aVariant); + break; + } + + case Variant::Tnull_t: { + NULL_TO_NPVARIANT(aVariant); + break; + } + + case Variant::Tbool: { + BOOLEAN_TO_NPVARIANT(aRemoteVariant.get_bool(), aVariant); + break; + } + + case Variant::Tint: { + INT32_TO_NPVARIANT(aRemoteVariant.get_int(), aVariant); + break; + } + + case Variant::Tdouble: { + DOUBLE_TO_NPVARIANT(aRemoteVariant.get_double(), aVariant); + break; + } + + case Variant::TnsCString: { + const nsCString& string = aRemoteVariant.get_nsCString(); + NPUTF8* buffer = reinterpret_cast(strdup(string.get())); + if (!buffer) { + NS_ERROR("Out of memory!"); + return false; + } + STRINGN_TO_NPVARIANT(buffer, string.Length(), aVariant); + break; + } + + case Variant::TPPluginScriptableObjectParent: { + NS_ASSERTION(aInstance, "Must have an instance!"); + NPObject* object = NPObjectFromVariant(aRemoteVariant); + if (!object) { + NS_ERROR("Er, this shouldn't fail!"); + return false; + } + + const NPNetscapeFuncs* npn = GetNetscapeFuncs(aInstance); + if (!npn) { + NS_ERROR("Null netscape funcs!"); + return false; + } + + npn->retainobject(object); + OBJECT_TO_NPVARIANT(object, aVariant); + break; + } + + case Variant::TPPluginScriptableObjectChild: { + NS_ASSERTION(!aInstance, "No instance should be given!"); + NS_ASSERTION(PluginModuleChild::current(), + "Should be running on child only!"); + + NPObject* object = NPObjectFromVariant(aRemoteVariant); + NS_ASSERTION(object, "Null object?!"); + + PluginModuleChild::sBrowserFuncs.retainobject(object); + OBJECT_TO_NPVARIANT(object, aVariant); + break; + } + + default: + NS_NOTREACHED("Shouldn't get here!"); + return false; + } + + return true; +} + +template +bool +mozilla::plugins::ConvertToRemoteVariant(const NPVariant& aVariant, + Variant& aRemoteVariant, + InstanceType* aInstance, + bool aProtectActors) +{ + if (NPVARIANT_IS_VOID(aVariant)) { + aRemoteVariant = mozilla::void_t(); + } + else if (NPVARIANT_IS_NULL(aVariant)) { + aRemoteVariant = mozilla::null_t(); + } + else if (NPVARIANT_IS_BOOLEAN(aVariant)) { + aRemoteVariant = NPVARIANT_TO_BOOLEAN(aVariant); + } + else if (NPVARIANT_IS_INT32(aVariant)) { + aRemoteVariant = NPVARIANT_TO_INT32(aVariant); + } + else if (NPVARIANT_IS_DOUBLE(aVariant)) { + aRemoteVariant = NPVARIANT_TO_DOUBLE(aVariant); + } + else if (NPVARIANT_IS_STRING(aVariant)) { + NPString str = NPVARIANT_TO_STRING(aVariant); + nsCString string(str.UTF8Characters, str.UTF8Length); + aRemoteVariant = string; + } + else if (NPVARIANT_IS_OBJECT(aVariant)) { + NPObject* object = NPVARIANT_TO_OBJECT(aVariant); + + typename VariantTraits::ScriptableObjectType* actor = + aInstance->GetActorForNPObject(object); + + if (!actor) { + NS_ERROR("Null actor!"); + return false; + } + + if (aProtectActors) { + actor->Protect(); + } + + aRemoteVariant = actor; + } + else { + NS_NOTREACHED("Shouldn't get here!"); + return false; + } + + return true; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginStreamChild.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginStreamChild.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginStreamChild.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginStreamChild.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,95 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugins. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "PluginStreamChild.h" +#include "mozilla/plugins/PluginInstanceChild.h" + +namespace mozilla { +namespace plugins { + +PluginStreamChild::PluginStreamChild() +{ + memset(&mStream, 0, sizeof(mStream)); + mStream.ndata = static_cast(this); +} + +bool +PluginStreamChild::Answer__delete__(const NPReason& reason, + const bool& artificial) +{ + AssertPluginThread(); + if (!artificial) + NPP_DestroyStream(reason); + return true; +} + +int32_t +PluginStreamChild::NPN_Write(int32_t length, void* buffer) +{ + AssertPluginThread(); + + int32_t written = 0; + CallNPN_Write(nsCString(static_cast(buffer), length), + &written); + if (written < 0) + PPluginStreamChild::Call__delete__(this, NPERR_GENERIC_ERROR, true); + // careful after here! |this| just got deleted + + return written; +} + +void +PluginStreamChild::NPP_DestroyStream(NPError reason) +{ + AssertPluginThread(); + + if (mClosed) + return; + + mClosed = true; + Instance()->mPluginIface->destroystream( + &Instance()->mData, &mStream, reason); +} + +PluginInstanceChild* +PluginStreamChild::Instance() +{ + return static_cast(Manager()); +} + +} // namespace plugins +} // namespace mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginStreamChild.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginStreamChild.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginStreamChild.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginStreamChild.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,87 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugins. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_plugins_PluginStreamChild_h +#define mozilla_plugins_PluginStreamChild_h + +#include "mozilla/plugins/PPluginStreamChild.h" +#include "mozilla/plugins/AStream.h" + +namespace mozilla { +namespace plugins { + +class PluginInstanceChild; + +class PluginStreamChild : public PPluginStreamChild, public AStream +{ + friend class PluginInstanceChild; + +public: + PluginStreamChild(); + virtual ~PluginStreamChild() { } + + NS_OVERRIDE virtual bool IsBrowserStream() { return false; } + + virtual bool Answer__delete__(const NPReason& reason, + const bool& artificial); + + int32_t NPN_Write(int32_t length, void* buffer); + void NPP_DestroyStream(NPError reason); + + void EnsureCorrectInstance(PluginInstanceChild* i) + { + if (i != Instance()) + NS_RUNTIMEABORT("Incorrect stream instance"); + } + void EnsureCorrectStream(NPStream* s) + { + if (s != &mStream) + NS_RUNTIMEABORT("Incorrect stream data"); + } + +private: + PluginInstanceChild* Instance(); + + NPStream mStream; + bool mClosed; +}; + + +} // namespace plugins +} // namespace mozilla + +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginStreamParent.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginStreamParent.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginStreamParent.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginStreamParent.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,98 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugins. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "PluginStreamParent.h" +#include "PluginInstanceParent.h" + +namespace mozilla { +namespace plugins { + +PluginStreamParent::PluginStreamParent(PluginInstanceParent* npp, + const nsCString& mimeType, + const nsCString& target, + NPError* result) + : mInstance(npp) + , mClosed(false) +{ + *result = mInstance->mNPNIface->newstream(mInstance->mNPP, + const_cast(mimeType.get()), + NullableStringGet(target), + &mStream); + if (*result == NPERR_NO_ERROR) + mStream->pdata = static_cast(this); + else + mStream = NULL; +} + +bool +PluginStreamParent::AnswerNPN_Write(const Buffer& data, int32_t* written) +{ + if (mClosed) { + *written = -1; + return true; + } + + *written = mInstance->mNPNIface->write(mInstance->mNPP, mStream, + data.Length(), + const_cast(data.get())); + if (*written < 0) + mClosed = true; + + return true; +} + +bool +PluginStreamParent::Answer__delete__(const NPError& reason, + const bool& artificial) +{ + if (!artificial) + this->NPN_DestroyStream(reason); + return true; +} + +void +PluginStreamParent::NPN_DestroyStream(NPReason reason) +{ + if (mClosed) + return; + + mInstance->mNPNIface->destroystream(mInstance->mNPP, mStream, reason); + mClosed = true; +} + +} // namespace plugins +} // namespace mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginStreamParent.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginStreamParent.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginStreamParent.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginStreamParent.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,76 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugins. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_plugins_PluginStreamParent_h +#define mozilla_plugins_PluginStreamParent_h + +#include "mozilla/plugins/PPluginStreamParent.h" +#include "mozilla/plugins/AStream.h" + +namespace mozilla { +namespace plugins { + +class PluginInstanceParent; + +class PluginStreamParent : public PPluginStreamParent, public AStream +{ + friend class PluginModuleParent; + friend class PluginInstanceParent; + +public: + PluginStreamParent(PluginInstanceParent* npp, const nsCString& mimeType, + const nsCString& target, NPError* result); + virtual ~PluginStreamParent() { } + + NS_OVERRIDE virtual bool IsBrowserStream() { return false; } + + virtual bool AnswerNPN_Write(const Buffer& data, int32_t* written); + + virtual bool Answer__delete__(const NPError& reason, const bool& artificial); + +private: + void NPN_DestroyStream(NPReason reason); + + PluginInstanceParent* mInstance; + NPStream* mStream; + bool mClosed; +}; + +} // namespace plugins +} // namespace mozilla + +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginThreadChild.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginThreadChild.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginThreadChild.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginThreadChild.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,119 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Ben Turner . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "mozilla/plugins/PluginThreadChild.h" + +#include "prlink.h" + +#include "base/command_line.h" +#include "base/string_util.h" +#include "chrome/common/child_process.h" +#include "chrome/common/chrome_switches.h" + +using mozilla::ipc::MozillaChildThread; + +namespace mozilla { +namespace plugins { + +PluginThreadChild::PluginThreadChild(ProcessHandle aParentHandle) : + MozillaChildThread(aParentHandle, MessageLoop::TYPE_UI) +{ + NS_ASSERTION(!gInstance, "Two PluginThreadChild?"); + gInstance = this; +} + +PluginThreadChild::~PluginThreadChild() +{ + gInstance = NULL; +} + +PluginThreadChild* PluginThreadChild::gInstance; + +void +PluginThreadChild::Init() +{ + MozillaChildThread::Init(); + + std::string pluginFilename; + +#if defined(OS_POSIX) + // NB: need to be very careful in ensuring that the first arg + // (after the binary name) here is indeed the plugin module path. + // Keep in sync with dom/plugins/PluginModuleParent. + std::vector values = CommandLine::ForCurrentProcess()->argv(); + NS_ABORT_IF_FALSE(values.size() >= 2, "not enough args"); + + pluginFilename = values[1]; + +#elif defined(OS_WIN) + std::vector values = + CommandLine::ForCurrentProcess()->GetLooseValues(); + NS_ABORT_IF_FALSE(values.size() >= 1, "not enough loose args"); + + pluginFilename = WideToUTF8(values[0]); + +#else +# error Sorry +#endif + + // FIXME owner_loop() is bad here + mPlugin.Init(pluginFilename, + GetParentProcessHandle(), owner_loop(), channel()); +} + +void +PluginThreadChild::CleanUp() +{ + mPlugin.CleanUp(); + MozillaChildThread::CleanUp(); +} + +/* static */ +void +PluginThreadChild::AppendNotesToCrashReport(const nsCString& aNotes) +{ + AssertPluginThread(); + + if (gInstance) { + gInstance->mPlugin.SendAppendNotesToCrashReport(aNotes); + } +} + +} // namespace plugins +} // namespace mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginThreadChild.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginThreadChild.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PluginThreadChild.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PluginThreadChild.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Ben Turner . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef dom_plugins_PluginThreadChild_h +#define dom_plugins_PluginThreadChild_h 1 + +#include "base/basictypes.h" + +#include "mozilla/ipc/MozillaChildThread.h" +#include "base/file_path.h" +#include "base/process.h" + +#include "mozilla/plugins/PluginModuleChild.h" + +namespace mozilla { +namespace plugins { +//----------------------------------------------------------------------------- + +// The PluginThreadChild class represents a background thread where plugin instances +// live. +class PluginThreadChild : public mozilla::ipc::MozillaChildThread { +public: + PluginThreadChild(ProcessHandle aParentHandle); + ~PluginThreadChild(); + + static PluginThreadChild* current() { + return gInstance; + } + + // For use on the plugin thread. + static void AppendNotesToCrashReport(const nsCString& aNotes); + +private: + static PluginThreadChild* gInstance; + + // Thread implementation: + virtual void Init(); + virtual void CleanUp(); + + PluginModuleChild mPlugin; + IPC::Channel* mChannel; + + DISALLOW_EVIL_CONSTRUCTORS(PluginThreadChild); +}; + +} // namespace plugins +} // namespace mozilla + +#endif // ifndef dom_plugins_PluginThreadChild_h diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PPluginIdentifier.ipdl firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PPluginIdentifier.ipdl --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PPluginIdentifier.ipdl 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PPluginIdentifier.ipdl 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,56 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugins. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Turner + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +include protocol "PPluginModule.ipdl"; + +namespace mozilla { +namespace plugins { + +/** + * Represents an NPIdentifier that wraps either a string or an integer. + */ +async protocol PPluginIdentifier +{ + manager PPluginModule; + +child: + async __delete__(); +}; + +} // namespace plugins +} // namespace mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PPluginInstance.ipdl firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PPluginInstance.ipdl --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PPluginInstance.ipdl 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PPluginInstance.ipdl 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,174 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugins. + * + * The Initial Developer of the Original Code is + * Chris Jones . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Benjamin Smedberg + * Ben Turner + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +include protocol "PPluginModule.ipdl"; +include protocol "PPluginScriptableObject.ipdl"; +include protocol "PBrowserStream.ipdl"; +include protocol "PPluginStream.ipdl"; +include protocol "PStreamNotify.ipdl"; + +include "mozilla/plugins/PluginMessageUtils.h"; + +using NPError; +using NPRemoteWindow; +using NPRemoteEvent; +using NPRect; +using NPNURLVariable; +using mozilla::plugins::NativeWindowHandle; + +namespace mozilla { +namespace plugins { + +rpc protocol PPluginInstance +{ + manager PPluginModule; + + manages PPluginScriptableObject; + manages PBrowserStream; + manages PPluginStream; + manages PStreamNotify; + +child: + rpc __delete__(); + + rpc NPP_SetWindow(NPRemoteWindow window); + + // this message is not used on non-X platforms + rpc NPP_GetValue_NPPVpluginNeedsXEmbed() + returns (bool value, NPError result); + rpc NPP_GetValue_NPPVpluginScriptableNPObject() + returns (nullable PPluginScriptableObject value, NPError result); + + rpc NPP_SetValue_NPNVprivateModeBool(bool value) returns (NPError result); + + rpc NPP_HandleEvent(NPRemoteEvent event) + returns (int16_t handled); + // special cases of HandleEvent to make mediating races simpler + rpc Paint(NPRemoteEvent event) + returns (int16_t handled); + // this is only used on windows to forward WM_WINDOWPOSCHANGE + async WindowPosChanged(NPRemoteEvent event); + + rpc NPP_Destroy() + returns (NPError rv); + +parent: + rpc NPN_GetValue_NPNVjavascriptEnabledBool() + returns (bool value, NPError result); + rpc NPN_GetValue_NPNVisOfflineBool() + returns (bool value, NPError result); + rpc NPN_GetValue_NPNVWindowNPObject() + returns (nullable PPluginScriptableObject value, NPError result); + rpc NPN_GetValue_NPNVPluginElementNPObject() + returns (nullable PPluginScriptableObject value, NPError result); + rpc NPN_GetValue_NPNVprivateModeBool() + returns (bool value, NPError result); + rpc NPN_GetValue_NPNVnetscapeWindow() + returns (NativeWindowHandle value, NPError result); + + rpc NPN_SetValue_NPPVpluginWindow(bool windowed) + returns (NPError result); + rpc NPN_SetValue_NPPVpluginTransparent(bool transparent) + returns (NPError result); + + rpc NPN_GetURL(nsCString url, nsCString target) + returns (NPError result); + rpc NPN_PostURL(nsCString url, nsCString target, nsCString buffer, bool file) + returns (NPError result); + + /** + * Covers both NPN_GetURLNotify and NPN_PostURLNotify. + * @TODO This would be more readable as an overloaded method, + * but IPDL doesn't allow that for constructors. + */ + rpc PStreamNotify(nsCString url, nsCString target, bool post, + nsCString buffer, bool file) + returns (NPError result); + + async NPN_InvalidateRect(NPRect rect); + + rpc NPN_PushPopupsEnabledState(bool aState) + returns (bool aSuccess); + + rpc NPN_PopPopupsEnabledState() + returns (bool aSuccess); + + rpc NPN_GetValueForURL(NPNURLVariable variable, nsCString url) + returns (nsCString value, NPError result); + + rpc NPN_SetValueForURL(NPNURLVariable variable, nsCString url, + nsCString value) + returns (NPError result); + + rpc NPN_GetAuthenticationInfo(nsCString protocol_, nsCString host, + int32_t port, nsCString scheme, + nsCString realm) + returns (nsCString username, nsCString password, NPError result); + +both: + async PPluginScriptableObject(); + +child: + /* NPP_NewStream */ + rpc PBrowserStream(nsCString url, + uint32_t length, + uint32_t lastmodified, + nullable PStreamNotify notifyData, + nsCString headers, + nsCString mimeType, + bool seekable) + returns (NPError rv, + uint16_t stype); + +parent: + /* NPN_NewStream */ + rpc PPluginStream(nsCString mimeType, + nsCString target) + returns (NPError result); + +parent: + rpc PluginGotFocus(); + sync SetNestedEventState(bool aState); +child: + rpc SetPluginFocus(); + rpc UpdateWindow(); +}; + +} // namespace plugins +} // namespace mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PPluginModule.ipdl firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PPluginModule.ipdl --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PPluginModule.ipdl 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PPluginModule.ipdl 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,102 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugins. + * + * The Initial Developer of the Original Code is + * Chris Jones . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Benjamin Smedberg + * Ben Turner + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +include protocol "PPluginIdentifier.ipdl"; +include protocol "PPluginInstance.ipdl"; + +include "npapi.h"; +include "mozilla/plugins/PluginMessageUtils.h"; + +using NPError; +using NPNVariable; +using mozilla::plugins::NativeThreadId; + +namespace mozilla { +namespace plugins { + +rpc protocol PPluginModule +{ + manages PPluginInstance; + manages PPluginIdentifier; + +both: + /** + * Sending a void string to this constructor creates an int identifier whereas + * sending a non-void string will create a string identifier. This constructor + * may be called by either child or parent. If a race occurs by calling the + * constructor with the same string or int argument then we create two actors + * and detect the second instance in the child. We prevent the parent's actor + * from leaking out to plugin code and only allow the child's to be used. + */ + async PPluginIdentifier(nsCString aString, + int32_t aInt); + +child: + // Return the plugin's thread ID, if it can be found. + rpc NP_Initialize() + returns (NativeThreadId tid, NPError rv); + + rpc PPluginInstance(nsCString aMimeType, + uint16_t aMode, + nsCString[] aNames, + nsCString[] aValues) + returns (NPError rv); + + rpc NP_Shutdown() + returns (NPError rv); + +parent: + rpc NPN_UserAgent() + returns (nsCString userAgent); + + rpc NPN_GetValue_WithBoolReturn(NPNVariable aVariable) + returns (NPError aError, + bool aBoolVal); + + // Wake up and process a few native events. Periodically called by + // Gtk-specific code upon detecting that the plugin process has + // entered a nested event loop. If the browser doesn't process + // native events, then "livelock" and some other glitches can occur. + rpc ProcessSomeEvents(); + + sync AppendNotesToCrashReport(nsCString aNotes); +}; + +} // namespace plugins +} // namespace mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PPluginScriptableObject.ipdl firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PPluginScriptableObject.ipdl --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PPluginScriptableObject.ipdl 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PPluginScriptableObject.ipdl 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,140 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugins. + * + * The Initial Developer of the Original Code is + * Chris Jones . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Benjamin Smedberg + * Ben Turner + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +include protocol "PPluginInstance.ipdl"; +include protocol "PPluginIdentifier.ipdl"; + +include "npapi.h"; +include "npruntime.h"; +include "mozilla/plugins/PluginMessageUtils.h"; + +using mozilla::void_t; +using mozilla::null_t; + +namespace mozilla { +namespace plugins { + +union Variant { + void_t; + null_t; + bool; + int; + double; + nsCString; + nullable PPluginScriptableObject; +}; + +rpc protocol PPluginScriptableObject +{ + manager PPluginInstance; + +both: + async __delete__(); + +parent: + rpc NPN_Evaluate(nsCString aScript) + returns (Variant aResult, + bool aSuccess); + +child: + rpc Invalidate(); + +both: + // NPClass methods + rpc HasMethod(PPluginIdentifier aId) + returns (bool aHasMethod); + + rpc Invoke(PPluginIdentifier aId, + Variant[] aArgs) + returns (Variant aResult, + bool aSuccess); + + rpc InvokeDefault(Variant[] aArgs) + returns (Variant aResult, + bool aSuccess); + + rpc HasProperty(PPluginIdentifier aId) + returns (bool aHasProperty); + + rpc SetProperty(PPluginIdentifier aId, + Variant aValue) + returns (bool aSuccess); + + rpc RemoveProperty(PPluginIdentifier aId) + returns (bool aSuccess); + + rpc Enumerate() + returns (PPluginIdentifier[] aProperties, + bool aSuccess); + + rpc Construct(Variant[] aArgs) + returns (Variant aResult, + bool aSuccess); + + // Objects are initially unprotected, and the Protect and Unprotect functions + // only affect protocol objects that represent NPObjects created in the same + // process (rather than protocol objects that are a proxy for an NPObject + // created in another process). Protocol objects representing local NPObjects + // are protected after an NPObject has been associated with the protocol + // object. Sending the protocol object as an argument to the other process + // temporarily protects the protocol object again for the duration of the call. + async Protect(); + async Unprotect(); + + /** + * GetProperty is slightly wonky due to the way we support NPObjects that have + * methods and properties with the same name. When child calls parent we + * simply return a property. When parent calls child, however, we need to do + * several checks at once and return all the results simultaneously. + */ +parent: + rpc GetParentProperty(PPluginIdentifier aId) + returns (Variant aResult, + bool aSuccess); + +child: + rpc GetChildProperty(PPluginIdentifier aId) + returns (bool aHasProperty, + bool aHasMethod, + Variant aResult, + bool aSuccess); +}; + +} // namespace plugins +} // namespace mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PPluginStream.ipdl firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PPluginStream.ipdl --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PPluginStream.ipdl 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PPluginStream.ipdl 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,70 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugins. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +include protocol "PPluginInstance.ipdl"; + +include "mozilla/plugins/PluginMessageUtils.h"; + +using mozilla::plugins::Buffer; +using NPError; +using NPReason; + +namespace mozilla { +namespace plugins { + +/** + * PPluginStream represents an NPStream sent from the plugin to the browser. + */ + +rpc protocol PPluginStream +{ + manager PPluginInstance; + +parent: + rpc NPN_Write(Buffer data) returns (int32_t written); + +both: + /** + * ~PPluginStream is for both NPN_DestroyStream and NPP_DestroyStream. + * @param artificial True when the stream is closed as a by-product of + * some other call (such as a failure in NPN_Write). + */ + rpc __delete__(NPReason reason, bool artificial); +}; + +} // namespace plugins +} // namespace mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/PStreamNotify.ipdl firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PStreamNotify.ipdl --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/PStreamNotify.ipdl 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/PStreamNotify.ipdl 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,27 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ + +include protocol "PPluginInstance.ipdl"; + +include "npapi.h"; + +using NPReason; + +namespace mozilla { +namespace plugins { + +/** + * This empty protocol exists only to be constructed and destroyed. + */ +rpc protocol PStreamNotify +{ + manager PPluginInstance; + +child: + /** + * Represents NPP_URLNotify + */ + async __delete__(NPReason reason); +}; + +} // namespace plugins +} // namespace mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/StreamNotifyChild.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/StreamNotifyChild.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/StreamNotifyChild.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/StreamNotifyChild.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,94 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* vim: set sw=2 ts=2 et : */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Chris Jones + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_plugins_StreamNotifyChild_h +#define mozilla_plugins_StreamNotifyChild_h + +#include "mozilla/plugins/PStreamNotifyChild.h" + +namespace mozilla { +namespace plugins { + +class BrowserStreamChild; + +class StreamNotifyChild : public PStreamNotifyChild +{ + friend class PluginInstanceChild; + friend class BrowserStreamChild; + +public: + StreamNotifyChild(const nsCString& aURL) + : mURL(aURL) + , mClosure(NULL) + , mBrowserStream(NULL) + { } + + NS_OVERRIDE virtual void ActorDestroy(ActorDestroyReason why); + + void SetValid(void* aClosure) { + mClosure = aClosure; + } + + void NPP_URLNotify(NPReason reason); + +private: + NS_OVERRIDE virtual bool Recv__delete__(const NPReason& reason); + + /** + * If a stream is created for this this URLNotify, we associate the objects + * so that the NPP_URLNotify call is not fired before the stream data is + * completely delivered. The BrowserStreamChild takes responsibility for + * calling NPP_URLNotify and deleting this object. + */ + void SetAssociatedStream(BrowserStreamChild* bs); + + nsCString mURL; + void* mClosure; + + /** + * If mBrowserStream is true, it is responsible for deleting this C++ object + * and DeallocPStreamNotify is not, so that the delayed delivery of + * NPP_URLNotify is possible. + */ + BrowserStreamChild* mBrowserStream; +}; + +} // namespace plugins +} // namespace mozilla + +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/plugins/StreamNotifyParent.h firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/StreamNotifyParent.h --- firefox-3.6.3+nobinonly/mozilla/dom/plugins/StreamNotifyParent.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/plugins/StreamNotifyParent.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,76 @@ +/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */ +/* vim: set sw=2 ts=2 et : */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Chris Jones + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_plugins_StreamNotifyParent_h +#define mozilla_plugins_StreamNotifyParent_h + +#include "mozilla/plugins/PStreamNotifyParent.h" + +namespace mozilla { +namespace plugins { + +class StreamNotifyParent : public PStreamNotifyParent +{ + friend class PluginInstanceParent; + + StreamNotifyParent() + : mDestructionFlag(NULL) + { } + ~StreamNotifyParent() { + if (mDestructionFlag) + *mDestructionFlag = true; + } + +public: + // If we are destroyed within the call to NPN_GetURLNotify, notify the caller + // so that we aren't destroyed again. see bug 536437. + void SetDestructionFlag(bool* flag) { + mDestructionFlag = flag; + } + void ClearDestructionFlag() { + mDestructionFlag = NULL; + } + +private: + bool* mDestructionFlag; +}; + +} // namespace plugins +} // namespace mozilla + +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/geolocation/MaemoLocationProvider.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/src/geolocation/MaemoLocationProvider.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/src/geolocation/MaemoLocationProvider.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/geolocation/MaemoLocationProvider.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,409 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Geolocation. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Oleg Romashin (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include +#include +#include "MaemoLocationProvider.h" +#include "nsIClassInfo.h" +#include "nsDOMClassInfoID.h" +#include "nsIDOMClassInfo.h" +#include "nsIPrefService.h" +#include "nsIPrefBranch.h" +#include "nsIServiceManager.h" +#include "nsServiceManagerUtils.h" + +//////////////////////////////////////////////////// +// nsGeoPositionCoords +//////////////////////////////////////////////////// + +class nsGeoPositionCoords : public nsIDOMGeoPositionCoords +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMGEOPOSITIONCOORDS + + nsGeoPositionCoords(double aLat, double aLong, double aAlt, double aHError, + double aVError, double aHeading, double aSpeed) : + mLat(aLat), mLong(aLong), mAlt(aAlt), mHError(aHError), + mVError(aVError), mHeading(aHeading), mSpeed(aSpeed) { }; +private: + ~nsGeoPositionCoords() { } + double mLat, mLong, mAlt, mHError, mVError, mHeading, mSpeed; +}; + +NS_INTERFACE_MAP_BEGIN(nsGeoPositionCoords) +NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoPositionCoords) +NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionCoords) +NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(GeoPositionCoords) +NS_INTERFACE_MAP_END + +NS_IMPL_THREADSAFE_ADDREF(nsGeoPositionCoords) +NS_IMPL_THREADSAFE_RELEASE(nsGeoPositionCoords) + +NS_IMETHODIMP +nsGeoPositionCoords::GetLatitude(double *aLatitude) +{ + *aLatitude = mLat; + return NS_OK; +} + +NS_IMETHODIMP +nsGeoPositionCoords::GetLongitude(double *aLongitude) +{ + *aLongitude = mLong; + return NS_OK; +} + +NS_IMETHODIMP +nsGeoPositionCoords::GetAltitude(double *aAltitude) +{ + *aAltitude = mAlt; + return NS_OK; +} + +NS_IMETHODIMP +nsGeoPositionCoords::GetAccuracy(double *aAccuracy) +{ + *aAccuracy = mHError; + return NS_OK; +} + +NS_IMETHODIMP +nsGeoPositionCoords::GetAltitudeAccuracy(double *aAltitudeAccuracy) +{ + *aAltitudeAccuracy = mVError; + return NS_OK; +} + +NS_IMETHODIMP +nsGeoPositionCoords::GetHeading(double *aHeading) +{ + *aHeading = mHeading; + return NS_OK; +} + +NS_IMETHODIMP +nsGeoPositionCoords::GetSpeed(double *aSpeed) +{ + *aSpeed = mSpeed; + return NS_OK; +} + +//////////////////////////////////////////////////// +// nsGeoPosition +//////////////////////////////////////////////////// + +class nsGeoPosition : public nsIDOMGeoPosition +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMGEOPOSITION + + nsGeoPosition(double aLat, double aLong, double aAlt, double aHError, + double aVError, double aHeading, double aSpeed, + long long aTimestamp): mTimestamp(aTimestamp) + { + mCoords = new nsGeoPositionCoords(aLat, aLong, aAlt, aHError, + aVError, aHeading, aSpeed); + NS_ASSERTION(mCoords, "null mCoords in nsGeoPosition"); + }; + +private: + ~nsGeoPosition() {} + long long mTimestamp; + nsRefPtr mCoords; +}; + +NS_INTERFACE_MAP_BEGIN(nsGeoPosition) +NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMGeoPosition) +NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPosition) +NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(GeoPosition) +NS_INTERFACE_MAP_END + +NS_IMPL_THREADSAFE_ADDREF(nsGeoPosition) +NS_IMPL_THREADSAFE_RELEASE(nsGeoPosition) + +NS_IMETHODIMP +nsGeoPosition::GetTimestamp(DOMTimeStamp* aTimestamp) +{ + *aTimestamp = mTimestamp; + return NS_OK; +} + +NS_IMETHODIMP +nsGeoPosition::GetCoords(nsIDOMGeoPositionCoords * *aCoords) +{ + NS_IF_ADDREF(*aCoords = mCoords); + return NS_OK; +} + +NS_IMETHODIMP +nsGeoPosition::GetAddress(nsIDOMGeoPositionAddress** aAddress) +{ + *aAddress = nsnull; + return NS_OK; +} + +NS_IMPL_ISUPPORTS2(MaemoLocationProvider, nsIGeolocationProvider, nsITimerCallback) + +MaemoLocationProvider::MaemoLocationProvider() : + mLocationChanged(0), + mControlError(0), + mDeviceDisconnected(0), + mControlStopped(0), + mHasSeenLocation(PR_FALSE), + mHasGPS(PR_TRUE), + mGPSControl(nsnull), + mGPSDevice(nsnull), + mIgnoreMinorChanges(PR_FALSE), + mPrevLat(0.0), + mPrevLong(0.0), + mIgnoreBigHErr(PR_TRUE), + mMaxHErr(1000), + mIgnoreBigVErr(PR_TRUE), + mMaxVErr(100) +{ +} + +MaemoLocationProvider::~MaemoLocationProvider() +{ +} + +void MaemoLocationProvider::DeviceDisconnected(LocationGPSDevice* device, void* self) +{ +} + +void MaemoLocationProvider::ControlStopped(LocationGPSDControl* device, void* self) +{ + MaemoLocationProvider* provider = static_cast(self); + provider->StartControl(); +} + +void MaemoLocationProvider::ControlError(LocationGPSDControl* control, void* self) +{ +} + +void MaemoLocationProvider::LocationChanged(LocationGPSDevice* device, void* self) +{ + if (!device || !device->fix) + return; + + guint32 &fields = device->fix->fields; + if (!(fields & LOCATION_GPS_DEVICE_LATLONG_SET)) + return; + + if (!(device->fix->eph && !isnan(device->fix->eph))) + return; + + MaemoLocationProvider* provider = static_cast(self); + NS_ENSURE_TRUE(provider, ); + provider->LocationUpdate(device); +} + +nsresult +MaemoLocationProvider::LocationUpdate(LocationGPSDevice* aDev) +{ + double hErr = aDev->fix->eph/100; + if (mIgnoreBigHErr && hErr > (double)mMaxHErr) + hErr = (double)mMaxHErr; + + double vErr = aDev->fix->epv/2; + if (mIgnoreBigVErr && vErr > (double)mMaxVErr) + vErr = (double)mMaxVErr; + + double altitude = 0, speed = 0, track = 0; + if (aDev->fix->epv && !isnan(aDev->fix->epv)) + altitude = aDev->fix->altitude; + if (aDev->fix->eps && !isnan(aDev->fix->eps)) + speed = aDev->fix->speed; + if (aDev->fix->epd && !isnan(aDev->fix->epd)) + track = aDev->fix->track; + +#ifdef DEBUG + double dist = location_distance_between(mPrevLat, mPrevLong, aDev->fix->latitude, aDev->fix->longitude)*1000; + fprintf(stderr, "dist:%.9f, Lat: %.6f, Long:%.6f, HErr:%g, Alt:%.6f, VErr:%g, dir:%g[%g], sp:%g[%g]\n", + dist, aDev->fix->latitude, aDev->fix->longitude, + hErr, altitude, + aDev->fix->epv/2, track, aDev->fix->epd, + speed, aDev->fix->eps); + mPrevLat = aDev->fix->latitude; + mPrevLong = aDev->fix->longitude; +#endif + + nsRefPtr somewhere = new nsGeoPosition(aDev->fix->latitude, + aDev->fix->longitude, + altitude, + hErr, + vErr, + track, + speed, + PR_Now()); + Update(somewhere); + + return NS_OK; +} + +NS_IMETHODIMP +MaemoLocationProvider::Notify(nsITimer* aTimer) +{ + LocationChanged(mGPSDevice, this); + return NS_OK; +} + +nsresult +MaemoLocationProvider::StartControl() +{ + if (mGPSControl) + return NS_OK; + + mGPSControl = location_gpsd_control_get_default(); + NS_ENSURE_TRUE(mGPSControl, NS_ERROR_FAILURE); + + mControlError = g_signal_connect(mGPSControl, "error", + G_CALLBACK(ControlError), this); + + mControlStopped = g_signal_connect(mGPSControl, "gpsd_stopped", + G_CALLBACK(ControlStopped), this); + + location_gpsd_control_start(mGPSControl); + return NS_OK; +} + +nsresult +MaemoLocationProvider::StartDevice() +{ + if (mGPSDevice) + return NS_OK; + + mGPSDevice = (LocationGPSDevice*)g_object_new(LOCATION_TYPE_GPS_DEVICE, NULL); + NS_ENSURE_TRUE(mGPSDevice, NS_ERROR_FAILURE); + + mLocationChanged = g_signal_connect(mGPSDevice, "changed", + G_CALLBACK(LocationChanged), this); + + mDeviceDisconnected = g_signal_connect(mGPSDevice, "disconnected", + G_CALLBACK(DeviceDisconnected), this); + return NS_OK; +} + +NS_IMETHODIMP MaemoLocationProvider::Startup() +{ + nsresult rv(NS_OK); + + nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); + if (!prefs) + return NS_ERROR_FAILURE; + + rv = StartControl(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = StartDevice(); + NS_ENSURE_SUCCESS(rv, rv); + + prefs->GetBoolPref("geo.herror.ignore.big", &mIgnoreBigHErr); + + if (mIgnoreBigHErr) + prefs->GetIntPref("geo.herror.max.value", &mMaxHErr); + + prefs->GetBoolPref("geo.verror.ignore.big", &mIgnoreBigVErr); + + if (mIgnoreBigVErr) + prefs->GetIntPref("geo.verror.max.value", &mMaxVErr); + + if (mUpdateTimer) + return NS_OK; + + PRInt32 update = 0; //0 second no timer created + prefs->GetIntPref("geo.default.update", &update); + + if (!update) + return NS_OK; + + mUpdateTimer = do_CreateInstance("@mozilla.org/timer;1", &rv); + + if (NS_FAILED(rv)) + return NS_ERROR_FAILURE; + + if (update) + mUpdateTimer->InitWithCallback(this, update, nsITimer::TYPE_REPEATING_SLACK); + + return NS_OK; +} + +NS_IMETHODIMP MaemoLocationProvider::Watch(nsIGeolocationUpdate *callback) +{ + if (mCallback) + return NS_OK; + + mCallback = callback; + return NS_OK; +} + +NS_IMETHODIMP MaemoLocationProvider::Shutdown() +{ + if (mUpdateTimer) + mUpdateTimer->Cancel(); + + g_signal_handler_disconnect(mGPSDevice, mLocationChanged); + g_signal_handler_disconnect(mGPSDevice, mDeviceDisconnected); + + g_signal_handler_disconnect(mGPSDevice, mControlError); + g_signal_handler_disconnect(mGPSDevice, mControlStopped); + + mHasSeenLocation = PR_FALSE; + mCallback = nsnull; + + if (mGPSControl) { + location_gpsd_control_stop(mGPSControl); + g_object_unref(mGPSControl); + mGPSControl = nsnull; + } + if (mGPSDevice) { + g_object_unref(mGPSDevice); + mGPSDevice = nsnull; + } + + return NS_OK; +} + +void MaemoLocationProvider::Update(nsIDOMGeoPosition* aPosition) +{ + mHasSeenLocation = PR_TRUE; + if (mCallback) + mCallback->Update(aPosition); +} + diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/geolocation/MaemoLocationProvider.h firefox-3.6.4+build1+nobinonly/mozilla/dom/src/geolocation/MaemoLocationProvider.h --- firefox-3.6.3+nobinonly/mozilla/dom/src/geolocation/MaemoLocationProvider.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/geolocation/MaemoLocationProvider.h 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,120 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Geolocation. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Doug Turner (Original Author) + * Nino D'Aversa + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsCOMPtr.h" +#include "nsAutoPtr.h" +#include "nsCOMArray.h" +#include "nsTArray.h" +#include "nsITimer.h" +#include "nsIObserver.h" +#include "nsIURI.h" + +#include "nsWeakPtr.h" +#include "nsCycleCollectionParticipant.h" + +#include "nsIDOMGeoGeolocation.h" +#include "nsIDOMGeoPosition.h" +#include "nsIDOMGeoPositionError.h" +#include "nsIDOMGeoPositionCallback.h" +#include "nsIDOMGeoPositionErrorCallback.h" +#include "nsIDOMGeoPositionOptions.h" +#include "nsIDOMNavigatorGeolocation.h" +#include "nsIDOMGeoPositionCoords.h" + +#include "nsPIDOMWindow.h" + +#include "nsIGeolocationProvider.h" + + +extern "C" +{ +#include +#include +#include +#include +} + +class MaemoLocationProvider : public nsIGeolocationProvider, + public nsITimerCallback + +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIGEOLOCATIONPROVIDER + NS_DECL_NSITIMERCALLBACK + + MaemoLocationProvider(); + + void Update(nsIDOMGeoPosition* aPosition); + + private: + ~MaemoLocationProvider(); + + nsresult StartControl(); + nsresult StartDevice(); + nsresult LocationUpdate(LocationGPSDevice* aDev); + + static void DeviceDisconnected(LocationGPSDevice* device, void* self); + static void ControlStopped(LocationGPSDControl* device, void* self); + static void ControlError(LocationGPSDControl* control, void* self); + static void LocationChanged(LocationGPSDevice* device, void* self); + + gulong mLocationChanged; + gulong mControlError; + gulong mDeviceDisconnected; + gulong mControlStopped; + + nsCOMPtr mCallback; + PRPackedBool mHasSeenLocation; + PRPackedBool mHasGPS; + + nsCOMPtr mUpdateTimer; + LocationGPSDControl* mGPSControl; + LocationGPSDevice* mGPSDevice; + + PRBool mIgnoreMinorChanges; + + double mPrevLat; + double mPrevLong; + + PRBool mIgnoreBigHErr; + PRInt32 mMaxHErr; + PRBool mIgnoreBigVErr; + PRInt32 mMaxVErr; + +}; diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/geolocation/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/dom/src/geolocation/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/dom/src/geolocation/Makefile.in 2010-04-02 16:57:55.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/geolocation/Makefile.in 2010-04-16 17:31:42.000000000 +0100 @@ -80,6 +80,11 @@ CPPSRCS += WinMobileLocationProvider.cpp endif +ifdef MOZ_PLATFORM_MAEMO +CPPSRCS += MaemoLocationProvider.cpp +LOCAL_INCLUDES += $(MOZ_PLATFORM_MAEMO_CFLAGS) +endif + EXTRA_COMPONENTS = \ NetworkGeolocationProvider.js \ GPSDGeolocationProvider.js \ diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/geolocation/nsGeolocation.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/src/geolocation/nsGeolocation.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/src/geolocation/nsGeolocation.cpp 2010-04-02 16:57:55.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/geolocation/nsGeolocation.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -61,6 +61,10 @@ #include "WinMobileLocationProvider.h" #endif +#ifdef MOZ_PLATFORM_MAEMO +#include "MaemoLocationProvider.h" +#endif + #include "nsIDOMDocument.h" #include "nsIDocument.h" @@ -410,12 +414,17 @@ // we should move these providers outside of this file! dft - // if WINCE, see if we should try the WINCE location provider #ifdef WINCE_WINDOWS_MOBILE provider = new WinMobileLocationProvider(); if (provider) mProviders.AppendObject(provider); #endif + +#ifdef MOZ_PLATFORM_MAEMO + provider = new MaemoLocationProvider(); + if (provider) + mProviders.AppendObject(provider); +#endif return NS_OK; } diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/threads/nsDOMThreadService.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/nsDOMThreadService.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/src/threads/nsDOMThreadService.cpp 2010-04-02 16:57:55.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/nsDOMThreadService.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -616,27 +616,44 @@ JSAutoSuspendRequest ar(aCx); scriptError = do_CreateInstance(NS_SCRIPTERROR_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv,); } - const PRUnichar* message = - reinterpret_cast(aReport->ucmessage); + if (NS_FAILED(rv)) { + return; + } - nsAutoString filename; - filename.AssignWithConversion(aReport->filename); + nsAutoString message, filename, line; + PRUint32 lineNumber, columnNumber, flags, errorNumber; - const PRUnichar* line = - reinterpret_cast(aReport->uclinebuf); + if (aReport) { + if (aReport->ucmessage) { + message.Assign(aReport->ucmessage); + } + filename.AssignWithConversion(aReport->filename); + line.Assign(aReport->uclinebuf); + lineNumber = aReport->lineno; + columnNumber = aReport->uctokenptr - aReport->uclinebuf; + flags = aReport->flags; + errorNumber = aReport->errorNumber; + } + else { + lineNumber = columnNumber = errorNumber = 0; + flags = nsIScriptError::errorFlag | nsIScriptError::exceptionFlag; + } - PRUint32 column = aReport->uctokenptr - aReport->uclinebuf; + if (message.IsEmpty()) { + message.AssignWithConversion(aMessage); + } - rv = scriptError->Init(message, filename.get(), line, aReport->lineno, - column, aReport->flags, "DOM Worker javascript"); - NS_ENSURE_SUCCESS(rv,); + rv = scriptError->Init(message.get(), filename.get(), line.get(), lineNumber, + columnNumber, flags, "DOM Worker javascript"); + if (NS_FAILED(rv)) { + return; + } // Don't call the error handler if we're out of stack space. - if (aReport->errorNumber != JSMSG_SCRIPT_STACK_QUOTA && - aReport->errorNumber != JSMSG_OVER_RECURSED) { + if (errorNumber != JSMSG_SCRIPT_STACK_QUOTA && + errorNumber != JSMSG_OVER_RECURSED) { // Try the onerror handler for the worker's scope. nsRefPtr scope = worker->GetInnerScope(); NS_ASSERTION(scope, "Null scope!"); @@ -645,9 +662,9 @@ if (hasListeners) { nsRefPtr event(new nsDOMWorkerErrorEvent()); if (event) { - rv = event->InitErrorEvent(NS_LITERAL_STRING("error"), PR_FALSE, PR_TRUE, - nsDependentString(message), filename, - aReport->lineno); + rv = event->InitErrorEvent(NS_LITERAL_STRING("error"), PR_FALSE, + PR_TRUE, nsDependentString(message), + filename, lineNumber); if (NS_SUCCEEDED(rv)) { event->SetTarget(scope); @@ -681,7 +698,9 @@ // top-level worker and we send the message to the main thread. rv = parent ? nsDOMThreadService::get()->Dispatch(parent, runnable) : NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL); - NS_ENSURE_SUCCESS(rv,); + if (NS_FAILED(rv)) { + return; + } } /******************************************************************************* diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/threads/nsDOMThreadService.h firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/nsDOMThreadService.h --- firefox-3.6.3+nobinonly/mozilla/dom/src/threads/nsDOMThreadService.h 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/nsDOMThreadService.h 2010-04-16 17:31:42.000000000 +0100 @@ -171,7 +171,7 @@ nsCOMPtr mThreadPool; // Maps nsIScriptGlobalObject* to nsDOMWorkerPool. - nsRefPtrHashtable mPools; + nsRefPtrHashtable mPools; // mMonitor protects all access to mWorkersInProgress and // mCreationsInProgress. diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/threads/nsDOMWorker.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/nsDOMWorker.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/src/threads/nsDOMWorker.cpp 2010-04-02 16:57:55.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/nsDOMWorker.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -935,6 +935,17 @@ return NS_ERROR_ABORT; } + // If the worker is suspended and we're running on the main thread then we + // can't actually dispatch the event yet. Instead we queue it for whenever + // we resume. + if (mWorker->IsSuspended() && NS_IsMainThread()) { + if (!mWorker->QueueSuspendedRunnable(this)) { + NS_ERROR("Out of memory?!"); + return NS_ERROR_ABORT; + } + return NS_OK; + } + nsCOMPtr target = mToInner ? static_cast(mWorker->GetInnerScope()) : static_cast(mWorker); @@ -1045,6 +1056,7 @@ } NS_ASSERTION(!mFeatures.Length(), "Live features!"); + NS_ASSERTION(!mQueuedRunnables.Length(), "Events that never ran!"); nsCOMPtr mainThread; NS_GetMainThread(getter_AddRefs(mainThread)); @@ -1349,6 +1361,9 @@ features[index]->Cancel(); } + // Make sure we kill any queued runnables that we never had a chance to run. + mQueuedRunnables.Clear(); + // We no longer need to keep our inner scope. mInnerScope = nsnull; mScopeWN = nsnull; @@ -1400,12 +1415,29 @@ if (shouldResumeFeatures) { ResumeFeatures(); } + + // Repost any events that were queued for the main thread while suspended. + PRUint32 count = mQueuedRunnables.Length(); + for (PRUint32 index = 0; index < count; index++) { + NS_DispatchToCurrentThread(mQueuedRunnables[index]); + } + mQueuedRunnables.Clear(); } PRBool nsDOMWorker::IsCanceled() { nsAutoLock lock(mLock); + return IsCanceledNoLock(); +} + +PRBool +nsDOMWorker::IsCanceledNoLock() +{ + // If we haven't started the close process then we're not canceled. + if (mStatus == eRunning) { + return PR_FALSE; + } // There are several conditions under which we want JS code to abort and all // other functions to bail: @@ -1934,6 +1966,13 @@ } #endif +PRBool +nsDOMWorker::QueueSuspendedRunnable(nsIRunnable* aRunnable) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + return mQueuedRunnables.AppendElement(aRunnable) ? PR_TRUE : PR_FALSE; +} + NS_IMETHODIMP nsDOMWorker::AddEventListener(const nsAString& aType, nsIDOMEventListener* aListener, @@ -1965,8 +2004,18 @@ nsDOMWorker::DispatchEvent(nsIDOMEvent* aEvent, PRBool* _retval) { - if (IsCanceled()) { - return NS_OK; + { + nsAutoLock lock(mLock); + if (IsCanceledNoLock()) { + return NS_OK; + } + if (mStatus == eTerminated) { + nsCOMPtr messageEvent(do_QueryInterface(aEvent)); + if (messageEvent) { + // This is a message event targeted to a terminated worker. Ignore it. + return NS_OK; + } + } } return nsDOMWorkerMessageHandler::DispatchEvent(aEvent, _retval); diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/threads/nsDOMWorker.h firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/nsDOMWorker.h --- firefox-3.6.3+nobinonly/mozilla/dom/src/threads/nsDOMWorker.h 2010-04-02 16:57:55.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/nsDOMWorker.h 2010-04-16 17:31:42.000000000 +0100 @@ -64,6 +64,7 @@ class nsICancelable; class nsIDOMEventListener; class nsIEventTarget; +class nsIRunnable; class nsIScriptGlobalObject; class nsIXPConnectWrappedNative; @@ -112,17 +113,13 @@ friend class nsDOMWorkerXHR; friend class nsDOMWorkerXHRProxy; friend class nsReportErrorRunnable; + friend class nsDOMFireEventRunnable; friend JSBool DOMWorkerOperationCallback(JSContext* aCx); friend void DOMWorkerErrorReporter(JSContext* aCx, const char* aMessage, JSErrorReport* aReport); -#ifdef DEBUG - // For fun assertions. - friend class nsDOMFireEventRunnable; -#endif - public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIDOMEVENTTARGET @@ -153,7 +150,9 @@ void Suspend(); void Resume(); + // This just calls IsCanceledNoLock with an autolock around the call. PRBool IsCanceled(); + PRBool IsClosing(); PRBool IsSuspended(); @@ -276,6 +275,12 @@ return mLocation; } + PRBool QueueSuspendedRunnable(nsIRunnable* aRunnable); + + // Determines if the worker should be considered "canceled". See the large + // comment in the implementation for more details. + PRBool IsCanceledNoLock(); + private: // mParent will live as long as mParentWN but only mParentWN will keep the JS @@ -315,6 +320,8 @@ nsCOMPtr mLocation; + nsTArray > mQueuedRunnables; + PRPackedBool mSuspended; PRPackedBool mCompileAttempted; }; diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/threads/nsDOMWorkerTimeout.cpp firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/nsDOMWorkerTimeout.cpp --- firefox-3.6.3+nobinonly/mozilla/dom/src/threads/nsDOMWorkerTimeout.cpp 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/nsDOMWorkerTimeout.cpp 2010-04-16 17:31:42.000000000 +0100 @@ -253,6 +253,7 @@ else { // If no interval was specified, treat this like a timeout, to avoid // setting an interval of 0 milliseconds. + interval = 0; aIsInterval = PR_FALSE; } diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/threads/test/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/test/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/dom/src/threads/test/Makefile.in 2010-04-02 16:57:55.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/test/Makefile.in 2010-04-16 17:31:42.000000000 +0100 @@ -82,6 +82,9 @@ relativeLoad_import.js \ test_scopeOnerror.html \ scopeOnerror_worker.js \ + test_suspend.html \ + suspend_iframe.html \ + suspend_worker.js \ test_simpleThread.html \ simpleThread_worker.js \ test_terminate.html \ @@ -102,6 +105,8 @@ testXHR.txt \ test_fibonacci.html \ fibonacci_worker.js \ + test_newError.html \ + newError_worker.js \ $(NULL) _SUBDIR_TEST_FILES = \ diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/threads/test/newError_worker.js firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/test/newError_worker.js --- firefox-3.6.3+nobinonly/mozilla/dom/src/threads/test/newError_worker.js 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/test/newError_worker.js 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1 @@ +throw new Error("foo!"); diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/threads/test/suspend_iframe.html firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/test/suspend_iframe.html --- firefox-3.6.3+nobinonly/mozilla/dom/src/threads/test/suspend_iframe.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/test/suspend_iframe.html 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,40 @@ + + + + Test for DOM Worker Threads Suspending + + + +
    +
    + +
    + + diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/threads/test/suspend_worker.js firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/test/suspend_worker.js --- firefox-3.6.3+nobinonly/mozilla/dom/src/threads/test/suspend_worker.js 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/test/suspend_worker.js 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,5 @@ +var counter = 0; + +setInterval(function() { + postMessage(++counter); +}, 100); diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/threads/test/test_closeOnGC.html firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/test/test_closeOnGC.html --- firefox-3.6.3+nobinonly/mozilla/dom/src/threads/test/test_closeOnGC.html 2010-04-02 16:57:55.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/test/test_closeOnGC.html 2010-04-16 17:31:42.000000000 +0100 @@ -26,7 +26,6 @@ } var interval = setInterval(function() { - dump("xxxben interval\n"); var xhr = new XMLHttpRequest(); xhr.open("GET", "closeOnGC_server.sjs", false); xhr.send(); diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/threads/test/test_newError.html firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/test/test_newError.html --- firefox-3.6.3+nobinonly/mozilla/dom/src/threads/test/test_newError.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/test/test_newError.html 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,30 @@ + + + Test for DOM Worker Threads + + + + + +
    +
    +
    + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/threads/test/test_suspend.html firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/test/test_suspend.html --- firefox-3.6.3+nobinonly/mozilla/dom/src/threads/test/test_suspend.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/test/test_suspend.html 2010-04-16 17:31:42.000000000 +0100 @@ -0,0 +1,140 @@ + + + + Test for DOM Worker Threads + + + + + +

    + +
    +
    +
    +
    + + diff -Nru firefox-3.6.3+nobinonly/mozilla/dom/src/threads/test/test_terminate.html firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/test/test_terminate.html --- firefox-3.6.3+nobinonly/mozilla/dom/src/threads/test/test_terminate.html 2010-04-02 16:57:55.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/dom/src/threads/test/test_terminate.html 2010-04-16 17:31:42.000000000 +0100 @@ -17,32 +17,26 @@
     
    +  
    +  
    +
    +
    +Mozilla Bug 531176
    +

    + +
    +
    +
    +
    +
    + + diff -Nru firefox-3.6.3+nobinonly/mozilla/editor/composer/src/nsEditingSession.cpp firefox-3.6.4+build1+nobinonly/mozilla/editor/composer/src/nsEditingSession.cpp --- firefox-3.6.3+nobinonly/mozilla/editor/composer/src/nsEditingSession.cpp 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/editor/composer/src/nsEditingSession.cpp 2010-04-16 17:31:45.000000000 +0100 @@ -1058,9 +1058,10 @@ if (NS_FAILED(rv)) return rv; mEditorStatus = eEditorCreationInProgress; + mDocShell = do_GetWeakReference(docShell); mLoadBlankDocTimer->InitWithFuncCallback( nsEditingSession::TimerCallback, - (void*)docShell, + static_cast (mDocShell.get()), 10, nsITimer::TYPE_ONE_SHOT); } } @@ -1073,7 +1074,7 @@ void nsEditingSession::TimerCallback(nsITimer* aTimer, void* aClosure) { - nsCOMPtr docShell = (nsIDocShell*)aClosure; + nsCOMPtr docShell = do_QueryReferent(static_cast (aClosure)); if (docShell) { nsCOMPtr webNav(do_QueryInterface(docShell)); diff -Nru firefox-3.6.3+nobinonly/mozilla/editor/composer/src/nsEditingSession.h firefox-3.6.4+build1+nobinonly/mozilla/editor/composer/src/nsEditingSession.h --- firefox-3.6.3+nobinonly/mozilla/editor/composer/src/nsEditingSession.h 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/editor/composer/src/nsEditingSession.h 2010-04-16 17:31:45.000000000 +0100 @@ -167,6 +167,9 @@ PRUint32 mBaseCommandControllerId; PRUint32 mDocStateControllerId; PRUint32 mHTMLCommandControllerId; + + // Make sure the docshell we use is safe + nsWeakPtr mDocShell; }; diff -Nru firefox-3.6.3+nobinonly/mozilla/extensions/cookie/test/file_testcommon.js firefox-3.6.4+build1+nobinonly/mozilla/extensions/cookie/test/file_testcommon.js --- firefox-3.6.3+nobinonly/mozilla/extensions/cookie/test/file_testcommon.js 2010-04-02 16:58:05.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/extensions/cookie/test/file_testcommon.js 2010-04-16 17:31:47.000000000 +0100 @@ -29,6 +29,15 @@ gPopup = window.open(uri, 'hai', 'width=100,height=100'); } +function finishTest() +{ + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch) + .clearUserPref("network.cookie.cookieBehavior"); + + SimpleTest.finish(); +} /** Receives MessageEvents to this window. */ // Count and check loads. @@ -39,7 +48,7 @@ gPopup.close(); window.removeEventListener("message", messageReceiver, false); - SimpleTest.finish(); + finishTest(); return; } @@ -68,5 +77,5 @@ is(count, gExpectedCookies, "total number of cookies"); cs.removeAll(); - SimpleTest.finish(); + finishTest(); } diff -Nru firefox-3.6.3+nobinonly/mozilla/extensions/cookie/test/file_testloadflags.js firefox-3.6.4+build1+nobinonly/mozilla/extensions/cookie/test/file_testloadflags.js --- firefox-3.6.3+nobinonly/mozilla/extensions/cookie/test/file_testloadflags.js 2010-04-02 16:58:05.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/extensions/cookie/test/file_testloadflags.js 2010-04-16 17:31:47.000000000 +0100 @@ -37,6 +37,18 @@ gPopup = window.open(uri, 'hai', 'width=100,height=100'); } +function finishTest() +{ + gObs.remove(); + + netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); + Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch) + .clearUserPref("network.cookie.cookieBehavior"); + + SimpleTest.finish(); +} + // Count headers. function obs () { netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); @@ -79,8 +91,7 @@ gPopup.close(); window.removeEventListener("message", messageReceiver, false); - gObs.remove(); - SimpleTest.finish(); + finishTest(); return; } @@ -99,8 +110,6 @@ // set a cookie from a domain of "localhost" document.cookie = "o=noes"; - gObs.remove(); - is(gHeaders, gExpectedHeaders, "number of observed request headers"); netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect"); @@ -113,5 +122,5 @@ is(count, gExpectedCookies, "total number of cookies"); cs.removeAll(); - SimpleTest.finish(); + finishTest(); } diff -Nru firefox-3.6.3+nobinonly/mozilla/extensions/layout-debug/src/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/extensions/layout-debug/src/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/extensions/layout-debug/src/Makefile.in 2010-04-02 16:58:06.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/extensions/layout-debug/src/Makefile.in 2010-04-16 17:31:47.000000000 +0100 @@ -47,7 +47,8 @@ EXPORT_LIBRARY = 1 IS_COMPONENT = 1 MODULE_NAME = nsLayoutDebugModule -LIBXUL_LIBRARY = 1 +LIBXUL_LIBRARY = 1 +GRE_MODULE = 1 REQUIRES = xpcom \ string \ diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/cairo/endian.patch firefox-3.6.4+build1+nobinonly/mozilla/gfx/cairo/endian.patch --- firefox-3.6.3+nobinonly/mozilla/gfx/cairo/endian.patch 2010-04-02 16:58:07.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/cairo/endian.patch 1970-01-01 01:00:00.000000000 +0100 @@ -1,11 +0,0 @@ ---- a/libpixman/src/pixman-private.h Fri Oct 19 14:55:56 2007 -+++ b/libpixman/src/pixman-private.h Fri Oct 19 14:56:45 2007 -@@ -5,6 +5,8 @@ - #ifndef PIXMAN_PRIVATE_H - #define PIXMAN_PRIVATE_H - -+#include "cairo-platform.h" -+ - #include "pixman.h" - #include - diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/cairo/libpixman/src/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/gfx/cairo/libpixman/src/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/gfx/cairo/libpixman/src/Makefile.in 2010-04-02 16:58:07.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/cairo/libpixman/src/Makefile.in 2010-04-16 17:31:48.000000000 +0100 @@ -163,6 +163,20 @@ # This library is used by other shared libs in a static build FORCE_USE_PIC = 1 +include $(topsrcdir)/config/config.mk + +ifndef MOZ_ENABLE_LIBXUL +ifdef GNU_CC +# -fvisibility=hidden works fine but PIXMAN_EXPORT is not used in header +# files, so pixman.h needs to be included before +# "#pragma GCC visibility -push(hidden)". +ifdef WRAP_SYSTEM_INCLUDES +MY_VISIBILITY_FLAGS := -include pixman.h $(VISIBILITY_FLAGS) +COMPILE_CFLAGS += $(MY_VISIBILITY_FLAGS) +endif # WRAP_SYSTEM_INCLUDES +endif # GNU_CC +endif # !MOZ_ENABLE_LIBXUL + include $(topsrcdir)/config/rules.mk CFLAGS += -DPACKAGE="mozpixman" -D_USE_MATH_DEFINES diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/cairo/libpixman/src/pixman.h firefox-3.6.4+build1+nobinonly/mozilla/gfx/cairo/libpixman/src/pixman.h --- firefox-3.6.3+nobinonly/mozilla/gfx/cairo/libpixman/src/pixman.h 2010-04-02 16:58:07.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/cairo/libpixman/src/pixman.h 2010-04-16 17:31:48.000000000 +0100 @@ -69,6 +69,8 @@ #ifndef PIXMAN_H__ #define PIXMAN_H__ +#include "cairo-platform.h" + #include /* diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/cairo/libpixman/src/pixman-private.h firefox-3.6.4+build1+nobinonly/mozilla/gfx/cairo/libpixman/src/pixman-private.h --- firefox-3.6.3+nobinonly/mozilla/gfx/cairo/libpixman/src/pixman-private.h 2010-04-02 16:58:07.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/cairo/libpixman/src/pixman-private.h 2010-04-16 17:31:48.000000000 +0100 @@ -5,8 +5,6 @@ #ifndef PIXMAN_PRIVATE_H #define PIXMAN_PRIVATE_H -#include "cairo-platform.h" - #include "pixman.h" #include diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/cairo/pixman-rename-and-endian.patch firefox-3.6.4+build1+nobinonly/mozilla/gfx/cairo/pixman-rename-and-endian.patch --- firefox-3.6.3+nobinonly/mozilla/gfx/cairo/pixman-rename-and-endian.patch 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/cairo/pixman-rename-and-endian.patch 2010-04-16 17:31:48.000000000 +0100 @@ -0,0 +1,22 @@ +diff --git a/gfx/cairo/libpixman/src/pixman.h b/gfx/cairo/libpixman/src/pixman.h +--- a/gfx/cairo/libpixman/src/pixman.h ++++ b/gfx/cairo/libpixman/src/pixman.h +@@ -64,16 +64,18 @@ SOFTWARE. + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + + #ifndef PIXMAN_H__ + #define PIXMAN_H__ + ++#include "cairo-platform.h" ++ + #include + + /* + * Standard integers + */ + #if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || defined (_sgi) || defined (__sun) || defined (sun) || defined (__digital__) + # include + #elif defined (_MSC_VER) diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/cairo/README firefox-3.6.4+build1+nobinonly/mozilla/gfx/cairo/README --- firefox-3.6.3+nobinonly/mozilla/gfx/cairo/README 2010-04-02 16:58:06.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/cairo/README 2010-04-16 17:31:47.000000000 +0100 @@ -70,7 +70,7 @@ pixman-neon.patch: add ARM NEON optimized compositing functions -endian.patch: include cairo-platform.h for endian macros +pixman-rename-and-endian.patch: include cairo-platform.h for renaming of external symbols and endian macros ==== disable printing patch ==== diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/ipc/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/gfx/ipc/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/gfx/ipc/Makefile.in 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/ipc/Makefile.in 2010-04-16 17:31:48.000000000 +0100 @@ -0,0 +1,73 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (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.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is mozilla.org code. +# +# The Initial Developer of the Original Code is +# Netscape Communications Corporation. +# Portions created by the Initial Developer are Copyright (C) 1998 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either of the GNU General Public License Version 2 or later (the "GPL"), +# or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +ifdef MOZ_IPC + +MODULE = gfxipc +LIBRARY_NAME = gfxipc_s +FORCE_STATIC_LIB = 1 +LIBXUL_LIBRARY = 1 +EXPORT_LIBRARY = 1 + +EXPORTS_NAMESPACES = mozilla/gfx + +EXPORTS_mozilla/gfx = \ + SharedDIB.h \ + $(NULL) + +CPPSRCS = SharedDIB.cpp + +ENABLE_CXX_EXCEPTIONS = 1 + +ifeq ($(MOZ_WIDGET_TOOLKIT),windows) +CPPSRCS += SharedDIBWin.cpp +EXPORTS_mozilla/gfx += SharedDIBWin.h +endif + +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk + +endif + +include $(topsrcdir)/config/rules.mk diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/ipc/SharedDIB.cpp firefox-3.6.4+build1+nobinonly/mozilla/gfx/ipc/SharedDIB.cpp --- firefox-3.6.3+nobinonly/mozilla/gfx/ipc/SharedDIB.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/ipc/SharedDIB.cpp 2010-04-16 17:31:48.000000000 +0100 @@ -0,0 +1,118 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jim Mathies + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "SharedDIB.h" + +namespace mozilla { +namespace gfx { + +SharedDIB::SharedDIB() : + mShMem(nsnull) +{ +} + +SharedDIB::~SharedDIB() +{ + Close(); +} + +nsresult +SharedDIB::Create(PRUint32 aSize) +{ + Close(); + + mShMem = new base::SharedMemory(); + if (!mShMem || !mShMem->Create("", false, false, aSize)) + return NS_ERROR_OUT_OF_MEMORY; + + // Map the entire section + if (!mShMem->Map(0)) + return NS_ERROR_FAILURE; + + return NS_OK; +} + +bool +SharedDIB::IsValid() +{ + if (!mShMem) + return false; + + return mShMem->IsHandleValid(mShMem->handle()); +} + +nsresult +SharedDIB::Close() +{ + if (mShMem) + delete mShMem; + + mShMem = nsnull; + + return NS_OK; +} + +nsresult +SharedDIB::Attach(Handle aHandle, PRUint32 aSize) +{ + Close(); + + mShMem = new base::SharedMemory(aHandle, false); + if(!mShMem) + return NS_ERROR_OUT_OF_MEMORY; + + if (!mShMem->Map(aSize)) + return NS_ERROR_FAILURE; + + return NS_OK; +} + +nsresult +SharedDIB::ShareToProcess(base::ProcessHandle aChildProcess, Handle *aChildHandle) +{ + if (!mShMem) + return NS_ERROR_UNEXPECTED; + + if (!mShMem->ShareToProcess(aChildProcess, aChildHandle)) + return NS_ERROR_UNEXPECTED; + + return NS_OK; +} + +} // gfx +} // mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/ipc/SharedDIB.h firefox-3.6.4+build1+nobinonly/mozilla/gfx/ipc/SharedDIB.h --- firefox-3.6.3+nobinonly/mozilla/gfx/ipc/SharedDIB.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/ipc/SharedDIB.h 2010-04-16 17:31:48.000000000 +0100 @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jim Mathies + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef gfx_SharedDIB_h__ +#define gfx_SharedDIB_h__ + +#include "base/shared_memory.h" +#include "prtypes.h" +#include "nscore.h" + +namespace mozilla { +namespace gfx { + +class SharedDIB +{ +public: + typedef base::SharedMemoryHandle Handle; + +public: + SharedDIB(); + ~SharedDIB(); + + // Create and allocate a new shared dib. + nsresult Create(PRUint32 aSize); + + // Destroy or release resources associated with this dib. + nsresult Close(); + + // Returns true if this object contains a valid dib. + bool IsValid(); + + // Wrap a new shared dib around allocated shared memory. Note aHandle must point + // to a memory section large enough to hold a dib of size aSize, otherwise this + // will fail. + nsresult Attach(Handle aHandle, PRUint32 aSize); + + // Returns a SharedMemoryHandle suitable for sharing with another process. + nsresult ShareToProcess(base::ProcessHandle aChildProcess, Handle *aChildHandle); + +protected: + base::SharedMemory *mShMem; +}; + +} // gfx +} // mozilla + +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/ipc/SharedDIBWin.cpp firefox-3.6.4+build1+nobinonly/mozilla/gfx/ipc/SharedDIBWin.cpp --- firefox-3.6.3+nobinonly/mozilla/gfx/ipc/SharedDIBWin.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/ipc/SharedDIBWin.cpp 2010-04-16 17:31:48.000000000 +0100 @@ -0,0 +1,161 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jim Mathies + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "SharedDIBWin.h" +#include "nsMathUtils.h" +#include "nsDebug.h" + +namespace mozilla { +namespace gfx { + +SharedDIBWin::SharedDIBWin() : + mSharedHdc(nsnull) + , mSharedBmp(nsnull) + , mOldObj(nsnull) +{ +} + +SharedDIBWin::~SharedDIBWin() +{ + Close(); +} + +nsresult +SharedDIBWin::Close() +{ + if (mSharedHdc && mOldObj) + ::SelectObject(mSharedHdc, mOldObj); + + if (mSharedHdc) + ::DeleteObject(mSharedHdc); + + if (mSharedBmp) + ::DeleteObject(mSharedBmp); + + mSharedHdc = NULL; + mOldObj = mSharedBmp = NULL; + + SharedDIB::Close(); + + return NS_OK; +} + +nsresult +SharedDIBWin::Create(HDC aHdc, PRUint32 aWidth, PRUint32 aHeight, PRUint32 aDepth) +{ + Close(); + + // create the offscreen shared dib + BITMAPINFOHEADER bmih; + PRUint32 size = SetupBitmapHeader(aWidth, aHeight, aDepth, &bmih); + + nsresult rv = SharedDIB::Create(size); + if (NS_FAILED(rv)) + return rv; + + if (NS_FAILED(SetupSurface(aHdc, &bmih))) { + Close(); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +SharedDIBWin::Attach(Handle aHandle, PRUint32 aWidth, PRUint32 aHeight, PRUint32 aDepth) +{ + Close(); + + BITMAPINFOHEADER bmih; + SetupBitmapHeader(aWidth, aHeight, aDepth, &bmih); + + nsresult rv = SharedDIB::Attach(aHandle, 0); + if (NS_FAILED(rv)) + return rv; + + if (NS_FAILED(SetupSurface(NULL, &bmih))) { + Close(); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +PRUint32 +SharedDIBWin::SetupBitmapHeader(PRUint32 aWidth, PRUint32 aHeight, PRUint32 aDepth, BITMAPINFOHEADER *aHeader) +{ + NS_ASSERTION(aDepth == 32, "Invalid SharedDIBWin depth"); + + memset((void*)aHeader, 0, sizeof(BITMAPINFOHEADER)); + aHeader->biSize = sizeof(BITMAPINFOHEADER); + aHeader->biWidth = aWidth; + aHeader->biHeight = aHeight; + aHeader->biPlanes = 1; + aHeader->biBitCount = aDepth; + aHeader->biCompression = BI_RGB; + + // deal better with varying depths. (we currently only ask for 32 bit) + return (sizeof(BITMAPINFOHEADER) + (aHeader->biHeight * aHeader->biWidth * (PRUint32)NS_ceil(aDepth/8))); +} + +nsresult +SharedDIBWin::SetupSurface(HDC aHdc, BITMAPINFOHEADER *aHdr) +{ + mSharedHdc = ::CreateCompatibleDC(aHdc); + + if (!mSharedHdc) + return NS_ERROR_FAILURE; + + void* ppvBits = nsnull; + mSharedBmp = ::CreateDIBSection(mSharedHdc, + (BITMAPINFO*)aHdr, + DIB_RGB_COLORS, + (void**)&ppvBits, + mShMem->handle(), + (unsigned long)sizeof(BITMAPINFOHEADER)); + if (!mSharedBmp) + return NS_ERROR_FAILURE; + + mOldObj = SelectObject(mSharedHdc, mSharedBmp); + + return NS_OK; +} + + +} // gfx +} // mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/ipc/SharedDIBWin.h firefox-3.6.4+build1+nobinonly/mozilla/gfx/ipc/SharedDIBWin.h --- firefox-3.6.3+nobinonly/mozilla/gfx/ipc/SharedDIBWin.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/ipc/SharedDIBWin.h 2010-04-16 17:31:48.000000000 +0100 @@ -0,0 +1,82 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Jim Mathies + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef gfx_SharedDIBWin_h__ +#define gfx_SharedDIBWin_h__ + +#include + +#include "SharedDIB.h" + +namespace mozilla { +namespace gfx { + +class SharedDIBWin : public SharedDIB +{ +public: + SharedDIBWin(); + ~SharedDIBWin(); + + // Allocate a new win32 dib section compatible with an hdc. The dib will + // be selected into the hdc on return. + nsresult Create(HDC aHdc, PRUint32 aWidth, PRUint32 aHeight, PRUint32 aDepth); + + // Wrap a dib section around an existing shared memory object. aHandle should + // point to a section large enough for the dib's memory, otherwise this call + // will fail. + nsresult Attach(Handle aHandle, PRUint32 aWidth, PRUint32 aHeight, PRUint32 aDepth); + + // Destroy or release resources associated with this dib. + nsresult Close(); + + // Return the HDC of the shared dib. + HDC GetHDC() { return mSharedHdc; } + +private: + HDC mSharedHdc; + HBITMAP mSharedBmp; + HGDIOBJ mOldObj; + + PRUint32 SetupBitmapHeader(PRUint32 aWidth, PRUint32 aHeight, PRUint32 aDepth, BITMAPINFOHEADER *aHeader); + nsresult SetupSurface(HDC aHdc, BITMAPINFOHEADER *aHdr); +}; + +} // gfx +} // mozilla + +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/gfx/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/gfx/Makefile.in 2010-04-02 16:58:06.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/Makefile.in 2010-04-16 17:31:47.000000000 +0100 @@ -50,6 +50,10 @@ DIRS += thebes public idl src qcms +ifdef MOZ_IPC +DIRS += ipc +endif + ifdef ENABLE_TESTS ifndef MOZ_ENABLE_LIBXUL TOOL_DIRS += tests diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/src/thebes/nsSystemFontsGTK2.h firefox-3.6.4+build1+nobinonly/mozilla/gfx/src/thebes/nsSystemFontsGTK2.h --- firefox-3.6.3+nobinonly/mozilla/gfx/src/thebes/nsSystemFontsGTK2.h 2010-04-02 16:58:07.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/src/thebes/nsSystemFontsGTK2.h 2010-04-16 17:31:48.000000000 +0100 @@ -39,7 +39,8 @@ #ifndef _NS_SYSTEMFONTSGTK2_H_ #define _NS_SYSTEMFONTSGTK2_H_ -#include +#include "gfxFont.h" +typedef struct _GtkWidget GtkWidget; class nsSystemFontsGTK2 { diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/src/thebes/nsThebesDeviceContext.cpp firefox-3.6.4+build1+nobinonly/mozilla/gfx/src/thebes/nsThebesDeviceContext.cpp --- firefox-3.6.3+nobinonly/mozilla/gfx/src/thebes/nsThebesDeviceContext.cpp 2010-04-02 16:58:07.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/src/thebes/nsThebesDeviceContext.cpp 2010-04-16 17:31:48.000000000 +0100 @@ -57,24 +57,6 @@ #include "nsIView.h" #include "nsILookAndFeel.h" -#ifdef MOZ_ENABLE_GTK2 -// for getenv -#include -// for round -#include - -#include -#include - -#include "nsFont.h" - -#include -#ifdef MOZ_X11 -#include -#endif /* MOZ_X11 */ -#include -#endif /* GTK2 */ - #include "gfxImageSurface.h" #ifdef MOZ_ENABLE_GTK2 @@ -109,15 +91,6 @@ #error Need to declare gSystemFonts! #endif -#if defined(MOZ_ENABLE_GTK2) && defined(MOZ_X11) -extern "C" { -static int x11_error_handler (Display *dpy, XErrorEvent *err) { - NS_ASSERTION(PR_FALSE, "X Error"); - return 0; -} -} -#endif - #ifdef PR_LOGGING PRLogModuleInfo* gThebesGFXLog = nsnull; #endif @@ -735,15 +708,6 @@ if (obs) obs->AddObserver(this, "memory-pressure", PR_TRUE); -#if defined(MOZ_ENABLE_GTK2) && defined(MOZ_X11) - if (getenv ("MOZ_X_SYNC")) { - PR_LOG (gThebesGFXLog, PR_LOG_DEBUG, ("+++ Enabling XSynchronize\n")); - XSynchronize (gdk_x11_get_default_xdisplay(), True); - XSetErrorHandler(x11_error_handler); - } - -#endif - mScreenManager = do_GetService("@mozilla.org/gfx/screenmanager;1"); return NS_OK; diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/thebes/public/gfxWindowsFonts.h firefox-3.6.4+build1+nobinonly/mozilla/gfx/thebes/public/gfxWindowsFonts.h --- firefox-3.6.3+nobinonly/mozilla/gfx/thebes/public/gfxWindowsFonts.h 2010-04-02 16:58:07.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/thebes/public/gfxWindowsFonts.h 2010-04-16 17:31:48.000000000 +0100 @@ -113,7 +113,9 @@ public: FontEntry(const nsAString& aFaceName, gfxWindowsFontType aFontType, PRBool aItalic, PRUint16 aWeight, gfxUserFontData *aUserFontData) : - gfxFontEntry(aFaceName), mFontType(aFontType), + gfxFontEntry(aFaceName), + mWindowsFamily(0), mWindowsPitch(0), + mFontType(aFontType), mForceGDI(PR_FALSE), mUnknownCMAP(PR_FALSE), mUnicodeFont(PR_FALSE), mSymbolFont(PR_FALSE), mCharset(0), mUnicodeRanges(0) diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/thebes/public/gfxWindowsNativeDrawing.h firefox-3.6.4+build1+nobinonly/mozilla/gfx/thebes/public/gfxWindowsNativeDrawing.h --- firefox-3.6.3+nobinonly/mozilla/gfx/thebes/public/gfxWindowsNativeDrawing.h 2010-04-02 16:58:07.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/thebes/public/gfxWindowsNativeDrawing.h 2010-04-16 17:31:48.000000000 +0100 @@ -111,6 +111,9 @@ /* Returns PR_TRUE if the native drawing should be executed again */ PRBool ShouldRenderAgain(); + /* Returns PR_TRUE if double pass alpha extraction is taking place. */ + PRBool IsDoublePass(); + /* Places the result to the context, if necessary */ void PaintToContext(); diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/thebes/src/gfxMacPlatformFontList.mm firefox-3.6.4+build1+nobinonly/mozilla/gfx/thebes/src/gfxMacPlatformFontList.mm --- firefox-3.6.3+nobinonly/mozilla/gfx/thebes/src/gfxMacPlatformFontList.mm 2010-04-02 16:58:08.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/thebes/src/gfxMacPlatformFontList.mm 2010-04-16 17:31:48.000000000 +0100 @@ -57,6 +57,20 @@ #include #include +class nsAutoreleasePool { +public: + nsAutoreleasePool() + { + mLocalPool = [[NSAutoreleasePool alloc] init]; + } + ~nsAutoreleasePool() + { + [mLocalPool release]; + } +private: + NSAutoreleasePool *mLocalPool; +}; + // font info loader constants static const PRUint32 kDelayBeforeLoadingCmaps = 8 * 1000; // 8secs static const PRUint32 kIntervalBetweenLoadingCmaps = 150; // 150ms @@ -288,6 +302,8 @@ nsresult MacOSFontEntry::GetFontTable(PRUint32 aTableTag, nsTArray& aBuffer) { + nsAutoreleasePool localPool; + ATSFontRef fontRef = GetFontRef(); if (fontRef == (ATSFontRef)kATSUInvalidFontID) return NS_ERROR_FAILURE; @@ -325,6 +341,8 @@ void gfxMacFontFamily::LocalizedName(nsAString& aLocalizedName) { + nsAutoreleasePool localPool; + if (!HasOtherFamilyNames()) { aLocalizedName = mName; return; @@ -365,6 +383,8 @@ void gfxSingleFaceMacFontFamily::LocalizedName(nsAString& aLocalizedName) { + nsAutoreleasePool localPool; + if (!HasOtherFamilyNames()) { aLocalizedName = mName; return; @@ -417,6 +437,8 @@ void gfxMacPlatformFontList::InitFontList() { + nsAutoreleasePool localPool; + ATSGeneration currentGeneration = ::ATSGetGeneration(); // need to ignore notifications after adding each font @@ -696,6 +718,8 @@ gfxFontEntry* gfxMacPlatformFontList::GetDefaultFont(const gfxFontStyle* aStyle, PRBool& aNeedsBold) { + nsAutoreleasePool localPool; + NSString *defaultFamily = [[NSFont userFontOfSize:aStyle->size] familyName]; nsAutoString familyName; @@ -717,6 +741,8 @@ gfxMacPlatformFontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry, const nsAString& aFontName) { + nsAutoreleasePool localPool; + NSString *faceName = GetNSStringForString(aFontName); // first lookup a single face based on postscript name diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/thebes/src/gfxWindowsFonts.cpp firefox-3.6.4+build1+nobinonly/mozilla/gfx/thebes/src/gfxWindowsFonts.cpp --- firefox-3.6.3+nobinonly/mozilla/gfx/thebes/src/gfxWindowsFonts.cpp 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/thebes/src/gfxWindowsFonts.cpp 2010-04-16 17:31:49.000000000 +0100 @@ -1825,15 +1825,18 @@ mFontSelected(PR_FALSE), mForceGDIPlace(PR_FALSE) { NS_ASSERTION(mMaxGlyphs < 65535, "UniscribeItem is too big, ScriptShape() will fail!"); - mGlyphs.SetLength(mMaxGlyphs); - mClusters.SetLength(mItemLength + 1); - mAttr.SetLength(mMaxGlyphs); } ~UniscribeItem() { free(mAlternativeString); } + PRBool AllocateBuffers() { + return (mGlyphs.SetLength(mMaxGlyphs) && + mClusters.SetLength(mItemLength + 1) && + mAttr.SetLength(mMaxGlyphs)); + } + /* possible return values: * S_OK - things succeeded * GDI_ERROR - things failed to shape. Might want to try again after calling DisableShaping() @@ -1870,8 +1873,10 @@ if (rv == E_OUTOFMEMORY) { mMaxGlyphs *= 2; - mGlyphs.SetLength(mMaxGlyphs); - mAttr.SetLength(mMaxGlyphs); + if (!mGlyphs.SetLength(mMaxGlyphs) || + !mAttr.SetLength(mMaxGlyphs)) { + return E_OUTOFMEMORY; + } continue; } @@ -2009,8 +2014,10 @@ } HRESULT Place() { - mOffsets.SetLength(mNumGlyphs); - mAdvances.SetLength(mNumGlyphs); + if (!mOffsets.SetLength(mNumGlyphs) || + !mAdvances.SetLength(mNumGlyphs)) { + return E_OUTOFMEMORY; + } if (mForceGDIPlace) return PlaceGDI(); @@ -2414,7 +2421,10 @@ mItems[i+1].iCharPos - mItems[i].iCharPos, &mItems[i], aGroup); - + if (!item->AllocateBuffers()) { + delete item; + return nsnull; + } return item; } @@ -2646,9 +2656,13 @@ int numItems = us.Itemize(); for (int i = 0; i < numItems; ++i) { - SaveDC(aDC); - nsAutoPtr item(us.GetItem(i, this)); + if (!item) { + // failed to initialize the item; give up (out of memory) + break; + } + + SaveDC(aDC); // jtdfix - push this into the pref handling code?? mItemLangGroup = nsnull; diff -Nru firefox-3.6.3+nobinonly/mozilla/gfx/thebes/src/gfxWindowsNativeDrawing.cpp firefox-3.6.4+build1+nobinonly/mozilla/gfx/thebes/src/gfxWindowsNativeDrawing.cpp --- firefox-3.6.3+nobinonly/mozilla/gfx/thebes/src/gfxWindowsNativeDrawing.cpp 2010-04-02 16:58:08.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/gfx/thebes/src/gfxWindowsNativeDrawing.cpp 2010-04-16 17:31:49.000000000 +0100 @@ -206,6 +206,22 @@ } PRBool +gfxWindowsNativeDrawing::IsDoublePass() +{ + // this is the same test we use in BeginNativeDrawing. + nsRefPtr surf = mContext->CurrentSurface(&mDeviceOffset.x, &mDeviceOffset.y); + if (!surf || surf->CairoStatus()) + return false; + if ((surf->GetType() == gfxASurface::SurfaceTypeWin32 || + surf->GetType() == gfxASurface::SurfaceTypeWin32Printing) && + (surf->GetContentType() != gfxASurface::CONTENT_COLOR || + (surf->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA && + !(mNativeDrawFlags & CAN_DRAW_TO_COLOR_ALPHA)))) + return PR_TRUE; + return PR_FALSE; +} + +PRBool gfxWindowsNativeDrawing::ShouldRenderAgain() { switch (mRenderState) { diff -Nru firefox-3.6.3+nobinonly/mozilla/.hgtags firefox-3.6.4+build1+nobinonly/mozilla/.hgtags --- firefox-3.6.3+nobinonly/mozilla/.hgtags 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/.hgtags 2010-04-16 17:31:29.000000000 +0100 @@ -41,15 +41,3 @@ c60105da01a09ff3b902744879a58247f5717029 FIREFOX_3_6b1_BUILD1 3596945fe21ef9974367519ec72d872e4435041f FENNEC_1_0_BUILD1 3596945fe21ef9974367519ec72d872e4435041f FENNEC_1_0_RELEASE -f14062c981ba6e70ebc85f751f5f38fa41de732d FIREFOX_3_6_2_BUILD1 -f14062c981ba6e70ebc85f751f5f38fa41de732d FIREFOX_3_6_2_RELEASE -827a6883442f5bd110e66616fc86df732b05b2d6 FIREFOX_3_6_2_BUILD2 -f14062c981ba6e70ebc85f751f5f38fa41de732d FIREFOX_3_6_2_RELEASE -827a6883442f5bd110e66616fc86df732b05b2d6 FIREFOX_3_6_2_RELEASE -cd857b3b0e33449cd97b98c00c058aa147171114 FIREFOX_3_6_2_BUILD3 -827a6883442f5bd110e66616fc86df732b05b2d6 FIREFOX_3_6_2_RELEASE -cd857b3b0e33449cd97b98c00c058aa147171114 FIREFOX_3_6_2_RELEASE -f14062c981ba6e70ebc85f751f5f38fa41de732d FIREFOX_3_6_2_BUILD1 -d6e028dc1b68ed3e716fbc2d5ca3ea52e4f9409a FIREFOX_3_6_2_BUILD1 -cd857b3b0e33449cd97b98c00c058aa147171114 FIREFOX_3_6_2_RELEASE -d6e028dc1b68ed3e716fbc2d5ca3ea52e4f9409a FIREFOX_3_6_2_RELEASE diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/app/defs.mk firefox-3.6.4+build1+nobinonly/mozilla/ipc/app/defs.mk --- firefox-3.6.3+nobinonly/mozilla/ipc/app/defs.mk 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/app/defs.mk 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,37 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (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.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla IPC. +# +# The Initial Developer of the Original Code is +# Chris Jones . +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +MOZ_CHILD_PROCESS_NAME := plugin-container$(BIN_SUFFIX) diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/app/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/ipc/app/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/ipc/app/Makefile.in 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/app/Makefile.in 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,111 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (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.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Plugin App. +# +# The Initial Developer of the Original Code is +# Ben Turner . +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Initial Developer. All Rights Reserved. +# Chris Jones +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = ipc + +include $(topsrcdir)/ipc/app/defs.mk +PROGRAM = $(MOZ_CHILD_PROCESS_NAME) + +CPPSRCS = \ + MozillaRuntimeMain.cpp \ + $(NULL) + +LIBS += \ + $(XPCOM_LIBS) \ + $(NSPR_LIBS) \ + $(NULL) + +LOCAL_INCLUDES += -I$(topsrcdir)/toolkit/xre + +ifndef MOZ_WINCONSOLE +ifdef MOZ_DEBUG +MOZ_WINCONSOLE = 1 +else +MOZ_WINCONSOLE = 0 +endif +endif + +# This switches $(INSTALL) to copy mode, like $(SYSINSTALL), so things that +# shouldn't get 755 perms need $(IFLAGS1) for either way of calling nsinstall. +NSDISTMODE = copy + +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk + +ifdef _MSC_VER +# Always enter a Windows program through wmain, whether or not we're +# a console application. +ifdef WINCE +WIN32_EXE_LDFLAGS += -ENTRY:mainWCRTStartup +else +WIN32_EXE_LDFLAGS += -ENTRY:wmainCRTStartup +endif +endif + +ifdef WINCE +EXTRA_DSO_LDOPTS += $(call EXPAND_LIBNAME,corelibc) +endif + +ifeq ($(OS_ARCH),WINNT) +OS_LIBS += $(call EXPAND_LIBNAME,comctl32 comdlg32 uuid shell32 ole32 oleaut32 version winspool) +OS_LIBS += $(call EXPAND_LIBNAME,usp10 msimg32) +endif + +include $(topsrcdir)/config/rules.mk + +ifeq ($(OS_ARCH),WINNT) +# +# Control the default heap size. +# This is the heap returned by GetProcessHeap(). +# As we use the CRT heap, the default size is too large and wastes VM. +# +# The default heap size is 1MB on Win32. +# The heap will grow if need be. +# +# Set it to 256k. See bug 127069. +# +ifndef GNU_CC +LDFLAGS += /HEAP:0x40000 +endif +endif diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/app/module.ver firefox-3.6.4+build1+nobinonly/mozilla/ipc/app/module.ver --- firefox-3.6.3+nobinonly/mozilla/ipc/app/module.ver 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/app/module.ver 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,6 @@ +WIN32_MODULE_COMPANYNAME=Mozilla Corporation +WIN32_MODULE_PRODUCTVERSION=@MOZ_APP_WINVERSION@ +WIN32_MODULE_PRODUCTVERSION_STRING=@MOZ_APP_VERSION@ +WIN32_MODULE_DESCRIPTION=Plugin Container for @MOZ_APP_DISPLAYNAME@ +WIN32_MODULE_PRODUCTNAME=@MOZ_APP_DISPLAYNAME@ +WIN32_MODULE_NAME=@MOZ_APP_DISPLAYNAME@ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/app/MozillaRuntimeMain.cpp firefox-3.6.4+build1+nobinonly/mozilla/ipc/app/MozillaRuntimeMain.cpp --- firefox-3.6.3+nobinonly/mozilla/ipc/app/MozillaRuntimeMain.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/app/MozillaRuntimeMain.cpp 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,91 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: sw=4 ts=4 et : + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Plugin App. + * + * The Initial Developer of the Original Code is + * Ben Turner . + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones . + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsXPCOM.h" +#include "nsXULAppAPI.h" + +// FIXME/cjones testing +#if !defined(OS_WIN) +#include +#endif + +#ifdef XP_WIN +#include +// we want a wmain entry point +#include "nsWindowsWMain.cpp" +#endif + +int +main(int argc, char* argv[]) +{ +#if defined(MOZ_CRASHREPORTER) + if (argc < 2) + return 1; + const char* const crashReporterArg = argv[--argc]; + +# if defined(XP_WIN) + // on windows, |crashReporterArg| is the named pipe on which the + // server is listening for requests, or "-" if crash reporting is + // disabled. + if (0 != strcmp("-", crashReporterArg) + && !XRE_SetRemoteExceptionHandler(crashReporterArg)) + return 1; +# elif defined(OS_LINUX) + // on POSIX, |crashReporterArg| is "true" if crash reporting is + // enabled, false otherwise + if (0 != strcmp("false", crashReporterArg) + && !XRE_SetRemoteExceptionHandler(NULL)) + return 1; +# else +# error "OOP crash reporting unsupported on this platform" +# endif +#endif // if defined(MOZ_CRASHREPORTER) + +#if defined(XP_WIN) && defined(DEBUG_bent) + MessageBox(NULL, L"Hi", L"Hi", MB_OK); +#endif + + GeckoProcessType proctype = + XRE_StringToChildProcessType(argv[argc - 1]); + + nsresult rv = XRE_InitChildProcess(argc - 1, argv, proctype); + NS_ENSURE_SUCCESS(rv, 1); + + return 0; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/app/plugin-container.exe.manifest firefox-3.6.4+build1+nobinonly/mozilla/ipc/app/plugin-container.exe.manifest --- firefox-3.6.3+nobinonly/mozilla/ipc/app/plugin-container.exe.manifest 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/app/plugin-container.exe.manifest 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,31 @@ + + + +Firefox Runtime + + + + + + + + + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/base/file_version_info_linux.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/base/file_version_info_linux.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/base/file_version_info_linux.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/base/file_version_info_linux.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,26 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FILE_VERSION_INFO_LINUX_H_ +#define BASE_FILE_VERSION_INFO_LINUX_H_ + +#define COMPANY_NAME L"@COMPANY_FULLNAME@" +#define FILE_DESCRIPTION L"@PRODUCT_FULLNAME@" +#define FILE_VERSION L"@MAJOR@.@MINOR@.@BUILD@.@PATCH@" +#define LEGAL_COPYRIGHT L"@COPYRIGHT@" +#define PRODUCT_NAME L"@PRODUCT_FULLNAME@" +#define PRODUCT_VERSION L"@MAJOR@.@MINOR@.@BUILD@.@PATCH@" +#define COMPANY_SHORT_NAME L"@COMPANY_SHORTNAME@" +#define PRODUCT_SHORT_NAME L"@PRODUCT_SHORTNAME@" +#define LAST_CHANGE L"@LASTCHANGE@" +#define OFFICIAL_BUILD 1 +// TODO(mmoss) Do these have values for Linux? +#define INTERNAL_NAME L"" +#define ORIGINAL_FILENAME L"" +#define PRIVATE_BUILD L"" +#define SPECIAL_BUILD L"" +#define COMMENTS L"" +#define LEGAL_TRADEMARKS L"" + +#endif // BASE_FILE_VERSION_INFO_LINUX_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/chromium-config.mk firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/chromium-config.mk --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/chromium-config.mk 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/chromium-config.mk 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,115 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (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.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the Mozilla platform. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation . +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +ifndef INCLUDED_CONFIG_MK +$(error Must include config.mk before this file.) +endif + +ifdef CHROMIUM_CONFIG_INCLUDED +$(error Must not include chromium-config.mk twice.) +endif + +CHROMIUM_CONFIG_INCLUDED = 1 + +EXTRA_DEPS += $(topsrcdir)/ipc/chromium/chromium-config.mk + +ifdef MOZ_IPC # { + +DEFINES += \ + -DEXCLUDE_SKIA_DEPENDENCIES \ + -DCHROMIUM_MOZILLA_BUILD \ + $(NULL) + +LOCAL_INCLUDES += \ + -I$(topsrcdir)/ipc/chromium/src \ + -I$(topsrcdir)/ipc/glue \ + -I$(DEPTH)/ipc/ipdl/_ipdlheaders \ + $(NULL) + +ifeq ($(OS_ARCH),Darwin) # { + +OS_MACOSX = 1 +OS_POSIX = 1 + +DEFINES += \ + -DOS_MACOSX=1 \ + -DOS_POSIX=1 \ + $(NULL) + +else # } { +ifeq ($(OS_ARCH),WINNT) # { +OS_LIBS += $(call EXPAND_LIBNAME,psapi shell32 dbghelp) + +OS_WIN = 1 + +DEFINES += \ + -DUNICODE \ + -D_UNICODE \ + -DNOMINMAX \ + -D_CRT_RAND_S \ + -DCERT_CHAIN_PARA_HAS_EXTRA_FIELDS \ + -D_SECURE_ATL \ + -D_HAS_TR1=0 \ + -DCHROMIUM_BUILD \ + -DU_STATIC_IMPLEMENTATION \ + -DCOMPILER_MSVC \ + -DOS_WIN=1 \ + -DWIN32 \ + -D_WIN32 \ + -D_WINDOWS \ + -DWIN32_LEAN_AND_MEAN \ + $(NULL) + +else # } { + +OS_LINUX = 1 +OS_POSIX = 1 + +DEFINES += \ + -DOS_LINUX=1 \ + -DOS_POSIX=1 \ + $(NULL) + +# NB: to stop gcc warnings about exporting template instantiation +OS_CXXFLAGS := $(filter-out -pedantic,$(OS_CXXFLAGS)) + +OS_CXXFLAGS += $(TK_CFLAGS) +OS_CFLAGS += $(TK_CFLAGS) + +endif # } +endif # } + +endif # } \ No newline at end of file diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/Makefile.in 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/Makefile.in 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,310 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (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.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is the Mozilla platform. +# +# The Initial Developer of the Original Code is +# the Mozilla Foundation . +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +OS_CXXFLAGS := $(filter-out -fshort-wchar,$(OS_CXXFLAGS)) + +LIBRARY_NAME = chromium_s +FORCE_STATIC_LIB = 1 +LIBXUL_LIBRARY = 1 +EXPORT_LIBRARY = 1 + +ENABLE_CXX_EXCEPTIONS = 1 +ACDEFINES = + +vpath %.c \ + $(srcdir)/src/third_party/libevent \ + $(NULL) + +vpath %.cc \ + $(srcdir)/src/base \ + $(srcdir)/src/base/third_party/dmg_fp \ + $(srcdir)/src/base/third_party/nspr \ + $(srcdir)/src/base/third_party/nss \ + $(srcdir)/src/chrome/common \ + $(NULL) + +vpath %.mm \ + $(srcdir)/src/base \ + $(srcdir)/src/chrome/common \ + $(NULL) + +# TODO: remove this hack when we switch to GCC 4.3! GCC 4.1 +# instantiates template declarations in wrapped system headers with +# the wrong visibility, which is causing the Linux tinderboxen to die in +# std::string internals. +VISIBILITY_FLAGS= + +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk + +CPPSRCS += \ + dtoa.cc \ + g_fmt.cc \ + prtime.cc \ + at_exit.cc \ + base_paths.cc \ + base_switches.cc \ + command_line.cc \ + debug_util.cc \ + file_path.cc \ + file_util.cc \ + histogram.cc \ + lazy_instance.cc \ + lock.cc \ + logging.cc \ + memory_debug.cc \ + message_loop.cc \ + message_pump_default.cc \ + non_thread_safe.cc \ + path_service.cc \ + pickle.cc \ + rand_util.cc \ + ref_counted.cc \ + revocable_store.cc \ + scoped_temp_dir.cc \ + sha2.cc \ + simple_thread.cc \ + stats_table.cc \ + string_escape.cc \ + string_piece.cc \ + string_util.cc \ + system_monitor.cc \ + thread.cc \ + thread_collision_warner.cc \ + time.cc \ + timer.cc \ + trace_event.cc \ + tracked.cc \ + tracked_objects.cc \ + child_process.cc \ + child_process_host.cc \ + child_process_info.cc \ + child_thread.cc \ + chrome_counters.cc \ + chrome_paths.cc \ + chrome_switches.cc \ + debug_flags.cc \ + env_vars.cc \ + ipc_channel_proxy.cc \ + ipc_logging.cc \ + ipc_message.cc \ + ipc_message_utils.cc \ + ipc_sync_channel.cc \ + ipc_sync_message.cc \ + message_router.cc \ + notification_service.cc \ + task_queue.cc \ + $(NULL) + +ifdef OS_WIN # { + +CPPSRCS += \ + base_paths_win.cc \ + cpu.cc \ + condition_variable_win.cc \ + debug_on_start.cc \ + debug_util_win.cc \ + event_recorder.cc \ + file_util_win.cc \ + file_version_info.cc \ + lock_impl_win.cc \ + idle_timer.cc \ + message_pump_win.cc \ + object_watcher.cc \ + platform_file_win.cc \ + platform_thread_win.cc \ + process_util_win.cc \ + process_win.cc \ + rand_util_win.cc \ + registry.cc \ + shared_memory_win.cc \ + sys_info_win.cc \ + sys_string_conversions_win.cc \ + system_monitor_win.cc \ + thread_local_storage_win.cc \ + thread_local_win.cc \ + time_win.cc \ + waitable_event_watcher_win.cc \ + waitable_event_win.cc \ + win_util.cc \ + chrome_paths_win.cc \ + ipc_channel_win.cc \ + process_watcher_win.cc \ + transport_dib_win.cc \ + $(NULL) + +endif # } OS_WIN + +ifdef OS_POSIX # { + +CPPSRCS += \ + sha512.cc \ + condition_variable_posix.cc \ + debug_util_posix.cc \ + event_recorder_stubs.cc \ + file_descriptor_shuffle.cc \ + file_util_posix.cc \ + lock_impl_posix.cc \ + message_pump_libevent.cc \ + platform_file_posix.cc \ + platform_thread_posix.cc \ + process_posix.cc \ + process_util_posix.cc \ + rand_util_posix.cc \ + shared_memory_posix.cc \ + string16.cc \ + system_monitor_posix.cc \ + sys_info_posix.cc \ + thread_local_posix.cc \ + thread_local_storage_posix.cc \ + waitable_event_posix.cc \ + waitable_event_watcher_posix.cc \ + file_descriptor_set_posix.cc \ + ipc_channel_posix.cc \ + process_watcher_posix_sigchld.cc \ + $(NULL) + +endif # } OS_POSIX + +ifdef OS_MACOSX # { + +CMMSRCS += \ + base_paths_mac.mm \ + file_util_mac.mm \ + file_version_info_mac.mm \ + mac_util.mm \ + message_pump_mac.mm \ + platform_thread_mac.mm \ + process_util_mac.mm \ + scoped_nsautorelease_pool.mm \ + sys_string_conversions_mac.mm \ + worker_pool_mac.mm \ + chrome_paths_mac.mm \ + mach_ipc_mac.mm \ + platform_util_mac.mm \ + $(NULL) + +CPPSRCS += \ + data_pack.cc \ + debug_util_mac.cc \ + hmac_mac.cc \ + idle_timer.cc \ + sys_info_mac.cc \ + time_mac.cc \ + mach_message_source_mac.cc \ + transport_dib_mac.cc \ + $(NULL) + +endif # } OS_MACOSX + +ifdef OS_LINUX # { + +CPPSRCS += \ + atomicops_internals_x86_gcc.cc \ + base_paths_linux.cc \ + data_pack.cc \ + file_util_linux.cc \ + file_version_info_linux.cc \ + idle_timer_none.cc \ + process_util_linux.cc \ + time_posix.cc \ + $(NULL) + +ifdef MOZ_ENABLE_GTK2 +CPPSRCS += \ + message_pump_glib.cc \ + $(NULL) +endif + +ifdef MOZ_ENABLE_QT +MOCSRCS = \ + moc_message_pump_qt.cc \ + $(NULL) + +CPPSRCS += \ + $(MOCSRCS) \ + message_pump_qt.cc \ + $(NULL) +endif + +endif # } OS_LINUX + +# libevent + +ifdef OS_POSIX # { + +LOCAL_INCLUDES += -I$(srcdir)/src/third_party/libevent +DEFINES += -DHAVE_CONFIG_H + +CSRCS += \ + buffer.c \ + evbuffer.c \ + evdns.c \ + event.c \ + event_tagging.c \ + evrpc.c \ + evutil.c \ + http.c \ + log.c \ + poll.c \ + select.c \ + signal.c \ + strlcpy.c \ + $(NULL) + +ifdef OS_MACOSX # { +LOCAL_INCLUDES += -I$(srcdir)/src/third_party/libevent/mac +CSRCS += kqueue.c +endif # } + +ifdef OS_LINUX # { +LOCAL_INCLUDES += -I$(srcdir)/src/third_party/libevent/linux +CSRCS += \ + epoll.c \ + epoll_sub.c \ + $(NULL) +endif # } + +endif # } + +include $(topsrcdir)/config/rules.mk diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/at_exit.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/at_exit.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/at_exit.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/at_exit.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,74 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/at_exit.h" +#include "base/logging.h" + +namespace base { + +// Keep a stack of registered AtExitManagers. We always operate on the most +// recent, and we should never have more than one outside of testing, when we +// use the shadow version of the constructor. We don't protect this for +// thread-safe access, since it will only be modified in testing. +static AtExitManager* g_top_manager = NULL; + +AtExitManager::AtExitManager() : next_manager_(NULL) { + DCHECK(!g_top_manager); + g_top_manager = this; +} + +AtExitManager::AtExitManager(bool shadow) : next_manager_(g_top_manager) { + DCHECK(shadow || !g_top_manager); + g_top_manager = this; +} + +AtExitManager::~AtExitManager() { + if (!g_top_manager) { + NOTREACHED() << "Tried to ~AtExitManager without an AtExitManager"; + return; + } + DCHECK(g_top_manager == this); + + ProcessCallbacksNow(); + g_top_manager = next_manager_; +} + +// static +void AtExitManager::RegisterCallback(AtExitCallbackType func, void* param) { + if (!g_top_manager) { + NOTREACHED() << "Tried to RegisterCallback without an AtExitManager"; + return; + } + + DCHECK(func); + + AutoLock lock(g_top_manager->lock_); + g_top_manager->stack_.push(CallbackAndParam(func, param)); +} + +// static +void AtExitManager::ProcessCallbacksNow() { + if (!g_top_manager) { + NOTREACHED() << "Tried to ProcessCallbacksNow without an AtExitManager"; + return; + } + + AutoLock lock(g_top_manager->lock_); + + while (!g_top_manager->stack_.empty()) { + CallbackAndParam callback_and_param = g_top_manager->stack_.top(); + g_top_manager->stack_.pop(); + + callback_and_param.func_(callback_and_param.param_); + } +} + +#ifdef CHROMIUM_MOZILLA_BUILD +// static +bool AtExitManager::AlreadyRegistered() { + return !!g_top_manager; +} +#endif + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/at_exit.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/at_exit.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/at_exit.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/at_exit.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,75 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_AT_EXIT_H_ +#define BASE_AT_EXIT_H_ + +#include + +#include "base/basictypes.h" +#include "base/lock.h" + +namespace base { + +// This class provides a facility similar to the CRT atexit(), except that +// we control when the callbacks are executed. Under Windows for a DLL they +// happen at a really bad time and under the loader lock. This facility is +// mostly used by base::Singleton. +// +// The usage is simple. Early in the main() or WinMain() scope create an +// AtExitManager object on the stack: +// int main(...) { +// base::AtExitManager exit_manager; +// +// } +// When the exit_manager object goes out of scope, all the registered +// callbacks and singleton destructors will be called. + +class AtExitManager { + protected: + // This constructor will allow this instance of AtExitManager to be created + // even if one already exists. This should only be used for testing! + // AtExitManagers are kept on a global stack, and it will be removed during + // destruction. This allows you to shadow another AtExitManager. + AtExitManager(bool shadow); + + public: + typedef void (*AtExitCallbackType)(void*); + + AtExitManager(); + + // The dtor calls all the registered callbacks. Do not try to register more + // callbacks after this point. + ~AtExitManager(); + + // Registers the specified function to be called at exit. The prototype of + // the callback function is void func(). + static void RegisterCallback(AtExitCallbackType func, void* param); + + // Calls the functions registered with RegisterCallback in LIFO order. It + // is possible to register new callbacks after calling this function. + static void ProcessCallbacksNow(); + +#ifdef CHROMIUM_MOZILLA_BUILD + static bool AlreadyRegistered(); +#endif + + private: + struct CallbackAndParam { + CallbackAndParam(AtExitCallbackType func, void* param) + : func_(func), param_(param) { } + AtExitCallbackType func_; + void* param_; + }; + + Lock lock_; + std::stack stack_; + AtExitManager* next_manager_; // Stack of managers to allow shadowing. + + DISALLOW_COPY_AND_ASSIGN(AtExitManager); +}; + +} // namespace base + +#endif // BASE_AT_EXIT_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/at_exit_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/at_exit_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/at_exit_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/at_exit_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,85 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/at_exit.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// Don't test the global AtExitManager, because asking it to process its +// AtExit callbacks can ruin the global state that other tests may depend on. +class ShadowingAtExitManager : public base::AtExitManager { + public: + ShadowingAtExitManager() : AtExitManager(true) {} +}; + +int g_test_counter_1 = 0; +int g_test_counter_2 = 0; + +void IncrementTestCounter1(void* unused) { + ++g_test_counter_1; +} + +void IncrementTestCounter2(void* unused) { + ++g_test_counter_2; +} + +void ZeroTestCounters() { + g_test_counter_1 = 0; + g_test_counter_2 = 0; +} + +void ExpectCounter1IsZero(void* unused) { + EXPECT_EQ(0, g_test_counter_1); +} + +void ExpectParamIsNull(void* param) { + EXPECT_EQ(static_cast(NULL), param); +} + +void ExpectParamIsCounter(void* param) { + EXPECT_EQ(&g_test_counter_1, param); +} + +} // namespace + +TEST(AtExitTest, Basic) { + ShadowingAtExitManager shadowing_at_exit_manager; + + ZeroTestCounters(); + base::AtExitManager::RegisterCallback(&IncrementTestCounter1, NULL); + base::AtExitManager::RegisterCallback(&IncrementTestCounter2, NULL); + base::AtExitManager::RegisterCallback(&IncrementTestCounter1, NULL); + + EXPECT_EQ(0, g_test_counter_1); + EXPECT_EQ(0, g_test_counter_2); + base::AtExitManager::ProcessCallbacksNow(); + EXPECT_EQ(2, g_test_counter_1); + EXPECT_EQ(1, g_test_counter_2); +} + +TEST(AtExitTest, LIFOOrder) { + ShadowingAtExitManager shadowing_at_exit_manager; + + ZeroTestCounters(); + base::AtExitManager::RegisterCallback(&IncrementTestCounter1, NULL); + base::AtExitManager::RegisterCallback(&ExpectCounter1IsZero, NULL); + base::AtExitManager::RegisterCallback(&IncrementTestCounter2, NULL); + + EXPECT_EQ(0, g_test_counter_1); + EXPECT_EQ(0, g_test_counter_2); + base::AtExitManager::ProcessCallbacksNow(); + EXPECT_EQ(1, g_test_counter_1); + EXPECT_EQ(1, g_test_counter_2); +} + +TEST(AtExitTest, Param) { + ShadowingAtExitManager shadowing_at_exit_manager; + + base::AtExitManager::RegisterCallback(&ExpectParamIsNull, NULL); + base::AtExitManager::RegisterCallback(&ExpectParamIsCounter, + &g_test_counter_1); + base::AtExitManager::ProcessCallbacksNow(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomicops.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomicops.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomicops.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomicops.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,139 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// For atomic operations on reference counts, see atomic_refcount.h. +// For atomic operations on sequence numbers, see atomic_sequence_num.h. + +// The routines exported by this module are subtle. If you use them, even if +// you get the code right, it will depend on careful reasoning about atomicity +// and memory ordering; it will be less readable, and harder to maintain. If +// you plan to use these routines, you should have a good reason, such as solid +// evidence that performance would otherwise suffer, or there being no +// alternative. You should assume only properties explicitly guaranteed by the +// specifications in this file. You are almost certainly _not_ writing code +// just for the x86; if you assume x86 semantics, x86 hardware bugs and +// implementations on other archtectures will cause your code to break. If you +// do not know what you are doing, avoid these routines, and use a Mutex. +// +// It is incorrect to make direct assignments to/from an atomic variable. +// You should use one of the Load or Store routines. The NoBarrier +// versions are provided when no barriers are needed: +// NoBarrier_Store() +// NoBarrier_Load() +// Although there are currently no compiler enforcement, you are encouraged +// to use these. +// + +#ifndef BASE_ATOMICOPS_H_ +#define BASE_ATOMICOPS_H_ + +#include "base/basictypes.h" +#include "base/port.h" + +namespace base { +namespace subtle { + +// Bug 1308991. We need this for /Wp64, to mark it safe for AtomicWord casting. +#ifndef OS_WIN +#define __w64 +#endif +typedef __w64 int32 Atomic32; +#ifdef ARCH_CPU_64_BITS +typedef int64 Atomic64; +#endif + +// Use AtomicWord for a machine-sized pointer. It will use the Atomic32 or +// Atomic64 routines below, depending on your architecture. +typedef intptr_t AtomicWord; + +// Atomically execute: +// result = *ptr; +// if (*ptr == old_value) +// *ptr = new_value; +// return result; +// +// I.e., replace "*ptr" with "new_value" if "*ptr" used to be "old_value". +// Always return the old value of "*ptr" +// +// This routine implies no memory barriers. +Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value); + +// Atomically store new_value into *ptr, returning the previous value held in +// *ptr. This routine implies no memory barriers. +Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value); + +// Atomically increment *ptr by "increment". Returns the new value of +// *ptr with the increment applied. This routine implies no memory barriers. +Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment); + +Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment); + +// These following lower-level operations are typically useful only to people +// implementing higher-level synchronization operations like spinlocks, +// mutexes, and condition-variables. They combine CompareAndSwap(), a load, or +// a store with appropriate memory-ordering instructions. "Acquire" operations +// ensure that no later memory access can be reordered ahead of the operation. +// "Release" operations ensure that no previous memory access can be reordered +// after the operation. "Barrier" operations have both "Acquire" and "Release" +// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory +// access. +Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value); +Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value); + +void MemoryBarrier(); +void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value); +void Acquire_Store(volatile Atomic32* ptr, Atomic32 value); +void Release_Store(volatile Atomic32* ptr, Atomic32 value); + +Atomic32 NoBarrier_Load(volatile const Atomic32* ptr); +Atomic32 Acquire_Load(volatile const Atomic32* ptr); +Atomic32 Release_Load(volatile const Atomic32* ptr); + +// 64-bit atomic operations (only available on 64-bit processors). +#ifdef ARCH_CPU_64_BITS +Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value); +Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value); +Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment); +Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment); + +Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value); +Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value); +void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value); +void Acquire_Store(volatile Atomic64* ptr, Atomic64 value); +void Release_Store(volatile Atomic64* ptr, Atomic64 value); +Atomic64 NoBarrier_Load(volatile const Atomic64* ptr); +Atomic64 Acquire_Load(volatile const Atomic64* ptr); +Atomic64 Release_Load(volatile const Atomic64* ptr); +#endif // CPU_ARCH_64_BITS + +} // namespace base::subtle +} // namespace base + +// Include our platform specific implementation. +#if defined(OS_WIN) && defined(COMPILER_MSVC) && defined(ARCH_CPU_X86_FAMILY) +#include "base/atomicops_internals_x86_msvc.h" +#elif defined(OS_MACOSX) && defined(ARCH_CPU_X86_FAMILY) +#include "base/atomicops_internals_x86_macosx.h" +#elif defined(COMPILER_GCC) && defined(ARCH_CPU_X86_FAMILY) +#include "base/atomicops_internals_x86_gcc.h" +#elif defined(COMPILER_GCC) && defined(ARCH_CPU_ARM_FAMILY) +#include "base/atomicops_internals_arm_gcc.h" +#else +#error "Atomic operations are not supported on your platform" +#endif + +#endif // BASE_ATOMICOPS_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_arm_gcc.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_arm_gcc.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_arm_gcc.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_arm_gcc.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,124 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is an internal atomic implementation, use base/atomicops.h instead. +// +// LinuxKernelCmpxchg and Barrier_AtomicIncrement are from Google Gears. + +#ifndef BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ +#define BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ + +namespace base { +namespace subtle { + +// 0xffff0fc0 is the hard coded address of a function provided by +// the kernel which implements an atomic compare-exchange. On older +// ARM architecture revisions (pre-v6) this may be implemented using +// a syscall. This address is stable, and in active use (hard coded) +// by at least glibc-2.7 and the Android C library. +typedef Atomic32 (*LinuxKernelCmpxchgFunc)(Atomic32 old_value, + Atomic32 new_value, + volatile Atomic32* ptr); +LinuxKernelCmpxchgFunc pLinuxKernelCmpxchg __attribute__((weak)) = + (LinuxKernelCmpxchgFunc) 0xffff0fc0; + +typedef void (*LinuxKernelMemoryBarrierFunc)(void); +LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) = + (LinuxKernelMemoryBarrierFunc) 0xffff0fa0; + + +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 prev_value = *ptr; + do { + if (!pLinuxKernelCmpxchg(old_value, new_value, + const_cast(ptr))) { + return old_value; + } + prev_value = *ptr; + } while (prev_value == old_value); + return prev_value; +} + +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { + Atomic32 old_value; + do { + old_value = *ptr; + } while (pLinuxKernelCmpxchg(old_value, new_value, + const_cast(ptr))); + return old_value; +} + +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + return Barrier_AtomicIncrement(ptr, increment); +} + +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + for (;;) { + // Atomic exchange the old value with an incremented one. + Atomic32 old_value = *ptr; + Atomic32 new_value = old_value + increment; + if (pLinuxKernelCmpxchg(old_value, new_value, + const_cast(ptr)) == 0) { + // The exchange took place as expected. + return new_value; + } + // Otherwise, *ptr changed mid-loop and we need to retry. + } + +} + +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; +} + +inline void MemoryBarrier() { + pLinuxKernelMemoryBarrier(); +} + +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { + MemoryBarrier(); + *ptr = value; +} + +inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { + return *ptr; +} + +inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { + Atomic32 value = *ptr; + MemoryBarrier(); + return value; +} + +inline Atomic32 Release_Load(volatile const Atomic32* ptr) { + MemoryBarrier(); + return *ptr; +} + +} // namespace base::subtle +} // namespace base + +#endif // BASE_ATOMICOPS_INTERNALS_ARM_GCC_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_x86_gcc.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_x86_gcc.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_x86_gcc.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_x86_gcc.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,104 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This module gets enough CPU information to optimize the +// atomicops module on x86. + +#include + +#include "base/atomicops.h" +#include "base/basictypes.h" + +// This file only makes sense with atomicops_internals_x86_gcc.h -- it +// depends on structs that are defined in that file. If atomicops.h +// doesn't sub-include that file, then we aren't needed, and shouldn't +// try to do anything. +#ifdef BASE_ATOMICOPS_INTERNALS_X86_GCC_H_ + +// Inline cpuid instruction. In PIC compilations, %ebx contains the address +// of the global offset table. To avoid breaking such executables, this code +// must preserve that register's value across cpuid instructions. +#if defined(__i386__) +#define cpuid(a, b, c, d, inp) \ + asm ("mov %%ebx, %%edi\n" \ + "cpuid\n" \ + "xchg %%edi, %%ebx\n" \ + : "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp)) +#elif defined (__x86_64__) +#define cpuid(a, b, c, d, inp) \ + asm ("mov %%rbx, %%rdi\n" \ + "cpuid\n" \ + "xchg %%rdi, %%rbx\n" \ + : "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp)) +#endif + +#if defined(cpuid) // initialize the struct only on x86 + +// Set the flags so that code will run correctly and conservatively, so even +// if we haven't been initialized yet, we're probably single threaded, and our +// default values should hopefully be pretty safe. +struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures = { + false, // bug can't exist before process spawns multiple threads + false, // no SSE2 +}; + +// Initialize the AtomicOps_Internalx86CPUFeatures struct. +static void AtomicOps_Internalx86CPUFeaturesInit() { + uint32 eax; + uint32 ebx; + uint32 ecx; + uint32 edx; + + // Get vendor string (issue CPUID with eax = 0) + cpuid(eax, ebx, ecx, edx, 0); + char vendor[13]; + memcpy(vendor, &ebx, 4); + memcpy(vendor + 4, &edx, 4); + memcpy(vendor + 8, &ecx, 4); + vendor[12] = 0; + + // get feature flags in ecx/edx, and family/model in eax + cpuid(eax, ebx, ecx, edx, 1); + + int family = (eax >> 8) & 0xf; // family and model fields + int model = (eax >> 4) & 0xf; + if (family == 0xf) { // use extended family and model fields + family += (eax >> 20) & 0xff; + model += ((eax >> 16) & 0xf) << 4; + } + + // Opteron Rev E has a bug in which on very rare occasions a locked + // instruction doesn't act as a read-acquire barrier if followed by a + // non-locked read-modify-write instruction. Rev F has this bug in + // pre-release versions, but not in versions released to customers, + // so we test only for Rev E, which is family 15, model 32..63 inclusive. + if (strcmp(vendor, "AuthenticAMD") == 0 && // AMD + family == 15 && + 32 <= model && model <= 63) { + AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug = true; + } else { + AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug = false; + } + + // edx bit 26 is SSE2 which we use to tell use whether we can use mfence + AtomicOps_Internalx86CPUFeatures.has_sse2 = ((edx >> 26) & 1); +} + +namespace { + +class AtomicOpsx86Initializer { + public: + AtomicOpsx86Initializer() { + AtomicOps_Internalx86CPUFeaturesInit(); + } +}; + +// A global to get use initialized on startup via static initialization :/ +AtomicOpsx86Initializer g_initer; + +} // namespace + +#endif // if x86 + +#endif // ifdef BASE_ATOMICOPS_INTERNALS_X86_GCC_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_x86_gcc.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_x86_gcc.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_x86_gcc.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_x86_gcc.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,261 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is an internal atomic implementation, use base/atomicops.h instead. + +#ifndef BASE_ATOMICOPS_INTERNALS_X86_GCC_H_ +#define BASE_ATOMICOPS_INTERNALS_X86_GCC_H_ + +// This struct is not part of the public API of this module; clients may not +// use it. +// Features of this x86. Values may not be correct before main() is run, +// but are set conservatively. +struct AtomicOps_x86CPUFeatureStruct { + bool has_amd_lock_mb_bug; // Processor has AMD memory-barrier bug; do lfence + // after acquire compare-and-swap. + bool has_sse2; // Processor has SSE2. +}; +extern struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures; + +#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory") + +namespace base { +namespace subtle { + +// 32-bit low-level operations on any platform. + +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 prev; + __asm__ __volatile__("lock; cmpxchgl %1,%2" + : "=a" (prev) + : "q" (new_value), "m" (*ptr), "0" (old_value) + : "memory"); + return prev; +} + +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { + __asm__ __volatile__("xchgl %1,%0" // The lock prefix is implicit for xchg. + : "=r" (new_value) + : "m" (*ptr), "0" (new_value) + : "memory"); + return new_value; // Now it's the previous value. +} + +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + Atomic32 temp = increment; + __asm__ __volatile__("lock; xaddl %0,%1" + : "+r" (temp), "+m" (*ptr) + : : "memory"); + // temp now holds the old value of *ptr + return temp + increment; +} + +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + Atomic32 temp = increment; + __asm__ __volatile__("lock; xaddl %0,%1" + : "+r" (temp), "+m" (*ptr) + : : "memory"); + // temp now holds the old value of *ptr + if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) { + __asm__ __volatile__("lfence" : : : "memory"); + } + return temp + increment; +} + +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value); + if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) { + __asm__ __volatile__("lfence" : : : "memory"); + } + return x; +} + +inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; +} + +#if defined(__x86_64__) + +// 64-bit implementations of memory barrier can be simpler, because it +// "mfence" is guaranteed to exist. +inline void MemoryBarrier() { + __asm__ __volatile__("mfence" : : : "memory"); +} + +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; + MemoryBarrier(); +} + +#else + +inline void MemoryBarrier() { + if (AtomicOps_Internalx86CPUFeatures.has_sse2) { + __asm__ __volatile__("mfence" : : : "memory"); + } else { // mfence is faster but not present on PIII + Atomic32 x = 0; + NoBarrier_AtomicExchange(&x, 0); // acts as a barrier on PIII + } +} + +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { + if (AtomicOps_Internalx86CPUFeatures.has_sse2) { + *ptr = value; + __asm__ __volatile__("mfence" : : : "memory"); + } else { + NoBarrier_AtomicExchange(ptr, value); + // acts as a barrier on PIII + } +} +#endif + +inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { + ATOMICOPS_COMPILER_BARRIER(); + *ptr = value; // An x86 store acts as a release barrier. + // See comments in Atomic64 version of Release_Store(), below. +} + +inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { + return *ptr; +} + +inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { + Atomic32 value = *ptr; // An x86 load acts as a acquire barrier. + // See comments in Atomic64 version of Release_Store(), below. + ATOMICOPS_COMPILER_BARRIER(); + return value; +} + +inline Atomic32 Release_Load(volatile const Atomic32* ptr) { + MemoryBarrier(); + return *ptr; +} + +#if defined(__x86_64__) + +// 64-bit low-level operations on 64-bit platform. + +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 prev; + __asm__ __volatile__("lock; cmpxchgq %1,%2" + : "=a" (prev) + : "q" (new_value), "m" (*ptr), "0" (old_value) + : "memory"); + return prev; +} + +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, + Atomic64 new_value) { + __asm__ __volatile__("xchgq %1,%0" // The lock prefix is implicit for xchg. + : "=r" (new_value) + : "m" (*ptr), "0" (new_value) + : "memory"); + return new_value; // Now it's the previous value. +} + +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + Atomic64 temp = increment; + __asm__ __volatile__("lock; xaddq %0,%1" + : "+r" (temp), "+m" (*ptr) + : : "memory"); + // temp now contains the previous value of *ptr + return temp + increment; +} + +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + Atomic64 temp = increment; + __asm__ __volatile__("lock; xaddq %0,%1" + : "+r" (temp), "+m" (*ptr) + : : "memory"); + // temp now contains the previous value of *ptr + if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) { + __asm__ __volatile__("lfence" : : : "memory"); + } + return temp + increment; +} + +inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; +} + +#if defined(CHROMIUM_MOZILLA_BUILD) +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value); + /* XXX/cjones: no idea if this is necessary... */ + if (AtomicOps_Internalx86CPUFeatures.has_amd_lock_mb_bug) { + __asm__ __volatile__("lfence" : : : "memory"); + } + return x; +} +#endif + +inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { + ATOMICOPS_COMPILER_BARRIER(); + + *ptr = value; // An x86 store acts as a release barrier + // for current AMD/Intel chips as of Jan 2008. + // See also Acquire_Load(), below. + + // When new chips come out, check: + // IA-32 Intel Architecture Software Developer's Manual, Volume 3: + // System Programming Guide, Chatper 7: Multiple-processor management, + // Section 7.2, Memory Ordering. + // Last seen at: + // http://developer.intel.com/design/pentium4/manuals/index_new.htm + // + // x86 stores/loads fail to act as barriers for a few instructions (clflush + // maskmovdqu maskmovq movntdq movnti movntpd movntps movntq) but these are + // not generated by the compiler, and are rare. Users of these instructions + // need to know about cache behaviour in any case since all of these involve + // either flushing cache lines or non-temporal cache hints. +} + +inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { + return *ptr; +} + +inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { + Atomic64 value = *ptr; // An x86 load acts as a acquire barrier, + // for current AMD/Intel chips as of Jan 2008. + // See also Release_Store(), above. + ATOMICOPS_COMPILER_BARRIER(); + return value; +} + +inline Atomic64 Release_Load(volatile const Atomic64* ptr) { + MemoryBarrier(); + return *ptr; +} +#endif // defined(__x86_64__) + +} // namespace base::subtle +} // namespace base + +#undef ATOMICOPS_COMPILER_BARRIER + +#endif // BASE_ATOMICOPS_INTERNALS_X86_GCC_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_x86_macosx.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_x86_macosx.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_x86_macosx.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_x86_macosx.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,279 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is an internal atomic implementation, use base/atomicops.h instead. + +#ifndef BASE_ATOMICOPS_INTERNALS_X86_MACOSX_H_ +#define BASE_ATOMICOPS_INTERNALS_X86_MACOSX_H_ + +#include + +namespace base { +namespace subtle { + +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 prev_value; + do { + if (OSAtomicCompareAndSwap32(old_value, new_value, + const_cast(ptr))) { + return old_value; + } + prev_value = *ptr; + } while (prev_value == old_value); + return prev_value; +} + +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr, + Atomic32 new_value) { + Atomic32 old_value; + do { + old_value = *ptr; + } while (!OSAtomicCompareAndSwap32(old_value, new_value, + const_cast(ptr))); + return old_value; +} + +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32 *ptr, + Atomic32 increment) { + return OSAtomicAdd32(increment, const_cast(ptr)); +} + +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32 *ptr, + Atomic32 increment) { + return OSAtomicAdd32Barrier(increment, const_cast(ptr)); +} + +inline void MemoryBarrier() { + OSMemoryBarrier(); +} + +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr, + Atomic32 old_value, + Atomic32 new_value) { + Atomic32 prev_value; + do { + if (OSAtomicCompareAndSwap32Barrier(old_value, new_value, + const_cast(ptr))) { + return old_value; + } + prev_value = *ptr; + } while (prev_value == old_value); + return prev_value; +} + +inline Atomic32 Release_CompareAndSwap(volatile Atomic32 *ptr, + Atomic32 old_value, + Atomic32 new_value) { + return Acquire_CompareAndSwap(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; +} + +inline void Acquire_Store(volatile Atomic32 *ptr, Atomic32 value) { + *ptr = value; + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic32 *ptr, Atomic32 value) { + MemoryBarrier(); + *ptr = value; +} + +inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { + return *ptr; +} + +inline Atomic32 Acquire_Load(volatile const Atomic32 *ptr) { + Atomic32 value = *ptr; + MemoryBarrier(); + return value; +} + +inline Atomic32 Release_Load(volatile const Atomic32 *ptr) { + MemoryBarrier(); + return *ptr; +} + +#ifdef __LP64__ + +// 64-bit implementation on 64-bit platform + +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 prev_value; + do { + if (OSAtomicCompareAndSwap64(old_value, new_value, + const_cast(ptr))) { + return old_value; + } + prev_value = *ptr; + } while (prev_value == old_value); + return prev_value; +} + +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr, + Atomic64 new_value) { + Atomic64 old_value; + do { + old_value = *ptr; + } while (!OSAtomicCompareAndSwap64(old_value, new_value, + const_cast(ptr))); + return old_value; +} + +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64 *ptr, + Atomic64 increment) { + return OSAtomicAdd64(increment, const_cast(ptr)); +} + +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64 *ptr, + Atomic64 increment) { + return OSAtomicAdd64Barrier(increment, const_cast(ptr)); +} + +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr, + Atomic64 old_value, + Atomic64 new_value) { + Atomic64 prev_value; + do { + if (OSAtomicCompareAndSwap64Barrier(old_value, new_value, + const_cast(ptr))) { + return old_value; + } + prev_value = *ptr; + } while (prev_value == old_value); + return prev_value; +} + +inline Atomic64 Release_CompareAndSwap(volatile Atomic64 *ptr, + Atomic64 old_value, + Atomic64 new_value) { + // The lib kern interface does not distinguish between + // Acquire and Release memory barriers; they are equivalent. + return Acquire_CompareAndSwap(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; +} + +inline void Acquire_Store(volatile Atomic64 *ptr, Atomic64 value) { + *ptr = value; + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) { + MemoryBarrier(); + *ptr = value; +} + +inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { + return *ptr; +} + +inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) { + Atomic64 value = *ptr; + MemoryBarrier(); + return value; +} + +inline Atomic64 Release_Load(volatile const Atomic64 *ptr) { + MemoryBarrier(); + return *ptr; +} + +#endif // defined(__LP64__) + +// MacOS uses long for intptr_t, AtomicWord and Atomic32 are always different +// on the Mac, even when they are the same size. We need to explicitly cast +// from AtomicWord to Atomic32/64 to implement the AtomicWord interface. +#ifdef __LP64__ +#define AtomicWordCastType Atomic64 +#else +#define AtomicWordCastType Atomic32 +#endif + +inline AtomicWord NoBarrier_CompareAndSwap(volatile AtomicWord* ptr, + AtomicWord old_value, + AtomicWord new_value) { + return NoBarrier_CompareAndSwap( + reinterpret_cast(ptr), + old_value, new_value); +} + +inline AtomicWord NoBarrier_AtomicExchange(volatile AtomicWord* ptr, + AtomicWord new_value) { + return NoBarrier_AtomicExchange( + reinterpret_cast(ptr), new_value); +} + +inline AtomicWord NoBarrier_AtomicIncrement(volatile AtomicWord* ptr, + AtomicWord increment) { + return NoBarrier_AtomicIncrement( + reinterpret_cast(ptr), increment); +} + +inline AtomicWord Barrier_AtomicIncrement(volatile AtomicWord* ptr, + AtomicWord increment) { + return Barrier_AtomicIncrement( + reinterpret_cast(ptr), increment); +} + +inline AtomicWord Acquire_CompareAndSwap(volatile AtomicWord* ptr, + AtomicWord old_value, + AtomicWord new_value) { + return base::subtle::Acquire_CompareAndSwap( + reinterpret_cast(ptr), + old_value, new_value); +} + +inline AtomicWord Release_CompareAndSwap(volatile AtomicWord* ptr, + AtomicWord old_value, + AtomicWord new_value) { + return base::subtle::Release_CompareAndSwap( + reinterpret_cast(ptr), + old_value, new_value); +} + +inline void NoBarrier_Store(volatile AtomicWord *ptr, AtomicWord value) { + NoBarrier_Store( + reinterpret_cast(ptr), value); +} + +inline void Acquire_Store(volatile AtomicWord* ptr, AtomicWord value) { + return base::subtle::Acquire_Store( + reinterpret_cast(ptr), value); +} + +inline void Release_Store(volatile AtomicWord* ptr, AtomicWord value) { + return base::subtle::Release_Store( + reinterpret_cast(ptr), value); +} + +inline AtomicWord NoBarrier_Load(volatile const AtomicWord *ptr) { + return NoBarrier_Load( + reinterpret_cast(ptr)); +} + +inline AtomicWord Acquire_Load(volatile const AtomicWord* ptr) { + return base::subtle::Acquire_Load( + reinterpret_cast(ptr)); +} + +inline AtomicWord Release_Load(volatile const AtomicWord* ptr) { + return base::subtle::Release_Load( + reinterpret_cast(ptr)); +} + +#undef AtomicWordCastType + +} // namespace base::subtle +} // namespace base + +#endif // BASE_ATOMICOPS_INTERNALS_X86_MACOSX_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_x86_msvc.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_x86_msvc.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_x86_msvc.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomicops_internals_x86_msvc.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,179 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is an internal atomic implementation, use base/atomicops.h instead. + +#ifndef BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_ +#define BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_ + +#include + +namespace base { +namespace subtle { + +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + LONG result = InterlockedCompareExchange( + reinterpret_cast(ptr), + static_cast(new_value), + static_cast(old_value)); + return static_cast(result); +} + +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { + LONG result = InterlockedExchange( + reinterpret_cast(ptr), + static_cast(new_value)); + return static_cast(result); +} + +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + return InterlockedExchangeAdd( + reinterpret_cast(ptr), + static_cast(increment)) + increment; +} + +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + return Barrier_AtomicIncrement(ptr, increment); +} + +#if !(defined(_MSC_VER) && _MSC_VER >= 1400) +#error "We require at least vs2005 for MemoryBarrier" +#endif +inline void MemoryBarrier() { + // We use MemoryBarrier from WinNT.h + ::MemoryBarrier(); +} + +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; +} + +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { + NoBarrier_AtomicExchange(ptr, value); + // acts as a barrier in this implementation +} + +inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { + *ptr = value; // works w/o barrier for current Intel chips as of June 2005 + // See comments in Atomic64 version of Release_Store() below. +} + +inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { + return *ptr; +} + +inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { + Atomic32 value = *ptr; + return value; +} + +inline Atomic32 Release_Load(volatile const Atomic32* ptr) { + MemoryBarrier(); + return *ptr; +} + +#if defined(_WIN64) + +// 64-bit low-level operations on 64-bit platform. + +COMPILE_ASSERT(sizeof(Atomic64) == sizeof(PVOID), atomic_word_is_atomic); + +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + PVOID result = InterlockedCompareExchangePointer( + reinterpret_cast(ptr), + reinterpret_cast(new_value), reinterpret_cast(old_value)); + return reinterpret_cast(result); +} + +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, + Atomic64 new_value) { + PVOID result = InterlockedExchangePointer( + reinterpret_cast(ptr), + reinterpret_cast(new_value)); + return reinterpret_cast(result); +} + +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + return InterlockedExchangeAdd64( + reinterpret_cast(ptr), + static_cast(increment)) + increment; +} + +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + return Barrier_AtomicIncrement(ptr, increment); +} + +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + return NoBarrier_CompareAndSwap(ptr, old_value, new_value); +} + +inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; +} + +inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { + NoBarrier_AtomicExchange(ptr, value); + // acts as a barrier in this implementation +} + +inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { + *ptr = value; // works w/o barrier for current Intel chips as of June 2005 + + // When new chips come out, check: + // IA-32 Intel Architecture Software Developer's Manual, Volume 3: + // System Programming Guide, Chatper 7: Multiple-processor management, + // Section 7.2, Memory Ordering. + // Last seen at: + // http://developer.intel.com/design/pentium4/manuals/index_new.htm +} + +inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { + return *ptr; +} + +inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { + Atomic64 value = *ptr; + return value; +} + +inline Atomic64 Release_Load(volatile const Atomic64* ptr) { + MemoryBarrier(); + return *ptr; +} + +#endif // defined(_WIN64) + +} // namespace base::subtle +} // namespace base + +#endif // BASE_ATOMICOPS_INTERNALS_X86_MSVC_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomicops_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomicops_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomicops_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomicops_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,237 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/atomicops.h" +#include "testing/gtest/include/gtest/gtest.h" + +template +static void TestAtomicIncrement() { + // For now, we just test single threaded execution + + // use a guard value to make sure the NoBarrier_AtomicIncrement doesn't go + // outside the expected address bounds. This is in particular to + // test that some future change to the asm code doesn't cause the + // 32-bit NoBarrier_AtomicIncrement doesn't do the wrong thing on 64-bit + // machines. + struct { + AtomicType prev_word; + AtomicType count; + AtomicType next_word; + } s; + + AtomicType prev_word_value, next_word_value; + memset(&prev_word_value, 0xFF, sizeof(AtomicType)); + memset(&next_word_value, 0xEE, sizeof(AtomicType)); + + s.prev_word = prev_word_value; + s.count = 0; + s.next_word = next_word_value; + + EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 1), 1); + EXPECT_EQ(s.count, 1); + EXPECT_EQ(s.prev_word, prev_word_value); + EXPECT_EQ(s.next_word, next_word_value); + + EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 2), 3); + EXPECT_EQ(s.count, 3); + EXPECT_EQ(s.prev_word, prev_word_value); + EXPECT_EQ(s.next_word, next_word_value); + + EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 3), 6); + EXPECT_EQ(s.count, 6); + EXPECT_EQ(s.prev_word, prev_word_value); + EXPECT_EQ(s.next_word, next_word_value); + + EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -3), 3); + EXPECT_EQ(s.count, 3); + EXPECT_EQ(s.prev_word, prev_word_value); + EXPECT_EQ(s.next_word, next_word_value); + + EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -2), 1); + EXPECT_EQ(s.count, 1); + EXPECT_EQ(s.prev_word, prev_word_value); + EXPECT_EQ(s.next_word, next_word_value); + + EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -1), 0); + EXPECT_EQ(s.count, 0); + EXPECT_EQ(s.prev_word, prev_word_value); + EXPECT_EQ(s.next_word, next_word_value); + + EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -1), -1); + EXPECT_EQ(s.count, -1); + EXPECT_EQ(s.prev_word, prev_word_value); + EXPECT_EQ(s.next_word, next_word_value); + + EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, -4), -5); + EXPECT_EQ(s.count, -5); + EXPECT_EQ(s.prev_word, prev_word_value); + EXPECT_EQ(s.next_word, next_word_value); + + EXPECT_EQ(base::subtle::NoBarrier_AtomicIncrement(&s.count, 5), 0); + EXPECT_EQ(s.count, 0); + EXPECT_EQ(s.prev_word, prev_word_value); + EXPECT_EQ(s.next_word, next_word_value); +} + + +#define NUM_BITS(T) (sizeof(T) * 8) + + +template +static void TestCompareAndSwap() { + AtomicType value = 0; + AtomicType prev = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 1); + EXPECT_EQ(1, value); + EXPECT_EQ(0, prev); + + // Use test value that has non-zero bits in both halves, more for testing + // 64-bit implementation on 32-bit platforms. + const AtomicType k_test_val = (GG_ULONGLONG(1) << + (NUM_BITS(AtomicType) - 2)) + 11; + value = k_test_val; + prev = base::subtle::NoBarrier_CompareAndSwap(&value, 0, 5); + EXPECT_EQ(k_test_val, value); + EXPECT_EQ(k_test_val, prev); + + value = k_test_val; + prev = base::subtle::NoBarrier_CompareAndSwap(&value, k_test_val, 5); + EXPECT_EQ(5, value); + EXPECT_EQ(k_test_val, prev); +} + + +template +static void TestAtomicExchange() { + AtomicType value = 0; + AtomicType new_value = base::subtle::NoBarrier_AtomicExchange(&value, 1); + EXPECT_EQ(1, value); + EXPECT_EQ(0, new_value); + + // Use test value that has non-zero bits in both halves, more for testing + // 64-bit implementation on 32-bit platforms. + const AtomicType k_test_val = (GG_ULONGLONG(1) << + (NUM_BITS(AtomicType) - 2)) + 11; + value = k_test_val; + new_value = base::subtle::NoBarrier_AtomicExchange(&value, k_test_val); + EXPECT_EQ(k_test_val, value); + EXPECT_EQ(k_test_val, new_value); + + value = k_test_val; + new_value = base::subtle::NoBarrier_AtomicExchange(&value, 5); + EXPECT_EQ(5, value); + EXPECT_EQ(k_test_val, new_value); +} + + +template +static void TestAtomicIncrementBounds() { + // Test at rollover boundary between int_max and int_min + AtomicType test_val = (GG_ULONGLONG(1) << + (NUM_BITS(AtomicType) - 1)); + AtomicType value = -1 ^ test_val; + AtomicType new_value = base::subtle::NoBarrier_AtomicIncrement(&value, 1); + EXPECT_EQ(test_val, value); + EXPECT_EQ(value, new_value); + + base::subtle::NoBarrier_AtomicIncrement(&value, -1); + EXPECT_EQ(-1 ^ test_val, value); + + // Test at 32-bit boundary for 64-bit atomic type. + test_val = GG_ULONGLONG(1) << (NUM_BITS(AtomicType) / 2); + value = test_val - 1; + new_value = base::subtle::NoBarrier_AtomicIncrement(&value, 1); + EXPECT_EQ(test_val, value); + EXPECT_EQ(value, new_value); + + base::subtle::NoBarrier_AtomicIncrement(&value, -1); + EXPECT_EQ(test_val - 1, value); +} + +// Return an AtomicType with the value 0xa5a5a5.. +template +static AtomicType TestFillValue() { + AtomicType val = 0; + memset(&val, 0xa5, sizeof(AtomicType)); + return val; +} + +// This is a simple sanity check that values are correct. Not testing +// atomicity +template +static void TestStore() { + const AtomicType kVal1 = TestFillValue(); + const AtomicType kVal2 = static_cast(-1); + + AtomicType value; + + base::subtle::NoBarrier_Store(&value, kVal1); + EXPECT_EQ(kVal1, value); + base::subtle::NoBarrier_Store(&value, kVal2); + EXPECT_EQ(kVal2, value); + + base::subtle::Acquire_Store(&value, kVal1); + EXPECT_EQ(kVal1, value); + base::subtle::Acquire_Store(&value, kVal2); + EXPECT_EQ(kVal2, value); + + base::subtle::Release_Store(&value, kVal1); + EXPECT_EQ(kVal1, value); + base::subtle::Release_Store(&value, kVal2); + EXPECT_EQ(kVal2, value); +} + +// This is a simple sanity check that values are correct. Not testing +// atomicity +template +static void TestLoad() { + const AtomicType kVal1 = TestFillValue(); + const AtomicType kVal2 = static_cast(-1); + + AtomicType value; + + value = kVal1; + EXPECT_EQ(kVal1, base::subtle::NoBarrier_Load(&value)); + value = kVal2; + EXPECT_EQ(kVal2, base::subtle::NoBarrier_Load(&value)); + + value = kVal1; + EXPECT_EQ(kVal1, base::subtle::Acquire_Load(&value)); + value = kVal2; + EXPECT_EQ(kVal2, base::subtle::Acquire_Load(&value)); + + value = kVal1; + EXPECT_EQ(kVal1, base::subtle::Release_Load(&value)); + value = kVal2; + EXPECT_EQ(kVal2, base::subtle::Release_Load(&value)); +} + +TEST(AtomicOpsTest, Inc) { + TestAtomicIncrement(); + TestAtomicIncrement(); +} + +TEST(AtomicOpsTest, CompareAndSwap) { + TestCompareAndSwap(); + TestCompareAndSwap(); +} + +TEST(AtomicOpsTest, Exchange) { + TestAtomicExchange(); + TestAtomicExchange(); +} + +TEST(AtomicOpsTest, IncrementBounds) { + TestAtomicIncrementBounds(); + TestAtomicIncrementBounds(); +} + +TEST(AtomicOpsTest, Store) { + TestStore(); + TestStore(); +} + +TEST(AtomicOpsTest, Load) { + TestLoad(); + TestLoad(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomic_ref_count.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomic_ref_count.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomic_ref_count.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomic_ref_count.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,63 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is a low level implementation of atomic semantics for reference +// counting. Please use base/ref_counted.h directly instead. + +#ifndef BASE_ATOMIC_REF_COUNT_H_ +#define BASE_ATOMIC_REF_COUNT_H_ + +#include "base/atomicops.h" + +namespace base { + +typedef subtle::Atomic32 AtomicRefCount; + +// Increment a reference count by "increment", which must exceed 0. +inline void AtomicRefCountIncN(volatile AtomicRefCount *ptr, + AtomicRefCount increment) { + subtle::NoBarrier_AtomicIncrement(ptr, increment); +} + +// Decrement a reference count by "decrement", which must exceed 0, +// and return whether the result is non-zero. +// Insert barriers to ensure that state written before the reference count +// became zero will be visible to a thread that has just made the count zero. +inline bool AtomicRefCountDecN(volatile AtomicRefCount *ptr, + AtomicRefCount decrement) { + return subtle::Barrier_AtomicIncrement(ptr, -decrement) != 0; +} + +// Increment a reference count by 1. +inline void AtomicRefCountInc(volatile AtomicRefCount *ptr) { + base::AtomicRefCountIncN(ptr, 1); +} + +// Decrement a reference count by 1 and return whether the result is non-zero. +// Insert barriers to ensure that state written before the reference count +// became zero will be visible to a thread that has just made the count zero. +inline bool AtomicRefCountDec(volatile AtomicRefCount *ptr) { + return base::AtomicRefCountDecN(ptr, 1); +} + +// Return whether the reference count is one. If the reference count is used +// in the conventional way, a refrerence count of 1 implies that the current +// thread owns the reference and no other thread shares it. This call performs +// the test for a reference count of one, and performs the memory barrier +// needed for the owning thread to act on the object, knowing that it has +// exclusive access to the object. +inline bool AtomicRefCountIsOne(volatile AtomicRefCount *ptr) { + return subtle::Acquire_Load(ptr) == 1; +} + +// Return whether the reference count is zero. With conventional object +// referencing counting, the object will be destroyed, so the reference count +// should never be zero. Hence this is generally used for a debug check. +inline bool AtomicRefCountIsZero(volatile AtomicRefCount *ptr) { + return subtle::Acquire_Load(ptr) == 0; +} + +} // namespace base + +#endif // BASE_ATOMIC_REF_COUNT_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomic_sequence_num.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomic_sequence_num.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/atomic_sequence_num.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/atomic_sequence_num.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,30 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_ATOMIC_SEQUENCE_NUM_H_ +#define BASE_ATOMIC_SEQUENCE_NUM_H_ + +#include "base/atomicops.h" +#include "base/basictypes.h" + +namespace base { + +class AtomicSequenceNumber { + public: + AtomicSequenceNumber() : seq_(0) { } + explicit AtomicSequenceNumber(base::LinkerInitialized x) { /* seq_ is 0 */ } + + int GetNext() { + return static_cast( + base::subtle::NoBarrier_AtomicIncrement(&seq_, 1) - 1); + } + + private: + base::subtle::Atomic32 seq_; + DISALLOW_COPY_AND_ASSIGN(AtomicSequenceNumber); +}; + +} // namespace base + +#endif // BASE_ATOMIC_SEQUENCE_NUM_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_drag_source.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_drag_source.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_drag_source.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_drag_source.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,60 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/base_drag_source.h" + +/////////////////////////////////////////////////////////////////////////////// +// BaseDragSource, public: + +BaseDragSource::BaseDragSource() : ref_count_(0) { +} + +/////////////////////////////////////////////////////////////////////////////// +// BaseDragSource, IDropSource implementation: + +HRESULT BaseDragSource::QueryContinueDrag(BOOL escape_pressed, + DWORD key_state) { + if (escape_pressed) { + OnDragSourceCancel(); + return DRAGDROP_S_CANCEL; + } + + if (!(key_state & MK_LBUTTON)) { + OnDragSourceDrop(); + return DRAGDROP_S_DROP; + } + + OnDragSourceMove(); + return S_OK; +} + +HRESULT BaseDragSource::GiveFeedback(DWORD effect) { + return DRAGDROP_S_USEDEFAULTCURSORS; +} + +/////////////////////////////////////////////////////////////////////////////// +// BaseDragSource, IUnknown implementation: + +HRESULT BaseDragSource::QueryInterface(const IID& iid, void** object) { + *object = NULL; + if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IDropSource)) { + *object = this; + } else { + return E_NOINTERFACE; + } + AddRef(); + return S_OK; +} + +ULONG BaseDragSource::AddRef() { + return ++ref_count_; +} + +ULONG BaseDragSource::Release() { + if (--ref_count_ == 0) { + delete this; + return 0U; + } + return ref_count_; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_drag_source.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_drag_source.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_drag_source.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_drag_source.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,46 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_BASE_DRAG_SOURCE_H_ +#define BASE_BASE_DRAG_SOURCE_H_ + +#include + +#include "base/basictypes.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// BaseDragSource +// +// A base IDropSource implementation. Handles notifications sent by an active +// drag-drop operation as the user mouses over other drop targets on their +// system. This object tells Windows whether or not the drag should continue, +// and supplies the appropriate cursors. +// +class BaseDragSource : public IDropSource { + public: + BaseDragSource(); + virtual ~BaseDragSource() { } + + // IDropSource implementation: + HRESULT __stdcall QueryContinueDrag(BOOL escape_pressed, DWORD key_state); + HRESULT __stdcall GiveFeedback(DWORD effect); + + // IUnknown implementation: + HRESULT __stdcall QueryInterface(const IID& iid, void** object); + ULONG __stdcall AddRef(); + ULONG __stdcall Release(); + + protected: + virtual void OnDragSourceCancel() { } + virtual void OnDragSourceDrop() { } + virtual void OnDragSourceMove() { } + + private: + LONG ref_count_; + + DISALLOW_EVIL_CONSTRUCTORS(BaseDragSource); +}; + +#endif // BASE_BASE_DRAG_SOURCE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_drop_target.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_drop_target.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_drop_target.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_drop_target.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,167 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/base_drop_target.h" + +#include + +#include "base/logging.h" + +/////////////////////////////////////////////////////////////////////////////// + +IDropTargetHelper* BaseDropTarget::cached_drop_target_helper_ = NULL; +int32 BaseDropTarget::drag_identity_ = 0; + +BaseDropTarget::BaseDropTarget(HWND hwnd) + : hwnd_(hwnd), + suspend_(false), + ref_count_(0) { + DCHECK(hwnd); + HRESULT result = RegisterDragDrop(hwnd, this); + DCHECK(SUCCEEDED(result)); +} + +BaseDropTarget::~BaseDropTarget() { +} + +// static +IDropTargetHelper* BaseDropTarget::DropHelper() { + if (!cached_drop_target_helper_) { + CoCreateInstance(CLSID_DragDropHelper, 0, CLSCTX_INPROC_SERVER, + IID_IDropTargetHelper, + reinterpret_cast(&cached_drop_target_helper_)); + } + return cached_drop_target_helper_; +} + +/////////////////////////////////////////////////////////////////////////////// +// BaseDropTarget, IDropTarget implementation: + +HRESULT BaseDropTarget::DragEnter(IDataObject* data_object, + DWORD key_state, + POINTL cursor_position, + DWORD* effect) { + // Tell the helper that we entered so it can update the drag image. + IDropTargetHelper* drop_helper = DropHelper(); + if (drop_helper) { + drop_helper->DragEnter(GetHWND(), data_object, + reinterpret_cast(&cursor_position), *effect); + } + + // You can't drag and drop within the same HWND. + if (suspend_) { + *effect = DROPEFFECT_NONE; + return S_OK; + } + + // Update the drag identity, skipping 0. + if (++drag_identity_ == 0) + ++drag_identity_; + + current_data_object_ = data_object; + POINT screen_pt = { cursor_position.x, cursor_position.y }; + *effect = OnDragEnter(current_data_object_, key_state, screen_pt, *effect); + return S_OK; +} + +HRESULT BaseDropTarget::DragOver(DWORD key_state, + POINTL cursor_position, + DWORD* effect) { + // Tell the helper that we moved over it so it can update the drag image. + IDropTargetHelper* drop_helper = DropHelper(); + if (drop_helper) + drop_helper->DragOver(reinterpret_cast(&cursor_position), *effect); + + if (suspend_) { + *effect = DROPEFFECT_NONE; + return S_OK; + } + + POINT screen_pt = { cursor_position.x, cursor_position.y }; + *effect = OnDragOver(current_data_object_, key_state, screen_pt, *effect); + return S_OK; +} + +HRESULT BaseDropTarget::DragLeave() { + // Tell the helper that we moved out of it so it can update the drag image. + IDropTargetHelper* drop_helper = DropHelper(); + if (drop_helper) + drop_helper->DragLeave(); + + OnDragLeave(current_data_object_); + + current_data_object_ = NULL; + return S_OK; +} + +HRESULT BaseDropTarget::Drop(IDataObject* data_object, + DWORD key_state, + POINTL cursor_position, + DWORD* effect) { + // Tell the helper that we dropped onto it so it can update the drag image. + IDropTargetHelper* drop_helper = DropHelper(); + if (drop_helper) { + drop_helper->Drop(current_data_object_, + reinterpret_cast(&cursor_position), *effect); + } + + if (suspend_) { + *effect = DROPEFFECT_NONE; + return S_OK; + } + + POINT screen_pt = { cursor_position.x, cursor_position.y }; + *effect = OnDrop(current_data_object_, key_state, screen_pt, *effect); + return S_OK; +} + +/////////////////////////////////////////////////////////////////////////////// +// BaseDropTarget, IUnknown implementation: + +HRESULT BaseDropTarget::QueryInterface(const IID& iid, void** object) { + *object = NULL; + if (IsEqualIID(iid, IID_IUnknown) || IsEqualIID(iid, IID_IDropTarget)) { + *object = this; + } else { + return E_NOINTERFACE; + } + AddRef(); + return S_OK; +} + +ULONG BaseDropTarget::AddRef() { + return ++ref_count_; +} + +ULONG BaseDropTarget::Release() { + if (--ref_count_ == 0) { + delete this; + return 0U; + } + return ref_count_; +} + +DWORD BaseDropTarget::OnDragEnter(IDataObject* data_object, + DWORD key_state, + POINT cursor_position, + DWORD effect) { + return DROPEFFECT_NONE; +} + +DWORD BaseDropTarget::OnDragOver(IDataObject* data_object, + DWORD key_state, + POINT cursor_position, + DWORD effect) { + return DROPEFFECT_NONE; +} + +void BaseDropTarget::OnDragLeave(IDataObject* data_object) { +} + +DWORD BaseDropTarget::OnDrop(IDataObject* data_object, + DWORD key_state, + POINT cursor_position, + DWORD effect) { + return DROPEFFECT_NONE; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_drop_target.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_drop_target.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_drop_target.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_drop_target.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,130 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_BASE_DROP_TARGET_H_ +#define BASE_BASE_DROP_TARGET_H_ + +#include + +#include "base/ref_counted.h" + +struct IDropTargetHelper; + +// A DropTarget implementation that takes care of the nitty gritty +// of dnd. While this class is concrete, subclasses will most likely +// want to override various OnXXX methods. +// +// Because BaseDropTarget is ref counted you shouldn't delete it directly, +// rather wrap it in a scoped_refptr. Be sure and invoke RevokeDragDrop(m_hWnd) +// before the HWND is deleted too. +// +// This class is meant to be used in a STA and is not multithread-safe. +class BaseDropTarget : public IDropTarget { + public: + // Create a new BaseDropTarget associating it with the given HWND. + explicit BaseDropTarget(HWND hwnd); + virtual ~BaseDropTarget(); + + // When suspend is set to |true|, the drop target does not receive drops from + // drags initiated within the owning HWND. + // TODO(beng): (http://b/1085385) figure out how we will handle legitimate + // drag-drop operations within the same HWND, such as dragging + // selected text to an edit field. + void set_suspend(bool suspend) { suspend_ = suspend; } + + // IDropTarget implementation: + HRESULT __stdcall DragEnter(IDataObject* data_object, + DWORD key_state, + POINTL cursor_position, + DWORD* effect); + HRESULT __stdcall DragOver(DWORD key_state, + POINTL cursor_position, + DWORD* effect); + HRESULT __stdcall DragLeave(); + HRESULT __stdcall Drop(IDataObject* data_object, + DWORD key_state, + POINTL cursor_position, + DWORD* effect); + + // IUnknown implementation: + HRESULT __stdcall QueryInterface(const IID& iid, void** object); + ULONG __stdcall AddRef(); + ULONG __stdcall Release(); + + protected: + // Returns the hosting HWND. + HWND GetHWND() { return hwnd_; } + + // Invoked when the cursor first moves over the hwnd during a dnd session. + // This should return a bitmask of the supported drop operations: + // DROPEFFECT_NONE, DROPEFFECT_COPY, DROPEFFECT_LINK and/or + // DROPEFFECT_MOVE. + virtual DWORD OnDragEnter(IDataObject* data_object, + DWORD key_state, + POINT cursor_position, + DWORD effect); + + // Invoked when the cursor moves over the window during a dnd session. + // This should return a bitmask of the supported drop operations: + // DROPEFFECT_NONE, DROPEFFECT_COPY, DROPEFFECT_LINK and/or + // DROPEFFECT_MOVE. + virtual DWORD OnDragOver(IDataObject* data_object, + DWORD key_state, + POINT cursor_position, + DWORD effect); + + // Invoked when the cursor moves outside the bounds of the hwnd during a + // dnd session. + virtual void OnDragLeave(IDataObject* data_object); + + // Invoked when the drop ends on the window. This should return the operation + // that was taken. + virtual DWORD OnDrop(IDataObject* data_object, + DWORD key_state, + POINT cursor_position, + DWORD effect); + + // Return the drag identity. + static int32 GetDragIdentity() { return drag_identity_; } + + private: + // Returns the cached drop helper, creating one if necessary. The returned + // object is not addrefed. May return NULL if the object couldn't be created. + static IDropTargetHelper* DropHelper(); + + // The data object currently being dragged over this drop target. + scoped_refptr current_data_object_; + + // A helper object that is used to provide drag image support while the mouse + // is dragging over the content area. + // + // DO NOT ACCESS DIRECTLY! Use DropHelper() instead, which will lazily create + // this if it doesn't exist yet. This object can take tens of milliseconds to + // create, and we don't want to block any window opening for this, especially + // since often, DnD will never be used. Instead, we force this penalty to the + // first time it is actually used. + static IDropTargetHelper* cached_drop_target_helper_; + + // The drag identity (id). An up-counter that increases when the cursor first + // moves over the HWND in a DnD session (OnDragEnter). 0 is reserved to mean + // the "no/unknown" identity, and is used for initialization. The identity is + // sent to the renderer in drag enter notifications. Note: the identity value + // is passed over the renderer NPAPI interface to gears, so use int32 instead + // of int here. + static int32 drag_identity_; + + // The HWND of the source. This HWND is used to determine coordinates for + // mouse events that are sent to the renderer notifying various drag states. + HWND hwnd_; + + // Whether or not we are currently processing drag notifications for drags + // initiated in this window. + bool suspend_; + + LONG ref_count_; + + DISALLOW_EVIL_CONSTRUCTORS(BaseDropTarget); +}; + +#endif // BASE_BASE_DROP_TARGET_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base.gyp firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base.gyp --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base.gyp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base.gyp 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,724 @@ +# Copyright (c) 2009 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'includes': [ + '../build/common.gypi', + ], + 'targets': [ + { + 'target_name': 'base', + 'type': '<(library)', + 'dependencies': [ + '../third_party/icu38/icu38.gyp:icui18n', + '../third_party/icu38/icu38.gyp:icuuc', + ], + 'msvs_guid': '1832A374-8A74-4F9E-B536-69A699B3E165', + 'sources': [ + '../build/build_config.h', + 'crypto/cssm_init.cc', + 'crypto/cssm_init.h', + 'crypto/signature_verifier.h', + 'crypto/signature_verifier_mac.cc', + 'crypto/signature_verifier_nss.cc', + 'crypto/signature_verifier_win.cc', + 'third_party/dmg_fp/dmg_fp.h', + 'third_party/dmg_fp/dtoa.cc', + 'third_party/dmg_fp/g_fmt.cc', + 'third_party/nspr/prcpucfg.h', + 'third_party/nspr/prcpucfg_win.h', + 'third_party/nspr/prtime.cc', + 'third_party/nspr/prtime.h', + 'third_party/nspr/prtypes.h', + 'third_party/nss/blapi.h', + 'third_party/nss/blapit.h', + 'third_party/nss/sha256.h', + 'third_party/nss/sha512.cc', + 'third_party/purify/pure.h', + 'third_party/purify/pure_api.c', + 'atomicops_internals_x86_gcc.cc', + 'at_exit.cc', + 'at_exit.h', + 'atomic_ref_count.h', + 'atomic_sequence_num.h', + 'atomicops.h', + 'atomicops_internals_x86_msvc.h', + 'base_drag_source.cc', + 'base_drag_source.h', + 'base_drop_target.cc', + 'base_drop_target.h', + 'base_paths.cc', + 'base_paths.h', + 'base_paths_linux.h', + 'base_paths_linux.cc', + 'base_paths_mac.h', + 'base_paths_mac.mm', + 'base_paths_win.cc', + 'base_paths_win.h', + 'base_switches.cc', + 'base_switches.h', + 'basictypes.h', + 'bzip2_error_handler.cc', + 'clipboard.cc', + 'clipboard.h', + 'clipboard_linux.cc', + 'clipboard_mac.mm', + 'clipboard_util.cc', + 'clipboard_util.h', + 'clipboard_win.cc', + 'command_line.cc', + 'command_line.h', + 'compiler_specific.h', + 'condition_variable.h', + 'condition_variable_posix.cc', + 'condition_variable_win.cc', + 'cpu.cc', + 'cpu.h', + 'data_pack.cc', + 'debug_on_start.cc', + 'debug_on_start.h', + 'debug_util.cc', + 'debug_util.h', + 'debug_util_mac.cc', + 'debug_util_posix.cc', + 'debug_util_win.cc', + 'directory_watcher.h', + 'directory_watcher_inotify.cc', + 'directory_watcher_mac.cc', + 'directory_watcher_win.cc', + 'event_recorder.cc', + 'event_recorder.h', + 'event_recorder_stubs.cc', + 'field_trial.cc', + 'field_trial.h', + 'file_descriptor_shuffle.cc', + 'file_descriptor_shuffle.h', + 'file_path.cc', + 'file_path.h', + 'file_util.cc', + 'file_util.h', + 'file_util_icu.cc', + 'file_util_linux.cc', + 'file_util_mac.mm', + 'file_util_posix.cc', + 'file_util_win.cc', + 'file_version_info.cc', + 'file_version_info.h', + 'file_version_info_linux.cc', + 'file_version_info_mac.mm', + 'fix_wp64.h', + 'float_util.h', + 'foundation_utils_mac.h', + 'hash_tables.h', + 'histogram.cc', + 'histogram.h', + 'hmac.h', + 'hmac_mac.cc', + 'hmac_nss.cc', + 'hmac_win.cc', + 'iat_patch.cc', + 'iat_patch.h', + 'icu_util.cc', + 'icu_util.h', + 'id_map.h', + 'idle_timer.cc', + 'idle_timer.h', + 'idle_timer_none.cc', + 'image_util.cc', + 'image_util.h', + 'json_reader.cc', + 'json_reader.h', + 'json_writer.cc', + 'json_writer.h', + 'keyboard_codes.h', + 'keyboard_codes_win.h', + 'lazy_instance.cc', + 'lazy_instance.h', + 'linked_ptr.h', + 'linux_util.cc', + 'linux_util.h', + 'lock.cc', + 'lock.h', + 'lock_impl.h', + 'lock_impl_posix.cc', + 'lock_impl_win.cc', + 'logging.cc', + 'logging.h', + 'mac_util.h', + 'mac_util.mm', + 'md5.cc', + 'md5.h', + 'memory_debug.cc', + 'memory_debug.h', + 'message_loop.cc', + 'message_loop.h', + 'message_pump.h', + 'message_pump_default.cc', + 'message_pump_default.h', + 'message_pump_glib.cc', + 'message_pump_glib.h', + 'message_pump_libevent.cc', + 'message_pump_libevent.h', + 'message_pump_mac.h', + 'message_pump_mac.mm', + 'message_pump_win.cc', + 'message_pump_win.h', + 'native_library.h', + 'native_library_linux.cc', + 'native_library_mac.mm', + 'native_library_win.cc', + 'non_thread_safe.cc', + 'non_thread_safe.h', + 'nss_init.cc', + 'nss_init.h', + 'object_watcher.cc', + 'object_watcher.h', + 'observer_list.h', + 'observer_list_threadsafe.h', + 'path_service.cc', + 'path_service.h', + 'pe_image.cc', + 'pe_image.h', + 'pickle.cc', + 'pickle.h', + 'platform_file.h', + 'platform_file_win.cc', + 'platform_file_posix.cc', + 'platform_thread.h', + 'platform_thread_mac.mm', + 'platform_thread_posix.cc', + 'platform_thread_win.cc', + 'port.h', + 'profiler.cc', + 'profiler.h', + 'process.h', + 'process_posix.cc', + 'process_util.h', + 'process_util_linux.cc', + 'process_util_mac.mm', + 'process_util_posix.cc', + 'process_util_win.cc', + 'process_win.cc', + 'rand_util.cc', + 'rand_util.h', + 'rand_util_posix.cc', + 'rand_util_win.cc', + 'ref_counted.cc', + 'ref_counted.h', + 'registry.cc', + 'registry.h', + 'resource_util.cc', + 'resource_util.h', + 'revocable_store.cc', + 'revocable_store.h', + 'scoped_bstr_win.cc', + 'scoped_bstr_win.h', + 'scoped_cftyperef.h', + 'scoped_clipboard_writer.cc', + 'scoped_clipboard_writer.h', + 'scoped_comptr_win.h', + 'scoped_handle.h', + 'scoped_handle_win.h', + 'scoped_nsautorelease_pool.h', + 'scoped_nsautorelease_pool.mm', + 'scoped_nsobject.h', + 'scoped_ptr.h', + 'scoped_temp_dir.cc', + 'scoped_temp_dir.h', + 'scoped_variant_win.cc', + 'scoped_variant_win.h', + 'scoped_vector.h', + 'sha2.cc', + 'sha2.h', + 'shared_memory.h', + 'shared_memory_posix.cc', + 'shared_memory_win.cc', + 'simple_thread.cc', + 'simple_thread.h', + 'singleton.h', + 'spin_wait.h', + 'stack_container.h', + 'stats_counters.h', + 'stats_table.cc', + 'stats_table.h', + 'stl_util-inl.h', + 'string16.cc', + 'string16.h', + 'string_escape.cc', + 'string_escape.h', + 'string_piece.cc', + 'string_piece.h', + 'string_tokenizer.h', + 'string_util.cc', + 'string_util.h', + 'string_util_icu.cc', + 'string_util_win.h', + 'sys_info.h', + 'sys_info_mac.cc', + 'sys_info_posix.cc', + 'sys_info_win.cc', + 'sys_string_conversions.h', + 'sys_string_conversions_linux.cc', + 'sys_string_conversions_mac.mm', + 'sys_string_conversions_win.cc', + 'system_monitor.cc', + 'system_monitor.h', + 'system_monitor_posix.cc', + 'system_monitor_win.cc', + 'task.h', + 'test_file_util.h', + 'test_file_util_linux.cc', + 'test_file_util_mac.cc', + 'test_file_util_posix.cc', + 'test_file_util_win.cc', + 'thread.cc', + 'thread.h', + 'thread_collision_warner.cc', + 'thread_collision_warner.h', + 'thread_local.h', + 'thread_local_posix.cc', + 'thread_local_storage.h', + 'thread_local_storage_posix.cc', + 'thread_local_storage_win.cc', + 'thread_local_win.cc', + 'time.cc', + 'time.h', + 'time_format.cc', + 'time_format.h', + 'time_mac.cc', + 'time_posix.cc', + 'time_win.cc', + 'timer.cc', + 'timer.h', + 'trace_event.cc', + 'trace_event.h', + 'tracked.cc', + 'tracked.h', + 'tracked_objects.cc', + 'tracked_objects.h', + 'tuple.h', + 'values.cc', + 'values.h', + 'version.cc', + 'version.h', + 'waitable_event.h', + 'waitable_event_posix.cc', + 'waitable_event_watcher.h', + 'waitable_event_watcher_posix.cc', + 'waitable_event_watcher_win.cc', + 'waitable_event_win.cc', + 'watchdog.cc', + 'watchdog.h', + 'win_util.cc', + 'win_util.h', + 'windows_message_list.h', + 'wmi_util.cc', + 'wmi_util.h', + 'word_iterator.cc', + 'word_iterator.h', + 'worker_pool.h', + 'worker_pool_linux.cc', + 'worker_pool_linux.h', + 'worker_pool_mac.mm', + 'worker_pool_win.cc', + ], + 'include_dirs': [ + '..', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '..', + ], + }, + # These warnings are needed for the files in third_party\dmg_fp. + 'msvs_disabled_warnings': [ + 4244, 4554, 4018, 4102, + ], + 'conditions': [ + [ 'OS == "linux"', { + 'actions': [ + { + 'action_name': 'linux_version', + 'variables': { + 'template_input_path': 'file_version_info_linux.h.version', + 'template_output_path': + '<(SHARED_INTERMEDIATE_DIR)/base/file_version_info_linux.h', + }, + 'inputs': [ + '<(template_input_path)', + '../chrome/VERSION', + '../chrome/tools/build/linux/version.sh', + ], + 'conditions': [ + [ 'branding == "Chrome"', { + 'inputs': ['../chrome/app/theme/google_chrome/BRANDING'] + }, { # else branding!="Chrome" + 'inputs': ['../chrome/app/theme/chromium/BRANDING'] + }], + ], + 'outputs': [ + # Use a non-existant output so this action always runs and + # generates version information, e.g. to capture revision + # changes, which aren't captured by file dependencies. + '<(SHARED_INTERMEDIATE_DIR)/base/file_version_info_linux.bogus', + ], + 'action': [ + '../chrome/tools/build/linux/version.sh', + '<(template_input_path)', '<(template_output_path)', + ], + }, + ], + 'include_dirs': [ + '<(SHARED_INTERMEDIATE_DIR)', + ], + 'sources/': [ ['exclude', '_(mac|win)\\.cc$'], + ['exclude', '\\.mm?$' ] ], + 'sources!': [ + # Linux has an implementation of idle_timer that depends + # on XScreenSaver, but it's unclear if we want it yet, + # so use idle_timer_none.cc instead. + 'idle_timer.cc', + ], + 'dependencies': [ + '../build/linux/system.gyp:gtk', + '../build/linux/system.gyp:nss', + ], + 'cflags': [ + '-Wno-write-strings', + ], + 'link_settings': { + 'libraries': [ + # We need rt for clock_gettime(). + '-lrt', + ], + }, + }, + { # else: OS != "linux" + 'sources!': [ + 'crypto/signature_verifier_nss.cc', + 'atomicops_internals_x86_gcc.cc', + 'directory_watcher_inotify.cc', + 'hmac_nss.cc', + 'idle_timer_none.cc', + 'linux_util.cc', + 'message_pump_glib.cc', + 'nss_init.cc', + 'nss_init.h', + 'time_posix.cc', + ], + } + ], + [ 'GENERATOR == "quentin"', { + # Quentin builds don't have a recent enough glibc to include the + # inotify headers + 'sources!': [ + 'directory_watcher_inotify.cc', + ], + 'sources': [ + 'directory_watcher_stub.cc', + ], + }, + ], + [ 'OS == "mac"', { + 'sources/': [ ['exclude', '_(linux|win)\\.cc$'] ], + 'sources!': [ + ], + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/AppKit.framework', + '$(SDKROOT)/System/Library/Frameworks/Carbon.framework', + '$(SDKROOT)/System/Library/Frameworks/CoreFoundation.framework', + '$(SDKROOT)/System/Library/Frameworks/Foundation.framework', + '$(SDKROOT)/System/Library/Frameworks/Security.framework', + ], + }, + }, + { # else: OS != "mac" + 'sources!': [ + 'crypto/cssm_init.cc', + 'crypto/cssm_init.h', + ], + } + ], + [ 'OS == "win"', { + 'sources/': [ ['exclude', '_(linux|mac|posix)\\.cc$'], + ['exclude', '\\.mm?$' ] ], + 'sources!': [ + 'data_pack.cc', + 'event_recorder_stubs.cc', + 'file_descriptor_shuffle.cc', + 'message_pump_libevent.cc', + 'string16.cc', + ], + }, + { # else: OS != "win" + 'dependencies': ['../third_party/libevent/libevent.gyp:libevent'], + 'sources!': [ + 'third_party/purify/pure_api.c', + 'base_drag_source.cc', + 'base_drop_target.cc', + 'cpu.cc', + 'clipboard_util.cc', + 'debug_on_start.cc', + 'event_recorder.cc', + 'file_version_info.cc', + 'iat_patch.cc', + 'image_util.cc', + 'object_watcher.cc', + 'pe_image.cc', + 'registry.cc', + 'resource_util.cc', + 'win_util.cc', + 'wmi_util.cc', + ], + }, + ], + ], + }, + { + 'target_name': 'base_gfx', + 'type': '<(library)', + 'msvs_guid': 'A508ADD3-CECE-4E0F-8448-2F5E454DF551', + 'sources': [ + 'gfx/gdi_util.cc', + 'gfx/gdi_util.h', + 'gfx/gtk_native_view_id_manager.cc', + 'gfx/gtk_native_view_id_manager.h', + 'gfx/gtk_util.cc', + 'gfx/gtk_util.h', + 'gfx/jpeg_codec.cc', + 'gfx/jpeg_codec.h', + 'gfx/native_theme.cc', + 'gfx/native_theme.h', + 'gfx/native_widget_types.h', + 'gfx/native_widget_types_gtk.cc', + 'gfx/platform_canvas.h', + 'gfx/platform_canvas_linux.h', + 'gfx/platform_canvas_mac.h', + 'gfx/platform_device_linux.h', + 'gfx/platform_device_mac.h', + 'gfx/png_decoder.cc', + 'gfx/png_decoder.h', + 'gfx/png_encoder.cc', + 'gfx/png_encoder.h', + 'gfx/point.cc', + 'gfx/point.h', + 'gfx/rect.cc', + 'gfx/rect.h', + 'gfx/size.cc', + 'gfx/size.h', + ], + 'mac_framework_dirs': [ + '$(SDKROOT)/System/Library/Frameworks/ApplicationServices.framework/Frameworks', + ], + 'dependencies': [ + 'base', + '../skia/skia.gyp:skia', + '../third_party/libjpeg/libjpeg.gyp:libjpeg', + '../third_party/libpng/libpng.gyp:libpng', + '../third_party/zlib/zlib.gyp:zlib', + ], + 'export_dependent_settings': [ + 'base', + ], + 'conditions': [ + ['OS == "linux"', { + 'dependencies': [ + '../build/linux/system.gyp:gtk', + ], + }], + [ 'OS != "win"', { 'sources!': [ + 'gfx/gdi_util.cc', + 'gfx/native_theme.cc', + ], + }], + [ 'OS != "linux"', { 'sources!': [ + 'gfx/gtk_native_view_id_manager.cc', + 'gfx/gtk_util.cc', + 'gfx/native_widget_types_gtk.cc', + ], + }], + ], + }, + { + 'target_name': 'base_unittests', + 'type': 'executable', + 'msvs_guid': '27A30967-4BBA-48D1-8522-CDE95F7B1CEC', + 'sources': [ + 'crypto/signature_verifier_unittest.cc', + 'gfx/jpeg_codec_unittest.cc', + 'gfx/native_theme_unittest.cc', + 'gfx/png_codec_unittest.cc', + 'gfx/rect_unittest.cc', + 'at_exit_unittest.cc', + 'atomicops_unittest.cc', + 'clipboard_unittest.cc', + 'command_line_unittest.cc', + 'condition_variable_unittest.cc', + 'data_pack_unittest.cc', + 'debug_util_unittest.cc', + 'directory_watcher_unittest.cc', + 'field_trial_unittest.cc', + 'file_descriptor_shuffle_unittest.cc', + 'file_path_unittest.cc', + 'file_util_unittest.cc', + 'file_version_info_unittest.cc', + 'histogram_unittest.cc', + 'hmac_unittest.cc', + 'idletimer_unittest.cc', + 'json_reader_unittest.cc', + 'json_writer_unittest.cc', + 'lazy_instance_unittest.cc', + 'linked_ptr_unittest.cc', + 'mac_util_unittest.cc', + 'message_loop_unittest.cc', + 'object_watcher_unittest.cc', + 'observer_list_unittest.cc', + 'path_service_unittest.cc', + 'pe_image_unittest.cc', + 'pickle_unittest.cc', + 'pr_time_unittest.cc', + 'process_util_unittest.cc', + 'rand_util_unittest.cc', + 'ref_counted_unittest.cc', + 'run_all_unittests.cc', + 'scoped_bstr_win_unittest.cc', + 'scoped_comptr_win_unittest.cc', + 'scoped_ptr_unittest.cc', + 'scoped_temp_dir_unittest.cc', + 'scoped_variant_win_unittest.cc', + 'sha2_unittest.cc', + 'shared_memory_unittest.cc', + 'simple_thread_unittest.cc', + 'singleton_unittest.cc', + 'stack_container_unittest.cc', + 'stats_table_unittest.cc', + 'string_escape_unittest.cc', + 'string_piece_unittest.cc', + 'string_tokenizer_unittest.cc', + 'string_util_unittest.cc', + 'sys_info_unittest.cc', + 'sys_string_conversions_unittest.cc', + 'system_monitor_unittest.cc', + 'thread_collision_warner_unittest.cc', + 'thread_local_storage_unittest.cc', + 'thread_local_unittest.cc', + 'thread_unittest.cc', + 'time_unittest.cc', + 'time_win_unittest.cc', + 'timer_unittest.cc', + 'tracked_objects_unittest.cc', + 'tuple_unittest.cc', + 'values_unittest.cc', + 'version_unittest.cc', + 'waitable_event_unittest.cc', + 'waitable_event_watcher_unittest.cc', + 'watchdog_unittest.cc', + 'win_util_unittest.cc', + 'wmi_util_unittest.cc', + 'word_iterator_unittest.cc', + 'worker_pool_unittest.cc', + ], + 'include_dirs': [ + # word_iterator.h (used by word_iterator_unittest.cc) leaks an ICU + # #include for unicode/uchar.h. This should probably be cleaned up. + '../third_party/icu38/public/common', + ], + 'dependencies': [ + 'base', + 'base_gfx', + '../skia/skia.gyp:skia', + '../testing/gtest.gyp:gtest', + ], + 'conditions': [ + ['OS == "linux"', { + 'sources!': [ + 'file_version_info_unittest.cc', + # Linux has an implementation of idle_timer, but it's unclear + # if we want it yet, so leave it 'unported' for now. + 'idletimer_unittest.cc', + 'worker_pool_linux_unittest.cc', + ], + 'dependencies': [ + '../build/linux/system.gyp:gtk', + '../build/linux/system.gyp:nss', + ], + }], + ['OS != "mac"', { + 'sources!': [ + 'mac_util_unittest.cc', + ], + }], + # This is needed to trigger the dll copy step on windows. + # TODO(mark): This should not be necessary. + ['OS == "win"', { + 'dependencies': [ + '../third_party/icu38/icu38.gyp:icudata', + ], + 'sources!': [ + 'data_pack_unittest.cc', + 'file_descriptor_shuffle_unittest.cc', + ], + }, { # OS != "win" + 'sources!': [ + 'gfx/native_theme_unittest.cc', + 'object_watcher_unittest.cc', + 'pe_image_unittest.cc', + 'scoped_bstr_win_unittest.cc', + 'scoped_comptr_win_unittest.cc', + 'scoped_variant_win_unittest.cc', + 'system_monitor_unittest.cc', + 'time_win_unittest.cc', + 'win_util_unittest.cc', + 'wmi_util_unittest.cc', + ], + }], + ], + }, + { + 'target_name': 'test_support_base', + 'type': '<(library)', + 'dependencies': [ + 'base', + '../testing/gtest.gyp:gtest', + ], + 'sources': [ + 'perftimer.cc', + 'run_all_perftests.cc', + ], + 'direct_dependent_settings': { + 'defines': [ + 'PERF_TEST', + ], + }, + 'conditions': [ + ['OS == "linux"', { + 'dependencies': [ + # Needed to handle the #include chain: + # base/perf_test_suite.h + # base/test_suite.h + # gtk/gtk.h + '../build/linux/system.gyp:gtk', + ], + }], + ], + }, + ], + 'conditions': [ + [ 'OS == "win"', { + 'targets': [ + { + 'target_name': 'debug_message', + 'type': 'executable', + 'sources': [ + 'debug_message.cc', + ], + }, + ], + }], + ], +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_paths.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_paths.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_paths.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_paths.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,38 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/base_paths.h" + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/path_service.h" + +namespace base { + +bool PathProvider(int key, FilePath* result) { + // NOTE: DIR_CURRENT is a special cased in PathService::Get + + FilePath cur; + switch (key) { + case base::DIR_EXE: + PathService::Get(base::FILE_EXE, &cur); + cur = cur.DirName(); + break; + case base::DIR_MODULE: + PathService::Get(base::FILE_MODULE, &cur); + cur = cur.DirName(); + break; + case base::DIR_TEMP: + if (!file_util::GetTempDir(&cur)) + return false; + break; + default: + return false; + } + + *result = cur; + return true; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_paths.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_paths.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_paths.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_paths.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,35 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_BASE_PATHS_H_ +#define BASE_BASE_PATHS_H_ + +// This file declares path keys for the base module. These can be used with +// the PathService to access various special directories and files. + +#include "base/basictypes.h" +#if defined(OS_WIN) +#include "base/base_paths_win.h" +#elif defined(OS_MACOSX) +#include "base/base_paths_mac.h" +#elif defined(OS_LINUX) +#include "base/base_paths_linux.h" +#endif +#include "base/path_service.h" + +namespace base { + +enum { + PATH_START = 0, + + DIR_CURRENT, // current directory + DIR_EXE, // directory containing FILE_EXE + DIR_MODULE, // directory containing FILE_MODULE + DIR_TEMP, // temporary directory + PATH_END +}; + +} // namespace base + +#endif // BASE_BASE_PATHS_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_paths_linux.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_paths_linux.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_paths_linux.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_paths_linux.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,46 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/base_paths_linux.h" + +#include + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/string_piece.h" +#include "base/sys_string_conversions.h" + +namespace base { + +bool PathProviderLinux(int key, FilePath* result) { + FilePath path; + switch (key) { + case base::FILE_EXE: + case base::FILE_MODULE: { // TODO(evanm): is this correct? + char bin_dir[PATH_MAX + 1]; + int bin_dir_size = readlink("/proc/self/exe", bin_dir, PATH_MAX); + if (bin_dir_size < 0 || bin_dir_size > PATH_MAX) { + NOTREACHED() << "Unable to resolve /proc/self/exe."; + return false; + } + bin_dir[bin_dir_size] = 0; + *result = FilePath(bin_dir); + return true; + } + case base::DIR_SOURCE_ROOT: + // On linux, unit tests execute two levels deep from the source root. + // For example: sconsbuild/{Debug|Release}/net_unittest + if (!PathService::Get(base::DIR_EXE, &path)) + return false; + path = path.Append(FilePath::kParentDirectory) + .Append(FilePath::kParentDirectory); + *result = path; + return true; + } + return false; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_paths_linux.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_paths_linux.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_paths_linux.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_paths_linux.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,30 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_BASE_PATHS_LINUX_H_ +#define BASE_BASE_PATHS_LINUX_H_ + +// This file declares Linux-specific path keys for the base module. +// These can be used with the PathService to access various special +// directories and files. + +namespace base { + +enum { + PATH_LINUX_START = 200, + + FILE_EXE, // Path and filename of the current executable. + FILE_MODULE, // Path and filename of the module containing the code for the + // PathService (which could differ from FILE_EXE if the + // PathService were compiled into a shared object, for example). + DIR_SOURCE_ROOT, // Returns the root of the source tree. This key is useful + // for tests that need to locate various resources. It + // should not be used outside of test code. + + PATH_LINUX_END +}; + +} // namespace base + +#endif // BASE_BASE_PATHS_LINUX_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_paths_mac.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_paths_mac.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_paths_mac.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_paths_mac.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,31 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_BASE_PATHS_MAC_H_ +#define BASE_BASE_PATHS_MAC_H_ + +// This file declares Mac-specific path keys for the base module. +// These can be used with the PathService to access various special +// directories and files. + +namespace base { + +enum { + PATH_MAC_START = 200, + + FILE_EXE, // path and filename of the current executable + FILE_MODULE, // path and filename of the module containing the code for the + // PathService (which could differ from FILE_EXE if the + // PathService were compiled into a library, for example) + DIR_APP_DATA, // ~/Library/Application Support/Google/Chrome + DIR_LOCAL_APP_DATA, // same as above (can we remove?) + DIR_SOURCE_ROOT, // Returns the root of the source tree. This key is useful + // for tests that need to locate various resources. It + // should not be used outside of test code. + PATH_MAC_END +}; + +} // namespace base + +#endif // BASE_BASE_PATHS_MAC_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_paths_mac.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_paths_mac.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_paths_mac.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_paths_mac.mm 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,58 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/base_paths_mac.h" + +#import + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/mac_util.h" +#include "base/path_service.h" +#include "base/string_util.h" + +namespace base { + +bool PathProviderMac(int key, FilePath* result) { + std::string cur; + switch (key) { + case base::FILE_EXE: + case base::FILE_MODULE: { + // Executable path can have relative references ("..") depending on + // how the app was launched. + NSString* path = + [[[NSBundle mainBundle] executablePath] stringByStandardizingPath]; + cur = [path fileSystemRepresentation]; + break; + } + case base::DIR_SOURCE_ROOT: { + FilePath path; + PathService::Get(base::DIR_EXE, &path); + if (mac_util::AmIBundled()) { + // The bundled app executables (Chromium, TestShell, etc) live five + // levels down, eg: + // src/xcodebuild/{Debug|Release}/Chromium.app/Contents/MacOS/Chromium. + path = path.DirName(); + path = path.DirName(); + path = path.DirName(); + path = path.DirName(); + *result = path.DirName(); + } else { + // Unit tests execute two levels deep from the source root, eg: + // src/xcodebuild/{Debug|Release}/base_unittests + path = path.DirName(); + *result = path.DirName(); + } + return true; + } + default: + return false; + } + + *result = FilePath(cur); + return true; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_paths_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_paths_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_paths_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_paths_win.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,118 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/base_paths_win.h" + +#include +#include + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/win_util.h" + +// http://blogs.msdn.com/oldnewthing/archive/2004/10/25/247180.aspx +extern "C" IMAGE_DOS_HEADER __ImageBase; + +namespace base { + +bool PathProviderWin(int key, FilePath* result) { + + // We need to go compute the value. It would be nice to support paths with + // names longer than MAX_PATH, but the system functions don't seem to be + // designed for it either, with the exception of GetTempPath (but other + // things will surely break if the temp path is too long, so we don't bother + // handling it. + wchar_t system_buffer[MAX_PATH]; + system_buffer[0] = 0; + + FilePath cur; + std::wstring wstring_path; + switch (key) { + case base::FILE_EXE: + GetModuleFileName(NULL, system_buffer, MAX_PATH); + cur = FilePath(system_buffer); + break; + case base::FILE_MODULE: { + // the resource containing module is assumed to be the one that + // this code lives in, whether that's a dll or exe + HMODULE this_module = reinterpret_cast(&__ImageBase); + GetModuleFileName(this_module, system_buffer, MAX_PATH); + cur = FilePath(system_buffer); + break; + } + case base::DIR_WINDOWS: + GetWindowsDirectory(system_buffer, MAX_PATH); + cur = FilePath(system_buffer); + break; + case base::DIR_SYSTEM: + GetSystemDirectory(system_buffer, MAX_PATH); + cur = FilePath(system_buffer); + break; + case base::DIR_PROGRAM_FILES: + if (FAILED(SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, NULL, + SHGFP_TYPE_CURRENT, system_buffer))) + return false; + cur = FilePath(system_buffer); + break; + case base::DIR_IE_INTERNET_CACHE: + if (FAILED(SHGetFolderPath(NULL, CSIDL_INTERNET_CACHE, NULL, + SHGFP_TYPE_CURRENT, system_buffer))) + return false; + cur = FilePath(system_buffer); + break; + case base::DIR_COMMON_START_MENU: + if (FAILED(SHGetFolderPath(NULL, CSIDL_COMMON_PROGRAMS, NULL, + SHGFP_TYPE_CURRENT, system_buffer))) + return false; + cur = FilePath(system_buffer); + break; + case base::DIR_START_MENU: + if (FAILED(SHGetFolderPath(NULL, CSIDL_PROGRAMS, NULL, + SHGFP_TYPE_CURRENT, system_buffer))) + return false; + cur = FilePath(system_buffer); + break; + case base::DIR_APP_DATA: + if (FAILED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, + system_buffer))) + return false; + cur = FilePath(system_buffer); + break; + case base::DIR_LOCAL_APP_DATA_LOW: + if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) { + return false; + } + // TODO(nsylvain): We should use SHGetKnownFolderPath instead. Bug 1281128 + if (FAILED(SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, + system_buffer))) + return false; + wstring_path = system_buffer; + file_util::UpOneDirectory(&wstring_path); + file_util::AppendToPath(&wstring_path, L"LocalLow"); + cur = FilePath(wstring_path); + break; + case base::DIR_LOCAL_APP_DATA: + if (FAILED(SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA, NULL, + SHGFP_TYPE_CURRENT, system_buffer))) + return false; + cur = FilePath(system_buffer); + break; + case base::DIR_SOURCE_ROOT: + // On Windows, unit tests execute two levels deep from the source root. + // For example: chrome/{Debug|Release}/ui_tests.exe + PathService::Get(base::DIR_EXE, &wstring_path); + file_util::UpOneDirectory(&wstring_path); + file_util::UpOneDirectory(&wstring_path); + cur = FilePath(wstring_path); + break; + default: + return false; + } + + *result = cur; + return true; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_paths_win.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_paths_win.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_paths_win.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_paths_win.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,42 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_BASE_PATHS_WIN_H__ +#define BASE_BASE_PATHS_WIN_H__ + +// This file declares windows-specific path keys for the base module. +// These can be used with the PathService to access various special +// directories and files. + +namespace base { + +enum { + PATH_WIN_START = 100, + + FILE_EXE, // path and filename of the current executable + FILE_MODULE, // path and filename of the module containing the code for the + // PathService (which could differ from FILE_EXE if the + // PathService were compiled into a DLL, for example) + DIR_WINDOWS, // Windows directory, usually "c:\windows" + DIR_SYSTEM, // Usually c:\windows\system32" + DIR_PROGRAM_FILES, // Usually c:\program files + + DIR_IE_INTERNET_CACHE, // Temporary Internet Files directory. + DIR_COMMON_START_MENU, // Usually "C:\Documents and Settings\All Users\ + // Start Menu\Programs" + DIR_START_MENU, // Usually "C:\Documents and Settings\\ + // Start Menu\Programs" + DIR_APP_DATA, // Application Data directory under the user profile. + DIR_LOCAL_APP_DATA_LOW, // Local AppData directory for low integrity level. + DIR_LOCAL_APP_DATA, // "Local Settings\Application Data" directory under the + // user profile. + DIR_SOURCE_ROOT, // Returns the root of the source tree. This key is useful + // for tests that need to locate various resources. It + // should not be used outside of test code. + PATH_WIN_END +}; + +} // namespace base + +#endif // BASE_BASE_PATHS_WIN_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_switches.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_switches.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_switches.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_switches.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,43 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/base_switches.h" + +namespace switches { + +// If the program includes chrome/common/debug_on_start.h, the process will +// start the JIT system-registered debugger on itself and will wait for 60 +// seconds for the debugger to attach to itself. Then a break point will be hit. +const wchar_t kDebugOnStart[] = L"debug-on-start"; + +// Will wait for 60 seconds for a debugger to come to attach to the process. +const wchar_t kWaitForDebugger[] = L"wait-for-debugger"; + +// Suppresses all error dialogs when present. +const wchar_t kNoErrorDialogs[] = L"noerrdialogs"; + +// Disables the crash reporting. +const wchar_t kDisableBreakpad[] = L"disable-breakpad"; + +// Generates full memory crash dump. +const wchar_t kFullMemoryCrashReport[] = L"full-memory-crash-report"; + +// The value of this switch determines whether the process is started as a +// renderer or plugin host. If it's empty, it's the browser. +const wchar_t kProcessType[] = L"type"; + +// Enable DCHECKs in release mode. +const wchar_t kEnableDCHECK[] = L"enable-dcheck"; + +// Refuse to make HTTP connections and refuse to accept certificate errors. +// For more information about the design of this feature, please see +// +// ForceHTTPS: Protecting High-Security Web Sites from Network Attacks +// Collin Jackson and Adam Barth +// In Proc. of the 17th International World Wide Web Conference (WWW 2008) +// +// Available at http://www.adambarth.com/papers/2008/jackson-barth.pdf +const wchar_t kForceHTTPS[] = L"force-https"; + +} // namespace switches diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_switches.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_switches.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/base_switches.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/base_switches.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,27 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines all the "base" command-line switches. + +#ifndef BASE_BASE_SWITCHES_H_ +#define BASE_BASE_SWITCHES_H_ + +#if defined(CHROMIUM_MOZILLA_BUILD) && defined(COMPILER_MSVC) +#include +#endif + +namespace switches { + +extern const wchar_t kDebugOnStart[]; +extern const wchar_t kWaitForDebugger[]; +extern const wchar_t kDisableBreakpad[]; +extern const wchar_t kFullMemoryCrashReport[]; +extern const wchar_t kNoErrorDialogs[]; +extern const wchar_t kProcessType[]; +extern const wchar_t kEnableDCHECK[]; +extern const wchar_t kForceHTTPS[]; + +} // namespace switches + +#endif // BASE_BASE_SWITCHES_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/basictypes.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/basictypes.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/basictypes.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/basictypes.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,395 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_BASICTYPES_H_ +#define BASE_BASICTYPES_H_ + +#ifdef CHROMIUM_MOZILLA_BUILD + +// Chromium includes a prtypes.h also, but it has been modified to include +// their build_config.h as well. We can therefore test for both to determine +// if someone screws up the include order. +#if defined(prtypes_h___) && !defined(BUILD_BUILD_CONFIG_H_) +#error You_must_include_basictypes.h_before_prtypes.h! +#endif + +#ifndef NO_NSPR_10_SUPPORT +#define NO_NSPR_10_SUPPORT +#define NO_NSPR_10_SUPPORT_SAVE +#endif + +#include "nspr/prtypes.h" + +#ifdef NO_NSPR_10_SUPPORT_SAVE +#undef NO_NSPR_10_SUPPORT_SAVE +#undef NO_NSPR_10_SUPPORT +#endif + +#ifdef _WIN32 +#undef _WIN32 +#define _WIN32_SAVE +#endif + +#include "nspr/obsolete/protypes.h" + +#define _INT32 +#define _UINT32 + +#ifdef _WIN32_SAVE +#undef _WIN32_SAVE +#define _WIN32 +#endif + +#endif // CHROMIUM_MOZILLA_BUILD + +#include // So we can set the bounds of our types +#include // For size_t +#include // for memcpy + +#include "base/port.h" // Types that only need exist on certain systems + +#ifndef COMPILER_MSVC +// stdint.h is part of C99 but MSVC doesn't have it. +#include // For intptr_t. +#endif + +typedef signed char schar; +typedef signed char int8; +typedef short int16; +// TODO(mbelshe) Remove these type guards. These are +// temporary to avoid conflicts with npapi.h. +#ifndef _INT32 +#define _INT32 +typedef int int32; +#endif +#if !(defined(CHROMIUM_MOZILLA_BUILD) && defined(PROTYPES_H)) +typedef long long int64; +#endif + +// NOTE: unsigned types are DANGEROUS in loops and other arithmetical +// places. Use the signed types unless your variable represents a bit +// pattern (eg a hash value) or you really need the extra bit. Do NOT +// use 'unsigned' to express "this value should always be positive"; +// use assertions for this. + +typedef unsigned char uint8; +typedef unsigned short uint16; +// TODO(mbelshe) Remove these type guards. These are +// temporary to avoid conflicts with npapi.h. +#ifndef _UINT32 +#define _UINT32 +typedef unsigned int uint32; +#endif +#if !(defined(CHROMIUM_MOZILLA_BUILD) && defined(PROTYPES_H)) +typedef unsigned long long uint64; +#endif + +// A type to represent a Unicode code-point value. As of Unicode 4.0, +// such values require up to 21 bits. +// (For type-checking on pointers, make this explicitly signed, +// and it should always be the signed version of whatever int32 is.) +typedef signed int char32; + +const uint8 kuint8max = (( uint8) 0xFF); +const uint16 kuint16max = ((uint16) 0xFFFF); +const uint32 kuint32max = ((uint32) 0xFFFFFFFF); +const uint64 kuint64max = ((uint64) GG_LONGLONG(0xFFFFFFFFFFFFFFFF)); +const int8 kint8min = (( int8) 0x80); +const int8 kint8max = (( int8) 0x7F); +const int16 kint16min = (( int16) 0x8000); +const int16 kint16max = (( int16) 0x7FFF); +const int32 kint32min = (( int32) 0x80000000); +const int32 kint32max = (( int32) 0x7FFFFFFF); +const int64 kint64min = (( int64) GG_LONGLONG(0x8000000000000000)); +const int64 kint64max = (( int64) GG_LONGLONG(0x7FFFFFFFFFFFFFFF)); + +#if defined(CHROMIUM_MOZILLA_BUILD) +// Platform- and hardware-dependent printf specifiers +# if defined(OS_POSIX) +# define __STDC_FORMAT_MACROS 1 +# include // for 64-bit integer format macros +# define PRId64L "I64d" +# define PRIu64L "I64u" +# define PRIx64L "I64x" +# elif defined(OS_WIN) +# define PRId64 "I64d" +# define PRIu64 "I64u" +# define PRIx64 "I64x" +# define PRId64L L"I64d" +# define PRIu64L L"I64u" +# define PRIx64L L"I64x" +# endif +#endif // defined(CHROMIUM_MOZILLA_BUILD) + +// A macro to disallow the copy constructor and operator= functions +// This should be used in the private: declarations for a class +#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ + TypeName(const TypeName&); \ + void operator=(const TypeName&) + +// An older, deprecated, politically incorrect name for the above. +#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) DISALLOW_COPY_AND_ASSIGN(TypeName) + +// A macro to disallow all the implicit constructors, namely the +// default constructor, copy constructor and operator= functions. +// +// This should be used in the private: declarations for a class +// that wants to prevent anyone from instantiating it. This is +// especially useful for classes containing only static methods. +#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \ + TypeName(); \ + DISALLOW_COPY_AND_ASSIGN(TypeName) + +// The arraysize(arr) macro returns the # of elements in an array arr. +// The expression is a compile-time constant, and therefore can be +// used in defining new arrays, for example. If you use arraysize on +// a pointer by mistake, you will get a compile-time error. +// +// One caveat is that arraysize() doesn't accept any array of an +// anonymous type or a type defined inside a function. In these rare +// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is +// due to a limitation in C++'s template system. The limitation might +// eventually be removed, but it hasn't happened yet. + +// This template function declaration is used in defining arraysize. +// Note that the function doesn't need an implementation, as we only +// use its type. +template +char (&ArraySizeHelper(T (&array)[N]))[N]; + +// That gcc wants both of these prototypes seems mysterious. VC, for +// its part, can't decide which to use (another mystery). Matching of +// template overloads: the final frontier. +#ifndef _MSC_VER +template +char (&ArraySizeHelper(const T (&array)[N]))[N]; +#endif + +#define arraysize(array) (sizeof(ArraySizeHelper(array))) + +// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize, +// but can be used on anonymous types or types defined inside +// functions. It's less safe than arraysize as it accepts some +// (although not all) pointers. Therefore, you should use arraysize +// whenever possible. +// +// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type +// size_t. +// +// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error +// +// "warning: division by zero in ..." +// +// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer. +// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays. +// +// The following comments are on the implementation details, and can +// be ignored by the users. +// +// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in +// the array) and sizeof(*(arr)) (the # of bytes in one array +// element). If the former is divisible by the latter, perhaps arr is +// indeed an array, in which case the division result is the # of +// elements in the array. Otherwise, arr cannot possibly be an array, +// and we generate a compiler error to prevent the code from +// compiling. +// +// Since the size of bool is implementation-defined, we need to cast +// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final +// result has type size_t. +// +// This macro is not perfect as it wrongfully accepts certain +// pointers, namely where the pointer size is divisible by the pointee +// size. Since all our code has to go through a 32-bit compiler, +// where a pointer is 4 bytes, this means all pointers to a type whose +// size is 3 or greater than 4 will be (righteously) rejected. + +#define ARRAYSIZE_UNSAFE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + static_cast(!(sizeof(a) % sizeof(*(a))))) + + +// Use implicit_cast as a safe version of static_cast or const_cast +// for upcasting in the type hierarchy (i.e. casting a pointer to Foo +// to a pointer to SuperclassOfFoo or casting a pointer to Foo to +// a const pointer to Foo). +// When you use implicit_cast, the compiler checks that the cast is safe. +// Such explicit implicit_casts are necessary in surprisingly many +// situations where C++ demands an exact type match instead of an +// argument type convertable to a target type. +// +// The From type can be inferred, so the preferred syntax for using +// implicit_cast is the same as for static_cast etc.: +// +// implicit_cast(expr) +// +// implicit_cast would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +template +inline To implicit_cast(From const &f) { + return f; +} + +// The COMPILE_ASSERT macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// COMPILE_ASSERT(ARRAYSIZE_UNSAFE(content_type_names) == CONTENT_NUM_TYPES, +// content_type_names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +template +struct CompileAssert { +}; + +#undef COMPILE_ASSERT +#define COMPILE_ASSERT(expr, msg) \ + typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] + +// Implementation details of COMPILE_ASSERT: +// +// - COMPILE_ASSERT works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outter parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert +// +// instead, these compilers will refuse to compile +// +// COMPILE_ASSERT(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + + +// MetatagId refers to metatag-id that we assign to +// each metatag pair.. +typedef uint32 MetatagId; + +// Argument type used in interfaces that can optionally take ownership +// of a passed in argument. If TAKE_OWNERSHIP is passed, the called +// object takes ownership of the argument. Otherwise it does not. +enum Ownership { + DO_NOT_TAKE_OWNERSHIP, + TAKE_OWNERSHIP +}; + +// bit_cast is a template function that implements the +// equivalent of "*reinterpret_cast(&source)". We need this in +// very low-level functions like the protobuf library and fast math +// support. +// +// float f = 3.14159265358979; +// int i = bit_cast(f); +// // i = 0x40490fdb +// +// The classical address-casting method is: +// +// // WRONG +// float f = 3.14159265358979; // WRONG +// int i = * reinterpret_cast(&f); // WRONG +// +// The address-casting method actually produces undefined behavior +// according to ISO C++ specification section 3.10 -15 -. Roughly, this +// section says: if an object in memory has one type, and a program +// accesses it with a different type, then the result is undefined +// behavior for most values of "different type". +// +// This is true for any cast syntax, either *(int*)&f or +// *reinterpret_cast(&f). And it is particularly true for +// conversions betweeen integral lvalues and floating-point lvalues. +// +// The purpose of 3.10 -15- is to allow optimizing compilers to assume +// that expressions with different types refer to different memory. gcc +// 4.0.1 has an optimizer that takes advantage of this. So a +// non-conforming program quietly produces wildly incorrect output. +// +// The problem is not the use of reinterpret_cast. The problem is type +// punning: holding an object in memory of one type and reading its bits +// back using a different type. +// +// The C++ standard is more subtle and complex than this, but that +// is the basic idea. +// +// Anyways ... +// +// bit_cast<> calls memcpy() which is blessed by the standard, +// especially by the example in section 3.9 . Also, of course, +// bit_cast<> wraps up the nasty logic in one place. +// +// Fortunately memcpy() is very fast. In optimized mode, with a +// constant size, gcc 2.95.3, gcc 4.0.1, and msvc 7.1 produce inline +// code with the minimal amount of data movement. On a 32-bit system, +// memcpy(d,s,4) compiles to one load and one store, and memcpy(d,s,8) +// compiles to two loads and two stores. +// +// I tested this code with gcc 2.95.3, gcc 4.0.1, icc 8.1, and msvc 7.1. +// +// WARNING: if Dest or Source is a non-POD type, the result of the memcpy +// is likely to surprise you. + +template +inline Dest bit_cast(const Source& source) { + // Compile time assertion: sizeof(Dest) == sizeof(Source) + // A compile error here means your Dest and Source have different sizes. + typedef char VerifySizesAreEqual [sizeof(Dest) == sizeof(Source) ? 1 : -1]; + + Dest dest; + memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +// The following enum should be used only as a constructor argument to indicate +// that the variable has static storage class, and that the constructor should +// do nothing to its state. It indicates to the reader that it is legal to +// declare a static instance of the class, provided the constructor is given +// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a +// static variable that has a constructor or a destructor because invocation +// order is undefined. However, IF the type can be initialized by filling with +// zeroes (which the loader does for static variables), AND the destructor also +// does nothing to the storage, AND there are no virtual methods, then a +// constructor declared as +// explicit MyClass(base::LinkerInitialized x) {} +// and invoked as +// static MyClass my_variable_name(base::LINKER_INITIALIZED); +namespace base { +enum LinkerInitialized { LINKER_INITIALIZED }; +} // base + + +#endif // BASE_BASICTYPES_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/build/base_gfx.vsprops firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/build/base_gfx.vsprops --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/build/base_gfx.vsprops 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/build/base_gfx.vsprops 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,8 @@ + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/build/base_unittests.vsprops firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/build/base_unittests.vsprops --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/build/base_unittests.vsprops 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/build/base_unittests.vsprops 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,8 @@ + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/build/base.vsprops firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/build/base.vsprops --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/build/base.vsprops 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/build/base.vsprops 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,8 @@ + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/bzip2_error_handler.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/bzip2_error_handler.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/bzip2_error_handler.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/bzip2_error_handler.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,12 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" + +// We define BZ_NO_STDIO in third_party/bzip2 to remove its internal STDERR +// error reporting. This requires us to export our own error handler. +extern "C" +void bz_internal_error(int errcode) { + CHECK(false) << "bzip2 internal error: " << errcode; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/clipboard.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/clipboard.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/clipboard.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/clipboard.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,64 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/clipboard.h" + +#include "base/logging.h" + +namespace { + +// A compromised renderer could send us bad data, so validate it. +bool IsBitmapSafe(const Clipboard::ObjectMapParams& params) { + const gfx::Size* size = + reinterpret_cast(&(params[1].front())); + return params[0].size() == + static_cast(size->width() * size->height() * 4); +} + +} + +void Clipboard::DispatchObject(ObjectType type, const ObjectMapParams& params) { + switch (type) { + case CBF_TEXT: + WriteText(&(params[0].front()), params[0].size()); + break; + + case CBF_HTML: + if (params.size() == 2) + WriteHTML(&(params[0].front()), params[0].size(), + &(params[1].front()), params[1].size()); + else + WriteHTML(&(params[0].front()), params[0].size(), NULL, 0); + break; + + case CBF_BOOKMARK: + WriteBookmark(&(params[0].front()), params[0].size(), + &(params[1].front()), params[1].size()); + break; + + case CBF_LINK: + WriteHyperlink(&(params[0].front()), params[0].size(), + &(params[1].front()), params[1].size()); + break; + + case CBF_FILES: + WriteFiles(&(params[0].front()), params[0].size()); + break; + + case CBF_WEBKIT: + WriteWebSmartPaste(); + break; + +#if defined(OS_WIN) || defined(OS_LINUX) + case CBF_BITMAP: + if (!IsBitmapSafe(params)) + return; + WriteBitmap(&(params[0].front()), &(params[1].front())); + break; +#endif // defined(OS_WIN) || defined(OS_LINUX) + + default: + NOTREACHED(); + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/clipboard.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/clipboard.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/clipboard.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/clipboard.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,204 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CLIPBOARD_H_ +#define BASE_CLIPBOARD_H_ + +#include +#include +#include + +#include "base/file_path.h" +#include "base/process.h" +#include "base/string16.h" +#include "base/gfx/size.h" + +class Clipboard { + public: + typedef std::string FormatType; +#if defined(OS_LINUX) + typedef struct _GtkClipboard GtkClipboard; + typedef std::map > TargetMap; +#endif + + // ObjectType designates the type of data to be stored in the clipboard. This + // designation is shared across all OSes. The system-specific designation + // is defined by FormatType. A single ObjectType might be represented by + // several system-specific FormatTypes. For example, on Linux the CBF_TEXT + // ObjectType maps to "text/plain", "STRING", and several other formats. On + // windows it maps to CF_UNICODETEXT. + enum ObjectType { + CBF_TEXT, + CBF_HTML, + CBF_BOOKMARK, + CBF_LINK, + CBF_FILES, + CBF_WEBKIT, + CBF_BITMAP, + CBF_SMBITMAP // bitmap from shared memory + }; + + // ObjectMap is a map from ObjectType to associated data. + // The data is organized differently for each ObjectType. The following + // table summarizes what kind of data is stored for each key. + // * indicates an optional argument. + // + // Key Arguments Type + // ------------------------------------- + // CBF_TEXT text char array + // CBF_HTML html char array + // url* char array + // CBF_BOOKMARK html char array + // url char array + // CBF_LINK html char array + // url char array + // CBF_FILES files char array representing multiple files. + // Filenames are separated by null characters and + // the final filename is double null terminated. + // The filenames are encoded in platform-specific + // encoding. + // CBF_WEBKIT none empty vector + // CBF_BITMAP pixels byte array + // size gfx::Size struct + // CBF_SMBITMAP shared_mem shared memory handle + // size gfx::Size struct + typedef std::vector ObjectMapParam; + typedef std::vector ObjectMapParams; + typedef std::map ObjectMap; + + Clipboard(); + ~Clipboard(); + + // Write a bunch of objects to the system clipboard. Copies are made of the + // contents of |objects|. On Windows they are copied to the system clipboard. + // On linux they are copied into a structure owned by the Clipboard object and + // kept until the system clipboard is set again. + void WriteObjects(const ObjectMap& objects); + + // Behaves as above. If there is some shared memory handle passed as one of + // the objects, it came from the process designated by |process|. This will + // assist in turning it into a shared memory region that the current process + // can use. + void WriteObjects(const ObjectMap& objects, base::ProcessHandle process); + + // Tests whether the clipboard contains a certain format + bool IsFormatAvailable(const FormatType& format) const; + + // Reads UNICODE text from the clipboard, if available. + void ReadText(string16* result) const; + + // Reads ASCII text from the clipboard, if available. + void ReadAsciiText(std::string* result) const; + + // Reads HTML from the clipboard, if available. + void ReadHTML(string16* markup, std::string* src_url) const; + + // Reads a bookmark from the clipboard, if available. + void ReadBookmark(string16* title, std::string* url) const; + + // Reads a file or group of files from the clipboard, if available, into the + // out parameter. + void ReadFile(FilePath* file) const; + void ReadFiles(std::vector* files) const; + + // Get format Identifiers for various types. + static FormatType GetUrlFormatType(); + static FormatType GetUrlWFormatType(); + static FormatType GetMozUrlFormatType(); + static FormatType GetPlainTextFormatType(); + static FormatType GetPlainTextWFormatType(); + static FormatType GetFilenameFormatType(); + static FormatType GetFilenameWFormatType(); + static FormatType GetWebKitSmartPasteFormatType(); + // Win: MS HTML Format, Other: Generic HTML format + static FormatType GetHtmlFormatType(); +#if defined(OS_WIN) + static FormatType GetBitmapFormatType(); + // Firefox text/html + static FormatType GetTextHtmlFormatType(); + static FormatType GetCFHDropFormatType(); + static FormatType GetFileDescriptorFormatType(); + static FormatType GetFileContentFormatZeroType(); + + // Duplicates any remote shared memory handle embedded inside |objects| that + // was created by |process| so that it can be used by this process. + static void DuplicateRemoteHandles(base::ProcessHandle process, + ObjectMap* objects); +#endif + + private: + void WriteText(const char* text_data, size_t text_len); + + void WriteHTML(const char* markup_data, + size_t markup_len, + const char* url_data, + size_t url_len); + + void WriteBookmark(const char* title_data, + size_t title_len, + const char* url_data, + size_t url_len); + + void WriteHyperlink(const char* title_data, + size_t title_len, + const char* url_data, + size_t url_len); + + void WriteWebSmartPaste(); + + void WriteFiles(const char* file_data, size_t file_len); + + void DispatchObject(ObjectType type, const ObjectMapParams& params); + + void WriteBitmap(const char* pixel_data, const char* size_data); +#if defined(OS_WIN) + void WriteBitmapFromSharedMemory(const char* bitmap_data, + const char* size_data, + base::ProcessHandle handle); + + void WriteBitmapFromHandle(HBITMAP source_hbitmap, + const gfx::Size& size); + + // Safely write to system clipboard. Free |handle| on failure. + void WriteToClipboard(unsigned int format, HANDLE handle); + + static void ParseBookmarkClipboardFormat(const string16& bookmark, + string16* title, + std::string* url); + + // Free a handle depending on its type (as intuited from format) + static void FreeData(unsigned int format, HANDLE data); + + // Return the window that should be the clipboard owner, creating it + // if neccessary. Marked const for lazily initialization by const methods. + HWND GetClipboardWindow() const; + + // Mark this as mutable so const methods can still do lazy initialization. + mutable HWND clipboard_owner_; + + // True if we can create a window. + bool create_window_; +#elif defined(OS_LINUX) + // Data is stored in the |clipboard_data_| map until it is saved to the system + // clipboard. The Store* functions save data to the |clipboard_data_| map. The + // SetGtkClipboard function replaces whatever is on the system clipboard with + // the contents of |clipboard_data_|. + // The Write* functions make a deep copy of the data passed to them an store + // it in |clipboard_data_|. + + // Write changes to gtk clipboard. + void SetGtkClipboard(); + // Free pointers in clipboard_data_ and clear() the map. + void FreeTargetMap(); + // Insert a mapping into clipboard_data_. + void InsertMapping(const char* key, char* data, size_t data_len); + + TargetMap* clipboard_data_; + GtkClipboard* clipboard_; +#endif + + DISALLOW_EVIL_CONSTRUCTORS(Clipboard); +}; + +#endif // BASE_CLIPBOARD_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/clipboard_linux.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/clipboard_linux.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/clipboard_linux.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/clipboard_linux.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,328 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/clipboard.h" + +#include +#include +#include +#include +#include + +#include "base/scoped_ptr.h" +#include "base/linux_util.h" +#include "base/string_util.h" + +namespace { + +const char kMimeBmp[] = "image/bmp"; +const char kMimeHtml[] = "text/html"; +const char kMimeText[] = "text/plain"; +const char kMimeWebkitSmartPaste[] = "chromium-internal/webkit-paste"; + +std::string GdkAtomToString(const GdkAtom& atom) { + gchar* name = gdk_atom_name(atom); + std::string rv(name); + g_free(name); + return rv; +} + +GdkAtom StringToGdkAtom(const std::string& str) { + return gdk_atom_intern(str.c_str(), false); +} + +// GtkClipboardGetFunc callback. +// GTK will call this when an application wants data we copied to the clipboard. +void GetData(GtkClipboard* clipboard, + GtkSelectionData* selection_data, + guint info, + gpointer user_data) { + Clipboard::TargetMap* data_map = + reinterpret_cast(user_data); + + std::string target_string = GdkAtomToString(selection_data->target); + Clipboard::TargetMap::iterator iter = data_map->find(target_string); + + if (iter == data_map->end()) + return; + + if (target_string == kMimeBmp) { + gtk_selection_data_set_pixbuf(selection_data, + reinterpret_cast(iter->second.first)); + } else { + gtk_selection_data_set(selection_data, selection_data->target, 8, + reinterpret_cast(iter->second.first), + iter->second.second); + } +} + +// GtkClipboardClearFunc callback. +// We are guaranteed this will be called exactly once for each call to +// gtk_clipboard_set_with_data +void ClearData(GtkClipboard* clipboard, + gpointer user_data) { + Clipboard::TargetMap* map = + reinterpret_cast(user_data); + std::set ptrs; + + for (Clipboard::TargetMap::iterator iter = map->begin(); + iter != map->end(); ++iter) { + if (iter->first == kMimeBmp) + g_object_unref(reinterpret_cast(iter->second.first)); + else + ptrs.insert(iter->second.first); + } + + for (std::set::iterator iter = ptrs.begin(); + iter != ptrs.end(); ++iter) + delete[] *iter; + + delete map; +} + +// Frees the pointers in the given map and clears the map. +// Does not double-free any pointers. +void FreeTargetMap(Clipboard::TargetMap map) { + std::set ptrs; + + for (Clipboard::TargetMap::iterator iter = map.begin(); + iter != map.end(); ++iter) + ptrs.insert(iter->second.first); + + for (std::set::iterator iter = ptrs.begin(); + iter != ptrs.end(); ++iter) + delete[] *iter; + + map.clear(); +} + +// Called on GdkPixbuf destruction; see WriteBitmap(). +void GdkPixbufFree(guchar* pixels, gpointer data) { + free(pixels); +} + +} // namespace + +Clipboard::Clipboard() { + clipboard_ = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); +} + +Clipboard::~Clipboard() { + // TODO(estade): do we want to save clipboard data after we exit? + // gtk_clipboard_set_can_store and gtk_clipboard_store work + // but have strangely awful performance. +} + +void Clipboard::WriteObjects(const ObjectMap& objects) { + clipboard_data_ = new TargetMap(); + + for (ObjectMap::const_iterator iter = objects.begin(); + iter != objects.end(); ++iter) { + DispatchObject(static_cast(iter->first), iter->second); + } + + SetGtkClipboard(); +} + +// Take ownership of the GTK clipboard and inform it of the targets we support. +void Clipboard::SetGtkClipboard() { + scoped_array targets( + new GtkTargetEntry[clipboard_data_->size()]); + + int i = 0; + for (Clipboard::TargetMap::iterator iter = clipboard_data_->begin(); + iter != clipboard_data_->end(); ++iter, ++i) { + char* target_string = new char[iter->first.size() + 1]; + strcpy(target_string, iter->first.c_str()); + targets[i].target = target_string; + targets[i].flags = 0; + targets[i].info = i; + } + + gtk_clipboard_set_with_data(clipboard_, targets.get(), + clipboard_data_->size(), + GetData, ClearData, + clipboard_data_); + + for (size_t i = 0; i < clipboard_data_->size(); i++) + delete[] targets[i].target; +} + +void Clipboard::WriteText(const char* text_data, size_t text_len) { + char* data = new char[text_len]; + memcpy(data, text_data, text_len); + + InsertMapping(kMimeText, data, text_len); + InsertMapping("TEXT", data, text_len); + InsertMapping("STRING", data, text_len); + InsertMapping("UTF8_STRING", data, text_len); + InsertMapping("COMPOUND_TEXT", data, text_len); +} + +void Clipboard::WriteHTML(const char* markup_data, + size_t markup_len, + const char* url_data, + size_t url_len) { + // TODO(estade): might not want to ignore |url_data| + char* data = new char[markup_len]; + memcpy(data, markup_data, markup_len); + + InsertMapping(kMimeHtml, data, markup_len); +} + +// Write an extra flavor that signifies WebKit was the last to modify the +// pasteboard. This flavor has no data. +void Clipboard::WriteWebSmartPaste() { + InsertMapping(kMimeWebkitSmartPaste, NULL, 0); +} + +void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) { + const gfx::Size* size = reinterpret_cast(size_data); + + guchar* data = base::BGRAToRGBA(reinterpret_cast(pixel_data), + size->width(), size->height(), 0); + + GdkPixbuf* pixbuf = + gdk_pixbuf_new_from_data(data, GDK_COLORSPACE_RGB, TRUE, + 8, size->width(), size->height(), + size->width() * 4, GdkPixbufFree, NULL); + // We store the GdkPixbuf*, and the size_t half of the pair is meaningless. + // Note that this contrasts with the vast majority of entries in our target + // map, which directly store the data and its length. + InsertMapping(kMimeBmp, reinterpret_cast(pixbuf), 0); +} + +void Clipboard::WriteBookmark(const char* title_data, size_t title_len, + const char* url_data, size_t url_len) { + // TODO(estade): implement this, but for now fail silently so we do not + // write error output during layout tests. + // NOTIMPLEMENTED(); +} + +void Clipboard::WriteHyperlink(const char* title_data, size_t title_len, + const char* url_data, size_t url_len) { + NOTIMPLEMENTED(); +} + +void Clipboard::WriteFiles(const char* file_data, size_t file_len) { + NOTIMPLEMENTED(); +} + +// We do not use gtk_clipboard_wait_is_target_available because of +// a bug with the gtk clipboard. It caches the available targets +// and does not always refresh the cache when it is appropriate. +// TODO(estade): When gnome bug 557315 is resolved, change this function +// to use gtk_clipboard_wait_is_target_available. Also, catch requests +// for plain text and change them to gtk_clipboard_wait_is_text_available +// (which checks for several standard text targets). +bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format) const { + bool retval = false; + GdkAtom* targets = NULL; + GtkSelectionData* data = + gtk_clipboard_wait_for_contents(clipboard_, + gdk_atom_intern("TARGETS", false)); + + if (!data) + return false; + + int num = 0; + gtk_selection_data_get_targets(data, &targets, &num); + + GdkAtom format_atom = StringToGdkAtom(format); + + for (int i = 0; i < num; i++) { + if (targets[i] == format_atom) { + retval = true; + break; + } + } + + gtk_selection_data_free(data); + g_free(targets); + + return retval; +} + +void Clipboard::ReadText(string16* result) const { + result->clear(); + gchar* text = gtk_clipboard_wait_for_text(clipboard_); + + if (text == NULL) + return; + + // TODO(estade): do we want to handle the possible error here? + UTF8ToUTF16(text, strlen(text), result); + g_free(text); +} + +void Clipboard::ReadAsciiText(std::string* result) const { + result->clear(); + gchar* text = gtk_clipboard_wait_for_text(clipboard_); + + if (text == NULL) + return; + + result->assign(text); + g_free(text); +} + +void Clipboard::ReadFile(FilePath* file) const { + *file = FilePath(); +} + +// TODO(estade): handle different charsets. +void Clipboard::ReadHTML(string16* markup, std::string* src_url) const { + markup->clear(); + + GtkSelectionData* data = gtk_clipboard_wait_for_contents(clipboard_, + StringToGdkAtom(GetHtmlFormatType())); + + if (!data) + return; + + UTF8ToUTF16(reinterpret_cast(data->data), + strlen(reinterpret_cast(data->data)), + markup); + gtk_selection_data_free(data); +} + +// static +Clipboard::FormatType Clipboard::GetPlainTextFormatType() { + return GdkAtomToString(GDK_TARGET_STRING); +} + +// static +Clipboard::FormatType Clipboard::GetPlainTextWFormatType() { + return GetPlainTextFormatType(); +} + +// static +Clipboard::FormatType Clipboard::GetHtmlFormatType() { + return std::string(kMimeHtml); +} + +// static +Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() { + return std::string(kMimeWebkitSmartPaste); +} + +// Insert the key/value pair in the clipboard_data structure. If +// the mapping already exists, it frees the associated data. Don't worry +// about double freeing because if the same key is inserted into the +// map twice, it must have come from different Write* functions and the +// data pointer cannot be the same. +void Clipboard::InsertMapping(const char* key, + char* data, + size_t data_len) { + TargetMap::iterator iter = clipboard_data_->find(key); + + if (iter != clipboard_data_->end()) { + if (strcmp(kMimeBmp, key) == 0) + g_object_unref(reinterpret_cast(iter->second.first)); + else + delete[] iter->second.first; + } + + (*clipboard_data_)[key] = std::make_pair(data, data_len); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/clipboard_mac.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/clipboard_mac.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/clipboard_mac.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/clipboard_mac.mm 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,283 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/clipboard.h" + +#import + +#include "base/logging.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" + +namespace { + +// Would be nice if this were in UTCoreTypes.h, but it isn't +const NSString* kUTTypeURLName = @"public.url-name"; + +// Tells us if WebKit was the last to write to the pasteboard. There's no +// actual data associated with this type. +const NSString *kWebSmartPastePboardType = @"NeXT smart paste pasteboard type"; + +NSPasteboard* GetPasteboard() { + // The pasteboard should not be nil in a UI session, but this handy DCHECK + // can help track down problems if someone tries using clipboard code outside + // of a UI session. + NSPasteboard* pasteboard = [NSPasteboard generalPasteboard]; + DCHECK(pasteboard); + return pasteboard; +} + +} // namespace + +Clipboard::Clipboard() { +} + +Clipboard::~Clipboard() { +} + +void Clipboard::WriteObjects(const ObjectMap& objects) { + NSPasteboard* pb = GetPasteboard(); + [pb declareTypes:[NSArray array] owner:nil]; + + for (ObjectMap::const_iterator iter = objects.begin(); + iter != objects.end(); ++iter) { + DispatchObject(static_cast(iter->first), iter->second); + } + +} + +void Clipboard::WriteText(const char* text_data, size_t text_len) { + std::string text_str(text_data, text_len); + NSString *text = base::SysUTF8ToNSString(text_str); + NSPasteboard* pb = GetPasteboard(); + [pb addTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil]; + [pb setString:text forType:NSStringPboardType]; +} + +void Clipboard::WriteHTML(const char* markup_data, + size_t markup_len, + const char* url_data, + size_t url_len) { + std::string html_fragment_str(markup_data, markup_len); + NSString *html_fragment = base::SysUTF8ToNSString(html_fragment_str); + + // TODO(avi): url_data? + NSPasteboard* pb = GetPasteboard(); + [pb addTypes:[NSArray arrayWithObject:NSHTMLPboardType] owner:nil]; + [pb setString:html_fragment forType:NSHTMLPboardType]; +} + +void Clipboard::WriteBookmark(const char* title_data, + size_t title_len, + const char* url_data, + size_t url_len) { + WriteHyperlink(title_data, title_len, url_data, url_len); +} + +void Clipboard::WriteHyperlink(const char* title_data, + size_t title_len, + const char* url_data, + size_t url_len) { + std::string title_str(title_data, title_len); + NSString *title = base::SysUTF8ToNSString(title_str); + std::string url_str(url_data, url_len); + NSString *url = base::SysUTF8ToNSString(url_str); + + // TODO(playmobil): In the Windows version of this function, an HTML + // representation of the bookmark is also added to the clipboard, to support + // drag and drop of web shortcuts. I don't think we need to do this on the + // Mac, but we should double check later on. + NSURL* nsurl = [NSURL URLWithString:url]; + + NSPasteboard* pb = GetPasteboard(); + // passing UTIs into the pasteboard methods is valid >= 10.5 + [pb addTypes:[NSArray arrayWithObjects:NSURLPboardType, + kUTTypeURLName, + nil] + owner:nil]; + [nsurl writeToPasteboard:pb]; + [pb setString:title forType:kUTTypeURLName]; +} + +void Clipboard::WriteFiles(const char* file_data, size_t file_len) { + NSMutableArray* fileList = [NSMutableArray arrayWithCapacity:1]; + + // Offset of current filename from start of file_data array. + size_t current_filename_offset = 0; + + // file_data is double null terminated (see table at top of clipboard.h). + // So this loop can ignore the second null terminator, thus file_len - 1. + // TODO(playmobil): If we need a loop like this on other platforms then split + // this out into a common function that outputs a std::vector. + for (size_t i = 0; i < file_len - 1; ++i) { + if (file_data[i] == '\0') { + const char* filename = &file_data[current_filename_offset]; + [fileList addObject:[NSString stringWithUTF8String:filename]]; + + current_filename_offset = i + 1; + continue; + } + } + + NSPasteboard* pb = GetPasteboard(); + [pb addTypes:[NSArray arrayWithObject:NSFilenamesPboardType] owner:nil]; + [pb setPropertyList:fileList forType:NSFilenamesPboardType]; +} + +// Write an extra flavor that signifies WebKit was the last to modify the +// pasteboard. This flavor has no data. +void Clipboard::WriteWebSmartPaste() { + NSPasteboard* pb = GetPasteboard(); + NSString* format = base::SysUTF8ToNSString(GetWebKitSmartPasteFormatType()); + [pb addTypes:[NSArray arrayWithObject:format] owner:nil]; + [pb setData:nil forType:format]; +} + +bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format) const { + NSString* format_ns = base::SysUTF8ToNSString(format); + + NSPasteboard* pb = GetPasteboard(); + NSArray* types = [pb types]; + + return [types containsObject:format_ns]; +} + +void Clipboard::ReadText(string16* result) const { + NSPasteboard* pb = GetPasteboard(); + NSString* contents = [pb stringForType:NSStringPboardType]; + + UTF8ToUTF16([contents UTF8String], + [contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding], + result); +} + +void Clipboard::ReadAsciiText(std::string* result) const { + NSPasteboard* pb = GetPasteboard(); + NSString* contents = [pb stringForType:NSStringPboardType]; + + if (!contents) + result->clear(); + else + result->assign([contents UTF8String]); +} + +void Clipboard::ReadHTML(string16* markup, std::string* src_url) const { + if (markup) { + NSPasteboard* pb = GetPasteboard(); + NSArray *supportedTypes = [NSArray arrayWithObjects:NSHTMLPboardType, + NSStringPboardType, + nil]; + NSString *bestType = [pb availableTypeFromArray:supportedTypes]; + NSString* contents = [pb stringForType:bestType]; + UTF8ToUTF16([contents UTF8String], + [contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding], + markup); + } + + // TODO(avi): src_url? + if (src_url) + src_url->clear(); +} + +void Clipboard::ReadBookmark(string16* title, std::string* url) const { + NSPasteboard* pb = GetPasteboard(); + + if (title) { + NSString* contents = [pb stringForType:kUTTypeURLName]; + UTF8ToUTF16([contents UTF8String], + [contents lengthOfBytesUsingEncoding:NSUTF8StringEncoding], + title); + } + + if (url) { + NSString* url_string = [[NSURL URLFromPasteboard:pb] absoluteString]; + if (!url_string) + url->clear(); + else + url->assign([url_string UTF8String]); + } +} + +void Clipboard::ReadFile(FilePath* file) const { + if (!file) { + NOTREACHED(); + return; + } + + *file = FilePath(); + std::vector files; + ReadFiles(&files); + + // Take the first file, if available. + if (!files.empty()) + *file = files[0]; +} + +void Clipboard::ReadFiles(std::vector* files) const { + if (!files) { + NOTREACHED(); + return; + } + + files->clear(); + + NSPasteboard* pb = GetPasteboard(); + NSArray* fileList = [pb propertyListForType:NSFilenamesPboardType]; + + for (unsigned int i = 0; i < [fileList count]; ++i) { + std::string file = [[fileList objectAtIndex:i] UTF8String]; + files->push_back(FilePath(file)); + } +} + +// static +Clipboard::FormatType Clipboard::GetUrlFormatType() { + static const std::string type = base::SysNSStringToUTF8(NSURLPboardType); + return type; +} + +// static +Clipboard::FormatType Clipboard::GetUrlWFormatType() { + static const std::string type = base::SysNSStringToUTF8(NSURLPboardType); + return type; +} + +// static +Clipboard::FormatType Clipboard::GetPlainTextFormatType() { + static const std::string type = base::SysNSStringToUTF8(NSStringPboardType); + return type; +} + +// static +Clipboard::FormatType Clipboard::GetPlainTextWFormatType() { + static const std::string type = base::SysNSStringToUTF8(NSStringPboardType); + return type; +} + +// static +Clipboard::FormatType Clipboard::GetFilenameFormatType() { + static const std::string type = + base::SysNSStringToUTF8(NSFilenamesPboardType); + return type; +} + +// static +Clipboard::FormatType Clipboard::GetFilenameWFormatType() { + static const std::string type = + base::SysNSStringToUTF8(NSFilenamesPboardType); + return type; +} + +// static +Clipboard::FormatType Clipboard::GetHtmlFormatType() { + static const std::string type = base::SysNSStringToUTF8(NSHTMLPboardType); + return type; +} + +// static +Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() { + static const std::string type = + base::SysNSStringToUTF8(kWebSmartPastePboardType); + return type; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/clipboard_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/clipboard_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/clipboard_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/clipboard_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,282 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/basictypes.h" +#include "base/clipboard.h" +#include "base/message_loop.h" +#include "base/scoped_clipboard_writer.h" +#include "base/string_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +#if defined(OS_WIN) +class ClipboardTest : public PlatformTest { + protected: + virtual void SetUp() { + message_loop_.reset(new MessageLoopForUI()); + } + virtual void TearDown() { + } + + private: + scoped_ptr message_loop_; +}; +#elif defined(OS_POSIX) +typedef PlatformTest ClipboardTest; +#endif // defined(OS_WIN) + +TEST_F(ClipboardTest, ClearTest) { + Clipboard clipboard; + + { + ScopedClipboardWriter clipboard_writer(&clipboard); + clipboard_writer.WriteText(ASCIIToUTF16("clear me")); + } + + { + ScopedClipboardWriter clipboard_writer(&clipboard); + clipboard_writer.WriteHTML(ASCIIToUTF16("broom"), ""); + } + + EXPECT_FALSE(clipboard.IsFormatAvailable( + Clipboard::GetPlainTextWFormatType())); + EXPECT_FALSE(clipboard.IsFormatAvailable( + Clipboard::GetPlainTextFormatType())); +} + +TEST_F(ClipboardTest, TextTest) { + Clipboard clipboard; + + string16 text(ASCIIToUTF16("This is a string16!#$")), text_result; + std::string ascii_text; + + { + ScopedClipboardWriter clipboard_writer(&clipboard); + clipboard_writer.WriteText(text); + } + + EXPECT_TRUE(clipboard.IsFormatAvailable( + Clipboard::GetPlainTextWFormatType())); + EXPECT_TRUE(clipboard.IsFormatAvailable( + Clipboard::GetPlainTextFormatType())); + clipboard.ReadText(&text_result); + + EXPECT_EQ(text, text_result); + clipboard.ReadAsciiText(&ascii_text); + EXPECT_EQ(UTF16ToUTF8(text), ascii_text); +} + +TEST_F(ClipboardTest, HTMLTest) { + Clipboard clipboard; + + string16 markup(ASCIIToUTF16("Hi!")), markup_result; + std::string url("http://www.example.com/"), url_result; + + { + ScopedClipboardWriter clipboard_writer(&clipboard); + clipboard_writer.WriteHTML(markup, url); + } + + EXPECT_EQ(true, clipboard.IsFormatAvailable( + Clipboard::GetHtmlFormatType())); + clipboard.ReadHTML(&markup_result, &url_result); + EXPECT_EQ(markup, markup_result); +#if defined(OS_WIN) + // TODO(playmobil): It's not clear that non windows clipboards need to support + // this. + EXPECT_EQ(url, url_result); +#endif // defined(OS_WIN) +} + +TEST_F(ClipboardTest, TrickyHTMLTest) { + Clipboard clipboard; + + string16 markup(ASCIIToUTF16("Bye!")), + markup_result; + std::string url, url_result; + + { + ScopedClipboardWriter clipboard_writer(&clipboard); + clipboard_writer.WriteHTML(markup, url); + } + + EXPECT_EQ(true, clipboard.IsFormatAvailable( + Clipboard::GetHtmlFormatType())); + clipboard.ReadHTML(&markup_result, &url_result); + EXPECT_EQ(markup, markup_result); +#if defined(OS_WIN) + // TODO(playmobil): It's not clear that non windows clipboards need to support + // this. + EXPECT_EQ(url, url_result); +#endif // defined(OS_WIN) +} + +// TODO(estade): Port the following test (decide what target we use for urls) +#if !defined(OS_LINUX) +TEST_F(ClipboardTest, BookmarkTest) { + Clipboard clipboard; + + string16 title(ASCIIToUTF16("The Example Company")), title_result; + std::string url("http://www.example.com/"), url_result; + + { + ScopedClipboardWriter clipboard_writer(&clipboard); + clipboard_writer.WriteBookmark(title, url); + } + + EXPECT_EQ(true, + clipboard.IsFormatAvailable(Clipboard::GetUrlWFormatType())); + clipboard.ReadBookmark(&title_result, &url_result); + EXPECT_EQ(title, title_result); + EXPECT_EQ(url, url_result); +} +#endif // defined(OS_WIN) + +TEST_F(ClipboardTest, MultiFormatTest) { + Clipboard clipboard; + + string16 text(ASCIIToUTF16("Hi!")), text_result; + string16 markup(ASCIIToUTF16("Hi!")), markup_result; + std::string url("http://www.example.com/"), url_result; + std::string ascii_text; + + { + ScopedClipboardWriter clipboard_writer(&clipboard); + clipboard_writer.WriteHTML(markup, url); + clipboard_writer.WriteText(text); + } + + EXPECT_EQ(true, + clipboard.IsFormatAvailable(Clipboard::GetHtmlFormatType())); + EXPECT_EQ(true, clipboard.IsFormatAvailable( + Clipboard::GetPlainTextWFormatType())); + EXPECT_EQ(true, clipboard.IsFormatAvailable( + Clipboard::GetPlainTextFormatType())); + clipboard.ReadHTML(&markup_result, &url_result); + EXPECT_EQ(markup, markup_result); +#if defined(OS_WIN) + // TODO(playmobil): It's not clear that non windows clipboards need to support + // this. + EXPECT_EQ(url, url_result); +#endif // defined(OS_WIN) + clipboard.ReadText(&text_result); + EXPECT_EQ(text, text_result); + clipboard.ReadAsciiText(&ascii_text); + EXPECT_EQ(UTF16ToUTF8(text), ascii_text); +} + +// TODO(estade): Port the following tests (decide what targets we use for files) +#if !defined(OS_LINUX) +// Files for this test don't actually need to exist on the file system, just +// don't try to use a non-existent file you've retrieved from the clipboard. +TEST_F(ClipboardTest, FileTest) { + Clipboard clipboard; +#if defined(OS_WIN) + FilePath file(L"C:\\Downloads\\My Downloads\\A Special File.txt"); +#elif defined(OS_MACOSX) + // OS X will print a warning message if we stick a non-existant file on the + // clipboard. + FilePath file("/usr/bin/make"); +#endif // defined(OS_MACOSX) + + { + ScopedClipboardWriter clipboard_writer(&clipboard); + clipboard_writer.WriteFile(file); + } + + FilePath out_file; + clipboard.ReadFile(&out_file); + EXPECT_EQ(file.value(), out_file.value()); +} + +TEST_F(ClipboardTest, MultipleFilesTest) { + Clipboard clipboard; + +#if defined(OS_WIN) + FilePath file1(L"C:\\Downloads\\My Downloads\\File 1.exe"); + FilePath file2(L"C:\\Downloads\\My Downloads\\File 2.pdf"); + FilePath file3(L"C:\\Downloads\\My Downloads\\File 3.doc"); +#elif defined(OS_MACOSX) + // OS X will print a warning message if we stick a non-existant file on the + // clipboard. + FilePath file1("/usr/bin/make"); + FilePath file2("/usr/bin/man"); + FilePath file3("/usr/bin/perl"); +#endif // defined(OS_MACOSX) + std::vector files; + files.push_back(file1); + files.push_back(file2); + files.push_back(file3); + + { + ScopedClipboardWriter clipboard_writer(&clipboard); + clipboard_writer.WriteFiles(files); + } + + std::vector out_files; + clipboard.ReadFiles(&out_files); + + EXPECT_EQ(files.size(), out_files.size()); + for (size_t i = 0; i < out_files.size(); ++i) + EXPECT_EQ(files[i].value(), out_files[i].value()); +} +#endif // !defined(OS_LINUX) + +#if defined(OS_WIN) // Windows only tests. +TEST_F(ClipboardTest, HyperlinkTest) { + Clipboard clipboard; + + string16 title(ASCIIToUTF16("The Example Company")), title_result; + std::string url("http://www.example.com/"), url_result; + string16 html(ASCIIToUTF16("" + "The Example Company")), html_result; + + { + ScopedClipboardWriter clipboard_writer(&clipboard); + clipboard_writer.WriteHyperlink(title, url); + } + + EXPECT_EQ(true, + clipboard.IsFormatAvailable(Clipboard::GetUrlWFormatType())); + EXPECT_EQ(true, + clipboard.IsFormatAvailable(Clipboard::GetHtmlFormatType())); + clipboard.ReadBookmark(&title_result, &url_result); + EXPECT_EQ(title, title_result); + EXPECT_EQ(url, url_result); + clipboard.ReadHTML(&html_result, &url_result); + EXPECT_EQ(html, html_result); +} + +TEST_F(ClipboardTest, WebSmartPasteTest) { + Clipboard clipboard; + + { + ScopedClipboardWriter clipboard_writer(&clipboard); + clipboard_writer.WriteWebSmartPaste(); + } + + EXPECT_EQ(true, clipboard.IsFormatAvailable( + Clipboard::GetWebKitSmartPasteFormatType())); +} + +TEST_F(ClipboardTest, BitmapTest) { + unsigned int fake_bitmap[] = { + 0x46155189, 0xF6A55C8D, 0x79845674, 0xFA57BD89, + 0x78FD46AE, 0x87C64F5A, 0x36EDC5AF, 0x4378F568, + 0x91E9F63A, 0xC31EA14F, 0x69AB32DF, 0x643A3FD1, + }; + + Clipboard clipboard; + + { + ScopedClipboardWriter clipboard_writer(&clipboard); + clipboard_writer.WriteBitmapFromPixels(fake_bitmap, gfx::Size(3, 4)); + } + + EXPECT_EQ(true, clipboard.IsFormatAvailable( + Clipboard::GetBitmapFormatType())); +} +#endif // defined(OS_WIN) diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/clipboard_util.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/clipboard_util.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/clipboard_util.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/clipboard_util.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,488 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/clipboard_util.h" + +#include +#include +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/scoped_handle.h" +#include "base/string_util.h" + +namespace { + +bool GetUrlFromHDrop(IDataObject* data_object, std::wstring* url, + std::wstring* title) { + DCHECK(data_object && url && title); + + STGMEDIUM medium; + if (FAILED(data_object->GetData(ClipboardUtil::GetCFHDropFormat(), &medium))) + return false; + + HDROP hdrop = static_cast(GlobalLock(medium.hGlobal)); + + if (!hdrop) + return false; + + bool success = false; + wchar_t filename[MAX_PATH]; + if (DragQueryFileW(hdrop, 0, filename, arraysize(filename))) { + wchar_t url_buffer[INTERNET_MAX_URL_LENGTH]; + if (0 == _wcsicmp(PathFindExtensionW(filename), L".url") && + GetPrivateProfileStringW(L"InternetShortcut", L"url", 0, url_buffer, + arraysize(url_buffer), filename)) { + *url = url_buffer; + PathRemoveExtension(filename); + title->assign(PathFindFileName(filename)); + success = true; + } + } + + DragFinish(hdrop); + GlobalUnlock(medium.hGlobal); + // We don't need to call ReleaseStgMedium here because as far as I can tell, + // DragFinish frees the hGlobal for us. + return success; +} + +bool SplitUrlAndTitle(const std::wstring& str, std::wstring* url, + std::wstring* title) { + DCHECK(url && title); + size_t newline_pos = str.find('\n'); + bool success = false; + if (newline_pos != std::string::npos) { + *url = str.substr(0, newline_pos); + title->assign(str.substr(newline_pos + 1)); + success = true; + } else { + *url = str; + title->assign(str); + success = true; + } + return success; +} + +} // namespace + + +FORMATETC* ClipboardUtil::GetUrlFormat() { + static UINT cf = RegisterClipboardFormat(CFSTR_INETURLA); + static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; + return &format; +} + +FORMATETC* ClipboardUtil::GetUrlWFormat() { + static UINT cf = RegisterClipboardFormat(CFSTR_INETURLW); + static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; + return &format; +} + +FORMATETC* ClipboardUtil::GetMozUrlFormat() { + // The format is "URL\nTitle" + static UINT cf = RegisterClipboardFormat(L"text/x-moz-url"); + static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; + return &format; +} + +FORMATETC* ClipboardUtil::GetPlainTextFormat() { + // We don't need to register this format since it's a built in format. + static FORMATETC format = {CF_TEXT, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; + return &format; +} + +FORMATETC* ClipboardUtil::GetPlainTextWFormat() { + // We don't need to register this format since it's a built in format. + static FORMATETC format = {CF_UNICODETEXT, 0, DVASPECT_CONTENT, -1, + TYMED_HGLOBAL}; + return &format; +} + +FORMATETC* ClipboardUtil::GetFilenameWFormat() { + static UINT cf = RegisterClipboardFormat(CFSTR_FILENAMEW); + static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; + return &format; +} + +FORMATETC* ClipboardUtil::GetFilenameFormat() +{ + static UINT cf = RegisterClipboardFormat(CFSTR_FILENAMEA); + static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; + return &format; +} + +FORMATETC* ClipboardUtil::GetHtmlFormat() { + static UINT cf = RegisterClipboardFormat(L"HTML Format"); + static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; + return &format; +} + +FORMATETC* ClipboardUtil::GetTextHtmlFormat() { + static UINT cf = RegisterClipboardFormat(L"text/html"); + static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; + return &format; +} + +FORMATETC* ClipboardUtil::GetCFHDropFormat() { + // We don't need to register this format since it's a built in format. + static FORMATETC format = {CF_HDROP, 0, DVASPECT_CONTENT, -1, + TYMED_HGLOBAL}; + return &format; +} + +FORMATETC* ClipboardUtil::GetFileDescriptorFormat() { + static UINT cf = RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR); + static FORMATETC format = {cf, 0, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; + return &format; +} + +FORMATETC* ClipboardUtil::GetFileContentFormatZero() { + static UINT cf = RegisterClipboardFormat(CFSTR_FILECONTENTS); + static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL}; + return &format; +} + +FORMATETC* ClipboardUtil::GetWebKitSmartPasteFormat() { + static UINT cf = RegisterClipboardFormat(L"WebKit Smart Paste Format"); + static FORMATETC format = {cf, 0, DVASPECT_CONTENT, 0, TYMED_HGLOBAL}; + return &format; +} + + +bool ClipboardUtil::HasUrl(IDataObject* data_object) { + DCHECK(data_object); + return SUCCEEDED(data_object->QueryGetData(GetMozUrlFormat())) || + SUCCEEDED(data_object->QueryGetData(GetUrlWFormat())) || + SUCCEEDED(data_object->QueryGetData(GetUrlFormat())) || + SUCCEEDED(data_object->QueryGetData(GetFilenameWFormat())) || + SUCCEEDED(data_object->QueryGetData(GetFilenameFormat())); +} + +bool ClipboardUtil::HasFilenames(IDataObject* data_object) { + DCHECK(data_object); + return SUCCEEDED(data_object->QueryGetData(GetCFHDropFormat())); +} + +bool ClipboardUtil::HasPlainText(IDataObject* data_object) { + DCHECK(data_object); + return SUCCEEDED(data_object->QueryGetData(GetPlainTextWFormat())) || + SUCCEEDED(data_object->QueryGetData(GetPlainTextFormat())); +} + + +bool ClipboardUtil::GetUrl(IDataObject* data_object, + std::wstring* url, std::wstring* title) { + DCHECK(data_object && url && title); + if (!HasUrl(data_object)) + return false; + + // Try to extract a URL from |data_object| in a variety of formats. + STGMEDIUM store; + if (GetUrlFromHDrop(data_object, url, title)) { + return true; + } + + if (SUCCEEDED(data_object->GetData(GetMozUrlFormat(), &store)) || + SUCCEEDED(data_object->GetData(GetUrlWFormat(), &store))) { + // Mozilla URL format or unicode URL + ScopedHGlobal data(store.hGlobal); + bool success = SplitUrlAndTitle(data.get(), url, title); + ReleaseStgMedium(&store); + if (success) + return true; + } + + if (SUCCEEDED(data_object->GetData(GetUrlFormat(), &store))) { + // URL using ascii + ScopedHGlobal data(store.hGlobal); + bool success = SplitUrlAndTitle(UTF8ToWide(data.get()), url, title); + ReleaseStgMedium(&store); + if (success) + return true; + } + + if (SUCCEEDED(data_object->GetData(GetFilenameWFormat(), &store))) { + // filename using unicode + ScopedHGlobal data(store.hGlobal); + bool success = false; + if (data.get() && data.get()[0] && (PathFileExists(data.get()) || + PathIsUNC(data.get()))) { + wchar_t file_url[INTERNET_MAX_URL_LENGTH]; + DWORD file_url_len = sizeof(file_url) / sizeof(file_url[0]); + if (SUCCEEDED(::UrlCreateFromPathW(data.get(), file_url, &file_url_len, + 0))) { + *url = file_url; + title->assign(file_url); + success = true; + } + } + ReleaseStgMedium(&store); + if (success) + return true; + } + + if (SUCCEEDED(data_object->GetData(GetFilenameFormat(), &store))) { + // filename using ascii + ScopedHGlobal data(store.hGlobal); + bool success = false; + if (data.get() && data.get()[0] && (PathFileExistsA(data.get()) || + PathIsUNCA(data.get()))) { + char file_url[INTERNET_MAX_URL_LENGTH]; + DWORD file_url_len = sizeof(file_url) / sizeof(file_url[0]); + if (SUCCEEDED(::UrlCreateFromPathA(data.get(), + file_url, + &file_url_len, + 0))) { + *url = UTF8ToWide(file_url); + title->assign(*url); + success = true; + } + } + ReleaseStgMedium(&store); + if (success) + return true; + } + + return false; +} + +bool ClipboardUtil::GetFilenames(IDataObject* data_object, + std::vector* filenames) { + DCHECK(data_object && filenames); + if (!HasFilenames(data_object)) + return false; + + STGMEDIUM medium; + if (FAILED(data_object->GetData(GetCFHDropFormat(), &medium))) + return false; + + HDROP hdrop = static_cast(GlobalLock(medium.hGlobal)); + if (!hdrop) + return false; + + const int kMaxFilenameLen = 4096; + const unsigned num_files = DragQueryFileW(hdrop, 0xffffffff, 0, 0); + for (unsigned int i = 0; i < num_files; ++i) { + wchar_t filename[kMaxFilenameLen]; + if (!DragQueryFileW(hdrop, i, filename, kMaxFilenameLen)) + continue; + filenames->push_back(filename); + } + + DragFinish(hdrop); + GlobalUnlock(medium.hGlobal); + // We don't need to call ReleaseStgMedium here because as far as I can tell, + // DragFinish frees the hGlobal for us. + return true; +} + +bool ClipboardUtil::GetPlainText(IDataObject* data_object, + std::wstring* plain_text) { + DCHECK(data_object && plain_text); + if (!HasPlainText(data_object)) + return false; + + STGMEDIUM store; + bool success = false; + if (SUCCEEDED(data_object->GetData(GetPlainTextWFormat(), &store))) { + // Unicode text + ScopedHGlobal data(store.hGlobal); + plain_text->assign(data.get()); + ReleaseStgMedium(&store); + success = true; + } else if (SUCCEEDED(data_object->GetData(GetPlainTextFormat(), &store))) { + // ascii text + ScopedHGlobal data(store.hGlobal); + plain_text->assign(UTF8ToWide(data.get())); + ReleaseStgMedium(&store); + success = true; + } else { + //If a file is dropped on the window, it does not provide either of the + //plain text formats, so here we try to forcibly get a url. + std::wstring title; + success = GetUrl(data_object, plain_text, &title); + } + + return success; +} + +bool ClipboardUtil::GetHtml(IDataObject* data_object, + std::wstring* html, std::string* base_url) { + DCHECK(data_object && html && base_url); + + if (SUCCEEDED(data_object->QueryGetData(GetHtmlFormat()))) { + STGMEDIUM store; + if (SUCCEEDED(data_object->GetData(GetHtmlFormat(), &store))) { + // MS CF html + ScopedHGlobal data(store.hGlobal); + + std::string html_utf8; + CFHtmlToHtml(std::string(data.get(), data.Size()), &html_utf8, base_url); + html->assign(UTF8ToWide(html_utf8)); + + ReleaseStgMedium(&store); + return true; + } + } + + if (FAILED(data_object->QueryGetData(GetTextHtmlFormat()))) + return false; + + STGMEDIUM store; + if (FAILED(data_object->GetData(GetTextHtmlFormat(), &store))) + return false; + + // text/html + ScopedHGlobal data(store.hGlobal); + html->assign(data.get()); + ReleaseStgMedium(&store); + return true; +} + +bool ClipboardUtil::GetFileContents(IDataObject* data_object, + std::wstring* filename, std::string* file_contents) { + DCHECK(data_object && filename && file_contents); + bool has_data = + SUCCEEDED(data_object->QueryGetData(GetFileContentFormatZero())) || + SUCCEEDED(data_object->QueryGetData(GetFileDescriptorFormat())); + + if (!has_data) + return false; + + STGMEDIUM content; + // The call to GetData can be very slow depending on what is in + // |data_object|. + if (SUCCEEDED(data_object->GetData(GetFileContentFormatZero(), &content))) { + if (TYMED_HGLOBAL == content.tymed) { + ScopedHGlobal data(content.hGlobal); + file_contents->assign(data.get(), data.Size()); + } + ReleaseStgMedium(&content); + } + + STGMEDIUM description; + if (SUCCEEDED(data_object->GetData(GetFileDescriptorFormat(), + &description))) { + ScopedHGlobal fgd(description.hGlobal); + // We expect there to be at least one file in here. + DCHECK(fgd->cItems >= 1); + filename->assign(fgd->fgd[0].cFileName); + ReleaseStgMedium(&description); + } + return true; +} + +// HtmlToCFHtml and CFHtmlToHtml are based on similar methods in +// WebCore/platform/win/ClipboardUtilitiesWin.cpp. +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +// Helper method for converting from text/html to MS CF_HTML. +// Documentation for the CF_HTML format is available at +// http://msdn.microsoft.com/en-us/library/aa767917(VS.85).aspx +std::string ClipboardUtil::HtmlToCFHtml(const std::string& html, + const std::string& base_url) { + if (html.empty()) + return std::string(); + + #define MAX_DIGITS 10 + #define MAKE_NUMBER_FORMAT_1(digits) MAKE_NUMBER_FORMAT_2(digits) + #define MAKE_NUMBER_FORMAT_2(digits) "%0" #digits "u" + #define NUMBER_FORMAT MAKE_NUMBER_FORMAT_1(MAX_DIGITS) + + static const char* header = "Version:0.9\r\n" + "StartHTML:" NUMBER_FORMAT "\r\n" + "EndHTML:" NUMBER_FORMAT "\r\n" + "StartFragment:" NUMBER_FORMAT "\r\n" + "EndFragment:" NUMBER_FORMAT "\r\n"; + static const char* source_url_prefix = "SourceURL:"; + + static const char* start_markup = + "\r\n\r\n\r\n"; + static const char* end_markup = + "\r\n\r\n\r\n"; + + // Calculate offsets + size_t start_html_offset = strlen(header) - strlen(NUMBER_FORMAT) * 4 + + MAX_DIGITS * 4; + if (!base_url.empty()) { + start_html_offset += strlen(source_url_prefix) + + base_url.length() + 2; // Add 2 for \r\n. + } + size_t start_fragment_offset = start_html_offset + strlen(start_markup); + size_t end_fragment_offset = start_fragment_offset + html.length(); + size_t end_html_offset = end_fragment_offset + strlen(end_markup); + + std::string result = StringPrintf(header, start_html_offset, + end_html_offset, start_fragment_offset, end_fragment_offset); + if (!base_url.empty()) { + result.append(source_url_prefix); + result.append(base_url); + result.append("\r\n"); + } + result.append(start_markup); + result.append(html); + result.append(end_markup); + + #undef MAX_DIGITS + #undef MAKE_NUMBER_FORMAT_1 + #undef MAKE_NUMBER_FORMAT_2 + #undef NUMBER_FORMAT + + return result; +} + +// Helper method for converting from MS CF_HTML to text/html. +void ClipboardUtil::CFHtmlToHtml(const std::string& cf_html, + std::string* html, + std::string* base_url) { + // Obtain base_url if present. + static std::string src_url_str("SourceURL:"); + size_t line_start = cf_html.find(src_url_str); + if (line_start != std::string::npos) { + size_t src_end = cf_html.find("\n", line_start); + size_t src_start = line_start + src_url_str.length(); + if (src_end != std::string::npos && src_start != std::string::npos) { + *base_url = cf_html.substr(src_start, src_end - src_start); + TrimWhitespace(*base_url, TRIM_ALL, base_url); + } + } + + // Find the markup between "" and "". + std::string cf_html_lower = StringToLowerASCII(cf_html); + size_t markup_start = cf_html_lower.find("', tag_start) + 1; + size_t tag_end = cf_html.rfind("EndFragment", std::string::npos); + size_t fragment_end = cf_html.rfind('<', tag_end); + if (fragment_start != std::string::npos && + fragment_end != std::string::npos) { + *html = cf_html.substr(fragment_start, fragment_end - fragment_start); + TrimWhitespace(*html, TRIM_ALL, html); + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/clipboard_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/clipboard_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/clipboard_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/clipboard_util.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,63 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Some helper functions for working with the clipboard and IDataObjects. + +#ifndef BASE_CLIPBOARD_UTIL_H_ +#define BASE_CLIPBOARD_UTIL_H_ + +#include +#include +#include + +class ClipboardUtil { + public: + ///////////////////////////////////////////////////////////////////////////// + // Clipboard formats. + static FORMATETC* GetUrlFormat(); + static FORMATETC* GetUrlWFormat(); + static FORMATETC* GetMozUrlFormat(); + static FORMATETC* GetPlainTextFormat(); + static FORMATETC* GetPlainTextWFormat(); + static FORMATETC* GetFilenameFormat(); + static FORMATETC* GetFilenameWFormat(); + // MS HTML Format + static FORMATETC* GetHtmlFormat(); + // Firefox text/html + static FORMATETC* GetTextHtmlFormat(); + static FORMATETC* GetCFHDropFormat(); + static FORMATETC* GetFileDescriptorFormat(); + static FORMATETC* GetFileContentFormatZero(); + static FORMATETC* GetWebKitSmartPasteFormat(); + + ///////////////////////////////////////////////////////////////////////////// + // These methods check to see if |data_object| has the requested type. + // Returns true if it does. + static bool HasUrl(IDataObject* data_object); + static bool HasFilenames(IDataObject* data_object); + static bool HasPlainText(IDataObject* data_object); + + ///////////////////////////////////////////////////////////////////////////// + // Helper methods to extract information from an IDataObject. These methods + // return true if the requested data type is found in |data_object|. + static bool GetUrl(IDataObject* data_object, + std::wstring* url, std::wstring* title); + static bool GetFilenames(IDataObject* data_object, + std::vector* filenames); + static bool GetPlainText(IDataObject* data_object, std::wstring* plain_text); + static bool GetHtml(IDataObject* data_object, std::wstring* text_html, + std::string* base_url); + static bool GetFileContents(IDataObject* data_object, + std::wstring* filename, + std::string* file_contents); + + // A helper method for converting between MS CF_HTML format and plain + // text/html. + static std::string HtmlToCFHtml(const std::string& html, + const std::string& base_url); + static void CFHtmlToHtml(const std::string& cf_html, std::string* html, + std::string* base_url); +}; + +#endif // BASE_CLIPBOARD_UTIL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/clipboard_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/clipboard_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/clipboard_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/clipboard_win.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,650 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Many of these functions are based on those found in +// webkit/port/platform/PasteboardWin.cpp + +#include "base/clipboard.h" + +#include +#include + +#include "base/clipboard_util.h" +#include "base/lock.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/shared_memory.h" +#include "base/string_util.h" + +namespace { + +// A scoper to manage acquiring and automatically releasing the clipboard. +class ScopedClipboard { + public: + ScopedClipboard() : opened_(false) { } + + ~ScopedClipboard() { + if (opened_) + Release(); + } + + bool Acquire(HWND owner) { + const int kMaxAttemptsToOpenClipboard = 5; + + if (opened_) { + NOTREACHED(); + return false; + } + + // Attempt to open the clipboard, which will acquire the Windows clipboard + // lock. This may fail if another process currently holds this lock. + // We're willing to try a few times in the hopes of acquiring it. + // + // This turns out to be an issue when using remote desktop because the + // rdpclip.exe process likes to read what we've written to the clipboard and + // send it to the RDP client. If we open and close the clipboard in quick + // succession, we might be trying to open it while rdpclip.exe has it open, + // See Bug 815425. + // + // In fact, we believe we'll only spin this loop over remote desktop. In + // normal situations, the user is initiating clipboard operations and there + // shouldn't be contention. + + for (int attempts = 0; attempts < kMaxAttemptsToOpenClipboard; ++attempts) { + // If we didn't manage to open the clipboard, sleep a bit and be hopeful. + if (attempts != 0) + ::Sleep(5); + + if (::OpenClipboard(owner)) { + opened_ = true; + return true; + } + } + + // We failed to acquire the clipboard. + return false; + } + + void Release() { + if (opened_) { + ::CloseClipboard(); + opened_ = false; + } else { + NOTREACHED(); + } + } + + private: + bool opened_; +}; + +LRESULT CALLBACK ClipboardOwnerWndProc(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam) { + LRESULT lresult = 0; + + switch (message) { + case WM_RENDERFORMAT: + // This message comes when SetClipboardData was sent a null data handle + // and now it's come time to put the data on the clipboard. + // We always set data, so there isn't a need to actually do anything here. + break; + case WM_RENDERALLFORMATS: + // This message comes when SetClipboardData was sent a null data handle + // and now this application is about to quit, so it must put data on + // the clipboard before it exits. + // We always set data, so there isn't a need to actually do anything here. + break; + case WM_DRAWCLIPBOARD: + break; + case WM_DESTROY: + break; + case WM_CHANGECBCHAIN: + break; + default: + lresult = DefWindowProc(hwnd, message, wparam, lparam); + break; + } + return lresult; +} + +template +HGLOBAL CreateGlobalData(const std::basic_string& str) { + HGLOBAL data = + ::GlobalAlloc(GMEM_MOVEABLE, ((str.size() + 1) * sizeof(charT))); + if (data) { + charT* raw_data = static_cast(::GlobalLock(data)); + memcpy(raw_data, str.data(), str.size() * sizeof(charT)); + raw_data[str.size()] = '\0'; + ::GlobalUnlock(data); + } + return data; +}; + +} // namespace + +Clipboard::Clipboard() : create_window_(false) { + if (MessageLoop::current()->type() == MessageLoop::TYPE_UI) { + // Make a dummy HWND to be the clipboard's owner. + WNDCLASSEX wcex = {0}; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.lpfnWndProc = ClipboardOwnerWndProc; + wcex.hInstance = GetModuleHandle(NULL); + wcex.lpszClassName = L"ClipboardOwnerWindowClass"; + ::RegisterClassEx(&wcex); + create_window_ = true; + } + + clipboard_owner_ = NULL; +} + +Clipboard::~Clipboard() { + if (clipboard_owner_) + ::DestroyWindow(clipboard_owner_); + clipboard_owner_ = NULL; +} + +void Clipboard::WriteObjects(const ObjectMap& objects) { + WriteObjects(objects, NULL); +} + +void Clipboard::WriteObjects(const ObjectMap& objects, + base::ProcessHandle process) { + ScopedClipboard clipboard; + if (!clipboard.Acquire(GetClipboardWindow())) + return; + + ::EmptyClipboard(); + + for (ObjectMap::const_iterator iter = objects.begin(); + iter != objects.end(); ++iter) { + if (iter->first == CBF_SMBITMAP) + WriteBitmapFromSharedMemory(&(iter->second[0].front()), + &(iter->second[1].front()), + process); + else + DispatchObject(static_cast(iter->first), iter->second); + } +} + +void Clipboard::WriteText(const char* text_data, size_t text_len) { + string16 text; + UTF8ToUTF16(text_data, text_len, &text); + HGLOBAL glob = CreateGlobalData(text); + + WriteToClipboard(CF_UNICODETEXT, glob); +} + +void Clipboard::WriteHTML(const char* markup_data, + size_t markup_len, + const char* url_data, + size_t url_len) { + std::string markup(markup_data, markup_len); + std::string url; + + if (url_len > 0) + url.assign(url_data, url_len); + + std::string html_fragment = ClipboardUtil::HtmlToCFHtml(markup, url); + HGLOBAL glob = CreateGlobalData(html_fragment); + + WriteToClipboard(StringToInt(GetHtmlFormatType()), glob); +} + +void Clipboard::WriteBookmark(const char* title_data, + size_t title_len, + const char* url_data, + size_t url_len) { + std::string bookmark(title_data, title_len); + bookmark.append(1, L'\n'); + bookmark.append(url_data, url_len); + + string16 wide_bookmark = UTF8ToWide(bookmark); + HGLOBAL glob = CreateGlobalData(wide_bookmark); + + WriteToClipboard(StringToInt(GetUrlWFormatType()), glob); +} + +void Clipboard::WriteHyperlink(const char* title_data, + size_t title_len, + const char* url_data, + size_t url_len) { + // Store as a bookmark. + WriteBookmark(title_data, title_len, url_data, url_len); + + std::string title(title_data, title_len), + url(url_data, url_len), + link(""); + link.append(title); + link.append(""); + + // Store hyperlink as html. + WriteHTML(link.c_str(), link.size(), NULL, 0); +} + +void Clipboard::WriteWebSmartPaste() { + DCHECK(clipboard_owner_); + ::SetClipboardData(StringToInt(GetWebKitSmartPasteFormatType()), NULL); +} + +void Clipboard::WriteBitmap(const char* pixel_data, const char* size_data) { + const gfx::Size* size = reinterpret_cast(size_data); + HDC dc = ::GetDC(NULL); + + // This doesn't actually cost us a memcpy when the bitmap comes from the + // renderer as we load it into the bitmap using setPixels which just sets a + // pointer. Someone has to memcpy it into GDI, it might as well be us here. + + // TODO(darin): share data in gfx/bitmap_header.cc somehow + BITMAPINFO bm_info = {0}; + bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bm_info.bmiHeader.biWidth = size->width(); + bm_info.bmiHeader.biHeight = -size->height(); // sets vertical orientation + bm_info.bmiHeader.biPlanes = 1; + bm_info.bmiHeader.biBitCount = 32; + bm_info.bmiHeader.biCompression = BI_RGB; + + // ::CreateDIBSection allocates memory for us to copy our bitmap into. + // Unfortunately, we can't write the created bitmap to the clipboard, + // (see http://msdn2.microsoft.com/en-us/library/ms532292.aspx) + void *bits; + HBITMAP source_hbitmap = + ::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, &bits, NULL, 0); + + if (bits && source_hbitmap) { + // Copy the bitmap out of shared memory and into GDI + memcpy(bits, pixel_data, 4 * size->width() * size->height()); + + // Now we have an HBITMAP, we can write it to the clipboard + WriteBitmapFromHandle(source_hbitmap, *size); + } + + ::DeleteObject(source_hbitmap); + ::ReleaseDC(NULL, dc); +} + +void Clipboard::WriteBitmapFromSharedMemory(const char* bitmap_data, + const char* size_data, + base::ProcessHandle process) { + const gfx::Size* size = reinterpret_cast(size_data); + + // bitmap_data has an encoded shared memory object. See + // DuplicateRemoteHandles(). + char* ptr = const_cast(bitmap_data); + scoped_ptr bitmap(* + reinterpret_cast(ptr)); + + // TODO(darin): share data in gfx/bitmap_header.cc somehow. + BITMAPINFO bm_info = {0}; + bm_info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + bm_info.bmiHeader.biWidth = size->width(); + // Sets the vertical orientation. + bm_info.bmiHeader.biHeight = -size->height(); + bm_info.bmiHeader.biPlanes = 1; + bm_info.bmiHeader.biBitCount = 32; + bm_info.bmiHeader.biCompression = BI_RGB; + + HDC dc = ::GetDC(NULL); + + // We can create an HBITMAP directly using the shared memory handle, saving + // a memcpy. + HBITMAP source_hbitmap = + ::CreateDIBSection(dc, &bm_info, DIB_RGB_COLORS, NULL, + bitmap->handle(), 0); + + if (source_hbitmap) { + // Now we can write the HBITMAP to the clipboard + WriteBitmapFromHandle(source_hbitmap, *size); + } + + ::DeleteObject(source_hbitmap); + ::ReleaseDC(NULL, dc); +} + +void Clipboard::WriteBitmapFromHandle(HBITMAP source_hbitmap, + const gfx::Size& size) { + // We would like to just call ::SetClipboardData on the source_hbitmap, + // but that bitmap might not be of a sort we can write to the clipboard. + // For this reason, we create a new bitmap, copy the bits over, and then + // write that to the clipboard. + + HDC dc = ::GetDC(NULL); + HDC compatible_dc = ::CreateCompatibleDC(NULL); + HDC source_dc = ::CreateCompatibleDC(NULL); + + // This is the HBITMAP we will eventually write to the clipboard + HBITMAP hbitmap = ::CreateCompatibleBitmap(dc, size.width(), size.height()); + if (!hbitmap) { + // Failed to create the bitmap + ::DeleteDC(compatible_dc); + ::DeleteDC(source_dc); + ::ReleaseDC(NULL, dc); + return; + } + + HBITMAP old_hbitmap = (HBITMAP)SelectObject(compatible_dc, hbitmap); + HBITMAP old_source = (HBITMAP)SelectObject(source_dc, source_hbitmap); + + // Now we need to blend it into an HBITMAP we can place on the clipboard + BLENDFUNCTION bf = {AC_SRC_OVER, 0, 255, AC_SRC_ALPHA}; + ::GdiAlphaBlend(compatible_dc, 0, 0, size.width(), size.height(), + source_dc, 0, 0, size.width(), size.height(), bf); + + // Clean up all the handles we just opened + ::SelectObject(compatible_dc, old_hbitmap); + ::SelectObject(source_dc, old_source); + ::DeleteObject(old_hbitmap); + ::DeleteObject(old_source); + ::DeleteDC(compatible_dc); + ::DeleteDC(source_dc); + ::ReleaseDC(NULL, dc); + + WriteToClipboard(CF_BITMAP, hbitmap); +} + +// Write a file or set of files to the clipboard in HDROP format. When the user +// invokes a paste command (in a Windows explorer shell, for example), the files +// will be copied to the paste location. +void Clipboard::WriteFiles(const char* file_data, size_t file_len) { + // Calculate the amount of space we'll need store the strings and + // a DROPFILES struct. + size_t bytes = sizeof(DROPFILES) + file_len; + + HANDLE hdata = ::GlobalAlloc(GMEM_MOVEABLE, bytes); + if (!hdata) + return; + + char* data = static_cast(::GlobalLock(hdata)); + DROPFILES* drop_files = reinterpret_cast(data); + drop_files->pFiles = sizeof(DROPFILES); + drop_files->fWide = TRUE; + + memcpy(data + sizeof(DROPFILES), file_data, file_len); + + ::GlobalUnlock(hdata); + WriteToClipboard(CF_HDROP, hdata); +} + +void Clipboard::WriteToClipboard(unsigned int format, HANDLE handle) { + DCHECK(clipboard_owner_); + if (handle && !::SetClipboardData(format, handle)) { + DCHECK(ERROR_CLIPBOARD_NOT_OPEN != GetLastError()); + FreeData(format, handle); + } +} + +bool Clipboard::IsFormatAvailable(const Clipboard::FormatType& format) const { + return ::IsClipboardFormatAvailable(StringToInt(format)) != FALSE; +} + +void Clipboard::ReadText(string16* result) const { + if (!result) { + NOTREACHED(); + return; + } + + result->clear(); + + // Acquire the clipboard. + ScopedClipboard clipboard; + if (!clipboard.Acquire(GetClipboardWindow())) + return; + + HANDLE data = ::GetClipboardData(CF_UNICODETEXT); + if (!data) + return; + + result->assign(static_cast(::GlobalLock(data))); + ::GlobalUnlock(data); +} + +void Clipboard::ReadAsciiText(std::string* result) const { + if (!result) { + NOTREACHED(); + return; + } + + result->clear(); + + // Acquire the clipboard. + ScopedClipboard clipboard; + if (!clipboard.Acquire(GetClipboardWindow())) + return; + + HANDLE data = ::GetClipboardData(CF_TEXT); + if (!data) + return; + + result->assign(static_cast(::GlobalLock(data))); + ::GlobalUnlock(data); +} + +void Clipboard::ReadHTML(string16* markup, std::string* src_url) const { + if (markup) + markup->clear(); + + if (src_url) + src_url->clear(); + + // Acquire the clipboard. + ScopedClipboard clipboard; + if (!clipboard.Acquire(GetClipboardWindow())) + return; + + HANDLE data = ::GetClipboardData(StringToInt(GetHtmlFormatType())); + if (!data) + return; + + std::string html_fragment(static_cast(::GlobalLock(data))); + ::GlobalUnlock(data); + + std::string markup_utf8; + ClipboardUtil::CFHtmlToHtml(html_fragment, &markup_utf8, src_url); + markup->assign(UTF8ToWide(markup_utf8)); +} + +void Clipboard::ReadBookmark(string16* title, std::string* url) const { + if (title) + title->clear(); + + if (url) + url->clear(); + + // Acquire the clipboard. + ScopedClipboard clipboard; + if (!clipboard.Acquire(GetClipboardWindow())) + return; + + HANDLE data = ::GetClipboardData(StringToInt(GetUrlWFormatType())); + if (!data) + return; + + string16 bookmark(static_cast(::GlobalLock(data))); + ::GlobalUnlock(data); + + ParseBookmarkClipboardFormat(bookmark, title, url); +} + +// Read a file in HDROP format from the clipboard. +void Clipboard::ReadFile(FilePath* file) const { + if (!file) { + NOTREACHED(); + return; + } + + *file = FilePath(); + std::vector files; + ReadFiles(&files); + + // Take the first file, if available. + if (!files.empty()) + *file = files[0]; +} + +// Read a set of files in HDROP format from the clipboard. +void Clipboard::ReadFiles(std::vector* files) const { + if (!files) { + NOTREACHED(); + return; + } + + files->clear(); + + ScopedClipboard clipboard; + if (!clipboard.Acquire(GetClipboardWindow())) + return; + + HDROP drop = static_cast(::GetClipboardData(CF_HDROP)); + if (!drop) + return; + + // Count of files in the HDROP. + int count = ::DragQueryFile(drop, 0xffffffff, NULL, 0); + + if (count) { + for (int i = 0; i < count; ++i) { + int size = ::DragQueryFile(drop, i, NULL, 0) + 1; + std::wstring file; + ::DragQueryFile(drop, i, WriteInto(&file, size), size); + files->push_back(FilePath(file)); + } + } +} + +// static +void Clipboard::ParseBookmarkClipboardFormat(const string16& bookmark, + string16* title, + std::string* url) { + const string16 kDelim = ASCIIToUTF16("\r\n"); + + const size_t title_end = bookmark.find_first_of(kDelim); + if (title) + title->assign(bookmark.substr(0, title_end)); + + if (url) { + const size_t url_start = bookmark.find_first_not_of(kDelim, title_end); + if (url_start != string16::npos) + *url = UTF16ToUTF8(bookmark.substr(url_start, string16::npos)); + } +} + +// static +Clipboard::FormatType Clipboard::GetUrlFormatType() { + return IntToString(ClipboardUtil::GetUrlFormat()->cfFormat); +} + +// static +Clipboard::FormatType Clipboard::GetUrlWFormatType() { + return IntToString(ClipboardUtil::GetUrlWFormat()->cfFormat); +} + +// static +Clipboard::FormatType Clipboard::GetMozUrlFormatType() { + return IntToString(ClipboardUtil::GetMozUrlFormat()->cfFormat); +} + +// static +Clipboard::FormatType Clipboard::GetPlainTextFormatType() { + return IntToString(ClipboardUtil::GetPlainTextFormat()->cfFormat); +} + +// static +Clipboard::FormatType Clipboard::GetPlainTextWFormatType() { + return IntToString(ClipboardUtil::GetPlainTextWFormat()->cfFormat); +} + +// static +Clipboard::FormatType Clipboard::GetFilenameFormatType() { + return IntToString(ClipboardUtil::GetFilenameFormat()->cfFormat); +} + +// static +Clipboard::FormatType Clipboard::GetFilenameWFormatType() { + return IntToString(ClipboardUtil::GetFilenameWFormat()->cfFormat); +} + +// MS HTML Format +// static +Clipboard::FormatType Clipboard::GetHtmlFormatType() { + return IntToString(ClipboardUtil::GetHtmlFormat()->cfFormat); +} + +// static +Clipboard::FormatType Clipboard::GetBitmapFormatType() { + return IntToString(CF_BITMAP); +} + +// Firefox text/html +// static +Clipboard::FormatType Clipboard::GetTextHtmlFormatType() { + return IntToString(ClipboardUtil::GetTextHtmlFormat()->cfFormat); +} + +// static +Clipboard::FormatType Clipboard::GetCFHDropFormatType() { + return IntToString(ClipboardUtil::GetCFHDropFormat()->cfFormat); +} + +// static +Clipboard::FormatType Clipboard::GetFileDescriptorFormatType() { + return IntToString(ClipboardUtil::GetFileDescriptorFormat()->cfFormat); +} + +// static +Clipboard::FormatType Clipboard::GetFileContentFormatZeroType() { + return IntToString(ClipboardUtil::GetFileContentFormatZero()->cfFormat); +} + +// static +void Clipboard::DuplicateRemoteHandles(base::ProcessHandle process, + ObjectMap* objects) { + for (ObjectMap::iterator iter = objects->begin(); iter != objects->end(); + ++iter) { + if (iter->first == CBF_SMBITMAP) { + // There is a shared memory handle encoded on the first ObjectMapParam. + // Use it to open a local handle to the memory. + char* bitmap_data = &(iter->second[0].front()); + base::SharedMemoryHandle* remote_bitmap_handle = + reinterpret_cast(bitmap_data); + + base::SharedMemory* bitmap = new base::SharedMemory(*remote_bitmap_handle, + false, process); + + // We store the object where the remote handle was located so it can + // be retrieved by the UI thread (see WriteBitmapFromSharedMemory()). + iter->second[0].clear(); + for (size_t i = 0; i < sizeof(bitmap); i++) + iter->second[0].push_back(reinterpret_cast(&bitmap)[i]); + } + } +} + +// static +Clipboard::FormatType Clipboard::GetWebKitSmartPasteFormatType() { + return IntToString(ClipboardUtil::GetWebKitSmartPasteFormat()->cfFormat); +} + +// static +void Clipboard::FreeData(unsigned int format, HANDLE data) { + if (format == CF_BITMAP) + ::DeleteObject(static_cast(data)); + else + ::GlobalFree(data); +} + +HWND Clipboard::GetClipboardWindow() const { + if (!clipboard_owner_ && create_window_) { + clipboard_owner_ = ::CreateWindow(L"ClipboardOwnerWindowClass", + L"ClipboardOwnerWindow", + 0, 0, 0, 0, 0, + HWND_MESSAGE, + 0, 0, 0); + } + return clipboard_owner_; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/command_line.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/command_line.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/command_line.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/command_line.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,362 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/command_line.h" + +#if defined(OS_WIN) +#include +#include +#endif + +#include + +#include "base/logging.h" +#include "base/singleton.h" +#include "base/string_piece.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" + +CommandLine* CommandLine::current_process_commandline_ = NULL; + +// Since we use a lazy match, make sure that longer versions (like L"--") +// are listed before shorter versions (like L"-") of similar prefixes. +#if defined(OS_WIN) +const wchar_t* const kSwitchPrefixes[] = {L"--", L"-", L"/"}; +const wchar_t kSwitchTerminator[] = L"--"; +const wchar_t kSwitchValueSeparator[] = L"="; +#elif defined(OS_POSIX) +// Unixes don't use slash as a switch. +const char* const kSwitchPrefixes[] = {"--", "-"}; +const char kSwitchTerminator[] = "--"; +const char kSwitchValueSeparator[] = "="; +#endif + +#if defined(OS_WIN) +// Lowercase a string. This is used to lowercase switch names. +// Is this what we really want? It seems crazy to me. I've left it in +// for backwards compatibility on Windows. +static void Lowercase(std::wstring* parameter) { + transform(parameter->begin(), parameter->end(), parameter->begin(), + tolower); +} +#endif + +#if defined(OS_WIN) +void CommandLine::ParseFromString(const std::wstring& command_line) { + TrimWhitespace(command_line, TRIM_ALL, &command_line_string_); + + if (command_line_string_.empty()) + return; + + int num_args = 0; + wchar_t** args = NULL; + + args = CommandLineToArgvW(command_line_string_.c_str(), &num_args); + + // Populate program_ with the trimmed version of the first arg. + TrimWhitespace(args[0], TRIM_ALL, &program_); + + bool parse_switches = true; + for (int i = 1; i < num_args; ++i) { + std::wstring arg; + TrimWhitespace(args[i], TRIM_ALL, &arg); + + if (!parse_switches) { + loose_values_.push_back(arg); + continue; + } + + if (arg == kSwitchTerminator) { + parse_switches = false; + continue; + } + + std::string switch_string; + std::wstring switch_value; + if (IsSwitch(arg, &switch_string, &switch_value)) { + switches_[switch_string] = switch_value; + } else { + loose_values_.push_back(arg); + } + } + + if (args) + LocalFree(args); +} +CommandLine::CommandLine(const std::wstring& program) { + if (!program.empty()) { + program_ = program; + command_line_string_ = L'"' + program + L'"'; + } +} +#elif defined(OS_POSIX) +CommandLine::CommandLine(int argc, const char* const* argv) { + for (int i = 0; i < argc; ++i) + argv_.push_back(argv[i]); + InitFromArgv(); +} +CommandLine::CommandLine(const std::vector& argv) { + argv_ = argv; + InitFromArgv(); +} + +void CommandLine::InitFromArgv() { + bool parse_switches = true; + for (size_t i = 1; i < argv_.size(); ++i) { + const std::string& arg = argv_[i]; + + if (!parse_switches) { + loose_values_.push_back(arg); + continue; + } + + if (arg == kSwitchTerminator) { + parse_switches = false; + continue; + } + + std::string switch_string; + std::string switch_value; + if (IsSwitch(arg, &switch_string, &switch_value)) { + switches_[switch_string] = switch_value; + } else { + loose_values_.push_back(arg); + } + } +} + +CommandLine::CommandLine(const std::wstring& program) { + argv_.push_back(WideToASCII(program)); +} +#endif + +// static +bool CommandLine::IsSwitch(const StringType& parameter_string, + std::string* switch_string, + StringType* switch_value) { + switch_string->clear(); + switch_value->clear(); + + for (size_t i = 0; i < arraysize(kSwitchPrefixes); ++i) { + StringType prefix(kSwitchPrefixes[i]); + if (parameter_string.find(prefix) != 0) + continue; + + const size_t switch_start = prefix.length(); + const size_t equals_position = parameter_string.find( + kSwitchValueSeparator, switch_start); + StringType switch_native; + if (equals_position == StringType::npos) { + switch_native = parameter_string.substr(switch_start); + } else { + switch_native = parameter_string.substr( + switch_start, equals_position - switch_start); + *switch_value = parameter_string.substr(equals_position + 1); + } +#if defined(OS_WIN) + Lowercase(&switch_native); + *switch_string = WideToASCII(switch_native); +#else + *switch_string = switch_native; +#endif + + return true; + } + + return false; +} + +// static +void CommandLine::Init(int argc, const char* const* argv) { + DCHECK(current_process_commandline_ == NULL); +#if defined(OS_WIN) + current_process_commandline_ = new CommandLine; + current_process_commandline_->ParseFromString(::GetCommandLineW()); +#elif defined(OS_POSIX) + current_process_commandline_ = new CommandLine(argc, argv); +#endif +} + +void CommandLine::Terminate() { + DCHECK(current_process_commandline_ != NULL); + delete current_process_commandline_; + current_process_commandline_ = NULL; +} + +bool CommandLine::HasSwitch(const std::wstring& switch_string) const { + std::wstring lowercased_switch(switch_string); +#if defined(OS_WIN) + Lowercase(&lowercased_switch); +#endif + return switches_.find(WideToASCII(lowercased_switch)) != switches_.end(); +} + +std::wstring CommandLine::GetSwitchValue( + const std::wstring& switch_string) const { + std::wstring lowercased_switch(switch_string); +#if defined(OS_WIN) + Lowercase(&lowercased_switch); +#endif + + std::map::const_iterator result = + switches_.find(WideToASCII(lowercased_switch)); + + if (result == switches_.end()) { + return L""; + } else { +#if defined(OS_WIN) + return result->second; +#else + return ASCIIToWide(result->second); +#endif + } +} + +#if defined(OS_WIN) +std::vector CommandLine::GetLooseValues() const { + return loose_values_; +} +std::wstring CommandLine::program() const { + return program_; +} +#else +std::vector CommandLine::GetLooseValues() const { + std::vector values; + for (size_t i = 0; i < loose_values_.size(); ++i) + values.push_back(ASCIIToWide(loose_values_[i])); + return values; +} +std::wstring CommandLine::program() const { + DCHECK(argv_.size() > 0); + return ASCIIToWide(argv_[0]); +} +#endif + + +// static +std::wstring CommandLine::PrefixedSwitchString( + const std::wstring& switch_string) { + return StringPrintf(L"%ls%ls", + kSwitchPrefixes[0], + switch_string.c_str()); +} + +// static +std::wstring CommandLine::PrefixedSwitchStringWithValue( + const std::wstring& switch_string, const std::wstring& value_string) { + if (value_string.empty()) { + return PrefixedSwitchString(switch_string); + } + + return StringPrintf(L"%ls%ls%ls%ls", + kSwitchPrefixes[0], + switch_string.c_str(), + kSwitchValueSeparator, + value_string.c_str()); +} + +#if defined(OS_WIN) +void CommandLine::AppendSwitch(const std::wstring& switch_string) { + std::wstring prefixed_switch_string = PrefixedSwitchString(switch_string); + command_line_string_.append(L" "); + command_line_string_.append(prefixed_switch_string); + switches_[WideToASCII(switch_string)] = L""; +} + +void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string, + const std::wstring& value_string) { + std::wstring value_string_edit; + + // NOTE(jhughes): If the value contains a quotation mark at one + // end but not both, you may get unusable output. + if (!value_string.empty() && + (value_string.find(L" ") != std::wstring::npos) && + (value_string[0] != L'"') && + (value_string[value_string.length() - 1] != L'"')) { + // need to provide quotes + value_string_edit = StringPrintf(L"\"%ls\"", value_string.c_str()); + } else { + value_string_edit = value_string; + } + + std::wstring combined_switch_string = + PrefixedSwitchStringWithValue(switch_string, value_string_edit); + + command_line_string_.append(L" "); + command_line_string_.append(combined_switch_string); + + switches_[WideToASCII(switch_string)] = value_string; +} + +void CommandLine::AppendLooseValue(const std::wstring& value) { + // TODO(evan): quoting? + command_line_string_.append(L" "); + command_line_string_.append(value); +} + +void CommandLine::AppendArguments(const CommandLine& other, + bool include_program) { + // Verify include_program is used correctly. + // Logic could be shorter but this is clearer. + DCHECK(include_program ? !other.program().empty() : other.program().empty()); + command_line_string_ += L" " + other.command_line_string_; + std::map::const_iterator i; + for (i = other.switches_.begin(); i != other.switches_.end(); ++i) + switches_[i->first] = i->second; +} + +void CommandLine::PrependWrapper(const std::wstring& wrapper) { + // The wrapper may have embedded arguments (like "gdb --args"). In this case, + // we don't pretend to do anything fancy, we just split on spaces. + std::vector wrapper_and_args; + SplitString(wrapper, ' ', &wrapper_and_args); + program_ = wrapper_and_args[0]; + command_line_string_ = wrapper + L" " + command_line_string_; +} + +#elif defined(OS_POSIX) +void CommandLine::AppendSwitch(const std::wstring& switch_string) { + std::string ascii_switch = WideToASCII(switch_string); + argv_.push_back(kSwitchPrefixes[0] + ascii_switch); + switches_[ascii_switch] = ""; +} + +void CommandLine::AppendSwitchWithValue(const std::wstring& switch_string, + const std::wstring& value_string) { + std::string ascii_switch = WideToASCII(switch_string); + std::string ascii_value = WideToASCII(value_string); + + argv_.push_back(kSwitchPrefixes[0] + ascii_switch + + kSwitchValueSeparator + ascii_value); + switches_[ascii_switch] = ascii_value; +} + +void CommandLine::AppendLooseValue(const std::wstring& value) { + argv_.push_back(WideToASCII(value)); +} + +void CommandLine::AppendArguments(const CommandLine& other, + bool include_program) { + // Verify include_program is used correctly. + // Logic could be shorter but this is clearer. + DCHECK(include_program ? !other.program().empty() : other.program().empty()); + + size_t first_arg = include_program ? 0 : 1; + for (size_t i = first_arg; i < other.argv_.size(); ++i) + argv_.push_back(other.argv_[i]); + std::map::const_iterator i; + for (i = other.switches_.begin(); i != other.switches_.end(); ++i) + switches_[i->first] = i->second; +} + +void CommandLine::PrependWrapper(const std::wstring& wrapper_wide) { + // The wrapper may have embedded arguments (like "gdb --args"). In this case, + // we don't pretend to do anything fancy, we just split on spaces. + const std::string wrapper = WideToASCII(wrapper_wide); + std::vector wrapper_and_args; + SplitString(wrapper, ' ', &wrapper_and_args); + argv_.insert(argv_.begin(), wrapper_and_args.begin(), wrapper_and_args.end()); +} + +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/command_line.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/command_line.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/command_line.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/command_line.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,191 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This class works with command lines: building and parsing. +// Switches can optionally have a value attached using an equals sign, +// as in "-switch=value". Arguments that aren't prefixed with a +// switch prefix are considered "loose parameters". Switch names are +// case-insensitive. An argument of "--" will terminate switch +// parsing, causing everything after to be considered as loose +// parameters. + +// There is a singleton read-only CommandLine that represents the command +// line that the current process was started with. It must be initialized +// in main() (or whatever the platform's equivalent function is). + +#ifndef BASE_COMMAND_LINE_H_ +#define BASE_COMMAND_LINE_H_ + +#include "build/build_config.h" + +#include +#include +#include + +#include "base/basictypes.h" +#include "base/logging.h" + +class InProcessBrowserTest; + +class CommandLine { + public: +#if defined(OS_WIN) + // Creates a parsed version of the given command-line string. + // The program name is assumed to be the first item in the string. + void ParseFromString(const std::wstring& command_line); +#elif defined(OS_POSIX) + // Initialize from an argv vector (or directly from main()'s argv). + CommandLine(int argc, const char* const* argv); + explicit CommandLine(const std::vector& argv); +#endif + + // Construct a new, empty command line. + // |program| is the name of the program to run (aka argv[0]). + // TODO(port): should be a FilePath. + explicit CommandLine(const std::wstring& program); + + // Initialize the current process CommandLine singleton. On Windows, + // ignores its arguments (we instead parse GetCommandLineW() + // directly) because we don't trust the CRT's parsing of the command + // line, but it still must be called to set up the command line. + static void Init(int argc, const char* const* argv); + + // Destroys the current process CommandLine singleton. This is necessary if + // you want to reset the base library to its initial state (for example in an + // outer library that needs to be able to terminate, and be re-initialized). + // If Init is called only once, e.g. in main(), calling Terminate() is not + // necessary. + static void Terminate(); + + // Get the singleton CommandLine representing the current process's + // command line. + static const CommandLine* ForCurrentProcess() { + DCHECK(current_process_commandline_); + return current_process_commandline_; + } + +#ifdef CHROMIUM_MOZILLA_BUILD + static bool IsInitialized() { + return !!current_process_commandline_; + } +#endif + + // Returns true if this command line contains the given switch. + // (Switch names are case-insensitive.) + bool HasSwitch(const std::wstring& switch_string) const; + + // Returns the value associated with the given switch. If the + // switch has no value or isn't present, this method returns + // the empty string. + std::wstring GetSwitchValue(const std::wstring& switch_string) const; + + // Get the remaining arguments to the command. + // WARNING: this is incorrect on POSIX; we must do string conversions. + std::vector GetLooseValues() const; + +#if defined(OS_WIN) + // Returns the original command line string. + const std::wstring& command_line_string() const { + return command_line_string_; + } +#elif defined(OS_POSIX) + // Returns the original command line string as a vector of strings. + const std::vector& argv() const { + return argv_; + } +#endif + + // Returns the program part of the command line string (the first item). + std::wstring program() const; + + // Return a copy of the string prefixed with a switch prefix. + // Used internally. + static std::wstring PrefixedSwitchString(const std::wstring& switch_string); + + // Return a copy of the string prefixed with a switch prefix, + // and appended with the given value. Used internally. + static std::wstring PrefixedSwitchStringWithValue( + const std::wstring& switch_string, + const std::wstring& value_string); + + // Appends the given switch string (preceded by a space and a switch + // prefix) to the given string. + void AppendSwitch(const std::wstring& switch_string); + + // Appends the given switch string (preceded by a space and a switch + // prefix) to the given string, with the given value attached. + void AppendSwitchWithValue(const std::wstring& switch_string, + const std::wstring& value_string); + + // Append a loose value to the command line. + void AppendLooseValue(const std::wstring& value); + + // Append the arguments from another command line to this one. + // If |include_program| is true, include |other|'s program as well. + void AppendArguments(const CommandLine& other, + bool include_program); + + // On POSIX systems it's common to run processes via a wrapper (like + // "valgrind" or "gdb --args"). + void PrependWrapper(const std::wstring& wrapper); + + private: + friend class InProcessBrowserTest; + + CommandLine() {} + + // Used by InProcessBrowserTest. + static CommandLine* ForCurrentProcessMutable() { + DCHECK(current_process_commandline_); + return current_process_commandline_; + } + + // The singleton CommandLine instance representing the current process's + // command line. + static CommandLine* current_process_commandline_; + + // We store a platform-native version of the command line, used when building + // up a new command line to be executed. This ifdef delimits that code. + +#if defined(OS_WIN) + // The quoted, space-separated command-line string. + std::wstring command_line_string_; + + // The name of the program. + std::wstring program_; + + // The type of native command line arguments. + typedef std::wstring StringType; + +#elif defined(OS_POSIX) + // The argv array, with the program name in argv_[0]. + std::vector argv_; + + // The type of native command line arguments. + typedef std::string StringType; + + // Shared by the two POSIX constructor forms. Initalize from argv_. + void InitFromArgv(); +#endif + + // Returns true and fills in |switch_string| and |switch_value| + // if |parameter_string| represents a switch. + static bool IsSwitch(const StringType& parameter_string, + std::string* switch_string, + StringType* switch_value); + + // Parsed-out values. + std::map switches_; + + // Non-switch command-line arguments. + std::vector loose_values_; + + // We allow copy constructors, because a common pattern is to grab a + // copy of the current process's command line and then add some + // flags to it. E.g.: + // CommandLine cl(*CommandLine::ForCurrentProcess()); + // cl.AppendSwitch(...); +}; + +#endif // BASE_COMMAND_LINE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/command_line_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/command_line_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/command_line_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/command_line_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,128 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "base/command_line.h" +#include "base/basictypes.h" +#include "base/string_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(CommandLineTest, CommandLineConstructor) { +#if defined(OS_WIN) + CommandLine cl(L""); + cl.ParseFromString(L"program --foo= -bAr /Spaetzel=pierogi /Baz flim " + L"--other-switches=\"--dog=canine --cat=feline\" " + L"-spaetzle=Crepe -=loosevalue flan " + L"--input-translation=\"45\"--output-rotation " + L"-- -- --not-a-switch " + L"\"in the time of submarines...\""); + EXPECT_FALSE(cl.command_line_string().empty()); +#elif defined(OS_POSIX) + const char* argv[] = {"program", "--foo=", "-bar", + "-spaetzel=pierogi", "-baz", "flim", + "--other-switches=--dog=canine --cat=feline", + "-spaetzle=Crepe", "-=loosevalue", "flan", + "--input-translation=45--output-rotation", + "--", "--", "--not-a-switch", + "in the time of submarines..."}; + CommandLine cl(arraysize(argv), argv); +#endif + EXPECT_FALSE(cl.HasSwitch(L"cruller")); + EXPECT_FALSE(cl.HasSwitch(L"flim")); + EXPECT_FALSE(cl.HasSwitch(L"program")); + EXPECT_FALSE(cl.HasSwitch(L"dog")); + EXPECT_FALSE(cl.HasSwitch(L"cat")); + EXPECT_FALSE(cl.HasSwitch(L"output-rotation")); + EXPECT_FALSE(cl.HasSwitch(L"not-a-switch")); + EXPECT_FALSE(cl.HasSwitch(L"--")); + + EXPECT_EQ(L"program", cl.program()); + + EXPECT_TRUE(cl.HasSwitch(L"foo")); + EXPECT_TRUE(cl.HasSwitch(L"bar")); + EXPECT_TRUE(cl.HasSwitch(L"baz")); + EXPECT_TRUE(cl.HasSwitch(L"spaetzle")); +#if defined(OS_WIN) + EXPECT_TRUE(cl.HasSwitch(L"SPAETZLE")); +#endif + EXPECT_TRUE(cl.HasSwitch(L"other-switches")); + EXPECT_TRUE(cl.HasSwitch(L"input-translation")); + + EXPECT_EQ(L"Crepe", cl.GetSwitchValue(L"spaetzle")); + EXPECT_EQ(L"", cl.GetSwitchValue(L"Foo")); + EXPECT_EQ(L"", cl.GetSwitchValue(L"bar")); + EXPECT_EQ(L"", cl.GetSwitchValue(L"cruller")); + EXPECT_EQ(L"--dog=canine --cat=feline", cl.GetSwitchValue(L"other-switches")); + EXPECT_EQ(L"45--output-rotation", cl.GetSwitchValue(L"input-translation")); + + std::vector loose_values = cl.GetLooseValues(); + ASSERT_EQ(5U, loose_values.size()); + + std::vector::const_iterator iter = loose_values.begin(); + EXPECT_EQ(L"flim", *iter); + ++iter; + EXPECT_EQ(L"flan", *iter); + ++iter; + EXPECT_EQ(L"--", *iter); + ++iter; + EXPECT_EQ(L"--not-a-switch", *iter); + ++iter; + EXPECT_EQ(L"in the time of submarines...", *iter); + ++iter; + EXPECT_TRUE(iter == loose_values.end()); +#if defined(OS_POSIX) + const std::vector& argvec = cl.argv(); + + for (size_t i = 0; i < argvec.size(); i++) { + EXPECT_EQ(0, argvec[i].compare(argv[i])); + } +#endif +} + +// Tests behavior with an empty input string. +TEST(CommandLineTest, EmptyString) { +#if defined(OS_WIN) + CommandLine cl(L""); + EXPECT_TRUE(cl.command_line_string().empty()); + EXPECT_TRUE(cl.program().empty()); +#elif defined(OS_POSIX) + CommandLine cl(0, NULL); + EXPECT_TRUE(cl.argv().size() == 0); +#endif + EXPECT_EQ(0U, cl.GetLooseValues().size()); +} + +// Test methods for appending switches to a command line. +TEST(CommandLineTest, AppendSwitches) { + std::wstring switch1 = L"switch1"; + std::wstring switch2 = L"switch2"; + std::wstring value = L"value"; + std::wstring switch3 = L"switch3"; + std::wstring value3 = L"a value with spaces"; + std::wstring switch4 = L"switch4"; + std::wstring value4 = L"\"a value with quotes\""; + +#if defined(OS_WIN) + CommandLine cl(L"Program"); +#elif defined(OS_POSIX) + std::vector argv; + argv.push_back(std::string("Program")); + CommandLine cl(argv); +#endif + + cl.AppendSwitch(switch1); + cl.AppendSwitchWithValue(switch2, value); + cl.AppendSwitchWithValue(switch3, value3); + cl.AppendSwitchWithValue(switch4, value4); + + EXPECT_TRUE(cl.HasSwitch(switch1)); + EXPECT_TRUE(cl.HasSwitch(switch2)); + EXPECT_EQ(value, cl.GetSwitchValue(switch2)); + EXPECT_TRUE(cl.HasSwitch(switch3)); + EXPECT_EQ(value3, cl.GetSwitchValue(switch3)); + EXPECT_TRUE(cl.HasSwitch(switch4)); + EXPECT_EQ(value4, cl.GetSwitchValue(switch4)); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/compiler_specific.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/compiler_specific.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/compiler_specific.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/compiler_specific.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,77 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_COMPILER_SPECIFIC_H_ +#define BASE_COMPILER_SPECIFIC_H_ + +#include "build/build_config.h" + +#if defined(COMPILER_MSVC) + +// Macros for suppressing and disabling warnings on MSVC. +// +// Warning numbers are enumerated at: +// http://msdn.microsoft.com/en-us/library/8x5x43k7(VS.80).aspx +// +// The warning pragma: +// http://msdn.microsoft.com/en-us/library/2c8f766e(VS.80).aspx +// +// Using __pragma instead of #pragma inside macros: +// http://msdn.microsoft.com/en-us/library/d9x1s805.aspx + +// MSVC_SUPPRESS_WARNING disables warning |n| for the remainder of the line and +// for the next line of the source file. +#define MSVC_SUPPRESS_WARNING(n) __pragma(warning(suppress:n)) + +// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled. +// The warning remains disabled until popped by MSVC_POP_WARNING. +#define MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \ + __pragma(warning(disable:n)) + +// MSVC_PUSH_WARNING_LEVEL pushes |n| as the global warning level. The level +// remains in effect until popped by MSVC_POP_WARNING(). Use 0 to disable all +// warnings. +#define MSVC_PUSH_WARNING_LEVEL(n) __pragma(warning(push, n)) + +// Pop effects of innermost MSVC_PUSH_* macro. +#define MSVC_POP_WARNING() __pragma(warning(pop)) + +#define MSVC_DISABLE_OPTIMIZE() __pragma(optimize("", off)) +#define MSVC_ENABLE_OPTIMIZE() __pragma(optimize("", on)) + +// Allows |this| to be passed as an argument in constructor initializer lists. +// This uses push/pop instead of the seemingly simpler suppress feature to avoid +// having the warning be disabled for more than just |code|. +// +// Example usage: +// Foo::Foo() : x(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(y(this)), z(3) {} +// +// Compiler warning C4355: 'this': used in base member initializer list: +// http://msdn.microsoft.com/en-us/library/3c594ae3(VS.80).aspx +#define ALLOW_THIS_IN_INITIALIZER_LIST(code) MSVC_PUSH_DISABLE_WARNING(4355) \ + code \ + MSVC_POP_WARNING() + +#else // Not MSVC + +#define MSVC_SUPPRESS_WARNING(n) +#define MSVC_PUSH_DISABLE_WARNING(n) +#define MSVC_PUSH_WARNING_LEVEL(n) +#define MSVC_POP_WARNING() +#define MSVC_DISABLE_OPTIMIZE() +#define MSVC_ENABLE_OPTIMIZE() +#define ALLOW_THIS_IN_INITIALIZER_LIST(code) code + +#endif // COMPILER_MSVC + + +#if defined(COMPILER_GCC) +#define ALLOW_UNUSED __attribute__((unused)) +#define WARN_UNUSED_RESULT __attribute__((warn_unused_result)) +#else // Not GCC +#define ALLOW_UNUSED +#define WARN_UNUSED_RESULT +#endif + +#endif // BASE_COMPILER_SPECIFIC_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/condition_variable.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/condition_variable.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/condition_variable.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/condition_variable.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,174 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ConditionVariable wraps pthreads condition variable synchronization or, on +// Windows, simulates it. This functionality is very helpful for having +// several threads wait for an event, as is common with a thread pool managed +// by a master. The meaning of such an event in the (worker) thread pool +// scenario is that additional tasks are now available for processing. It is +// used in Chrome in the DNS prefetching system to notify worker threads that +// a queue now has items (tasks) which need to be tended to. A related use +// would have a pool manager waiting on a ConditionVariable, waiting for a +// thread in the pool to announce (signal) that there is now more room in a +// (bounded size) communications queue for the manager to deposit tasks, or, +// as a second example, that the queue of tasks is completely empty and all +// workers are waiting. +// +// USAGE NOTE 1: spurious signal events are possible with this and +// most implementations of condition variables. As a result, be +// *sure* to retest your condition before proceeding. The following +// is a good example of doing this correctly: +// +// while (!work_to_be_done()) Wait(...); +// +// In contrast do NOT do the following: +// +// if (!work_to_be_done()) Wait(...); // Don't do this. +// +// Especially avoid the above if you are relying on some other thread only +// issuing a signal up *if* there is work-to-do. There can/will +// be spurious signals. Recheck state on waiting thread before +// assuming the signal was intentional. Caveat caller ;-). +// +// USAGE NOTE 2: Broadcast() frees up all waiting threads at once, +// which leads to contention for the locks they all held when they +// called Wait(). This results in POOR performance. A much better +// approach to getting a lot of threads out of Wait() is to have each +// thread (upon exiting Wait()) call Signal() to free up another +// Wait'ing thread. Look at condition_variable_unittest.cc for +// both examples. +// +// Broadcast() can be used nicely during teardown, as it gets the job +// done, and leaves no sleeping threads... and performance is less +// critical at that point. +// +// The semantics of Broadcast() are carefully crafted so that *all* +// threads that were waiting when the request was made will indeed +// get signaled. Some implementations mess up, and don't signal them +// all, while others allow the wait to be effectively turned off (for +// a while while waiting threads come around). This implementation +// appears correct, as it will not "lose" any signals, and will guarantee +// that all threads get signaled by Broadcast(). +// +// This implementation offers support for "performance" in its selection of +// which thread to revive. Performance, in direct contrast with "fairness," +// assures that the thread that most recently began to Wait() is selected by +// Signal to revive. Fairness would (if publicly supported) assure that the +// thread that has Wait()ed the longest is selected. The default policy +// may improve performance, as the selected thread may have a greater chance of +// having some of its stack data in various CPU caches. +// +// For a discussion of the many very subtle implementation details, see the FAQ +// at the end of condition_variable_win.cc. + +#ifndef BASE_CONDITION_VARIABLE_H_ +#define BASE_CONDITION_VARIABLE_H_ + +#include "base/lock.h" + +namespace base { + class TimeDelta; +} + +class ConditionVariable { + public: + // Construct a cv for use with ONLY one user lock. + explicit ConditionVariable(Lock* user_lock); + + ~ConditionVariable(); + + // Wait() releases the caller's critical section atomically as it starts to + // sleep, and the reacquires it when it is signaled. + void Wait(); + void TimedWait(const base::TimeDelta& max_time); + + // Broadcast() revives all waiting threads. + void Broadcast(); + // Signal() revives one waiting thread. + void Signal(); + + private: + +#if defined(OS_WIN) + + // Define Event class that is used to form circularly linked lists. + // The list container is an element with NULL as its handle_ value. + // The actual list elements have a non-zero handle_ value. + // All calls to methods MUST be done under protection of a lock so that links + // can be validated. Without the lock, some links might asynchronously + // change, and the assertions would fail (as would list change operations). + class Event { + public: + // Default constructor with no arguments creates a list container. + Event(); + ~Event(); + + // InitListElement transitions an instance from a container, to an element. + void InitListElement(); + + // Methods for use on lists. + bool IsEmpty() const; + void PushBack(Event* other); + Event* PopFront(); + Event* PopBack(); + + // Methods for use on list elements. + // Accessor method. + HANDLE handle() const; + // Pull an element from a list (if it's in one). + Event* Extract(); + + // Method for use on a list element or on a list. + bool IsSingleton() const; + + private: + // Provide pre/post conditions to validate correct manipulations. + bool ValidateAsDistinct(Event* other) const; + bool ValidateAsItem() const; + bool ValidateAsList() const; + bool ValidateLinks() const; + + HANDLE handle_; + Event* next_; + Event* prev_; + DISALLOW_COPY_AND_ASSIGN(Event); + }; + + // Note that RUNNING is an unlikely number to have in RAM by accident. + // This helps with defensive destructor coding in the face of user error. + enum RunState { SHUTDOWN = 0, RUNNING = 64213 }; + + // Internal implementation methods supporting Wait(). + Event* GetEventForWaiting(); + void RecycleEvent(Event* used_event); + + RunState run_state_; + + // Private critical section for access to member data. + Lock internal_lock_; + + // Lock that is acquired before calling Wait(). + Lock& user_lock_; + + // Events that threads are blocked on. + Event waiting_list_; + + // Free list for old events. + Event recycling_list_; + int recycling_list_size_; + + // The number of allocated, but not yet deleted events. + int allocation_counter_; + +#elif defined(OS_POSIX) + + pthread_cond_t condition_; + pthread_mutex_t* user_mutex_; + +#endif + + DISALLOW_COPY_AND_ASSIGN(ConditionVariable); +}; + +#endif // BASE_CONDITION_VARIABLE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/condition_variable_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/condition_variable_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/condition_variable_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/condition_variable_posix.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,61 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/condition_variable.h" + +#include +#include + +#include "base/lock.h" +#include "base/lock_impl.h" +#include "base/logging.h" +#include "base/time.h" + +using base::Time; +using base::TimeDelta; + +ConditionVariable::ConditionVariable(Lock* user_lock) + : user_mutex_(user_lock->lock_impl()->os_lock()) { + int rv = pthread_cond_init(&condition_, NULL); + DCHECK(rv == 0); +} + +ConditionVariable::~ConditionVariable() { + int rv = pthread_cond_destroy(&condition_); + DCHECK(rv == 0); +} + +void ConditionVariable::Wait() { + int rv = pthread_cond_wait(&condition_, user_mutex_); + DCHECK(rv == 0); +} + +void ConditionVariable::TimedWait(const TimeDelta& max_time) { + int64 usecs = max_time.InMicroseconds(); + + // The timeout argument to pthread_cond_timedwait is in absolute time. + struct timeval now; + gettimeofday(&now, NULL); + + struct timespec abstime; + abstime.tv_sec = now.tv_sec + (usecs / Time::kMicrosecondsPerSecond); + abstime.tv_nsec = (now.tv_usec + (usecs % Time::kMicrosecondsPerSecond)) * + Time::kNanosecondsPerMicrosecond; + abstime.tv_sec += abstime.tv_nsec / Time::kNanosecondsPerSecond; + abstime.tv_nsec %= Time::kNanosecondsPerSecond; + DCHECK(abstime.tv_sec >= now.tv_sec); // Overflow paranoia + + int rv = pthread_cond_timedwait(&condition_, user_mutex_, &abstime); + DCHECK(rv == 0 || rv == ETIMEDOUT); +} + +void ConditionVariable::Broadcast() { + int rv = pthread_cond_broadcast(&condition_); + DCHECK(rv == 0); +} + +void ConditionVariable::Signal() { + int rv = pthread_cond_signal(&condition_); + DCHECK(rv == 0); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/condition_variable_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/condition_variable_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/condition_variable_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/condition_variable_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,738 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Multi-threaded tests of ConditionVariable class. + +#include +#include +#include + +#include "base/condition_variable.h" +#include "base/logging.h" +#include "base/platform_thread.h" +#include "base/scoped_ptr.h" +#include "base/spin_wait.h" +#include "base/thread_collision_warner.h" +#include "base/time.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +using base::TimeDelta; +using base::TimeTicks; + +namespace { +//------------------------------------------------------------------------------ +// Define our test class, with several common variables. +//------------------------------------------------------------------------------ + +class ConditionVariableTest : public PlatformTest { + public: + const TimeDelta kZeroMs; + const TimeDelta kTenMs; + const TimeDelta kThirtyMs; + const TimeDelta kFortyFiveMs; + const TimeDelta kSixtyMs; + const TimeDelta kOneHundredMs; + + explicit ConditionVariableTest() + : kZeroMs(TimeDelta::FromMilliseconds(0)), + kTenMs(TimeDelta::FromMilliseconds(10)), + kThirtyMs(TimeDelta::FromMilliseconds(30)), + kFortyFiveMs(TimeDelta::FromMilliseconds(45)), + kSixtyMs(TimeDelta::FromMilliseconds(60)), + kOneHundredMs(TimeDelta::FromMilliseconds(100)) { + } +}; + +//------------------------------------------------------------------------------ +// Define a class that will control activities an several multi-threaded tests. +// The general structure of multi-threaded tests is that a test case will +// construct an instance of a WorkQueue. The WorkQueue will spin up some +// threads and control them throughout their lifetime, as well as maintaining +// a central repository of the work thread's activity. Finally, the WorkQueue +// will command the the worker threads to terminate. At that point, the test +// cases will validate that the WorkQueue has records showing that the desired +// activities were performed. +//------------------------------------------------------------------------------ + +// Callers are responsible for synchronizing access to the following class. +// The WorkQueue::lock_, as accessed via WorkQueue::lock(), should be used for +// all synchronized access. +class WorkQueue : public PlatformThread::Delegate { + public: + explicit WorkQueue(int thread_count); + ~WorkQueue(); + + // PlatformThread::Delegate interface. + void ThreadMain(); + + //---------------------------------------------------------------------------- + // Worker threads only call the following methods. + // They should use the lock to get exclusive access. + int GetThreadId(); // Get an ID assigned to a thread.. + bool EveryIdWasAllocated() const; // Indicates that all IDs were handed out. + TimeDelta GetAnAssignment(int thread_id); // Get a work task duration. + void WorkIsCompleted(int thread_id); + + int task_count() const; + bool allow_help_requests() const; // Workers can signal more workers. + bool shutdown() const; // Check if shutdown has been requested. + + void thread_shutting_down(); + + + //---------------------------------------------------------------------------- + // Worker threads can call them but not needed to acquire a lock. + Lock* lock(); + + ConditionVariable* work_is_available(); + ConditionVariable* all_threads_have_ids(); + ConditionVariable* no_more_tasks(); + + //---------------------------------------------------------------------------- + // The rest of the methods are for use by the controlling master thread (the + // test case code). + void ResetHistory(); + int GetMinCompletionsByWorkerThread() const; + int GetMaxCompletionsByWorkerThread() const; + int GetNumThreadsTakingAssignments() const; + int GetNumThreadsCompletingTasks() const; + int GetNumberOfCompletedTasks() const; + TimeDelta GetWorkTime() const; + + void SetWorkTime(TimeDelta delay); + void SetTaskCount(int count); + void SetAllowHelp(bool allow); + + // Caller must acquire lock before calling. + void SetShutdown(); + + // Compares the |shutdown_task_count_| to the |thread_count| and returns true + // if they are equal. This check will acquire the |lock_| so the caller + // should not hold the lock when calling this method. + bool ThreadSafeCheckShutdown(int thread_count); + + private: + // Both worker threads and controller use the following to synchronize. + Lock lock_; + ConditionVariable work_is_available_; // To tell threads there is work. + + // Conditions to notify the controlling process (if it is interested). + ConditionVariable all_threads_have_ids_; // All threads are running. + ConditionVariable no_more_tasks_; // Task count is zero. + + const int thread_count_; + scoped_array thread_handles_; + std::vector assignment_history_; // Number of assignment per worker. + std::vector completion_history_; // Number of completions per worker. + int thread_started_counter_; // Used to issue unique id to workers. + int shutdown_task_count_; // Number of tasks told to shutdown + int task_count_; // Number of assignment tasks waiting to be processed. + TimeDelta worker_delay_; // Time each task takes to complete. + bool allow_help_requests_; // Workers can signal more workers. + bool shutdown_; // Set when threads need to terminate. + + DFAKE_MUTEX(locked_methods_); +}; + +//------------------------------------------------------------------------------ +// The next section contains the actual tests. +//------------------------------------------------------------------------------ + +TEST_F(ConditionVariableTest, StartupShutdownTest) { + Lock lock; + + // First try trivial startup/shutdown. + { + ConditionVariable cv1(&lock); + } // Call for cv1 destruction. + + // Exercise with at least a few waits. + ConditionVariable cv(&lock); + + lock.Acquire(); + cv.TimedWait(kTenMs); // Wait for 10 ms. + cv.TimedWait(kTenMs); // Wait for 10 ms. + lock.Release(); + + lock.Acquire(); + cv.TimedWait(kTenMs); // Wait for 10 ms. + cv.TimedWait(kTenMs); // Wait for 10 ms. + cv.TimedWait(kTenMs); // Wait for 10 ms. + lock.Release(); +} // Call for cv destruction. + +TEST_F(ConditionVariableTest, TimeoutTest) { + Lock lock; + ConditionVariable cv(&lock); + lock.Acquire(); + + TimeTicks start = TimeTicks::Now(); + const TimeDelta WAIT_TIME = TimeDelta::FromMilliseconds(300); + // Allow for clocking rate granularity. + const TimeDelta FUDGE_TIME = TimeDelta::FromMilliseconds(50); + + cv.TimedWait(WAIT_TIME + FUDGE_TIME); + TimeDelta duration = TimeTicks::Now() - start; + // We can't use EXPECT_GE here as the TimeDelta class does not support the + // required stream conversion. + EXPECT_TRUE(duration >= WAIT_TIME); + + lock.Release(); +} + +// Test serial task servicing, as well as two parallel task servicing methods. +// TODO(maruel): http://crbug.com/10607 +TEST_F(ConditionVariableTest, DISABLED_MultiThreadConsumerTest) { + const int kThreadCount = 10; + WorkQueue queue(kThreadCount); // Start the threads. + + const int kTaskCount = 10; // Number of tasks in each mini-test here. + + base::Time start_time; // Used to time task processing. + + { + AutoLock auto_lock(*queue.lock()); + while (!queue.EveryIdWasAllocated()) + queue.all_threads_have_ids()->Wait(); + } + + // Wait a bit more to allow threads to reach their wait state. + // If threads aren't in a wait state, they may start to gobble up tasks in + // parallel, short-circuiting (breaking) this test. + PlatformThread::Sleep(100); + + { + // Since we have no tasks yet, all threads should be waiting by now. + AutoLock auto_lock(*queue.lock()); + EXPECT_EQ(0, queue.GetNumThreadsTakingAssignments()); + EXPECT_EQ(0, queue.GetNumThreadsCompletingTasks()); + EXPECT_EQ(0, queue.task_count()); + EXPECT_EQ(0, queue.GetMaxCompletionsByWorkerThread()); + EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread()); + EXPECT_EQ(0, queue.GetNumberOfCompletedTasks()); + + // Set up to make one worker do 30ms tasks sequentially. + queue.ResetHistory(); + queue.SetTaskCount(kTaskCount); + queue.SetWorkTime(kThirtyMs); + queue.SetAllowHelp(false); + + start_time = base::Time::Now(); + } + + queue.work_is_available()->Signal(); // Start up one thread. + + + { + // Wait until all 10 work tasks have at least been assigned. + AutoLock auto_lock(*queue.lock()); + while(queue.task_count()) + queue.no_more_tasks()->Wait(); + // The last of the tasks *might* still be running, but... all but one should + // be done by now, since tasks are being done serially. + EXPECT_LE(queue.GetWorkTime().InMilliseconds() * (kTaskCount - 1), + (base::Time::Now() - start_time).InMilliseconds()); + + EXPECT_EQ(1, queue.GetNumThreadsTakingAssignments()); + EXPECT_EQ(1, queue.GetNumThreadsCompletingTasks()); + EXPECT_LE(kTaskCount - 1, queue.GetMaxCompletionsByWorkerThread()); + EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread()); + EXPECT_LE(kTaskCount - 1, queue.GetNumberOfCompletedTasks()); + } + + // Wait to be sure all tasks are done. + while (1) { + { + AutoLock auto_lock(*queue.lock()); + if (kTaskCount == queue.GetNumberOfCompletedTasks()) + break; + } + PlatformThread::Sleep(30); // Wait a little. + } + + { + // Check that all work was done by one thread id. + AutoLock auto_lock(*queue.lock()); + EXPECT_EQ(1, queue.GetNumThreadsTakingAssignments()); + EXPECT_EQ(1, queue.GetNumThreadsCompletingTasks()); + EXPECT_EQ(0, queue.task_count()); + EXPECT_EQ(kTaskCount, queue.GetMaxCompletionsByWorkerThread()); + EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread()); + EXPECT_EQ(kTaskCount, queue.GetNumberOfCompletedTasks()); + + // Set up to make each task include getting help from another worker, so + // so that the work gets done in paralell. + queue.ResetHistory(); + queue.SetTaskCount(kTaskCount); + queue.SetWorkTime(kThirtyMs); + queue.SetAllowHelp(true); + + start_time = base::Time::Now(); + } + + queue.work_is_available()->Signal(); // But each worker can signal another. + // Wait to allow the all workers to get done. + while (1) { + { + AutoLock auto_lock(*queue.lock()); + if (kTaskCount == queue.GetNumberOfCompletedTasks()) + break; + } + PlatformThread::Sleep(30); // Wait a little. + } + + { + // Wait until all work tasks have at least been assigned. + AutoLock auto_lock(*queue.lock()); + while(queue.task_count()) + queue.no_more_tasks()->Wait(); + // Since they can all run almost in parallel, there is no guarantee that all + // tasks are finished, but we should have gotten here faster than it would + // take to run all tasks serially. + EXPECT_GT(queue.GetWorkTime().InMilliseconds() * (kTaskCount - 1), + (base::Time::Now() - start_time).InMilliseconds()); + + // To avoid racy assumptions, we'll just assert that at least 2 threads + // did work. + EXPECT_LE(2, queue.GetNumThreadsTakingAssignments()); + EXPECT_EQ(kTaskCount, queue.GetNumberOfCompletedTasks()); + + // Try to ask all workers to help, and only a few will do the work. + queue.ResetHistory(); + queue.SetTaskCount(3); + queue.SetWorkTime(kThirtyMs); + queue.SetAllowHelp(false); + } + queue.work_is_available()->Broadcast(); // Make them all try. + // Wait to allow the 3 workers to get done. + PlatformThread::Sleep(45); + + { + AutoLock auto_lock(*queue.lock()); + EXPECT_EQ(3, queue.GetNumThreadsTakingAssignments()); + EXPECT_EQ(3, queue.GetNumThreadsCompletingTasks()); + EXPECT_EQ(0, queue.task_count()); + EXPECT_EQ(1, queue.GetMaxCompletionsByWorkerThread()); + EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread()); + EXPECT_EQ(3, queue.GetNumberOfCompletedTasks()); + + // Set up to make each task get help from another worker. + queue.ResetHistory(); + queue.SetTaskCount(3); + queue.SetWorkTime(kThirtyMs); + queue.SetAllowHelp(true); // Allow (unnecessary) help requests. + } + queue.work_is_available()->Broadcast(); // We already signal all threads. + // Wait to allow the 3 workers to get done. + PlatformThread::Sleep(100); + + { + AutoLock auto_lock(*queue.lock()); + EXPECT_EQ(3, queue.GetNumThreadsTakingAssignments()); + EXPECT_EQ(3, queue.GetNumThreadsCompletingTasks()); + EXPECT_EQ(0, queue.task_count()); + EXPECT_EQ(1, queue.GetMaxCompletionsByWorkerThread()); + EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread()); + EXPECT_EQ(3, queue.GetNumberOfCompletedTasks()); + + // Set up to make each task get help from another worker. + queue.ResetHistory(); + queue.SetTaskCount(20); + queue.SetWorkTime(kThirtyMs); + queue.SetAllowHelp(true); + } + queue.work_is_available()->Signal(); // But each worker can signal another. + // Wait to allow the 10 workers to get done. + PlatformThread::Sleep(100); // Should take about 60 ms. + + { + AutoLock auto_lock(*queue.lock()); + EXPECT_EQ(10, queue.GetNumThreadsTakingAssignments()); + EXPECT_EQ(10, queue.GetNumThreadsCompletingTasks()); + EXPECT_EQ(0, queue.task_count()); + EXPECT_EQ(2, queue.GetMaxCompletionsByWorkerThread()); + EXPECT_EQ(2, queue.GetMinCompletionsByWorkerThread()); + EXPECT_EQ(20, queue.GetNumberOfCompletedTasks()); + + // Same as last test, but with Broadcast(). + queue.ResetHistory(); + queue.SetTaskCount(20); // 2 tasks per process. + queue.SetWorkTime(kThirtyMs); + queue.SetAllowHelp(true); + } + queue.work_is_available()->Broadcast(); + // Wait to allow the 10 workers to get done. + PlatformThread::Sleep(100); // Should take about 60 ms. + + { + AutoLock auto_lock(*queue.lock()); + EXPECT_EQ(10, queue.GetNumThreadsTakingAssignments()); + EXPECT_EQ(10, queue.GetNumThreadsCompletingTasks()); + EXPECT_EQ(0, queue.task_count()); + EXPECT_EQ(2, queue.GetMaxCompletionsByWorkerThread()); + EXPECT_EQ(2, queue.GetMinCompletionsByWorkerThread()); + EXPECT_EQ(20, queue.GetNumberOfCompletedTasks()); + + queue.SetShutdown(); + } + queue.work_is_available()->Broadcast(); // Force check for shutdown. + + SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1), + queue.ThreadSafeCheckShutdown(kThreadCount)); + PlatformThread::Sleep(10); // Be sure they're all shutdown. +} + +TEST_F(ConditionVariableTest, LargeFastTaskTest) { + const int kThreadCount = 200; + WorkQueue queue(kThreadCount); // Start the threads. + + Lock private_lock; // Used locally for master to wait. + AutoLock private_held_lock(private_lock); + ConditionVariable private_cv(&private_lock); + + { + AutoLock auto_lock(*queue.lock()); + while (!queue.EveryIdWasAllocated()) + queue.all_threads_have_ids()->Wait(); + } + + // Wait a bit more to allow threads to reach their wait state. + private_cv.TimedWait(kThirtyMs); + + { + // Since we have no tasks, all threads should be waiting by now. + AutoLock auto_lock(*queue.lock()); + EXPECT_EQ(0, queue.GetNumThreadsTakingAssignments()); + EXPECT_EQ(0, queue.GetNumThreadsCompletingTasks()); + EXPECT_EQ(0, queue.task_count()); + EXPECT_EQ(0, queue.GetMaxCompletionsByWorkerThread()); + EXPECT_EQ(0, queue.GetMinCompletionsByWorkerThread()); + EXPECT_EQ(0, queue.GetNumberOfCompletedTasks()); + + // Set up to make all workers do (an average of) 20 tasks. + queue.ResetHistory(); + queue.SetTaskCount(20 * kThreadCount); + queue.SetWorkTime(kFortyFiveMs); + queue.SetAllowHelp(false); + } + queue.work_is_available()->Broadcast(); // Start up all threads. + // Wait until we've handed out all tasks. + { + AutoLock auto_lock(*queue.lock()); + while (queue.task_count() != 0) + queue.no_more_tasks()->Wait(); + } + + // Wait till the last of the tasks complete. + // Don't bother to use locks: We may not get info in time... but we'll see it + // eventually. + SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1), + 20 * kThreadCount == + queue.GetNumberOfCompletedTasks()); + + { + // With Broadcast(), every thread should have participated. + // but with racing.. they may not all have done equal numbers of tasks. + AutoLock auto_lock(*queue.lock()); + EXPECT_EQ(kThreadCount, queue.GetNumThreadsTakingAssignments()); + EXPECT_EQ(kThreadCount, queue.GetNumThreadsCompletingTasks()); + EXPECT_EQ(0, queue.task_count()); + EXPECT_LE(20, queue.GetMaxCompletionsByWorkerThread()); + EXPECT_EQ(20 * kThreadCount, queue.GetNumberOfCompletedTasks()); + + // Set up to make all workers do (an average of) 4 tasks. + queue.ResetHistory(); + queue.SetTaskCount(kThreadCount * 4); + queue.SetWorkTime(kFortyFiveMs); + queue.SetAllowHelp(true); // Might outperform Broadcast(). + } + queue.work_is_available()->Signal(); // Start up one thread. + + // Wait until we've handed out all tasks + { + AutoLock auto_lock(*queue.lock()); + while (queue.task_count() != 0) + queue.no_more_tasks()->Wait(); + } + + // Wait till the last of the tasks complete. + // Don't bother to use locks: We may not get info in time... but we'll see it + // eventually. + SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1), + 4 * kThreadCount == + queue.GetNumberOfCompletedTasks()); + + { + // With Signal(), every thread should have participated. + // but with racing.. they may not all have done four tasks. + AutoLock auto_lock(*queue.lock()); + EXPECT_EQ(kThreadCount, queue.GetNumThreadsTakingAssignments()); + EXPECT_EQ(kThreadCount, queue.GetNumThreadsCompletingTasks()); + EXPECT_EQ(0, queue.task_count()); + EXPECT_LE(4, queue.GetMaxCompletionsByWorkerThread()); + EXPECT_EQ(4 * kThreadCount, queue.GetNumberOfCompletedTasks()); + + queue.SetShutdown(); + } + queue.work_is_available()->Broadcast(); // Force check for shutdown. + + // Wait for shutdowns to complete. + SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(1), + queue.ThreadSafeCheckShutdown(kThreadCount)); + PlatformThread::Sleep(10); // Be sure they're all shutdown. +} + +//------------------------------------------------------------------------------ +// Finally we provide the implementation for the methods in the WorkQueue class. +//------------------------------------------------------------------------------ + +WorkQueue::WorkQueue(int thread_count) + : lock_(), + work_is_available_(&lock_), + all_threads_have_ids_(&lock_), + no_more_tasks_(&lock_), + thread_count_(thread_count), + thread_handles_(new PlatformThreadHandle[thread_count]), + assignment_history_(thread_count), + completion_history_(thread_count), + thread_started_counter_(0), + shutdown_task_count_(0), + task_count_(0), + allow_help_requests_(false), + shutdown_(false) { + EXPECT_GE(thread_count_, 1); + ResetHistory(); + SetTaskCount(0); + SetWorkTime(TimeDelta::FromMilliseconds(30)); + + for (int i = 0; i < thread_count_; ++i) { + PlatformThreadHandle pth; + EXPECT_TRUE(PlatformThread::Create(0, this, &pth)); + thread_handles_[i] = pth; + } +} + +WorkQueue::~WorkQueue() { + { + AutoLock auto_lock(lock_); + SetShutdown(); + } + work_is_available_.Broadcast(); // Tell them all to terminate. + + for (int i = 0; i < thread_count_; ++i) { + PlatformThread::Join(thread_handles_[i]); + } +} + +int WorkQueue::GetThreadId() { + DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_); + DCHECK(!EveryIdWasAllocated()); + return thread_started_counter_++; // Give out Unique IDs. +} + +bool WorkQueue::EveryIdWasAllocated() const { + DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_); + return thread_count_ == thread_started_counter_; +} + +TimeDelta WorkQueue::GetAnAssignment(int thread_id) { + DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_); + DCHECK_LT(0, task_count_); + assignment_history_[thread_id]++; + if (0 == --task_count_) { + no_more_tasks_.Signal(); + } + return worker_delay_; +} + +void WorkQueue::WorkIsCompleted(int thread_id) { + DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_); + completion_history_[thread_id]++; +} + +int WorkQueue::task_count() const { + DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_); + return task_count_; +} + +bool WorkQueue::allow_help_requests() const { + DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_); + return allow_help_requests_; +} + +bool WorkQueue::shutdown() const { + lock_.AssertAcquired(); + DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_); + return shutdown_; +} + +// Because this method is called from the test's main thread we need to actually +// take the lock. Threads will call the thread_shutting_down() method with the +// lock already acquired. +bool WorkQueue::ThreadSafeCheckShutdown(int thread_count) { + bool all_shutdown; + AutoLock auto_lock(lock_); + { + // Declare in scope so DFAKE is guranteed to be destroyed before AutoLock. + DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_); + all_shutdown = (shutdown_task_count_ == thread_count); + } + return all_shutdown; +} + +void WorkQueue::thread_shutting_down() { + lock_.AssertAcquired(); + DFAKE_SCOPED_RECURSIVE_LOCK(locked_methods_); + shutdown_task_count_++; +} + +Lock* WorkQueue::lock() { + return &lock_; +} + +ConditionVariable* WorkQueue::work_is_available() { + return &work_is_available_; +} + +ConditionVariable* WorkQueue::all_threads_have_ids() { + return &all_threads_have_ids_; +} + +ConditionVariable* WorkQueue::no_more_tasks() { + return &no_more_tasks_; +} + +void WorkQueue::ResetHistory() { + for (int i = 0; i < thread_count_; ++i) { + assignment_history_[i] = 0; + completion_history_[i] = 0; + } +} + +int WorkQueue::GetMinCompletionsByWorkerThread() const { + int minumum = completion_history_[0]; + for (int i = 0; i < thread_count_; ++i) + minumum = std::min(minumum, completion_history_[i]); + return minumum; +} + +int WorkQueue::GetMaxCompletionsByWorkerThread() const { + int maximum = completion_history_[0]; + for (int i = 0; i < thread_count_; ++i) + maximum = std::max(maximum, completion_history_[i]); + return maximum; +} + +int WorkQueue::GetNumThreadsTakingAssignments() const { + int count = 0; + for (int i = 0; i < thread_count_; ++i) + if (assignment_history_[i]) + count++; + return count; +} + +int WorkQueue::GetNumThreadsCompletingTasks() const { + int count = 0; + for (int i = 0; i < thread_count_; ++i) + if (completion_history_[i]) + count++; + return count; +} + +int WorkQueue::GetNumberOfCompletedTasks() const { + int total = 0; + for (int i = 0; i < thread_count_; ++i) + total += completion_history_[i]; + return total; +} + +TimeDelta WorkQueue::GetWorkTime() const { + return worker_delay_; +} + +void WorkQueue::SetWorkTime(TimeDelta delay) { + worker_delay_ = delay; +} + +void WorkQueue::SetTaskCount(int count) { + task_count_ = count; +} + +void WorkQueue::SetAllowHelp(bool allow) { + allow_help_requests_ = allow; +} + +void WorkQueue::SetShutdown() { + lock_.AssertAcquired(); + shutdown_ = true; +} + +//------------------------------------------------------------------------------ +// Define the standard worker task. Several tests will spin out many of these +// threads. +//------------------------------------------------------------------------------ + +// The multithread tests involve several threads with a task to perform as +// directed by an instance of the class WorkQueue. +// The task is to: +// a) Check to see if there are more tasks (there is a task counter). +// a1) Wait on condition variable if there are no tasks currently. +// b) Call a function to see what should be done. +// c) Do some computation based on the number of milliseconds returned in (b). +// d) go back to (a). + +// WorkQueue::ThreadMain() implements the above task for all threads. +// It calls the controlling object to tell the creator about progress, and to +// ask about tasks. + +void WorkQueue::ThreadMain() { + int thread_id; + { + AutoLock auto_lock(lock_); + thread_id = GetThreadId(); + if (EveryIdWasAllocated()) + all_threads_have_ids()->Signal(); // Tell creator we're ready. + } + + Lock private_lock; // Used to waste time on "our work". + while (1) { // This is the main consumer loop. + TimeDelta work_time; + bool could_use_help; + { + AutoLock auto_lock(lock_); + while (0 == task_count() && !shutdown()) { + work_is_available()->Wait(); + } + if (shutdown()) { + // Ack the notification of a shutdown message back to the controller. + thread_shutting_down(); + return; // Terminate. + } + // Get our task duration from the queue. + work_time = GetAnAssignment(thread_id); + could_use_help = (task_count() > 0) && allow_help_requests(); + } // Release lock + + // Do work (outside of locked region. + if (could_use_help) + work_is_available()->Signal(); // Get help from other threads. + + if (work_time > TimeDelta::FromMilliseconds(0)) { + // We could just sleep(), but we'll instead further exercise the + // condition variable class, and do a timed wait. + AutoLock auto_lock(private_lock); + ConditionVariable private_cv(&private_lock); + private_cv.TimedWait(work_time); // Unsynchronized waiting. + } + + { + AutoLock auto_lock(lock_); + // Send notification that we completed our "work." + WorkIsCompleted(thread_id); + } + } +} + +} // namespace diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/condition_variable_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/condition_variable_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/condition_variable_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/condition_variable_win.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,446 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/condition_variable.h" + +#include + +#include "base/lock.h" +#include "base/logging.h" +#include "base/time.h" + +using base::TimeDelta; + +ConditionVariable::ConditionVariable(Lock* user_lock) + : user_lock_(*user_lock), + run_state_(RUNNING), + allocation_counter_(0), + recycling_list_size_(0) { + DCHECK(user_lock); +} + +ConditionVariable::~ConditionVariable() { + AutoLock auto_lock(internal_lock_); + run_state_ = SHUTDOWN; // Prevent any more waiting. + + DCHECK_EQ(recycling_list_size_, allocation_counter_); + if (recycling_list_size_ != allocation_counter_) { // Rare shutdown problem. + // There are threads of execution still in this->TimedWait() and yet the + // caller has instigated the destruction of this instance :-/. + // A common reason for such "overly hasty" destruction is that the caller + // was not willing to wait for all the threads to terminate. Such hasty + // actions are a violation of our usage contract, but we'll give the + // waiting thread(s) one last chance to exit gracefully (prior to our + // destruction). + // Note: waiting_list_ *might* be empty, but recycling is still pending. + AutoUnlock auto_unlock(internal_lock_); + Broadcast(); // Make sure all waiting threads have been signaled. + Sleep(10); // Give threads a chance to grab internal_lock_. + // All contained threads should be blocked on user_lock_ by now :-). + } // Reacquire internal_lock_. + + DCHECK_EQ(recycling_list_size_, allocation_counter_); +} + +void ConditionVariable::Wait() { + // Default to "wait forever" timing, which means have to get a Signal() + // or Broadcast() to come out of this wait state. + TimedWait(TimeDelta::FromMilliseconds(INFINITE)); +} + +void ConditionVariable::TimedWait(const TimeDelta& max_time) { + Event* waiting_event; + HANDLE handle; + { + AutoLock auto_lock(internal_lock_); + if (RUNNING != run_state_) return; // Destruction in progress. + waiting_event = GetEventForWaiting(); + handle = waiting_event->handle(); + DCHECK(handle); + } // Release internal_lock. + + { + AutoUnlock unlock(user_lock_); // Release caller's lock + WaitForSingleObject(handle, static_cast(max_time.InMilliseconds())); + // Minimize spurious signal creation window by recycling asap. + AutoLock auto_lock(internal_lock_); + RecycleEvent(waiting_event); + // Release internal_lock_ + } // Reacquire callers lock to depth at entry. +} + +// Broadcast() is guaranteed to signal all threads that were waiting (i.e., had +// a cv_event internally allocated for them) before Broadcast() was called. +void ConditionVariable::Broadcast() { + std::stack handles; // See FAQ-question-10. + { + AutoLock auto_lock(internal_lock_); + if (waiting_list_.IsEmpty()) + return; + while (!waiting_list_.IsEmpty()) + // This is not a leak from waiting_list_. See FAQ-question 12. + handles.push(waiting_list_.PopBack()->handle()); + } // Release internal_lock_. + while (!handles.empty()) { + SetEvent(handles.top()); + handles.pop(); + } +} + +// Signal() will select one of the waiting threads, and signal it (signal its +// cv_event). For better performance we signal the thread that went to sleep +// most recently (LIFO). If we want fairness, then we wake the thread that has +// been sleeping the longest (FIFO). +void ConditionVariable::Signal() { + HANDLE handle; + { + AutoLock auto_lock(internal_lock_); + if (waiting_list_.IsEmpty()) + return; // No one to signal. + // Only performance option should be used. + // This is not a leak from waiting_list. See FAQ-question 12. + handle = waiting_list_.PopBack()->handle(); // LIFO. + } // Release internal_lock_. + SetEvent(handle); +} + +// GetEventForWaiting() provides a unique cv_event for any caller that needs to +// wait. This means that (worst case) we may over time create as many cv_event +// objects as there are threads simultaneously using this instance's Wait() +// functionality. +ConditionVariable::Event* ConditionVariable::GetEventForWaiting() { + // We hold internal_lock, courtesy of Wait(). + Event* cv_event; + if (0 == recycling_list_size_) { + DCHECK(recycling_list_.IsEmpty()); + cv_event = new Event(); + cv_event->InitListElement(); + allocation_counter_++; + // CHECK_NE is not defined in our codebase, so we have to use CHECK + CHECK(cv_event->handle()); + } else { + cv_event = recycling_list_.PopFront(); + recycling_list_size_--; + } + waiting_list_.PushBack(cv_event); + return cv_event; +} + +// RecycleEvent() takes a cv_event that was previously used for Wait()ing, and +// recycles it for use in future Wait() calls for this or other threads. +// Note that there is a tiny chance that the cv_event is still signaled when we +// obtain it, and that can cause spurious signals (if/when we re-use the +// cv_event), but such is quite rare (see FAQ-question-5). +void ConditionVariable::RecycleEvent(Event* used_event) { + // We hold internal_lock, courtesy of Wait(). + // If the cv_event timed out, then it is necessary to remove it from + // waiting_list_. If it was selected by Broadcast() or Signal(), then it is + // already gone. + used_event->Extract(); // Possibly redundant + recycling_list_.PushBack(used_event); + recycling_list_size_++; +} +//------------------------------------------------------------------------------ +// The next section provides the implementation for the private Event class. +//------------------------------------------------------------------------------ + +// Event provides a doubly-linked-list of events for use exclusively by the +// ConditionVariable class. + +// This custom container was crafted because no simple combination of STL +// classes appeared to support the functionality required. The specific +// unusual requirement for a linked-list-class is support for the Extract() +// method, which can remove an element from a list, potentially for insertion +// into a second list. Most critically, the Extract() method is idempotent, +// turning the indicated element into an extracted singleton whether it was +// contained in a list or not. This functionality allows one (or more) of +// threads to do the extraction. The iterator that identifies this extractable +// element (in this case, a pointer to the list element) can be used after +// arbitrary manipulation of the (possibly) enclosing list container. In +// general, STL containers do not provide iterators that can be used across +// modifications (insertions/extractions) of the enclosing containers, and +// certainly don't provide iterators that can be used if the identified +// element is *deleted* (removed) from the container. + +// It is possible to use multiple redundant containers, such as an STL list, +// and an STL map, to achieve similar container semantics. This container has +// only O(1) methods, while the corresponding (multiple) STL container approach +// would have more complex O(log(N)) methods (yeah... N isn't that large). +// Multiple containers also makes correctness more difficult to assert, as +// data is redundantly stored and maintained, which is generally evil. + +ConditionVariable::Event::Event() : handle_(0) { + next_ = prev_ = this; // Self referencing circular. +} + +ConditionVariable::Event::~Event() { + if (0 == handle_) { + // This is the list holder + while (!IsEmpty()) { + Event* cv_event = PopFront(); + DCHECK(cv_event->ValidateAsItem()); + delete cv_event; + } + } + DCHECK(IsSingleton()); + if (0 != handle_) { + int ret_val = CloseHandle(handle_); + DCHECK(ret_val); + } +} + +// Change a container instance permanently into an element of a list. +void ConditionVariable::Event::InitListElement() { + DCHECK(!handle_); + handle_ = CreateEvent(NULL, false, false, NULL); + CHECK(handle_); +} + +// Methods for use on lists. +bool ConditionVariable::Event::IsEmpty() const { + DCHECK(ValidateAsList()); + return IsSingleton(); +} + +void ConditionVariable::Event::PushBack(Event* other) { + DCHECK(ValidateAsList()); + DCHECK(other->ValidateAsItem()); + DCHECK(other->IsSingleton()); + // Prepare other for insertion. + other->prev_ = prev_; + other->next_ = this; + // Cut into list. + prev_->next_ = other; + prev_ = other; + DCHECK(ValidateAsDistinct(other)); +} + +ConditionVariable::Event* ConditionVariable::Event::PopFront() { + DCHECK(ValidateAsList()); + DCHECK(!IsSingleton()); + return next_->Extract(); +} + +ConditionVariable::Event* ConditionVariable::Event::PopBack() { + DCHECK(ValidateAsList()); + DCHECK(!IsSingleton()); + return prev_->Extract(); +} + +// Methods for use on list elements. +// Accessor method. +HANDLE ConditionVariable::Event::handle() const { + DCHECK(ValidateAsItem()); + return handle_; +} + +// Pull an element from a list (if it's in one). +ConditionVariable::Event* ConditionVariable::Event::Extract() { + DCHECK(ValidateAsItem()); + if (!IsSingleton()) { + // Stitch neighbors together. + next_->prev_ = prev_; + prev_->next_ = next_; + // Make extractee into a singleton. + prev_ = next_ = this; + } + DCHECK(IsSingleton()); + return this; +} + +// Method for use on a list element or on a list. +bool ConditionVariable::Event::IsSingleton() const { + DCHECK(ValidateLinks()); + return next_ == this; +} + +// Provide pre/post conditions to validate correct manipulations. +bool ConditionVariable::Event::ValidateAsDistinct(Event* other) const { + return ValidateLinks() && other->ValidateLinks() && (this != other); +} + +bool ConditionVariable::Event::ValidateAsItem() const { + return (0 != handle_) && ValidateLinks(); +} + +bool ConditionVariable::Event::ValidateAsList() const { + return (0 == handle_) && ValidateLinks(); +} + +bool ConditionVariable::Event::ValidateLinks() const { + // Make sure both of our neighbors have links that point back to us. + // We don't do the O(n) check and traverse the whole loop, and instead only + // do a local check to (and returning from) our immediate neighbors. + return (next_->prev_ == this) && (prev_->next_ == this); +} + + +/* +FAQ On subtle implementation details: + +1) What makes this problem subtle? Please take a look at "Strategies +for Implementing POSIX Condition Variables on Win32" by Douglas +C. Schmidt and Irfan Pyarali. +http://www.cs.wustl.edu/~schmidt/win32-cv-1.html It includes +discussions of numerous flawed strategies for implementing this +functionality. I'm not convinced that even the final proposed +implementation has semantics that are as nice as this implementation +(especially with regard to Broadcast() and the impact on threads that +try to Wait() after a Broadcast() has been called, but before all the +original waiting threads have been signaled). + +2) Why can't you use a single wait_event for all threads that call +Wait()? See FAQ-question-1, or consider the following: If a single +event were used, then numerous threads calling Wait() could release +their cs locks, and be preempted just before calling +WaitForSingleObject(). If a call to Broadcast() was then presented on +a second thread, it would be impossible to actually signal all +waiting(?) threads. Some number of SetEvent() calls *could* be made, +but there could be no guarantee that those led to to more than one +signaled thread (SetEvent()'s may be discarded after the first!), and +there could be no guarantee that the SetEvent() calls didn't just +awaken "other" threads that hadn't even started waiting yet (oops). +Without any limit on the number of requisite SetEvent() calls, the +system would be forced to do many such calls, allowing many new waits +to receive spurious signals. + +3) How does this implementation cause spurious signal events? The +cause in this implementation involves a race between a signal via +time-out and a signal via Signal() or Broadcast(). The series of +actions leading to this are: + +a) Timer fires, and a waiting thread exits the line of code: + + WaitForSingleObject(waiting_event, max_time.InMilliseconds()); + +b) That thread (in (a)) is randomly pre-empted after the above line, +leaving the waiting_event reset (unsignaled) and still in the +waiting_list_. + +c) A call to Signal() (or Broadcast()) on a second thread proceeds, and +selects the waiting cv_event (identified in step (b)) as the event to revive +via a call to SetEvent(). + +d) The Signal() method (step c) calls SetEvent() on waiting_event (step b). + +e) The waiting cv_event (step b) is now signaled, but no thread is +waiting on it. + +f) When that waiting_event (step b) is reused, it will immediately +be signaled (spuriously). + + +4) Why do you recycle events, and cause spurious signals? First off, +the spurious events are very rare. They can only (I think) appear +when the race described in FAQ-question-3 takes place. This should be +very rare. Most(?) uses will involve only timer expiration, or only +Signal/Broadcast() actions. When both are used, it will be rare that +the race will appear, and it would require MANY Wait() and signaling +activities. If this implementation did not recycle events, then it +would have to create and destroy events for every call to Wait(). +That allocation/deallocation and associated construction/destruction +would be costly (per wait), and would only be a rare benefit (when the +race was "lost" and a spurious signal took place). That would be bad +(IMO) optimization trade-off. Finally, such spurious events are +allowed by the specification of condition variables (such as +implemented in Vista), and hence it is better if any user accommodates +such spurious events (see usage note in condition_variable.h). + +5) Why don't you reset events when you are about to recycle them, or +about to reuse them, so that the spurious signals don't take place? +The thread described in FAQ-question-3 step c may be pre-empted for an +arbitrary length of time before proceeding to step d. As a result, +the wait_event may actually be re-used *before* step (e) is reached. +As a result, calling reset would not help significantly. + +6) How is it that the callers lock is released atomically with the +entry into a wait state? We commit to the wait activity when we +allocate the wait_event for use in a given call to Wait(). This +allocation takes place before the caller's lock is released (and +actually before our internal_lock_ is released). That allocation is +the defining moment when "the wait state has been entered," as that +thread *can* now be signaled by a call to Broadcast() or Signal(). +Hence we actually "commit to wait" before releasing the lock, making +the pair effectively atomic. + +8) Why do you need to lock your data structures during waiting, as the +caller is already in possession of a lock? We need to Acquire() and +Release() our internal lock during Signal() and Broadcast(). If we tried +to use a callers lock for this purpose, we might conflict with their +external use of the lock. For example, the caller may use to consistently +hold a lock on one thread while calling Signal() on another, and that would +block Signal(). + +9) Couldn't a more efficient implementation be provided if you +preclude using more than one external lock in conjunction with a +single ConditionVariable instance? Yes, at least it could be viewed +as a simpler API (since you don't have to reiterate the lock argument +in each Wait() call). One of the constructors now takes a specific +lock as an argument, and a there are corresponding Wait() calls that +don't specify a lock now. It turns that the resulting implmentation +can't be made more efficient, as the internal lock needs to be used by +Signal() and Broadcast(), to access internal data structures. As a +result, I was not able to utilize the user supplied lock (which is +being used by the user elsewhere presumably) to protect the private +member access. + +9) Since you have a second lock, how can be be sure that there is no +possible deadlock scenario? Our internal_lock_ is always the last +lock acquired, and the first one released, and hence a deadlock (due +to critical section problems) is impossible as a consequence of our +lock. + +10) When doing a Broadcast(), why did you copy all the events into +an STL queue, rather than making a linked-loop, and iterating over it? +The iterating during Broadcast() is done so outside the protection +of the internal lock. As a result, other threads, such as the thread +wherein a related event is waiting, could asynchronously manipulate +the links around a cv_event. As a result, the link structure cannot +be used outside a lock. Broadcast() could iterate over waiting +events by cycling in-and-out of the protection of the internal_lock, +but that appears more expensive than copying the list into an STL +stack. + +11) Why did the lock.h file need to be modified so much for this +change? Central to a Condition Variable is the atomic release of a +lock during a Wait(). This places Wait() functionality exactly +mid-way between the two classes, Lock and Condition Variable. Given +that there can be nested Acquire()'s of locks, and Wait() had to +Release() completely a held lock, it was necessary to augment the Lock +class with a recursion counter. Even more subtle is the fact that the +recursion counter (in a Lock) must be protected, as many threads can +access it asynchronously. As a positive fallout of this, there are +now some DCHECKS to be sure no one Release()s a Lock more than they +Acquire()ed it, and there is ifdef'ed functionality that can detect +nested locks (legal under windows, but not under Posix). + +12) Why is it that the cv_events removed from list in Broadcast() and Signal() +are not leaked? How are they recovered?? The cv_events that appear to leak are +taken from the waiting_list_. For each element in that list, there is currently +a thread in or around the WaitForSingleObject() call of Wait(), and those +threads have references to these otherwise leaked events. They are passed as +arguments to be recycled just aftre returning from WaitForSingleObject(). + +13) Why did you use a custom container class (the linked list), when STL has +perfectly good containers, such as an STL list? The STL list, as with any +container, does not guarantee the utility of an iterator across manipulation +(such as insertions and deletions) of the underlying container. The custom +double-linked-list container provided that assurance. I don't believe any +combination of STL containers provided the services that were needed at the same +O(1) efficiency as the custom linked list. The unusual requirement +for the container class is that a reference to an item within a container (an +iterator) needed to be maintained across an arbitrary manipulation of the +container. This requirement exposes itself in the Wait() method, where a +waiting_event must be selected prior to the WaitForSingleObject(), and then it +must be used as part of recycling to remove the related instance from the +waiting_list. A hash table (STL map) could be used, but I was embarrased to +use a complex and relatively low efficiency container when a doubly linked list +provided O(1) performance in all required operations. Since other operations +to provide performance-and/or-fairness required queue (FIFO) and list (LIFO) +containers, I would also have needed to use an STL list/queue as well as an STL +map. In the end I decided it would be "fun" to just do it right, and I +put so many assertions (DCHECKs) into the container class that it is trivial to +code review and validate its correctness. + +*/ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/cpu.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/cpu.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/cpu.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/cpu.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,54 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/cpu.h" +#include +#include + +namespace base { + +CPU::CPU() + : type_(0), + family_(0), + model_(0), + stepping_(0), + ext_model_(0), + ext_family_(0), + cpu_vendor_("unknown") { + Initialize(); +} + +void CPU::Initialize() { + int cpu_info[4] = {-1}; + char cpu_string[0x20]; + + // __cpuid with an InfoType argument of 0 returns the number of + // valid Ids in CPUInfo[0] and the CPU identification string in + // the other three array elements. The CPU identification string is + // not in linear order. The code below arranges the information + // in a human readable form. + // + // More info can be found here: + // http://msdn.microsoft.com/en-us/library/hskdteyh.aspx + __cpuid(cpu_info, 0); + int num_ids = cpu_info[0]; + memset(cpu_string, 0, sizeof(cpu_string)); + *(reinterpret_cast(cpu_string)) = cpu_info[1]; + *(reinterpret_cast(cpu_string+4)) = cpu_info[3]; + *(reinterpret_cast(cpu_string+8)) = cpu_info[2]; + + // Interpret CPU feature information. + if (num_ids > 0) { + __cpuid(cpu_info, 1); + stepping_ = cpu_info[0] & 0xf; + model_ = (cpu_info[0] >> 4) & 0xf; + family_ = (cpu_info[0] >> 8) & 0xf; + type_ = (cpu_info[0] >> 12) & 0x3; + ext_model_ = (cpu_info[0] >> 16) & 0xf; + ext_family_ = (cpu_info[0] >> 20) & 0xff; + cpu_vendor_ = cpu_string; + } +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/cpu.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/cpu.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/cpu.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/cpu.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,42 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CPU_H_ +#define BASE_CPU_H_ + +#include + +namespace base { + +// Query information about the processor. +class CPU { + public: + // Constructor + CPU(); + + // Accessors for CPU information. + const std::string& vendor_name() const { return cpu_vendor_; } + int stepping() const { return stepping_; } + int model() const { return model_; } + int family() const { return family_; } + int type() const { return type_; } + int extended_model() const { return ext_model_; } + int extended_family() const { return ext_family_; } + + private: + // Query the processor for CPUID information. + void Initialize(); + + int type_; // process type + int family_; // family of the processor + int model_; // model of processor + int stepping_; // processor revision number + int ext_model_; + int ext_family_; + std::string cpu_vendor_; +}; + +} // namespace base + +#endif // BASE_CPU_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/crypto/cssm_init.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/crypto/cssm_init.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/crypto/cssm_init.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/crypto/cssm_init.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,72 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/crypto/cssm_init.h" + +#include + +#include "base/logging.h" +#include "base/singleton.h" + +// When writing crypto code for Mac OS X, you may find the following +// documentation useful: +// - Common Security: CDSA and CSSM, Version 2 (with corrigenda) +// http://www.opengroup.org/security/cdsa.htm +// - Apple Cryptographic Service Provider Functional Specification +// - CryptoSample: http://developer.apple.com/SampleCode/CryptoSample/ + +namespace { + +class CSSMInitSingleton { + public: + CSSMInitSingleton() : inited_(false), loaded_(false) { + static CSSM_VERSION version = {2, 0}; + // TODO(wtc): what should our caller GUID be? + static const CSSM_GUID test_guid = { + 0xFADE, 0, 0, { 1, 2, 3, 4, 5, 6, 7, 0 } + }; + CSSM_RETURN crtn; + CSSM_PVC_MODE pvc_policy = CSSM_PVC_NONE; + crtn = CSSM_Init(&version, CSSM_PRIVILEGE_SCOPE_NONE, &test_guid, + CSSM_KEY_HIERARCHY_NONE, &pvc_policy, NULL); + if (crtn) { + NOTREACHED(); + return; + } + inited_ = true; + + crtn = CSSM_ModuleLoad(&gGuidAppleCSP, CSSM_KEY_HIERARCHY_NONE, NULL, NULL); + if (crtn) { + NOTREACHED(); + return; + } + loaded_ = true; + } + + ~CSSMInitSingleton() { + CSSM_RETURN crtn; + if (loaded_) { + crtn = CSSM_ModuleUnload(&gGuidAppleCSP, NULL, NULL); + DCHECK(crtn == CSSM_OK); + } + if (inited_) { + crtn = CSSM_Terminate(); + DCHECK(crtn == CSSM_OK); + } + } + + private: + bool inited_; // True if CSSM_Init has been called successfully. + bool loaded_; // True if CSSM_ModuleLoad has been called successfully. +}; + +} // namespace + +namespace base { + +void EnsureCSSMInit() { + Singleton::get(); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/crypto/cssm_init.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/crypto/cssm_init.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/crypto/cssm_init.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/crypto/cssm_init.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,17 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CRYPTO_CSSM_INIT_H_ +#define BASE_CRYPTO_CSSM_INIT_H_ + +namespace base { + +// Initialize CSSM if it isn't already initialized. This must be called before +// any other CSSM functions. This function is thread-safe, and CSSM will only +// ever be initialized once. CSSM will be properly shut down on program exit. +void EnsureCSSMInit(); + +} // namespace base + +#endif // BASE_CRYPTO_CSSM_INIT_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,105 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_CRYPTO_SIGNATURE_VERIFIER_H_ +#define BASE_CRYPTO_SIGNATURE_VERIFIER_H_ + +#include "build/build_config.h" + +#if defined(OS_LINUX) +#include +#elif defined(OS_MACOSX) +#include +#elif defined(OS_WIN) +#include +#include +#endif + +#include + +#include "base/basictypes.h" + +namespace base { + +// The SignatureVerifier class verifies a signature using a bare public key +// (as opposed to a certificate). +class SignatureVerifier { + public: + SignatureVerifier(); + ~SignatureVerifier(); + + // Streaming interface: + + // Initiates a signature verification operation. This should be followed + // by one or more VerifyUpdate calls and a VerifyFinal call. + // + // The signature algorithm is specified as a DER encoded ASN.1 + // AlgorithmIdentifier structure: + // AlgorithmIdentifier ::= SEQUENCE { + // algorithm OBJECT IDENTIFIER, + // parameters ANY DEFINED BY algorithm OPTIONAL } + // + // The signature is encoded according to the signature algorithm, but it + // must not be further encoded in an ASN.1 BIT STRING. + // Note: An RSA signatures is actually a big integer. It must be in the + // big-endian byte order. + // + // The public key is specified as a DER encoded ASN.1 SubjectPublicKeyInfo + // structure, which contains not only the public key but also its type + // (algorithm): + // SubjectPublicKeyInfo ::= SEQUENCE { + // algorithm AlgorithmIdentifier, + // subjectPublicKey BIT STRING } + bool VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len); + + // Feeds a piece of the data to the signature verifier. + void VerifyUpdate(const uint8* data_part, int data_part_len); + + // Concludes a signature verification operation. Returns true if the + // signature is valid. Returns false if the signature is invalid or an + // error occurred. + bool VerifyFinal(); + + // Note: we can provide a one-shot interface if there is interest: + // bool Verify(const uint8* data, + // int data_len, + // const uint8* signature_algorithm, + // int signature_algorithm_len, + // const uint8* signature, + // int signature_len, + // const uint8* public_key_info, + // int public_key_info_len); + + private: + void Reset(); + + std::vector signature_; + +#if defined(OS_LINUX) + VFYContext* vfy_context_; +#elif defined(OS_MACOSX) + std::vector public_key_info_; + + CSSM_CSP_HANDLE csp_handle_; + + CSSM_CC_HANDLE sig_handle_; + + CSSM_KEY public_key_; +#elif defined(OS_WIN) + HCRYPTPROV provider_; + + HCRYPTHASH hash_object_; + + HCRYPTKEY public_key_; +#endif +}; + +} // namespace base + +#endif // BASE_CRYPTO_SIGNATURE_VERIFIER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier_mac.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier_mac.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier_mac.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier_mac.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,143 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/crypto/signature_verifier.h" + +#include + +#include "base/crypto/cssm_init.h" +#include "base/logging.h" + +namespace { + +void* AppMalloc(CSSM_SIZE size, void *alloc_ref) { + return malloc(size); +} + +void AppFree(void* mem_ptr, void* alloc_ref) { + free(mem_ptr); +} + +void* AppRealloc(void* ptr, CSSM_SIZE size, void* alloc_ref) { + return realloc(ptr, size); +} + +void* AppCalloc(uint32 num, CSSM_SIZE size, void* alloc_ref) { + return calloc(num, size); +} + +const CSSM_API_MEMORY_FUNCS mem_funcs = { + AppMalloc, + AppFree, + AppRealloc, + AppCalloc, + NULL +}; + +} // namespace + +namespace base { + +SignatureVerifier::SignatureVerifier() : csp_handle_(0), sig_handle_(0) { + EnsureCSSMInit(); + + static CSSM_VERSION version = {2, 0}; + CSSM_RETURN crtn; + crtn = CSSM_ModuleAttach(&gGuidAppleCSP, &version, &mem_funcs, 0, + CSSM_SERVICE_CSP, 0, CSSM_KEY_HIERARCHY_NONE, + NULL, 0, NULL, &csp_handle_); + DCHECK(crtn == CSSM_OK); +} + +SignatureVerifier::~SignatureVerifier() { + Reset(); + if (csp_handle_) { + CSSM_RETURN crtn = CSSM_ModuleDetach(csp_handle_); + DCHECK(crtn == CSSM_OK); + } +} + +bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len) { + signature_.assign(signature, signature + signature_len); + public_key_info_.assign(public_key_info, + public_key_info + public_key_info_len); + + CSSM_ALGORITHMS key_alg = CSSM_ALGID_RSA; // TODO(wtc): hardcoded. + + memset(&public_key_, 0, sizeof(public_key_)); + public_key_.KeyData.Data = const_cast(&public_key_info_[0]); + public_key_.KeyData.Length = public_key_info_.size(); + public_key_.KeyHeader.HeaderVersion = CSSM_KEYHEADER_VERSION; + public_key_.KeyHeader.BlobType = CSSM_KEYBLOB_RAW; + public_key_.KeyHeader.Format = CSSM_KEYBLOB_RAW_FORMAT_X509; + public_key_.KeyHeader.AlgorithmId = key_alg; + public_key_.KeyHeader.KeyClass = CSSM_KEYCLASS_PUBLIC_KEY; + public_key_.KeyHeader.KeyAttr = CSSM_KEYATTR_EXTRACTABLE; + public_key_.KeyHeader.KeyUsage = CSSM_KEYUSE_VERIFY; + CSSM_KEY_SIZE key_size; + CSSM_RETURN crtn; + crtn = CSSM_QueryKeySizeInBits(csp_handle_, NULL, &public_key_, &key_size); + if (crtn) { + NOTREACHED() << "CSSM_QueryKeySizeInBits failed: " << crtn; + return false; + } + public_key_.KeyHeader.LogicalKeySizeInBits = key_size.LogicalKeySizeInBits; + + // TODO(wtc): decode signature_algorithm... + CSSM_ALGORITHMS sig_alg = CSSM_ALGID_SHA1WithRSA; + + crtn = CSSM_CSP_CreateSignatureContext(csp_handle_, sig_alg, NULL, + &public_key_, &sig_handle_); + if (crtn) { + NOTREACHED(); + return false; + } + crtn = CSSM_VerifyDataInit(sig_handle_); + if (crtn) { + NOTREACHED(); + return false; + } + return true; +} + +void SignatureVerifier::VerifyUpdate(const uint8* data_part, + int data_part_len) { + CSSM_DATA data; + data.Data = const_cast(data_part); + data.Length = data_part_len; + CSSM_RETURN crtn = CSSM_VerifyDataUpdate(sig_handle_, &data, 1); + DCHECK(crtn == CSSM_OK); +} + +bool SignatureVerifier::VerifyFinal() { + CSSM_DATA sig; + sig.Data = const_cast(&signature_[0]); + sig.Length = signature_.size(); + CSSM_RETURN crtn = CSSM_VerifyDataFinal(sig_handle_, &sig); + Reset(); + + // crtn is CSSMERR_CSP_VERIFY_FAILED if signature verification fails. + return (crtn == CSSM_OK); +} + +void SignatureVerifier::Reset() { + CSSM_RETURN crtn; + if (sig_handle_) { + crtn = CSSM_DeleteContext(sig_handle_); + DCHECK(crtn == CSSM_OK); + sig_handle_ = 0; + } + signature_.clear(); + + // Can't call CSSM_FreeKey on public_key_ because we constructed + // public_key_ manually. +} + +} // namespace base + diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier_nss.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier_nss.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier_nss.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier_nss.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,114 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/crypto/signature_verifier.h" + +#include +#include +#include + +#include "base/logging.h" +#include "base/nss_init.h" + +namespace base { + +SignatureVerifier::SignatureVerifier() : vfy_context_(NULL) { + EnsureNSSInit(); +} + +SignatureVerifier::~SignatureVerifier() { + Reset(); +} + +bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len) { + signature_.assign(signature, signature + signature_len); + + CERTSubjectPublicKeyInfo* spki = NULL; + SECItem spki_der; + spki_der.type = siBuffer; + spki_der.data = const_cast(public_key_info); + spki_der.len = public_key_info_len; + spki = SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_der); + if (!spki) + return false; + SECKEYPublicKey* public_key = SECKEY_ExtractPublicKey(spki); + SECKEY_DestroySubjectPublicKeyInfo(spki); // Done with spki. + if (!public_key) + return false; + + PLArenaPool* arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (!arena) { + SECKEY_DestroyPublicKey(public_key); + return false; + } + + SECItem sig_alg_der; + sig_alg_der.type = siBuffer; + sig_alg_der.data = const_cast(signature_algorithm); + sig_alg_der.len = signature_algorithm_len; + SECAlgorithmID sig_alg_id; + SECStatus rv; + rv = SEC_QuickDERDecodeItem(arena, &sig_alg_id, SECOID_AlgorithmIDTemplate, + &sig_alg_der); + if (rv != SECSuccess) { + SECKEY_DestroyPublicKey(public_key); + PORT_FreeArena(arena, PR_TRUE); + return false; + } + + SECItem sig; + sig.type = siBuffer; + sig.data = const_cast(signature); + sig.len = signature_len; + SECOidTag hash_alg_tag; + vfy_context_ = VFY_CreateContextWithAlgorithmID(public_key, &sig, + &sig_alg_id, &hash_alg_tag, + NULL); + SECKEY_DestroyPublicKey(public_key); // Done with public_key. + PORT_FreeArena(arena, PR_TRUE); // Done with sig_alg_id. + if (!vfy_context_) { + // A corrupted RSA signature could be detected without the data, so + // VFY_CreateContextWithAlgorithmID may fail with SEC_ERROR_BAD_SIGNATURE + // (-8182). + return false; + } + + rv = VFY_Begin(vfy_context_); + if (rv != SECSuccess) { + NOTREACHED(); + return false; + } + return true; +} + +void SignatureVerifier::VerifyUpdate(const uint8* data_part, + int data_part_len) { + SECStatus rv = VFY_Update(vfy_context_, data_part, data_part_len); + DCHECK(rv == SECSuccess); +} + +bool SignatureVerifier::VerifyFinal() { + SECStatus rv = VFY_End(vfy_context_); + Reset(); + + // If signature verification fails, the error code is + // SEC_ERROR_BAD_SIGNATURE (-8182). + return (rv == SECSuccess); +} + +void SignatureVerifier::Reset() { + if (vfy_context_) { + VFY_DestroyContext(vfy_context_, PR_TRUE); + vfy_context_ = NULL; + } + signature_.clear(); +} + +} // namespace base + diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,268 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/crypto/signature_verifier.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(SignatureVerifierTest, BasicTest) { + // The input data in this test comes from real certificates. + // + // tbs_certificate ("to-be-signed certificate", the part of a certificate + // that is signed), signature_algorithm, and algorithm come from the + // certificate of bugs.webkit.org. + // + // public_key_info comes from the certificate of the issuer, Go Daddy Secure + // Certification Authority. + // + // The bytes in the array initializers are formatted to expose the DER + // encoding of the ASN.1 structures. + + // The data that is signed is the following ASN.1 structure: + // TBSCertificate ::= SEQUENCE { + // ... -- omitted, not important + // } + const uint8 tbs_certificate[1017] = { + 0x30, 0x82, 0x03, 0xf5, // a SEQUENCE of length 1013 (0x3f5) + 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x43, 0xdd, 0x63, 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, + 0x00, 0x30, 0x81, 0xca, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x10, 0x30, 0x0e, 0x06, 0x03, 0x55, + 0x04, 0x08, 0x13, 0x07, 0x41, 0x72, 0x69, 0x7a, 0x6f, 0x6e, 0x61, 0x31, + 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0a, 0x53, 0x63, + 0x6f, 0x74, 0x74, 0x73, 0x64, 0x61, 0x6c, 0x65, 0x31, 0x1a, 0x30, 0x18, + 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x47, 0x6f, 0x44, 0x61, 0x64, + 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x20, 0x49, 0x6e, 0x63, 0x2e, + 0x31, 0x33, 0x30, 0x31, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x2a, 0x68, + 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, + 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, + 0x69, 0x74, 0x6f, 0x72, 0x79, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, + 0x04, 0x03, 0x13, 0x27, 0x47, 0x6f, 0x20, 0x44, 0x61, 0x64, 0x64, 0x79, + 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x41, 0x75, + 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x31, 0x11, 0x30, 0x0f, 0x06, + 0x03, 0x55, 0x04, 0x05, 0x13, 0x08, 0x30, 0x37, 0x39, 0x36, 0x39, 0x32, + 0x38, 0x37, 0x30, 0x1e, 0x17, 0x0d, 0x30, 0x38, 0x30, 0x33, 0x31, 0x38, + 0x32, 0x33, 0x33, 0x35, 0x31, 0x39, 0x5a, 0x17, 0x0d, 0x31, 0x31, 0x30, + 0x33, 0x31, 0x38, 0x32, 0x33, 0x33, 0x35, 0x31, 0x39, 0x5a, 0x30, 0x79, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, + 0x53, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, + 0x43, 0x61, 0x6c, 0x69, 0x66, 0x6f, 0x72, 0x6e, 0x69, 0x61, 0x31, 0x12, + 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x09, 0x43, 0x75, 0x70, + 0x65, 0x72, 0x74, 0x69, 0x6e, 0x6f, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, + 0x55, 0x04, 0x0a, 0x13, 0x0a, 0x41, 0x70, 0x70, 0x6c, 0x65, 0x20, 0x49, + 0x6e, 0x63, 0x2e, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0b, + 0x13, 0x0c, 0x4d, 0x61, 0x63, 0x20, 0x4f, 0x53, 0x20, 0x46, 0x6f, 0x72, + 0x67, 0x65, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x0c, 0x2a, 0x2e, 0x77, 0x65, 0x62, 0x6b, 0x69, 0x74, 0x2e, 0x6f, 0x72, + 0x67, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, + 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, + 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xa7, 0x62, 0x79, 0x41, 0xda, 0x28, + 0xf2, 0xc0, 0x4f, 0xe0, 0x25, 0xaa, 0xa1, 0x2e, 0x3b, 0x30, 0x94, 0xb5, + 0xc9, 0x26, 0x3a, 0x1b, 0xe2, 0xd0, 0xcc, 0xa2, 0x95, 0xe2, 0x91, 0xc0, + 0xf0, 0x40, 0x9e, 0x27, 0x6e, 0xbd, 0x6e, 0xde, 0x7c, 0xb6, 0x30, 0x5c, + 0xb8, 0x9b, 0x01, 0x2f, 0x92, 0x04, 0xa1, 0xef, 0x4a, 0xb1, 0x6c, 0xb1, + 0x7e, 0x8e, 0xcd, 0xa6, 0xf4, 0x40, 0x73, 0x1f, 0x2c, 0x96, 0xad, 0xff, + 0x2a, 0x6d, 0x0e, 0xba, 0x52, 0x84, 0x83, 0xb0, 0x39, 0xee, 0xc9, 0x39, + 0xdc, 0x1e, 0x34, 0xd0, 0xd8, 0x5d, 0x7a, 0x09, 0xac, 0xa9, 0xee, 0xca, + 0x65, 0xf6, 0x85, 0x3a, 0x6b, 0xee, 0xe4, 0x5c, 0x5e, 0xf8, 0xda, 0xd1, + 0xce, 0x88, 0x47, 0xcd, 0x06, 0x21, 0xe0, 0xb9, 0x4b, 0xe4, 0x07, 0xcb, + 0x57, 0xdc, 0xca, 0x99, 0x54, 0xf7, 0x0e, 0xd5, 0x17, 0x95, 0x05, 0x2e, + 0xe9, 0xb1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x82, 0x01, 0xce, 0x30, + 0x82, 0x01, 0xca, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, + 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, + 0x02, 0x05, 0xa0, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x16, + 0x30, 0x14, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x01, + 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02, 0x30, 0x57, + 0x06, 0x03, 0x55, 0x1d, 0x1f, 0x04, 0x50, 0x30, 0x4e, 0x30, 0x4c, 0xa0, + 0x4a, 0xa0, 0x48, 0x86, 0x46, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, + 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, + 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, + 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, 0x6f, 0x72, 0x79, 0x2f, + 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, 0x65, 0x78, 0x74, 0x65, 0x6e, + 0x64, 0x65, 0x64, 0x69, 0x73, 0x73, 0x75, 0x69, 0x6e, 0x67, 0x33, 0x2e, + 0x63, 0x72, 0x6c, 0x30, 0x52, 0x06, 0x03, 0x55, 0x1d, 0x20, 0x04, 0x4b, + 0x30, 0x49, 0x30, 0x47, 0x06, 0x0b, 0x60, 0x86, 0x48, 0x01, 0x86, 0xfd, + 0x6d, 0x01, 0x07, 0x17, 0x02, 0x30, 0x38, 0x30, 0x36, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x02, 0x01, 0x16, 0x2a, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, + 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, 0x79, + 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, 0x74, + 0x6f, 0x72, 0x79, 0x30, 0x7f, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, + 0x07, 0x01, 0x01, 0x04, 0x73, 0x30, 0x71, 0x30, 0x23, 0x06, 0x08, 0x2b, + 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x86, 0x17, 0x68, 0x74, 0x74, + 0x70, 0x3a, 0x2f, 0x2f, 0x6f, 0x63, 0x73, 0x70, 0x2e, 0x67, 0x6f, 0x64, + 0x61, 0x64, 0x64, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x30, 0x4a, 0x06, 0x08, + 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x02, 0x86, 0x3e, 0x68, 0x74, + 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x73, 0x2e, 0x67, 0x6f, 0x64, 0x61, 0x64, 0x64, + 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x72, 0x65, 0x70, 0x6f, 0x73, 0x69, + 0x74, 0x6f, 0x72, 0x79, 0x2f, 0x67, 0x64, 0x5f, 0x69, 0x6e, 0x74, 0x65, + 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x74, 0x65, 0x2e, 0x63, 0x72, 0x74, + 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x48, + 0xdf, 0x60, 0x32, 0xcc, 0x89, 0x01, 0xb6, 0xdc, 0x2f, 0xe3, 0x73, 0xb5, + 0x9c, 0x16, 0x58, 0x32, 0x68, 0xa9, 0xc3, 0x30, 0x1f, 0x06, 0x03, 0x55, + 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xfd, 0xac, 0x61, 0x32, + 0x93, 0x6c, 0x45, 0xd6, 0xe2, 0xee, 0x85, 0x5f, 0x9a, 0xba, 0xe7, 0x76, + 0x99, 0x68, 0xcc, 0xe7, 0x30, 0x23, 0x06, 0x03, 0x55, 0x1d, 0x11, 0x04, + 0x1c, 0x30, 0x1a, 0x82, 0x0c, 0x2a, 0x2e, 0x77, 0x65, 0x62, 0x6b, 0x69, + 0x74, 0x2e, 0x6f, 0x72, 0x67, 0x82, 0x0a, 0x77, 0x65, 0x62, 0x6b, 0x69, + 0x74, 0x2e, 0x6f, 0x72, 0x67 + }; + + // The signature algorithm is specified as the following ASN.1 structure: + // AlgorithmIdentifier ::= SEQUENCE { + // algorithm OBJECT IDENTIFIER, + // parameters ANY DEFINED BY algorithm OPTIONAL } + // + const uint8 signature_algorithm[15] = { + 0x30, 0x0d, // a SEQUENCE of length 13 (0xd) + 0x06, 0x09, // an OBJECT IDENTIFIER of length 9 + // 1.2.840.113549.1.1.5 - sha1WithRSAEncryption + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, + 0x05, 0x00, // a NULL of length 0 + }; + + // RSA signature, a big integer in the big-endian byte order. + const uint8 signature[256] = { + 0x1e, 0x6a, 0xe7, 0xe0, 0x4f, 0xe7, 0x4d, 0xd0, 0x69, 0x7c, 0xf8, 0x8f, + 0x99, 0xb4, 0x18, 0x95, 0x36, 0x24, 0x0f, 0x0e, 0xa3, 0xea, 0x34, 0x37, + 0xf4, 0x7d, 0xd5, 0x92, 0x35, 0x53, 0x72, 0x76, 0x3f, 0x69, 0xf0, 0x82, + 0x56, 0xe3, 0x94, 0x7a, 0x1d, 0x1a, 0x81, 0xaf, 0x9f, 0xc7, 0x43, 0x01, + 0x64, 0xd3, 0x7c, 0x0d, 0xc8, 0x11, 0x4e, 0x4a, 0xe6, 0x1a, 0xc3, 0x01, + 0x74, 0xe8, 0x35, 0x87, 0x5c, 0x61, 0xaa, 0x8a, 0x46, 0x06, 0xbe, 0x98, + 0x95, 0x24, 0x9e, 0x01, 0xe3, 0xe6, 0xa0, 0x98, 0xee, 0x36, 0x44, 0x56, + 0x8d, 0x23, 0x9c, 0x65, 0xea, 0x55, 0x6a, 0xdf, 0x66, 0xee, 0x45, 0xe8, + 0xa0, 0xe9, 0x7d, 0x9a, 0xba, 0x94, 0xc5, 0xc8, 0xc4, 0x4b, 0x98, 0xff, + 0x9a, 0x01, 0x31, 0x6d, 0xf9, 0x2b, 0x58, 0xe7, 0xe7, 0x2a, 0xc5, 0x4d, + 0xbb, 0xbb, 0xcd, 0x0d, 0x70, 0xe1, 0xad, 0x03, 0xf5, 0xfe, 0xf4, 0x84, + 0x71, 0x08, 0xd2, 0xbc, 0x04, 0x7b, 0x26, 0x1c, 0xa8, 0x0f, 0x9c, 0xd8, + 0x12, 0x6a, 0x6f, 0x2b, 0x67, 0xa1, 0x03, 0x80, 0x9a, 0x11, 0x0b, 0xe9, + 0xe0, 0xb5, 0xb3, 0xb8, 0x19, 0x4e, 0x0c, 0xa4, 0xd9, 0x2b, 0x3b, 0xc2, + 0xca, 0x20, 0xd3, 0x0c, 0xa4, 0xff, 0x93, 0x13, 0x1f, 0xfc, 0xba, 0x94, + 0x93, 0x8c, 0x64, 0x15, 0x2e, 0x28, 0xa9, 0x55, 0x8c, 0x2c, 0x48, 0xd3, + 0xd3, 0xc1, 0x50, 0x69, 0x19, 0xe8, 0x34, 0xd3, 0xf1, 0x04, 0x9f, 0x0a, + 0x7a, 0x21, 0x87, 0xbf, 0xb9, 0x59, 0x37, 0x2e, 0xf4, 0x71, 0xa5, 0x3e, + 0xbe, 0xcd, 0x70, 0x83, 0x18, 0xf8, 0x8a, 0x72, 0x85, 0x45, 0x1f, 0x08, + 0x01, 0x6f, 0x37, 0xf5, 0x2b, 0x7b, 0xea, 0xb9, 0x8b, 0xa3, 0xcc, 0xfd, + 0x35, 0x52, 0xdd, 0x66, 0xde, 0x4f, 0x30, 0xc5, 0x73, 0x81, 0xb6, 0xe8, + 0x3c, 0xd8, 0x48, 0x8a + }; + + // The public key is specified as the following ASN.1 structure: + // SubjectPublicKeyInfo ::= SEQUENCE { + // algorithm AlgorithmIdentifier, + // subjectPublicKey BIT STRING } + const uint8 public_key_info[294] = { + 0x30, 0x82, 0x01, 0x22, // a SEQUENCE of length 290 (0x122) + // algorithm + 0x30, 0x0d, // a SEQUENCE of length 13 + 0x06, 0x09, // an OBJECT IDENTIFIER of length 9 + 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, + 0x05, 0x00, // a NULL of length 0 + // subjectPublicKey + 0x03, 0x82, 0x01, 0x0f, // a BIT STRING of length 271 (0x10f) + 0x00, // number of unused bits + 0x30, 0x82, 0x01, 0x0a, // a SEQUENCE of length 266 (0x10a) + // modulus + 0x02, 0x82, 0x01, 0x01, // an INTEGER of length 257 (0x101) + 0x00, 0xc4, 0x2d, 0xd5, 0x15, 0x8c, 0x9c, 0x26, 0x4c, 0xec, + 0x32, 0x35, 0xeb, 0x5f, 0xb8, 0x59, 0x01, 0x5a, 0xa6, 0x61, + 0x81, 0x59, 0x3b, 0x70, 0x63, 0xab, 0xe3, 0xdc, 0x3d, 0xc7, + 0x2a, 0xb8, 0xc9, 0x33, 0xd3, 0x79, 0xe4, 0x3a, 0xed, 0x3c, + 0x30, 0x23, 0x84, 0x8e, 0xb3, 0x30, 0x14, 0xb6, 0xb2, 0x87, + 0xc3, 0x3d, 0x95, 0x54, 0x04, 0x9e, 0xdf, 0x99, 0xdd, 0x0b, + 0x25, 0x1e, 0x21, 0xde, 0x65, 0x29, 0x7e, 0x35, 0xa8, 0xa9, + 0x54, 0xeb, 0xf6, 0xf7, 0x32, 0x39, 0xd4, 0x26, 0x55, 0x95, + 0xad, 0xef, 0xfb, 0xfe, 0x58, 0x86, 0xd7, 0x9e, 0xf4, 0x00, + 0x8d, 0x8c, 0x2a, 0x0c, 0xbd, 0x42, 0x04, 0xce, 0xa7, 0x3f, + 0x04, 0xf6, 0xee, 0x80, 0xf2, 0xaa, 0xef, 0x52, 0xa1, 0x69, + 0x66, 0xda, 0xbe, 0x1a, 0xad, 0x5d, 0xda, 0x2c, 0x66, 0xea, + 0x1a, 0x6b, 0xbb, 0xe5, 0x1a, 0x51, 0x4a, 0x00, 0x2f, 0x48, + 0xc7, 0x98, 0x75, 0xd8, 0xb9, 0x29, 0xc8, 0xee, 0xf8, 0x66, + 0x6d, 0x0a, 0x9c, 0xb3, 0xf3, 0xfc, 0x78, 0x7c, 0xa2, 0xf8, + 0xa3, 0xf2, 0xb5, 0xc3, 0xf3, 0xb9, 0x7a, 0x91, 0xc1, 0xa7, + 0xe6, 0x25, 0x2e, 0x9c, 0xa8, 0xed, 0x12, 0x65, 0x6e, 0x6a, + 0xf6, 0x12, 0x44, 0x53, 0x70, 0x30, 0x95, 0xc3, 0x9c, 0x2b, + 0x58, 0x2b, 0x3d, 0x08, 0x74, 0x4a, 0xf2, 0xbe, 0x51, 0xb0, + 0xbf, 0x87, 0xd0, 0x4c, 0x27, 0x58, 0x6b, 0xb5, 0x35, 0xc5, + 0x9d, 0xaf, 0x17, 0x31, 0xf8, 0x0b, 0x8f, 0xee, 0xad, 0x81, + 0x36, 0x05, 0x89, 0x08, 0x98, 0xcf, 0x3a, 0xaf, 0x25, 0x87, + 0xc0, 0x49, 0xea, 0xa7, 0xfd, 0x67, 0xf7, 0x45, 0x8e, 0x97, + 0xcc, 0x14, 0x39, 0xe2, 0x36, 0x85, 0xb5, 0x7e, 0x1a, 0x37, + 0xfd, 0x16, 0xf6, 0x71, 0x11, 0x9a, 0x74, 0x30, 0x16, 0xfe, + 0x13, 0x94, 0xa3, 0x3f, 0x84, 0x0d, 0x4f, + // public exponent + 0x02, 0x03, // an INTEGER of length 3 + 0x01, 0x00, 0x01 + }; + + // We use the signature verifier to perform four signature verification + // tests. + base::SignatureVerifier verifier; + bool ok; + + // Test 1: feed all of the data to the verifier at once (a single + // VerifyUpdate call). + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + signature, sizeof(signature), + public_key_info, sizeof(public_key_info)); + EXPECT_TRUE(ok); + verifier.VerifyUpdate(tbs_certificate, sizeof(tbs_certificate)); + ok = verifier.VerifyFinal(); + EXPECT_TRUE(ok); + + // Test 2: feed the data to the verifier in three parts (three VerifyUpdate + // calls). + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + signature, sizeof(signature), + public_key_info, sizeof(public_key_info)); + EXPECT_TRUE(ok); + verifier.VerifyUpdate(tbs_certificate, 256); + verifier.VerifyUpdate(tbs_certificate + 256, 256); + verifier.VerifyUpdate(tbs_certificate + 512, sizeof(tbs_certificate) - 512); + ok = verifier.VerifyFinal(); + EXPECT_TRUE(ok); + + // Test 3: verify the signature with incorrect data. + uint8 bad_tbs_certificate[sizeof(tbs_certificate)]; + memcpy(bad_tbs_certificate, tbs_certificate, sizeof(tbs_certificate)); + bad_tbs_certificate[10] += 1; // Corrupt one byte of the data. + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + signature, sizeof(signature), + public_key_info, sizeof(public_key_info)); + EXPECT_TRUE(ok); + verifier.VerifyUpdate(bad_tbs_certificate, sizeof(bad_tbs_certificate)); + ok = verifier.VerifyFinal(); + + // Purify disables digital signature verification, causing the Windows + // CryptoAPI function CryptVerifySignature to always succeed. So we can't + // check the signature verification results of the negative tests when + // running inside Purify. See http://crbug.com/10031. +#ifndef PURIFY + EXPECT_FALSE(ok); +#endif + + // Test 4: verify a bad signature. + uint8 bad_signature[sizeof(signature)]; + memcpy(bad_signature, signature, sizeof(signature)); + bad_signature[10] += 1; // Corrupt one byte of the signature. + ok = verifier.VerifyInit(signature_algorithm, + sizeof(signature_algorithm), + bad_signature, sizeof(bad_signature), + public_key_info, sizeof(public_key_info)); + + // A crypto library (e.g., NSS) may detect that the signature is corrupted + // and cause VerifyInit to return false, so it is fine for 'ok' to be false. + if (ok) { + verifier.VerifyUpdate(tbs_certificate, sizeof(tbs_certificate)); + ok = verifier.VerifyFinal(); +#ifndef PURIFY + EXPECT_FALSE(ok); +#endif + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/crypto/signature_verifier_win.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,148 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/crypto/signature_verifier.h" + +#include "base/logging.h" + +#pragma comment(lib, "crypt32.lib") + +namespace { + +// Wrappers of malloc and free for CRYPT_DECODE_PARA, which requires the +// WINAPI calling convention. +void* WINAPI MyCryptAlloc(size_t size) { + return malloc(size); +} + +void WINAPI MyCryptFree(void* p) { + free(p); +} + +} // namespace + +namespace base { + +SignatureVerifier::SignatureVerifier() : hash_object_(0), public_key_(0) { + if (!CryptAcquireContext(&provider_, NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + provider_ = 0; +} + +SignatureVerifier::~SignatureVerifier() { + Reset(); + if (provider_) { + BOOL ok = CryptReleaseContext(provider_, 0); + DCHECK(ok); + } +} + +bool SignatureVerifier::VerifyInit(const uint8* signature_algorithm, + int signature_algorithm_len, + const uint8* signature, + int signature_len, + const uint8* public_key_info, + int public_key_info_len) { + signature_.reserve(signature_len); + // CryptoAPI uses big integers in the little-endian byte order, so we need + // to first swap the order of signature bytes. + for (int i = signature_len - 1; i >= 0; --i) + signature_.push_back(signature[i]); + + CRYPT_DECODE_PARA decode_para; + decode_para.cbSize = sizeof(decode_para); + decode_para.pfnAlloc = MyCryptAlloc; + decode_para.pfnFree = MyCryptFree; + CERT_PUBLIC_KEY_INFO* cert_public_key_info = NULL; + DWORD struct_len = 0; + BOOL ok; + ok = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + X509_PUBLIC_KEY_INFO, + public_key_info, + public_key_info_len, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, + &decode_para, + &cert_public_key_info, + &struct_len); + if (!ok) + return false; + + ok = CryptImportPublicKeyInfo(provider_, + X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + cert_public_key_info, &public_key_); + free(cert_public_key_info); + if (!ok) + return false; + + CRYPT_ALGORITHM_IDENTIFIER* signature_algorithm_id; + struct_len = 0; + ok = CryptDecodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, + X509_ALGORITHM_IDENTIFIER, + signature_algorithm, + signature_algorithm_len, + CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, + &decode_para, + &signature_algorithm_id, + &struct_len); + DCHECK(ok || GetLastError() == ERROR_FILE_NOT_FOUND); + ALG_ID hash_alg_id; + if (ok) { + hash_alg_id = CALG_MD4; // Initialize to a weak hash algorithm that we + // don't support. + if (!strcmp(signature_algorithm_id->pszObjId, szOID_RSA_SHA1RSA)) + hash_alg_id = CALG_SHA1; + else if (!strcmp(signature_algorithm_id->pszObjId, szOID_RSA_MD5RSA)) + hash_alg_id = CALG_MD5; + free(signature_algorithm_id); + DCHECK(hash_alg_id != CALG_MD4); + if (hash_alg_id == CALG_MD4) + return false; // Unsupported hash algorithm. + } else if (GetLastError() == ERROR_FILE_NOT_FOUND) { + // TODO(wtc): X509_ALGORITHM_IDENTIFIER isn't supported on XP SP2. We + // may be able to encapsulate signature_algorithm in a dummy SignedContent + // and decode it with X509_CERT into a CERT_SIGNED_CONTENT_INFO. For now, + // just hardcode the hash algorithm to be SHA-1. + hash_alg_id = CALG_SHA1; + } else { + return false; + } + + ok = CryptCreateHash(provider_, hash_alg_id, 0, 0, &hash_object_); + if (!ok) + return false; + return true; +} + +void SignatureVerifier::VerifyUpdate(const uint8* data_part, + int data_part_len) { + BOOL ok = CryptHashData(hash_object_, data_part, data_part_len, 0); + DCHECK(ok) << "CryptHashData failed: " << GetLastError(); +} + +bool SignatureVerifier::VerifyFinal() { + BOOL ok = CryptVerifySignature(hash_object_, &signature_[0], + signature_.size(), public_key_, NULL, 0); + Reset(); + if (!ok) + return false; + return true; +} + +void SignatureVerifier::Reset() { + BOOL ok; + if (hash_object_) { + ok = CryptDestroyHash(hash_object_); + DCHECK(ok); + hash_object_ = 0; + } + if (public_key_) { + ok = CryptDestroyKey(public_key_); + DCHECK(ok); + public_key_ = 0; + } + signature_.clear(); +} + +} // namespace base + Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/data_pack_unittest/sample.pak and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/data_pack_unittest/sample.pak differ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/binary_file.bin and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/binary_file.bin differ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/binary_file_diff.bin and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/binary_file_diff.bin differ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/binary_file_same.bin and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/binary_file_same.bin differ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/different_first.txt firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/different_first.txt --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/different_first.txt 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/different_first.txt 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1 @@ +this file is the same. diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/different_last.txt firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/different_last.txt --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/different_last.txt 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/different_last.txt 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1 @@ +This file is the same. \ No newline at end of file diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/different.txt firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/different.txt --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/different.txt 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/different.txt 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1 @@ +This file is different. diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/original.txt firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/original.txt --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/original.txt 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/original.txt 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1 @@ +This file is the same. diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/same_length.txt firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/same_length.txt --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/same_length.txt 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/same_length.txt 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1 @@ +This file is not same. diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/same.txt firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/same.txt --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/same.txt 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/same.txt 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1 @@ +This file is the same. diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/shortened.txt firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/shortened.txt --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/shortened.txt 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_util_unittest/shortened.txt 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1 @@ +This file is the \ No newline at end of file Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_version_info_unittest/FileVersionInfoTest1.dll and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_version_info_unittest/FileVersionInfoTest1.dll differ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/file_version_info_unittest/FileVersionInfoTest2.dll and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/file_version_info_unittest/FileVersionInfoTest2.dll differ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe.gtest.txt firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe.gtest.txt --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe.gtest.txt 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe.gtest.txt 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,30 @@ +# this test causes Purify to get completely confused, aborting the test and +# popping up 10 or more error dialogs +StatsTableTest.MultipleProcesses + +# see bug 1151158 +# causes purify to occasionally crash, possibly the same reason as 1110206 below +StatsTableTest.MultipleThreads + +# this test takes a really long time to run in Purify +TimeTicks.Rollover +TimeTicks.WinRollover + +# see bug 1110206 +ConditionVariableTest.LargeFastTaskTest + +# see bug 1150075 +MessageLoopTest.Crasher* + +# see bug 1195707 +WMIUtilTest.* + +# see issue 7412 +ScopedTempDir.* + +# see issue 7477 +ObserverListThreadSafeTest.CrossThreadObserver +ObserverListThreadSafeTest.CrossThreadNotifications + +# see bug 10031 +SignatureVerifierTest.BasicTest diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe_MLK_flakey.txt firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe_MLK_flakey.txt --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe_MLK_flakey.txt 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe_MLK_flakey.txt 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,18 @@ +CoTaskMemAlloc [OLE32.DLL] +Alloc Location + ... + base/wmi_util.cc WMIUtil::CreateLocalConnection(bool) + base/wmi_util.cc WMIProcessUtil::Launch(class std::basic_string const &,int *) + base/wmi_util_unittest.cc WMIUtilTest_TestLaunchProcess_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +base::ObjectWatcher::StartWatching(void *,Delegate::ObjectWatcher::base *) [base_unittests.exe] +Alloc Location + ... + base/object_watcher.cc base::ObjectWatcher::StartWatching(void *,Delegate::ObjectWatcher::base *) + base/directory_watcher_win.cc DirectoryWatcher::Impl::OnObjectSignaled(void *) + base/object_watcher.cc base::ObjectWatcher::Watch::Run(void) + base/message_loop.cc MessageLoop::RunTask(Task *) + ^^^ + diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe_MLK_ignore.txt firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe_MLK_ignore.txt --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe_MLK_ignore.txt 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe_MLK_ignore.txt 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,93 @@ +# ----- +# Leaks in ::RaiseException, called when we log a fatal error. See bug 1078612. + +std::strstreambuf::overflow(int) [base_unittests.exe] +Alloc Location + ... + base/check_handler_unittest.cc ThisFunctionAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::_Mutex::_Mutex(void) [base_unittests.exe] +Alloc Location + ... + base/logging.cc logging::LogMessage::LogMessage(char const*,int,int) + base/check_handler_unittest.cc ThisFunctionAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::basic_streambuf::std>::basic_streambuf::std>(void) [base_unittests.exe] +Alloc Location + ... + base/logging.cc logging::LogMessage::LogMessage(char const*,int,int) + base/check_handler_unittest.cc ThisFunctionAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::ios_base::_Init(void) [base_unittests.exe] +Alloc Location + ... + base/logging.cc logging::LogMessage::LogMessage(char const*,int,int) + base/check_handler_unittest.cc ThisFunctionAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::D::_Allocate(unsigned int,char *) [base_unittests.exe] +Alloc Location + ... + base/logging.cc logging::LogMessage::~LogMessage(void) + base/check_handler_unittest.cc ThisFunctionAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::strstreambuf::overflow(int) [base_unittests.exe] +Alloc Location + ... + base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::_Mutex::_Mutex(void) [base_unittests.exe] +Alloc Location + ... + base/logging.cc logging::LogMessage::LogMessage(char const*,int,int) + base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::basic_streambuf::std>::basic_streambuf::std>(void) [base_unittests.exe] +Alloc Location + ... + base/logging.cc logging::LogMessage::LogMessage(char const*,int,int) + base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::ios_base::_Init(void) [base_unittests.exe] +Alloc Location + ... + base/logging.cc logging::LogMessage::LogMessage(char const*,int,int) + base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::D::_Allocate(unsigned int,char *) [base_unittests.exe] +Alloc Location + ... + base/logging.cc logging::LogMessage::~LogMessage(void) + base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +# End of leaks in ::RaiseException +# ----- diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe_MLK.txt firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe_MLK.txt --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe_MLK.txt 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe_MLK.txt 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,97 @@ +std::D::_Allocate(unsigned int,char *) [base_unittests.exe] +Alloc Location + ... + base/check_handler_unittest.cc ThisFunctionAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::_Mutex::_Mutex(void) [base_unittests.exe] +Alloc Location + ... + base/logging.cc logging::LogMessage::LogMessage(char const*,int,int) + base/check_handler_unittest.cc ThisFunctionAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::basic_streambuf::std>::basic_streambuf::std>(void) [base_unittests.exe] +Alloc Location + ... + base/logging.cc logging::LogMessage::LogMessage(char const*,int,int) + base/check_handler_unittest.cc ThisFunctionAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::ios_base::_Init(void) [base_unittests.exe] +Alloc Location + ... + base/logging.cc logging::LogMessage::LogMessage(char const*,int,int) + base/check_handler_unittest.cc ThisFunctionAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::D::_Allocate(unsigned int,char *) [base_unittests.exe] +Alloc Location + ... + base/logging.cc logging::LogMessage::~LogMessage(void) + base/check_handler_unittest.cc ThisFunctionAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckFunc_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::D::_Allocate(unsigned int,char *) [base_unittests.exe] +Alloc Location + ... + base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::_Mutex::_Mutex(void) [base_unittests.exe] +Alloc Location + ... + base/logging.cc logging::LogMessage::LogMessage(char const*,int,int) + base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::basic_streambuf::std>::basic_streambuf::std>(void) [base_unittests.exe] +Alloc Location + ... + base/logging.cc logging::LogMessage::LogMessage(char const*,int,int) + base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::ios_base::_Init(void) [base_unittests.exe] +Alloc Location + ... + base/logging.cc logging::LogMessage::LogMessage(char const*,int,int) + base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +std::D::_Allocate(unsigned int,char *) [base_unittests.exe] +Alloc Location + ... + base/logging.cc logging::LogMessage::~LogMessage(void) + base/check_handler_unittest.cc SimpleTestClass::ThisMethodAsserts(void) + base/check_handler_unittest.cc CheckHandlerTest_TestMacroCheckObj_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + +CoTaskMemAlloc [OLE32.DLL] +Alloc Location + ... + base/wmi_util.cc WMIUtil::CreateLocalConnection(bool) + base/wmi_util.cc WMIProcessUtil::Launch(class std::basic_string const &,int *) + base/wmi_util_unittest.cc WMIUtilTest_TestLaunchProcess_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe_PAR_ignore.txt firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe_PAR_ignore.txt --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe_PAR_ignore.txt 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/purify/base_unittests.exe_PAR_ignore.txt 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,8 @@ +# Probably a Purify error. See bug 1076843. +WideCharToMultiByte: Invalid size (0x27) for destination buffer. +Error Location + ... + base/file_util_unittest.cc FileUtilTest_ResolveShortcutTest_Test::TestBody(void) + testing/gtest/src/gtest.cc testing::Test::Run(void) + ^^^ + diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/valgrind/base_unittests.gtest.txt firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/valgrind/base_unittests.gtest.txt --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data/valgrind/base_unittests.gtest.txt 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data/valgrind/base_unittests.gtest.txt 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,8 @@ +# This test currently times out in valgrind, see http://crbug.com/9194 +WatchdogTest.AlarmTest + +# These tests occassionally hangs under Valgrind on Mac. valgrind-darwin r9573 +# Revisit with better valgrind. +# Valgrind bug: https://bugs.kde.org/show_bug.cgi?id=189661 +TimerTest.RepeatingTimer +TimerTest.RepeatingTimer_Cancel diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data_pack.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data_pack.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data_pack.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data_pack.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,115 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/data_pack.h" + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/string_piece.h" + +// For details of the file layout, see +// http://dev.chromium.org/developers/design-documents/linuxresourcesandlocalizedstrings + +namespace { +static const uint32_t kFileFormatVersion = 1; +// Length of file header: version and entry count. +static const size_t kHeaderLength = 2 * sizeof(uint32_t); + +struct DataPackEntry { + uint32_t resource_id; + uint32_t file_offset; + uint32_t length; + + static int CompareById(const void* void_key, const void* void_entry) { + uint32_t key = *reinterpret_cast(void_key); + const DataPackEntry* entry = + reinterpret_cast(void_entry); + if (key < entry->resource_id) { + return -1; + } else if (key > entry->resource_id) { + return 1; + } else { + return 0; + } + } +} __attribute((packed)); + +} // anonymous namespace + +namespace base { + +// In .cc for MemoryMappedFile dtor. +DataPack::DataPack() : resource_count_(0) { +} +DataPack::~DataPack() { +} + +bool DataPack::Load(const FilePath& path) { + mmap_.reset(new file_util::MemoryMappedFile); + if (!mmap_->Initialize(path)) { + mmap_.reset(); + return false; + } + + // Parse the header of the file. + // First uint32_t: version; second: resource count. + const uint32* ptr = reinterpret_cast(mmap_->data()); + uint32 version = ptr[0]; + if (version != kFileFormatVersion) { + LOG(ERROR) << "Bad data pack version: got " << version << ", expected " + << kFileFormatVersion; + mmap_.reset(); + return false; + } + resource_count_ = ptr[1]; + + // Sanity check the file. + // 1) Check we have enough entries. + if (kHeaderLength + resource_count_ * sizeof(DataPackEntry) > + mmap_->length()) { + LOG(ERROR) << "Data pack file corruption: too short for number of " + "entries specified."; + mmap_.reset(); + return false; + } + // 2) Verify the entries are within the appropriate bounds. + for (size_t i = 0; i < resource_count_; ++i) { + const DataPackEntry* entry = reinterpret_cast( + mmap_->data() + kHeaderLength + (i * sizeof(DataPackEntry))); + if (entry->file_offset + entry->length > mmap_->length()) { + LOG(ERROR) << "Entry #" << i << " in data pack points off end of file. " + << "Was the file corrupted?"; + mmap_.reset(); + return false; + } + } + + return true; +} + +bool DataPack::Get(uint32_t resource_id, StringPiece* data) { + // It won't be hard to make this endian-agnostic, but it's not worth + // bothering to do right now. +#if defined(__BYTE_ORDER) + // Linux check + COMPILE_ASSERT(__BYTE_ORDER == __LITTLE_ENDIAN, + datapack_assumes_little_endian); +#elif defined(__BIG_ENDIAN__) + // Mac check + #error DataPack assumes little endian +#endif + + DataPackEntry* target = reinterpret_cast( + bsearch(&resource_id, mmap_->data() + kHeaderLength, resource_count_, + sizeof(DataPackEntry), DataPackEntry::CompareById)); + if (!target) { + LOG(ERROR) << "No resource found with id: " << resource_id; + return false; + } + + data->set(mmap_->data() + target->file_offset, target->length); + return true; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data_pack.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data_pack.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data_pack.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data_pack.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,48 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// DataPack represents a read-only view onto an on-disk file that contains +// (key, value) pairs of data. It's used to store static resources like +// translation strings and images. + +#ifndef BASE_DATA_PACK_H_ +#define BASE_DATA_PACK_H_ + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" + +namespace file_util { + class MemoryMappedFile; +} +class FilePath; +class StringPiece; + +namespace base { + +class DataPack { + public: + DataPack(); + ~DataPack(); + + // Load a pack file from |path|, returning false on error. + bool Load(const FilePath& path); + + // Get resource by id |resource_id|, filling in |data|. + // The data is owned by the DataPack object and should not be modified. + // Returns false if the resource id isn't found. + bool Get(uint32_t resource_id, StringPiece* data); + + private: + // The memory-mapped data. + scoped_ptr mmap_; + + // Number of resources in the data. + size_t resource_count_; + + DISALLOW_COPY_AND_ASSIGN(DataPack); +}; + +} // namespace base + +#endif // BASE_DATA_PACK_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data_pack_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data_pack_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/data_pack_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/data_pack_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,41 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/data_pack.h" + +#include "base/file_path.h" +#include "base/path_service.h" +#include "base/string_piece.h" +#include "testing/gtest/include/gtest/gtest.h" + +class DataPackTest : public testing::Test { + public: + DataPackTest() { + PathService::Get(base::DIR_SOURCE_ROOT, &data_path_); + data_path_ = data_path_.Append( + FILE_PATH_LITERAL("base/data/data_pack_unittest/sample.pak")); + } + + FilePath data_path_; +}; + +TEST_F(DataPackTest, Load) { + base::DataPack pack; + ASSERT_TRUE(pack.Load(data_path_)); + + StringPiece data; + ASSERT_TRUE(pack.Get(4, &data)); + EXPECT_EQ("this is id 4", data); + ASSERT_TRUE(pack.Get(6, &data)); + EXPECT_EQ("this is id 6", data); + + // Try reading zero-length data blobs, just in case. + ASSERT_TRUE(pack.Get(1, &data)); + EXPECT_EQ(0U, data.length()); + ASSERT_TRUE(pack.Get(10, &data)); + EXPECT_EQ(0U, data.length()); + + // Try looking up an invalid key. + ASSERT_FALSE(pack.Get(140, &data)); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_message.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_message.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_message.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_message.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,17 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +// Display the command line. This program is designed to be called from +// another process to display assertions. Since the other process has +// complete control of our command line, we assume that it did *not* +// add the program name as the first parameter. This allows us to just +// show the command line directly as the message. +int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, + LPSTR lpCmdLine, int nCmdShow) { + LPWSTR cmdline = GetCommandLineW(); + MessageBox(NULL, cmdline, L"Kr\x00d8m", MB_TOPMOST); + return 0; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_on_start.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_on_start.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_on_start.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_on_start.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,65 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/debug_on_start.h" + +#include "base/base_switches.h" +#include "base/basictypes.h" +#include "base/debug_util.h" + +// Minimalist implementation to try to find a command line argument. We can use +// kernel32 exported functions but not the CRT functions because we're too early +// in the process startup. +// The code is not that bright and will find things like ---argument or +// /-/argument. +// Note: command_line is non-destructively modified. +bool DebugOnStart::FindArgument(wchar_t* command_line, const wchar_t* argument) +{ + int argument_len = lstrlen(argument); + int command_line_len = lstrlen(command_line); + while (command_line_len > argument_len) { + wchar_t first_char = command_line[0]; + wchar_t last_char = command_line[argument_len+1]; + // Try to find an argument. + if ((first_char == L'-' || first_char == L'/') && + (last_char == L' ' || last_char == 0 || last_char == L'=')) { + command_line[argument_len+1] = 0; + // Skip the - or / + if (lstrcmpi(command_line+1, argument) == 0) { + // Found it. + command_line[argument_len+1] = last_char; + return true; + } + // Fix back. + command_line[argument_len+1] = last_char; + } + // Continue searching. + ++command_line; + --command_line_len; + } + return false; +} + +// static +int __cdecl DebugOnStart::Init() { + // Try to find the argument. + if (FindArgument(GetCommandLine(), switches::kDebugOnStart)) { + // We can do 2 things here: + // - Ask for a debugger to attach to us. This involve reading the registry + // key and creating the process. + // - Do a int3. + + // It will fails if we run in a sandbox. That is expected. + DebugUtil::SpawnDebuggerOnProcess(GetCurrentProcessId()); + + // Wait for a debugger to come take us. + DebugUtil::WaitForDebugger(60, false); + } else if (FindArgument(GetCommandLine(), switches::kWaitForDebugger)) { + // Wait for a debugger to come take us. + DebugUtil::WaitForDebugger(60, true); + } + return 0; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_on_start.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_on_start.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_on_start.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_on_start.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,67 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Define the necessary code and global data to look for kDebugOnStart command +// line argument. When the command line argument is detected, it invokes the +// debugger, if no system-wide debugger is registered, a debug break is done. + +#ifndef BASE_DEBUG_ON_START_H_ +#define BASE_DEBUG_ON_START_H_ + +#include "base/basictypes.h" + +// This only works on Windows. +#if defined(OS_WIN) + +#ifndef DECLSPEC_SELECTANY +#define DECLSPEC_SELECTANY __declspec(selectany) +#endif + +// Debug on start functions and data. +class DebugOnStart { + public: + // Expected function type in the .CRT$XI* section. + // Note: See VC\crt\src\internal.h for reference. + typedef int (__cdecl *PIFV)(void); + + // Looks at the command line for kDebugOnStart argument. If found, it invokes + // the debugger, if this fails, it crashes. + static int __cdecl Init(); + + // Returns true if the 'argument' is present in the 'command_line'. It does + // not use the CRT, only Kernel32 functions. + static bool FindArgument(wchar_t* command_line, const wchar_t* argument); +}; + +// Set the function pointer to our function to look for a crash on start. The +// XIB section is started pretty early in the program initialization so in +// theory it should be called before any user created global variable +// initialization code and CRT initialization code. +// Note: See VC\crt\src\defsects.inc and VC\crt\src\crt0.c for reference. +#ifdef _WIN64 + +// "Fix" the segment. On x64, the .CRT segment is merged into the .rdata segment +// so it contains const data only. +#pragma const_seg(push, ".CRT$XIB") +// Declare the pointer so the CRT will find it. +extern const DebugOnStart::PIFV debug_on_start; +DECLSPEC_SELECTANY const DebugOnStart::PIFV debug_on_start = + &DebugOnStart::Init; +// Fix back the segment. +#pragma const_seg(pop) + +#else // _WIN64 + +// "Fix" the segment. On x86, the .CRT segment is merged into the .data segment +// so it contains non-const data only. +#pragma data_seg(push, ".CRT$XIB") +// Declare the pointer so the CRT will find it. +DECLSPEC_SELECTANY DebugOnStart::PIFV debug_on_start = &DebugOnStart::Init; +// Fix back the segment. +#pragma data_seg(pop) + +#endif // _WIN64 +#endif // defined(OS_WIN) + +#endif // BASE_DEBUG_ON_START_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_util.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_util.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_util.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_util.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,77 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/debug_util.h" + +#include "base/platform_thread.h" + +#include +#include +#include + +#ifdef OS_WIN +#include +#else +#include +#endif + +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif + +bool DebugUtil::WaitForDebugger(int wait_seconds, bool silent) { + for (int i = 0; i < wait_seconds * 10; ++i) { + if (BeingDebugged()) { + if (!silent) + BreakDebugger(); + return true; + } + PlatformThread::Sleep(100); + } + return false; +} + +const void *const *StackTrace::Addresses(size_t* count) { + *count = trace_.size(); + if (trace_.size()) + return &trace_[0]; + return NULL; +} + +namespace mozilla { + +EnvironmentLog::EnvironmentLog(const char* varname) +{ + const char *e = getenv(varname); + if (e && *e) + fname_ = e; +} + +EnvironmentLog::~EnvironmentLog() +{ +} + +void +EnvironmentLog::print(const char* format, ...) +{ + if (!fname_.size()) + return; + + FILE* f; + if (fname_.compare("-") == 0) + f = fdopen(dup(STDOUT_FILENO), "a"); + else + f = fopen(fname_.c_str(), "a"); + + if (!f) + return; + + va_list a; + va_start(a, format); + vfprintf(f, format, a); + va_end(a); + fclose(f); +} + +} // namespace mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_util.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,88 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This is a cross platform interface for helper functions related to debuggers. +// You should use this to test if you're running under a debugger, and if you +// would like to yield (breakpoint) into the debugger. + +#ifndef BASE_DEBUG_UTIL_H_ +#define BASE_DEBUG_UTIL_H_ + +#include +#include + +#include "base/basictypes.h" + +// A stacktrace can be helpful in debugging. For example, you can include a +// stacktrace member in a object (probably around #ifndef NDEBUG) so that you +// can later see where the given object was created from. +class StackTrace { + public: + // Create a stacktrace from the current location + StackTrace(); + // Get an array of instruction pointer values. + // count: (output) the number of elements in the returned array + const void *const *Addresses(size_t* count); + // Print a backtrace to stderr + void PrintBacktrace(); + + // Resolve backtrace to symbols and write to stream. + void OutputToStream(std::ostream* os); + + private: + std::vector trace_; + + DISALLOW_EVIL_CONSTRUCTORS(StackTrace); +}; + +class DebugUtil { + public: + // Starts the registered system-wide JIT debugger to attach it to specified + // process. + static bool SpawnDebuggerOnProcess(unsigned process_id); + + // Waits wait_seconds seconds for a debugger to attach to the current process. + // When silent is false, an exception is thrown when a debugger is detected. + static bool WaitForDebugger(int wait_seconds, bool silent); + + // Are we running under a debugger? + // On OS X, the underlying mechanism doesn't work when the sandbox is enabled. + // To get around this, this function caches its value. + // WARNING: Because of this, on OS X, a call MUST be made to this function + // BEFORE the sandbox is enabled. + static bool BeingDebugged(); + + // Break into the debugger, assumes a debugger is present. + static void BreakDebugger(); + +#if defined(OS_MACOSX) + // On OS X, it can take a really long time for the OS Crash handler to + // process a Chrome crash. This translates into a long wait till the process + // actually dies. + // This method disables OS Crash reporting entireley. + // TODO(playmobil): Remove this when we have Breakpad integration enabled - + // see http://crbug.com/7652 + static void DisableOSCrashDumps(); +#endif // defined(OS_MACOSX) +}; + +namespace mozilla { + +class EnvironmentLog +{ +public: + EnvironmentLog(const char* varname); + ~EnvironmentLog(); + + void print(const char* format, ...); + +private: + std::string fname_; + + DISALLOW_EVIL_CONSTRUCTORS(EnvironmentLog); +}; + +} // namespace mozilla + +#endif // BASE_DEBUG_UTIL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_util_mac.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_util_mac.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_util_mac.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_util_mac.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,35 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/debug_util.h" + +#include + +#include "base/basictypes.h" + +static void ExitSignalHandler(int sig) { + exit(128 + sig); +} + +// static +void DebugUtil::DisableOSCrashDumps() { + int signals_to_intercept[] ={SIGINT, + SIGHUP, + SIGTERM, + SIGABRT, + SIGILL, + SIGTRAP, + SIGEMT, + SIGFPE, + SIGBUS, + SIGSEGV, + SIGSYS, + SIGPIPE, + SIGXCPU, + SIGXFSZ}; + // For all these signals, just wire thing sup so we exit immediately. + for (size_t i = 0; i < arraysize(signals_to_intercept); ++i) { + signal(signals_to_intercept[i], ExitSignalHandler); + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_util_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_util_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_util_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_util_posix.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,160 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "build/build_config.h" +#include "base/debug_util.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "base/basictypes.h" +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/string_piece.h" + +// static +bool DebugUtil::SpawnDebuggerOnProcess(unsigned /* process_id */) { + NOTIMPLEMENTED(); + return false; +} + +#if defined(OS_MACOSX) + +// Based on Apple's recommended method as described in +// http://developer.apple.com/qa/qa2004/qa1361.html +// static +bool DebugUtil::BeingDebugged() { + // If the process is sandboxed then we can't use the sysctl, so cache the + // value. + static bool is_set = false; + static bool being_debugged = false; + + if (is_set) { + return being_debugged; + } + + // Initialize mib, which tells sysctl what info we want. In this case, + // we're looking for information about a specific process ID. + int mib[] = { + CTL_KERN, + KERN_PROC, + KERN_PROC_PID, + getpid() + }; + + // Caution: struct kinfo_proc is marked __APPLE_API_UNSTABLE. The source and + // binary interfaces may change. + struct kinfo_proc info; + size_t info_size = sizeof(info); + + int sysctl_result = sysctl(mib, arraysize(mib), &info, &info_size, NULL, 0); + DCHECK(sysctl_result == 0); + if (sysctl_result != 0) { + is_set = true; + being_debugged = false; + return being_debugged; + } + + // This process is being debugged if the P_TRACED flag is set. + is_set = true; + being_debugged = (info.kp_proc.p_flag & P_TRACED) != 0; + return being_debugged; +} + +#elif defined(OS_LINUX) + +// We can look in /proc/self/status for TracerPid. We are likely used in crash +// handling, so we are careful not to use the heap or have side effects. +// Another option that is common is to try to ptrace yourself, but then we +// can't detach without forking(), and that's not so great. +// static +bool DebugUtil::BeingDebugged() { + int status_fd = open("/proc/self/status", O_RDONLY); + if (status_fd == -1) + return false; + + // We assume our line will be in the first 1024 characters and that we can + // read this much all at once. In practice this will generally be true. + // This simplifies and speeds up things considerably. + char buf[1024]; + + ssize_t num_read = HANDLE_EINTR(read(status_fd, buf, sizeof(buf))); + HANDLE_EINTR(close(status_fd)); + + if (num_read <= 0) + return false; + + StringPiece status(buf, num_read); + StringPiece tracer("TracerPid:\t"); + + StringPiece::size_type pid_index = status.find(tracer); + if (pid_index == StringPiece::npos) + return false; + + // Our pid is 0 without a debugger, assume this for any pid starting with 0. + pid_index += tracer.size(); + return pid_index < status.size() && status[pid_index] != '0'; +} + +#endif // OS_LINUX + +// static +void DebugUtil::BreakDebugger() { +#if !defined(ARCH_CPU_ARM_FAMILY) + asm ("int3"); +#endif +} + +StackTrace::StackTrace() { + const int kMaxCallers = 256; + + void* callers[kMaxCallers]; + int count = backtrace(callers, kMaxCallers); + + // Though the backtrace API man page does not list any possible negative + // return values, we still still exclude them because they would break the + // memcpy code below. + if (count > 0) { + trace_.resize(count); + memcpy(&trace_[0], callers, sizeof(callers[0]) * count); + } else { + trace_.resize(0); + } +} + +void StackTrace::PrintBacktrace() { + fflush(stderr); + backtrace_symbols_fd(&trace_[0], trace_.size(), STDERR_FILENO); +} + +void StackTrace::OutputToStream(std::ostream* os) { +#ifdef CHROMIUM_MOZILLA_BUILD + return; +#else + scoped_ptr_malloc trace_symbols( + backtrace_symbols(&trace_[0], trace_.size())); + + // If we can't retrieve the symbols, print an error and just dump the raw + // addresses. + if (trace_symbols.get() == NULL) { + (*os) << "Unable get symbols for backtrace (" << strerror(errno) + << "). Dumping raw addresses in trace:\n"; + for (size_t i = 0; i < trace_.size(); ++i) { + (*os) << "\t" << trace_[i] << "\n"; + } + } else { + (*os) << "Backtrace:\n"; + for (size_t i = 0; i < trace_.size(); ++i) { + (*os) << "\t" << trace_symbols.get()[i] << "\n"; + } + } +#endif +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_util_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_util_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_util_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_util_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,77 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "base/debug_util.h" +#include "base/logging.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(StackTrace, OutputToStream) { + StackTrace trace; + + // Dump the trace into a string. + std::ostringstream os; + trace.OutputToStream(&os); + std::string backtrace_message = os.str(); + + size_t frames_found = 0; + trace.Addresses(&frames_found); + if (frames_found == 0) { + LOG(ERROR) << "No stack frames found. Skipping rest of test."; + return; + } + + // Check if the output has symbol initialization warning. If it does, fail. + if (backtrace_message.find("Dumping unresolved backtrace") != + std::string::npos) { + LOG(ERROR) << "Unable to resolve symbols. Skipping rest of test."; + return; + } + +#if 0 +//TODO(ajwong): Disabling checking of symbol resolution since it depends +// on whether or not symbols are present, and there are too many +// configurations to reliably ensure that symbols are findable. +#if defined(OS_MACOSX) + + // Symbol resolution via the backtrace_symbol funciton does not work well + // in OsX. + // See this thread: + // + // http://lists.apple.com/archives/darwin-dev/2009/Mar/msg00111.html + // + // Just check instead that we find our way back to the "start" symbol + // which should be the first symbol in the trace. + // + // TODO(port): Find a more reliable way to resolve symbols. + + // Expect to at least find main. + EXPECT_TRUE(backtrace_message.find("start") != std::string::npos) + << "Expected to find start in backtrace:\n" + << backtrace_message; + +#else // defined(OS_MACOSX) + + // Expect to at least find main. + EXPECT_TRUE(backtrace_message.find("main") != std::string::npos) + << "Expected to find main in backtrace:\n" + << backtrace_message; + +#if defined(OS_WIN) +// MSVC doesn't allow the use of C99's __func__ within C++, so we fake it with +// MSVC's __FUNCTION__ macro. +#define __func__ __FUNCTION__ +#endif + + // Expect to find this function as well. + // Note: This will fail if not linked with -rdynamic (aka -export_dynamic) + EXPECT_TRUE(backtrace_message.find(__func__) != std::string::npos) + << "Expected to find " << __func__ << " in backtrace:\n" + << backtrace_message; + +#endif // define(OS_MACOSX) +#endif +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_util_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_util_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/debug_util_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/debug_util_win.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,289 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/debug_util.h" + +#include +#include + +#include "base/basictypes.h" +#include "base/lock.h" +#include "base/logging.h" +#include "base/singleton.h" + +namespace { + +// Minimalist key reader. +// Note: Does not use the CRT. +bool RegReadString(HKEY root, const wchar_t* subkey, + const wchar_t* value_name, wchar_t* buffer, int* len) { + HKEY key = NULL; + DWORD res = RegOpenKeyEx(root, subkey, 0, KEY_READ, &key); + if (ERROR_SUCCESS != res || key == NULL) + return false; + + DWORD type = 0; + DWORD buffer_size = *len * sizeof(wchar_t); + // We don't support REG_EXPAND_SZ. + res = RegQueryValueEx(key, value_name, NULL, &type, + reinterpret_cast(buffer), &buffer_size); + if (ERROR_SUCCESS == res && buffer_size != 0 && type == REG_SZ) { + // Make sure the buffer is NULL terminated. + buffer[*len - 1] = 0; + *len = lstrlen(buffer); + RegCloseKey(key); + return true; + } + RegCloseKey(key); + return false; +} + +// Replaces each "%ld" in input per a value. Not efficient but it works. +// Note: Does not use the CRT. +bool StringReplace(const wchar_t* input, int value, wchar_t* output, + int output_len) { + memset(output, 0, output_len*sizeof(wchar_t)); + int input_len = lstrlen(input); + + for (int i = 0; i < input_len; ++i) { + int current_output_len = lstrlen(output); + + if (input[i] == L'%' && input[i + 1] == L'l' && input[i + 2] == L'd') { + // Make sure we have enough place left. + if ((current_output_len + 12) >= output_len) + return false; + + // Cheap _itow(). + wsprintf(output+current_output_len, L"%d", value); + i += 2; + } else { + if (current_output_len >= output_len) + return false; + output[current_output_len] = input[i]; + } + } + return true; +} + +// SymbolContext is a threadsafe singleton that wraps the DbgHelp Sym* family +// of functions. The Sym* family of functions may only be invoked by one +// thread at a time. SymbolContext code may access a symbol server over the +// network while holding the lock for this singleton. In the case of high +// latency, this code will adversly affect performance. +// +// There is also a known issue where this backtrace code can interact +// badly with breakpad if breakpad is invoked in a separate thread while +// we are using the Sym* functions. This is because breakpad does now +// share a lock with this function. See this related bug: +// +// http://code.google.com/p/google-breakpad/issues/detail?id=311 +// +// This is a very unlikely edge case, and the current solution is to +// just ignore it. +class SymbolContext { + public: + static SymbolContext* Get() { + // We use a leaky singleton because code may call this during process + // termination. + return + Singleton >::get(); + } + + // Initializes the symbols for the process if it hasn't been done yet. + // Subsequent calls will not reinitialize the symbol, but instead return + // the error code from the first call. + bool Init() { + AutoLock lock(lock_); + if (!initialized_) { + process_ = GetCurrentProcess(); + + // Defer symbol load until they're needed, use undecorated names, and + // get line numbers. + SymSetOptions(SYMOPT_DEFERRED_LOADS | + SYMOPT_UNDNAME | + SYMOPT_LOAD_LINES); + if (SymInitialize(process_, NULL, TRUE)) { + init_error_ = ERROR_SUCCESS; + } else { + init_error_ = GetLastError(); + } + } + + initialized_ = true; + return init_error_ == ERROR_SUCCESS; + } + + // Returns the error code of a failed initialization. This should only be + // called if Init() has been called. We do not LOG(FATAL) here because + // this code is called might be triggered by a LOG(FATAL) itself. Instead, + // we log an ERROR, and return ERROR_INVALID_DATA. + DWORD init_error() { + if (!initialized_) { + LOG(ERROR) << "Calling GetInitError() before Init() was called. " + << "Returning ERROR_INVALID_DATA."; + return ERROR_INVALID_DATA; + } + + return init_error_; + } + + // Returns the process this was initialized for. This should only be + // called if Init() has been called. We LOG(ERROR) in this situation. + // LOG(FATAL) is not used because this code is might be triggered + // by a LOG(FATAL) itself. + HANDLE process() { + if (!initialized_) { + LOG(ERROR) << "Calling process() before Init() was called. " + << "Returning NULL."; + return NULL; + } + + return process_; + } + + // For the given trace, attempts to resolve the symbols, and output a trace + // to the ostream os. The format for each line of the backtrace is: + // + // SymbolName[0xAddress+Offset] (FileName:LineNo) + // + // This function should only be called if Init() has been called. We do not + // LOG(FATAL) here because this code is called might be triggered by a + // LOG(FATAL) itself. + void OutputTraceToStream(const std::vector& trace, std::ostream* os) { + AutoLock lock(lock_); + + for (size_t i = 0; (i < trace.size()) && os->good(); ++i) { + const int kMaxNameLength = 256; + DWORD_PTR frame = reinterpret_cast(trace[i]); + + // Code adapted from MSDN example: + // http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx + ULONG64 buffer[ + (sizeof(SYMBOL_INFO) + + kMaxNameLength * sizeof(wchar_t) + + sizeof(ULONG64) - 1) / + sizeof(ULONG64)]; + + // Initialize symbol information retrieval structures. + DWORD64 sym_displacement = 0; + PSYMBOL_INFO symbol = reinterpret_cast(&buffer[0]); + symbol->SizeOfStruct = sizeof(SYMBOL_INFO); + symbol->MaxNameLen = kMaxNameLength; + BOOL has_symbol = SymFromAddr(process(), frame, + &sym_displacement, symbol); + + // Attempt to retrieve line number information. + DWORD line_displacement = 0; + IMAGEHLP_LINE64 line = {}; + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + BOOL has_line = SymGetLineFromAddr64(process(), frame, + &line_displacement, &line); + + // Output the backtrace line. + (*os) << "\t"; + if (has_symbol) { + (*os) << symbol->Name << " [0x" << trace[i] << "+" + << sym_displacement << "]"; + } else { + // If there is no symbol informtion, add a spacer. + (*os) << "(No symbol) [0x" << trace[i] << "]"; + } + if (has_line) { + (*os) << " (" << line.FileName << ":" << line.LineNumber << ")"; + } + (*os) << "\n"; + } + } + + SymbolContext() + : initialized_(false), + process_(NULL), + init_error_(ERROR_SUCCESS) { + } + + private: + Lock lock_; + bool initialized_; + HANDLE process_; + DWORD init_error_; + + DISALLOW_COPY_AND_ASSIGN(SymbolContext); +}; + +} // namespace + +// Note: Does not use the CRT. +bool DebugUtil::SpawnDebuggerOnProcess(unsigned process_id) { + wchar_t reg_value[1026]; + int len = arraysize(reg_value); + if (RegReadString(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug", + L"Debugger", reg_value, &len)) { + wchar_t command_line[1026]; + if (StringReplace(reg_value, process_id, command_line, + arraysize(command_line))) { + // We don't mind if the debugger is present because it will simply fail + // to attach to this process. + STARTUPINFO startup_info = {0}; + startup_info.cb = sizeof(startup_info); + PROCESS_INFORMATION process_info = {0}; + + if (CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL, + &startup_info, &process_info)) { + CloseHandle(process_info.hThread); + WaitForInputIdle(process_info.hProcess, 10000); + CloseHandle(process_info.hProcess); + return true; + } + } + } + return false; +} + +// static +bool DebugUtil::BeingDebugged() { + return ::IsDebuggerPresent() != 0; +} + +// static +void DebugUtil::BreakDebugger() { + __debugbreak(); +} + +StackTrace::StackTrace() { + // From http://msdn.microsoft.com/en-us/library/bb204633(VS.85).aspx, + // the sum of FramesToSkip and FramesToCapture must be less than 63, + // so set it to 62. + const int kMaxCallers = 62; + + void* callers[kMaxCallers]; + // TODO(ajwong): Migrate this to StackWalk64. + int count = CaptureStackBackTrace(0, kMaxCallers, callers, NULL); + if (count > 0) { + trace_.resize(count); + memcpy(&trace_[0], callers, sizeof(callers[0]) * count); + } else { + trace_.resize(0); + } +} + +void StackTrace::PrintBacktrace() { + OutputToStream(&std::cerr); +} + +void StackTrace::OutputToStream(std::ostream* os) { + SymbolContext* context = SymbolContext::Get(); + + if (context->Init() != ERROR_SUCCESS) { + DWORD error = context->init_error(); + (*os) << "Error initializing symbols (" << error + << "). Dumping unresolved backtrace:\n"; + for (size_t i = 0; (i < trace_.size()) && os->good(); ++i) { + (*os) << "\t" << trace_[i] << "\n"; + } + } else { + (*os) << "Backtrace:\n"; + context->OutputTraceToStream(trace_, os); + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/DEPS firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/DEPS --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/DEPS 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/DEPS 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,6 @@ +include_rules = [ + "+third_party/zlib", + "+third_party/libevent", + "+third_party/libjpeg", + "+third_party/dmg_fp", +] diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,55 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This module provides a way to monitor a directory for changes. + +#ifndef BASE_DIRECTORY_WATCHER_H_ +#define BASE_DIRECTORY_WATCHER_H_ + +#include "base/basictypes.h" +#include "base/ref_counted.h" + +class FilePath; + +// This class lets you register interest in changes on a directory. +// The delegate will get called whenever a file is added or changed in the +// directory. +class DirectoryWatcher { + public: + class Delegate { + public: + virtual void OnDirectoryChanged(const FilePath& path) = 0; + }; + + DirectoryWatcher(); + ~DirectoryWatcher() {} + + // Register interest in any changes in the directory |path|. + // OnDirectoryChanged will be called back for each change within the dir. + // If |recursive| is true, the delegate will be notified for each change + // within the directory tree starting at |path|. Returns false on error. + // + // Note: on Windows you may got more notifications for non-recursive watch + // than you expect, especially on versions earlier than Vista. The behavior + // is consistent on any particular version of Windows, but not across + // different versions. + bool Watch(const FilePath& path, Delegate* delegate, bool recursive) { + return impl_->Watch(path, delegate, recursive); + } + + // Used internally to encapsulate different members on different platforms. + class PlatformDelegate : public base::RefCounted { + public: + virtual ~PlatformDelegate() {} + virtual bool Watch(const FilePath& path, Delegate* delegate, + bool recursive) = 0; + }; + + private: + scoped_refptr impl_; + + DISALLOW_COPY_AND_ASSIGN(DirectoryWatcher); +}; + +#endif // BASE_DIRECTORY_WATCHER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_inotify.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_inotify.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_inotify.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_inotify.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,321 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/directory_watcher.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "base/eintr_wrapper.h" +#include "base/file_path.h" +#include "base/hash_tables.h" +#include "base/lock.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/scoped_ptr.h" +#include "base/singleton.h" +#include "base/task.h" +#include "base/thread.h" + +namespace { + +// Singleton to manage all inotify watches. +class InotifyReader { + public: + typedef int Watch; // Watch descriptor used by AddWatch and RemoveWatch. + static const Watch kInvalidWatch = -1; + + // Watch |path| for changes. |delegate| will be notified on each change. Does + // not check for duplicates. If you call it n times with same |path| + // and |delegate|, it will receive n notifications for each change + // in |path|. It makes implementation of DirectoryWatcher simple. + // Returns kInvalidWatch on failure. + Watch AddWatch(const FilePath& path, DirectoryWatcher::Delegate* delegate); + + // Remove |watch| for |delegate|. If you had n watches for same |delegate| + // and path, after calling this function you will have n - 1. + // Returns true on success. + bool RemoveWatch(Watch watch, DirectoryWatcher::Delegate* delegate); + + // Callback for InotifyReaderTask. + void OnInotifyEvent(inotify_event* event); + + private: + friend struct DefaultSingletonTraits; + + typedef std::pair DelegateInfo; + typedef std::multiset DelegateSet; + + InotifyReader(); + ~InotifyReader(); + + // We keep track of which delegates want to be notified on which watches. + // Multiset is used because there may be many DirectoryWatchers for same path + // and delegate. + base::hash_map delegates_; + + // For each watch we also want to know the path it's watching. + base::hash_map paths_; + + // Lock to protect delegates_ and paths_. + Lock lock_; + + // Separate thread on which we run blocking read for inotify events. + base::Thread thread_; + + // File descriptor returned by inotify_init. + const int inotify_fd_; + + // Use self-pipe trick to unblock select during shutdown. + int shutdown_pipe_[2]; + + // Flag set to true when startup was successful. + bool valid_; + + DISALLOW_COPY_AND_ASSIGN(InotifyReader); +}; + +class InotifyReaderTask : public Task { + public: + InotifyReaderTask(InotifyReader* reader, int inotify_fd, int shutdown_fd) + : reader_(reader), + inotify_fd_(inotify_fd), + shutdown_fd_(shutdown_fd) { + } + + virtual void Run() { + while (true) { + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(inotify_fd_, &rfds); + FD_SET(shutdown_fd_, &rfds); + + // Wait until some inotify events are available. + int select_result = + HANDLE_EINTR(select(std::max(inotify_fd_, shutdown_fd_) + 1, + &rfds, NULL, NULL, NULL)); + if (select_result < 0) { + DLOG(WARNING) << "select failed: " << strerror(errno); + return; + } + + if (FD_ISSET(shutdown_fd_, &rfds)) + return; + + // Adjust buffer size to current event queue size. + int buffer_size; + int ioctl_result = HANDLE_EINTR(ioctl(inotify_fd_, FIONREAD, + &buffer_size)); + + if (ioctl_result != 0) { + DLOG(WARNING) << "ioctl failed: " << strerror(errno); + return; + } + + std::vector buffer(buffer_size); + + ssize_t bytes_read = HANDLE_EINTR(read(inotify_fd_, &buffer[0], + buffer_size)); + + if (bytes_read < 0) { + DLOG(WARNING) << "read from inotify fd failed: " << strerror(errno); + return; + } + + ssize_t i = 0; + while (i < bytes_read) { + inotify_event* event = reinterpret_cast(&buffer[i]); + size_t event_size = sizeof(inotify_event) + event->len; + DCHECK(i + event_size <= static_cast(bytes_read)); + reader_->OnInotifyEvent(event); + i += event_size; + } + } + } + + private: + InotifyReader* reader_; + int inotify_fd_; + int shutdown_fd_; + + DISALLOW_COPY_AND_ASSIGN(InotifyReaderTask); +}; + +class InotifyReaderNotifyTask : public Task { + public: + InotifyReaderNotifyTask(DirectoryWatcher::Delegate* delegate, + const FilePath& path) + : delegate_(delegate), + path_(path) { + } + + virtual void Run() { + delegate_->OnDirectoryChanged(path_); + } + + private: + DirectoryWatcher::Delegate* delegate_; + FilePath path_; + + DISALLOW_COPY_AND_ASSIGN(InotifyReaderNotifyTask); +}; + +InotifyReader::InotifyReader() + : thread_("inotify_reader"), + inotify_fd_(inotify_init()), + valid_(false) { + shutdown_pipe_[0] = -1; + shutdown_pipe_[1] = -1; + if (inotify_fd_ >= 0 && pipe(shutdown_pipe_) == 0 && thread_.Start()) { + thread_.message_loop()->PostTask( + FROM_HERE, new InotifyReaderTask(this, inotify_fd_, shutdown_pipe_[0])); + valid_ = true; + } +} + +InotifyReader::~InotifyReader() { + if (valid_) { + // Write to the self-pipe so that the select call in InotifyReaderTask + // returns. + HANDLE_EINTR(write(shutdown_pipe_[1], "", 1)); + thread_.Stop(); + } + if (inotify_fd_ >= 0) + close(inotify_fd_); + if (shutdown_pipe_[0] >= 0) + close(shutdown_pipe_[0]); + if (shutdown_pipe_[1] >= 0) + close(shutdown_pipe_[1]); +} + +InotifyReader::Watch InotifyReader::AddWatch( + const FilePath& path, DirectoryWatcher::Delegate* delegate) { + if (!valid_) + return kInvalidWatch; + + AutoLock auto_lock(lock_); + + Watch watch = inotify_add_watch(inotify_fd_, path.value().c_str(), + IN_CREATE | IN_DELETE | + IN_CLOSE_WRITE | IN_MOVE); + if (watch == kInvalidWatch) + return kInvalidWatch; + + if (paths_[watch].empty()) + paths_[watch] = path; // We don't yet watch this path. + + delegates_[watch].insert(std::make_pair(delegate, MessageLoop::current())); + + return watch; +} + +bool InotifyReader::RemoveWatch(Watch watch, + DirectoryWatcher::Delegate* delegate) { + if (!valid_) + return false; + + AutoLock auto_lock(lock_); + + if (paths_[watch].empty()) + return false; // We don't recognize this watch. + + // Only erase one occurrence of delegate (there may be more). + delegates_[watch].erase( + delegates_[watch].find(std::make_pair(delegate, MessageLoop::current()))); + + if (delegates_[watch].empty()) { + paths_.erase(watch); + delegates_.erase(watch); + + return (inotify_rm_watch(inotify_fd_, watch) == 0); + } + + return true; +} + +void InotifyReader::OnInotifyEvent(inotify_event* event) { + if (event->mask & IN_IGNORED) + return; + + DelegateSet delegates_to_notify; + FilePath changed_path; + + { + AutoLock auto_lock(lock_); + changed_path = paths_[event->wd]; + delegates_to_notify.insert(delegates_[event->wd].begin(), + delegates_[event->wd].end()); + } + + DelegateSet::iterator i; + for (i = delegates_to_notify.begin(); i != delegates_to_notify.end(); ++i) { + DirectoryWatcher::Delegate* delegate = i->first; + MessageLoop* loop = i->second; + loop->PostTask(FROM_HERE, + new InotifyReaderNotifyTask(delegate, changed_path)); + } +} + +class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate { + public: + DirectoryWatcherImpl() : watch_(InotifyReader::kInvalidWatch) {} + ~DirectoryWatcherImpl(); + + virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate, + bool recursive); + + private: + // Delegate to notify upon changes. + DirectoryWatcher::Delegate* delegate_; + + // Path we're watching (passed to delegate). + FilePath path_; + + // Watch returned by InotifyReader. + InotifyReader::Watch watch_; + + DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImpl); +}; + +DirectoryWatcherImpl::~DirectoryWatcherImpl() { + if (watch_ != InotifyReader::kInvalidWatch) + Singleton::get()->RemoveWatch(watch_, delegate_); +} + +bool DirectoryWatcherImpl::Watch(const FilePath& path, + DirectoryWatcher::Delegate* delegate, bool recursive) { + DCHECK(watch_ == InotifyReader::kInvalidWatch); // Can only watch one path. + + if (recursive) { + // TODO(phajdan.jr): Support recursive watches. + // Unfortunately inotify has no "native" support for them, but it can be + // emulated by walking the directory tree and setting up watches for each + // directory. Of course this is ineffective for large directory trees. + // For the algorithm, see the link below: + // http://osdir.com/ml/gnome.dashboard.devel/2004-10/msg00022.html + NOTIMPLEMENTED(); + return false; + } + + delegate_ = delegate; + path_ = path; + watch_ = Singleton::get()->AddWatch(path, delegate_); + + return watch_ != InotifyReader::kInvalidWatch; +} + +} // namespace + +DirectoryWatcher::DirectoryWatcher() { + impl_ = new DirectoryWatcherImpl(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_mac.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_mac.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_mac.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_mac.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,121 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/directory_watcher.h" + +#include + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/scoped_cftyperef.h" + +namespace { + +const CFAbsoluteTime kEventLatencySeconds = 0.3; + +class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate { + public: + DirectoryWatcherImpl() {} + ~DirectoryWatcherImpl() { + if (!path_.value().empty()) { + FSEventStreamStop(fsevent_stream_); + FSEventStreamInvalidate(fsevent_stream_); + FSEventStreamRelease(fsevent_stream_); + } + } + + virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate, + bool recursive); + + void OnFSEventsCallback(const FilePath& event_path) { + DCHECK(!path_.value().empty()); + if (!recursive_) { + FilePath absolute_event_path = event_path; + if (!file_util::AbsolutePath(&absolute_event_path)) + return; + if (absolute_event_path != path_) + return; + } + delegate_->OnDirectoryChanged(path_); + } + + private: + // Delegate to notify upon changes. + DirectoryWatcher::Delegate* delegate_; + + // Path we're watching (passed to delegate). + FilePath path_; + + // Indicates recursive watch. + bool recursive_; + + // Backend stream we receive event callbacks from (strong reference). + FSEventStreamRef fsevent_stream_; + + DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImpl); +}; + +void FSEventsCallback(ConstFSEventStreamRef stream, + void* event_watcher, size_t num_events, + void* event_paths, const FSEventStreamEventFlags flags[], + const FSEventStreamEventId event_ids[]) { + char** paths = reinterpret_cast(event_paths); + DirectoryWatcherImpl* watcher = + reinterpret_cast (event_watcher); + for (size_t i = 0; i < num_events; i++) { + watcher->OnFSEventsCallback(FilePath(paths[i])); + } +} + +bool DirectoryWatcherImpl::Watch(const FilePath& path, + DirectoryWatcher::Delegate* delegate, + bool recursive) { + DCHECK(path_.value().empty()); // Can only watch one path. + + DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI); + + if (!file_util::PathExists(path)) + return false; + + path_ = path; + if (!file_util::AbsolutePath(&path_)) { + path_ = FilePath(); // Make sure we're marked as not-in-use. + return false; + } + delegate_ = delegate; + recursive_ = recursive; + + scoped_cftyperef cf_path(CFStringCreateWithCString( + NULL, path.value().c_str(), kCFStringEncodingMacHFS)); + CFStringRef path_for_array = cf_path.get(); + scoped_cftyperef watched_paths(CFArrayCreate( + NULL, reinterpret_cast(&path_for_array), 1, + &kCFTypeArrayCallBacks)); + + FSEventStreamContext context; + context.version = 0; + context.info = this; + context.retain = NULL; + context.release = NULL; + context.copyDescription = NULL; + + fsevent_stream_ = FSEventStreamCreate(NULL, &FSEventsCallback, &context, + watched_paths, + kFSEventStreamEventIdSinceNow, + kEventLatencySeconds, + kFSEventStreamCreateFlagNone); + FSEventStreamScheduleWithRunLoop(fsevent_stream_, CFRunLoopGetCurrent(), + kCFRunLoopDefaultMode); + FSEventStreamStart(fsevent_stream_); + + return true; +} + +} // namespace + +DirectoryWatcher::DirectoryWatcher() { + impl_ = new DirectoryWatcherImpl(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_stub.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_stub.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_stub.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_stub.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,20 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file exists for Linux systems which don't have the inotify headers, and +// thus cannot build directory_watcher_inotify.cc + +#include "base/directory_watcher.h" + +class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate { + public: + virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate, + bool recursive) { + return false; + } +}; + +DirectoryWatcher::DirectoryWatcher() { + impl_ = new DirectoryWatcherImpl(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,406 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/directory_watcher.h" + +#include + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/platform_thread.h" +#include "base/string_util.h" +#if defined(OS_WIN) +#include "base/win_util.h" +#endif // defined(OS_WIN) +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +// For tests where we wait a bit to verify nothing happened +const int kWaitForEventTime = 1000; + +class DirectoryWatcherTest : public testing::Test { + public: + // Implementation of DirectoryWatcher on Mac requires UI loop. + DirectoryWatcherTest() : loop_(MessageLoop::TYPE_UI) { + } + + void OnTestDelegateFirstNotification(const FilePath& path) { + notified_delegates_++; + if (notified_delegates_ >= expected_notified_delegates_) + MessageLoop::current()->Quit(); + } + + protected: + virtual void SetUp() { + // Name a subdirectory of the temp directory. + FilePath path; + ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &path)); + test_dir_ = path.Append(FILE_PATH_LITERAL("DirectoryWatcherTest")); + + // Create a fresh, empty copy of this directory. + file_util::Delete(test_dir_, true); + file_util::CreateDirectory(test_dir_); + } + + virtual void TearDown() { + // Make sure there are no tasks in the loop. + loop_.RunAllPending(); + + // Clean up test directory. + ASSERT_TRUE(file_util::Delete(test_dir_, true)); + ASSERT_FALSE(file_util::PathExists(test_dir_)); + } + + // Write |content| to the |filename|. Returns true on success. + bool WriteTestFile(const FilePath& filename, + const std::string& content) { + return (file_util::WriteFile(filename, content.c_str(), content.length()) == + static_cast(content.length())); + } + + // Create directory |name| under test_dir_. If |sync| is true, runs + // SyncIfPOSIX. Returns path to the created directory, including test_dir_. + FilePath CreateTestDirDirectoryASCII(const std::string& name, bool sync) { + FilePath path(test_dir_.AppendASCII(name)); + EXPECT_TRUE(file_util::CreateDirectory(path)); + if (sync) + SyncIfPOSIX(); + return path; + } + + void SetExpectedNumberOfNotifiedDelegates(int n) { + notified_delegates_ = 0; + expected_notified_delegates_ = n; + } + + void VerifyExpectedNumberOfNotifiedDelegates() { + // Check that we get at least the expected number of notified delegates. + if (expected_notified_delegates_ - notified_delegates_ > 0) + loop_.Run(); + + // Check that we get no more than the expected number of notified delegates. + loop_.PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask, + kWaitForEventTime); + loop_.Run(); + EXPECT_EQ(expected_notified_delegates_, notified_delegates_); + } + + // We need this function for reliable tests on Mac OS X. FSEvents API + // has a latency interval and can merge multiple events into one, + // and we need a clear distinction between events triggered by test setup code + // and test code. + void SyncIfPOSIX() { +#if defined(OS_POSIX) + sync(); +#endif // defined(OS_POSIX) + } + + MessageLoop loop_; + + // The path to a temporary directory used for testing. + FilePath test_dir_; + + // The number of test delegates which received their notification. + int notified_delegates_; + + // The number of notified test delegates after which we quit the message loop. + int expected_notified_delegates_; +}; + +class TestDelegate : public DirectoryWatcher::Delegate { + public: + TestDelegate(DirectoryWatcherTest* test) + : test_(test), + got_notification_(false), + original_thread_id_(PlatformThread::CurrentId()) { + } + + bool got_notification() const { + return got_notification_; + } + + void reset() { + got_notification_ = false; + } + + virtual void OnDirectoryChanged(const FilePath& path) { + EXPECT_EQ(original_thread_id_, PlatformThread::CurrentId()); + if (!got_notification_) + test_->OnTestDelegateFirstNotification(path); + got_notification_ = true; + } + + private: + // Hold a pointer to current test fixture to inform it on first notification. + DirectoryWatcherTest* test_; + + // Set to true after first notification. + bool got_notification_; + + // Keep track of original thread id to verify that callbacks are called + // on the same thread. + PlatformThreadId original_thread_id_; +}; + +// Basic test: add a file and verify we notice it. +TEST_F(DirectoryWatcherTest, NewFile) { + DirectoryWatcher watcher; + TestDelegate delegate(this); + ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false)); + + SetExpectedNumberOfNotifiedDelegates(1); + ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "content")); + VerifyExpectedNumberOfNotifiedDelegates(); +} + +// Verify that modifying a file is caught. +TEST_F(DirectoryWatcherTest, ModifiedFile) { + // Write a file to the test dir. + ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "content")); + SyncIfPOSIX(); + + DirectoryWatcher watcher; + TestDelegate delegate(this); + ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false)); + + // Now make sure we get notified if the file is modified. + SetExpectedNumberOfNotifiedDelegates(1); + ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "new content")); + VerifyExpectedNumberOfNotifiedDelegates(); +} + +TEST_F(DirectoryWatcherTest, DeletedFile) { + // Write a file to the test dir. + ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "content")); + SyncIfPOSIX(); + + DirectoryWatcher watcher; + TestDelegate delegate(this); + ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false)); + + // Now make sure we get notified if the file is deleted. + SetExpectedNumberOfNotifiedDelegates(1); + ASSERT_TRUE(file_util::Delete(test_dir_.AppendASCII("test_file"), false)); + VerifyExpectedNumberOfNotifiedDelegates(); +} + +// Verify that letting the watcher go out of scope stops notifications. +TEST_F(DirectoryWatcherTest, Unregister) { + TestDelegate delegate(this); + + { + DirectoryWatcher watcher; + ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false)); + + // And then let it fall out of scope, clearing its watch. + } + + // Write a file to the test dir. + SetExpectedNumberOfNotifiedDelegates(0); + ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "content")); + VerifyExpectedNumberOfNotifiedDelegates(); +} + +TEST_F(DirectoryWatcherTest, SubDirRecursive) { + FilePath subdir(CreateTestDirDirectoryASCII("SubDir", true)); + +#if defined(OS_LINUX) + // TODO(port): Recursive watches are not implemented on Linux. + return; +#endif // !defined(OS_WIN) + + // Verify that modifications to a subdirectory are noticed by recursive watch. + TestDelegate delegate(this); + DirectoryWatcher watcher; + ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, true)); + // Write a file to the subdir. + SetExpectedNumberOfNotifiedDelegates(1); + ASSERT_TRUE(WriteTestFile(subdir.AppendASCII("test_file"), "some content")); + VerifyExpectedNumberOfNotifiedDelegates(); +} + +TEST_F(DirectoryWatcherTest, SubDirNonRecursive) { +#if defined(OS_WIN) + // Disable this test for earlier version of Windows. It turned out to be + // very difficult to create a reliable test for them. + if (win_util::GetWinVersion() < win_util::WINVERSION_VISTA) + return; +#endif // defined(OS_WIN) + + FilePath subdir(CreateTestDirDirectoryASCII("SubDir", false)); + + // Create a test file before the test. On Windows we get a notification + // when creating a file in a subdir even with a non-recursive watch. + ASSERT_TRUE(WriteTestFile(subdir.AppendASCII("test_file"), "some content")); + + SyncIfPOSIX(); + + // Verify that modifications to a subdirectory are not noticed + // by a not-recursive watch. + DirectoryWatcher watcher; + TestDelegate delegate(this); + ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, false)); + + // Modify the test file. There should be no notifications. + SetExpectedNumberOfNotifiedDelegates(0); + ASSERT_TRUE(WriteTestFile(subdir.AppendASCII("test_file"), "other content")); + VerifyExpectedNumberOfNotifiedDelegates(); +} + +namespace { +// Used by the DeleteDuringNotify test below. +// Deletes the DirectoryWatcher when it's notified. +class Deleter : public DirectoryWatcher::Delegate { + public: + Deleter(DirectoryWatcher* watcher, MessageLoop* loop) + : watcher_(watcher), + loop_(loop) { + } + + virtual void OnDirectoryChanged(const FilePath& path) { + watcher_.reset(NULL); + loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + } + + scoped_ptr watcher_; + MessageLoop* loop_; +}; +} // anonymous namespace + +// Verify that deleting a watcher during the callback +TEST_F(DirectoryWatcherTest, DeleteDuringNotify) { + DirectoryWatcher* watcher = new DirectoryWatcher; + Deleter deleter(watcher, &loop_); // Takes ownership of watcher. + ASSERT_TRUE(watcher->Watch(test_dir_, &deleter, false)); + + ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "content")); + loop_.Run(); + + // We win if we haven't crashed yet. + // Might as well double-check it got deleted, too. + ASSERT_TRUE(deleter.watcher_.get() == NULL); +} + +TEST_F(DirectoryWatcherTest, MultipleWatchersSingleFile) { + DirectoryWatcher watcher1, watcher2; + TestDelegate delegate1(this), delegate2(this); + ASSERT_TRUE(watcher1.Watch(test_dir_, &delegate1, false)); + ASSERT_TRUE(watcher2.Watch(test_dir_, &delegate2, false)); + + SetExpectedNumberOfNotifiedDelegates(2); + ASSERT_TRUE(WriteTestFile(test_dir_.AppendASCII("test_file"), "content")); + VerifyExpectedNumberOfNotifiedDelegates(); +} + +TEST_F(DirectoryWatcherTest, MultipleWatchersDifferentFiles) { + const int kNumberOfWatchers = 5; + DirectoryWatcher watchers[kNumberOfWatchers]; + TestDelegate delegates[kNumberOfWatchers] = {this, this, this, this, this}; + FilePath subdirs[kNumberOfWatchers]; + for (int i = 0; i < kNumberOfWatchers; i++) { + subdirs[i] = CreateTestDirDirectoryASCII("Dir" + IntToString(i), false); + ASSERT_TRUE(watchers[i].Watch(subdirs[i], &delegates[i], false)); + } + for (int i = 0; i < kNumberOfWatchers; i++) { + // Verify that we only get modifications from one watcher (each watcher has + // different directory). + + for (int j = 0; j < kNumberOfWatchers; j++) + delegates[j].reset(); + + // Write a file to the subdir. + SetExpectedNumberOfNotifiedDelegates(1); + ASSERT_TRUE(WriteTestFile(subdirs[i].AppendASCII("test_file"), "content")); + VerifyExpectedNumberOfNotifiedDelegates(); + + loop_.RunAllPending(); + } +} + +#if defined(OS_WIN) || defined(OS_MACOSX) +// TODO(phajdan.jr): Enable when support for Linux recursive watches is added. + +TEST_F(DirectoryWatcherTest, WatchCreatedDirectory) { + TestDelegate delegate(this); + DirectoryWatcher watcher; + ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, true)); + + SetExpectedNumberOfNotifiedDelegates(1); + FilePath subdir(CreateTestDirDirectoryASCII("SubDir", true)); + // Create a file inside the subdir to force Windows to fire notifications. + ASSERT_TRUE(WriteTestFile(subdir.AppendASCII("test_file"), "some content")); + VerifyExpectedNumberOfNotifiedDelegates(); + + delegate.reset(); + + // Verify that changes inside the subdir are noticed. + SetExpectedNumberOfNotifiedDelegates(1); + ASSERT_TRUE(WriteTestFile(subdir.AppendASCII("test_file"), "other content")); + VerifyExpectedNumberOfNotifiedDelegates(); +} + +TEST_F(DirectoryWatcherTest, RecursiveWatchDeletedSubdirectory) { + FilePath subdir(CreateTestDirDirectoryASCII("SubDir", true)); + + TestDelegate delegate(this); + DirectoryWatcher watcher; + ASSERT_TRUE(watcher.Watch(test_dir_, &delegate, true)); + + // Write a file to the subdir. + SetExpectedNumberOfNotifiedDelegates(1); + ASSERT_TRUE(WriteTestFile(subdir.AppendASCII("test_file"), "some content")); + VerifyExpectedNumberOfNotifiedDelegates(); + + delegate.reset(); + + SetExpectedNumberOfNotifiedDelegates(1); + ASSERT_TRUE(file_util::Delete(subdir, true)); + VerifyExpectedNumberOfNotifiedDelegates(); +} + +TEST_F(DirectoryWatcherTest, MoveFileAcrossWatches) { + FilePath subdir1(CreateTestDirDirectoryASCII("SubDir1", true)); + FilePath subdir2(CreateTestDirDirectoryASCII("SubDir2", true)); + + TestDelegate delegate1(this), delegate2(this); + DirectoryWatcher watcher1, watcher2; + ASSERT_TRUE(watcher1.Watch(subdir1, &delegate1, true)); + ASSERT_TRUE(watcher2.Watch(subdir2, &delegate2, true)); + + SetExpectedNumberOfNotifiedDelegates(1); + ASSERT_TRUE(WriteTestFile(subdir1.AppendASCII("file"), "some content")); + SyncIfPOSIX(); + VerifyExpectedNumberOfNotifiedDelegates(); + + delegate1.reset(); + delegate2.reset(); + + SetExpectedNumberOfNotifiedDelegates(2); + ASSERT_TRUE(file_util::Move(subdir1.AppendASCII("file"), + subdir2.AppendASCII("file"))); + VerifyExpectedNumberOfNotifiedDelegates(); + + delegate1.reset(); + delegate2.reset(); + + SetExpectedNumberOfNotifiedDelegates(1); + ASSERT_TRUE(WriteTestFile(subdir2.AppendASCII("file"), "other content")); + VerifyExpectedNumberOfNotifiedDelegates(); +} +#endif // defined(OS_WIN) || defined(OS_MACOSX) + +// Verify that watching a directory that doesn't exist fails, but doesn't +// asssert. +// Basic test: add a file and verify we notice it. +TEST_F(DirectoryWatcherTest, NonExistentDirectory) { + DirectoryWatcher watcher; + ASSERT_FALSE(watcher.Watch(test_dir_.AppendASCII("does-not-exist"), NULL, + false)); +} + +} // namespace diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/directory_watcher_win.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,82 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/directory_watcher.h" + +#include "base/file_path.h" +#include "base/logging.h" +#include "base/object_watcher.h" +#include "base/ref_counted.h" + +namespace { + +class DirectoryWatcherImpl : public DirectoryWatcher::PlatformDelegate, + public base::ObjectWatcher::Delegate { + public: + DirectoryWatcherImpl() : handle_(INVALID_HANDLE_VALUE) {} + virtual ~DirectoryWatcherImpl(); + + virtual bool Watch(const FilePath& path, DirectoryWatcher::Delegate* delegate, + bool recursive); + + // Callback from MessageLoopForIO. + virtual void OnObjectSignaled(HANDLE object); + + private: + // Delegate to notify upon changes. + DirectoryWatcher::Delegate* delegate_; + // Path we're watching (passed to delegate). + FilePath path_; + // Handle for FindFirstChangeNotification. + HANDLE handle_; + // ObjectWatcher to watch handle_ for events. + base::ObjectWatcher watcher_; + + DISALLOW_COPY_AND_ASSIGN(DirectoryWatcherImpl); +}; + +DirectoryWatcherImpl::~DirectoryWatcherImpl() { + if (handle_ != INVALID_HANDLE_VALUE) { + watcher_.StopWatching(); + FindCloseChangeNotification(handle_); + } +} + +bool DirectoryWatcherImpl::Watch(const FilePath& path, + DirectoryWatcher::Delegate* delegate, bool recursive) { + DCHECK(path_.value().empty()); // Can only watch one path. + + handle_ = FindFirstChangeNotification( + path.value().c_str(), + recursive, + FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_SIZE | + FILE_NOTIFY_CHANGE_LAST_WRITE); + if (handle_ == INVALID_HANDLE_VALUE) + return false; + + delegate_ = delegate; + path_ = path; + watcher_.StartWatching(handle_, this); + + return true; +} + +void DirectoryWatcherImpl::OnObjectSignaled(HANDLE object) { + DCHECK(object == handle_); + // Make sure we stay alive through the body of this function. + scoped_refptr keep_alive(this); + + delegate_->OnDirectoryChanged(path_); + + // Register for more notifications on file change. + BOOL ok = FindNextChangeNotification(object); + DCHECK(ok); + watcher_.StartWatching(object, this); +} + +} // namespace + +DirectoryWatcher::DirectoryWatcher() { + impl_ = new DirectoryWatcherImpl(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/eintr_wrapper.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/eintr_wrapper.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/eintr_wrapper.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/eintr_wrapper.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,33 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This provides a wrapper around system calls which may be interrupted by a +// signal and return EINTR. See man 7 signal. +// +// On Windows, this wrapper macro does nothing. + +#ifndef BASE_EINTR_WRAPPER_H_ +#define BASE_EINTR_WRAPPER_H_ + +#include "build/build_config.h" + +#if defined(OS_POSIX) + +#include + +#define HANDLE_EINTR(x) ({ \ + typeof(x) __eintr_result__; \ + do { \ + __eintr_result__ = x; \ + } while (__eintr_result__ == -1 && errno == EINTR); \ + __eintr_result__;\ +}) + +#else + +#define HANDLE_EINTR(x) x + +#endif // OS_POSIX + +#endif // !BASE_EINTR_WRAPPER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/event_recorder.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/event_recorder.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/event_recorder.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/event_recorder.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,259 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "build/build_config.h" + +#include +#include + +#include "base/event_recorder.h" +#include "base/file_util.h" +#include "base/logging.h" + +// A note about time. +// For perfect playback of events, you'd like a very accurate timer +// so that events are played back at exactly the same time that +// they were recorded. However, windows has a clock which is only +// granular to ~15ms. We see more consistent event playback when +// using a higher resolution timer. To do this, we use the +// timeGetTime API instead of the default GetTickCount() API. + +namespace base { + +EventRecorder* EventRecorder::current_ = NULL; + +LRESULT CALLBACK StaticRecordWndProc(int nCode, WPARAM wParam, + LPARAM lParam) { + CHECK(EventRecorder::current()); + return EventRecorder::current()->RecordWndProc(nCode, wParam, lParam); +} + +LRESULT CALLBACK StaticPlaybackWndProc(int nCode, WPARAM wParam, + LPARAM lParam) { + CHECK(EventRecorder::current()); + return EventRecorder::current()->PlaybackWndProc(nCode, wParam, lParam); +} + +EventRecorder::~EventRecorder() { + // Try to assert early if the caller deletes the recorder + // while it is still in use. + DCHECK(!journal_hook_); + DCHECK(!is_recording_ && !is_playing_); +} + +bool EventRecorder::StartRecording(const FilePath& filename) { + if (journal_hook_ != NULL) + return false; + if (is_recording_ || is_playing_) + return false; + + // Open the recording file. + DCHECK(file_ == NULL); + file_ = file_util::OpenFile(filename, "wb+"); + if (!file_) { + DLOG(ERROR) << "EventRecorder could not open log file"; + return false; + } + + // Set the faster clock, if possible. + ::timeBeginPeriod(1); + + // Set the recording hook. JOURNALRECORD can only be used as a global hook. + journal_hook_ = ::SetWindowsHookEx(WH_JOURNALRECORD, StaticRecordWndProc, + GetModuleHandle(NULL), 0); + if (!journal_hook_) { + DLOG(ERROR) << "EventRecorder Record Hook failed"; + file_util::CloseFile(file_); + return false; + } + + is_recording_ = true; + return true; +} + +void EventRecorder::StopRecording() { + if (is_recording_) { + DCHECK(journal_hook_ != NULL); + + if (!::UnhookWindowsHookEx(journal_hook_)) { + DLOG(ERROR) << "EventRecorder Unhook failed"; + // Nothing else we can really do here. + return; + } + + ::timeEndPeriod(1); + + DCHECK(file_ != NULL); + file_util::CloseFile(file_); + file_ = NULL; + + journal_hook_ = NULL; + is_recording_ = false; + } +} + +bool EventRecorder::StartPlayback(const FilePath& filename) { + if (journal_hook_ != NULL) + return false; + if (is_recording_ || is_playing_) + return false; + + // Open the recording file. + DCHECK(file_ == NULL); + file_ = file_util::OpenFile(filename, "rb"); + if (!file_) { + DLOG(ERROR) << "EventRecorder Playback could not open log file"; + return false; + } + // Read the first event from the record. + if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1) { + DLOG(ERROR) << "EventRecorder Playback has no records!"; + file_util::CloseFile(file_); + return false; + } + + // Set the faster clock, if possible. + ::timeBeginPeriod(1); + + // Playback time is tricky. When playing back, we read a series of events, + // each with timeouts. Simply subtracting the delta between two timers will + // lead to fast playback (about 2x speed). The API has two events, one + // which advances to the next event (HC_SKIP), and another that requests the + // event (HC_GETNEXT). The same event will be requested multiple times. + // Each time the event is requested, we must calculate the new delay. + // To do this, we track the start time of the playback, and constantly + // re-compute the delay. I mention this only because I saw two examples + // of how to use this code on the net, and both were broken :-) + playback_start_time_ = timeGetTime(); + playback_first_msg_time_ = playback_msg_.time; + + // Set the hook. JOURNALPLAYBACK can only be used as a global hook. + journal_hook_ = ::SetWindowsHookEx(WH_JOURNALPLAYBACK, StaticPlaybackWndProc, + GetModuleHandle(NULL), 0); + if (!journal_hook_) { + DLOG(ERROR) << "EventRecorder Playback Hook failed"; + return false; + } + + is_playing_ = true; + + return true; +} + +void EventRecorder::StopPlayback() { + if (is_playing_) { + DCHECK(journal_hook_ != NULL); + + if (!::UnhookWindowsHookEx(journal_hook_)) { + DLOG(ERROR) << "EventRecorder Unhook failed"; + // Nothing else we can really do here. + } + + DCHECK(file_ != NULL); + file_util::CloseFile(file_); + file_ = NULL; + + ::timeEndPeriod(1); + + journal_hook_ = NULL; + is_playing_ = false; + } +} + +// Windows callback hook for the recorder. +LRESULT EventRecorder::RecordWndProc(int nCode, WPARAM wParam, LPARAM lParam) { + static bool recording_enabled = true; + EVENTMSG* msg_ptr = NULL; + + // The API says we have to do this. + // See http://msdn2.microsoft.com/en-us/library/ms644983(VS.85).aspx + if (nCode < 0) + return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam); + + // Check for the break key being pressed and stop recording. + if (::GetKeyState(VK_CANCEL) & 0x8000) { + StopRecording(); + return ::CallNextHookEx(journal_hook_, nCode, wParam, lParam); + } + + // The Journal Recorder must stop recording events when system modal + // dialogs are present. (see msdn link above) + switch(nCode) { + case HC_SYSMODALON: + recording_enabled = false; + break; + case HC_SYSMODALOFF: + recording_enabled = true; + break; + } + + if (nCode == HC_ACTION && recording_enabled) { + // Aha - we have an event to record. + msg_ptr = reinterpret_cast(lParam); + msg_ptr->time = timeGetTime(); + fwrite(msg_ptr, sizeof(EVENTMSG), 1, file_); + fflush(file_); + } + + return CallNextHookEx(journal_hook_, nCode, wParam, lParam); +} + +// Windows callback for the playback mode. +LRESULT EventRecorder::PlaybackWndProc(int nCode, WPARAM wParam, + LPARAM lParam) { + static bool playback_enabled = true; + int delay = 0; + + switch(nCode) { + // A system modal dialog box is being displayed. Stop playing back + // messages. + case HC_SYSMODALON: + playback_enabled = false; + break; + + // A system modal dialog box is destroyed. We can start playing back + // messages again. + case HC_SYSMODALOFF: + playback_enabled = true; + break; + + // Prepare to copy the next mouse or keyboard event to playback. + case HC_SKIP: + if (!playback_enabled) + break; + + // Read the next event from the record. + if (fread(&playback_msg_, sizeof(EVENTMSG), 1, file_) != 1) + this->StopPlayback(); + break; + + // Copy the mouse or keyboard event to the EVENTMSG structure in lParam. + case HC_GETNEXT: + if (!playback_enabled) + break; + + memcpy(reinterpret_cast(lParam), &playback_msg_, + sizeof(playback_msg_)); + + // The return value is the amount of time (in milliseconds) to wait + // before playing back the next message in the playback queue. Each + // time this is called, we recalculate the delay relative to our current + // wall clock. + delay = (playback_msg_.time - playback_first_msg_time_) - + (timeGetTime() - playback_start_time_); + if (delay < 0) + delay = 0; + return delay; + + // An application has called PeekMessage with wRemoveMsg set to PM_NOREMOVE + // indicating that the message is not removed from the message queue after + // PeekMessage processing. + case HC_NOREMOVE: + break; + } + + return CallNextHookEx(journal_hook_, nCode, wParam, lParam); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/event_recorder.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/event_recorder.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/event_recorder.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/event_recorder.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,102 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_EVENT_RECORDER_H_ +#define BASE_EVENT_RECORDER_H_ + +#include +#if defined(OS_WIN) +#include +#endif +#include "base/basictypes.h" + +class FilePath; + +namespace base { + +// A class for recording and playing back keyboard and mouse input events. +// +// Note - if you record events, and the playback with the windows in +// different sizes or positions, the playback will fail. When +// recording and playing, you should move the relevant windows +// to constant sizes and locations. +// TODO(mbelshe) For now this is a singleton. I believe that this class +// could be easily modified to: +// support two simultaneous recorders +// be playing back events while already recording events. +// Why? Imagine if the product had a "record a macro" feature. +// You might be recording globally, while recording or playing back +// a macro. I don't think two playbacks make sense. +class EventRecorder { + public: + // Get the singleton EventRecorder. + // We can only handle one recorder/player at a time. + static EventRecorder* current() { + if (!current_) + current_ = new EventRecorder(); + return current_; + } + + // Starts recording events. + // Will clobber the file if it already exists. + // Returns true on success, or false if an error occurred. + bool StartRecording(const FilePath& filename); + + // Stops recording. + void StopRecording(); + + // Is the EventRecorder currently recording. + bool is_recording() const { return is_recording_; } + + // Plays events previously recorded. + // Returns true on success, or false if an error occurred. + bool StartPlayback(const FilePath& filename); + + // Stops playback. + void StopPlayback(); + + // Is the EventRecorder currently playing. + bool is_playing() const { return is_playing_; } + +#if defined(OS_WIN) + // C-style callbacks for the EventRecorder. + // Used for internal purposes only. + LRESULT RecordWndProc(int nCode, WPARAM wParam, LPARAM lParam); + LRESULT PlaybackWndProc(int nCode, WPARAM wParam, LPARAM lParam); +#endif + + private: + // Create a new EventRecorder. Events are saved to the file filename. + // If the file already exists, it will be deleted before recording + // starts. + explicit EventRecorder() + : is_recording_(false), + is_playing_(false), +#if defined(OS_WIN) + journal_hook_(NULL), + file_(NULL), +#endif + playback_first_msg_time_(0), + playback_start_time_(0) { + } + ~EventRecorder(); + + static EventRecorder* current_; // Our singleton. + + bool is_recording_; + bool is_playing_; +#if defined(OS_WIN) + HHOOK journal_hook_; + FILE* file_; + EVENTMSG playback_msg_; +#endif + int playback_first_msg_time_; + int playback_start_time_; + + DISALLOW_EVIL_CONSTRUCTORS(EventRecorder); +}; + +} // namespace base + +#endif // BASE_EVENT_RECORDER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/event_recorder_stubs.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/event_recorder_stubs.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/event_recorder_stubs.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/event_recorder_stubs.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,28 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/event_recorder.h" + +// This file implements a link stub for EventRecorder that can be used on +// platforms that don't have a working EventRecorder implementation. + +namespace base { + +EventRecorder* EventRecorder::current_; // Our singleton. + +bool EventRecorder::StartRecording(const FilePath& filename) { + return true; +} + +void EventRecorder::StopRecording() { +} + +bool EventRecorder::StartPlayback(const FilePath& filename) { + return false; +} + +void EventRecorder::StopPlayback() { +} + +} // namespace diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/field_trial.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/field_trial.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/field_trial.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/field_trial.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,113 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +#include "base/field_trial.h" +#include "base/logging.h" +#include "base/rand_util.h" +#include "base/string_util.h" + +using base::Time; + +// static +const int FieldTrial::kNotParticipating = -1; + +//------------------------------------------------------------------------------ +// FieldTrial methods and members. + +FieldTrial::FieldTrial(const std::string& name, + const Probability total_probability) + : name_(name), + divisor_(total_probability), + random_(static_cast(divisor_ * base::RandDouble())), + accumulated_group_probability_(0), + next_group_number_(0), + group_(kNotParticipating) { + FieldTrialList::Register(this); +} + +int FieldTrial::AppendGroup(const std::string& name, + Probability group_probability) { + DCHECK(group_probability <= divisor_); + accumulated_group_probability_ += group_probability; + DCHECK(accumulated_group_probability_ <= divisor_); + if (group_ == kNotParticipating && accumulated_group_probability_ > random_) { + // This is the group that crossed the random line, so we do teh assignment. + group_ = next_group_number_; + if (name.empty()) + StringAppendF(&group_name_, "_%d", group_); + else + group_name_ = name; + } + return next_group_number_++; +} + +// static +std::string FieldTrial::MakeName(const std::string& name_prefix, + const std::string& trial_name) { + std::string big_string(name_prefix); + return big_string.append(FieldTrialList::FindFullName(trial_name)); +} + +//------------------------------------------------------------------------------ +// FieldTrialList methods and members. + +// static +FieldTrialList* FieldTrialList::global_ = NULL; + +FieldTrialList::FieldTrialList() + : application_start_time_(Time::Now()) { + DCHECK(!global_); + global_ = this; +} + +FieldTrialList::~FieldTrialList() { + AutoLock auto_lock(lock_); + while (!registered_.empty()) { + RegistrationList::iterator it = registered_.begin(); + it->second->Release(); + registered_.erase(it->first); + } + DCHECK(this == global_); + global_ = NULL; +} + +// static +void FieldTrialList::Register(FieldTrial* trial) { + AutoLock auto_lock(global_->lock_); + DCHECK(!global_->PreLockedFind(trial->name())); + trial->AddRef(); + global_->registered_[trial->name()] = trial; +} + +// static +int FieldTrialList::FindValue(const std::string& name) { + FieldTrial* field_trial = Find(name); + if (field_trial) + return field_trial->group(); + return FieldTrial::kNotParticipating; +} + +// static +std::string FieldTrialList::FindFullName(const std::string& name) { + FieldTrial* field_trial = Find(name); + if (field_trial) + return field_trial->group_name(); + return ""; +} + +// static +FieldTrial* FieldTrialList::Find(const std::string& name) { + if (!global_) + return NULL; + AutoLock auto_lock(global_->lock_); + return global_->PreLockedFind(name); +} + +FieldTrial* FieldTrialList::PreLockedFind(const std::string& name) { + RegistrationList::iterator it = registered_.find(name); + if (registered_.end() == it) + return NULL; + return it->second; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/field_trial.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/field_trial.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/field_trial.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/field_trial.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,192 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// FieldTrial is a class for handling details of statistical experiments +// performed by actual users in the field (i.e., in a shipped or beta product). +// All code is called exclusively on the UI thread currently. +// +// The simplest example is an experiment to see whether one of two options +// produces "better" results across our user population. In that scenario, UMA +// data is uploaded to aggregate the test results, and this FieldTrial class +// manages the state of each such experiment (state == which option was +// pseudo-randomly selected). +// +// States are typically generated randomly, either based on a one time +// randomization (generated randomly once, and then persistently reused in the +// client during each future run of the program), or by a startup randomization +// (generated each time the application starts up, but held constant during the +// duration of the process), or by continuous randomization across a run (where +// the state can be recalculated again and again, many times during a process). +// Only startup randomization is implemented thus far. + +//------------------------------------------------------------------------------ +// Example: Suppose we have an experiment involving memory, such as determining +// the impact of memory model command line flags actual memory use. +// We assume that we already have a histogram of memory usage, such as: + +// HISTOGRAM_COUNTS("Memory.RendererTotal", count); + +// Somewhere in main thread initialization code, we'd probably define an +// instance of a FieldTrial, with code such as: + +// // Note, FieldTrials are reference counted, and persist automagically until +// // process teardown, courtesy of their automatic registration in +// // FieldTrialList. +// scoped_refptr trial = new FieldTrial("MemoryExperiment", 1000); +// int group1 = trial->AppendGroup("_high_mem", 20); // 2% in _high_mem group. +// int group2 = trial->AppendGroup("_low_mem", 20); // 2% in _low_mem group. +// // Take action depending of which group we randomly land in. +// if (trial->group() == group1) +// SetMemoryModel(HIGH); // Sample setting of browser state. +// else if (trial->group() == group2) +// SetMemoryModel(LOW); // Sample alternate setting. + +// We then modify any histograms we wish to correlate with our experiment to +// have slighly different names, depending on what group the trial instance +// happened (randomly) to be assigned to: + +// HISTOGRAM_COUNTS(FieldTrial::MakeName("Memory.RendererTotal", +// "MemoryExperiment").data(), count); + +// The above code will create 3 distinct histograms, with each run of the +// application being assigned to of of teh three groups, and for each group, the +// correspondingly named histogram will be populated: + +// Memory.RendererTotal // 96% of users still fill this histogram. +// Memory.RendererTotal_high_mem // 2% of users will fill this histogram. +// Memory.RendererTotal_low_mem // 2% of users will fill this histogram. + +//------------------------------------------------------------------------------ + +#ifndef BASE_FIELD_TRIAL_H_ +#define BASE_FIELD_TRIAL_H_ + +#include +#include + +#include "base/lock.h" +#include "base/non_thread_safe.h" +#include "base/ref_counted.h" +#include "base/time.h" + + +class FieldTrial : public base::RefCounted { + public: + static const int kNotParticipating; + + typedef int Probability; // Use scaled up probability. + + // The name is used to register the instance with the FieldTrialList class, + // and can be used to find the trial (only one trial can be present for each + // name). + // Group probabilities that are later supplied must sum to less than or equal + // to the total_probability. + FieldTrial(const std::string& name, Probability total_probability); + + // Establish the name and probability of the next group in this trial. + // Sometimes, based on construction randomization, this call may causes the + // provided group to be *THE* group selected for use in this instance. + int AppendGroup(const std::string& name, Probability group_probability); + + // Return the name of the FieldTrial (excluding the group name). + std::string name() const { return name_; } + + // Return the randomly selected group number that was assigned. + // Return kNotParticipating if the instance is not participating in the + // experiment. + int group() const { return group_; } + + // If the field trial is not in an experiment, this returns the empty string. + // if the group's name is empty, a name of "_" concatenated with the group + // number is used as the group name. + std::string group_name() const { return group_name_; } + + // Helper function for the most common use: as an argument to specifiy the + // name of a HISTOGRAM. Use the original histogram name as the name_prefix. + static std::string MakeName(const std::string& name_prefix, + const std::string& trial_name); + + private: + // The name of the field trial, as can be found via the FieldTrialList. + // This is empty of the trial is not in the experiment. + const std::string name_; + + // The maximu sum of all probabilities supplied, which corresponds to 100%. + // This is the scaling factor used to adjust supplied probabilities. + Probability divisor_; + + // The randomly selected probability that is used to select a group (or have + // the instance not participate). It is the product of divisor_ and a random + // number between [0, 1). + Probability random_; + + // Sum of the probabilities of all appended groups. + Probability accumulated_group_probability_; + + int next_group_number_; + + // The pseudo-randomly assigned group number. + // This is kNotParticipating if no group has been assigned. + int group_; + + // A textual name for the randomly selected group, including the Trial name. + // If this Trial is not a member of an group, this string is empty. + std::string group_name_; + + DISALLOW_COPY_AND_ASSIGN(FieldTrial); +}; + +//------------------------------------------------------------------------------ +// Class with a list of all active field trials. A trial is active if it has +// been registered, which includes evaluating its state based on its probaility. +// Only one instance of this class exists. +class FieldTrialList { + public: + // This singleton holds the global list of registered FieldTrials. + FieldTrialList(); + // Destructor Release()'s references to all registered FieldTrial instances. + ~FieldTrialList(); + + // Register() stores a pointer to the given trial in a global map. + // This method also AddRef's the indicated trial. + static void Register(FieldTrial* trial); + + // The Find() method can be used to test to see if a named Trial was already + // registered, or to retrieve a pointer to it from the global map. + static FieldTrial* Find(const std::string& name); + + static int FindValue(const std::string& name); + + static std::string FindFullName(const std::string& name); + + // The time of construction of the global map is recorded in a static variable + // and is commonly used by experiments to identify the time since the start + // of the application. In some experiments it may be useful to discount + // data that is gathered before the application has reached sufficient + // stability (example: most DLL have loaded, etc.) + static base::Time application_start_time() { + if (global_) + return global_->application_start_time_; + // For testing purposes only, or when we don't yet have a start time. + return base::Time::Now(); + } + + private: + // Helper function should be called only while holding lock_. + FieldTrial* PreLockedFind(const std::string& name); + + typedef std::map RegistrationList; + + static FieldTrialList* global_; // The singleton of this class. + + base::Time application_start_time_; + + // Lock for access to registered_. + Lock lock_; + RegistrationList registered_; + + DISALLOW_COPY_AND_ASSIGN(FieldTrialList); +}; + +#endif // BASE_FIELD_TRIAL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/field_trial_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/field_trial_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/field_trial_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/field_trial_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,116 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Test of FieldTrial class + +#include "base/field_trial.h" + +#include "base/string_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +class FieldTrialTest : public testing::Test { + public: + FieldTrialTest() : trial_list_() { } + + private: + FieldTrialList trial_list_; +}; + +// Test registration, and also check that destructors are called for trials +// (and that Purify doesn't catch us leaking). +TEST_F(FieldTrialTest, Registration) { + const char* name1 = "name 1 test"; + const char* name2 = "name 2 test"; + EXPECT_FALSE(FieldTrialList::Find(name1)); + EXPECT_FALSE(FieldTrialList::Find(name2)); + + FieldTrial* trial1 = new FieldTrial(name1, 10); + EXPECT_EQ(trial1->group(), FieldTrial::kNotParticipating); + EXPECT_EQ(trial1->name(), name1); + EXPECT_EQ(trial1->group_name(), ""); + + trial1->AppendGroup("", 7); + + EXPECT_EQ(trial1, FieldTrialList::Find(name1)); + EXPECT_FALSE(FieldTrialList::Find(name2)); + + FieldTrial* trial2 = new FieldTrial(name2, 10); + EXPECT_EQ(trial2->group(), FieldTrial::kNotParticipating); + EXPECT_EQ(trial2->name(), name2); + EXPECT_EQ(trial2->group_name(), ""); + + trial2->AppendGroup("a first group", 7); + + EXPECT_EQ(trial1, FieldTrialList::Find(name1)); + EXPECT_EQ(trial2, FieldTrialList::Find(name2)); + // Note: FieldTrialList should delete the objects at shutdown. +} + +TEST_F(FieldTrialTest, AbsoluteProbabilities) { + char always_true[] = " always true"; + char always_false[] = " always false"; + for (int i = 1; i < 250; ++i) { + // Try lots of names, by changing the first character of the name. + always_true[0] = i; + always_false[0] = i; + + FieldTrial* trial_true = new FieldTrial(always_true, 10); + const std::string winner = "_TheWinner"; + int winner_group = trial_true->AppendGroup(winner, 10); + + EXPECT_EQ(trial_true->group(), winner_group); + EXPECT_EQ(trial_true->group_name(), winner); + + FieldTrial* trial_false = new FieldTrial(always_false, 10); + int loser_group = trial_false->AppendGroup("ALoser", 0); + + EXPECT_NE(trial_false->group(), loser_group); + } +} + +TEST_F(FieldTrialTest, MiddleProbabilities) { + char name[] = " same name"; + bool false_event_seen = false; + bool true_event_seen = false; + for (int i = 1; i < 250; ++i) { + name[0] = i; + FieldTrial* trial = new FieldTrial(name, 10); + int might_win = trial->AppendGroup("MightWin", 5); + + if (trial->group() == might_win) { + true_event_seen = true; + } else { + false_event_seen = true; + } + if (false_event_seen && true_event_seen) + return; // Successful test!!! + } + // Very surprising to get here. Probability should be around 1 in 2 ** 250. + // One of the following will fail. + EXPECT_TRUE(false_event_seen); + EXPECT_TRUE(true_event_seen); +} + +TEST_F(FieldTrialTest, OneWinner) { + char name[] = "Some name"; + int group_count(10); + + FieldTrial* trial = new FieldTrial(name, group_count); + int winner_index(-2); + std::string winner_name; + + for (int i = 1; i <= group_count; ++i) { + int might_win = trial->AppendGroup("", 1); + + if (trial->group() == might_win) { + EXPECT_EQ(winner_index, -2); + winner_index = might_win; + StringAppendF(&winner_name, "_%d", might_win); + EXPECT_EQ(winner_name, trial->group_name()); + } + } + EXPECT_GE(winner_index, 0); + EXPECT_EQ(trial->group(), winner_index); + EXPECT_EQ(winner_name, trial->group_name()); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_descriptor_posix.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_descriptor_posix.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_descriptor_posix.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_descriptor_posix.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,36 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FILE_DESCRIPTOR_POSIX_H_ +#define BASE_FILE_DESCRIPTOR_POSIX_H_ + +namespace base { + +// ----------------------------------------------------------------------------- +// We introduct a special structure for file descriptors in order that we are +// able to use template specialisation to special-case their handling. +// +// WARNING: (Chromium only) There are subtleties to consider if serialising +// these objects over IPC. See comments in chrome/common/ipc_message_utils.h +// above the template specialisation for this structure. +// ----------------------------------------------------------------------------- +struct FileDescriptor { + FileDescriptor() + : fd(-1), + auto_close(false) { } + + FileDescriptor(int ifd, bool iauto_close) + : fd(ifd), + auto_close(iauto_close) { } + + int fd; + // If true, this file descriptor should be closed after it has been used. For + // example an IPC system might interpret this flag as indicating that the + // file descriptor it has been given should be closed after use. + bool auto_close; +}; + +} // namespace base + +#endif // BASE_FILE_DESCRIPTOR_POSIX_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_descriptor_shuffle.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_descriptor_shuffle.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_descriptor_shuffle.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_descriptor_shuffle.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,80 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_descriptor_shuffle.h" + +#include +#include + +#include "base/eintr_wrapper.h" +#include "base/logging.h" + +namespace base { + +bool PerformInjectiveMultimap(const InjectiveMultimap& m_in, + InjectionDelegate* delegate) { + InjectiveMultimap m(m_in); + std::vector extra_fds; + + for (InjectiveMultimap::iterator i = m.begin(); i != m.end(); ++i) { + int temp_fd = -1; + + // We DCHECK the injectiveness of the mapping. + for (InjectiveMultimap::iterator j = i + 1; j != m.end(); ++j) + DCHECK(i->dest != j->dest); + + const bool is_identity = i->source == i->dest; + + for (InjectiveMultimap::iterator j = i + 1; j != m.end(); ++j) { + if (!is_identity && i->dest == j->source) { + if (temp_fd == -1) { + if (!delegate->Duplicate(&temp_fd, i->dest)) + return false; + extra_fds.push_back(temp_fd); + } + + j->source = temp_fd; + j->close = false; + } + + if (i->close && i->source == j->dest) + i->close = false; + + if (i->close && i->source == j->source) { + i->close = false; + j->close = true; + } + } + + if (!is_identity) { + if (!delegate->Move(i->source, i->dest)) + return false; + } + + if (!is_identity && i->close) + delegate->Close(i->source); + } + + for (std::vector::const_iterator + i = extra_fds.begin(); i != extra_fds.end(); ++i) { + delegate->Close(*i); + } + + return true; +} + +bool FileDescriptorTableInjection::Duplicate(int* result, int fd) { + *result = HANDLE_EINTR(dup(fd)); + return *result >= 0; +} + +bool FileDescriptorTableInjection::Move(int src, int dest) { + return HANDLE_EINTR(dup2(src, dest)) != -1; +} + +void FileDescriptorTableInjection::Close(int fd) { + HANDLE_EINTR(close(fd)); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_descriptor_shuffle.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_descriptor_shuffle.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_descriptor_shuffle.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_descriptor_shuffle.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,76 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FILE_DESCRIPTOR_SHUFFLE_H_ +#define BASE_FILE_DESCRIPTOR_SHUFFLE_H_ + +// This code exists to perform the shuffling of file descriptors which is +// commonly needed when forking subprocesses. The naive approve is very simple, +// just call dup2 to setup the desired descriptors, but wrong. It's tough to +// handle the edge cases (like mapping 0 -> 1, 1 -> 0) correctly. +// +// In order to unittest this code, it's broken into the abstract action (an +// injective multimap) and the concrete code for dealing with file descriptors. +// Users should use the code like this: +// base::InjectiveMultimap file_descriptor_map; +// file_descriptor_map.push_back(base::InjectionArc(devnull, 0, true)); +// file_descriptor_map.push_back(base::InjectionArc(devnull, 2, true)); +// file_descriptor_map.push_back(base::InjectionArc(pipe[1], 1, true)); +// base::ShuffleFileDescriptors(file_descriptor_map); +// +// and trust the the Right Thing will get done. + +#include + +namespace base { + +// A Delegate which performs the actions required to perform an injective +// multimapping in place. +class InjectionDelegate { + public: + // Duplicate |fd|, an element of the domain, and write a fresh element of the + // domain into |result|. Returns true iff successful. + virtual bool Duplicate(int* result, int fd) = 0; + // Destructively move |src| to |dest|, overwriting |dest|. Returns true iff + // successful. + virtual bool Move(int src, int dest) = 0; + // Delete an element of the domain. + virtual void Close(int fd) = 0; +}; + +// An implementation of the InjectionDelegate interface using the file +// descriptor table of the current process as the domain. +class FileDescriptorTableInjection : public InjectionDelegate { + bool Duplicate(int* result, int fd); + bool Move(int src, int dest); + void Close(int fd); +}; + +// A single arc of the directed graph which describes an injective multimapping. +struct InjectionArc { + InjectionArc(int in_source, int in_dest, bool in_close) + : source(in_source), + dest(in_dest), + close(in_close) { + } + + int source; + int dest; + bool close; // if true, delete the source element after performing the + // mapping. +}; + +typedef std::vector InjectiveMultimap; + +bool PerformInjectiveMultimap(const InjectiveMultimap& map, + InjectionDelegate* delegate); + +static inline bool ShuffleFileDescriptors(const InjectiveMultimap& map) { + FileDescriptorTableInjection delegate; + return PerformInjectiveMultimap(map, &delegate); +} + +} // namespace base + +#endif // !BASE_FILE_DESCRIPTOR_SHUFFLE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_descriptor_shuffle_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_descriptor_shuffle_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_descriptor_shuffle_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_descriptor_shuffle_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,289 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_descriptor_shuffle.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::InjectiveMultimap; +using base::InjectionArc; +using base::PerformInjectiveMultimap; +using base::InjectionDelegate; + +namespace { +typedef testing::Test FileDescriptorShuffleTest; +} + +// 'Duplicated' file descriptors start at this number +static const int kDuplicateBase = 1000; + +struct Action { + enum Type { + CLOSE, + MOVE, + DUPLICATE, + }; + + Action(Type in_type, int in_fd1, int in_fd2 = -1) + : type(in_type), + fd1(in_fd1), + fd2(in_fd2) { + } + + bool operator==(const Action& other) const { + return other.type == type && + other.fd1 == fd1 && + other.fd2 == fd2; + } + + Type type; + int fd1; + int fd2; +}; + +class InjectionTracer : public InjectionDelegate { + public: + InjectionTracer() + : next_duplicate_(kDuplicateBase) { + } + + bool Duplicate(int* result, int fd) { + *result = next_duplicate_++; + actions_.push_back(Action(Action::DUPLICATE, *result, fd)); + return true; + } + + bool Move(int src, int dest) { + actions_.push_back(Action(Action::MOVE, src, dest)); + return true; + } + + void Close(int fd) { + actions_.push_back(Action(Action::CLOSE, fd)); + } + + const std::vector& actions() const { return actions_; } + + private: + int next_duplicate_; + std::vector actions_; +}; + +TEST(FileDescriptorShuffleTest, Empty) { + InjectiveMultimap map; + InjectionTracer tracer; + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(0u, tracer.actions().size()); +} + +TEST(FileDescriptorShuffleTest, Noop) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 0, false)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(0u, tracer.actions().size()); +} + +TEST(FileDescriptorShuffleTest, NoopAndClose) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 0, true)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(0u, tracer.actions().size()); +} + +TEST(FileDescriptorShuffleTest, Simple1) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, false)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(1u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); +} + +TEST(FileDescriptorShuffleTest, Simple2) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, false)); + map.push_back(InjectionArc(2, 3, false)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(2u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 2, 3)); +} + +TEST(FileDescriptorShuffleTest, Simple3) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, true)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(2u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::CLOSE, 0)); +} + +TEST(FileDescriptorShuffleTest, Simple4) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(10, 0, true)); + map.push_back(InjectionArc(1, 1, true)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(2u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 10, 0)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::CLOSE, 10)); +} + +TEST(FileDescriptorShuffleTest, Cycle) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, false)); + map.push_back(InjectionArc(1, 0, false)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(4u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == + Action(Action::DUPLICATE, kDuplicateBase, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0)); + EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase)); +} + +TEST(FileDescriptorShuffleTest, CycleAndClose1) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, true)); + map.push_back(InjectionArc(1, 0, false)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(4u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == + Action(Action::DUPLICATE, kDuplicateBase, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0)); + EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase)); +} + +TEST(FileDescriptorShuffleTest, CycleAndClose2) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, false)); + map.push_back(InjectionArc(1, 0, true)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(4u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == + Action(Action::DUPLICATE, kDuplicateBase, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0)); + EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase)); +} + +TEST(FileDescriptorShuffleTest, CycleAndClose3) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, true)); + map.push_back(InjectionArc(1, 0, true)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(4u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == + Action(Action::DUPLICATE, kDuplicateBase, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[2] == Action(Action::MOVE, kDuplicateBase, 0)); + EXPECT_TRUE(tracer.actions()[3] == Action(Action::CLOSE, kDuplicateBase)); +} + +TEST(FileDescriptorShuffleTest, Fanout) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, false)); + map.push_back(InjectionArc(0, 2, false)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(2u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2)); +} + +TEST(FileDescriptorShuffleTest, FanoutAndClose1) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, true)); + map.push_back(InjectionArc(0, 2, false)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(3u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2)); + EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0)); +} + +TEST(FileDescriptorShuffleTest, FanoutAndClose2) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, false)); + map.push_back(InjectionArc(0, 2, true)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(3u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2)); + EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0)); +} + +TEST(FileDescriptorShuffleTest, FanoutAndClose3) { + InjectiveMultimap map; + InjectionTracer tracer; + map.push_back(InjectionArc(0, 1, true)); + map.push_back(InjectionArc(0, 2, true)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &tracer)); + EXPECT_EQ(3u, tracer.actions().size()); + EXPECT_TRUE(tracer.actions()[0] == Action(Action::MOVE, 0, 1)); + EXPECT_TRUE(tracer.actions()[1] == Action(Action::MOVE, 0, 2)); + EXPECT_TRUE(tracer.actions()[2] == Action(Action::CLOSE, 0)); +} + +class FailingDelegate : public InjectionDelegate { + public: + bool Duplicate(int* result, int fd) { + return false; + } + + bool Move(int src, int dest) { + return false; + } + + void Close(int fd) { + } +}; + +TEST(FileDescriptorShuffleTest, EmptyWithFailure) { + InjectiveMultimap map; + FailingDelegate failing; + + EXPECT_TRUE(PerformInjectiveMultimap(map, &failing)); +} + +TEST(FileDescriptorShuffleTest, NoopWithFailure) { + InjectiveMultimap map; + FailingDelegate failing; + map.push_back(InjectionArc(0, 0, false)); + + EXPECT_TRUE(PerformInjectiveMultimap(map, &failing)); +} + +TEST(FileDescriptorShuffleTest, Simple1WithFailure) { + InjectiveMultimap map; + FailingDelegate failing; + map.push_back(InjectionArc(0, 1, false)); + + EXPECT_FALSE(PerformInjectiveMultimap(map, &failing)); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_path.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_path.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_path.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_path.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,318 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_path.h" +#include "base/logging.h" + +// These includes are just for the *Hack functions, and should be removed +// when those functions are removed. +#include "base/string_piece.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" + +#if defined(FILE_PATH_USES_WIN_SEPARATORS) +const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/"); +#else // FILE_PATH_USES_WIN_SEPARATORS +const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/"); +#endif // FILE_PATH_USES_WIN_SEPARATORS + +const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL("."); +const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL(".."); + +const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.'); + + +namespace { + +// If this FilePath contains a drive letter specification, returns the +// position of the last character of the drive letter specification, +// otherwise returns npos. This can only be true on Windows, when a pathname +// begins with a letter followed by a colon. On other platforms, this always +// returns npos. +FilePath::StringType::size_type FindDriveLetter( + const FilePath::StringType& path) { +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + // This is dependent on an ASCII-based character set, but that's a + // reasonable assumption. iswalpha can be too inclusive here. + if (path.length() >= 2 && path[1] == L':' && + ((path[0] >= L'A' && path[0] <= L'Z') || + (path[0] >= L'a' && path[0] <= L'z'))) { + return 1; + } +#endif // FILE_PATH_USES_DRIVE_LETTERS + return FilePath::StringType::npos; +} + +bool IsPathAbsolute(const FilePath::StringType& path) { +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + FilePath::StringType::size_type letter = FindDriveLetter(path); + if (letter != FilePath::StringType::npos) { + // Look for a separator right after the drive specification. + return path.length() > letter + 1 && + FilePath::IsSeparator(path[letter + 1]); + } + // Look for a pair of leading separators. + return path.length() > 1 && + FilePath::IsSeparator(path[0]) && FilePath::IsSeparator(path[1]); +#else // FILE_PATH_USES_DRIVE_LETTERS + // Look for a separator in the first position. + return path.length() > 0 && FilePath::IsSeparator(path[0]); +#endif // FILE_PATH_USES_DRIVE_LETTERS +} + +} // namespace + +bool FilePath::IsSeparator(CharType character) { + for (size_t i = 0; i < arraysize(kSeparators) - 1; ++i) { + if (character == kSeparators[i]) { + return true; + } + } + + return false; +} + +// libgen's dirname and basename aren't guaranteed to be thread-safe and aren't +// guaranteed to not modify their input strings, and in fact are implemented +// differently in this regard on different platforms. Don't use them, but +// adhere to their behavior. +FilePath FilePath::DirName() const { + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + // The drive letter, if any, always needs to remain in the output. If there + // is no drive letter, as will always be the case on platforms which do not + // support drive letters, letter will be npos, or -1, so the comparisons and + // resizes below using letter will still be valid. + StringType::size_type letter = FindDriveLetter(new_path.path_); + + StringType::size_type last_separator = + new_path.path_.find_last_of(kSeparators, StringType::npos, + arraysize(kSeparators) - 1); + if (last_separator == StringType::npos) { + // path_ is in the current directory. + new_path.path_.resize(letter + 1); + } else if (last_separator == letter + 1) { + // path_ is in the root directory. + new_path.path_.resize(letter + 2); + } else if (last_separator == letter + 2 && + IsSeparator(new_path.path_[letter + 1])) { + // path_ is in "//" (possibly with a drive letter); leave the double + // separator intact indicating alternate root. + new_path.path_.resize(letter + 3); + } else if (last_separator != 0) { + // path_ is somewhere else, trim the basename. + new_path.path_.resize(last_separator); + } + + new_path.StripTrailingSeparatorsInternal(); + if (!new_path.path_.length()) + new_path.path_ = kCurrentDirectory; + + return new_path; +} + +FilePath FilePath::BaseName() const { + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + // The drive letter, if any, is always stripped. + StringType::size_type letter = FindDriveLetter(new_path.path_); + if (letter != StringType::npos) { + new_path.path_.erase(0, letter + 1); + } + + // Keep everything after the final separator, but if the pathname is only + // one character and it's a separator, leave it alone. + StringType::size_type last_separator = + new_path.path_.find_last_of(kSeparators, StringType::npos, + arraysize(kSeparators) - 1); + if (last_separator != StringType::npos && + last_separator < new_path.path_.length() - 1) { + new_path.path_.erase(0, last_separator + 1); + } + + return new_path; +} + +FilePath::StringType FilePath::Extension() const { + // BaseName() calls StripTrailingSeparators, so cases like /foo.baz/// work. + StringType base = BaseName().value(); + + // Special case "." and ".." + if (base == kCurrentDirectory || base == kParentDirectory) + return StringType(); + + const StringType::size_type last_dot = base.rfind(kExtensionSeparator); + if (last_dot == StringType::npos) + return StringType(); + return StringType(base, last_dot); +} + +FilePath FilePath::RemoveExtension() const { + StringType ext = Extension(); + // It's important to check Extension() since that verifies that the + // kExtensionSeparator actually appeared in the last path component. + if (ext.empty()) + return FilePath(path_); + // Since Extension() verified that the extension is in fact in the last path + // component, this substr will effectively strip trailing separators. + const StringType::size_type last_dot = path_.rfind(kExtensionSeparator); + return FilePath(path_.substr(0, last_dot)); +} + +FilePath FilePath::InsertBeforeExtension(const StringType& suffix) const { + if (suffix.empty()) + return FilePath(path_); + + if (path_.empty()) + return FilePath(); + + StringType base = BaseName().value(); + if (base.empty()) + return FilePath(); + if (*(base.end() - 1) == kExtensionSeparator) { + // Special case "." and ".." + if (base == kCurrentDirectory || base == kParentDirectory) { + return FilePath(); + } + } + + StringType ext = Extension(); + StringType ret = RemoveExtension().value(); + ret.append(suffix); + ret.append(ext); + return FilePath(ret); +} + +FilePath FilePath::ReplaceExtension(const StringType& extension) const { + if (path_.empty()) + return FilePath(); + + StringType base = BaseName().value(); + if (base.empty()) + return FilePath(); + if (*(base.end() - 1) == kExtensionSeparator) { + // Special case "." and ".." + if (base == kCurrentDirectory || base == kParentDirectory) { + return FilePath(); + } + } + + FilePath no_ext = RemoveExtension(); + // If the new extension is "" or ".", then just remove the current extension. + if (extension.empty() || extension == StringType(1, kExtensionSeparator)) + return no_ext; + + StringType str = no_ext.value(); + if (extension[0] != kExtensionSeparator) + str.append(1, kExtensionSeparator); + str.append(extension); + return FilePath(str); +} + +FilePath FilePath::Append(const StringType& component) const { + DCHECK(!IsPathAbsolute(component)); + if (path_.compare(kCurrentDirectory) == 0) { + // Append normally doesn't do any normalization, but as a special case, + // when appending to kCurrentDirectory, just return a new path for the + // component argument. Appending component to kCurrentDirectory would + // serve no purpose other than needlessly lengthening the path, and + // it's likely in practice to wind up with FilePath objects containing + // only kCurrentDirectory when calling DirName on a single relative path + // component. + return FilePath(component); + } + + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + // Don't append a separator if the path is empty (indicating the current + // directory) or if the path component is empty (indicating nothing to + // append). + if (component.length() > 0 && new_path.path_.length() > 0) { + + // Don't append a separator if the path still ends with a trailing + // separator after stripping (indicating the root directory). + if (!IsSeparator(new_path.path_[new_path.path_.length() - 1])) { + + // Don't append a separator if the path is just a drive letter. + if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) { + new_path.path_.append(1, kSeparators[0]); + } + } + } + + new_path.path_.append(component); + return new_path; +} + +FilePath FilePath::Append(const FilePath& component) const { + return Append(component.value()); +} + +FilePath FilePath::AppendASCII(const std::string& component) const { + DCHECK(IsStringASCII(component)); +#if defined(OS_WIN) + return Append(ASCIIToWide(component)); +#elif defined(OS_POSIX) + return Append(component); +#endif +} + +bool FilePath::IsAbsolute() const { + return IsPathAbsolute(path_); +} + +#if defined(OS_POSIX) +// See file_path.h for a discussion of the encoding of paths on POSIX +// platforms. These *Hack() functions are not quite correct, but they're +// only temporary while we fix the remainder of the code. +// Remember to remove the #includes at the top when you remove these. + +// static +FilePath FilePath::FromWStringHack(const std::wstring& wstring) { + return FilePath(base::SysWideToNativeMB(wstring)); +} +std::wstring FilePath::ToWStringHack() const { + return base::SysNativeMBToWide(path_); +} +#elif defined(OS_WIN) +// static +FilePath FilePath::FromWStringHack(const std::wstring& wstring) { + return FilePath(wstring); +} +std::wstring FilePath::ToWStringHack() const { + return path_; +} +#endif + +FilePath FilePath::StripTrailingSeparators() const { + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + return new_path; +} + +void FilePath::StripTrailingSeparatorsInternal() { + // If there is no drive letter, start will be 1, which will prevent stripping + // the leading separator if there is only one separator. If there is a drive + // letter, start will be set appropriately to prevent stripping the first + // separator following the drive letter, if a separator immediately follows + // the drive letter. + StringType::size_type start = FindDriveLetter(path_) + 2; + + StringType::size_type last_stripped = StringType::npos; + for (StringType::size_type pos = path_.length(); + pos > start && IsSeparator(path_[pos - 1]); + --pos) { + // If the string only has two separators and they're at the beginning, + // don't strip them, unless the string began with more than two separators. + if (pos != start + 1 || last_stripped == start + 2 || + !IsSeparator(path_[start - 1])) { + path_.resize(pos - 1); + last_stripped = pos; + } + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_path.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_path.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_path.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_path.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,268 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// FilePath is a container for pathnames stored in a platform's native string +// type, providing containers for manipulation in according with the +// platform's conventions for pathnames. It supports the following path +// types: +// +// POSIX Windows +// --------------- ---------------------------------- +// Fundamental type char[] wchar_t[] +// Encoding unspecified* UTF-16 +// Separator / \, tolerant of / +// Drive letters no case-insensitive A-Z followed by : +// Alternate root // (surprise!) \\, for UNC paths +// +// * The encoding need not be specified on POSIX systems, although some +// POSIX-compliant systems do specify an encoding. Mac OS X uses UTF-8. +// Linux does not specify an encoding, but in practice, the locale's +// character set may be used. +// +// FilePath objects are intended to be used anywhere paths are. An +// application may pass FilePath objects around internally, masking the +// underlying differences between systems, only differing in implementation +// where interfacing directly with the system. For example, a single +// OpenFile(const FilePath &) function may be made available, allowing all +// callers to operate without regard to the underlying implementation. On +// POSIX-like platforms, OpenFile might wrap fopen, and on Windows, it might +// wrap _wfopen_s, perhaps both by calling file_path.value().c_str(). This +// allows each platform to pass pathnames around without requiring conversions +// between encodings, which has an impact on performance, but more imporantly, +// has an impact on correctness on platforms that do not have well-defined +// encodings for pathnames. +// +// Several methods are available to perform common operations on a FilePath +// object, such as determining the parent directory (DirName), isolating the +// final path component (BaseName), and appending a relative pathname string +// to an existing FilePath object (Append). These methods are highly +// recommended over attempting to split and concatenate strings directly. +// These methods are based purely on string manipulation and knowledge of +// platform-specific pathname conventions, and do not consult the filesystem +// at all, making them safe to use without fear of blocking on I/O operations. +// These methods do not function as mutators but instead return distinct +// instances of FilePath objects, and are therefore safe to use on const +// objects. The objects themselves are safe to share between threads. +// +// To aid in initialization of FilePath objects from string literals, a +// FILE_PATH_LITERAL macro is provided, which accounts for the difference +// between char[]-based pathnames on POSIX systems and wchar_t[]-based +// pathnames on Windows. +// +// Because a FilePath object should not be instantiated at the global scope, +// instead, use a FilePath::CharType[] and initialize it with +// FILE_PATH_LITERAL. At runtime, a FilePath object can be created from the +// character array. Example: +// +// | const FilePath::CharType kLogFileName[] = FILE_PATH_LITERAL("log.txt"); +// | +// | void Function() { +// | FilePath log_file_path(kLogFileName); +// | [...] +// | } + +#ifndef BASE_FILE_PATH_H_ +#define BASE_FILE_PATH_H_ + +#include + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/hash_tables.h" + +// Windows-style drive letter support and pathname separator characters can be +// enabled and disabled independently, to aid testing. These #defines are +// here so that the same setting can be used in both the implementation and +// in the unit test. +#if defined(OS_WIN) +#define FILE_PATH_USES_DRIVE_LETTERS +#define FILE_PATH_USES_WIN_SEPARATORS +#endif // OS_WIN + +// An abstraction to isolate users from the differences between native +// pathnames on different platforms. +class FilePath { + public: +#if defined(OS_POSIX) + // On most platforms, native pathnames are char arrays, and the encoding + // may or may not be specified. On Mac OS X, native pathnames are encoded + // in UTF-8. + typedef std::string StringType; +#elif defined(OS_WIN) + // On Windows, for Unicode-aware applications, native pathnames are wchar_t + // arrays encoded in UTF-16. + typedef std::wstring StringType; +#endif // OS_WIN + + typedef StringType::value_type CharType; + + // Null-terminated array of separators used to separate components in + // hierarchical paths. Each character in this array is a valid separator, + // but kSeparators[0] is treated as the canonical separator and will be used + // when composing pathnames. + static const CharType kSeparators[]; + + // A special path component meaning "this directory." + static const CharType kCurrentDirectory[]; + + // A special path component meaning "the parent directory." + static const CharType kParentDirectory[]; + + // The character used to identify a file extension. + static const CharType kExtensionSeparator; + + FilePath() {} + FilePath(const FilePath& that) : path_(that.path_) {} + explicit FilePath(const StringType& path) : path_(path) {} + + FilePath& operator=(const FilePath& that) { + path_ = that.path_; + return *this; + } + + bool operator==(const FilePath& that) const { + return path_ == that.path_; + } + + bool operator!=(const FilePath& that) const { + return path_ != that.path_; + } + + // Required for some STL containers and operations + bool operator<(const FilePath& that) const { + return path_ < that.path_; + } + + const StringType& value() const { return path_; } + + bool empty() const { return path_.empty(); } + + // Returns true if |character| is in kSeparators. + static bool IsSeparator(CharType character); + + // Returns a FilePath corresponding to the directory containing the path + // named by this object, stripping away the file component. If this object + // only contains one component, returns a FilePath identifying + // kCurrentDirectory. If this object already refers to the root directory, + // returns a FilePath identifying the root directory. + FilePath DirName() const; + + // Returns a FilePath corresponding to the last path component of this + // object, either a file or a directory. If this object already refers to + // the root directory, returns a FilePath identifying the root directory; + // this is the only situation in which BaseName will return an absolute path. + FilePath BaseName() const; + + // Returns ".jpg" for path "C:\pics\jojo.jpg", or an empty string if + // the file has no extension. If non-empty, Extension() will always start + // with precisely one ".". The following code should always work regardless + // of the value of path. + // new_path = path.RemoveExtension().value().append(path.Extension()); + // ASSERT(new_path == path.value()); + // NOTE: this is different from the original file_util implementation which + // returned the extension without a leading "." ("jpg" instead of ".jpg") + StringType Extension() const; + + // Returns "C:\pics\jojo" for path "C:\pics\jojo.jpg" + // NOTE: this is slightly different from the similar file_util implementation + // which returned simply 'jojo'. + FilePath RemoveExtension() const; + + // Inserts |suffix| after the file name portion of |path| but before the + // extension. Returns "" if BaseName() == "." or "..". + // Examples: + // path == "C:\pics\jojo.jpg" suffix == " (1)", returns "C:\pics\jojo (1).jpg" + // path == "jojo.jpg" suffix == " (1)", returns "jojo (1).jpg" + // path == "C:\pics\jojo" suffix == " (1)", returns "C:\pics\jojo (1)" + // path == "C:\pics.old\jojo" suffix == " (1)", returns "C:\pics.old\jojo (1)" + FilePath InsertBeforeExtension(const StringType& suffix) const; + + // Replaces the extension of |file_name| with |extension|. If |file_name| + // does not have an extension, them |extension| is added. If |extension| is + // empty, then the extension is removed from |file_name|. + // Returns "" if BaseName() == "." or "..". + FilePath ReplaceExtension(const StringType& extension) const; + + // Returns a FilePath by appending a separator and the supplied path + // component to this object's path. Append takes care to avoid adding + // excessive separators if this object's path already ends with a separator. + // If this object's path is kCurrentDirectory, a new FilePath corresponding + // only to |component| is returned. |component| must be a relative path; + // it is an error to pass an absolute path. + FilePath Append(const StringType& component) const WARN_UNUSED_RESULT; + FilePath Append(const FilePath& component) const WARN_UNUSED_RESULT; + + // Although Windows StringType is std::wstring, since the encoding it uses for + // paths is well defined, it can handle ASCII path components as well. + // Mac uses UTF8, and since ASCII is a subset of that, it works there as well. + // On Linux, although it can use any 8-bit encoding for paths, we assume that + // ASCII is a valid subset, regardless of the encoding, since many operating + // system paths will always be ASCII. + FilePath AppendASCII(const std::string& component) const WARN_UNUSED_RESULT; + + // Returns true if this FilePath contains an absolute path. On Windows, an + // absolute path begins with either a drive letter specification followed by + // a separator character, or with two separator characters. On POSIX + // platforms, an absolute path begins with a separator character. + bool IsAbsolute() const; + + // Returns a copy of this FilePath that does not end with a trailing + // separator. + FilePath StripTrailingSeparators() const; + + // Older Chromium code assumes that paths are always wstrings. + // This function converts a wstring to a FilePath, and is useful to smooth + // porting that old code to the FilePath API. + // It has "Hack" in its name so people feel bad about using it. + // TODO(port): remove these functions. + static FilePath FromWStringHack(const std::wstring& wstring); + + // Older Chromium code assumes that paths are always wstrings. + // This function produces a wstring from a FilePath, and is useful to smooth + // porting that old code to the FilePath API. + // It has "Hack" in its name so people feel bad about using it. + // TODO(port): remove these functions. + std::wstring ToWStringHack() const; + + private: + // Remove trailing separators from this object. If the path is absolute, it + // will never be stripped any more than to refer to the absolute root + // directory, so "////" will become "/", not "". A leading pair of + // separators is never stripped, to support alternate roots. This is used to + // support UNC paths on Windows. + void StripTrailingSeparatorsInternal(); + + StringType path_; +}; + +// Macros for string literal initialization of FilePath::CharType[]. +#if defined(OS_POSIX) +#define FILE_PATH_LITERAL(x) x +#elif defined(OS_WIN) +#define FILE_PATH_LITERAL(x) L ## x +#endif // OS_WIN + +// Implement hash function so that we can use FilePaths in hashsets and maps. +#if defined(COMPILER_GCC) +namespace __gnu_cxx { + +template<> +struct hash { + size_t operator()(const FilePath& f) const { + return hash()(f.value()); + } +}; + +} // namespace __gnu_cxx +#elif defined(COMPILER_MSVC) +namespace stdext { + +inline size_t hash_value(const FilePath& f) { + return hash_value(f.value()); +} + +} // namespace stdext +#endif // COMPILER + +#endif // BASE_FILE_PATH_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_path_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_path_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_path_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_path_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,534 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/string_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +// This macro helps avoid wrapped lines in the test structs. +#define FPL(x) FILE_PATH_LITERAL(x) + +struct UnaryTestData { + const FilePath::CharType* input; + const FilePath::CharType* expected; +}; + +struct UnaryBooleanTestData { + const FilePath::CharType* input; + bool expected; +}; + +struct BinaryTestData { + const FilePath::CharType* inputs[2]; + const FilePath::CharType* expected; +}; + +// file_util winds up using autoreleased objects on the Mac, so this needs +// to be a PlatformTest +class FilePathTest : public PlatformTest { + protected: + virtual void SetUp() { + PlatformTest::SetUp(); + } + virtual void TearDown() { + PlatformTest::TearDown(); + } +}; + +TEST_F(FilePathTest, DirName) { + const struct UnaryTestData cases[] = { + { FPL(""), FPL(".") }, + { FPL("aa"), FPL(".") }, + { FPL("/aa/bb"), FPL("/aa") }, + { FPL("/aa/bb/"), FPL("/aa") }, + { FPL("/aa/bb//"), FPL("/aa") }, + { FPL("/aa/bb/ccc"), FPL("/aa/bb") }, + { FPL("/aa"), FPL("/") }, + { FPL("/aa/"), FPL("/") }, + { FPL("/"), FPL("/") }, + { FPL("//"), FPL("//") }, + { FPL("///"), FPL("/") }, + { FPL("aa/"), FPL(".") }, + { FPL("aa/bb"), FPL("aa") }, + { FPL("aa/bb/"), FPL("aa") }, + { FPL("aa/bb//"), FPL("aa") }, + { FPL("aa//bb//"), FPL("aa") }, + { FPL("aa//bb/"), FPL("aa") }, + { FPL("aa//bb"), FPL("aa") }, + { FPL("//aa/bb"), FPL("//aa") }, + { FPL("//aa/"), FPL("//") }, + { FPL("//aa"), FPL("//") }, + { FPL("0:"), FPL(".") }, + { FPL("@:"), FPL(".") }, + { FPL("[:"), FPL(".") }, + { FPL("`:"), FPL(".") }, + { FPL("{:"), FPL(".") }, + { FPL("\xB3:"), FPL(".") }, + { FPL("\xC5:"), FPL(".") }, +#if defined(OS_WIN) + { FPL("\x0143:"), FPL(".") }, +#endif // OS_WIN +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { FPL("c:"), FPL("c:") }, + { FPL("C:"), FPL("C:") }, + { FPL("A:"), FPL("A:") }, + { FPL("Z:"), FPL("Z:") }, + { FPL("a:"), FPL("a:") }, + { FPL("z:"), FPL("z:") }, + { FPL("c:aa"), FPL("c:") }, + { FPL("c:/"), FPL("c:/") }, + { FPL("c://"), FPL("c://") }, + { FPL("c:///"), FPL("c:/") }, + { FPL("c:/aa"), FPL("c:/") }, + { FPL("c:/aa/"), FPL("c:/") }, + { FPL("c:/aa/bb"), FPL("c:/aa") }, + { FPL("c:aa/bb"), FPL("c:aa") }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#if defined(FILE_PATH_USES_WIN_SEPARATORS) + { FPL("\\aa\\bb"), FPL("\\aa") }, + { FPL("\\aa\\bb\\"), FPL("\\aa") }, + { FPL("\\aa\\bb\\\\"), FPL("\\aa") }, + { FPL("\\aa\\bb\\ccc"), FPL("\\aa\\bb") }, + { FPL("\\aa"), FPL("\\") }, + { FPL("\\aa\\"), FPL("\\") }, + { FPL("\\"), FPL("\\") }, + { FPL("\\\\"), FPL("\\\\") }, + { FPL("\\\\\\"), FPL("\\") }, + { FPL("aa\\"), FPL(".") }, + { FPL("aa\\bb"), FPL("aa") }, + { FPL("aa\\bb\\"), FPL("aa") }, + { FPL("aa\\bb\\\\"), FPL("aa") }, + { FPL("aa\\\\bb\\\\"), FPL("aa") }, + { FPL("aa\\\\bb\\"), FPL("aa") }, + { FPL("aa\\\\bb"), FPL("aa") }, + { FPL("\\\\aa\\bb"), FPL("\\\\aa") }, + { FPL("\\\\aa\\"), FPL("\\\\") }, + { FPL("\\\\aa"), FPL("\\\\") }, +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { FPL("c:\\"), FPL("c:\\") }, + { FPL("c:\\\\"), FPL("c:\\\\") }, + { FPL("c:\\\\\\"), FPL("c:\\") }, + { FPL("c:\\aa"), FPL("c:\\") }, + { FPL("c:\\aa\\"), FPL("c:\\") }, + { FPL("c:\\aa\\bb"), FPL("c:\\aa") }, + { FPL("c:aa\\bb"), FPL("c:aa") }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#endif // FILE_PATH_USES_WIN_SEPARATORS + }; + + for (size_t i = 0; i < arraysize(cases); ++i) { + FilePath input(cases[i].input); + FilePath observed = input.DirName(); + EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) << + "i: " << i << ", input: " << input.value(); + } +} + +TEST_F(FilePathTest, BaseName) { + const struct UnaryTestData cases[] = { + { FPL(""), FPL("") }, + { FPL("aa"), FPL("aa") }, + { FPL("/aa/bb"), FPL("bb") }, + { FPL("/aa/bb/"), FPL("bb") }, + { FPL("/aa/bb//"), FPL("bb") }, + { FPL("/aa/bb/ccc"), FPL("ccc") }, + { FPL("/aa"), FPL("aa") }, + { FPL("/"), FPL("/") }, + { FPL("//"), FPL("//") }, + { FPL("///"), FPL("/") }, + { FPL("aa/"), FPL("aa") }, + { FPL("aa/bb"), FPL("bb") }, + { FPL("aa/bb/"), FPL("bb") }, + { FPL("aa/bb//"), FPL("bb") }, + { FPL("aa//bb//"), FPL("bb") }, + { FPL("aa//bb/"), FPL("bb") }, + { FPL("aa//bb"), FPL("bb") }, + { FPL("//aa/bb"), FPL("bb") }, + { FPL("//aa/"), FPL("aa") }, + { FPL("//aa"), FPL("aa") }, + { FPL("0:"), FPL("0:") }, + { FPL("@:"), FPL("@:") }, + { FPL("[:"), FPL("[:") }, + { FPL("`:"), FPL("`:") }, + { FPL("{:"), FPL("{:") }, + { FPL("\xB3:"), FPL("\xB3:") }, + { FPL("\xC5:"), FPL("\xC5:") }, +#if defined(OS_WIN) + { FPL("\x0143:"), FPL("\x0143:") }, +#endif // OS_WIN +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { FPL("c:"), FPL("") }, + { FPL("C:"), FPL("") }, + { FPL("A:"), FPL("") }, + { FPL("Z:"), FPL("") }, + { FPL("a:"), FPL("") }, + { FPL("z:"), FPL("") }, + { FPL("c:aa"), FPL("aa") }, + { FPL("c:/"), FPL("/") }, + { FPL("c://"), FPL("//") }, + { FPL("c:///"), FPL("/") }, + { FPL("c:/aa"), FPL("aa") }, + { FPL("c:/aa/"), FPL("aa") }, + { FPL("c:/aa/bb"), FPL("bb") }, + { FPL("c:aa/bb"), FPL("bb") }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#if defined(FILE_PATH_USES_WIN_SEPARATORS) + { FPL("\\aa\\bb"), FPL("bb") }, + { FPL("\\aa\\bb\\"), FPL("bb") }, + { FPL("\\aa\\bb\\\\"), FPL("bb") }, + { FPL("\\aa\\bb\\ccc"), FPL("ccc") }, + { FPL("\\aa"), FPL("aa") }, + { FPL("\\"), FPL("\\") }, + { FPL("\\\\"), FPL("\\\\") }, + { FPL("\\\\\\"), FPL("\\") }, + { FPL("aa\\"), FPL("aa") }, + { FPL("aa\\bb"), FPL("bb") }, + { FPL("aa\\bb\\"), FPL("bb") }, + { FPL("aa\\bb\\\\"), FPL("bb") }, + { FPL("aa\\\\bb\\\\"), FPL("bb") }, + { FPL("aa\\\\bb\\"), FPL("bb") }, + { FPL("aa\\\\bb"), FPL("bb") }, + { FPL("\\\\aa\\bb"), FPL("bb") }, + { FPL("\\\\aa\\"), FPL("aa") }, + { FPL("\\\\aa"), FPL("aa") }, +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { FPL("c:\\"), FPL("\\") }, + { FPL("c:\\\\"), FPL("\\\\") }, + { FPL("c:\\\\\\"), FPL("\\") }, + { FPL("c:\\aa"), FPL("aa") }, + { FPL("c:\\aa\\"), FPL("aa") }, + { FPL("c:\\aa\\bb"), FPL("bb") }, + { FPL("c:aa\\bb"), FPL("bb") }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#endif // FILE_PATH_USES_WIN_SEPARATORS + }; + + for (size_t i = 0; i < arraysize(cases); ++i) { + FilePath input(cases[i].input); + FilePath observed = input.BaseName(); + EXPECT_EQ(FilePath::StringType(cases[i].expected), observed.value()) << + "i: " << i << ", input: " << input.value(); + } +} + +TEST_F(FilePathTest, Append) { + const struct BinaryTestData cases[] = { + { { FPL(""), FPL("cc") }, FPL("cc") }, + { { FPL("."), FPL("ff") }, FPL("ff") }, + { { FPL("/"), FPL("cc") }, FPL("/cc") }, + { { FPL("/aa"), FPL("") }, FPL("/aa") }, + { { FPL("/aa/"), FPL("") }, FPL("/aa") }, + { { FPL("//aa"), FPL("") }, FPL("//aa") }, + { { FPL("//aa/"), FPL("") }, FPL("//aa") }, + { { FPL("//"), FPL("aa") }, FPL("//aa") }, +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { { FPL("c:"), FPL("a") }, FPL("c:a") }, + { { FPL("c:"), FPL("") }, FPL("c:") }, + { { FPL("c:/"), FPL("a") }, FPL("c:/a") }, + { { FPL("c://"), FPL("a") }, FPL("c://a") }, + { { FPL("c:///"), FPL("a") }, FPL("c:/a") }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#if defined(FILE_PATH_USES_WIN_SEPARATORS) + // Append introduces the default separator character, so these test cases + // need to be defined with different expected results on platforms that use + // different default separator characters. + { { FPL("\\"), FPL("cc") }, FPL("\\cc") }, + { { FPL("\\aa"), FPL("") }, FPL("\\aa") }, + { { FPL("\\aa\\"), FPL("") }, FPL("\\aa") }, + { { FPL("\\\\aa"), FPL("") }, FPL("\\\\aa") }, + { { FPL("\\\\aa\\"), FPL("") }, FPL("\\\\aa") }, + { { FPL("\\\\"), FPL("aa") }, FPL("\\\\aa") }, + { { FPL("/aa/bb"), FPL("cc") }, FPL("/aa/bb\\cc") }, + { { FPL("/aa/bb/"), FPL("cc") }, FPL("/aa/bb\\cc") }, + { { FPL("aa/bb/"), FPL("cc") }, FPL("aa/bb\\cc") }, + { { FPL("aa/bb"), FPL("cc") }, FPL("aa/bb\\cc") }, + { { FPL("a/b"), FPL("c") }, FPL("a/b\\c") }, + { { FPL("a/b/"), FPL("c") }, FPL("a/b\\c") }, + { { FPL("//aa"), FPL("bb") }, FPL("//aa\\bb") }, + { { FPL("//aa/"), FPL("bb") }, FPL("//aa\\bb") }, + { { FPL("\\aa\\bb"), FPL("cc") }, FPL("\\aa\\bb\\cc") }, + { { FPL("\\aa\\bb\\"), FPL("cc") }, FPL("\\aa\\bb\\cc") }, + { { FPL("aa\\bb\\"), FPL("cc") }, FPL("aa\\bb\\cc") }, + { { FPL("aa\\bb"), FPL("cc") }, FPL("aa\\bb\\cc") }, + { { FPL("a\\b"), FPL("c") }, FPL("a\\b\\c") }, + { { FPL("a\\b\\"), FPL("c") }, FPL("a\\b\\c") }, + { { FPL("\\\\aa"), FPL("bb") }, FPL("\\\\aa\\bb") }, + { { FPL("\\\\aa\\"), FPL("bb") }, FPL("\\\\aa\\bb") }, +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { { FPL("c:\\"), FPL("a") }, FPL("c:\\a") }, + { { FPL("c:\\\\"), FPL("a") }, FPL("c:\\\\a") }, + { { FPL("c:\\\\\\"), FPL("a") }, FPL("c:\\a") }, + { { FPL("c:\\"), FPL("") }, FPL("c:\\") }, + { { FPL("c:\\a"), FPL("b") }, FPL("c:\\a\\b") }, + { { FPL("c:\\a\\"), FPL("b") }, FPL("c:\\a\\b") }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#else // FILE_PATH_USES_WIN_SEPARATORS + { { FPL("/aa/bb"), FPL("cc") }, FPL("/aa/bb/cc") }, + { { FPL("/aa/bb/"), FPL("cc") }, FPL("/aa/bb/cc") }, + { { FPL("aa/bb/"), FPL("cc") }, FPL("aa/bb/cc") }, + { { FPL("aa/bb"), FPL("cc") }, FPL("aa/bb/cc") }, + { { FPL("a/b"), FPL("c") }, FPL("a/b/c") }, + { { FPL("a/b/"), FPL("c") }, FPL("a/b/c") }, + { { FPL("//aa"), FPL("bb") }, FPL("//aa/bb") }, + { { FPL("//aa/"), FPL("bb") }, FPL("//aa/bb") }, +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { { FPL("c:/"), FPL("a") }, FPL("c:/a") }, + { { FPL("c:/"), FPL("") }, FPL("c:/") }, + { { FPL("c:/a"), FPL("b") }, FPL("c:/a/b") }, + { { FPL("c:/a/"), FPL("b") }, FPL("c:/a/b") }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#endif // FILE_PATH_USES_WIN_SEPARATORS + }; + + for (size_t i = 0; i < arraysize(cases); ++i) { + FilePath root(cases[i].inputs[0]); + FilePath::StringType leaf(cases[i].inputs[1]); + FilePath observed_str = root.Append(leaf); + EXPECT_EQ(FilePath::StringType(cases[i].expected), observed_str.value()) << + "i: " << i << ", root: " << root.value() << ", leaf: " << leaf; + FilePath observed_path = root.Append(FilePath(leaf)); + EXPECT_EQ(FilePath::StringType(cases[i].expected), observed_path.value()) << + "i: " << i << ", root: " << root.value() << ", leaf: " << leaf; + + // TODO(erikkay): It would be nice to have a unicode test append value to + // handle the case when AppendASCII is passed UTF8 +#if defined(OS_WIN) + std::string ascii = WideToASCII(leaf); +#elif defined(OS_POSIX) + std::string ascii = leaf; +#endif + observed_str = root.AppendASCII(ascii); + EXPECT_EQ(FilePath::StringType(cases[i].expected), observed_str.value()) << + "i: " << i << ", root: " << root.value() << ", leaf: " << leaf; + } +} + +TEST_F(FilePathTest, IsAbsolute) { + const struct UnaryBooleanTestData cases[] = { + { FPL(""), false }, + { FPL("a"), false }, + { FPL("c:"), false }, + { FPL("c:a"), false }, + { FPL("a/b"), false }, + { FPL("//"), true }, + { FPL("//a"), true }, + { FPL("c:a/b"), false }, + { FPL("?:/a"), false }, +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { FPL("/"), false }, + { FPL("/a"), false }, + { FPL("/."), false }, + { FPL("/.."), false }, + { FPL("c:/"), true }, + { FPL("c:/a"), true }, + { FPL("c:/."), true }, + { FPL("c:/.."), true }, + { FPL("C:/a"), true }, + { FPL("d:/a"), true }, +#else // FILE_PATH_USES_DRIVE_LETTERS + { FPL("/"), true }, + { FPL("/a"), true }, + { FPL("/."), true }, + { FPL("/.."), true }, + { FPL("c:/"), false }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#if defined(FILE_PATH_USES_WIN_SEPARATORS) + { FPL("a\\b"), false }, + { FPL("\\\\"), true }, + { FPL("\\\\a"), true }, + { FPL("a\\b"), false }, + { FPL("\\\\"), true }, + { FPL("//a"), true }, + { FPL("c:a\\b"), false }, + { FPL("?:\\a"), false }, +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + { FPL("\\"), false }, + { FPL("\\a"), false }, + { FPL("\\."), false }, + { FPL("\\.."), false }, + { FPL("c:\\"), true }, + { FPL("c:\\"), true }, + { FPL("c:\\a"), true }, + { FPL("c:\\."), true }, + { FPL("c:\\.."), true }, + { FPL("C:\\a"), true }, + { FPL("d:\\a"), true }, +#else // FILE_PATH_USES_DRIVE_LETTERS + { FPL("\\"), true }, + { FPL("\\a"), true }, + { FPL("\\."), true }, + { FPL("\\.."), true }, + { FPL("c:\\"), false }, +#endif // FILE_PATH_USES_DRIVE_LETTERS +#endif // FILE_PATH_USES_WIN_SEPARATORS + }; + + for (size_t i = 0; i < arraysize(cases); ++i) { + FilePath input(cases[i].input); + bool observed = input.IsAbsolute(); + EXPECT_EQ(cases[i].expected, observed) << + "i: " << i << ", input: " << input.value(); + } +} + +TEST_F(FilePathTest, Extension) { + FilePath base_dir(FILE_PATH_LITERAL("base_dir")); + + FilePath jpg = base_dir.Append(FILE_PATH_LITERAL("foo.jpg")); + EXPECT_EQ(jpg.Extension(), FILE_PATH_LITERAL(".jpg")); + + FilePath base = jpg.BaseName().RemoveExtension(); + EXPECT_EQ(base.value(), FILE_PATH_LITERAL("foo")); + + FilePath path_no_ext = base_dir.Append(base); + EXPECT_EQ(jpg.RemoveExtension().value(), path_no_ext.value()); + + EXPECT_EQ(path_no_ext.value(), path_no_ext.RemoveExtension().value()); + EXPECT_EQ(path_no_ext.Extension(), FILE_PATH_LITERAL("")); +} + +TEST_F(FilePathTest, Extension2) { + const struct UnaryTestData cases[] = { +#if defined(FILE_PATH_USES_WIN_SEPARATORS) + { FPL("C:\\a\\b\\c.ext"), FPL(".ext") }, + { FPL("C:\\a\\b\\c."), FPL(".") }, + { FPL("C:\\a\\b\\c"), FPL("") }, + { FPL("C:\\a\\b\\"), FPL("") }, + { FPL("C:\\a\\b.\\"), FPL(".") }, + { FPL("C:\\a\\b\\c.ext1.ext2"), FPL(".ext2") }, + { FPL("C:\\foo.bar\\\\\\"), FPL(".bar") }, + { FPL("C:\\foo.bar\\.."), FPL("") }, + { FPL("C:\\foo.bar\\..\\\\"), FPL("") }, +#endif + { FPL("/foo/bar/baz.ext"), FPL(".ext") }, + { FPL("/foo/bar/baz."), FPL(".") }, + { FPL("/foo/bar/baz.."), FPL(".") }, + { FPL("/foo/bar/baz"), FPL("") }, + { FPL("/foo/bar/"), FPL("") }, + { FPL("/foo/bar./"), FPL(".") }, + { FPL("/foo/bar/baz.ext1.ext2"), FPL(".ext2") }, + { FPL("."), FPL("") }, + { FPL(".."), FPL("") }, + { FPL("./foo"), FPL("") }, + { FPL("./foo.ext"), FPL(".ext") }, + { FPL("/foo.ext1/bar.ext2"), FPL(".ext2") }, + { FPL("/foo.bar////"), FPL(".bar") }, + { FPL("/foo.bar/.."), FPL("") }, + { FPL("/foo.bar/..////"), FPL("") }, + }; + for (unsigned int i = 0; i < arraysize(cases); ++i) { + FilePath path(cases[i].input); + FilePath::StringType extension = path.Extension(); + EXPECT_STREQ(cases[i].expected, extension.c_str()) << "i: " << i << + ", path: " << path.value(); + } +} + +TEST_F(FilePathTest, InsertBeforeExtension) { + const struct BinaryTestData cases[] = { + { { FPL(""), FPL("") }, FPL("") }, + { { FPL(""), FPL("txt") }, FPL("") }, + { { FPL("."), FPL("txt") }, FPL("") }, + { { FPL(".."), FPL("txt") }, FPL("") }, + { { FPL("foo.dll"), FPL("txt") }, FPL("footxt.dll") }, + { { FPL("."), FPL("") }, FPL(".") }, + { { FPL("foo.dll"), FPL(".txt") }, FPL("foo.txt.dll") }, + { { FPL("foo"), FPL("txt") }, FPL("footxt") }, + { { FPL("foo"), FPL(".txt") }, FPL("foo.txt") }, + { { FPL("foo.baz.dll"), FPL("txt") }, FPL("foo.baztxt.dll") }, + { { FPL("foo.baz.dll"), FPL(".txt") }, FPL("foo.baz.txt.dll") }, + { { FPL("foo.dll"), FPL("") }, FPL("foo.dll") }, + { { FPL("foo.dll"), FPL(".") }, FPL("foo..dll") }, + { { FPL("foo"), FPL("") }, FPL("foo") }, + { { FPL("foo"), FPL(".") }, FPL("foo.") }, + { { FPL("foo.baz.dll"), FPL("") }, FPL("foo.baz.dll") }, + { { FPL("foo.baz.dll"), FPL(".") }, FPL("foo.baz..dll") }, +#if defined(FILE_PATH_USES_WIN_SEPARATORS) + { { FPL("\\"), FPL("") }, FPL("\\") }, + { { FPL("\\"), FPL("txt") }, FPL("\\txt") }, + { { FPL("\\."), FPL("txt") }, FPL("") }, + { { FPL("\\.."), FPL("txt") }, FPL("") }, + { { FPL("\\."), FPL("") }, FPL("\\.") }, + { { FPL("C:\\bar\\foo.dll"), FPL("txt") }, + FPL("C:\\bar\\footxt.dll") }, + { { FPL("C:\\bar.baz\\foodll"), FPL("txt") }, + FPL("C:\\bar.baz\\foodlltxt") }, + { { FPL("C:\\bar.baz\\foo.dll"), FPL("txt") }, + FPL("C:\\bar.baz\\footxt.dll") }, + { { FPL("C:\\bar.baz\\foo.dll.exe"), FPL("txt") }, + FPL("C:\\bar.baz\\foo.dlltxt.exe") }, + { { FPL("C:\\bar.baz\\foo"), FPL("") }, + FPL("C:\\bar.baz\\foo") }, + { { FPL("C:\\bar.baz\\foo.exe"), FPL("") }, + FPL("C:\\bar.baz\\foo.exe") }, + { { FPL("C:\\bar.baz\\foo.dll.exe"), FPL("") }, + FPL("C:\\bar.baz\\foo.dll.exe") }, + { { FPL("C:\\bar\\baz\\foo.exe"), FPL(" (1)") }, + FPL("C:\\bar\\baz\\foo (1).exe") }, + { { FPL("C:\\foo.baz\\\\"), FPL(" (1)") }, FPL("C:\\foo (1).baz") }, + { { FPL("C:\\foo.baz\\..\\"), FPL(" (1)") }, FPL("") }, +#endif + { { FPL("/"), FPL("") }, FPL("/") }, + { { FPL("/"), FPL("txt") }, FPL("/txt") }, + { { FPL("/."), FPL("txt") }, FPL("") }, + { { FPL("/.."), FPL("txt") }, FPL("") }, + { { FPL("/."), FPL("") }, FPL("/.") }, + { { FPL("/bar/foo.dll"), FPL("txt") }, FPL("/bar/footxt.dll") }, + { { FPL("/bar.baz/foodll"), FPL("txt") }, FPL("/bar.baz/foodlltxt") }, + { { FPL("/bar.baz/foo.dll"), FPL("txt") }, FPL("/bar.baz/footxt.dll") }, + { { FPL("/bar.baz/foo.dll.exe"), FPL("txt") }, + FPL("/bar.baz/foo.dlltxt.exe") }, + { { FPL("/bar.baz/foo"), FPL("") }, FPL("/bar.baz/foo") }, + { { FPL("/bar.baz/foo.exe"), FPL("") }, FPL("/bar.baz/foo.exe") }, + { { FPL("/bar.baz/foo.dll.exe"), FPL("") }, FPL("/bar.baz/foo.dll.exe") }, + { { FPL("/bar/baz/foo.exe"), FPL(" (1)") }, FPL("/bar/baz/foo (1).exe") }, + { { FPL("/bar/baz/..////"), FPL(" (1)") }, FPL("") }, + }; + for (unsigned int i = 0; i < arraysize(cases); ++i) { + FilePath path(cases[i].inputs[0]); + FilePath result = path.InsertBeforeExtension(cases[i].inputs[1]); + EXPECT_EQ(cases[i].expected, result.value()) << "i: " << i << + ", path: " << path.value() << ", insert: " << cases[i].inputs[1]; + } +} + +TEST_F(FilePathTest, ReplaceExtension) { + const struct BinaryTestData cases[] = { + { { FPL(""), FPL("") }, FPL("") }, + { { FPL(""), FPL("txt") }, FPL("") }, + { { FPL("."), FPL("txt") }, FPL("") }, + { { FPL(".."), FPL("txt") }, FPL("") }, + { { FPL("."), FPL("") }, FPL("") }, + { { FPL("foo.dll"), FPL("txt") }, FPL("foo.txt") }, + { { FPL("foo..dll"), FPL("txt") }, FPL("foo..txt") }, + { { FPL("foo.dll"), FPL(".txt") }, FPL("foo.txt") }, + { { FPL("foo"), FPL("txt") }, FPL("foo.txt") }, + { { FPL("foo."), FPL("txt") }, FPL("foo.txt") }, + { { FPL("foo.."), FPL("txt") }, FPL("foo..txt") }, + { { FPL("foo"), FPL(".txt") }, FPL("foo.txt") }, + { { FPL("foo.baz.dll"), FPL("txt") }, FPL("foo.baz.txt") }, + { { FPL("foo.baz.dll"), FPL(".txt") }, FPL("foo.baz.txt") }, + { { FPL("foo.dll"), FPL("") }, FPL("foo") }, + { { FPL("foo.dll"), FPL(".") }, FPL("foo") }, + { { FPL("foo"), FPL("") }, FPL("foo") }, + { { FPL("foo"), FPL(".") }, FPL("foo") }, + { { FPL("foo.baz.dll"), FPL("") }, FPL("foo.baz") }, + { { FPL("foo.baz.dll"), FPL(".") }, FPL("foo.baz") }, +#if defined(FILE_PATH_USES_WIN_SEPARATORS) + { { FPL("C:\\foo.bar\\foo"), FPL("baz") }, FPL("C:\\foo.bar\\foo.baz") }, + { { FPL("C:\\foo.bar\\..\\\\"), FPL("baz") }, FPL("") }, +#endif + { { FPL("/foo.bar/foo"), FPL("baz") }, FPL("/foo.bar/foo.baz") }, + { { FPL("/foo.bar/..////"), FPL("baz") }, FPL("") }, + }; + for (unsigned int i = 0; i < arraysize(cases); ++i) { + FilePath path(cases[i].inputs[0]); + FilePath replaced = path.ReplaceExtension(cases[i].inputs[1]); + EXPECT_EQ(cases[i].expected, replaced.value()) << "i: " << i << + ", path: " << path.value() << ", replace: " << cases[i].inputs[1]; + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_util.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_util.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_util.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_util.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,429 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_util.h" + +#if defined(OS_WIN) +#include +#endif +#include + +#include + +#include "base/file_path.h" +#include "base/logging.h" +#include "base/string_util.h" + +#include "base/string_piece.h" +#include "base/sys_string_conversions.h" + +namespace { + +const FilePath::CharType kExtensionSeparator = FILE_PATH_LITERAL('.'); + +} // namespace + +namespace file_util { + +void PathComponents(const FilePath& path, + std::vector* components) { + DCHECK(components); + if (!components) + return; + + FilePath::StringType path_str = path.value(); + FilePath::StringType::size_type start = 0; + FilePath::StringType::size_type end = + path_str.find_first_of(FilePath::kSeparators); + + // If the path starts with a separator, add it to components. + if (end == start) { + components->push_back(FilePath::StringType(path_str, 0, 1)); + start = end + 1; + end = path_str.find_first_of(FilePath::kSeparators, start); + } + while (end != FilePath::StringType::npos) { + FilePath::StringType component = + FilePath::StringType(path_str, start, end - start); + components->push_back(component); + start = end + 1; + end = path_str.find_first_of(FilePath::kSeparators, start); + } + + components->push_back(FilePath::StringType(path_str, start)); +} + +bool EndsWithSeparator(const FilePath& path) { + FilePath::StringType value = path.value(); + if (value.empty()) + return false; + + return FilePath::IsSeparator(value[value.size() - 1]); +} + +bool EnsureEndsWithSeparator(FilePath* path) { + if (!DirectoryExists(*path)) + return false; + + if (EndsWithSeparator(*path)) + return true; + + FilePath::StringType& path_str = + const_cast(path->value()); + path_str.append(&FilePath::kSeparators[0], 1); + + return true; +} + +void TrimTrailingSeparator(std::wstring* dir) { + while (dir->length() > 1 && EndsWithSeparator(dir)) + dir->resize(dir->length() - 1); +} + +FilePath::StringType GetFileExtensionFromPath(const FilePath& path) { + FilePath::StringType file_name = path.BaseName().value(); + const FilePath::StringType::size_type last_dot = + file_name.rfind(kExtensionSeparator); + return FilePath::StringType(last_dot == FilePath::StringType::npos ? + FILE_PATH_LITERAL("") : + file_name, last_dot+1); +} + +void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix) { + FilePath::StringType& value = + const_cast(path->value()); + + const FilePath::StringType::size_type last_dot = + value.rfind(kExtensionSeparator); + const FilePath::StringType::size_type last_separator = + value.find_last_of(FilePath::StringType(FilePath::kSeparators)); + + if (last_dot == FilePath::StringType::npos || + (last_separator != std::wstring::npos && last_dot < last_separator)) { + // The path looks something like "C:\pics.old\jojo" or "C:\pics\jojo". + // We should just append the suffix to the entire path. + value.append(suffix); + return; + } + + value.insert(last_dot, suffix); +} + +void ReplaceExtension(FilePath* path, const FilePath::StringType& extension) { + FilePath::StringType clean_extension; + // If the new extension is "" or ".", then we will just remove the current + // extension. + if (!extension.empty() && + extension != FilePath::StringType(&kExtensionSeparator, 1)) { + if (extension[0] != kExtensionSeparator) + clean_extension.append(&kExtensionSeparator, 1); + clean_extension.append(extension); + } + + FilePath::StringType& value = + const_cast(path->value()); + const FilePath::StringType::size_type last_dot = + value.rfind(kExtensionSeparator); + const FilePath::StringType::size_type last_separator = + value.find_last_of(FilePath::StringType(FilePath::kSeparators)); + + // Erase the current extension, if any. + if ((last_dot > last_separator || + last_separator == FilePath::StringType::npos) && + last_dot != FilePath::StringType::npos) + value.erase(last_dot); + + value.append(clean_extension); +} + +bool ContentsEqual(const FilePath& filename1, const FilePath& filename2) { + // We open the file in binary format even if they are text files because + // we are just comparing that bytes are exactly same in both files and not + // doing anything smart with text formatting. + std::ifstream file1(filename1.value().c_str(), + std::ios::in | std::ios::binary); + std::ifstream file2(filename2.value().c_str(), + std::ios::in | std::ios::binary); + + // Even if both files aren't openable (and thus, in some sense, "equal"), + // any unusable file yields a result of "false". + if (!file1.is_open() || !file2.is_open()) + return false; + + const int BUFFER_SIZE = 2056; + char buffer1[BUFFER_SIZE], buffer2[BUFFER_SIZE]; + do { + file1.read(buffer1, BUFFER_SIZE); + file2.read(buffer2, BUFFER_SIZE); + + if ((file1.eof() && !file2.eof()) || + (!file1.eof() && file2.eof()) || + (file1.gcount() != file2.gcount()) || + (memcmp(buffer1, buffer2, file1.gcount()))) { + file1.close(); + file2.close(); + return false; + } + } while (!file1.eof() && !file2.eof()); + + file1.close(); + file2.close(); + return true; +} + +bool ReadFileToString(const FilePath& path, std::string* contents) { + FILE* file = OpenFile(path, "rb"); + if (!file) { + return false; + } + + char buf[1 << 16]; + size_t len; + while ((len = fread(buf, 1, sizeof(buf), file)) > 0) { + contents->append(buf, len); + } + CloseFile(file); + + return true; +} + +FILE* CreateAndOpenTemporaryFile(FilePath* path) { + FilePath directory; + if (!GetTempDir(&directory)) + return false; + + return CreateAndOpenTemporaryFileInDir(directory, path); +} + +bool GetFileSize(const FilePath& file_path, int64* file_size) { + FileInfo info; + if (!GetFileInfo(file_path, &info)) + return false; + *file_size = info.size; + return true; +} + +bool CloseFile(FILE* file) { + if (file == NULL) + return true; + return fclose(file) == 0; +} + +bool TruncateFile(FILE* file) { + if (file == NULL) + return false; + long current_offset = ftell(file); + if (current_offset == -1) + return false; +#if defined(OS_WIN) + int fd = _fileno(file); + if (_chsize(fd, current_offset) != 0) + return false; +#else + int fd = fileno(file); + if (ftruncate(fd, current_offset) != 0) + return false; +#endif + return true; +} + +bool ContainsPath(const FilePath &parent, const FilePath& child) { + FilePath abs_parent = FilePath(parent); + FilePath abs_child = FilePath(child); + + if (!file_util::AbsolutePath(&abs_parent) || + !file_util::AbsolutePath(&abs_child)) + return false; + +#if defined(OS_WIN) + // file_util::AbsolutePath() does not flatten case on Windows, so we must do + // a case-insensitive compare. + if (!StartsWith(abs_child.value(), abs_parent.value(), false)) +#else + if (!StartsWithASCII(abs_child.value(), abs_parent.value(), true)) +#endif + return false; + + // file_util::AbsolutePath() normalizes '/' to '\' on Windows, so we only need + // to check kSeparators[0]. + if (abs_child.value().length() <= abs_parent.value().length() || + abs_child.value()[abs_parent.value().length()] != + FilePath::kSeparators[0]) + return false; + + return true; +} + +/////////////////////////////////////////////// +// MemoryMappedFile + +MemoryMappedFile::~MemoryMappedFile() { + CloseHandles(); +} + +bool MemoryMappedFile::Initialize(const FilePath& file_name) { + if (IsValid()) + return false; + + if (!MapFileToMemory(file_name)) { + CloseHandles(); + return false; + } + + return true; +} + +bool MemoryMappedFile::IsValid() { + return data_ != NULL; +} + +// Deprecated functions ---------------------------------------------------- + +bool ReadFileToString(const std::wstring& path, std::string* contents) { + return ReadFileToString(FilePath::FromWStringHack(path), contents); +} + +bool AbsolutePath(std::wstring* path_str) { + FilePath path(FilePath::FromWStringHack(*path_str)); + if (!AbsolutePath(&path)) + return false; + *path_str = path.ToWStringHack(); + return true; +} +void AppendToPath(std::wstring* path, const std::wstring& new_ending) { + if (!path) { + NOTREACHED(); + return; // Don't crash in this function in release builds. + } + + if (!EndsWithSeparator(path)) + path->push_back(FilePath::kSeparators[0]); + path->append(new_ending); +} +bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path, + bool recursive) { + return CopyDirectory(FilePath::FromWStringHack(from_path), + FilePath::FromWStringHack(to_path), + recursive); +} +bool ContentsEqual(const std::wstring& filename1, + const std::wstring& filename2) { + return ContentsEqual(FilePath::FromWStringHack(filename1), + FilePath::FromWStringHack(filename2)); +} +bool CopyFile(const std::wstring& from_path, const std::wstring& to_path) { + return CopyFile(FilePath::FromWStringHack(from_path), + FilePath::FromWStringHack(to_path)); +} +bool CreateDirectory(const std::wstring& full_path) { + return CreateDirectory(FilePath::FromWStringHack(full_path)); +} +bool CreateNewTempDirectory(const std::wstring& prefix, + std::wstring* new_temp_path) { +#if defined(OS_WIN) + FilePath::StringType dir_prefix(prefix); +#elif defined(OS_POSIX) + FilePath::StringType dir_prefix = WideToUTF8(prefix); +#endif + FilePath temp_path; + if (!CreateNewTempDirectory(dir_prefix, &temp_path)) + return false; + *new_temp_path = temp_path.ToWStringHack(); + return true; +} +bool CreateTemporaryFileName(std::wstring* temp_file) { + FilePath temp_file_path; + if (!CreateTemporaryFileName(&temp_file_path)) + return false; + *temp_file = temp_file_path.ToWStringHack(); + return true; +} +bool Delete(const std::wstring& path, bool recursive) { + return Delete(FilePath::FromWStringHack(path), recursive); +} +bool DirectoryExists(const std::wstring& path) { + return DirectoryExists(FilePath::FromWStringHack(path)); +} +bool EndsWithSeparator(std::wstring* path) { + return EndsWithSeparator(FilePath::FromWStringHack(*path)); +} +bool EndsWithSeparator(const std::wstring& path) { + return EndsWithSeparator(FilePath::FromWStringHack(path)); +} +bool GetCurrentDirectory(std::wstring* path_str) { + FilePath path; + if (!GetCurrentDirectory(&path)) + return false; + *path_str = path.ToWStringHack(); + return true; +} +std::wstring GetFileExtensionFromPath(const std::wstring& path) { + FilePath::StringType extension = + GetFileExtensionFromPath(FilePath::FromWStringHack(path)); +#if defined(OS_WIN) + return extension; +#elif defined(OS_POSIX) + return UTF8ToWide(extension); +#endif +} +bool GetFileInfo(const std::wstring& file_path, FileInfo* results) { + return GetFileInfo(FilePath::FromWStringHack(file_path), results); +} +std::wstring GetFilenameFromPath(const std::wstring& path) { + if (path.empty() || EndsWithSeparator(path)) + return std::wstring(); + + return FilePath::FromWStringHack(path).BaseName().ToWStringHack(); +} +bool GetFileSize(const std::wstring& file_path, int64* file_size) { + return GetFileSize(FilePath::FromWStringHack(file_path), file_size); +} +bool GetTempDir(std::wstring* path_str) { + FilePath path; + if (!GetTempDir(&path)) + return false; + *path_str = path.ToWStringHack(); + return true; +} +bool Move(const std::wstring& from_path, const std::wstring& to_path) { + return Move(FilePath::FromWStringHack(from_path), + FilePath::FromWStringHack(to_path)); +} +FILE* OpenFile(const std::wstring& filename, const char* mode) { + return OpenFile(FilePath::FromWStringHack(filename), mode); +} +bool PathExists(const std::wstring& path) { + return PathExists(FilePath::FromWStringHack(path)); +} +bool PathIsWritable(const std::wstring& path) { + return PathIsWritable(FilePath::FromWStringHack(path)); +} +int ReadFile(const std::wstring& filename, char* data, int size) { + return ReadFile(FilePath::FromWStringHack(filename), data, size); +} +bool SetCurrentDirectory(const std::wstring& directory) { + return SetCurrentDirectory(FilePath::FromWStringHack(directory)); +} +void UpOneDirectory(std::wstring* dir) { + FilePath path = FilePath::FromWStringHack(*dir); + FilePath directory = path.DirName(); + // If there is no separator, we will get back kCurrentDirectory. + // In this case don't change |dir|. + if (directory.value() != FilePath::kCurrentDirectory) + *dir = directory.ToWStringHack(); +} +void UpOneDirectoryOrEmpty(std::wstring* dir) { + FilePath path = FilePath::FromWStringHack(*dir); + FilePath directory = path.DirName(); + // If there is no separator, we will get back kCurrentDirectory. + // In this case, clear dir. + if (directory == path || directory.value() == FilePath::kCurrentDirectory) + dir->clear(); + else + *dir = directory.ToWStringHack(); +} +int WriteFile(const std::wstring& filename, const char* data, int size) { + return WriteFile(FilePath::FromWStringHack(filename), data, size); +} +} // namespace diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_util.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,522 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains utility functions for dealing with the local +// filesystem. + +#ifndef BASE_FILE_UTIL_H_ +#define BASE_FILE_UTIL_H_ + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#elif defined(OS_POSIX) +#include +#include +#endif + +#include + +#include +#include +#include + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "base/file_path.h" + +namespace base { +class Time; +} + +namespace file_util { + +//----------------------------------------------------------------------------- +// Functions that operate purely on a path string w/o touching the filesystem: + +// Returns a vector of all of the components of the provided path. +void PathComponents(const FilePath& path, + std::vector* components); +#if defined(OS_WIN) +// Deprecated temporary compatibility function. +void PathComponents(const std::wstring& path, + std::vector* components); +#endif + +// Returns true if the given path ends with a path separator character. +bool EndsWithSeparator(const FilePath& path); +// These two versions are both deprecated. TODO(estade): remove them. +bool EndsWithSeparator(std::wstring* path); +bool EndsWithSeparator(const std::wstring& path); + +// Makes sure that |path| ends with a separator IFF path is a directory that +// exists. Returns true if |path| is an existing directory, false otherwise. +bool EnsureEndsWithSeparator(FilePath* path); + +// Modifies a string by trimming all trailing separators from the end. +// Deprecated. FilePath does this automatically, and if it's constructed from a +// path with a trailing separator, StripTrailingSeparators() may be used. +void TrimTrailingSeparator(std::wstring* dir); + +// Strips the topmost directory from the end of 'dir'. Assumes 'dir' does not +// refer to a file. +// If 'dir' is a root directory, return without change. +// Deprecated. Use FilePath::DirName instead. +void UpOneDirectory(std::wstring* dir); +// Strips the topmost directory from the end of 'dir'. Assumes 'dir' does not +// refer to a file. +// If 'dir' is a root directory, the result becomes empty string. +// Deprecated. Use FilePath::DirName instead. +void UpOneDirectoryOrEmpty(std::wstring* dir); + +// Returns the filename portion of 'path', without any leading \'s or /'s. +// Deprecated. Use FilePath::BaseName instead. +std::wstring GetFilenameFromPath(const std::wstring& path); + +// Deprecated compatibility function. Use FilePath::Extension. +FilePath::StringType GetFileExtensionFromPath(const FilePath& path); +// Deprecated temporary compatibility function. +std::wstring GetFileExtensionFromPath(const std::wstring& path); + +// Returns the directory component of a path, without the trailing +// path separator, or an empty string on error. The function does not +// check for the existence of the path, so if it is passed a directory +// without the trailing \, it will interpret the last component of the +// path as a file and chomp it. This does not support relative paths. +// Examples: +// path == "C:\pics\jojo.jpg", returns "C:\pics" +// path == "C:\Windows\system32\", returns "C:\Windows\system32" +// path == "C:\Windows\system32", returns "C:\Windows" +std::wstring GetDirectoryFromPath(const std::wstring& path); + +// Appends new_ending to path, adding a separator between the two if necessary. +void AppendToPath(std::wstring* path, const std::wstring& new_ending); + +// Convert provided relative path into an absolute path. Returns false on +// error. On POSIX, this function fails if the path does not exist. +bool AbsolutePath(FilePath* path); +// Deprecated temporary compatibility function. +bool AbsolutePath(std::wstring* path); + +// Returns true if |parent| contains |child|. Both paths are converted to +// absolute paths before doing the comparison. +bool ContainsPath(const FilePath& parent, const FilePath& child); + +// Deprecated compatibility function. Use FilePath::InsertBeforeExtension. +void InsertBeforeExtension(FilePath* path, const FilePath::StringType& suffix); + +// Deprecated compatibility function. Use FilePath::ReplaceExtension. +void ReplaceExtension(FilePath* file_name, + const FilePath::StringType& extension); + +#if defined(OS_WIN) +// Deprecated temporary compatibility functions. +void InsertBeforeExtension(std::wstring* path, const std::wstring& suffix); +void ReplaceExtension(std::wstring* file_name, const std::wstring& extension); +#endif + +// Replaces characters in 'file_name' that are illegal for file names with +// 'replace_char'. 'file_name' must not be a full or relative path, but just the +// file name component. Any leading or trailing whitespace in 'file_name' is +// removed. +// Example: +// file_name == "bad:file*name?.txt", changed to: "bad-file-name-.txt" when +// 'replace_char' is '-'. +void ReplaceIllegalCharacters(std::wstring* file_name, int replace_char); + +//----------------------------------------------------------------------------- +// Functions that involve filesystem access or modification: + +// created on or after the given |file_time|. Doesn't count ".." or ".". +// +// Note for POSIX environments: a file created before |file_time| +// can be mis-detected as a newer file due to low precision of +// timestmap of file creation time. If you need to avoid such +// mis-detection perfectly, you should wait one second before +// obtaining |file_time|. +int CountFilesCreatedAfter(const FilePath& path, + const base::Time& file_time); + +// Deletes the given path, whether it's a file or a directory. +// If it's a directory, it's perfectly happy to delete all of the +// directory's contents. Passing true to recursive deletes +// subdirectories and their contents as well. +// Returns true if successful, false otherwise. +// +// WARNING: USING THIS WITH recursive==true IS EQUIVALENT +// TO "rm -rf", SO USE WITH CAUTION. +bool Delete(const FilePath& path, bool recursive); +// Deprecated temporary compatibility function. +bool Delete(const std::wstring& path, bool recursive); + +// Moves the given path, whether it's a file or a directory. +// If a simple rename is not possible, such as in the case where the paths are +// on different volumes, this will attempt to copy and delete. Returns +// true for success. +bool Move(const FilePath& from_path, const FilePath& to_path); +// Deprecated temporary compatibility function. +bool Move(const std::wstring& from_path, const std::wstring& to_path); + +// Copies a single file. Use CopyDirectory to copy directories. +bool CopyFile(const FilePath& from_path, const FilePath& to_path); +// Deprecated temporary compatibility function. +bool CopyFile(const std::wstring& from_path, const std::wstring& to_path); + +// Copies the given path, and optionally all subdirectories and their contents +// as well. +// If there are files existing under to_path, always overwrite. +// Returns true if successful, false otherwise. +// Dont't use wildcards on the names, it may stop working without notice. +// +// If you only need to copy a file use CopyFile, it's faster. +bool CopyDirectory(const FilePath& from_path, const FilePath& to_path, + bool recursive); +// Deprecated temporary compatibility function. +bool CopyDirectory(const std::wstring& from_path, const std::wstring& to_path, + bool recursive); + +// Returns true if the given path exists on the local filesystem, +// false otherwise. +bool PathExists(const FilePath& path); +// Deprecated temporary compatibility function. +bool PathExists(const std::wstring& path); + +// Returns true if the given path is writable by the user, false otherwise. +bool PathIsWritable(const FilePath& path); +// Deprecated temporary compatibility function. +bool PathIsWritable(const std::wstring& path); + +// Returns true if the given path exists and is a directory, false otherwise. +bool DirectoryExists(const FilePath& path); +// Deprecated temporary compatibility function. +bool DirectoryExists(const std::wstring& path); + +#if defined(OS_WIN) +// Gets the creation time of the given file (expressed in the local timezone), +// and returns it via the creation_time parameter. Returns true if successful, +// false otherwise. +bool GetFileCreationLocalTime(const std::wstring& filename, + LPSYSTEMTIME creation_time); + +// Same as above, but takes a previously-opened file handle instead of a name. +bool GetFileCreationLocalTimeFromHandle(HANDLE file_handle, + LPSYSTEMTIME creation_time); +#endif // defined(OS_WIN) + +// Returns true if the contents of the two files given are equal, false +// otherwise. If either file can't be read, returns false. +bool ContentsEqual(const FilePath& filename1, + const FilePath& filename2); +// Deprecated temporary compatibility function. +bool ContentsEqual(const std::wstring& filename1, + const std::wstring& filename2); + +// Read the file at |path| into |contents|, returning true on success. +// Useful for unit tests. +bool ReadFileToString(const FilePath& path, std::string* contents); +// Deprecated version. +bool ReadFileToString(const std::wstring& path, std::string* contents); + +#if defined(OS_POSIX) +// Read exactly |bytes| bytes from file descriptor |fd|, storing the result +// in |buffer|. This function is protected against EINTR and partial reads. +// Returns true iff |bytes| bytes have been successfuly read from |fd|. +bool ReadFromFD(int fd, char* buffer, size_t bytes); +#endif // defined(OS_POSIX) + +#if defined(OS_WIN) +// Resolve Windows shortcut (.LNK file) +// Argument path specifies a valid LNK file. On success, return true and put +// the URL into path. If path is a invalid .LNK file, return false. +bool ResolveShortcut(FilePath* path); +// Deprecated temporary compatibility function. +bool ResolveShortcut(std::wstring* path); + +// Create a Windows shortcut (.LNK file) +// This method creates a shortcut link using the information given. Ensure +// you have initialized COM before calling into this function. 'source' +// and 'destination' parameters are required, everything else can be NULL. +// 'source' is the existing file, 'destination' is the new link file to be +// created; for best results pass the filename with the .lnk extension. +// The 'icon' can specify a dll or exe in which case the icon index is the +// resource id. +// Note that if the shortcut exists it will overwrite it. +bool CreateShortcutLink(const wchar_t *source, const wchar_t *destination, + const wchar_t *working_dir, const wchar_t *arguments, + const wchar_t *description, const wchar_t *icon, + int icon_index); + +// Update a Windows shortcut (.LNK file). This method assumes the shortcut +// link already exists (otherwise false is returned). Ensure you have +// initialized COM before calling into this function. Only 'destination' +// parameter is required, everything else can be NULL (but if everything else +// is NULL no changes are made to the shortcut). 'destination' is the link +// file to be updated. For best results pass the filename with the .lnk +// extension. +bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination, + const wchar_t *working_dir, const wchar_t *arguments, + const wchar_t *description, const wchar_t *icon, + int icon_index); + +// Return true if the given directory is empty +bool IsDirectoryEmpty(const std::wstring& dir_path); + +// Copy from_path to to_path recursively and then delete from_path recursively. +// Returns true if all operations succeed. +// This function simulates Move(), but unlike Move() it works across volumes. +// This fuction is not transactional. +bool CopyAndDeleteDirectory(const FilePath& from_path, + const FilePath& to_path); +#endif + +// Get the temporary directory provided by the system. +bool GetTempDir(FilePath* path); +// Deprecated temporary compatibility function. +bool GetTempDir(std::wstring* path); +// Get a temporary directory for shared memory files. +// Only useful on POSIX; redirects to GetTempDir() on Windows. +bool GetShmemTempDir(FilePath* path); + +// Creates a temporary file. The full path is placed in |path|, and the +// function returns true if was successful in creating the file. The file will +// be empty and all handles closed after this function returns. +// TODO(erikkay): rename this function and track down all of the callers. +// (Clarification of erik's comment: the intent is to rename the BlahFileName() +// calls into BlahFile(), since they create temp files (not temp filenames).) +bool CreateTemporaryFileName(FilePath* path); +// Deprecated temporary compatibility function. +bool CreateTemporaryFileName(std::wstring* temp_file); + +// Create and open a temporary file. File is opened for read/write. +// The full path is placed in |path|, and the function returns true if +// was successful in creating and opening the file. +FILE* CreateAndOpenTemporaryFile(FilePath* path); +// Like above but for shmem files. Only useful for POSIX. +FILE* CreateAndOpenTemporaryShmemFile(FilePath* path); + +// Similar to CreateAndOpenTemporaryFile, but the file is created in |dir|. +FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path); + +// Same as CreateTemporaryFileName but the file is created in |dir|. +bool CreateTemporaryFileNameInDir(const std::wstring& dir, + std::wstring* temp_file); + +// Create a new directory under TempPath. If prefix is provided, the new +// directory name is in the format of prefixyyyy. +// NOTE: prefix is ignored in the POSIX implementation. +// TODO(erikkay): is this OK? +// If success, return true and output the full path of the directory created. +bool CreateNewTempDirectory(const FilePath::StringType& prefix, + FilePath* new_temp_path); +// Deprecated temporary compatibility function. +bool CreateNewTempDirectory(const std::wstring& prefix, + std::wstring* new_temp_path); + +// Creates a directory, as well as creating any parent directories, if they +// don't exist. Returns 'true' on successful creation, or if the directory +// already exists. +bool CreateDirectory(const FilePath& full_path); +// Deprecated temporary compatibility function. +bool CreateDirectory(const std::wstring& full_path); + +// Returns the file size. Returns true on success. +bool GetFileSize(const FilePath& file_path, int64* file_size); +// Deprecated temporary compatibility function. +bool GetFileSize(const std::wstring& file_path, int64* file_size); + +// Used to hold information about a given file path. See GetFileInfo below. +struct FileInfo { + // The size of the file in bytes. Undefined when is_directory is true. + int64 size; + + // True if the file corresponds to a directory. + bool is_directory; + + // Add additional fields here as needed. +}; + +// Returns information about the given file path. +bool GetFileInfo(const FilePath& file_path, FileInfo* info); +// Deprecated temporary compatibility function. +bool GetFileInfo(const std::wstring& file_path, FileInfo* info); + +// Wrapper for fopen-like calls. Returns non-NULL FILE* on success. +FILE* OpenFile(const FilePath& filename, const char* mode); +// Deprecated temporary compatibility functions. +FILE* OpenFile(const std::string& filename, const char* mode); +FILE* OpenFile(const std::wstring& filename, const char* mode); + +// Closes file opened by OpenFile. Returns true on success. +bool CloseFile(FILE* file); + +// Truncates an open file to end at the location of the current file pointer. +// This is a cross-platform analog to Windows' SetEndOfFile() function. +bool TruncateFile(FILE* file); + +// Reads the given number of bytes from the file into the buffer. Returns +// the number of read bytes, or -1 on error. +int ReadFile(const FilePath& filename, char* data, int size); +// Deprecated temporary compatibility function. +int ReadFile(const std::wstring& filename, char* data, int size); + +// Writes the given buffer into the file, overwriting any data that was +// previously there. Returns the number of bytes written, or -1 on error. +int WriteFile(const FilePath& filename, const char* data, int size); +// Deprecated temporary compatibility function. +int WriteFile(const std::wstring& filename, const char* data, int size); + +// Gets the current working directory for the process. +bool GetCurrentDirectory(FilePath* path); +// Deprecated temporary compatibility function. +bool GetCurrentDirectory(std::wstring* path); + +// Sets the current working directory for the process. +bool SetCurrentDirectory(const FilePath& path); +// Deprecated temporary compatibility function. +bool SetCurrentDirectory(const std::wstring& current_directory); + +// A class to handle auto-closing of FILE*'s. +class ScopedFILEClose { + public: + inline void operator()(FILE* x) const { + if (x) { + fclose(x); + } + } +}; + +typedef scoped_ptr_malloc ScopedFILE; + +// A class for enumerating the files in a provided path. The order of the +// results is not guaranteed. +// +// DO NOT USE FROM THE MAIN THREAD of your application unless it is a test +// program where latency does not matter. This class is blocking. +class FileEnumerator { + public: +#if defined(OS_WIN) + typedef WIN32_FIND_DATA FindInfo; +#elif defined(OS_POSIX) + typedef struct { + struct stat stat; + std::string filename; + } FindInfo; +#endif + + enum FILE_TYPE { + FILES = 0x1, + DIRECTORIES = 0x2, + FILES_AND_DIRECTORIES = 0x3 + }; + + // |root_path| is the starting directory to search for. It may or may not end + // in a slash. + // + // If |recursive| is true, this will enumerate all matches in any + // subdirectories matched as well. It does a breadth-first search, so all + // files in one directory will be returned before any files in a + // subdirectory. + // + // |file_type| specifies whether the enumerator should match files, + // directories, or both. + // + // |pattern| is an optional pattern for which files to match. This + // works like shell globbing. For example, "*.txt" or "Foo???.doc". + // However, be careful in specifying patterns that aren't cross platform + // since the underlying code uses OS-specific matching routines. In general, + // Windows matching is less featureful than others, so test there first. + // If unspecified, this will match all files. + // NOTE: the pattern only matches the contents of root_path, not files in + // recursive subdirectories. + // TODO(erikkay): Fix the pattern matching to work at all levels. + FileEnumerator(const FilePath& root_path, + bool recursive, + FileEnumerator::FILE_TYPE file_type); + FileEnumerator(const FilePath& root_path, + bool recursive, + FileEnumerator::FILE_TYPE file_type, + const FilePath::StringType& pattern); + ~FileEnumerator(); + + // Returns an empty string if there are no more results. + FilePath Next(); + + // Write the file info into |info|. + void GetFindInfo(FindInfo* info); + + private: + FilePath root_path_; + bool recursive_; + FILE_TYPE file_type_; + FilePath pattern_; // Empty when we want to find everything. + + // Set to true when there is a find operation open. This way, we can lazily + // start the operations when the caller calls Next(). + bool is_in_find_op_; + + // A stack that keeps track of which subdirectories we still need to + // enumerate in the breadth-first search. + std::stack pending_paths_; + +#if defined(OS_WIN) + WIN32_FIND_DATA find_data_; + HANDLE find_handle_; +#elif defined(OS_POSIX) + FTS* fts_; + FTSENT* fts_ent_; +#endif + + DISALLOW_EVIL_CONSTRUCTORS(FileEnumerator); +}; + +class MemoryMappedFile { + public: + // The default constructor sets all members to invalid/null values. + MemoryMappedFile(); + ~MemoryMappedFile(); + + // Opens an existing file and maps it into memory. Access is restricted to + // read only. If this object already points to a valid memory mapped file + // then this method will fail and return false. If it cannot open the file, + // the file does not exist, or the memory mapping fails, it will return false. + // Later we may want to allow the user to specify access. + bool Initialize(const FilePath& file_name); + + const uint8* data() const { return data_; } + size_t length() const { return length_; } + + // Is file_ a valid file handle that points to an open, memory mapped file? + bool IsValid(); + + private: + // Map the file to memory, set data_ to that memory address. Return true on + // success, false on any kind of failure. This is a helper for Initialize(). + bool MapFileToMemory(const FilePath& file_name); + + // Closes all open handles. Later we may want to make this public. + void CloseHandles(); + +#if defined(OS_WIN) + HANDLE file_; + HANDLE file_mapping_; +#elif defined(OS_POSIX) + // The file descriptor. + int file_; +#endif + uint8* data_; + size_t length_; + + DISALLOW_COPY_AND_ASSIGN(MemoryMappedFile); +}; + +// Renames a file using the SHFileOperation API to ensure that the target file +// gets the correct default security descriptor in the new path. +bool RenameFileAndResetSecurityDescriptor( + const FilePath& source_file_path, + const FilePath& target_file_path); + +} // namespace file_util + +#endif // BASE_FILE_UTIL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_util_icu.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_util_icu.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_util_icu.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_util_icu.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,90 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// File utilities that use the ICU library go in this file. Functions using ICU +// are separated from the other functions to prevent ICU being pulled in by the +// linker if there is a false dependency. +// +// (The VS2005 linker finds such a false dependency and adds ~300K of ICU to +// chrome.exe if this code lives in file_util.cc, even though none of this code +// is called.) + +#include "base/file_util.h" + +#include "base/string_util.h" +#include "unicode/uniset.h" + +namespace file_util { + +void ReplaceIllegalCharacters(std::wstring* file_name, int replace_char) { + DCHECK(file_name); + + // Control characters, formatting characters, non-characters, and + // some printable ASCII characters regarded as dangerous ('"*/:<>?\\'). + // See http://blogs.msdn.com/michkap/archive/2006/11/03/941420.aspx + // and http://msdn2.microsoft.com/en-us/library/Aa365247.aspx + // TODO(jungshik): Revisit the set. ZWJ and ZWNJ are excluded because they + // are legitimate in Arabic and some S/SE Asian scripts. However, when used + // elsewhere, they can be confusing/problematic. + // Also, consider wrapping the set with our Singleton class to create and + // freeze it only once. Note that there's a trade-off between memory and + // speed. + + UErrorCode status = U_ZERO_ERROR; +#if defined(WCHAR_T_IS_UTF16) + UnicodeSet illegal_characters(UnicodeString( + L"[[\"*/:<>?\\\\|][:Cc:][:Cf:] - [\u200c\u200d]]"), status); +#else + UnicodeSet illegal_characters(UNICODE_STRING_SIMPLE( + "[[\"*/:<>?\\\\|][:Cc:][:Cf:] - [\\u200c\\u200d]]").unescape(), status); +#endif + DCHECK(U_SUCCESS(status)); + // Add non-characters. If this becomes a performance bottleneck by + // any chance, check |ucs4 & 0xFFFEu == 0xFFFEu|, instead. + illegal_characters.add(0xFDD0, 0xFDEF); + for (int i = 0; i <= 0x10; ++i) { + int plane_base = 0x10000 * i; + illegal_characters.add(plane_base + 0xFFFE, plane_base + 0xFFFF); + } + illegal_characters.freeze(); + DCHECK(!illegal_characters.contains(replace_char) && replace_char < 0x10000); + + // Remove leading and trailing whitespace. + TrimWhitespace(*file_name, TRIM_ALL, file_name); + + std::wstring::size_type i = 0; + std::wstring::size_type length = file_name->size(); + const wchar_t* wstr = file_name->data(); +#if defined(WCHAR_T_IS_UTF16) + // Using |span| method of UnicodeSet might speed things up a bit, but + // it's not likely to matter here. + std::wstring temp; + temp.reserve(length); + while (i < length) { + UChar32 ucs4; + std::wstring::size_type prev = i; + U16_NEXT(wstr, i, length, ucs4); + if (illegal_characters.contains(ucs4)) { + temp.push_back(replace_char); + } else if (ucs4 < 0x10000) { + temp.push_back(ucs4); + } else { + temp.push_back(wstr[prev]); + temp.push_back(wstr[prev + 1]); + } + } + file_name->swap(temp); +#elif defined(WCHAR_T_IS_UTF32) + while (i < length) { + if (illegal_characters.contains(wstr[i])) { + (*file_name)[i] = replace_char; + } + ++i; + } +#else +#error wchar_t* should be either UTF-16 or UTF-32 +#endif +} + +} // namespace diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_util_linux.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_util_linux.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_util_linux.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_util_linux.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,78 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_util.h" + +#include + +#include +#include + +#include "base/eintr_wrapper.h" +#include "base/file_path.h" +#include "base/string_util.h" + +namespace file_util { + +bool GetTempDir(FilePath* path) { + const char* tmp = getenv("TMPDIR"); + if (tmp) + *path = FilePath(tmp); + else + *path = FilePath("/tmp"); + return true; +} + +bool GetShmemTempDir(FilePath* path) { + *path = FilePath("/dev/shm"); + return true; +} + +bool CopyFile(const FilePath& from_path, const FilePath& to_path) { + int infile = open(from_path.value().c_str(), O_RDONLY); + if (infile < 0) + return false; + + int outfile = creat(to_path.value().c_str(), 0666); + if (outfile < 0) { + close(infile); + return false; + } + + const size_t kBufferSize = 32768; + std::vector buffer(kBufferSize); + bool result = true; + + while (result) { + ssize_t bytes_read = HANDLE_EINTR(read(infile, &buffer[0], buffer.size())); + if (bytes_read < 0) { + result = false; + break; + } + if (bytes_read == 0) + break; + // Allow for partial writes + ssize_t bytes_written_per_read = 0; + do { + ssize_t bytes_written_partial = HANDLE_EINTR(write( + outfile, + &buffer[bytes_written_per_read], + bytes_read - bytes_written_per_read)); + if (bytes_written_partial < 0) { + result = false; + break; + } + bytes_written_per_read += bytes_written_partial; + } while (bytes_written_per_read < bytes_read); + } + + if (HANDLE_EINTR(close(infile)) < 0) + result = false; + if (HANDLE_EINTR(close(outfile)) < 0) + result = false; + + return result; +} + +} // namespace file_util diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_util_mac.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_util_mac.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_util_mac.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_util_mac.mm 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,33 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_util.h" + +#import +#include + +#include "base/file_path.h" +#include "base/logging.h" +#include "base/string_util.h" + +namespace file_util { + +bool GetTempDir(FilePath* path) { + NSString* tmp = NSTemporaryDirectory(); + if (tmp == nil) + return false; + *path = FilePath([tmp fileSystemRepresentation]); + return true; +} + +bool GetShmemTempDir(FilePath* path) { + return GetTempDir(path); +} + +bool CopyFile(const FilePath& from_path, const FilePath& to_path) { + return (copyfile(from_path.value().c_str(), + to_path.value().c_str(), NULL, COPYFILE_ALL) == 0); +} + +} // namespace diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_util_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_util_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_util_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_util_posix.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,642 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "base/basictypes.h" +#include "base/eintr_wrapper.h" +#include "base/file_path.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "base/time.h" + +namespace file_util { + +#if defined(GOOGLE_CHROME_BUILD) +static const char* kTempFileName = "com.google.chrome.XXXXXX"; +#else +static const char* kTempFileName = "org.chromium.XXXXXX"; +#endif + +std::wstring GetDirectoryFromPath(const std::wstring& path) { + if (EndsWithSeparator(path)) { + std::wstring dir = path; + TrimTrailingSeparator(&dir); + return dir; + } else { + char full_path[PATH_MAX]; + base::strlcpy(full_path, WideToUTF8(path).c_str(), arraysize(full_path)); + return UTF8ToWide(dirname(full_path)); + } +} + +bool AbsolutePath(FilePath* path) { + char full_path[PATH_MAX]; + if (realpath(path->value().c_str(), full_path) == NULL) + return false; + *path = FilePath(full_path); + return true; +} + +int CountFilesCreatedAfter(const FilePath& path, + const base::Time& comparison_time) { + int file_count = 0; + + DIR* dir = opendir(path.value().c_str()); + if (dir) { + struct dirent ent_buf; + struct dirent* ent; + while (readdir_r(dir, &ent_buf, &ent) == 0 && ent) { + if ((strcmp(ent->d_name, ".") == 0) || + (strcmp(ent->d_name, "..") == 0)) + continue; + + struct stat64 st; + int test = stat64(path.Append(ent->d_name).value().c_str(), &st); + if (test != 0) { + LOG(ERROR) << "stat64 failed: " << strerror(errno); + continue; + } + // Here, we use Time::TimeT(), which discards microseconds. This + // means that files which are newer than |comparison_time| may + // be considered older. If we don't discard microseconds, it + // introduces another issue. Suppose the following case: + // + // 1. Get |comparison_time| by Time::Now() and the value is 10.1 (secs). + // 2. Create a file and the current time is 10.3 (secs). + // + // As POSIX doesn't have microsecond precision for |st_ctime|, + // the creation time of the file created in the step 2 is 10 and + // the file is considered older than |comparison_time|. After + // all, we may have to accept either of the two issues: 1. files + // which are older than |comparison_time| are considered newer + // (current implementation) 2. files newer than + // |comparison_time| are considered older. + if (st.st_ctime >= comparison_time.ToTimeT()) + ++file_count; + } + closedir(dir); + } + return file_count; +} + +// TODO(erikkay): The Windows version of this accepts paths like "foo/bar/*" +// which works both with and without the recursive flag. I'm not sure we need +// that functionality. If not, remove from file_util_win.cc, otherwise add it +// here. +bool Delete(const FilePath& path, bool recursive) { + const char* path_str = path.value().c_str(); + struct stat64 file_info; + int test = stat64(path_str, &file_info); + if (test != 0) { + // The Windows version defines this condition as success. + bool ret = (errno == ENOENT || errno == ENOTDIR); + return ret; + } + if (!S_ISDIR(file_info.st_mode)) + return (unlink(path_str) == 0); + if (!recursive) + return (rmdir(path_str) == 0); + + bool success = true; + int ftsflags = FTS_PHYSICAL | FTS_NOSTAT; + char top_dir[PATH_MAX]; + if (base::strlcpy(top_dir, path_str, + arraysize(top_dir)) >= arraysize(top_dir)) { + return false; + } + char* dir_list[2] = { top_dir, NULL }; + FTS* fts = fts_open(dir_list, ftsflags, NULL); + if (fts) { + FTSENT* fts_ent = fts_read(fts); + while (success && fts_ent != NULL) { + switch (fts_ent->fts_info) { + case FTS_DNR: + case FTS_ERR: + // log error + success = false; + continue; + break; + case FTS_DP: + success = (rmdir(fts_ent->fts_accpath) == 0); + break; + case FTS_D: + break; + case FTS_NSOK: + case FTS_F: + case FTS_SL: + case FTS_SLNONE: + success = (unlink(fts_ent->fts_accpath) == 0); + break; + default: + DCHECK(false); + break; + } + fts_ent = fts_read(fts); + } + fts_close(fts); + } + return success; +} + +bool Move(const FilePath& from_path, const FilePath& to_path) { + if (rename(from_path.value().c_str(), to_path.value().c_str()) == 0) + return true; + + if (!CopyDirectory(from_path, to_path, true)) + return false; + + Delete(from_path, true); + return true; +} + +bool CopyDirectory(const FilePath& from_path, + const FilePath& to_path, + bool recursive) { + // Some old callers of CopyDirectory want it to support wildcards. + // After some discussion, we decided to fix those callers. + // Break loudly here if anyone tries to do this. + // TODO(evanm): remove this once we're sure it's ok. + DCHECK(to_path.value().find('*') == std::string::npos); + DCHECK(from_path.value().find('*') == std::string::npos); + + char top_dir[PATH_MAX]; + if (base::strlcpy(top_dir, from_path.value().c_str(), + arraysize(top_dir)) >= arraysize(top_dir)) { + return false; + } + + char* dir_list[] = { top_dir, NULL }; + FTS* fts = fts_open(dir_list, FTS_PHYSICAL | FTS_NOSTAT, NULL); + if (!fts) { + LOG(ERROR) << "fts_open failed: " << strerror(errno); + return false; + } + + int error = 0; + FTSENT* ent; + while (!error && (ent = fts_read(fts)) != NULL) { + // ent->fts_path is the source path, including from_path, so paste + // the suffix after from_path onto to_path to create the target_path. + std::string suffix(&ent->fts_path[from_path.value().size()]); + // Strip the leading '/' (if any). + if (!suffix.empty()) { + DCHECK_EQ('/', suffix[0]); + suffix.erase(0, 1); + } + const FilePath target_path = to_path.Append(suffix); + switch (ent->fts_info) { + case FTS_D: // Preorder directory. + // If we encounter a subdirectory in a non-recursive copy, prune it + // from the traversal. + if (!recursive && ent->fts_level > 0) { + if (fts_set(fts, ent, FTS_SKIP) != 0) + error = errno; + continue; + } + + // Try creating the target dir, continuing on it if it exists already. + // Rely on the user's umask to produce correct permissions. + if (mkdir(target_path.value().c_str(), 0777) != 0) { + if (errno != EEXIST) + error = errno; + } + break; + case FTS_F: // Regular file. + case FTS_NSOK: // File, no stat info requested. + errno = 0; + if (!CopyFile(FilePath(ent->fts_path), target_path)) + error = errno ? errno : EINVAL; + break; + case FTS_DP: // Postorder directory. + case FTS_DOT: // "." or ".." + // Skip it. + continue; + case FTS_DC: // Directory causing a cycle. + // Skip this branch. + if (fts_set(fts, ent, FTS_SKIP) != 0) + error = errno; + break; + case FTS_DNR: // Directory cannot be read. + case FTS_ERR: // Error. + case FTS_NS: // Stat failed. + // Abort with the error. + error = ent->fts_errno; + break; + case FTS_SL: // Symlink. + case FTS_SLNONE: // Symlink with broken target. + LOG(WARNING) << "CopyDirectory() skipping symbolic link: " << + ent->fts_path; + continue; + case FTS_DEFAULT: // Some other sort of file. + LOG(WARNING) << "CopyDirectory() skipping file of unknown type: " << + ent->fts_path; + continue; + default: + NOTREACHED(); + continue; // Hope for the best! + } + } + // fts_read may have returned NULL and set errno to indicate an error. + if (!error && errno != 0) + error = errno; + + if (!fts_close(fts)) { + // If we already have an error, let's use that error instead of the error + // fts_close set. + if (!error) + error = errno; + } + + if (error) { + LOG(ERROR) << "CopyDirectory(): " << strerror(error); + return false; + } + return true; +} + +bool PathExists(const FilePath& path) { + struct stat64 file_info; + return (stat64(path.value().c_str(), &file_info) == 0); +} + +bool PathIsWritable(const FilePath& path) { + FilePath test_path(path); + struct stat64 file_info; + if (stat64(test_path.value().c_str(), &file_info) != 0) { + // If the path doesn't exist, test the parent dir. + test_path = test_path.DirName(); + // If the parent dir doesn't exist, then return false (the path is not + // directly writable). + if (stat64(test_path.value().c_str(), &file_info) != 0) + return false; + } + if (S_IWOTH & file_info.st_mode) + return true; + if (getegid() == file_info.st_gid && (S_IWGRP & file_info.st_mode)) + return true; + if (geteuid() == file_info.st_uid && (S_IWUSR & file_info.st_mode)) + return true; + return false; +} + +bool DirectoryExists(const FilePath& path) { + struct stat64 file_info; + if (stat64(path.value().c_str(), &file_info) == 0) + return S_ISDIR(file_info.st_mode); + return false; +} + +// TODO(erikkay): implement +#if 0 +bool GetFileCreationLocalTimeFromHandle(int fd, + LPSYSTEMTIME creation_time) { + if (!file_handle) + return false; + + FILETIME utc_filetime; + if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL)) + return false; + + FILETIME local_filetime; + if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime)) + return false; + + return !!FileTimeToSystemTime(&local_filetime, creation_time); +} + +bool GetFileCreationLocalTime(const std::string& filename, + LPSYSTEMTIME creation_time) { + ScopedHandle file_handle( + CreateFile(filename.c_str(), GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); + return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time); +} +#endif + +bool ReadFromFD(int fd, char* buffer, size_t bytes) { + size_t total_read = 0; + while (total_read < bytes) { + ssize_t bytes_read = + HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read)); + if (bytes_read <= 0) + break; + total_read += bytes_read; + } + return total_read == bytes; +} + +// Creates and opens a temporary file in |directory|, returning the +// file descriptor. |path| is set to the temporary file path. +// Note TODO(erikkay) comment in header for BlahFileName() calls; the +// intent is to rename these files BlahFile() (since they create +// files, not filenames). This function does NOT unlink() the file. +int CreateAndOpenFdForTemporaryFile(FilePath directory, FilePath* path) { + *path = directory.Append(kTempFileName); + const std::string& tmpdir_string = path->value(); + // this should be OK since mkstemp just replaces characters in place + char* buffer = const_cast(tmpdir_string.c_str()); + + return mkstemp(buffer); +} + +bool CreateTemporaryFileName(FilePath* path) { + FilePath directory; + if (!GetTempDir(&directory)) + return false; + int fd = CreateAndOpenFdForTemporaryFile(directory, path); + if (fd < 0) + return false; + close(fd); + return true; +} + +FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) { + FilePath directory; + if (!GetShmemTempDir(&directory)) + return false; + + return CreateAndOpenTemporaryFileInDir(directory, path); +} + +FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { + int fd = CreateAndOpenFdForTemporaryFile(dir, path); + if (fd < 0) + return NULL; + + return fdopen(fd, "a+"); +} + +bool CreateTemporaryFileNameInDir(const std::wstring& dir, + std::wstring* temp_file) { + // Not implemented yet. + NOTREACHED(); + return false; +} + +bool CreateNewTempDirectory(const FilePath::StringType& prefix, + FilePath* new_temp_path) { + FilePath tmpdir; + if (!GetTempDir(&tmpdir)) + return false; + tmpdir = tmpdir.Append(kTempFileName); + std::string tmpdir_string = tmpdir.value(); + // this should be OK since mkdtemp just replaces characters in place + char* buffer = const_cast(tmpdir_string.c_str()); + char* dtemp = mkdtemp(buffer); + if (!dtemp) + return false; + *new_temp_path = FilePath(dtemp); + return true; +} + +bool CreateDirectory(const FilePath& full_path) { + std::vector subpaths; + + // Collect a list of all parent directories. + FilePath last_path = full_path; + subpaths.push_back(full_path); + for (FilePath path = full_path.DirName(); + path.value() != last_path.value(); path = path.DirName()) { + subpaths.push_back(path); + last_path = path; + } + + // Iterate through the parents and create the missing ones. + for (std::vector::reverse_iterator i = subpaths.rbegin(); + i != subpaths.rend(); ++i) { + if (!DirectoryExists(*i)) { + if (mkdir(i->value().c_str(), 0777) != 0) + return false; + } + } + return true; +} + +bool GetFileInfo(const FilePath& file_path, FileInfo* results) { + struct stat64 file_info; + if (stat64(file_path.value().c_str(), &file_info) != 0) + return false; + results->is_directory = S_ISDIR(file_info.st_mode); + results->size = file_info.st_size; + return true; +} + +FILE* OpenFile(const std::string& filename, const char* mode) { + return OpenFile(FilePath(filename), mode); +} + +FILE* OpenFile(const FilePath& filename, const char* mode) { + return fopen(filename.value().c_str(), mode); +} + +int ReadFile(const FilePath& filename, char* data, int size) { + int fd = open(filename.value().c_str(), O_RDONLY); + if (fd < 0) + return -1; + + int ret_value = HANDLE_EINTR(read(fd, data, size)); + HANDLE_EINTR(close(fd)); + return ret_value; +} + +int WriteFile(const FilePath& filename, const char* data, int size) { + int fd = creat(filename.value().c_str(), 0666); + if (fd < 0) + return -1; + + // Allow for partial writes + ssize_t bytes_written_total = 0; + do { + ssize_t bytes_written_partial = + HANDLE_EINTR(write(fd, data + bytes_written_total, + size - bytes_written_total)); + if (bytes_written_partial < 0) { + HANDLE_EINTR(close(fd)); + return -1; + } + bytes_written_total += bytes_written_partial; + } while (bytes_written_total < size); + + HANDLE_EINTR(close(fd)); + return bytes_written_total; +} + +// Gets the current working directory for the process. +bool GetCurrentDirectory(FilePath* dir) { + char system_buffer[PATH_MAX] = ""; + if (!getcwd(system_buffer, sizeof(system_buffer))) { + NOTREACHED(); + return false; + } + *dir = FilePath(system_buffer); + return true; +} + +// Sets the current working directory for the process. +bool SetCurrentDirectory(const FilePath& path) { + int ret = chdir(path.value().c_str()); + return !ret; +} + +/////////////////////////////////////////////// +// FileEnumerator + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + FileEnumerator::FILE_TYPE file_type) + : recursive_(recursive), + file_type_(file_type), + is_in_find_op_(false), + fts_(NULL) { + pending_paths_.push(root_path); +} + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + FileEnumerator::FILE_TYPE file_type, + const FilePath::StringType& pattern) + : recursive_(recursive), + file_type_(file_type), + pattern_(root_path.value()), + is_in_find_op_(false), + fts_(NULL) { + // The Windows version of this code only matches against items in the top-most + // directory, and we're comparing fnmatch against full paths, so this is the + // easiest way to get the right pattern. + pattern_ = pattern_.Append(pattern); + pending_paths_.push(root_path); +} + +FileEnumerator::~FileEnumerator() { + if (fts_) + fts_close(fts_); +} + +void FileEnumerator::GetFindInfo(FindInfo* info) { + DCHECK(info); + + if (!is_in_find_op_) + return; + + memcpy(&(info->stat), fts_ent_->fts_statp, sizeof(info->stat)); + info->filename.assign(fts_ent_->fts_name); +} + +// As it stands, this method calls itself recursively when the next item of +// the fts enumeration doesn't match (type, pattern, etc.). In the case of +// large directories with many files this can be quite deep. +// TODO(erikkay) - get rid of this recursive pattern +FilePath FileEnumerator::Next() { + if (!is_in_find_op_) { + if (pending_paths_.empty()) + return FilePath(); + + // The last find FindFirstFile operation is done, prepare a new one. + root_path_ = pending_paths_.top(); + root_path_ = root_path_.StripTrailingSeparators(); + pending_paths_.pop(); + + // Start a new find operation. + int ftsflags = FTS_LOGICAL; + char top_dir[PATH_MAX]; + base::strlcpy(top_dir, root_path_.value().c_str(), arraysize(top_dir)); + char* dir_list[2] = { top_dir, NULL }; + fts_ = fts_open(dir_list, ftsflags, NULL); + if (!fts_) + return Next(); + is_in_find_op_ = true; + } + + fts_ent_ = fts_read(fts_); + if (fts_ent_ == NULL) { + fts_close(fts_); + fts_ = NULL; + is_in_find_op_ = false; + return Next(); + } + + // Level 0 is the top, which is always skipped. + if (fts_ent_->fts_level == 0) + return Next(); + + // Patterns are only matched on the items in the top-most directory. + // (see Windows implementation) + if (fts_ent_->fts_level == 1 && pattern_.value().length() > 0) { + if (fnmatch(pattern_.value().c_str(), fts_ent_->fts_path, 0) != 0) { + if (fts_ent_->fts_info == FTS_D) + fts_set(fts_, fts_ent_, FTS_SKIP); + return Next(); + } + } + + FilePath cur_file(fts_ent_->fts_path); + if (fts_ent_->fts_info == FTS_D) { + // If not recursive, then prune children. + if (!recursive_) + fts_set(fts_, fts_ent_, FTS_SKIP); + return (file_type_ & FileEnumerator::DIRECTORIES) ? cur_file : Next(); + } else if (fts_ent_->fts_info == FTS_F) { + return (file_type_ & FileEnumerator::FILES) ? cur_file : Next(); + } + // TODO(erikkay) - verify that the other fts_info types aren't interesting + return Next(); +} + +/////////////////////////////////////////////// +// MemoryMappedFile + +MemoryMappedFile::MemoryMappedFile() + : file_(-1), + data_(NULL), + length_(0) { +} + +bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) { + file_ = open(file_name.value().c_str(), O_RDONLY); + if (file_ == -1) + return false; + + struct stat file_stat; + if (fstat(file_, &file_stat) == -1) + return false; + length_ = file_stat.st_size; + + data_ = static_cast( + mmap(NULL, length_, PROT_READ, MAP_SHARED, file_, 0)); + if (data_ == MAP_FAILED) + data_ = NULL; + return data_ != NULL; +} + +void MemoryMappedFile::CloseHandles() { + if (data_ != NULL) + munmap(data_, length_); + if (file_ != -1) + close(file_); + + data_ = NULL; + length_ = 0; + file_ = -1; +} + +} // namespace file_util diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_util_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_util_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_util_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_util_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,1096 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#include +#include +#endif + +#include +#include +#include + +#include "base/base_paths.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/platform_thread.h" +#include "base/string_util.h" +#include "base/time.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +// This macro helps avoid wrapped lines in the test structs. +#define FPL(x) FILE_PATH_LITERAL(x) + +namespace { + +// file_util winds up using autoreleased objects on the Mac, so this needs +// to be a PlatformTest +class FileUtilTest : public PlatformTest { + protected: + virtual void SetUp() { + PlatformTest::SetUp(); + // Name a subdirectory of the temp directory. + ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_)); + test_dir_ = test_dir_.Append(FILE_PATH_LITERAL("FileUtilTest")); + + // Create a fresh, empty copy of this directory. + file_util::Delete(test_dir_, true); + file_util::CreateDirectory(test_dir_); + } + virtual void TearDown() { + PlatformTest::TearDown(); + // Clean up test directory + ASSERT_TRUE(file_util::Delete(test_dir_, true)); + ASSERT_FALSE(file_util::PathExists(test_dir_)); + } + + // the path to temporary directory used to contain the test operations + FilePath test_dir_; +}; + +// Collects all the results from the given file enumerator, and provides an +// interface to query whether a given file is present. +class FindResultCollector { + public: + FindResultCollector(file_util::FileEnumerator& enumerator) { + FilePath cur_file; + while (!(cur_file = enumerator.Next()).value().empty()) { + FilePath::StringType path = cur_file.value(); + // The file should not be returned twice. + EXPECT_TRUE(files_.end() == files_.find(path)) + << "Same file returned twice"; + + // Save for later. + files_.insert(path); + } + } + + // Returns true if the enumerator found the file. + bool HasFile(const FilePath& file) const { + return files_.find(file.value()) != files_.end(); + } + + int size() { + return static_cast(files_.size()); + } + + private: + std::set files_; +}; + +// Simple function to dump some text into a new file. +void CreateTextFile(const FilePath& filename, + const std::wstring& contents) { + std::ofstream file; + file.open(WideToUTF8(filename.ToWStringHack()).c_str()); + ASSERT_TRUE(file.is_open()); + file << contents; + file.close(); +} + +// Simple function to take out some text from a file. +std::wstring ReadTextFile(const FilePath& filename) { + wchar_t contents[64]; + std::wifstream file; + file.open(WideToUTF8(filename.ToWStringHack()).c_str()); + EXPECT_TRUE(file.is_open()); + file.getline(contents, 64); + file.close(); + return std::wstring(contents); +} + +#if defined(OS_WIN) +uint64 FileTimeAsUint64(const FILETIME& ft) { + ULARGE_INTEGER u; + u.LowPart = ft.dwLowDateTime; + u.HighPart = ft.dwHighDateTime; + return u.QuadPart; +} +#endif + +const struct append_case { + const wchar_t* path; + const wchar_t* ending; + const wchar_t* result; +} append_cases[] = { +#if defined(OS_WIN) + {L"c:\\colon\\backslash", L"path", L"c:\\colon\\backslash\\path"}, + {L"c:\\colon\\backslash\\", L"path", L"c:\\colon\\backslash\\path"}, + {L"c:\\colon\\backslash\\\\", L"path", L"c:\\colon\\backslash\\\\path"}, + {L"c:\\colon\\backslash\\", L"", L"c:\\colon\\backslash\\"}, + {L"c:\\colon\\backslash", L"", L"c:\\colon\\backslash\\"}, + {L"", L"path", L"\\path"}, + {L"", L"", L"\\"}, +#elif defined(OS_POSIX) + {L"/foo/bar", L"path", L"/foo/bar/path"}, + {L"/foo/bar/", L"path", L"/foo/bar/path"}, + {L"/foo/bar//", L"path", L"/foo/bar//path"}, + {L"/foo/bar/", L"", L"/foo/bar/"}, + {L"/foo/bar", L"", L"/foo/bar/"}, + {L"", L"path", L"/path"}, + {L"", L"", L"/"}, +#endif +}; + +TEST_F(FileUtilTest, AppendToPath) { + for (unsigned int i = 0; i < arraysize(append_cases); ++i) { + const append_case& value = append_cases[i]; + std::wstring result = value.path; + file_util::AppendToPath(&result, value.ending); + EXPECT_EQ(value.result, result); + } + +#ifdef NDEBUG + file_util::AppendToPath(NULL, L"path"); // asserts in debug mode +#endif +} + +static const struct InsertBeforeExtensionCase { + const FilePath::CharType* path; + const FilePath::CharType* suffix; + const FilePath::CharType* result; +} kInsertBeforeExtension[] = { + {FPL(""), FPL(""), FPL("")}, + {FPL(""), FPL("txt"), FPL("txt")}, + {FPL("."), FPL("txt"), FPL("txt.")}, + {FPL("."), FPL(""), FPL(".")}, + {FPL("foo.dll"), FPL("txt"), FPL("footxt.dll")}, + {FPL("foo.dll"), FPL(".txt"), FPL("foo.txt.dll")}, + {FPL("foo"), FPL("txt"), FPL("footxt")}, + {FPL("foo"), FPL(".txt"), FPL("foo.txt")}, + {FPL("foo.baz.dll"), FPL("txt"), FPL("foo.baztxt.dll")}, + {FPL("foo.baz.dll"), FPL(".txt"), FPL("foo.baz.txt.dll")}, + {FPL("foo.dll"), FPL(""), FPL("foo.dll")}, + {FPL("foo.dll"), FPL("."), FPL("foo..dll")}, + {FPL("foo"), FPL(""), FPL("foo")}, + {FPL("foo"), FPL("."), FPL("foo.")}, + {FPL("foo.baz.dll"), FPL(""), FPL("foo.baz.dll")}, + {FPL("foo.baz.dll"), FPL("."), FPL("foo.baz..dll")}, +#if defined(OS_WIN) + {FPL("\\"), FPL(""), FPL("\\")}, + {FPL("\\"), FPL("txt"), FPL("\\txt")}, + {FPL("\\."), FPL("txt"), FPL("\\txt.")}, + {FPL("\\."), FPL(""), FPL("\\.")}, + {FPL("C:\\bar\\foo.dll"), FPL("txt"), FPL("C:\\bar\\footxt.dll")}, + {FPL("C:\\bar.baz\\foodll"), FPL("txt"), FPL("C:\\bar.baz\\foodlltxt")}, + {FPL("C:\\bar.baz\\foo.dll"), FPL("txt"), FPL("C:\\bar.baz\\footxt.dll")}, + {FPL("C:\\bar.baz\\foo.dll.exe"), FPL("txt"), + FPL("C:\\bar.baz\\foo.dlltxt.exe")}, + {FPL("C:\\bar.baz\\foo"), FPL(""), FPL("C:\\bar.baz\\foo")}, + {FPL("C:\\bar.baz\\foo.exe"), FPL(""), FPL("C:\\bar.baz\\foo.exe")}, + {FPL("C:\\bar.baz\\foo.dll.exe"), FPL(""), FPL("C:\\bar.baz\\foo.dll.exe")}, + {FPL("C:\\bar\\baz\\foo.exe"), FPL(" (1)"), FPL("C:\\bar\\baz\\foo (1).exe")}, +#elif defined(OS_POSIX) + {FPL("/"), FPL(""), FPL("/")}, + {FPL("/"), FPL("txt"), FPL("/txt")}, + {FPL("/."), FPL("txt"), FPL("/txt.")}, + {FPL("/."), FPL(""), FPL("/.")}, + {FPL("/bar/foo.dll"), FPL("txt"), FPL("/bar/footxt.dll")}, + {FPL("/bar.baz/foodll"), FPL("txt"), FPL("/bar.baz/foodlltxt")}, + {FPL("/bar.baz/foo.dll"), FPL("txt"), FPL("/bar.baz/footxt.dll")}, + {FPL("/bar.baz/foo.dll.exe"), FPL("txt"), FPL("/bar.baz/foo.dlltxt.exe")}, + {FPL("/bar.baz/foo"), FPL(""), FPL("/bar.baz/foo")}, + {FPL("/bar.baz/foo.exe"), FPL(""), FPL("/bar.baz/foo.exe")}, + {FPL("/bar.baz/foo.dll.exe"), FPL(""), FPL("/bar.baz/foo.dll.exe")}, + {FPL("/bar/baz/foo.exe"), FPL(" (1)"), FPL("/bar/baz/foo (1).exe")}, +#endif +}; + +TEST_F(FileUtilTest, InsertBeforeExtensionTest) { + for (unsigned int i = 0; i < arraysize(kInsertBeforeExtension); ++i) { + FilePath path(kInsertBeforeExtension[i].path); + file_util::InsertBeforeExtension(&path, kInsertBeforeExtension[i].suffix); + EXPECT_EQ(kInsertBeforeExtension[i].result, path.value()); + } +} + +static const struct filename_case { + const wchar_t* path; + const wchar_t* filename; +} filename_cases[] = { +#if defined(OS_WIN) + {L"c:\\colon\\backslash", L"backslash"}, + {L"c:\\colon\\backslash\\", L""}, + {L"\\\\filename.exe", L"filename.exe"}, + {L"filename.exe", L"filename.exe"}, + {L"", L""}, + {L"\\\\\\", L""}, + {L"c:/colon/backslash", L"backslash"}, + {L"c:/colon/backslash/", L""}, + {L"//////", L""}, + {L"///filename.exe", L"filename.exe"}, +#elif defined(OS_POSIX) + {L"/foo/bar", L"bar"}, + {L"/foo/bar/", L""}, + {L"/filename.exe", L"filename.exe"}, + {L"filename.exe", L"filename.exe"}, + {L"", L""}, + {L"/", L""}, +#endif +}; + +TEST_F(FileUtilTest, GetFilenameFromPath) { + for (unsigned int i = 0; i < arraysize(filename_cases); ++i) { + const filename_case& value = filename_cases[i]; + std::wstring result = file_util::GetFilenameFromPath(value.path); + EXPECT_EQ(value.filename, result); + } +} + +// Test finding the file type from a path name +static const struct extension_case { + const wchar_t* path; + const wchar_t* extension; +} extension_cases[] = { +#if defined(OS_WIN) + {L"C:\\colon\\backslash\\filename.extension", L"extension"}, + {L"C:\\colon\\backslash\\filename.", L""}, + {L"C:\\colon\\backslash\\filename", L""}, + {L"C:\\colon\\backslash\\", L""}, + {L"C:\\colon\\backslash.\\", L""}, + {L"C:\\colon\\backslash\filename.extension.extension2", L"extension2"}, +#elif defined(OS_POSIX) + {L"/foo/bar/filename.extension", L"extension"}, + {L"/foo/bar/filename.", L""}, + {L"/foo/bar/filename", L""}, + {L"/foo/bar/", L""}, + {L"/foo/bar./", L""}, + {L"/foo/bar/filename.extension.extension2", L"extension2"}, + {L".", L""}, + {L"..", L""}, + {L"./foo", L""}, + {L"./foo.extension", L"extension"}, + {L"/foo.extension1/bar.extension2", L"extension2"}, +#endif +}; + +TEST_F(FileUtilTest, GetFileExtensionFromPath) { + for (unsigned int i = 0; i < arraysize(extension_cases); ++i) { + const extension_case& ext = extension_cases[i]; + const std::wstring fext = file_util::GetFileExtensionFromPath(ext.path); + EXPECT_EQ(ext.extension, fext); + } +} + +// Test finding the directory component of a path +static const struct dir_case { + const wchar_t* full_path; + const wchar_t* directory; +} dir_cases[] = { +#if defined(OS_WIN) + {L"C:\\WINDOWS\\system32\\gdi32.dll", L"C:\\WINDOWS\\system32"}, + {L"C:\\WINDOWS\\system32\\not_exist_thx_1138", L"C:\\WINDOWS\\system32"}, + {L"C:\\WINDOWS\\system32\\", L"C:\\WINDOWS\\system32"}, + {L"C:\\WINDOWS\\system32\\\\", L"C:\\WINDOWS\\system32"}, + {L"C:\\WINDOWS\\system32", L"C:\\WINDOWS"}, + {L"C:\\WINDOWS\\system32.\\", L"C:\\WINDOWS\\system32."}, + {L"C:\\", L"C:"}, +#elif defined(OS_POSIX) + {L"/foo/bar/gdi32.dll", L"/foo/bar"}, + {L"/foo/bar/not_exist_thx_1138", L"/foo/bar"}, + {L"/foo/bar/", L"/foo/bar"}, + {L"/foo/bar//", L"/foo/bar"}, + {L"/foo/bar", L"/foo"}, + {L"/foo/bar./", L"/foo/bar."}, + {L"/", L"/"}, + {L".", L"."}, + {L"..", L"."}, // yes, ".." technically lives in "." +#endif +}; + +TEST_F(FileUtilTest, GetDirectoryFromPath) { + for (unsigned int i = 0; i < arraysize(dir_cases); ++i) { + const dir_case& dir = dir_cases[i]; + const std::wstring parent = + file_util::GetDirectoryFromPath(dir.full_path); + EXPECT_EQ(dir.directory, parent); + } +} + +TEST_F(FileUtilTest, CountFilesCreatedAfter) { + // Create old file (that we don't want to count) + FilePath old_file_name = test_dir_.Append(FILE_PATH_LITERAL("Old File.txt")); + CreateTextFile(old_file_name, L"Just call me Mr. Creakybits"); + + // Age to perfection +#if defined(OS_WIN) + PlatformThread::Sleep(100); +#elif defined(OS_POSIX) + // We need to wait at least one second here because the precision of + // file creation time is one second. + PlatformThread::Sleep(1500); +#endif + + // Establish our cutoff time + base::Time now(base::Time::NowFromSystemTime()); + EXPECT_EQ(0, file_util::CountFilesCreatedAfter(test_dir_, now)); + + // Create a new file (that we do want to count) + FilePath new_file_name = test_dir_.Append(FILE_PATH_LITERAL("New File.txt")); + CreateTextFile(new_file_name, L"Waaaaaaaaaaaaaah."); + + // We should see only the new file. + EXPECT_EQ(1, file_util::CountFilesCreatedAfter(test_dir_, now)); + + // Delete new file, we should see no files after cutoff now + EXPECT_TRUE(file_util::Delete(new_file_name, false)); + EXPECT_EQ(0, file_util::CountFilesCreatedAfter(test_dir_, now)); +} + +// Tests that the Delete function works as expected, especially +// the recursion flag. Also coincidentally tests PathExists. +TEST_F(FileUtilTest, Delete) { + // Create a file + FilePath file_name = test_dir_.Append(FILE_PATH_LITERAL("Test File.txt")); + CreateTextFile(file_name, L"I'm cannon fodder."); + + ASSERT_TRUE(file_util::PathExists(file_name)); + + FilePath subdir_path = test_dir_.Append(FILE_PATH_LITERAL("Subdirectory")); + file_util::CreateDirectory(subdir_path); + + ASSERT_TRUE(file_util::PathExists(subdir_path)); + + FilePath directory_contents = test_dir_; +#if defined(OS_WIN) + // TODO(erikkay): see if anyone's actually using this feature of the API + directory_contents = directory_contents.Append(FILE_PATH_LITERAL("*")); + // Delete non-recursively and check that only the file is deleted + ASSERT_TRUE(file_util::Delete(directory_contents, false)); + EXPECT_FALSE(file_util::PathExists(file_name)); + EXPECT_TRUE(file_util::PathExists(subdir_path)); +#endif + + // Delete recursively and make sure all contents are deleted + ASSERT_TRUE(file_util::Delete(directory_contents, true)); + EXPECT_FALSE(file_util::PathExists(file_name)); + EXPECT_FALSE(file_util::PathExists(subdir_path)); +} + +TEST_F(FileUtilTest, Move) { + // Create a directory + FilePath dir_name_from = + test_dir_.Append(FILE_PATH_LITERAL("Move_From_Subdir")); + file_util::CreateDirectory(dir_name_from); + ASSERT_TRUE(file_util::PathExists(dir_name_from)); + + // Create a file under the directory + FilePath file_name_from = + dir_name_from.Append(FILE_PATH_LITERAL("Move_Test_File.txt")); + CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); + ASSERT_TRUE(file_util::PathExists(file_name_from)); + + // Move the directory + FilePath dir_name_to = test_dir_.Append(FILE_PATH_LITERAL("Move_To_Subdir")); + FilePath file_name_to = + dir_name_to.Append(FILE_PATH_LITERAL("Move_Test_File.txt")); + + ASSERT_FALSE(file_util::PathExists(dir_name_to)); + + EXPECT_TRUE(file_util::Move(dir_name_from, dir_name_to)); + + // Check everything has been moved. + EXPECT_FALSE(file_util::PathExists(dir_name_from)); + EXPECT_FALSE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(dir_name_to)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); +} + +TEST_F(FileUtilTest, CopyDirectoryRecursively) { + // Create a directory. + FilePath dir_name_from = + test_dir_.Append(FILE_PATH_LITERAL("Copy_From_Subdir")); + file_util::CreateDirectory(dir_name_from); + ASSERT_TRUE(file_util::PathExists(dir_name_from)); + + // Create a file under the directory. + FilePath file_name_from = + dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); + CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); + ASSERT_TRUE(file_util::PathExists(file_name_from)); + + // Create a subdirectory. + FilePath subdir_name_from = + dir_name_from.Append(FILE_PATH_LITERAL("Subdir")); + file_util::CreateDirectory(subdir_name_from); + ASSERT_TRUE(file_util::PathExists(subdir_name_from)); + + // Create a file under the subdirectory. + FilePath file_name2_from = + subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); + CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle"); + ASSERT_TRUE(file_util::PathExists(file_name2_from)); + + // Copy the directory recursively. + FilePath dir_name_to = + test_dir_.Append(FILE_PATH_LITERAL("Copy_To_Subdir")); + FilePath file_name_to = + dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); + FilePath subdir_name_to = + dir_name_to.Append(FILE_PATH_LITERAL("Subdir")); + FilePath file_name2_to = + subdir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); + + ASSERT_FALSE(file_util::PathExists(dir_name_to)); + + EXPECT_TRUE(file_util::CopyDirectory(dir_name_from, dir_name_to, true)); + + // Check everything has been copied. + EXPECT_TRUE(file_util::PathExists(dir_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(subdir_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name2_from)); + EXPECT_TRUE(file_util::PathExists(dir_name_to)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_TRUE(file_util::PathExists(subdir_name_to)); + EXPECT_TRUE(file_util::PathExists(file_name2_to)); +} + +TEST_F(FileUtilTest, CopyDirectory) { + // Create a directory. + FilePath dir_name_from = + test_dir_.Append(FILE_PATH_LITERAL("Copy_From_Subdir")); + file_util::CreateDirectory(dir_name_from); + ASSERT_TRUE(file_util::PathExists(dir_name_from)); + + // Create a file under the directory. + FilePath file_name_from = + dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); + CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); + ASSERT_TRUE(file_util::PathExists(file_name_from)); + + // Create a subdirectory. + FilePath subdir_name_from = + dir_name_from.Append(FILE_PATH_LITERAL("Subdir")); + file_util::CreateDirectory(subdir_name_from); + ASSERT_TRUE(file_util::PathExists(subdir_name_from)); + + // Create a file under the subdirectory. + FilePath file_name2_from = + subdir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); + CreateTextFile(file_name2_from, L"Gooooooooooooooooooooogle"); + ASSERT_TRUE(file_util::PathExists(file_name2_from)); + + // Copy the directory not recursively. + FilePath dir_name_to = + test_dir_.Append(FILE_PATH_LITERAL("Copy_To_Subdir")); + FilePath file_name_to = + dir_name_to.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); + FilePath subdir_name_to = + dir_name_to.Append(FILE_PATH_LITERAL("Subdir")); + + ASSERT_FALSE(file_util::PathExists(dir_name_to)); + + EXPECT_TRUE(file_util::CopyDirectory(dir_name_from, dir_name_to, false)); + + // Check everything has been copied. + EXPECT_TRUE(file_util::PathExists(dir_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(subdir_name_from)); + EXPECT_TRUE(file_util::PathExists(file_name2_from)); + EXPECT_TRUE(file_util::PathExists(dir_name_to)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); + EXPECT_FALSE(file_util::PathExists(subdir_name_to)); +} + +TEST_F(FileUtilTest, CopyFile) { + // Create a directory + FilePath dir_name_from = + test_dir_.Append(FILE_PATH_LITERAL("Copy_From_Subdir")); + file_util::CreateDirectory(dir_name_from); + ASSERT_TRUE(file_util::PathExists(dir_name_from)); + + // Create a file under the directory + FilePath file_name_from = + dir_name_from.Append(FILE_PATH_LITERAL("Copy_Test_File.txt")); + const std::wstring file_contents(L"Gooooooooooooooooooooogle"); + CreateTextFile(file_name_from, file_contents); + ASSERT_TRUE(file_util::PathExists(file_name_from)); + + // Copy the file. + FilePath dest_file = dir_name_from.Append(FILE_PATH_LITERAL("DestFile.txt")); + ASSERT_TRUE(file_util::CopyFile(file_name_from, dest_file)); + + // Copy the file to another location using '..' in the path. + std::wstring dest_file2(dir_name_from.ToWStringHack()); + file_util::AppendToPath(&dest_file2, L".."); + file_util::AppendToPath(&dest_file2, L"DestFile.txt"); + ASSERT_TRUE(file_util::CopyFile(file_name_from, + FilePath::FromWStringHack(dest_file2))); + std::wstring dest_file2_test(dir_name_from.ToWStringHack()); + file_util::UpOneDirectory(&dest_file2_test); + file_util::AppendToPath(&dest_file2_test, L"DestFile.txt"); + + // Check everything has been copied. + EXPECT_TRUE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(dest_file)); + const std::wstring read_contents = ReadTextFile(dest_file); + EXPECT_EQ(file_contents, read_contents); + EXPECT_TRUE(file_util::PathExists( + FilePath::FromWStringHack(dest_file2_test))); + EXPECT_TRUE(file_util::PathExists(FilePath::FromWStringHack(dest_file2))); +} + +// TODO(erikkay): implement +#if defined(OS_WIN) +TEST_F(FileUtilTest, GetFileCreationLocalTime) { + FilePath file_name = test_dir_.Append(L"Test File.txt"); + + SYSTEMTIME start_time; + GetLocalTime(&start_time); + Sleep(100); + CreateTextFile(file_name, L"New file!"); + Sleep(100); + SYSTEMTIME end_time; + GetLocalTime(&end_time); + + SYSTEMTIME file_creation_time; + file_util::GetFileCreationLocalTime(file_name.value(), &file_creation_time); + + FILETIME start_filetime; + SystemTimeToFileTime(&start_time, &start_filetime); + FILETIME end_filetime; + SystemTimeToFileTime(&end_time, &end_filetime); + FILETIME file_creation_filetime; + SystemTimeToFileTime(&file_creation_time, &file_creation_filetime); + + EXPECT_EQ(-1, CompareFileTime(&start_filetime, &file_creation_filetime)) << + "start time: " << FileTimeAsUint64(start_filetime) << ", " << + "creation time: " << FileTimeAsUint64(file_creation_filetime); + + EXPECT_EQ(-1, CompareFileTime(&file_creation_filetime, &end_filetime)) << + "creation time: " << FileTimeAsUint64(file_creation_filetime) << ", " << + "end time: " << FileTimeAsUint64(end_filetime); + + ASSERT_TRUE(DeleteFile(file_name.value().c_str())); +} +#endif + +// file_util winds up using autoreleased objects on the Mac, so this needs +// to be a PlatformTest. +typedef PlatformTest ReadOnlyFileUtilTest; + +TEST_F(ReadOnlyFileUtilTest, ContentsEqual) { + FilePath data_dir; + ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir)); + data_dir = data_dir.Append(FILE_PATH_LITERAL("base")) + .Append(FILE_PATH_LITERAL("data")) + .Append(FILE_PATH_LITERAL("file_util_unittest")); + ASSERT_TRUE(file_util::PathExists(data_dir)); + + FilePath original_file = + data_dir.Append(FILE_PATH_LITERAL("original.txt")); + FilePath same_file = + data_dir.Append(FILE_PATH_LITERAL("same.txt")); + FilePath same_length_file = + data_dir.Append(FILE_PATH_LITERAL("same_length.txt")); + FilePath different_file = + data_dir.Append(FILE_PATH_LITERAL("different.txt")); + FilePath different_first_file = + data_dir.Append(FILE_PATH_LITERAL("different_first.txt")); + FilePath different_last_file = + data_dir.Append(FILE_PATH_LITERAL("different_last.txt")); + FilePath empty1_file = + data_dir.Append(FILE_PATH_LITERAL("empty1.txt")); + FilePath empty2_file = + data_dir.Append(FILE_PATH_LITERAL("empty2.txt")); + FilePath shortened_file = + data_dir.Append(FILE_PATH_LITERAL("shortened.txt")); + FilePath binary_file = + data_dir.Append(FILE_PATH_LITERAL("binary_file.bin")); + FilePath binary_file_same = + data_dir.Append(FILE_PATH_LITERAL("binary_file_same.bin")); + FilePath binary_file_diff = + data_dir.Append(FILE_PATH_LITERAL("binary_file_diff.bin")); + + EXPECT_TRUE(file_util::ContentsEqual(original_file, original_file)); + EXPECT_TRUE(file_util::ContentsEqual(original_file, same_file)); + EXPECT_FALSE(file_util::ContentsEqual(original_file, same_length_file)); + EXPECT_FALSE(file_util::ContentsEqual(original_file, different_file)); + EXPECT_FALSE(file_util::ContentsEqual(L"bogusname", L"bogusname")); + EXPECT_FALSE(file_util::ContentsEqual(original_file, different_first_file)); + EXPECT_FALSE(file_util::ContentsEqual(original_file, different_last_file)); + EXPECT_TRUE(file_util::ContentsEqual(empty1_file, empty2_file)); + EXPECT_FALSE(file_util::ContentsEqual(original_file, shortened_file)); + EXPECT_FALSE(file_util::ContentsEqual(shortened_file, original_file)); + EXPECT_TRUE(file_util::ContentsEqual(binary_file, binary_file_same)); + EXPECT_FALSE(file_util::ContentsEqual(binary_file, binary_file_diff)); +} + +// We don't need equivalent functionality outside of Windows. +#if defined(OS_WIN) +TEST_F(FileUtilTest, ResolveShortcutTest) { + FilePath target_file = test_dir_.Append(L"Target.txt"); + CreateTextFile(target_file, L"This is the target."); + + FilePath link_file = test_dir_.Append(L"Link.lnk"); + + HRESULT result; + IShellLink *shell = NULL; + IPersistFile *persist = NULL; + + CoInitialize(NULL); + // Temporarily create a shortcut for test + result = CoCreateInstance(CLSID_ShellLink, NULL, + CLSCTX_INPROC_SERVER, IID_IShellLink, + reinterpret_cast(&shell)); + EXPECT_TRUE(SUCCEEDED(result)); + result = shell->QueryInterface(IID_IPersistFile, + reinterpret_cast(&persist)); + EXPECT_TRUE(SUCCEEDED(result)); + result = shell->SetPath(target_file.value().c_str()); + EXPECT_TRUE(SUCCEEDED(result)); + result = shell->SetDescription(L"ResolveShortcutTest"); + EXPECT_TRUE(SUCCEEDED(result)); + result = persist->Save(link_file.value().c_str(), TRUE); + EXPECT_TRUE(SUCCEEDED(result)); + if (persist) + persist->Release(); + if (shell) + shell->Release(); + + bool is_solved; + std::wstring link_file_str = link_file.value(); + is_solved = file_util::ResolveShortcut(&link_file_str); + EXPECT_TRUE(is_solved); + std::wstring contents; + contents = ReadTextFile(FilePath(link_file_str)); + EXPECT_EQ(L"This is the target.", contents); + + // Cleaning + DeleteFile(target_file.value().c_str()); + DeleteFile(link_file_str.c_str()); + CoUninitialize(); +} + +TEST_F(FileUtilTest, CreateShortcutTest) { + const wchar_t file_contents[] = L"This is another target."; + FilePath target_file = test_dir_.Append(L"Target1.txt"); + CreateTextFile(target_file, file_contents); + + FilePath link_file = test_dir_.Append(L"Link1.lnk"); + + CoInitialize(NULL); + EXPECT_TRUE(file_util::CreateShortcutLink(target_file.value().c_str(), + link_file.value().c_str(), + NULL, NULL, NULL, NULL, 0)); + std::wstring resolved_name = link_file.value(); + EXPECT_TRUE(file_util::ResolveShortcut(&resolved_name)); + std::wstring read_contents = ReadTextFile(FilePath(resolved_name)); + EXPECT_EQ(file_contents, read_contents); + + DeleteFile(target_file.value().c_str()); + DeleteFile(link_file.value().c_str()); + CoUninitialize(); +} + +TEST_F(FileUtilTest, CopyAndDeleteDirectoryTest) { + // Create a directory + FilePath dir_name_from = + test_dir_.Append(FILE_PATH_LITERAL("CopyAndDelete_From_Subdir")); + file_util::CreateDirectory(dir_name_from); + ASSERT_TRUE(file_util::PathExists(dir_name_from)); + + // Create a file under the directory + FilePath file_name_from = + dir_name_from.Append(FILE_PATH_LITERAL("CopyAndDelete_Test_File.txt")); + CreateTextFile(file_name_from, L"Gooooooooooooooooooooogle"); + ASSERT_TRUE(file_util::PathExists(file_name_from)); + + // Move the directory by using CopyAndDeleteDirectory + FilePath dir_name_to = test_dir_.Append( + FILE_PATH_LITERAL("CopyAndDelete_To_Subdir")); + FilePath file_name_to = + dir_name_to.Append(FILE_PATH_LITERAL("CopyAndDelete_Test_File.txt")); + + ASSERT_FALSE(file_util::PathExists(dir_name_to)); + + EXPECT_TRUE(file_util::CopyAndDeleteDirectory(dir_name_from, dir_name_to)); + + // Check everything has been moved. + EXPECT_FALSE(file_util::PathExists(dir_name_from)); + EXPECT_FALSE(file_util::PathExists(file_name_from)); + EXPECT_TRUE(file_util::PathExists(dir_name_to)); + EXPECT_TRUE(file_util::PathExists(file_name_to)); +} +#endif + +TEST_F(FileUtilTest, CreateTemporaryFileNameTest) { + std::wstring temp_files[3]; + for (int i = 0; i < 3; i++) { + ASSERT_TRUE(file_util::CreateTemporaryFileName(&(temp_files[i]))); + EXPECT_TRUE(file_util::PathExists(temp_files[i])); + EXPECT_FALSE(file_util::DirectoryExists(temp_files[i])); + } + for (int i = 0; i < 3; i++) + EXPECT_FALSE(temp_files[i] == temp_files[(i+1)%3]); + for (int i = 0; i < 3; i++) + EXPECT_TRUE(file_util::Delete(temp_files[i], false)); +} + +TEST_F(FileUtilTest, CreateAndOpenTemporaryFileNameTest) { + FilePath names[3]; + FILE *fps[3]; + int i; + + // Create; make sure they are open and exist. + for (i = 0; i < 3; ++i) { + fps[i] = file_util::CreateAndOpenTemporaryFile(&(names[i])); + ASSERT_TRUE(fps[i]); + EXPECT_TRUE(file_util::PathExists(names[i])); + } + + // Make sure all names are unique. + for (i = 0; i < 3; ++i) { + EXPECT_FALSE(names[i] == names[(i+1)%3]); + } + + // Close and delete. + for (i = 0; i < 3; ++i) { + EXPECT_TRUE(file_util::CloseFile(fps[i])); + EXPECT_TRUE(file_util::Delete(names[i], false)); + } +} + +TEST_F(FileUtilTest, CreateNewTempDirectoryTest) { + std::wstring temp_dir; + ASSERT_TRUE(file_util::CreateNewTempDirectory(std::wstring(), &temp_dir)); + EXPECT_TRUE(file_util::PathExists(temp_dir)); + EXPECT_TRUE(file_util::Delete(temp_dir, false)); +} + +TEST_F(FileUtilTest, GetShmemTempDirTest) { + FilePath dir; + EXPECT_TRUE(file_util::GetShmemTempDir(&dir)); + EXPECT_TRUE(file_util::DirectoryExists(dir)); +} + +TEST_F(FileUtilTest, CreateDirectoryTest) { + FilePath test_root = + test_dir_.Append(FILE_PATH_LITERAL("create_directory_test")); +#if defined(OS_WIN) + FilePath test_path = + test_root.Append(FILE_PATH_LITERAL("dir\\tree\\likely\\doesnt\\exist\\")); +#elif defined(OS_POSIX) + FilePath test_path = + test_root.Append(FILE_PATH_LITERAL("dir/tree/likely/doesnt/exist/")); +#endif + + EXPECT_FALSE(file_util::PathExists(test_path)); + EXPECT_TRUE(file_util::CreateDirectory(test_path)); + EXPECT_TRUE(file_util::PathExists(test_path)); + // CreateDirectory returns true if the DirectoryExists returns true. + EXPECT_TRUE(file_util::CreateDirectory(test_path)); + + // Doesn't work to create it on top of a non-dir + test_path = test_path.Append(FILE_PATH_LITERAL("foobar.txt")); + EXPECT_FALSE(file_util::PathExists(test_path)); + CreateTextFile(test_path, L"test file"); + EXPECT_TRUE(file_util::PathExists(test_path)); + EXPECT_FALSE(file_util::CreateDirectory(test_path)); + + EXPECT_TRUE(file_util::Delete(test_root, true)); + EXPECT_FALSE(file_util::PathExists(test_root)); + EXPECT_FALSE(file_util::PathExists(test_path)); +} + +TEST_F(FileUtilTest, DetectDirectoryTest) { + // Check a directory + FilePath test_root = + test_dir_.Append(FILE_PATH_LITERAL("detect_directory_test")); + EXPECT_FALSE(file_util::PathExists(test_root)); + EXPECT_TRUE(file_util::CreateDirectory(test_root)); + EXPECT_TRUE(file_util::PathExists(test_root)); + EXPECT_TRUE(file_util::DirectoryExists(test_root)); + + // Check a file + FilePath test_path = + test_root.Append(FILE_PATH_LITERAL("foobar.txt")); + EXPECT_FALSE(file_util::PathExists(test_path)); + CreateTextFile(test_path, L"test file"); + EXPECT_TRUE(file_util::PathExists(test_path)); + EXPECT_FALSE(file_util::DirectoryExists(test_path)); + EXPECT_TRUE(file_util::Delete(test_path, false)); + + EXPECT_TRUE(file_util::Delete(test_root, true)); +} + +static const struct goodbad_pair { + std::wstring bad_name; + std::wstring good_name; +} kIllegalCharacterCases[] = { + {L"bad*file:name?.jpg", L"bad-file-name-.jpg"}, + {L"**********::::.txt", L"--------------.txt"}, + // We can't use UCNs (universal character names) for C0/C1 characters and + // U+007F, but \x escape is interpreted by MSVC and gcc as we intend. + {L"bad\x0003\x0091 file\u200E\u200Fname.png", L"bad-- file--name.png"}, +#if defined(OS_WIN) + {L"bad*file\\name.jpg", L"bad-file-name.jpg"}, + {L"\t bad*file\\name/.jpg ", L"bad-file-name-.jpg"}, + {L"bad\uFFFFfile\U0010FFFEname.jpg ", L"bad-file-name.jpg"}, +#elif defined(OS_POSIX) + {L"bad*file?name.jpg", L"bad-file-name.jpg"}, + {L"\t bad*file?name/.jpg ", L"bad-file-name-.jpg"}, + {L"bad\uFFFFfile-name.jpg ", L"bad-file-name.jpg"}, +#endif + {L"this_file_name is okay!.mp3", L"this_file_name is okay!.mp3"}, + {L"\u4E00\uAC00.mp3", L"\u4E00\uAC00.mp3"}, + {L"\u0635\u200C\u0644.mp3", L"\u0635\u200C\u0644.mp3"}, + {L"\U00010330\U00010331.mp3", L"\U00010330\U00010331.mp3"}, + // Unassigned codepoints are ok. + {L"\u0378\U00040001.mp3", L"\u0378\U00040001.mp3"}, +}; + +TEST_F(FileUtilTest, ReplaceIllegalCharactersTest) { + for (unsigned int i = 0; i < arraysize(kIllegalCharacterCases); ++i) { + std::wstring bad_name(kIllegalCharacterCases[i].bad_name); + file_util::ReplaceIllegalCharacters(&bad_name, L'-'); + EXPECT_EQ(kIllegalCharacterCases[i].good_name, bad_name); + } +} + +static const struct ReplaceExtensionCase { + std::wstring file_name; + FilePath::StringType extension; + std::wstring result; +} kReplaceExtension[] = { + {L"", FILE_PATH_LITERAL(""), L""}, + {L"", FILE_PATH_LITERAL("txt"), L".txt"}, + {L".", FILE_PATH_LITERAL("txt"), L".txt"}, + {L".", FILE_PATH_LITERAL(""), L""}, + {L"foo.dll", FILE_PATH_LITERAL("txt"), L"foo.txt"}, + {L"foo.dll", FILE_PATH_LITERAL(".txt"), L"foo.txt"}, + {L"foo", FILE_PATH_LITERAL("txt"), L"foo.txt"}, + {L"foo", FILE_PATH_LITERAL(".txt"), L"foo.txt"}, + {L"foo.baz.dll", FILE_PATH_LITERAL("txt"), L"foo.baz.txt"}, + {L"foo.baz.dll", FILE_PATH_LITERAL(".txt"), L"foo.baz.txt"}, + {L"foo.dll", FILE_PATH_LITERAL(""), L"foo"}, + {L"foo.dll", FILE_PATH_LITERAL("."), L"foo"}, + {L"foo", FILE_PATH_LITERAL(""), L"foo"}, + {L"foo", FILE_PATH_LITERAL("."), L"foo"}, + {L"foo.baz.dll", FILE_PATH_LITERAL(""), L"foo.baz"}, + {L"foo.baz.dll", FILE_PATH_LITERAL("."), L"foo.baz"}, +}; + +TEST_F(FileUtilTest, ReplaceExtensionTest) { + for (unsigned int i = 0; i < arraysize(kReplaceExtension); ++i) { + FilePath path = FilePath::FromWStringHack(kReplaceExtension[i].file_name); + file_util::ReplaceExtension(&path, kReplaceExtension[i].extension); + EXPECT_EQ(kReplaceExtension[i].result, path.ToWStringHack()); + } +} + +// Make sure ReplaceExtension doesn't replace an extension that occurs as one of +// the directory names of the path. +TEST_F(FileUtilTest, ReplaceExtensionTestWithPathSeparators) { + FilePath path; + path = path.Append(FILE_PATH_LITERAL("foo.bar")); + path = path.Append(FILE_PATH_LITERAL("foo")); + // '/foo.bar/foo' with extension '.baz' + FilePath result_path = path; + file_util::ReplaceExtension(&result_path, FILE_PATH_LITERAL(".baz")); + EXPECT_EQ(path.value() + FILE_PATH_LITERAL(".baz"), + result_path.value()); +} + +TEST_F(FileUtilTest, FileEnumeratorTest) { + // Test an empty directory. + file_util::FileEnumerator f0(test_dir_, true, + file_util::FileEnumerator::FILES_AND_DIRECTORIES); + EXPECT_EQ(f0.Next().value(), FILE_PATH_LITERAL("")); + EXPECT_EQ(f0.Next().value(), FILE_PATH_LITERAL("")); + + // create the directories + FilePath dir1 = test_dir_.Append(FILE_PATH_LITERAL("dir1")); + EXPECT_TRUE(file_util::CreateDirectory(dir1)); + FilePath dir2 = test_dir_.Append(FILE_PATH_LITERAL("dir2")); + EXPECT_TRUE(file_util::CreateDirectory(dir2)); + FilePath dir2inner = dir2.Append(FILE_PATH_LITERAL("inner")); + EXPECT_TRUE(file_util::CreateDirectory(dir2inner)); + + // create the files + FilePath dir2file = dir2.Append(FILE_PATH_LITERAL("dir2file.txt")); + CreateTextFile(dir2file, L""); + FilePath dir2innerfile = dir2inner.Append(FILE_PATH_LITERAL("innerfile.txt")); + CreateTextFile(dir2innerfile, L""); + FilePath file1 = test_dir_.Append(FILE_PATH_LITERAL("file1.txt")); + CreateTextFile(file1, L""); + FilePath file2_rel = + dir2.Append(FilePath::kParentDirectory) + .Append(FILE_PATH_LITERAL("file2.txt")); + CreateTextFile(file2_rel, L""); + FilePath file2_abs = test_dir_.Append(FILE_PATH_LITERAL("file2.txt")); + + // Only enumerate files. + file_util::FileEnumerator f1(test_dir_, true, + file_util::FileEnumerator::FILES); + FindResultCollector c1(f1); + EXPECT_TRUE(c1.HasFile(file1)); + EXPECT_TRUE(c1.HasFile(file2_abs)); + EXPECT_TRUE(c1.HasFile(dir2file)); + EXPECT_TRUE(c1.HasFile(dir2innerfile)); + EXPECT_EQ(c1.size(), 4); + + // Only enumerate directories. + file_util::FileEnumerator f2(test_dir_, true, + file_util::FileEnumerator::DIRECTORIES); + FindResultCollector c2(f2); + EXPECT_TRUE(c2.HasFile(dir1)); + EXPECT_TRUE(c2.HasFile(dir2)); + EXPECT_TRUE(c2.HasFile(dir2inner)); + EXPECT_EQ(c2.size(), 3); + + // Only enumerate directories non-recursively. + file_util::FileEnumerator f2_non_recursive( + test_dir_, false, file_util::FileEnumerator::DIRECTORIES); + FindResultCollector c2_non_recursive(f2_non_recursive); + EXPECT_TRUE(c2_non_recursive.HasFile(dir1)); + EXPECT_TRUE(c2_non_recursive.HasFile(dir2)); + EXPECT_EQ(c2_non_recursive.size(), 2); + + // Enumerate files and directories. + file_util::FileEnumerator f3(test_dir_, true, + file_util::FileEnumerator::FILES_AND_DIRECTORIES); + FindResultCollector c3(f3); + EXPECT_TRUE(c3.HasFile(dir1)); + EXPECT_TRUE(c3.HasFile(dir2)); + EXPECT_TRUE(c3.HasFile(file1)); + EXPECT_TRUE(c3.HasFile(file2_abs)); + EXPECT_TRUE(c3.HasFile(dir2file)); + EXPECT_TRUE(c3.HasFile(dir2inner)); + EXPECT_TRUE(c3.HasFile(dir2innerfile)); + EXPECT_EQ(c3.size(), 7); + + // Non-recursive operation. + file_util::FileEnumerator f4(test_dir_, false, + file_util::FileEnumerator::FILES_AND_DIRECTORIES); + FindResultCollector c4(f4); + EXPECT_TRUE(c4.HasFile(dir2)); + EXPECT_TRUE(c4.HasFile(dir2)); + EXPECT_TRUE(c4.HasFile(file1)); + EXPECT_TRUE(c4.HasFile(file2_abs)); + EXPECT_EQ(c4.size(), 4); + + // Enumerate with a pattern. + file_util::FileEnumerator f5(test_dir_, true, + file_util::FileEnumerator::FILES_AND_DIRECTORIES, + FILE_PATH_LITERAL("dir*")); + FindResultCollector c5(f5); + EXPECT_TRUE(c5.HasFile(dir1)); + EXPECT_TRUE(c5.HasFile(dir2)); + EXPECT_TRUE(c5.HasFile(dir2file)); + EXPECT_TRUE(c5.HasFile(dir2inner)); + EXPECT_TRUE(c5.HasFile(dir2innerfile)); + EXPECT_EQ(c5.size(), 5); + + // Make sure the destructor closes the find handle while in the middle of a + // query to allow TearDown to delete the directory. + file_util::FileEnumerator f6(test_dir_, true, + file_util::FileEnumerator::FILES_AND_DIRECTORIES); + EXPECT_FALSE(f6.Next().value().empty()); // Should have found something + // (we don't care what). +} + + +void PathComponents(const std::wstring& path, + std::vector* components) { + DCHECK(components != NULL); + if (components == NULL) + return; + std::wstring::size_type start = 0; + std::wstring::size_type end = path.find('/', start); + + // Special case the "/" or "\" directory. On Windows with a drive letter, + // this code path won't hit, but the right thing should still happen. + // "E:\foo" will turn into "E:","foo". + if (end == start) { + components->push_back(std::wstring(path, 0, 1)); + start = end + 1; + end = path.find('/', start); + } + while (end != std::wstring::npos) { + std::wstring component = std::wstring(path, start, end - start); + components->push_back(component); + start = end + 1; + end = path.find('/', start); + } + std::wstring component = std::wstring(path, start); + components->push_back(component); +} + +static const struct PathComponentsCase { + const FilePath::CharType* path; + const FilePath::CharType* result; +} kPathComponents[] = { + {FILE_PATH_LITERAL("/foo/bar/baz/"), FILE_PATH_LITERAL("/|foo|bar|baz|")}, + {FILE_PATH_LITERAL("/foo/bar/baz"), FILE_PATH_LITERAL("/|foo|bar|baz")}, + {FILE_PATH_LITERAL("e:/foo"), FILE_PATH_LITERAL("e:|foo")}, +}; + +TEST_F(FileUtilTest, PathComponentsTest) { + for (size_t i = 0; i < arraysize(kPathComponents); ++i) { + FilePath path(kPathComponents[i].path); + std::vector comps; + file_util::PathComponents(path, &comps); + + FilePath::StringType result; + for (size_t j = 0; j < comps.size(); ++j) { + result.append(comps[j]); + if (j < comps.size() - 1) + result.append(FILE_PATH_LITERAL("|"), 1); + } + EXPECT_EQ(kPathComponents[i].result, result); + } +} + +TEST_F(FileUtilTest, Contains) { + FilePath data_dir = test_dir_.Append(FILE_PATH_LITERAL("FilePathTest")); + + // Create a fresh, empty copy of this directory. + if (file_util::PathExists(data_dir)) { + ASSERT_TRUE(file_util::Delete(data_dir, true)); + } + ASSERT_TRUE(file_util::CreateDirectory(data_dir)); + + FilePath foo(data_dir.Append(FILE_PATH_LITERAL("foo"))); + FilePath bar(foo.Append(FILE_PATH_LITERAL("bar.txt"))); + FilePath baz(data_dir.Append(FILE_PATH_LITERAL("baz.txt"))); + FilePath foobar(data_dir.Append(FILE_PATH_LITERAL("foobar.txt"))); + + // Annoyingly, the directories must actually exist in order for realpath(), + // which Contains() relies on in posix, to work. + ASSERT_TRUE(file_util::CreateDirectory(foo)); + std::string data("hello"); + ASSERT_TRUE(file_util::WriteFile(bar, data.c_str(), data.length())); + ASSERT_TRUE(file_util::WriteFile(baz, data.c_str(), data.length())); + ASSERT_TRUE(file_util::WriteFile(foobar, data.c_str(), data.length())); + + EXPECT_TRUE(file_util::ContainsPath(foo, bar)); + EXPECT_FALSE(file_util::ContainsPath(foo, baz)); + EXPECT_FALSE(file_util::ContainsPath(foo, foobar)); + EXPECT_FALSE(file_util::ContainsPath(foo, foo)); + +// Platform-specific concerns + FilePath foo_caps(data_dir.Append(FILE_PATH_LITERAL("FOO"))); +#if defined(OS_WIN) + EXPECT_TRUE(file_util::ContainsPath(foo, + foo_caps.Append(FILE_PATH_LITERAL("bar.txt")))); + EXPECT_TRUE(file_util::ContainsPath(foo, + FilePath(foo.value() + FILE_PATH_LITERAL("/bar.txt")))); +#elif defined(OS_LINUX) + EXPECT_FALSE(file_util::ContainsPath(foo, + foo_caps.Append(FILE_PATH_LITERAL("bar.txt")))); +#else + // We can't really do this test on osx since the case-sensitivity of the + // filesystem is configurable. +#endif +} + +} // namespace diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_util_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_util_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_util_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_util_win.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,817 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_util.h" + +#include +#include +#include +#include +#include + +#include "base/file_path.h" +#include "base/logging.h" +#include "base/scoped_handle.h" +#include "base/string_util.h" +#include "base/time.h" +#include "base/win_util.h" + +namespace file_util { + +std::wstring GetDirectoryFromPath(const std::wstring& path) { + wchar_t path_buffer[MAX_PATH]; + wchar_t* file_ptr = NULL; + if (GetFullPathName(path.c_str(), MAX_PATH, path_buffer, &file_ptr) == 0) + return L""; + + std::wstring::size_type length = + file_ptr ? file_ptr - path_buffer : path.length(); + std::wstring directory(path, 0, length); + TrimTrailingSeparator(&directory); + return directory; +} + +bool AbsolutePath(FilePath* path) { + wchar_t file_path_buf[MAX_PATH]; + if (!_wfullpath(file_path_buf, path->value().c_str(), MAX_PATH)) + return false; + *path = FilePath(file_path_buf); + return true; +} + +int CountFilesCreatedAfter(const FilePath& path, + const base::Time& comparison_time) { + int file_count = 0; + FILETIME comparison_filetime(comparison_time.ToFileTime()); + + WIN32_FIND_DATA find_file_data; + // All files in given dir + std::wstring filename_spec = path.Append(L"*").value(); + HANDLE find_handle = FindFirstFile(filename_spec.c_str(), &find_file_data); + if (find_handle != INVALID_HANDLE_VALUE) { + do { + // Don't count current or parent directories. + if ((wcscmp(find_file_data.cFileName, L"..") == 0) || + (wcscmp(find_file_data.cFileName, L".") == 0)) + continue; + + long result = CompareFileTime(&find_file_data.ftCreationTime, + &comparison_filetime); + // File was created after or on comparison time + if ((result == 1) || (result == 0)) + ++file_count; + } while (FindNextFile(find_handle, &find_file_data)); + FindClose(find_handle); + } + + return file_count; +} + +bool Delete(const FilePath& path, bool recursive) { + if (path.value().length() >= MAX_PATH) + return false; + + // If we're not recursing use DeleteFile; it should be faster. DeleteFile + // fails if passed a directory though, which is why we fall through on + // failure to the SHFileOperation. + if (!recursive && DeleteFile(path.value().c_str()) != 0) + return true; + + // SHFILEOPSTRUCT wants the path to be terminated with two NULLs, + // so we have to use wcscpy because wcscpy_s writes non-NULLs + // into the rest of the buffer. + wchar_t double_terminated_path[MAX_PATH + 1] = {0}; +#pragma warning(suppress:4996) // don't complain about wcscpy deprecation + wcscpy(double_terminated_path, path.value().c_str()); + + SHFILEOPSTRUCT file_operation = {0}; + file_operation.wFunc = FO_DELETE; + file_operation.pFrom = double_terminated_path; + file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION; + if (!recursive) + file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY; + int err = SHFileOperation(&file_operation); + // Some versions of Windows return ERROR_FILE_NOT_FOUND when + // deleting an empty directory. + return (err == 0 || err == ERROR_FILE_NOT_FOUND); +} + +bool Move(const FilePath& from_path, const FilePath& to_path) { + // NOTE: I suspect we could support longer paths, but that would involve + // analyzing all our usage of files. + if (from_path.value().length() >= MAX_PATH || + to_path.value().length() >= MAX_PATH) { + return false; + } + if (MoveFileEx(from_path.value().c_str(), to_path.value().c_str(), + MOVEFILE_COPY_ALLOWED | MOVEFILE_REPLACE_EXISTING) != 0) + return true; + if (DirectoryExists(from_path)) { + // MoveFileEx fails if moving directory across volumes. We will simulate + // the move by using Copy and Delete. Ideally we could check whether + // from_path and to_path are indeed in different volumes. + return CopyAndDeleteDirectory(from_path, to_path); + } + return false; +} + +bool CopyFile(const FilePath& from_path, const FilePath& to_path) { + // NOTE: I suspect we could support longer paths, but that would involve + // analyzing all our usage of files. + if (from_path.value().length() >= MAX_PATH || + to_path.value().length() >= MAX_PATH) { + return false; + } + return (::CopyFile(from_path.value().c_str(), to_path.value().c_str(), + false) != 0); +} + +bool ShellCopy(const FilePath& from_path, const FilePath& to_path, + bool recursive) { + // NOTE: I suspect we could support longer paths, but that would involve + // analyzing all our usage of files. + if (from_path.value().length() >= MAX_PATH || + to_path.value().length() >= MAX_PATH) { + return false; + } + + // SHFILEOPSTRUCT wants the path to be terminated with two NULLs, + // so we have to use wcscpy because wcscpy_s writes non-NULLs + // into the rest of the buffer. + wchar_t double_terminated_path_from[MAX_PATH + 1] = {0}; + wchar_t double_terminated_path_to[MAX_PATH + 1] = {0}; +#pragma warning(suppress:4996) // don't complain about wcscpy deprecation + wcscpy(double_terminated_path_from, from_path.value().c_str()); +#pragma warning(suppress:4996) // don't complain about wcscpy deprecation + wcscpy(double_terminated_path_to, to_path.value().c_str()); + + SHFILEOPSTRUCT file_operation = {0}; + file_operation.wFunc = FO_COPY; + file_operation.pFrom = double_terminated_path_from; + file_operation.pTo = double_terminated_path_to; + file_operation.fFlags = FOF_NOERRORUI | FOF_SILENT | FOF_NOCONFIRMATION | + FOF_NOCONFIRMMKDIR; + if (!recursive) + file_operation.fFlags |= FOF_NORECURSION | FOF_FILESONLY; + + return (SHFileOperation(&file_operation) == 0); +} + +bool CopyDirectory(const FilePath& from_path, const FilePath& to_path, + bool recursive) { + if (recursive) + return ShellCopy(from_path, to_path, true); + + // Instead of creating a new directory, we copy the old one to include the + // security information of the folder as part of the copy. + if (!PathExists(to_path)) { + // Except that Vista fails to do that, and instead do a recursive copy if + // the target directory doesn't exist. + if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) + CreateDirectory(to_path); + else + ShellCopy(from_path, to_path, false); + } + + FilePath directory = from_path.Append(L"*.*"); + return ShellCopy(directory, to_path, false); +} + +bool CopyAndDeleteDirectory(const FilePath& from_path, + const FilePath& to_path) { + if (CopyDirectory(from_path, to_path, true)) { + if (Delete(from_path, true)) { + return true; + } + // Like Move, this function is not transactional, so we just + // leave the copied bits behind if deleting from_path fails. + // If to_path exists previously then we have already overwritten + // it by now, we don't get better off by deleting the new bits. + } + return false; +} + + +bool PathExists(const FilePath& path) { + return (GetFileAttributes(path.value().c_str()) != INVALID_FILE_ATTRIBUTES); +} + +bool PathIsWritable(const FilePath& path) { + HANDLE dir = + CreateFile(path.value().c_str(), FILE_ADD_FILE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + + if (dir == INVALID_HANDLE_VALUE) + return false; + + CloseHandle(dir); + return true; +} + +bool DirectoryExists(const FilePath& path) { + DWORD fileattr = GetFileAttributes(path.value().c_str()); + if (fileattr != INVALID_FILE_ATTRIBUTES) + return (fileattr & FILE_ATTRIBUTE_DIRECTORY) != 0; + return false; +} + +bool GetFileCreationLocalTimeFromHandle(HANDLE file_handle, + LPSYSTEMTIME creation_time) { + if (!file_handle) + return false; + + FILETIME utc_filetime; + if (!GetFileTime(file_handle, &utc_filetime, NULL, NULL)) + return false; + + FILETIME local_filetime; + if (!FileTimeToLocalFileTime(&utc_filetime, &local_filetime)) + return false; + + return !!FileTimeToSystemTime(&local_filetime, creation_time); +} + +bool GetFileCreationLocalTime(const std::wstring& filename, + LPSYSTEMTIME creation_time) { + ScopedHandle file_handle( + CreateFile(filename.c_str(), GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)); + return GetFileCreationLocalTimeFromHandle(file_handle.Get(), creation_time); +} + +bool ResolveShortcut(std::wstring* path) { + FilePath file_path(*path); + bool result = ResolveShortcut(&file_path); + *path = file_path.value(); + return result; +} + +bool ResolveShortcut(FilePath* path) { + HRESULT result; + IShellLink *shell = NULL; + bool is_resolved = false; + + // Get pointer to the IShellLink interface + result = CoCreateInstance(CLSID_ShellLink, NULL, + CLSCTX_INPROC_SERVER, IID_IShellLink, + reinterpret_cast(&shell)); + if (SUCCEEDED(result)) { + IPersistFile *persist = NULL; + // Query IShellLink for the IPersistFile interface + result = shell->QueryInterface(IID_IPersistFile, + reinterpret_cast(&persist)); + if (SUCCEEDED(result)) { + WCHAR temp_path[MAX_PATH]; + // Load the shell link + result = persist->Load(path->value().c_str(), STGM_READ); + if (SUCCEEDED(result)) { + // Try to find the target of a shortcut + result = shell->Resolve(0, SLR_NO_UI); + if (SUCCEEDED(result)) { + result = shell->GetPath(temp_path, MAX_PATH, + NULL, SLGP_UNCPRIORITY); + *path = FilePath(temp_path); + is_resolved = true; + } + } + } + if (persist) + persist->Release(); + } + if (shell) + shell->Release(); + + return is_resolved; +} + +bool CreateShortcutLink(const wchar_t *source, const wchar_t *destination, + const wchar_t *working_dir, const wchar_t *arguments, + const wchar_t *description, const wchar_t *icon, + int icon_index) { + IShellLink *i_shell_link = NULL; + IPersistFile *i_persist_file = NULL; + + // Get pointer to the IShellLink interface + HRESULT result = CoCreateInstance(CLSID_ShellLink, NULL, + CLSCTX_INPROC_SERVER, IID_IShellLink, + reinterpret_cast(&i_shell_link)); + if (FAILED(result)) + return false; + + // Query IShellLink for the IPersistFile interface + result = i_shell_link->QueryInterface(IID_IPersistFile, + reinterpret_cast(&i_persist_file)); + if (FAILED(result)) { + i_shell_link->Release(); + return false; + } + + if (FAILED(i_shell_link->SetPath(source))) { + i_persist_file->Release(); + i_shell_link->Release(); + return false; + } + + if (working_dir && FAILED(i_shell_link->SetWorkingDirectory(working_dir))) { + i_persist_file->Release(); + i_shell_link->Release(); + return false; + } + + if (arguments && FAILED(i_shell_link->SetArguments(arguments))) { + i_persist_file->Release(); + i_shell_link->Release(); + return false; + } + + if (description && FAILED(i_shell_link->SetDescription(description))) { + i_persist_file->Release(); + i_shell_link->Release(); + return false; + } + + if (icon && FAILED(i_shell_link->SetIconLocation(icon, icon_index))) { + i_persist_file->Release(); + i_shell_link->Release(); + return false; + } + + result = i_persist_file->Save(destination, TRUE); + i_persist_file->Release(); + i_shell_link->Release(); + return SUCCEEDED(result); +} + + +bool UpdateShortcutLink(const wchar_t *source, const wchar_t *destination, + const wchar_t *working_dir, const wchar_t *arguments, + const wchar_t *description, const wchar_t *icon, + int icon_index) { + // Get pointer to the IPersistFile interface and load existing link + IShellLink *i_shell_link = NULL; + if (FAILED(CoCreateInstance(CLSID_ShellLink, NULL, + CLSCTX_INPROC_SERVER, IID_IShellLink, + reinterpret_cast(&i_shell_link)))) + return false; + + IPersistFile *i_persist_file = NULL; + if (FAILED(i_shell_link->QueryInterface( + IID_IPersistFile, reinterpret_cast(&i_persist_file)))) { + i_shell_link->Release(); + return false; + } + + if (FAILED(i_persist_file->Load(destination, 0))) { + i_persist_file->Release(); + i_shell_link->Release(); + return false; + } + + if (source && FAILED(i_shell_link->SetPath(source))) { + i_persist_file->Release(); + i_shell_link->Release(); + return false; + } + + if (working_dir && FAILED(i_shell_link->SetWorkingDirectory(working_dir))) { + i_persist_file->Release(); + i_shell_link->Release(); + return false; + } + + if (arguments && FAILED(i_shell_link->SetArguments(arguments))) { + i_persist_file->Release(); + i_shell_link->Release(); + return false; + } + + if (description && FAILED(i_shell_link->SetDescription(description))) { + i_persist_file->Release(); + i_shell_link->Release(); + return false; + } + + if (icon && FAILED(i_shell_link->SetIconLocation(icon, icon_index))) { + i_persist_file->Release(); + i_shell_link->Release(); + return false; + } + + HRESULT result = i_persist_file->Save(destination, TRUE); + i_persist_file->Release(); + i_shell_link->Release(); + return SUCCEEDED(result); +} + +bool IsDirectoryEmpty(const std::wstring& dir_path) { + FileEnumerator files(FilePath(dir_path), + false, FileEnumerator::FILES_AND_DIRECTORIES); + if (files.Next().value().empty()) + return true; + return false; +} + +bool GetTempDir(FilePath* path) { + wchar_t temp_path[MAX_PATH + 1]; + DWORD path_len = ::GetTempPath(MAX_PATH, temp_path); + if (path_len >= MAX_PATH || path_len <= 0) + return false; + // TODO(evanm): the old behavior of this function was to always strip the + // trailing slash. We duplicate this here, but it shouldn't be necessary + // when everyone is using the appropriate FilePath APIs. + std::wstring path_str(temp_path); + TrimTrailingSeparator(&path_str); + *path = FilePath(path_str); + return true; +} + +bool GetShmemTempDir(FilePath* path) { + return GetTempDir(path); +} + +bool CreateTemporaryFileName(FilePath* path) { + std::wstring temp_path, temp_file; + + if (!GetTempDir(&temp_path)) + return false; + + if (CreateTemporaryFileNameInDir(temp_path, &temp_file)) { + *path = FilePath(temp_file); + return true; + } + + return false; +} + +FILE* CreateAndOpenTemporaryShmemFile(FilePath* path) { + return CreateAndOpenTemporaryFile(path); +} + +// On POSIX we have semantics to create and open a temporary file +// atomically. +// TODO(jrg): is there equivalent call to use on Windows instead of +// going 2-step? +FILE* CreateAndOpenTemporaryFileInDir(const FilePath& dir, FilePath* path) { + std::wstring wstring_path; + if (!CreateTemporaryFileNameInDir(dir.value(), &wstring_path)) { + return NULL; + } + *path = FilePath(wstring_path); + // Open file in binary mode, to avoid problems with fwrite. On Windows + // it replaces \n's with \r\n's, which may surprise you. + // Reference: http://msdn.microsoft.com/en-us/library/h9t88zwz(VS.71).aspx + return OpenFile(*path, "wb+"); +} + +bool CreateTemporaryFileNameInDir(const std::wstring& dir, + std::wstring* temp_file) { + wchar_t temp_name[MAX_PATH + 1]; + + if (!GetTempFileName(dir.c_str(), L"", 0, temp_name)) + return false; // fail! + + DWORD path_len = GetLongPathName(temp_name, temp_name, MAX_PATH); + if (path_len > MAX_PATH + 1 || path_len == 0) + return false; // fail! + + temp_file->assign(temp_name, path_len); + return true; +} + +bool CreateNewTempDirectory(const FilePath::StringType& prefix, + FilePath* new_temp_path) { + FilePath system_temp_dir; + if (!GetTempDir(&system_temp_dir)) + return false; + + FilePath path_to_create; + srand(static_cast(time(NULL))); + + int count = 0; + while (count < 50) { + // Try create a new temporary directory with random generated name. If + // the one exists, keep trying another path name until we reach some limit. + path_to_create = system_temp_dir; + std::wstring new_dir_name; + new_dir_name.assign(prefix); + new_dir_name.append(IntToWString(rand() % kint16max)); + path_to_create = path_to_create.Append(new_dir_name); + + if (::CreateDirectory(path_to_create.value().c_str(), NULL)) + break; + count++; + } + + if (count == 50) { + return false; + } + + *new_temp_path = path_to_create; + return true; +} + +bool CreateDirectory(const FilePath& full_path) { + if (DirectoryExists(full_path)) + return true; + int err = SHCreateDirectoryEx(NULL, full_path.value().c_str(), NULL); + return err == ERROR_SUCCESS; +} + +bool GetFileInfo(const FilePath& file_path, FileInfo* results) { + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesEx(file_path.ToWStringHack().c_str(), + GetFileExInfoStandard, &attr)) { + return false; + } + + ULARGE_INTEGER size; + size.HighPart = attr.nFileSizeHigh; + size.LowPart = attr.nFileSizeLow; + results->size = size.QuadPart; + + results->is_directory = + (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + return true; +} + +FILE* OpenFile(const FilePath& filename, const char* mode) { + std::wstring w_mode = ASCIIToWide(std::string(mode)); + FILE* file; + if (_wfopen_s(&file, filename.value().c_str(), w_mode.c_str()) != 0) { + return NULL; + } + return file; +} + +FILE* OpenFile(const std::string& filename, const char* mode) { + FILE* file; + if (fopen_s(&file, filename.c_str(), mode) != 0) { + return NULL; + } + return file; +} + +int ReadFile(const FilePath& filename, char* data, int size) { + ScopedHandle file(CreateFile(filename.value().c_str(), + GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + FILE_FLAG_SEQUENTIAL_SCAN, + NULL)); + if (file == INVALID_HANDLE_VALUE) + return -1; + + int ret_value; + DWORD read; + if (::ReadFile(file, data, size, &read, NULL) && read == size) { + ret_value = static_cast(read); + } else { + ret_value = -1; + } + + return ret_value; +} + +int WriteFile(const FilePath& filename, const char* data, int size) { + ScopedHandle file(CreateFile(filename.value().c_str(), + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + 0, + NULL)); + if (file == INVALID_HANDLE_VALUE) { + LOG(WARNING) << "CreateFile failed for path " << filename.value() << + " error code=" << GetLastError() << + " error text=" << win_util::FormatLastWin32Error(); + return -1; + } + + DWORD written; + BOOL result = ::WriteFile(file, data, size, &written, NULL); + if (result && written == size) + return static_cast(written); + + if (!result) { + // WriteFile failed. + LOG(WARNING) << "writing file " << filename.value() << + " failed, error code=" << GetLastError() << + " description=" << win_util::FormatLastWin32Error(); + } else { + // Didn't write all the bytes. + LOG(WARNING) << "wrote" << written << " bytes to " << + filename.value() << " expected " << size; + } + return -1; +} + +bool RenameFileAndResetSecurityDescriptor(const FilePath& source_file_path, + const FilePath& target_file_path) { + // The parameters to SHFileOperation must be terminated with 2 NULL chars. + std::wstring source = source_file_path.value(); + std::wstring target = target_file_path.value(); + + source.append(1, L'\0'); + target.append(1, L'\0'); + + SHFILEOPSTRUCT move_info = {0}; + move_info.wFunc = FO_MOVE; + move_info.pFrom = source.c_str(); + move_info.pTo = target.c_str(); + move_info.fFlags = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | + FOF_NOCONFIRMMKDIR | FOF_NOCOPYSECURITYATTRIBS; + + if (0 != SHFileOperation(&move_info)) + return false; + + return true; +} + +// Gets the current working directory for the process. +bool GetCurrentDirectory(FilePath* dir) { + wchar_t system_buffer[MAX_PATH]; + system_buffer[0] = 0; + DWORD len = ::GetCurrentDirectory(MAX_PATH, system_buffer); + if (len == 0 || len > MAX_PATH) + return false; + // TODO(evanm): the old behavior of this function was to always strip the + // trailing slash. We duplicate this here, but it shouldn't be necessary + // when everyone is using the appropriate FilePath APIs. + std::wstring dir_str(system_buffer); + file_util::TrimTrailingSeparator(&dir_str); + *dir = FilePath(dir_str); + return true; +} + +// Sets the current working directory for the process. +bool SetCurrentDirectory(const FilePath& directory) { + BOOL ret = ::SetCurrentDirectory(directory.value().c_str()); + return ret != 0; +} + +/////////////////////////////////////////////// +// FileEnumerator + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + FileEnumerator::FILE_TYPE file_type) + : recursive_(recursive), + file_type_(file_type), + is_in_find_op_(false), + find_handle_(INVALID_HANDLE_VALUE) { + pending_paths_.push(root_path); +} + +FileEnumerator::FileEnumerator(const FilePath& root_path, + bool recursive, + FileEnumerator::FILE_TYPE file_type, + const FilePath::StringType& pattern) + : recursive_(recursive), + file_type_(file_type), + is_in_find_op_(false), + pattern_(pattern), + find_handle_(INVALID_HANDLE_VALUE) { + pending_paths_.push(root_path); +} + +FileEnumerator::~FileEnumerator() { + if (find_handle_ != INVALID_HANDLE_VALUE) + FindClose(find_handle_); +} + +void FileEnumerator::GetFindInfo(FindInfo* info) { + DCHECK(info); + + if (!is_in_find_op_) + return; + + memcpy(info, &find_data_, sizeof(*info)); +} + +FilePath FileEnumerator::Next() { + if (!is_in_find_op_) { + if (pending_paths_.empty()) + return FilePath(); + + // The last find FindFirstFile operation is done, prepare a new one. + root_path_ = pending_paths_.top(); + pending_paths_.pop(); + + // Start a new find operation. + FilePath src = root_path_; + + if (pattern_.value().empty()) + src = src.Append(L"*"); // No pattern = match everything. + else + src = src.Append(pattern_); + + find_handle_ = FindFirstFile(src.value().c_str(), &find_data_); + is_in_find_op_ = true; + + } else { + // Search for the next file/directory. + if (!FindNextFile(find_handle_, &find_data_)) { + FindClose(find_handle_); + find_handle_ = INVALID_HANDLE_VALUE; + } + } + + if (INVALID_HANDLE_VALUE == find_handle_) { + is_in_find_op_ = false; + + // This is reached when we have finished a directory and are advancing to + // the next one in the queue. We applied the pattern (if any) to the files + // in the root search directory, but for those directories which were + // matched, we want to enumerate all files inside them. This will happen + // when the handle is empty. + pattern_ = FilePath(); + + return Next(); + } + + FilePath cur_file(find_data_.cFileName); + // Skip over . and .. + if (L"." == cur_file.value() || L".." == cur_file.value()) + return Next(); + + // Construct the absolute filename. + cur_file = root_path_.Append(cur_file); + + if (find_data_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (recursive_) { + // If |cur_file| is a directory, and we are doing recursive searching, add + // it to pending_paths_ so we scan it after we finish scanning this + // directory. + pending_paths_.push(cur_file); + } + return (file_type_ & FileEnumerator::DIRECTORIES) ? cur_file : Next(); + } + return (file_type_ & FileEnumerator::FILES) ? cur_file : Next(); +} + +/////////////////////////////////////////////// +// MemoryMappedFile + +MemoryMappedFile::MemoryMappedFile() + : file_(INVALID_HANDLE_VALUE), + file_mapping_(INVALID_HANDLE_VALUE), + data_(NULL), + length_(INVALID_FILE_SIZE) { +} + +bool MemoryMappedFile::MapFileToMemory(const FilePath& file_name) { + file_ = ::CreateFile(file_name.value().c_str(), GENERIC_READ, + FILE_SHARE_READ, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, NULL); + if (file_ == INVALID_HANDLE_VALUE) + return false; + + length_ = ::GetFileSize(file_, NULL); + if (length_ == INVALID_FILE_SIZE) + return false; + + file_mapping_ = ::CreateFileMapping(file_, NULL, PAGE_READONLY, + 0, length_, NULL); + if (file_mapping_ == INVALID_HANDLE_VALUE) + return false; + + data_ = static_cast( + ::MapViewOfFile(file_mapping_, FILE_MAP_READ, 0, 0, length_)); + return data_ != NULL; +} + +void MemoryMappedFile::CloseHandles() { + if (data_) + ::UnmapViewOfFile(data_); + if (file_mapping_ != INVALID_HANDLE_VALUE) + ::CloseHandle(file_mapping_); + if (file_ != INVALID_HANDLE_VALUE) + ::CloseHandle(file_); + + data_ = NULL; + file_mapping_ = file_ = INVALID_HANDLE_VALUE; + length_ = INVALID_FILE_SIZE; +} + +// Deprecated functions ---------------------------------------------------- + +void InsertBeforeExtension(std::wstring* path_str, + const std::wstring& suffix) { + FilePath path(*path_str); + InsertBeforeExtension(&path, suffix); + path_str->assign(path.value()); +} +void PathComponents(const std::wstring& path, + std::vector* components) { + PathComponents(FilePath(path), components); +} +void ReplaceExtension(std::wstring* file_name, const std::wstring& extension) { + FilePath path(*file_name); + ReplaceExtension(&path, extension); + file_name->assign(path.value()); +} +} // namespace file_util diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_version_info.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_version_info.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_version_info.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_version_info.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,185 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/file_version_info.h" + +#include "base/logging.h" +#include "base/path_service.h" + +// This has to be last. +#include + +FileVersionInfo::FileVersionInfo(void* data, int language, int code_page) + : language_(language), code_page_(code_page) { + data_.reset((char*) data); + fixed_file_info_ = NULL; + UINT size; + ::VerQueryValue(data_.get(), L"\\", (LPVOID*)&fixed_file_info_, &size); +} + +FileVersionInfo::~FileVersionInfo() { + DCHECK(data_.get()); +} + +typedef struct { + WORD language; + WORD code_page; +} LanguageAndCodePage; + +// static +FileVersionInfo* FileVersionInfo::CreateFileVersionInfoForCurrentModule() { + std::wstring app_path; + if (!PathService::Get(base::FILE_MODULE, &app_path)) + return NULL; + + return CreateFileVersionInfo(app_path); +} + +// static +FileVersionInfo* FileVersionInfo::CreateFileVersionInfo( + const FilePath& file_path) { + DWORD dummy; + const wchar_t* path = file_path.value().c_str(); + DWORD length = ::GetFileVersionInfoSize(path, &dummy); + if (length == 0) + return NULL; + + void* data = calloc(length, 1); + if (!data) + return NULL; + + if (!::GetFileVersionInfo(path, dummy, length, data)) { + free(data); + return NULL; + } + + LanguageAndCodePage* translate = NULL; + uint32 page_count; + BOOL query_result = VerQueryValue(data, L"\\VarFileInfo\\Translation", + (void**) &translate, &page_count); + + if (query_result && translate) { + return new FileVersionInfo(data, translate->language, + translate->code_page); + + } else { + free(data); + return NULL; + } +} + +FileVersionInfo* FileVersionInfo::CreateFileVersionInfo( + const std::wstring& file_path) { + FilePath file_path_fp = FilePath::FromWStringHack(file_path); + return CreateFileVersionInfo(file_path_fp); +} + +std::wstring FileVersionInfo::company_name() { + return GetStringValue(L"CompanyName"); +} + +std::wstring FileVersionInfo::company_short_name() { + return GetStringValue(L"CompanyShortName"); +} + +std::wstring FileVersionInfo::internal_name() { + return GetStringValue(L"InternalName"); +} + +std::wstring FileVersionInfo::product_name() { + return GetStringValue(L"ProductName"); +} + +std::wstring FileVersionInfo::product_short_name() { + return GetStringValue(L"ProductShortName"); +} + +std::wstring FileVersionInfo::comments() { + return GetStringValue(L"Comments"); +} + +std::wstring FileVersionInfo::legal_copyright() { + return GetStringValue(L"LegalCopyright"); +} + +std::wstring FileVersionInfo::product_version() { + return GetStringValue(L"ProductVersion"); +} + +std::wstring FileVersionInfo::file_description() { + return GetStringValue(L"FileDescription"); +} + +std::wstring FileVersionInfo::legal_trademarks() { + return GetStringValue(L"LegalTrademarks"); +} + +std::wstring FileVersionInfo::private_build() { + return GetStringValue(L"PrivateBuild"); +} + +std::wstring FileVersionInfo::file_version() { + return GetStringValue(L"FileVersion"); +} + +std::wstring FileVersionInfo::original_filename() { + return GetStringValue(L"OriginalFilename"); +} + +std::wstring FileVersionInfo::special_build() { + return GetStringValue(L"SpecialBuild"); +} + +std::wstring FileVersionInfo::last_change() { + return GetStringValue(L"LastChange"); +} + +bool FileVersionInfo::is_official_build() { + return (GetStringValue(L"Official Build").compare(L"1") == 0); +} + +bool FileVersionInfo::GetValue(const wchar_t* name, std::wstring* value_str) { + + WORD lang_codepage[8]; + int i = 0; + // Use the language and codepage from the DLL. + lang_codepage[i++] = language_; + lang_codepage[i++] = code_page_; + // Use the default language and codepage from the DLL. + lang_codepage[i++] = ::GetUserDefaultLangID(); + lang_codepage[i++] = code_page_; + // Use the language from the DLL and Latin codepage (most common). + lang_codepage[i++] = language_; + lang_codepage[i++] = 1252; + // Use the default language and Latin codepage (most common). + lang_codepage[i++] = ::GetUserDefaultLangID(); + lang_codepage[i++] = 1252; + + i = 0; + while (i < arraysize(lang_codepage)) { + wchar_t sub_block[MAX_PATH]; + WORD language = lang_codepage[i++]; + WORD code_page = lang_codepage[i++]; + _snwprintf_s(sub_block, MAX_PATH, MAX_PATH, + L"\\StringFileInfo\\%04x%04x\\%ls", language, code_page, name); + LPVOID value = NULL; + uint32 size; + BOOL r = ::VerQueryValue(data_.get(), sub_block, &value, &size); + if (r && value) { + value_str->assign(static_cast(value)); + return true; + } + } + return false; +} + +std::wstring FileVersionInfo::GetStringValue(const wchar_t* name) { + std::wstring str; + if (GetValue(name, &str)) + return str; + else + return L""; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_version_info.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_version_info.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_version_info.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_version_info.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,96 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FILE_VERSION_INFO_H__ +#define BASE_FILE_VERSION_INFO_H__ + +#include + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/scoped_ptr.h" + +#if defined(OS_WIN) +struct tagVS_FIXEDFILEINFO; +typedef tagVS_FIXEDFILEINFO VS_FIXEDFILEINFO; +#elif defined(OS_MACOSX) +#ifdef __OBJC__ +@class NSBundle; +#else +class NSBundle; +#endif +#endif + +// Provides a way to access the version information for a file. +// This is the information you access when you select a file in the Windows +// explorer, right-click select Properties, then click the Version tab. + +class FileVersionInfo { + public: + // Creates a FileVersionInfo for the specified path. Returns NULL if something + // goes wrong (typically the file does not exit or cannot be opened). The + // returned object should be deleted when you are done with it. + static FileVersionInfo* CreateFileVersionInfo(const FilePath& file_path); + // This version, taking a wstring, is deprecated and only kept around + // until we can fix all callers. + static FileVersionInfo* CreateFileVersionInfo(const std::wstring& file_path); + + // Creates a FileVersionInfo for the current module. Returns NULL in case + // of error. The returned object should be deleted when you are done with it. + static FileVersionInfo* CreateFileVersionInfoForCurrentModule(); + + ~FileVersionInfo(); + + // Accessors to the different version properties. + // Returns an empty string if the property is not found. + std::wstring company_name(); + std::wstring company_short_name(); + std::wstring product_name(); + std::wstring product_short_name(); + std::wstring internal_name(); + std::wstring product_version(); + std::wstring private_build(); + std::wstring special_build(); + std::wstring comments(); + std::wstring original_filename(); + std::wstring file_description(); + std::wstring file_version(); + std::wstring legal_copyright(); + std::wstring legal_trademarks(); + std::wstring last_change(); + bool is_official_build(); + + // Lets you access other properties not covered above. + bool GetValue(const wchar_t* name, std::wstring* value); + + // Similar to GetValue but returns a wstring (empty string if the property + // does not exist). + std::wstring GetStringValue(const wchar_t* name); + +#ifdef OS_WIN + // Get the fixed file info if it exists. Otherwise NULL + VS_FIXEDFILEINFO* fixed_file_info() { return fixed_file_info_; } +#endif + + private: +#if defined(OS_WIN) + FileVersionInfo(void* data, int language, int code_page); + + scoped_ptr_malloc data_; + int language_; + int code_page_; + // This is a pointer into the data_ if it exists. Otherwise NULL. + VS_FIXEDFILEINFO* fixed_file_info_; +#elif defined(OS_MACOSX) + explicit FileVersionInfo(NSBundle *bundle); + + NSBundle *bundle_; +#elif defined(OS_LINUX) + FileVersionInfo(); +#endif + + DISALLOW_EVIL_CONSTRUCTORS(FileVersionInfo); +}; + +#endif // BASE_FILE_VERSION_INFO_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_version_info_linux.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_version_info_linux.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_version_info_linux.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_version_info_linux.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,86 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_version_info.h" +#include "base/file_version_info_linux.h" + +#include + +// TODO(mmoss) This only provides version info for the current binary, but it's +// also called for arbitrary files (e.g. plugins). +// See http://code.google.com/p/chromium/issues/detail?id=8132 for a discussion +// on what we should do with this module. + +FileVersionInfo::FileVersionInfo() {} + +FileVersionInfo::~FileVersionInfo() {} + +// static +FileVersionInfo* FileVersionInfo::CreateFileVersionInfoForCurrentModule() { + return new FileVersionInfo(); +} + +std::wstring FileVersionInfo::company_name() { + return COMPANY_NAME; +} + +std::wstring FileVersionInfo::company_short_name() { + return COMPANY_SHORT_NAME; +} + +std::wstring FileVersionInfo::product_name() { + return PRODUCT_NAME; +} + +std::wstring FileVersionInfo::product_short_name() { + return PRODUCT_SHORT_NAME; +} + +std::wstring FileVersionInfo::internal_name() { + return INTERNAL_NAME; +} + +std::wstring FileVersionInfo::product_version() { + return PRODUCT_VERSION; +} + +std::wstring FileVersionInfo::private_build() { + return PRIVATE_BUILD; +} + +std::wstring FileVersionInfo::special_build() { + return SPECIAL_BUILD; +} + +std::wstring FileVersionInfo::comments() { + return COMMENTS; +} + +std::wstring FileVersionInfo::original_filename() { + return ORIGINAL_FILENAME; +} + +std::wstring FileVersionInfo::file_description() { + return FILE_DESCRIPTION; +} + +std::wstring FileVersionInfo::file_version() { + return FILE_VERSION; +} + +std::wstring FileVersionInfo::legal_copyright() { + return LEGAL_COPYRIGHT; +} + +std::wstring FileVersionInfo::legal_trademarks() { + return LEGAL_TRADEMARKS; +} + +std::wstring FileVersionInfo::last_change() { + return LAST_CHANGE; +} + +bool FileVersionInfo::is_official_build() { + return OFFICIAL_BUILD; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_version_info_linux.h.version firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_version_info_linux.h.version --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_version_info_linux.h.version 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_version_info_linux.h.version 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,26 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FILE_VERSION_INFO_LINUX_H_ +#define BASE_FILE_VERSION_INFO_LINUX_H_ + +#define COMPANY_NAME L"@COMPANY_FULLNAME@" +#define FILE_DESCRIPTION L"@PRODUCT_FULLNAME@" +#define FILE_VERSION L"@MAJOR@.@MINOR@.@BUILD@.@PATCH@" +#define LEGAL_COPYRIGHT L"@COPYRIGHT@" +#define PRODUCT_NAME L"@PRODUCT_FULLNAME@" +#define PRODUCT_VERSION L"@MAJOR@.@MINOR@.@BUILD@.@PATCH@" +#define COMPANY_SHORT_NAME L"@COMPANY_SHORTNAME@" +#define PRODUCT_SHORT_NAME L"@PRODUCT_SHORTNAME@" +#define LAST_CHANGE L"@LASTCHANGE@" +#define OFFICIAL_BUILD @OFFICIAL_BUILD@ +// TODO(mmoss) Do these have values for Linux? +#define INTERNAL_NAME L"" +#define ORIGINAL_FILENAME L"" +#define PRIVATE_BUILD L"" +#define SPECIAL_BUILD L"" +#define COMMENTS L"" +#define LEGAL_TRADEMARKS L"" + +#endif // BASE_FILE_VERSION_INFO_LINUX_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_version_info_mac.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_version_info_mac.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_version_info_mac.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_version_info_mac.mm 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,131 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_version_info.h" + +#import + +#include "base/logging.h" +#include "base/string_util.h" + +FileVersionInfo::FileVersionInfo(NSBundle *bundle) : bundle_(bundle) { + [bundle_ retain]; +} + +FileVersionInfo::~FileVersionInfo() { + [bundle_ release]; +} + +// static +FileVersionInfo* FileVersionInfo::CreateFileVersionInfoForCurrentModule() { + // TODO(erikkay): this should really use bundleForClass, but we don't have + // a class to hang onto yet. + NSBundle* bundle = [NSBundle mainBundle]; + return new FileVersionInfo(bundle); +} + +// static +FileVersionInfo* FileVersionInfo::CreateFileVersionInfo( + const std::wstring& file_path) { + NSString* path = [NSString stringWithCString: + reinterpret_cast(file_path.c_str()) + encoding:NSUTF32StringEncoding]; + return new FileVersionInfo([NSBundle bundleWithPath:path]); +} + +// static +FileVersionInfo* FileVersionInfo::CreateFileVersionInfo( + const FilePath& file_path) { + NSString* path = [NSString stringWithUTF8String:file_path.value().c_str()]; + return new FileVersionInfo([NSBundle bundleWithPath:path]); +} + +std::wstring FileVersionInfo::company_name() { + return L""; +} + +std::wstring FileVersionInfo::company_short_name() { + return L""; +} + +std::wstring FileVersionInfo::internal_name() { + return L""; +} + +std::wstring FileVersionInfo::product_name() { + return GetStringValue(L"CFBundleName"); +} + +std::wstring FileVersionInfo::product_short_name() { + return GetStringValue(L"CFBundleName"); +} + +std::wstring FileVersionInfo::comments() { + return L""; +} + +std::wstring FileVersionInfo::legal_copyright() { + return GetStringValue(L"CFBundleGetInfoString"); +} + +std::wstring FileVersionInfo::product_version() { + return GetStringValue(L"CFBundleShortVersionString"); +} + +std::wstring FileVersionInfo::file_description() { + return L""; +} + +std::wstring FileVersionInfo::legal_trademarks() { + return L""; +} + +std::wstring FileVersionInfo::private_build() { + return L""; +} + +std::wstring FileVersionInfo::file_version() { + // CFBundleVersion has limitations that may not be honored by a + // proper Chromium version number, so try KSVersion first. + std::wstring version = GetStringValue(L"KSVersion"); + if (version == L"") + version = GetStringValue(L"CFBundleVersion"); + return version; +} + +std::wstring FileVersionInfo::original_filename() { + return GetStringValue(L"CFBundleName"); +} + +std::wstring FileVersionInfo::special_build() { + return L""; +} + +std::wstring FileVersionInfo::last_change() { + return L""; +} + +bool FileVersionInfo::is_official_build() { + return false; +} + +bool FileVersionInfo::GetValue(const wchar_t* name, std::wstring* value_str) { + if (bundle_) { + NSString* value = [bundle_ objectForInfoDictionaryKey: + [NSString stringWithUTF8String:WideToUTF8(name).c_str()]]; + if (value) { + *value_str = reinterpret_cast( + [value cStringUsingEncoding:NSUTF32StringEncoding]); + return true; + } + } + return false; +} + +std::wstring FileVersionInfo::GetStringValue(const wchar_t* name) { + std::wstring str; + if (GetValue(name, &str)) + return str; + return L""; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_version_info_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_version_info_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/file_version_info_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/file_version_info_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,134 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/scoped_ptr.h" +#include "base/file_version_info.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class FileVersionInfoTest : public testing::Test { +}; + +std::wstring GetTestDataPath() { + std::wstring path; + PathService::Get(base::DIR_SOURCE_ROOT, &path); + file_util::AppendToPath(&path, L"base"); + file_util::AppendToPath(&path, L"data"); + file_util::AppendToPath(&path, L"file_version_info_unittest"); + return path; +} + +} + +#ifdef OS_WIN +TEST(FileVersionInfoTest, HardCodedProperties) { + const wchar_t* kDLLNames[] = { + L"FileVersionInfoTest1.dll" + }; + + const wchar_t* kExpectedValues[1][15] = { + // FileVersionInfoTest.dll + L"Goooooogle", // company_name + L"Google", // company_short_name + L"This is the product name", // product_name + L"This is the product short name", // product_short_name + L"The Internal Name", // internal_name + L"4.3.2.1", // product_version + L"Private build property", // private_build + L"Special build property", // special_build + L"This is a particularly interesting comment", // comments + L"This is the original filename", // original_filename + L"This is my file description", // file_description + L"1.2.3.4", // file_version + L"This is the legal copyright", // legal_copyright + L"This is the legal trademarks", // legal_trademarks + L"This is the last change", // last_change + + }; + + for (int i = 0; i < arraysize(kDLLNames); ++i) { + std::wstring dll_path = GetTestDataPath(); + file_util::AppendToPath(&dll_path, kDLLNames[i]); + + scoped_ptr version_info( + FileVersionInfo::CreateFileVersionInfo(dll_path)); + + int j = 0; + EXPECT_EQ(kExpectedValues[i][j++], version_info->company_name()); + EXPECT_EQ(kExpectedValues[i][j++], version_info->company_short_name()); + EXPECT_EQ(kExpectedValues[i][j++], version_info->product_name()); + EXPECT_EQ(kExpectedValues[i][j++], version_info->product_short_name()); + EXPECT_EQ(kExpectedValues[i][j++], version_info->internal_name()); + EXPECT_EQ(kExpectedValues[i][j++], version_info->product_version()); + EXPECT_EQ(kExpectedValues[i][j++], version_info->private_build()); + EXPECT_EQ(kExpectedValues[i][j++], version_info->special_build()); + EXPECT_EQ(kExpectedValues[i][j++], version_info->comments()); + EXPECT_EQ(kExpectedValues[i][j++], version_info->original_filename()); + EXPECT_EQ(kExpectedValues[i][j++], version_info->file_description()); + EXPECT_EQ(kExpectedValues[i][j++], version_info->file_version()); + EXPECT_EQ(kExpectedValues[i][j++], version_info->legal_copyright()); + EXPECT_EQ(kExpectedValues[i][j++], version_info->legal_trademarks()); + EXPECT_EQ(kExpectedValues[i][j++], version_info->last_change()); + } +} +#endif + +#ifdef OS_WIN +TEST(FileVersionInfoTest, IsOfficialBuild) { + const wchar_t* kDLLNames[] = { + L"FileVersionInfoTest1.dll", + L"FileVersionInfoTest2.dll" + }; + + const bool kExpected[] = { + true, + false, + }; + + // Test consistency check. + ASSERT_EQ(arraysize(kDLLNames), arraysize(kExpected)); + + for (int i = 0; i < arraysize(kDLLNames); ++i) { + std::wstring dll_path = GetTestDataPath(); + file_util::AppendToPath(&dll_path, kDLLNames[i]); + + scoped_ptr version_info( + FileVersionInfo::CreateFileVersionInfo(dll_path)); + + EXPECT_EQ(kExpected[i], version_info->is_official_build()); + } +} +#endif + +TEST(FileVersionInfoTest, CustomProperties) { + std::wstring dll_path = GetTestDataPath(); + file_util::AppendToPath(&dll_path, L"FileVersionInfoTest1.dll"); + + scoped_ptr version_info( + FileVersionInfo::CreateFileVersionInfo(dll_path)); + + // Test few existing properties. + std::wstring str; +#ifdef OS_WIN + EXPECT_TRUE(version_info->GetValue(L"Custom prop 1", &str)); + EXPECT_EQ(L"Un", str); + EXPECT_EQ(L"Un", version_info->GetStringValue(L"Custom prop 1")); + + EXPECT_TRUE(version_info->GetValue(L"Custom prop 2", &str)); + EXPECT_EQ(L"Deux", str); + EXPECT_EQ(L"Deux", version_info->GetStringValue(L"Custom prop 2")); + + EXPECT_TRUE(version_info->GetValue(L"Custom prop 3", &str)); + EXPECT_EQ(L"1600 Amphitheatre Parkway Mountain View, CA 94043", str); + EXPECT_EQ(L"1600 Amphitheatre Parkway Mountain View, CA 94043", + version_info->GetStringValue(L"Custom prop 3")); +#endif + + // Test an non-existing property. + EXPECT_FALSE(version_info->GetValue(L"Unknown property", &str)); + EXPECT_EQ(L"", version_info->GetStringValue(L"Unknown property")); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/fix_wp64.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/fix_wp64.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/fix_wp64.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/fix_wp64.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,75 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Various inline functions and macros to fix compilation of 32 bit target +// on MSVC with /Wp64 flag enabled. + +#ifndef BASE_FIX_WP64_H__ +#define BASE_FIX_WP64_H__ + +#include + +// Platform SDK fixes when building with /Wp64 for a 32 bits target. +#if !defined(_WIN64) && defined(_Wp64) + +#ifdef InterlockedExchangePointer +#undef InterlockedExchangePointer +// The problem is that the macro provided for InterlockedExchangePointer() is +// doing a (LONG) C-style cast that triggers invariably the warning C4312 when +// building on 32 bits. +inline void* InterlockedExchangePointer(void* volatile* target, void* value) { + return reinterpret_cast(static_cast(InterlockedExchange( + reinterpret_cast(target), + static_cast(reinterpret_cast(value))))); +} +#endif // #ifdef InterlockedExchangePointer + +#ifdef SetWindowLongPtrA +#undef SetWindowLongPtrA +// When build on 32 bits, SetWindowLongPtrX() is a macro that redirects to +// SetWindowLongX(). The problem is that this function takes a LONG argument +// instead of a LONG_PTR. +inline LONG_PTR SetWindowLongPtrA(HWND window, int index, LONG_PTR new_long) { + return ::SetWindowLongA(window, index, static_cast(new_long)); +} +#endif // #ifdef SetWindowLongPtrA + +#ifdef SetWindowLongPtrW +#undef SetWindowLongPtrW +inline LONG_PTR SetWindowLongPtrW(HWND window, int index, LONG_PTR new_long) { + return ::SetWindowLongW(window, index, static_cast(new_long)); +} +#endif // #ifdef SetWindowLongPtrW + +#ifdef GetWindowLongPtrA +#undef GetWindowLongPtrA +inline LONG_PTR GetWindowLongPtrA(HWND window, int index) { + return ::GetWindowLongA(window, index); +} +#endif // #ifdef GetWindowLongPtrA + +#ifdef GetWindowLongPtrW +#undef GetWindowLongPtrW +inline LONG_PTR GetWindowLongPtrW(HWND window, int index) { + return ::GetWindowLongW(window, index); +} +#endif // #ifdef GetWindowLongPtrW + +#ifdef SetClassLongPtrA +#undef SetClassLongPtrA +inline LONG_PTR SetClassLongPtrA(HWND window, int index, LONG_PTR new_long) { + return ::SetClassLongA(window, index, static_cast(new_long)); +} +#endif // #ifdef SetClassLongPtrA + +#ifdef SetClassLongPtrW +#undef SetClassLongPtrW +inline LONG_PTR SetClassLongPtrW(HWND window, int index, LONG_PTR new_long) { + return ::SetClassLongW(window, index, static_cast(new_long)); +} +#endif // #ifdef SetClassLongPtrW + +#endif // #if !defined(_WIN64) && defined(_Wp64) + +#endif // BASE_FIX_WP64_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/float_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/float_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/float_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/float_util.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,25 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FLOAT_UTIL_H_ +#define BASE_FLOAT_UTIL_H_ + +#include "build/build_config.h" + +#include +#include + +namespace base { + +inline bool IsFinite(const double& number) { +#if defined(OS_POSIX) + return finite(number) != 0; +#elif defined(OS_WIN) + return _finite(number) != 0; +#endif +} + +} // namespace base + +#endif // BASE_FLOAT_UTIL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/foundation_utils_mac.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/foundation_utils_mac.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/foundation_utils_mac.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/foundation_utils_mac.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,37 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FOUNDATION_UTILS_MAC_H_ +#define BASE_FOUNDATION_UTILS_MAC_H_ + +#include +#import + +// CFTypeRefToNSObjectAutorelease transfers ownership of a Core Foundation +// object (one derived from CFTypeRef) to the Foundation memory management +// system. In a traditional managed-memory environment, cf_object is +// autoreleased and returned as an NSObject. In a garbage-collected +// environment, cf_object is marked as eligible for garbage collection. +// +// This function should only be used to convert a concrete CFTypeRef type to +// its equivalent "toll-free bridged" NSObject subclass, for example, +// converting a CFStringRef to NSString. +// +// By calling this function, callers relinquish any ownership claim to +// cf_object. In a managed-memory environment, the object's ownership will be +// managed by the innermost NSAutoreleasePool, so after this function returns, +// callers should not assume that cf_object is valid any longer than the +// returned NSObject. +static inline id CFTypeRefToNSObjectAutorelease(CFTypeRef cf_object) { + // When GC is on, NSMakeCollectable marks cf_object for GC and autorelease + // is a no-op. + // + // In the traditional GC-less environment, NSMakeCollectable is a no-op, + // and cf_object is autoreleased, balancing out the caller's ownership claim. + // + // NSMakeCollectable returns nil when used on a NULL object. + return [NSMakeCollectable(cf_object) autorelease]; +} + +#endif // BASE_FOUNDATION_UTILS_MAC_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/DEPS firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/DEPS --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/DEPS 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/DEPS 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,4 @@ +include_rules = [ + "+skia", + "+third_party/libpng" +] diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/gdi_util.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/gdi_util.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/gdi_util.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/gdi_util.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,79 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/gfx/gdi_util.h" + +namespace gfx { + +void CreateBitmapHeader(int width, int height, BITMAPINFOHEADER* hdr) { + CreateBitmapHeaderWithColorDepth(width, height, 32, hdr); +} + +void CreateBitmapHeaderWithColorDepth(int width, int height, int color_depth, + BITMAPINFOHEADER* hdr) { + // These values are shared with gfx::PlatformDevice + hdr->biSize = sizeof(BITMAPINFOHEADER); + hdr->biWidth = width; + hdr->biHeight = -height; // minus means top-down bitmap + hdr->biPlanes = 1; + hdr->biBitCount = color_depth; + hdr->biCompression = BI_RGB; // no compression + hdr->biSizeImage = 0; + hdr->biXPelsPerMeter = 1; + hdr->biYPelsPerMeter = 1; + hdr->biClrUsed = 0; + hdr->biClrImportant = 0; +} + + +void CreateBitmapV4Header(int width, int height, BITMAPV4HEADER* hdr) { + // Because bmp v4 header is just an extension, we just create a v3 header and + // copy the bits over to the v4 header. + BITMAPINFOHEADER header_v3; + CreateBitmapHeader(width, height, &header_v3); + memset(hdr, 0, sizeof(BITMAPV4HEADER)); + memcpy(hdr, &header_v3, sizeof(BITMAPINFOHEADER)); + + // Correct the size of the header and fill in the mask values. + hdr->bV4Size = sizeof(BITMAPV4HEADER); + hdr->bV4RedMask = 0x00ff0000; + hdr->bV4GreenMask = 0x0000ff00; + hdr->bV4BlueMask = 0x000000ff; + hdr->bV4AlphaMask = 0xff000000; +} + +// Creates a monochrome bitmap header. +void CreateMonochromeBitmapHeader(int width, + int height, + BITMAPINFOHEADER* hdr) { + hdr->biSize = sizeof(BITMAPINFOHEADER); + hdr->biWidth = width; + hdr->biHeight = -height; + hdr->biPlanes = 1; + hdr->biBitCount = 1; + hdr->biCompression = BI_RGB; + hdr->biSizeImage = 0; + hdr->biXPelsPerMeter = 1; + hdr->biYPelsPerMeter = 1; + hdr->biClrUsed = 0; + hdr->biClrImportant = 0; +} + +void SubtractRectanglesFromRegion(HRGN hrgn, + const std::vector& cutouts) { + if (cutouts.size()) { + HRGN cutout = ::CreateRectRgn(0, 0, 0, 0); + for (size_t i = 0; i < cutouts.size(); i++) { + ::SetRectRgn(cutout, + cutouts[i].x(), + cutouts[i].y(), + cutouts[i].right(), + cutouts[i].bottom()); + ::CombineRgn(hrgn, hrgn, cutout, RGN_DIFF); + } + ::DeleteObject(cutout); + } +} + +} // namespace gfx diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/gdi_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/gdi_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/gdi_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/gdi_util.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,36 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_GFX_GDI_UTIL_H__ +#define BASE_GFX_GDI_UTIL_H__ + +#include +#include +#include "base/gfx/rect.h" + +namespace gfx { + +// Creates a BITMAPINFOHEADER structure given the bitmap's size. +void CreateBitmapHeader(int width, int height, BITMAPINFOHEADER* hdr); + +// Creates a BITMAPINFOHEADER structure given the bitmap's size and +// color depth in bits per pixel. +void CreateBitmapHeaderWithColorDepth(int width, int height, int color_depth, + BITMAPINFOHEADER* hdr); + +// Creates a BITMAPV4HEADER structure given the bitmap's size. You probably +// only need to use BMP V4 if you need transparency (alpha channel). This +// function sets the AlphaMask to 0xff000000. +void CreateBitmapV4Header(int width, int height, BITMAPV4HEADER* hdr); + +// Creates a monochrome bitmap header. +void CreateMonochromeBitmapHeader(int width, int height, BITMAPINFOHEADER* hdr); + +// Modify the given hrgn by subtracting the given rectangles. +void SubtractRectanglesFromRegion(HRGN hrgn, + const std::vector& cutouts); + +} // namespace gfx + +#endif // BASE_GFX_GDI_UTIL_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/gtk_native_view_id_manager.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/gtk_native_view_id_manager.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/gtk_native_view_id_manager.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/gtk_native_view_id_manager.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,145 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/gfx/gtk_native_view_id_manager.h" + +#include "base/gfx/rect.h" +#include "base/logging.h" +#include "base/rand_util.h" + +#include +#include + +// ----------------------------------------------------------------------------- +// Bounce functions for GTK to callback into a C++ object... + +static void OnRealize(gfx::NativeView widget, void* arg) { + GtkNativeViewManager* manager = reinterpret_cast(arg); + manager->OnRealize(widget); +} + +static void OnUnrealize(gfx::NativeView widget, void *arg) { + GtkNativeViewManager* manager = reinterpret_cast(arg); + manager->OnUnrealize(widget); +} + +static void OnDestroy(GtkObject* obj, void* arg) { + GtkNativeViewManager* manager = reinterpret_cast(arg); + manager->OnDestroy(reinterpret_cast(obj)); +} + +// ----------------------------------------------------------------------------- + + +// ----------------------------------------------------------------------------- +// Public functions... + +GtkNativeViewManager::GtkNativeViewManager() { +} + +gfx::NativeViewId GtkNativeViewManager::GetIdForWidget(gfx::NativeView widget) { + // This is just for unit tests: + if (!widget) + return 0; + + AutoLock locked(lock_); + + std::map::const_iterator i = + native_view_to_id_.find(widget); + + if (i != native_view_to_id_.end()) + return i->second; + + gfx::NativeViewId new_id = + static_cast(base::RandUint64()); + while (id_to_info_.find(new_id) != id_to_info_.end()) + new_id = static_cast(base::RandUint64()); + + NativeViewInfo info; + if (GTK_WIDGET_REALIZED(widget)) { + GdkWindow *gdk_window = widget->window; + CHECK(gdk_window); + info.x_window_id = GDK_WINDOW_XID(gdk_window); + } + + native_view_to_id_[widget] = new_id; + id_to_info_[new_id] = info; + + g_signal_connect(widget, "realize", G_CALLBACK(::OnRealize), this); + g_signal_connect(widget, "unrealize", G_CALLBACK(::OnUnrealize), this); + g_signal_connect(widget, "destroy", G_CALLBACK(::OnDestroy), this); + + return new_id; +} + +bool GtkNativeViewManager::GetXIDForId(XID* output, gfx::NativeViewId id) { + AutoLock locked(lock_); + + std::map::const_iterator i = + id_to_info_.find(id); + + if (i == id_to_info_.end()) + return false; + + *output = i->second.x_window_id; + return true; +} + +// ----------------------------------------------------------------------------- + + +// ----------------------------------------------------------------------------- +// Private functions... + +gfx::NativeViewId GtkNativeViewManager::GetWidgetId(gfx::NativeView widget) { + lock_.AssertAcquired(); + + std::map::const_iterator i = + native_view_to_id_.find(widget); + + CHECK(i != native_view_to_id_.end()); + return i->second; +} + +void GtkNativeViewManager::OnRealize(gfx::NativeView widget) { + AutoLock locked(lock_); + + const gfx::NativeViewId id = GetWidgetId(widget); + std::map::iterator i = + id_to_info_.find(id); + + CHECK(i != id_to_info_.end()); + CHECK(widget->window); + + i->second.x_window_id = GDK_WINDOW_XID(widget->window); +} + +void GtkNativeViewManager::OnUnrealize(gfx::NativeView widget) { + AutoLock locked(lock_); + + const gfx::NativeViewId id = GetWidgetId(widget); + std::map::iterator i = + id_to_info_.find(id); + + CHECK(i != id_to_info_.end()); + + i->second.x_window_id = 0; +} + +void GtkNativeViewManager::OnDestroy(gfx::NativeView widget) { + AutoLock locked(lock_); + + std::map::iterator i = + native_view_to_id_.find(widget); + CHECK(i != native_view_to_id_.end()); + + std::map::iterator j = + id_to_info_.find(i->second); + CHECK(j != id_to_info_.end()); + + native_view_to_id_.erase(i); + id_to_info_.erase(j); +} + +// ----------------------------------------------------------------------------- diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/gtk_native_view_id_manager.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/gtk_native_view_id_manager.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/gtk_native_view_id_manager.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/gtk_native_view_id_manager.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,91 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_GFX_GTK_NATIVE_VIEW_ID_MANAGER_H_ +#define BASE_GFX_GTK_NATIVE_VIEW_ID_MANAGER_H_ + +#include + +#include "base/singleton.h" +#include "base/gfx/native_widget_types.h" + +typedef unsigned long XID; + +// NativeViewIds are the opaque values which the renderer holds as a reference +// to a window. These ids are often used in sync calls from the renderer and +// one cannot terminate sync calls on the UI thread as that can lead to +// deadlocks. +// +// Because of this, we have the BACKGROUND_X11 thread for these calls and this +// thread has a separate X connection in order to answer them. But one cannot +// use GTK on multiple threads, so the BACKGROUND_X11 thread deals only in Xlib +// calls and, thus, XIDs. +// +// So we could make NativeViewIds be the X id of the window. However, at the +// time when we need to tell the renderer about its NativeViewId, an XID isn't +// availible and it goes very much against the grain of the code to make it so. +// Also, we worry that GTK might choose to change the underlying X window id +// when, say, the widget is hidden or repacked. Finally, if we used XIDs then a +// compromised renderer could start asking questions about any X windows on the +// system. +// +// Thus, we have this object. It produces random NativeViewIds from GtkWidget +// pointers and observes the various signals from the widget for when an X +// window is created, destroyed etc. Thus it provides a thread safe mapping +// from NativeViewIds to the current XID for that widget. +// +// You get a reference to the global instance with: +// Singleton() +class GtkNativeViewManager { + public: + // Must be called from the UI thread: + // + // Return a NativeViewId for the given widget and attach to the various + // signals emitted by that widget. The NativeViewId is pseudo-randomly + // allocated so that a compromised renderer trying to guess values will fail + // with high probability. The NativeViewId will not be reused for the + // lifetime of the GtkWidget. + gfx::NativeViewId GetIdForWidget(gfx::NativeView widget); + + // May be called from any thread: + // + // xid: (output) the resulting X window ID, or 0 + // id: a value previously returned from GetIdForWidget + // returns: true if |id| is a valid id, false otherwise. + // + // If the widget referenced by |id| does not current have an X window id, + // |*xid| is set to 0. + bool GetXIDForId(XID* xid, gfx::NativeViewId id); + + // These are actually private functions, but need to be called from statics. + void OnRealize(gfx::NativeView widget); + void OnUnrealize(gfx::NativeView widget); + void OnDestroy(gfx::NativeView widget); + + private: + // This object is a singleton: + GtkNativeViewManager(); + friend struct DefaultSingletonTraits; + + struct NativeViewInfo { + NativeViewInfo() + : x_window_id(0) { + } + + XID x_window_id; + }; + + gfx::NativeViewId GetWidgetId(gfx::NativeView id); + + // protects native_view_to_id_ and id_to_info_ + Lock lock_; + // If asked for an id for the same widget twice, we want to return the same + // id. So this records the current mapping. + std::map native_view_to_id_; + std::map id_to_info_; + + DISALLOW_COPY_AND_ASSIGN(GtkNativeViewManager); +}; + +#endif // BASE_GFX_GTK_NATIVE_VIEW_ID_MANAGER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/gtk_util.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/gtk_util.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/gtk_util.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/gtk_util.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,30 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/gfx/gtk_util.h" + +#include +#include +#include + +#include "base/gfx/rect.h" + +namespace gfx { + +const GdkColor kGdkWhite = GDK_COLOR_RGB(0xff, 0xff, 0xff); +const GdkColor kGdkBlack = GDK_COLOR_RGB(0x00, 0x00, 0x00); +const GdkColor kGdkGreen = GDK_COLOR_RGB(0x00, 0xff, 0x00); + +void SubtractRectanglesFromRegion(GdkRegion* region, + const std::vector& cutouts) { + for (size_t i = 0; i < cutouts.size(); ++i) { + GdkRectangle rect = cutouts[i].ToGdkRectangle(); + GdkRegion* rect_region = gdk_region_rectangle(&rect); + gdk_region_subtract(region, rect_region); + // TODO(deanm): It would be nice to be able to reuse the GdkRegion here. + gdk_region_destroy(rect_region); + } +} + +} // namespace gfx diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/gtk_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/gtk_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/gtk_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/gtk_util.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,57 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_GFX_GTK_UTIL_H_ +#define BASE_GFX_GTK_UTIL_H_ + +#include +#include + +#include + +#include "base/scoped_ptr.h" + +typedef struct _GdkColor GdkColor; +typedef struct _GdkRegion GdkRegion; + +// Define a macro for creating GdkColors from RGB values. This is a macro to +// allow static construction of literals, etc. Use this like: +// GdkColor white = GDK_COLOR_RGB(0xff, 0xff, 0xff); +#define GDK_COLOR_RGB(r, g, b) {0, r * 257, g * 257, b * 257} + +namespace gfx { + +class Rect; + +extern const GdkColor kGdkWhite; +extern const GdkColor kGdkBlack; +extern const GdkColor kGdkGreen; + +// Modify the given region by subtracting the given rectangles. +void SubtractRectanglesFromRegion(GdkRegion* region, + const std::vector& cutouts); + +} // namespace gfx + +namespace { +// A helper class that will g_object_unref |p| when it goes out of scope. +// This never adds a ref, it only unrefs. +template +struct GObjectUnrefer { + void operator()(Type* ptr) const { + if (ptr) + g_object_unref(ptr); + } +}; +} // namespace + +// It's not legal C++ to have a templatized typedefs, so we wrap it in a +// struct. When using this, you need to include ::Type. E.g., +// ScopedGObject::Type loader(gdk_pixbuf_loader_new()); +template +struct ScopedGObject { + typedef scoped_ptr_malloc > Type; +}; + +#endif // BASE_GFX_GTK_UTIL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/jpeg_codec.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/jpeg_codec.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/jpeg_codec.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/jpeg_codec.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,523 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/gfx/jpeg_codec.h" + +#include + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "skia/include/SkBitmap.h" + +extern "C" { +#include "third_party/libjpeg/jpeglib.h" +} + +// Encoder/decoder shared stuff ------------------------------------------------ + +namespace { + +// used to pass error info through the JPEG library +struct CoderErrorMgr { + jpeg_error_mgr pub; + jmp_buf setjmp_buffer; +}; + +void ErrorExit(jpeg_common_struct* cinfo) { + CoderErrorMgr *err = reinterpret_cast(cinfo->err); + + // Return control to the setjmp point. + longjmp(err->setjmp_buffer, false); +} + +} // namespace + +// Encoder --------------------------------------------------------------------- +// +// This code is based on nsJPEGEncoder from Mozilla. +// Copyright 2005 Google Inc. (Brett Wilson, contributor) + +namespace { + +// Initial size for the output buffer in the JpegEncoderState below. +const static int initial_output_buffer_size = 8192; + +struct JpegEncoderState { + JpegEncoderState(std::vector* o) + : out(o), + image_buffer_used(0) { + } + + // Output buffer, of which 'image_buffer_used' bytes are actually used (this + // will often be less than the actual size of the vector because we size it + // so that libjpeg can write directly into it. + std::vector* out; + + // Number of bytes in the 'out' buffer that are actually used (see above). + size_t image_buffer_used; +}; + +// Initializes the JpegEncoderState for encoding, and tells libjpeg about where +// the output buffer is. +// +// From the JPEG library: +// "Initialize destination. This is called by jpeg_start_compress() before +// any data is actually written. It must initialize next_output_byte and +// free_in_buffer. free_in_buffer must be initialized to a positive value." +void InitDestination(jpeg_compress_struct* cinfo) { + JpegEncoderState* state = static_cast(cinfo->client_data); + DCHECK(state->image_buffer_used == 0) << "initializing after use"; + + state->out->resize(initial_output_buffer_size); + state->image_buffer_used = 0; + + cinfo->dest->next_output_byte = &(*state->out)[0]; + cinfo->dest->free_in_buffer = initial_output_buffer_size; +} + +// Resize the buffer that we give to libjpeg and update our and its state. +// +// From the JPEG library: +// "Callback used by libjpeg whenever the buffer has filled (free_in_buffer +// reaches zero). In typical applications, it should write out the *entire* +// buffer (use the saved start address and buffer length; ignore the current +// state of next_output_byte and free_in_buffer). Then reset the pointer & +// count to the start of the buffer, and return TRUE indicating that the +// buffer has been dumped. free_in_buffer must be set to a positive value +// when TRUE is returned. A FALSE return should only be used when I/O +// suspension is desired (this operating mode is discussed in the next +// section)." +boolean EmptyOutputBuffer(jpeg_compress_struct* cinfo) { + JpegEncoderState* state = static_cast(cinfo->client_data); + + // note the new size, the buffer is full + state->image_buffer_used = state->out->size(); + + // expand buffer, just double size each time + state->out->resize(state->out->size() * 2); + + // tell libjpeg where to write the next data + cinfo->dest->next_output_byte = &(*state->out)[state->image_buffer_used]; + cinfo->dest->free_in_buffer = state->out->size() - state->image_buffer_used; + return 1; +} + +// Cleans up the JpegEncoderState to prepare for returning in the final form. +// +// From the JPEG library: +// "Terminate destination --- called by jpeg_finish_compress() after all data +// has been written. In most applications, this must flush any data +// remaining in the buffer. Use either next_output_byte or free_in_buffer to +// determine how much data is in the buffer." +void TermDestination(jpeg_compress_struct* cinfo) { + JpegEncoderState* state = static_cast(cinfo->client_data); + DCHECK(state->out->size() >= state->image_buffer_used); + + // update the used byte based on the next byte libjpeg would write to + state->image_buffer_used = cinfo->dest->next_output_byte - &(*state->out)[0]; + DCHECK(state->image_buffer_used < state->out->size()) << + "JPEG library busted, got a bad image buffer size"; + + // update our buffer so that it exactly encompases the desired data + state->out->resize(state->image_buffer_used); +} + +// Converts RGBA to RGB (removing the alpha values) to prepare to send data to +// libjpeg. This converts one row of data in rgba with the given width in +// pixels the the given rgb destination buffer (which should have enough space +// reserved for the final data). +void StripAlpha(const unsigned char* rgba, int pixel_width, unsigned char* rgb) +{ + for (int x = 0; x < pixel_width; x++) { + const unsigned char* pixel_in = &rgba[x * 4]; + unsigned char* pixel_out = &rgb[x * 3]; + pixel_out[0] = pixel_in[0]; + pixel_out[1] = pixel_in[1]; + pixel_out[2] = pixel_in[2]; + } +} + +// Converts BGRA to RGB by reordering the color components and dropping the +// alpha. This converts one row of data in rgba with the given width in +// pixels the the given rgb destination buffer (which should have enough space +// reserved for the final data). +void BGRAtoRGB(const unsigned char* bgra, int pixel_width, unsigned char* rgb) +{ + for (int x = 0; x < pixel_width; x++) { + const unsigned char* pixel_in = &bgra[x * 4]; + unsigned char* pixel_out = &rgb[x * 3]; + pixel_out[0] = pixel_in[2]; + pixel_out[1] = pixel_in[1]; + pixel_out[2] = pixel_in[0]; + } +} + +// This class destroys the given jpeg_compress object when it goes out of +// scope. It simplifies the error handling in Encode (and even applies to the +// success case). +class CompressDestroyer { + public: + CompressDestroyer() : cinfo_(NULL) { + } + ~CompressDestroyer() { + DestroyManagedObject(); + } + void SetManagedObject(jpeg_compress_struct* ci) { + DestroyManagedObject(); + cinfo_ = ci; + } + void DestroyManagedObject() { + if (cinfo_) { + jpeg_destroy_compress(cinfo_); + cinfo_ = NULL; + } + } + private: + jpeg_compress_struct* cinfo_; +}; + +} // namespace + +bool JPEGCodec::Encode(const unsigned char* input, ColorFormat format, + int w, int h, int row_byte_width, + int quality, std::vector* output) { + jpeg_compress_struct cinfo; + CompressDestroyer destroyer; + output->clear(); + + // We set up the normal JPEG error routines, then override error_exit. + // This must be done before the call to create_compress. + CoderErrorMgr errmgr; + cinfo.err = jpeg_std_error(&errmgr.pub); + errmgr.pub.error_exit = ErrorExit; + // Establish the setjmp return context for ErrorExit to use. + if (setjmp(errmgr.setjmp_buffer)) { + // If we get here, the JPEG code has signaled an error. + // MSDN notes: "if you intend your code to be portable, do not rely on + // correct destruction of frame-based objects when executing a nonlocal + // goto using a call to longjmp." So we delete the CompressDestroyer's + // object manually instead. + destroyer.DestroyManagedObject(); + return false; + } + + // The destroyer will destroy() cinfo on exit. + jpeg_create_compress(&cinfo); + destroyer.SetManagedObject(&cinfo); + + cinfo.image_width = w; + cinfo.image_height = h; + cinfo.input_components = 3; + cinfo.in_color_space = JCS_RGB; + cinfo.data_precision = 8; + + jpeg_set_defaults(&cinfo); + jpeg_set_quality(&cinfo, quality, 1); // quality here is 0-100 + + // set up the destination manager + jpeg_destination_mgr destmgr; + destmgr.init_destination = InitDestination; + destmgr.empty_output_buffer = EmptyOutputBuffer; + destmgr.term_destination = TermDestination; + cinfo.dest = &destmgr; + + JpegEncoderState state(output); + cinfo.client_data = &state; + + jpeg_start_compress(&cinfo, 1); + + // feed it the rows, doing necessary conversions for the color format + if (format == FORMAT_RGB) { + // no conversion necessary + while (cinfo.next_scanline < cinfo.image_height) { + const unsigned char* row = &input[cinfo.next_scanline * row_byte_width]; + jpeg_write_scanlines(&cinfo, const_cast(&row), 1); + } + } else { + // get the correct format converter + void (*converter)(const unsigned char* in, int w, unsigned char* rgb); + if (format == FORMAT_RGBA) { + converter = StripAlpha; + } else if (format == FORMAT_BGRA) { + converter = BGRAtoRGB; + } else { + NOTREACHED() << "Invalid pixel format"; + return false; + } + + // output row after converting + unsigned char* row = new unsigned char[w * 3]; + + while (cinfo.next_scanline < cinfo.image_height) { + converter(&input[cinfo.next_scanline * row_byte_width], w, row); + jpeg_write_scanlines(&cinfo, &row, 1); + } + delete[] row; + } + + jpeg_finish_compress(&cinfo); + return true; +} + +// Decoder -------------------------------------------------------------------- + +namespace { + +struct JpegDecoderState { + JpegDecoderState(const unsigned char* in, size_t len) + : input_buffer(in), input_buffer_length(len) { + } + + const unsigned char* input_buffer; + size_t input_buffer_length; +}; + +// Callback to initialize the source. +// +// From the JPEG library: +// "Initialize source. This is called by jpeg_read_header() before any data is +// actually read. May leave bytes_in_buffer set to 0 (in which case a +// fill_input_buffer() call will occur immediately)." +void InitSource(j_decompress_ptr cinfo) { + JpegDecoderState* state = static_cast(cinfo->client_data); + cinfo->src->next_input_byte = state->input_buffer; + cinfo->src->bytes_in_buffer = state->input_buffer_length; +} + +// Callback to fill the buffer. Since our buffer already contains all the data, +// we should never need to provide more data. If libjpeg thinks it needs more +// data, our input is probably corrupt. +// +// From the JPEG library: +// "This is called whenever bytes_in_buffer has reached zero and more data is +// wanted. In typical applications, it should read fresh data into the buffer +// (ignoring the current state of next_input_byte and bytes_in_buffer), reset +// the pointer & count to the start of the buffer, and return TRUE indicating +// that the buffer has been reloaded. It is not necessary to fill the buffer +// entirely, only to obtain at least one more byte. bytes_in_buffer MUST be +// set to a positive value if TRUE is returned. A FALSE return should only +// be used when I/O suspension is desired." +boolean FillInputBuffer(j_decompress_ptr cinfo) { + return false; +} + +// Skip data in the buffer. Since we have all the data at once, this operation +// is easy. It is not clear if this ever gets called because the JPEG library +// should be able to do the skip itself (it has all the data). +// +// From the JPEG library: +// "Skip num_bytes worth of data. The buffer pointer and count should be +// advanced over num_bytes input bytes, refilling the buffer as needed. This +// is used to skip over a potentially large amount of uninteresting data +// (such as an APPn marker). In some applications it may be possible to +// optimize away the reading of the skipped data, but it's not clear that +// being smart is worth much trouble; large skips are uncommon. +// bytes_in_buffer may be zero on return. A zero or negative skip count +// should be treated as a no-op." +void SkipInputData(j_decompress_ptr cinfo, long num_bytes) { + if (num_bytes > static_cast(cinfo->src->bytes_in_buffer)) { + // Since all our data should be in the buffer, trying to skip beyond it + // means that there is some kind of error or corrupt input data. A 0 for + // bytes left means it will call FillInputBuffer which will then fail. + cinfo->src->next_input_byte += cinfo->src->bytes_in_buffer; + cinfo->src->bytes_in_buffer = 0; + } else if (num_bytes > 0) { + cinfo->src->bytes_in_buffer -= static_cast(num_bytes); + cinfo->src->next_input_byte += num_bytes; + } +} + +// Our source doesn't need any cleanup, so this is a NOP. +// +// From the JPEG library: +// "Terminate source --- called by jpeg_finish_decompress() after all data has +// been read to clean up JPEG source manager. NOT called by jpeg_abort() or +// jpeg_destroy()." +void TermSource(j_decompress_ptr cinfo) { +} + +// Converts one row of rgb data to rgba data by adding a fully-opaque alpha +// value. +void AddAlpha(const unsigned char* rgb, int pixel_width, unsigned char* rgba) { + for (int x = 0; x < pixel_width; x++) { + const unsigned char* pixel_in = &rgb[x * 3]; + unsigned char* pixel_out = &rgba[x * 4]; + pixel_out[0] = pixel_in[0]; + pixel_out[1] = pixel_in[1]; + pixel_out[2] = pixel_in[2]; + pixel_out[3] = 0xff; + } +} + +// Converts one row of RGB data to BGRA by reordering the color components and +// adding alpha values of 0xff. +void RGBtoBGRA(const unsigned char* bgra, int pixel_width, unsigned char* rgb) +{ + for (int x = 0; x < pixel_width; x++) { + const unsigned char* pixel_in = &bgra[x * 3]; + unsigned char* pixel_out = &rgb[x * 4]; + pixel_out[0] = pixel_in[2]; + pixel_out[1] = pixel_in[1]; + pixel_out[2] = pixel_in[0]; + pixel_out[3] = 0xff; + } +} + +// This class destroys the given jpeg_decompress object when it goes out of +// scope. It simplifies the error handling in Decode (and even applies to the +// success case). +class DecompressDestroyer { + public: + DecompressDestroyer() : cinfo_(NULL) { + } + ~DecompressDestroyer() { + DestroyManagedObject(); + } + void SetManagedObject(jpeg_decompress_struct* ci) { + DestroyManagedObject(); + cinfo_ = ci; + } + void DestroyManagedObject() { + if (cinfo_) { + jpeg_destroy_decompress(cinfo_); + cinfo_ = NULL; + } + } + private: + jpeg_decompress_struct* cinfo_; +}; + +} // namespace + +bool JPEGCodec::Decode(const unsigned char* input, size_t input_size, + ColorFormat format, std::vector* output, + int* w, int* h) { + jpeg_decompress_struct cinfo; + DecompressDestroyer destroyer; + output->clear(); + + // We set up the normal JPEG error routines, then override error_exit. + // This must be done before the call to create_decompress. + CoderErrorMgr errmgr; + cinfo.err = jpeg_std_error(&errmgr.pub); + errmgr.pub.error_exit = ErrorExit; + // Establish the setjmp return context for ErrorExit to use. + if (setjmp(errmgr.setjmp_buffer)) { + // If we get here, the JPEG code has signaled an error. + // See note in JPEGCodec::Encode() for why we need to destroy the cinfo + // manually here. + destroyer.DestroyManagedObject(); + return false; + } + + // The destroyer will destroy() cinfo on exit. We don't want to set the + // destroyer's object until cinfo is initialized. + jpeg_create_decompress(&cinfo); + destroyer.SetManagedObject(&cinfo); + + // set up the source manager + jpeg_source_mgr srcmgr; + srcmgr.init_source = InitSource; + srcmgr.fill_input_buffer = FillInputBuffer; + srcmgr.skip_input_data = SkipInputData; + srcmgr.resync_to_restart = jpeg_resync_to_restart; // use default routine + srcmgr.term_source = TermSource; + cinfo.src = &srcmgr; + + JpegDecoderState state(input, input_size); + cinfo.client_data = &state; + + // fill the file metadata into our buffer + if (jpeg_read_header(&cinfo, true) != JPEG_HEADER_OK) + return false; + + // we want to always get RGB data out + switch (cinfo.jpeg_color_space) { + case JCS_GRAYSCALE: + case JCS_RGB: + case JCS_YCbCr: + cinfo.out_color_space = JCS_RGB; + break; + case JCS_CMYK: + case JCS_YCCK: + default: + // Mozilla errors out on these color spaces, so I presume that the jpeg + // library can't do automatic color space conversion for them. We don't + // care about these anyway. + return false; + } + cinfo.output_components = 3; + + jpeg_calc_output_dimensions(&cinfo); + *w = cinfo.output_width; + *h = cinfo.output_height; + + jpeg_start_decompress(&cinfo); + + // FIXME(brettw) we may want to allow the capability for callers to request + // how to align row lengths as we do for the compressor. + int row_read_stride = cinfo.output_width * cinfo.output_components; + + if (format == FORMAT_RGB) { + // easy case, row needs no conversion + int row_write_stride = row_read_stride; + output->resize(row_write_stride * cinfo.output_height); + + for (int row = 0; row < static_cast(cinfo.output_height); row++) { + unsigned char* rowptr = &(*output)[row * row_write_stride]; + if (!jpeg_read_scanlines(&cinfo, &rowptr, 1)) + return false; + } + } else { + // Rows need conversion to output format: read into a temporary buffer and + // expand to the final one. Performance: we could avoid the extra + // allocation by doing the expansion in-place. + int row_write_stride; + void (*converter)(const unsigned char* rgb, int w, unsigned char* out); + if (format == FORMAT_RGBA) { + row_write_stride = cinfo.output_width * 4; + converter = AddAlpha; + } else if (format == FORMAT_BGRA) { + row_write_stride = cinfo.output_width * 4; + converter = RGBtoBGRA; + } else { + NOTREACHED() << "Invalid pixel format"; + jpeg_destroy_decompress(&cinfo); + return false; + } + + output->resize(row_write_stride * cinfo.output_height); + + scoped_array row_data(new unsigned char[row_read_stride]); + unsigned char* rowptr = row_data.get(); + for (int row = 0; row < static_cast(cinfo.output_height); row++) { + if (!jpeg_read_scanlines(&cinfo, &rowptr, 1)) + return false; + converter(rowptr, *w, &(*output)[row * row_write_stride]); + } + } + + jpeg_finish_decompress(&cinfo); + jpeg_destroy_decompress(&cinfo); + return true; +} + +// static +SkBitmap* JPEGCodec::Decode(const unsigned char* input, size_t input_size) { + int w, h; + std::vector data_vector; + // Use FORMAT_BGRA as that maps to Skia's 32 bit (kARGB_8888_Config) format. + if (!Decode(input, input_size, FORMAT_BGRA, &data_vector, &w, &h)) + return NULL; + + // Skia only handles 32 bit images. + int data_length = w * h * 4; + + SkBitmap* bitmap = new SkBitmap(); + bitmap->setConfig(SkBitmap::kARGB_8888_Config, w, h); + bitmap->allocPixels(); + memcpy(bitmap->getAddr32(0, 0), &data_vector[0], data_length); + + return bitmap; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/jpeg_codec.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/jpeg_codec.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/jpeg_codec.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/jpeg_codec.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,59 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_GFX_JPEG_CODEC_H_ +#define BASE_GFX_JPEG_CODEC_H_ + +#include + +class SkBitmap; + +// Interface for encoding/decoding JPEG data. This is a wrapper around libjpeg, +// which has an inconvenient interface for callers. This is only used for UI +// elements, WebKit has its own more complicated JPEG decoder which handles, +// among other things, partially downloaded data. +class JPEGCodec { + public: + enum ColorFormat { + // 3 bytes per pixel (packed), in RGB order regardless of endianness. + // This is the native JPEG format. + FORMAT_RGB, + + // 4 bytes per pixel, in RGBA order in mem regardless of endianness. + FORMAT_RGBA, + + // 4 bytes per pixel, in BGRA order in mem regardless of endianness. + // This is the default Windows DIB order. + FORMAT_BGRA + }; + + // Encodes the given raw 'input' data, with each pixel being represented as + // given in 'format'. The encoded JPEG data will be written into the supplied + // vector and true will be returned on success. On failure (false), the + // contents of the output buffer are undefined. + // + // w, h: dimensions of the image + // row_byte_width: the width in bytes of each row. This may be greater than + // w * bytes_per_pixel if there is extra padding at the end of each row + // (often, each row is padded to the next machine word). + // quality: an integer in the range 0-100, where 100 is the highest quality. + static bool Encode(const unsigned char* input, ColorFormat format, + int w, int h, int row_byte_width, + int quality, std::vector* output); + + // Decodes the JPEG data contained in input of length input_size. The + // decoded data will be placed in *output with the dimensions in *w and *h + // on success (returns true). This data will be written in the'format' + // format. On failure, the values of these output variables is undefined. + static bool Decode(const unsigned char* input, size_t input_size, + ColorFormat format, std::vector* output, + int* w, int* h); + + // Decodes the JPEG data contained in input of length input_size. If + // successful, a SkBitmap is created and returned. It is up to the caller + // to delete the returned bitmap. + static SkBitmap* Decode(const unsigned char* input, size_t input_size); +}; + +#endif // BASE_GFX_JPEG_CODEC_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/jpeg_codec_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/jpeg_codec_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/jpeg_codec_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/jpeg_codec_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,147 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/gfx/jpeg_codec.h" +#include "testing/gtest/include/gtest/gtest.h" + +// out of 100, this indicates how compressed it will be, this should be changed +// with jpeg equality threshold +// static int jpeg_quality = 75; // FIXME(brettw) +static int jpeg_quality = 100; + +// The threshold of average color differences where we consider two images +// equal. This number was picked to be a little above the observed difference +// using the above quality. +static double jpeg_equality_threshold = 1.0; + +// Computes the average difference between each value in a and b. A and b +// should be the same size. Used to see if two images are approximately equal +// in the presence of compression. +static double AveragePixelDelta(const std::vector& a, + const std::vector& b) { + // if the sizes are different, say the average difference is the maximum + if (a.size() != b.size()) + return 255.0; + if (a.size() == 0) + return 0; // prevent divide by 0 below + + double acc = 0.0; + for (size_t i = 0; i < a.size(); i++) + acc += fabs(static_cast(a[i]) - static_cast(b[i])); + + return acc / static_cast(a.size()); +} + +static void MakeRGBImage(int w, int h, std::vector* dat) { + dat->resize(w * h * 3); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + unsigned char* org_px = &(*dat)[(y * w + x) * 3]; + org_px[0] = x * 3; // r + org_px[1] = x * 3 + 1; // g + org_px[2] = x * 3 + 2; // b + } + } +} + +TEST(JPEGCodec, EncodeDecodeRGB) { + int w = 20, h = 20; + + // create an image with known values + std::vector original; + MakeRGBImage(w, h, &original); + + // encode, making sure it was compressed some + std::vector encoded; + EXPECT_TRUE(JPEGCodec::Encode(&original[0], JPEGCodec::FORMAT_RGB, w, h, + w * 3, jpeg_quality, &encoded)); + EXPECT_GT(original.size(), encoded.size()); + + // decode, it should have the same size as the original + std::vector decoded; + int outw, outh; + EXPECT_TRUE(JPEGCodec::Decode(&encoded[0], encoded.size(), + JPEGCodec::FORMAT_RGB, &decoded, + &outw, &outh)); + ASSERT_EQ(w, outw); + ASSERT_EQ(h, outh); + ASSERT_EQ(original.size(), decoded.size()); + + // Images must be approximately equal (compression will have introduced some + // minor artifacts). + ASSERT_GE(jpeg_equality_threshold, AveragePixelDelta(original, decoded)); +} + +TEST(JPEGCodec, EncodeDecodeRGBA) { + int w = 20, h = 20; + + // create an image with known values, a must be opaque because it will be + // lost during compression + std::vector original; + original.resize(w * h * 4); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + unsigned char* org_px = &original[(y * w + x) * 4]; + org_px[0] = x * 3; // r + org_px[1] = x * 3 + 1; // g + org_px[2] = x * 3 + 2; // b + org_px[3] = 0xFF; // a (opaque) + } + } + + // encode, making sure it was compressed some + std::vector encoded; + EXPECT_TRUE(JPEGCodec::Encode(&original[0], JPEGCodec::FORMAT_RGBA, w, h, + w * 4, jpeg_quality, &encoded)); + EXPECT_GT(original.size(), encoded.size()); + + // decode, it should have the same size as the original + std::vector decoded; + int outw, outh; + EXPECT_TRUE(JPEGCodec::Decode(&encoded[0], encoded.size(), + JPEGCodec::FORMAT_RGBA, &decoded, + &outw, &outh)); + ASSERT_EQ(w, outw); + ASSERT_EQ(h, outh); + ASSERT_EQ(original.size(), decoded.size()); + + // Images must be approximately equal (compression will have introduced some + // minor artifacts). + ASSERT_GE(jpeg_equality_threshold, AveragePixelDelta(original, decoded)); +} + +// Test that corrupted data decompression causes failures. +TEST(JPEGCodec, DecodeCorrupted) { + int w = 20, h = 20; + + // some random data (an uncompressed image) + std::vector original; + MakeRGBImage(w, h, &original); + + // it should fail when given non-JPEG compressed data + std::vector output; + int outw, outh; + ASSERT_FALSE(JPEGCodec::Decode(&original[0], original.size(), + JPEGCodec::FORMAT_RGB, &output, + &outw, &outh)); + + // make some compressed data + std::vector compressed; + ASSERT_TRUE(JPEGCodec::Encode(&original[0], JPEGCodec::FORMAT_RGB, w, h, + w * 3, jpeg_quality, &compressed)); + + // try decompressing a truncated version + ASSERT_FALSE(JPEGCodec::Decode(&compressed[0], compressed.size() / 2, + JPEGCodec::FORMAT_RGB, &output, + &outw, &outh)); + + // corrupt it and try decompressing that + for (int i = 10; i < 30; i++) + compressed[i] = i; + ASSERT_FALSE(JPEGCodec::Decode(&compressed[0], compressed.size(), + JPEGCodec::FORMAT_RGB, &output, + &outw, &outh)); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_theme.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_theme.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_theme.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_theme.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,710 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/gfx/native_theme.h" + +#include +#include +#include +#include + +#include "base/gfx/gdi_util.h" +#include "base/gfx/rect.h" +#include "base/logging.h" +#include "base/scoped_handle.h" +#include "skia/ext/platform_canvas.h" +#include "skia/ext/skia_utils_win.h" +#include "skia/include/SkShader.h" + +namespace { + +void SetCheckerboardShader(SkPaint* paint, const RECT& align_rect) { + // Create a 2x2 checkerboard pattern using the 3D face and highlight colors. + SkColor face = skia::COLORREFToSkColor(GetSysColor(COLOR_3DFACE)); + SkColor highlight = skia::COLORREFToSkColor(GetSysColor(COLOR_3DHILIGHT)); + SkColor buffer[] = { face, highlight, highlight, face }; + // Confusing bit: we first create a temporary bitmap with our desired pattern, + // then copy it to another bitmap. The temporary bitmap doesn't take + // ownership of the pixel data, and so will point to garbage when this + // function returns. The copy will copy the pixel data into a place owned by + // the bitmap, which is in turn owned by the shader, etc., so it will live + // until we're done using it. + SkBitmap temp_bitmap; + temp_bitmap.setConfig(SkBitmap::kARGB_8888_Config, 2, 2); + temp_bitmap.setPixels(buffer); + SkBitmap bitmap; + temp_bitmap.copyTo(&bitmap, temp_bitmap.config()); + SkShader* shader = SkShader::CreateBitmapShader(bitmap, + SkShader::kRepeat_TileMode, + SkShader::kRepeat_TileMode); + + // Align the pattern with the upper corner of |align_rect|. + SkMatrix matrix; + matrix.setTranslate(SkIntToScalar(align_rect.left), + SkIntToScalar(align_rect.top)); + shader->setLocalMatrix(matrix); + paint->setShader(shader)->safeUnref(); +} + +} // namespace + +namespace gfx { + +/* static */ +const NativeTheme* NativeTheme::instance() { + // The global NativeTheme instance. + static const NativeTheme s_native_theme; + return &s_native_theme; +} + +NativeTheme::NativeTheme() + : theme_dll_(LoadLibrary(L"uxtheme.dll")), + draw_theme_(NULL), + draw_theme_ex_(NULL), + get_theme_color_(NULL), + get_theme_content_rect_(NULL), + get_theme_part_size_(NULL), + open_theme_(NULL), + close_theme_(NULL), + set_theme_properties_(NULL), + is_theme_active_(NULL), + get_theme_int_(NULL) { + if (theme_dll_) { + draw_theme_ = reinterpret_cast( + GetProcAddress(theme_dll_, "DrawThemeBackground")); + draw_theme_ex_ = reinterpret_cast( + GetProcAddress(theme_dll_, "DrawThemeBackgroundEx")); + get_theme_color_ = reinterpret_cast( + GetProcAddress(theme_dll_, "GetThemeColor")); + get_theme_content_rect_ = reinterpret_cast( + GetProcAddress(theme_dll_, "GetThemeBackgroundContentRect")); + get_theme_part_size_ = reinterpret_cast( + GetProcAddress(theme_dll_, "GetThemePartSize")); + open_theme_ = reinterpret_cast( + GetProcAddress(theme_dll_, "OpenThemeData")); + close_theme_ = reinterpret_cast( + GetProcAddress(theme_dll_, "CloseThemeData")); + set_theme_properties_ = reinterpret_cast( + GetProcAddress(theme_dll_, "SetThemeAppProperties")); + is_theme_active_ = reinterpret_cast( + GetProcAddress(theme_dll_, "IsThemeActive")); + get_theme_int_ = reinterpret_cast( + GetProcAddress(theme_dll_, "GetThemeInt")); + } + memset(theme_handles_, 0, sizeof(theme_handles_)); +} + +NativeTheme::~NativeTheme() { + if (theme_dll_) { + // todo (cpu): fix this soon. + // CloseHandles(); + FreeLibrary(theme_dll_); + } +} + +HRESULT NativeTheme::PaintButton(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect) const { + HANDLE handle = GetThemeHandle(BUTTON); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + + // Draw it manually. + // All pressed states have both low bits set, and no other states do. + const bool focused = ((state_id & ETS_FOCUSED) == ETS_FOCUSED); + const bool pressed = ((state_id & PBS_PRESSED) == PBS_PRESSED); + if ((BP_PUSHBUTTON == part_id) && (pressed || focused)) { + // BP_PUSHBUTTON has a focus rect drawn around the outer edge, and the + // button itself is shrunk by 1 pixel. + HBRUSH brush = GetSysColorBrush(COLOR_3DDKSHADOW); + if (brush) { + FrameRect(hdc, rect, brush); + InflateRect(rect, -1, -1); + } + } + DrawFrameControl(hdc, rect, DFC_BUTTON, classic_state); + + // Draw the focus rectangle (the dotted line box) only on buttons. For radio + // and checkboxes, we let webkit draw the focus rectangle (orange glow). + if ((BP_PUSHBUTTON == part_id) && focused) { + // The focus rect is inside the button. The exact number of pixels depends + // on whether we're in classic mode or using uxtheme. + if (handle && get_theme_content_rect_) { + get_theme_content_rect_(handle, hdc, part_id, state_id, rect, rect); + } else { + InflateRect(rect, -GetSystemMetrics(SM_CXEDGE), + -GetSystemMetrics(SM_CYEDGE)); + } + DrawFocusRect(hdc, rect); + } + + return S_OK; +} + +HRESULT NativeTheme::PaintDialogBackground(HDC hdc, bool active, + RECT* rect) const { + HANDLE handle = GetThemeHandle(WINDOW); + if (handle && draw_theme_) { + return draw_theme_(handle, hdc, WP_DIALOG, + active ? FS_ACTIVE : FS_INACTIVE, rect, NULL); + } + + // Classic just renders a flat color background. + FillRect(hdc, rect, reinterpret_cast(COLOR_3DFACE + 1)); + return S_OK; +} + +HRESULT NativeTheme::PaintListBackground(HDC hdc, + bool enabled, + RECT* rect) const { + HANDLE handle = GetThemeHandle(LIST); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, 1, TS_NORMAL, rect, NULL); + + // Draw it manually. + HBRUSH bg_brush = GetSysColorBrush(COLOR_WINDOW); + FillRect(hdc, rect, bg_brush); + DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); + return S_OK; +} + +HRESULT NativeTheme::PaintMenuArrow(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + RECT* rect, + MenuArrowDirection arrow_direction, + bool is_highlighted) const { + HANDLE handle = GetThemeHandle(MENU); + if (handle && draw_theme_) { + if (arrow_direction == RIGHT_POINTING_ARROW) { + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + } else { + // There is no way to tell the uxtheme API to draw a left pointing arrow; + // it doesn't have a flag equivalent to DFCS_MENUARROWRIGHT. But they + // are needed for RTL locales on Vista. So use a memory DC and mirror + // the region with GDI's StretchBlt. + Rect r(*rect); + ScopedHDC mem_dc(CreateCompatibleDC(hdc)); + ScopedBitmap mem_bitmap(CreateCompatibleBitmap(hdc, r.width(), + r.height())); + HGDIOBJ old_bitmap = SelectObject(mem_dc, mem_bitmap); + // Copy and horizontally mirror the background from hdc into mem_dc. Use + // a negative-width source rect, starting at the rightmost pixel. + StretchBlt(mem_dc, 0, 0, r.width(), r.height(), + hdc, r.right()-1, r.y(), -r.width(), r.height(), SRCCOPY); + // Draw the arrow. + RECT theme_rect = {0, 0, r.width(), r.height()}; + HRESULT result = draw_theme_(handle, mem_dc, part_id, + state_id, &theme_rect, NULL); + // Copy and mirror the result back into mem_dc. + StretchBlt(hdc, r.x(), r.y(), r.width(), r.height(), + mem_dc, r.width()-1, 0, -r.width(), r.height(), SRCCOPY); + SelectObject(mem_dc, old_bitmap); + return result; + } + } + + // For some reason, Windows uses the name DFCS_MENUARROWRIGHT to indicate a + // left pointing arrow. This makes the following 'if' statement slightly + // counterintuitive. + UINT state; + if (arrow_direction == RIGHT_POINTING_ARROW) + state = DFCS_MENUARROW; + else + state = DFCS_MENUARROWRIGHT; + return PaintFrameControl(hdc, rect, DFC_MENU, state, is_highlighted); +} + +HRESULT NativeTheme::PaintMenuBackground(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + RECT* rect) const { + HANDLE handle = GetThemeHandle(MENU); + if (handle && draw_theme_) { + HRESULT result = draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + FrameRect(hdc, rect, GetSysColorBrush(COLOR_3DSHADOW)); + return result; + } + + FillRect(hdc, rect, GetSysColorBrush(COLOR_MENU)); + DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT); + return S_OK; +} + +HRESULT NativeTheme::PaintMenuCheckBackground(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + RECT* rect) const { + HANDLE handle = GetThemeHandle(MENU); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + // Nothing to do for background. + return S_OK; +} + +HRESULT NativeTheme::PaintMenuCheck(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + RECT* rect, + bool is_highlighted) const { + HANDLE handle = GetThemeHandle(MENU); + if (handle && draw_theme_) { + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + } + return PaintFrameControl(hdc, rect, DFC_MENU, DFCS_MENUCHECK, is_highlighted); +} + +HRESULT NativeTheme::PaintMenuGutter(HDC hdc, + int part_id, + int state_id, + RECT* rect) const { + HANDLE handle = GetThemeHandle(MENU); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + return E_NOTIMPL; +} + +HRESULT NativeTheme::PaintMenuItemBackground(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + bool selected, + RECT* rect) const { + HANDLE handle = GetThemeHandle(MENU); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + if (selected) + FillRect(hdc, rect, GetSysColorBrush(COLOR_HIGHLIGHT)); + return S_OK; +} + +HRESULT NativeTheme::PaintMenuList(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect) const { + HANDLE handle = GetThemeHandle(MENULIST); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + + // Draw it manually. + DrawFrameControl(hdc, rect, DFC_SCROLL, DFCS_SCROLLCOMBOBOX | classic_state); + return S_OK; +} + +HRESULT NativeTheme::PaintMenuSeparator(HDC hdc, + int part_id, + int state_id, + RECT* rect) const { + HANDLE handle = GetThemeHandle(MENU); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + DrawEdge(hdc, rect, EDGE_ETCHED, BF_TOP); + return S_OK; +} + +HRESULT NativeTheme::PaintScrollbarArrow(HDC hdc, + int state_id, + int classic_state, + RECT* rect) const { + HANDLE handle = GetThemeHandle(SCROLLBAR); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, SBP_ARROWBTN, state_id, rect, NULL); + + // Draw it manually. + DrawFrameControl(hdc, rect, DFC_SCROLL, classic_state); + return S_OK; +} + +HRESULT NativeTheme::PaintScrollbarTrack( + HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* target_rect, + RECT* align_rect, + skia::PlatformCanvasWin* canvas) const { + HANDLE handle = GetThemeHandle(SCROLLBAR); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, target_rect, NULL); + + // Draw it manually. + const DWORD colorScrollbar = GetSysColor(COLOR_SCROLLBAR); + const DWORD color3DFace = GetSysColor(COLOR_3DFACE); + if ((colorScrollbar != color3DFace) && + (colorScrollbar != GetSysColor(COLOR_WINDOW))) { + FillRect(hdc, target_rect, reinterpret_cast(COLOR_SCROLLBAR + 1)); + } else { + SkPaint paint; + SetCheckerboardShader(&paint, *align_rect); + canvas->drawIRect(skia::RECTToSkIRect(*target_rect), paint); + } + if (classic_state & DFCS_PUSHED) + InvertRect(hdc, target_rect); + return S_OK; +} + +HRESULT NativeTheme::PaintScrollbarThumb(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect) const { + HANDLE handle = GetThemeHandle(SCROLLBAR); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + + // Draw it manually. + if ((part_id == SBP_THUMBBTNHORZ) || (part_id == SBP_THUMBBTNVERT)) + DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT | BF_MIDDLE); + // Classic mode doesn't have a gripper. + return S_OK; +} + +HRESULT NativeTheme::PaintStatusGripper(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect) const { + HANDLE handle = GetThemeHandle(STATUS); + if (handle && draw_theme_) { + // Paint the status bar gripper. There doesn't seem to be a + // standard gripper in Windows for the space between + // scrollbars. This is pretty close, but it's supposed to be + // painted over a status bar. + return draw_theme_(handle, hdc, SP_GRIPPER, 0, rect, NULL); + } + + // Draw a windows classic scrollbar gripper. + DrawFrameControl(hdc, rect, DFC_SCROLL, DFCS_SCROLLSIZEGRIP); + return S_OK; +} + +HRESULT NativeTheme::PaintTabPanelBackground(HDC hdc, RECT* rect) const { + HANDLE handle = GetThemeHandle(TAB); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, TABP_BODY, 0, rect, NULL); + + // Classic just renders a flat color background. + FillRect(hdc, rect, reinterpret_cast(COLOR_3DFACE + 1)); + return S_OK; +} + +HRESULT NativeTheme::PaintTrackbar(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect, + skia::PlatformCanvasWin* canvas) const { + // Make the channel be 4 px thick in the center of the supplied rect. (4 px + // matches what XP does in various menus; GetThemePartSize() doesn't seem to + // return good values here.) + RECT channel_rect = *rect; + const int channel_thickness = 4; + if (part_id == TKP_TRACK) { + channel_rect.top += + ((channel_rect.bottom - channel_rect.top - channel_thickness) / 2); + channel_rect.bottom = channel_rect.top + channel_thickness; + } else if (part_id == TKP_TRACKVERT) { + channel_rect.left += + ((channel_rect.right - channel_rect.left - channel_thickness) / 2); + channel_rect.right = channel_rect.left + channel_thickness; + } // else this isn't actually a channel, so |channel_rect| == |rect|. + + HANDLE handle = GetThemeHandle(TRACKBAR); + if (handle && draw_theme_) + return draw_theme_(handle, hdc, part_id, state_id, &channel_rect, NULL); + + // Classic mode, draw it manually. + if ((part_id == TKP_TRACK) || (part_id == TKP_TRACKVERT)) { + DrawEdge(hdc, &channel_rect, EDGE_SUNKEN, BF_RECT); + } else if (part_id == TKP_THUMBVERT) { + DrawEdge(hdc, rect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE); + } else { + // Split rect into top and bottom pieces. + RECT top_section = *rect; + RECT bottom_section = *rect; + top_section.bottom -= ((bottom_section.right - bottom_section.left) / 2); + bottom_section.top = top_section.bottom; + DrawEdge(hdc, &top_section, EDGE_RAISED, + BF_LEFT | BF_TOP | BF_RIGHT | BF_SOFT | BF_MIDDLE | BF_ADJUST); + + // Split triangular piece into two diagonals. + RECT& left_half = bottom_section; + RECT right_half = bottom_section; + right_half.left += ((bottom_section.right - bottom_section.left) / 2); + left_half.right = right_half.left; + DrawEdge(hdc, &left_half, EDGE_RAISED, + BF_DIAGONAL_ENDTOPLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST); + DrawEdge(hdc, &right_half, EDGE_RAISED, + BF_DIAGONAL_ENDBOTTOMLEFT | BF_SOFT | BF_MIDDLE | BF_ADJUST); + + // If the button is pressed, draw hatching. + if (classic_state & DFCS_PUSHED) { + SkPaint paint; + SetCheckerboardShader(&paint, *rect); + + // Fill all three pieces with the pattern. + canvas->drawIRect(skia::RECTToSkIRect(top_section), paint); + + SkScalar left_triangle_top = SkIntToScalar(left_half.top); + SkScalar left_triangle_right = SkIntToScalar(left_half.right); + SkPath left_triangle; + left_triangle.moveTo(SkIntToScalar(left_half.left), left_triangle_top); + left_triangle.lineTo(left_triangle_right, left_triangle_top); + left_triangle.lineTo(left_triangle_right, + SkIntToScalar(left_half.bottom)); + left_triangle.close(); + canvas->drawPath(left_triangle, paint); + + SkScalar right_triangle_left = SkIntToScalar(right_half.left); + SkScalar right_triangle_top = SkIntToScalar(right_half.top); + SkPath right_triangle; + right_triangle.moveTo(right_triangle_left, right_triangle_top); + right_triangle.lineTo(SkIntToScalar(right_half.right), + right_triangle_top); + right_triangle.lineTo(right_triangle_left, + SkIntToScalar(right_half.bottom)); + right_triangle.close(); + canvas->drawPath(right_triangle, paint); + } + } + return S_OK; +} + +HRESULT NativeTheme::PaintTextField(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect, + COLORREF color, + bool fill_content_area, + bool draw_edges) const { + // TODO(ojan): http://b/1210017 Figure out how to give the ability to + // exclude individual edges from being drawn. + + HANDLE handle = GetThemeHandle(TEXTFIELD); + // TODO(mpcomplete): can we detect if the color is specified by the user, + // and if not, just use the system color? + // CreateSolidBrush() accepts a RGB value but alpha must be 0. + HBRUSH bg_brush = CreateSolidBrush(color); + HRESULT hr; + // DrawThemeBackgroundEx was introduced in XP SP2, so that it's possible + // draw_theme_ex_ is NULL and draw_theme_ is non-null. + if (handle && (draw_theme_ex_ || (draw_theme_ && draw_edges))) { + if (draw_theme_ex_) { + static DTBGOPTS omit_border_options = { + sizeof(DTBGOPTS), + DTBG_OMITBORDER, + {0,0,0,0} + }; + DTBGOPTS* draw_opts = draw_edges ? NULL : &omit_border_options; + hr = draw_theme_ex_(handle, hdc, part_id, state_id, rect, draw_opts); + } else { + hr = draw_theme_(handle, hdc, part_id, state_id, rect, NULL); + } + + // TODO(maruel): Need to be fixed if get_theme_content_rect_ is NULL. + if (fill_content_area && get_theme_content_rect_) { + RECT content_rect; + hr = get_theme_content_rect_(handle, hdc, part_id, state_id, rect, + &content_rect); + FillRect(hdc, &content_rect, bg_brush); + } + } else { + // Draw it manually. + if (draw_edges) + DrawEdge(hdc, rect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); + + if (fill_content_area) { + FillRect(hdc, rect, (classic_state & DFCS_INACTIVE) ? + reinterpret_cast(COLOR_BTNFACE + 1) : bg_brush); + } + hr = S_OK; + } + DeleteObject(bg_brush); + return hr; +} + +bool NativeTheme::IsThemingActive() const { + if (is_theme_active_) + return !!is_theme_active_(); + return false; +} + +HRESULT NativeTheme::GetThemePartSize(ThemeName theme_name, + HDC hdc, + int part_id, + int state_id, + RECT* rect, + int ts, + SIZE* size) const { + HANDLE handle = GetThemeHandle(theme_name); + if (handle && get_theme_part_size_) + return get_theme_part_size_(handle, hdc, part_id, state_id, rect, ts, size); + + return E_NOTIMPL; +} + +HRESULT NativeTheme::GetThemeColor(ThemeName theme, + int part_id, + int state_id, + int prop_id, + SkColor* color) const { + HANDLE handle = GetThemeHandle(theme); + if (handle && get_theme_color_) { + COLORREF color_ref; + if (get_theme_color_(handle, part_id, state_id, prop_id, &color_ref) == + S_OK) { + *color = skia::COLORREFToSkColor(color_ref); + return S_OK; + } + } + return E_NOTIMPL; +} + +SkColor NativeTheme::GetThemeColorWithDefault(ThemeName theme, + int part_id, + int state_id, + int prop_id, + int default_sys_color) const { + SkColor color; + if (GetThemeColor(theme, part_id, state_id, prop_id, &color) != S_OK) + color = skia::COLORREFToSkColor(GetSysColor(default_sys_color)); + return color; +} + +HRESULT NativeTheme::GetThemeInt(ThemeName theme, + int part_id, + int state_id, + int prop_id, + int *value) const { + HANDLE handle = GetThemeHandle(theme); + if (handle && get_theme_int_) + return get_theme_int_(handle, part_id, state_id, prop_id, value); + return E_NOTIMPL; +} + +Size NativeTheme::GetThemeBorderSize(ThemeName theme) const { + // For simplicity use the wildcard state==0, part==0, since it works + // for the cases we currently depend on. + int border; + if (GetThemeInt(theme, 0, 0, TMT_BORDERSIZE, &border) == S_OK) + return Size(border, border); + else + return Size(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE)); +} + + +void NativeTheme::DisableTheming() const { + if (!set_theme_properties_) + return; + set_theme_properties_(0); +} + +HRESULT NativeTheme::PaintFrameControl(HDC hdc, + RECT* rect, + UINT type, + UINT state, + bool is_highlighted) const { + const int width = rect->right - rect->left; + const int height = rect->bottom - rect->top; + + // DrawFrameControl for menu arrow/check wants a monochrome bitmap. + ScopedBitmap mask_bitmap(CreateBitmap(width, height, 1, 1, NULL)); + + if (mask_bitmap == NULL) + return E_OUTOFMEMORY; + + ScopedHDC bitmap_dc(CreateCompatibleDC(NULL)); + HGDIOBJ org_bitmap = SelectObject(bitmap_dc, mask_bitmap); + RECT local_rect = { 0, 0, width, height }; + DrawFrameControl(bitmap_dc, &local_rect, type, state); + + // We're going to use BitBlt with a b&w mask. This results in using the dest + // dc's text color for the black bits in the mask, and the dest dc's + // background color for the white bits in the mask. DrawFrameControl draws the + // check in black, and the background in white. + COLORREF old_bg_color = + SetBkColor(hdc, + GetSysColor(is_highlighted ? COLOR_HIGHLIGHT : COLOR_MENU)); + COLORREF old_text_color = + SetTextColor(hdc, + GetSysColor(is_highlighted ? COLOR_HIGHLIGHTTEXT : + COLOR_MENUTEXT)); + BitBlt(hdc, rect->left, rect->top, width, height, bitmap_dc, 0, 0, SRCCOPY); + SetBkColor(hdc, old_bg_color); + SetTextColor(hdc, old_text_color); + + SelectObject(bitmap_dc, org_bitmap); + + return S_OK; +} + +void NativeTheme::CloseHandles() const +{ + if (!close_theme_) + return; + + for (int i = 0; i < LAST; ++i) { + if (theme_handles_[i]) + close_theme_(theme_handles_[i]); + theme_handles_[i] = NULL; + } +} + +HANDLE NativeTheme::GetThemeHandle(ThemeName theme_name) const +{ + if (!open_theme_ || theme_name < 0 || theme_name >= LAST) + return 0; + + if (theme_handles_[theme_name]) + return theme_handles_[theme_name]; + + // Not found, try to load it. + HANDLE handle = 0; + switch (theme_name) { + case BUTTON: + handle = open_theme_(NULL, L"Button"); + break; + case LIST: + handle = open_theme_(NULL, L"Listview"); + break; + case MENU: + handle = open_theme_(NULL, L"Menu"); + break; + case MENULIST: + handle = open_theme_(NULL, L"Combobox"); + break; + case SCROLLBAR: + handle = open_theme_(NULL, L"Scrollbar"); + break; + case STATUS: + handle = open_theme_(NULL, L"Status"); + break; + case TAB: + handle = open_theme_(NULL, L"Tab"); + break; + case TEXTFIELD: + handle = open_theme_(NULL, L"Edit"); + break; + case TRACKBAR: + handle = open_theme_(NULL, L"Trackbar"); + break; + case WINDOW: + handle = open_theme_(NULL, L"Window"); + break; + default: + NOTREACHED(); + } + theme_handles_[theme_name] = handle; + return handle; +} + +} // namespace gfx diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_theme.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_theme.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_theme.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_theme.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,296 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// A wrapper class for working with custom XP/Vista themes provided in +// uxtheme.dll. This is a singleton class that can be grabbed using +// NativeTheme::instance(). +// For more information on visual style parts and states, see: +// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp + +#ifndef BASE_GFX_NATIVE_THEME_H__ +#define BASE_GFX_NATIVE_THEME_H__ + +#include +#include +#include "base/basictypes.h" +#include "base/gfx/size.h" +#include "skia/include/SkColor.h" + +namespace skia { +class PlatformCanvasWin; +} // namespace skia + +namespace gfx { + +// TODO: Define class member enums to replace part_id and state_id parameters +// that are currently defined in . Afterward, classic_state should +// be removed and class users wouldn't need to include anymore. +// This would enable HOT state on non-themed UI (like when RDP'ing) and would +// simplify usage. +// TODO: This class should probably be changed to be platform independent at +// the same time. +class NativeTheme { + public: + enum ThemeName { + BUTTON, + LIST, + MENU, + MENULIST, + SCROLLBAR, + STATUS, + TAB, + TEXTFIELD, + TRACKBAR, + WINDOW, + LAST + }; + + // This enumeration is used within PaintMenuArrow in order to indicate the + // direction the menu arrow should point to. + enum MenuArrowDirection { + LEFT_POINTING_ARROW, + RIGHT_POINTING_ARROW + }; + + typedef HRESULT (WINAPI* DrawThemeBackgroundPtr)(HANDLE theme, + HDC hdc, + int part_id, + int state_id, + const RECT* rect, + const RECT* clip_rect); + typedef HRESULT (WINAPI* DrawThemeBackgroundExPtr)(HANDLE theme, + HDC hdc, + int part_id, + int state_id, + const RECT* rect, + const DTBGOPTS* opts); + typedef HRESULT (WINAPI* GetThemeColorPtr)(HANDLE hTheme, + int part_id, + int state_id, + int prop_id, + COLORREF* color); + typedef HRESULT (WINAPI* GetThemeContentRectPtr)(HANDLE hTheme, + HDC hdc, + int part_id, + int state_id, + const RECT* rect, + RECT* content_rect); + typedef HRESULT (WINAPI* GetThemePartSizePtr)(HANDLE hTheme, + HDC hdc, + int part_id, + int state_id, + RECT* rect, + int ts, + SIZE* size); + typedef HANDLE (WINAPI* OpenThemeDataPtr)(HWND window, + LPCWSTR class_list); + typedef HRESULT (WINAPI* CloseThemeDataPtr)(HANDLE theme); + + typedef void (WINAPI* SetThemeAppPropertiesPtr) (DWORD flags); + typedef BOOL (WINAPI* IsThemeActivePtr)(); + typedef HRESULT (WINAPI* GetThemeIntPtr)(HANDLE hTheme, + int part_id, + int state_id, + int prop_id, + int *value); + + HRESULT PaintButton(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect) const; + + HRESULT PaintDialogBackground(HDC dc, bool active, RECT* rect) const; + + HRESULT PaintListBackground(HDC dc, bool enabled, RECT* rect) const; + + // |arrow_direction| determines whether the arrow is pointing to the left or + // to the right. In RTL locales, sub-menus open from right to left and + // therefore the menu arrow should point to the left and not to the right. + HRESULT PaintMenuArrow(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + RECT* rect, + MenuArrowDirection arrow_direction, + bool is_highlighted) const; + + HRESULT PaintMenuBackground(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + RECT* rect) const; + + HRESULT PaintMenuCheck(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + RECT* rect, + bool is_highlighted) const; + + HRESULT PaintMenuCheckBackground(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + RECT* rect) const; + + HRESULT PaintMenuGutter(HDC hdc, + int part_id, + int state_id, + RECT* rect) const; + + HRESULT PaintMenuItemBackground(ThemeName theme, + HDC hdc, + int part_id, + int state_id, + bool selected, + RECT* rect) const; + + HRESULT PaintMenuList(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect) const; + + HRESULT PaintMenuSeparator(HDC hdc, + int part_id, + int state_id, + RECT* rect) const; + + // Paints a scrollbar arrow. |classic_state| should have the appropriate + // classic part number ORed in already. + HRESULT PaintScrollbarArrow(HDC hdc, + int state_id, + int classic_state, + RECT* rect) const; + + // Paints a scrollbar track section. |align_rect| is only used in classic + // mode, and makes sure the checkerboard pattern in |target_rect| is aligned + // with one presumed to be in |align_rect|. + HRESULT PaintScrollbarTrack(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* target_rect, + RECT* align_rect, + skia::PlatformCanvasWin* canvas) const; + + // Paints a scrollbar thumb or gripper. + HRESULT PaintScrollbarThumb(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect) const; + + HRESULT PaintStatusGripper(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect) const; + + HRESULT PaintTabPanelBackground(HDC dc, RECT* rect) const; + + HRESULT PaintTextField(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect, + COLORREF color, + bool fill_content_area, + bool draw_edges) const; + + HRESULT PaintTrackbar(HDC hdc, + int part_id, + int state_id, + int classic_state, + RECT* rect, + skia::PlatformCanvasWin* canvas) const; + + bool IsThemingActive() const; + + HRESULT GetThemePartSize(ThemeName themeName, + HDC hdc, + int part_id, + int state_id, + RECT* rect, + int ts, + SIZE* size) const; + + HRESULT GetThemeColor(ThemeName theme, + int part_id, + int state_id, + int prop_id, + SkColor* color) const; + + // Get the theme color if theming is enabled. If theming is unsupported + // for this part, use Win32's GetSysColor to find the color specified + // by default_sys_color. + SkColor GetThemeColorWithDefault(ThemeName theme, + int part_id, + int state_id, + int prop_id, + int default_sys_color) const; + + HRESULT GetThemeInt(ThemeName theme, + int part_id, + int state_id, + int prop_id, + int *result) const; + + // Get the thickness of the border associated with the specified theme, + // defaulting to GetSystemMetrics edge size if themes are disabled. + // In Classic Windows, borders are typically 2px; on XP+, they are 1px. + Size GetThemeBorderSize(ThemeName theme) const; + + // Disables all theming for top-level windows in the entire process, from + // when this method is called until the process exits. All the other + // methods in this class will continue to work, but their output will ignore + // the user's theme. This is meant for use when running tests that require + // consistent visual results. + void DisableTheming() const; + + // Closes cached theme handles so we can unload the DLL or update our UI + // for a theme change. + void CloseHandles() const; + + // Gets our singleton instance. + static const NativeTheme* instance(); + + private: + NativeTheme(); + ~NativeTheme(); + + HRESULT PaintFrameControl(HDC hdc, + RECT* rect, + UINT type, + UINT state, + bool is_highlighted) const; + + // Returns a handle to the theme data. + HANDLE GetThemeHandle(ThemeName theme_name) const; + + // Function pointers into uxtheme.dll. + DrawThemeBackgroundPtr draw_theme_; + DrawThemeBackgroundExPtr draw_theme_ex_; + GetThemeColorPtr get_theme_color_; + GetThemeContentRectPtr get_theme_content_rect_; + GetThemePartSizePtr get_theme_part_size_; + OpenThemeDataPtr open_theme_; + CloseThemeDataPtr close_theme_; + SetThemeAppPropertiesPtr set_theme_properties_; + IsThemeActivePtr is_theme_active_; + GetThemeIntPtr get_theme_int_; + + // Handle to uxtheme.dll. + HMODULE theme_dll_; + + // A cache of open theme handles. + mutable HANDLE theme_handles_[LAST]; + + DISALLOW_EVIL_CONSTRUCTORS(NativeTheme); +}; + +} // namespace gfx + +#endif // BASE_GFX_NATIVE_THEME_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_theme_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_theme_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_theme_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_theme_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,11 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/gfx/native_theme.h" + +#include "testing/gtest/include/gtest/gtest.h" + +TEST(NativeThemeTest, Init) { + ASSERT_TRUE(gfx::NativeTheme::instance() != NULL); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_widget_types_gtk.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_widget_types_gtk.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_widget_types_gtk.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_widget_types_gtk.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,16 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/gfx/native_widget_types.h" + +#include "base/gfx/gtk_native_view_id_manager.h" +#include "base/logging.h" + +namespace gfx { + +NativeViewId IdFromNativeView(NativeView view) { + return Singleton()->GetIdForWidget(view); +} + +} // namespace gfx diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_widget_types.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_widget_types.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_widget_types.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/native_widget_types.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,112 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_GFX_NATIVE_WIDGET_TYPES_H_ +#define BASE_GFX_NATIVE_WIDGET_TYPES_H_ + +#include "base/basictypes.h" +#include "build/build_config.h" + +// This file provides cross platform typedefs for native widget types. +// NativeWindow: this is a handle to a native, top-level window +// NativeView: this is a handle to a native UI element. It may be the +// same type as a NativeWindow on some platforms. +// NativeViewId: Often, in our cross process model, we need to pass around a +// reference to a "window". This reference will, say, be echoed back from a +// renderer to the browser when it wishes to query it's size. On Windows, a +// HWND can be used for this. On other platforms, we may wish to pass +// around X window ids, or maybe abstract identifiers. +// +// As a rule of thumb - if you're in the renderer, you should be dealing +// with NativeViewIds. This should remind you that you shouldn't be doing +// direct operations on platform widgets from the renderer process. +// +// If you're in the browser, you're probably dealing with NativeViews, +// unless you're in the IPC layer, which will be translating between +// NativeViewIds from the renderer and NativeViews. +// +// NativeEditView: a handle to a native edit-box. The Mac folks wanted this +// specific typedef. +// +// The name 'View' here meshes with OS X where the UI elements are called +// 'views' and with our Chrome UI code where the elements are also called +// 'views'. + +#if defined(OS_WIN) +#include +#elif defined(OS_MACOSX) +struct CGContext; +#ifdef __OBJC__ +@class NSView; +@class NSWindow; +@class NSTextField; +#else +class NSView; +class NSWindow; +class NSTextField; +#endif // __OBJC__ +#elif defined(OS_LINUX) +typedef struct _GtkWidget GtkWidget; +typedef struct _GtkWindow GtkWindow; +typedef struct _cairo_surface cairo_surface_t; +#endif + +namespace gfx { + +#if defined(OS_WIN) +typedef HWND NativeView; +typedef HWND NativeWindow; +typedef HWND NativeEditView; +typedef HDC NativeDrawingContext; +#elif defined(OS_MACOSX) +typedef NSView* NativeView; +typedef NSWindow* NativeWindow; +typedef NSTextField* NativeEditView; +typedef CGContext* NativeDrawingContext; +#elif defined(OS_LINUX) +typedef GtkWidget* NativeView; +typedef GtkWindow* NativeWindow; +typedef GtkWidget* NativeEditView; +typedef cairo_surface_t* NativeDrawingContext; +#endif + +// Note: for test_shell we're packing a pointer into the NativeViewId. So, if +// you make it a type which is smaller than a pointer, you have to fix +// test_shell. +// +// See comment at the top of the file for usage. +typedef intptr_t NativeViewId; + +// Convert a NativeViewId to a NativeView. +// On Windows, these are both HWNDS so it's just a cast. +// On Mac, for now, we pass the NSView pointer into the renderer +// On Linux we use an opaque id +#if defined(OS_WIN) || defined(OS_MACOSX) +static inline NativeView NativeViewFromId(NativeViewId id) { + return reinterpret_cast(id); +} +#elif defined(OS_LINUX) +// A NativeView on Linux is a GtkWidget*. However, we can't go directly from an +// X window ID to a GtkWidget. Thus, functions which handle NativeViewIds from +// the renderer have to use Xlib. This is fine since these functions are +// generally performed on the BACKGROUND_X thread which can't use GTK anyway. + +#define NativeViewFromId(x) NATIVE_VIEW_FROM_ID_NOT_AVAILIBLE_ON_LINUX + +#endif // defined(OS_LINUX) + +// Convert a NativeView to a NativeViewId. See the comments above +// NativeViewFromId. +#if defined(OS_WIN) || defined(OS_MACOSX) +static inline NativeViewId IdFromNativeView(NativeView view) { + return reinterpret_cast(view); +} +#elif defined(OS_LINUX) +// Not inlined because it involves pulling too many headers. +NativeViewId IdFromNativeView(NativeView view); +#endif // defined(OS_LINUX) + +} // namespace gfx + +#endif // BASE_GFX_NATIVE_WIDGET_TYPES_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_canvas.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_canvas.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_canvas.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_canvas.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,7 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// TODO(brettw) this file should be removed and the includes changed to this +// new location. +#include "skia/ext/platform_canvas.h" diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_canvas_linux.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_canvas_linux.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_canvas_linux.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_canvas_linux.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,12 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_GFX_PLATFORM_CANVAS_LINUX_H_ +#define BASE_GFX_PLATFORM_CANVAS_LINUX_H_ + +// TODO(brettw) this file should be removed and the includes changed to this +// new location. +#include "skia/ext/platform_canvas_linux.h" + +#endif // BASE_GFX_PLATFORM_CANVAS_LINUX_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_canvas_mac.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_canvas_mac.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_canvas_mac.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_canvas_mac.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,12 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_GFX_PLATFORM_CANVAS_MAC_H__ +#define BASE_GFX_PLATFORM_CANVAS_MAC_H__ + +// TODO(brettw) this file should be removed and the includes changed to this +// new location. +#include "skia/ext/platform_canvas_mac.h" + +#endif // BASE_GFX_PLATFORM_CANVAS_MAC_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_device_linux.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_device_linux.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_device_linux.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_device_linux.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,12 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_GFX_PLATFORM_DEVICE_LINUX_H_ +#define BASE_GFX_PLATFORM_DEVICE_LINUX_H_ + +// TODO(brettw) this file should be removed and the includes changed to this +// new location. +#include "skia/ext/platform_device_linux.h" + +#endif // BASE_GFX_PLATFORM_DEVICE_LINUX_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_device_mac.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_device_mac.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_device_mac.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/platform_device_mac.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,12 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_GFX_PLATFORM_DEVICE_MAC_H__ +#define BASE_GFX_PLATFORM_DEVICE_MAC_H__ + +// TODO(brettw) this file should be removed and the includes changed to this +// new location. +#include "skia/ext/platform_device_mac.h" + +#endif // BASE_GFX_PLATFORM_DEVICE_MAC_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_codec_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_codec_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_codec_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_codec_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,202 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/gfx/png_encoder.h" +#include "base/gfx/png_decoder.h" +#include "testing/gtest/include/gtest/gtest.h" + +static void MakeRGBImage(int w, int h, std::vector* dat) { + dat->resize(w * h * 3); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + unsigned char* org_px = &(*dat)[(y * w + x) * 3]; + org_px[0] = x * 3; // r + org_px[1] = x * 3 + 1; // g + org_px[2] = x * 3 + 2; // b + } + } +} + +// Set use_transparency to write data into the alpha channel, otherwise it will +// be filled with 0xff. With the alpha channel stripped, this should yield the +// same image as MakeRGBImage above, so the code below can make reference +// images for conversion testing. +static void MakeRGBAImage(int w, int h, bool use_transparency, + std::vector* dat) { + dat->resize(w * h * 4); + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + unsigned char* org_px = &(*dat)[(y * w + x) * 4]; + org_px[0] = x * 3; // r + org_px[1] = x * 3 + 1; // g + org_px[2] = x * 3 + 2; // b + if (use_transparency) + org_px[3] = x*3 + 3; // a + else + org_px[3] = 0xFF; // a (opaque) + } + } +} + +TEST(PNGCodec, EncodeDecodeRGB) { + const int w = 20, h = 20; + + // create an image with known values + std::vector original; + MakeRGBImage(w, h, &original); + + // encode + std::vector encoded; + EXPECT_TRUE(PNGEncoder::Encode(&original[0], PNGEncoder::FORMAT_RGB, w, h, + w * 3, false, &encoded)); + + // decode, it should have the same size as the original + std::vector decoded; + int outw, outh; + EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(), + PNGDecoder::FORMAT_RGB, &decoded, + &outw, &outh)); + ASSERT_EQ(w, outw); + ASSERT_EQ(h, outh); + ASSERT_EQ(original.size(), decoded.size()); + + // Images must be equal + ASSERT_TRUE(original == decoded); +} + +TEST(PNGCodec, EncodeDecodeRGBA) { + const int w = 20, h = 20; + + // create an image with known values, a must be opaque because it will be + // lost during encoding + std::vector original; + MakeRGBAImage(w, h, true, &original); + + // encode + std::vector encoded; + EXPECT_TRUE(PNGEncoder::Encode(&original[0], PNGEncoder::FORMAT_RGBA, w, h, + w * 4, false, &encoded)); + + // decode, it should have the same size as the original + std::vector decoded; + int outw, outh; + EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(), + PNGDecoder::FORMAT_RGBA, &decoded, + &outw, &outh)); + ASSERT_EQ(w, outw); + ASSERT_EQ(h, outh); + ASSERT_EQ(original.size(), decoded.size()); + + // Images must be exactly equal + ASSERT_TRUE(original == decoded); +} + +// Test that corrupted data decompression causes failures. +TEST(PNGCodec, DecodeCorrupted) { + int w = 20, h = 20; + + // Make some random data (an uncompressed image). + std::vector original; + MakeRGBImage(w, h, &original); + + // It should fail when given non-JPEG compressed data. + std::vector output; + int outw, outh; + EXPECT_FALSE(PNGDecoder::Decode(&original[0], original.size(), + PNGDecoder::FORMAT_RGB, &output, + &outw, &outh)); + + // Make some compressed data. + std::vector compressed; + EXPECT_TRUE(PNGEncoder::Encode(&original[0], PNGEncoder::FORMAT_RGB, w, h, + w * 3, false, &compressed)); + + // Try decompressing a truncated version. + EXPECT_FALSE(PNGDecoder::Decode(&compressed[0], compressed.size() / 2, + PNGDecoder::FORMAT_RGB, &output, + &outw, &outh)); + + // Corrupt it and try decompressing that. + for (int i = 10; i < 30; i++) + compressed[i] = i; + EXPECT_FALSE(PNGDecoder::Decode(&compressed[0], compressed.size(), + PNGDecoder::FORMAT_RGB, &output, + &outw, &outh)); +} + +TEST(PNGCodec, EncodeDecodeBGRA) { + const int w = 20, h = 20; + + // Create an image with known values, alpha must be opaque because it will be + // lost during encoding. + std::vector original; + MakeRGBAImage(w, h, true, &original); + + // Encode. + std::vector encoded; + EXPECT_TRUE(PNGEncoder::Encode(&original[0], PNGEncoder::FORMAT_BGRA, w, h, + w * 4, false, &encoded)); + + // Decode, it should have the same size as the original. + std::vector decoded; + int outw, outh; + EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(), + PNGDecoder::FORMAT_BGRA, &decoded, + &outw, &outh)); + ASSERT_EQ(w, outw); + ASSERT_EQ(h, outh); + ASSERT_EQ(original.size(), decoded.size()); + + // Images must be exactly equal. + ASSERT_TRUE(original == decoded); +} + +TEST(PNGCodec, StripAddAlpha) { + const int w = 20, h = 20; + + // These should be the same except one has a 0xff alpha channel. + std::vector original_rgb; + MakeRGBImage(w, h, &original_rgb); + std::vector original_rgba; + MakeRGBAImage(w, h, false, &original_rgba); + + // Encode RGBA data as RGB. + std::vector encoded; + EXPECT_TRUE(PNGEncoder::Encode(&original_rgba[0], + PNGEncoder::FORMAT_RGBA, + w, h, + w * 4, true, &encoded)); + + // Decode the RGB to RGBA. + std::vector decoded; + int outw, outh; + EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(), + PNGDecoder::FORMAT_RGBA, &decoded, + &outw, &outh)); + + // Decoded and reference should be the same (opaque alpha). + ASSERT_EQ(w, outw); + ASSERT_EQ(h, outh); + ASSERT_EQ(original_rgba.size(), decoded.size()); + ASSERT_TRUE(original_rgba == decoded); + + // Encode RGBA to RGBA. + EXPECT_TRUE(PNGEncoder::Encode(&original_rgba[0], + PNGEncoder::FORMAT_RGBA, + w, h, + w * 4, false, &encoded)); + + // Decode the RGBA to RGB. + EXPECT_TRUE(PNGDecoder::Decode(&encoded[0], encoded.size(), + PNGDecoder::FORMAT_RGB, &decoded, + &outw, &outh)); + + // It should be the same as our non-alpha-channel reference. + ASSERT_EQ(w, outw); + ASSERT_EQ(h, outh); + ASSERT_EQ(original_rgb.size(), decoded.size()); + ASSERT_TRUE(original_rgb == decoded); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_decoder.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_decoder.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_decoder.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_decoder.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,354 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/gfx/png_decoder.h" + +#include "base/logging.h" +#include "skia/include/SkBitmap.h" + +extern "C" { +#include "third_party/libpng/png.h" +} + +namespace { + +// Converts BGRA->RGBA and RGBA->BGRA. +void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width, + unsigned char* output) { + for (int x = 0; x < pixel_width; x++) { + const unsigned char* pixel_in = &input[x * 4]; + unsigned char* pixel_out = &output[x * 4]; + pixel_out[0] = pixel_in[2]; + pixel_out[1] = pixel_in[1]; + pixel_out[2] = pixel_in[0]; + pixel_out[3] = pixel_in[3]; + } +} + +void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width, + unsigned char* rgb) { + for (int x = 0; x < pixel_width; x++) { + const unsigned char* pixel_in = &rgba[x * 4]; + unsigned char* pixel_out = &rgb[x * 3]; + pixel_out[0] = pixel_in[0]; + pixel_out[1] = pixel_in[1]; + pixel_out[2] = pixel_in[2]; + } +} + +} // namespace + +// Decoder -------------------------------------------------------------------- +// +// This code is based on WebKit libpng interface (PNGImageDecoder), which is +// in turn based on the Mozilla png decoder. + +namespace { + +// Gamma constants: We assume we're on Windows which uses a gamma of 2.2. +const double kMaxGamma = 21474.83; // Maximum gamma accepted by png library. +const double kDefaultGamma = 2.2; +const double kInverseGamma = 1.0 / kDefaultGamma; + +// Maximum pixel dimension we'll try to decode. +const png_uint_32 kMaxSize = 4096; + +class PngDecoderState { + public: + PngDecoderState(PNGDecoder::ColorFormat ofmt, std::vector* o) + : output_format(ofmt), + output_channels(0), + output(o), + row_converter(NULL), + width(0), + height(0), + done(false) { + } + + PNGDecoder::ColorFormat output_format; + int output_channels; + + std::vector* output; + + // Called to convert a row from the library to the correct output format. + // When NULL, no conversion is necessary. + void (*row_converter)(const unsigned char* in, int w, unsigned char* out); + + // Size of the image, set in the info callback. + int width; + int height; + + // Set to true when we've found the end of the data. + bool done; + + private: + DISALLOW_EVIL_CONSTRUCTORS(PngDecoderState); +}; + +void ConvertRGBtoRGBA(const unsigned char* rgb, int pixel_width, + unsigned char* rgba) { + for (int x = 0; x < pixel_width; x++) { + const unsigned char* pixel_in = &rgb[x * 3]; + unsigned char* pixel_out = &rgba[x * 4]; + pixel_out[0] = pixel_in[0]; + pixel_out[1] = pixel_in[1]; + pixel_out[2] = pixel_in[2]; + pixel_out[3] = 0xff; + } +} + +void ConvertRGBtoBGRA(const unsigned char* rgb, int pixel_width, + unsigned char* bgra) { + for (int x = 0; x < pixel_width; x++) { + const unsigned char* pixel_in = &rgb[x * 3]; + unsigned char* pixel_out = &bgra[x * 4]; + pixel_out[0] = pixel_in[2]; + pixel_out[1] = pixel_in[1]; + pixel_out[2] = pixel_in[0]; + pixel_out[3] = 0xff; + } +} + +// Called when the png header has been read. This code is based on the WebKit +// PNGImageDecoder +void DecodeInfoCallback(png_struct* png_ptr, png_info* info_ptr) { + PngDecoderState* state = static_cast( + png_get_progressive_ptr(png_ptr)); + + int bit_depth, color_type, interlace_type, compression_type; + int filter_type, channels; + png_uint_32 w, h; + png_get_IHDR(png_ptr, info_ptr, &w, &h, &bit_depth, &color_type, + &interlace_type, &compression_type, &filter_type); + + // Bounds check. When the image is unreasonably big, we'll error out and + // end up back at the setjmp call when we set up decoding. + if (w > kMaxSize || h > kMaxSize) + longjmp(png_ptr->jmpbuf, 1); + state->width = static_cast(w); + state->height = static_cast(h); + + // Expand to ensure we use 24-bit for RGB and 32-bit for RGBA. + if (color_type == PNG_COLOR_TYPE_PALETTE || + (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)) + png_set_expand(png_ptr); + + // Transparency for paletted images. + if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) + png_set_expand(png_ptr); + + // Convert 16-bit to 8-bit. + if (bit_depth == 16) + png_set_strip_16(png_ptr); + + // Expand grayscale to RGB. + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png_ptr); + + // Deal with gamma and keep it under our control. + double gamma; + if (png_get_gAMA(png_ptr, info_ptr, &gamma)) { + if (gamma <= 0.0 || gamma > kMaxGamma) { + gamma = kInverseGamma; + png_set_gAMA(png_ptr, info_ptr, gamma); + } + png_set_gamma(png_ptr, kDefaultGamma, gamma); + } else { + png_set_gamma(png_ptr, kDefaultGamma, kInverseGamma); + } + + // Tell libpng to send us rows for interlaced pngs. + if (interlace_type == PNG_INTERLACE_ADAM7) + png_set_interlace_handling(png_ptr); + + // Update our info now + png_read_update_info(png_ptr, info_ptr); + channels = png_get_channels(png_ptr, info_ptr); + + // Pick our row format converter necessary for this data. + if (channels == 3) { + switch (state->output_format) { + case PNGDecoder::FORMAT_RGB: + state->row_converter = NULL; // no conversion necessary + state->output_channels = 3; + break; + case PNGDecoder::FORMAT_RGBA: + state->row_converter = &ConvertRGBtoRGBA; + state->output_channels = 4; + break; + case PNGDecoder::FORMAT_BGRA: + state->row_converter = &ConvertRGBtoBGRA; + state->output_channels = 4; + break; + default: + NOTREACHED() << "Unknown output format"; + break; + } + } else if (channels == 4) { + switch (state->output_format) { + case PNGDecoder::FORMAT_RGB: + state->row_converter = &ConvertRGBAtoRGB; + state->output_channels = 3; + break; + case PNGDecoder::FORMAT_RGBA: + state->row_converter = NULL; // no conversion necessary + state->output_channels = 4; + break; + case PNGDecoder::FORMAT_BGRA: + state->row_converter = &ConvertBetweenBGRAandRGBA; + state->output_channels = 4; + break; + default: + NOTREACHED() << "Unknown output format"; + break; + } + } else { + NOTREACHED() << "Unknown input channels"; + longjmp(png_ptr->jmpbuf, 1); + } + + state->output->resize(state->width * state->output_channels * state->height); +} + +void DecodeRowCallback(png_struct* png_ptr, png_byte* new_row, + png_uint_32 row_num, int pass) { + PngDecoderState* state = static_cast( + png_get_progressive_ptr(png_ptr)); + + DCHECK(pass == 0) << "We didn't turn on interlace handling, but libpng is " + "giving us interlaced data."; + if (static_cast(row_num) > state->height) { + NOTREACHED() << "Invalid row"; + return; + } + + unsigned char* dest = &(*state->output)[ + state->width * state->output_channels * row_num]; + if (state->row_converter) + state->row_converter(new_row, state->width, dest); + else + memcpy(dest, new_row, state->width * state->output_channels); +} + +void DecodeEndCallback(png_struct* png_ptr, png_info* info) { + PngDecoderState* state = static_cast( + png_get_progressive_ptr(png_ptr)); + + // Mark the image as complete, this will tell the Decode function that we + // have successfully found the end of the data. + state->done = true; +} + +// Automatically destroys the given read structs on destruction to make +// cleanup and error handling code cleaner. +class PngReadStructDestroyer { + public: + PngReadStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) { + } + ~PngReadStructDestroyer() { + png_destroy_read_struct(ps_, pi_, NULL); + } + private: + png_struct** ps_; + png_info** pi_; +}; + +} // namespace + +// static +bool PNGDecoder::Decode(const unsigned char* input, size_t input_size, + ColorFormat format, std::vector* output, + int* w, int* h) { + if (input_size < 8) + return false; // Input data too small to be a png + + // Have libpng check the signature, it likes the first 8 bytes. + if (png_sig_cmp(const_cast(input), 0, 8) != 0) + return false; + + png_struct* png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, + png_voidp_NULL, + png_error_ptr_NULL, + png_error_ptr_NULL); + if (!png_ptr) + return false; + + png_info* info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_read_struct(&png_ptr, NULL, NULL); + return false; + } + + PngReadStructDestroyer destroyer(&png_ptr, &info_ptr); + if (setjmp(png_jmpbuf(png_ptr))) { + // The destroyer will ensure that the structures are cleaned up in this + // case, even though we may get here as a jump from random parts of the + // PNG library called below. + return false; + } + + PngDecoderState state(format, output); + + png_set_progressive_read_fn(png_ptr, &state, &DecodeInfoCallback, + &DecodeRowCallback, &DecodeEndCallback); + png_process_data(png_ptr, + info_ptr, + const_cast(input), + input_size); + + if (!state.done) { + // Fed it all the data but the library didn't think we got all the data, so + // this file must be truncated. + output->clear(); + return false; + } + + *w = state.width; + *h = state.height; + return true; +} + +// static +bool PNGDecoder::Decode(const std::vector* data, + SkBitmap* bitmap) { + DCHECK(bitmap); + if (!data || data->empty()) + return false; + int width, height; + std::vector decoded_data; + if (PNGDecoder::Decode(&data->front(), data->size(), PNGDecoder::FORMAT_BGRA, + &decoded_data, &width, &height)) { + bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height); + bitmap->allocPixels(); + memcpy(bitmap->getPixels(), &decoded_data.front(), width * height * 4); + return true; + } + return false; +} + +//static +SkBitmap* PNGDecoder::CreateSkBitmapFromBGRAFormat( + std::vector& bgra, int width, int height) { + SkBitmap* bitmap = new SkBitmap(); + bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height); + bitmap->allocPixels(); + + bool opaque = false; + unsigned char* bitmap_data = + reinterpret_cast(bitmap->getAddr32(0, 0)); + for (int i = width * height * 4 - 4; i >= 0; i -= 4) { + unsigned char alpha = bgra[i + 3]; + if (!opaque && alpha != 255) { + opaque = false; + } + bitmap_data[i + 3] = alpha; + bitmap_data[i] = (bgra[i] * alpha) >> 8; + bitmap_data[i + 1] = (bgra[i + 1] * alpha) >> 8; + bitmap_data[i + 2] = (bgra[i + 2] * alpha) >> 8; + } + + bitmap->setIsOpaque(opaque); + return bitmap; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_decoder.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_decoder.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_decoder.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_decoder.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,63 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_GFX_PNG_DECODER_H_ +#define BASE_GFX_PNG_DECODER_H_ + +#include + +#include "base/basictypes.h" + +class SkBitmap; + +// Interface for decoding PNG data. This is a wrapper around libpng, +// which has an inconvenient interface for callers. This is currently designed +// for use in tests only (where we control the files), so the handling isn't as +// robust as would be required for a browser (see Decode() for more). WebKit +// has its own more complicated PNG decoder which handles, among other things, +// partially downloaded data. +class PNGDecoder { + public: + enum ColorFormat { + // 3 bytes per pixel (packed), in RGB order regardless of endianness. + // This is the native JPEG format. + FORMAT_RGB, + + // 4 bytes per pixel, in RGBA order in memory regardless of endianness. + FORMAT_RGBA, + + // 4 bytes per pixel, in BGRA order in memory regardless of endianness. + // This is the default Windows DIB order. + FORMAT_BGRA + }; + + // Decodes the PNG data contained in input of length input_size. The + // decoded data will be placed in *output with the dimensions in *w and *h + // on success (returns true). This data will be written in the 'format' + // format. On failure, the values of these output variables are undefined. + // + // This function may not support all PNG types, and it hasn't been tested + // with a large number of images, so assume a new format may not work. It's + // really designed to be able to read in something written by Encode() above. + static bool Decode(const unsigned char* input, size_t input_size, + ColorFormat format, std::vector* output, + int* w, int* h); + + // A convenience function for decoding PNGs as previously encoded by the PNG + // encoder. Chrome encodes png in the format PNGDecoder::FORMAT_BGRA. + // + // Returns true if data is non-null and can be decoded as a png, false + // otherwise. + static bool Decode(const std::vector* data, SkBitmap* icon); + + // Create a SkBitmap from a decoded BGRA DIB. The caller owns the returned + // SkBitmap. + static SkBitmap* CreateSkBitmapFromBGRAFormat( + std::vector& bgra, int width, int height); + + private: + DISALLOW_COPY_AND_ASSIGN(PNGDecoder); +}; + +#endif // BASE_GFX_PNG_DECODER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_encoder.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_encoder.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_encoder.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_encoder.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,205 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "base/gfx/png_encoder.h" +#include "base/logging.h" +#include "skia/include/SkBitmap.h" + +extern "C" { +#include "third_party/libpng/png.h" +} + +namespace { + +// Converts BGRA->RGBA and RGBA->BGRA. +void ConvertBetweenBGRAandRGBA(const unsigned char* input, int pixel_width, + unsigned char* output) { + for (int x = 0; x < pixel_width; x++) { + const unsigned char* pixel_in = &input[x * 4]; + unsigned char* pixel_out = &output[x * 4]; + pixel_out[0] = pixel_in[2]; + pixel_out[1] = pixel_in[1]; + pixel_out[2] = pixel_in[0]; + pixel_out[3] = pixel_in[3]; + } +} + +void ConvertRGBAtoRGB(const unsigned char* rgba, int pixel_width, + unsigned char* rgb) { + for (int x = 0; x < pixel_width; x++) { + const unsigned char* pixel_in = &rgba[x * 4]; + unsigned char* pixel_out = &rgb[x * 3]; + pixel_out[0] = pixel_in[0]; + pixel_out[1] = pixel_in[1]; + pixel_out[2] = pixel_in[2]; + } +} + +} // namespace + +// Encoder -------------------------------------------------------------------- +// +// This section of the code is based on nsPNGEncoder.cpp in Mozilla +// (Copyright 2005 Google Inc.) + +namespace { + +// Passed around as the io_ptr in the png structs so our callbacks know where +// to write data. +struct PngEncoderState { + PngEncoderState(std::vector* o) : out(o) {} + std::vector* out; +}; + +// Called by libpng to flush its internal buffer to ours. +void EncoderWriteCallback(png_structp png, png_bytep data, png_size_t size) { + PngEncoderState* state = static_cast(png_get_io_ptr(png)); + DCHECK(state->out); + + size_t old_size = state->out->size(); + state->out->resize(old_size + size); + memcpy(&(*state->out)[old_size], data, size); +} + +void ConvertBGRAtoRGB(const unsigned char* bgra, int pixel_width, + unsigned char* rgb) { + for (int x = 0; x < pixel_width; x++) { + const unsigned char* pixel_in = &bgra[x * 4]; + unsigned char* pixel_out = &rgb[x * 3]; + pixel_out[0] = pixel_in[2]; + pixel_out[1] = pixel_in[1]; + pixel_out[2] = pixel_in[0]; + } +} + +// Automatically destroys the given write structs on destruction to make +// cleanup and error handling code cleaner. +class PngWriteStructDestroyer { + public: + PngWriteStructDestroyer(png_struct** ps, png_info** pi) : ps_(ps), pi_(pi) { + } + ~PngWriteStructDestroyer() { + png_destroy_write_struct(ps_, pi_); + } + private: + png_struct** ps_; + png_info** pi_; + + DISALLOW_EVIL_CONSTRUCTORS(PngWriteStructDestroyer); +}; + +} // namespace + +// static +bool PNGEncoder::Encode(const unsigned char* input, ColorFormat format, + int w, int h, int row_byte_width, + bool discard_transparency, + std::vector* output) { + // Run to convert an input row into the output row format, NULL means no + // conversion is necessary. + void (*converter)(const unsigned char* in, int w, unsigned char* out) = NULL; + + int input_color_components, output_color_components; + int png_output_color_type; + switch (format) { + case FORMAT_RGB: + input_color_components = 3; + output_color_components = 3; + png_output_color_type = PNG_COLOR_TYPE_RGB; + discard_transparency = false; + break; + + case FORMAT_RGBA: + input_color_components = 4; + if (discard_transparency) { + output_color_components = 3; + png_output_color_type = PNG_COLOR_TYPE_RGB; + converter = ConvertRGBAtoRGB; + } else { + output_color_components = 4; + png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; + converter = NULL; + } + break; + + case FORMAT_BGRA: + input_color_components = 4; + if (discard_transparency) { + output_color_components = 3; + png_output_color_type = PNG_COLOR_TYPE_RGB; + converter = ConvertBGRAtoRGB; + } else { + output_color_components = 4; + png_output_color_type = PNG_COLOR_TYPE_RGB_ALPHA; + converter = ConvertBetweenBGRAandRGBA; + } + break; + + default: + NOTREACHED() << "Unknown pixel format"; + return false; + } + + // Row stride should be at least as long as the length of the data. + DCHECK(input_color_components * w <= row_byte_width); + + png_struct* png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, + png_voidp_NULL, + png_error_ptr_NULL, + png_error_ptr_NULL); + if (!png_ptr) + return false; + png_info* info_ptr = png_create_info_struct(png_ptr); + if (!info_ptr) { + png_destroy_write_struct(&png_ptr, NULL); + return false; + } + PngWriteStructDestroyer destroyer(&png_ptr, &info_ptr); + + if (setjmp(png_jmpbuf(png_ptr))) { + // The destroyer will ensure that the structures are cleaned up in this + // case, even though we may get here as a jump from random parts of the + // PNG library called below. + return false; + } + + // Set our callback for libpng to give us the data. + PngEncoderState state(output); + png_set_write_fn(png_ptr, &state, EncoderWriteCallback, NULL); + + png_set_IHDR(png_ptr, info_ptr, w, h, 8, png_output_color_type, + PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, + PNG_FILTER_TYPE_DEFAULT); + png_write_info(png_ptr, info_ptr); + + if (!converter) { + // No conversion needed, give the data directly to libpng. + for (int y = 0; y < h; y ++) + png_write_row(png_ptr, + const_cast(&input[y * row_byte_width])); + } else { + // Needs conversion using a separate buffer. + unsigned char* row = new unsigned char[w * output_color_components]; + for (int y = 0; y < h; y ++) { + converter(&input[y * row_byte_width], w, row); + png_write_row(png_ptr, row); + } + delete[] row; + } + + png_write_end(png_ptr, info_ptr); + return true; +} + +// static +bool PNGEncoder::EncodeBGRASkBitmap(const SkBitmap& input, + bool discard_transparency, + std::vector* output) { + SkAutoLockPixels input_lock(input); + DCHECK(input.empty() || input.bytesPerPixel() == 4); + return Encode(static_cast(input.getPixels()), + PNGEncoder::FORMAT_BGRA, input.width(), input.height(), + input.rowBytes(), discard_transparency, output); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_encoder.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_encoder.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_encoder.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/png_encoder.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,68 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_GFX_PNG_ENCODER_H_ +#define BASE_GFX_PNG_ENCODER_H_ + +#include + +#include "base/basictypes.h" + +class SkBitmap; + +// Interface for encoding PNG data. This is a wrapper around libpng, +// which has an inconvenient interface for callers. This is currently designed +// for use in tests only (where we control the files), so the handling isn't as +// robust as would be required for a browser (see Decode() for more). WebKit +// has its own more complicated PNG decoder which handles, among other things, +// partially downloaded data. +class PNGEncoder { + public: + enum ColorFormat { + // 3 bytes per pixel (packed), in RGB order regardless of endianness. + // This is the native JPEG format. + FORMAT_RGB, + + // 4 bytes per pixel, in RGBA order in memory regardless of endianness. + FORMAT_RGBA, + + // 4 bytes per pixel, in BGRA order in memory regardless of endianness. + // This is the default Windows DIB order. + FORMAT_BGRA + }; + + // Encodes the given raw 'input' data, with each pixel being represented as + // given in 'format'. The encoded PNG data will be written into the supplied + // vector and true will be returned on success. On failure (false), the + // contents of the output buffer are undefined. + // + // When writing alpha values, the input colors are assumed to be post + // multiplied. + // + // w, h: dimensions of the image + // row_byte_width: the width in bytes of each row. This may be greater than + // w * bytes_per_pixel if there is extra padding at the end of each row + // (often, each row is padded to the next machine word). + // discard_transparency: when true, and when the input data format includes + // alpha values, these alpha values will be discarded and only RGB will be + // written to the resulting file. Otherwise, alpha values in the input + // will be preserved. + static bool Encode(const unsigned char* input, ColorFormat format, + int w, int h, int row_byte_width, + bool discard_transparency, + std::vector* output); + + // Call PNGEncoder::Encode on the supplied SkBitmap |input|, which is assumed + // to be BGRA, 32 bits per pixel. The params |discard_transparency| and + // |output| are passed directly to Encode; refer to Encode for more + // information. During the call, an SkAutoLockPixels lock is held on |input|. + static bool EncodeBGRASkBitmap(const SkBitmap& input, + bool discard_transparency, + std::vector* output); + + private: + DISALLOW_COPY_AND_ASSIGN(PNGEncoder); +}; + +#endif // BASE_GFX_PNG_ENCODER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/point.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/point.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/point.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/point.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,44 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/gfx/point.h" + +#if defined(OS_WIN) +#include +#endif + +namespace gfx { + +Point::Point() : x_(0), y_(0) { +} + +Point::Point(int x, int y) : x_(x), y_(y) { +} + +#if defined(OS_WIN) +Point::Point(const POINT& point) : x_(point.x), y_(point.y) { +} + +Point& Point::operator=(const POINT& point) { + x_ = point.x; + y_ = point.y; + return *this; +} + +POINT Point::ToPOINT() const { + POINT p; + p.x = x_; + p.y = y_; + return p; +} +#elif defined(OS_MACOSX) +Point::Point(const CGPoint& point) : x_(point.x), y_(point.y) { +} + +CGPoint Point::ToCGPoint() const { + return CGPointMake(x_, y_); +} +#endif + +} // namespace gfx diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/point.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/point.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/point.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/point.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,77 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_GFX_POINT_H__ +#define BASE_GFX_POINT_H__ + +#include "build/build_config.h" + +#include + +#if defined(OS_WIN) +typedef struct tagPOINT POINT; +#elif defined(OS_MACOSX) +#include +#endif + +namespace gfx { + +// +// A point has an x and y coordinate. +// +class Point { + public: + Point(); + Point(int x, int y); +#if defined(OS_WIN) + explicit Point(const POINT& point); + Point& operator=(const POINT& point); +#elif defined(OS_MACOSX) + explicit Point(const CGPoint& point); +#endif + + ~Point() {} + + int x() const { return x_; } + int y() const { return y_; } + + void SetPoint(int x, int y) { + x_ = x; + y_ = y; + } + + void set_x(int x) { x_ = x; } + void set_y(int y) { y_ = y; } + + void Offset(int delta_x, int delta_y) { + x_ += delta_x; + y_ += delta_y; + } + + bool operator==(const Point& rhs) const { + return x_ == rhs.x_ && y_ == rhs.y_; + } + + bool operator!=(const Point& rhs) const { + return !(*this == rhs); + } + +#if defined(OS_WIN) + POINT ToPOINT() const; +#elif defined(OS_MACOSX) + CGPoint ToCGPoint() const; +#endif + + private: + int x_; + int y_; +}; + +} // namespace gfx + +inline std::ostream& operator<<(std::ostream& out, const gfx::Point& p) { + return out << p.x() << "," << p.y(); +} + +#endif // BASE_GFX_POINT_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/rect.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/rect.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/rect.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/rect.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,226 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/gfx/rect.h" + +#if defined(OS_WIN) +#include +#elif defined(OS_MACOSX) +#include +#elif defined(OS_LINUX) +#include +#endif + +#include "base/logging.h" + +namespace { + +void AdjustAlongAxis(int dst_origin, int dst_size, int* origin, int* size) { + if (*origin < dst_origin) { + *origin = dst_origin; + *size = std::min(dst_size, *size); + } else { + *size = std::min(dst_size, *size); + *origin = std::min(dst_origin + dst_size, *origin + *size) - *size; + } +} + +} // namespace + +namespace gfx { + +Rect::Rect() { +} + +Rect::Rect(int width, int height) { + set_width(width); + set_height(height); +} + +Rect::Rect(int x, int y, int width, int height) + : origin_(x, y) { + set_width(width); + set_height(height); +} + +Rect::Rect(const gfx::Point& origin, const gfx::Size& size) + : origin_(origin), size_(size) { +} + +#if defined(OS_WIN) +Rect::Rect(const RECT& r) + : origin_(r.left, r.top) { + set_width(r.right - r.left); + set_height(r.bottom - r.top); +} + +Rect& Rect::operator=(const RECT& r) { + origin_.SetPoint(r.left, r.top); + set_width(r.right - r.left); + set_height(r.bottom - r.top); + return *this; +} +#elif defined(OS_MACOSX) +Rect::Rect(const CGRect& r) + : origin_(r.origin.x, r.origin.y) { + set_width(r.size.width); + set_height(r.size.height); +} + +Rect& Rect::operator=(const CGRect& r) { + origin_.SetPoint(r.origin.x, r.origin.y); + set_width(r.size.width); + set_height(r.size.height); + return *this; +} +#elif defined(OS_LINUX) +Rect::Rect(const GdkRectangle& r) + : origin_(r.x, r.y) { + set_width(r.width); + set_height(r.height); +} + +Rect& Rect::operator=(const GdkRectangle& r) { + origin_.SetPoint(r.x, r.y); + set_width(r.width); + set_height(r.height); + return *this; +} +#endif + +void Rect::set_width(int width) { + size_.set_width(width); +} +void Rect::set_height(int height) { + size_.set_height(height); +} + +void Rect::SetRect(int x, int y, int width, int height) { + origin_.SetPoint(x, y); + set_width(width); + set_height(height); +} + +void Rect::Inset(int left, int top, int right, int bottom) { + Offset(left, top); + set_width(std::max(width() - left - right, 0)); + set_height(std::max(height() - top - bottom, 0)); +} + +void Rect::Offset(int horizontal, int vertical) { + set_x(x() + horizontal); + set_y(y() + vertical); +} + +bool Rect::operator==(const Rect& other) const { + return origin_ == other.origin_ && size_ == other.size_; +} + +#if defined(OS_WIN) +RECT Rect::ToRECT() const { + RECT r; + r.left = x(); + r.right = right(); + r.top = y(); + r.bottom = bottom(); + return r; +} +#elif defined(OS_LINUX) +GdkRectangle Rect::ToGdkRectangle() const { + GdkRectangle r = {x(), y(), width(), height()}; + return r; +} +#elif defined(OS_MACOSX) +CGRect Rect::ToCGRect() const { + return CGRectMake(x(), y(), width(), height()); +} +#endif + +bool Rect::Contains(int point_x, int point_y) const { + return (point_x >= x()) && (point_x < right()) && + (point_y >= y()) && (point_y < bottom()); +} + +bool Rect::Contains(const Rect& rect) const { + return (rect.x() >= x() && rect.right() <= right() && + rect.y() >= y() && rect.bottom() <= bottom()); +} + +bool Rect::Intersects(const Rect& rect) const { + return !(rect.x() >= right() || rect.right() <= x() || + rect.y() >= bottom() || rect.bottom() <= y()); +} + +Rect Rect::Intersect(const Rect& rect) const { + int rx = std::max(x(), rect.x()); + int ry = std::max(y(), rect.y()); + int rr = std::min(right(), rect.right()); + int rb = std::min(bottom(), rect.bottom()); + + if (rx >= rr || ry >= rb) + rx = ry = rr = rb = 0; // non-intersecting + + return Rect(rx, ry, rr - rx, rb - ry); +} + +Rect Rect::Union(const Rect& rect) const { + // special case empty rects... + if (IsEmpty()) + return rect; + if (rect.IsEmpty()) + return *this; + + int rx = std::min(x(), rect.x()); + int ry = std::min(y(), rect.y()); + int rr = std::max(right(), rect.right()); + int rb = std::max(bottom(), rect.bottom()); + + return Rect(rx, ry, rr - rx, rb - ry); +} + +Rect Rect::Subtract(const Rect& rect) const { + // boundary cases: + if (!Intersects(rect)) + return *this; + if (rect.Contains(*this)) + return Rect(); + + int rx = x(); + int ry = y(); + int rr = right(); + int rb = bottom(); + + if (rect.y() <= y() && rect.bottom() >= bottom()) { + // complete intersection in the y-direction + if (rect.x() <= x()) { + rx = rect.right(); + } else { + rr = rect.x(); + } + } else if (rect.x() <= x() && rect.right() >= right()) { + // complete intersection in the x-direction + if (rect.y() <= y()) { + ry = rect.bottom(); + } else { + rb = rect.y(); + } + } + return Rect(rx, ry, rr - rx, rb - ry); +} + +Rect Rect::AdjustToFit(const Rect& rect) const { + int new_x = x(); + int new_y = y(); + int new_width = width(); + int new_height = height(); + AdjustAlongAxis(rect.x(), rect.width(), &new_x, &new_width); + AdjustAlongAxis(rect.y(), rect.height(), &new_y, &new_height); + return Rect(new_x, new_y, new_width, new_height); +} + +Point Rect::CenterPoint() const { + return Point(x() + (width() + 1) / 2, y() + (height() + 1) / 2); +} + +} // namespace gfx diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/rect.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/rect.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/rect.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/rect.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,162 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines a simple integer rectangle class. The containment semantics +// are array-like; that is, the coordinate (x, y) is considered to be +// contained by the rectangle, but the coordinate (x + width, y) is not. +// The class will happily let you create malformed rectangles (that is, +// rectangles with negative width and/or height), but there will be assertions +// in the operations (such as contain()) to complain in this case. + +#ifndef BASE_GFX_RECT_H__ +#define BASE_GFX_RECT_H__ + +#include + +#include "base/gfx/point.h" +#include "base/gfx/size.h" + +#if defined(OS_WIN) +typedef struct tagRECT RECT; +#elif defined(OS_LINUX) +typedef struct _GdkRectangle GdkRectangle; +#endif + +namespace gfx { + +class Rect { + public: + Rect(); + Rect(int width, int height); + Rect(int x, int y, int width, int height); +#if defined(OS_WIN) + explicit Rect(const RECT& r); +#elif defined(OS_MACOSX) + explicit Rect(const CGRect& r); +#elif defined(OS_LINUX) + explicit Rect(const GdkRectangle& r); +#endif + Rect(const gfx::Point& origin, const gfx::Size& size); + + ~Rect() {} + +#if defined(OS_WIN) + Rect& operator=(const RECT& r); +#elif defined(OS_MACOSX) + Rect& operator=(const CGRect& r); +#elif defined(OS_LINUX) + Rect& operator=(const GdkRectangle& r); +#endif + + int x() const { return origin_.x(); } + void set_x(int x) { origin_.set_x(x); } + + int y() const { return origin_.y(); } + void set_y(int y) { origin_.set_y(y); } + + int width() const { return size_.width(); } + void set_width(int width); + + int height() const { return size_.height(); } + void set_height(int height); + + const gfx::Point& origin() const { return origin_; } + void set_origin(const gfx::Point& origin) { origin_ = origin; } + + const gfx::Size& size() const { return size_; } + + int right() const { return x() + width(); } + int bottom() const { return y() + height(); } + + void SetRect(int x, int y, int width, int height); + + // Shrink the rectangle by a horizontal and vertical distance on all sides. + void Inset(int horizontal, int vertical) { + Inset(horizontal, vertical, horizontal, vertical); + } + + // Shrink the rectangle by the specified amount on each side. + void Inset(int left, int top, int right, int bottom); + + // Move the rectangle by a horizontal and vertical distance. + void Offset(int horizontal, int vertical); + void Offset(const gfx::Point& point) { + Offset(point.x(), point.y()); + } + + // Returns true if the area of the rectangle is zero. + bool IsEmpty() const { return size_.IsEmpty(); } + + bool operator==(const Rect& other) const; + + bool operator!=(const Rect& other) const { + return !(*this == other); + } + +#if defined(OS_WIN) + // Construct an equivalent Win32 RECT object. + RECT ToRECT() const; +#elif defined(OS_LINUX) + GdkRectangle ToGdkRectangle() const; +#elif defined(OS_MACOSX) + // Construct an equivalent CoreGraphics object. + CGRect ToCGRect() const; +#endif + + // Returns true if the point identified by point_x and point_y falls inside + // this rectangle. The point (x, y) is inside the rectangle, but the + // point (x + width, y + height) is not. + bool Contains(int point_x, int point_y) const; + + // Returns true if the specified point is contained by this rectangle. + bool Contains(const gfx::Point& point) const { + return Contains(point.x(), point.y()); + } + + // Returns true if this rectangle contains the specified rectangle. + bool Contains(const Rect& rect) const; + + // Returns true if this rectangle intersects the specified rectangle. + bool Intersects(const Rect& rect) const; + + // Computes the intersection of this rectangle with the given rectangle. + Rect Intersect(const Rect& rect) const; + + // Computes the union of this rectangle with the given rectangle. The union + // is the smallest rectangle containing both rectangles. + Rect Union(const Rect& rect) const; + + // Computes the rectangle resulting from subtracting |rect| from |this|. If + // |rect| does not intersect completely in either the x- or y-direction, then + // |*this| is returned. If |rect| contains |this|, then an empty Rect is + // returned. + Rect Subtract(const Rect& rect) const; + + // Returns true if this rectangle equals that of the supplied rectangle. + bool Equals(const Rect& rect) const { + return *this == rect; + } + + // Fits as much of the receiving rectangle into the supplied rectangle as + // possible, returning the result. For example, if the receiver had + // a x-location of 2 and a width of 4, and the supplied rectangle had + // an x-location of 0 with a width of 5, the returned rectangle would have + // an x-location of 1 with a width of 4. + Rect AdjustToFit(const Rect& rect) const; + + // Returns the center of this rectangle. + Point CenterPoint() const; + + private: + gfx::Point origin_; + gfx::Size size_; +}; + +} // namespace gfx + +inline std::ostream& operator<<(std::ostream& out, const gfx::Rect& r) { + return out << r.origin() << " " << r.size(); +} + +#endif // BASE_GFX_RECT_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/rect_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/rect_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/rect_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/rect_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,280 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "base/gfx/rect.h" +#include "testing/gtest/include/gtest/gtest.h" + +typedef testing::Test RectTest; + +TEST(RectTest, Contains) { + static const struct ContainsCase { + int rect_x; + int rect_y; + int rect_width; + int rect_height; + int point_x; + int point_y; + bool contained; + } contains_cases[] = { + {0, 0, 10, 10, 0, 0, true}, + {0, 0, 10, 10, 5, 5, true}, + {0, 0, 10, 10, 9, 9, true}, + {0, 0, 10, 10, 5, 10, false}, + {0, 0, 10, 10, 10, 5, false}, + {0, 0, 10, 10, -1, -1, false}, + {0, 0, 10, 10, 50, 50, false}, + #ifdef NDEBUG + {0, 0, -10, -10, 0, 0, false}, + #endif // NDEBUG + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(contains_cases); ++i) { + const ContainsCase& value = contains_cases[i]; + gfx::Rect rect(value.rect_x, value.rect_y, + value.rect_width, value.rect_height); + EXPECT_EQ(value.contained, rect.Contains(value.point_x, value.point_y)); + } +} + +TEST(RectTest, Intersects) { + static const struct { + int x1; // rect 1 + int y1; + int w1; + int h1; + int x2; // rect 2 + int y2; + int w2; + int h2; + bool intersects; + } tests[] = { + { 0, 0, 0, 0, 0, 0, 0, 0, false }, + { 0, 0, 10, 10, 0, 0, 10, 10, true }, + { 0, 0, 10, 10, 10, 10, 10, 10, false }, + { 10, 10, 10, 10, 0, 0, 10, 10, false }, + { 10, 10, 10, 10, 5, 5, 10, 10, true }, + { 10, 10, 10, 10, 15, 15, 10, 10, true }, + { 10, 10, 10, 10, 20, 15, 10, 10, false }, + { 10, 10, 10, 10, 21, 15, 10, 10, false } + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + gfx::Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1); + gfx::Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2); + EXPECT_EQ(tests[i].intersects, r1.Intersects(r2)); + } +} + +TEST(RectTest, Intersect) { + static const struct { + int x1; // rect 1 + int y1; + int w1; + int h1; + int x2; // rect 2 + int y2; + int w2; + int h2; + int x3; // rect 3: the union of rects 1 and 2 + int y3; + int w3; + int h3; + } tests[] = { + { 0, 0, 0, 0, // zeros + 0, 0, 0, 0, + 0, 0, 0, 0 }, + { 0, 0, 4, 4, // equal + 0, 0, 4, 4, + 0, 0, 4, 4 }, + { 0, 0, 4, 4, // neighboring + 4, 4, 4, 4, + 0, 0, 0, 0 }, + { 0, 0, 4, 4, // overlapping corners + 2, 2, 4, 4, + 2, 2, 2, 2 }, + { 0, 0, 4, 4, // T junction + 3, 1, 4, 2, + 3, 1, 1, 2 }, + { 3, 0, 2, 2, // gap + 0, 0, 2, 2, + 0, 0, 0, 0 } + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + gfx::Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1); + gfx::Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2); + gfx::Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3); + gfx::Rect ir = r1.Intersect(r2); + EXPECT_EQ(r3.x(), ir.x()); + EXPECT_EQ(r3.y(), ir.y()); + EXPECT_EQ(r3.width(), ir.width()); + EXPECT_EQ(r3.height(), ir.height()); + } +} + +TEST(RectTest, Union) { + static const struct Test { + int x1; // rect 1 + int y1; + int w1; + int h1; + int x2; // rect 2 + int y2; + int w2; + int h2; + int x3; // rect 3: the union of rects 1 and 2 + int y3; + int w3; + int h3; + } tests[] = { + { 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0 }, + { 0, 0, 4, 4, + 0, 0, 4, 4, + 0, 0, 4, 4 }, + { 0, 0, 4, 4, + 4, 4, 4, 4, + 0, 0, 8, 8 }, + { 0, 0, 4, 4, + 0, 5, 4, 4, + 0, 0, 4, 9 }, + { 0, 0, 2, 2, + 3, 3, 2, 2, + 0, 0, 5, 5 }, + { 3, 3, 2, 2, // reverse r1 and r2 from previous test + 0, 0, 2, 2, + 0, 0, 5, 5 }, + { 0, 0, 0, 0, // union with empty rect + 2, 2, 2, 2, + 2, 2, 2, 2 } + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + gfx::Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1); + gfx::Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2); + gfx::Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3); + gfx::Rect u = r1.Union(r2); + EXPECT_EQ(r3.x(), u.x()); + EXPECT_EQ(r3.y(), u.y()); + EXPECT_EQ(r3.width(), u.width()); + EXPECT_EQ(r3.height(), u.height()); + } +} + +TEST(RectTest, Equals) { + ASSERT_TRUE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(0, 0, 0, 0))); + ASSERT_TRUE(gfx::Rect(1, 2, 3, 4).Equals(gfx::Rect(1, 2, 3, 4))); + ASSERT_FALSE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(0, 0, 0, 1))); + ASSERT_FALSE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(0, 0, 1, 0))); + ASSERT_FALSE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(0, 1, 0, 0))); + ASSERT_FALSE(gfx::Rect(0, 0, 0, 0).Equals(gfx::Rect(1, 0, 0, 0))); +} + +TEST(RectTest, AdjustToFit) { + static const struct Test { + int x1; // source + int y1; + int w1; + int h1; + int x2; // target + int y2; + int w2; + int h2; + int x3; // rect 3: results of invoking AdjustToFit + int y3; + int w3; + int h3; + } tests[] = { + { 0, 0, 2, 2, + 0, 0, 2, 2, + 0, 0, 2, 2 }, + { 2, 2, 3, 3, + 0, 0, 4, 4, + 1, 1, 3, 3 }, + { -1, -1, 5, 5, + 0, 0, 4, 4, + 0, 0, 4, 4 }, + { 2, 2, 4, 4, + 0, 0, 3, 3, + 0, 0, 3, 3 }, + { 2, 2, 1, 1, + 0, 0, 3, 3, + 2, 2, 1, 1 } + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { + gfx::Rect r1(tests[i].x1, tests[i].y1, tests[i].w1, tests[i].h1); + gfx::Rect r2(tests[i].x2, tests[i].y2, tests[i].w2, tests[i].h2); + gfx::Rect r3(tests[i].x3, tests[i].y3, tests[i].w3, tests[i].h3); + gfx::Rect u(r1.AdjustToFit(r2)); + EXPECT_EQ(r3.x(), u.x()); + EXPECT_EQ(r3.y(), u.y()); + EXPECT_EQ(r3.width(), u.width()); + EXPECT_EQ(r3.height(), u.height()); + } +} + +TEST(RectTest, Subtract) { + // Matching + EXPECT_TRUE( + gfx::Rect(10, 10, 20, 20).Subtract( + gfx::Rect(10, 10, 20, 20)).Equals( + gfx::Rect(0, 0, 0, 0))); + + // Contains + EXPECT_TRUE( + gfx::Rect(10, 10, 20, 20).Subtract( + gfx::Rect(5, 5, 30, 30)).Equals( + gfx::Rect(0, 0, 0, 0))); + + // No intersection + EXPECT_TRUE( + gfx::Rect(10, 10, 20, 20).Subtract( + gfx::Rect(30, 30, 20, 20)).Equals( + gfx::Rect(10, 10, 20, 20))); + + // Not a complete intersection in either direction + EXPECT_TRUE( + gfx::Rect(10, 10, 20, 20).Subtract( + gfx::Rect(15, 15, 20, 20)).Equals( + gfx::Rect(10, 10, 20, 20))); + + // Complete intersection in the x-direction + EXPECT_TRUE( + gfx::Rect(10, 10, 20, 20).Subtract( + gfx::Rect(10, 15, 20, 20)).Equals( + gfx::Rect(10, 10, 20, 5))); + + // Complete intersection in the x-direction + EXPECT_TRUE( + gfx::Rect(10, 10, 20, 20).Subtract( + gfx::Rect(5, 15, 30, 20)).Equals( + gfx::Rect(10, 10, 20, 5))); + + // Complete intersection in the x-direction + EXPECT_TRUE( + gfx::Rect(10, 10, 20, 20).Subtract( + gfx::Rect(5, 5, 30, 20)).Equals( + gfx::Rect(10, 25, 20, 5))); + + // Complete intersection in the y-direction + EXPECT_TRUE( + gfx::Rect(10, 10, 20, 20).Subtract( + gfx::Rect(10, 10, 10, 30)).Equals( + gfx::Rect(20, 10, 10, 20))); + + // Complete intersection in the y-direction + EXPECT_TRUE( + gfx::Rect(10, 10, 20, 20).Subtract( + gfx::Rect(5, 5, 20, 30)).Equals( + gfx::Rect(25, 10, 5, 20))); +} + +TEST(RectTest, IsEmpty) { + EXPECT_TRUE(gfx::Rect(0, 0, 0, 0).IsEmpty()); + EXPECT_TRUE(gfx::Rect(0, 0, 0, 0).size().IsEmpty()); + EXPECT_TRUE(gfx::Rect(0, 0, 10, 0).IsEmpty()); + EXPECT_TRUE(gfx::Rect(0, 0, 10, 0).size().IsEmpty()); + EXPECT_TRUE(gfx::Rect(0, 0, 0, 10).IsEmpty()); + EXPECT_TRUE(gfx::Rect(0, 0, 0, 10).size().IsEmpty()); + EXPECT_FALSE(gfx::Rect(0, 0, 10, 10).IsEmpty()); + EXPECT_FALSE(gfx::Rect(0, 0, 10, 10).size().IsEmpty()); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/size.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/size.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/size.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/size.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,53 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/gfx/size.h" + +#if defined(OS_WIN) +#include +#elif defined(OS_MACOSX) +#include +#endif + +#include "base/logging.h" + + +namespace gfx { + +Size::Size(int width, int height) { + set_width(width); + set_height(height); +} + +#if defined(OS_WIN) +SIZE Size::ToSIZE() const { + SIZE s; + s.cx = width_; + s.cy = height_; + return s; +} +#elif defined(OS_MACOSX) +CGSize Size::ToCGSize() const { + return CGSizeMake(width_, height_); +} +#endif + +void Size::set_width(int width) { + if (width < 0) { + NOTREACHED(); + width = 0; + } + width_ = width; +} + +void Size::set_height(int height) { + if (height < 0) { + NOTREACHED(); + height = 0; + } + height_ = height; +} + + +} // namespace gfx diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/size.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/size.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/gfx/size.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/gfx/size.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,76 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_GFX_SIZE_H__ +#define BASE_GFX_SIZE_H__ + +#include "build/build_config.h" + +#include + +#if defined(OS_WIN) +typedef struct tagSIZE SIZE; +#elif defined(OS_MACOSX) +#include +#endif + +namespace gfx { + +// +// A size has width and height values. +// +class Size { + public: + Size() : width_(0), height_(0) {} + Size(int width, int height); + + ~Size() {} + + int width() const { return width_; } + int height() const { return height_; } + + void SetSize(int width, int height) { + set_width(width); + set_height(height); + } + + void Enlarge(int width, int height) { + set_width(width_ + width); + set_height(height_ + height); + } + + void set_width(int width); + void set_height(int height); + + bool operator==(const Size& s) const { + return width_ == s.width_ && height_ == s.height_; + } + + bool operator!=(const Size& s) const { + return !(*this == s); + } + + bool IsEmpty() const { + // Size doesn't allow negative dimensions, so testing for 0 is enough. + return (width_ == 0) || (height_ == 0); + } + +#if defined(OS_WIN) + SIZE ToSIZE() const; +#elif defined(OS_MACOSX) + CGSize ToCGSize() const; +#endif + + private: + int width_; + int height_; +}; + +} // namespace gfx + +inline std::ostream& operator<<(std::ostream& out, const gfx::Size& s) { + return out << s.width() << "x" << s.height(); +} + +#endif // BASE_GFX_SIZE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/hash_tables.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/hash_tables.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/hash_tables.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/hash_tables.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,104 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// + +// +// Deal with the differences between Microsoft and GNU implemenations +// of hash_map. Allows all platforms to use |base::hash_map| and +// |base::hash_set|. +// eg: +// base::hash_map my_map; +// base::hash_set my_set; +// + +#ifndef BASE_HASH_TABLES_H_ +#define BASE_HASH_TABLES_H_ + +#include "build/build_config.h" + +#include "base/string16.h" + +#if defined(COMPILER_MSVC) +#include +#include +namespace base { +using stdext::hash_map; +using stdext::hash_set; +} +#elif defined(COMPILER_GCC) +// This is a hack to disable the gcc 4.4 warning about hash_map and hash_set +// being deprecated. We can get rid of this when we upgrade to VS2008 and we +// can use and . +#ifdef __DEPRECATED +#define CHROME_OLD__DEPRECATED __DEPRECATED +#undef __DEPRECATED +#endif + +#include +#include +#include + +#ifdef CHROME_OLD__DEPRECATED +#define __DEPRECATED CHROME_OLD__DEPRECATED +#undef CHROME_OLD__DEPRECATED +#endif + +namespace base { +using __gnu_cxx::hash_map; +using __gnu_cxx::hash_set; +} // namespace base + +namespace __gnu_cxx { + +// The GNU C++ library provides identiy hash functions for many integral types, +// but not for |long long|. This hash function will truncate if |size_t| is +// narrower than |long long|. This is probably good enough for what we will +// use it for. + +#define DEFINE_TRIVIAL_HASH(integral_type) \ + template<> \ + struct hash { \ + std::size_t operator()(integral_type value) const { \ + return static_cast(value); \ + } \ + } + +DEFINE_TRIVIAL_HASH(long long); +DEFINE_TRIVIAL_HASH(unsigned long long); + +#undef DEFINE_TRIVIAL_HASH + +// Implement string hash functions so that strings of various flavors can +// be used as keys in STL maps and sets. The hash algorithm comes from the +// GNU C++ library, in . It is duplicated here because GCC +// versions prior to 4.3.2 are unable to compile when RTTI +// is disabled, as it is in our build. + +#define DEFINE_STRING_HASH(string_type) \ + template<> \ + struct hash { \ + std::size_t operator()(const string_type& s) const { \ + std::size_t result = 0; \ + for (string_type::const_iterator i = s.begin(); i != s.end(); ++i) \ + result = (result * 131) + *i; \ + return result; \ + } \ + } + +DEFINE_STRING_HASH(std::string); +DEFINE_STRING_HASH(std::wstring); + +#if defined(WCHAR_T_IS_UTF32) +// If string16 and std::wstring are not the same type, provide a +// specialization for string16. +DEFINE_STRING_HASH(string16); +#endif // WCHAR_T_IS_UTF32 + +#undef DEFINE_STRING_HASH + +} // namespace __gnu_cxx + +#endif // COMPILER + +#endif // BASE_HASH_TABLES_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/histogram.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/histogram.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/histogram.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/histogram.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,794 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Histogram is an object that aggregates statistics, and can summarize them in +// various forms, including ASCII graphical, HTML, and numerically (as a +// vector of numbers corresponding to each of the aggregating buckets). +// See header file for details and examples. + +#include "base/histogram.h" + +#include +#include + +#include "base/logging.h" +#include "base/pickle.h" +#include "base/string_util.h" + +using base::TimeDelta; + +typedef Histogram::Count Count; + +// static +const int Histogram::kHexRangePrintingFlag = 0x8000; + +Histogram::Histogram(const char* name, Sample minimum, + Sample maximum, size_t bucket_count) + : histogram_name_(name), + declared_min_(minimum), + declared_max_(maximum), + bucket_count_(bucket_count), + flags_(0), + ranges_(bucket_count + 1, 0), + sample_(), + registered_(false) { + Initialize(); +} + +Histogram::Histogram(const char* name, TimeDelta minimum, + TimeDelta maximum, size_t bucket_count) + : histogram_name_(name), + declared_min_(static_cast (minimum.InMilliseconds())), + declared_max_(static_cast (maximum.InMilliseconds())), + bucket_count_(bucket_count), + flags_(0), + ranges_(bucket_count + 1, 0), + sample_(), + registered_(false) { + Initialize(); +} + +Histogram::~Histogram() { + if (registered_) + StatisticsRecorder::UnRegister(this); + // Just to make sure most derived class did this properly... + DCHECK(ValidateBucketRanges()); +} + +void Histogram::Add(int value) { + if (!registered_) + registered_ = StatisticsRecorder::Register(this); + if (value >= kSampleType_MAX) + value = kSampleType_MAX - 1; + if (value < 0) + value = 0; + size_t index = BucketIndex(value); + DCHECK(value >= ranges(index)); + DCHECK(value < ranges(index + 1)); + Accumulate(value, 1, index); +} + +void Histogram::AddSampleSet(const SampleSet& sample) { + sample_.Add(sample); +} + +// The following methods provide a graphical histogram display. +void Histogram::WriteHTMLGraph(std::string* output) const { + // TBD(jar) Write a nice HTML bar chart, with divs an mouse-overs etc. + output->append("
    ");
    +  WriteAscii(true, "
    ", output); + output->append("
    "); +} + +void Histogram::WriteAscii(bool graph_it, const std::string& newline, + std::string* output) const { + // Get local (stack) copies of all effectively volatile class data so that we + // are consistent across our output activities. + SampleSet snapshot; + SnapshotSample(&snapshot); + Count sample_count = snapshot.TotalCount(); + + WriteAsciiHeader(snapshot, sample_count, output); + output->append(newline); + + // Prepare to normalize graphical rendering of bucket contents. + double max_size = 0; + if (graph_it) + max_size = GetPeakBucketSize(snapshot); + + // Calculate space needed to print bucket range numbers. Leave room to print + // nearly the largest bucket range without sliding over the histogram. + size_t largest_non_empty_bucket = bucket_count() - 1; + while (0 == snapshot.counts(largest_non_empty_bucket)) { + if (0 == largest_non_empty_bucket) + break; // All buckets are empty. + --largest_non_empty_bucket; + } + + // Calculate largest print width needed for any of our bucket range displays. + size_t print_width = 1; + for (size_t i = 0; i < bucket_count(); ++i) { + if (snapshot.counts(i)) { + size_t width = GetAsciiBucketRange(i).size() + 1; + if (width > print_width) + print_width = width; + } + } + + int64 remaining = sample_count; + int64 past = 0; + // Output the actual histogram graph. + for (size_t i = 0; i < bucket_count(); ++i) { + Count current = snapshot.counts(i); + if (!current && !PrintEmptyBucket(i)) + continue; + remaining -= current; + StringAppendF(output, "%#*s ", print_width, GetAsciiBucketRange(i).c_str()); + if (0 == current && i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) { + while (i < bucket_count() - 1 && 0 == snapshot.counts(i + 1)) + ++i; + output->append("... "); + output->append(newline); + continue; // No reason to plot emptiness. + } + double current_size = GetBucketSize(current, i); + if (graph_it) + WriteAsciiBucketGraph(current_size, max_size, output); + WriteAsciiBucketContext(past, current, remaining, i, output); + output->append(newline); + past += current; + } + DCHECK(past == sample_count); +} + +bool Histogram::ValidateBucketRanges() const { + // Standard assertions that all bucket ranges should satisfy. + DCHECK(ranges_.size() == bucket_count_ + 1); + DCHECK(0 == ranges_[0]); + DCHECK(declared_min() == ranges_[1]); + DCHECK(declared_max() == ranges_[bucket_count_ - 1]); + DCHECK(kSampleType_MAX == ranges_[bucket_count_]); + return true; +} + +void Histogram::Initialize() { + sample_.Resize(*this); + if (declared_min_ <= 0) + declared_min_ = 1; + if (declared_max_ >= kSampleType_MAX) + declared_max_ = kSampleType_MAX - 1; + DCHECK(declared_min_ > 0); // We provide underflow bucket. + DCHECK(declared_min_ <= declared_max_); + DCHECK(1 < bucket_count_); + size_t maximal_bucket_count = declared_max_ - declared_min_ + 2; + DCHECK(bucket_count_ <= maximal_bucket_count); + DCHECK(0 == ranges_[0]); + ranges_[bucket_count_] = kSampleType_MAX; + InitializeBucketRange(); + DCHECK(ValidateBucketRanges()); + registered_ = StatisticsRecorder::Register(this); +} + +// Calculate what range of values are held in each bucket. +// We have to be careful that we don't pick a ratio between starting points in +// consecutive buckets that is sooo small, that the integer bounds are the same +// (effectively making one bucket get no values). We need to avoid: +// (ranges_[i] == ranges_[i + 1] +// To avoid that, we just do a fine-grained bucket width as far as we need to +// until we get a ratio that moves us along at least 2 units at a time. From +// that bucket onward we do use the exponential growth of buckets. +void Histogram::InitializeBucketRange() { + double log_max = log(static_cast(declared_max())); + double log_ratio; + double log_next; + size_t bucket_index = 1; + Sample current = declared_min(); + SetBucketRange(bucket_index, current); + while (bucket_count() > ++bucket_index) { + double log_current; + log_current = log(static_cast(current)); + // Calculate the count'th root of the range. + log_ratio = (log_max - log_current) / (bucket_count() - bucket_index); + // See where the next bucket would start. + log_next = log_current + log_ratio; + int next; + next = static_cast(floor(exp(log_next) + 0.5)); + if (next > current) + current = next; + else + ++current; // Just do a narrow bucket, and keep trying. + SetBucketRange(bucket_index, current); + } + + DCHECK(bucket_count() == bucket_index); +} + +size_t Histogram::BucketIndex(Sample value) const { + // Use simple binary search. This is very general, but there are better + // approaches if we knew that the buckets were linearly distributed. + DCHECK(ranges(0) <= value); + DCHECK(ranges(bucket_count()) > value); + size_t under = 0; + size_t over = bucket_count(); + size_t mid; + + do { + DCHECK(over >= under); + mid = (over + under)/2; + if (mid == under) + break; + if (ranges(mid) <= value) + under = mid; + else + over = mid; + } while (true); + + DCHECK(ranges(mid) <= value && ranges(mid+1) > value); + return mid; +} + +// Use the actual bucket widths (like a linear histogram) until the widths get +// over some transition value, and then use that transition width. Exponentials +// get so big so fast (and we don't expect to see a lot of entries in the large +// buckets), so we need this to make it possible to see what is going on and +// not have 0-graphical-height buckets. +double Histogram::GetBucketSize(Count current, size_t i) const { + DCHECK(ranges(i + 1) > ranges(i)); + static const double kTransitionWidth = 5; + double denominator = ranges(i + 1) - ranges(i); + if (denominator > kTransitionWidth) + denominator = kTransitionWidth; // Stop trying to normalize. + return current/denominator; +} + +//------------------------------------------------------------------------------ +// The following two methods can be overridden to provide a thread safe +// version of this class. The cost of locking is low... but an error in each +// of these methods has minimal impact. For now, I'll leave this unlocked, +// and I don't believe I can loose more than a count or two. +// The vectors are NOT reallocated, so there is no risk of them moving around. + +// Update histogram data with new sample. +void Histogram::Accumulate(Sample value, Count count, size_t index) { + // Note locking not done in this version!!! + sample_.Accumulate(value, count, index); +} + +// Do a safe atomic snapshot of sample data. +// This implementation assumes we are on a safe single thread. +void Histogram::SnapshotSample(SampleSet* sample) const { + // Note locking not done in this version!!! + *sample = sample_; +} + +//------------------------------------------------------------------------------ +// Accessor methods + +void Histogram::SetBucketRange(size_t i, Sample value) { + DCHECK(bucket_count_ > i); + ranges_[i] = value; +} + +//------------------------------------------------------------------------------ +// Private methods + +double Histogram::GetPeakBucketSize(const SampleSet& snapshot) const { + double max = 0; + for (size_t i = 0; i < bucket_count() ; ++i) { + double current_size = GetBucketSize(snapshot.counts(i), i); + if (current_size > max) + max = current_size; + } + return max; +} + +void Histogram::WriteAsciiHeader(const SampleSet& snapshot, + Count sample_count, + std::string* output) const { + StringAppendF(output, + "Histogram: %s recorded %ld samples", + histogram_name().c_str(), + sample_count); + if (0 == sample_count) { + DCHECK(0 == snapshot.sum()); + } else { + double average = static_cast(snapshot.sum()) / sample_count; + double variance = static_cast(snapshot.square_sum())/sample_count + - average * average; + double standard_deviation = sqrt(variance); + + StringAppendF(output, + ", average = %.1f, standard deviation = %.1f", + average, standard_deviation); + } + if (flags_ & ~kHexRangePrintingFlag ) + StringAppendF(output, " (flags = 0x%x)", flags_ & ~kHexRangePrintingFlag); +} + +void Histogram::WriteAsciiBucketContext(const int64 past, + const Count current, + const int64 remaining, + const size_t i, + std::string* output) const { + double scaled_sum = (past + current + remaining) / 100.0; + WriteAsciiBucketValue(current, scaled_sum, output); + if (0 < i) { + double percentage = past / scaled_sum; + StringAppendF(output, " {%3.1f%%}", percentage); + } +} + +const std::string Histogram::GetAsciiBucketRange(size_t i) const { + std::string result; + if (kHexRangePrintingFlag & flags_) + StringAppendF(&result, "%#x", ranges(i)); + else + StringAppendF(&result, "%d", ranges(i)); + return result; +} + +void Histogram::WriteAsciiBucketValue(Count current, double scaled_sum, + std::string* output) const { + StringAppendF(output, " (%d = %3.1f%%)", current, current/scaled_sum); +} + +void Histogram::WriteAsciiBucketGraph(double current_size, double max_size, + std::string* output) const { + const int k_line_length = 72; // Maximal horizontal width of graph. + int x_count = static_cast(k_line_length * (current_size / max_size) + + 0.5); + int x_remainder = k_line_length - x_count; + + while (0 < x_count--) + output->append("-"); + output->append("O"); + while (0 < x_remainder--) + output->append(" "); +} + +// static +std::string Histogram::SerializeHistogramInfo(const Histogram& histogram, + const SampleSet& snapshot) { + Pickle pickle; + + pickle.WriteString(histogram.histogram_name()); + pickle.WriteInt(histogram.declared_min()); + pickle.WriteInt(histogram.declared_max()); + pickle.WriteSize(histogram.bucket_count()); + pickle.WriteInt(histogram.histogram_type()); + pickle.WriteInt(histogram.flags()); + + snapshot.Serialize(&pickle); + return std::string(static_cast(pickle.data()), pickle.size()); +} + +// static +void Histogram::DeserializeHistogramList( + const std::vector& histograms) { + for (std::vector::const_iterator it = histograms.begin(); + it < histograms.end(); + ++it) { + DeserializeHistogramInfo(*it); + } +} + +// static +bool Histogram::DeserializeHistogramInfo(const std::string& histogram_info) { + if (histogram_info.empty()) { + return false; + } + + Pickle pickle(histogram_info.data(), + static_cast(histogram_info.size())); + void* iter = NULL; + size_t bucket_count; + int declared_min; + int declared_max; + int histogram_type; + int flags; + std::string histogram_name; + SampleSet sample; + + if (!pickle.ReadString(&iter, &histogram_name) || + !pickle.ReadInt(&iter, &declared_min) || + !pickle.ReadInt(&iter, &declared_max) || + !pickle.ReadSize(&iter, &bucket_count) || + !pickle.ReadInt(&iter, &histogram_type) || + !pickle.ReadInt(&iter, &flags) || + !sample.Histogram::SampleSet::Deserialize(&iter, pickle)) { + LOG(ERROR) << "Picke error decoding Histogram: " << histogram_name; + return false; + } + + Histogram* render_histogram = + StatisticsRecorder::GetHistogram(histogram_name); + + if (render_histogram == NULL) { + if (histogram_type == EXPONENTIAL) { + render_histogram = new Histogram(histogram_name.c_str(), + declared_min, + declared_max, + bucket_count); + } else if (histogram_type == LINEAR) { + render_histogram = reinterpret_cast + (new LinearHistogram(histogram_name.c_str(), + declared_min, + declared_max, + bucket_count)); + } else { + LOG(ERROR) << "Error Deserializing Histogram Unknown histogram_type: " << + histogram_type; + return false; + } + DCHECK(!(flags & kRendererHistogramFlag)); + render_histogram->SetFlags(flags | kRendererHistogramFlag); + } + + DCHECK(declared_min == render_histogram->declared_min()); + DCHECK(declared_max == render_histogram->declared_max()); + DCHECK(bucket_count == render_histogram->bucket_count()); + DCHECK(histogram_type == render_histogram->histogram_type()); + + if (render_histogram->flags() & kRendererHistogramFlag) { + render_histogram->AddSampleSet(sample); + } else { + DLOG(INFO) << "Single thread mode, histogram observed and not copied: " << + histogram_name; + } + + return true; +} + + +//------------------------------------------------------------------------------ +// Methods for the Histogram::SampleSet class +//------------------------------------------------------------------------------ + +Histogram::SampleSet::SampleSet() + : counts_(), + sum_(0), + square_sum_(0) { +} + +void Histogram::SampleSet::Resize(const Histogram& histogram) { + counts_.resize(histogram.bucket_count(), 0); +} + +void Histogram::SampleSet::CheckSize(const Histogram& histogram) const { + DCHECK(counts_.size() == histogram.bucket_count()); +} + + +void Histogram::SampleSet::Accumulate(Sample value, Count count, + size_t index) { + DCHECK(count == 1 || count == -1); + counts_[index] += count; + sum_ += count * value; + square_sum_ += (count * value) * static_cast(value); + DCHECK(counts_[index] >= 0); + DCHECK(sum_ >= 0); + DCHECK(square_sum_ >= 0); +} + +Count Histogram::SampleSet::TotalCount() const { + Count total = 0; + for (Counts::const_iterator it = counts_.begin(); + it != counts_.end(); + ++it) { + total += *it; + } + return total; +} + +void Histogram::SampleSet::Add(const SampleSet& other) { + DCHECK(counts_.size() == other.counts_.size()); + sum_ += other.sum_; + square_sum_ += other.square_sum_; + for (size_t index = 0; index < counts_.size(); ++index) + counts_[index] += other.counts_[index]; +} + +void Histogram::SampleSet::Subtract(const SampleSet& other) { + DCHECK(counts_.size() == other.counts_.size()); + // Note: Race conditions in snapshotting a sum or square_sum may lead to + // (temporary) negative values when snapshots are later combined (and deltas + // calculated). As a result, we don't currently CHCEK() for positive values. + sum_ -= other.sum_; + square_sum_ -= other.square_sum_; + for (size_t index = 0; index < counts_.size(); ++index) { + counts_[index] -= other.counts_[index]; + DCHECK(counts_[index] >= 0); + } +} + +bool Histogram::SampleSet::Serialize(Pickle* pickle) const { + pickle->WriteInt64(sum_); + pickle->WriteInt64(square_sum_); + pickle->WriteSize(counts_.size()); + + for (size_t index = 0; index < counts_.size(); ++index) { + pickle->WriteInt(counts_[index]); + } + + return true; +} + +bool Histogram::SampleSet::Deserialize(void** iter, const Pickle& pickle) { + DCHECK(counts_.size() == 0); + DCHECK(sum_ == 0); + DCHECK(square_sum_ == 0); + + size_t counts_size; + + if (!pickle.ReadInt64(iter, &sum_) || + !pickle.ReadInt64(iter, &square_sum_) || + !pickle.ReadSize(iter, &counts_size)) { + return false; + } + + if (counts_size <= 0) + return false; + + counts_.resize(counts_size, 0); + for (size_t index = 0; index < counts_size; ++index) { + if (!pickle.ReadInt(iter, &counts_[index])) { + return false; + } + } + + return true; +} + +//------------------------------------------------------------------------------ +// LinearHistogram: This histogram uses a traditional set of evenly spaced +// buckets. +//------------------------------------------------------------------------------ + +LinearHistogram::LinearHistogram(const char* name, Sample minimum, + Sample maximum, size_t bucket_count) + : Histogram(name, minimum >= 1 ? minimum : 1, maximum, bucket_count) { + InitializeBucketRange(); + DCHECK(ValidateBucketRanges()); +} + +LinearHistogram::LinearHistogram(const char* name, + TimeDelta minimum, TimeDelta maximum, size_t bucket_count) + : Histogram(name, minimum >= TimeDelta::FromMilliseconds(1) ? + minimum : TimeDelta::FromMilliseconds(1), + maximum, bucket_count) { + // Do a "better" (different) job at init than a base classes did... + InitializeBucketRange(); + DCHECK(ValidateBucketRanges()); +} + +void LinearHistogram::SetRangeDescriptions( + const DescriptionPair descriptions[]) { + for (int i =0; descriptions[i].description; ++i) { + bucket_description_[descriptions[i].sample] = descriptions[i].description; + } +} + +const std::string LinearHistogram::GetAsciiBucketRange(size_t i) const { + int range = ranges(i); + BucketDescriptionMap::const_iterator it = bucket_description_.find(range); + if (it == bucket_description_.end()) + return Histogram::GetAsciiBucketRange(i); + return it->second; +} + +bool LinearHistogram::PrintEmptyBucket(size_t index) const { + return bucket_description_.find(ranges(index)) == bucket_description_.end(); +} + + +void LinearHistogram::InitializeBucketRange() { + DCHECK(0 < declared_min()); // 0 is the underflow bucket here. + double min = declared_min(); + double max = declared_max(); + size_t i; + for (i = 1; i < bucket_count(); ++i) { + double linear_range = (min * (bucket_count() -1 - i) + max * (i - 1)) / + (bucket_count() - 2); + SetBucketRange(i, static_cast (linear_range + 0.5)); + } +} + +// Find bucket to increment for sample value. +size_t LinearHistogram::BucketIndex(Sample value) const { + if (value < declared_min()) return 0; + if (value >= declared_max()) return bucket_count() - 1; + size_t index; + index = static_cast(((value - declared_min()) * (bucket_count() - 2)) + / (declared_max() - declared_min()) + 1); + DCHECK(1 <= index && bucket_count() > index); + return index; +} + +double LinearHistogram::GetBucketSize(Count current, size_t i) const { + DCHECK(ranges(i + 1) > ranges(i)); + // Adjacent buckets with different widths would have "surprisingly" many (few) + // samples in a histogram if we didn't normalize this way. + double denominator = ranges(i + 1) - ranges(i); + return current/denominator; +} + +//------------------------------------------------------------------------------ +// This section provides implementation for ThreadSafeHistogram. +//------------------------------------------------------------------------------ + +ThreadSafeHistogram::ThreadSafeHistogram(const char* name, Sample minimum, + Sample maximum, size_t bucket_count) + : Histogram(name, minimum, maximum, bucket_count), + lock_() { + } + +void ThreadSafeHistogram::Remove(int value) { + if (value >= kSampleType_MAX) + value = kSampleType_MAX - 1; + size_t index = BucketIndex(value); + Accumulate(value, -1, index); +} + +void ThreadSafeHistogram::Accumulate(Sample value, Count count, size_t index) { + AutoLock lock(lock_); + Histogram::Accumulate(value, count, index); +} + +void ThreadSafeHistogram::SnapshotSample(SampleSet* sample) const { + AutoLock lock(lock_); + Histogram::SnapshotSample(sample); +}; + + +//------------------------------------------------------------------------------ +// The next section handles global (central) support for all histograms, as well +// as startup/teardown of this service. +//------------------------------------------------------------------------------ + +// This singleton instance should be started during the single threaded portion +// of main(), and hence it is not thread safe. It initializes globals to +// provide support for all future calls. +StatisticsRecorder::StatisticsRecorder() { + DCHECK(!histograms_); + lock_ = new Lock; + histograms_ = new HistogramMap; +} + +StatisticsRecorder::~StatisticsRecorder() { + DCHECK(histograms_); + + if (dump_on_exit_) { + std::string output; + WriteGraph("", &output); + LOG(INFO) << output; + } + + // Clean up. + delete histograms_; + histograms_ = NULL; + delete lock_; + lock_ = NULL; +} + +// static +bool StatisticsRecorder::WasStarted() { + return NULL != histograms_; +} + +// static +bool StatisticsRecorder::Register(Histogram* histogram) { + if (!histograms_) + return false; + const std::string name = histogram->histogram_name(); + AutoLock auto_lock(*lock_); + + DCHECK(histograms_->end() == histograms_->find(name)) << name << " is already" + "registered as a histogram. Check for duplicate use of the name, or a " + "race where a static initializer could be run by several threads."; + (*histograms_)[name] = histogram; + return true; +} + +// static +void StatisticsRecorder::UnRegister(Histogram* histogram) { + if (!histograms_) + return; + const std::string name = histogram->histogram_name(); + AutoLock auto_lock(*lock_); + DCHECK(histograms_->end() != histograms_->find(name)); + histograms_->erase(name); + if (dump_on_exit_) { + std::string output; + histogram->WriteAscii(true, "\n", &output); + LOG(INFO) << output; + } +} + +// static +void StatisticsRecorder::WriteHTMLGraph(const std::string& query, + std::string* output) { + if (!histograms_) + return; + output->append("About Histograms"); + if (!query.empty()) + output->append(" - " + query); + output->append("" + // We'd like the following no-cache... but it doesn't work. + // "" + ""); + + Histograms snapshot; + GetSnapshot(query, &snapshot); + for (Histograms::iterator it = snapshot.begin(); + it != snapshot.end(); + ++it) { + (*it)->WriteHTMLGraph(output); + output->append("


    "); + } + output->append(""); +} + +// static +void StatisticsRecorder::WriteGraph(const std::string& query, + std::string* output) { + if (!histograms_) + return; + if (query.length()) + StringAppendF(output, "Collections of histograms for %s\n", query.c_str()); + else + output->append("Collections of all histograms\n"); + + Histograms snapshot; + GetSnapshot(query, &snapshot); + for (Histograms::iterator it = snapshot.begin(); + it != snapshot.end(); + ++it) { + (*it)->WriteAscii(true, "\n", output); + output->append("\n"); + } +} + +// static +void StatisticsRecorder::GetHistograms(Histograms* output) { + if (!histograms_) + return; + AutoLock auto_lock(*lock_); + for (HistogramMap::iterator it = histograms_->begin(); + histograms_->end() != it; + ++it) { + output->push_back(it->second); + } +} + +Histogram* StatisticsRecorder::GetHistogram(const std::string& query) { + if (!histograms_) + return NULL; + AutoLock auto_lock(*lock_); + for (HistogramMap::iterator it = histograms_->begin(); + histograms_->end() != it; + ++it) { + if (it->first.find(query) != std::string::npos) + return it->second; + } + return NULL; +} + +// private static +void StatisticsRecorder::GetSnapshot(const std::string& query, + Histograms* snapshot) { + AutoLock auto_lock(*lock_); + for (HistogramMap::iterator it = histograms_->begin(); + histograms_->end() != it; + ++it) { + if (it->first.find(query) != std::string::npos) + snapshot->push_back(it->second); + } +} + +// static +StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; +// static +Lock* StatisticsRecorder::lock_ = NULL; +// static +bool StatisticsRecorder::dump_on_exit_ = false; diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/histogram.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/histogram.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/histogram.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/histogram.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,558 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Histogram is an object that aggregates statistics, and can summarize them in +// various forms, including ASCII graphical, HTML, and numerically (as a +// vector of numbers corresponding to each of the aggregating buckets). + +// It supports calls to accumulate either time intervals (which are processed +// as integral number of milliseconds), or arbitrary integral units. + +// The default layout of buckets is exponential. For example, buckets might +// contain (sequentially) the count of values in the following intervals: +// [0,1), [1,2), [2,4), [4,8), [8,16), [16,32), [32,64), [64,infinity) +// That bucket allocation would actually result from construction of a histogram +// for values between 1 and 64, with 8 buckets, such as: +// Histogram count(L"some name", 1, 64, 8); +// Note that the underflow bucket [0,1) and the overflow bucket [64,infinity) +// are not counted by the constructor in the user supplied "bucket_count" +// argument. +// The above example has an exponential ratio of 2 (doubling the bucket width +// in each consecutive bucket. The Histogram class automatically calculates +// the smallest ratio that it can use to construct the number of buckets +// selected in the constructor. An another example, if you had 50 buckets, +// and millisecond time values from 1 to 10000, then the ratio between +// consecutive bucket widths will be approximately somewhere around the 50th +// root of 10000. This approach provides very fine grain (narrow) buckets +// at the low end of the histogram scale, but allows the histogram to cover a +// gigantic range with the addition of very few buckets. + +#ifndef BASE_HISTOGRAM_H_ +#define BASE_HISTOGRAM_H_ + +#include +#include +#include + +#include "base/lock.h" +#include "base/time.h" + +//------------------------------------------------------------------------------ +// Provide easy general purpose histogram in a macro, just like stats counters. +// The first four macros use 50 buckets. + +#define HISTOGRAM_TIMES(name, sample) do { \ + static Histogram counter((name), base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromSeconds(10), 50); \ + counter.AddTime(sample); \ + } while (0) + +#define HISTOGRAM_COUNTS(name, sample) do { \ + static Histogram counter((name), 1, 1000000, 50); \ + counter.Add(sample); \ + } while (0) + +#define HISTOGRAM_COUNTS_100(name, sample) do { \ + static Histogram counter((name), 1, 100, 50); \ + counter.Add(sample); \ + } while (0) + +#define HISTOGRAM_COUNTS_10000(name, sample) do { \ + static Histogram counter((name), 1, 10000, 50); \ + counter.Add(sample); \ + } while (0) + +#define HISTOGRAM_PERCENTAGE(name, under_one_hundred) do { \ + static LinearHistogram counter((name), 1, 100, 101); \ + counter.Add(under_one_hundred); \ + } while (0) + +// For folks that need real specific times, use this, but you'll only get +// samples that are in the range (overly large samples are discarded). +#define HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) do { \ + static Histogram counter((name), min, max, bucket_count); \ + if ((sample) < (max)) counter.AddTime(sample); \ + } while (0) + +//------------------------------------------------------------------------------ +// This macro set is for a histogram that can support both addition and removal +// of samples. It should be used to render the accumulated asset allocation +// of some samples. For example, it can sample memory allocation sizes, and +// memory releases (as negative samples). +// To simplify the interface, only non-zero values can be sampled, with positive +// numbers indicating addition, and negative numbers implying dimunition +// (removal). +// Note that the underlying ThreadSafeHistogram() uses locking to ensure that +// counts are precise (no chance of losing an addition or removal event, due to +// multithread racing). This precision is required to prevent missed-counts from +// resulting in drift, as the calls to Remove() for a given value should always +// be equal in number or fewer than the corresponding calls to Add(). + +#define ASSET_HISTOGRAM_COUNTS(name, sample) do { \ + static ThreadSafeHistogram counter((name), 1, 1000000, 50); \ + if (0 == sample) break; \ + if (sample >= 0) \ + counter.Add(sample); \ + else\ + counter.Remove(-sample); \ + } while (0) + +//------------------------------------------------------------------------------ +// Define Debug vs non-debug flavors of macros. +#ifndef NDEBUG + +#define DHISTOGRAM_TIMES(name, sample) HISTOGRAM_TIMES(name, sample) +#define DHISTOGRAM_COUNTS(name, sample) HISTOGRAM_COUNTS(name, sample) +#define DASSET_HISTOGRAM_COUNTS(name, sample) ASSET_HISTOGRAM_COUNTS(name, \ + sample) +#define DHISTOGRAM_PERCENTAGE(name, under_one_hundred) HISTOGRAM_PERCENTAGE(\ + name, under_one_hundred) +#define DHISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) \ + HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) + +#else // NDEBUG + +#define DHISTOGRAM_TIMES(name, sample) do {} while (0) +#define DHISTOGRAM_COUNTS(name, sample) do {} while (0) +#define DASSET_HISTOGRAM_COUNTS(name, sample) do {} while (0) +#define DHISTOGRAM_PERCENTAGE(name, under_one_hundred) do {} while (0) +#define DHISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) \ + do {} while (0) + +#endif // NDEBUG + +//------------------------------------------------------------------------------ +// The following macros provide typical usage scenarios for callers that wish +// to record histogram data, and have the data submitted/uploaded via UMA. +// Not all systems support such UMA, but if they do, the following macros +// should work with the service. + +static const int kUmaTargetedHistogramFlag = 0x1; + +// This indicates the histogram is shadow copy of renderer histrogram +// constructed by unpick method and updated regularly from renderer upload +// of histograms. +static const int kRendererHistogramFlag = 1 << 4; + +#define UMA_HISTOGRAM_TIMES(name, sample) do { \ + static Histogram counter((name), base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromSeconds(10), 50); \ + counter.SetFlags(kUmaTargetedHistogramFlag); \ + counter.AddTime(sample); \ + } while (0) + +#define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) do { \ + static Histogram counter((name), base::TimeDelta::FromMilliseconds(10), \ + base::TimeDelta::FromMinutes(3), 50); \ + counter.SetFlags(kUmaTargetedHistogramFlag); \ + counter.AddTime(sample); \ + } while (0) + +// Use this macro when times can routinely be much longer than 10 seconds. +#define UMA_HISTOGRAM_LONG_TIMES(name, sample) do { \ + static Histogram counter((name), base::TimeDelta::FromMilliseconds(1), \ + base::TimeDelta::FromHours(1), 50); \ + counter.SetFlags(kUmaTargetedHistogramFlag); \ + counter.AddTime(sample); \ + } while (0) + +#define UMA_HISTOGRAM_CLIPPED_TIMES(name, sample, min, max, bucket_count) do { \ + static Histogram counter((name), min, max, bucket_count); \ + counter.SetFlags(kUmaTargetedHistogramFlag); \ + if ((sample) < (max)) counter.AddTime(sample); \ + } while (0) + +#define UMA_HISTOGRAM_COUNTS(name, sample) do { \ + static Histogram counter((name), 1, 1000000, 50); \ + counter.SetFlags(kUmaTargetedHistogramFlag); \ + counter.Add(sample); \ + } while (0) + +#define UMA_HISTOGRAM_COUNTS_100(name, sample) do { \ + static Histogram counter((name), 1, 100, 50); \ + counter.SetFlags(kUmaTargetedHistogramFlag); \ + counter.Add(sample); \ + } while (0) + +#define UMA_HISTOGRAM_COUNTS_10000(name, sample) do { \ + static Histogram counter((name), 1, 10000, 50); \ + counter.SetFlags(kUmaTargetedHistogramFlag); \ + counter.Add(sample); \ + } while (0) + +#define UMA_HISTOGRAM_MEMORY_KB(name, sample) do { \ + static Histogram counter((name), 1000, 500000, 50); \ + counter.SetFlags(kUmaTargetedHistogramFlag); \ + counter.Add(sample); \ + } while (0) + +#define UMA_HISTOGRAM_MEMORY_MB(name, sample) do { \ + static Histogram counter((name), 1, 1000, 50); \ + counter.SetFlags(kUmaTargetedHistogramFlag); \ + counter.Add(sample); \ + } while (0) + +#define UMA_HISTOGRAM_PERCENTAGE(name, under_one_hundred) do { \ + static LinearHistogram counter((name), 1, 100, 101); \ + counter.SetFlags(kUmaTargetedHistogramFlag); \ + counter.Add(under_one_hundred); \ + } while (0) + +//------------------------------------------------------------------------------ + +class Pickle; + +class Histogram { + public: + typedef int Sample; // Used for samples (and ranges of samples). + typedef int Count; // Used to count samples in a bucket. + static const Sample kSampleType_MAX = INT_MAX; + + typedef std::vector Counts; + typedef std::vector Ranges; + + static const int kHexRangePrintingFlag; + + enum BucketLayout { + EXPONENTIAL, + LINEAR + }; + + //---------------------------------------------------------------------------- + // Statistic values, developed over the life of the histogram. + + class SampleSet { + public: + explicit SampleSet(); + // Adjust size of counts_ for use with given histogram. + void Resize(const Histogram& histogram); + void CheckSize(const Histogram& histogram) const; + + // Accessor for histogram to make routine additions. + void Accumulate(Sample value, Count count, size_t index); + + // Accessor methods. + Count counts(size_t i) const { return counts_[i]; } + Count TotalCount() const; + int64 sum() const { return sum_; } + int64 square_sum() const { return square_sum_; } + + // Arithmetic manipulation of corresponding elements of the set. + void Add(const SampleSet& other); + void Subtract(const SampleSet& other); + + bool Serialize(Pickle* pickle) const; + bool Deserialize(void** iter, const Pickle& pickle); + + protected: + // Actual histogram data is stored in buckets, showing the count of values + // that fit into each bucket. + Counts counts_; + + // Save simple stats locally. Note that this MIGHT get done in base class + // without shared memory at some point. + int64 sum_; // sum of samples. + int64 square_sum_; // sum of squares of samples. + }; + //---------------------------------------------------------------------------- + + Histogram(const char* name, Sample minimum, + Sample maximum, size_t bucket_count); + Histogram(const char* name, base::TimeDelta minimum, + base::TimeDelta maximum, size_t bucket_count); + virtual ~Histogram(); + + void Add(int value); + // Accept a TimeDelta to increment. + void AddTime(base::TimeDelta time) { + Add(static_cast(time.InMilliseconds())); + } + + void AddSampleSet(const SampleSet& sample); + + // The following methods provide graphical histogram displays. + void WriteHTMLGraph(std::string* output) const; + void WriteAscii(bool graph_it, const std::string& newline, + std::string* output) const; + + // Support generic flagging of Histograms. + // 0x1 Currently used to mark this histogram to be recorded by UMA.. + // 0x8000 means print ranges in hex. + void SetFlags(int flags) { flags_ |= flags; } + void ClearFlags(int flags) { flags_ &= ~flags; } + int flags() const { return flags_; } + + virtual BucketLayout histogram_type() const { return EXPONENTIAL; } + + // Convenience methods for serializing/deserializing the histograms. + // Histograms from Renderer process are serialized and sent to the browser. + // Browser process reconstructs the histogram from the pickled version + // accumulates the browser-side shadow copy of histograms (that mirror + // histograms created in the renderer). + + // Serialize the given snapshot of a Histogram into a String. Uses + // Pickle class to flatten the object. + static std::string SerializeHistogramInfo(const Histogram& histogram, + const SampleSet& snapshot); + // The following method accepts a list of pickled histograms and + // builds a histogram and updates shadow copy of histogram data in the + // browser process. + static void DeserializeHistogramList( + const std::vector& histograms); + static bool DeserializeHistogramInfo(const std::string& state); + + + //---------------------------------------------------------------------------- + // Accessors for serialization and testing. + //---------------------------------------------------------------------------- + const std::string histogram_name() const { return histogram_name_; } + Sample declared_min() const { return declared_min_; } + Sample declared_max() const { return declared_max_; } + virtual Sample ranges(size_t i) const { return ranges_[i];} + virtual size_t bucket_count() const { return bucket_count_; } + // Snapshot the current complete set of sample data. + // Override with atomic/locked snapshot if needed. + virtual void SnapshotSample(SampleSet* sample) const; + + protected: + // Method to override to skip the display of the i'th bucket if it's empty. + virtual bool PrintEmptyBucket(size_t index) const { return true; } + + //---------------------------------------------------------------------------- + // Methods to override to create histogram with different bucket widths. + //---------------------------------------------------------------------------- + // Initialize ranges_ mapping. + virtual void InitializeBucketRange(); + // Find bucket to increment for sample value. + virtual size_t BucketIndex(Sample value) const; + // Get normalized size, relative to the ranges_[i]. + virtual double GetBucketSize(Count current, size_t i) const; + + // Return a string description of what goes in a given bucket. + // Most commonly this is the numeric value, but in derived classes it may + // be a name (or string description) given to the bucket. + virtual const std::string GetAsciiBucketRange(size_t it) const; + + //---------------------------------------------------------------------------- + // Methods to override to create thread safe histogram. + //---------------------------------------------------------------------------- + // Update all our internal data, including histogram + virtual void Accumulate(Sample value, Count count, size_t index); + + //---------------------------------------------------------------------------- + // Accessors for derived classes. + //---------------------------------------------------------------------------- + void SetBucketRange(size_t i, Sample value); + + // Validate that ranges_ was created sensibly (top and bottom range + // values relate properly to the declared_min_ and declared_max_).. + bool ValidateBucketRanges() const; + + private: + // Post constructor initialization. + void Initialize(); + + //---------------------------------------------------------------------------- + // Helpers for emitting Ascii graphic. Each method appends data to output. + + // Find out how large the (graphically) the largest bucket will appear to be. + double GetPeakBucketSize(const SampleSet& snapshot) const; + + // Write a common header message describing this histogram. + void WriteAsciiHeader(const SampleSet& snapshot, + Count sample_count, std::string* output) const; + + // Write information about previous, current, and next buckets. + // Information such as cumulative percentage, etc. + void WriteAsciiBucketContext(const int64 past, const Count current, + const int64 remaining, const size_t i, + std::string* output) const; + + // Write textual description of the bucket contents (relative to histogram). + // Output is the count in the buckets, as well as the percentage. + void WriteAsciiBucketValue(Count current, double scaled_sum, + std::string* output) const; + + // Produce actual graph (set of blank vs non blank char's) for a bucket. + void WriteAsciiBucketGraph(double current_size, double max_size, + std::string* output) const; + + //---------------------------------------------------------------------------- + // Invariant values set at/near construction time + + // ASCII version of original name given to the constructor. All identically + // named instances will be coalesced cross-project TODO(jar). + // If a user needs one histogram name to be called by several places in a + // single process, a central function should be defined by teh user, which + // defins the single declared instance of the named histogram. + const std::string histogram_name_; + Sample declared_min_; // Less than this goes into counts_[0] + Sample declared_max_; // Over this goes into counts_[bucket_count_ - 1]. + size_t bucket_count_; // Dimension of counts_[]. + + // Flag the histogram for recording by UMA via metric_services.h. + int flags_; + + // For each index, show the least value that can be stored in the + // corresponding bucket. We also append one extra element in this array, + // containing kSampleType_MAX, to make calculations easy. + // The dimension of ranges_ is bucket_count + 1. + Ranges ranges_; + + // Finally, provide the state that changes with the addition of each new + // sample. + SampleSet sample_; + + // Indicate if successfully registered. + bool registered_; + + DISALLOW_COPY_AND_ASSIGN(Histogram); +}; + +//------------------------------------------------------------------------------ + +// LinearHistogram is a more traditional histogram, with evenly spaced +// buckets. +class LinearHistogram : public Histogram { + public: + struct DescriptionPair { + Sample sample; + const char* description; // Null means end of a list of pairs. + }; + LinearHistogram(const char* name, Sample minimum, + Sample maximum, size_t bucket_count); + + LinearHistogram(const char* name, base::TimeDelta minimum, + base::TimeDelta maximum, size_t bucket_count); + ~LinearHistogram() {} + + // Store a list of number/text values for use in rendering the histogram. + // The last element in the array has a null in its "description" slot. + void SetRangeDescriptions(const DescriptionPair descriptions[]); + + virtual BucketLayout histogram_type() const { return LINEAR; } + + protected: + // Initialize ranges_ mapping. + virtual void InitializeBucketRange(); + // Find bucket to increment for sample value. + virtual size_t BucketIndex(Sample value) const; + virtual double GetBucketSize(Count current, size_t i) const; + + // If we have a description for a bucket, then return that. Otherwise + // let parent class provide a (numeric) description. + virtual const std::string GetAsciiBucketRange(size_t i) const; + + // Skip printing of name for numeric range if we have a name (and if this is + // an empty bucket). + virtual bool PrintEmptyBucket(size_t index) const; + + private: + // For some ranges, we store a printable description of a bucket range. + // If there is no desciption, then GetAsciiBucketRange() uses parent class + // to provide a description. + typedef std::map BucketDescriptionMap; + BucketDescriptionMap bucket_description_; + + DISALLOW_COPY_AND_ASSIGN(LinearHistogram); +}; + +//------------------------------------------------------------------------------ + +// BooleanHistogram is a histogram for booleans. +class BooleanHistogram : public LinearHistogram { + public: + explicit BooleanHistogram(const char* name) + : LinearHistogram(name, 0, 2, 3) { + } + + void AddBoolean(bool value) { Add(value ? 1 : 0); } + + private: + DISALLOW_COPY_AND_ASSIGN(BooleanHistogram); +}; + +//------------------------------------------------------------------------------ +// This section provides implementation for ThreadSafeHistogram. +//------------------------------------------------------------------------------ + +class ThreadSafeHistogram : public Histogram { + public: + ThreadSafeHistogram(const char* name, Sample minimum, + Sample maximum, size_t bucket_count); + + // Provide the analog to Add() + void Remove(int value); + + protected: + // Provide locked versions to get precise counts. + virtual void Accumulate(Sample value, Count count, size_t index); + + virtual void SnapshotSample(SampleSet* sample) const; + + private: + mutable Lock lock_; + + DISALLOW_COPY_AND_ASSIGN(ThreadSafeHistogram); +}; + +//------------------------------------------------------------------------------ +// StatisticsRecorder handles all histograms in the system. It provides a +// general place for histograms to register, and supports a global API for +// accessing (i.e., dumping, or graphing) the data in all the histograms. + +class StatisticsRecorder { + public: + typedef std::vector Histograms; + + StatisticsRecorder(); + + ~StatisticsRecorder(); + + // Find out if histograms can now be registered into our list. + static bool WasStarted(); + + // Register, or add a new histogram to the collection of statistics. + // Return true if registered. + static bool Register(Histogram* histogram); + // Unregister, or remove, a histogram from the collection of statistics. + static void UnRegister(Histogram* histogram); + + // Methods for printing histograms. Only histograms which have query as + // a substring are written to output (an empty string will process all + // registered histograms). + static void WriteHTMLGraph(const std::string& query, std::string* output); + static void WriteGraph(const std::string& query, std::string* output); + + // Method for extracting histograms which were marked for use by UMA. + static void GetHistograms(Histograms* output); + + // Find a histogram by name. This method is thread safe. + static Histogram* GetHistogram(const std::string& query); + + static void set_dump_on_exit(bool enable) { dump_on_exit_ = enable; } + + // GetSnapshot copies some of the pointers to registered histograms into the + // caller supplied vector (Histograms). Only histograms with names matching + // query are returned. The query must be a substring of histogram name for its + // pointer to be copied. + static void GetSnapshot(const std::string& query, Histograms* snapshot); + + + private: + // We keep all registered histograms in a map, from name to histogram. + typedef std::map HistogramMap; + + static HistogramMap* histograms_; + + // lock protects access to the above map. + static Lock* lock_; + + // Dump all known histograms to log. + static bool dump_on_exit_; + + DISALLOW_COPY_AND_ASSIGN(StatisticsRecorder); +}; + +#endif // BASE_HISTOGRAM_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/histogram_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/histogram_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/histogram_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/histogram_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,293 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Test of Histogram class + +#include "base/histogram.h" +#include "base/string_util.h" +#include "base/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::TimeDelta; + +namespace { + +class HistogramTest : public testing::Test { +}; + +// Check for basic syntax and use. +TEST(HistogramTest, StartupShutdownTest) { + // Try basic construction + Histogram histogram("TestHistogram", 1, 1000, 10); + Histogram histogram1("Test1Histogram", 1, 1000, 10); + + LinearHistogram linear_histogram("TestLinearHistogram", 1, 1000, 10); + LinearHistogram linear_histogram1("Test1LinearHistogram", 1, 1000, 10); + + // Use standard macros (but with fixed samples) + HISTOGRAM_TIMES("Test2Histogram", TimeDelta::FromDays(1)); + HISTOGRAM_COUNTS("Test3Histogram", 30); + + DHISTOGRAM_TIMES("Test4Histogram", TimeDelta::FromDays(1)); + DHISTOGRAM_COUNTS("Test5Histogram", 30); + + ASSET_HISTOGRAM_COUNTS("Test6Histogram", 129); + + // Try to construct samples. + Histogram::SampleSet sample1; + Histogram::SampleSet sample2; + + // Use copy constructor of SampleSet + sample1 = sample2; + Histogram::SampleSet sample3(sample1); + + // Finally test a statistics recorder, without really using it. + StatisticsRecorder recorder; +} + +// Repeat with a recorder present to register with. +TEST(HistogramTest, RecordedStartupTest) { + // Test a statistics recorder, by letting histograms register. + StatisticsRecorder recorder; // This initializes the global state. + + StatisticsRecorder::Histograms histograms; + EXPECT_EQ(0U, histograms.size()); + StatisticsRecorder::GetHistograms(&histograms); // Load up lists + EXPECT_EQ(0U, histograms.size()); + + // Try basic construction + Histogram histogram("TestHistogram", 1, 1000, 10); + histograms.clear(); + StatisticsRecorder::GetHistograms(&histograms); // Load up lists + EXPECT_EQ(1U, histograms.size()); + Histogram histogram1("Test1Histogram", 1, 1000, 10); + histograms.clear(); + StatisticsRecorder::GetHistograms(&histograms); // Load up lists + EXPECT_EQ(2U, histograms.size()); + + LinearHistogram linear_histogram("TestLinearHistogram", 1, 1000, 10); + LinearHistogram linear_histogram1("Test1LinearHistogram", 1, 1000, 10); + histograms.clear(); + StatisticsRecorder::GetHistograms(&histograms); // Load up lists + EXPECT_EQ(4U, histograms.size()); + + // Use standard macros (but with fixed samples) + HISTOGRAM_TIMES("Test2Histogram", TimeDelta::FromDays(1)); + HISTOGRAM_COUNTS("Test3Histogram", 30); + histograms.clear(); + StatisticsRecorder::GetHistograms(&histograms); // Load up lists + EXPECT_EQ(6U, histograms.size()); + + ASSET_HISTOGRAM_COUNTS("TestAssetHistogram", 1000); + histograms.clear(); + StatisticsRecorder::GetHistograms(&histograms); // Load up lists + EXPECT_EQ(7U, histograms.size()); + + DHISTOGRAM_TIMES("Test4Histogram", TimeDelta::FromDays(1)); + DHISTOGRAM_COUNTS("Test5Histogram", 30); + histograms.clear(); + StatisticsRecorder::GetHistograms(&histograms); // Load up lists +#ifndef NDEBUG + EXPECT_EQ(9U, histograms.size()); +#else + EXPECT_EQ(7U, histograms.size()); +#endif +} + +TEST(HistogramTest, RangeTest) { + StatisticsRecorder recorder; + StatisticsRecorder::Histograms histograms; + + recorder.GetHistograms(&histograms); + EXPECT_EQ(0U, histograms.size()); + + Histogram histogram("Histogram", 1, 64, 8); // As mentioned in header file. + // Check that we got a nice exponential when there was enough rooom. + EXPECT_EQ(0, histogram.ranges(0)); + int power_of_2 = 1; + for (int i = 1; i < 8; i++) { + EXPECT_EQ(power_of_2, histogram.ranges(i)); + power_of_2 *= 2; + } + EXPECT_EQ(INT_MAX, histogram.ranges(8)); + + Histogram short_histogram("Histogram Shortened", 1, 7, 8); + // Check that when the number of buckets is short, we get a linear histogram + // for lack of space to do otherwise. + for (int i = 0; i < 8; i++) + EXPECT_EQ(i, short_histogram.ranges(i)); + EXPECT_EQ(INT_MAX, short_histogram.ranges(8)); + + LinearHistogram linear_histogram("Linear", 1, 7, 8); + // We also get a nice linear set of bucket ranges when we ask for it + for (int i = 0; i < 8; i++) + EXPECT_EQ(i, linear_histogram.ranges(i)); + EXPECT_EQ(INT_MAX, linear_histogram.ranges(8)); + + LinearHistogram linear_broad_histogram("Linear widened", 2, 14, 8); + // ...but when the list has more space, then the ranges naturally spread out. + for (int i = 0; i < 8; i++) + EXPECT_EQ(2 * i, linear_broad_histogram.ranges(i)); + EXPECT_EQ(INT_MAX, linear_broad_histogram.ranges(8)); + + ThreadSafeHistogram threadsafe_histogram("ThreadSafe", 1, 32, 15); + // When space is a little tight, we transition from linear to exponential. + // This is what happens in both the basic histogram, and the threadsafe + // variant (which is derived). + EXPECT_EQ(0, threadsafe_histogram.ranges(0)); + EXPECT_EQ(1, threadsafe_histogram.ranges(1)); + EXPECT_EQ(2, threadsafe_histogram.ranges(2)); + EXPECT_EQ(3, threadsafe_histogram.ranges(3)); + EXPECT_EQ(4, threadsafe_histogram.ranges(4)); + EXPECT_EQ(5, threadsafe_histogram.ranges(5)); + EXPECT_EQ(6, threadsafe_histogram.ranges(6)); + EXPECT_EQ(7, threadsafe_histogram.ranges(7)); + EXPECT_EQ(9, threadsafe_histogram.ranges(8)); + EXPECT_EQ(11, threadsafe_histogram.ranges(9)); + EXPECT_EQ(14, threadsafe_histogram.ranges(10)); + EXPECT_EQ(17, threadsafe_histogram.ranges(11)); + EXPECT_EQ(21, threadsafe_histogram.ranges(12)); + EXPECT_EQ(26, threadsafe_histogram.ranges(13)); + EXPECT_EQ(32, threadsafe_histogram.ranges(14)); + EXPECT_EQ(INT_MAX, threadsafe_histogram.ranges(15)); + + recorder.GetHistograms(&histograms); + EXPECT_EQ(5U, histograms.size()); +} + +// Make sure histogram handles out-of-bounds data gracefully. +TEST(HistogramTest, BoundsTest) { + const size_t kBucketCount = 50; + Histogram histogram("Bounded", 10, 100, kBucketCount); + + // Put two samples "out of bounds" above and below. + histogram.Add(5); + histogram.Add(-50); + + histogram.Add(100); + histogram.Add(10000); + + // Verify they landed in the underflow, and overflow buckets. + Histogram::SampleSet sample; + histogram.SnapshotSample(&sample); + EXPECT_EQ(2, sample.counts(0)); + EXPECT_EQ(0, sample.counts(1)); + size_t array_size = histogram.bucket_count(); + EXPECT_EQ(kBucketCount, array_size); + EXPECT_EQ(0, sample.counts(array_size - 2)); + EXPECT_EQ(2, sample.counts(array_size - 1)); +} + +// Check to be sure samples land as expected is "correct" buckets. +TEST(HistogramTest, BucketPlacementTest) { + Histogram histogram("Histogram", 1, 64, 8); // As mentioned in header file. + + // Check that we got a nice exponential since there was enough rooom. + EXPECT_EQ(0, histogram.ranges(0)); + int power_of_2 = 1; + for (int i = 1; i < 8; i++) { + EXPECT_EQ(power_of_2, histogram.ranges(i)); + power_of_2 *= 2; + } + EXPECT_EQ(INT_MAX, histogram.ranges(8)); + + // Add i+1 samples to the i'th bucket. + histogram.Add(0); + power_of_2 = 1; + for (int i = 1; i < 8; i++) { + for (int j = 0; j <= i; j++) + histogram.Add(power_of_2); + power_of_2 *= 2; + } + // Leave overflow bucket empty. + + // Check to see that the bucket counts reflect our additions. + Histogram::SampleSet sample; + histogram.SnapshotSample(&sample); + EXPECT_EQ(INT_MAX, histogram.ranges(8)); + for (int i = 0; i < 8; i++) + EXPECT_EQ(i + 1, sample.counts(i)); +} + +static const char kAssetTestHistogramName[] = "AssetCountTest"; +static const char kAssetTestDebugHistogramName[] = "DAssetCountTest"; +void AssetCountFunction(int sample) { + ASSET_HISTOGRAM_COUNTS(kAssetTestHistogramName, sample); + DASSET_HISTOGRAM_COUNTS(kAssetTestDebugHistogramName, sample); +} +// Check that asset can be added and removed from buckets. +TEST(HistogramTest, AssetCountTest) { + // Start up a recorder system to identify all histograms. + StatisticsRecorder recorder; + + // Call through the macro to instantiate the static variables. + AssetCountFunction(100); // Put a sample in the bucket for 100. + + // Find the histogram. + StatisticsRecorder::Histograms histogram_list; + StatisticsRecorder::GetHistograms(&histogram_list); + ASSERT_NE(0U, histogram_list.size()); + const Histogram* our_histogram = NULL; + const Histogram* our_debug_histogram = NULL; + for (StatisticsRecorder::Histograms::iterator it = histogram_list.begin(); + it != histogram_list.end(); + ++it) { + if (!(*it)->histogram_name().compare(kAssetTestHistogramName)) + our_histogram = *it; + else if (!(*it)->histogram_name().compare(kAssetTestDebugHistogramName)) { + our_debug_histogram = *it; + } + } + ASSERT_TRUE(our_histogram); +#ifndef NDEBUG + EXPECT_TRUE(our_debug_histogram); +#else + EXPECT_FALSE(our_debug_histogram); +#endif + // Verify it has a 1 in exactly one bucket (where we put the sample). + Histogram::SampleSet sample; + our_histogram->SnapshotSample(&sample); + int match_count = 0; + for (size_t i = 0; i < our_histogram->bucket_count(); ++i) { + if (sample.counts(i) > 0) { + EXPECT_LT(++match_count, 2) << "extra count in bucket " << i; + } + } + EXPECT_EQ(1, match_count); + + // Remove our sample. + AssetCountFunction(-100); // Remove a sample from the bucket for 100. + our_histogram->SnapshotSample(&sample); // Extract data set. + + // Verify that the bucket is now empty, as are all the other buckets. + for (size_t i = 0; i < our_histogram->bucket_count(); ++i) { + EXPECT_EQ(0, sample.counts(i)) << "extra count in bucket " << i; + } + + if (!our_debug_histogram) + return; // This is a production build. + + // Repeat test with debug histogram. Note that insertion and deletion above + // should have cancelled each other out. + AssetCountFunction(100); // Add a sample into the bucket for 100. + our_debug_histogram->SnapshotSample(&sample); + match_count = 0; + for (size_t i = 0; i < our_debug_histogram->bucket_count(); ++i) { + if (sample.counts(i) > 0) { + EXPECT_LT(++match_count, 2) << "extra count in bucket " << i; + } + } + EXPECT_EQ(1, match_count); + + // Remove our sample. + AssetCountFunction(-100); // Remove a sample from the bucket for 100. + our_debug_histogram->SnapshotSample(&sample); // Extract data set. + + // Verify that the bucket is now empty, as are all the other buckets. + for (size_t i = 0; i < our_debug_histogram->bucket_count(); ++i) { + EXPECT_EQ(0, sample.counts(i)) << "extra count in bucket " << i; + } +} + +} // namespace diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/hmac.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/hmac.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/hmac.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/hmac.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,56 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Utility class for calculating the HMAC for a given message. We currently +// only support SHA1 for the hash algorithm, but this can be extended easily. + +#ifndef BASE_HMAC_H_ +#define BASE_HMAC_H_ + +#include + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" + +namespace base { + +// Simplify the interface and reduce includes by abstracting out the internals. +struct HMACPlatformData; + +class HMAC { + public: + // The set of supported hash functions. Extend as required. + enum HashAlgorithm { + SHA1 + }; + + explicit HMAC(HashAlgorithm hash_alg); + ~HMAC(); + + // Initializes this instance using |key| of the length |key_length|. Call Init + // only once. It returns false on the second or later calls. + bool Init(const unsigned char* key, int key_length); + + // Initializes this instance using |key|. Call Init only once. It returns + // false on the second or later calls. + bool Init(const std::string& key) { + return Init(reinterpret_cast(key.data()), + static_cast(key.size())); + } + + // Calculates the HMAC for the message in |data| using the algorithm supplied + // to the constructor and the key supplied to the Init method. The HMAC is + // returned in |digest|, which has |digest_length| bytes of storage available. + bool Sign(const std::string& data, unsigned char* digest, int digest_length); + + private: + HashAlgorithm hash_alg_; + scoped_ptr plat_; + + DISALLOW_COPY_AND_ASSIGN(HMAC); +}; + +} // namespace base + +#endif // BASE_HMAC_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/hmac_mac.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/hmac_mac.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/hmac_mac.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/hmac_mac.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,69 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/hmac.h" + +#include + +#include "base/logging.h" + +namespace base { + +struct HMACPlatformData { + std::string key_; +}; + +HMAC::HMAC(HashAlgorithm hash_alg) + : hash_alg_(hash_alg), plat_(new HMACPlatformData()) { + // Only SHA-1 digest is supported now. + DCHECK(hash_alg_ == SHA1); +} + +bool HMAC::Init(const unsigned char *key, int key_length) { + if (!plat_->key_.empty()) { + // Init must not be called more than once on the same HMAC object. + NOTREACHED(); + return false; + } + + plat_->key_.assign(reinterpret_cast(key), key_length); + + return true; +} + +HMAC::~HMAC() { + // Zero out key copy. + plat_->key_.assign(plat_->key_.length(), std::string::value_type()); + plat_->key_.clear(); + plat_->key_.reserve(0); +} + +bool HMAC::Sign(const std::string& data, + unsigned char* digest, + int digest_length) { + CCHmacAlgorithm algorithm; + int algorithm_digest_length; + switch (hash_alg_) { + case SHA1: + algorithm = kCCHmacAlgSHA1; + algorithm_digest_length = CC_SHA1_DIGEST_LENGTH; + break; + default: + NOTREACHED(); + return false; + } + + if (digest_length < algorithm_digest_length) { + NOTREACHED(); + return false; + } + + CCHmac(algorithm, + plat_->key_.data(), plat_->key_.length(), data.data(), data.length(), + digest); + + return true; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/hmac_nss.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/hmac_nss.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/hmac_nss.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/hmac_nss.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,134 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/hmac.h" + +#include +#include + +#include "base/logging.h" +#include "base/nss_init.h" +#include "base/scoped_ptr.h" + +namespace { + +template +struct NSSDestroyer { + void operator()(Type* ptr) const { + if (ptr) + Destroyer(ptr); + } +}; + +void DestroyContext(PK11Context* context) { + PK11_DestroyContext(context, PR_TRUE); +} + +// Define some convenient scopers around NSS pointers. +typedef scoped_ptr_malloc< + PK11SlotInfo, NSSDestroyer > ScopedNSSSlot; +typedef scoped_ptr_malloc< + PK11SymKey, NSSDestroyer > ScopedNSSSymKey; +typedef scoped_ptr_malloc< + PK11Context, NSSDestroyer > ScopedNSSContext; + +} // namespace + +namespace base { + +struct HMACPlatformData { + ScopedNSSSlot slot_; + ScopedNSSSymKey sym_key_; +}; + +HMAC::HMAC(HashAlgorithm hash_alg) + : hash_alg_(hash_alg), plat_(new HMACPlatformData()) { + // Only SHA-1 digest is supported now. + DCHECK(hash_alg_ == SHA1); +} + +bool HMAC::Init(const unsigned char *key, int key_length) { + base::EnsureNSSInit(); + + if (hash_alg_ != SHA1) { + NOTREACHED(); + return false; + } + + if (plat_->slot_.get() || plat_->slot_.get()) { + // Init must not be called more than twice on the same HMAC object. + NOTREACHED(); + return false; + } + + plat_->slot_.reset(PK11_GetBestSlot(CKM_SHA_1_HMAC, NULL)); + if (!plat_->slot_.get()) { + NOTREACHED(); + return false; + } + + SECItem key_item; + key_item.type = siBuffer; + key_item.data = const_cast(key); // NSS API isn't const. + key_item.len = key_length; + + plat_->sym_key_.reset(PK11_ImportSymKey(plat_->slot_.get(), + CKM_SHA_1_HMAC, + PK11_OriginUnwrap, + CKA_SIGN, + &key_item, + NULL)); + if (!plat_->sym_key_.get()) { + NOTREACHED(); + return false; + } + + return true; +} + +HMAC::~HMAC() { +} + +bool HMAC::Sign(const std::string& data, + unsigned char* digest, + int digest_length) { + if (!plat_->sym_key_.get()) { + // Init has not been called before Sign. + NOTREACHED(); + return false; + } + + SECItem param = { siBuffer, NULL, 0 }; + ScopedNSSContext context(PK11_CreateContextBySymKey(CKM_SHA_1_HMAC, + CKA_SIGN, + plat_->sym_key_.get(), + ¶m)); + if (!context.get()) { + NOTREACHED(); + return false; + } + + if (PK11_DigestBegin(context.get()) != SECSuccess) { + NOTREACHED(); + return false; + } + + if (PK11_DigestOp(context.get(), + reinterpret_cast(data.data()), + data.length()) != SECSuccess) { + NOTREACHED(); + return false; + } + + unsigned int len = 0; + if (PK11_DigestFinal(context.get(), + digest, &len, digest_length) != SECSuccess) { + NOTREACHED(); + return false; + } + + return true; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/hmac_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/hmac_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/hmac_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/hmac_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,164 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/hmac.h" +#include "testing/gtest/include/gtest/gtest.h" + +static const int kDigestSize = 20; + +TEST(HMACTest, HmacSafeBrowsingResponseTest) { + const int kKeySize = 16; + + // Client key. + const unsigned char kClientKey[kKeySize] = + { 0xbf, 0xf6, 0x83, 0x4b, 0x3e, 0xa3, 0x23, 0xdd, + 0x96, 0x78, 0x70, 0x8e, 0xa1, 0x9d, 0x3b, 0x40 }; + + // Expected HMAC result using kMessage and kClientKey. + const unsigned char kReceivedHmac[kDigestSize] = + { 0xb9, 0x3c, 0xd6, 0xf0, 0x49, 0x47, 0xe2, 0x52, + 0x59, 0x7a, 0xbd, 0x1f, 0x2b, 0x4c, 0x83, 0xad, + 0x86, 0xd2, 0x48, 0x85 }; + + const char kMessage[] = +"n:1896\ni:goog-malware-shavar\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shav" +"ar_s_445-450\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_439-444\nu:s" +".ytimg.com/safebrowsing/rd/goog-malware-shavar_s_437\nu:s.ytimg.com/safebrowsi" +"ng/rd/goog-malware-shavar_s_436\nu:s.ytimg.com/safebrowsing/rd/goog-malware-sh" +"avar_s_433-435\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_431\nu:s.y" +"timg.com/safebrowsing/rd/goog-malware-shavar_s_430\nu:s.ytimg.com/safebrowsing" +"/rd/goog-malware-shavar_s_429\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shav" +"ar_s_428\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_426\nu:s.ytimg.c" +"om/safebrowsing/rd/goog-malware-shavar_s_424\nu:s.ytimg.com/safebrowsing/rd/go" +"og-malware-shavar_s_423\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_4" +"22\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_420\nu:s.ytimg.com/saf" +"ebrowsing/rd/goog-malware-shavar_s_419\nu:s.ytimg.com/safebrowsing/rd/goog-mal" +"ware-shavar_s_414\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_409-411" +"\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_405\nu:s.ytimg.com/safeb" +"rowsing/rd/goog-malware-shavar_s_404\nu:s.ytimg.com/safebrowsing/rd/goog-malwa" +"re-shavar_s_402\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_s_401\nu:s." +"ytimg.com/safebrowsing/rd/goog-malware-shavar_a_973-978\nu:s.ytimg.com/safebro" +"wsing/rd/goog-malware-shavar_a_937-972\nu:s.ytimg.com/safebrowsing/rd/goog-mal" +"ware-shavar_a_931-936\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_a_925" +"-930\nu:s.ytimg.com/safebrowsing/rd/goog-malware-shavar_a_919-924\ni:goog-phis" +"h-shavar\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2633\nu:s.ytimg.co" +"m/safebrowsing/rd/goog-phish-shavar_a_2632\nu:s.ytimg.com/safebrowsing/rd/goog" +"-phish-shavar_a_2629-2631\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2" +"626-2628\nu:s.ytimg.com/safebrowsing/rd/goog-phish-shavar_a_2625\n"; + + std::string message_data(kMessage); + + base::HMAC hmac(base::HMAC::SHA1); + ASSERT_TRUE(hmac.Init(kClientKey, kKeySize)); + unsigned char calculated_hmac[kDigestSize]; + + EXPECT_TRUE(hmac.Sign(message_data, calculated_hmac, kDigestSize)); + EXPECT_EQ(memcmp(kReceivedHmac, calculated_hmac, kDigestSize), 0); +} + +// Test cases from RFC 2202 section 3 +TEST(HMACTest, RFC2202TestCases) { + const struct { + const char *key; + const int key_len; + const char *data; + const int data_len; + const char *digest; + } cases[] = { + { "\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B\x0B" + "\x0B\x0B\x0B\x0B", 20, + "Hi There", 8, + "\xB6\x17\x31\x86\x55\x05\x72\x64\xE2\x8B\xC0\xB6\xFB\x37\x8C\x8E" + "\xF1\x46\xBE\x00" }, + { "Jefe", 4, + "what do ya want for nothing?", 28, + "\xEF\xFC\xDF\x6A\xE5\xEB\x2F\xA2\xD2\x74\x16\xD5\xF1\x84\xDF\x9C" + "\x25\x9A\x7C\x79" }, + { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA", 20, + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD\xDD" + "\xDD\xDD", 50, + "\x12\x5D\x73\x42\xB9\xAC\x11\xCD\x91\xA3\x9A\xF4\x8A\xA1\x7B\x4F" + "\x63\xF1\x75\xD3" }, + { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10" + "\x11\x12\x13\x14\x15\x16\x17\x18\x19", 25, + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD\xCD" + "\xCD\xCD", 50, + "\x4C\x90\x07\xF4\x02\x62\x50\xC6\xBC\x84\x14\xF9\xBF\x50\xC8\x6C" + "\x2D\x72\x35\xDA" }, + { "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C" + "\x0C\x0C\x0C\x0C", 20, + "Test With Truncation", 20, + "\x4C\x1A\x03\x42\x4B\x55\xE0\x7F\xE7\xF2\x7B\xE1\xD5\x8B\xB9\x32" + "\x4A\x9A\x5A\x04" }, + { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", + 80, + "Test Using Larger Than Block-Size Key - Hash Key First", 54, + "\xAA\x4A\xE5\xE1\x52\x72\xD0\x0E\x95\x70\x56\x37\xCE\x8A\x3B\x55" + "\xED\x40\x21\x12" }, + { "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA", + 80, + "Test Using Larger Than Block-Size Key and Larger " + "Than One Block-Size Data", 73, + "\xE8\xE9\x9D\x0F\x45\x23\x7D\x78\x6D\x6B\xBA\xA7\x96\x5C\x78\x08" + "\xBB\xFF\x1A\x91" } + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + base::HMAC hmac(base::HMAC::SHA1); + ASSERT_TRUE(hmac.Init(reinterpret_cast(cases[i].key), + cases[i].key_len)); + std::string data_string(cases[i].data, cases[i].data_len); + unsigned char digest[kDigestSize]; + EXPECT_TRUE(hmac.Sign(data_string, digest, kDigestSize)); + EXPECT_EQ(memcmp(cases[i].digest, digest, kDigestSize), 0); + } +} + +TEST(HMACTest, HMACObjectReuse) { + const char *key = + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA" + "\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA\xAA"; + const int key_len = 80; + + const struct { + const char *data; + const int data_len; + const char *digest; + } cases[] = { + { "Test Using Larger Than Block-Size Key - Hash Key First", 54, + "\xAA\x4A\xE5\xE1\x52\x72\xD0\x0E\x95\x70\x56\x37\xCE\x8A\x3B\x55" + "\xED\x40\x21\x12" }, + { "Test Using Larger Than Block-Size Key and Larger " + "Than One Block-Size Data", 73, + "\xE8\xE9\x9D\x0F\x45\x23\x7D\x78\x6D\x6B\xBA\xA7\x96\x5C\x78\x08" + "\xBB\xFF\x1A\x91" } + }; + + base::HMAC hmac(base::HMAC::SHA1); + ASSERT_TRUE(hmac.Init(reinterpret_cast(key), key_len)); + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + std::string data_string(cases[i].data, cases[i].data_len); + unsigned char digest[kDigestSize]; + EXPECT_TRUE(hmac.Sign(data_string, digest, kDigestSize)); + EXPECT_EQ(memcmp(cases[i].digest, digest, kDigestSize), 0); + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/hmac_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/hmac_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/hmac_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/hmac_win.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,131 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/hmac.h" + +#include +#include + +#include +#include + +#include "base/logging.h" + +namespace base { + +struct HMACPlatformData { + // Windows Crypt API resources. + HCRYPTPROV provider_; + HCRYPTHASH hash_; + HCRYPTKEY hkey_; +}; + +HMAC::HMAC(HashAlgorithm hash_alg) + : hash_alg_(hash_alg), plat_(new HMACPlatformData()) { + // Only SHA-1 digest is supported now. + DCHECK(hash_alg_ == SHA1); +} + +bool HMAC::Init(const unsigned char *key, int key_length) { + if (plat_->provider_ || plat_->hkey_) { + // Init must not be called more than once on the same HMAC object. + NOTREACHED(); + return false; + } + + if (!CryptAcquireContext(&plat_->provider_, NULL, NULL, + PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { + NOTREACHED(); + plat_->provider_ = NULL; + return false; + } + + // This code doesn't work on Win2k because PLAINTEXTKEYBLOB and + // CRYPT_IPSEC_HMAC_KEY are not supported on Windows 2000. PLAINTEXTKEYBLOB + // allows the import of an unencrypted key. For Win2k support, a cubmbersome + // exponent-of-one key procedure must be used: + // http://support.microsoft.com/kb/228786/en-us + // CRYPT_IPSEC_HMAC_KEY allows keys longer than 16 bytes. + + struct KeyBlob { + BLOBHEADER header; + DWORD key_size; + BYTE key_data[1]; + }; + size_t key_blob_size = std::max(offsetof(KeyBlob, key_data) + key_length, + sizeof(KeyBlob)); + std::vector key_blob_storage = std::vector(key_blob_size); + KeyBlob* key_blob = reinterpret_cast(&key_blob_storage[0]); + key_blob->header.bType = PLAINTEXTKEYBLOB; + key_blob->header.bVersion = CUR_BLOB_VERSION; + key_blob->header.reserved = 0; + key_blob->header.aiKeyAlg = CALG_RC2; + key_blob->key_size = key_length; + memcpy(key_blob->key_data, key, key_length); + + if (!CryptImportKey(plat_->provider_, &key_blob_storage[0], + key_blob_storage.size(), 0, CRYPT_IPSEC_HMAC_KEY, + &plat_->hkey_)) { + NOTREACHED(); + plat_->hkey_ = NULL; + return false; + } + + // Destroy the copy of the key. + SecureZeroMemory(key_blob->key_data, key_length); + + return true; +} + +HMAC::~HMAC() { + BOOL ok; + if (plat_->hkey_) { + ok = CryptDestroyKey(plat_->hkey_); + DCHECK(ok); + } + if (plat_->hash_) { + ok = CryptDestroyHash(plat_->hash_); + DCHECK(ok); + } + if (plat_->provider_) { + ok = CryptReleaseContext(plat_->provider_, 0); + DCHECK(ok); + } +} + +bool HMAC::Sign(const std::string& data, + unsigned char* digest, + int digest_length) { + if (!plat_->provider_ || !plat_->hkey_) + return false; + + if (hash_alg_ != SHA1) { + NOTREACHED(); + return false; + } + + if (!CryptCreateHash( + plat_->provider_, CALG_HMAC, plat_->hkey_, 0, &plat_->hash_)) + return false; + + HMAC_INFO hmac_info; + memset(&hmac_info, 0, sizeof(hmac_info)); + hmac_info.HashAlgid = CALG_SHA1; + if (!CryptSetHashParam(plat_->hash_, HP_HMAC_INFO, + reinterpret_cast(&hmac_info), 0)) + return false; + + if (!CryptHashData(plat_->hash_, + reinterpret_cast(data.data()), + static_cast(data.size()), 0)) + return false; + + DWORD sha1_size = digest_length; + if (!CryptGetHashParam(plat_->hash_, HP_HASHVAL, digest, &sha1_size, 0)) + return false; + + return true; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/iat_patch.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/iat_patch.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/iat_patch.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/iat_patch.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,240 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/iat_patch.h" +#include "base/logging.h" + +namespace iat_patch { + +struct InterceptFunctionInformation { + bool finished_operation; + const char* imported_from_module; + const char* function_name; + void* new_function; + void** old_function; + IMAGE_THUNK_DATA** iat_thunk; + DWORD return_code; +}; + +static void* GetIATFunction(IMAGE_THUNK_DATA* iat_thunk) { + if (NULL == iat_thunk) { + NOTREACHED(); + return NULL; + } + + // Works around the 64 bit portability warning: + // The Function member inside IMAGE_THUNK_DATA is really a pointer + // to the IAT function. IMAGE_THUNK_DATA correctly maps to IMAGE_THUNK_DATA32 + // or IMAGE_THUNK_DATA64 for correct pointer size. + union FunctionThunk { + IMAGE_THUNK_DATA thunk; + void* pointer; + } iat_function; + + iat_function.thunk = *iat_thunk; + return iat_function.pointer; +} + +static bool InterceptEnumCallback(const PEImage &image, const char* module, + DWORD ordinal, const char* name, DWORD hint, + IMAGE_THUNK_DATA* iat, void* cookie) { + InterceptFunctionInformation* intercept_information = + reinterpret_cast(cookie); + + if (NULL == intercept_information) { + NOTREACHED(); + return false; + } + + DCHECK(module); + + if ((0 == lstrcmpiA(module, intercept_information->imported_from_module)) && + (NULL != name) && + (0 == lstrcmpiA(name, intercept_information->function_name))) { + // Save the old pointer. + if (NULL != intercept_information->old_function) { + *(intercept_information->old_function) = GetIATFunction(iat); + } + + if (NULL != intercept_information->iat_thunk) { + *(intercept_information->iat_thunk) = iat; + } + + // portability check + COMPILE_ASSERT(sizeof(iat->u1.Function) == + sizeof(intercept_information->new_function), unknown_IAT_thunk_format); + + // Patch the function. + intercept_information->return_code = + ModifyCode(&(iat->u1.Function), + &(intercept_information->new_function), + sizeof(intercept_information->new_function)); + + // Terminate further enumeration. + intercept_information->finished_operation = true; + return false; + } + + return true; +} + +DWORD InterceptImportedFunction(HMODULE module_handle, + const char* imported_from_module, + const char* function_name, void* new_function, + void** old_function, + IMAGE_THUNK_DATA** iat_thunk) { + if ((NULL == module_handle) || (NULL == imported_from_module) || + (NULL == function_name) || (NULL == new_function)) { + NOTREACHED(); + return ERROR_INVALID_PARAMETER; + } + + PEImage target_image(module_handle); + if (!target_image.VerifyMagic()) { + NOTREACHED(); + return ERROR_INVALID_PARAMETER; + } + + InterceptFunctionInformation intercept_information = { + false, + imported_from_module, + function_name, + new_function, + old_function, + iat_thunk, + ERROR_GEN_FAILURE}; + + // First go through the IAT. If we don't find the import we are looking + // for in IAT, search delay import table. + target_image.EnumAllImports(InterceptEnumCallback, &intercept_information); + if (!intercept_information.finished_operation) { + target_image.EnumAllDelayImports(InterceptEnumCallback, + &intercept_information); + } + + return intercept_information.return_code; +} + +DWORD RestoreImportedFunction(void* intercept_function, + void* original_function, + IMAGE_THUNK_DATA* iat_thunk) { + if ((NULL == intercept_function) || (NULL == original_function) || + (NULL == iat_thunk)) { + NOTREACHED(); + return ERROR_INVALID_PARAMETER; + } + + if (GetIATFunction(iat_thunk) != intercept_function) { + // Check if someone else has intercepted on top of us. + // We cannot unpatch in this case, just raise a red flag. + NOTREACHED(); + return ERROR_INVALID_FUNCTION; + } + + return ModifyCode(&(iat_thunk->u1.Function), + &original_function, + sizeof(original_function)); +} + +DWORD ModifyCode(void* old_code, void* new_code, int length) { + if ((NULL == old_code) || (NULL == new_code) || (0 == length)) { + NOTREACHED(); + return ERROR_INVALID_PARAMETER; + } + + // Change the page protection so that we can write. + DWORD error = NO_ERROR; + DWORD old_page_protection = 0; + if (VirtualProtect(old_code, + length, + PAGE_READWRITE, + &old_page_protection)) { + + // Write the data. + CopyMemory(old_code, new_code, length); + + // Restore the old page protection. + error = ERROR_SUCCESS; + VirtualProtect(old_code, + length, + old_page_protection, + &old_page_protection); + } else { + error = GetLastError(); + NOTREACHED(); + } + + return error; +} + +IATPatchFunction::IATPatchFunction() + : module_handle_(NULL), + original_function_(NULL), + iat_thunk_(NULL), + intercept_function_(NULL) { +} + +IATPatchFunction::~IATPatchFunction() { + if (NULL != intercept_function_) { + DWORD error = Unpatch(); + DCHECK_EQ(NO_ERROR, error); + } +} + +DWORD IATPatchFunction::Patch(const wchar_t* module, + const char* imported_from_module, + const char* function_name, + void* new_function) { + DCHECK_EQ(static_cast(NULL), original_function_); + DCHECK_EQ(static_cast(NULL), iat_thunk_); + DCHECK_EQ(static_cast(NULL), intercept_function_); + + HMODULE module_handle = LoadLibraryW(module); + + if (module_handle == NULL) { + NOTREACHED(); + return GetLastError(); + } + + DWORD error = InterceptImportedFunction(module_handle, + imported_from_module, + function_name, + new_function, + &original_function_, + &iat_thunk_); + + if (NO_ERROR == error) { + DCHECK_NE(original_function_, intercept_function_); + module_handle_ = module_handle; + intercept_function_ = new_function; + } else { + FreeLibrary(module_handle); + } + + return error; +} + +DWORD IATPatchFunction::Unpatch() { + DWORD error = RestoreImportedFunction(intercept_function_, + original_function_, + iat_thunk_); + DCHECK(NO_ERROR == error); + + // Hands off the intercept if we fail to unpatch. + // If IATPatchFunction::Unpatch fails during RestoreImportedFunction + // it means that we cannot safely unpatch the import address table + // patch. In this case its better to be hands off the intercept as + // trying to unpatch again in the destructor of IATPatchFunction is + // not going to be any safer + if (module_handle_) + FreeLibrary(module_handle_); + module_handle_ = NULL; + intercept_function_ = NULL; + original_function_ = NULL; + iat_thunk_ = NULL; + + return error; +} + +} // namespace iat_patch diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/iat_patch.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/iat_patch.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/iat_patch.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/iat_patch.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,122 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file declares a helpers to intercept functions from a DLL. +// +// This set of functions are designed to intercept functions for a +// specific DLL imported from another DLL. This is the case when, +// for example, we want to intercept CertDuplicateCertificateContext +// function (exported from crypt32.dll) called by wininet.dll. + +#ifndef BASE_IAT_PATCH_H__ +#define BASE_IAT_PATCH_H__ + +#include +#include "base/basictypes.h" +#include "base/pe_image.h" + +namespace iat_patch { + +// Helper to intercept a function in an import table of a specific +// module. +// +// Arguments: +// module_handle Module to be intercepted +// imported_from_module Module that exports the symbol +// function_name Name of the API to be intercepted +// new_function Interceptor function +// old_function Receives the original function pointer +// iat_thunk Receives pointer to IAT_THUNK_DATA +// for the API from the import table. +// +// Returns: Returns NO_ERROR on success or Windows error code +// as defined in winerror.h +// +DWORD InterceptImportedFunction(HMODULE module_handle, + const char* imported_from_module, + const char* function_name, + void* new_function, + void** old_function, + IMAGE_THUNK_DATA** iat_thunk); + +// Restore intercepted IAT entry with the original function. +// +// Arguments: +// intercept_function Interceptor function +// original_function Receives the original function pointer +// +// Returns: Returns NO_ERROR on success or Windows error code +// as defined in winerror.h +// +DWORD RestoreImportedFunction(void* intercept_function, + void* original_function, + IMAGE_THUNK_DATA* iat_thunk); + +// Change the page protection (of code pages) to writable and copy +// the data at the specified location +// +// Arguments: +// old_code Target location to copy +// new_code Source +// length Number of bytes to copy +// +// Returns: Windows error code (winerror.h). NO_ERROR if successful +// +DWORD ModifyCode(void* old_code, + void* new_code, + int length); + +// A class that encapsulates IAT patching helpers and restores +// the original function in the destructor. +class IATPatchFunction { + public: + IATPatchFunction(); + ~IATPatchFunction(); + + // Intercept a function in an import table of a specific + // module. Save the original function and the import + // table address. These values will be used later + // during Unpatch + // + // Arguments: + // module Module to be intercepted + // imported_from_module Module that exports the 'function_name' + // function_name Name of the API to be intercepted + // + // Returns: Windows error code (winerror.h). NO_ERROR if successful + // + // Note: Patching a function will make the IAT patch take some "ownership" on + // |module|. It will LoadLibrary(module) to keep the DLL alive until a call + // to Unpatch(), which will call FreeLibrary() and allow the module to be + // unloaded. The idea is to help prevent the DLL from going away while a + // patch is still active. + // + DWORD Patch(const wchar_t* module, + const char* imported_from_module, + const char* function_name, + void* new_function); + + // Unpatch the IAT entry using internally saved original + // function. + // + // Returns: Windows error code (winerror.h). NO_ERROR if successful + // + DWORD Unpatch(); + + bool is_patched() const { + return (NULL != intercept_function_); + } + + private: + HMODULE module_handle_; + void* intercept_function_; + void* original_function_; + IMAGE_THUNK_DATA* iat_thunk_; + + DISALLOW_EVIL_CONSTRUCTORS(IATPatchFunction); +}; + +} // namespace iat_patch + +#endif // BASE_IAT_PATCH_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/icu_util.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/icu_util.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/icu_util.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/icu_util.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,92 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#endif + +#include + +#include "base/icu_util.h" + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/sys_string_conversions.h" +#include "unicode/putil.h" +#include "unicode/udata.h" + +#define ICU_UTIL_DATA_FILE 0 +#define ICU_UTIL_DATA_SHARED 1 +#define ICU_UTIL_DATA_STATIC 2 + +#ifndef ICU_UTIL_DATA_IMPL + +#if defined(OS_WIN) +#define ICU_UTIL_DATA_IMPL ICU_UTIL_DATA_SHARED +#elif defined(OS_MACOSX) +#define ICU_UTIL_DATA_IMPL ICU_UTIL_DATA_STATIC +#elif defined(OS_LINUX) +#define ICU_UTIL_DATA_IMPL ICU_UTIL_DATA_FILE +#endif + +#endif // ICU_UTIL_DATA_IMPL + +#if defined(OS_WIN) +#define ICU_UTIL_DATA_SYMBOL "icudt38_dat" +#define ICU_UTIL_DATA_SHARED_MODULE_NAME L"icudt38.dll" +#endif + +namespace icu_util { + +bool Initialize() { +#ifndef NDEBUG + // Assert that we are not called more than once. Even though calling this + // function isn't harmful (ICU can handle it), being called twice probably + // indicates a programming error. + static bool called_once = false; + DCHECK(!called_once); + called_once = true; +#endif + +#if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED) + // We expect to find the ICU data module alongside the current module. + std::wstring data_path; + PathService::Get(base::DIR_MODULE, &data_path); + file_util::AppendToPath(&data_path, ICU_UTIL_DATA_SHARED_MODULE_NAME); + + HMODULE module = LoadLibrary(data_path.c_str()); + if (!module) + return false; + + FARPROC addr = GetProcAddress(module, ICU_UTIL_DATA_SYMBOL); + if (!addr) + return false; + + UErrorCode err = U_ZERO_ERROR; + udata_setCommonData(reinterpret_cast(addr), &err); + return err == U_ZERO_ERROR; +#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC) + // Mac bundles the ICU data in. + return true; +#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) + // For now, expect the data file to be alongside the executable. + // This is sufficient while we work on unit tests, but will eventually + // likely live in a data directory. + FilePath data_path; + bool path_ok = PathService::Get(base::DIR_EXE, &data_path); + DCHECK(path_ok); + u_setDataDirectory(data_path.value().c_str()); + // Only look for the packaged data file; + // the default behavior is to look for individual files. + UErrorCode err = U_ZERO_ERROR; + udata_setFileAccess(UDATA_ONLY_PACKAGES, &err); + return err == U_ZERO_ERROR; +#endif +} + +} // namespace icu_util diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/icu_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/icu_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/icu_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/icu_util.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,16 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_ICU_UTIL_H__ +#define BASE_ICU_UTIL_H__ + +namespace icu_util { + +// Call this function to load ICU's data tables for the current process. This +// function should be called before ICU is used. +bool Initialize(); + +} // namespace icu_util + +#endif // BASE_ICU_UTIL_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/idle_timer.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/idle_timer.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/idle_timer.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/idle_timer.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,161 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/idle_timer.h" + +// We may not want to port idle_timer to Linux, but we have implemented it +// anyway. Define this to 1 to enable the Linux idle timer and then add the +// libs that need to be linked (Xss). +#define ENABLE_XSS_SUPPORT 0 + +#if defined(OS_MACOSX) +#include +#endif + +#if defined(OS_LINUX) && ENABLE_XSS_SUPPORT +// We may not want to port idle_timer to Linux, but we have implemented it +// anyway. Remove the 0 above if we want it. +#include +#include +#include "base/lazy_instance.h" +#include "base/thread_local.h" +#endif + +#include "base/message_loop.h" +#include "base/time.h" + +namespace base { + +#if defined(OS_WIN) +bool OSIdleTimeSource(int32 *milliseconds_interval_since_last_event) { + LASTINPUTINFO lastInputInfo; + lastInputInfo.cbSize = sizeof(lastInputInfo); + if (GetLastInputInfo(&lastInputInfo) == 0) { + return false; + } + int32 last_input_time = lastInputInfo.dwTime; + + // Note: On Windows GetLastInputInfo returns a 32bit value which rolls over + // ~49days. + int32 current_time = GetTickCount(); + int32 delta = current_time - last_input_time; + // delta will go negative if we've been idle for 2GB of ticks. + if (delta < 0) + delta = -delta; + *milliseconds_interval_since_last_event = delta; + return true; +} +#elif defined(OS_MACOSX) +bool OSIdleTimeSource(int32 *milliseconds_interval_since_last_event) { + *milliseconds_interval_since_last_event = + CGEventSourceSecondsSinceLastEventType( + kCGEventSourceStateCombinedSessionState, + kCGAnyInputEventType) * 1000.0; + return true; +} +#elif defined(OS_LINUX) && ENABLE_XSS_SUPPORT +class IdleState { + public: + IdleState() { + int event_base, error_base; + have_idle_info_ = XScreenSaverQueryExtension(GDK_DISPLAY(), &event_base, + &error_base); + if (have_idle_info_) + idle_info_.Set(XScreenSaverAllocInfo()); + } + + ~IdleState() { + if (idle_info_.Get()) { + XFree(idle_info_.Get()); + idle_info_.~ThreadLocalPointer(); + } + } + + int32 IdleTime() { + if (have_idle_info_ && idle_info_.Get()) { + XScreenSaverQueryInfo(GDK_DISPLAY(), GDK_ROOT_WINDOW(), + idle_info_.Get()); + return idle_info_.Get()->idle; + } + return -1; + } + + private: + bool have_idle_info_; + ThreadLocalPointer idle_info_; + + DISALLOW_COPY_AND_ASSIGN(IdleState); +}; + +bool OSIdleTimeSource(int32* milliseconds_interval_since_last_event) { + static LazyInstance state_instance(base::LINKER_INITIALIZED); + IdleState* state = state_instance.Pointer(); + int32 idle_time = state->IdleTime(); + if (0 < idle_time) { + *milliseconds_interval_since_last_event = idle_time; + return true; + } + return false; +} +#endif + +IdleTimer::IdleTimer(TimeDelta idle_time, bool repeat) + : idle_interval_(idle_time), + repeat_(repeat), + idle_time_source_(OSIdleTimeSource) { + DCHECK_EQ(MessageLoop::TYPE_UI, MessageLoop::current()->type()) << + "Requires a thread that processes Windows UI events"; +} + +IdleTimer::~IdleTimer() { + Stop(); +} + +void IdleTimer::Start() { + StartTimer(); +} + +void IdleTimer::Stop() { + timer_.Stop(); +} + +void IdleTimer::Run() { + // Verify we can fire the idle timer. + if (TimeUntilIdle().InMilliseconds() <= 0) { + OnIdle(); + last_time_fired_ = Time::Now(); + } + Stop(); + StartTimer(); // Restart the timer for next run. +} + +void IdleTimer::StartTimer() { + DCHECK(!timer_.IsRunning()); + TimeDelta delay = TimeUntilIdle(); + if (delay.InMilliseconds() < 0) + delay = TimeDelta(); + timer_.Start(delay, this, &IdleTimer::Run); +} + +TimeDelta IdleTimer::CurrentIdleTime() { + int32 interval = 0; + if (idle_time_source_(&interval)) { + return TimeDelta::FromMilliseconds(interval); + } + NOTREACHED(); + return TimeDelta::FromMilliseconds(0); +} + +TimeDelta IdleTimer::TimeUntilIdle() { + TimeDelta time_since_last_fire = Time::Now() - last_time_fired_; + TimeDelta current_idle_time = CurrentIdleTime(); + if (current_idle_time > time_since_last_fire) { + if (repeat_) + return idle_interval_ - time_since_last_fire; + return idle_interval_; + } + return idle_interval_ - current_idle_time; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/idle_timer.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/idle_timer.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/idle_timer.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/idle_timer.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,97 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// IdleTimer is a recurring Timer which runs only when the system is idle. +// System Idle time is defined as not having any user keyboard or mouse +// activity for some period of time. Because the timer is user dependant, it +// is possible for the timer to never fire. +// +// Usage should be for low-priority work, and may look like this: +// +// class MyIdleTimer : public IdleTimer { +// public: +// // This timer will run repeatedly after 5 seconds of idle time +// MyIdleTimer() : IdleTimer(TimeDelta::FromSeconds(5), true) {}; +// virtual void OnIdle() { do something }; +// } +// +// MyIdleTimer *timer = new MyIdleTimer(); +// timer->Start(); +// +// // As with all Timers, the caller must dispose the object. +// delete timer; // Will Stop the timer and cleanup. +// +// NOTE: An IdleTimer can only be used on a thread that processes UI events. +// Such a thread should be running a MessageLoopForUI. + +#ifndef BASE_IDLE_TIMER_H_ +#define BASE_IDLE_TIMER_H_ + +#if defined(OS_WIN) +#include +#endif + +#include "base/basictypes.h" +#include "base/task.h" +#include "base/timer.h" + +namespace base { + +// Function prototype - Get the number of milliseconds that the user has been +// idle. +typedef bool (*IdleTimeSource)(int32 *milliseconds_interval_since_last_event); + +class IdleTimer { + public: + // Create an IdleTimer. + // idle_time: idle time required before this timer can run. + // repeat: true if the timer should fire multiple times per idle, + // false to fire once per idle. + IdleTimer(TimeDelta idle_time, bool repeat); + + // On destruction, the IdleTimer will Stop itself. + virtual ~IdleTimer(); + + // Start the IdleTimer. + void Start(); + + // Stop the IdleTimer. + void Stop(); + + // The method to run when the timer elapses. + virtual void OnIdle() = 0; + + protected: + // Override the IdleTimeSource. + void set_idle_time_source(IdleTimeSource idle_time_source) { + idle_time_source_ = idle_time_source; + } + + private: + // Called when timer_ expires. + void Run(); + + // Start the timer. + void StartTimer(); + + // Gets the number of milliseconds since the last input event. + TimeDelta CurrentIdleTime(); + + // Compute time until idle. Returns 0 if we are now idle. + TimeDelta TimeUntilIdle(); + + TimeDelta idle_interval_; + bool repeat_; + Time last_time_fired_; // The last time the idle timer fired. + // will be 0 until the timer fires the first time. + OneShotTimer timer_; + + IdleTimeSource idle_time_source_; + + DISALLOW_COPY_AND_ASSIGN(IdleTimer); +}; + +} // namespace base + +#endif // BASE_IDLE_TIMER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/idle_timer_none.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/idle_timer_none.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/idle_timer_none.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/idle_timer_none.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,25 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A stub implementation of IdleTimer used to provide symbols needed by Chrome. +// It's unclear whether we need the idle timer in the linux port, so we're +// leaving it unported for now. + +#include "base/idle_timer.h" + +namespace base { + +IdleTimer::IdleTimer(TimeDelta idle_time, bool repeat) { +} + +IdleTimer::~IdleTimer() { +} + +void IdleTimer::Start() { +} + +void IdleTimer::Stop() { +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/idletimer_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/idletimer_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/idletimer_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/idletimer_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,240 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/idle_timer.h" +#include "base/message_loop.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::Time; +using base::TimeDelta; +using base::IdleTimer; + + +// If the timers fire too quickly, it can be tricky to make timer tests +// reliable on all buildbots. This constant sets a minimum timer delta where +// we expect that we should be able to reliably count timers without problems +// due to slight clock/scheduling variances. +const int kSafeTestIntervalMs = 500; + +namespace { + +// We Mock the GetLastInputInfo function to return +// the time stored here. +static Time mock_timer_started; + +bool MockIdleTimeSource(int32 *milliseconds_interval_since_last_event) { + TimeDelta delta = Time::Now() - mock_timer_started; + *milliseconds_interval_since_last_event = + static_cast(delta.InMilliseconds()); + return true; +} + +// TestIdle task fires after 100ms of idle time. +class TestIdleTask : public IdleTimer { + public: + TestIdleTask(bool repeat) + : IdleTimer(TimeDelta::FromMilliseconds(kSafeTestIntervalMs), repeat), + idle_counter_(0) { + set_idle_time_source(MockIdleTimeSource); + } + + int get_idle_counter() { return idle_counter_; } + + virtual void OnIdle() { + idle_counter_++; + } + + private: + int idle_counter_; +}; + +// A task to help us quit the test. +class TestFinishedTask { + public: + TestFinishedTask() {} + void Run() { + MessageLoop::current()->Quit(); + } +}; + +// A timer which resets the idle clock. +class ResetIdleTask { + public: + ResetIdleTask() {} + void Run() { + mock_timer_started = Time::Now(); + } +}; + +class IdleTimerTest : public testing::Test { + private: + // IdleTimer requires a UI message loop on the current thread. + MessageLoopForUI message_loop_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// NoRepeat tests: +// A non-repeating idle timer will fire once on idle, and +// then will not fire again unless it goes non-idle first. + +TEST_F(IdleTimerTest, NoRepeatIdle) { + // Create an IdleTimer, which should fire once after 500ms. + // Create a Quit timer which will fire after 1s. + // Verify that we fired exactly once. + + mock_timer_started = Time::Now(); + TestIdleTask test_task(false); + + TestFinishedTask finish_task; + base::OneShotTimer timer; + timer.Start(TimeDelta::FromMilliseconds(2 * kSafeTestIntervalMs), + &finish_task, &TestFinishedTask::Run); + + test_task.Start(); + MessageLoop::current()->Run(); + + EXPECT_EQ(test_task.get_idle_counter(), 1); +} + +TEST_F(IdleTimerTest, NoRepeatFlipIdleOnce) { + // Create an IdleTimer, which should fire once after 500ms. + // Create a Quit timer which will fire after 5s. + // Create a timer to reset once, idle after 2s. + // Verify that we fired exactly twice. + + mock_timer_started = Time::Now(); + TestIdleTask test_task(false); + + TestFinishedTask finish_task; + ResetIdleTask reset_task; + + base::OneShotTimer t1; + t1.Start(TimeDelta::FromMilliseconds(10 * kSafeTestIntervalMs), &finish_task, + &TestFinishedTask::Run); + + base::OneShotTimer t2; + t2.Start(TimeDelta::FromMilliseconds(4 * kSafeTestIntervalMs), &reset_task, + &ResetIdleTask::Run); + + test_task.Start(); + MessageLoop::current()->Run(); + + EXPECT_EQ(test_task.get_idle_counter(), 2); +} + +TEST_F(IdleTimerTest, NoRepeatNotIdle) { + // Create an IdleTimer, which should fire once after 500ms. + // Create a Quit timer which will fire after 5s. + // Create a timer to reset idle every 50ms. + // Verify that we never fired. + + mock_timer_started = Time::Now(); + TestIdleTask test_task(false); + + TestFinishedTask finish_task; + ResetIdleTask reset_task; + + base::OneShotTimer t; + t.Start(TimeDelta::FromMilliseconds(10 * kSafeTestIntervalMs), &finish_task, + &TestFinishedTask::Run); + + base::RepeatingTimer reset_timer; + reset_timer.Start(TimeDelta::FromMilliseconds(50), &reset_task, + &ResetIdleTask::Run); + + test_task.Start(); + + MessageLoop::current()->Run(); + + reset_timer.Stop(); + + EXPECT_EQ(test_task.get_idle_counter(), 0); +} + +/////////////////////////////////////////////////////////////////////////////// +// Repeat tests: +// A repeating idle timer will fire repeatedly on each interval, as long +// as it has been idle. So, if the machine remains idle, it will continue +// firing over and over. + +TEST_F(IdleTimerTest, Repeat) { + // Create an IdleTimer, which should fire repeatedly after 500ms. + // Create a Quit timer which will fire after 1.5s. + // Verify that we fired 2-3 times. + mock_timer_started = Time::Now(); + TestIdleTask test_task(true); + + TestFinishedTask finish_task; + + base::OneShotTimer t; + t.Start(TimeDelta::FromMilliseconds(kSafeTestIntervalMs * 3), &finish_task, + &TestFinishedTask::Run); + + test_task.Start(); + MessageLoop::current()->Run(); + + // In a perfect world, the idle_counter should be 2. However, + // due to timer 'slop', accept 2 or 3. + EXPECT_GE(test_task.get_idle_counter(), 2); + EXPECT_LE(test_task.get_idle_counter(), 3); +} + +TEST_F(IdleTimerTest, RepeatIdleReset) { + // Create an IdleTimer, which should fire repeatedly after 500ms. + // Create a Quit timer which will fire after 5s. + // Create a reset timer, which fires after 2500ms + // Verify that we fired 8-10 times. + mock_timer_started = Time::Now(); + TestIdleTask test_task(true); + + ResetIdleTask reset_task; + TestFinishedTask finish_task; + + base::OneShotTimer t1; + t1.Start(TimeDelta::FromMilliseconds(10 * kSafeTestIntervalMs), &finish_task, + &TestFinishedTask::Run); + + base::OneShotTimer t2; + t2.Start(TimeDelta::FromMilliseconds(5 * kSafeTestIntervalMs), &reset_task, + &ResetIdleTask::Run); + + test_task.Start(); + MessageLoop::current()->Run(); + + // In a perfect world, the idle_counter should be 9. However, + // since timers aren't guaranteed to fire perfectly, this can + // be less. Accept 8-10. + EXPECT_GE(test_task.get_idle_counter(), 8); + EXPECT_LE(test_task.get_idle_counter(), 10); +} + +TEST_F(IdleTimerTest, RepeatNotIdle) { + // Create an IdleTimer, which should fire repeatedly after 500ms. + // Create a Quit timer which will fire after 4s. + // Create a timer to reset idle every 50ms. + // Verify that we never fired. + + mock_timer_started = Time::Now(); + TestIdleTask test_task(true); + + TestFinishedTask finish_task; + ResetIdleTask reset_task; + + base::OneShotTimer t; + t.Start(TimeDelta::FromMilliseconds(8 * kSafeTestIntervalMs), &finish_task, + &TestFinishedTask::Run); + + base::RepeatingTimer reset_timer; + reset_timer.Start(TimeDelta::FromMilliseconds(50), &reset_task, + &ResetIdleTask::Run); + + test_task.Start(); + MessageLoop::current()->Run(); + + reset_timer.Stop(); + + EXPECT_EQ(test_task.get_idle_counter(), 0); +} + +} // namespace diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/id_map.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/id_map.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/id_map.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/id_map.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,93 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_ID_MAP_H__ +#define BASE_ID_MAP_H__ + +#include "base/basictypes.h" +#include "base/hash_tables.h" +#include "base/logging.h" + +// This object maintains a list of IDs that can be quickly converted to +// pointers to objects. It is implemented as a hash table, optimized for +// relatively small data sets (in the common case, there will be exactly one +// item in the list). +// +// Items can be inserted into the container with arbitrary ID, but the caller +// must ensure they are unique. Inserting IDs and relying on automatically +// generated ones is not allowed because they can collide. +template +class IDMap { + private: + typedef base::hash_map HashTable; + typedef typename HashTable::iterator iterator; + + public: + // support const iterators over the items + // Note, use iterator->first to get the ID, iterator->second to get the T* + typedef typename HashTable::const_iterator const_iterator; + + IDMap() : next_id_(1) { + } + IDMap(const IDMap& other) : next_id_(other.next_id_), + data_(other.data_) { + } + + const_iterator begin() const { + return data_.begin(); + } + const_iterator end() const { + return data_.end(); + } + + // Adds a view with an automatically generated unique ID. See AddWithID. + int32 Add(T* data) { + int32 this_id = next_id_; + DCHECK(data_.find(this_id) == data_.end()) << "Inserting duplicate item"; + data_[this_id] = data; + next_id_++; + return this_id; + } + + // Adds a new data member with the specified ID. The ID must not be in + // the list. The caller either must generate all unique IDs itself and use + // this function, or allow this object to generate IDs and call Add. These + // two methods may not be mixed, or duplicate IDs may be generated + void AddWithID(T* data, int32 id) { + DCHECK(data_.find(id) == data_.end()) << "Inserting duplicate item"; + data_[id] = data; + } + + void Remove(int32 id) { + iterator i = data_.find(id); + if (i == data_.end()) { + NOTREACHED() << "Attempting to remove an item not in the list"; + return; + } + data_.erase(i); + } + + bool IsEmpty() const { + return data_.empty(); + } + + T* Lookup(int32 id) const { + const_iterator i = data_.find(id); + if (i == data_.end()) + return NULL; + return i->second; + } + + size_t size() const { + return data_.size(); + } + + protected: + // The next ID that we will return from Add() + int32 next_id_; + + HashTable data_; +}; + +#endif // BASE_ID_MAP_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/image_util.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/image_util.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/image_util.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/image_util.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,72 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +#include +#include +#include + +#include "base/image_util.h" +#include "base/process_util.h" + +// imagehlp.dll appears to ship in all win versions after Win95. +// nsylvain verified it is present in win2k. +// Using #pragma comment for dependency, instead of LoadLibrary/GetProcAddress. +#pragma comment(lib, "imagehlp.lib") + +namespace image_util { + +// ImageMetrics +ImageMetrics::ImageMetrics(HANDLE process) : process_(process) { +} + +ImageMetrics::~ImageMetrics() { +} + +bool ImageMetrics::GetDllImageSectionData(const std::string& loaded_dll_name, + ImageSectionsData* section_sizes) { + // Get a handle to the loaded DLL + HMODULE the_dll = GetModuleHandleA(loaded_dll_name.c_str()); + char full_filename[MAX_PATH]; + // Get image path + if (GetModuleFileNameExA(process_, the_dll, full_filename, MAX_PATH)) { + return GetImageSectionSizes(full_filename, section_sizes); + } + return false; +} + +bool ImageMetrics::GetProcessImageSectionData(ImageSectionsData* + section_sizes) { + char exe_path[MAX_PATH]; + // Get image path + if (GetModuleFileNameExA(process_, NULL, exe_path, MAX_PATH)) { + return GetImageSectionSizes(exe_path, section_sizes); + } + return false; +} + +// private +bool ImageMetrics::GetImageSectionSizes(char* qualified_path, + ImageSectionsData* result) { + LOADED_IMAGE li; + // TODO (timsteele): There is no unicode version for MapAndLoad, hence + // why ansi functions are used in this class. Should we try and rewrite + // this call ourselves to be safe? + if (MapAndLoad(qualified_path, 0, &li, FALSE, TRUE)) { + IMAGE_SECTION_HEADER* section_header = li.Sections; + for (unsigned i = 0; i < li.NumberOfSections; i++, section_header++) { + std::string name(reinterpret_cast(section_header->Name)); + ImageSectionData data(name, section_header->Misc.VirtualSize ? + section_header->Misc.VirtualSize : + section_header->SizeOfRawData); + // copy into result + result->push_back(data); + } + } else { + // map and load failed + return false; + } + UnMapAndLoad(&li); + return true; +} + +} // namespace image_util diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/image_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/image_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/image_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/image_util.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,66 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file/namespace contains utility functions for gathering +// information about PE (Portable Executable) headers within +// images (dll's / exe's ) + +#ifndef BASE_IMAGE_UTIL_H_ +#define BASE_IMAGE_UTIL_H_ + +#include +#include + +#include "base/basictypes.h" + +namespace image_util { + +// Contains both the PE section name (.text, .reloc etc) and its size. +struct ImageSectionData { + ImageSectionData (const std::string& section_name, size_t section_size) + : name (section_name), + size_in_bytes(section_size) { + } + + std::string name; + size_t size_in_bytes; +}; + +typedef std::vector ImageSectionsData; + +// Provides image statistics for modules of a specified process, or for the +// specified process' own executable file. To use, invoke CreateImageMetrics() +// to get an instance for a specified process, then access the information via +// methods. +class ImageMetrics { + public: + // Creates an ImageMetrics instance for given process owned by + // the caller. + explicit ImageMetrics(HANDLE process); + ~ImageMetrics(); + + // Fills a vector of ImageSectionsData containing name/size info + // for every section found in the specified dll's PE section table. + // The DLL must be loaded by the process associated with this ImageMetrics + // instance. + bool GetDllImageSectionData(const std::string& loaded_dll_name, + ImageSectionsData* section_sizes); + + // Fills a vector if ImageSectionsData containing name/size info + // for every section found in the executable file of the process + // associated with this ImageMetrics instance. + bool GetProcessImageSectionData(ImageSectionsData* section_sizes); + + private: + // Helper for GetDllImageSectionData and GetProcessImageSectionData + bool GetImageSectionSizes(char* qualified_path, ImageSectionsData* result); + + HANDLE process_; + + DISALLOW_COPY_AND_ASSIGN(ImageMetrics); +}; + +} // namespace image_util + +#endif // BASE_IMAGE_UTIL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/json_reader.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/json_reader.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/json_reader.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/json_reader.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,641 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/json_reader.h" + +#include "base/float_util.h" +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "base/values.h" + +static const JSONReader::Token kInvalidToken(JSONReader::Token::INVALID_TOKEN, + 0, 0); +static const int kStackLimit = 100; + +namespace { + +inline int HexToInt(wchar_t c) { + if ('0' <= c && c <= '9') { + return c - '0'; + } else if ('A' <= c && c <= 'F') { + return c - 'A' + 10; + } else if ('a' <= c && c <= 'f') { + return c - 'a' + 10; + } + NOTREACHED(); + return 0; +} + +// A helper method for ParseNumberToken. It reads an int from the end of +// token. The method returns false if there is no valid integer at the end of +// the token. +bool ReadInt(JSONReader::Token& token, bool can_have_leading_zeros) { + wchar_t first = token.NextChar(); + int len = 0; + + // Read in more digits + wchar_t c = first; + while ('\0' != c && '0' <= c && c <= '9') { + ++token.length; + ++len; + c = token.NextChar(); + } + // We need at least 1 digit. + if (len == 0) + return false; + + if (!can_have_leading_zeros && len > 1 && '0' == first) + return false; + + return true; +} + +// A helper method for ParseStringToken. It reads |digits| hex digits from the +// token. If the sequence if digits is not valid (contains other characters), +// the method returns false. +bool ReadHexDigits(JSONReader::Token& token, int digits) { + for (int i = 1; i <= digits; ++i) { + wchar_t c = *(token.begin + token.length + i); + if ('\0' == c) + return false; + if (!(('0' <= c && c <= '9') || ('a' <= c && c <= 'f') || + ('A' <= c && c <= 'F'))) { + return false; + } + } + + token.length += digits; + return true; +} + +} // anonymous namespace + +const char* JSONReader::kBadRootElementType = + "Root value must be an array or object."; +const char* JSONReader::kInvalidEscape = + "Invalid escape sequence."; +const char* JSONReader::kSyntaxError = + "Syntax error."; +const char* JSONReader::kTrailingComma = + "Trailing comma not allowed."; +const char* JSONReader::kTooMuchNesting = + "Too much nesting."; +const char* JSONReader::kUnexpectedDataAfterRoot = + "Unexpected data after root element."; +const char* JSONReader::kUnsupportedEncoding = + "Unsupported encoding. JSON must be UTF-8."; +const char* JSONReader::kUnquotedDictionaryKey = + "Dictionary keys must be quoted."; + +/* static */ +Value* JSONReader::Read(const std::string& json, + bool allow_trailing_comma) { + return ReadAndReturnError(json, allow_trailing_comma, NULL); +} + +/* static */ +Value* JSONReader::ReadAndReturnError(const std::string& json, + bool allow_trailing_comma, + std::string *error_message_out) { + JSONReader reader = JSONReader(); + Value* root = reader.JsonToValue(json, true, allow_trailing_comma); + if (root) + return root; + + if (error_message_out) + *error_message_out = reader.error_message(); + + return NULL; +} + +/* static */ +std::string JSONReader::FormatErrorMessage(int line, int column, + const char* description) { + return StringPrintf("Line: %i, column: %i, %s", + line, column, description); +} + +JSONReader::JSONReader() + : start_pos_(NULL), json_pos_(NULL), stack_depth_(0), + allow_trailing_comma_(false) {} + +Value* JSONReader::JsonToValue(const std::string& json, bool check_root, + bool allow_trailing_comma) { + // The input must be in UTF-8. + if (!IsStringUTF8(json.c_str())) { + error_message_ = kUnsupportedEncoding; + return NULL; + } + + // The conversion from UTF8 to wstring removes null bytes for us + // (a good thing). + std::wstring json_wide(UTF8ToWide(json)); + start_pos_ = json_wide.c_str(); + + // When the input JSON string starts with a UTF-8 Byte-Order-Mark + // (0xEF, 0xBB, 0xBF), the UTF8ToWide() function converts it to a Unicode + // BOM (U+FEFF). To avoid the JSONReader::BuildValue() function from + // mis-treating a Unicode BOM as an invalid character and returning NULL, + // skip a converted Unicode BOM if it exists. + if (!json_wide.empty() && start_pos_[0] == 0xFEFF) { + ++start_pos_; + } + + json_pos_ = start_pos_; + allow_trailing_comma_ = allow_trailing_comma; + stack_depth_ = 0; + error_message_.clear(); + + scoped_ptr root(BuildValue(check_root)); + if (root.get()) { + if (ParseToken().type == Token::END_OF_INPUT) { + return root.release(); + } else { + SetErrorMessage(kUnexpectedDataAfterRoot, json_pos_); + } + } + + // Default to calling errors "syntax errors". + if (error_message_.empty()) + SetErrorMessage(kSyntaxError, json_pos_); + + return NULL; +} + +Value* JSONReader::BuildValue(bool is_root) { + ++stack_depth_; + if (stack_depth_ > kStackLimit) { + SetErrorMessage(kTooMuchNesting, json_pos_); + return NULL; + } + + Token token = ParseToken(); + // The root token must be an array or an object. + if (is_root && token.type != Token::OBJECT_BEGIN && + token.type != Token::ARRAY_BEGIN) { + SetErrorMessage(kBadRootElementType, json_pos_); + return NULL; + } + + scoped_ptr node; + + switch (token.type) { + case Token::END_OF_INPUT: + case Token::INVALID_TOKEN: + return NULL; + + case Token::NULL_TOKEN: + node.reset(Value::CreateNullValue()); + break; + + case Token::BOOL_TRUE: + node.reset(Value::CreateBooleanValue(true)); + break; + + case Token::BOOL_FALSE: + node.reset(Value::CreateBooleanValue(false)); + break; + + case Token::NUMBER: + node.reset(DecodeNumber(token)); + if (!node.get()) + return NULL; + break; + + case Token::STRING: + node.reset(DecodeString(token)); + if (!node.get()) + return NULL; + break; + + case Token::ARRAY_BEGIN: + { + json_pos_ += token.length; + token = ParseToken(); + + node.reset(new ListValue()); + while (token.type != Token::ARRAY_END) { + Value* array_node = BuildValue(false); + if (!array_node) + return NULL; + static_cast(node.get())->Append(array_node); + + // After a list value, we expect a comma or the end of the list. + token = ParseToken(); + if (token.type == Token::LIST_SEPARATOR) { + json_pos_ += token.length; + token = ParseToken(); + // Trailing commas are invalid according to the JSON RFC, but some + // consumers need the parsing leniency, so handle accordingly. + if (token.type == Token::ARRAY_END) { + if (!allow_trailing_comma_) { + SetErrorMessage(kTrailingComma, json_pos_); + return NULL; + } + // Trailing comma OK, stop parsing the Array. + break; + } + } else if (token.type != Token::ARRAY_END) { + // Unexpected value after list value. Bail out. + return NULL; + } + } + if (token.type != Token::ARRAY_END) { + return NULL; + } + break; + } + + case Token::OBJECT_BEGIN: + { + json_pos_ += token.length; + token = ParseToken(); + + node.reset(new DictionaryValue); + while (token.type != Token::OBJECT_END) { + if (token.type != Token::STRING) { + SetErrorMessage(kUnquotedDictionaryKey, json_pos_); + return NULL; + } + scoped_ptr dict_key_value(DecodeString(token)); + if (!dict_key_value.get()) + return NULL; + + // Convert the key into a wstring. + std::wstring dict_key; + bool success = dict_key_value->GetAsString(&dict_key); + DCHECK(success); + + json_pos_ += token.length; + token = ParseToken(); + if (token.type != Token::OBJECT_PAIR_SEPARATOR) + return NULL; + + json_pos_ += token.length; + token = ParseToken(); + Value* dict_value = BuildValue(false); + if (!dict_value) + return NULL; + static_cast(node.get())->Set(dict_key, dict_value); + + // After a key/value pair, we expect a comma or the end of the + // object. + token = ParseToken(); + if (token.type == Token::LIST_SEPARATOR) { + json_pos_ += token.length; + token = ParseToken(); + // Trailing commas are invalid according to the JSON RFC, but some + // consumers need the parsing leniency, so handle accordingly. + if (token.type == Token::OBJECT_END) { + if (!allow_trailing_comma_) { + SetErrorMessage(kTrailingComma, json_pos_); + return NULL; + } + // Trailing comma OK, stop parsing the Object. + break; + } + } else if (token.type != Token::OBJECT_END) { + // Unexpected value after last object value. Bail out. + return NULL; + } + } + if (token.type != Token::OBJECT_END) + return NULL; + + break; + } + + default: + // We got a token that's not a value. + return NULL; + } + json_pos_ += token.length; + + --stack_depth_; + return node.release(); +} + +JSONReader::Token JSONReader::ParseNumberToken() { + // We just grab the number here. We validate the size in DecodeNumber. + // According to RFC4627, a valid number is: [minus] int [frac] [exp] + Token token(Token::NUMBER, json_pos_, 0); + wchar_t c = *json_pos_; + if ('-' == c) { + ++token.length; + c = token.NextChar(); + } + + if (!ReadInt(token, false)) + return kInvalidToken; + + // Optional fraction part + c = token.NextChar(); + if ('.' == c) { + ++token.length; + if (!ReadInt(token, true)) + return kInvalidToken; + c = token.NextChar(); + } + + // Optional exponent part + if ('e' == c || 'E' == c) { + ++token.length; + c = token.NextChar(); + if ('-' == c || '+' == c) { + ++token.length; + c = token.NextChar(); + } + if (!ReadInt(token, true)) + return kInvalidToken; + } + + return token; +} + +Value* JSONReader::DecodeNumber(const Token& token) { + const std::wstring num_string(token.begin, token.length); + + int num_int; + if (StringToInt(WideToUTF16Hack(num_string), &num_int)) + return Value::CreateIntegerValue(num_int); + + double num_double; + if (StringToDouble(WideToUTF16Hack(num_string), &num_double) && + base::IsFinite(num_double)) + return Value::CreateRealValue(num_double); + + return NULL; +} + +JSONReader::Token JSONReader::ParseStringToken() { + Token token(Token::STRING, json_pos_, 1); + wchar_t c = token.NextChar(); + while ('\0' != c) { + if ('\\' == c) { + ++token.length; + c = token.NextChar(); + // Make sure the escaped char is valid. + switch (c) { + case 'x': + if (!ReadHexDigits(token, 2)) { + SetErrorMessage(kInvalidEscape, json_pos_ + token.length); + return kInvalidToken; + } + break; + case 'u': + if (!ReadHexDigits(token, 4)) { + SetErrorMessage(kInvalidEscape, json_pos_ + token.length); + return kInvalidToken; + } + break; + case '\\': + case '/': + case 'b': + case 'f': + case 'n': + case 'r': + case 't': + case 'v': + case '"': + break; + default: + SetErrorMessage(kInvalidEscape, json_pos_ + token.length); + return kInvalidToken; + } + } else if ('"' == c) { + ++token.length; + return token; + } + ++token.length; + c = token.NextChar(); + } + return kInvalidToken; +} + +Value* JSONReader::DecodeString(const Token& token) { + std::wstring decoded_str; + decoded_str.reserve(token.length - 2); + + for (int i = 1; i < token.length - 1; ++i) { + wchar_t c = *(token.begin + i); + if ('\\' == c) { + ++i; + c = *(token.begin + i); + switch (c) { + case '"': + case '/': + case '\\': + decoded_str.push_back(c); + break; + case 'b': + decoded_str.push_back('\b'); + break; + case 'f': + decoded_str.push_back('\f'); + break; + case 'n': + decoded_str.push_back('\n'); + break; + case 'r': + decoded_str.push_back('\r'); + break; + case 't': + decoded_str.push_back('\t'); + break; + case 'v': + decoded_str.push_back('\v'); + break; + + case 'x': + decoded_str.push_back((HexToInt(*(token.begin + i + 1)) << 4) + + HexToInt(*(token.begin + i + 2))); + i += 2; + break; + case 'u': + decoded_str.push_back((HexToInt(*(token.begin + i + 1)) << 12 ) + + (HexToInt(*(token.begin + i + 2)) << 8) + + (HexToInt(*(token.begin + i + 3)) << 4) + + HexToInt(*(token.begin + i + 4))); + i += 4; + break; + + default: + // We should only have valid strings at this point. If not, + // ParseStringToken didn't do it's job. + NOTREACHED(); + return NULL; + } + } else { + // Not escaped + decoded_str.push_back(c); + } + } + return Value::CreateStringValue(decoded_str); +} + +JSONReader::Token JSONReader::ParseToken() { + static const std::wstring kNullString(L"null"); + static const std::wstring kTrueString(L"true"); + static const std::wstring kFalseString(L"false"); + + EatWhitespaceAndComments(); + + Token token(Token::INVALID_TOKEN, 0, 0); + switch (*json_pos_) { + case '\0': + token.type = Token::END_OF_INPUT; + break; + + case 'n': + if (NextStringMatch(kNullString)) + token = Token(Token::NULL_TOKEN, json_pos_, 4); + break; + + case 't': + if (NextStringMatch(kTrueString)) + token = Token(Token::BOOL_TRUE, json_pos_, 4); + break; + + case 'f': + if (NextStringMatch(kFalseString)) + token = Token(Token::BOOL_FALSE, json_pos_, 5); + break; + + case '[': + token = Token(Token::ARRAY_BEGIN, json_pos_, 1); + break; + + case ']': + token = Token(Token::ARRAY_END, json_pos_, 1); + break; + + case ',': + token = Token(Token::LIST_SEPARATOR, json_pos_, 1); + break; + + case '{': + token = Token(Token::OBJECT_BEGIN, json_pos_, 1); + break; + + case '}': + token = Token(Token::OBJECT_END, json_pos_, 1); + break; + + case ':': + token = Token(Token::OBJECT_PAIR_SEPARATOR, json_pos_, 1); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '-': + token = ParseNumberToken(); + break; + + case '"': + token = ParseStringToken(); + break; + } + return token; +} + +bool JSONReader::NextStringMatch(const std::wstring& str) { + for (size_t i = 0; i < str.length(); ++i) { + if ('\0' == *json_pos_) + return false; + if (*(json_pos_ + i) != str[i]) + return false; + } + return true; +} + +void JSONReader::EatWhitespaceAndComments() { + while ('\0' != *json_pos_) { + switch (*json_pos_) { + case ' ': + case '\n': + case '\r': + case '\t': + ++json_pos_; + break; + case '/': + // TODO(tc): This isn't in the RFC so it should be a parser flag. + if (!EatComment()) + return; + break; + default: + // Not a whitespace char, just exit. + return; + } + } +} + +bool JSONReader::EatComment() { + if ('/' != *json_pos_) + return false; + + wchar_t next_char = *(json_pos_ + 1); + if ('/' == next_char) { + // Line comment, read until \n or \r + json_pos_ += 2; + while ('\0' != *json_pos_) { + switch (*json_pos_) { + case '\n': + case '\r': + ++json_pos_; + return true; + default: + ++json_pos_; + } + } + } else if ('*' == next_char) { + // Block comment, read until */ + json_pos_ += 2; + while ('\0' != *json_pos_) { + switch (*json_pos_) { + case '*': + if ('/' == *(json_pos_ + 1)) { + json_pos_ += 2; + return true; + } + default: + ++json_pos_; + } + } + } else { + return false; + } + return true; +} + +void JSONReader::SetErrorMessage(const char* description, + const wchar_t* error_pos) { + int line_number = 1; + int column_number = 1; + + // Figure out the line and column the error occured at. + for (const wchar_t* pos = start_pos_; pos != error_pos; ++pos) { + if (*pos == '\0') { + NOTREACHED(); + return; + } + + if (*pos == '\n') { + ++line_number; + column_number = 1; + } else { + ++column_number; + } + } + + error_message_ = FormatErrorMessage(line_number, column_number, description); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/json_reader.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/json_reader.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/json_reader.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/json_reader.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,186 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// A JSON parser. Converts strings of JSON into a Value object (see +// base/values.h). +// http://www.ietf.org/rfc/rfc4627.txt?number=4627 +// +// Known limitations/deviations from the RFC: +// - Only knows how to parse ints within the range of a signed 32 bit int and +// decimal numbers within a double. +// - Assumes input is encoded as UTF8. The spec says we should allow UTF-16 +// (BE or LE) and UTF-32 (BE or LE) as well. +// - We limit nesting to 100 levels to prevent stack overflow (this is allowed +// by the RFC). +// - A Unicode FAQ ("http://unicode.org/faq/utf_bom.html") writes a data +// stream may start with a Unicode Byte-Order-Mark (U+FEFF), i.e. the input +// UTF-8 string for the JSONReader::JsonToValue() function may start with a +// UTF-8 BOM (0xEF, 0xBB, 0xBF). +// To avoid the function from mis-treating a UTF-8 BOM as an invalid +// character, the function skips a Unicode BOM at the beginning of the +// Unicode string (converted from the input UTF-8 string) before parsing it. +// +// TODO(tc): Add a parsing option to to relax object keys being wrapped in +// double quotes +// TODO(tc): Add an option to disable comment stripping +// TODO(aa): Consider making the constructor public and the static Read() method +// only a convenience for the common uses with more complex configuration going +// on the instance. + +#ifndef BASE_JSON_READER_H_ +#define BASE_JSON_READER_H_ + +#include + +#include "base/basictypes.h" +#include "testing/gtest/include/gtest/gtest_prod.h" + +class Value; + +class JSONReader { + public: + // A struct to hold a JS token. + class Token { + public: + enum Type { + OBJECT_BEGIN, // { + OBJECT_END, // } + ARRAY_BEGIN, // [ + ARRAY_END, // ] + STRING, + NUMBER, + BOOL_TRUE, // true + BOOL_FALSE, // false + NULL_TOKEN, // null + LIST_SEPARATOR, // , + OBJECT_PAIR_SEPARATOR, // : + END_OF_INPUT, + INVALID_TOKEN, + }; + Token(Type t, const wchar_t* b, int len) + : type(t), begin(b), length(len) {} + + Type type; + + // A pointer into JSONReader::json_pos_ that's the beginning of this token. + const wchar_t* begin; + + // End should be one char past the end of the token. + int length; + + // Get the character that's one past the end of this token. + wchar_t NextChar() { + return *(begin + length); + } + }; + + // Error messages that can be returned. + static const char* kBadRootElementType; + static const char* kInvalidEscape; + static const char* kSyntaxError; + static const char* kTrailingComma; + static const char* kTooMuchNesting; + static const char* kUnexpectedDataAfterRoot; + static const char* kUnsupportedEncoding; + static const char* kUnquotedDictionaryKey; + + JSONReader(); + + // Reads and parses |json|, returning a Value. The caller owns the returned + // instance. If |json| is not a properly formed JSON string, returns NULL. + // If |allow_trailing_comma| is true, we will ignore trailing commas in + // objects and arrays even though this goes against the RFC. + static Value* Read(const std::string& json, bool allow_trailing_comma); + + // Reads and parses |json| like Read(). |error_message_out| is optional. If + // specified and NULL is returned, |error_message_out| will be populated with + // a string describing the error. Otherwise, |error_message_out| is + // unmodified. + static Value* ReadAndReturnError(const std::string& json, + bool allow_trailing_comma, + std::string* error_message_out); + + // Returns the error message if the last call to JsonToValue() failed. If the + // last call did not fail, returns a valid empty string. + std::string error_message() { return error_message_; } + + // Reads and parses |json|, returning a Value. The caller owns the returned + // instance. If |json| is not a properly formed JSON string, returns NULL and + // a detailed error can be retrieved from |error_message()|. + // If |check_root| is true, we require that the root object be an object or + // array. Otherwise, it can be any valid JSON type. + // If |allow_trailing_comma| is true, we will ignore trailing commas in + // objects and arrays even though this goes against the RFC. + Value* JsonToValue(const std::string& json, bool check_root, + bool allow_trailing_comma); + + private: + static std::string FormatErrorMessage(int line, int column, + const char* description); + + DISALLOW_EVIL_CONSTRUCTORS(JSONReader); + + FRIEND_TEST(JSONReaderTest, Reading); + FRIEND_TEST(JSONReaderTest, ErrorMessages); + + // Recursively build Value. Returns NULL if we don't have a valid JSON + // string. If |is_root| is true, we verify that the root element is either + // an object or an array. + Value* BuildValue(bool is_root); + + // Parses a sequence of characters into a Token::NUMBER. If the sequence of + // characters is not a valid number, returns a Token::INVALID_TOKEN. Note + // that DecodeNumber is used to actually convert from a string to an + // int/double. + Token ParseNumberToken(); + + // Try and convert the substring that token holds into an int or a double. If + // we can (ie., no overflow), return the value, else return NULL. + Value* DecodeNumber(const Token& token); + + // Parses a sequence of characters into a Token::STRING. If the sequence of + // characters is not a valid string, returns a Token::INVALID_TOKEN. Note + // that DecodeString is used to actually decode the escaped string into an + // actual wstring. + Token ParseStringToken(); + + // Convert the substring into a value string. This should always succeed + // (otherwise ParseStringToken would have failed). + Value* DecodeString(const Token& token); + + // Grabs the next token in the JSON stream. This does not increment the + // stream so it can be used to look ahead at the next token. + Token ParseToken(); + + // Increments |json_pos_| past leading whitespace and comments. + void EatWhitespaceAndComments(); + + // If |json_pos_| is at the start of a comment, eat it, otherwise, returns + // false. + bool EatComment(); + + // Checks if |json_pos_| matches str. + bool NextStringMatch(const std::wstring& str); + + // Creates the error message that will be returned to the caller. The current + // line and column are determined and added into the final message. + void SetErrorMessage(const char* description, const wchar_t* error_pos); + + // Pointer to the starting position in the input string. + const wchar_t* start_pos_; + + // Pointer to the current position in the input string. + const wchar_t* json_pos_; + + // Used to keep track of how many nested lists/dicts there are. + int stack_depth_; + + // A parser flag that allows trailing commas in objects and arrays. + bool allow_trailing_comma_; + + // Contains the error message for the last call to JsonToValue(), if any. + std::string error_message_; +}; + +#endif // BASE_JSON_READER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/json_reader_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/json_reader_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/json_reader_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/json_reader_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,492 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/gtest/include/gtest/gtest.h" +#include "base/json_reader.h" +#include "base/scoped_ptr.h" +#include "base/values.h" +#include "build/build_config.h" + +TEST(JSONReaderTest, Reading) { + // some whitespace checking + scoped_ptr root; + root.reset(JSONReader().JsonToValue(" null ", false, false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_NULL)); + + // Invalid JSON string + root.reset(JSONReader().JsonToValue("nu", false, false)); + ASSERT_FALSE(root.get()); + + // Simple bool + root.reset(JSONReader().JsonToValue("true ", false, false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_BOOLEAN)); + + // Test number formats + root.reset(JSONReader().JsonToValue("43", false, false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_INTEGER)); + int int_val = 0; + ASSERT_TRUE(root->GetAsInteger(&int_val)); + ASSERT_EQ(43, int_val); + + // According to RFC4627, oct, hex, and leading zeros are invalid JSON. + root.reset(JSONReader().JsonToValue("043", false, false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("0x43", false, false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("00", false, false)); + ASSERT_FALSE(root.get()); + + // Test 0 (which needs to be special cased because of the leading zero + // clause). + root.reset(JSONReader().JsonToValue("0", false, false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_INTEGER)); + int_val = 1; + ASSERT_TRUE(root->GetAsInteger(&int_val)); + ASSERT_EQ(0, int_val); + + // Numbers that overflow ints should succeed, being internally promoted to + // storage as doubles + root.reset(JSONReader().JsonToValue("2147483648", false, false)); + ASSERT_TRUE(root.get()); + double real_val; +#ifdef ARCH_CPU_32_BITS + ASSERT_TRUE(root->IsType(Value::TYPE_REAL)); + real_val = 0.0; + ASSERT_TRUE(root->GetAsReal(&real_val)); + ASSERT_DOUBLE_EQ(2147483648.0, real_val); +#else + ASSERT_TRUE(root->IsType(Value::TYPE_INTEGER)); + int_val = 0; + ASSERT_TRUE(root->GetAsInteger(&int_val)); + ASSERT_EQ(2147483648, int_val); +#endif + root.reset(JSONReader().JsonToValue("-2147483649", false, false)); + ASSERT_TRUE(root.get()); +#ifdef ARCH_CPU_32_BITS + ASSERT_TRUE(root->IsType(Value::TYPE_REAL)); + real_val = 0.0; + ASSERT_TRUE(root->GetAsReal(&real_val)); + ASSERT_DOUBLE_EQ(-2147483649.0, real_val); +#else + ASSERT_TRUE(root->IsType(Value::TYPE_INTEGER)); + int_val = 0; + ASSERT_TRUE(root->GetAsInteger(&int_val)); + ASSERT_EQ(-2147483649, int_val); +#endif + + // Parse a double + root.reset(JSONReader().JsonToValue("43.1", false, false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_REAL)); + real_val = 0.0; + ASSERT_TRUE(root->GetAsReal(&real_val)); + ASSERT_DOUBLE_EQ(43.1, real_val); + + root.reset(JSONReader().JsonToValue("4.3e-1", false, false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_REAL)); + real_val = 0.0; + ASSERT_TRUE(root->GetAsReal(&real_val)); + ASSERT_DOUBLE_EQ(.43, real_val); + + root.reset(JSONReader().JsonToValue("2.1e0", false, false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_REAL)); + real_val = 0.0; + ASSERT_TRUE(root->GetAsReal(&real_val)); + ASSERT_DOUBLE_EQ(2.1, real_val); + + root.reset(JSONReader().JsonToValue("2.1e+0001", false, false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_REAL)); + real_val = 0.0; + ASSERT_TRUE(root->GetAsReal(&real_val)); + ASSERT_DOUBLE_EQ(21.0, real_val); + + root.reset(JSONReader().JsonToValue("0.01", false, false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_REAL)); + real_val = 0.0; + ASSERT_TRUE(root->GetAsReal(&real_val)); + ASSERT_DOUBLE_EQ(0.01, real_val); + + root.reset(JSONReader().JsonToValue("1.00", false, false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_REAL)); + real_val = 0.0; + ASSERT_TRUE(root->GetAsReal(&real_val)); + ASSERT_DOUBLE_EQ(1.0, real_val); + + // Fractional parts must have a digit before and after the decimal point. + root.reset(JSONReader().JsonToValue("1.", false, false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue(".1", false, false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("1.e10", false, false)); + ASSERT_FALSE(root.get()); + + // Exponent must have a digit following the 'e'. + root.reset(JSONReader().JsonToValue("1e", false, false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("1E", false, false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("1e1.", false, false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("1e1.0", false, false)); + ASSERT_FALSE(root.get()); + + // INF/-INF/NaN are not valid + root.reset(JSONReader().JsonToValue("1e1000", false, false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("-1e1000", false, false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("NaN", false, false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("nan", false, false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("inf", false, false)); + ASSERT_FALSE(root.get()); + + // Invalid number formats + root.reset(JSONReader().JsonToValue("4.3.1", false, false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("4e3.1", false, false)); + ASSERT_FALSE(root.get()); + + // Test string parser + root.reset(JSONReader().JsonToValue("\"hello world\"", false, false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_STRING)); + std::wstring str_val; + ASSERT_TRUE(root->GetAsString(&str_val)); + ASSERT_EQ(L"hello world", str_val); + + // Empty string + root.reset(JSONReader().JsonToValue("\"\"", false, false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_STRING)); + str_val.clear(); + ASSERT_TRUE(root->GetAsString(&str_val)); + ASSERT_EQ(L"", str_val); + + // Test basic string escapes + root.reset(JSONReader().JsonToValue("\" \\\"\\\\\\/\\b\\f\\n\\r\\t\\v\"", + false, false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_STRING)); + str_val.clear(); + ASSERT_TRUE(root->GetAsString(&str_val)); + ASSERT_EQ(L" \"\\/\b\f\n\r\t\v", str_val); + + // Test hex and unicode escapes including the null character. + root.reset(JSONReader().JsonToValue("\"\\x41\\x00\\u1234\"", false, + false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_STRING)); + str_val.clear(); + ASSERT_TRUE(root->GetAsString(&str_val)); + ASSERT_EQ(std::wstring(L"A\0\x1234", 3), str_val); + + // Test invalid strings + root.reset(JSONReader().JsonToValue("\"no closing quote", false, false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("\"\\z invalid escape char\"", false, + false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("\"\\xAQ invalid hex code\"", false, + false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("not enough hex chars\\x1\"", false, + false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("\"not enough escape chars\\u123\"", + false, false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("\"extra backslash at end of input\\\"", + false, false)); + ASSERT_FALSE(root.get()); + + // Basic array + root.reset(JSONReader::Read("[true, false, null]", false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_LIST)); + ListValue* list = static_cast(root.get()); + ASSERT_EQ(3U, list->GetSize()); + + // Test with trailing comma. Should be parsed the same as above. + scoped_ptr root2; + root2.reset(JSONReader::Read("[true, false, null, ]", true)); + EXPECT_TRUE(root->Equals(root2.get())); + + // Empty array + root.reset(JSONReader::Read("[]", false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_LIST)); + list = static_cast(root.get()); + ASSERT_EQ(0U, list->GetSize()); + + // Nested arrays + root.reset(JSONReader::Read("[[true], [], [false, [], [null]], null]", + false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_LIST)); + list = static_cast(root.get()); + ASSERT_EQ(4U, list->GetSize()); + + // Lots of trailing commas. + root2.reset(JSONReader::Read("[[true], [], [false, [], [null, ] , ], null,]", + true)); + EXPECT_TRUE(root->Equals(root2.get())); + + // Invalid, missing close brace. + root.reset(JSONReader::Read("[[true], [], [false, [], [null]], null", false)); + ASSERT_FALSE(root.get()); + + // Invalid, too many commas + root.reset(JSONReader::Read("[true,, null]", false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader::Read("[true,, null]", true)); + ASSERT_FALSE(root.get()); + + // Invalid, no commas + root.reset(JSONReader::Read("[true null]", false)); + ASSERT_FALSE(root.get()); + + // Invalid, trailing comma + root.reset(JSONReader::Read("[true,]", false)); + ASSERT_FALSE(root.get()); + + // Valid if we set |allow_trailing_comma| to true. + root.reset(JSONReader::Read("[true,]", true)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_LIST)); + list = static_cast(root.get()); + EXPECT_EQ(1U, list->GetSize()); + Value* tmp_value = NULL; + ASSERT_TRUE(list->Get(0, &tmp_value)); + EXPECT_TRUE(tmp_value->IsType(Value::TYPE_BOOLEAN)); + bool bool_value = false; + ASSERT_TRUE(tmp_value->GetAsBoolean(&bool_value)); + EXPECT_TRUE(bool_value); + + // Don't allow empty elements, even if |allow_trailing_comma| is + // true. + root.reset(JSONReader::Read("[,]", true)); + EXPECT_FALSE(root.get()); + root.reset(JSONReader::Read("[true,,]", true)); + EXPECT_FALSE(root.get()); + root.reset(JSONReader::Read("[,true,]", true)); + EXPECT_FALSE(root.get()); + root.reset(JSONReader::Read("[true,,false]", true)); + EXPECT_FALSE(root.get()); + + // Test objects + root.reset(JSONReader::Read("{}", false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY)); + + root.reset(JSONReader::Read( + "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\" }", + false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY)); + DictionaryValue* dict_val = static_cast(root.get()); + real_val = 0.0; + ASSERT_TRUE(dict_val->GetReal(L"number", &real_val)); + ASSERT_DOUBLE_EQ(9.87654321, real_val); + Value* null_val = NULL; + ASSERT_TRUE(dict_val->Get(L"null", &null_val)); + ASSERT_TRUE(null_val->IsType(Value::TYPE_NULL)); + str_val.clear(); + ASSERT_TRUE(dict_val->GetString(L"S", &str_val)); + ASSERT_EQ(L"str", str_val); + + root2.reset(JSONReader::Read( + "{\"number\":9.87654321, \"null\":null , \"\\x53\" : \"str\", }", true)); + EXPECT_TRUE(root->Equals(root2.get())); + + // Test nesting + root.reset(JSONReader::Read( + "{\"inner\":{\"array\":[true]},\"false\":false,\"d\":{}}", false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY)); + dict_val = static_cast(root.get()); + DictionaryValue* inner_dict = NULL; + ASSERT_TRUE(dict_val->GetDictionary(L"inner", &inner_dict)); + ListValue* inner_array = NULL; + ASSERT_TRUE(inner_dict->GetList(L"array", &inner_array)); + ASSERT_EQ(1U, inner_array->GetSize()); + bool_value = true; + ASSERT_TRUE(dict_val->GetBoolean(L"false", &bool_value)); + ASSERT_FALSE(bool_value); + inner_dict = NULL; + ASSERT_TRUE(dict_val->GetDictionary(L"d", &inner_dict)); + + root2.reset(JSONReader::Read( + "{\"inner\": {\"array\":[true] , },\"false\":false,\"d\":{},}", true)); + EXPECT_TRUE(root->Equals(root2.get())); + + // Invalid, no closing brace + root.reset(JSONReader::Read("{\"a\": true", false)); + ASSERT_FALSE(root.get()); + + // Invalid, keys must be quoted + root.reset(JSONReader::Read("{foo:true}", false)); + ASSERT_FALSE(root.get()); + + // Invalid, trailing comma + root.reset(JSONReader::Read("{\"a\":true,}", false)); + ASSERT_FALSE(root.get()); + + // Invalid, too many commas + root.reset(JSONReader::Read("{\"a\":true,,\"b\":false}", false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader::Read("{\"a\":true,,\"b\":false}", true)); + ASSERT_FALSE(root.get()); + + // Invalid, no separator + root.reset(JSONReader::Read("{\"a\" \"b\"}", false)); + ASSERT_FALSE(root.get()); + + // Invalid, lone comma. + root.reset(JSONReader::Read("{,}", false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader::Read("{,}", true)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader::Read("{\"a\":true,,}", true)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader::Read("{,\"a\":true}", true)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader::Read("{\"a\":true,,\"b\":false}", true)); + ASSERT_FALSE(root.get()); + + // Test stack overflow + std::string evil(1000000, '['); + evil.append(std::string(1000000, ']')); + root.reset(JSONReader::Read(evil, false)); + ASSERT_FALSE(root.get()); + + // A few thousand adjacent lists is fine. + std::string not_evil("["); + not_evil.reserve(15010); + for (int i = 0; i < 5000; ++i) { + not_evil.append("[],"); + } + not_evil.append("[]]"); + root.reset(JSONReader::Read(not_evil, false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_LIST)); + list = static_cast(root.get()); + ASSERT_EQ(5001U, list->GetSize()); + + // Test utf8 encoded input + root.reset(JSONReader().JsonToValue("\"\xe7\xbd\x91\xe9\xa1\xb5\"", + false, false)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_STRING)); + str_val.clear(); + ASSERT_TRUE(root->GetAsString(&str_val)); + ASSERT_EQ(L"\x7f51\x9875", str_val); + + // Test invalid utf8 encoded input + root.reset(JSONReader().JsonToValue("\"345\xb0\xa1\xb0\xa2\"", + false, false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader().JsonToValue("\"123\xc0\x81\"", + false, false)); + ASSERT_FALSE(root.get()); + + // Test invalid root objects. + root.reset(JSONReader::Read("null", false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader::Read("true", false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader::Read("10", false)); + ASSERT_FALSE(root.get()); + root.reset(JSONReader::Read("\"root\"", false)); + ASSERT_FALSE(root.get()); +} + +TEST(JSONReaderTest, ErrorMessages) { + // Error strings should not be modified in case of success. + std::string error_message; + scoped_ptr root; + root.reset(JSONReader::ReadAndReturnError("[42]", false, &error_message)); + EXPECT_TRUE(error_message.empty()); + + // Test line and column counting + const char* big_json = "[\n0,\n1,\n2,\n3,4,5,6 7,\n8,\n9\n]"; + // error here --------------------------------^ + root.reset(JSONReader::ReadAndReturnError(big_json, false, &error_message)); + EXPECT_FALSE(root.get()); + EXPECT_EQ(JSONReader::FormatErrorMessage(5, 9, JSONReader::kSyntaxError), + error_message); + + // Test each of the error conditions + root.reset(JSONReader::ReadAndReturnError("{},{}", false, &error_message)); + EXPECT_FALSE(root.get()); + EXPECT_EQ(JSONReader::FormatErrorMessage(1, 3, + JSONReader::kUnexpectedDataAfterRoot), error_message); + + std::string nested_json; + for (int i = 0; i < 101; ++i) { + nested_json.insert(nested_json.begin(), '['); + nested_json.append(1, ']'); + } + root.reset(JSONReader::ReadAndReturnError(nested_json, false, + &error_message)); + EXPECT_FALSE(root.get()); + EXPECT_EQ(JSONReader::FormatErrorMessage(1, 101, JSONReader::kTooMuchNesting), + error_message); + + root.reset(JSONReader::ReadAndReturnError("42", false, &error_message)); + EXPECT_FALSE(root.get()); + EXPECT_EQ(JSONReader::FormatErrorMessage(1, 1, + JSONReader::kBadRootElementType), error_message); + + root.reset(JSONReader::ReadAndReturnError("[1,]", false, &error_message)); + EXPECT_FALSE(root.get()); + EXPECT_EQ(JSONReader::FormatErrorMessage(1, 4, JSONReader::kTrailingComma), + error_message); + + root.reset(JSONReader::ReadAndReturnError("{foo:\"bar\"}", false, + &error_message)); + EXPECT_FALSE(root.get()); + EXPECT_EQ(JSONReader::FormatErrorMessage(1, 2, + JSONReader::kUnquotedDictionaryKey), error_message); + + root.reset(JSONReader::ReadAndReturnError("{\"foo\":\"bar\",}", false, + &error_message)); + EXPECT_FALSE(root.get()); + EXPECT_EQ(JSONReader::FormatErrorMessage(1, 14, JSONReader::kTrailingComma), + error_message); + + root.reset(JSONReader::ReadAndReturnError("[nu]", false, &error_message)); + EXPECT_FALSE(root.get()); + EXPECT_EQ(JSONReader::FormatErrorMessage(1, 2, JSONReader::kSyntaxError), + error_message); + + root.reset(JSONReader::ReadAndReturnError("[\"xxx\\xq\"]", false, + &error_message)); + EXPECT_FALSE(root.get()); + EXPECT_EQ(JSONReader::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape), + error_message); + + root.reset(JSONReader::ReadAndReturnError("[\"xxx\\uq\"]", false, + &error_message)); + EXPECT_FALSE(root.get()); + EXPECT_EQ(JSONReader::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape), + error_message); + + root.reset(JSONReader::ReadAndReturnError("[\"xxx\\q\"]", false, + &error_message)); + EXPECT_FALSE(root.get()); + EXPECT_EQ(JSONReader::FormatErrorMessage(1, 7, JSONReader::kInvalidEscape), + error_message); + +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/json_writer.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/json_writer.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/json_writer.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/json_writer.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,175 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/json_writer.h" + +#include "base/logging.h" +#include "base/string_util.h" +#include "base/values.h" +#include "base/string_escape.h" + +const char kPrettyPrintLineEnding[] = "\r\n"; + +/* static */ +void JSONWriter::Write(const Value* const node, bool pretty_print, + std::string* json) { + json->clear(); + // Is there a better way to estimate the size of the output? + json->reserve(1024); + JSONWriter writer(pretty_print, json); + writer.BuildJSONString(node, 0); + if (pretty_print) + json->append(kPrettyPrintLineEnding); +} + +JSONWriter::JSONWriter(bool pretty_print, std::string* json) + : json_string_(json), + pretty_print_(pretty_print) { + DCHECK(json); +} + +void JSONWriter::BuildJSONString(const Value* const node, int depth) { + switch(node->GetType()) { + case Value::TYPE_NULL: + json_string_->append("null"); + break; + + case Value::TYPE_BOOLEAN: + { + bool value; + bool result = node->GetAsBoolean(&value); + DCHECK(result); + json_string_->append(value ? "true" : "false"); + break; + } + + case Value::TYPE_INTEGER: + { + int value; + bool result = node->GetAsInteger(&value); + DCHECK(result); + StringAppendF(json_string_, "%d", value); + break; + } + + case Value::TYPE_REAL: + { + double value; + bool result = node->GetAsReal(&value); + DCHECK(result); + std::string real = DoubleToString(value); + // Ensure that the number has a .0 if there's no decimal or 'e'. This + // makes sure that when we read the JSON back, it's interpreted as a + // real rather than an int. + if (real.find('.') == std::string::npos && + real.find('e') == std::string::npos && + real.find('E') == std::string::npos) { + real.append(".0"); + } + // The JSON spec requires that non-integer values in the range (-1,1) + // have a zero before the decimal point - ".52" is not valid, "0.52" is. + if (real[0] == '.') { + real.insert(0, "0"); + } else if (real.length() > 1 && real[0] == '-' && real[1] == '.') { + // "-.1" bad "-0.1" good + real.insert(1, "0"); + } + json_string_->append(real); + break; + } + + case Value::TYPE_STRING: + { + std::wstring value; + bool result = node->GetAsString(&value); + DCHECK(result); + AppendQuotedString(value); + break; + } + + case Value::TYPE_LIST: + { + json_string_->append("["); + if (pretty_print_) + json_string_->append(" "); + + const ListValue* list = static_cast(node); + for (size_t i = 0; i < list->GetSize(); ++i) { + if (i != 0) { + json_string_->append(","); + if (pretty_print_) + json_string_->append(" "); + } + + Value* value = NULL; + bool result = list->Get(i, &value); + DCHECK(result); + BuildJSONString(value, depth); + } + + if (pretty_print_) + json_string_->append(" "); + json_string_->append("]"); + break; + } + + case Value::TYPE_DICTIONARY: + { + json_string_->append("{"); + if (pretty_print_) + json_string_->append(kPrettyPrintLineEnding); + + const DictionaryValue* dict = + static_cast(node); + for (DictionaryValue::key_iterator key_itr = dict->begin_keys(); + key_itr != dict->end_keys(); + ++key_itr) { + + if (key_itr != dict->begin_keys()) { + json_string_->append(","); + if (pretty_print_) + json_string_->append(kPrettyPrintLineEnding); + } + + Value* value = NULL; + bool result = dict->Get(*key_itr, &value); + DCHECK(result); + + if (pretty_print_) + IndentLine(depth + 1); + AppendQuotedString(*key_itr); + if (pretty_print_) { + json_string_->append(": "); + } else { + json_string_->append(":"); + } + BuildJSONString(value, depth + 1); + } + + if (pretty_print_) { + json_string_->append(kPrettyPrintLineEnding); + IndentLine(depth); + json_string_->append("}"); + } else { + json_string_->append("}"); + } + break; + } + + default: + // TODO(jhughes): handle TYPE_BINARY + NOTREACHED() << "unknown json type"; + } +} + +void JSONWriter::AppendQuotedString(const std::wstring& str) { + string_escape::JavascriptDoubleQuote(WideToUTF16Hack(str), true, + json_string_); +} + +void JSONWriter::IndentLine(int depth) { + // It may be faster to keep an indent string so we don't have to keep + // reallocating. + json_string_->append(std::string(depth * 3, ' ')); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/json_writer.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/json_writer.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/json_writer.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/json_writer.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,47 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_JSON_WRITER_H_ +#define BASE_JSON_WRITER_H_ + +#include + +#include "base/basictypes.h" + +class Value; + +class JSONWriter { + public: + // Given a root node, generates a JSON string and puts it into |json|. + // If |pretty_print| is true, return a slightly nicer formated json string + // (pads with whitespace to help readability). If |pretty_print| is false, + // we try to generate as compact a string as possible. + // TODO(tc): Should we generate json if it would be invalid json (e.g., + // |node| is not a DictionaryValue/ListValue or if there are inf/-inf float + // values)? + static void Write(const Value* const node, bool pretty_print, + std::string* json); + + private: + JSONWriter(bool pretty_print, std::string* json); + + // Called recursively to build the JSON string. Whe completed, value is + // json_string_ will contain the JSON. + void BuildJSONString(const Value* const node, int depth); + + // Appends a quoted, escaped, version of str to json_string_. + void AppendQuotedString(const std::wstring& str); + + // Adds space to json_string_ for the indent level. + void IndentLine(int depth); + + // Where we write JSON data as we generate it. + std::string* json_string_; + + bool pretty_print_; + + DISALLOW_COPY_AND_ASSIGN(JSONWriter); +}; + +#endif // BASE_JSON_WRITER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/json_writer_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/json_writer_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/json_writer_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/json_writer_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,68 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/gtest/include/gtest/gtest.h" +#include "base/json_writer.h" +#include "base/values.h" + +TEST(JSONWriterTest, Writing) { + // Test null + Value* root = Value::CreateNullValue(); + std::string output_js; + JSONWriter::Write(root, false, &output_js); + ASSERT_EQ("null", output_js); + delete root; + + // Test empty dict + root = new DictionaryValue; + JSONWriter::Write(root, false, &output_js); + ASSERT_EQ("{}", output_js); + delete root; + + // Test empty list + root = new ListValue; + JSONWriter::Write(root, false, &output_js); + ASSERT_EQ("[]", output_js); + delete root; + + // Test Real values should always have a decimal or an 'e'. + root = Value::CreateRealValue(1.0); + JSONWriter::Write(root, false, &output_js); + ASSERT_EQ("1.0", output_js); + delete root; + + // Test Real values in the the range (-1, 1) must have leading zeros + root = Value::CreateRealValue(0.2); + JSONWriter::Write(root, false, &output_js); + ASSERT_EQ("0.2", output_js); + delete root; + + // Test Real values in the the range (-1, 1) must have leading zeros + root = Value::CreateRealValue(-0.8); + JSONWriter::Write(root, false, &output_js); + ASSERT_EQ("-0.8", output_js); + delete root; + + // Writer unittests like empty list/dict nesting, + // list list nesting, etc. + DictionaryValue root_dict; + ListValue* list = new ListValue; + root_dict.Set(L"list", list); + DictionaryValue* inner_dict = new DictionaryValue; + list->Append(inner_dict); + inner_dict->SetInteger(L"inner int", 10); + ListValue* inner_list = new ListValue; + list->Append(inner_list); + list->Append(Value::CreateBooleanValue(true)); + + JSONWriter::Write(&root_dict, false, &output_js); + ASSERT_EQ("{\"list\":[{\"inner int\":10},[],true]}", output_js); + JSONWriter::Write(&root_dict, true, &output_js); + ASSERT_EQ("{\r\n" + " \"list\": [ {\r\n" + " \"inner int\": 10\r\n" + " }, [ ], true ]\r\n" + "}\r\n", + output_js); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/keyboard_codes.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/keyboard_codes.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/keyboard_codes.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/keyboard_codes.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,16 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_KEYBOARD_CODES_H_ +#define BASE_KEYBOARD_CODES_H_ + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include "base/keyboard_codes_win.h" +#elif defined(OS_POSIX) +#include "base/keyboard_codes_posix.h" +#endif + +#endif // BASE_KEYBOARD_CODES_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/keyboard_codes_posix.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/keyboard_codes_posix.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/keyboard_codes_posix.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/keyboard_codes_posix.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,208 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/* + * Copyright (C) 2006 Michael Emmel mike.emmel@gmail.com. All rights reserved. + * Copyright (C) 2008, 2009 Google Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES, LOSS OF USE, DATA, OR + * PROFITS, OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef BASE_KEYBOARD_CODES_POSIX_H_ +#define BASE_KEYBOARD_CODES_POSIX_H_ + +namespace base { + +enum { + VKEY_BACK = 0x08, + VKEY_TAB = 0x09, + VKEY_CLEAR = 0x0C, + VKEY_RETURN = 0x0D, + VKEY_SHIFT = 0x10, + VKEY_CONTROL = 0x11, + VKEY_MENU = 0x12, + VKEY_PAUSE = 0x13, + VKEY_CAPITAL = 0x14, + VKEY_KANA = 0x15, + VKEY_HANGUL = 0x15, + VKEY_JUNJA = 0x17, + VKEY_FINAL = 0x18, + VKEY_HANJA = 0x19, + VKEY_KANJI = 0x19, + VKEY_ESCAPE = 0x1B, + VKEY_CONVERT = 0x1C, + VKEY_NONCONVERT = 0x1D, + VKEY_ACCEPT = 0x1E, + VKEY_MODECHANGE = 0x1F, + VKEY_SPACE = 0x20, + VKEY_PRIOR = 0x21, + VKEY_NEXT = 0x22, + VKEY_END = 0x23, + VKEY_HOME = 0x24, + VKEY_LEFT = 0x25, + VKEY_UP = 0x26, + VKEY_RIGHT = 0x27, + VKEY_DOWN = 0x28, + VKEY_SELECT = 0x29, + VKEY_PRINT = 0x2A, + VKEY_EXECUTE = 0x2B, + VKEY_SNAPSHOT = 0x2C, + VKEY_INSERT = 0x2D, + VKEY_DELETE = 0x2E, + VKEY_HELP = 0x2F, + VKEY_0 = 0x30, + VKEY_1 = 0x31, + VKEY_2 = 0x32, + VKEY_3 = 0x33, + VKEY_4 = 0x34, + VKEY_5 = 0x35, + VKEY_6 = 0x36, + VKEY_7 = 0x37, + VKEY_8 = 0x38, + VKEY_9 = 0x39, + VKEY_A = 0x41, + VKEY_B = 0x42, + VKEY_C = 0x43, + VKEY_D = 0x44, + VKEY_E = 0x45, + VKEY_F = 0x46, + VKEY_G = 0x47, + VKEY_H = 0x48, + VKEY_I = 0x49, + VKEY_J = 0x4A, + VKEY_K = 0x4B, + VKEY_L = 0x4C, + VKEY_M = 0x4D, + VKEY_N = 0x4E, + VKEY_O = 0x4F, + VKEY_P = 0x50, + VKEY_Q = 0x51, + VKEY_R = 0x52, + VKEY_S = 0x53, + VKEY_T = 0x54, + VKEY_U = 0x55, + VKEY_V = 0x56, + VKEY_W = 0x57, + VKEY_X = 0x58, + VKEY_Y = 0x59, + VKEY_Z = 0x5A, + VKEY_LWIN = 0x5B, + VKEY_RWIN = 0x5C, + VKEY_APPS = 0x5D, + VKEY_SLEEP = 0x5F, + VKEY_NUMPAD0 = 0x60, + VKEY_NUMPAD1 = 0x61, + VKEY_NUMPAD2 = 0x62, + VKEY_NUMPAD3 = 0x63, + VKEY_NUMPAD4 = 0x64, + VKEY_NUMPAD5 = 0x65, + VKEY_NUMPAD6 = 0x66, + VKEY_NUMPAD7 = 0x67, + VKEY_NUMPAD8 = 0x68, + VKEY_NUMPAD9 = 0x69, + VKEY_MULTIPLY = 0x6A, + VKEY_ADD = 0x6B, + VKEY_SEPARATOR = 0x6C, + VKEY_SUBTRACT = 0x6D, + VKEY_DECIMAL = 0x6E, + VKEY_DIVIDE = 0x6F, + VKEY_F1 = 0x70, + VKEY_F2 = 0x71, + VKEY_F3 = 0x72, + VKEY_F4 = 0x73, + VKEY_F5 = 0x74, + VKEY_F6 = 0x75, + VKEY_F7 = 0x76, + VKEY_F8 = 0x77, + VKEY_F9 = 0x78, + VKEY_F10 = 0x79, + VKEY_F11 = 0x7A, + VKEY_F12 = 0x7B, + VKEY_F13 = 0x7C, + VKEY_F14 = 0x7D, + VKEY_F15 = 0x7E, + VKEY_F16 = 0x7F, + VKEY_F17 = 0x80, + VKEY_F18 = 0x81, + VKEY_F19 = 0x82, + VKEY_F20 = 0x83, + VKEY_F21 = 0x84, + VKEY_F22 = 0x85, + VKEY_F23 = 0x86, + VKEY_F24 = 0x87, + VKEY_NUMLOCK = 0x90, + VKEY_SCROLL = 0x91, + VKEY_LSHIFT = 0xA0, + VKEY_RSHIFT = 0xA1, + VKEY_LCONTROL = 0xA2, + VKEY_RCONTROL = 0xA3, + VKEY_LMENU = 0xA4, + VKEY_RMENU = 0xA5, + VKEY_BROWSER_BACK = 0xA6, + VKEY_BROWSER_FORWARD = 0xA7, + VKEY_BROWSER_REFRESH = 0xA8, + VKEY_BROWSER_STOP = 0xA9, + VKEY_BROWSER_SEARCH = 0xAA, + VKEY_BROWSER_FAVORITES = 0xAB, + VKEY_BROWSER_HOME = 0xAC, + VKEY_VOLUME_MUTE = 0xAD, + VKEY_VOLUME_DOWN = 0xAE, + VKEY_VOLUME_UP = 0xAF, + VKEY_MEDIA_NEXT_TRACK = 0xB0, + VKEY_MEDIA_PREV_TRACK = 0xB1, + VKEY_MEDIA_STOP = 0xB2, + VKEY_MEDIA_PLAY_PAUSE = 0xB3, + VKEY_MEDIA_LAUNCH_MAIL = 0xB4, + VKEY_MEDIA_LAUNCH_MEDIA_SELECT = 0xB5, + VKEY_MEDIA_LAUNCH_APP1 = 0xB6, + VKEY_MEDIA_LAUNCH_APP2 = 0xB7, + VKEY_OEM_1 = 0xBA, + VKEY_OEM_PLUS = 0xBB, + VKEY_OEM_COMMA = 0xBC, + VKEY_OEM_MINUS = 0xBD, + VKEY_OEM_PERIOD = 0xBE, + VKEY_OEM_2 = 0xBF, + VKEY_OEM_3 = 0xC0, + VKEY_OEM_4 = 0xDB, + VKEY_OEM_5 = 0xDC, + VKEY_OEM_6 = 0xDD, + VKEY_OEM_7 = 0xDE, + VKEY_OEM_8 = 0xDF, + VKEY_OEM_102 = 0xE2, + VKEY_PROCESSKEY = 0xE5, + VKEY_PACKET = 0xE7, + VKEY_ATTN = 0xF6, + VKEY_CRSEL = 0xF7, + VKEY_EXSEL = 0xF8, + VKEY_EREOF = 0xF9, + VKEY_PLAY = 0xFA, + VKEY_ZOOM = 0xFB, + VKEY_NONAME = 0xFC, + VKEY_PA1 = 0xFD, + VKEY_OEM_CLEAR = 0xFE, + VKEY_UNKNOWN = 0 +}; + +} // namespace views + +#endif // BASE_KEYBOARD_CODES_POSIX_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/keyboard_codes_win.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/keyboard_codes_win.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/keyboard_codes_win.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/keyboard_codes_win.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,184 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_KEYBOARD_CODES_WIN_H_ +#define BASE_KEYBOARD_CODES_WIN_H_ + +#include + +namespace base { + +enum { + VKEY_BACK = VK_BACK, + VKEY_TAB = VK_TAB, + VKEY_CLEAR = VK_CLEAR, + VKEY_RETURN = VK_RETURN, + VKEY_SHIFT = VK_SHIFT, + VKEY_CONTROL = VK_CONTROL, + VKEY_MENU = VK_MENU, + VKEY_PAUSE = VK_PAUSE, + VKEY_CAPITAL = VK_CAPITAL, + VKEY_KANA = VK_KANA, + VKEY_HANGUL = VK_HANGUL, + VKEY_JUNJA = VK_JUNJA, + VKEY_FINAL = VK_FINAL, + VKEY_HANJA = VK_HANJA, + VKEY_KANJI = VK_KANJI, + VKEY_ESCAPE = VK_ESCAPE, + VKEY_CONVERT = VK_CONVERT, + VKEY_NONCONVERT = VK_NONCONVERT, + VKEY_ACCEPT = VK_ACCEPT, + VKEY_MODECHANGE = VK_MODECHANGE, + VKEY_SPACE = VK_SPACE, + VKEY_PRIOR = VK_PRIOR, + VKEY_NEXT = VK_NEXT, + VKEY_END = VK_END, + VKEY_HOME = VK_HOME, + VKEY_LEFT = VK_LEFT, + VKEY_UP = VK_UP, + VKEY_RIGHT = VK_RIGHT, + VKEY_DOWN = VK_DOWN, + VKEY_SELECT = VK_SELECT, + VKEY_PRINT = VK_PRINT, + VKEY_EXECUTE = VK_EXECUTE, + VKEY_SNAPSHOT = VK_SNAPSHOT, + VKEY_INSERT = VK_INSERT, + VKEY_DELETE = VK_DELETE, + VKEY_HELP = VK_HELP, + VKEY_0 = '0', + VKEY_1 = '1', + VKEY_2 = '2', + VKEY_3 = '3', + VKEY_4 = '4', + VKEY_5 = '5', + VKEY_6 = '6', + VKEY_7 = '7', + VKEY_8 = '8', + VKEY_9 = '9', + VKEY_A = 'A', + VKEY_B = 'B', + VKEY_C = 'C', + VKEY_D = 'D', + VKEY_E = 'E', + VKEY_F = 'F', + VKEY_G = 'G', + VKEY_H = 'H', + VKEY_I = 'I', + VKEY_J = 'J', + VKEY_K = 'K', + VKEY_L = 'L', + VKEY_M = 'M', + VKEY_N = 'N', + VKEY_O = 'O', + VKEY_P = 'P', + VKEY_Q = 'Q', + VKEY_R = 'R', + VKEY_S = 'S', + VKEY_T = 'T', + VKEY_U = 'U', + VKEY_V = 'V', + VKEY_W = 'W', + VKEY_X = 'X', + VKEY_Y = 'Y', + VKEY_Z = 'Z', + VKEY_LWIN = VK_LWIN, + VKEY_RWIN = VK_RWIN, + VKEY_APPS = VK_APPS, + VKEY_SLEEP = VK_SLEEP, + VKEY_NUMPAD0 = VK_NUMPAD0, + VKEY_NUMPAD1 = VK_NUMPAD1, + VKEY_NUMPAD2 = VK_NUMPAD2, + VKEY_NUMPAD3 = VK_NUMPAD3, + VKEY_NUMPAD4 = VK_NUMPAD4, + VKEY_NUMPAD5 = VK_NUMPAD5, + VKEY_NUMPAD6 = VK_NUMPAD6, + VKEY_NUMPAD7 = VK_NUMPAD7, + VKEY_NUMPAD8 = VK_NUMPAD8, + VKEY_NUMPAD9 = VK_NUMPAD9, + VKEY_MULTIPLY = VK_MULTIPLY, + VKEY_ADD = VK_ADD, + VKEY_SEPARATOR = VK_SEPARATOR, + VKEY_SUBTRACT = VK_SUBTRACT, + VKEY_DECIMAL = VK_DECIMAL, + VKEY_DIVIDE = VK_DIVIDE, + VKEY_F1 = VK_F1, + VKEY_F2 = VK_F2, + VKEY_F3 = VK_F3, + VKEY_F4 = VK_F4, + VKEY_F5 = VK_F5, + VKEY_F6 = VK_F6, + VKEY_F7 = VK_F7, + VKEY_F8 = VK_F8, + VKEY_F9 = VK_F9, + VKEY_F10 = VK_F10, + VKEY_F11 = VK_F11, + VKEY_F12 = VK_F12, + VKEY_F13 = VK_F13, + VKEY_F14 = VK_F14, + VKEY_F15 = VK_F15, + VKEY_F16 = VK_F16, + VKEY_F17 = VK_F17, + VKEY_F18 = VK_F18, + VKEY_F19 = VK_F19, + VKEY_F20 = VK_F20, + VKEY_F21 = VK_F21, + VKEY_F22 = VK_F22, + VKEY_F23 = VK_F23, + VKEY_F24 = VK_F24, + VKEY_NUMLOCK = VK_NUMLOCK, + VKEY_SCROLL = VK_SCROLL, + VKEY_LSHIFT = VK_LSHIFT, + VKEY_RSHIFT = VK_RSHIFT, + VKEY_LCONTROL = VK_LCONTROL, + VKEY_RCONTROL = VK_RCONTROL, + VKEY_LMENU = VK_LMENU, + VKEY_RMENU = VK_RMENU, + VKEY_BROWSER_BACK = VK_BROWSER_BACK, + VKEY_BROWSER_FORWARD = VK_BROWSER_FORWARD, + VKEY_BROWSER_REFRESH = VK_BROWSER_REFRESH, + VKEY_BROWSER_STOP = VK_BROWSER_STOP, + VKEY_BROWSER_SEARCH = VK_BROWSER_SEARCH, + VKEY_BROWSER_FAVORITES = VK_BROWSER_FAVORITES, + VKEY_BROWSER_HOME = VK_BROWSER_HOME, + VKEY_VOLUME_MUTE = VK_VOLUME_MUTE, + VKEY_VOLUME_DOWN = VK_VOLUME_DOWN, + VKEY_VOLUME_UP = VK_VOLUME_UP, + VKEY_MEDIA_NEXT_TRACK = VK_MEDIA_NEXT_TRACK, + VKEY_MEDIA_PREV_TRACK = VK_MEDIA_PREV_TRACK, + VKEY_MEDIA_STOP = VK_MEDIA_STOP, + VKEY_MEDIA_PLAY_PAUSE = VK_MEDIA_PLAY_PAUSE, + VKEY_MEDIA_LAUNCH_MAIL = 0xB4, + VKEY_MEDIA_LAUNCH_MEDIA_SELECT = 0xB5, + VKEY_MEDIA_LAUNCH_APP1 = 0xB6, + VKEY_MEDIA_LAUNCH_APP2 = 0xB7, + VKEY_OEM_1 = VK_OEM_1, + VKEY_OEM_PLUS = VK_OEM_PLUS, + VKEY_OEM_COMMA = VK_OEM_COMMA, + VKEY_OEM_MINUS = VK_OEM_MINUS, + VKEY_OEM_PERIOD = VK_OEM_PERIOD, + VKEY_OEM_2 = VK_OEM_2, + VKEY_OEM_3 = VK_OEM_3, + VKEY_OEM_4 = VK_OEM_4, + VKEY_OEM_5 = VK_OEM_5, + VKEY_OEM_6 = VK_OEM_6, + VKEY_OEM_7 = VK_OEM_7, + VKEY_OEM_8 = VK_OEM_8, + VKEY_OEM_102 = VK_OEM_102, + VKEY_PROCESSKEY = VK_PROCESSKEY, + VKEY_PACKET = VK_PACKET, + VKEY_ATTN = VK_ATTN, + VKEY_CRSEL = VK_CRSEL, + VKEY_EXSEL = VK_EXSEL, + VKEY_EREOF = VK_EREOF, + VKEY_PLAY = VK_PLAY, + VKEY_ZOOM = VK_ZOOM, + VKEY_NONAME = VK_NONAME, + VKEY_PA1 = VK_PA1, + VKEY_OEM_CLEAR = VK_OEM_CLEAR, + VKEY_UNKNOWN = 0 +}; + +} // namespace views + +#endif // BASE_KEYBOARD_CODES_WIN_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/lazy_instance.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/lazy_instance.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/lazy_instance.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/lazy_instance.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,34 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/lazy_instance.h" + +#include "base/at_exit.h" +#include "base/atomicops.h" +#include "base/basictypes.h" +#include "base/platform_thread.h" + +namespace base { + +void LazyInstanceHelper::EnsureInstance(void* instance, + void (*ctor)(void*), + void (*dtor)(void*)) { + // Try to create the instance, if we're the first, will go from EMPTY + // to CREATING, otherwise we've already been beaten here. + if (base::subtle::Acquire_CompareAndSwap( + &state_, STATE_EMPTY, STATE_CREATING) == STATE_EMPTY) { + // Created the instance in the space provided by |instance|. + ctor(instance); + // Instance is created, go from CREATING to CREATED. + base::subtle::Release_Store(&state_, STATE_CREATED); + // Register the destructor callback with AtExitManager. + base::AtExitManager::RegisterCallback(dtor, instance); + } else { + // It's either in the process of being created, or already created. Spin. + while (base::subtle::NoBarrier_Load(&state_) != STATE_CREATED) + PlatformThread::YieldCurrentThread(); + } +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/lazy_instance.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/lazy_instance.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/lazy_instance.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/lazy_instance.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,110 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// The LazyInstance class manages a single instance of Type, +// which will be lazily created on the first time it's accessed. This class is +// useful for places you would normally use a function-level static, but you +// need to have guaranteed thread-safety. The Type constructor will only ever +// be called once, even if two threads are racing to create the object. Get() +// and Pointer() will always return the same, completely initialized instance. +// When the instance is constructed it is registered with AtExitManager. The +// destructor will be called on program exit. +// +// LazyInstance is completely thread safe, assuming that you create it safely. +// The class was designed to be POD initialized, so it shouldn't require a +// static constructor. It really only makes sense to declare a LazyInstance as +// a global variable using the base::LinkerInitialized constructor. +// +// LazyInstance is similar to Singleton, except it does not have the singleton +// property. You can have multiple LazyInstance's of the same type, and each +// will manage a unique instance. It also preallocates the space for Type, as +// to avoid allocating the Type instance on the heap. This may help with the +// performance of creating the instance, and reducing heap fragmentation. This +// requires that Type be a complete type so we can determine the size. +// +// Example usage: +// static LazyInstance my_instance(base::LINKER_INITIALIZED); +// void SomeMethod() { +// my_instance.Get().SomeMethod(); // MyClass::SomeMethod() +// +// MyClass* ptr = my_instance.Pointer(); +// ptr->DoDoDo(); // MyClass::DoDoDo +// } + +#ifndef BASE_LAZY_INSTANCE_H_ +#define BASE_LAZY_INSTANCE_H_ + +#include "base/atomicops.h" +#include "base/basictypes.h" + +namespace base { + +template +struct DefaultLazyInstanceTraits { + static void New(void* instance) { + // Use placement new to initialize our instance in our preallocated space. + // The parenthesis is very important here to force POD type initialization. + new (instance) Type(); + } + static void Delete(void* instance) { + // Explicitly call the destructor. + reinterpret_cast(instance)->~Type(); + } +}; + +// We pull out some of the functionality into a non-templated base, so that we +// can implement the more complicated pieces out of line in the .cc file. +class LazyInstanceHelper { + protected: + enum { + STATE_EMPTY = 0, + STATE_CREATING = 1, + STATE_CREATED = 2 + }; + + explicit LazyInstanceHelper(LinkerInitialized x) { /* state_ is 0 */ } + // Declaring a destructor (even if it's empty) will cause MSVC to register a + // static initializer to register the empty destructor with atexit(). + + // Make sure that instance is created, creating or waiting for it to be + // created if neccessary. Constructs with |ctor| in the space provided by + // |instance| and registers dtor for destruction at program exit. + void EnsureInstance(void* instance, void (*ctor)(void*), void (*dtor)(void*)); + + base::subtle::Atomic32 state_; + + private: + DISALLOW_COPY_AND_ASSIGN(LazyInstanceHelper); +}; + +template > +class LazyInstance : public LazyInstanceHelper { + public: + explicit LazyInstance(LinkerInitialized x) : LazyInstanceHelper(x) { } + // Declaring a destructor (even if it's empty) will cause MSVC to register a + // static initializer to register the empty destructor with atexit(). + + Type& Get() { + return *Pointer(); + } + + Type* Pointer() { + Type* instance = reinterpret_cast(&buf_); + + // We will hopefully have fast access when the instance is already created. + if (base::subtle::NoBarrier_Load(&state_) != STATE_CREATED) + EnsureInstance(instance, Traits::New, Traits::Delete); + + return instance; + } + + private: + int8 buf_[sizeof(Type)]; // Preallocate the space for the Type instance. + + DISALLOW_COPY_AND_ASSIGN(LazyInstance); +}; + +} // namespace base + +#endif // BASE_LAZY_INSTANCE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/lazy_instance_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/lazy_instance_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/lazy_instance_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/lazy_instance_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,100 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/at_exit.h" +#include "base/atomic_sequence_num.h" +#include "base/lazy_instance.h" +#include "base/simple_thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class ShadowingAtExitManager : public base::AtExitManager { + public: + ShadowingAtExitManager() : AtExitManager(true) { } +}; + +base::AtomicSequenceNumber constructed_seq_(base::LINKER_INITIALIZED); +base::AtomicSequenceNumber destructed_seq_(base::LINKER_INITIALIZED); + +class ConstructAndDestructLogger { + public: + ConstructAndDestructLogger() { + constructed_seq_.GetNext(); + } + ~ConstructAndDestructLogger() { + destructed_seq_.GetNext(); + } +}; + +class SlowConstructor { + public: + SlowConstructor() : some_int_(0) { + PlatformThread::Sleep(1000); // Sleep for 1 second to try to cause a race. + ++constructed; + some_int_ = 12; + } + int some_int() const { return some_int_; } + + static int constructed; + private: + int some_int_; +}; + +int SlowConstructor::constructed = 0; + +class SlowDelegate : public base::DelegateSimpleThread::Delegate { + public: + SlowDelegate(base::LazyInstance* lazy) : lazy_(lazy) { } + virtual void Run() { + EXPECT_EQ(12, lazy_->Get().some_int()); + EXPECT_EQ(12, lazy_->Pointer()->some_int()); + } + + private: + base::LazyInstance* lazy_; +}; + +} // namespace + +static base::LazyInstance lazy_logger( + base::LINKER_INITIALIZED); + +TEST(LazyInstanceTest, Basic) { + { + ShadowingAtExitManager shadow; + + EXPECT_EQ(0, constructed_seq_.GetNext()); + EXPECT_EQ(0, destructed_seq_.GetNext()); + + lazy_logger.Get(); + EXPECT_EQ(2, constructed_seq_.GetNext()); + EXPECT_EQ(1, destructed_seq_.GetNext()); + + lazy_logger.Pointer(); + EXPECT_EQ(3, constructed_seq_.GetNext()); + EXPECT_EQ(2, destructed_seq_.GetNext()); + } + EXPECT_EQ(4, constructed_seq_.GetNext()); + EXPECT_EQ(4, destructed_seq_.GetNext()); +} + +static base::LazyInstance lazy_slow(base::LINKER_INITIALIZED); + +TEST(LazyInstanceTest, ConstructorThreadSafety) { + { + ShadowingAtExitManager shadow; + + SlowDelegate delegate(&lazy_slow); + EXPECT_EQ(0, SlowConstructor::constructed); + + base::DelegateSimpleThreadPool pool("lazy_instance_cons", 5); + pool.AddWork(&delegate, 20); + EXPECT_EQ(0, SlowConstructor::constructed); + + pool.Start(); + pool.JoinAll(); + EXPECT_EQ(1, SlowConstructor::constructed); + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/linked_ptr.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/linked_ptr.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/linked_ptr.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/linked_ptr.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,174 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// A "smart" pointer type with reference tracking. Every pointer to a +// particular object is kept on a circular linked list. When the last pointer +// to an object is destroyed or reassigned, the object is deleted. +// +// Used properly, this deletes the object when the last reference goes away. +// There are several caveats: +// - Like all reference counting schemes, cycles lead to leaks. +// - Each smart pointer is actually two pointers (8 bytes instead of 4). +// - Every time a pointer is released, the entire list of pointers to that +// object is traversed. This class is therefore NOT SUITABLE when there +// will often be more than two or three pointers to a particular object. +// - References are only tracked as long as linked_ptr<> objects are copied. +// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS +// will happen (double deletion). +// +// A good use of this class is storing object references in STL containers. +// You can safely put linked_ptr<> in a vector<>. +// Other uses may not be as good. +// +// Note: If you use an incomplete type with linked_ptr<>, the class +// *containing* linked_ptr<> must have a constructor and destructor (even +// if they do nothing!). +// +// Thread Safety: +// A linked_ptr is NOT thread safe. Copying a linked_ptr object is +// effectively a read-write operation. +// +// Alternative: to linked_ptr is shared_ptr, which +// - is also two pointers in size (8 bytes for 32 bit addresses) +// - is thread safe for copying and deletion +// - supports weak_ptrs + +#ifndef BASE_LINKED_PTR_H_ +#define BASE_LINKED_PTR_H_ + +#include "base/logging.h" // for CHECK macros + +// This is used internally by all instances of linked_ptr<>. It needs to be +// a non-template class because different types of linked_ptr<> can refer to +// the same object (linked_ptr(obj) vs linked_ptr(obj)). +// So, it needs to be possible for different types of linked_ptr to participate +// in the same circular linked list, so we need a single class type here. +// +// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr. +class linked_ptr_internal { + public: + // Create a new circle that includes only this instance. + void join_new() { + next_ = this; + } + + // Join an existing circle. + void join(linked_ptr_internal const* ptr) { + next_ = ptr->next_; + ptr->next_ = this; + } + + // Leave whatever circle we're part of. Returns true iff we were the + // last member of the circle. Once this is done, you can join() another. + bool depart() { + if (next_ == this) return true; + linked_ptr_internal const* p = next_; + while (p->next_ != this) p = p->next_; + p->next_ = next_; + return false; + } + + private: + mutable linked_ptr_internal const* next_; +}; + +template +class linked_ptr { + public: + typedef T element_type; + + // Take over ownership of a raw pointer. This should happen as soon as + // possible after the object is created. + explicit linked_ptr(T* ptr = NULL) { capture(ptr); } + ~linked_ptr() { depart(); } + + // Copy an existing linked_ptr<>, adding ourselves to the list of references. + template linked_ptr(linked_ptr const& ptr) { copy(&ptr); } + linked_ptr(linked_ptr const& ptr) { DCHECK_NE(&ptr, this); copy(&ptr); } + + // Assignment releases the old value and acquires the new. + template linked_ptr& operator=(linked_ptr const& ptr) { + depart(); + copy(&ptr); + return *this; + } + + linked_ptr& operator=(linked_ptr const& ptr) { + if (&ptr != this) { + depart(); + copy(&ptr); + } + return *this; + } + + // Smart pointer members. + void reset(T* ptr = NULL) { depart(); capture(ptr); } + T* get() const { return value_; } + T* operator->() const { return value_; } + T& operator*() const { return *value_; } + // Release ownership of the pointed object and returns it. + // Sole ownership by this linked_ptr object is required. + T* release() { + bool last = link_.depart(); + CHECK(last); + T* v = value_; + value_ = NULL; + return v; + } + + bool operator==(const T* p) const { return value_ == p; } + bool operator!=(const T* p) const { return value_ != p; } + template + bool operator==(linked_ptr const& ptr) const { + return value_ == ptr.get(); + } + template + bool operator!=(linked_ptr const& ptr) const { + return value_ != ptr.get(); + } + + private: + template + friend class linked_ptr; + + T* value_; + linked_ptr_internal link_; + + void depart() { + if (link_.depart()) delete value_; + } + + void capture(T* ptr) { + value_ = ptr; + link_.join_new(); + } + + template void copy(linked_ptr const* ptr) { + value_ = ptr->get(); + if (value_) + link_.join(&ptr->link_); + else + link_.join_new(); + } +}; + +template inline +bool operator==(T* ptr, const linked_ptr& x) { + return ptr == x.get(); +} + +template inline +bool operator!=(T* ptr, const linked_ptr& x) { + return ptr != x.get(); +} + +// A function to convert T* into linked_ptr +// Doing e.g. make_linked_ptr(new FooBarBaz(arg)) is a shorter notation +// for linked_ptr >(new FooBarBaz(arg)) +template +linked_ptr make_linked_ptr(T* ptr) { + return linked_ptr(ptr); +} + +#endif // BASE_LINKED_PTR_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/linked_ptr_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/linked_ptr_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/linked_ptr_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/linked_ptr_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,110 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "base/linked_ptr.h" + +#include "base/string_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +int num = 0; + +std::string history; + +// Class which tracks allocation/deallocation +struct A { + A(): mynum(num++) { history += StringPrintf("A%d ctor\n", mynum); } + virtual ~A() { history += StringPrintf("A%d dtor\n", mynum); } + virtual void Use() { history += StringPrintf("A%d use\n", mynum); } + int mynum; +}; + +// Subclass +struct B: public A { + B() { history += StringPrintf("B%d ctor\n", mynum); } + ~B() { history += StringPrintf("B%d dtor\n", mynum); } + virtual void Use() { history += StringPrintf("B%d use\n", mynum); } +}; + +} // namespace + +TEST(LinkedPtrTest, Test) { + { + linked_ptr a0, a1, a2; + a0 = a0; + a1 = a2; + ASSERT_EQ(a0.get(), static_cast(NULL)); + ASSERT_EQ(a1.get(), static_cast(NULL)); + ASSERT_EQ(a2.get(), static_cast(NULL)); + ASSERT_TRUE(a0 == NULL); + ASSERT_TRUE(a1 == NULL); + ASSERT_TRUE(a2 == NULL); + + { + linked_ptr a3(new A); + a0 = a3; + ASSERT_TRUE(a0 == a3); + ASSERT_TRUE(a0 != NULL); + ASSERT_TRUE(a0.get() == a3); + ASSERT_TRUE(a0 == a3.get()); + linked_ptr a4(a0); + a1 = a4; + linked_ptr a5(new A); + ASSERT_TRUE(a5.get() != a3); + ASSERT_TRUE(a5 != a3.get()); + a2 = a5; + linked_ptr b0(new B); + linked_ptr a6(b0); + ASSERT_TRUE(b0 == a6); + ASSERT_TRUE(a6 == b0); + ASSERT_TRUE(b0 != NULL); + a5 = b0; + a5 = b0; + a3->Use(); + a4->Use(); + a5->Use(); + a6->Use(); + b0->Use(); + (*b0).Use(); + b0.get()->Use(); + } + + a0->Use(); + a1->Use(); + a2->Use(); + + a1 = a2; + a2.reset(new A); + a0.reset(); + + linked_ptr a7; + } + + ASSERT_EQ(history, + "A0 ctor\n" + "A1 ctor\n" + "A2 ctor\n" + "B2 ctor\n" + "A0 use\n" + "A0 use\n" + "B2 use\n" + "B2 use\n" + "B2 use\n" + "B2 use\n" + "B2 use\n" + "B2 dtor\n" + "A2 dtor\n" + "A0 use\n" + "A0 use\n" + "A1 use\n" + "A3 ctor\n" + "A0 dtor\n" + "A3 dtor\n" + "A1 dtor\n" + ); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/linux_util.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/linux_util.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/linux_util.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/linux_util.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,31 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "linux_util.h" + +#include + +namespace base { + +uint8_t* BGRAToRGBA(const uint8_t* pixels, int width, int height, int stride) { + if (stride == 0) + stride = width * 4; + + uint8_t* new_pixels = static_cast(malloc(height * stride)); + + // We have to copy the pixels and swap from BGRA to RGBA. + for (int i = 0; i < height; ++i) { + for (int j = 0; j < width; ++j) { + int idx = i * stride + j * 4; + new_pixels[idx] = pixels[idx + 2]; + new_pixels[idx + 1] = pixels[idx + 1]; + new_pixels[idx + 2] = pixels[idx]; + new_pixels[idx + 3] = pixels[idx + 3]; + } + } + + return new_pixels; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/linux_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/linux_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/linux_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/linux_util.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,19 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_LINUX_UTIL_H__ +#define BASE_LINUX_UTIL_H__ + +#include + +namespace base { + +// Makes a copy of |pixels| with the ordering changed from BGRA to RGBA. +// The caller is responsible for free()ing the data. If |stride| is 0, +// it's assumed to be 4 * |width|. +uint8_t* BGRAToRGBA(const uint8_t* pixels, int width, int height, int stride); + +} // namespace base + +#endif // BASE_LINUX_UTIL_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/lock.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/lock.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/lock.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/lock.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,7 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Lock class. + +// Depricated file. See lock_impl_*.cc for platform specific versions. diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/lock.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/lock.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/lock.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/lock.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,75 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_LOCK_H_ +#define BASE_LOCK_H_ + +#include "base/lock_impl.h" + +// A convenient wrapper for an OS specific critical section. + +class Lock { + public: + Lock() : lock_() {} + ~Lock() {} + void Acquire() { lock_.Lock(); } + void Release() { lock_.Unlock(); } + // If the lock is not held, take it and return true. If the lock is already + // held by another thread, immediately return false. + bool Try() { return lock_.Try(); } + + // In debug builds this method checks that the lock has been acquired by the + // calling thread. If the lock has not been acquired, then the method + // will DCHECK(). In non-debug builds, the LockImpl's implementation of + // AssertAcquired() is an empty inline method. + void AssertAcquired() const { return lock_.AssertAcquired(); } + + // Return the underlying lock implementation. + // TODO(awalker): refactor lock and condition variables so that this is + // unnecessary. + LockImpl* lock_impl() { return &lock_; } + + private: + LockImpl lock_; // Platform specific underlying lock implementation. + + DISALLOW_COPY_AND_ASSIGN(Lock); +}; + +// A helper class that acquires the given Lock while the AutoLock is in scope. +class AutoLock { + public: + explicit AutoLock(Lock& lock) : lock_(lock) { + lock_.Acquire(); + } + + ~AutoLock() { + lock_.AssertAcquired(); + lock_.Release(); + } + + private: + Lock& lock_; + DISALLOW_COPY_AND_ASSIGN(AutoLock); +}; + +// AutoUnlock is a helper that will Release() the |lock| argument in the +// constructor, and re-Acquire() it in the destructor. +class AutoUnlock { + public: + explicit AutoUnlock(Lock& lock) : lock_(lock) { + // We require our caller to have the lock. + lock_.AssertAcquired(); + lock_.Release(); + } + + ~AutoUnlock() { + lock_.Acquire(); + } + + private: + Lock& lock_; + DISALLOW_COPY_AND_ASSIGN(AutoUnlock); +}; + +#endif // BASE_LOCK_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/lock_impl.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/lock_impl.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/lock_impl.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/lock_impl.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,77 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_LOCK_IMPL_H_ +#define BASE_LOCK_IMPL_H_ + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#elif defined(OS_POSIX) +#include +#endif + +#include "base/basictypes.h" +#include "base/platform_thread.h" + +// This class implements the underlying platform-specific spin-lock mechanism +// used for the Lock class. Most users should not use LockImpl directly, but +// should instead use Lock. +class LockImpl { + public: +#if defined(OS_WIN) + typedef CRITICAL_SECTION OSLockType; +#elif defined(OS_POSIX) + typedef pthread_mutex_t OSLockType; +#endif + + LockImpl(); + ~LockImpl(); + + // If the lock is not held, take it and return true. If the lock is already + // held by something else, immediately return false. + bool Try(); + + // Take the lock, blocking until it is available if necessary. + void Lock(); + + // Release the lock. This must only be called by the lock's holder: after + // a successful call to Try, or a call to Lock. + void Unlock(); + + // Debug-only method that will DCHECK() if the lock is not acquired by the + // current thread. In non-debug builds, no check is performed. + // Because linux and mac condition variables modify the underlyning lock + // through the os_lock() method, runtime assertions can not be done on those + // builds. +#if defined(NDEBUG) || !defined(OS_WIN) + void AssertAcquired() const {} +#else + void AssertAcquired() const; +#endif + + // Return the native underlying lock. Not supported for Windows builds. + // TODO(awalker): refactor lock and condition variables so that this is + // unnecessary. +#if !defined(OS_WIN) + OSLockType* os_lock() { return &os_lock_; } +#endif + + private: + OSLockType os_lock_; + +#if !defined(NDEBUG) && defined(OS_WIN) + // All private data is implicitly protected by lock_. + // Be VERY careful to only access members under that lock. + PlatformThreadId owning_thread_id_; + int32 recursion_count_shadow_; + bool recursion_used_; // Allow debugging to continued after a DCHECK(). +#endif // NDEBUG + + DISALLOW_COPY_AND_ASSIGN(LockImpl); +}; + + +#endif // BASE_LOCK_IMPL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/lock_impl_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/lock_impl_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/lock_impl_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/lock_impl_posix.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,48 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/lock_impl.h" + +#include + +#include "base/logging.h" + +LockImpl::LockImpl() { +#ifndef NDEBUG + // In debug, setup attributes for lock error checking. + pthread_mutexattr_t mta; + int rv = pthread_mutexattr_init(&mta); + DCHECK_EQ(rv, 0); + rv = pthread_mutexattr_settype(&mta, PTHREAD_MUTEX_ERRORCHECK); + DCHECK_EQ(rv, 0); + rv = pthread_mutex_init(&os_lock_, &mta); + DCHECK_EQ(rv, 0); + rv = pthread_mutexattr_destroy(&mta); + DCHECK_EQ(rv, 0); +#else + // In release, go with the default lock attributes. + pthread_mutex_init(&os_lock_, NULL); +#endif +} + +LockImpl::~LockImpl() { + int rv = pthread_mutex_destroy(&os_lock_); + DCHECK_EQ(rv, 0); +} + +bool LockImpl::Try() { + int rv = pthread_mutex_trylock(&os_lock_); + DCHECK(rv == 0 || rv == EBUSY); + return rv == 0; +} + +void LockImpl::Lock() { + int rv = pthread_mutex_lock(&os_lock_); + DCHECK_EQ(rv, 0); +} + +void LockImpl::Unlock() { + int rv = pthread_mutex_unlock(&os_lock_); + DCHECK_EQ(rv, 0); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/lock_impl_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/lock_impl_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/lock_impl_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/lock_impl_win.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,73 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/lock_impl.h" +#include "base/logging.h" + +// NOTE: Although windows critical sections support recursive locks, we do not +// allow this, and we will commonly fire a DCHECK() if a thread attempts to +// acquire the lock a second time (while already holding it). + +LockImpl::LockImpl() { +#ifndef NDEBUG + recursion_count_shadow_ = 0; + recursion_used_ = false; + owning_thread_id_ = 0; +#endif // NDEBUG + // The second parameter is the spin count, for short-held locks it avoid the + // contending thread from going to sleep which helps performance greatly. + ::InitializeCriticalSectionAndSpinCount(&os_lock_, 2000); +} + +LockImpl::~LockImpl() { + ::DeleteCriticalSection(&os_lock_); +} + +bool LockImpl::Try() { + if (::TryEnterCriticalSection(&os_lock_) != FALSE) { +#ifndef NDEBUG + // ONLY access data after locking. + owning_thread_id_ = PlatformThread::CurrentId(); + DCHECK_NE(owning_thread_id_, 0); + recursion_count_shadow_++; + if (2 == recursion_count_shadow_ && !recursion_used_) { + recursion_used_ = true; + DCHECK(false); // Catch accidental redundant lock acquisition. + } +#endif + return true; + } + return false; +} + +void LockImpl::Lock() { + ::EnterCriticalSection(&os_lock_); +#ifndef NDEBUG + // ONLY access data after locking. + owning_thread_id_ = PlatformThread::CurrentId(); + DCHECK_NE(owning_thread_id_, 0); + recursion_count_shadow_++; + if (2 == recursion_count_shadow_ && !recursion_used_) { + recursion_used_ = true; + DCHECK(false); // Catch accidental redundant lock acquisition. + } +#endif // NDEBUG +} + +void LockImpl::Unlock() { +#ifndef NDEBUG + --recursion_count_shadow_; // ONLY access while lock is still held. + DCHECK(0 <= recursion_count_shadow_); + owning_thread_id_ = 0; +#endif // NDEBUG + ::LeaveCriticalSection(&os_lock_); +} + +// In non-debug builds, this method is declared as an empty inline method. +#ifndef NDEBUG +void LockImpl::AssertAcquired() const { + DCHECK(recursion_count_shadow_ > 0); + DCHECK_EQ(owning_thread_id_, PlatformThread::CurrentId()); +} +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/logging.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/logging.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/logging.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/logging.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,675 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" + +#ifdef CHROMIUM_MOZILLA_BUILD + +#include "prmem.h" +#include "prprf.h" +#include "base/string_util.h" +#include "nsXPCOM.h" + +namespace mozilla { + +Logger::~Logger() +{ + PRLogModuleLevel prlevel = PR_LOG_DEBUG; + int xpcomlevel = -1; + + switch (mSeverity) { + case LOG_INFO: + prlevel = PR_LOG_DEBUG; + xpcomlevel = -1; + break; + + case LOG_WARNING: + prlevel = PR_LOG_WARNING; + xpcomlevel = NS_DEBUG_WARNING; + break; + + case LOG_ERROR: + prlevel = PR_LOG_ERROR; + xpcomlevel = NS_DEBUG_WARNING; + break; + + case LOG_ERROR_REPORT: + prlevel = PR_LOG_ERROR; + xpcomlevel = NS_DEBUG_ASSERTION; + break; + + case LOG_FATAL: + prlevel = PR_LOG_ERROR; + xpcomlevel = NS_DEBUG_ABORT; + break; + } + + PR_LOG(GetLog(), prlevel, ("%s:%i: %s", mFile, mLine, mMsg ? mMsg : "")); + if (xpcomlevel != -1) + NS_DebugBreak(xpcomlevel, mMsg, NULL, mFile, mLine); + + PR_Free(mMsg); +} + +void +Logger::printf(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + mMsg = PR_vsprintf_append(mMsg, fmt, args); + va_end(args); +} + +PRLogModuleInfo* Logger::gChromiumPRLog; + +PRLogModuleInfo* Logger::GetLog() +{ + if (!gChromiumPRLog) + gChromiumPRLog = PR_NewLogModule("chromium"); + return gChromiumPRLog; +} + +} // namespace mozilla + +mozilla::Logger& +operator<<(mozilla::Logger& log, const char* s) +{ + log.printf("%s", s); + return log; +} + +mozilla::Logger& +operator<<(mozilla::Logger& log, const std::string& s) +{ + log.printf("%s", s.c_str()); + return log; +} + +mozilla::Logger& +operator<<(mozilla::Logger& log, int i) +{ + log.printf("%i", i); + return log; +} + +mozilla::Logger& +operator<<(mozilla::Logger& log, const std::wstring& s) +{ + log.printf("%s", WideToASCII(s).c_str()); + return log; +} + +mozilla::Logger& +operator<<(mozilla::Logger& log, void* p) +{ + log.printf("%p", p); + return log; +} + +#else + +#if defined(OS_WIN) +#include +typedef HANDLE FileHandle; +typedef HANDLE MutexHandle; +#elif defined(OS_MACOSX) +#include +#include +#include +#include +#elif defined(OS_LINUX) +#include +#include +#endif + +#if defined(OS_POSIX) +#include +#include +#include +#include +#define MAX_PATH PATH_MAX +typedef FILE* FileHandle; +typedef pthread_mutex_t* MutexHandle; +#endif + +#include +#include +#include +#include + +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/debug_util.h" +#include "base/lock_impl.h" +#include "base/string_piece.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" + +namespace logging { + +bool g_enable_dcheck = false; + +const char* const log_severity_names[LOG_NUM_SEVERITIES] = { + "INFO", "WARNING", "ERROR", "ERROR_REPORT", "FATAL" }; + +int min_log_level = 0; +LogLockingState lock_log_file = LOCK_LOG_FILE; + +// The default set here for logging_destination will only be used if +// InitLogging is not called. On Windows, use a file next to the exe; +// on POSIX platforms, where it may not even be possible to locate the +// executable on disk, use stderr. +#if defined(OS_WIN) +LoggingDestination logging_destination = LOG_ONLY_TO_FILE; +#elif defined(OS_POSIX) +LoggingDestination logging_destination = LOG_ONLY_TO_SYSTEM_DEBUG_LOG; +#endif + +const int kMaxFilteredLogLevel = LOG_WARNING; +std::string* log_filter_prefix; + +// For LOG_ERROR and above, always print to stderr. +const int kAlwaysPrintErrorLevel = LOG_ERROR; + +// Which log file to use? This is initialized by InitLogging or +// will be lazily initialized to the default value when it is +// first needed. +#if defined(OS_WIN) +typedef wchar_t PathChar; +typedef std::wstring PathString; +#else +typedef char PathChar; +typedef std::string PathString; +#endif +PathString* log_file_name = NULL; + +// this file is lazily opened and the handle may be NULL +FileHandle log_file = NULL; + +// what should be prepended to each message? +bool log_process_id = false; +bool log_thread_id = false; +bool log_timestamp = true; +bool log_tickcount = false; + +// An assert handler override specified by the client to be called instead of +// the debug message dialog and process termination. +LogAssertHandlerFunction log_assert_handler = NULL; +// An report handler override specified by the client to be called instead of +// the debug message dialog. +LogReportHandlerFunction log_report_handler = NULL; + +// The lock is used if log file locking is false. It helps us avoid problems +// with multiple threads writing to the log file at the same time. Use +// LockImpl directly instead of using Lock, because Lock makes logging calls. +static LockImpl* log_lock = NULL; + +// When we don't use a lock, we are using a global mutex. We need to do this +// because LockFileEx is not thread safe. +#if defined(OS_WIN) +MutexHandle log_mutex = NULL; +#elif defined(OS_POSIX) +pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; +#endif + +// Helper functions to wrap platform differences. + +int32 CurrentProcessId() { +#if defined(OS_WIN) + return GetCurrentProcessId(); +#elif defined(OS_POSIX) + return getpid(); +#endif +} + +int32 CurrentThreadId() { +#if defined(OS_WIN) + return GetCurrentThreadId(); +#elif defined(OS_MACOSX) + return mach_thread_self(); +#elif defined(OS_LINUX) + return syscall(__NR_gettid); +#endif +} + +uint64 TickCount() { +#if defined(OS_WIN) + return GetTickCount(); +#elif defined(OS_MACOSX) + return mach_absolute_time(); +#elif defined(OS_LINUX) + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + + uint64 absolute_micro = + static_cast(ts.tv_sec) * 1000000 + + static_cast(ts.tv_nsec) / 1000; + + return absolute_micro; +#endif +} + +void CloseFile(FileHandle log) { +#if defined(OS_WIN) + CloseHandle(log); +#else + fclose(log); +#endif +} + +void DeleteFilePath(const PathString& log_name) { +#if defined(OS_WIN) + DeleteFile(log_name.c_str()); +#else + unlink(log_name.c_str()); +#endif +} + +// Called by logging functions to ensure that debug_file is initialized +// and can be used for writing. Returns false if the file could not be +// initialized. debug_file will be NULL in this case. +bool InitializeLogFileHandle() { + if (log_file) + return true; + + if (!log_file_name) { + // Nobody has called InitLogging to specify a debug log file, so here we + // initialize the log file name to a default. +#if defined(OS_WIN) + // On Windows we use the same path as the exe. + wchar_t module_name[MAX_PATH]; + GetModuleFileName(NULL, module_name, MAX_PATH); + log_file_name = new std::wstring(module_name); + std::wstring::size_type last_backslash = + log_file_name->rfind('\\', log_file_name->size()); + if (last_backslash != std::wstring::npos) + log_file_name->erase(last_backslash + 1); + *log_file_name += L"debug.log"; +#elif defined(OS_POSIX) + // On other platforms we just use the current directory. + log_file_name = new std::string("debug.log"); +#endif + } + + if (logging_destination == LOG_ONLY_TO_FILE || + logging_destination == LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG) { +#if defined(OS_WIN) + log_file = CreateFile(log_file_name->c_str(), GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) { + // try the current directory + log_file = CreateFile(L".\\debug.log", GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, + OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) { + log_file = NULL; + return false; + } + } + SetFilePointer(log_file, 0, 0, FILE_END); +#elif defined(OS_POSIX) + log_file = fopen(log_file_name->c_str(), "a"); + if (log_file == NULL) + return false; +#endif + } + + return true; +} + +void InitLogMutex() { +#if defined(OS_WIN) + if (!log_mutex) { + // \ is not a legal character in mutex names so we replace \ with / + std::wstring safe_name(*log_file_name); + std::replace(safe_name.begin(), safe_name.end(), '\\', '/'); + std::wstring t(L"Global\\"); + t.append(safe_name); + log_mutex = ::CreateMutex(NULL, FALSE, t.c_str()); + } +#elif defined(OS_POSIX) + // statically initialized +#endif +} + +void InitLogging(const PathChar* new_log_file, LoggingDestination logging_dest, + LogLockingState lock_log, OldFileDeletionState delete_old) { + g_enable_dcheck = + CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableDCHECK); + + if (log_file) { + // calling InitLogging twice or after some log call has already opened the + // default log file will re-initialize to the new options + CloseFile(log_file); + log_file = NULL; + } + + lock_log_file = lock_log; + logging_destination = logging_dest; + + // ignore file options if logging is disabled or only to system + if (logging_destination == LOG_NONE || + logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG) + return; + + if (!log_file_name) + log_file_name = new PathString(); + *log_file_name = new_log_file; + if (delete_old == DELETE_OLD_LOG_FILE) + DeleteFilePath(*log_file_name); + + if (lock_log_file == LOCK_LOG_FILE) { + InitLogMutex(); + } else if (!log_lock) { + log_lock = new LockImpl(); + } + + InitializeLogFileHandle(); +} + +void SetMinLogLevel(int level) { + min_log_level = level; +} + +int GetMinLogLevel() { + return min_log_level; +} + +void SetLogFilterPrefix(const char* filter) { + if (log_filter_prefix) { + delete log_filter_prefix; + log_filter_prefix = NULL; + } + + if (filter) + log_filter_prefix = new std::string(filter); +} + +void SetLogItems(bool enable_process_id, bool enable_thread_id, + bool enable_timestamp, bool enable_tickcount) { + log_process_id = enable_process_id; + log_thread_id = enable_thread_id; + log_timestamp = enable_timestamp; + log_tickcount = enable_tickcount; +} + +void SetLogAssertHandler(LogAssertHandlerFunction handler) { + log_assert_handler = handler; +} + +void SetLogReportHandler(LogReportHandlerFunction handler) { + log_report_handler = handler; +} + +// Displays a message box to the user with the error message in it. For +// Windows programs, it's possible that the message loop is messed up on +// a fatal error, and creating a MessageBox will cause that message loop +// to be run. Instead, we try to spawn another process that displays its +// command line. We look for "Debug Message.exe" in the same directory as +// the application. If it exists, we use it, otherwise, we use a regular +// message box. +void DisplayDebugMessage(const std::string& str) { + if (str.empty()) + return; + +#if defined(OS_WIN) + // look for the debug dialog program next to our application + wchar_t prog_name[MAX_PATH]; + GetModuleFileNameW(NULL, prog_name, MAX_PATH); + wchar_t* backslash = wcsrchr(prog_name, '\\'); + if (backslash) + backslash[1] = 0; + wcscat_s(prog_name, MAX_PATH, L"debug_message.exe"); + + std::wstring cmdline = base::SysUTF8ToWide(str); + if (cmdline.empty()) + return; + + STARTUPINFO startup_info; + memset(&startup_info, 0, sizeof(startup_info)); + startup_info.cb = sizeof(startup_info); + + PROCESS_INFORMATION process_info; + if (CreateProcessW(prog_name, &cmdline[0], NULL, NULL, false, 0, NULL, + NULL, &startup_info, &process_info)) { + WaitForSingleObject(process_info.hProcess, INFINITE); + CloseHandle(process_info.hThread); + CloseHandle(process_info.hProcess); + } else { + // debug process broken, let's just do a message box + MessageBoxW(NULL, &cmdline[0], L"Fatal error", + MB_OK | MB_ICONHAND | MB_TOPMOST); + } +#else + fprintf(stderr, "%s\n", str.c_str()); +#endif +} + +#if defined(OS_WIN) +LogMessage::SaveLastError::SaveLastError() : last_error_(::GetLastError()) { +} + +LogMessage::SaveLastError::~SaveLastError() { + ::SetLastError(last_error_); +} +#endif // defined(OS_WIN) + +LogMessage::LogMessage(const char* file, int line, LogSeverity severity, + int ctr) + : severity_(severity) { + Init(file, line); +} + +LogMessage::LogMessage(const char* file, int line, const CheckOpString& result) + : severity_(LOG_FATAL) { + Init(file, line); + stream_ << "Check failed: " << (*result.str_); +} + +LogMessage::LogMessage(const char* file, int line, LogSeverity severity, + const CheckOpString& result) + : severity_(severity) { + Init(file, line); + stream_ << "Check failed: " << (*result.str_); +} + +LogMessage::LogMessage(const char* file, int line) + : severity_(LOG_INFO) { + Init(file, line); +} + +LogMessage::LogMessage(const char* file, int line, LogSeverity severity) + : severity_(severity) { + Init(file, line); +} + +// writes the common header info to the stream +void LogMessage::Init(const char* file, int line) { + // log only the filename + const char* last_slash = strrchr(file, '\\'); + if (last_slash) + file = last_slash + 1; + + // TODO(darin): It might be nice if the columns were fixed width. + + stream_ << '['; + if (log_process_id) + stream_ << CurrentProcessId() << ':'; + if (log_thread_id) + stream_ << CurrentThreadId() << ':'; + if (log_timestamp) { + time_t t = time(NULL); +#if _MSC_VER >= 1400 + struct tm local_time = {0}; + localtime_s(&local_time, &t); + struct tm* tm_time = &local_time; +#else + struct tm* tm_time = localtime(&t); +#endif + stream_ << std::setfill('0') + << std::setw(2) << 1 + tm_time->tm_mon + << std::setw(2) << tm_time->tm_mday + << '/' + << std::setw(2) << tm_time->tm_hour + << std::setw(2) << tm_time->tm_min + << std::setw(2) << tm_time->tm_sec + << ':'; + } + if (log_tickcount) + stream_ << TickCount() << ':'; + stream_ << log_severity_names[severity_] << ":" << file << + "(" << line << ")] "; + + message_start_ = stream_.tellp(); +} + +LogMessage::~LogMessage() { + // TODO(brettw) modify the macros so that nothing is executed when the log + // level is too high. + if (severity_ < min_log_level) + return; + + std::string str_newline(stream_.str()); +#if defined(OS_WIN) + str_newline.append("\r\n"); +#else + str_newline.append("\n"); +#endif + + if (log_filter_prefix && severity_ <= kMaxFilteredLogLevel && + str_newline.compare(message_start_, log_filter_prefix->size(), + log_filter_prefix->data()) != 0) { + return; + } + + if (logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG || + logging_destination == LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG) { +#if defined(OS_WIN) + OutputDebugStringA(str_newline.c_str()); + if (severity_ >= kAlwaysPrintErrorLevel) +#endif + // TODO(erikkay): this interferes with the layout tests since it grabs + // stderr and stdout and diffs them against known data. Our info and warn + // logs add noise to that. Ideally, the layout tests would set the log + // level to ignore anything below error. When that happens, we should + // take this fprintf out of the #else so that Windows users can benefit + // from the output when running tests from the command-line. In the + // meantime, we leave this in for Mac and Linux, but until this is fixed + // they won't be able to pass any layout tests that have info or warn logs. + // See http://b/1343647 + fprintf(stderr, "%s", str_newline.c_str()); + } else if (severity_ >= kAlwaysPrintErrorLevel) { + // When we're only outputting to a log file, above a certain log level, we + // should still output to stderr so that we can better detect and diagnose + // problems with unit tests, especially on the buildbots. + fprintf(stderr, "%s", str_newline.c_str()); + } + + // write to log file + if (logging_destination != LOG_NONE && + logging_destination != LOG_ONLY_TO_SYSTEM_DEBUG_LOG && + InitializeLogFileHandle()) { + // We can have multiple threads and/or processes, so try to prevent them + // from clobbering each other's writes. + if (lock_log_file == LOCK_LOG_FILE) { + // Ensure that the mutex is initialized in case the client app did not + // call InitLogging. This is not thread safe. See below. + InitLogMutex(); + +#if defined(OS_WIN) + DWORD r = ::WaitForSingleObject(log_mutex, INFINITE); + DCHECK(r != WAIT_ABANDONED); +#elif defined(OS_POSIX) + pthread_mutex_lock(&log_mutex); +#endif + } else { + // use the lock + if (!log_lock) { + // The client app did not call InitLogging, and so the lock has not + // been created. We do this on demand, but if two threads try to do + // this at the same time, there will be a race condition to create + // the lock. This is why InitLogging should be called from the main + // thread at the beginning of execution. + log_lock = new LockImpl(); + } + log_lock->Lock(); + } + +#if defined(OS_WIN) + SetFilePointer(log_file, 0, 0, SEEK_END); + DWORD num_written; + WriteFile(log_file, + static_cast(str_newline.c_str()), + static_cast(str_newline.length()), + &num_written, + NULL); +#else + fprintf(log_file, "%s", str_newline.c_str()); +#endif + + if (lock_log_file == LOCK_LOG_FILE) { +#if defined(OS_WIN) + ReleaseMutex(log_mutex); +#elif defined(OS_POSIX) + pthread_mutex_unlock(&log_mutex); +#endif + } else { + log_lock->Unlock(); + } + } + + if (severity_ == LOG_FATAL) { + // display a message or break into the debugger on a fatal error + if (DebugUtil::BeingDebugged()) { + DebugUtil::BreakDebugger(); + } else { +#ifndef NDEBUG + // Dump a stack trace on a fatal. + StackTrace trace; + stream_ << "\n"; // Newline to separate from log message. + trace.OutputToStream(&stream_); +#endif + + if (log_assert_handler) { + // make a copy of the string for the handler out of paranoia + log_assert_handler(std::string(stream_.str())); + } else { + // Don't use the string with the newline, get a fresh version to send to + // the debug message process. We also don't display assertions to the + // user in release mode. The enduser can't do anything with this + // information, and displaying message boxes when the application is + // hosed can cause additional problems. +#ifndef NDEBUG + DisplayDebugMessage(stream_.str()); +#endif + // Crash the process to generate a dump. + DebugUtil::BreakDebugger(); + } + } + } else if (severity_ == LOG_ERROR_REPORT) { + // We are here only if the user runs with --enable-dcheck in release mode. + if (log_report_handler) { + log_report_handler(std::string(stream_.str())); + } else { + DisplayDebugMessage(stream_.str()); + } + } +} + +void CloseLogFile() { + if (!log_file) + return; + + CloseFile(log_file); + log_file = NULL; +} + +} // namespace logging + +std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) { + return out << base::SysWideToUTF8(std::wstring(wstr)); +} + +#endif // CHROMIUM_MOZILLA_BUILD diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/logging.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/logging.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/logging.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/logging.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,762 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_LOGGING_H_ +#define BASE_LOGGING_H_ + +#include +#include + +#include "base/basictypes.h" + +#ifdef CHROMIUM_MOZILLA_BUILD + +#include "prlog.h" + +// Replace the Chromium logging code with NSPR-based logging code and +// some C++ wrappers to emulate std::ostream + +#define ERROR 0 + +namespace mozilla { + +enum LogSeverity { + LOG_INFO, + LOG_WARNING, + LOG_ERROR, + LOG_ERROR_REPORT, + LOG_FATAL, + LOG_0 = LOG_ERROR +}; + +class Logger +{ +public: + Logger(LogSeverity severity, const char* file, int line) + : mSeverity(severity) + , mFile(file) + , mLine(line) + , mMsg(NULL) + { } + + ~Logger(); + + // not private so that the operator<< overloads can get to it + void printf(const char* fmt, ...); + +private: + static PRLogModuleInfo* gChromiumPRLog; + static PRLogModuleInfo* GetLog(); + + LogSeverity mSeverity; + const char* mFile; + int mLine; + char* mMsg; + + DISALLOW_EVIL_CONSTRUCTORS(Logger); +}; + +class LogWrapper +{ +public: + LogWrapper(LogSeverity severity, const char* file, int line) : + log(severity, file, line) { } + + operator Logger&() const { return log; } + +private: + mutable Logger log; + + DISALLOW_EVIL_CONSTRUCTORS(LogWrapper); +}; + +struct EmptyLog +{ +}; + +} // namespace mozilla + +mozilla::Logger& operator<<(mozilla::Logger& log, const char* s); +mozilla::Logger& operator<<(mozilla::Logger& log, const std::string& s); +mozilla::Logger& operator<<(mozilla::Logger& log, int i); +mozilla::Logger& operator<<(mozilla::Logger& log, const std::wstring& s); +mozilla::Logger& operator<<(mozilla::Logger& log, void* p); + +template +const mozilla::EmptyLog& operator <<(const mozilla::EmptyLog& log, const T&) +{ + return log; +} + +#define LOG(info) mozilla::LogWrapper(mozilla::LOG_ ## info, __FILE__, __LINE__) +#define LOG_IF(info, condition) \ + if (!(condition)) mozilla::LogWrapper(mozilla::LOG_ ## info, __FILE__, __LINE__) + +#ifdef DEBUG +#define DLOG(info) LOG(info) +#define DLOG_IF(info) LOG_IF(info) +#define DCHECK(condition) CHECK(condition) +#else +#define DLOG(info) mozilla::EmptyLog() +#define DLOG_IF(info, condition) mozilla::EmptyLog() +#define DCHECK(condition) while (false && (condition)) mozilla::EmptyLog() +#endif + +#define LOG_ASSERT(cond) CHECK(ERROR) +#define DLOG_ASSERT(cond) DCHECK(ERROR) + +#define NOTREACHED() LOG(ERROR) +#define NOTIMPLEMENTED() LOG(ERROR) + +#define CHECK(condition) LOG_IF(FATAL, condition) + +#define DCHECK_EQ(v1, v2) DCHECK((v1) == (v2)) +#define DCHECK_NE(v1, v2) DCHECK((v1) != (v2)) +#define DCHECK_LE(v1, v2) DCHECK((v1) <= (v2)) +#define DCHECK_LT(v1, v2) DCHECK((v1) < (v2)) +#define DCHECK_GE(v1, v2) DCHECK((v1) >= (v2)) +#define DCHECK_GT(v1, v2) DCHECK((v1) > (v2)) + +#ifdef assert +#undef assert +#endif +#define assert DLOG_ASSERT + +#else + +#include + +// +// Optional message capabilities +// ----------------------------- +// Assertion failed messages and fatal errors are displayed in a dialog box +// before the application exits. However, running this UI creates a message +// loop, which causes application messages to be processed and potentially +// dispatched to existing application windows. Since the application is in a +// bad state when this assertion dialog is displayed, these messages may not +// get processed and hang the dialog, or the application might go crazy. +// +// Therefore, it can be beneficial to display the error dialog in a separate +// process from the main application. When the logging system needs to display +// a fatal error dialog box, it will look for a program called +// "DebugMessage.exe" in the same directory as the application executable. It +// will run this application with the message as the command line, and will +// not include the name of the application as is traditional for easier +// parsing. +// +// The code for DebugMessage.exe is only one line. In WinMain, do: +// MessageBox(NULL, GetCommandLineW(), L"Fatal Error", 0); +// +// If DebugMessage.exe is not found, the logging code will use a normal +// MessageBox, potentially causing the problems discussed above. + + +// Instructions +// ------------ +// +// Make a bunch of macros for logging. The way to log things is to stream +// things to LOG(). E.g., +// +// LOG(INFO) << "Found " << num_cookies << " cookies"; +// +// You can also do conditional logging: +// +// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// The above will cause log messages to be output on the 1st, 11th, 21st, ... +// times it is executed. Note that the special COUNTER value is used to +// identify which repetition is happening. +// +// The CHECK(condition) macro is active in both debug and release builds and +// effectively performs a LOG(FATAL) which terminates the process and +// generates a crashdump unless a debugger is attached. +// +// There are also "debug mode" logging macros like the ones above: +// +// DLOG(INFO) << "Found cookies"; +// +// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// All "debug mode" logging is compiled away to nothing for non-debug mode +// compiles. LOG_IF and development flags also work well together +// because the code can be compiled away sometimes. +// +// We also have +// +// LOG_ASSERT(assertion); +// DLOG_ASSERT(assertion); +// +// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion; +// +// We also override the standard 'assert' to use 'DLOG_ASSERT'. +// +// The supported severity levels for macros that allow you to specify one +// are (in increasing order of severity) INFO, WARNING, ERROR, ERROR_REPORT, +// and FATAL. +// +// Very important: logging a message at the FATAL severity level causes +// the program to terminate (after the message is logged). +// +// Note the special severity of ERROR_REPORT only available/relevant in normal +// mode, which displays error dialog without terminating the program. There is +// no error dialog for severity ERROR or below in normal mode. +// +// There is also the special severity of DFATAL, which logs FATAL in +// debug mode, ERROR_REPORT in normal mode. + +namespace logging { + +// Where to record logging output? A flat file and/or system debug log via +// OutputDebugString. Defaults on Windows to LOG_ONLY_TO_FILE, and on +// POSIX to LOG_ONLY_TO_SYSTEM_DEBUG_LOG (aka stderr). +enum LoggingDestination { LOG_NONE, + LOG_ONLY_TO_FILE, + LOG_ONLY_TO_SYSTEM_DEBUG_LOG, + LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG }; + +// Indicates that the log file should be locked when being written to. +// Often, there is no locking, which is fine for a single threaded program. +// If logging is being done from multiple threads or there can be more than +// one process doing the logging, the file should be locked during writes to +// make each log outut atomic. Other writers will block. +// +// All processes writing to the log file must have their locking set for it to +// work properly. Defaults to DONT_LOCK_LOG_FILE. +enum LogLockingState { LOCK_LOG_FILE, DONT_LOCK_LOG_FILE }; + +// On startup, should we delete or append to an existing log file (if any)? +// Defaults to APPEND_TO_OLD_LOG_FILE. +enum OldFileDeletionState { DELETE_OLD_LOG_FILE, APPEND_TO_OLD_LOG_FILE }; + +// Sets the log file name and other global logging state. Calling this function +// is recommended, and is normally done at the beginning of application init. +// If you don't call it, all the flags will be initialized to their default +// values, and there is a race condition that may leak a critical section +// object if two threads try to do the first log at the same time. +// See the definition of the enums above for descriptions and default values. +// +// The default log file is initialized to "debug.log" in the application +// directory. You probably don't want this, especially since the program +// directory may not be writable on an enduser's system. +#if defined(OS_WIN) +void InitLogging(const wchar_t* log_file, LoggingDestination logging_dest, + LogLockingState lock_log, OldFileDeletionState delete_old); +#elif defined(OS_POSIX) +// TODO(avi): do we want to do a unification of character types here? +void InitLogging(const char* log_file, LoggingDestination logging_dest, + LogLockingState lock_log, OldFileDeletionState delete_old); +#endif + +// Sets the log level. Anything at or above this level will be written to the +// log file/displayed to the user (if applicable). Anything below this level +// will be silently ignored. The log level defaults to 0 (everything is logged) +// if this function is not called. +void SetMinLogLevel(int level); + +// Gets the current log level. +int GetMinLogLevel(); + +// Sets the log filter prefix. Any log message below LOG_ERROR severity that +// doesn't start with this prefix with be silently ignored. The filter defaults +// to NULL (everything is logged) if this function is not called. Messages +// with severity of LOG_ERROR or higher will not be filtered. +void SetLogFilterPrefix(const char* filter); + +// Sets the common items you want to be prepended to each log message. +// process and thread IDs default to off, the timestamp defaults to on. +// If this function is not called, logging defaults to writing the timestamp +// only. +void SetLogItems(bool enable_process_id, bool enable_thread_id, + bool enable_timestamp, bool enable_tickcount); + +// Sets the Log Assert Handler that will be used to notify of check failures. +// The default handler shows a dialog box and then terminate the process, +// however clients can use this function to override with their own handling +// (e.g. a silent one for Unit Tests) +typedef void (*LogAssertHandlerFunction)(const std::string& str); +void SetLogAssertHandler(LogAssertHandlerFunction handler); +// Sets the Log Report Handler that will be used to notify of check failures +// in non-debug mode. The default handler shows a dialog box and continues +// the execution, however clients can use this function to override with their +// own handling. +typedef void (*LogReportHandlerFunction)(const std::string& str); +void SetLogReportHandler(LogReportHandlerFunction handler); + +typedef int LogSeverity; +const LogSeverity LOG_INFO = 0; +const LogSeverity LOG_WARNING = 1; +const LogSeverity LOG_ERROR = 2; +const LogSeverity LOG_ERROR_REPORT = 3; +const LogSeverity LOG_FATAL = 4; +const LogSeverity LOG_NUM_SEVERITIES = 5; + +// LOG_DFATAL_LEVEL is LOG_FATAL in debug mode, ERROR_REPORT in normal mode +#ifdef NDEBUG +const LogSeverity LOG_DFATAL_LEVEL = LOG_ERROR_REPORT; +#else +const LogSeverity LOG_DFATAL_LEVEL = LOG_FATAL; +#endif + +// A few definitions of macros that don't generate much code. These are used +// by LOG() and LOG_IF, etc. Since these are used all over our code, it's +// better to have compact code for these operations. +#define COMPACT_GOOGLE_LOG_INFO \ + logging::LogMessage(__FILE__, __LINE__) +#define COMPACT_GOOGLE_LOG_WARNING \ + logging::LogMessage(__FILE__, __LINE__, logging::LOG_WARNING) +#define COMPACT_GOOGLE_LOG_ERROR \ + logging::LogMessage(__FILE__, __LINE__, logging::LOG_ERROR) +#define COMPACT_GOOGLE_LOG_ERROR_REPORT \ + logging::LogMessage(__FILE__, __LINE__, logging::LOG_ERROR_REPORT) +#define COMPACT_GOOGLE_LOG_FATAL \ + logging::LogMessage(__FILE__, __LINE__, logging::LOG_FATAL) +#define COMPACT_GOOGLE_LOG_DFATAL \ + logging::LogMessage(__FILE__, __LINE__, logging::LOG_DFATAL_LEVEL) + +// wingdi.h defines ERROR to be 0. When we call LOG(ERROR), it gets +// substituted with 0, and it expands to COMPACT_GOOGLE_LOG_0. To allow us +// to keep using this syntax, we define this macro to do the same thing +// as COMPACT_GOOGLE_LOG_ERROR, and also define ERROR the same way that +// the Windows SDK does for consistency. +#define ERROR 0 +#define COMPACT_GOOGLE_LOG_0 \ + logging::LogMessage(__FILE__, __LINE__, logging::LOG_ERROR) + +// We use the preprocessor's merging operator, "##", so that, e.g., +// LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO. There's some funny +// subtle difference between ostream member streaming functions (e.g., +// ostream::operator<<(int) and ostream non-member streaming functions +// (e.g., ::operator<<(ostream&, string&): it turns out that it's +// impossible to stream something like a string directly to an unnamed +// ostream. We employ a neat hack by calling the stream() member +// function of LogMessage which seems to avoid the problem. + +#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream() +#define SYSLOG(severity) LOG(severity) + +#define LOG_IF(severity, condition) \ + !(condition) ? (void) 0 : logging::LogMessageVoidify() & LOG(severity) +#define SYSLOG_IF(severity, condition) LOG_IF(severity, condition) + +#define LOG_ASSERT(condition) \ + LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". " +#define SYSLOG_ASSERT(condition) \ + SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". " + +// CHECK dies with a fatal error if condition is not true. It is *not* +// controlled by NDEBUG, so the check will be executed regardless of +// compilation mode. +#define CHECK(condition) \ + LOG_IF(FATAL, !(condition)) << "Check failed: " #condition ". " + +// A container for a string pointer which can be evaluated to a bool - +// true iff the pointer is NULL. +struct CheckOpString { + CheckOpString(std::string* str) : str_(str) { } + // No destructor: if str_ is non-NULL, we're about to LOG(FATAL), + // so there's no point in cleaning up str_. + operator bool() const { return str_ != NULL; } + std::string* str_; +}; + +// Build the error message string. This is separate from the "Impl" +// function template because it is not performance critical and so can +// be out of line, while the "Impl" code should be inline. +template +std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) { + std::ostringstream ss; + ss << names << " (" << v1 << " vs. " << v2 << ")"; + std::string* msg = new std::string(ss.str()); + return msg; +} + +extern std::string* MakeCheckOpStringIntInt(int v1, int v2, const char* names); + +template +std::string* MakeCheckOpString(const int& v1, + const int& v2, + const char* names) { + return MakeCheckOpStringIntInt(v1, v2, names); +} + +// Plus some debug-logging macros that get compiled to nothing for production +// +// DEBUG_MODE is for uses like +// if (DEBUG_MODE) foo.CheckThatFoo(); +// instead of +// #ifndef NDEBUG +// foo.CheckThatFoo(); +// #endif + +#ifdef OFFICIAL_BUILD +// We want to have optimized code for an official build so we remove DLOGS and +// DCHECK from the executable. + +#define DLOG(severity) \ + true ? (void) 0 : logging::LogMessageVoidify() & LOG(severity) + +#define DLOG_IF(severity, condition) \ + true ? (void) 0 : logging::LogMessageVoidify() & LOG(severity) + +#define DLOG_ASSERT(condition) \ + true ? (void) 0 : LOG_ASSERT(condition) + +enum { DEBUG_MODE = 0 }; + +// This macro can be followed by a sequence of stream parameters in +// non-debug mode. The DCHECK and friends macros use this so that +// the expanded expression DCHECK(foo) << "asdf" is still syntactically +// valid, even though the expression will get optimized away. +// In order to avoid variable unused warnings for code that only uses a +// variable in a CHECK, we make sure to use the macro arguments. +#define NDEBUG_EAT_STREAM_PARAMETERS \ + logging::LogMessage(__FILE__, __LINE__).stream() + +#define DCHECK(condition) \ + while (false && (condition)) NDEBUG_EAT_STREAM_PARAMETERS + +#define DCHECK_EQ(val1, val2) \ + while (false && (val1) == (val2)) NDEBUG_EAT_STREAM_PARAMETERS + +#define DCHECK_NE(val1, val2) \ + while (false && (val1) == (val2)) NDEBUG_EAT_STREAM_PARAMETERS + +#define DCHECK_LE(val1, val2) \ + while (false && (val1) == (val2)) NDEBUG_EAT_STREAM_PARAMETERS + +#define DCHECK_LT(val1, val2) \ + while (false && (val1) == (val2)) NDEBUG_EAT_STREAM_PARAMETERS + +#define DCHECK_GE(val1, val2) \ + while (false && (val1) == (val2)) NDEBUG_EAT_STREAM_PARAMETERS + +#define DCHECK_GT(val1, val2) \ + while (false && (val1) == (val2)) NDEBUG_EAT_STREAM_PARAMETERS + +#define DCHECK_STREQ(str1, str2) \ + while (false && (str1) == (str2)) NDEBUG_EAT_STREAM_PARAMETERS + +#define DCHECK_STRCASEEQ(str1, str2) \ + while (false && (str1) == (str2)) NDEBUG_EAT_STREAM_PARAMETERS + +#define DCHECK_STRNE(str1, str2) \ + while (false && (str1) == (str2)) NDEBUG_EAT_STREAM_PARAMETERS + +#define DCHECK_STRCASENE(str1, str2) \ + while (false && (str1) == (str2)) NDEBUG_EAT_STREAM_PARAMETERS + +#else +#ifndef NDEBUG +// On a regular debug build, we want to have DCHECKS and DLOGS enabled. + +#define DLOG(severity) LOG(severity) +#define DLOG_IF(severity, condition) LOG_IF(severity, condition) +#define DLOG_ASSERT(condition) LOG_ASSERT(condition) + +// debug-only checking. not executed in NDEBUG mode. +enum { DEBUG_MODE = 1 }; +#define DCHECK(condition) \ + LOG_IF(FATAL, !(condition)) << "Check failed: " #condition ". " + +// Helper macro for binary operators. +// Don't use this macro directly in your code, use DCHECK_EQ et al below. +#define DCHECK_OP(name, op, val1, val2) \ + if (logging::CheckOpString _result = \ + logging::Check##name##Impl((val1), (val2), #val1 " " #op " " #val2)) \ + logging::LogMessage(__FILE__, __LINE__, _result).stream() + +// Helper functions for string comparisons. +// To avoid bloat, the definitions are in logging.cc. +#define DECLARE_DCHECK_STROP_IMPL(func, expected) \ + std::string* Check##func##expected##Impl(const char* s1, \ + const char* s2, \ + const char* names); +DECLARE_DCHECK_STROP_IMPL(strcmp, true) +DECLARE_DCHECK_STROP_IMPL(strcmp, false) +DECLARE_DCHECK_STROP_IMPL(_stricmp, true) +DECLARE_DCHECK_STROP_IMPL(_stricmp, false) +#undef DECLARE_DCHECK_STROP_IMPL + +// Helper macro for string comparisons. +// Don't use this macro directly in your code, use CHECK_STREQ et al below. +#define DCHECK_STROP(func, op, expected, s1, s2) \ + while (CheckOpString _result = \ + logging::Check##func##expected##Impl((s1), (s2), \ + #s1 " " #op " " #s2)) \ + LOG(FATAL) << *_result.str_ + +// String (char*) equality/inequality checks. +// CASE versions are case-insensitive. +// +// Note that "s1" and "s2" may be temporary strings which are destroyed +// by the compiler at the end of the current "full expression" +// (e.g. DCHECK_STREQ(Foo().c_str(), Bar().c_str())). + +#define DCHECK_STREQ(s1, s2) DCHECK_STROP(strcmp, ==, true, s1, s2) +#define DCHECK_STRNE(s1, s2) DCHECK_STROP(strcmp, !=, false, s1, s2) +#define DCHECK_STRCASEEQ(s1, s2) DCHECK_STROP(_stricmp, ==, true, s1, s2) +#define DCHECK_STRCASENE(s1, s2) DCHECK_STROP(_stricmp, !=, false, s1, s2) + +#define DCHECK_INDEX(I,A) DCHECK(I < (sizeof(A)/sizeof(A[0]))) +#define DCHECK_BOUND(B,A) DCHECK(B <= (sizeof(A)/sizeof(A[0]))) + +#else // NDEBUG +// On a regular release build we want to be able to enable DCHECKS through the +// command line. +#define DLOG(severity) \ + true ? (void) 0 : logging::LogMessageVoidify() & LOG(severity) + +#define DLOG_IF(severity, condition) \ + true ? (void) 0 : logging::LogMessageVoidify() & LOG(severity) + +#define DLOG_ASSERT(condition) \ + true ? (void) 0 : LOG_ASSERT(condition) + +enum { DEBUG_MODE = 0 }; + +// This macro can be followed by a sequence of stream parameters in +// non-debug mode. The DCHECK and friends macros use this so that +// the expanded expression DCHECK(foo) << "asdf" is still syntactically +// valid, even though the expression will get optimized away. +#define NDEBUG_EAT_STREAM_PARAMETERS \ + logging::LogMessage(__FILE__, __LINE__).stream() + +// Set to true in InitLogging when we want to enable the dchecks in release. +extern bool g_enable_dcheck; +#define DCHECK(condition) \ + !logging::g_enable_dcheck ? void (0) : \ + LOG_IF(ERROR_REPORT, !(condition)) << "Check failed: " #condition ". " + +// Helper macro for binary operators. +// Don't use this macro directly in your code, use DCHECK_EQ et al below. +#define DCHECK_OP(name, op, val1, val2) \ + if (logging::g_enable_dcheck) \ + if (logging::CheckOpString _result = \ + logging::Check##name##Impl((val1), (val2), #val1 " " #op " " #val2)) \ + logging::LogMessage(__FILE__, __LINE__, logging::LOG_ERROR_REPORT, \ + _result).stream() + +#define DCHECK_STREQ(str1, str2) \ + while (false) NDEBUG_EAT_STREAM_PARAMETERS + +#define DCHECK_STRCASEEQ(str1, str2) \ + while (false) NDEBUG_EAT_STREAM_PARAMETERS + +#define DCHECK_STRNE(str1, str2) \ + while (false) NDEBUG_EAT_STREAM_PARAMETERS + +#define DCHECK_STRCASENE(str1, str2) \ + while (false) NDEBUG_EAT_STREAM_PARAMETERS + +#endif // NDEBUG + +// Helper functions for DCHECK_OP macro. +// The (int, int) specialization works around the issue that the compiler +// will not instantiate the template version of the function on values of +// unnamed enum type - see comment below. +#define DEFINE_DCHECK_OP_IMPL(name, op) \ + template \ + inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \ + const char* names) { \ + if (v1 op v2) return NULL; \ + else return MakeCheckOpString(v1, v2, names); \ + } \ + inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \ + if (v1 op v2) return NULL; \ + else return MakeCheckOpString(v1, v2, names); \ + } +DEFINE_DCHECK_OP_IMPL(EQ, ==) +DEFINE_DCHECK_OP_IMPL(NE, !=) +DEFINE_DCHECK_OP_IMPL(LE, <=) +DEFINE_DCHECK_OP_IMPL(LT, < ) +DEFINE_DCHECK_OP_IMPL(GE, >=) +DEFINE_DCHECK_OP_IMPL(GT, > ) +#undef DEFINE_DCHECK_OP_IMPL + +// Equality/Inequality checks - compare two values, and log a LOG_FATAL message +// including the two values when the result is not as expected. The values +// must have operator<<(ostream, ...) defined. +// +// You may append to the error message like so: +// DCHECK_NE(1, 2) << ": The world must be ending!"; +// +// We are very careful to ensure that each argument is evaluated exactly +// once, and that anything which is legal to pass as a function argument is +// legal here. In particular, the arguments may be temporary expressions +// which will end up being destroyed at the end of the apparent statement, +// for example: +// DCHECK_EQ(string("abc")[1], 'b'); +// +// WARNING: These may not compile correctly if one of the arguments is a pointer +// and the other is NULL. To work around this, simply static_cast NULL to the +// type of the desired pointer. + +#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2) +#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2) +#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2) +#define DCHECK_LT(val1, val2) DCHECK_OP(LT, < , val1, val2) +#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2) +#define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2) + +#endif // OFFICIAL_BUILD + +#define NOTREACHED() DCHECK(false) + +// Redefine the standard assert to use our nice log files +#undef assert +#define assert(x) DLOG_ASSERT(x) + +// This class more or less represents a particular log message. You +// create an instance of LogMessage and then stream stuff to it. +// When you finish streaming to it, ~LogMessage is called and the +// full message gets streamed to the appropriate destination. +// +// You shouldn't actually use LogMessage's constructor to log things, +// though. You should use the LOG() macro (and variants thereof) +// above. +class LogMessage { + public: + LogMessage(const char* file, int line, LogSeverity severity, int ctr); + + // Two special constructors that generate reduced amounts of code at + // LOG call sites for common cases. + // + // Used for LOG(INFO): Implied are: + // severity = LOG_INFO, ctr = 0 + // + // Using this constructor instead of the more complex constructor above + // saves a couple of bytes per call site. + LogMessage(const char* file, int line); + + // Used for LOG(severity) where severity != INFO. Implied + // are: ctr = 0 + // + // Using this constructor instead of the more complex constructor above + // saves a couple of bytes per call site. + LogMessage(const char* file, int line, LogSeverity severity); + + // A special constructor used for check failures. + // Implied severity = LOG_FATAL + LogMessage(const char* file, int line, const CheckOpString& result); + + // A special constructor used for check failures, with the option to + // specify severity. + LogMessage(const char* file, int line, LogSeverity severity, + const CheckOpString& result); + + ~LogMessage(); + + std::ostream& stream() { return stream_; } + + private: + void Init(const char* file, int line); + + LogSeverity severity_; + std::ostringstream stream_; + size_t message_start_; // Offset of the start of the message (past prefix + // info). +#if defined(OS_WIN) + // Stores the current value of GetLastError in the constructor and restores + // it in the destructor by calling SetLastError. + // This is useful since the LogMessage class uses a lot of Win32 calls + // that will lose the value of GLE and the code that called the log function + // will have lost the thread error value when the log call returns. + class SaveLastError { + public: + SaveLastError(); + ~SaveLastError(); + + unsigned long get_error() const { return last_error_; } + + protected: + unsigned long last_error_; + }; + + SaveLastError last_error_; +#endif + + DISALLOW_COPY_AND_ASSIGN(LogMessage); +}; + +// A non-macro interface to the log facility; (useful +// when the logging level is not a compile-time constant). +inline void LogAtLevel(int const log_level, std::string const &msg) { + LogMessage(__FILE__, __LINE__, log_level).stream() << msg; +} + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". +class LogMessageVoidify { + public: + LogMessageVoidify() { } + // This has to be an operator with a precedence lower than << but + // higher than ?: + void operator&(std::ostream&) { } +}; + +// Closes the log file explicitly if open. +// NOTE: Since the log file is opened as necessary by the action of logging +// statements, there's no guarantee that it will stay closed +// after this call. +void CloseLogFile(); + +} // namespace logging + +// These functions are provided as a convenience for logging, which is where we +// use streams (it is against Google style to use streams in other places). It +// is designed to allow you to emit non-ASCII Unicode strings to the log file, +// which is normally ASCII. It is relatively slow, so try not to use it for +// common cases. Non-ASCII characters will be converted to UTF-8 by these +// operators. +std::ostream& operator<<(std::ostream& out, const wchar_t* wstr); +inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) { + return out << wstr.c_str(); +} + +// The NOTIMPLEMENTED() macro annotates codepaths which have +// not been implemented yet. +// +// The implementation of this macro is controlled by NOTIMPLEMENTED_POLICY: +// 0 -- Do nothing (stripped by compiler) +// 1 -- Warn at compile time +// 2 -- Fail at compile time +// 3 -- Fail at runtime (DCHECK) +// 4 -- [default] LOG(ERROR) at runtime +// 5 -- LOG(ERROR) at runtime, only once per call-site + +#ifndef NOTIMPLEMENTED_POLICY +// Select default policy: LOG(ERROR) +#define NOTIMPLEMENTED_POLICY 4 +#endif + +#if defined(COMPILER_GCC) +// On Linux, with GCC, we can use __PRETTY_FUNCTION__ to get the demangled name +// of the current function in the NOTIMPLEMENTED message. +#define NOTIMPLEMENTED_MSG "Not implemented reached in " << __PRETTY_FUNCTION__ +#else +#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED" +#endif + +#if NOTIMPLEMENTED_POLICY == 0 +#define NOTIMPLEMENTED() ; +#elif NOTIMPLEMENTED_POLICY == 1 +// TODO, figure out how to generate a warning +#define NOTIMPLEMENTED() COMPILE_ASSERT(false, NOT_IMPLEMENTED) +#elif NOTIMPLEMENTED_POLICY == 2 +#define NOTIMPLEMENTED() COMPILE_ASSERT(false, NOT_IMPLEMENTED) +#elif NOTIMPLEMENTED_POLICY == 3 +#define NOTIMPLEMENTED() NOTREACHED() +#elif NOTIMPLEMENTED_POLICY == 4 +#define NOTIMPLEMENTED() LOG(ERROR) << NOTIMPLEMENTED_MSG +#elif NOTIMPLEMENTED_POLICY == 5 +#define NOTIMPLEMENTED() do {\ + static int count = 0;\ + LOG_IF(ERROR, 0 == count++) << NOTIMPLEMENTED_MSG;\ +} while(0) +#endif + +#endif // CHROMIUM_MOZILLA_BUILD + +#endif // BASE_LOGGING_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/mac_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/mac_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/mac_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/mac_util.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,39 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MAC_UTIL_H_ +#define BASE_MAC_UTIL_H_ + +struct FSRef; +class FilePath; + +#ifdef __OBJC__ +@class NSBundle; +#else +class NSBundle; +#endif + +#include + +namespace mac_util { + +std::string PathFromFSRef(const FSRef& ref); +bool FSRefFromPath(const std::string& path, FSRef* ref); + +// Returns true if the application is running from a bundle +bool AmIBundled(); + +// Returns the main bundle or the override, used for code that needs +// to fetch resources from bundles, but work within a unittest where we +// aren't a bundle. +NSBundle* MainAppBundle(); + +// Set the bundle that MainAppBundle will return, overriding the default value +// (Restore the default by calling SetOverrideAppBundle(nil)). +void SetOverrideAppBundle(NSBundle* bundle); +void SetOverrideAppBundlePath(const FilePath& file_path); + +} // namespace mac_util + +#endif // BASE_MAC_UTIL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/mac_util.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/mac_util.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/mac_util.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/mac_util.mm 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,69 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/mac_util.h" + +#include +#import + +#include "base/file_path.h" +#include "base/logging.h" +#include "base/scoped_cftyperef.h" +#include "base/sys_string_conversions.h" + +namespace mac_util { + +std::string PathFromFSRef(const FSRef& ref) { + scoped_cftyperef url( + CFURLCreateFromFSRef(kCFAllocatorDefault, &ref)); + NSString *path_string = [(NSURL *)url.get() path]; + return [path_string fileSystemRepresentation]; +} + +bool FSRefFromPath(const std::string& path, FSRef* ref) { + OSStatus status = FSPathMakeRef((const UInt8*)path.c_str(), + ref, nil); + return status == noErr; +} + +// Adapted from http://developer.apple.com/carbon/tipsandtricks.html#AmIBundled +bool AmIBundled() { + ProcessSerialNumber psn = {0, kCurrentProcess}; + + FSRef fsref; + if (GetProcessBundleLocation(&psn, &fsref) != noErr) + return false; + + FSCatalogInfo info; + if (FSGetCatalogInfo(&fsref, kFSCatInfoNodeFlags, &info, + NULL, NULL, NULL) != noErr) { + return false; + } + + return info.nodeFlags & kFSNodeIsDirectoryMask; +} + +// No threading worries since NSBundle isn't thread safe. +static NSBundle* g_override_app_bundle = nil; + +NSBundle* MainAppBundle() { + if (g_override_app_bundle) + return g_override_app_bundle; + return [NSBundle mainBundle]; +} + +void SetOverrideAppBundle(NSBundle* bundle) { + [g_override_app_bundle release]; + g_override_app_bundle = [bundle retain]; +} + +void SetOverrideAppBundlePath(const FilePath& file_path) { + NSString* path = base::SysUTF8ToNSString(file_path.value()); + NSBundle* bundle = [NSBundle bundleWithPath:path]; + DCHECK(bundle) << "failed to load the bundle: " << file_path.value(); + + SetOverrideAppBundle(bundle); +} + +} // namespace mac_util diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/mac_util_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/mac_util_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/mac_util_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/mac_util_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,20 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/mac_util.h" + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +typedef PlatformTest MacUtilTest; + +TEST_F(MacUtilTest, TestFSRef) { + FSRef ref; + std::string path("/System/Library"); + + ASSERT_TRUE(mac_util::FSRefFromPath(path, &ref)); + EXPECT_EQ(path, mac_util::PathFromFSRef(ref)); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/md5.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/md5.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/md5.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/md5.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,279 @@ +// The original file was copied from sqlite, and was in the public domain. +// Modifications Copyright 2006 Google Inc. All Rights Reserved + +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#include + +#include "base/md5.h" + +#include "base/basictypes.h" + +struct Context { + uint32 buf[4]; + uint32 bits[2]; + unsigned char in[64]; +}; + +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse (unsigned char *buf, unsigned longs){ + uint32 t; + do { + t = (uint32)((unsigned)buf[3]<<8 | buf[2]) << 16 | + ((unsigned)buf[1]<<8 | buf[0]); + *(uint32 *)buf = t; + buf += 4; + } while (--longs); +} +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(uint32 buf[4], const uint32 in[16]){ + register uint32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(MD5Context *pCtx){ + struct Context *ctx = (struct Context *)pCtx; + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(MD5Context *pCtx, const void *inbuf, size_t len){ + struct Context *ctx = (struct Context *)pCtx; + const unsigned char* buf = (const unsigned char*)inbuf; + uint32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32)len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += static_cast(len >> 29); + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if ( t ) { + unsigned char *p = (unsigned char *)ctx->in + t; + + t = 64-t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + buf += t; + len -= t; + } + + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(MD5Digest* digest, MD5Context *pCtx){ + struct Context *ctx = (struct Context *)pCtx; + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (uint32 *)ctx->in); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count-8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((uint32 *)ctx->in)[ 14 ] = ctx->bits[0]; + ((uint32 *)ctx->in)[ 15 ] = ctx->bits[1]; + + MD5Transform(ctx->buf, (uint32 *)ctx->in); + byteReverse((unsigned char *)ctx->buf, 4); + memcpy(digest->a, ctx->buf, 16); + memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +std::string MD5DigestToBase16(const MD5Digest& digest){ + static char const zEncode[] = "0123456789abcdef"; + + std::string ret; + ret.resize(32); + + int j = 0; + for(int i = 0; i < 16; i ++){ + int a = digest.a[i]; + ret[j++] = zEncode[(a>>4)&0xf]; + ret[j++] = zEncode[a & 0xf]; + } + return ret; +} + +void MD5Sum(const void* data, size_t length, MD5Digest* digest) { + MD5Context ctx; + MD5Init(&ctx); + MD5Update(&ctx, static_cast(data), length); + MD5Final(digest, &ctx); +} + +std::string MD5String(const std::string& str) { + MD5Digest digest; + MD5Sum(str.data(), str.length(), &digest); + return MD5DigestToBase16(digest); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/md5.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/md5.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/md5.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/md5.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,57 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MD5_H__ +#define BASE_MD5_H__ + +#include + +// These functions perform MD5 operations. The simplest call is MD5Sum to +// generate the MD5 sum of the given data. +// +// You can also compute the MD5 sum of data incrementally by making multiple +// calls to MD5Update: +// MD5Context ctx; // intermediate MD5 data: do not use +// MD5Init(&ctx); +// MD5Update(&ctx, data1, length1); +// MD5Update(&ctx, data2, length2); +// ... +// +// MD5Digest digest; // the result of the computation +// MD5Final(&digest, &ctx); +// +// You can call MD5DigestToBase16 to generate a string of the digest. + +// The output of an MD5 operation +typedef struct MD5Digest_struct { + unsigned char a[16]; +} MD5Digest; + +// Used for storing intermediate data during an MD5 computation. Callers +// should not access the data. +typedef char MD5Context[88]; + +// Computes the MD5 sum of the given data buffer with the given length. +// The given 'digest' structure will be filled with the result data. +void MD5Sum(const void* data, size_t length, MD5Digest* digest); + +// Initializes the given MD5 context structure for subsequent calls to +// MD5Update. +void MD5Init(MD5Context* context); + +// For the given buffer of data, updates the given MD5 context with the sum of +// the data. You can call this any number of times during the computation, +// exept that MD5Init must have been called first. +void MD5Update(MD5Context* context, const void* buf, size_t len); + +// Finalizes the MD5 operation and fills the buffer with the digest. +void MD5Final(MD5Digest* digest, MD5Context* pCtx); + +// Converts a digest into human-readable hexadecimal. +std::string MD5DigestToBase16(const MD5Digest& digest); + +// Returns the MD5 (in hexadecimal) of a string. +std::string MD5String(const std::string& str); + +#endif // BASE_MD5_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/memory_debug.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/memory_debug.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/memory_debug.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/memory_debug.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,54 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/memory_debug.h" + +#ifdef PURIFY +// this #define is used to prevent people from directly using pure.h +// instead of memory_debug.h +#define PURIFY_PRIVATE_INCLUDE +#include "base/third_party/purify/pure.h" +#endif + +namespace base { + +bool MemoryDebug::memory_in_use_ = false; + +void MemoryDebug::SetMemoryInUseEnabled(bool enabled) { + memory_in_use_ = enabled; +} + +void MemoryDebug::DumpAllMemoryInUse() { +#ifdef PURIFY + if (memory_in_use_) + PurifyAllInuse(); +#endif +} + +void MemoryDebug::DumpNewMemoryInUse() { +#ifdef PURIFY + if (memory_in_use_) + PurifyNewInuse(); +#endif +} + +void MemoryDebug::DumpAllLeaks() { +#ifdef PURIFY + PurifyAllLeaks(); +#endif +} + +void MemoryDebug::DumpNewLeaks() { +#ifdef PURIFY + PurifyNewLeaks(); +#endif +} + +void MemoryDebug::MarkAsInitialized(void* addr, size_t size) { +#ifdef PURIFY + PurifyMarkAsInitialized(addr, size); +#endif +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/memory_debug.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/memory_debug.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/memory_debug.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/memory_debug.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,46 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Functions used to debug memory usage, leaks, and other memory issues. +// All methods are effectively no-ops unless this program is being run through +// a supported memory tool (currently, only Purify) + +#ifndef BASE_MEMORY_DEBUG_H_ +#define BASE_MEMORY_DEBUG_H_ + +#include "base/basictypes.h" + +namespace base { + +class MemoryDebug { + public: + // Since MIU messages are a lot of data, and we don't always want this data, + // we have a global switch. If disabled, *MemoryInUse are no-ops. + static void SetMemoryInUseEnabled(bool enabled); + + // Dump information about all memory in use. + static void DumpAllMemoryInUse(); + // Dump information about new memory in use since the last + // call to DumpAllMemoryInUse() or DumpNewMemoryInUse(). + static void DumpNewMemoryInUse(); + + // Dump information about all current memory leaks. + static void DumpAllLeaks(); + // Dump information about new memory leaks since the last + // call to DumpAllLeaks() or DumpNewLeaks() + static void DumpNewLeaks(); + + // Mark |size| bytes of memory as initialized, so it doesn't produce any UMRs + // or UMCs. + static void MarkAsInitialized(void* addr, size_t size); + + private: + static bool memory_in_use_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(MemoryDebug); +}; + +} // namespace base + +#endif // BASE_MEMORY_DEBUG_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_loop.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_loop.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_loop.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_loop.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,655 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop.h" + +#include + +#include "base/compiler_specific.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/message_pump_default.h" +#include "base/string_util.h" +#include "base/thread_local.h" + +#if defined(OS_MACOSX) +#include "base/message_pump_mac.h" +#endif +#if defined(OS_POSIX) +#include "base/message_pump_libevent.h" +#endif +#if defined(OS_LINUX) +#ifdef MOZ_WIDGET_GTK2 +#include "base/message_pump_glib.h" +#endif +#ifdef MOZ_WIDGET_QT +#include "base/message_pump_qt.h" +#endif +#endif + +#ifdef CHROMIUM_MOZILLA_BUILD +#include "MessagePump.h" +#endif + +using base::Time; +using base::TimeDelta; + +// A lazily created thread local storage for quick access to a thread's message +// loop, if one exists. This should be safe and free of static constructors. +static base::LazyInstance > lazy_tls_ptr( + base::LINKER_INITIALIZED); + +//------------------------------------------------------------------------------ + +// Logical events for Histogram profiling. Run with -message-loop-histogrammer +// to get an accounting of messages and actions taken on each thread. +static const int kTaskRunEvent = 0x1; +static const int kTimerEvent = 0x2; + +// Provide range of message IDs for use in histogramming and debug display. +static const int kLeastNonZeroMessageId = 1; +static const int kMaxMessageId = 1099; +static const int kNumberOfDistinctMessagesDisplayed = 1100; + +//------------------------------------------------------------------------------ + +#if defined(OS_WIN) + +// Upon a SEH exception in this thread, it restores the original unhandled +// exception filter. +static int SEHFilter(LPTOP_LEVEL_EXCEPTION_FILTER old_filter) { + ::SetUnhandledExceptionFilter(old_filter); + return EXCEPTION_CONTINUE_SEARCH; +} + +// Retrieves a pointer to the current unhandled exception filter. There +// is no standalone getter method. +static LPTOP_LEVEL_EXCEPTION_FILTER GetTopSEHFilter() { + LPTOP_LEVEL_EXCEPTION_FILTER top_filter = NULL; + top_filter = ::SetUnhandledExceptionFilter(0); + ::SetUnhandledExceptionFilter(top_filter); + return top_filter; +} + +#endif // defined(OS_WIN) + +//------------------------------------------------------------------------------ + +// static +MessageLoop* MessageLoop::current() { + // TODO(darin): sadly, we cannot enable this yet since people call us even + // when they have no intention of using us. + //DCHECK(loop) << "Ouch, did you forget to initialize me?"; + return lazy_tls_ptr.Pointer()->Get(); +} + +MessageLoop::MessageLoop(Type type) + : type_(type), + nestable_tasks_allowed_(true), + exception_restoration_(false), + state_(NULL), + next_sequence_num_(0) { + DCHECK(!current()) << "should only have one message loop per thread"; + lazy_tls_ptr.Pointer()->Set(this); + +#ifdef CHROMIUM_MOZILLA_BUILD + if (type_ == TYPE_MOZILLA_UI) { + pump_ = new mozilla::ipc::MessagePump(); + return; + } + if (type_ == TYPE_MOZILLA_CHILD) { + pump_ = new mozilla::ipc::MessagePumpForChildProcess(); + return; + } +#endif +#if defined(OS_WIN) + // TODO(rvargas): Get rid of the OS guards. + if (type_ == TYPE_DEFAULT) { + pump_ = new base::MessagePumpDefault(); + } else if (type_ == TYPE_IO) { + pump_ = new base::MessagePumpForIO(); + } else { + DCHECK(type_ == TYPE_UI); + pump_ = new base::MessagePumpForUI(); + } +#elif defined(OS_POSIX) + if (type_ == TYPE_UI) { +#if defined(OS_MACOSX) + pump_ = base::MessagePumpMac::Create(); +#elif defined(OS_LINUX) + pump_ = new base::MessagePumpForUI(); +#endif // OS_LINUX + } else if (type_ == TYPE_IO) { + pump_ = new base::MessagePumpLibevent(); + } else { + pump_ = new base::MessagePumpDefault(); + } +#endif // OS_POSIX +} + +MessageLoop::~MessageLoop() { + DCHECK(this == current()); + + // Let interested parties have one last shot at accessing this. + FOR_EACH_OBSERVER(DestructionObserver, destruction_observers_, + WillDestroyCurrentMessageLoop()); + + DCHECK(!state_); + + // Clean up any unprocessed tasks, but take care: deleting a task could + // result in the addition of more tasks (e.g., via DeleteSoon). We set a + // limit on the number of times we will allow a deleted task to generate more + // tasks. Normally, we should only pass through this loop once or twice. If + // we end up hitting the loop limit, then it is probably due to one task that + // is being stubborn. Inspect the queues to see who is left. + bool did_work; + for (int i = 0; i < 100; ++i) { + DeletePendingTasks(); + ReloadWorkQueue(); + // If we end up with empty queues, then break out of the loop. + did_work = DeletePendingTasks(); + if (!did_work) + break; + } + DCHECK(!did_work); + + // OK, now make it so that no one can find us. + lazy_tls_ptr.Pointer()->Set(NULL); +} + +void MessageLoop::AddDestructionObserver(DestructionObserver *obs) { + DCHECK(this == current()); + destruction_observers_.AddObserver(obs); +} + +void MessageLoop::RemoveDestructionObserver(DestructionObserver *obs) { + DCHECK(this == current()); + destruction_observers_.RemoveObserver(obs); +} + +void MessageLoop::Run() { + AutoRunState save_state(this); + RunHandler(); +} + +void MessageLoop::RunAllPending() { + AutoRunState save_state(this); + state_->quit_received = true; // Means run until we would otherwise block. + RunHandler(); +} + +// Runs the loop in two different SEH modes: +// enable_SEH_restoration_ = false : any unhandled exception goes to the last +// one that calls SetUnhandledExceptionFilter(). +// enable_SEH_restoration_ = true : any unhandled exception goes to the filter +// that was existed before the loop was run. +void MessageLoop::RunHandler() { +#if defined(OS_WIN) + if (exception_restoration_) { + LPTOP_LEVEL_EXCEPTION_FILTER current_filter = GetTopSEHFilter(); + __try { + RunInternal(); + } __except(SEHFilter(current_filter)) { + } + return; + } +#endif + + RunInternal(); +} + +//------------------------------------------------------------------------------ + +void MessageLoop::RunInternal() { + DCHECK(this == current()); + + StartHistogrammer(); + +#if defined(OS_WIN) && !defined(CHROMIUM_MOZILLA_BUILD) + if (state_->dispatcher) { + pump_win()->RunWithDispatcher(this, state_->dispatcher); + return; + } +#endif + + pump_->Run(this); +} + +//------------------------------------------------------------------------------ +// Wrapper functions for use in above message loop framework. + +bool MessageLoop::ProcessNextDelayedNonNestableTask() { + if (state_->run_depth != 1) + return false; + + if (deferred_non_nestable_work_queue_.empty()) + return false; + + Task* task = deferred_non_nestable_work_queue_.front().task; + deferred_non_nestable_work_queue_.pop(); + + RunTask(task); + return true; +} + +//------------------------------------------------------------------------------ + +void MessageLoop::Quit() { + DCHECK(current() == this); + if (state_) { + state_->quit_received = true; + } else { + NOTREACHED() << "Must be inside Run to call Quit"; + } +} + +void MessageLoop::PostTask( + const tracked_objects::Location& from_here, Task* task) { + PostTask_Helper(from_here, task, 0, true); +} + +void MessageLoop::PostDelayedTask( + const tracked_objects::Location& from_here, Task* task, int delay_ms) { + PostTask_Helper(from_here, task, delay_ms, true); +} + +void MessageLoop::PostNonNestableTask( + const tracked_objects::Location& from_here, Task* task) { + PostTask_Helper(from_here, task, 0, false); +} + +void MessageLoop::PostNonNestableDelayedTask( + const tracked_objects::Location& from_here, Task* task, int delay_ms) { + PostTask_Helper(from_here, task, delay_ms, false); +} + +// Possibly called on a background thread! +void MessageLoop::PostTask_Helper( + const tracked_objects::Location& from_here, Task* task, int delay_ms, + bool nestable) { + task->SetBirthPlace(from_here); + + PendingTask pending_task(task, nestable); + + if (delay_ms > 0) { + pending_task.delayed_run_time = + Time::Now() + TimeDelta::FromMilliseconds(delay_ms); + } else { + DCHECK(delay_ms == 0) << "delay should not be negative"; + } + + // Warning: Don't try to short-circuit, and handle this thread's tasks more + // directly, as it could starve handling of foreign threads. Put every task + // into this queue. + + scoped_refptr pump; + { + AutoLock locked(incoming_queue_lock_); + +#ifdef CHROMIUM_MOZILLA_BUILD + incoming_queue_.push(pending_task); +#else + bool was_empty = incoming_queue_.empty(); + incoming_queue_.push(pending_task); + if (!was_empty) + return; // Someone else should have started the sub-pump. +#endif + + pump = pump_; + } + // Since the incoming_queue_ may contain a task that destroys this message + // loop, we cannot exit incoming_queue_lock_ until we are done with |this|. + // We use a stack-based reference to the message pump so that we can call + // ScheduleWork outside of incoming_queue_lock_. + + pump->ScheduleWork(); +} + +void MessageLoop::SetNestableTasksAllowed(bool allowed) { + if (nestable_tasks_allowed_ != allowed) { + nestable_tasks_allowed_ = allowed; + if (!nestable_tasks_allowed_) + return; + // Start the native pump if we are not already pumping. + pump_->ScheduleWork(); + } +} + +void MessageLoop::ScheduleWork() { + // Start the native pump if we are not already pumping. + pump_->ScheduleWork(); +} + +bool MessageLoop::NestableTasksAllowed() const { + return nestable_tasks_allowed_; +} + +//------------------------------------------------------------------------------ + +void MessageLoop::RunTask(Task* task) { + DCHECK(nestable_tasks_allowed_); + // Execute the task and assume the worst: It is probably not reentrant. + nestable_tasks_allowed_ = false; + + HistogramEvent(kTaskRunEvent); + task->Run(); + delete task; + + nestable_tasks_allowed_ = true; +} + +bool MessageLoop::DeferOrRunPendingTask(const PendingTask& pending_task) { + if (pending_task.nestable || state_->run_depth == 1) { + RunTask(pending_task.task); + // Show that we ran a task (Note: a new one might arrive as a + // consequence!). + return true; + } + + // We couldn't run the task now because we're in a nested message loop + // and the task isn't nestable. + deferred_non_nestable_work_queue_.push(pending_task); + return false; +} + +void MessageLoop::AddToDelayedWorkQueue(const PendingTask& pending_task) { + // Move to the delayed work queue. Initialize the sequence number + // before inserting into the delayed_work_queue_. The sequence number + // is used to faciliate FIFO sorting when two tasks have the same + // delayed_run_time value. + PendingTask new_pending_task(pending_task); + new_pending_task.sequence_num = next_sequence_num_++; + delayed_work_queue_.push(new_pending_task); +} + +void MessageLoop::ReloadWorkQueue() { + // We can improve performance of our loading tasks from incoming_queue_ to + // work_queue_ by waiting until the last minute (work_queue_ is empty) to + // load. That reduces the number of locks-per-task significantly when our + // queues get large. + if (!work_queue_.empty()) + return; // Wait till we *really* need to lock and load. + + // Acquire all we can from the inter-thread queue with one lock acquisition. + { + AutoLock lock(incoming_queue_lock_); + if (incoming_queue_.empty()) + return; + std::swap(incoming_queue_, work_queue_); + DCHECK(incoming_queue_.empty()); + } +} + +bool MessageLoop::DeletePendingTasks() { + bool did_work = !work_queue_.empty(); + while (!work_queue_.empty()) { + PendingTask pending_task = work_queue_.front(); + work_queue_.pop(); + if (!pending_task.delayed_run_time.is_null()) { + // We want to delete delayed tasks in the same order in which they would + // normally be deleted in case of any funny dependencies between delayed + // tasks. + AddToDelayedWorkQueue(pending_task); + } else { + // TODO(darin): Delete all tasks once it is safe to do so. + // Until it is totally safe, just do it when running purify. +#ifdef PURIFY + delete pending_task.task; +#endif // PURIFY + } + } + did_work |= !deferred_non_nestable_work_queue_.empty(); + while (!deferred_non_nestable_work_queue_.empty()) { + // TODO(darin): Delete all tasks once it is safe to do so. + // Until it is totaly safe, just delete them to keep purify happy. +#ifdef PURIFY + Task* task = deferred_non_nestable_work_queue_.front().task; +#endif + deferred_non_nestable_work_queue_.pop(); +#ifdef PURIFY + delete task; +#endif + } + did_work |= !delayed_work_queue_.empty(); + while (!delayed_work_queue_.empty()) { + Task* task = delayed_work_queue_.top().task; + delayed_work_queue_.pop(); + delete task; + } + return did_work; +} + +bool MessageLoop::DoWork() { + if (!nestable_tasks_allowed_) { + // Task can't be executed right now. + return false; + } + + for (;;) { + ReloadWorkQueue(); + if (work_queue_.empty()) + break; + + // Execute oldest task. + do { + PendingTask pending_task = work_queue_.front(); + work_queue_.pop(); + if (!pending_task.delayed_run_time.is_null()) { + AddToDelayedWorkQueue(pending_task); + // If we changed the topmost task, then it is time to re-schedule. + if (delayed_work_queue_.top().task == pending_task.task) + pump_->ScheduleDelayedWork(pending_task.delayed_run_time); + } else { + if (DeferOrRunPendingTask(pending_task)) + return true; + } + } while (!work_queue_.empty()); + } + + // Nothing happened. + return false; +} + +bool MessageLoop::DoDelayedWork(Time* next_delayed_work_time) { + if (!nestable_tasks_allowed_ || delayed_work_queue_.empty()) { + *next_delayed_work_time = Time(); + return false; + } + + if (delayed_work_queue_.top().delayed_run_time > Time::Now()) { + *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time; + return false; + } + + PendingTask pending_task = delayed_work_queue_.top(); + delayed_work_queue_.pop(); + + if (!delayed_work_queue_.empty()) + *next_delayed_work_time = delayed_work_queue_.top().delayed_run_time; + + return DeferOrRunPendingTask(pending_task); +} + +bool MessageLoop::DoIdleWork() { + if (ProcessNextDelayedNonNestableTask()) + return true; + + if (state_->quit_received) + pump_->Quit(); + + return false; +} + +//------------------------------------------------------------------------------ +// MessageLoop::AutoRunState + +MessageLoop::AutoRunState::AutoRunState(MessageLoop* loop) : loop_(loop) { + // Make the loop reference us. + previous_state_ = loop_->state_; + if (previous_state_) { + run_depth = previous_state_->run_depth + 1; + } else { + run_depth = 1; + } + loop_->state_ = this; + + // Initialize the other fields: + quit_received = false; +#if defined(OS_WIN) + dispatcher = NULL; +#endif +} + +MessageLoop::AutoRunState::~AutoRunState() { + loop_->state_ = previous_state_; +} + +//------------------------------------------------------------------------------ +// MessageLoop::PendingTask + +bool MessageLoop::PendingTask::operator<(const PendingTask& other) const { + // Since the top of a priority queue is defined as the "greatest" element, we + // need to invert the comparison here. We want the smaller time to be at the + // top of the heap. + + if (delayed_run_time < other.delayed_run_time) + return false; + + if (delayed_run_time > other.delayed_run_time) + return true; + + // If the times happen to match, then we use the sequence number to decide. + // Compare the difference to support integer roll-over. + return (sequence_num - other.sequence_num) > 0; +} + +//------------------------------------------------------------------------------ +// Method and data for histogramming events and actions taken by each instance +// on each thread. + +// static +bool MessageLoop::enable_histogrammer_ = false; + +// static +void MessageLoop::EnableHistogrammer(bool enable) { + enable_histogrammer_ = enable; +} + +void MessageLoop::StartHistogrammer() { + if (enable_histogrammer_ && !message_histogram_.get() + && StatisticsRecorder::WasStarted()) { + DCHECK(!thread_name_.empty()); + message_histogram_.reset( + new LinearHistogram(("MsgLoop:" + thread_name_).c_str(), + kLeastNonZeroMessageId, + kMaxMessageId, + kNumberOfDistinctMessagesDisplayed)); + message_histogram_->SetFlags(message_histogram_->kHexRangePrintingFlag); + message_histogram_->SetRangeDescriptions(event_descriptions_); + } +} + +void MessageLoop::HistogramEvent(int event) { + if (message_histogram_.get()) + message_histogram_->Add(event); +} + +// Provide a macro that takes an expression (such as a constant, or macro +// constant) and creates a pair to initalize an array of pairs. In this case, +// our pair consists of the expressions value, and the "stringized" version +// of the expression (i.e., the exrpression put in quotes). For example, if +// we have: +// #define FOO 2 +// #define BAR 5 +// then the following: +// VALUE_TO_NUMBER_AND_NAME(FOO + BAR) +// will expand to: +// {7, "FOO + BAR"} +// We use the resulting array as an argument to our histogram, which reads the +// number as a bucket identifier, and proceeds to use the corresponding name +// in the pair (i.e., the quoted string) when printing out a histogram. +#define VALUE_TO_NUMBER_AND_NAME(name) {name, #name}, + +// static +const LinearHistogram::DescriptionPair MessageLoop::event_descriptions_[] = { + // Provide some pretty print capability in our histogram for our internal + // messages. + + // A few events we handle (kindred to messages), and used to profile actions. + VALUE_TO_NUMBER_AND_NAME(kTaskRunEvent) + VALUE_TO_NUMBER_AND_NAME(kTimerEvent) + + {-1, NULL} // The list must be null terminated, per API to histogram. +}; + +//------------------------------------------------------------------------------ +// MessageLoopForUI + +#if defined(OS_WIN) + +void MessageLoopForUI::Run(Dispatcher* dispatcher) { + AutoRunState save_state(this); + state_->dispatcher = dispatcher; + RunHandler(); +} + +void MessageLoopForUI::AddObserver(Observer* observer) { + pump_win()->AddObserver(observer); +} + +void MessageLoopForUI::RemoveObserver(Observer* observer) { + pump_win()->RemoveObserver(observer); +} + +void MessageLoopForUI::WillProcessMessage(const MSG& message) { + pump_win()->WillProcessMessage(message); +} +void MessageLoopForUI::DidProcessMessage(const MSG& message) { + pump_win()->DidProcessMessage(message); +} +void MessageLoopForUI::PumpOutPendingPaintMessages() { + pump_ui()->PumpOutPendingPaintMessages(); +} + +#endif // defined(OS_WIN) + +//------------------------------------------------------------------------------ +// MessageLoopForIO + +#if defined(OS_WIN) + +void MessageLoopForIO::RegisterIOHandler(HANDLE file, IOHandler* handler) { + pump_io()->RegisterIOHandler(file, handler); +} + +bool MessageLoopForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) { + return pump_io()->WaitForIOCompletion(timeout, filter); +} + +#elif defined(OS_POSIX) + +bool MessageLoopForIO::WatchFileDescriptor(int fd, + bool persistent, + Mode mode, + FileDescriptorWatcher *controller, + Watcher *delegate) { + return pump_libevent()->WatchFileDescriptor( + fd, + persistent, + static_cast(mode), + controller, + delegate); +} + +#if defined(CHROMIUM_MOZILLA_BUILD) +bool +MessageLoopForIO::CatchSignal(int sig, + SignalEvent* sigevent, + SignalWatcher* delegate) +{ + return pump_libevent()->CatchSignal(sig, sigevent, delegate); +} +#endif // defined(CHROMIUM_MOZILLA_BUILD) + +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_loop.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_loop.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_loop.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_loop.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,546 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_LOOP_H_ +#define BASE_MESSAGE_LOOP_H_ + +#include +#include +#include +#include + +#include "base/histogram.h" +#include "base/message_pump.h" +#include "base/observer_list.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "base/timer.h" + +#if defined(OS_WIN) +// We need this to declare base::MessagePumpWin::Dispatcher, which we should +// really just eliminate. +#include "base/message_pump_win.h" +#elif defined(OS_POSIX) +#include "base/message_pump_libevent.h" +#endif + +#ifdef CHROMIUM_MOZILLA_BUILD +namespace mozilla { +namespace ipc { + +class DoWorkRunnable; + +} /* namespace ipc */ +} /* namespace mozilla */ +#endif + +// A MessageLoop is used to process events for a particular thread. There is +// at most one MessageLoop instance per thread. +// +// Events include at a minimum Task instances submitted to PostTask or those +// managed by TimerManager. Depending on the type of message pump used by the +// MessageLoop other events such as UI messages may be processed. On Windows +// APC calls (as time permits) and signals sent to a registered set of HANDLEs +// may also be processed. +// +// NOTE: Unless otherwise specified, a MessageLoop's methods may only be called +// on the thread where the MessageLoop's Run method executes. +// +// NOTE: MessageLoop has task reentrancy protection. This means that if a +// task is being processed, a second task cannot start until the first task is +// finished. Reentrancy can happen when processing a task, and an inner +// message pump is created. That inner pump then processes native messages +// which could implicitly start an inner task. Inner message pumps are created +// with dialogs (DialogBox), common dialogs (GetOpenFileName), OLE functions +// (DoDragDrop), printer functions (StartDoc) and *many* others. +// +// Sample workaround when inner task processing is needed: +// bool old_state = MessageLoop::current()->NestableTasksAllowed(); +// MessageLoop::current()->SetNestableTasksAllowed(true); +// HRESULT hr = DoDragDrop(...); // Implicitly runs a modal message loop here. +// MessageLoop::current()->SetNestableTasksAllowed(old_state); +// // Process hr (the result returned by DoDragDrop(). +// +// Please be SURE your task is reentrant (nestable) and all global variables +// are stable and accessible before calling SetNestableTasksAllowed(true). +// +class MessageLoop : public base::MessagePump::Delegate { + +#ifdef CHROMIUM_MOZILLA_BUILD + friend class mozilla::ipc::DoWorkRunnable; +#endif + +public: + static void EnableHistogrammer(bool enable_histogrammer); + + // A DestructionObserver is notified when the current MessageLoop is being + // destroyed. These obsevers are notified prior to MessageLoop::current() + // being changed to return NULL. This gives interested parties the chance to + // do final cleanup that depends on the MessageLoop. + // + // NOTE: Any tasks posted to the MessageLoop during this notification will + // not be run. Instead, they will be deleted. + // + class DestructionObserver { + public: + virtual ~DestructionObserver() {} + virtual void WillDestroyCurrentMessageLoop() = 0; + }; + + // Add a DestructionObserver, which will start receiving notifications + // immediately. + void AddDestructionObserver(DestructionObserver* destruction_observer); + + // Remove a DestructionObserver. It is safe to call this method while a + // DestructionObserver is receiving a notification callback. + void RemoveDestructionObserver(DestructionObserver* destruction_observer); + + // The "PostTask" family of methods call the task's Run method asynchronously + // from within a message loop at some point in the future. + // + // With the PostTask variant, tasks are invoked in FIFO order, inter-mixed + // with normal UI or IO event processing. With the PostDelayedTask variant, + // tasks are called after at least approximately 'delay_ms' have elapsed. + // + // The NonNestable variants work similarly except that they promise never to + // dispatch the task from a nested invocation of MessageLoop::Run. Instead, + // such tasks get deferred until the top-most MessageLoop::Run is executing. + // + // The MessageLoop takes ownership of the Task, and deletes it after it has + // been Run(). + // + // NOTE: These methods may be called on any thread. The Task will be invoked + // on the thread that executes MessageLoop::Run(). + + void PostTask( + const tracked_objects::Location& from_here, Task* task); + + void PostDelayedTask( + const tracked_objects::Location& from_here, Task* task, int delay_ms); + + void PostNonNestableTask( + const tracked_objects::Location& from_here, Task* task); + + void PostNonNestableDelayedTask( + const tracked_objects::Location& from_here, Task* task, int delay_ms); + + // A variant on PostTask that deletes the given object. This is useful + // if the object needs to live until the next run of the MessageLoop (for + // example, deleting a RenderProcessHost from within an IPC callback is not + // good). + // + // NOTE: This method may be called on any thread. The object will be deleted + // on the thread that executes MessageLoop::Run(). If this is not the same + // as the thread that calls PostDelayedTask(FROM_HERE, ), then T MUST inherit + // from RefCountedThreadSafe! + template + void DeleteSoon(const tracked_objects::Location& from_here, T* object) { + PostNonNestableTask(from_here, new DeleteTask(object)); + } + + // A variant on PostTask that releases the given reference counted object + // (by calling its Release method). This is useful if the object needs to + // live until the next run of the MessageLoop, or if the object needs to be + // released on a particular thread. + // + // NOTE: This method may be called on any thread. The object will be + // released (and thus possibly deleted) on the thread that executes + // MessageLoop::Run(). If this is not the same as the thread that calls + // PostDelayedTask(FROM_HERE, ), then T MUST inherit from + // RefCountedThreadSafe! + template + void ReleaseSoon(const tracked_objects::Location& from_here, T* object) { + PostNonNestableTask(from_here, new ReleaseTask(object)); + } + + // Run the message loop. + void Run(); + + // Process all pending tasks, windows messages, etc., but don't wait/sleep. + // Return as soon as all items that can be run are taken care of. + void RunAllPending(); + + // Signals the Run method to return after it is done processing all pending + // messages. This method may only be called on the same thread that called + // Run, and Run must still be on the call stack. + // + // Use QuitTask if you need to Quit another thread's MessageLoop, but note + // that doing so is fairly dangerous if the target thread makes nested calls + // to MessageLoop::Run. The problem being that you won't know which nested + // run loop you are quiting, so be careful! + // + void Quit(); + + // Invokes Quit on the current MessageLoop when run. Useful to schedule an + // arbitrary MessageLoop to Quit. + class QuitTask : public Task { + public: + virtual void Run() { + MessageLoop::current()->Quit(); + } + }; + + // A MessageLoop has a particular type, which indicates the set of + // asynchronous events it may process in addition to tasks and timers. + // + // TYPE_DEFAULT + // This type of ML only supports tasks and timers. + // + // TYPE_UI + // This type of ML also supports native UI events (e.g., Windows messages). + // See also MessageLoopForUI. + // + // TYPE_IO + // This type of ML also supports asynchronous IO. See also + // MessageLoopForIO. + // + // TYPE_MOZILLA_CHILD + // This type of ML is used in Mozilla child processes which initialize + // XPCOM and use the gecko event loop. + // + // TYPE_MOZILLA_UI + // This type of ML is used in Mozilla parent processes which initialize + // XPCOM and use the gecko event loop. + // + enum Type { + TYPE_DEFAULT, + TYPE_UI, + TYPE_IO +#ifdef CHROMIUM_MOZILLA_BUILD + , TYPE_MOZILLA_CHILD + , TYPE_MOZILLA_UI +#endif + }; + + // Normally, it is not necessary to instantiate a MessageLoop. Instead, it + // is typical to make use of the current thread's MessageLoop instance. + explicit MessageLoop(Type type = TYPE_DEFAULT); + ~MessageLoop(); + + // Returns the type passed to the constructor. + Type type() const { return type_; } + + // Optional call to connect the thread name with this loop. + void set_thread_name(const std::string& thread_name) { + DCHECK(thread_name_.empty()) << "Should not rename this thread!"; + thread_name_ = thread_name; + } + const std::string& thread_name() const { return thread_name_; } + + // Returns the MessageLoop object for the current thread, or null if none. + static MessageLoop* current(); + + // Enables or disables the recursive task processing. This happens in the case + // of recursive message loops. Some unwanted message loop may occurs when + // using common controls or printer functions. By default, recursive task + // processing is disabled. + // + // The specific case where tasks get queued is: + // - The thread is running a message loop. + // - It receives a task #1 and execute it. + // - The task #1 implicitly start a message loop, like a MessageBox in the + // unit test. This can also be StartDoc or GetSaveFileName. + // - The thread receives a task #2 before or while in this second message + // loop. + // - With NestableTasksAllowed set to true, the task #2 will run right away. + // Otherwise, it will get executed right after task #1 completes at "thread + // message loop level". + void SetNestableTasksAllowed(bool allowed); + void ScheduleWork(); + bool NestableTasksAllowed() const; + + // Enables or disables the restoration during an exception of the unhandled + // exception filter that was active when Run() was called. This can happen + // if some third party code call SetUnhandledExceptionFilter() and never + // restores the previous filter. + void set_exception_restoration(bool restore) { + exception_restoration_ = restore; + } + + //---------------------------------------------------------------------------- + protected: + struct RunState { + // Used to count how many Run() invocations are on the stack. + int run_depth; + + // Used to record that Quit() was called, or that we should quit the pump + // once it becomes idle. + bool quit_received; + +#if defined(OS_WIN) + base::MessagePumpWin::Dispatcher* dispatcher; +#endif + }; + + class AutoRunState : RunState { + public: + explicit AutoRunState(MessageLoop* loop); + ~AutoRunState(); + private: + MessageLoop* loop_; + RunState* previous_state_; + }; + + // This structure is copied around by value. + struct PendingTask { + Task* task; // The task to run. + base::Time delayed_run_time; // The time when the task should be run. + int sequence_num; // Used to facilitate sorting by run time. + bool nestable; // True if OK to dispatch from a nested loop. + + PendingTask(Task* task, bool nestable) + : task(task), sequence_num(0), nestable(nestable) { + } + + // Used to support sorting. + bool operator<(const PendingTask& other) const; + }; + + typedef std::queue TaskQueue; + typedef std::priority_queue DelayedTaskQueue; + +#if defined(OS_WIN) + base::MessagePumpWin* pump_win() { + return static_cast(pump_.get()); + } +#elif defined(OS_POSIX) + base::MessagePumpLibevent* pump_libevent() { + return static_cast(pump_.get()); + } +#endif + + // A function to encapsulate all the exception handling capability in the + // stacks around the running of a main message loop. It will run the message + // loop in a SEH try block or not depending on the set_SEH_restoration() + // flag. + void RunHandler(); + + // A surrounding stack frame around the running of the message loop that + // supports all saving and restoring of state, as is needed for any/all (ugly) + // recursive calls. + void RunInternal(); + + // Called to process any delayed non-nestable tasks. + bool ProcessNextDelayedNonNestableTask(); + + //---------------------------------------------------------------------------- + // Run a work_queue_ task or new_task, and delete it (if it was processed by + // PostTask). If there are queued tasks, the oldest one is executed and + // new_task is queued. new_task is optional and can be NULL. In this NULL + // case, the method will run one pending task (if any exist). Returns true if + // it executes a task. Queued tasks accumulate only when there is a + // non-nestable task currently processing, in which case the new_task is + // appended to the list work_queue_. Such re-entrancy generally happens when + // an unrequested message pump (typical of a native dialog) is executing in + // the context of a task. + bool QueueOrRunTask(Task* new_task); + + // Runs the specified task and deletes it. + void RunTask(Task* task); + + // Calls RunTask or queues the pending_task on the deferred task list if it + // cannot be run right now. Returns true if the task was run. + bool DeferOrRunPendingTask(const PendingTask& pending_task); + + // Adds the pending task to delayed_work_queue_. + void AddToDelayedWorkQueue(const PendingTask& pending_task); + + // Load tasks from the incoming_queue_ into work_queue_ if the latter is + // empty. The former requires a lock to access, while the latter is directly + // accessible on this thread. + void ReloadWorkQueue(); + + // Delete tasks that haven't run yet without running them. Used in the + // destructor to make sure all the task's destructors get called. Returns + // true if some work was done. + bool DeletePendingTasks(); + + // Post a task to our incomming queue. + void PostTask_Helper(const tracked_objects::Location& from_here, Task* task, + int delay_ms, bool nestable); + + // base::MessagePump::Delegate methods: + virtual bool DoWork(); + virtual bool DoDelayedWork(base::Time* next_delayed_work_time); + virtual bool DoIdleWork(); + + // Start recording histogram info about events and action IF it was enabled + // and IF the statistics recorder can accept a registration of our histogram. + void StartHistogrammer(); + + // Add occurence of event to our histogram, so that we can see what is being + // done in a specific MessageLoop instance (i.e., specific thread). + // If message_histogram_ is NULL, this is a no-op. + void HistogramEvent(int event); + + static const LinearHistogram::DescriptionPair event_descriptions_[]; + static bool enable_histogrammer_; + + Type type_; + + // A list of tasks that need to be processed by this instance. Note that + // this queue is only accessed (push/pop) by our current thread. + TaskQueue work_queue_; + + // Contains delayed tasks, sorted by their 'delayed_run_time' property. + DelayedTaskQueue delayed_work_queue_; + + // A queue of non-nestable tasks that we had to defer because when it came + // time to execute them we were in a nested message loop. They will execute + // once we're out of nested message loops. + TaskQueue deferred_non_nestable_work_queue_; + + scoped_refptr pump_; + + ObserverList destruction_observers_; + + // A recursion block that prevents accidentally running additonal tasks when + // insider a (accidentally induced?) nested message pump. + bool nestable_tasks_allowed_; + + bool exception_restoration_; + + std::string thread_name_; + // A profiling histogram showing the counts of various messages and events. + scoped_ptr message_histogram_; + + // A null terminated list which creates an incoming_queue of tasks that are + // aquired under a mutex for processing on this instance's thread. These tasks + // have not yet been sorted out into items for our work_queue_ vs items that + // will be handled by the TimerManager. + TaskQueue incoming_queue_; + // Protect access to incoming_queue_. + Lock incoming_queue_lock_; + + RunState* state_; + + // The next sequence number to use for delayed tasks. + int next_sequence_num_; + + DISALLOW_COPY_AND_ASSIGN(MessageLoop); +}; + +//----------------------------------------------------------------------------- +// MessageLoopForUI extends MessageLoop with methods that are particular to a +// MessageLoop instantiated with TYPE_UI. +// +// This class is typically used like so: +// MessageLoopForUI::current()->...call some method... +// +class MessageLoopForUI : public MessageLoop { + public: + MessageLoopForUI(Type type=TYPE_UI) : MessageLoop(type) { + } + + // Returns the MessageLoopForUI of the current thread. + static MessageLoopForUI* current() { + MessageLoop* loop = MessageLoop::current(); + if (!loop) + return NULL; +#ifdef CHROMIUM_MOZILLA_BUILD + Type type = loop->type(); + DCHECK(type == MessageLoop::TYPE_UI || + type == MessageLoop::TYPE_MOZILLA_UI); +#else + DCHECK_EQ(MessageLoop::TYPE_UI, loop->type()); +#endif + return static_cast(loop); + } + +#if defined(OS_WIN) + typedef base::MessagePumpWin::Dispatcher Dispatcher; + typedef base::MessagePumpWin::Observer Observer; + + // Please see MessagePumpWin for definitions of these methods. + void Run(Dispatcher* dispatcher); + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + void WillProcessMessage(const MSG& message); + void DidProcessMessage(const MSG& message); + void PumpOutPendingPaintMessages(); + + protected: + // TODO(rvargas): Make this platform independent. + base::MessagePumpForUI* pump_ui() { + return static_cast(pump_.get()); + } +#endif // defined(OS_WIN) +}; + +// Do not add any member variables to MessageLoopForUI! This is important b/c +// MessageLoopForUI is often allocated via MessageLoop(TYPE_UI). Any extra +// data that you need should be stored on the MessageLoop's pump_ instance. +COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForUI), + MessageLoopForUI_should_not_have_extra_member_variables); + +//----------------------------------------------------------------------------- +// MessageLoopForIO extends MessageLoop with methods that are particular to a +// MessageLoop instantiated with TYPE_IO. +// +// This class is typically used like so: +// MessageLoopForIO::current()->...call some method... +// +class MessageLoopForIO : public MessageLoop { + public: + MessageLoopForIO() : MessageLoop(TYPE_IO) { + } + + // Returns the MessageLoopForIO of the current thread. + static MessageLoopForIO* current() { + MessageLoop* loop = MessageLoop::current(); + DCHECK_EQ(MessageLoop::TYPE_IO, loop->type()); + return static_cast(loop); + } + +#if defined(OS_WIN) + typedef base::MessagePumpForIO::IOHandler IOHandler; + typedef base::MessagePumpForIO::IOContext IOContext; + + // Please see MessagePumpWin for definitions of these methods. + void RegisterIOHandler(HANDLE file_handle, IOHandler* handler); + bool WaitForIOCompletion(DWORD timeout, IOHandler* filter); + + protected: + // TODO(rvargas): Make this platform independent. + base::MessagePumpForIO* pump_io() { + return static_cast(pump_.get()); + } + +#elif defined(OS_POSIX) + typedef base::MessagePumpLibevent::Watcher Watcher; + typedef base::MessagePumpLibevent::FileDescriptorWatcher + FileDescriptorWatcher; + + enum Mode { + WATCH_READ = base::MessagePumpLibevent::WATCH_READ, + WATCH_WRITE = base::MessagePumpLibevent::WATCH_WRITE, + WATCH_READ_WRITE = base::MessagePumpLibevent::WATCH_READ_WRITE + }; + + // Please see MessagePumpLibevent for definition. + bool WatchFileDescriptor(int fd, + bool persistent, + Mode mode, + FileDescriptorWatcher *controller, + Watcher *delegate); + +#if defined(CHROMIUM_MOZILLA_BUILD) + typedef base::MessagePumpLibevent::SignalEvent SignalEvent; + typedef base::MessagePumpLibevent::SignalWatcher SignalWatcher; + bool CatchSignal(int sig, + SignalEvent* sigevent, + SignalWatcher* delegate); +#endif // defined(CHROMIUM_MOZILLA_BUILD) + +#endif // defined(OS_POSIX) +}; + +// Do not add any member variables to MessageLoopForIO! This is important b/c +// MessageLoopForIO is often allocated via MessageLoop(TYPE_IO). Any extra +// data that you need should be stored on the MessageLoop's pump_ instance. +COMPILE_ASSERT(sizeof(MessageLoop) == sizeof(MessageLoopForIO), + MessageLoopForIO_should_not_have_extra_member_variables); + +#endif // BASE_MESSAGE_LOOP_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_loop_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_loop_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_loop_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_loop_unittest.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,1458 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/platform_thread.h" +#include "base/ref_counted.h" +#include "base/thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(OS_WIN) +#include "base/message_pump_win.h" +#include "base/scoped_handle.h" +#endif +#if defined(OS_POSIX) +#include "base/message_pump_libevent.h" +#endif + +using base::Thread; +using base::Time; +using base::TimeDelta; + +// TODO(darin): Platform-specific MessageLoop tests should be grouped together +// to avoid chopping this file up with so many #ifdefs. + +namespace { + +class MessageLoopTest : public testing::Test {}; + +class Foo : public base::RefCounted { + public: + Foo() : test_count_(0) { + } + + void Test0() { + ++test_count_; + } + + void Test1ConstRef(const std::string& a) { + ++test_count_; + result_.append(a); + } + + void Test1Ptr(std::string* a) { + ++test_count_; + result_.append(*a); + } + + void Test1Int(int a) { + test_count_ += a; + } + + void Test2Ptr(std::string* a, std::string* b) { + ++test_count_; + result_.append(*a); + result_.append(*b); + } + + void Test2Mixed(const std::string& a, std::string* b) { + ++test_count_; + result_.append(a); + result_.append(*b); + } + + int test_count() const { return test_count_; } + const std::string& result() const { return result_; } + + private: + int test_count_; + std::string result_; +}; + +class QuitMsgLoop : public base::RefCounted { + public: + void QuitNow() { + MessageLoop::current()->Quit(); + } +}; + +void RunTest_PostTask(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + // Add tests to message loop + scoped_refptr foo = new Foo(); + std::string a("a"), b("b"), c("c"), d("d"); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + foo.get(), &Foo::Test0)); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + foo.get(), &Foo::Test1ConstRef, a)); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + foo.get(), &Foo::Test1Ptr, &b)); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + foo.get(), &Foo::Test1Int, 100)); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + foo.get(), &Foo::Test2Ptr, &a, &c)); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + foo.get(), &Foo::Test2Mixed, a, &d)); + + // After all tests, post a message that will shut down the message loop + scoped_refptr quit = new QuitMsgLoop(); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + quit.get(), &QuitMsgLoop::QuitNow)); + + // Now kick things off + MessageLoop::current()->Run(); + + EXPECT_EQ(foo->test_count(), 105); + EXPECT_EQ(foo->result(), "abacad"); +} + +void RunTest_PostTask_SEH(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + // Add tests to message loop + scoped_refptr foo = new Foo(); + std::string a("a"), b("b"), c("c"), d("d"); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + foo.get(), &Foo::Test0)); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + foo.get(), &Foo::Test1ConstRef, a)); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + foo.get(), &Foo::Test1Ptr, &b)); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + foo.get(), &Foo::Test1Int, 100)); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + foo.get(), &Foo::Test2Ptr, &a, &c)); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + foo.get(), &Foo::Test2Mixed, a, &d)); + + // After all tests, post a message that will shut down the message loop + scoped_refptr quit = new QuitMsgLoop(); + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + quit.get(), &QuitMsgLoop::QuitNow)); + + // Now kick things off with the SEH block active. + MessageLoop::current()->set_exception_restoration(true); + MessageLoop::current()->Run(); + MessageLoop::current()->set_exception_restoration(false); + + EXPECT_EQ(foo->test_count(), 105); + EXPECT_EQ(foo->result(), "abacad"); +} + +// This class runs slowly to simulate a large amount of work being done. +class SlowTask : public Task { + public: + SlowTask(int pause_ms, int* quit_counter) + : pause_ms_(pause_ms), quit_counter_(quit_counter) { + } + virtual void Run() { + PlatformThread::Sleep(pause_ms_); + if (--(*quit_counter_) == 0) + MessageLoop::current()->Quit(); + } + private: + int pause_ms_; + int* quit_counter_; +}; + +// This class records the time when Run was called in a Time object, which is +// useful for building a variety of MessageLoop tests. +class RecordRunTimeTask : public SlowTask { + public: + RecordRunTimeTask(Time* run_time, int* quit_counter) + : SlowTask(10, quit_counter), run_time_(run_time) { + } + virtual void Run() { + *run_time_ = Time::Now(); + // Cause our Run function to take some time to execute. As a result we can + // count on subsequent RecordRunTimeTask objects running at a future time, + // without worry about the resolution of our system clock being an issue. + SlowTask::Run(); + } + private: + Time* run_time_; +}; + +void RunTest_PostDelayedTask_Basic(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + // Test that PostDelayedTask results in a delayed task. + + const int kDelayMS = 100; + + int num_tasks = 1; + Time run_time; + + loop.PostDelayedTask( + FROM_HERE, new RecordRunTimeTask(&run_time, &num_tasks), kDelayMS); + + Time time_before_run = Time::Now(); + loop.Run(); + Time time_after_run = Time::Now(); + + EXPECT_EQ(0, num_tasks); + EXPECT_LT(kDelayMS, (time_after_run - time_before_run).InMilliseconds()); +} + +void RunTest_PostDelayedTask_InDelayOrder(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + // Test that two tasks with different delays run in the right order. + + int num_tasks = 2; + Time run_time1, run_time2; + + loop.PostDelayedTask( + FROM_HERE, new RecordRunTimeTask(&run_time1, &num_tasks), 200); + // If we get a large pause in execution (due to a context switch) here, this + // test could fail. + loop.PostDelayedTask( + FROM_HERE, new RecordRunTimeTask(&run_time2, &num_tasks), 10); + + loop.Run(); + EXPECT_EQ(0, num_tasks); + + EXPECT_TRUE(run_time2 < run_time1); +} + +void RunTest_PostDelayedTask_InPostOrder(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + // Test that two tasks with the same delay run in the order in which they + // were posted. + // + // NOTE: This is actually an approximate test since the API only takes a + // "delay" parameter, so we are not exactly simulating two tasks that get + // posted at the exact same time. It would be nice if the API allowed us to + // specify the desired run time. + + const int kDelayMS = 100; + + int num_tasks = 2; + Time run_time1, run_time2; + + loop.PostDelayedTask( + FROM_HERE, new RecordRunTimeTask(&run_time1, &num_tasks), kDelayMS); + loop.PostDelayedTask( + FROM_HERE, new RecordRunTimeTask(&run_time2, &num_tasks), kDelayMS); + + loop.Run(); + EXPECT_EQ(0, num_tasks); + + EXPECT_TRUE(run_time1 < run_time2); +} + +void RunTest_PostDelayedTask_InPostOrder_2( + MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + // Test that a delayed task still runs after a normal tasks even if the + // normal tasks take a long time to run. + + const int kPauseMS = 50; + + int num_tasks = 2; + Time run_time; + + loop.PostTask( + FROM_HERE, new SlowTask(kPauseMS, &num_tasks)); + loop.PostDelayedTask( + FROM_HERE, new RecordRunTimeTask(&run_time, &num_tasks), 10); + + Time time_before_run = Time::Now(); + loop.Run(); + Time time_after_run = Time::Now(); + + EXPECT_EQ(0, num_tasks); + + EXPECT_LT(kPauseMS, (time_after_run - time_before_run).InMilliseconds()); +} + +void RunTest_PostDelayedTask_InPostOrder_3( + MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + // Test that a delayed task still runs after a pile of normal tasks. The key + // difference between this test and the previous one is that here we return + // the MessageLoop a lot so we give the MessageLoop plenty of opportunities + // to maybe run the delayed task. It should know not to do so until the + // delayed task's delay has passed. + + int num_tasks = 11; + Time run_time1, run_time2; + + // Clutter the ML with tasks. + for (int i = 1; i < num_tasks; ++i) + loop.PostTask(FROM_HERE, new RecordRunTimeTask(&run_time1, &num_tasks)); + + loop.PostDelayedTask( + FROM_HERE, new RecordRunTimeTask(&run_time2, &num_tasks), 1); + + loop.Run(); + EXPECT_EQ(0, num_tasks); + + EXPECT_TRUE(run_time2 > run_time1); +} + +void RunTest_PostDelayedTask_SharedTimer(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + // Test that the interval of the timer, used to run the next delayed task, is + // set to a value corresponding to when the next delayed task should run. + + // By setting num_tasks to 1, we ensure that the first task to run causes the + // run loop to exit. + int num_tasks = 1; + Time run_time1, run_time2; + + loop.PostDelayedTask( + FROM_HERE, new RecordRunTimeTask(&run_time1, &num_tasks), 1000000); + loop.PostDelayedTask( + FROM_HERE, new RecordRunTimeTask(&run_time2, &num_tasks), 10); + + Time start_time = Time::Now(); + + loop.Run(); + EXPECT_EQ(0, num_tasks); + + // Ensure that we ran in far less time than the slower timer. + TimeDelta total_time = Time::Now() - start_time; + EXPECT_GT(5000, total_time.InMilliseconds()); + + // In case both timers somehow run at nearly the same time, sleep a little + // and then run all pending to force them both to have run. This is just + // encouraging flakiness if there is any. + PlatformThread::Sleep(100); + loop.RunAllPending(); + + EXPECT_TRUE(run_time1.is_null()); + EXPECT_FALSE(run_time2.is_null()); +} + +#if defined(OS_WIN) + +class SubPumpTask : public Task { + public: + virtual void Run() { + MessageLoop::current()->SetNestableTasksAllowed(true); + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + MessageLoop::current()->Quit(); + } +}; + +class SubPumpQuitTask : public Task { + public: + SubPumpQuitTask() { + } + virtual void Run() { + PostQuitMessage(0); + } +}; + +void RunTest_PostDelayedTask_SharedTimer_SubPump() { + MessageLoop loop(MessageLoop::TYPE_UI); + + // Test that the interval of the timer, used to run the next delayed task, is + // set to a value corresponding to when the next delayed task should run. + + // By setting num_tasks to 1, we ensure that the first task to run causes the + // run loop to exit. + int num_tasks = 1; + Time run_time; + + loop.PostTask(FROM_HERE, new SubPumpTask()); + + // This very delayed task should never run. + loop.PostDelayedTask( + FROM_HERE, new RecordRunTimeTask(&run_time, &num_tasks), 1000000); + + // This slightly delayed task should run from within SubPumpTask::Run(). + loop.PostDelayedTask( + FROM_HERE, new SubPumpQuitTask(), 10); + + Time start_time = Time::Now(); + + loop.Run(); + EXPECT_EQ(1, num_tasks); + + // Ensure that we ran in far less time than the slower timer. + TimeDelta total_time = Time::Now() - start_time; + EXPECT_GT(5000, total_time.InMilliseconds()); + + // In case both timers somehow run at nearly the same time, sleep a little + // and then run all pending to force them both to have run. This is just + // encouraging flakiness if there is any. + PlatformThread::Sleep(100); + loop.RunAllPending(); + + EXPECT_TRUE(run_time.is_null()); +} + +#endif // defined(OS_WIN) + +class RecordDeletionTask : public Task { + public: + RecordDeletionTask(Task* post_on_delete, bool* was_deleted) + : post_on_delete_(post_on_delete), was_deleted_(was_deleted) { + } + ~RecordDeletionTask() { + *was_deleted_ = true; + if (post_on_delete_) + MessageLoop::current()->PostTask(FROM_HERE, post_on_delete_); + } + virtual void Run() {} + private: + Task* post_on_delete_; + bool* was_deleted_; +}; + +void RunTest_EnsureTaskDeletion(MessageLoop::Type message_loop_type) { + bool a_was_deleted = false; + bool b_was_deleted = false; + { + MessageLoop loop(message_loop_type); + loop.PostTask( + FROM_HERE, new RecordDeletionTask(NULL, &a_was_deleted)); + loop.PostDelayedTask( + FROM_HERE, new RecordDeletionTask(NULL, &b_was_deleted), 1000); + } + EXPECT_TRUE(a_was_deleted); + EXPECT_TRUE(b_was_deleted); +} + +void RunTest_EnsureTaskDeletion_Chain(MessageLoop::Type message_loop_type) { + bool a_was_deleted = false; + bool b_was_deleted = false; + bool c_was_deleted = false; + { + MessageLoop loop(message_loop_type); + RecordDeletionTask* a = new RecordDeletionTask(NULL, &a_was_deleted); + RecordDeletionTask* b = new RecordDeletionTask(a, &b_was_deleted); + RecordDeletionTask* c = new RecordDeletionTask(b, &c_was_deleted); + loop.PostTask(FROM_HERE, c); + } + EXPECT_TRUE(a_was_deleted); + EXPECT_TRUE(b_was_deleted); + EXPECT_TRUE(c_was_deleted); +} + +class NestingTest : public Task { + public: + explicit NestingTest(int* depth) : depth_(depth) { + } + void Run() { + if (*depth_ > 0) { + *depth_ -= 1; + MessageLoop::current()->PostTask(FROM_HERE, new NestingTest(depth_)); + + MessageLoop::current()->SetNestableTasksAllowed(true); + MessageLoop::current()->Run(); + } + MessageLoop::current()->Quit(); + } + private: + int* depth_; +}; + +#if defined(OS_WIN) + +LONG WINAPI BadExceptionHandler(EXCEPTION_POINTERS *ex_info) { + ADD_FAILURE() << "bad exception handler"; + ::ExitProcess(ex_info->ExceptionRecord->ExceptionCode); + return EXCEPTION_EXECUTE_HANDLER; +} + +// This task throws an SEH exception: initially write to an invalid address. +// If the right SEH filter is installed, it will fix the error. +class CrasherTask : public Task { + public: + // Ctor. If trash_SEH_handler is true, the task will override the unhandled + // exception handler with one sure to crash this test. + explicit CrasherTask(bool trash_SEH_handler) + : trash_SEH_handler_(trash_SEH_handler) { + } + void Run() { + PlatformThread::Sleep(1); + if (trash_SEH_handler_) + ::SetUnhandledExceptionFilter(&BadExceptionHandler); + // Generate a SEH fault. We do it in asm to make sure we know how to undo + // the damage. + +#if defined(_M_IX86) + + __asm { + mov eax, dword ptr [CrasherTask::bad_array_] + mov byte ptr [eax], 66 + } + +#elif defined(_M_X64) + + bad_array_[0] = 66; + +#else +#error "needs architecture support" +#endif + + MessageLoop::current()->Quit(); + } + // Points the bad array to a valid memory location. + static void FixError() { + bad_array_ = &valid_store_; + } + + private: + bool trash_SEH_handler_; + static volatile char* bad_array_; + static char valid_store_; +}; + +volatile char* CrasherTask::bad_array_ = 0; +char CrasherTask::valid_store_ = 0; + +// This SEH filter fixes the problem and retries execution. Fixing requires +// that the last instruction: mov eax, [CrasherTask::bad_array_] to be retried +// so we move the instruction pointer 5 bytes back. +LONG WINAPI HandleCrasherTaskException(EXCEPTION_POINTERS *ex_info) { + if (ex_info->ExceptionRecord->ExceptionCode != EXCEPTION_ACCESS_VIOLATION) + return EXCEPTION_EXECUTE_HANDLER; + + CrasherTask::FixError(); + +#if defined(_M_IX86) + + ex_info->ContextRecord->Eip -= 5; + +#elif defined(_M_X64) + + ex_info->ContextRecord->Rip -= 5; + +#endif + + return EXCEPTION_CONTINUE_EXECUTION; +} + +void RunTest_Crasher(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + if (::IsDebuggerPresent()) + return; + + LPTOP_LEVEL_EXCEPTION_FILTER old_SEH_filter = + ::SetUnhandledExceptionFilter(&HandleCrasherTaskException); + + MessageLoop::current()->PostTask(FROM_HERE, new CrasherTask(false)); + MessageLoop::current()->set_exception_restoration(true); + MessageLoop::current()->Run(); + MessageLoop::current()->set_exception_restoration(false); + + ::SetUnhandledExceptionFilter(old_SEH_filter); +} + +void RunTest_CrasherNasty(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + if (::IsDebuggerPresent()) + return; + + LPTOP_LEVEL_EXCEPTION_FILTER old_SEH_filter = + ::SetUnhandledExceptionFilter(&HandleCrasherTaskException); + + MessageLoop::current()->PostTask(FROM_HERE, new CrasherTask(true)); + MessageLoop::current()->set_exception_restoration(true); + MessageLoop::current()->Run(); + MessageLoop::current()->set_exception_restoration(false); + + ::SetUnhandledExceptionFilter(old_SEH_filter); +} + +#endif // defined(OS_WIN) + +void RunTest_Nesting(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + int depth = 100; + MessageLoop::current()->PostTask(FROM_HERE, new NestingTest(&depth)); + MessageLoop::current()->Run(); + EXPECT_EQ(depth, 0); +} + +const wchar_t* const kMessageBoxTitle = L"MessageLoop Unit Test"; + +enum TaskType { + MESSAGEBOX, + ENDDIALOG, + RECURSIVE, + TIMEDMESSAGELOOP, + QUITMESSAGELOOP, + ORDERERD, + PUMPS, +}; + +// Saves the order in which the tasks executed. +struct TaskItem { + TaskItem(TaskType t, int c, bool s) + : type(t), + cookie(c), + start(s) { + } + + TaskType type; + int cookie; + bool start; + + bool operator == (const TaskItem& other) const { + return type == other.type && cookie == other.cookie && start == other.start; + } +}; + +typedef std::vector TaskList; + +std::ostream& operator <<(std::ostream& os, TaskType type) { + switch (type) { + case MESSAGEBOX: os << "MESSAGEBOX"; break; + case ENDDIALOG: os << "ENDDIALOG"; break; + case RECURSIVE: os << "RECURSIVE"; break; + case TIMEDMESSAGELOOP: os << "TIMEDMESSAGELOOP"; break; + case QUITMESSAGELOOP: os << "QUITMESSAGELOOP"; break; + case ORDERERD: os << "ORDERERD"; break; + case PUMPS: os << "PUMPS"; break; + default: + NOTREACHED(); + os << "Unknown TaskType"; + break; + } + return os; +} + +std::ostream& operator <<(std::ostream& os, const TaskItem& item) { + if (item.start) + return os << item.type << " " << item.cookie << " starts"; + else + return os << item.type << " " << item.cookie << " ends"; +} + +// Saves the order the tasks ran. +class OrderedTasks : public Task { + public: + OrderedTasks(TaskList* order, int cookie) + : order_(order), + type_(ORDERERD), + cookie_(cookie) { + } + OrderedTasks(TaskList* order, TaskType type, int cookie) + : order_(order), + type_(type), + cookie_(cookie) { + } + + void RunStart() { + TaskItem item(type_, cookie_, true); + DLOG(INFO) << item; + order_->push_back(item); + } + void RunEnd() { + TaskItem item(type_, cookie_, false); + DLOG(INFO) << item; + order_->push_back(item); + } + + virtual void Run() { + RunStart(); + RunEnd(); + } + + protected: + TaskList* order() const { + return order_; + } + + int cookie() const { + return cookie_; + } + + private: + TaskList* order_; + TaskType type_; + int cookie_; +}; + +#if defined(OS_WIN) + +// MessageLoop implicitly start a "modal message loop". Modal dialog boxes, +// common controls (like OpenFile) and StartDoc printing function can cause +// implicit message loops. +class MessageBoxTask : public OrderedTasks { + public: + MessageBoxTask(TaskList* order, int cookie, bool is_reentrant) + : OrderedTasks(order, MESSAGEBOX, cookie), + is_reentrant_(is_reentrant) { + } + + virtual void Run() { + RunStart(); + if (is_reentrant_) + MessageLoop::current()->SetNestableTasksAllowed(true); + MessageBox(NULL, L"Please wait...", kMessageBoxTitle, MB_OK); + RunEnd(); + } + + private: + bool is_reentrant_; +}; + +// Will end the MessageBox. +class EndDialogTask : public OrderedTasks { + public: + EndDialogTask(TaskList* order, int cookie) + : OrderedTasks(order, ENDDIALOG, cookie) { + } + + virtual void Run() { + RunStart(); + HWND window = GetActiveWindow(); + if (window != NULL) { + EXPECT_NE(EndDialog(window, IDCONTINUE), 0); + // Cheap way to signal that the window wasn't found if RunEnd() isn't + // called. + RunEnd(); + } + } +}; + +#endif // defined(OS_WIN) + +class RecursiveTask : public OrderedTasks { + public: + RecursiveTask(int depth, TaskList* order, int cookie, bool is_reentrant) + : OrderedTasks(order, RECURSIVE, cookie), + depth_(depth), + is_reentrant_(is_reentrant) { + } + + virtual void Run() { + RunStart(); + if (depth_ > 0) { + if (is_reentrant_) + MessageLoop::current()->SetNestableTasksAllowed(true); + MessageLoop::current()->PostTask(FROM_HERE, + new RecursiveTask(depth_ - 1, order(), cookie(), is_reentrant_)); + } + RunEnd(); + } + + private: + int depth_; + bool is_reentrant_; +}; + +class QuitTask : public OrderedTasks { + public: + QuitTask(TaskList* order, int cookie) + : OrderedTasks(order, QUITMESSAGELOOP, cookie) { + } + + virtual void Run() { + RunStart(); + MessageLoop::current()->Quit(); + RunEnd(); + } +}; + +#if defined(OS_WIN) + +class Recursive2Tasks : public Task { + public: + Recursive2Tasks(MessageLoop* target, + HANDLE event, + bool expect_window, + TaskList* order, + bool is_reentrant) + : target_(target), + event_(event), + expect_window_(expect_window), + order_(order), + is_reentrant_(is_reentrant) { + } + + virtual void Run() { + target_->PostTask(FROM_HERE, + new RecursiveTask(2, order_, 1, is_reentrant_)); + target_->PostTask(FROM_HERE, + new MessageBoxTask(order_, 2, is_reentrant_)); + target_->PostTask(FROM_HERE, + new RecursiveTask(2, order_, 3, is_reentrant_)); + // The trick here is that for recursive task processing, this task will be + // ran _inside_ the MessageBox message loop, dismissing the MessageBox + // without a chance. + // For non-recursive task processing, this will be executed _after_ the + // MessageBox will have been dismissed by the code below, where + // expect_window_ is true. + target_->PostTask(FROM_HERE, new EndDialogTask(order_, 4)); + target_->PostTask(FROM_HERE, new QuitTask(order_, 5)); + + // Enforce that every tasks are sent before starting to run the main thread + // message loop. + ASSERT_TRUE(SetEvent(event_)); + + // Poll for the MessageBox. Don't do this at home! At the speed we do it, + // you will never realize one MessageBox was shown. + for (; expect_window_;) { + HWND window = FindWindow(L"#32770", kMessageBoxTitle); + if (window) { + // Dismiss it. + for (;;) { + HWND button = FindWindowEx(window, NULL, L"Button", NULL); + if (button != NULL) { + EXPECT_TRUE(0 == SendMessage(button, WM_LBUTTONDOWN, 0, 0)); + EXPECT_TRUE(0 == SendMessage(button, WM_LBUTTONUP, 0, 0)); + break; + } + } + break; + } + } + } + + private: + MessageLoop* target_; + HANDLE event_; + TaskList* order_; + bool expect_window_; + bool is_reentrant_; +}; + +#endif // defined(OS_WIN) + +void RunTest_RecursiveDenial1(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + EXPECT_TRUE(MessageLoop::current()->NestableTasksAllowed()); + TaskList order; + MessageLoop::current()->PostTask(FROM_HERE, + new RecursiveTask(2, &order, 1, false)); + MessageLoop::current()->PostTask(FROM_HERE, + new RecursiveTask(2, &order, 2, false)); + MessageLoop::current()->PostTask(FROM_HERE, new QuitTask(&order, 3)); + + MessageLoop::current()->Run(); + + // FIFO order. + ASSERT_EQ(14U, order.size()); + EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order[ 2], TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order[ 3], TaskItem(RECURSIVE, 2, false)); + EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 3, true)); + EXPECT_EQ(order[ 5], TaskItem(QUITMESSAGELOOP, 3, false)); + EXPECT_EQ(order[ 6], TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order[ 7], TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order[ 8], TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 2, false)); + EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order[11], TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order[12], TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order[13], TaskItem(RECURSIVE, 2, false)); +} + +void RunTest_RecursiveSupport1(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + TaskList order; + MessageLoop::current()->PostTask(FROM_HERE, + new RecursiveTask(2, &order, 1, true)); + MessageLoop::current()->PostTask(FROM_HERE, + new RecursiveTask(2, &order, 2, true)); + MessageLoop::current()->PostTask(FROM_HERE, + new QuitTask(&order, 3)); + + MessageLoop::current()->Run(); + + // FIFO order. + ASSERT_EQ(14U, order.size()); + EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order[ 2], TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order[ 3], TaskItem(RECURSIVE, 2, false)); + EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 3, true)); + EXPECT_EQ(order[ 5], TaskItem(QUITMESSAGELOOP, 3, false)); + EXPECT_EQ(order[ 6], TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order[ 7], TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order[ 8], TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 2, false)); + EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order[11], TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order[12], TaskItem(RECURSIVE, 2, true)); + EXPECT_EQ(order[13], TaskItem(RECURSIVE, 2, false)); +} + +#if defined(OS_WIN) +// TODO(darin): These tests need to be ported since they test critical +// message loop functionality. + +// A side effect of this test is the generation a beep. Sorry. +void RunTest_RecursiveDenial2(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + Thread worker("RecursiveDenial2_worker"); + Thread::Options options; + options.message_loop_type = message_loop_type; + ASSERT_EQ(true, worker.StartWithOptions(options)); + TaskList order; + ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL)); + worker.message_loop()->PostTask(FROM_HERE, + new Recursive2Tasks(MessageLoop::current(), + event, + true, + &order, + false)); + // Let the other thread execute. + WaitForSingleObject(event, INFINITE); + MessageLoop::current()->Run(); + + ASSERT_EQ(order.size(), 17); + EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order[ 2], TaskItem(MESSAGEBOX, 2, true)); + EXPECT_EQ(order[ 3], TaskItem(MESSAGEBOX, 2, false)); + EXPECT_EQ(order[ 4], TaskItem(RECURSIVE, 3, true)); + EXPECT_EQ(order[ 5], TaskItem(RECURSIVE, 3, false)); + // When EndDialogTask is processed, the window is already dismissed, hence no + // "end" entry. + EXPECT_EQ(order[ 6], TaskItem(ENDDIALOG, 4, true)); + EXPECT_EQ(order[ 7], TaskItem(QUITMESSAGELOOP, 5, true)); + EXPECT_EQ(order[ 8], TaskItem(QUITMESSAGELOOP, 5, false)); + EXPECT_EQ(order[ 9], TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order[11], TaskItem(RECURSIVE, 3, true)); + EXPECT_EQ(order[12], TaskItem(RECURSIVE, 3, false)); + EXPECT_EQ(order[13], TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order[14], TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order[15], TaskItem(RECURSIVE, 3, true)); + EXPECT_EQ(order[16], TaskItem(RECURSIVE, 3, false)); +} + +// A side effect of this test is the generation a beep. Sorry. This test also +// needs to process windows messages on the current thread. +void RunTest_RecursiveSupport2(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + Thread worker("RecursiveSupport2_worker"); + Thread::Options options; + options.message_loop_type = message_loop_type; + ASSERT_EQ(true, worker.StartWithOptions(options)); + TaskList order; + ScopedHandle event(CreateEvent(NULL, FALSE, FALSE, NULL)); + worker.message_loop()->PostTask(FROM_HERE, + new Recursive2Tasks(MessageLoop::current(), + event, + false, + &order, + true)); + // Let the other thread execute. + WaitForSingleObject(event, INFINITE); + MessageLoop::current()->Run(); + + ASSERT_EQ(order.size(), 18); + EXPECT_EQ(order[ 0], TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order[ 1], TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order[ 2], TaskItem(MESSAGEBOX, 2, true)); + // Note that this executes in the MessageBox modal loop. + EXPECT_EQ(order[ 3], TaskItem(RECURSIVE, 3, true)); + EXPECT_EQ(order[ 4], TaskItem(RECURSIVE, 3, false)); + EXPECT_EQ(order[ 5], TaskItem(ENDDIALOG, 4, true)); + EXPECT_EQ(order[ 6], TaskItem(ENDDIALOG, 4, false)); + EXPECT_EQ(order[ 7], TaskItem(MESSAGEBOX, 2, false)); + /* The order can subtly change here. The reason is that when RecursiveTask(1) + is called in the main thread, if it is faster than getting to the + PostTask(FROM_HERE, QuitTask) execution, the order of task execution can + change. We don't care anyway that the order isn't correct. + EXPECT_EQ(order[ 8], TaskItem(QUITMESSAGELOOP, 5, true)); + EXPECT_EQ(order[ 9], TaskItem(QUITMESSAGELOOP, 5, false)); + EXPECT_EQ(order[10], TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order[11], TaskItem(RECURSIVE, 1, false)); + */ + EXPECT_EQ(order[12], TaskItem(RECURSIVE, 3, true)); + EXPECT_EQ(order[13], TaskItem(RECURSIVE, 3, false)); + EXPECT_EQ(order[14], TaskItem(RECURSIVE, 1, true)); + EXPECT_EQ(order[15], TaskItem(RECURSIVE, 1, false)); + EXPECT_EQ(order[16], TaskItem(RECURSIVE, 3, true)); + EXPECT_EQ(order[17], TaskItem(RECURSIVE, 3, false)); +} + +#endif // defined(OS_WIN) + +class TaskThatPumps : public OrderedTasks { + public: + TaskThatPumps(TaskList* order, int cookie) + : OrderedTasks(order, PUMPS, cookie) { + } + + virtual void Run() { + RunStart(); + bool old_state = MessageLoop::current()->NestableTasksAllowed(); + MessageLoop::current()->SetNestableTasksAllowed(true); + MessageLoop::current()->RunAllPending(); + MessageLoop::current()->SetNestableTasksAllowed(old_state); + RunEnd(); + } +}; + +// Tests that non nestable tasks run in FIFO if there are no nested loops. +void RunTest_NonNestableWithNoNesting(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + TaskList order; + + Task* task = new OrderedTasks(&order, 1); + MessageLoop::current()->PostNonNestableTask(FROM_HERE, task); + MessageLoop::current()->PostTask(FROM_HERE, new OrderedTasks(&order, 2)); + MessageLoop::current()->PostTask(FROM_HERE, new QuitTask(&order, 3)); + MessageLoop::current()->Run(); + + // FIFO order. + ASSERT_EQ(6U, order.size()); + EXPECT_EQ(order[ 0], TaskItem(ORDERERD, 1, true)); + EXPECT_EQ(order[ 1], TaskItem(ORDERERD, 1, false)); + EXPECT_EQ(order[ 2], TaskItem(ORDERERD, 2, true)); + EXPECT_EQ(order[ 3], TaskItem(ORDERERD, 2, false)); + EXPECT_EQ(order[ 4], TaskItem(QUITMESSAGELOOP, 3, true)); + EXPECT_EQ(order[ 5], TaskItem(QUITMESSAGELOOP, 3, false)); +} + +// Tests that non nestable tasks don't run when there's code in the call stack. +void RunTest_NonNestableInNestedLoop(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + TaskList order; + + MessageLoop::current()->PostTask(FROM_HERE, + new TaskThatPumps(&order, 1)); + Task* task = new OrderedTasks(&order, 2); + MessageLoop::current()->PostNonNestableTask(FROM_HERE, task); + MessageLoop::current()->PostTask(FROM_HERE, new OrderedTasks(&order, 3)); + MessageLoop::current()->PostTask(FROM_HERE, new OrderedTasks(&order, 4)); + Task* non_nestable_quit = new QuitTask(&order, 5); + MessageLoop::current()->PostNonNestableTask(FROM_HERE, non_nestable_quit); + + MessageLoop::current()->Run(); + + // FIFO order. + ASSERT_EQ(10U, order.size()); + EXPECT_EQ(order[ 0], TaskItem(PUMPS, 1, true)); + EXPECT_EQ(order[ 1], TaskItem(ORDERERD, 3, true)); + EXPECT_EQ(order[ 2], TaskItem(ORDERERD, 3, false)); + EXPECT_EQ(order[ 3], TaskItem(ORDERERD, 4, true)); + EXPECT_EQ(order[ 4], TaskItem(ORDERERD, 4, false)); + EXPECT_EQ(order[ 5], TaskItem(PUMPS, 1, false)); + EXPECT_EQ(order[ 6], TaskItem(ORDERERD, 2, true)); + EXPECT_EQ(order[ 7], TaskItem(ORDERERD, 2, false)); + EXPECT_EQ(order[ 8], TaskItem(QUITMESSAGELOOP, 5, true)); + EXPECT_EQ(order[ 9], TaskItem(QUITMESSAGELOOP, 5, false)); +} + +#if defined(OS_WIN) + +class DispatcherImpl : public MessageLoopForUI::Dispatcher { + public: + DispatcherImpl() : dispatch_count_(0) {} + + virtual bool Dispatch(const MSG& msg) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + return (++dispatch_count_ != 2); + } + + int dispatch_count_; +}; + +void RunTest_Dispatcher(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + class MyTask : public Task { + public: + virtual void Run() { + PostMessage(NULL, WM_LBUTTONDOWN, 0, 0); + PostMessage(NULL, WM_LBUTTONUP, 'A', 0); + } + }; + Task* task = new MyTask(); + MessageLoop::current()->PostDelayedTask(FROM_HERE, task, 100); + DispatcherImpl dispatcher; + MessageLoopForUI::current()->Run(&dispatcher); + ASSERT_EQ(2, dispatcher.dispatch_count_); +} + +class TestIOHandler : public MessageLoopForIO::IOHandler { + public: + TestIOHandler(const wchar_t* name, HANDLE signal, bool wait); + + virtual void OnIOCompleted(MessageLoopForIO::IOContext* context, + DWORD bytes_transfered, DWORD error); + + void Init(); + void WaitForIO(); + OVERLAPPED* context() { return &context_.overlapped; } + DWORD size() { return sizeof(buffer_); } + + private: + char buffer_[48]; + MessageLoopForIO::IOContext context_; + HANDLE signal_; + ScopedHandle file_; + bool wait_; +}; + +TestIOHandler::TestIOHandler(const wchar_t* name, HANDLE signal, bool wait) + : signal_(signal), wait_(wait) { + memset(buffer_, 0, sizeof(buffer_)); + memset(&context_, 0, sizeof(context_)); + context_.handler = this; + + file_.Set(CreateFile(name, GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_FLAG_OVERLAPPED, NULL)); + EXPECT_TRUE(file_.IsValid()); +} + +void TestIOHandler::Init() { + MessageLoopForIO::current()->RegisterIOHandler(file_, this); + + DWORD read; + EXPECT_FALSE(ReadFile(file_, buffer_, size(), &read, context())); + EXPECT_EQ(ERROR_IO_PENDING, GetLastError()); + if (wait_) + WaitForIO(); +} + +void TestIOHandler::OnIOCompleted(MessageLoopForIO::IOContext* context, + DWORD bytes_transfered, DWORD error) { + ASSERT_TRUE(context == &context_); + ASSERT_TRUE(SetEvent(signal_)); +} + +void TestIOHandler::WaitForIO() { + EXPECT_TRUE(MessageLoopForIO::current()->WaitForIOCompletion(300, this)); + EXPECT_TRUE(MessageLoopForIO::current()->WaitForIOCompletion(400, this)); +} + +class IOHandlerTask : public Task { + public: + explicit IOHandlerTask(TestIOHandler* handler) : handler_(handler) {} + virtual void Run() { + handler_->Init(); + } + + private: + TestIOHandler* handler_; +}; + +void RunTest_IOHandler() { + ScopedHandle callback_called(CreateEvent(NULL, TRUE, FALSE, NULL)); + ASSERT_TRUE(callback_called.IsValid()); + + const wchar_t* kPipeName = L"\\\\.\\pipe\\iohandler_pipe"; + ScopedHandle server(CreateNamedPipe(kPipeName, PIPE_ACCESS_OUTBOUND, 0, 1, + 0, 0, 0, NULL)); + ASSERT_TRUE(server.IsValid()); + + Thread thread("IOHandler test"); + Thread::Options options; + options.message_loop_type = MessageLoop::TYPE_IO; + ASSERT_TRUE(thread.StartWithOptions(options)); + + MessageLoop* thread_loop = thread.message_loop(); + ASSERT_TRUE(NULL != thread_loop); + + TestIOHandler handler(kPipeName, callback_called, false); + IOHandlerTask* task = new IOHandlerTask(&handler); + thread_loop->PostTask(FROM_HERE, task); + Sleep(100); // Make sure the thread runs and sleeps for lack of work. + + const char buffer[] = "Hello there!"; + DWORD written; + EXPECT_TRUE(WriteFile(server, buffer, sizeof(buffer), &written, NULL)); + + DWORD result = WaitForSingleObject(callback_called, 1000); + EXPECT_EQ(WAIT_OBJECT_0, result); + + thread.Stop(); +} + +void RunTest_WaitForIO() { + ScopedHandle callback1_called(CreateEvent(NULL, TRUE, FALSE, NULL)); + ScopedHandle callback2_called(CreateEvent(NULL, TRUE, FALSE, NULL)); + ASSERT_TRUE(callback1_called.IsValid()); + ASSERT_TRUE(callback2_called.IsValid()); + + const wchar_t* kPipeName1 = L"\\\\.\\pipe\\iohandler_pipe1"; + const wchar_t* kPipeName2 = L"\\\\.\\pipe\\iohandler_pipe2"; + ScopedHandle server1(CreateNamedPipe(kPipeName1, PIPE_ACCESS_OUTBOUND, 0, 1, + 0, 0, 0, NULL)); + ScopedHandle server2(CreateNamedPipe(kPipeName2, PIPE_ACCESS_OUTBOUND, 0, 1, + 0, 0, 0, NULL)); + ASSERT_TRUE(server1.IsValid()); + ASSERT_TRUE(server2.IsValid()); + + Thread thread("IOHandler test"); + Thread::Options options; + options.message_loop_type = MessageLoop::TYPE_IO; + ASSERT_TRUE(thread.StartWithOptions(options)); + + MessageLoop* thread_loop = thread.message_loop(); + ASSERT_TRUE(NULL != thread_loop); + + TestIOHandler handler1(kPipeName1, callback1_called, false); + TestIOHandler handler2(kPipeName2, callback2_called, true); + IOHandlerTask* task1 = new IOHandlerTask(&handler1); + IOHandlerTask* task2 = new IOHandlerTask(&handler2); + thread_loop->PostTask(FROM_HERE, task1); + Sleep(100); // Make sure the thread runs and sleeps for lack of work. + thread_loop->PostTask(FROM_HERE, task2); + Sleep(100); + + // At this time handler1 is waiting to be called, and the thread is waiting + // on the Init method of handler2, filtering only handler2 callbacks. + + const char buffer[] = "Hello there!"; + DWORD written; + EXPECT_TRUE(WriteFile(server1, buffer, sizeof(buffer), &written, NULL)); + Sleep(200); + EXPECT_EQ(WAIT_TIMEOUT, WaitForSingleObject(callback1_called, 0)) << + "handler1 has not been called"; + + EXPECT_TRUE(WriteFile(server2, buffer, sizeof(buffer), &written, NULL)); + + HANDLE objects[2] = { callback1_called.Get(), callback2_called.Get() }; + DWORD result = WaitForMultipleObjects(2, objects, TRUE, 1000); + EXPECT_EQ(WAIT_OBJECT_0, result); + + thread.Stop(); +} + +#endif // defined(OS_WIN) + +} // namespace + +//----------------------------------------------------------------------------- +// Each test is run against each type of MessageLoop. That way we are sure +// that message loops work properly in all configurations. Of course, in some +// cases, a unit test may only be for a particular type of loop. + +TEST(MessageLoopTest, PostTask) { + RunTest_PostTask(MessageLoop::TYPE_DEFAULT); + RunTest_PostTask(MessageLoop::TYPE_UI); + RunTest_PostTask(MessageLoop::TYPE_IO); +} + +TEST(MessageLoopTest, PostTask_SEH) { + RunTest_PostTask_SEH(MessageLoop::TYPE_DEFAULT); + RunTest_PostTask_SEH(MessageLoop::TYPE_UI); + RunTest_PostTask_SEH(MessageLoop::TYPE_IO); +} + +TEST(MessageLoopTest, PostDelayedTask_Basic) { + RunTest_PostDelayedTask_Basic(MessageLoop::TYPE_DEFAULT); + RunTest_PostDelayedTask_Basic(MessageLoop::TYPE_UI); + RunTest_PostDelayedTask_Basic(MessageLoop::TYPE_IO); +} + +TEST(MessageLoopTest, PostDelayedTask_InDelayOrder) { + RunTest_PostDelayedTask_InDelayOrder(MessageLoop::TYPE_DEFAULT); + RunTest_PostDelayedTask_InDelayOrder(MessageLoop::TYPE_UI); + RunTest_PostDelayedTask_InDelayOrder(MessageLoop::TYPE_IO); +} + +TEST(MessageLoopTest, PostDelayedTask_InPostOrder) { + RunTest_PostDelayedTask_InPostOrder(MessageLoop::TYPE_DEFAULT); + RunTest_PostDelayedTask_InPostOrder(MessageLoop::TYPE_UI); + RunTest_PostDelayedTask_InPostOrder(MessageLoop::TYPE_IO); +} + +TEST(MessageLoopTest, PostDelayedTask_InPostOrder_2) { + RunTest_PostDelayedTask_InPostOrder_2(MessageLoop::TYPE_DEFAULT); + RunTest_PostDelayedTask_InPostOrder_2(MessageLoop::TYPE_UI); + RunTest_PostDelayedTask_InPostOrder_2(MessageLoop::TYPE_IO); +} + +TEST(MessageLoopTest, PostDelayedTask_InPostOrder_3) { + RunTest_PostDelayedTask_InPostOrder_3(MessageLoop::TYPE_DEFAULT); + RunTest_PostDelayedTask_InPostOrder_3(MessageLoop::TYPE_UI); + RunTest_PostDelayedTask_InPostOrder_3(MessageLoop::TYPE_IO); +} + +TEST(MessageLoopTest, PostDelayedTask_SharedTimer) { + RunTest_PostDelayedTask_SharedTimer(MessageLoop::TYPE_DEFAULT); + RunTest_PostDelayedTask_SharedTimer(MessageLoop::TYPE_UI); + RunTest_PostDelayedTask_SharedTimer(MessageLoop::TYPE_IO); +} + +#if defined(OS_WIN) +TEST(MessageLoopTest, PostDelayedTask_SharedTimer_SubPump) { + RunTest_PostDelayedTask_SharedTimer_SubPump(); +} +#endif + +// TODO(darin): re-enable these tests once MessageLoop supports them again. +#if 0 +TEST(MessageLoopTest, EnsureTaskDeletion) { + RunTest_EnsureTaskDeletion(MessageLoop::TYPE_DEFAULT); + RunTest_EnsureTaskDeletion(MessageLoop::TYPE_UI); + RunTest_EnsureTaskDeletion(MessageLoop::TYPE_IO); +} + +TEST(MessageLoopTest, EnsureTaskDeletion_Chain) { + RunTest_EnsureTaskDeletion_Chain(MessageLoop::TYPE_DEFAULT); + RunTest_EnsureTaskDeletion_Chain(MessageLoop::TYPE_UI); + RunTest_EnsureTaskDeletion_Chain(MessageLoop::TYPE_IO); +} +#endif + +#if defined(OS_WIN) +TEST(MessageLoopTest, Crasher) { + RunTest_Crasher(MessageLoop::TYPE_DEFAULT); + RunTest_Crasher(MessageLoop::TYPE_UI); + RunTest_Crasher(MessageLoop::TYPE_IO); +} + +TEST(MessageLoopTest, CrasherNasty) { + RunTest_CrasherNasty(MessageLoop::TYPE_DEFAULT); + RunTest_CrasherNasty(MessageLoop::TYPE_UI); + RunTest_CrasherNasty(MessageLoop::TYPE_IO); +} +#endif // defined(OS_WIN) + +TEST(MessageLoopTest, Nesting) { + RunTest_Nesting(MessageLoop::TYPE_DEFAULT); + RunTest_Nesting(MessageLoop::TYPE_UI); + RunTest_Nesting(MessageLoop::TYPE_IO); +} + +TEST(MessageLoopTest, RecursiveDenial1) { + RunTest_RecursiveDenial1(MessageLoop::TYPE_DEFAULT); + RunTest_RecursiveDenial1(MessageLoop::TYPE_UI); + RunTest_RecursiveDenial1(MessageLoop::TYPE_IO); +} + +TEST(MessageLoopTest, RecursiveSupport1) { + RunTest_RecursiveSupport1(MessageLoop::TYPE_DEFAULT); + RunTest_RecursiveSupport1(MessageLoop::TYPE_UI); + RunTest_RecursiveSupport1(MessageLoop::TYPE_IO); +} + +#if defined(OS_WIN) +TEST(MessageLoopTest, RecursiveDenial2) { + RunTest_RecursiveDenial2(MessageLoop::TYPE_DEFAULT); + RunTest_RecursiveDenial2(MessageLoop::TYPE_UI); + RunTest_RecursiveDenial2(MessageLoop::TYPE_IO); +} + +TEST(MessageLoopTest, RecursiveSupport2) { + // This test requires a UI loop + RunTest_RecursiveSupport2(MessageLoop::TYPE_UI); +} +#endif // defined(OS_WIN) + +TEST(MessageLoopTest, NonNestableWithNoNesting) { + RunTest_NonNestableWithNoNesting(MessageLoop::TYPE_DEFAULT); + RunTest_NonNestableWithNoNesting(MessageLoop::TYPE_UI); + RunTest_NonNestableWithNoNesting(MessageLoop::TYPE_IO); +} + +TEST(MessageLoopTest, NonNestableInNestedLoop) { + RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_DEFAULT); + RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_UI); + RunTest_NonNestableInNestedLoop(MessageLoop::TYPE_IO); +} + +#if defined(OS_WIN) +TEST(MessageLoopTest, Dispatcher) { + // This test requires a UI loop + RunTest_Dispatcher(MessageLoop::TYPE_UI); +} + +TEST(MessageLoopTest, IOHandler) { + RunTest_IOHandler(); +} + +TEST(MessageLoopTest, WaitForIO) { + RunTest_WaitForIO(); +} +#endif // defined(OS_WIN) + +#if defined(OS_POSIX) + +namespace { + +class QuitDelegate : public + base::MessagePumpLibevent::Watcher { + public: + virtual void OnFileCanWriteWithoutBlocking(int fd) { + MessageLoop::current()->Quit(); + } + virtual void OnFileCanReadWithoutBlocking(int fd) { + MessageLoop::current()->Quit(); + } +}; + +} // namespace + +TEST(MessageLoopTest, DISABLED_FileDescriptorWatcherOutlivesMessageLoop) { + // Simulate a MessageLoop that dies before an FileDescriptorWatcher. + // This could happen when people use the Singleton pattern or atexit. + // This is disabled for now because it fails (valgrind shows + // invalid reads), and it's not clear any code relies on this... + // TODO(dkegel): enable if it turns out we rely on this + + // Create a file descriptor. Doesn't need to be readable or writable, + // as we don't need to actually get any notifications. + // pipe() is just the easiest way to do it. + int pipefds[2]; + int err = pipe(pipefds); + ASSERT_TRUE(err == 0); + int fd = pipefds[1]; + { + // Arrange for controller to live longer than message loop. + base::MessagePumpLibevent::FileDescriptorWatcher controller; + { + MessageLoopForIO message_loop; + + QuitDelegate delegate; + message_loop.WatchFileDescriptor(fd, + true, MessageLoopForIO::WATCH_WRITE, &controller, &delegate); + // and don't run the message loop, just destroy it. + } + } + close(pipefds[0]); + close(pipefds[1]); +} + +TEST(MessageLoopTest, FileDescriptorWatcherDoubleStop) { + // Verify that it's ok to call StopWatchingFileDescriptor(). + // (Errors only showed up in valgrind.) + int pipefds[2]; + int err = pipe(pipefds); + ASSERT_TRUE(err == 0); + int fd = pipefds[1]; + { + // Arrange for message loop to live longer than controller. + MessageLoopForIO message_loop; + { + base::MessagePumpLibevent::FileDescriptorWatcher controller; + + QuitDelegate delegate; + message_loop.WatchFileDescriptor(fd, + true, MessageLoopForIO::WATCH_WRITE, &controller, &delegate); + controller.StopWatchingFileDescriptor(); + } + } + close(pipefds[0]); + close(pipefds[1]); +} + +#endif // defined(OS_LINUX) diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_default.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_default.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_default.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_default.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,77 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_pump_default.h" + +#include "base/logging.h" +#include "base/scoped_nsautorelease_pool.h" + +namespace base { + +MessagePumpDefault::MessagePumpDefault() + : keep_running_(true), + event_(false, false) { +} + +void MessagePumpDefault::Run(Delegate* delegate) { + DCHECK(keep_running_) << "Quit must have been called outside of Run!"; + + for (;;) { + ScopedNSAutoreleasePool autorelease_pool; + + bool did_work = delegate->DoWork(); + if (!keep_running_) + break; + + did_work |= delegate->DoDelayedWork(&delayed_work_time_); + if (!keep_running_) + break; + + if (did_work) + continue; + + did_work = delegate->DoIdleWork(); + if (!keep_running_) + break; + + if (did_work) + continue; + + if (delayed_work_time_.is_null()) { + event_.Wait(); + } else { + TimeDelta delay = delayed_work_time_ - Time::Now(); + if (delay > TimeDelta()) { + event_.TimedWait(delay); + } else { + // It looks like delayed_work_time_ indicates a time in the past, so we + // need to call DoDelayedWork now. + delayed_work_time_ = Time(); + } + } + // Since event_ is auto-reset, we don't need to do anything special here + // other than service each delegate method. + } + + keep_running_ = true; +} + +void MessagePumpDefault::Quit() { + keep_running_ = false; +} + +void MessagePumpDefault::ScheduleWork() { + // Since this can be called on any thread, we need to ensure that our Run + // loop wakes up. + event_.Signal(); +} + +void MessagePumpDefault::ScheduleDelayedWork(const Time& delayed_work_time) { + // We know that we can't be blocked on Wait right now since this method can + // only be called on the same thread as Run, so we only need to update our + // record of how long to sleep when we do sleep. + delayed_work_time_ = delayed_work_time; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_default.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_default.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_default.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_default.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,47 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_PUMP_DEFAULT_H_ +#define BASE_MESSAGE_PUMP_DEFAULT_H_ + +#include "base/message_pump.h" +#include "base/time.h" +#include "base/waitable_event.h" + +namespace base { + +class MessagePumpDefault : public MessagePump { + public: + MessagePumpDefault(); + ~MessagePumpDefault() {} + + // MessagePump methods: + virtual void Run(Delegate* delegate); + virtual void Quit(); + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const Time& delayed_work_time); + +#ifdef CHROMIUM_MOZILLA_BUILD + protected: +#else + private: +#endif + // This flag is set to false when Run should return. + bool keep_running_; + + // Used to sleep until there is more work to do. + WaitableEvent event_; + + // The time at which we should call DoDelayedWork. + Time delayed_work_time_; + +#ifdef CHROMIUM_MOZILLA_BUILD + private: +#endif + DISALLOW_COPY_AND_ASSIGN(MessagePumpDefault); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_DEFAULT_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_glib.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_glib.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_glib.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_glib.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,334 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_pump_glib.h" + +#include +#include + +#include +#include + +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/platform_thread.h" + +namespace { + +// We send a byte across a pipe to wakeup the event loop. +const char kWorkScheduled = '\0'; + +// Return a timeout suitable for the glib loop, -1 to block forever, +// 0 to return right away, or a timeout in milliseconds from now. +int GetTimeIntervalMilliseconds(base::Time from) { + if (from.is_null()) + return -1; + + // Be careful here. TimeDelta has a precision of microseconds, but we want a + // value in milliseconds. If there are 5.5ms left, should the delay be 5 or + // 6? It should be 6 to avoid executing delayed work too early. + int delay = static_cast( + ceil((from - base::Time::Now()).InMillisecondsF())); + + // If this value is negative, then we need to run delayed work soon. + return delay < 0 ? 0 : delay; +} + +// A brief refresher on GLib: +// GLib sources have four callbacks: Prepare, Check, Dispatch and Finalize. +// On each iteration of the GLib pump, it calls each source's Prepare function. +// This function should return TRUE if it wants GLib to call its Dispatch, and +// FALSE otherwise. It can also set a timeout in this case for the next time +// Prepare should be called again (it may be called sooner). +// After the Prepare calls, GLib does a poll to check for events from the +// system. File descriptors can be attached to the sources. The poll may block +// if none of the Prepare calls returned TRUE. It will block indefinitely, or +// by the minimum time returned by a source in Prepare. +// After the poll, GLib calls Check for each source that returned FALSE +// from Prepare. The return value of Check has the same meaning as for Prepare, +// making Check a second chance to tell GLib we are ready for Dispatch. +// Finally, GLib calls Dispatch for each source that is ready. If Dispatch +// returns FALSE, GLib will destroy the source. Dispatch calls may be recursive +// (i.e., you can call Run from them), but Prepare and Check cannot. +// Finalize is called when the source is destroyed. +// NOTE: It is common for subsytems to want to process pending events while +// doing intensive work, for example the flash plugin. They usually use the +// following pattern (recommended by the GTK docs): +// while (gtk_events_pending()) { +// gtk_main_iteration(); +// } +// +// gtk_events_pending just calls g_main_context_pending, which does the +// following: +// - Call prepare on all the sources. +// - Do the poll with a timeout of 0 (not blocking). +// - Call check on all the sources. +// - *Does not* call dispatch on the sources. +// - Return true if any of prepare() or check() returned true. +// +// gtk_main_iteration just calls g_main_context_iteration, which does the whole +// thing, respecting the timeout for the poll (and block, although it is +// expected not to if gtk_events_pending returned true), and call dispatch. +// +// Thus it is important to only return true from prepare or check if we +// actually have events or work to do. We also need to make sure we keep +// internal state consistent so that if prepare/check return true when called +// from gtk_events_pending, they will still return true when called right +// after, from gtk_main_iteration. +// +// For the GLib pump we try to follow the Windows UI pump model: +// - Whenever we receive a wakeup event or the timer for delayed work expires, +// we run DoWork and/or DoDelayedWork. That part will also run in the other +// event pumps. +// - We also run DoWork, DoDelayedWork, and possibly DoIdleWork in the main +// loop, around event handling. + +struct WorkSource : public GSource { + base::MessagePumpForUI* pump; +}; + +gboolean WorkSourcePrepare(GSource* source, + gint* timeout_ms) { + *timeout_ms = static_cast(source)->pump->HandlePrepare(); + // We always return FALSE, so that our timeout is honored. If we were + // to return TRUE, the timeout would be considered to be 0 and the poll + // would never block. Once the poll is finished, Check will be called. + return FALSE; +} + +gboolean WorkSourceCheck(GSource* source) { + // Only return TRUE if Dispatch should be called. + return static_cast(source)->pump->HandleCheck(); +} + +gboolean WorkSourceDispatch(GSource* source, + GSourceFunc unused_func, + gpointer unused_data) { + + static_cast(source)->pump->HandleDispatch(); + // Always return TRUE so our source stays registered. + return TRUE; +} + +// I wish these could be const, but g_source_new wants non-const. +GSourceFuncs WorkSourceFuncs = { + WorkSourcePrepare, + WorkSourceCheck, + WorkSourceDispatch, + NULL +}; + +} // namespace + + +namespace base { + +MessagePumpForUI::MessagePumpForUI() + : state_(NULL), + context_(g_main_context_default()), + wakeup_gpollfd_(new GPollFD) { + // Create our wakeup pipe, which is used to flag when work was scheduled. + int fds[2]; + CHECK(pipe(fds) == 0); + wakeup_pipe_read_ = fds[0]; + wakeup_pipe_write_ = fds[1]; + wakeup_gpollfd_->fd = wakeup_pipe_read_; + wakeup_gpollfd_->events = G_IO_IN; + + work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource)); + static_cast(work_source_)->pump = this; + g_source_add_poll(work_source_, wakeup_gpollfd_.get()); + // Use a low priority so that we let other events in the queue go first. + g_source_set_priority(work_source_, G_PRIORITY_DEFAULT_IDLE); + // This is needed to allow Run calls inside Dispatch. + g_source_set_can_recurse(work_source_, TRUE); + g_source_attach(work_source_, context_); + gdk_event_handler_set(&EventDispatcher, this, NULL); +} + +MessagePumpForUI::~MessagePumpForUI() { + gdk_event_handler_set(reinterpret_cast(gtk_main_do_event), + this, NULL); + g_source_destroy(work_source_); + g_source_unref(work_source_); + close(wakeup_pipe_read_); + close(wakeup_pipe_write_); +} + +void MessagePumpForUI::RunWithDispatcher(Delegate* delegate, + Dispatcher* dispatcher) { +#ifndef NDEBUG + // Make sure we only run this on one thread. GTK only has one message pump + // so we can only have one UI loop per process. + static PlatformThreadId thread_id = PlatformThread::CurrentId(); + DCHECK(thread_id == PlatformThread::CurrentId()) << + "Running MessagePumpForUI on two different threads; " + "this is unsupported by GLib!"; +#endif + + RunState state; + state.delegate = delegate; + state.dispatcher = dispatcher; + state.should_quit = false; + state.run_depth = state_ ? state_->run_depth + 1 : 1; + state.has_work = false; + + RunState* previous_state = state_; + state_ = &state; + + // We really only do a single task for each iteration of the loop. If we + // have done something, assume there is likely something more to do. This + // will mean that we don't block on the message pump until there was nothing + // more to do. We also set this to true to make sure not to block on the + // first iteration of the loop, so RunAllPending() works correctly. + bool more_work_is_plausible = true; + + // We run our own loop instead of using g_main_loop_quit in one of the + // callbacks. This is so we only quit our own loops, and we don't quit + // nested loops run by others. TODO(deanm): Is this what we want? + for (;;) { + // Don't block if we think we have more work to do. + bool block = !more_work_is_plausible; + + // g_main_context_iteration returns true if events have been dispatched. + more_work_is_plausible = g_main_context_iteration(context_, block); + if (state_->should_quit) + break; + + more_work_is_plausible |= state_->delegate->DoWork(); + if (state_->should_quit) + break; + + more_work_is_plausible |= + state_->delegate->DoDelayedWork(&delayed_work_time_); + if (state_->should_quit) + break; + + if (more_work_is_plausible) + continue; + + more_work_is_plausible = state_->delegate->DoIdleWork(); + if (state_->should_quit) + break; + } + + state_ = previous_state; +} + +// Return the timeout we want passed to poll. +int MessagePumpForUI::HandlePrepare() { + // We know we have work, but we haven't called HandleDispatch yet. Don't let + // the pump block so that we can do some processing. + if (state_ && // state_ may be null during tests. + state_->has_work) + return 0; + + // We don't think we have work to do, but make sure not to block + // longer than the next time we need to run delayed work. + return GetTimeIntervalMilliseconds(delayed_work_time_); +} + +bool MessagePumpForUI::HandleCheck() { + if (!state_) // state_ may be null during tests. + return false; + + // We should only ever have a single message on the wakeup pipe, since we + // are only signaled when the queue went from empty to non-empty. The glib + // poll will tell us whether there was data, so this read shouldn't block. + if (wakeup_gpollfd_->revents & G_IO_IN) { + char msg; + if (HANDLE_EINTR(read(wakeup_pipe_read_, &msg, 1)) != 1 || msg != '!') { + NOTREACHED() << "Error reading from the wakeup pipe."; + } + // Since we ate the message, we need to record that we have more work, + // because HandleCheck() may be called without HandleDispatch being called + // afterwards. + state_->has_work = true; + } + + if (state_->has_work) + return true; + + if (GetTimeIntervalMilliseconds(delayed_work_time_) == 0) { + // The timer has expired. That condition will stay true until we process + // that delayed work, so we don't need to record this differently. + return true; + } + + return false; +} + +void MessagePumpForUI::HandleDispatch() { + state_->has_work = false; + if (state_->delegate->DoWork()) { + // NOTE: on Windows at this point we would call ScheduleWork (see + // MessagePumpForUI::HandleWorkMessage in message_pump_win.cc). But here, + // instead of posting a message on the wakeup pipe, we can avoid the + // syscalls and just signal that we have more work. + state_->has_work = true; + } + + if (state_->should_quit) + return; + + state_->delegate->DoDelayedWork(&delayed_work_time_); +} + +void MessagePumpForUI::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void MessagePumpForUI::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +void MessagePumpForUI::WillProcessEvent(GdkEvent* event) { + FOR_EACH_OBSERVER(Observer, observers_, WillProcessEvent(event)); +} + +void MessagePumpForUI::DidProcessEvent(GdkEvent* event) { + FOR_EACH_OBSERVER(Observer, observers_, DidProcessEvent(event)); +} + +void MessagePumpForUI::Quit() { + if (state_) { + state_->should_quit = true; + } else { + NOTREACHED() << "Quit called outside Run!"; + } +} + +void MessagePumpForUI::ScheduleWork() { + // This can be called on any thread, so we don't want to touch any state + // variables as we would then need locks all over. This ensures that if + // we are sleeping in a poll that we will wake up. + char msg = '!'; + if (HANDLE_EINTR(write(wakeup_pipe_write_, &msg, 1)) != 1) { + NOTREACHED() << "Could not write to the UI message loop wakeup pipe!"; + } +} + +void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) { + // We need to wake up the loop in case the poll timeout needs to be + // adjusted. This will cause us to try to do work, but that's ok. + delayed_work_time_ = delayed_work_time; + ScheduleWork(); +} + +// static +void MessagePumpForUI::EventDispatcher(GdkEvent* event, gpointer data) { + MessagePumpForUI* message_pump = reinterpret_cast(data); + + message_pump->WillProcessEvent(event); + if (message_pump->state_ && // state_ may be null during tests. + message_pump->state_->dispatcher) { + if (!message_pump->state_->dispatcher->Dispatch(event)) + message_pump->state_->should_quit = true; + } else { + gtk_main_do_event(event); + } + message_pump->DidProcessEvent(event); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_glib.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_glib.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_glib.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_glib.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,142 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_PUMP_GLIB_H_ +#define BASE_MESSAGE_PUMP_GLIB_H_ + +#include "base/message_pump.h" +#include "base/observer_list.h" +#include "base/scoped_ptr.h" +#include "base/time.h" + +typedef union _GdkEvent GdkEvent; +typedef struct _GMainContext GMainContext; +typedef struct _GPollFD GPollFD; +typedef struct _GSource GSource; + +namespace base { + +// This class implements a MessagePump needed for TYPE_UI MessageLoops on +// OS_LINUX platforms using GLib. +class MessagePumpForUI : public MessagePump { + public: + // Observer is notified prior to a GdkEvent event being dispatched. As + // Observers are notified of every change, they have to be FAST! + class Observer { + public: + virtual ~Observer() {} + + // This method is called before processing a message. + virtual void WillProcessEvent(GdkEvent* event) = 0; + + // This method is called after processing a message. + virtual void DidProcessEvent(GdkEvent* event) = 0; + }; + + // Dispatcher is used during a nested invocation of Run to dispatch events. + // If Run is invoked with a non-NULL Dispatcher, MessageLoop does not + // dispatch events (or invoke gtk_main_do_event), rather every event is + // passed to Dispatcher's Dispatch method for dispatch. It is up to the + // Dispatcher to dispatch, or not, the event. + // + // The nested loop is exited by either posting a quit, or returning false + // from Dispatch. + class Dispatcher { + public: + virtual ~Dispatcher() {} + // Dispatches the event. If true is returned processing continues as + // normal. If false is returned, the nested loop exits immediately. + virtual bool Dispatch(GdkEvent* event) = 0; + }; + + MessagePumpForUI(); + virtual ~MessagePumpForUI(); + + // Like MessagePump::Run, but GdkEvent objects are routed through dispatcher. + virtual void RunWithDispatcher(Delegate* delegate, Dispatcher* dispatcher); + + virtual void Run(Delegate* delegate) { RunWithDispatcher(delegate, NULL); } + virtual void Quit(); + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const Time& delayed_work_time); + + // Internal methods used for processing the pump callbacks. They are + // public for simplicity but should not be used directly. HandlePrepare + // is called during the prepare step of glib, and returns a timeout that + // will be passed to the poll. HandleCheck is called after the poll + // has completed, and returns whether or not HandleDispatch should be called. + // HandleDispatch is called if HandleCheck returned true. + int HandlePrepare(); + bool HandleCheck(); + void HandleDispatch(); + + // Adds an Observer, which will start receiving notifications immediately. + void AddObserver(Observer* observer); + + // Removes an Observer. It is safe to call this method while an Observer is + // receiving a notification callback. + void RemoveObserver(Observer* observer); + + private: + // We may make recursive calls to Run, so we save state that needs to be + // separate between them in this structure type. + struct RunState { + Delegate* delegate; + Dispatcher* dispatcher; + + // Used to flag that the current Run() invocation should return ASAP. + bool should_quit; + + // Used to count how many Run() invocations are on the stack. + int run_depth; + + // This keeps the state of whether the pump got signaled that there was new + // work to be done. Since we eat the message on the wake up pipe as soon as + // we get it, we keep that state here to stay consistent. + bool has_work; + }; + + // Invoked from EventDispatcher. Notifies all observers we're about to + // process an event. + void WillProcessEvent(GdkEvent* event); + + // Invoked from EventDispatcher. Notifies all observers we processed an + // event. + void DidProcessEvent(GdkEvent* event); + + // Callback prior to gdk dispatching an event. + static void EventDispatcher(GdkEvent* event, void* data); + + RunState* state_; + + // This is a GLib structure that we can add event sources to. We use the + // default GLib context, which is the one to which all GTK events are + // dispatched. + GMainContext* context_; + + // This is the time when we need to do delayed work. + Time delayed_work_time_; + + // The work source. It is shared by all calls to Run and destroyed when + // the message pump is destroyed. + GSource* work_source_; + + // We use a wakeup pipe to make sure we'll get out of the glib polling phase + // when another thread has scheduled us to do some work. There is a glib + // mechanism g_main_context_wakeup, but this won't guarantee that our event's + // Dispatch() will be called. + int wakeup_pipe_read_; + int wakeup_pipe_write_; + // Use a scoped_ptr to avoid needing the definition of GPollFD in the header. + scoped_ptr wakeup_gpollfd_; + + // List of observers. + ObserverList observers_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_GLIB_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,122 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_PUMP_H_ +#define BASE_MESSAGE_PUMP_H_ + +#include "base/ref_counted.h" + +namespace base { + +class Time; + +class MessagePump : public RefCountedThreadSafe { + public: + // Please see the comments above the Run method for an illustration of how + // these delegate methods are used. + class Delegate { + public: + virtual ~Delegate() {} + + // Called from within Run in response to ScheduleWork or when the message + // pump would otherwise call DoDelayedWork. Returns true to indicate that + // work was done. DoDelayedWork will not be called if DoWork returns true. + virtual bool DoWork() = 0; + + // Called from within Run in response to ScheduleDelayedWork or when the + // message pump would otherwise sleep waiting for more work. Returns true + // to indicate that delayed work was done. DoIdleWork will not be called + // if DoDelayedWork returns true. Upon return |next_delayed_work_time| + // indicates the time when DoDelayedWork should be called again. If + // |next_delayed_work_time| is null (per Time::is_null), then the queue of + // future delayed work (timer events) is currently empty, and no additional + // calls to this function need to be scheduled. + virtual bool DoDelayedWork(Time* next_delayed_work_time) = 0; + + // Called from within Run just before the message pump goes to sleep. + // Returns true to indicate that idle work was done. + virtual bool DoIdleWork() = 0; + }; + + virtual ~MessagePump() {} + + // The Run method is called to enter the message pump's run loop. + // + // Within the method, the message pump is responsible for processing native + // messages as well as for giving cycles to the delegate periodically. The + // message pump should take care to mix delegate callbacks with native + // message processing so neither type of event starves the other of cycles. + // + // The anatomy of a typical run loop: + // + // for (;;) { + // bool did_work = DoInternalWork(); + // if (should_quit_) + // break; + // + // did_work |= delegate_->DoWork(); + // if (should_quit_) + // break; + // + // did_work |= delegate_->DoDelayedWork(); + // if (should_quit_) + // break; + // + // if (did_work) + // continue; + // + // did_work = delegate_->DoIdleWork(); + // if (should_quit_) + // break; + // + // if (did_work) + // continue; + // + // WaitForWork(); + // } + // + // Here, DoInternalWork is some private method of the message pump that is + // responsible for dispatching the next UI message or notifying the next IO + // completion (for example). WaitForWork is a private method that simply + // blocks until there is more work of any type to do. + // + // Notice that the run loop cycles between calling DoInternalWork, DoWork, + // and DoDelayedWork methods. This helps ensure that neither work queue + // starves the other. This is important for message pumps that are used to + // drive animations, for example. + // + // Notice also that after each callout to foreign code, the run loop checks + // to see if it should quit. The Quit method is responsible for setting this + // flag. No further work is done once the quit flag is set. + // + // NOTE: Care must be taken to handle Run being called again from within any + // of the callouts to foreign code. Native message pumps may also need to + // deal with other native message pumps being run outside their control + // (e.g., the MessageBox API on Windows pumps UI messages!). To be specific, + // the callouts (DoWork and DoDelayedWork) MUST still be provided even in + // nested sub-loops that are "seemingly" outside the control of this message + // pump. DoWork in particular must never be starved for time slices unless + // it returns false (meaning it has run out of things to do). + // + virtual void Run(Delegate* delegate) = 0; + + // Quit immediately from the most recently entered run loop. This method may + // only be used on the thread that called Run. + virtual void Quit() = 0; + + // Schedule a DoWork callback to happen reasonably soon. Does nothing if a + // DoWork callback is already scheduled. This method may be called from any + // thread. Once this call is made, DoWork should not be "starved" at least + // until it returns a value of false. + virtual void ScheduleWork() = 0; + + // Schedule a DoDelayedWork callback to happen at the specified time, + // cancelling any pending DoDelayedWork callback. This method may only be + // used on the thread that called Run. + virtual void ScheduleDelayedWork(const Time& delayed_work_time) = 0; +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_libevent.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_libevent.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_libevent.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_libevent.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,373 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_pump_libevent.h" + +#include +#include + +#include "eintr_wrapper.h" +#include "base/logging.h" +#include "base/scoped_nsautorelease_pool.h" +#include "base/scoped_ptr.h" +#include "base/time.h" +#include "third_party/libevent/event.h" + +// Lifecycle of struct event +// Libevent uses two main data structures: +// struct event_base (of which there is one per message pump), and +// struct event (of which there is roughly one per socket). +// The socket's struct event is created in +// MessagePumpLibevent::WatchFileDescriptor(), +// is owned by the FileDescriptorWatcher, and is destroyed in +// StopWatchingFileDescriptor(). +// It is moved into and out of lists in struct event_base by +// the libevent functions event_add() and event_del(). +// +// TODO(dkegel): +// At the moment bad things happen if a FileDescriptorWatcher +// is active after its MessagePumpLibevent has been destroyed. +// See MessageLoopTest.FileDescriptorWatcherOutlivesMessageLoop +// Not clear yet whether that situation occurs in practice, +// but if it does, we need to fix it. + +namespace base { + +// Return 0 on success +// Too small a function to bother putting in a library? +static int SetNonBlocking(int fd) { + int flags = fcntl(fd, F_GETFL, 0); + if (flags == -1) + flags = 0; + return fcntl(fd, F_SETFL, flags | O_NONBLOCK); +} + +MessagePumpLibevent::FileDescriptorWatcher::FileDescriptorWatcher() + : is_persistent_(false), + event_(NULL) { +} + +MessagePumpLibevent::FileDescriptorWatcher::~FileDescriptorWatcher() { + if (event_) { + StopWatchingFileDescriptor(); + } +} + +void MessagePumpLibevent::FileDescriptorWatcher::Init(event *e, + bool is_persistent) { + DCHECK(e); + DCHECK(event_ == NULL); + + is_persistent_ = is_persistent; + event_ = e; +} + +event *MessagePumpLibevent::FileDescriptorWatcher::ReleaseEvent() { + struct event *e = event_; + event_ = NULL; + return e; +} + +bool MessagePumpLibevent::FileDescriptorWatcher::StopWatchingFileDescriptor() { + event* e = ReleaseEvent(); + if (e == NULL) + return true; + + // event_del() is a no-op if the event isn't active. + int rv = event_del(e); + delete e; + return (rv == 0); +} + +// Called if a byte is received on the wakeup pipe. +void MessagePumpLibevent::OnWakeup(int socket, short flags, void* context) { + base::MessagePumpLibevent* that = + static_cast(context); + DCHECK(that->wakeup_pipe_out_ == socket); + + // Remove and discard the wakeup byte. + char buf; + int nread = HANDLE_EINTR(read(socket, &buf, 1)); + DCHECK_EQ(nread, 1); + // Tell libevent to break out of inner loop. + event_base_loopbreak(that->event_base_); +} + +MessagePumpLibevent::MessagePumpLibevent() + : keep_running_(true), + in_run_(false), + event_base_(event_base_new()), + wakeup_pipe_in_(-1), + wakeup_pipe_out_(-1) { + if (!Init()) + NOTREACHED(); +} + +bool MessagePumpLibevent::Init() { + int fds[2]; + if (pipe(fds)) { + DLOG(ERROR) << "pipe() failed, errno: " << errno; + return false; + } + if (SetNonBlocking(fds[0])) { + DLOG(ERROR) << "SetNonBlocking for pipe fd[0] failed, errno: " << errno; + return false; + } + if (SetNonBlocking(fds[1])) { + DLOG(ERROR) << "SetNonBlocking for pipe fd[1] failed, errno: " << errno; + return false; + } + wakeup_pipe_out_ = fds[0]; + wakeup_pipe_in_ = fds[1]; + + wakeup_event_ = new event; + event_set(wakeup_event_, wakeup_pipe_out_, EV_READ | EV_PERSIST, + OnWakeup, this); + event_base_set(event_base_, wakeup_event_); + + if (event_add(wakeup_event_, 0)) + return false; + return true; +} + +MessagePumpLibevent::~MessagePumpLibevent() { + DCHECK(wakeup_event_); + DCHECK(event_base_); + event_del(wakeup_event_); + delete wakeup_event_; + if (wakeup_pipe_in_ >= 0) + close(wakeup_pipe_in_); + if (wakeup_pipe_out_ >= 0) + close(wakeup_pipe_out_); + event_base_free(event_base_); +} + +bool MessagePumpLibevent::WatchFileDescriptor(int fd, + bool persistent, + Mode mode, + FileDescriptorWatcher *controller, + Watcher *delegate) { + DCHECK(fd > 0); + DCHECK(controller); + DCHECK(delegate); + DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE); + + int event_mask = persistent ? EV_PERSIST : 0; + if ((mode & WATCH_READ) != 0) { + event_mask |= EV_READ; + } + if ((mode & WATCH_WRITE) != 0) { + event_mask |= EV_WRITE; + } + + // |should_delete_event| is true if we're modifying an event that's currently + // active in |controller|. + // If we're modifying an existing event and there's an error then we need to + // tell libevent to clean it up via event_delete() before returning. + bool should_delete_event = true; + scoped_ptr evt(controller->ReleaseEvent()); + if (evt.get() == NULL) { + should_delete_event = false; + // Ownership is transferred to the controller. + evt.reset(new event); + } + + // Set current interest mask and message pump for this event. + event_set(evt.get(), fd, event_mask, OnLibeventNotification, + delegate); + + // Tell libevent which message pump this socket will belong to when we add it. + if (event_base_set(event_base_, evt.get()) != 0) { + if (should_delete_event) { + event_del(evt.get()); + } + return false; + } + + // Add this socket to the list of monitored sockets. + if (event_add(evt.get(), NULL) != 0) { + if (should_delete_event) { + event_del(evt.get()); + } + return false; + } + + // Transfer ownership of evt to controller. + controller->Init(evt.release(), persistent); + return true; +} + + +void MessagePumpLibevent::OnLibeventNotification(int fd, short flags, + void* context) { + Watcher* watcher = static_cast(context); + + if (flags & EV_WRITE) { + watcher->OnFileCanWriteWithoutBlocking(fd); + } + if (flags & EV_READ) { + watcher->OnFileCanReadWithoutBlocking(fd); + } +} + + +#if defined(CHROMIUM_MOZILLA_BUILD) +MessagePumpLibevent::SignalEvent::SignalEvent() : + event_(NULL) +{ +} + +MessagePumpLibevent::SignalEvent::~SignalEvent() +{ + if (event_) { + StopCatching(); + } +} + +void +MessagePumpLibevent::SignalEvent::Init(event *e) +{ + DCHECK(e); + DCHECK(event_ == NULL); + event_ = e; +} + +bool +MessagePumpLibevent::SignalEvent::StopCatching() +{ + // XXX/cjones: this code could be shared with + // FileDescriptorWatcher. ironic that libevent is "more" + // object-oriented than this C++ + event* e = ReleaseEvent(); + if (e == NULL) + return true; + + // event_del() is a no-op if the event isn't active. + int rv = event_del(e); + delete e; + return (rv == 0); +} + +event * +MessagePumpLibevent::SignalEvent::ReleaseEvent() +{ + event *e = event_; + event_ = NULL; + return e; +} + +bool +MessagePumpLibevent::CatchSignal(int sig, + SignalEvent* sigevent, + SignalWatcher* delegate) +{ + DCHECK(sig > 0); + DCHECK(sigevent); + DCHECK(delegate); + // TODO if we want to support re-using SignalEvents, this code needs + // to jump through the same hoops as WatchFileDescriptor(). Not + // needed at present + DCHECK(NULL == sigevent->event_); + + scoped_ptr evt(new event); + signal_set(evt.get(), sig, OnLibeventSignalNotification, delegate); + + if (event_base_set(event_base_, evt.get())) + return false; + + if (signal_add(evt.get(), NULL)) + return false; + + // Transfer ownership of evt to controller. + sigevent->Init(evt.release()); + return true; +} + +void +MessagePumpLibevent::OnLibeventSignalNotification(int sig, short flags, + void* context) +{ + DCHECK(sig > 0); + DCHECK(EV_SIGNAL == flags); + DCHECK(context); + reinterpret_cast(context)->OnSignal(sig); +} +#endif // defined(CHROMIUM_MOZILLA_BUILD) + + +// Reentrant! +void MessagePumpLibevent::Run(Delegate* delegate) { + DCHECK(keep_running_) << "Quit must have been called outside of Run!"; + + bool old_in_run = in_run_; + in_run_ = true; + + for (;;) { + ScopedNSAutoreleasePool autorelease_pool; + + bool did_work = delegate->DoWork(); + if (!keep_running_) + break; + + did_work |= delegate->DoDelayedWork(&delayed_work_time_); + if (!keep_running_) + break; + + if (did_work) + continue; + + did_work = delegate->DoIdleWork(); + if (!keep_running_) + break; + + if (did_work) + continue; + + // EVLOOP_ONCE tells libevent to only block once, + // but to service all pending events when it wakes up. + if (delayed_work_time_.is_null()) { + event_base_loop(event_base_, EVLOOP_ONCE); + } else { + TimeDelta delay = delayed_work_time_ - Time::Now(); + if (delay > TimeDelta()) { + struct timeval poll_tv; + poll_tv.tv_sec = delay.InSeconds(); + poll_tv.tv_usec = delay.InMicroseconds() % Time::kMicrosecondsPerSecond; + event_base_loopexit(event_base_, &poll_tv); + event_base_loop(event_base_, EVLOOP_ONCE); + } else { + // It looks like delayed_work_time_ indicates a time in the past, so we + // need to call DoDelayedWork now. + delayed_work_time_ = Time(); + } + } + } + + keep_running_ = true; + in_run_ = old_in_run; +} + +void MessagePumpLibevent::Quit() { + DCHECK(in_run_); + // Tell both libevent and Run that they should break out of their loops. + keep_running_ = false; + ScheduleWork(); +} + +void MessagePumpLibevent::ScheduleWork() { + // Tell libevent (in a threadsafe way) that it should break out of its loop. + char buf = 0; + int nwrite = HANDLE_EINTR(write(wakeup_pipe_in_, &buf, 1)); + DCHECK(nwrite == 1 || errno == EAGAIN) + << "[nwrite:" << nwrite << "] [errno:" << errno << "]"; +} + +void MessagePumpLibevent::ScheduleDelayedWork(const Time& delayed_work_time) { + // We know that we can't be blocked on Wait right now since this method can + // only be called on the same thread as Run, so we only need to update our + // record of how long to sleep when we do sleep. + delayed_work_time_ = delayed_work_time; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_libevent.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_libevent.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_libevent.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_libevent.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,184 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_PUMP_LIBEVENT_H_ +#define BASE_MESSAGE_PUMP_LIBEVENT_H_ + +#include "base/message_pump.h" +#include "base/time.h" + +// Declare structs we need from libevent.h rather than including it +struct event_base; +struct event; + +namespace base { + +// Class to monitor sockets and issue callbacks when sockets are ready for I/O +// TODO(dkegel): add support for background file IO somehow +class MessagePumpLibevent : public MessagePump { + public: + + // Object returned by WatchFileDescriptor to manage further watching. + class FileDescriptorWatcher { + public: + FileDescriptorWatcher(); + ~FileDescriptorWatcher(); // Implicitly calls StopWatchingFileDescriptor. + + // NOTE: These methods aren't called StartWatching()/StopWatching() to + // avoid confusion with the win32 ObjectWatcher class. + + // Stop watching the FD, always safe to call. No-op if there's nothing + // to do. + bool StopWatchingFileDescriptor(); + + private: + // Called by MessagePumpLibevent, ownership of |e| is transferred to this + // object. + void Init(event* e, bool is_persistent); + + // Used by MessagePumpLibevent to take ownership of event_. + event *ReleaseEvent(); + friend class MessagePumpLibevent; + + private: + bool is_persistent_; // false if this event is one-shot. + event* event_; + DISALLOW_COPY_AND_ASSIGN(FileDescriptorWatcher); + }; + + // Used with WatchFileDescptor to asynchronously monitor the I/O readiness of + // a File Descriptor. + class Watcher { + public: + virtual ~Watcher() {} + // Called from MessageLoop::Run when an FD can be read from/written to + // without blocking + virtual void OnFileCanReadWithoutBlocking(int fd) = 0; + virtual void OnFileCanWriteWithoutBlocking(int fd) = 0; + }; + + MessagePumpLibevent(); + virtual ~MessagePumpLibevent(); + + enum Mode { + WATCH_READ = 1 << 0, + WATCH_WRITE = 1 << 1, + WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE + }; + + // Have the current thread's message loop watch for a a situation in which + // reading/writing to the FD can be performed without Blocking. + // Callers must provide a preallocated FileDescriptorWatcher object which + // can later be used to manage the Lifetime of this event. + // If a FileDescriptorWatcher is passed in which is already attached to + // an event, then the effect is cumulative i.e. after the call |controller| + // will watch both the previous event and the new one. + // If an error occurs while calling this method in a cumulative fashion, the + // event previously attached to |controller| is aborted. + // Returns true on success. + // TODO(dkegel): switch to edge-triggered readiness notification + bool WatchFileDescriptor(int fd, + bool persistent, + Mode mode, + FileDescriptorWatcher *controller, + Watcher *delegate); + + +#if defined(CHROMIUM_MOZILLA_BUILD) + // This is analagous to FileDescriptorWatcher above, which really is + // just a wrapper around libevent's |struct event|. This class acts + // as a sort of "scoped event watcher" in that it guarantees that + // when this class is out of scope, the signal-event it wraps is + // removed from libevent's guts. + // + // XXX/cjones: this isn't my favorite API, but preserving it in + // order to match code above + class SignalEvent { + friend class MessagePumpLibevent; + + public: + SignalEvent(); + ~SignalEvent(); // implicitly calls StopCatching() + + // Have libevent forget this event. + bool StopCatching(); + + private: + void Init(event* e); + event* ReleaseEvent(); + + event* event_; + + DISALLOW_COPY_AND_ASSIGN(SignalEvent); + }; + + class SignalWatcher { + public: + virtual ~SignalWatcher() {} + // Called from MessageLoop::Run when |sig| has been delivered to + // this process + virtual void OnSignal(int sig) = 0; + }; + + // Have the current thread's message loop catch the signal |sig|. + // Multiple watchers can catch the same signal; they're all notified + // upon its delivery. Callers must provide a preallocated + // SignalEvent object which can be used to manage the lifetime of + // this event. Returns true on success. + bool CatchSignal(int sig, + SignalEvent* sigevent, + SignalWatcher* delegate); +#endif // defined(CHROMIUM_MOZILLA_BUILD) + + + // MessagePump methods: + virtual void Run(Delegate* delegate); + virtual void Quit(); + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const Time& delayed_work_time); + + private: + + // Risky part of constructor. Returns true on success. + bool Init(); + + // This flag is set to false when Run should return. + bool keep_running_; + + // This flag is set when inside Run. + bool in_run_; + + // The time at which we should call DoDelayedWork. + Time delayed_work_time_; + + // Libevent dispatcher. Watches all sockets registered with it, and sends + // readiness callbacks when a socket is ready for I/O. + event_base* event_base_; + + // Called by libevent to tell us a registered FD can be read/written to. + static void OnLibeventNotification(int fd, short flags, + void* context); + +#if defined(CHROMIUM_MOZILLA_BUILD) + // Called by libevent upon receiving a signal + static void OnLibeventSignalNotification(int sig, short flags, + void* context); +#endif + + // Unix pipe used to implement ScheduleWork() + // ... callback; called by libevent inside Run() when pipe is ready to read + static void OnWakeup(int socket, short flags, void* context); + // ... write end; ScheduleWork() writes a single byte to it + int wakeup_pipe_in_; + // ... read end; OnWakeup reads it and then breaks Run() out of its sleep + int wakeup_pipe_out_; + // ... libevent wrapper for read end + event* wakeup_event_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpLibevent); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_LIBEVENT_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_mac.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_mac.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_mac.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_mac.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,181 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// The basis for all native run loops on the Mac is the CFRunLoop. It can be +// used directly, it can be used as the driving force behind the similar +// Foundation NSRunLoop, and it can be used to implement higher-level event +// loops such as the NSApplication event loop. +// +// This file introduces a basic CFRunLoop-based implementation of the +// MessagePump interface called CFRunLoopBase. CFRunLoopBase contains all +// of the machinery necessary to dispatch events to a delegate, but does not +// implement the specific run loop. Concrete subclasses must provide their +// own DoRun and Quit implementations. +// +// A concrete subclass that just runs a CFRunLoop loop is provided in +// MessagePumpCFRunLoop. For an NSRunLoop, the similar MessagePumpNSRunLoop +// is provided. +// +// For the application's event loop, an implementation based on AppKit's +// NSApplication event system is provided in MessagePumpNSApplication. +// +// Typically, MessagePumpNSApplication only makes sense on a Cocoa +// application's main thread. If a CFRunLoop-based message pump is needed on +// any other thread, one of the other concrete subclasses is preferrable. +// MessagePumpMac::Create is defined, which returns a new NSApplication-based +// or NSRunLoop-based MessagePump subclass depending on which thread it is +// called on. + +#ifndef BASE_MESSAGE_PUMP_MAC_H_ +#define BASE_MESSAGE_PUMP_MAC_H_ + +#include "base/message_pump.h" + +#include + +namespace base { + +class Time; + +class MessagePumpCFRunLoopBase : public MessagePump { + public: + MessagePumpCFRunLoopBase(); + virtual ~MessagePumpCFRunLoopBase(); + + // Subclasses should implement the work they need to do in MessagePump::Run + // in the DoRun method. MessagePumpCFRunLoopBase::Run calls DoRun directly. + // This arrangement is used because MessagePumpCFRunLoopBase needs to set + // up and tear down things before and after the "meat" of DoRun. + virtual void Run(Delegate* delegate); + virtual void DoRun(Delegate* delegate) = 0; + + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const Time& delayed_work_time); + + protected: + // The thread's run loop. + CFRunLoopRef run_loop_; + + private: + // Timer callback scheduled by ScheduleDelayedWork. This does not do any + // work, but it signals delayed_work_source_ so that delayed work can be + // performed within the appropriate priority constraints. + static void RunDelayedWorkTimer(CFRunLoopTimerRef timer, void* info); + + // Perform highest-priority work. This is associated with work_source_ + // signalled by ScheduleWork. + static void RunWork(void* info); + + // Perform delayed-priority work. This is associated with + // delayed_work_source_ signalled by RunDelayedWorkTimer, and is responsible + // for calling ScheduleDelayedWork again if appropriate. + static void RunDelayedWork(void* info); + + // Observer callback responsible for performing idle-priority work, before + // the run loop goes to sleep. Associated with idle_work_observer_. + static void RunIdleWork(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, void* info); + + // The timer, sources, and observer are described above alongside their + // callbacks. + CFRunLoopTimerRef delayed_work_timer_; + CFRunLoopSourceRef work_source_; + CFRunLoopSourceRef delayed_work_source_; + CFRunLoopObserverRef idle_work_observer_; + + // (weak) Delegate passed as an argument to the innermost Run call. + Delegate* delegate_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoopBase); +}; + +class MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase { + public: + MessagePumpCFRunLoop(); + virtual ~MessagePumpCFRunLoop(); + + virtual void DoRun(Delegate* delegate); + virtual void Quit(); + + private: + // Observer callback called when the run loop starts and stops, at the + // beginning and end of calls to CFRunLoopRun. This is used to maintain + // nesting_level_ and to handle deferred loop quits. Associated with + // enter_exit_observer_. + static void EnterExitRunLoop(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, void* info); + + // Observer for EnterExitRunLoop. + CFRunLoopObserverRef enter_exit_observer_; + + // The recursion depth of the currently-executing CFRunLoopRun loop on the + // run loop's thread. 0 if no run loops are running inside of whatever scope + // the object was created in. + int nesting_level_; + + // The recursion depth (calculated in the same way as nesting_level_) of the + // innermost executing CFRunLoopRun loop started by a call to Run. + int innermost_quittable_; + + // True if Quit is called to stop the innermost MessagePump + // (innermost_quittable_) but some other CFRunLoopRun loop (nesting_level_) + // is running inside the MessagePump's innermost Run call. + bool quit_pending_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoop); +}; + +class MessagePumpNSRunLoop : public MessagePumpCFRunLoopBase { + public: + MessagePumpNSRunLoop(); + virtual ~MessagePumpNSRunLoop(); + + virtual void DoRun(Delegate* delegate); + virtual void Quit(); + + private: + // A source that doesn't do anything but provide something signalable + // attached to the run loop. This source will be signalled when Quit + // is called, to cause the loop to wake up so that it can stop. + CFRunLoopSourceRef quit_source_; + + // False after Quit is called. + bool keep_running_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpNSRunLoop); +}; + +class MessagePumpNSApplication : public MessagePumpCFRunLoopBase { + public: + MessagePumpNSApplication(); + + virtual void DoRun(Delegate* delegate); + virtual void Quit(); + + private: + // False after Quit is called. + bool keep_running_; + + // True if DoRun is managing its own run loop as opposed to letting + // -[NSApplication run] handle it. The outermost run loop in the application + // is managed by -[NSApplication run], inner run loops are handled by a loop + // in DoRun. + bool running_own_loop_; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpNSApplication); +}; + +class MessagePumpMac { + public: + // Returns a new instance of MessagePumpNSApplication if called on the main + // thread. Otherwise, returns a new instance of MessagePumpNSRunLoop. + static MessagePump* Create(); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(MessagePumpMac); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_MAC_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_mac.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_mac.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_mac.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_mac.mm 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,386 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_pump_mac.h" + +#import +#import +#include + +#include "base/scoped_nsautorelease_pool.h" +#include "base/time.h" + +namespace { + +void NoOp(void* info) { +} + +} // namespace + +namespace base { + +// Must be called on the run loop thread. +MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase() + : delegate_(NULL) { + run_loop_ = CFRunLoopGetCurrent(); + CFRetain(run_loop_); + + // Set a repeating timer with a preposterous firing time and interval. The + // timer will effectively never fire as-is. The firing time will be adjusted + // as needed when ScheduleDelayedWork is called. + CFRunLoopTimerContext timer_context = CFRunLoopTimerContext(); + timer_context.info = this; + delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator + DBL_MAX, // fire time + DBL_MAX, // interval + 0, // flags (ignored) + 0, // priority (ignored) + RunDelayedWorkTimer, + &timer_context); + CFRunLoopAddTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); + + CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); + source_context.info = this; + source_context.perform = RunWork; + work_source_ = CFRunLoopSourceCreate(NULL, // allocator + 0, // priority + &source_context); + CFRunLoopAddSource(run_loop_, work_source_, kCFRunLoopCommonModes); + + source_context.perform = RunDelayedWork; + delayed_work_source_ = CFRunLoopSourceCreate(NULL, // allocator + 1, // priority + &source_context); + CFRunLoopAddSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes); + + CFRunLoopObserverContext observer_context = CFRunLoopObserverContext(); + observer_context.info = this; + idle_work_observer_ = CFRunLoopObserverCreate(NULL, // allocator + kCFRunLoopBeforeWaiting, + true, // repeat + 0, // priority + RunIdleWork, + &observer_context); + CFRunLoopAddObserver(run_loop_, idle_work_observer_, kCFRunLoopCommonModes); +} + +// Ideally called on the run loop thread. +MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() { + CFRunLoopRemoveObserver(run_loop_, idle_work_observer_, + kCFRunLoopCommonModes); + CFRelease(idle_work_observer_); + + CFRunLoopRemoveSource(run_loop_, delayed_work_source_, kCFRunLoopCommonModes); + CFRelease(delayed_work_source_); + + CFRunLoopRemoveSource(run_loop_, work_source_, kCFRunLoopCommonModes); + CFRelease(work_source_); + + CFRunLoopRemoveTimer(run_loop_, delayed_work_timer_, kCFRunLoopCommonModes); + CFRelease(delayed_work_timer_); + + CFRelease(run_loop_); +} + +// Must be called on the run loop thread. +void MessagePumpCFRunLoopBase::Run(Delegate* delegate) { + Delegate* last_delegate = delegate_; + delegate_ = delegate; + + DoRun(delegate); + + delegate_ = last_delegate; +} + +// May be called on any thread. +void MessagePumpCFRunLoopBase::ScheduleWork() { + CFRunLoopSourceSignal(work_source_); + CFRunLoopWakeUp(run_loop_); +} + +// Must be called on the run loop thread. +void MessagePumpCFRunLoopBase::ScheduleDelayedWork( + const Time& delayed_work_time) { + Time::Exploded exploded; + delayed_work_time.UTCExplode(&exploded); + double seconds = exploded.second + + (static_cast((delayed_work_time.ToInternalValue()) % + Time::kMicrosecondsPerSecond) / + Time::kMicrosecondsPerSecond); + CFGregorianDate gregorian = { + exploded.year, + exploded.month, + exploded.day_of_month, + exploded.hour, + exploded.minute, + seconds + }; + CFAbsoluteTime fire_time = CFGregorianDateGetAbsoluteTime(gregorian, NULL); + + CFRunLoopTimerSetNextFireDate(delayed_work_timer_, fire_time); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer, + void* info) { + MessagePumpCFRunLoop* self = static_cast(info); + + // CFRunLoopTimers fire outside of the priority scheme for CFRunLoopSources. + // In order to establish the proper priority where delegate_->DoDelayedWork + // can only be called if delegate_->DoWork returns false, the timer used + // to schedule delayed work must signal a CFRunLoopSource set at a lower + // priority than the one used for delegate_->DoWork. + CFRunLoopSourceSignal(self->delayed_work_source_); +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunWork(void* info) { + MessagePumpCFRunLoop* self = static_cast(info); + + // If we're on the main event loop, the NSApp runloop won't clean up the + // autoreleasepool until there is UI event, so use a local one for any + // autoreleased objects to ensure they go away sooner. + ScopedNSAutoreleasePool autorelease_pool; + + // Call DoWork once, and if something was done, arrange to come back here + // again as long as the loop is still running. + if (self->delegate_->DoWork()) { + CFRunLoopSourceSignal(self->work_source_); + } +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunDelayedWork(void* info) { + MessagePumpCFRunLoop* self = static_cast(info); + + // If we're on the main event loop, the NSApp runloop won't clean up the + // autoreleasepool until there is UI event, so use a local one for any + // autoreleased objects to ensure they go away sooner. + ScopedNSAutoreleasePool autorelease_pool; + + Time next_time; + self->delegate_->DoDelayedWork(&next_time); + if (!next_time.is_null()) { + TimeDelta delay = next_time - Time::Now(); + if (delay > TimeDelta()) { + // There's more delayed work to be done in the future. + self->ScheduleDelayedWork(next_time); + } else { + // There's more delayed work to be done, and its time is in the past. + // Arrange to come back here directly as long as the loop is still + // running. + CFRunLoopSourceSignal(self->delayed_work_source_); + } + } +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoopBase::RunIdleWork(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, + void* info) { + MessagePumpCFRunLoop* self = static_cast(info); + + // If we're on the main event loop, the NSApp runloop won't clean up the + // autoreleasepool until there is UI event, so use a local one for any + // autoreleased objects to ensure they go away sooner. + ScopedNSAutoreleasePool autorelease_pool; + + if (self->delegate_->DoIdleWork()) { + // If idle work was done, don't let the loop go to sleep. More idle work + // might be waiting. + CFRunLoopWakeUp(self->run_loop_); + } +} + +// Must be called on the run loop thread. +MessagePumpCFRunLoop::MessagePumpCFRunLoop() + : nesting_level_(0), + innermost_quittable_(0), + quit_pending_(false) { + CFRunLoopObserverContext observer_context = CFRunLoopObserverContext(); + observer_context.info = this; + enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator + kCFRunLoopEntry | + kCFRunLoopExit, + true, // repeat + 0, // priority + EnterExitRunLoop, + &observer_context); + CFRunLoopAddObserver(run_loop_, enter_exit_observer_, kCFRunLoopCommonModes); +} + +// Ideally called on the run loop thread. If other CFRunLoopRun loops were +// running lower on the run loop thread's stack when this object was created, +// the same number of CFRunLoopRun loops must be running when this object is +// destroyed. +MessagePumpCFRunLoop::~MessagePumpCFRunLoop() { + CFRunLoopRemoveObserver(run_loop_, enter_exit_observer_, + kCFRunLoopCommonModes); + CFRelease(enter_exit_observer_); +} + +// Called by CFRunLoopBase::DoRun. If other CFRunLoopRun loops were running +// lower on the run loop thread's stack when this object was created, the same +// number of CFRunLoopRun loops must be running for the outermost call to Run. +// Run/DoRun are reentrant after that point. +void MessagePumpCFRunLoop::DoRun(Delegate* delegate) { + // nesting_level_ will be incremented in EnterExitRunLoop, so set + // innermost_quittable_ accordingly. + int last_innermost_quittable = innermost_quittable_; + innermost_quittable_ = nesting_level_ + 1; + + // This is completely identical to calling CFRunLoopRun(), except autorelease + // pool management is introduced. + int result; + do { + ScopedNSAutoreleasePool autorelease_pool; + result = CFRunLoopRunInMode(kCFRunLoopDefaultMode, DBL_MAX, false); + } while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished); + + // Restore the previous state of the object. + innermost_quittable_ = last_innermost_quittable; +} + +// Must be called on the run loop thread. +void MessagePumpCFRunLoop::Quit() { + // Stop the innermost run loop managed by this MessagePumpCFRunLoop object. + if (nesting_level_ == innermost_quittable_) { + // This object is running the innermost loop, just stop it. + CFRunLoopStop(run_loop_); + } else { + // There's another loop running inside the loop managed by this object. + // In other words, someone else called CFRunLoopRun on the same thread, + // higher on the stack than our highest Run call. Don't preempt other + // run loops, just mark the object to quit our innermost run loop as soon + // as the other inner loops we don't manage are done. + quit_pending_ = true; + } +} + +// Called from the run loop. +// static +void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopObserverRef observer, + CFRunLoopActivity activity, + void* info) { + MessagePumpCFRunLoop* self = static_cast(info); + + switch (activity) { + case kCFRunLoopEntry: + // If the run loop was entered by a call to Run, this will properly + // balance the decrement done in Run before entering the loop. + ++self->nesting_level_; + break; + case kCFRunLoopExit: + if (--self->nesting_level_ == self->innermost_quittable_ && + self->quit_pending_) { + // Quit was called while loops other than those managed by this object + // were running further inside a run loop managed by this object. Now + // that all unmanaged inner run loops are gone, stop the loop running + // just inside Run. + CFRunLoopStop(self->run_loop_); + self->quit_pending_ = false; + } + break; + default: + break; + } +} + +MessagePumpNSRunLoop::MessagePumpNSRunLoop() + : keep_running_(true) { + CFRunLoopSourceContext source_context = CFRunLoopSourceContext(); + source_context.perform = NoOp; + quit_source_ = CFRunLoopSourceCreate(NULL, // allocator + 0, // priority + &source_context); + CFRunLoopAddSource(run_loop_, quit_source_, kCFRunLoopCommonModes); +} + +MessagePumpNSRunLoop::~MessagePumpNSRunLoop() { + CFRunLoopRemoveSource(run_loop_, quit_source_, kCFRunLoopCommonModes); + CFRelease(quit_source_); +} + +void MessagePumpNSRunLoop::DoRun(Delegate* delegate) { + while (keep_running_) { + // NSRunLoop manages autorelease pools itself. + [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode + beforeDate:[NSDate distantFuture]]; + } + + keep_running_ = true; +} + +void MessagePumpNSRunLoop::Quit() { + keep_running_ = false; + CFRunLoopSourceSignal(quit_source_); + CFRunLoopWakeUp(run_loop_); +} + +MessagePumpNSApplication::MessagePumpNSApplication() + : keep_running_(true), + running_own_loop_(false) { +} + +void MessagePumpNSApplication::DoRun(Delegate* delegate) { + bool last_running_own_loop_ = running_own_loop_; + + [NSApplication sharedApplication]; + + if (![NSApp isRunning]) { + running_own_loop_ = false; + // NSApplication manages autorelease pools itself when run this way. + [NSApp run]; + } else { + running_own_loop_ = true; + while (keep_running_) { + ScopedNSAutoreleasePool autorelease_pool; + NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask + untilDate:[NSDate distantFuture] + inMode:NSDefaultRunLoopMode + dequeue:YES]; + if (event) { + [NSApp sendEvent:event]; + } + } + keep_running_ = true; + } + + running_own_loop_ = last_running_own_loop_; +} + +void MessagePumpNSApplication::Quit() { + if (!running_own_loop_) { + [NSApp stop:nil]; + } else { + keep_running_ = false; + } + + // Send a fake event to wake the loop up. + [NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined + location:NSMakePoint(0,0) + modifierFlags:0 + timestamp:0 + windowNumber:0 + context:NULL + subtype:0 + data1:0 + data2:0] + atStart:NO]; +} + +// static +MessagePump* MessagePumpMac::Create() { + if ([NSThread isMainThread]) { + return new MessagePumpNSApplication; + } + + return new MessagePumpNSRunLoop; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_qt.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_qt.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_qt.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_qt.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,142 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_pump_qt.h" + +#include +#include +#include + +#include +#include + +#include "base/eintr_wrapper.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/platform_thread.h" + +namespace { +// Cached QEvent user type, registered for our event system +static int sPokeEvent; +} // namespace + +namespace base { + +MessagePumpForUI::MessagePumpForUI() + : qt_pump(*this) +{ +} + +MessagePumpForUI::~MessagePumpForUI() { +} + +MessagePumpQt::MessagePumpQt(MessagePumpForUI &aPump) + : pump(aPump) +{ + // Register our custom event type, to use in qApp event loop +#if (QT_VERSION >= QT_VERSION_CHECK(4, 4, 0)) + sPokeEvent = QEvent::registerEventType(); +#else + sPokeEvent = QEvent::User+5000; +#endif +} + +MessagePumpQt::~MessagePumpQt() +{ +} + +bool +MessagePumpQt::event(QEvent *e) +{ + if (e->type() == sPokeEvent) { + pump.HandleDispatch(); + return true; + } + return false; +} + +void MessagePumpForUI::Run(Delegate* delegate) { + RunState state; + state.delegate = delegate; + state.should_quit = false; + state.run_depth = state_ ? state_->run_depth + 1 : 1; + // We really only do a single task for each iteration of the loop. If we + // have done something, assume there is likely something more to do. This + // will mean that we don't block on the message pump until there was nothing + // more to do. We also set this to true to make sure not to block on the + // first iteration of the loop, so RunAllPending() works correctly. + state.more_work_is_plausible = true; + + RunState* previous_state = state_; + state_ = &state; + + // We run our own loop instead of using g_main_loop_quit in one of the + // callbacks. This is so we only quit our own loops, and we don't quit + // nested loops run by others. TODO(deanm): Is this what we want? + + while (!state_->should_quit) { + QAbstractEventDispatcher *dispatcher = QAbstractEventDispatcher::instance(qApp->thread()); + if (!dispatcher) + return; + dispatcher->processEvents(QEventLoop::AllEvents | QEventLoop::WaitForMoreEvents); + } + + state_ = previous_state; +} + +void MessagePumpForUI::HandleDispatch() { + // We should only ever have a single message on the wakeup pipe, since we + // are only signaled when the queue went from empty to non-empty. The qApp + // poll will tell us whether there was data, so this read shouldn't block. + if (state_->should_quit) + return; + + state_->more_work_is_plausible = false; + + if (state_->delegate->DoWork()) + state_->more_work_is_plausible = true; + + if (state_->should_quit) + return; + + if (state_->delegate->DoDelayedWork(&delayed_work_time_)) + state_->more_work_is_plausible = true; + if (state_->should_quit) + return; + + // Don't do idle work if we think there are more important things + // that we could be doing. + if (state_->more_work_is_plausible) + return; + + if (state_->delegate->DoIdleWork()) + state_->more_work_is_plausible = true; + if (state_->should_quit) + return; +} + +void MessagePumpForUI::Quit() { + if (state_) { + state_->should_quit = true; + } else { + NOTREACHED() << "Quit called outside Run!"; + } +} + +void MessagePumpForUI::ScheduleWork() { + // This can be called on any thread, so we don't want to touch any state + // variables as we would then need locks all over. This ensures that if + // we are sleeping in a poll that we will wake up. + QCoreApplication::postEvent(&qt_pump, + new QEvent((QEvent::Type) sPokeEvent)); +} + +void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) { + // We need to wake up the loop in case the poll timeout needs to be + // adjusted. This will cause us to try to do work, but that's ok. + delayed_work_time_ = delayed_work_time; + ScheduleWork(); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_qt.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_qt.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_qt.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_qt.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,80 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_PUMP_QT_H_ +#define BASE_MESSAGE_PUMP_QT_H_ + +#include + +#include "base/message_pump.h" +#include "base/time.h" + +namespace base { + +class MessagePumpForUI; + +class MessagePumpQt : public QObject { + Q_OBJECT + + public: + MessagePumpQt(MessagePumpForUI &pump); + ~MessagePumpQt(); + + virtual bool event (QEvent *e); + + private: + base::MessagePumpForUI &pump; +}; + +// This class implements a MessagePump needed for TYPE_UI MessageLoops on +// OS_LINUX platforms using QApplication event loop +class MessagePumpForUI : public MessagePump { + + public: + MessagePumpForUI(); + ~MessagePumpForUI(); + + virtual void Run(Delegate* delegate); + virtual void Quit(); + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const Time& delayed_work_time); + + // Internal methods used for processing the pump callbacks. They are + // public for simplicity but should not be used directly. + // HandleDispatch is called after the poll has completed. + void HandleDispatch(); + + private: + // We may make recursive calls to Run, so we save state that needs to be + // separate between them in this structure type. + struct RunState { + Delegate* delegate; + + // Used to flag that the current Run() invocation should return ASAP. + bool should_quit; + + // Used to count how many Run() invocations are on the stack. + int run_depth; + + // Used internally for controlling whether we want a message pump + // iteration to be blocking or not. + bool more_work_is_plausible; + }; + + RunState* state_; + + // This is the time when we need to do delayed work. + Time delayed_work_time_; + + // MessagePump implementation for Qt based on the GLib implement. + // On Qt we use a QObject base class and the + // default qApp in order to process events through QEventLoop. + MessagePumpQt qt_pump; + + DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI); +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_QT_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_win.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,557 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_pump_win.h" + +#include + +#include "base/histogram.h" +#include "base/win_util.h" + +using base::Time; + +namespace base { + +static const wchar_t kWndClass[] = L"Chrome_MessagePumpWindow"; + +// Message sent to get an additional time slice for pumping (processing) another +// task (a series of such messages creates a continuous task pump). +static const int kMsgHaveWork = WM_USER + 1; + +//----------------------------------------------------------------------------- +// MessagePumpWin public: + +void MessagePumpWin::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void MessagePumpWin::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +void MessagePumpWin::WillProcessMessage(const MSG& msg) { + FOR_EACH_OBSERVER(Observer, observers_, WillProcessMessage(msg)); +} + +void MessagePumpWin::DidProcessMessage(const MSG& msg) { + FOR_EACH_OBSERVER(Observer, observers_, DidProcessMessage(msg)); +} + +void MessagePumpWin::RunWithDispatcher( + Delegate* delegate, Dispatcher* dispatcher) { + RunState s; + s.delegate = delegate; + s.dispatcher = dispatcher; + s.should_quit = false; + s.run_depth = state_ ? state_->run_depth + 1 : 1; + + RunState* previous_state = state_; + state_ = &s; + + DoRunLoop(); + + state_ = previous_state; +} + +void MessagePumpWin::Quit() { + DCHECK(state_); + state_->should_quit = true; +} + +//----------------------------------------------------------------------------- +// MessagePumpWin protected: + +int MessagePumpWin::GetCurrentDelay() const { + if (delayed_work_time_.is_null()) + return -1; + + // Be careful here. TimeDelta has a precision of microseconds, but we want a + // value in milliseconds. If there are 5.5ms left, should the delay be 5 or + // 6? It should be 6 to avoid executing delayed work too early. + double timeout = ceil((delayed_work_time_ - Time::Now()).InMillisecondsF()); + + // If this value is negative, then we need to run delayed work soon. + int delay = static_cast(timeout); + if (delay < 0) + delay = 0; + + return delay; +} + +//----------------------------------------------------------------------------- +// MessagePumpForUI public: + +MessagePumpForUI::MessagePumpForUI() { + InitMessageWnd(); +} + +MessagePumpForUI::~MessagePumpForUI() { + DestroyWindow(message_hwnd_); + UnregisterClass(kWndClass, GetModuleHandle(NULL)); +} + +void MessagePumpForUI::ScheduleWork() { + if (InterlockedExchange(&have_work_, 1)) + return; // Someone else continued the pumping. + + // Make sure the MessagePump does some work for us. + PostMessage(message_hwnd_, kMsgHaveWork, reinterpret_cast(this), 0); +} + +void MessagePumpForUI::ScheduleDelayedWork(const Time& delayed_work_time) { + // + // We would *like* to provide high resolution timers. Windows timers using + // SetTimer() have a 10ms granularity. We have to use WM_TIMER as a wakeup + // mechanism because the application can enter modal windows loops where it + // is not running our MessageLoop; the only way to have our timers fire in + // these cases is to post messages there. + // + // To provide sub-10ms timers, we process timers directly from our run loop. + // For the common case, timers will be processed there as the run loop does + // its normal work. However, we *also* set the system timer so that WM_TIMER + // events fire. This mops up the case of timers not being able to work in + // modal message loops. It is possible for the SetTimer to pop and have no + // pending timers, because they could have already been processed by the + // run loop itself. + // + // We use a single SetTimer corresponding to the timer that will expire + // soonest. As new timers are created and destroyed, we update SetTimer. + // Getting a spurrious SetTimer event firing is benign, as we'll just be + // processing an empty timer queue. + // + delayed_work_time_ = delayed_work_time; + + int delay_msec = GetCurrentDelay(); + DCHECK(delay_msec >= 0); + if (delay_msec < USER_TIMER_MINIMUM) + delay_msec = USER_TIMER_MINIMUM; + + // Create a WM_TIMER event that will wake us up to check for any pending + // timers (in case we are running within a nested, external sub-pump). + SetTimer(message_hwnd_, reinterpret_cast(this), delay_msec, NULL); +} + +void MessagePumpForUI::PumpOutPendingPaintMessages() { + // If we are being called outside of the context of Run, then don't try to do + // any work. + if (!state_) + return; + + // Create a mini-message-pump to force immediate processing of only Windows + // WM_PAINT messages. Don't provide an infinite loop, but do enough peeking + // to get the job done. Actual common max is 4 peeks, but we'll be a little + // safe here. + const int kMaxPeekCount = 20; + bool win2k = win_util::GetWinVersion() <= win_util::WINVERSION_2000; + int peek_count; + for (peek_count = 0; peek_count < kMaxPeekCount; ++peek_count) { + MSG msg; + if (win2k) { + if (!PeekMessage(&msg, NULL, WM_PAINT, WM_PAINT, PM_REMOVE)) + break; + } else { + if (!PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_QS_PAINT)) + break; + } + ProcessMessageHelper(msg); + if (state_->should_quit) // Handle WM_QUIT. + break; + } + // Histogram what was really being used, to help to adjust kMaxPeekCount. + DHISTOGRAM_COUNTS("Loop.PumpOutPendingPaintMessages Peeks", peek_count); +} + +//----------------------------------------------------------------------------- +// MessagePumpForUI private: + +// static +LRESULT CALLBACK MessagePumpForUI::WndProcThunk( + HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { + switch (message) { + case kMsgHaveWork: + reinterpret_cast(wparam)->HandleWorkMessage(); + break; + case WM_TIMER: + reinterpret_cast(wparam)->HandleTimerMessage(); + break; + } + return DefWindowProc(hwnd, message, wparam, lparam); +} + +void MessagePumpForUI::DoRunLoop() { + // IF this was just a simple PeekMessage() loop (servicing all possible work + // queues), then Windows would try to achieve the following order according + // to MSDN documentation about PeekMessage with no filter): + // * Sent messages + // * Posted messages + // * Sent messages (again) + // * WM_PAINT messages + // * WM_TIMER messages + // + // Summary: none of the above classes is starved, and sent messages has twice + // the chance of being processed (i.e., reduced service time). + + for (;;) { + // If we do any work, we may create more messages etc., and more work may + // possibly be waiting in another task group. When we (for example) + // ProcessNextWindowsMessage(), there is a good chance there are still more + // messages waiting. On the other hand, when any of these methods return + // having done no work, then it is pretty unlikely that calling them again + // quickly will find any work to do. Finally, if they all say they had no + // work, then it is a good time to consider sleeping (waiting) for more + // work. + + bool more_work_is_plausible = ProcessNextWindowsMessage(); + if (state_->should_quit) + break; + + more_work_is_plausible |= state_->delegate->DoWork(); + if (state_->should_quit) + break; + + more_work_is_plausible |= + state_->delegate->DoDelayedWork(&delayed_work_time_); + // If we did not process any delayed work, then we can assume that our + // existing WM_TIMER if any will fire when delayed work should run. We + // don't want to disturb that timer if it is already in flight. However, + // if we did do all remaining delayed work, then lets kill the WM_TIMER. + if (more_work_is_plausible && delayed_work_time_.is_null()) + KillTimer(message_hwnd_, reinterpret_cast(this)); + if (state_->should_quit) + break; + + if (more_work_is_plausible) + continue; + + more_work_is_plausible = state_->delegate->DoIdleWork(); + if (state_->should_quit) + break; + + if (more_work_is_plausible) + continue; + + WaitForWork(); // Wait (sleep) until we have work to do again. + } +} + +void MessagePumpForUI::InitMessageWnd() { + HINSTANCE hinst = GetModuleHandle(NULL); + + WNDCLASSEX wc = {0}; + wc.cbSize = sizeof(wc); + wc.lpfnWndProc = WndProcThunk; + wc.hInstance = hinst; + wc.lpszClassName = kWndClass; + RegisterClassEx(&wc); + + message_hwnd_ = + CreateWindow(kWndClass, 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, 0); + DCHECK(message_hwnd_); +} + +void MessagePumpForUI::WaitForWork() { + // Wait until a message is available, up to the time needed by the timer + // manager to fire the next set of timers. + int delay = GetCurrentDelay(); + if (delay < 0) // Negative value means no timers waiting. + delay = INFINITE; + + DWORD result; + result = MsgWaitForMultipleObjectsEx(0, NULL, delay, QS_ALLINPUT, + MWMO_INPUTAVAILABLE); + + if (WAIT_OBJECT_0 == result) { + // A WM_* message is available. + // If a parent child relationship exists between windows across threads + // then their thread inputs are implicitly attached. + // This causes the MsgWaitForMultipleObjectsEx API to return indicating + // that messages are ready for processing (specifically mouse messages + // intended for the child window. Occurs if the child window has capture) + // The subsequent PeekMessages call fails to return any messages thus + // causing us to enter a tight loop at times. + // The WaitMessage call below is a workaround to give the child window + // sometime to process its input messages. + MSG msg = {0}; + DWORD queue_status = GetQueueStatus(QS_MOUSE); + if (HIWORD(queue_status) & QS_MOUSE && + !PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_NOREMOVE)) { + WaitMessage(); + } + return; + } + + DCHECK_NE(WAIT_FAILED, result) << GetLastError(); +} + +void MessagePumpForUI::HandleWorkMessage() { + // If we are being called outside of the context of Run, then don't try to do + // any work. This could correspond to a MessageBox call or something of that + // sort. + if (!state_) { + // Since we handled a kMsgHaveWork message, we must still update this flag. + InterlockedExchange(&have_work_, 0); + return; + } + + // Let whatever would have run had we not been putting messages in the queue + // run now. This is an attempt to make our dummy message not starve other + // messages that may be in the Windows message queue. + ProcessPumpReplacementMessage(); + + // Now give the delegate a chance to do some work. He'll let us know if he + // needs to do more work. + if (state_->delegate->DoWork()) + ScheduleWork(); +} + +void MessagePumpForUI::HandleTimerMessage() { + KillTimer(message_hwnd_, reinterpret_cast(this)); + + // If we are being called outside of the context of Run, then don't do + // anything. This could correspond to a MessageBox call or something of + // that sort. + if (!state_) + return; + + state_->delegate->DoDelayedWork(&delayed_work_time_); + if (!delayed_work_time_.is_null()) { + // A bit gratuitous to set delayed_work_time_ again, but oh well. + ScheduleDelayedWork(delayed_work_time_); + } +} + +bool MessagePumpForUI::ProcessNextWindowsMessage() { + // If there are sent messages in the queue then PeekMessage internally + // dispatches the message and returns false. We return true in this + // case to ensure that the message loop peeks again instead of calling + // MsgWaitForMultipleObjectsEx again. + bool sent_messages_in_queue = false; + DWORD queue_status = GetQueueStatus(QS_SENDMESSAGE); + if (HIWORD(queue_status) & QS_SENDMESSAGE) + sent_messages_in_queue = true; + + MSG msg; + if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + return ProcessMessageHelper(msg); + + return sent_messages_in_queue; +} + +bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) { + if (WM_QUIT == msg.message) { + // Repost the QUIT message so that it will be retrieved by the primary + // GetMessage() loop. + state_->should_quit = true; + PostQuitMessage(static_cast(msg.wParam)); + return false; + } + + // While running our main message pump, we discard kMsgHaveWork messages. + if (msg.message == kMsgHaveWork && msg.hwnd == message_hwnd_) + return ProcessPumpReplacementMessage(); + + WillProcessMessage(msg); + + if (state_->dispatcher) { + if (!state_->dispatcher->Dispatch(msg)) + state_->should_quit = true; + } else { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + DidProcessMessage(msg); + return true; +} + +bool MessagePumpForUI::ProcessPumpReplacementMessage() { + // When we encounter a kMsgHaveWork message, this method is called to peek + // and process a replacement message, such as a WM_PAINT or WM_TIMER. The + // goal is to make the kMsgHaveWork as non-intrusive as possible, even though + // a continuous stream of such messages are posted. This method carefully + // peeks a message while there is no chance for a kMsgHaveWork to be pending, + // then resets the have_work_ flag (allowing a replacement kMsgHaveWork to + // possibly be posted), and finally dispatches that peeked replacement. Note + // that the re-post of kMsgHaveWork may be asynchronous to this thread!! + + MSG msg; + bool have_message = (0 != PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)); + DCHECK(!have_message || kMsgHaveWork != msg.message || + msg.hwnd != message_hwnd_); + + // Since we discarded a kMsgHaveWork message, we must update the flag. + int old_have_work = InterlockedExchange(&have_work_, 0); + DCHECK(old_have_work); + + // We don't need a special time slice if we didn't have_message to process. + if (!have_message) + return false; + + // Guarantee we'll get another time slice in the case where we go into native + // windows code. This ScheduleWork() may hurt performance a tiny bit when + // tasks appear very infrequently, but when the event queue is busy, the + // kMsgHaveWork events get (percentage wise) rarer and rarer. + ScheduleWork(); + return ProcessMessageHelper(msg); +} + +//----------------------------------------------------------------------------- +// MessagePumpForIO public: + +MessagePumpForIO::MessagePumpForIO() { + port_.Set(CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 1)); + DCHECK(port_.IsValid()); +} + +void MessagePumpForIO::ScheduleWork() { + if (InterlockedExchange(&have_work_, 1)) + return; // Someone else continued the pumping. + + // Make sure the MessagePump does some work for us. + BOOL ret = PostQueuedCompletionStatus(port_, 0, + reinterpret_cast(this), + reinterpret_cast(this)); + DCHECK(ret); +} + +void MessagePumpForIO::ScheduleDelayedWork(const Time& delayed_work_time) { + // We know that we can't be blocked right now since this method can only be + // called on the same thread as Run, so we only need to update our record of + // how long to sleep when we do sleep. + delayed_work_time_ = delayed_work_time; +} + +void MessagePumpForIO::RegisterIOHandler(HANDLE file_handle, + IOHandler* handler) { + ULONG_PTR key = reinterpret_cast(handler); + HANDLE port = CreateIoCompletionPort(file_handle, port_, key, 1); + DCHECK(port == port_.Get()); +} + +//----------------------------------------------------------------------------- +// MessagePumpForIO private: + +void MessagePumpForIO::DoRunLoop() { + for (;;) { + // If we do any work, we may create more messages etc., and more work may + // possibly be waiting in another task group. When we (for example) + // WaitForIOCompletion(), there is a good chance there are still more + // messages waiting. On the other hand, when any of these methods return + // having done no work, then it is pretty unlikely that calling them + // again quickly will find any work to do. Finally, if they all say they + // had no work, then it is a good time to consider sleeping (waiting) for + // more work. + + bool more_work_is_plausible = state_->delegate->DoWork(); + if (state_->should_quit) + break; + + more_work_is_plausible |= WaitForIOCompletion(0, NULL); + if (state_->should_quit) + break; + + more_work_is_plausible |= + state_->delegate->DoDelayedWork(&delayed_work_time_); + if (state_->should_quit) + break; + + if (more_work_is_plausible) + continue; + + more_work_is_plausible = state_->delegate->DoIdleWork(); + if (state_->should_quit) + break; + + if (more_work_is_plausible) + continue; + + WaitForWork(); // Wait (sleep) until we have work to do again. + } +} + +// Wait until IO completes, up to the time needed by the timer manager to fire +// the next set of timers. +void MessagePumpForIO::WaitForWork() { + // We do not support nested IO message loops. This is to avoid messy + // recursion problems. + DCHECK(state_->run_depth == 1) << "Cannot nest an IO message loop!"; + + int timeout = GetCurrentDelay(); + if (timeout < 0) // Negative value means no timers waiting. + timeout = INFINITE; + + WaitForIOCompletion(timeout, NULL); +} + +bool MessagePumpForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) { + IOItem item; + if (completed_io_.empty() || !MatchCompletedIOItem(filter, &item)) { + // We have to ask the system for another IO completion. + if (!GetIOItem(timeout, &item)) + return false; + + if (ProcessInternalIOItem(item)) + return true; + } + + if (item.context->handler) { + if (filter && item.handler != filter) { + // Save this item for later + completed_io_.push_back(item); + } else { + DCHECK(item.context->handler == item.handler); + item.handler->OnIOCompleted(item.context, item.bytes_transfered, + item.error); + } + } else { + // The handler must be gone by now, just cleanup the mess. + delete item.context; + } + return true; +} + +// Asks the OS for another IO completion result. +bool MessagePumpForIO::GetIOItem(DWORD timeout, IOItem* item) { + memset(item, 0, sizeof(*item)); + ULONG_PTR key = NULL; + OVERLAPPED* overlapped = NULL; + if (!GetQueuedCompletionStatus(port_.Get(), &item->bytes_transfered, &key, + &overlapped, timeout)) { + if (!overlapped) + return false; // Nothing in the queue. + item->error = GetLastError(); + item->bytes_transfered = 0; + } + + item->handler = reinterpret_cast(key); + item->context = reinterpret_cast(overlapped); + return true; +} + +bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) { + if (this == reinterpret_cast(item.context) && + this == reinterpret_cast(item.handler)) { + // This is our internal completion. + DCHECK(!item.bytes_transfered); + InterlockedExchange(&have_work_, 0); + return true; + } + return false; +} + +// Returns a completion item that was previously received. +bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) { + DCHECK(!completed_io_.empty()); + for (std::list::iterator it = completed_io_.begin(); + it != completed_io_.end(); ++it) { + if (!filter || it->handler == filter) { + *item = *it; + completed_io_.erase(it); + return true; + } + } + return false; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_win.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_win.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/message_pump_win.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/message_pump_win.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,343 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MESSAGE_PUMP_WIN_H_ +#define BASE_MESSAGE_PUMP_WIN_H_ + +#include + +#include + +#include "base/lock.h" +#include "base/message_pump.h" +#include "base/observer_list.h" +#include "base/scoped_handle.h" +#include "base/time.h" + +namespace base { + +// MessagePumpWin serves as the base for specialized versions of the MessagePump +// for Windows. It provides basic functionality like handling of observers and +// controlling the lifetime of the message pump. +class MessagePumpWin : public MessagePump { + public: + // An Observer is an object that receives global notifications from the + // MessageLoop. + // + // NOTE: An Observer implementation should be extremely fast! + // + class Observer { + public: + virtual ~Observer() {} + + // This method is called before processing a message. + // The message may be undefined in which case msg.message is 0 + virtual void WillProcessMessage(const MSG& msg) = 0; + + // This method is called when control returns from processing a UI message. + // The message may be undefined in which case msg.message is 0 + virtual void DidProcessMessage(const MSG& msg) = 0; + }; + + // Dispatcher is used during a nested invocation of Run to dispatch events. + // If Run is invoked with a non-NULL Dispatcher, MessageLoop does not + // dispatch events (or invoke TranslateMessage), rather every message is + // passed to Dispatcher's Dispatch method for dispatch. It is up to the + // Dispatcher to dispatch, or not, the event. + // + // The nested loop is exited by either posting a quit, or returning false + // from Dispatch. + class Dispatcher { + public: + virtual ~Dispatcher() {} + // Dispatches the event. If true is returned processing continues as + // normal. If false is returned, the nested loop exits immediately. + virtual bool Dispatch(const MSG& msg) = 0; + }; + + MessagePumpWin() : have_work_(0), state_(NULL) {} + virtual ~MessagePumpWin() {} + + // Add an Observer, which will start receiving notifications immediately. + void AddObserver(Observer* observer); + + // Remove an Observer. It is safe to call this method while an Observer is + // receiving a notification callback. + void RemoveObserver(Observer* observer); + + // Give a chance to code processing additional messages to notify the + // message loop observers that another message has been processed. + void WillProcessMessage(const MSG& msg); + void DidProcessMessage(const MSG& msg); + + // Like MessagePump::Run, but MSG objects are routed through dispatcher. + void RunWithDispatcher(Delegate* delegate, Dispatcher* dispatcher); + + // MessagePump methods: + virtual void Run(Delegate* delegate) { RunWithDispatcher(delegate, NULL); } + virtual void Quit(); + + protected: + struct RunState { + Delegate* delegate; + Dispatcher* dispatcher; + + // Used to flag that the current Run() invocation should return ASAP. + bool should_quit; + + // Used to count how many Run() invocations are on the stack. + int run_depth; + }; + + virtual void DoRunLoop() = 0; + int GetCurrentDelay() const; + + ObserverList observers_; + + // The time at which delayed work should run. + Time delayed_work_time_; + + // A boolean value used to indicate if there is a kMsgDoWork message pending + // in the Windows Message queue. There is at most one such message, and it + // can drive execution of tasks when a native message pump is running. + LONG have_work_; + + // State for the current invocation of Run. + RunState* state_; +}; + +//----------------------------------------------------------------------------- +// MessagePumpForUI extends MessagePumpWin with methods that are particular to a +// MessageLoop instantiated with TYPE_UI. +// +// MessagePumpForUI implements a "traditional" Windows message pump. It contains +// a nearly infinite loop that peeks out messages, and then dispatches them. +// Intermixed with those peeks are callouts to DoWork for pending tasks, and +// DoDelayedWork for pending timers. When there are no events to be serviced, +// this pump goes into a wait state. In most cases, this message pump handles +// all processing. +// +// However, when a task, or windows event, invokes on the stack a native dialog +// box or such, that window typically provides a bare bones (native?) message +// pump. That bare-bones message pump generally supports little more than a +// peek of the Windows message queue, followed by a dispatch of the peeked +// message. MessageLoop extends that bare-bones message pump to also service +// Tasks, at the cost of some complexity. +// +// The basic structure of the extension (refered to as a sub-pump) is that a +// special message, kMsgHaveWork, is repeatedly injected into the Windows +// Message queue. Each time the kMsgHaveWork message is peeked, checks are +// made for an extended set of events, including the availability of Tasks to +// run. +// +// After running a task, the special message kMsgHaveWork is again posted to +// the Windows Message queue, ensuring a future time slice for processing a +// future event. To prevent flooding the Windows Message queue, care is taken +// to be sure that at most one kMsgHaveWork message is EVER pending in the +// Window's Message queue. +// +// There are a few additional complexities in this system where, when there are +// no Tasks to run, this otherwise infinite stream of messages which drives the +// sub-pump is halted. The pump is automatically re-started when Tasks are +// queued. +// +// A second complexity is that the presence of this stream of posted tasks may +// prevent a bare-bones message pump from ever peeking a WM_PAINT or WM_TIMER. +// Such paint and timer events always give priority to a posted message, such as +// kMsgHaveWork messages. As a result, care is taken to do some peeking in +// between the posting of each kMsgHaveWork message (i.e., after kMsgHaveWork +// is peeked, and before a replacement kMsgHaveWork is posted). +// +// NOTE: Although it may seem odd that messages are used to start and stop this +// flow (as opposed to signaling objects, etc.), it should be understood that +// the native message pump will *only* respond to messages. As a result, it is +// an excellent choice. It is also helpful that the starter messages that are +// placed in the queue when new task arrive also awakens DoRunLoop. +// +class MessagePumpForUI : public MessagePumpWin { + public: + MessagePumpForUI(); + virtual ~MessagePumpForUI(); + + // MessagePump methods: + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const Time& delayed_work_time); + + // Applications can call this to encourage us to process all pending WM_PAINT + // messages. This method will process all paint messages the Windows Message + // queue can provide, up to some fixed number (to avoid any infinite loops). + void PumpOutPendingPaintMessages(); + + private: + static LRESULT CALLBACK WndProcThunk( + HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + virtual void DoRunLoop(); + void InitMessageWnd(); + void WaitForWork(); + void HandleWorkMessage(); + void HandleTimerMessage(); + bool ProcessNextWindowsMessage(); + bool ProcessMessageHelper(const MSG& msg); + bool ProcessPumpReplacementMessage(); + + // A hidden message-only window. + HWND message_hwnd_; +}; + +//----------------------------------------------------------------------------- +// MessagePumpForIO extends MessagePumpWin with methods that are particular to a +// MessageLoop instantiated with TYPE_IO. This version of MessagePump does not +// deal with Windows mesagges, and instead has a Run loop based on Completion +// Ports so it is better suited for IO operations. +// +class MessagePumpForIO : public MessagePumpWin { + public: + struct IOContext; + + // Clients interested in receiving OS notifications when asynchronous IO + // operations complete should implement this interface and register themselves + // with the message pump. + // + // Typical use #1: + // // Use only when there are no user's buffers involved on the actual IO, + // // so that all the cleanup can be done by the message pump. + // class MyFile : public IOHandler { + // MyFile() { + // ... + // context_ = new IOContext; + // context_->handler = this; + // message_pump->RegisterIOHandler(file_, this); + // } + // ~MyFile() { + // if (pending_) { + // // By setting the handler to NULL, we're asking for this context + // // to be deleted when received, without calling back to us. + // context_->handler = NULL; + // } else { + // delete context_; + // } + // } + // virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered, + // DWORD error) { + // pending_ = false; + // } + // void DoSomeIo() { + // ... + // // The only buffer required for this operation is the overlapped + // // structure. + // ConnectNamedPipe(file_, &context_->overlapped); + // pending_ = true; + // } + // bool pending_; + // IOContext* context_; + // HANDLE file_; + // }; + // + // Typical use #2: + // class MyFile : public IOHandler { + // MyFile() { + // ... + // message_pump->RegisterIOHandler(file_, this); + // } + // // Plus some code to make sure that this destructor is not called + // // while there are pending IO operations. + // ~MyFile() { + // } + // virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered, + // DWORD error) { + // ... + // delete context; + // } + // void DoSomeIo() { + // ... + // IOContext* context = new IOContext; + // // This is not used for anything. It just prevents the context from + // // being considered "abandoned". + // context->handler = this; + // ReadFile(file_, buffer, num_bytes, &read, &context->overlapped); + // } + // HANDLE file_; + // }; + // + // Typical use #3: + // Same as the previous example, except that in order to deal with the + // requirement stated for the destructor, the class calls WaitForIOCompletion + // from the destructor to block until all IO finishes. + // ~MyFile() { + // while(pending_) + // message_pump->WaitForIOCompletion(INFINITE, this); + // } + // + class IOHandler { + public: + virtual ~IOHandler() {} + // This will be called once the pending IO operation associated with + // |context| completes. |error| is the Win32 error code of the IO operation + // (ERROR_SUCCESS if there was no error). |bytes_transfered| will be zero + // on error. + virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered, + DWORD error) = 0; + }; + + // The extended context that should be used as the base structure on every + // overlapped IO operation. |handler| must be set to the registered IOHandler + // for the given file when the operation is started, and it can be set to NULL + // before the operation completes to indicate that the handler should not be + // called anymore, and instead, the IOContext should be deleted when the OS + // notifies the completion of this operation. Please remember that any buffers + // involved with an IO operation should be around until the callback is + // received, so this technique can only be used for IO that do not involve + // additional buffers (other than the overlapped structure itself). + struct IOContext { + OVERLAPPED overlapped; + IOHandler* handler; + }; + + MessagePumpForIO(); + virtual ~MessagePumpForIO() {} + + // MessagePump methods: + virtual void ScheduleWork(); + virtual void ScheduleDelayedWork(const Time& delayed_work_time); + + // Register the handler to be used when asynchronous IO for the given file + // completes. The registration persists as long as |file_handle| is valid, so + // |handler| must be valid as long as there is pending IO for the given file. + void RegisterIOHandler(HANDLE file_handle, IOHandler* handler); + + // Waits for the next IO completion that should be processed by |filter|, for + // up to |timeout| milliseconds. Return true if any IO operation completed, + // regardless of the involved handler, and false if the timeout expired. If + // the completion port received any message and the involved IO handler + // matches |filter|, the callback is called before returning from this code; + // if the handler is not the one that we are looking for, the callback will + // be postponed for another time, so reentrancy problems can be avoided. + // External use of this method should be reserved for the rare case when the + // caller is willing to allow pausing regular task dispatching on this thread. + bool WaitForIOCompletion(DWORD timeout, IOHandler* filter); + + private: + struct IOItem { + IOHandler* handler; + IOContext* context; + DWORD bytes_transfered; + DWORD error; + }; + + virtual void DoRunLoop(); + void WaitForWork(); + bool MatchCompletedIOItem(IOHandler* filter, IOItem* item); + bool GetIOItem(DWORD timeout, IOItem* item); + bool ProcessInternalIOItem(const IOItem& item); + + // The completion port associated with this thread. + ScopedHandle port_; + // This list will be empty almost always. It stores IO completions that have + // not been delivered yet because somebody was doing cleanup. + std::list completed_io_; +}; + +} // namespace base + +#endif // BASE_MESSAGE_PUMP_WIN_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/multiprocess_test.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/multiprocess_test.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/multiprocess_test.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/multiprocess_test.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,121 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MULTIPROCESS_TEST_H__ +#define BASE_MULTIPROCESS_TEST_H__ + +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/process_util.h" +#include "base/string_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/multiprocess_func_list.h" +#include "testing/platform_test.h" + +#if defined(OS_POSIX) +#include +#include +#endif + +// Command line switch to invoke a child process rather than +// to run the normal test suite. +static const wchar_t kRunClientProcess[] = L"client"; + +// A MultiProcessTest is a test class which makes it easier to +// write a test which requires code running out of process. +// +// To create a multiprocess test simply follow these steps: +// +// 1) Derive your test from MultiProcessTest. Example: +// +// class MyTest : public MultiProcessTest { +// }; +// +// TEST_F(MyTest, TestCaseName) { +// ... +// } +// +// 2) Create a mainline function for the child processes and include +// testing/multiprocess_func_list.h. +// See the declaration of the MULTIPROCESS_TEST_MAIN macro +// in that file for an example. +// 3) Call SpawnChild(L"foo"), where "foo" is the name of +// the function you wish to run in the child processes. +// That's it! +// +class MultiProcessTest : public PlatformTest { + protected: + // Run a child process. + // 'procname' is the name of a function which the child will + // execute. It must be exported from this library in order to + // run. + // + // Example signature: + // extern "C" int __declspec(dllexport) FooBar() { + // // do client work here + // } + // + // Returns the handle to the child, or NULL on failure + // + // TODO(darin): re-enable this once we have base/debug_util.h + // ProcessDebugFlags(&cl, DebugUtil::UNKNOWN, false); + base::ProcessHandle SpawnChild(const std::wstring& procname) { + return SpawnChild(procname, false); + } + + base::ProcessHandle SpawnChild(const std::wstring& procname, + bool debug_on_start) { +#if defined(OS_WIN) + return SpawnChildImpl(procname, debug_on_start); +#elif defined(OS_POSIX) + base::file_handle_mapping_vector empty_file_list; + return SpawnChildImpl(procname, empty_file_list, debug_on_start); +#endif + } + +#if defined(OS_POSIX) + base::ProcessHandle SpawnChild( + const std::wstring& procname, + const base::file_handle_mapping_vector& fds_to_map, + bool debug_on_start) { + return SpawnChildImpl(procname, fds_to_map, debug_on_start); + } +#endif + + private: +#if defined(OS_WIN) + base::ProcessHandle SpawnChildImpl( + const std::wstring& procname, + bool debug_on_start) { + CommandLine cl(*CommandLine::ForCurrentProcess()); + base::ProcessHandle handle = static_cast(NULL); + cl.AppendSwitchWithValue(kRunClientProcess, procname); + + if (debug_on_start) + cl.AppendSwitch(switches::kDebugOnStart); + + base::LaunchApp(cl, false, true, &handle); + return handle; + } +#elif defined(OS_POSIX) + // TODO(port): with the CommandLine refactoring, this code is very similar + // to the Windows code. Investigate whether this can be made shorter. + base::ProcessHandle SpawnChildImpl( + const std::wstring& procname, + const base::file_handle_mapping_vector& fds_to_map, + bool debug_on_start) { + CommandLine cl(*CommandLine::ForCurrentProcess()); + base::ProcessHandle handle = static_cast(NULL); + cl.AppendSwitchWithValue(kRunClientProcess, procname); + + if (debug_on_start) + cl.AppendSwitch(switches::kDebugOnStart); + + base::LaunchApp(cl.argv(), fds_to_map, false, &handle); + return handle; + } +#endif +}; + +#endif // BASE_MULTIPROCESS_TEST_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/native_library.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/native_library.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/native_library.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/native_library.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,47 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NATIVE_LIBRARY_H_ +#define BASE_NATIVE_LIBRARY_H_ + +// This file defines a cross-platform "NativeLibrary" type which represents +// a loadable module. + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#elif defined(OS_MACOSX) +#import +#endif // OS_* + +class FilePath; + +namespace base { + +#if defined(OS_WIN) +typedef HMODULE NativeLibrary; +typedef char* NativeLibraryFunctionNameType; +#elif defined(OS_MACOSX) +typedef CFBundleRef NativeLibrary; +typedef CFStringRef NativeLibraryFunctionNameType; +#elif defined(OS_LINUX) +typedef void* NativeLibrary; +typedef const char* NativeLibraryFunctionNameType; +#endif // OS_* + +// Loads a native library from disk. Release it with UnloadNativeLibrary when +// you're done. +NativeLibrary LoadNativeLibrary(const FilePath& library_path); + +// Unloads a native library. +void UnloadNativeLibrary(NativeLibrary library); + +// Gets a function pointer from a native library. +void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, + NativeLibraryFunctionNameType name); + +} // namespace base + +#endif // BASE_NATIVE_LIBRARY_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/native_library_linux.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/native_library_linux.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/native_library_linux.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/native_library_linux.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,36 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/native_library.h" + +#include + +#include "base/file_path.h" +#include "base/logging.h" + +namespace base { + +// static +NativeLibrary LoadNativeLibrary(const FilePath& library_path) { + void* dl = dlopen(library_path.value().c_str(), RTLD_LAZY); + if (!dl) + NOTREACHED() << "dlopen failed: " << dlerror(); + + return dl; +} + +// static +void UnloadNativeLibrary(NativeLibrary library) { + int ret = dlclose(library); + if (ret < 0) + NOTREACHED() << "dlclose failed: " << dlerror(); +} + +// static +void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, + NativeLibraryFunctionNameType name) { + return dlsym(library, name); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/native_library_mac.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/native_library_mac.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/native_library_mac.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/native_library_mac.mm 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,38 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/native_library.h" + +#import + +#include "base/file_path.h" +#include "base/scoped_cftyperef.h" + +namespace base { + +// static +NativeLibrary LoadNativeLibrary(const FilePath& library_path) { + scoped_cftyperef url(CFURLCreateFromFileSystemRepresentation( + kCFAllocatorDefault, + (const UInt8*)library_path.value().c_str(), + library_path.value().length(), + true)); + if (!url) + return NULL; + + return CFBundleCreate(kCFAllocatorDefault, url.get()); +} + +// static +void UnloadNativeLibrary(NativeLibrary library) { + CFRelease(library); +} + +// static +void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, + NativeLibraryFunctionNameType name) { + return CFBundleGetFunctionPointerForName(library, name); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/native_library_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/native_library_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/native_library_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/native_library_win.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,46 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/native_library.h" + +#include + +#include "base/file_path.h" +#include "base/path_service.h" + +namespace base { + +// static +NativeLibrary LoadNativeLibrary(const FilePath& library_path) { + // Switch the current directory to the library directory as the library + // may have dependencies on DLLs in this directory. + bool restore_directory = false; + std::wstring current_directory; + if (PathService::Get(base::DIR_CURRENT, ¤t_directory)) { + FilePath plugin_path = library_path.DirName(); + if (!plugin_path.value().empty()) { + PathService::SetCurrentDirectory(plugin_path.value()); + restore_directory = true; + } + } + + HMODULE module = LoadLibrary(library_path.value().c_str()); + if (restore_directory) + PathService::SetCurrentDirectory(current_directory); + + return module; +} + +// static +void UnloadNativeLibrary(NativeLibrary library) { + FreeLibrary(library); +} + +// static +void* GetFunctionPointerFromNativeLibrary(NativeLibrary library, + NativeLibraryFunctionNameType name) { + return GetProcAddress(library, name); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/non_thread_safe.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/non_thread_safe.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/non_thread_safe.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/non_thread_safe.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,24 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/non_thread_safe.h" + +// These checks are only done in debug builds. +#ifndef NDEBUG + +#include "base/logging.h" + +NonThreadSafe::NonThreadSafe() + : valid_thread_id_(PlatformThread::CurrentId()) { +} + +bool NonThreadSafe::CalledOnValidThread() const { + return valid_thread_id_ == PlatformThread::CurrentId(); +} + +NonThreadSafe::~NonThreadSafe() { + DCHECK(CalledOnValidThread()); +} + +#endif // NDEBUG diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/non_thread_safe.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/non_thread_safe.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/non_thread_safe.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/non_thread_safe.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,52 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NON_THREAD_SAFE_H__ +#define BASE_NON_THREAD_SAFE_H__ + +#include "base/platform_thread.h" + +// A helper class used to help verify that methods of a class are +// called from the same thread. One can inherit from this class and use +// CalledOnValidThread() to verify. +// +// This is intended to be used with classes that appear to be thread safe, but +// aren't. For example, a service or a singleton like the preferences system. +// +// Example: +// class MyClass : public NonThreadSafe { +// public: +// void Foo() { +// DCHECK(CalledOnValidThread()); +// ... (do stuff) ... +// } +// } +// +// In Release mode, CalledOnValidThread will always return true. +// +#ifndef NDEBUG +class NonThreadSafe { + public: + NonThreadSafe(); + ~NonThreadSafe(); + + bool CalledOnValidThread() const; + + private: + PlatformThreadId valid_thread_id_; +}; +#else +// Do nothing in release mode. +class NonThreadSafe { + public: + NonThreadSafe() {} + ~NonThreadSafe() {} + + bool CalledOnValidThread() const { + return true; + } +}; +#endif // NDEBUG + +#endif // BASE_NON_THREAD_SAFE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/no_windows2000_unittest.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/no_windows2000_unittest.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/no_windows2000_unittest.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/no_windows2000_unittest.h 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,21 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NO_WINDOWS2000_UNITTEST_H_ +#define BASE_NO_WINDOWS2000_UNITTEST_H_ + +#include "testing/gtest/include/gtest/gtest.h" +#include "base/win_util.h" + +// Disable the whole test case when executing on Windows 2000 or lower. +// Note: Parent should be testing::Test or UITest. +template +class NoWindows2000Test : public Parent { + public: + static bool IsTestCaseDisabled() { + return win_util::GetWinVersion() <= win_util::WINVERSION_2000; + } +}; + +#endif // BASE_NO_WINDOWS2000_UNITTEST_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/nss_init.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/nss_init.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/nss_init.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/nss_init.cc 2010-04-16 17:31:50.000000000 +0100 @@ -0,0 +1,109 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/nss_init.h" + +#include +#include +#include +#include + +// Work around https://bugzilla.mozilla.org/show_bug.cgi?id=455424 +// until NSS 3.12.2 comes out and we update to it. +#define Lock FOO_NSS_Lock +#include +#include +#undef Lock + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/singleton.h" + +namespace { + +// Load nss's built-in root certs. +SECMODModule *InitDefaultRootCerts() { + const char* kModulePath = "libnssckbi.so"; + char modparams[1024]; + snprintf(modparams, sizeof(modparams), + "name=\"Root Certs\" library=\"%s\"", kModulePath); + SECMODModule *root = SECMOD_LoadUserModule(modparams, NULL, PR_FALSE); + if (root) + return root; + + // Aw, snap. Can't find/load root cert shared library. + // This will make it hard to talk to anybody via https. + NOTREACHED(); + return NULL; +} + +class NSSInitSingleton { + public: + NSSInitSingleton() { + // Initialize without using a persistant database (e.g. ~/.netscape) + SECStatus status = NSS_NoDB_Init("."); + if (status != SECSuccess) { + char buffer[513] = "Couldn't retrieve error"; + PRInt32 err_length = PR_GetErrorTextLength(); + if (err_length > 0 && static_cast(err_length) < sizeof(buffer)) + PR_GetErrorText(buffer); + + NOTREACHED() << "Error calling NSS_NoDB_Init: " << buffer; + } + + root_ = InitDefaultRootCerts(); + + NSS_SetDomesticPolicy(); + + // Explicitly enable exactly those ciphers with keys of at least 80 bits + for (int i = 0; i < SSL_NumImplementedCiphers; i++) { + SSLCipherSuiteInfo info; + if (SSL_GetCipherSuiteInfo(SSL_ImplementedCiphers[i], &info, + sizeof(info)) == SECSuccess) { + SSL_CipherPrefSetDefault(SSL_ImplementedCiphers[i], + (info.effectiveKeyBits >= 80)); + } + } + + // Enable SSL + SSL_OptionSetDefault(SSL_SECURITY, PR_TRUE); + + // All other SSL options are set per-session by SSLClientSocket. + } + + ~NSSInitSingleton() { + if (root_) { + SECMOD_UnloadUserModule(root_); + SECMOD_DestroyModule(root_); + root_ = NULL; + } + + // Have to clear the cache, or NSS_Shutdown fails with SEC_ERROR_BUSY + SSL_ClearSessionCache(); + + SECStatus status = NSS_Shutdown(); + if (status != SECSuccess) + LOG(ERROR) << "NSS_Shutdown failed, leak? See " + "http://code.google.com/p/chromium/issues/detail?id=4609"; + + PL_ArenaFinish(); + + PRStatus prstatus = PR_Cleanup(); + if (prstatus != PR_SUCCESS) + LOG(ERROR) << "PR_Cleanup failed?"; + } + + private: + SECMODModule *root_; +}; + +} // namespace + +namespace base { + +void EnsureNSSInit() { + Singleton::get(); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/nss_init.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/nss_init.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/nss_init.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/nss_init.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,17 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_NSS_INIT_H_ +#define BASE_NSS_INIT_H_ + +namespace base { + +// Initialize NSS if it isn't already initialized. This must be called before +// any other NSS functions. This function is thread-safe, and NSS will only +// ever be initialized once. NSS will be properly shut down on program exit. +void EnsureNSSInit(); + +} // namespace base + +#endif // BASE_NSS_INIT_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/object_watcher.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/object_watcher.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/object_watcher.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/object_watcher.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,138 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/object_watcher.h" + +#include "base/logging.h" + +namespace base { + +//----------------------------------------------------------------------------- + +struct ObjectWatcher::Watch : public Task { + ObjectWatcher* watcher; // The associated ObjectWatcher instance + HANDLE object; // The object being watched + HANDLE wait_object; // Returned by RegisterWaitForSingleObject + MessageLoop* origin_loop; // Used to get back to the origin thread + Delegate* delegate; // Delegate to notify when signaled + bool did_signal; // DoneWaiting was called + + virtual void Run() { + // The watcher may have already been torn down, in which case we need to + // just get out of dodge. + if (!watcher) + return; + + DCHECK(did_signal); + watcher->StopWatching(); + + delegate->OnObjectSignaled(object); + } +}; + +//----------------------------------------------------------------------------- + +ObjectWatcher::ObjectWatcher() : watch_(NULL) { +} + +ObjectWatcher::~ObjectWatcher() { + StopWatching(); +} + +bool ObjectWatcher::StartWatching(HANDLE object, Delegate* delegate) { + if (watch_) { + NOTREACHED() << "Already watching an object"; + return false; + } + + Watch* watch = new Watch; + watch->watcher = this; + watch->object = object; + watch->origin_loop = MessageLoop::current(); + watch->delegate = delegate; + watch->did_signal = false; + + // Since our job is to just notice when an object is signaled and report the + // result back to this thread, we can just run on a Windows wait thread. + DWORD wait_flags = WT_EXECUTEINWAITTHREAD | WT_EXECUTEONLYONCE; + + if (!RegisterWaitForSingleObject(&watch->wait_object, object, DoneWaiting, + watch, INFINITE, wait_flags)) { + NOTREACHED() << "RegisterWaitForSingleObject failed: " << GetLastError(); + delete watch; + return false; + } + + watch_ = watch; + + // We need to know if the current message loop is going away so we can + // prevent the wait thread from trying to access a dead message loop. + MessageLoop::current()->AddDestructionObserver(this); + return true; +} + +bool ObjectWatcher::StopWatching() { + if (!watch_) + return false; + + // Make sure ObjectWatcher is used in a single-threaded fashion. + DCHECK(watch_->origin_loop == MessageLoop::current()); + + // If DoneWaiting is in progress, we wait for it to finish. We know whether + // DoneWaiting happened or not by inspecting the did_signal flag. + if (!UnregisterWaitEx(watch_->wait_object, INVALID_HANDLE_VALUE)) { + NOTREACHED() << "UnregisterWaitEx failed: " << GetLastError(); + return false; + } + + // Make sure that we see any mutation to did_signal. This should be a no-op + // since we expect that UnregisterWaitEx resulted in a memory barrier, but + // just to be sure, we're going to be explicit. + MemoryBarrier(); + + // If the watch has been posted, then we need to make sure it knows not to do + // anything once it is run. + watch_->watcher = NULL; + + // If DoneWaiting was called, then the watch would have been posted as a + // task, and will therefore be deleted by the MessageLoop. Otherwise, we + // need to take care to delete it here. + if (!watch_->did_signal) + delete watch_; + + watch_ = NULL; + + MessageLoop::current()->RemoveDestructionObserver(this); + return true; +} + +HANDLE ObjectWatcher::GetWatchedObject() { + if (!watch_) + return NULL; + + return watch_->object; +} + +// static +void CALLBACK ObjectWatcher::DoneWaiting(void* param, BOOLEAN timed_out) { + DCHECK(!timed_out); + + Watch* watch = static_cast(param); + + // Record that we ran this function. + watch->did_signal = true; + + // We rely on the locking in PostTask() to ensure that a memory barrier is + // provided, which in turn ensures our change to did_signal can be observed + // on the target thread. + watch->origin_loop->PostTask(FROM_HERE, watch); +} + +void ObjectWatcher::WillDestroyCurrentMessageLoop() { + // Need to shutdown the watch so that we don't try to access the MessageLoop + // after this point. + StopWatching(); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/object_watcher.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/object_watcher.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/object_watcher.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/object_watcher.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,91 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_OBJECT_WATCHER_H_ +#define BASE_OBJECT_WATCHER_H_ + +#include + +#include "base/message_loop.h" + +namespace base { + +// A class that provides a means to asynchronously wait for a Windows object to +// become signaled. It is an abstraction around RegisterWaitForSingleObject +// that provides a notification callback, OnObjectSignaled, that runs back on +// the origin thread (i.e., the thread that called StartWatching). +// +// This class acts like a smart pointer such that when it goes out-of-scope, +// UnregisterWaitEx is automatically called, and any in-flight notification is +// suppressed. +// +// Typical usage: +// +// class MyClass : public base::ObjectWatcher::Delegate { +// public: +// void DoStuffWhenSignaled(HANDLE object) { +// watcher_.StartWatching(object, this); +// } +// virtual void OnObjectSignaled(HANDLE object) { +// // OK, time to do stuff! +// } +// private: +// base::ObjectWatcher watcher_; +// }; +// +// In the above example, MyClass wants to "do stuff" when object becomes +// signaled. ObjectWatcher makes this task easy. When MyClass goes out of +// scope, the watcher_ will be destroyed, and there is no need to worry about +// OnObjectSignaled being called on a deleted MyClass pointer. Easy! +// +class ObjectWatcher : public MessageLoop::DestructionObserver { + public: + class Delegate { + public: + virtual ~Delegate() {} + // Called from the MessageLoop when a signaled object is detected. To + // continue watching the object, AddWatch must be called again. + virtual void OnObjectSignaled(HANDLE object) = 0; + }; + + ObjectWatcher(); + ~ObjectWatcher(); + + // When the object is signaled, the given delegate is notified on the thread + // where StartWatching is called. The ObjectWatcher is not responsible for + // deleting the delegate. + // + // Returns true if the watch was started. Otherwise, false is returned. + // + bool StartWatching(HANDLE object, Delegate* delegate); + + // Stops watching. Does nothing if the watch has already completed. If the + // watch is still active, then it is canceled, and the associated delegate is + // not notified. + // + // Returns true if the watch was canceled. Otherwise, false is returned. + // + bool StopWatching(); + + // Returns the handle of the object being watched, or NULL if the object + // watcher is stopped. + HANDLE GetWatchedObject(); + + private: + // Called on a background thread when done waiting. + static void CALLBACK DoneWaiting(void* param, BOOLEAN timed_out); + + // MessageLoop::DestructionObserver implementation: + virtual void WillDestroyCurrentMessageLoop(); + + // Internal state. + struct Watch; + Watch* watch_; + + DISALLOW_COPY_AND_ASSIGN(ObjectWatcher); +}; + +} // namespace base + +#endif // BASE_OBJECT_WATCHER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/object_watcher_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/object_watcher_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/object_watcher_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/object_watcher_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,143 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/message_loop.h" +#include "base/object_watcher.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class QuitDelegate : public base::ObjectWatcher::Delegate { + public: + virtual void OnObjectSignaled(HANDLE object) { + MessageLoop::current()->Quit(); + } +}; + +class DecrementCountDelegate : public base::ObjectWatcher::Delegate { + public: + DecrementCountDelegate(int* counter) : counter_(counter) { + } + virtual void OnObjectSignaled(HANDLE object) { + --(*counter_); + } + private: + int* counter_; +}; + +} // namespace + +void RunTest_BasicSignal(MessageLoop::Type message_loop_type) { + MessageLoop message_loop(message_loop_type); + + base::ObjectWatcher watcher; + EXPECT_EQ(NULL, watcher.GetWatchedObject()); + + // A manual-reset event that is not yet signaled. + HANDLE event = CreateEvent(NULL, TRUE, FALSE, NULL); + + QuitDelegate delegate; + bool ok = watcher.StartWatching(event, &delegate); + EXPECT_TRUE(ok); + EXPECT_EQ(event, watcher.GetWatchedObject()); + + SetEvent(event); + + MessageLoop::current()->Run(); + + EXPECT_EQ(NULL, watcher.GetWatchedObject()); + CloseHandle(event); +} + +void RunTest_BasicCancel(MessageLoop::Type message_loop_type) { + MessageLoop message_loop(message_loop_type); + + base::ObjectWatcher watcher; + + // A manual-reset event that is not yet signaled. + HANDLE event = CreateEvent(NULL, TRUE, FALSE, NULL); + + QuitDelegate delegate; + bool ok = watcher.StartWatching(event, &delegate); + EXPECT_TRUE(ok); + + watcher.StopWatching(); + + CloseHandle(event); +} + + +void RunTest_CancelAfterSet(MessageLoop::Type message_loop_type) { + MessageLoop message_loop(message_loop_type); + + base::ObjectWatcher watcher; + + int counter = 1; + DecrementCountDelegate delegate(&counter); + + // A manual-reset event that is not yet signaled. + HANDLE event = CreateEvent(NULL, TRUE, FALSE, NULL); + + bool ok = watcher.StartWatching(event, &delegate); + EXPECT_TRUE(ok); + + SetEvent(event); + + // Let the background thread do its business + Sleep(30); + + watcher.StopWatching(); + + MessageLoop::current()->RunAllPending(); + + // Our delegate should not have fired. + EXPECT_EQ(1, counter); + + CloseHandle(event); +} + +void RunTest_OutlivesMessageLoop(MessageLoop::Type message_loop_type) { + // Simulate a MessageLoop that dies before an ObjectWatcher. This ordinarily + // doesn't happen when people use the Thread class, but it can happen when + // people use the Singleton pattern or atexit. + HANDLE event = CreateEvent(NULL, TRUE, FALSE, NULL); // not signaled + { + base::ObjectWatcher watcher; + { + MessageLoop message_loop(message_loop_type); + + QuitDelegate delegate; + watcher.StartWatching(event, &delegate); + } + } + CloseHandle(event); +} + +//----------------------------------------------------------------------------- + +TEST(ObjectWatcherTest, BasicSignal) { + RunTest_BasicSignal(MessageLoop::TYPE_DEFAULT); + RunTest_BasicSignal(MessageLoop::TYPE_IO); + RunTest_BasicSignal(MessageLoop::TYPE_UI); +} + +TEST(ObjectWatcherTest, BasicCancel) { + RunTest_BasicCancel(MessageLoop::TYPE_DEFAULT); + RunTest_BasicCancel(MessageLoop::TYPE_IO); + RunTest_BasicCancel(MessageLoop::TYPE_UI); +} + +TEST(ObjectWatcherTest, CancelAfterSet) { + RunTest_CancelAfterSet(MessageLoop::TYPE_DEFAULT); + RunTest_CancelAfterSet(MessageLoop::TYPE_IO); + RunTest_CancelAfterSet(MessageLoop::TYPE_UI); +} + +TEST(ObjectWatcherTest, OutlivesMessageLoop) { + RunTest_OutlivesMessageLoop(MessageLoop::TYPE_DEFAULT); + RunTest_OutlivesMessageLoop(MessageLoop::TYPE_IO); + RunTest_OutlivesMessageLoop(MessageLoop::TYPE_UI); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/observer_list.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/observer_list.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/observer_list.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/observer_list.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,176 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_OBSERVER_LIST_H__ +#define BASE_OBSERVER_LIST_H__ + +#include +#include +#include + +#include "base/basictypes.h" +#include "base/logging.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// OVERVIEW: +// +// A container for a list of observers. Unlike a normal STL vector or list, +// this container can be modified during iteration without invalidating the +// iterator. So, it safely handles the case of an observer removing itself +// or other observers from the list while observers are being notified. +// +// TYPICAL USAGE: +// +// class MyWidget { +// public: +// ... +// +// class Observer { +// public: +// virtual void OnFoo(MyWidget* w) = 0; +// virtual void OnBar(MyWidget* w, int x, int y) = 0; +// }; +// +// void AddObserver(Observer* obs) { +// observer_list_.AddObserver(obs); +// } +// +// void RemoveObserver(Observer* obs) { +// observer_list_.RemoveObserver(obs); +// } +// +// void NotifyFoo() { +// FOR_EACH_OBSERVER(Observer, observer_list_, OnFoo(this)); +// } +// +// void NotifyBar(int x, int y) { +// FOR_EACH_OBSERVER(Observer, observer_list_, OnBar(this, x, y)); +// } +// +// private: +// ObserverList observer_list_; +// }; +// +// +/////////////////////////////////////////////////////////////////////////////// + +template +class ObserverList { + public: + // Enumeration of which observers are notified. + enum NotificationType { + // Specifies that any observers added during notification are notified. + // This is the default type if non type is provided to the constructor. + NOTIFY_ALL, + + // Specifies that observers added while sending out notification are not + // notified. + NOTIFY_EXISTING_ONLY + }; + + ObserverList() : notify_depth_(0), type_(NOTIFY_ALL) {} + ObserverList(NotificationType type) : notify_depth_(0), type_(type) {} + ~ObserverList() { + // When check_empty is true, assert that the list is empty on destruction. + if (check_empty) { + Compact(); + DCHECK_EQ(observers_.size(), 0U); + } + } + + // Add an observer to the list. + void AddObserver(ObserverType* obs) { + DCHECK(find(observers_.begin(), observers_.end(), obs) == observers_.end()) + << "Observers can only be added once!"; + observers_.push_back(obs); + } + + // Remove an observer from the list. + void RemoveObserver(ObserverType* obs) { + typename ListType::iterator it = + std::find(observers_.begin(), observers_.end(), obs); + if (it != observers_.end()) { + if (notify_depth_) { + *it = 0; + } else { + observers_.erase(it); + } + } + } + + size_t size() const { + return observers_.size(); + } + + ObserverType* GetElementAt(int index) const { + return observers_[index]; + } + + // An iterator class that can be used to access the list of observers. See + // also the FOREACH_OBSERVER macro defined below. + class Iterator { + public: + Iterator(const ObserverList& list) + : list_(list), + index_(0), + max_index_(list.type_ == NOTIFY_ALL ? + std::numeric_limits::max() : + list.observers_.size()) { + ++list_.notify_depth_; + } + + ~Iterator() { + if (--list_.notify_depth_ == 0) + list_.Compact(); + } + + ObserverType* GetNext() { + ListType& observers = list_.observers_; + // Advance if the current element is null + size_t max_index = std::min(max_index_, observers.size()); + while (index_ < max_index && !observers[index_]) + ++index_; + return index_ < max_index ? observers[index_++] : NULL; + } + + private: + const ObserverList& list_; + size_t index_; + size_t max_index_; + }; + + private: + typedef std::vector ListType; + + void Compact() const { + typename ListType::iterator it = observers_.begin(); + while (it != observers_.end()) { + if (*it) { + ++it; + } else { + it = observers_.erase(it); + } + } + } + + // These are marked mutable to facilitate having NotifyAll be const. + mutable ListType observers_; + mutable int notify_depth_; + NotificationType type_; + + friend class ObserverList::Iterator; + + DISALLOW_EVIL_CONSTRUCTORS(ObserverList); +}; + +#define FOR_EACH_OBSERVER(ObserverType, observer_list, func) \ + do { \ + ObserverList::Iterator it(observer_list); \ + ObserverType* obs; \ + while ((obs = it.GetNext()) != NULL) \ + obs->func; \ + } while (0) + +#endif // BASE_OBSERVER_LIST_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/observer_list_threadsafe.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/observer_list_threadsafe.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/observer_list_threadsafe.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/observer_list_threadsafe.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,199 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_OBSERVER_LIST_THREADSAFE_H_ +#define BASE_OBSERVER_LIST_THREADSAFE_H_ + +#include +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/observer_list.h" +#include "base/ref_counted.h" +#include "base/task.h" + +/////////////////////////////////////////////////////////////////////////////// +// +// OVERVIEW: +// +// A thread-safe container for a list of observers. +// This is similar to the observer_list (see observer_list.h), but it +// is more robust for multi-threaded situations. +// +// The following use cases are supported: +// * Observers can register for notifications from any thread. +// Callbacks to the observer will occur on the same thread where +// the observer initially called AddObserver() from. +// * Any thread may trigger a notification via NOTIFY_OBSERVERS. +// * Observers can remove themselves from the observer list inside +// of a callback. +// * If one thread is notifying observers concurrently with an observer +// removing itself from the observer list, the notifications will +// be silently dropped. +// +// The drawback of the threadsafe observer list is that notifications +// are not as real-time as the non-threadsafe version of this class. +// Notifications will always be done via PostTask() to another thread, +// whereas with the non-thread-safe observer_list, notifications happen +// synchronously and immediately. +// +// IMPLEMENTATION NOTES +// The ObserverListThreadSafe maintains an ObserverList for each thread +// which uses the ThreadSafeObserver. When Notifying the observers, +// we simply call PostTask to each registered thread, and then each thread +// will notify its regular ObserverList. +// +/////////////////////////////////////////////////////////////////////////////// +template +class ObserverListThreadSafe + : public base::RefCountedThreadSafe > { + public: + ObserverListThreadSafe() {} + + ~ObserverListThreadSafe() { + typename ObserversListMap::const_iterator it; + for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) + delete (*it).second; + observer_lists_.clear(); + } + + // Add an observer to the list. + void AddObserver(ObserverType* obs) { + ObserverList* list = NULL; + MessageLoop* loop = MessageLoop::current(); + // TODO(mbelshe): Get rid of this check. Its needed right now because + // Time currently triggers usage of the ObserverList. + // And unittests use time without a MessageLoop. + if (!loop) + return; // Some unittests may access this without a message loop. + { + AutoLock lock(list_lock_); + if (observer_lists_.find(loop) == observer_lists_.end()) + observer_lists_[loop] = new ObserverList(); + list = observer_lists_[loop]; + } + list->AddObserver(obs); + } + + // Remove an observer from the list. + // If there are pending notifications in-transit to the observer, they will + // be aborted. + // RemoveObserver MUST be called from the same thread which called + // AddObserver. + void RemoveObserver(ObserverType* obs) { + ObserverList* list = NULL; + MessageLoop* loop = MessageLoop::current(); + if (!loop) + return; // On shutdown, it is possible that current() is already null. + { + AutoLock lock(list_lock_); + list = observer_lists_[loop]; + if (!list) { + NOTREACHED() << "RemoveObserver called on for unknown thread"; + return; + } + + // If we're about to remove the last observer from the list, + // then we can remove this observer_list entirely. + if (list->size() == 1) + observer_lists_.erase(loop); + } + list->RemoveObserver(obs); + + // If RemoveObserver is called from a notification, the size will be + // nonzero. Instead of deleting here, the NotifyWrapper will delete + // when it finishes iterating. + if (list->size() == 0) + delete list; + } + + // Notify methods. + // Make a thread-safe callback to each Observer in the list. + // Note, these calls are effectively asynchronous. You cannot assume + // that at the completion of the Notify call that all Observers have + // been Notified. The notification may still be pending delivery. + template + void Notify(Method m) { + UnboundMethod method(m, MakeTuple()); + Notify(method); + } + + template + void Notify(Method m, const A &a) { + UnboundMethod > method(m, MakeTuple(a)); + Notify >(method); + } + + // TODO(mbelshe): Add more wrappers for Notify() with more arguments. + + private: + template + void Notify(const UnboundMethod& method) { + AutoLock lock(list_lock_); + typename ObserversListMap::iterator it; + for (it = observer_lists_.begin(); it != observer_lists_.end(); ++it) { + MessageLoop* loop = (*it).first; + ObserverList* list = (*it).second; + loop->PostTask(FROM_HERE, + NewRunnableMethod(this, + &ObserverListThreadSafe:: + template NotifyWrapper, list, method)); + } + } + + // Wrapper which is called to fire the notifications for each thread's + // ObserverList. This function MUST be called on the thread which owns + // the unsafe ObserverList. + template + void NotifyWrapper(ObserverList* list, + const UnboundMethod& method) { + + // Check that this list still needs notifications. + { + AutoLock lock(list_lock_); + typename ObserversListMap::iterator it = + observer_lists_.find(MessageLoop::current()); + + // The ObserverList could have been removed already. In fact, it could + // have been removed and then re-added! If the master list's loop + // does not match this one, then we do not need to finish this + // notification. + if (it == observer_lists_.end() || it->second != list) + return; + } + + { + typename ObserverList::Iterator it(*list); + ObserverType* obs; + while ((obs = it.GetNext()) != NULL) + method.Run(obs); + } + + // If there are no more observers on the list, we can now delete it. + if (list->size() == 0) { +#ifndef NDEBUG + { + AutoLock lock(list_lock_); + // Verify this list is no longer registered. + typename ObserversListMap::iterator it = + observer_lists_.find(MessageLoop::current()); + DCHECK(it == observer_lists_.end() || it->second != list); + } +#endif + delete list; + } + } + + typedef std::map*> ObserversListMap; + + // These are marked mutable to facilitate having NotifyAll be const. + Lock list_lock_; // Protects the observer_lists_. + ObserversListMap observer_lists_; + + DISALLOW_EVIL_CONSTRUCTORS(ObserverListThreadSafe); +}; + +#endif // BASE_OBSERVER_LIST_THREADSAFE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/observer_list_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/observer_list_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/observer_list_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/observer_list_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,306 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop.h" +#include "base/observer_list.h" +#include "base/observer_list_threadsafe.h" +#include "base/platform_thread.h" +#include "base/ref_counted.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::Time; + +namespace { + +class ObserverListTest : public testing::Test { +}; + +class Foo { + public: + virtual void Observe(int x) = 0; + virtual ~Foo() {} +}; + +class Adder : public Foo { + public: + explicit Adder(int scaler) : total(0), scaler_(scaler) {} + virtual void Observe(int x) { + total += x * scaler_; + } + virtual ~Adder() { } + int total; + private: + int scaler_; +}; + +class Disrupter : public Foo { + public: + Disrupter(ObserverList* list, Foo* doomed) + : list_(list), doomed_(doomed) { } + virtual ~Disrupter() { } + virtual void Observe(int x) { + list_->RemoveObserver(doomed_); + } + private: + ObserverList* list_; + Foo* doomed_; +}; + +class ThreadSafeDisrupter : public Foo { + public: + ThreadSafeDisrupter(ObserverListThreadSafe* list, Foo* doomed) + : list_(list), doomed_(doomed) { } + virtual ~ThreadSafeDisrupter() { } + virtual void Observe(int x) { + list_->RemoveObserver(doomed_); + } + private: + ObserverListThreadSafe* list_; + Foo* doomed_; +}; + +class AddInObserve : public Foo { + public: + AddInObserve(ObserverList* observer_list) + : added(false), + observer_list(observer_list), + adder(1) { + } + virtual void Observe(int x) { + if (!added) { + added = true; + observer_list->AddObserver(&adder); + } + } + + bool added; + ObserverList* observer_list; + Adder adder; +}; + + +class ObserverListThreadSafeTest : public testing::Test { +}; + +static const int kThreadRunTime = 10000; // ms to run the multi-threaded test. + +// A thread for use in the ThreadSafeObserver test +// which will add and remove itself from the notification +// list repeatedly. +class AddRemoveThread : public PlatformThread::Delegate, + public Foo { + public: + AddRemoveThread(ObserverListThreadSafe* list, bool notify) + : list_(list), + in_list_(false), + start_(Time::Now()), + count_observes_(0), + count_addtask_(0), + do_notifies_(notify) { + factory_ = new ScopedRunnableMethodFactory(this); + } + + virtual ~AddRemoveThread() { + delete factory_; + } + + void ThreadMain() { + loop_ = new MessageLoop(); // Fire up a message loop. + loop_->PostTask(FROM_HERE, + factory_->NewRunnableMethod(&AddRemoveThread::AddTask)); + loop_->Run(); + //LOG(ERROR) << "Loop 0x" << std::hex << loop_ << " done. " << + // count_observes_ << ", " << count_addtask_; + delete loop_; + loop_ = reinterpret_cast(0xdeadbeef); + delete this; + } + + // This task just keeps posting to itself in an attempt + // to race with the notifier. + void AddTask() { + count_addtask_++; + + if ((Time::Now() - start_).InMilliseconds() > kThreadRunTime) { + LOG(INFO) << "DONE!"; + return; + } + + if (!in_list_) { + list_->AddObserver(this); + in_list_ = true; + } + + if (do_notifies_) { + list_->Notify(&Foo::Observe, 10); + } + + loop_->PostDelayedTask(FROM_HERE, + factory_->NewRunnableMethod(&AddRemoveThread::AddTask), 0); + } + + void Quit() { + loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + } + + virtual void Observe(int x) { + count_observes_++; + + // If we're getting called after we removed ourselves from + // the list, that is very bad! + DCHECK(in_list_); + + // This callback should fire on the appropriate thread + EXPECT_EQ(loop_, MessageLoop::current()); + + list_->RemoveObserver(this); + in_list_ = false; + } + + private: + ObserverListThreadSafe* list_; + MessageLoop* loop_; + bool in_list_; // Are we currently registered for notifications. + // in_list_ is only used on |this| thread. + Time start_; // The time we started the test. + + int count_observes_; // Number of times we observed. + int count_addtask_; // Number of times thread AddTask was called + bool do_notifies_; // Whether these threads should do notifications. + + ScopedRunnableMethodFactory* factory_; +}; + +} // namespace + +TEST(ObserverListTest, BasicTest) { + ObserverList observer_list; + Adder a(1), b(-1), c(1), d(-1); + Disrupter evil(&observer_list, &c); + + observer_list.AddObserver(&a); + observer_list.AddObserver(&b); + + FOR_EACH_OBSERVER(Foo, observer_list, Observe(10)); + + observer_list.AddObserver(&evil); + observer_list.AddObserver(&c); + observer_list.AddObserver(&d); + + FOR_EACH_OBSERVER(Foo, observer_list, Observe(10)); + + EXPECT_EQ(a.total, 20); + EXPECT_EQ(b.total, -20); + EXPECT_EQ(c.total, 0); + EXPECT_EQ(d.total, -10); +} + +TEST(ObserverListThreadSafeTest, BasicTest) { + MessageLoop loop; + + scoped_refptr > observer_list( + new ObserverListThreadSafe); + Adder a(1); + Adder b(-1); + Adder c(1); + Adder d(-1); + ThreadSafeDisrupter evil(observer_list.get(), &c); + + observer_list->AddObserver(&a); + observer_list->AddObserver(&b); + + observer_list->Notify(&Foo::Observe, 10); + loop.RunAllPending(); + + observer_list->AddObserver(&evil); + observer_list->AddObserver(&c); + observer_list->AddObserver(&d); + + observer_list->Notify(&Foo::Observe, 10); + loop.RunAllPending(); + + EXPECT_EQ(a.total, 20); + EXPECT_EQ(b.total, -20); + EXPECT_EQ(c.total, 0); + EXPECT_EQ(d.total, -10); +} + + +// A test driver for a multi-threaded notification loop. Runs a number +// of observer threads, each of which constantly adds/removes itself +// from the observer list. Optionally, if cross_thread_notifies is set +// to true, the observer threads will also trigger notifications to +// all observers. +static void ThreadSafeObserverHarness(int num_threads, + bool cross_thread_notifies) { + MessageLoop loop; + + const int kMaxThreads = 15; + num_threads = num_threads > kMaxThreads ? kMaxThreads : num_threads; + + scoped_refptr > observer_list( + new ObserverListThreadSafe); + Adder a(1); + Adder b(-1); + Adder c(1); + Adder d(-1); + + observer_list->AddObserver(&a); + observer_list->AddObserver(&b); + + AddRemoveThread* threaded_observer[kMaxThreads]; + PlatformThreadHandle threads[kMaxThreads]; + for (int index = 0; index < num_threads; index++) { + threaded_observer[index] = new AddRemoveThread(observer_list.get(), false); + EXPECT_TRUE(PlatformThread::Create(0, + threaded_observer[index], &threads[index])); + } + + Time start = Time::Now(); + while (true) { + if ((Time::Now() - start).InMilliseconds() > kThreadRunTime) + break; + + observer_list->Notify(&Foo::Observe, 10); + + loop.RunAllPending(); + } + + for (int index = 0; index < num_threads; index++) { + threaded_observer[index]->Quit(); + PlatformThread::Join(threads[index]); + } +} + +TEST(ObserverListThreadSafeTest, CrossThreadObserver) { + // Use 7 observer threads. Notifications only come from + // the main thread. + ThreadSafeObserverHarness(7, false); +} + +TEST(ObserverListThreadSafeTest, CrossThreadNotifications) { + // Use 3 observer threads. Notifications will fire from + // the main thread and all 3 observer threads. + ThreadSafeObserverHarness(3, true); +} + +TEST(ObserverListTest, Existing) { + ObserverList observer_list(ObserverList::NOTIFY_EXISTING_ONLY); + Adder a(1); + AddInObserve b(&observer_list); + + observer_list.AddObserver(&a); + observer_list.AddObserver(&b); + + FOR_EACH_OBSERVER(Foo, observer_list, Observe(1)); + + EXPECT_TRUE(b.added); + // B's adder should not have been notified because it was added during + // notificaiton. + EXPECT_EQ(0, b.adder.total); + + // Notify again to make sure b's adder is notified. + FOR_EACH_OBSERVER(Foo, observer_list, Observe(1)); + EXPECT_EQ(1, b.adder.total); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/path_service.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/path_service.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/path_service.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/path_service.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,267 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/path_service.h" + +#ifdef OS_WIN +#include +#include +#include +#endif + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/hash_tables.h" +#include "base/lock.h" +#include "base/logging.h" +#include "base/singleton.h" +#include "base/string_util.h" + +namespace base { + bool PathProvider(int key, FilePath* result); +#if defined(OS_WIN) + bool PathProviderWin(int key, FilePath* result); +#elif defined(OS_MACOSX) + bool PathProviderMac(int key, FilePath* result); +#elif defined(OS_LINUX) + bool PathProviderLinux(int key, FilePath* result); +#endif +} + +namespace { + +typedef base::hash_map PathMap; +typedef base::hash_set PathSet; + +// We keep a linked list of providers. In a debug build we ensure that no two +// providers claim overlapping keys. +struct Provider { + PathService::ProviderFunc func; + struct Provider* next; +#ifndef NDEBUG + int key_start; + int key_end; +#endif + bool is_static; +}; + +static Provider base_provider = { + base::PathProvider, + NULL, +#ifndef NDEBUG + base::PATH_START, + base::PATH_END, +#endif + true +}; + +#ifdef OS_WIN +static Provider base_provider_win = { + base::PathProviderWin, + &base_provider, +#ifndef NDEBUG + base::PATH_WIN_START, + base::PATH_WIN_END, +#endif + true +}; +#endif + +#ifdef OS_MACOSX +static Provider base_provider_mac = { + base::PathProviderMac, + &base_provider, +#ifndef NDEBUG + base::PATH_MAC_START, + base::PATH_MAC_END, +#endif + true +}; +#endif + +#if defined(OS_LINUX) +static Provider base_provider_linux = { + base::PathProviderLinux, + &base_provider, +#ifndef NDEBUG + base::PATH_LINUX_START, + base::PATH_LINUX_END, +#endif + true +}; +#endif + + +struct PathData { + Lock lock; + PathMap cache; // Track mappings from path key to path value. + PathSet overrides; // Track whether a path has been overridden. + Provider* providers; // Linked list of path service providers. + + PathData() { +#if defined(OS_WIN) + providers = &base_provider_win; +#elif defined(OS_MACOSX) + providers = &base_provider_mac; +#elif defined(OS_LINUX) + providers = &base_provider_linux; +#endif + } + + ~PathData() { + Provider* p = providers; + while (p) { + Provider* next = p->next; + if (!p->is_static) + delete p; + p = next; + } + } +}; + +static PathData* GetPathData() { + return Singleton::get(); +} + +} // namespace + + +// static +bool PathService::GetFromCache(int key, FilePath* result) { + PathData* path_data = GetPathData(); + AutoLock scoped_lock(path_data->lock); + + // check for a cached version + PathMap::const_iterator it = path_data->cache.find(key); + if (it != path_data->cache.end()) { + *result = it->second; + return true; + } + return false; +} + +// static +void PathService::AddToCache(int key, const FilePath& path) { + PathData* path_data = GetPathData(); + AutoLock scoped_lock(path_data->lock); + // Save the computed path in our cache. + path_data->cache[key] = path; +} + +// TODO(brettw): this function does not handle long paths (filename > MAX_PATH) +// characters). This isn't supported very well by Windows right now, so it is +// moot, but we should keep this in mind for the future. +// static +bool PathService::Get(int key, FilePath* result) { + PathData* path_data = GetPathData(); + DCHECK(path_data); + DCHECK(result); + DCHECK(key >= base::DIR_CURRENT); + + // special case the current directory because it can never be cached + if (key == base::DIR_CURRENT) + return file_util::GetCurrentDirectory(result); + + if (GetFromCache(key, result)) + return true; + + FilePath path; + + // search providers for the requested path + // NOTE: it should be safe to iterate here without the lock + // since RegisterProvider always prepends. + Provider* provider = path_data->providers; + while (provider) { + if (provider->func(key, &path)) + break; + DCHECK(path.empty()) << "provider should not have modified path"; + provider = provider->next; + } + + if (path.empty()) + return false; + + AddToCache(key, path); + + *result = path; + return true; +} + +// static +bool PathService::Get(int key, std::wstring* result) { + // Deprecated compatibility function. + FilePath path; + if (!Get(key, &path)) + return false; + *result = path.ToWStringHack(); + return true; +} + +bool PathService::IsOverridden(int key) { + PathData* path_data = GetPathData(); + DCHECK(path_data); + + AutoLock scoped_lock(path_data->lock); + return path_data->overrides.find(key) != path_data->overrides.end(); +} + +bool PathService::Override(int key, const std::wstring& path) { + PathData* path_data = GetPathData(); + DCHECK(path_data); + DCHECK(key > base::DIR_CURRENT) << "invalid path key"; + + std::wstring file_path = path; +#if defined(OS_WIN) + // On Windows we switch the current working directory to load plugins (at + // least). That's not the case on POSIX. + // Also, on POSIX, AbsolutePath fails if called on a non-existant path. + if (!file_util::AbsolutePath(&file_path)) + return false; +#endif + + // make sure the directory exists: + if (!file_util::CreateDirectory(file_path)) + return false; + + file_util::TrimTrailingSeparator(&file_path); + + AutoLock scoped_lock(path_data->lock); + path_data->cache[key] = FilePath::FromWStringHack(file_path); + path_data->overrides.insert(key); + return true; +} + +bool PathService::SetCurrentDirectory(const std::wstring& current_directory) { + return file_util::SetCurrentDirectory(current_directory); +} + +void PathService::RegisterProvider(ProviderFunc func, int key_start, + int key_end) { + PathData* path_data = GetPathData(); + DCHECK(path_data); + DCHECK(key_end > key_start); + + AutoLock scoped_lock(path_data->lock); + + Provider* p; + +#ifndef NDEBUG + p = path_data->providers; + while (p) { + DCHECK(key_start >= p->key_end || key_end <= p->key_start) << + "path provider collision"; + p = p->next; + } +#endif + + p = new Provider; + p->is_static = false; + p->func = func; + p->next = path_data->providers; +#ifndef NDEBUG + p->key_start = key_start; + p->key_end = key_end; +#endif + path_data->providers = p; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/path_service.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/path_service.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/path_service.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/path_service.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,80 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_PATH_SERVICE_H__ +#define BASE_PATH_SERVICE_H__ + +#include "build/build_config.h" +#ifdef OS_WIN +// TODO(erikkay): this should be removable, but because SetCurrentDirectory +// is the name of a Windows function, it gets macro-ized to SetCurrentDirectoryW +// by windows.h, which leads to a different name in the header vs. the impl. +// Even if we could fix that, it would still hose all callers of the function. +// The right thing is likely to rename. +#include +#endif + +#include + +#include "base/base_paths.h" + +class FilePath; + +// The path service is a global table mapping keys to file system paths. It is +// OK to use this service from multiple threads. +// +class PathService { + public: + // Retrieves a path to a special directory or file and places it into the + // string pointed to by 'path'. If you ask for a directory it is guaranteed + // to NOT have a path separator at the end. For example, "c:\windows\temp" + // Directories are also guaranteed to exist when this function succeeds. + // + // Returns true if the directory or file was successfully retrieved. On + // failure, 'path' will not be changed. + static bool Get(int key, FilePath* path); + // This version, producing a wstring, is deprecated and only kept around + // until we can fix all callers. + static bool Get(int key, std::wstring* path); + + // Overrides the path to a special directory or file. This cannot be used to + // change the value of DIR_CURRENT, but that should be obvious. Also, if the + // path specifies a directory that does not exist, the directory will be + // created by this method. This method returns true if successful. + // + // If the given path is relative, then it will be resolved against + // DIR_CURRENT. + // + // WARNING: Consumers of PathService::Get may expect paths to be constant + // over the lifetime of the app, so this method should be used with caution. + static bool Override(int key, const std::wstring& path); + + // Return whether a path was overridden. + static bool IsOverridden(int key); + + // Sets the current directory. + static bool SetCurrentDirectory(const std::wstring& current_directory); + + // To extend the set of supported keys, you can register a path provider, + // which is just a function mirroring PathService::Get. The ProviderFunc + // returns false if it cannot provide a non-empty path for the given key. + // Otherwise, true is returned. + // + // WARNING: This function could be called on any thread from which the + // PathService is used, so a the ProviderFunc MUST BE THREADSAFE. + // + typedef bool (*ProviderFunc)(int, FilePath*); + + // Call to register a path provider. You must specify the range "[key_start, + // key_end)" of supported path keys. + static void RegisterProvider(ProviderFunc provider, + int key_start, + int key_end); + private: + static bool GetFromCache(int key, FilePath* path); + static void AddToCache(int key, const FilePath& path); + +}; + +#endif // BASE_PATH_SERVICE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/path_service_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/path_service_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/path_service_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/path_service_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,62 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/path_service.h" + +#include "base/basictypes.h" +#include "base/file_util.h" +#include "base/file_path.h" +#if defined(OS_WIN) +#include "base/win_util.h" +#endif +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gtest/include/gtest/gtest-spi.h" +#include "testing/platform_test.h" + +namespace { + +// Returns true if PathService::Get returns true and sets the path parameter +// to non-empty for the given PathService::DirType enumeration value. +bool ReturnsValidPath(int dir_type) { + FilePath path; + bool result = PathService::Get(dir_type, &path); + return result && !path.value().empty() && file_util::PathExists(path); +} + +#if defined(OS_WIN) +// Function to test DIR_LOCAL_APP_DATA_LOW on Windows XP. Make sure it fails. +bool ReturnsInvalidPath(int dir_type) { + std::wstring path; + bool result = PathService::Get(base::DIR_LOCAL_APP_DATA_LOW, &path); + return !result && path.empty(); +} +#endif + +} // namespace + +// On the Mac this winds up using some autoreleased objects, so we need to +// be a PlatformTest. +typedef PlatformTest PathServiceTest; + +// Test that all PathService::Get calls return a value and a true result +// in the development environment. (This test was created because a few +// later changes to Get broke the semantics of the function and yielded the +// correct value while returning false.) +TEST_F(PathServiceTest, Get) { + for (int key = base::DIR_CURRENT; key < base::PATH_END; ++key) { + EXPECT_PRED1(ReturnsValidPath, key); + } +#ifdef OS_WIN + for (int key = base::PATH_WIN_START + 1; key < base::PATH_WIN_END; ++key) { + if (key == base::DIR_LOCAL_APP_DATA_LOW && + win_util::GetWinVersion() < win_util::WINVERSION_VISTA) { + // DIR_LOCAL_APP_DATA_LOW is not supported prior Vista and is expected to + // fail. + EXPECT_TRUE(ReturnsInvalidPath(key)) << key; + } else { + EXPECT_TRUE(ReturnsValidPath(key)) << key; + } + } +#endif +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/pe_image.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/pe_image.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/pe_image.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/pe_image.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,542 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file implements PEImage, a generic class to manipulate PE files. +// This file was adapted from GreenBorder's Code. + +#include "base/pe_image.h" + +#ifdef _WIN64 +#error This code is not tested on x64. Please make sure all the base unit tests\ + pass before doing any real work. The current unit tests don't test the\ + differences between 32- and 64-bits implementations. Bugs may slip through.\ + You need to improve the coverage before continuing. +#endif + +// Structure to perform imports enumerations. +struct EnumAllImportsStorage { + PEImage::EnumImportsFunction callback; + PVOID cookie; +}; + +// Callback used to enumerate imports. See EnumImportChunksFunction. +bool ProcessImportChunk(const PEImage &image, LPCSTR module, + PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, PVOID cookie) { + EnumAllImportsStorage &storage = *reinterpret_cast( + cookie); + + return image.EnumOneImportChunk(storage.callback, module, name_table, iat, + storage.cookie); +} + +// Callback used to enumerate delay imports. See EnumDelayImportChunksFunction. +bool ProcessDelayImportChunk(const PEImage &image, + PImgDelayDescr delay_descriptor, + LPCSTR module, PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, PIMAGE_THUNK_DATA bound_iat, + PIMAGE_THUNK_DATA unload_iat, PVOID cookie) { + EnumAllImportsStorage &storage = *reinterpret_cast( + cookie); + + return image.EnumOneDelayImportChunk(storage.callback, delay_descriptor, + module, name_table, iat, bound_iat, + unload_iat, storage.cookie); +} + +void PEImage::set_module(HMODULE module) { + module_ = module; +} + +PIMAGE_DOS_HEADER PEImage::GetDosHeader() const { + return reinterpret_cast(module_); +} + +PIMAGE_NT_HEADERS PEImage::GetNTHeaders() const { + PIMAGE_DOS_HEADER dos_header = GetDosHeader(); + + return reinterpret_cast( + reinterpret_cast(dos_header) + dos_header->e_lfanew); +} + +PIMAGE_SECTION_HEADER PEImage::GetSectionHeader(UINT section) const { + PIMAGE_NT_HEADERS nt_headers = GetNTHeaders(); + PIMAGE_SECTION_HEADER first_section = IMAGE_FIRST_SECTION(nt_headers); + + if (section < nt_headers->FileHeader.NumberOfSections) + return first_section + section; + else + return NULL; +} + +WORD PEImage::GetNumSections() const { + return GetNTHeaders()->FileHeader.NumberOfSections; +} + +DWORD PEImage::GetImageDirectoryEntrySize(UINT directory) const { + PIMAGE_NT_HEADERS nt_headers = GetNTHeaders(); + + return nt_headers->OptionalHeader.DataDirectory[directory].Size; +} + +PVOID PEImage::GetImageDirectoryEntryAddr(UINT directory) const { + PIMAGE_NT_HEADERS nt_headers = GetNTHeaders(); + + return RVAToAddr( + nt_headers->OptionalHeader.DataDirectory[directory].VirtualAddress); +} + +PIMAGE_SECTION_HEADER PEImage::GetImageSectionFromAddr(PVOID address) const { + PBYTE target = reinterpret_cast(address); + PIMAGE_SECTION_HEADER section; + + for (UINT i = 0; NULL != (section = GetSectionHeader(i)); i++) { + // Don't use the virtual RVAToAddr. + PBYTE start = reinterpret_cast( + PEImage::RVAToAddr(section->VirtualAddress)); + + DWORD size = section->Misc.VirtualSize; + + if ((start <= target) && (start + size > target)) + return section; + } + + return NULL; +} + +PIMAGE_SECTION_HEADER PEImage::GetImageSectionHeaderByName( + LPCSTR section_name) const { + if (NULL == section_name) + return NULL; + + PIMAGE_SECTION_HEADER ret = NULL; + int num_sections = GetNumSections(); + + for (int i = 0; i < num_sections; i++) { + PIMAGE_SECTION_HEADER section = GetSectionHeader(i); + if (0 == _strnicmp(reinterpret_cast(section->Name), section_name, + sizeof(section->Name))) { + ret = section; + break; + } + } + + return ret; +} + +PDWORD PEImage::GetExportEntry(LPCSTR name) const { + PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory(); + + if (NULL == exports) + return NULL; + + WORD ordinal = 0; + if (!GetProcOrdinal(name, &ordinal)) + return NULL; + + PDWORD functions = reinterpret_cast( + RVAToAddr(exports->AddressOfFunctions)); + + return functions + ordinal - exports->Base; +} + +FARPROC PEImage::GetProcAddress(LPCSTR function_name) const { + PDWORD export_entry = GetExportEntry(function_name); + if (NULL == export_entry) + return NULL; + + PBYTE function = reinterpret_cast(RVAToAddr(*export_entry)); + + PBYTE exports = reinterpret_cast( + GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT)); + DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT); + + // Check for forwarded exports as a special case. + if (exports <= function && exports + size > function) +#pragma warning(push) +#pragma warning(disable: 4312) + // This cast generates a warning because it is 32 bit specific. + return reinterpret_cast(0xFFFFFFFF); +#pragma warning(pop) + + return reinterpret_cast(function); +} + +bool PEImage::GetProcOrdinal(LPCSTR function_name, WORD *ordinal) const { + if (NULL == ordinal) + return false; + + PIMAGE_EXPORT_DIRECTORY exports = GetExportDirectory(); + + if (NULL == exports) + return false; + + if (IsOrdinal(function_name)) { + *ordinal = ToOrdinal(function_name); + } else { + PDWORD names = reinterpret_cast(RVAToAddr(exports->AddressOfNames)); + PDWORD lower = names; + PDWORD upper = names + exports->NumberOfNames; + int cmp = -1; + + // Binary Search for the name. + while (lower != upper) { + PDWORD middle = lower + (upper - lower) / 2; + LPCSTR name = reinterpret_cast(RVAToAddr(*middle)); + + cmp = strcmp(function_name, name); + + if (cmp == 0) { + lower = middle; + break; + } + + if (cmp > 0) + lower = middle + 1; + else + upper = middle; + } + + if (cmp != 0) + return false; + + + PWORD ordinals = reinterpret_cast( + RVAToAddr(exports->AddressOfNameOrdinals)); + + *ordinal = ordinals[lower - names] + static_cast(exports->Base); + } + + return true; +} + +bool PEImage::EnumSections(EnumSectionsFunction callback, PVOID cookie) const { + PIMAGE_NT_HEADERS nt_headers = GetNTHeaders(); + UINT num_sections = nt_headers->FileHeader.NumberOfSections; + PIMAGE_SECTION_HEADER section = GetSectionHeader(0); + + for (UINT i = 0; i < num_sections; i++, section++) { + PVOID section_start = RVAToAddr(section->VirtualAddress); + DWORD size = section->Misc.VirtualSize; + + if (!callback(*this, section, section_start, size, cookie)) + return false; + } + + return true; +} + +bool PEImage::EnumExports(EnumExportsFunction callback, PVOID cookie) const { + PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT); + DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_EXPORT); + + // Check if there are any exports at all. + if (NULL == directory || 0 == size) + return true; + + PIMAGE_EXPORT_DIRECTORY exports = reinterpret_cast( + directory); + UINT ordinal_base = exports->Base; + UINT num_funcs = exports->NumberOfFunctions; + UINT num_names = exports->NumberOfNames; + PDWORD functions = reinterpret_cast(RVAToAddr( + exports->AddressOfFunctions)); + PDWORD names = reinterpret_cast(RVAToAddr(exports->AddressOfNames)); + PWORD ordinals = reinterpret_cast(RVAToAddr( + exports->AddressOfNameOrdinals)); + + for (UINT count = 0; count < num_funcs; count++) { + PVOID func = RVAToAddr(functions[count]); + if (NULL == func) + continue; + + // Check for a name. + LPCSTR name = NULL; + UINT hint; + for (hint = 0; hint < num_names; hint++) { + if (ordinals[hint] == count) { + name = reinterpret_cast(RVAToAddr(names[hint])); + break; + } + } + + if (name == NULL) + hint = 0; + + // Check for forwarded exports. + LPCSTR forward = NULL; + if (reinterpret_cast(func) >= reinterpret_cast(directory) && + reinterpret_cast(func) <= reinterpret_cast(directory) + + size) { + forward = reinterpret_cast(func); + func = 0; + } + + if (!callback(*this, ordinal_base + count, hint, name, func, forward, + cookie)) + return false; + } + + return true; +} + +bool PEImage::EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const { + PVOID directory = GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_BASERELOC); + DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_BASERELOC); + PIMAGE_BASE_RELOCATION base = reinterpret_cast( + directory); + + if (directory == NULL || size < sizeof(IMAGE_BASE_RELOCATION)) + return true; + + while (base->SizeOfBlock) { + PWORD reloc = reinterpret_cast(base + 1); + UINT num_relocs = (base->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / + sizeof(WORD); + + for (UINT i = 0; i < num_relocs; i++, reloc++) { + WORD type = *reloc >> 12; + PVOID address = RVAToAddr(base->VirtualAddress + (*reloc & 0x0FFF)); + + if (!callback(*this, type, address, cookie)) + return false; + } + + base = reinterpret_cast( + reinterpret_cast(base) + base->SizeOfBlock); + } + + return true; +} + +bool PEImage::EnumImportChunks(EnumImportChunksFunction callback, + PVOID cookie) const { + DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_IMPORT); + PIMAGE_IMPORT_DESCRIPTOR import = GetFirstImportChunk(); + + if (import == NULL || size < sizeof(IMAGE_IMPORT_DESCRIPTOR)) + return true; + + for (; import->FirstThunk; import++) { + LPCSTR module_name = reinterpret_cast(RVAToAddr(import->Name)); + PIMAGE_THUNK_DATA name_table = reinterpret_cast( + RVAToAddr(import->OriginalFirstThunk)); + PIMAGE_THUNK_DATA iat = reinterpret_cast( + RVAToAddr(import->FirstThunk)); + + if (!callback(*this, module_name, name_table, iat, cookie)) + return false; + } + + return true; +} + +bool PEImage::EnumOneImportChunk(EnumImportsFunction callback, + LPCSTR module_name, + PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, PVOID cookie) const { + if (NULL == name_table) + return false; + + for (; name_table && name_table->u1.Ordinal; name_table++, iat++) { + LPCSTR name = NULL; + WORD ordinal = 0; + WORD hint = 0; + + if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) { + ordinal = static_cast(IMAGE_ORDINAL32(name_table->u1.Ordinal)); + } else { + PIMAGE_IMPORT_BY_NAME import = reinterpret_cast( + RVAToAddr(name_table->u1.ForwarderString)); + + hint = import->Hint; + name = reinterpret_cast(&import->Name); + } + + if (!callback(*this, module_name, ordinal, name, hint, iat, cookie)) + return false; + } + + return true; +} + +bool PEImage::EnumAllImports(EnumImportsFunction callback, PVOID cookie) const { + EnumAllImportsStorage temp = { callback, cookie }; + return EnumImportChunks(ProcessImportChunk, &temp); +} + +bool PEImage::EnumDelayImportChunks(EnumDelayImportChunksFunction callback, + PVOID cookie) const { + PVOID directory = GetImageDirectoryEntryAddr( + IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT); + DWORD size = GetImageDirectoryEntrySize(IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT); + PImgDelayDescr delay_descriptor = reinterpret_cast(directory); + + if (directory == NULL || size == 0) + return true; + + for (; delay_descriptor->rvaHmod; delay_descriptor++) { + PIMAGE_THUNK_DATA name_table; + PIMAGE_THUNK_DATA iat; + PIMAGE_THUNK_DATA bound_iat; // address of the optional bound IAT + PIMAGE_THUNK_DATA unload_iat; // address of optional copy of original IAT + LPCSTR module_name; + + // check if VC7-style imports, using RVAs instead of + // VC6-style addresses. + bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0; + + if (rvas) { + module_name = reinterpret_cast( + RVAToAddr(delay_descriptor->rvaDLLName)); + name_table = reinterpret_cast( + RVAToAddr(delay_descriptor->rvaINT)); + iat = reinterpret_cast( + RVAToAddr(delay_descriptor->rvaIAT)); + bound_iat = reinterpret_cast( + RVAToAddr(delay_descriptor->rvaBoundIAT)); + unload_iat = reinterpret_cast( + RVAToAddr(delay_descriptor->rvaUnloadIAT)); + } else { +#pragma warning(push) +#pragma warning(disable: 4312) + // These casts generate warnings because they are 32 bit specific. + module_name = reinterpret_cast(delay_descriptor->rvaDLLName); + name_table = reinterpret_cast( + delay_descriptor->rvaINT); + iat = reinterpret_cast(delay_descriptor->rvaIAT); + bound_iat = reinterpret_cast( + delay_descriptor->rvaBoundIAT); + unload_iat = reinterpret_cast( + delay_descriptor->rvaUnloadIAT); +#pragma warning(pop) + } + + if (!callback(*this, delay_descriptor, module_name, name_table, iat, + bound_iat, unload_iat, cookie)) + return false; + } + + return true; +} + +bool PEImage::EnumOneDelayImportChunk(EnumImportsFunction callback, + PImgDelayDescr delay_descriptor, + LPCSTR module_name, + PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, + PIMAGE_THUNK_DATA bound_iat, + PIMAGE_THUNK_DATA unload_iat, + PVOID cookie) const { + UNREFERENCED_PARAMETER(bound_iat); + UNREFERENCED_PARAMETER(unload_iat); + + for (; name_table->u1.Ordinal; name_table++, iat++) { + LPCSTR name = NULL; + WORD ordinal = 0; + WORD hint = 0; + + if (IMAGE_SNAP_BY_ORDINAL(name_table->u1.Ordinal)) { + ordinal = static_cast(IMAGE_ORDINAL32(name_table->u1.Ordinal)); + } else { + PIMAGE_IMPORT_BY_NAME import; + bool rvas = (delay_descriptor->grAttrs & dlattrRva) != 0; + + if (rvas) { + import = reinterpret_cast( + RVAToAddr(name_table->u1.ForwarderString)); + } else { +#pragma warning(push) +#pragma warning(disable: 4312) + // This cast generates a warning because it is 32 bit specific. + import = reinterpret_cast( + name_table->u1.ForwarderString); +#pragma warning(pop) + } + + hint = import->Hint; + name = reinterpret_cast(&import->Name); + } + + if (!callback(*this, module_name, ordinal, name, hint, iat, cookie)) + return false; + } + + return true; +} + +bool PEImage::EnumAllDelayImports(EnumImportsFunction callback, + PVOID cookie) const { + EnumAllImportsStorage temp = { callback, cookie }; + return EnumDelayImportChunks(ProcessDelayImportChunk, &temp); +} + +bool PEImage::VerifyMagic() const { + PIMAGE_DOS_HEADER dos_header = GetDosHeader(); + + if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) + return false; + + PIMAGE_NT_HEADERS nt_headers = GetNTHeaders(); + + if (nt_headers->Signature != IMAGE_NT_SIGNATURE) + return false; + + if (nt_headers->FileHeader.SizeOfOptionalHeader != + sizeof(IMAGE_OPTIONAL_HEADER)) + return false; + + if (nt_headers->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) + return false; + + return true; +} + +bool PEImage::ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const { + LPVOID address = RVAToAddr(rva); + return ImageAddrToOnDiskOffset(address, on_disk_offset); +} + +bool PEImage::ImageAddrToOnDiskOffset(LPVOID address, + DWORD *on_disk_offset) const { + if (NULL == address) + return false; + + // Get the section that this address belongs to. + PIMAGE_SECTION_HEADER section_header = GetImageSectionFromAddr(address); + if (NULL == section_header) + return false; + +#pragma warning(push) +#pragma warning(disable: 4311) + // These casts generate warnings because they are 32 bit specific. + // Don't follow the virtual RVAToAddr, use the one on the base. + DWORD offset_within_section = reinterpret_cast(address) - + reinterpret_cast(PEImage::RVAToAddr( + section_header->VirtualAddress)); +#pragma warning(pop) + + *on_disk_offset = section_header->PointerToRawData + offset_within_section; + return true; +} + +PVOID PEImage::RVAToAddr(DWORD_PTR rva) const { + if (rva == 0) + return NULL; + + return reinterpret_cast(module_) + rva; +} + +PVOID PEImageAsData::RVAToAddr(DWORD_PTR rva) const { + if (rva == 0) + return NULL; + + PVOID in_memory = PEImage::RVAToAddr(rva); + DWORD dummy; + + if (!ImageAddrToOnDiskOffset(in_memory, &dummy)) + return NULL; + + return in_memory; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/pe_image.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/pe_image.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/pe_image.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/pe_image.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,257 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file was adapted from GreenBorder's Code. +// To understand what this class is about (for other than well known functions +// as GetProcAddress), a good starting point is "An In-Depth Look into the +// Win32 Portable Executable File Format" by Matt Pietrek: +// http://msdn.microsoft.com/msdnmag/issues/02/02/PE/default.aspx + +#ifndef BASE_PE_IMAGE_H_ +#define BASE_PE_IMAGE_H_ + +#include +#include + +// This class is a wrapper for the Portable Executable File Format (PE). +// It's main purpose is to provide an easy way to work with imports and exports +// from a file, mapped in memory as image. +class PEImage { + public: + // Callback to enumerate sections. + // cookie is the value passed to the enumerate method. + // Returns true to continue the enumeration. + typedef bool (*EnumSectionsFunction)(const PEImage &image, + PIMAGE_SECTION_HEADER header, + PVOID section_start, DWORD section_size, + PVOID cookie); + + // Callback to enumerate exports. + // function is the actual address of the symbol. If forward is not null, it + // contains the dll and symbol to forward this export to. cookie is the value + // passed to the enumerate method. + // Returns true to continue the enumeration. + typedef bool (*EnumExportsFunction)(const PEImage &image, DWORD ordinal, + DWORD hint, LPCSTR name, PVOID function, + LPCSTR forward, PVOID cookie); + + // Callback to enumerate import blocks. + // name_table and iat point to the imports name table and address table for + // this block. cookie is the value passed to the enumerate method. + // Returns true to continue the enumeration. + typedef bool (*EnumImportChunksFunction)(const PEImage &image, LPCSTR module, + PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, PVOID cookie); + + // Callback to enumerate imports. + // module is the dll that exports this symbol. cookie is the value passed to + // the enumerate method. + // Returns true to continue the enumeration. + typedef bool (*EnumImportsFunction)(const PEImage &image, LPCSTR module, + DWORD ordinal, LPCSTR name, DWORD hint, + PIMAGE_THUNK_DATA iat, PVOID cookie); + + // Callback to enumerate dalayed import blocks. + // module is the dll that exports this block of symbols. cookie is the value + // passed to the enumerate method. + // Returns true to continue the enumeration. + typedef bool (*EnumDelayImportChunksFunction)(const PEImage &image, + PImgDelayDescr delay_descriptor, + LPCSTR module, + PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, + PIMAGE_THUNK_DATA bound_iat, + PIMAGE_THUNK_DATA unload_iat, + PVOID cookie); + + // Callback to enumerate relocations. + // cookie is the value passed to the enumerate method. + // Returns true to continue the enumeration. + typedef bool (*EnumRelocsFunction)(const PEImage &image, WORD type, + PVOID address, PVOID cookie); + + explicit PEImage(HMODULE module) : module_(module) {} + explicit PEImage(const void* module) { + module_ = reinterpret_cast(const_cast(module)); + } + + // Gets the HMODULE for this object. + HMODULE module() const; + + // Sets this object's HMODULE. + void set_module(HMODULE module); + + // Checks if this symbol is actually an ordinal. + static bool IsOrdinal(LPCSTR name); + + // Converts a named symbol to the corresponding ordinal. + static WORD ToOrdinal(LPCSTR name); + + // Returns the DOS_HEADER for this PE. + PIMAGE_DOS_HEADER GetDosHeader() const; + + // Returns the NT_HEADER for this PE. + PIMAGE_NT_HEADERS GetNTHeaders() const; + + // Returns number of sections of this PE. + WORD GetNumSections() const; + + // Returns the header for a given section. + // returns NULL if there is no such section. + PIMAGE_SECTION_HEADER GetSectionHeader(UINT section) const; + + // Returns the size of a given directory entry. + DWORD GetImageDirectoryEntrySize(UINT directory) const; + + // Returns the address of a given directory entry. + PVOID GetImageDirectoryEntryAddr(UINT directory) const; + + // Returns the section header for a given address. + // Use: s = image.GetImageSectionFromAddr(a); + // Post: 's' is the section header of the section that contains 'a' + // or NULL if there is no such section. + PIMAGE_SECTION_HEADER GetImageSectionFromAddr(PVOID address) const; + + // Returns the section header for a given section. + PIMAGE_SECTION_HEADER GetImageSectionHeaderByName(LPCSTR section_name) const; + + // Returns the first block of imports. + PIMAGE_IMPORT_DESCRIPTOR GetFirstImportChunk() const; + + // Returns the exports directory. + PIMAGE_EXPORT_DIRECTORY GetExportDirectory() const; + + // Returns a given export entry. + // Use: e = image.GetExportEntry(f); + // Pre: 'f' is either a zero terminated string or ordinal + // Post: 'e' is a pointer to the export directory entry + // that contains 'f's export RVA, or NULL if 'f' + // is not exported from this image + PDWORD GetExportEntry(LPCSTR name) const; + + // Returns the address for a given exported symbol. + // Use: p = image.GetProcAddress(f); + // Pre: 'f' is either a zero terminated string or ordinal. + // Post: if 'f' is a non-forwarded export from image, 'p' is + // the exported function. If 'f' is a forwarded export + // then p is the special value 0xFFFFFFFF. In this case + // RVAToAddr(*GetExportEntry) can be used to resolve + // the string that describes the forward. + FARPROC GetProcAddress(LPCSTR function_name) const; + + // Retrieves the ordinal for a given exported symbol. + // Returns true if the symbol was found. + bool GetProcOrdinal(LPCSTR function_name, WORD *ordinal) const; + + // Enumerates PE sections. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + bool EnumSections(EnumSectionsFunction callback, PVOID cookie) const; + + // Enumerates PE exports. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + bool EnumExports(EnumExportsFunction callback, PVOID cookie) const; + + // Enumerates PE imports. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + bool EnumAllImports(EnumImportsFunction callback, PVOID cookie) const; + + // Enumerates PE import blocks. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + bool EnumImportChunks(EnumImportChunksFunction callback, PVOID cookie) const; + + // Enumerates the imports from a single PE import block. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + bool EnumOneImportChunk(EnumImportsFunction callback, LPCSTR module_name, + PIMAGE_THUNK_DATA name_table, PIMAGE_THUNK_DATA iat, + PVOID cookie) const; + + + // Enumerates PE delay imports. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + bool EnumAllDelayImports(EnumImportsFunction callback, PVOID cookie) const; + + // Enumerates PE delay import blocks. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + bool EnumDelayImportChunks(EnumDelayImportChunksFunction callback, + PVOID cookie) const; + + // Enumerates imports from a single PE delay import block. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + bool EnumOneDelayImportChunk(EnumImportsFunction callback, + PImgDelayDescr delay_descriptor, + LPCSTR module_name, + PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, + PIMAGE_THUNK_DATA bound_iat, + PIMAGE_THUNK_DATA unload_iat, + PVOID cookie) const; + + // Enumerates PE relocation entries. + // cookie is a generic cookie to pass to the callback. + // Returns true on success. + bool EnumRelocs(EnumRelocsFunction callback, PVOID cookie) const; + + // Verifies the magic values on the PE file. + // Returns true if all values are correct. + bool VerifyMagic() const; + + // Converts an rva value to the appropriate address. + virtual PVOID RVAToAddr(DWORD_PTR rva) const; + + // Converts an rva value to an offset on disk. + // Returns true on success. + bool ImageRVAToOnDiskOffset(DWORD rva, DWORD *on_disk_offset) const; + + // Converts an address to an offset on disk. + // Returns true on success. + bool ImageAddrToOnDiskOffset(LPVOID address, DWORD *on_disk_offset) const; + + private: + HMODULE module_; +}; + +// This class is an extension to the PEImage class that allows working with PE +// files mapped as data instead of as image file. +class PEImageAsData : public PEImage { + public: + explicit PEImageAsData(HMODULE hModule) : PEImage(hModule) {} + + virtual PVOID RVAToAddr(DWORD_PTR rva) const; +}; + +inline bool PEImage::IsOrdinal(LPCSTR name) { +#pragma warning(push) +#pragma warning(disable: 4311) + // This cast generates a warning because it is 32 bit specific. + return reinterpret_cast(name) <= 0xFFFF; +#pragma warning(pop) +} + +inline WORD PEImage::ToOrdinal(LPCSTR name) { + return reinterpret_cast(name); +} + +inline HMODULE PEImage::module() const { + return module_; +} + +inline PIMAGE_IMPORT_DESCRIPTOR PEImage::GetFirstImportChunk() const { + return reinterpret_cast( + GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_IMPORT)); +} + +inline PIMAGE_EXPORT_DIRECTORY PEImage::GetExportDirectory() const { + return reinterpret_cast( + GetImageDirectoryEntryAddr(IMAGE_DIRECTORY_ENTRY_EXPORT)); +} + +#endif // BASE_PE_IMAGE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/pe_image_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/pe_image_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/pe_image_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/pe_image_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,205 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains unit tests for PEImage. + +#include "testing/gtest/include/gtest/gtest.h" +#include "base/pe_image.h" +#include "base/win_util.h" + +// Just counts the number of invocations. +bool ExportsCallback(const PEImage &image, + DWORD ordinal, + DWORD hint, + LPCSTR name, + PVOID function, + LPCSTR forward, + PVOID cookie) { + int* count = reinterpret_cast(cookie); + (*count)++; + return true; +} + +// Just counts the number of invocations. +bool ImportsCallback(const PEImage &image, + LPCSTR module, + DWORD ordinal, + LPCSTR name, + DWORD hint, + PIMAGE_THUNK_DATA iat, + PVOID cookie) { + int* count = reinterpret_cast(cookie); + (*count)++; + return true; +} + +// Just counts the number of invocations. +bool SectionsCallback(const PEImage &image, + PIMAGE_SECTION_HEADER header, + PVOID section_start, + DWORD section_size, + PVOID cookie) { + int* count = reinterpret_cast(cookie); + (*count)++; + return true; +} + +// Just counts the number of invocations. +bool RelocsCallback(const PEImage &image, + WORD type, + PVOID address, + PVOID cookie) { + int* count = reinterpret_cast(cookie); + (*count)++; + return true; +} + +// Just counts the number of invocations. +bool ImportChunksCallback(const PEImage &image, + LPCSTR module, + PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, + PVOID cookie) { + int* count = reinterpret_cast(cookie); + (*count)++; + return true; +} + +// Just counts the number of invocations. +bool DelayImportChunksCallback(const PEImage &image, + PImgDelayDescr delay_descriptor, + LPCSTR module, + PIMAGE_THUNK_DATA name_table, + PIMAGE_THUNK_DATA iat, + PIMAGE_THUNK_DATA bound_iat, + PIMAGE_THUNK_DATA unload_iat, + PVOID cookie) { + int* count = reinterpret_cast(cookie); + (*count)++; + return true; +} + +// We'll be using some known values for the tests. +enum Value { + sections = 0, + imports_dlls, + delay_dlls, + exports, + imports, + delay_imports, + relocs +}; + +// Retrieves the expected value from advapi32.dll based on the OS. +int GetExpectedValue(Value value, DWORD os) { + const int xp_delay_dlls = 2; + const int xp_exports = 675; + const int xp_imports = 422; + const int xp_delay_imports = 8; + const int xp_relocs = 9180; + const int vista_delay_dlls = 4; + const int vista_exports = 799; + const int vista_imports = 476; + const int vista_delay_imports = 24; + const int vista_relocs = 10188; + const int w2k_delay_dlls = 0; + const int w2k_exports = 566; + const int w2k_imports = 357; + const int w2k_delay_imports = 0; + const int w2k_relocs = 7388; + + // Contains the expected value, for each enumerated property (Value), and the + // OS version: [Value][os_version] + const int expected[][3] = { + {4, 4, 4}, + {3, 3, 3}, + {w2k_delay_dlls, xp_delay_dlls, vista_delay_dlls}, + {w2k_exports, xp_exports, vista_exports}, + {w2k_imports, xp_imports, vista_imports}, + {w2k_delay_imports, xp_delay_imports, vista_delay_imports}, + {w2k_relocs, xp_relocs, vista_relocs} + }; + + if (value > relocs) + return 0; + if (50 == os) + os = 0; // 5.0 + else if (51 == os || 52 == os) + os = 1; + else if (os >= 60) + os = 2; // 6.x + else + return 0; + + return expected[value][os]; +} + +// Tests that we are able to enumerate stuff from a PE file, and that +// the actual number of items found is within the expected range. +TEST(PEImageTest, EnumeratesPE) { + // Windows Server 2003 is not supported as a test environment for this test. + if (win_util::GetWinVersion() == win_util::WINVERSION_SERVER_2003) + return; + HMODULE module = LoadLibrary(L"advapi32.dll"); + ASSERT_TRUE(NULL != module); + + PEImage pe(module); + int count = 0; + EXPECT_TRUE(pe.VerifyMagic()); + + DWORD os = pe.GetNTHeaders()->OptionalHeader.MajorOperatingSystemVersion; + os = os * 10 + pe.GetNTHeaders()->OptionalHeader.MinorOperatingSystemVersion; + + pe.EnumSections(SectionsCallback, &count); + EXPECT_EQ(GetExpectedValue(sections, os), count); + + count = 0; + pe.EnumImportChunks(ImportChunksCallback, &count); + EXPECT_EQ(GetExpectedValue(imports_dlls, os), count); + + count = 0; + pe.EnumDelayImportChunks(DelayImportChunksCallback, &count); + EXPECT_EQ(GetExpectedValue(delay_dlls, os), count); + + count = 0; + pe.EnumExports(ExportsCallback, &count); + EXPECT_GT(count, GetExpectedValue(exports, os) - 20); + EXPECT_LT(count, GetExpectedValue(exports, os) + 100); + + count = 0; + pe.EnumAllImports(ImportsCallback, &count); + EXPECT_GT(count, GetExpectedValue(imports, os) - 20); + EXPECT_LT(count, GetExpectedValue(imports, os) + 100); + + count = 0; + pe.EnumAllDelayImports(ImportsCallback, &count); + EXPECT_GT(count, GetExpectedValue(delay_imports, os) - 2); + EXPECT_LT(count, GetExpectedValue(delay_imports, os) + 8); + + count = 0; + pe.EnumRelocs(RelocsCallback, &count); + EXPECT_GT(count, GetExpectedValue(relocs, os) - 150); + EXPECT_LT(count, GetExpectedValue(relocs, os) + 1500); + + FreeLibrary(module); +} + +// Tests that we can locate an specific exported symbol, by name and by ordinal. +TEST(PEImageTest, RetrievesExports) { + HMODULE module = LoadLibrary(L"advapi32.dll"); + ASSERT_TRUE(NULL != module); + + PEImage pe(module); + WORD ordinal; + + EXPECT_TRUE(pe.GetProcOrdinal("RegEnumKeyExW", &ordinal)); + + FARPROC address1 = pe.GetProcAddress("RegEnumKeyExW"); + FARPROC address2 = pe.GetProcAddress(reinterpret_cast(ordinal)); + EXPECT_TRUE(address1 != NULL); + EXPECT_TRUE(address2 != NULL); + EXPECT_TRUE(address1 == address2); + + FreeLibrary(module); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/perf_test_suite.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/perf_test_suite.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/perf_test_suite.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/perf_test_suite.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,51 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_PERF_TEST_SUITE_H_ +#define BASE_PERF_TEST_SUITE_H_ + +#include "base/command_line.h" +#include "base/debug_util.h" +#include "base/file_path.h" +#include "base/perftimer.h" +#include "base/process_util.h" +#include "base/string_util.h" +#include "base/test_suite.h" + +class PerfTestSuite : public TestSuite { + public: + PerfTestSuite(int argc, char** argv) : TestSuite(argc, argv) { + } + + virtual void Initialize() { + TestSuite::Initialize(); + + // Initialize the perf timer log + FilePath log_path; + std::wstring log_file = + CommandLine::ForCurrentProcess()->GetSwitchValue(L"log-file"); + if (log_file.empty()) { + FilePath exe; + PathService::Get(base::FILE_EXE, &exe); + log_path = exe.ReplaceExtension(FILE_PATH_LITERAL("log")); + log_path = log_path.InsertBeforeExtension(FILE_PATH_LITERAL("_perf")); + } else { + log_path = FilePath::FromWStringHack(log_file); + } + ASSERT_TRUE(InitPerfLog(log_path)); + + // Raise to high priority to have more precise measurements. Since we don't + // aim at 1% precision, it is not necessary to run at realtime level. + if (!DebugUtil::BeingDebugged()) + base::RaiseProcessToHighPriority(); + } + + virtual void Shutdown() { + TestSuite::Shutdown(); + + FinalizePerfLog(); + } +}; + +#endif // BASE_PERF_TEST_SUITE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/perftimer.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/perftimer.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/perftimer.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/perftimer.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,45 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/perftimer.h" + +#include +#include + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" + +static FILE* perf_log_file = NULL; + +bool InitPerfLog(const FilePath& log_file) { + if (perf_log_file) { + // trying to initialize twice + NOTREACHED(); + return false; + } + + perf_log_file = file_util::OpenFile(log_file, "w"); + return perf_log_file != NULL; +} + +void FinalizePerfLog() { + if (!perf_log_file) { + // trying to cleanup without initializing + NOTREACHED(); + return; + } + file_util::CloseFile(perf_log_file); +} + +void LogPerfResult(const char* test_name, double value, const char* units) { + if (!perf_log_file) { + NOTREACHED(); + return; + } + + fprintf(perf_log_file, "%s\t%g\t%s\n", test_name, value, units); + printf("%s\t%g\t%s\n", test_name, value, units); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/perftimer.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/perftimer.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/perftimer.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/perftimer.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,81 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_PERFTIMER_H_ +#define BASE_PERFTIMER_H_ + +#include +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/time.h" + +// ---------------------------------------------------------------------- +// Initializes and finalizes the perf log. These functions should be +// called at the beginning and end (respectively) of running all the +// performance tests. The init function returns true on success. +// ---------------------------------------------------------------------- +bool InitPerfLog(const FilePath& log_path); +void FinalizePerfLog(); + +// ---------------------------------------------------------------------- +// LogPerfResult +// Writes to the perf result log the given 'value' resulting from the +// named 'test'. The units are to aid in reading the log by people. +// ---------------------------------------------------------------------- +void LogPerfResult(const char* test_name, double value, const char* units); + +// ---------------------------------------------------------------------- +// PerfTimer +// A simple wrapper around Now() +// ---------------------------------------------------------------------- +class PerfTimer { + public: + PerfTimer() { + begin_ = base::TimeTicks::Now(); + } + + // Returns the time elapsed since object construction + base::TimeDelta Elapsed() const { + return base::TimeTicks::Now() - begin_; + } + + private: + base::TimeTicks begin_; +}; + +// ---------------------------------------------------------------------- +// PerfTimeLogger +// Automates calling LogPerfResult for the common case where you want +// to measure the time that something took. Call Done() when the test +// is complete if you do extra work after the test or there are stack +// objects with potentially expensive constructors. Otherwise, this +// class with automatically log on destruction. +// ---------------------------------------------------------------------- +class PerfTimeLogger { + public: + explicit PerfTimeLogger(const char* test_name) + : logged_(false), + test_name_(test_name) { + } + + ~PerfTimeLogger() { + if (!logged_) + Done(); + } + + void Done() { + // we use a floating-point millisecond value because it is more + // intuitive than microseconds and we want more precision than + // integer milliseconds + LogPerfResult(test_name_.c_str(), timer_.Elapsed().InMillisecondsF(), "ms"); + logged_ = true; + } + + private: + bool logged_; + std::string test_name_; + PerfTimer timer_; +}; + +#endif // BASE_PERFTIMER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/pickle.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/pickle.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/pickle.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/pickle.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,446 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/pickle.h" + +#include + +#include +#include + +//------------------------------------------------------------------------------ + +// static +const int Pickle::kPayloadUnit = 64; + +// We mark a read only pickle with a special capacity_. +static const size_t kCapacityReadOnly = std::numeric_limits::max(); + +// Payload is uint32 aligned. + +Pickle::Pickle() + : header_(NULL), + header_size_(sizeof(Header)), + capacity_(0), + variable_buffer_offset_(0) { + Resize(kPayloadUnit); + header_->payload_size = 0; +} + +Pickle::Pickle(int header_size) + : header_(NULL), + header_size_(AlignInt(header_size, sizeof(uint32))), + capacity_(0), + variable_buffer_offset_(0) { + DCHECK(static_cast(header_size) >= sizeof(Header)); + DCHECK(header_size <= kPayloadUnit); + Resize(kPayloadUnit); + header_->payload_size = 0; +} + +Pickle::Pickle(const char* data, int data_len) + : header_(reinterpret_cast(const_cast(data))), + header_size_(data_len - header_->payload_size), + capacity_(kCapacityReadOnly), + variable_buffer_offset_(0) { + DCHECK(header_size_ >= sizeof(Header)); + DCHECK(header_size_ == AlignInt(header_size_, sizeof(uint32))); +} + +Pickle::Pickle(const Pickle& other) + : header_(NULL), + header_size_(other.header_size_), + capacity_(0), + variable_buffer_offset_(other.variable_buffer_offset_) { + size_t payload_size = header_size_ + other.header_->payload_size; + bool resized = Resize(payload_size); + CHECK(resized); // Realloc failed. + memcpy(header_, other.header_, payload_size); +} + +Pickle::~Pickle() { + if (capacity_ != kCapacityReadOnly) + free(header_); +} + +Pickle& Pickle::operator=(const Pickle& other) { + if (header_size_ != other.header_size_ && capacity_ != kCapacityReadOnly) { + free(header_); + header_ = NULL; + header_size_ = other.header_size_; + } + bool resized = Resize(other.header_size_ + other.header_->payload_size); + CHECK(resized); // Realloc failed. + memcpy(header_, other.header_, header_size_ + other.header_->payload_size); + variable_buffer_offset_ = other.variable_buffer_offset_; + return *this; +} + +bool Pickle::ReadBool(void** iter, bool* result) const { + DCHECK(iter); + + int tmp; + if (!ReadInt(iter, &tmp)) + return false; + DCHECK(0 == tmp || 1 == tmp); + *result = tmp ? true : false; + return true; +} + +bool Pickle::ReadInt16(void** iter, int16* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return false; + + memcpy(result, *iter, sizeof(*result)); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadUInt16(void** iter, uint16* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return false; + + memcpy(result, *iter, sizeof(*result)); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadInt(void** iter, int* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return false; + + // TODO(jar) bug 1129285: Pickle should be cleaned up, and not dependent on + // alignment. + // Next line is otherwise the same as: memcpy(result, *iter, sizeof(*result)); + *result = *reinterpret_cast(*iter); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadLong(void** iter, long* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return false; + + // TODO(jar) bug 1129285: Pickle should be cleaned up, and not dependent on + // alignment. + memcpy(result, *iter, sizeof(*result)); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadULong(void** iter, unsigned long* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return false; + + // TODO(jar) bug 1129285: Pickle should be cleaned up, and not dependent on + // alignment. + memcpy(result, *iter, sizeof(*result)); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadLength(void** iter, int* result) const { + if (!ReadInt(iter, result)) + return false; + return ((*result) >= 0); +} + +bool Pickle::ReadSize(void** iter, size_t* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return false; + + // TODO(jar) bug 1129285: Pickle should be cleaned up, and not dependent on + // alignment. + // Next line is otherwise the same as: memcpy(result, *iter, sizeof(*result)); + *result = *reinterpret_cast(*iter); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadUInt32(void** iter, uint32* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return false; + + memcpy(result, *iter, sizeof(*result)); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadInt64(void** iter, int64* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return false; + + memcpy(result, *iter, sizeof(*result)); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadIntPtr(void** iter, intptr_t* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, sizeof(*result))) + return false; + + memcpy(result, *iter, sizeof(*result)); + + UpdateIter(iter, sizeof(*result)); + return true; +} + +bool Pickle::ReadString(void** iter, std::string* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + int len; + if (!ReadLength(iter, &len)) + return false; + if (!IteratorHasRoomFor(*iter, len)) + return false; + + char* chars = reinterpret_cast(*iter); + result->assign(chars, len); + + UpdateIter(iter, len); + return true; +} + +bool Pickle::ReadWString(void** iter, std::wstring* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + int len; + if (!ReadLength(iter, &len)) + return false; + if (!IteratorHasRoomFor(*iter, len * sizeof(wchar_t))) + return false; + + wchar_t* chars = reinterpret_cast(*iter); + result->assign(chars, len); + + UpdateIter(iter, len * sizeof(wchar_t)); + return true; +} + +bool Pickle::ReadString16(void** iter, string16* result) const { + DCHECK(iter); + if (!*iter) + *iter = const_cast(payload()); + + int len; + if (!ReadLength(iter, &len)) + return false; + if (!IteratorHasRoomFor(*iter, len)) + return false; + + char16* chars = reinterpret_cast(*iter); + result->assign(chars, len); + + UpdateIter(iter, len * sizeof(char16)); + return true; +} + +bool Pickle::ReadBytes(void** iter, const char** data, int length) const { + DCHECK(iter); + DCHECK(data); + if (!*iter) + *iter = const_cast(payload()); + + if (!IteratorHasRoomFor(*iter, length)) + return false; + + *data = reinterpret_cast(*iter); + + UpdateIter(iter, length); + return true; +} + +bool Pickle::ReadData(void** iter, const char** data, int* length) const { + DCHECK(iter); + DCHECK(data); + DCHECK(length); + if (!*iter) + *iter = const_cast(payload()); + + if (!ReadLength(iter, length)) + return false; + + return ReadBytes(iter, data, *length); +} + +char* Pickle::BeginWrite(size_t length) { + // write at a uint32-aligned offset from the beginning of the header + size_t offset = AlignInt(header_->payload_size, sizeof(uint32)); + +#ifdef CHROMIUM_MOZILLA_BUILD + size_t new_size = offset + AlignInt(length, sizeof(uint32)); +#else + size_t new_size = offset + length; +#endif + size_t needed_size = header_size_ + new_size; + if (needed_size > capacity_ && !Resize(std::max(capacity_ * 2, needed_size))) + return NULL; + +#ifdef ARCH_CPU_64_BITS + DCHECK_LE(length, std::numeric_limits::max()); +#endif + + header_->payload_size = static_cast(new_size); + return payload() + offset; +} + +void Pickle::EndWrite(char* dest, int length) { + // Zero-pad to keep tools like purify from complaining about uninitialized + // memory. + if (length % sizeof(uint32)) + memset(dest + length, 0, sizeof(uint32) - (length % sizeof(uint32))); +} + +bool Pickle::WriteBytes(const void* data, int data_len) { + DCHECK(capacity_ != kCapacityReadOnly) << "oops: pickle is readonly"; + + char* dest = BeginWrite(data_len); + if (!dest) + return false; + + memcpy(dest, data, data_len); + + EndWrite(dest, data_len); + return true; +} + +bool Pickle::WriteString(const std::string& value) { + if (!WriteInt(static_cast(value.size()))) + return false; + + return WriteBytes(value.data(), static_cast(value.size())); +} + +bool Pickle::WriteWString(const std::wstring& value) { + if (!WriteInt(static_cast(value.size()))) + return false; + + return WriteBytes(value.data(), + static_cast(value.size() * sizeof(wchar_t))); +} + +bool Pickle::WriteString16(const string16& value) { + if (!WriteInt(static_cast(value.size()))) + return false; + + return WriteBytes(value.data(), + static_cast(value.size()) * sizeof(char16)); +} + +bool Pickle::WriteData(const char* data, int length) { + return WriteInt(length) && WriteBytes(data, length); +} + +char* Pickle::BeginWriteData(int length) { + DCHECK_EQ(variable_buffer_offset_, 0U) << + "There can only be one variable buffer in a Pickle"; + + if (!WriteInt(length)) + return false; + + char *data_ptr = BeginWrite(length); + if (!data_ptr) + return NULL; + + variable_buffer_offset_ = + data_ptr - reinterpret_cast(header_) - sizeof(int); + + // EndWrite doesn't necessarily have to be called after the write operation, + // so we call it here to pad out what the caller will eventually write. + EndWrite(data_ptr, length); + return data_ptr; +} + +void Pickle::TrimWriteData(int new_length) { + DCHECK(variable_buffer_offset_ != 0); + + // Fetch the the variable buffer size + int* cur_length = reinterpret_cast( + reinterpret_cast(header_) + variable_buffer_offset_); + + if (new_length < 0 || new_length > *cur_length) { + NOTREACHED() << "Invalid length in TrimWriteData."; + return; + } + + // Update the payload size and variable buffer size + header_->payload_size -= (*cur_length - new_length); + *cur_length = new_length; +} + +bool Pickle::Resize(size_t new_capacity) { + new_capacity = AlignInt(new_capacity, kPayloadUnit); + + void* p = realloc(header_, new_capacity); + if (!p) + return false; + + header_ = reinterpret_cast(p); + capacity_ = new_capacity; + return true; +} + +// static +const char* Pickle::FindNext(size_t header_size, + const char* start, + const char* end) { + DCHECK(header_size == AlignInt(header_size, sizeof(uint32))); + DCHECK(header_size <= static_cast(kPayloadUnit)); + + const Header* hdr = reinterpret_cast(start); + const char* payload_base = start + header_size; + const char* payload_end = payload_base + hdr->payload_size; + if (payload_end < payload_base) + return NULL; + + return (payload_end > end) ? NULL : payload_end; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/pickle.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/pickle.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/pickle.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/pickle.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,258 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_PICKLE_H__ +#define BASE_PICKLE_H__ + +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/string16.h" +#include "testing/gtest/include/gtest/gtest_prod.h" + +// This class provides facilities for basic binary value packing and unpacking. +// +// The Pickle class supports appending primitive values (ints, strings, etc.) +// to a pickle instance. The Pickle instance grows its internal memory buffer +// dynamically to hold the sequence of primitive values. The internal memory +// buffer is exposed as the "data" of the Pickle. This "data" can be passed +// to a Pickle object to initialize it for reading. +// +// When reading from a Pickle object, it is important for the consumer to know +// what value types to read and in what order to read them as the Pickle does +// not keep track of the type of data written to it. +// +// The Pickle's data has a header which contains the size of the Pickle's +// payload. It can optionally support additional space in the header. That +// space is controlled by the header_size parameter passed to the Pickle +// constructor. +// +class Pickle { + public: + ~Pickle(); + + // Initialize a Pickle object using the default header size. + Pickle(); + + // Initialize a Pickle object with the specified header size in bytes, which + // must be greater-than-or-equal-to sizeof(Pickle::Header). The header size + // will be rounded up to ensure that the header size is 32bit-aligned. + explicit Pickle(int header_size); + + // Initializes a Pickle from a const block of data. The data is not copied; + // instead the data is merely referenced by this Pickle. Only const methods + // should be used on the Pickle when initialized this way. The header + // padding size is deduced from the data length. + Pickle(const char* data, int data_len); + + // Initializes a Pickle as a deep copy of another Pickle. + Pickle(const Pickle& other); + + // Performs a deep copy. + Pickle& operator=(const Pickle& other); + + // Returns the size of the Pickle's data. + int size() const { return static_cast(header_size_ + + header_->payload_size); } + + // Returns the data for this Pickle. + const void* data() const { return header_; } + + // Methods for reading the payload of the Pickle. To read from the start of + // the Pickle, initialize *iter to NULL. If successful, these methods return + // true. Otherwise, false is returned to indicate that the result could not + // be extracted. + bool ReadBool(void** iter, bool* result) const; + bool ReadInt16(void** iter, int16* result) const; + bool ReadUInt16(void** iter, uint16* result) const; + bool ReadShort(void** iter, short* result) const; + bool ReadInt(void** iter, int* result) const; + bool ReadLong(void** iter, long* result) const; + bool ReadULong(void** iter, unsigned long* result) const; + bool ReadSize(void** iter, size_t* result) const; + bool ReadUInt32(void** iter, uint32* result) const; + bool ReadInt64(void** iter, int64* result) const; + bool ReadIntPtr(void** iter, intptr_t* result) const; + bool ReadString(void** iter, std::string* result) const; + bool ReadWString(void** iter, std::wstring* result) const; + bool ReadString16(void** iter, string16* result) const; + bool ReadData(void** iter, const char** data, int* length) const; + bool ReadBytes(void** iter, const char** data, int length) const; + + // Safer version of ReadInt() checks for the result not being negative. + // Use it for reading the object sizes. + bool ReadLength(void** iter, int* result) const; + + // Methods for adding to the payload of the Pickle. These values are + // appended to the end of the Pickle's payload. When reading values from a + // Pickle, it is important to read them in the order in which they were added + // to the Pickle. + bool WriteBool(bool value) { + return WriteInt(value ? 1 : 0); + } + bool WriteInt16(int16 value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteUInt16(uint16 value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteInt(int value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteLong(long value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteULong(unsigned long value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteSize(size_t value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteUInt32(uint32 value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteInt64(int64 value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteIntPtr(intptr_t value) { + return WriteBytes(&value, sizeof(value)); + } + bool WriteString(const std::string& value); + bool WriteWString(const std::wstring& value); + bool WriteString16(const string16& value); + bool WriteData(const char* data, int length); + bool WriteBytes(const void* data, int data_len); + + // Same as WriteData, but allows the caller to write directly into the + // Pickle. This saves a copy in cases where the data is not already + // available in a buffer. The caller should take care to not write more + // than the length it declares it will. Use ReadData to get the data. + // Returns NULL on failure. + // + // The returned pointer will only be valid until the next write operation + // on this Pickle. + char* BeginWriteData(int length); + + // For Pickles which contain variable length buffers (e.g. those created + // with BeginWriteData), the Pickle can + // be 'trimmed' if the amount of data required is less than originally + // requested. For example, you may have created a buffer with 10K of data, + // but decided to only fill 10 bytes of that data. Use this function + // to trim the buffer so that we don't send 9990 bytes of unused data. + // You cannot increase the size of the variable buffer; only shrink it. + // This function assumes that the length of the variable buffer has + // not been changed. + void TrimWriteData(int length); + +#if defined(CHROMIUM_MOZILLA_BUILD) + void EndRead(void* iter) const { + DCHECK(iter == end_of_payload()); + } +#endif + + // Payload follows after allocation of Header (header size is customizable). + struct Header { + uint32 payload_size; // Specifies the size of the payload. + }; + + // Returns the header, cast to a user-specified type T. The type T must be a + // subclass of Header and its size must correspond to the header_size passed + // to the Pickle constructor. + template + T* headerT() { + DCHECK(sizeof(T) == header_size_); + return static_cast(header_); + } + template + const T* headerT() const { + DCHECK(sizeof(T) == header_size_); + return static_cast(header_); + } + + // Returns true if the given iterator could point to data with the given + // length. If there is no room for the given data before the end of the + // payload, returns false. + bool IteratorHasRoomFor(const void* iter, int len) const { + if ((len < 0) || (iter < header_) || iter > end_of_payload()) + return false; + const char* end_of_region = reinterpret_cast(iter) + len; + // Watch out for overflow in pointer calculation, which wraps. + return (iter <= end_of_region) && (end_of_region <= end_of_payload()); + } + + protected: + size_t payload_size() const { return header_->payload_size; } + + char* payload() { + return reinterpret_cast(header_) + header_size_; + } + const char* payload() const { + return reinterpret_cast(header_) + header_size_; + } + + // Returns the address of the byte immediately following the currently valid + // header + payload. + char* end_of_payload() { + return payload() + payload_size(); + } + const char* end_of_payload() const { + return payload() + payload_size(); + } + + size_t capacity() const { + return capacity_; + } + + // Resizes the buffer for use when writing the specified amount of data. The + // location that the data should be written at is returned, or NULL if there + // was an error. Call EndWrite with the returned offset and the given length + // to pad out for the next write. + char* BeginWrite(size_t length); + + // Completes the write operation by padding the data with NULL bytes until it + // is padded. Should be paired with BeginWrite, but it does not necessarily + // have to be called after the data is written. + void EndWrite(char* dest, int length); + + // Resize the capacity, note that the input value should include the size of + // the header: new_capacity = sizeof(Header) + desired_payload_capacity. + // A realloc() failure will cause a Resize failure... and caller should check + // the return result for true (i.e., successful resizing). + bool Resize(size_t new_capacity); + + // Aligns 'i' by rounding it up to the next multiple of 'alignment' + static size_t AlignInt(size_t i, int alignment) { + return i + (alignment - (i % alignment)) % alignment; + } + + // Moves the iterator by the given number of bytes, making sure it is aligned. + // Pointer (iterator) is NOT aligned, but the change in the pointer + // is guaranteed to be a multiple of sizeof(uint32). + static void UpdateIter(void** iter, int bytes) { + *iter = static_cast(*iter) + AlignInt(bytes, sizeof(uint32)); + } + + // Find the end of the pickled data that starts at range_start. Returns NULL + // if the entire Pickle is not found in the given data range. + static const char* FindNext(size_t header_size, + const char* range_start, + const char* range_end); + + // The allocation granularity of the payload. + static const int kPayloadUnit; + + private: + Header* header_; + size_t header_size_; // Supports extra data between header and payload. + // Allocation size of payload (or -1 if allocation is const). + size_t capacity_; + size_t variable_buffer_offset_; // IF non-zero, then offset to a buffer. + + FRIEND_TEST(PickleTest, Resize); + FRIEND_TEST(PickleTest, FindNext); + FRIEND_TEST(PickleTest, IteratorHasRoom); +}; + +#endif // BASE_PICKLE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/pickle_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/pickle_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/pickle_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/pickle_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,220 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/basictypes.h" +#include "base/pickle.h" +#include "base/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const int testint = 2093847192; +const std::string teststr("Hello world"); // note non-aligned string length +const std::wstring testwstr(L"Hello, world"); +const char testdata[] = "AAA\0BBB\0"; +const int testdatalen = arraysize(testdata) - 1; +const bool testbool1 = false; +const bool testbool2 = true; + +// checks that the result +void VerifyResult(const Pickle& pickle) { + void* iter = NULL; + + int outint; + EXPECT_TRUE(pickle.ReadInt(&iter, &outint)); + EXPECT_EQ(testint, outint); + + std::string outstr; + EXPECT_TRUE(pickle.ReadString(&iter, &outstr)); + EXPECT_EQ(teststr, outstr); + + std::wstring outwstr; + EXPECT_TRUE(pickle.ReadWString(&iter, &outwstr)); + EXPECT_EQ(testwstr, outwstr); + + bool outbool; + EXPECT_TRUE(pickle.ReadBool(&iter, &outbool)); + EXPECT_EQ(testbool1, outbool); + EXPECT_TRUE(pickle.ReadBool(&iter, &outbool)); + EXPECT_EQ(testbool2, outbool); + + const char* outdata; + int outdatalen; + EXPECT_TRUE(pickle.ReadData(&iter, &outdata, &outdatalen)); + EXPECT_EQ(testdatalen, outdatalen); + EXPECT_EQ(memcmp(testdata, outdata, outdatalen), 0); + + EXPECT_TRUE(pickle.ReadData(&iter, &outdata, &outdatalen)); + EXPECT_EQ(testdatalen, outdatalen); + EXPECT_EQ(memcmp(testdata, outdata, outdatalen), 0); + + // reads past the end should fail + EXPECT_FALSE(pickle.ReadInt(&iter, &outint)); +} + +} // namespace + +TEST(PickleTest, EncodeDecode) { + Pickle pickle; + + EXPECT_TRUE(pickle.WriteInt(testint)); + EXPECT_TRUE(pickle.WriteString(teststr)); + EXPECT_TRUE(pickle.WriteWString(testwstr)); + EXPECT_TRUE(pickle.WriteBool(testbool1)); + EXPECT_TRUE(pickle.WriteBool(testbool2)); + EXPECT_TRUE(pickle.WriteData(testdata, testdatalen)); + + // Over allocate BeginWriteData so we can test TrimWriteData. + char* dest = pickle.BeginWriteData(testdatalen + 100); + EXPECT_TRUE(dest); + memcpy(dest, testdata, testdatalen); + + pickle.TrimWriteData(testdatalen); + + VerifyResult(pickle); + + // test copy constructor + Pickle pickle2(pickle); + VerifyResult(pickle2); + + // test operator= + Pickle pickle3; + pickle3 = pickle; + VerifyResult(pickle3); +} + +TEST(PickleTest, ZeroLenStr) { + Pickle pickle; + EXPECT_TRUE(pickle.WriteString("")); + + void* iter = NULL; + std::string outstr; + EXPECT_TRUE(pickle.ReadString(&iter, &outstr)); + EXPECT_EQ("", outstr); +} + +TEST(PickleTest, ZeroLenWStr) { + Pickle pickle; + EXPECT_TRUE(pickle.WriteWString(L"")); + + void* iter = NULL; + std::string outstr; + EXPECT_TRUE(pickle.ReadString(&iter, &outstr)); + EXPECT_EQ("", outstr); +} + +TEST(PickleTest, BadLenStr) { + Pickle pickle; + EXPECT_TRUE(pickle.WriteInt(-2)); + + void* iter = NULL; + std::string outstr; + EXPECT_FALSE(pickle.ReadString(&iter, &outstr)); +} + +TEST(PickleTest, BadLenWStr) { + Pickle pickle; + EXPECT_TRUE(pickle.WriteInt(-1)); + + void* iter = NULL; + std::wstring woutstr; + EXPECT_FALSE(pickle.ReadWString(&iter, &woutstr)); +} + +TEST(PickleTest, FindNext) { + Pickle pickle; + EXPECT_TRUE(pickle.WriteInt(1)); + EXPECT_TRUE(pickle.WriteString("Domo")); + + const char* start = reinterpret_cast(pickle.data()); + const char* end = start + pickle.size(); + + EXPECT_TRUE(end == Pickle::FindNext(pickle.header_size_, start, end)); + EXPECT_TRUE(NULL == Pickle::FindNext(pickle.header_size_, start, end - 1)); + EXPECT_TRUE(end == Pickle::FindNext(pickle.header_size_, start, end + 1)); +} + +TEST(PickleTest, IteratorHasRoom) { + Pickle pickle; + EXPECT_TRUE(pickle.WriteInt(1)); + EXPECT_TRUE(pickle.WriteInt(2)); + + const void* iter = 0; + EXPECT_FALSE(pickle.IteratorHasRoomFor(iter, 1)); + iter = pickle.payload(); + EXPECT_TRUE(pickle.IteratorHasRoomFor(iter, 0)); + EXPECT_TRUE(pickle.IteratorHasRoomFor(iter, 1)); + EXPECT_FALSE(pickle.IteratorHasRoomFor(iter, -1)); + EXPECT_TRUE(pickle.IteratorHasRoomFor(iter, sizeof(int) * 2)); + EXPECT_FALSE(pickle.IteratorHasRoomFor(iter, (sizeof(int) * 2) + 1)); +} + +TEST(PickleTest, Resize) { + size_t unit = Pickle::kPayloadUnit; + scoped_array data(new char[unit]); + char* data_ptr = data.get(); + for (size_t i = 0; i < unit; i++) + data_ptr[i] = 'G'; + + // construct a message that will be exactly the size of one payload unit, + // note that any data will have a 4-byte header indicating the size + const size_t payload_size_after_header = unit - sizeof(uint32); + Pickle pickle; + pickle.WriteData(data_ptr, + static_cast(payload_size_after_header - sizeof(uint32))); + size_t cur_payload = payload_size_after_header; + + // note: we assume 'unit' is a power of 2 + EXPECT_EQ(unit, pickle.capacity()); + EXPECT_EQ(pickle.payload_size(), payload_size_after_header); + + // fill out a full page (noting data header) + pickle.WriteData(data_ptr, static_cast(unit - sizeof(uint32))); + cur_payload += unit; + EXPECT_EQ(unit * 2, pickle.capacity()); + EXPECT_EQ(cur_payload, pickle.payload_size()); + + // one more byte should double the capacity + pickle.WriteData(data_ptr, 1); + cur_payload += 5; + EXPECT_EQ(unit * 4, pickle.capacity()); + EXPECT_EQ(cur_payload, pickle.payload_size()); +} + +namespace { + +struct CustomHeader : Pickle::Header { + int blah; +}; + +} // namespace + +TEST(PickleTest, HeaderPadding) { + const uint32 kMagic = 0x12345678; + + Pickle pickle(sizeof(CustomHeader)); + pickle.WriteInt(kMagic); + + // this should not overwrite the 'int' payload + pickle.headerT()->blah = 10; + + void* iter = NULL; + int result; + ASSERT_TRUE(pickle.ReadInt(&iter, &result)); + + EXPECT_EQ(static_cast(result), kMagic); +} + +TEST(PickleTest, EqualsOperator) { + Pickle source; + source.WriteInt(1); + + Pickle copy_refs_source_buffer(static_cast(source.data()), + source.size()); + Pickle copy; + copy = copy_refs_source_buffer; + ASSERT_EQ(source.size(), copy.size()); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/platform_file.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/platform_file.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/platform_file.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/platform_file.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,46 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_PLATFORM_FILE_H_ +#define BASE_PLATFORM_FILE_H_ + +#include "build/build_config.h" +#if defined(OS_WIN) +#include +#endif + +#include + +namespace base { + +#if defined(OS_WIN) +typedef HANDLE PlatformFile; +const PlatformFile kInvalidPlatformFileValue = INVALID_HANDLE_VALUE; +#elif defined(OS_POSIX) +typedef int PlatformFile; +const PlatformFile kInvalidPlatformFileValue = -1; +#endif + +enum PlatformFileFlags { + PLATFORM_FILE_OPEN = 1, + PLATFORM_FILE_CREATE = 2, + PLATFORM_FILE_OPEN_ALWAYS = 4, // May create a new file. + PLATFORM_FILE_CREATE_ALWAYS = 8, // May overwrite an old file. + PLATFORM_FILE_READ = 16, + PLATFORM_FILE_WRITE = 32, + PLATFORM_FILE_EXCLUSIVE_READ = 64, // EXCLUSIVE is opposite of Windows SHARE + PLATFORM_FILE_EXCLUSIVE_WRITE = 128, + PLATFORM_FILE_ASYNC = 256 +}; + +// Creates or open the given file. If PLATFORM_FILE_OPEN_ALWAYS is used, and +// |created| is provided, |created| will be set to true if the file was created +// or to false in case the file was just opened. +PlatformFile CreatePlatformFile(const std::wstring& name, + int flags, + bool* created); + +} // namespace base + +#endif // BASE_PLATFORM_FILE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/platform_file_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/platform_file_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/platform_file_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/platform_file_posix.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,64 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/platform_file.h" + +#include +#include + +#include "base/logging.h" +#include "base/string_util.h" + +namespace base { + +// TODO(erikkay): does it make sense to support PLATFORM_FILE_EXCLUSIVE_* here? +PlatformFile CreatePlatformFile(const std::wstring& name, + int flags, + bool* created) { + int open_flags = 0; + if (flags & PLATFORM_FILE_CREATE) + open_flags = O_CREAT | O_EXCL; + + if (flags & PLATFORM_FILE_CREATE_ALWAYS) { + DCHECK(!open_flags); + open_flags = O_CREAT | O_TRUNC; + } + + if (!open_flags && !(flags & PLATFORM_FILE_OPEN) && + !(flags & PLATFORM_FILE_OPEN_ALWAYS)) { + NOTREACHED(); + errno = ENOTSUP; + return kInvalidPlatformFileValue; + } + + if (flags & PLATFORM_FILE_WRITE && flags & PLATFORM_FILE_READ) { + open_flags |= O_RDWR; + } else if (flags & PLATFORM_FILE_WRITE) { + open_flags |= O_WRONLY; + } else if (!(flags & PLATFORM_FILE_READ)) { + NOTREACHED(); + } + + DCHECK(O_RDONLY == 0); + + int descriptor = open(WideToUTF8(name).c_str(), open_flags, + S_IRUSR | S_IWUSR); + + if (flags & PLATFORM_FILE_OPEN_ALWAYS) { + if (descriptor > 0) { + if (created) + *created = false; + } else { + open_flags |= O_CREAT; + descriptor = open(WideToUTF8(name).c_str(), open_flags, + S_IRUSR | S_IWUSR); + if (created && descriptor > 0) + *created = true; + } + } + + return descriptor; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/platform_file_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/platform_file_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/platform_file_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/platform_file_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,62 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/platform_file.h" + +#include "base/logging.h" + +namespace base { + +PlatformFile CreatePlatformFile(const std::wstring& name, + int flags, + bool* created) { + DWORD disposition = 0; + + if (flags & PLATFORM_FILE_OPEN) + disposition = OPEN_EXISTING; + + if (flags & PLATFORM_FILE_CREATE) { + DCHECK(!disposition); + disposition = CREATE_NEW; + } + + if (flags & PLATFORM_FILE_OPEN_ALWAYS) { + DCHECK(!disposition); + disposition = OPEN_ALWAYS; + } + + if (flags & PLATFORM_FILE_CREATE_ALWAYS) { + DCHECK(!disposition); + disposition = CREATE_ALWAYS; + } + + if (!disposition) { + NOTREACHED(); + return NULL; + } + + DWORD access = (flags & PLATFORM_FILE_READ) ? GENERIC_READ : 0; + if (flags & PLATFORM_FILE_WRITE) + access |= GENERIC_WRITE; + + DWORD sharing = (flags & PLATFORM_FILE_EXCLUSIVE_READ) ? 0 : FILE_SHARE_READ; + if (!(flags & PLATFORM_FILE_EXCLUSIVE_WRITE)) + sharing |= FILE_SHARE_WRITE; + + DWORD create_flags = 0; + if (flags & PLATFORM_FILE_ASYNC) + create_flags |= FILE_FLAG_OVERLAPPED; + + HANDLE file = CreateFile(name.c_str(), access, sharing, NULL, disposition, + create_flags, NULL); + + if ((flags & PLATFORM_FILE_OPEN_ALWAYS) && created && + INVALID_HANDLE_VALUE != file) { + *created = (ERROR_ALREADY_EXISTS != GetLastError()); + } + + return file; +} + +} // namespace disk_cache diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/platform_thread.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/platform_thread.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/platform_thread.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/platform_thread.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,82 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// WARNING: You should *NOT* be using this class directly. PlatformThread is +// the low-level platform-specific abstraction to the OS's threading interface. +// You should instead be using a message-loop driven Thread, see thread.h. + +#ifndef BASE_PLATFORM_THREAD_H_ +#define BASE_PLATFORM_THREAD_H_ + +#include "base/basictypes.h" + +// PlatformThreadHandle should not be assumed to be a numeric type, since the +// standard intends to allow pthread_t to be a structure. This means you +// should not initialize it to a value, like 0. If it's a member variable, the +// constructor can safely "value initialize" using () in the initializer list. +#if defined(OS_WIN) +#include +typedef DWORD PlatformThreadId; +typedef void* PlatformThreadHandle; // HANDLE +#elif defined(OS_POSIX) +#include +typedef pthread_t PlatformThreadHandle; +#if defined(OS_LINUX) +#include +typedef pid_t PlatformThreadId; +#elif defined(OS_MACOSX) +#include +typedef mach_port_t PlatformThreadId; +#endif +#endif + +// A namespace for low-level thread functions. +class PlatformThread { + public: + // Gets the current thread id, which may be useful for logging purposes. + static PlatformThreadId CurrentId(); + + // Yield the current thread so another thread can be scheduled. + static void YieldCurrentThread(); + + // Sleeps for the specified duration (units are milliseconds). + static void Sleep(int duration_ms); + + // Sets the thread name visible to a debugger. This has no effect otherwise. + static void SetName(const char* name); + + // Implement this interface to run code on a background thread. Your + // ThreadMain method will be called on the newly created thread. + class Delegate { + public: + virtual ~Delegate() {} + virtual void ThreadMain() = 0; + }; + + // Creates a new thread. The |stack_size| parameter can be 0 to indicate + // that the default stack size should be used. Upon success, + // |*thread_handle| will be assigned a handle to the newly created thread, + // and |delegate|'s ThreadMain method will be executed on the newly created + // thread. + // NOTE: When you are done with the thread handle, you must call Join to + // release system resources associated with the thread. You must ensure that + // the Delegate object outlives the thread. + static bool Create(size_t stack_size, Delegate* delegate, + PlatformThreadHandle* thread_handle); + + // CreateNonJoinable() does the same thing as Create() except the thread + // cannot be Join()'d. Therefore, it also does not output a + // PlatformThreadHandle. + static bool CreateNonJoinable(size_t stack_size, Delegate* delegate); + + // Joins with a thread created via the Create function. This function blocks + // the caller until the designated thread exits. This will invalidate + // |thread_handle|. + static void Join(PlatformThreadHandle thread_handle); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(PlatformThread); +}; + +#endif // BASE_PLATFORM_THREAD_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/platform_thread_mac.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/platform_thread_mac.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/platform_thread_mac.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/platform_thread_mac.mm 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,48 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/platform_thread.h" + +#import + +#include "base/logging.h" + +// A simple class that demonstrates our impressive ability to do nothing. +@interface NoOp : NSObject + +// Does the deed. Or does it? ++ (void)noOp; + +@end + +@implementation NoOp + ++ (void)noOp { +} + +@end + +namespace base { + +// If Cocoa is to be used on more than one thread, it must know that the +// application is multithreaded. Since it's possible to enter Cocoa code +// from threads created by pthread_thread_create, Cocoa won't necessarily +// be aware that the application is multithreaded. Spawning an NSThread is +// enough to get Cocoa to set up for multithreaded operation, so this is done +// if necessary before pthread_thread_create spawns any threads. +// +// http://developer.apple.com/documentation/Cocoa/Conceptual/Multithreading/CreatingThreads/chapter_4_section_4.html +void InitThreading() { + static BOOL multithreaded = [NSThread isMultiThreaded]; + if (!multithreaded) { + [NSThread detachNewThreadSelector:@selector(noOp) + toTarget:[NoOp class] + withObject:nil]; + multithreaded = YES; + + DCHECK([NSThread isMultiThreaded]); + } +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/platform_thread_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/platform_thread_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/platform_thread_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/platform_thread_posix.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,120 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/platform_thread.h" + +#include +#include + +#if defined(OS_MACOSX) +#include +#elif defined(OS_LINUX) +#include +#include +#endif + +#if defined(OS_MACOSX) +namespace base { +void InitThreading(); +} // namespace +#endif + +static void* ThreadFunc(void* closure) { + PlatformThread::Delegate* delegate = + static_cast(closure); + delegate->ThreadMain(); + return NULL; +} + +// static +PlatformThreadId PlatformThread::CurrentId() { + // Pthreads doesn't have the concept of a thread ID, so we have to reach down + // into the kernel. +#if defined(OS_MACOSX) + return mach_thread_self(); +#elif defined(OS_LINUX) + return syscall(__NR_gettid); +#endif +} + +// static +void PlatformThread::YieldCurrentThread() { + sched_yield(); +} + +// static +void PlatformThread::Sleep(int duration_ms) { + struct timespec sleep_time, remaining; + + // Contains the portion of duration_ms >= 1 sec. + sleep_time.tv_sec = duration_ms / 1000; + duration_ms -= sleep_time.tv_sec * 1000; + + // Contains the portion of duration_ms < 1 sec. + sleep_time.tv_nsec = duration_ms * 1000 * 1000; // nanoseconds. + + while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR) + sleep_time = remaining; +} + +// static +void PlatformThread::SetName(const char* name) { + // The POSIX standard does not provide for naming threads, and neither Linux + // nor Mac OS X (our two POSIX targets) provide any non-portable way of doing + // it either. (Some BSDs provide pthread_set_name_np but that isn't much of a + // consolation prize.) + // TODO(darin): decide whether stuffing the name in TLS or other in-memory + // structure would be useful for debugging or not. +} + +namespace { + +bool CreateThread(size_t stack_size, bool joinable, + PlatformThread::Delegate* delegate, + PlatformThreadHandle* thread_handle) { +#if defined(OS_MACOSX) + base::InitThreading(); +#endif // OS_MACOSX + + bool success = false; + pthread_attr_t attributes; + pthread_attr_init(&attributes); + + // Pthreads are joinable by default, so only specify the detached attribute if + // the thread should be non-joinable. + if (!joinable) { + pthread_attr_setdetachstate(&attributes, PTHREAD_CREATE_DETACHED); + } + + if (stack_size > 0) + pthread_attr_setstacksize(&attributes, stack_size); + + success = !pthread_create(thread_handle, &attributes, ThreadFunc, delegate); + + pthread_attr_destroy(&attributes); + return success; +} + +} // anonymous namespace + +// static +bool PlatformThread::Create(size_t stack_size, Delegate* delegate, + PlatformThreadHandle* thread_handle) { + return CreateThread(stack_size, true /* joinable thread */, + delegate, thread_handle); +} + +// static +bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { + PlatformThreadHandle unused; + + bool result = CreateThread(stack_size, false /* non-joinable thread */, + delegate, &unused); + return result; +} + +// static +void PlatformThread::Join(PlatformThreadHandle thread_handle) { + pthread_join(thread_handle, NULL); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/platform_thread_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/platform_thread_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/platform_thread_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/platform_thread_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,105 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/platform_thread.h" + +#include "base/logging.h" +#include "base/win_util.h" + +namespace { + +// The information on how to set the thread name comes from +// a MSDN article: http://msdn2.microsoft.com/en-us/library/xcb2z8hs.aspx +const DWORD kVCThreadNameException = 0x406D1388; + +typedef struct tagTHREADNAME_INFO { + DWORD dwType; // Must be 0x1000. + LPCSTR szName; // Pointer to name (in user addr space). + DWORD dwThreadID; // Thread ID (-1=caller thread). + DWORD dwFlags; // Reserved for future use, must be zero. +} THREADNAME_INFO; + +DWORD __stdcall ThreadFunc(void* closure) { + PlatformThread::Delegate* delegate = + static_cast(closure); + delegate->ThreadMain(); + return NULL; +} + +} // namespace + +// static +PlatformThreadId PlatformThread::CurrentId() { + return GetCurrentThreadId(); +} + +// static +void PlatformThread::YieldCurrentThread() { + ::Sleep(0); +} + +// static +void PlatformThread::Sleep(int duration_ms) { + ::Sleep(duration_ms); +} + +// static +void PlatformThread::SetName(const char* name) { + // The debugger needs to be around to catch the name in the exception. If + // there isn't a debugger, we are just needlessly throwing an exception. + if (!::IsDebuggerPresent()) + return; + + THREADNAME_INFO info; + info.dwType = 0x1000; + info.szName = name; + info.dwThreadID = CurrentId(); + info.dwFlags = 0; + + __try { + RaiseException(kVCThreadNameException, 0, sizeof(info)/sizeof(DWORD), + reinterpret_cast(&info)); + } __except(EXCEPTION_CONTINUE_EXECUTION) { + } +} + +// static +bool PlatformThread::Create(size_t stack_size, Delegate* delegate, + PlatformThreadHandle* thread_handle) { + unsigned int flags = 0; + if (stack_size > 0 && win_util::GetWinVersion() >= win_util::WINVERSION_XP) { + flags = STACK_SIZE_PARAM_IS_A_RESERVATION; + } else { + stack_size = 0; + } + + // Using CreateThread here vs _beginthreadex makes thread creation a bit + // faster and doesn't require the loader lock to be available. Our code will + // have to work running on CreateThread() threads anyway, since we run code + // on the Windows thread pool, etc. For some background on the difference: + // http://www.microsoft.com/msj/1099/win32/win321099.aspx + *thread_handle = CreateThread( + NULL, stack_size, ThreadFunc, delegate, flags, NULL); + return *thread_handle != NULL; +} + +// static +bool PlatformThread::CreateNonJoinable(size_t stack_size, Delegate* delegate) { + PlatformThreadHandle thread_handle; + bool result = Create(stack_size, delegate, &thread_handle); + CloseHandle(thread_handle); + return result; +} + +// static +void PlatformThread::Join(PlatformThreadHandle thread_handle) { + DCHECK(thread_handle); + + // Wait for the thread to exit. It should already have terminated but make + // sure this assumption is valid. + DWORD result = WaitForSingleObject(thread_handle, INFINITE); + DCHECK_EQ(WAIT_OBJECT_0, result); + + CloseHandle(thread_handle); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/port.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/port.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/port.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/port.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,74 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_PORT_H_ +#define BASE_PORT_H_ + +#include +#include "build/build_config.h" + +#ifdef COMPILER_MSVC +#define GG_LONGLONG(x) x##I64 +#define GG_ULONGLONG(x) x##UI64 +#else +#define GG_LONGLONG(x) x##LL +#define GG_ULONGLONG(x) x##ULL +#endif + +// Per C99 7.8.14, define __STDC_CONSTANT_MACROS before including +// to get the INTn_C and UINTn_C macros for integer constants. It's difficult +// to guarantee any specific ordering of header includes, so it's difficult to +// guarantee that the INTn_C macros can be defined by including at +// any specific point. Provide GG_INTn_C macros instead. + +#define GG_INT8_C(x) (x) +#define GG_INT16_C(x) (x) +#define GG_INT32_C(x) (x) +#define GG_INT64_C(x) GG_LONGLONG(x) + +#define GG_UINT8_C(x) (x ## U) +#define GG_UINT16_C(x) (x ## U) +#define GG_UINT32_C(x) (x ## U) +#define GG_UINT64_C(x) GG_ULONGLONG(x) + +namespace base { + +// It's possible for functions that use a va_list, such as StringPrintf, to +// invalidate the data in it upon use. The fix is to make a copy of the +// structure before using it and use that copy instead. va_copy is provided +// for this purpose. MSVC does not provide va_copy, so define an +// implementation here. It is not guaranteed that assignment is a copy, so the +// StringUtil.VariableArgsFunc unit test tests this capability. +#if !defined(CHROMIUM_MOZILLA_BUILD) +inline void va_copy(va_list& a, va_list& b) { +#if defined(COMPILER_GCC) + ::va_copy(a, b); +#elif defined(COMPILER_MSVC) + a = b; +#endif +} + +#else +// The C standard says that va_copy is a "macro", not a function. Trying to +// use va_list as ref args to a function, as above, breaks some machines. +# if defined(COMPILER_GCC) +# define base_va_copy(_a, _b) ::va_copy(_a, _b) +# elif defined(COMPILER_MSVC) +# define base_va_copy(_a, _b) (_a = _b) +# else +# error No va_copy for your compiler +# endif + +#endif + +} // namespace base + +// Define an OS-neutral wrapper for shared library entry points +#if defined(OS_WIN) +#define API_CALL __stdcall +#elif defined(OS_LINUX) || defined(OS_MACOSX) +#define API_CALL +#endif + +#endif // BASE_PORT_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,93 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_PROCESS_H_ +#define BASE_PROCESS_H_ + +#include "base/basictypes.h" + +#include +#ifdef OS_WIN +#include +#endif + +namespace base { + +// ProcessHandle is a platform specific type which represents the underlying OS +// handle to a process. +// ProcessId is a number which identifies the process in the OS. +#if defined(OS_WIN) +typedef HANDLE ProcessHandle; +typedef DWORD ProcessId; +#elif defined(OS_POSIX) +// On POSIX, our ProcessHandle will just be the PID. +typedef pid_t ProcessHandle; +typedef pid_t ProcessId; +#endif + +class Process { + public: + Process() : process_(0), last_working_set_size_(0) {} + explicit Process(ProcessHandle handle) : + process_(handle), last_working_set_size_(0) {} + + // A handle to the current process. + static Process Current(); + + // Get/Set the handle for this process. The handle will be 0 if the process + // is no longer running. + ProcessHandle handle() const { return process_; } + void set_handle(ProcessHandle handle) { process_ = handle; } + + // Get the PID for this process. + ProcessId pid() const; + + // Is the this process the current process. + bool is_current() const; + + // Close the process handle. This will not terminate the process. + void Close(); + + // Terminates the process with extreme prejudice. The given result code will + // be the exit code of the process. If the process has already exited, this + // will do nothing. + void Terminate(int result_code); + + // A process is backgrounded when it's priority is lower than normal. + // Return true if this process is backgrounded, false otherwise. + bool IsProcessBackgrounded() const; + + // Set a prcess as backgrounded. If value is true, the priority + // of the process will be lowered. If value is false, the priority + // of the process will be made "normal" - equivalent to default + // process priority. + // Returns true if the priority was changed, false otherwise. + bool SetProcessBackgrounded(bool value); + + // Reduces the working set of memory used by the process. + // The algorithm used by this function is intentionally vague. Repeated calls + // to this function consider the process' previous required Working Set sizes + // to determine a reasonable reduction. This helps give memory back to the OS + // in increments without over releasing memory. + // When the WorkingSet is reduced, it is permanent, until the caller calls + // UnReduceWorkingSet. + // Returns true if successful, false otherwise. + bool ReduceWorkingSet(); + + // Undoes the effects of prior calls to ReduceWorkingSet(). + // Returns true if successful, false otherwise. + bool UnReduceWorkingSet(); + + // Releases as much of the working set back to the OS as possible. + // Returns true if successful, false otherwise. + bool EmptyWorkingSet(); + + private: + ProcessHandle process_; + size_t last_working_set_size_; +}; + +} // namespace base + +#endif // BASE_PROCESS_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process_posix.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,70 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process.h" +#include "base/process_util.h" + +namespace base { + +void Process::Close() { + process_ = 0; + // if the process wasn't termiated (so we waited) or the state + // wasn't already collected w/ a wait from process_utils, we're gonna + // end up w/ a zombie when it does finally exit. +} + +void Process::Terminate(int result_code) { + // result_code isn't supportable. + if (!process_) + return; + // We don't wait here. It's the responsibility of other code to reap the + // child. + KillProcess(process_, result_code, false); +} + +bool Process::IsProcessBackgrounded() const { + // http://code.google.com/p/chromium/issues/detail?id=8083 + return false; +} + +bool Process::SetProcessBackgrounded(bool value) { + // http://code.google.com/p/chromium/issues/detail?id=8083 + // Just say we did it to keep renderer happy at the moment. Need to finish + // cleaning this up w/in higher layers since windows is probably the only + // one that can raise priorities w/o privileges. + return true; +} + +bool Process::ReduceWorkingSet() { + // http://code.google.com/p/chromium/issues/detail?id=8083 + return false; +} + +bool Process::UnReduceWorkingSet() { + // http://code.google.com/p/chromium/issues/detail?id=8083 + return false; +} + +bool Process::EmptyWorkingSet() { + // http://code.google.com/p/chromium/issues/detail?id=8083 + return false; +} + +ProcessId Process::pid() const { + if (process_ == 0) + return 0; + + return GetProcId(process_); +} + +bool Process::is_current() const { + return process_ == GetCurrentProcessHandle(); +} + +// static +Process Process::Current() { + return Process(GetCurrentProcessHandle()); +} + +} // namspace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process_util.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,405 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file/namespace contains utility functions for enumerating, ending and +// computing statistics of processes. + +#ifndef BASE_PROCESS_UTIL_H_ +#define BASE_PROCESS_UTIL_H_ + +#include "base/basictypes.h" + +#if defined(OS_WIN) +#include +#include +#elif defined(OS_LINUX) +#include +#include +#include +#endif + +#include +#include +#include + +#include "base/command_line.h" +#include "base/process.h" + +#if defined(OS_WIN) +typedef PROCESSENTRY32 ProcessEntry; +typedef IO_COUNTERS IoCounters; +#elif defined(OS_POSIX) +// TODO(port): we should not rely on a Win32 structure. +struct ProcessEntry { + int pid; + int ppid; + char szExeFile[NAME_MAX + 1]; +}; + +struct IoCounters { + unsigned long long ReadOperationCount; + unsigned long long WriteOperationCount; + unsigned long long OtherOperationCount; + unsigned long long ReadTransferCount; + unsigned long long WriteTransferCount; + unsigned long long OtherTransferCount; +}; + +#include "base/file_descriptor_shuffle.h" +#endif + +#if defined(OS_MACOSX) +struct kinfo_proc; +#endif + +namespace base { + +// A minimalistic but hopefully cross-platform set of exit codes. +// Do not change the enumeration values or you will break third-party +// installers. +enum { + PROCESS_END_NORMAL_TERMINATON = 0, + PROCESS_END_KILLED_BY_USER = 1, + PROCESS_END_PROCESS_WAS_HUNG = 2 +}; + +// Returns the id of the current process. +ProcessId GetCurrentProcId(); + +// Returns the ProcessHandle of the current process. +ProcessHandle GetCurrentProcessHandle(); + +// Converts a PID to a process handle. This handle must be closed by +// CloseProcessHandle when you are done with it. Returns true on success. +bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle); + +// Converts a PID to a process handle. On Windows the handle is opened +// with more access rights and must only be used by trusted code. +// You have to close returned handle using CloseProcessHandle. Returns true +// on success. +bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle); + +// Closes the process handle opened by OpenProcessHandle. +void CloseProcessHandle(ProcessHandle process); + +// Returns the unique ID for the specified process. This is functionally the +// same as Windows' GetProcessId(), but works on versions of Windows before +// Win XP SP1 as well. +ProcessId GetProcId(ProcessHandle process); + +#if defined(OS_POSIX) +// Sets all file descriptors to close on exec except for stdin, stdout +// and stderr. +// TODO(agl): remove this function +// WARNING: do not use. It's inherently race-prone in the face of +// multi-threading. +void SetAllFDsToCloseOnExec(); +// Close all file descriptors, expect those which are a destination in the +// given multimap. Only call this function in a child process where you know +// that there aren't any other threads. +void CloseSuperfluousFds(const base::InjectiveMultimap& saved_map); +#endif + +#if defined(OS_WIN) +// Runs the given application name with the given command line. Normally, the +// first command line argument should be the path to the process, and don't +// forget to quote it. +// +// If wait is true, it will block and wait for the other process to finish, +// otherwise, it will just continue asynchronously. +// +// Example (including literal quotes) +// cmdline = "c:\windows\explorer.exe" -foo "c:\bar\" +// +// If process_handle is non-NULL, the process handle of the launched app will be +// stored there on a successful launch. +// NOTE: In this case, the caller is responsible for closing the handle so +// that it doesn't leak! +bool LaunchApp(const std::wstring& cmdline, + bool wait, bool start_hidden, ProcessHandle* process_handle); +#elif defined(OS_POSIX) +// Runs the application specified in argv[0] with the command line argv. +// Before launching all FDs open in the parent process will be marked as +// close-on-exec. |fds_to_remap| defines a mapping of src fd->dest fd to +// propagate FDs into the child process. +// +// As above, if wait is true, execute synchronously. The pid will be stored +// in process_handle if that pointer is non-null. +// +// Note that the first argument in argv must point to the filename, +// and must be fully specified. +typedef std::vector > file_handle_mapping_vector; +bool LaunchApp(const std::vector& argv, + const file_handle_mapping_vector& fds_to_remap, + bool wait, ProcessHandle* process_handle); + +#if defined(CHROMIUM_MOZILLA_BUILD) && defined(OS_LINUX) +typedef std::map environment_map; +bool LaunchApp(const std::vector& argv, + const file_handle_mapping_vector& fds_to_remap, + const environment_map& env_vars_to_set, + bool wait, ProcessHandle* process_handle); +#endif +#endif + +// Executes the application specified by cl. This function delegates to one +// of the above two platform-specific functions. +bool LaunchApp(const CommandLine& cl, + bool wait, bool start_hidden, ProcessHandle* process_handle); + +#if defined(OS_WIN) +// Executes the application specified by |cmd_line| and copies the contents +// printed to the standard output to |output|, which should be non NULL. +// Blocks until the started process terminates. +// Returns true if the application was run successfully, false otherwise. +bool GetAppOutput(const std::wstring& cmd_line, std::string* output); +#elif defined(OS_POSIX) +// Executes the application specified by |cl| and wait for it to exit. Stores +// the output (stdout) in |output|. Redirects stderr to /dev/null. Returns true +// on success (application launched and exited cleanly, with exit code +// indicating success). |output| is modified only when the function finished +// successfully. +bool GetAppOutput(const CommandLine& cl, std::string* output); +#endif + +// Used to filter processes by process ID. +class ProcessFilter { + public: + // Returns true to indicate set-inclusion and false otherwise. This method + // should not have side-effects and should be idempotent. + virtual bool Includes(ProcessId pid, ProcessId parent_pid) const = 0; + virtual ~ProcessFilter() { } +}; + +// Returns the number of processes on the machine that are running from the +// given executable name. If filter is non-null, then only processes selected +// by the filter will be counted. +int GetProcessCount(const std::wstring& executable_name, + const ProcessFilter* filter); + +// Attempts to kill all the processes on the current machine that were launched +// from the given executable name, ending them with the given exit code. If +// filter is non-null, then only processes selected by the filter are killed. +// Returns false if all processes were able to be killed off, false if at least +// one couldn't be killed. +bool KillProcesses(const std::wstring& executable_name, int exit_code, + const ProcessFilter* filter); + +// Attempts to kill the process identified by the given process +// entry structure, giving it the specified exit code. If |wait| is true, wait +// for the process to be actually terminated before returning. +// Returns true if this is successful, false otherwise. +bool KillProcess(ProcessHandle process, int exit_code, bool wait); +#if defined(OS_WIN) +bool KillProcessById(ProcessId process_id, int exit_code, bool wait); +#endif + +// Get the termination status (exit code) of the process and return true if the +// status indicates the process crashed. |child_exited| is set to true iff the +// child process has terminated. (|child_exited| may be NULL.) +// +// On Windows, it is an error to call this if the process hasn't terminated +// yet. On POSIX, |child_exited| is set correctly since we detect terminate in +// a different manner on POSIX. +bool DidProcessCrash(bool* child_exited, ProcessHandle handle); + +// Waits for process to exit. In POSIX systems, if the process hasn't been +// signaled then puts the exit code in |exit_code|; otherwise it's considered +// a failure. On Windows |exit_code| is always filled. Returns true on success, +// and closes |handle| in any case. +bool WaitForExitCode(ProcessHandle handle, int* exit_code); + +// Wait for all the processes based on the named executable to exit. If filter +// is non-null, then only processes selected by the filter are waited on. +// Returns after all processes have exited or wait_milliseconds have expired. +// Returns true if all the processes exited, false otherwise. +bool WaitForProcessesToExit(const std::wstring& executable_name, + int wait_milliseconds, + const ProcessFilter* filter); + +// Wait for a single process to exit. Return true if it exited cleanly within +// the given time limit. +bool WaitForSingleProcess(ProcessHandle handle, + int wait_milliseconds); + +// Returns true when |wait_milliseconds| have elapsed and the process +// is still running. +bool CrashAwareSleep(ProcessHandle handle, int wait_milliseconds); + +// Waits a certain amount of time (can be 0) for all the processes with a given +// executable name to exit, then kills off any of them that are still around. +// If filter is non-null, then only processes selected by the filter are waited +// on. Killed processes are ended with the given exit code. Returns false if +// any processes needed to be killed, true if they all exited cleanly within +// the wait_milliseconds delay. +bool CleanupProcesses(const std::wstring& executable_name, + int wait_milliseconds, + int exit_code, + const ProcessFilter* filter); + +// This class provides a way to iterate through the list of processes +// on the current machine that were started from the given executable +// name. To use, create an instance and then call NextProcessEntry() +// until it returns false. +class NamedProcessIterator { + public: + NamedProcessIterator(const std::wstring& executable_name, + const ProcessFilter* filter); + ~NamedProcessIterator(); + + // If there's another process that matches the given executable name, + // returns a const pointer to the corresponding PROCESSENTRY32. + // If there are no more matching processes, returns NULL. + // The returned pointer will remain valid until NextProcessEntry() + // is called again or this NamedProcessIterator goes out of scope. + const ProcessEntry* NextProcessEntry(); + + private: + // Determines whether there's another process (regardless of executable) + // left in the list of all processes. Returns true and sets entry_ to + // that process's info if there is one, false otherwise. + bool CheckForNextProcess(); + + bool IncludeEntry(); + + // Initializes a PROCESSENTRY32 data structure so that it's ready for + // use with Process32First/Process32Next. + void InitProcessEntry(ProcessEntry* entry); + + std::wstring executable_name_; + +#if defined(OS_WIN) + HANDLE snapshot_; + bool started_iteration_; +#elif defined(OS_LINUX) + DIR *procfs_dir_; +#elif defined(OS_MACOSX) + std::vector kinfo_procs_; + size_t index_of_kinfo_proc_; +#endif + ProcessEntry entry_; + const ProcessFilter* filter_; + + DISALLOW_EVIL_CONSTRUCTORS(NamedProcessIterator); +}; + +// Working Set (resident) memory usage broken down by +// priv (private): These pages (kbytes) cannot be shared with any other process. +// shareable: These pages (kbytes) can be shared with other processes under +// the right circumstances. +// shared : These pages (kbytes) are currently shared with at least one +// other process. +struct WorkingSetKBytes { + size_t priv; + size_t shareable; + size_t shared; +}; + +// Committed (resident + paged) memory usage broken down by +// private: These pages cannot be shared with any other process. +// mapped: These pages are mapped into the view of a section (backed by +// pagefile.sys) +// image: These pages are mapped into the view of an image section (backed by +// file system) +struct CommittedKBytes { + size_t priv; + size_t mapped; + size_t image; +}; + +// Free memory (Megabytes marked as free) in the 2G process address space. +// total : total amount in megabytes marked as free. Maximum value is 2048. +// largest : size of the largest contiguous amount of memory found. It is +// always smaller or equal to FreeMBytes::total. +// largest_ptr: starting address of the largest memory block. +struct FreeMBytes { + size_t total; + size_t largest; + void* largest_ptr; +}; + +// Provides performance metrics for a specified process (CPU usage, memory and +// IO counters). To use it, invoke CreateProcessMetrics() to get an instance +// for a specific process, then access the information with the different get +// methods. +class ProcessMetrics { + public: + // Creates a ProcessMetrics for the specified process. + // The caller owns the returned object. + static ProcessMetrics* CreateProcessMetrics(ProcessHandle process); + + ~ProcessMetrics(); + + // Returns the current space allocated for the pagefile, in bytes (these pages + // may or may not be in memory). + size_t GetPagefileUsage() const; + // Returns the peak space allocated for the pagefile, in bytes. + size_t GetPeakPagefileUsage() const; + // Returns the current working set size, in bytes. + size_t GetWorkingSetSize() const; + // Returns private usage, in bytes. Private bytes is the amount + // of memory currently allocated to a process that cannot be shared. + // Note: returns 0 on unsupported OSes: prior to XP SP2. + size_t GetPrivateBytes() const; + // Fills a CommittedKBytes with both resident and paged + // memory usage as per definition of CommittedBytes. + void GetCommittedKBytes(CommittedKBytes* usage) const; + // Fills a WorkingSetKBytes containing resident private and shared memory + // usage in bytes, as per definition of WorkingSetBytes. + bool GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const; + + // Computes the current process available memory for allocation. + // It does a linear scan of the address space querying each memory region + // for its free (unallocated) status. It is useful for estimating the memory + // load and fragmentation. + bool CalculateFreeMemory(FreeMBytes* free) const; + + // Returns the CPU usage in percent since the last time this method was + // called. The first time this method is called it returns 0 and will return + // the actual CPU info on subsequent calls. + // Note that on multi-processor machines, the CPU usage value is for all + // CPUs. So if you have 2 CPUs and your process is using all the cycles + // of 1 CPU and not the other CPU, this method returns 50. + int GetCPUUsage(); + + // Retrieves accounting information for all I/O operations performed by the + // process. + // If IO information is retrieved successfully, the function returns true + // and fills in the IO_COUNTERS passed in. The function returns false + // otherwise. + bool GetIOCounters(IoCounters* io_counters) const; + + private: + explicit ProcessMetrics(ProcessHandle process); + + ProcessHandle process_; + + int processor_count_; + + // Used to store the previous times so we can compute the CPU usage. + int64 last_time_; + int64 last_system_time_; + + DISALLOW_EVIL_CONSTRUCTORS(ProcessMetrics); +}; + +// Enables low fragmentation heap (LFH) for every heaps of this process. This +// won't have any effect on heaps created after this function call. It will not +// modify data allocated in the heaps before calling this function. So it is +// better to call this function early in initialization and again before +// entering the main loop. +// Note: Returns true on Windows 2000 without doing anything. +bool EnableLowFragmentationHeap(); + +// Enable 'terminate on heap corruption' flag. Helps protect against heap +// overflow. Has no effect if the OS doesn't provide the necessary facility. +void EnableTerminationOnHeapCorruption(); + +// If supported on the platform, and the user has sufficent rights, increase +// the current process's scheduling priority to a high priority. +void RaiseProcessToHighPriority(); + +} // namespace base + +#endif // BASE_PROCESS_UTIL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process_util_linux.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process_util_linux.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process_util_linux.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process_util_linux.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,256 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process_util.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "base/debug_util.h" +#include "base/eintr_wrapper.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/string_tokenizer.h" +#include "base/string_util.h" + +namespace { + +enum ParsingState { + KEY_NAME, + KEY_VALUE +}; + +static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG"); + +} // namespace + +namespace base { + +#if defined(CHROMIUM_MOZILLA_BUILD) +bool LaunchApp(const std::vector& argv, + const file_handle_mapping_vector& fds_to_remap, + bool wait, ProcessHandle* process_handle) { + return LaunchApp(argv, fds_to_remap, environment_map(), + wait, process_handle); +} +#endif + +bool LaunchApp(const std::vector& argv, + const file_handle_mapping_vector& fds_to_remap, +#if defined(CHROMIUM_MOZILLA_BUILD) + const environment_map& env_vars_to_set, +#endif + bool wait, ProcessHandle* process_handle) { + pid_t pid = fork(); + if (pid < 0) + return false; + + if (pid == 0) { + InjectiveMultimap fd_shuffle; + for (file_handle_mapping_vector::const_iterator + it = fds_to_remap.begin(); it != fds_to_remap.end(); ++it) { + fd_shuffle.push_back(InjectionArc(it->first, it->second, false)); + } + + if (!ShuffleFileDescriptors(fd_shuffle)) + exit(127); + + CloseSuperfluousFds(fd_shuffle); + +#if defined(CHROMIUM_MOZILLA_BUILD) + for (environment_map::const_iterator it = env_vars_to_set.begin(); + it != env_vars_to_set.end(); ++it) { + if (setenv(it->first.c_str(), it->second.c_str(), 1/*overwrite*/)) + exit(127); + } +#endif + + scoped_array argv_cstr(new char*[argv.size() + 1]); + for (size_t i = 0; i < argv.size(); i++) + argv_cstr[i] = const_cast(argv[i].c_str()); + argv_cstr[argv.size()] = NULL; + execvp(argv_cstr[0], argv_cstr.get()); +#if defined(CHROMIUM_MOZILLA_BUILD) + // if we get here, we're in serious trouble and should complain loudly + DLOG(ERROR) << "FAILED TO exec() CHILD PROCESS, path: " << argv_cstr[0]; +#endif + exit(127); + } else { + gProcessLog.print("==> process %d launched child process %d\n", + GetCurrentProcId(), pid); + if (wait) + HANDLE_EINTR(waitpid(pid, 0, 0)); + + if (process_handle) + *process_handle = pid; + } + + return true; +} + +bool LaunchApp(const CommandLine& cl, + bool wait, bool start_hidden, + ProcessHandle* process_handle) { + file_handle_mapping_vector no_files; + return LaunchApp(cl.argv(), no_files, wait, process_handle); +} + +NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name, + const ProcessFilter* filter) + : executable_name_(executable_name), filter_(filter) { + procfs_dir_ = opendir("/proc"); +} + +NamedProcessIterator::~NamedProcessIterator() { + if (procfs_dir_) { + closedir(procfs_dir_); + procfs_dir_ = NULL; + } +} + +const ProcessEntry* NamedProcessIterator::NextProcessEntry() { + bool result = false; + do { + result = CheckForNextProcess(); + } while (result && !IncludeEntry()); + + if (result) + return &entry_; + + return NULL; +} + +bool NamedProcessIterator::CheckForNextProcess() { + // TODO(port): skip processes owned by different UID + + dirent* slot = 0; + const char* openparen; + const char* closeparen; + + // Arbitrarily guess that there will never be more than 200 non-process + // files in /proc. Hardy has 53. + int skipped = 0; + const int kSkipLimit = 200; + while (skipped < kSkipLimit) { + slot = readdir(procfs_dir_); + // all done looking through /proc? + if (!slot) + return false; + + // If not a process, keep looking for one. + bool notprocess = false; + int i; + for (i = 0; i < NAME_MAX && slot->d_name[i]; ++i) { + if (!isdigit(slot->d_name[i])) { + notprocess = true; + break; + } + } + if (i == NAME_MAX || notprocess) { + skipped++; + continue; + } + + // Read the process's status. + char buf[NAME_MAX + 12]; + sprintf(buf, "/proc/%s/stat", slot->d_name); + FILE *fp = fopen(buf, "r"); + if (!fp) + return false; + const char* result = fgets(buf, sizeof(buf), fp); + fclose(fp); + if (!result) + return false; + + // Parse the status. It is formatted like this: + // %d (%s) %c %d ... + // pid (name) runstate ppid + // To avoid being fooled by names containing a closing paren, scan + // backwards. + openparen = strchr(buf, '('); + closeparen = strrchr(buf, ')'); + if (!openparen || !closeparen) + return false; + char runstate = closeparen[2]; + + // Is the process in 'Zombie' state, i.e. dead but waiting to be reaped? + // Allowed values: D R S T Z + if (runstate != 'Z') + break; + + // Nope, it's a zombie; somebody isn't cleaning up after their children. + // (e.g. WaitForProcessesToExit doesn't clean up after dead children yet.) + // There could be a lot of zombies, can't really decrement i here. + } + if (skipped >= kSkipLimit) { + NOTREACHED(); + return false; + } + + entry_.pid = atoi(slot->d_name); + entry_.ppid = atoi(closeparen + 3); + + // TODO(port): read pid's commandline's $0, like killall does. Using the + // short name between openparen and closeparen won't work for long names! + int len = closeparen - openparen - 1; + if (len > NAME_MAX) + len = NAME_MAX; + memcpy(entry_.szExeFile, openparen + 1, len); + entry_.szExeFile[len] = 0; + + return true; +} + +bool NamedProcessIterator::IncludeEntry() { + // TODO(port): make this also work for non-ASCII filenames + if (WideToASCII(executable_name_) != entry_.szExeFile) + return false; + if (!filter_) + return true; + return filter_->Includes(entry_.pid, entry_.ppid); +} + +// To have /proc/self/io file you must enable CONFIG_TASK_IO_ACCOUNTING +// in your kernel configuration. +bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { + std::string proc_io_contents; + if (!file_util::ReadFileToString(L"/proc/self/io", &proc_io_contents)) + return false; + + (*io_counters).OtherOperationCount = 0; + (*io_counters).OtherTransferCount = 0; + + StringTokenizer tokenizer(proc_io_contents, ": \n"); + ParsingState state = KEY_NAME; + std::string last_key_name; + while (tokenizer.GetNext()) { + switch (state) { + case KEY_NAME: + last_key_name = tokenizer.token(); + state = KEY_VALUE; + break; + case KEY_VALUE: + DCHECK(!last_key_name.empty()); + if (last_key_name == "syscr") { + (*io_counters).ReadOperationCount = StringToInt64(tokenizer.token()); + } else if (last_key_name == "syscw") { + (*io_counters).WriteOperationCount = StringToInt64(tokenizer.token()); + } else if (last_key_name == "rchar") { + (*io_counters).ReadTransferCount = StringToInt64(tokenizer.token()); + } else if (last_key_name == "wchar") { + (*io_counters).WriteTransferCount = StringToInt64(tokenizer.token()); + } + state = KEY_NAME; + break; + } + } + return true; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process_util_mac.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process_util_mac.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process_util_mac.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process_util_mac.mm 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,236 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +#include "base/process_util.h" + +#import +#include +#include +#include +#include +#include + +#include + +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "base/time.h" + +namespace base { + +bool LaunchApp(const std::vector& argv, + const file_handle_mapping_vector& fds_to_remap, + bool wait, ProcessHandle* process_handle) { + bool retval = true; + + char* argv_copy[argv.size() + 1]; + for (size_t i = 0; i < argv.size(); i++) { + argv_copy[i] = const_cast(argv[i].c_str()); + } + argv_copy[argv.size()] = NULL; + + // Make sure we don't leak any FDs to the child process by marking all FDs + // as close-on-exec. + SetAllFDsToCloseOnExec(); + + posix_spawn_file_actions_t file_actions; + if (posix_spawn_file_actions_init(&file_actions) != 0) { + return false; + } + + // Turn fds_to_remap array into a set of dup2 calls. + for (file_handle_mapping_vector::const_iterator it = fds_to_remap.begin(); + it != fds_to_remap.end(); + ++it) { + int src_fd = it->first; + int dest_fd = it->second; + + if (src_fd == dest_fd) { + int flags = fcntl(src_fd, F_GETFD); + if (flags != -1) { + fcntl(src_fd, F_SETFD, flags & ~FD_CLOEXEC); + } + } else { + if (posix_spawn_file_actions_adddup2(&file_actions, src_fd, dest_fd) != 0) + { + posix_spawn_file_actions_destroy(&file_actions); + return false; + } + } + } + + int pid = 0; + int spawn_succeeded = (posix_spawnp(&pid, + argv_copy[0], + &file_actions, + NULL, + argv_copy, + *_NSGetEnviron()) == 0); + + posix_spawn_file_actions_destroy(&file_actions); + + bool process_handle_valid = pid > 0; + if (!spawn_succeeded || !process_handle_valid) { + retval = false; + } else { + if (wait) + HANDLE_EINTR(waitpid(pid, 0, 0)); + + if (process_handle) + *process_handle = pid; + } + + return retval; +} + +bool LaunchApp(const CommandLine& cl, + bool wait, bool start_hidden, ProcessHandle* process_handle) { + // TODO(playmobil): Do we need to respect the start_hidden flag? + file_handle_mapping_vector no_files; + return LaunchApp(cl.argv(), no_files, wait, process_handle); +} + +NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name, + const ProcessFilter* filter) + : executable_name_(executable_name), + index_of_kinfo_proc_(0), + filter_(filter) { + // Get a snapshot of all of my processes (yes, as we loop it can go stale, but + // but trying to find where we were in a constantly changing list is basically + // impossible. + + int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_UID, geteuid() }; + + // Since more processes could start between when we get the size and when + // we get the list, we do a loop to keep trying until we get it. + bool done = false; + int try_num = 1; + const int max_tries = 10; + do { + // Get the size of the buffer + size_t len = 0; + if (sysctl(mib, arraysize(mib), NULL, &len, NULL, 0) < 0) { + LOG(ERROR) << "failed to get the size needed for the process list"; + kinfo_procs_.resize(0); + done = true; + } else { + size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); + // Leave some spare room for process table growth (more could show up + // between when we check and now) + num_of_kinfo_proc += 4; + kinfo_procs_.resize(num_of_kinfo_proc); + len = num_of_kinfo_proc * sizeof(struct kinfo_proc); + // Load the list of processes + if (sysctl(mib, arraysize(mib), &kinfo_procs_[0], &len, NULL, 0) < 0) { + // If we get a mem error, it just means we need a bigger buffer, so + // loop around again. Anything else is a real error and give up. + if (errno != ENOMEM) { + LOG(ERROR) << "failed to get the process list"; + kinfo_procs_.resize(0); + done = true; + } + } else { + // Got the list, just make sure we're sized exactly right + size_t num_of_kinfo_proc = len / sizeof(struct kinfo_proc); + kinfo_procs_.resize(num_of_kinfo_proc); + done = true; + } + } + } while (!done && (try_num++ < max_tries)); + + if (!done) { + LOG(ERROR) << "failed to collect the process list in a few tries"; + kinfo_procs_.resize(0); + } +} + +NamedProcessIterator::~NamedProcessIterator() { +} + +const ProcessEntry* NamedProcessIterator::NextProcessEntry() { + bool result = false; + do { + result = CheckForNextProcess(); + } while (result && !IncludeEntry()); + + if (result) { + return &entry_; + } + + return NULL; +} + +bool NamedProcessIterator::CheckForNextProcess() { + std::string executable_name_utf8(WideToUTF8(executable_name_)); + + std::string data; + std::string exec_name; + + for (; index_of_kinfo_proc_ < kinfo_procs_.size(); ++index_of_kinfo_proc_) { + kinfo_proc* kinfo = &kinfo_procs_[index_of_kinfo_proc_]; + + // Skip processes just awaiting collection + if ((kinfo->kp_proc.p_pid > 0) && (kinfo->kp_proc.p_stat == SZOMB)) + continue; + + int mib[] = { CTL_KERN, KERN_PROCARGS, kinfo->kp_proc.p_pid }; + + // Found out what size buffer we need + size_t data_len = 0; + if (sysctl(mib, arraysize(mib), NULL, &data_len, NULL, 0) < 0) { + LOG(ERROR) << "failed to figure out the buffer size for a commandline"; + continue; + } + + data.resize(data_len); + if (sysctl(mib, arraysize(mib), &data[0], &data_len, NULL, 0) < 0) { + LOG(ERROR) << "failed to fetch a commandline"; + continue; + } + + // Data starts w/ the full path null termed, so we have to extract just the + // executable name from the path. + + size_t exec_name_end = data.find('\0'); + if (exec_name_end == std::string::npos) { + LOG(ERROR) << "command line data didn't match expected format"; + continue; + } + size_t last_slash = data.rfind('/', exec_name_end); + if (last_slash == std::string::npos) + exec_name = data.substr(0, exec_name_end); + else + exec_name = data.substr(last_slash + 1, exec_name_end - last_slash - 1); + + // Check the name + if (executable_name_utf8 == exec_name) { + entry_.pid = kinfo->kp_proc.p_pid; + entry_.ppid = kinfo->kp_eproc.e_ppid; + base::strlcpy(entry_.szExeFile, exec_name.c_str(), + sizeof(entry_.szExeFile)); + // Start w/ the next entry next time through + ++index_of_kinfo_proc_; + // Done + return true; + } + } + return false; +} + +bool NamedProcessIterator::IncludeEntry() { + // Don't need to check the name, we did that w/in CheckForNextProcess. + if (!filter_) + return true; + return filter_->Includes(entry_.pid, entry_.ppid); +} + +bool ProcessMetrics::GetIOCounters(IoCounters* io_counters) const { + // TODO(pinkerton): can we implement this? On linux it relies on /proc. + NOTIMPLEMENTED(); + return false; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process_util_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process_util_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process_util_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process_util_posix.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,537 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "base/basictypes.h" +#include "base/eintr_wrapper.h" +#include "base/logging.h" +#include "base/platform_thread.h" +#include "base/process_util.h" +#include "base/scoped_ptr.h" +#include "base/sys_info.h" +#include "base/time.h" +#include "base/waitable_event.h" + +const int kMicrosecondsPerSecond = 1000000; + +namespace base { + +ProcessId GetCurrentProcId() { + return getpid(); +} + +ProcessHandle GetCurrentProcessHandle() { + return GetCurrentProcId(); +} + +bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) { + // On Posix platforms, process handles are the same as PIDs, so we + // don't need to do anything. + *handle = pid; + return true; +} + +bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) { + // On POSIX permissions are checked for each operation on process, + // not when opening a "handle". + return OpenProcessHandle(pid, handle); +} + +void CloseProcessHandle(ProcessHandle process) { + // See OpenProcessHandle, nothing to do. + return; +} + +ProcessId GetProcId(ProcessHandle process) { + return process; +} + +// Attempts to kill the process identified by the given process +// entry structure. Ignores specified exit_code; posix can't force that. +// Returns true if this is successful, false otherwise. +bool KillProcess(ProcessHandle process_id, int exit_code, bool wait) { + bool result = kill(process_id, SIGTERM) == 0; + + if (result && wait) { + int tries = 60; + // The process may not end immediately due to pending I/O + while (tries-- > 0) { + int pid = HANDLE_EINTR(waitpid(process_id, NULL, WNOHANG)); + if (pid == process_id) + break; + + sleep(1); + } + + result = kill(process_id, SIGKILL) == 0; + } + + if (!result) + DLOG(ERROR) << "Unable to terminate process."; + + return result; +} + +// A class to handle auto-closing of DIR*'s. +class ScopedDIRClose { + public: + inline void operator()(DIR* x) const { + if (x) { + closedir(x); + } + } +}; +typedef scoped_ptr_malloc ScopedDIR; + +void CloseSuperfluousFds(const base::InjectiveMultimap& saved_mapping) { +#if defined(OS_LINUX) + static const rlim_t kSystemDefaultMaxFds = 8192; + static const char fd_dir[] = "/proc/self/fd"; +#elif defined(OS_MACOSX) + static const rlim_t kSystemDefaultMaxFds = 256; + static const char fd_dir[] = "/dev/fd"; +#endif + std::set saved_fds; + + // Get the maximum number of FDs possible. + struct rlimit nofile; + rlim_t max_fds; + if (getrlimit(RLIMIT_NOFILE, &nofile)) { + // getrlimit failed. Take a best guess. + max_fds = kSystemDefaultMaxFds; + DLOG(ERROR) << "getrlimit(RLIMIT_NOFILE) failed: " << errno; + } else { + max_fds = nofile.rlim_cur; + } + + if (max_fds > INT_MAX) + max_fds = INT_MAX; + + // Don't close stdin, stdout and stderr + saved_fds.insert(STDIN_FILENO); + saved_fds.insert(STDOUT_FILENO); + saved_fds.insert(STDERR_FILENO); + + for (base::InjectiveMultimap::const_iterator + i = saved_mapping.begin(); i != saved_mapping.end(); ++i) { + saved_fds.insert(i->dest); + } + + ScopedDIR dir_closer(opendir(fd_dir)); + DIR *dir = dir_closer.get(); + if (NULL == dir) { + DLOG(ERROR) << "Unable to open " << fd_dir; + + // Fallback case: Try every possible fd. + for (rlim_t i = 0; i < max_fds; ++i) { + const int fd = static_cast(i); + if (saved_fds.find(fd) != saved_fds.end()) + continue; + + HANDLE_EINTR(close(fd)); + } + return; + } + + struct dirent *ent; + while ((ent = readdir(dir))) { + // Skip . and .. entries. + if (ent->d_name[0] == '.') + continue; + + char *endptr; + errno = 0; + const long int fd = strtol(ent->d_name, &endptr, 10); + if (ent->d_name[0] == 0 || *endptr || fd < 0 || errno) + continue; + if (saved_fds.find(fd) != saved_fds.end()) + continue; + + // When running under Valgrind, Valgrind opens several FDs for its + // own use and will complain if we try to close them. All of + // these FDs are >= |max_fds|, so we can check against that here + // before closing. See https://bugs.kde.org/show_bug.cgi?id=191758 + if (fd < static_cast(max_fds)) + HANDLE_EINTR(close(fd)); + } +} + +// Sets all file descriptors to close on exec except for stdin, stdout +// and stderr. +// TODO(agl): Remove this function. It's fundamentally broken for multithreaded +// apps. +void SetAllFDsToCloseOnExec() { +#if defined(OS_LINUX) + const char fd_dir[] = "/proc/self/fd"; +#elif defined(OS_MACOSX) + const char fd_dir[] = "/dev/fd"; +#endif + ScopedDIR dir_closer(opendir(fd_dir)); + DIR *dir = dir_closer.get(); + if (NULL == dir) { + DLOG(ERROR) << "Unable to open " << fd_dir; + return; + } + + struct dirent *ent; + while ((ent = readdir(dir))) { + // Skip . and .. entries. + if (ent->d_name[0] == '.') + continue; + int i = atoi(ent->d_name); + // We don't close stdin, stdout or stderr. + if (i <= STDERR_FILENO) + continue; + + int flags = fcntl(i, F_GETFD); + if ((flags == -1) || (fcntl(i, F_SETFD, flags | FD_CLOEXEC) == -1)) { + DLOG(ERROR) << "fcntl failure."; + } + } +} + +ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process), + last_time_(0), + last_system_time_(0) { + processor_count_ = base::SysInfo::NumberOfProcessors(); +} + +// static +ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { + return new ProcessMetrics(process); +} + +ProcessMetrics::~ProcessMetrics() { } + +void EnableTerminationOnHeapCorruption() { + // On POSIX, there nothing to do AFAIK. +} + +void RaiseProcessToHighPriority() { + // On POSIX, we don't actually do anything here. We could try to nice() or + // setpriority() or sched_getscheduler, but these all require extra rights. +} + +bool DidProcessCrash(bool* child_exited, ProcessHandle handle) { + int status; + const int result = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); + if (result == -1) { + LOG(ERROR) << "waitpid failed pid:" << handle << " errno:" << errno; + if (child_exited) + *child_exited = false; + return false; + } else if (result == 0) { + // the child hasn't exited yet. + if (child_exited) + *child_exited = false; + return false; + } + + if (child_exited) + *child_exited = true; + + if (WIFSIGNALED(status)) { + switch(WTERMSIG(status)) { + case SIGSEGV: + case SIGILL: + case SIGABRT: + case SIGFPE: + return true; + default: + return false; + } + } + + if (WIFEXITED(status)) + return WEXITSTATUS(status) != 0; + + return false; +} + +bool WaitForExitCode(ProcessHandle handle, int* exit_code) { + int status; + if (HANDLE_EINTR(waitpid(handle, &status, 0)) == -1) { + NOTREACHED(); + return false; + } + + if (WIFEXITED(status)) { + *exit_code = WEXITSTATUS(status); + return true; + } + + // If it didn't exit cleanly, it must have been signaled. + DCHECK(WIFSIGNALED(status)); + return false; +} + +namespace { + +int WaitpidWithTimeout(ProcessHandle handle, int wait_milliseconds, + bool* success) { + // This POSIX version of this function only guarantees that we wait no less + // than |wait_milliseconds| for the proces to exit. The child process may + // exit sometime before the timeout has ended but we may still block for + // up to 0.25 seconds after the fact. + // + // waitpid() has no direct support on POSIX for specifying a timeout, you can + // either ask it to block indefinitely or return immediately (WNOHANG). + // When a child process terminates a SIGCHLD signal is sent to the parent. + // Catching this signal would involve installing a signal handler which may + // affect other parts of the application and would be difficult to debug. + // + // Our strategy is to call waitpid() once up front to check if the process + // has already exited, otherwise to loop for wait_milliseconds, sleeping for + // at most 0.25 secs each time using usleep() and then calling waitpid(). + // + // usleep() is speced to exit if a signal is received for which a handler + // has been installed. This means that when a SIGCHLD is sent, it will exit + // depending on behavior external to this function. + // + // This function is used primarily for unit tests, if we want to use it in + // the application itself it would probably be best to examine other routes. + int status = -1; + pid_t ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); + static const int64 kQuarterSecondInMicroseconds = kMicrosecondsPerSecond/4; + + // If the process hasn't exited yet, then sleep and try again. + Time wakeup_time = Time::Now() + TimeDelta::FromMilliseconds( + wait_milliseconds); + while (ret_pid == 0) { + Time now = Time::Now(); + if (now > wakeup_time) + break; + // Guaranteed to be non-negative! + int64 sleep_time_usecs = (wakeup_time - now).InMicroseconds(); + // Don't sleep for more than 0.25 secs at a time. + if (sleep_time_usecs > kQuarterSecondInMicroseconds) { + sleep_time_usecs = kQuarterSecondInMicroseconds; + } + + // usleep() will return 0 and set errno to EINTR on receipt of a signal + // such as SIGCHLD. + usleep(sleep_time_usecs); + ret_pid = HANDLE_EINTR(waitpid(handle, &status, WNOHANG)); + } + + if (success) + *success = (ret_pid != -1); + + return status; +} + +} // namespace + +bool WaitForSingleProcess(ProcessHandle handle, int wait_milliseconds) { + bool waitpid_success; + int status; + if (wait_milliseconds == base::kNoTimeout) + waitpid_success = (HANDLE_EINTR(waitpid(handle, &status, 0)) != -1); + else + status = WaitpidWithTimeout(handle, wait_milliseconds, &waitpid_success); + if (status != -1) { + DCHECK(waitpid_success); + return WIFEXITED(status); + } else { + return false; + } +} + +bool CrashAwareSleep(ProcessHandle handle, int wait_milliseconds) { + bool waitpid_success; + int status = WaitpidWithTimeout(handle, wait_milliseconds, &waitpid_success); + if (status != -1) { + DCHECK(waitpid_success); + return !(WIFEXITED(status) || WIFSIGNALED(status)); + } else { + // If waitpid returned with an error, then the process doesn't exist + // (which most probably means it didn't exist before our call). + return waitpid_success; + } +} + +namespace { + +int64 TimeValToMicroseconds(const struct timeval& tv) { + return tv.tv_sec * kMicrosecondsPerSecond + tv.tv_usec; +} + +} + +int ProcessMetrics::GetCPUUsage() { + struct timeval now; + struct rusage usage; + + int retval = gettimeofday(&now, NULL); + if (retval) + return 0; + retval = getrusage(RUSAGE_SELF, &usage); + if (retval) + return 0; + + int64 system_time = (TimeValToMicroseconds(usage.ru_stime) + + TimeValToMicroseconds(usage.ru_utime)) / + processor_count_; + int64 time = TimeValToMicroseconds(now); + + if ((last_system_time_ == 0) || (last_time_ == 0)) { + // First call, just set the last values. + last_system_time_ = system_time; + last_time_ = time; + return 0; + } + + int64 system_time_delta = system_time - last_system_time_; + int64 time_delta = time - last_time_; + DCHECK(time_delta != 0); + if (time_delta == 0) + return 0; + + // We add time_delta / 2 so the result is rounded. + int cpu = static_cast((system_time_delta * 100 + time_delta / 2) / + time_delta); + + last_system_time_ = system_time; + last_time_ = time; + + return cpu; +} + +bool GetAppOutput(const CommandLine& cl, std::string* output) { + int pipe_fd[2]; + pid_t pid; + + if (pipe(pipe_fd) < 0) + return false; + + switch (pid = fork()) { + case -1: // error + close(pipe_fd[0]); + close(pipe_fd[1]); + return false; + case 0: // child + { + int dev_null = open("/dev/null", O_WRONLY); + if (dev_null < 0) + exit(127); + + InjectiveMultimap fd_shuffle; + fd_shuffle.push_back(InjectionArc(pipe_fd[1], STDOUT_FILENO, true)); + fd_shuffle.push_back(InjectionArc(dev_null, STDERR_FILENO, true)); + fd_shuffle.push_back(InjectionArc(dev_null, STDIN_FILENO, true)); + + if (!ShuffleFileDescriptors(fd_shuffle)) + exit(127); + + CloseSuperfluousFds(fd_shuffle); + + const std::vector argv = cl.argv(); + scoped_array argv_cstr(new char*[argv.size() + 1]); + for (size_t i = 0; i < argv.size(); i++) + argv_cstr[i] = const_cast(argv[i].c_str()); + argv_cstr[argv.size()] = NULL; + execvp(argv_cstr[0], argv_cstr.get()); + exit(127); + } + default: // parent + { + // Close our writing end of pipe now. Otherwise later read would not + // be able to detect end of child's output (in theory we could still + // write to the pipe). + close(pipe_fd[1]); + + int exit_code = EXIT_FAILURE; + bool success = WaitForExitCode(pid, &exit_code); + if (!success || exit_code != EXIT_SUCCESS) { + close(pipe_fd[0]); + return false; + } + + char buffer[256]; + std::string buf_output; + + while (true) { + ssize_t bytes_read = + HANDLE_EINTR(read(pipe_fd[0], buffer, sizeof(buffer))); + if (bytes_read <= 0) + break; + buf_output.append(buffer, bytes_read); + } + output->swap(buf_output); + close(pipe_fd[0]); + return true; + } + } +} + +int GetProcessCount(const std::wstring& executable_name, + const ProcessFilter* filter) { + int count = 0; + + NamedProcessIterator iter(executable_name, filter); + while (iter.NextProcessEntry()) + ++count; + return count; +} + +bool KillProcesses(const std::wstring& executable_name, int exit_code, + const ProcessFilter* filter) { + bool result = true; + const ProcessEntry* entry; + + NamedProcessIterator iter(executable_name, filter); + while ((entry = iter.NextProcessEntry()) != NULL) + result = KillProcess((*entry).pid, exit_code, true) && result; + + return result; +} + +bool WaitForProcessesToExit(const std::wstring& executable_name, + int wait_milliseconds, + const ProcessFilter* filter) { + bool result = false; + + // TODO(port): This is inefficient, but works if there are multiple procs. + // TODO(port): use waitpid to avoid leaving zombies around + + base::Time end_time = base::Time::Now() + + base::TimeDelta::FromMilliseconds(wait_milliseconds); + do { + NamedProcessIterator iter(executable_name, filter); + if (!iter.NextProcessEntry()) { + result = true; + break; + } + PlatformThread::Sleep(100); + } while ((base::Time::Now() - end_time) > base::TimeDelta()); + + return result; +} + +bool CleanupProcesses(const std::wstring& executable_name, + int wait_milliseconds, + int exit_code, + const ProcessFilter* filter) { + bool exited_cleanly = + WaitForProcessesToExit(executable_name, wait_milliseconds, + filter); + if (!exited_cleanly) + KillProcesses(executable_name, exit_code, filter); + return exited_cleanly; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process_util_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process_util_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process_util_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process_util_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,274 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#define _CRT_SECURE_NO_WARNINGS + +#include "base/command_line.h" +#include "base/eintr_wrapper.h" +#include "base/file_path.h" +#include "base/multiprocess_test.h" +#include "base/path_service.h" +#include "base/platform_thread.h" +#include "base/process_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(OS_LINUX) +#include +#endif +#if defined(OS_POSIX) +#include +#include +#endif +#if defined(OS_WIN) +#include +#endif + +namespace base { + +class ProcessUtilTest : public MultiProcessTest { +}; + +MULTIPROCESS_TEST_MAIN(SimpleChildProcess) { + return 0; +} + +TEST_F(ProcessUtilTest, SpawnChild) { + ProcessHandle handle = this->SpawnChild(L"SimpleChildProcess"); + + ASSERT_NE(static_cast(NULL), handle); + EXPECT_TRUE(WaitForSingleProcess(handle, 5000)); + base::CloseProcessHandle(handle); +} + +MULTIPROCESS_TEST_MAIN(SlowChildProcess) { + // Sleep until file "SlowChildProcess.die" is created. + FILE *fp; + do { + PlatformThread::Sleep(100); + fp = fopen("SlowChildProcess.die", "r"); + } while (!fp); + fclose(fp); + remove("SlowChildProcess.die"); + exit(0); + return 0; +} + +TEST_F(ProcessUtilTest, KillSlowChild) { + remove("SlowChildProcess.die"); + ProcessHandle handle = this->SpawnChild(L"SlowChildProcess"); + ASSERT_NE(static_cast(NULL), handle); + FILE *fp = fopen("SlowChildProcess.die", "w"); + fclose(fp); + EXPECT_TRUE(base::WaitForSingleProcess(handle, 5000)); + base::CloseProcessHandle(handle); +} + +// TODO(estade): if possible, port these 2 tests. +#if defined(OS_WIN) +TEST_F(ProcessUtilTest, EnableLFH) { + ASSERT_TRUE(EnableLowFragmentationHeap()); + if (IsDebuggerPresent()) { + // Under these conditions, LFH can't be enabled. There's no point to test + // anything. + const char* no_debug_env = getenv("_NO_DEBUG_HEAP"); + if (!no_debug_env || strcmp(no_debug_env, "1")) + return; + } + HANDLE heaps[1024] = { 0 }; + unsigned number_heaps = GetProcessHeaps(1024, heaps); + EXPECT_GT(number_heaps, 0u); + for (unsigned i = 0; i < number_heaps; ++i) { + ULONG flag = 0; + SIZE_T length; + ASSERT_NE(0, HeapQueryInformation(heaps[i], + HeapCompatibilityInformation, + &flag, + sizeof(flag), + &length)); + // If flag is 0, the heap is a standard heap that does not support + // look-asides. If flag is 1, the heap supports look-asides. If flag is 2, + // the heap is a low-fragmentation heap (LFH). Note that look-asides are not + // supported on the LFH. + + // We don't have any documented way of querying the HEAP_NO_SERIALIZE flag. + EXPECT_LE(flag, 2u); + EXPECT_NE(flag, 1u); + } +} + +TEST_F(ProcessUtilTest, CalcFreeMemory) { + ProcessMetrics* metrics = + ProcessMetrics::CreateProcessMetrics(::GetCurrentProcess()); + ASSERT_TRUE(NULL != metrics); + + // Typical values here is ~1900 for total and ~1000 for largest. Obviously + // it depends in what other tests have done to this process. + FreeMBytes free_mem1 = {0}; + EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem1)); + EXPECT_LT(10u, free_mem1.total); + EXPECT_LT(10u, free_mem1.largest); + EXPECT_GT(2048u, free_mem1.total); + EXPECT_GT(2048u, free_mem1.largest); + EXPECT_GE(free_mem1.total, free_mem1.largest); + EXPECT_TRUE(NULL != free_mem1.largest_ptr); + + // Allocate 20M and check again. It should have gone down. + const int kAllocMB = 20; + char* alloc = new char[kAllocMB * 1024 * 1024]; + EXPECT_TRUE(NULL != alloc); + + size_t expected_total = free_mem1.total - kAllocMB; + size_t expected_largest = free_mem1.largest; + + FreeMBytes free_mem2 = {0}; + EXPECT_TRUE(metrics->CalculateFreeMemory(&free_mem2)); + EXPECT_GE(free_mem2.total, free_mem2.largest); + EXPECT_GE(expected_total, free_mem2.total); + EXPECT_GE(expected_largest, free_mem2.largest); + EXPECT_TRUE(NULL != free_mem2.largest_ptr); + + delete[] alloc; + delete metrics; +} + +TEST_F(ProcessUtilTest, GetAppOutput) { + // Let's create a decently long message. + std::string message; + for (int i = 0; i < 1025; i++) { // 1025 so it does not end on a kilo-byte + // boundary. + message += "Hello!"; + } + + FilePath python_runtime; + ASSERT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &python_runtime)); + python_runtime = python_runtime.Append(FILE_PATH_LITERAL("third_party")) + .Append(FILE_PATH_LITERAL("python_24")) + .Append(FILE_PATH_LITERAL("python.exe")); + + // You have to put every parameter between quotes, otherwise this won't work, + // don't ask me why. + std::wstring cmd_line = L"\"" + python_runtime.value() + L"\" " + + L"\"-c\" \"import sys; sys.stdout.write('" + ASCIIToWide(message) + + L"');\""; + std::string output; + ASSERT_TRUE(base::GetAppOutput(cmd_line, &output)); + EXPECT_EQ(message, output); + + // Let's make sure stderr is ignored. + cmd_line = L"\"" + python_runtime.value() + L"\" " + + L"\"-c\" \"import sys; sys.stderr.write('Hello!');\""; + output.clear(); + ASSERT_TRUE(base::GetAppOutput(cmd_line, &output)); + EXPECT_EQ("", output); +} +#endif // defined(OS_WIN) + +#if defined(OS_POSIX) +// Returns the maximum number of files that a process can have open. +// Returns 0 on error. +int GetMaxFilesOpenInProcess() { + struct rlimit rlim; + if (getrlimit(RLIMIT_NOFILE, &rlim) != 0) { + return 0; + } + + // rlim_t is a uint64 - clip to maxint. We do this since FD #s are ints + // which are all 32 bits on the supported platforms. + rlim_t max_int = static_cast(std::numeric_limits::max()); + if (rlim.rlim_cur > max_int) { + return max_int; + } + + return rlim.rlim_cur; +} + +const int kChildPipe = 20; // FD # for write end of pipe in child process. +MULTIPROCESS_TEST_MAIN(ProcessUtilsLeakFDChildProcess) { + // This child process counts the number of open FDs, it then writes that + // number out to a pipe connected to the parent. + int num_open_files = 0; + int write_pipe = kChildPipe; + int max_files = GetMaxFilesOpenInProcess(); + for (int i = STDERR_FILENO + 1; i < max_files; i++) { + if (i != kChildPipe) { + if (HANDLE_EINTR(close(i)) != -1) { + LOG(WARNING) << "Leaked FD " << i; + num_open_files += 1; + } + } + } + + // InitLogging always opens a file at startup. + int expected_num_open_fds = 1; +#if defined(OS_LINUX) + // On Linux, '/etc/localtime' is opened before the test's main() enters. + expected_num_open_fds += 1; +#endif // defined(OS_LINUX) + num_open_files -= expected_num_open_fds; + + int written = HANDLE_EINTR(write(write_pipe, &num_open_files, + sizeof(num_open_files))); + DCHECK_EQ(static_cast(written), sizeof(num_open_files)); + HANDLE_EINTR(close(write_pipe)); + + return 0; +} + +TEST_F(ProcessUtilTest, FDRemapping) { + // Open some files to check they don't get leaked to the child process. + int fds[2]; + if (pipe(fds) < 0) + NOTREACHED(); + int pipe_read_fd = fds[0]; + int pipe_write_fd = fds[1]; + + // open some dummy fds to make sure they don't propogate over to the + // child process. + int dev_null = open("/dev/null", O_RDONLY); + int sockets[2]; + socketpair(AF_UNIX, SOCK_STREAM, 0, sockets); + + file_handle_mapping_vector fd_mapping_vec; + fd_mapping_vec.push_back(std::pair(pipe_write_fd, kChildPipe)); + ProcessHandle handle = this->SpawnChild(L"ProcessUtilsLeakFDChildProcess", + fd_mapping_vec, + false); + ASSERT_NE(static_cast(NULL), handle); + HANDLE_EINTR(close(pipe_write_fd)); + + // Read number of open files in client process from pipe; + int num_open_files = -1; + ssize_t bytes_read = + HANDLE_EINTR(read(pipe_read_fd, &num_open_files, sizeof(num_open_files))); + ASSERT_EQ(bytes_read, static_cast(sizeof(num_open_files))); + + // Make sure 0 fds are leaked to the client. + ASSERT_EQ(0, num_open_files); + + EXPECT_TRUE(WaitForSingleProcess(handle, 1000)); + base::CloseProcessHandle(handle); + HANDLE_EINTR(close(fds[0])); + HANDLE_EINTR(close(sockets[0])); + HANDLE_EINTR(close(sockets[1])); + HANDLE_EINTR(close(dev_null)); +} + +TEST_F(ProcessUtilTest, GetAppOutput) { + std::string output; + EXPECT_TRUE(GetAppOutput(CommandLine(L"true"), &output)); + EXPECT_STREQ("", output.c_str()); + + EXPECT_FALSE(GetAppOutput(CommandLine(L"false"), &output)); + + std::vector argv; + argv.push_back("/bin/echo"); + argv.push_back("-n"); + argv.push_back("foobar42"); + EXPECT_TRUE(GetAppOutput(CommandLine(argv), &output)); + EXPECT_STREQ("foobar42", output.c_str()); +} + +#endif // defined(OS_POSIX) + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process_util_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process_util_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process_util_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process_util_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,773 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process_util.h" + +#include +#include +#include + +#include "base/debug_util.h" +#include "base/histogram.h" +#include "base/logging.h" +#include "base/scoped_handle_win.h" +#include "base/scoped_ptr.h" + +namespace { + +// System pagesize. This value remains constant on x86/64 architectures. +const int PAGESIZE_KB = 4; + +// HeapSetInformation function pointer. +typedef BOOL (WINAPI* HeapSetFn)(HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T); + +static mozilla::EnvironmentLog gProcessLog("MOZ_PROCESS_LOG"); + +} // namespace + +namespace base { + +ProcessId GetCurrentProcId() { + return ::GetCurrentProcessId(); +} + +ProcessHandle GetCurrentProcessHandle() { + return ::GetCurrentProcess(); +} + +bool OpenProcessHandle(ProcessId pid, ProcessHandle* handle) { + // TODO(phajdan.jr): Take even more permissions out of this list. + ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE | + PROCESS_TERMINATE | + PROCESS_QUERY_INFORMATION | + SYNCHRONIZE, + FALSE, pid); + + if (result == INVALID_HANDLE_VALUE) + return false; + + *handle = result; + return true; +} + +bool OpenPrivilegedProcessHandle(ProcessId pid, ProcessHandle* handle) { + ProcessHandle result = OpenProcess(PROCESS_DUP_HANDLE | + PROCESS_TERMINATE | + PROCESS_QUERY_INFORMATION | + PROCESS_VM_READ | + SYNCHRONIZE, + FALSE, pid); + + if (result == INVALID_HANDLE_VALUE) + return false; + + *handle = result; + return true; +} + +void CloseProcessHandle(ProcessHandle process) { + CloseHandle(process); +} + +// Helper for GetProcId() +bool GetProcIdViaGetProcessId(ProcessHandle process, DWORD* id) { + // Dynamically get a pointer to GetProcessId(). + typedef DWORD (WINAPI *GetProcessIdFunction)(HANDLE); + static GetProcessIdFunction GetProcessIdPtr = NULL; + static bool initialize_get_process_id = true; + if (initialize_get_process_id) { + initialize_get_process_id = false; + HMODULE kernel32_handle = GetModuleHandle(L"kernel32.dll"); + if (!kernel32_handle) { + NOTREACHED(); + return false; + } + GetProcessIdPtr = reinterpret_cast(GetProcAddress( + kernel32_handle, "GetProcessId")); + } + if (!GetProcessIdPtr) + return false; + // Ask for the process ID. + *id = (*GetProcessIdPtr)(process); + return true; +} + +// Helper for GetProcId() +bool GetProcIdViaNtQueryInformationProcess(ProcessHandle process, DWORD* id) { + // Dynamically get a pointer to NtQueryInformationProcess(). + typedef NTSTATUS (WINAPI *NtQueryInformationProcessFunction)( + HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); + static NtQueryInformationProcessFunction NtQueryInformationProcessPtr = NULL; + static bool initialize_query_information_process = true; + if (initialize_query_information_process) { + initialize_query_information_process = false; + // According to nsylvain, ntdll.dll is guaranteed to be loaded, even though + // the Windows docs seem to imply that you should LoadLibrary() it. + HMODULE ntdll_handle = GetModuleHandle(L"ntdll.dll"); + if (!ntdll_handle) { + NOTREACHED(); + return false; + } + NtQueryInformationProcessPtr = + reinterpret_cast(GetProcAddress( + ntdll_handle, "NtQueryInformationProcess")); + } + if (!NtQueryInformationProcessPtr) + return false; + // Ask for the process ID. + PROCESS_BASIC_INFORMATION info; + ULONG bytes_returned; + NTSTATUS status = (*NtQueryInformationProcessPtr)(process, + ProcessBasicInformation, + &info, sizeof info, + &bytes_returned); + if (!SUCCEEDED(status) || (bytes_returned != (sizeof info))) + return false; + + *id = static_cast(info.UniqueProcessId); + return true; +} + +ProcessId GetProcId(ProcessHandle process) { + // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights. + HANDLE current_process = GetCurrentProcess(); + HANDLE process_with_query_rights; + if (DuplicateHandle(current_process, process, current_process, + &process_with_query_rights, PROCESS_QUERY_INFORMATION, + false, 0)) { + // Try to use GetProcessId(), if it exists. Fall back on + // NtQueryInformationProcess() otherwise (< Win XP SP1). + DWORD id; + bool success = + GetProcIdViaGetProcessId(process_with_query_rights, &id) || + GetProcIdViaNtQueryInformationProcess(process_with_query_rights, &id); + CloseHandle(process_with_query_rights); + if (success) + return id; + } + + // We're screwed. + NOTREACHED(); + return 0; +} + +bool LaunchApp(const std::wstring& cmdline, + bool wait, bool start_hidden, ProcessHandle* process_handle) { + STARTUPINFO startup_info = {0}; + startup_info.cb = sizeof(startup_info); + startup_info.dwFlags = STARTF_USESHOWWINDOW; + startup_info.wShowWindow = start_hidden ? SW_HIDE : SW_SHOW; + PROCESS_INFORMATION process_info; + if (!CreateProcess(NULL, + const_cast(cmdline.c_str()), NULL, NULL, + FALSE, 0, NULL, NULL, + &startup_info, &process_info)) + return false; + + gProcessLog.print("==> process %d launched child process %d\n", + GetCurrentProcId(), + process_info.dwProcessId); + + // Handles must be closed or they will leak + CloseHandle(process_info.hThread); + + if (wait) + WaitForSingleObject(process_info.hProcess, INFINITE); + + // If the caller wants the process handle, we won't close it. + if (process_handle) { + *process_handle = process_info.hProcess; + } else { + CloseHandle(process_info.hProcess); + } + return true; +} + +bool LaunchApp(const CommandLine& cl, + bool wait, bool start_hidden, ProcessHandle* process_handle) { + return LaunchApp(cl.command_line_string(), wait, + start_hidden, process_handle); +} + +// Attempts to kill the process identified by the given process +// entry structure, giving it the specified exit code. +// Returns true if this is successful, false otherwise. +bool KillProcessById(ProcessId process_id, int exit_code, bool wait) { + HANDLE process = OpenProcess(PROCESS_TERMINATE | SYNCHRONIZE, + FALSE, // Don't inherit handle + process_id); + if (!process) + return false; + + bool ret = KillProcess(process, exit_code, wait); + CloseHandle(process); + return ret; +} + +bool GetAppOutput(const std::wstring& cmd_line, std::string* output) { + if (!output) { + NOTREACHED(); + return false; + } + + HANDLE out_read = NULL; + HANDLE out_write = NULL; + + SECURITY_ATTRIBUTES sa_attr; + // Set the bInheritHandle flag so pipe handles are inherited. + sa_attr.nLength = sizeof(SECURITY_ATTRIBUTES); + sa_attr.bInheritHandle = TRUE; + sa_attr.lpSecurityDescriptor = NULL; + + // Create the pipe for the child process's STDOUT. + if (!CreatePipe(&out_read, &out_write, &sa_attr, 0)) { + NOTREACHED() << "Failed to create pipe"; + return false; + } + + // Ensure we don't leak the handles. + ScopedHandle scoped_out_read(out_read); + ScopedHandle scoped_out_write(out_write); + + // Ensure the read handle to the pipe for STDOUT is not inherited. + if (!SetHandleInformation(out_read, HANDLE_FLAG_INHERIT, 0)) { + NOTREACHED() << "Failed to disabled pipe inheritance"; + return false; + } + + // Now create the child process + PROCESS_INFORMATION proc_info = { 0 }; + STARTUPINFO start_info = { 0 }; + + start_info.cb = sizeof(STARTUPINFO); + start_info.hStdOutput = out_write; + // Keep the normal stdin and stderr. + start_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + start_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); + start_info.dwFlags |= STARTF_USESTDHANDLES; + + // Create the child process. + if (!CreateProcess(NULL, const_cast(cmd_line.c_str()), NULL, NULL, + TRUE, // Handles are inherited. + 0, NULL, NULL, &start_info, &proc_info)) { + NOTREACHED() << "Failed to start process"; + return false; + } + + // We don't need the thread handle, close it now. + CloseHandle(proc_info.hThread); + + // Close our writing end of pipe now. Otherwise later read would not be able + // to detect end of child's output. + scoped_out_write.Close(); + + // Read output from the child process's pipe for STDOUT + const int kBufferSize = 1024; + char buffer[kBufferSize]; + + for (;;) { + DWORD bytes_read = 0; + BOOL success = ReadFile(out_read, buffer, kBufferSize, &bytes_read, NULL); + if (!success || bytes_read == 0) + break; + output->append(buffer, bytes_read); + } + + // Let's wait for the process to finish. + WaitForSingleObject(proc_info.hProcess, INFINITE); + CloseHandle(proc_info.hProcess); + + return true; +} + +bool KillProcess(ProcessHandle process, int exit_code, bool wait) { + bool result = (TerminateProcess(process, exit_code) != FALSE); + if (result && wait) { + // The process may not end immediately due to pending I/O + if (WAIT_OBJECT_0 != WaitForSingleObject(process, 60 * 1000)) + DLOG(ERROR) << "Error waiting for process exit: " << GetLastError(); + } else if (!result) { + DLOG(ERROR) << "Unable to terminate process: " << GetLastError(); + } + return result; +} + +bool DidProcessCrash(bool* child_exited, ProcessHandle handle) { + DWORD exitcode = 0; + + if (child_exited) + *child_exited = true; // On Windows it an error to call this function if + // the child hasn't already exited. + if (!::GetExitCodeProcess(handle, &exitcode)) { + NOTREACHED(); + return false; + } + if (exitcode == STILL_ACTIVE) { + // The process is likely not dead or it used 0x103 as exit code. + NOTREACHED(); + return false; + } + + // Warning, this is not generic code; it heavily depends on the way + // the rest of the code kills a process. + + if (exitcode == PROCESS_END_NORMAL_TERMINATON || + exitcode == PROCESS_END_KILLED_BY_USER || + exitcode == PROCESS_END_PROCESS_WAS_HUNG || + exitcode == 0xC0000354 || // STATUS_DEBUGGER_INACTIVE. + exitcode == 0xC000013A || // Control-C/end session. + exitcode == 0x40010004) { // Debugger terminated process/end session. + return false; + } + + // All other exit codes indicate crashes. + + // TODO(jar): Remove histogramming code when UMA stats are consistent with + // other crash metrics. + // Histogram the low order 3 nibbles for UMA + const int kLeastValue = 0; + const int kMaxValue = 0xFFF; + const int kBucketCount = kMaxValue - kLeastValue + 1; + static LinearHistogram least_significant_histogram("ExitCodes.LSNibbles", + kLeastValue + 1, kMaxValue, kBucketCount); + least_significant_histogram.SetFlags(kUmaTargetedHistogramFlag | + LinearHistogram::kHexRangePrintingFlag); + least_significant_histogram.Add(exitcode & 0xFFF); + + // Histogram the high order 3 nibbles + static LinearHistogram most_significant_histogram("ExitCodes.MSNibbles", + kLeastValue + 1, kMaxValue, kBucketCount); + most_significant_histogram.SetFlags(kUmaTargetedHistogramFlag | + LinearHistogram::kHexRangePrintingFlag); + // Avoid passing in negative numbers by shifting data into low end of dword. + most_significant_histogram.Add((exitcode >> 20) & 0xFFF); + + // Histogram the middle order 2 nibbles + static LinearHistogram mid_significant_histogram("ExitCodes.MidNibbles", + 1, 0xFF, 0x100); + mid_significant_histogram.SetFlags(kUmaTargetedHistogramFlag | + LinearHistogram::kHexRangePrintingFlag); + mid_significant_histogram.Add((exitcode >> 12) & 0xFF); + + return true; +} + +bool WaitForExitCode(ProcessHandle handle, int* exit_code) { + ScopedHandle closer(handle); // Ensure that we always close the handle. + if (::WaitForSingleObject(handle, INFINITE) != WAIT_OBJECT_0) { + NOTREACHED(); + return false; + } + DWORD temp_code; // Don't clobber out-parameters in case of failure. + if (!::GetExitCodeProcess(handle, &temp_code)) + return false; + *exit_code = temp_code; + return true; +} + +NamedProcessIterator::NamedProcessIterator(const std::wstring& executable_name, + const ProcessFilter* filter) + : started_iteration_(false), + executable_name_(executable_name), + filter_(filter) { + snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); +} + +NamedProcessIterator::~NamedProcessIterator() { + CloseHandle(snapshot_); +} + + +const ProcessEntry* NamedProcessIterator::NextProcessEntry() { + bool result = false; + do { + result = CheckForNextProcess(); + } while (result && !IncludeEntry()); + + if (result) { + return &entry_; + } + + return NULL; +} + +bool NamedProcessIterator::CheckForNextProcess() { + InitProcessEntry(&entry_); + + if (!started_iteration_) { + started_iteration_ = true; + return !!Process32First(snapshot_, &entry_); + } + + return !!Process32Next(snapshot_, &entry_); +} + +bool NamedProcessIterator::IncludeEntry() { + return _wcsicmp(executable_name_.c_str(), entry_.szExeFile) == 0 && + (!filter_ || filter_->Includes(entry_.th32ProcessID, + entry_.th32ParentProcessID)); +} + +void NamedProcessIterator::InitProcessEntry(ProcessEntry* entry) { + memset(entry, 0, sizeof(*entry)); + entry->dwSize = sizeof(*entry); +} + +int GetProcessCount(const std::wstring& executable_name, + const ProcessFilter* filter) { + int count = 0; + + NamedProcessIterator iter(executable_name, filter); + while (iter.NextProcessEntry()) + ++count; + return count; +} + +bool KillProcesses(const std::wstring& executable_name, int exit_code, + const ProcessFilter* filter) { + bool result = true; + const ProcessEntry* entry; + + NamedProcessIterator iter(executable_name, filter); + while (entry = iter.NextProcessEntry()) { + if (!KillProcessById((*entry).th32ProcessID, exit_code, true)) + result = false; + } + + return result; +} + +bool WaitForProcessesToExit(const std::wstring& executable_name, + int wait_milliseconds, + const ProcessFilter* filter) { + const ProcessEntry* entry; + bool result = true; + DWORD start_time = GetTickCount(); + + NamedProcessIterator iter(executable_name, filter); + while (entry = iter.NextProcessEntry()) { + DWORD remaining_wait = + std::max(0, wait_milliseconds - + static_cast(GetTickCount() - start_time)); + HANDLE process = OpenProcess(SYNCHRONIZE, + FALSE, + entry->th32ProcessID); + DWORD wait_result = WaitForSingleObject(process, remaining_wait); + CloseHandle(process); + result = result && (wait_result == WAIT_OBJECT_0); + } + + return result; +} + +bool WaitForSingleProcess(ProcessHandle handle, int wait_milliseconds) { + bool retval = WaitForSingleObject(handle, wait_milliseconds) == WAIT_OBJECT_0; + return retval; +} + +bool CrashAwareSleep(ProcessHandle handle, int wait_milliseconds) { + bool retval = WaitForSingleObject(handle, wait_milliseconds) == WAIT_TIMEOUT; + return retval; +} + +bool CleanupProcesses(const std::wstring& executable_name, + int wait_milliseconds, + int exit_code, + const ProcessFilter* filter) { + bool exited_cleanly = WaitForProcessesToExit(executable_name, + wait_milliseconds, + filter); + if (!exited_cleanly) + KillProcesses(executable_name, exit_code, filter); + return exited_cleanly; +} + +/////////////////////////////////////////////////////////////////////////////// +// ProcesMetrics + +ProcessMetrics::ProcessMetrics(ProcessHandle process) : process_(process), + last_time_(0), + last_system_time_(0) { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + processor_count_ = system_info.dwNumberOfProcessors; +} + +// static +ProcessMetrics* ProcessMetrics::CreateProcessMetrics(ProcessHandle process) { + return new ProcessMetrics(process); +} + +ProcessMetrics::~ProcessMetrics() { } + +size_t ProcessMetrics::GetPagefileUsage() const { + PROCESS_MEMORY_COUNTERS pmc; + if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) { + return pmc.PagefileUsage; + } + return 0; +} + +// Returns the peak space allocated for the pagefile, in bytes. +size_t ProcessMetrics::GetPeakPagefileUsage() const { + PROCESS_MEMORY_COUNTERS pmc; + if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) { + return pmc.PeakPagefileUsage; + } + return 0; +} + +// Returns the current working set size, in bytes. +size_t ProcessMetrics::GetWorkingSetSize() const { + PROCESS_MEMORY_COUNTERS pmc; + if (GetProcessMemoryInfo(process_, &pmc, sizeof(pmc))) { + return pmc.WorkingSetSize; + } + return 0; +} + +size_t ProcessMetrics::GetPrivateBytes() const { + // PROCESS_MEMORY_COUNTERS_EX is not supported until XP SP2. + // GetProcessMemoryInfo() will simply fail on prior OS. So the requested + // information is simply not available. Hence, we will return 0 on unsupported + // OSes. Unlike most Win32 API, we don't need to initialize the "cb" member. + PROCESS_MEMORY_COUNTERS_EX pmcx; + if (GetProcessMemoryInfo(process_, + reinterpret_cast(&pmcx), + sizeof(pmcx))) { + return pmcx.PrivateUsage; + } + return 0; +} + +void ProcessMetrics::GetCommittedKBytes(CommittedKBytes* usage) const { + MEMORY_BASIC_INFORMATION mbi = {0}; + size_t committed_private = 0; + size_t committed_mapped = 0; + size_t committed_image = 0; + void* base_address = NULL; + while (VirtualQueryEx(process_, base_address, &mbi, sizeof(mbi)) == + sizeof(mbi)) { + if (mbi.State == MEM_COMMIT) { + if (mbi.Type == MEM_PRIVATE) { + committed_private += mbi.RegionSize; + } else if (mbi.Type == MEM_MAPPED) { + committed_mapped += mbi.RegionSize; + } else if (mbi.Type == MEM_IMAGE) { + committed_image += mbi.RegionSize; + } else { + NOTREACHED(); + } + } + void* new_base = (static_cast(mbi.BaseAddress)) + mbi.RegionSize; + // Avoid infinite loop by weird MEMORY_BASIC_INFORMATION. + // If we query 64bit processes in a 32bit process, VirtualQueryEx() + // returns such data. + if (new_base <= base_address) { + usage->image = 0; + usage->mapped = 0; + usage->priv = 0; + return; + } + base_address = new_base; + } + usage->image = committed_image / 1024; + usage->mapped = committed_mapped / 1024; + usage->priv = committed_private / 1024; +} + +bool ProcessMetrics::GetWorkingSetKBytes(WorkingSetKBytes* ws_usage) const { + size_t ws_private = 0; + size_t ws_shareable = 0; + size_t ws_shared = 0; + + DCHECK(ws_usage); + memset(ws_usage, 0, sizeof(*ws_usage)); + + DWORD number_of_entries = 4096; // Just a guess. + PSAPI_WORKING_SET_INFORMATION* buffer = NULL; + int retries = 5; + for (;;) { + DWORD buffer_size = sizeof(PSAPI_WORKING_SET_INFORMATION) + + (number_of_entries * sizeof(PSAPI_WORKING_SET_BLOCK)); + + // if we can't expand the buffer, don't leak the previous + // contents or pass a NULL pointer to QueryWorkingSet + PSAPI_WORKING_SET_INFORMATION* new_buffer = + reinterpret_cast( + realloc(buffer, buffer_size)); + if (!new_buffer) { + free(buffer); + return false; + } + buffer = new_buffer; + + // Call the function once to get number of items + if (QueryWorkingSet(process_, buffer, buffer_size)) + break; // Success + + if (GetLastError() != ERROR_BAD_LENGTH) { + free(buffer); + return false; + } + + number_of_entries = static_cast(buffer->NumberOfEntries); + + // Maybe some entries are being added right now. Increase the buffer to + // take that into account. + number_of_entries = static_cast(number_of_entries * 1.25); + + if (--retries == 0) { + free(buffer); // If we're looping, eventually fail. + return false; + } + } + + // On windows 2000 the function returns 1 even when the buffer is too small. + // The number of entries that we are going to parse is the minimum between the + // size we allocated and the real number of entries. + number_of_entries = + std::min(number_of_entries, static_cast(buffer->NumberOfEntries)); + for (unsigned int i = 0; i < number_of_entries; i++) { + if (buffer->WorkingSetInfo[i].Shared) { + ws_shareable++; + if (buffer->WorkingSetInfo[i].ShareCount > 1) + ws_shared++; + } else { + ws_private++; + } + } + + ws_usage->priv = ws_private * PAGESIZE_KB; + ws_usage->shareable = ws_shareable * PAGESIZE_KB; + ws_usage->shared = ws_shared * PAGESIZE_KB; + free(buffer); + return true; +} + +static uint64 FileTimeToUTC(const FILETIME& ftime) { + LARGE_INTEGER li; + li.LowPart = ftime.dwLowDateTime; + li.HighPart = ftime.dwHighDateTime; + return li.QuadPart; +} + +int ProcessMetrics::GetCPUUsage() { + FILETIME now; + FILETIME creation_time; + FILETIME exit_time; + FILETIME kernel_time; + FILETIME user_time; + + GetSystemTimeAsFileTime(&now); + + if (!GetProcessTimes(process_, &creation_time, &exit_time, + &kernel_time, &user_time)) { + // We don't assert here because in some cases (such as in the Task Manager) + // we may call this function on a process that has just exited but we have + // not yet received the notification. + return 0; + } + int64 system_time = (FileTimeToUTC(kernel_time) + FileTimeToUTC(user_time)) / + processor_count_; + int64 time = FileTimeToUTC(now); + + if ((last_system_time_ == 0) || (last_time_ == 0)) { + // First call, just set the last values. + last_system_time_ = system_time; + last_time_ = time; + return 0; + } + + int64 system_time_delta = system_time - last_system_time_; + int64 time_delta = time - last_time_; + DCHECK(time_delta != 0); + if (time_delta == 0) + return 0; + + // We add time_delta / 2 so the result is rounded. + int cpu = static_cast((system_time_delta * 100 + time_delta / 2) / + time_delta); + + last_system_time_ = system_time; + last_time_ = time; + + return cpu; +} + +bool ProcessMetrics::GetIOCounters(IO_COUNTERS* io_counters) const { + return GetProcessIoCounters(process_, io_counters) != FALSE; +} + +bool ProcessMetrics::CalculateFreeMemory(FreeMBytes* free) const { + const SIZE_T kTopAdress = 0x7F000000; + const SIZE_T kMegabyte = 1024 * 1024; + SIZE_T accumulated = 0; + + MEMORY_BASIC_INFORMATION largest = {0}; + UINT_PTR scan = 0; + while (scan < kTopAdress) { + MEMORY_BASIC_INFORMATION info; + if (!::VirtualQueryEx(process_, reinterpret_cast(scan), + &info, sizeof(info))) + return false; + if (info.State == MEM_FREE) { + accumulated += info.RegionSize; + UINT_PTR end = scan + info.RegionSize; + if (info.RegionSize > (largest.RegionSize)) + largest = info; + } + scan += info.RegionSize; + } + free->largest = largest.RegionSize / kMegabyte; + free->largest_ptr = largest.BaseAddress; + free->total = accumulated / kMegabyte; + return true; +} + +bool EnableLowFragmentationHeap() { + HMODULE kernel32 = GetModuleHandle(L"kernel32.dll"); + HeapSetFn heap_set = reinterpret_cast(GetProcAddress( + kernel32, + "HeapSetInformation")); + + // On Windows 2000, the function is not exported. This is not a reason to + // fail. + if (!heap_set) + return true; + + unsigned number_heaps = GetProcessHeaps(0, NULL); + if (!number_heaps) + return false; + + // Gives us some extra space in the array in case a thread is creating heaps + // at the same time we're querying them. + static const int MARGIN = 8; + scoped_array heaps(new HANDLE[number_heaps + MARGIN]); + number_heaps = GetProcessHeaps(number_heaps + MARGIN, heaps.get()); + if (!number_heaps) + return false; + + for (unsigned i = 0; i < number_heaps; ++i) { + ULONG lfh_flag = 2; + // Don't bother with the result code. It may fails on heaps that have the + // HEAP_NO_SERIALIZE flag. This is expected and not a problem at all. + heap_set(heaps[i], + HeapCompatibilityInformation, + &lfh_flag, + sizeof(lfh_flag)); + } + return true; +} + +void EnableTerminationOnHeapCorruption() { + // Ignore the result code. Supported on XP SP3 and Vista. + HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0); +} + +void RaiseProcessToHighPriority() { + SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/process_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/process_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,126 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/process.h" +#include "base/logging.h" +#include "base/process_util.h" +#include "base/scoped_ptr.h" + +namespace base { + +void Process::Close() { + if (!process_) + return; + ::CloseHandle(process_); + process_ = NULL; +} + +void Process::Terminate(int result_code) { + if (!process_) + return; + ::TerminateProcess(process_, result_code); +} + +bool Process::IsProcessBackgrounded() const { + DCHECK(process_); + DWORD priority = GetPriorityClass(process_); + if (priority == 0) + return false; // Failure case. + return priority == BELOW_NORMAL_PRIORITY_CLASS; +} + +bool Process::SetProcessBackgrounded(bool value) { + DCHECK(process_); + DWORD priority = value ? BELOW_NORMAL_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS; + return (SetPriorityClass(process_, priority) != 0); +} + +// According to MSDN, these are the default values which XP +// uses to govern working set soft limits. +// http://msdn.microsoft.com/en-us/library/ms686234.aspx +static const int kWinDefaultMinSet = 50 * 4096; +static const int kWinDefaultMaxSet = 345 * 4096; +static const int kDampingFactor = 2; + +bool Process::ReduceWorkingSet() { + if (!process_) + return false; + // The idea here is that when we the process' working set has gone + // down, we want to release those pages to the OS quickly. However, + // when it is not going down, we want to be careful not to release + // too much back to the OS, as it could cause additional paging. + + // We use a damping function to lessen the working set over time. + // As the process grows/shrinks, this algorithm will lag with + // working set reduction. + // + // The intended algorithm is: + // TargetWorkingSetSize = (LastWorkingSet/2 + CurrentWorkingSet) /2 + + scoped_ptr metrics( + ProcessMetrics::CreateProcessMetrics(process_)); + WorkingSetKBytes working_set; + if (!metrics->GetWorkingSetKBytes(&working_set)) + return false; + + + // We want to compute the amount of working set that the process + // needs to keep in memory. Since other processes contain the + // pages which are shared, we don't need to reserve them in our + // process, the system already has them tagged. Keep in mind, we + // don't get to control *which* pages get released, but if we + // assume reasonable distribution of pages, this should generally + // be the right value. + size_t current_working_set_size = working_set.priv + + working_set.shareable; + + size_t max_size = current_working_set_size; + if (last_working_set_size_) + max_size = (max_size + last_working_set_size_) / 2; // Average. + max_size *= 1024; // Convert to KBytes. + last_working_set_size_ = current_working_set_size / kDampingFactor; + + BOOL rv = SetProcessWorkingSetSize(process_, kWinDefaultMinSet, max_size); + return rv == TRUE; +} + +bool Process::UnReduceWorkingSet() { + if (!process_) + return false; + + if (!last_working_set_size_) + return true; // There was nothing to undo. + + // We've had a reduced working set. Make sure we have lots of + // headroom now that we're active again. + size_t limit = last_working_set_size_ * kDampingFactor * 2 * 1024; + BOOL rv = SetProcessWorkingSetSize(process_, kWinDefaultMinSet, limit); + return rv == TRUE; +} + +bool Process::EmptyWorkingSet() { + if (!process_) + return false; + + BOOL rv = SetProcessWorkingSetSize(process_, -1, -1); + return rv == TRUE; +} + +ProcessId Process::pid() const { + if (process_ == 0) + return 0; + + return GetProcId(process_); +} + +bool Process::is_current() const { + return process_ == GetCurrentProcess(); +} + +// static +Process Process::Current() { + return Process(GetCurrentProcess()); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/profiler.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/profiler.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/profiler.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/profiler.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,47 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/profiler.h" +#include "base/string_util.h" + +// When actually using quantify, uncomment the following line. +//#define QUANTIFY + +#ifdef QUANTIFY +// this #define is used to prevent people from directly using pure.h +// instead of profiler.h +#define PURIFY_PRIVATE_INCLUDE +#include "base/third_party/purify/pure.h" +#endif // QUANTIFY + +namespace base { + +void Profiler::StartRecording() { +#ifdef QUANTIFY + QuantifyStartRecordingData(); +#endif +} + +void Profiler::StopRecording() { +#ifdef QUANTIFY + QuantifyStopRecordingData(); +#endif +} + +void Profiler::ClearData() { +#ifdef QUANTIFY + QuantifyClearData(); +#endif +} + +void Profiler::SetThreadName(const char *name) { +#ifdef QUANTIFY + // make a copy since the Quantify function takes a char*, not const char* + char buffer[512]; + base::snprintf(buffer, sizeof(buffer)-1, "%s", name); + QuantifySetThreadName(buffer); +#endif +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/profiler.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/profiler.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/profiler.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/profiler.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,38 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// An abstraction for functions used to control execution time profiling. +// All methods are effectively no-ops unless this program is being run through +// a supported tool (currently only Quantify, a companion tool to Purify) + +#ifndef BASE_PROFILER_H__ +#define BASE_PROFILER_H__ + +#include "base/basictypes.h" + +namespace base { + +class Profiler { + public: + // Starts or resumes recording. + static void StartRecording(); + + // Stops recording until StartRecording is called or the program exits. + static void StopRecording(); + + // Throw away data collected so far. This can be useful to call before + // your first call to StartRecording, for instance to avoid counting any + // time in application startup. + static void ClearData(); + + // Sets the name of the current thread for display in the profiler's UI. + static void SetThreadName(const char *name); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(Profiler); +}; + +} // namespace base + +#endif // BASE_PROFILER_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/pr_time_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/pr_time_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/pr_time_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/pr_time_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,269 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/third_party/nspr/prtime.h" +#include "base/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::Time; + +namespace { + +// time_t representation of 15th Oct 2007 12:45:00 PDT +PRTime comparison_time_pdt = 1192477500 * Time::kMicrosecondsPerSecond; + +// Specialized test fixture allowing time strings without timezones to be +// tested by comparing them to a known time in the local zone. +class PRTimeTest : public testing::Test { + protected: + virtual void SetUp() { + // Use mktime to get a time_t, and turn it into a PRTime by converting + // seconds to microseconds. Use 15th Oct 2007 12:45:00 local. This + // must be a time guaranteed to be outside of a DST fallback hour in + // any timezone. + struct tm local_comparison_tm = { + 0, // second + 45, // minute + 12, // hour + 15, // day of month + 10 - 1, // month + 2007 - 1900, // year + 0, // day of week (ignored, output only) + 0, // day of year (ignored, output only) + -1 // DST in effect, -1 tells mktime to figure it out + }; + comparison_time_local_ = mktime(&local_comparison_tm) * + Time::kMicrosecondsPerSecond; + ASSERT_GT(comparison_time_local_, 0); + } + + PRTime comparison_time_local_; +}; + +// Tests the PR_ParseTimeString nspr helper function for +// a variety of time strings. +TEST_F(PRTimeTest, ParseTimeTest1) { + time_t current_time = 0; + time(¤t_time); + + const int BUFFER_SIZE = 64; + struct tm local_time = {0}; + char time_buf[BUFFER_SIZE] = {0}; +#if defined(OS_WIN) + localtime_s(&local_time, ¤t_time); + asctime_s(time_buf, arraysize(time_buf), &local_time); +#elif defined(OS_POSIX) + localtime_r(¤t_time, &local_time); + asctime_r(&local_time, time_buf); +#endif + + PRTime current_time64 = static_cast(current_time) * PR_USEC_PER_SEC; + + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString(time_buf, PR_FALSE, &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(current_time64, parsed_time); +} + +TEST_F(PRTimeTest, ParseTimeTest2) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("Mon, 15 Oct 2007 19:45:00 GMT", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(parsed_time, comparison_time_pdt); +} + +TEST_F(PRTimeTest, ParseTimeTest3) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("15 Oct 07 12:45:00", PR_FALSE, + &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(parsed_time, comparison_time_local_); +} + +TEST_F(PRTimeTest, ParseTimeTest4) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("15 Oct 07 19:45 GMT", PR_FALSE, + &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(parsed_time, comparison_time_pdt); +} + +TEST_F(PRTimeTest, ParseTimeTest5) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("Mon Oct 15 12:45 PDT 2007", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(parsed_time, comparison_time_pdt); +} + +TEST_F(PRTimeTest, ParseTimeTest6) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("Monday, Oct 15, 2007 12:45 PM", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(parsed_time, comparison_time_local_); +} + +TEST_F(PRTimeTest, ParseTimeTest7) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("10/15/07 12:45:00 PM", PR_FALSE, + &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(parsed_time, comparison_time_local_); +} + +TEST_F(PRTimeTest, ParseTimeTest8) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("15-OCT-2007 12:45pm", PR_FALSE, + &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(parsed_time, comparison_time_local_); +} + +TEST_F(PRTimeTest, ParseTimeTest9) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("16 Oct 2007 4:45-JST (Tuesday)", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(parsed_time, comparison_time_pdt); +} + +// This tests the Time::FromString wrapper over PR_ParseTimeString +TEST_F(PRTimeTest, ParseTimeTest10) { + Time parsed_time; + bool result = Time::FromString(L"15/10/07 12:45", &parsed_time); + EXPECT_EQ(true, result); + + time_t computed_time = parsed_time.ToTimeT(); + time_t time_to_compare = comparison_time_local_ / + Time::kMicrosecondsPerSecond; + EXPECT_EQ(computed_time, time_to_compare); +} + +// This tests the Time::FromString wrapper over PR_ParseTimeString +TEST_F(PRTimeTest, ParseTimeTest11) { + Time parsed_time; + bool result = Time::FromString(L"Mon, 15 Oct 2007 19:45:00 GMT", + &parsed_time); + EXPECT_EQ(true, result); + + time_t computed_time = parsed_time.ToTimeT(); + time_t time_to_compare = comparison_time_pdt / Time::kMicrosecondsPerSecond; + EXPECT_EQ(computed_time, time_to_compare); +} + +// Test some of edge cases around epoch, etc. +TEST_F(PRTimeTest, ParseTimeTestEpoch0) { + Time parsed_time; + + // time_t == epoch == 0 + EXPECT_EQ(true, Time::FromString(L"Thu Jan 01 01:00:00 +0100 1970", + &parsed_time)); + EXPECT_EQ(0, parsed_time.ToTimeT()); + EXPECT_EQ(true, Time::FromString(L"Thu Jan 01 00:00:00 GMT 1970", + &parsed_time)); + EXPECT_EQ(0, parsed_time.ToTimeT()); +} + +TEST_F(PRTimeTest, ParseTimeTestEpoch1) { + Time parsed_time; + + // time_t == 1 second after epoch == 1 + EXPECT_EQ(true, Time::FromString(L"Thu Jan 01 01:00:01 +0100 1970", + &parsed_time)); + EXPECT_EQ(1, parsed_time.ToTimeT()); + EXPECT_EQ(true, Time::FromString(L"Thu Jan 01 00:00:01 GMT 1970", + &parsed_time)); + EXPECT_EQ(1, parsed_time.ToTimeT()); +} + +TEST_F(PRTimeTest, ParseTimeTestEpoch2) { + Time parsed_time; + + // time_t == 2 seconds after epoch == 2 + EXPECT_EQ(true, Time::FromString(L"Thu Jan 01 01:00:02 +0100 1970", + &parsed_time)); + EXPECT_EQ(2, parsed_time.ToTimeT()); + EXPECT_EQ(true, Time::FromString(L"Thu Jan 01 00:00:02 GMT 1970", + &parsed_time)); + EXPECT_EQ(2, parsed_time.ToTimeT()); +} + +TEST_F(PRTimeTest, ParseTimeTestEpochNeg1) { + Time parsed_time; + + // time_t == 1 second before epoch == -1 + EXPECT_EQ(true, Time::FromString(L"Thu Jan 01 00:59:59 +0100 1970", + &parsed_time)); + EXPECT_EQ(-1, parsed_time.ToTimeT()); + EXPECT_EQ(true, Time::FromString(L"Wed Dec 31 23:59:59 GMT 1969", + &parsed_time)); + EXPECT_EQ(-1, parsed_time.ToTimeT()); +} + +TEST_F(PRTimeTest, ParseTimeTestEpochNeg2) { + Time parsed_time; + + // time_t == 2 seconds before epoch == -2 + EXPECT_EQ(true, Time::FromString(L"Thu Jan 01 00:59:58 +0100 1970", + &parsed_time)); + EXPECT_EQ(-2, parsed_time.ToTimeT()); + EXPECT_EQ(true, Time::FromString(L"Wed Dec 31 23:59:58 GMT 1969", + &parsed_time)); + EXPECT_EQ(-2, parsed_time.ToTimeT()); +} + +TEST_F(PRTimeTest, ParseTimeTestEpoch1960) { + Time parsed_time; + + // time_t before Epoch, in 1960 + EXPECT_EQ(true, Time::FromString(L"Wed Jun 29 19:40:01 +0100 1960", + &parsed_time)); + EXPECT_EQ(-299999999, parsed_time.ToTimeT()); + EXPECT_EQ(true, Time::FromString(L"Wed Jun 29 18:40:01 GMT 1960", + &parsed_time)); + EXPECT_EQ(-299999999, parsed_time.ToTimeT()); + EXPECT_EQ(true, Time::FromString(L"Wed Jun 29 17:40:01 GMT 1960", + &parsed_time)); + EXPECT_EQ(-300003599, parsed_time.ToTimeT()); +} + +TEST_F(PRTimeTest, ParseTimeTestEmpty) { + Time parsed_time; + EXPECT_FALSE(Time::FromString(L"", &parsed_time)); +} + +// This test should not crash when compiled with Visual C++ 2005 (see +// http://crbug.com/4387). +TEST_F(PRTimeTest, ParseTimeTestOutOfRange) { + PRTime parsed_time = 0; + // Note the lack of timezone in the time string. The year has to be 3001. + // The date has to be after 23:59:59, December 31, 3000, US Pacific Time, so + // we use January 2, 3001 to make sure it's after the magic maximum in any + // timezone. + PRStatus result = PR_ParseTimeString("Sun Jan 2 00:00:00 3001", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); +} + +TEST_F(PRTimeTest, ParseTimeTestNotNormalized1) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("Mon Oct 15 12:44:60 PDT 2007", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(comparison_time_pdt, parsed_time); +} + +TEST_F(PRTimeTest, ParseTimeTestNotNormalized2) { + PRTime parsed_time = 0; + PRStatus result = PR_ParseTimeString("Sun Oct 14 36:45 PDT 2007", + PR_FALSE, &parsed_time); + EXPECT_EQ(PR_SUCCESS, result); + EXPECT_EQ(comparison_time_pdt, parsed_time); +} + +} // namespace diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/rand_util.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/rand_util.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/rand_util.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/rand_util.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,40 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/rand_util.h" + +#include + +#include + +#include "base/basictypes.h" +#include "base/logging.h" + +namespace base { + +int RandInt(int min, int max) { + DCHECK(min <= max); + + uint64 range = static_cast(max) - min + 1; + uint64 number = base::RandUint64(); + int result = min + static_cast(number % range); + DCHECK(result >= min && result <= max); + return result; +} + +double RandDouble() { + // We try to get maximum precision by masking out as many bits as will fit + // in the target type's mantissa, and raising it to an appropriate power to + // produce output in the range [0, 1). For IEEE 754 doubles, the mantissa + // is expected to accommodate 53 bits. + + COMPILE_ASSERT(std::numeric_limits::radix == 2, otherwise_use_scalbn); + static const int kBits = std::numeric_limits::digits; + uint64 random_bits = base::RandUint64() & ((GG_UINT64_C(1) << kBits) - 1); + double result = ldexp(static_cast(random_bits), -1 * kBits); + DCHECK(result >= 0.0 && result < 1.0); + return result; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/rand_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/rand_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/rand_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/rand_util.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,23 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_RAND_UTIL_H_ +#define BASE_RAND_UTIL_H_ + +#include "base/basictypes.h" + +namespace base { + +// Returns a random number in range [0, kuint64max]. Thread-safe. +uint64 RandUint64(); + +// Returns a random number between min and max (inclusive). Thread-safe. +int RandInt(int min, int max); + +// Returns a random double in range [0, 1). Thread-safe. +double RandDouble(); + +} // namespace base + +#endif // BASE_RAND_UTIL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/rand_util_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/rand_util_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/rand_util_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/rand_util_posix.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,29 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/rand_util.h" + +#include +#include + +#include "base/file_util.h" +#include "base/logging.h" + +namespace base { + +uint64 RandUint64() { + uint64 number; + + int urandom_fd = open("/dev/urandom", O_RDONLY); + CHECK(urandom_fd >= 0); + bool success = file_util::ReadFromFD(urandom_fd, + reinterpret_cast(&number), + sizeof(number)); + CHECK(success); + close(urandom_fd); + + return number; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/rand_util_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/rand_util_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/rand_util_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/rand_util_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,29 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/rand_util.h" + +#include + +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const int kIntMin = std::numeric_limits::min(); +const int kIntMax = std::numeric_limits::max(); + +} // namespace + +TEST(RandUtilTest, SameMinAndMax) { + EXPECT_EQ(base::RandInt(0, 0), 0); + EXPECT_EQ(base::RandInt(kIntMin, kIntMin), kIntMin); + EXPECT_EQ(base::RandInt(kIntMax, kIntMax), kIntMax); +} + +TEST(RandUtilTest, RandDouble) { + // Force 64-bit precision, making sure we're not in a 80-bit FPU register. + volatile double number = base::RandDouble(); + EXPECT_GT(1.0, number); + EXPECT_LE(0.0, number); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/rand_util_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/rand_util_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/rand_util_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/rand_util_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,30 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/rand_util.h" + +#include + +#include "base/basictypes.h" +#include "base/logging.h" + +namespace { + +uint32 RandUint32() { + uint32 number; + CHECK(rand_s(&number) == 0); + return number; +} + +} // namespace + +namespace base { + +uint64 RandUint64() { + uint32 first_half = RandUint32(); + uint32 second_half = RandUint32(); + return (static_cast(first_half) << 32) + second_half; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/ref_counted.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/ref_counted.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/ref_counted.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/ref_counted.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,88 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/ref_counted.h" + +#include "base/logging.h" +#include "base/thread_collision_warner.h" + +namespace base { + +namespace subtle { + +RefCountedBase::RefCountedBase() : ref_count_(0) { +#ifndef NDEBUG + in_dtor_ = false; +#endif +} + +RefCountedBase::~RefCountedBase() { +#ifndef NDEBUG + DCHECK(in_dtor_) << "RefCounted object deleted without calling Release()"; +#endif +} + +void RefCountedBase::AddRef() { + // TODO(maruel): Add back once it doesn't assert 500 times/sec. + // Current thread books the critical section "AddRelease" without release it. + // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_); +#ifndef NDEBUG + DCHECK(!in_dtor_); +#endif + ++ref_count_; +} + +bool RefCountedBase::Release() { + // TODO(maruel): Add back once it doesn't assert 500 times/sec. + // Current thread books the critical section "AddRelease" without release it. + // DFAKE_SCOPED_LOCK_THREAD_LOCKED(add_release_); +#ifndef NDEBUG + DCHECK(!in_dtor_); +#endif + if (--ref_count_ == 0) { +#ifndef NDEBUG + in_dtor_ = true; +#endif + return true; + } + return false; +} + +RefCountedThreadSafeBase::RefCountedThreadSafeBase() : ref_count_(0) { +#ifndef NDEBUG + in_dtor_ = false; +#endif +} + +RefCountedThreadSafeBase::~RefCountedThreadSafeBase() { +#ifndef NDEBUG + DCHECK(in_dtor_) << "RefCountedThreadSafe object deleted without " + "calling Release()"; +#endif +} + +void RefCountedThreadSafeBase::AddRef() { +#ifndef NDEBUG + DCHECK(!in_dtor_); +#endif + AtomicRefCountInc(&ref_count_); +} + +bool RefCountedThreadSafeBase::Release() { +#ifndef NDEBUG + DCHECK(!in_dtor_); + DCHECK(!AtomicRefCountIsZero(&ref_count_)); +#endif + if (!AtomicRefCountDec(&ref_count_)) { +#ifndef NDEBUG + in_dtor_ = true; +#endif + return true; + } + return false; +} + +} // namespace subtle + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/ref_counted.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/ref_counted.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/ref_counted.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/ref_counted.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,231 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_REF_COUNTED_H_ +#define BASE_REF_COUNTED_H_ + +#include "base/atomic_ref_count.h" +#include "base/thread_collision_warner.h" + +namespace base { + +namespace subtle { + +class RefCountedBase { + protected: + RefCountedBase(); + ~RefCountedBase(); + + void AddRef(); + + // Returns true if the object should self-delete. + bool Release(); + + private: + int ref_count_; +#ifndef NDEBUG + bool in_dtor_; +#endif + + DFAKE_MUTEX(add_release_); + + DISALLOW_COPY_AND_ASSIGN(RefCountedBase); +}; + +class RefCountedThreadSafeBase { + protected: + RefCountedThreadSafeBase(); + ~RefCountedThreadSafeBase(); + + void AddRef(); + + // Returns true if the object should self-delete. + bool Release(); + + private: + AtomicRefCount ref_count_; +#ifndef NDEBUG + bool in_dtor_; +#endif + + DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafeBase); +}; + + + +} // namespace subtle + +// +// A base class for reference counted classes. Otherwise, known as a cheap +// knock-off of WebKit's RefCounted class. To use this guy just extend your +// class from it like so: +// +// class MyFoo : public base::RefCounted { +// ... +// }; +// +template +class RefCounted : public subtle::RefCountedBase { + public: + RefCounted() { } + ~RefCounted() { } + + void AddRef() { + subtle::RefCountedBase::AddRef(); + } + + void Release() { + if (subtle::RefCountedBase::Release()) { + delete static_cast(this); + } + } + + private: + DISALLOW_COPY_AND_ASSIGN(RefCounted); +}; + +// +// A thread-safe variant of RefCounted +// +// class MyFoo : public base::RefCountedThreadSafe { +// ... +// }; +// +template +class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase { + public: + RefCountedThreadSafe() { } + ~RefCountedThreadSafe() { } + + void AddRef() { + subtle::RefCountedThreadSafeBase::AddRef(); + } + + void Release() { + if (subtle::RefCountedThreadSafeBase::Release()) { + delete static_cast(this); + } + } + + private: + DISALLOW_COPY_AND_ASSIGN(RefCountedThreadSafe); +}; + +// +// A wrapper for some piece of data so we can place other things in +// scoped_refptrs<>. +// +template +class RefCountedData : public base::RefCounted< base::RefCountedData > { + public: + RefCountedData() : data() {} + RefCountedData(const T& in_value) : data(in_value) {} + + T data; +}; + +} // namespace base + +// +// A smart pointer class for reference counted objects. Use this class instead +// of calling AddRef and Release manually on a reference counted object to +// avoid common memory leaks caused by forgetting to Release an object +// reference. Sample usage: +// +// class MyFoo : public RefCounted { +// ... +// }; +// +// void some_function() { +// scoped_refptr foo = new MyFoo(); +// foo->Method(param); +// // |foo| is released when this function returns +// } +// +// void some_other_function() { +// scoped_refptr foo = new MyFoo(); +// ... +// foo = NULL; // explicitly releases |foo| +// ... +// if (foo) +// foo->Method(param); +// } +// +// The above examples show how scoped_refptr acts like a pointer to T. +// Given two scoped_refptr classes, it is also possible to exchange +// references between the two objects, like so: +// +// { +// scoped_refptr a = new MyFoo(); +// scoped_refptr b; +// +// b.swap(a); +// // now, |b| references the MyFoo object, and |a| references NULL. +// } +// +// To make both |a| and |b| in the above example reference the same MyFoo +// object, simply use the assignment operator: +// +// { +// scoped_refptr a = new MyFoo(); +// scoped_refptr b; +// +// b = a; +// // now, |a| and |b| each own a reference to the same MyFoo object. +// } +// +template +class scoped_refptr { + public: + scoped_refptr() : ptr_(NULL) { + } + + scoped_refptr(T* p) : ptr_(p) { + if (ptr_) + ptr_->AddRef(); + } + + scoped_refptr(const scoped_refptr& r) : ptr_(r.ptr_) { + if (ptr_) + ptr_->AddRef(); + } + + ~scoped_refptr() { + if (ptr_) + ptr_->Release(); + } + + T* get() const { return ptr_; } + operator T*() const { return ptr_; } + T* operator->() const { return ptr_; } + + scoped_refptr& operator=(T* p) { + // AddRef first so that self assignment should work + if (p) + p->AddRef(); + if (ptr_ ) + ptr_ ->Release(); + ptr_ = p; + return *this; + } + + scoped_refptr& operator=(const scoped_refptr& r) { + return *this = r.ptr_; + } + + void swap(T** pp) { + T* p = ptr_; + ptr_ = *pp; + *pp = p; + } + + void swap(scoped_refptr& r) { + swap(&r.ptr_); + } + + protected: + T* ptr_; +}; + +#endif // BASE_REF_COUNTED_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/ref_counted_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/ref_counted_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/ref_counted_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/ref_counted_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,33 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/gtest/include/gtest/gtest.h" +#include "base/ref_counted.h" + +namespace { + +class SelfAssign : public base::RefCounted { +}; + +class CheckDerivedMemberAccess : public scoped_refptr { + public: + CheckDerivedMemberAccess() { + // This shouldn't compile if we don't have access to the member variable. + SelfAssign** pptr = &ptr_; + EXPECT_EQ(*pptr, ptr_); + } +}; + +} // end namespace + +TEST(RefCountedUnitTest, TestSelfAssignment) { + SelfAssign* p = new SelfAssign; + scoped_refptr var = p; + var = var; + EXPECT_EQ(var.get(), p); +} + +TEST(RefCountedUnitTest, ScopedRefPtrMemberAccess) { + CheckDerivedMemberAccess check; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/registry.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/registry.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/registry.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/registry.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,417 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// All Rights Reserved. + +#include "base/registry.h" + +#include +#include +#include + +#pragma comment(lib, "shlwapi.lib") // for SHDeleteKey + +// local types (see the same declarations in the header file) +#define tchar TCHAR +#define CTP const tchar* +#define tstr std::basic_string + +// +// RegistryValueIterator +// + +RegistryValueIterator::RegistryValueIterator(HKEY root_key, + LPCTSTR folder_key) { + LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_); + if (result != ERROR_SUCCESS) { + key_ = NULL; + } else { + DWORD count = 0; + result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, &count, + NULL, NULL, NULL, NULL); + + if (result != ERROR_SUCCESS) { + ::RegCloseKey(key_); + key_ = NULL; + } else { + index_ = count - 1; + } + } + + Read(); +} + +RegistryValueIterator::~RegistryValueIterator() { + if (key_) + ::RegCloseKey(key_); +} + +bool RegistryValueIterator::Valid() const { + // true while the iterator is valid + return key_ != NULL && index_ >= 0; +} + +void RegistryValueIterator::operator ++ () { + // advance to the next entry in the folder + --index_; + Read(); +} + +bool RegistryValueIterator::Read() { + if (Valid()) { + DWORD ncount = sizeof(name_)/sizeof(*name_); + value_size_ = sizeof(value_); + LRESULT r = ::RegEnumValue(key_, index_, name_, &ncount, NULL, &type_, + reinterpret_cast(value_), &value_size_); + if (ERROR_SUCCESS == r) + return true; + } + + name_[0] = '\0'; + value_[0] = '\0'; + value_size_ = 0; + return false; +} + +DWORD RegistryValueIterator::ValueCount() const { + + DWORD count = 0; + HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, NULL, + &count, NULL, NULL, NULL, NULL); + + if (result != ERROR_SUCCESS) + return 0; + + return count; +} + +// +// RegistryKeyIterator +// + +RegistryKeyIterator::RegistryKeyIterator(HKEY root_key, + LPCTSTR folder_key) { + LONG result = RegOpenKeyEx(root_key, folder_key, 0, KEY_READ, &key_); + if (result != ERROR_SUCCESS) { + key_ = NULL; + } else { + DWORD count = 0; + HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, + NULL, NULL, NULL, NULL, NULL); + + if (result != ERROR_SUCCESS) { + ::RegCloseKey(key_); + key_ = NULL; + } else { + index_ = count - 1; + } + } + + Read(); +} + +RegistryKeyIterator::~RegistryKeyIterator() { + if (key_) + ::RegCloseKey(key_); +} + +bool RegistryKeyIterator::Valid() const { + // true while the iterator is valid + return key_ != NULL && index_ >= 0; +} + +void RegistryKeyIterator::operator ++ () { + // advance to the next entry in the folder + --index_; + Read(); +} + +bool RegistryKeyIterator::Read() { + if (Valid()) { + DWORD ncount = sizeof(name_)/sizeof(*name_); + FILETIME written; + LRESULT r = ::RegEnumKeyEx(key_, index_, name_, &ncount, NULL, NULL, + NULL, &written); + if (ERROR_SUCCESS == r) + return true; + } + + name_[0] = '\0'; + return false; +} + +DWORD RegistryKeyIterator::SubkeyCount() const { + + DWORD count = 0; + HRESULT result = ::RegQueryInfoKey(key_, NULL, 0, NULL, &count, NULL, NULL, + NULL, NULL, NULL, NULL, NULL); + + if (result != ERROR_SUCCESS) + return 0; + + return count; +} + +// +// RegKey +// + +RegKey::RegKey(HKEY rootkey, const tchar* subkey, REGSAM access) + : key_(NULL), watch_event_(0) { + if (rootkey) { + if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) + this->Create(rootkey, subkey, access); + else + this->Open(rootkey, subkey, access); + } + else assert(!subkey); +} + +void RegKey::Close() { + StopWatching(); + if (key_) { + ::RegCloseKey(key_); + key_ = NULL; + } +} + +bool RegKey::Create(HKEY rootkey, const tchar* subkey, REGSAM access) { + DWORD disposition_value; + return CreateWithDisposition(rootkey, subkey, &disposition_value, access); +} + +bool RegKey::CreateWithDisposition(HKEY rootkey, const tchar* subkey, + DWORD* disposition, REGSAM access) { + assert(rootkey && subkey && access && disposition); + this->Close(); + + LONG const result = RegCreateKeyEx(rootkey, + subkey, + 0, + NULL, + REG_OPTION_NON_VOLATILE, + access, + NULL, + &key_, + disposition ); + if (result != ERROR_SUCCESS) { + key_ = NULL; + return false; + } + else return true; +} + +bool RegKey::Open(HKEY rootkey, const tchar* subkey, REGSAM access) { + assert(rootkey && subkey && access); + this->Close(); + + LONG const result = RegOpenKeyEx(rootkey, subkey, 0, + access, &key_ ); + if (result != ERROR_SUCCESS) { + key_ = NULL; + return false; + } + else return true; +} + +bool RegKey::CreateKey(const tchar* name, REGSAM access) { + assert(name && access); + + HKEY subkey = NULL; + LONG const result = RegCreateKeyEx(key_, name, 0, NULL, + REG_OPTION_NON_VOLATILE, + access, NULL, &subkey, NULL); + this->Close(); + + key_ = subkey; + return (result == ERROR_SUCCESS); +} + +bool RegKey::OpenKey(const tchar* name, REGSAM access) { + assert(name && access); + + HKEY subkey = NULL; + LONG const result = RegOpenKeyEx(key_, name, 0, access, &subkey); + + this->Close(); + + key_ = subkey; + return (result == ERROR_SUCCESS); +} + +DWORD RegKey::ValueCount() { + DWORD count = 0; + HRESULT const result = ::RegQueryInfoKey(key_, NULL, 0, NULL, NULL, NULL, + NULL, &count, NULL, NULL, NULL, NULL); + return (result != ERROR_SUCCESS) ? 0 : count; +} + +bool RegKey::ReadName(int index, tstr* name) { + tchar buf[256]; + DWORD bufsize = sizeof(buf)/sizeof(*buf); + LRESULT r = ::RegEnumValue(key_, index, buf, &bufsize, NULL, NULL, + NULL, NULL); + if (r != ERROR_SUCCESS) + return false; + if (name) + *name = buf; + return true; +} + +bool RegKey::ValueExists(const tchar* name) { + if (!key_) return false; + const HRESULT result = RegQueryValueEx(key_, name, 0, NULL, NULL, NULL); + return (result == ERROR_SUCCESS); +} + +bool RegKey::ReadValue(const tchar* name, void* data, + DWORD* dsize, DWORD* dtype) { + if (!key_) return false; + HRESULT const result = RegQueryValueEx(key_, name, 0, dtype, + reinterpret_cast(data), + dsize); + return (result == ERROR_SUCCESS); +} + +bool RegKey::ReadValue(const tchar* name, tstr * value) { + assert(value); + static const size_t kMaxStringLength = 1024; // This is after expansion. + // Use the one of the other forms of ReadValue if 1024 is too small for you. + TCHAR raw_value[kMaxStringLength]; + DWORD type = REG_SZ, size = sizeof(raw_value); + if (this->ReadValue(name, raw_value, &size, &type)) { + if (type == REG_SZ) { + *value = raw_value; + } else if (type == REG_EXPAND_SZ) { + TCHAR expanded[kMaxStringLength]; + size = ExpandEnvironmentStrings(raw_value, expanded, kMaxStringLength); + // Success: returns the number of TCHARs copied + // Fail: buffer too small, returns the size required + // Fail: other, returns 0 + if (size == 0 || size > kMaxStringLength) + return false; + *value = expanded; + } else { + // Not a string. Oops. + return false; + } + return true; + } + else return false; +} + +bool RegKey::ReadValueDW(const tchar* name, DWORD * value) { + assert(value); + DWORD type = REG_DWORD, size = sizeof(DWORD), result = 0; + if (this->ReadValue(name, &result, &size, &type) + && (type == REG_DWORD || type == REG_BINARY) + && size == sizeof(DWORD)) { + *value = result; + return true; + } + else return false; +} + +bool RegKey::WriteValue(const tchar* name, + const void * data, + DWORD dsize, + DWORD dtype) { + assert(data); + if (!key_) return false; + HRESULT const result = RegSetValueEx( + key_, + name, + 0, + dtype, + reinterpret_cast(const_cast(data)), + dsize); + return (result == ERROR_SUCCESS); +} + +bool RegKey::WriteValue(const tchar * name, const tchar * value) { + return this->WriteValue(name, value, + static_cast(sizeof(*value) * (_tcslen(value) + 1)), REG_SZ); +} + +bool RegKey::WriteValue(const tchar * name, DWORD value) { + return this->WriteValue(name, &value, + static_cast(sizeof(value)), REG_DWORD); +} + +bool RegKey::DeleteKey(const tchar * name) { + if (!key_) return false; + return (ERROR_SUCCESS == SHDeleteKey(key_, name)); +} + + +bool RegKey::DeleteValue(const tchar * value_name) { + assert(value_name); + HRESULT const result = RegDeleteValue(key_, value_name); + return (result == ERROR_SUCCESS); +} + +bool RegKey::StartWatching() { + if (!watch_event_) + watch_event_ = CreateEvent(NULL, TRUE, FALSE, NULL); + + DWORD filter = REG_NOTIFY_CHANGE_NAME | + REG_NOTIFY_CHANGE_ATTRIBUTES | + REG_NOTIFY_CHANGE_LAST_SET | + REG_NOTIFY_CHANGE_SECURITY; + + // Watch the registry key for a change of value. + HRESULT result = RegNotifyChangeKeyValue(key_, TRUE, filter, + watch_event_, TRUE); + if (SUCCEEDED(result)) { + return true; + } else { + CloseHandle(watch_event_); + watch_event_ = 0; + return false; + } +} + +bool RegKey::StopWatching() { + if (watch_event_) { + CloseHandle(watch_event_); + watch_event_ = 0; + return true; + } + return false; +} + +bool RegKey::HasChanged() { + if (watch_event_) { + if (WaitForSingleObject(watch_event_, 0) == WAIT_OBJECT_0) { + StartWatching(); + return true; + } + } + return false; +} + +// Register a COM object with the most usual properties. +bool RegisterCOMServer(const tchar* guid, + const tchar* name, + const tchar* path) { + RegKey key(HKEY_CLASSES_ROOT, _T("CLSID"), KEY_WRITE); + key.CreateKey(guid, KEY_WRITE); + key.WriteValue(NULL, name); + key.CreateKey(_T("InprocServer32"), KEY_WRITE); + key.WriteValue(NULL, path); + key.WriteValue(_T("ThreadingModel"), _T("Apartment")); + return true; +} + +bool RegisterCOMServer(const tchar* guid, const tchar* name, HINSTANCE module) { + tchar module_path[MAX_PATH]; + ::GetModuleFileName(module, module_path, MAX_PATH); + _tcslwr_s(module_path, MAX_PATH); + return RegisterCOMServer(guid, name, module_path); +} + +bool UnregisterCOMServer(const tchar* guid) { + RegKey key(HKEY_CLASSES_ROOT, _T("CLSID"), KEY_WRITE); + key.DeleteKey(guid); + return true; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/registry.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/registry.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/registry.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/registry.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,225 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// All Rights Reserved. + +#ifndef BASE_REGISTRY_H__ +#define BASE_REGISTRY_H__ + +#include +#include +#include +#include + +// The shared file uses a bunch of header files that define types that we don't. +// To avoid changing much code from the standard version, and also to avoid +// polluting our namespace with extra types we don't want, we define these types +// here with the preprocessor and undefine them at the end of the file. +#define tchar TCHAR +#define CTP const tchar* +#define tstr std::basic_string + +// RegKey +// Utility class to read from and manipulate the registry. +// Registry vocabulary primer: a "key" is like a folder, in which there +// are "values", which are pairs, with an associated data type. + +class RegKey { + public: + RegKey(HKEY rootkey = NULL, CTP subkey = NULL, REGSAM access = KEY_READ); + // start there + + ~RegKey() { this->Close(); } + + bool Create(HKEY rootkey, CTP subkey, REGSAM access = KEY_READ); + + bool CreateWithDisposition(HKEY rootkey, CTP subkey, DWORD* disposition, + REGSAM access = KEY_READ); + + bool Open(HKEY rootkey, CTP subkey, REGSAM access = KEY_READ); + + // Create a subkey (or open if exists) + bool CreateKey(CTP name, REGSAM access); + + // Open a subkey + bool OpenKey(CTP name, REGSAM access); + + // all done, eh? + void Close(); + + DWORD ValueCount(); // Count of the number of value extant + + bool ReadName(int index, tstr* name); // Determine the Nth value's name + + // True while the key is valid + bool Valid() const { return NULL != key_; } + + // Kill key and everything that liveth below it; please be careful out there + bool DeleteKey(CTP name); + + // Delete a single value within the key + bool DeleteValue(CTP name); + + bool ValueExists(CTP name); + bool ReadValue(CTP name, void * data, DWORD * dsize, DWORD * dtype = NULL); + bool ReadValue(CTP name, tstr * value); + bool ReadValueDW(CTP name, DWORD * value); // Named to differ from tstr* + + bool WriteValue(CTP name, const void * data, DWORD dsize, + DWORD dtype = REG_BINARY); + bool WriteValue(CTP name, CTP value); + bool WriteValue(CTP name, DWORD value); + + // StartWatching() + // Start watching the key to see if any of its values have changed. + // The key must have been opened with the KEY_NOTIFY access + // privelege. + bool StartWatching(); + + // HasChanged() + // If StartWatching hasn't been called, always returns false. + // Otherwise, returns true if anything under the key has changed. + // This can't be const because the watch_event_ may be refreshed. + bool HasChanged(); + + // StopWatching() + // Will automatically be called by destructor if not manually called + // beforehand. Returns true if it was watching, false otherwise. + bool StopWatching(); + + inline bool IsWatching() const { return watch_event_ != 0; } + HANDLE watch_event() const { return watch_event_; } + HKEY Handle() const { return key_; } + + private: + HKEY key_; // the registry key being iterated + HANDLE watch_event_; +}; + + +// Standalone registry functions -- sorta deprecated, they now map to +// using RegKey + + +// Add a raw data to the registry -- you can pass NULL for the data if +// you just want to create a key +inline bool AddToRegistry(HKEY root_key, CTP key, CTP value_name, + void const * data, DWORD dsize, + DWORD dtype = REG_BINARY) { + return RegKey(root_key, key, KEY_WRITE).WriteValue(value_name, data, dsize, + dtype); +} + +// Convenience routine to add a string value to the registry +inline bool AddToRegistry(HKEY root_key, CTP key, CTP value_name, CTP value) { + return AddToRegistry(root_key, key, value_name, value, + sizeof(*value) * (lstrlen(value) + 1), REG_SZ); +} + +// Read raw data from the registry -- pass something as the dtype +// parameter if you care to learn what type the value was stored as +inline bool ReadFromRegistry(HKEY root_key, CTP key, CTP value_name, + void* data, DWORD* dsize, DWORD* dtype = NULL) { + return RegKey(root_key, key).ReadValue(value_name, data, dsize, dtype); +} + + +// Delete a value or a key from the registry +inline bool DeleteFromRegistry(HKEY root_key, CTP subkey, CTP value_name) { + if (value_name) + return ERROR_SUCCESS == ::SHDeleteValue(root_key, subkey, value_name); + else + return ERROR_SUCCESS == ::SHDeleteKey(root_key, subkey); +} + + + +// delete a key and all subkeys from the registry +inline bool DeleteKeyFromRegistry(HKEY root_key, CTP key_path, CTP key_name) { + RegKey key; + return key.Open(root_key, key_path, KEY_WRITE) + && key.DeleteKey(key_name); +} + + +// Iterates the entries found in a particular folder on the registry. +// For this application I happen to know I wont need data size larger +// than MAX_PATH, but in real life this wouldn't neccessarily be +// adequate. +class RegistryValueIterator { + public: + // Specify a key in construction + RegistryValueIterator(HKEY root_key, LPCTSTR folder_key); + + ~RegistryValueIterator(); + + DWORD ValueCount() const; // count of the number of subkeys extant + + bool Valid() const; // true while the iterator is valid + + void operator++(); // advance to the next entry in the folder + + // The pointers returned by these functions are statics owned by the + // Name and Value functions + CTP Name() const { return name_; } + CTP Value() const { return value_; } + DWORD ValueSize() const { return value_size_; } + DWORD Type() const { return type_; } + + int Index() const { return index_; } + + private: + bool Read(); // read in the current values + + HKEY key_; // the registry key being iterated + int index_; // current index of the iteration + + // Current values + TCHAR name_[MAX_PATH]; + TCHAR value_[MAX_PATH]; + DWORD value_size_; + DWORD type_; +}; + + +class RegistryKeyIterator { + public: + // Specify a parent key in construction + RegistryKeyIterator(HKEY root_key, LPCTSTR folder_key); + + ~RegistryKeyIterator(); + + DWORD SubkeyCount() const; // count of the number of subkeys extant + + bool Valid() const; // true while the iterator is valid + + void operator++(); // advance to the next entry in the folder + + // The pointer returned by Name() is a static owned by the function + CTP Name() const { return name_; } + + int Index() const { return index_; } + + private: + bool Read(); // read in the current values + + HKEY key_; // the registry key being iterated + int index_; // current index of the iteration + + // Current values + TCHAR name_[MAX_PATH]; +}; + + +// Register a COM object with the most usual properties. +bool RegisterCOMServer(const tchar* guid, const tchar* name, + const tchar* modulepath); +bool RegisterCOMServer(const tchar* guid, const tchar* name, HINSTANCE module); +bool UnregisterCOMServer(const tchar* guid); + +// undo the local types defined above +#undef tchar +#undef CTP +#undef tstr + +#endif // BASE_REGISTRY_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/resource_util.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/resource_util.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/resource_util.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/resource_util.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,37 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/resource_util.h" + +namespace base { +bool GetDataResourceFromModule(HMODULE module, int resource_id, + void** data, size_t* length) { + if (!module) + return false; + + if (!IS_INTRESOURCE(resource_id)) { + NOTREACHED(); + return false; + } + + HRSRC hres_info = FindResource(module, MAKEINTRESOURCE(resource_id), + L"BINDATA"); + if (NULL == hres_info) + return false; + + DWORD data_size = SizeofResource(module, hres_info); + HGLOBAL hres = LoadResource(module, hres_info); + if (!hres) + return false; + + void* resource = LockResource(hres); + if (!resource) + return false; + + *data = resource; + *length = static_cast(data_size); + return true; +} +} // namespace diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/resource_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/resource_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/resource_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/resource_util.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,24 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains utility functions for accessing resources in external +// files (DLLs) or embedded in the executable itself. + +#ifndef BASE_RESOURCE_UTIL_H__ +#define BASE_RESOURCE_UTIL_H__ + +#include +#include + +#include "base/basictypes.h" + +namespace base { +// Function for getting a data resource (BINDATA) from a dll. Some +// resources are optional, especially in unit tests, so this returns false +// but doesn't raise an error if the resource can't be loaded. +bool GetDataResourceFromModule(HMODULE module, int resource_id, + void** data, size_t* length); +} // namespace + +#endif // BASE_RESOURCE_UTIL_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/revocable_store.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/revocable_store.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/revocable_store.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/revocable_store.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,47 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/revocable_store.h" + +#include "base/logging.h" + +RevocableStore::Revocable::Revocable(RevocableStore* store) + : store_reference_(store->owning_reference_) { + // We AddRef() the owning reference. + DCHECK(store_reference_->store()); + store_reference_->store()->Add(this); +} + +RevocableStore::Revocable::~Revocable() { + if (!revoked()) { + // Notify the store of our destruction. + --(store_reference_->store()->count_); + } +} + +RevocableStore::RevocableStore() : count_(0) { + // Create a new owning reference. + owning_reference_ = new StoreRef(this); +} + +RevocableStore::~RevocableStore() { + // Revoke all the items in the store. + owning_reference_->set_store(NULL); +} + +void RevocableStore::Add(Revocable* item) { + DCHECK(!item->revoked()); + ++count_; +} + +void RevocableStore::RevokeAll() { + // We revoke all the existing items in the store and reset our count. + owning_reference_->set_store(NULL); + count_ = 0; + + // Then we create a new owning reference for new items that get added. + // This Release()s the old owning reference, allowing it to be freed after + // all the items that were in the store are eventually destroyed. + owning_reference_ = new StoreRef(this); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/revocable_store.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/revocable_store.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/revocable_store.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/revocable_store.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,74 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_REVOCABLE_STORE_H_ +#define BASE_REVOCABLE_STORE_H_ + +#include "base/ref_counted.h" + +// |RevocableStore| is a container of items that can be removed from the store. +class RevocableStore { + public: + // A |StoreRef| is used to link the |RevocableStore| to its items. There is + // one StoreRef per store, and each item holds a reference to it. If the + // store wishes to revoke its items, it sets |store_| to null. Items are + // permitted to release their reference to the |StoreRef| when they no longer + // require the store. + class StoreRef : public base::RefCounted { + public: + StoreRef(RevocableStore* store) : store_(store) { } + + void set_store(RevocableStore* store) { store_ = store; } + RevocableStore* store() const { return store_; } + + private: + RevocableStore* store_; + + DISALLOW_EVIL_CONSTRUCTORS(StoreRef); + }; + + // An item in the store. On construction, the object adds itself to the + // store. + class Revocable { + public: + Revocable(RevocableStore* store); + ~Revocable(); + + // This item has been revoked if it no longer has a pointer to the store. + bool revoked() const { return !store_reference_->store(); } + + private: + // We hold a reference to the store through this ref pointer. We release + // this reference on destruction. + scoped_refptr store_reference_; + + DISALLOW_EVIL_CONSTRUCTORS(Revocable); + }; + + RevocableStore(); + ~RevocableStore(); + + // Revokes all the items in the store. + void RevokeAll(); + + // Returns true if there are no items in the store. + bool empty() const { return count_ == 0; } + + private: + friend class Revocable; + + // Adds an item to the store. To add an item to the store, construct it + // with a pointer to the store. + void Add(Revocable* item); + + // This is the reference the unrevoked items in the store hold. + scoped_refptr owning_reference_; + + // The number of unrevoked items in the store. + int count_; + + DISALLOW_EVIL_CONSTRUCTORS(RevocableStore); +}; + +#endif // BASE_REVOCABLE_STORE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/run_all_perftests.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/run_all_perftests.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/run_all_perftests.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/run_all_perftests.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,9 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/perf_test_suite.h" + +int main(int argc, char** argv) { + return PerfTestSuite(argc, argv).Run(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/run_all_unittests.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/run_all_unittests.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/run_all_unittests.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/run_all_unittests.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,9 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/test_suite.h" + +int main(int argc, char** argv) { + return TestSuite(argc, argv).Run(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_bstr_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_bstr_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_bstr_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_bstr_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,66 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/scoped_bstr_win.h" + +#include "base/logging.h" + + +ScopedBstr::ScopedBstr(const wchar_t* non_bstr) + : bstr_(SysAllocString(non_bstr)) { +} + +ScopedBstr::~ScopedBstr() { + COMPILE_ASSERT(sizeof(ScopedBstr) == sizeof(BSTR), ScopedBstrSize); + SysFreeString(bstr_); +} + +void ScopedBstr::Reset(BSTR bstr) { + if (bstr != bstr_) { + // if |bstr_| is NULL, SysFreeString does nothing. + SysFreeString(bstr_); + bstr_ = bstr; + } +} + +BSTR ScopedBstr::Release() { + BSTR bstr = bstr_; + bstr_ = NULL; + return bstr; +} + +void ScopedBstr::Swap(ScopedBstr& bstr2) { + BSTR tmp = bstr_; + bstr_ = bstr2.bstr_; + bstr2.bstr_ = tmp; +} + +BSTR* ScopedBstr::Receive() { + DCHECK(bstr_ == NULL) << "BSTR leak."; + return &bstr_; +} + +BSTR ScopedBstr::Allocate(const wchar_t* wide_str) { + Reset(SysAllocString(wide_str)); + return bstr_; +} + +BSTR ScopedBstr::AllocateBytes(int bytes) { + Reset(SysAllocStringByteLen(NULL, bytes)); + return bstr_; +} + +void ScopedBstr::SetByteLen(uint32 bytes) { + DCHECK(bstr_ != NULL) << "attempting to modify a NULL bstr"; + uint32* data = reinterpret_cast(bstr_); + data[-1] = bytes; +} + +uint32 ScopedBstr::Length() const { + return SysStringLen(bstr_); +} + +uint32 ScopedBstr::ByteLength() const { + return SysStringByteLen(bstr_); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_bstr_win.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_bstr_win.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_bstr_win.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_bstr_win.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,143 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SCOPED_BSTR_WIN_H_ +#define BASE_SCOPED_BSTR_WIN_H_ + +#include "base/basictypes.h" // needed to pick up OS_WIN + +#include "base/logging.h" + +#include +#include + +// Manages a BSTR string pointer. +// The class interface is based on scoped_ptr. +class ScopedBstr { + public: + ScopedBstr() : bstr_(NULL) { + } + + // Constructor to create a new BSTR. + // NOTE: Do not pass a BSTR to this constructor expecting ownership to + // be transferred - even though it compiles! ;-) + explicit ScopedBstr(const wchar_t* non_bstr); + ~ScopedBstr(); + + // Give ScopedBstr ownership over an already allocated BSTR or NULL. + // If you need to allocate a new BSTR instance, use |allocate| instead. + void Reset(BSTR bstr = NULL); + + // Releases ownership of the BSTR to the caller. + BSTR Release(); + + // Creates a new BSTR from a wide string. + // If you already have a BSTR and want to transfer ownership to the + // ScopedBstr instance, call |reset| instead. + // Returns a pointer to the new BSTR, or NULL if allocation failed. + BSTR Allocate(const wchar_t* wide_str); + + // Allocates a new BSTR with the specified number of bytes. + // Returns a pointer to the new BSTR, or NULL if allocation failed. + BSTR AllocateBytes(int bytes); + + // Sets the allocated length field of the already-allocated BSTR to be + // |bytes|. This is useful when the BSTR was preallocated with e.g. + // SysAllocStringLen or SysAllocStringByteLen (call |AllocateBytes|) and + // then not all the bytes are being used. + // Note that if you want to set the length to a specific number of characters, + // you need to multiply by sizeof(wchar_t). Oddly, there's no public API to + // set the length, so we do this ourselves by hand. + // + // NOTE: The actual allocated size of the BSTR MUST be >= bytes. + // That responsibility is with the caller. + void SetByteLen(uint32 bytes); + + // Swap values of two ScopedBstr's. + void Swap(ScopedBstr& bstr2); + + // Retrieves the pointer address. + // Used to receive BSTRs as out arguments (and take ownership). + // The function DCHECKs on the current value being NULL. + // Usage: GetBstr(bstr.Receive()); + BSTR* Receive(); + + // Returns number of chars in the BSTR. + uint32 Length() const; + + // Returns the number of bytes allocated for the BSTR. + uint32 ByteLength() const; + + operator BSTR() const { + return bstr_; + } + + protected: + BSTR bstr_; + + private: + // Forbid comparison of ScopedBstr types. You should never have the same + // BSTR owned by two different scoped_ptrs. + bool operator==(const ScopedBstr& bstr2) const; + bool operator!=(const ScopedBstr& bstr2) const; + DISALLOW_COPY_AND_ASSIGN(ScopedBstr); +}; + +// Template class to generate a BSTR from a static wide string +// without touching the heap. Use this class via the StackBstrVar and +// StackBstr macros. +template +class StackBstrT { + public: + // Try to stay as const as we can in an attempt to avoid someone + // using the class incorrectly (e.g. by supplying a variable instead + // of a verbatim string. We also have an assert in the constructor + // as an extra runtime check since the const-ness only catches one case. + explicit StackBstrT(const wchar_t* const str) { + // The BSTR API uses UINT, but we prefer uint32. + // Make sure we'll know about it if these types don't match. + COMPILE_ASSERT(sizeof(uint32) == sizeof(UINT), UintToUint32); + COMPILE_ASSERT(sizeof(wchar_t) == sizeof(OLECHAR), WcharToOlechar); + + // You shouldn't pass string pointers to this constructor since + // there's no way for the compiler to calculate the length of the + // string (string_bytes will be equal to pointer size in those cases). + DCHECK(lstrlenW(str) == (string_bytes / sizeof(bstr_.str_[0])) - 1) << + "not expecting a string pointer"; + memcpy(bstr_.str_, str, string_bytes); + bstr_.len_ = string_bytes - sizeof(wchar_t); + } + + operator BSTR() { + return bstr_.str_; + } + + protected: + struct BstrInternal { + uint32 len_; + wchar_t str_[string_bytes / sizeof(wchar_t)]; + } bstr_; +}; + +// Use this macro to generate an inline BSTR from a wide string. +// This is about 6 times faster than using the SysAllocXxx functions to +// allocate a BSTR and helps with keeping heap fragmentation down. +// Example: +// DoBstrStuff(StackBstr(L"This is my BSTR")); +// Where DoBstrStuff is: +// HRESULT DoBstrStuff(BSTR bstr) { ... } +#define StackBstr(str) \ + static_cast(StackBstrT(str)) + +// If you need a named BSTR variable that's based on a fixed string +// (e.g. if the BSTR is used inside a loop or more than one place), +// use StackBstrVar to declare a variable. +// Example: +// StackBstrVar(L"my_property", myprop); +// for (int i = 0; i < objects.length(); ++i) +// ProcessValue(objects[i].GetProp(myprop)); // GetProp accepts BSTR +#define StackBstrVar(str, var) \ + StackBstrT var(str) + +#endif // BASE_SCOPED_BSTR_WIN_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_bstr_win_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_bstr_win_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_bstr_win_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_bstr_win_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,93 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/scoped_bstr_win.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +static const wchar_t kTestString1[] = L"123"; +static const wchar_t kTestString2[] = L"456789"; +uint32 test1_len = arraysize(kTestString1) - 1; +uint32 test2_len = arraysize(kTestString2) - 1; + +void DumbBstrTests() { + ScopedBstr b; + EXPECT_TRUE(b == NULL); + EXPECT_TRUE(b.Length() == 0); + EXPECT_TRUE(b.ByteLength() == 0); + b.Reset(NULL); + EXPECT_TRUE(b == NULL); + EXPECT_TRUE(b.Release() == NULL); + ScopedBstr b2; + b.Swap(b2); + EXPECT_TRUE(b2 == NULL); +} + +void GiveMeABstr(BSTR* ret) { + *ret = SysAllocString(kTestString1); +} + +void BasicBstrTests() { + ScopedBstr b1(kTestString1); + EXPECT_TRUE(b1.Length() == test1_len); + EXPECT_TRUE(b1.ByteLength() == test1_len * sizeof(kTestString1[0])); + + ScopedBstr b2; + b1.Swap(b2); + EXPECT_TRUE(b2.Length() == test1_len); + EXPECT_TRUE(b1.Length() == 0); + EXPECT_TRUE(lstrcmpW(b2, kTestString1) == 0); + BSTR tmp = b2.Release(); + EXPECT_TRUE(tmp != NULL); + EXPECT_TRUE(lstrcmpW(tmp, kTestString1) == 0); + EXPECT_TRUE(b2 == NULL); + SysFreeString(tmp); + + GiveMeABstr(b2.Receive()); + EXPECT_TRUE(b2 != NULL); + b2.Reset(); + EXPECT_TRUE(b2.AllocateBytes(100) != NULL); + EXPECT_TRUE(b2.ByteLength() == 100); + EXPECT_TRUE(b2.Length() == 100 / sizeof(kTestString1[0])); + lstrcpyW(static_cast(b2), kTestString1); + EXPECT_TRUE(lstrlen(b2) == test1_len); + EXPECT_TRUE(b2.Length() == 100 / sizeof(kTestString1[0])); + b2.SetByteLen(lstrlen(b2) * sizeof(kTestString2[0])); + EXPECT_TRUE(lstrlen(b2) == b2.Length()); + + EXPECT_TRUE(b1.Allocate(kTestString2) != NULL); + EXPECT_TRUE(b1.Length() == test2_len); + b1.SetByteLen((test2_len - 1) * sizeof(kTestString2[0])); + EXPECT_TRUE(b1.Length() == test2_len - 1); +} + +} // namespace + +TEST(ScopedBstrTest, ScopedBstr) { + DumbBstrTests(); + BasicBstrTests(); +} + +#define kSourceStr L"this is a string" +#define kSourceStrEmpty L"" + +TEST(StackBstrTest, StackBstr) { + ScopedBstr system_bstr(kSourceStr); + StackBstrVar(kSourceStr, stack_bstr); + EXPECT_EQ(VarBstrCmp(system_bstr, stack_bstr, LOCALE_USER_DEFAULT, 0), + VARCMP_EQ); + + StackBstrVar(kSourceStrEmpty, empty); + uint32 l1 = SysStringLen(stack_bstr); + uint32 l2 = SysStringLen(StackBstr(kSourceStr)); + uint32 l3 = SysStringLen(system_bstr); + EXPECT_TRUE(l1 == l2); + EXPECT_TRUE(l2 == l3); + EXPECT_TRUE(SysStringLen(empty) == 0); + + const wchar_t one_more_test[] = L"this is my const string"; + EXPECT_EQ(SysStringLen(StackBstr(one_more_test)), + lstrlenW(one_more_test)); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_cftyperef.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_cftyperef.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_cftyperef.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_cftyperef.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,78 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SCOPED_CFTYPEREF_H_ +#define BASE_SCOPED_CFTYPEREF_H_ + +#include +#include "base/basictypes.h" + +// scoped_cftyperef<> is patterned after scoped_ptr<>, but maintains ownership +// of a CoreFoundation object: any object that can be represented as a +// CFTypeRef. Style deviations here are solely for compatibility with +// scoped_ptr<>'s interface, with which everyone is already familiar. +// +// When scoped_cftyperef<> takes ownership of an object (in the constructor or +// in reset()), it takes over the caller's existing ownership claim. The +// caller must own the object it gives to scoped_cftyperef<>, and relinquishes +// an ownership claim to that object. scoped_cftyperef<> does not call +// CFRetain(). +template +class scoped_cftyperef { + public: + typedef CFT element_type; + + explicit scoped_cftyperef(CFT object = NULL) + : object_(object) { + } + + ~scoped_cftyperef() { + if (object_) + CFRelease(object_); + } + + void reset(CFT object = NULL) { + if (object_) + CFRelease(object_); + object_ = object; + } + + bool operator==(CFT that) const { + return object_ == that; + } + + bool operator!=(CFT that) const { + return object_ != that; + } + + operator CFT() const { + return object_; + } + + CFT get() const { + return object_; + } + + void swap(scoped_cftyperef& that) { + CFT temp = that.object_; + that.object_ = object_; + object_ = temp; + } + + // scoped_cftyperef<>::release() is like scoped_ptr<>::release. It is NOT + // a wrapper for CFRelease(). To force a scoped_cftyperef<> object to call + // CFRelease(), use scoped_cftyperef<>::reset(). + CFT release() { + CFT temp = object_; + object_ = NULL; + return temp; + } + + private: + CFT object_; + + DISALLOW_COPY_AND_ASSIGN(scoped_cftyperef); +}; + +#endif // BASE_SCOPED_CFTYPEREF_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_clipboard_writer.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_clipboard_writer.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_clipboard_writer.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_clipboard_writer.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,140 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file implements the ScopedClipboardWriter class. Documentation on its +// purpose can be found in base/scoped_clipboard_writer.h. Documentation on the +// format of the parameters for each clipboard target can be found in +// base/clipboard.h. +#include "base/scoped_clipboard_writer.h" + +#include "base/string_util.h" + +ScopedClipboardWriter::ScopedClipboardWriter(Clipboard* clipboard) + : clipboard_(clipboard) { +} + +ScopedClipboardWriter::~ScopedClipboardWriter() { + if (!objects_.empty() && clipboard_) + clipboard_->WriteObjects(objects_); +} + +void ScopedClipboardWriter::WriteText(const string16& text) { + if (text.empty()) + return; + + std::string utf8_text = UTF16ToUTF8(text); + + Clipboard::ObjectMapParams parameters; + parameters.push_back(Clipboard::ObjectMapParam(utf8_text.begin(), + utf8_text.end())); + objects_[Clipboard::CBF_TEXT] = parameters; +} + +void ScopedClipboardWriter::WriteHTML(const string16& markup, + const std::string& source_url) { + if (markup.empty()) + return; + + std::string utf8_markup = UTF16ToUTF8(markup); + + Clipboard::ObjectMapParams parameters; + parameters.push_back( + Clipboard::ObjectMapParam(utf8_markup.begin(), + utf8_markup.end())); + if (!source_url.empty()) { + parameters.push_back(Clipboard::ObjectMapParam(source_url.begin(), + source_url.end())); + } + + objects_[Clipboard::CBF_HTML] = parameters; +} + +void ScopedClipboardWriter::WriteBookmark(const string16& bookmark_title, + const std::string& url) { + if (bookmark_title.empty() || url.empty()) + return; + + std::string utf8_markup = UTF16ToUTF8(bookmark_title); + + Clipboard::ObjectMapParams parameters; + parameters.push_back(Clipboard::ObjectMapParam(utf8_markup.begin(), + utf8_markup.end())); + parameters.push_back(Clipboard::ObjectMapParam(url.begin(), url.end())); + objects_[Clipboard::CBF_BOOKMARK] = parameters; +} + +void ScopedClipboardWriter::WriteHyperlink(const string16& link_text, + const std::string& url) { + if (link_text.empty() || url.empty()) + return; + + std::string utf8_markup = UTF16ToUTF8(link_text); + + Clipboard::ObjectMapParams parameters; + parameters.push_back(Clipboard::ObjectMapParam(utf8_markup.begin(), + utf8_markup.end())); + parameters.push_back(Clipboard::ObjectMapParam(url.begin(), url.end())); + objects_[Clipboard::CBF_LINK] = parameters; +} + +void ScopedClipboardWriter::WriteFile(const FilePath& file) { + WriteFiles(std::vector(1, file)); +} + +// Save the filenames as a string separated by nulls and terminated with an +// extra null. +void ScopedClipboardWriter::WriteFiles(const std::vector& files) { + if (files.empty()) + return; + + Clipboard::ObjectMapParam parameter; + + for (std::vector::const_iterator iter = files.begin(); + iter != files.end(); ++iter) { + FilePath filepath = *iter; + FilePath::StringType filename = filepath.value(); + + size_t data_length = filename.length() * sizeof(FilePath::CharType); + const char* data = reinterpret_cast(filename.data()); + const char* data_end = data + data_length; + + for (const char* ch = data; ch < data_end; ++ch) + parameter.push_back(*ch); + + // NUL-terminate the string. + for (size_t i = 0; i < sizeof(FilePath::CharType); ++i) + parameter.push_back('\0'); + } + + // NUL-terminate the string list. + for (size_t i = 0; i < sizeof(FilePath::CharType); ++i) + parameter.push_back('\0'); + + Clipboard::ObjectMapParams parameters; + parameters.push_back(parameter); + objects_[Clipboard::CBF_FILES] = parameters; +} + +void ScopedClipboardWriter::WriteWebSmartPaste() { + objects_[Clipboard::CBF_WEBKIT] = Clipboard::ObjectMapParams(); +} + +void ScopedClipboardWriter::WriteBitmapFromPixels(const void* pixels, + const gfx::Size& size) { + Clipboard::ObjectMapParam pixels_parameter, size_parameter; + const char* pixels_data = reinterpret_cast(pixels); + size_t pixels_length = 4 * size.width() * size.height(); + for (size_t i = 0; i < pixels_length; i++) + pixels_parameter.push_back(pixels_data[i]); + + const char* size_data = reinterpret_cast(&size); + size_t size_length = sizeof(gfx::Size); + for (size_t i = 0; i < size_length; i++) + size_parameter.push_back(size_data[i]); + + Clipboard::ObjectMapParams parameters; + parameters.push_back(pixels_parameter); + parameters.push_back(size_parameter); + objects_[Clipboard::CBF_BITMAP] = parameters; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_clipboard_writer.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_clipboard_writer.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_clipboard_writer.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_clipboard_writer.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,68 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file declares the ScopedClipboardWriter class, a wrapper around +// the Clipboard class which simplifies writing data to the system clipboard. +// Upon deletion the class atomically writes all data to |clipboard_|, +// avoiding any potential race condition with other processes that are also +// writing to the system clipboard. +#ifndef BASE_SCOPED_CLIPBOARD_WRITER_H_ +#define BASE_SCOPED_CLIPBOARD_WRITER_H_ + +#include +#include + +#include "base/clipboard.h" +#include "base/file_path.h" +#include "base/string16.h" + +// This class is a wrapper for |Clipboard| that handles packing data +// into a Clipboard::ObjectMap. +// NB: You should probably NOT be using this class if you include +// webkit_glue.h. Use ScopedClipboardWriterGlue instead. +class ScopedClipboardWriter { + public: + // Create an instance that is a simple wrapper around clipboard. + ScopedClipboardWriter(Clipboard* clipboard); + + ~ScopedClipboardWriter(); + + // Converts |text| to UTF-8 and adds it to the clipboard. + void WriteText(const string16& text); + + // Adds HTML to the clipboard. The url parameter is optional, but especially + // useful if the HTML fragment contains relative links. + void WriteHTML(const string16& markup, const std::string& source_url); + + // Adds a bookmark to the clipboard. + void WriteBookmark(const string16& bookmark_title, + const std::string& url); + + // Adds both a bookmark and an HTML hyperlink to the clipboard. It is a + // convenience wrapper around WriteBookmark and WriteHTML. |link_text| is + // used as the bookmark title. + void WriteHyperlink(const string16& link_text, const std::string& url); + + // Adds a file or group of files to the clipboard. + void WriteFile(const FilePath& file); + void WriteFiles(const std::vector& files); + + // Used by WebKit to determine whether WebKit wrote the clipboard last + void WriteWebSmartPaste(); + + // Adds a bitmap to the clipboard + // Pixel format is assumed to be 32-bit BI_RGB. + void WriteBitmapFromPixels(const void* pixels, const gfx::Size& size); + + protected: + // We accumulate the data passed to the various targets in the |objects_| + // vector, and pass it to Clipboard::WriteObjects() during object destruction. + Clipboard::ObjectMap objects_; + Clipboard* clipboard_; + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedClipboardWriter); +}; + +#endif // BASE_SCOPED_CLIPBOARD_WRITER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_comptr_win.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_comptr_win.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_comptr_win.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_comptr_win.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,151 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SCOPED_COMPTR_WIN_H_ +#define BASE_SCOPED_COMPTR_WIN_H_ + +#include + +#include "base/logging.h" +#include "base/ref_counted.h" + +// Utility template to prevent users of ScopedComPtr from calling AddRef and/or +// Release() without going through the ScopedComPtr class. +template +class BlockIUnknownMethods : public Interface { + private: + STDMETHOD(QueryInterface)(REFIID iid, void** object) = 0; + STDMETHOD_(ULONG, AddRef)() = 0; + STDMETHOD_(ULONG, Release)() = 0; +}; + +// A fairly minimalistic smart class for COM interface pointers. +// Uses scoped_refptr for the basic smart pointer functionality +// and adds a few IUnknown specific services. +template +class ScopedComPtr : public scoped_refptr { + public: + typedef scoped_refptr ParentClass; + + ScopedComPtr() { + } + + explicit ScopedComPtr(Interface* p) : ParentClass(p) { + } + + explicit ScopedComPtr(const ScopedComPtr& p) + : ParentClass(p) { + } + + ~ScopedComPtr() { + // We don't want the smart pointer class to be bigger than the pointer + // it wraps. + COMPILE_ASSERT(sizeof(ScopedComPtr) == + sizeof(Interface*), ScopedComPtrSize); + } + + // Explicit Release() of the held object. Useful for reuse of the + // ScopedComPtr instance. + // Note that this function equates to IUnknown::Release and should not + // be confused with e.g. scoped_ptr::release(). + void Release() { + if (ptr_ != NULL) { + ptr_->Release(); + ptr_ = NULL; + } + } + + // Sets the internal pointer to NULL and returns the held object without + // releasing the reference. + Interface* Detach() { + Interface* p = ptr_; + ptr_ = NULL; + return p; + } + + // Accepts an interface pointer that has already been addref-ed. + void Attach(Interface* p) { + DCHECK(ptr_ == NULL); + ptr_ = p; + } + + // Retrieves the pointer address. + // Used to receive object pointers as out arguments (and take ownership). + // The function DCHECKs on the current value being NULL. + // Usage: Foo(p.Receive()); + Interface** Receive() { + DCHECK(ptr_ == NULL) << "Object leak. Pointer must be NULL"; + return &ptr_; + } + + template + HRESULT QueryInterface(Query** p) { + DCHECK(p != NULL); + DCHECK(ptr_ != NULL); + // IUnknown already has a template version of QueryInterface + // so the iid parameter is implicit here. The only thing this + // function adds are the DCHECKs. + return ptr_->QueryInterface(p); + } + + // Queries |other| for the interface this object wraps and returns the + // error code from the other->QueryInterface operation. + HRESULT QueryFrom(IUnknown* object) { + DCHECK(object != NULL); + return object->QueryInterface(Receive()); + } + + // Convenience wrapper around CoCreateInstance + HRESULT CreateInstance(const CLSID& clsid, IUnknown* outer = NULL, + DWORD context = CLSCTX_ALL) { + DCHECK(ptr_ == NULL); + HRESULT hr = ::CoCreateInstance(clsid, outer, context, *interface_id, + reinterpret_cast(&ptr_)); + return hr; + } + + // Checks if the identity of |other| and this object is the same. + bool IsSameObject(IUnknown* other) { + if (!other && !ptr_) + return true; + + if (!other || !ptr_) + return false; + + ScopedComPtr my_identity; + QueryInterface(my_identity.Receive()); + + ScopedComPtr other_identity; + other->QueryInterface(other_identity.Receive()); + + return static_cast(my_identity) == + static_cast(other_identity); + } + + // Provides direct access to the interface. + // Here we use a well known trick to make sure we block access to + // IUknown methods so that something bad like this doesn't happen: + // ScopedComPtr p(Foo()); + // p->Release(); + // ... later the destructor runs, which will Release() again. + // and to get the benefit of the DCHECKs we add to QueryInterface. + // There's still a way to call these methods if you absolutely must + // by statically casting the ScopedComPtr instance to the wrapped interface + // and then making the call... but generally that shouldn't be necessary. + BlockIUnknownMethods* operator->() const { + DCHECK(ptr_ != NULL); + return reinterpret_cast*>(ptr_); + } + + // Pull in operator=() from the parent class. + using scoped_refptr::operator=; + + // static methods + + static const IID& iid() { + return *interface_id; + } +}; + +#endif // BASE_SCOPED_COMPTR_WIN_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_comptr_win_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_comptr_win_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_comptr_win_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_comptr_win_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,52 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/scoped_comptr_win.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(ScopedComPtrTest, ScopedComPtr) { + EXPECT_TRUE(memcmp(&ScopedComPtr::iid(), &IID_IUnknown, + sizeof(IID)) == 0); + + EXPECT_TRUE(SUCCEEDED(::CoInitialize(NULL))); + + { + ScopedComPtr unk; + EXPECT_TRUE(SUCCEEDED(unk.CreateInstance(CLSID_ShellLink))); + ScopedComPtr unk2; + unk2.Attach(unk.Detach()); + EXPECT_TRUE(unk == NULL); + EXPECT_TRUE(unk2 != NULL); + + ScopedComPtr mem_alloc; + EXPECT_TRUE(SUCCEEDED(CoGetMalloc(1, mem_alloc.Receive()))); + + // test ScopedComPtr& constructor + ScopedComPtr copy1(mem_alloc); + EXPECT_TRUE(copy1.IsSameObject(mem_alloc)); + EXPECT_FALSE(copy1.IsSameObject(unk2)); // unk2 is valid but different + EXPECT_FALSE(copy1.IsSameObject(unk)); // unk is NULL + + IMalloc* naked_copy = copy1.Detach(); + copy1 = naked_copy; // Test the =(T*) operator. + naked_copy->Release(); + + copy1.Release(); + EXPECT_FALSE(copy1.IsSameObject(unk2)); // unk2 is valid, copy1 is not + + // test Interface* constructor + ScopedComPtr copy2(static_cast(mem_alloc)); + EXPECT_TRUE(copy2.IsSameObject(mem_alloc)); + + EXPECT_TRUE(SUCCEEDED(unk.QueryFrom(mem_alloc))); + EXPECT_TRUE(unk != NULL); + unk.Release(); + EXPECT_TRUE(unk == NULL); + EXPECT_TRUE(unk.IsSameObject(copy1)); // both are NULL + } + + ::CoUninitialize(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_handle.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_handle.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_handle.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_handle.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,54 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SCOPED_HANDLE_H_ +#define BASE_SCOPED_HANDLE_H_ + +#include + +#include "base/basictypes.h" + +#if defined(OS_WIN) +#include "base/scoped_handle_win.h" +#endif + +class ScopedStdioHandle { + public: + ScopedStdioHandle() + : handle_(NULL) { } + + explicit ScopedStdioHandle(FILE* handle) + : handle_(handle) { } + + ~ScopedStdioHandle() { + Close(); + } + + void Close() { + if (handle_) { + fclose(handle_); + handle_ = NULL; + } + } + + FILE* get() const { return handle_; } + + FILE* Take() { + FILE* temp = handle_; + handle_ = NULL; + return temp; + } + + void Set(FILE* newhandle) { + Close(); + handle_ = newhandle; + } + + private: + FILE* handle_; + + DISALLOW_EVIL_CONSTRUCTORS(ScopedStdioHandle); +}; + +#endif // BASE_SCOPED_HANDLE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_handle_win.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_handle_win.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_handle_win.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_handle_win.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,217 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SCOPED_HANDLE_WIN_H_ +#define BASE_SCOPED_HANDLE_WIN_H_ + +#include + +#include "base/basictypes.h" +#include "base/logging.h" + +// Used so we always remember to close the handle. +// The class interface matches that of ScopedStdioHandle in addition to an +// IsValid() method since invalid handles on windows can be either NULL or +// INVALID_HANDLE_VALUE (-1). +// +// Example: +// ScopedHandle hfile(CreateFile(...)); +// if (!hfile.Get()) +// ...process error +// ReadFile(hfile.Get(), ...); +// +// To sqirrel the handle away somewhere else: +// secret_handle_ = hfile.Take(); +// +// To explicitly close the handle: +// hfile.Close(); +class ScopedHandle { + public: + ScopedHandle() : handle_(NULL) { + } + + explicit ScopedHandle(HANDLE h) : handle_(NULL) { + Set(h); + } + + ~ScopedHandle() { + Close(); + } + + // Use this instead of comparing to INVALID_HANDLE_VALUE to pick up our NULL + // usage for errors. + bool IsValid() const { + return handle_ != NULL; + } + + void Set(HANDLE new_handle) { + Close(); + + // Windows is inconsistent about invalid handles, so we always use NULL + if (new_handle != INVALID_HANDLE_VALUE) + handle_ = new_handle; + } + + HANDLE Get() { + return handle_; + } + + operator HANDLE() { return handle_; } + + HANDLE Take() { + // transfers ownership away from this object + HANDLE h = handle_; + handle_ = NULL; + return h; + } + + void Close() { + if (handle_) { + if (!::CloseHandle(handle_)) { + NOTREACHED(); + } + handle_ = NULL; + } + } + + private: + HANDLE handle_; + DISALLOW_EVIL_CONSTRUCTORS(ScopedHandle); +}; + +// Like ScopedHandle, but for HANDLEs returned from FindFile(). +class ScopedFindFileHandle { + public: + explicit ScopedFindFileHandle(HANDLE handle) : handle_(handle) { + // Windows is inconsistent about invalid handles, so we always use NULL + if (handle_ == INVALID_HANDLE_VALUE) + handle_ = NULL; + } + + ~ScopedFindFileHandle() { + if (handle_) + FindClose(handle_); + } + + // Use this instead of comparing to INVALID_HANDLE_VALUE to pick up our NULL + // usage for errors. + bool IsValid() const { return handle_ != NULL; } + + operator HANDLE() { return handle_; } + + private: + HANDLE handle_; + + DISALLOW_EVIL_CONSTRUCTORS(ScopedFindFileHandle); +}; + +// Like ScopedHandle but for HDC. Only use this on HDCs returned from +// CreateCompatibleDC. For an HDC returned by GetDC, use ReleaseDC instead. +class ScopedHDC { + public: + ScopedHDC() : hdc_(NULL) { } + explicit ScopedHDC(HDC h) : hdc_(h) { } + + ~ScopedHDC() { + Close(); + } + + HDC Get() { + return hdc_; + } + + void Set(HDC h) { + Close(); + hdc_ = h; + } + + operator HDC() { return hdc_; } + + private: + void Close() { +#ifdef NOGDI + assert(false); +#else + if (hdc_) + DeleteDC(hdc_); +#endif // NOGDI + } + + HDC hdc_; + DISALLOW_EVIL_CONSTRUCTORS(ScopedHDC); +}; + +// Like ScopedHandle but for GDI objects. +template +class ScopedGDIObject { + public: + ScopedGDIObject() : object_(NULL) {} + explicit ScopedGDIObject(T object) : object_(object) {} + + ~ScopedGDIObject() { + Close(); + } + + T Get() { + return object_; + } + + void Set(T object) { + if (object_ && object != object_) + Close(); + object_ = object; + } + + ScopedGDIObject& operator=(T object) { + Set(object); + return *this; + } + + operator T() { return object_; } + + private: + void Close() { + if (object_) + DeleteObject(object_); + } + + T object_; + DISALLOW_COPY_AND_ASSIGN(ScopedGDIObject); +}; + +// Typedefs for some common use cases. +typedef ScopedGDIObject ScopedBitmap; +typedef ScopedGDIObject ScopedHRGN; +typedef ScopedGDIObject ScopedHFONT; + + +// Like ScopedHandle except for HGLOBAL. +template +class ScopedHGlobal { + public: + explicit ScopedHGlobal(HGLOBAL glob) : glob_(glob) { + data_ = static_cast(GlobalLock(glob_)); + } + ~ScopedHGlobal() { + GlobalUnlock(glob_); + } + + T* get() { return data_; } + + size_t Size() const { return GlobalSize(glob_); } + + T* operator->() const { + assert(data_ != 0); + return data_; + } + + private: + HGLOBAL glob_; + + T* data_; + + DISALLOW_EVIL_CONSTRUCTORS(ScopedHGlobal); +}; + +#endif // BASE_SCOPED_HANDLE_WIN_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_nsautorelease_pool.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_nsautorelease_pool.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_nsautorelease_pool.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_nsautorelease_pool.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,52 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SCOPED_NSAUTORELEASE_POOL_H_ +#define BASE_SCOPED_NSAUTORELEASE_POOL_H_ + +#include "base/basictypes.h" + +#if defined(OS_MACOSX) +#if defined(__OBJC__) +@class NSAutoreleasePool; +#else // __OBJC__ +class NSAutoreleasePool; +#endif // __OBJC__ +#endif // OS_MACOSX + +namespace base { + +// On the Mac, ScopedNSAutoreleasePool allocates an NSAutoreleasePool when +// instantiated and sends it a -drain message when destroyed. This allows an +// autorelease pool to be maintained in ordinary C++ code without bringing in +// any direct Objective-C dependency. +// +// On other platforms, ScopedNSAutoreleasePool is an empty object with no +// effects. This allows it to be used directly in cross-platform code without +// ugly #ifdefs. +class ScopedNSAutoreleasePool { + public: +#if !defined(OS_MACOSX) + ScopedNSAutoreleasePool() {} + void Recycle() { } +#else // OS_MACOSX + ScopedNSAutoreleasePool(); + ~ScopedNSAutoreleasePool(); + + // Clear out the pool in case its position on the stack causes it to be + // alive for long periods of time (such as the entire length of the app). + // Only use then when you're certain the items currently in the pool are + // no longer needed. + void Recycle(); + private: + NSAutoreleasePool* autorelease_pool_; +#endif // OS_MACOSX + + private: + DISALLOW_COPY_AND_ASSIGN(ScopedNSAutoreleasePool); +}; + +} // namespace base + +#endif // BASE_SCOPED_NSAUTORELEASE_POOL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_nsautorelease_pool.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_nsautorelease_pool.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_nsautorelease_pool.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_nsautorelease_pool.mm 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,30 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/scoped_nsautorelease_pool.h" + +#import + +#include "base/logging.h" + +namespace base { + +ScopedNSAutoreleasePool::ScopedNSAutoreleasePool() + : autorelease_pool_([[NSAutoreleasePool alloc] init]) { + DCHECK(autorelease_pool_); +} + +ScopedNSAutoreleasePool::~ScopedNSAutoreleasePool() { + [autorelease_pool_ drain]; +} + +// Cycle the internal pool, allowing everything there to get cleaned up and +// start anew. +void ScopedNSAutoreleasePool::Recycle() { + [autorelease_pool_ drain]; + autorelease_pool_ = [[NSAutoreleasePool alloc] init]; + DCHECK(autorelease_pool_); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_nsobject.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_nsobject.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_nsobject.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_nsobject.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,76 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SCOPED_NSOBJECT_H_ +#define BASE_SCOPED_NSOBJECT_H_ + +#import +#include "base/basictypes.h" + +// scoped_nsobject<> is patterned after scoped_ptr<>, but maintains ownership +// of an NSObject subclass object. Style deviations here are solely for +// compatibility with scoped_ptr<>'s interface, with which everyone is already +// familiar. +// +// When scoped_nsobject<> takes ownership of an object (in the constructor or +// in reset()), it takes over the caller's existing ownership claim. The +// caller must own the object it gives to scoped_nsobject<>, and relinquishes +// an ownership claim to that object. scoped_nsobject<> does not call +// -retain. +template +class scoped_nsobject { + public: + typedef NST* element_type; + + explicit scoped_nsobject(NST* object = nil) + : object_(object) { + } + + ~scoped_nsobject() { + [object_ release]; + } + + void reset(NST* object = nil) { + [object_ release]; + object_ = object; + } + + bool operator==(NST* that) const { + return object_ == that; + } + + bool operator!=(NST* that) const { + return object_ != that; + } + + operator NST*() const { + return object_; + } + + NST* get() const { + return object_; + } + + void swap(scoped_nsobject& that) { + NST* temp = that.object_; + that.object_ = object_; + object_ = temp; + } + + // scoped_nsobject<>::release() is like scoped_ptr<>::release. It is NOT + // a wrapper for [object_ release]. To force a scoped_nsobject<> object to + // call [object_ release], use scoped_nsobject<>::reset(). + NST* release() { + NST* temp = object_; + object_ = nil; + return temp; + } + + private: + NST* object_; + + DISALLOW_COPY_AND_ASSIGN(scoped_nsobject); +}; + +#endif // BASE_SCOPED_NSOBJECT_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_ptr.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_ptr.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_ptr.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_ptr.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,380 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Scopers help you manage ownership of a pointer, helping you easily manage the +// a pointer within a scope, and automatically destroying the pointer at the +// end of a scope. There are two main classes you will use, which coorespond +// to the operators new/delete and new[]/delete[]. +// +// Example usage (scoped_ptr): +// { +// scoped_ptr foo(new Foo("wee")); +// } // foo goes out of scope, releasing the pointer with it. +// +// { +// scoped_ptr foo; // No pointer managed. +// foo.reset(new Foo("wee")); // Now a pointer is managed. +// foo.reset(new Foo("wee2")); // Foo("wee") was destroyed. +// foo.reset(new Foo("wee3")); // Foo("wee2") was destroyed. +// foo->Method(); // Foo::Method() called. +// foo.get()->Method(); // Foo::Method() called. +// SomeFunc(foo.Release()); // SomeFunc takes owernship, foo no longer +// // manages a pointer. +// foo.reset(new Foo("wee4")); // foo manages a pointer again. +// foo.reset(); // Foo("wee4") destroyed, foo no longer +// // manages a pointer. +// } // foo wasn't managing a pointer, so nothing was destroyed. +// +// Example usage (scoped_array): +// { +// scoped_array foo(new Foo[100]); +// foo.get()->Method(); // Foo::Method on the 0th element. +// foo[10].Method(); // Foo::Method on the 10th element. +// } + +#ifndef BASE_SCOPED_PTR_H_ +#define BASE_SCOPED_PTR_H_ + +// This is an implementation designed to match the anticipated future TR2 +// implementation of the scoped_ptr class, and its closely-related brethren, +// scoped_array, scoped_ptr_malloc. + +#include +#include +#include + +// A scoped_ptr is like a T*, except that the destructor of scoped_ptr +// automatically deletes the pointer it holds (if any). +// That is, scoped_ptr owns the T object that it points to. +// Like a T*, a scoped_ptr may hold either NULL or a pointer to a T object. +// Also like T*, scoped_ptr is thread-compatible, and once you +// dereference it, you get the threadsafety guarantees of T. +// +// The size of a scoped_ptr is small: +// sizeof(scoped_ptr) == sizeof(C*) +template +class scoped_ptr { + public: + + // The element type + typedef C element_type; + + // Constructor. Defaults to intializing with NULL. + // There is no way to create an uninitialized scoped_ptr. + // The input parameter must be allocated with new. + explicit scoped_ptr(C* p = NULL) : ptr_(p) { } + + // Destructor. If there is a C object, delete it. + // We don't need to test ptr_ == NULL because C++ does that for us. + ~scoped_ptr() { + enum { type_must_be_complete = sizeof(C) }; + delete ptr_; + } + + // Reset. Deletes the current owned object, if any. + // Then takes ownership of a new object, if given. + // this->reset(this->get()) works. + void reset(C* p = NULL) { + if (p != ptr_) { + enum { type_must_be_complete = sizeof(C) }; + delete ptr_; + ptr_ = p; + } + } + + // Accessors to get the owned object. + // operator* and operator-> will assert() if there is no current object. + C& operator*() const { + assert(ptr_ != NULL); + return *ptr_; + } + C* operator->() const { + assert(ptr_ != NULL); + return ptr_; + } + C* get() const { return ptr_; } + + // Comparison operators. + // These return whether two scoped_ptr refer to the same object, not just to + // two different but equal objects. + bool operator==(C* p) const { return ptr_ == p; } + bool operator!=(C* p) const { return ptr_ != p; } + + // Swap two scoped pointers. + void swap(scoped_ptr& p2) { + C* tmp = ptr_; + ptr_ = p2.ptr_; + p2.ptr_ = tmp; + } + + // Release a pointer. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + C* release() { + C* retVal = ptr_; + ptr_ = NULL; + return retVal; + } + + private: + C* ptr_; + + // Forbid comparison of scoped_ptr types. If C2 != C, it totally doesn't + // make sense, and if C2 == C, it still doesn't make sense because you should + // never have the same object owned by two different scoped_ptrs. + template bool operator==(scoped_ptr const& p2) const; + template bool operator!=(scoped_ptr const& p2) const; + + // Disallow evil constructors + scoped_ptr(const scoped_ptr&); + void operator=(const scoped_ptr&); +}; + +// Free functions +template +void swap(scoped_ptr& p1, scoped_ptr& p2) { + p1.swap(p2); +} + +template +bool operator==(C* p1, const scoped_ptr& p2) { + return p1 == p2.get(); +} + +template +bool operator!=(C* p1, const scoped_ptr& p2) { + return p1 != p2.get(); +} + +// scoped_array is like scoped_ptr, except that the caller must allocate +// with new [] and the destructor deletes objects with delete []. +// +// As with scoped_ptr, a scoped_array either points to an object +// or is NULL. A scoped_array owns the object that it points to. +// scoped_array is thread-compatible, and once you index into it, +// the returned objects have only the threadsafety guarantees of T. +// +// Size: sizeof(scoped_array) == sizeof(C*) +template +class scoped_array { + public: + + // The element type + typedef C element_type; + + // Constructor. Defaults to intializing with NULL. + // There is no way to create an uninitialized scoped_array. + // The input parameter must be allocated with new []. + explicit scoped_array(C* p = NULL) : array_(p) { } + + // Destructor. If there is a C object, delete it. + // We don't need to test ptr_ == NULL because C++ does that for us. + ~scoped_array() { + enum { type_must_be_complete = sizeof(C) }; + delete[] array_; + } + + // Reset. Deletes the current owned object, if any. + // Then takes ownership of a new object, if given. + // this->reset(this->get()) works. + void reset(C* p = NULL) { + if (p != array_) { + enum { type_must_be_complete = sizeof(C) }; + delete[] array_; + array_ = p; + } + } + + // Get one element of the current object. + // Will assert() if there is no current object, or index i is negative. + C& operator[](std::ptrdiff_t i) const { + assert(i >= 0); + assert(array_ != NULL); + return array_[i]; + } + + // Get a pointer to the zeroth element of the current object. + // If there is no current object, return NULL. + C* get() const { + return array_; + } + + // Comparison operators. + // These return whether two scoped_array refer to the same object, not just to + // two different but equal objects. + bool operator==(C* p) const { return array_ == p; } + bool operator!=(C* p) const { return array_ != p; } + + // Swap two scoped arrays. + void swap(scoped_array& p2) { + C* tmp = array_; + array_ = p2.array_; + p2.array_ = tmp; + } + + // Release an array. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + C* release() { + C* retVal = array_; + array_ = NULL; + return retVal; + } + + private: + C* array_; + + // Forbid comparison of different scoped_array types. + template bool operator==(scoped_array const& p2) const; + template bool operator!=(scoped_array const& p2) const; + + // Disallow evil constructors + scoped_array(const scoped_array&); + void operator=(const scoped_array&); +}; + +// Free functions +template +void swap(scoped_array& p1, scoped_array& p2) { + p1.swap(p2); +} + +template +bool operator==(C* p1, const scoped_array& p2) { + return p1 == p2.get(); +} + +template +bool operator!=(C* p1, const scoped_array& p2) { + return p1 != p2.get(); +} + +// This class wraps the c library function free() in a class that can be +// passed as a template argument to scoped_ptr_malloc below. +class ScopedPtrMallocFree { + public: + inline void operator()(void* x) const { + free(x); + } +}; + +// scoped_ptr_malloc<> is similar to scoped_ptr<>, but it accepts a +// second template argument, the functor used to free the object. + +template +class scoped_ptr_malloc { + public: + + // The element type + typedef C element_type; + + // Constructor. Defaults to intializing with NULL. + // There is no way to create an uninitialized scoped_ptr. + // The input parameter must be allocated with an allocator that matches the + // Free functor. For the default Free functor, this is malloc, calloc, or + // realloc. + explicit scoped_ptr_malloc(C* p = NULL): ptr_(p) {} + + // Destructor. If there is a C object, call the Free functor. + ~scoped_ptr_malloc() { + free_(ptr_); + } + + // Reset. Calls the Free functor on the current owned object, if any. + // Then takes ownership of a new object, if given. + // this->reset(this->get()) works. + void reset(C* p = NULL) { + if (ptr_ != p) { + free_(ptr_); + ptr_ = p; + } + } + + // Get the current object. + // operator* and operator-> will cause an assert() failure if there is + // no current object. + C& operator*() const { + assert(ptr_ != NULL); + return *ptr_; + } + + C* operator->() const { + assert(ptr_ != NULL); + return ptr_; + } + + C* get() const { + return ptr_; + } + + // Comparison operators. + // These return whether a scoped_ptr_malloc and a plain pointer refer + // to the same object, not just to two different but equal objects. + // For compatibility wwith the boost-derived implementation, these + // take non-const arguments. + bool operator==(C* p) const { + return ptr_ == p; + } + + bool operator!=(C* p) const { + return ptr_ != p; + } + + // Swap two scoped pointers. + void swap(scoped_ptr_malloc & b) { + C* tmp = b.ptr_; + b.ptr_ = ptr_; + ptr_ = tmp; + } + + // Release a pointer. + // The return value is the current pointer held by this object. + // If this object holds a NULL pointer, the return value is NULL. + // After this operation, this object will hold a NULL pointer, + // and will not own the object any more. + C* release() { + C* tmp = ptr_; + ptr_ = NULL; + return tmp; + } + + private: + C* ptr_; + + // no reason to use these: each scoped_ptr_malloc should have its own object + template + bool operator==(scoped_ptr_malloc const& p) const; + template + bool operator!=(scoped_ptr_malloc const& p) const; + + static FreeProc const free_; + + // Disallow evil constructors + scoped_ptr_malloc(const scoped_ptr_malloc&); + void operator=(const scoped_ptr_malloc&); +}; + +template +FP const scoped_ptr_malloc::free_ = FP(); + +template inline +void swap(scoped_ptr_malloc& a, scoped_ptr_malloc& b) { + a.swap(b); +} + +template inline +bool operator==(C* p, const scoped_ptr_malloc& b) { + return p == b.get(); +} + +template inline +bool operator!=(C* p, const scoped_ptr_malloc& b) { + return p != b.get(); +} + +#endif // BASE_SCOPED_PTR_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_ptr_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_ptr_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_ptr_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_ptr_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,169 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class ConDecLogger { + public: + ConDecLogger() : ptr_(NULL) { } + explicit ConDecLogger(int* ptr) { set_ptr(ptr); } + ~ConDecLogger() { --*ptr_; } + + void set_ptr(int* ptr) { ptr_ = ptr; ++*ptr_; } + + int SomeMeth(int x) { return x; } + + private: + int* ptr_; + DISALLOW_COPY_AND_ASSIGN(ConDecLogger); +}; + +} // namespace + +TEST(ScopedPtrTest, ScopedPtr) { + int constructed = 0; + + { + scoped_ptr scoper(new ConDecLogger(&constructed)); + EXPECT_EQ(1, constructed); + EXPECT_TRUE(scoper.get()); + + EXPECT_EQ(10, scoper->SomeMeth(10)); + EXPECT_EQ(10, scoper.get()->SomeMeth(10)); + EXPECT_EQ(10, (*scoper).SomeMeth(10)); + } + EXPECT_EQ(0, constructed); + + // Test reset() and release() + { + scoped_ptr scoper(new ConDecLogger(&constructed)); + EXPECT_EQ(1, constructed); + EXPECT_TRUE(scoper.get()); + + scoper.reset(new ConDecLogger(&constructed)); + EXPECT_EQ(1, constructed); + EXPECT_TRUE(scoper.get()); + + scoper.reset(); + EXPECT_EQ(0, constructed); + EXPECT_FALSE(scoper.get()); + + scoper.reset(new ConDecLogger(&constructed)); + EXPECT_EQ(1, constructed); + EXPECT_TRUE(scoper.get()); + + ConDecLogger* take = scoper.release(); + EXPECT_EQ(1, constructed); + EXPECT_FALSE(scoper.get()); + delete take; + EXPECT_EQ(0, constructed); + + scoper.reset(new ConDecLogger(&constructed)); + EXPECT_EQ(1, constructed); + EXPECT_TRUE(scoper.get()); + } + EXPECT_EQ(0, constructed); + + // Test swap(), == and != + { + scoped_ptr scoper1; + scoped_ptr scoper2; + EXPECT_TRUE(scoper1 == scoper2.get()); + EXPECT_FALSE(scoper1 != scoper2.get()); + + ConDecLogger* logger = new ConDecLogger(&constructed); + scoper1.reset(logger); + EXPECT_EQ(logger, scoper1.get()); + EXPECT_FALSE(scoper2.get()); + EXPECT_FALSE(scoper1 == scoper2.get()); + EXPECT_TRUE(scoper1 != scoper2.get()); + + scoper2.swap(scoper1); + EXPECT_EQ(logger, scoper2.get()); + EXPECT_FALSE(scoper1.get()); + EXPECT_FALSE(scoper1 == scoper2.get()); + EXPECT_TRUE(scoper1 != scoper2.get()); + } + EXPECT_EQ(0, constructed); +} + +TEST(ScopedPtrTest, ScopedArray) { + static const int kNumLoggers = 12; + + int constructed = 0; + + { + scoped_array scoper(new ConDecLogger[kNumLoggers]); + EXPECT_TRUE(scoper.get()); + EXPECT_EQ(&scoper[0], scoper.get()); + for (int i = 0; i < kNumLoggers; ++i) { + scoper[i].set_ptr(&constructed); + } + EXPECT_EQ(12, constructed); + + EXPECT_EQ(10, scoper.get()->SomeMeth(10)); + EXPECT_EQ(10, scoper[2].SomeMeth(10)); + } + EXPECT_EQ(0, constructed); + + // Test reset() and release() + { + scoped_array scoper; + EXPECT_FALSE(scoper.get()); + scoper.release(); + EXPECT_FALSE(scoper.get()); + scoper.reset(); + EXPECT_FALSE(scoper.get()); + + scoper.reset(new ConDecLogger[kNumLoggers]); + for (int i = 0; i < kNumLoggers; ++i) { + scoper[i].set_ptr(&constructed); + } + EXPECT_EQ(12, constructed); + scoper.reset(); + EXPECT_EQ(0, constructed); + + scoper.reset(new ConDecLogger[kNumLoggers]); + for (int i = 0; i < kNumLoggers; ++i) { + scoper[i].set_ptr(&constructed); + } + EXPECT_EQ(12, constructed); + ConDecLogger* ptr = scoper.release(); + EXPECT_EQ(12, constructed); + delete[] ptr; + EXPECT_EQ(0, constructed); + } + EXPECT_EQ(0, constructed); + + // Test swap(), == and != + { + scoped_array scoper1; + scoped_array scoper2; + EXPECT_TRUE(scoper1 == scoper2.get()); + EXPECT_FALSE(scoper1 != scoper2.get()); + + ConDecLogger* loggers = new ConDecLogger[kNumLoggers]; + for (int i = 0; i < kNumLoggers; ++i) { + loggers[i].set_ptr(&constructed); + } + scoper1.reset(loggers); + EXPECT_EQ(loggers, scoper1.get()); + EXPECT_FALSE(scoper2.get()); + EXPECT_FALSE(scoper1 == scoper2.get()); + EXPECT_TRUE(scoper1 != scoper2.get()); + + scoper2.swap(scoper1); + EXPECT_EQ(loggers, scoper2.get()); + EXPECT_FALSE(scoper1.get()); + EXPECT_FALSE(scoper1 == scoper2.get()); + EXPECT_TRUE(scoper1 != scoper2.get()); + } + EXPECT_EQ(0, constructed); +} + +// TODO scoped_ptr_malloc diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_temp_dir.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_temp_dir.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_temp_dir.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_temp_dir.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,47 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/scoped_temp_dir.h" + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/string_util.h" + +ScopedTempDir::ScopedTempDir() { +} + +ScopedTempDir::~ScopedTempDir() { + if (!path_.empty() && !file_util::Delete(path_, true)) + LOG(ERROR) << "ScopedTempDir unable to delete " << path_.value(); +} + +bool ScopedTempDir::CreateUniqueTempDir() { + // This "scoped_dir" prefix is only used on Windows and serves as a template + // for the unique name. + if (!file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("scoped_dir"), + &path_)) + return false; + + return true; +} + +bool ScopedTempDir::Set(const FilePath& path) { + DCHECK(path_.empty()); + if (!file_util::DirectoryExists(path) && + !file_util::CreateDirectory(path)) { + return false; + } + path_ = path; + return true; +} + +FilePath ScopedTempDir::Take() { + FilePath ret = path_; + path_ = FilePath(); + return ret; +} + +bool ScopedTempDir::IsValid() const { + return !path_.empty() && file_util::DirectoryExists(path_); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_temp_dir.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_temp_dir.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_temp_dir.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_temp_dir.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,47 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SCOPED_TEMP_DIR_H_ +#define BASE_SCOPED_TEMP_DIR_H_ + +// An object representing a temporary / scratch directory that should be cleaned +// up (recursively) when this object goes out of scope. Note that since +// deletion occurs during the destructor, no further error handling is possible +// if the directory fails to be deleted. As a result, deletion is not +// guaranteed by this class. + +#include "base/file_path.h" + +class ScopedTempDir { + public: + // No directory is owned/created initially. + ScopedTempDir(); + + // Recursively delete path_ + ~ScopedTempDir(); + + // Creates a unique directory in TempPath, and takes ownership of it. + // See file_util::CreateNewTemporaryDirectory. + bool CreateUniqueTempDir(); + + // Takes ownership of directory at |path|, creating it if necessary. + // Don't call multiple times unless Take() has been called first. + bool Set(const FilePath& path); + + // Caller takes ownership of the temporary directory so it won't be destroyed + // when this object goes out of scope. + FilePath Take(); + + const FilePath& path() const { return path_; } + + // Returns true if path_ is non-empty and exists. + bool IsValid() const; + + private: + FilePath path_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTempDir); +}; + +#endif // BASE_SCOPED_TEMP_DIR_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_temp_dir_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_temp_dir_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_temp_dir_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_temp_dir_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,57 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_util.h" +#include "base/scoped_temp_dir.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(ScopedTempDir, FullPath) { + FilePath test_path; + file_util::CreateNewTempDirectory(FILE_PATH_LITERAL("scoped_temp_dir"), + &test_path); + + // Against an existing dir, it should get destroyed when leaving scope. + EXPECT_TRUE(file_util::DirectoryExists(test_path)); + { + ScopedTempDir dir; + EXPECT_TRUE(dir.Set(test_path)); + EXPECT_TRUE(dir.IsValid()); + } + EXPECT_FALSE(file_util::DirectoryExists(test_path)); + + { + ScopedTempDir dir; + dir.Set(test_path); + // Now the dir doesn't exist, so ensure that it gets created. + EXPECT_TRUE(file_util::DirectoryExists(test_path)); + // When we call Release(), it shouldn't get destroyed when leaving scope. + FilePath path = dir.Take(); + EXPECT_EQ(path.value(), test_path.value()); + EXPECT_FALSE(dir.IsValid()); + } + EXPECT_TRUE(file_util::DirectoryExists(test_path)); + + // Clean up. + { + ScopedTempDir dir; + dir.Set(test_path); + } + EXPECT_FALSE(file_util::DirectoryExists(test_path)); +} + +TEST(ScopedTempDir, TempDir) { + // In this case, just verify that a directory was created and that it's a + // child of TempDir. + FilePath test_path; + { + ScopedTempDir dir; + EXPECT_TRUE(dir.CreateUniqueTempDir()); + test_path = dir.path(); + EXPECT_TRUE(file_util::DirectoryExists(test_path)); + FilePath tmp_dir; + EXPECT_TRUE(file_util::GetTempDir(&tmp_dir)); + EXPECT_TRUE(test_path.value().find(tmp_dir.value()) != std::string::npos); + } + EXPECT_FALSE(file_util::DirectoryExists(test_path)); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_variant_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_variant_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_variant_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_variant_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,259 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/scoped_variant_win.h" +#include "base/logging.h" + +// Global, const instance of an empty variant. +const VARIANT ScopedVariant::kEmptyVariant = { VT_EMPTY }; + +ScopedVariant::~ScopedVariant() { + COMPILE_ASSERT(sizeof(ScopedVariant) == sizeof(VARIANT), ScopedVariantSize); + ::VariantClear(&var_); +} + +ScopedVariant::ScopedVariant(const wchar_t* str) { + var_.vt = VT_EMPTY; + Set(str); +} + +ScopedVariant::ScopedVariant(const wchar_t* str, UINT length) { + var_.vt = VT_BSTR; + var_.bstrVal = ::SysAllocStringLen(str, length); +} + +ScopedVariant::ScopedVariant(int value, VARTYPE vt) { + var_.vt = vt; + var_.lVal = value; +} + +ScopedVariant::ScopedVariant(IDispatch* dispatch) { + var_.vt = VT_EMPTY; + Set(dispatch); +} + +ScopedVariant::ScopedVariant(IUnknown* unknown) { + var_.vt = VT_EMPTY; + Set(unknown); +} + +ScopedVariant::ScopedVariant(const VARIANT& var) { + var_.vt = VT_EMPTY; + Set(var); +} + +void ScopedVariant::Reset(const VARIANT& var) { + if (&var != &var_) { + ::VariantClear(&var_); + var_ = var; + } +} + +VARIANT ScopedVariant::Release() { + VARIANT var = var_; + var_.vt = VT_EMPTY; + return var; +} + +void ScopedVariant::Swap(ScopedVariant& var) { + VARIANT tmp = var_; + var_ = var.var_; + var.var_ = tmp; +} + +VARIANT* ScopedVariant::Receive() { + DCHECK(!IsLeakableVarType(var_.vt)) << "variant leak. type: " << var_.vt; + return &var_; +} + +VARIANT ScopedVariant::Copy() const { + VARIANT ret = { VT_EMPTY }; + ::VariantCopy(&ret, &var_); + return ret; +} + +int ScopedVariant::Compare(const VARIANT& var, bool ignore_case) const { + ULONG flags = ignore_case ? NORM_IGNORECASE : 0; + HRESULT hr = ::VarCmp(const_cast(&var_), const_cast(&var), + LOCALE_USER_DEFAULT, flags); + int ret = 0; + + switch (hr) { + case VARCMP_LT: + ret = -1; + break; + + case VARCMP_GT: + case VARCMP_NULL: + ret = 1; + break; + + default: + // Equal. + break; + } + + return ret; +} + +void ScopedVariant::Set(const wchar_t* str) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + var_.vt = VT_BSTR; + var_.bstrVal = ::SysAllocString(str); +} + +void ScopedVariant::Set(int8 i8) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + var_.vt = VT_I1; + var_.cVal = i8; +} + +void ScopedVariant::Set(uint8 ui8) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + var_.vt = VT_UI1; + var_.bVal = ui8; +} + +void ScopedVariant::Set(int16 i16) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + var_.vt = VT_I2; + var_.iVal = i16; +} + +void ScopedVariant::Set(uint16 ui16) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + var_.vt = VT_UI2; + var_.uiVal = ui16; +} + +void ScopedVariant::Set(int32 i32) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + var_.vt = VT_I4; + var_.lVal = i32; +} + +void ScopedVariant::Set(uint32 ui32) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + var_.vt = VT_UI4; + var_.ulVal = ui32; +} + +void ScopedVariant::Set(int64 i64) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + var_.vt = VT_I8; + var_.llVal = i64; +} + +void ScopedVariant::Set(uint64 ui64) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + var_.vt = VT_UI8; + var_.ullVal = ui64; +} + +void ScopedVariant::Set(float r32) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + var_.vt = VT_R4; + var_.fltVal = r32; +} + +void ScopedVariant::Set(double r64) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + var_.vt = VT_R8; + var_.dblVal = r64; +} + +void ScopedVariant::SetDate(DATE date) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + var_.vt = VT_DATE; + var_.date = date; +} + +void ScopedVariant::Set(IDispatch* disp) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + var_.vt = VT_DISPATCH; + var_.pdispVal = disp; + if (disp) + disp->AddRef(); +} + +void ScopedVariant::Set(bool b) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + var_.vt = VT_BOOL; + var_.boolVal = b ? VARIANT_TRUE : VARIANT_FALSE; +} + +void ScopedVariant::Set(IUnknown* unk) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + var_.vt = VT_UNKNOWN; + var_.punkVal = unk; + if (unk) + unk->AddRef(); +} + +void ScopedVariant::Set(SAFEARRAY* array) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + if (SUCCEEDED(::SafeArrayGetVartype(array, &var_.vt))) { + var_.vt |= VT_ARRAY; + var_.parray = array; + } else { + DCHECK(array == NULL) << "Unable to determine safearray vartype"; + var_.vt = VT_EMPTY; + } +} + +void ScopedVariant::Set(const VARIANT& var) { + DCHECK(!IsLeakableVarType(var_.vt)) << "leaking variant: " << var_.vt; + if (FAILED(::VariantCopy(&var_, &var))) { + DLOG(ERROR) << "VariantCopy failed"; + var_.vt = VT_EMPTY; + } +} + +ScopedVariant& ScopedVariant::operator=(const VARIANT& var) { + if (&var != &var_) { + VariantClear(&var_); + Set(var); + } + return *this; +} + +bool ScopedVariant::IsLeakableVarType(VARTYPE vt) { + bool leakable = false; + switch (vt & VT_TYPEMASK) { + case VT_BSTR: + case VT_DISPATCH: + // we treat VT_VARIANT as leakable to err on the safe side. + case VT_VARIANT: + case VT_UNKNOWN: + case VT_SAFEARRAY: + + // very rarely used stuff (if ever): + case VT_VOID: + case VT_PTR: + case VT_CARRAY: + case VT_USERDEFINED: + case VT_LPSTR: + case VT_LPWSTR: + case VT_RECORD: + case VT_INT_PTR: + case VT_UINT_PTR: + case VT_FILETIME: + case VT_BLOB: + case VT_STREAM: + case VT_STORAGE: + case VT_STREAMED_OBJECT: + case VT_STORED_OBJECT: + case VT_BLOB_OBJECT: + case VT_VERSIONED_STREAM: + case VT_BSTR_BLOB: + leakable = true; + break; + } + + if (!leakable && (vt & VT_ARRAY) != 0) { + leakable = true; + } + + return leakable; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_variant_win.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_variant_win.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_variant_win.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_variant_win.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,153 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SCOPED_VARIANT_WIN_H_ +#define BASE_SCOPED_VARIANT_WIN_H_ + +#include +#include + +#include "base/basictypes.h" // needed to pick up OS_WIN +#include "base/logging.h" + +// Scoped VARIANT class for automatically freeing a COM VARIANT at the +// end of a scope. Additionally provides a few functions to make the +// encapsulated VARIANT easier to use. +// Instead of inheriting from VARIANT, we take the containment approach +// in order to have more control over the usage of the variant and guard +// against memory leaks. +class ScopedVariant { + public: + // Declaration of a global variant variable that's always VT_EMPTY + static const VARIANT kEmptyVariant; + + // Default constructor. + ScopedVariant() { + // This is equivalent to what VariantInit does, but less code. + var_.vt = VT_EMPTY; + } + + // Constructor to create a new VT_BSTR VARIANT. + // NOTE: Do not pass a BSTR to this constructor expecting ownership to + // be transferred + explicit ScopedVariant(const wchar_t* str); + + // Creates a new VT_BSTR variant of a specified length. + explicit ScopedVariant(const wchar_t* str, UINT length); + + // Creates a new integral type variant and assigns the value to + // VARIANT.lVal (32 bit sized field). + explicit ScopedVariant(int value, VARTYPE vt = VT_I4); + + // VT_DISPATCH + explicit ScopedVariant(IDispatch* dispatch); + + // VT_UNKNOWN + explicit ScopedVariant(IUnknown* unknown); + + // Copies the variant. + explicit ScopedVariant(const VARIANT& var); + + ~ScopedVariant(); + + inline VARTYPE type() const { + return var_.vt; + } + + // Give ScopedVariant ownership over an already allocated VARIANT. + void Reset(const VARIANT& var = kEmptyVariant); + + // Releases ownership of the VARIANT to the caller. + VARIANT Release(); + + // Swap two ScopedVariant's. + void Swap(ScopedVariant& var); + + // Returns a copy of the variant. + VARIANT Copy() const; + + // The return value is 0 if the variants are equal, 1 if this object is + // greater than |var|, -1 if it is smaller. + int Compare(const VARIANT& var, bool ignore_case = false) const; + + // Retrieves the pointer address. + // Used to receive a VARIANT as an out argument (and take ownership). + // The function DCHECKs on the current value being empty/null. + // Usage: GetVariant(var.receive()); + VARIANT* Receive(); + + void Set(const wchar_t* str); + + // Setters for simple types. + void Set(int8 i8); + void Set(uint8 ui8); + void Set(int16 i16); + void Set(uint16 ui16); + void Set(int32 i32); + void Set(uint32 ui32); + void Set(int64 i64); + void Set(uint64 ui64); + void Set(float r32); + void Set(double r64); + void Set(bool b); + + // Creates a copy of |var| and assigns as this instance's value. + // Note that this is different from the Reset() method that's used to + // free the current value and assume ownership. + void Set(const VARIANT& var); + + // COM object setters + void Set(IDispatch* disp); + void Set(IUnknown* unk); + + // SAFEARRAY support + void Set(SAFEARRAY* array); + + // Special setter for DATE since DATE is a double and we already have + // a setter for double. + void SetDate(DATE date); + + // Allows const access to the contained variant without DCHECKs etc. + // This support is necessary for the V_XYZ (e.g. V_BSTR) set of macros to + // work properly but still doesn't allow modifications since we want control + // over that. + const VARIANT* operator&() const { + return &var_; + } + + // Like other scoped classes (e.g scoped_refptr, ScopedComPtr, ScopedBstr) + // we support the assignment operator for the type we wrap. + ScopedVariant& operator=(const VARIANT& var); + + // A hack to pass a pointer to the variant where the accepting + // function treats the variant as an input-only, read-only value + // but the function prototype requires a non const variant pointer. + // There's no DCHECK or anything here. Callers must know what they're doing. + VARIANT* AsInput() const { + // The nature of this function is const, so we declare + // it as such and cast away the constness here. + return const_cast(&var_); + } + + // Allows the ScopedVariant instance to be passed to functions either by value + // or by const reference. + operator const VARIANT&() const { + return var_; + } + + // Used as a debug check to see if we're leaking anything. + static bool IsLeakableVarType(VARTYPE vt); + + protected: + VARIANT var_; + + private: + // Comparison operators for ScopedVariant are not supported at this point. + // Use the Compare method instead. + bool operator==(const ScopedVariant& var) const; + bool operator!=(const ScopedVariant& var) const; + DISALLOW_COPY_AND_ASSIGN(ScopedVariant); +}; + +#endif // BASE_SCOPED_VARIANT_WIN_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_variant_win_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_variant_win_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_variant_win_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_variant_win_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,257 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/scoped_variant_win.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +static const wchar_t kTestString1[] = L"Used to create BSTRs"; +static const wchar_t kTestString2[] = L"Also used to create BSTRs"; + +void GiveMeAVariant(VARIANT* ret) { + EXPECT_TRUE(ret != NULL); + ret->vt = VT_BSTR; + V_BSTR(ret) = ::SysAllocString(kTestString1); +} + +// A dummy IDispatch implementation (if you can call it that). +// The class does nothing intelligent really. Only increments a counter +// when AddRef is called and decrements it when Release is called. +class FakeComObject : public IDispatch { + public: + FakeComObject() : ref_(0) { + } + + STDMETHOD_(DWORD, AddRef)() { + ref_++; + return ref_; + } + + STDMETHOD_(DWORD, Release)() { + ref_--; + return ref_; + } + + STDMETHOD(QueryInterface)(REFIID, void**) { + return E_NOTIMPL; + } + + STDMETHOD(GetTypeInfoCount)(UINT*) { + return E_NOTIMPL; + } + + STDMETHOD(GetTypeInfo)(UINT, LCID, ITypeInfo**) { + return E_NOTIMPL; + } + + STDMETHOD(GetIDsOfNames)(REFIID, LPOLESTR*, UINT, LCID, DISPID*) { + return E_NOTIMPL; + } + + STDMETHOD(Invoke)(DISPID, REFIID, LCID, WORD, DISPPARAMS*, VARIANT*, + EXCEPINFO*, UINT*) { + return E_NOTIMPL; + } + + // A way to check the internal reference count of the class. + int ref_count() const { + return ref_; + } + + protected: + int ref_; +}; + +} // namespace + +TEST(ScopedVariantTest, ScopedVariant) { + ScopedVariant var; + EXPECT_TRUE(var.type() == VT_EMPTY); + // V_BSTR(&var) = NULL; <- NOTE: Assignment like that is not supported + + ScopedVariant var_bstr(L"VT_BSTR"); + EXPECT_EQ(VT_BSTR, V_VT(&var_bstr)); + EXPECT_TRUE(V_BSTR(&var_bstr) != NULL); // can't use EXPECT_NE for BSTR + var_bstr.Reset(); + EXPECT_NE(VT_BSTR, V_VT(&var_bstr)); + var_bstr.Set(kTestString2); + EXPECT_EQ(VT_BSTR, V_VT(&var_bstr)); + + VARIANT tmp = var_bstr.Release(); + EXPECT_EQ(VT_EMPTY, V_VT(&var_bstr)); + EXPECT_EQ(VT_BSTR, V_VT(&tmp)); + EXPECT_EQ(0, lstrcmp(V_BSTR(&tmp), kTestString2)); + + var.Reset(tmp); + EXPECT_EQ(VT_BSTR, V_VT(&var)); + EXPECT_EQ(0, lstrcmpW(V_BSTR(&var), kTestString2)); + + var_bstr.Swap(var); + EXPECT_EQ(VT_EMPTY, V_VT(&var)); + EXPECT_EQ(VT_BSTR, V_VT(&var_bstr)); + EXPECT_EQ(0, lstrcmpW(V_BSTR(&var_bstr), kTestString2)); + var_bstr.Reset(); + + // Test the Compare and Copy routines. + GiveMeAVariant(var_bstr.Receive()); + ScopedVariant var_bstr2(V_BSTR(&var_bstr)); + EXPECT_EQ(0, var_bstr.Compare(var_bstr2)); + var_bstr2.Reset(); + EXPECT_NE(0, var_bstr.Compare(var_bstr2)); + var_bstr2.Reset(var_bstr.Copy()); + EXPECT_EQ(0, var_bstr.Compare(var_bstr2)); + var_bstr2.Reset(); + var_bstr2.Set(V_BSTR(&var_bstr)); + EXPECT_EQ(0, var_bstr.Compare(var_bstr2)); + var_bstr2.Reset(); + var_bstr.Reset(); + + // Test for the SetDate setter. + SYSTEMTIME sys_time; + ::GetSystemTime(&sys_time); + DATE date; + ::SystemTimeToVariantTime(&sys_time, &date); + var.Reset(); + var.SetDate(date); + EXPECT_EQ(VT_DATE, var.type()); + EXPECT_EQ(date, V_DATE(&var)); + + // Simple setter tests. These do not require resetting the variant + // after each test since the variant type is not "leakable" (i.e. doesn't + // need to be freed explicitly). + + // We need static cast here since char defaults to int (!?). + var.Set(static_cast('v')); + EXPECT_EQ(VT_I1, var.type()); + EXPECT_EQ('v', V_I1(&var)); + + var.Set(static_cast(123)); + EXPECT_EQ(VT_I2, var.type()); + EXPECT_EQ(123, V_I2(&var)); + + var.Set(static_cast(123)); + EXPECT_EQ(VT_I4, var.type()); + EXPECT_EQ(123, V_I4(&var)); + + var.Set(static_cast(123)); + EXPECT_EQ(VT_I8, var.type()); + EXPECT_EQ(123, V_I8(&var)); + + var.Set(static_cast(123)); + EXPECT_EQ(VT_UI1, var.type()); + EXPECT_EQ(123, V_UI1(&var)); + + var.Set(static_cast(123)); + EXPECT_EQ(VT_UI2, var.type()); + EXPECT_EQ(123, V_UI2(&var)); + + var.Set(static_cast(123)); + EXPECT_EQ(VT_UI4, var.type()); + EXPECT_EQ(123, V_UI4(&var)); + + var.Set(static_cast(123)); + EXPECT_EQ(VT_UI8, var.type()); + EXPECT_EQ(123, V_UI8(&var)); + + var.Set(123.123f); + EXPECT_EQ(VT_R4, var.type()); + EXPECT_EQ(123.123f, V_R4(&var)); + + var.Set(static_cast(123.123)); + EXPECT_EQ(VT_R8, var.type()); + EXPECT_EQ(123.123, V_R8(&var)); + + var.Set(true); + EXPECT_EQ(VT_BOOL, var.type()); + EXPECT_EQ(VARIANT_TRUE, V_BOOL(&var)); + var.Set(false); + EXPECT_EQ(VT_BOOL, var.type()); + EXPECT_EQ(VARIANT_FALSE, V_BOOL(&var)); + + // Com interface tests + + var.Set(static_cast(NULL)); + EXPECT_EQ(VT_DISPATCH, var.type()); + EXPECT_EQ(NULL, V_DISPATCH(&var)); + var.Reset(); + + var.Set(static_cast(NULL)); + EXPECT_EQ(VT_UNKNOWN, var.type()); + EXPECT_EQ(NULL, V_UNKNOWN(&var)); + var.Reset(); + + FakeComObject faker; + EXPECT_EQ(0, faker.ref_count()); + var.Set(static_cast(&faker)); + EXPECT_EQ(VT_DISPATCH, var.type()); + EXPECT_EQ(&faker, V_DISPATCH(&var)); + EXPECT_EQ(1, faker.ref_count()); + var.Reset(); + EXPECT_EQ(0, faker.ref_count()); + + var.Set(static_cast(&faker)); + EXPECT_EQ(VT_UNKNOWN, var.type()); + EXPECT_EQ(&faker, V_UNKNOWN(&var)); + EXPECT_EQ(1, faker.ref_count()); + var.Reset(); + EXPECT_EQ(0, faker.ref_count()); + + { + ScopedVariant disp_var(&faker); + EXPECT_EQ(VT_DISPATCH, disp_var.type()); + EXPECT_EQ(&faker, V_DISPATCH(&disp_var)); + EXPECT_EQ(1, faker.ref_count()); + } + EXPECT_EQ(0, faker.ref_count()); + + { + ScopedVariant ref1(&faker); + EXPECT_EQ(1, faker.ref_count()); + ScopedVariant ref2(static_cast(ref1)); + EXPECT_EQ(2, faker.ref_count()); + ScopedVariant ref3; + ref3 = static_cast(ref2); + EXPECT_EQ(3, faker.ref_count()); + } + EXPECT_EQ(0, faker.ref_count()); + + { + ScopedVariant unk_var(static_cast(&faker)); + EXPECT_EQ(VT_UNKNOWN, unk_var.type()); + EXPECT_EQ(&faker, V_UNKNOWN(&unk_var)); + EXPECT_EQ(1, faker.ref_count()); + } + EXPECT_EQ(0, faker.ref_count()); + + VARIANT raw; + raw.vt = VT_UNKNOWN; + raw.punkVal = &faker; + EXPECT_EQ(0, faker.ref_count()); + var.Set(raw); + EXPECT_EQ(1, faker.ref_count()); + var.Reset(); + EXPECT_EQ(0, faker.ref_count()); + + { + ScopedVariant number(123); + EXPECT_EQ(VT_I4, number.type()); + EXPECT_EQ(123, V_I4(&number)); + } + + // SAFEARRAY tests + var.Set(static_cast(NULL)); + EXPECT_EQ(VT_EMPTY, var.type()); + + SAFEARRAY* sa = ::SafeArrayCreateVector(VT_UI1, 0, 100); + ASSERT_TRUE(sa != NULL); + + var.Set(sa); +#ifndef OFFICIAL_BUILD + EXPECT_TRUE(ScopedVariant::IsLeakableVarType(var.type())); +#endif + EXPECT_EQ(VT_ARRAY | VT_UI1, var.type()); + EXPECT_EQ(sa, V_ARRAY(&var)); + // The array is destroyed in the destructor of var. +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_vector.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_vector.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/scoped_vector.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/scoped_vector.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,52 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SCOPED_VECTOR_H_ +#define BASE_SCOPED_VECTOR_H_ + +#include + +#include "base/logging.h" +#include "base/stl_util-inl.h" + +// ScopedVector wraps a vector deleting the elements from its +// destructor. +template +class ScopedVector { + public: + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; + + ScopedVector() {} + ~ScopedVector() { reset(); } + + std::vector* operator->() { return &v; } + const std::vector* operator->() const { return &v; } + T* operator[](size_t i) { return v[i]; } + const T* operator[](size_t i) const { return v[i]; } + + bool empty() const { return v.empty(); } + size_t size() const { return v.size(); } + + iterator begin() { return v.begin(); } + const_iterator begin() const { return v.begin(); } + iterator end() { return v.end(); } + const_iterator end() const { return v.end(); } + + void push_back(T* elem) { v.push_back(elem); } + + std::vector& get() { return v; } + const std::vector& get() const { return v; } + void swap(ScopedVector& other) { v.swap(other.v); } + void release(std::vector* out) { out->swap(v); v.clear(); } + + void reset() { STLDeleteElements(&v); } + + private: + std::vector v; + + DISALLOW_COPY_AND_ASSIGN(ScopedVector); +}; + +#endif // BASE_SCOPED_VECTOR_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sha2.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sha2.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sha2.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sha2.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,22 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/sha2.h" + +#include "base/third_party/nss/blapi.h" +#include "base/third_party/nss/sha256.h" + +namespace base { + +void SHA256HashString(const std::string& str, void* output, size_t len) { + SHA256Context ctx; + + SHA256_Begin(&ctx); + SHA256_Update(&ctx, reinterpret_cast(str.data()), + static_cast(str.length())); + SHA256_End(&ctx, static_cast(output), NULL, + static_cast(len)); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sha2.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sha2.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sha2.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sha2.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,27 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SHA2_H__ +#define BASE_SHA2_H__ + +#include + +namespace base { + +// These functions perform SHA-256 operations. +// +// Functions for SHA-384 and SHA-512 can be added when the need arises. + +enum { + SHA256_LENGTH = 32 // length in bytes of a SHA-256 hash +}; + +// Computes the SHA-256 hash of the input string 'str' and stores the first +// 'len' bytes of the hash in the output buffer 'output'. If 'len' > 32, +// only 32 bytes (the full hash) are stored in the 'output' buffer. +void SHA256HashString(const std::string& str, void* output, size_t len); + +} // namespace base + +#endif // BASE_SHA2_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sha2_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sha2_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sha2_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sha2_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,78 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/sha2.h" + +#include "base/basictypes.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(Sha256Test, Test1) { + // Example B.1 from FIPS 180-2: one-block message. + std::string input1 = "abc"; + int expected1[] = { 0xba, 0x78, 0x16, 0xbf, + 0x8f, 0x01, 0xcf, 0xea, + 0x41, 0x41, 0x40, 0xde, + 0x5d, 0xae, 0x22, 0x23, + 0xb0, 0x03, 0x61, 0xa3, + 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, + 0xf2, 0x00, 0x15, 0xad }; + + uint8 output1[base::SHA256_LENGTH]; + base::SHA256HashString(input1, output1, sizeof(output1)); + for (size_t i = 0; i < base::SHA256_LENGTH; i++) + EXPECT_EQ(expected1[i], static_cast(output1[i])); + + uint8 output_truncated1[4]; // 4 bytes == 32 bits + base::SHA256HashString(input1, output_truncated1, sizeof(output_truncated1)); + for (size_t i = 0; i < sizeof(output_truncated1); i++) + EXPECT_EQ(expected1[i], static_cast(output_truncated1[i])); +} + +TEST(Sha256Test, Test2) { + // Example B.2 from FIPS 180-2: multi-block message. + std::string input2 = + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"; + int expected2[] = { 0x24, 0x8d, 0x6a, 0x61, + 0xd2, 0x06, 0x38, 0xb8, + 0xe5, 0xc0, 0x26, 0x93, + 0x0c, 0x3e, 0x60, 0x39, + 0xa3, 0x3c, 0xe4, 0x59, + 0x64, 0xff, 0x21, 0x67, + 0xf6, 0xec, 0xed, 0xd4, + 0x19, 0xdb, 0x06, 0xc1 }; + + uint8 output2[base::SHA256_LENGTH]; + base::SHA256HashString(input2, output2, sizeof(output2)); + for (size_t i = 0; i < base::SHA256_LENGTH; i++) + EXPECT_EQ(expected2[i], static_cast(output2[i])); + + uint8 output_truncated2[6]; + base::SHA256HashString(input2, output_truncated2, sizeof(output_truncated2)); + for (size_t i = 0; i < sizeof(output_truncated2); i++) + EXPECT_EQ(expected2[i], static_cast(output_truncated2[i])); +} + +TEST(Sha256Test, Test3) { + // Example B.3 from FIPS 180-2: long message. + std::string input3(1000000, 'a'); // 'a' repeated a million times + int expected3[] = { 0xcd, 0xc7, 0x6e, 0x5c, + 0x99, 0x14, 0xfb, 0x92, + 0x81, 0xa1, 0xc7, 0xe2, + 0x84, 0xd7, 0x3e, 0x67, + 0xf1, 0x80, 0x9a, 0x48, + 0xa4, 0x97, 0x20, 0x0e, + 0x04, 0x6d, 0x39, 0xcc, + 0xc7, 0x11, 0x2c, 0xd0 }; + + uint8 output3[base::SHA256_LENGTH]; + base::SHA256HashString(input3, output3, sizeof(output3)); + for (size_t i = 0; i < base::SHA256_LENGTH; i++) + EXPECT_EQ(expected3[i], static_cast(output3[i])); + + uint8 output_truncated3[12]; + base::SHA256HashString(input3, output_truncated3, sizeof(output_truncated3)); + for (size_t i = 0; i < sizeof(output_truncated3); i++) + EXPECT_EQ(expected3[i], static_cast(output_truncated3[i])); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/shared_memory.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/shared_memory.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/shared_memory.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/shared_memory.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,210 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SHARED_MEMORY_H_ +#define BASE_SHARED_MEMORY_H_ + +#include "build/build_config.h" + +#if defined(OS_POSIX) +#include +#include +#include "base/file_descriptor_posix.h" +#endif +#include + +#include "base/basictypes.h" +#include "base/process.h" + +namespace base { + +// SharedMemoryHandle is a platform specific type which represents +// the underlying OS handle to a shared memory segment. +#if defined(OS_WIN) +typedef HANDLE SharedMemoryHandle; +typedef HANDLE SharedMemoryLock; +#elif defined(OS_POSIX) +// A SharedMemoryId is sufficient to identify a given shared memory segment on a +// system, but insufficient to map it. +typedef FileDescriptor SharedMemoryHandle; +typedef ino_t SharedMemoryId; +// On POSIX, the lock is implemented as a lockf() on the mapped file, +// so no additional member (or definition of SharedMemoryLock) is +// needed. +#endif + +// Platform abstraction for shared memory. Provides a C++ wrapper +// around the OS primitive for a memory mapped file. +class SharedMemory { + public: + // Create a new SharedMemory object. + SharedMemory(); + + // Create a new SharedMemory object from an existing, open + // shared memory file. + SharedMemory(SharedMemoryHandle handle, bool read_only); + + // Create a new SharedMemory object from an existing, open + // shared memory file that was created by a remote process and not shared + // to the current process. + SharedMemory(SharedMemoryHandle handle, bool read_only, + base::ProcessHandle process); + + // Destructor. Will close any open files. + ~SharedMemory(); + + // Return true iff the given handle is valid (i.e. not the distingished + // invalid value; NULL for a HANDLE and -1 for a file descriptor) + static bool IsHandleValid(const SharedMemoryHandle& handle); + + // Return invalid handle (see comment above for exact definition). + static SharedMemoryHandle NULLHandle(); + + // Creates or opens a shared memory segment based on a name. + // If read_only is true, opens the memory as read-only. + // If open_existing is true, and the shared memory already exists, + // opens the existing shared memory and ignores the size parameter. + // If name is the empty string, use a unique name. + // Returns true on success, false on failure. +#ifdef CHROMIUM_MOZILLA_BUILD + bool Create(const std::string& name, bool read_only, bool open_existing, +#else + bool Create(const std::wstring& name, bool read_only, bool open_existing, +#endif + size_t size); + + // Deletes resources associated with a shared memory segment based on name. + // Not all platforms require this call. + bool Delete(const std::wstring& name); + + // Opens a shared memory segment based on a name. + // If read_only is true, opens for read-only access. + // If name is the empty string, use a unique name. + // Returns true on success, false on failure. + bool Open(const std::wstring& name, bool read_only); + + // Maps the shared memory into the caller's address space. + // Returns true on success, false otherwise. The memory address + // is accessed via the memory() accessor. + bool Map(size_t bytes); + + // Unmaps the shared memory from the caller's address space. + // Returns true if successful; returns false on error or if the + // memory is not mapped. + bool Unmap(); + + // Get the size of the opened shared memory backing file. + // Note: This size is only available to the creator of the + // shared memory, and not to those that opened shared memory + // created externally. + // Returns 0 if not opened or unknown. + size_t max_size() const { return max_size_; } + + // Gets a pointer to the opened memory space if it has been + // Mapped via Map(). Returns NULL if it is not mapped. + void *memory() const { return memory_; } + + // Get access to the underlying OS handle for this segment. + // Use of this handle for anything other than an opaque + // identifier is not portable. + SharedMemoryHandle handle() const; + +#if defined(OS_POSIX) + // Return a unique identifier for this shared memory segment. Inode numbers + // are technically only unique to a single filesystem. However, we always + // allocate shared memory backing files from the same directory, so will end + // up on the same filesystem. + SharedMemoryId id() const { return inode_; } +#endif + + // Closes the open shared memory segment. + // It is safe to call Close repeatedly. + void Close(); + + // Share the shared memory to another process. Attempts + // to create a platform-specific new_handle which can be + // used in a remote process to access the shared memory + // file. new_handle is an ouput parameter to receive + // the handle for use in the remote process. + // Returns true on success, false otherwise. + bool ShareToProcess(base::ProcessHandle process, + SharedMemoryHandle* new_handle) { + return ShareToProcessCommon(process, new_handle, false); + } + + // Logically equivalent to: + // bool ok = ShareToProcess(process, new_handle); + // Close(); + // return ok; + // Note that the memory is unmapped by calling this method, regardless of the + // return value. + bool GiveToProcess(ProcessHandle process, + SharedMemoryHandle* new_handle) { + return ShareToProcessCommon(process, new_handle, true); + } + + // Lock the shared memory. + // This is a cross-process lock which may be recursively + // locked by the same thread. + // TODO(port): + // WARNING: on POSIX the lock only works across processes, not + // across threads. 2 threads in the same process can both grab the + // lock at the same time. There are several solutions for this + // (futex, lockf+anon_semaphore) but none are both clean and common + // across Mac and Linux. + void Lock(); + + // Release the shared memory lock. + void Unlock(); + + private: +#if defined(OS_POSIX) + bool CreateOrOpen(const std::wstring &name, int posix_flags, size_t size); + bool FilenameForMemoryName(const std::wstring &memname, + std::wstring *filename); + void LockOrUnlockCommon(int function); + +#endif + bool ShareToProcessCommon(ProcessHandle process, + SharedMemoryHandle* new_handle, + bool close_self); + +#if defined(OS_WIN) + std::wstring name_; + HANDLE mapped_file_; +#elif defined(OS_POSIX) + int mapped_file_; + ino_t inode_; +#endif + void* memory_; + bool read_only_; + size_t max_size_; +#if !defined(OS_POSIX) + SharedMemoryLock lock_; +#endif + + DISALLOW_EVIL_CONSTRUCTORS(SharedMemory); +}; + +// A helper class that acquires the shared memory lock while +// the SharedMemoryAutoLock is in scope. +class SharedMemoryAutoLock { + public: + explicit SharedMemoryAutoLock(SharedMemory* shared_memory) + : shared_memory_(shared_memory) { + shared_memory_->Lock(); + } + + ~SharedMemoryAutoLock() { + shared_memory_->Unlock(); + } + + private: + SharedMemory* shared_memory_; + DISALLOW_EVIL_CONSTRUCTORS(SharedMemoryAutoLock); +}; + +} // namespace base + +#endif // BASE_SHARED_MEMORY_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/shared_memory_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/shared_memory_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/shared_memory_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/shared_memory_posix.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,320 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/shared_memory.h" + +#include +#include +#include +#include +#include + +#include "base/file_util.h" +#include "base/logging.h" +#include "base/platform_thread.h" +#include "base/string_util.h" + +namespace base { + +namespace { +// Paranoia. Semaphores and shared memory segments should live in different +// namespaces, but who knows what's out there. +const char kSemaphoreSuffix[] = "-sem"; +} + +SharedMemory::SharedMemory() + : mapped_file_(-1), + inode_(0), + memory_(NULL), + read_only_(false), + max_size_(0) { +} + +SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only) + : mapped_file_(handle.fd), + inode_(0), + memory_(NULL), + read_only_(read_only), + max_size_(0) { + struct stat st; + if (fstat(handle.fd, &st) == 0) { + // If fstat fails, then the file descriptor is invalid and we'll learn this + // fact when Map() fails. + inode_ = st.st_ino; + } +} + +SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only, + ProcessHandle process) + : mapped_file_(handle.fd), + memory_(NULL), + read_only_(read_only), + max_size_(0) { + // We don't handle this case yet (note the ignored parameter); let's die if + // someone comes calling. + NOTREACHED(); +} + +SharedMemory::~SharedMemory() { + Close(); +} + +// static +bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { + return handle.fd >= 0; +} + +// static +SharedMemoryHandle SharedMemory::NULLHandle() { + return SharedMemoryHandle(); +} + +#ifdef CHROMIUM_MOZILLA_BUILD +bool SharedMemory::Create(const std::string &cname, bool read_only, +#else +bool SharedMemory::Create(const std::wstring &name, bool read_only, +#endif + bool open_existing, size_t size) { + read_only_ = read_only; + +#ifdef CHROMIUM_MOZILLA_BUILD + std::wstring name = UTF8ToWide(cname); +#endif + + int posix_flags = 0; + posix_flags |= read_only ? O_RDONLY : O_RDWR; + if (!open_existing || mapped_file_ <= 0) + posix_flags |= O_CREAT; + + if (!CreateOrOpen(name, posix_flags, size)) + return false; + + max_size_ = size; + return true; +} + +// Our current implementation of shmem is with mmap()ing of files. +// These files need to be deleted explicitly. +// In practice this call is only needed for unit tests. +bool SharedMemory::Delete(const std::wstring& name) { + std::wstring mem_filename; + if (FilenameForMemoryName(name, &mem_filename) == false) + return false; + + FilePath path(WideToUTF8(mem_filename)); + if (file_util::PathExists(path)) { + return file_util::Delete(path, false); + } + + // Doesn't exist, so success. + return true; +} + +bool SharedMemory::Open(const std::wstring &name, bool read_only) { + read_only_ = read_only; + + int posix_flags = 0; + posix_flags |= read_only ? O_RDONLY : O_RDWR; + + return CreateOrOpen(name, posix_flags, 0); +} + +// For the given shmem named |memname|, return a filename to mmap() +// (and possibly create). Modifies |filename|. Return false on +// error, or true of we are happy. +bool SharedMemory::FilenameForMemoryName(const std::wstring &memname, + std::wstring *filename) { + std::wstring mem_filename; + + // mem_name will be used for a filename; make sure it doesn't + // contain anything which will confuse us. + DCHECK(memname.find_first_of(L"/") == std::string::npos); + DCHECK(memname.find_first_of(L"\0") == std::string::npos); + + FilePath temp_dir; + if (file_util::GetShmemTempDir(&temp_dir) == false) + return false; + + mem_filename = UTF8ToWide(temp_dir.value()); + file_util::AppendToPath(&mem_filename, L"com.google.chrome.shmem." + memname); + *filename = mem_filename; + return true; +} + +// Chromium mostly only use the unique/private shmem as specified by +// "name == L"". The exception is in the StatsTable. +// TODO(jrg): there is no way to "clean up" all unused named shmem if +// we restart from a crash. (That isn't a new problem, but it is a problem.) +// In case we want to delete it later, it may be useful to save the value +// of mem_filename after FilenameForMemoryName(). +bool SharedMemory::CreateOrOpen(const std::wstring &name, + int posix_flags, size_t size) { + DCHECK(mapped_file_ == -1); + + file_util::ScopedFILE file_closer; + FILE *fp; + + if (name == L"") { + // It doesn't make sense to have a read-only private piece of shmem + DCHECK(posix_flags & (O_RDWR | O_WRONLY)); + + FilePath path; + fp = file_util::CreateAndOpenTemporaryShmemFile(&path); + + // Deleting the file prevents anyone else from mapping it in + // (making it private), and prevents the need for cleanup (once + // the last fd is closed, it is truly freed). + file_util::Delete(path, false); + } else { + std::wstring mem_filename; + if (FilenameForMemoryName(name, &mem_filename) == false) + return false; + + std::string mode; + switch (posix_flags) { + case (O_RDWR | O_CREAT): + // Careful: "w+" will truncate if it already exists. + mode = "a+"; + break; + case O_RDWR: + mode = "r+"; + break; + case O_RDONLY: + mode = "r"; + break; + default: + NOTIMPLEMENTED(); + break; + } + + fp = file_util::OpenFile(mem_filename, mode.c_str()); + } + + if (fp == NULL) + return false; + file_closer.reset(fp); // close when we go out of scope + + // Make sure the (new) file is the right size. + // According to the man page, "Use of truncate() to extend a file is + // not portable." + if (size && (posix_flags & (O_RDWR | O_CREAT))) { + // Get current size. + size_t current_size = 0; + struct stat stat; + if (fstat(fileno(fp), &stat) != 0) + return false; + current_size = stat.st_size; + // Possibly grow. + if (current_size < size) { + if (fseeko(fp, current_size, SEEK_SET) != 0) + return false; + size_t writesize = size - current_size; + scoped_array buf(new char[writesize]); + memset(buf.get(), 0, writesize); + if (fwrite(buf.get(), 1, writesize, fp) != writesize) { + return false; + } + if (fflush(fp) != 0) + return false; + } else if (current_size > size) { + // possibly shrink. + if ((ftruncate(fileno(fp), size) != 0) || + (fflush(fp) != 0)) { + return false; + } + } + } + + mapped_file_ = dup(fileno(fp)); + DCHECK(mapped_file_ >= 0); + + struct stat st; + if (fstat(mapped_file_, &st)) + NOTREACHED(); + inode_ = st.st_ino; + + return true; +} + +bool SharedMemory::Map(size_t bytes) { + if (mapped_file_ == -1) + return false; + + memory_ = mmap(NULL, bytes, PROT_READ | (read_only_ ? 0 : PROT_WRITE), + MAP_SHARED, mapped_file_, 0); + + if (memory_) + max_size_ = bytes; + + bool mmap_succeeded = (memory_ != (void*)-1); + DCHECK(mmap_succeeded) << "Call to mmap failed, errno=" << errno; + return mmap_succeeded; +} + +bool SharedMemory::Unmap() { + if (memory_ == NULL) + return false; + + munmap(memory_, max_size_); + memory_ = NULL; + max_size_ = 0; + return true; +} + +bool SharedMemory::ShareToProcessCommon(ProcessHandle process, + SharedMemoryHandle *new_handle, + bool close_self) { + const int new_fd = dup(mapped_file_); + DCHECK(new_fd >= -1); + new_handle->fd = new_fd; + new_handle->auto_close = true; + + if (close_self) + Close(); + + return true; +} + + +void SharedMemory::Close() { + Unmap(); + + if (mapped_file_ > 0) { + close(mapped_file_); + mapped_file_ = -1; + } +} + +void SharedMemory::LockOrUnlockCommon(int function) { + DCHECK(mapped_file_ >= 0); + while (lockf(mapped_file_, function, 0) < 0) { + if (errno == EINTR) { + continue; + } else if (errno == ENOLCK) { + // temporary kernel resource exaustion + PlatformThread::Sleep(500); + continue; + } else { + NOTREACHED() << "lockf() failed." + << " function:" << function + << " fd:" << mapped_file_ + << " errno:" << errno + << " msg:" << strerror(errno); + } + } +} + +void SharedMemory::Lock() { + LockOrUnlockCommon(F_LOCK); +} + +void SharedMemory::Unlock() { + LockOrUnlockCommon(F_ULOCK); +} + +SharedMemoryHandle SharedMemory::handle() const { + return FileDescriptor(mapped_file_, false); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/shared_memory_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/shared_memory_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/shared_memory_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/shared_memory_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,346 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "base/multiprocess_test.h" +#include "base/platform_thread.h" +#include "base/scoped_nsautorelease_pool.h" +#include "base/shared_memory.h" +#include "base/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +static const int kNumThreads = 5; +static const int kNumTasks = 5; + +namespace base { + +namespace { + +// Each thread will open the shared memory. Each thread will take a different 4 +// byte int pointer, and keep changing it, with some small pauses in between. +// Verify that each thread's value in the shared memory is always correct. +class MultipleThreadMain : public PlatformThread::Delegate { + public: + explicit MultipleThreadMain(int16 id) : id_(id) {} + ~MultipleThreadMain() {} + + static void CleanUp() { + SharedMemory memory; + memory.Delete(s_test_name_); + } + + // PlatformThread::Delegate interface. + void ThreadMain() { + ScopedNSAutoreleasePool pool; // noop if not OSX + const int kDataSize = 1024; + SharedMemory memory; + bool rv = memory.Create(s_test_name_, false, true, kDataSize); + EXPECT_TRUE(rv); + rv = memory.Map(kDataSize); + EXPECT_TRUE(rv); + int *ptr = static_cast(memory.memory()) + id_; + EXPECT_EQ(*ptr, 0); + + for (int idx = 0; idx < 100; idx++) { + *ptr = idx; + PlatformThread::Sleep(1); // Short wait. + EXPECT_EQ(*ptr, idx); + } + + memory.Close(); + } + + private: + int16 id_; + + static const wchar_t* const s_test_name_; + + DISALLOW_COPY_AND_ASSIGN(MultipleThreadMain); +}; + +const wchar_t* const MultipleThreadMain::s_test_name_ = + L"SharedMemoryOpenThreadTest"; + +// TODO(port): +// This test requires the ability to pass file descriptors between processes. +// We haven't done that yet in Chrome for POSIX. +#if defined(OS_WIN) +// Each thread will open the shared memory. Each thread will take the memory, +// and keep changing it while trying to lock it, with some small pauses in +// between. Verify that each thread's value in the shared memory is always +// correct. +class MultipleLockThread : public PlatformThread::Delegate { + public: + explicit MultipleLockThread(int id) : id_(id) {} + ~MultipleLockThread() {} + + // PlatformThread::Delegate interface. + void ThreadMain() { + const int kDataSize = sizeof(int); + SharedMemoryHandle handle = NULL; + { + SharedMemory memory1; + EXPECT_TRUE(memory1.Create(L"SharedMemoryMultipleLockThreadTest", + false, true, kDataSize)); + EXPECT_TRUE(memory1.ShareToProcess(GetCurrentProcess(), &handle)); + // TODO(paulg): Implement this once we have a posix version of + // SharedMemory::ShareToProcess. + EXPECT_TRUE(true); + } + + SharedMemory memory2(handle, false); + EXPECT_TRUE(memory2.Map(kDataSize)); + volatile int* const ptr = static_cast(memory2.memory()); + + for (int idx = 0; idx < 20; idx++) { + memory2.Lock(); + int i = (id_ << 16) + idx; + *ptr = i; + PlatformThread::Sleep(1); // Short wait. + EXPECT_EQ(*ptr, i); + memory2.Unlock(); + } + + memory2.Close(); + } + + private: + int id_; + + DISALLOW_COPY_AND_ASSIGN(MultipleLockThread); +}; +#endif + +} // namespace + +TEST(SharedMemoryTest, OpenClose) { + const int kDataSize = 1024; + std::wstring test_name = L"SharedMemoryOpenCloseTest"; + + // Open two handles to a memory segment, confirm that they are mapped + // separately yet point to the same space. + SharedMemory memory1; + bool rv = memory1.Delete(test_name); + EXPECT_TRUE(rv); + rv = memory1.Delete(test_name); + EXPECT_TRUE(rv); + rv = memory1.Open(test_name, false); + EXPECT_FALSE(rv); + rv = memory1.Create(test_name, false, false, kDataSize); + EXPECT_TRUE(rv); + rv = memory1.Map(kDataSize); + EXPECT_TRUE(rv); + SharedMemory memory2; + rv = memory2.Open(test_name, false); + EXPECT_TRUE(rv); + rv = memory2.Map(kDataSize); + EXPECT_TRUE(rv); + EXPECT_NE(memory1.memory(), memory2.memory()); // Compare the pointers. + + // Make sure we don't segfault. (it actually happened!) + ASSERT_NE(memory1.memory(), static_cast(NULL)); + ASSERT_NE(memory2.memory(), static_cast(NULL)); + + // Write data to the first memory segment, verify contents of second. + memset(memory1.memory(), '1', kDataSize); + EXPECT_EQ(memcmp(memory1.memory(), memory2.memory(), kDataSize), 0); + + // Close the first memory segment, and verify the second has the right data. + memory1.Close(); + char *start_ptr = static_cast(memory2.memory()); + char *end_ptr = start_ptr + kDataSize; + for (char* ptr = start_ptr; ptr < end_ptr; ptr++) + EXPECT_EQ(*ptr, '1'); + + // Close the second memory segment. + memory2.Close(); + + rv = memory1.Delete(test_name); + EXPECT_TRUE(rv); + rv = memory2.Delete(test_name); + EXPECT_TRUE(rv); +} + +// Create a set of N threads to each open a shared memory segment and write to +// it. Verify that they are always reading/writing consistent data. +TEST(SharedMemoryTest, MultipleThreads) { + MultipleThreadMain::CleanUp(); + // On POSIX we have a problem when 2 threads try to create the shmem + // (a file) at exactly the same time, since create both creates the + // file and zerofills it. We solve the problem for this unit test + // (make it not flaky) by starting with 1 thread, then + // intentionally don't clean up its shmem before running with + // kNumThreads. + + int threadcounts[] = { 1, kNumThreads }; + for (size_t i = 0; i < sizeof(threadcounts) / sizeof(threadcounts); i++) { + int numthreads = threadcounts[i]; + scoped_array thread_handles; + scoped_array thread_delegates; + + thread_handles.reset(new PlatformThreadHandle[numthreads]); + thread_delegates.reset(new MultipleThreadMain*[numthreads]); + + // Spawn the threads. + for (int16 index = 0; index < numthreads; index++) { + PlatformThreadHandle pth; + thread_delegates[index] = new MultipleThreadMain(index); + EXPECT_TRUE(PlatformThread::Create(0, thread_delegates[index], &pth)); + thread_handles[index] = pth; + } + + // Wait for the threads to finish. + for (int index = 0; index < numthreads; index++) { + PlatformThread::Join(thread_handles[index]); + delete thread_delegates[index]; + } + } + MultipleThreadMain::CleanUp(); +} + +// TODO(port): this test requires the MultipleLockThread class +// (defined above), which requires the ability to pass file +// descriptors between processes. We haven't done that yet in Chrome +// for POSIX. +#if defined(OS_WIN) +// Create a set of threads to each open a shared memory segment and write to it +// with the lock held. Verify that they are always reading/writing consistent +// data. +TEST(SharedMemoryTest, Lock) { + PlatformThreadHandle thread_handles[kNumThreads]; + MultipleLockThread* thread_delegates[kNumThreads]; + + // Spawn the threads. + for (int index = 0; index < kNumThreads; ++index) { + PlatformThreadHandle pth; + thread_delegates[index] = new MultipleLockThread(index); + EXPECT_TRUE(PlatformThread::Create(0, thread_delegates[index], &pth)); + thread_handles[index] = pth; + } + + // Wait for the threads to finish. + for (int index = 0; index < kNumThreads; ++index) { + PlatformThread::Join(thread_handles[index]); + delete thread_delegates[index]; + } +} +#endif + +// Allocate private (unique) shared memory with an empty string for a +// name. Make sure several of them don't point to the same thing as +// we might expect if the names are equal. +TEST(SharedMemoryTest, AnonymousPrivate) { + int i, j; + int count = 4; + bool rv; + const int kDataSize = 8192; + + scoped_array memories(new SharedMemory[count]); + scoped_array pointers(new int*[count]); + ASSERT_TRUE(memories.get()); + ASSERT_TRUE(pointers.get()); + + for (i = 0; i < count; i++) { + rv = memories[i].Create(L"", false, true, kDataSize); + EXPECT_TRUE(rv); + rv = memories[i].Map(kDataSize); + EXPECT_TRUE(rv); + int *ptr = static_cast(memories[i].memory()); + EXPECT_TRUE(ptr); + pointers[i] = ptr; + } + + for (i = 0; i < count; i++) { + // zero out the first int in each except for i; for that one, make it 100. + for (j = 0; j < count; j++) { + if (i == j) + pointers[j][0] = 100; + else + pointers[j][0] = 0; + } + // make sure there is no bleeding of the 100 into the other pointers + for (j = 0; j < count; j++) { + if (i == j) + EXPECT_EQ(100, pointers[j][0]); + else + EXPECT_EQ(0, pointers[j][0]); + } + } + + for (int i = 0; i < count; i++) { + memories[i].Close(); + } + +} + + +// On POSIX it is especially important we test shmem across processes, +// not just across threads. But the test is enabled on all platforms. +class SharedMemoryProcessTest : public MultiProcessTest { + public: + + static void CleanUp() { + SharedMemory memory; + memory.Delete(s_test_name_); + } + + static int TaskTestMain() { + int errors = 0; + ScopedNSAutoreleasePool pool; // noop if not OSX + const int kDataSize = 1024; + SharedMemory memory; + bool rv = memory.Create(s_test_name_, false, true, kDataSize); + EXPECT_TRUE(rv); + if (rv != true) + errors++; + rv = memory.Map(kDataSize); + EXPECT_TRUE(rv); + if (rv != true) + errors++; + int *ptr = static_cast(memory.memory()); + + for (int idx = 0; idx < 20; idx++) { + memory.Lock(); + int i = (1 << 16) + idx; + *ptr = i; + PlatformThread::Sleep(10); // Short wait. + if (*ptr != i) + errors++; + memory.Unlock(); + } + + memory.Close(); + return errors; + } + + private: + static const wchar_t* const s_test_name_; +}; + +const wchar_t* const SharedMemoryProcessTest::s_test_name_ = L"MPMem"; + + +TEST_F(SharedMemoryProcessTest, Tasks) { + SharedMemoryProcessTest::CleanUp(); + + base::ProcessHandle handles[kNumTasks]; + for (int index = 0; index < kNumTasks; ++index) { + handles[index] = SpawnChild(L"SharedMemoryTestMain"); + } + + int exit_code = 0; + for (int index = 0; index < kNumTasks; ++index) { + EXPECT_TRUE(base::WaitForExitCode(handles[index], &exit_code)); + EXPECT_TRUE(exit_code == 0); + } + + SharedMemoryProcessTest::CleanUp(); +} + +MULTIPROCESS_TEST_MAIN(SharedMemoryTestMain) { + return SharedMemoryProcessTest::TaskTestMain(); +} + + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/shared_memory_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/shared_memory_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/shared_memory_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/shared_memory_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,196 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/shared_memory.h" + +#include "base/logging.h" +#include "base/win_util.h" +#ifdef CHROMIUM_MOZILLA_BUILD +# include "base/string_util.h" +#endif + +namespace base { + +SharedMemory::SharedMemory() + : mapped_file_(NULL), + memory_(NULL), + read_only_(false), + max_size_(0), + lock_(NULL) { +} + +SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only) + : mapped_file_(handle), + memory_(NULL), + read_only_(read_only), + max_size_(0), + lock_(NULL) { +} + +SharedMemory::SharedMemory(SharedMemoryHandle handle, bool read_only, + ProcessHandle process) + : mapped_file_(NULL), + memory_(NULL), + read_only_(read_only), + max_size_(0), + lock_(NULL) { + ::DuplicateHandle(process, handle, + GetCurrentProcess(), &mapped_file_, + STANDARD_RIGHTS_REQUIRED | + (read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS), + FALSE, 0); +} + +SharedMemory::~SharedMemory() { + Close(); + if (lock_ != NULL) + CloseHandle(lock_); +} + +// static +bool SharedMemory::IsHandleValid(const SharedMemoryHandle& handle) { + return handle != NULL; +} + +// static +SharedMemoryHandle SharedMemory::NULLHandle() { + return NULL; +} + +#ifdef CHROMIUM_MOZILLA_BUILD +bool SharedMemory::Create(const std::string &cname, bool read_only, +#else +bool SharedMemory::Create(const std::wstring &name, bool read_only, +#endif + bool open_existing, size_t size) { + DCHECK(mapped_file_ == NULL); + +#ifdef CHROMIUM_MOZILLA_BUILD + std::wstring name = UTF8ToWide(cname); +#endif + + name_ = name; + read_only_ = read_only; + mapped_file_ = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, + read_only_ ? PAGE_READONLY : PAGE_READWRITE, 0, static_cast(size), + name.empty() ? NULL : name.c_str()); + if (!mapped_file_) + return false; + + // Check if the shared memory pre-exists. + if (GetLastError() == ERROR_ALREADY_EXISTS && !open_existing) { + Close(); + return false; + } + max_size_ = size; + return true; +} + +bool SharedMemory::Delete(const std::wstring& name) { + // intentionally empty -- there is nothing for us to do on Windows. + return true; +} + +bool SharedMemory::Open(const std::wstring &name, bool read_only) { + DCHECK(mapped_file_ == NULL); + + name_ = name; + read_only_ = read_only; + mapped_file_ = OpenFileMapping( + read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, false, + name.empty() ? NULL : name.c_str()); + if (mapped_file_ != NULL) { + // Note: size_ is not set in this case. + return true; + } + return false; +} + +bool SharedMemory::Map(size_t bytes) { + if (mapped_file_ == NULL) + return false; + + memory_ = MapViewOfFile(mapped_file_, + read_only_ ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, 0, 0, bytes); + if (memory_ != NULL) { + return true; + } + return false; +} + +bool SharedMemory::Unmap() { + if (memory_ == NULL) + return false; + + UnmapViewOfFile(memory_); + memory_ = NULL; + return true; +} + +bool SharedMemory::ShareToProcessCommon(ProcessHandle process, + SharedMemoryHandle *new_handle, + bool close_self) { + *new_handle = 0; + DWORD access = STANDARD_RIGHTS_REQUIRED | FILE_MAP_READ; + DWORD options = 0; + HANDLE mapped_file = mapped_file_; + HANDLE result; + if (!read_only_) + access |= FILE_MAP_WRITE; + if (close_self) { + // DUPLICATE_CLOSE_SOURCE causes DuplicateHandle to close mapped_file. + options = DUPLICATE_CLOSE_SOURCE; + mapped_file_ = NULL; + Unmap(); + } + + if (process == GetCurrentProcess() && close_self) { + *new_handle = mapped_file; + return true; + } + + if (!DuplicateHandle(GetCurrentProcess(), mapped_file, process, + &result, access, FALSE, options)) + return false; + *new_handle = result; + return true; +} + + +void SharedMemory::Close() { + if (memory_ != NULL) { + UnmapViewOfFile(memory_); + memory_ = NULL; + } + + if (mapped_file_ != NULL) { + CloseHandle(mapped_file_); + mapped_file_ = NULL; + } +} + +void SharedMemory::Lock() { + if (lock_ == NULL) { + std::wstring name = name_; + name.append(L"lock"); + lock_ = CreateMutex(NULL, FALSE, name.c_str()); + DCHECK(lock_ != NULL); + if (lock_ == NULL) { + DLOG(ERROR) << "Could not create mutex" << GetLastError(); + return; // there is nothing good we can do here. + } + } + WaitForSingleObject(lock_, INFINITE); +} + +void SharedMemory::Unlock() { + DCHECK(lock_ != NULL); + ReleaseMutex(lock_); +} + +SharedMemoryHandle SharedMemory::handle() const { + return mapped_file_; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/simple_thread.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/simple_thread.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/simple_thread.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/simple_thread.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,118 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/simple_thread.h" + +#include "base/waitable_event.h" +#include "base/logging.h" +#include "base/platform_thread.h" +#include "base/string_util.h" + +namespace base { + +void SimpleThread::Start() { + DCHECK(!HasBeenStarted()) << "Tried to Start a thread multiple times."; + bool success = PlatformThread::Create(options_.stack_size(), this, &thread_); + CHECK(success); + event_.Wait(); // Wait for the thread to complete initialization. +} + +void SimpleThread::Join() { + DCHECK(HasBeenStarted()) << "Tried to Join a never-started thread."; + DCHECK(!HasBeenJoined()) << "Tried to Join a thread multiple times."; + PlatformThread::Join(thread_); + joined_ = true; +} + +void SimpleThread::ThreadMain() { + tid_ = PlatformThread::CurrentId(); + // Construct our full name of the form "name_prefix_/TID". + name_.push_back('/'); + name_.append(IntToString(tid_)); + PlatformThread::SetName(name_.c_str()); + + // We've initialized our new thread, signal that we're done to Start(). + event_.Signal(); + + Run(); +} + +SimpleThread::~SimpleThread() { + DCHECK(HasBeenStarted()) << "SimpleThread was never started."; + DCHECK(HasBeenJoined()) << "SimpleThread destroyed without being Join()ed."; +} + +void DelegateSimpleThread::Run() { + DCHECK(delegate_) << "Tried to call Run without a delegate (called twice?)"; + delegate_->Run(); + delegate_ = NULL; +} + +DelegateSimpleThreadPool::~DelegateSimpleThreadPool() { + DCHECK(threads_.empty()); + DCHECK(delegates_.empty()); + DCHECK(!dry_.IsSignaled()); +} + +void DelegateSimpleThreadPool::Start() { + DCHECK(threads_.empty()) << "Start() called with outstanding threads."; + for (int i = 0; i < num_threads_; ++i) { + DelegateSimpleThread* thread = new DelegateSimpleThread(this, name_prefix_); + thread->Start(); + threads_.push_back(thread); + } +} + +void DelegateSimpleThreadPool::JoinAll() { + DCHECK(!threads_.empty()) << "JoinAll() called with no outstanding threads."; + + // Tell all our threads to quit their worker loop. + AddWork(NULL, num_threads_); + + // Join and destroy all the worker threads. + for (int i = 0; i < num_threads_; ++i) { + threads_[i]->Join(); + delete threads_[i]; + } + threads_.clear(); + DCHECK(delegates_.empty()); +} + +void DelegateSimpleThreadPool::AddWork(Delegate* delegate, int repeat_count) { + AutoLock locked(lock_); + for (int i = 0; i < repeat_count; ++i) + delegates_.push(delegate); + // If we were empty, signal that we have work now. + if (!dry_.IsSignaled()) + dry_.Signal(); +} + +void DelegateSimpleThreadPool::Run() { + Delegate* work; + + while (true) { + dry_.Wait(); + { + AutoLock locked(lock_); + if (!dry_.IsSignaled()) + continue; + + DCHECK(!delegates_.empty()); + work = delegates_.front(); + delegates_.pop(); + + // Signal to any other threads that we're currently out of work. + if (delegates_.empty()) + dry_.Reset(); + } + + // A NULL delegate pointer signals us to quit. + if (!work) + break; + + work->Run(); + } +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/simple_thread.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/simple_thread.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/simple_thread.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/simple_thread.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,189 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// WARNING: You should probably be using Thread (thread.h) instead. Thread is +// Chrome's message-loop based Thread abstraction, and if you are a +// thread running in the browser, there will likely be assumptions +// that your thread will have an associated message loop. +// +// This is a simple thread interface that backs to a native operating system +// thread. You should use this only when you want a thread that does not have +// an associated MessageLoop. Unittesting is the best example of this. +// +// The simplest interface to use is DelegateSimpleThread, which will create +// a new thread, and execute the Delegate's virtual Run() in this new thread +// until it has completed, exiting the thread. +// +// NOTE: You *MUST* call Join on the thread to clean up the underlying thread +// resources. You are also responsible for destructing the SimpleThread object. +// It is invalid to destroy a SimpleThread while it is running, or without +// Start() having been called (and a thread never created). The Delegate +// object should live as long as a DelegateSimpleThread. +// +// Thread Safety: A SimpleThread is not completely thread safe. It is safe to +// access it from the creating thread or from the newly created thread. This +// implies that the creator thread should be the thread that calls Join. +// +// Example: +// class MyThreadRunner : public DelegateSimpleThread::Delegate { ... }; +// MyThreadRunner runner; +// DelegateSimpleThread thread(&runner, "good_name_here"); +// thread.Start(); +// // Start will return after the Thread has been successfully started and +// // initialized. The newly created thread will invoke runner->Run(), and +// // run until it returns. +// thread.Join(); // Wait until the thread has exited. You *MUST* Join! +// // The SimpleThread object is still valid, however you may not call Join +// // or Start again. + +#ifndef BASE_SIMPLE_THREAD_H_ +#define BASE_SIMPLE_THREAD_H_ + +#include +#include +#include + +#include "base/basictypes.h" +#include "base/lock.h" +#include "base/waitable_event.h" +#include "base/platform_thread.h" + +namespace base { + +// This is the base SimpleThread. You can derive from it and implement the +// virtual Run method, or you can use the DelegateSimpleThread interface. +class SimpleThread : public PlatformThread::Delegate { + public: + class Options { + public: + Options() : stack_size_(0) { } + ~Options() { } + + // We use the standard compiler-supplied copy constructor. + + // A custom stack size, or 0 for the system default. + void set_stack_size(size_t size) { stack_size_ = size; } + size_t stack_size() const { return stack_size_; } + private: + size_t stack_size_; + }; + + // Create a SimpleThread. |options| should be used to manage any specific + // configuration involving the thread creation and management. + // Every thread has a name, in the form of |name_prefix|/TID, for example + // "my_thread/321". The thread will not be created until Start() is called. + explicit SimpleThread(const std::string& name_prefix) + : name_prefix_(name_prefix), name_(name_prefix), + thread_(), event_(true, false), tid_(0), joined_(false) { } + SimpleThread(const std::string& name_prefix, const Options& options) + : name_prefix_(name_prefix), name_(name_prefix), options_(options), + thread_(), event_(true, false), tid_(0), joined_(false) { } + + virtual ~SimpleThread(); + + virtual void Start(); + virtual void Join(); + + // We follow the PlatformThread Delegate interface. + virtual void ThreadMain(); + + // Subclasses should override the Run method. + virtual void Run() = 0; + + // Return the thread name prefix, or "unnamed" if none was supplied. + std::string name_prefix() { return name_prefix_; } + + // Return the completed name including TID, only valid after Start(). + std::string name() { return name_; } + + // Return the thread id, only valid after Start(). + PlatformThreadId tid() { return tid_; } + + // Return True if Start() has ever been called. + bool HasBeenStarted() { return event_.IsSignaled(); } + + // Return True if Join() has evern been called. + bool HasBeenJoined() { return joined_; } + + private: + const std::string name_prefix_; + std::string name_; + const Options options_; + PlatformThreadHandle thread_; // PlatformThread handle, invalid after Join! + WaitableEvent event_; // Signaled if Start() was ever called. + PlatformThreadId tid_; // The backing thread's id. + bool joined_; // True if Join has been called. +}; + +class DelegateSimpleThread : public SimpleThread { + public: + class Delegate { + public: + Delegate() { } + virtual ~Delegate() { } + virtual void Run() = 0; + }; + + DelegateSimpleThread(Delegate* delegate, + const std::string& name_prefix) + : SimpleThread(name_prefix), delegate_(delegate) { } + DelegateSimpleThread(Delegate* delegate, + const std::string& name_prefix, + const Options& options) + : SimpleThread(name_prefix, options), delegate_(delegate) { } + + virtual ~DelegateSimpleThread() { } + virtual void Run(); + private: + Delegate* delegate_; +}; + +// DelegateSimpleThreadPool allows you to start up a fixed number of threads, +// and then add jobs which will be dispatched to the threads. This is +// convenient when you have a lot of small work that you want done +// multi-threaded, but don't want to spawn a thread for each small bit of work. +// +// You just call AddWork() to add a delegate to the list of work to be done. +// JoinAll() will make sure that all outstanding work is processed, and wait +// for everything to finish. You can reuse a pool, so you can call Start() +// again after you've called JoinAll(). +class DelegateSimpleThreadPool : public DelegateSimpleThread::Delegate { + public: + typedef DelegateSimpleThread::Delegate Delegate; + + DelegateSimpleThreadPool(const std::string name_prefix, int num_threads) + : name_prefix_(name_prefix), num_threads_(num_threads), + dry_(true, false) { } + ~DelegateSimpleThreadPool(); + + // Start up all of the underlying threads, and start processing work if we + // have any. + void Start(); + + // Make sure all outstanding work is finished, and wait for and destroy all + // of the underlying threads in the pool. + void JoinAll(); + + // It is safe to AddWork() any time, before or after Start(). + // Delegate* should always be a valid pointer, NULL is reserved internally. + void AddWork(Delegate* work, int repeat_count); + void AddWork(Delegate* work) { + AddWork(work, 1); + } + + // We implement the Delegate interface, for running our internal threads. + virtual void Run(); + + private: + const std::string name_prefix_; + int num_threads_; + std::vector threads_; + std::queue delegates_; + Lock lock_; // Locks delegates_ + WaitableEvent dry_; // Not signaled when there is no work to do. +}; + +} // namespace base + +#endif // BASE_SIMPLE_THREAD_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/simple_thread_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/simple_thread_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/simple_thread_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/simple_thread_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,167 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/atomic_sequence_num.h" +#include "base/lock.h" +#include "base/simple_thread.h" +#include "base/string_util.h" +#include "base/waitable_event.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class SetIntRunner : public base::DelegateSimpleThread::Delegate { + public: + SetIntRunner(int* ptr, int val) : ptr_(ptr), val_(val) { } + ~SetIntRunner() { } + + virtual void Run() { + *ptr_ = val_; + } + + private: + int* ptr_; + int val_; +}; + +class WaitEventRunner : public base::DelegateSimpleThread::Delegate { + public: + WaitEventRunner(base::WaitableEvent* event) : event_(event) { } + ~WaitEventRunner() { } + + virtual void Run() { + EXPECT_FALSE(event_->IsSignaled()); + event_->Signal(); + EXPECT_TRUE(event_->IsSignaled()); + } + private: + base::WaitableEvent* event_; +}; + +class SeqRunner : public base::DelegateSimpleThread::Delegate { + public: + SeqRunner(base::AtomicSequenceNumber* seq) : seq_(seq) { } + virtual void Run() { + seq_->GetNext(); + } + + private: + base::AtomicSequenceNumber* seq_; +}; + +// We count up on a sequence number, firing on the event when we've hit our +// expected amount, otherwise we wait on the event. This will ensure that we +// have all threads outstanding until we hit our expected thread pool size. +class VerifyPoolRunner : public base::DelegateSimpleThread::Delegate { + public: + VerifyPoolRunner(base::AtomicSequenceNumber* seq, + int total, base::WaitableEvent* event) + : seq_(seq), total_(total), event_(event) { } + + virtual void Run() { + if (seq_->GetNext() == total_) { + event_->Signal(); + } else { + event_->Wait(); + } + } + + private: + base::AtomicSequenceNumber* seq_; + int total_; + base::WaitableEvent* event_; +}; + +} // namespace + +TEST(SimpleThreadTest, CreateAndJoin) { + int stack_int = 0; + + SetIntRunner runner(&stack_int, 7); + EXPECT_EQ(0, stack_int); + + base::DelegateSimpleThread thread(&runner, "int_setter"); + EXPECT_FALSE(thread.HasBeenStarted()); + EXPECT_FALSE(thread.HasBeenJoined()); + EXPECT_EQ(0, stack_int); + + thread.Start(); + EXPECT_TRUE(thread.HasBeenStarted()); + EXPECT_FALSE(thread.HasBeenJoined()); + + thread.Join(); + EXPECT_TRUE(thread.HasBeenStarted()); + EXPECT_TRUE(thread.HasBeenJoined()); + EXPECT_EQ(7, stack_int); +} + +TEST(SimpleThreadTest, WaitForEvent) { + // Create a thread, and wait for it to signal us. + base::WaitableEvent event(true, false); + + WaitEventRunner runner(&event); + base::DelegateSimpleThread thread(&runner, "event_waiter"); + + EXPECT_FALSE(event.IsSignaled()); + thread.Start(); + event.Wait(); + EXPECT_TRUE(event.IsSignaled()); + thread.Join(); +} + +TEST(SimpleThreadTest, NamedWithOptions) { + base::WaitableEvent event(true, false); + + WaitEventRunner runner(&event); + base::SimpleThread::Options options; + base::DelegateSimpleThread thread(&runner, "event_waiter", options); + EXPECT_EQ(thread.name_prefix(), "event_waiter"); + EXPECT_FALSE(event.IsSignaled()); + + thread.Start(); + EXPECT_EQ(thread.name_prefix(), "event_waiter"); + EXPECT_EQ(thread.name(), std::string("event_waiter/") + + IntToString(thread.tid())); + event.Wait(); + + EXPECT_TRUE(event.IsSignaled()); + thread.Join(); + + // We keep the name and tid, even after the thread is gone. + EXPECT_EQ(thread.name_prefix(), "event_waiter"); + EXPECT_EQ(thread.name(), std::string("event_waiter/") + + IntToString(thread.tid())); +} + +TEST(SimpleThreadTest, ThreadPool) { + base::AtomicSequenceNumber seq; + SeqRunner runner(&seq); + base::DelegateSimpleThreadPool pool("seq_runner", 10); + + // Add work before we're running. + pool.AddWork(&runner, 300); + + EXPECT_EQ(seq.GetNext(), 0); + pool.Start(); + + // Add work while we're running. + pool.AddWork(&runner, 300); + + pool.JoinAll(); + + EXPECT_EQ(seq.GetNext(), 601); + + // We can reuse our pool. Verify that all 10 threads can actually run in + // parallel, so this test will only pass if there are actually 10 threads. + base::AtomicSequenceNumber seq2; + base::WaitableEvent event(true, false); + // Changing 9 to 10, for example, would cause us JoinAll() to never return. + VerifyPoolRunner verifier(&seq2, 9, &event); + pool.Start(); + + pool.AddWork(&verifier, 10); + + pool.JoinAll(); + EXPECT_EQ(seq2.GetNext(), 10); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/singleton.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/singleton.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/singleton.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/singleton.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,181 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SINGLETON_H_ +#define BASE_SINGLETON_H_ + +#include "base/at_exit.h" +#include "base/atomicops.h" +#include "base/platform_thread.h" + +// Default traits for Singleton. Calls operator new and operator delete on +// the object. Registers automatic deletion at process exit. +// Overload if you need arguments or another memory allocation function. +template +struct DefaultSingletonTraits { + // Allocates the object. + static Type* New() { + // The parenthesis is very important here; it forces POD type + // initialization. + return new Type(); + } + + // Destroys the object. + static void Delete(Type* x) { + delete x; + } + + // Set to true to automatically register deletion of the object on process + // exit. See below for the required call that makes this happen. + static const bool kRegisterAtExit = true; +}; + + +// Alternate traits for use with the Singleton. Identical to +// DefaultSingletonTraits except that the Singleton will not be cleaned up +// at exit. +template +struct LeakySingletonTraits : public DefaultSingletonTraits { + static const bool kRegisterAtExit = false; +}; + + +// The Singleton class manages a single +// instance of Type which will be created on first use and will be destroyed at +// normal process exit). The Trait::Delete function will not be called on +// abnormal process exit. +// +// DifferentiatingType is used as a key to differentiate two different +// singletons having the same memory allocation functions but serving a +// different purpose. This is mainly used for Locks serving different purposes. +// +// Example usages: (none are preferred, they all result in the same code) +// 1. FooClass* ptr = Singleton::get(); +// ptr->Bar(); +// 2. Singleton()->Bar(); +// 3. Singleton::get()->Bar(); +// +// Singleton<> has no non-static members and doesn't need to actually be +// instantiated. It does no harm to instantiate it and use it as a class member +// or at global level since it is acting as a POD type. +// +// This class is itself thread-safe. The underlying Type must of course be +// thread-safe if you want to use it concurrently. Two parameters may be tuned +// depending on the user's requirements. +// +// Glossary: +// RAE = kRegisterAtExit +// +// On every platform, if Traits::RAE is true, the singleton will be destroyed at +// process exit. More precisely it uses base::AtExitManager which requires an +// object of this type to be instanciated. AtExitManager mimics the semantics +// of atexit() such as LIFO order but under Windows is safer to call. For more +// information see at_exit.h. +// +// If Traits::RAE is false, the singleton will not be freed at process exit, +// thus the singleton will be leaked if it is ever accessed. Traits::RAE +// shouldn't be false unless absolutely necessary. Remember that the heap where +// the object is allocated may be destroyed by the CRT anyway. +// +// If you want to ensure that your class can only exist as a singleton, make +// its constructors private, and make DefaultSingletonTraits<> a friend: +// +// #include "base/singleton.h" +// class FooClass { +// public: +// void Bar() { ... } +// private: +// FooClass() { ... } +// friend struct DefaultSingletonTraits; +// +// DISALLOW_EVIL_CONSTRUCTORS(FooClass); +// }; +// +// Caveats: +// (a) Every call to get(), operator->() and operator*() incurs some overhead +// (16ns on my P4/2.8GHz) to check whether the object has already been +// initialized. You may wish to cache the result of get(); it will not +// change. +// +// (b) Your factory function must never throw an exception. This class is not +// exception-safe. +// +template , + typename DifferentiatingType = Type> +class Singleton { + public: + // This class is safe to be constructed and copy-constructed since it has no + // member. + + // Return a pointer to the one true instance of the class. + static Type* get() { + // Our AtomicWord doubles as a spinlock, where a value of + // kBeingCreatedMarker means the spinlock is being held for creation. + static const base::subtle::AtomicWord kBeingCreatedMarker = 1; + + base::subtle::AtomicWord value = base::subtle::NoBarrier_Load(&instance_); + if (value != 0 && value != kBeingCreatedMarker) + return reinterpret_cast(value); + + // Object isn't created yet, maybe we will get to create it, let's try... + if (base::subtle::Acquire_CompareAndSwap(&instance_, + 0, + kBeingCreatedMarker) == 0) { + // instance_ was NULL and is now kBeingCreatedMarker. Only one thread + // will ever get here. Threads might be spinning on us, and they will + // stop right after we do this store. + Type* newval = Traits::New(); + base::subtle::Release_Store( + &instance_, reinterpret_cast(newval)); + + if (Traits::kRegisterAtExit) + base::AtExitManager::RegisterCallback(OnExit, NULL); + + return newval; + } + + // We hit a race. Another thread beat us and either: + // - Has the object in BeingCreated state + // - Already has the object created... + // We know value != NULL. It could be kBeingCreatedMarker, or a valid ptr. + // Unless your constructor can be very time consuming, it is very unlikely + // to hit this race. When it does, we just spin and yield the thread until + // the object has been created. + while (true) { + value = base::subtle::NoBarrier_Load(&instance_); + if (value != kBeingCreatedMarker) + break; + PlatformThread::YieldCurrentThread(); + } + + return reinterpret_cast(value); + } + + // Shortcuts. + Type& operator*() { + return *get(); + } + + Type* operator->() { + return get(); + } + + private: + // Adapter function for use with AtExit(). This should be called single + // threaded, but we might as well take the precautions anyway. + static void OnExit(void* unused) { + // AtExit should only ever be register after the singleton instance was + // created. We should only ever get here with a valid instance_ pointer. + Traits::Delete(reinterpret_cast( + base::subtle::NoBarrier_AtomicExchange(&instance_, 0))); + } + static base::subtle::AtomicWord instance_; +}; + +template +base::subtle::AtomicWord Singleton:: + instance_ = 0; + +#endif // BASE_SINGLETON_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/singleton_objc.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/singleton_objc.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/singleton_objc.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/singleton_objc.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,60 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Support for using the Singleton pattern with Objective-C objects. A +// SingletonObjC is the same as a Singleton, except the default traits are +// appropriate for Objective-C objects. A typical Objective-C object of type +// NSExampleType can be maintained as a singleton and accessed with: +// +// NSExampleType* exampleSingleton = SingletonObjC::get(); +// +// The first time this is used, it will create exampleSingleton as the result +// of [[NSExampleType alloc] init]. Subsequent calls will return the same +// NSExampleType* object. The object will be released by calling +// -[NSExampleType release] when Singleton's atexit routines run +// (see singleton.h). +// +// For Objective-C objects initialized through means other than the +// no-parameter -init selector, DefaultSingletonObjCTraits may be extended +// as needed: +// +// struct FooSingletonTraits : public DefaultSingletonObjCTraits { +// static Foo* New() { +// return [[Foo alloc] initWithName:@"selecty"]; +// } +// } +// ... +// Foo* widgetSingleton = SingletonObjC::get(); + +#ifndef BASE_SINGLETON_OBJC_H_ +#define BASE_SINGLETON_OBJC_H_ + +#import +#include "base/singleton.h" + +// Singleton traits usable to manage traditional Objective-C objects, which +// are instantiated by sending |alloc| and |init| messages, and are deallocated +// in a memory-managed environment when their retain counts drop to 0 by +// sending |release| messages. +template +struct DefaultSingletonObjCTraits : public DefaultSingletonTraits { + static Type* New() { + return [[Type alloc] init]; + } + + static void Delete(Type* object) { + [object release]; + } +}; + +// Exactly like Singleton, but without the DefaultSingletonObjCTraits as the +// default trait class. This makes it straightforward for Objective-C++ code +// to hold Objective-C objects as singletons. +template, + typename DifferentiatingType = Type> +class SingletonObjC : public Singleton { +}; + +#endif // BASE_SINGLETON_OBJC_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/singleton_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/singleton_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/singleton_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/singleton_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,209 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/at_exit.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/singleton.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class ShadowingAtExitManager : public base::AtExitManager { + public: + ShadowingAtExitManager() : AtExitManager(true) { } +}; + +COMPILE_ASSERT(DefaultSingletonTraits::kRegisterAtExit == true, a); + +template +struct LockTrait : public DefaultSingletonTraits { +}; + +struct Init5Trait : public DefaultSingletonTraits { + static int* New() { + return new int(5); + } +}; + +typedef void (*CallbackFunc)(); + +struct CallbackTrait : public DefaultSingletonTraits { + static void Delete(CallbackFunc* p) { + if (*p) + (*p)(); + DefaultSingletonTraits::Delete(p); + } +}; + +struct NoLeakTrait : public CallbackTrait { +}; + +struct LeakTrait : public CallbackTrait { + static const bool kRegisterAtExit = false; +}; + +int* SingletonInt1() { + return Singleton::get(); +} + +int* SingletonInt2() { + // Force to use a different singleton than SingletonInt1. + return Singleton >::get(); +} + +class DummyDifferentiatingClass { +}; + +int* SingletonInt3() { + // Force to use a different singleton than SingletonInt1 and SingletonInt2. + // Note that any type can be used; int, float, std::wstring... + return Singleton, + DummyDifferentiatingClass>::get(); +} + +int* SingletonInt4() { + return Singleton >::get(); +} + +int* SingletonInt5() { + return Singleton::get(); +} + +void SingletonNoLeak(CallbackFunc CallOnQuit) { + *Singleton::get() = CallOnQuit; +} + +void SingletonLeak(CallbackFunc CallOnQuit) { + *Singleton::get() = CallOnQuit; +} + +CallbackFunc* GetLeakySingleton() { + return Singleton::get(); +} + +} // namespace + +class SingletonTest : public testing::Test { + public: + SingletonTest() { } + + virtual void SetUp() { + non_leak_called_ = false; + leaky_called_ = false; + } + + protected: + void VerifiesCallbacks() { + EXPECT_TRUE(non_leak_called_); + EXPECT_FALSE(leaky_called_); + non_leak_called_ = false; + leaky_called_ = false; + } + + void VerifiesCallbacksNotCalled() { + EXPECT_FALSE(non_leak_called_); + EXPECT_FALSE(leaky_called_); + non_leak_called_ = false; + leaky_called_ = false; + } + + static void CallbackNoLeak() { + non_leak_called_ = true; + } + + static void CallbackLeak() { + leaky_called_ = true; + } + + private: + static bool non_leak_called_; + static bool leaky_called_; +}; + +bool SingletonTest::non_leak_called_ = false; +bool SingletonTest::leaky_called_ = false; + +TEST_F(SingletonTest, Basic) { + int* singleton_int_1; + int* singleton_int_2; + int* singleton_int_3; + int* singleton_int_4; + int* singleton_int_5; + CallbackFunc* leaky_singleton; + + { + ShadowingAtExitManager sem; + { + singleton_int_1 = SingletonInt1(); + } + // Ensure POD type initialization. + EXPECT_EQ(*singleton_int_1, 0); + *singleton_int_1 = 1; + + EXPECT_EQ(singleton_int_1, SingletonInt1()); + EXPECT_EQ(*singleton_int_1, 1); + + { + singleton_int_2 = SingletonInt2(); + } + // Same instance that 1. + EXPECT_EQ(*singleton_int_2, 1); + EXPECT_EQ(singleton_int_1, singleton_int_2); + + { + singleton_int_3 = SingletonInt3(); + } + // Different instance than 1 and 2. + EXPECT_EQ(*singleton_int_3, 0); + EXPECT_NE(singleton_int_1, singleton_int_3); + *singleton_int_3 = 3; + EXPECT_EQ(*singleton_int_1, 1); + EXPECT_EQ(*singleton_int_2, 1); + + { + singleton_int_4 = SingletonInt4(); + } + // Use a lock for creation. Not really tested at length. + EXPECT_EQ(*singleton_int_4, 0); + *singleton_int_4 = 4; + EXPECT_NE(singleton_int_1, singleton_int_4); + EXPECT_NE(singleton_int_3, singleton_int_4); + + { + singleton_int_5 = SingletonInt5(); + } + // Is default initialized to 5. + EXPECT_EQ(*singleton_int_5, 5); + EXPECT_NE(singleton_int_1, singleton_int_5); + EXPECT_NE(singleton_int_3, singleton_int_5); + EXPECT_NE(singleton_int_4, singleton_int_5); + + SingletonNoLeak(&CallbackNoLeak); + SingletonLeak(&CallbackLeak); + leaky_singleton = GetLeakySingleton(); + EXPECT_TRUE(leaky_singleton); + } + + // Verify that only the expected callback has been called. + VerifiesCallbacks(); + // Delete the leaky singleton. It is interesting to note that Purify does + // *not* detect the leak when this call is commented out. :( + DefaultSingletonTraits::Delete(leaky_singleton); + + { + ShadowingAtExitManager sem; + // Verifiy that the variables were reset. + { + singleton_int_1 = SingletonInt1(); + EXPECT_EQ(*singleton_int_1, 0); + } + { + singleton_int_5 = SingletonInt5(); + EXPECT_EQ(*singleton_int_5, 5); + } + } + // The leaky singleton shouldn't leak since SingletonLeak has not been called. + VerifiesCallbacksNotCalled(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/spin_wait.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/spin_wait.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/spin_wait.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/spin_wait.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,51 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file provides a macro ONLY for use in testing. +// DO NOT USE IN PRODUCTION CODE. There are much better ways to wait. + +// This code is very helpful in testing multi-threaded code, without depending +// on almost any primitives. This is especially helpful if you are testing +// those primitive multi-threaded constructs. + +// We provide a simple one argument spin wait (for 1 second), and a generic +// spin wait (for longer periods of time). + +#ifndef BASE_SPIN_WAIT_H__ +#define BASE_SPIN_WAIT_H__ + +#include "base/platform_thread.h" +#include "base/time.h" + +// Provide a macro that will wait no longer than 1 second for an asynchronous +// change is the value of an expression. +// A typical use would be: +// +// SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(0 == f(x)); +// +// The expression will be evaluated repeatedly until it is true, or until +// the time (1 second) expires. +// Since tests generally have a 5 second watch dog timer, this spin loop is +// typically used to get the padding needed on a given test platform to assure +// that the test passes, even if load varies, and external events vary. + +#define SPIN_FOR_1_SECOND_OR_UNTIL_TRUE(expression) \ + SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(base::TimeDelta::FromSeconds(1), \ + (expression)) + +#define SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(delta, expression) do { \ + base::Time start = base::Time::Now(); \ + const base::TimeDelta kTimeout = delta; \ + while(!(expression)) { \ + if (kTimeout < base::Time::Now() - start) { \ + EXPECT_LE((base::Time::Now() - start).InMilliseconds(), \ + kTimeout.InMilliseconds()) << "Timed out"; \ + break; \ + } \ + PlatformThread::Sleep(50); \ + } \ + } \ + while(0) + +#endif // BASE_SPIN_WAIT_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/stack_container.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/stack_container.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/stack_container.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/stack_container.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,253 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STACK_CONTAINER_H_ +#define BASE_STACK_CONTAINER_H_ + +#include +#include + +#include "base/basictypes.h" + +// This allocator can be used with STL containers to provide a stack buffer +// from which to allocate memory and overflows onto the heap. This stack buffer +// would be allocated on the stack and allows us to avoid heap operations in +// some situations. +// +// STL likes to make copies of allocators, so the allocator itself can't hold +// the data. Instead, we make the creator responsible for creating a +// StackAllocator::Source which contains the data. Copying the allocator +// merely copies the pointer to this shared source, so all allocators created +// based on our allocator will share the same stack buffer. +// +// This stack buffer implementation is very simple. The first allocation that +// fits in the stack buffer will use the stack buffer. Any subsequent +// allocations will not use the stack buffer, even if there is unused room. +// This makes it appropriate for array-like containers, but the caller should +// be sure to reserve() in the container up to the stack buffer size. Otherwise +// the container will allocate a small array which will "use up" the stack +// buffer. +template +class StackAllocator : public std::allocator { + public: + typedef typename std::allocator::pointer pointer; + typedef typename std::allocator::size_type size_type; + + // Backing store for the allocator. The container owner is responsible for + // maintaining this for as long as any containers using this allocator are + // live. + struct Source { + Source() : used_stack_buffer_(false) { + } + + // Casts the buffer in its right type. + T* stack_buffer() { return reinterpret_cast(stack_buffer_); } + const T* stack_buffer() const { + return reinterpret_cast(stack_buffer_); + } + + // + // IMPORTANT: Take care to ensure that stack_buffer_ is aligned + // since it is used to mimic an array of T. + // Be careful while declaring any unaligned types (like bool) + // before stack_buffer_. + // + + // The buffer itself. It is not of type T because we don't want the + // constructors and destructors to be automatically called. Define a POD + // buffer of the right size instead. + char stack_buffer_[sizeof(T[stack_capacity])]; + + // Set when the stack buffer is used for an allocation. We do not track + // how much of the buffer is used, only that somebody is using it. + bool used_stack_buffer_; + }; + + // Used by containers when they want to refer to an allocator of type U. + template + struct rebind { + typedef StackAllocator other; + }; + + // For the straight up copy c-tor, we can share storage. + StackAllocator(const StackAllocator& rhs) + : source_(rhs.source_) { + } + + // ISO C++ requires the following constructor to be defined, + // and std::vector in VC++2008SP1 Release fails with an error + // in the class _Container_base_aux_alloc_real (from ) + // if the constructor does not exist. + // For this constructor, we cannot share storage; there's + // no guarantee that the Source buffer of Ts is large enough + // for Us. + // TODO: If we were fancy pants, perhaps we could share storage + // iff sizeof(T) == sizeof(U). + template + StackAllocator(const StackAllocator& other) + : source_(NULL) { + } + + explicit StackAllocator(Source* source) : source_(source) { + } + + // Actually do the allocation. Use the stack buffer if nobody has used it yet + // and the size requested fits. Otherwise, fall through to the standard + // allocator. + pointer allocate(size_type n, void* hint = 0) { + if (source_ != NULL && !source_->used_stack_buffer_ + && n <= stack_capacity) { + source_->used_stack_buffer_ = true; + return source_->stack_buffer(); + } else { + return std::allocator::allocate(n, hint); + } + } + + // Free: when trying to free the stack buffer, just mark it as free. For + // non-stack-buffer pointers, just fall though to the standard allocator. + void deallocate(pointer p, size_type n) { + if (source_ != NULL && p == source_->stack_buffer()) + source_->used_stack_buffer_ = false; + else + std::allocator::deallocate(p, n); + } + + private: + Source* source_; +}; + +// A wrapper around STL containers that maintains a stack-sized buffer that the +// initial capacity of the vector is based on. Growing the container beyond the +// stack capacity will transparently overflow onto the heap. The container must +// support reserve(). +// +// WATCH OUT: the ContainerType MUST use the proper StackAllocator for this +// type. This object is really intended to be used only internally. You'll want +// to use the wrappers below for different types. +template +class StackContainer { + public: + typedef TContainerType ContainerType; + typedef typename ContainerType::value_type ContainedType; + typedef StackAllocator Allocator; + + // Allocator must be constructed before the container! + StackContainer() : allocator_(&stack_data_), container_(allocator_) { + // Make the container use the stack allocation by reserving our buffer size + // before doing anything else. + container_.reserve(stack_capacity); + } + + // Getters for the actual container. + // + // Danger: any copies of this made using the copy constructor must have + // shorter lifetimes than the source. The copy will share the same allocator + // and therefore the same stack buffer as the original. Use std::copy to + // copy into a "real" container for longer-lived objects. + ContainerType& container() { return container_; } + const ContainerType& container() const { return container_; } + + // Support operator-> to get to the container. This allows nicer syntax like: + // StackContainer<...> foo; + // std::sort(foo->begin(), foo->end()); + ContainerType* operator->() { return &container_; } + const ContainerType* operator->() const { return &container_; } + +#ifdef UNIT_TEST + // Retrieves the stack source so that that unit tests can verify that the + // buffer is being used properly. + const typename Allocator::Source& stack_data() const { + return stack_data_; + } +#endif + + protected: + typename Allocator::Source stack_data_; + Allocator allocator_; + ContainerType container_; + + DISALLOW_EVIL_CONSTRUCTORS(StackContainer); +}; + +// StackString +template +class StackString : public StackContainer< + std::basic_string, + StackAllocator >, + stack_capacity> { + public: + StackString() : StackContainer< + std::basic_string, + StackAllocator >, + stack_capacity>() { + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(StackString); +}; + +// StackWString +template +class StackWString : public StackContainer< + std::basic_string, + StackAllocator >, + stack_capacity> { + public: + StackWString() : StackContainer< + std::basic_string, + StackAllocator >, + stack_capacity>() { + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(StackWString); +}; + +// StackVector +// +// Example: +// StackVector foo; +// foo->push_back(22); // we have overloaded operator-> +// foo[0] = 10; // as well as operator[] +template +class StackVector : public StackContainer< + std::vector >, + stack_capacity> { + public: + StackVector() : StackContainer< + std::vector >, + stack_capacity>() { + } + + // We need to put this in STL containers sometimes, which requires a copy + // constructor. We can't call the regular copy constructor because that will + // take the stack buffer from the original. Here, we create an empty object + // and make a stack buffer of its own. + StackVector(const StackVector& other) + : StackContainer< + std::vector >, + stack_capacity>() { + this->container().assign(other->begin(), other->end()); + } + + StackVector& operator=( + const StackVector& other) { + this->container().assign(other->begin(), other->end()); + return *this; + } + + // Vectors are commonly indexed, which isn't very convenient even with + // operator-> (using "->at()" does exception stuff we don't want). + T& operator[](size_t i) { return this->container().operator[](i); } + const T& operator[](size_t i) const { + return this->container().operator[](i); + } +}; + +#endif // BASE_STACK_CONTAINER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/stack_container_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/stack_container_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/stack_container_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/stack_container_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,116 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/stack_container.h" + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "base/ref_counted.h" + +namespace { + +class Dummy : public base::RefCounted { + public: + Dummy(int* alive) : alive_(alive) { + ++*alive_; + } + ~Dummy() { + --*alive_; + } + private: + int* const alive_; +}; + +} // namespace + +TEST(StackContainer, Vector) { + const int stack_size = 3; + StackVector vect; + const int* stack_buffer = &vect.stack_data().stack_buffer()[0]; + + // The initial |stack_size| elements should appear in the stack buffer. + EXPECT_EQ(static_cast(stack_size), vect.container().capacity()); + for (int i = 0; i < stack_size; i++) { + vect.container().push_back(i); + EXPECT_EQ(stack_buffer, &vect.container()[0]); + EXPECT_TRUE(vect.stack_data().used_stack_buffer_); + } + + // Adding more elements should push the array onto the heap. + for (int i = 0; i < stack_size; i++) { + vect.container().push_back(i + stack_size); + EXPECT_NE(stack_buffer, &vect.container()[0]); + EXPECT_FALSE(vect.stack_data().used_stack_buffer_); + } + + // The array should still be in order. + for (int i = 0; i < stack_size * 2; i++) + EXPECT_EQ(i, vect.container()[i]); + + // Resize to smaller. Our STL implementation won't reallocate in this case, + // otherwise it might use our stack buffer. We reserve right after the resize + // to guarantee it isn't using the stack buffer, even though it doesn't have + // much data. + vect.container().resize(stack_size); + vect.container().reserve(stack_size * 2); + EXPECT_FALSE(vect.stack_data().used_stack_buffer_); + + // Copying the small vector to another should use the same allocator and use + // the now-unused stack buffer. GENERALLY CALLERS SHOULD NOT DO THIS since + // they have to get the template types just right and it can cause errors. + std::vector > other(vect.container()); + EXPECT_EQ(stack_buffer, &other.front()); + EXPECT_TRUE(vect.stack_data().used_stack_buffer_); + for (int i = 0; i < stack_size; i++) + EXPECT_EQ(i, other[i]); +} + +TEST(StackContainer, VectorDoubleDelete) { + // Regression testing for double-delete. + typedef StackVector, 2> Vector; + typedef Vector::ContainerType Container; + Vector vect; + + int alive = 0; + scoped_refptr dummy(new Dummy(&alive)); + EXPECT_EQ(alive, 1); + + vect->push_back(dummy); + EXPECT_EQ(alive, 1); + + Dummy* dummy_unref = dummy.get(); + dummy = NULL; + EXPECT_EQ(alive, 1); + + Container::iterator itr = std::find(vect->begin(), vect->end(), dummy_unref); + EXPECT_EQ(itr->get(), dummy_unref); + vect->erase(itr); + EXPECT_EQ(alive, 0); + + // Shouldn't crash at exit. +} + +TEST(StackContainer, BufferAlignment) { + StackVector text; + text->push_back(L'A'); + text->push_back(L'B'); + text->push_back(L'C'); + text->push_back(L'D'); + text->push_back(L'E'); + text->push_back(L'F'); + text->push_back(0); + + const wchar_t* buffer = &text[1]; + bool even_aligned = (0 == (((size_t)buffer) & 0x1)); + EXPECT_EQ(even_aligned, true); +} + +#ifdef COMPILER_MSVC +// Make sure all the class compiles correctly. +// TODO(pinkerton): i'm not sure why this doesn't compile on GCC, but +// it doesn't. +template StackVector; +template StackVector, 2>; +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/stats_counters.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/stats_counters.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/stats_counters.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/stats_counters.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,257 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +#ifndef BASE_STATS_COUNTERS_H__ +#define BASE_STATS_COUNTERS_H__ + +#include +#include "base/stats_table.h" +#include "base/time.h" + +// StatsCounters are dynamically created values which can be tracked in +// the StatsTable. They are designed to be lightweight to create and +// easy to use. +// +// Since StatsCounters can be created dynamically by name, there is +// a hash table lookup to find the counter in the table. A StatsCounter +// object can be created once and used across multiple threads safely. +// +// Example usage: +// { +// StatsCounter request_count("RequestCount"); +// request_count.Increment(); +// } +// +// Note that creating counters on the stack does work, however creating +// the counter object requires a hash table lookup. For inner loops, it +// may be better to create the counter either as a member of another object +// (or otherwise outside of the loop) for maximum performance. +// +// Internally, a counter represents a value in a row of a StatsTable. +// The row has a 32bit value for each process/thread in the table and also +// a name (stored in the table metadata). +// +// NOTE: In order to make stats_counters usable in lots of different code, +// avoid any dependencies inside this header file. +// + +//------------------------------------------------------------------------------ +// Define macros for ease of use. They also allow us to change definitions +// as the implementation varies, or depending on compile options. +//------------------------------------------------------------------------------ +// First provide generic macros, which exist in production as well as debug. +#define STATS_COUNTER(name, delta) do { \ + static StatsCounter counter(name); \ + counter.Add(delta); \ +} while (0) + +#define SIMPLE_STATS_COUNTER(name) STATS_COUNTER(name, 1) + +#define RATE_COUNTER(name, duration) do { \ + static StatsRate hit_count(name); \ + hit_count.AddTime(duration); \ +} while (0) + +// Define Debug vs non-debug flavors of macros. +#ifndef NDEBUG + +#define DSTATS_COUNTER(name, delta) STATS_COUNTER(name, delta) +#define DSIMPLE_STATS_COUNTER(name) SIMPLE_STATS_COUNTER(name) +#define DRATE_COUNTER(name, duration) RATE_COUNTER(name, duration) + +#else // NDEBUG + +#define DSTATS_COUNTER(name, delta) do {} while (0) +#define DSIMPLE_STATS_COUNTER(name) do {} while (0) +#define DRATE_COUNTER(name, duration) do {} while (0) + +#endif // NDEBUG + +//------------------------------------------------------------------------------ +// StatsCounter represents a counter in the StatsTable class. +class StatsCounter { + public: + // Create a StatsCounter object. + explicit StatsCounter(const std::string& name) + : counter_id_(-1) { + // We prepend the name with 'c:' to indicate that it is a counter. + name_ = "c:"; + name_.append(name); + }; + + virtual ~StatsCounter() {} + + // Sets the counter to a specific value. + void Set(int value) { + int* loc = GetPtr(); + if (loc) *loc = value; + } + + // Increments the counter. + void Increment() { + Add(1); + } + + virtual void Add(int value) { + int* loc = GetPtr(); + if (loc) + (*loc) += value; + } + + // Decrements the counter. + void Decrement() { + Add(-1); + } + + void Subtract(int value) { + Add(-value); + } + + // Is this counter enabled? + // Returns false if table is full. + bool Enabled() { + return GetPtr() != NULL; + } + + int value() { + int* loc = GetPtr(); + if (loc) return *loc; + return 0; + } + + protected: + StatsCounter() + : counter_id_(-1) { + } + + // Returns the cached address of this counter location. + int* GetPtr() { + StatsTable* table = StatsTable::current(); + if (!table) + return NULL; + + // If counter_id_ is -1, then we haven't looked it up yet. + if (counter_id_ == -1) { + counter_id_ = table->FindCounter(name_); + if (table->GetSlot() == 0) { + if (!table->RegisterThread("")) { + // There is no room for this thread. This thread + // cannot use counters. + counter_id_ = 0; + return NULL; + } + } + } + + // If counter_id_ is > 0, then we have a valid counter. + if (counter_id_ > 0) + return table->GetLocation(counter_id_, table->GetSlot()); + + // counter_id_ was zero, which means the table is full. + return NULL; + } + + std::string name_; + // The counter id in the table. We initialize to -1 (an invalid value) + // and then cache it once it has been looked up. The counter_id is + // valid across all threads and processes. + int32 counter_id_; +}; + + +// A StatsCounterTimer is a StatsCounter which keeps a timer during +// the scope of the StatsCounterTimer. On destruction, it will record +// its time measurement. +class StatsCounterTimer : protected StatsCounter { + public: + // Constructs and starts the timer. + explicit StatsCounterTimer(const std::string& name) { + // we prepend the name with 't:' to indicate that it is a timer. + name_ = "t:"; + name_.append(name); + } + + // Start the timer. + void Start() { + if (!Enabled()) + return; + start_time_ = base::TimeTicks::Now(); + stop_time_ = base::TimeTicks(); + } + + // Stop the timer and record the results. + void Stop() { + if (!Enabled() || !Running()) + return; + stop_time_ = base::TimeTicks::Now(); + Record(); + } + + // Returns true if the timer is running. + bool Running() { + return Enabled() && !start_time_.is_null() && stop_time_.is_null(); + } + + // Accept a TimeDelta to increment. + virtual void AddTime(base::TimeDelta time) { + Add(static_cast(time.InMilliseconds())); + } + + protected: + // Compute the delta between start and stop, in milliseconds. + void Record() { + AddTime(stop_time_ - start_time_); + } + + base::TimeTicks start_time_; + base::TimeTicks stop_time_; +}; + +// A StatsRate is a timer that keeps a count of the number of intervals added so +// that several statistics can be produced: +// min, max, avg, count, total +class StatsRate : public StatsCounterTimer { + public: + // Constructs and starts the timer. + explicit StatsRate(const char* name) + : StatsCounterTimer(name), + counter_(name), + largest_add_(std::string(" ").append(name).append("MAX").c_str()) { + } + + virtual void Add(int value) { + counter_.Increment(); + StatsCounterTimer::Add(value); + if (value > largest_add_.value()) + largest_add_.Set(value); + } + + private: + StatsCounter counter_; + StatsCounter largest_add_; +}; + + +// Helper class for scoping a timer or rate. +template class StatsScope { + public: + explicit StatsScope(T& timer) + : timer_(timer) { + timer_.Start(); + } + + ~StatsScope() { + timer_.Stop(); + } + + void Stop() { + timer_.Stop(); + } + + private: + T& timer_; +}; + +#endif // BASE_STATS_COUNTERS_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/stats_table.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/stats_table.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/stats_table.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/stats_table.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,564 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/stats_table.h" + +#include "base/logging.h" +#include "base/platform_thread.h" +#include "base/process_util.h" +#include "base/scoped_ptr.h" +#include "base/shared_memory.h" +#include "base/string_piece.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" +#include "base/thread_local_storage.h" + +#if defined(OS_POSIX) +#include "errno.h" +#endif + +// The StatsTable uses a shared memory segment that is laid out as follows +// +// +-------------------------------------------+ +// | Version | Size | MaxCounters | MaxThreads | +// +-------------------------------------------+ +// | Thread names table | +// +-------------------------------------------+ +// | Thread TID table | +// +-------------------------------------------+ +// | Thread PID table | +// +-------------------------------------------+ +// | Counter names table | +// +-------------------------------------------+ +// | Data | +// +-------------------------------------------+ +// +// The data layout is a grid, where the columns are the thread_ids and the +// rows are the counter_ids. +// +// If the first character of the thread_name is '\0', then that column is +// empty. +// If the first character of the counter_name is '\0', then that row is +// empty. +// +// About Locking: +// This class is designed to be both multi-thread and multi-process safe. +// Aside from initialization, this is done by partitioning the data which +// each thread uses so that no locking is required. However, to allocate +// the rows and columns of the table to particular threads, locking is +// required. +// +// At the shared-memory level, we have a lock. This lock protects the +// shared-memory table only, and is used when we create new counters (e.g. +// use rows) or when we register new threads (e.g. use columns). Reading +// data from the table does not require any locking at the shared memory +// level. +// +// Each process which accesses the table will create a StatsTable object. +// The StatsTable maintains a hash table of the existing counters in the +// table for faster lookup. Since the hash table is process specific, +// each process maintains its own cache. We avoid complexity here by never +// de-allocating from the hash table. (Counters are dynamically added, +// but not dynamically removed). + +// In order for external viewers to be able to read our shared memory, +// we all need to use the same size ints. +COMPILE_ASSERT(sizeof(int)==4, expect_4_byte_ints); + +namespace { + +// An internal version in case we ever change the format of this +// file, and so that we can identify our table. +const int kTableVersion = 0x13131313; + +// The name for un-named counters and threads in the table. +const char kUnknownName[] = ""; + +// Calculates delta to align an offset to the size of an int +inline int AlignOffset(int offset) { + return (sizeof(int) - (offset % sizeof(int))) % sizeof(int); +} + +inline int AlignedSize(int size) { + return size + AlignOffset(size); +} + +// StatsTableTLSData carries the data stored in the TLS slots for the +// StatsTable. This is used so that we can properly cleanup when the +// thread exits and return the table slot. +// +// Each thread that calls RegisterThread in the StatsTable will have +// a StatsTableTLSData stored in its TLS. +struct StatsTableTLSData { + StatsTable* table; + int slot; +}; + +} // namespace + +// The StatsTablePrivate maintains convenience pointers into the +// shared memory segment. Use this class to keep the data structure +// clean and accessible. +class StatsTablePrivate { + public: + // Various header information contained in the memory mapped segment. + struct TableHeader { + int version; + int size; + int max_counters; + int max_threads; + }; + + // Construct a new StatsTablePrivate based on expected size parameters, or + // return NULL on failure. + static StatsTablePrivate* New(const std::string& name, int size, + int max_threads, int max_counters); + + base::SharedMemory* shared_memory() { return &shared_memory_; } + + // Accessors for our header pointers + TableHeader* table_header() const { return table_header_; } + int version() const { return table_header_->version; } + int size() const { return table_header_->size; } + int max_counters() const { return table_header_->max_counters; } + int max_threads() const { return table_header_->max_threads; } + + // Accessors for our tables + char* thread_name(int slot_id) const { + return &thread_names_table_[ + (slot_id-1) * (StatsTable::kMaxThreadNameLength)]; + } + PlatformThreadId* thread_tid(int slot_id) const { + return &(thread_tid_table_[slot_id-1]); + } + int* thread_pid(int slot_id) const { + return &(thread_pid_table_[slot_id-1]); + } + char* counter_name(int counter_id) const { + return &counter_names_table_[ + (counter_id-1) * (StatsTable::kMaxCounterNameLength)]; + } + int* row(int counter_id) const { + return &data_table_[(counter_id-1) * max_threads()]; + } + + private: + // Constructor is private because you should use New() instead. + StatsTablePrivate() {} + + // Initializes the table on first access. Sets header values + // appropriately and zeroes all counters. + void InitializeTable(void* memory, int size, int max_counters, + int max_threads); + + // Initializes our in-memory pointers into a pre-created StatsTable. + void ComputeMappedPointers(void* memory); + + base::SharedMemory shared_memory_; + TableHeader* table_header_; + char* thread_names_table_; + PlatformThreadId* thread_tid_table_; + int* thread_pid_table_; + char* counter_names_table_; + int* data_table_; +}; + +// static +StatsTablePrivate* StatsTablePrivate::New(const std::string& name, + int size, + int max_threads, + int max_counters) { + scoped_ptr priv(new StatsTablePrivate()); +#ifdef CHROMIUM_MOZILLA_BUILD + if (!priv->shared_memory_.Create(name, false, true, +#else + if (!priv->shared_memory_.Create(base::SysUTF8ToWide(name), false, true, +#endif + size)) + return NULL; + if (!priv->shared_memory_.Map(size)) + return NULL; + void* memory = priv->shared_memory_.memory(); + + TableHeader* header = static_cast(memory); + + // If the version does not match, then assume the table needs + // to be initialized. + if (header->version != kTableVersion) + priv->InitializeTable(memory, size, max_counters, max_threads); + + // We have a valid table, so compute our pointers. + priv->ComputeMappedPointers(memory); + + return priv.release(); +} + +void StatsTablePrivate::InitializeTable(void* memory, int size, + int max_counters, + int max_threads) { + // Zero everything. + memset(memory, 0, size); + + // Initialize the header. + TableHeader* header = static_cast(memory); + header->version = kTableVersion; + header->size = size; + header->max_counters = max_counters; + header->max_threads = max_threads; +} + +void StatsTablePrivate::ComputeMappedPointers(void* memory) { + char* data = static_cast(memory); + int offset = 0; + + table_header_ = reinterpret_cast(data); + offset += sizeof(*table_header_); + offset += AlignOffset(offset); + + // Verify we're looking at a valid StatsTable. + DCHECK_EQ(table_header_->version, kTableVersion); + + thread_names_table_ = reinterpret_cast(data + offset); + offset += sizeof(char) * + max_threads() * StatsTable::kMaxThreadNameLength; + offset += AlignOffset(offset); + + thread_tid_table_ = reinterpret_cast(data + offset); + offset += sizeof(int) * max_threads(); + offset += AlignOffset(offset); + + thread_pid_table_ = reinterpret_cast(data + offset); + offset += sizeof(int) * max_threads(); + offset += AlignOffset(offset); + + counter_names_table_ = reinterpret_cast(data + offset); + offset += sizeof(char) * + max_counters() * StatsTable::kMaxCounterNameLength; + offset += AlignOffset(offset); + + data_table_ = reinterpret_cast(data + offset); + offset += sizeof(int) * max_threads() * max_counters(); + + DCHECK_EQ(offset, size()); +} + + + +// We keep a singleton table which can be easily accessed. +StatsTable* StatsTable::global_table_ = NULL; + +StatsTable::StatsTable(const std::string& name, int max_threads, + int max_counters) + : impl_(NULL), + tls_index_(SlotReturnFunction) { + int table_size = + AlignedSize(sizeof(StatsTablePrivate::TableHeader)) + + AlignedSize((max_counters * sizeof(char) * kMaxCounterNameLength)) + + AlignedSize((max_threads * sizeof(char) * kMaxThreadNameLength)) + + AlignedSize(max_threads * sizeof(int)) + + AlignedSize(max_threads * sizeof(int)) + + AlignedSize((sizeof(int) * (max_counters * max_threads))); + + impl_ = StatsTablePrivate::New(name, table_size, max_threads, max_counters); + + // TODO(port): clean up this error reporting. +#if defined(OS_WIN) + if (!impl_) + LOG(ERROR) << "StatsTable did not initialize:" << GetLastError(); +#elif defined(OS_POSIX) + if (!impl_) + LOG(ERROR) << "StatsTable did not initialize:" << strerror(errno); +#endif +} + +StatsTable::~StatsTable() { + // Before we tear down our copy of the table, be sure to + // unregister our thread. + UnregisterThread(); + + // Return ThreadLocalStorage. At this point, if any registered threads + // still exist, they cannot Unregister. + tls_index_.Free(); + + // Cleanup our shared memory. + delete impl_; + + // If we are the global table, unregister ourselves. + if (global_table_ == this) + global_table_ = NULL; +} + +int StatsTable::RegisterThread(const std::string& name) { + int slot = 0; + + // Registering a thread requires that we lock the shared memory + // so that two threads don't grab the same slot. Fortunately, + // thread creation shouldn't happen in inner loops. + { + base::SharedMemoryAutoLock lock(impl_->shared_memory()); + slot = FindEmptyThread(); + if (!slot) { + return 0; + } + + DCHECK(impl_); + + // We have space, so consume a column in the table. + std::string thread_name = name; + if (name.empty()) + thread_name = kUnknownName; + base::strlcpy(impl_->thread_name(slot), thread_name.c_str(), + kMaxThreadNameLength); + *(impl_->thread_tid(slot)) = PlatformThread::CurrentId(); + *(impl_->thread_pid(slot)) = base::GetCurrentProcId(); + } + + // Set our thread local storage. + StatsTableTLSData* data = new StatsTableTLSData; + data->table = this; + data->slot = slot; + tls_index_.Set(data); + return slot; +} + +StatsTableTLSData* StatsTable::GetTLSData() const { + StatsTableTLSData* data = + static_cast(tls_index_.Get()); + if (!data) + return NULL; + + DCHECK(data->slot); + DCHECK_EQ(data->table, this); + return data; +} + +void StatsTable::UnregisterThread() { + UnregisterThread(GetTLSData()); +} + +void StatsTable::UnregisterThread(StatsTableTLSData* data) { + if (!data) + return; + DCHECK(impl_); + + // Mark the slot free by zeroing out the thread name. + char* name = impl_->thread_name(data->slot); + *name = '\0'; + + // Remove the calling thread's TLS so that it cannot use the slot. + tls_index_.Set(NULL); + delete data; +} + +void StatsTable::SlotReturnFunction(void* data) { + // This is called by the TLS destructor, which on some platforms has + // already cleared the TLS info, so use the tls_data argument + // rather than trying to fetch it ourselves. + StatsTableTLSData* tls_data = static_cast(data); + if (tls_data) { + DCHECK(tls_data->table); + tls_data->table->UnregisterThread(tls_data); + } +} + +int StatsTable::CountThreadsRegistered() const { + if (!impl_) + return 0; + + // Loop through the shared memory and count the threads that are active. + // We intentionally do not lock the table during the operation. + int count = 0; + for (int index = 1; index <= impl_->max_threads(); index++) { + char* name = impl_->thread_name(index); + if (*name != '\0') + count++; + } + return count; +} + +int StatsTable::GetSlot() const { + StatsTableTLSData* data = GetTLSData(); + if (!data) + return 0; + return data->slot; +} + +int StatsTable::FindEmptyThread() const { + // Note: the API returns slots numbered from 1..N, although + // internally, the array is 0..N-1. This is so that we can return + // zero as "not found". + // + // The reason for doing this is because the thread 'slot' is stored + // in TLS, which is always initialized to zero, not -1. If 0 were + // returned as a valid slot number, it would be confused with the + // uninitialized state. + if (!impl_) + return 0; + + int index = 1; + for (; index <= impl_->max_threads(); index++) { + char* name = impl_->thread_name(index); + if (!*name) + break; + } + if (index > impl_->max_threads()) + return 0; // The table is full. + return index; +} + +int StatsTable::FindCounterOrEmptyRow(const std::string& name) const { + // Note: the API returns slots numbered from 1..N, although + // internally, the array is 0..N-1. This is so that we can return + // zero as "not found". + // + // There isn't much reason for this other than to be consistent + // with the way we track columns for thread slots. (See comments + // in FindEmptyThread for why it is done this way). + if (!impl_) + return 0; + + int free_slot = 0; + for (int index = 1; index <= impl_->max_counters(); index++) { + char* row_name = impl_->counter_name(index); + if (!*row_name && !free_slot) + free_slot = index; // save that we found a free slot + else if (!strncmp(row_name, name.c_str(), kMaxCounterNameLength)) + return index; + } + return free_slot; +} + +int StatsTable::FindCounter(const std::string& name) { + // Note: the API returns counters numbered from 1..N, although + // internally, the array is 0..N-1. This is so that we can return + // zero as "not found". + if (!impl_) + return 0; + + // Create a scope for our auto-lock. + { + AutoLock scoped_lock(counters_lock_); + + // Attempt to find the counter. + CountersMap::const_iterator iter; + iter = counters_.find(name); + if (iter != counters_.end()) + return iter->second; + } + + // Counter does not exist, so add it. + return AddCounter(name); +} + +int StatsTable::AddCounter(const std::string& name) { + DCHECK(impl_); + + if (!impl_) + return 0; + + int counter_id = 0; + { + // To add a counter to the shared memory, we need the + // shared memory lock. + base::SharedMemoryAutoLock lock(impl_->shared_memory()); + + // We have space, so create a new counter. + counter_id = FindCounterOrEmptyRow(name); + if (!counter_id) + return 0; + + std::string counter_name = name; + if (name.empty()) + counter_name = kUnknownName; + base::strlcpy(impl_->counter_name(counter_id), counter_name.c_str(), + kMaxCounterNameLength); + } + + // now add to our in-memory cache + { + AutoLock lock(counters_lock_); + counters_[name] = counter_id; + } + return counter_id; +} + +int* StatsTable::GetLocation(int counter_id, int slot_id) const { + if (!impl_) + return NULL; + if (slot_id > impl_->max_threads()) + return NULL; + + int* row = impl_->row(counter_id); + return &(row[slot_id-1]); +} + +const char* StatsTable::GetRowName(int index) const { + if (!impl_) + return NULL; + + return impl_->counter_name(index); +} + +int StatsTable::GetRowValue(int index, int pid) const { + if (!impl_) + return 0; + + int rv = 0; + int* row = impl_->row(index); + for (int slot_id = 0; slot_id < impl_->max_threads(); slot_id++) { + if (pid == 0 || *impl_->thread_pid(slot_id) == pid) + rv += row[slot_id]; + } + return rv; +} + +int StatsTable::GetRowValue(int index) const { + return GetRowValue(index, 0); +} + +int StatsTable::GetCounterValue(const std::string& name, int pid) { + if (!impl_) + return 0; + + int row = FindCounter(name); + if (!row) + return 0; + return GetRowValue(row, pid); +} + +int StatsTable::GetCounterValue(const std::string& name) { + return GetCounterValue(name, 0); +} + +int StatsTable::GetMaxCounters() const { + if (!impl_) + return 0; + return impl_->max_counters(); +} + +int StatsTable::GetMaxThreads() const { + if (!impl_) + return 0; + return impl_->max_threads(); +} + +int* StatsTable::FindLocation(const char* name) { + // Get the static StatsTable + StatsTable *table = StatsTable::current(); + if (!table) + return NULL; + + // Get the slot for this thread. Try to register + // it if none exists. + int slot = table->GetSlot(); + if (!slot && !(slot = table->RegisterThread(""))) + return NULL; + + // Find the counter id for the counter. + std::string str_name(name); + int counter = table->FindCounter(str_name); + + // Now we can find the location in the table. + return table->GetLocation(counter, slot); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/stats_table.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/stats_table.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/stats_table.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/stats_table.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,191 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// A StatsTable is a table of statistics. It can be used across multiple +// processes and threads, maintaining cheap statistics counters without +// locking. +// +// The goal is to make it very cheap and easy for developers to add +// counters to code, without having to build one-off utilities or mechanisms +// to track the counters, and also to allow a single "view" to display +// the contents of all counters. +// +// To achieve this, StatsTable creates a shared memory segment to store +// the data for the counters. Upon creation, it has a specific size +// which governs the maximum number of counters and concurrent +// threads/processes which can use it. +// + +#ifndef BASE_STATS_TABLE_H__ +#define BASE_STATS_TABLE_H__ + +#include +#include "base/basictypes.h" +#include "base/hash_tables.h" +#include "base/lock.h" +#include "base/thread_local_storage.h" + +class StatsTablePrivate; + +namespace { +struct StatsTableTLSData; +} + +class StatsTable { + public: + // Create a new StatsTable. + // If a StatsTable already exists with the specified name, this StatsTable + // will use the same shared memory segment as the original. Otherwise, + // a new StatsTable is created and all counters are zeroed. + // + // name is the name of the StatsTable to use. + // + // max_threads is the maximum number of threads the table will support. + // If the StatsTable already exists, this number is ignored. + // + // max_counters is the maximum number of counters the table will support. + // If the StatsTable already exists, this number is ignored. + StatsTable(const std::string& name, int max_threads, int max_counters); + + // Destroys the StatsTable. When the last StatsTable is destroyed + // (across all processes), the StatsTable is removed from disk. + ~StatsTable(); + + // For convenience, we create a static table. This is generally + // used automatically by the counters. + static StatsTable* current() { return global_table_; } + + // Set the global table for use in this process. + static void set_current(StatsTable* value) { global_table_ = value; } + + // Get the slot id for the calling thread. Returns 0 if no + // slot is assigned. + int GetSlot() const; + + // All threads that contribute data to the table must register with the + // table first. This function will set thread local storage for the + // thread containing the location in the table where this thread will + // write its counter data. + // + // name is just a debugging tag to label the thread, and it does not + // need to be unique. It will be truncated to kMaxThreadNameLength-1 + // characters. + // + // On success, returns the slot id for this thread. On failure, + // returns 0. + int RegisterThread(const std::string& name); + + // Returns the number of threads currently registered. This is really not + // useful except for diagnostics and debugging. + int CountThreadsRegistered() const; + + // Find a counter in the StatsTable. + // + // Returns an id for the counter which can be used to call GetLocation(). + // If the counter does not exist, attempts to create a row for the new + // counter. If there is no space in the table for the new counter, + // returns 0. + int FindCounter(const std::string& name); + + // TODO(mbelshe): implement RemoveCounter. + + // Gets the location of a particular value in the table based on + // the counter id and slot id. + int* GetLocation(int counter_id, int slot_id) const; + + // Gets the counter name at a particular row. If the row is empty, + // returns NULL. + const char* GetRowName(int index) const; + + // Gets the sum of the values for a particular row. + int GetRowValue(int index) const; + + // Gets the sum of the values for a particular row for a given pid. + int GetRowValue(int index, int pid) const; + + // Gets the sum of the values for a particular counter. If the counter + // does not exist, creates the counter. + int GetCounterValue(const std::string& name); + + // Gets the sum of the values for a particular counter for a given pid. + // If the counter does not exist, creates the counter. + int GetCounterValue(const std::string& name, int pid); + + // The maxinum number of counters/rows in the table. + int GetMaxCounters() const; + + // The maxinum number of threads/columns in the table. + int GetMaxThreads() const; + + // The maximum length (in characters) of a Thread's name including + // null terminator, as stored in the shared memory. + static const int kMaxThreadNameLength = 32; + + // The maximum length (in characters) of a Counter's name including + // null terminator, as stored in the shared memory. + static const int kMaxCounterNameLength = 32; + + // Convenience function to lookup a counter location for a + // counter by name for the calling thread. Will register + // the thread if it is not already registered. + static int* FindLocation(const char *name); + + private: + // Returns the space occupied by a thread in the table. Generally used + // if a thread terminates but the process continues. This function + // does not zero out the thread's counters. + // Cannot be used inside a posix tls destructor. + void UnregisterThread(); + + // This variant expects the tls data to be passed in, so it is safe to + // call from inside a posix tls destructor (see doc for pthread_key_create). + void UnregisterThread(StatsTableTLSData* tls_data); + + // The SlotReturnFunction is called at thread exit for each thread + // which used the StatsTable. + static void SlotReturnFunction(void* data); + + // Locates a free slot in the table. Returns a number > 0 on success, + // or 0 on failure. The caller must hold the shared_memory lock when + // calling this function. + int FindEmptyThread() const; + + // Locates a counter in the table or finds an empty row. Returns a + // number > 0 on success, or 0 on failure. The caller must hold the + // shared_memory_lock when calling this function. + int FindCounterOrEmptyRow(const std::string& name) const; + + // Internal function to add a counter to the StatsTable. Assumes that + // the counter does not already exist in the table. + // + // name is a unique identifier for this counter, and will be truncated + // to kMaxCounterNameLength-1 characters. + // + // On success, returns the counter_id for the newly added counter. + // On failure, returns 0. + int AddCounter(const std::string& name); + + // Get the TLS data for the calling thread. Returns NULL if none is + // initialized. + StatsTableTLSData* GetTLSData() const; + + typedef base::hash_map CountersMap; + + StatsTablePrivate* impl_; + // The counters_lock_ protects the counters_ hash table. + Lock counters_lock_; + // The counters_ hash map is an in-memory hash of the counters. + // It is used for quick lookup of counters, but is cannot be used + // as a substitute for what is in the shared memory. Even though + // we don't have a counter in our hash table, another process may + // have created it. + CountersMap counters_; + TLSSlot tls_index_; + + static StatsTable* global_table_; + + DISALLOW_EVIL_CONSTRUCTORS(StatsTable); +}; + +#endif // BASE_STATS_TABLE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/stats_table_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/stats_table_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/stats_table_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/stats_table_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,407 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/multiprocess_test.h" +#include "base/platform_thread.h" +#include "base/simple_thread.h" +#include "base/shared_memory.h" +#include "base/stats_table.h" +#include "base/stats_counters.h" +#include "base/string_util.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/multiprocess_func_list.h" + +#if defined(OS_WIN) +#include +#include +#endif + +namespace base { + +class StatsTableTest : public MultiProcessTest { + public: + void DeleteShmem(std::string name) { + base::SharedMemory mem; + mem.Delete(UTF8ToWide(name)); + } +}; + +// Open a StatsTable and verify that we can write to each of the +// locations in the table. +TEST_F(StatsTableTest, VerifySlots) { + const std::string kTableName = "VerifySlotsStatTable"; + const int kMaxThreads = 1; + const int kMaxCounter = 5; + DeleteShmem(kTableName); + StatsTable table(kTableName, kMaxThreads, kMaxCounter); + + // Register a single thread. + std::string thread_name = "mainThread"; + int slot_id = table.RegisterThread(thread_name); + EXPECT_TRUE(slot_id); + + // Fill up the table with counters. + std::string counter_base_name = "counter"; + for (int index=0; index < kMaxCounter; index++) { + std::string counter_name = counter_base_name; + StringAppendF(&counter_name, "counter.ctr%d", index); + int counter_id = table.FindCounter(counter_name); + EXPECT_GT(counter_id, 0); + } + + // Try to allocate an additional thread. Verify it fails. + slot_id = table.RegisterThread("too many threads"); + EXPECT_EQ(slot_id, 0); + + // Try to allocate an additional counter. Verify it fails. + int counter_id = table.FindCounter(counter_base_name); + EXPECT_EQ(counter_id, 0); + + DeleteShmem(kTableName); +} + +// CounterZero will continually be set to 0. +const std::string kCounterZero = "CounterZero"; +// Counter1313 will continually be set to 1313. +const std::string kCounter1313 = "Counter1313"; +// CounterIncrement will be incremented each time. +const std::string kCounterIncrement = "CounterIncrement"; +// CounterDecrement will be decremented each time. +const std::string kCounterDecrement = "CounterDecrement"; +// CounterMixed will be incremented by odd numbered threads and +// decremented by even threads. +const std::string kCounterMixed = "CounterMixed"; +// The number of thread loops that we will do. +const int kThreadLoops = 1000; + +class StatsTableThread : public base::SimpleThread { +public: + StatsTableThread(std::string name, int id) + : base::SimpleThread(name), id_(id) { } + virtual void Run(); +private: + int id_; +}; + +void StatsTableThread::Run() { + // Each thread will open the shared memory and set counters + // concurrently in a loop. We'll use some pauses to + // mixup the thread scheduling. + + StatsCounter zero_counter(kCounterZero); + StatsCounter lucky13_counter(kCounter1313); + StatsCounter increment_counter(kCounterIncrement); + StatsCounter decrement_counter(kCounterDecrement); + for (int index = 0; index < kThreadLoops; index++) { + StatsCounter mixed_counter(kCounterMixed); // create this one in the loop + zero_counter.Set(0); + lucky13_counter.Set(1313); + increment_counter.Increment(); + decrement_counter.Decrement(); + if (id_ % 2) + mixed_counter.Decrement(); + else + mixed_counter.Increment(); + PlatformThread::Sleep(index % 10); // short wait + } +} + +// Create a few threads and have them poke on their counters. +// Currently disabled. See bug report below: +// TODO(maruel): http://crbug.com/10611 +TEST_F(StatsTableTest, MultipleThreads) { +#if 0 + // Create a stats table. + const std::string kTableName = "MultipleThreadStatTable"; + const int kMaxThreads = 20; + const int kMaxCounter = 5; + DeleteShmem(kTableName); + StatsTable table(kTableName, kMaxThreads, kMaxCounter); + StatsTable::set_current(&table); + + EXPECT_EQ(0, table.CountThreadsRegistered()); + + // Spin up a set of threads to go bang on the various counters. + // After we join the threads, we'll make sure the counters + // contain the values we expected. + StatsTableThread* threads[kMaxThreads]; + + // Spawn the threads. + for (int index = 0; index < kMaxThreads; index++) { + threads[index] = new StatsTableThread("MultipleThreadsTest", index); + threads[index]->Start(); + } + + // Wait for the threads to finish. + for (int index = 0; index < kMaxThreads; index++) { + threads[index]->Join(); + delete threads[index]; + } + + StatsCounter zero_counter(kCounterZero); + StatsCounter lucky13_counter(kCounter1313); + StatsCounter increment_counter(kCounterIncrement); + StatsCounter decrement_counter(kCounterDecrement); + StatsCounter mixed_counter(kCounterMixed); + + // Verify the various counters are correct. + std::string name; + name = "c:" + kCounterZero; + EXPECT_EQ(0, table.GetCounterValue(name)); + name = "c:" + kCounter1313; + EXPECT_EQ(1313 * kMaxThreads, + table.GetCounterValue(name)); + name = "c:" + kCounterIncrement; + EXPECT_EQ(kMaxThreads * kThreadLoops, + table.GetCounterValue(name)); + name = "c:" + kCounterDecrement; + EXPECT_EQ(-kMaxThreads * kThreadLoops, + table.GetCounterValue(name)); + name = "c:" + kCounterMixed; + EXPECT_EQ((kMaxThreads % 2) * kThreadLoops, + table.GetCounterValue(name)); + EXPECT_EQ(0, table.CountThreadsRegistered()); + + DeleteShmem(kTableName); +#endif +} + +const std::string kMPTableName = "MultipleProcessStatTable"; + +MULTIPROCESS_TEST_MAIN(StatsTableMultipleProcessMain) { + // Each process will open the shared memory and set counters + // concurrently in a loop. We'll use some pauses to + // mixup the scheduling. + + StatsTable table(kMPTableName, 0, 0); + StatsTable::set_current(&table); + StatsCounter zero_counter(kCounterZero); + StatsCounter lucky13_counter(kCounter1313); + StatsCounter increment_counter(kCounterIncrement); + StatsCounter decrement_counter(kCounterDecrement); + for (int index = 0; index < kThreadLoops; index++) { + zero_counter.Set(0); + lucky13_counter.Set(1313); + increment_counter.Increment(); + decrement_counter.Decrement(); + PlatformThread::Sleep(index % 10); // short wait + } + return 0; +} + +// Create a few processes and have them poke on their counters. +TEST_F(StatsTableTest, MultipleProcesses) { + // Create a stats table. + const int kMaxProcs = 20; + const int kMaxCounter = 5; + DeleteShmem(kMPTableName); + StatsTable table(kMPTableName, kMaxProcs, kMaxCounter); + StatsTable::set_current(&table); + EXPECT_EQ(0, table.CountThreadsRegistered()); + + // Spin up a set of processes to go bang on the various counters. + // After we join the processes, we'll make sure the counters + // contain the values we expected. + ProcessHandle procs[kMaxProcs]; + + // Spawn the processes. + for (int16 index = 0; index < kMaxProcs; index++) { + procs[index] = this->SpawnChild(L"StatsTableMultipleProcessMain"); + EXPECT_NE(static_cast(NULL), procs[index]); + } + + // Wait for the processes to finish. + for (int index = 0; index < kMaxProcs; index++) { + EXPECT_TRUE(WaitForSingleProcess(procs[index], 60 * 1000)); + base::CloseProcessHandle(procs[index]); + } + + StatsCounter zero_counter(kCounterZero); + StatsCounter lucky13_counter(kCounter1313); + StatsCounter increment_counter(kCounterIncrement); + StatsCounter decrement_counter(kCounterDecrement); + + // Verify the various counters are correct. + std::string name; + name = "c:" + kCounterZero; + EXPECT_EQ(0, table.GetCounterValue(name)); + name = "c:" + kCounter1313; + EXPECT_EQ(1313 * kMaxProcs, + table.GetCounterValue(name)); + name = "c:" + kCounterIncrement; + EXPECT_EQ(kMaxProcs * kThreadLoops, + table.GetCounterValue(name)); + name = "c:" + kCounterDecrement; + EXPECT_EQ(-kMaxProcs * kThreadLoops, + table.GetCounterValue(name)); + EXPECT_EQ(0, table.CountThreadsRegistered()); + + DeleteShmem(kMPTableName); +} + +class MockStatsCounter : public StatsCounter { + public: + MockStatsCounter(const std::string& name) + : StatsCounter(name) {} + int* Pointer() { return GetPtr(); } +}; + +// Test some basic StatsCounter operations +TEST_F(StatsTableTest, StatsCounter) { + // Create a stats table. + const std::string kTableName = "StatTable"; + const int kMaxThreads = 20; + const int kMaxCounter = 5; + DeleteShmem(kTableName); + StatsTable table(kTableName, kMaxThreads, kMaxCounter); + StatsTable::set_current(&table); + + MockStatsCounter foo("foo"); + + // Test initial state. + EXPECT_TRUE(foo.Enabled()); + EXPECT_NE(foo.Pointer(), static_cast(0)); + EXPECT_EQ(0, table.GetCounterValue("c:foo")); + EXPECT_EQ(0, *(foo.Pointer())); + + // Test Increment. + while(*(foo.Pointer()) < 123) foo.Increment(); + EXPECT_EQ(123, table.GetCounterValue("c:foo")); + foo.Add(0); + EXPECT_EQ(123, table.GetCounterValue("c:foo")); + foo.Add(-1); + EXPECT_EQ(122, table.GetCounterValue("c:foo")); + + // Test Set. + foo.Set(0); + EXPECT_EQ(0, table.GetCounterValue("c:foo")); + foo.Set(100); + EXPECT_EQ(100, table.GetCounterValue("c:foo")); + foo.Set(-1); + EXPECT_EQ(-1, table.GetCounterValue("c:foo")); + foo.Set(0); + EXPECT_EQ(0, table.GetCounterValue("c:foo")); + + // Test Decrement. + foo.Subtract(1); + EXPECT_EQ(-1, table.GetCounterValue("c:foo")); + foo.Subtract(0); + EXPECT_EQ(-1, table.GetCounterValue("c:foo")); + foo.Subtract(-1); + EXPECT_EQ(0, table.GetCounterValue("c:foo")); + + DeleteShmem(kTableName); +} + +class MockStatsCounterTimer : public StatsCounterTimer { + public: + MockStatsCounterTimer(const std::string& name) + : StatsCounterTimer(name) {} + + TimeTicks start_time() { return start_time_; } + TimeTicks stop_time() { return stop_time_; } +}; + +// Test some basic StatsCounterTimer operations +TEST_F(StatsTableTest, StatsCounterTimer) { + // Create a stats table. + const std::string kTableName = "StatTable"; + const int kMaxThreads = 20; + const int kMaxCounter = 5; + StatsTable table(kTableName, kMaxThreads, kMaxCounter); + StatsTable::set_current(&table); + + MockStatsCounterTimer bar("bar"); + + // Test initial state. + EXPECT_FALSE(bar.Running()); + EXPECT_TRUE(bar.start_time().is_null()); + EXPECT_TRUE(bar.stop_time().is_null()); + + // Do some timing. + bar.Start(); + PlatformThread::Sleep(500); + bar.Stop(); + EXPECT_LE(500, table.GetCounterValue("t:bar")); + + // Verify that timing again is additive. + bar.Start(); + PlatformThread::Sleep(500); + bar.Stop(); + EXPECT_LE(1000, table.GetCounterValue("t:bar")); +} + +// Test some basic StatsRate operations +TEST_F(StatsTableTest, StatsRate) { + // Create a stats table. + const std::string kTableName = "StatTable"; + const int kMaxThreads = 20; + const int kMaxCounter = 5; + StatsTable table(kTableName, kMaxThreads, kMaxCounter); + StatsTable::set_current(&table); + + StatsRate baz("baz"); + + // Test initial state. + EXPECT_FALSE(baz.Running()); + EXPECT_EQ(0, table.GetCounterValue("c:baz")); + EXPECT_EQ(0, table.GetCounterValue("t:baz")); + + // Do some timing. + baz.Start(); + PlatformThread::Sleep(500); + baz.Stop(); + EXPECT_EQ(1, table.GetCounterValue("c:baz")); + EXPECT_LE(500, table.GetCounterValue("t:baz")); + + // Verify that timing again is additive. + baz.Start(); + PlatformThread::Sleep(500); + baz.Stop(); + EXPECT_EQ(2, table.GetCounterValue("c:baz")); + EXPECT_LE(1000, table.GetCounterValue("t:baz")); +} + +// Test some basic StatsScope operations +TEST_F(StatsTableTest, StatsScope) { + // Create a stats table. + const std::string kTableName = "StatTable"; + const int kMaxThreads = 20; + const int kMaxCounter = 5; + DeleteShmem(kTableName); + StatsTable table(kTableName, kMaxThreads, kMaxCounter); + StatsTable::set_current(&table); + + StatsCounterTimer foo("foo"); + StatsRate bar("bar"); + + // Test initial state. + EXPECT_EQ(0, table.GetCounterValue("t:foo")); + EXPECT_EQ(0, table.GetCounterValue("t:bar")); + EXPECT_EQ(0, table.GetCounterValue("c:bar")); + + // Try a scope. + { + StatsScope timer(foo); + StatsScope timer2(bar); + PlatformThread::Sleep(500); + } + EXPECT_LE(500, table.GetCounterValue("t:foo")); + EXPECT_LE(500, table.GetCounterValue("t:bar")); + EXPECT_EQ(1, table.GetCounterValue("c:bar")); + + // Try a second scope. + { + StatsScope timer(foo); + StatsScope timer2(bar); + PlatformThread::Sleep(500); + } + EXPECT_LE(1000, table.GetCounterValue("t:foo")); + EXPECT_LE(1000, table.GetCounterValue("t:bar")); + EXPECT_EQ(2, table.GetCounterValue("c:bar")); + + DeleteShmem(kTableName); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/stl_util-inl.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/stl_util-inl.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/stl_util-inl.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/stl_util-inl.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,450 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// STL utility functions. Usually, these replace built-in, but slow(!), +// STL functions with more efficient versions. + +#ifndef BASE_STL_UTIL_INL_H_ +#define BASE_STL_UTIL_INL_H_ + +#include // for memcpy +#include +#include +#include +#include +#include + +// Clear internal memory of an STL object. +// STL clear()/reserve(0) does not always free internal memory allocated +// This function uses swap/destructor to ensure the internal memory is freed. +template void STLClearObject(T* obj) { + T tmp; + tmp.swap(*obj); + obj->reserve(0); // this is because sometimes "T tmp" allocates objects with + // memory (arena implementation?). use reserve() + // to clear() even if it doesn't always work +} + +// Reduce memory usage on behalf of object if it is using more than +// "bytes" bytes of space. By default, we clear objects over 1MB. +template inline void STLClearIfBig(T* obj, size_t limit = 1<<20) { + if (obj->capacity() >= limit) { + STLClearObject(obj); + } else { + obj->clear(); + } +} + +// Reserve space for STL object. +// STL's reserve() will always copy. +// This function avoid the copy if we already have capacity +template void STLReserveIfNeeded(T* obj, int new_size) { + if (obj->capacity() < new_size) // increase capacity + obj->reserve(new_size); + else if (obj->size() > new_size) // reduce size + obj->resize(new_size); +} + +// STLDeleteContainerPointers() +// For a range within a container of pointers, calls delete +// (non-array version) on these pointers. +// NOTE: for these three functions, we could just implement a DeleteObject +// functor and then call for_each() on the range and functor, but this +// requires us to pull in all of algorithm.h, which seems expensive. +// For hash_[multi]set, it is important that this deletes behind the iterator +// because the hash_set may call the hash function on the iterator when it is +// advanced, which could result in the hash function trying to deference a +// stale pointer. +template +void STLDeleteContainerPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete *temp; + } +} + +// STLDeleteContainerPairPointers() +// For a range within a container of pairs, calls delete +// (non-array version) on BOTH items in the pairs. +// NOTE: Like STLDeleteContainerPointers, it is important that this deletes +// behind the iterator because if both the key and value are deleted, the +// container may call the hash function on the iterator when it is advanced, +// which could result in the hash function trying to dereference a stale +// pointer. +template +void STLDeleteContainerPairPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete temp->first; + delete temp->second; + } +} + +// STLDeleteContainerPairFirstPointers() +// For a range within a container of pairs, calls delete (non-array version) +// on the FIRST item in the pairs. +// NOTE: Like STLDeleteContainerPointers, deleting behind the iterator. +template +void STLDeleteContainerPairFirstPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + ForwardIterator temp = begin; + ++begin; + delete temp->first; + } +} + +// STLDeleteContainerPairSecondPointers() +// For a range within a container of pairs, calls delete +// (non-array version) on the SECOND item in the pairs. +template +void STLDeleteContainerPairSecondPointers(ForwardIterator begin, + ForwardIterator end) { + while (begin != end) { + delete begin->second; + ++begin; + } +} + +template +inline void STLAssignToVector(std::vector* vec, + const T* ptr, + size_t n) { + vec->resize(n); + memcpy(&vec->front(), ptr, n*sizeof(T)); +} + +/***** Hack to allow faster assignment to a vector *****/ + +// This routine speeds up an assignment of 32 bytes to a vector from +// about 250 cycles per assignment to about 140 cycles. +// +// Usage: +// STLAssignToVectorChar(&vec, ptr, size); +// STLAssignToString(&str, ptr, size); + +inline void STLAssignToVectorChar(std::vector* vec, + const char* ptr, + size_t n) { + STLAssignToVector(vec, ptr, n); +} + +inline void STLAssignToString(std::string* str, const char* ptr, size_t n) { + str->resize(n); + memcpy(&*str->begin(), ptr, n); +} + +// To treat a possibly-empty vector as an array, use these functions. +// If you know the array will never be empty, you can use &*v.begin() +// directly, but that is allowed to dump core if v is empty. This +// function is the most efficient code that will work, taking into +// account how our STL is actually implemented. THIS IS NON-PORTABLE +// CODE, so call us instead of repeating the nonportable code +// everywhere. If our STL implementation changes, we will need to +// change this as well. + +template +inline T* vector_as_array(std::vector* v) { +# ifdef NDEBUG + return &*v->begin(); +# else + return v->empty() ? NULL : &*v->begin(); +# endif +} + +template +inline const T* vector_as_array(const std::vector* v) { +# ifdef NDEBUG + return &*v->begin(); +# else + return v->empty() ? NULL : &*v->begin(); +# endif +} + +// Return a mutable char* pointing to a string's internal buffer, +// which may not be null-terminated. Writing through this pointer will +// modify the string. +// +// string_as_array(&str)[i] is valid for 0 <= i < str.size() until the +// next call to a string method that invalidates iterators. +// +// As of 2006-04, there is no standard-blessed way of getting a +// mutable reference to a string's internal buffer. However, issue 530 +// (http://www.open-std.org/JTC1/SC22/WG21/docs/lwg-active.html#530) +// proposes this as the method. According to Matt Austern, this should +// already work on all current implementations. +inline char* string_as_array(std::string* str) { + // DO NOT USE const_cast(str->data())! See the unittest for why. + return str->empty() ? NULL : &*str->begin(); +} + +// These are methods that test two hash maps/sets for equality. These exist +// because the == operator in the STL can return false when the maps/sets +// contain identical elements. This is because it compares the internal hash +// tables which may be different if the order of insertions and deletions +// differed. + +template +inline bool +HashSetEquality(const HashSet& set_a, + const HashSet& set_b) { + if (set_a.size() != set_b.size()) return false; + for (typename HashSet::const_iterator i = set_a.begin(); + i != set_a.end(); + ++i) { + if (set_b.find(*i) == set_b.end()) + return false; + } + return true; +} + +template +inline bool +HashMapEquality(const HashMap& map_a, + const HashMap& map_b) { + if (map_a.size() != map_b.size()) return false; + for (typename HashMap::const_iterator i = map_a.begin(); + i != map_a.end(); ++i) { + typename HashMap::const_iterator j = map_b.find(i->first); + if (j == map_b.end()) return false; + if (i->second != j->second) return false; + } + return true; +} + +// The following functions are useful for cleaning up STL containers +// whose elements point to allocated memory. + +// STLDeleteElements() deletes all the elements in an STL container and clears +// the container. This function is suitable for use with a vector, set, +// hash_set, or any other STL container which defines sensible begin(), end(), +// and clear() methods. +// +// If container is NULL, this function is a no-op. +// +// As an alternative to calling STLDeleteElements() directly, consider +// STLElementDeleter (defined below), which ensures that your container's +// elements are deleted when the STLElementDeleter goes out of scope. +template +void STLDeleteElements(T *container) { + if (!container) return; + STLDeleteContainerPointers(container->begin(), container->end()); + container->clear(); +} + +// Given an STL container consisting of (key, value) pairs, STLDeleteValues +// deletes all the "value" components and clears the container. Does nothing +// in the case it's given a NULL pointer. + +template +void STLDeleteValues(T *v) { + if (!v) return; + for (typename T::iterator i = v->begin(); i != v->end(); ++i) { + delete i->second; + } + v->clear(); +} + + +// The following classes provide a convenient way to delete all elements or +// values from STL containers when they goes out of scope. This greatly +// simplifies code that creates temporary objects and has multiple return +// statements. Example: +// +// vector tmp_proto; +// STLElementDeleter > d(&tmp_proto); +// if (...) return false; +// ... +// return success; + +// Given a pointer to an STL container this class will delete all the element +// pointers when it goes out of scope. + +template class STLElementDeleter { + public: + STLElementDeleter(STLContainer *ptr) : container_ptr_(ptr) {} + ~STLElementDeleter() { STLDeleteElements(container_ptr_); } + private: + STLContainer *container_ptr_; +}; + +// Given a pointer to an STL container this class will delete all the value +// pointers when it goes out of scope. + +template class STLValueDeleter { + public: + STLValueDeleter(STLContainer *ptr) : container_ptr_(ptr) {} + ~STLValueDeleter() { STLDeleteValues(container_ptr_); } + private: + STLContainer *container_ptr_; +}; + + +// Forward declare some callback classes in callback.h for STLBinaryFunction +template +class ResultCallback2; + +// STLBinaryFunction is a wrapper for the ResultCallback2 class in callback.h +// It provides an operator () method instead of a Run method, so it may be +// passed to STL functions in . +// +// The client should create callback with NewPermanentCallback, and should +// delete callback after it is done using the STLBinaryFunction. + +template +class STLBinaryFunction : public std::binary_function { + public: + typedef ResultCallback2 Callback; + + STLBinaryFunction(Callback* callback) + : callback_(callback) { + assert(callback_); + } + + Result operator() (Arg1 arg1, Arg2 arg2) { + return callback_->Run(arg1, arg2); + } + + private: + Callback* callback_; +}; + +// STLBinaryPredicate is a specialized version of STLBinaryFunction, where the +// return type is bool and both arguments have type Arg. It can be used +// wherever STL requires a StrictWeakOrdering, such as in sort() or +// lower_bound(). +// +// templated typedefs are not supported, so instead we use inheritance. + +template +class STLBinaryPredicate : public STLBinaryFunction { + public: + typedef typename STLBinaryPredicate::Callback Callback; + STLBinaryPredicate(Callback* callback) + : STLBinaryFunction(callback) { + } +}; + +// Functors that compose arbitrary unary and binary functions with a +// function that "projects" one of the members of a pair. +// Specifically, if p1 and p2, respectively, are the functions that +// map a pair to its first and second, respectively, members, the +// table below summarizes the functions that can be constructed: +// +// * UnaryOperate1st(f) returns the function x -> f(p1(x)) +// * UnaryOperate2nd(f) returns the function x -> f(p2(x)) +// * BinaryOperate1st(f) returns the function (x,y) -> f(p1(x),p1(y)) +// * BinaryOperate2nd(f) returns the function (x,y) -> f(p2(x),p2(y)) +// +// A typical usage for these functions would be when iterating over +// the contents of an STL map. For other sample usage, see the unittest. + +template +class UnaryOperateOnFirst + : public std::unary_function { + public: + UnaryOperateOnFirst() { + } + + UnaryOperateOnFirst(const UnaryOp& f) : f_(f) { + } + + typename UnaryOp::result_type operator()(const Pair& p) const { + return f_(p.first); + } + + private: + UnaryOp f_; +}; + +template +UnaryOperateOnFirst UnaryOperate1st(const UnaryOp& f) { + return UnaryOperateOnFirst(f); +} + +template +class UnaryOperateOnSecond + : public std::unary_function { + public: + UnaryOperateOnSecond() { + } + + UnaryOperateOnSecond(const UnaryOp& f) : f_(f) { + } + + typename UnaryOp::result_type operator()(const Pair& p) const { + return f_(p.second); + } + + private: + UnaryOp f_; +}; + +template +UnaryOperateOnSecond UnaryOperate2nd(const UnaryOp& f) { + return UnaryOperateOnSecond(f); +} + +template +class BinaryOperateOnFirst + : public std::binary_function { + public: + BinaryOperateOnFirst() { + } + + BinaryOperateOnFirst(const BinaryOp& f) : f_(f) { + } + + typename BinaryOp::result_type operator()(const Pair& p1, + const Pair& p2) const { + return f_(p1.first, p2.first); + } + + private: + BinaryOp f_; +}; + +template +BinaryOperateOnFirst BinaryOperate1st(const BinaryOp& f) { + return BinaryOperateOnFirst(f); +} + +template +class BinaryOperateOnSecond + : public std::binary_function { + public: + BinaryOperateOnSecond() { + } + + BinaryOperateOnSecond(const BinaryOp& f) : f_(f) { + } + + typename BinaryOp::result_type operator()(const Pair& p1, + const Pair& p2) const { + return f_(p1.second, p2.second); + } + + private: + BinaryOp f_; +}; + +template +BinaryOperateOnSecond BinaryOperate2nd(const BinaryOp& f) { + return BinaryOperateOnSecond(f); +} + +// Translates a set into a vector. +template +std::vector SetToVector(const std::set& values) { + std::vector result; + result.reserve(values.size()); + result.insert(result.begin(), values.begin(), values.end()); + return result; +} + +#endif // BASE_STL_UTIL_INL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string16.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string16.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string16.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string16.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,78 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/string16.h" + +#if defined(WCHAR_T_IS_UTF16) + +#error This file should not be used on 2-byte wchar_t systems +// If this winds up being needed on 2-byte wchar_t systems, either the +// definitions below can be used, or the host system's wide character +// functions like wmemcmp can be wrapped. + +#elif defined(WCHAR_T_IS_UTF32) + +#include "base/string_util.h" + +namespace base { + +int c16memcmp(const char16* s1, const char16* s2, size_t n) { + // We cannot call memcmp because that changes the semantics. + while (n-- > 0) { + if (*s1 != *s2) { + // We cannot use (*s1 - *s2) because char16 is unsigned. + return ((*s1 < *s2) ? -1 : 1); + } + ++s1; + ++s2; + } + return 0; +} + +size_t c16len(const char16* s) { + const char16 *s_orig = s; + while (*s) { + ++s; + } + return s - s_orig; +} + +const char16* c16memchr(const char16* s, char16 c, size_t n) { + while (n-- > 0) { + if (*s == c) { + return s; + } + ++s; + } + return 0; +} + +char16* c16memmove(char16* s1, const char16* s2, size_t n) { + return reinterpret_cast(memmove(s1, s2, n * sizeof(char16))); +} + +char16* c16memcpy(char16* s1, const char16* s2, size_t n) { + return reinterpret_cast(memcpy(s1, s2, n * sizeof(char16))); +} + +char16* c16memset(char16* s, char16 c, size_t n) { + char16 *s_orig = s; + while (n-- > 0) { + *s = c; + ++s; + } + return s_orig; +} + +} // namespace base + +template class std::basic_string; + +#ifndef CHROMIUM_MOZILLA_BUILD +std::ostream& operator<<(std::ostream& out, const string16& str) { + return out << UTF16ToUTF8(str); +} +#endif + +#endif // WCHAR_T_IS_UTF32 diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string16.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string16.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string16.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string16.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,173 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STRING16_H_ +#define BASE_STRING16_H_ + +// WHAT: +// A version of std::basic_string that provides 2-byte characters even when +// wchar_t is not implemented as a 2-byte type. You can access this class as +// string16. We also define char16, which string16 is based upon. +// +// WHY: +// On Windows, wchar_t is 2 bytes, and it can conveniently handle UTF-16/UCS-2 +// data. Plenty of existing code operates on strings encoded as UTF-16. +// +// On many other platforms, sizeof(wchar_t) is 4 bytes by default. We can make +// it 2 bytes by using the GCC flag -fshort-wchar. But then std::wstring fails +// at run time, because it calls some functions (like wcslen) that come from +// the system's native C library -- which was built with a 4-byte wchar_t! +// It's wasteful to use 4-byte wchar_t strings to carry UTF-16 data, and it's +// entirely improper on those systems where the encoding of wchar_t is defined +// as UTF-32. +// +// Here, we define string16, which is similar to std::wstring but replaces all +// libc functions with custom, 2-byte-char compatible routines. It is capable +// of carrying UTF-16-encoded data. + +#include +#include + +#include "base/basictypes.h" + +#if defined(WCHAR_T_IS_UTF16) + +typedef wchar_t char16; +typedef std::wstring string16; + +#elif defined(WCHAR_T_IS_UTF32) + +typedef uint16 char16; + +namespace base { + +// char16 versions of the functions required by string16_char_traits; these +// are based on the wide character functions of similar names ("w" or "wcs" +// instead of "c16"). +int c16memcmp(const char16* s1, const char16* s2, size_t n); +size_t c16len(const char16* s); +const char16* c16memchr(const char16* s, char16 c, size_t n); +char16* c16memmove(char16* s1, const char16* s2, size_t n); +char16* c16memcpy(char16* s1, const char16* s2, size_t n); +char16* c16memset(char16* s, char16 c, size_t n); + +struct string16_char_traits { + typedef char16 char_type; + typedef int int_type; + + // int_type needs to be able to hold each possible value of char_type, and in + // addition, the distinct value of eof(). + COMPILE_ASSERT(sizeof(int_type) > sizeof(char_type), unexpected_type_width); + + typedef std::streamoff off_type; + typedef mbstate_t state_type; + typedef std::fpos pos_type; + + static void assign(char_type& c1, const char_type& c2) { + c1 = c2; + } + + static bool eq(const char_type& c1, const char_type& c2) { + return c1 == c2; + } + static bool lt(const char_type& c1, const char_type& c2) { + return c1 < c2; + } + + static int compare(const char_type* s1, const char_type* s2, size_t n) { + return c16memcmp(s1, s2, n); + } + + static size_t length(const char_type* s) { + return c16len(s); + } + + static const char_type* find(const char_type* s, size_t n, + const char_type& a) { + return c16memchr(s, a, n); + } + + static char_type* move(char_type* s1, const char_type* s2, int_type n) { + return c16memmove(s1, s2, n); + } + + static char_type* copy(char_type* s1, const char_type* s2, size_t n) { + return c16memcpy(s1, s2, n); + } + + static char_type* assign(char_type* s, size_t n, char_type a) { + return c16memset(s, a, n); + } + + static int_type not_eof(const int_type& c) { + return eq_int_type(c, eof()) ? 0 : c; + } + + static char_type to_char_type(const int_type& c) { + return char_type(c); + } + + static int_type to_int_type(const char_type& c) { + return int_type(c); + } + + static bool eq_int_type(const int_type& c1, const int_type& c2) { + return c1 == c2; + } + + static int_type eof() { + return static_cast(EOF); + } +}; + +} // namespace base + +// The string class will be explicitly instantiated only once, in string16.cc. +// +// std::basic_string<> in GNU libstdc++ contains a static data member, +// _S_empty_rep_storage, to represent empty strings. When an operation such +// as assignment or destruction is performed on a string, causing its existing +// data member to be invalidated, it must not be freed if this static data +// member is being used. Otherwise, it counts as an attempt to free static +// (and not allocated) data, which is a memory error. +// +// Generally, due to C++ template magic, _S_empty_rep_storage will be marked +// as a coalesced symbol, meaning that the linker will combine multiple +// instances into a single one when generating output. +// +// If a string class is used by multiple shared libraries, a problem occurs. +// Each library will get its own copy of _S_empty_rep_storage. When strings +// are passed across a library boundary for alteration or destruction, memory +// errors will result. GNU libstdc++ contains a configuration option, +// --enable-fully-dynamic-string (_GLIBCXX_FULLY_DYNAMIC_STRING), which +// disables the static data member optimization, but it's a good optimization +// and non-STL code is generally at the mercy of the system's STL +// configuration. Fully-dynamic strings are not the default for GNU libstdc++ +// libstdc++ itself or for the libstdc++ installations on the systems we care +// about, such as Mac OS X and relevant flavors of Linux. +// +// See also http://gcc.gnu.org/bugzilla/show_bug.cgi?id=24196 . +// +// To avoid problems, string classes need to be explicitly instantiated only +// once, in exactly one library. All other string users see it via an "extern" +// declaration. This is precisely how GNU libstdc++ handles +// std::basic_string (string) and std::basic_string (wstring). +// +// This also works around a Mac OS X linker bug in ld64-85.2.1 (Xcode 3.1.2), +// in which the linker does not fully coalesce symbols when dead code +// stripping is enabled. This bug causes the memory errors described above +// to occur even when a std::basic_string<> does not cross shared library +// boundaries, such as in statically-linked executables. +// +// TODO(mark): File this bug with Apple and update this note with a bug number. + +extern template class std::basic_string; + +typedef std::basic_string string16; + +extern std::ostream& operator<<(std::ostream& out, const string16& str); + +#endif // WCHAR_T_IS_UTF32 + +#endif // BASE_STRING16_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_escape.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_escape.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_escape.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_escape.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,98 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/string_escape.h" + +#include + +#include "base/string_util.h" + +namespace string_escape { + +// Try to escape |c| as a "SingleEscapeCharacter" (\n, etc). If successful, +// returns true and appends the escape sequence to |dst|. +template +static bool JavascriptSingleEscapeChar(const CHAR c, std::string* dst) { + // WARNING: if you add a new case here, you need to update the reader as well. + switch (c) { + case '\b': + dst->append("\\b"); + break; + case '\f': + dst->append("\\f"); + break; + case '\n': + dst->append("\\n"); + break; + case '\r': + dst->append("\\r"); + break; + case '\t': + dst->append("\\t"); + break; + case '\v': + dst->append("\\v"); + break; + case '\\': + dst->append("\\\\"); + break; + case '"': + dst->append("\\\""); + break; + default: + return false; + } + return true; +} + +void JavascriptDoubleQuote(const string16& str, + bool put_in_quotes, + std::string* dst) { + if (put_in_quotes) + dst->push_back('"'); + + for (string16::const_iterator it = str.begin(); it != str.end(); ++it) { + char16 c = *it; + if (!JavascriptSingleEscapeChar(c, dst)) { + if (c > 255) { + // Non-ascii values need to be unicode dst-> + // TODO(tc): Some unicode values are handled specially. See + // spidermonkey code. + StringAppendF(dst, "\\u%04X", c); + } else if (c < 32 || c > 126) { + // Spidermonkey hex escapes these values. + StringAppendF(dst, "\\x%02X", c); + } else { + dst->push_back(static_cast(c)); + } + } + } + + if (put_in_quotes) + dst->push_back('"'); +} + +void JavascriptDoubleQuote(const std::string& str, + bool put_in_quotes, + std::string* dst) { + if (put_in_quotes) + dst->push_back('"'); + + for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) { + unsigned char c = *it; + if (!JavascriptSingleEscapeChar(c, dst)) { + // Hex encode if the character is non-printable 7bit ascii + if (c < 32 || c == 127) { + StringAppendF(dst, "\\x%02X", c); + } else { + dst->push_back(static_cast(c)); + } + } + } + + if (put_in_quotes) + dst->push_back('"'); +} + +} // namespace string_escape diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_escape.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_escape.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_escape.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_escape.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,35 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// This file defines utility functions for escaping strings. + +#ifndef BASE_STRING_ESCAPE_H__ +#define BASE_STRING_ESCAPE_H__ + +#include "base/string16.h" + +namespace string_escape { + +// Escape |str| appropriately for a javascript string litereal, _appending_ the +// result to |dst|. This will create standard escape sequences (\b, \n), +// hex escape sequences (\x00), and unicode escape sequences (\uXXXX). +// If |put_in_quotes| is true, the result will be surrounded in double quotes. +// The outputted literal, when interpreted by the browser, should result in a +// javascript string that is identical and the same length as the input |str|. +void JavascriptDoubleQuote(const string16& str, + bool put_in_quotes, + std::string* dst); + +// Similar to the wide version, but for narrow strings. It will not use +// \uXXXX unicode escape sequences. It will pass non-7bit characters directly +// into the string unencoded, allowing the browser to interpret the encoding. +// The outputted literal, when interpreted by the browser, could result in a +// javascript string of a different length than the input |str|. +void JavascriptDoubleQuote(const std::string& str, + bool put_in_quotes, + std::string* dst); + +} // namespace string_escape + +#endif // BASE_STRING_ESCAPE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_escape_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_escape_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_escape_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_escape_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,64 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/gtest/include/gtest/gtest.h" +#include "base/string_escape.h" +#include "base/string_util.h" + +TEST(StringEscapeTest, JavascriptDoubleQuote) { + static const char* kToEscape = "\b\001aZ\"\\wee"; + static const char* kEscaped = "\\b\\x01aZ\\\"\\\\wee"; + static const char* kEscapedQuoted = "\"\\b\\x01aZ\\\"\\\\wee\""; + static const wchar_t* kUToEscape = L"\b\x0001" L"a\x123fZ\"\\wee"; + static const char* kUEscaped = "\\b\\x01a\\u123FZ\\\"\\\\wee"; + static const char* kUEscapedQuoted = "\"\\b\\x01a\\u123FZ\\\"\\\\wee\""; + + std::string out; + + // Test wide unicode escaping + out = "testy: "; + string_escape::JavascriptDoubleQuote(WideToUTF16(kUToEscape), false, &out); + ASSERT_EQ(std::string("testy: ") + kUEscaped, out); + + out = "testy: "; + string_escape::JavascriptDoubleQuote(WideToUTF16(kUToEscape), true, &out); + ASSERT_EQ(std::string("testy: ") + kUEscapedQuoted, out); + + // Test null and high bit / negative unicode values + string16 str16 = UTF8ToUTF16("TeSt"); + str16.push_back(0); + str16.push_back(0xffb1); + str16.push_back(0x00ff); + + out = "testy: "; + string_escape::JavascriptDoubleQuote(str16, false, &out); + ASSERT_EQ("testy: TeSt\\x00\\uFFB1\\xFF", out); + + // Test escaping of 7bit ascii + out = "testy: "; + string_escape::JavascriptDoubleQuote(std::string(kToEscape), false, &out); + ASSERT_EQ(std::string("testy: ") + kEscaped, out); + + out = "testy: "; + string_escape::JavascriptDoubleQuote(std::string(kToEscape), true, &out); + ASSERT_EQ(std::string("testy: ") + kEscapedQuoted, out); + + // Test null, non-printable, and non-7bit + std::string str("TeSt"); + str.push_back(0); + str.push_back(15); + str.push_back(127); + str.push_back(-16); + str.push_back(-128); + str.push_back('!'); + + out = "testy: "; + string_escape::JavascriptDoubleQuote(str, false, &out); + ASSERT_EQ("testy: TeSt\\x00\\x0F\\x7F\xf0\x80!", out); + + // Test escape sequences + out = "testy: "; + string_escape::JavascriptDoubleQuote("a\b\f\n\r\t\v\1\\.\"z", false, &out); + ASSERT_EQ("testy: a\\b\\f\\n\\r\\t\\v\\x01\\\\.\\\"z", out); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_piece.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_piece.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_piece.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_piece.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,215 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Copied from strings/stringpiece.cc with modifications + +#include +#include + +#include "base/string_piece.h" + +typedef StringPiece::size_type size_type; + +std::ostream& operator<<(std::ostream& o, const StringPiece& piece) { + o.write(piece.data(), static_cast(piece.size())); + return o; +} + +bool operator==(const StringPiece& x, const StringPiece& y) { + if (x.size() != y.size()) + return false; + + return StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0; +} + +void StringPiece::CopyToString(std::string* target) const { + target->assign(!empty() ? data() : "", size()); +} + +void StringPiece::AppendToString(std::string* target) const { + if (!empty()) + target->append(data(), size()); +} + +size_type StringPiece::copy(char* buf, size_type n, size_type pos) const { + size_type ret = std::min(length_ - pos, n); + memcpy(buf, ptr_ + pos, ret); + return ret; +} + +size_type StringPiece::find(const StringPiece& s, size_type pos) const { + if (pos > length_) + return npos; + + const char* result = std::search(ptr_ + pos, ptr_ + length_, + s.ptr_, s.ptr_ + s.length_); + const size_type xpos = result - ptr_; + return xpos + s.length_ <= length_ ? xpos : npos; +} + +size_type StringPiece::find(char c, size_type pos) const { + if (pos >= length_) + return npos; + + const char* result = std::find(ptr_ + pos, ptr_ + length_, c); + return result != ptr_ + length_ ? result - ptr_ : npos; +} + +size_type StringPiece::rfind(const StringPiece& s, size_type pos) const { + if (length_ < s.length_) + return npos; + + if (s.empty()) + return std::min(length_, pos); + + const char* last = ptr_ + std::min(length_ - s.length_, pos) + s.length_; + const char* result = std::find_end(ptr_, last, s.ptr_, s.ptr_ + s.length_); + return result != last ? result - ptr_ : npos; +} + +size_type StringPiece::rfind(char c, size_type pos) const { + if (length_ == 0) + return npos; + + for (size_type i = std::min(pos, length_ - 1); ; --i) { + if (ptr_[i] == c) + return i; + if (i == 0) + break; + } + return npos; +} + +// For each character in characters_wanted, sets the index corresponding +// to the ASCII code of that character to 1 in table. This is used by +// the find_.*_of methods below to tell whether or not a character is in +// the lookup table in constant time. +// The argument `table' must be an array that is large enough to hold all +// the possible values of an unsigned char. Thus it should be be declared +// as follows: +// bool table[UCHAR_MAX + 1] +static inline void BuildLookupTable(const StringPiece& characters_wanted, + bool* table) { + const size_type length = characters_wanted.length(); + const char* const data = characters_wanted.data(); + for (size_type i = 0; i < length; ++i) { + table[static_cast(data[i])] = true; + } +} + +size_type StringPiece::find_first_of(const StringPiece& s, + size_type pos) const { + if (length_ == 0 || s.length_ == 0) + return npos; + + // Avoid the cost of BuildLookupTable() for a single-character search. + if (s.length_ == 1) + return find_first_of(s.ptr_[0], pos); + + bool lookup[UCHAR_MAX + 1] = { false }; + BuildLookupTable(s, lookup); + for (size_type i = pos; i < length_; ++i) { + if (lookup[static_cast(ptr_[i])]) { + return i; + } + } + return npos; +} + +size_type StringPiece::find_first_not_of(const StringPiece& s, + size_type pos) const { + if (length_ == 0) + return npos; + + if (s.length_ == 0) + return 0; + + // Avoid the cost of BuildLookupTable() for a single-character search. + if (s.length_ == 1) + return find_first_not_of(s.ptr_[0], pos); + + bool lookup[UCHAR_MAX + 1] = { false }; + BuildLookupTable(s, lookup); + for (size_type i = pos; i < length_; ++i) { + if (!lookup[static_cast(ptr_[i])]) { + return i; + } + } + return npos; +} + +size_type StringPiece::find_first_not_of(char c, size_type pos) const { + if (length_ == 0) + return npos; + + for (; pos < length_; ++pos) { + if (ptr_[pos] != c) { + return pos; + } + } + return npos; +} + +size_type StringPiece::find_last_of(const StringPiece& s, size_type pos) const { + if (length_ == 0 || s.length_ == 0) + return npos; + + // Avoid the cost of BuildLookupTable() for a single-character search. + if (s.length_ == 1) + return find_last_of(s.ptr_[0], pos); + + bool lookup[UCHAR_MAX + 1] = { false }; + BuildLookupTable(s, lookup); + for (size_type i = std::min(pos, length_ - 1); ; --i) { + if (lookup[static_cast(ptr_[i])]) + return i; + if (i == 0) + break; + } + return npos; +} + +size_type StringPiece::find_last_not_of(const StringPiece& s, + size_type pos) const { + if (length_ == 0) + return npos; + + size_type i = std::min(pos, length_ - 1); + if (s.length_ == 0) + return i; + + // Avoid the cost of BuildLookupTable() for a single-character search. + if (s.length_ == 1) + return find_last_not_of(s.ptr_[0], pos); + + bool lookup[UCHAR_MAX + 1] = { false }; + BuildLookupTable(s, lookup); + for (; ; --i) { + if (!lookup[static_cast(ptr_[i])]) + return i; + if (i == 0) + break; + } + return npos; +} + +size_type StringPiece::find_last_not_of(char c, size_type pos) const { + if (length_ == 0) + return npos; + + for (size_type i = std::min(pos, length_ - 1); ; --i) { + if (ptr_[i] != c) + return i; + if (i == 0) + break; + } + return npos; +} + +StringPiece StringPiece::substr(size_type pos, size_type n) const { + if (pos > length_) pos = length_; + if (n > length_ - pos) n = length_ - pos; + return StringPiece(ptr_ + pos, n); +} + +const StringPiece::size_type StringPiece::npos = size_type(-1); diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_piece.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_piece.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_piece.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_piece.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,184 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Copied from strings/stringpiece.h with modifications +// +// A string-like object that points to a sized piece of memory. +// +// Functions or methods may use const StringPiece& parameters to accept either +// a "const char*" or a "string" value that will be implicitly converted to +// a StringPiece. The implicit conversion means that it is often appropriate +// to include this .h file in other files rather than forward-declaring +// StringPiece as would be appropriate for most other Google classes. +// +// Systematic usage of StringPiece is encouraged as it will reduce unnecessary +// conversions from "const char*" to "string" and back again. +// + +#ifndef BASE_STRING_PIECE_H_ +#define BASE_STRING_PIECE_H_ + +#include +#include +#include + +#include "base/basictypes.h" + +class StringPiece { + public: + typedef size_t size_type; + + private: + const char* ptr_; + size_type length_; + + public: + // We provide non-explicit singleton constructors so users can pass + // in a "const char*" or a "string" wherever a "StringPiece" is + // expected. + StringPiece() : ptr_(NULL), length_(0) { } + StringPiece(const char* str) + : ptr_(str), length_((str == NULL) ? 0 : strlen(str)) { } + StringPiece(const std::string& str) + : ptr_(str.data()), length_(str.size()) { } + StringPiece(const char* offset, size_type len) + : ptr_(offset), length_(len) { } + + // data() may return a pointer to a buffer with embedded NULs, and the + // returned buffer may or may not be null terminated. Therefore it is + // typically a mistake to pass data() to a routine that expects a NUL + // terminated string. + const char* data() const { return ptr_; } + size_type size() const { return length_; } + size_type length() const { return length_; } + bool empty() const { return length_ == 0; } + + void clear() { ptr_ = NULL; length_ = 0; } + void set(const char* data, size_type len) { ptr_ = data; length_ = len; } + void set(const char* str) { + ptr_ = str; + length_ = str ? strlen(str) : 0; + } + void set(const void* data, size_type len) { + ptr_ = reinterpret_cast(data); + length_ = len; + } + + char operator[](size_type i) const { return ptr_[i]; } + + void remove_prefix(size_type n) { + ptr_ += n; + length_ -= n; + } + + void remove_suffix(size_type n) { + length_ -= n; + } + + int compare(const StringPiece& x) const { + int r = wordmemcmp(ptr_, x.ptr_, std::min(length_, x.length_)); + if (r == 0) { + if (length_ < x.length_) r = -1; + else if (length_ > x.length_) r = +1; + } + return r; + } + + std::string as_string() const { + // std::string doesn't like to take a NULL pointer even with a 0 size. + return std::string(!empty() ? data() : "", size()); + } + + void CopyToString(std::string* target) const; + void AppendToString(std::string* target) const; + + // Does "this" start with "x" + bool starts_with(const StringPiece& x) const { + return ((length_ >= x.length_) && + (wordmemcmp(ptr_, x.ptr_, x.length_) == 0)); + } + + // Does "this" end with "x" + bool ends_with(const StringPiece& x) const { + return ((length_ >= x.length_) && + (wordmemcmp(ptr_ + (length_-x.length_), x.ptr_, x.length_) == 0)); + } + + // standard STL container boilerplate + typedef char value_type; + typedef const char* pointer; + typedef const char& reference; + typedef const char& const_reference; + typedef ptrdiff_t difference_type; + static const size_type npos; + typedef const char* const_iterator; + typedef const char* iterator; + typedef std::reverse_iterator const_reverse_iterator; + typedef std::reverse_iterator reverse_iterator; + iterator begin() const { return ptr_; } + iterator end() const { return ptr_ + length_; } + const_reverse_iterator rbegin() const { + return const_reverse_iterator(ptr_ + length_); + } + const_reverse_iterator rend() const { + return const_reverse_iterator(ptr_); + } + + size_type max_size() const { return length_; } + size_type capacity() const { return length_; } + + size_type copy(char* buf, size_type n, size_type pos = 0) const; + + size_type find(const StringPiece& s, size_type pos = 0) const; + size_type find(char c, size_type pos = 0) const; + size_type rfind(const StringPiece& s, size_type pos = npos) const; + size_type rfind(char c, size_type pos = npos) const; + + size_type find_first_of(const StringPiece& s, size_type pos = 0) const; + size_type find_first_of(char c, size_type pos = 0) const { + return find(c, pos); + } + size_type find_first_not_of(const StringPiece& s, size_type pos = 0) const; + size_type find_first_not_of(char c, size_type pos = 0) const; + size_type find_last_of(const StringPiece& s, size_type pos = npos) const; + size_type find_last_of(char c, size_type pos = npos) const { + return rfind(c, pos); + } + size_type find_last_not_of(const StringPiece& s, size_type pos = npos) const; + size_type find_last_not_of(char c, size_type pos = npos) const; + + StringPiece substr(size_type pos, size_type n = npos) const; + + static int wordmemcmp(const char* p, const char* p2, size_type N) { + return memcmp(p, p2, N); + } +}; + +bool operator==(const StringPiece& x, const StringPiece& y); + +inline bool operator!=(const StringPiece& x, const StringPiece& y) { + return !(x == y); +} + +inline bool operator<(const StringPiece& x, const StringPiece& y) { + const int r = StringPiece::wordmemcmp(x.data(), y.data(), + std::min(x.size(), y.size())); + return ((r < 0) || ((r == 0) && (x.size() < y.size()))); +} + +inline bool operator>(const StringPiece& x, const StringPiece& y) { + return y < x; +} + +inline bool operator<=(const StringPiece& x, const StringPiece& y) { + return !(x > y); +} + +inline bool operator>=(const StringPiece& x, const StringPiece& y) { + return !(x < y); +} + +// allow StringPiece to be logged (needed for unit testing). +extern std::ostream& operator<<(std::ostream& o, const StringPiece& piece); + +#endif // BASE_STRING_PIECE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_piece_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_piece_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_piece_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_piece_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,540 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/string_piece.h" + +#include "testing/gtest/include/gtest/gtest.h" + +TEST(StringPieceTest, CheckComparisonOperators) { +#define CMP_Y(op, x, y) \ + ASSERT_TRUE( (StringPiece((x)) op StringPiece((y)))); \ + ASSERT_TRUE( (StringPiece((x)).compare(StringPiece((y))) op 0)) + +#define CMP_N(op, x, y) \ + ASSERT_FALSE(StringPiece((x)) op StringPiece((y))); \ + ASSERT_FALSE(StringPiece((x)).compare(StringPiece((y))) op 0) + + CMP_Y(==, "", ""); + CMP_Y(==, "a", "a"); + CMP_Y(==, "aa", "aa"); + CMP_N(==, "a", ""); + CMP_N(==, "", "a"); + CMP_N(==, "a", "b"); + CMP_N(==, "a", "aa"); + CMP_N(==, "aa", "a"); + + CMP_N(!=, "", ""); + CMP_N(!=, "a", "a"); + CMP_N(!=, "aa", "aa"); + CMP_Y(!=, "a", ""); + CMP_Y(!=, "", "a"); + CMP_Y(!=, "a", "b"); + CMP_Y(!=, "a", "aa"); + CMP_Y(!=, "aa", "a"); + + CMP_Y(<, "a", "b"); + CMP_Y(<, "a", "aa"); + CMP_Y(<, "aa", "b"); + CMP_Y(<, "aa", "bb"); + CMP_N(<, "a", "a"); + CMP_N(<, "b", "a"); + CMP_N(<, "aa", "a"); + CMP_N(<, "b", "aa"); + CMP_N(<, "bb", "aa"); + + CMP_Y(<=, "a", "a"); + CMP_Y(<=, "a", "b"); + CMP_Y(<=, "a", "aa"); + CMP_Y(<=, "aa", "b"); + CMP_Y(<=, "aa", "bb"); + CMP_N(<=, "b", "a"); + CMP_N(<=, "aa", "a"); + CMP_N(<=, "b", "aa"); + CMP_N(<=, "bb", "aa"); + + CMP_N(>=, "a", "b"); + CMP_N(>=, "a", "aa"); + CMP_N(>=, "aa", "b"); + CMP_N(>=, "aa", "bb"); + CMP_Y(>=, "a", "a"); + CMP_Y(>=, "b", "a"); + CMP_Y(>=, "aa", "a"); + CMP_Y(>=, "b", "aa"); + CMP_Y(>=, "bb", "aa"); + + CMP_N(>, "a", "a"); + CMP_N(>, "a", "b"); + CMP_N(>, "a", "aa"); + CMP_N(>, "aa", "b"); + CMP_N(>, "aa", "bb"); + CMP_Y(>, "b", "a"); + CMP_Y(>, "aa", "a"); + CMP_Y(>, "b", "aa"); + CMP_Y(>, "bb", "aa"); + + std::string x; + for (int i = 0; i < 256; i++) { + x += 'a'; + std::string y = x; + CMP_Y(==, x, y); + for (int j = 0; j < i; j++) { + std::string z = x; + z[j] = 'b'; // Differs in position 'j' + CMP_N(==, x, z); + } + } + +#undef CMP_Y +#undef CMP_N +} + +TEST(StringPieceTest, CheckSTL) { + StringPiece a("abcdefghijklmnopqrstuvwxyz"); + StringPiece b("abc"); + StringPiece c("xyz"); + StringPiece d("foobar"); + StringPiece e; + std::string temp("123"); + temp += '\0'; + temp += "456"; + StringPiece f(temp); + + ASSERT_EQ(a[6], 'g'); + ASSERT_EQ(b[0], 'a'); + ASSERT_EQ(c[2], 'z'); + ASSERT_EQ(f[3], '\0'); + ASSERT_EQ(f[5], '5'); + + ASSERT_EQ(*d.data(), 'f'); + ASSERT_EQ(d.data()[5], 'r'); + ASSERT_TRUE(e.data() == NULL); + + ASSERT_EQ(*a.begin(), 'a'); + ASSERT_EQ(*(b.begin() + 2), 'c'); + ASSERT_EQ(*(c.end() - 1), 'z'); + + ASSERT_EQ(*a.rbegin(), 'z'); + ASSERT_EQ(*(b.rbegin() + 2), 'a'); + ASSERT_EQ(*(c.rend() - 1), 'x'); + ASSERT_TRUE(a.rbegin() + 26 == a.rend()); + + ASSERT_EQ(a.size(), 26U); + ASSERT_EQ(b.size(), 3U); + ASSERT_EQ(c.size(), 3U); + ASSERT_EQ(d.size(), 6U); + ASSERT_EQ(e.size(), 0U); + ASSERT_EQ(f.size(), 7U); + + ASSERT_TRUE(!d.empty()); + ASSERT_TRUE(d.begin() != d.end()); + ASSERT_TRUE(d.begin() + 6 == d.end()); + + ASSERT_TRUE(e.empty()); + ASSERT_TRUE(e.begin() == e.end()); + + d.clear(); + ASSERT_EQ(d.size(), 0U); + ASSERT_TRUE(d.empty()); + ASSERT_TRUE(d.data() == NULL); + ASSERT_TRUE(d.begin() == d.end()); + + ASSERT_GE(a.max_size(), a.capacity()); + ASSERT_GE(a.capacity(), a.size()); + + char buf[4] = { '%', '%', '%', '%' }; + ASSERT_EQ(a.copy(buf, 4), 4U); + ASSERT_EQ(buf[0], a[0]); + ASSERT_EQ(buf[1], a[1]); + ASSERT_EQ(buf[2], a[2]); + ASSERT_EQ(buf[3], a[3]); + ASSERT_EQ(a.copy(buf, 3, 7), 3U); + ASSERT_EQ(buf[0], a[7]); + ASSERT_EQ(buf[1], a[8]); + ASSERT_EQ(buf[2], a[9]); + ASSERT_EQ(buf[3], a[3]); + ASSERT_EQ(c.copy(buf, 99), 3U); + ASSERT_EQ(buf[0], c[0]); + ASSERT_EQ(buf[1], c[1]); + ASSERT_EQ(buf[2], c[2]); + ASSERT_EQ(buf[3], a[3]); + + ASSERT_EQ(StringPiece::npos, std::string::npos); + + ASSERT_EQ(a.find(b), 0U); + ASSERT_EQ(a.find(b, 1), StringPiece::npos); + ASSERT_EQ(a.find(c), 23U); + ASSERT_EQ(a.find(c, 9), 23U); + ASSERT_EQ(a.find(c, StringPiece::npos), StringPiece::npos); + ASSERT_EQ(b.find(c), StringPiece::npos); + ASSERT_EQ(b.find(c, StringPiece::npos), StringPiece::npos); + ASSERT_EQ(a.find(d), 0U); + ASSERT_EQ(a.find(e), 0U); + ASSERT_EQ(a.find(d, 12), 12U); + ASSERT_EQ(a.find(e, 17), 17U); + StringPiece g("xx not found bb"); + ASSERT_EQ(a.find(g), StringPiece::npos); + // empty string nonsense + ASSERT_EQ(d.find(b), StringPiece::npos); + ASSERT_EQ(e.find(b), StringPiece::npos); + ASSERT_EQ(d.find(b, 4), StringPiece::npos); + ASSERT_EQ(e.find(b, 7), StringPiece::npos); + + size_t empty_search_pos = std::string().find(std::string()); + ASSERT_EQ(d.find(d), empty_search_pos); + ASSERT_EQ(d.find(e), empty_search_pos); + ASSERT_EQ(e.find(d), empty_search_pos); + ASSERT_EQ(e.find(e), empty_search_pos); + ASSERT_EQ(d.find(d, 4), std::string().find(std::string(), 4)); + ASSERT_EQ(d.find(e, 4), std::string().find(std::string(), 4)); + ASSERT_EQ(e.find(d, 4), std::string().find(std::string(), 4)); + ASSERT_EQ(e.find(e, 4), std::string().find(std::string(), 4)); + + ASSERT_EQ(a.find('a'), 0U); + ASSERT_EQ(a.find('c'), 2U); + ASSERT_EQ(a.find('z'), 25U); + ASSERT_EQ(a.find('$'), StringPiece::npos); + ASSERT_EQ(a.find('\0'), StringPiece::npos); + ASSERT_EQ(f.find('\0'), 3U); + ASSERT_EQ(f.find('3'), 2U); + ASSERT_EQ(f.find('5'), 5U); + ASSERT_EQ(g.find('o'), 4U); + ASSERT_EQ(g.find('o', 4), 4U); + ASSERT_EQ(g.find('o', 5), 8U); + ASSERT_EQ(a.find('b', 5), StringPiece::npos); + // empty string nonsense + ASSERT_EQ(d.find('\0'), StringPiece::npos); + ASSERT_EQ(e.find('\0'), StringPiece::npos); + ASSERT_EQ(d.find('\0', 4), StringPiece::npos); + ASSERT_EQ(e.find('\0', 7), StringPiece::npos); + ASSERT_EQ(d.find('x'), StringPiece::npos); + ASSERT_EQ(e.find('x'), StringPiece::npos); + ASSERT_EQ(d.find('x', 4), StringPiece::npos); + ASSERT_EQ(e.find('x', 7), StringPiece::npos); + + ASSERT_EQ(a.rfind(b), 0U); + ASSERT_EQ(a.rfind(b, 1), 0U); + ASSERT_EQ(a.rfind(c), 23U); + ASSERT_EQ(a.rfind(c, 22U), StringPiece::npos); + ASSERT_EQ(a.rfind(c, 1U), StringPiece::npos); + ASSERT_EQ(a.rfind(c, 0U), StringPiece::npos); + ASSERT_EQ(b.rfind(c), StringPiece::npos); + ASSERT_EQ(b.rfind(c, 0U), StringPiece::npos); + ASSERT_EQ(a.rfind(d), (size_t) a.as_string().rfind(std::string())); + ASSERT_EQ(a.rfind(e), a.as_string().rfind(std::string())); + ASSERT_EQ(a.rfind(d, 12), 12U); + ASSERT_EQ(a.rfind(e, 17), 17U); + ASSERT_EQ(a.rfind(g), StringPiece::npos); + ASSERT_EQ(d.rfind(b), StringPiece::npos); + ASSERT_EQ(e.rfind(b), StringPiece::npos); + ASSERT_EQ(d.rfind(b, 4), StringPiece::npos); + ASSERT_EQ(e.rfind(b, 7), StringPiece::npos); + // empty string nonsense + ASSERT_EQ(d.rfind(d, 4), std::string().rfind(std::string())); + ASSERT_EQ(e.rfind(d, 7), std::string().rfind(std::string())); + ASSERT_EQ(d.rfind(e, 4), std::string().rfind(std::string())); + ASSERT_EQ(e.rfind(e, 7), std::string().rfind(std::string())); + ASSERT_EQ(d.rfind(d), std::string().rfind(std::string())); + ASSERT_EQ(e.rfind(d), std::string().rfind(std::string())); + ASSERT_EQ(d.rfind(e), std::string().rfind(std::string())); + ASSERT_EQ(e.rfind(e), std::string().rfind(std::string())); + + ASSERT_EQ(g.rfind('o'), 8U); + ASSERT_EQ(g.rfind('q'), StringPiece::npos); + ASSERT_EQ(g.rfind('o', 8), 8U); + ASSERT_EQ(g.rfind('o', 7), 4U); + ASSERT_EQ(g.rfind('o', 3), StringPiece::npos); + ASSERT_EQ(f.rfind('\0'), 3U); + ASSERT_EQ(f.rfind('\0', 12), 3U); + ASSERT_EQ(f.rfind('3'), 2U); + ASSERT_EQ(f.rfind('5'), 5U); + // empty string nonsense + ASSERT_EQ(d.rfind('o'), StringPiece::npos); + ASSERT_EQ(e.rfind('o'), StringPiece::npos); + ASSERT_EQ(d.rfind('o', 4), StringPiece::npos); + ASSERT_EQ(e.rfind('o', 7), StringPiece::npos); + + ASSERT_EQ(a.find_first_of(b), 0U); + ASSERT_EQ(a.find_first_of(b, 0), 0U); + ASSERT_EQ(a.find_first_of(b, 1), 1U); + ASSERT_EQ(a.find_first_of(b, 2), 2U); + ASSERT_EQ(a.find_first_of(b, 3), StringPiece::npos); + ASSERT_EQ(a.find_first_of(c), 23U); + ASSERT_EQ(a.find_first_of(c, 23), 23U); + ASSERT_EQ(a.find_first_of(c, 24), 24U); + ASSERT_EQ(a.find_first_of(c, 25), 25U); + ASSERT_EQ(a.find_first_of(c, 26), StringPiece::npos); + ASSERT_EQ(g.find_first_of(b), 13U); + ASSERT_EQ(g.find_first_of(c), 0U); + ASSERT_EQ(a.find_first_of(f), StringPiece::npos); + ASSERT_EQ(f.find_first_of(a), StringPiece::npos); + // empty string nonsense + ASSERT_EQ(a.find_first_of(d), StringPiece::npos); + ASSERT_EQ(a.find_first_of(e), StringPiece::npos); + ASSERT_EQ(d.find_first_of(b), StringPiece::npos); + ASSERT_EQ(e.find_first_of(b), StringPiece::npos); + ASSERT_EQ(d.find_first_of(d), StringPiece::npos); + ASSERT_EQ(e.find_first_of(d), StringPiece::npos); + ASSERT_EQ(d.find_first_of(e), StringPiece::npos); + ASSERT_EQ(e.find_first_of(e), StringPiece::npos); + + ASSERT_EQ(a.find_first_not_of(b), 3U); + ASSERT_EQ(a.find_first_not_of(c), 0U); + ASSERT_EQ(b.find_first_not_of(a), StringPiece::npos); + ASSERT_EQ(c.find_first_not_of(a), StringPiece::npos); + ASSERT_EQ(f.find_first_not_of(a), 0U); + ASSERT_EQ(a.find_first_not_of(f), 0U); + ASSERT_EQ(a.find_first_not_of(d), 0U); + ASSERT_EQ(a.find_first_not_of(e), 0U); + // empty string nonsense + ASSERT_EQ(d.find_first_not_of(a), StringPiece::npos); + ASSERT_EQ(e.find_first_not_of(a), StringPiece::npos); + ASSERT_EQ(d.find_first_not_of(d), StringPiece::npos); + ASSERT_EQ(e.find_first_not_of(d), StringPiece::npos); + ASSERT_EQ(d.find_first_not_of(e), StringPiece::npos); + ASSERT_EQ(e.find_first_not_of(e), StringPiece::npos); + + StringPiece h("===="); + ASSERT_EQ(h.find_first_not_of('='), StringPiece::npos); + ASSERT_EQ(h.find_first_not_of('=', 3), StringPiece::npos); + ASSERT_EQ(h.find_first_not_of('\0'), 0U); + ASSERT_EQ(g.find_first_not_of('x'), 2U); + ASSERT_EQ(f.find_first_not_of('\0'), 0U); + ASSERT_EQ(f.find_first_not_of('\0', 3), 4U); + ASSERT_EQ(f.find_first_not_of('\0', 2), 2U); + // empty string nonsense + ASSERT_EQ(d.find_first_not_of('x'), StringPiece::npos); + ASSERT_EQ(e.find_first_not_of('x'), StringPiece::npos); + ASSERT_EQ(d.find_first_not_of('\0'), StringPiece::npos); + ASSERT_EQ(e.find_first_not_of('\0'), StringPiece::npos); + + // StringPiece g("xx not found bb"); + StringPiece i("56"); + ASSERT_EQ(h.find_last_of(a), StringPiece::npos); + ASSERT_EQ(g.find_last_of(a), g.size()-1); + ASSERT_EQ(a.find_last_of(b), 2U); + ASSERT_EQ(a.find_last_of(c), a.size()-1); + ASSERT_EQ(f.find_last_of(i), 6U); + ASSERT_EQ(a.find_last_of('a'), 0U); + ASSERT_EQ(a.find_last_of('b'), 1U); + ASSERT_EQ(a.find_last_of('z'), 25U); + ASSERT_EQ(a.find_last_of('a', 5), 0U); + ASSERT_EQ(a.find_last_of('b', 5), 1U); + ASSERT_EQ(a.find_last_of('b', 0), StringPiece::npos); + ASSERT_EQ(a.find_last_of('z', 25), 25U); + ASSERT_EQ(a.find_last_of('z', 24), StringPiece::npos); + ASSERT_EQ(f.find_last_of(i, 5), 5U); + ASSERT_EQ(f.find_last_of(i, 6), 6U); + ASSERT_EQ(f.find_last_of(a, 4), StringPiece::npos); + // empty string nonsense + ASSERT_EQ(f.find_last_of(d), StringPiece::npos); + ASSERT_EQ(f.find_last_of(e), StringPiece::npos); + ASSERT_EQ(f.find_last_of(d, 4), StringPiece::npos); + ASSERT_EQ(f.find_last_of(e, 4), StringPiece::npos); + ASSERT_EQ(d.find_last_of(d), StringPiece::npos); + ASSERT_EQ(d.find_last_of(e), StringPiece::npos); + ASSERT_EQ(e.find_last_of(d), StringPiece::npos); + ASSERT_EQ(e.find_last_of(e), StringPiece::npos); + ASSERT_EQ(d.find_last_of(f), StringPiece::npos); + ASSERT_EQ(e.find_last_of(f), StringPiece::npos); + ASSERT_EQ(d.find_last_of(d, 4), StringPiece::npos); + ASSERT_EQ(d.find_last_of(e, 4), StringPiece::npos); + ASSERT_EQ(e.find_last_of(d, 4), StringPiece::npos); + ASSERT_EQ(e.find_last_of(e, 4), StringPiece::npos); + ASSERT_EQ(d.find_last_of(f, 4), StringPiece::npos); + ASSERT_EQ(e.find_last_of(f, 4), StringPiece::npos); + + ASSERT_EQ(a.find_last_not_of(b), a.size()-1); + ASSERT_EQ(a.find_last_not_of(c), 22U); + ASSERT_EQ(b.find_last_not_of(a), StringPiece::npos); + ASSERT_EQ(b.find_last_not_of(b), StringPiece::npos); + ASSERT_EQ(f.find_last_not_of(i), 4U); + ASSERT_EQ(a.find_last_not_of(c, 24), 22U); + ASSERT_EQ(a.find_last_not_of(b, 3), 3U); + ASSERT_EQ(a.find_last_not_of(b, 2), StringPiece::npos); + // empty string nonsense + ASSERT_EQ(f.find_last_not_of(d), f.size()-1); + ASSERT_EQ(f.find_last_not_of(e), f.size()-1); + ASSERT_EQ(f.find_last_not_of(d, 4), 4U); + ASSERT_EQ(f.find_last_not_of(e, 4), 4U); + ASSERT_EQ(d.find_last_not_of(d), StringPiece::npos); + ASSERT_EQ(d.find_last_not_of(e), StringPiece::npos); + ASSERT_EQ(e.find_last_not_of(d), StringPiece::npos); + ASSERT_EQ(e.find_last_not_of(e), StringPiece::npos); + ASSERT_EQ(d.find_last_not_of(f), StringPiece::npos); + ASSERT_EQ(e.find_last_not_of(f), StringPiece::npos); + ASSERT_EQ(d.find_last_not_of(d, 4), StringPiece::npos); + ASSERT_EQ(d.find_last_not_of(e, 4), StringPiece::npos); + ASSERT_EQ(e.find_last_not_of(d, 4), StringPiece::npos); + ASSERT_EQ(e.find_last_not_of(e, 4), StringPiece::npos); + ASSERT_EQ(d.find_last_not_of(f, 4), StringPiece::npos); + ASSERT_EQ(e.find_last_not_of(f, 4), StringPiece::npos); + + ASSERT_EQ(h.find_last_not_of('x'), h.size() - 1); + ASSERT_EQ(h.find_last_not_of('='), StringPiece::npos); + ASSERT_EQ(b.find_last_not_of('c'), 1U); + ASSERT_EQ(h.find_last_not_of('x', 2), 2U); + ASSERT_EQ(h.find_last_not_of('=', 2), StringPiece::npos); + ASSERT_EQ(b.find_last_not_of('b', 1), 0U); + // empty string nonsense + ASSERT_EQ(d.find_last_not_of('x'), StringPiece::npos); + ASSERT_EQ(e.find_last_not_of('x'), StringPiece::npos); + ASSERT_EQ(d.find_last_not_of('\0'), StringPiece::npos); + ASSERT_EQ(e.find_last_not_of('\0'), StringPiece::npos); + + ASSERT_EQ(a.substr(0, 3), b); + ASSERT_EQ(a.substr(23), c); + ASSERT_EQ(a.substr(23, 3), c); + ASSERT_EQ(a.substr(23, 99), c); + ASSERT_EQ(a.substr(0), a); + ASSERT_EQ(a.substr(3, 2), "de"); + // empty string nonsense + ASSERT_EQ(a.substr(99, 2), e); + ASSERT_EQ(d.substr(99), e); + ASSERT_EQ(d.substr(0, 99), e); + ASSERT_EQ(d.substr(99, 99), e); +} + +TEST(StringPieceTest, CheckCustom) { + StringPiece a("foobar"); + std::string s1("123"); + s1 += '\0'; + s1 += "456"; + StringPiece b(s1); + StringPiece e; + std::string s2; + + // CopyToString + a.CopyToString(&s2); + ASSERT_EQ(s2.size(), 6U); + ASSERT_EQ(s2, "foobar"); + b.CopyToString(&s2); + ASSERT_EQ(s2.size(), 7U); + ASSERT_EQ(s1, s2); + e.CopyToString(&s2); + ASSERT_TRUE(s2.empty()); + + // AppendToString + s2.erase(); + a.AppendToString(&s2); + ASSERT_EQ(s2.size(), 6U); + ASSERT_EQ(s2, "foobar"); + a.AppendToString(&s2); + ASSERT_EQ(s2.size(), 12U); + ASSERT_EQ(s2, "foobarfoobar"); + + // starts_with + ASSERT_TRUE(a.starts_with(a)); + ASSERT_TRUE(a.starts_with("foo")); + ASSERT_TRUE(a.starts_with(e)); + ASSERT_TRUE(b.starts_with(s1)); + ASSERT_TRUE(b.starts_with(b)); + ASSERT_TRUE(b.starts_with(e)); + ASSERT_TRUE(e.starts_with("")); + ASSERT_TRUE(!a.starts_with(b)); + ASSERT_TRUE(!b.starts_with(a)); + ASSERT_TRUE(!e.starts_with(a)); + + // ends with + ASSERT_TRUE(a.ends_with(a)); + ASSERT_TRUE(a.ends_with("bar")); + ASSERT_TRUE(a.ends_with(e)); + ASSERT_TRUE(b.ends_with(s1)); + ASSERT_TRUE(b.ends_with(b)); + ASSERT_TRUE(b.ends_with(e)); + ASSERT_TRUE(e.ends_with("")); + ASSERT_TRUE(!a.ends_with(b)); + ASSERT_TRUE(!b.ends_with(a)); + ASSERT_TRUE(!e.ends_with(a)); + + // remove_prefix + StringPiece c(a); + c.remove_prefix(3); + ASSERT_EQ(c, "bar"); + c = a; + c.remove_prefix(0); + ASSERT_EQ(c, a); + c.remove_prefix(c.size()); + ASSERT_EQ(c, e); + + // remove_suffix + c = a; + c.remove_suffix(3); + ASSERT_EQ(c, "foo"); + c = a; + c.remove_suffix(0); + ASSERT_EQ(c, a); + c.remove_suffix(c.size()); + ASSERT_EQ(c, e); + + // set + c.set("foobar", 6); + ASSERT_EQ(c, a); + c.set("foobar", 0); + ASSERT_EQ(c, e); + c.set("foobar", 7); + ASSERT_NE(c, a); + + c.set("foobar"); + ASSERT_EQ(c, a); + + c.set(static_cast("foobar"), 6); + ASSERT_EQ(c, a); + c.set(static_cast("foobar"), 0); + ASSERT_EQ(c, e); + c.set(static_cast("foobar"), 7); + ASSERT_NE(c, a); + + // as_string + std::string s3(a.as_string().c_str(), 7); + ASSERT_EQ(c, s3); + std::string s4(e.as_string()); + ASSERT_TRUE(s4.empty()); +} + +TEST(StringPieceTest, CheckNULL) { + // we used to crash here, but now we don't. + StringPiece s(NULL); + ASSERT_EQ(s.data(), (const char*)NULL); + ASSERT_EQ(s.size(), 0U); + + s.set(NULL); + ASSERT_EQ(s.data(), (const char*)NULL); + ASSERT_EQ(s.size(), 0U); +} + +TEST(StringPieceTest, CheckComparisons2) { + StringPiece abc("abcdefghijklmnopqrstuvwxyz"); + + // check comparison operations on strings longer than 4 bytes. + ASSERT_TRUE(abc == StringPiece("abcdefghijklmnopqrstuvwxyz")); + ASSERT_TRUE(abc.compare(StringPiece("abcdefghijklmnopqrstuvwxyz")) == 0); + + ASSERT_TRUE(abc < StringPiece("abcdefghijklmnopqrstuvwxzz")); + ASSERT_TRUE(abc.compare(StringPiece("abcdefghijklmnopqrstuvwxzz")) < 0); + + ASSERT_TRUE(abc > StringPiece("abcdefghijklmnopqrstuvwxyy")); + ASSERT_TRUE(abc.compare(StringPiece("abcdefghijklmnopqrstuvwxyy")) > 0); + + // starts_with + ASSERT_TRUE(abc.starts_with(abc)); + ASSERT_TRUE(abc.starts_with("abcdefghijklm")); + ASSERT_TRUE(!abc.starts_with("abcdefguvwxyz")); + + // ends_with + ASSERT_TRUE(abc.ends_with(abc)); + ASSERT_TRUE(!abc.ends_with("abcdefguvwxyz")); + ASSERT_TRUE(abc.ends_with("nopqrstuvwxyz")); +} + +TEST(StringPieceTest, StringCompareNotAmbiguous) { + ASSERT_TRUE("hello" == std::string("hello")); + ASSERT_TRUE("hello" < std::string("world")); +} + +TEST(StringPieceTest, HeterogenousStringPieceEquals) { + ASSERT_TRUE(StringPiece("hello") == std::string("hello")); + ASSERT_TRUE("hello" == StringPiece("hello")); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_tokenizer.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_tokenizer.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_tokenizer.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_tokenizer.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,202 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STRING_TOKENIZER_H_ +#define BASE_STRING_TOKENIZER_H_ + +#include + +// StringTokenizerT is a simple string tokenizer class. It works like an +// iterator that with each step (see the Advance method) updates members that +// refer to the next token in the input string. The user may optionally +// configure the tokenizer to return delimiters. +// +// +// EXAMPLE 1: +// +// StringTokenizer t("this is a test", " "); +// while (t.GetNext()) { +// printf("%s\n", t.token().c_str()); +// } +// +// Output: +// +// this +// is +// a +// test +// +// +// EXAMPLE 2: +// +// StringTokenizer t("no-cache=\"foo, bar\", private", ", "); +// t.set_quote_chars("\""); +// while (t.GetNext()) { +// printf("%s\n", t.token().c_str()); +// } +// +// Output: +// +// no-cache="foo, bar" +// private +// +// +// EXAMPLE 3: +// +// bool next_is_option = false, next_is_value = false; +// std::string input = "text/html; charset=UTF-8; foo=bar"; +// StringTokenizer t(input, "; ="); +// t.set_options(StringTokenizer::RETURN_DELIMS); +// while (t.GetNext()) { +// if (t.token_is_delim()) { +// switch (*t.token_begin()) { +// case ';': +// next_is_option = true; +// break; +// case '=': +// next_is_value = true; +// break; +// } +// } else { +// const char* label; +// if (next_is_option) { +// label = "option-name"; +// next_is_option = false; +// } else if (next_is_value) { +// label = "option-value"; +// next_is_value = false; +// } else { +// label = "mime-type"; +// } +// printf("%s: %s\n", label, t.token().c_str()); +// } +// } +// +// +template +class StringTokenizerT { + public: + typedef typename str::value_type char_type; + + // Options that may be pass to set_options() + enum { + // Specifies the delimiters should be returned as tokens + RETURN_DELIMS = 1 << 0, + }; + + StringTokenizerT(const str& string, + const str& delims) { + Init(string.begin(), string.end(), delims); + } + + StringTokenizerT(const_iterator string_begin, + const_iterator string_end, + const str& delims) { + Init(string_begin, string_end, delims); + } + + // Set the options for this tokenizer. By default, this is 0. + void set_options(int options) { options_ = options; } + + // Set the characters to regard as quotes. By default, this is empty. When + // a quote char is encountered, the tokenizer will switch into a mode where + // it ignores delimiters that it finds. It switches out of this mode once it + // finds another instance of the quote char. If a backslash is encountered + // within a quoted string, then the next character is skipped. + void set_quote_chars(const str& quotes) { quotes_ = quotes; } + + // Call this method to advance the tokenizer to the next delimiter. This + // returns false if the tokenizer is complete. This method must be called + // before calling any of the token* methods. + bool GetNext() { + AdvanceState state; + token_is_delim_ = false; + for (;;) { + token_begin_ = token_end_; + if (token_end_ == end_) + return false; + ++token_end_; + if (AdvanceOne(&state, *token_begin_)) + break; + if (options_ & RETURN_DELIMS) { + token_is_delim_ = true; + return true; + } + // else skip over delim + } + while (token_end_ != end_ && AdvanceOne(&state, *token_end_)) + ++token_end_; + return true; + } + + // Returns true if token is a delimiter. When the tokenizer is constructed + // with the RETURN_DELIMS option, this method can be used to check if the + // returned token is actually a delimiter. + bool token_is_delim() const { return token_is_delim_; } + + // If GetNext() returned true, then these methods may be used to read the + // value of the token. + const_iterator token_begin() const { return token_begin_; } + const_iterator token_end() const { return token_end_; } + str token() const { return str(token_begin_, token_end_); } + + private: + void Init(const_iterator string_begin, + const_iterator string_end, + const str& delims) { + token_end_ = string_begin; + end_ = string_end; + delims_ = delims; + options_ = 0; + } + + bool IsDelim(char_type c) const { + return delims_.find(c) != str::npos; + } + + bool IsQuote(char_type c) const { + return quotes_.find(c) != str::npos; + } + + struct AdvanceState { + bool in_quote; + bool in_escape; + char_type quote_char; + AdvanceState() : in_quote(false), in_escape(false) {} + }; + + // Returns true if a delimiter was not hit. + bool AdvanceOne(AdvanceState* state, char_type c) { + if (state->in_quote) { + if (state->in_escape) { + state->in_escape = false; + } else if (c == '\\') { + state->in_escape = true; + } else if (c == state->quote_char) { + state->in_quote = false; + } + } else { + if (IsDelim(c)) + return false; + state->in_quote = IsQuote(state->quote_char = c); + } + return true; + } + + const_iterator token_begin_; + const_iterator token_end_; + const_iterator end_; + str delims_; + str quotes_; + int options_; + bool token_is_delim_; +}; + +typedef StringTokenizerT + StringTokenizer; +typedef StringTokenizerT + WStringTokenizer; +typedef StringTokenizerT CStringTokenizer; + +#endif // BASE_STRING_TOKENIZER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_tokenizer_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_tokenizer_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_tokenizer_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_tokenizer_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,207 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/string_tokenizer.h" +#include "testing/gtest/include/gtest/gtest.h" + +using std::string; + +namespace { +class StringTokenizerTest : public testing::Test {}; +} + +TEST(StringTokenizerTest, Simple) { + string input = "this is a test"; + StringTokenizer t(input, " "); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("this"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("is"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("a"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("test"), t.token()); + + EXPECT_FALSE(t.GetNext()); +} + +TEST(StringTokenizerTest, RetDelims) { + string input = "this is a test"; + StringTokenizer t(input, " "); + t.set_options(StringTokenizer::RETURN_DELIMS); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("this"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string(" "), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("is"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string(" "), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("a"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string(" "), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("test"), t.token()); + + EXPECT_FALSE(t.GetNext()); +} + +TEST(StringTokenizerTest, ManyDelims) { + string input = "this: is, a-test"; + StringTokenizer t(input, ": ,-"); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("this"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("is"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("a"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("test"), t.token()); + + EXPECT_FALSE(t.GetNext()); +} + +TEST(StringTokenizerTest, ParseHeader) { + string input = "Content-Type: text/html ; charset=UTF-8"; + StringTokenizer t(input, ": ;="); + t.set_options(StringTokenizer::RETURN_DELIMS); + + EXPECT_TRUE(t.GetNext()); + EXPECT_FALSE(t.token_is_delim()); + EXPECT_EQ(string("Content-Type"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_TRUE(t.token_is_delim()); + EXPECT_EQ(string(":"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_TRUE(t.token_is_delim()); + EXPECT_EQ(string(" "), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_FALSE(t.token_is_delim()); + EXPECT_EQ(string("text/html"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_TRUE(t.token_is_delim()); + EXPECT_EQ(string(" "), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_TRUE(t.token_is_delim()); + EXPECT_EQ(string(";"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_TRUE(t.token_is_delim()); + EXPECT_EQ(string(" "), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_FALSE(t.token_is_delim()); + EXPECT_EQ(string("charset"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_TRUE(t.token_is_delim()); + EXPECT_EQ(string("="), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_FALSE(t.token_is_delim()); + EXPECT_EQ(string("UTF-8"), t.token()); + + EXPECT_FALSE(t.GetNext()); + EXPECT_FALSE(t.token_is_delim()); +} + +TEST(StringTokenizerTest, ParseQuotedString) { + string input = "foo bar 'hello world' baz"; + StringTokenizer t(input, " "); + t.set_quote_chars("'"); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("foo"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("bar"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("'hello world'"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("baz"), t.token()); + + EXPECT_FALSE(t.GetNext()); +} + +TEST(StringTokenizerTest, ParseQuotedString_Malformed) { + string input = "bar 'hello wo"; + StringTokenizer t(input, " "); + t.set_quote_chars("'"); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("bar"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("'hello wo"), t.token()); + + EXPECT_FALSE(t.GetNext()); +} + +TEST(StringTokenizerTest, ParseQuotedString_Multiple) { + string input = "bar 'hel\"lo\" wo' baz\""; + StringTokenizer t(input, " "); + t.set_quote_chars("'\""); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("bar"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("'hel\"lo\" wo'"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("baz\""), t.token()); + + EXPECT_FALSE(t.GetNext()); +} + +TEST(StringTokenizerTest, ParseQuotedString_EscapedQuotes) { + string input = "foo 'don\\'t do that'"; + StringTokenizer t(input, " "); + t.set_quote_chars("'"); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("foo"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("'don\\'t do that'"), t.token()); + + EXPECT_FALSE(t.GetNext()); +} + +TEST(StringTokenizerTest, ParseQuotedString_EscapedQuotes2) { + string input = "foo='a, b', bar"; + StringTokenizer t(input, ", "); + t.set_quote_chars("'"); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("foo='a, b'"), t.token()); + + EXPECT_TRUE(t.GetNext()); + EXPECT_EQ(string("bar"), t.token()); + + EXPECT_FALSE(t.GetNext()); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_util.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_util.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_util.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_util.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,1747 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/string_util.h" + +#include "build/build_config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/singleton.h" +#include "base/third_party/dmg_fp/dmg_fp.h" + +namespace { + +// Force the singleton used by Empty[W]String[16] to be a unique type. This +// prevents other code that might accidentally use Singleton from +// getting our internal one. +struct EmptyStrings { + EmptyStrings() {} + const std::string s; + const std::wstring ws; + const string16 s16; +}; + +// Hack to convert any char-like type to its unsigned counterpart. +// For example, it will convert char, signed char and unsigned char to unsigned +// char. +template +struct ToUnsigned { + typedef T Unsigned; +}; + +template<> +struct ToUnsigned { + typedef unsigned char Unsigned; +}; +template<> +struct ToUnsigned { + typedef unsigned char Unsigned; +}; +template<> +struct ToUnsigned { +#if defined(WCHAR_T_IS_UTF16) + typedef unsigned short Unsigned; +#elif defined(WCHAR_T_IS_UTF32) + typedef uint32 Unsigned; +#endif +}; +template<> +struct ToUnsigned { + typedef unsigned short Unsigned; +}; + +// Used by ReplaceStringPlaceholders to track the position in the string of +// replaced parameters. +struct ReplacementOffset { + ReplacementOffset(int parameter, size_t offset) + : parameter(parameter), + offset(offset) {} + + // Index of the parameter. + int parameter; + + // Starting position in the string. + size_t offset; +}; + +static bool CompareParameter(const ReplacementOffset& elem1, + const ReplacementOffset& elem2) { + return elem1.parameter < elem2.parameter; +} + +// Generalized string-to-number conversion. +// +// StringToNumberTraits should provide: +// - a typedef for string_type, the STL string type used as input. +// - a typedef for value_type, the target numeric type. +// - a static function, convert_func, which dispatches to an appropriate +// strtol-like function and returns type value_type. +// - a static function, valid_func, which validates |input| and returns a bool +// indicating whether it is in proper form. This is used to check for +// conditions that convert_func tolerates but should result in +// StringToNumber returning false. For strtol-like funtions, valid_func +// should check for leading whitespace. +template +bool StringToNumber(const typename StringToNumberTraits::string_type& input, + typename StringToNumberTraits::value_type* output) { + typedef StringToNumberTraits traits; + + errno = 0; // Thread-safe? It is on at least Mac, Linux, and Windows. + typename traits::string_type::value_type* endptr = NULL; + typename traits::value_type value = traits::convert_func(input.c_str(), + &endptr); + *output = value; + + // Cases to return false: + // - If errno is ERANGE, there was an overflow or underflow. + // - If the input string is empty, there was nothing to parse. + // - If endptr does not point to the end of the string, there are either + // characters remaining in the string after a parsed number, or the string + // does not begin with a parseable number. endptr is compared to the + // expected end given the string's stated length to correctly catch cases + // where the string contains embedded NUL characters. + // - valid_func determines that the input is not in preferred form. + return errno == 0 && + !input.empty() && + input.c_str() + input.length() == endptr && + traits::valid_func(input); +} + +class StringToLongTraits { + public: + typedef std::string string_type; + typedef long value_type; + static const int kBase = 10; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { + return strtol(str, endptr, kBase); + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !isspace(str[0]); + } +}; + +class String16ToLongTraits { + public: + typedef string16 string_type; + typedef long value_type; + static const int kBase = 10; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { +#if defined(WCHAR_T_IS_UTF16) + return wcstol(str, endptr, kBase); +#elif defined(WCHAR_T_IS_UTF32) + std::string ascii_string = UTF16ToASCII(string16(str)); + char* ascii_end = NULL; + value_type ret = strtol(ascii_string.c_str(), &ascii_end, kBase); + if (ascii_string.c_str() + ascii_string.length() == ascii_end) { + *endptr = + const_cast(str) + ascii_string.length(); + } + return ret; +#endif + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !iswspace(str[0]); + } +}; + +class StringToInt64Traits { + public: + typedef std::string string_type; + typedef int64 value_type; + static const int kBase = 10; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { +#ifdef OS_WIN + return _strtoi64(str, endptr, kBase); +#else // assume OS_POSIX + return strtoll(str, endptr, kBase); +#endif + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !isspace(str[0]); + } +}; + +class String16ToInt64Traits { + public: + typedef string16 string_type; + typedef int64 value_type; + static const int kBase = 10; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { +#ifdef OS_WIN + return _wcstoi64(str, endptr, kBase); +#else // assume OS_POSIX + std::string ascii_string = UTF16ToASCII(string16(str)); + char* ascii_end = NULL; + value_type ret = strtoll(ascii_string.c_str(), &ascii_end, kBase); + if (ascii_string.c_str() + ascii_string.length() == ascii_end) { + *endptr = + const_cast(str) + ascii_string.length(); + } + return ret; +#endif + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !iswspace(str[0]); + } +}; + +// For the HexString variants, use the unsigned variants like strtoul for +// convert_func so that input like "0x80000000" doesn't result in an overflow. + +class HexStringToLongTraits { + public: + typedef std::string string_type; + typedef long value_type; + static const int kBase = 16; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { + return strtoul(str, endptr, kBase); + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !isspace(str[0]); + } +}; + +class HexString16ToLongTraits { + public: + typedef string16 string_type; + typedef long value_type; + static const int kBase = 16; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { +#if defined(WCHAR_T_IS_UTF16) + return wcstoul(str, endptr, kBase); +#elif defined(WCHAR_T_IS_UTF32) + std::string ascii_string = UTF16ToASCII(string16(str)); + char* ascii_end = NULL; + value_type ret = strtoul(ascii_string.c_str(), &ascii_end, kBase); + if (ascii_string.c_str() + ascii_string.length() == ascii_end) { + *endptr = + const_cast(str) + ascii_string.length(); + } + return ret; +#endif + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !iswspace(str[0]); + } +}; + +class StringToDoubleTraits { + public: + typedef std::string string_type; + typedef double value_type; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { + return dmg_fp::strtod(str, endptr); + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !isspace(str[0]); + } +}; + +class String16ToDoubleTraits { + public: + typedef string16 string_type; + typedef double value_type; + static inline value_type convert_func(const string_type::value_type* str, + string_type::value_type** endptr) { + // Because dmg_fp::strtod does not like char16, we convert it to ASCII. + // In theory, this should be safe, but it's possible that 16-bit chars + // might get ignored by accident causing something to be parsed when it + // shouldn't. + std::string ascii_string = UTF16ToASCII(string16(str)); + char* ascii_end = NULL; + value_type ret = dmg_fp::strtod(ascii_string.c_str(), &ascii_end); + if (ascii_string.c_str() + ascii_string.length() == ascii_end) { + // Put endptr at end of input string, so it's not recognized as an error. + *endptr = + const_cast(str) + ascii_string.length(); + } + + return ret; + } + static inline bool valid_func(const string_type& str) { + return !str.empty() && !iswspace(str[0]); + } +}; + +} // namespace + + +namespace base { + +bool IsWprintfFormatPortable(const wchar_t* format) { + for (const wchar_t* position = format; *position != '\0'; ++position) { + + if (*position == '%') { + bool in_specification = true; + bool modifier_l = false; + while (in_specification) { + // Eat up characters until reaching a known specifier. + if (*++position == '\0') { + // The format string ended in the middle of a specification. Call + // it portable because no unportable specifications were found. The + // string is equally broken on all platforms. + return true; + } + + if (*position == 'l') { + // 'l' is the only thing that can save the 's' and 'c' specifiers. + modifier_l = true; + } else if (((*position == 's' || *position == 'c') && !modifier_l) || + *position == 'S' || *position == 'C' || *position == 'F' || + *position == 'D' || *position == 'O' || *position == 'U') { + // Not portable. + return false; + } + + if (wcschr(L"diouxXeEfgGaAcspn%", *position)) { + // Portable, keep scanning the rest of the format string. + in_specification = false; + } + } + } + + } + + return true; +} + + +} // namespace base + +#ifdef CHROMIUM_MOZILLA_BUILD +namespace base { +#endif + +const std::string& EmptyString() { + return Singleton::get()->s; +} + +const std::wstring& EmptyWString() { + return Singleton::get()->ws; +} + +const string16& EmptyString16() { + return Singleton::get()->s16; +} + +#ifdef CHROMIUM_MOZILLA_BUILD +} +#endif + +const wchar_t kWhitespaceWide[] = { + 0x0009, // to + 0x000A, + 0x000B, + 0x000C, + 0x000D, + 0x0020, // Space + 0x0085, // + 0x00A0, // No-Break Space + 0x1680, // Ogham Space Mark + 0x180E, // Mongolian Vowel Separator + 0x2000, // En Quad to Hair Space + 0x2001, + 0x2002, + 0x2003, + 0x2004, + 0x2005, + 0x2006, + 0x2007, + 0x2008, + 0x2009, + 0x200A, + 0x200C, // Zero Width Non-Joiner + 0x2028, // Line Separator + 0x2029, // Paragraph Separator + 0x202F, // Narrow No-Break Space + 0x205F, // Medium Mathematical Space + 0x3000, // Ideographic Space + 0 +}; +const char kWhitespaceASCII[] = { + 0x09, // to + 0x0A, + 0x0B, + 0x0C, + 0x0D, + 0x20, // Space + 0 +}; +const char* const kCodepageUTF8 = "UTF-8"; + +template +TrimPositions TrimStringT(const STR& input, + const typename STR::value_type trim_chars[], + TrimPositions positions, + STR* output) { + // Find the edges of leading/trailing whitespace as desired. + const typename STR::size_type last_char = input.length() - 1; + const typename STR::size_type first_good_char = (positions & TRIM_LEADING) ? + input.find_first_not_of(trim_chars) : 0; + const typename STR::size_type last_good_char = (positions & TRIM_TRAILING) ? + input.find_last_not_of(trim_chars) : last_char; + + // When the string was all whitespace, report that we stripped off whitespace + // from whichever position the caller was interested in. For empty input, we + // stripped no whitespace, but we still need to clear |output|. + if (input.empty() || + (first_good_char == STR::npos) || (last_good_char == STR::npos)) { + bool input_was_empty = input.empty(); // in case output == &input + output->clear(); + return input_was_empty ? TRIM_NONE : positions; + } + + // Trim the whitespace. + *output = + input.substr(first_good_char, last_good_char - first_good_char + 1); + + // Return where we trimmed from. + return static_cast( + ((first_good_char == 0) ? TRIM_NONE : TRIM_LEADING) | + ((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING)); +} + +bool TrimString(const std::wstring& input, + const wchar_t trim_chars[], + std::wstring* output) { + return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE; +} + +bool TrimString(const std::string& input, + const char trim_chars[], + std::string* output) { + return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE; +} + +TrimPositions TrimWhitespace(const std::wstring& input, + TrimPositions positions, + std::wstring* output) { + return TrimStringT(input, kWhitespaceWide, positions, output); +} + +TrimPositions TrimWhitespaceASCII(const std::string& input, + TrimPositions positions, + std::string* output) { + return TrimStringT(input, kWhitespaceASCII, positions, output); +} + +// This function is only for backward-compatibility. +// To be removed when all callers are updated. +TrimPositions TrimWhitespace(const std::string& input, + TrimPositions positions, + std::string* output) { + return TrimWhitespaceASCII(input, positions, output); +} + +std::wstring CollapseWhitespace(const std::wstring& text, + bool trim_sequences_with_line_breaks) { + std::wstring result; + result.resize(text.size()); + + // Set flags to pretend we're already in a trimmed whitespace sequence, so we + // will trim any leading whitespace. + bool in_whitespace = true; + bool already_trimmed = true; + + int chars_written = 0; + for (std::wstring::const_iterator i(text.begin()); i != text.end(); ++i) { + if (IsWhitespace(*i)) { + if (!in_whitespace) { + // Reduce all whitespace sequences to a single space. + in_whitespace = true; + result[chars_written++] = L' '; + } + if (trim_sequences_with_line_breaks && !already_trimmed && + ((*i == '\n') || (*i == '\r'))) { + // Whitespace sequences containing CR or LF are eliminated entirely. + already_trimmed = true; + --chars_written; + } + } else { + // Non-whitespace chracters are copied straight across. + in_whitespace = false; + already_trimmed = false; + result[chars_written++] = *i; + } + } + + if (in_whitespace && !already_trimmed) { + // Any trailing whitespace is eliminated. + --chars_written; + } + + result.resize(chars_written); + return result; +} + +std::string WideToASCII(const std::wstring& wide) { + DCHECK(IsStringASCII(wide)); + return std::string(wide.begin(), wide.end()); +} + +std::wstring ASCIIToWide(const std::string& ascii) { + DCHECK(IsStringASCII(ascii)); + return std::wstring(ascii.begin(), ascii.end()); +} + +std::string UTF16ToASCII(const string16& utf16) { + DCHECK(IsStringASCII(utf16)); + return std::string(utf16.begin(), utf16.end()); +} + +string16 ASCIIToUTF16(const std::string& ascii) { + DCHECK(IsStringASCII(ascii)); + return string16(ascii.begin(), ascii.end()); +} + +// Latin1 is just the low range of Unicode, so we can copy directly to convert. +bool WideToLatin1(const std::wstring& wide, std::string* latin1) { + std::string output; + output.resize(wide.size()); + latin1->clear(); + for (size_t i = 0; i < wide.size(); i++) { + if (wide[i] > 255) + return false; + output[i] = static_cast(wide[i]); + } + latin1->swap(output); + return true; +} + +bool IsString8Bit(const std::wstring& str) { + for (size_t i = 0; i < str.length(); i++) { + if (str[i] > 255) + return false; + } + return true; +} + +template +static bool DoIsStringASCII(const STR& str) { + for (size_t i = 0; i < str.length(); i++) { + typename ToUnsigned::Unsigned c = str[i]; + if (c > 0x7F) + return false; + } + return true; +} + +bool IsStringASCII(const std::wstring& str) { + return DoIsStringASCII(str); +} + +#if !defined(WCHAR_T_IS_UTF16) +bool IsStringASCII(const string16& str) { + return DoIsStringASCII(str); +} +#endif + +bool IsStringASCII(const std::string& str) { + return DoIsStringASCII(str); +} + +// Helper functions that determine whether the given character begins a +// UTF-8 sequence of bytes with the given length. A character satisfies +// "IsInUTF8Sequence" if it is anything but the first byte in a multi-byte +// character. +static inline bool IsBegin2ByteUTF8(int c) { + return (c & 0xE0) == 0xC0; +} +static inline bool IsBegin3ByteUTF8(int c) { + return (c & 0xF0) == 0xE0; +} +static inline bool IsBegin4ByteUTF8(int c) { + return (c & 0xF8) == 0xF0; +} +static inline bool IsInUTF8Sequence(int c) { + return (c & 0xC0) == 0x80; +} + +// This function was copied from Mozilla, with modifications. The original code +// was 'IsUTF8' in xpcom/string/src/nsReadableUtils.cpp. The license block for +// this function is: +// This function subject to the Mozilla Public License Version +// 1.1 (the "License"); you may not use this code except in compliance with +// the License. You may obtain a copy of the License at +// http://www.mozilla.org/MPL/ +// +// Software distributed under the License is distributed on an "AS IS" basis, +// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +// for the specific language governing rights and limitations under the +// License. +// +// The Original Code is mozilla.org code. +// +// The Initial Developer of the Original Code is +// Netscape Communications Corporation. +// Portions created by the Initial Developer are Copyright (C) 2000 +// the Initial Developer. All Rights Reserved. +// +// Contributor(s): +// Scott Collins (original author) +// +// This is a template so that it can be run on wide and 8-bit strings. We want +// to run it on wide strings when we have input that we think may have +// originally been UTF-8, but has been converted to wide characters because +// that's what we (and Windows) use internally. +template +static bool IsStringUTF8T(const CHAR* str, int length) { + bool overlong = false; + bool surrogate = false; + bool nonchar = false; + + // overlong byte upper bound + typename ToUnsigned::Unsigned olupper = 0; + + // surrogate byte lower bound + typename ToUnsigned::Unsigned slower = 0; + + // incremented when inside a multi-byte char to indicate how many bytes + // are left in the sequence + int positions_left = 0; + + for (int i = 0; i < length; i++) { + // This whole function assume an unsigned value so force its conversion to + // an unsigned value. + typename ToUnsigned::Unsigned c = str[i]; + if (c < 0x80) + continue; // ASCII + + if (c <= 0xC1) { + // [80-BF] where not expected, [C0-C1] for overlong + return false; + } else if (IsBegin2ByteUTF8(c)) { + positions_left = 1; + } else if (IsBegin3ByteUTF8(c)) { + positions_left = 2; + if (c == 0xE0) { + // to exclude E0[80-9F][80-BF] + overlong = true; + olupper = 0x9F; + } else if (c == 0xED) { + // ED[A0-BF][80-BF]: surrogate codepoint + surrogate = true; + slower = 0xA0; + } else if (c == 0xEF) { + // EF BF [BE-BF] : non-character + // TODO(jungshik): EF B7 [90-AF] should be checked as well. + nonchar = true; + } + } else if (c <= 0xF4) { + positions_left = 3; + nonchar = true; + if (c == 0xF0) { + // to exclude F0[80-8F][80-BF]{2} + overlong = true; + olupper = 0x8F; + } else if (c == 0xF4) { + // to exclude F4[90-BF][80-BF] + // actually not surrogates but codepoints beyond 0x10FFFF + surrogate = true; + slower = 0x90; + } + } else { + return false; + } + + // eat the rest of this multi-byte character + while (positions_left) { + positions_left--; + i++; + c = str[i]; + if (!c) + return false; // end of string but not end of character sequence + + // non-character : EF BF [BE-BF] or F[0-7] [89AB]F BF [BE-BF] + if (nonchar && ((!positions_left && c < 0xBE) || + (positions_left == 1 && c != 0xBF) || + (positions_left == 2 && 0x0F != (0x0F & c) ))) { + nonchar = false; + } + if (!IsInUTF8Sequence(c) || (overlong && c <= olupper) || + (surrogate && slower <= c) || (nonchar && !positions_left) ) { + return false; + } + overlong = surrogate = false; + } + } + return true; +} + +bool IsStringUTF8(const std::string& str) { + return IsStringUTF8T(str.data(), str.length()); +} + +bool IsStringWideUTF8(const std::wstring& str) { + return IsStringUTF8T(str.data(), str.length()); +} + +template +static inline bool DoLowerCaseEqualsASCII(Iter a_begin, + Iter a_end, + const char* b) { + for (Iter it = a_begin; it != a_end; ++it, ++b) { + if (!*b || ToLowerASCII(*it) != *b) + return false; + } + return *b == 0; +} + +// Front-ends for LowerCaseEqualsASCII. +bool LowerCaseEqualsASCII(const std::string& a, const char* b) { + return DoLowerCaseEqualsASCII(a.begin(), a.end(), b); +} + +bool LowerCaseEqualsASCII(const std::wstring& a, const char* b) { + return DoLowerCaseEqualsASCII(a.begin(), a.end(), b); +} + +bool LowerCaseEqualsASCII(std::string::const_iterator a_begin, + std::string::const_iterator a_end, + const char* b) { + return DoLowerCaseEqualsASCII(a_begin, a_end, b); +} + +bool LowerCaseEqualsASCII(std::wstring::const_iterator a_begin, + std::wstring::const_iterator a_end, + const char* b) { + return DoLowerCaseEqualsASCII(a_begin, a_end, b); +} +bool LowerCaseEqualsASCII(const char* a_begin, + const char* a_end, + const char* b) { + return DoLowerCaseEqualsASCII(a_begin, a_end, b); +} +bool LowerCaseEqualsASCII(const wchar_t* a_begin, + const wchar_t* a_end, + const char* b) { + return DoLowerCaseEqualsASCII(a_begin, a_end, b); +} + +bool StartsWithASCII(const std::string& str, + const std::string& search, + bool case_sensitive) { + if (case_sensitive) + return str.compare(0, search.length(), search) == 0; + else + return base::strncasecmp(str.c_str(), search.c_str(), search.length()) == 0; +} + +bool StartsWith(const std::wstring& str, + const std::wstring& search, + bool case_sensitive) { + if (case_sensitive) + return str.compare(0, search.length(), search) == 0; + else { + if (search.size() > str.size()) + return false; + return std::equal(search.begin(), search.end(), str.begin(), +#if defined(CHROMIUM_MOZILLA_BUILD) + chromium_CaseInsensitiveCompare()); +#else + CaseInsensitiveCompare()); +#endif + } +} + +DataUnits GetByteDisplayUnits(int64 bytes) { + // The byte thresholds at which we display amounts. A byte count is displayed + // in unit U when kUnitThresholds[U] <= bytes < kUnitThresholds[U+1]. + // This must match the DataUnits enum. + static const int64 kUnitThresholds[] = { + 0, // DATA_UNITS_BYTE, + 3*1024, // DATA_UNITS_KILOBYTE, + 2*1024*1024, // DATA_UNITS_MEGABYTE, + 1024*1024*1024 // DATA_UNITS_GIGABYTE, + }; + + if (bytes < 0) { + NOTREACHED() << "Negative bytes value"; + return DATA_UNITS_BYTE; + } + + int unit_index = arraysize(kUnitThresholds); + while (--unit_index > 0) { + if (bytes >= kUnitThresholds[unit_index]) + break; + } + + DCHECK(unit_index >= DATA_UNITS_BYTE && unit_index <= DATA_UNITS_GIGABYTE); + return DataUnits(unit_index); +} + +// TODO(mpcomplete): deal with locale +// Byte suffixes. This must match the DataUnits enum. +static const wchar_t* const kByteStrings[] = { + L"B", + L"kB", + L"MB", + L"GB" +}; + +static const wchar_t* const kSpeedStrings[] = { + L"B/s", + L"kB/s", + L"MB/s", + L"GB/s" +}; + +std::wstring FormatBytesInternal(int64 bytes, + DataUnits units, + bool show_units, + const wchar_t* const* suffix) { + if (bytes < 0) { + NOTREACHED() << "Negative bytes value"; + return std::wstring(); + } + + DCHECK(units >= DATA_UNITS_BYTE && units <= DATA_UNITS_GIGABYTE); + + // Put the quantity in the right units. + double unit_amount = static_cast(bytes); + for (int i = 0; i < units; ++i) + unit_amount /= 1024.0; + + wchar_t tmp[64]; + // If the first decimal digit is 0, don't show it. + double int_part; + double fractional_part = modf(unit_amount, &int_part); + modf(fractional_part * 10, &int_part); + if (int_part == 0) { + base::swprintf(tmp, arraysize(tmp), + L"%lld", static_cast(unit_amount)); + } else { + base::swprintf(tmp, arraysize(tmp), L"%.1lf", unit_amount); + } + + std::wstring ret(tmp); + if (show_units) { + ret += L" "; + ret += suffix[units]; + } + + return ret; +} + +std::wstring FormatBytes(int64 bytes, DataUnits units, bool show_units) { + return FormatBytesInternal(bytes, units, show_units, kByteStrings); +} + +std::wstring FormatSpeed(int64 bytes, DataUnits units, bool show_units) { + return FormatBytesInternal(bytes, units, show_units, kSpeedStrings); +} + +template +void DoReplaceSubstringsAfterOffset(StringType* str, + typename StringType::size_type start_offset, + const StringType& find_this, + const StringType& replace_with, + bool replace_all) { + if ((start_offset == StringType::npos) || (start_offset >= str->length())) + return; + + DCHECK(!find_this.empty()); + for (typename StringType::size_type offs(str->find(find_this, start_offset)); + offs != StringType::npos; offs = str->find(find_this, offs)) { + str->replace(offs, find_this.length(), replace_with); + offs += replace_with.length(); + + if (!replace_all) + break; + } +} + +void ReplaceFirstSubstringAfterOffset(string16* str, + string16::size_type start_offset, + const string16& find_this, + const string16& replace_with) { + DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, + false); // replace first instance +} + +void ReplaceFirstSubstringAfterOffset(std::string* str, + std::string::size_type start_offset, + const std::string& find_this, + const std::string& replace_with) { + DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, + false); // replace first instance +} + +void ReplaceSubstringsAfterOffset(string16* str, + string16::size_type start_offset, + const string16& find_this, + const string16& replace_with) { + DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, + true); // replace all instances +} + +void ReplaceSubstringsAfterOffset(std::string* str, + std::string::size_type start_offset, + const std::string& find_this, + const std::string& replace_with) { + DoReplaceSubstringsAfterOffset(str, start_offset, find_this, replace_with, + true); // replace all instances +} + +// Overloaded wrappers around vsnprintf and vswprintf. The buf_size parameter +// is the size of the buffer. These return the number of characters in the +// formatted string excluding the NUL terminator. If the buffer is not +// large enough to accommodate the formatted string without truncation, they +// return the number of characters that would be in the fully-formatted string +// (vsnprintf, and vswprintf on Windows), or -1 (vswprintf on POSIX platforms). +inline int vsnprintfT(char* buffer, + size_t buf_size, + const char* format, + va_list argptr) { + return base::vsnprintf(buffer, buf_size, format, argptr); +} + +inline int vsnprintfT(wchar_t* buffer, + size_t buf_size, + const wchar_t* format, + va_list argptr) { + return base::vswprintf(buffer, buf_size, format, argptr); +} + +// Templatized backend for StringPrintF/StringAppendF. This does not finalize +// the va_list, the caller is expected to do that. +template +static void StringAppendVT(StringType* dst, + const typename StringType::value_type* format, + va_list ap) { + // First try with a small fixed size buffer. + // This buffer size should be kept in sync with StringUtilTest.GrowBoundary + // and StringUtilTest.StringPrintfBounds. + typename StringType::value_type stack_buf[1024]; + + va_list backup_ap; +#if !defined(CHROMIUM_MOZILLA_BUILD) + base::va_copy(backup_ap, ap); +#else + base_va_copy(backup_ap, ap); +#endif // !defined(CHROMIUM_MOZILLA_BUILD) + +#if !defined(OS_WIN) + errno = 0; +#endif + int result = vsnprintfT(stack_buf, arraysize(stack_buf), format, backup_ap); + va_end(backup_ap); + + if (result >= 0 && result < static_cast(arraysize(stack_buf))) { + // It fit. + dst->append(stack_buf, result); + return; + } + + // Repeatedly increase buffer size until it fits. + int mem_length = arraysize(stack_buf); + while (true) { + if (result < 0) { +#if !defined(OS_WIN) + // On Windows, vsnprintfT always returns the number of characters in a + // fully-formatted string, so if we reach this point, something else is + // wrong and no amount of buffer-doubling is going to fix it. + if (errno != 0 && errno != EOVERFLOW) +#endif + { + // If an error other than overflow occurred, it's never going to work. + DLOG(WARNING) << "Unable to printf the requested string due to error."; + return; + } + // Try doubling the buffer size. + mem_length *= 2; + } else { + // We need exactly "result + 1" characters. + mem_length = result + 1; + } + + if (mem_length > 32 * 1024 * 1024) { + // That should be plenty, don't try anything larger. This protects + // against huge allocations when using vsnprintfT implementations that + // return -1 for reasons other than overflow without setting errno. + DLOG(WARNING) << "Unable to printf the requested string due to size."; + return; + } + + std::vector mem_buf(mem_length); + + // Restore the va_list before we use it again. +#if !defined(CHROMIUM_MOZILLA_BUILD) + base::va_copy(backup_ap, ap); +#else + base_va_copy(backup_ap, ap); +#endif // !defined(CHROMIUM_MOZILLA_BUILD) + + result = vsnprintfT(&mem_buf[0], mem_length, format, ap); + va_end(backup_ap); + + if ((result >= 0) && (result < mem_length)) { + // It fit. + dst->append(&mem_buf[0], result); + return; + } + } +} + +namespace { + +template +struct IntToStringT { + + // This is to avoid a compiler warning about unary minus on unsigned type. + // For example, say you had the following code: + // template + // INT abs(INT value) { return value < 0 ? -value : value; } + // Even though if INT is unsigned, it's impossible for value < 0, so the + // unary minus will never be taken, the compiler will still generate a + // warning. We do a little specialization dance... + template + struct ToUnsignedT { }; + + template + struct ToUnsignedT { + static UINT2 ToUnsigned(INT2 value) { + return static_cast(value); + } + }; + + template + struct ToUnsignedT { + static UINT2 ToUnsigned(INT2 value) { + return static_cast(value < 0 ? -value : value); + } + }; + + static STR IntToString(INT value) { + // log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4. + // So round up to allocate 3 output characters per byte, plus 1 for '-'. + const int kOutputBufSize = 3 * sizeof(INT) + 1; + + // Allocate the whole string right away, we will right back to front, and + // then return the substr of what we ended up using. + STR outbuf(kOutputBufSize, 0); + + bool is_neg = value < 0; + // Even though is_neg will never be true when INT is parameterized as + // unsigned, even the presence of the unary operation causes a warning. + UINT res = ToUnsignedT::ToUnsigned(value); + + for (typename STR::iterator it = outbuf.end();;) { + --it; + DCHECK(it != outbuf.begin()); + *it = static_cast((res % 10) + '0'); + res /= 10; + + // We're done.. + if (res == 0) { + if (is_neg) { + --it; + DCHECK(it != outbuf.begin()); + *it = static_cast('-'); + } + return STR(it, outbuf.end()); + } + } + NOTREACHED(); + return STR(); + } +}; + +} + +std::string IntToString(int value) { + return IntToStringT:: + IntToString(value); +} +std::wstring IntToWString(int value) { + return IntToStringT:: + IntToString(value); +} +std::string UintToString(unsigned int value) { + return IntToStringT:: + IntToString(value); +} +std::wstring UintToWString(unsigned int value) { + return IntToStringT:: + IntToString(value); +} +std::string Int64ToString(int64 value) { + return IntToStringT:: + IntToString(value); +} +std::wstring Int64ToWString(int64 value) { + return IntToStringT:: + IntToString(value); +} +std::string Uint64ToString(uint64 value) { + return IntToStringT:: + IntToString(value); +} +std::wstring Uint64ToWString(uint64 value) { + return IntToStringT:: + IntToString(value); +} + +std::string DoubleToString(double value) { + // According to g_fmt.cc, it is sufficient to declare a buffer of size 32. + char buffer[32]; + dmg_fp::g_fmt(buffer, value); + return std::string(buffer); +} + +std::wstring DoubleToWString(double value) { + return ASCIIToWide(DoubleToString(value)); +} + +void StringAppendV(std::string* dst, const char* format, va_list ap) { + StringAppendVT(dst, format, ap); +} + +void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap) { + StringAppendVT(dst, format, ap); +} + +std::string StringPrintf(const char* format, ...) { + va_list ap; + va_start(ap, format); + std::string result; + StringAppendV(&result, format, ap); + va_end(ap); + return result; +} + +std::wstring StringPrintf(const wchar_t* format, ...) { + va_list ap; + va_start(ap, format); + std::wstring result; + StringAppendV(&result, format, ap); + va_end(ap); + return result; +} + +const std::string& SStringPrintf(std::string* dst, const char* format, ...) { + va_list ap; + va_start(ap, format); + dst->clear(); + StringAppendV(dst, format, ap); + va_end(ap); + return *dst; +} + +const std::wstring& SStringPrintf(std::wstring* dst, + const wchar_t* format, ...) { + va_list ap; + va_start(ap, format); + dst->clear(); + StringAppendV(dst, format, ap); + va_end(ap); + return *dst; +} + +void StringAppendF(std::string* dst, const char* format, ...) { + va_list ap; + va_start(ap, format); + StringAppendV(dst, format, ap); + va_end(ap); +} + +void StringAppendF(std::wstring* dst, const wchar_t* format, ...) { + va_list ap; + va_start(ap, format); + StringAppendV(dst, format, ap); + va_end(ap); +} + +template +static void SplitStringT(const STR& str, + const typename STR::value_type s, + bool trim_whitespace, + std::vector* r) { + size_t last = 0; + size_t i; + size_t c = str.size(); + for (i = 0; i <= c; ++i) { + if (i == c || str[i] == s) { + size_t len = i - last; + STR tmp = str.substr(last, len); + if (trim_whitespace) { + STR t_tmp; + TrimWhitespace(tmp, TRIM_ALL, &t_tmp); + r->push_back(t_tmp); + } else { + r->push_back(tmp); + } + last = i + 1; + } + } +} + +void SplitString(const std::wstring& str, + wchar_t s, + std::vector* r) { + SplitStringT(str, s, true, r); +} + +void SplitString(const std::string& str, + char s, + std::vector* r) { + SplitStringT(str, s, true, r); +} + +void SplitStringDontTrim(const std::wstring& str, + wchar_t s, + std::vector* r) { + SplitStringT(str, s, false, r); +} + +void SplitStringDontTrim(const std::string& str, + char s, + std::vector* r) { + SplitStringT(str, s, false, r); +} + +template +static STR JoinStringT(const std::vector& parts, + typename STR::value_type sep) { + if (parts.size() == 0) return STR(); + + STR result(parts[0]); + typename std::vector::const_iterator iter = parts.begin(); + ++iter; + + for (; iter != parts.end(); ++iter) { + result += sep; + result += *iter; + } + + return result; +} + +std::string JoinString(const std::vector& parts, char sep) { + return JoinStringT(parts, sep); +} + +std::wstring JoinString(const std::vector& parts, wchar_t sep) { + return JoinStringT(parts, sep); +} + +void SplitStringAlongWhitespace(const std::wstring& str, + std::vector* result) { + const size_t length = str.length(); + if (!length) + return; + + bool last_was_ws = false; + size_t last_non_ws_start = 0; + for (size_t i = 0; i < length; ++i) { + switch(str[i]) { + // HTML 5 defines whitespace as: space, tab, LF, line tab, FF, or CR. + case L' ': + case L'\t': + case L'\xA': + case L'\xB': + case L'\xC': + case L'\xD': + if (!last_was_ws) { + if (i > 0) { + result->push_back( + str.substr(last_non_ws_start, i - last_non_ws_start)); + } + last_was_ws = true; + } + break; + + default: // Not a space character. + if (last_was_ws) { + last_was_ws = false; + last_non_ws_start = i; + } + break; + } + } + if (!last_was_ws) { + result->push_back( + str.substr(last_non_ws_start, length - last_non_ws_start)); + } +} + +string16 ReplaceStringPlaceholders(const string16& format_string, + const string16& a, + size_t* offset) { + std::vector offsets; + string16 result = ReplaceStringPlaceholders(format_string, a, + string16(), + string16(), + string16(), &offsets); + DCHECK(offsets.size() == 1); + if (offset) { + *offset = offsets[0]; + } + return result; +} + +string16 ReplaceStringPlaceholders(const string16& format_string, + const string16& a, + const string16& b, + std::vector* offsets) { + return ReplaceStringPlaceholders(format_string, a, b, string16(), + string16(), offsets); +} + +string16 ReplaceStringPlaceholders(const string16& format_string, + const string16& a, + const string16& b, + const string16& c, + std::vector* offsets) { + return ReplaceStringPlaceholders(format_string, a, b, c, string16(), + offsets); +} + +string16 ReplaceStringPlaceholders(const string16& format_string, + const string16& a, + const string16& b, + const string16& c, + const string16& d, + std::vector* offsets) { + // We currently only support up to 4 place holders ($1 through $4), although + // it's easy enough to add more. + const string16* subst_texts[] = { &a, &b, &c, &d }; + + string16 formatted; + formatted.reserve(format_string.length() + a.length() + + b.length() + c.length() + d.length()); + + std::vector r_offsets; + + // Replace $$ with $ and $1-$4 with placeholder text if it exists. + for (string16::const_iterator i = format_string.begin(); + i != format_string.end(); ++i) { + if ('$' == *i) { + if (i + 1 != format_string.end()) { + ++i; + DCHECK('$' == *i || ('1' <= *i && *i <= '4')) << + "Invalid placeholder: " << *i; + if ('$' == *i) { + formatted.push_back('$'); + } else { + int index = *i - '1'; + if (offsets) { + ReplacementOffset r_offset(index, + static_cast(formatted.size())); + r_offsets.insert(std::lower_bound(r_offsets.begin(), + r_offsets.end(), r_offset, + &CompareParameter), + r_offset); + } + formatted.append(*subst_texts[index]); + } + } + } else { + formatted.push_back(*i); + } + } + if (offsets) { + for (std::vector::const_iterator i = r_offsets.begin(); + i != r_offsets.end(); ++i) { + offsets->push_back(i->offset); + } + } + return formatted; +} + +template +static bool IsWildcard(CHAR character) { + return character == '*' || character == '?'; +} + +// Move the strings pointers to the point where they start to differ. +template +static void EatSameChars(const CHAR** pattern, const CHAR** string) { + bool escaped = false; + while (**pattern && **string) { + if (!escaped && IsWildcard(**pattern)) { + // We don't want to match wildcard here, except if it's escaped. + return; + } + + // Check if the escapement char is found. If so, skip it and move to the + // next character. + if (!escaped && **pattern == L'\\') { + escaped = true; + (*pattern)++; + continue; + } + + // Check if the chars match, if so, increment the ptrs. + if (**pattern == **string) { + (*pattern)++; + (*string)++; + } else { + // Uh ho, it did not match, we are done. If the last char was an + // escapement, that means that it was an error to advance the ptr here, + // let's put it back where it was. This also mean that the MatchPattern + // function will return false because if we can't match an escape char + // here, then no one will. + if (escaped) { + (*pattern)--; + } + return; + } + + escaped = false; + } +} + +template +static void EatWildcard(const CHAR** pattern) { + while(**pattern) { + if (!IsWildcard(**pattern)) + return; + (*pattern)++; + } +} + +template +static bool MatchPatternT(const CHAR* eval, const CHAR* pattern) { + // Eat all the matching chars. + EatSameChars(&pattern, &eval); + + // If the string is empty, then the pattern must be empty too, or contains + // only wildcards. + if (*eval == 0) { + EatWildcard(&pattern); + if (*pattern) + return false; + return true; + } + + // Pattern is empty but not string, this is not a match. + if (*pattern == 0) + return false; + + // If this is a question mark, then we need to compare the rest with + // the current string or the string with one character eaten. + if (pattern[0] == '?') { + if (MatchPatternT(eval, pattern + 1) || + MatchPatternT(eval + 1, pattern + 1)) + return true; + } + + // This is a *, try to match all the possible substrings with the remainder + // of the pattern. + if (pattern[0] == '*') { + while (*eval) { + if (MatchPatternT(eval, pattern + 1)) + return true; + eval++; + } + + // We reached the end of the string, let see if the pattern contains only + // wildcards. + if (*eval == 0) { + EatWildcard(&pattern); + if (*pattern) + return false; + return true; + } + } + + return false; +} + +bool MatchPattern(const std::wstring& eval, const std::wstring& pattern) { + return MatchPatternT(eval.c_str(), pattern.c_str()); +} + +bool MatchPattern(const std::string& eval, const std::string& pattern) { + return MatchPatternT(eval.c_str(), pattern.c_str()); +} + +// For the various *ToInt conversions, there are no *ToIntTraits classes to use +// because there's no such thing as strtoi. Use *ToLongTraits through a cast +// instead, requiring that long and int are compatible and equal-width. They +// are on our target platforms. + +// XXX Sigh. + +#if !defined(ARCH_CPU_64_BITS) || !defined(CHROMIUM_MOZILLA_BUILD) +bool StringToInt(const std::string& input, int* output) { + COMPILE_ASSERT(sizeof(int) == sizeof(long), cannot_strtol_to_int); + return StringToNumber(input, + reinterpret_cast(output)); +} + +bool StringToInt(const string16& input, int* output) { + COMPILE_ASSERT(sizeof(int) == sizeof(long), cannot_wcstol_to_int); + return StringToNumber(input, + reinterpret_cast(output)); +} + +#else +bool StringToInt(const std::string& input, int* output) { + long tmp; + bool ok = StringToNumber(input, &tmp); + if (!ok || tmp > kint32max) { + return false; + } + *output = static_cast(tmp); + return true; +} + +bool StringToInt(const string16& input, int* output) { + long tmp; + bool ok = StringToNumber(input, &tmp); + if (!ok || tmp > kint32max) { + return false; + } + *output = static_cast(tmp); + return true; +} +#endif // !defined(ARCH_CPU_64_BITS) || !defined(CHROMIUM_MOZILLA_BUILD) + +bool StringToInt64(const std::string& input, int64* output) { + return StringToNumber(input, output); +} + +bool StringToInt64(const string16& input, int64* output) { + return StringToNumber(input, output); +} + +#if !defined(ARCH_CPU_64_BITS) || !defined(CHROMIUM_MOZILLA_BUILD) +bool HexStringToInt(const std::string& input, int* output) { + COMPILE_ASSERT(sizeof(int) == sizeof(long), cannot_strtol_to_int); + return StringToNumber(input, + reinterpret_cast(output)); +} + +bool HexStringToInt(const string16& input, int* output) { + COMPILE_ASSERT(sizeof(int) == sizeof(long), cannot_wcstol_to_int); + return StringToNumber( + input, reinterpret_cast(output)); +} + +#else +bool HexStringToInt(const std::string& input, int* output) { + long tmp; + bool ok = StringToNumber(input, &tmp); + if (!ok || tmp > kint32max) { + return false; + } + *output = static_cast(tmp); + return true; +} + +bool HexStringToInt(const string16& input, int* output) { + long tmp; + bool ok = StringToNumber(input, &tmp); + if (!ok || tmp > kint32max) { + return false; + } + *output = static_cast(tmp); + return true; +} + +#endif // !defined(ARCH_CPU_64_BITS) || !defined(CHROMIUM_MOZILLA_BUILD) + +namespace { + +template +bool HexDigitToIntT(const CHAR digit, uint8* val) { + if (digit >= '0' && digit <= '9') + *val = digit - '0'; + else if (digit >= 'a' && digit <= 'f') + *val = 10 + digit - 'a'; + else if (digit >= 'A' && digit <= 'F') + *val = 10 + digit - 'A'; + else + return false; + return true; +} + +template +bool HexStringToBytesT(const STR& input, std::vector* output) { + DCHECK(output->size() == 0); + int count = input.size(); + if (count == 0 || (count % 2) != 0) + return false; + for (int i = 0; i < count / 2; ++i) { + uint8 msb = 0; // most significant 4 bits + uint8 lsb = 0; // least significant 4 bits + if (!HexDigitToIntT(input[i * 2], &msb) || + !HexDigitToIntT(input[i * 2 + 1], &lsb)) + return false; + output->push_back((msb << 4) | lsb); + } + return true; +} + +} // namespace + +bool HexStringToBytes(const std::string& input, std::vector* output) { + return HexStringToBytesT(input, output); +} + +bool HexStringToBytes(const string16& input, std::vector* output) { + return HexStringToBytesT(input, output); +} + +int StringToInt(const std::string& value) { + int result; + StringToInt(value, &result); + return result; +} + +int StringToInt(const string16& value) { + int result; + StringToInt(value, &result); + return result; +} + +int64 StringToInt64(const std::string& value) { + int64 result; + StringToInt64(value, &result); + return result; +} + +int64 StringToInt64(const string16& value) { + int64 result; + StringToInt64(value, &result); + return result; +} + +int HexStringToInt(const std::string& value) { + int result; + HexStringToInt(value, &result); + return result; +} + +int HexStringToInt(const string16& value) { + int result; + HexStringToInt(value, &result); + return result; +} + +bool StringToDouble(const std::string& input, double* output) { + return StringToNumber(input, output); +} + +bool StringToDouble(const string16& input, double* output) { + return StringToNumber(input, output); +} + +double StringToDouble(const std::string& value) { + double result; + StringToDouble(value, &result); + return result; +} + +double StringToDouble(const string16& value) { + double result; + StringToDouble(value, &result); + return result; +} + +// The following code is compatible with the OpenBSD lcpy interface. See: +// http://www.gratisoft.us/todd/papers/strlcpy.html +// ftp://ftp.openbsd.org/pub/OpenBSD/src/lib/libc/string/{wcs,str}lcpy.c + +namespace { + +template +size_t lcpyT(CHAR* dst, const CHAR* src, size_t dst_size) { + for (size_t i = 0; i < dst_size; ++i) { + if ((dst[i] = src[i]) == 0) // We hit and copied the terminating NULL. + return i; + } + + // We were left off at dst_size. We over copied 1 byte. Null terminate. + if (dst_size != 0) + dst[dst_size - 1] = 0; + + // Count the rest of the |src|, and return it's length in characters. + while (src[dst_size]) ++dst_size; + return dst_size; +} + +} // namespace + +size_t base::strlcpy(char* dst, const char* src, size_t dst_size) { + return lcpyT(dst, src, dst_size); +} +size_t base::wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size) { + return lcpyT(dst, src, dst_size); +} + +bool ElideString(const std::wstring& input, int max_len, std::wstring* output) { + DCHECK(max_len >= 0); + if (static_cast(input.length()) <= max_len) { + output->assign(input); + return false; + } + + switch (max_len) { + case 0: + output->clear(); + break; + case 1: + output->assign(input.substr(0, 1)); + break; + case 2: + output->assign(input.substr(0, 2)); + break; + case 3: + output->assign(input.substr(0, 1) + L"." + + input.substr(input.length() - 1)); + break; + case 4: + output->assign(input.substr(0, 1) + L".." + + input.substr(input.length() - 1)); + break; + default: { + int rstr_len = (max_len - 3) / 2; + int lstr_len = rstr_len + ((max_len - 3) % 2); + output->assign(input.substr(0, lstr_len) + L"..." + + input.substr(input.length() - rstr_len)); + break; + } + } + + return true; +} + +std::string HexEncode(const void* bytes, size_t size) { + static const char kHexChars[] = "0123456789ABCDEF"; + + // Each input byte creates two output hex characters. + std::string ret(size * 2, '\0'); + + for (size_t i = 0; i < size; ++i) { + char b = reinterpret_cast(bytes)[i]; + ret[(i * 2)] = kHexChars[(b >> 4) & 0xf]; + ret[(i * 2) + 1] = kHexChars[b & 0xf]; + } + return ret; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_util.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,617 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// This file defines utility functions for working with strings. + +#ifndef BASE_STRING_UTIL_H_ +#define BASE_STRING_UTIL_H_ + +#include // va_list + +#include +#include + +#include "base/basictypes.h" +#include "base/string16.h" +#include "base/string_piece.h" // For implicit conversions. + +// Safe standard library wrappers for all platforms. + +namespace base { + +// C standard-library functions like "strncasecmp" and "snprintf" that aren't +// cross-platform are provided as "base::strncasecmp", and their prototypes +// are listed below. These functions are then implemented as inline calls +// to the platform-specific equivalents in the platform-specific headers. + +// Compare the two strings s1 and s2 without regard to case using +// the current locale; returns 0 if they are equal, 1 if s1 > s2, and -1 if +// s2 > s1 according to a lexicographic comparison. +int strcasecmp(const char* s1, const char* s2); + +// Compare up to count characters of s1 and s2 without regard to case using +// the current locale; returns 0 if they are equal, 1 if s1 > s2, and -1 if +// s2 > s1 according to a lexicographic comparison. +int strncasecmp(const char* s1, const char* s2, size_t count); + +// Wrapper for vsnprintf that always null-terminates and always returns the +// number of characters that would be in an untruncated formatted +// string, even when truncation occurs. +int vsnprintf(char* buffer, size_t size, const char* format, va_list arguments); + +// vswprintf always null-terminates, but when truncation occurs, it will either +// return -1 or the number of characters that would be in an untruncated +// formatted string. The actual return value depends on the underlying +// C library's vswprintf implementation. +int vswprintf(wchar_t* buffer, size_t size, + const wchar_t* format, va_list arguments); + +// Some of these implementations need to be inlined. + +inline int snprintf(char* buffer, size_t size, const char* format, ...) { + va_list arguments; + va_start(arguments, format); + int result = vsnprintf(buffer, size, format, arguments); + va_end(arguments); + return result; +} + +inline int swprintf(wchar_t* buffer, size_t size, const wchar_t* format, ...) { + va_list arguments; + va_start(arguments, format); + int result = vswprintf(buffer, size, format, arguments); + va_end(arguments); + return result; +} + +// BSD-style safe and consistent string copy functions. +// Copies |src| to |dst|, where |dst_size| is the total allocated size of |dst|. +// Copies at most |dst_size|-1 characters, and always NULL terminates |dst|, as +// long as |dst_size| is not 0. Returns the length of |src| in characters. +// If the return value is >= dst_size, then the output was truncated. +// NOTE: All sizes are in number of characters, NOT in bytes. +size_t strlcpy(char* dst, const char* src, size_t dst_size); +size_t wcslcpy(wchar_t* dst, const wchar_t* src, size_t dst_size); + +// Scan a wprintf format string to determine whether it's portable across a +// variety of systems. This function only checks that the conversion +// specifiers used by the format string are supported and have the same meaning +// on a variety of systems. It doesn't check for other errors that might occur +// within a format string. +// +// Nonportable conversion specifiers for wprintf are: +// - 's' and 'c' without an 'l' length modifier. %s and %c operate on char +// data on all systems except Windows, which treat them as wchar_t data. +// Use %ls and %lc for wchar_t data instead. +// - 'S' and 'C', which operate on wchar_t data on all systems except Windows, +// which treat them as char data. Use %ls and %lc for wchar_t data +// instead. +// - 'F', which is not identified by Windows wprintf documentation. +// - 'D', 'O', and 'U', which are deprecated and not available on all systems. +// Use %ld, %lo, and %lu instead. +// +// Note that there is no portable conversion specifier for char data when +// working with wprintf. +// +// This function is intended to be called from base::vswprintf. +bool IsWprintfFormatPortable(const wchar_t* format); + +} // namespace base + +#if defined(OS_WIN) +#include "base/string_util_win.h" +#elif defined(OS_POSIX) +#include "base/string_util_posix.h" +#else +#error Define string operations appropriately for your platform +#endif + +#ifdef CHROMIUM_MOZILLA_BUILD +namespace base { +#endif +// Returns a reference to a globally unique empty string that functions can +// return. Use this to avoid static construction of strings, not to replace +// any and all uses of "std::string()" as nicer-looking sugar. +// These functions are threadsafe. +const std::string& EmptyString(); +const std::wstring& EmptyWString(); +const string16& EmptyString16(); +#ifdef CHROMIUM_MOZILLA_BUILD +} +#endif + +extern const wchar_t kWhitespaceWide[]; +extern const char kWhitespaceASCII[]; + +// Names of codepages (charsets) understood by icu. +extern const char* const kCodepageUTF8; + +// Removes characters in trim_chars from the beginning and end of input. +// NOTE: Safe to use the same variable for both input and output. +bool TrimString(const std::wstring& input, + const wchar_t trim_chars[], + std::wstring* output); +bool TrimString(const std::string& input, + const char trim_chars[], + std::string* output); + +// Trims any whitespace from either end of the input string. Returns where +// whitespace was found. +// The non-wide version has two functions: +// * TrimWhitespaceASCII() +// This function is for ASCII strings and only looks for ASCII whitespace; +// * TrimWhitespaceUTF8() +// This function is for UTF-8 strings and looks for Unicode whitespace. +// Please choose the best one according to your usage. +// NOTE: Safe to use the same variable for both input and output. +enum TrimPositions { + TRIM_NONE = 0, + TRIM_LEADING = 1 << 0, + TRIM_TRAILING = 1 << 1, + TRIM_ALL = TRIM_LEADING | TRIM_TRAILING, +}; +TrimPositions TrimWhitespace(const std::wstring& input, + TrimPositions positions, + std::wstring* output); +TrimPositions TrimWhitespaceASCII(const std::string& input, + TrimPositions positions, + std::string* output); +TrimPositions TrimWhitespaceUTF8(const std::string& input, + TrimPositions positions, + std::string* output); + +// Deprecated. This function is only for backward compatibility and calls +// TrimWhitespaceASCII(). +TrimPositions TrimWhitespace(const std::string& input, + TrimPositions positions, + std::string* output); + +// Searches for CR or LF characters. Removes all contiguous whitespace +// strings that contain them. This is useful when trying to deal with text +// copied from terminals. +// Returns |text, with the following three transformations: +// (1) Leading and trailing whitespace is trimmed. +// (2) If |trim_sequences_with_line_breaks| is true, any other whitespace +// sequences containing a CR or LF are trimmed. +// (3) All other whitespace sequences are converted to single spaces. +std::wstring CollapseWhitespace(const std::wstring& text, + bool trim_sequences_with_line_breaks); + +// These convert between ASCII (7-bit) and Wide/UTF16 strings. +std::string WideToASCII(const std::wstring& wide); +std::wstring ASCIIToWide(const std::string& ascii); +std::string UTF16ToASCII(const string16& utf16); +string16 ASCIIToUTF16(const std::string& ascii); + +// These convert between UTF-8, -16, and -32 strings. They are potentially slow, +// so avoid unnecessary conversions. The low-level versions return a boolean +// indicating whether the conversion was 100% valid. In this case, it will still +// do the best it can and put the result in the output buffer. The versions that +// return strings ignore this error and just return the best conversion +// possible. +bool WideToUTF8(const wchar_t* src, size_t src_len, std::string* output); +std::string WideToUTF8(const std::wstring& wide); +bool UTF8ToWide(const char* src, size_t src_len, std::wstring* output); +std::wstring UTF8ToWide(const StringPiece& utf8); + +bool WideToUTF16(const wchar_t* src, size_t src_len, string16* output); +string16 WideToUTF16(const std::wstring& wide); +bool UTF16ToWide(const char16* src, size_t src_len, std::wstring* output); +std::wstring UTF16ToWide(const string16& utf16); + +bool UTF8ToUTF16(const char* src, size_t src_len, string16* output); +string16 UTF8ToUTF16(const std::string& utf8); +bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output); +std::string UTF16ToUTF8(const string16& utf16); + +// We are trying to get rid of wstring as much as possible, but it's too big +// a mess to do it all at once. These conversions should be used when we +// really should just be passing a string16 around, but we haven't finished +// porting whatever module uses wstring and the conversion is being used as a +// stopcock. This makes it easy to grep for the ones that should be removed. +#if defined(OS_WIN) +# define WideToUTF16Hack +# define UTF16ToWideHack +#else +# define WideToUTF16Hack WideToUTF16 +# define UTF16ToWideHack UTF16ToWide +#endif + +// Defines the error handling modes of WideToCodepage and CodepageToWide. +class OnStringUtilConversionError { + public: + enum Type { + // The function will return failure. The output buffer will be empty. + FAIL, + + // The offending characters are skipped and the conversion will proceed as + // if they did not exist. + SKIP, + }; + + private: + OnStringUtilConversionError(); +}; + +// Converts between wide strings and the encoding specified. If the +// encoding doesn't exist or the encoding fails (when on_error is FAIL), +// returns false. +bool WideToCodepage(const std::wstring& wide, + const char* codepage_name, + OnStringUtilConversionError::Type on_error, + std::string* encoded); +bool CodepageToWide(const std::string& encoded, + const char* codepage_name, + OnStringUtilConversionError::Type on_error, + std::wstring* wide); + +// Converts the given wide string to the corresponding Latin1. This will fail +// (return false) if any characters are more than 255. +bool WideToLatin1(const std::wstring& wide, std::string* latin1); + +// Returns true if the specified string matches the criteria. How can a wide +// string be 8-bit or UTF8? It contains only characters that are < 256 (in the +// first case) or characters that use only 8-bits and whose 8-bit +// representation looks like a UTF-8 string (the second case). +bool IsString8Bit(const std::wstring& str); +bool IsStringUTF8(const std::string& str); +bool IsStringWideUTF8(const std::wstring& str); +bool IsStringASCII(const std::wstring& str); +bool IsStringASCII(const std::string& str); +bool IsStringASCII(const string16& str); + +// ASCII-specific tolower. The standard library's tolower is locale sensitive, +// so we don't want to use it here. +template inline Char ToLowerASCII(Char c) { + return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c; +} + +// Converts the elements of the given string. This version uses a pointer to +// clearly differentiate it from the non-pointer variant. +template inline void StringToLowerASCII(str* s) { + for (typename str::iterator i = s->begin(); i != s->end(); ++i) + *i = ToLowerASCII(*i); +} + +template inline str StringToLowerASCII(const str& s) { + // for std::string and std::wstring + str output(s); + StringToLowerASCII(&output); + return output; +} + +// ASCII-specific toupper. The standard library's toupper is locale sensitive, +// so we don't want to use it here. +template inline Char ToUpperASCII(Char c) { + return (c >= 'a' && c <= 'z') ? (c + ('A' - 'a')) : c; +} + +// Converts the elements of the given string. This version uses a pointer to +// clearly differentiate it from the non-pointer variant. +template inline void StringToUpperASCII(str* s) { + for (typename str::iterator i = s->begin(); i != s->end(); ++i) + *i = ToUpperASCII(*i); +} + +template inline str StringToUpperASCII(const str& s) { + // for std::string and std::wstring + str output(s); + StringToUpperASCII(&output); + return output; +} + +// Compare the lower-case form of the given string against the given ASCII +// string. This is useful for doing checking if an input string matches some +// token, and it is optimized to avoid intermediate string copies. This API is +// borrowed from the equivalent APIs in Mozilla. +bool LowerCaseEqualsASCII(const std::string& a, const char* b); +bool LowerCaseEqualsASCII(const std::wstring& a, const char* b); + +// Same thing, but with string iterators instead. +bool LowerCaseEqualsASCII(std::string::const_iterator a_begin, + std::string::const_iterator a_end, + const char* b); +bool LowerCaseEqualsASCII(std::wstring::const_iterator a_begin, + std::wstring::const_iterator a_end, + const char* b); +bool LowerCaseEqualsASCII(const char* a_begin, + const char* a_end, + const char* b); +bool LowerCaseEqualsASCII(const wchar_t* a_begin, + const wchar_t* a_end, + const char* b); + +// Returns true if str starts with search, or false otherwise. +bool StartsWithASCII(const std::string& str, + const std::string& search, + bool case_sensitive); +bool StartsWith(const std::wstring& str, + const std::wstring& search, + bool case_sensitive); + +// Determines the type of ASCII character, independent of locale (the C +// library versions will change based on locale). +template +inline bool IsAsciiWhitespace(Char c) { + return c == ' ' || c == '\r' || c == '\n' || c == '\t'; +} +template +inline bool IsAsciiAlpha(Char c) { + return ((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')); +} +template +inline bool IsAsciiDigit(Char c) { + return c >= '0' && c <= '9'; +} + +// Returns true if it's a whitespace character. +inline bool IsWhitespace(wchar_t c) { + return wcschr(kWhitespaceWide, c) != NULL; +} + +// TODO(mpcomplete): Decide if we should change these names to KIBI, etc, +// or if we should actually use metric units, or leave as is. +enum DataUnits { + DATA_UNITS_BYTE = 0, + DATA_UNITS_KILOBYTE, + DATA_UNITS_MEGABYTE, + DATA_UNITS_GIGABYTE, +}; + +// Return the unit type that is appropriate for displaying the amount of bytes +// passed in. +DataUnits GetByteDisplayUnits(int64 bytes); + +// Return a byte string in human-readable format, displayed in units appropriate +// specified by 'units', with an optional unit suffix. +// Ex: FormatBytes(512, DATA_UNITS_KILOBYTE, true) => "0.5 KB" +// Ex: FormatBytes(10*1024, DATA_UNITS_MEGABYTE, false) => "0.1" +std::wstring FormatBytes(int64 bytes, DataUnits units, bool show_units); + +// As above, but with "/s" units. +// Ex: FormatSpeed(512, DATA_UNITS_KILOBYTE, true) => "0.5 KB/s" +// Ex: FormatSpeed(10*1024, DATA_UNITS_MEGABYTE, false) => "0.1" +std::wstring FormatSpeed(int64 bytes, DataUnits units, bool show_units); + +// Return a number formated with separators in the user's locale way. +// Ex: FormatNumber(1234567) => 1,234,567 +std::wstring FormatNumber(int64 number); + +// Starting at |start_offset| (usually 0), replace the first instance of +// |find_this| with |replace_with|. +void ReplaceFirstSubstringAfterOffset(string16* str, + string16::size_type start_offset, + const string16& find_this, + const string16& replace_with); +void ReplaceFirstSubstringAfterOffset(std::string* str, + std::string::size_type start_offset, + const std::string& find_this, + const std::string& replace_with); + +// Starting at |start_offset| (usually 0), look through |str| and replace all +// instances of |find_this| with |replace_with|. +// +// This does entire substrings; use std::replace in for single +// characters, for example: +// std::replace(str.begin(), str.end(), 'a', 'b'); +void ReplaceSubstringsAfterOffset(string16* str, + string16::size_type start_offset, + const string16& find_this, + const string16& replace_with); +void ReplaceSubstringsAfterOffset(std::string* str, + std::string::size_type start_offset, + const std::string& find_this, + const std::string& replace_with); + +// Specialized string-conversion functions. +std::string IntToString(int value); +std::wstring IntToWString(int value); +std::string UintToString(unsigned int value); +std::wstring UintToWString(unsigned int value); +std::string Int64ToString(int64 value); +std::wstring Int64ToWString(int64 value); +std::string Uint64ToString(uint64 value); +std::wstring Uint64ToWString(uint64 value); +// The DoubleToString methods convert the double to a string format that +// ignores the locale. If you want to use locale specific formatting, use ICU. +std::string DoubleToString(double value); +std::wstring DoubleToWString(double value); + +// Perform a best-effort conversion of the input string to a numeric type, +// setting |*output| to the result of the conversion. Returns true for +// "perfect" conversions; returns false in the following cases: +// - Overflow/underflow. |*output| will be set to the maximum value supported +// by the data type. +// - Trailing characters in the string after parsing the number. |*output| +// will be set to the value of the number that was parsed. +// - No characters parseable as a number at the beginning of the string. +// |*output| will be set to 0. +// - Empty string. |*output| will be set to 0. +bool StringToInt(const std::string& input, int* output); +bool StringToInt(const string16& input, int* output); +bool StringToInt64(const std::string& input, int64* output); +bool StringToInt64(const string16& input, int64* output); +bool HexStringToInt(const std::string& input, int* output); +bool HexStringToInt(const string16& input, int* output); + +// Similar to the previous functions, except that output is a vector of bytes. +// |*output| will contain as many bytes as were successfully parsed prior to the +// error. There is no overflow, but input.size() must be evenly divisible by 2. +// Leading 0x or +/- are not allowed. +bool HexStringToBytes(const std::string& input, std::vector* output); +bool HexStringToBytes(const string16& input, std::vector* output); + +// For floating-point conversions, only conversions of input strings in decimal +// form are defined to work. Behavior with strings representing floating-point +// numbers in hexadecimal, and strings representing non-fininte values (such as +// NaN and inf) is undefined. Otherwise, these behave the same as the integral +// variants. This expects the input string to NOT be specific to the locale. +// If your input is locale specific, use ICU to read the number. +bool StringToDouble(const std::string& input, double* output); +bool StringToDouble(const string16& input, double* output); + +// Convenience forms of the above, when the caller is uninterested in the +// boolean return value. These return only the |*output| value from the +// above conversions: a best-effort conversion when possible, otherwise, 0. +int StringToInt(const std::string& value); +int StringToInt(const string16& value); +int64 StringToInt64(const std::string& value); +int64 StringToInt64(const string16& value); +int HexStringToInt(const std::string& value); +int HexStringToInt(const string16& value); +double StringToDouble(const std::string& value); +double StringToDouble(const string16& value); + +// Return a C++ string given printf-like input. +std::string StringPrintf(const char* format, ...); +std::wstring StringPrintf(const wchar_t* format, ...); + +// Store result into a supplied string and return it +const std::string& SStringPrintf(std::string* dst, const char* format, ...); +const std::wstring& SStringPrintf(std::wstring* dst, + const wchar_t* format, ...); + +// Append result to a supplied string +void StringAppendF(std::string* dst, const char* format, ...); +void StringAppendF(std::wstring* dst, const wchar_t* format, ...); + +// Lower-level routine that takes a va_list and appends to a specified +// string. All other routines are just convenience wrappers around it. +void StringAppendV(std::string* dst, const char* format, va_list ap); +void StringAppendV(std::wstring* dst, const wchar_t* format, va_list ap); + +// This is mpcomplete's pattern for saving a string copy when dealing with +// a function that writes results into a wchar_t[] and wanting the result to +// end up in a std::wstring. It ensures that the std::wstring's internal +// buffer has enough room to store the characters to be written into it, and +// sets its .length() attribute to the right value. +// +// The reserve() call allocates the memory required to hold the string +// plus a terminating null. This is done because resize() isn't +// guaranteed to reserve space for the null. The resize() call is +// simply the only way to change the string's 'length' member. +// +// XXX-performance: the call to wide.resize() takes linear time, since it fills +// the string's buffer with nulls. I call it to change the length of the +// string (needed because writing directly to the buffer doesn't do this). +// Perhaps there's a constant-time way to change the string's length. +template +inline typename string_type::value_type* WriteInto(string_type* str, + size_t length_with_null) { + str->reserve(length_with_null); + str->resize(length_with_null - 1); + return &((*str)[0]); +} + +//----------------------------------------------------------------------------- + +// Function objects to aid in comparing/searching strings. + +#if defined(CHROMIUM_MOZILLA_BUILD) +template struct chromium_CaseInsensitiveCompare { +#else +template struct CaseInsensitiveCompare { +#endif + public: + bool operator()(Char x, Char y) const { + return tolower(x) == tolower(y); + } +}; + +template struct CaseInsensitiveCompareASCII { + public: + bool operator()(Char x, Char y) const { + return ToLowerASCII(x) == ToLowerASCII(y); + } +}; + +//----------------------------------------------------------------------------- + +// Splits |str| into a vector of strings delimited by |s|. Append the results +// into |r| as they appear. If several instances of |s| are contiguous, or if +// |str| begins with or ends with |s|, then an empty string is inserted. +// +// Every substring is trimmed of any leading or trailing white space. +void SplitString(const std::wstring& str, + wchar_t s, + std::vector* r); +void SplitString(const std::string& str, + char s, + std::vector* r); + +// The same as SplitString, but don't trim white space. +void SplitStringDontTrim(const std::wstring& str, + wchar_t s, + std::vector* r); +void SplitStringDontTrim(const std::string& str, + char s, + std::vector* r); + +// Does the opposite of SplitString(). +std::wstring JoinString(const std::vector& parts, wchar_t s); +std::string JoinString(const std::vector& parts, char s); + +// WARNING: this uses whitespace as defined by the HTML5 spec. If you need +// a function similar to this but want to trim all types of whitespace, then +// factor this out into a function that takes a string containing the characters +// that are treated as whitespace. +// +// Splits the string along whitespace (where whitespace is the five space +// characters defined by HTML 5). Each contiguous block of non-whitespace +// characters is added to result. +void SplitStringAlongWhitespace(const std::wstring& str, + std::vector* result); + +// Replace $1-$2-$3 in the format string with |a| and |b| respectively. +// Additionally, $$ is replaced by $. The offset/offsets parameter here can be +// NULL. +string16 ReplaceStringPlaceholders(const string16& format_string, + const string16& a, + size_t* offset); + +string16 ReplaceStringPlaceholders(const string16& format_string, + const string16& a, + const string16& b, + std::vector* offsets); + +string16 ReplaceStringPlaceholders(const string16& format_string, + const string16& a, + const string16& b, + const string16& c, + std::vector* offsets); + +string16 ReplaceStringPlaceholders(const string16& format_string, + const string16& a, + const string16& b, + const string16& c, + const string16& d, + std::vector* offsets); + +// If the size of |input| is more than |max_len|, this function returns true and +// |input| is shortened into |output| by removing chars in the middle (they are +// replaced with up to 3 dots, as size permits). +// Ex: ElideString(L"Hello", 10, &str) puts Hello in str and returns false. +// ElideString(L"Hello my name is Tom", 10, &str) puts "Hell...Tom" in str and +// returns true. +bool ElideString(const std::wstring& input, int max_len, std::wstring* output); + +// Returns true if the string passed in matches the pattern. The pattern +// string can contain wildcards like * and ? +// TODO(iyengar) This function may not work correctly for CJK strings as +// it does individual character matches. +// The backslash character (\) is an escape character for * and ? +bool MatchPattern(const std::wstring& string, const std::wstring& pattern); +bool MatchPattern(const std::string& string, const std::string& pattern); + +// Returns a hex string representation of a binary buffer. +// The returned hex string will be in upper case. +// This function does not check if |size| is within reasonable limits since +// it's written with trusted data in mind. +// If you suspect that the data you want to format might be large, +// the absolute max size for |size| should be is +// std::numeric_limits::max() / 2 +std::string HexEncode(const void* bytes, size_t size); + + +#endif // BASE_STRING_UTIL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_util_icu.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_util_icu.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_util_icu.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_util_icu.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,545 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/string_util.h" + +#include +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/singleton.h" +#include "unicode/ucnv.h" +#include "unicode/numfmt.h" +#include "unicode/ustring.h" + +namespace { + +// ReadUnicodeCharacter -------------------------------------------------------- + +// Reads a UTF-8 stream, placing the next code point into the given output +// |*code_point|. |src| represents the entire string to read, and |*char_index| +// is the character offset within the string to start reading at. |*char_index| +// will be updated to index the last character read, such that incrementing it +// (as in a for loop) will take the reader to the next character. +// +// Returns true on success. On false, |*code_point| will be invalid. +bool ReadUnicodeCharacter(const char* src, int32 src_len, + int32* char_index, uint32* code_point_out) { + // U8_NEXT expects to be able to use -1 to signal an error, so we must + // use a signed type for code_point. But this function returns false + // on error anyway, so code_point_out is unsigned. + int32 code_point; + U8_NEXT(src, *char_index, src_len, code_point); + *code_point_out = static_cast(code_point); + + // The ICU macro above moves to the next char, we want to point to the last + // char consumed. + (*char_index)--; + + // Validate the decoded value. + return U_IS_UNICODE_CHAR(code_point); +} + +// Reads a UTF-16 character. The usage is the same as the 8-bit version above. +bool ReadUnicodeCharacter(const char16* src, int32 src_len, + int32* char_index, uint32* code_point) { + if (U16_IS_SURROGATE(src[*char_index])) { + if (!U16_IS_SURROGATE_LEAD(src[*char_index]) || + *char_index + 1 >= src_len || + !U16_IS_TRAIL(src[*char_index + 1])) { + // Invalid surrogate pair. + return false; + } + + // Valid surrogate pair. + *code_point = U16_GET_SUPPLEMENTARY(src[*char_index], + src[*char_index + 1]); + (*char_index)++; + } else { + // Not a surrogate, just one 16-bit word. + *code_point = src[*char_index]; + } + + return U_IS_UNICODE_CHAR(*code_point); +} + +#if defined(WCHAR_T_IS_UTF32) +// Reads UTF-32 character. The usage is the same as the 8-bit version above. +bool ReadUnicodeCharacter(const wchar_t* src, int32 src_len, + int32* char_index, uint32* code_point) { + // Conversion is easy since the source is 32-bit. + *code_point = src[*char_index]; + + // Validate the value. + return U_IS_UNICODE_CHAR(*code_point); +} +#endif // defined(WCHAR_T_IS_UTF32) + +// WriteUnicodeCharacter ------------------------------------------------------- + +// Appends a UTF-8 character to the given 8-bit string. +void WriteUnicodeCharacter(uint32 code_point, std::string* output) { + if (code_point <= 0x7f) { + // Fast path the common case of one byte. + output->push_back(code_point); + return; + } + + // U8_APPEND_UNSAFE can append up to 4 bytes. + int32 char_offset = static_cast(output->length()); + output->resize(char_offset + U8_MAX_LENGTH); + + U8_APPEND_UNSAFE(&(*output)[0], char_offset, code_point); + + // U8_APPEND_UNSAFE will advance our pointer past the inserted character, so + // it will represent the new length of the string. + output->resize(char_offset); +} + +// Appends the given code point as a UTF-16 character to the STL string. +void WriteUnicodeCharacter(uint32 code_point, string16* output) { + if (U16_LENGTH(code_point) == 1) { + // Thie code point is in the Basic Multilingual Plane (BMP). + output->push_back(static_cast(code_point)); + } else { + // Non-BMP characters use a double-character encoding. + int32 char_offset = static_cast(output->length()); + output->resize(char_offset + U16_MAX_LENGTH); + U16_APPEND_UNSAFE(&(*output)[0], char_offset, code_point); + } +} + +#if defined(WCHAR_T_IS_UTF32) +// Appends the given UTF-32 character to the given 32-bit string. +inline void WriteUnicodeCharacter(uint32 code_point, std::wstring* output) { + // This is the easy case, just append the character. + output->push_back(code_point); +} +#endif // defined(WCHAR_T_IS_UTF32) + +// Generalized Unicode converter ----------------------------------------------- + +// Converts the given source Unicode character type to the given destination +// Unicode character type as a STL string. The given input buffer and size +// determine the source, and the given output STL string will be replaced by +// the result. +template +bool ConvertUnicode(const SRC_CHAR* src, size_t src_len, DEST_STRING* output) { + output->clear(); + + // ICU requires 32-bit numbers. + bool success = true; + int32 src_len32 = static_cast(src_len); + for (int32 i = 0; i < src_len32; i++) { + uint32 code_point; + if (ReadUnicodeCharacter(src, src_len32, &i, &code_point)) + WriteUnicodeCharacter(code_point, output); + else + success = false; + } + return success; +} + + +// Guesses the length of the output in UTF-8 in bytes, and reserves that amount +// of space in the given string. We also assume that the input character types +// are unsigned, which will be true for UTF-16 and -32 on our systems. We assume +// the string length is greater than zero. +template +void ReserveUTF8Output(const CHAR* src, size_t src_len, std::string* output) { + if (src[0] < 0x80) { + // Assume that the entire input will be ASCII. + output->reserve(src_len); + } else { + // Assume that the entire input is non-ASCII and will have 3 bytes per char. + output->reserve(src_len * 3); + } +} + +// Guesses the size of the output buffer (containing either UTF-16 or -32 data) +// given some UTF-8 input that will be converted to it. See ReserveUTF8Output. +// We assume the source length is > 0. +template +void ReserveUTF16Or32Output(const char* src, size_t src_len, STRING* output) { + if (static_cast(src[0]) < 0x80) { + // Assume the input is all ASCII, which means 1:1 correspondence. + output->reserve(src_len); + } else { + // Otherwise assume that the UTF-8 sequences will have 2 bytes for each + // character. + output->reserve(src_len / 2); + } +} + +} // namespace + +// UTF-8 <-> Wide -------------------------------------------------------------- + +std::string WideToUTF8(const std::wstring& wide) { + std::string ret; + if (wide.empty()) + return ret; + + // Ignore the success flag of this call, it will do the best it can for + // invalid input, which is what we want here. + WideToUTF8(wide.data(), wide.length(), &ret); + return ret; +} + +bool WideToUTF8(const wchar_t* src, size_t src_len, std::string* output) { + if (src_len == 0) { + output->clear(); + return true; + } + + ReserveUTF8Output(src, src_len, output); + return ConvertUnicode(src, src_len, output); +} + +std::wstring UTF8ToWide(const StringPiece& utf8) { + std::wstring ret; + if (utf8.empty()) + return ret; + + UTF8ToWide(utf8.data(), utf8.length(), &ret); + return ret; +} + +bool UTF8ToWide(const char* src, size_t src_len, std::wstring* output) { + if (src_len == 0) { + output->clear(); + return true; + } + + ReserveUTF16Or32Output(src, src_len, output); + return ConvertUnicode(src, src_len, output); +} + +// UTF-16 <-> Wide ------------------------------------------------------------- + +#if defined(WCHAR_T_IS_UTF16) + +// When wide == UTF-16, then conversions are a NOP. +string16 WideToUTF16(const std::wstring& wide) { + return wide; +} + +bool WideToUTF16(const wchar_t* src, size_t src_len, string16* output) { + output->assign(src, src_len); + return true; +} + +std::wstring UTF16ToWide(const string16& utf16) { + return utf16; +} + +bool UTF16ToWide(const char16* src, size_t src_len, std::wstring* output) { + output->assign(src, src_len); + return true; +} + +#elif defined(WCHAR_T_IS_UTF32) + +string16 WideToUTF16(const std::wstring& wide) { + string16 ret; + if (wide.empty()) + return ret; + + WideToUTF16(wide.data(), wide.length(), &ret); + return ret; +} + +bool WideToUTF16(const wchar_t* src, size_t src_len, string16* output) { + if (src_len == 0) { + output->clear(); + return true; + } + + // Assume that normally we won't have any non-BMP characters so the counts + // will be the same. + output->reserve(src_len); + return ConvertUnicode(src, src_len, output); +} + +std::wstring UTF16ToWide(const string16& utf16) { + std::wstring ret; + if (utf16.empty()) + return ret; + + UTF16ToWide(utf16.data(), utf16.length(), &ret); + return ret; +} + +bool UTF16ToWide(const char16* src, size_t src_len, std::wstring* output) { + if (src_len == 0) { + output->clear(); + return true; + } + + // Assume that normally we won't have any non-BMP characters so the counts + // will be the same. + output->reserve(src_len); + return ConvertUnicode(src, src_len, output); +} + +#endif // defined(WCHAR_T_IS_UTF32) + +// UTF16 <-> UTF8 -------------------------------------------------------------- + +#if defined(WCHAR_T_IS_UTF32) + +bool UTF8ToUTF16(const char* src, size_t src_len, string16* output) { + if (src_len == 0) { + output->clear(); + return true; + } + + ReserveUTF16Or32Output(src, src_len, output); + return ConvertUnicode(src, src_len, output); +} + +string16 UTF8ToUTF16(const std::string& utf8) { + string16 ret; + if (utf8.empty()) + return ret; + + // Ignore the success flag of this call, it will do the best it can for + // invalid input, which is what we want here. + UTF8ToUTF16(utf8.data(), utf8.length(), &ret); + return ret; +} + +bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output) { + if (src_len == 0) { + output->clear(); + return true; + } + + ReserveUTF8Output(src, src_len, output); + return ConvertUnicode(src, src_len, output); +} + +std::string UTF16ToUTF8(const string16& utf16) { + std::string ret; + if (utf16.empty()) + return ret; + + // Ignore the success flag of this call, it will do the best it can for + // invalid input, which is what we want here. + UTF16ToUTF8(utf16.data(), utf16.length(), &ret); + return ret; +} + +#elif defined(WCHAR_T_IS_UTF16) +// Easy case since we can use the "wide" versions we already wrote above. + +bool UTF8ToUTF16(const char* src, size_t src_len, string16* output) { + return UTF8ToWide(src, src_len, output); +} + +string16 UTF8ToUTF16(const std::string& utf8) { + return UTF8ToWide(utf8); +} + +bool UTF16ToUTF8(const char16* src, size_t src_len, std::string* output) { + return WideToUTF8(src, src_len, output); +} + +std::string UTF16ToUTF8(const string16& utf16) { + return WideToUTF8(utf16); +} + +#endif + +// Codepage <-> Wide ----------------------------------------------------------- + +// Convert a unicode string into the specified codepage_name. If the codepage +// isn't found, return false. +bool WideToCodepage(const std::wstring& wide, + const char* codepage_name, + OnStringUtilConversionError::Type on_error, + std::string* encoded) { + encoded->clear(); + + UErrorCode status = U_ZERO_ERROR; + UConverter* converter = ucnv_open(codepage_name, &status); + if (!U_SUCCESS(status)) + return false; + + const UChar* uchar_src; + int uchar_len; +#if defined(WCHAR_T_IS_UTF16) + uchar_src = wide.c_str(); + uchar_len = static_cast(wide.length()); +#elif defined(WCHAR_T_IS_UTF32) + // When wchar_t is wider than UChar (16 bits), transform |wide| into a + // UChar* string. Size the UChar* buffer to be large enough to hold twice + // as many UTF-16 code points as there are UTF-16 characters, in case each + // character translates to a UTF-16 surrogate pair, and leave room for a NUL + // terminator. + std::vector wide_uchar(wide.length() * 2 + 1); + u_strFromWCS(&wide_uchar[0], wide_uchar.size(), &uchar_len, + wide.c_str(), wide.length(), &status); + uchar_src = &wide_uchar[0]; + DCHECK(U_SUCCESS(status)) << "failed to convert wstring to UChar*"; +#endif // defined(WCHAR_T_IS_UTF32) + + int encoded_max_length = UCNV_GET_MAX_BYTES_FOR_STRING(uchar_len, + ucnv_getMaxCharSize(converter)); + encoded->resize(encoded_max_length); + + // Setup our error handler. + switch (on_error) { + case OnStringUtilConversionError::FAIL: + ucnv_setFromUCallBack(converter, UCNV_FROM_U_CALLBACK_STOP, 0, + NULL, NULL, &status); + break; + case OnStringUtilConversionError::SKIP: + ucnv_setFromUCallBack(converter, UCNV_FROM_U_CALLBACK_SKIP, 0, + NULL, NULL, &status); + break; + default: + NOTREACHED(); + } + + // ucnv_fromUChars returns size not including terminating null + int actual_size = ucnv_fromUChars(converter, &(*encoded)[0], + encoded_max_length, uchar_src, uchar_len, &status); + encoded->resize(actual_size); + ucnv_close(converter); + if (U_SUCCESS(status)) + return true; + encoded->clear(); // Make sure the output is empty on error. + return false; +} + +// Converts a string of the given codepage into unicode. +// If the codepage isn't found, return false. +bool CodepageToWide(const std::string& encoded, + const char* codepage_name, + OnStringUtilConversionError::Type on_error, + std::wstring* wide) { + wide->clear(); + + UErrorCode status = U_ZERO_ERROR; + UConverter* converter = ucnv_open(codepage_name, &status); + if (!U_SUCCESS(status)) + return false; + + // The worst case is all the input characters are non-BMP (32-bit) ones. + size_t uchar_max_length = encoded.length() * 2 + 1; + + UChar* uchar_dst; +#if defined(WCHAR_T_IS_UTF16) + uchar_dst = WriteInto(wide, uchar_max_length); +#elif defined(WCHAR_T_IS_UTF32) + // When wchar_t is wider than UChar (16 bits), convert into a temporary + // UChar* buffer. + std::vector wide_uchar(uchar_max_length); + uchar_dst = &wide_uchar[0]; +#endif // defined(WCHAR_T_IS_UTF32) + + // Setup our error handler. + switch (on_error) { + case OnStringUtilConversionError::FAIL: + ucnv_setToUCallBack(converter, UCNV_TO_U_CALLBACK_STOP, 0, + NULL, NULL, &status); + break; + case OnStringUtilConversionError::SKIP: + ucnv_setToUCallBack(converter, UCNV_TO_U_CALLBACK_SKIP, 0, + NULL, NULL, &status); + break; + default: + NOTREACHED(); + } + + int actual_size = ucnv_toUChars(converter, + uchar_dst, + static_cast(uchar_max_length), + encoded.data(), + static_cast(encoded.length()), + &status); + ucnv_close(converter); + if (!U_SUCCESS(status)) { + wide->clear(); // Make sure the output is empty on error. + return false; + } + +#ifdef WCHAR_T_IS_UTF32 + // When wchar_t is wider than UChar (16 bits), it's not possible to wind up + // with any more wchar_t elements than UChar elements. ucnv_toUChars + // returns the number of UChar elements not including the NUL terminator, so + // leave extra room for that. + u_strToWCS(WriteInto(wide, actual_size + 1), actual_size + 1, &actual_size, + uchar_dst, actual_size, &status); + DCHECK(U_SUCCESS(status)) << "failed to convert UChar* to wstring"; +#endif // WCHAR_T_IS_UTF32 + + wide->resize(actual_size); + return true; +} + +// Number formatting ----------------------------------------------------------- + +namespace { + +struct NumberFormatSingletonTraits + : public DefaultSingletonTraits { + static NumberFormat* New() { + UErrorCode status = U_ZERO_ERROR; + NumberFormat* formatter = NumberFormat::createInstance(status); + DCHECK(U_SUCCESS(status)); + return formatter; + } + // There's no ICU call to destroy a NumberFormat object other than + // operator delete, so use the default Delete, which calls operator delete. + // This can cause problems if a different allocator is used by this file than + // by ICU. +}; + +} // namespace + +std::wstring FormatNumber(int64 number) { + NumberFormat* number_format = + Singleton::get(); + + if (!number_format) { + // As a fallback, just return the raw number in a string. + return StringPrintf(L"%lld", number); + } + UnicodeString ustr; + number_format->format(number, ustr); + +#if defined(WCHAR_T_IS_UTF16) + return std::wstring(ustr.getBuffer(), + static_cast(ustr.length())); +#elif defined(WCHAR_T_IS_UTF32) + wchar_t buffer[64]; // A int64 is less than 20 chars long, so 64 chars + // leaves plenty of room for formating stuff. + int length = 0; + UErrorCode error = U_ZERO_ERROR; + u_strToWCS(buffer, 64, &length, ustr.getBuffer(), ustr.length() , &error); + if (U_FAILURE(error)) { + NOTREACHED(); + // As a fallback, just return the raw number in a string. + return StringPrintf(L"%lld", number); + } + return std::wstring(buffer, static_cast(length)); +#endif // defined(WCHAR_T_IS_UTF32) +} + +TrimPositions TrimWhitespaceUTF8(const std::string& input, + TrimPositions positions, + std::string* output) { + // This implementation is not so fast since it converts the text encoding + // twice. Please feel free to file a bug if this function hurts the + // performance of Chrome. + DCHECK(IsStringUTF8(input)); + std::wstring input_wide = UTF8ToWide(input); + std::wstring output_wide; + TrimPositions result = TrimWhitespace(input_wide, positions, &output_wide); + *output = WideToUTF8(output_wide); + return result; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_util_posix.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_util_posix.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_util_posix.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_util_posix.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,44 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STRING_UTIL_POSIX_H_ +#define BASE_STRING_UTIL_POSIX_H_ + +#include +#include +#include +#include + +#include "base/logging.h" + +namespace base { + +// Chromium code style is to not use malloc'd strings; this is only for use +// for interaction with APIs that require it. +inline char* strdup(const char* str) { + return ::strdup(str); +} + +inline int strcasecmp(const char* string1, const char* string2) { + return ::strcasecmp(string1, string2); +} + +inline int strncasecmp(const char* string1, const char* string2, size_t count) { + return ::strncasecmp(string1, string2, count); +} + +inline int vsnprintf(char* buffer, size_t size, + const char* format, va_list arguments) { + return ::vsnprintf(buffer, size, format, arguments); +} + +inline int vswprintf(wchar_t* buffer, size_t size, + const wchar_t* format, va_list arguments) { + DCHECK(IsWprintfFormatPortable(format)); + return ::vswprintf(buffer, size, format, arguments); +} + +} // namespace base + +#endif // BASE_STRING_UTIL_POSIX_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_util_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_util_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_util_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_util_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,1621 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include +#include + +#include "base/basictypes.h" +#include "base/string_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +} + +static const struct trim_case { + const wchar_t* input; + const TrimPositions positions; + const wchar_t* output; + const TrimPositions return_value; +} trim_cases[] = { + {L" Google Video ", TRIM_LEADING, L"Google Video ", TRIM_LEADING}, + {L" Google Video ", TRIM_TRAILING, L" Google Video", TRIM_TRAILING}, + {L" Google Video ", TRIM_ALL, L"Google Video", TRIM_ALL}, + {L"Google Video", TRIM_ALL, L"Google Video", TRIM_NONE}, + {L"", TRIM_ALL, L"", TRIM_NONE}, + {L" ", TRIM_LEADING, L"", TRIM_LEADING}, + {L" ", TRIM_TRAILING, L"", TRIM_TRAILING}, + {L" ", TRIM_ALL, L"", TRIM_ALL}, + {L"\t\rTest String\n", TRIM_ALL, L"Test String", TRIM_ALL}, + {L"\x2002Test String\x00A0\x3000", TRIM_ALL, L"Test String", TRIM_ALL}, +}; + +static const struct trim_case_ascii { + const char* input; + const TrimPositions positions; + const char* output; + const TrimPositions return_value; +} trim_cases_ascii[] = { + {" Google Video ", TRIM_LEADING, "Google Video ", TRIM_LEADING}, + {" Google Video ", TRIM_TRAILING, " Google Video", TRIM_TRAILING}, + {" Google Video ", TRIM_ALL, "Google Video", TRIM_ALL}, + {"Google Video", TRIM_ALL, "Google Video", TRIM_NONE}, + {"", TRIM_ALL, "", TRIM_NONE}, + {" ", TRIM_LEADING, "", TRIM_LEADING}, + {" ", TRIM_TRAILING, "", TRIM_TRAILING}, + {" ", TRIM_ALL, "", TRIM_ALL}, + {"\t\rTest String\n", TRIM_ALL, "Test String", TRIM_ALL}, +}; + +TEST(StringUtilTest, TrimWhitespace) { + std::wstring output; // Allow contents to carry over to next testcase + for (size_t i = 0; i < arraysize(trim_cases); ++i) { + const trim_case& value = trim_cases[i]; + EXPECT_EQ(value.return_value, + TrimWhitespace(value.input, value.positions, &output)); + EXPECT_EQ(value.output, output); + } + + // Test that TrimWhitespace() can take the same string for input and output + output = L" This is a test \r\n"; + EXPECT_EQ(TRIM_ALL, TrimWhitespace(output, TRIM_ALL, &output)); + EXPECT_EQ(L"This is a test", output); + + // Once more, but with a string of whitespace + output = L" \r\n"; + EXPECT_EQ(TRIM_ALL, TrimWhitespace(output, TRIM_ALL, &output)); + EXPECT_EQ(L"", output); + + std::string output_ascii; + for (size_t i = 0; i < arraysize(trim_cases_ascii); ++i) { + const trim_case_ascii& value = trim_cases_ascii[i]; + EXPECT_EQ(value.return_value, + TrimWhitespace(value.input, value.positions, &output_ascii)); + EXPECT_EQ(value.output, output_ascii); + } +} + +static const struct trim_case_utf8 { + const char* input; + const TrimPositions positions; + const char* output; + const TrimPositions return_value; +} trim_cases_utf8[] = { + // UTF-8 strings that start (and end) with Unicode space characters + // (including zero-width spaces). + {"\xE2\x80\x80Test String\xE2\x80\x81", TRIM_ALL, "Test String", TRIM_ALL}, + {"\xE2\x80\x82Test String\xE2\x80\x83", TRIM_ALL, "Test String", TRIM_ALL}, + {"\xE2\x80\x84Test String\xE2\x80\x85", TRIM_ALL, "Test String", TRIM_ALL}, + {"\xE2\x80\x86Test String\xE2\x80\x87", TRIM_ALL, "Test String", TRIM_ALL}, + {"\xE2\x80\x88Test String\xE2\x80\x8A", TRIM_ALL, "Test String", TRIM_ALL}, + {"\xE3\x80\x80Test String\xE3\x80\x80", TRIM_ALL, "Test String", TRIM_ALL}, + // UTF-8 strings that end with 0x85 (NEL in ISO-8859). + {"\xD0\x85", TRIM_TRAILING, "\xD0\x85", TRIM_NONE}, + {"\xD9\x85", TRIM_TRAILING, "\xD9\x85", TRIM_NONE}, + {"\xEC\x97\x85", TRIM_TRAILING, "\xEC\x97\x85", TRIM_NONE}, + {"\xF0\x90\x80\x85", TRIM_TRAILING, "\xF0\x90\x80\x85", TRIM_NONE}, + // UTF-8 strings that end with 0xA0 (non-break space in ISO-8859-1). + {"\xD0\xA0", TRIM_TRAILING, "\xD0\xA0", TRIM_NONE}, + {"\xD9\xA0", TRIM_TRAILING, "\xD9\xA0", TRIM_NONE}, + {"\xEC\x97\xA0", TRIM_TRAILING, "\xEC\x97\xA0", TRIM_NONE}, + {"\xF0\x90\x80\xA0", TRIM_TRAILING, "\xF0\x90\x80\xA0", TRIM_NONE}, +}; + +TEST(StringUtilTest, TrimWhitespaceUTF8) { + std::string output_ascii; + for (size_t i = 0; i < arraysize(trim_cases_ascii); ++i) { + const trim_case_ascii& value = trim_cases_ascii[i]; + EXPECT_EQ(value.return_value, + TrimWhitespaceASCII(value.input, value.positions, &output_ascii)); + EXPECT_EQ(value.output, output_ascii); + } + + // Test that TrimWhiteSpaceUTF8() can remove Unicode space characters and + // prevent from removing UTF-8 characters that end with an ISO-8859 NEL. + std::string output_utf8; + for (size_t i = 0; i < arraysize(trim_cases_utf8); ++i) { + const trim_case_utf8& value = trim_cases_utf8[i]; + EXPECT_EQ(value.return_value, + TrimWhitespaceUTF8(value.input, value.positions, &output_utf8)); + EXPECT_EQ(value.output, output_utf8); + } +} + +static const struct collapse_case { + const wchar_t* input; + const bool trim; + const wchar_t* output; +} collapse_cases[] = { + {L" Google Video ", false, L"Google Video"}, + {L"Google Video", false, L"Google Video"}, + {L"", false, L""}, + {L" ", false, L""}, + {L"\t\rTest String\n", false, L"Test String"}, + {L"\x2002Test String\x00A0\x3000", false, L"Test String"}, + {L" Test \n \t String ", false, L"Test String"}, + {L"\x2002Test\x1680 \x2028 \tString\x00A0\x3000", false, L"Test String"}, + {L" Test String", false, L"Test String"}, + {L"Test String ", false, L"Test String"}, + {L"Test String", false, L"Test String"}, + {L"", true, L""}, + {L"\n", true, L""}, + {L" \r ", true, L""}, + {L"\nFoo", true, L"Foo"}, + {L"\r Foo ", true, L"Foo"}, + {L" Foo bar ", true, L"Foo bar"}, + {L" \tFoo bar \n", true, L"Foo bar"}, + {L" a \r b\n c \r\n d \t\re \t f \n ", true, L"abcde f"}, +}; + +TEST(StringUtilTest, CollapseWhitespace) { + for (size_t i = 0; i < arraysize(collapse_cases); ++i) { + const collapse_case& value = collapse_cases[i]; + EXPECT_EQ(value.output, CollapseWhitespace(value.input, value.trim)); + } +} + + +TEST(StringUtilTest, IsStringUTF8) { + EXPECT_TRUE(IsStringUTF8("abc")); + EXPECT_TRUE(IsStringUTF8("\xc2\x81")); + EXPECT_TRUE(IsStringUTF8("\xe1\x80\xbf")); + EXPECT_TRUE(IsStringUTF8("\xf1\x80\xa0\xbf")); + EXPECT_TRUE(IsStringUTF8("a\xc2\x81\xe1\x80\xbf\xf1\x80\xa0\xbf")); + EXPECT_TRUE(IsStringUTF8("\xef\xbb\xbf" "abc")); // UTF-8 BOM + + // surrogate code points + EXPECT_FALSE(IsStringUTF8("\xed\xa0\x80\xed\xbf\xbf")); + EXPECT_FALSE(IsStringUTF8("\xed\xa0\x8f")); + EXPECT_FALSE(IsStringUTF8("\xed\xbf\xbf")); + + // overlong sequences + EXPECT_FALSE(IsStringUTF8("\xc0\x80")); // U+0000 + EXPECT_FALSE(IsStringUTF8("\xc1\x80\xc1\x81")); // "AB" + EXPECT_FALSE(IsStringUTF8("\xe0\x80\x80")); // U+0000 + EXPECT_FALSE(IsStringUTF8("\xe0\x82\x80")); // U+0080 + EXPECT_FALSE(IsStringUTF8("\xe0\x9f\xbf")); // U+07ff + EXPECT_FALSE(IsStringUTF8("\xf0\x80\x80\x8D")); // U+000D + EXPECT_FALSE(IsStringUTF8("\xf0\x80\x82\x91")); // U+0091 + EXPECT_FALSE(IsStringUTF8("\xf0\x80\xa0\x80")); // U+0800 + EXPECT_FALSE(IsStringUTF8("\xf0\x8f\xbb\xbf")); // U+FEFF (BOM) + EXPECT_FALSE(IsStringUTF8("\xf8\x80\x80\x80\xbf")); // U+003F + EXPECT_FALSE(IsStringUTF8("\xfc\x80\x80\x80\xa0\xa5")); // U+00A5 + + // Beyond U+10FFFF (the upper limit of Unicode codespace) + EXPECT_FALSE(IsStringUTF8("\xf4\x90\x80\x80")); // U+110000 + EXPECT_FALSE(IsStringUTF8("\xf8\xa0\xbf\x80\xbf")); // 5 bytes + EXPECT_FALSE(IsStringUTF8("\xfc\x9c\xbf\x80\xbf\x80")); // 6 bytes + + // BOMs in UTF-16(BE|LE) and UTF-32(BE|LE) + EXPECT_FALSE(IsStringUTF8("\xfe\xff")); + EXPECT_FALSE(IsStringUTF8("\xff\xfe")); + EXPECT_FALSE(IsStringUTF8(std::string("\x00\x00\xfe\xff", 4))); + EXPECT_FALSE(IsStringUTF8("\xff\xfe\x00\x00")); + + // Non-characters : U+xxFFF[EF] where xx is 0x00 through 0x10 and + EXPECT_FALSE(IsStringUTF8("\xef\xbf\xbe")); // U+FFFE) + EXPECT_FALSE(IsStringUTF8("\xf0\x8f\xbf\xbe")); // U+1FFFE + EXPECT_FALSE(IsStringUTF8("\xf3\xbf\xbf\xbf")); // U+10FFFF + + // This should also be false, but currently we pass them through. + // Disable them for now. +#if 0 + EXPECT_FALSE(IsStringUTF8("\xef\xb7\x90")); // U+FDD0 + EXPECT_FALSE(IsStringUTF8("\xef\xb7\xaf")); // U+FDEF +#endif + + // Strings in legacy encodings. We can certainly make up strings + // in a legacy encoding that are valid in UTF-8, but in real data, + // most of them are invalid as UTF-8. + EXPECT_FALSE(IsStringUTF8("caf\xe9")); // cafe with U+00E9 in ISO-8859-1 + EXPECT_FALSE(IsStringUTF8("\xb0\xa1\xb0\xa2")); // U+AC00, U+AC001 in EUC-KR + EXPECT_FALSE(IsStringUTF8("\xa7\x41\xa6\x6e")); // U+4F60 U+597D in Big5 + // "abc" with U+201[CD] in windows-125[0-8] + EXPECT_FALSE(IsStringUTF8("\x93" "abc\x94")); + // U+0639 U+064E U+0644 U+064E in ISO-8859-6 + EXPECT_FALSE(IsStringUTF8("\xd9\xee\xe4\xee")); + // U+03B3 U+03B5 U+03B9 U+03AC in ISO-8859-7 + EXPECT_FALSE(IsStringUTF8("\xe3\xe5\xe9\xdC")); +} + +static const wchar_t* const kConvertRoundtripCases[] = { + L"Google Video", + // "网页 图片 资讯更多 »" + L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb", + // "Παγκόσμιος Ιστός" + L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9" + L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2", + // "Поиск страниц на русском" + L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442" + L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430" + L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c", + // "전체서비스" + L"\xc804\xccb4\xc11c\xbe44\xc2a4", + + // Test characters that take more than 16 bits. This will depend on whether + // wchar_t is 16 or 32 bits. +#if defined(WCHAR_T_IS_UTF16) + L"\xd800\xdf00", + // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E) + L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44", +#elif defined(WCHAR_T_IS_UTF32) + L"\x10300", + // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E) + L"\x11d40\x11d41\x11d42\x11d43\x11d44", +#endif +}; + +TEST(StringUtilTest, ConvertUTF8AndWide) { + // we round-trip all the wide strings through UTF-8 to make sure everything + // agrees on the conversion. This uses the stream operators to test them + // simultaneously. + for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) { + std::ostringstream utf8; + utf8 << WideToUTF8(kConvertRoundtripCases[i]); + std::wostringstream wide; + wide << UTF8ToWide(utf8.str()); + + EXPECT_EQ(kConvertRoundtripCases[i], wide.str()); + } +} + +TEST(StringUtilTest, ConvertUTF8AndWideEmptyString) { + // An empty std::wstring should be converted to an empty std::string, + // and vice versa. + std::wstring wempty; + std::string empty; + EXPECT_EQ(empty, WideToUTF8(wempty)); + EXPECT_EQ(wempty, UTF8ToWide(empty)); +} + +TEST(StringUtilTest, ConvertUTF8ToWide) { + struct UTF8ToWideCase { + const char* utf8; + const wchar_t* wide; + bool success; + } convert_cases[] = { + // Regular UTF-8 input. + {"\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d", true}, + // Invalid Unicode code point. + {"\xef\xbf\xbfHello", L"Hello", false}, + // Truncated UTF-8 sequence. + {"\xe4\xa0\xe5\xa5\xbd", L"\x597d", false}, + // Truncated off the end. + {"\xe5\xa5\xbd\xe4\xa0", L"\x597d", false}, + // Non-shortest-form UTF-8. + {"\xf0\x84\xbd\xa0\xe5\xa5\xbd", L"\x597d", false}, + // This UTF-8 character decodes to a UTF-16 surrogate, which is illegal. + {"\xed\xb0\x80", L"", false}, + // Non-BMP character. The result will either be in UTF-16 or UTF-32. +#if defined(WCHAR_T_IS_UTF16) + {"A\xF0\x90\x8C\x80z", L"A\xd800\xdf00z", true}, +#elif defined(WCHAR_T_IS_UTF32) + {"A\xF0\x90\x8C\x80z", L"A\x10300z", true}, +#endif + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(convert_cases); i++) { + std::wstring converted; + EXPECT_EQ(convert_cases[i].success, + UTF8ToWide(convert_cases[i].utf8, + strlen(convert_cases[i].utf8), + &converted)); + std::wstring expected(convert_cases[i].wide); + EXPECT_EQ(expected, converted); + } + + // Manually test an embedded NULL. + std::wstring converted; + EXPECT_TRUE(UTF8ToWide("\00Z\t", 3, &converted)); + ASSERT_EQ(3U, converted.length()); +#if defined(WCHAR_T_IS_UNSIGNED) + EXPECT_EQ(0U, converted[0]); +#else + EXPECT_EQ(0, converted[0]); +#endif + EXPECT_EQ('Z', converted[1]); + EXPECT_EQ('\t', converted[2]); + + // Make sure that conversion replaces, not appends. + EXPECT_TRUE(UTF8ToWide("B", 1, &converted)); + ASSERT_EQ(1U, converted.length()); + EXPECT_EQ('B', converted[0]); +} + +#if defined(WCHAR_T_IS_UTF16) +// This test is only valid when wchar_t == UTF-16. +TEST(StringUtilTest, ConvertUTF16ToUTF8) { + struct UTF16ToUTF8Case { + const wchar_t* utf16; + const char* utf8; + bool success; + } convert_cases[] = { + // Regular UTF-16 input. + {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true}, + // Test a non-BMP character. + {L"\xd800\xdf00", "\xF0\x90\x8C\x80", true}, + // Invalid Unicode code point. + {L"\xffffHello", "Hello", false}, + // The first character is a truncated UTF-16 character. + {L"\xd800\x597d", "\xe5\xa5\xbd", false}, + // Truncated at the end. + {L"\x597d\xd800", "\xe5\xa5\xbd", false}, + }; + + for (int i = 0; i < arraysize(convert_cases); i++) { + std::string converted; + EXPECT_EQ(convert_cases[i].success, + WideToUTF8(convert_cases[i].utf16, + wcslen(convert_cases[i].utf16), + &converted)); + std::string expected(convert_cases[i].utf8); + EXPECT_EQ(expected, converted); + } +} + +#elif defined(WCHAR_T_IS_UTF32) +// This test is only valid when wchar_t == UTF-32. +TEST(StringUtilTest, ConvertUTF32ToUTF8) { + struct UTF8ToWideCase { + const wchar_t* utf32; + const char* utf8; + bool success; + } convert_cases[] = { + // Regular 16-bit input. + {L"\x4f60\x597d", "\xe4\xbd\xa0\xe5\xa5\xbd", true}, + // Test a non-BMP character. + {L"A\x10300z", "A\xF0\x90\x8C\x80z", true}, + // Invalid Unicode code points. + {L"\xffffHello", "Hello", false}, + {L"\xfffffffHello", "Hello", false}, + // The first character is a truncated UTF-16 character. + {L"\xd800\x597d", "\xe5\xa5\xbd", false}, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(convert_cases); i++) { + std::string converted; + EXPECT_EQ(convert_cases[i].success, + WideToUTF8(convert_cases[i].utf32, + wcslen(convert_cases[i].utf32), + &converted)); + std::string expected(convert_cases[i].utf8); + EXPECT_EQ(expected, converted); + } +} +#endif // defined(WCHAR_T_IS_UTF32) + +TEST(StringUtilTest, ConvertMultiString) { + static wchar_t wmulti[] = { + L'f', L'o', L'o', L'\0', + L'b', L'a', L'r', L'\0', + L'b', L'a', L'z', L'\0', + L'\0' + }; + static char multi[] = { + 'f', 'o', 'o', '\0', + 'b', 'a', 'r', '\0', + 'b', 'a', 'z', '\0', + '\0' + }; + std::wstring wmultistring; + memcpy(WriteInto(&wmultistring, arraysize(wmulti)), wmulti, sizeof(wmulti)); + EXPECT_EQ(arraysize(wmulti) - 1, wmultistring.length()); + std::string expected; + memcpy(WriteInto(&expected, arraysize(multi)), multi, sizeof(multi)); + EXPECT_EQ(arraysize(multi) - 1, expected.length()); + const std::string& converted = WideToUTF8(wmultistring); + EXPECT_EQ(arraysize(multi) - 1, converted.length()); + EXPECT_EQ(expected, converted); +} + +TEST(StringUtilTest, ConvertCodepageUTF8) { + // Make sure WideToCodepage works like WideToUTF8. + for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) { + std::string expected(WideToUTF8(kConvertRoundtripCases[i])); + std::string utf8; + EXPECT_TRUE(WideToCodepage(kConvertRoundtripCases[i], kCodepageUTF8, + OnStringUtilConversionError::SKIP, &utf8)); + EXPECT_EQ(expected, utf8); + } +} + +TEST(StringUtilTest, ConvertBetweenCodepageAndWide) { + static const struct { + const char* codepage_name; + const char* encoded; + OnStringUtilConversionError::Type on_error; + bool success; + const wchar_t* wide; + } kConvertCodepageCases[] = { + // Test a case where the input can no be decoded, using both SKIP and FAIL + // error handling rules. "A7 41" is valid, but "A6" isn't. + {"big5", + "\xA7\x41\xA6", + OnStringUtilConversionError::FAIL, + false, + L""}, + {"big5", + "\xA7\x41\xA6", + OnStringUtilConversionError::SKIP, + true, + L"\x4F60"}, + // Arabic (ISO-8859) + {"iso-8859-6", + "\xC7\xEE\xE4\xD3\xF1\xEE\xE4\xC7\xE5\xEF" " " + "\xD9\xEE\xE4\xEE\xEA\xF2\xE3\xEF\xE5\xF2", + OnStringUtilConversionError::FAIL, + true, + L"\x0627\x064E\x0644\x0633\x0651\x064E\x0644\x0627\x0645\x064F" L" " + L"\x0639\x064E\x0644\x064E\x064A\x0652\x0643\x064F\x0645\x0652"}, + // Chinese Simplified (GB2312) + {"gb2312", + "\xC4\xE3\xBA\xC3", + OnStringUtilConversionError::FAIL, + true, + L"\x4F60\x597D"}, + // Chinese Traditional (BIG5) + {"big5", + "\xA7\x41\xA6\x6E", + OnStringUtilConversionError::FAIL, + true, + L"\x4F60\x597D"}, + // Greek (ISO-8859) + {"iso-8859-7", + "\xE3\xE5\xE9\xDC" " " "\xF3\xEF\xF5", + OnStringUtilConversionError::FAIL, + true, + L"\x03B3\x03B5\x03B9\x03AC" L" " L"\x03C3\x03BF\x03C5"}, + // Hebrew (Windows) + {"windows-1255", /* to be replaced with "iso-8859-8-I"? */ + "\xF9\xD1\xC8\xEC\xE5\xC9\xED", + OnStringUtilConversionError::FAIL, + true, + L"\x05E9\x05C1\x05B8\x05DC\x05D5\x05B9\x05DD"}, + // Hindi Devanagari (ISCII) + {"iscii-dev", + "\xEF\x42" "\xC6\xCC\xD7\xE8\xB3\xDA\xCF", + OnStringUtilConversionError::FAIL, + true, + L"\x0928\x092E\x0938\x094D\x0915\x093E\x0930"}, + // Korean (EUC) + {"euc-kr", + "\xBE\xC8\xB3\xE7\xC7\xCF\xBC\xBC\xBF\xE4", + OnStringUtilConversionError::FAIL, + true, + L"\xC548\xB155\xD558\xC138\xC694"}, + // Japanese (EUC) + {"euc-jp", + "\xA4\xB3\xA4\xF3\xA4\xCB\xA4\xC1\xA4\xCF", + OnStringUtilConversionError::FAIL, + true, + L"\x3053\x3093\x306B\x3061\x306F"}, + // Japanese (ISO-2022) + {"iso-2022-jp", + "\x1B\x24\x42" "\x24\x33\x24\x73\x24\x4B\x24\x41\x24\x4F" "\x1B\x28\x42", + OnStringUtilConversionError::FAIL, + true, + L"\x3053\x3093\x306B\x3061\x306F"}, + // Japanese (Shift-JIS) + {"sjis", + "\x82\xB1\x82\xF1\x82\xC9\x82\xBF\x82\xCD", + OnStringUtilConversionError::FAIL, + true, + L"\x3053\x3093\x306B\x3061\x306F"}, + // Russian (KOI8) + {"koi8-r", + "\xDA\xC4\xD2\xC1\xD7\xD3\xD4\xD7\xD5\xCA\xD4\xC5", + OnStringUtilConversionError::FAIL, + true, + L"\x0437\x0434\x0440\x0430\x0432\x0441\x0442\x0432" + L"\x0443\x0439\x0442\x0435"}, + // Thai (ISO-8859) + {"windows-874", /* to be replaced with "iso-8859-11". */ + "\xCA\xC7\xD1\xCA\xB4\xD5" "\xA4\xC3\xD1\xBA", + OnStringUtilConversionError::FAIL, + true, + L"\x0E2A\x0E27\x0E31\x0E2A\x0E14\x0E35" + L"\x0E04\x0E23\x0e31\x0E1A"}, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kConvertCodepageCases); ++i) { + std::wstring wide; + bool success = CodepageToWide(kConvertCodepageCases[i].encoded, + kConvertCodepageCases[i].codepage_name, + kConvertCodepageCases[i].on_error, + &wide); + EXPECT_EQ(kConvertCodepageCases[i].success, success); + EXPECT_EQ(kConvertCodepageCases[i].wide, wide); + + // When decoding was successful and nothing was skipped, we also check the + // reverse conversion. + if (success && + kConvertCodepageCases[i].on_error == + OnStringUtilConversionError::FAIL) { + std::string encoded; + success = WideToCodepage(wide, kConvertCodepageCases[i].codepage_name, + kConvertCodepageCases[i].on_error, &encoded); + EXPECT_EQ(kConvertCodepageCases[i].success, success); + EXPECT_EQ(kConvertCodepageCases[i].encoded, encoded); + } + } + + // The above cases handled codepage->wide errors, but not wide->codepage. + // Test that here. + std::string encoded("Temp data"); // Make sure the string gets cleared. + + // First test going to an encoding that can not represent that character. + EXPECT_FALSE(WideToCodepage(L"Chinese\xff27", "iso-8859-1", + OnStringUtilConversionError::FAIL, &encoded)); + EXPECT_TRUE(encoded.empty()); + EXPECT_TRUE(WideToCodepage(L"Chinese\xff27", "iso-8859-1", + OnStringUtilConversionError::SKIP, &encoded)); + EXPECT_STREQ("Chinese", encoded.c_str()); + +#if defined(WCHAR_T_IS_UTF16) + // When we're in UTF-16 mode, test an invalid UTF-16 character in the input. + EXPECT_FALSE(WideToCodepage(L"a\xd800z", "iso-8859-1", + OnStringUtilConversionError::FAIL, &encoded)); + EXPECT_TRUE(encoded.empty()); + EXPECT_TRUE(WideToCodepage(L"a\xd800z", "iso-8859-1", + OnStringUtilConversionError::SKIP, &encoded)); + EXPECT_STREQ("az", encoded.c_str()); +#endif // WCHAR_T_IS_UTF16 + + // Invalid characters should fail. + EXPECT_TRUE(WideToCodepage(L"a\xffffz", "iso-8859-1", + OnStringUtilConversionError::SKIP, &encoded)); + EXPECT_STREQ("az", encoded.c_str()); + + // Invalid codepages should fail. + EXPECT_FALSE(WideToCodepage(L"Hello, world", "awesome-8571-2", + OnStringUtilConversionError::SKIP, &encoded)); +} + +TEST(StringUtilTest, ConvertASCII) { + static const char* char_cases[] = { + "Google Video", + "Hello, world\n", + "0123ABCDwxyz \a\b\t\r\n!+,.~" + }; + + static const wchar_t* const wchar_cases[] = { + L"Google Video", + L"Hello, world\n", + L"0123ABCDwxyz \a\b\t\r\n!+,.~" + }; + + for (size_t i = 0; i < arraysize(char_cases); ++i) { + EXPECT_TRUE(IsStringASCII(char_cases[i])); + std::wstring wide = ASCIIToWide(char_cases[i]); + EXPECT_EQ(wchar_cases[i], wide); + + EXPECT_TRUE(IsStringASCII(wchar_cases[i])); + std::string ascii = WideToASCII(wchar_cases[i]); + EXPECT_EQ(char_cases[i], ascii); + } + + EXPECT_FALSE(IsStringASCII("Google \x80Video")); + EXPECT_FALSE(IsStringASCII(L"Google \x80Video")); + + // Convert empty strings. + std::wstring wempty; + std::string empty; + EXPECT_EQ(empty, WideToASCII(wempty)); + EXPECT_EQ(wempty, ASCIIToWide(empty)); + + // Convert strings with an embedded NUL character. + const char chars_with_nul[] = "test\0string"; + const int length_with_nul = arraysize(chars_with_nul) - 1; + std::string string_with_nul(chars_with_nul, length_with_nul); + std::wstring wide_with_nul = ASCIIToWide(string_with_nul); + EXPECT_EQ(static_cast(length_with_nul), + wide_with_nul.length()); + std::string narrow_with_nul = WideToASCII(wide_with_nul); + EXPECT_EQ(static_cast(length_with_nul), + narrow_with_nul.length()); + EXPECT_EQ(0, string_with_nul.compare(narrow_with_nul)); +} + +TEST(StringUtilTest, ToUpperASCII) { + EXPECT_EQ('C', ToUpperASCII('C')); + EXPECT_EQ('C', ToUpperASCII('c')); + EXPECT_EQ('2', ToUpperASCII('2')); + + EXPECT_EQ(L'C', ToUpperASCII(L'C')); + EXPECT_EQ(L'C', ToUpperASCII(L'c')); + EXPECT_EQ(L'2', ToUpperASCII(L'2')); + + std::string in_place_a("Cc2"); + StringToUpperASCII(&in_place_a); + EXPECT_EQ("CC2", in_place_a); + + std::wstring in_place_w(L"Cc2"); + StringToUpperASCII(&in_place_w); + EXPECT_EQ(L"CC2", in_place_w); + + std::string original_a("Cc2"); + std::string upper_a = StringToUpperASCII(original_a); + EXPECT_EQ("CC2", upper_a); + + std::wstring original_w(L"Cc2"); + std::wstring upper_w = StringToUpperASCII(original_w); + EXPECT_EQ(L"CC2", upper_w); +} + +static const struct { + const wchar_t* src_w; + const char* src_a; + const char* dst; +} lowercase_cases[] = { + {L"FoO", "FoO", "foo"}, + {L"foo", "foo", "foo"}, + {L"FOO", "FOO", "foo"}, +}; + +TEST(StringUtilTest, LowerCaseEqualsASCII) { + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(lowercase_cases); ++i) { + EXPECT_TRUE(LowerCaseEqualsASCII(lowercase_cases[i].src_w, + lowercase_cases[i].dst)); + EXPECT_TRUE(LowerCaseEqualsASCII(lowercase_cases[i].src_a, + lowercase_cases[i].dst)); + } +} + +TEST(StringUtilTest, GetByteDisplayUnits) { + static const struct { + int64 bytes; + DataUnits expected; + } cases[] = { + {0, DATA_UNITS_BYTE}, + {512, DATA_UNITS_BYTE}, + {10*1024, DATA_UNITS_KILOBYTE}, + {10*1024*1024, DATA_UNITS_MEGABYTE}, + {10LL*1024*1024*1024, DATA_UNITS_GIGABYTE}, + {~(1LL<<63), DATA_UNITS_GIGABYTE}, +#ifdef NDEBUG + {-1, DATA_UNITS_BYTE}, +#endif + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) + EXPECT_EQ(cases[i].expected, GetByteDisplayUnits(cases[i].bytes)); +} + +TEST(StringUtilTest, FormatBytes) { + static const struct { + int64 bytes; + DataUnits units; + const wchar_t* expected; + const wchar_t* expected_with_units; + } cases[] = { + {0, DATA_UNITS_BYTE, L"0", L"0 B"}, + {512, DATA_UNITS_BYTE, L"512", L"512 B"}, + {512, DATA_UNITS_KILOBYTE, L"0.5", L"0.5 kB"}, + {1024*1024, DATA_UNITS_KILOBYTE, L"1024", L"1024 kB"}, + {1024*1024, DATA_UNITS_MEGABYTE, L"1", L"1 MB"}, + {1024*1024*1024, DATA_UNITS_GIGABYTE, L"1", L"1 GB"}, + {10LL*1024*1024*1024, DATA_UNITS_GIGABYTE, L"10", L"10 GB"}, + {~(1LL<<63), DATA_UNITS_GIGABYTE, L"8589934592", L"8589934592 GB"}, + // Make sure the first digit of the fractional part works. + {1024*1024 + 103, DATA_UNITS_KILOBYTE, L"1024.1", L"1024.1 kB"}, + {1024*1024 + 205 * 1024, DATA_UNITS_MEGABYTE, L"1.2", L"1.2 MB"}, + {1024*1024*1024 + (927 * 1024*1024), DATA_UNITS_GIGABYTE, + L"1.9", L"1.9 GB"}, + {10LL*1024*1024*1024, DATA_UNITS_GIGABYTE, L"10", L"10 GB"}, +#ifdef NDEBUG + {-1, DATA_UNITS_BYTE, L"", L""}, +#endif + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + EXPECT_EQ(cases[i].expected, + FormatBytes(cases[i].bytes, cases[i].units, false)); + EXPECT_EQ(cases[i].expected_with_units, + FormatBytes(cases[i].bytes, cases[i].units, true)); + } +} + +TEST(StringUtilTest, ReplaceSubstringsAfterOffset) { + static const struct { + const char* str; + string16::size_type start_offset; + const char* find_this; + const char* replace_with; + const char* expected; + } cases[] = { + {"aaa", 0, "a", "b", "bbb"}, + {"abb", 0, "ab", "a", "ab"}, + {"Removing some substrings inging", 0, "ing", "", "Remov some substrs "}, + {"Not found", 0, "x", "0", "Not found"}, + {"Not found again", 5, "x", "0", "Not found again"}, + {" Making it much longer ", 0, " ", "Four score and seven years ago", + "Four score and seven years agoMakingFour score and seven years agoit" + "Four score and seven years agomuchFour score and seven years agolonger" + "Four score and seven years ago"}, + {"Invalid offset", 9999, "t", "foobar", "Invalid offset"}, + {"Replace me only me once", 9, "me ", "", "Replace me only once"}, + {"abababab", 2, "ab", "c", "abccc"}, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { + string16 str = ASCIIToUTF16(cases[i].str); + ReplaceSubstringsAfterOffset(&str, cases[i].start_offset, + ASCIIToUTF16(cases[i].find_this), + ASCIIToUTF16(cases[i].replace_with)); + EXPECT_EQ(ASCIIToUTF16(cases[i].expected), str); + } +} + +TEST(StringUtilTest, ReplaceFirstSubstringAfterOffset) { + static const struct { + const char* str; + string16::size_type start_offset; + const char* find_this; + const char* replace_with; + const char* expected; + } cases[] = { + {"aaa", 0, "a", "b", "baa"}, + {"abb", 0, "ab", "a", "ab"}, + {"Removing some substrings inging", 0, "ing", "", + "Remov some substrings inging"}, + {"Not found", 0, "x", "0", "Not found"}, + {"Not found again", 5, "x", "0", "Not found again"}, + {" Making it much longer ", 0, " ", "Four score and seven years ago", + "Four score and seven years agoMaking it much longer "}, + {"Invalid offset", 9999, "t", "foobar", "Invalid offset"}, + {"Replace me only me once", 4, "me ", "", "Replace only me once"}, + {"abababab", 2, "ab", "c", "abcabab"}, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { + string16 str = ASCIIToUTF16(cases[i].str); + ReplaceFirstSubstringAfterOffset(&str, cases[i].start_offset, + ASCIIToUTF16(cases[i].find_this), + ASCIIToUTF16(cases[i].replace_with)); + EXPECT_EQ(ASCIIToUTF16(cases[i].expected), str); + } +} + +namespace { + +template +struct IntToStringTest { + INT num; + const char* sexpected; + const char* uexpected; +}; + +} + +TEST(StringUtilTest, IntToString) { + + static const IntToStringTest int_tests[] = { + { 0, "0", "0" }, + { -1, "-1", "4294967295" }, + { std::numeric_limits::max(), "2147483647", "2147483647" }, + { std::numeric_limits::min(), "-2147483648", "2147483648" }, + }; + static const IntToStringTest int64_tests[] = { + { 0, "0", "0" }, + { -1, "-1", "18446744073709551615" }, + { std::numeric_limits::max(), + "9223372036854775807", + "9223372036854775807", }, + { std::numeric_limits::min(), + "-9223372036854775808", + "9223372036854775808" }, + }; + + for (size_t i = 0; i < arraysize(int_tests); ++i) { + const IntToStringTest* test = &int_tests[i]; + EXPECT_EQ(IntToString(test->num), test->sexpected); + EXPECT_EQ(IntToWString(test->num), UTF8ToWide(test->sexpected)); + EXPECT_EQ(UintToString(test->num), test->uexpected); + EXPECT_EQ(UintToWString(test->num), UTF8ToWide(test->uexpected)); + } + for (size_t i = 0; i < arraysize(int64_tests); ++i) { + const IntToStringTest* test = &int64_tests[i]; + EXPECT_EQ(Int64ToString(test->num), test->sexpected); + EXPECT_EQ(Int64ToWString(test->num), UTF8ToWide(test->sexpected)); + EXPECT_EQ(Uint64ToString(test->num), test->uexpected); + EXPECT_EQ(Uint64ToWString(test->num), UTF8ToWide(test->uexpected)); + } +} + +TEST(StringUtilTest, Uint64ToString) { + static const struct { + uint64 input; + std::string output; + } cases[] = { + {0, "0"}, + {42, "42"}, + {INT_MAX, "2147483647"}, + {kuint64max, "18446744073709551615"}, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) + EXPECT_EQ(cases[i].output, Uint64ToString(cases[i].input)); +} + +TEST(StringUtilTest, StringToInt) { + static const struct { + std::string input; + int output; + bool success; + } cases[] = { + {"0", 0, true}, + {"42", 42, true}, + {"-2147483648", INT_MIN, true}, + {"2147483647", INT_MAX, true}, + {"", 0, false}, + {" 42", 42, false}, + {"42 ", 42, false}, + {"\t\n\v\f\r 42", 42, false}, + {"blah42", 0, false}, + {"42blah", 42, false}, + {"blah42blah", 0, false}, + {"-273.15", -273, false}, + {"+98.6", 98, false}, + {"--123", 0, false}, + {"++123", 0, false}, + {"-+123", 0, false}, + {"+-123", 0, false}, + {"-", 0, false}, + {"-2147483649", INT_MIN, false}, + {"-99999999999", INT_MIN, false}, + {"2147483648", INT_MAX, false}, + {"99999999999", INT_MAX, false}, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + EXPECT_EQ(cases[i].output, StringToInt(cases[i].input)); + int output; + EXPECT_EQ(cases[i].success, StringToInt(cases[i].input, &output)); + EXPECT_EQ(cases[i].output, output); + + std::wstring wide_input = ASCIIToWide(cases[i].input); + EXPECT_EQ(cases[i].output, StringToInt(WideToUTF16Hack(wide_input))); + EXPECT_EQ(cases[i].success, StringToInt(WideToUTF16Hack(wide_input), + &output)); + EXPECT_EQ(cases[i].output, output); + } + + // One additional test to verify that conversion of numbers in strings with + // embedded NUL characters. The NUL and extra data after it should be + // interpreted as junk after the number. + const char input[] = "6\06"; + std::string input_string(input, arraysize(input) - 1); + int output; + EXPECT_FALSE(StringToInt(input_string, &output)); + EXPECT_EQ(6, output); + + std::wstring wide_input = ASCIIToWide(input_string); + EXPECT_FALSE(StringToInt(WideToUTF16Hack(wide_input), &output)); + EXPECT_EQ(6, output); +} + +TEST(StringUtilTest, StringToInt64) { + static const struct { + std::string input; + int64 output; + bool success; + } cases[] = { + {"0", 0, true}, + {"42", 42, true}, + {"-2147483648", INT_MIN, true}, + {"2147483647", INT_MAX, true}, + {"-2147483649", GG_INT64_C(-2147483649), true}, + {"-99999999999", GG_INT64_C(-99999999999), true}, + {"2147483648", GG_INT64_C(2147483648), true}, + {"99999999999", GG_INT64_C(99999999999), true}, + {"9223372036854775807", kint64max, true}, + {"-9223372036854775808", kint64min, true}, + {"09", 9, true}, + {"-09", -9, true}, + {"", 0, false}, + {" 42", 42, false}, + {"42 ", 42, false}, + {"\t\n\v\f\r 42", 42, false}, + {"blah42", 0, false}, + {"42blah", 42, false}, + {"blah42blah", 0, false}, + {"-273.15", -273, false}, + {"+98.6", 98, false}, + {"--123", 0, false}, + {"++123", 0, false}, + {"-+123", 0, false}, + {"+-123", 0, false}, + {"-", 0, false}, + {"-9223372036854775809", kint64min, false}, + {"-99999999999999999999", kint64min, false}, + {"9223372036854775808", kint64max, false}, + {"99999999999999999999", kint64max, false}, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + EXPECT_EQ(cases[i].output, StringToInt64(cases[i].input)); + int64 output; + EXPECT_EQ(cases[i].success, StringToInt64(cases[i].input, &output)); + EXPECT_EQ(cases[i].output, output); + + std::wstring wide_input = ASCIIToWide(cases[i].input); + EXPECT_EQ(cases[i].output, StringToInt64(WideToUTF16Hack(wide_input))); + EXPECT_EQ(cases[i].success, StringToInt64(WideToUTF16Hack(wide_input), + &output)); + EXPECT_EQ(cases[i].output, output); + } + + // One additional test to verify that conversion of numbers in strings with + // embedded NUL characters. The NUL and extra data after it should be + // interpreted as junk after the number. + const char input[] = "6\06"; + std::string input_string(input, arraysize(input) - 1); + int64 output; + EXPECT_FALSE(StringToInt64(input_string, &output)); + EXPECT_EQ(6, output); + + std::wstring wide_input = ASCIIToWide(input_string); + EXPECT_FALSE(StringToInt64(WideToUTF16Hack(wide_input), &output)); + EXPECT_EQ(6, output); +} + +TEST(StringUtilTest, HexStringToInt) { + static const struct { + std::string input; + int output; + bool success; + } cases[] = { + {"0", 0, true}, + {"42", 66, true}, + {"-42", -66, true}, + {"+42", 66, true}, + {"7fffffff", INT_MAX, true}, + {"80000000", INT_MIN, true}, + {"ffffffff", -1, true}, + {"DeadBeef", 0xdeadbeef, true}, + {"0x42", 66, true}, + {"-0x42", -66, true}, + {"+0x42", 66, true}, + {"0x7fffffff", INT_MAX, true}, + {"0x80000000", INT_MIN, true}, + {"0xffffffff", -1, true}, + {"0XDeadBeef", 0xdeadbeef, true}, + {"0x0f", 15, true}, + {"0f", 15, true}, + {" 45", 0x45, false}, + {"\t\n\v\f\r 0x45", 0x45, false}, + {" 45", 0x45, false}, + {"45 ", 0x45, false}, + {"efgh", 0xef, false}, + {"0xefgh", 0xef, false}, + {"hgfe", 0, false}, + {"100000000", -1, false}, // don't care about |output|, just |success| + {"-", 0, false}, + {"", 0, false}, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + EXPECT_EQ(cases[i].output, HexStringToInt(cases[i].input)); + int output; + EXPECT_EQ(cases[i].success, HexStringToInt(cases[i].input, &output)); + EXPECT_EQ(cases[i].output, output); + + std::wstring wide_input = ASCIIToWide(cases[i].input); + EXPECT_EQ(cases[i].output, HexStringToInt(WideToUTF16Hack(wide_input))); + EXPECT_EQ(cases[i].success, HexStringToInt(WideToUTF16Hack(wide_input), + &output)); + EXPECT_EQ(cases[i].output, output); + } + // One additional test to verify that conversion of numbers in strings with + // embedded NUL characters. The NUL and extra data after it should be + // interpreted as junk after the number. + const char input[] = "0xc0ffee\09"; + std::string input_string(input, arraysize(input) - 1); + int output; + EXPECT_FALSE(HexStringToInt(input_string, &output)); + EXPECT_EQ(0xc0ffee, output); + + std::wstring wide_input = ASCIIToWide(input_string); + EXPECT_FALSE(HexStringToInt(WideToUTF16Hack(wide_input), &output)); + EXPECT_EQ(0xc0ffee, output); +} + +TEST(StringUtilTest, HexStringToBytes) { + static const struct { + const std::string input; + const char* output; + size_t output_len; + bool success; + } cases[] = { + {"0", "", 0, false}, // odd number of characters fails + {"00", "\0", 1, true}, + {"42", "\x42", 1, true}, + {"-42", "", 0, false}, // any non-hex value fails + {"+42", "", 0, false}, + {"7fffffff", "\x7f\xff\xff\xff", 4, true}, + {"80000000", "\x80\0\0\0", 4, true}, + {"deadbeef", "\xde\xad\xbe\xef", 4, true}, + {"DeadBeef", "\xde\xad\xbe\xef", 4, true}, + {"0x42", "", 0, false}, // leading 0x fails (x is not hex) + {"0f", "\xf", 1, true}, + {"45 ", "\x45", 1, false}, + {"efgh", "\xef", 1, false}, + {"", "", 0, false}, + {"0123456789ABCDEF", "\x01\x23\x45\x67\x89\xAB\xCD\xEF", 8, true}, + {"0123456789ABCDEF012345", + "\x01\x23\x45\x67\x89\xAB\xCD\xEF\x01\x23\x45", 11, true}, + }; + + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + std::vector output; + std::vector compare; + EXPECT_EQ(cases[i].success, HexStringToBytes(cases[i].input, &output)) << + i << ": " << cases[i].input; + for (size_t j = 0; j < cases[i].output_len; ++j) + compare.push_back(static_cast(cases[i].output[j])); + ASSERT_EQ(output.size(), compare.size()) << i << ": " << cases[i].input; + EXPECT_TRUE(std::equal(output.begin(), output.end(), compare.begin())) << + i << ": " << cases[i].input; + + output.clear(); + compare.clear(); + + std::wstring wide_input = ASCIIToWide(cases[i].input); + EXPECT_EQ(cases[i].success, + HexStringToBytes(WideToUTF16Hack(wide_input), &output)) << + i << ": " << cases[i].input; + for (size_t j = 0; j < cases[i].output_len; ++j) + compare.push_back(static_cast(cases[i].output[j])); + ASSERT_EQ(output.size(), compare.size()) << i << ": " << cases[i].input; + EXPECT_TRUE(std::equal(output.begin(), output.end(), compare.begin())) << + i << ": " << cases[i].input; + } +} + +TEST(StringUtilTest, StringToDouble) { + static const struct { + std::string input; + double output; + bool success; + } cases[] = { + {"0", 0.0, true}, + {"42", 42.0, true}, + {"-42", -42.0, true}, + {"123.45", 123.45, true}, + {"-123.45", -123.45, true}, + {"+123.45", 123.45, true}, + {"2.99792458e8", 299792458.0, true}, + {"149597870.691E+3", 149597870691.0, true}, + {"6.", 6.0, true}, + {"9e99999999999999999999", HUGE_VAL, false}, + {"-9e99999999999999999999", -HUGE_VAL, false}, + {"1e-2", 0.01, true}, + {" 1e-2", 0.01, false}, + {"1e-2 ", 0.01, false}, + {"-1E-7", -0.0000001, true}, + {"01e02", 100, true}, + {"2.3e15", 2.3e15, true}, + {"\t\n\v\f\r -123.45e2", -12345.0, false}, + {"+123 e4", 123.0, false}, + {"123e ", 123.0, false}, + {"123e", 123.0, false}, + {" 2.99", 2.99, false}, + {"1e3.4", 1000.0, false}, + {"nothing", 0.0, false}, + {"-", 0.0, false}, + {"+", 0.0, false}, + {"", 0.0, false}, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + EXPECT_DOUBLE_EQ(cases[i].output, StringToDouble(cases[i].input)); + double output; + EXPECT_EQ(cases[i].success, StringToDouble(cases[i].input, &output)); + EXPECT_DOUBLE_EQ(cases[i].output, output); + + std::wstring wide_input = ASCIIToWide(cases[i].input); + EXPECT_DOUBLE_EQ(cases[i].output, + StringToDouble(WideToUTF16Hack(wide_input))); + EXPECT_EQ(cases[i].success, StringToDouble(WideToUTF16Hack(wide_input), + &output)); + EXPECT_DOUBLE_EQ(cases[i].output, output); + } + + // One additional test to verify that conversion of numbers in strings with + // embedded NUL characters. The NUL and extra data after it should be + // interpreted as junk after the number. + const char input[] = "3.14\0159"; + std::string input_string(input, arraysize(input) - 1); + double output; + EXPECT_FALSE(StringToDouble(input_string, &output)); + EXPECT_DOUBLE_EQ(3.14, output); + + std::wstring wide_input = ASCIIToWide(input_string); + EXPECT_FALSE(StringToDouble(WideToUTF16Hack(wide_input), &output)); + EXPECT_DOUBLE_EQ(3.14, output); +} + +// This checks where we can use the assignment operator for a va_list. We need +// a way to do this since Visual C doesn't support va_copy, but assignment on +// va_list is not guaranteed to be a copy. See StringAppendVT which uses this +// capability. +static void VariableArgsFunc(const char* format, ...) { + va_list org; + va_start(org, format); + + va_list dup; + base::va_copy(dup, org); + int i1 = va_arg(org, int); + int j1 = va_arg(org, int); + char* s1 = va_arg(org, char*); + double d1 = va_arg(org, double); + va_end(org); + + int i2 = va_arg(dup, int); + int j2 = va_arg(dup, int); + char* s2 = va_arg(dup, char*); + double d2 = va_arg(dup, double); + + EXPECT_EQ(i1, i2); + EXPECT_EQ(j1, j2); + EXPECT_STREQ(s1, s2); + EXPECT_EQ(d1, d2); + + va_end(dup); +} + +TEST(StringUtilTest, VAList) { + VariableArgsFunc("%d %d %s %lf", 45, 92, "This is interesting", 9.21); +} + +TEST(StringUtilTest, StringPrintfEmptyFormat) { + const char* empty = ""; + EXPECT_EQ("", StringPrintf(empty)); + EXPECT_EQ("", StringPrintf("%s", "")); +} + +TEST(StringUtilTest, StringPrintfMisc) { + EXPECT_EQ("123hello w", StringPrintf("%3d%2s %1c", 123, "hello", 'w')); + EXPECT_EQ(L"123hello w", StringPrintf(L"%3d%2ls %1lc", 123, L"hello", 'w')); +} + +TEST(StringUtilTest, StringAppendfStringEmptyParam) { + std::string value("Hello"); + StringAppendF(&value, ""); + EXPECT_EQ("Hello", value); + + std::wstring valuew(L"Hello"); + StringAppendF(&valuew, L""); + EXPECT_EQ(L"Hello", valuew); +} + +TEST(StringUtilTest, StringAppendfEmptyString) { + std::string value("Hello"); + StringAppendF(&value, "%s", ""); + EXPECT_EQ("Hello", value); + + std::wstring valuew(L"Hello"); + StringAppendF(&valuew, L"%ls", L""); + EXPECT_EQ(L"Hello", valuew); +} + +TEST(StringUtilTest, StringAppendfString) { + std::string value("Hello"); + StringAppendF(&value, " %s", "World"); + EXPECT_EQ("Hello World", value); + + std::wstring valuew(L"Hello"); + StringAppendF(&valuew, L" %ls", L"World"); + EXPECT_EQ(L"Hello World", valuew); +} + +TEST(StringUtilTest, StringAppendfInt) { + std::string value("Hello"); + StringAppendF(&value, " %d", 123); + EXPECT_EQ("Hello 123", value); + + std::wstring valuew(L"Hello"); + StringAppendF(&valuew, L" %d", 123); + EXPECT_EQ(L"Hello 123", valuew); +} + +// Make sure that lengths exactly around the initial buffer size are handled +// correctly. +TEST(StringUtilTest, StringPrintfBounds) { + const int src_len = 1026; + char src[src_len]; + for (size_t i = 0; i < arraysize(src); i++) + src[i] = 'A'; + + wchar_t srcw[src_len]; + for (size_t i = 0; i < arraysize(srcw); i++) + srcw[i] = 'A'; + + for (int i = 1; i < 3; i++) { + src[src_len - i] = 0; + std::string out; + SStringPrintf(&out, "%s", src); + EXPECT_STREQ(src, out.c_str()); + + srcw[src_len - i] = 0; + std::wstring outw; + SStringPrintf(&outw, L"%ls", srcw); + EXPECT_STREQ(srcw, outw.c_str()); + } +} + +// Test very large sprintfs that will cause the buffer to grow. +TEST(StringUtilTest, Grow) { + char src[1026]; + for (size_t i = 0; i < arraysize(src); i++) + src[i] = 'A'; + src[1025] = 0; + + const char* fmt = "%sB%sB%sB%sB%sB%sB%s"; + + std::string out; + SStringPrintf(&out, fmt, src, src, src, src, src, src, src); + + char* ref = new char[320000]; +#if defined(OS_WIN) + sprintf_s(ref, 320000, fmt, src, src, src, src, src, src, src); +#elif defined(OS_POSIX) + snprintf(ref, 320000, fmt, src, src, src, src, src, src, src); +#endif + + EXPECT_STREQ(ref, out.c_str()); + delete[] ref; +} + +// Test the boundary condition for the size of the string_util's +// internal buffer. +TEST(StringUtilTest, GrowBoundary) { + const int string_util_buf_len = 1024; + // Our buffer should be one larger than the size of StringAppendVT's stack + // buffer. + const int buf_len = string_util_buf_len + 1; + char src[buf_len + 1]; // Need extra one for NULL-terminator. + for (int i = 0; i < buf_len; ++i) + src[i] = 'a'; + src[buf_len] = 0; + + std::string out; + SStringPrintf(&out, "%s", src); + + EXPECT_STREQ(src, out.c_str()); +} + +// TODO(evanm): what's the proper cross-platform test here? +#if defined(OS_WIN) +// sprintf in Visual Studio fails when given U+FFFF. This tests that the +// failure case is gracefuly handled. +TEST(StringUtilTest, Invalid) { + wchar_t invalid[2]; + invalid[0] = 0xffff; + invalid[1] = 0; + + std::wstring out; + SStringPrintf(&out, L"%ls", invalid); + EXPECT_STREQ(L"", out.c_str()); +} +#endif + +// Test for SplitString +TEST(StringUtilTest, SplitString) { + std::vector r; + + SplitString(L"a,b,c", L',', &r); + EXPECT_EQ(3U, r.size()); + EXPECT_EQ(r[0], L"a"); + EXPECT_EQ(r[1], L"b"); + EXPECT_EQ(r[2], L"c"); + r.clear(); + + SplitString(L"a, b, c", L',', &r); + EXPECT_EQ(3U, r.size()); + EXPECT_EQ(r[0], L"a"); + EXPECT_EQ(r[1], L"b"); + EXPECT_EQ(r[2], L"c"); + r.clear(); + + SplitString(L"a,,c", L',', &r); + EXPECT_EQ(3U, r.size()); + EXPECT_EQ(r[0], L"a"); + EXPECT_EQ(r[1], L""); + EXPECT_EQ(r[2], L"c"); + r.clear(); + + SplitString(L"", L'*', &r); + EXPECT_EQ(1U, r.size()); + EXPECT_EQ(r[0], L""); + r.clear(); + + SplitString(L"foo", L'*', &r); + EXPECT_EQ(1U, r.size()); + EXPECT_EQ(r[0], L"foo"); + r.clear(); + + SplitString(L"foo ,", L',', &r); + EXPECT_EQ(2U, r.size()); + EXPECT_EQ(r[0], L"foo"); + EXPECT_EQ(r[1], L""); + r.clear(); + + SplitString(L",", L',', &r); + EXPECT_EQ(2U, r.size()); + EXPECT_EQ(r[0], L""); + EXPECT_EQ(r[1], L""); + r.clear(); + + SplitString(L"\t\ta\t", L'\t', &r); + EXPECT_EQ(4U, r.size()); + EXPECT_EQ(r[0], L""); + EXPECT_EQ(r[1], L""); + EXPECT_EQ(r[2], L"a"); + EXPECT_EQ(r[3], L""); + r.clear(); + + SplitStringDontTrim(L"\t\ta\t", L'\t', &r); + EXPECT_EQ(4U, r.size()); + EXPECT_EQ(r[0], L""); + EXPECT_EQ(r[1], L""); + EXPECT_EQ(r[2], L"a"); + EXPECT_EQ(r[3], L""); + r.clear(); + + SplitString(L"\ta\t\nb\tcc", L'\n', &r); + EXPECT_EQ(2U, r.size()); + EXPECT_EQ(r[0], L"a"); + EXPECT_EQ(r[1], L"b\tcc"); + r.clear(); + + SplitStringDontTrim(L"\ta\t\nb\tcc", L'\n', &r); + EXPECT_EQ(2U, r.size()); + EXPECT_EQ(r[0], L"\ta\t"); + EXPECT_EQ(r[1], L"b\tcc"); + r.clear(); +} + +// Test for JoinString +TEST(StringUtilTest, JoinString) { + std::vector in; + EXPECT_EQ("", JoinString(in, ',')); + + in.push_back("a"); + EXPECT_EQ("a", JoinString(in, ',')); + + in.push_back("b"); + in.push_back("c"); + EXPECT_EQ("a,b,c", JoinString(in, ',')); + + in.push_back(""); + EXPECT_EQ("a,b,c,", JoinString(in, ',')); + in.push_back(" "); + EXPECT_EQ("a|b|c|| ", JoinString(in, '|')); +} + +TEST(StringUtilTest, StartsWith) { + EXPECT_TRUE(StartsWithASCII("javascript:url", "javascript", true)); + EXPECT_FALSE(StartsWithASCII("JavaScript:url", "javascript", true)); + EXPECT_TRUE(StartsWithASCII("javascript:url", "javascript", false)); + EXPECT_TRUE(StartsWithASCII("JavaScript:url", "javascript", false)); + EXPECT_FALSE(StartsWithASCII("java", "javascript", true)); + EXPECT_FALSE(StartsWithASCII("java", "javascript", false)); + EXPECT_FALSE(StartsWithASCII("", "javascript", false)); + EXPECT_FALSE(StartsWithASCII("", "javascript", true)); + EXPECT_TRUE(StartsWithASCII("java", "", false)); + EXPECT_TRUE(StartsWithASCII("java", "", true)); + + EXPECT_TRUE(StartsWith(L"javascript:url", L"javascript", true)); + EXPECT_FALSE(StartsWith(L"JavaScript:url", L"javascript", true)); + EXPECT_TRUE(StartsWith(L"javascript:url", L"javascript", false)); + EXPECT_TRUE(StartsWith(L"JavaScript:url", L"javascript", false)); + EXPECT_FALSE(StartsWith(L"java", L"javascript", true)); + EXPECT_FALSE(StartsWith(L"java", L"javascript", false)); + EXPECT_FALSE(StartsWith(L"", L"javascript", false)); + EXPECT_FALSE(StartsWith(L"", L"javascript", true)); + EXPECT_TRUE(StartsWith(L"java", L"", false)); + EXPECT_TRUE(StartsWith(L"java", L"", true)); +} + +TEST(StringUtilTest, GetStringFWithOffsets) { + std::vector offsets; + + ReplaceStringPlaceholders(ASCIIToUTF16("Hello, $1. Your number is $2."), + ASCIIToUTF16("1"), + ASCIIToUTF16("2"), + &offsets); + EXPECT_EQ(2U, offsets.size()); + EXPECT_EQ(7U, offsets[0]); + EXPECT_EQ(25U, offsets[1]); + offsets.clear(); + + ReplaceStringPlaceholders(ASCIIToUTF16("Hello, $2. Your number is $1."), + ASCIIToUTF16("1"), + ASCIIToUTF16("2"), + &offsets); + EXPECT_EQ(2U, offsets.size()); + EXPECT_EQ(25U, offsets[0]); + EXPECT_EQ(7U, offsets[1]); + offsets.clear(); +} + +TEST(StringUtilTest, SplitStringAlongWhitespace) { + struct TestData { + const std::wstring input; + const size_t expected_result_count; + const std::wstring output1; + const std::wstring output2; + } data[] = { + { L"a", 1, L"a", L"" }, + { L" ", 0, L"", L"" }, + { L" a", 1, L"a", L"" }, + { L" ab ", 1, L"ab", L"" }, + { L" ab c", 2, L"ab", L"c" }, + { L" ab c ", 2, L"ab", L"c" }, + { L" ab cd", 2, L"ab", L"cd" }, + { L" ab cd ", 2, L"ab", L"cd" }, + { L" \ta\t", 1, L"a", L"" }, + { L" b\ta\t", 2, L"b", L"a" }, + { L" b\tat", 2, L"b", L"at" }, + { L"b\tat", 2, L"b", L"at" }, + { L"b\t at", 2, L"b", L"at" }, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(data); ++i) { + std::vector results; + SplitStringAlongWhitespace(data[i].input, &results); + ASSERT_EQ(data[i].expected_result_count, results.size()); + if (data[i].expected_result_count > 0) + ASSERT_EQ(data[i].output1, results[0]); + if (data[i].expected_result_count > 1) + ASSERT_EQ(data[i].output2, results[1]); + } +} + +TEST(StringUtilTest, MatchPatternTest) { + EXPECT_EQ(MatchPattern(L"www.google.com", L"*.com"), true); + EXPECT_EQ(MatchPattern(L"www.google.com", L"*"), true); + EXPECT_EQ(MatchPattern(L"www.google.com", L"www*.g*.org"), false); + EXPECT_EQ(MatchPattern(L"Hello", L"H?l?o"), true); + EXPECT_EQ(MatchPattern(L"www.google.com", L"http://*)"), false); + EXPECT_EQ(MatchPattern(L"www.msn.com", L"*.COM"), false); + EXPECT_EQ(MatchPattern(L"Hello*1234", L"He??o\\*1*"), true); + EXPECT_EQ(MatchPattern(L"", L"*.*"), false); + EXPECT_EQ(MatchPattern(L"", L"*"), true); + EXPECT_EQ(MatchPattern(L"", L"?"), true); + EXPECT_EQ(MatchPattern(L"", L""), true); + EXPECT_EQ(MatchPattern(L"Hello", L""), false); + EXPECT_EQ(MatchPattern(L"Hello*", L"Hello*"), true); + EXPECT_EQ(MatchPattern("Hello*", "Hello*"), true); // narrow string +} + +TEST(StringUtilTest, LcpyTest) { + // Test the normal case where we fit in our buffer. + { + char dst[10]; + wchar_t wdst[10]; + EXPECT_EQ(7U, base::strlcpy(dst, "abcdefg", arraysize(dst))); + EXPECT_EQ(0, memcmp(dst, "abcdefg", 8)); + EXPECT_EQ(7U, base::wcslcpy(wdst, L"abcdefg", arraysize(wdst))); + EXPECT_EQ(0, memcmp(wdst, L"abcdefg", sizeof(wchar_t) * 8)); + } + + // Test dst_size == 0, nothing should be written to |dst| and we should + // have the equivalent of strlen(src). + { + char dst[2] = {1, 2}; + wchar_t wdst[2] = {1, 2}; + EXPECT_EQ(7U, base::strlcpy(dst, "abcdefg", 0)); + EXPECT_EQ(1, dst[0]); + EXPECT_EQ(2, dst[1]); + EXPECT_EQ(7U, base::wcslcpy(wdst, L"abcdefg", 0)); +#if defined(WCHAR_T_IS_UNSIGNED) + EXPECT_EQ(1U, wdst[0]); + EXPECT_EQ(2U, wdst[1]); +#else + EXPECT_EQ(1, wdst[0]); + EXPECT_EQ(2, wdst[1]); +#endif + } + + // Test the case were we _just_ competely fit including the null. + { + char dst[8]; + wchar_t wdst[8]; + EXPECT_EQ(7U, base::strlcpy(dst, "abcdefg", arraysize(dst))); + EXPECT_EQ(0, memcmp(dst, "abcdefg", 8)); + EXPECT_EQ(7U, base::wcslcpy(wdst, L"abcdefg", arraysize(wdst))); + EXPECT_EQ(0, memcmp(wdst, L"abcdefg", sizeof(wchar_t) * 8)); + } + + // Test the case were we we are one smaller, so we can't fit the null. + { + char dst[7]; + wchar_t wdst[7]; + EXPECT_EQ(7U, base::strlcpy(dst, "abcdefg", arraysize(dst))); + EXPECT_EQ(0, memcmp(dst, "abcdef", 7)); + EXPECT_EQ(7U, base::wcslcpy(wdst, L"abcdefg", arraysize(wdst))); + EXPECT_EQ(0, memcmp(wdst, L"abcdef", sizeof(wchar_t) * 7)); + } + + // Test the case were we are just too small. + { + char dst[3]; + wchar_t wdst[3]; + EXPECT_EQ(7U, base::strlcpy(dst, "abcdefg", arraysize(dst))); + EXPECT_EQ(0, memcmp(dst, "ab", 3)); + EXPECT_EQ(7U, base::wcslcpy(wdst, L"abcdefg", arraysize(wdst))); + EXPECT_EQ(0, memcmp(wdst, L"ab", sizeof(wchar_t) * 3)); + } +} + +TEST(StringUtilTest, WprintfFormatPortabilityTest) { + struct TestData { + const wchar_t* input; + bool portable; + } cases[] = { + { L"%ls", true }, + { L"%s", false }, + { L"%S", false }, + { L"%lS", false }, + { L"Hello, %s", false }, + { L"%lc", true }, + { L"%c", false }, + { L"%C", false }, + { L"%lC", false }, + { L"%ls %s", false }, + { L"%s %ls", false }, + { L"%s %ls %s", false }, + { L"%f", true }, + { L"%f %F", false }, + { L"%d %D", false }, + { L"%o %O", false }, + { L"%u %U", false }, + { L"%f %d %o %u", true }, + { L"%-8d (%02.1f%)", true }, + { L"% 10s", false }, + { L"% 10ls", true } + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + EXPECT_EQ(cases[i].portable, base::IsWprintfFormatPortable(cases[i].input)); + } +} + +TEST(StringUtilTest, ElideString) { + struct TestData { + const wchar_t* input; + int max_len; + bool result; + const wchar_t* output; + } cases[] = { + { L"Hello", 0, true, L"" }, + { L"", 0, false, L"" }, + { L"Hello, my name is Tom", 1, true, L"H" }, + { L"Hello, my name is Tom", 2, true, L"He" }, + { L"Hello, my name is Tom", 3, true, L"H.m" }, + { L"Hello, my name is Tom", 4, true, L"H..m" }, + { L"Hello, my name is Tom", 5, true, L"H...m" }, + { L"Hello, my name is Tom", 6, true, L"He...m" }, + { L"Hello, my name is Tom", 7, true, L"He...om" }, + { L"Hello, my name is Tom", 10, true, L"Hell...Tom" }, + { L"Hello, my name is Tom", 100, false, L"Hello, my name is Tom" } + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + std::wstring output; + EXPECT_EQ(cases[i].result, + ElideString(cases[i].input, cases[i].max_len, &output)); + EXPECT_TRUE(output == cases[i].output); + } +} + +TEST(StringUtilTest, HexEncode) { + std::string hex(HexEncode(NULL, 0)); + EXPECT_EQ(hex.length(), 0U); + unsigned char bytes[] = {0x01, 0xff, 0x02, 0xfe, 0x03, 0x80, 0x81}; + hex = HexEncode(bytes, sizeof(bytes)); + EXPECT_EQ(hex.compare("01FF02FE038081"), 0); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_util_win.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_util_win.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/string_util_win.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/string_util_win.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,51 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STRING_UTIL_WIN_H_ +#define BASE_STRING_UTIL_WIN_H_ + +#include +#include +#include +#include + +#include "base/logging.h" + +namespace base { + +// Chromium code style is to not use malloc'd strings; this is only for use +// for interaction with APIs that require it. +inline char* strdup(const char* str) { + return _strdup(str); +} + +inline int strcasecmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} + +inline int strncasecmp(const char* s1, const char* s2, size_t count) { + return _strnicmp(s1, s2, count); +} + +inline int vsnprintf(char* buffer, size_t size, + const char* format, va_list arguments) { + int length = vsnprintf_s(buffer, size, size - 1, format, arguments); + if (length < 0) + return _vscprintf(format, arguments); + return length; +} + +inline int vswprintf(wchar_t* buffer, size_t size, + const wchar_t* format, va_list arguments) { + DCHECK(IsWprintfFormatPortable(format)); + + int length = _vsnwprintf_s(buffer, size, size - 1, format, arguments); + if (length < 0) + return _vscwprintf(format, arguments); + return length; +} + +} // namespace base + +#endif // BASE_STRING_UTIL_WIN_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_info.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_info.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_info.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_info.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,83 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SYS_INFO_H_ +#define BASE_SYS_INFO_H_ + +#include "base/basictypes.h" + +#include + +namespace base { + +class SysInfo { + public: + // Return the number of logical processors/cores on the current machine. + // WARNING: On POSIX, this method uses static variables and is not threadsafe + // until it's been initialized by being called once without a race. + static int NumberOfProcessors(); + + // Return the number of bytes of physical memory on the current machine. + static int64 AmountOfPhysicalMemory(); + + // Return the number of megabytes of physical memory on the current machine. + static int AmountOfPhysicalMemoryMB() { + return static_cast(AmountOfPhysicalMemory() / 1024 / 1024); + } + + // Return the available disk space in bytes on the volume containing |path|, + // or -1 on failure. + static int64 AmountOfFreeDiskSpace(const std::wstring& path); + + // Return true if the given environment variable is defined. + // TODO: find a better place for HasEnvVar. + static bool HasEnvVar(const wchar_t* var); + + // Return the value of the given environment variable + // or an empty string if not defined. + // TODO: find a better place for GetEnvVar. + static std::wstring GetEnvVar(const wchar_t* var); + + // Returns the name of the host operating system. + static std::string OperatingSystemName(); + + // Returns the version of the host operating system. + static std::string OperatingSystemVersion(); + + // Retrieves detailed numeric values for the OS version. + // WARNING: On OS X, this method uses static variables and is not threadsafe + // until it's been initialized by being called once without a race. + // TODO(port): Implement a Linux version of this method and enable the + // corresponding unit test. + static void OperatingSystemVersionNumbers(int32 *major_version, + int32 *minor_version, + int32 *bugfix_version); + + // Returns the CPU architecture of the system. Exact return value may differ + // across platforms. + static std::string CPUArchitecture(); + + // Returns the pixel dimensions of the primary display via the + // width and height parameters. + static void GetPrimaryDisplayDimensions(int* width, int* height); + + // Return the number of displays. + static int DisplayCount(); + + // Return the smallest amount of memory (in bytes) which the VM system will + // allocate. + static size_t VMAllocationGranularity(); + +#if defined(OS_MACOSX) + // Under the OS X Sandbox, our access to the system is limited, this call + // caches the system info on startup before we turn the Sandbox on. + // The above functions are all wired up to return the cached value so the rest + // of the code can call them in the Sandbox without worrying. + static void CacheSysInfo(); +#endif +}; + +} // namespace base + +#endif // BASE_SYS_INFO_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_info_mac.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_info_mac.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_info_mac.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_info_mac.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,46 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/sys_info.h" + +#include + +namespace base { + +// static +void SysInfo::OperatingSystemVersionNumbers(int32 *major_version, + int32 *minor_version, + int32 *bugfix_version) { + static bool is_initialized = false; + static int32 major_version_cached = 0; + static int32 minor_version_cached = 0; + static int32 bugfix_version_cached = 0; + + if (!is_initialized) { + // Gestalt can't be called in the sandbox, so we cache its return value. + Gestalt(gestaltSystemVersionMajor, + reinterpret_cast(&major_version_cached)); + Gestalt(gestaltSystemVersionMinor, + reinterpret_cast(&minor_version_cached)); + Gestalt(gestaltSystemVersionBugFix, + reinterpret_cast(&bugfix_version_cached)); + is_initialized = true; + } + + *major_version = major_version_cached; + *minor_version = minor_version_cached; + *bugfix_version = bugfix_version_cached; +} + +// static +void SysInfo::CacheSysInfo() { + // Due to startup time concerns [premature optimization?] we only cache values + // from functions we know to be called in the renderer & fail when the sandbox + // is enabled. + NumberOfProcessors(); + int32 dummy; + OperatingSystemVersionNumbers(&dummy, &dummy, &dummy); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_info_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_info_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_info_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_info_posix.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,137 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/sys_info.h" +#include "base/basictypes.h" + +#include +#include +#include +#include +#include + +#if defined(OS_MACOSX) +#include +#include +#endif + +#include "base/logging.h" +#include "base/string_util.h" + +namespace base { + +int SysInfo::NumberOfProcessors() { + // It seems that sysconf returns the number of "logical" processors on both + // mac and linux. So we get the number of "online logical" processors. + static long res = sysconf(_SC_NPROCESSORS_ONLN); + if (res == -1) { + NOTREACHED(); + return 1; + } + + return static_cast(res); +} + +// static +int64 SysInfo::AmountOfPhysicalMemory() { + // _SC_PHYS_PAGES is not part of POSIX and not available on OS X +#if defined(OS_MACOSX) + struct host_basic_info hostinfo; + mach_msg_type_number_t count = HOST_BASIC_INFO_COUNT; + int result = host_info(mach_host_self(), + HOST_BASIC_INFO, + reinterpret_cast(&hostinfo), + &count); + DCHECK_EQ(HOST_BASIC_INFO_COUNT, count); + if (result != KERN_SUCCESS) { + NOTREACHED(); + return 0; + } + + return static_cast(hostinfo.max_mem); +#else + long pages = sysconf(_SC_PHYS_PAGES); + long page_size = sysconf(_SC_PAGE_SIZE); + if (pages == -1 || page_size == -1) { + NOTREACHED(); + return 0; + } + + return static_cast(pages) * page_size; +#endif +} + +// static +int64 SysInfo::AmountOfFreeDiskSpace(const std::wstring& path) { + struct statvfs stats; + if (statvfs(WideToUTF8(path).c_str(), &stats) != 0) { + return -1; + } + return static_cast(stats.f_bavail) * stats.f_frsize; +} + +// static +bool SysInfo::HasEnvVar(const wchar_t* var) { + std::string var_utf8 = WideToUTF8(std::wstring(var)); + return getenv(var_utf8.c_str()) != NULL; +} + +// static +std::wstring SysInfo::GetEnvVar(const wchar_t* var) { + std::string var_utf8 = WideToUTF8(std::wstring(var)); + char* value = getenv(var_utf8.c_str()); + if (!value) { + return L""; + } else { + return UTF8ToWide(value); + } +} + +// static +std::string SysInfo::OperatingSystemName() { + utsname info; + if (uname(&info) < 0) { + NOTREACHED(); + return ""; + } + return std::string(info.sysname); +} + +// static +std::string SysInfo::OperatingSystemVersion() { + utsname info; + if (uname(&info) < 0) { + NOTREACHED(); + return ""; + } + return std::string(info.release); +} + +// static +std::string SysInfo::CPUArchitecture() { + utsname info; + if (uname(&info) < 0) { + NOTREACHED(); + return ""; + } + return std::string(info.machine); +} + +// static +void SysInfo::GetPrimaryDisplayDimensions(int* width, int* height) { + NOTIMPLEMENTED(); +} + +// static +int SysInfo::DisplayCount() { + NOTIMPLEMENTED(); + return 1; +} + +// static +size_t SysInfo::VMAllocationGranularity() { + return getpagesize(); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_info_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_info_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_info_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_info_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,54 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_util.h" +#include "base/sys_info.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +typedef PlatformTest SysInfoTest; + +TEST_F(SysInfoTest, NumProcs) { + // We aren't actually testing that it's correct, just that it's sane. + EXPECT_GE(base::SysInfo::NumberOfProcessors(), 1); +} + +TEST_F(SysInfoTest, AmountOfMem) { + // We aren't actually testing that it's correct, just that it's sane. + EXPECT_GT(base::SysInfo::AmountOfPhysicalMemory(), 0); + EXPECT_GT(base::SysInfo::AmountOfPhysicalMemoryMB(), 0); +} + +TEST_F(SysInfoTest, AmountOfFreeDiskSpace) { + // We aren't actually testing that it's correct, just that it's sane. + std::wstring tmp_path; + ASSERT_TRUE(file_util::GetTempDir(&tmp_path)); + EXPECT_GT(base::SysInfo::AmountOfFreeDiskSpace(tmp_path), 0) << tmp_path; +} + +TEST_F(SysInfoTest, GetEnvVar) { + // Every setup should have non-empty PATH... + EXPECT_NE(base::SysInfo::GetEnvVar(L"PATH"), L""); +} + +TEST_F(SysInfoTest, HasEnvVar) { + // Every setup should have PATH... + EXPECT_TRUE(base::SysInfo::HasEnvVar(L"PATH")); +} + +// TODO(port): If and when there's a LINUX version of this method, enable this +// unit test. +#if defined(OS_WIN) || defined(OS_MACOSX) +TEST_F(SysInfoTest, OperatingSystemVersionNumbers) { + int32 os_major_version = -1; + int32 os_minor_version = -1; + int32 os_bugfix_version = -1; + base::SysInfo::OperatingSystemVersionNumbers(&os_major_version, + &os_minor_version, + &os_bugfix_version); + EXPECT_GT(os_major_version, -1); + EXPECT_GT(os_minor_version, -1); + EXPECT_GT(os_bugfix_version, -1); +} +#endif // OS_WIN || OS_MACOSX diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_info_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_info_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_info_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_info_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,123 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/sys_info.h" + +#include + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" + +namespace base { + +// static +int SysInfo::NumberOfProcessors() { + SYSTEM_INFO info; + GetSystemInfo(&info); + return static_cast(info.dwNumberOfProcessors); +} + +// static +int64 SysInfo::AmountOfPhysicalMemory() { + MEMORYSTATUSEX memory_info; + memory_info.dwLength = sizeof(memory_info); + if (!GlobalMemoryStatusEx(&memory_info)) { + NOTREACHED(); + return 0; + } + + int64 rv = static_cast(memory_info.ullTotalPhys); + if (rv < 0) + rv = kint64max; + return rv; +} + +// static +int64 SysInfo::AmountOfFreeDiskSpace(const std::wstring& path) { + ULARGE_INTEGER available, total, free; + if (!GetDiskFreeSpaceExW(path.c_str(), &available, &total, &free)) { + return -1; + } + int64 rv = static_cast(available.QuadPart); + if (rv < 0) + rv = kint64max; + return rv; +} + +// static +bool SysInfo::HasEnvVar(const wchar_t* var) { + return GetEnvironmentVariable(var, NULL, 0) != 0; +} + +// static +std::wstring SysInfo::GetEnvVar(const wchar_t* var) { + DWORD value_length = GetEnvironmentVariable(var, NULL, 0); + if (value_length == 0) { + return L""; + } + scoped_array value(new wchar_t[value_length]); + GetEnvironmentVariable(var, value.get(), value_length); + return std::wstring(value.get()); +} + +// static +std::string SysInfo::OperatingSystemName() { + return "Windows NT"; +} + +// static +std::string SysInfo::OperatingSystemVersion() { + OSVERSIONINFO info = {0}; + info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + GetVersionEx(&info); + + return StringPrintf("%lu.%lu", info.dwMajorVersion, info.dwMinorVersion); +} + +// TODO: Implement OperatingSystemVersionComplete, which would include +// patchlevel/service pack number. See chrome/browser/views/bug_report_view.cc, +// BugReportView::SetOSVersion. + +// static +std::string SysInfo::CPUArchitecture() { + // TODO: Make this vary when we support any other architectures. + return "x86"; +} + +// static +void SysInfo::GetPrimaryDisplayDimensions(int* width, int* height) { + if (width) + *width = GetSystemMetrics(SM_CXSCREEN); + + if (height) + *height = GetSystemMetrics(SM_CYSCREEN); +} + +// static +int SysInfo::DisplayCount() { + return GetSystemMetrics(SM_CMONITORS); +} + +// static +size_t SysInfo::VMAllocationGranularity() { + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + + return sysinfo.dwAllocationGranularity; +} + +// static +void SysInfo::OperatingSystemVersionNumbers(int32 *major_version, + int32 *minor_version, + int32 *bugfix_version) { + OSVERSIONINFO info = {0}; + info.dwOSVersionInfoSize = sizeof(info); + GetVersionEx(&info); + *major_version = info.dwMajorVersion; + *minor_version = info.dwMinorVersion; + *bugfix_version = 0; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,83 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SYS_STRING_CONVERSIONS_H_ +#define BASE_SYS_STRING_CONVERSIONS_H_ + +// Provides system-dependent string type conversions for cases where it's +// necessary to not use ICU. Generally, you should not need this in Chrome, +// but it is used in some shared code. Dependencies should be minimal. + +#include +#include "base/basictypes.h" +#include "base/string16.h" + +#if defined(OS_MACOSX) +#include +#ifdef __OBJC__ +@class NSString; +#else +class NSString; +#endif +#endif // OS_MACOSX + +class StringPiece; + +namespace base { + +// Converts between wide and UTF-8 representations of a string. On error, the +// result is system-dependent. +std::string SysWideToUTF8(const std::wstring& wide); +std::wstring SysUTF8ToWide(const StringPiece& utf8); + +// Converts between wide and the system multi-byte representations of a string. +// DANGER: This will lose information and can change (on Windows, this can +// change between reboots). +std::string SysWideToNativeMB(const std::wstring& wide); +std::wstring SysNativeMBToWide(const StringPiece& native_mb); + +// Windows-specific ------------------------------------------------------------ + +#if defined(OS_WIN) + +// Converts between 8-bit and wide strings, using the given code page. The +// code page identifier is one accepted by the Windows function +// MultiByteToWideChar(). +std::wstring SysMultiByteToWide(const StringPiece& mb, uint32 code_page); +std::string SysWideToMultiByte(const std::wstring& wide, uint32 code_page); + +#endif // defined(OS_WIN) + +// Mac-specific ---------------------------------------------------------------- + +#if defined(OS_MACOSX) + +// Converts between STL strings and CFStringRefs/NSStrings. + +// Creates a string, and returns it with a refcount of 1. You are responsible +// for releasing it. Returns NULL on failure. +CFStringRef SysUTF8ToCFStringRef(const std::string& utf8); +CFStringRef SysUTF16ToCFStringRef(const string16& utf16); +CFStringRef SysWideToCFStringRef(const std::wstring& wide); + +// Same, but returns an autoreleased NSString. +NSString* SysUTF8ToNSString(const std::string& utf8); +NSString* SysUTF16ToNSString(const string16& utf16); +NSString* SysWideToNSString(const std::wstring& wide); + +// Converts a CFStringRef to an STL string. Returns an empty string on failure. +std::string SysCFStringRefToUTF8(CFStringRef ref); +string16 SysCFStringRefToUTF16(CFStringRef ref); +std::wstring SysCFStringRefToWide(CFStringRef ref); + +// Same, but accepts NSString input. +std::string SysNSStringToUTF8(NSString* ref); +string16 SysNSStringToUTF16(NSString* ref); +std::wstring SysNSStringToWide(NSString* ref); + +#endif // defined(OS_MACOSX) + +} // namespace base + +#endif // BASE_SYS_STRING_CONVERSIONS_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions_linux.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions_linux.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions_linux.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions_linux.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,35 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/sys_string_conversions.h" + +#include "base/string_piece.h" +#include "base/string_util.h" + +namespace base { + +std::string SysWideToUTF8(const std::wstring& wide) { + // In theory this should be using the system-provided conversion rather + // than our ICU, but this will do for now. + return WideToUTF8(wide); +} +std::wstring SysUTF8ToWide(const StringPiece& utf8) { + // In theory this should be using the system-provided conversion rather + // than our ICU, but this will do for now. + std::wstring out; + UTF8ToWide(utf8.data(), utf8.size(), &out); + return out; +} + +std::string SysWideToNativeMB(const std::wstring& wide) { + // TODO(evanm): we can't assume Linux is UTF-8. + return SysWideToUTF8(wide); +} + +std::wstring SysNativeMBToWide(const StringPiece& native_mb) { + // TODO(evanm): we can't assume Linux is UTF-8. + return SysUTF8ToWide(native_mb); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions_mac.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions_mac.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions_mac.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions_mac.mm 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,198 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/sys_string_conversions.h" + +#import + +#include + +#include "base/foundation_utils_mac.h" +#include "base/scoped_cftyperef.h" +#include "base/string_piece.h" + +namespace base { + +namespace { + +// Convert the supplied CFString into the specified encoding, and return it as +// an STL string of the template type. Returns an empty string on failure. +// +// Do not assert in this function since it is used by the asssertion code! +template +static StringType CFStringToSTLStringWithEncodingT(CFStringRef cfstring, + CFStringEncoding encoding) { + CFIndex length = CFStringGetLength(cfstring); + if (length == 0) + return StringType(); + + CFRange whole_string = CFRangeMake(0, length); + CFIndex out_size; + CFIndex converted = CFStringGetBytes(cfstring, + whole_string, + encoding, + 0, // lossByte + false, // isExternalRepresentation + NULL, // buffer + 0, // maxBufLen + &out_size); + if (converted == 0 || out_size == 0) + return StringType(); + + // out_size is the number of UInt8-sized units needed in the destination. + // A buffer allocated as UInt8 units might not be properly aligned to + // contain elements of StringType::value_type. Use a container for the + // proper value_type, and convert out_size by figuring the number of + // value_type elements per UInt8. Leave room for a NUL terminator. + typename StringType::size_type elements = + out_size * sizeof(UInt8) / sizeof(typename StringType::value_type) + 1; + + std::vector out_buffer(elements); + converted = CFStringGetBytes(cfstring, + whole_string, + encoding, + 0, // lossByte + false, // isExternalRepresentation + reinterpret_cast(&out_buffer[0]), + out_size, + NULL); // usedBufLen + if (converted == 0) + return StringType(); + + out_buffer[elements - 1] = '\0'; + return StringType(&out_buffer[0], elements - 1); +} + +// Given an STL string |in| with an encoding specified by |in_encoding|, +// convert it to |out_encoding| and return it as an STL string of the +// |OutStringType| template type. Returns an empty string on failure. +// +// Do not assert in this function since it is used by the asssertion code! +template +static OutStringType STLStringToSTLStringWithEncodingsT( + const InStringType& in, + CFStringEncoding in_encoding, + CFStringEncoding out_encoding) { + typename InStringType::size_type in_length = in.length(); + if (in_length == 0) + return OutStringType(); + + scoped_cftyperef cfstring( + CFStringCreateWithBytesNoCopy(NULL, + reinterpret_cast(in.data()), + in_length * + sizeof(typename InStringType::value_type), + in_encoding, + false, + kCFAllocatorNull)); + if (!cfstring) + return OutStringType(); + + return CFStringToSTLStringWithEncodingT(cfstring, + out_encoding); +} + +// Given an STL string |in| with an encoding specified by |in_encoding|, +// return it as a CFStringRef. Returns NULL on failure. +template +static CFStringRef STLStringToCFStringWithEncodingsT( + const StringType& in, + CFStringEncoding in_encoding) { + typename StringType::size_type in_length = in.length(); + if (in_length == 0) + return CFSTR(""); + + return CFStringCreateWithBytes(kCFAllocatorDefault, + reinterpret_cast(in.data()), + in_length * + sizeof(typename StringType::value_type), + in_encoding, + false); +} + +// Specify the byte ordering explicitly, otherwise CFString will be confused +// when strings don't carry BOMs, as they typically won't. +static const CFStringEncoding kNarrowStringEncoding = kCFStringEncodingUTF8; +#ifdef __BIG_ENDIAN__ +static const CFStringEncoding kMediumStringEncoding = kCFStringEncodingUTF16BE; +static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32BE; +#elif defined(__LITTLE_ENDIAN__) +static const CFStringEncoding kMediumStringEncoding = kCFStringEncodingUTF16LE; +static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32LE; +#endif // __LITTLE_ENDIAN__ + +} // namespace + +// Do not assert in this function since it is used by the asssertion code! +std::string SysWideToUTF8(const std::wstring& wide) { + return STLStringToSTLStringWithEncodingsT( + wide, kWideStringEncoding, kNarrowStringEncoding); +} + +// Do not assert in this function since it is used by the asssertion code! +std::wstring SysUTF8ToWide(const StringPiece& utf8) { + return STLStringToSTLStringWithEncodingsT( + utf8, kNarrowStringEncoding, kWideStringEncoding); +} + +std::string SysWideToNativeMB(const std::wstring& wide) { + return SysWideToUTF8(wide); +} + +std::wstring SysNativeMBToWide(const StringPiece& native_mb) { + return SysUTF8ToWide(native_mb); +} + +CFStringRef SysUTF8ToCFStringRef(const std::string& utf8) { + return STLStringToCFStringWithEncodingsT(utf8, kNarrowStringEncoding); +} + +CFStringRef SysUTF16ToCFStringRef(const string16& utf16) { + return STLStringToCFStringWithEncodingsT(utf16, kMediumStringEncoding); +} + +CFStringRef SysWideToCFStringRef(const std::wstring& wide) { + return STLStringToCFStringWithEncodingsT(wide, kWideStringEncoding); +} + +NSString* SysUTF8ToNSString(const std::string& utf8) { + return CFTypeRefToNSObjectAutorelease(SysUTF8ToCFStringRef(utf8)); +} + +NSString* SysUTF16ToNSString(const string16& utf16) { + return CFTypeRefToNSObjectAutorelease(SysUTF16ToCFStringRef(utf16)); +} + +NSString* SysWideToNSString(const std::wstring& wide) { + return CFTypeRefToNSObjectAutorelease(SysWideToCFStringRef(wide)); +} + +std::string SysCFStringRefToUTF8(CFStringRef ref) { + return CFStringToSTLStringWithEncodingT(ref, + kNarrowStringEncoding); +} + +string16 SysCFStringRefToUTF16(CFStringRef ref) { + return CFStringToSTLStringWithEncodingT(ref, + kMediumStringEncoding); +} + +std::wstring SysCFStringRefToWide(CFStringRef ref) { + return CFStringToSTLStringWithEncodingT(ref, + kWideStringEncoding); +} + +std::string SysNSStringToUTF8(NSString* nsstring) { + return SysCFStringRefToUTF8(reinterpret_cast(nsstring)); +} + +string16 SysNSStringToUTF16(NSString* nsstring) { + return SysCFStringRefToUTF16(reinterpret_cast(nsstring)); +} + +std::wstring SysNSStringToWide(NSString* nsstring) { + return SysCFStringRefToWide(reinterpret_cast(nsstring)); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,68 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/string_piece.h" +#include "base/sys_string_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" + +#ifdef WCHAR_T_IS_UTF32 +static const std::wstring kSysWideOldItalicLetterA = L"\x10300"; +#else +static const std::wstring kSysWideOldItalicLetterA = L"\xd800\xdf00"; +#endif + +TEST(SysStrings, SysWideToUTF8) { + using base::SysWideToUTF8; + EXPECT_EQ("Hello, world", SysWideToUTF8(L"Hello, world")); + EXPECT_EQ("\xe4\xbd\xa0\xe5\xa5\xbd", SysWideToUTF8(L"\x4f60\x597d")); + + // >16 bits + EXPECT_EQ("\xF0\x90\x8C\x80", SysWideToUTF8(kSysWideOldItalicLetterA)); + + // Error case. When Windows finds a UTF-16 character going off the end of + // a string, it just converts that literal value to UTF-8, even though this + // is invalid. + // + // This is what XP does, but Vista has different behavior, so we don't bother + // verifying it: + //EXPECT_EQ("\xE4\xBD\xA0\xED\xA0\x80zyxw", + // SysWideToUTF8(L"\x4f60\xd800zyxw")); + + // Test embedded NULLs. + std::wstring wide_null(L"a"); + wide_null.push_back(0); + wide_null.push_back('b'); + + std::string expected_null("a"); + expected_null.push_back(0); + expected_null.push_back('b'); + + EXPECT_EQ(expected_null, SysWideToUTF8(wide_null)); +} + +TEST(SysStrings, SysUTF8ToWide) { + using base::SysUTF8ToWide; + EXPECT_EQ(L"Hello, world", SysUTF8ToWide("Hello, world")); + EXPECT_EQ(L"\x4f60\x597d", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5\xbd")); + // >16 bits + EXPECT_EQ(kSysWideOldItalicLetterA, SysUTF8ToWide("\xF0\x90\x8C\x80")); + + // Error case. When Windows finds an invalid UTF-8 character, it just skips + // it. This seems weird because it's inconsistent with the reverse conversion. + // + // This is what XP does, but Vista has different behavior, so we don't bother + // verifying it: + //EXPECT_EQ(L"\x4f60zyxw", SysUTF8ToWide("\xe4\xbd\xa0\xe5\xa5zyxw")); + + // Test embedded NULLs. + std::string utf8_null("a"); + utf8_null.push_back(0); + utf8_null.push_back('b'); + + std::wstring expected_null(L"a"); + expected_null.push_back(0); + expected_null.push_back('b'); + + EXPECT_EQ(expected_null, SysUTF8ToWide(utf8_null)); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/sys_string_conversions_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,70 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/sys_string_conversions.h" + +#include + +#include "base/string_piece.h" + +namespace base { + +// Do not assert in this function since it is used by the asssertion code! +std::string SysWideToUTF8(const std::wstring& wide) { + return SysWideToMultiByte(wide, CP_UTF8); +} + +// Do not assert in this function since it is used by the asssertion code! +std::wstring SysUTF8ToWide(const StringPiece& utf8) { + return SysMultiByteToWide(utf8, CP_UTF8); +} + +std::string SysWideToNativeMB(const std::wstring& wide) { + return SysWideToMultiByte(wide, CP_ACP); +} + +std::wstring SysNativeMBToWide(const StringPiece& native_mb) { + return SysMultiByteToWide(native_mb, CP_ACP); +} + +// Do not assert in this function since it is used by the asssertion code! +std::wstring SysMultiByteToWide(const StringPiece& mb, uint32 code_page) { + if (mb.empty()) + return std::wstring(); + + int mb_length = static_cast(mb.length()); + // Compute the length of the buffer. + int charcount = MultiByteToWideChar(code_page, 0, + mb.data(), mb_length, NULL, 0); + if (charcount == 0) + return std::wstring(); + + std::wstring wide; + wide.resize(charcount); + MultiByteToWideChar(code_page, 0, mb.data(), mb_length, &wide[0], charcount); + + return wide; +} + +// Do not assert in this function since it is used by the asssertion code! +std::string SysWideToMultiByte(const std::wstring& wide, uint32 code_page) { + int wide_length = static_cast(wide.length()); + if (wide_length == 0) + return std::string(); + + // Compute the length of the buffer we'll need. + int charcount = WideCharToMultiByte(code_page, 0, wide.data(), wide_length, + NULL, 0, NULL, NULL); + if (charcount == 0) + return std::string(); + + std::string mb; + mb.resize(charcount); + WideCharToMultiByte(code_page, 0, wide.data(), wide_length, + &mb[0], charcount, NULL, NULL); + + return mb; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/system_monitor.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/system_monitor.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/system_monitor.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/system_monitor.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,89 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/system_monitor.h" +#include "base/logging.h" +#include "base/message_loop.h" + +namespace base { + +#if defined(ENABLE_BATTERY_MONITORING) +// The amount of time (in ms) to wait before running the initial +// battery check. +static int kDelayedBatteryCheckMs = 10 * 1000; +#endif // defined(ENABLE_BATTERY_MONITORING) + +SystemMonitor::SystemMonitor() + : battery_in_use_(false), + suspended_(false) { + observer_list_ = new ObserverListThreadSafe(); +} + +void SystemMonitor::ProcessPowerMessage(PowerEvent event_id) { + // Suppress duplicate notifications. Some platforms may + // send multiple notifications of the same event. + switch (event_id) { + case POWER_STATE_EVENT: + { + bool on_battery = IsBatteryPower(); + if (on_battery != battery_in_use_) { + battery_in_use_ = on_battery; + NotifyPowerStateChange(); + } + } + break; + case RESUME_EVENT: + if (suspended_) { + suspended_ = false; + NotifyResume(); + } + break; + case SUSPEND_EVENT: + if (!suspended_) { + suspended_ = true; + NotifySuspend(); + } + break; + } +} + +void SystemMonitor::AddObserver(PowerObserver* obs) { + observer_list_->AddObserver(obs); +} + +void SystemMonitor::RemoveObserver(PowerObserver* obs) { + observer_list_->RemoveObserver(obs); +} + +void SystemMonitor::NotifyPowerStateChange() { + LOG(INFO) << "PowerStateChange: " + << (BatteryPower() ? "On" : "Off") << " battery"; + observer_list_->Notify(&PowerObserver::OnPowerStateChange, this); +} + +void SystemMonitor::NotifySuspend() { + LOG(INFO) << "Power Suspending"; + observer_list_->Notify(&PowerObserver::OnSuspend, this); +} + +void SystemMonitor::NotifyResume() { + LOG(INFO) << "Power Resuming"; + observer_list_->Notify(&PowerObserver::OnResume, this); +} + +void SystemMonitor::Start() { +#if defined(ENABLE_BATTERY_MONITORING) + DCHECK(MessageLoop::current()); // Can't call start too early. + SystemMonitor* monitor = Get(); + monitor->delayed_battery_check_.Start( + TimeDelta::FromMilliseconds(kDelayedBatteryCheckMs), monitor, + &SystemMonitor::BatteryCheck); +#endif // defined(ENABLE_BATTERY_MONITORING) +} + +void SystemMonitor::BatteryCheck() { + ProcessPowerMessage(SystemMonitor::POWER_STATE_EVENT); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/system_monitor.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/system_monitor.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/system_monitor.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/system_monitor.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,129 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_SYSTEM_MONITOR_H_ +#define BASE_SYSTEM_MONITOR_H_ + +#include "base/observer_list_threadsafe.h" +#include "base/singleton.h" + +// Windows HiRes timers drain the battery faster so we need to know the battery +// status. This isn't true for other platforms. +#if defined(OS_WIN) +#define ENABLE_BATTERY_MONITORING 1 +#else +#undef ENABLE_BATTERY_MONITORING +#endif // !OS_WIN + +namespace base { + +// Class for monitoring various system-related subsystems +// such as power management, network status, etc. +// TODO(mbelshe): Add support beyond just power management. +class SystemMonitor { + public: + // Access to the Singleton + static SystemMonitor* Get() { + // Uses the LeakySingletonTrait because cleanup is optional. + return + Singleton >::get(); + } + + // Start the System Monitor within a process. This method + // is provided so that the battery check can be deferred. + // The MessageLoop must be started before calling this + // method. + // This is a no-op on platforms for which ENABLE_BATTERY_MONITORING is + // disabled. + static void Start(); + + // + // Power-related APIs + // + + // Is the computer currently on battery power. + // Can be called on any thread. + bool BatteryPower() { + // Using a lock here is not necessary for just a bool. + return battery_in_use_; + } + + // Normalized list of power events. + enum PowerEvent { + POWER_STATE_EVENT, // The Power status of the system has changed. + SUSPEND_EVENT, // The system is being suspended. + RESUME_EVENT // The system is being resumed. + }; + + // Callbacks will be called on the thread which creates the SystemMonitor. + // During the callback, Add/RemoveObserver will block until the callbacks + // are finished. Observers should implement quick callback functions; if + // lengthy operations are needed, the observer should take care to invoke + // the operation on an appropriate thread. + class PowerObserver { + public: + // Notification of a change in power status of the computer, such + // as from switching between battery and A/C power. + virtual void OnPowerStateChange(SystemMonitor*) = 0; + + // Notification that the system is suspending. + virtual void OnSuspend(SystemMonitor*) = 0; + + // Notification that the system is resuming. + virtual void OnResume(SystemMonitor*) = 0; + }; + + // Add a new observer. + // Can be called from any thread. + // Must not be called from within a notification callback. + void AddObserver(PowerObserver* obs); + + // Remove an existing observer. + // Can be called from any thread. + // Must not be called from within a notification callback. + void RemoveObserver(PowerObserver* obs); + +#if defined(OS_WIN) + // Windows-specific handling of a WM_POWERBROADCAST message. + // Embedders of this API should hook their top-level window + // message loop and forward WM_POWERBROADCAST through this call. + void ProcessWmPowerBroadcastMessage(int event_id); +#endif + + // Cross-platform handling of a power event. + void ProcessPowerMessage(PowerEvent event_id); + + // Constructor. + // Don't use this; access SystemMonitor via the Singleton. + SystemMonitor(); + + private: + // Platform-specific method to check whether the system is currently + // running on battery power. Returns true if running on batteries, + // false otherwise. + bool IsBatteryPower(); + + // Checks the battery status and notifies observers if the battery + // status has changed. + void BatteryCheck(); + + // Functions to trigger notifications. + void NotifyPowerStateChange(); + void NotifySuspend(); + void NotifyResume(); + + scoped_refptr > observer_list_; + bool battery_in_use_; + bool suspended_; + +#if defined(ENABLE_BATTERY_MONITORING) + base::OneShotTimer delayed_battery_check_; +#endif + + DISALLOW_COPY_AND_ASSIGN(SystemMonitor); +}; + +} + +#endif // BASE_SYSTEM_MONITOR_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/system_monitor_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/system_monitor_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/system_monitor_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/system_monitor_posix.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,14 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/system_monitor.h" + +namespace base { + +bool SystemMonitor::IsBatteryPower() { + NOTIMPLEMENTED(); + return false; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/system_monitor_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/system_monitor_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/system_monitor_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/system_monitor_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,78 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/system_monitor.h" +#include "testing/gtest/include/gtest/gtest.h" + +class PowerTest : public base::SystemMonitor::PowerObserver { + public: + PowerTest() + : battery_(false), + power_state_changes_(0), + suspends_(0), + resumes_(0) {}; + + // PowerObserver callbacks. + void OnPowerStateChange(base::SystemMonitor*) { power_state_changes_++; }; + void OnSuspend(base::SystemMonitor*) { suspends_++; }; + void OnResume(base::SystemMonitor*) { resumes_++; }; + + // Test status counts. + bool battery() { return battery_; } + int power_state_changes() { return power_state_changes_; } + int suspends() { return suspends_; } + int resumes() { return resumes_; } + + private: + bool battery_; // Do we currently think we're on battery power. + int power_state_changes_; // Count of OnPowerStateChange notifications. + int suspends_; // Count of OnSuspend notifications. + int resumes_; // Count of OnResume notifications. +}; + +TEST(SystemMonitor, PowerNotifications) { + const int kObservers = 5; + + // Initialize a message loop for this to run on. + MessageLoop loop; + // Initialize time() since it registers as a SystemMonitor observer. + base::Time now = base::Time::Now(); + + base::SystemMonitor* monitor = base::SystemMonitor::Get(); + PowerTest test[kObservers]; + for (int index = 0; index < kObservers; ++index) + monitor->AddObserver(&test[index]); + + // Send a bunch of power changes. Since the battery power hasn't + // actually changed, we shouldn't get notifications. + for (int index = 0; index < 5; index++) { + monitor->ProcessPowerMessage(base::SystemMonitor::POWER_STATE_EVENT); + EXPECT_EQ(test[0].power_state_changes(), 0); + } + + // Sending resume when not suspended should have no effect. + monitor->ProcessPowerMessage(base::SystemMonitor::RESUME_EVENT); + loop.RunAllPending(); + EXPECT_EQ(test[0].resumes(), 0); + + // Pretend we suspended. + monitor->ProcessPowerMessage(base::SystemMonitor::SUSPEND_EVENT); + loop.RunAllPending(); + EXPECT_EQ(test[0].suspends(), 1); + + // Send a second suspend notification. This should be suppressed. + monitor->ProcessPowerMessage(base::SystemMonitor::SUSPEND_EVENT); + loop.RunAllPending(); + EXPECT_EQ(test[0].suspends(), 1); + + // Pretend we were awakened. + monitor->ProcessPowerMessage(base::SystemMonitor::RESUME_EVENT); + loop.RunAllPending(); + EXPECT_EQ(test[0].resumes(), 1); + + // Send a duplicate resume notification. This should be suppressed. + monitor->ProcessPowerMessage(base::SystemMonitor::RESUME_EVENT); + loop.RunAllPending(); + EXPECT_EQ(test[0].resumes(), 1); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/system_monitor_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/system_monitor_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/system_monitor_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/system_monitor_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,49 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/system_monitor.h" + +namespace base { + +void SystemMonitor::ProcessWmPowerBroadcastMessage(int event_id) { + PowerEvent power_event; + switch (event_id) { + case PBT_APMPOWERSTATUSCHANGE: // The power status changed. + power_event = POWER_STATE_EVENT; + break; + case PBT_APMRESUMEAUTOMATIC: // Non-user initiated resume from suspend. + case PBT_APMRESUMESUSPEND: // User initiated resume from suspend. + power_event = RESUME_EVENT; + break; + case PBT_APMSUSPEND: // System has been suspended. + power_event = SUSPEND_EVENT; + break; + default: + return; + + // Other Power Events: + // PBT_APMBATTERYLOW - removed in Vista. + // PBT_APMOEMEVENT - removed in Vista. + // PBT_APMQUERYSUSPEND - removed in Vista. + // PBT_APMQUERYSUSPENDFAILED - removed in Vista. + // PBT_APMRESUMECRITICAL - removed in Vista. + // PBT_POWERSETTINGCHANGE - user changed the power settings. + } + ProcessPowerMessage(power_event); +} + +// Function to query the system to see if it is currently running on +// battery power. Returns true if running on battery. +bool SystemMonitor::IsBatteryPower() { + SYSTEM_POWER_STATUS status; + if (!GetSystemPowerStatus(&status)) { + LOG(ERROR) << "GetSystemPowerStatus failed: " << GetLastError(); + return false; + } + return (status.ACLineStatus == 0); +} + + + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/task.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/task.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/task.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/task.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,669 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_TASK_H_ +#define BASE_TASK_H_ + +#include "base/non_thread_safe.h" +#include "base/revocable_store.h" +#include "base/tracked.h" +#include "base/tuple.h" + +// Task ------------------------------------------------------------------------ +// +// A task is a generic runnable thingy, usually used for running code on a +// different thread or for scheduling future tasks off of the message loop. + +class Task : public tracked_objects::Tracked { + public: + Task() {} + virtual ~Task() {} + + // Tasks are automatically deleted after Run is called. + virtual void Run() = 0; +}; + +class CancelableTask : public Task { + public: + // Not all tasks support cancellation. + virtual void Cancel() = 0; +}; + +// Scoped Factories ------------------------------------------------------------ +// +// These scoped factory objects can be used by non-refcounted objects to safely +// place tasks in a message loop. Each factory guarantees that the tasks it +// produces will not run after the factory is destroyed. Commonly, factories +// are declared as class members, so the class' tasks will automatically cancel +// when the class instance is destroyed. +// +// Exampe Usage: +// +// class MyClass { +// private: +// // This factory will be used to schedule invocations of SomeMethod. +// ScopedRunnableMethodFactory some_method_factory_; +// +// public: +// // It is safe to suppress warning 4355 here. +// MyClass() : some_method_factory_(this) { } +// +// void SomeMethod() { +// // If this function might be called directly, you might want to revoke +// // any outstanding runnable methods scheduled to call it. If it's not +// // referenced other than by the factory, this is unnecessary. +// some_method_factory_.RevokeAll(); +// ... +// } +// +// void ScheduleSomeMethod() { +// // If you'd like to only only have one pending task at a time, test for +// // |empty| before manufacturing another task. +// if (!some_method_factory_.empty()) +// return; +// +// // The factories are not thread safe, so always invoke on +// // |MessageLoop::current()|. +// MessageLoop::current()->PostDelayedTask(FROM_HERE, +// some_method_factory_.NewRunnableMethod(&MyClass::SomeMethod), +// kSomeMethodDelayMS); +// } +// }; + +// A ScopedTaskFactory produces tasks of type |TaskType| and prevents them from +// running after it is destroyed. +template +class ScopedTaskFactory : public RevocableStore { + public: + ScopedTaskFactory() { } + + // Create a new task. + inline TaskType* NewTask() { + return new TaskWrapper(this); + } + + class TaskWrapper : public TaskType, public NonThreadSafe { + public: + explicit TaskWrapper(RevocableStore* store) : revocable_(store) { } + + virtual void Run() { + if (!revocable_.revoked()) + TaskType::Run(); + } + + private: + Revocable revocable_; + + DISALLOW_EVIL_CONSTRUCTORS(TaskWrapper); + }; + + private: + DISALLOW_EVIL_CONSTRUCTORS(ScopedTaskFactory); +}; + +// A ScopedRunnableMethodFactory creates runnable methods for a specified +// object. This is particularly useful for generating callbacks for +// non-reference counted objects when the factory is a member of the object. +template +class ScopedRunnableMethodFactory : public RevocableStore { + public: + explicit ScopedRunnableMethodFactory(T* object) : object_(object) { } + + template + inline Task* NewRunnableMethod(Method method) { + typedef typename ScopedTaskFactory >::TaskWrapper TaskWrapper; + + TaskWrapper* task = new TaskWrapper(this); + task->Init(object_, method, MakeTuple()); + return task; + } + + template + inline Task* NewRunnableMethod(Method method, const A& a) { + typedef typename ScopedTaskFactory > >::TaskWrapper TaskWrapper; + + TaskWrapper* task = new TaskWrapper(this); + task->Init(object_, method, MakeTuple(a)); + return task; + } + + template + inline Task* NewRunnableMethod(Method method, const A& a, const B& b) { + typedef typename ScopedTaskFactory > >::TaskWrapper TaskWrapper; + + TaskWrapper* task = new TaskWrapper(this); + task->Init(object_, method, MakeTuple(a, b)); + return task; + } + + template + inline Task* NewRunnableMethod(Method method, + const A& a, + const B& b, + const C& c) { + typedef typename ScopedTaskFactory > >::TaskWrapper TaskWrapper; + + TaskWrapper* task = new TaskWrapper(this); + task->Init(object_, method, MakeTuple(a, b, c)); + return task; + } + + template + inline Task* NewRunnableMethod(Method method, + const A& a, + const B& b, + const C& c, + const D& d) { + typedef typename ScopedTaskFactory > >::TaskWrapper TaskWrapper; + + TaskWrapper* task = new TaskWrapper(this); + task->Init(object_, method, MakeTuple(a, b, c, d)); + return task; + } + + template + inline Task* NewRunnableMethod(Method method, + const A& a, + const B& b, + const C& c, + const D& d, + const E& e) { + typedef typename ScopedTaskFactory > >::TaskWrapper TaskWrapper; + + TaskWrapper* task = new TaskWrapper(this); + task->Init(object_, method, MakeTuple(a, b, c, d, e)); + return task; + } + + protected: + template + class RunnableMethod : public Task { + public: + RunnableMethod() { } + + void Init(T* obj, Method meth, const Params& params) { + obj_ = obj; + meth_ = meth; + params_ = params; + } + + virtual void Run() { DispatchToMethod(obj_, meth_, params_); } + + private: + T* obj_; + Method meth_; + Params params_; + + DISALLOW_EVIL_CONSTRUCTORS(RunnableMethod); + }; + + private: + T* object_; + + DISALLOW_EVIL_CONSTRUCTORS(ScopedRunnableMethodFactory); +}; + +// General task implementations ------------------------------------------------ + +// Task to delete an object +template +class DeleteTask : public CancelableTask { + public: + explicit DeleteTask(T* obj) : obj_(obj) { + } + virtual void Run() { + delete obj_; + } + virtual void Cancel() { + obj_ = NULL; + } + private: + T* obj_; +}; + +// Task to Release() an object +template +class ReleaseTask : public CancelableTask { + public: + explicit ReleaseTask(T* obj) : obj_(obj) { + } + virtual void Run() { + if (obj_) + obj_->Release(); + } + virtual void Cancel() { + obj_ = NULL; + } + private: + T* obj_; +}; + +// RunnableMethodTraits -------------------------------------------------------- +// +// This traits-class is used by RunnableMethod to manage the lifetime of the +// callee object. By default, it is assumed that the callee supports AddRef +// and Release methods. A particular class can specialize this template to +// define other lifetime management. For example, if the callee is known to +// live longer than the RunnableMethod object, then a RunnableMethodTraits +// struct could be defined with empty RetainCallee and ReleaseCallee methods. + +template +struct RunnableMethodTraits { + static void RetainCallee(T* obj) { + obj->AddRef(); + } + static void ReleaseCallee(T* obj) { + obj->Release(); + } +}; + +// RunnableMethod and RunnableFunction ----------------------------------------- +// +// Runnable methods are a type of task that call a function on an object when +// they are run. We implement both an object and a set of NewRunnableMethod and +// NewRunnableFunction functions for convenience. These functions are +// overloaded and will infer the template types, simplifying calling code. +// +// The template definitions all use the following names: +// T - the class type of the object you're supplying +// this is not needed for the Static version of the call +// Method/Function - the signature of a pointer to the method or function you +// want to call +// Param - the parameter(s) to the method, possibly packed as a Tuple +// A - the first parameter (if any) to the method +// B - the second parameter (if any) to the mathod +// +// Put these all together and you get an object that can call a method whose +// signature is: +// R T::MyFunction([A[, B]]) +// +// Usage: +// PostTask(FROM_HERE, NewRunnableMethod(object, &Object::method[, a[, b]]) +// PostTask(FROM_HERE, NewRunnableFunction(&function[, a[, b]]) + +// RunnableMethod and NewRunnableMethod implementation ------------------------- + +template +class RunnableMethod : public CancelableTask, + public RunnableMethodTraits { + public: + RunnableMethod(T* obj, Method meth, const Params& params) + : obj_(obj), meth_(meth), params_(params) { + RetainCallee(obj_); + } + ~RunnableMethod() { + ReleaseCallee(); + } + + virtual void Run() { + if (obj_) + DispatchToMethod(obj_, meth_, params_); + } + + virtual void Cancel() { + ReleaseCallee(); + } + + private: + void ReleaseCallee() { + if (obj_) { + RunnableMethodTraits::ReleaseCallee(obj_); + obj_ = NULL; + } + } + + T* obj_; + Method meth_; + Params params_; +}; + +template +inline CancelableTask* NewRunnableMethod(T* object, Method method) { + return new RunnableMethod(object, method, MakeTuple()); +} + +template +inline CancelableTask* NewRunnableMethod(T* object, Method method, const A& a) { + return new RunnableMethod >(object, + method, + MakeTuple(a)); +} + +template +inline CancelableTask* NewRunnableMethod(T* object, Method method, +const A& a, const B& b) { + return new RunnableMethod >(object, method, + MakeTuple(a, b)); +} + +template +inline CancelableTask* NewRunnableMethod(T* object, Method method, + const A& a, const B& b, const C& c) { + return new RunnableMethod >(object, method, + MakeTuple(a, b, c)); +} + +template +inline CancelableTask* NewRunnableMethod(T* object, Method method, + const A& a, const B& b, + const C& c, const D& d) { + return new RunnableMethod >(object, method, + MakeTuple(a, b, + c, d)); +} + +template +inline CancelableTask* NewRunnableMethod(T* object, Method method, + const A& a, const B& b, + const C& c, const D& d, const E& e) { + return new RunnableMethod >(object, + method, + MakeTuple(a, b, c, d, e)); +} + +template +inline CancelableTask* NewRunnableMethod(T* object, Method method, + const A& a, const B& b, + const C& c, const D& d, const E& e, + const F& f) { + return new RunnableMethod >(object, + method, + MakeTuple(a, b, c, d, e, + f)); +} + +template +inline CancelableTask* NewRunnableMethod(T* object, Method method, + const A& a, const B& b, + const C& c, const D& d, const E& e, + const F& f, const G& g) { + return new RunnableMethod >(object, + method, + MakeTuple(a, b, c, d, + e, f, g)); +} + +// RunnableFunction and NewRunnableFunction implementation --------------------- + +template +class RunnableFunction : public CancelableTask { + public: + RunnableFunction(Function function, const Params& params) + : function_(function), params_(params) { + } + + ~RunnableFunction() { + } + + virtual void Run() { + if (function_) + DispatchToFunction(function_, params_); + } + + virtual void Cancel() { + } + + private: + Function function_; + Params params_; +}; + +template +inline CancelableTask* NewRunnableFunction(Function function) { + return new RunnableFunction(function, MakeTuple()); +} + +template +inline CancelableTask* NewRunnableFunction(Function function, const A& a) { + return new RunnableFunction >(function, MakeTuple(a)); +} + +template +inline CancelableTask* NewRunnableFunction(Function function, + const A& a, const B& b) { + return new RunnableFunction >(function, + MakeTuple(a, b)); +} + +template +inline CancelableTask* NewRunnableFunction(Function function, + const A& a, const B& b, + const C& c) { + return new RunnableFunction >(function, + MakeTuple(a, b, c)); +} + +template +inline CancelableTask* NewRunnableFunction(Function function, + const A& a, const B& b, + const C& c, const D& d) { + return new RunnableFunction >(function, + MakeTuple(a, b, + c, d)); +} + +template +inline CancelableTask* NewRunnableFunction(Function function, + const A& a, const B& b, + const C& c, const D& d, + const E& e) { + return new RunnableFunction >(function, + MakeTuple(a, b, + c, d, + e)); +} + +// Callback -------------------------------------------------------------------- +// +// A Callback is like a Task but with unbound parameters. It is basically an +// object-oriented function pointer. +// +// Callbacks are designed to work with Tuples. A set of helper functions and +// classes is provided to hide the Tuple details from the consumer. Client +// code will generally work with the CallbackRunner base class, which merely +// provides a Run method and is returned by the New* functions. This allows +// users to not care which type of class implements the callback, only that it +// has a certain number and type of arguments. +// +// The implementation of this is done by CallbackImpl, which inherits +// CallbackStorage to store the data. This allows the storage of the data +// (requiring the class type T) to be hidden from users, who will want to call +// this regardless of the implementor's type T. +// +// Note that callbacks currently have no facility for cancelling or abandoning +// them. We currently handle this at a higher level for cases where this is +// necessary. The pointer in a callback must remain valid until the callback +// is made. +// +// Like Task, the callback executor is responsible for deleting the callback +// pointer once the callback has executed. +// +// Example client usage: +// void Object::DoStuff(int, string); +// Callback2::Type* callback = +// NewCallback(obj, &Object::DoStuff); +// callback->Run(5, string("hello")); +// delete callback; +// or, equivalently, using tuples directly: +// CallbackRunner >* callback = +// NewCallback(obj, &Object::DoStuff); +// callback->RunWithParams(MakeTuple(5, string("hello"))); + +// Base for all Callbacks that handles storage of the pointers. +template +class CallbackStorage { + public: + CallbackStorage(T* obj, Method meth) : obj_(obj), meth_(meth) { + } + + protected: + T* obj_; + Method meth_; +}; + +// Interface that is exposed to the consumer, that does the actual calling +// of the method. +template +class CallbackRunner { + public: + typedef Params TupleType; + + virtual ~CallbackRunner() {} + virtual void RunWithParams(const Params& params) = 0; + + // Convenience functions so callers don't have to deal with Tuples. + inline void Run() { + RunWithParams(Tuple0()); + } + + template + inline void Run(const Arg1& a) { + RunWithParams(Params(a)); + } + + template + inline void Run(const Arg1& a, const Arg2& b) { + RunWithParams(Params(a, b)); + } + + template + inline void Run(const Arg1& a, const Arg2& b, const Arg3& c) { + RunWithParams(Params(a, b, c)); + } + + template + inline void Run(const Arg1& a, const Arg2& b, const Arg3& c, const Arg4& d) { + RunWithParams(Params(a, b, c, d)); + } + + template + inline void Run(const Arg1& a, const Arg2& b, const Arg3& c, + const Arg4& d, const Arg5& e) { + RunWithParams(Params(a, b, c, d, e)); + } +}; + +template +class CallbackImpl : public CallbackStorage, + public CallbackRunner { + public: + CallbackImpl(T* obj, Method meth) : CallbackStorage(obj, meth) { + } + virtual void RunWithParams(const Params& params) { + // use "this->" to force C++ to look inside our templatized base class; see + // Effective C++, 3rd Ed, item 43, p210 for details. + DispatchToMethod(this->obj_, this->meth_, params); + } +}; + +// 0-arg implementation +struct Callback0 { + typedef CallbackRunner Type; +}; + +template +typename Callback0::Type* NewCallback(T* object, void (T::*method)()) { + return new CallbackImpl(object, method); +} + +// 1-arg implementation +template +struct Callback1 { + typedef CallbackRunner > Type; +}; + +template +typename Callback1::Type* NewCallback(T* object, + void (T::*method)(Arg1)) { + return new CallbackImpl >(object, method); +} + +// 2-arg implementation +template +struct Callback2 { + typedef CallbackRunner > Type; +}; + +template +typename Callback2::Type* NewCallback( + T* object, + void (T::*method)(Arg1, Arg2)) { + return new CallbackImpl >(object, method); +} + +// 3-arg implementation +template +struct Callback3 { + typedef CallbackRunner > Type; +}; + +template +typename Callback3::Type* NewCallback( + T* object, + void (T::*method)(Arg1, Arg2, Arg3)) { + return new CallbackImpl >(object, method); +} + +// 4-arg implementation +template +struct Callback4 { + typedef CallbackRunner > Type; +}; + +template +typename Callback4::Type* NewCallback( + T* object, + void (T::*method)(Arg1, Arg2, Arg3, Arg4)) { + return new CallbackImpl >(object, method); +} + +// 5-arg implementation +template +struct Callback5 { + typedef CallbackRunner > Type; +}; + +template +typename Callback5::Type* NewCallback( + T* object, + void (T::*method)(Arg1, Arg2, Arg3, Arg4, Arg5)) { + return new CallbackImpl >(object, method); +} + +// An UnboundMethod is a wrapper for a method where the actual object is +// provided at Run dispatch time. +template +class UnboundMethod { + public: + UnboundMethod(Method m, Params p) : m_(m), p_(p) {} + void Run(T* obj) const { + DispatchToMethod(obj, m_, p_); + } + private: + Method m_; + Params p_; +}; + +#endif // BASE_TASK_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/test_file_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/test_file_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/test_file_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/test_file_util.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,31 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_TEST_FILE_UTIL_H_ +#define BASE_TEST_FILE_UTIL_H_ + +// File utility functions used only by tests. + +#include + +class FilePath; + +namespace file_util { + +// Clear a specific file from the system cache. After this call, trying +// to access this file will result in a cold load from the hard drive. +bool EvictFileFromSystemCache(const FilePath& file); + +// Like CopyFileNoCache but recursively copies all files and subdirectories +// in the given input directory to the output directory. Any files in the +// destination that already exist will be overwritten. +// +// Returns true on success. False means there was some error copying, so the +// state of the destination is unknown. +bool CopyRecursiveDirNoCache(const std::wstring& source_dir, + const std::wstring& dest_dir); + +} // namespace file_util + +#endif // BASE_TEST_FILE_UTIL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/test_file_util_linux.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/test_file_util_linux.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/test_file_util_linux.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/test_file_util_linux.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,27 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/test_file_util.h" + +#include +#include +#include + +#include "base/file_path.h" + +namespace file_util { + +bool EvictFileFromSystemCache(const FilePath& file) { + int fd = open(file.value().c_str(), O_RDONLY); + if (fd < 0) + return false; + if (fdatasync(fd) != 0) + return false; + if (posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED) != 0) + return false; + close(fd); + return true; +} + +} // namespace file_util diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/test_file_util_mac.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/test_file_util_mac.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/test_file_util_mac.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/test_file_util_mac.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,36 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/test_file_util.h" + +#include +#include +#include "base/logging.h" +#include "base/file_util.h" + +namespace file_util { + +bool EvictFileFromSystemCache(const FilePath& file) { + // There aren't any really direct ways to purge a file from the UBC. From + // talking with Amit Singh, the safest is to mmap the file with MAP_FILE (the + // default) + MAP_SHARED, then do an msync to invalidate the memory. The next + // open should then have to load the file from disk. + + file_util::MemoryMappedFile mapped_file; + if (!mapped_file.Initialize(file)) { + DLOG(WARNING) << "failed to memory map " << file.value(); + return false; + } + + if (msync(const_cast(mapped_file.data()), mapped_file.length(), + MS_INVALIDATE) != 0) { + DLOG(WARNING) << "failed to invalidate memory map of " << file.value() + << ", errno: " << errno; + return false; + } + + return true; +} + +} // namespace file_util diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/test_file_util_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/test_file_util_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/test_file_util_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/test_file_util_posix.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,119 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/test_file_util.h" + +#include +#include +#include +#include + +#include + +#include "base/logging.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/string_util.h" + +namespace file_util { + +bool CopyRecursiveDirNoCache(const std::wstring& source_dir, + const std::wstring& dest_dir) { + const FilePath from_path(FilePath::FromWStringHack(source_dir)); + const FilePath to_path(FilePath::FromWStringHack(dest_dir)); + + char top_dir[PATH_MAX]; + if (base::strlcpy(top_dir, from_path.value().c_str(), + arraysize(top_dir)) >= arraysize(top_dir)) { + return false; + } + + char* dir_list[] = { top_dir, NULL }; + FTS* fts = fts_open(dir_list, FTS_PHYSICAL | FTS_NOSTAT, NULL); + if (!fts) { + LOG(ERROR) << "fts_open failed: " << strerror(errno); + return false; + } + + int error = 0; + FTSENT* ent; + while (!error && (ent = fts_read(fts)) != NULL) { + // ent->fts_path is the source path, including from_path, so paste + // the suffix after from_path onto to_path to create the target_path. + std::string suffix(&ent->fts_path[from_path.value().size()]); + // Strip the leading '/' (if any). + if (!suffix.empty()) { + DCHECK_EQ('/', suffix[0]); + suffix.erase(0, 1); + } + const FilePath target_path = to_path.Append(suffix); + switch (ent->fts_info) { + case FTS_D: // Preorder directory. + // Try creating the target dir, continuing on it if it exists already. + // Rely on the user's umask to produce correct permissions. + if (mkdir(target_path.value().c_str(), 0777) != 0) { + if (errno != EEXIST) + error = errno; + } + break; + case FTS_F: // Regular file. + case FTS_NSOK: // File, no stat info requested. + { + errno = 0; + FilePath source_path(ent->fts_path); + if (CopyFile(source_path, target_path)) { + bool success = EvictFileFromSystemCache( + target_path.Append(source_path.BaseName())); + DCHECK(success); + } else { + error = errno ? errno : EINVAL; + } + } + break; + case FTS_DP: // Postorder directory. + case FTS_DOT: // "." or ".." + // Skip it. + continue; + case FTS_DC: // Directory causing a cycle. + // Skip this branch. + if (fts_set(fts, ent, FTS_SKIP) != 0) + error = errno; + break; + case FTS_DNR: // Directory cannot be read. + case FTS_ERR: // Error. + case FTS_NS: // Stat failed. + // Abort with the error. + error = ent->fts_errno; + break; + case FTS_SL: // Symlink. + case FTS_SLNONE: // Symlink with broken target. + LOG(WARNING) << "skipping symbolic link: " << ent->fts_path; + continue; + case FTS_DEFAULT: // Some other sort of file. + LOG(WARNING) << "skipping file of unknown type: " << ent->fts_path; + continue; + default: + NOTREACHED(); + continue; // Hope for the best! + } + } + // fts_read may have returned NULL and set errno to indicate an error. + if (!error && errno != 0) + error = errno; + + if (!fts_close(fts)) { + // If we already have an error, let's use that error instead of the error + // fts_close set. + if (!error) + error = errno; + } + + if (error) { + LOG(ERROR) << strerror(error); + return false; + } + return true; +} + +} // namespace file_util diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/test_file_util_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/test_file_util_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/test_file_util_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/test_file_util_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,160 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/test_file_util.h" + +#include + +#include + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/scoped_handle.h" + +namespace file_util { + +// We could use GetSystemInfo to get the page size, but this serves +// our purpose fine since 4K is the page size on x86 as well as x64. +static const ptrdiff_t kPageSize = 4096; + +bool EvictFileFromSystemCache(const FilePath& file) { + // Request exclusive access to the file and overwrite it with no buffering. + ScopedHandle file_handle( + CreateFile(file.value().c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, NULL)); + if (!file_handle) + return false; + + // Get some attributes to restore later. + BY_HANDLE_FILE_INFORMATION bhi = {0}; + CHECK(::GetFileInformationByHandle(file_handle, &bhi)); + + // Execute in chunks. It could be optimized. We want to do few of these since + // these operations will be slow without the cache. + + // Non-buffered reads and writes need to be sector aligned and since sector + // sizes typically range from 512-4096 bytes, we just use the page size. + // The buffer size is twice the size of a page (minus one) since we need to + // get an aligned pointer into the buffer that we can use. + char buffer[2 * kPageSize - 1]; + // Get an aligned pointer into buffer. + char* read_write = reinterpret_cast( + reinterpret_cast(buffer + kPageSize - 1) & ~(kPageSize - 1)); + DCHECK((reinterpret_cast(read_write) % kPageSize) == 0); + + // If the file size isn't a multiple of kPageSize, we'll need special + // processing. + bool file_is_page_aligned = true; + int total_bytes = 0; + DWORD bytes_read, bytes_written; + for (;;) { + bytes_read = 0; + ReadFile(file_handle, read_write, kPageSize, &bytes_read, NULL); + if (bytes_read == 0) + break; + + if (bytes_read < kPageSize) { + // Zero out the remaining part of the buffer. + // WriteFile will fail if we provide a buffer size that isn't a + // sector multiple, so we'll have to write the entire buffer with + // padded zeros and then use SetEndOfFile to truncate the file. + ZeroMemory(read_write + bytes_read, kPageSize - bytes_read); + file_is_page_aligned = false; + } + + // Move back to the position we just read from. + // Note that SetFilePointer will also fail if total_bytes isn't sector + // aligned, but that shouldn't happen here. + DCHECK((total_bytes % kPageSize) == 0); + SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN); + if (!WriteFile(file_handle, read_write, kPageSize, &bytes_written, NULL) || + bytes_written != kPageSize) { + DCHECK(false); + return false; + } + + total_bytes += bytes_read; + + // If this is false, then we just processed the last portion of the file. + if (!file_is_page_aligned) + break; + } + + if (!file_is_page_aligned) { + // The size of the file isn't a multiple of the page size, so we'll have + // to open the file again, this time without the FILE_FLAG_NO_BUFFERING + // flag and use SetEndOfFile to mark EOF. + file_handle.Set(NULL); + file_handle.Set(CreateFile(file.value().c_str(), GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, 0, NULL)); + CHECK(SetFilePointer(file_handle, total_bytes, NULL, FILE_BEGIN) != + INVALID_SET_FILE_POINTER); + CHECK(::SetEndOfFile(file_handle)); + } + + // Restore the file attributes. + CHECK(::SetFileTime(file_handle, &bhi.ftCreationTime, &bhi.ftLastAccessTime, + &bhi.ftLastWriteTime)); + + return true; +} + +// Like CopyFileNoCache but recursively copies all files and subdirectories +// in the given input directory to the output directory. +bool CopyRecursiveDirNoCache(const std::wstring& source_dir, + const std::wstring& dest_dir) { + // Try to create the directory if it doesn't already exist. + if (!CreateDirectory(dest_dir)) { + if (GetLastError() != ERROR_ALREADY_EXISTS) + return false; + } + + std::vector files_copied; + + std::wstring src(source_dir); + file_util::AppendToPath(&src, L"*"); + + WIN32_FIND_DATA fd; + HANDLE fh = FindFirstFile(src.c_str(), &fd); + if (fh == INVALID_HANDLE_VALUE) + return false; + + do { + std::wstring cur_file(fd.cFileName); + if (cur_file == L"." || cur_file == L"..") + continue; // Skip these special entries. + + std::wstring cur_source_path(source_dir); + file_util::AppendToPath(&cur_source_path, cur_file); + + std::wstring cur_dest_path(dest_dir); + file_util::AppendToPath(&cur_dest_path, cur_file); + + if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + // Recursively copy a subdirectory. We stripped "." and ".." already. + if (!CopyRecursiveDirNoCache(cur_source_path, cur_dest_path)) { + FindClose(fh); + return false; + } + } else { + // Copy the file. + if (!::CopyFile(cur_source_path.c_str(), cur_dest_path.c_str(), false)) { + FindClose(fh); + return false; + } + + // We don't check for errors from this function, often, we are copying + // files that are in the repository, and they will have read-only set. + // This will prevent us from evicting from the cache, but these don't + // matter anyway. + EvictFileFromSystemCache(FilePath::FromWStringHack(cur_dest_path)); + } + } while (FindNextFile(fh, &fd)); + + FindClose(fh); + return true; +} + +} // namespace file_util diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/test_suite.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/test_suite.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/test_suite.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/test_suite.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,134 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_TEST_SUITE_H_ +#define BASE_TEST_SUITE_H_ + +// Defines a basic test suite framework for running gtest based tests. You can +// instantiate this class in your main function and call its Run method to run +// any gtest based tests that are linked into your executable. + +#include "base/at_exit.h" +#include "base/base_paths.h" +#include "base/command_line.h" +#include "base/debug_on_start.h" +#include "base/file_path.h" +#include "base/icu_util.h" +#include "base/logging.h" +#include "base/multiprocess_test.h" +#include "base/scoped_nsautorelease_pool.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/multiprocess_func_list.h" + +#if defined(OS_WIN) +#include +#elif defined(OS_LINUX) +#include +#endif + +class TestSuite { + public: + TestSuite(int argc, char** argv) { + base::EnableTerminationOnHeapCorruption(); + CommandLine::Init(argc, argv); + testing::InitGoogleTest(&argc, argv); +#if defined(OS_LINUX) + gtk_init_check(&argc, &argv); +#endif + // Don't add additional code to this constructor. Instead add it to + // Initialize(). See bug 6436. + } + + virtual ~TestSuite() { + CommandLine::Terminate(); + } + + // Don't add additional code to this method. Instead add it to + // Initialize(). See bug 6436. + int Run() { + base::ScopedNSAutoreleasePool scoped_pool; + + Initialize(); + std::wstring client_func = + CommandLine::ForCurrentProcess()->GetSwitchValue(kRunClientProcess); + // Check to see if we are being run as a client process. + if (!client_func.empty()) { + // Convert our function name to a usable string for GetProcAddress. + std::string func_name(client_func.begin(), client_func.end()); + + return multi_process_function_list::InvokeChildProcessTest(func_name); + } + int result = RUN_ALL_TESTS(); + + // This MUST happen before Shutdown() since Shutdown() tears down + // objects (such as NotificationService::current()) that Cocoa + // objects use to remove themselves as observers. + scoped_pool.Recycle(); + + Shutdown(); + + return result; + } + + protected: + // All fatal log messages (e.g. DCHECK failures) imply unit test failures. + static void UnitTestAssertHandler(const std::string& str) { + FAIL() << str; + } + +#if defined(OS_WIN) + // Disable crash dialogs so that it doesn't gum up the buildbot + virtual void SuppressErrorDialogs() { + UINT new_flags = SEM_FAILCRITICALERRORS | + SEM_NOGPFAULTERRORBOX | + SEM_NOOPENFILEERRORBOX; + + // Preserve existing error mode, as discussed at + // http://blogs.msdn.com/oldnewthing/archive/2004/07/27/198410.aspx + UINT existing_flags = SetErrorMode(new_flags); + SetErrorMode(existing_flags | new_flags); + } +#endif + + // Override these for custom initialization and shutdown handling. Use these + // instead of putting complex code in your constructor/destructor. + + virtual void Initialize() { + // Initialize logging. + FilePath exe; + PathService::Get(base::FILE_EXE, &exe); + FilePath log_filename = exe.ReplaceExtension(FILE_PATH_LITERAL("log")); + logging::InitLogging(log_filename.value().c_str(), + logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG, + logging::LOCK_LOG_FILE, + logging::DELETE_OLD_LOG_FILE); + // We want process and thread IDs because we may have multiple processes. + // Note: temporarily enabled timestamps in an effort to catch bug 6361. + logging::SetLogItems(true, true, true, true); + +#if defined(OS_WIN) + // In some cases, we do not want to see standard error dialogs. + if (!IsDebuggerPresent() && + !CommandLine::ForCurrentProcess()->HasSwitch(L"show-error-dialogs")) { + SuppressErrorDialogs(); +#if !defined(PURIFY) + // When the code in this file moved around, bug 6436 resurfaced. + // As a hack workaround, just #ifdef out this code for Purify builds. + logging::SetLogAssertHandler(UnitTestAssertHandler); +#endif + } +#endif + + icu_util::Initialize(); + } + + virtual void Shutdown() { + } + + // Make sure that we setup an AtExitManager so Singleton objects will be + // destroyed. + base::AtExitManager at_exit_manager_; +}; + +#endif // BASE_TEST_SUITE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/dmg_fp.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/dmg_fp.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/dmg_fp.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/dmg_fp.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,30 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef THIRD_PARTY_DMG_FP_H_ +#define THIRD_PARTY_DMG_FP_H_ + +namespace dmg_fp { + +// Return a nearest machine number to the input decimal +// string (or set errno to ERANGE). With IEEE arithmetic, ties are +// broken by the IEEE round-even rule. Otherwise ties are broken by +// biased rounding (add half and chop). +double strtod(const char* s00, char** se); + +// Convert double to ASCII string. For meaning of parameters +// see dtoa.cc file. +char* dtoa(double d, int mode, int ndigits, + int* decpt, int* sign, char** rve); + +// Must be used to free values returned by dtoa. +void freedtoa(char* s); + +// Store the closest decimal approximation to x in b (null terminated). +// Returns a pointer to b. It is sufficient for |b| to be 32 characters. +char* g_fmt(char* b, double x); + +} // namespace dmg_fp + +#endif // THIRD_PARTY_DMG_FP_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/dtoa.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/dtoa.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/dtoa.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/dtoa.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,4205 @@ +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991, 2000, 2001 by Lucent Technologies. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +/* Please send bug reports to David M. Gay (dmg at acm dot org, + * with " at " changed at "@" and " dot " changed to "."). */ + +/* On a machine with IEEE extended-precision registers, it is + * necessary to specify double-precision (53-bit) rounding precision + * before invoking strtod or dtoa. If the machine uses (the equivalent + * of) Intel 80x87 arithmetic, the call + * _control87(PC_53, MCW_PC); + * does this with many compilers. Whether this or another call is + * appropriate depends on the compiler; for this to work, it may be + * necessary to #include "float.h" or another system-dependent header + * file. + */ + +/* strtod for IEEE-, VAX-, and IBM-arithmetic machines. + * + * This strtod returns a nearest machine number to the input decimal + * string (or sets errno to ERANGE). With IEEE arithmetic, ties are + * broken by the IEEE round-even rule. Otherwise ties are broken by + * biased rounding (add half and chop). + * + * Inspired loosely by William D. Clinger's paper "How to Read Floating + * Point Numbers Accurately" [Proc. ACM SIGPLAN '90, pp. 92-101]. + * + * Modifications: + * + * 1. We only require IEEE, IBM, or VAX double-precision + * arithmetic (not IEEE double-extended). + * 2. We get by with floating-point arithmetic in a case that + * Clinger missed -- when we're computing d * 10^n + * for a small integer d and the integer n is not too + * much larger than 22 (the maximum integer k for which + * we can represent 10^k exactly), we may be able to + * compute (d*10^k) * 10^(e-k) with just one roundoff. + * 3. Rather than a bit-at-a-time adjustment of the binary + * result in the hard case, we use floating-point + * arithmetic to determine the adjustment to within + * one bit; only in really hard cases do we need to + * compute a second residual. + * 4. Because of 3., we don't need a large table of powers of 10 + * for ten-to-e (just some small tables, e.g. of 10^k + * for 0 <= k <= 22). + */ + +/* + * #define IEEE_8087 for IEEE-arithmetic machines where the least + * significant byte has the lowest address. + * #define IEEE_MC68k for IEEE-arithmetic machines where the most + * significant byte has the lowest address. + * #define Long int on machines with 32-bit ints and 64-bit longs. + * #define IBM for IBM mainframe-style floating-point arithmetic. + * #define VAX for VAX-style floating-point arithmetic (D_floating). + * #define No_leftright to omit left-right logic in fast floating-point + * computation of dtoa. + * #define Honor_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and strtod and dtoa should round accordingly. Unless Trust_FLT_ROUNDS + * is also #defined, fegetround() will be queried for the rounding mode. + * Note that both FLT_ROUNDS and fegetround() are specified by the C99 + * standard (and are specified to be consistent, with fesetround() + * affecting the value of FLT_ROUNDS), but that some (Linux) systems + * do not work correctly in this regard, so using fegetround() is more + * portable than using FLT_FOUNDS directly. + * #define Check_FLT_ROUNDS if FLT_ROUNDS can assume the values 2 or 3 + * and Honor_FLT_ROUNDS is not #defined. + * #define RND_PRODQUOT to use rnd_prod and rnd_quot (assembly routines + * that use extended-precision instructions to compute rounded + * products and quotients) with IBM. + * #define ROUND_BIASED for IEEE-format with biased rounding. + * #define Inaccurate_Divide for IEEE-format with correctly rounded + * products but inaccurate quotients, e.g., for Intel i860. + * #define NO_LONG_LONG on machines that do not have a "long long" + * integer type (of >= 64 bits). On such machines, you can + * #define Just_16 to store 16 bits per 32-bit Long when doing + * high-precision integer arithmetic. Whether this speeds things + * up or slows things down depends on the machine and the number + * being converted. If long long is available and the name is + * something other than "long long", #define Llong to be the name, + * and if "unsigned Llong" does not work as an unsigned version of + * Llong, #define #ULLong to be the corresponding unsigned type. + * #define KR_headers for old-style C function headers. + * #define Bad_float_h if your system lacks a float.h or if it does not + * define some or all of DBL_DIG, DBL_MAX_10_EXP, DBL_MAX_EXP, + * FLT_RADIX, FLT_ROUNDS, and DBL_MAX. + * #define MALLOC your_malloc, where your_malloc(n) acts like malloc(n) + * if memory is available and otherwise does something you deem + * appropriate. If MALLOC is undefined, malloc will be invoked + * directly -- and assumed always to succeed. Similarly, if you + * want something other than the system's free() to be called to + * recycle memory acquired from MALLOC, #define FREE to be the + * name of the alternate routine. (FREE or free is only called in + * pathological cases, e.g., in a dtoa call after a dtoa return in + * mode 3 with thousands of digits requested.) + * #define Omit_Private_Memory to omit logic (added Jan. 1998) for making + * memory allocations from a private pool of memory when possible. + * When used, the private pool is PRIVATE_MEM bytes long: 2304 bytes, + * unless #defined to be a different length. This default length + * suffices to get rid of MALLOC calls except for unusual cases, + * such as decimal-to-binary conversion of a very long string of + * digits. The longest string dtoa can return is about 751 bytes + * long. For conversions by strtod of strings of 800 digits and + * all dtoa conversions in single-threaded executions with 8-byte + * pointers, PRIVATE_MEM >= 7400 appears to suffice; with 4-byte + * pointers, PRIVATE_MEM >= 7112 appears adequate. + * #define NO_INFNAN_CHECK if you do not wish to have INFNAN_CHECK + * #defined automatically on IEEE systems. On such systems, + * when INFNAN_CHECK is #defined, strtod checks + * for Infinity and NaN (case insensitively). On some systems + * (e.g., some HP systems), it may be necessary to #define NAN_WORD0 + * appropriately -- to the most significant word of a quiet NaN. + * (On HP Series 700/800 machines, -DNAN_WORD0=0x7ff40000 works.) + * When INFNAN_CHECK is #defined and No_Hex_NaN is not #defined, + * strtod also accepts (case insensitively) strings of the form + * NaN(x), where x is a string of hexadecimal digits and spaces; + * if there is only one string of hexadecimal digits, it is taken + * for the 52 fraction bits of the resulting NaN; if there are two + * or more strings of hex digits, the first is for the high 20 bits, + * the second and subsequent for the low 32 bits, with intervening + * white space ignored; but if this results in none of the 52 + * fraction bits being on (an IEEE Infinity symbol), then NAN_WORD0 + * and NAN_WORD1 are used instead. + * #define MULTIPLE_THREADS if the system offers preemptively scheduled + * multiple threads. In this case, you must provide (or suitably + * #define) two locks, acquired by ACQUIRE_DTOA_LOCK(n) and freed + * by FREE_DTOA_LOCK(n) for n = 0 or 1. (The second lock, accessed + * in pow5mult, ensures lazy evaluation of only one copy of high + * powers of 5; omitting this lock would introduce a small + * probability of wasting memory, but would otherwise be harmless.) + * You must also invoke freedtoa(s) to free the value s returned by + * dtoa. You may do so whether or not MULTIPLE_THREADS is #defined. + * #define NO_IEEE_Scale to disable new (Feb. 1997) logic in strtod that + * avoids underflows on inputs whose result does not underflow. + * If you #define NO_IEEE_Scale on a machine that uses IEEE-format + * floating-point numbers and flushes underflows to zero rather + * than implementing gradual underflow, then you must also #define + * Sudden_Underflow. + * #define USE_LOCALE to use the current locale's decimal_point value. + * #define SET_INEXACT if IEEE arithmetic is being used and extra + * computation should be done to set the inexact flag when the + * result is inexact and avoid setting inexact when the result + * is exact. In this case, dtoa.c must be compiled in + * an environment, perhaps provided by #include "dtoa.c" in a + * suitable wrapper, that defines two functions, + * int get_inexact(void); + * void clear_inexact(void); + * such that get_inexact() returns a nonzero value if the + * inexact bit is already set, and clear_inexact() sets the + * inexact bit to 0. When SET_INEXACT is #defined, strtod + * also does extra computations to set the underflow and overflow + * flags when appropriate (i.e., when the result is tiny and + * inexact or when it is a numeric value rounded to +-infinity). + * #define NO_ERRNO if strtod should not assign errno = ERANGE when + * the result overflows to +-Infinity or underflows to 0. + * #define NO_HEX_FP to omit recognition of hexadecimal floating-point + * values by strtod. + * #define NO_STRTOD_BIGCOMP (on IEEE-arithmetic systems only for now) + * to disable logic for "fast" testing of very long input strings + * to strtod. This testing proceeds by initially truncating the + * input string, then if necessary comparing the whole string with + * a decimal expansion to decide close cases. This logic is only + * used for input more than STRTOD_DIGLIM digits long (default 40). + */ + +#define IEEE_8087 +#define NO_HEX_FP + +#ifndef Long +#define Long long +#endif +#ifndef ULong +typedef unsigned Long ULong; +#endif + +#ifdef DEBUG +#include "stdio.h" +#define Bug(x) {fprintf(stderr, "%s\n", x); exit(1);} +#endif + +#include "stdlib.h" +#include "string.h" + +#ifdef USE_LOCALE +#include "locale.h" +#endif + +#ifdef Honor_FLT_ROUNDS +#ifndef Trust_FLT_ROUNDS +#include +#endif +#endif + +#ifdef MALLOC +#ifdef KR_headers +extern char *MALLOC(); +#else +extern void *MALLOC(size_t); +#endif +#else +#define MALLOC malloc +#endif + +#ifndef Omit_Private_Memory +#ifndef PRIVATE_MEM +#define PRIVATE_MEM 2304 +#endif +#define PRIVATE_mem ((PRIVATE_MEM+sizeof(double)-1)/sizeof(double)) +static double private_mem[PRIVATE_mem], *pmem_next = private_mem; +#endif + +#undef IEEE_Arith +#undef Avoid_Underflow +#ifdef IEEE_MC68k +#define IEEE_Arith +#endif +#ifdef IEEE_8087 +#define IEEE_Arith +#endif + +#ifdef IEEE_Arith +#ifndef NO_INFNAN_CHECK +#undef INFNAN_CHECK +#define INFNAN_CHECK +#endif +#else +#undef INFNAN_CHECK +#define NO_STRTOD_BIGCOMP +#endif + +#include "errno.h" + +#ifdef Bad_float_h + +#ifdef IEEE_Arith +#define DBL_DIG 15 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define FLT_RADIX 2 +#endif /*IEEE_Arith*/ + +#ifdef IBM +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 75 +#define DBL_MAX_EXP 63 +#define FLT_RADIX 16 +#define DBL_MAX 7.2370055773322621e+75 +#endif + +#ifdef VAX +#define DBL_DIG 16 +#define DBL_MAX_10_EXP 38 +#define DBL_MAX_EXP 127 +#define FLT_RADIX 2 +#define DBL_MAX 1.7014118346046923e+38 +#endif + +#ifndef LONG_MAX +#define LONG_MAX 2147483647 +#endif + +#else /* ifndef Bad_float_h */ +#include "float.h" +#endif /* Bad_float_h */ + +#ifndef __MATH_H__ +#include "math.h" +#endif + +namespace dmg_fp { + +#ifndef CONST +#ifdef KR_headers +#define CONST /* blank */ +#else +#define CONST const +#endif +#endif + +#if defined(IEEE_8087) + defined(IEEE_MC68k) + defined(VAX) + defined(IBM) != 1 +Exactly one of IEEE_8087, IEEE_MC68k, VAX, or IBM should be defined. +#endif + +typedef union { double d; ULong L[2]; } U; + +#ifdef IEEE_8087 +#define word0(x) (x)->L[1] +#define word1(x) (x)->L[0] +#else +#define word0(x) (x)->L[0] +#define word1(x) (x)->L[1] +#endif +#define dval(x) (x)->d + +#ifndef STRTOD_DIGLIM +#define STRTOD_DIGLIM 40 +#endif + +#ifdef DIGLIM_DEBUG +extern int strtod_diglim; +#else +#define strtod_diglim STRTOD_DIGLIM +#endif + +/* The following definition of Storeinc is appropriate for MIPS processors. + * An alternative that might be better on some machines is + * #define Storeinc(a,b,c) (*a++ = b << 16 | c & 0xffff) + */ +#if defined(IEEE_8087) + defined(VAX) +#define Storeinc(a,b,c) (((unsigned short *)a)[1] = (unsigned short)b, \ +((unsigned short *)a)[0] = (unsigned short)c, a++) +#else +#define Storeinc(a,b,c) (((unsigned short *)a)[0] = (unsigned short)b, \ +((unsigned short *)a)[1] = (unsigned short)c, a++) +#endif + +/* #define P DBL_MANT_DIG */ +/* Ten_pmax = floor(P*log(2)/log(5)) */ +/* Bletch = (highest power of 2 < DBL_MAX_10_EXP) / 16 */ +/* Quick_max = floor((P-1)*log(FLT_RADIX)/log(10) - 1) */ +/* Int_max = floor(P*log(FLT_RADIX)/log(10) - 1) */ + +#ifdef IEEE_Arith +#define Exp_shift 20 +#define Exp_shift1 20 +#define Exp_msk1 0x100000 +#define Exp_msk11 0x100000 +#define Exp_mask 0x7ff00000 +#define P 53 +#define Nbits 53 +#define Bias 1023 +#define Emax 1023 +#define Emin (-1022) +#define Exp_1 0x3ff00000 +#define Exp_11 0x3ff00000 +#define Ebits 11 +#define Frac_mask 0xfffff +#define Frac_mask1 0xfffff +#define Ten_pmax 22 +#define Bletch 0x10 +#define Bndry_mask 0xfffff +#define Bndry_mask1 0xfffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 1 +#define Tiny0 0 +#define Tiny1 1 +#define Quick_max 14 +#define Int_max 14 +#ifndef NO_IEEE_Scale +#define Avoid_Underflow +#ifdef Flush_Denorm /* debugging option */ +#undef Sudden_Underflow +#endif +#endif + +#ifndef Flt_Rounds +#ifdef FLT_ROUNDS +#define Flt_Rounds FLT_ROUNDS +#else +#define Flt_Rounds 1 +#endif +#endif /*Flt_Rounds*/ + +#ifdef Honor_FLT_ROUNDS +#undef Check_FLT_ROUNDS +#define Check_FLT_ROUNDS +#else +#define Rounding Flt_Rounds +#endif + +#else /* ifndef IEEE_Arith */ +#undef Check_FLT_ROUNDS +#undef Honor_FLT_ROUNDS +#undef SET_INEXACT +#undef Sudden_Underflow +#define Sudden_Underflow +#ifdef IBM +#undef Flt_Rounds +#define Flt_Rounds 0 +#define Exp_shift 24 +#define Exp_shift1 24 +#define Exp_msk1 0x1000000 +#define Exp_msk11 0x1000000 +#define Exp_mask 0x7f000000 +#define P 14 +#define Nbits 56 +#define Bias 65 +#define Emax 248 +#define Emin (-260) +#define Exp_1 0x41000000 +#define Exp_11 0x41000000 +#define Ebits 8 /* exponent has 7 bits, but 8 is the right value in b2d */ +#define Frac_mask 0xffffff +#define Frac_mask1 0xffffff +#define Bletch 4 +#define Ten_pmax 22 +#define Bndry_mask 0xefffff +#define Bndry_mask1 0xffffff +#define LSB 1 +#define Sign_bit 0x80000000 +#define Log2P 4 +#define Tiny0 0x100000 +#define Tiny1 0 +#define Quick_max 14 +#define Int_max 15 +#else /* VAX */ +#undef Flt_Rounds +#define Flt_Rounds 1 +#define Exp_shift 23 +#define Exp_shift1 7 +#define Exp_msk1 0x80 +#define Exp_msk11 0x800000 +#define Exp_mask 0x7f80 +#define P 56 +#define Nbits 56 +#define Bias 129 +#define Emax 126 +#define Emin (-129) +#define Exp_1 0x40800000 +#define Exp_11 0x4080 +#define Ebits 8 +#define Frac_mask 0x7fffff +#define Frac_mask1 0xffff007f +#define Ten_pmax 24 +#define Bletch 2 +#define Bndry_mask 0xffff007f +#define Bndry_mask1 0xffff007f +#define LSB 0x10000 +#define Sign_bit 0x8000 +#define Log2P 1 +#define Tiny0 0x80 +#define Tiny1 0 +#define Quick_max 15 +#define Int_max 15 +#endif /* IBM, VAX */ +#endif /* IEEE_Arith */ + +#ifndef IEEE_Arith +#define ROUND_BIASED +#endif + +#ifdef RND_PRODQUOT +#define rounded_product(a,b) a = rnd_prod(a, b) +#define rounded_quotient(a,b) a = rnd_quot(a, b) +#ifdef KR_headers +extern double rnd_prod(), rnd_quot(); +#else +extern double rnd_prod(double, double), rnd_quot(double, double); +#endif +#else +#define rounded_product(a,b) a *= b +#define rounded_quotient(a,b) a /= b +#endif + +#define Big0 (Frac_mask1 | Exp_msk1*(DBL_MAX_EXP+Bias-1)) +#define Big1 0xffffffff + +#ifndef Pack_32 +#define Pack_32 +#endif + +typedef struct BCinfo BCinfo; + struct +BCinfo { int dp0, dp1, dplen, dsign, e0, inexact, nd, nd0, rounding, scale, uflchk; }; + +#ifdef KR_headers +#define FFFFFFFF ((((unsigned long)0xffff)<<16)|(unsigned long)0xffff) +#else +#define FFFFFFFF 0xffffffffUL +#endif + +#ifdef NO_LONG_LONG +#undef ULLong +#ifdef Just_16 +#undef Pack_32 +/* When Pack_32 is not defined, we store 16 bits per 32-bit Long. + * This makes some inner loops simpler and sometimes saves work + * during multiplications, but it often seems to make things slightly + * slower. Hence the default is now to store 32 bits per Long. + */ +#endif +#else /* long long available */ +#ifndef Llong +#define Llong long long +#endif +#ifndef ULLong +#define ULLong unsigned Llong +#endif +#endif /* NO_LONG_LONG */ + +#ifndef MULTIPLE_THREADS +#define ACQUIRE_DTOA_LOCK(n) /*nothing*/ +#define FREE_DTOA_LOCK(n) /*nothing*/ +#endif + +#define Kmax 7 + +double strtod(const char *s00, char **se); +char *dtoa(double d, int mode, int ndigits, + int *decpt, int *sign, char **rve); + + struct +Bigint { + struct Bigint *next; + int k, maxwds, sign, wds; + ULong x[1]; + }; + + typedef struct Bigint Bigint; + + static Bigint *freelist[Kmax+1]; + + static Bigint * +Balloc +#ifdef KR_headers + (k) int k; +#else + (int k) +#endif +{ + int x; + Bigint *rv; +#ifndef Omit_Private_Memory + unsigned int len; +#endif + + ACQUIRE_DTOA_LOCK(0); + /* The k > Kmax case does not need ACQUIRE_DTOA_LOCK(0), */ + /* but this case seems very unlikely. */ + if (k <= Kmax && (rv = freelist[k])) + freelist[k] = rv->next; + else { + x = 1 << k; +#ifdef Omit_Private_Memory + rv = (Bigint *)MALLOC(sizeof(Bigint) + (x-1)*sizeof(ULong)); +#else + len = (sizeof(Bigint) + (x-1)*sizeof(ULong) + sizeof(double) - 1) + /sizeof(double); + if (k <= Kmax && pmem_next - private_mem + len <= PRIVATE_mem) { + rv = (Bigint*)pmem_next; + pmem_next += len; + } + else + rv = (Bigint*)MALLOC(len*sizeof(double)); +#endif + rv->k = k; + rv->maxwds = x; + } + FREE_DTOA_LOCK(0); + rv->sign = rv->wds = 0; + return rv; + } + + static void +Bfree +#ifdef KR_headers + (v) Bigint *v; +#else + (Bigint *v) +#endif +{ + if (v) { + if (v->k > Kmax) +#ifdef FREE + FREE((void*)v); +#else + free((void*)v); +#endif + else { + ACQUIRE_DTOA_LOCK(0); + v->next = freelist[v->k]; + freelist[v->k] = v; + FREE_DTOA_LOCK(0); + } + } + } + +#define Bcopy(x,y) memcpy((char *)&x->sign, (char *)&y->sign, \ +y->wds*sizeof(Long) + 2*sizeof(int)) + + static Bigint * +multadd +#ifdef KR_headers + (b, m, a) Bigint *b; int m, a; +#else + (Bigint *b, int m, int a) /* multiply by m and add a */ +#endif +{ + int i, wds; +#ifdef ULLong + ULong *x; + ULLong carry, y; +#else + ULong carry, *x, y; +#ifdef Pack_32 + ULong xi, z; +#endif +#endif + Bigint *b1; + + wds = b->wds; + x = b->x; + i = 0; + carry = a; + do { +#ifdef ULLong + y = *x * (ULLong)m + carry; + carry = y >> 32; + *x++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + xi = *x; + y = (xi & 0xffff) * m + carry; + z = (xi >> 16) * m + (y >> 16); + carry = z >> 16; + *x++ = (z << 16) + (y & 0xffff); +#else + y = *x * m + carry; + carry = y >> 16; + *x++ = y & 0xffff; +#endif +#endif + } + while(++i < wds); + if (carry) { + if (wds >= b->maxwds) { + b1 = Balloc(b->k+1); + Bcopy(b1, b); + Bfree(b); + b = b1; + } + b->x[wds++] = carry; + b->wds = wds; + } + return b; + } + + static Bigint * +s2b +#ifdef KR_headers + (s, nd0, nd, y9, dplen) CONST char *s; int nd0, nd, dplen; ULong y9; +#else + (CONST char *s, int nd0, int nd, ULong y9, int dplen) +#endif +{ + Bigint *b; + int i, k; + Long x, y; + + x = (nd + 8) / 9; + for(k = 0, y = 1; x > y; y <<= 1, k++) ; +#ifdef Pack_32 + b = Balloc(k); + b->x[0] = y9; + b->wds = 1; +#else + b = Balloc(k+1); + b->x[0] = y9 & 0xffff; + b->wds = (b->x[1] = y9 >> 16) ? 2 : 1; +#endif + + i = 9; + if (9 < nd0) { + s += 9; + do b = multadd(b, 10, *s++ - '0'); + while(++i < nd0); + s += dplen; + } + else + s += dplen + 9; + for(; i < nd; i++) + b = multadd(b, 10, *s++ - '0'); + return b; + } + + static int +hi0bits +#ifdef KR_headers + (x) ULong x; +#else + (ULong x) +#endif +{ + int k = 0; + + if (!(x & 0xffff0000)) { + k = 16; + x <<= 16; + } + if (!(x & 0xff000000)) { + k += 8; + x <<= 8; + } + if (!(x & 0xf0000000)) { + k += 4; + x <<= 4; + } + if (!(x & 0xc0000000)) { + k += 2; + x <<= 2; + } + if (!(x & 0x80000000)) { + k++; + if (!(x & 0x40000000)) + return 32; + } + return k; + } + + static int +lo0bits +#ifdef KR_headers + (y) ULong *y; +#else + (ULong *y) +#endif +{ + int k; + ULong x = *y; + + if (x & 7) { + if (x & 1) + return 0; + if (x & 2) { + *y = x >> 1; + return 1; + } + *y = x >> 2; + return 2; + } + k = 0; + if (!(x & 0xffff)) { + k = 16; + x >>= 16; + } + if (!(x & 0xff)) { + k += 8; + x >>= 8; + } + if (!(x & 0xf)) { + k += 4; + x >>= 4; + } + if (!(x & 0x3)) { + k += 2; + x >>= 2; + } + if (!(x & 1)) { + k++; + x >>= 1; + if (!x) + return 32; + } + *y = x; + return k; + } + + static Bigint * +i2b +#ifdef KR_headers + (i) int i; +#else + (int i) +#endif +{ + Bigint *b; + + b = Balloc(1); + b->x[0] = i; + b->wds = 1; + return b; + } + + static Bigint * +mult +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int k, wa, wb, wc; + ULong *x, *xa, *xae, *xb, *xbe, *xc, *xc0; + ULong y; +#ifdef ULLong + ULLong carry, z; +#else + ULong carry, z; +#ifdef Pack_32 + ULong z2; +#endif +#endif + + if (a->wds < b->wds) { + c = a; + a = b; + b = c; + } + k = a->k; + wa = a->wds; + wb = b->wds; + wc = wa + wb; + if (wc > a->maxwds) + k++; + c = Balloc(k); + for(x = c->x, xa = x + wc; x < xa; x++) + *x = 0; + xa = a->x; + xae = xa + wa; + xb = b->x; + xbe = xb + wb; + xc0 = c->x; +#ifdef ULLong + for(; xb < xbe; xc0++) { + if ((y = *xb++)) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * (ULLong)y + *xc + carry; + carry = z >> 32; + *xc++ = z & FFFFFFFF; + } + while(x < xae); + *xc = carry; + } + } +#else +#ifdef Pack_32 + for(; xb < xbe; xb++, xc0++) { + if (y = *xb & 0xffff) { + x = xa; + xc = xc0; + carry = 0; + do { + z = (*x & 0xffff) * y + (*xc & 0xffff) + carry; + carry = z >> 16; + z2 = (*x++ >> 16) * y + (*xc >> 16) + carry; + carry = z2 >> 16; + Storeinc(xc, z2, z); + } + while(x < xae); + *xc = carry; + } + if (y = *xb >> 16) { + x = xa; + xc = xc0; + carry = 0; + z2 = *xc; + do { + z = (*x & 0xffff) * y + (*xc >> 16) + carry; + carry = z >> 16; + Storeinc(xc, z, z2); + z2 = (*x++ >> 16) * y + (*xc & 0xffff) + carry; + carry = z2 >> 16; + } + while(x < xae); + *xc = z2; + } + } +#else + for(; xb < xbe; xc0++) { + if (y = *xb++) { + x = xa; + xc = xc0; + carry = 0; + do { + z = *x++ * y + *xc + carry; + carry = z >> 16; + *xc++ = z & 0xffff; + } + while(x < xae); + *xc = carry; + } + } +#endif +#endif + for(xc0 = c->x, xc = xc0 + wc; wc > 0 && !*--xc; --wc) ; + c->wds = wc; + return c; + } + + static Bigint *p5s; + + static Bigint * +pow5mult +#ifdef KR_headers + (b, k) Bigint *b; int k; +#else + (Bigint *b, int k) +#endif +{ + Bigint *b1, *p5, *p51; + int i; + static int p05[3] = { 5, 25, 125 }; + + if ((i = k & 3)) + b = multadd(b, p05[i-1], 0); + + if (!(k >>= 2)) + return b; + if (!(p5 = p5s)) { + /* first time */ +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p5 = p5s)) { + p5 = p5s = i2b(625); + p5->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p5 = p5s = i2b(625); + p5->next = 0; +#endif + } + for(;;) { + if (k & 1) { + b1 = mult(b, p5); + Bfree(b); + b = b1; + } + if (!(k >>= 1)) + break; + if (!(p51 = p5->next)) { +#ifdef MULTIPLE_THREADS + ACQUIRE_DTOA_LOCK(1); + if (!(p51 = p5->next)) { + p51 = p5->next = mult(p5,p5); + p51->next = 0; + } + FREE_DTOA_LOCK(1); +#else + p51 = p5->next = mult(p5,p5); + p51->next = 0; +#endif + } + p5 = p51; + } + return b; + } + + static Bigint * +lshift +#ifdef KR_headers + (b, k) Bigint *b; int k; +#else + (Bigint *b, int k) +#endif +{ + int i, k1, n, n1; + Bigint *b1; + ULong *x, *x1, *xe, z; + +#ifdef Pack_32 + n = k >> 5; +#else + n = k >> 4; +#endif + k1 = b->k; + n1 = n + b->wds + 1; + for(i = b->maxwds; n1 > i; i <<= 1) + k1++; + b1 = Balloc(k1); + x1 = b1->x; + for(i = 0; i < n; i++) + *x1++ = 0; + x = b->x; + xe = x + b->wds; +#ifdef Pack_32 + if (k &= 0x1f) { + k1 = 32 - k; + z = 0; + do { + *x1++ = *x << k | z; + z = *x++ >> k1; + } + while(x < xe); + if ((*x1 = z)) + ++n1; + } +#else + if (k &= 0xf) { + k1 = 16 - k; + z = 0; + do { + *x1++ = *x << k & 0xffff | z; + z = *x++ >> k1; + } + while(x < xe); + if (*x1 = z) + ++n1; + } +#endif + else do + *x1++ = *x++; + while(x < xe); + b1->wds = n1 - 1; + Bfree(b); + return b1; + } + + static int +cmp +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + ULong *xa, *xa0, *xb, *xb0; + int i, j; + + i = a->wds; + j = b->wds; +#ifdef DEBUG + if (i > 1 && !a->x[i-1]) + Bug("cmp called with a->x[a->wds-1] == 0"); + if (j > 1 && !b->x[j-1]) + Bug("cmp called with b->x[b->wds-1] == 0"); +#endif + if (i -= j) + return i; + xa0 = a->x; + xa = xa0 + j; + xb0 = b->x; + xb = xb0 + j; + for(;;) { + if (*--xa != *--xb) + return *xa < *xb ? -1 : 1; + if (xa <= xa0) + break; + } + return 0; + } + + static Bigint * +diff +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + Bigint *c; + int i, wa, wb; + ULong *xa, *xae, *xb, *xbe, *xc; +#ifdef ULLong + ULLong borrow, y; +#else + ULong borrow, y; +#ifdef Pack_32 + ULong z; +#endif +#endif + + i = cmp(a,b); + if (!i) { + c = Balloc(0); + c->wds = 1; + c->x[0] = 0; + return c; + } + if (i < 0) { + c = a; + a = b; + b = c; + i = 1; + } + else + i = 0; + c = Balloc(a->k); + c->sign = i; + wa = a->wds; + xa = a->x; + xae = xa + wa; + wb = b->wds; + xb = b->x; + xbe = xb + wb; + xc = c->x; + borrow = 0; +#ifdef ULLong + do { + y = (ULLong)*xa++ - *xb++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = y & FFFFFFFF; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = y >> 32 & (ULong)1; + *xc++ = y & FFFFFFFF; + } +#else +#ifdef Pack_32 + do { + y = (*xa & 0xffff) - (*xb & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - (*xb++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } + while(xb < xbe); + while(xa < xae) { + y = (*xa & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*xa++ >> 16) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(xc, z, y); + } +#else + do { + y = *xa++ - *xb++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } + while(xb < xbe); + while(xa < xae) { + y = *xa++ - borrow; + borrow = (y & 0x10000) >> 16; + *xc++ = y & 0xffff; + } +#endif +#endif + while(!*--xc) + wa--; + c->wds = wa; + return c; + } + + static double +ulp +#ifdef KR_headers + (x) U *x; +#else + (U *x) +#endif +{ + Long L; + U u; + + L = (word0(x) & Exp_mask) - (P-1)*Exp_msk1; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + if (L > 0) { +#endif +#endif +#ifdef IBM + L |= Exp_msk1 >> 4; +#endif + word0(&u) = L; + word1(&u) = 0; +#ifndef Avoid_Underflow +#ifndef Sudden_Underflow + } + else { + L = -L >> Exp_shift; + if (L < Exp_shift) { + word0(&u) = 0x80000 >> L; + word1(&u) = 0; + } + else { + word0(&u) = 0; + L -= Exp_shift; + word1(&u) = L >= 31 ? 1 : 1 << 31 - L; + } + } +#endif +#endif + return dval(&u); + } + + static double +b2d +#ifdef KR_headers + (a, e) Bigint *a; int *e; +#else + (Bigint *a, int *e) +#endif +{ + ULong *xa, *xa0, w, y, z; + int k; + U d; +#ifdef VAX + ULong d0, d1; +#else +#define d0 word0(&d) +#define d1 word1(&d) +#endif + + xa0 = a->x; + xa = xa0 + a->wds; + y = *--xa; +#ifdef DEBUG + if (!y) Bug("zero y in b2d"); +#endif + k = hi0bits(y); + *e = 32 - k; +#ifdef Pack_32 + if (k < Ebits) { + d0 = Exp_1 | y >> (Ebits - k); + w = xa > xa0 ? *--xa : 0; + d1 = y << ((32-Ebits) + k) | w >> (Ebits - k); + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + if (k -= Ebits) { + d0 = Exp_1 | y << k | z >> (32 - k); + y = xa > xa0 ? *--xa : 0; + d1 = z << k | y >> (32 - k); + } + else { + d0 = Exp_1 | y; + d1 = z; + } +#else + if (k < Ebits + 16) { + z = xa > xa0 ? *--xa : 0; + d0 = Exp_1 | y << k - Ebits | z >> Ebits + 16 - k; + w = xa > xa0 ? *--xa : 0; + y = xa > xa0 ? *--xa : 0; + d1 = z << k + 16 - Ebits | w << k - Ebits | y >> 16 + Ebits - k; + goto ret_d; + } + z = xa > xa0 ? *--xa : 0; + w = xa > xa0 ? *--xa : 0; + k -= Ebits + 16; + d0 = Exp_1 | y << k + 16 | z << k | w >> 16 - k; + y = xa > xa0 ? *--xa : 0; + d1 = w << k + 16 | y << k; +#endif + ret_d: +#ifdef VAX + word0(&d) = d0 >> 16 | d0 << 16; + word1(&d) = d1 >> 16 | d1 << 16; +#else +#undef d0 +#undef d1 +#endif + return dval(&d); + } + + static Bigint * +d2b +#ifdef KR_headers + (d, e, bits) U *d; int *e, *bits; +#else + (U *d, int *e, int *bits) +#endif +{ + Bigint *b; + int de, k; + ULong *x, y, z; +#ifndef Sudden_Underflow + int i; +#endif +#ifdef VAX + ULong d0, d1; + d0 = word0(d) >> 16 | word0(d) << 16; + d1 = word1(d) >> 16 | word1(d) << 16; +#else +#define d0 word0(d) +#define d1 word1(d) +#endif + +#ifdef Pack_32 + b = Balloc(1); +#else + b = Balloc(2); +#endif + x = b->x; + + z = d0 & Frac_mask; + d0 &= 0x7fffffff; /* clear sign bit, which we ignore */ +#ifdef Sudden_Underflow + de = (int)(d0 >> Exp_shift); +#ifndef IBM + z |= Exp_msk11; +#endif +#else + if ((de = (int)(d0 >> Exp_shift))) + z |= Exp_msk1; +#endif +#ifdef Pack_32 + if ((y = d1)) { + if ((k = lo0bits(&y))) { + x[0] = y | z << (32 - k); + z >>= k; + } + else + x[0] = y; +#ifndef Sudden_Underflow + i = +#endif + b->wds = (x[1] = z) ? 2 : 1; + } + else { + k = lo0bits(&z); + x[0] = z; +#ifndef Sudden_Underflow + i = +#endif + b->wds = 1; + k += 32; + } +#else + if (y = d1) { + if (k = lo0bits(&y)) + if (k >= 16) { + x[0] = y | z << 32 - k & 0xffff; + x[1] = z >> k - 16 & 0xffff; + x[2] = z >> k; + i = 2; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16 | z << 16 - k & 0xffff; + x[2] = z >> k & 0xffff; + x[3] = z >> k+16; + i = 3; + } + else { + x[0] = y & 0xffff; + x[1] = y >> 16; + x[2] = z & 0xffff; + x[3] = z >> 16; + i = 3; + } + } + else { +#ifdef DEBUG + if (!z) + Bug("Zero passed to d2b"); +#endif + k = lo0bits(&z); + if (k >= 16) { + x[0] = z; + i = 0; + } + else { + x[0] = z & 0xffff; + x[1] = z >> 16; + i = 1; + } + k += 32; + } + while(!x[i]) + --i; + b->wds = i + 1; +#endif +#ifndef Sudden_Underflow + if (de) { +#endif +#ifdef IBM + *e = (de - Bias - (P-1) << 2) + k; + *bits = 4*P + 8 - k - hi0bits(word0(d) & Frac_mask); +#else + *e = de - Bias - (P-1) + k; + *bits = P - k; +#endif +#ifndef Sudden_Underflow + } + else { + *e = de - Bias - (P-1) + 1 + k; +#ifdef Pack_32 + *bits = 32*i - hi0bits(x[i-1]); +#else + *bits = (i+2)*16 - hi0bits(x[i]); +#endif + } +#endif + return b; + } +#undef d0 +#undef d1 + + static double +ratio +#ifdef KR_headers + (a, b) Bigint *a, *b; +#else + (Bigint *a, Bigint *b) +#endif +{ + U da, db; + int k, ka, kb; + + dval(&da) = b2d(a, &ka); + dval(&db) = b2d(b, &kb); +#ifdef Pack_32 + k = ka - kb + 32*(a->wds - b->wds); +#else + k = ka - kb + 16*(a->wds - b->wds); +#endif +#ifdef IBM + if (k > 0) { + word0(&da) += (k >> 2)*Exp_msk1; + if (k &= 3) + dval(&da) *= 1 << k; + } + else { + k = -k; + word0(&db) += (k >> 2)*Exp_msk1; + if (k &= 3) + dval(&db) *= 1 << k; + } +#else + if (k > 0) + word0(&da) += k*Exp_msk1; + else { + k = -k; + word0(&db) += k*Exp_msk1; + } +#endif + return dval(&da) / dval(&db); + } + + static CONST double +tens[] = { + 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, + 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, + 1e20, 1e21, 1e22 +#ifdef VAX + , 1e23, 1e24 +#endif + }; + + static CONST double +#ifdef IEEE_Arith +bigtens[] = { 1e16, 1e32, 1e64, 1e128, 1e256 }; +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64, 1e-128, +#ifdef Avoid_Underflow + 9007199254740992.*9007199254740992.e-256 + /* = 2^106 * 1e-256 */ +#else + 1e-256 +#endif + }; +/* The factor of 2^53 in tinytens[4] helps us avoid setting the underflow */ +/* flag unnecessarily. It leads to a song and dance at the end of strtod. */ +#define Scale_Bit 0x10 +#define n_bigtens 5 +#else +#ifdef IBM +bigtens[] = { 1e16, 1e32, 1e64 }; +static CONST double tinytens[] = { 1e-16, 1e-32, 1e-64 }; +#define n_bigtens 3 +#else +bigtens[] = { 1e16, 1e32 }; +static CONST double tinytens[] = { 1e-16, 1e-32 }; +#define n_bigtens 2 +#endif +#endif + +#undef Need_Hexdig +#ifdef INFNAN_CHECK +#ifndef No_Hex_NaN +#define Need_Hexdig +#endif +#endif + +#ifndef Need_Hexdig +#ifndef NO_HEX_FP +#define Need_Hexdig +#endif +#endif + +#ifdef Need_Hexdig /*{*/ +static unsigned char hexdig[256]; + + static void +#ifdef KR_headers +htinit(h, s, inc) unsigned char *h; unsigned char *s; int inc; +#else +htinit(unsigned char *h, unsigned char *s, int inc) +#endif +{ + int i, j; + for(i = 0; (j = s[i]) !=0; i++) + h[j] = i + inc; + } + + static void +#ifdef KR_headers +hexdig_init() +#else +hexdig_init(void) +#endif +{ +#define USC (unsigned char *) + htinit(hexdig, USC "0123456789", 0x10); + htinit(hexdig, USC "abcdef", 0x10 + 10); + htinit(hexdig, USC "ABCDEF", 0x10 + 10); + } +#endif /* } Need_Hexdig */ + +#ifdef INFNAN_CHECK + +#ifndef NAN_WORD0 +#define NAN_WORD0 0x7ff80000 +#endif + +#ifndef NAN_WORD1 +#define NAN_WORD1 0 +#endif + + static int +match +#ifdef KR_headers + (sp, t) char **sp, *t; +#else + (CONST char **sp, CONST char *t) +#endif +{ + int c, d; + CONST char *s = *sp; + + while((d = *t++)) { + if ((c = *++s) >= 'A' && c <= 'Z') + c += 'a' - 'A'; + if (c != d) + return 0; + } + *sp = s + 1; + return 1; + } + +#ifndef No_Hex_NaN + static void +hexnan +#ifdef KR_headers + (rvp, sp) U *rvp; CONST char **sp; +#else + (U *rvp, CONST char **sp) +#endif +{ + ULong c, x[2]; + CONST char *s; + int c1, havedig, udx0, xshift; + + if (!hexdig['0']) + hexdig_init(); + x[0] = x[1] = 0; + havedig = xshift = 0; + udx0 = 1; + s = *sp; + /* allow optional initial 0x or 0X */ + while((c = *(CONST unsigned char*)(s+1)) && c <= ' ') + ++s; + if (s[1] == '0' && (s[2] == 'x' || s[2] == 'X')) + s += 2; + while((c = *(CONST unsigned char*)++s)) { + if ((c1 = hexdig[c])) + c = c1 & 0xf; + else if (c <= ' ') { + if (udx0 && havedig) { + udx0 = 0; + xshift = 1; + } + continue; + } +#ifdef GDTOA_NON_PEDANTIC_NANCHECK + else if (/*(*/ c == ')' && havedig) { + *sp = s + 1; + break; + } + else + return; /* invalid form: don't change *sp */ +#else + else { + do { + if (/*(*/ c == ')') { + *sp = s + 1; + break; + } + } while((c = *++s)); + break; + } +#endif + havedig = 1; + if (xshift) { + xshift = 0; + x[0] = x[1]; + x[1] = 0; + } + if (udx0) + x[0] = (x[0] << 4) | (x[1] >> 28); + x[1] = (x[1] << 4) | c; + } + if ((x[0] &= 0xfffff) || x[1]) { + word0(rvp) = Exp_mask | x[0]; + word1(rvp) = x[1]; + } + } +#endif /*No_Hex_NaN*/ +#endif /* INFNAN_CHECK */ + +#ifdef Pack_32 +#define ULbits 32 +#define kshift 5 +#define kmask 31 +#else +#define ULbits 16 +#define kshift 4 +#define kmask 15 +#endif +#ifndef NO_HEX_FP /*{*/ + + static void +#ifdef KR_headers +rshift(b, k) Bigint *b; int k; +#else +rshift(Bigint *b, int k) +#endif +{ + ULong *x, *x1, *xe, y; + int n; + + x = x1 = b->x; + n = k >> kshift; + if (n < b->wds) { + xe = x + b->wds; + x += n; + if (k &= kmask) { + n = 32 - k; + y = *x++ >> k; + while(x < xe) { + *x1++ = (y | (*x << n)) & 0xffffffff; + y = *x++ >> k; + } + if ((*x1 = y) !=0) + x1++; + } + else + while(x < xe) + *x1++ = *x++; + } + if ((b->wds = x1 - b->x) == 0) + b->x[0] = 0; + } + + static ULong +#ifdef KR_headers +any_on(b, k) Bigint *b; int k; +#else +any_on(Bigint *b, int k) +#endif +{ + int n, nwds; + ULong *x, *x0, x1, x2; + + x = b->x; + nwds = b->wds; + n = k >> kshift; + if (n > nwds) + n = nwds; + else if (n < nwds && (k &= kmask)) { + x1 = x2 = x[n]; + x1 >>= k; + x1 <<= k; + if (x1 != x2) + return 1; + } + x0 = x; + x += n; + while(x > x0) + if (*--x) + return 1; + return 0; + } + +enum { /* rounding values: same as FLT_ROUNDS */ + Round_zero = 0, + Round_near = 1, + Round_up = 2, + Round_down = 3 + }; + + static Bigint * +#ifdef KR_headers +increment(b) Bigint *b; +#else +increment(Bigint *b) +#endif +{ + ULong *x, *xe; + Bigint *b1; + + x = b->x; + xe = x + b->wds; + do { + if (*x < (ULong)0xffffffffL) { + ++*x; + return b; + } + *x++ = 0; + } while(x < xe); + { + if (b->wds >= b->maxwds) { + b1 = Balloc(b->k+1); + Bcopy(b1,b); + Bfree(b); + b = b1; + } + b->x[b->wds++] = 1; + } + return b; + } + + void +#ifdef KR_headers +gethex(sp, rvp, rounding, sign) + CONST char **sp; U *rvp; int rounding, sign; +#else +gethex( CONST char **sp, U *rvp, int rounding, int sign) +#endif +{ + Bigint *b; + CONST unsigned char *decpt, *s0, *s, *s1; + Long e, e1; + ULong L, lostbits, *x; + int big, denorm, esign, havedig, k, n, nbits, up, zret; +#ifdef IBM + int j; +#endif + enum { +#ifdef IEEE_Arith /*{{*/ + emax = 0x7fe - Bias - P + 1, + emin = Emin - P + 1 +#else /*}{*/ + emin = Emin - P, +#ifdef VAX + emax = 0x7ff - Bias - P + 1 +#endif +#ifdef IBM + emax = 0x7f - Bias - P +#endif +#endif /*}}*/ + }; +#ifdef USE_LOCALE + int i; +#ifdef NO_LOCALE_CACHE + const unsigned char *decimalpoint = (unsigned char*) + localeconv()->decimal_point; +#else + const unsigned char *decimalpoint; + static unsigned char *decimalpoint_cache; + if (!(s0 = decimalpoint_cache)) { + s0 = (unsigned char*)localeconv()->decimal_point; + if ((decimalpoint_cache = (unsigned char*) + MALLOC(strlen((CONST char*)s0) + 1))) { + strcpy((char*)decimalpoint_cache, (CONST char*)s0); + s0 = decimalpoint_cache; + } + } + decimalpoint = s0; +#endif +#endif + + if (!hexdig['0']) + hexdig_init(); + havedig = 0; + s0 = *(CONST unsigned char **)sp + 2; + while(s0[havedig] == '0') + havedig++; + s0 += havedig; + s = s0; + decpt = 0; + zret = 0; + e = 0; + if (hexdig[*s]) + havedig++; + else { + zret = 1; +#ifdef USE_LOCALE + for(i = 0; decimalpoint[i]; ++i) { + if (s[i] != decimalpoint[i]) + goto pcheck; + } + decpt = s += i; +#else + if (*s != '.') + goto pcheck; + decpt = ++s; +#endif + if (!hexdig[*s]) + goto pcheck; + while(*s == '0') + s++; + if (hexdig[*s]) + zret = 0; + havedig = 1; + s0 = s; + } + while(hexdig[*s]) + s++; +#ifdef USE_LOCALE + if (*s == *decimalpoint && !decpt) { + for(i = 1; decimalpoint[i]; ++i) { + if (s[i] != decimalpoint[i]) + goto pcheck; + } + decpt = s += i; +#else + if (*s == '.' && !decpt) { + decpt = ++s; +#endif + while(hexdig[*s]) + s++; + }/*}*/ + if (decpt) + e = -(((Long)(s-decpt)) << 2); + pcheck: + s1 = s; + big = esign = 0; + switch(*s) { + case 'p': + case 'P': + switch(*++s) { + case '-': + esign = 1; + /* no break */ + case '+': + s++; + } + if ((n = hexdig[*s]) == 0 || n > 0x19) { + s = s1; + break; + } + e1 = n - 0x10; + while((n = hexdig[*++s]) !=0 && n <= 0x19) { + if (e1 & 0xf8000000) + big = 1; + e1 = 10*e1 + n - 0x10; + } + if (esign) + e1 = -e1; + e += e1; + } + *sp = (char*)s; + if (!havedig) + *sp = (char*)s0 - 1; + if (zret) + goto retz1; + if (big) { + if (esign) { +#ifdef IEEE_Arith + switch(rounding) { + case Round_up: + if (sign) + break; + goto ret_tiny; + case Round_down: + if (!sign) + break; + goto ret_tiny; + } +#endif + goto retz; +#ifdef IEEE_Arith + ret_tiny: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + word0(rvp) = 0; + word1(rvp) = 1; + return; +#endif /* IEEE_Arith */ + } + switch(rounding) { + case Round_near: + goto ovfl1; + case Round_up: + if (!sign) + goto ovfl1; + goto ret_big; + case Round_down: + if (sign) + goto ovfl1; + goto ret_big; + } + ret_big: + word0(rvp) = Big0; + word1(rvp) = Big1; + return; + } + n = s1 - s0 - 1; + for(k = 0; n > (1 << (kshift-2)) - 1; n >>= 1) + k++; + b = Balloc(k); + x = b->x; + n = 0; + L = 0; +#ifdef USE_LOCALE + for(i = 0; decimalpoint[i+1]; ++i); +#endif + while(s1 > s0) { +#ifdef USE_LOCALE + if (*--s1 == decimalpoint[i]) { + s1 -= i; + continue; + } +#else + if (*--s1 == '.') + continue; +#endif + if (n == ULbits) { + *x++ = L; + L = 0; + n = 0; + } + L |= (hexdig[*s1] & 0x0f) << n; + n += 4; + } + *x++ = L; + b->wds = n = x - b->x; + n = ULbits*n - hi0bits(L); + nbits = Nbits; + lostbits = 0; + x = b->x; + if (n > nbits) { + n -= nbits; + if (any_on(b,n)) { + lostbits = 1; + k = n - 1; + if (x[k>>kshift] & 1 << (k & kmask)) { + lostbits = 2; + if (k > 0 && any_on(b,k)) + lostbits = 3; + } + } + rshift(b, n); + e += n; + } + else if (n < nbits) { + n = nbits - n; + b = lshift(b, n); + e -= n; + x = b->x; + } + if (e > Emax) { + ovfl: + Bfree(b); + ovfl1: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + word0(rvp) = Exp_mask; + word1(rvp) = 0; + return; + } + denorm = 0; + if (e < emin) { + denorm = 1; + n = emin - e; + if (n >= nbits) { +#ifdef IEEE_Arith /*{*/ + switch (rounding) { + case Round_near: + if (n == nbits && (n < 2 || any_on(b,n-1))) + goto ret_tiny; + break; + case Round_up: + if (!sign) + goto ret_tiny; + break; + case Round_down: + if (sign) + goto ret_tiny; + } +#endif /* } IEEE_Arith */ + Bfree(b); + retz: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + retz1: + rvp->d = 0.; + return; + } + k = n - 1; + if (lostbits) + lostbits = 1; + else if (k > 0) + lostbits = any_on(b,k); + if (x[k>>kshift] & 1 << (k & kmask)) + lostbits |= 2; + nbits -= n; + rshift(b,n); + e = emin; + } + if (lostbits) { + up = 0; + switch(rounding) { + case Round_zero: + break; + case Round_near: + if (lostbits & 2 + && (lostbits & 1) | (x[0] & 1)) + up = 1; + break; + case Round_up: + up = 1 - sign; + break; + case Round_down: + up = sign; + } + if (up) { + k = b->wds; + b = increment(b); + x = b->x; + if (denorm) { +#if 0 + if (nbits == Nbits - 1 + && x[nbits >> kshift] & 1 << (nbits & kmask)) + denorm = 0; /* not currently used */ +#endif + } + else if (b->wds > k + || ((n = nbits & kmask) !=0 + && hi0bits(x[k-1]) < 32-n)) { + rshift(b,1); + if (++e > Emax) + goto ovfl; + } + } + } +#ifdef IEEE_Arith + if (denorm) + word0(rvp) = b->wds > 1 ? b->x[1] & ~0x100000 : 0; + else + word0(rvp) = (b->x[1] & ~0x100000) | ((e + 0x3ff + 52) << 20); + word1(rvp) = b->x[0]; +#endif +#ifdef IBM + if ((j = e & 3)) { + k = b->x[0] & ((1 << j) - 1); + rshift(b,j); + if (k) { + switch(rounding) { + case Round_up: + if (!sign) + increment(b); + break; + case Round_down: + if (sign) + increment(b); + break; + case Round_near: + j = 1 << (j-1); + if (k & j && ((k & (j-1)) | lostbits)) + increment(b); + } + } + } + e >>= 2; + word0(rvp) = b->x[1] | ((e + 65 + 13) << 24); + word1(rvp) = b->x[0]; +#endif +#ifdef VAX + /* The next two lines ignore swap of low- and high-order 2 bytes. */ + /* word0(rvp) = (b->x[1] & ~0x800000) | ((e + 129 + 55) << 23); */ + /* word1(rvp) = b->x[0]; */ + word0(rvp) = ((b->x[1] & ~0x800000) >> 16) | ((e + 129 + 55) << 7) | (b->x[1] << 16); + word1(rvp) = (b->x[0] >> 16) | (b->x[0] << 16); +#endif + Bfree(b); + } +#endif /*}!NO_HEX_FP*/ + + static int +#ifdef KR_headers +dshift(b, p2) Bigint *b; int p2; +#else +dshift(Bigint *b, int p2) +#endif +{ + int rv = hi0bits(b->x[b->wds-1]) - 4; + if (p2 > 0) + rv -= p2; + return rv & kmask; + } + + static int +quorem +#ifdef KR_headers + (b, S) Bigint *b, *S; +#else + (Bigint *b, Bigint *S) +#endif +{ + int n; + ULong *bx, *bxe, q, *sx, *sxe; +#ifdef ULLong + ULLong borrow, carry, y, ys; +#else + ULong borrow, carry, y, ys; +#ifdef Pack_32 + ULong si, z, zs; +#endif +#endif + + n = S->wds; +#ifdef DEBUG + /*debug*/ if (b->wds > n) + /*debug*/ Bug("oversize b in quorem"); +#endif + if (b->wds < n) + return 0; + sx = S->x; + sxe = sx + --n; + bx = b->x; + bxe = bx + n; + q = *bxe / (*sxe + 1); /* ensure q <= true quotient */ +#ifdef DEBUG + /*debug*/ if (q > 9) + /*debug*/ Bug("oversized quotient in quorem"); +#endif + if (q) { + borrow = 0; + carry = 0; + do { +#ifdef ULLong + ys = *sx++ * (ULLong)q + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) * q + carry; + zs = (si >> 16) * q + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ * q + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + if (!*bxe) { + bx = b->x; + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + if (cmp(b, S) >= 0) { + q++; + borrow = 0; + carry = 0; + bx = b->x; + sx = S->x; + do { +#ifdef ULLong + ys = *sx++ + carry; + carry = ys >> 32; + y = *bx - (ys & FFFFFFFF) - borrow; + borrow = y >> 32 & (ULong)1; + *bx++ = y & FFFFFFFF; +#else +#ifdef Pack_32 + si = *sx++; + ys = (si & 0xffff) + carry; + zs = (si >> 16) + (ys >> 16); + carry = zs >> 16; + y = (*bx & 0xffff) - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + z = (*bx >> 16) - (zs & 0xffff) - borrow; + borrow = (z & 0x10000) >> 16; + Storeinc(bx, z, y); +#else + ys = *sx++ + carry; + carry = ys >> 16; + y = *bx - (ys & 0xffff) - borrow; + borrow = (y & 0x10000) >> 16; + *bx++ = y & 0xffff; +#endif +#endif + } + while(sx <= sxe); + bx = b->x; + bxe = bx + n; + if (!*bxe) { + while(--bxe > bx && !*bxe) + --n; + b->wds = n; + } + } + return q; + } + +#ifndef NO_STRTOD_BIGCOMP + + static void +bigcomp +#ifdef KR_headers + (rv, s0, bc) + U *rv; CONST char *s0; BCinfo *bc; +#else + (U *rv, CONST char *s0, BCinfo *bc) +#endif +{ + Bigint *b, *d; + int b2, bbits, d2, dd, dig, dsign, i, j, nd, nd0, p2, p5, speccase; + + dsign = bc->dsign; + nd = bc->nd; + nd0 = bc->nd0; + p5 = nd + bc->e0 - 1; + dd = speccase = 0; +#ifndef Sudden_Underflow + if (rv->d == 0.) { /* special case: value near underflow-to-zero */ + /* threshold was rounded to zero */ + b = i2b(1); + p2 = Emin - P + 1; + bbits = 1; +#ifdef Avoid_Underflow + word0(rv) = (P+2) << Exp_shift; +#else + word1(rv) = 1; +#endif + i = 0; +#ifdef Honor_FLT_ROUNDS + if (bc->rounding == 1) +#endif + { + speccase = 1; + --p2; + dsign = 0; + goto have_i; + } + } + else +#endif + b = d2b(rv, &p2, &bbits); +#ifdef Avoid_Underflow + p2 -= bc->scale; +#endif + /* floor(log2(rv)) == bbits - 1 + p2 */ + /* Check for denormal case. */ + i = P - bbits; + if (i > (j = P - Emin - 1 + p2)) { +#ifdef Sudden_Underflow + Bfree(b); + b = i2b(1); + p2 = Emin; + i = P - 1; +#ifdef Avoid_Underflow + word0(rv) = (1 + bc->scale) << Exp_shift; +#else + word0(rv) = Exp_msk1; +#endif + word1(rv) = 0; +#else + i = j; +#endif + } +#ifdef Honor_FLT_ROUNDS + if (bc->rounding != 1) { + if (i > 0) + b = lshift(b, i); + if (dsign) + b = increment(b); + } + else +#endif + { + b = lshift(b, ++i); + b->x[0] |= 1; + } +#ifndef Sudden_Underflow + have_i: +#endif + p2 -= p5 + i; + d = i2b(1); + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + */ + if (p5 > 0) + d = pow5mult(d, p5); + else if (p5 < 0) + b = pow5mult(b, -p5); + if (p2 > 0) { + b2 = p2; + d2 = 0; + } + else { + b2 = 0; + d2 = -p2; + } + i = dshift(d, d2); + if ((b2 += i) > 0) + b = lshift(b, b2); + if ((d2 += i) > 0) + d = lshift(d, d2); + + /* Now b/d = exactly half-way between the two floating-point values */ + /* on either side of the input string. Compute first digit of b/d. */ + + if (!(dig = quorem(b,d))) { + b = multadd(b, 10, 0); /* very unlikely */ + dig = quorem(b,d); + } + + /* Compare b/d with s0 */ + + for(i = 0; i < nd0; ) { + if ((dd = s0[i++] - '0' - dig)) + goto ret; + if (!b->x[0] && b->wds == 1) { + if (i < nd) + dd = 1; + goto ret; + } + b = multadd(b, 10, 0); + dig = quorem(b,d); + } + for(j = bc->dp1; i++ < nd;) { + if ((dd = s0[j++] - '0' - dig)) + goto ret; + if (!b->x[0] && b->wds == 1) { + if (i < nd) + dd = 1; + goto ret; + } + b = multadd(b, 10, 0); + dig = quorem(b,d); + } + if (b->x[0] || b->wds > 1) + dd = -1; + ret: + Bfree(b); + Bfree(d); +#ifdef Honor_FLT_ROUNDS + if (bc->rounding != 1) { + if (dd < 0) { + if (bc->rounding == 0) { + if (!dsign) + goto retlow1; + } + else if (dsign) + goto rethi1; + } + else if (dd > 0) { + if (bc->rounding == 0) { + if (dsign) + goto rethi1; + goto ret1; + } + if (!dsign) + goto rethi1; + dval(rv) += 2.*ulp(rv); + } + else { + bc->inexact = 0; + if (dsign) + goto rethi1; + } + } + else +#endif + if (speccase) { + if (dd <= 0) + rv->d = 0.; + } + else if (dd < 0) { + if (!dsign) /* does not happen for round-near */ +retlow1: + dval(rv) -= ulp(rv); + } + else if (dd > 0) { + if (dsign) { + rethi1: + dval(rv) += ulp(rv); + } + } + else { + /* Exact half-way case: apply round-even rule. */ + if (word1(rv) & 1) { + if (dsign) + goto rethi1; + goto retlow1; + } + } + +#ifdef Honor_FLT_ROUNDS + ret1: +#endif + return; + } +#endif /* NO_STRTOD_BIGCOMP */ + + double +strtod +#ifdef KR_headers + (s00, se) CONST char *s00; char **se; +#else + (CONST char *s00, char **se) +#endif +{ + int bb2, bb5, bbe, bd2, bd5, bbbits, bs2, c, e, e1; + int esign, i, j, k, nd, nd0, nf, nz, nz0, sign; + CONST char *s, *s0, *s1; + double aadj, aadj1; + Long L; + U aadj2, adj, rv, rv0; + ULong y, z; + BCinfo bc; + Bigint *bb, *bb1, *bd, *bd0, *bs, *delta; +#ifdef SET_INEXACT + int oldinexact; +#endif +#ifdef Honor_FLT_ROUNDS /*{*/ +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + bc.rounding = Flt_Rounds; +#else /*}{*/ + bc.rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: bc.rounding = 0; break; + case FE_UPWARD: bc.rounding = 2; break; + case FE_DOWNWARD: bc.rounding = 3; + } +#endif /*}}*/ +#endif /*}*/ +#ifdef USE_LOCALE + CONST char *s2; +#endif + + sign = nz0 = nz = bc.dplen = bc.uflchk = 0; + dval(&rv) = 0.; + for(s = s00;;s++) switch(*s) { + case '-': + sign = 1; + /* no break */ + case '+': + if (*++s) + goto break2; + /* no break */ + case 0: + goto ret0; + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + case ' ': + continue; + default: + goto break2; + } + break2: + if (*s == '0') { +#ifndef NO_HEX_FP /*{*/ + switch(s[1]) { + case 'x': + case 'X': +#ifdef Honor_FLT_ROUNDS + gethex(&s, &rv, bc.rounding, sign); +#else + gethex(&s, &rv, 1, sign); +#endif + goto ret; + } +#endif /*}*/ + nz0 = 1; + while(*++s == '0') ; + if (!*s) + goto ret; + } + s0 = s; + y = z = 0; + for(nd = nf = 0; (c = *s) >= '0' && c <= '9'; nd++, s++) + if (nd < 9) + y = 10*y + c - '0'; + else if (nd < 16) + z = 10*z + c - '0'; + nd0 = nd; + bc.dp0 = bc.dp1 = s - s0; +#ifdef USE_LOCALE + s1 = localeconv()->decimal_point; + if (c == *s1) { + c = '.'; + if (*++s1) { + s2 = s; + for(;;) { + if (*++s2 != *s1) { + c = 0; + break; + } + if (!*++s1) { + s = s2; + break; + } + } + } + } +#endif + if (c == '.') { + c = *++s; + bc.dp1 = s - s0; + bc.dplen = bc.dp1 - bc.dp0; + if (!nd) { + for(; c == '0'; c = *++s) + nz++; + if (c > '0' && c <= '9') { + s0 = s; + nf += nz; + nz = 0; + goto have_dig; + } + goto dig_done; + } + for(; c >= '0' && c <= '9'; c = *++s) { + have_dig: + nz++; + if (c -= '0') { + nf += nz; + for(i = 1; i < nz; i++) + if (nd++ < 9) + y *= 10; + else if (nd <= DBL_DIG + 1) + z *= 10; + if (nd++ < 9) + y = 10*y + c; + else if (nd <= DBL_DIG + 1) + z = 10*z + c; + nz = 0; + } + } + } + dig_done: + e = 0; + if (c == 'e' || c == 'E') { + if (!nd && !nz && !nz0) { + goto ret0; + } + s00 = s; + esign = 0; + switch(c = *++s) { + case '-': + esign = 1; + case '+': + c = *++s; + } + if (c >= '0' && c <= '9') { + while(c == '0') + c = *++s; + if (c > '0' && c <= '9') { + L = c - '0'; + s1 = s; + while((c = *++s) >= '0' && c <= '9') + L = 10*L + c - '0'; + if (s - s1 > 8 || L > 19999) + /* Avoid confusion from exponents + * so large that e might overflow. + */ + e = 19999; /* safe for 16 bit ints */ + else + e = (int)L; + if (esign) + e = -e; + } + else + e = 0; + } + else + s = s00; + } + if (!nd) { + if (!nz && !nz0) { +#ifdef INFNAN_CHECK + /* Check for Nan and Infinity */ + if (!bc.dplen) + switch(c) { + case 'i': + case 'I': + if (match(&s,"nf")) { + --s; + if (!match(&s,"inity")) + ++s; + word0(&rv) = 0x7ff00000; + word1(&rv) = 0; + goto ret; + } + break; + case 'n': + case 'N': + if (match(&s, "an")) { + word0(&rv) = NAN_WORD0; + word1(&rv) = NAN_WORD1; +#ifndef No_Hex_NaN + if (*s == '(') /*)*/ + hexnan(&rv, &s); +#endif + goto ret; + } + } +#endif /* INFNAN_CHECK */ + ret0: + s = s00; + sign = 0; + } + goto ret; + } + bc.e0 = e1 = e -= nf; + + /* Now we have nd0 digits, starting at s0, followed by a + * decimal point, followed by nd-nd0 digits. The number we're + * after is the integer represented by those digits times + * 10**e */ + + if (!nd0) + nd0 = nd; + k = nd < DBL_DIG + 1 ? nd : DBL_DIG + 1; + dval(&rv) = y; + if (k > 9) { +#ifdef SET_INEXACT + if (k > DBL_DIG) + oldinexact = get_inexact(); +#endif + dval(&rv) = tens[k - 9] * dval(&rv) + z; + } + bd0 = 0; + if (nd <= DBL_DIG +#ifndef RND_PRODQUOT +#ifndef Honor_FLT_ROUNDS + && Flt_Rounds == 1 +#endif +#endif + ) { + if (!e) + goto ret; + if (e > 0) { + if (e <= Ten_pmax) { +#ifdef VAX + goto vax_ovfl_check; +#else +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + /* rv = */ rounded_product(dval(&rv), tens[e]); + goto ret; +#endif + } + i = DBL_DIG - nd; + if (e <= Ten_pmax + i) { + /* A fancier test would sometimes let us do + * this for larger i values. + */ +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + e -= i; + dval(&rv) *= tens[i]; +#ifdef VAX + /* VAX exponent range is so narrow we must + * worry about overflow here... + */ + vax_ovfl_check: + word0(&rv) -= P*Exp_msk1; + /* rv = */ rounded_product(dval(&rv), tens[e]); + if ((word0(&rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) + goto ovfl; + word0(&rv) += P*Exp_msk1; +#else + /* rv = */ rounded_product(dval(&rv), tens[e]); +#endif + goto ret; + } + } +#ifndef Inaccurate_Divide + else if (e >= -Ten_pmax) { +#ifdef Honor_FLT_ROUNDS + /* round correctly FLT_ROUNDS = 2 or 3 */ + if (sign) { + rv.d = -rv.d; + sign = 0; + } +#endif + /* rv = */ rounded_quotient(dval(&rv), tens[-e]); + goto ret; + } +#endif + } + e1 += nd - k; + +#ifdef IEEE_Arith +#ifdef SET_INEXACT + bc.inexact = 1; + if (k <= DBL_DIG) + oldinexact = get_inexact(); +#endif +#ifdef Avoid_Underflow + bc.scale = 0; +#endif +#ifdef Honor_FLT_ROUNDS + if (bc.rounding >= 2) { + if (sign) + bc.rounding = bc.rounding == 2 ? 0 : 2; + else + if (bc.rounding != 2) + bc.rounding = 0; + } +#endif +#endif /*IEEE_Arith*/ + + /* Get starting approximation = rv * 10**e1 */ + + if (e1 > 0) { + if ((i = e1 & 15)) + dval(&rv) *= tens[i]; + if (e1 &= ~15) { + if (e1 > DBL_MAX_10_EXP) { + ovfl: +#ifndef NO_ERRNO + errno = ERANGE; +#endif + /* Can't trust HUGE_VAL */ +#ifdef IEEE_Arith +#ifdef Honor_FLT_ROUNDS + switch(bc.rounding) { + case 0: /* toward 0 */ + case 3: /* toward -infinity */ + word0(&rv) = Big0; + word1(&rv) = Big1; + break; + default: + word0(&rv) = Exp_mask; + word1(&rv) = 0; + } +#else /*Honor_FLT_ROUNDS*/ + word0(&rv) = Exp_mask; + word1(&rv) = 0; +#endif /*Honor_FLT_ROUNDS*/ +#ifdef SET_INEXACT + /* set overflow bit */ + dval(&rv0) = 1e300; + dval(&rv0) *= dval(&rv0); +#endif +#else /*IEEE_Arith*/ + word0(&rv) = Big0; + word1(&rv) = Big1; +#endif /*IEEE_Arith*/ + goto ret; + } + e1 >>= 4; + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= bigtens[j]; + /* The last multiplication could overflow. */ + word0(&rv) -= P*Exp_msk1; + dval(&rv) *= bigtens[j]; + if ((z = word0(&rv) & Exp_mask) + > Exp_msk1*(DBL_MAX_EXP+Bias-P)) + goto ovfl; + if (z > Exp_msk1*(DBL_MAX_EXP+Bias-1-P)) { + /* set to largest number */ + /* (Can't trust DBL_MAX) */ + word0(&rv) = Big0; + word1(&rv) = Big1; + } + else + word0(&rv) += P*Exp_msk1; + } + } + else if (e1 < 0) { + e1 = -e1; + if ((i = e1 & 15)) + dval(&rv) /= tens[i]; + if (e1 >>= 4) { + if (e1 >= 1 << n_bigtens) + goto undfl; +#ifdef Avoid_Underflow + if (e1 & Scale_Bit) + bc.scale = 2*P; + for(j = 0; e1 > 0; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= tinytens[j]; + if (bc.scale && (j = 2*P + 1 - ((word0(&rv) & Exp_mask) + >> Exp_shift)) > 0) { + /* scaled rv is denormal; clear j low bits */ + if (j >= 32) { + word1(&rv) = 0; + if (j >= 53) + word0(&rv) = (P+2)*Exp_msk1; + else + word0(&rv) &= 0xffffffff << (j-32); + } + else + word1(&rv) &= 0xffffffff << j; + } +#else + for(j = 0; e1 > 1; j++, e1 >>= 1) + if (e1 & 1) + dval(&rv) *= tinytens[j]; + /* The last multiplication could underflow. */ + dval(&rv0) = dval(&rv); + dval(&rv) *= tinytens[j]; + if (!dval(&rv)) { + dval(&rv) = 2.*dval(&rv0); + dval(&rv) *= tinytens[j]; +#endif + if (!dval(&rv)) { + undfl: + dval(&rv) = 0.; +#ifndef NO_ERRNO + errno = ERANGE; +#endif + goto ret; + } +#ifndef Avoid_Underflow + word0(&rv) = Tiny0; + word1(&rv) = Tiny1; + /* The refinement below will clean + * this approximation up. + */ + } +#endif + } + } + + /* Now the hard part -- adjusting rv to the correct value.*/ + + /* Put digits into bd: true value = bd * 10^e */ + + bc.nd = nd; +#ifndef NO_STRTOD_BIGCOMP + bc.nd0 = nd0; /* Only needed if nd > strtod_diglim, but done here */ + /* to silence an erroneous warning about bc.nd0 */ + /* possibly not being initialized. */ + if (nd > strtod_diglim) { + /* ASSERT(strtod_diglim >= 18); 18 == one more than the */ + /* minimum number of decimal digits to distinguish double values */ + /* in IEEE arithmetic. */ + i = j = 18; + if (i > nd0) + j += bc.dplen; + for(;;) { + if (--j <= bc.dp1 && j >= bc.dp0) + j = bc.dp0 - 1; + if (s0[j] != '0') + break; + --i; + } + e += nd - i; + nd = i; + if (nd0 > nd) + nd0 = nd; + if (nd < 9) { /* must recompute y */ + y = 0; + for(i = 0; i < nd0; ++i) + y = 10*y + s0[i] - '0'; + for(j = bc.dp1; i < nd; ++i) + y = 10*y + s0[j++] - '0'; + } + } +#endif + bd0 = s2b(s0, nd0, nd, y, bc.dplen); + + for(;;) { + bd = Balloc(bd0->k); + Bcopy(bd, bd0); + bb = d2b(&rv, &bbe, &bbbits); /* rv = bb * 2^bbe */ + bs = i2b(1); + + if (e >= 0) { + bb2 = bb5 = 0; + bd2 = bd5 = e; + } + else { + bb2 = bb5 = -e; + bd2 = bd5 = 0; + } + if (bbe >= 0) + bb2 += bbe; + else + bd2 -= bbe; + bs2 = bb2; +#ifdef Honor_FLT_ROUNDS + if (bc.rounding != 1) + bs2++; +#endif +#ifdef Avoid_Underflow + j = bbe - bc.scale; + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j += P - Emin; + else + j = P + 1 - bbbits; +#else /*Avoid_Underflow*/ +#ifdef Sudden_Underflow +#ifdef IBM + j = 1 + 4*P - 3 - bbbits + ((bbe + bbbits - 1) & 3); +#else + j = P + 1 - bbbits; +#endif +#else /*Sudden_Underflow*/ + j = bbe; + i = j + bbbits - 1; /* logb(rv) */ + if (i < Emin) /* denormal */ + j += P - Emin; + else + j = P + 1 - bbbits; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + bb2 += j; + bd2 += j; +#ifdef Avoid_Underflow + bd2 += bc.scale; +#endif + i = bb2 < bd2 ? bb2 : bd2; + if (i > bs2) + i = bs2; + if (i > 0) { + bb2 -= i; + bd2 -= i; + bs2 -= i; + } + if (bb5 > 0) { + bs = pow5mult(bs, bb5); + bb1 = mult(bs, bb); + Bfree(bb); + bb = bb1; + } + if (bb2 > 0) + bb = lshift(bb, bb2); + if (bd5 > 0) + bd = pow5mult(bd, bd5); + if (bd2 > 0) + bd = lshift(bd, bd2); + if (bs2 > 0) + bs = lshift(bs, bs2); + delta = diff(bb, bd); + bc.dsign = delta->sign; + delta->sign = 0; + i = cmp(delta, bs); +#ifndef NO_STRTOD_BIGCOMP + if (bc.nd > nd && i <= 0) { + if (bc.dsign) + break; /* Must use bigcomp(). */ +#ifdef Honor_FLT_ROUNDS + if (bc.rounding != 1) { + if (i < 0) + break; + } + else +#endif + { + bc.nd = nd; + i = -1; /* Discarded digits make delta smaller. */ + } + } +#endif +#ifdef Honor_FLT_ROUNDS + if (bc.rounding != 1) { + if (i < 0) { + /* Error is less than an ulp */ + if (!delta->x[0] && delta->wds <= 1) { + /* exact */ +#ifdef SET_INEXACT + bc.inexact = 0; +#endif + break; + } + if (bc.rounding) { + if (bc.dsign) { + adj.d = 1.; + goto apply_adj; + } + } + else if (!bc.dsign) { + adj.d = -1.; + if (!word1(&rv) + && !(word0(&rv) & Frac_mask)) { + y = word0(&rv) & Exp_mask; +#ifdef Avoid_Underflow + if (!bc.scale || y > 2*P*Exp_msk1) +#else + if (y) +#endif + { + delta = lshift(delta,Log2P); + if (cmp(delta, bs) <= 0) + adj.d = -0.5; + } + } + apply_adj: +#ifdef Avoid_Underflow + if (bc.scale && (y = word0(&rv) & Exp_mask) + <= 2*P*Exp_msk1) + word0(&adj) += (2*P+1)*Exp_msk1 - y; +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= + P*Exp_msk1) { + word0(&rv) += P*Exp_msk1; + dval(&rv) += adj.d*ulp(dval(&rv)); + word0(&rv) -= P*Exp_msk1; + } + else +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + dval(&rv) += adj.d*ulp(&rv); + } + break; + } + adj.d = ratio(delta, bs); + if (adj.d < 1.) + adj.d = 1.; + if (adj.d <= 0x7ffffffe) { + /* adj = rounding ? ceil(adj) : floor(adj); */ + y = adj.d; + if (y != adj.d) { + if (!((bc.rounding>>1) ^ bc.dsign)) + y++; + adj.d = y; + } + } +#ifdef Avoid_Underflow + if (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) + word0(&adj) += (2*P+1)*Exp_msk1 - y; +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) { + word0(&rv) += P*Exp_msk1; + adj.d *= ulp(dval(&rv)); + if (bc.dsign) + dval(&rv) += adj.d; + else + dval(&rv) -= adj.d; + word0(&rv) -= P*Exp_msk1; + goto cont; + } +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + adj.d *= ulp(&rv); + if (bc.dsign) { + if (word0(&rv) == Big0 && word1(&rv) == Big1) + goto ovfl; + dval(&rv) += adj.d; + } + else + dval(&rv) -= adj.d; + goto cont; + } +#endif /*Honor_FLT_ROUNDS*/ + + if (i < 0) { + /* Error is less than half an ulp -- check for + * special case of mantissa a power of two. + */ + if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask +#ifdef IEEE_Arith +#ifdef Avoid_Underflow + || (word0(&rv) & Exp_mask) <= (2*P+1)*Exp_msk1 +#else + || (word0(&rv) & Exp_mask) <= Exp_msk1 +#endif +#endif + ) { +#ifdef SET_INEXACT + if (!delta->x[0] && delta->wds <= 1) + bc.inexact = 0; +#endif + break; + } + if (!delta->x[0] && delta->wds <= 1) { + /* exact result */ +#ifdef SET_INEXACT + bc.inexact = 0; +#endif + break; + } + delta = lshift(delta,Log2P); + if (cmp(delta, bs) > 0) + goto drop_down; + break; + } + if (i == 0) { + /* exactly half-way between */ + if (bc.dsign) { + if ((word0(&rv) & Bndry_mask1) == Bndry_mask1 + && word1(&rv) == ( +#ifdef Avoid_Underflow + (bc.scale && (y = word0(&rv) & Exp_mask) <= 2*P*Exp_msk1) + ? (0xffffffff & (0xffffffff << (2*P+1-(y>>Exp_shift)))) : +#endif + 0xffffffff)) { + /*boundary case -- increment exponent*/ + word0(&rv) = (word0(&rv) & Exp_mask) + + Exp_msk1 +#ifdef IBM + | Exp_msk1 >> 4 +#endif + ; + word1(&rv) = 0; +#ifdef Avoid_Underflow + bc.dsign = 0; +#endif + break; + } + } + else if (!(word0(&rv) & Bndry_mask) && !word1(&rv)) { + drop_down: + /* boundary case -- decrement exponent */ +#ifdef Sudden_Underflow /*{{*/ + L = word0(&rv) & Exp_mask; +#ifdef IBM + if (L < Exp_msk1) +#else +#ifdef Avoid_Underflow + if (L <= (bc.scale ? (2*P+1)*Exp_msk1 : Exp_msk1)) +#else + if (L <= Exp_msk1) +#endif /*Avoid_Underflow*/ +#endif /*IBM*/ + { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } + L -= Exp_msk1; +#else /*Sudden_Underflow}{*/ +#ifdef Avoid_Underflow + if (bc.scale) { + L = word0(&rv) & Exp_mask; + if (L <= (2*P+1)*Exp_msk1) { + if (L > (P+2)*Exp_msk1) + /* round even ==> */ + /* accept rv */ + break; + /* rv = smallest denormal */ + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } + } +#endif /*Avoid_Underflow*/ + L = (word0(&rv) & Exp_mask) - Exp_msk1; +#endif /*Sudden_Underflow}}*/ + word0(&rv) = L | Bndry_mask1; + word1(&rv) = 0xffffffff; +#ifdef IBM + goto cont; +#else + break; +#endif + } +#ifndef ROUND_BIASED + if (!(word1(&rv) & LSB)) + break; +#endif + if (bc.dsign) + dval(&rv) += ulp(&rv); +#ifndef ROUND_BIASED + else { + dval(&rv) -= ulp(&rv); +#ifndef Sudden_Underflow + if (!dval(&rv)) { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } +#endif + } +#ifdef Avoid_Underflow + bc.dsign = 1 - bc.dsign; +#endif +#endif + break; + } + if ((aadj = ratio(delta, bs)) <= 2.) { + if (bc.dsign) + aadj = aadj1 = 1.; + else if (word1(&rv) || word0(&rv) & Bndry_mask) { +#ifndef Sudden_Underflow + if (word1(&rv) == Tiny1 && !word0(&rv)) { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } +#endif + aadj = 1.; + aadj1 = -1.; + } + else { + /* special case -- power of FLT_RADIX to be */ + /* rounded down... */ + + if (aadj < 2./FLT_RADIX) + aadj = 1./FLT_RADIX; + else + aadj *= 0.5; + aadj1 = -aadj; + } + } + else { + aadj *= 0.5; + aadj1 = bc.dsign ? aadj : -aadj; +#ifdef Check_FLT_ROUNDS + switch(bc.rounding) { + case 2: /* towards +infinity */ + aadj1 -= 0.5; + break; + case 0: /* towards 0 */ + case 3: /* towards -infinity */ + aadj1 += 0.5; + } +#else + if (Flt_Rounds == 0) + aadj1 += 0.5; +#endif /*Check_FLT_ROUNDS*/ + } + y = word0(&rv) & Exp_mask; + + /* Check for overflow */ + + if (y == Exp_msk1*(DBL_MAX_EXP+Bias-1)) { + dval(&rv0) = dval(&rv); + word0(&rv) -= P*Exp_msk1; + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + if ((word0(&rv) & Exp_mask) >= + Exp_msk1*(DBL_MAX_EXP+Bias-P)) { + if (word0(&rv0) == Big0 && word1(&rv0) == Big1) + goto ovfl; + word0(&rv) = Big0; + word1(&rv) = Big1; + goto cont; + } + else + word0(&rv) += P*Exp_msk1; + } + else { +#ifdef Avoid_Underflow + if (bc.scale && y <= 2*P*Exp_msk1) { + if (aadj <= 0x7fffffff) { + if ((z = aadj) <= 0) + z = 1; + aadj = z; + aadj1 = bc.dsign ? aadj : -aadj; + } + dval(&aadj2) = aadj1; + word0(&aadj2) += (2*P+1)*Exp_msk1 - y; + aadj1 = dval(&aadj2); + } + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; +#else +#ifdef Sudden_Underflow + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) { + dval(&rv0) = dval(&rv); + word0(&rv) += P*Exp_msk1; + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; +#ifdef IBM + if ((word0(&rv) & Exp_mask) < P*Exp_msk1) +#else + if ((word0(&rv) & Exp_mask) <= P*Exp_msk1) +#endif + { + if (word0(&rv0) == Tiny0 + && word1(&rv0) == Tiny1) { + if (bc.nd >nd) { + bc.uflchk = 1; + break; + } + goto undfl; + } + word0(&rv) = Tiny0; + word1(&rv) = Tiny1; + goto cont; + } + else + word0(&rv) -= P*Exp_msk1; + } + else { + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; + } +#else /*Sudden_Underflow*/ + /* Compute adj so that the IEEE rounding rules will + * correctly round rv + adj in some half-way cases. + * If rv * ulp(rv) is denormalized (i.e., + * y <= (P-1)*Exp_msk1), we must adjust aadj to avoid + * trouble from bits lost to denormalization; + * example: 1.2e-307 . + */ + if (y <= (P-1)*Exp_msk1 && aadj > 1.) { + aadj1 = (double)(int)(aadj + 0.5); + if (!bc.dsign) + aadj1 = -aadj1; + } + adj.d = aadj1 * ulp(&rv); + dval(&rv) += adj.d; +#endif /*Sudden_Underflow*/ +#endif /*Avoid_Underflow*/ + } + z = word0(&rv) & Exp_mask; +#ifndef SET_INEXACT + if (bc.nd == nd) { +#ifdef Avoid_Underflow + if (!bc.scale) +#endif + if (y == z) { + /* Can we stop now? */ + L = (Long)aadj; + aadj -= L; + /* The tolerances below are conservative. */ + if (bc.dsign || word1(&rv) || word0(&rv) & Bndry_mask) { + if (aadj < .4999999 || aadj > .5000001) + break; + } + else if (aadj < .4999999/FLT_RADIX) + break; + } + } +#endif + cont: + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(delta); + } + Bfree(bb); + Bfree(bd); + Bfree(bs); + Bfree(bd0); + Bfree(delta); +#ifndef NO_STRTOD_BIGCOMP + if (bc.nd > nd) + bigcomp(&rv, s0, &bc); +#endif +#ifdef SET_INEXACT + if (bc.inexact) { + if (!oldinexact) { + word0(&rv0) = Exp_1 + (70 << Exp_shift); + word1(&rv0) = 0; + dval(&rv0) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif +#ifdef Avoid_Underflow + if (bc.scale) { + word0(&rv0) = Exp_1 - 2*P*Exp_msk1; + word1(&rv0) = 0; + dval(&rv) *= dval(&rv0); +#ifndef NO_ERRNO + /* try to avoid the bug of testing an 8087 register value */ +#ifdef IEEE_Arith + if (!(word0(&rv) & Exp_mask)) +#else + if (word0(&rv) == 0 && word1(&rv) == 0) +#endif + errno = ERANGE; +#endif + } +#endif /* Avoid_Underflow */ +#ifdef SET_INEXACT + if (bc.inexact && !(word0(&rv) & Exp_mask)) { + /* set underflow bit */ + dval(&rv0) = 1e-300; + dval(&rv0) *= dval(&rv0); + } +#endif + ret: + if (se) + *se = (char *)s; + return sign ? -dval(&rv) : dval(&rv); + } + +#ifndef MULTIPLE_THREADS + static char *dtoa_result; +#endif + + static char * +#ifdef KR_headers +rv_alloc(i) int i; +#else +rv_alloc(int i) +#endif +{ + int j, k, *r; + + j = sizeof(ULong); + for(k = 0; + sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= (size_t)i; + j <<= 1) + k++; + r = (int*)Balloc(k); + *r = k; + return +#ifndef MULTIPLE_THREADS + dtoa_result = +#endif + (char *)(r+1); + } + + static char * +#ifdef KR_headers +nrv_alloc(s, rve, n) char *s, **rve; int n; +#else +nrv_alloc(CONST char *s, char **rve, int n) +#endif +{ + char *rv, *t; + + t = rv = rv_alloc(n); + while((*t = *s++)) t++; + if (rve) + *rve = t; + return rv; + } + +/* freedtoa(s) must be used to free values s returned by dtoa + * when MULTIPLE_THREADS is #defined. It should be used in all cases, + * but for consistency with earlier versions of dtoa, it is optional + * when MULTIPLE_THREADS is not defined. + */ + + void +#ifdef KR_headers +freedtoa(s) char *s; +#else +freedtoa(char *s) +#endif +{ + Bigint *b = (Bigint *)((int *)s - 1); + b->maxwds = 1 << (b->k = *(int*)b); + Bfree(b); +#ifndef MULTIPLE_THREADS + if (s == dtoa_result) + dtoa_result = 0; +#endif + } + +/* dtoa for IEEE arithmetic (dmg): convert double to ASCII string. + * + * Inspired by "How to Print Floating-Point Numbers Accurately" by + * Guy L. Steele, Jr. and Jon L. White [Proc. ACM SIGPLAN '90, pp. 112-126]. + * + * Modifications: + * 1. Rather than iterating, we use a simple numeric overestimate + * to determine k = floor(log10(d)). We scale relevant + * quantities using O(log2(k)) rather than O(k) multiplications. + * 2. For some modes > 2 (corresponding to ecvt and fcvt), we don't + * try to generate digits strictly left to right. Instead, we + * compute with fewer bits and propagate the carry if necessary + * when rounding the final digit up. This is often faster. + * 3. Under the assumption that input will be rounded nearest, + * mode 0 renders 1e23 as 1e23 rather than 9.999999999999999e22. + * That is, we allow equality in stopping tests when the + * round-nearest rule will give the same floating-point value + * as would satisfaction of the stopping test with strict + * inequality. + * 4. We remove common factors of powers of 2 from relevant + * quantities. + * 5. When converting floating-point integers less than 1e16, + * we use floating-point arithmetic rather than resorting + * to multiple-precision integers. + * 6. When asked to produce fewer than 15 digits, we first try + * to get by with floating-point arithmetic; we resort to + * multiple-precision integer arithmetic only if we cannot + * guarantee that the floating-point calculation has given + * the correctly rounded result. For k requested digits and + * "uniformly" distributed input, the probability is + * something like 10^(k-15) that we must resort to the Long + * calculation. + */ + + char * +dtoa +#ifdef KR_headers + (dd, mode, ndigits, decpt, sign, rve) + double dd; int mode, ndigits, *decpt, *sign; char **rve; +#else + (double dd, int mode, int ndigits, int *decpt, int *sign, char **rve) +#endif +{ + /* Arguments ndigits, decpt, sign are similar to those + of ecvt and fcvt; trailing zeros are suppressed from + the returned string. If not null, *rve is set to point + to the end of the return value. If d is +-Infinity or NaN, + then *decpt is set to 9999. + + mode: + 0 ==> shortest string that yields d when read in + and rounded to nearest. + 1 ==> like 0, but with Steele & White stopping rule; + e.g. with IEEE P754 arithmetic , mode 0 gives + 1e23 whereas mode 1 gives 9.999999999999999e22. + 2 ==> max(1,ndigits) significant digits. This gives a + return value similar to that of ecvt, except + that trailing zeros are suppressed. + 3 ==> through ndigits past the decimal point. This + gives a return value similar to that from fcvt, + except that trailing zeros are suppressed, and + ndigits can be negative. + 4,5 ==> similar to 2 and 3, respectively, but (in + round-nearest mode) with the tests of mode 0 to + possibly return a shorter string that rounds to d. + With IEEE arithmetic and compilation with + -DHonor_FLT_ROUNDS, modes 4 and 5 behave the same + as modes 2 and 3 when FLT_ROUNDS != 1. + 6-9 ==> Debugging modes similar to mode - 4: don't try + fast floating-point estimate (if applicable). + + Values of mode other than 0-9 are treated as mode 0. + + Sufficient space is allocated to the return value + to hold the suppressed trailing zeros. + */ + + int bbits, b2, b5, be, dig, i, ieps, ilim, ilim0, ilim1, + j, j1, k, k0, k_check, leftright, m2, m5, s2, s5, + spec_case, try_quick; + Long L; +#ifndef Sudden_Underflow + int denorm; + ULong x; +#endif + Bigint *b, *b1, *delta, *mlo, *mhi, *S; + U d2, eps, u; + double ds; + char *s, *s0; +#ifdef SET_INEXACT + int inexact, oldinexact; +#endif +#ifdef Honor_FLT_ROUNDS /*{*/ + int Rounding; +#ifdef Trust_FLT_ROUNDS /*{{ only define this if FLT_ROUNDS really works! */ + Rounding = Flt_Rounds; +#else /*}{*/ + Rounding = 1; + switch(fegetround()) { + case FE_TOWARDZERO: Rounding = 0; break; + case FE_UPWARD: Rounding = 2; break; + case FE_DOWNWARD: Rounding = 3; + } +#endif /*}}*/ +#endif /*}*/ + +#ifndef MULTIPLE_THREADS + if (dtoa_result) { + freedtoa(dtoa_result); + dtoa_result = 0; + } +#endif + + u.d = dd; + if (word0(&u) & Sign_bit) { + /* set sign for everything, including 0's and NaNs */ + *sign = 1; + word0(&u) &= ~Sign_bit; /* clear sign bit */ + } + else + *sign = 0; + +#if defined(IEEE_Arith) + defined(VAX) +#ifdef IEEE_Arith + if ((word0(&u) & Exp_mask) == Exp_mask) +#else + if (word0(&u) == 0x8000) +#endif + { + /* Infinity or NaN */ + *decpt = 9999; +#ifdef IEEE_Arith + if (!word1(&u) && !(word0(&u) & 0xfffff)) + return nrv_alloc("Infinity", rve, 8); +#endif + return nrv_alloc("NaN", rve, 3); + } +#endif +#ifdef IBM + dval(&u) += 0; /* normalize */ +#endif + if (!dval(&u)) { + *decpt = 1; + return nrv_alloc("0", rve, 1); + } + +#ifdef SET_INEXACT + try_quick = oldinexact = get_inexact(); + inexact = 1; +#endif +#ifdef Honor_FLT_ROUNDS + if (Rounding >= 2) { + if (*sign) + Rounding = Rounding == 2 ? 0 : 2; + else + if (Rounding != 2) + Rounding = 0; + } +#endif + + b = d2b(&u, &be, &bbits); +#ifdef Sudden_Underflow + i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)); +#else + if ((i = (int)(word0(&u) >> Exp_shift1 & (Exp_mask>>Exp_shift1)))) { +#endif + dval(&d2) = dval(&u); + word0(&d2) &= Frac_mask1; + word0(&d2) |= Exp_11; +#ifdef IBM + if (j = 11 - hi0bits(word0(&d2) & Frac_mask)) + dval(&d2) /= 1 << j; +#endif + + /* log(x) ~=~ log(1.5) + (x-1.5)/1.5 + * log10(x) = log(x) / log(10) + * ~=~ log(1.5)/log(10) + (x-1.5)/(1.5*log(10)) + * log10(d) = (i-Bias)*log(2)/log(10) + log10(d2) + * + * This suggests computing an approximation k to log10(d) by + * + * k = (i - Bias)*0.301029995663981 + * + ( (d2-1.5)*0.289529654602168 + 0.176091259055681 ); + * + * We want k to be too large rather than too small. + * The error in the first-order Taylor series approximation + * is in our favor, so we just round up the constant enough + * to compensate for any error in the multiplication of + * (i - Bias) by 0.301029995663981; since |i - Bias| <= 1077, + * and 1077 * 0.30103 * 2^-52 ~=~ 7.2e-14, + * adding 1e-13 to the constant term more than suffices. + * Hence we adjust the constant term to 0.1760912590558. + * (We could get a more accurate k by invoking log10, + * but this is probably not worthwhile.) + */ + + i -= Bias; +#ifdef IBM + i <<= 2; + i += j; +#endif +#ifndef Sudden_Underflow + denorm = 0; + } + else { + /* d is denormalized */ + + i = bbits + be + (Bias + (P-1) - 1); + x = i > 32 ? word0(&u) << (64 - i) | word1(&u) >> (i - 32) + : word1(&u) << (32 - i); + dval(&d2) = x; + word0(&d2) -= 31*Exp_msk1; /* adjust exponent */ + i -= (Bias + (P-1) - 1) + 1; + denorm = 1; + } +#endif + ds = (dval(&d2)-1.5)*0.289529654602168 + 0.1760912590558 + i*0.301029995663981; + k = (int)ds; + if (ds < 0. && ds != k) + k--; /* want k = floor(ds) */ + k_check = 1; + if (k >= 0 && k <= Ten_pmax) { + if (dval(&u) < tens[k]) + k--; + k_check = 0; + } + j = bbits - i - 1; + if (j >= 0) { + b2 = 0; + s2 = j; + } + else { + b2 = -j; + s2 = 0; + } + if (k >= 0) { + b5 = 0; + s5 = k; + s2 += k; + } + else { + b2 -= k; + b5 = -k; + s5 = 0; + } + if (mode < 0 || mode > 9) + mode = 0; + +#ifndef SET_INEXACT +#ifdef Check_FLT_ROUNDS + try_quick = Rounding == 1; +#else + try_quick = 1; +#endif +#endif /*SET_INEXACT*/ + + if (mode > 5) { + mode -= 4; + try_quick = 0; + } + leftright = 1; + ilim = ilim1 = -1; /* Values for cases 0 and 1; done here to */ + /* silence erroneous "gcc -Wall" warning. */ + switch(mode) { + case 0: + case 1: + i = 18; + ndigits = 0; + break; + case 2: + leftright = 0; + /* no break */ + case 4: + if (ndigits <= 0) + ndigits = 1; + ilim = ilim1 = i = ndigits; + break; + case 3: + leftright = 0; + /* no break */ + case 5: + i = ndigits + k + 1; + ilim = i; + ilim1 = i - 1; + if (i <= 0) + i = 1; + } + s = s0 = rv_alloc(i); + +#ifdef Honor_FLT_ROUNDS + if (mode > 1 && Rounding != 1) + leftright = 0; +#endif + + if (ilim >= 0 && ilim <= Quick_max && try_quick) { + + /* Try to get by with floating-point arithmetic. */ + + i = 0; + dval(&d2) = dval(&u); + k0 = k; + ilim0 = ilim; + ieps = 2; /* conservative */ + if (k > 0) { + ds = tens[k&0xf]; + j = k >> 4; + if (j & Bletch) { + /* prevent overflows */ + j &= Bletch - 1; + dval(&u) /= bigtens[n_bigtens-1]; + ieps++; + } + for(; j; j >>= 1, i++) + if (j & 1) { + ieps++; + ds *= bigtens[i]; + } + dval(&u) /= ds; + } + else if ((j1 = -k)) { + dval(&u) *= tens[j1 & 0xf]; + for(j = j1 >> 4; j; j >>= 1, i++) + if (j & 1) { + ieps++; + dval(&u) *= bigtens[i]; + } + } + if (k_check && dval(&u) < 1. && ilim > 0) { + if (ilim1 <= 0) + goto fast_failed; + ilim = ilim1; + k--; + dval(&u) *= 10.; + ieps++; + } + dval(&eps) = ieps*dval(&u) + 7.; + word0(&eps) -= (P-1)*Exp_msk1; + if (ilim == 0) { + S = mhi = 0; + dval(&u) -= 5.; + if (dval(&u) > dval(&eps)) + goto one_digit; + if (dval(&u) < -dval(&eps)) + goto no_digits; + goto fast_failed; + } +#ifndef No_leftright + if (leftright) { + /* Use Steele & White method of only + * generating digits needed. + */ + dval(&eps) = 0.5/tens[ilim-1] - dval(&eps); + for(i = 0;;) { + L = dval(&u); + dval(&u) -= L; + *s++ = '0' + (int)L; + if (dval(&u) < dval(&eps)) + goto ret1; + if (1. - dval(&u) < dval(&eps)) + goto bump_up; + if (++i >= ilim) + break; + dval(&eps) *= 10.; + dval(&u) *= 10.; + } + } + else { +#endif + /* Generate ilim digits, then fix them up. */ + dval(&eps) *= tens[ilim-1]; + for(i = 1;; i++, dval(&u) *= 10.) { + L = (Long)(dval(&u)); + if (!(dval(&u) -= L)) + ilim = i; + *s++ = '0' + (int)L; + if (i == ilim) { + if (dval(&u) > 0.5 + dval(&eps)) + goto bump_up; + else if (dval(&u) < 0.5 - dval(&eps)) { + while(*--s == '0'); + s++; + goto ret1; + } + break; + } + } +#ifndef No_leftright + } +#endif + fast_failed: + s = s0; + dval(&u) = dval(&d2); + k = k0; + ilim = ilim0; + } + + /* Do we have a "small" integer? */ + + if (be >= 0 && k <= Int_max) { + /* Yes. */ + ds = tens[k]; + if (ndigits < 0 && ilim <= 0) { + S = mhi = 0; + if (ilim < 0 || dval(&u) <= 5*ds) + goto no_digits; + goto one_digit; + } + for(i = 1;; i++, dval(&u) *= 10.) { + L = (Long)(dval(&u) / ds); + dval(&u) -= L*ds; +#ifdef Check_FLT_ROUNDS + /* If FLT_ROUNDS == 2, L will usually be high by 1 */ + if (dval(&u) < 0) { + L--; + dval(&u) += ds; + } +#endif + *s++ = '0' + (int)L; + if (!dval(&u)) { +#ifdef SET_INEXACT + inexact = 0; +#endif + break; + } + if (i == ilim) { +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(Rounding) { + case 0: goto ret1; + case 2: goto bump_up; + } +#endif + dval(&u) += dval(&u); + if (dval(&u) > ds || (dval(&u) == ds && L & 1)) { + bump_up: + while(*--s == '9') + if (s == s0) { + k++; + *s = '0'; + break; + } + ++*s++; + } + break; + } + } + goto ret1; + } + + m2 = b2; + m5 = b5; + mhi = mlo = 0; + if (leftright) { + i = +#ifndef Sudden_Underflow + denorm ? be + (Bias + (P-1) - 1 + 1) : +#endif +#ifdef IBM + 1 + 4*P - 3 - bbits + ((bbits + be - 1) & 3); +#else + 1 + P - bbits; +#endif + b2 += i; + s2 += i; + mhi = i2b(1); + } + if (m2 > 0 && s2 > 0) { + i = m2 < s2 ? m2 : s2; + b2 -= i; + m2 -= i; + s2 -= i; + } + if (b5 > 0) { + if (leftright) { + if (m5 > 0) { + mhi = pow5mult(mhi, m5); + b1 = mult(mhi, b); + Bfree(b); + b = b1; + } + if ((j = b5 - m5)) + b = pow5mult(b, j); + } + else + b = pow5mult(b, b5); + } + S = i2b(1); + if (s5 > 0) + S = pow5mult(S, s5); + + /* Check for special case that d is a normalized power of 2. */ + + spec_case = 0; + if ((mode < 2 || leftright) +#ifdef Honor_FLT_ROUNDS + && Rounding == 1 +#endif + ) { + if (!word1(&u) && !(word0(&u) & Bndry_mask) +#ifndef Sudden_Underflow + && word0(&u) & (Exp_mask & ~Exp_msk1) +#endif + ) { + /* The special case */ + b2 += Log2P; + s2 += Log2P; + spec_case = 1; + } + } + + /* Arrange for convenient computation of quotients: + * shift left if necessary so divisor has 4 leading 0 bits. + * + * Perhaps we should just compute leading 28 bits of S once + * and for all and pass them and a shift to quorem, so it + * can do shifts and ors to compute the numerator for q. + */ +#ifdef Pack_32 + if ((i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0x1f)) + i = 32 - i; +#define iInc 28 +#else + if (i = ((s5 ? 32 - hi0bits(S->x[S->wds-1]) : 1) + s2) & 0xf) + i = 16 - i; +#define iInc 12 +#endif + i = dshift(S, s2); + b2 += i; + m2 += i; + s2 += i; + if (b2 > 0) + b = lshift(b, b2); + if (s2 > 0) + S = lshift(S, s2); + if (k_check) { + if (cmp(b,S) < 0) { + k--; + b = multadd(b, 10, 0); /* we botched the k estimate */ + if (leftright) + mhi = multadd(mhi, 10, 0); + ilim = ilim1; + } + } + if (ilim <= 0 && (mode == 3 || mode == 5)) { + if (ilim < 0 || cmp(b,S = multadd(S,5,0)) <= 0) { + /* no digits, fcvt style */ + no_digits: + k = -1 - ndigits; + goto ret; + } + one_digit: + *s++ = '1'; + k++; + goto ret; + } + if (leftright) { + if (m2 > 0) + mhi = lshift(mhi, m2); + + /* Compute mlo -- check for special case + * that d is a normalized power of 2. + */ + + mlo = mhi; + if (spec_case) { + mhi = Balloc(mhi->k); + Bcopy(mhi, mlo); + mhi = lshift(mhi, Log2P); + } + + for(i = 1;;i++) { + dig = quorem(b,S) + '0'; + /* Do we yet have the shortest decimal string + * that will round to d? + */ + j = cmp(b, mlo); + delta = diff(S, mhi); + j1 = delta->sign ? 1 : cmp(b, delta); + Bfree(delta); +#ifndef ROUND_BIASED + if (j1 == 0 && mode != 1 && !(word1(&u) & 1) +#ifdef Honor_FLT_ROUNDS + && Rounding >= 1 +#endif + ) { + if (dig == '9') + goto round_9_up; + if (j > 0) + dig++; +#ifdef SET_INEXACT + else if (!b->x[0] && b->wds <= 1) + inexact = 0; +#endif + *s++ = dig; + goto ret; + } +#endif + if (j < 0 || (j == 0 && mode != 1 +#ifndef ROUND_BIASED + && !(word1(&u) & 1) +#endif + )) { + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto accept_dig; + } +#ifdef Honor_FLT_ROUNDS + if (mode > 1) + switch(Rounding) { + case 0: goto accept_dig; + case 2: goto keep_dig; + } +#endif /*Honor_FLT_ROUNDS*/ + if (j1 > 0) { + b = lshift(b, 1); + j1 = cmp(b, S); + if ((j1 > 0 || (j1 == 0 && dig & 1)) + && dig++ == '9') + goto round_9_up; + } + accept_dig: + *s++ = dig; + goto ret; + } + if (j1 > 0) { +#ifdef Honor_FLT_ROUNDS + if (!Rounding) + goto accept_dig; +#endif + if (dig == '9') { /* possible if i == 1 */ + round_9_up: + *s++ = '9'; + goto roundoff; + } + *s++ = dig + 1; + goto ret; + } +#ifdef Honor_FLT_ROUNDS + keep_dig: +#endif + *s++ = dig; + if (i == ilim) + break; + b = multadd(b, 10, 0); + if (mlo == mhi) + mlo = mhi = multadd(mhi, 10, 0); + else { + mlo = multadd(mlo, 10, 0); + mhi = multadd(mhi, 10, 0); + } + } + } + else + for(i = 1;; i++) { + *s++ = dig = quorem(b,S) + '0'; + if (!b->x[0] && b->wds <= 1) { +#ifdef SET_INEXACT + inexact = 0; +#endif + goto ret; + } + if (i >= ilim) + break; + b = multadd(b, 10, 0); + } + + /* Round off last digit */ + +#ifdef Honor_FLT_ROUNDS + switch(Rounding) { + case 0: goto trimzeros; + case 2: goto roundoff; + } +#endif + b = lshift(b, 1); + j = cmp(b, S); + if (j > 0 || (j == 0 && dig & 1)) { + roundoff: + while(*--s == '9') + if (s == s0) { + k++; + *s++ = '1'; + goto ret; + } + ++*s++; + } + else { +#ifdef Honor_FLT_ROUNDS + trimzeros: +#endif + while(*--s == '0'); + s++; + } + ret: + Bfree(S); + if (mhi) { + if (mlo && mlo != mhi) + Bfree(mlo); + Bfree(mhi); + } + ret1: +#ifdef SET_INEXACT + if (inexact) { + if (!oldinexact) { + word0(&u) = Exp_1 + (70 << Exp_shift); + word1(&u) = 0; + dval(&u) += 1.; + } + } + else if (!oldinexact) + clear_inexact(); +#endif + Bfree(b); + *s = 0; + *decpt = k + 1; + if (rve) + *rve = s; + return s0; + } + +} // namespace dmg_fp diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/gcc_warnings.patch firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/gcc_warnings.patch --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/gcc_warnings.patch 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/gcc_warnings.patch 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,126 @@ +Index: dtoa.cc +--- dtoa.cc (old copy) ++++ dtoa.cc (working copy) +@@ -179,6 +179,9 @@ + * used for input more than STRTOD_DIGLIM digits long (default 40). + */ + ++#define IEEE_8087 ++#define NO_HEX_FP ++ + #ifndef Long + #define Long long + #endif +@@ -280,9 +283,7 @@ + #include "math.h" + #endif + +-#ifdef __cplusplus +-extern "C" { +-#endif ++namespace dmg_fp { + + #ifndef CONST + #ifdef KR_headers +@@ -511,11 +512,9 @@ + + #define Kmax 7 + +-#ifdef __cplusplus +-extern "C" double strtod(const char *s00, char **se); +-extern "C" char *dtoa(double d, int mode, int ndigits, ++double strtod(const char *s00, char **se); ++char *dtoa(double d, int mode, int ndigits, + int *decpt, int *sign, char **rve); +-#endif + + struct + Bigint { +@@ -1527,7 +1526,7 @@ + #ifdef KR_headers + (sp, t) char **sp, *t; + #else +- (CONST char **sp, char *t) ++ (CONST char **sp, CONST char *t) + #endif + { + int c, d; +@@ -2234,7 +2234,7 @@ bigcomp + nd = bc->nd; + nd0 = bc->nd0; + p5 = nd + bc->e0 - 1; +- speccase = 0; ++ dd = speccase = 0; + #ifndef Sudden_Underflow + if (rv->d == 0.) { /* special case: value near underflow-to-zero */ + /* threshold was rounded to zero */ +@@ -3431,7 +3430,7 @@ + + j = sizeof(ULong); + for(k = 0; +- sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= i; ++ sizeof(Bigint) - sizeof(ULong) - sizeof(int) + j <= (size_t)i; + j <<= 1) + k++; + r = (int*)Balloc(k); +@@ -3447,7 +3446,7 @@ + #ifdef KR_headers + nrv_alloc(s, rve, n) char *s, **rve; int n; + #else +-nrv_alloc(char *s, char **rve, int n) ++nrv_alloc(CONST char *s, char **rve, int n) + #endif + { + char *rv, *t; +@@ -4202,6 +4201,5 @@ + *rve = s; + return s0; + } +-#ifdef __cplusplus +-} +-#endif ++ ++} // namespace dmg_fp +Index: g_fmt.cc +--- g_fmt.cc (old copy) ++++ g_fmt.cc (new copy) +@@ -46,14 +46,14 @@ g_fmt(register char *b, double x) + if (sign) + *b++ = '-'; + if (decpt == 9999) /* Infinity or Nan */ { +- while(*b++ = *s++); ++ while((*b++ = *s++)); + goto done0; + } + if (decpt <= -4 || decpt > se - s + 5) { + *b++ = *s++; + if (*s) { + *b++ = '.'; +- while(*b = *s++) ++ while((*b = *s++)) + b++; + } + *b++ = 'e'; +@@ -79,10 +79,10 @@ g_fmt(register char *b, double x) + *b++ = '.'; + for(; decpt < 0; decpt++) + *b++ = '0'; +- while(*b++ = *s++); ++ while((*b++ = *s++)); + } + else { +- while(*b = *s++) { ++ while((*b = *s++)) { + b++; + if (--decpt == 0 && *s) + *b++ = '.'; +@@ -93,7 +93,9 @@ g_fmt(register char *b, double x) + } + done0: + freedtoa(s0); ++#ifdef IGNORE_ZERO_SIGN + done: ++#endif + return b0; + } + diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/g_fmt.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/g_fmt.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/g_fmt.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/g_fmt.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,102 @@ +/**************************************************************** + * + * The author of this software is David M. Gay. + * + * Copyright (c) 1991, 1996 by Lucent Technologies. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose without fee is hereby granted, provided that this entire notice + * is included in all copies of any software which is or includes a copy + * or modification of this software and in all copies of the supporting + * documentation for such software. + * + * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTY. IN PARTICULAR, NEITHER THE AUTHOR NOR LUCENT MAKES ANY + * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY + * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE. + * + ***************************************************************/ + +/* g_fmt(buf,x) stores the closest decimal approximation to x in buf; + * it suffices to declare buf + * char buf[32]; + */ + +#include "dmg_fp.h" + +namespace dmg_fp { + + char * +g_fmt(register char *b, double x) +{ + register int i, k; + register char *s; + int decpt, j, sign; + char *b0, *s0, *se; + + b0 = b; +#ifdef IGNORE_ZERO_SIGN + if (!x) { + *b++ = '0'; + *b = 0; + goto done; + } +#endif + s = s0 = dtoa(x, 0, 0, &decpt, &sign, &se); + if (sign) + *b++ = '-'; + if (decpt == 9999) /* Infinity or Nan */ { + while((*b++ = *s++)); + goto done0; + } + if (decpt <= -4 || decpt > se - s + 5) { + *b++ = *s++; + if (*s) { + *b++ = '.'; + while((*b = *s++)) + b++; + } + *b++ = 'e'; + /* sprintf(b, "%+.2d", decpt - 1); */ + if (--decpt < 0) { + *b++ = '-'; + decpt = -decpt; + } + else + *b++ = '+'; + for(j = 2, k = 10; 10*k <= decpt; j++, k *= 10); + for(;;) { + i = decpt / k; + *b++ = i + '0'; + if (--j <= 0) + break; + decpt -= i*k; + decpt *= 10; + } + *b = 0; + } + else if (decpt <= 0) { + *b++ = '.'; + for(; decpt < 0; decpt++) + *b++ = '0'; + while((*b++ = *s++)); + } + else { + while((*b = *s++)) { + b++; + if (--decpt == 0 && *s) + *b++ = '.'; + } + for(; decpt > 0; decpt--) + *b++ = '0'; + *b = 0; + } + done0: + freedtoa(s0); +#ifdef IGNORE_ZERO_SIGN + done: +#endif + return b0; + } + +} // namespace dmg_fp diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/README.chromium firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/README.chromium --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/README.chromium 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/dmg_fp/README.chromium 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,15 @@ +This directory contains David M. Gay's floating point routines. +License can be found in individual .cc files. + +Original dtoa.c file can be found at . +Original g_fmt.c file can be found at . +You may be also interested in . + +List of changes made to original code: + - wrapped functions in dmg_fp namespace + - renamed .c files to .cc + - added dmg_fp.h header + - added #define IEEE_8087 to dtoa.cc + - added #define NO_HEX_FP to dtoa.cc + - made some minor changes to allow clean compilation under g++ -Wall, see + gcc_warnings.patch. diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prcpucfg.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prcpucfg.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prcpucfg.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prcpucfg.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,43 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__ +#define BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__ + +#if defined(WIN32) +#include "base/third_party/nspr/prcpucfg_win.h" +#elif defined(__APPLE__) +#include "base/third_party/nspr/prcpucfg_mac.h" +#elif defined(__linux__) +#include "base/third_party/nspr/prcpucfg_linux.h" +#else +#error Provide a prcpucfg.h appropriate for your platform +#endif + +#endif // BASE_THIRD_PARTY_NSPR_PRCPUCFG_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prcpucfg_linux.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prcpucfg_linux.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prcpucfg_linux.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prcpucfg_linux.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,707 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape Portable Runtime (NSPR). + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#ifndef LINUX +#define LINUX +#endif + +#define PR_AF_INET6 10 /* same as AF_INET6 */ + +#ifdef __powerpc64__ + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#define IS_64 + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 8 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 8 +#define PR_BYTES_PER_DWORD 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 64 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 64 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 6 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 6 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 8 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__powerpc__) + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 4 +#define PR_BYTES_PER_DWORD 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 32 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 32 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 5 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 4 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__alpha) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#define IS_64 + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 8 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 8 +#define PR_BYTES_PER_DWORD 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 64 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 64 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 6 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 6 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 8 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__ia64__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#define IS_64 + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 8 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 8 +#define PR_BYTES_PER_DWORD 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 64 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 64 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 6 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 6 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 8 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__x86_64__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#define IS_64 + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 8 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 8 +#define PR_BYTES_PER_DWORD 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 64 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 64 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 6 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 6 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 8 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__mc68000__) + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 4 +#define PR_BYTES_PER_DWORD 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 32 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 32 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 5 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 2 +#define PR_ALIGN_OF_LONG 2 +#define PR_ALIGN_OF_INT64 2 +#define PR_ALIGN_OF_FLOAT 2 +#define PR_ALIGN_OF_DOUBLE 2 +#define PR_ALIGN_OF_POINTER 2 +#define PR_ALIGN_OF_WORD 2 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__sparc__) + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 4 +#define PR_BYTES_PER_DWORD 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 32 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 32 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 5 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 4 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__i386__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 4 +#define PR_BYTES_PER_DWORD 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 32 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 32 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 5 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 4 +#define PR_ALIGN_OF_INT64 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__mips__) + +#ifdef __MIPSEB__ +#define IS_BIG_ENDIAN 1 +#undef IS_LITTLE_ENDIAN +#elif defined(__MIPSEL__) +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#else +#error "Unknown MIPS endianness." +#endif + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 4 +#define PR_BYTES_PER_DWORD 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 32 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 32 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 5 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 4 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__arm__) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 4 +#define PR_BYTES_PER_DWORD 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 32 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 32 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 5 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 4 +#define PR_ALIGN_OF_INT64 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__hppa__) + +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 4 +#define PR_BYTES_PER_DWORD 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 32 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 32 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 5 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 4 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__s390x__) + +#define IS_BIG_ENDIAN 1 +#undef IS_LITTLE_ENDIAN +#define IS_64 + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 8 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 8 +#define PR_BYTES_PER_DWORD 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 64 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 64 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 6 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 6 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 8 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 +#define PR_ALIGN_OF_WORD 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(__s390__) + +#define IS_BIG_ENDIAN 1 +#undef IS_LITTLE_ENDIAN + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 4 +#define PR_BYTES_PER_DWORD 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 32 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 32 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 5 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 4 +#define PR_ALIGN_OF_INT64 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#else + +#error "Unknown CPU architecture" + +#endif + +#define HAVE_LONG_LONG +#if PR_ALIGN_OF_DOUBLE == 8 +#define HAVE_ALIGNED_DOUBLES +#endif +#if PR_ALIGN_OF_INT64 == 8 +#define HAVE_ALIGNED_LONGLONGS +#endif + +#ifndef NO_NSPR_10_SUPPORT + +#define BYTES_PER_BYTE PR_BYTES_PER_BYTE +#define BYTES_PER_SHORT PR_BYTES_PER_SHORT +#define BYTES_PER_INT PR_BYTES_PER_INT +#define BYTES_PER_INT64 PR_BYTES_PER_INT64 +#define BYTES_PER_LONG PR_BYTES_PER_LONG +#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT +#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE +#define BYTES_PER_WORD PR_BYTES_PER_WORD +#define BYTES_PER_DWORD PR_BYTES_PER_DWORD + +#define BITS_PER_BYTE PR_BITS_PER_BYTE +#define BITS_PER_SHORT PR_BITS_PER_SHORT +#define BITS_PER_INT PR_BITS_PER_INT +#define BITS_PER_INT64 PR_BITS_PER_INT64 +#define BITS_PER_LONG PR_BITS_PER_LONG +#define BITS_PER_FLOAT PR_BITS_PER_FLOAT +#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE +#define BITS_PER_WORD PR_BITS_PER_WORD + +#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2 +#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2 +#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2 +#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2 +#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2 +#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2 +#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2 +#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2 + +#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT +#define ALIGN_OF_INT PR_ALIGN_OF_INT +#define ALIGN_OF_LONG PR_ALIGN_OF_LONG +#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64 +#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT +#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE +#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER +#define ALIGN_OF_WORD PR_ALIGN_OF_WORD + +#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2 +#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2 +#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2 + +#endif /* NO_NSPR_10_SUPPORT */ + +#endif /* nspr_cpucfg___ */ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prcpucfg_mac.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prcpucfg_mac.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prcpucfg_mac.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prcpucfg_mac.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,145 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape Portable Runtime (NSPR). + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_UNIX +#define XP_UNIX +#endif + +#define PR_AF_INET6 30 /* same as AF_INET6 */ + +#if defined(i386) +#undef IS_BIG_ENDIAN +#define IS_LITTLE_ENDIAN 1 +#else +#undef IS_LITTLE_ENDIAN +#define IS_BIG_ENDIAN 1 +#endif + +#define HAVE_LONG_LONG +#undef HAVE_ALIGNED_DOUBLES +#define HAVE_ALIGNED_LONGLONGS 1 + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 4 +#define PR_BYTES_PER_DWORD 8 +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 32 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 32 +#define PR_BITS_PER_DWORD 64 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 5 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 4 +#define PR_ALIGN_OF_INT64 4 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 +#define PR_ALIGN_OF_WORD 4 + +#ifndef NO_NSPR_10_SUPPORT + +#define BYTES_PER_BYTE PR_BYTES_PER_BYTE +#define BYTES_PER_SHORT PR_BYTES_PER_SHORT +#define BYTES_PER_INT PR_BYTES_PER_INT +#define BYTES_PER_INT64 PR_BYTES_PER_INT64 +#define BYTES_PER_LONG PR_BYTES_PER_LONG +#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT +#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE +#define BYTES_PER_WORD PR_BYTES_PER_WORD +#define BYTES_PER_DWORD PR_BYTES_PER_DWORD + +#define BITS_PER_BYTE PR_BITS_PER_BYTE +#define BITS_PER_SHORT PR_BITS_PER_SHORT +#define BITS_PER_INT PR_BITS_PER_INT +#define BITS_PER_INT64 PR_BITS_PER_INT64 +#define BITS_PER_LONG PR_BITS_PER_LONG +#define BITS_PER_FLOAT PR_BITS_PER_FLOAT +#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE +#define BITS_PER_WORD PR_BITS_PER_WORD + +#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2 +#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2 +#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2 +#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2 +#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2 +#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2 +#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2 +#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2 + +#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT +#define ALIGN_OF_INT PR_ALIGN_OF_INT +#define ALIGN_OF_LONG PR_ALIGN_OF_LONG +#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64 +#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT +#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE +#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER +#define ALIGN_OF_WORD PR_ALIGN_OF_WORD + +#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2 +#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2 +#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2 + +#endif /* NO_NSPR_10_SUPPORT */ + +#endif /* nspr_cpucfg___ */ + diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prcpucfg_win.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prcpucfg_win.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prcpucfg_win.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prcpucfg_win.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,300 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape Portable Runtime (NSPR). + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nspr_cpucfg___ +#define nspr_cpucfg___ + +#ifndef XP_PC +#define XP_PC +#endif + +#ifndef WIN32 +#define WIN32 +#endif + +#ifndef WIN95 +#define WIN95 +#endif + +#define PR_AF_INET6 23 /* same as AF_INET6 */ + +#if defined(_M_IX86) || defined(_X86_) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_WORD 4 +#define PR_BYTES_PER_DWORD 8 +#define PR_BYTES_PER_DOUBLE 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 32 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_WORD 32 +#define PR_BITS_PER_DWORD 64 +#define PR_BITS_PER_DOUBLE 64 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_WORD_LOG2 5 +#define PR_BITS_PER_DWORD_LOG2 6 +#define PR_BITS_PER_DOUBLE_LOG2 6 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 4 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_WORD 4 +#define PR_ALIGN_OF_DWORD 8 +#define PR_ALIGN_OF_DOUBLE 4 +#define PR_ALIGN_OF_POINTER 4 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 2 + +#elif defined(_ALPHA_) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_DOUBLE 8 +#define PR_BYTES_PER_WORD 4 +#define PR_BYTES_PER_DWORD 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 32 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_DOUBLE 64 +#define PR_BITS_PER_WORD 32 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_DOUBLE_LOG2 6 +#define PR_BITS_PER_WORD_LOG2 5 + +#define PR_BYTES_PER_WORD_LOG2 2 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 4 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 4 + +#elif defined(_AMD64_) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#define IS_64 + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_WORD 8 +#define PR_BYTES_PER_DWORD 8 +#define PR_BYTES_PER_DOUBLE 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 32 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_WORD 64 +#define PR_BITS_PER_DWORD 64 +#define PR_BITS_PER_DOUBLE 64 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_WORD_LOG2 6 +#define PR_BITS_PER_DWORD_LOG2 6 +#define PR_BITS_PER_DOUBLE_LOG2 6 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 4 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_WORD 8 +#define PR_ALIGN_OF_DWORD 8 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#elif defined(_IA64_) + +#define IS_LITTLE_ENDIAN 1 +#undef IS_BIG_ENDIAN +#define IS_64 + +#define PR_BYTES_PER_BYTE 1 +#define PR_BYTES_PER_SHORT 2 +#define PR_BYTES_PER_INT 4 +#define PR_BYTES_PER_INT64 8 +#define PR_BYTES_PER_LONG 4 +#define PR_BYTES_PER_FLOAT 4 +#define PR_BYTES_PER_WORD 8 +#define PR_BYTES_PER_DWORD 8 +#define PR_BYTES_PER_DOUBLE 8 + +#define PR_BITS_PER_BYTE 8 +#define PR_BITS_PER_SHORT 16 +#define PR_BITS_PER_INT 32 +#define PR_BITS_PER_INT64 64 +#define PR_BITS_PER_LONG 32 +#define PR_BITS_PER_FLOAT 32 +#define PR_BITS_PER_WORD 64 +#define PR_BITS_PER_DWORD 64 +#define PR_BITS_PER_DOUBLE 64 + +#define PR_BITS_PER_BYTE_LOG2 3 +#define PR_BITS_PER_SHORT_LOG2 4 +#define PR_BITS_PER_INT_LOG2 5 +#define PR_BITS_PER_INT64_LOG2 6 +#define PR_BITS_PER_LONG_LOG2 5 +#define PR_BITS_PER_FLOAT_LOG2 5 +#define PR_BITS_PER_WORD_LOG2 6 +#define PR_BITS_PER_DWORD_LOG2 6 +#define PR_BITS_PER_DOUBLE_LOG2 6 + +#define PR_ALIGN_OF_SHORT 2 +#define PR_ALIGN_OF_INT 4 +#define PR_ALIGN_OF_LONG 4 +#define PR_ALIGN_OF_INT64 8 +#define PR_ALIGN_OF_FLOAT 4 +#define PR_ALIGN_OF_WORD 8 +#define PR_ALIGN_OF_DWORD 8 +#define PR_ALIGN_OF_DOUBLE 8 +#define PR_ALIGN_OF_POINTER 8 + +#define PR_BYTES_PER_WORD_LOG2 3 +#define PR_BYTES_PER_DWORD_LOG2 3 + +#else /* defined(_M_IX86) || defined(_X86_) */ + +#error unknown processor architecture + +#endif /* defined(_M_IX86) || defined(_X86_) */ + +#ifndef HAVE_LONG_LONG +#define HAVE_LONG_LONG +#endif + +#ifndef NO_NSPR_10_SUPPORT + +#define BYTES_PER_BYTE PR_BYTES_PER_BYTE +#define BYTES_PER_SHORT PR_BYTES_PER_SHORT +#define BYTES_PER_INT PR_BYTES_PER_INT +#define BYTES_PER_INT64 PR_BYTES_PER_INT64 +#define BYTES_PER_LONG PR_BYTES_PER_LONG +#define BYTES_PER_FLOAT PR_BYTES_PER_FLOAT +#define BYTES_PER_DOUBLE PR_BYTES_PER_DOUBLE +#define BYTES_PER_WORD PR_BYTES_PER_WORD +#define BYTES_PER_DWORD PR_BYTES_PER_DWORD + +#define BITS_PER_BYTE PR_BITS_PER_BYTE +#define BITS_PER_SHORT PR_BITS_PER_SHORT +#define BITS_PER_INT PR_BITS_PER_INT +#define BITS_PER_INT64 PR_BITS_PER_INT64 +#define BITS_PER_LONG PR_BITS_PER_LONG +#define BITS_PER_FLOAT PR_BITS_PER_FLOAT +#define BITS_PER_DOUBLE PR_BITS_PER_DOUBLE +#define BITS_PER_WORD PR_BITS_PER_WORD + +#define BITS_PER_BYTE_LOG2 PR_BITS_PER_BYTE_LOG2 +#define BITS_PER_SHORT_LOG2 PR_BITS_PER_SHORT_LOG2 +#define BITS_PER_INT_LOG2 PR_BITS_PER_INT_LOG2 +#define BITS_PER_INT64_LOG2 PR_BITS_PER_INT64_LOG2 +#define BITS_PER_LONG_LOG2 PR_BITS_PER_LONG_LOG2 +#define BITS_PER_FLOAT_LOG2 PR_BITS_PER_FLOAT_LOG2 +#define BITS_PER_DOUBLE_LOG2 PR_BITS_PER_DOUBLE_LOG2 +#define BITS_PER_WORD_LOG2 PR_BITS_PER_WORD_LOG2 + +#define ALIGN_OF_SHORT PR_ALIGN_OF_SHORT +#define ALIGN_OF_INT PR_ALIGN_OF_INT +#define ALIGN_OF_LONG PR_ALIGN_OF_LONG +#define ALIGN_OF_INT64 PR_ALIGN_OF_INT64 +#define ALIGN_OF_FLOAT PR_ALIGN_OF_FLOAT +#define ALIGN_OF_DOUBLE PR_ALIGN_OF_DOUBLE +#define ALIGN_OF_POINTER PR_ALIGN_OF_POINTER +#define ALIGN_OF_WORD PR_ALIGN_OF_WORD + +#define BYTES_PER_WORD_LOG2 PR_BYTES_PER_WORD_LOG2 +#define BYTES_PER_DWORD_LOG2 PR_BYTES_PER_DWORD_LOG2 +#define WORDS_PER_DWORD_LOG2 PR_WORDS_PER_DWORD_LOG2 + +#endif /* NO_NSPR_10_SUPPORT */ + +#endif /* nspr_cpucfg___ */ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prtime.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prtime.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prtime.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prtime.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,1203 @@ +/* Portions are Copyright (C) 2007 Google Inc */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape Portable Runtime (NSPR). + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * prtime.cc -- + * NOTE: The original nspr file name is prtime.c + * + * NSPR date and time functions + * + * CVS revision 3.37 + */ + +/* + * The following functions were copied from the NSPR prtime.c file. + * PR_ParseTimeString + * We inlined the new PR_ParseTimeStringToExplodedTime function to avoid + * copying PR_ExplodeTime and PR_LocalTimeParameters. (The PR_ExplodeTime + * and PR_ImplodeTime calls cancel each other out.) + * PR_NormalizeTime + * PR_GMTParameters + * PR_ImplodeTime + * This was modified to use the Win32 SYSTEMTIME/FILETIME structures + * and the timezone offsets are applied to the FILETIME structure. + * All types and macros are defined in the base/third_party/prtime.h file. + * These have been copied from the following nspr files. We have only copied + * over the types we need. + * 1. prtime.h + * 2. prtypes.h + * 3. prlong.h + */ + +#include "base/third_party/nspr/prtime.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#elif defined(OS_MACOSX) +#include +#endif +#include /* for EINVAL */ +#include + +/* Implements the Unix localtime_r() function for windows */ +#if defined(OS_WIN) +static void localtime_r(const time_t* secs, struct tm* time) { + (void) localtime_s(time, secs); +} +#endif + +/* + *------------------------------------------------------------------------ + * + * PR_ImplodeTime -- + * + * Cf. time_t mktime(struct tm *tp) + * Note that 1 year has < 2^25 seconds. So an PRInt32 is large enough. + * + *------------------------------------------------------------------------ + */ +PRTime +PR_ImplodeTime(const PRExplodedTime *exploded) +{ + // This is important, we want to make sure multiplications are + // done with the correct precision. + static const PRTime kSecondsToMicroseconds = static_cast(1000000); +#if defined(OS_WIN) + // Create the system struct representing our exploded time. + SYSTEMTIME st = {0}; + FILETIME ft = {0}; + ULARGE_INTEGER uli = {0}; + + st.wYear = exploded->tm_year; + st.wMonth = exploded->tm_month + 1; + st.wDayOfWeek = exploded->tm_wday; + st.wDay = exploded->tm_mday; + st.wHour = exploded->tm_hour; + st.wMinute = exploded->tm_min; + st.wSecond = exploded->tm_sec; + st.wMilliseconds = exploded->tm_usec/1000; + // Convert to FILETIME. + if (!SystemTimeToFileTime(&st, &ft)) { + NOTREACHED() << "Unable to convert time"; + return 0; + } + // Apply offsets. + uli.LowPart = ft.dwLowDateTime; + uli.HighPart = ft.dwHighDateTime; + // Convert from Windows epoch to NSPR epoch, and 100-nanoseconds units + // to microsecond units. + PRTime result = + static_cast((uli.QuadPart / 10) - 11644473600000000i64); + // Adjust for time zone and dst. Convert from seconds to microseconds. + result -= (exploded->tm_params.tp_gmt_offset + + exploded->tm_params.tp_dst_offset) * kSecondsToMicroseconds; + return result; +#elif defined(OS_MACOSX) + // Create the system struct representing our exploded time. + CFGregorianDate gregorian_date; + gregorian_date.year = exploded->tm_year; + gregorian_date.month = exploded->tm_month + 1; + gregorian_date.day = exploded->tm_mday; + gregorian_date.hour = exploded->tm_hour; + gregorian_date.minute = exploded->tm_min; + gregorian_date.second = exploded->tm_sec; + + // Compute |absolute_time| in seconds, correct for gmt and dst + // (note the combined offset will be negative when we need to add it), then + // convert to microseconds which is what PRTime expects. + CFAbsoluteTime absolute_time = + CFGregorianDateGetAbsoluteTime(gregorian_date, NULL); + PRTime result = static_cast(absolute_time); + result -= exploded->tm_params.tp_gmt_offset + + exploded->tm_params.tp_dst_offset; + result += kCFAbsoluteTimeIntervalSince1970; // PRTime epoch is 1970 + result *= kSecondsToMicroseconds; + result += exploded->tm_usec; + return result; +#elif defined(OS_LINUX) + struct tm exp_tm = {0}; + exp_tm.tm_sec = exploded->tm_sec; + exp_tm.tm_min = exploded->tm_min; + exp_tm.tm_hour = exploded->tm_hour; + exp_tm.tm_mday = exploded->tm_mday; + exp_tm.tm_mon = exploded->tm_month; + exp_tm.tm_year = exploded->tm_year - 1900; + + // We assume that time_t is defined as a long. + time_t absolute_time = timegm(&exp_tm); + + // If timegm returned -1. Since we don't pass it a time zone, the only + // valid case of returning -1 is 1 second before Epoch (Dec 31, 1969). + if (absolute_time == -1 && + exploded->tm_year != 1969 && exploded->tm_month != 11 && + exploded->tm_mday != 31 && exploded->tm_hour != 23 && + exploded->tm_min != 59 && exploded->tm_sec != 59) { + // Date was possibly too far in the future and would overflow. Return + // the most future date possible (year 2038). + if (exploded->tm_year >= 1970) + return static_cast(LONG_MAX) * kSecondsToMicroseconds; + // Date was possibly too far in the past and would underflow. Return + // the most past date possible (year 1901). + return static_cast(LONG_MIN) * kSecondsToMicroseconds; + } + + PRTime result = static_cast(absolute_time); + result -= exploded->tm_params.tp_gmt_offset + + exploded->tm_params.tp_dst_offset; + result *= kSecondsToMicroseconds; + result += exploded->tm_usec; + return result; +#else +#error No PR_ImplodeTime implemented on your platform. +#endif +} + +/* + * The COUNT_LEAPS macro counts the number of leap years passed by + * till the start of the given year Y. At the start of the year 4 + * A.D. the number of leap years passed by is 0, while at the start of + * the year 5 A.D. this count is 1. The number of years divisible by + * 100 but not divisible by 400 (the non-leap years) is deducted from + * the count to get the correct number of leap years. + * + * The COUNT_DAYS macro counts the number of days since 01/01/01 till the + * start of the given year Y. The number of days at the start of the year + * 1 is 0 while the number of days at the start of the year 2 is 365 + * (which is ((2)-1) * 365) and so on. The reference point is 01/01/01 + * midnight 00:00:00. + */ + +#define COUNT_LEAPS(Y) ( ((Y)-1)/4 - ((Y)-1)/100 + ((Y)-1)/400 ) +#define COUNT_DAYS(Y) ( ((Y)-1)*365 + COUNT_LEAPS(Y) ) +#define DAYS_BETWEEN_YEARS(A, B) (COUNT_DAYS(B) - COUNT_DAYS(A)) + +/* + * Static variables used by functions in this file + */ + +/* + * The following array contains the day of year for the last day of + * each month, where index 1 is January, and day 0 is January 1. + */ + +static const int lastDayOfMonth[2][13] = { + {-1, 30, 58, 89, 119, 150, 180, 211, 242, 272, 303, 333, 364}, + {-1, 30, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365} +}; + +/* + * The number of days in a month + */ + +static const PRInt8 nDays[2][12] = { + {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, + {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} +}; + +/* + *------------------------------------------------------------------------- + * + * IsLeapYear -- + * + * Returns 1 if the year is a leap year, 0 otherwise. + * + *------------------------------------------------------------------------- + */ + +static int IsLeapYear(PRInt16 year) +{ + if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) + return 1; + else + return 0; +} + +/* + * 'secOffset' should be less than 86400 (i.e., a day). + * 'time' should point to a normalized PRExplodedTime. + */ + +static void +ApplySecOffset(PRExplodedTime *time, PRInt32 secOffset) +{ + time->tm_sec += secOffset; + + /* Note that in this implementation we do not count leap seconds */ + if (time->tm_sec < 0 || time->tm_sec >= 60) { + time->tm_min += time->tm_sec / 60; + time->tm_sec %= 60; + if (time->tm_sec < 0) { + time->tm_sec += 60; + time->tm_min--; + } + } + + if (time->tm_min < 0 || time->tm_min >= 60) { + time->tm_hour += time->tm_min / 60; + time->tm_min %= 60; + if (time->tm_min < 0) { + time->tm_min += 60; + time->tm_hour--; + } + } + + if (time->tm_hour < 0) { + /* Decrement mday, yday, and wday */ + time->tm_hour += 24; + time->tm_mday--; + time->tm_yday--; + if (time->tm_mday < 1) { + time->tm_month--; + if (time->tm_month < 0) { + time->tm_month = 11; + time->tm_year--; + if (IsLeapYear(time->tm_year)) + time->tm_yday = 365; + else + time->tm_yday = 364; + } + time->tm_mday = nDays[IsLeapYear(time->tm_year)][time->tm_month]; + } + time->tm_wday--; + if (time->tm_wday < 0) + time->tm_wday = 6; + } else if (time->tm_hour > 23) { + /* Increment mday, yday, and wday */ + time->tm_hour -= 24; + time->tm_mday++; + time->tm_yday++; + if (time->tm_mday > + nDays[IsLeapYear(time->tm_year)][time->tm_month]) { + time->tm_mday = 1; + time->tm_month++; + if (time->tm_month > 11) { + time->tm_month = 0; + time->tm_year++; + time->tm_yday = 0; + } + } + time->tm_wday++; + if (time->tm_wday > 6) + time->tm_wday = 0; + } +} + +void +PR_NormalizeTime(PRExplodedTime *time, PRTimeParamFn params) +{ + int daysInMonth; + PRInt32 numDays; + + /* Get back to GMT */ + time->tm_sec -= time->tm_params.tp_gmt_offset + + time->tm_params.tp_dst_offset; + time->tm_params.tp_gmt_offset = 0; + time->tm_params.tp_dst_offset = 0; + + /* Now normalize GMT */ + + if (time->tm_usec < 0 || time->tm_usec >= 1000000) { + time->tm_sec += time->tm_usec / 1000000; + time->tm_usec %= 1000000; + if (time->tm_usec < 0) { + time->tm_usec += 1000000; + time->tm_sec--; + } + } + + /* Note that we do not count leap seconds in this implementation */ + if (time->tm_sec < 0 || time->tm_sec >= 60) { + time->tm_min += time->tm_sec / 60; + time->tm_sec %= 60; + if (time->tm_sec < 0) { + time->tm_sec += 60; + time->tm_min--; + } + } + + if (time->tm_min < 0 || time->tm_min >= 60) { + time->tm_hour += time->tm_min / 60; + time->tm_min %= 60; + if (time->tm_min < 0) { + time->tm_min += 60; + time->tm_hour--; + } + } + + if (time->tm_hour < 0 || time->tm_hour >= 24) { + time->tm_mday += time->tm_hour / 24; + time->tm_hour %= 24; + if (time->tm_hour < 0) { + time->tm_hour += 24; + time->tm_mday--; + } + } + + /* Normalize month and year before mday */ + if (time->tm_month < 0 || time->tm_month >= 12) { + time->tm_year += time->tm_month / 12; + time->tm_month %= 12; + if (time->tm_month < 0) { + time->tm_month += 12; + time->tm_year--; + } + } + + /* Now that month and year are in proper range, normalize mday */ + + if (time->tm_mday < 1) { + /* mday too small */ + do { + /* the previous month */ + time->tm_month--; + if (time->tm_month < 0) { + time->tm_month = 11; + time->tm_year--; + } + time->tm_mday += nDays[IsLeapYear(time->tm_year)][time->tm_month]; + } while (time->tm_mday < 1); + } else { + daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month]; + while (time->tm_mday > daysInMonth) { + /* mday too large */ + time->tm_mday -= daysInMonth; + time->tm_month++; + if (time->tm_month > 11) { + time->tm_month = 0; + time->tm_year++; + } + daysInMonth = nDays[IsLeapYear(time->tm_year)][time->tm_month]; + } + } + + /* Recompute yday and wday */ + time->tm_yday = time->tm_mday + + lastDayOfMonth[IsLeapYear(time->tm_year)][time->tm_month]; + + numDays = DAYS_BETWEEN_YEARS(1970, time->tm_year) + time->tm_yday; + time->tm_wday = (numDays + 4) % 7; + if (time->tm_wday < 0) { + time->tm_wday += 7; + } + + /* Recompute time parameters */ + + time->tm_params = params(time); + + ApplySecOffset(time, time->tm_params.tp_gmt_offset + + time->tm_params.tp_dst_offset); +} + +/* + *------------------------------------------------------------------------ + * + * PR_GMTParameters -- + * + * Returns the PRTimeParameters for Greenwich Mean Time. + * Trivially, both the tp_gmt_offset and tp_dst_offset fields are 0. + * + *------------------------------------------------------------------------ + */ + +PRTimeParameters +PR_GMTParameters(const PRExplodedTime *gmt) +{ +#if defined(XP_MAC) +#pragma unused (gmt) +#endif + + PRTimeParameters retVal = { 0, 0 }; + return retVal; +} + +/* + * The following code implements PR_ParseTimeString(). It is based on + * ns/lib/xp/xp_time.c, revision 1.25, by Jamie Zawinski . + */ + +/* + * We only recognize the abbreviations of a small subset of time zones + * in North America, Europe, and Japan. + * + * PST/PDT: Pacific Standard/Daylight Time + * MST/MDT: Mountain Standard/Daylight Time + * CST/CDT: Central Standard/Daylight Time + * EST/EDT: Eastern Standard/Daylight Time + * AST: Atlantic Standard Time + * NST: Newfoundland Standard Time + * GMT: Greenwich Mean Time + * BST: British Summer Time + * MET: Middle Europe Time + * EET: Eastern Europe Time + * JST: Japan Standard Time + */ + +typedef enum +{ + TT_UNKNOWN, + + TT_SUN, TT_MON, TT_TUE, TT_WED, TT_THU, TT_FRI, TT_SAT, + + TT_JAN, TT_FEB, TT_MAR, TT_APR, TT_MAY, TT_JUN, + TT_JUL, TT_AUG, TT_SEP, TT_OCT, TT_NOV, TT_DEC, + + TT_PST, TT_PDT, TT_MST, TT_MDT, TT_CST, TT_CDT, TT_EST, TT_EDT, + TT_AST, TT_NST, TT_GMT, TT_BST, TT_MET, TT_EET, TT_JST +} TIME_TOKEN; + +/* + * This parses a time/date string into a PRTime + * (microseconds after "1-Jan-1970 00:00:00 GMT"). + * It returns PR_SUCCESS on success, and PR_FAILURE + * if the time/date string can't be parsed. + * + * Many formats are handled, including: + * + * 14 Apr 89 03:20:12 + * 14 Apr 89 03:20 GMT + * Fri, 17 Mar 89 4:01:33 + * Fri, 17 Mar 89 4:01 GMT + * Mon Jan 16 16:12 PDT 1989 + * Mon Jan 16 16:12 +0130 1989 + * 6 May 1992 16:41-JST (Wednesday) + * 22-AUG-1993 10:59:12.82 + * 22-AUG-1993 10:59pm + * 22-AUG-1993 12:59am + * 22-AUG-1993 12:59 PM + * Friday, August 04, 1995 3:54 PM + * 06/21/95 04:24:34 PM + * 20/06/95 21:07 + * 95-06-08 19:32:48 EDT + * + * If the input string doesn't contain a description of the timezone, + * we consult the `default_to_gmt' to decide whether the string should + * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE). + * The correct value for this argument depends on what standard specified + * the time string which you are parsing. + */ + +PRStatus +PR_ParseTimeString( + const char *string, + PRBool default_to_gmt, + PRTime *result_imploded) +{ + PRExplodedTime tm; + PRExplodedTime *result = &tm; + TIME_TOKEN dotw = TT_UNKNOWN; + TIME_TOKEN month = TT_UNKNOWN; + TIME_TOKEN zone = TT_UNKNOWN; + int zone_offset = -1; + int dst_offset = 0; + int date = -1; + PRInt32 year = -1; + int hour = -1; + int min = -1; + int sec = -1; + + const char *rest = string; + + int iterations = 0; + + PR_ASSERT(string && result); + if (!string || !result) return PR_FAILURE; + + while (*rest) + { + + if (iterations++ > 1000) + { + return PR_FAILURE; + } + + switch (*rest) + { + case 'a': case 'A': + if (month == TT_UNKNOWN && + (rest[1] == 'p' || rest[1] == 'P') && + (rest[2] == 'r' || rest[2] == 'R')) + month = TT_APR; + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_AST; + else if (month == TT_UNKNOWN && + (rest[1] == 'u' || rest[1] == 'U') && + (rest[2] == 'g' || rest[2] == 'G')) + month = TT_AUG; + break; + case 'b': case 'B': + if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_BST; + break; + case 'c': case 'C': + if (zone == TT_UNKNOWN && + (rest[1] == 'd' || rest[1] == 'D') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_CDT; + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_CST; + break; + case 'd': case 'D': + if (month == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 'c' || rest[2] == 'C')) + month = TT_DEC; + break; + case 'e': case 'E': + if (zone == TT_UNKNOWN && + (rest[1] == 'd' || rest[1] == 'D') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_EDT; + else if (zone == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_EET; + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_EST; + break; + case 'f': case 'F': + if (month == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 'b' || rest[2] == 'B')) + month = TT_FEB; + else if (dotw == TT_UNKNOWN && + (rest[1] == 'r' || rest[1] == 'R') && + (rest[2] == 'i' || rest[2] == 'I')) + dotw = TT_FRI; + break; + case 'g': case 'G': + if (zone == TT_UNKNOWN && + (rest[1] == 'm' || rest[1] == 'M') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_GMT; + break; + case 'j': case 'J': + if (month == TT_UNKNOWN && + (rest[1] == 'a' || rest[1] == 'A') && + (rest[2] == 'n' || rest[2] == 'N')) + month = TT_JAN; + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_JST; + else if (month == TT_UNKNOWN && + (rest[1] == 'u' || rest[1] == 'U') && + (rest[2] == 'l' || rest[2] == 'L')) + month = TT_JUL; + else if (month == TT_UNKNOWN && + (rest[1] == 'u' || rest[1] == 'U') && + (rest[2] == 'n' || rest[2] == 'N')) + month = TT_JUN; + break; + case 'm': case 'M': + if (month == TT_UNKNOWN && + (rest[1] == 'a' || rest[1] == 'A') && + (rest[2] == 'r' || rest[2] == 'R')) + month = TT_MAR; + else if (month == TT_UNKNOWN && + (rest[1] == 'a' || rest[1] == 'A') && + (rest[2] == 'y' || rest[2] == 'Y')) + month = TT_MAY; + else if (zone == TT_UNKNOWN && + (rest[1] == 'd' || rest[1] == 'D') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_MDT; + else if (zone == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_MET; + else if (dotw == TT_UNKNOWN && + (rest[1] == 'o' || rest[1] == 'O') && + (rest[2] == 'n' || rest[2] == 'N')) + dotw = TT_MON; + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_MST; + break; + case 'n': case 'N': + if (month == TT_UNKNOWN && + (rest[1] == 'o' || rest[1] == 'O') && + (rest[2] == 'v' || rest[2] == 'V')) + month = TT_NOV; + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_NST; + break; + case 'o': case 'O': + if (month == TT_UNKNOWN && + (rest[1] == 'c' || rest[1] == 'C') && + (rest[2] == 't' || rest[2] == 'T')) + month = TT_OCT; + break; + case 'p': case 'P': + if (zone == TT_UNKNOWN && + (rest[1] == 'd' || rest[1] == 'D') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_PDT; + else if (zone == TT_UNKNOWN && + (rest[1] == 's' || rest[1] == 'S') && + (rest[2] == 't' || rest[2] == 'T')) + zone = TT_PST; + break; + case 's': case 'S': + if (dotw == TT_UNKNOWN && + (rest[1] == 'a' || rest[1] == 'A') && + (rest[2] == 't' || rest[2] == 'T')) + dotw = TT_SAT; + else if (month == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 'p' || rest[2] == 'P')) + month = TT_SEP; + else if (dotw == TT_UNKNOWN && + (rest[1] == 'u' || rest[1] == 'U') && + (rest[2] == 'n' || rest[2] == 'N')) + dotw = TT_SUN; + break; + case 't': case 'T': + if (dotw == TT_UNKNOWN && + (rest[1] == 'h' || rest[1] == 'H') && + (rest[2] == 'u' || rest[2] == 'U')) + dotw = TT_THU; + else if (dotw == TT_UNKNOWN && + (rest[1] == 'u' || rest[1] == 'U') && + (rest[2] == 'e' || rest[2] == 'E')) + dotw = TT_TUE; + break; + case 'u': case 'U': + if (zone == TT_UNKNOWN && + (rest[1] == 't' || rest[1] == 'T') && + !(rest[2] >= 'A' && rest[2] <= 'Z') && + !(rest[2] >= 'a' && rest[2] <= 'z')) + /* UT is the same as GMT but UTx is not. */ + zone = TT_GMT; + break; + case 'w': case 'W': + if (dotw == TT_UNKNOWN && + (rest[1] == 'e' || rest[1] == 'E') && + (rest[2] == 'd' || rest[2] == 'D')) + dotw = TT_WED; + break; + + case '+': case '-': + { + const char *end; + int sign; + if (zone_offset != -1) + { + /* already got one... */ + rest++; + break; + } + if (zone != TT_UNKNOWN && zone != TT_GMT) + { + /* GMT+0300 is legal, but PST+0300 is not. */ + rest++; + break; + } + + sign = ((*rest == '+') ? 1 : -1); + rest++; /* move over sign */ + end = rest; + while (*end >= '0' && *end <= '9') + end++; + if (rest == end) /* no digits here */ + break; + + if ((end - rest) == 4) + /* offset in HHMM */ + zone_offset = (((((rest[0]-'0')*10) + (rest[1]-'0')) * 60) + + (((rest[2]-'0')*10) + (rest[3]-'0'))); + else if ((end - rest) == 2) + /* offset in hours */ + zone_offset = (((rest[0]-'0')*10) + (rest[1]-'0')) * 60; + else if ((end - rest) == 1) + /* offset in hours */ + zone_offset = (rest[0]-'0') * 60; + else + /* 3 or >4 */ + break; + + zone_offset *= sign; + zone = TT_GMT; + break; + } + + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + { + int tmp_hour = -1; + int tmp_min = -1; + int tmp_sec = -1; + const char *end = rest + 1; + while (*end >= '0' && *end <= '9') + end++; + + /* end is now the first character after a range of digits. */ + + if (*end == ':') + { + if (hour >= 0 && min >= 0) /* already got it */ + break; + + /* We have seen "[0-9]+:", so this is probably HH:MM[:SS] */ + if ((end - rest) > 2) + /* it is [0-9][0-9][0-9]+: */ + break; + else if ((end - rest) == 2) + tmp_hour = ((rest[0]-'0')*10 + + (rest[1]-'0')); + else + tmp_hour = (rest[0]-'0'); + + /* move over the colon, and parse minutes */ + + rest = ++end; + while (*end >= '0' && *end <= '9') + end++; + + if (end == rest) + /* no digits after first colon? */ + break; + else if ((end - rest) > 2) + /* it is [0-9][0-9][0-9]+: */ + break; + else if ((end - rest) == 2) + tmp_min = ((rest[0]-'0')*10 + + (rest[1]-'0')); + else + tmp_min = (rest[0]-'0'); + + /* now go for seconds */ + rest = end; + if (*rest == ':') + rest++; + end = rest; + while (*end >= '0' && *end <= '9') + end++; + + if (end == rest) + /* no digits after second colon - that's ok. */ + ; + else if ((end - rest) > 2) + /* it is [0-9][0-9][0-9]+: */ + break; + else if ((end - rest) == 2) + tmp_sec = ((rest[0]-'0')*10 + + (rest[1]-'0')); + else + tmp_sec = (rest[0]-'0'); + + /* If we made it here, we've parsed hour and min, + and possibly sec, so it worked as a unit. */ + + /* skip over whitespace and see if there's an AM or PM + directly following the time. + */ + if (tmp_hour <= 12) + { + const char *s = end; + while (*s && (*s == ' ' || *s == '\t')) + s++; + if ((s[0] == 'p' || s[0] == 'P') && + (s[1] == 'm' || s[1] == 'M')) + /* 10:05pm == 22:05, and 12:05pm == 12:05 */ + tmp_hour = (tmp_hour == 12 ? 12 : tmp_hour + 12); + else if (tmp_hour == 12 && + (s[0] == 'a' || s[0] == 'A') && + (s[1] == 'm' || s[1] == 'M')) + /* 12:05am == 00:05 */ + tmp_hour = 0; + } + + hour = tmp_hour; + min = tmp_min; + sec = tmp_sec; + rest = end; + break; + } + else if ((*end == '/' || *end == '-') && + end[1] >= '0' && end[1] <= '9') + { + /* Perhaps this is 6/16/95, 16/6/95, 6-16-95, or 16-6-95 + or even 95-06-05... + #### But it doesn't handle 1995-06-22. + */ + int n1, n2, n3; + const char *s; + + if (month != TT_UNKNOWN) + /* if we saw a month name, this can't be. */ + break; + + s = rest; + + n1 = (*s++ - '0'); /* first 1 or 2 digits */ + if (*s >= '0' && *s <= '9') + n1 = n1*10 + (*s++ - '0'); + + if (*s != '/' && *s != '-') /* slash */ + break; + s++; + + if (*s < '0' || *s > '9') /* second 1 or 2 digits */ + break; + n2 = (*s++ - '0'); + if (*s >= '0' && *s <= '9') + n2 = n2*10 + (*s++ - '0'); + + if (*s != '/' && *s != '-') /* slash */ + break; + s++; + + if (*s < '0' || *s > '9') /* third 1, 2, 4, or 5 digits */ + break; + n3 = (*s++ - '0'); + if (*s >= '0' && *s <= '9') + n3 = n3*10 + (*s++ - '0'); + + if (*s >= '0' && *s <= '9') /* optional digits 3, 4, and 5 */ + { + n3 = n3*10 + (*s++ - '0'); + if (*s < '0' || *s > '9') + break; + n3 = n3*10 + (*s++ - '0'); + if (*s >= '0' && *s <= '9') + n3 = n3*10 + (*s++ - '0'); + } + + if ((*s >= '0' && *s <= '9') || /* followed by non-alphanum */ + (*s >= 'A' && *s <= 'Z') || + (*s >= 'a' && *s <= 'z')) + break; + + /* Ok, we parsed three 1-2 digit numbers, with / or - + between them. Now decide what the hell they are + (DD/MM/YY or MM/DD/YY or YY/MM/DD.) + */ + + if (n1 > 31 || n1 == 0) /* must be YY/MM/DD */ + { + if (n2 > 12) break; + if (n3 > 31) break; + year = n1; + if (year < 70) + year += 2000; + else if (year < 100) + year += 1900; + month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1); + date = n3; + rest = s; + break; + } + + if (n1 > 12 && n2 > 12) /* illegal */ + { + rest = s; + break; + } + + if (n3 < 70) + n3 += 2000; + else if (n3 < 100) + n3 += 1900; + + if (n1 > 12) /* must be DD/MM/YY */ + { + date = n1; + month = (TIME_TOKEN)(n2 + ((int)TT_JAN) - 1); + year = n3; + } + else /* assume MM/DD/YY */ + { + /* #### In the ambiguous case, should we consult the + locale to find out the local default? */ + month = (TIME_TOKEN)(n1 + ((int)TT_JAN) - 1); + date = n2; + year = n3; + } + rest = s; + } + else if ((*end >= 'A' && *end <= 'Z') || + (*end >= 'a' && *end <= 'z')) + /* Digits followed by non-punctuation - what's that? */ + ; + else if ((end - rest) == 5) /* five digits is a year */ + year = (year < 0 + ? ((rest[0]-'0')*10000L + + (rest[1]-'0')*1000L + + (rest[2]-'0')*100L + + (rest[3]-'0')*10L + + (rest[4]-'0')) + : year); + else if ((end - rest) == 4) /* four digits is a year */ + year = (year < 0 + ? ((rest[0]-'0')*1000L + + (rest[1]-'0')*100L + + (rest[2]-'0')*10L + + (rest[3]-'0')) + : year); + else if ((end - rest) == 2) /* two digits - date or year */ + { + int n = ((rest[0]-'0')*10 + + (rest[1]-'0')); + /* If we don't have a date (day of the month) and we see a number + less than 32, then assume that is the date. + + Otherwise, if we have a date and not a year, assume this is the + year. If it is less than 70, then assume it refers to the 21st + century. If it is two digits (>= 70), assume it refers to this + century. Otherwise, assume it refers to an unambiguous year. + + The world will surely end soon. + */ + if (date < 0 && n < 32) + date = n; + else if (year < 0) + { + if (n < 70) + year = 2000 + n; + else if (n < 100) + year = 1900 + n; + else + year = n; + } + /* else what the hell is this. */ + } + else if ((end - rest) == 1) /* one digit - date */ + date = (date < 0 ? (rest[0]-'0') : date); + /* else, three or more than five digits - what's that? */ + + break; + } + } + + /* Skip to the end of this token, whether we parsed it or not. + Tokens are delimited by whitespace, or ,;-/ + But explicitly not :+-. + */ + while (*rest && + *rest != ' ' && *rest != '\t' && + *rest != ',' && *rest != ';' && + *rest != '-' && *rest != '+' && + *rest != '/' && + *rest != '(' && *rest != ')' && *rest != '[' && *rest != ']') + rest++; + /* skip over uninteresting chars. */ + SKIP_MORE: + while (*rest && + (*rest == ' ' || *rest == '\t' || + *rest == ',' || *rest == ';' || *rest == '/' || + *rest == '(' || *rest == ')' || *rest == '[' || *rest == ']')) + rest++; + + /* "-" is ignored at the beginning of a token if we have not yet + parsed a year (e.g., the second "-" in "30-AUG-1966"), or if + the character after the dash is not a digit. */ + if (*rest == '-' && ((rest > string && isalpha(rest[-1]) && year < 0) + || rest[1] < '0' || rest[1] > '9')) + { + rest++; + goto SKIP_MORE; + } + + } + + if (zone != TT_UNKNOWN && zone_offset == -1) + { + switch (zone) + { + case TT_PST: zone_offset = -8 * 60; break; + case TT_PDT: zone_offset = -8 * 60; dst_offset = 1 * 60; break; + case TT_MST: zone_offset = -7 * 60; break; + case TT_MDT: zone_offset = -7 * 60; dst_offset = 1 * 60; break; + case TT_CST: zone_offset = -6 * 60; break; + case TT_CDT: zone_offset = -6 * 60; dst_offset = 1 * 60; break; + case TT_EST: zone_offset = -5 * 60; break; + case TT_EDT: zone_offset = -5 * 60; dst_offset = 1 * 60; break; + case TT_AST: zone_offset = -4 * 60; break; + case TT_NST: zone_offset = -3 * 60 - 30; break; + case TT_GMT: zone_offset = 0 * 60; break; + case TT_BST: zone_offset = 0 * 60; dst_offset = 1 * 60; break; + case TT_MET: zone_offset = 1 * 60; break; + case TT_EET: zone_offset = 2 * 60; break; + case TT_JST: zone_offset = 9 * 60; break; + default: + PR_ASSERT (0); + break; + } + } + + /* If we didn't find a year, month, or day-of-the-month, we can't + possibly parse this, and in fact, mktime() will do something random + (I'm seeing it return "Tue Feb 5 06:28:16 2036", which is no doubt + a numerologically significant date... */ + if (month == TT_UNKNOWN || date == -1 || year == -1 || year > PR_INT16_MAX) + return PR_FAILURE; + + memset(result, 0, sizeof(*result)); + if (sec != -1) + result->tm_sec = sec; + if (min != -1) + result->tm_min = min; + if (hour != -1) + result->tm_hour = hour; + if (date != -1) + result->tm_mday = date; + if (month != TT_UNKNOWN) + result->tm_month = (((int)month) - ((int)TT_JAN)); + if (year != -1) + result->tm_year = year; + if (dotw != TT_UNKNOWN) + result->tm_wday = (((int)dotw) - ((int)TT_SUN)); + /* + * Mainly to compute wday and yday, but normalized time is also required + * by the check below that works around a Visual C++ 2005 mktime problem. + */ + PR_NormalizeTime(result, PR_GMTParameters); + /* The remaining work is to set the gmt and dst offsets in tm_params. */ + + if (zone == TT_UNKNOWN && default_to_gmt) + { + /* No zone was specified, so pretend the zone was GMT. */ + zone = TT_GMT; + zone_offset = 0; + } + + if (zone_offset == -1) + { + /* no zone was specified, and we're to assume that everything + is local. */ + struct tm localTime; + time_t secs; + + PR_ASSERT(result->tm_month > -1 && + result->tm_mday > 0 && + result->tm_hour > -1 && + result->tm_min > -1 && + result->tm_sec > -1); + + /* + * To obtain time_t from a tm structure representing the local + * time, we call mktime(). However, we need to see if we are + * on 1-Jan-1970 or before. If we are, we can't call mktime() + * because mktime() will crash on win16. In that case, we + * calculate zone_offset based on the zone offset at + * 00:00:00, 2 Jan 1970 GMT, and subtract zone_offset from the + * date we are parsing to transform the date to GMT. We also + * do so if mktime() returns (time_t) -1 (time out of range). + */ + + /* month, day, hours, mins and secs are always non-negative + so we dont need to worry about them. */ + if(result->tm_year >= 1970) + { + PRInt64 usec_per_sec; + + localTime.tm_sec = result->tm_sec; + localTime.tm_min = result->tm_min; + localTime.tm_hour = result->tm_hour; + localTime.tm_mday = result->tm_mday; + localTime.tm_mon = result->tm_month; + localTime.tm_year = result->tm_year - 1900; + /* Set this to -1 to tell mktime "I don't care". If you set + it to 0 or 1, you are making assertions about whether the + date you are handing it is in daylight savings mode or not; + and if you're wrong, it will "fix" it for you. */ + localTime.tm_isdst = -1; + +#if _MSC_VER == 1400 /* 1400 = Visual C++ 2005 (8.0) */ + /* + * mktime will return (time_t) -1 if the input is a date + * after 23:59:59, December 31, 3000, US Pacific Time (not + * UTC as documented): + * http://msdn.microsoft.com/en-us/library/d1y53h2a(VS.80).aspx + * But if the year is 3001, mktime also invokes the invalid + * parameter handler, causing the application to crash. This + * problem has been reported in + * http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=266036. + * We avoid this crash by not calling mktime if the date is + * out of range. To use a simple test that works in any time + * zone, we consider year 3000 out of range as well. (See + * bug 480740.) + */ + if (result->tm_year >= 3000) { + /* Emulate what mktime would have done. */ + errno = EINVAL; + secs = (time_t) -1; + } else { + secs = mktime(&localTime); + } +#else + secs = mktime(&localTime); +#endif + if (secs != (time_t) -1) + { + PRTime usecs64; + LL_I2L(usecs64, secs); + LL_I2L(usec_per_sec, PR_USEC_PER_SEC); + LL_MUL(usecs64, usecs64, usec_per_sec); + *result_imploded = usecs64; + return PR_SUCCESS; + } + } + + /* So mktime() can't handle this case. We assume the + zone_offset for the date we are parsing is the same as + the zone offset on 00:00:00 2 Jan 1970 GMT. */ + secs = 86400; + localtime_r(&secs, &localTime); + zone_offset = localTime.tm_min + + 60 * localTime.tm_hour + + 1440 * (localTime.tm_mday - 2); + } + + result->tm_params.tp_gmt_offset = zone_offset * 60; + result->tm_params.tp_dst_offset = dst_offset * 60; + + *result_imploded = PR_ImplodeTime(result); + return PR_SUCCESS; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prtime.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prtime.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prtime.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prtime.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,243 @@ +/* Portions are Copyright (C) 2007 Google Inc */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape Portable Runtime (NSPR). + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + *--------------------------------------------------------------------------- + * + * prtime.h -- + * + * NSPR date and time functions + * CVS revision 3.10 + * This file contains definitions of NSPR's basic types required by + * prtime.cc. These types have been copied over from the following NSPR + * files prtime.h, prtypes.h(CVS revision 3.35), prlong.h(CVS revision 3.13) + * + *--------------------------------------------------------------------------- + */ + +#ifndef BASE_PRTIME_H__ +#define BASE_PRTIME_H__ + +#include "base/logging.h" +#include "base/third_party/nspr/prtypes.h" + +#ifdef CHROMIUM_MOZILLA_BUILD +PR_BEGIN_EXTERN_C +#endif + +#ifndef CHROMIUM_MOZILLA_BUILD +#define PR_ASSERT DCHECK +#endif + +#define LL_I2L(l, i) ((l) = (PRInt64)(i)) +#define LL_MUL(r, a, b) ((r) = (a) * (b)) + +/**********************************************************************/ +/************************* TYPES AND CONSTANTS ************************/ +/**********************************************************************/ + +#define PR_MSEC_PER_SEC 1000UL +#define PR_USEC_PER_SEC 1000000UL +#define PR_NSEC_PER_SEC 1000000000UL +#define PR_USEC_PER_MSEC 1000UL +#define PR_NSEC_PER_MSEC 1000000UL + +/* + * PRTime -- + * + * NSPR represents basic time as 64-bit signed integers relative + * to midnight (00:00:00), January 1, 1970 Greenwich Mean Time (GMT). + * (GMT is also known as Coordinated Universal Time, UTC.) + * The units of time are in microseconds. Negative times are allowed + * to represent times prior to the January 1970 epoch. Such values are + * intended to be exported to other systems or converted to human + * readable form. + * + * Notes on porting: PRTime corresponds to time_t in ANSI C. NSPR 1.0 + * simply uses PRInt64. + */ + +typedef PRInt64 PRTime; + +/* + * Time zone and daylight saving time corrections applied to GMT to + * obtain the local time of some geographic location + */ + +typedef struct PRTimeParameters { + PRInt32 tp_gmt_offset; /* the offset from GMT in seconds */ + PRInt32 tp_dst_offset; /* contribution of DST in seconds */ +} PRTimeParameters; + +/* + * PRExplodedTime -- + * + * Time broken down into human-readable components such as year, month, + * day, hour, minute, second, and microsecond. Time zone and daylight + * saving time corrections may be applied. If they are applied, the + * offsets from the GMT must be saved in the 'tm_params' field so that + * all the information is available to reconstruct GMT. + * + * Notes on porting: PRExplodedTime corrresponds to struct tm in + * ANSI C, with the following differences: + * - an additional field tm_usec; + * - replacing tm_isdst by tm_params; + * - the month field is spelled tm_month, not tm_mon; + * - we use absolute year, AD, not the year since 1900. + * The corresponding type in NSPR 1.0 is called PRTime. Below is + * a table of date/time type correspondence in the three APIs: + * API time since epoch time in components + * ANSI C time_t struct tm + * NSPR 1.0 PRInt64 PRTime + * NSPR 2.0 PRTime PRExplodedTime + */ + +typedef struct PRExplodedTime { + PRInt32 tm_usec; /* microseconds past tm_sec (0-99999) */ + PRInt32 tm_sec; /* seconds past tm_min (0-61, accomodating + up to two leap seconds) */ + PRInt32 tm_min; /* minutes past tm_hour (0-59) */ + PRInt32 tm_hour; /* hours past tm_day (0-23) */ + PRInt32 tm_mday; /* days past tm_mon (1-31, note that it + starts from 1) */ + PRInt32 tm_month; /* months past tm_year (0-11, Jan = 0) */ + PRInt16 tm_year; /* absolute year, AD (note that we do not + count from 1900) */ + + PRInt8 tm_wday; /* calculated day of the week + (0-6, Sun = 0) */ + PRInt16 tm_yday; /* calculated day of the year + (0-365, Jan 1 = 0) */ + + PRTimeParameters tm_params; /* time parameters used by conversion */ +} PRExplodedTime; + +/* + * PRTimeParamFn -- + * + * A function of PRTimeParamFn type returns the time zone and + * daylight saving time corrections for some geographic location, + * given the current time in GMT. The input argument gmt should + * point to a PRExplodedTime that is in GMT, i.e., whose + * tm_params contains all 0's. + * + * For any time zone other than GMT, the computation is intended to + * consist of two steps: + * - Figure out the time zone correction, tp_gmt_offset. This number + * usually depends on the geographic location only. But it may + * also depend on the current time. For example, all of China + * is one time zone right now. But this situation may change + * in the future. + * - Figure out the daylight saving time correction, tp_dst_offset. + * This number depends on both the geographic location and the + * current time. Most of the DST rules are expressed in local + * current time. If so, one should apply the time zone correction + * to GMT before applying the DST rules. + */ + +typedef PRTimeParameters (PR_CALLBACK *PRTimeParamFn)(const PRExplodedTime *gmt); + +/**********************************************************************/ +/****************************** FUNCTIONS *****************************/ +/**********************************************************************/ + +NSPR_API(PRTime) +PR_ImplodeTime(const PRExplodedTime *exploded); + +/* + * Adjust exploded time to normalize field overflows after manipulation. + * Note that the following fields of PRExplodedTime should not be + * manipulated: + * - tm_month and tm_year: because the number of days in a month and + * number of days in a year are not constant, it is ambiguous to + * manipulate the month and year fields, although one may be tempted + * to. For example, what does "a month from January 31st" mean? + * - tm_wday and tm_yday: these fields are calculated by NSPR. Users + * should treat them as "read-only". + */ + +NSPR_API(void) PR_NormalizeTime( + PRExplodedTime *exploded, PRTimeParamFn params); + +/**********************************************************************/ +/*********************** TIME PARAMETER FUNCTIONS *********************/ +/**********************************************************************/ + +/* Time parameters that represent Greenwich Mean Time */ +NSPR_API(PRTimeParameters) PR_GMTParameters(const PRExplodedTime *gmt); + +/* + * This parses a time/date string into a PRTime + * (microseconds after "1-Jan-1970 00:00:00 GMT"). + * It returns PR_SUCCESS on success, and PR_FAILURE + * if the time/date string can't be parsed. + * + * Many formats are handled, including: + * + * 14 Apr 89 03:20:12 + * 14 Apr 89 03:20 GMT + * Fri, 17 Mar 89 4:01:33 + * Fri, 17 Mar 89 4:01 GMT + * Mon Jan 16 16:12 PDT 1989 + * Mon Jan 16 16:12 +0130 1989 + * 6 May 1992 16:41-JST (Wednesday) + * 22-AUG-1993 10:59:12.82 + * 22-AUG-1993 10:59pm + * 22-AUG-1993 12:59am + * 22-AUG-1993 12:59 PM + * Friday, August 04, 1995 3:54 PM + * 06/21/95 04:24:34 PM + * 20/06/95 21:07 + * 95-06-08 19:32:48 EDT + * + * If the input string doesn't contain a description of the timezone, + * we consult the `default_to_gmt' to decide whether the string should + * be interpreted relative to the local time zone (PR_FALSE) or GMT (PR_TRUE). + * The correct value for this argument depends on what standard specified + * the time string which you are parsing. + */ + +NSPR_API(PRStatus) PR_ParseTimeString ( + const char *string, + PRBool default_to_gmt, + PRTime *result); + +#ifdef CHROMIUM_MOZILLA_BUILD +PR_END_EXTERN_C +#endif + +#endif // BASE_PRTIME_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prtypes.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prtypes.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prtypes.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/prtypes.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,575 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape Portable Runtime (NSPR). + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* +** File: prtypes.h +** Description: Definitions of NSPR's basic types +** +** Prototypes and macros used to make up for deficiencies that we have found +** in ANSI environments. +** +** Since we do not wrap and all the other standard headers, authors +** of portable code will not know in general that they need these definitions. +** Instead of requiring these authors to find the dependent uses in their code +** and take the following steps only in those C files, we take steps once here +** for all C files. +**/ + +#ifndef prtypes_h___ +#define prtypes_h___ + +#include "build/build_config.h" + +#ifdef OS_WIN +// This files assumes windows.h has been included first since it expects _X86_ +// or _AMD64_ to be defined. +#include +#endif // OS_WIN + +#ifdef MDCPUCFG +#include MDCPUCFG +#else +#include "base/third_party/nspr/prcpucfg.h" +#endif + +#include + +/*********************************************************************** +** MACROS: PR_EXTERN +** PR_IMPLEMENT +** DESCRIPTION: +** These are only for externally visible routines and globals. For +** internal routines, just use "extern" for type checking and that +** will not export internal cross-file or forward-declared symbols. +** Define a macro for declaring procedures return types. We use this to +** deal with windoze specific type hackery for DLL definitions. Use +** PR_EXTERN when the prototype for the method is declared. Use +** PR_IMPLEMENT for the implementation of the method. +** +** Example: +** in dowhim.h +** PR_EXTERN( void ) DoWhatIMean( void ); +** in dowhim.c +** PR_IMPLEMENT( void ) DoWhatIMean( void ) { return; } +** +** +***********************************************************************/ +#if defined(WIN32) + +#define PR_EXPORT(__type) extern __declspec(dllexport) __type +#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type +#define PR_IMPORT(__type) extern __type +#define PR_IMPORT_DATA(__type) __declspec(dllimport) __type + +#define PR_EXTERN(__type) extern __declspec(dllexport) __type +#define PR_IMPLEMENT(__type) __type +#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type +#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type + +#define PR_CALLBACK +#define PR_CALLBACK_DECL +#define PR_STATIC_CALLBACK(__x) static __x + +#elif defined(XP_BEOS) + +#define PR_EXPORT(__type) extern __declspec(dllexport) __type +#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type +#define PR_IMPORT(__type) extern __declspec(dllexport) __type +#define PR_IMPORT_DATA(__type) extern __declspec(dllexport) __type + +#define PR_EXTERN(__type) extern __declspec(dllexport) __type +#define PR_IMPLEMENT(__type) __declspec(dllexport) __type +#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type +#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type + +#define PR_CALLBACK +#define PR_CALLBACK_DECL +#define PR_STATIC_CALLBACK(__x) static __x + +#elif defined(WIN16) + +#define PR_CALLBACK_DECL __cdecl + +#if defined(_WINDLL) +#define PR_EXPORT(__type) extern __type _cdecl _export _loadds +#define PR_IMPORT(__type) extern __type _cdecl _export _loadds +#define PR_EXPORT_DATA(__type) extern __type _export +#define PR_IMPORT_DATA(__type) extern __type _export + +#define PR_EXTERN(__type) extern __type _cdecl _export _loadds +#define PR_IMPLEMENT(__type) __type _cdecl _export _loadds +#define PR_EXTERN_DATA(__type) extern __type _export +#define PR_IMPLEMENT_DATA(__type) __type _export + +#define PR_CALLBACK __cdecl __loadds +#define PR_STATIC_CALLBACK(__x) static __x PR_CALLBACK + +#else /* this must be .EXE */ +#define PR_EXPORT(__type) extern __type _cdecl _export +#define PR_IMPORT(__type) extern __type _cdecl _export +#define PR_EXPORT_DATA(__type) extern __type _export +#define PR_IMPORT_DATA(__type) extern __type _export + +#define PR_EXTERN(__type) extern __type _cdecl _export +#define PR_IMPLEMENT(__type) __type _cdecl _export +#define PR_EXTERN_DATA(__type) extern __type _export +#define PR_IMPLEMENT_DATA(__type) __type _export + +#define PR_CALLBACK __cdecl __loadds +#define PR_STATIC_CALLBACK(__x) __x PR_CALLBACK +#endif /* _WINDLL */ + +#elif defined(XP_MAC) + +#define PR_EXPORT(__type) extern __declspec(export) __type +#define PR_EXPORT_DATA(__type) extern __declspec(export) __type +#define PR_IMPORT(__type) extern __declspec(export) __type +#define PR_IMPORT_DATA(__type) extern __declspec(export) __type + +#define PR_EXTERN(__type) extern __declspec(export) __type +#define PR_IMPLEMENT(__type) __declspec(export) __type +#define PR_EXTERN_DATA(__type) extern __declspec(export) __type +#define PR_IMPLEMENT_DATA(__type) __declspec(export) __type + +#define PR_CALLBACK +#define PR_CALLBACK_DECL +#define PR_STATIC_CALLBACK(__x) static __x + +#elif defined(XP_OS2) && defined(__declspec) + +#define PR_EXPORT(__type) extern __declspec(dllexport) __type +#define PR_EXPORT_DATA(__type) extern __declspec(dllexport) __type +#define PR_IMPORT(__type) extern __declspec(dllimport) __type +#define PR_IMPORT_DATA(__type) extern __declspec(dllimport) __type + +#define PR_EXTERN(__type) extern __declspec(dllexport) __type +#define PR_IMPLEMENT(__type) __declspec(dllexport) __type +#define PR_EXTERN_DATA(__type) extern __declspec(dllexport) __type +#define PR_IMPLEMENT_DATA(__type) __declspec(dllexport) __type + +#define PR_CALLBACK +#define PR_CALLBACK_DECL +#define PR_STATIC_CALLBACK(__x) static __x + +#elif defined(XP_OS2_VACPP) + +#define PR_EXPORT(__type) extern __type +#define PR_EXPORT_DATA(__type) extern __type +#define PR_IMPORT(__type) extern __type +#define PR_IMPORT_DATA(__type) extern __type + +#define PR_EXTERN(__type) extern __type +#define PR_IMPLEMENT(__type) __type +#define PR_EXTERN_DATA(__type) extern __type +#define PR_IMPLEMENT_DATA(__type) __type +#define PR_CALLBACK _Optlink +#define PR_CALLBACK_DECL +#define PR_STATIC_CALLBACK(__x) static __x PR_CALLBACK + +#else /* Unix */ + +/* GCC 3.3 and later support the visibility attribute. */ +#if (__GNUC__ >= 4) || \ + (__GNUC__ == 3 && __GNUC_MINOR__ >= 3) +#define PR_VISIBILITY_DEFAULT __attribute__((visibility("default"))) +#else +#define PR_VISIBILITY_DEFAULT +#endif + +#define PR_EXPORT(__type) extern PR_VISIBILITY_DEFAULT __type +#define PR_EXPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type +#define PR_IMPORT(__type) extern PR_VISIBILITY_DEFAULT __type +#define PR_IMPORT_DATA(__type) extern PR_VISIBILITY_DEFAULT __type + +#define PR_EXTERN(__type) extern PR_VISIBILITY_DEFAULT __type +#define PR_IMPLEMENT(__type) PR_VISIBILITY_DEFAULT __type +#define PR_EXTERN_DATA(__type) extern PR_VISIBILITY_DEFAULT __type +#define PR_IMPLEMENT_DATA(__type) PR_VISIBILITY_DEFAULT __type +#define PR_CALLBACK +#define PR_CALLBACK_DECL +#define PR_STATIC_CALLBACK(__x) static __x + +#endif + +#if defined(_NSPR_BUILD_) +#define NSPR_API(__type) PR_EXPORT(__type) +#define NSPR_DATA_API(__type) PR_EXPORT_DATA(__type) +#else +#define NSPR_API(__type) PR_IMPORT(__type) +#define NSPR_DATA_API(__type) PR_IMPORT_DATA(__type) +#endif + +/*********************************************************************** +** MACROS: PR_BEGIN_MACRO +** PR_END_MACRO +** DESCRIPTION: +** Macro body brackets so that macros with compound statement definitions +** behave syntactically more like functions when called. +***********************************************************************/ +#define PR_BEGIN_MACRO do { +#define PR_END_MACRO } while (0) + +/*********************************************************************** +** MACROS: PR_BEGIN_EXTERN_C +** PR_END_EXTERN_C +** DESCRIPTION: +** Macro shorthands for conditional C++ extern block delimiters. +***********************************************************************/ +#ifdef __cplusplus +#define PR_BEGIN_EXTERN_C extern "C" { +#define PR_END_EXTERN_C } +#else +#define PR_BEGIN_EXTERN_C +#define PR_END_EXTERN_C +#endif + +/*********************************************************************** +** MACROS: PR_BIT +** PR_BITMASK +** DESCRIPTION: +** Bit masking macros. XXX n must be <= 31 to be portable +***********************************************************************/ +#define PR_BIT(n) ((PRUint32)1 << (n)) +#define PR_BITMASK(n) (PR_BIT(n) - 1) + +/*********************************************************************** +** MACROS: PR_ROUNDUP +** PR_MIN +** PR_MAX +** PR_ABS +** DESCRIPTION: +** Commonly used macros for operations on compatible types. +***********************************************************************/ +#define PR_ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y)) +#define PR_MIN(x,y) ((x)<(y)?(x):(y)) +#define PR_MAX(x,y) ((x)>(y)?(x):(y)) +#define PR_ABS(x) ((x)<0?-(x):(x)) + +PR_BEGIN_EXTERN_C + +/************************************************************************ +** TYPES: PRUint8 +** PRInt8 +** DESCRIPTION: +** The int8 types are known to be 8 bits each. There is no type that +** is equivalent to a plain "char". +************************************************************************/ +#if PR_BYTES_PER_BYTE == 1 +typedef unsigned char PRUint8; +/* +** Some cfront-based C++ compilers do not like 'signed char' and +** issue the warning message: +** warning: "signed" not implemented (ignored) +** For these compilers, we have to define PRInt8 as plain 'char'. +** Make sure that plain 'char' is indeed signed under these compilers. +*/ +#if (defined(HPUX) && defined(__cplusplus) \ + && !defined(__GNUC__) && __cplusplus < 199707L) \ + || (defined(SCO) && defined(__cplusplus) \ + && !defined(__GNUC__) && __cplusplus == 1L) +typedef char PRInt8; +#else +typedef signed char PRInt8; +#endif +#else +#error No suitable type for PRInt8/PRUint8 +#endif + +/************************************************************************ + * MACROS: PR_INT8_MAX + * PR_INT8_MIN + * PR_UINT8_MAX + * DESCRIPTION: + * The maximum and minimum values of a PRInt8 or PRUint8. +************************************************************************/ + +#define PR_INT8_MAX 127 +#define PR_INT8_MIN (-128) +#define PR_UINT8_MAX 255U + +/************************************************************************ +** TYPES: PRUint16 +** PRInt16 +** DESCRIPTION: +** The int16 types are known to be 16 bits each. +************************************************************************/ +#if PR_BYTES_PER_SHORT == 2 +typedef unsigned short PRUint16; +typedef short PRInt16; +#else +#error No suitable type for PRInt16/PRUint16 +#endif + +/************************************************************************ + * MACROS: PR_INT16_MAX + * PR_INT16_MIN + * PR_UINT16_MAX + * DESCRIPTION: + * The maximum and minimum values of a PRInt16 or PRUint16. +************************************************************************/ + +#define PR_INT16_MAX 32767 +#define PR_INT16_MIN (-32768) +#define PR_UINT16_MAX 65535U + +/************************************************************************ +** TYPES: PRUint32 +** PRInt32 +** DESCRIPTION: +** The int32 types are known to be 32 bits each. +************************************************************************/ +#if PR_BYTES_PER_INT == 4 +typedef unsigned int PRUint32; +typedef int PRInt32; +#define PR_INT32(x) x +#define PR_UINT32(x) x ## U +#elif PR_BYTES_PER_LONG == 4 +typedef unsigned long PRUint32; +typedef long PRInt32; +#define PR_INT32(x) x ## L +#define PR_UINT32(x) x ## UL +#else +#error No suitable type for PRInt32/PRUint32 +#endif + +/************************************************************************ + * MACROS: PR_INT32_MAX + * PR_INT32_MIN + * PR_UINT32_MAX + * DESCRIPTION: + * The maximum and minimum values of a PRInt32 or PRUint32. +************************************************************************/ + +#define PR_INT32_MAX PR_INT32(2147483647) +#define PR_INT32_MIN (-PR_INT32_MAX - 1) +#define PR_UINT32_MAX PR_UINT32(4294967295) + +/************************************************************************ +** TYPES: PRUint64 +** PRInt64 +** DESCRIPTION: +** The int64 types are known to be 64 bits each. Care must be used when +** declaring variables of type PRUint64 or PRInt64. Different hardware +** architectures and even different compilers have varying support for +** 64 bit values. The only guaranteed portability requires the use of +** the LL_ macros (see prlong.h). +************************************************************************/ +#ifdef HAVE_LONG_LONG +#if PR_BYTES_PER_LONG == 8 +typedef long PRInt64; +typedef unsigned long PRUint64; +#elif defined(WIN16) +typedef __int64 PRInt64; +typedef unsigned __int64 PRUint64; +#elif defined(WIN32) && !defined(__GNUC__) +typedef __int64 PRInt64; +typedef unsigned __int64 PRUint64; +#else +typedef long long PRInt64; +typedef unsigned long long PRUint64; +#endif /* PR_BYTES_PER_LONG == 8 */ +#else /* !HAVE_LONG_LONG */ +typedef struct { +#ifdef IS_LITTLE_ENDIAN + PRUint32 lo, hi; +#else + PRUint32 hi, lo; +#endif +} PRInt64; +typedef PRInt64 PRUint64; +#endif /* !HAVE_LONG_LONG */ + +/************************************************************************ +** TYPES: PRUintn +** PRIntn +** DESCRIPTION: +** The PRIntn types are most appropriate for automatic variables. They are +** guaranteed to be at least 16 bits, though various architectures may +** define them to be wider (e.g., 32 or even 64 bits). These types are +** never valid for fields of a structure. +************************************************************************/ +#if PR_BYTES_PER_INT >= 2 +typedef int PRIntn; +typedef unsigned int PRUintn; +#else +#error 'sizeof(int)' not sufficient for platform use +#endif + +/************************************************************************ +** TYPES: PRFloat64 +** DESCRIPTION: +** NSPR's floating point type is always 64 bits. +************************************************************************/ +typedef double PRFloat64; + +/************************************************************************ +** TYPES: PRSize +** DESCRIPTION: +** A type for representing the size of objects. +************************************************************************/ +typedef size_t PRSize; + + +/************************************************************************ +** TYPES: PROffset32, PROffset64 +** DESCRIPTION: +** A type for representing byte offsets from some location. +************************************************************************/ +typedef PRInt32 PROffset32; +typedef PRInt64 PROffset64; + +/************************************************************************ +** TYPES: PRPtrDiff +** DESCRIPTION: +** A type for pointer difference. Variables of this type are suitable +** for storing a pointer or pointer subtraction. +************************************************************************/ +typedef ptrdiff_t PRPtrdiff; + +/************************************************************************ +** TYPES: PRUptrdiff +** DESCRIPTION: +** A type for pointer difference. Variables of this type are suitable +** for storing a pointer or pointer sutraction. +************************************************************************/ +#ifdef _WIN64 +typedef unsigned __int64 PRUptrdiff; +#else +typedef unsigned long PRUptrdiff; +#endif + +/************************************************************************ +** TYPES: PRBool +** DESCRIPTION: +** Use PRBool for variables and parameter types. Use PR_FALSE and PR_TRUE +** for clarity of target type in assignments and actual arguments. Use +** 'if (bool)', 'while (!bool)', '(bool) ? x : y' etc., to test booleans +** just as you would C int-valued conditions. +************************************************************************/ +typedef PRIntn PRBool; +#define PR_TRUE 1 +#define PR_FALSE 0 + +/************************************************************************ +** TYPES: PRPackedBool +** DESCRIPTION: +** Use PRPackedBool within structs where bitfields are not desirable +** but minimum and consistant overhead matters. +************************************************************************/ +typedef PRUint8 PRPackedBool; + +/* +** Status code used by some routines that have a single point of failure or +** special status return. +*/ +typedef enum { PR_FAILURE = -1, PR_SUCCESS = 0 } PRStatus; + +#ifndef __PRUNICHAR__ +#define __PRUNICHAR__ +#if defined(WIN32) || defined(XP_MAC) +typedef wchar_t PRUnichar; +#else +typedef PRUint16 PRUnichar; +#endif +#endif + +/* +** WARNING: The undocumented data types PRWord and PRUword are +** only used in the garbage collection and arena code. Do not +** use PRWord and PRUword in new code. +** +** A PRWord is an integer that is the same size as a void*. +** It implements the notion of a "word" in the Java Virtual +** Machine. (See Sec. 3.4 "Words", The Java Virtual Machine +** Specification, Addison-Wesley, September 1996. +** http://java.sun.com/docs/books/vmspec/index.html.) +*/ +#ifdef _WIN64 +typedef __int64 PRWord; +typedef unsigned __int64 PRUword; +#else +typedef long PRWord; +typedef unsigned long PRUword; +#endif + +#if defined(NO_NSPR_10_SUPPORT) +#else +/********* ???????????????? FIX ME ??????????????????????????? *****/ +/********************** Some old definitions until pr=>ds transition is done ***/ +/********************** Also, we are still using NSPR 1.0. GC ******************/ +/* +** Fundamental NSPR macros, used nearly everywhere. +*/ + +#define PR_PUBLIC_API PR_IMPLEMENT + +/* +** Macro body brackets so that macros with compound statement definitions +** behave syntactically more like functions when called. +*/ +#define NSPR_BEGIN_MACRO do { +#define NSPR_END_MACRO } while (0) + +/* +** Macro shorthands for conditional C++ extern block delimiters. +*/ +#ifdef NSPR_BEGIN_EXTERN_C +#undef NSPR_BEGIN_EXTERN_C +#endif +#ifdef NSPR_END_EXTERN_C +#undef NSPR_END_EXTERN_C +#endif + +#ifdef __cplusplus +#define NSPR_BEGIN_EXTERN_C extern "C" { +#define NSPR_END_EXTERN_C } +#else +#define NSPR_BEGIN_EXTERN_C +#define NSPR_END_EXTERN_C +#endif + +/********* ????????????? End Fix me ?????????????????????????????? *****/ +#endif /* NO_NSPR_10_SUPPORT */ + +PR_END_EXTERN_C + +#if !defined(NO_NSPR_10_SUPPORT) +#include "base/basictypes.h" +#endif + +#endif /* prtypes_h___ */ + diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/README.chromium firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/README.chromium --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/README.chromium 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nspr/README.chromium 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,2 @@ +The original code is the Netscape Portable Runtime (NSPR), licensed under +the MPL/GPL/LGPL tri-license (http://www.mozilla.org/MPL/). diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/blapi.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/blapi.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/blapi.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/blapi.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,101 @@ +/* + * crypto.h - public data structures and prototypes for the crypto library + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dr Vipul Gupta , Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* $Id: blapi.h,v 1.27 2007/11/09 18:49:32 wtc%google.com Exp $ */ + +#ifndef _BLAPI_H_ +#define _BLAPI_H_ + +#include "base/third_party/nss/blapit.h" + +/******************************************/ + +extern SHA256Context *SHA256_NewContext(void); +extern void SHA256_DestroyContext(SHA256Context *cx, PRBool freeit); +extern void SHA256_Begin(SHA256Context *cx); +extern void SHA256_Update(SHA256Context *cx, const unsigned char *input, + unsigned int inputLen); +extern void SHA256_End(SHA256Context *cx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen); +extern SECStatus SHA256_HashBuf(unsigned char *dest, const unsigned char *src, + uint32 src_length); +extern SECStatus SHA256_Hash(unsigned char *dest, const char *src); +extern void SHA256_TraceState(SHA256Context *cx); +extern unsigned int SHA256_FlattenSize(SHA256Context *cx); +extern SECStatus SHA256_Flatten(SHA256Context *cx,unsigned char *space); +extern SHA256Context * SHA256_Resurrect(unsigned char *space, void *arg); +extern void SHA256_Clone(SHA256Context *dest, SHA256Context *src); + +/******************************************/ + +extern SHA512Context *SHA512_NewContext(void); +extern void SHA512_DestroyContext(SHA512Context *cx, PRBool freeit); +extern void SHA512_Begin(SHA512Context *cx); +extern void SHA512_Update(SHA512Context *cx, const unsigned char *input, + unsigned int inputLen); +extern void SHA512_End(SHA512Context *cx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen); +extern SECStatus SHA512_HashBuf(unsigned char *dest, const unsigned char *src, + uint32 src_length); +extern SECStatus SHA512_Hash(unsigned char *dest, const char *src); +extern void SHA512_TraceState(SHA512Context *cx); +extern unsigned int SHA512_FlattenSize(SHA512Context *cx); +extern SECStatus SHA512_Flatten(SHA512Context *cx,unsigned char *space); +extern SHA512Context * SHA512_Resurrect(unsigned char *space, void *arg); +extern void SHA512_Clone(SHA512Context *dest, SHA512Context *src); + +/******************************************/ + +extern SHA384Context *SHA384_NewContext(void); +extern void SHA384_DestroyContext(SHA384Context *cx, PRBool freeit); +extern void SHA384_Begin(SHA384Context *cx); +extern void SHA384_Update(SHA384Context *cx, const unsigned char *input, + unsigned int inputLen); +extern void SHA384_End(SHA384Context *cx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen); +extern SECStatus SHA384_HashBuf(unsigned char *dest, const unsigned char *src, + uint32 src_length); +extern SECStatus SHA384_Hash(unsigned char *dest, const char *src); +extern void SHA384_TraceState(SHA384Context *cx); +extern unsigned int SHA384_FlattenSize(SHA384Context *cx); +extern SECStatus SHA384_Flatten(SHA384Context *cx,unsigned char *space); +extern SHA384Context * SHA384_Resurrect(unsigned char *space, void *arg); +extern void SHA384_Clone(SHA384Context *dest, SHA384Context *src); + +#endif /* _BLAPI_H_ */ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/blapit.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/blapit.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/blapit.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/blapit.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,91 @@ +/* + * blapit.h - public data structures for the crypto library + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dr Vipul Gupta and + * Douglas Stebila , Sun Microsystems Laboratories + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* $Id: blapit.h,v 1.20 2007/02/28 19:47:37 rrelyea%redhat.com Exp $ */ + +#ifndef _BLAPIT_H_ +#define _BLAPIT_H_ + +#include "base/third_party/nspr/prtypes.h" + +/* +** A status code. Status's are used by procedures that return status +** values. Again the motivation is so that a compiler can generate +** warnings when return values are wrong. Correct testing of status codes: +** +** SECStatus rv; +** rv = some_function (some_argument); +** if (rv != SECSuccess) +** do_an_error_thing(); +** +*/ +typedef enum _SECStatus { + SECWouldBlock = -2, + SECFailure = -1, + SECSuccess = 0 +} SECStatus; + +#define SHA256_LENGTH 32 /* bytes */ +#define SHA384_LENGTH 48 /* bytes */ +#define SHA512_LENGTH 64 /* bytes */ +#define HASH_LENGTH_MAX SHA512_LENGTH + +/* + * Input block size for each hash algorithm. + */ + +#define SHA256_BLOCK_LENGTH 64 /* bytes */ +#define SHA384_BLOCK_LENGTH 128 /* bytes */ +#define SHA512_BLOCK_LENGTH 128 /* bytes */ +#define HASH_BLOCK_LENGTH_MAX SHA512_BLOCK_LENGTH + +/*************************************************************************** +** Opaque objects +*/ + +struct SHA256ContextStr ; +struct SHA512ContextStr ; + +typedef struct SHA256ContextStr SHA256Context; +typedef struct SHA512ContextStr SHA512Context; +/* SHA384Context is really a SHA512ContextStr. This is not a mistake. */ +typedef struct SHA512ContextStr SHA384Context; + +#endif /* _BLAPIT_H_ */ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/README.chromium firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/README.chromium --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/README.chromium 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/README.chromium 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,8 @@ +The original code is the Network Security Services (NSS), licensed under +the MPL/GPL/LGPL tri-license (http://www.mozilla.org/MPL/). + +We extracted the SHA-256 source files, eliminated unneeded dependencies, +deleted or commented out unused code, and tweaked them for Chrome's source +tree. sha512.c is renamed sha512.cc so that it can include Chrome's C++ +header "base/basictypes.h". We define NOUNROLL256 to reduce the object code +size. diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/sha256.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/sha256.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/sha256.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/sha256.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,51 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef _SHA_256_H_ +#define _SHA_256_H_ + +#include "base/third_party/nspr/prtypes.h" + +struct SHA256ContextStr { + union { + PRUint32 w[64]; /* message schedule, input buffer, plus 48 words */ + PRUint8 b[256]; + } u; + PRUint32 h[8]; /* 8 state variables */ + PRUint32 sizeHi,sizeLo; /* 64-bit count of hashed bytes. */ +}; + +#endif /* _SHA_256_H_ */ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/sha512.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/sha512.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/sha512.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/nss/sha512.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,1391 @@ +/* + * sha512.c - implementation of SHA256, SHA384 and SHA512 + * + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ +/* $Id: sha512.c,v 1.9 2006/10/13 16:54:04 wtchang%redhat.com Exp $ */ + +// Prevent manual unrolling in the sha256 code, which reduces the binary code +// size from ~10k to ~1k. The performance should be reasonable for our use. +#define NOUNROLL256 1 + +#include "base/third_party/nspr/prtypes.h" /* for PRUintXX */ +#if defined(_X86_) || defined(SHA_NO_LONG_LONG) +#define NOUNROLL512 1 +#undef HAVE_LONG_LONG +#endif +#include "base/third_party/nss/blapi.h" +#include "base/third_party/nss/sha256.h" /* for struct SHA256ContextStr */ + +#include +#include +#define PORT_New(type) static_cast(malloc(sizeof(type))) +#define PORT_ZFree(ptr, len) do { memset(ptr, 0, len); free(ptr); } while (0) +#define PORT_Strlen(s) static_cast(strlen(s)) +#define PORT_Memcpy memcpy + +/* ============= Common constants and defines ======================= */ + +#define W ctx->u.w +#define B ctx->u.b +#define H ctx->h + +#define SHR(x,n) (x >> n) +#define SHL(x,n) (x << n) +#define Ch(x,y,z) ((x & y) ^ (~x & z)) +#define Maj(x,y,z) ((x & y) ^ (x & z) ^ (y & z)) + +/* Padding used with all flavors of SHA */ +static const PRUint8 pad[240] = { +0x80,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + /* compiler will fill the rest in with zeros */ +}; + +/* ============= SHA256 implemenmtation ================================== */ + +/* SHA-256 constants, K256. */ +static const PRUint32 K256[64] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 +}; + +/* SHA-256 initial hash values */ +static const PRUint32 H256[8] = { + 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19 +}; + +#if defined(_MSC_VER) && defined(_X86_) +#ifndef FORCEINLINE +#if (_MSC_VER >= 1200) +#define FORCEINLINE __forceinline +#else +#define FORCEINLINE __inline +#endif +#endif +#define FASTCALL __fastcall + +static FORCEINLINE PRUint32 FASTCALL +swap4b(PRUint32 dwd) +{ + __asm { + mov eax,dwd + bswap eax + } +} + +#define SHA_HTONL(x) swap4b(x) +#define BYTESWAP4(x) x = SHA_HTONL(x) + +#elif defined(LINUX) && defined(_X86_) +#undef __OPTIMIZE__ +#define __OPTIMIZE__ 1 +#undef __pentium__ +#define __pentium__ 1 +#include +#define SHA_HTONL(x) bswap_32(x) +#define BYTESWAP4(x) x = SHA_HTONL(x) + +#else /* neither windows nor Linux PC */ +#define SWAP4MASK 0x00FF00FF +#define SHA_HTONL(x) (t1 = (x), t1 = (t1 << 16) | (t1 >> 16), \ + ((t1 & SWAP4MASK) << 8) | ((t1 >> 8) & SWAP4MASK)) +#define BYTESWAP4(x) x = SHA_HTONL(x) +#endif + +#if defined(_MSC_VER) && defined(_X86_) +#pragma intrinsic (_lrotr, _lrotl) +#define ROTR32(x,n) _lrotr(x,n) +#define ROTL32(x,n) _lrotl(x,n) +#else +#define ROTR32(x,n) ((x >> n) | (x << ((8 * sizeof x) - n))) +#define ROTL32(x,n) ((x << n) | (x >> ((8 * sizeof x) - n))) +#endif + +/* Capitol Sigma and lower case sigma functions */ +#define S0(x) (ROTR32(x, 2) ^ ROTR32(x,13) ^ ROTR32(x,22)) +#define S1(x) (ROTR32(x, 6) ^ ROTR32(x,11) ^ ROTR32(x,25)) +#define s0(x) (t1 = x, ROTR32(t1, 7) ^ ROTR32(t1,18) ^ SHR(t1, 3)) +#define s1(x) (t2 = x, ROTR32(t2,17) ^ ROTR32(t2,19) ^ SHR(t2,10)) + +SHA256Context * +SHA256_NewContext(void) +{ + SHA256Context *ctx = PORT_New(SHA256Context); + return ctx; +} + +void +SHA256_DestroyContext(SHA256Context *ctx, PRBool freeit) +{ + if (freeit) { + PORT_ZFree(ctx, sizeof *ctx); + } +} + +void +SHA256_Begin(SHA256Context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + memcpy(H, H256, sizeof H256); +} + +static void +SHA256_Compress(SHA256Context *ctx) +{ + { + register PRUint32 t1, t2; + +#if defined(IS_LITTLE_ENDIAN) + BYTESWAP4(W[0]); + BYTESWAP4(W[1]); + BYTESWAP4(W[2]); + BYTESWAP4(W[3]); + BYTESWAP4(W[4]); + BYTESWAP4(W[5]); + BYTESWAP4(W[6]); + BYTESWAP4(W[7]); + BYTESWAP4(W[8]); + BYTESWAP4(W[9]); + BYTESWAP4(W[10]); + BYTESWAP4(W[11]); + BYTESWAP4(W[12]); + BYTESWAP4(W[13]); + BYTESWAP4(W[14]); + BYTESWAP4(W[15]); +#endif + +#define INITW(t) W[t] = (s1(W[t-2]) + W[t-7] + s0(W[t-15]) + W[t-16]) + + /* prepare the "message schedule" */ +#ifdef NOUNROLL256 + { + int t; + for (t = 16; t < 64; ++t) { + INITW(t); + } + } +#else + INITW(16); + INITW(17); + INITW(18); + INITW(19); + + INITW(20); + INITW(21); + INITW(22); + INITW(23); + INITW(24); + INITW(25); + INITW(26); + INITW(27); + INITW(28); + INITW(29); + + INITW(30); + INITW(31); + INITW(32); + INITW(33); + INITW(34); + INITW(35); + INITW(36); + INITW(37); + INITW(38); + INITW(39); + + INITW(40); + INITW(41); + INITW(42); + INITW(43); + INITW(44); + INITW(45); + INITW(46); + INITW(47); + INITW(48); + INITW(49); + + INITW(50); + INITW(51); + INITW(52); + INITW(53); + INITW(54); + INITW(55); + INITW(56); + INITW(57); + INITW(58); + INITW(59); + + INITW(60); + INITW(61); + INITW(62); + INITW(63); + +#endif +#undef INITW + } + { + PRUint32 a, b, c, d, e, f, g, h; + + a = H[0]; + b = H[1]; + c = H[2]; + d = H[3]; + e = H[4]; + f = H[5]; + g = H[6]; + h = H[7]; + +#define ROUND(n,a,b,c,d,e,f,g,h) \ + h += S1(e) + Ch(e,f,g) + K256[n] + W[n]; \ + d += h; \ + h += S0(a) + Maj(a,b,c); + +#ifdef NOUNROLL256 + { + int t; + for (t = 0; t < 64; t+= 8) { + ROUND(t+0,a,b,c,d,e,f,g,h) + ROUND(t+1,h,a,b,c,d,e,f,g) + ROUND(t+2,g,h,a,b,c,d,e,f) + ROUND(t+3,f,g,h,a,b,c,d,e) + ROUND(t+4,e,f,g,h,a,b,c,d) + ROUND(t+5,d,e,f,g,h,a,b,c) + ROUND(t+6,c,d,e,f,g,h,a,b) + ROUND(t+7,b,c,d,e,f,g,h,a) + } + } +#else + ROUND( 0,a,b,c,d,e,f,g,h) + ROUND( 1,h,a,b,c,d,e,f,g) + ROUND( 2,g,h,a,b,c,d,e,f) + ROUND( 3,f,g,h,a,b,c,d,e) + ROUND( 4,e,f,g,h,a,b,c,d) + ROUND( 5,d,e,f,g,h,a,b,c) + ROUND( 6,c,d,e,f,g,h,a,b) + ROUND( 7,b,c,d,e,f,g,h,a) + + ROUND( 8,a,b,c,d,e,f,g,h) + ROUND( 9,h,a,b,c,d,e,f,g) + ROUND(10,g,h,a,b,c,d,e,f) + ROUND(11,f,g,h,a,b,c,d,e) + ROUND(12,e,f,g,h,a,b,c,d) + ROUND(13,d,e,f,g,h,a,b,c) + ROUND(14,c,d,e,f,g,h,a,b) + ROUND(15,b,c,d,e,f,g,h,a) + + ROUND(16,a,b,c,d,e,f,g,h) + ROUND(17,h,a,b,c,d,e,f,g) + ROUND(18,g,h,a,b,c,d,e,f) + ROUND(19,f,g,h,a,b,c,d,e) + ROUND(20,e,f,g,h,a,b,c,d) + ROUND(21,d,e,f,g,h,a,b,c) + ROUND(22,c,d,e,f,g,h,a,b) + ROUND(23,b,c,d,e,f,g,h,a) + + ROUND(24,a,b,c,d,e,f,g,h) + ROUND(25,h,a,b,c,d,e,f,g) + ROUND(26,g,h,a,b,c,d,e,f) + ROUND(27,f,g,h,a,b,c,d,e) + ROUND(28,e,f,g,h,a,b,c,d) + ROUND(29,d,e,f,g,h,a,b,c) + ROUND(30,c,d,e,f,g,h,a,b) + ROUND(31,b,c,d,e,f,g,h,a) + + ROUND(32,a,b,c,d,e,f,g,h) + ROUND(33,h,a,b,c,d,e,f,g) + ROUND(34,g,h,a,b,c,d,e,f) + ROUND(35,f,g,h,a,b,c,d,e) + ROUND(36,e,f,g,h,a,b,c,d) + ROUND(37,d,e,f,g,h,a,b,c) + ROUND(38,c,d,e,f,g,h,a,b) + ROUND(39,b,c,d,e,f,g,h,a) + + ROUND(40,a,b,c,d,e,f,g,h) + ROUND(41,h,a,b,c,d,e,f,g) + ROUND(42,g,h,a,b,c,d,e,f) + ROUND(43,f,g,h,a,b,c,d,e) + ROUND(44,e,f,g,h,a,b,c,d) + ROUND(45,d,e,f,g,h,a,b,c) + ROUND(46,c,d,e,f,g,h,a,b) + ROUND(47,b,c,d,e,f,g,h,a) + + ROUND(48,a,b,c,d,e,f,g,h) + ROUND(49,h,a,b,c,d,e,f,g) + ROUND(50,g,h,a,b,c,d,e,f) + ROUND(51,f,g,h,a,b,c,d,e) + ROUND(52,e,f,g,h,a,b,c,d) + ROUND(53,d,e,f,g,h,a,b,c) + ROUND(54,c,d,e,f,g,h,a,b) + ROUND(55,b,c,d,e,f,g,h,a) + + ROUND(56,a,b,c,d,e,f,g,h) + ROUND(57,h,a,b,c,d,e,f,g) + ROUND(58,g,h,a,b,c,d,e,f) + ROUND(59,f,g,h,a,b,c,d,e) + ROUND(60,e,f,g,h,a,b,c,d) + ROUND(61,d,e,f,g,h,a,b,c) + ROUND(62,c,d,e,f,g,h,a,b) + ROUND(63,b,c,d,e,f,g,h,a) +#endif + + H[0] += a; + H[1] += b; + H[2] += c; + H[3] += d; + H[4] += e; + H[5] += f; + H[6] += g; + H[7] += h; + } +#undef ROUND +} + +#undef s0 +#undef s1 +#undef S0 +#undef S1 + +void +SHA256_Update(SHA256Context *ctx, const unsigned char *input, + unsigned int inputLen) +{ + unsigned int inBuf = ctx->sizeLo & 0x3f; + if (!inputLen) + return; + + /* Add inputLen into the count of bytes processed, before processing */ + if ((ctx->sizeLo += inputLen) < inputLen) + ctx->sizeHi++; + + /* if data already in buffer, attemp to fill rest of buffer */ + if (inBuf) { + unsigned int todo = SHA256_BLOCK_LENGTH - inBuf; + if (inputLen < todo) + todo = inputLen; + memcpy(B + inBuf, input, todo); + input += todo; + inputLen -= todo; + if (inBuf + todo == SHA256_BLOCK_LENGTH) + SHA256_Compress(ctx); + } + + /* if enough data to fill one or more whole buffers, process them. */ + while (inputLen >= SHA256_BLOCK_LENGTH) { + memcpy(B, input, SHA256_BLOCK_LENGTH); + input += SHA256_BLOCK_LENGTH; + inputLen -= SHA256_BLOCK_LENGTH; + SHA256_Compress(ctx); + } + /* if data left over, fill it into buffer */ + if (inputLen) + memcpy(B, input, inputLen); +} + +void +SHA256_End(SHA256Context *ctx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen) +{ + unsigned int inBuf = ctx->sizeLo & 0x3f; + unsigned int padLen = (inBuf < 56) ? (56 - inBuf) : (56 + 64 - inBuf); + PRUint32 hi, lo; +#ifdef SWAP4MASK + PRUint32 t1; +#endif + + hi = (ctx->sizeHi << 3) | (ctx->sizeLo >> 29); + lo = (ctx->sizeLo << 3); + + SHA256_Update(ctx, pad, padLen); + +#if defined(IS_LITTLE_ENDIAN) + W[14] = SHA_HTONL(hi); + W[15] = SHA_HTONL(lo); +#else + W[14] = hi; + W[15] = lo; +#endif + SHA256_Compress(ctx); + + /* now output the answer */ +#if defined(IS_LITTLE_ENDIAN) + BYTESWAP4(H[0]); + BYTESWAP4(H[1]); + BYTESWAP4(H[2]); + BYTESWAP4(H[3]); + BYTESWAP4(H[4]); + BYTESWAP4(H[5]); + BYTESWAP4(H[6]); + BYTESWAP4(H[7]); +#endif + padLen = PR_MIN(SHA256_LENGTH, maxDigestLen); + memcpy(digest, H, padLen); + if (digestLen) + *digestLen = padLen; +} + +/* Comment out unused code, mostly the SHA384 and SHA512 implementations. */ +#if 0 +SECStatus +SHA256_HashBuf(unsigned char *dest, const unsigned char *src, + uint32 src_length) +{ + SHA256Context ctx; + unsigned int outLen; + + SHA256_Begin(&ctx); + SHA256_Update(&ctx, src, src_length); + SHA256_End(&ctx, dest, &outLen, SHA256_LENGTH); + + return SECSuccess; +} + + +SECStatus +SHA256_Hash(unsigned char *dest, const char *src) +{ + return SHA256_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src)); +} + + +void SHA256_TraceState(SHA256Context *ctx) { } + +unsigned int +SHA256_FlattenSize(SHA256Context *ctx) +{ + return sizeof *ctx; +} + +SECStatus +SHA256_Flatten(SHA256Context *ctx,unsigned char *space) +{ + PORT_Memcpy(space, ctx, sizeof *ctx); + return SECSuccess; +} + +SHA256Context * +SHA256_Resurrect(unsigned char *space, void *arg) +{ + SHA256Context *ctx = SHA256_NewContext(); + if (ctx) + PORT_Memcpy(ctx, space, sizeof *ctx); + return ctx; +} + +void SHA256_Clone(SHA256Context *dest, SHA256Context *src) +{ + memcpy(dest, src, sizeof *dest); +} + + +/* ======= SHA512 and SHA384 common constants and defines ================= */ + +/* common #defines for SHA512 and SHA384 */ +#if defined(HAVE_LONG_LONG) +#define ROTR64(x,n) ((x >> n) | (x << (64 - n))) +#define ROTL64(x,n) ((x << n) | (x >> (64 - n))) + +#define S0(x) (ROTR64(x,28) ^ ROTR64(x,34) ^ ROTR64(x,39)) +#define S1(x) (ROTR64(x,14) ^ ROTR64(x,18) ^ ROTR64(x,41)) +#define s0(x) (t1 = x, ROTR64(t1, 1) ^ ROTR64(t1, 8) ^ SHR(t1,7)) +#define s1(x) (t2 = x, ROTR64(t2,19) ^ ROTR64(t2,61) ^ SHR(t2,6)) + +#if PR_BYTES_PER_LONG == 8 +#define ULLC(hi,lo) 0x ## hi ## lo ## UL +#elif defined(_MSC_VER) +#define ULLC(hi,lo) 0x ## hi ## lo ## ui64 +#else +#define ULLC(hi,lo) 0x ## hi ## lo ## ULL +#endif + +#define SHA_MASK16 ULLC(0000FFFF,0000FFFF) +#define SHA_MASK8 ULLC(00FF00FF,00FF00FF) +#define SHA_HTONLL(x) (t1 = x, \ + t1 = ((t1 & SHA_MASK8 ) << 8) | ((t1 >> 8) & SHA_MASK8 ), \ + t1 = ((t1 & SHA_MASK16) << 16) | ((t1 >> 16) & SHA_MASK16), \ + (t1 >> 32) | (t1 << 32)) +#define BYTESWAP8(x) x = SHA_HTONLL(x) + +#else /* no long long */ + +#if defined(IS_LITTLE_ENDIAN) +#define ULLC(hi,lo) { 0x ## lo ## U, 0x ## hi ## U } +#else +#define ULLC(hi,lo) { 0x ## hi ## U, 0x ## lo ## U } +#endif + +#define SHA_HTONLL(x) ( BYTESWAP4(x.lo), BYTESWAP4(x.hi), \ + x.hi ^= x.lo ^= x.hi ^= x.lo, x) +#define BYTESWAP8(x) do { PRUint32 tmp; BYTESWAP4(x.lo); BYTESWAP4(x.hi); \ + tmp = x.lo; x.lo = x.hi; x.hi = tmp; } while (0) +#endif + +/* SHA-384 and SHA-512 constants, K512. */ +static const PRUint64 K512[80] = { +#if PR_BYTES_PER_LONG == 8 + 0x428a2f98d728ae22UL , 0x7137449123ef65cdUL , + 0xb5c0fbcfec4d3b2fUL , 0xe9b5dba58189dbbcUL , + 0x3956c25bf348b538UL , 0x59f111f1b605d019UL , + 0x923f82a4af194f9bUL , 0xab1c5ed5da6d8118UL , + 0xd807aa98a3030242UL , 0x12835b0145706fbeUL , + 0x243185be4ee4b28cUL , 0x550c7dc3d5ffb4e2UL , + 0x72be5d74f27b896fUL , 0x80deb1fe3b1696b1UL , + 0x9bdc06a725c71235UL , 0xc19bf174cf692694UL , + 0xe49b69c19ef14ad2UL , 0xefbe4786384f25e3UL , + 0x0fc19dc68b8cd5b5UL , 0x240ca1cc77ac9c65UL , + 0x2de92c6f592b0275UL , 0x4a7484aa6ea6e483UL , + 0x5cb0a9dcbd41fbd4UL , 0x76f988da831153b5UL , + 0x983e5152ee66dfabUL , 0xa831c66d2db43210UL , + 0xb00327c898fb213fUL , 0xbf597fc7beef0ee4UL , + 0xc6e00bf33da88fc2UL , 0xd5a79147930aa725UL , + 0x06ca6351e003826fUL , 0x142929670a0e6e70UL , + 0x27b70a8546d22ffcUL , 0x2e1b21385c26c926UL , + 0x4d2c6dfc5ac42aedUL , 0x53380d139d95b3dfUL , + 0x650a73548baf63deUL , 0x766a0abb3c77b2a8UL , + 0x81c2c92e47edaee6UL , 0x92722c851482353bUL , + 0xa2bfe8a14cf10364UL , 0xa81a664bbc423001UL , + 0xc24b8b70d0f89791UL , 0xc76c51a30654be30UL , + 0xd192e819d6ef5218UL , 0xd69906245565a910UL , + 0xf40e35855771202aUL , 0x106aa07032bbd1b8UL , + 0x19a4c116b8d2d0c8UL , 0x1e376c085141ab53UL , + 0x2748774cdf8eeb99UL , 0x34b0bcb5e19b48a8UL , + 0x391c0cb3c5c95a63UL , 0x4ed8aa4ae3418acbUL , + 0x5b9cca4f7763e373UL , 0x682e6ff3d6b2b8a3UL , + 0x748f82ee5defb2fcUL , 0x78a5636f43172f60UL , + 0x84c87814a1f0ab72UL , 0x8cc702081a6439ecUL , + 0x90befffa23631e28UL , 0xa4506cebde82bde9UL , + 0xbef9a3f7b2c67915UL , 0xc67178f2e372532bUL , + 0xca273eceea26619cUL , 0xd186b8c721c0c207UL , + 0xeada7dd6cde0eb1eUL , 0xf57d4f7fee6ed178UL , + 0x06f067aa72176fbaUL , 0x0a637dc5a2c898a6UL , + 0x113f9804bef90daeUL , 0x1b710b35131c471bUL , + 0x28db77f523047d84UL , 0x32caab7b40c72493UL , + 0x3c9ebe0a15c9bebcUL , 0x431d67c49c100d4cUL , + 0x4cc5d4becb3e42b6UL , 0x597f299cfc657e2aUL , + 0x5fcb6fab3ad6faecUL , 0x6c44198c4a475817UL +#else + ULLC(428a2f98,d728ae22), ULLC(71374491,23ef65cd), + ULLC(b5c0fbcf,ec4d3b2f), ULLC(e9b5dba5,8189dbbc), + ULLC(3956c25b,f348b538), ULLC(59f111f1,b605d019), + ULLC(923f82a4,af194f9b), ULLC(ab1c5ed5,da6d8118), + ULLC(d807aa98,a3030242), ULLC(12835b01,45706fbe), + ULLC(243185be,4ee4b28c), ULLC(550c7dc3,d5ffb4e2), + ULLC(72be5d74,f27b896f), ULLC(80deb1fe,3b1696b1), + ULLC(9bdc06a7,25c71235), ULLC(c19bf174,cf692694), + ULLC(e49b69c1,9ef14ad2), ULLC(efbe4786,384f25e3), + ULLC(0fc19dc6,8b8cd5b5), ULLC(240ca1cc,77ac9c65), + ULLC(2de92c6f,592b0275), ULLC(4a7484aa,6ea6e483), + ULLC(5cb0a9dc,bd41fbd4), ULLC(76f988da,831153b5), + ULLC(983e5152,ee66dfab), ULLC(a831c66d,2db43210), + ULLC(b00327c8,98fb213f), ULLC(bf597fc7,beef0ee4), + ULLC(c6e00bf3,3da88fc2), ULLC(d5a79147,930aa725), + ULLC(06ca6351,e003826f), ULLC(14292967,0a0e6e70), + ULLC(27b70a85,46d22ffc), ULLC(2e1b2138,5c26c926), + ULLC(4d2c6dfc,5ac42aed), ULLC(53380d13,9d95b3df), + ULLC(650a7354,8baf63de), ULLC(766a0abb,3c77b2a8), + ULLC(81c2c92e,47edaee6), ULLC(92722c85,1482353b), + ULLC(a2bfe8a1,4cf10364), ULLC(a81a664b,bc423001), + ULLC(c24b8b70,d0f89791), ULLC(c76c51a3,0654be30), + ULLC(d192e819,d6ef5218), ULLC(d6990624,5565a910), + ULLC(f40e3585,5771202a), ULLC(106aa070,32bbd1b8), + ULLC(19a4c116,b8d2d0c8), ULLC(1e376c08,5141ab53), + ULLC(2748774c,df8eeb99), ULLC(34b0bcb5,e19b48a8), + ULLC(391c0cb3,c5c95a63), ULLC(4ed8aa4a,e3418acb), + ULLC(5b9cca4f,7763e373), ULLC(682e6ff3,d6b2b8a3), + ULLC(748f82ee,5defb2fc), ULLC(78a5636f,43172f60), + ULLC(84c87814,a1f0ab72), ULLC(8cc70208,1a6439ec), + ULLC(90befffa,23631e28), ULLC(a4506ceb,de82bde9), + ULLC(bef9a3f7,b2c67915), ULLC(c67178f2,e372532b), + ULLC(ca273ece,ea26619c), ULLC(d186b8c7,21c0c207), + ULLC(eada7dd6,cde0eb1e), ULLC(f57d4f7f,ee6ed178), + ULLC(06f067aa,72176fba), ULLC(0a637dc5,a2c898a6), + ULLC(113f9804,bef90dae), ULLC(1b710b35,131c471b), + ULLC(28db77f5,23047d84), ULLC(32caab7b,40c72493), + ULLC(3c9ebe0a,15c9bebc), ULLC(431d67c4,9c100d4c), + ULLC(4cc5d4be,cb3e42b6), ULLC(597f299c,fc657e2a), + ULLC(5fcb6fab,3ad6faec), ULLC(6c44198c,4a475817) +#endif +}; + +struct SHA512ContextStr { + union { + PRUint64 w[80]; /* message schedule, input buffer, plus 64 words */ + PRUint32 l[160]; + PRUint8 b[640]; + } u; + PRUint64 h[8]; /* 8 state variables */ + PRUint64 sizeLo; /* 64-bit count of hashed bytes. */ +}; + +/* =========== SHA512 implementation ===================================== */ + +/* SHA-512 initial hash values */ +static const PRUint64 H512[8] = { +#if PR_BYTES_PER_LONG == 8 + 0x6a09e667f3bcc908UL , 0xbb67ae8584caa73bUL , + 0x3c6ef372fe94f82bUL , 0xa54ff53a5f1d36f1UL , + 0x510e527fade682d1UL , 0x9b05688c2b3e6c1fUL , + 0x1f83d9abfb41bd6bUL , 0x5be0cd19137e2179UL +#else + ULLC(6a09e667,f3bcc908), ULLC(bb67ae85,84caa73b), + ULLC(3c6ef372,fe94f82b), ULLC(a54ff53a,5f1d36f1), + ULLC(510e527f,ade682d1), ULLC(9b05688c,2b3e6c1f), + ULLC(1f83d9ab,fb41bd6b), ULLC(5be0cd19,137e2179) +#endif +}; + + +SHA512Context * +SHA512_NewContext(void) +{ + SHA512Context *ctx = PORT_New(SHA512Context); + return ctx; +} + +void +SHA512_DestroyContext(SHA512Context *ctx, PRBool freeit) +{ + if (freeit) { + PORT_ZFree(ctx, sizeof *ctx); + } +} + +void +SHA512_Begin(SHA512Context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + memcpy(H, H512, sizeof H512); +} + +#if defined(SHA512_TRACE) +#if defined(HAVE_LONG_LONG) +#define DUMP(n,a,d,e,h) printf(" t = %2d, %s = %016lx, %s = %016lx\n", \ + n, #e, d, #a, h); +#else +#define DUMP(n,a,d,e,h) printf(" t = %2d, %s = %08x%08x, %s = %08x%08x\n", \ + n, #e, d.hi, d.lo, #a, h.hi, h.lo); +#endif +#else +#define DUMP(n,a,d,e,h) +#endif + +#if defined(HAVE_LONG_LONG) + +#define ADDTO(x,y) y += x + +#define INITW(t) W[t] = (s1(W[t-2]) + W[t-7] + s0(W[t-15]) + W[t-16]) + +#define ROUND(n,a,b,c,d,e,f,g,h) \ + h += S1(e) + Ch(e,f,g) + K512[n] + W[n]; \ + d += h; \ + h += S0(a) + Maj(a,b,c); \ + DUMP(n,a,d,e,h) + +#else /* use only 32-bit variables, and don't unroll loops */ + +#undef NOUNROLL512 +#define NOUNROLL512 1 + +#define ADDTO(x,y) y.lo += x.lo; y.hi += x.hi + (x.lo > y.lo) + +#define ROTR64a(x,n,lo,hi) (x.lo >> n | x.hi << (32-n)) +#define ROTR64A(x,n,lo,hi) (x.lo << (64-n) | x.hi >> (n-32)) +#define SHR64a(x,n,lo,hi) (x.lo >> n | x.hi << (32-n)) + +/* Capitol Sigma and lower case sigma functions */ +#define s0lo(x) (ROTR64a(x,1,lo,hi) ^ ROTR64a(x,8,lo,hi) ^ SHR64a(x,7,lo,hi)) +#define s0hi(x) (ROTR64a(x,1,hi,lo) ^ ROTR64a(x,8,hi,lo) ^ (x.hi >> 7)) + +#define s1lo(x) (ROTR64a(x,19,lo,hi) ^ ROTR64A(x,61,lo,hi) ^ SHR64a(x,6,lo,hi)) +#define s1hi(x) (ROTR64a(x,19,hi,lo) ^ ROTR64A(x,61,hi,lo) ^ (x.hi >> 6)) + +#define S0lo(x)(ROTR64a(x,28,lo,hi) ^ ROTR64A(x,34,lo,hi) ^ ROTR64A(x,39,lo,hi)) +#define S0hi(x)(ROTR64a(x,28,hi,lo) ^ ROTR64A(x,34,hi,lo) ^ ROTR64A(x,39,hi,lo)) + +#define S1lo(x)(ROTR64a(x,14,lo,hi) ^ ROTR64a(x,18,lo,hi) ^ ROTR64A(x,41,lo,hi)) +#define S1hi(x)(ROTR64a(x,14,hi,lo) ^ ROTR64a(x,18,hi,lo) ^ ROTR64A(x,41,hi,lo)) + +/* 32-bit versions of Ch and Maj */ +#define Chxx(x,y,z,lo) ((x.lo & y.lo) ^ (~x.lo & z.lo)) +#define Majx(x,y,z,lo) ((x.lo & y.lo) ^ (x.lo & z.lo) ^ (y.lo & z.lo)) + +#define INITW(t) \ + do { \ + PRUint32 lo, tm; \ + PRUint32 cy = 0; \ + lo = s1lo(W[t-2]); \ + lo += (tm = W[t-7].lo); if (lo < tm) cy++; \ + lo += (tm = s0lo(W[t-15])); if (lo < tm) cy++; \ + lo += (tm = W[t-16].lo); if (lo < tm) cy++; \ + W[t].lo = lo; \ + W[t].hi = cy + s1hi(W[t-2]) + W[t-7].hi + s0hi(W[t-15]) + W[t-16].hi; \ + } while (0) + +#define ROUND(n,a,b,c,d,e,f,g,h) \ + { \ + PRUint32 lo, tm, cy; \ + lo = S1lo(e); \ + lo += (tm = Chxx(e,f,g,lo)); cy = (lo < tm); \ + lo += (tm = K512[n].lo); if (lo < tm) cy++; \ + lo += (tm = W[n].lo); if (lo < tm) cy++; \ + h.lo += lo; if (h.lo < lo) cy++; \ + h.hi += cy + S1hi(e) + Chxx(e,f,g,hi) + K512[n].hi + W[n].hi; \ + d.lo += h.lo; \ + d.hi += h.hi + (d.lo < h.lo); \ + lo = S0lo(a); \ + lo += (tm = Majx(a,b,c,lo)); cy = (lo < tm); \ + h.lo += lo; if (h.lo < lo) cy++; \ + h.hi += cy + S0hi(a) + Majx(a,b,c,hi); \ + DUMP(n,a,d,e,h) \ + } +#endif + +static void +SHA512_Compress(SHA512Context *ctx) +{ +#if defined(IS_LITTLE_ENDIAN) + { +#if defined(HAVE_LONG_LONG) + PRUint64 t1; +#else + PRUint32 t1; +#endif + BYTESWAP8(W[0]); + BYTESWAP8(W[1]); + BYTESWAP8(W[2]); + BYTESWAP8(W[3]); + BYTESWAP8(W[4]); + BYTESWAP8(W[5]); + BYTESWAP8(W[6]); + BYTESWAP8(W[7]); + BYTESWAP8(W[8]); + BYTESWAP8(W[9]); + BYTESWAP8(W[10]); + BYTESWAP8(W[11]); + BYTESWAP8(W[12]); + BYTESWAP8(W[13]); + BYTESWAP8(W[14]); + BYTESWAP8(W[15]); + } +#endif + + { + PRUint64 t1, t2; +#ifdef NOUNROLL512 + { + /* prepare the "message schedule" */ + int t; + for (t = 16; t < 80; ++t) { + INITW(t); + } + } +#else + INITW(16); + INITW(17); + INITW(18); + INITW(19); + + INITW(20); + INITW(21); + INITW(22); + INITW(23); + INITW(24); + INITW(25); + INITW(26); + INITW(27); + INITW(28); + INITW(29); + + INITW(30); + INITW(31); + INITW(32); + INITW(33); + INITW(34); + INITW(35); + INITW(36); + INITW(37); + INITW(38); + INITW(39); + + INITW(40); + INITW(41); + INITW(42); + INITW(43); + INITW(44); + INITW(45); + INITW(46); + INITW(47); + INITW(48); + INITW(49); + + INITW(50); + INITW(51); + INITW(52); + INITW(53); + INITW(54); + INITW(55); + INITW(56); + INITW(57); + INITW(58); + INITW(59); + + INITW(60); + INITW(61); + INITW(62); + INITW(63); + INITW(64); + INITW(65); + INITW(66); + INITW(67); + INITW(68); + INITW(69); + + INITW(70); + INITW(71); + INITW(72); + INITW(73); + INITW(74); + INITW(75); + INITW(76); + INITW(77); + INITW(78); + INITW(79); +#endif + } +#ifdef SHA512_TRACE + { + int i; + for (i = 0; i < 80; ++i) { +#ifdef HAVE_LONG_LONG + printf("W[%2d] = %016lx\n", i, W[i]); +#else + printf("W[%2d] = %08x%08x\n", i, W[i].hi, W[i].lo); +#endif + } + } +#endif + { + PRUint64 a, b, c, d, e, f, g, h; + + a = H[0]; + b = H[1]; + c = H[2]; + d = H[3]; + e = H[4]; + f = H[5]; + g = H[6]; + h = H[7]; + +#ifdef NOUNROLL512 + { + int t; + for (t = 0; t < 80; t+= 8) { + ROUND(t+0,a,b,c,d,e,f,g,h) + ROUND(t+1,h,a,b,c,d,e,f,g) + ROUND(t+2,g,h,a,b,c,d,e,f) + ROUND(t+3,f,g,h,a,b,c,d,e) + ROUND(t+4,e,f,g,h,a,b,c,d) + ROUND(t+5,d,e,f,g,h,a,b,c) + ROUND(t+6,c,d,e,f,g,h,a,b) + ROUND(t+7,b,c,d,e,f,g,h,a) + } + } +#else + ROUND( 0,a,b,c,d,e,f,g,h) + ROUND( 1,h,a,b,c,d,e,f,g) + ROUND( 2,g,h,a,b,c,d,e,f) + ROUND( 3,f,g,h,a,b,c,d,e) + ROUND( 4,e,f,g,h,a,b,c,d) + ROUND( 5,d,e,f,g,h,a,b,c) + ROUND( 6,c,d,e,f,g,h,a,b) + ROUND( 7,b,c,d,e,f,g,h,a) + + ROUND( 8,a,b,c,d,e,f,g,h) + ROUND( 9,h,a,b,c,d,e,f,g) + ROUND(10,g,h,a,b,c,d,e,f) + ROUND(11,f,g,h,a,b,c,d,e) + ROUND(12,e,f,g,h,a,b,c,d) + ROUND(13,d,e,f,g,h,a,b,c) + ROUND(14,c,d,e,f,g,h,a,b) + ROUND(15,b,c,d,e,f,g,h,a) + + ROUND(16,a,b,c,d,e,f,g,h) + ROUND(17,h,a,b,c,d,e,f,g) + ROUND(18,g,h,a,b,c,d,e,f) + ROUND(19,f,g,h,a,b,c,d,e) + ROUND(20,e,f,g,h,a,b,c,d) + ROUND(21,d,e,f,g,h,a,b,c) + ROUND(22,c,d,e,f,g,h,a,b) + ROUND(23,b,c,d,e,f,g,h,a) + + ROUND(24,a,b,c,d,e,f,g,h) + ROUND(25,h,a,b,c,d,e,f,g) + ROUND(26,g,h,a,b,c,d,e,f) + ROUND(27,f,g,h,a,b,c,d,e) + ROUND(28,e,f,g,h,a,b,c,d) + ROUND(29,d,e,f,g,h,a,b,c) + ROUND(30,c,d,e,f,g,h,a,b) + ROUND(31,b,c,d,e,f,g,h,a) + + ROUND(32,a,b,c,d,e,f,g,h) + ROUND(33,h,a,b,c,d,e,f,g) + ROUND(34,g,h,a,b,c,d,e,f) + ROUND(35,f,g,h,a,b,c,d,e) + ROUND(36,e,f,g,h,a,b,c,d) + ROUND(37,d,e,f,g,h,a,b,c) + ROUND(38,c,d,e,f,g,h,a,b) + ROUND(39,b,c,d,e,f,g,h,a) + + ROUND(40,a,b,c,d,e,f,g,h) + ROUND(41,h,a,b,c,d,e,f,g) + ROUND(42,g,h,a,b,c,d,e,f) + ROUND(43,f,g,h,a,b,c,d,e) + ROUND(44,e,f,g,h,a,b,c,d) + ROUND(45,d,e,f,g,h,a,b,c) + ROUND(46,c,d,e,f,g,h,a,b) + ROUND(47,b,c,d,e,f,g,h,a) + + ROUND(48,a,b,c,d,e,f,g,h) + ROUND(49,h,a,b,c,d,e,f,g) + ROUND(50,g,h,a,b,c,d,e,f) + ROUND(51,f,g,h,a,b,c,d,e) + ROUND(52,e,f,g,h,a,b,c,d) + ROUND(53,d,e,f,g,h,a,b,c) + ROUND(54,c,d,e,f,g,h,a,b) + ROUND(55,b,c,d,e,f,g,h,a) + + ROUND(56,a,b,c,d,e,f,g,h) + ROUND(57,h,a,b,c,d,e,f,g) + ROUND(58,g,h,a,b,c,d,e,f) + ROUND(59,f,g,h,a,b,c,d,e) + ROUND(60,e,f,g,h,a,b,c,d) + ROUND(61,d,e,f,g,h,a,b,c) + ROUND(62,c,d,e,f,g,h,a,b) + ROUND(63,b,c,d,e,f,g,h,a) + + ROUND(64,a,b,c,d,e,f,g,h) + ROUND(65,h,a,b,c,d,e,f,g) + ROUND(66,g,h,a,b,c,d,e,f) + ROUND(67,f,g,h,a,b,c,d,e) + ROUND(68,e,f,g,h,a,b,c,d) + ROUND(69,d,e,f,g,h,a,b,c) + ROUND(70,c,d,e,f,g,h,a,b) + ROUND(71,b,c,d,e,f,g,h,a) + + ROUND(72,a,b,c,d,e,f,g,h) + ROUND(73,h,a,b,c,d,e,f,g) + ROUND(74,g,h,a,b,c,d,e,f) + ROUND(75,f,g,h,a,b,c,d,e) + ROUND(76,e,f,g,h,a,b,c,d) + ROUND(77,d,e,f,g,h,a,b,c) + ROUND(78,c,d,e,f,g,h,a,b) + ROUND(79,b,c,d,e,f,g,h,a) +#endif + + ADDTO(a,H[0]); + ADDTO(b,H[1]); + ADDTO(c,H[2]); + ADDTO(d,H[3]); + ADDTO(e,H[4]); + ADDTO(f,H[5]); + ADDTO(g,H[6]); + ADDTO(h,H[7]); + } +} + +void +SHA512_Update(SHA512Context *ctx, const unsigned char *input, + unsigned int inputLen) +{ + unsigned int inBuf; + if (!inputLen) + return; + +#if defined(HAVE_LONG_LONG) + inBuf = (unsigned int)ctx->sizeLo & 0x7f; + /* Add inputLen into the count of bytes processed, before processing */ + ctx->sizeLo += inputLen; +#else + inBuf = (unsigned int)ctx->sizeLo.lo & 0x7f; + ctx->sizeLo.lo += inputLen; + if (ctx->sizeLo.lo < inputLen) ctx->sizeLo.hi++; +#endif + + /* if data already in buffer, attemp to fill rest of buffer */ + if (inBuf) { + unsigned int todo = SHA512_BLOCK_LENGTH - inBuf; + if (inputLen < todo) + todo = inputLen; + memcpy(B + inBuf, input, todo); + input += todo; + inputLen -= todo; + if (inBuf + todo == SHA512_BLOCK_LENGTH) + SHA512_Compress(ctx); + } + + /* if enough data to fill one or more whole buffers, process them. */ + while (inputLen >= SHA512_BLOCK_LENGTH) { + memcpy(B, input, SHA512_BLOCK_LENGTH); + input += SHA512_BLOCK_LENGTH; + inputLen -= SHA512_BLOCK_LENGTH; + SHA512_Compress(ctx); + } + /* if data left over, fill it into buffer */ + if (inputLen) + memcpy(B, input, inputLen); +} + +void +SHA512_End(SHA512Context *ctx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen) +{ +#if defined(HAVE_LONG_LONG) + unsigned int inBuf = (unsigned int)ctx->sizeLo & 0x7f; + unsigned int padLen = (inBuf < 112) ? (112 - inBuf) : (112 + 128 - inBuf); + PRUint64 lo, t1; + lo = (ctx->sizeLo << 3); +#else + unsigned int inBuf = (unsigned int)ctx->sizeLo.lo & 0x7f; + unsigned int padLen = (inBuf < 112) ? (112 - inBuf) : (112 + 128 - inBuf); + PRUint64 lo = ctx->sizeLo; + PRUint32 t1; + lo.lo <<= 3; +#endif + + SHA512_Update(ctx, pad, padLen); + +#if defined(HAVE_LONG_LONG) + W[14] = 0; +#else + W[14].lo = 0; + W[14].hi = 0; +#endif + + W[15] = lo; +#if defined(IS_LITTLE_ENDIAN) + BYTESWAP8(W[15]); +#endif + SHA512_Compress(ctx); + + /* now output the answer */ +#if defined(IS_LITTLE_ENDIAN) + BYTESWAP8(H[0]); + BYTESWAP8(H[1]); + BYTESWAP8(H[2]); + BYTESWAP8(H[3]); + BYTESWAP8(H[4]); + BYTESWAP8(H[5]); + BYTESWAP8(H[6]); + BYTESWAP8(H[7]); +#endif + padLen = PR_MIN(SHA512_LENGTH, maxDigestLen); + memcpy(digest, H, padLen); + if (digestLen) + *digestLen = padLen; +} + +SECStatus +SHA512_HashBuf(unsigned char *dest, const unsigned char *src, + uint32 src_length) +{ + SHA512Context ctx; + unsigned int outLen; + + SHA512_Begin(&ctx); + SHA512_Update(&ctx, src, src_length); + SHA512_End(&ctx, dest, &outLen, SHA512_LENGTH); + + return SECSuccess; +} + + +SECStatus +SHA512_Hash(unsigned char *dest, const char *src) +{ + return SHA512_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src)); +} + + +void SHA512_TraceState(SHA512Context *ctx) { } + +unsigned int +SHA512_FlattenSize(SHA512Context *ctx) +{ + return sizeof *ctx; +} + +SECStatus +SHA512_Flatten(SHA512Context *ctx,unsigned char *space) +{ + PORT_Memcpy(space, ctx, sizeof *ctx); + return SECSuccess; +} + +SHA512Context * +SHA512_Resurrect(unsigned char *space, void *arg) +{ + SHA512Context *ctx = SHA512_NewContext(); + if (ctx) + PORT_Memcpy(ctx, space, sizeof *ctx); + return ctx; +} + +void SHA512_Clone(SHA512Context *dest, SHA512Context *src) +{ + memcpy(dest, src, sizeof *dest); +} + +/* ======================================================================= */ +/* SHA384 uses a SHA512Context as the real context. +** The only differences between SHA384 an SHA512 are: +** a) the intialization values for the context, and +** b) the number of bytes of data produced as output. +*/ + +/* SHA-384 initial hash values */ +static const PRUint64 H384[8] = { +#if PR_BYTES_PER_LONG == 8 + 0xcbbb9d5dc1059ed8UL , 0x629a292a367cd507UL , + 0x9159015a3070dd17UL , 0x152fecd8f70e5939UL , + 0x67332667ffc00b31UL , 0x8eb44a8768581511UL , + 0xdb0c2e0d64f98fa7UL , 0x47b5481dbefa4fa4UL +#else + ULLC(cbbb9d5d,c1059ed8), ULLC(629a292a,367cd507), + ULLC(9159015a,3070dd17), ULLC(152fecd8,f70e5939), + ULLC(67332667,ffc00b31), ULLC(8eb44a87,68581511), + ULLC(db0c2e0d,64f98fa7), ULLC(47b5481d,befa4fa4) +#endif +}; + +SHA384Context * +SHA384_NewContext(void) +{ + return SHA512_NewContext(); +} + +void +SHA384_DestroyContext(SHA384Context *ctx, PRBool freeit) +{ + SHA512_DestroyContext(ctx, freeit); +} + +void +SHA384_Begin(SHA384Context *ctx) +{ + memset(ctx, 0, sizeof *ctx); + memcpy(H, H384, sizeof H384); +} + +void +SHA384_Update(SHA384Context *ctx, const unsigned char *input, + unsigned int inputLen) +{ + SHA512_Update(ctx, input, inputLen); +} + +void +SHA384_End(SHA384Context *ctx, unsigned char *digest, + unsigned int *digestLen, unsigned int maxDigestLen) +{ +#define SHA_MIN(a,b) (a < b ? a : b) + unsigned int maxLen = SHA_MIN(maxDigestLen, SHA384_LENGTH); + SHA512_End(ctx, digest, digestLen, maxLen); +} + +SECStatus +SHA384_HashBuf(unsigned char *dest, const unsigned char *src, + uint32 src_length) +{ + SHA512Context ctx; + unsigned int outLen; + + SHA384_Begin(&ctx); + SHA512_Update(&ctx, src, src_length); + SHA512_End(&ctx, dest, &outLen, SHA384_LENGTH); + + return SECSuccess; +} + +SECStatus +SHA384_Hash(unsigned char *dest, const char *src) +{ + return SHA384_HashBuf(dest, (const unsigned char *)src, PORT_Strlen(src)); +} + +void SHA384_TraceState(SHA384Context *ctx) { } + +unsigned int +SHA384_FlattenSize(SHA384Context *ctx) +{ + return sizeof(SHA384Context); +} + +SECStatus +SHA384_Flatten(SHA384Context *ctx,unsigned char *space) +{ + return SHA512_Flatten(ctx, space); +} + +SHA384Context * +SHA384_Resurrect(unsigned char *space, void *arg) +{ + return SHA512_Resurrect(space, arg); +} + +void SHA384_Clone(SHA384Context *dest, SHA384Context *src) +{ + memcpy(dest, src, sizeof *dest); +} +#endif /* Comment out unused code. */ + +/* ======================================================================= */ +#ifdef SELFTEST +#include + +static const char abc[] = { "abc" }; +static const char abcdbc[] = { + "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" +}; +static const char abcdef[] = { + "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" +}; + +void +dumpHash32(const unsigned char *buf, unsigned int bufLen) +{ + unsigned int i; + for (i = 0; i < bufLen; i += 4) { + printf(" %02x%02x%02x%02x", buf[i], buf[i+1], buf[i+2], buf[i+3]); + } + printf("\n"); +} + +void test256(void) +{ + unsigned char outBuf[SHA256_LENGTH]; + + printf("SHA256, input = %s\n", abc); + SHA256_Hash(outBuf, abc); + dumpHash32(outBuf, sizeof outBuf); + + printf("SHA256, input = %s\n", abcdbc); + SHA256_Hash(outBuf, abcdbc); + dumpHash32(outBuf, sizeof outBuf); +} + +void +dumpHash64(const unsigned char *buf, unsigned int bufLen) +{ + unsigned int i; + for (i = 0; i < bufLen; i += 8) { + if (i % 32 == 0) + printf("\n"); + printf(" %02x%02x%02x%02x%02x%02x%02x%02x", + buf[i ], buf[i+1], buf[i+2], buf[i+3], + buf[i+4], buf[i+5], buf[i+6], buf[i+7]); + } + printf("\n"); +} + +void test512(void) +{ + unsigned char outBuf[SHA512_LENGTH]; + + printf("SHA512, input = %s\n", abc); + SHA512_Hash(outBuf, abc); + dumpHash64(outBuf, sizeof outBuf); + + printf("SHA512, input = %s\n", abcdef); + SHA512_Hash(outBuf, abcdef); + dumpHash64(outBuf, sizeof outBuf); +} + +void time512(void) +{ + unsigned char outBuf[SHA512_LENGTH]; + + SHA512_Hash(outBuf, abc); + SHA512_Hash(outBuf, abcdef); +} + +void test384(void) +{ + unsigned char outBuf[SHA384_LENGTH]; + + printf("SHA384, input = %s\n", abc); + SHA384_Hash(outBuf, abc); + dumpHash64(outBuf, sizeof outBuf); + + printf("SHA384, input = %s\n", abcdef); + SHA384_Hash(outBuf, abcdef); + dumpHash64(outBuf, sizeof outBuf); +} + +int main (int argc, char *argv[], char *envp[]) +{ + int i = 1; + if (argc > 1) { + i = atoi(argv[1]); + } + if (i < 2) { + test256(); + test512(); + test384(); + } else { + while (i-- > 0) { + time512(); + } + printf("done\n"); + } + return 0; +} + +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/purify/pure_api.c firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/purify/pure_api.c --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/purify/pure_api.c 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/purify/pure_api.c 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,152 @@ +/* + * Header file of Pure API function declarations. + * + * Explicitly no copyright. + * You may recompile and redistribute these definitions as required. + * + * NOTE1: In some situations when compiling with MFC, you should + * enable the setting 'Not using precompiled headers' in Visual C++ + * to avoid a compiler diagnostic. + * + * NOTE2: This file works through the use of deep magic. Calls to functions + * in this file are replaced with calls into the OCI runtime system + * when an instrumented version of this program is run. + * + * NOTE3: The static vars avoidGy_n (where n is a unique number) are used + * to prevent optimizing the functions away when compiler option + * /Gy is set. This is needed so that NOTE2 works properly. + */ + +// Chromium note: We used to only compile this code if PURIFY was defined, +// because we did special builds with all optimizations turned off for Purify. +// However, for profiling with Quantify, we want most/all optimizations turned +// on so that we measure something closer to real execution. + +#ifdef _WINDOWS // we only use Purify/Quantify on Windows + +#pragma once + extern int errno; +typedef int ptrdiff_t; +typedef unsigned int size_t; +typedef unsigned short wchar_t; +static int avoidGy_1 = 0; +static int avoidGy_2 = 0; +static int avoidGy_3 = 0; +static int avoidGy_4 = 0; +static int avoidGy_5 = 0; +static int avoidGy_6 = 0; +static int avoidGy_7 = 0; +static int avoidGy_8 = 0; +static int avoidGy_9 = 0; +static int avoidGy_10 = 0; +static int avoidGy_11 = 0; +static int avoidGy_12 = 0; +static int avoidGy_13 = 0; +static int avoidGy_14 = 0; +static int avoidGy_15 = 0; +static int avoidGy_16 = 0; +static int avoidGy_17 = 0; +static int avoidGy_18 = 0; +static int avoidGy_19 = 0; +static int avoidGy_20 = 0; +static int avoidGy_21 = 0; +static int avoidGy_22 = 0; +static int avoidGy_23 = 0; +static int avoidGy_24 = 0; +static int avoidGy_25 = 0; +static int avoidGy_26 = 0; +static int avoidGy_27 = 0; +static int avoidGy_28 = 0; +static int avoidGy_29 = 0; +static int avoidGy_30 = 0; +static int avoidGy_31 = 0; +static int avoidGy_32 = 0; +static int avoidGy_33 = 0; +static int avoidGy_34 = 0; +static int avoidGy_35 = 0; +static int avoidGy_36 = 0; +static int avoidGy_37 = 0; +static int avoidGy_38 = 0; +static int avoidGy_39 = 0; +static int avoidGy_40 = 0; +static int avoidGy_41 = 0; +static int avoidGy_42 = 0; +static int avoidGy_43 = 0; +static int avoidGy_44 = 0; +static int avoidGy_45 = 0; +static int avoidGy_46 = 0; +static int avoidGy_47 = 0; +static int avoidGy_48 = 0; +static int avoidGy_49 = 0; +static int avoidGy_50 = 0; +static int avoidGy_51 = 0; +static int avoidGy_52 = 0; +static int avoidGy_53 = 0; +static int avoidGy_54 = 0; +static int avoidGy_55 = 0; +static int avoidGy_56 = 0; +static int avoidGy_57 = 0; +static int avoidGy_58 = 0; +static int avoidGy_59 = 0; +static int avoidGy_60 = 0; +static int avoidGy_61 = 0; +static int avoidGy_62 = 0; +static int avoidGy_63 = 0; +static int avoidGy_64 = 0; +static int avoidGy_65 = 0; +static int avoidGy_PL_01 = 0; +static int avoidGy_PL_02 = 0; +__declspec(dllexport) int __cdecl PurePrintf(const char *fmt, ...) { if(!++avoidGy_1); fmt; return 0; } +__declspec(dllexport) int __cdecl PurifyIsRunning(void) { if(!++avoidGy_2); return 0; } +__declspec(dllexport) int __cdecl PurifyPrintf(const char *fmt, ...) { if(!++avoidGy_3); fmt; return 0; } +__declspec(dllexport) size_t __cdecl PurifyNewInuse(void) { if(!++avoidGy_4); return 0; } +__declspec(dllexport) size_t __cdecl PurifyAllInuse(void) { if(!++avoidGy_5); return 0; } +__declspec(dllexport) size_t __cdecl PurifyClearInuse(void) { if(!++avoidGy_6); return 0; } +__declspec(dllexport) size_t __cdecl PurifyNewLeaks(void) { if(!++avoidGy_7); return 0; } +__declspec(dllexport) size_t __cdecl PurifyAllLeaks(void) { if(!++avoidGy_8); return 0; } +__declspec(dllexport) size_t __cdecl PurifyClearLeaks(void) { if(!++avoidGy_9); return 0; } +__declspec(dllexport) size_t __cdecl PurifyAllHandlesInuse(void) { if(!++avoidGy_10); return 0; } +__declspec(dllexport) size_t __cdecl PurifyNewHandlesInuse(void) { if(!++avoidGy_11); return 0; } +__declspec(dllexport) size_t __cdecl PurifyDescribe(void *addr) { if(!++avoidGy_12); addr; return 0; } +__declspec(dllexport) int __cdecl PurifyWhatColors(void *addr, size_t size) { if(!++avoidGy_13); addr; size; return 0; } +__declspec(dllexport) int __cdecl PurifyAssertIsReadable(const void *addr, size_t size) { if(!++avoidGy_14); addr; size; return 1; } +__declspec(dllexport) int __cdecl PurifyAssertIsWritable(const void *addr, size_t size) { if(!++avoidGy_15); addr; size; return 1; } +__declspec(dllexport) int __cdecl PurifyIsReadable(const void *addr, size_t size) { if(!++avoidGy_16); addr; size; return 1; } +__declspec(dllexport) int __cdecl PurifyIsWritable(const void *addr, size_t size) { if(!++avoidGy_17); addr; size; return 1; } +__declspec(dllexport) int __cdecl PurifyIsInitialized(const void *addr, size_t size) { if(!++avoidGy_18); addr; size; return 1; } +__declspec(dllexport) int __cdecl PurifyRed(void *addr, size_t size) { if(!++avoidGy_19); addr; size; return 0; } +__declspec(dllexport) int __cdecl PurifyGreen(void *addr, size_t size) { if(!++avoidGy_20); addr; size; return 0; } +__declspec(dllexport) int __cdecl PurifyYellow(void *addr, size_t size) { if(!++avoidGy_21); addr; size; return 0; } +__declspec(dllexport) int __cdecl PurifyBlue(void *addr, size_t size) { if(!++avoidGy_22); addr; size; return 0; } +__declspec(dllexport) int __cdecl PurifyMarkAsInitialized(void *addr, size_t size) { if(!++avoidGy_23); addr; size; return 0; } +__declspec(dllexport) int __cdecl PurifyMarkAsUninitialized(void *addr, size_t size) { if(!++avoidGy_24); addr; size; return 0; } +__declspec(dllexport) int __cdecl PurifyMarkForTrap(void *addr, size_t size) { if(!++avoidGy_25); addr; size; return 0; } +__declspec(dllexport) int __cdecl PurifyMarkForNoTrap(void *addr, size_t size) { if(!++avoidGy_26); addr; size; return 0; } +__declspec(dllexport) int __cdecl PurifyHeapValidate(unsigned int hHeap, unsigned int dwFlags, const void *addr) + { if(!++avoidGy_27); hHeap; dwFlags; addr; return 1; } +__declspec(dllexport) int __cdecl PurifySetLateDetectScanCounter(int counter) { if(!++avoidGy_28); counter; return 0; }; +__declspec(dllexport) int __cdecl PurifySetLateDetectScanInterval(int seconds) { if(!++avoidGy_29); seconds; return 0; }; +__declspec(dllexport) void __cdecl PurifySetPoolId(const void *mem, int id) { if(!++avoidGy_61); mem; id; return; }; +__declspec(dllexport) int __cdecl PurifyGetPoolId(const void *mem) { if(!++avoidGy_62); mem; return 0; }; +__declspec(dllexport) void __cdecl PurifySetUserData(const void *mem, void *data) { if(!++avoidGy_63); mem; data; return; }; +__declspec(dllexport) void * __cdecl PurifyGetUserData(const void *mem) { if(!++avoidGy_64); mem; return 0; }; +__declspec(dllexport) void __cdecl PurifyMapPool(int id, void(*fn)()) { if(!++avoidGy_65); id; fn; return; }; +__declspec(dllexport) int __cdecl CoverageIsRunning(void) { if(!++avoidGy_30); return 0; } +__declspec(dllexport) int __cdecl CoverageDisableRecordingData(void) { if(!++avoidGy_31); return 0; } +__declspec(dllexport) int __cdecl CoverageStartRecordingData(void) { if(!++avoidGy_32); return 0; } +__declspec(dllexport) int __cdecl CoverageStopRecordingData(void) { if(!++avoidGy_33); return 0; } +__declspec(dllexport) int __cdecl CoverageClearData(void) { if(!++avoidGy_34); return 0; } +__declspec(dllexport) int __cdecl CoverageIsRecordingData(void) { if(!++avoidGy_35); return 0; } +__declspec(dllexport) int __cdecl CoverageAddAnnotation(char *str) { if(!++avoidGy_36); str; return 0; } +__declspec(dllexport) int __cdecl CoverageSaveData(void) { if(!++avoidGy_37); return 0; } +__declspec(dllexport) int __cdecl QuantifyIsRunning(void) { if(!++avoidGy_42); return 0; } +__declspec(dllexport) int __cdecl QuantifyDisableRecordingData(void) { if(!++avoidGy_43); return 0; } +__declspec(dllexport) int __cdecl QuantifyStartRecordingData(void) { if(!++avoidGy_44); return 0; } +__declspec(dllexport) int __cdecl QuantifyStopRecordingData(void) { if(!++avoidGy_45); return 0; } +__declspec(dllexport) int __cdecl QuantifyClearData(void) { if(!++avoidGy_46); return 0; } +__declspec(dllexport) int __cdecl QuantifyIsRecordingData(void) { if(!++avoidGy_47); return 0; } +__declspec(dllexport) int __cdecl QuantifyAddAnnotation(char *str) { if(!++avoidGy_48); str; return 0; } +__declspec(dllexport) int __cdecl QuantifySaveData(void) { if(!++avoidGy_49); return 0; } +__declspec(dllexport) int __cdecl QuantifySetThreadName(const char *szName) { if(!++avoidGy_50) ; szName; return 0; } + +#endif // _WINDOWS diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/purify/pure.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/purify/pure.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/third_party/purify/pure.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/third_party/purify/pure.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,145 @@ +/* + * Header file of Pure API function declarations. + * +* (C) Copyright IBM Corporation. 2006, 2006. All Rights Reserved. + * You may recompile and redistribute these definitions as required. + * + * Version 1.0 + */ + +#if defined(PURIFY) || defined(QUANTIFY) + +#if defined(c_plusplus) || defined(__cplusplus) +extern "C" { +#endif + +// Don't include this file directly, use purify.h instead. +// If you need something that's not there, add it. +#ifdef PURIFY_PRIVATE_INCLUDE + +#define PURE_H_VERSION 1 +#include + +////////////////////////////// +// API's Specific to Purify // +////////////////////////////// + +// TRUE when Purify is running. +int __cdecl PurifyIsRunning(void) ; +// +// Print a string to the viewer. +// +int __cdecl PurePrintf(const char *fmt, ...) ; +int __cdecl PurifyPrintf(const char *fmt, ...) ; +// +// Purify functions for leak and memory-in-use functionalty. +// +size_t __cdecl PurifyNewInuse(void) ; +size_t __cdecl PurifyAllInuse(void) ; +size_t __cdecl PurifyClearInuse(void) ; +size_t __cdecl PurifyNewLeaks(void) ; +size_t __cdecl PurifyAllLeaks(void) ; +size_t __cdecl PurifyClearLeaks(void) ; +// +// Purify functions for handle leakage. +// +size_t __cdecl PurifyAllHandlesInuse(void) ; +size_t __cdecl PurifyNewHandlesInuse(void) ; +// +// Functions that tell you about the state of memory. +// +size_t __cdecl PurifyDescribe(void *addr) ; +size_t __cdecl PurifyWhatColors(void *addr, size_t size) ; +// +// Functions to test the state of memory. If the memory is not +// accessable, an error is signaled just as if there were a memory +// reference and the function returns false. +// +int __cdecl PurifyAssertIsReadable(const void *addr, size_t size) ; // size used to be an int, until IA64 came along +int __cdecl PurifyAssertIsWritable(const void *addr, size_t size) ; +// +// Functions to test the state of memory. If the memory is not +// accessable, these functions return false. No error is signaled. +// +int __cdecl PurifyIsReadable(const void *addr, size_t size) ; +int __cdecl PurifyIsWritable(const void *addr, size_t size) ; +int __cdecl PurifyIsInitialized(const void *addr, size_t size) ; +// +// Functions to set the state of memory. +// +void __cdecl PurifyMarkAsInitialized(void *addr, size_t size) ; +void __cdecl PurifyMarkAsUninitialized(void *addr, size_t size) ; +// +// Functions to do late detection of ABWs, FMWs, IPWs. +// +#define PURIFY_HEAP_CRT (HANDLE) ~(__int64) 1 /* 0xfffffffe */ +#define PURIFY_HEAP_ALL (HANDLE) ~(__int64) 2 /* 0xfffffffd */ +#define PURIFY_HEAP_BLOCKS_LIVE 0x80000000 +#define PURIFY_HEAP_BLOCKS_DEFERRED_FREE 0x40000000 +#define PURIFY_HEAP_BLOCKS_ALL (PURIFY_HEAP_BLOCKS_LIVE|PURIFY_HEAP_BLOCKS_DEFERRED_FREE) +int __cdecl PurifyHeapValidate(unsigned int hHeap, unsigned int dwFlags, const void *addr) ; +int __cdecl PurifySetLateDetectScanCounter(int counter); +int __cdecl PurifySetLateDetectScanInterval(int seconds); +// +// Functions to support pool allocators +// +void __cdecl PurifySetPoolId(const void *mem, int id); +int __cdecl PurifyGetPoolId(const void *mem); +void __cdecl PurifySetUserData(const void *mem, void *data); +void * __cdecl PurifyGetUserData(const void *mem); +void __cdecl PurifyMapPool(int id, void(*fn)()); + + +//////////////////////////////// +// API's Specific to Quantify // +//////////////////////////////// + +// TRUE when Quantify is running. +int __cdecl QuantifyIsRunning(void) ; + +// +// Functions for controlling collection +// +int __cdecl QuantifyDisableRecordingData(void) ; +int __cdecl QuantifyStartRecordingData(void) ; +int __cdecl QuantifyStopRecordingData(void) ; +int __cdecl QuantifyClearData(void) ; +int __cdecl QuantifyIsRecordingData(void) ; + +// Add a comment to the dataset +int __cdecl QuantifyAddAnnotation(char *) ; + +// Save the current data, creating a "checkpoint" dataset +int __cdecl QuantifySaveData(void) ; + +// Set the name of the current thread in the viewer +int __cdecl QuantifySetThreadName(char *) ; + +//////////////////////////////// +// API's Specific to Coverage // +//////////////////////////////// + +// TRUE when Coverage is running. +int __cdecl CoverageIsRunning(void) ; +// +// Functions for controlling collection +// +int __cdecl CoverageDisableRecordingData(void) ; +int __cdecl CoverageStartRecordingData(void) ; +int __cdecl CoverageStopRecordingData(void) ; +int __cdecl CoverageClearData(void) ; +int __cdecl CoverageIsRecordingData(void) ; +// Add a comment to the dataset +int __cdecl CoverageAddAnnotation(char *) ; + +// Save the current data, creating a "checkpoint" dataset +int __cdecl CoverageSaveData(void) ; + + +#endif // PURIFY_PRIVATE_INCLUDE + +#if defined(c_plusplus) || defined(__cplusplus) +} +#endif + +#endif // defined(PURIFY) || defined(QUANTIFY) \ No newline at end of file diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,178 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/thread.h" + +#include "base/lazy_instance.h" +#include "base/string_util.h" +#include "base/thread_local.h" +#include "base/waitable_event.h" + +namespace base { + +// This task is used to trigger the message loop to exit. +class ThreadQuitTask : public Task { + public: + virtual void Run() { + MessageLoop::current()->Quit(); + Thread::SetThreadWasQuitProperly(true); + } +}; + +// Used to pass data to ThreadMain. This structure is allocated on the stack +// from within StartWithOptions. +struct Thread::StartupData { + // We get away with a const reference here because of how we are allocated. + const Thread::Options& options; + + // Used to synchronize thread startup. + WaitableEvent event; + + explicit StartupData(const Options& opt) + : options(opt), + event(false, false) {} +}; + +Thread::Thread(const char *name) + : startup_data_(NULL), + thread_(0), + message_loop_(NULL), + thread_id_(0), + name_(name) { +} + +Thread::~Thread() { + Stop(); +} + +namespace { + +// We use this thread-local variable to record whether or not a thread exited +// because its Stop method was called. This allows us to catch cases where +// MessageLoop::Quit() is called directly, which is unexpected when using a +// Thread to setup and run a MessageLoop. +base::LazyInstance lazy_tls_bool( + base::LINKER_INITIALIZED); + +} // namespace + +void Thread::SetThreadWasQuitProperly(bool flag) { + lazy_tls_bool.Pointer()->Set(flag); +} + +bool Thread::GetThreadWasQuitProperly() { + bool quit_properly = true; +#ifndef NDEBUG + quit_properly = lazy_tls_bool.Pointer()->Get(); +#endif + return quit_properly; +} + +bool Thread::Start() { + return StartWithOptions(Options()); +} + +bool Thread::StartWithOptions(const Options& options) { + DCHECK(!message_loop_); + + SetThreadWasQuitProperly(false); + + StartupData startup_data(options); + startup_data_ = &startup_data; + + if (!PlatformThread::Create(options.stack_size, this, &thread_)) { + DLOG(ERROR) << "failed to create thread"; + startup_data_ = NULL; // Record that we failed to start. + return false; + } + + // Wait for the thread to start and initialize message_loop_ + startup_data.event.Wait(); + + DCHECK(message_loop_); + return true; +} + +void Thread::Stop() { + if (!thread_was_started()) + return; + + // We should only be called on the same thread that started us. + DCHECK_NE(thread_id_, PlatformThread::CurrentId()); + + // StopSoon may have already been called. + if (message_loop_) + message_loop_->PostTask(FROM_HERE, new ThreadQuitTask()); + + // Wait for the thread to exit. It should already have terminated but make + // sure this assumption is valid. + // + // TODO(darin): Unfortunately, we need to keep message_loop_ around until + // the thread exits. Some consumers are abusing the API. Make them stop. + // + PlatformThread::Join(thread_); + + // The thread can't receive messages anymore. + message_loop_ = NULL; + + // The thread no longer needs to be joined. + startup_data_ = NULL; +} + +void Thread::StopSoon() { + if (!message_loop_) + return; + + // We should only be called on the same thread that started us. + DCHECK_NE(thread_id_, PlatformThread::CurrentId()); + + // We had better have a message loop at this point! If we do not, then it + // most likely means that the thread terminated unexpectedly, probably due + // to someone calling Quit() on our message loop directly. + DCHECK(message_loop_); + + message_loop_->PostTask(FROM_HERE, new ThreadQuitTask()); +} + +void Thread::ThreadMain() { + // The message loop for this thread. + MessageLoop message_loop(startup_data_->options.message_loop_type); + + // Complete the initialization of our Thread object. + thread_id_ = PlatformThread::CurrentId(); + PlatformThread::SetName(name_.c_str()); + message_loop.set_thread_name(name_); + message_loop_ = &message_loop; + +#if defined(CHROMIUM_MOZILLA_BUILD) + bool wait_for_init = startup_data_->options.wait_for_init; + if (!wait_for_init) + startup_data_->event.Signal(); +#endif + + // Let the thread do extra initialization. + // Let's do this before signaling we are started. + Init(); + +#if defined(CHROMIUM_MOZILLA_BUILD) + if (wait_for_init) +#endif + startup_data_->event.Signal(); + // startup_data_ can't be touched anymore since the starting thread is now + // unlocked. + + message_loop.Run(); + + // Let the thread do extra cleanup. + CleanUp(); + + // Assert that MessageLoop::Quit was called by ThreadQuitTask. + DCHECK(GetThreadWasQuitProperly()); + + // We can't receive messages anymore. + message_loop_ = NULL; + thread_id_ = 0; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_collision_warner.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_collision_warner.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_collision_warner.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_collision_warner.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,64 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/thread_collision_warner.h" + +#include "base/logging.h" +#include "base/platform_thread.h" + +namespace base { + +void DCheckAsserter::warn() { + NOTREACHED() << "Thread Collision"; +} + +static subtle::Atomic32 CurrentThread() { + const PlatformThreadId current_thread_id = PlatformThread::CurrentId(); + // We need to get the thread id into an atomic data type. This might be a + // truncating conversion, but any loss-of-information just increases the + // chance of a fault negative, not a false positive. + const subtle::Atomic32 atomic_thread_id = + static_cast(current_thread_id); + + return atomic_thread_id; +} + +void ThreadCollisionWarner::EnterSelf() { + // If the active thread is 0 then I'll write the current thread ID + // if two or more threads arrive here only one will succeed to + // write on valid_thread_id_ the current thread ID. + subtle::Atomic32 current_thread_id = CurrentThread(); + + int previous_value = subtle::NoBarrier_CompareAndSwap(&valid_thread_id_, + 0, + current_thread_id); + if (previous_value != 0 && previous_value != current_thread_id) { + // gotcha! a thread is trying to use the same class and that is + // not current thread. + asserter_->warn(); + } + + subtle::NoBarrier_AtomicIncrement(&counter_, 1); +} + +void ThreadCollisionWarner::Enter() { + subtle::Atomic32 current_thread_id = CurrentThread(); + + if (subtle::NoBarrier_CompareAndSwap(&valid_thread_id_, + 0, + current_thread_id) != 0) { + // gotcha! another thread is trying to use the same class. + asserter_->warn(); + } + + subtle::NoBarrier_AtomicIncrement(&counter_, 1); +} + +void ThreadCollisionWarner::Leave() { + if (subtle::Barrier_AtomicIncrement(&counter_, -1) == 0) { + subtle::NoBarrier_Store(&valid_thread_id_, 0); + } +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_collision_warner.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_collision_warner.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_collision_warner.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_collision_warner.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,242 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_THREAD_COLLISION_WARNER_H_ +#define BASE_THREAD_COLLISION_WARNER_H_ + +#include + +#include "base/atomicops.h" + +// A helper class alongside macros to be used to verify assumptions about thread +// safety of a class. +// +// Example: Queue implementation non thread-safe but still usable if clients +// are synchronized somehow. +// +// In this case the macro DFAKE_SCOPED_LOCK has to be +// used, it checks that if a thread is inside the push/pop then +// noone else is still inside the pop/push +// +// class NonThreadSafeQueue { +// public: +// ... +// void push(int) { DFAKE_SCOPED_LOCK(push_pop_); ... } +// int pop() { DFAKE_SCOPED_LOCK(push_pop_); ... } +// ... +// private: +// DFAKE_MUTEX(push_pop_); +// }; +// +// +// Example: Queue implementation non thread-safe but still usable if clients +// are synchronized somehow, it calls a method to "protect" from +// a "protected" method +// +// In this case the macro DFAKE_SCOPED_RECURSIVE_LOCK +// has to be used, it checks that if a thread is inside the push/pop +// then noone else is still inside the pop/push +// +// class NonThreadSafeQueue { +// public: +// void push(int) { +// DFAKE_SCOPED_LOCK(push_pop_); +// ... +// } +// int pop() { +// DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); +// bar(); +// ... +// } +// void bar() { DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); ... } +// ... +// private: +// DFAKE_MUTEX(push_pop_); +// }; +// +// +// Example: Queue implementation not usable even if clients are synchronized, +// so only one thread in the class life cycle can use the two members +// push/pop. +// +// In this case the macro DFAKE_SCOPED_LOCK_THREAD_LOCKED pins the +// specified +// critical section the first time a thread enters push or pop, from +// that time on only that thread is allowed to execute push or pop. +// +// class NonThreadSafeQueue { +// public: +// ... +// void push(int) { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } +// int pop() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); ... } +// ... +// private: +// DFAKE_MUTEX(push_pop_); +// }; +// +// +// Example: Class that has to be contructed/destroyed on same thread, it has +// a "shareable" method (with external syncronization) and a not +// shareable method (even with external synchronization). +// +// In this case 3 Critical sections have to be defined +// +// class ExoticClass { +// public: +// ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } +// ~ExoticClass() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } +// +// void Shareable() { DFAKE_SCOPED_LOCK(shareable_section_); ... } +// void NotShareable() { DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); ... } +// ... +// private: +// DFAKE_MUTEX(ctor_dtor_); +// DFAKE_MUTEX(shareable_section_); +// }; + + +#if !defined(NDEBUG) + +// Defines a class member that acts like a mutex. It is used only as a +// verification tool. +#define DFAKE_MUTEX(obj) \ + mutable base::ThreadCollisionWarner obj +// Asserts the call is never called simultaneously in two threads. Used at +// member function scope. +#define DFAKE_SCOPED_LOCK(obj) \ + base::ThreadCollisionWarner::ScopedCheck s_check_##obj(&obj) +// Asserts the call is never called simultaneously in two threads. Used at +// member function scope. Same as DFAKE_SCOPED_LOCK but allows recursive locks. +#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) \ + base::ThreadCollisionWarner::ScopedRecursiveCheck sr_check_##obj(&obj) +// Asserts the code is always executed in the same thread. +#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) \ + base::ThreadCollisionWarner::Check check_##obj(&obj) + +#else + +#define DFAKE_MUTEX(obj) +#define DFAKE_SCOPED_LOCK(obj) ((void)0) +#define DFAKE_SCOPED_RECURSIVE_LOCK(obj) ((void)0) +#define DFAKE_SCOPED_LOCK_THREAD_LOCKED(obj) ((void)0) + +#endif + +namespace base { + +// The class ThreadCollisionWarner uses an Asserter to notify the collision +// AsserterBase is the interfaces and DCheckAsserter is the default asserter +// used. During the unit tests is used another class that doesn't "DCHECK" +// in case of collision (check thread_collision_warner_unittests.cc) +struct AsserterBase { + virtual ~AsserterBase() {} + virtual void warn() = 0; +}; + +struct DCheckAsserter : public AsserterBase { + virtual ~DCheckAsserter() {} + virtual void warn(); +}; + +class ThreadCollisionWarner { + public: + // The parameter asserter is there only for test purpose + ThreadCollisionWarner(AsserterBase* asserter = new DCheckAsserter()) + : valid_thread_id_(0), + counter_(0), + asserter_(asserter) {} + + ~ThreadCollisionWarner() { + delete asserter_; + } + + // This class is meant to be used through the macro + // DFAKE_SCOPED_LOCK_THREAD_LOCKED + // it doesn't leave the critical section, as opposed to ScopedCheck, + // because the critical section being pinned is allowed to be used only + // from one thread + class Check { + public: + explicit Check(ThreadCollisionWarner* warner) + : warner_(warner) { + warner_->EnterSelf(); + } + + ~Check() {} + + private: + ThreadCollisionWarner* warner_; + + DISALLOW_COPY_AND_ASSIGN(Check); + }; + + // This class is meant to be used through the macro + // DFAKE_SCOPED_LOCK + class ScopedCheck { + public: + explicit ScopedCheck(ThreadCollisionWarner* warner) + : warner_(warner) { + warner_->Enter(); + } + + ~ScopedCheck() { + warner_->Leave(); + } + + private: + ThreadCollisionWarner* warner_; + + DISALLOW_COPY_AND_ASSIGN(ScopedCheck); + }; + + // This class is meant to be used through the macro + // DFAKE_SCOPED_RECURSIVE_LOCK + class ScopedRecursiveCheck { + public: + explicit ScopedRecursiveCheck(ThreadCollisionWarner* warner) + : warner_(warner) { + warner_->EnterSelf(); + } + + ~ScopedRecursiveCheck() { + warner_->Leave(); + } + + private: + ThreadCollisionWarner* warner_; + + DISALLOW_COPY_AND_ASSIGN(ScopedRecursiveCheck); + }; + + private: + // This method stores the current thread identifier and does a DCHECK + // if a another thread has already done it, it is safe if same thread + // calls this multiple time (recursion allowed). + void EnterSelf(); + + // Same as EnterSelf but recursion is not allowed. + void Enter(); + + // Removes the thread_id stored in order to allow other threads to + // call EnterSelf or Enter. + void Leave(); + + // This stores the thread id that is inside the critical section, if the + // value is 0 then no thread is inside. + volatile subtle::Atomic32 valid_thread_id_; + + // Counter to trace how many time a critical section was "pinned" + // (when allowed) in order to unpin it when counter_ reaches 0. + volatile subtle::Atomic32 counter_; + + // Here only for class unit tests purpose, during the test I need to not + // DCHECK but notify the collision with something else. + AsserterBase* asserter_; + + DISALLOW_COPY_AND_ASSIGN(ThreadCollisionWarner); +}; + +} // namespace base + +#endif // BASE_THREAD_COLLISION_WARNER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_collision_warner_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_collision_warner_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_collision_warner_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_collision_warner_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,384 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/compiler_specific.h" +#include "base/lock.h" +#include "base/platform_thread.h" +#include "base/simple_thread.h" +#include "base/thread_collision_warner.h" +#include "testing/gtest/include/gtest/gtest.h" + +// '' : local class member function does not have a body +MSVC_PUSH_DISABLE_WARNING(4822) + + +#if defined(NDEBUG) + +// Would cause a memory leak otherwise. +#undef DFAKE_MUTEX +#define DFAKE_MUTEX(obj) scoped_ptr obj + +// In Release, we expect the AsserterBase::warn() to not happen. +#define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_FALSE + +#else + +// In Debug, we expect the AsserterBase::warn() to happen. +#define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_TRUE + +#endif + + +namespace { + +// This is the asserter used with ThreadCollisionWarner instead of the default +// DCheckAsserter. The method fail_state is used to know if a collision took +// place. +class AssertReporter : public base::AsserterBase { + public: + AssertReporter() + : failed_(false) {} + + virtual void warn() { + failed_ = true; + } + + virtual ~AssertReporter() {} + + bool fail_state() const { return failed_; } + void reset() { failed_ = false; } + + private: + bool failed_; +}; + +} // namespace + +TEST(ThreadCollisionTest, BookCriticalSection) { + AssertReporter* local_reporter = new AssertReporter(); + + base::ThreadCollisionWarner warner(local_reporter); + EXPECT_FALSE(local_reporter->fail_state()); + + { // Pin section. + DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner); + EXPECT_FALSE(local_reporter->fail_state()); + { // Pin section. + DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner); + EXPECT_FALSE(local_reporter->fail_state()); + } + } +} + +TEST(ThreadCollisionTest, ScopedRecursiveBookCriticalSection) { + AssertReporter* local_reporter = new AssertReporter(); + + base::ThreadCollisionWarner warner(local_reporter); + EXPECT_FALSE(local_reporter->fail_state()); + + { // Pin section. + DFAKE_SCOPED_RECURSIVE_LOCK(warner); + EXPECT_FALSE(local_reporter->fail_state()); + { // Pin section again (allowed by DFAKE_SCOPED_RECURSIVE_LOCK) + DFAKE_SCOPED_RECURSIVE_LOCK(warner); + EXPECT_FALSE(local_reporter->fail_state()); + } // Unpin section. + } // Unpin section. + + // Check that section is not pinned + { // Pin section. + DFAKE_SCOPED_LOCK(warner); + EXPECT_FALSE(local_reporter->fail_state()); + } // Unpin section. +} + +TEST(ThreadCollisionTest, ScopedBookCriticalSection) { + AssertReporter* local_reporter = new AssertReporter(); + + base::ThreadCollisionWarner warner(local_reporter); + EXPECT_FALSE(local_reporter->fail_state()); + + { // Pin section. + DFAKE_SCOPED_LOCK(warner); + EXPECT_FALSE(local_reporter->fail_state()); + } // Unpin section. + + { // Pin section. + DFAKE_SCOPED_LOCK(warner); + EXPECT_FALSE(local_reporter->fail_state()); + { + // Pin section again (not allowed by DFAKE_SCOPED_LOCK) + DFAKE_SCOPED_LOCK(warner); + EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state()); + // Reset the status of warner for further tests. + local_reporter->reset(); + } // Unpin section. + } // Unpin section. + + { + // Pin section. + DFAKE_SCOPED_LOCK(warner); + EXPECT_FALSE(local_reporter->fail_state()); + } // Unpin section. +} + +TEST(ThreadCollisionTest, MTBookCriticalSectionTest) { + class NonThreadSafeQueue { + public: + explicit NonThreadSafeQueue(base::AsserterBase* asserter) + : push_pop_(asserter) { + } + + void push(int value) { + DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); + } + + int pop() { + DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_); + return 0; + } + + private: + DFAKE_MUTEX(push_pop_); + + DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue); + }; + + class QueueUser : public base::DelegateSimpleThread::Delegate { + public: + explicit QueueUser(NonThreadSafeQueue& queue) + : queue_(queue) {} + + virtual void Run() { + queue_.push(0); + queue_.pop(); + } + + private: + NonThreadSafeQueue& queue_; + }; + + AssertReporter* local_reporter = new AssertReporter(); + + NonThreadSafeQueue queue(local_reporter); + + QueueUser queue_user_a(queue); + QueueUser queue_user_b(queue); + + base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a"); + base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b"); + + thread_a.Start(); + thread_b.Start(); + + thread_a.Join(); + thread_b.Join(); + + EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state()); +} + +TEST(ThreadCollisionTest, MTScopedBookCriticalSectionTest) { + // Queue with a 5 seconds push execution time, hopefuly the two used threads + // in the test will enter the push at same time. + class NonThreadSafeQueue { + public: + explicit NonThreadSafeQueue(base::AsserterBase* asserter) + : push_pop_(asserter) { + } + + void push(int value) { + DFAKE_SCOPED_LOCK(push_pop_); + PlatformThread::Sleep(5000); + } + + int pop() { + DFAKE_SCOPED_LOCK(push_pop_); + return 0; + } + + private: + DFAKE_MUTEX(push_pop_); + + DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue); + }; + + class QueueUser : public base::DelegateSimpleThread::Delegate { + public: + explicit QueueUser(NonThreadSafeQueue& queue) + : queue_(queue) {} + + virtual void Run() { + queue_.push(0); + queue_.pop(); + } + + private: + NonThreadSafeQueue& queue_; + }; + + AssertReporter* local_reporter = new AssertReporter(); + + NonThreadSafeQueue queue(local_reporter); + + QueueUser queue_user_a(queue); + QueueUser queue_user_b(queue); + + base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a"); + base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b"); + + thread_a.Start(); + thread_b.Start(); + + thread_a.Join(); + thread_b.Join(); + + EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state()); +} + +TEST(ThreadCollisionTest, MTSynchedScopedBookCriticalSectionTest) { + // Queue with a 5 seconds push execution time, hopefuly the two used threads + // in the test will enter the push at same time. + class NonThreadSafeQueue { + public: + explicit NonThreadSafeQueue(base::AsserterBase* asserter) + : push_pop_(asserter) { + } + + void push(int value) { + DFAKE_SCOPED_LOCK(push_pop_); + PlatformThread::Sleep(5000); + } + + int pop() { + DFAKE_SCOPED_LOCK(push_pop_); + return 0; + } + + private: + DFAKE_MUTEX(push_pop_); + + DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue); + }; + + // This time the QueueUser class protects the non thread safe queue with + // a lock. + class QueueUser : public base::DelegateSimpleThread::Delegate { + public: + QueueUser(NonThreadSafeQueue& queue, Lock& lock) + : queue_(queue), + lock_(lock) {} + + virtual void Run() { + { + AutoLock auto_lock(lock_); + queue_.push(0); + } + { + AutoLock auto_lock(lock_); + queue_.pop(); + } + } + private: + NonThreadSafeQueue& queue_; + Lock& lock_; + }; + + AssertReporter* local_reporter = new AssertReporter(); + + NonThreadSafeQueue queue(local_reporter); + + Lock lock; + + QueueUser queue_user_a(queue, lock); + QueueUser queue_user_b(queue, lock); + + base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a"); + base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b"); + + thread_a.Start(); + thread_b.Start(); + + thread_a.Join(); + thread_b.Join(); + + EXPECT_FALSE(local_reporter->fail_state()); +} + +TEST(ThreadCollisionTest, MTSynchedScopedRecursiveBookCriticalSectionTest) { + // Queue with a 5 seconds push execution time, hopefuly the two used threads + // in the test will enter the push at same time. + class NonThreadSafeQueue { + public: + explicit NonThreadSafeQueue(base::AsserterBase* asserter) + : push_pop_(asserter) { + } + + void push(int) { + DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); + bar(); + PlatformThread::Sleep(5000); + } + + int pop() { + DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); + return 0; + } + + void bar() { + DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_); + } + + private: + DFAKE_MUTEX(push_pop_); + + DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue); + }; + + // This time the QueueUser class protects the non thread safe queue with + // a lock. + class QueueUser : public base::DelegateSimpleThread::Delegate { + public: + QueueUser(NonThreadSafeQueue& queue, Lock& lock) + : queue_(queue), + lock_(lock) {} + + virtual void Run() { + { + AutoLock auto_lock(lock_); + queue_.push(0); + } + { + AutoLock auto_lock(lock_); + queue_.bar(); + } + { + AutoLock auto_lock(lock_); + queue_.pop(); + } + } + private: + NonThreadSafeQueue& queue_; + Lock& lock_; + }; + + AssertReporter* local_reporter = new AssertReporter(); + + NonThreadSafeQueue queue(local_reporter); + + Lock lock; + + QueueUser queue_user_a(queue, lock); + QueueUser queue_user_b(queue, lock); + + base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a"); + base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b"); + + thread_a.Start(); + thread_b.Start(); + + thread_a.Join(); + thread_b.Join(); + + EXPECT_FALSE(local_reporter->fail_state()); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,181 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_THREAD_H_ +#define BASE_THREAD_H_ + +#include + +#include "base/message_loop.h" +#include "base/platform_thread.h" + +namespace base { + +// A simple thread abstraction that establishes a MessageLoop on a new thread. +// The consumer uses the MessageLoop of the thread to cause code to execute on +// the thread. When this object is destroyed the thread is terminated. All +// pending tasks queued on the thread's message loop will run to completion +// before the thread is terminated. +class Thread : PlatformThread::Delegate { + public: + struct Options { + // Specifies the type of message loop that will be allocated on the thread. + MessageLoop::Type message_loop_type; + + // Specifies the maximum stack size that the thread is allowed to use. + // This does not necessarily correspond to the thread's initial stack size. + // A value of 0 indicates that the default maximum should be used. + size_t stack_size; + +#if defined(CHROMIUM_MOZILLA_BUILD) + // Specifies whether the thread that launched this sub-thread + // should wait for the sub-thread's Init() routine to finish + // before dropping into the event loop. If it is false, the + // sub-thread will signal just before calling Init(). If true, + // the sub-thread will signal just after. + bool wait_for_init; + + Options() : + message_loop_type(MessageLoop::TYPE_DEFAULT), + stack_size(0), + wait_for_init(true) + {} + Options(MessageLoop::Type type, size_t size) : + message_loop_type(type), + stack_size(size), + wait_for_init(true) + {} + Options(MessageLoop::Type type, size_t size, bool wait_init) : + message_loop_type(type), + stack_size(size), + wait_for_init(wait_init) + {} + +#else + Options() : message_loop_type(MessageLoop::TYPE_DEFAULT), stack_size(0) {} + Options(MessageLoop::Type type, size_t size) + : message_loop_type(type), stack_size(size) {} +#endif + }; + + // Constructor. + // name is a display string to identify the thread. + explicit Thread(const char *name); + + // Destroys the thread, stopping it if necessary. + // + // NOTE: If you are subclassing from Thread, and you wish for your CleanUp + // method to be called, then you need to call Stop() from your destructor. + // + virtual ~Thread(); + + // Starts the thread. Returns true if the thread was successfully started; + // otherwise, returns false. Upon successful return, the message_loop() + // getter will return non-null. + // + // Note: This function can't be called on Windows with the loader lock held; + // i.e. during a DllMain, global object construction or destruction, atexit() + // callback. + bool Start(); + + // Starts the thread. Behaves exactly like Start in addition to allow to + // override the default options. + // + // Note: This function can't be called on Windows with the loader lock held; + // i.e. during a DllMain, global object construction or destruction, atexit() + // callback. + bool StartWithOptions(const Options& options); + + // Signals the thread to exit and returns once the thread has exited. After + // this method returns, the Thread object is completely reset and may be used + // as if it were newly constructed (i.e., Start may be called again). + // + // Stop may be called multiple times and is simply ignored if the thread is + // already stopped. + // + // NOTE: This method is optional. It is not strictly necessary to call this + // method as the Thread's destructor will take care of stopping the thread if + // necessary. + // + void Stop(); + + // Signals the thread to exit in the near future. + // + // WARNING: This function is not meant to be commonly used. Use at your own + // risk. Calling this function will cause message_loop() to become invalid in + // the near future. This function was created to workaround a specific + // deadlock on Windows with printer worker thread. In any other case, Stop() + // should be used. + // + // StopSoon should not be called multiple times as it is risky to do so. It + // could cause a timing issue in message_loop() access. Call Stop() to reset + // the thread object once it is known that the thread has quit. + void StopSoon(); + + // Returns the message loop for this thread. Use the MessageLoop's + // PostTask methods to execute code on the thread. This only returns + // non-null after a successful call to Start. After Stop has been called, + // this will return NULL. + // + // NOTE: You must not call this MessageLoop's Quit method directly. Use + // the Thread's Stop method instead. + // + MessageLoop* message_loop() const { return message_loop_; } + + // Set the name of this thread (for display in debugger too). + const std::string &thread_name() { return name_; } + + // The native thread handle. + PlatformThreadHandle thread_handle() { return thread_; } + + // The thread ID. + PlatformThreadId thread_id() const { return thread_id_; } + + // Returns true if the thread has been started, and not yet stopped. + // When a thread is running, the thread_id_ is non-zero. + bool IsRunning() const { return thread_id_ != 0; } + + protected: + // Called just prior to starting the message loop + virtual void Init() {} + + // Called just after the message loop ends + virtual void CleanUp() {} + + static void SetThreadWasQuitProperly(bool flag); + static bool GetThreadWasQuitProperly(); + + private: + // PlatformThread::Delegate methods: + virtual void ThreadMain(); + + // We piggy-back on the startup_data_ member to know if we successfully + // started the thread. This way we know that we need to call Join. + bool thread_was_started() const { return startup_data_ != NULL; } + + // Used to pass data to ThreadMain. + struct StartupData; + StartupData* startup_data_; + + // The thread's handle. + PlatformThreadHandle thread_; + + // The thread's message loop. Valid only while the thread is alive. Set + // by the created thread. + MessageLoop* message_loop_; + + // Our thread's ID. + PlatformThreadId thread_id_; + + // The name of the thread. Used for debugging purposes. + std::string name_; + + friend class ThreadQuitTask; + + DISALLOW_COPY_AND_ASSIGN(Thread); +}; + +} // namespace base + +#endif // BASE_THREAD_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_local.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_local.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_local.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_local.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,121 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// WARNING: Thread local storage is a bit tricky to get right. Please make +// sure that this is really the proper solution for what you're trying to +// achieve. Don't prematurely optimize, most likely you can just use a Lock. +// +// These classes implement a warpper around the platform's TLS storage +// mechanism. On construction, they will allocate a TLS slot, and free the +// TLS slot on destruction. No memory management (creation or destruction) is +// handled. This means for uses of ThreadLocalPointer, you must correctly +// manage the memory yourself, these classes will not destroy the pointer for +// you. There are no at-thread-exit actions taken by these classes. +// +// ThreadLocalPointer wraps a Type*. It performs no creation or +// destruction, so memory management must be handled elsewhere. The first call +// to Get() on a thread will return NULL. You can update the pointer with a +// call to Set(). +// +// ThreadLocalBoolean wraps a bool. It will default to false if it has never +// been set otherwise with Set(). +// +// Thread Safety: An instance of ThreadLocalStorage is completely thread safe +// once it has been created. If you want to dynamically create an instance, +// you must of course properly deal with safety and race conditions. This +// means a function-level static initializer is generally inappropiate. +// +// Example usage: +// // My class is logically attached to a single thread. We cache a pointer +// // on the thread it was created on, so we can implement current(). +// MyClass::MyClass() { +// DCHECK(Singleton >::get()->Get() == NULL); +// Singleton >::get()->Set(this); +// } +// +// MyClass::~MyClass() { +// DCHECK(Singleton >::get()->Get() != NULL); +// Singleton >::get()->Set(NULL); +// } +// +// // Return the current MyClass associated with the calling thread, can be +// // NULL if there isn't a MyClass associated. +// MyClass* MyClass::current() { +// return Singleton >::get()->Get(); +// } + +#ifndef BASE_THREAD_LOCAL_H_ +#define BASE_THREAD_LOCAL_H_ + +#include "base/basictypes.h" + +#if defined(OS_POSIX) +#include +#endif + +namespace base { + +// Helper functions that abstract the cross-platform APIs. Do not use directly. +struct ThreadLocalPlatform { +#if defined(OS_WIN) + typedef int SlotType; +#elif defined(OS_POSIX) + typedef pthread_key_t SlotType; +#endif + + static void AllocateSlot(SlotType& slot); + static void FreeSlot(SlotType& slot); + static void* GetValueFromSlot(SlotType& slot); + static void SetValueInSlot(SlotType& slot, void* value); +}; + +template +class ThreadLocalPointer { + public: + ThreadLocalPointer() : slot_() { + ThreadLocalPlatform::AllocateSlot(slot_); + } + + ~ThreadLocalPointer() { + ThreadLocalPlatform::FreeSlot(slot_); + } + + Type* Get() { + return static_cast(ThreadLocalPlatform::GetValueFromSlot(slot_)); + } + + void Set(Type* ptr) { + ThreadLocalPlatform::SetValueInSlot(slot_, ptr); + } + + private: + typedef ThreadLocalPlatform::SlotType SlotType; + + SlotType slot_; + + DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer); +}; + +class ThreadLocalBoolean { + public: + ThreadLocalBoolean() { } + ~ThreadLocalBoolean() { } + + bool Get() { + return tlp_.Get() != NULL; + } + + void Set(bool val) { + tlp_.Set(reinterpret_cast(val ? 1 : 0)); + } + + private: + ThreadLocalPointer tlp_; + + DISALLOW_COPY_AND_ASSIGN(ThreadLocalBoolean); +}; + +} // namespace base + +#endif // BASE_THREAD_LOCAL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_local_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_local_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_local_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_local_posix.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,36 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/thread_local.h" + +#include + +#include "base/logging.h" + +namespace base { + +// static +void ThreadLocalPlatform::AllocateSlot(SlotType& slot) { + int error = pthread_key_create(&slot, NULL); + CHECK(error == 0); +} + +// static +void ThreadLocalPlatform::FreeSlot(SlotType& slot) { + int error = pthread_key_delete(slot); + DCHECK(error == 0); +} + +// static +void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) { + return pthread_getspecific(slot); +} + +// static +void ThreadLocalPlatform::SetValueInSlot(SlotType& slot, void* value) { + int error = pthread_setspecific(slot, value); + CHECK(error == 0); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_local_storage.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_local_storage.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_local_storage.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_local_storage.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,94 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_THREAD_LOCAL_STORAGE_H_ +#define BASE_THREAD_LOCAL_STORAGE_H_ + +#include "base/basictypes.h" + +#if defined(OS_POSIX) +#include +#endif + +// Wrapper for thread local storage. This class doesn't do much except provide +// an API for portability. +class ThreadLocalStorage { + public: + + // Prototype for the TLS destructor function, which can be optionally used to + // cleanup thread local storage on thread exit. 'value' is the data that is + // stored in thread local storage. + typedef void (*TLSDestructorFunc)(void* value); + + // A key representing one value stored in TLS. + class Slot { + public: + Slot(TLSDestructorFunc destructor = NULL); + + // This constructor should be used for statics. + // It returns an uninitialized Slot. + explicit Slot(base::LinkerInitialized x) {} + + // Set up the TLS slot. Called by the constructor. + // 'destructor' is a pointer to a function to perform per-thread cleanup of + // this object. If set to NULL, no cleanup is done for this TLS slot. + // Returns false on error. + bool Initialize(TLSDestructorFunc destructor); + + // Free a previously allocated TLS 'slot'. + // If a destructor was set for this slot, removes + // the destructor so that remaining threads exiting + // will not free data. + void Free(); + + // Get the thread-local value stored in slot 'slot'. + // Values are guaranteed to initially be zero. + void* Get() const; + + // Set the thread-local value stored in slot 'slot' to + // value 'value'. + void Set(void* value); + + bool initialized() const { return initialized_; } + + private: + // The internals of this struct should be considered private. + bool initialized_; +#if defined(OS_WIN) + int slot_; +#elif defined(OS_POSIX) + pthread_key_t key_; +#endif + + DISALLOW_COPY_AND_ASSIGN(Slot); + }; + +#if defined(OS_WIN) + // Function called when on thread exit to call TLS + // destructor functions. This function is used internally. + static void ThreadExit(); + + private: + // Function to lazily initialize our thread local storage. + static void **Initialize(); + + private: + // The maximum number of 'slots' in our thread local storage stack. + // For now, this is fixed. We could either increase statically, or + // we could make it dynamic in the future. + static const int kThreadLocalStorageSize = 64; + + static long tls_key_; + static long tls_max_; + static TLSDestructorFunc tls_destructors_[kThreadLocalStorageSize]; +#endif // OS_WIN + + DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorage); +}; + +// Temporary backwards-compatible name. +// TODO(evanm): replace all usage of TLSSlot. +typedef ThreadLocalStorage::Slot TLSSlot; + +#endif // BASE_THREAD_LOCAL_STORAGE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_local_storage_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_local_storage_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_local_storage_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_local_storage_posix.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,44 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/thread_local_storage.h" + +#include "base/logging.h" + +ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) + : initialized_(false) { + Initialize(destructor); +} + +bool ThreadLocalStorage::Slot::Initialize(TLSDestructorFunc destructor) { + DCHECK(!initialized_); + int error = pthread_key_create(&key_, destructor); + if (error) { + NOTREACHED(); + return false; + } + + initialized_ = true; + return true; +} + +void ThreadLocalStorage::Slot::Free() { + DCHECK(initialized_); + int error = pthread_key_delete(key_); + if (error) + NOTREACHED(); + initialized_ = false; +} + +void* ThreadLocalStorage::Slot::Get() const { + DCHECK(initialized_); + return pthread_getspecific(key_); +} + +void ThreadLocalStorage::Slot::Set(void* value) { + DCHECK(initialized_); + int error = pthread_setspecific(key_, value); + if (error) + NOTREACHED(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_local_storage_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_local_storage_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_local_storage_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_local_storage_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,94 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if defined(OS_WIN) +#include +#include +#endif + +#include "base/simple_thread.h" +#include "base/thread_local_storage.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(OS_WIN) +// Ignore warnings about ptr->int conversions that we use when +// storing ints into ThreadLocalStorage. +#pragma warning(disable : 4311 4312) +#endif + +const int kInitialTlsValue = 0x5555; +static ThreadLocalStorage::Slot tls_slot(base::LINKER_INITIALIZED); + + +class ThreadLocalStorageRunner : public base::DelegateSimpleThread::Delegate { + public: + explicit ThreadLocalStorageRunner(int* tls_value_ptr) + : tls_value_ptr_(tls_value_ptr) {} + + virtual ~ThreadLocalStorageRunner() {} + + virtual void Run() { + *tls_value_ptr_ = kInitialTlsValue; + tls_slot.Set(tls_value_ptr_); + + int *ptr = static_cast(tls_slot.Get()); + EXPECT_EQ(ptr, tls_value_ptr_); + EXPECT_EQ(*ptr, kInitialTlsValue); + *tls_value_ptr_ = 0; + + ptr = static_cast(tls_slot.Get()); + EXPECT_EQ(ptr, tls_value_ptr_); + EXPECT_EQ(*ptr, 0); + } + + private: + int* tls_value_ptr_; + DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorageRunner); +}; + + +void ThreadLocalStorageCleanup(void *value) { + int *ptr = reinterpret_cast(value); + if (ptr) + *ptr = kInitialTlsValue; +} + + +TEST(ThreadLocalStorageTest, Basics) { + ThreadLocalStorage::Slot slot; + slot.Set(reinterpret_cast(123)); + int value = reinterpret_cast(slot.Get()); + EXPECT_EQ(value, 123); +} + +TEST(ThreadLocalStorageTest, TLSDestructors) { + // Create a TLS index with a destructor. Create a set of + // threads that set the TLS, while the destructor cleans it up. + // After the threads finish, verify that the value is cleaned up. + const int kNumThreads = 5; + int values[kNumThreads]; + ThreadLocalStorageRunner* thread_delegates[kNumThreads]; + base::DelegateSimpleThread* threads[kNumThreads]; + + tls_slot.Initialize(ThreadLocalStorageCleanup); + + // Spawn the threads. + for (int index = 0; index < kNumThreads; index++) { + values[index] = kInitialTlsValue; + thread_delegates[index] = new ThreadLocalStorageRunner(&values[index]); + threads[index] = new base::DelegateSimpleThread(thread_delegates[index], + "tls thread"); + threads[index]->Start(); + } + + // Wait for the threads to finish. + for (int index = 0; index < kNumThreads; index++) { + threads[index]->Join(); + delete threads[index]; + delete thread_delegates[index]; + + // Verify that the destructor was called and that we reset. + EXPECT_EQ(values[index], kInitialTlsValue); + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_local_storage_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_local_storage_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_local_storage_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_local_storage_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,186 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/thread_local_storage.h" + +#include + +#include "base/logging.h" + +// In order to make TLS destructors work, we need to keep function +// pointers to the destructor for each TLS that we allocate. +// We make this work by allocating a single OS-level TLS, which +// contains an array of slots for the application to use. In +// parallel, we also allocate an array of destructors, which we +// keep track of and call when threads terminate. + +// tls_key_ is the one native TLS that we use. It stores our +// table. +long ThreadLocalStorage::tls_key_ = TLS_OUT_OF_INDEXES; + +// tls_max_ is the high-water-mark of allocated thread local storage. +// We intentionally skip 0 so that it is not confused with an +// unallocated TLS slot. +long ThreadLocalStorage::tls_max_ = 1; + +// An array of destructor function pointers for the slots. If +// a slot has a destructor, it will be stored in its corresponding +// entry in this array. +ThreadLocalStorage::TLSDestructorFunc + ThreadLocalStorage::tls_destructors_[kThreadLocalStorageSize]; + +void** ThreadLocalStorage::Initialize() { + if (tls_key_ == TLS_OUT_OF_INDEXES) { + long value = TlsAlloc(); + DCHECK(value != TLS_OUT_OF_INDEXES); + + // Atomically test-and-set the tls_key. If the key is TLS_OUT_OF_INDEXES, + // go ahead and set it. Otherwise, do nothing, as another + // thread already did our dirty work. + if (InterlockedCompareExchange(&tls_key_, value, TLS_OUT_OF_INDEXES) != + TLS_OUT_OF_INDEXES) { + // We've been shortcut. Another thread replaced tls_key_ first so we need + // to destroy our index and use the one the other thread got first. + TlsFree(value); + } + } + DCHECK(TlsGetValue(tls_key_) == NULL); + + // Create an array to store our data. + void** tls_data = new void*[kThreadLocalStorageSize]; + memset(tls_data, 0, sizeof(void*[kThreadLocalStorageSize])); + TlsSetValue(tls_key_, tls_data); + return tls_data; +} + +ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) + : initialized_(false) { + Initialize(destructor); +} + +bool ThreadLocalStorage::Slot::Initialize(TLSDestructorFunc destructor) { + if (tls_key_ == TLS_OUT_OF_INDEXES || !TlsGetValue(tls_key_)) + ThreadLocalStorage::Initialize(); + + // Grab a new slot. + slot_ = InterlockedIncrement(&tls_max_) - 1; + if (slot_ >= kThreadLocalStorageSize) { + NOTREACHED(); + return false; + } + + // Setup our destructor. + tls_destructors_[slot_] = destructor; + initialized_ = true; + return true; +} + +void ThreadLocalStorage::Slot::Free() { + // At this time, we don't reclaim old indices for TLS slots. + // So all we need to do is wipe the destructor. + tls_destructors_[slot_] = NULL; + initialized_ = false; +} + +void* ThreadLocalStorage::Slot::Get() const { + void** tls_data = static_cast(TlsGetValue(tls_key_)); + if (!tls_data) + tls_data = ThreadLocalStorage::Initialize(); + DCHECK(slot_ >= 0 && slot_ < kThreadLocalStorageSize); + return tls_data[slot_]; +} + +void ThreadLocalStorage::Slot::Set(void* value) { + void** tls_data = static_cast(TlsGetValue(tls_key_)); + if (!tls_data) + tls_data = ThreadLocalStorage::Initialize(); + DCHECK(slot_ >= 0 && slot_ < kThreadLocalStorageSize); + tls_data[slot_] = value; +} + +void ThreadLocalStorage::ThreadExit() { + if (tls_key_ == TLS_OUT_OF_INDEXES) + return; + + void** tls_data = static_cast(TlsGetValue(tls_key_)); + + // Maybe we have never initialized TLS for this thread. + if (!tls_data) + return; + + for (int slot = 0; slot < tls_max_; slot++) { + if (tls_destructors_[slot] != NULL) { + void* value = tls_data[slot]; + tls_destructors_[slot](value); + } + } + + delete[] tls_data; + + // In case there are other "onexit" handlers... + TlsSetValue(tls_key_, NULL); +} + +// Thread Termination Callbacks. +// Windows doesn't support a per-thread destructor with its +// TLS primitives. So, we build it manually by inserting a +// function to be called on each thread's exit. +// This magic is from http://www.codeproject.com/threads/tls.asp +// and it works for VC++ 7.0 and later. + +#ifdef _WIN64 + +// This makes the linker create the TLS directory if it's not already +// there. (e.g. if __declspec(thread) is not used). +#pragma comment(linker, "/INCLUDE:_tls_used") + +#else // _WIN64 + +// This makes the linker create the TLS directory if it's not already +// there. (e.g. if __declspec(thread) is not used). +#pragma comment(linker, "/INCLUDE:__tls_used") + +#endif // _WIN64 + +// Static callback function to call with each thread termination. +void NTAPI OnThreadExit(PVOID module, DWORD reason, PVOID reserved) +{ + // On XP SP0 & SP1, the DLL_PROCESS_ATTACH is never seen. It is sent on SP2+ + // and on W2K and W2K3. So don't assume it is sent. + if (DLL_THREAD_DETACH == reason || DLL_PROCESS_DETACH == reason) + ThreadLocalStorage::ThreadExit(); +} + +// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are +// called automatically by the OS loader code (not the CRT) when the module is +// loaded and on thread creation. They are NOT called if the module has been +// loaded by a LoadLibrary() call. It must have implicitly been loaded at +// process startup. +// By implicitly loaded, I mean that it is directly referenced by the main EXE +// or by one of its dependent DLLs. Delay-loaded DLL doesn't count as being +// implicitly loaded. +// +// See VC\crt\src\tlssup.c for reference. +#ifdef _WIN64 + +// .CRT section is merged with .rdata on x64 so it must be constant data. +#pragma const_seg(".CRT$XLB") +// When defining a const variable, it must have external linkage to be sure the +// linker doesn't discard it. If this value is discarded, the OnThreadExit +// function will never be called. +extern const PIMAGE_TLS_CALLBACK p_thread_callback; +const PIMAGE_TLS_CALLBACK p_thread_callback = OnThreadExit; + +// Reset the default section. +#pragma const_seg() + +#else // _WIN64 + +#pragma data_seg(".CRT$XLB") +PIMAGE_TLS_CALLBACK p_thread_callback = OnThreadExit; + +// Reset the default section. +#pragma data_seg() + +#endif // _WIN64 diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_local_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_local_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_local_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_local_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,159 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/simple_thread.h" +#include "base/thread_local.h" +#include "base/waitable_event.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class ThreadLocalTesterBase : public base::DelegateSimpleThreadPool::Delegate { + public: + typedef base::ThreadLocalPointer TLPType; + + ThreadLocalTesterBase(TLPType* tlp, base::WaitableEvent* done) + : tlp_(tlp), done_(done) { } + ~ThreadLocalTesterBase() { } + + protected: + TLPType* tlp_; + base::WaitableEvent* done_; +}; + +class SetThreadLocal : public ThreadLocalTesterBase { + public: + SetThreadLocal(TLPType* tlp, base::WaitableEvent* done) + : ThreadLocalTesterBase(tlp, done), val_(NULL) { } + ~SetThreadLocal() { } + + void set_value(ThreadLocalTesterBase* val) { val_ = val; } + + virtual void Run() { + DCHECK(!done_->IsSignaled()); + tlp_->Set(val_); + done_->Signal(); + } + + private: + ThreadLocalTesterBase* val_; +}; + +class GetThreadLocal : public ThreadLocalTesterBase { + public: + GetThreadLocal(TLPType* tlp, base::WaitableEvent* done) + : ThreadLocalTesterBase(tlp, done), ptr_(NULL) { } + ~GetThreadLocal() { } + + void set_ptr(ThreadLocalTesterBase** ptr) { ptr_ = ptr; } + + virtual void Run() { + DCHECK(!done_->IsSignaled()); + *ptr_ = tlp_->Get(); + done_->Signal(); + } + + private: + ThreadLocalTesterBase** ptr_; +}; + +} // namespace + +// In this test, we start 2 threads which will access a ThreadLocalPointer. We +// make sure the default is NULL, and the pointers are unique to the threads. +TEST(ThreadLocalTest, Pointer) { + base::DelegateSimpleThreadPool tp1("ThreadLocalTest tp1", 1); + base::DelegateSimpleThreadPool tp2("ThreadLocalTest tp1", 1); + tp1.Start(); + tp2.Start(); + + base::ThreadLocalPointer tlp; + + static ThreadLocalTesterBase* const kBogusPointer = + reinterpret_cast(0x1234); + + ThreadLocalTesterBase* tls_val; + base::WaitableEvent done(true, false); + + GetThreadLocal getter(&tlp, &done); + getter.set_ptr(&tls_val); + + // Check that both threads defaulted to NULL. + tls_val = kBogusPointer; + done.Reset(); + tp1.AddWork(&getter); + done.Wait(); + EXPECT_EQ(static_cast(NULL), tls_val); + + tls_val = kBogusPointer; + done.Reset(); + tp2.AddWork(&getter); + done.Wait(); + EXPECT_EQ(static_cast(NULL), tls_val); + + + SetThreadLocal setter(&tlp, &done); + setter.set_value(kBogusPointer); + + // Have thread 1 set their pointer value to kBogusPointer. + done.Reset(); + tp1.AddWork(&setter); + done.Wait(); + + tls_val = NULL; + done.Reset(); + tp1.AddWork(&getter); + done.Wait(); + EXPECT_EQ(kBogusPointer, tls_val); + + // Make sure thread 2 is still NULL + tls_val = kBogusPointer; + done.Reset(); + tp2.AddWork(&getter); + done.Wait(); + EXPECT_EQ(static_cast(NULL), tls_val); + + // Set thread 2 to kBogusPointer + 1. + setter.set_value(kBogusPointer + 1); + + done.Reset(); + tp2.AddWork(&setter); + done.Wait(); + + tls_val = NULL; + done.Reset(); + tp2.AddWork(&getter); + done.Wait(); + EXPECT_EQ(kBogusPointer + 1, tls_val); + + // Make sure thread 1 is still kBogusPointer. + tls_val = NULL; + done.Reset(); + tp1.AddWork(&getter); + done.Wait(); + EXPECT_EQ(kBogusPointer, tls_val); + + tp1.JoinAll(); + tp2.JoinAll(); +} + +TEST(ThreadLocalTest, Boolean) { + { + base::ThreadLocalBoolean tlb; + EXPECT_EQ(false, tlb.Get()); + + tlb.Set(false); + EXPECT_EQ(false, tlb.Get()); + + tlb.Set(true); + EXPECT_EQ(true, tlb.Get()); + } + + // Our slot should have been freed, we're all reset. + { + base::ThreadLocalBoolean tlb; + EXPECT_EQ(false, tlb.Get()); + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_local_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_local_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_local_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_local_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,38 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/thread_local.h" + +#include + +#include "base/logging.h" + +namespace base { + +// static +void ThreadLocalPlatform::AllocateSlot(SlotType& slot) { + slot = TlsAlloc(); + CHECK(slot != TLS_OUT_OF_INDEXES); +} + +// static +void ThreadLocalPlatform::FreeSlot(SlotType& slot) { + if (!TlsFree(slot)) { + NOTREACHED() << "Failed to deallocate tls slot with TlsFree()."; + } +} + +// static +void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) { + return TlsGetValue(slot); +} + +// static +void ThreadLocalPlatform::SetValueInSlot(SlotType& slot, void* value) { + if (!TlsSetValue(slot, value)) { + CHECK(false) << "Failed to TlsSetValue()."; + } +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/thread_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/thread_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,140 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/lock.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "base/thread.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +using base::Thread; + +typedef PlatformTest ThreadTest; + +namespace { + +class ToggleValue : public Task { + public: + explicit ToggleValue(bool* value) : value_(value) { + } + virtual void Run() { + *value_ = !*value_; + } + private: + bool* value_; +}; + +class SleepSome : public Task { + public: + explicit SleepSome(int msec) : msec_(msec) { + } + virtual void Run() { + PlatformThread::Sleep(msec_); + } + private: + int msec_; +}; + +class SleepInsideInitThread : public Thread { + public: + SleepInsideInitThread() : Thread("none") { init_called_ = false; } + virtual ~SleepInsideInitThread() { } + + virtual void Init() { + PlatformThread::Sleep(500); + init_called_ = true; + } + bool InitCalled() { return init_called_; } + private: + bool init_called_; +}; + +} // namespace + +TEST_F(ThreadTest, Restart) { + Thread a("Restart"); + a.Stop(); + EXPECT_FALSE(a.message_loop()); + EXPECT_FALSE(a.IsRunning()); + EXPECT_TRUE(a.Start()); + EXPECT_TRUE(a.message_loop()); + EXPECT_TRUE(a.IsRunning()); + a.Stop(); + EXPECT_FALSE(a.message_loop()); + EXPECT_FALSE(a.IsRunning()); + EXPECT_TRUE(a.Start()); + EXPECT_TRUE(a.message_loop()); + EXPECT_TRUE(a.IsRunning()); + a.Stop(); + EXPECT_FALSE(a.message_loop()); + EXPECT_FALSE(a.IsRunning()); + a.Stop(); + EXPECT_FALSE(a.message_loop()); + EXPECT_FALSE(a.IsRunning()); +} + +TEST_F(ThreadTest, StartWithOptions_StackSize) { + Thread a("StartWithStackSize"); + // Ensure that the thread can work with only 12 kb and still process a + // message. + Thread::Options options; + options.stack_size = 12*1024; + EXPECT_TRUE(a.StartWithOptions(options)); + EXPECT_TRUE(a.message_loop()); + EXPECT_TRUE(a.IsRunning()); + + bool was_invoked = false; + a.message_loop()->PostTask(FROM_HERE, new ToggleValue(&was_invoked)); + + // wait for the task to run (we could use a kernel event here + // instead to avoid busy waiting, but this is sufficient for + // testing purposes). + for (int i = 100; i >= 0 && !was_invoked; --i) { + PlatformThread::Sleep(10); + } + EXPECT_TRUE(was_invoked); +} + +TEST_F(ThreadTest, TwoTasks) { + bool was_invoked = false; + { + Thread a("TwoTasks"); + EXPECT_TRUE(a.Start()); + EXPECT_TRUE(a.message_loop()); + + // Test that all events are dispatched before the Thread object is + // destroyed. We do this by dispatching a sleep event before the + // event that will toggle our sentinel value. + a.message_loop()->PostTask(FROM_HERE, new SleepSome(20)); + a.message_loop()->PostTask(FROM_HERE, new ToggleValue(&was_invoked)); + } + EXPECT_TRUE(was_invoked); +} + +TEST_F(ThreadTest, StopSoon) { + Thread a("StopSoon"); + EXPECT_TRUE(a.Start()); + EXPECT_TRUE(a.message_loop()); + EXPECT_TRUE(a.IsRunning()); + a.StopSoon(); + a.StopSoon(); + a.Stop(); + EXPECT_FALSE(a.message_loop()); + EXPECT_FALSE(a.IsRunning()); +} + +TEST_F(ThreadTest, ThreadName) { + Thread a("ThreadName"); + EXPECT_TRUE(a.Start()); + EXPECT_EQ("ThreadName", a.thread_name()); +} + +// Make sure we can't use a thread between Start() and Init(). +TEST_F(ThreadTest, SleepInsideInit) { + SleepInsideInitThread t; + EXPECT_FALSE(t.InitCalled()); + t.Start(); + EXPECT_TRUE(t.InitCalled()); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,102 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/time.h" +#include "base/string_util.h" +#include "base/sys_string_conversions.h" +#include "base/third_party/nspr/prtime.h" + +#include "base/logging.h" + +namespace base { + +// TimeDelta ------------------------------------------------------------------ + +int TimeDelta::InDays() const { + return static_cast(delta_ / Time::kMicrosecondsPerDay); +} + +int TimeDelta::InHours() const { + return static_cast(delta_ / Time::kMicrosecondsPerHour); +} + +int TimeDelta::InMinutes() const { + return static_cast(delta_ / Time::kMicrosecondsPerMinute); +} + +double TimeDelta::InSecondsF() const { + return static_cast(delta_) / Time::kMicrosecondsPerSecond; +} + +int64 TimeDelta::InSeconds() const { + return delta_ / Time::kMicrosecondsPerSecond; +} + +double TimeDelta::InMillisecondsF() const { + return static_cast(delta_) / Time::kMicrosecondsPerMillisecond; +} + +int64 TimeDelta::InMilliseconds() const { + return delta_ / Time::kMicrosecondsPerMillisecond; +} + +int64 TimeDelta::InMicroseconds() const { + return delta_; +} + +// Time ----------------------------------------------------------------------- + +// static +Time Time::FromTimeT(time_t tt) { + if (tt == 0) + return Time(); // Preserve 0 so we can tell it doesn't exist. + return (tt * kMicrosecondsPerSecond) + kTimeTToMicrosecondsOffset; +} + +time_t Time::ToTimeT() const { + if (us_ == 0) + return 0; // Preserve 0 so we can tell it doesn't exist. + return (us_ - kTimeTToMicrosecondsOffset) / kMicrosecondsPerSecond; +} + +// static +Time Time::FromDoubleT(double dt) { + return (dt * static_cast(kMicrosecondsPerSecond)) + + kTimeTToMicrosecondsOffset; +} + +double Time::ToDoubleT() const { + if (us_ == 0) + return 0; // Preserve 0 so we can tell it doesn't exist. + return (static_cast(us_ - kTimeTToMicrosecondsOffset) / + static_cast(kMicrosecondsPerSecond)); +} + +Time Time::LocalMidnight() const { + Exploded exploded; + LocalExplode(&exploded); + exploded.hour = 0; + exploded.minute = 0; + exploded.second = 0; + exploded.millisecond = 0; + return FromLocalExploded(exploded); +} + +// static +bool Time::FromString(const wchar_t* time_string, Time* parsed_time) { + DCHECK((time_string != NULL) && (parsed_time != NULL)); + std::string ascii_time_string = SysWideToUTF8(time_string); + if (ascii_time_string.length() == 0) + return false; + PRTime result_time = 0; + PRStatus result = PR_ParseTimeString(ascii_time_string.c_str(), PR_FALSE, + &result_time); + if (PR_SUCCESS != result) + return false; + result_time += kTimeTToMicrosecondsOffset; + *parsed_time = Time(result_time); + return true; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time_format.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time_format.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time_format.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time_format.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,72 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/time_format.h" + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "base/time.h" +#include "unicode/datefmt.h" + +using base::Time; + +namespace { + +std::wstring TimeFormat(const DateFormat* formatter, + const Time& time) { + DCHECK(formatter); + UnicodeString date_string; + + formatter->format(static_cast(time.ToDoubleT() * 1000), date_string); + std::wstring output; + bool success = UTF16ToWide(date_string.getBuffer(), date_string.length(), + &output); + DCHECK(success); + return output; +} + +} + +namespace base { + +std::wstring TimeFormatTimeOfDay(const Time& time) { + // We can omit the locale parameter because the default should match + // Chrome's application locale. + scoped_ptr formatter(DateFormat::createTimeInstance( + DateFormat::kShort)); + return TimeFormat(formatter.get(), time); +} + +std::wstring TimeFormatShortDate(const Time& time) { + scoped_ptr formatter(DateFormat::createDateInstance( + DateFormat::kMedium)); + return TimeFormat(formatter.get(), time); +} + +std::wstring TimeFormatShortDateNumeric(const Time& time) { + scoped_ptr formatter(DateFormat::createDateInstance( + DateFormat::kShort)); + return TimeFormat(formatter.get(), time); +} + +std::wstring TimeFormatShortDateAndTime(const Time& time) { + scoped_ptr formatter(DateFormat::createDateTimeInstance( + DateFormat::kShort)); + return TimeFormat(formatter.get(), time); +} + +std::wstring TimeFormatFriendlyDateAndTime(const Time& time) { + scoped_ptr formatter(DateFormat::createDateTimeInstance( + DateFormat::kFull)); + return TimeFormat(formatter.get(), time); +} + +std::wstring TimeFormatFriendlyDate(const Time& time) { + scoped_ptr formatter(DateFormat::createDateInstance( + DateFormat::kFull)); + return TimeFormat(formatter.get(), time); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time_format.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time_format.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time_format.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time_format.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,40 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Basic time formatting methods. These methods use the current locale +// formatting for displaying the time. + +#ifndef BASE_TIME_FORMAT_H_ +#define BASE_TIME_FORMAT_H_ + +#include + +namespace base { + +class Time; + +// Returns the time of day, e.g., "3:07 PM". +std::wstring TimeFormatTimeOfDay(const Time& time); + +// Returns a shortened date, e.g. "Nov 7, 2007" +std::wstring TimeFormatShortDate(const Time& time); + +// Returns a numeric date such as 12/13/52. +std::wstring TimeFormatShortDateNumeric(const Time& time); + +// Formats a time in a friendly sentence format, e.g. +// "Monday, March 6, 2008 2:44:30 PM". +std::wstring TimeFormatShortDateAndTime(const Time& time); + +// Formats a time in a friendly sentence format, e.g. +// "Monday, March 6, 2008 2:44:30 PM". +std::wstring TimeFormatFriendlyDateAndTime(const Time& time); + +// Formats a time in a friendly sentence format, e.g. +// "Monday, March 6, 2008". +std::wstring TimeFormatFriendlyDate(const Time& time); + +} // namespace base + +#endif // BASE_TIME_FORMAT_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,484 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Time represents an absolute point in time, internally represented as +// microseconds (s/1,000,000) since a platform-dependent epoch. Each +// platform's epoch, along with other system-dependent clock interface +// routines, is defined in time_PLATFORM.cc. +// +// TimeDelta represents a duration of time, internally represented in +// microseconds. +// +// TimeTicks represents an abstract time that is always incrementing for use +// in measuring time durations. It is internally represented in microseconds. +// It can not be converted to a human-readable time, but is guaranteed not to +// decrease (if the user changes the computer clock, Time::Now() may actually +// decrease or jump). +// +// These classes are represented as only a 64-bit value, so they can be +// efficiently passed by value. + +#ifndef BASE_TIME_H_ +#define BASE_TIME_H_ + +#include + +#include "base/basictypes.h" + +#if defined(OS_WIN) +// For FILETIME in FromFileTime, until it moves to a new converter class. +// See TODO(iyengar) below. +#include +#endif + +namespace base { + +class Time; +class TimeTicks; + +// This unit test does a lot of manual time manipulation. +class PageLoadTrackerUnitTest; + +// TimeDelta ------------------------------------------------------------------ + +class TimeDelta { + public: + TimeDelta() : delta_(0) { + } + + // Converts units of time to TimeDeltas. + static TimeDelta FromDays(int64 days); + static TimeDelta FromHours(int64 hours); + static TimeDelta FromMinutes(int64 minutes); + static TimeDelta FromSeconds(int64 secs); + static TimeDelta FromMilliseconds(int64 ms); + static TimeDelta FromMicroseconds(int64 us); + + // Returns the internal numeric value of the TimeDelta object. Please don't + // use this and do arithmetic on it, as it is more error prone than using the + // provided operators. + int64 ToInternalValue() const { + return delta_; + } + + // Returns the time delta in some unit. The F versions return a floating + // point value, the "regular" versions return a rounded-down value. + int InDays() const; + int InHours() const; + int InMinutes() const; + double InSecondsF() const; + int64 InSeconds() const; + double InMillisecondsF() const; + int64 InMilliseconds() const; + int64 InMicroseconds() const; + + TimeDelta& operator=(TimeDelta other) { + delta_ = other.delta_; + return *this; + } + + // Computations with other deltas. + TimeDelta operator+(TimeDelta other) const { + return TimeDelta(delta_ + other.delta_); + } + TimeDelta operator-(TimeDelta other) const { + return TimeDelta(delta_ - other.delta_); + } + + TimeDelta& operator+=(TimeDelta other) { + delta_ += other.delta_; + return *this; + } + TimeDelta& operator-=(TimeDelta other) { + delta_ -= other.delta_; + return *this; + } + TimeDelta operator-() const { + return TimeDelta(-delta_); + } + + // Computations with ints, note that we only allow multiplicative operations + // with ints, and additive operations with other deltas. + TimeDelta operator*(int64 a) const { + return TimeDelta(delta_ * a); + } + TimeDelta operator/(int64 a) const { + return TimeDelta(delta_ / a); + } + TimeDelta& operator*=(int64 a) { + delta_ *= a; + return *this; + } + TimeDelta& operator/=(int64 a) { + delta_ /= a; + return *this; + } + int64 operator/(TimeDelta a) const { + return delta_ / a.delta_; + } + + // Defined below because it depends on the definition of the other classes. + Time operator+(Time t) const; + TimeTicks operator+(TimeTicks t) const; + + // Comparison operators. + bool operator==(TimeDelta other) const { + return delta_ == other.delta_; + } + bool operator!=(TimeDelta other) const { + return delta_ != other.delta_; + } + bool operator<(TimeDelta other) const { + return delta_ < other.delta_; + } + bool operator<=(TimeDelta other) const { + return delta_ <= other.delta_; + } + bool operator>(TimeDelta other) const { + return delta_ > other.delta_; + } + bool operator>=(TimeDelta other) const { + return delta_ >= other.delta_; + } + + private: + friend class Time; + friend class TimeTicks; + friend TimeDelta operator*(int64 a, TimeDelta td); + + // Constructs a delta given the duration in microseconds. This is private + // to avoid confusion by callers with an integer constructor. Use + // FromSeconds, FromMilliseconds, etc. instead. + explicit TimeDelta(int64 delta_us) : delta_(delta_us) { + } + + // Delta in microseconds. + int64 delta_; +}; + +inline TimeDelta operator*(int64 a, TimeDelta td) { + return TimeDelta(a * td.delta_); +} + +// Time ----------------------------------------------------------------------- + +// Represents a wall clock time. +class Time { + public: + static const int64 kMillisecondsPerSecond = 1000; + static const int64 kMicrosecondsPerMillisecond = 1000; + static const int64 kMicrosecondsPerSecond = kMicrosecondsPerMillisecond * + kMillisecondsPerSecond; + static const int64 kMicrosecondsPerMinute = kMicrosecondsPerSecond * 60; + static const int64 kMicrosecondsPerHour = kMicrosecondsPerMinute * 60; + static const int64 kMicrosecondsPerDay = kMicrosecondsPerHour * 24; + static const int64 kMicrosecondsPerWeek = kMicrosecondsPerDay * 7; + static const int64 kNanosecondsPerMicrosecond = 1000; + static const int64 kNanosecondsPerSecond = kNanosecondsPerMicrosecond * + kMicrosecondsPerSecond; + + // Represents an exploded time that can be formatted nicely. This is kind of + // like the Win32 SYSTEMTIME structure or the Unix "struct tm" with a few + // additions and changes to prevent errors. + struct Exploded { + int year; // Four digit year "2007" + int month; // 1-based month (values 1 = January, etc.) + int day_of_week; // 0-based day of week (0 = Sunday, etc.) + int day_of_month; // 1-based day of month (1-31) + int hour; // Hour within the current day (0-23) + int minute; // Minute within the current hour (0-59) + int second; // Second within the current minute (0-59 plus leap + // seconds which may take it up to 60). + int millisecond; // Milliseconds within the current second (0-999) + }; + + // Contains the NULL time. Use Time::Now() to get the current time. + explicit Time() : us_(0) { + } + + // Returns true if the time object has not been initialized. + bool is_null() const { + return us_ == 0; + } + + // Returns the current time. Watch out, the system might adjust its clock + // in which case time will actually go backwards. We don't guarantee that + // times are increasing, or that two calls to Now() won't be the same. + static Time Now(); + + // Returns the current time. Same as Now() except that this function always + // uses system time so that there are no discrepancies between the returned + // time and system time even on virtual environments including our test bot. + // For timing sensitive unittests, this function should be used. + static Time NowFromSystemTime(); + + // Converts to/from time_t in UTC and a Time class. + // TODO(brettw) this should be removed once everybody starts using the |Time| + // class. + static Time FromTimeT(time_t tt); + time_t ToTimeT() const; + + // Converts time to/from a double which is the number of seconds since epoch + // (Jan 1, 1970). Webkit uses this format to represent time. + static Time FromDoubleT(double dt); + double ToDoubleT() const; + + +#if defined(OS_WIN) + static Time FromFileTime(FILETIME ft); + FILETIME ToFileTime() const; +#endif + + // Converts an exploded structure representing either the local time or UTC + // into a Time class. + static Time FromUTCExploded(const Exploded& exploded) { + return FromExploded(false, exploded); + } + static Time FromLocalExploded(const Exploded& exploded) { + return FromExploded(true, exploded); + } + + // Converts an integer value representing Time to a class. This is used + // when deserializing a |Time| structure, using a value known to be + // compatible. It is not provided as a constructor because the integer type + // may be unclear from the perspective of a caller. + static Time FromInternalValue(int64 us) { + return Time(us); + } + + // Converts a string representation of time to a Time object. + // An example of a time string which is converted is as below:- + // "Tue, 15 Nov 1994 12:45:26 GMT". If the timezone is not specified + // in the input string, we assume local time. + // TODO(iyengar) Move the FromString/FromTimeT/ToTimeT/FromFileTime to + // a new time converter class. + static bool FromString(const wchar_t* time_string, Time* parsed_time); + + // For serializing, use FromInternalValue to reconstitute. Please don't use + // this and do arithmetic on it, as it is more error prone than using the + // provided operators. + int64 ToInternalValue() const { + return us_; + } + + // Fills the given exploded structure with either the local time or UTC from + // this time structure (containing UTC). + void UTCExplode(Exploded* exploded) const { + return Explode(false, exploded); + } + void LocalExplode(Exploded* exploded) const { + return Explode(true, exploded); + } + + // Rounds this time down to the nearest day in local time. It will represent + // midnight on that day. + Time LocalMidnight() const; + + Time& operator=(Time other) { + us_ = other.us_; + return *this; + } + + // Compute the difference between two times. + TimeDelta operator-(Time other) const { + return TimeDelta(us_ - other.us_); + } + + // Modify by some time delta. + Time& operator+=(TimeDelta delta) { + us_ += delta.delta_; + return *this; + } + Time& operator-=(TimeDelta delta) { + us_ -= delta.delta_; + return *this; + } + + // Return a new time modified by some delta. + Time operator+(TimeDelta delta) const { + return us_ + delta.delta_; + } + Time operator-(TimeDelta delta) const { + return us_ - delta.delta_; + } + + // Comparison operators + bool operator==(Time other) const { + return us_ == other.us_; + } + bool operator!=(Time other) const { + return us_ != other.us_; + } + bool operator<(Time other) const { + return us_ < other.us_; + } + bool operator<=(Time other) const { + return us_ <= other.us_; + } + bool operator>(Time other) const { + return us_ > other.us_; + } + bool operator>=(Time other) const { + return us_ >= other.us_; + } + + private: + friend class TimeDelta; + + // Explodes the given time to either local time |is_local = true| or UTC + // |is_local = false|. + void Explode(bool is_local, Exploded* exploded) const; + + // Unexplodes a given time assuming the source is either local time + // |is_local = true| or UTC |is_local = false|. + static Time FromExploded(bool is_local, const Exploded& exploded); + + Time(int64 us) : us_(us) { + } + + // The representation of Jan 1, 1970 UTC in microseconds since the + // platform-dependent epoch. + static const int64 kTimeTToMicrosecondsOffset; + + // Time in microseconds in UTC. + int64 us_; +}; + +inline Time TimeDelta::operator+(Time t) const { + return Time(t.us_ + delta_); +} + +// Inline the TimeDelta factory methods, for fast TimeDelta construction. + +// static +inline TimeDelta TimeDelta::FromDays(int64 days) { + return TimeDelta(days * Time::kMicrosecondsPerDay); +} + +// static +inline TimeDelta TimeDelta::FromHours(int64 hours) { + return TimeDelta(hours * Time::kMicrosecondsPerHour); +} + +// static +inline TimeDelta TimeDelta::FromMinutes(int64 minutes) { + return TimeDelta(minutes * Time::kMicrosecondsPerMinute); +} + +// static +inline TimeDelta TimeDelta::FromSeconds(int64 secs) { + return TimeDelta(secs * Time::kMicrosecondsPerSecond); +} + +// static +inline TimeDelta TimeDelta::FromMilliseconds(int64 ms) { + return TimeDelta(ms * Time::kMicrosecondsPerMillisecond); +} + +// static +inline TimeDelta TimeDelta::FromMicroseconds(int64 us) { + return TimeDelta(us); +} + +// TimeTicks ------------------------------------------------------------------ + +class TimeTicks { + public: + TimeTicks() : ticks_(0) { + } + + // Platform-dependent tick count representing "right now." + // The resolution of this clock is ~1-15ms. Resolution varies depending + // on hardware/operating system configuration. + static TimeTicks Now(); + + // Returns a platform-dependent high-resolution tick count. Implementation + // is hardware dependent and may or may not return sub-millisecond + // resolution. THIS CALL IS GENERALLY MUCH MORE EXPENSIVE THAN Now() AND + // SHOULD ONLY BE USED WHEN IT IS REALLY NEEDED. + static TimeTicks HighResNow(); + + // Returns true if this object has not been initialized. + bool is_null() const { + return ticks_ == 0; + } + + // Returns the internal numeric value of the TimeTicks object. + int64 ToInternalValue() const { + return ticks_; + } + + TimeTicks& operator=(TimeTicks other) { + ticks_ = other.ticks_; + return *this; + } + + // Compute the difference between two times. + TimeDelta operator-(TimeTicks other) const { + return TimeDelta(ticks_ - other.ticks_); + } + + // Modify by some time delta. + TimeTicks& operator+=(TimeDelta delta) { + ticks_ += delta.delta_; + return *this; + } + TimeTicks& operator-=(TimeDelta delta) { + ticks_ -= delta.delta_; + return *this; + } + + // Return a new TimeTicks modified by some delta. + TimeTicks operator+(TimeDelta delta) const { + return TimeTicks(ticks_ + delta.delta_); + } + TimeTicks operator-(TimeDelta delta) const { + return TimeTicks(ticks_ - delta.delta_); + } + + // Comparison operators + bool operator==(TimeTicks other) const { + return ticks_ == other.ticks_; + } + bool operator!=(TimeTicks other) const { + return ticks_ != other.ticks_; + } + bool operator<(TimeTicks other) const { + return ticks_ < other.ticks_; + } + bool operator<=(TimeTicks other) const { + return ticks_ <= other.ticks_; + } + bool operator>(TimeTicks other) const { + return ticks_ > other.ticks_; + } + bool operator>=(TimeTicks other) const { + return ticks_ >= other.ticks_; + } + + protected: + friend class TimeDelta; + friend class PageLoadTrackerUnitTest; + + // Please use Now() to create a new object. This is for internal use + // and testing. Ticks is in microseconds. + explicit TimeTicks(int64 ticks) : ticks_(ticks) { + } + + // Tick count in microseconds. + int64 ticks_; + +#if defined(OS_WIN) + typedef DWORD (*TickFunctionType)(void); + static TickFunctionType SetMockTickFunction(TickFunctionType ticker); +#endif +}; + +inline TimeTicks TimeDelta::operator+(TimeTicks t) const { + return TimeTicks(t.ticks_ + delta_); +} + +} // namespace base + +#endif // BASE_TIME_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time_mac.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time_mac.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time_mac.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time_mac.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,125 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/time.h" + +#include +#include +#include +#include +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/scoped_cftyperef.h" + +namespace base { + +// The Time routines in this file use Mach and CoreFoundation APIs, since the +// POSIX definition of time_t in Mac OS X wraps around after 2038--and +// there are already cookie expiration dates, etc., past that time out in +// the field. Using CFDate prevents that problem, and using mach_absolute_time +// for TimeTicks gives us nice high-resolution interval timing. + +// Time ----------------------------------------------------------------------- + +// The internal representation of Time uses a 64-bit microsecond count +// from 1970-01-01 00:00:00 UTC. Core Foundation uses a double second count +// since 2001-01-01 00:00:00 UTC. + +// Some functions in time.cc use time_t directly, so we provide a zero offset +// for them. The epoch is 1970-01-01 00:00:00 UTC. +// static +const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(0); + +// static +Time Time::Now() { + CFAbsoluteTime now = + CFAbsoluteTimeGetCurrent() + kCFAbsoluteTimeIntervalSince1970; + return Time(static_cast(now * kMicrosecondsPerSecond)); +} + +// static +Time Time::NowFromSystemTime() { + // Just use Now() because Now() returns the system time. + return Now(); +} + +// static +Time Time::FromExploded(bool is_local, const Exploded& exploded) { + CFGregorianDate date; + date.second = exploded.second + + exploded.millisecond / static_cast(kMillisecondsPerSecond); + date.minute = exploded.minute; + date.hour = exploded.hour; + date.day = exploded.day_of_month; + date.month = exploded.month; + date.year = exploded.year; + + scoped_cftyperef + time_zone(is_local ? CFTimeZoneCopySystem() : NULL); + CFAbsoluteTime seconds = CFGregorianDateGetAbsoluteTime(date, time_zone) + + kCFAbsoluteTimeIntervalSince1970; + return Time(static_cast(seconds * kMicrosecondsPerSecond)); +} + +void Time::Explode(bool is_local, Exploded* exploded) const { + CFAbsoluteTime seconds = + (static_cast(us_) / kMicrosecondsPerSecond) - + kCFAbsoluteTimeIntervalSince1970; + + scoped_cftyperef + time_zone(is_local ? CFTimeZoneCopySystem() : NULL); + CFGregorianDate date = CFAbsoluteTimeGetGregorianDate(seconds, time_zone); + + exploded->year = date.year; + exploded->month = date.month; + exploded->day_of_month = date.day; + exploded->hour = date.hour; + exploded->minute = date.minute; + exploded->second = date.second; + exploded->millisecond = + static_cast(date.second * kMillisecondsPerSecond) % + kMillisecondsPerSecond; +} + +// TimeTicks ------------------------------------------------------------------ + +// static +TimeTicks TimeTicks::Now() { + uint64_t absolute_micro; + + static mach_timebase_info_data_t timebase_info; + if (timebase_info.denom == 0) { + // Zero-initialization of statics guarantees that denom will be 0 before + // calling mach_timebase_info. mach_timebase_info will never set denom to + // 0 as that would be invalid, so the zero-check can be used to determine + // whether mach_timebase_info has already been called. This is + // recommended by Apple's QA1398. + kern_return_t kr = mach_timebase_info(&timebase_info); + DCHECK(kr == KERN_SUCCESS); + } + + // mach_absolute_time is it when it comes to ticks on the Mac. Other calls + // with less precision (such as TickCount) just call through to + // mach_absolute_time. + + // timebase_info converts absolute time tick units into nanoseconds. Convert + // to microseconds up front to stave off overflows. + absolute_micro = mach_absolute_time() / Time::kNanosecondsPerMicrosecond * + timebase_info.numer / timebase_info.denom; + + // Don't bother with the rollover handling that the Windows version does. + // With numer and denom = 1 (the expected case), the 64-bit absolute time + // reported in nanoseconds is enough to last nearly 585 years. + + return TimeTicks(absolute_micro); +} + +// static +TimeTicks TimeTicks::HighResNow() { + return Now(); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time_posix.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,184 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/time.h" + +#ifdef OS_MACOSX +#include +#endif +#include +#include + +#include + +#include "base/basictypes.h" +#include "base/logging.h" + +namespace base { + +// The Time routines in this file use standard POSIX routines, or almost- +// standard routines in the case of timegm. We need to use a Mach-specific +// function for TimeTicks::Now() on Mac OS X. + +// Time ----------------------------------------------------------------------- + +// Some functions in time.cc use time_t directly, so we provide a zero offset +// for them. The epoch is 1970-01-01 00:00:00 UTC. +// static +const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(0); + +// static +Time Time::Now() { + struct timeval tv; + struct timezone tz = { 0, 0 }; // UTC + if (gettimeofday(&tv, &tz) != 0) { + DCHECK(0) << "Could not determine time of day"; + } + // Combine seconds and microseconds in a 64-bit field containing microseconds + // since the epoch. That's enough for nearly 600 centuries. + return tv.tv_sec * kMicrosecondsPerSecond + tv.tv_usec; +} + +// static +Time Time::NowFromSystemTime() { + // Just use Now() because Now() returns the system time. + return Now(); +} + +// static +Time Time::FromExploded(bool is_local, const Exploded& exploded) { + struct tm timestruct; + timestruct.tm_sec = exploded.second; + timestruct.tm_min = exploded.minute; + timestruct.tm_hour = exploded.hour; + timestruct.tm_mday = exploded.day_of_month; + timestruct.tm_mon = exploded.month - 1; + timestruct.tm_year = exploded.year - 1900; + timestruct.tm_wday = exploded.day_of_week; // mktime/timegm ignore this + timestruct.tm_yday = 0; // mktime/timegm ignore this + timestruct.tm_isdst = -1; // attempt to figure it out + timestruct.tm_gmtoff = 0; // not a POSIX field, so mktime/timegm ignore + timestruct.tm_zone = NULL; // not a POSIX field, so mktime/timegm ignore + + time_t seconds; + if (is_local) + seconds = mktime(×truct); + else + seconds = timegm(×truct); + + int64 milliseconds; + // Handle overflow. Clamping the range to what mktime and timegm might + // return is the best that can be done here. It's not ideal, but it's better + // than failing here or ignoring the overflow case and treating each time + // overflow as one second prior to the epoch. + if (seconds == -1 && + (exploded.year < 1969 || exploded.year > 1970)) { + // If exploded.year is 1969 or 1970, take -1 as correct, with the + // time indicating 1 second prior to the epoch. (1970 is allowed to handle + // time zone and DST offsets.) Otherwise, return the most future or past + // time representable. Assumes the time_t epoch is 1970-01-01 00:00:00 UTC. + // + // The minimum and maximum representible times that mktime and timegm could + // return are used here instead of values outside that range to allow for + // proper round-tripping between exploded and counter-type time + // representations in the presence of possible truncation to time_t by + // division and use with other functions that accept time_t. + // + // When representing the most distant time in the future, add in an extra + // 999ms to avoid the time being less than any other possible value that + // this function can return. + if (exploded.year < 1969) { + milliseconds = std::numeric_limits::min() * + kMillisecondsPerSecond; + } else { + milliseconds = (std::numeric_limits::max() * + kMillisecondsPerSecond) + + kMillisecondsPerSecond - 1; + } + } else { + milliseconds = seconds * kMillisecondsPerSecond + exploded.millisecond; + } + + return Time(milliseconds * kMicrosecondsPerMillisecond); +} + +void Time::Explode(bool is_local, Exploded* exploded) const { + // Time stores times with microsecond resolution, but Exploded only carries + // millisecond resolution, so begin by being lossy. + int64 milliseconds = us_ / kMicrosecondsPerMillisecond; + time_t seconds = milliseconds / kMillisecondsPerSecond; + + struct tm timestruct; + if (is_local) + localtime_r(&seconds, ×truct); + else + gmtime_r(&seconds, ×truct); + + exploded->year = timestruct.tm_year + 1900; + exploded->month = timestruct.tm_mon + 1; + exploded->day_of_week = timestruct.tm_wday; + exploded->day_of_month = timestruct.tm_mday; + exploded->hour = timestruct.tm_hour; + exploded->minute = timestruct.tm_min; + exploded->second = timestruct.tm_sec; + exploded->millisecond = milliseconds % kMillisecondsPerSecond; +} + +// TimeTicks ------------------------------------------------------------------ + +// static +TimeTicks TimeTicks::Now() { + uint64_t absolute_micro; + +#if defined(OS_MACOSX) + static mach_timebase_info_data_t timebase_info; + if (timebase_info.denom == 0) { + // Zero-initialization of statics guarantees that denom will be 0 before + // calling mach_timebase_info. mach_timebase_info will never set denom to + // 0 as that would be invalid, so the zero-check can be used to determine + // whether mach_timebase_info has already been called. This is + // recommended by Apple's QA1398. + kern_return_t kr = mach_timebase_info(&timebase_info); + DCHECK(kr == KERN_SUCCESS); + } + + // mach_absolute_time is it when it comes to ticks on the Mac. Other calls + // with less precision (such as TickCount) just call through to + // mach_absolute_time. + + // timebase_info converts absolute time tick units into nanoseconds. Convert + // to microseconds up front to stave off overflows. + absolute_micro = mach_absolute_time() / Time::kNanosecondsPerMicrosecond * + timebase_info.numer / timebase_info.denom; + + // Don't bother with the rollover handling that the Windows version does. + // With numer and denom = 1 (the expected case), the 64-bit absolute time + // reported in nanoseconds is enough to last nearly 585 years. + +#elif defined(OS_POSIX) && \ + defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK >= 0 + + struct timespec ts; + if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) { + NOTREACHED() << "clock_gettime(CLOCK_MONOTONIC) failed."; + return TimeTicks(); + } + + absolute_micro = + (static_cast(ts.tv_sec) * Time::kMicrosecondsPerSecond) + + (static_cast(ts.tv_nsec) / Time::kNanosecondsPerMicrosecond); + +#else // _POSIX_MONOTONIC_CLOCK +#error No usable tick clock function on this platform. +#endif // _POSIX_MONOTONIC_CLOCK + + return TimeTicks(absolute_micro); +} + +// static +TimeTicks TimeTicks::HighResNow() { + return Now(); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/timer.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/timer.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/timer.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/timer.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,28 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/timer.h" + +#include "base/message_loop.h" + +namespace base { + +void BaseTimer_Helper::OrphanDelayedTask() { + if (delayed_task_) { + delayed_task_->timer_ = NULL; + delayed_task_ = NULL; + } +} + +void BaseTimer_Helper::InitiateDelayedTask(TimerTask* timer_task) { + OrphanDelayedTask(); + + delayed_task_ = timer_task; + delayed_task_->timer_ = this; + MessageLoop::current()->PostDelayedTask( + FROM_HERE, timer_task, + static_cast(timer_task->delay_.InMilliseconds())); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/timer.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/timer.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/timer.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/timer.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,271 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// OneShotTimer and RepeatingTimer provide a simple timer API. As the names +// suggest, OneShotTimer calls you back once after a time delay expires. +// RepeatingTimer on the other hand calls you back periodically with the +// prescribed time interval. +// +// OneShotTimer and RepeatingTimer both cancel the timer when they go out of +// scope, which makes it easy to ensure that you do not get called when your +// object has gone out of scope. Just instantiate a OneShotTimer or +// RepeatingTimer as a member variable of the class for which you wish to +// receive timer events. +// +// Sample RepeatingTimer usage: +// +// class MyClass { +// public: +// void StartDoingStuff() { +// timer_.Start(TimeDelta::FromSeconds(1), this, &MyClass::DoStuff); +// } +// void StopDoingStuff() { +// timer_.Stop(); +// } +// private: +// void DoStuff() { +// // This method is called every second to do stuff. +// ... +// } +// base::RepeatingTimer timer_; +// }; +// +// Both OneShotTimer and RepeatingTimer also support a Reset method, which +// allows you to easily defer the timer event until the timer delay passes once +// again. So, in the above example, if 0.5 seconds have already passed, +// calling Reset on timer_ would postpone DoStuff by another 1 second. In +// other words, Reset is shorthand for calling Stop and then Start again with +// the same arguments. + +#ifndef BASE_TIMER_H_ +#define BASE_TIMER_H_ + +// IMPORTANT: If you change timer code, make sure that all tests (including +// disabled ones) from timer_unittests.cc pass locally. Some are disabled +// because they're flaky on the buildbot, but when you run them locally you +// should be able to tell the difference. + +#include "base/logging.h" +#include "base/task.h" +#include "base/time.h" + +class MessageLoop; + +namespace base { + +//----------------------------------------------------------------------------- +// This class is an implementation detail of OneShotTimer and RepeatingTimer. +// Please do not use this class directly. +// +// This class exists to share code between BaseTimer template instantiations. +// +class BaseTimer_Helper { + public: + // Stops the timer. + ~BaseTimer_Helper() { + OrphanDelayedTask(); + } + + // Returns true if the timer is running (i.e., not stopped). + bool IsRunning() const { + return delayed_task_ != NULL; + } + + // Returns the current delay for this timer. May only call this method when + // the timer is running! + TimeDelta GetCurrentDelay() const { + DCHECK(IsRunning()); + return delayed_task_->delay_; + } + + protected: + BaseTimer_Helper() : delayed_task_(NULL) {} + + // We have access to the timer_ member so we can orphan this task. + class TimerTask : public Task { + public: + TimerTask(TimeDelta delay) : delay_(delay) { + // timer_ is set in InitiateDelayedTask. + } + virtual ~TimerTask() {} + BaseTimer_Helper* timer_; + TimeDelta delay_; + }; + + // Used to orphan delayed_task_ so that when it runs it does nothing. + void OrphanDelayedTask(); + + // Used to initiated a new delayed task. This has the side-effect of + // orphaning delayed_task_ if it is non-null. + void InitiateDelayedTask(TimerTask* timer_task); + + TimerTask* delayed_task_; + + DISALLOW_COPY_AND_ASSIGN(BaseTimer_Helper); +}; + +//----------------------------------------------------------------------------- +// This class is an implementation detail of OneShotTimer and RepeatingTimer. +// Please do not use this class directly. +template +class BaseTimer : public BaseTimer_Helper { + public: + typedef void (Receiver::*ReceiverMethod)(); + + // Call this method to start the timer. It is an error to call this method + // while the timer is already running. + void Start(TimeDelta delay, Receiver* receiver, ReceiverMethod method) { + DCHECK(!IsRunning()); + InitiateDelayedTask(new TimerTask(delay, receiver, method)); + } + + // Call this method to stop the timer. It is a no-op if the timer is not + // running. + void Stop() { + OrphanDelayedTask(); + } + + // Call this method to reset the timer delay of an already running timer. + void Reset() { + DCHECK(IsRunning()); + InitiateDelayedTask(static_cast(delayed_task_)->Clone()); + } + + private: + typedef BaseTimer SelfType; + + class TimerTask : public BaseTimer_Helper::TimerTask { + public: + TimerTask(TimeDelta delay, Receiver* receiver, ReceiverMethod method) + : BaseTimer_Helper::TimerTask(delay), + receiver_(receiver), + method_(method) { + } + + virtual ~TimerTask() { + // This task may be getting cleared because the MessageLoop has been + // destructed. If so, don't leave the Timer with a dangling pointer + // to this now-defunct task. + ClearBaseTimer(); + } + + virtual void Run() { + if (!timer_) // timer_ is null if we were orphaned. + return; + if (kIsRepeating) + ResetBaseTimer(); + else + ClearBaseTimer(); + DispatchToMethod(receiver_, method_, Tuple0()); + } + + TimerTask* Clone() const { + return new TimerTask(delay_, receiver_, method_); + } + + private: + // Inform the Base that the timer is no longer active. + void ClearBaseTimer() { + if (timer_) { + SelfType* self = static_cast(timer_); + // It is possible that the Timer has already been reset, and that this + // Task is old. So, if the Timer points to a different task, assume + // that the Timer has already taken care of properly setting the task. + if (self->delayed_task_ == this) + self->delayed_task_ = NULL; + // By now the delayed_task_ in the Timer does not point to us anymore. + // We should reset our own timer_ because the Timer can not do this + // for us in its destructor. + timer_ = NULL; + } + } + + // Inform the Base that we're resetting the timer. + void ResetBaseTimer() { + DCHECK(timer_); + DCHECK(kIsRepeating); + SelfType* self = static_cast(timer_); + self->Reset(); + } + + Receiver* receiver_; + ReceiverMethod method_; + }; +}; + +//----------------------------------------------------------------------------- +// A simple, one-shot timer. See usage notes at the top of the file. +template +class OneShotTimer : public BaseTimer {}; + +//----------------------------------------------------------------------------- +// A simple, repeating timer. See usage notes at the top of the file. +template +class RepeatingTimer : public BaseTimer {}; + +//----------------------------------------------------------------------------- +// A Delay timer is like The Button from Lost. Once started, you have to keep +// calling Reset otherwise it will call the given method in the MessageLoop +// thread. +// +// Once created, it is inactive until Reset is called. Once |delay| seconds have +// passed since the last call to Reset, the callback is made. Once the callback +// has been made, it's inactive until Reset is called again. +// +// If destroyed, the timeout is canceled and will not occur even if already +// inflight. +template +class DelayTimer { + public: + typedef void (Receiver::*ReceiverMethod)(); + + DelayTimer(TimeDelta delay, Receiver* receiver, ReceiverMethod method) + : receiver_(receiver), + method_(method), + delay_(delay) { + } + + void Reset() { + DelayFor(delay_); + } + + private: + void DelayFor(TimeDelta delay) { + trigger_time_ = Time::Now() + delay; + + // If we already have a timer that will expire at or before the given delay, + // then we have nothing more to do now. + if (timer_.IsRunning() && timer_.GetCurrentDelay() <= delay) + return; + + // The timer isn't running, or will expire too late, so restart it. + timer_.Stop(); + timer_.Start(delay, this, &DelayTimer::Check); + } + + void Check() { + if (trigger_time_.is_null()) + return; + + // If we have not waited long enough, then wait some more. + const Time now = Time::Now(); + if (now < trigger_time_) { + DelayFor(trigger_time_ - now); + return; + } + + (receiver_->*method_)(); + } + + Receiver *const receiver_; + const ReceiverMethod method_; + const TimeDelta delay_; + + OneShotTimer > timer_; + Time trigger_time_; +}; + +} // namespace base + +#endif // BASE_TIMER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/timer_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/timer_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/timer_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/timer_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,354 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop.h" +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "base/timer.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::TimeDelta; + +namespace { + +class OneShotTimerTester { + public: + OneShotTimerTester(bool* did_run, unsigned milliseconds = 10) + : did_run_(did_run), + delay_ms_(milliseconds) { + } + void Start() { + timer_.Start(TimeDelta::FromMilliseconds(delay_ms_), this, + &OneShotTimerTester::Run); + } + private: + void Run() { + *did_run_ = true; + MessageLoop::current()->Quit(); + } + bool* did_run_; + base::OneShotTimer timer_; + const unsigned delay_ms_; +}; + +class OneShotSelfDeletingTimerTester { + public: + OneShotSelfDeletingTimerTester(bool* did_run) : + did_run_(did_run), + timer_(new base::OneShotTimer()) { + } + void Start() { + timer_->Start(TimeDelta::FromMilliseconds(10), this, + &OneShotSelfDeletingTimerTester::Run); + } + private: + void Run() { + *did_run_ = true; + timer_.reset(); + MessageLoop::current()->Quit(); + } + bool* did_run_; + scoped_ptr > timer_; +}; + +class RepeatingTimerTester { + public: + RepeatingTimerTester(bool* did_run) : did_run_(did_run), counter_(10) { + } + void Start() { + timer_.Start(TimeDelta::FromMilliseconds(10), this, + &RepeatingTimerTester::Run); + } + private: + void Run() { + if (--counter_ == 0) { + *did_run_ = true; + MessageLoop::current()->Quit(); + } + } + bool* did_run_; + int counter_; + base::RepeatingTimer timer_; +}; + +void RunTest_OneShotTimer(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + bool did_run = false; + OneShotTimerTester f(&did_run); + f.Start(); + + MessageLoop::current()->Run(); + + EXPECT_TRUE(did_run); +} + +void RunTest_OneShotTimer_Cancel(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + bool did_run_a = false; + OneShotTimerTester* a = new OneShotTimerTester(&did_run_a); + + // This should run before the timer expires. + MessageLoop::current()->DeleteSoon(FROM_HERE, a); + + // Now start the timer. + a->Start(); + + bool did_run_b = false; + OneShotTimerTester b(&did_run_b); + b.Start(); + + MessageLoop::current()->Run(); + + EXPECT_FALSE(did_run_a); + EXPECT_TRUE(did_run_b); +} + +void RunTest_OneShotSelfDeletingTimer(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + bool did_run = false; + OneShotSelfDeletingTimerTester f(&did_run); + f.Start(); + + MessageLoop::current()->Run(); + + EXPECT_TRUE(did_run); +} + +void RunTest_RepeatingTimer(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + bool did_run = false; + RepeatingTimerTester f(&did_run); + f.Start(); + + MessageLoop::current()->Run(); + + EXPECT_TRUE(did_run); +} + +void RunTest_RepeatingTimer_Cancel(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + bool did_run_a = false; + RepeatingTimerTester* a = new RepeatingTimerTester(&did_run_a); + + // This should run before the timer expires. + MessageLoop::current()->DeleteSoon(FROM_HERE, a); + + // Now start the timer. + a->Start(); + + bool did_run_b = false; + RepeatingTimerTester b(&did_run_b); + b.Start(); + + MessageLoop::current()->Run(); + + EXPECT_FALSE(did_run_a); + EXPECT_TRUE(did_run_b); +} + +class DelayTimerTarget { + public: + DelayTimerTarget() + : signaled_(false) { + } + + bool signaled() const { return signaled_; } + + void Signal() { + ASSERT_FALSE(signaled_); + signaled_ = true; + } + + private: + bool signaled_; +}; + +void RunTest_DelayTimer_NoCall(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + // If Delay is never called, the timer shouldn't go off. + DelayTimerTarget target; + base::DelayTimer timer( + TimeDelta::FromMilliseconds(1), &target, &DelayTimerTarget::Signal); + + bool did_run = false; + OneShotTimerTester tester(&did_run); + tester.Start(); + MessageLoop::current()->Run(); + + ASSERT_FALSE(target.signaled()); +} + +void RunTest_DelayTimer_OneCall(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + DelayTimerTarget target; + base::DelayTimer timer( + TimeDelta::FromMilliseconds(1), &target, &DelayTimerTarget::Signal); + timer.Reset(); + + bool did_run = false; + OneShotTimerTester tester(&did_run, 100 /* milliseconds */); + tester.Start(); + MessageLoop::current()->Run(); + + ASSERT_TRUE(target.signaled()); +} + +struct ResetHelper { + ResetHelper(base::DelayTimer* timer, + DelayTimerTarget* target) + : timer_(timer), + target_(target) { + } + + void Reset() { + ASSERT_FALSE(target_->signaled()); + timer_->Reset(); + } + + private: + base::DelayTimer *const timer_; + DelayTimerTarget *const target_; +}; + +void RunTest_DelayTimer_Reset(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + // If Delay is never called, the timer shouldn't go off. + DelayTimerTarget target; + base::DelayTimer timer( + TimeDelta::FromMilliseconds(50), &target, &DelayTimerTarget::Signal); + timer.Reset(); + + ResetHelper reset_helper(&timer, &target); + + base::OneShotTimer timers[20]; + for (size_t i = 0; i < arraysize(timers); ++i) { + timers[i].Start(TimeDelta::FromMilliseconds(i * 10), &reset_helper, + &ResetHelper::Reset); + } + + bool did_run = false; + OneShotTimerTester tester(&did_run, 300); + tester.Start(); + MessageLoop::current()->Run(); + + ASSERT_TRUE(target.signaled()); +} + +class DelayTimerFatalTarget { + public: + void Signal() { + ASSERT_TRUE(false); + } +}; + + +void RunTest_DelayTimer_Deleted(MessageLoop::Type message_loop_type) { + MessageLoop loop(message_loop_type); + + DelayTimerFatalTarget target; + + { + base::DelayTimer timer( + TimeDelta::FromMilliseconds(50), &target, + &DelayTimerFatalTarget::Signal); + timer.Reset(); + } + + // When the timer is deleted, the DelayTimerFatalTarget should never be + // called. + PlatformThread::Sleep(100); +} + +} // namespace + +//----------------------------------------------------------------------------- +// Each test is run against each type of MessageLoop. That way we are sure +// that timers work properly in all configurations. + +TEST(TimerTest, OneShotTimer) { + RunTest_OneShotTimer(MessageLoop::TYPE_DEFAULT); + RunTest_OneShotTimer(MessageLoop::TYPE_UI); + RunTest_OneShotTimer(MessageLoop::TYPE_IO); +} + +TEST(TimerTest, OneShotTimer_Cancel) { + RunTest_OneShotTimer_Cancel(MessageLoop::TYPE_DEFAULT); + RunTest_OneShotTimer_Cancel(MessageLoop::TYPE_UI); + RunTest_OneShotTimer_Cancel(MessageLoop::TYPE_IO); +} + +// If underline timer does not handle properly, we will crash or fail +// in full page heap or purify environment. +TEST(TimerTest, OneShotSelfDeletingTimer) { + RunTest_OneShotSelfDeletingTimer(MessageLoop::TYPE_DEFAULT); + RunTest_OneShotSelfDeletingTimer(MessageLoop::TYPE_UI); + RunTest_OneShotSelfDeletingTimer(MessageLoop::TYPE_IO); +} + +TEST(TimerTest, RepeatingTimer) { + RunTest_RepeatingTimer(MessageLoop::TYPE_DEFAULT); + RunTest_RepeatingTimer(MessageLoop::TYPE_UI); + RunTest_RepeatingTimer(MessageLoop::TYPE_IO); +} + +TEST(TimerTest, RepeatingTimer_Cancel) { + RunTest_RepeatingTimer_Cancel(MessageLoop::TYPE_DEFAULT); + RunTest_RepeatingTimer_Cancel(MessageLoop::TYPE_UI); + RunTest_RepeatingTimer_Cancel(MessageLoop::TYPE_IO); +} + +TEST(TimerTest, DelayTimer_NoCall) { + RunTest_DelayTimer_NoCall(MessageLoop::TYPE_DEFAULT); + RunTest_DelayTimer_NoCall(MessageLoop::TYPE_UI); + RunTest_DelayTimer_NoCall(MessageLoop::TYPE_IO); +} + +TEST(TimerTest, DelayTimer_OneCall) { + RunTest_DelayTimer_OneCall(MessageLoop::TYPE_DEFAULT); + RunTest_DelayTimer_OneCall(MessageLoop::TYPE_UI); + RunTest_DelayTimer_OneCall(MessageLoop::TYPE_IO); +} + +// Disabled because it's flaky on the buildbot. Bug not filed because this +// kind of test is difficult to make not-flaky on buildbots. +TEST(TimerTest, DISABLED_DelayTimer_Reset) { + RunTest_DelayTimer_Reset(MessageLoop::TYPE_DEFAULT); + RunTest_DelayTimer_Reset(MessageLoop::TYPE_UI); + RunTest_DelayTimer_Reset(MessageLoop::TYPE_IO); +} + +TEST(TimerTest, DelayTimer_Deleted) { + RunTest_DelayTimer_Deleted(MessageLoop::TYPE_DEFAULT); + RunTest_DelayTimer_Deleted(MessageLoop::TYPE_UI); + RunTest_DelayTimer_Deleted(MessageLoop::TYPE_IO); +} + +TEST(TimerTest, MessageLoopShutdown) { + // This test is designed to verify that shutdown of the + // message loop does not cause crashes if there were pending + // timers not yet fired. It may only trigger exceptions + // if debug heap checking (or purify) is enabled. + bool did_run = false; + { + OneShotTimerTester a(&did_run); + OneShotTimerTester b(&did_run); + OneShotTimerTester c(&did_run); + OneShotTimerTester d(&did_run); + { + MessageLoop loop(MessageLoop::TYPE_DEFAULT); + a.Start(); + b.Start(); + } // MessageLoop destructs by falling out of scope. + } // OneShotTimers destruct. SHOULD NOT CRASH, of course. + + EXPECT_EQ(false, did_run); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,142 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/platform_thread.h" +#include "base/time.h" +#include "build/build_config.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::Time; +using base::TimeDelta; +using base::TimeTicks; + +// Test conversions to/from time_t and exploding/unexploding. +TEST(Time, TimeT) { + // C library time and exploded time. + time_t now_t_1 = time(NULL); + struct tm tms; +#if defined(OS_WIN) + localtime_s(&tms, &now_t_1); +#elif defined(OS_POSIX) + localtime_r(&now_t_1, &tms); +#endif + + // Convert to ours. + Time our_time_1 = Time::FromTimeT(now_t_1); + Time::Exploded exploded; + our_time_1.LocalExplode(&exploded); + + // This will test both our exploding and our time_t -> Time conversion. + EXPECT_EQ(tms.tm_year + 1900, exploded.year); + EXPECT_EQ(tms.tm_mon + 1, exploded.month); + EXPECT_EQ(tms.tm_mday, exploded.day_of_month); + EXPECT_EQ(tms.tm_hour, exploded.hour); + EXPECT_EQ(tms.tm_min, exploded.minute); + EXPECT_EQ(tms.tm_sec, exploded.second); + + // Convert exploded back to the time struct. + Time our_time_2 = Time::FromLocalExploded(exploded); + EXPECT_TRUE(our_time_1 == our_time_2); + + time_t now_t_2 = our_time_2.ToTimeT(); + EXPECT_EQ(now_t_1, now_t_2); + + EXPECT_EQ(10, Time().FromTimeT(10).ToTimeT()); + EXPECT_EQ(10.0, Time().FromTimeT(10).ToDoubleT()); + + // Conversions of 0 should stay 0. + EXPECT_EQ(0, Time().ToTimeT()); + EXPECT_EQ(0, Time::FromTimeT(0).ToInternalValue()); +} + +TEST(Time, ZeroIsSymmetric) { + Time zero_time(Time::FromTimeT(0)); + EXPECT_EQ(0, zero_time.ToTimeT()); + + EXPECT_EQ(0.0, zero_time.ToDoubleT()); +} + +TEST(Time, LocalExplode) { + Time a = Time::Now(); + Time::Exploded exploded; + a.LocalExplode(&exploded); + + Time b = Time::FromLocalExploded(exploded); + + // The exploded structure doesn't have microseconds, so the result will be + // rounded to the nearest millisecond. + EXPECT_TRUE((a - b) < TimeDelta::FromMilliseconds(1)); +} + +TEST(Time, UTCExplode) { + Time a = Time::Now(); + Time::Exploded exploded; + a.UTCExplode(&exploded); + + Time b = Time::FromUTCExploded(exploded); + EXPECT_TRUE((a - b) < TimeDelta::FromMilliseconds(1)); +} + +TEST(Time, LocalMidnight) { + Time::Exploded exploded; + Time::Now().LocalMidnight().LocalExplode(&exploded); + EXPECT_EQ(0, exploded.hour); + EXPECT_EQ(0, exploded.minute); + EXPECT_EQ(0, exploded.second); + EXPECT_EQ(0, exploded.millisecond); +} + +TEST(TimeTicks, Deltas) { + for (int index = 0; index < 500; index++) { + TimeTicks ticks_start = TimeTicks::Now(); + PlatformThread::Sleep(10); + TimeTicks ticks_stop = TimeTicks::Now(); + TimeDelta delta = ticks_stop - ticks_start; + // Note: Although we asked for a 10ms sleep, if the + // time clock has a finer granularity than the Sleep() + // clock, it is quite possible to wakeup early. Here + // is how that works: + // Time(ms timer) Time(us timer) + // 5 5010 + // 6 6010 + // 7 7010 + // 8 8010 + // 9 9000 + // Elapsed 4ms 3990us + // + // Unfortunately, our InMilliseconds() function truncates + // rather than rounds. We should consider fixing this + // so that our averages come out better. + EXPECT_GE(delta.InMilliseconds(), 9); + EXPECT_GE(delta.InMicroseconds(), 9000); + EXPECT_EQ(delta.InSeconds(), 0); + } +} + +TEST(TimeTicks, HighResNow) { + TimeTicks ticks_start = TimeTicks::HighResNow(); + PlatformThread::Sleep(10); + TimeTicks ticks_stop = TimeTicks::HighResNow(); + TimeDelta delta = ticks_stop - ticks_start; + EXPECT_GE(delta.InMicroseconds(), 9000); +} + +TEST(TimeDelta, FromAndIn) { + EXPECT_TRUE(TimeDelta::FromDays(2) == TimeDelta::FromHours(48)); + EXPECT_TRUE(TimeDelta::FromHours(3) == TimeDelta::FromMinutes(180)); + EXPECT_TRUE(TimeDelta::FromMinutes(2) == TimeDelta::FromSeconds(120)); + EXPECT_TRUE(TimeDelta::FromSeconds(2) == TimeDelta::FromMilliseconds(2000)); + EXPECT_TRUE(TimeDelta::FromMilliseconds(2) == + TimeDelta::FromMicroseconds(2000)); + EXPECT_EQ(13, TimeDelta::FromDays(13).InDays()); + EXPECT_EQ(13, TimeDelta::FromHours(13).InHours()); + EXPECT_EQ(13, TimeDelta::FromMinutes(13).InMinutes()); + EXPECT_EQ(13, TimeDelta::FromSeconds(13).InSeconds()); + EXPECT_EQ(13.0, TimeDelta::FromSeconds(13).InSecondsF()); + EXPECT_EQ(13, TimeDelta::FromMilliseconds(13).InMilliseconds()); + EXPECT_EQ(13.0, TimeDelta::FromMilliseconds(13).InMillisecondsF()); + EXPECT_EQ(13, TimeDelta::FromMicroseconds(13).InMicroseconds()); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,404 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + + +// Windows Timer Primer +// +// A good article: http://www.ddj.com/windows/184416651 +// A good mozilla bug: http://bugzilla.mozilla.org/show_bug.cgi?id=363258 +// +// The default windows timer, GetSystemTimeAsFileTime is not very precise. +// It is only good to ~15.5ms. +// +// QueryPerformanceCounter is the logical choice for a high-precision timer. +// However, it is known to be buggy on some hardware. Specifically, it can +// sometimes "jump". On laptops, QPC can also be very expensive to call. +// It's 3-4x slower than timeGetTime() on desktops, but can be 10x slower +// on laptops. A unittest exists which will show the relative cost of various +// timers on any system. +// +// The next logical choice is timeGetTime(). timeGetTime has a precision of +// 1ms, but only if you call APIs (timeBeginPeriod()) which affect all other +// applications on the system. By default, precision is only 15.5ms. +// Unfortunately, we don't want to call timeBeginPeriod because we don't +// want to affect other applications. Further, on mobile platforms, use of +// faster multimedia timers can hurt battery life. See the intel +// article about this here: +// http://softwarecommunity.intel.com/articles/eng/1086.htm +// +// To work around all this, we're going to generally use timeGetTime(). We +// will only increase the system-wide timer if we're not running on battery +// power. Using timeBeginPeriod(1) is a requirement in order to make our +// message loop waits have the same resolution that our time measurements +// do. Otherwise, WaitForSingleObject(..., 1) will no less than 15ms when +// there is nothing else to waken the Wait. + +#include "base/time.h" + +#pragma comment(lib, "winmm.lib") +#include +#include + +#include "base/basictypes.h" +#include "base/lock.h" +#include "base/logging.h" +#include "base/cpu.h" +#include "base/singleton.h" +#include "base/system_monitor.h" + +using base::Time; +using base::TimeDelta; +using base::TimeTicks; + +namespace { + +// From MSDN, FILETIME "Contains a 64-bit value representing the number of +// 100-nanosecond intervals since January 1, 1601 (UTC)." +int64 FileTimeToMicroseconds(const FILETIME& ft) { + // Need to bit_cast to fix alignment, then divide by 10 to convert + // 100-nanoseconds to milliseconds. This only works on little-endian + // machines. + return bit_cast(ft) / 10; +} + +void MicrosecondsToFileTime(int64 us, FILETIME* ft) { + DCHECK(us >= 0) << "Time is less than 0, negative values are not " + "representable in FILETIME"; + + // Multiply by 10 to convert milliseconds to 100-nanoseconds. Bit_cast will + // handle alignment problems. This only works on little-endian machines. + *ft = bit_cast(us * 10); +} + +int64 CurrentWallclockMicroseconds() { + FILETIME ft; + ::GetSystemTimeAsFileTime(&ft); + return FileTimeToMicroseconds(ft); +} + +// Time between resampling the un-granular clock for this API. 60 seconds. +const int kMaxMillisecondsToAvoidDrift = 60 * Time::kMillisecondsPerSecond; + +int64 initial_time = 0; +TimeTicks initial_ticks; + +void InitializeClock() { + initial_ticks = TimeTicks::Now(); + initial_time = CurrentWallclockMicroseconds(); +} + +} // namespace + +// Time ----------------------------------------------------------------------- + +// The internal representation of Time uses FILETIME, whose epoch is 1601-01-01 +// 00:00:00 UTC. ((1970-1601)*365+89)*24*60*60*1000*1000, where 89 is the +// number of leap year days between 1601 and 1970: (1970-1601)/4 excluding +// 1700, 1800, and 1900. +// static +const int64 Time::kTimeTToMicrosecondsOffset = GG_INT64_C(11644473600000000); + +// static +Time Time::Now() { + if (initial_time == 0) + InitializeClock(); + + // We implement time using the high-resolution timers so that we can get + // timeouts which are smaller than 10-15ms. If we just used + // CurrentWallclockMicroseconds(), we'd have the less-granular timer. + // + // To make this work, we initialize the clock (initial_time) and the + // counter (initial_ctr). To compute the initial time, we can check + // the number of ticks that have elapsed, and compute the delta. + // + // To avoid any drift, we periodically resync the counters to the system + // clock. + while(true) { + TimeTicks ticks = TimeTicks::Now(); + + // Calculate the time elapsed since we started our timer + TimeDelta elapsed = ticks - initial_ticks; + + // Check if enough time has elapsed that we need to resync the clock. + if (elapsed.InMilliseconds() > kMaxMillisecondsToAvoidDrift) { + InitializeClock(); + continue; + } + + return Time(elapsed + initial_time); + } +} + +// static +Time Time::NowFromSystemTime() { + // Force resync. + InitializeClock(); + return Time(initial_time); +} + +// static +Time Time::FromFileTime(FILETIME ft) { + return Time(FileTimeToMicroseconds(ft)); +} + +FILETIME Time::ToFileTime() const { + FILETIME utc_ft; + MicrosecondsToFileTime(us_, &utc_ft); + return utc_ft; +} + +// static +Time Time::FromExploded(bool is_local, const Exploded& exploded) { + // Create the system struct representing our exploded time. It will either be + // in local time or UTC. + SYSTEMTIME st; + st.wYear = exploded.year; + st.wMonth = exploded.month; + st.wDayOfWeek = exploded.day_of_week; + st.wDay = exploded.day_of_month; + st.wHour = exploded.hour; + st.wMinute = exploded.minute; + st.wSecond = exploded.second; + st.wMilliseconds = exploded.millisecond; + + // Convert to FILETIME. + FILETIME ft; + if (!SystemTimeToFileTime(&st, &ft)) { + NOTREACHED() << "Unable to convert time"; + return Time(0); + } + + // Ensure that it's in UTC. + if (is_local) { + FILETIME utc_ft; + LocalFileTimeToFileTime(&ft, &utc_ft); + return Time(FileTimeToMicroseconds(utc_ft)); + } + return Time(FileTimeToMicroseconds(ft)); +} + +void Time::Explode(bool is_local, Exploded* exploded) const { + // FILETIME in UTC. + FILETIME utc_ft; + MicrosecondsToFileTime(us_, &utc_ft); + + // FILETIME in local time if necessary. + BOOL success = TRUE; + FILETIME ft; + if (is_local) + success = FileTimeToLocalFileTime(&utc_ft, &ft); + else + ft = utc_ft; + + // FILETIME in SYSTEMTIME (exploded). + SYSTEMTIME st; + if (!success || !FileTimeToSystemTime(&ft, &st)) { + NOTREACHED() << "Unable to convert time, don't know why"; + ZeroMemory(exploded, sizeof(exploded)); + return; + } + + exploded->year = st.wYear; + exploded->month = st.wMonth; + exploded->day_of_week = st.wDayOfWeek; + exploded->day_of_month = st.wDay; + exploded->hour = st.wHour; + exploded->minute = st.wMinute; + exploded->second = st.wSecond; + exploded->millisecond = st.wMilliseconds; +} + +// TimeTicks ------------------------------------------------------------------ +namespace { + +// We define a wrapper to adapt between the __stdcall and __cdecl call of the +// mock function, and to avoid a static constructor. Assigning an import to a +// function pointer directly would require setup code to fetch from the IAT. +DWORD timeGetTimeWrapper() { + return timeGetTime(); +} + + +DWORD (*tick_function)(void) = &timeGetTimeWrapper; + +// We use timeGetTime() to implement TimeTicks::Now(). This can be problematic +// because it returns the number of milliseconds since Windows has started, +// which will roll over the 32-bit value every ~49 days. We try to track +// rollover ourselves, which works if TimeTicks::Now() is called at least every +// 49 days. +class NowSingleton : public base::SystemMonitor::PowerObserver { + public: + NowSingleton() + : rollover_(TimeDelta::FromMilliseconds(0)), + last_seen_(0), + hi_res_clock_enabled_(false) { + base::SystemMonitor* system = base::SystemMonitor::Get(); + system->AddObserver(this); + UseHiResClock(!system->BatteryPower()); + } + + ~NowSingleton() { + UseHiResClock(false); + base::SystemMonitor* monitor = base::SystemMonitor::Get(); + if (monitor) + monitor->RemoveObserver(this); + } + + TimeDelta Now() { + AutoLock locked(lock_); + // We should hold the lock while calling tick_function to make sure that + // we keep our last_seen_ stay correctly in sync. + DWORD now = tick_function(); + if (now < last_seen_) + rollover_ += TimeDelta::FromMilliseconds(0x100000000I64); // ~49.7 days. + last_seen_ = now; + return TimeDelta::FromMilliseconds(now) + rollover_; + } + + // Interfaces for monitoring Power changes. + void OnPowerStateChange(base::SystemMonitor* system) { + UseHiResClock(!system->BatteryPower()); + } + + void OnSuspend(base::SystemMonitor* system) {} + void OnResume(base::SystemMonitor* system) {} + + private: + // Enable or disable the faster multimedia timer. + void UseHiResClock(bool enabled) { + if (enabled == hi_res_clock_enabled_) + return; + if (enabled) + timeBeginPeriod(1); + else + timeEndPeriod(1); + hi_res_clock_enabled_ = enabled; + } + + Lock lock_; // To protected last_seen_ and rollover_. + TimeDelta rollover_; // Accumulation of time lost due to rollover. + DWORD last_seen_; // The last timeGetTime value we saw, to detect rollover. + bool hi_res_clock_enabled_; + + DISALLOW_COPY_AND_ASSIGN(NowSingleton); +}; + +// Overview of time counters: +// (1) CPU cycle counter. (Retrieved via RDTSC) +// The CPU counter provides the highest resolution time stamp and is the least +// expensive to retrieve. However, the CPU counter is unreliable and should not +// be used in production. Its biggest issue is that it is per processor and it +// is not synchronized between processors. Also, on some computers, the counters +// will change frequency due to thermal and power changes, and stop in some +// states. +// +// (2) QueryPerformanceCounter (QPC). The QPC counter provides a high- +// resolution (100 nanoseconds) time stamp but is comparatively more expensive +// to retrieve. What QueryPerformanceCounter actually does is up to the HAL. +// (with some help from ACPI). +// According to http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx +// in the worst case, it gets the counter from the rollover interrupt on the +// programmable interrupt timer. In best cases, the HAL may conclude that the +// RDTSC counter runs at a constant frequency, then it uses that instead. On +// multiprocessor machines, it will try to verify the values returned from +// RDTSC on each processor are consistent with each other, and apply a handful +// of workarounds for known buggy hardware. In other words, QPC is supposed to +// give consistent result on a multiprocessor computer, but it is unreliable in +// reality due to bugs in BIOS or HAL on some, especially old computers. +// With recent updates on HAL and newer BIOS, QPC is getting more reliable but +// it should be used with caution. +// +// (3) System time. The system time provides a low-resolution (typically 10ms +// to 55 milliseconds) time stamp but is comparatively less expensive to +// retrieve and more reliable. +class HighResNowSingleton { + public: + HighResNowSingleton() + : ticks_per_microsecond_(0.0), + skew_(0) { + InitializeClock(); + + // On Athlon X2 CPUs (e.g. model 15) QueryPerformanceCounter is + // unreliable. Fallback to low-res clock. + base::CPU cpu; + if (cpu.vendor_name() == "AuthenticAMD" && cpu.family() == 15) + DisableHighResClock(); + } + + bool IsUsingHighResClock() { + return ticks_per_microsecond_ != 0.0; + } + + void DisableHighResClock() { + ticks_per_microsecond_ = 0.0; + } + + TimeDelta Now() { + // Our maximum tolerance for QPC drifting. + const int kMaxTimeDrift = 50 * Time::kMicrosecondsPerMillisecond; + + if (IsUsingHighResClock()) { + int64 now = UnreliableNow(); + + // Verify that QPC does not seem to drift. + DCHECK(now - ReliableNow() - skew_ < kMaxTimeDrift); + + return TimeDelta::FromMicroseconds(now); + } + + // Just fallback to the slower clock. + return Singleton::get()->Now(); + } + + private: + // Synchronize the QPC clock with GetSystemTimeAsFileTime. + void InitializeClock() { + LARGE_INTEGER ticks_per_sec = {0}; + if (!QueryPerformanceFrequency(&ticks_per_sec)) + return; // Broken, we don't guarantee this function works. + ticks_per_microsecond_ = static_cast(ticks_per_sec.QuadPart) / + static_cast(Time::kMicrosecondsPerSecond); + + skew_ = UnreliableNow() - ReliableNow(); + } + + // Get the number of microseconds since boot in a reliable fashion + int64 UnreliableNow() { + LARGE_INTEGER now; + QueryPerformanceCounter(&now); + return static_cast(now.QuadPart / ticks_per_microsecond_); + } + + // Get the number of microseconds since boot in a reliable fashion + int64 ReliableNow() { + return Singleton::get()->Now().InMicroseconds(); + } + + // Cached clock frequency -> microseconds. This assumes that the clock + // frequency is faster than one microsecond (which is 1MHz, should be OK). + float ticks_per_microsecond_; // 0 indicates QPF failed and we're broken. + int64 skew_; // Skew between lo-res and hi-res clocks (for debugging). + + DISALLOW_COPY_AND_ASSIGN(HighResNowSingleton); +}; + +} // namespace + +// static +TimeTicks::TickFunctionType TimeTicks::SetMockTickFunction( + TickFunctionType ticker) { + TickFunctionType old = tick_function; + tick_function = ticker; + return old; +} + +// static +TimeTicks TimeTicks::Now() { + return TimeTicks() + Singleton::get()->Now(); +} + +// static +TimeTicks TimeTicks::HighResNow() { + return TimeTicks() + Singleton::get()->Now(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time_win_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time_win_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/time_win_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/time_win_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,204 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include + +#include "base/time.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::Time; +using base::TimeDelta; +using base::TimeTicks; + +namespace { + +class MockTimeTicks : public TimeTicks { + public: + static DWORD Ticker() { + return static_cast(InterlockedIncrement(&ticker_)); + } + + static void InstallTicker() { + old_tick_function_ = SetMockTickFunction(&Ticker); + ticker_ = -5; + } + + static void UninstallTicker() { + SetMockTickFunction(old_tick_function_); + } + + private: + static volatile LONG ticker_; + static TickFunctionType old_tick_function_; +}; + +volatile LONG MockTimeTicks::ticker_; +MockTimeTicks::TickFunctionType MockTimeTicks::old_tick_function_; + +HANDLE g_rollover_test_start; + +unsigned __stdcall RolloverTestThreadMain(void* param) { + int64 counter = reinterpret_cast(param); + DWORD rv = WaitForSingleObject(g_rollover_test_start, INFINITE); + EXPECT_EQ(rv, WAIT_OBJECT_0); + + TimeTicks last = TimeTicks::Now(); + for (int index = 0; index < counter; index++) { + TimeTicks now = TimeTicks::Now(); + int64 milliseconds = (now - last).InMilliseconds(); + // This is a tight loop; we could have looped faster than our + // measurements, so the time might be 0 millis. + EXPECT_GE(milliseconds, 0); + EXPECT_LT(milliseconds, 250); + last = now; + } + return 0; +} + +} // namespace + +TEST(TimeTicks, WinRollover) { + // The internal counter rolls over at ~49days. We'll use a mock + // timer to test this case. + // Basic test algorithm: + // 1) Set clock to rollover - N + // 2) Create N threads + // 3) Start the threads + // 4) Each thread loops through TimeTicks() N times + // 5) Each thread verifies integrity of result. + + const int kThreads = 8; + // Use int64 so we can cast into a void* without a compiler warning. + const int64 kChecks = 10; + + // It takes a lot of iterations to reproduce the bug! + // (See bug 1081395) + for (int loop = 0; loop < 4096; loop++) { + // Setup + MockTimeTicks::InstallTicker(); + g_rollover_test_start = CreateEvent(0, TRUE, FALSE, 0); + HANDLE threads[kThreads]; + + for (int index = 0; index < kThreads; index++) { + void* argument = reinterpret_cast(kChecks); + unsigned thread_id; + threads[index] = reinterpret_cast( + _beginthreadex(NULL, 0, RolloverTestThreadMain, argument, 0, + &thread_id)); + EXPECT_NE((HANDLE)NULL, threads[index]); + } + + // Start! + SetEvent(g_rollover_test_start); + + // Wait for threads to finish + for (int index = 0; index < kThreads; index++) { + DWORD rv = WaitForSingleObject(threads[index], INFINITE); + EXPECT_EQ(rv, WAIT_OBJECT_0); + } + + CloseHandle(g_rollover_test_start); + + // Teardown + MockTimeTicks::UninstallTicker(); + } +} + +TEST(TimeTicks, SubMillisecondTimers) { + // Loop for a bit getting timers quickly. We want to + // see at least one case where we get a new sample in + // less than one millisecond. + bool saw_submillisecond_timer = false; + int64 min_timer = 1000; + TimeTicks last_time = TimeTicks::HighResNow(); + for (int index = 0; index < 1000; index++) { + TimeTicks now = TimeTicks::HighResNow(); + TimeDelta delta = now - last_time; + if (delta.InMicroseconds() > 0 && + delta.InMicroseconds() < 1000) { + if (min_timer > delta.InMicroseconds()) + min_timer = delta.InMicroseconds(); + saw_submillisecond_timer = true; + } + last_time = now; + } + EXPECT_TRUE(saw_submillisecond_timer); + printf("Min timer is: %ldus\n", static_cast(min_timer)); +} + +TEST(TimeTicks, TimeGetTimeCaps) { + // Test some basic assumptions that we expect about how timeGetDevCaps works. + + TIMECAPS caps; + MMRESULT status = timeGetDevCaps(&caps, sizeof(caps)); + EXPECT_EQ(TIMERR_NOERROR, status); + if (status != TIMERR_NOERROR) { + printf("Could not get timeGetDevCaps\n"); + return; + } + + EXPECT_GE(static_cast(caps.wPeriodMin), 1); + EXPECT_GT(static_cast(caps.wPeriodMax), 1); + EXPECT_GE(static_cast(caps.wPeriodMin), 1); + EXPECT_GT(static_cast(caps.wPeriodMax), 1); + printf("timeGetTime range is %d to %dms\n", caps.wPeriodMin, + caps.wPeriodMax); +} + +TEST(TimeTicks, QueryPerformanceFrequency) { + // Test some basic assumptions that we expect about QPC. + + LARGE_INTEGER frequency; + BOOL rv = QueryPerformanceFrequency(&frequency); + EXPECT_EQ(TRUE, rv); + EXPECT_GT(frequency.QuadPart, 1000000); // Expect at least 1MHz + printf("QueryPerformanceFrequency is %5.2fMHz\n", + frequency.QuadPart / 1000000.0); +} + +TEST(TimeTicks, TimerPerformance) { + // Verify that various timer mechanisms can always complete quickly. + // Note: This is a somewhat arbitrary test. + const int kLoops = 10000; + // Due to the fact that these run on bbots, which are horribly slow, + // we can't really make any guarantees about minimum runtime. + // Really, we want these to finish in ~10ms, and that is generous. + const int kMaxTime = 35; // Maximum acceptible milliseconds for test. + + typedef TimeTicks (*TestFunc)(); + struct TestCase { + TestFunc func; + char *description; + }; + // Cheating a bit here: assumes sizeof(TimeTicks) == sizeof(Time) + // in order to create a single test case list. + COMPILE_ASSERT(sizeof(TimeTicks) == sizeof(Time), + test_only_works_with_same_sizes); + TestCase cases[] = { + { reinterpret_cast(Time::Now), "Time::Now" }, + { TimeTicks::Now, "TimeTicks::Now" }, + { TimeTicks::HighResNow, "TimeTicks::HighResNow" }, + { NULL, "" } + }; + + int test_case = 0; + while (cases[test_case].func) { + TimeTicks start = TimeTicks::HighResNow(); + for (int index = 0; index < kLoops; index++) + cases[test_case].func(); + TimeTicks stop = TimeTicks::HighResNow(); + // Turning off the check for acceptible delays. Without this check, + // the test really doesn't do much other than measure. But the + // measurements are still useful for testing timers on various platforms. + // The reason to remove the check is because the tests run on many + // buildbots, some of which are VMs. These machines can run horribly + // slow, and there is really no value for checking against a max timer. + //EXPECT_LT((stop - start).InMilliseconds(), kMaxTime); + printf("%s: %1.2fus per call\n", cases[test_case].description, + (stop - start).InMillisecondsF() * 1000 / kLoops); + test_case++; + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/trace_event.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/trace_event.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/trace_event.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/trace_event.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,155 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/trace_event.h" + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/platform_thread.h" +#include "base/process_util.h" +#include "base/string_util.h" +#include "base/time.h" + +#define USE_UNRELIABLE_NOW + +namespace base { + +static const char* kEventTypeNames[] = { + "BEGIN", + "END", + "INSTANT" +}; + +static const FilePath::CharType* kLogFileName = + FILE_PATH_LITERAL("trace_%d.log"); + +TraceLog::TraceLog() : enabled_(false), log_file_(NULL) { + base::ProcessHandle proc = base::GetCurrentProcessHandle(); + process_metrics_.reset(base::ProcessMetrics::CreateProcessMetrics(proc)); +} + +TraceLog::~TraceLog() { + Stop(); +} + +// static +bool TraceLog::IsTracing() { + TraceLog* trace = Singleton::get(); + return trace->enabled_; +} + +// static +bool TraceLog::StartTracing() { + TraceLog* trace = Singleton::get(); + return trace->Start(); +} + +bool TraceLog::Start() { + if (enabled_) + return true; + enabled_ = OpenLogFile(); + if (enabled_) { + Log("var raw_trace_events = [\n"); + trace_start_time_ = TimeTicks::Now(); + timer_.Start(TimeDelta::FromMilliseconds(250), this, &TraceLog::Heartbeat); + } + return enabled_; +} + +// static +void TraceLog::StopTracing() { + TraceLog* trace = Singleton::get(); + return trace->Stop(); +} + +void TraceLog::Stop() { + if (enabled_) { + enabled_ = false; + Log("];\n"); + CloseLogFile(); + timer_.Stop(); + } +} + +void TraceLog::Heartbeat() { + std::string cpu = StringPrintf("%d", process_metrics_->GetCPUUsage()); + TRACE_EVENT_INSTANT("heartbeat.cpu", 0, cpu); +} + +void TraceLog::CloseLogFile() { + if (log_file_) { + file_util::CloseFile(log_file_); + } +} + +bool TraceLog::OpenLogFile() { + FilePath::StringType pid_filename = + StringPrintf(kLogFileName, base::GetCurrentProcId()); + FilePath log_file_path; + if (!PathService::Get(base::DIR_EXE, &log_file_path)) + return false; + log_file_path = log_file_path.Append(pid_filename); + log_file_ = file_util::OpenFile(log_file_path, "a"); + if (!log_file_) { + // try the current directory + log_file_ = file_util::OpenFile(FilePath(pid_filename), "a"); + if (!log_file_) { + return false; + } + } + return true; +} + +void TraceLog::Trace(const std::string& name, + EventType type, + const void* id, + const std::wstring& extra, + const char* file, + int line) { + if (!enabled_) + return; + Trace(name, type, id, WideToUTF8(extra), file, line); +} + +void TraceLog::Trace(const std::string& name, + EventType type, + const void* id, + const std::string& extra, + const char* file, + int line) { + if (!enabled_) + return; + +#ifdef USE_UNRELIABLE_NOW + TimeTicks tick = TimeTicks::HighResNow(); +#else + TimeTicks tick = TimeTicks::Now(); +#endif + TimeDelta delta = tick - trace_start_time_; + int64 usec = delta.InMicroseconds(); + std::string msg = + StringPrintf("{'pid':'0x%lx', 'tid':'0x%lx', 'type':'%s', " + "'name':'%s', 'id':'0x%lx', 'extra':'%s', 'file':'%s', " + "'line_number':'%d', 'usec_begin': %I64d},\n", + base::GetCurrentProcId(), + PlatformThread::CurrentId(), + kEventTypeNames[type], + name.c_str(), + id, + extra.c_str(), + file, + line, + usec); + + Log(msg); +} + +void TraceLog::Log(const std::string& msg) { + AutoLock lock(file_lock_); + + fprintf(log_file_, "%s", msg.c_str()); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/trace_event.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/trace_event.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/trace_event.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/trace_event.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,126 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Trace events to track application performance. Events consist of a name +// a type (BEGIN, END or INSTANT), a tracking id and extra string data. +// In addition, the current process id, thread id, a timestamp down to the +// microsecond and a file and line number of the calling location. +// +// The current implementation logs these events into a log file of the form +// trace_.log where it's designed to be post-processed to generate a +// trace report. In the future, it may use another mechansim to facilitate +// real-time analysis. + +#ifndef BASE_TRACE_EVENT_H_ +#define BASE_TRACE_EVENT_H_ + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#endif + +#include + +#include "base/lock.h" +#include "base/scoped_ptr.h" +#include "base/singleton.h" +#include "base/time.h" +#include "base/timer.h" + +// Use the following macros rather than using the TraceLog class directly as the +// underlying implementation may change in the future. Here's a sample usage: +// TRACE_EVENT_BEGIN("v8.run", documentId, scriptLocation); +// RunScript(script); +// TRACE_EVENT_END("v8.run", documentId, scriptLocation); + +// Record that an event (of name, id) has begun. All BEGIN events should have +// corresponding END events with a matching (name, id). +#define TRACE_EVENT_BEGIN(name, id, extra) \ + Singleton::get()->Trace(name, \ + base::TraceLog::EVENT_BEGIN, \ + reinterpret_cast(id), \ + extra, \ + __FILE__, \ + __LINE__) + +// Record that an event (of name, id) has ended. All END events should have +// corresponding BEGIN events with a matching (name, id). +#define TRACE_EVENT_END(name, id, extra) \ + Singleton::get()->Trace(name, \ + base::TraceLog::EVENT_END, \ + reinterpret_cast(id), \ + extra, \ + __FILE__, \ + __LINE__) + +// Record that an event (of name, id) with no duration has happened. +#define TRACE_EVENT_INSTANT(name, id, extra) \ + Singleton::get()->Trace(name, \ + base::TraceLog::EVENT_INSTANT, \ + reinterpret_cast(id), \ + extra, \ + __FILE__, \ + __LINE__) + +namespace base { +class ProcessMetrics; +} + +namespace base { + +class TraceLog { + public: + enum EventType { + EVENT_BEGIN, + EVENT_END, + EVENT_INSTANT + }; + + // Is tracing currently enabled. + static bool IsTracing(); + // Start logging trace events. + static bool StartTracing(); + // Stop logging trace events. + static void StopTracing(); + + // Log a trace event of (name, type, id) with the optional extra string. + void Trace(const std::string& name, + EventType type, + const void* id, + const std::wstring& extra, + const char* file, + int line); + void Trace(const std::string& name, + EventType type, + const void* id, + const std::string& extra, + const char* file, + int line); + + private: + // This allows constructor and destructor to be private and usable only + // by the Singleton class. + friend struct DefaultSingletonTraits; + + TraceLog(); + ~TraceLog(); + bool OpenLogFile(); + void CloseLogFile(); + bool Start(); + void Stop(); + void Heartbeat(); + void Log(const std::string& msg); + + bool enabled_; + FILE* log_file_; + Lock file_lock_; + TimeTicks trace_start_time_; + scoped_ptr process_metrics_; + RepeatingTimer timer_; +}; + +} // namespace base + +#endif // BASE_TRACE_EVENT_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/tracked.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/tracked.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/tracked.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/tracked.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,94 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/tracked.h" + +#include "base/string_util.h" +#include "base/tracked_objects.h" + +using base::Time; + +namespace tracked_objects { + +//------------------------------------------------------------------------------ +void Location::Write(bool display_filename, bool display_function_name, + std::string* output) const { + StringAppendF(output, "%s[%d] ", + display_filename ? file_name_ : "line", + line_number_); + + if (display_function_name) { + WriteFunctionName(output); + output->push_back(' '); + } +} + +void Location::WriteFunctionName(std::string* output) const { + // Translate "<" to "<" for HTML safety. + // TODO(jar): Support ASCII or html for logging in ASCII. + for (const char *p = function_name_; *p; p++) { + switch (*p) { + case '<': + output->append("<"); + break; + + case '>': + output->append(">"); + break; + + default: + output->push_back(*p); + break; + } + } +} + +//------------------------------------------------------------------------------ + +#ifndef TRACK_ALL_TASK_OBJECTS + +Tracked::Tracked() {} +Tracked::~Tracked() {} +void Tracked::SetBirthPlace(const Location& from_here) {} +bool Tracked::MissingBirthplace() const { return false; } +void Tracked::ResetBirthTime() {} + +#else + +Tracked::Tracked() : tracked_births_(NULL), tracked_birth_time_(Time::Now()) { + if (!ThreadData::IsActive()) + return; + SetBirthPlace(Location("NoFunctionName", "NeedToSetBirthPlace", -1)); +} + +Tracked::~Tracked() { + if (!ThreadData::IsActive() || !tracked_births_) + return; + ThreadData::current()->TallyADeath(*tracked_births_, + Time::Now() - tracked_birth_time_); +} + +void Tracked::SetBirthPlace(const Location& from_here) { + if (!ThreadData::IsActive()) + return; + if (tracked_births_) + tracked_births_->ForgetBirth(); + ThreadData* current_thread_data = ThreadData::current(); + if (!current_thread_data) + return; // Shutdown started, and this thread wasn't registered. + tracked_births_ = current_thread_data->FindLifetime(from_here); + tracked_births_->RecordBirth(); +} + +void Tracked::ResetBirthTime() { + tracked_birth_time_ = Time::Now(); +} + +bool Tracked::MissingBirthplace() const { + return -1 == tracked_births_->location().line_number(); +} + +#endif // NDEBUG + +} // namespace tracked_objects diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/tracked.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/tracked.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/tracked.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/tracked.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,128 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//------------------------------------------------------------------------------ +// Tracked is the base class for all tracked objects. During construction, it +// registers the fact that an instance was created, and at destruction time, it +// records that event. The instance may be tagged with a name, which is refered +// to as its Location. The Location is a file and line number, most +// typically indicated where the object was constructed. In some cases, as the +// object's significance is refined (for example, a Task object is augmented to +// do additonal things), its Location may be redefined to that later location. + +// Tracking includes (for each instance) recording the birth thread, death +// thread, and duration of life (from construction to destruction). All this +// data is accumulated and filtered for review at about:objects. + +#ifndef BASE_TRACKED_H_ +#define BASE_TRACKED_H_ + +#include + +#include "base/time.h" + +#ifndef NDEBUG +#ifndef TRACK_ALL_TASK_OBJECTS +#define TRACK_ALL_TASK_OBJECTS +#endif // TRACK_ALL_TASK_OBJECTS +#endif // NDEBUG + +namespace tracked_objects { + +//------------------------------------------------------------------------------ +// Location provides basic info where of an object was constructed, or was +// significantly brought to life. + +class Location { + public: + // Constructor should be called with a long-lived char*, such as __FILE__. + // It assumes the provided value will persist as a global constant, and it + // will not make a copy of it. + Location(const char* function_name, const char* file_name, int line_number) + : function_name_(function_name), + file_name_(file_name), + line_number_(line_number) { } + + // Provide a default constructor for easy of debugging. + Location() + : function_name_("Unknown"), + file_name_("Unknown"), + line_number_(-1) { } + + // Comparison operator for insertion into a std::map<> hash tables. + // All we need is *some* (any) hashing distinction. Strings should already + // be unique, so we don't bother with strcmp or such. + // Use line number as the primary key (because it is fast, and usually gets us + // a difference), and then pointers as secondary keys (just to get some + // distinctions). + bool operator < (const Location& other) const { + if (line_number_ != other.line_number_) + return line_number_ < other.line_number_; + if (file_name_ != other.file_name_) + return file_name_ < other.file_name_; + return function_name_ < other.function_name_; + } + + const char* function_name() const { return function_name_; } + const char* file_name() const { return file_name_; } + int line_number() const { return line_number_; } + + void Write(bool display_filename, bool display_function_name, + std::string* output) const; + + // Write function_name_ in HTML with '<' and '>' properly encoded. + void WriteFunctionName(std::string* output) const; + + private: + const char* const function_name_; + const char* const file_name_; + const int line_number_; +}; + + +//------------------------------------------------------------------------------ +// Define a macro to record the current source location. + +#define FROM_HERE tracked_objects::Location(__FUNCTION__, __FILE__, __LINE__) + + +//------------------------------------------------------------------------------ + + +class Births; + +class Tracked { + public: + Tracked(); + virtual ~Tracked(); + + // Used to record the FROM_HERE location of a caller. + void SetBirthPlace(const Location& from_here); + + // When a task sits around a long time, such as in a timer, or object watcher, + // this method should be called when the task becomes active, and its + // significant lifetime begins (and its waiting to be woken up has passed). + void ResetBirthTime(); + + bool MissingBirthplace() const; + + private: +#ifdef TRACK_ALL_TASK_OBJECTS + + // Pointer to instance were counts of objects with the same birth location + // (on the same thread) are stored. + Births* tracked_births_; + // The time this object was constructed. If its life consisted of a long + // waiting period, and then it became active, then this value is generally + // reset before the object begins it active life. + base::Time tracked_birth_time_; + +#endif // TRACK_ALL_TASK_OBJECTS + + DISALLOW_COPY_AND_ASSIGN(Tracked); +}; + +} // namespace tracked_objects + +#endif // BASE_TRACKED_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/tracked_objects.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/tracked_objects.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/tracked_objects.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/tracked_objects.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,906 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/tracked_objects.h" + +#include + +#include "base/string_util.h" + +using base::TimeDelta; + +namespace tracked_objects { + +// A TLS slot to the TrackRegistry for the current thread. +// static +TLSSlot ThreadData::tls_index_(base::LINKER_INITIALIZED); + +//------------------------------------------------------------------------------ +// Death data tallies durations when a death takes place. + +void DeathData::RecordDeath(const TimeDelta& duration) { + ++count_; + life_duration_ += duration; + int64 milliseconds = duration.InMilliseconds(); + square_duration_ += milliseconds * milliseconds; +} + +int DeathData::AverageMsDuration() const { + return static_cast(life_duration_.InMilliseconds() / count_); +} + +double DeathData::StandardDeviation() const { + double average = AverageMsDuration(); + double variance = static_cast(square_duration_)/count_ + - average * average; + return sqrt(variance); +} + + +void DeathData::AddDeathData(const DeathData& other) { + count_ += other.count_; + life_duration_ += other.life_duration_; + square_duration_ += other.square_duration_; +} + +void DeathData::Write(std::string* output) const { + if (!count_) + return; + if (1 == count_) + StringAppendF(output, "(1)Life in %dms ", AverageMsDuration()); + else + StringAppendF(output, "(%d)Lives %dms/life ", count_, AverageMsDuration()); +} + +void DeathData::Clear() { + count_ = 0; + life_duration_ = TimeDelta(); + square_duration_ = 0; +} + +//------------------------------------------------------------------------------ + +BirthOnThread::BirthOnThread(const Location& location) + : location_(location), + birth_thread_(ThreadData::current()) { } + +//------------------------------------------------------------------------------ +Births::Births(const Location& location) + : BirthOnThread(location), + birth_count_(0) { } + +//------------------------------------------------------------------------------ +// ThreadData maintains the central data for all births and death. + +// static +ThreadData* ThreadData::first_ = NULL; +// static +Lock ThreadData::list_lock_; + +// static +ThreadData::Status ThreadData::status_ = ThreadData::UNINITIALIZED; + +ThreadData::ThreadData() : next_(NULL), message_loop_(MessageLoop::current()) {} + +// static +ThreadData* ThreadData::current() { + if (!tls_index_.initialized()) + return NULL; + + ThreadData* registry = static_cast(tls_index_.Get()); + if (!registry) { + // We have to create a new registry for ThreadData. + bool too_late_to_create = false; + { + registry = new ThreadData; + AutoLock lock(list_lock_); + // Use lock to insure we have most recent status. + if (!IsActive()) { + too_late_to_create = true; + } else { + // Use lock to insert into list. + registry->next_ = first_; + first_ = registry; + } + } // Release lock. + if (too_late_to_create) { + delete registry; + registry = NULL; + } else { + tls_index_.Set(registry); + } + } + return registry; +} + +// Do mininimal fixups for searching function names. +static std::string UnescapeQuery(const std::string& query) { + std::string result; + for (size_t i = 0; i < query.size(); i++) { + char next = query[i]; + if ('%' == next && i + 2 < query.size()) { + std::string hex = query.substr(i + 1, 2); + char replacement = '\0'; + // Only bother with "<", ">", and " ". + if (LowerCaseEqualsASCII(hex, "3c")) + replacement ='<'; + else if (LowerCaseEqualsASCII(hex, "3e")) + replacement = '>'; + else if (hex == "20") + replacement = ' '; + if (replacement) { + next = replacement; + i += 2; + } + } + result.push_back(next); + } + return result; +} + +// static +void ThreadData::WriteHTML(const std::string& query, std::string* output) { + if (!ThreadData::IsActive()) + return; // Not yet initialized. + + DCHECK(ThreadData::current()); + + output->append("About Objects"); + std::string escaped_query = UnescapeQuery(query); + if (!escaped_query.empty()) + output->append(" - " + escaped_query); + output->append("
    ");
    +
    +  DataCollector collected_data;  // Gather data.
    +  collected_data.AddListOfLivingObjects();  // Add births that are still alive.
    +
    +  // Data Gathering is complete. Now to sort/process/render.
    +  DataCollector::Collection* collection = collected_data.collection();
    +
    +  // Create filtering and sort comparison object.
    +  Comparator comparator;
    +  comparator.ParseQuery(escaped_query);
    +
    +  // Filter out acceptable (matching) instances.
    +  DataCollector::Collection match_array;
    +  for (DataCollector::Collection::iterator it = collection->begin();
    +       it != collection->end(); ++it) {
    +    if (comparator.Acceptable(*it))
    +      match_array.push_back(*it);
    +  }
    +
    +  comparator.Sort(&match_array);
    +
    +  WriteHTMLTotalAndSubtotals(match_array, comparator, output);
    +
    +  comparator.Clear();  // Delete tiebreaker_ instances.
    +
    +  output->append("
    "); +} + +// static +void ThreadData::WriteHTMLTotalAndSubtotals( + const DataCollector::Collection& match_array, + const Comparator& comparator, + std::string* output) { + if (!match_array.size()) { + output->append("There were no tracked matches."); + } else { + // Aggregate during printing + Aggregation totals; + for (size_t i = 0; i < match_array.size(); ++i) { + totals.AddDeathSnapshot(match_array[i]); + } + output->append("Aggregate Stats: "); + totals.Write(output); + output->append("

    "); + + Aggregation subtotals; + for (size_t i = 0; i < match_array.size(); ++i) { + if (0 == i || !comparator.Equivalent(match_array[i - 1], + match_array[i])) { + // Print group's defining characteristics. + comparator.WriteSortGrouping(match_array[i], output); + output->append("

    "); + } + comparator.WriteSnapshot(match_array[i], output); + output->append("
    "); + subtotals.AddDeathSnapshot(match_array[i]); + if (i + 1 >= match_array.size() || + !comparator.Equivalent(match_array[i], + match_array[i + 1])) { + // Print aggregate stats for the group. + output->append("
    "); + subtotals.Write(output); + output->append("


    "); + subtotals.Clear(); + } + } + } +} + +Births* ThreadData::FindLifetime(const Location& location) { + if (!message_loop_) // In case message loop wasn't yet around... + message_loop_ = MessageLoop::current(); // Find it now. + + BirthMap::iterator it = birth_map_.find(location); + if (it != birth_map_.end()) + return it->second; + Births* tracker = new Births(location); + + // Lock since the map may get relocated now, and other threads sometimes + // snapshot it (but they lock before copying it). + AutoLock lock(lock_); + birth_map_[location] = tracker; + return tracker; +} + +void ThreadData::TallyADeath(const Births& lifetimes, + const TimeDelta& duration) { + if (!message_loop_) // In case message loop wasn't yet around... + message_loop_ = MessageLoop::current(); // Find it now. + + DeathMap::iterator it = death_map_.find(&lifetimes); + if (it != death_map_.end()) { + it->second.RecordDeath(duration); + return; + } + + AutoLock lock(lock_); // Lock since the map may get relocated now. + death_map_[&lifetimes].RecordDeath(duration); +} + +// static +ThreadData* ThreadData::first() { + AutoLock lock(list_lock_); + return first_; +} + +const std::string ThreadData::ThreadName() const { + if (message_loop_) + return message_loop_->thread_name(); + return "ThreadWithoutMessageLoop"; +} + +// This may be called from another thread. +void ThreadData::SnapshotBirthMap(BirthMap *output) const { + AutoLock lock(*const_cast(&lock_)); + for (BirthMap::const_iterator it = birth_map_.begin(); + it != birth_map_.end(); ++it) + (*output)[it->first] = it->second; +} + +// This may be called from another thread. +void ThreadData::SnapshotDeathMap(DeathMap *output) const { + AutoLock lock(*const_cast(&lock_)); + for (DeathMap::const_iterator it = death_map_.begin(); + it != death_map_.end(); ++it) + (*output)[it->first] = it->second; +} + +#ifdef OS_WIN +void ThreadData::RunOnAllThreads(void (*function)()) { + ThreadData* list = first(); // Get existing list. + + std::vector message_loops; + for (ThreadData* it = list; it; it = it->next()) { + if (current() != it && it->message_loop()) + message_loops.push_back(it->message_loop()); + } + + ThreadSafeDownCounter* counter = + new ThreadSafeDownCounter(message_loops.size() + 1); // Extra one for us! + + HANDLE completion_handle = CreateEvent(NULL, false, false, NULL); + // Tell all other threads to run. + for (size_t i = 0; i < message_loops.size(); ++i) + message_loops[i]->PostTask(FROM_HERE, + new RunTheStatic(function, completion_handle, counter)); + + // Also run Task on our thread. + RunTheStatic local_task(function, completion_handle, counter); + local_task.Run(); + + WaitForSingleObject(completion_handle, INFINITE); + int ret_val = CloseHandle(completion_handle); + DCHECK(ret_val); +} +#endif + +// static +bool ThreadData::StartTracking(bool status) { +#ifndef TRACK_ALL_TASK_OBJECTS + return false; // Not compiled in. +#endif + + if (!status) { + AutoLock lock(list_lock_); + DCHECK(status_ == ACTIVE || status_ == SHUTDOWN); + status_ = SHUTDOWN; + return true; + } + AutoLock lock(list_lock_); + DCHECK(status_ == UNINITIALIZED); + CHECK(tls_index_.Initialize(NULL)); + status_ = ACTIVE; + return true; +} + +// static +bool ThreadData::IsActive() { + return status_ == ACTIVE; +} + +#ifdef OS_WIN +// static +void ThreadData::ShutdownMultiThreadTracking() { + // Using lock, guarantee that no new ThreadData instances will be created. + if (!StartTracking(false)) + return; + + RunOnAllThreads(ShutdownDisablingFurtherTracking); + + // Now the *only* threads that might change the database are the threads with + // no messages loops. They might still be adding data to their birth records, + // but since no objects are deleted on those threads, there will be no further + // access to to cross-thread data. + // We could do a cleanup on all threads except for the ones without + // MessageLoops, but we won't bother doing cleanup (destruction of data) yet. + return; +} +#endif + +// static +void ThreadData::ShutdownSingleThreadedCleanup() { + // We must be single threaded... but be careful anyway. + if (!StartTracking(false)) + return; + ThreadData* thread_data_list; + { + AutoLock lock(list_lock_); + thread_data_list = first_; + first_ = NULL; + } + + while (thread_data_list) { + ThreadData* next_thread_data = thread_data_list; + thread_data_list = thread_data_list->next(); + + for (BirthMap::iterator it = next_thread_data->birth_map_.begin(); + next_thread_data->birth_map_.end() != it; ++it) + delete it->second; // Delete the Birth Records. + next_thread_data->birth_map_.clear(); + next_thread_data->death_map_.clear(); + delete next_thread_data; // Includes all Death Records. + } + + CHECK(tls_index_.initialized()); + tls_index_.Free(); + DCHECK(!tls_index_.initialized()); + status_ = UNINITIALIZED; +} + +// static +void ThreadData::ShutdownDisablingFurtherTracking() { + // Redundantly set status SHUTDOWN on this thread. + if (!StartTracking(false)) + return; +} + + +//------------------------------------------------------------------------------ + +ThreadData::ThreadSafeDownCounter::ThreadSafeDownCounter(size_t count) + : remaining_count_(count) { + DCHECK(remaining_count_ > 0); +} + +bool ThreadData::ThreadSafeDownCounter::LastCaller() { + { + AutoLock lock(lock_); + if (--remaining_count_) + return false; + } // Release lock, so we can delete everything in this instance. + delete this; + return true; +} + +//------------------------------------------------------------------------------ +#ifdef OS_WIN +ThreadData::RunTheStatic::RunTheStatic(FunctionPointer function, + HANDLE completion_handle, + ThreadSafeDownCounter* counter) + : function_(function), + completion_handle_(completion_handle), + counter_(counter) { +} + +void ThreadData::RunTheStatic::Run() { + function_(); + if (counter_->LastCaller()) + SetEvent(completion_handle_); +} +#endif + +//------------------------------------------------------------------------------ +// Individual 3-tuple of birth (place and thread) along with death thread, and +// the accumulated stats for instances (DeathData). + +Snapshot::Snapshot(const BirthOnThread& birth_on_thread, + const ThreadData& death_thread, + const DeathData& death_data) + : birth_(&birth_on_thread), + death_thread_(&death_thread), + death_data_(death_data) { +} + +Snapshot::Snapshot(const BirthOnThread& birth_on_thread, int count) + : birth_(&birth_on_thread), + death_thread_(NULL), + death_data_(DeathData(count)) { +} + +const std::string Snapshot::DeathThreadName() const { + if (death_thread_) + return death_thread_->ThreadName(); + return "Still_Alive"; +} + +void Snapshot::Write(std::string* output) const { + death_data_.Write(output); + StringAppendF(output, "%s->%s ", + birth_->birth_thread()->ThreadName().c_str(), + death_thread_->ThreadName().c_str()); + birth_->location().Write(true, true, output); +} + +void Snapshot::Add(const Snapshot& other) { + death_data_.AddDeathData(other.death_data_); +} + +//------------------------------------------------------------------------------ +// DataCollector + +DataCollector::DataCollector() { + DCHECK(ThreadData::IsActive()); + + ThreadData* my_list = ThreadData::current()->first(); + + count_of_contributing_threads_ = 0; + for (ThreadData* thread_data = my_list; + thread_data; + thread_data = thread_data->next()) { + ++count_of_contributing_threads_; + } + + // Gather data serially. A different constructor could be used to do in + // parallel, and then invoke an OnCompletion task. + for (ThreadData* thread_data = my_list; + thread_data; + thread_data = thread_data->next()) { + Append(*thread_data); + } +} + +void DataCollector::Append(const ThreadData& thread_data) { + // Get copy of data (which is done under ThreadData's lock). + ThreadData::BirthMap birth_map; + thread_data.SnapshotBirthMap(&birth_map); + ThreadData::DeathMap death_map; + thread_data.SnapshotDeathMap(&death_map); + + // Use our lock to protect our accumulation activity. + AutoLock lock(accumulation_lock_); + + DCHECK(count_of_contributing_threads_); + + for (ThreadData::DeathMap::const_iterator it = death_map.begin(); + it != death_map.end(); ++it) { + collection_.push_back(Snapshot(*it->first, thread_data, it->second)); + global_birth_count_[it->first] -= it->first->birth_count(); + } + + for (ThreadData::BirthMap::const_iterator it = birth_map.begin(); + it != birth_map.end(); ++it) { + global_birth_count_[it->second] += it->second->birth_count(); + } + + --count_of_contributing_threads_; +} + +DataCollector::Collection* DataCollector::collection() { + DCHECK(!count_of_contributing_threads_); + return &collection_; +} + +void DataCollector::AddListOfLivingObjects() { + DCHECK(!count_of_contributing_threads_); + for (BirthCount::iterator it = global_birth_count_.begin(); + it != global_birth_count_.end(); ++it) { + if (it->second > 0) + collection_.push_back(Snapshot(*it->first, it->second)); + } +} + +//------------------------------------------------------------------------------ +// Aggregation + +void Aggregation::AddDeathSnapshot(const Snapshot& snapshot) { + AddBirth(snapshot.birth()); + death_threads_[snapshot.death_thread()]++; + AddDeathData(snapshot.death_data()); +} + +void Aggregation::AddBirths(const Births& births) { + AddBirth(births); + birth_count_ += births.birth_count(); +} +void Aggregation::AddBirth(const BirthOnThread& birth) { + AddBirthPlace(birth.location()); + birth_threads_[birth.birth_thread()]++; +} + +void Aggregation::AddBirthPlace(const Location& location) { + locations_[location]++; + birth_files_[location.file_name()]++; +} + +void Aggregation::Write(std::string* output) const { + if (locations_.size() == 1) { + locations_.begin()->first.Write(true, true, output); + } else { + StringAppendF(output, "%d Locations. ", locations_.size()); + if (birth_files_.size() > 1) + StringAppendF(output, "%d Files. ", birth_files_.size()); + else + StringAppendF(output, "All born in %s. ", + birth_files_.begin()->first.c_str()); + } + + if (birth_threads_.size() > 1) + StringAppendF(output, "%d BirthingThreads. ", birth_threads_.size()); + else + StringAppendF(output, "All born on %s. ", + birth_threads_.begin()->first->ThreadName().c_str()); + + if (death_threads_.size() > 1) { + StringAppendF(output, "%d DeathThreads. ", death_threads_.size()); + } else { + if (death_threads_.begin()->first) + StringAppendF(output, "All deleted on %s. ", + death_threads_.begin()->first->ThreadName().c_str()); + else + output->append("All these objects are still alive."); + } + + if (birth_count_ > 1) + StringAppendF(output, "Births=%d ", birth_count_); + + DeathData::Write(output); +} + +void Aggregation::Clear() { + birth_count_ = 0; + birth_files_.clear(); + locations_.clear(); + birth_threads_.clear(); + DeathData::Clear(); + death_threads_.clear(); +} + +//------------------------------------------------------------------------------ +// Comparison object for sorting. + +Comparator::Comparator() + : selector_(NIL), + tiebreaker_(NULL), + combined_selectors_(0), + use_tiebreaker_for_sort_only_(false) {} + +void Comparator::Clear() { + if (tiebreaker_) { + tiebreaker_->Clear(); + delete tiebreaker_; + tiebreaker_ = NULL; + } + use_tiebreaker_for_sort_only_ = false; + selector_ = NIL; +} + +void Comparator::Sort(DataCollector::Collection* collection) const { + std::sort(collection->begin(), collection->end(), *this); +} + + +bool Comparator::operator()(const Snapshot& left, + const Snapshot& right) const { + switch (selector_) { + case BIRTH_THREAD: + if (left.birth_thread() != right.birth_thread() && + left.birth_thread()->ThreadName() != + right.birth_thread()->ThreadName()) + return left.birth_thread()->ThreadName() < + right.birth_thread()->ThreadName(); + break; + + case DEATH_THREAD: + if (left.death_thread() != right.death_thread() && + left.DeathThreadName() != + right.DeathThreadName()) { + if (!left.death_thread()) + return true; + if (!right.death_thread()) + return false; + return left.DeathThreadName() < + right.DeathThreadName(); + } + break; + + case BIRTH_FILE: + if (left.location().file_name() != right.location().file_name()) { + int comp = strcmp(left.location().file_name(), + right.location().file_name()); + if (comp) + return 0 > comp; + } + break; + + case BIRTH_FUNCTION: + if (left.location().function_name() != right.location().function_name()) { + int comp = strcmp(left.location().function_name(), + right.location().function_name()); + if (comp) + return 0 > comp; + } + break; + + case BIRTH_LINE: + if (left.location().line_number() != right.location().line_number()) + return left.location().line_number() < + right.location().line_number(); + break; + + case COUNT: + if (left.count() != right.count()) + return left.count() > right.count(); // Sort large at front of vector. + break; + + case AVERAGE_DURATION: + if (left.AverageMsDuration() != right.AverageMsDuration()) + return left.AverageMsDuration() > right.AverageMsDuration(); + break; + + default: + break; + } + if (tiebreaker_) + return tiebreaker_->operator()(left, right); + return false; +} + +bool Comparator::Equivalent(const Snapshot& left, + const Snapshot& right) const { + switch (selector_) { + case BIRTH_THREAD: + if (left.birth_thread() != right.birth_thread() && + left.birth_thread()->ThreadName() != + right.birth_thread()->ThreadName()) + return false; + break; + + case DEATH_THREAD: + if (left.death_thread() != right.death_thread() && + left.DeathThreadName() != right.DeathThreadName()) + return false; + break; + + case BIRTH_FILE: + if (left.location().file_name() != right.location().file_name()) { + int comp = strcmp(left.location().file_name(), + right.location().file_name()); + if (comp) + return false; + } + break; + + case BIRTH_FUNCTION: + if (left.location().function_name() != right.location().function_name()) { + int comp = strcmp(left.location().function_name(), + right.location().function_name()); + if (comp) + return false; + } + break; + + case COUNT: + if (left.count() != right.count()) + return false; + break; + + case AVERAGE_DURATION: + if (left.life_duration() != right.life_duration()) + return false; + break; + + default: + break; + } + if (tiebreaker_ && !use_tiebreaker_for_sort_only_) + return tiebreaker_->Equivalent(left, right); + return true; +} + +bool Comparator::Acceptable(const Snapshot& sample) const { + if (required_.size()) { + switch (selector_) { + case BIRTH_THREAD: + if (sample.birth_thread()->ThreadName().find(required_) == + std::string::npos) + return false; + break; + + case DEATH_THREAD: + if (sample.DeathThreadName().find(required_) == std::string::npos) + return false; + break; + + case BIRTH_FILE: + if (!strstr(sample.location().file_name(), required_.c_str())) + return false; + break; + + case BIRTH_FUNCTION: + if (!strstr(sample.location().function_name(), required_.c_str())) + return false; + break; + + default: + break; + } + } + if (tiebreaker_ && !use_tiebreaker_for_sort_only_) + return tiebreaker_->Acceptable(sample); + return true; +} + +void Comparator::SetTiebreaker(Selector selector, const std::string required) { + if (selector == selector_ || NIL == selector) + return; + combined_selectors_ |= selector; + if (NIL == selector_) { + selector_ = selector; + if (required.size()) + required_ = required; + return; + } + if (tiebreaker_) { + if (use_tiebreaker_for_sort_only_) { + Comparator* temp = new Comparator; + temp->tiebreaker_ = tiebreaker_; + tiebreaker_ = temp; + } + } else { + tiebreaker_ = new Comparator; + DCHECK(!use_tiebreaker_for_sort_only_); + } + tiebreaker_->SetTiebreaker(selector, required); +} + +bool Comparator::IsGroupedBy(Selector selector) const { + return 0 != (selector & combined_selectors_); +} + +void Comparator::SetSubgroupTiebreaker(Selector selector) { + if (selector == selector_ || NIL == selector) + return; + if (!tiebreaker_) { + use_tiebreaker_for_sort_only_ = true; + tiebreaker_ = new Comparator; + tiebreaker_->SetTiebreaker(selector, ""); + } else { + tiebreaker_->SetSubgroupTiebreaker(selector); + } +} + +void Comparator::ParseKeyphrase(const std::string key_phrase) { + static std::map key_map; + static bool initialized = false; + if (!initialized) { + initialized = true; + key_map["count"] = COUNT; + key_map["duration"] = AVERAGE_DURATION; + key_map["birth"] = BIRTH_THREAD; + key_map["death"] = DEATH_THREAD; + key_map["file"] = BIRTH_FILE; + key_map["function"] = BIRTH_FUNCTION; + key_map["line"] = BIRTH_LINE; + } + + std::string required; + size_t equal_offset = key_phrase.find('=', 0); + if (key_phrase.npos != equal_offset) + required = key_phrase.substr(equal_offset + 1, key_phrase.npos); + std::string keyword(key_phrase.substr(0, equal_offset)); + keyword = StringToLowerASCII(keyword); + if (key_map.end() == key_map.find(keyword)) + return; + SetTiebreaker(key_map[keyword], required); +} + +bool Comparator::ParseQuery(const std::string query) { + for (size_t i = 0; i < query.size();) { + size_t slash_offset = query.find('/', i); + ParseKeyphrase(query.substr(i, slash_offset - i)); + if (query.npos == slash_offset) + break; + i = slash_offset + 1; + } + + // Select subgroup ordering (if we want to display the subgroup) + SetSubgroupTiebreaker(COUNT); + SetSubgroupTiebreaker(AVERAGE_DURATION); + SetSubgroupTiebreaker(BIRTH_THREAD); + SetSubgroupTiebreaker(DEATH_THREAD); + SetSubgroupTiebreaker(BIRTH_FUNCTION); + SetSubgroupTiebreaker(BIRTH_FILE); + SetSubgroupTiebreaker(BIRTH_LINE); + + return true; +} + +bool Comparator::WriteSortGrouping(const Snapshot& sample, + std::string* output) const { + bool wrote_data = false; + switch (selector_) { + case BIRTH_THREAD: + StringAppendF(output, "All new on %s ", + sample.birth_thread()->ThreadName().c_str()); + wrote_data = true; + break; + + case DEATH_THREAD: + if (sample.death_thread()) + StringAppendF(output, "All deleted on %s ", + sample.DeathThreadName().c_str()); + else + output->append("All still alive "); + wrote_data = true; + break; + + case BIRTH_FILE: + StringAppendF(output, "All born in %s ", + sample.location().file_name()); + break; + + case BIRTH_FUNCTION: + output->append("All born in "); + sample.location().WriteFunctionName(output); + output->push_back(' '); + break; + + default: + break; + } + if (tiebreaker_ && !use_tiebreaker_for_sort_only_) { + wrote_data |= tiebreaker_->WriteSortGrouping(sample, output); + } + return wrote_data; +} + +void Comparator::WriteSnapshot(const Snapshot& sample, + std::string* output) const { + sample.death_data().Write(output); + if (!(combined_selectors_ & BIRTH_THREAD) || + !(combined_selectors_ & DEATH_THREAD)) + StringAppendF(output, "%s->%s ", + (combined_selectors_ & BIRTH_THREAD) ? "*" : + sample.birth().birth_thread()->ThreadName().c_str(), + (combined_selectors_ & DEATH_THREAD) ? "*" : + sample.DeathThreadName().c_str()); + sample.birth().location().Write(!(combined_selectors_ & BIRTH_FILE), + !(combined_selectors_ & BIRTH_FUNCTION), + output); +} + +} // namespace tracked_objects diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/tracked_objects.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/tracked_objects.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/tracked_objects.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/tracked_objects.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,514 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_TRACKED_OBJECTS_H_ +#define BASE_TRACKED_OBJECTS_H_ + +//------------------------------------------------------------------------------ +#include +#include +#include + +#include "base/lock.h" +#include "base/message_loop.h" +#include "base/thread_local_storage.h" +#include "base/tracked.h" + + +namespace tracked_objects { + +//------------------------------------------------------------------------------ +// For a specific thread, and a specific birth place, the collection of all +// death info (with tallies for each death thread, to prevent access conflicts). +class ThreadData; +class BirthOnThread { + public: + explicit BirthOnThread(const Location& location); + + const Location location() const { return location_; } + const ThreadData* birth_thread() const { return birth_thread_; } + + private: + // File/lineno of birth. This defines the essence of the type, as the context + // of the birth (construction) often tell what the item is for. This field + // is const, and hence safe to access from any thread. + const Location location_; + + // The thread that records births into this object. Only this thread is + // allowed to access birth_count_ (which changes over time). + const ThreadData* birth_thread_; // The thread this birth took place on. + + DISALLOW_COPY_AND_ASSIGN(BirthOnThread); +}; + +//------------------------------------------------------------------------------ +// A class for accumulating counts of births (without bothering with a map<>). + +class Births: public BirthOnThread { + public: + explicit Births(const Location& location); + + int birth_count() const { return birth_count_; } + + // When we have a birth we update the count for this BirhPLace. + void RecordBirth() { ++birth_count_; } + + // When a birthplace is changed (updated), we need to decrement the counter + // for the old instance. + void ForgetBirth() { --birth_count_; } // We corrected a birth place. + + private: + // The number of births on this thread for our location_. + int birth_count_; + + DISALLOW_COPY_AND_ASSIGN(Births); +}; + +//------------------------------------------------------------------------------ +// Basic info summarizing multiple destructions of an object with a single +// birthplace (fixed Location). Used both on specific threads, and also used +// in snapshots when integrating assembled data. + +class DeathData { + public: + // Default initializer. + DeathData() : count_(0), square_duration_(0) {} + + // When deaths have not yet taken place, and we gather data from all the + // threads, we create DeathData stats that tally the number of births without + // a corrosponding death. + explicit DeathData(int count) : count_(count), square_duration_(0) {} + + void RecordDeath(const base::TimeDelta& duration); + + // Metrics accessors. + int count() const { return count_; } + base::TimeDelta life_duration() const { return life_duration_; } + int64 square_duration() const { return square_duration_; } + int AverageMsDuration() const; + double StandardDeviation() const; + + // Accumulate metrics from other into this. + void AddDeathData(const DeathData& other); + + // Simple print of internal state. + void Write(std::string* output) const; + + void Clear(); + + private: + int count_; // Number of destructions. + base::TimeDelta life_duration_; // Sum of all lifetime durations. + int64 square_duration_; // Sum of squares in milliseconds. +}; + +//------------------------------------------------------------------------------ +// A temporary collection of data that can be sorted and summarized. It is +// gathered (carefully) from many threads. Instances are held in arrays and +// processed, filtered, and rendered. +// The source of this data was collected on many threads, and is asynchronously +// changing. The data in this instance is not asynchronously changing. + +class Snapshot { + public: + // When snapshotting a full life cycle set (birth-to-death), use this: + Snapshot(const BirthOnThread& birth_on_thread, const ThreadData& death_thread, + const DeathData& death_data); + + // When snapshotting a birth, with no death yet, use this: + Snapshot(const BirthOnThread& birth_on_thread, int count); + + + const ThreadData* birth_thread() const { return birth_->birth_thread(); } + const Location location() const { return birth_->location(); } + const BirthOnThread& birth() const { return *birth_; } + const ThreadData* death_thread() const {return death_thread_; } + const DeathData& death_data() const { return death_data_; } + const std::string DeathThreadName() const; + + int count() const { return death_data_.count(); } + base::TimeDelta life_duration() const { return death_data_.life_duration(); } + int64 square_duration() const { return death_data_.square_duration(); } + int AverageMsDuration() const { return death_data_.AverageMsDuration(); } + + void Write(std::string* output) const; + + void Add(const Snapshot& other); + + private: + const BirthOnThread* birth_; // Includes Location and birth_thread. + const ThreadData* death_thread_; + DeathData death_data_; +}; +//------------------------------------------------------------------------------ +// DataCollector is a container class for Snapshot and BirthOnThread count +// items. It protects the gathering under locks, so that it could be called via +// Posttask on any threads, such as all the target threads in parallel. + +class DataCollector { + public: + typedef std::vector Collection; + + // Construct with a list of how many threads should contribute. This helps us + // determine (in the async case) when we are done with all contributions. + DataCollector(); + + // Add all stats from the indicated thread into our arrays. This function is + // mutex protected, and *could* be called from any threads (although current + // implementation serialized calls to Append). + void Append(const ThreadData& thread_data); + + // After the accumulation phase, the following access is to process data. + Collection* collection(); + + // After collection of death data is complete, we can add entries for all the + // remaining living objects. + void AddListOfLivingObjects(); + + private: + // This instance may be provided to several threads to contribute data. The + // following counter tracks how many more threads will contribute. When it is + // zero, then all asynchronous contributions are complete, and locked access + // is no longer needed. + int count_of_contributing_threads_; + + // The array that we collect data into. + Collection collection_; + + // The total number of births recorded at each location for which we have not + // seen a death count. + typedef std::map BirthCount; + BirthCount global_birth_count_; + + Lock accumulation_lock_; // Protects access during accumulation phase. + + DISALLOW_COPY_AND_ASSIGN(DataCollector); +}; + +//------------------------------------------------------------------------------ +// Aggregation contains summaries (totals and subtotals) of groups of Snapshot +// instances to provide printing of these collections on a single line. + +class Aggregation: public DeathData { + public: + Aggregation() : birth_count_(0) {} + + void AddDeathSnapshot(const Snapshot& snapshot); + void AddBirths(const Births& births); + void AddBirth(const BirthOnThread& birth); + void AddBirthPlace(const Location& location); + void Write(std::string* output) const; + void Clear(); + + private: + int birth_count_; + std::map birth_files_; + std::map locations_; + std::map birth_threads_; + DeathData death_data_; + std::map death_threads_; + + DISALLOW_COPY_AND_ASSIGN(Aggregation); +}; + +//------------------------------------------------------------------------------ +// Comparator does the comparison of Snapshot instances. It is +// used to order the instances in a vector. It orders them into groups (for +// aggregation), and can also order instances within the groups (for detailed +// rendering of the instances). + +class Comparator { + public: + enum Selector { + NIL = 0, + BIRTH_THREAD = 1, + DEATH_THREAD = 2, + BIRTH_FILE = 4, + BIRTH_FUNCTION = 8, + BIRTH_LINE = 16, + COUNT = 32, + AVERAGE_DURATION = 64, + TOTAL_DURATION = 128, + }; + + explicit Comparator(); + + // Reset the comparator to a NIL selector. Reset() and recursively delete any + // tiebreaker_ entries. NOTE: We can't use a standard destructor, because + // the sort algorithm makes copies of this object, and then deletes them, + // which would cause problems (either we'd make expensive deep copies, or we'd + // do more thna one delete on a tiebreaker_. + void Clear(); + + // The less() operator for sorting the array via std::sort(). + bool operator()(const Snapshot& left, const Snapshot& right) const; + + void Sort(DataCollector::Collection* collection) const; + + // Check to see if the items are sort equivalents (should be aggregated). + bool Equivalent(const Snapshot& left, const Snapshot& right) const; + + // Check to see if all required fields are present in the given sample. + bool Acceptable(const Snapshot& sample) const; + + // A comparator can be refined by specifying what to do if the selected basis + // for comparison is insufficient to establish an ordering. This call adds + // the indicated attribute as the new "least significant" basis of comparison. + void SetTiebreaker(Selector selector, const std::string required); + + // Indicate if this instance is set up to sort by the given Selector, thereby + // putting that information in the SortGrouping, so it is not needed in each + // printed line. + bool IsGroupedBy(Selector selector) const; + + // Using the tiebreakers as set above, we mostly get an ordering, which + // equivalent groups. If those groups are displayed (rather than just being + // aggregated, then the following is used to order them (within the group). + void SetSubgroupTiebreaker(Selector selector); + + // Translate a keyword and restriction in URL path to a selector for sorting. + void ParseKeyphrase(const std::string key_phrase); + + // Parse a query in an about:objects URL to decide on sort ordering. + bool ParseQuery(const std::string query); + + // Output a header line that can be used to indicated what items will be + // collected in the group. It lists all (potentially) tested attributes and + // their values (in the sample item). + bool WriteSortGrouping(const Snapshot& sample, std::string* output) const; + + // Output a sample, with SortGroup details not displayed. + void WriteSnapshot(const Snapshot& sample, std::string* output) const; + + private: + // The selector directs this instance to compare based on the specified + // members of the tested elements. + enum Selector selector_; + + // For filtering into acceptable and unacceptable snapshot instance, the + // following is required to be a substring of the selector_ field. + std::string required_; + + // If this instance can't decide on an ordering, we can consult a tie-breaker + // which may have a different basis of comparison. + Comparator* tiebreaker_; + + // We or together all the selectors we sort on (not counting sub-group + // selectors), so that we can tell if we've decided to group on any given + // criteria. + int combined_selectors_; + + // Some tiebreakrs are for subgroup ordering, and not for basic ordering (in + // preparation for aggregation). The subgroup tiebreakers are not consulted + // when deciding if two items are in equivalent groups. This flag tells us + // to ignore the tiebreaker when doing Equivalent() testing. + bool use_tiebreaker_for_sort_only_; +}; + + +//------------------------------------------------------------------------------ +// For each thread, we have a ThreadData that stores all tracking info generated +// on this thread. This prevents the need for locking as data accumulates. + +class ThreadData { + public: + typedef std::map BirthMap; + typedef std::map DeathMap; + + ThreadData(); + + // Using Thread Local Store, find the current instance for collecting data. + // If an instance does not exist, construct one (and remember it for use on + // this thread. + // If shutdown has already started, and we don't yet have an instance, then + // return null. + static ThreadData* current(); + + // For a given about:objects URL, develop resulting HTML, and append to + // output. + static void WriteHTML(const std::string& query, std::string* output); + + // For a given accumulated array of results, use the comparator to sort and + // subtotal, writing the results to the output. + static void WriteHTMLTotalAndSubtotals( + const DataCollector::Collection& match_array, + const Comparator& comparator, std::string* output); + + // In this thread's data, find a place to record a new birth. + Births* FindLifetime(const Location& location); + + // Find a place to record a death on this thread. + void TallyADeath(const Births& lifetimes, const base::TimeDelta& duration); + + // (Thread safe) Get start of list of instances. + static ThreadData* first(); + // Iterate through the null terminated list of instances. + ThreadData* next() const { return next_; } + + MessageLoop* message_loop() const { return message_loop_; } + const std::string ThreadName() const; + + // Using our lock, make a copy of the specified maps. These calls may arrive + // from non-local threads. + void SnapshotBirthMap(BirthMap *output) const; + void SnapshotDeathMap(DeathMap *output) const; + + static void RunOnAllThreads(void (*Func)()); + + // Set internal status_ to either become ACTIVE, or later, to be SHUTDOWN, + // based on argument being true or false respectively. + // IF tracking is not compiled in, this function will return false. + static bool StartTracking(bool status); + static bool IsActive(); + +#ifdef OS_WIN + // WARNING: ONLY call this function when all MessageLoops are still intact for + // all registered threads. IF you call it later, you will crash. + // Note: You don't need to call it at all, and you can wait till you are + // single threaded (again) to do the cleanup via + // ShutdownSingleThreadedCleanup(). + // Start the teardown (shutdown) process in a multi-thread mode by disabling + // further additions to thread database on all threads. First it makes a + // local (locked) change to prevent any more threads from registering. Then + // it Posts a Task to all registered threads to be sure they are aware that no + // more accumulation can take place. + static void ShutdownMultiThreadTracking(); +#endif + + // WARNING: ONLY call this function when you are running single threaded + // (again) and all message loops and threads have terminated. Until that + // point some threads may still attempt to write into our data structures. + // Delete recursively all data structures, starting with the list of + // ThreadData instances. + static void ShutdownSingleThreadedCleanup(); + + private: + // Current allowable states of the tracking system. The states always + // proceed towards SHUTDOWN, and never go backwards. + enum Status { + UNINITIALIZED, + ACTIVE, + SHUTDOWN, + }; + + // A class used to count down which is accessed by several threads. This is + // used to make sure RunOnAllThreads() actually runs a task on the expected + // count of threads. + class ThreadSafeDownCounter { + public: + // Constructor sets the count, once and for all. + explicit ThreadSafeDownCounter(size_t count); + + // Decrement the count, and return true if we hit zero. Also delete this + // instance automatically when we hit zero. + bool LastCaller(); + + private: + size_t remaining_count_; + Lock lock_; // protect access to remaining_count_. + }; + +#ifdef OS_WIN + // A Task class that runs a static method supplied, and checks to see if this + // is the last tasks instance (on last thread) that will run the method. + // IF this is the last run, then the supplied event is signalled. + class RunTheStatic : public Task { + public: + typedef void (*FunctionPointer)(); + RunTheStatic(FunctionPointer function, + HANDLE completion_handle, + ThreadSafeDownCounter* counter); + // Run the supplied static method, and optionally set the event. + void Run(); + + private: + FunctionPointer function_; + HANDLE completion_handle_; + // Make sure enough tasks are called before completion is signaled. + ThreadSafeDownCounter* counter_; + + DISALLOW_COPY_AND_ASSIGN(RunTheStatic); + }; +#endif + + // Each registered thread is called to set status_ to SHUTDOWN. + // This is done redundantly on every registered thread because it is not + // protected by a mutex. Running on all threads guarantees we get the + // notification into the memory cache of all possible threads. + static void ShutdownDisablingFurtherTracking(); + + // We use thread local store to identify which ThreadData to interact with. + static TLSSlot tls_index_ ; + + // Link to the most recently created instance (starts a null terminated list). + static ThreadData* first_; + // Protection for access to first_. + static Lock list_lock_; + + + // We set status_ to SHUTDOWN when we shut down the tracking service. This + // setting is redundantly established by all participating + // threads so that we are *guaranteed* (without locking) that all threads + // can "see" the status and avoid additional calls into the service. + static Status status_; + + // Link to next instance (null terminated list). Used to globally track all + // registered instances (corresponds to all registered threads where we keep + // data). + ThreadData* next_; + + // The message loop where tasks needing to access this instance's private data + // should be directed. Since some threads have no message loop, some + // instances have data that can't be (safely) modified externally. + MessageLoop* message_loop_; + + // A map used on each thread to keep track of Births on this thread. + // This map should only be accessed on the thread it was constructed on. + // When a snapshot is needed, this structure can be locked in place for the + // duration of the snapshotting activity. + BirthMap birth_map_; + + // Similar to birth_map_, this records informations about death of tracked + // instances (i.e., when a tracked instance was destroyed on this thread). + DeathMap death_map_; + + // Lock to protect *some* access to BirthMap and DeathMap. We only use + // locking protection when we are growing the maps, or using an iterator. We + // only do writes to members from this thread, so the updates of values are + // atomic. Folks can read from other threads, and get (via races) new or old + // data, but that is considered acceptable errors (mis-information). + Lock lock_; + + DISALLOW_COPY_AND_ASSIGN(ThreadData); +}; + + +//------------------------------------------------------------------------------ +// Provide simple way to to start global tracking, and to tear down tracking +// when done. Note that construction and destruction of this object must be +// done when running in single threaded mode (before spawning a lot of threads +// for construction, and after shutting down all the threads for destruction). + +class AutoTracking { + public: + AutoTracking() { ThreadData::StartTracking(true); } + + ~AutoTracking() { +#ifndef NDEBUG // Don't call these in a Release build: they just waste time. + // The following should ONLY be called when in single threaded mode. It is + // unsafe to do this cleanup if other threads are still active. + // It is also very unnecessary, so I'm only doing this in debug to satisfy + // purify (if we need to!). + ThreadData::ShutdownSingleThreadedCleanup(); +#endif + } + + private: + DISALLOW_COPY_AND_ASSIGN(AutoTracking); +}; + + +} // namespace tracked_objects + +#endif // BASE_TRACKED_OBJECTS_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/tracked_objects_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/tracked_objects_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/tracked_objects_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/tracked_objects_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,94 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Test of classes in the tracked_objects.h classes. + +#include "base/tracked_objects.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace tracked_objects { + +class TrackedObjectsTest : public testing::Test { + public: + MessageLoop message_loop_; +}; + +TEST_F(TrackedObjectsTest, MinimalStartupShutdown) { + // Minimal test doesn't even create any tasks. + if (!ThreadData::StartTracking(true)) + return; + + EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. + ThreadData* data = ThreadData::current(); + EXPECT_TRUE(ThreadData::first()); // Now class was constructed. + EXPECT_TRUE(data); + EXPECT_TRUE(!data->next()); + EXPECT_EQ(data, ThreadData::current()); + ThreadData::BirthMap birth_map; + data->SnapshotBirthMap(&birth_map); + EXPECT_EQ(0u, birth_map.size()); + ThreadData::DeathMap death_map; + data->SnapshotDeathMap(&death_map); + EXPECT_EQ(0u, death_map.size()); + ThreadData::ShutdownSingleThreadedCleanup(); + + // Do it again, just to be sure we reset state completely. + ThreadData::StartTracking(true); + EXPECT_FALSE(ThreadData::first()); // No activity even on this thread. + data = ThreadData::current(); + EXPECT_TRUE(ThreadData::first()); // Now class was constructed. + EXPECT_TRUE(data); + EXPECT_TRUE(!data->next()); + EXPECT_EQ(data, ThreadData::current()); + birth_map.clear(); + data->SnapshotBirthMap(&birth_map); + EXPECT_EQ(0u, birth_map.size()); + death_map.clear(); + data->SnapshotDeathMap(&death_map); + EXPECT_EQ(0u, death_map.size()); + ThreadData::ShutdownSingleThreadedCleanup(); +} + +class NoopTracked : public tracked_objects::Tracked { +}; + +TEST_F(TrackedObjectsTest, TinyStartupShutdown) { + if (!ThreadData::StartTracking(true)) + return; + + // Instigate tracking on a single tracked object, or our thread. + NoopTracked tracked; + + const ThreadData* data = ThreadData::first(); + EXPECT_TRUE(data); + EXPECT_TRUE(!data->next()); + EXPECT_EQ(data, ThreadData::current()); + ThreadData::BirthMap birth_map; + data->SnapshotBirthMap(&birth_map); + EXPECT_EQ(1u, birth_map.size()); // 1 birth location. + EXPECT_EQ(1, birth_map.begin()->second->birth_count()); // 1 birth. + ThreadData::DeathMap death_map; + data->SnapshotDeathMap(&death_map); + EXPECT_EQ(0u, death_map.size()); // No deaths. + + + // Now instigate a birth, and a death. + delete new NoopTracked; + + birth_map.clear(); + data->SnapshotBirthMap(&birth_map); + EXPECT_EQ(1u, birth_map.size()); // 1 birth location. + EXPECT_EQ(2, birth_map.begin()->second->birth_count()); // 2 births. + death_map.clear(); + data->SnapshotDeathMap(&death_map); + EXPECT_EQ(1u, death_map.size()); // 1 location. + EXPECT_EQ(1, death_map.begin()->second.count()); // 1 death. + + // The births were at the same location as the one known death. + EXPECT_EQ(birth_map.begin()->second, death_map.begin()->first); + + ThreadData::ShutdownSingleThreadedCleanup(); +} + +} // namespace tracked_objects diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/tuple.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/tuple.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/tuple.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/tuple.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,878 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A Tuple is a generic templatized container, similar in concept to std::pair. +// There are classes Tuple0 to Tuple6, cooresponding to the number of elements +// it contains. The convenient MakeTuple() function takes 0 to 6 arguments, +// and will construct and return the appropriate Tuple object. The functions +// DispatchToMethod and DispatchToFunction take a function pointer or instance +// and method pointer, and unpack a tuple into arguments to the call. +// +// Tuple elements are copied by value, and stored in the tuple. See the unit +// tests for more details of how/when the values are copied. +// +// Example usage: +// // These two methods of creating a Tuple are identical. +// Tuple2 tuple_a(1, "wee"); +// Tuple2 tuple_b = MakeTuple(1, "wee"); +// +// void SomeFunc(int a, const char* b) { } +// DispatchToFunction(&SomeFunc, tuple_a); // SomeFunc(1, "wee") +// DispatchToFunction( +// &SomeFunc, MakeTuple(10, "foo")); // SomeFunc(10, "foo") +// +// struct { void SomeMeth(int a, int b, int c) { } } foo; +// DispatchToMethod(&foo, &Foo::SomeMeth, MakeTuple(1, 2, 3)); +// // foo->SomeMeth(1, 2, 3); + +#ifndef BASE_TUPLE_H__ +#define BASE_TUPLE_H__ + +// Traits ---------------------------------------------------------------------- +// +// A simple traits class for tuple arguments. +// +// ValueType: the bare, nonref version of a type (same as the type for nonrefs). +// RefType: the ref version of a type (same as the type for refs). +// ParamType: what type to pass to functions (refs should not be constified). + +template +struct TupleTraits { + typedef P ValueType; + typedef P& RefType; + typedef const P& ParamType; +}; + +template +struct TupleTraits { + typedef P ValueType; + typedef P& RefType; + typedef P& ParamType; +}; + +// Tuple ----------------------------------------------------------------------- +// +// This set of classes is useful for bundling 0 or more heterogeneous data types +// into a single variable. The advantage of this is that it greatly simplifies +// function objects that need to take an arbitrary number of parameters; see +// RunnableMethod and IPC::MessageWithTuple. +// +// Tuple0 is supplied to act as a 'void' type. It can be used, for example, +// when dispatching to a function that accepts no arguments (see the +// Dispatchers below). +// Tuple1
    is rarely useful. One such use is when A is non-const ref that you +// want filled by the dispatchee, and the tuple is merely a container for that +// output (a "tier"). See MakeRefTuple and its usages. + +struct Tuple0 { + typedef Tuple0 ValueTuple; + typedef Tuple0 RefTuple; +}; + +template +struct Tuple1 { + public: + typedef A TypeA; + typedef Tuple1::ValueType> ValueTuple; + typedef Tuple1::RefType> RefTuple; + + Tuple1() {} + explicit Tuple1(typename TupleTraits::ParamType a) : a(a) {} + + A a; +}; + +template +struct Tuple2 { + public: + typedef A TypeA; + typedef B TypeB; + typedef Tuple2::ValueType, + typename TupleTraits::ValueType> ValueTuple; + typedef Tuple2::RefType, + typename TupleTraits::RefType> RefTuple; + + Tuple2() {} + Tuple2(typename TupleTraits::ParamType a, + typename TupleTraits::ParamType b) + : a(a), b(b) { + } + + A a; + B b; +}; + +template +struct Tuple3 { + public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef Tuple3::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType> ValueTuple; + typedef Tuple3::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType> RefTuple; + + Tuple3() {} + Tuple3(typename TupleTraits::ParamType a, + typename TupleTraits::ParamType b, + typename TupleTraits::ParamType c) + : a(a), b(b), c(c){ + } + + A a; + B b; + C c; +}; + +template +struct Tuple4 { + public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + typedef Tuple4::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType> ValueTuple; + typedef Tuple4::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType> RefTuple; + + Tuple4() {} + Tuple4(typename TupleTraits::ParamType a, + typename TupleTraits::ParamType b, + typename TupleTraits::ParamType c, + typename TupleTraits::ParamType d) + : a(a), b(b), c(c), d(d) { + } + + A a; + B b; + C c; + D d; +}; + +template +struct Tuple5 { +public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + typedef E TypeE; + typedef Tuple5::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType> ValueTuple; + typedef Tuple5::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType> RefTuple; + + Tuple5() {} + Tuple5(typename TupleTraits::ParamType a, + typename TupleTraits::ParamType b, + typename TupleTraits::ParamType c, + typename TupleTraits::ParamType d, + typename TupleTraits::ParamType e) + : a(a), b(b), c(c), d(d), e(e) { + } + + A a; + B b; + C c; + D d; + E e; +}; + +template +struct Tuple6 { +public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + typedef E TypeE; + typedef F TypeF; + typedef Tuple6::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType> ValueTuple; + typedef Tuple6::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType> RefTuple; + + Tuple6() {} + Tuple6(typename TupleTraits::ParamType a, + typename TupleTraits::ParamType b, + typename TupleTraits::ParamType c, + typename TupleTraits::ParamType d, + typename TupleTraits::ParamType e, + typename TupleTraits::ParamType f) + : a(a), b(b), c(c), d(d), e(e), f(f) { + } + + A a; + B b; + C c; + D d; + E e; + F f; +}; + +template +struct Tuple7 { +public: + typedef A TypeA; + typedef B TypeB; + typedef C TypeC; + typedef D TypeD; + typedef E TypeE; + typedef F TypeF; + typedef G TypeG; + typedef Tuple7::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType, + typename TupleTraits::ValueType> ValueTuple; + typedef Tuple7::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType, + typename TupleTraits::RefType> RefTuple; + + Tuple7() {} + Tuple7(typename TupleTraits::ParamType a, + typename TupleTraits::ParamType b, + typename TupleTraits::ParamType c, + typename TupleTraits::ParamType d, + typename TupleTraits::ParamType e, + typename TupleTraits::ParamType f, + typename TupleTraits::ParamType g) + : a(a), b(b), c(c), d(d), e(e), f(f), g(g) { + } + + A a; + B b; + C c; + D d; + E e; + F f; + G g; +}; + +// Tuple creators ------------------------------------------------------------- +// +// Helper functions for constructing tuples while inferring the template +// argument types. + +inline Tuple0 MakeTuple() { + return Tuple0(); +} + +template +inline Tuple1 MakeTuple(const A& a) { + return Tuple1(a); +} + +template +inline Tuple2 MakeTuple(const A& a, const B& b) { + return Tuple2(a, b); +} + +template +inline Tuple3 MakeTuple(const A& a, const B& b, const C& c) { + return Tuple3(a, b, c); +} + +template +inline Tuple4 MakeTuple(const A& a, const B& b, const C& c, + const D& d) { + return Tuple4(a, b, c, d); +} + +template +inline Tuple5 MakeTuple(const A& a, const B& b, const C& c, + const D& d, const E& e) { + return Tuple5(a, b, c, d, e); +} + +template +inline Tuple6 MakeTuple(const A& a, const B& b, const C& c, + const D& d, const E& e, const F& f) { + return Tuple6(a, b, c, d, e, f); +} + +template +inline Tuple7 MakeTuple(const A& a, const B& b, const C& c, + const D& d, const E& e, const F& f, + const G& g) { + return Tuple7(a, b, c, d, e, f, g); +} + +// The following set of helpers make what Boost refers to as "Tiers" - a tuple +// of references. + +template +inline Tuple1 MakeRefTuple(A& a) { + return Tuple1(a); +} + +template +inline Tuple2 MakeRefTuple(A& a, B& b) { + return Tuple2(a, b); +} + +template +inline Tuple3 MakeRefTuple(A& a, B& b, C& c) { + return Tuple3(a, b, c); +} + +template +inline Tuple4 MakeRefTuple(A& a, B& b, C& c, D& d) { + return Tuple4(a, b, c, d); +} + +template +inline Tuple5 MakeRefTuple(A& a, B& b, C& c, D& d, E& e) { + return Tuple5(a, b, c, d, e); +} + +template +inline Tuple6 MakeRefTuple(A& a, B& b, C& c, D& d, E& e, + F& f) { + return Tuple6(a, b, c, d, e, f); +} + +template +inline Tuple7 MakeRefTuple(A& a, B& b, C& c, D& d, + E& e, F& f, G& g) { + return Tuple7(a, b, c, d, e, f, g); +} + +// Dispatchers ---------------------------------------------------------------- +// +// Helper functions that call the given method on an object, with the unpacked +// tuple arguments. Notice that they all have the same number of arguments, +// so you need only write: +// DispatchToMethod(object, &Object::method, args); +// This is very useful for templated dispatchers, since they don't need to know +// what type |args| is. + +// Non-Static Dispatchers with no out params. + +template +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple0& arg) { + (obj->*method)(); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, const A& arg) { + (obj->*method)(arg); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, const Tuple1& arg) { + (obj->*method)(arg.a); +} + +template +inline void DispatchToMethod(ObjT* obj, + Method method, + const Tuple2& arg) { + (obj->*method)(arg.a, arg.b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3& arg) { + (obj->*method)(arg.a, arg.b, arg.c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple7& arg) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f, arg.g); +} + +// Static Dispatchers with no out params. + +template +inline void DispatchToFunction(Function function, const Tuple0& arg) { + (*function)(); +} + +template +inline void DispatchToFunction(Function function, const A& arg) { + (*function)(arg); +} + +template +inline void DispatchToFunction(Function function, const Tuple1& arg) { + (*function)(arg.a); +} + +template +inline void DispatchToFunction(Function function, const Tuple2& arg) { + (*function)(arg.a, arg.b); +} + +template +inline void DispatchToFunction(Function function, const Tuple3& arg) { + (*function)(arg.a, arg.b, arg.c); +} + +template +inline void DispatchToFunction(Function function, + const Tuple4& arg) { + (*function)(arg.a, arg.b, arg.c, arg.d); +} + +template +inline void DispatchToFunction(Function function, + const Tuple5& arg) { + (*function)(arg.a, arg.b, arg.c, arg.d, arg.e); +} + +template +inline void DispatchToFunction(Function function, + const Tuple6& arg) { + (*function)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f); +} + +// Dispatchers with 0 out param (as a Tuple0). + +template +inline void DispatchToMethod(ObjT* obj, + Method method, + const Tuple0& arg, Tuple0*) { + (obj->*method)(); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, const A& arg, Tuple0*) { + (obj->*method)(arg); +} + +template +inline void DispatchToMethod(ObjT* obj, + Method method, + const Tuple1& arg, Tuple0*) { + (obj->*method)(arg.a); +} + +template +inline void DispatchToMethod(ObjT* obj, + Method method, + const Tuple2& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b, arg.c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6& arg, Tuple0*) { + (obj->*method)(arg.a, arg.b, arg.c, arg.d, arg.e, arg.f); +} + +// Dispatchers with 1 out param. + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple0& in, + Tuple1* out) { + (obj->*method)(&out->a); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const InA& in, + Tuple1* out) { + (obj->*method)(in, &out->a); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple1& in, + Tuple1* out) { + (obj->*method)(in.a, &out->a); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2& in, + Tuple1* out) { + (obj->*method)(in.a, in.b, &out->a); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3& in, + Tuple1* out) { + (obj->*method)(in.a, in.b, in.c, &out->a); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4& in, + Tuple1* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5& in, + Tuple1* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6& in, + Tuple1* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, &out->a); +} + +// Dispatchers with 2 out params. + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple0& in, + Tuple2* out) { + (obj->*method)(&out->a, &out->b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const InA& in, + Tuple2* out) { + (obj->*method)(in, &out->a, &out->b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple1& in, + Tuple2* out) { + (obj->*method)(in.a, &out->a, &out->b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2& in, + Tuple2* out) { + (obj->*method)(in.a, in.b, &out->a, &out->b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3& in, + Tuple2* out) { + (obj->*method)(in.a, in.b, in.c, &out->a, &out->b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4& in, + Tuple2* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5& in, + Tuple2* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a, &out->b); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6& in, + Tuple2* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, &out->a, &out->b); +} + +// Dispatchers with 3 out params. + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple0& in, + Tuple3* out) { + (obj->*method)(&out->a, &out->b, &out->c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const InA& in, + Tuple3* out) { + (obj->*method)(in, &out->a, &out->b, &out->c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple1& in, + Tuple3* out) { + (obj->*method)(in.a, &out->a, &out->b, &out->c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2& in, + Tuple3* out) { + (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3& in, + Tuple3* out) { + (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4& in, + Tuple3* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5& in, + Tuple3* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, &out->a, &out->b, &out->c); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6& in, + Tuple3* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, &out->a, &out->b, &out->c); +} + +// Dispatchers with 4 out params. + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple0& in, + Tuple4* out) { + (obj->*method)(&out->a, &out->b, &out->c, &out->d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const InA& in, + Tuple4* out) { + (obj->*method)(in, &out->a, &out->b, &out->c, &out->d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple1& in, + Tuple4* out) { + (obj->*method)(in.a, &out->a, &out->b, &out->c, &out->d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2& in, + Tuple4* out) { + (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c, &out->d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3& in, + Tuple4* out) { + (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c, &out->d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4& in, + Tuple4* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c, &out->d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5& in, + Tuple4* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, + &out->a, &out->b, &out->c, &out->d); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6& in, + Tuple4* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, + &out->a, &out->b, &out->c, &out->d); +} + +// Dispatchers with 5 out params. + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple0& in, + Tuple5* out) { + (obj->*method)(&out->a, &out->b, &out->c, &out->d, &out->e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const InA& in, + Tuple5* out) { + (obj->*method)(in, &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple1& in, + Tuple5* out) { + (obj->*method)(in.a, &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple2& in, + Tuple5* out) { + (obj->*method)(in.a, in.b, &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple3& in, + Tuple5* out) { + (obj->*method)(in.a, in.b, in.c, &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple4& in, + Tuple5* out) { + (obj->*method)(in.a, in.b, in.c, in.d, &out->a, &out->b, &out->c, &out->d, + &out->e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple5& in, + Tuple5* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, + &out->a, &out->b, &out->c, &out->d, &out->e); +} + +template +inline void DispatchToMethod(ObjT* obj, Method method, + const Tuple6& in, + Tuple5* out) { + (obj->*method)(in.a, in.b, in.c, in.d, in.e, in.f, + &out->a, &out->b, &out->c, &out->d, &out->e); +} + +#endif // BASE_TUPLE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/tuple_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/tuple_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/tuple_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/tuple_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,126 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/tuple.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +void DoAdd(int a, int b, int c, int* res) { + *res = a + b + c; +} + +struct Addy { + Addy() { } + void DoAdd(int a, int b, int c, int d, int* res) { + *res = a + b + c + d; + } +}; + +struct Addz { + Addz() { } + void DoAdd(int a, int b, int c, int d, int e, int* res) { + *res = a + b + c + d + e; + } +}; + +} // namespace + +TEST(TupleTest, Basic) { + Tuple0 t0 = MakeTuple(); + Tuple1 t1(1); + Tuple2 t2 = MakeTuple(1, static_cast("wee")); + Tuple3 t3(1, 2, 3); + Tuple4 t4(1, 2, 3, &t1.a); + Tuple5 t5(1, 2, 3, 4, &t4.a); + Tuple6 t6(1, 2, 3, 4, 5, &t4.a); + + EXPECT_EQ(1, t1.a); + EXPECT_EQ(1, t2.a); + EXPECT_EQ(1, t3.a); + EXPECT_EQ(2, t3.b); + EXPECT_EQ(3, t3.c); + EXPECT_EQ(1, t4.a); + EXPECT_EQ(2, t4.b); + EXPECT_EQ(3, t4.c); + EXPECT_EQ(1, t5.a); + EXPECT_EQ(2, t5.b); + EXPECT_EQ(3, t5.c); + EXPECT_EQ(4, t5.d); + EXPECT_EQ(1, t6.a); + EXPECT_EQ(2, t6.b); + EXPECT_EQ(3, t6.c); + EXPECT_EQ(4, t6.d); + EXPECT_EQ(5, t6.e); + + EXPECT_EQ(1, t1.a); + DispatchToFunction(&DoAdd, t4); + EXPECT_EQ(6, t1.a); + + int res = 0; + DispatchToFunction(&DoAdd, MakeTuple(9, 8, 7, &res)); + EXPECT_EQ(24, res); + + Addy addy; + EXPECT_EQ(1, t4.a); + DispatchToMethod(&addy, &Addy::DoAdd, t5); + EXPECT_EQ(10, t4.a); + + Addz addz; + EXPECT_EQ(10, t4.a); + DispatchToMethod(&addz, &Addz::DoAdd, t6); + EXPECT_EQ(15, t4.a); +} + +namespace { + +struct CopyLogger { + CopyLogger() { ++TimesConstructed; } + CopyLogger(const CopyLogger& tocopy) { ++TimesConstructed; ++TimesCopied; } + ~CopyLogger() { } + + static int TimesCopied; + static int TimesConstructed; +}; + +void SomeLoggerMethRef(const CopyLogger& logy, const CopyLogger* ptr, bool* b) { + *b = &logy == ptr; +} + +void SomeLoggerMethCopy(CopyLogger logy, const CopyLogger* ptr, bool* b) { + *b = &logy == ptr; +} + +int CopyLogger::TimesCopied = 0; +int CopyLogger::TimesConstructed = 0; + +} // namespace + +TEST(TupleTest, Copying) { + CopyLogger logger; + EXPECT_EQ(0, CopyLogger::TimesCopied); + EXPECT_EQ(1, CopyLogger::TimesConstructed); + + bool res = false; + + // Creating the tuple should copy the class to store internally in the tuple. + Tuple3 tuple(logger, &logger, &res); + tuple.b = &tuple.a; + EXPECT_EQ(2, CopyLogger::TimesConstructed); + EXPECT_EQ(1, CopyLogger::TimesCopied); + + // Our internal Logger and the one passed to the function should be the same. + res = false; + DispatchToFunction(&SomeLoggerMethRef, tuple); + EXPECT_TRUE(res); + EXPECT_EQ(2, CopyLogger::TimesConstructed); + EXPECT_EQ(1, CopyLogger::TimesCopied); + + // Now they should be different, since the function call will make a copy. + res = false; + DispatchToFunction(&SomeLoggerMethCopy, tuple); + EXPECT_FALSE(res); + EXPECT_EQ(3, CopyLogger::TimesConstructed); + EXPECT_EQ(2, CopyLogger::TimesCopied); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/values.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/values.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/values.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/values.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,653 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/string_util.h" +#include "base/values.h" + +///////////////////// Value //////////////////// + +Value::~Value() { +} + +// static +Value* Value::CreateNullValue() { + return new Value(TYPE_NULL); +} + +// static +Value* Value::CreateBooleanValue(bool in_value) { + return new FundamentalValue(in_value); +} + +// static +Value* Value::CreateIntegerValue(int in_value) { + return new FundamentalValue(in_value); +} + +// static +Value* Value::CreateRealValue(double in_value) { + return new FundamentalValue(in_value); +} + +// static +Value* Value::CreateStringValue(const std::string& in_value) { + return new StringValue(in_value); +} + +// static +Value* Value::CreateStringValue(const std::wstring& in_value) { + return new StringValue(in_value); +} + +// static +BinaryValue* Value::CreateBinaryValue(char* buffer, size_t size) { + return BinaryValue::Create(buffer, size); +} + +bool Value::GetAsBoolean(bool* in_value) const { + return false; +} + +bool Value::GetAsInteger(int* in_value) const { + return false; +} + +bool Value::GetAsReal(double* in_value) const { + return false; +} + +bool Value::GetAsString(std::string* in_value) const { + return false; +} + +bool Value::GetAsString(std::wstring* in_value) const { + return false; +} + +Value* Value::DeepCopy() const { + // This method should only be getting called for null Values--all subclasses + // need to provide their own implementation;. + DCHECK(IsType(TYPE_NULL)); + return CreateNullValue(); +} + +bool Value::Equals(const Value* other) const { + // This method should only be getting called for null Values--all subclasses + // need to provide their own implementation;. + DCHECK(IsType(TYPE_NULL)); + return other->IsType(TYPE_NULL); +} + +///////////////////// FundamentalValue //////////////////// + +FundamentalValue::~FundamentalValue() { +} + +bool FundamentalValue::GetAsBoolean(bool* out_value) const { + if (out_value && IsType(TYPE_BOOLEAN)) + *out_value = boolean_value_; + return (IsType(TYPE_BOOLEAN)); +} + +bool FundamentalValue::GetAsInteger(int* out_value) const { + if (out_value && IsType(TYPE_INTEGER)) + *out_value = integer_value_; + return (IsType(TYPE_INTEGER)); +} + +bool FundamentalValue::GetAsReal(double* out_value) const { + if (out_value && IsType(TYPE_REAL)) + *out_value = real_value_; + return (IsType(TYPE_REAL)); +} + +Value* FundamentalValue::DeepCopy() const { + switch (GetType()) { + case TYPE_BOOLEAN: + return CreateBooleanValue(boolean_value_); + + case TYPE_INTEGER: + return CreateIntegerValue(integer_value_); + + case TYPE_REAL: + return CreateRealValue(real_value_); + + default: + NOTREACHED(); + return NULL; + } +} + +bool FundamentalValue::Equals(const Value* other) const { + if (other->GetType() != GetType()) + return false; + + switch (GetType()) { + case TYPE_BOOLEAN: { + bool lhs, rhs; + return GetAsBoolean(&lhs) && other->GetAsBoolean(&rhs) && lhs == rhs; + } + case TYPE_INTEGER: { + int lhs, rhs; + return GetAsInteger(&lhs) && other->GetAsInteger(&rhs) && lhs == rhs; + } + case TYPE_REAL: { + double lhs, rhs; + return GetAsReal(&lhs) && other->GetAsReal(&rhs) && lhs == rhs; + } + default: + NOTREACHED(); + return false; + } +} + +///////////////////// StringValue //////////////////// + +StringValue::StringValue(const std::string& in_value) + : Value(TYPE_STRING), + value_(in_value) { + DCHECK(IsStringUTF8(in_value)); +} + +StringValue::StringValue(const std::wstring& in_value) + : Value(TYPE_STRING), + value_(WideToUTF8(in_value)) { +} + +StringValue::~StringValue() { +} + +bool StringValue::GetAsString(std::string* out_value) const { + if (out_value) + *out_value = value_; + return true; +} + +bool StringValue::GetAsString(std::wstring* out_value) const { + if (out_value) + *out_value = UTF8ToWide(value_); + return true; +} + +Value* StringValue::DeepCopy() const { + return CreateStringValue(value_); +} + +bool StringValue::Equals(const Value* other) const { + if (other->GetType() != GetType()) + return false; + std::string lhs, rhs; + return GetAsString(&lhs) && other->GetAsString(&rhs) && lhs == rhs; +} + +///////////////////// BinaryValue //////////////////// + +//static +BinaryValue* BinaryValue::Create(char* buffer, size_t size) { + if (!buffer) + return NULL; + + return new BinaryValue(buffer, size); +} + +//static +BinaryValue* BinaryValue::CreateWithCopiedBuffer(char* buffer, size_t size) { + if (!buffer) + return NULL; + + char* buffer_copy = new char[size]; + memcpy(buffer_copy, buffer, size); + return new BinaryValue(buffer_copy, size); +} + + +BinaryValue::BinaryValue(char* buffer, size_t size) + : Value(TYPE_BINARY), + buffer_(buffer), + size_(size) { + DCHECK(buffer_); +} + +BinaryValue::~BinaryValue() { + DCHECK(buffer_); + if (buffer_) + delete[] buffer_; +} + +Value* BinaryValue::DeepCopy() const { + return CreateWithCopiedBuffer(buffer_, size_); +} + +bool BinaryValue::Equals(const Value* other) const { + if (other->GetType() != GetType()) + return false; + const BinaryValue* other_binary = static_cast(other); + if (other_binary->size_ != size_) + return false; + return !memcmp(buffer_, other_binary->buffer_, size_); +} + +///////////////////// DictionaryValue //////////////////// + +DictionaryValue::~DictionaryValue() { + Clear(); +} + +void DictionaryValue::Clear() { + ValueMap::iterator dict_iterator = dictionary_.begin(); + while (dict_iterator != dictionary_.end()) { + delete dict_iterator->second; + ++dict_iterator; + } + + dictionary_.clear(); +} + +bool DictionaryValue::HasKey(const std::wstring& key) const { + ValueMap::const_iterator current_entry = dictionary_.find(key); + DCHECK((current_entry == dictionary_.end()) || current_entry->second); + return current_entry != dictionary_.end(); +} + +void DictionaryValue::SetInCurrentNode(const std::wstring& key, + Value* in_value) { + // If there's an existing value here, we need to delete it, because + // we own all our children. + if (HasKey(key)) { + DCHECK(dictionary_[key] != in_value); // This would be bogus + delete dictionary_[key]; + } + + dictionary_[key] = in_value; +} + +bool DictionaryValue::Set(const std::wstring& path, Value* in_value) { + DCHECK(in_value); + + std::wstring key = path; + + size_t delimiter_position = path.find_first_of(L".", 0); + // If there isn't a dictionary delimiter in the path, we're done. + if (delimiter_position == std::wstring::npos) { + SetInCurrentNode(key, in_value); + return true; + } else { + key = path.substr(0, delimiter_position); + } + + // Assume that we're indexing into a dictionary. + DictionaryValue* entry = NULL; + if (!HasKey(key) || (dictionary_[key]->GetType() != TYPE_DICTIONARY)) { + entry = new DictionaryValue; + SetInCurrentNode(key, entry); + } else { + entry = static_cast(dictionary_[key]); + } + + std::wstring remaining_path = path.substr(delimiter_position + 1); + return entry->Set(remaining_path, in_value); +} + +bool DictionaryValue::SetBoolean(const std::wstring& path, bool in_value) { + return Set(path, CreateBooleanValue(in_value)); +} + +bool DictionaryValue::SetInteger(const std::wstring& path, int in_value) { + return Set(path, CreateIntegerValue(in_value)); +} + +bool DictionaryValue::SetReal(const std::wstring& path, double in_value) { + return Set(path, CreateRealValue(in_value)); +} + +bool DictionaryValue::SetString(const std::wstring& path, + const std::string& in_value) { + return Set(path, CreateStringValue(in_value)); +} + +bool DictionaryValue::SetString(const std::wstring& path, + const std::wstring& in_value) { + return Set(path, CreateStringValue(in_value)); +} + +bool DictionaryValue::Get(const std::wstring& path, Value** out_value) const { + std::wstring key = path; + + size_t delimiter_position = path.find_first_of(L".", 0); + if (delimiter_position != std::wstring::npos) { + key = path.substr(0, delimiter_position); + } + + ValueMap::const_iterator entry_iterator = dictionary_.find(key); + if (entry_iterator == dictionary_.end()) + return false; + Value* entry = entry_iterator->second; + + if (delimiter_position == std::wstring::npos) { + if (out_value) + *out_value = entry; + return true; + } + + if (entry->IsType(TYPE_DICTIONARY)) { + DictionaryValue* dictionary = static_cast(entry); + return dictionary->Get(path.substr(delimiter_position + 1), out_value); + } + + return false; +} + +bool DictionaryValue::GetBoolean(const std::wstring& path, + bool* bool_value) const { + Value* value; + if (!Get(path, &value)) + return false; + + return value->GetAsBoolean(bool_value); +} + +bool DictionaryValue::GetInteger(const std::wstring& path, + int* out_value) const { + Value* value; + if (!Get(path, &value)) + return false; + + return value->GetAsInteger(out_value); +} + +bool DictionaryValue::GetReal(const std::wstring& path, + double* out_value) const { + Value* value; + if (!Get(path, &value)) + return false; + + return value->GetAsReal(out_value); +} + +bool DictionaryValue::GetString(const std::wstring& path, + std::string* out_value) const { + Value* value; + if (!Get(path, &value)) + return false; + + return value->GetAsString(out_value); +} + +bool DictionaryValue::GetString(const std::wstring& path, + std::wstring* out_value) const { + Value* value; + if (!Get(path, &value)) + return false; + + return value->GetAsString(out_value); +} + +bool DictionaryValue::GetBinary(const std::wstring& path, + BinaryValue** out_value) const { + Value* value; + bool result = Get(path, &value); + if (!result || !value->IsType(TYPE_BINARY)) + return false; + + if (out_value) + *out_value = static_cast(value); + + return true; +} + +bool DictionaryValue::GetDictionary(const std::wstring& path, + DictionaryValue** out_value) const { + Value* value; + bool result = Get(path, &value); + if (!result || !value->IsType(TYPE_DICTIONARY)) + return false; + + if (out_value) + *out_value = static_cast(value); + + return true; +} + +bool DictionaryValue::GetList(const std::wstring& path, + ListValue** out_value) const { + Value* value; + bool result = Get(path, &value); + if (!result || !value->IsType(TYPE_LIST)) + return false; + + if (out_value) + *out_value = static_cast(value); + + return true; +} + +bool DictionaryValue::Remove(const std::wstring& path, Value** out_value) { + std::wstring key = path; + + size_t delimiter_position = path.find_first_of(L".", 0); + if (delimiter_position != std::wstring::npos) { + key = path.substr(0, delimiter_position); + } + + ValueMap::iterator entry_iterator = dictionary_.find(key); + if (entry_iterator == dictionary_.end()) + return false; + Value* entry = entry_iterator->second; + + if (delimiter_position == std::wstring::npos) { + if (out_value) + *out_value = entry; + else + delete entry; + + dictionary_.erase(entry_iterator); + return true; + } + + if (entry->IsType(TYPE_DICTIONARY)) { + DictionaryValue* dictionary = static_cast(entry); + return dictionary->Remove(path.substr(delimiter_position + 1), out_value); + } + + return false; +} + +Value* DictionaryValue::DeepCopy() const { + DictionaryValue* result = new DictionaryValue; + + ValueMap::const_iterator current_entry = dictionary_.begin(); + while (current_entry != dictionary_.end()) { + result->Set(current_entry->first, current_entry->second->DeepCopy()); + ++current_entry; + } + + return result; +} + +bool DictionaryValue::Equals(const Value* other) const { + if (other->GetType() != GetType()) + return false; + + const DictionaryValue* other_dict = + static_cast(other); + key_iterator lhs_it(begin_keys()); + key_iterator rhs_it(other_dict->begin_keys()); + while (lhs_it != end_keys() && rhs_it != other_dict->end_keys()) { + Value* lhs; + Value* rhs; + if (!Get(*lhs_it, &lhs) || !other_dict->Get(*rhs_it, &rhs) || + !lhs->Equals(rhs)) { + return false; + } + ++lhs_it; + ++rhs_it; + } + if (lhs_it != end_keys() || rhs_it != other_dict->end_keys()) + return false; + + return true; +} + +///////////////////// ListValue //////////////////// + +ListValue::~ListValue() { + Clear(); +} + +void ListValue::Clear() { + ValueVector::iterator list_iterator = list_.begin(); + while (list_iterator != list_.end()) { + delete *list_iterator; + ++list_iterator; + } + list_.clear(); +} + +bool ListValue::Set(size_t index, Value* in_value) { + if (!in_value) + return false; + + if (index >= list_.size()) { + // Pad out any intermediate indexes with null settings + while (index > list_.size()) + Append(CreateNullValue()); + Append(in_value); + } else { + DCHECK(list_[index] != in_value); + delete list_[index]; + list_[index] = in_value; + } + return true; +} + +bool ListValue::Get(size_t index, Value** out_value) const { + if (index >= list_.size()) + return false; + + if (out_value) + *out_value = list_[index]; + + return true; +} + +bool ListValue::GetBoolean(size_t index, bool* bool_value) const { + Value* value; + if (!Get(index, &value)) + return false; + + return value->GetAsBoolean(bool_value); +} + +bool ListValue::GetInteger(size_t index, int* out_value) const { + Value* value; + if (!Get(index, &value)) + return false; + + return value->GetAsInteger(out_value); +} + +bool ListValue::GetReal(size_t index, double* out_value) const { + Value* value; + if (!Get(index, &value)) + return false; + + return value->GetAsReal(out_value); +} + +bool ListValue::GetString(size_t index, std::string* out_value) const { + Value* value; + if (!Get(index, &value)) + return false; + + return value->GetAsString(out_value); +} + +bool ListValue::GetBinary(size_t index, BinaryValue** out_value) const { + Value* value; + bool result = Get(index, &value); + if (!result || !value->IsType(TYPE_BINARY)) + return false; + + if (out_value) + *out_value = static_cast(value); + + return true; +} + +bool ListValue::GetDictionary(size_t index, DictionaryValue** out_value) const { + Value* value; + bool result = Get(index, &value); + if (!result || !value->IsType(TYPE_DICTIONARY)) + return false; + + if (out_value) + *out_value = static_cast(value); + + return true; +} + +bool ListValue::GetList(size_t index, ListValue** out_value) const { + Value* value; + bool result = Get(index, &value); + if (!result || !value->IsType(TYPE_LIST)) + return false; + + if (out_value) + *out_value = static_cast(value); + + return true; +} + +bool ListValue::Remove(size_t index, Value** out_value) { + if (index >= list_.size()) + return false; + + if (out_value) + *out_value = list_[index]; + else + delete list_[index]; + + ValueVector::iterator entry = list_.begin(); + entry += index; + + list_.erase(entry); + return true; +} + +void ListValue::Append(Value* in_value) { + DCHECK(in_value); + list_.push_back(in_value); +} + +Value* ListValue::DeepCopy() const { + ListValue* result = new ListValue; + + ValueVector::const_iterator current_entry = list_.begin(); + while (current_entry != list_.end()) { + result->Append((*current_entry)->DeepCopy()); + ++current_entry; + } + + return result; +} + +bool ListValue::Equals(const Value* other) const { + if (other->GetType() != GetType()) + return false; + + const ListValue* other_list = + static_cast(other); + const_iterator lhs_it, rhs_it; + for (lhs_it = begin(), rhs_it = other_list->begin(); + lhs_it != end() && rhs_it != other_list->end(); + ++lhs_it, ++rhs_it) { + if (!(*lhs_it)->Equals(*rhs_it)) + return false; + } + if (lhs_it != end() || rhs_it != other_list->end()) + return false; + + return true; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/values.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/values.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/values.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/values.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,375 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file specifies a recursive data storage class called Value +// intended for storing setting and other persistable data. +// It includes the ability to specify (recursive) lists and dictionaries, so +// it's fairly expressive. However, the API is optimized for the common case, +// namely storing a hierarchical tree of simple values. Given a +// DictionaryValue root, you can easily do things like: +// +// root->SetString(L"global.pages.homepage", L"http://goateleporter.com"); +// std::wstring homepage = L"http://google.com"; // default/fallback value +// root->GetString(L"global.pages.homepage", &homepage); +// +// where "global" and "pages" are also DictionaryValues, and "homepage" +// is a string setting. If some elements of the path didn't exist yet, +// the SetString() method would create the missing elements and attach them +// to root before attaching the homepage value. + +#ifndef BASE_VALUES_H_ +#define BASE_VALUES_H_ + +#include +#include +#include +#include + +#include "base/basictypes.h" + +class Value; +class FundamentalValue; +class StringValue; +class BinaryValue; +class DictionaryValue; +class ListValue; + +typedef std::vector ValueVector; +typedef std::map ValueMap; + +// The Value class is the base class for Values. A Value can be +// instantiated via the Create*Value() factory methods, or by directly +// creating instances of the subclasses. +class Value { + public: + virtual ~Value(); + + // Convenience methods for creating Value objects for various + // kinds of values without thinking about which class implements them. + // These can always be expected to return a valid Value*. + static Value* CreateNullValue(); + static Value* CreateBooleanValue(bool in_value); + static Value* CreateIntegerValue(int in_value); + static Value* CreateRealValue(double in_value); + static Value* CreateStringValue(const std::string& in_value); + static Value* CreateStringValue(const std::wstring& in_value); + + // This one can return NULL if the input isn't valid. If the return value + // is non-null, the new object has taken ownership of the buffer pointer. + static BinaryValue* CreateBinaryValue(char* buffer, size_t size); + + typedef enum { + TYPE_NULL = 0, + TYPE_BOOLEAN, + TYPE_INTEGER, + TYPE_REAL, + TYPE_STRING, + TYPE_BINARY, + TYPE_DICTIONARY, + TYPE_LIST + } ValueType; + + // Returns the type of the value stored by the current Value object. + // Each type will be implemented by only one subclass of Value, so it's + // safe to use the ValueType to determine whether you can cast from + // Value* to (Implementing Class)*. Also, a Value object never changes + // its type after construction. + ValueType GetType() const { return type_; } + + // Returns true if the current object represents a given type. + bool IsType(ValueType type) const { return type == type_; } + + // These methods allow the convenient retrieval of settings. + // If the current setting object can be converted into the given type, + // the value is returned through the "value" parameter and true is returned; + // otherwise, false is returned and "value" is unchanged. + virtual bool GetAsBoolean(bool* out_value) const; + virtual bool GetAsInteger(int* out_value) const; + virtual bool GetAsReal(double* out_value) const; + virtual bool GetAsString(std::string* out_value) const; + virtual bool GetAsString(std::wstring* out_value) const; + + // This creates a deep copy of the entire Value tree, and returns a pointer + // to the copy. The caller gets ownership of the copy, of course. + virtual Value* DeepCopy() const; + + // Compares if two Value objects have equal contents. + virtual bool Equals(const Value* other) const; + + protected: + // This isn't safe for end-users (they should use the Create*Value() + // static methods above), but it's useful for subclasses. + Value(ValueType type) : type_(type) {} + + private: + DISALLOW_EVIL_CONSTRUCTORS(Value); + Value(); + + ValueType type_; +}; + +// FundamentalValue represents the simple fundamental types of values. +class FundamentalValue : public Value { + public: + FundamentalValue(bool in_value) + : Value(TYPE_BOOLEAN), boolean_value_(in_value) {} + FundamentalValue(int in_value) + : Value(TYPE_INTEGER), integer_value_(in_value) {} + FundamentalValue(double in_value) + : Value(TYPE_REAL), real_value_(in_value) {} + ~FundamentalValue(); + + // Subclassed methods + virtual bool GetAsBoolean(bool* out_value) const; + virtual bool GetAsInteger(int* out_value) const; + virtual bool GetAsReal(double* out_value) const; + virtual Value* DeepCopy() const; + virtual bool Equals(const Value* other) const; + + private: + DISALLOW_EVIL_CONSTRUCTORS(FundamentalValue); + + union { + bool boolean_value_; + int integer_value_; + double real_value_; + }; +}; + +class StringValue : public Value { + public: + // Initializes a StringValue with a UTF-8 narrow character string. + StringValue(const std::string& in_value); + + // Initializes a StringValue with a wide character string. + StringValue(const std::wstring& in_value); + + ~StringValue(); + + // Subclassed methods + bool GetAsString(std::string* out_value) const; + bool GetAsString(std::wstring* out_value) const; + Value* DeepCopy() const; + virtual bool Equals(const Value* other) const; + + private: + DISALLOW_EVIL_CONSTRUCTORS(StringValue); + + std::string value_; +}; + +class BinaryValue: public Value { +public: + // Creates a Value to represent a binary buffer. The new object takes + // ownership of the pointer passed in, if successful. + // Returns NULL if buffer is NULL. + static BinaryValue* Create(char* buffer, size_t size); + + // For situations where you want to keep ownership of your buffer, this + // factory method creates a new BinaryValue by copying the contents of the + // buffer that's passed in. + // Returns NULL if buffer is NULL. + static BinaryValue* CreateWithCopiedBuffer(char* buffer, size_t size); + + ~BinaryValue(); + + // Subclassed methods + Value* DeepCopy() const; + virtual bool Equals(const Value* other) const; + + size_t GetSize() const { return size_; } + char* GetBuffer() { return buffer_; } + +private: + DISALLOW_EVIL_CONSTRUCTORS(BinaryValue); + + // Constructor is private so that only objects with valid buffer pointers + // and size values can be created. + BinaryValue(char* buffer, size_t size); + + char* buffer_; + size_t size_; +}; + +class DictionaryValue : public Value { + public: + DictionaryValue() : Value(TYPE_DICTIONARY) {} + ~DictionaryValue(); + + // Subclassed methods + Value* DeepCopy() const; + virtual bool Equals(const Value* other) const; + + // Returns true if the current dictionary has a value for the given key. + bool HasKey(const std::wstring& key) const; + + // Returns the number of Values in this dictionary. + size_t GetSize() const { return dictionary_.size(); } + + // Clears any current contents of this dictionary. + void Clear(); + + // Sets the Value associated with the given path starting from this object. + // A path has the form "" or "..[...]", where "." indexes + // into the next DictionaryValue down. Obviously, "." can't be used + // within a key, but there are no other restrictions on keys. + // If the key at any step of the way doesn't exist, or exists but isn't + // a DictionaryValue, a new DictionaryValue will be created and attached + // to the path in that location. + // Note that the dictionary takes ownership of the value referenced by + // |in_value|. + bool Set(const std::wstring& path, Value* in_value); + + // Convenience forms of Set(). These methods will replace any existing + // value at that path, even if it has a different type. + bool SetBoolean(const std::wstring& path, bool in_value); + bool SetInteger(const std::wstring& path, int in_value); + bool SetReal(const std::wstring& path, double in_value); + bool SetString(const std::wstring& path, const std::string& in_value); + bool SetString(const std::wstring& path, const std::wstring& in_value); + + // Gets the Value associated with the given path starting from this object. + // A path has the form "" or "..[...]", where "." indexes + // into the next DictionaryValue down. If the path can be resolved + // successfully, the value for the last key in the path will be returned + // through the "value" parameter, and the function will return true. + // Otherwise, it will return false and "value" will be untouched. + // Note that the dictionary always owns the value that's returned. + bool Get(const std::wstring& path, Value** out_value) const; + + // These are convenience forms of Get(). The value will be retrieved + // and the return value will be true if the path is valid and the value at + // the end of the path can be returned in the form specified. + bool GetBoolean(const std::wstring& path, bool* out_value) const; + bool GetInteger(const std::wstring& path, int* out_value) const; + bool GetReal(const std::wstring& path, double* out_value) const; + bool GetString(const std::wstring& path, std::string* out_value) const; + bool GetString(const std::wstring& path, std::wstring* out_value) const; + bool GetBinary(const std::wstring& path, BinaryValue** out_value) const; + bool GetDictionary(const std::wstring& path, + DictionaryValue** out_value) const; + bool GetList(const std::wstring& path, ListValue** out_value) const; + + // Removes the Value with the specified path from this dictionary (or one + // of its child dictionaries, if the path is more than just a local key). + // If |out_value| is non-NULL, the removed Value AND ITS OWNERSHIP will be + // passed out via out_value. If |out_value| is NULL, the removed value will + // be deleted. This method returns true if |path| is a valid path; otherwise + // it will return false and the DictionaryValue object will be unchanged. + bool Remove(const std::wstring& path, Value** out_value); + + // This class provides an iterator for the keys in the dictionary. + // It can't be used to modify the dictionary. + class key_iterator + : private std::iterator { + public: + key_iterator(ValueMap::const_iterator itr) { itr_ = itr; } + key_iterator operator++() { ++itr_; return *this; } + const std::wstring& operator*() { return itr_->first; } + bool operator!=(const key_iterator& other) { return itr_ != other.itr_; } + bool operator==(const key_iterator& other) { return itr_ == other.itr_; } + + private: + ValueMap::const_iterator itr_; + }; + + key_iterator begin_keys() const { return key_iterator(dictionary_.begin()); } + key_iterator end_keys() const { return key_iterator(dictionary_.end()); } + + private: + DISALLOW_EVIL_CONSTRUCTORS(DictionaryValue); + + // Associates the value |in_value| with the |key|. This method should be + // used instead of "dictionary_[key] = foo" so that any previous value can + // be properly deleted. + void SetInCurrentNode(const std::wstring& key, Value* in_value); + + ValueMap dictionary_; +}; + +// This type of Value represents a list of other Value values. +class ListValue : public Value { + public: + ListValue() : Value(TYPE_LIST) {} + ~ListValue(); + + // Subclassed methods + Value* DeepCopy() const; + virtual bool Equals(const Value* other) const; + + // Clears the contents of this ListValue + void Clear(); + + // Returns the number of Values in this list. + size_t GetSize() const { return list_.size(); } + + // Sets the list item at the given index to be the Value specified by + // the value given. If the index beyond the current end of the list, null + // Values will be used to pad out the list. + // Returns true if successful, or false if the index was negative or + // the value is a null pointer. + bool Set(size_t index, Value* in_value); + + // Gets the Value at the given index. Modifies value (and returns true) + // only if the index falls within the current list range. + // Note that the list always owns the Value passed out via out_value. + bool Get(size_t index, Value** out_value) const; + + // Convenience forms of Get(). Modifies value (and returns true) only if + // the index is valid and the Value at that index can be returned in + // the specified form. + bool GetBoolean(size_t index, bool* out_value) const; + bool GetInteger(size_t index, int* out_value) const; + bool GetReal(size_t index, double* out_value) const; + bool GetString(size_t index, std::string* out_value) const; + bool GetBinary(size_t index, BinaryValue** out_value) const; + bool GetDictionary(size_t index, DictionaryValue** out_value) const; + bool GetList(size_t index, ListValue** out_value) const; + + // Removes the Value with the specified index from this list. + // If |out_value| is non-NULL, the removed Value AND ITS OWNERSHIP will be + // passed out via |out_value|. If |out_value| is NULL, the removed value will + // be deleted. This method returns true if |index| is valid; otherwise + // it will return false and the ListValue object will be unchanged. + bool Remove(size_t index, Value** out_value); + + // Appends a Value to the end of the list. + void Append(Value* in_value); + + // Iteration + typedef ValueVector::iterator iterator; + typedef ValueVector::const_iterator const_iterator; + + ListValue::iterator begin() { return list_.begin(); } + ListValue::iterator end() { return list_.end(); } + + ListValue::const_iterator begin() const { return list_.begin(); } + ListValue::const_iterator end() const { return list_.end(); } + + ListValue::iterator Erase(iterator item) { + return list_.erase(item); + } + + private: + DISALLOW_EVIL_CONSTRUCTORS(ListValue); + + ValueVector list_; +}; + +// This interface is implemented by classes that know how to serialize and +// deserialize Value objects. +class ValueSerializer { + public: + virtual ~ValueSerializer() {} + + virtual bool Serialize(const Value& root) = 0; + + // This method deserializes the subclass-specific format into a Value object. + // If the return value is non-NULL, the caller takes ownership of returned + // Value. If the return value is NULL, and if error_message is non-NULL, + // error_message should be filled with a message describing the error. + virtual Value* Deserialize(std::string* error_message) = 0; +}; + +#endif // BASE_VALUES_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/values_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/values_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/values_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/values_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,450 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/values.h" +#include "base/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +class ValuesTest: public testing::Test { +}; + +TEST(ValuesTest, Basic) { + // Test basic dictionary getting/setting + DictionaryValue settings; + std::wstring homepage = L"http://google.com"; + ASSERT_FALSE( + settings.GetString(L"global.homepage", &homepage)); + ASSERT_EQ(std::wstring(L"http://google.com"), homepage); + + ASSERT_FALSE(settings.Get(L"global", NULL)); + ASSERT_TRUE(settings.Set(L"global", Value::CreateBooleanValue(true))); + ASSERT_TRUE(settings.Get(L"global", NULL)); + ASSERT_TRUE(settings.SetString(L"global.homepage", L"http://scurvy.com")); + ASSERT_TRUE(settings.Get(L"global", NULL)); + homepage = L"http://google.com"; + ASSERT_TRUE(settings.GetString(L"global.homepage", &homepage)); + ASSERT_EQ(std::wstring(L"http://scurvy.com"), homepage); + + // Test storing a dictionary in a list. + ListValue* toolbar_bookmarks; + ASSERT_FALSE( + settings.GetList(L"global.toolbar.bookmarks", &toolbar_bookmarks)); + + toolbar_bookmarks = new ListValue; + settings.Set(L"global.toolbar.bookmarks", toolbar_bookmarks); + ASSERT_TRUE( + settings.GetList(L"global.toolbar.bookmarks", &toolbar_bookmarks)); + + DictionaryValue* new_bookmark = new DictionaryValue; + new_bookmark->SetString(L"name", L"Froogle"); + new_bookmark->SetString(L"url", L"http://froogle.com"); + toolbar_bookmarks->Append(new_bookmark); + + ListValue* bookmark_list; + ASSERT_TRUE(settings.GetList(L"global.toolbar.bookmarks", &bookmark_list)); + DictionaryValue* bookmark; + ASSERT_EQ(1U, bookmark_list->GetSize()); + ASSERT_TRUE(bookmark_list->GetDictionary(0, &bookmark)); + std::wstring bookmark_name = L"Unnamed"; + ASSERT_TRUE(bookmark->GetString(L"name", &bookmark_name)); + ASSERT_EQ(std::wstring(L"Froogle"), bookmark_name); + std::wstring bookmark_url; + ASSERT_TRUE(bookmark->GetString(L"url", &bookmark_url)); + ASSERT_EQ(std::wstring(L"http://froogle.com"), bookmark_url); +} + +TEST(ValuesTest, List) { + scoped_ptr mixed_list(new ListValue()); + mixed_list->Set(0, Value::CreateBooleanValue(true)); + mixed_list->Set(1, Value::CreateIntegerValue(42)); + mixed_list->Set(2, Value::CreateRealValue(88.8)); + mixed_list->Set(3, Value::CreateStringValue("foo")); + ASSERT_EQ(4u, mixed_list->GetSize()); + + Value *value = NULL; + bool bool_value = false; + int int_value = 0; + double double_value = 0.0; + std::string string_value; + + ASSERT_FALSE(mixed_list->Get(4, &value)); + + ASSERT_FALSE(mixed_list->GetInteger(0, &int_value)); + ASSERT_EQ(0, int_value); + ASSERT_FALSE(mixed_list->GetReal(1, &double_value)); + ASSERT_EQ(0.0, double_value); + ASSERT_FALSE(mixed_list->GetString(2, &string_value)); + ASSERT_EQ("", string_value); + ASSERT_FALSE(mixed_list->GetBoolean(3, &bool_value)); + ASSERT_EQ(false, bool_value); + + ASSERT_TRUE(mixed_list->GetBoolean(0, &bool_value)); + ASSERT_EQ(true, bool_value); + ASSERT_TRUE(mixed_list->GetInteger(1, &int_value)); + ASSERT_EQ(42, int_value); + ASSERT_TRUE(mixed_list->GetReal(2, &double_value)); + ASSERT_EQ(88.8, double_value); + ASSERT_TRUE(mixed_list->GetString(3, &string_value)); + ASSERT_EQ("foo", string_value); +} + +TEST(ValuesTest, BinaryValue) { + char* buffer = NULL; + // Passing a null buffer pointer doesn't yield a BinaryValue + scoped_ptr binary(BinaryValue::Create(buffer, 0)); + ASSERT_FALSE(binary.get()); + + // If you want to represent an empty binary value, use a zero-length buffer. + buffer = new char[1]; + ASSERT_TRUE(buffer); + binary.reset(BinaryValue::Create(buffer, 0)); + ASSERT_TRUE(binary.get()); + ASSERT_TRUE(binary->GetBuffer()); + ASSERT_EQ(buffer, binary->GetBuffer()); + ASSERT_EQ(0U, binary->GetSize()); + + // Test the common case of a non-empty buffer + buffer = new char[15]; + binary.reset(BinaryValue::Create(buffer, 15)); + ASSERT_TRUE(binary.get()); + ASSERT_TRUE(binary->GetBuffer()); + ASSERT_EQ(buffer, binary->GetBuffer()); + ASSERT_EQ(15U, binary->GetSize()); + + char stack_buffer[42]; + memset(stack_buffer, '!', 42); + binary.reset(BinaryValue::CreateWithCopiedBuffer(stack_buffer, 42)); + ASSERT_TRUE(binary.get()); + ASSERT_TRUE(binary->GetBuffer()); + ASSERT_NE(stack_buffer, binary->GetBuffer()); + ASSERT_EQ(42U, binary->GetSize()); + ASSERT_EQ(0, memcmp(stack_buffer, binary->GetBuffer(), binary->GetSize())); +} + +TEST(ValuesTest, StringValue) { + // Test overloaded CreateStringValue. + scoped_ptr narrow_value(Value::CreateStringValue("narrow")); + ASSERT_TRUE(narrow_value.get()); + ASSERT_TRUE(narrow_value->IsType(Value::TYPE_STRING)); + scoped_ptr wide_value(Value::CreateStringValue(L"wide")); + ASSERT_TRUE(wide_value.get()); + ASSERT_TRUE(wide_value->IsType(Value::TYPE_STRING)); + + // Test overloaded GetString. + std::string narrow = "http://google.com"; + std::wstring wide = L"http://google.com"; + ASSERT_TRUE(narrow_value->GetAsString(&narrow)); + ASSERT_TRUE(narrow_value->GetAsString(&wide)); + ASSERT_EQ(std::string("narrow"), narrow); + ASSERT_EQ(std::wstring(L"narrow"), wide); + ASSERT_TRUE(wide_value->GetAsString(&narrow)); + ASSERT_TRUE(wide_value->GetAsString(&wide)); + ASSERT_EQ(std::string("wide"), narrow); + ASSERT_EQ(std::wstring(L"wide"), wide); +} + +// This is a Value object that allows us to tell if it's been +// properly deleted by modifying the value of external flag on destruction. +class DeletionTestValue : public Value { +public: + DeletionTestValue(bool* deletion_flag) : Value(TYPE_NULL) { + Init(deletion_flag); // Separate function so that we can use ASSERT_* + } + + void Init(bool* deletion_flag) { + ASSERT_TRUE(deletion_flag); + deletion_flag_ = deletion_flag; + *deletion_flag_ = false; + } + + ~DeletionTestValue() { + *deletion_flag_ = true; + } + +private: + bool* deletion_flag_; +}; + +TEST(ValuesTest, ListDeletion) { + bool deletion_flag = true; + + { + ListValue list; + list.Append(new DeletionTestValue(&deletion_flag)); + EXPECT_FALSE(deletion_flag); + } + EXPECT_TRUE(deletion_flag); + + { + ListValue list; + list.Append(new DeletionTestValue(&deletion_flag)); + EXPECT_FALSE(deletion_flag); + list.Clear(); + EXPECT_TRUE(deletion_flag); + } + + { + ListValue list; + list.Append(new DeletionTestValue(&deletion_flag)); + EXPECT_FALSE(deletion_flag); + EXPECT_TRUE(list.Set(0, Value::CreateNullValue())); + EXPECT_TRUE(deletion_flag); + } +} + +TEST(ValuesTest, ListRemoval) { + bool deletion_flag = true; + Value* removed_item = NULL; + + { + ListValue list; + list.Append(new DeletionTestValue(&deletion_flag)); + EXPECT_FALSE(deletion_flag); + EXPECT_EQ(1U, list.GetSize()); + EXPECT_FALSE(list.Remove(std::numeric_limits::max(), + &removed_item)); + EXPECT_FALSE(list.Remove(1, &removed_item)); + EXPECT_TRUE(list.Remove(0, &removed_item)); + ASSERT_TRUE(removed_item); + EXPECT_EQ(0U, list.GetSize()); + } + EXPECT_FALSE(deletion_flag); + delete removed_item; + removed_item = NULL; + EXPECT_TRUE(deletion_flag); + + { + ListValue list; + list.Append(new DeletionTestValue(&deletion_flag)); + EXPECT_FALSE(deletion_flag); + EXPECT_TRUE(list.Remove(0, NULL)); + EXPECT_TRUE(deletion_flag); + EXPECT_EQ(0U, list.GetSize()); + } +} + +TEST(ValuesTest, DictionaryDeletion) { + std::wstring key = L"test"; + bool deletion_flag = true; + + { + DictionaryValue dict; + dict.Set(key, new DeletionTestValue(&deletion_flag)); + EXPECT_FALSE(deletion_flag); + } + EXPECT_TRUE(deletion_flag); + + { + DictionaryValue dict; + dict.Set(key, new DeletionTestValue(&deletion_flag)); + EXPECT_FALSE(deletion_flag); + dict.Clear(); + EXPECT_TRUE(deletion_flag); + } + + { + DictionaryValue dict; + dict.Set(key, new DeletionTestValue(&deletion_flag)); + EXPECT_FALSE(deletion_flag); + dict.Set(key, Value::CreateNullValue()); + EXPECT_TRUE(deletion_flag); + } +} + +TEST(ValuesTest, DictionaryRemoval) { + std::wstring key = L"test"; + bool deletion_flag = true; + Value* removed_item = NULL; + + { + DictionaryValue dict; + dict.Set(key, new DeletionTestValue(&deletion_flag)); + EXPECT_FALSE(deletion_flag); + EXPECT_TRUE(dict.HasKey(key)); + EXPECT_FALSE(dict.Remove(L"absent key", &removed_item)); + EXPECT_TRUE(dict.Remove(key, &removed_item)); + EXPECT_FALSE(dict.HasKey(key)); + ASSERT_TRUE(removed_item); + } + EXPECT_FALSE(deletion_flag); + delete removed_item; + removed_item = NULL; + EXPECT_TRUE(deletion_flag); + + { + DictionaryValue dict; + dict.Set(key, new DeletionTestValue(&deletion_flag)); + EXPECT_FALSE(deletion_flag); + EXPECT_TRUE(dict.HasKey(key)); + EXPECT_TRUE(dict.Remove(key, NULL)); + EXPECT_TRUE(deletion_flag); + EXPECT_FALSE(dict.HasKey(key)); + } +} + +TEST(ValuesTest, DeepCopy) { + DictionaryValue original_dict; + Value* original_null = Value::CreateNullValue(); + original_dict.Set(L"null", original_null); + Value* original_bool = Value::CreateBooleanValue(true); + original_dict.Set(L"bool", original_bool); + Value* original_int = Value::CreateIntegerValue(42); + original_dict.Set(L"int", original_int); + Value* original_real = Value::CreateRealValue(3.14); + original_dict.Set(L"real", original_real); + Value* original_string = Value::CreateStringValue("hello"); + original_dict.Set(L"string", original_string); + Value* original_wstring = Value::CreateStringValue(L"peek-a-boo"); + original_dict.Set(L"wstring", original_wstring); + + char* original_buffer = new char[42]; + memset(original_buffer, '!', 42); + BinaryValue* original_binary = Value::CreateBinaryValue(original_buffer, 42); + original_dict.Set(L"binary", original_binary); + + ListValue* original_list = new ListValue(); + Value* original_list_element_0 = Value::CreateIntegerValue(0); + original_list->Append(original_list_element_0); + Value* original_list_element_1 = Value::CreateIntegerValue(1); + original_list->Append(original_list_element_1); + original_dict.Set(L"list", original_list); + + DictionaryValue* copy_dict = + static_cast(original_dict.DeepCopy()); + ASSERT_TRUE(copy_dict); + ASSERT_NE(copy_dict, &original_dict); + + Value* copy_null = NULL; + ASSERT_TRUE(copy_dict->Get(L"null", ©_null)); + ASSERT_TRUE(copy_null); + ASSERT_NE(copy_null, original_null); + ASSERT_TRUE(copy_null->IsType(Value::TYPE_NULL)); + + Value* copy_bool = NULL; + ASSERT_TRUE(copy_dict->Get(L"bool", ©_bool)); + ASSERT_TRUE(copy_bool); + ASSERT_NE(copy_bool, original_bool); + ASSERT_TRUE(copy_bool->IsType(Value::TYPE_BOOLEAN)); + bool copy_bool_value = false; + ASSERT_TRUE(copy_bool->GetAsBoolean(©_bool_value)); + ASSERT_TRUE(copy_bool_value); + + Value* copy_int = NULL; + ASSERT_TRUE(copy_dict->Get(L"int", ©_int)); + ASSERT_TRUE(copy_int); + ASSERT_NE(copy_int, original_int); + ASSERT_TRUE(copy_int->IsType(Value::TYPE_INTEGER)); + int copy_int_value = 0; + ASSERT_TRUE(copy_int->GetAsInteger(©_int_value)); + ASSERT_EQ(42, copy_int_value); + + Value* copy_real = NULL; + ASSERT_TRUE(copy_dict->Get(L"real", ©_real)); + ASSERT_TRUE(copy_real); + ASSERT_NE(copy_real, original_real); + ASSERT_TRUE(copy_real->IsType(Value::TYPE_REAL)); + double copy_real_value = 0; + ASSERT_TRUE(copy_real->GetAsReal(©_real_value)); + ASSERT_EQ(3.14, copy_real_value); + + Value* copy_string = NULL; + ASSERT_TRUE(copy_dict->Get(L"string", ©_string)); + ASSERT_TRUE(copy_string); + ASSERT_NE(copy_string, original_string); + ASSERT_TRUE(copy_string->IsType(Value::TYPE_STRING)); + std::string copy_string_value; + std::wstring copy_wstring_value; + ASSERT_TRUE(copy_string->GetAsString(©_string_value)); + ASSERT_TRUE(copy_string->GetAsString(©_wstring_value)); + ASSERT_EQ(std::string("hello"), copy_string_value); + ASSERT_EQ(std::wstring(L"hello"), copy_wstring_value); + + Value* copy_wstring = NULL; + ASSERT_TRUE(copy_dict->Get(L"wstring", ©_wstring)); + ASSERT_TRUE(copy_wstring); + ASSERT_NE(copy_wstring, original_wstring); + ASSERT_TRUE(copy_wstring->IsType(Value::TYPE_STRING)); + ASSERT_TRUE(copy_wstring->GetAsString(©_string_value)); + ASSERT_TRUE(copy_wstring->GetAsString(©_wstring_value)); + ASSERT_EQ(std::string("peek-a-boo"), copy_string_value); + ASSERT_EQ(std::wstring(L"peek-a-boo"), copy_wstring_value); + + Value* copy_binary = NULL; + ASSERT_TRUE(copy_dict->Get(L"binary", ©_binary)); + ASSERT_TRUE(copy_binary); + ASSERT_NE(copy_binary, original_binary); + ASSERT_TRUE(copy_binary->IsType(Value::TYPE_BINARY)); + ASSERT_NE(original_binary->GetBuffer(), + static_cast(copy_binary)->GetBuffer()); + ASSERT_EQ(original_binary->GetSize(), + static_cast(copy_binary)->GetSize()); + ASSERT_EQ(0, memcmp(original_binary->GetBuffer(), + static_cast(copy_binary)->GetBuffer(), + original_binary->GetSize())); + + Value* copy_value = NULL; + ASSERT_TRUE(copy_dict->Get(L"list", ©_value)); + ASSERT_TRUE(copy_value); + ASSERT_NE(copy_value, original_list); + ASSERT_TRUE(copy_value->IsType(Value::TYPE_LIST)); + ListValue* copy_list = static_cast(copy_value); + ASSERT_EQ(2U, copy_list->GetSize()); + + Value* copy_list_element_0; + ASSERT_TRUE(copy_list->Get(0, ©_list_element_0)); + ASSERT_TRUE(copy_list_element_0); + ASSERT_NE(copy_list_element_0, original_list_element_0); + int copy_list_element_0_value; + ASSERT_TRUE(copy_list_element_0->GetAsInteger(©_list_element_0_value)); + ASSERT_EQ(0, copy_list_element_0_value); + + Value* copy_list_element_1; + ASSERT_TRUE(copy_list->Get(1, ©_list_element_1)); + ASSERT_TRUE(copy_list_element_1); + ASSERT_NE(copy_list_element_1, original_list_element_1); + int copy_list_element_1_value; + ASSERT_TRUE(copy_list_element_1->GetAsInteger(©_list_element_1_value)); + ASSERT_EQ(1, copy_list_element_1_value); + + delete copy_dict; +} + +TEST(ValuesTest, Equals) { + Value* null1 = Value::CreateNullValue(); + Value* null2 = Value::CreateNullValue(); + EXPECT_NE(null1, null2); + EXPECT_TRUE(null1->Equals(null2)); + + Value* boolean = Value::CreateBooleanValue(false); + EXPECT_FALSE(null1->Equals(boolean)); + delete null1; + delete null2; + delete boolean; + + DictionaryValue dv; + dv.SetBoolean(L"a", false); + dv.SetInteger(L"b", 2); + dv.SetReal(L"c", 2.5); + dv.SetString(L"d1", "string"); + dv.SetString(L"d2", L"string"); + dv.Set(L"e", Value::CreateNullValue()); + + DictionaryValue* copy = static_cast(dv.DeepCopy()); + EXPECT_TRUE(dv.Equals(copy)); + + ListValue* list = new ListValue; + list->Append(Value::CreateNullValue()); + list->Append(new DictionaryValue); + dv.Set(L"f", list); + + EXPECT_FALSE(dv.Equals(copy)); + copy->Set(L"f", list->DeepCopy()); + EXPECT_TRUE(dv.Equals(copy)); + + list->Append(Value::CreateBooleanValue(true)); + EXPECT_FALSE(dv.Equals(copy)); + delete copy; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/version.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/version.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/version.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/version.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,82 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/string_util.h" +#include "base/version.h" + +// static +Version* Version::GetVersionFromString(const std::wstring& version_str) { + if (!IsStringASCII(version_str)) + return NULL; + return GetVersionFromString(WideToASCII(version_str)); +} + +// static +Version* Version::GetVersionFromString(const std::string& version_str) { + Version* vers = new Version(); + if (vers->InitFromString(version_str)) + return vers; + delete vers; + return NULL; +} + +bool Version::Equals(const Version& that) const { + return CompareTo(that) == 0; +} + +int Version::CompareTo(const Version& other) const { + std::vector other_components = other.components(); + size_t count = std::min(components_.size(), other_components.size()); + for (size_t i = 0; i < count; ++i) { + if (components_[i] > other_components[i]) + return 1; + if (components_[i] < other_components[i]) + return -1; + } + if (components_.size() > other_components.size()) { + for (size_t i = count; i < components_.size(); ++i) + if (components_[i] > 0) + return 1; + } else if (components_.size() < other_components.size()) { + for (size_t i = count; i < other_components.size(); ++i) + if (other_components[i] > 0) + return -1; + } + return 0; +} + +const std::string Version::GetString() const { + std::string version_str; + int count = components_.size(); + for (int i = 0; i < count - 1; ++i) { + version_str.append(IntToString(components_[i])); + version_str.append("."); + } + version_str.append(IntToString(components_[count - 1])); + return version_str; +} + +bool Version::InitFromString(const std::string& version_str) { + std::vector numbers; + SplitString(version_str, '.', &numbers); + for (std::vector::iterator i = numbers.begin(); + i != numbers.end(); ++i) { + int num; + if (!StringToInt(*i, &num)) + return false; + if (num < 0) + return false; + const uint16 max = 0xFFFF; + if (num > max) + return false; + // This throws out things like +3, or 032. + if (IntToString(num) != *i) + return false; + uint16 component = static_cast(num); + components_.push_back(component); + } + return true; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/version.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/version.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/version.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/version.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,40 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_VERSION_H_ +#define BASE_VERSION_H_ + +#include +#include + +#include "base/basictypes.h" + +class Version { +public: + // The version string must be made up of 1 or more uint16's separated + // by '.'. Returns NULL if string is not in this format. + // Caller is responsible for freeing the Version object once done. + static Version* GetVersionFromString(const std::wstring& version_str); + static Version* GetVersionFromString(const std::string& version_str); + + ~Version() {} + + bool Equals(const Version& other) const; + + // Returns -1, 0, 1 for <, ==, >. + int CompareTo(const Version& other) const; + + // Return the string representation of this version. + const std::string GetString() const; + + const std::vector& components() const { return components_; } + +private: + Version() {} + bool InitFromString(const std::string& version_str); + + std::vector components_; +}; + +#endif // BASE_VERSION_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/version_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/version_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/version_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/version_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,62 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/scoped_ptr.h" +#include "base/version.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +TEST(Version, GetVersionFromString) { + static const struct version_string { + const char* input; + size_t parts; + bool success; + } cases[] = { + {"0", 1, true}, + {"0.0", 2, true}, + {"65537.0", 0, false}, + {"-1.0", 0, false}, + {"1.-1.0", 0, false}, + {"+1.0", 0, false}, + {"1.+1.0", 0, false}, + {"1.0a", 0, false}, + {"1.2.3.4.5.6.7.8.9.0", 10, true}, + {"02.1", 0, false}, + {"f.1", 0, false}, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + scoped_ptr vers(Version::GetVersionFromString(cases[i].input)); + EXPECT_EQ(cases[i].success, vers.get() != NULL); + if (cases[i].success) + EXPECT_EQ(cases[i].parts, vers->components().size()); + } +} + +TEST(Version, Compare) { + static const struct version_compare { + const char* lhs; + const char* rhs; + int expected; + } cases[] = { + {"1.0", "1.0", 0}, + {"1.0", "0.0", 1}, + {"1.0", "2.0", -1}, + {"1.0", "1.1", -1}, + {"1.1", "1.0", 1}, + {"1.0", "1.0.1", -1}, + {"1.1", "1.0.1", 1}, + {"1.1", "1.0.1", 1}, + {"1.0.0", "1.0", 0}, + {"1.0.3", "1.0.20", -1}, + }; + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); ++i) { + scoped_ptr lhs(Version::GetVersionFromString(cases[i].lhs)); + scoped_ptr rhs(Version::GetVersionFromString(cases[i].rhs)); + EXPECT_EQ(lhs->CompareTo(*rhs), cases[i].expected) << + cases[i].lhs << " ? " << cases[i].rhs; + } +} + +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/waitable_event.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/waitable_event.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/waitable_event.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/waitable_event.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,180 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_WAITABLE_EVENT_H_ +#define BASE_WAITABLE_EVENT_H_ + +#include "base/basictypes.h" + +#if defined(OS_WIN) +#include +#endif + +#if defined(OS_POSIX) +#include +#include +#include "base/condition_variable.h" +#include "base/lock.h" +#include "base/ref_counted.h" +#endif + +#include "base/message_loop.h" + +namespace base { + +// This replaces INFINITE from Win32 +static const int kNoTimeout = -1; + +class TimeDelta; + +// A WaitableEvent can be a useful thread synchronization tool when you want to +// allow one thread to wait for another thread to finish some work. For +// non-Windows systems, this can only be used from within a single address +// space. +// +// Use a WaitableEvent when you would otherwise use a Lock+ConditionVariable to +// protect a simple boolean value. However, if you find yourself using a +// WaitableEvent in conjunction with a Lock to wait for a more complex state +// change (e.g., for an item to be added to a queue), then you should probably +// be using a ConditionVariable instead of a WaitableEvent. +// +// NOTE: On Windows, this class provides a subset of the functionality afforded +// by a Windows event object. This is intentional. If you are writing Windows +// specific code and you need other features of a Windows event, then you might +// be better off just using an Windows event directly. +class WaitableEvent { + public: + // If manual_reset is true, then to set the event state to non-signaled, a + // consumer must call the Reset method. If this parameter is false, then the + // system automatically resets the event state to non-signaled after a single + // waiting thread has been released. + WaitableEvent(bool manual_reset, bool initially_signaled); + +#if defined(OS_WIN) + // Create a WaitableEvent from an Event HANDLE which has already been + // created. This objects takes ownership of the HANDLE and will close it when + // deleted. + explicit WaitableEvent(HANDLE event_handle); + + // Releases ownership of the handle from this object. + HANDLE Release(); +#endif + + ~WaitableEvent(); + + // Put the event in the un-signaled state. + void Reset(); + + // Put the event in the signaled state. Causing any thread blocked on Wait + // to be woken up. + void Signal(); + + // Returns true if the event is in the signaled state, else false. If this + // is not a manual reset event, then this test will cause a reset. + bool IsSignaled(); + + // Wait indefinitely for the event to be signaled. Returns true if the event + // was signaled, else false is returned to indicate that waiting failed. + bool Wait(); + + // Wait up until max_time has passed for the event to be signaled. Returns + // true if the event was signaled. If this method returns false, then it + // does not necessarily mean that max_time was exceeded. + bool TimedWait(const TimeDelta& max_time); + +#if defined(OS_WIN) + HANDLE handle() const { return handle_; } +#endif + + // Wait, synchronously, on multiple events. + // waitables: an array of WaitableEvent pointers + // count: the number of elements in @waitables + // + // returns: the index of a WaitableEvent which has been signaled. + // + // You MUST NOT delete any of the WaitableEvent objects while this wait is + // happening. + static size_t WaitMany(WaitableEvent** waitables, size_t count); + + // For asynchronous waiting, see WaitableEventWatcher + + // This is a private helper class. It's here because it's used by friends of + // this class (such as WaitableEventWatcher) to be able to enqueue elements + // of the wait-list + class Waiter { + public: + // Signal the waiter to wake up. + // + // Consider the case of a Waiter which is in multiple WaitableEvent's + // wait-lists. Each WaitableEvent is automatic-reset and two of them are + // signaled at the same time. Now, each will wake only the first waiter in + // the wake-list before resetting. However, if those two waiters happen to + // be the same object (as can happen if another thread didn't have a chance + // to dequeue the waiter from the other wait-list in time), two auto-resets + // will have happened, but only one waiter has been signaled! + // + // Because of this, a Waiter may "reject" a wake by returning false. In + // this case, the auto-reset WaitableEvent shouldn't act as if anything has + // been notified. + virtual bool Fire(WaitableEvent* signaling_event) = 0; + + // Waiters may implement this in order to provide an extra condition for + // two Waiters to be considered equal. In WaitableEvent::Dequeue, if the + // pointers match then this function is called as a final check. See the + // comments in ~Handle for why. + virtual bool Compare(void* tag) = 0; + }; + + private: + friend class WaitableEventWatcher; + +#if defined(OS_WIN) + HANDLE handle_; +#else + // On Windows, one can close a HANDLE which is currently being waited on. The + // MSDN documentation says that the resulting behaviour is 'undefined', but + // it doesn't crash. However, if we were to include the following members + // directly then, on POSIX, one couldn't use WaitableEventWatcher to watch an + // event which gets deleted. This mismatch has bitten us several times now, + // so we have a kernel of the WaitableEvent, which is reference counted. + // WaitableEventWatchers may then take a reference and thus match the Windows + // behaviour. + struct WaitableEventKernel : + public RefCountedThreadSafe { + public: + WaitableEventKernel(bool manual_reset, bool initially_signaled) + : manual_reset_(manual_reset), + signaled_(initially_signaled) { + } + + bool Dequeue(Waiter* waiter, void* tag); + + Lock lock_; + const bool manual_reset_; + bool signaled_; + std::list waiters_; + }; + + scoped_refptr kernel_; + + bool SignalAll(); + bool SignalOne(); + void Enqueue(Waiter* waiter); + + // When dealing with arrays of WaitableEvent*, we want to sort by the address + // of the WaitableEvent in order to have a globally consistent locking order. + // In that case we keep them, in sorted order, in an array of pairs where the + // second element is the index of the WaitableEvent in the original, + // unsorted, array. + typedef std::pair WaiterAndIndex; + static size_t EnqueueMany(WaiterAndIndex* waitables, + size_t count, Waiter* waiter); +#endif + + DISALLOW_COPY_AND_ASSIGN(WaitableEvent); +}; + +} // namespace base + +#endif // BASE_WAITABLE_EVENT_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_posix.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,390 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/waitable_event.h" + +#include "base/condition_variable.h" +#include "base/lock.h" +#include "base/message_loop.h" + +// ----------------------------------------------------------------------------- +// A WaitableEvent on POSIX is implemented as a wait-list. Currently we don't +// support cross-process events (where one process can signal an event which +// others are waiting on). Because of this, we can avoid having one thread per +// listener in several cases. +// +// The WaitableEvent maintains a list of waiters, protected by a lock. Each +// waiter is either an async wait, in which case we have a Task and the +// MessageLoop to run it on, or a blocking wait, in which case we have the +// condition variable to signal. +// +// Waiting involves grabbing the lock and adding oneself to the wait list. Async +// waits can be canceled, which means grabbing the lock and removing oneself +// from the list. +// +// Waiting on multiple events is handled by adding a single, synchronous wait to +// the wait-list of many events. An event passes a pointer to itself when +// firing a waiter and so we can store that pointer to find out which event +// triggered. +// ----------------------------------------------------------------------------- + +namespace base { + +// ----------------------------------------------------------------------------- +// This is just an abstract base class for waking the two types of waiters +// ----------------------------------------------------------------------------- +WaitableEvent::WaitableEvent(bool manual_reset, bool initially_signaled) + : kernel_(new WaitableEventKernel(manual_reset, initially_signaled)) { +} + +WaitableEvent::~WaitableEvent() { +} + +void WaitableEvent::Reset() { + AutoLock locked(kernel_->lock_); + kernel_->signaled_ = false; +} + +void WaitableEvent::Signal() { + AutoLock locked(kernel_->lock_); + + if (kernel_->signaled_) + return; + + if (kernel_->manual_reset_) { + SignalAll(); + kernel_->signaled_ = true; + } else { + // In the case of auto reset, if no waiters were woken, we remain + // signaled. + if (!SignalOne()) + kernel_->signaled_ = true; + } +} + +bool WaitableEvent::IsSignaled() { + AutoLock locked(kernel_->lock_); + + const bool result = kernel_->signaled_; + if (result && !kernel_->manual_reset_) + kernel_->signaled_ = false; + return result; +} + +// ----------------------------------------------------------------------------- +// Synchronous waits + +// ----------------------------------------------------------------------------- +// This is an synchronous waiter. The thread is waiting on the given condition +// variable and the fired flag in this object. +// ----------------------------------------------------------------------------- +class SyncWaiter : public WaitableEvent::Waiter { + public: + SyncWaiter(ConditionVariable* cv, Lock* lock) + : fired_(false), + cv_(cv), + lock_(lock), + signaling_event_(NULL) { + } + + bool Fire(WaitableEvent *signaling_event) { + lock_->Acquire(); + const bool previous_value = fired_; + fired_ = true; + if (!previous_value) + signaling_event_ = signaling_event; + lock_->Release(); + + if (previous_value) + return false; + + cv_->Broadcast(); + + // SyncWaiters are stack allocated on the stack of the blocking thread. + return true; + } + + WaitableEvent* signaled_event() const { + return signaling_event_; + } + + // --------------------------------------------------------------------------- + // These waiters are always stack allocated and don't delete themselves. Thus + // there's no problem and the ABA tag is the same as the object pointer. + // --------------------------------------------------------------------------- + bool Compare(void* tag) { + return this == tag; + } + + // --------------------------------------------------------------------------- + // Called with lock held. + // --------------------------------------------------------------------------- + bool fired() const { + return fired_; + } + + // --------------------------------------------------------------------------- + // During a TimedWait, we need a way to make sure that an auto-reset + // WaitableEvent doesn't think that this event has been signaled between + // unlocking it and removing it from the wait-list. Called with lock held. + // --------------------------------------------------------------------------- + void Disable() { + fired_ = true; + } + + private: + bool fired_; + ConditionVariable *const cv_; + Lock *const lock_; + WaitableEvent* signaling_event_; // The WaitableEvent which woke us +}; + +bool WaitableEvent::TimedWait(const TimeDelta& max_time) { + const Time end_time(Time::Now() + max_time); + const bool finite_time = max_time.ToInternalValue() >= 0; + + kernel_->lock_.Acquire(); + if (kernel_->signaled_) { + if (!kernel_->manual_reset_) { + // In this case we were signaled when we had no waiters. Now that + // someone has waited upon us, we can automatically reset. + kernel_->signaled_ = false; + } + + kernel_->lock_.Release(); + return true; + } + + Lock lock; + lock.Acquire(); + ConditionVariable cv(&lock); + SyncWaiter sw(&cv, &lock); + + Enqueue(&sw); + kernel_->lock_.Release(); + // We are violating locking order here by holding the SyncWaiter lock but not + // the WaitableEvent lock. However, this is safe because we don't lock @lock_ + // again before unlocking it. + + for (;;) { + const Time current_time(Time::Now()); + + if (sw.fired() || (finite_time && current_time >= end_time)) { + const bool return_value = sw.fired(); + + // We can't acquire @lock_ before releasing @lock (because of locking + // order), however, inbetween the two a signal could be fired and @sw + // would accept it, however we will still return false, so the signal + // would be lost on an auto-reset WaitableEvent. Thus we call Disable + // which makes sw::Fire return false. + sw.Disable(); + lock.Release(); + + kernel_->lock_.Acquire(); + kernel_->Dequeue(&sw, &sw); + kernel_->lock_.Release(); + + return return_value; + } + + if (finite_time) { + const TimeDelta max_wait(end_time - current_time); + cv.TimedWait(max_wait); + } else { + cv.Wait(); + } + } +} + +bool WaitableEvent::Wait() { + return TimedWait(TimeDelta::FromSeconds(-1)); +} + +// ----------------------------------------------------------------------------- + + +// ----------------------------------------------------------------------------- +// Synchronous waiting on multiple objects. + +static bool // StrictWeakOrdering +cmp_fst_addr(const std::pair &a, + const std::pair &b) { + return a.first < b.first; +} + +// static +size_t WaitableEvent::WaitMany(WaitableEvent** raw_waitables, + size_t count) { + DCHECK(count) << "Cannot wait on no events"; + + // We need to acquire the locks in a globally consistent order. Thus we sort + // the array of waitables by address. We actually sort a pairs so that we can + // map back to the original index values later. + std::vector > waitables; + waitables.reserve(count); + for (size_t i = 0; i < count; ++i) + waitables.push_back(std::make_pair(raw_waitables[i], i)); + + DCHECK_EQ(count, waitables.size()); + + sort(waitables.begin(), waitables.end(), cmp_fst_addr); + + // The set of waitables must be distinct. Since we have just sorted by + // address, we can check this cheaply by comparing pairs of consecutive + // elements. + for (size_t i = 0; i < waitables.size() - 1; ++i) { + DCHECK(waitables[i].first != waitables[i+1].first); + } + + Lock lock; + ConditionVariable cv(&lock); + SyncWaiter sw(&cv, &lock); + + const size_t r = EnqueueMany(&waitables[0], count, &sw); + if (r) { + // One of the events is already signaled. The SyncWaiter has not been + // enqueued anywhere. EnqueueMany returns the count of remaining waitables + // when the signaled one was seen, so the index of the signaled event is + // @count - @r. + return waitables[count - r].second; + } + + // At this point, we hold the locks on all the WaitableEvents and we have + // enqueued our waiter in them all. + lock.Acquire(); + // Release the WaitableEvent locks in the reverse order + for (size_t i = 0; i < count; ++i) { + waitables[count - (1 + i)].first->kernel_->lock_.Release(); + } + + for (;;) { + if (sw.fired()) + break; + + cv.Wait(); + } + lock.Release(); + + // The address of the WaitableEvent which fired is stored in the SyncWaiter. + WaitableEvent *const signaled_event = sw.signaled_event(); + // This will store the index of the raw_waitables which fired. + size_t signaled_index = 0; + + // Take the locks of each WaitableEvent in turn (except the signaled one) and + // remove our SyncWaiter from the wait-list + for (size_t i = 0; i < count; ++i) { + if (raw_waitables[i] != signaled_event) { + raw_waitables[i]->kernel_->lock_.Acquire(); + // There's no possible ABA issue with the address of the SyncWaiter here + // because it lives on the stack. Thus the tag value is just the pointer + // value again. + raw_waitables[i]->kernel_->Dequeue(&sw, &sw); + raw_waitables[i]->kernel_->lock_.Release(); + } else { + signaled_index = i; + } + } + + return signaled_index; +} + +// ----------------------------------------------------------------------------- +// If return value == 0: +// The locks of the WaitableEvents have been taken in order and the Waiter has +// been enqueued in the wait-list of each. None of the WaitableEvents are +// currently signaled +// else: +// None of the WaitableEvent locks are held. The Waiter has not been enqueued +// in any of them and the return value is the index of the first WaitableEvent +// which was signaled, from the end of the array. +// ----------------------------------------------------------------------------- +// static +size_t WaitableEvent::EnqueueMany + (std::pair* waitables, + size_t count, Waiter* waiter) { + if (!count) + return 0; + + waitables[0].first->kernel_->lock_.Acquire(); + if (waitables[0].first->kernel_->signaled_) { + if (!waitables[0].first->kernel_->manual_reset_) + waitables[0].first->kernel_->signaled_ = false; + waitables[0].first->kernel_->lock_.Release(); + return count; + } + + const size_t r = EnqueueMany(waitables + 1, count - 1, waiter); + if (r) { + waitables[0].first->kernel_->lock_.Release(); + } else { + waitables[0].first->Enqueue(waiter); + } + + return r; +} + +// ----------------------------------------------------------------------------- + + +// ----------------------------------------------------------------------------- +// Private functions... + +// ----------------------------------------------------------------------------- +// Wake all waiting waiters. Called with lock held. +// ----------------------------------------------------------------------------- +bool WaitableEvent::SignalAll() { + bool signaled_at_least_one = false; + + for (std::list::iterator + i = kernel_->waiters_.begin(); i != kernel_->waiters_.end(); ++i) { + if ((*i)->Fire(this)) + signaled_at_least_one = true; + } + + kernel_->waiters_.clear(); + return signaled_at_least_one; +} + +// --------------------------------------------------------------------------- +// Try to wake a single waiter. Return true if one was woken. Called with lock +// held. +// --------------------------------------------------------------------------- +bool WaitableEvent::SignalOne() { + for (;;) { + if (kernel_->waiters_.empty()) + return false; + + const bool r = (*kernel_->waiters_.begin())->Fire(this); + kernel_->waiters_.pop_front(); + if (r) + return true; + } +} + +// ----------------------------------------------------------------------------- +// Add a waiter to the list of those waiting. Called with lock held. +// ----------------------------------------------------------------------------- +void WaitableEvent::Enqueue(Waiter* waiter) { + kernel_->waiters_.push_back(waiter); +} + +// ----------------------------------------------------------------------------- +// Remove a waiter from the list of those waiting. Return true if the waiter was +// actually removed. Called with lock held. +// ----------------------------------------------------------------------------- +bool WaitableEvent::WaitableEventKernel::Dequeue(Waiter* waiter, void* tag) { + for (std::list::iterator + i = waiters_.begin(); i != waiters_.end(); ++i) { + if (*i == waiter && (*i)->Compare(tag)) { + waiters_.erase(i); + return true; + } + } + + return false; +} + +// ----------------------------------------------------------------------------- + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,109 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/time.h" +#include "base/waitable_event.h" +#include "base/platform_thread.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::TimeDelta; +using base::WaitableEvent; + +namespace { +typedef testing::Test WaitableEventTest; +} + +TEST(WaitableEventTest, ManualBasics) { + WaitableEvent event(true, false); + + EXPECT_FALSE(event.IsSignaled()); + + event.Signal(); + EXPECT_TRUE(event.IsSignaled()); + EXPECT_TRUE(event.IsSignaled()); + + event.Reset(); + EXPECT_FALSE(event.IsSignaled()); + EXPECT_FALSE(event.TimedWait(TimeDelta::FromMilliseconds(10))); + + event.Signal(); + EXPECT_TRUE(event.Wait()); + EXPECT_TRUE(event.TimedWait(TimeDelta::FromMilliseconds(10))); +} + +TEST(WaitableEventTest, AutoBasics) { + WaitableEvent event(false, false); + + EXPECT_FALSE(event.IsSignaled()); + + event.Signal(); + EXPECT_TRUE(event.IsSignaled()); + EXPECT_FALSE(event.IsSignaled()); + + event.Reset(); + EXPECT_FALSE(event.IsSignaled()); + EXPECT_FALSE(event.TimedWait(TimeDelta::FromMilliseconds(10))); + + event.Signal(); + EXPECT_TRUE(event.Wait()); + EXPECT_FALSE(event.TimedWait(TimeDelta::FromMilliseconds(10))); + + event.Signal(); + EXPECT_TRUE(event.TimedWait(TimeDelta::FromMilliseconds(10))); +} + +TEST(WaitableEventTest, WaitManyShortcut) { + WaitableEvent* ev[5]; + for (unsigned i = 0; i < 5; ++i) + ev[i] = new WaitableEvent(false, false); + + ev[3]->Signal(); + EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 3u); + + ev[3]->Signal(); + EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 3u); + + ev[4]->Signal(); + EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 4u); + + ev[0]->Signal(); + EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 0u); + + for (unsigned i = 0; i < 5; ++i) + delete ev[i]; +} + +class WaitableEventSignaler : public PlatformThread::Delegate { + public: + WaitableEventSignaler(double seconds, WaitableEvent* ev) + : seconds_(seconds), + ev_(ev) { + } + + void ThreadMain() { + PlatformThread::Sleep(static_cast(seconds_ * 1000)); + ev_->Signal(); + } + + private: + const double seconds_; + WaitableEvent *const ev_; +}; + +TEST(WaitableEventTest, WaitMany) { + WaitableEvent* ev[5]; + for (unsigned i = 0; i < 5; ++i) + ev[i] = new WaitableEvent(false, false); + + WaitableEventSignaler signaler(0.1, ev[2]); + PlatformThreadHandle thread; + PlatformThread::Create(0, &signaler, &thread); + + EXPECT_EQ(WaitableEvent::WaitMany(ev, 5), 2u); + + PlatformThread::Join(thread); + + for (unsigned i = 0; i < 5; ++i) + delete ev[i]; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_watcher.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_watcher.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_watcher.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_watcher.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,153 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_WAITABLE_EVENT_WATCHER_H_ +#define BASE_WAITABLE_EVENT_WATCHER_H_ + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include "base/object_watcher.h" +#else +#include "base/message_loop.h" +#include "base/waitable_event.h" +#endif + +namespace base { + +class Flag; +class AsyncWaiter; +class AsyncCallbackTask; +class WaitableEvent; + +// ----------------------------------------------------------------------------- +// This class provides a way to wait on a WaitableEvent asynchronously. +// +// Each instance of this object can be waiting on a single WaitableEvent. When +// the waitable event is signaled, a callback is made in the thread of a given +// MessageLoop. This callback can be deleted by deleting the waiter. +// +// Typical usage: +// +// class MyClass : public base::WaitableEventWatcher::Delegate { +// public: +// void DoStuffWhenSignaled(WaitableEvent *waitable_event) { +// watcher_.StartWatching(waitable_event, this); +// } +// virtual void OnWaitableEventSignaled(WaitableEvent* waitable_event) { +// // OK, time to do stuff! +// } +// private: +// base::WaitableEventWatcher watcher_; +// }; +// +// In the above example, MyClass wants to "do stuff" when waitable_event +// becomes signaled. WaitableEventWatcher makes this task easy. When MyClass +// goes out of scope, the watcher_ will be destroyed, and there is no need to +// worry about OnWaitableEventSignaled being called on a deleted MyClass +// pointer. +// +// BEWARE: With automatically reset WaitableEvents, a signal may be lost if it +// occurs just before a WaitableEventWatcher is deleted. There is currently no +// safe way to stop watching an automatic reset WaitableEvent without possibly +// missing a signal. +// +// NOTE: you /are/ allowed to delete the WaitableEvent while still waiting on +// it with a Watcher. It will act as if the event was never signaled. +// ----------------------------------------------------------------------------- + +class WaitableEventWatcher +#if defined(OS_POSIX) + : public MessageLoop::DestructionObserver +#endif +{ + public: + + WaitableEventWatcher(); + ~WaitableEventWatcher(); + + class Delegate { + public: + virtual ~Delegate() { } + + // ------------------------------------------------------------------------- + // This is called on the MessageLoop thread when WaitableEvent has been + // signaled. + // + // Note: the event may not be signaled by the time that this function is + // called. This indicates only that it has been signaled at some point in + // the past. + // ------------------------------------------------------------------------- + virtual void OnWaitableEventSignaled(WaitableEvent* waitable_event) = 0; + }; + + // --------------------------------------------------------------------------- + // When @event is signaled, the given delegate is called on the thread of the + // current message loop when StartWatching is called. The delegate is not + // deleted. + // --------------------------------------------------------------------------- + bool StartWatching(WaitableEvent* event, Delegate* delegate); + + // --------------------------------------------------------------------------- + // Cancel the current watch. Must be called from the same thread which + // started the watch. + // + // Does nothing if no event is being watched, nor if the watch has completed. + // The delegate will *not* be called for the current watch after this + // function returns. Since the delegate runs on the same thread as this + // function, it cannot be called during this function either. + // --------------------------------------------------------------------------- + void StopWatching(); + + // --------------------------------------------------------------------------- + // Return the currently watched event, or NULL if no object is currently being + // watched. + // --------------------------------------------------------------------------- + WaitableEvent* GetWatchedEvent(); + + private: + WaitableEvent* event_; + +#if defined(OS_WIN) + // --------------------------------------------------------------------------- + // The helper class exists because, if WaitableEventWatcher were to inherit + // from ObjectWatcher::Delegate, then it couldn't also have an inner class + // called Delegate (at least on Windows). Thus this object exists to proxy + // the callback function + // --------------------------------------------------------------------------- + class ObjectWatcherHelper : public ObjectWatcher::Delegate { + public: + ObjectWatcherHelper(WaitableEventWatcher* watcher); + + // ------------------------------------------------------------------------- + // Implementation of ObjectWatcher::Delegate + // ------------------------------------------------------------------------- + void OnObjectSignaled(HANDLE h); + + private: + WaitableEventWatcher *const watcher_; + }; + + void OnObjectSignaled(); + + Delegate* delegate_; + ObjectWatcherHelper helper_; + ObjectWatcher watcher_; +#else + // --------------------------------------------------------------------------- + // Implementation of MessageLoop::DestructionObserver + // --------------------------------------------------------------------------- + void WillDestroyCurrentMessageLoop(); + + MessageLoop* message_loop_; + scoped_refptr cancel_flag_; + AsyncWaiter* waiter_; + AsyncCallbackTask* callback_task_; + scoped_refptr kernel_; +#endif +}; + +} // namespace base + +#endif // BASE_WAITABLE_EVENT_WATCHER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_watcher_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_watcher_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_watcher_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_watcher_posix.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,275 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/waitable_event_watcher.h" + +#include "base/condition_variable.h" +#include "base/lock.h" +#include "base/message_loop.h" +#include "base/waitable_event.h" + +namespace base { + +// ----------------------------------------------------------------------------- +// WaitableEventWatcher (async waits). +// +// The basic design is that we add an AsyncWaiter to the wait-list of the event. +// That AsyncWaiter has a pointer to MessageLoop, and a Task to be posted to it. +// The MessageLoop ends up running the task, which calls the delegate. +// +// Since the wait can be canceled, we have a thread-safe Flag object which is +// set when the wait has been canceled. At each stage in the above, we check the +// flag before going onto the next stage. Since the wait may only be canceled in +// the MessageLoop which runs the Task, we are assured that the delegate cannot +// be called after canceling... + +// ----------------------------------------------------------------------------- +// A thread-safe, reference-counted, write-once flag. +// ----------------------------------------------------------------------------- +class Flag : public RefCountedThreadSafe { + public: + Flag() { flag_ = false; } + + void Set() { + AutoLock locked(lock_); + flag_ = true; + } + + bool value() const { + AutoLock locked(lock_); + return flag_; + } + + private: + mutable Lock lock_; + bool flag_; +}; + +// ----------------------------------------------------------------------------- +// This is an asynchronous waiter which posts a task to a MessageLoop when +// fired. An AsyncWaiter may only be in a single wait-list. +// ----------------------------------------------------------------------------- +class AsyncWaiter : public WaitableEvent::Waiter { + public: + AsyncWaiter(MessageLoop* message_loop, Task* task, Flag* flag) + : message_loop_(message_loop), + cb_task_(task), + flag_(flag) { } + + bool Fire(WaitableEvent* event) { + if (flag_->value()) { + // If the callback has been canceled, we don't enqueue the task, we just + // delete it instead. + delete cb_task_; + } else { + message_loop_->PostTask(FROM_HERE, cb_task_); + } + + // We are removed from the wait-list by the WaitableEvent itself. It only + // remains to delete ourselves. + delete this; + + // We can always return true because an AsyncWaiter is never in two + // different wait-lists at the same time. + return true; + } + + // See StopWatching for discussion + bool Compare(void* tag) { + return tag == flag_.get(); + } + + private: + MessageLoop *const message_loop_; + Task *const cb_task_; + scoped_refptr flag_; +}; + +// ----------------------------------------------------------------------------- +// For async waits we need to make a callback in a MessageLoop thread. We do +// this by posting this task, which calls the delegate and keeps track of when +// the event is canceled. +// ----------------------------------------------------------------------------- +class AsyncCallbackTask : public Task { + public: + AsyncCallbackTask(Flag* flag, WaitableEventWatcher::Delegate* delegate, + WaitableEvent* event) + : flag_(flag), + delegate_(delegate), + event_(event) { + } + + void Run() { + // Runs in MessageLoop thread. + if (!flag_->value()) { + // This is to let the WaitableEventWatcher know that the event has occured + // because it needs to be able to return NULL from GetWatchedObject + flag_->Set(); + delegate_->OnWaitableEventSignaled(event_); + } + + // We are deleted by the MessageLoop + } + + private: + scoped_refptr flag_; + WaitableEventWatcher::Delegate *const delegate_; + WaitableEvent *const event_; +}; + +WaitableEventWatcher::WaitableEventWatcher() + : event_(NULL), + message_loop_(NULL), + cancel_flag_(NULL), + callback_task_(NULL) { +} + +WaitableEventWatcher::~WaitableEventWatcher() { + StopWatching(); +} + +// ----------------------------------------------------------------------------- +// The Handle is how the user cancels a wait. After deleting the Handle we +// insure that the delegate cannot be called. +// ----------------------------------------------------------------------------- +bool WaitableEventWatcher::StartWatching + (WaitableEvent* event, WaitableEventWatcher::Delegate* delegate) { + MessageLoop *const current_ml = MessageLoop::current(); + DCHECK(current_ml) << "Cannot create WaitableEventWatcher without a " + "current MessageLoop"; + + // A user may call StartWatching from within the callback function. In this + // case, we won't know that we have finished watching, expect that the Flag + // will have been set in AsyncCallbackTask::Run() + if (cancel_flag_.get() && cancel_flag_->value()) { + if (message_loop_) { + message_loop_->RemoveDestructionObserver(this); + message_loop_ = NULL; + } + + cancel_flag_ = NULL; + } + + DCHECK(!cancel_flag_.get()) << "StartWatching called while still watching"; + + cancel_flag_ = new Flag; + callback_task_ = new AsyncCallbackTask(cancel_flag_, delegate, event); + WaitableEvent::WaitableEventKernel* kernel = event->kernel_.get(); + + AutoLock locked(kernel->lock_); + + if (kernel->signaled_) { + if (!kernel->manual_reset_) + kernel->signaled_ = false; + + // No hairpinning - we can't call the delegate directly here. We have to + // enqueue a task on the MessageLoop as normal. + current_ml->PostTask(FROM_HERE, callback_task_); + return true; + } + + message_loop_ = current_ml; + current_ml->AddDestructionObserver(this); + + event_ = event; + kernel_ = kernel; + waiter_ = new AsyncWaiter(current_ml, callback_task_, cancel_flag_); + event->Enqueue(waiter_); + + return true; +} + +void WaitableEventWatcher::StopWatching() { + if (message_loop_) { + message_loop_->RemoveDestructionObserver(this); + message_loop_ = NULL; + } + + if (!cancel_flag_.get()) // if not currently watching... + return; + + if (cancel_flag_->value()) { + // In this case, the event has fired, but we haven't figured that out yet. + // The WaitableEvent may have been deleted too. + cancel_flag_ = NULL; + return; + } + + if (!kernel_.get()) { + // We have no kernel. This means that we never enqueued a Waiter on an + // event because the event was already signaled when StartWatching was + // called. + // + // In this case, a task was enqueued on the MessageLoop and will run. + // We set the flag in case the task hasn't yet run. The flag will stop the + // delegate getting called. If the task has run then we have the last + // reference to the flag and it will be deleted immedately after. + cancel_flag_->Set(); + cancel_flag_ = NULL; + return; + } + + AutoLock locked(kernel_->lock_); + // We have a lock on the kernel. No one else can signal the event while we + // have it. + + // We have a possible ABA issue here. If Dequeue was to compare only the + // pointer values then it's possible that the AsyncWaiter could have been + // fired, freed and the memory reused for a different Waiter which was + // enqueued in the same wait-list. We would think that that waiter was our + // AsyncWaiter and remove it. + // + // To stop this, Dequeue also takes a tag argument which is passed to the + // virtual Compare function before the two are considered a match. So we need + // a tag which is good for the lifetime of this handle: the Flag. Since we + // have a reference to the Flag, its memory cannot be reused while this object + // still exists. So if we find a waiter with the correct pointer value, and + // which shares a Flag pointer, we have a real match. + if (kernel_->Dequeue(waiter_, cancel_flag_.get())) { + // Case 2: the waiter hasn't been signaled yet; it was still on the wait + // list. We've removed it, thus we can delete it and the task (which cannot + // have been enqueued with the MessageLoop because the waiter was never + // signaled) + delete waiter_; + delete callback_task_; + cancel_flag_ = NULL; + return; + } + + // Case 3: the waiter isn't on the wait-list, thus it was signaled. It may + // not have run yet, so we set the flag to tell it not to bother enqueuing the + // task on the MessageLoop, but to delete it instead. The Waiter deletes + // itself once run. + cancel_flag_->Set(); + cancel_flag_ = NULL; + + // If the waiter has already run then the task has been enqueued. If the Task + // hasn't yet run, the flag will stop the delegate from getting called. (This + // is thread safe because one may only delete a Handle from the MessageLoop + // thread.) + // + // If the delegate has already been called then we have nothing to do. The + // task has been deleted by the MessageLoop. +} + +WaitableEvent* WaitableEventWatcher::GetWatchedEvent() { + if (!cancel_flag_.get()) + return NULL; + + if (cancel_flag_->value()) + return NULL; + + return event_; +} + +// ----------------------------------------------------------------------------- +// This is called when the MessageLoop which the callback will be run it is +// deleted. We need to cancel the callback as if we had been deleted, but we +// will still be deleted at some point in the future. +// ----------------------------------------------------------------------------- +void WaitableEventWatcher::WillDestroyCurrentMessageLoop() { + StopWatching(); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_watcher_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_watcher_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_watcher_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_watcher_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,158 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/message_loop.h" +#include "base/platform_thread.h" +#include "base/waitable_event.h" +#include "base/waitable_event_watcher.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::WaitableEvent; +using base::WaitableEventWatcher; + +namespace { + +class QuitDelegate : public WaitableEventWatcher::Delegate { + public: + virtual void OnWaitableEventSignaled(WaitableEvent* event) { + MessageLoop::current()->Quit(); + } +}; + +class DecrementCountDelegate : public WaitableEventWatcher::Delegate { + public: + DecrementCountDelegate(int* counter) : counter_(counter) { + } + virtual void OnWaitableEventSignaled(WaitableEvent* object) { + --(*counter_); + } + private: + int* counter_; +}; + +void RunTest_BasicSignal(MessageLoop::Type message_loop_type) { + MessageLoop message_loop(message_loop_type); + + // A manual-reset event that is not yet signaled. + WaitableEvent event(true, false); + + WaitableEventWatcher watcher; + EXPECT_EQ(NULL, watcher.GetWatchedEvent()); + + QuitDelegate delegate; + watcher.StartWatching(&event, &delegate); + EXPECT_EQ(&event, watcher.GetWatchedEvent()); + + event.Signal(); + + MessageLoop::current()->Run(); + + EXPECT_EQ(NULL, watcher.GetWatchedEvent()); +} + +void RunTest_BasicCancel(MessageLoop::Type message_loop_type) { + MessageLoop message_loop(message_loop_type); + + // A manual-reset event that is not yet signaled. + WaitableEvent event(true, false); + + WaitableEventWatcher watcher; + + QuitDelegate delegate; + watcher.StartWatching(&event, &delegate); + + watcher.StopWatching(); +} + +void RunTest_CancelAfterSet(MessageLoop::Type message_loop_type) { + MessageLoop message_loop(message_loop_type); + + // A manual-reset event that is not yet signaled. + WaitableEvent event(true, false); + + WaitableEventWatcher watcher; + + int counter = 1; + DecrementCountDelegate delegate(&counter); + + watcher.StartWatching(&event, &delegate); + + event.Signal(); + + // Let the background thread do its business + PlatformThread::Sleep(30); + + watcher.StopWatching(); + + MessageLoop::current()->RunAllPending(); + + // Our delegate should not have fired. + EXPECT_EQ(1, counter); +} + +void RunTest_OutlivesMessageLoop(MessageLoop::Type message_loop_type) { + // Simulate a MessageLoop that dies before an WaitableEventWatcher. This + // ordinarily doesn't happen when people use the Thread class, but it can + // happen when people use the Singleton pattern or atexit. + WaitableEvent event(true, false); + { + WaitableEventWatcher watcher; + { + MessageLoop message_loop(message_loop_type); + + QuitDelegate delegate; + watcher.StartWatching(&event, &delegate); + } + } +} + +void RunTest_DeleteUnder(MessageLoop::Type message_loop_type) { + // Delete the WaitableEvent out from under the Watcher. This is explictly + // allowed by the interface. + + MessageLoop message_loop(message_loop_type); + + { + WaitableEventWatcher watcher; + + WaitableEvent* event = new WaitableEvent(false, false); + QuitDelegate delegate; + watcher.StartWatching(event, &delegate); + delete event; + } +} + +} // namespace + +//----------------------------------------------------------------------------- + +TEST(WaitableEventWatcherTest, BasicSignal) { + RunTest_BasicSignal(MessageLoop::TYPE_DEFAULT); + RunTest_BasicSignal(MessageLoop::TYPE_IO); + RunTest_BasicSignal(MessageLoop::TYPE_UI); +} + +TEST(WaitableEventWatcherTest, BasicCancel) { + RunTest_BasicCancel(MessageLoop::TYPE_DEFAULT); + RunTest_BasicCancel(MessageLoop::TYPE_IO); + RunTest_BasicCancel(MessageLoop::TYPE_UI); +} + +TEST(WaitableEventWatcherTest, CancelAfterSet) { + RunTest_CancelAfterSet(MessageLoop::TYPE_DEFAULT); + RunTest_CancelAfterSet(MessageLoop::TYPE_IO); + RunTest_CancelAfterSet(MessageLoop::TYPE_UI); +} + +TEST(WaitableEventWatcherTest, OutlivesMessageLoop) { + RunTest_OutlivesMessageLoop(MessageLoop::TYPE_DEFAULT); + RunTest_OutlivesMessageLoop(MessageLoop::TYPE_IO); + RunTest_OutlivesMessageLoop(MessageLoop::TYPE_UI); +} + +TEST(WaitableEventWatcherTest, DeleteUnder) { + RunTest_DeleteUnder(MessageLoop::TYPE_DEFAULT); + RunTest_DeleteUnder(MessageLoop::TYPE_IO); + RunTest_DeleteUnder(MessageLoop::TYPE_UI); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_watcher_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_watcher_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_watcher_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_watcher_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,60 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/waitable_event_watcher.h" + +#include "base/compiler_specific.h" +#include "base/object_watcher.h" +#include "base/waitable_event.h" + +namespace base { + +WaitableEventWatcher::ObjectWatcherHelper::ObjectWatcherHelper( + WaitableEventWatcher* watcher) + : watcher_(watcher) { +}; + +void WaitableEventWatcher::ObjectWatcherHelper::OnObjectSignaled(HANDLE h) { + watcher_->OnObjectSignaled(); +} + + +WaitableEventWatcher::WaitableEventWatcher() + : event_(NULL), + ALLOW_THIS_IN_INITIALIZER_LIST(helper_(this)), + delegate_(NULL) { +} + +WaitableEventWatcher::~WaitableEventWatcher() { +} + +bool WaitableEventWatcher::StartWatching(WaitableEvent* event, + Delegate* delegate) { + delegate_ = delegate; + event_ = event; + + return watcher_.StartWatching(event->handle(), &helper_); +} + +void WaitableEventWatcher::StopWatching() { + delegate_ = NULL; + event_ = NULL; + watcher_.StopWatching(); +} + +WaitableEvent* WaitableEventWatcher::GetWatchedEvent() { + return event_; +} + +void WaitableEventWatcher::OnObjectSignaled() { + WaitableEvent* event = event_; + Delegate* delegate = delegate_; + event_ = NULL; + delegate_ = NULL; + DCHECK(event); + + delegate->OnWaitableEventSignaled(event); +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/waitable_event_win.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,97 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/waitable_event.h" + +#include +#include + +#include "base/logging.h" +#include "base/time.h" + +namespace base { + +WaitableEvent::WaitableEvent(bool manual_reset, bool signaled) + : handle_(CreateEvent(NULL, manual_reset, signaled, NULL)) { + // We're probably going to crash anyways if this is ever NULL, so we might as + // well make our stack reports more informative by crashing here. + CHECK(handle_); +} + +WaitableEvent::WaitableEvent(HANDLE handle) + : handle_(handle) { + CHECK(handle) << "Tried to create WaitableEvent from NULL handle"; +} + +WaitableEvent::~WaitableEvent() { + CloseHandle(handle_); +} + +HANDLE WaitableEvent::Release() { + HANDLE rv = handle_; + handle_ = INVALID_HANDLE_VALUE; + return rv; +} + +void WaitableEvent::Reset() { + ResetEvent(handle_); +} + +void WaitableEvent::Signal() { + SetEvent(handle_); +} + +bool WaitableEvent::IsSignaled() { + return TimedWait(TimeDelta::FromMilliseconds(0)); +} + +bool WaitableEvent::Wait() { + DWORD result = WaitForSingleObject(handle_, INFINITE); + // It is most unexpected that this should ever fail. Help consumers learn + // about it if it should ever fail. + DCHECK(result == WAIT_OBJECT_0) << "WaitForSingleObject failed"; + return result == WAIT_OBJECT_0; +} + +bool WaitableEvent::TimedWait(const TimeDelta& max_time) { + DCHECK(max_time >= TimeDelta::FromMicroseconds(0)); + // Be careful here. TimeDelta has a precision of microseconds, but this API + // is in milliseconds. If there are 5.5ms left, should the delay be 5 or 6? + // It should be 6 to avoid returning too early. + double timeout = ceil(max_time.InMillisecondsF()); + DWORD result = WaitForSingleObject(handle_, static_cast(timeout)); + switch (result) { + case WAIT_OBJECT_0: + return true; + case WAIT_TIMEOUT: + return false; + } + // It is most unexpected that this should ever fail. Help consumers learn + // about it if it should ever fail. + NOTREACHED() << "WaitForSingleObject failed"; + return false; +} + +// static +size_t WaitableEvent::WaitMany(WaitableEvent** events, size_t count) { + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + CHECK(count <= MAXIMUM_WAIT_OBJECTS) + << "Can only wait on " << MAXIMUM_WAIT_OBJECTS << " with WaitMany"; + + for (size_t i = 0; i < count; ++i) + handles[i] = events[i]->handle(); + + DWORD result = + WaitForMultipleObjects(count, handles, + FALSE, // don't wait for all the objects + INFINITE); // no timeout + if (result < WAIT_OBJECT_0 || result >= WAIT_OBJECT_0 + count) { + NOTREACHED() << "WaitForMultipleObjects failed: " << GetLastError(); + return 0; + } + + return result - WAIT_OBJECT_0; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/watchdog.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/watchdog.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/watchdog.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/watchdog.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,134 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/watchdog.h" + +#include "base/compiler_specific.h" +#include "base/platform_thread.h" + +using base::TimeDelta; +using base::TimeTicks; + +//------------------------------------------------------------------------------ +// Public API methods. + +// Start thread running in a Disarmed state. +Watchdog::Watchdog(const TimeDelta& duration, + const std::string& thread_watched_name, + bool enabled) + : init_successful_(false), + lock_(), + condition_variable_(&lock_), + state_(DISARMED), + duration_(duration), + thread_watched_name_(thread_watched_name), + ALLOW_THIS_IN_INITIALIZER_LIST(delegate_(this)) { + if (!enabled) + return; // Don't start thread, or doing anything really. + init_successful_ = PlatformThread::Create(0, // Default stack size. + &delegate_, + &handle_); + DCHECK(init_successful_); +} + +// Notify watchdog thread, and wait for it to finish up. +Watchdog::~Watchdog() { + if (!init_successful_) + return; + { + AutoLock lock(lock_); + state_ = SHUTDOWN; + } + condition_variable_.Signal(); + PlatformThread::Join(handle_); +} + +void Watchdog::Arm() { + ArmAtStartTime(TimeTicks::Now()); +} + +void Watchdog::ArmSomeTimeDeltaAgo(const TimeDelta& time_delta) { + ArmAtStartTime(TimeTicks::Now() - time_delta); +} + +// Start clock for watchdog. +void Watchdog::ArmAtStartTime(const TimeTicks start_time) { + { + AutoLock lock(lock_); + start_time_ = start_time; + state_ = ARMED; + } + // Force watchdog to wake up, and go to sleep with the timer ticking with the + // proper duration. + condition_variable_.Signal(); +} + +// Disable watchdog so that it won't do anything when time expires. +void Watchdog::Disarm() { + AutoLock lock(lock_); + state_ = DISARMED; + // We don't need to signal, as the watchdog will eventually wake up, and it + // will check its state and time, and act accordingly. +} + +//------------------------------------------------------------------------------ +// Internal private methods that the watchdog thread uses. + +void Watchdog::ThreadDelegate::ThreadMain() { + SetThreadName(); + TimeDelta remaining_duration; + while (1) { + AutoLock lock(watchdog_->lock_); + while (DISARMED == watchdog_->state_) + watchdog_->condition_variable_.Wait(); + if (SHUTDOWN == watchdog_->state_) + return; + DCHECK(ARMED == watchdog_->state_); + remaining_duration = watchdog_->duration_ - + (TimeTicks::Now() - watchdog_->start_time_); + if (remaining_duration.InMilliseconds() > 0) { + // Spurios wake? Timer drifts? Go back to sleep for remaining time. + watchdog_->condition_variable_.TimedWait(remaining_duration); + } else { + // We overslept, so this seems like a real alarm. + // Watch out for a user that stopped the debugger on a different alarm! + { + AutoLock static_lock(static_lock_); + if (last_debugged_alarm_time_ > watchdog_->start_time_) { + // False alarm: we started our clock before the debugger break (last + // alarm time). + watchdog_->start_time_ += last_debugged_alarm_delay_; + if (last_debugged_alarm_time_ > watchdog_->start_time_) + // Too many alarms must have taken place. + watchdog_->state_ = DISARMED; + continue; + } + } + watchdog_->state_ = DISARMED; // Only alarm at most once. + TimeTicks last_alarm_time = TimeTicks::Now(); + watchdog_->Alarm(); // Set a break point here to debug on alarms. + TimeDelta last_alarm_delay = TimeTicks::Now() - last_alarm_time; + if (last_alarm_delay > TimeDelta::FromMilliseconds(2)) { + // Ignore race of two alarms/breaks going off at roughly the same time. + AutoLock static_lock(static_lock_); + // This was a real debugger break. + last_debugged_alarm_time_ = last_alarm_time; + last_debugged_alarm_delay_ = last_alarm_delay; + } + } + } +} + +void Watchdog::ThreadDelegate::SetThreadName() const { + std::string name = watchdog_->thread_watched_name_ + " Watchdog"; + PlatformThread::SetName(name.c_str()); + DLOG(INFO) << "Watchdog active: " << name; +} + +// static +Lock Watchdog::static_lock_; // Lock for access of static data... +// static +TimeTicks Watchdog::last_debugged_alarm_time_ = TimeTicks(); +// static +TimeDelta Watchdog::last_debugged_alarm_delay_; diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/watchdog.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/watchdog.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/watchdog.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/watchdog.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,92 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// The Watchdog class creates a second thread that can Alarm if a specific +// duration of time passes without proper attention. The duration of time is +// specified at construction time. The Watchdog may be used many times by +// simply calling Arm() (to start timing) and Disarm() (to reset the timer). +// The Watchdog is typically used under a debugger, where the stack traces on +// other threads can be examined if/when the Watchdog alarms. + +// Some watchdogs will be enabled or disabled via command line switches. To +// facilitate such code, an "enabled" argument for the constuctor can be used +// to permanently disable the watchdog. Disabled watchdogs don't even spawn +// a second thread, and their methods call (Arm() and Disarm()) return very +// quickly. + +#ifndef BASE_WATCHDOG_H__ +#define BASE_WATCHDOG_H__ + +#include + +#include "base/condition_variable.h" +#include "base/lock.h" +#include "base/logging.h" +#include "base/platform_thread.h" +#include "base/time.h" + +class Watchdog { + public: + // Constructor specifies how long the Watchdog will wait before alarming. + Watchdog(const base::TimeDelta& duration, + const std::string& thread_watched_name, + bool enabled); + virtual ~Watchdog(); + + // Start timing, and alarm when time expires (unless we're disarm()ed.) + void Arm(); // Arm starting now. + void ArmSomeTimeDeltaAgo(const base::TimeDelta& time_delta); + void ArmAtStartTime(const base::TimeTicks start_time); + + // Reset time, and do not set off the alarm. + void Disarm(); + + // Alarm is called if the time expires after an Arm() without someone calling + // Disarm(). This method can be overridden to create testable classes. + virtual void Alarm() { + DLOG(INFO) << "Watchdog alarmed for " << thread_watched_name_; + } + + private: + class ThreadDelegate : public PlatformThread::Delegate { + public: + explicit ThreadDelegate(Watchdog* watchdog) : watchdog_(watchdog) { + } + virtual void ThreadMain(); + private: + Watchdog* watchdog_; + + void SetThreadName() const; + }; + + enum State {ARMED, DISARMED, SHUTDOWN }; + + bool init_successful_; + + Lock lock_; // Mutex for state_. + ConditionVariable condition_variable_; + State state_; + const base::TimeDelta duration_; // How long after start_time_ do we alarm? + const std::string thread_watched_name_; + PlatformThreadHandle handle_; + ThreadDelegate delegate_; // Store it, because it must outlive the thread. + + base::TimeTicks start_time_; // Start of epoch, and alarm after duration_. + + // When the debugger breaks (when we alarm), all the other alarms that are + // armed will expire (also alarm). To diminish this effect, we track any + // delay due to debugger breaks, and we *try* to adjust the effective start + // time of other alarms to step past the debugging break. + // Without this safety net, any alarm will typically trigger a host of follow + // on alarms from callers that specify old times. + static Lock static_lock_; // Lock for access of static data... + // When did we last alarm and get stuck (for a while) in a debugger? + static base::TimeTicks last_debugged_alarm_time_; + // How long did we sit on a break in the debugger? + static base::TimeDelta last_debugged_alarm_delay_; + + DISALLOW_COPY_AND_ASSIGN(Watchdog); +}; + +#endif // BASE_WATCHDOG_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/watchdog_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/watchdog_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/watchdog_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/watchdog_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,117 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Tests for Watchdog class. + +#include "base/platform_thread.h" +#include "base/spin_wait.h" +#include "base/time.h" +#include "base/watchdog.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::TimeDelta; + +namespace { + +//------------------------------------------------------------------------------ +// Provide a derived class to facilitate testing. + +class WatchdogCounter : public Watchdog { + public: + WatchdogCounter(const TimeDelta& duration, + const std::string& thread_watched_name, + bool enabled) + : Watchdog(duration, thread_watched_name, enabled), alarm_counter_(0) { + } + + virtual ~WatchdogCounter() {} + + virtual void Alarm() { + alarm_counter_++; + Watchdog::Alarm(); + } + + int alarm_counter() { return alarm_counter_; } + + private: + int alarm_counter_; + + DISALLOW_COPY_AND_ASSIGN(WatchdogCounter); +}; + +class WatchdogTest : public testing::Test { +}; + + +//------------------------------------------------------------------------------ +// Actual tests + +// Minimal constructor/destructor test. +TEST(WatchdogTest, StartupShutdownTest) { + Watchdog watchdog1(TimeDelta::FromMilliseconds(300), "Disabled", false); + Watchdog watchdog2(TimeDelta::FromMilliseconds(300), "Enabled", true); +} + +// Test ability to call Arm and Disarm repeatedly. +TEST(WatchdogTest, ArmDisarmTest) { + Watchdog watchdog1(TimeDelta::FromMilliseconds(300), "Disabled", false); + watchdog1.Arm(); + watchdog1.Disarm(); + watchdog1.Arm(); + watchdog1.Disarm(); + + Watchdog watchdog2(TimeDelta::FromMilliseconds(300), "Enabled", true); + watchdog2.Arm(); + watchdog2.Disarm(); + watchdog2.Arm(); + watchdog2.Disarm(); +} + +// Make sure a basic alarm fires when the time has expired. +TEST(WatchdogTest, AlarmTest) { + WatchdogCounter watchdog(TimeDelta::FromMilliseconds(10), "Enabled", true); + watchdog.Arm(); + SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5), + watchdog.alarm_counter() > 0); + EXPECT_EQ(1, watchdog.alarm_counter()); + + // Set a time greater than the timeout into the past. + watchdog.ArmSomeTimeDeltaAgo(TimeDelta::FromSeconds(2)); + // It should instantly go off, but certainly in less than 5 minutes. + SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5), + watchdog.alarm_counter() > 1); + + EXPECT_EQ(2, watchdog.alarm_counter()); +} + +// Make sure a disable alarm does nothing, even if we arm it. +TEST(WatchdogTest, ConstructorDisabledTest) { + WatchdogCounter watchdog(TimeDelta::FromMilliseconds(10), "Disabled", false); + watchdog.Arm(); + // Alarm should not fire, as it was disabled. + PlatformThread::Sleep(500); + EXPECT_EQ(0, watchdog.alarm_counter()); +} + +// Make sure Disarming will prevent firing, even after Arming. +TEST(WatchdogTest, DisarmTest) { + WatchdogCounter watchdog(TimeDelta::FromSeconds(5), "Enabled", true); + watchdog.Arm(); + PlatformThread::Sleep(100); // Don't sleep too long + watchdog.Disarm(); + // Alarm should not fire. + PlatformThread::Sleep(5500); + EXPECT_EQ(0, watchdog.alarm_counter()); + + // ...but even after disarming, we can still use the alarm... + // Set a time greater than the timeout into the past. + watchdog.ArmSomeTimeDeltaAgo(TimeDelta::FromSeconds(2)); + // It should almost instantly go off, but certainly in less than 5 minutes. + SPIN_FOR_TIMEDELTA_OR_UNTIL_TRUE(TimeDelta::FromMinutes(5), + watchdog.alarm_counter() > 0); + + EXPECT_EQ(1, watchdog.alarm_counter()); +} + +} // namespace diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/windows_message_list.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/windows_message_list.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/windows_message_list.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/windows_message_list.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,249 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// WARNING: DO NOT USE standard header file protection. +// This file may be include several times in its entirety. + +// This file contains a list of all messages supported by Windows as would be +// handled in a message loop. We only list the messages provided in +// , and do not currently include (the otherwise undefined) +// #define WM_SYSTIMER 0x118 + +// By using various macro tricks, this list can be used to create pretty print +// functions for the messages. See message_loop.cc for an example. + +// Start list of Windows Messages given in +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NULL) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CREATE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DESTROY) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOVE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SIZE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ACTIVATE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETFOCUS) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KILLFOCUS) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ENABLE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETREDRAW) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETTEXT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETTEXT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETTEXTLENGTH) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PAINT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CLOSE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUERYENDSESSION) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUERYOPEN) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ENDSESSION) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUIT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ERASEBKGND) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSCOLORCHANGE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SHOWWINDOW) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_WININICHANGE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETTINGCHANGE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DEVMODECHANGE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ACTIVATEAPP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_FONTCHANGE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_TIMECHANGE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CANCELMODE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETCURSOR) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEACTIVATE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CHILDACTIVATE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUEUESYNC) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETMINMAXINFO) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PAINTICON) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ICONERASEBKGND) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NEXTDLGCTL) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SPOOLERSTATUS) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DRAWITEM) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MEASUREITEM) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DELETEITEM) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_VKEYTOITEM) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CHARTOITEM) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETFONT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETFONT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETHOTKEY) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETHOTKEY) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUERYDRAGICON) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COMPAREITEM) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETOBJECT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COMPACTING) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COMMNOTIFY) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_WINDOWPOSCHANGING) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_WINDOWPOSCHANGED) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_POWER) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COPYDATA) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CANCELJOURNAL) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NOTIFY) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INPUTLANGCHANGEREQUEST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INPUTLANGCHANGE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_TCARD) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HELP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_USERCHANGED) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NOTIFYFORMAT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CONTEXTMENU) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_STYLECHANGING) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_STYLECHANGED) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DISPLAYCHANGE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETICON) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SETICON) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCCREATE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCDESTROY) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCCALCSIZE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCHITTEST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCPAINT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCACTIVATE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETDLGCODE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYNCPAINT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMOUSEMOVE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCLBUTTONDOWN) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCLBUTTONUP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCLBUTTONDBLCLK) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCRBUTTONDOWN) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCRBUTTONUP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCRBUTTONDBLCLK) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMBUTTONDOWN) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMBUTTONUP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMBUTTONDBLCLK) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCXBUTTONDOWN) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCXBUTTONUP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCXBUTTONDBLCLK) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INPUT_DEVICE_CHANGE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INPUT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KEYFIRST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KEYDOWN) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KEYUP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CHAR) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DEADCHAR) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSKEYDOWN) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSKEYUP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSCHAR) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSDEADCHAR) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_UNICHAR) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KEYLAST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_KEYLAST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_STARTCOMPOSITION) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_ENDCOMPOSITION) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_COMPOSITION) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_KEYLAST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INITDIALOG) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COMMAND) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SYSCOMMAND) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_TIMER) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HSCROLL) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_VSCROLL) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INITMENU) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_INITMENUPOPUP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENUSELECT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENUCHAR) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ENTERIDLE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENURBUTTONUP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENUDRAG) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENUGETOBJECT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_UNINITMENUPOPUP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MENUCOMMAND) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CHANGEUISTATE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_UPDATEUISTATE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUERYUISTATE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORMSGBOX) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLOREDIT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORLISTBOX) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORBTN) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORDLG) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORSCROLLBAR) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CTLCOLORSTATIC) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEFIRST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEMOVE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_LBUTTONDOWN) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_LBUTTONUP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_LBUTTONDBLCLK) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_RBUTTONDOWN) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_RBUTTONUP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_RBUTTONDBLCLK) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MBUTTONDOWN) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MBUTTONUP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MBUTTONDBLCLK) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEWHEEL) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_XBUTTONDOWN) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_XBUTTONUP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_XBUTTONDBLCLK) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEHWHEEL) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSELAST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSELAST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSELAST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSELAST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PARENTNOTIFY) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ENTERMENULOOP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_EXITMENULOOP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NEXTMENU) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SIZING) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CAPTURECHANGED) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOVING) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_POWERBROADCAST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DEVICECHANGE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDICREATE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIDESTROY) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIACTIVATE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIRESTORE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDINEXT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIMAXIMIZE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDITILE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDICASCADE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIICONARRANGE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIGETACTIVE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDISETMENU) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ENTERSIZEMOVE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_EXITSIZEMOVE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DROPFILES) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MDIREFRESHMENU) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_SETCONTEXT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_NOTIFY) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_CONTROL) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_COMPOSITIONFULL) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_SELECT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_CHAR) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_REQUEST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_KEYDOWN) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_IME_KEYUP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSEHOVER) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_MOUSELEAVE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMOUSEHOVER) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_NCMOUSELEAVE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_WTSSESSION_CHANGE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_TABLET_FIRST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_TABLET_LAST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CUT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_COPY) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PASTE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CLEAR) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_UNDO) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_RENDERFORMAT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_RENDERALLFORMATS) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DESTROYCLIPBOARD) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DRAWCLIPBOARD) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PAINTCLIPBOARD) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_VSCROLLCLIPBOARD) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_SIZECLIPBOARD) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_ASKCBFORMATNAME) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CHANGECBCHAIN) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HSCROLLCLIPBOARD) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_QUERYNEWPALETTE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PALETTEISCHANGING) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PALETTECHANGED) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HOTKEY) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PRINT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PRINTCLIENT) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_APPCOMMAND) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_THEMECHANGED) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_CLIPBOARDUPDATE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DWMCOMPOSITIONCHANGED) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DWMNCRENDERINGCHANGED) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DWMCOLORIZATIONCOLORCHANGED) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_DWMWINDOWMAXIMIZEDCHANGE) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_GETTITLEBARINFOEX) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HANDHELDFIRST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_HANDHELDLAST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_AFXFIRST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_AFXLAST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PENWINFIRST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_PENWINLAST) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_APP) +A_NAMED_MESSAGE_FROM_WINUSER_H(WM_USER) +// End list of Windows Messages given in diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/win_util.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/win_util.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/win_util.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/win_util.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,461 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/win_util.h" + +#include +#include + +#include "base/logging.h" +#include "base/registry.h" +#include "base/scoped_handle.h" +#include "base/scoped_ptr.h" +#include "base/singleton.h" +#include "base/string_util.h" +#include "base/tracked.h" + +namespace win_util { + +#define SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(struct_name, member) \ + offsetof(struct_name, member) + \ + (sizeof static_cast(NULL)->member) +#define NONCLIENTMETRICS_SIZE_PRE_VISTA \ + SIZEOF_STRUCT_WITH_SPECIFIED_LAST_MEMBER(NONCLIENTMETRICS, lfMessageFont) + +void GetNonClientMetrics(NONCLIENTMETRICS* metrics) { + DCHECK(metrics); + + static const UINT SIZEOF_NONCLIENTMETRICS = + (GetWinVersion() >= WINVERSION_VISTA) ? + sizeof(NONCLIENTMETRICS) : NONCLIENTMETRICS_SIZE_PRE_VISTA; + metrics->cbSize = SIZEOF_NONCLIENTMETRICS; + const bool success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, + SIZEOF_NONCLIENTMETRICS, metrics, + 0); + DCHECK(success); +} + +WinVersion GetWinVersion() { + static bool checked_version = false; + static WinVersion win_version = WINVERSION_PRE_2000; + if (!checked_version) { + OSVERSIONINFOEX version_info; + version_info.dwOSVersionInfoSize = sizeof version_info; + GetVersionEx(reinterpret_cast(&version_info)); + if (version_info.dwMajorVersion == 5) { + switch (version_info.dwMinorVersion) { + case 0: + win_version = WINVERSION_2000; + break; + case 1: + win_version = WINVERSION_XP; + break; + case 2: + default: + win_version = WINVERSION_SERVER_2003; + break; + } + } else if (version_info.dwMajorVersion == 6) { + if (version_info.wProductType != VER_NT_WORKSTATION) { + // 2008 is 6.0, and 2008 R2 is 6.1. + win_version = WINVERSION_2008; + } else { + if (version_info.dwMinorVersion == 0) { + win_version = WINVERSION_VISTA; + } else { + win_version = WINVERSION_WIN7; + } + } + } else if (version_info.dwMajorVersion > 6) { + win_version = WINVERSION_WIN7; + } + checked_version = true; + } + return win_version; +} + +void GetServicePackLevel(int* major, int* minor) { + DCHECK(major && minor); + static bool checked_version = false; + static int service_pack_major = -1; + static int service_pack_minor = -1; + if (!checked_version) { + OSVERSIONINFOEX version_info = {0}; + version_info.dwOSVersionInfoSize = sizeof(version_info); + GetVersionEx(reinterpret_cast(&version_info)); + service_pack_major = version_info.wServicePackMajor; + service_pack_minor = version_info.wServicePackMinor; + checked_version = true; + } + + *major = service_pack_major; + *minor = service_pack_minor; +} + +bool AddAccessToKernelObject(HANDLE handle, WELL_KNOWN_SID_TYPE known_sid, + ACCESS_MASK access) { + PSECURITY_DESCRIPTOR descriptor = NULL; + PACL old_dacl = NULL; + PACL new_dacl = NULL; + + if (ERROR_SUCCESS != GetSecurityInfo(handle, SE_KERNEL_OBJECT, + DACL_SECURITY_INFORMATION, NULL, NULL, &old_dacl, NULL, + &descriptor)) + return false; + + BYTE sid[SECURITY_MAX_SID_SIZE] = {0}; + DWORD size_sid = SECURITY_MAX_SID_SIZE; + + if (known_sid == WinSelfSid) { + // We hijack WinSelfSid when we want to add the current user instead of + // a known sid. + HANDLE token = NULL; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) { + LocalFree(descriptor); + return false; + } + + DWORD size = sizeof(TOKEN_USER) + size_sid; + TOKEN_USER* token_user = reinterpret_cast(new BYTE[size]); + scoped_ptr token_user_ptr(token_user); + BOOL ret = GetTokenInformation(token, TokenUser, token_user, size, &size); + + CloseHandle(token); + + if (!ret) { + LocalFree(descriptor); + return false; + } + memcpy(sid, token_user->User.Sid, size_sid); + } else { + if (!CreateWellKnownSid(known_sid , NULL, sid, &size_sid)) { + LocalFree(descriptor); + return false; + } + } + + EXPLICIT_ACCESS new_access = {0}; + new_access.grfAccessMode = GRANT_ACCESS; + new_access.grfAccessPermissions = access; + new_access.grfInheritance = NO_INHERITANCE; + + new_access.Trustee.pMultipleTrustee = NULL; + new_access.Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE; + new_access.Trustee.TrusteeForm = TRUSTEE_IS_SID; + new_access.Trustee.ptstrName = reinterpret_cast(&sid); + + if (ERROR_SUCCESS != SetEntriesInAcl(1, &new_access, old_dacl, &new_dacl)) { + LocalFree(descriptor); + return false; + } + + DWORD result = SetSecurityInfo(handle, SE_KERNEL_OBJECT, + DACL_SECURITY_INFORMATION, NULL, NULL, + new_dacl, NULL); + + LocalFree(new_dacl); + LocalFree(descriptor); + + if (ERROR_SUCCESS != result) + return false; + + return true; +} + +bool GetUserSidString(std::wstring* user_sid) { + // Get the current token. + HANDLE token = NULL; + if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token)) + return false; + ScopedHandle token_scoped(token); + + DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE; + scoped_ptr user(reinterpret_cast(new BYTE[size])); + + if (!::GetTokenInformation(token, TokenUser, user.get(), size, &size)) + return false; + + if (!user->User.Sid) + return false; + + // Convert the data to a string. + wchar_t* sid_string; + if (!::ConvertSidToStringSid(user->User.Sid, &sid_string)) + return false; + + *user_sid = sid_string; + + ::LocalFree(sid_string); + + return true; +} + +bool GetLogonSessionOnlyDACL(SECURITY_DESCRIPTOR** security_descriptor) { + // Get the current token. + HANDLE token = NULL; + if (!OpenProcessToken(::GetCurrentProcess(), TOKEN_QUERY, &token)) + return false; + ScopedHandle token_scoped(token); + + // Get the size of the TokenGroups structure. + DWORD size = 0; + BOOL result = GetTokenInformation(token, TokenGroups, NULL, 0, &size); + if (result != FALSE && GetLastError() != ERROR_INSUFFICIENT_BUFFER) + return false; + + // Get the data. + scoped_ptr token_groups; + token_groups.reset(reinterpret_cast(new char[size])); + + if (!GetTokenInformation(token, TokenGroups, token_groups.get(), size, &size)) + return false; + + // Look for the logon sid. + SID* logon_sid = NULL; + for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) { + if ((token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) != 0) { + logon_sid = static_cast(token_groups->Groups[i].Sid); + break; + } + } + + if (!logon_sid) + return false; + + // Convert the data to a string. + wchar_t* sid_string; + if (!ConvertSidToStringSid(logon_sid, &sid_string)) + return false; + + static const wchar_t dacl_format[] = L"D:(A;OICI;GA;;;%ls)"; + wchar_t dacl[SECURITY_MAX_SID_SIZE + arraysize(dacl_format) + 1] = {0}; + wsprintf(dacl, dacl_format, sid_string); + + LocalFree(sid_string); + + // Convert the string to a security descriptor + if (!ConvertStringSecurityDescriptorToSecurityDescriptor( + dacl, + SDDL_REVISION_1, + reinterpret_cast(security_descriptor), + NULL)) { + return false; + } + + return true; +} + +#pragma warning(push) +#pragma warning(disable:4312 4244) +WNDPROC SetWindowProc(HWND hwnd, WNDPROC proc) { + // The reason we don't return the SetwindowLongPtr() value is that it returns + // the orignal window procedure and not the current one. I don't know if it is + // a bug or an intended feature. + WNDPROC oldwindow_proc = + reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_WNDPROC)); + SetWindowLongPtr(hwnd, GWLP_WNDPROC, reinterpret_cast(proc)); + return oldwindow_proc; +} + +void* SetWindowUserData(HWND hwnd, void* user_data) { + return + reinterpret_cast(SetWindowLongPtr(hwnd, GWLP_USERDATA, + reinterpret_cast(user_data))); +} + +void* GetWindowUserData(HWND hwnd) { + return reinterpret_cast(GetWindowLongPtr(hwnd, GWLP_USERDATA)); +} + +// Maps to the WNDPROC for a window that was active before the subclass was +// installed. +static const wchar_t* const kHandlerKey = L"__ORIGINAL_MESSAGE_HANDLER__"; + +bool IsSubclassed(HWND window, WNDPROC subclass_proc) { + WNDPROC original_handler = + reinterpret_cast(GetWindowLongPtr(window, GWLP_WNDPROC)); + return original_handler == subclass_proc; +} + +bool Subclass(HWND window, WNDPROC subclass_proc) { + WNDPROC original_handler = + reinterpret_cast(GetWindowLongPtr(window, GWLP_WNDPROC)); + if (original_handler != subclass_proc) { + win_util::SetWindowProc(window, subclass_proc); + SetProp(window, kHandlerKey, original_handler); + return true; + } + return false; +} + +bool Unsubclass(HWND window, WNDPROC subclass_proc) { + WNDPROC current_handler = + reinterpret_cast(GetWindowLongPtr(window, GWLP_WNDPROC)); + if (current_handler == subclass_proc) { + HANDLE original_handler = GetProp(window, kHandlerKey); + if (original_handler) { + RemoveProp(window, kHandlerKey); + win_util::SetWindowProc(window, + reinterpret_cast(original_handler)); + return true; + } + } + return false; +} + +WNDPROC GetSuperclassWNDPROC(HWND window) { + return reinterpret_cast(GetProp(window, kHandlerKey)); +} + +#pragma warning(pop) + +bool IsShiftPressed() { + return (::GetKeyState(VK_SHIFT) & 0x80) == 0x80; +} + +bool IsCtrlPressed() { + return (::GetKeyState(VK_CONTROL) & 0x80) == 0x80; +} + +bool IsAltPressed() { + return (::GetKeyState(VK_MENU) & 0x80) == 0x80; +} + +std::wstring GetClassName(HWND window) { + // GetClassNameW will return a truncated result (properly null terminated) if + // the given buffer is not large enough. So, it is not possible to determine + // that we got the entire class name if the result is exactly equal to the + // size of the buffer minus one. + DWORD buffer_size = MAX_PATH; + while (true) { + std::wstring output; + DWORD size_ret = + GetClassNameW(window, WriteInto(&output, buffer_size), buffer_size); + if (size_ret == 0) + break; + if (size_ret < (buffer_size - 1)) { + output.resize(size_ret); + return output; + } + buffer_size *= 2; + } + return std::wstring(); // error +} + +bool UserAccountControlIsEnabled() { + RegKey key(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\System"); + DWORD uac_enabled; + if (!key.ReadValueDW(L"EnableLUA", &uac_enabled)) + return true; + // Users can set the EnableLUA value to something arbitrary, like 2, which + // Vista will treat as UAC enabled, so we make sure it is not set to 0. + return (uac_enabled != 0); +} + +std::wstring FormatMessage(unsigned messageid) { + wchar_t* string_buffer = NULL; + unsigned string_length = ::FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, messageid, 0, + reinterpret_cast(&string_buffer), 0, NULL); + + std::wstring formatted_string; + if (string_buffer) { + formatted_string = string_buffer; + LocalFree(reinterpret_cast(string_buffer)); + } else { + // The formating failed. simply convert the message value into a string. + SStringPrintf(&formatted_string, L"message number %d", messageid); + } + return formatted_string; +} + +std::wstring FormatLastWin32Error() { + return FormatMessage(GetLastError()); +} + +typedef std::map HWNDInfoMap; +struct HWNDBirthMapTrait : public DefaultSingletonTraits { +}; +struct HWNDDeathMapTrait : public DefaultSingletonTraits { +}; + +void NotifyHWNDCreation(const tracked_objects::Location& from_here, HWND hwnd) { + HWNDInfoMap* birth_map = Singleton::get(); + HWNDInfoMap::iterator birth_iter = birth_map->find(hwnd); + if (birth_iter != birth_map->end()) { + birth_map->erase(birth_iter); + + // We have already seen this HWND, was it destroyed? + HWNDInfoMap* death_map = Singleton::get(); + HWNDInfoMap::iterator death_iter = death_map->find(hwnd); + if (death_iter == death_map->end()) { + // We did not get a destruction notification. The code is probably not + // calling NotifyHWNDDestruction for that HWND. + NOTREACHED() << "Creation of HWND reported for already tracked HWND. The " + "HWND destruction is probably not tracked properly. " + "Fix it!"; + } else { + death_map->erase(death_iter); + } + } + birth_map->insert(std::pair(hwnd, + from_here)); +} + +void NotifyHWNDDestruction(const tracked_objects::Location& from_here, + HWND hwnd) { + HWNDInfoMap* death_map = Singleton::get(); + HWNDInfoMap::iterator death_iter = death_map->find(hwnd); + + HWNDInfoMap* birth_map = Singleton::get(); + HWNDInfoMap::iterator birth_iter = birth_map->find(hwnd); + + if (death_iter != death_map->end()) { + std::string allocation, first_delete, second_delete; + if (birth_iter != birth_map->end()) + birth_iter->second.Write(true, true, &allocation); + death_iter->second.Write(true, true, &first_delete); + from_here.Write(true, true, &second_delete); + LOG(FATAL) << "Double delete of an HWND. Please file a bug with info on " + "how you got that assertion and the following information:\n" + "Double delete of HWND 0x" << hwnd << "\n" << + "Allocated at " << allocation << "\n" << + "Deleted first at " << first_delete << "\n" << + "Deleted again at " << second_delete; + death_map->erase(death_iter); + } + + if (birth_iter == birth_map->end()) { + NOTREACHED() << "Destruction of HWND reported for unknown HWND. The HWND " + "construction is probably not tracked properly. Fix it!"; + } + death_map->insert(std::pair(hwnd, + from_here)); +} + +} // namespace win_util + +#ifdef _MSC_VER +// +// If the ASSERT below fails, please install Visual Studio 2005 Service Pack 1. +// +extern char VisualStudio2005ServicePack1Detection[10]; +COMPILE_ASSERT(sizeof(&VisualStudio2005ServicePack1Detection) == sizeof(void*), + VS2005SP1Detect); +// +// Chrome requires at least Service Pack 1 for Visual Studio 2005. +// +#endif // _MSC_VER + +#if 0 +#error You must install the Windows 2008 or Vista Software Development Kit and \ +set it as your default include path to build this library. You can grab it by \ +searching for "download windows sdk 2008" in your favorite web search engine. \ +Also make sure you register the SDK with Visual Studio, by selecting \ +"Integrate Windows SDK with Visual Studio 2005" from the Windows SDK \ +menu (see Start - All Programs - Microsoft Windows SDK - \ +Visual Studio Registration). +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/win_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/win_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/win_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/win_util.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,122 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_WIN_UTIL_H__ +#define BASE_WIN_UTIL_H__ + +#include +#include + +#include + +#include "base/tracked.h" + +namespace win_util { + +// NOTE: Keep these in order so callers can do things like +// "if (GetWinVersion() > WINVERSION_2000) ...". It's OK to change the values, +// though. +enum WinVersion { + WINVERSION_PRE_2000 = 0, // Not supported + WINVERSION_2000 = 1, + WINVERSION_XP = 2, + WINVERSION_SERVER_2003 = 3, + WINVERSION_VISTA = 4, + WINVERSION_2008 = 5, + WINVERSION_WIN7 = 6, +}; + +void GetNonClientMetrics(NONCLIENTMETRICS* metrics); + +// Returns the running version of Windows. +WinVersion GetWinVersion(); + +// Returns the major and minor version of the service pack installed. +void GetServicePackLevel(int* major, int* minor); + +// Adds an ACE in the DACL of the object referenced by handle. The ACE is +// granting |access| to the user |known_sid|. +// If |known_sid| is WinSelfSid, the sid of the current user will be added to +// the DACL. +bool AddAccessToKernelObject(HANDLE handle, WELL_KNOWN_SID_TYPE known_sid, + ACCESS_MASK access); + +// Returns the string representing the current user sid. +bool GetUserSidString(std::wstring* user_sid); + +// Creates a security descriptor with a DACL that has one ace giving full +// access to the current logon session. +// The security descriptor returned must be freed using LocalFree. +// The function returns true if it succeeds, false otherwise. +bool GetLogonSessionOnlyDACL(SECURITY_DESCRIPTOR** security_descriptor); + +// Useful for subclassing a HWND. Returns the previous window procedure. +WNDPROC SetWindowProc(HWND hwnd, WNDPROC wndproc); + +// Returns true if the existing window procedure is the same as |subclass_proc|. +bool IsSubclassed(HWND window, WNDPROC subclass_proc); + +// Subclasses a window, replacing its existing window procedure with the +// specified one. Returns true if the current window procedure was replaced, +// false if the window has already been subclassed with the specified +// subclass procedure. +bool Subclass(HWND window, WNDPROC subclass_proc); + +// Unsubclasses a window subclassed using Subclass. Returns true if +// the window was subclassed with the specified |subclass_proc| and the window +// was successfully unsubclassed, false if the window's window procedure is not +// |subclass_proc|. +bool Unsubclass(HWND window, WNDPROC subclass_proc); + +// Retrieves the original WNDPROC of a window subclassed using +// SubclassWindow. +WNDPROC GetSuperclassWNDPROC(HWND window); + +// Pointer-friendly wrappers around Get/SetWindowLong(..., GWLP_USERDATA, ...) +// Returns the previously set value. +void* SetWindowUserData(HWND hwnd, void* user_data); +void* GetWindowUserData(HWND hwnd); + +// Returns true if the shift key is currently pressed. +bool IsShiftPressed(); + +// Returns true if the ctrl key is currently pressed. +bool IsCtrlPressed(); + +// Returns true if the alt key is currently pressed. +bool IsAltPressed(); + +// A version of the GetClassNameW API that returns the class name in an +// std::wstring. An empty result indicates a failure to get the class name. +std::wstring GetClassName(HWND window); + +// Returns false if user account control (UAC) has been disabled with the +// EnableLUA registry flag. Returns true if user account control is enabled. +// NOTE: The EnableLUA registry flag, which is ignored on Windows XP +// machines, might still exist and be set to 0 (UAC disabled), in which case +// this function will return false. You should therefore check this flag only +// if the OS is Vista. +bool UserAccountControlIsEnabled(); + +// Use the Win32 API FormatMessage() function to generate a string, using +// Windows's default Message Compiled resources; ignoring the inserts. +std::wstring FormatMessage(unsigned messageid); + +// Uses the last Win32 error to generate a human readable message string. +std::wstring FormatLastWin32Error(); + +// These 2 methods are used to track HWND creation/destruction to investigate +// a mysterious crasher http://crbugs.com/4714 (crasher in on NCDestroy) that +// might be caused by a multiple delete of an HWND. +void NotifyHWNDCreation(const tracked_objects::Location& from_here, HWND hwnd); +void NotifyHWNDDestruction(const tracked_objects::Location& from_here, + HWND hwnd); + +#define TRACK_HWND_CREATION(hwnd) win_util::NotifyHWNDCreation(FROM_HERE, hwnd) +#define TRACK_HWND_DESTRUCTION(hwnd) \ + win_util::NotifyHWNDDestruction(FROM_HERE, hwnd) + +} // namespace win_util + +#endif // BASE_WIN_UTIL_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/win_util_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/win_util_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/win_util_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/win_util_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,88 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "base/registry.h" +#include "base/string_util.h" +#include "base/win_util.h" + +// The test is somewhat silly, because the Vista bots some have UAC enabled +// and some have it disabled. At least we check that it does not crash. +TEST(BaseWinUtilTest, TestIsUACEnabled) { + if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) { + win_util::UserAccountControlIsEnabled(); + } else { + EXPECT_TRUE(win_util::UserAccountControlIsEnabled()); + } +} + +TEST(BaseWinUtilTest, TestGetUserSidString) { + std::wstring user_sid; + EXPECT_TRUE(win_util::GetUserSidString(&user_sid)); + EXPECT_TRUE(!user_sid.empty()); +} + +TEST(BaseWinUtilTest, TestGetNonClientMetrics) { + NONCLIENTMETRICS metrics = {0}; + win_util::GetNonClientMetrics(&metrics); + EXPECT_TRUE(metrics.cbSize > 0); + EXPECT_TRUE(metrics.iScrollWidth > 0); + EXPECT_TRUE(metrics.iScrollHeight > 0); +} + +namespace { + +// Saves the current thread's locale ID when initialized, and restores it when +// the instance is going out of scope. +class ThreadLocaleSaver { + public: + ThreadLocaleSaver() : original_locale_id_(GetThreadLocale()) {} + ~ThreadLocaleSaver() { SetThreadLocale(original_locale_id_); } + + private: + LCID original_locale_id_; + + DISALLOW_COPY_AND_ASSIGN(ThreadLocaleSaver); +}; + +} // namespace + +TEST(BaseWinUtilTest, FormatMessage) { + // Because we cannot write tests of every language, we only test the message + // of en-US locale. Here, we change the current locale temporarily. + ThreadLocaleSaver thread_locale_saver; + WORD language_id = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US); + LCID locale_id = MAKELCID(language_id, SORT_DEFAULT); + ASSERT_TRUE(SetThreadLocale(locale_id)); + + const int kAccessDeniedErrorCode = 5; + SetLastError(kAccessDeniedErrorCode); + ASSERT_EQ(GetLastError(), kAccessDeniedErrorCode); + std::wstring value; + TrimWhitespace(win_util::FormatLastWin32Error(), TRIM_ALL, &value); + EXPECT_EQ(std::wstring(L"Access is denied."), value); + + // Manually call the OS function + wchar_t * string_buffer = NULL; + unsigned string_length = + ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, NULL, + kAccessDeniedErrorCode, 0, + reinterpret_cast(&string_buffer), 0, NULL); + + // Verify the call succeeded + ASSERT_TRUE(string_length); + ASSERT_TRUE(string_buffer); + + // Verify the string is the same by different calls + EXPECT_EQ(win_util::FormatLastWin32Error(), std::wstring(string_buffer)); + EXPECT_EQ(win_util::FormatMessage(kAccessDeniedErrorCode), + std::wstring(string_buffer)); + + // Done with the buffer allocated by ::FormatMessage() + LocalFree(string_buffer); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/wmi_util.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/wmi_util.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/wmi_util.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/wmi_util.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,146 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include "base/basictypes.h" +#include "base/scoped_bstr_win.h" +#include "base/scoped_comptr_win.h" + +#pragma comment(lib, "wbemuuid.lib") + +#include "base/wmi_util.h" + +namespace { +// Simple class to manage the lifetime of a variant. +// TODO(tommi): Replace this for a more useful class. +class VariantHelper : public VARIANT { + public: + VariantHelper() { + vt = VT_EMPTY; + } + explicit VariantHelper(VARTYPE type) { + vt = type; + } + ~VariantHelper() { + ::VariantClear(this); + } + private: + DISALLOW_COPY_AND_ASSIGN(VariantHelper); +}; + +} // namespace + +bool WMIUtil::CreateLocalConnection(bool set_blanket, + IWbemServices** wmi_services) { + ScopedComPtr wmi_locator; + HRESULT hr = wmi_locator.CreateInstance(CLSID_WbemLocator, NULL, + CLSCTX_INPROC_SERVER); + if (FAILED(hr)) + return false; + + ScopedComPtr wmi_services_r; + hr = wmi_locator->ConnectServer(StackBstr(L"ROOT\\CIMV2"), NULL, NULL, 0, + NULL, 0, 0, wmi_services_r.Receive()); + if (FAILED(hr)) + return false; + + if (set_blanket) { + hr = ::CoSetProxyBlanket(wmi_services_r, + RPC_C_AUTHN_WINNT, + RPC_C_AUTHZ_NONE, + NULL, + RPC_C_AUTHN_LEVEL_CALL, + RPC_C_IMP_LEVEL_IMPERSONATE, + NULL, + EOAC_NONE); + if (FAILED(hr)) + return false; + } + + *wmi_services = wmi_services_r.Detach(); + return true; +} + +bool WMIUtil::CreateClassMethodObject(IWbemServices* wmi_services, + const std::wstring& class_name, + const std::wstring& method_name, + IWbemClassObject** class_instance) { + // We attempt to instantiate a COM object that represents a WMI object plus + // a method rolled into one entity. + ScopedBstr b_class_name(class_name.c_str()); + ScopedBstr b_method_name(method_name.c_str()); + ScopedComPtr class_object; + HRESULT hr; + hr = wmi_services->GetObject(b_class_name, 0, NULL, + class_object.Receive(), NULL); + if (FAILED(hr)) + return false; + + ScopedComPtr params_def; + hr = class_object->GetMethod(b_method_name, 0, params_def.Receive(), NULL); + if (FAILED(hr)) + return false; + + if (NULL == params_def) { + // You hit this special case if the WMI class is not a CIM class. MSDN + // sometimes tells you this. Welcome to WMI hell. + return false; + } + + hr = params_def->SpawnInstance(0, class_instance); + return(SUCCEEDED(hr)); +} + +bool SetParameter(IWbemClassObject* class_method, + const std::wstring& parameter_name, VARIANT* parameter) { + HRESULT hr = class_method->Put(parameter_name.c_str(), 0, parameter, 0); + return SUCCEEDED(hr); +} + + +// The code in Launch() basically calls the Create Method of the Win32_Process +// CIM class is documented here: +// http://msdn2.microsoft.com/en-us/library/aa389388(VS.85).aspx + +bool WMIProcessUtil::Launch(const std::wstring& command_line, int* process_id) { + ScopedComPtr wmi_local; + if (!WMIUtil::CreateLocalConnection(true, wmi_local.Receive())) + return false; + + const wchar_t class_name[] = L"Win32_Process"; + const wchar_t method_name[] = L"Create"; + ScopedComPtr process_create; + if (!WMIUtil::CreateClassMethodObject(wmi_local, class_name, method_name, + process_create.Receive())) + return false; + + VariantHelper b_command_line(VT_BSTR); + b_command_line.bstrVal = ::SysAllocString(command_line.c_str()); + + if (!SetParameter(process_create, L"CommandLine", &b_command_line)) + return false; + + ScopedComPtr out_params; + HRESULT hr = wmi_local->ExecMethod(StackBstr(class_name), + StackBstr(method_name), 0, NULL, + process_create, out_params.Receive(), + NULL); + if (FAILED(hr)) + return false; + + VariantHelper ret_value; + hr = out_params->Get(L"ReturnValue", 0, &ret_value, NULL, 0); + if (FAILED(hr) || (0 != ret_value.uintVal)) + return false; + + VariantHelper pid; + hr = out_params->Get(L"ProcessId", 0, &pid, NULL, 0); + if (FAILED(hr) || (0 == pid.intVal)) + return false; + + if (process_id) + *process_id = pid.intVal; + + return true; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/wmi_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/wmi_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/wmi_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/wmi_util.h 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,73 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// WMI (Windows Management and Instrumentation) is a big, complex, COM-based +// API that can be used to perform all sorts of things. Sometimes is the best +// way to accomplish something under windows but its lack of an approachable +// C++ interface prevents its use. This collection of fucntions is a step in +// that direction. +// There are two classes; WMIUtil and WMIProcessUtil. The first +// one contain generic helpers and the second one contains the only +// functionality that is needed right now which is to use WMI to launch a +// process. +// To use any function on this header you must call CoInitialize or +// CoInitializeEx beforehand. +// +// For more information about WMI programming: +// http://msdn2.microsoft.com/en-us/library/aa384642(VS.85).aspx + +#ifndef BASE_WMI_UTIL_H__ +#define BASE_WMI_UTIL_H__ + +#include +#include + +class WMIUtil { + public: + // Creates an instance of the WMI service connected to the local computer and + // returns its COM interface. If 'set-blanket' is set to true, the basic COM + // security blanket is applied to the returned interface. This is almost + // always desirable unless you set the parameter to false and apply a custom + // COM security blanket. + // Returns true if succeeded and 'wmi_services': the pointer to the service. + // When done with the interface you must call Release(); + static bool CreateLocalConnection(bool set_blanket, + IWbemServices** wmi_services); + + // Creates a WMI method using from a WMI class named 'class_name' that + // contains a method named 'method_name'. Only WMI classes that are CIM + // classes can be created using this function. + // Returns true if succeeded and 'class_instance' returns a pointer to the + // WMI method that you can fill with parameter values using SetParameter. + // When done with the interface you must call Release(); + static bool CreateClassMethodObject(IWbemServices* wmi_services, + const std::wstring& class_name, + const std::wstring& method_name, + IWbemClassObject** class_instance); + + // Fills a single parameter given an instanced 'class_method'. Returns true + // if operation succeeded. When all the parameters are set the method can + // be executed using IWbemServices::ExecMethod(). + static bool SetParameter(IWbemClassObject* class_method, + const std::wstring& parameter_name, + VARIANT* parameter); +}; + +// This class contains functionality of the WMI class 'Win32_Process' +// more info: http://msdn2.microsoft.com/en-us/library/aa394372(VS.85).aspx +class WMIProcessUtil { + public: + // Creates a new process from 'command_line'. The advantage over CreateProcess + // is that it allows you to always break out from a Job object that the caller + // is attached to even if the Job object flags prevent that. + // Returns true and the process id in process_id if the process is launched + // successful. False otherwise. + // Note that a fully qualified path must be specified in most cases unless + // the program is not in the search path of winmgmt.exe. + // Processes created this way are children of wmiprvse.exe and run with the + // caller credentials. + static bool Launch(const std::wstring& command_line, int* process_id); +}; + +#endif // BASE_WMI_UTIL_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/wmi_util_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/wmi_util_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/wmi_util_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/wmi_util_unittest.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,55 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "base/wmi_util.h" + +TEST(WMIUtilTest, TestLocalConnectionSecurityBlanket) { + ::CoInitialize(NULL); + IWbemServices* services = NULL; + EXPECT_TRUE(WMIUtil::CreateLocalConnection(true, &services)); + ASSERT_TRUE(NULL != services); + ULONG refs = services->Release(); + EXPECT_EQ(refs, 0); + ::CoUninitialize(); +} + +TEST(WMIUtilTest, TestLocalConnectionNoSecurityBlanket) { + ::CoInitialize(NULL); + IWbemServices* services = NULL; + EXPECT_TRUE(WMIUtil::CreateLocalConnection(false, &services)); + ASSERT_TRUE(NULL != services); + ULONG refs = services->Release(); + EXPECT_EQ(refs, 0); + ::CoUninitialize(); +} + +TEST(WMIUtilTest, TestCreateClassMethod) { + ::CoInitialize(NULL); + IWbemServices* wmi_services = NULL; + EXPECT_TRUE(WMIUtil::CreateLocalConnection(true, &wmi_services)); + ASSERT_TRUE(NULL != wmi_services); + IWbemClassObject* class_method = NULL; + EXPECT_TRUE(WMIUtil::CreateClassMethodObject(wmi_services, + L"Win32_ShortcutFile", + L"Rename", &class_method)); + ASSERT_TRUE(NULL != class_method); + ULONG refs = class_method->Release(); + EXPECT_EQ(refs, 0); + refs = wmi_services->Release(); + EXPECT_EQ(refs, 0); + ::CoUninitialize(); +} + +// Creates an instance of cmd which executes 'echo' and exits immediately. +TEST(WMIUtilTest, TestLaunchProcess) { + ::CoInitialize(NULL); + int pid = 0; + bool result = WMIProcessUtil::Launch(L"cmd.exe /c echo excelent!", &pid); + EXPECT_TRUE(result); + EXPECT_GT(pid, 0); + ::CoUninitialize(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/word_iterator.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/word_iterator.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/word_iterator.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/word_iterator.cc 2010-04-16 17:31:51.000000000 +0100 @@ -0,0 +1,87 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/word_iterator.h" + +#include "base/logging.h" +#include "unicode/ubrk.h" +#include "unicode/ustring.h" + +const size_t npos = -1; + +WordIterator::WordIterator(const std::wstring& str, BreakType break_type) + : iter_(NULL), + string_(str), + break_type_(break_type), + prev_(npos), + pos_(0) { +} + +WordIterator::~WordIterator() { + if (iter_) + ubrk_close(iter_); +} + +bool WordIterator::Init() { + UErrorCode status = U_ZERO_ERROR; + UBreakIteratorType break_type; + switch (break_type_) { + case BREAK_WORD: + break_type = UBRK_WORD; + break; + case BREAK_LINE: + break_type = UBRK_LINE; + break; + default: + NOTREACHED(); + break_type = UBRK_LINE; + } +#if defined(WCHAR_T_IS_UTF16) + iter_ = ubrk_open(break_type, NULL, + string_.data(), static_cast(string_.size()), + &status); +#else // WCHAR_T_IS_UTF16 + // When wchar_t is wider than UChar (16 bits), transform |string_| into a + // UChar* string. Size the UChar* buffer to be large enough to hold twice + // as many UTF-16 code points as there are UCS-4 characters, in case each + // character translates to a UTF-16 surrogate pair, and leave room for a NUL + // terminator. + // TODO(avi): avoid this alloc + chars_.resize(string_.length() * sizeof(UChar) + 1); + + UErrorCode error = U_ZERO_ERROR; + int32_t destLength; + u_strFromWCS(&chars_[0], chars_.size(), &destLength, string_.data(), + string_.length(), &error); + + iter_ = ubrk_open(break_type, NULL, &chars_[0], destLength, &status); +#endif + if (U_FAILURE(status)) { + NOTREACHED() << "ubrk_open failed"; + return false; + } + ubrk_first(iter_); // Move the iterator to the beginning of the string. + return true; +} + +bool WordIterator::Advance() { + prev_ = pos_; + const int32_t pos = ubrk_next(iter_); + if (pos == UBRK_DONE) { + pos_ = npos; + return false; + } else { + pos_ = static_cast(pos); + return true; + } +} + +bool WordIterator::IsWord() const { + return (ubrk_getRuleStatus(iter_) != UBRK_WORD_NONE); +} + +std::wstring WordIterator::GetWord() const { + DCHECK(prev_ != npos && pos_ != npos); + return string_.substr(prev_, pos_ - prev_); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/word_iterator.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/word_iterator.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/word_iterator.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/word_iterator.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,87 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_WORD_ITERATOR_H__ +#define BASE_WORD_ITERATOR_H__ + +#include +#include + +#include "unicode/uchar.h" + +#include "base/basictypes.h" + +// The WordIterator class iterates through the words and word breaks +// in a string. (In the string " foo bar! ", the word breaks are at the +// periods in ". .foo. .bar.!. .".) +// +// To extract the words from a string, move a WordIterator through the +// string and test whether IsWord() is true. E.g., +// WordIterator iter(str, WordIterator::BREAK_WORD); +// if (!iter.Init()) return false; +// while (iter.Advance()) { +// if (iter.IsWord()) { +// // region [iter.prev(),iter.pos()) contains a word. +// LOG(INFO) << "word: " << iter.GetWord(); +// } +// } + + +class WordIterator { + public: + enum BreakType { + BREAK_WORD, + BREAK_LINE + }; + + // Requires |str| to live as long as the WordIterator does. + WordIterator(const std::wstring& str, BreakType break_type); + ~WordIterator(); + + // Init() must be called before any of the iterators are valid. + // Returns false if ICU failed to initialize. + bool Init(); + + // Return the current break position within the string, + // or WordIterator::npos when done. + size_t pos() const { return pos_; } + // Return the value of pos() returned before Advance() was last called. + size_t prev() const { return prev_; } + + // Advance to the next break. Returns false if we've run past the end of + // the string. (Note that the very last "word break" is after the final + // character in the string, and when we advance to that position it's the + // last time Advance() returns true.) + bool Advance(); + + // Returns true if the break we just hit is the end of a word. + // (Otherwise, the break iterator just skipped over e.g. whitespace + // or punctuation.) + bool IsWord() const; + + // Return the word between prev() and pos(). + // Advance() must have been called successfully at least once + // for pos() to have advanced to somewhere useful. + std::wstring GetWord() const; + + private: + // ICU iterator. + void* iter_; +#if !defined(WCHAR_T_IS_UTF16) + std::vector chars_; +#endif + + // The string we're iterating over. + const std::wstring& string_; + + // The breaking style (word/line). + BreakType break_type_; + + // Previous and current iterator positions. + size_t prev_, pos_; + + DISALLOW_EVIL_CONSTRUCTORS(WordIterator); +}; + +#endif // BASE_WORD_ITERATOR_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/word_iterator_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/word_iterator_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/word_iterator_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/word_iterator_unittest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,68 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/word_iterator.h" + +#include "testing/gtest/include/gtest/gtest.h" + +TEST(WordIteratorTest, BreakWord) { + std::wstring str(L" foo bar! \npouet boom"); + WordIterator iter(str, WordIterator::BREAK_WORD); + ASSERT_TRUE(iter.Init()); + EXPECT_TRUE(iter.Advance()); + EXPECT_FALSE(iter.IsWord()); + EXPECT_EQ(L" ", iter.GetWord()); + EXPECT_TRUE(iter.Advance()); + EXPECT_TRUE(iter.IsWord()); + EXPECT_EQ(L"foo", iter.GetWord()); + EXPECT_TRUE(iter.Advance()); + EXPECT_FALSE(iter.IsWord()); + EXPECT_EQ(L" ", iter.GetWord()); + EXPECT_TRUE(iter.Advance()); + EXPECT_TRUE(iter.IsWord()); + EXPECT_EQ(L"bar", iter.GetWord()); + EXPECT_TRUE(iter.Advance()); + EXPECT_FALSE(iter.IsWord()); + EXPECT_EQ(L"!", iter.GetWord()); + EXPECT_TRUE(iter.Advance()); + EXPECT_FALSE(iter.IsWord()); + EXPECT_EQ(L" ", iter.GetWord()); + EXPECT_TRUE(iter.Advance()); + EXPECT_FALSE(iter.IsWord()); + EXPECT_EQ(L"\n", iter.GetWord()); + EXPECT_TRUE(iter.Advance()); + EXPECT_TRUE(iter.IsWord()); + EXPECT_EQ(L"pouet", iter.GetWord()); + EXPECT_TRUE(iter.Advance()); + EXPECT_FALSE(iter.IsWord()); + EXPECT_EQ(L" ", iter.GetWord()); + EXPECT_TRUE(iter.Advance()); + EXPECT_TRUE(iter.IsWord()); + EXPECT_EQ(L"boom", iter.GetWord()); + EXPECT_FALSE(iter.Advance()); + EXPECT_FALSE(iter.IsWord()); +} + +TEST(WordIteratorTest, BreakLine) { + std::wstring str(L" foo bar! \npouet boom"); + WordIterator iter(str, WordIterator::BREAK_LINE); + ASSERT_TRUE(iter.Init()); + EXPECT_TRUE(iter.Advance()); + EXPECT_FALSE(iter.IsWord()); + EXPECT_EQ(L" ", iter.GetWord()); + EXPECT_TRUE(iter.Advance()); + EXPECT_FALSE(iter.IsWord()); + EXPECT_EQ(L"foo ", iter.GetWord()); + EXPECT_TRUE(iter.Advance()); + EXPECT_TRUE(iter.IsWord()); + EXPECT_EQ(L"bar! \n", iter.GetWord()); + EXPECT_TRUE(iter.Advance()); + EXPECT_FALSE(iter.IsWord()); + EXPECT_EQ(L"pouet ", iter.GetWord()); + EXPECT_TRUE(iter.Advance()); + EXPECT_FALSE(iter.IsWord()); + EXPECT_EQ(L"boom", iter.GetWord()); + EXPECT_FALSE(iter.Advance()); + EXPECT_FALSE(iter.IsWord()); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/worker_pool.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/worker_pool.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/worker_pool.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/worker_pool.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,24 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_WORKER_POOL_H_ +#define BASE_WORKER_POOL_H_ + +#include "base/tracked.h" + +class Task; + +// This is a facility that runs tasks that don't require a specific thread or +// a message loop. +class WorkerPool { + public: + // This function posts |task| to run on a worker thread. |task_is_slow| + // should be used for tasks that will take a long time to execute. Returns + // false if |task| could not be posted to a worker thread. Regardless of + // return value, ownership of |task| is transferred to the worker pool. + static bool PostTask(const tracked_objects::Location& from_here, + Task* task, bool task_is_slow); +}; + +#endif // BASE_WORKER_POOL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_linux.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_linux.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_linux.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_linux.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,167 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/worker_pool.h" +#include "base/worker_pool_linux.h" + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/platform_thread.h" +#include "base/ref_counted.h" +#include "base/string_util.h" +#include "base/task.h" + +namespace { + +const int kIdleSecondsBeforeExit = 10 * 60; +const int kWorkerThreadStackSize = 64 * 1024; + +class WorkerPoolImpl { + public: + WorkerPoolImpl(); + ~WorkerPoolImpl(); + + void PostTask(const tracked_objects::Location& from_here, Task* task, + bool task_is_slow); + + private: + scoped_refptr pool_; +}; + +WorkerPoolImpl::WorkerPoolImpl() + : pool_(new base::LinuxDynamicThreadPool( + "WorkerPool", kIdleSecondsBeforeExit)) {} + +WorkerPoolImpl::~WorkerPoolImpl() { + pool_->Terminate(); +} + +void WorkerPoolImpl::PostTask(const tracked_objects::Location& from_here, + Task* task, bool task_is_slow) { + task->SetBirthPlace(from_here); + pool_->PostTask(task); +} + +base::LazyInstance g_lazy_worker_pool(base::LINKER_INITIALIZED); + +class WorkerThread : public PlatformThread::Delegate { + public: + WorkerThread(const std::string& name_prefix, int idle_seconds_before_exit, + base::LinuxDynamicThreadPool* pool) + : name_prefix_(name_prefix), + idle_seconds_before_exit_(idle_seconds_before_exit), + pool_(pool) {} + + virtual void ThreadMain(); + + private: + const std::string name_prefix_; + const int idle_seconds_before_exit_; + scoped_refptr pool_; + + DISALLOW_COPY_AND_ASSIGN(WorkerThread); +}; + +void WorkerThread::ThreadMain() { + const std::string name = + StringPrintf("%s/%d", name_prefix_.c_str(), + IntToString(PlatformThread::CurrentId()).c_str()); + PlatformThread::SetName(name.c_str()); + + for (;;) { + Task* task = pool_->WaitForTask(); + if (!task) + break; + task->Run(); + delete task; + } + + // The WorkerThread is non-joinable, so it deletes itself. + delete this; +} + +} // namespace + +bool WorkerPool::PostTask(const tracked_objects::Location& from_here, + Task* task, bool task_is_slow) { + g_lazy_worker_pool.Pointer()->PostTask(from_here, task, task_is_slow); + return true; +} + +namespace base { + +LinuxDynamicThreadPool::LinuxDynamicThreadPool( + const std::string& name_prefix, + int idle_seconds_before_exit) + : name_prefix_(name_prefix), + idle_seconds_before_exit_(idle_seconds_before_exit), + tasks_available_cv_(&lock_), + num_idle_threads_(0), + terminated_(false), + num_idle_threads_cv_(NULL) {} + +LinuxDynamicThreadPool::~LinuxDynamicThreadPool() { + while (!tasks_.empty()) { + Task* task = tasks_.front(); + tasks_.pop(); + delete task; + } +} + +void LinuxDynamicThreadPool::Terminate() { + { + AutoLock locked(lock_); + DCHECK(!terminated_) << "Thread pool is already terminated."; + terminated_ = true; + } + tasks_available_cv_.Broadcast(); +} + +void LinuxDynamicThreadPool::PostTask(Task* task) { + AutoLock locked(lock_); + DCHECK(!terminated_) << + "This thread pool is already terminated. Do not post new tasks."; + + tasks_.push(task); + + // We have enough worker threads. + if (static_cast(num_idle_threads_) >= tasks_.size()) { + tasks_available_cv_.Signal(); + } else { + // The new PlatformThread will take ownership of the WorkerThread object, + // which will delete itself on exit. + WorkerThread* worker = + new WorkerThread(name_prefix_, idle_seconds_before_exit_, this); + PlatformThread::CreateNonJoinable(kWorkerThreadStackSize, worker); + } +} + +Task* LinuxDynamicThreadPool::WaitForTask() { + AutoLock locked(lock_); + + if (terminated_) + return NULL; + + if (tasks_.empty()) { // No work available, wait for work. + num_idle_threads_++; + if (num_idle_threads_cv_.get()) + num_idle_threads_cv_->Signal(); + tasks_available_cv_.TimedWait( + TimeDelta::FromSeconds(kIdleSecondsBeforeExit)); + num_idle_threads_--; + if (num_idle_threads_cv_.get()) + num_idle_threads_cv_->Signal(); + if (tasks_.empty()) { + // We waited for work, but there's still no work. Return NULL to signal + // the thread to terminate. + return NULL; + } + } + + Task* task = tasks_.front(); + tasks_.pop(); + return task; +} + +} // namespace base diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_linux.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_linux.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_linux.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_linux.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,88 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// The thread pool used in the Linux implementation of WorkerPool dynamically +// adds threads as necessary to handle all tasks. It keeps old threads around +// for a period of time to allow them to be reused. After this waiting period, +// the threads exit. This thread pool uses non-joinable threads, therefore +// worker threads are not joined during process shutdown. This means that +// potentially long running tasks (such as DNS lookup) do not block process +// shutdown, but also means that process shutdown may "leak" objects. Note that +// although LinuxDynamicThreadPool spawns the worker threads and manages the +// task queue, it does not own the worker threads. The worker threads ask the +// LinuxDynamicThreadPool for work and eventually clean themselves up. The +// worker threads all maintain scoped_refptrs to the LinuxDynamicThreadPool +// instance, which prevents LinuxDynamicThreadPool from disappearing before all +// worker threads exit. The owner of LinuxDynamicThreadPool should likewise +// maintain a scoped_refptr to the LinuxDynamicThreadPool instance. +// +// NOTE: The classes defined in this file are only meant for use by the Linux +// implementation of WorkerPool. No one else should be using these classes. +// These symbols are exported in a header purely for testing purposes. + +#ifndef BASE_WORKER_POOL_LINUX_H_ +#define BASE_WORKER_POOL_LINUX_H_ + +#include +#include + +#include "base/basictypes.h" +#include "base/condition_variable.h" +#include "base/lock.h" +#include "base/platform_thread.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" + +class Task; + +namespace base { + +class LinuxDynamicThreadPool + : public RefCountedThreadSafe { + public: + class LinuxDynamicThreadPoolPeer; + + // All worker threads will share the same |name_prefix|. They will exit after + // |idle_seconds_before_exit|. + LinuxDynamicThreadPool(const std::string& name_prefix, + int idle_seconds_before_exit); + ~LinuxDynamicThreadPool(); + + // Indicates that the thread pool is going away. Stops handing out tasks to + // worker threads. Wakes up all the idle threads to let them exit. + void Terminate(); + + // Adds |task| to the thread pool. LinuxDynamicThreadPool assumes ownership + // of |task|. + void PostTask(Task* task); + + // Worker thread method to wait for up to |idle_seconds_before_exit| for more + // work from the thread pool. Returns NULL if no work is available. + Task* WaitForTask(); + + private: + friend class LinuxDynamicThreadPoolPeer; + + const std::string name_prefix_; + const int idle_seconds_before_exit_; + + Lock lock_; // Protects all the variables below. + + // Signal()s worker threads to let them know more tasks are available. + // Also used for Broadcast()'ing to worker threads to let them know the pool + // is being deleted and they can exit. + ConditionVariable tasks_available_cv_; + int num_idle_threads_; + std::queue tasks_; + bool terminated_; + // Only used for tests to ensure correct thread ordering. It will always be + // NULL in non-test code. + scoped_ptr num_idle_threads_cv_; + + DISALLOW_COPY_AND_ASSIGN(LinuxDynamicThreadPool); +}; + +} // namespace base + +#endif // BASE_WORKER_POOL_LINUX_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_linux_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_linux_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_linux_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_linux_unittest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,268 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/worker_pool_linux.h" + +#include + +#include "base/condition_variable.h" +#include "base/lock.h" +#include "base/platform_thread.h" +#include "base/task.h" +#include "base/waitable_event.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +// Peer class to provide passthrough access to LinuxDynamicThreadPool internals. +class LinuxDynamicThreadPool::LinuxDynamicThreadPoolPeer { + public: + explicit LinuxDynamicThreadPoolPeer(LinuxDynamicThreadPool* pool) + : pool_(pool) {} + + Lock* lock() { return &pool_->lock_; } + ConditionVariable* tasks_available_cv() { + return &pool_->tasks_available_cv_; + } + const std::queue& tasks() const { return pool_->tasks_; } + int num_idle_threads() const { return pool_->num_idle_threads_; } + ConditionVariable* num_idle_threads_cv() { + return pool_->num_idle_threads_cv_.get(); + } + void set_num_idle_threads_cv(ConditionVariable* cv) { + pool_->num_idle_threads_cv_.reset(cv); + } + + private: + LinuxDynamicThreadPool* pool_; + + DISALLOW_COPY_AND_ASSIGN(LinuxDynamicThreadPoolPeer); +}; + +} // namespace base + +namespace { + +// IncrementingTask's main purpose is to increment a counter. It also updates a +// set of unique thread ids, and signals a ConditionVariable on completion. +// Note that since it does not block, there is no way to control the number of +// threads used if more than one IncrementingTask is consecutively posted to the +// thread pool, since the first one might finish executing before the subsequent +// PostTask() calls get invoked. +class IncrementingTask : public Task { + public: + IncrementingTask(Lock* counter_lock, + int* counter, + Lock* unique_threads_lock, + std::set* unique_threads) + : counter_lock_(counter_lock), + unique_threads_lock_(unique_threads_lock), + unique_threads_(unique_threads), + counter_(counter) {} + + virtual void Run() { + AddSelfToUniqueThreadSet(); + AutoLock locked(*counter_lock_); + (*counter_)++; + } + + void AddSelfToUniqueThreadSet() { + AutoLock locked(*unique_threads_lock_); + unique_threads_->insert(PlatformThread::CurrentId()); + } + + private: + Lock* counter_lock_; + Lock* unique_threads_lock_; + std::set* unique_threads_; + int* counter_; + + DISALLOW_COPY_AND_ASSIGN(IncrementingTask); +}; + +// BlockingIncrementingTask is a simple wrapper around IncrementingTask that +// allows for waiting at the start of Run() for a WaitableEvent to be signalled. +class BlockingIncrementingTask : public Task { + public: + BlockingIncrementingTask(Lock* counter_lock, + int* counter, + Lock* unique_threads_lock, + std::set* unique_threads, + Lock* num_waiting_to_start_lock, + int* num_waiting_to_start, + ConditionVariable* num_waiting_to_start_cv, + base::WaitableEvent* start) + : incrementer_( + counter_lock, counter, unique_threads_lock, unique_threads), + num_waiting_to_start_lock_(num_waiting_to_start_lock), + num_waiting_to_start_(num_waiting_to_start), + num_waiting_to_start_cv_(num_waiting_to_start_cv), + start_(start) {} + + virtual void Run() { + { + AutoLock num_waiting_to_start_locked(*num_waiting_to_start_lock_); + (*num_waiting_to_start_)++; + } + num_waiting_to_start_cv_->Signal(); + CHECK(start_->Wait()); + incrementer_.Run(); + } + + private: + IncrementingTask incrementer_; + Lock* num_waiting_to_start_lock_; + int* num_waiting_to_start_; + ConditionVariable* num_waiting_to_start_cv_; + base::WaitableEvent* start_; + + DISALLOW_COPY_AND_ASSIGN(BlockingIncrementingTask); +}; + +class LinuxDynamicThreadPoolTest : public testing::Test { + protected: + LinuxDynamicThreadPoolTest() + : pool_(new base::LinuxDynamicThreadPool("dynamic_pool", 60*60)), + peer_(pool_.get()), + counter_(0), + num_waiting_to_start_(0), + num_waiting_to_start_cv_(&num_waiting_to_start_lock_), + start_(true, false) {} + + virtual void SetUp() { + peer_.set_num_idle_threads_cv(new ConditionVariable(peer_.lock())); + } + + virtual void TearDown() { + // Wake up the idle threads so they can terminate. + if (pool_.get()) pool_->Terminate(); + } + + void WaitForTasksToStart(int num_tasks) { + AutoLock num_waiting_to_start_locked(num_waiting_to_start_lock_); + while (num_waiting_to_start_ < num_tasks) { + num_waiting_to_start_cv_.Wait(); + } + } + + void WaitForIdleThreads(int num_idle_threads) { + AutoLock pool_locked(*peer_.lock()); + while (peer_.num_idle_threads() < num_idle_threads) { + peer_.num_idle_threads_cv()->Wait(); + } + } + + Task* CreateNewIncrementingTask() { + return new IncrementingTask(&counter_lock_, &counter_, + &unique_threads_lock_, &unique_threads_); + } + + Task* CreateNewBlockingIncrementingTask() { + return new BlockingIncrementingTask( + &counter_lock_, &counter_, &unique_threads_lock_, &unique_threads_, + &num_waiting_to_start_lock_, &num_waiting_to_start_, + &num_waiting_to_start_cv_, &start_); + } + + scoped_refptr pool_; + base::LinuxDynamicThreadPool::LinuxDynamicThreadPoolPeer peer_; + Lock counter_lock_; + int counter_; + Lock unique_threads_lock_; + std::set unique_threads_; + Lock num_waiting_to_start_lock_; + int num_waiting_to_start_; + ConditionVariable num_waiting_to_start_cv_; + base::WaitableEvent start_; +}; + +TEST_F(LinuxDynamicThreadPoolTest, Basic) { + EXPECT_EQ(0, peer_.num_idle_threads()); + EXPECT_EQ(0U, unique_threads_.size()); + EXPECT_EQ(0U, peer_.tasks().size()); + + // Add one task and wait for it to be completed. + pool_->PostTask(CreateNewIncrementingTask()); + + WaitForIdleThreads(1); + + EXPECT_EQ(1U, unique_threads_.size()) << + "There should be only one thread allocated for one task."; + EXPECT_EQ(1, peer_.num_idle_threads()); + EXPECT_EQ(1, counter_); +} + +TEST_F(LinuxDynamicThreadPoolTest, ReuseIdle) { + // Add one task and wait for it to be completed. + pool_->PostTask(CreateNewIncrementingTask()); + + WaitForIdleThreads(1); + + // Add another 2 tasks. One should reuse the existing worker thread. + pool_->PostTask(CreateNewBlockingIncrementingTask()); + pool_->PostTask(CreateNewBlockingIncrementingTask()); + + WaitForTasksToStart(2); + start_.Signal(); + WaitForIdleThreads(2); + + EXPECT_EQ(2U, unique_threads_.size()); + EXPECT_EQ(2, peer_.num_idle_threads()); + EXPECT_EQ(3, counter_); +} + +TEST_F(LinuxDynamicThreadPoolTest, TwoActiveTasks) { + // Add two blocking tasks. + pool_->PostTask(CreateNewBlockingIncrementingTask()); + pool_->PostTask(CreateNewBlockingIncrementingTask()); + + EXPECT_EQ(0, counter_) << "Blocking tasks should not have started yet."; + + WaitForTasksToStart(2); + start_.Signal(); + WaitForIdleThreads(2); + + EXPECT_EQ(2U, unique_threads_.size()); + EXPECT_EQ(2, peer_.num_idle_threads()) << "Existing threads are now idle."; + EXPECT_EQ(2, counter_); +} + +TEST_F(LinuxDynamicThreadPoolTest, Complex) { + // Add two non blocking tasks and wait for them to finish. + pool_->PostTask(CreateNewIncrementingTask()); + + WaitForIdleThreads(1); + + // Add two blocking tasks, start them simultaneously, and wait for them to + // finish. + pool_->PostTask(CreateNewBlockingIncrementingTask()); + pool_->PostTask(CreateNewBlockingIncrementingTask()); + + WaitForTasksToStart(2); + start_.Signal(); + WaitForIdleThreads(2); + + EXPECT_EQ(3, counter_); + EXPECT_EQ(2, peer_.num_idle_threads()); + EXPECT_EQ(2U, unique_threads_.size()); + + // Wake up all idle threads so they can exit. + { + AutoLock locked(*peer_.lock()); + while (peer_.num_idle_threads() > 0) { + peer_.tasks_available_cv()->Signal(); + peer_.num_idle_threads_cv()->Wait(); + } + } + + // Add another non blocking task. There are no threads to reuse. + pool_->PostTask(CreateNewIncrementingTask()); + WaitForIdleThreads(1); + + EXPECT_EQ(3U, unique_threads_.size()); + EXPECT_EQ(1, peer_.num_idle_threads()); + EXPECT_EQ(4, counter_); +} + +} // namespace diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_mac.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_mac.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_mac.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_mac.mm 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,72 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/worker_pool.h" + +#import + +#include "base/logging.h" +#import "base/singleton_objc.h" +#include "base/task.h" + +// TaskOperation adapts Task->Run() for use in an NSOperationQueue. +@interface TaskOperation : NSOperation { + @private + Task* task_; // (strong) +} + +// Returns an autoreleased instance of TaskOperation. See -initWithTask: for +// details. ++ (id)taskOperationWithTask:(Task*)task; + +// Designated initializer. |task| is adopted as the Task* whose Run method +// this operation will call when executed. +- (id)initWithTask:(Task*)task; + +@end + +@implementation TaskOperation + ++ (id)taskOperationWithTask:(Task*)task { + return [[[TaskOperation alloc] initWithTask:task] autorelease]; +} + +- (id)init { + return [self initWithTask:NULL]; +} + +- (id)initWithTask:(Task*)task { + if ((self = [super init])) { + task_ = task; + } + return self; +} + +- (void)main { + DCHECK(task_) << "-[TaskOperation main] called with no task"; + task_->Run(); + delete task_; + task_ = NULL; +} + +- (void)dealloc { + DCHECK(!task_) << "-[TaskOperation dealloc] called on unused TaskOperation"; + delete task_; + [super dealloc]; +} + +@end + +bool WorkerPool::PostTask(const tracked_objects::Location& from_here, + Task* task, bool task_is_slow) { + // Ignore |task_is_slow|, it doesn't map directly to any tunable aspect of + // an NSOperation. + + task->SetBirthPlace(from_here); + + NSOperationQueue* operation_queue = SingletonObjC::get(); + [operation_queue addOperation:[TaskOperation taskOperationWithTask:task]]; + + return true; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_unittest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,44 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/task.h" +#include "base/waitable_event.h" +#include "base/worker_pool.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/platform_test.h" + +using base::WaitableEvent; + +typedef PlatformTest WorkerPoolTest; + +namespace { + +class PostTaskTestTask : public Task { + public: + PostTaskTestTask(WaitableEvent* event) : event_(event) { + } + + void Run() { + event_->Signal(); + } + + private: + WaitableEvent* event_; +}; + +TEST_F(WorkerPoolTest, PostTask) { + WaitableEvent test_event(false, false); + WaitableEvent long_test_event(false, false); + bool signaled; + + WorkerPool::PostTask(FROM_HERE, new PostTaskTestTask(&test_event), false); + WorkerPool::PostTask(FROM_HERE, new PostTaskTestTask(&long_test_event), true); + + signaled = test_event.Wait(); + EXPECT_TRUE(signaled); + signaled = long_test_event.Wait(); + EXPECT_TRUE(signaled); +} + +} // namespace diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/base/worker_pool_win.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,36 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/worker_pool.h" + +#include "base/logging.h" +#include "base/task.h" + +namespace { + +DWORD CALLBACK WorkItemCallback(void* param) { + Task* task = static_cast(param); + task->Run(); + delete task; + return 0; +} + +} // namespace + +bool WorkerPool::PostTask(const tracked_objects::Location& from_here, + Task* task, bool task_is_slow) { + task->SetBirthPlace(from_here); + + ULONG flags = 0; + if (task_is_slow) + flags |= WT_EXECUTELONGFUNCTION; + + if (!QueueUserWorkItem(WorkItemCallback, task, flags)) { + DLOG(ERROR) << "QueueUserWorkItem failed: " << GetLastError(); + delete task; + return false; + } + + return true; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/build/build_config.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/build/build_config.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/build/build_config.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/build/build_config.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,87 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file adds defines about the platform we're currently building on. +// Operating System: +// OS_WIN / OS_MACOSX / OS_LINUX / OS_POSIX (MACOSX or LINUX) +// Compiler: +// COMPILER_MSVC / COMPILER_GCC +// Processor: +// ARCH_CPU_X86 / ARCH_CPU_X86_64 / ARCH_CPU_X86_FAMILY (X86 or X86_64) +// ARCH_CPU_32_BITS / ARCH_CPU_64_BITS + +#ifndef BUILD_BUILD_CONFIG_H_ +#define BUILD_BUILD_CONFIG_H_ + +// A set of macros to use for platform detection. +#if defined(__APPLE__) +#define OS_MACOSX 1 +#elif defined(__linux__) +#define OS_LINUX 1 +#elif defined(_WIN32) +#define OS_WIN 1 +#else +#error Please add support for your platform in build/build_config.h +#endif + +// For access to standard POSIX features, use OS_POSIX instead of a more +// specific macro. +#if defined(OS_MACOSX) || defined(OS_LINUX) +#define OS_POSIX 1 +#endif + +// Compiler detection. +#if defined(__GNUC__) +#define COMPILER_GCC 1 +#elif defined(_MSC_VER) +#define COMPILER_MSVC 1 +#else +#error Please add support for your compiler in build/build_config.h +#endif + +// Processor architecture detection. For more info on what's defined, see: +// http://msdn.microsoft.com/en-us/library/b0084kay.aspx +// http://www.agner.org/optimize/calling_conventions.pdf +// or with gcc, run: "echo | gcc -E -dM -" +#if defined(_M_X64) || defined(__x86_64__) +#define ARCH_CPU_X86_FAMILY 1 +#define ARCH_CPU_X86_64 1 +#define ARCH_CPU_64_BITS 1 +#elif defined(_M_IX86) || defined(__i386__) +#define ARCH_CPU_X86_FAMILY 1 +#define ARCH_CPU_X86 1 +#define ARCH_CPU_32_BITS 1 +#elif defined(__ARMEL__) +#define ARCH_CPU_ARM_FAMILY 1 +#define ARCH_CPU_ARMEL 1 +#define ARCH_CPU_32_BITS 1 +#define WCHAR_T_IS_UNSIGNED 1 +#else +#error Please add support for your architecture in build/build_config.h +#endif + +// Type detection for wchar_t. +#ifndef CHROMIUM_MOZILLA_BUILD + +#if defined(OS_WIN) +#define WCHAR_T_IS_UTF16 +#elif defined(OS_POSIX) && defined(COMPILER_GCC) && \ + defined(__WCHAR_MAX__) && \ + (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff) +#define WCHAR_T_IS_UTF32 +#else +#error Please add support for your compiler in build/build_config.h +#endif + +#else // CHROMIUM_MOZILLA_BUILD + +#if defined(OS_WIN) +#define WCHAR_T_IS_UTF16 +#else +#define WCHAR_T_IS_UTF32 +#endif + +#endif // CHROMIUM_MOZILLA_BUILD + +#endif // BUILD_BUILD_CONFIG_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/accessibility_types.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/accessibility_types.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/accessibility_types.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/accessibility_types.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,48 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_ACCESSIBILITY_TYPES_H_ +#define CHROME_COMMON_ACCESSIBILITY_TYPES_H_ + +//////////////////////////////////////////////////////////////////////////////// +// +// AccessibilityTypes +// +// Provides enumerations used to preserve platform-independence in accessibility +// functions used in various Views, both in Browser\Views and Views. +// +//////////////////////////////////////////////////////////////////////////////// +class AccessibilityTypes { + public: + // This defines an enumeration of the supported accessibility roles in our + // Views (e.g. used in View::GetAccessibleRole). Any interface using roles + // must provide a conversion to its own roles (see e.g. + // ViewAccessibility::get_accRole and ViewAccessibility::MSAARole). + enum Role { + ROLE_APPLICATION, + ROLE_BUTTONDROPDOWN, + ROLE_CLIENT, + ROLE_GROUPING, + ROLE_PAGETAB, + ROLE_PUSHBUTTON, + ROLE_TEXT, + ROLE_TOOLBAR + }; + + // This defines an enumeration of the supported accessibility roles in our + // Views (e.g. used in View::GetAccessibleState). Any interface using roles + // must provide a conversion to its own roles (see e.g. + // ViewAccessibility::get_accState and ViewAccessibility::MSAAState). + enum State { + STATE_HASPOPUP, + STATE_READONLY + }; + + private: + // Do not instantiate this class. + AccessibilityTypes() {} + ~AccessibilityTypes() {} +}; + +#endif // CHROME_COMMON_ACCESSIBILITY_TYPES_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_context_impl.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_context_impl.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_context_impl.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_context_impl.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,84 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/app_cache/app_cache_context_impl.h" + +#include "base/logging.h" +#include "chrome/common/render_messages.h" +#include "chrome/common/child_thread.h" +#include "googleurl/src/gurl.h" + +IDMap AppCacheContextImpl::all_contexts; + +// static +AppCacheContextImpl* AppCacheContextImpl::FromContextId(int id) { + return all_contexts.Lookup(id); +} + +AppCacheContextImpl::AppCacheContextImpl(IPC::Message::Sender *sender) + : context_id_(kNoAppCacheContextId), + app_cache_id_(kUnknownAppCacheId), + pending_select_request_id_(0), + sender_(sender) { + DCHECK(sender_); +} + +AppCacheContextImpl::~AppCacheContextImpl() { + UnInitializeContext(); +} + +void AppCacheContextImpl::Initialize(ContextType context_type, + WebAppCacheContext *parent) { + DCHECK(context_id_ == kNoAppCacheContextId); + DCHECK(((context_type == MAIN_FRAME) && !parent) || + ((context_type != MAIN_FRAME) && parent)); + + context_id_ = all_contexts.Add(this); + CHECK(context_id_ != kNoAppCacheContextId); + + sender_->Send(new AppCacheMsg_ContextCreated(context_type, + context_id_, + parent ? parent->GetContextID() + : kNoAppCacheContextId)); +} + +void AppCacheContextImpl::UnInitializeContext() { + if (context_id_ != kNoAppCacheContextId) { + sender_->Send(new AppCacheMsg_ContextDestroyed(context_id_)); + all_contexts.Remove(context_id_); + context_id_ = kNoAppCacheContextId; + } +} + +void AppCacheContextImpl::SelectAppCacheWithoutManifest( + const GURL &document_url, + int64 cache_document_was_loaded_from) { + DCHECK(context_id_ != kNoAppCacheContextId); + app_cache_id_ = kUnknownAppCacheId; // unknown until we get a response + sender_->Send(new AppCacheMsg_SelectAppCache( + context_id_, ++pending_select_request_id_, + document_url, cache_document_was_loaded_from, + GURL::EmptyGURL())); +} + +void AppCacheContextImpl::SelectAppCacheWithManifest( + const GURL &document_url, + int64 cache_document_was_loaded_from, + const GURL &manifest_url) { + DCHECK(context_id_ != kNoAppCacheContextId); + app_cache_id_ = kUnknownAppCacheId; // unknown until we get a response + sender_->Send(new AppCacheMsg_SelectAppCache( + context_id_, ++pending_select_request_id_, + document_url, cache_document_was_loaded_from, + manifest_url)); +} + +void AppCacheContextImpl::OnAppCacheSelected(int select_request_id, + int64 app_cache_id) { + if (select_request_id == pending_select_request_id_) { + DCHECK(app_cache_id_ == kUnknownAppCacheId); + DCHECK(app_cache_id != kUnknownAppCacheId); + app_cache_id_ = app_cache_id; + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_context_impl.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_context_impl.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_context_impl.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_context_impl.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,48 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_APP_CACHE_APP_CACHE_CONTEXT_IMPL_H_ +#define CHROME_COMMON_APP_CACHE_APP_CACHE_CONTEXT_IMPL_H_ + +#include "base/id_map.h" +#include "chrome/common/ipc_message.h" +#include "webkit/glue/webappcachecontext.h" + +// A concrete implemenation of WebAppCacheContext for use in a child process. +class AppCacheContextImpl : public WebAppCacheContext { + public: + // Returns the context having given id or NULL if there is no such context. + static AppCacheContextImpl* FromContextId(int id); + + AppCacheContextImpl(IPC::Message::Sender* sender); + virtual ~AppCacheContextImpl(); + + // WebAppCacheContext implementation + virtual int GetContextID() { return context_id_; } + virtual int64 GetAppCacheID() { return app_cache_id_; } + virtual void Initialize(WebAppCacheContext::ContextType context_type, + WebAppCacheContext* opt_parent); + virtual void SelectAppCacheWithoutManifest( + const GURL& document_url, + int64 cache_document_was_loaded_from); + virtual void SelectAppCacheWithManifest( + const GURL& document_url, + int64 cache_document_was_loaded_from, + const GURL& manifest_url); + + // Called by AppCacheDispatcher when the browser has selected an appcache. + void OnAppCacheSelected(int select_request_id, int64 app_cache_id); + + private: + void UnInitializeContext(); + + int context_id_; + int64 app_cache_id_; + int pending_select_request_id_; + IPC::Message::Sender* sender_; + + static IDMap all_contexts; +}; + +#endif // CHROME_COMMON_APP_CACHE_APP_CACHE_CONTEXT_IMPL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_dispatcher.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_dispatcher.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_dispatcher.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_dispatcher.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,26 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/app_cache/app_cache_dispatcher.h" + +#include "chrome/common/app_cache/app_cache_context_impl.h" +#include "chrome/common/render_messages.h" + +bool AppCacheDispatcher::OnMessageReceived(const IPC::Message& msg) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(AppCacheDispatcher, msg) + IPC_MESSAGE_HANDLER(AppCacheMsg_AppCacheSelected, OnAppCacheSelected) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void AppCacheDispatcher::OnAppCacheSelected(int context_id, + int select_request_id, + int64 app_cache_id) { + AppCacheContextImpl *context = AppCacheContextImpl::FromContextId(context_id); + if (context) { + context->OnAppCacheSelected(select_request_id, app_cache_id); + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_dispatcher.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_dispatcher.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_dispatcher.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_dispatcher.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,26 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_APP_CACHE_APP_CACHE_DISPATCHER_H_ +#define CHROME_COMMON_APP_CACHE_APP_CACHE_DISPATCHER_H_ + +#include "base/basictypes.h" +#include "chrome/common/ipc_message.h" + +// Dispatches app cache related messages sent to a child process from the +// main browser process. There is one instance per child process. Messages +// are dispatched on the main child thread. The ChildThread base class +// creates an instance and delegates calls to it. +class AppCacheDispatcher { + public: + bool OnMessageReceived(const IPC::Message& msg); + + private: + // AppCacheContextImpl related messages + void OnAppCacheSelected(int context_id, + int select_request_id, + int64 app_cache_id); +}; + +#endif // CHROME_COMMON_APP_CACHE_APP_CACHE_DISPATCHER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_dispatcher_host.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_dispatcher_host.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_dispatcher_host.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_dispatcher_host.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,60 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/app_cache/app_cache_dispatcher_host.h" + +#include "chrome/common/render_messages.h" + +AppCacheDispatcherHost::~AppCacheDispatcherHost() { + if (sender_) { + // TODO(michaeln): plumb to request_context_->app_cache_service + // to remove contexts for the child process that is going away. + } +} + +void AppCacheDispatcherHost::Initialize(IPC::Message::Sender* sender) { + DCHECK(sender); + sender_ = sender; + // TODO(michaeln): plumb to request_context_->app_cache_service to + // tell it about this child process coming into existance +} + +bool AppCacheDispatcherHost::OnMessageReceived(const IPC::Message& msg, + bool *msg_ok) { + DCHECK(sender_); + *msg_ok = true; + bool handled = true; + IPC_BEGIN_MESSAGE_MAP_EX(AppCacheDispatcherHost, msg, *msg_ok) + IPC_MESSAGE_HANDLER(AppCacheMsg_ContextCreated, OnContextCreated); + IPC_MESSAGE_HANDLER(AppCacheMsg_ContextDestroyed, OnContextDestroyed); + IPC_MESSAGE_HANDLER(AppCacheMsg_SelectAppCache, OnSelectAppCache); + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP_EX() + return handled; +} + +void AppCacheDispatcherHost::OnContextCreated( + WebAppCacheContext::ContextType context_type, + int context_id, + int opt_parent_id) { + // TODO(michaeln): implement me, plumb to request_context->app_cache_service + DCHECK(context_id != WebAppCacheContext::kNoAppCacheContextId); +} + +void AppCacheDispatcherHost::OnContextDestroyed(int context_id) { + // TODO(michaeln): implement me, plumb to request_context->app_cache_service + DCHECK(context_id != WebAppCacheContext::kNoAppCacheContextId); +} + +void AppCacheDispatcherHost::OnSelectAppCache( + int context_id, + int select_request_id, + const GURL& document_url, + int64 cache_document_was_loaded_from, + const GURL& opt_manifest_url) { + // TODO(michaeln): implement me, plumb to request_context->app_cache_service + DCHECK(context_id != WebAppCacheContext::kNoAppCacheContextId); + Send(new AppCacheMsg_AppCacheSelected(context_id, select_request_id, + WebAppCacheContext::kNoAppCacheId)); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_dispatcher_host.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_dispatcher_host.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_dispatcher_host.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/app_cache/app_cache_dispatcher_host.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,43 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_APP_CACHE_APP_CACHE_DISPATCHER_HOST_H_ +#define CHROME_COMMON_APP_CACHE_APP_CACHE_DISPATCHER_HOST_H_ + +#include "base/id_map.h" +#include "chrome/common/ipc_message.h" +#include "webkit/glue/webappcachecontext.h" + +class GURL; + +// Handles app cache related messages sent to the main browser process from +// its child processes. There is a distinct host for each child process. +// Messages are handled on the IO thread. The ResourceMessageFilter creates +// an instance and delegates calls to it. +class AppCacheDispatcherHost { + public: + AppCacheDispatcherHost() : sender_(NULL) {} + ~AppCacheDispatcherHost(); + void Initialize(IPC::Message::Sender* sender); + bool OnMessageReceived(const IPC::Message& msg, bool* msg_is_ok); + + private: + // AppCacheContextImpl related messages + void OnContextCreated(WebAppCacheContext::ContextType context_type, + int context_id, int opt_parent_id); + void OnContextDestroyed(int context_id); + void OnSelectAppCache(int context_id, + int select_request_id, + const GURL& document_url, + int64 cache_document_was_loaded_from, + const GURL& opt_manifest_url); + + bool Send(IPC::Message* msg) { + return sender_->Send(msg); + } + + IPC::Message::Sender* sender_; +}; + +#endif // CHROME_COMMON_APP_CACHE_APP_CACHE_DISPATCHER_HOST_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/bindings_policy.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/bindings_policy.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/bindings_policy.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/bindings_policy.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,42 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_BINDINGS_POLICY_H__ +#define CHROME_COMMON_BINDINGS_POLICY_H__ + +// This is a utility class that specifies flag values for the types of +// JavaScript bindings exposed to renderers. +class BindingsPolicy { + public: + enum { + // HTML-based UI bindings that allows he js content to send JSON-encoded + // data back to the browser process. + DOM_UI = 1 << 0, + // DOM automation bindings that allows the js content to send JSON-encoded + // data back to automation in the parent process. (By default this isn't + // allowed unless the app has been started up with the --dom-automation + // switch.) + DOM_AUTOMATION = 1 << 1, + // Bindings that allow access to the external host (through automation). + EXTERNAL_HOST = 1 << 2, + // Special bindings with privileged APIs for code running in the extension + // process. + EXTENSION = 1 << 3, + }; + + static bool is_dom_ui_enabled(int flags) { + return (flags & DOM_UI) != 0; + } + static bool is_dom_automation_enabled(int flags) { + return (flags & DOM_AUTOMATION) != 0; + } + static bool is_external_host_enabled(int flags) { + return (flags & EXTERNAL_HOST) != 0; + } + static bool is_extension_enabled(int flags) { + return (flags & EXTENSION) != 0; + } +}; + +#endif // CHROME_COMMON_BINDINGS_POLICY_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/bzip2_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/bzip2_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/bzip2_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/bzip2_unittest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,67 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/bzip2/bzlib.h" + +namespace { + class Bzip2Test : public testing::Test { + }; +}; + +// This test does a simple round trip to test that the bzip2 library is +// present and working. +TEST(Bzip2Test, Roundtrip) { + char input[] = "Test Data, More Test Data, Even More Data of Test"; + char output[256]; + + memset(output, 0, arraysize(output)); + + bz_stream stream; + stream.bzalloc = NULL; + stream.bzfree = NULL; + stream.opaque = NULL; + int result = BZ2_bzCompressInit(&stream, + 9, // 900k block size + 0, // quiet + 0); // default work factor + ASSERT_EQ(BZ_OK, result); + + stream.next_in = input; + stream.avail_in = arraysize(input) - 1; + stream.next_out = output; + stream.avail_out = arraysize(output); + do { + result = BZ2_bzCompress(&stream, BZ_FINISH); + } while (result == BZ_FINISH_OK); + ASSERT_EQ(BZ_STREAM_END, result); + result = BZ2_bzCompressEnd(&stream); + ASSERT_EQ(BZ_OK, result); + int written = stream.total_out_lo32; + + // Make sure we wrote something; otherwise not sure what to expect + ASSERT_GT(written, 0); + + // Now decompress and check that we got the same thing. + result = BZ2_bzDecompressInit(&stream, 0, 0); + ASSERT_EQ(BZ_OK, result); + char output2[256]; + memset(output2, 0, arraysize(output2)); + + stream.next_in = output; + stream.avail_in = written; + stream.next_out = output2; + stream.avail_out = arraysize(output2); + + do { + result = BZ2_bzDecompress(&stream); + } while (result == BZ_OK); + ASSERT_EQ(result, BZ_STREAM_END); + result = BZ2_bzDecompressEnd(&stream); + ASSERT_EQ(result, BZ_OK); + + EXPECT_EQ(arraysize(input) - 1, stream.total_out_lo32); + EXPECT_STREQ(input, output2); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,59 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/child_process.h" + +#include "base/basictypes.h" +#include "base/string_util.h" +#include "chrome/common/child_thread.h" + +ChildProcess* ChildProcess::child_process_; + +ChildProcess::ChildProcess(ChildThread* child_thread) + : child_thread_(child_thread), + ref_count_(0), + shutdown_event_(true, false) { + DCHECK(!child_process_); + child_process_ = this; + if (child_thread_.get()) // null in unittests. + child_thread_->Run(); +} + +ChildProcess::~ChildProcess() { + DCHECK(child_process_ == this); + + // Signal this event before destroying the child process. That way all + // background threads can cleanup. + // For example, in the renderer the RenderThread instances will be able to + // notice shutdown before the render process begins waiting for them to exit. + shutdown_event_.Signal(); + + if (child_thread_.get()) + child_thread_->Stop(); + + child_process_ = NULL; +} + +void ChildProcess::AddRefProcess() { + DCHECK(!child_thread_.get() || // null in unittests. + MessageLoop::current() == child_thread_->message_loop()); + ref_count_++; +} + +void ChildProcess::ReleaseProcess() { + DCHECK(!child_thread_.get() || // null in unittests. + MessageLoop::current() == child_thread_->message_loop()); + DCHECK(ref_count_); + DCHECK(child_process_); + if (--ref_count_) + return; + + if (child_thread_.get()) // null in unittests. + child_thread_->OnProcessFinalRelease(); +} + +base::WaitableEvent* ChildProcess::GetShutDownEvent() { + DCHECK(child_process_); + return &child_process_->shutdown_event_; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,67 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_CHILD_PROCESS_H__ +#define CHROME_COMMON_CHILD_PROCESS_H__ + +#include +#include +#include "base/basictypes.h" +#include "base/message_loop.h" +#include "base/scoped_ptr.h" +#include "base/waitable_event.h" + +class ChildThread; + + +// Base class for child processes of the browser process (i.e. renderer and +// plugin host). This is a singleton object for each child process. +class ChildProcess { + public: + // Child processes should have an object that derives from this class. The + // constructor will return once ChildThread has started. + ChildProcess(ChildThread* child_thread); + virtual ~ChildProcess(); + + // Getter for this process' main thread. + ChildThread* child_thread() { return child_thread_.get(); } + + // A global event object that is signalled when the main thread's message + // loop exits. This gives background threads a way to observe the main + // thread shutting down. This can be useful when a background thread is + // waiting for some information from the browser process. If the browser + // process goes away prematurely, the background thread can at least notice + // the child processes's main thread exiting to determine that it should give + // up waiting. + // For example, see the renderer code used to implement + // webkit_glue::GetCookies. + base::WaitableEvent* GetShutDownEvent(); + + // These are used for ref-counting the child process. The process shuts + // itself down when the ref count reaches 0. + // For example, in the renderer process, generally each tab managed by this + // process will hold a reference to the process, and release when closed. + void AddRefProcess(); + void ReleaseProcess(); + + // Getter for the one ChildProcess object for this process. + static ChildProcess* current() { return child_process_; } + + private: + // NOTE: make sure that child_thread_ is listed before shutdown_event_, since + // it depends on it (indirectly through IPC::SyncChannel). + scoped_ptr child_thread_; + + int ref_count_; + + // An event that will be signalled when we shutdown. + base::WaitableEvent shutdown_event_; + + // The singleton instance for this process. + static ChildProcess* child_process_; + + DISALLOW_EVIL_CONSTRUCTORS(ChildProcess); +}; + +#endif // CHROME_COMMON_CHILD_PROCESS_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process_host.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process_host.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process_host.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process_host.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,255 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/child_process_host.h" + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/process_util.h" +#include "base/singleton.h" +#include "base/waitable_event.h" +#ifdef CHROMIUM_MOZILLA_BUILD +#include "mozilla/ipc/BrowserProcessSubThread.h" +typedef mozilla::ipc::BrowserProcessSubThread ChromeThread; +#else +#include "chrome/browser/chrome_thread.h" +#endif +#include "chrome/common/ipc_logging.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_type.h" +#ifndef CHROMIUM_MOZILLA_BUILD +#include "chrome/common/plugin_messages.h" +#endif +#include "chrome/common/process_watcher.h" +#include "chrome/common/result_codes.h" + + +namespace { +typedef std::list ChildProcessList; + +// The NotificationTask is used to notify about plugin process connection/ +// disconnection. It is needed because the notifications in the +// NotificationService must happen in the main thread. +class ChildNotificationTask : public Task { + public: + ChildNotificationTask( + NotificationType notification_type, ChildProcessInfo* info) + : notification_type_(notification_type), info_(*info) { } + + virtual void Run() { + NotificationService::current()-> + Notify(notification_type_, NotificationService::AllSources(), + Details(&info_)); + } + + private: + NotificationType notification_type_; + ChildProcessInfo info_; +}; + +} // namespace + + + +ChildProcessHost::ChildProcessHost( + ProcessType type, ResourceDispatcherHost* resource_dispatcher_host) + : +#ifdef CHROMIUM_MOZILLA_BUILD + ChildProcessInfo(type), +#else + Receiver(type), +#endif + ALLOW_THIS_IN_INITIALIZER_LIST(listener_(this)), + resource_dispatcher_host_(resource_dispatcher_host), + opening_channel_(false), + process_event_(NULL) { + Singleton::get()->push_back(this); +} + + +ChildProcessHost::~ChildProcessHost() { + Singleton::get()->remove(this); + + if (handle()) { + watcher_.StopWatching(); + ProcessWatcher::EnsureProcessTerminated(handle()); + +#if defined(OS_WIN) + // Above call took ownership, so don't want WaitableEvent to assert because + // the handle isn't valid anymore. + process_event_->Release(); +#endif + } +} + +bool ChildProcessHost::CreateChannel() { + channel_id_ = GenerateRandomChannelID(this); + channel_.reset(new IPC::Channel( + channel_id_, IPC::Channel::MODE_SERVER, &listener_)); + if (!channel_->Connect()) + return false; + + opening_channel_ = true; + + return true; +} + +void ChildProcessHost::SetHandle(base::ProcessHandle process) { +#if defined(OS_WIN) + process_event_.reset(new base::WaitableEvent(process)); + + DCHECK(!handle()); + set_handle(process); + watcher_.StartWatching(process_event_.get(), this); +#endif +} + +void ChildProcessHost::InstanceCreated() { + Notify(NotificationType::CHILD_INSTANCE_CREATED); +} + +bool ChildProcessHost::Send(IPC::Message* msg) { + if (!channel_.get()) { + delete msg; + return false; + } + return channel_->Send(msg); +} + +void ChildProcessHost::Notify(NotificationType type) { +#ifdef CHROMIUM_MOZILLA_BUILD + ChromeThread::GetMessageLoop(ChromeThread::IO)->PostTask( +#else + resource_dispatcher_host_->ui_loop()->PostTask( +#endif + FROM_HERE, new ChildNotificationTask(type, this)); +} + +void ChildProcessHost::OnWaitableEventSignaled(base::WaitableEvent *event) { +#if defined(OS_WIN) + HANDLE object = event->handle(); + DCHECK(handle()); + DCHECK_EQ(object, handle()); + + bool did_crash = base::DidProcessCrash(NULL, object); + if (did_crash) { + // Report that this child process crashed. + Notify(NotificationType::CHILD_PROCESS_CRASHED); + } + // Notify in the main loop of the disconnection. + Notify(NotificationType::CHILD_PROCESS_HOST_DISCONNECTED); +#endif + +#ifndef CHROMIUM_MOZILLA_BUILD + delete this; +#endif +} + +ChildProcessHost::ListenerHook::ListenerHook(ChildProcessHost* host) + : host_(host) { +} + +void ChildProcessHost::ListenerHook::OnMessageReceived( + const IPC::Message& msg) { +#ifdef IPC_MESSAGE_LOG_ENABLED + IPC::Logging* logger = IPC::Logging::current(); + if (msg.type() == IPC_LOGGING_ID) { + logger->OnReceivedLoggingMessage(msg); + return; + } + + if (logger->Enabled()) + logger->OnPreDispatchMessage(msg); +#endif + + bool msg_is_ok = true; +#ifdef CHROMIUM_MOZILLA_BUILD + bool handled = false; +#else + bool handled = host_->resource_dispatcher_host_->OnMessageReceived( + msg, host_, &msg_is_ok); +#endif + + if (!handled) { +#ifdef CHROMIUM_MOZILLA_BUILD + if (0) { +#else + if (msg.type() == PluginProcessHostMsg_ShutdownRequest::ID) { + // Must remove the process from the list now, in case it gets used for a + // new instance before our watcher tells us that the process terminated. + Singleton::get()->remove(host_); + if (host_->CanShutdown()) + host_->Send(new PluginProcessMsg_Shutdown()); +#endif + } else { + host_->OnMessageReceived(msg); + } + } + + if (!msg_is_ok) + base::KillProcess(host_->handle(), ResultCodes::KILLED_BAD_MESSAGE, false); + +#ifdef IPC_MESSAGE_LOG_ENABLED + if (logger->Enabled()) + logger->OnPostDispatchMessage(msg, host_->channel_id_); +#endif +} + +void ChildProcessHost::ListenerHook::OnChannelConnected(int32 peer_pid) { + host_->opening_channel_ = false; + host_->OnChannelConnected(peer_pid); +#ifndef CHROMIUM_MOZILLA_BUILD + host_->Send(new PluginProcessMsg_AskBeforeShutdown()); +#endif + + // Notify in the main loop of the connection. + host_->Notify(NotificationType::CHILD_PROCESS_HOST_CONNECTED); +} + +void ChildProcessHost::ListenerHook::OnChannelError() { + host_->opening_channel_ = false; + host_->OnChannelError(); +} + + +ChildProcessHost::Iterator::Iterator() : all_(true) { +#ifndef CHROMIUM_MOZILLA_BUILD + DCHECK(MessageLoop::current() == + ChromeThread::GetMessageLoop(ChromeThread::IO)) << + "ChildProcessInfo::Iterator must be used on the IO thread."; +#endif + iterator_ = Singleton::get()->begin(); +} + +ChildProcessHost::Iterator::Iterator(ProcessType type) + : all_(false), type_(type) { +#ifndef CHROMIUM_MOZILLA_BUILD + DCHECK(MessageLoop::current() == + ChromeThread::GetMessageLoop(ChromeThread::IO)) << + "ChildProcessInfo::Iterator must be used on the IO thread."; +#endif + iterator_ = Singleton::get()->begin(); + if (!Done() && (*iterator_)->type() != type_) + ++(*this); +} + +ChildProcessHost* ChildProcessHost::Iterator::operator++() { + do { + ++iterator_; + if (Done()) + break; + + if (!all_ && (*iterator_)->type() != type_) + continue; + + return *iterator_; + } while (true); + + return NULL; +} + +bool ChildProcessHost::Iterator::Done() { + return iterator_ == Singleton::get()->end(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process_host.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process_host.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process_host.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process_host.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,141 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_CHILD_PROCESS_HOST_H_ +#define CHROME_COMMON_CHILD_PROCESS_HOST_H_ + +#include "build/build_config.h" + +#include + +#include "base/basictypes.h" +#include "base/scoped_ptr.h" +#include "base/waitable_event_watcher.h" +#ifdef CHROMIUM_MOZILLA_BUILD +class ResourceDispatcherHost; +#else +#include "chrome/browser/renderer_host/resource_dispatcher_host.h" +#endif +#include "chrome/common/child_process_info.h" +#include "chrome/common/ipc_channel.h" + +class NotificationType; + +// Plugins/workers and other child processes that live on the IO thread should +// derive from this class. +class ChildProcessHost : +#ifdef CHROMIUM_MOZILLA_BUILD + public IPC::Message::Sender, + public ChildProcessInfo, +#else + public ResourceDispatcherHost::Receiver, +#endif + public base::WaitableEventWatcher::Delegate, + public IPC::Channel::Listener { + public: + virtual ~ChildProcessHost(); + + // ResourceDispatcherHost::Receiver implementation: + virtual bool Send(IPC::Message* msg); + + // The Iterator class allows iteration through either all child processes, or + // ones of a specific type, depending on which constructor is used. Note that + // this should be done from the IO thread and that the iterator should not be + // kept around as it may be invalidated on subsequent event processing in the + // event loop. + class Iterator { + public: + Iterator(); + Iterator(ProcessType type); + ChildProcessHost* operator->() { return *iterator_; } + ChildProcessHost* operator*() { return *iterator_; } + ChildProcessHost* operator++(); + bool Done(); + + private: + bool all_; + ProcessType type_; + std::list::iterator iterator_; + }; + + protected: + ChildProcessHost(ProcessType type, + ResourceDispatcherHost* resource_dispatcher_host = 0); + + // Derived classes return true if it's ok to shut down the child process. + virtual bool CanShutdown() = 0; + + // Creates the IPC channel. Returns true iff it succeeded. + bool CreateChannel(); + + // Once the subclass gets a handle to the process, it needs to tell + // ChildProcessHost using this function. + void SetHandle(base::ProcessHandle handle); + + // Notifies us that an instance has been created on this child process. + void InstanceCreated(); + + // IPC::Channel::Listener implementation: + virtual void OnMessageReceived(const IPC::Message& msg) { } + virtual void OnChannelConnected(int32 peer_pid) { } + virtual void OnChannelError() { } + + bool opening_channel() { return opening_channel_; } + const std::wstring& channel_id() { return channel_id_; } + +#ifdef CHROMIUM_MOZILLA_BUILD + base::WaitableEvent* GetProcessEvent() { return process_event_.get(); } +#endif + + const IPC::Channel& channel() const { return *channel_; } +#ifdef CHROMIUM_MOZILLA_BUILD + IPC::Channel* channelp() const { return channel_.get(); } +#endif + + private: + // Sends the given notification to the notification service on the UI thread. + void Notify(NotificationType type); + +#ifdef CHROMIUM_MOZILLA_BUILD + protected: +#endif + // WaitableEventWatcher::Delegate implementation: + virtual void OnWaitableEventSignaled(base::WaitableEvent *event); +#ifdef CHROMIUM_MOZILLA_BUILD + private: +#endif + + // By using an internal class as the IPC::Channel::Listener, we can intercept + // OnMessageReceived/OnChannelConnected and do our own processing before + // calling the subclass' implementation. + class ListenerHook : public IPC::Channel::Listener { + public: + ListenerHook(ChildProcessHost* host); + virtual void OnMessageReceived(const IPC::Message& msg); + virtual void OnChannelConnected(int32 peer_pid); + virtual void OnChannelError(); + private: + ChildProcessHost* host_; + }; + + ListenerHook listener_; + + ResourceDispatcherHost* resource_dispatcher_host_; + + // True while we're waiting the channel to be opened. + bool opening_channel_; + + // The IPC::Channel. + scoped_ptr channel_; + + // IPC Channel's id. + std::wstring channel_id_; + + // Used to watch the child process handle. + base::WaitableEventWatcher watcher_; + + scoped_ptr process_event_; +}; + +#endif // CHROME_COMMON_CHILD_PROCESS_HOST_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process_info.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process_info.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process_info.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process_info.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,88 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/child_process_info.h" + +#include + +#ifndef CHROMIUM_MOZILLA_BUILD +#include "app/l10n_util.h" +#endif +#include "base/logging.h" +#include "base/process_util.h" +#include "base/rand_util.h" +#include "base/string_util.h" +#ifndef CHROMIUM_MOZILLA_BUILD +#include "grit/generated_resources.h" +#endif + +std::wstring ChildProcessInfo::GetTypeNameInEnglish( + ChildProcessInfo::ProcessType type) { + switch (type) { + case BROWSER_PROCESS: + return L"Browser"; + case RENDER_PROCESS: + return L"Tab"; + case PLUGIN_PROCESS: + return L"Plug-in"; + case WORKER_PROCESS: + return L"Web Worker"; + case UNKNOWN_PROCESS: + default: + DCHECK(false) << "Unknown child process type!"; + return L"Unknown"; + } +} + +std::wstring ChildProcessInfo::GetLocalizedTitle() const { +#ifdef CHROMIUM_MOZILLA_BUILD + return name_; +#else + std::wstring title = name_; + if (type_ == ChildProcessInfo::PLUGIN_PROCESS && title.empty()) + title = l10n_util::GetString(IDS_TASK_MANAGER_UNKNOWN_PLUGIN_NAME); + + int message_id; + if (type_ == ChildProcessInfo::PLUGIN_PROCESS) { + message_id = IDS_TASK_MANAGER_PLUGIN_PREFIX; + } else if (type_ == ChildProcessInfo::WORKER_PROCESS) { + message_id = IDS_TASK_MANAGER_WORKER_PREFIX; + } else { + DCHECK(false) << "Need localized name for child process type."; + return title; + } + + // Explicitly mark name as LTR if there is no strong RTL character, + // to avoid the wrong concatenation result similar to "!Yahoo! Mail: the + // best web-based Email: NIGULP", in which "NIGULP" stands for the Hebrew + // or Arabic word for "plugin". + l10n_util::AdjustStringForLocaleDirection(title, &title); + return l10n_util::GetStringF(message_id, title); +#endif +} + +ChildProcessInfo::ChildProcessInfo(ProcessType type) { + // This constructor is only used by objects which derive from this class, + // which means *this* is a real object that refers to a child process, and not + // just a simple object that contains information about it. So add it to our + // list of running processes. + type_ = type; + pid_ = -1; +} + + +ChildProcessInfo::~ChildProcessInfo() { +} + +std::wstring ChildProcessInfo::GenerateRandomChannelID(void* instance) { + // Note: the string must start with the current process id, this is how + // child processes determine the pid of the parent. + // Build the channel ID. This is composed of a unique identifier for the + // parent browser process, an identifier for the child instance, and a random + // component. We use a random component so that a hacked child process can't + // cause denial of service by causing future named pipe creation to fail. + return StringPrintf(L"%d.%x.%d", + base::GetCurrentProcId(), instance, + base::RandInt(0, std::numeric_limits::max())); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process_info.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process_info.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process_info.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_process_info.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,106 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_CHILD_PROCESS_INFO_H_ +#define CHROME_COMMON_CHILD_PROCESS_INFO_H_ + +#include + +#include "base/process.h" + +// Holds information about a child process. +class ChildProcessInfo { + public: + enum ProcessType { + BROWSER_PROCESS, + RENDER_PROCESS, + PLUGIN_PROCESS, + WORKER_PROCESS, + UNKNOWN_PROCESS, + }; + + // Returns the type of the process. + ProcessType type() const { return type_; } + + // Returns the name of the process. i.e. for plugins it might be Flash, while + // for workers it might be the domain that it's from. + std::wstring name() const { return name_; } + + // Getter to the process handle. + base::ProcessHandle handle() const { return process_.handle(); } + + virtual int GetProcessId() const { + if (pid_ != -1) + return pid_; + + pid_ = process_.pid(); + return pid_; + } + void SetProcessBackgrounded() const { process_.SetProcessBackgrounded(true); } + void ReduceWorkingSet() const { process_.ReduceWorkingSet(); } + + // Returns an English name of the process type, should only be used for non + // user-visible strings, or debugging pages like about:memory. + static std::wstring GetTypeNameInEnglish(ProcessType type); + + // Returns a localized title for the child process. For example, a plugin + // process would be "Plug-in: Flash" when name is "Flash". + std::wstring GetLocalizedTitle() const; + + ChildProcessInfo(const ChildProcessInfo& original) { + type_ = original.type_; + name_ = original.name_; + process_ = original.process_; + pid_ = original.pid_; + } + + ChildProcessInfo& operator=(const ChildProcessInfo& original) { + if (&original != this) { + type_ = original.type_; + name_ = original.name_; + process_ = original.process_; + pid_ = original.pid_; + } + return *this; + } + + virtual ~ChildProcessInfo(); + + // We define the < operator so that the ChildProcessInfo can be used as a key + // in a std::map. + bool operator <(const ChildProcessInfo& rhs) const { + if (process_.handle() != rhs.process_.handle()) + return process_ .handle() < rhs.process_.handle(); + return false; + } + + bool operator ==(const ChildProcessInfo& rhs) const { + return process_.handle() == rhs.process_.handle(); + } + + // Generates a unique channel name for a child renderer/plugin process. + // The "instance" pointer value is baked into the channel id. + static std::wstring GenerateRandomChannelID(void* instance); + + protected: + void set_type(ProcessType type) { type_ = type; } + void set_name(const std::wstring& name) { name_ = name; } + void set_handle(base::ProcessHandle handle) { + process_.set_handle(handle); + pid_ = -1; + } + + // Derived objects need to use this constructor so we know what type we are. + ChildProcessInfo(ProcessType type); + + private: + ProcessType type_; + std::wstring name_; + mutable int pid_; // Cache of the process id. + + // The handle to the process. + mutable base::Process process_; +}; + +#endif // CHROME_COMMON_CHILD_PROCESS_INFO_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_thread.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_thread.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_thread.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_thread.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,143 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/child_thread.h" + +#include "base/string_util.h" +#include "base/command_line.h" +#include "chrome/common/child_process.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/ipc_logging.h" +#ifndef CHROMIUM_MOZILLA_BUILD +#include "chrome/common/plugin_messages.h" +#include "webkit/glue/webkit_glue.h" +#endif + +// V8 needs a 1MB stack size. +const size_t ChildThread::kV8StackSize = 1024 * 1024; + +ChildThread::ChildThread(Thread::Options options) + : Thread("Chrome_ChildThread"), + owner_loop_(MessageLoop::current()), + options_(options), + check_with_browser_before_shutdown_(false) { + DCHECK(owner_loop_); + channel_name_ = CommandLine::ForCurrentProcess()->GetSwitchValue( + switches::kProcessChannelID); + + if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUserAgent)) { +#ifndef CHROMIUM_MOZILLA_BUILD + webkit_glue::SetUserAgent(WideToUTF8( + CommandLine::ForCurrentProcess()->GetSwitchValue( + switches::kUserAgent))); +#endif + } +} + +ChildThread::~ChildThread() { +} + +bool ChildThread::Run() { + return StartWithOptions(options_); +} + +void ChildThread::OnChannelError() { + owner_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); +} + +bool ChildThread::Send(IPC::Message* msg) { + if (!channel_.get()) { + delete msg; + return false; + } + + return channel_->Send(msg); +} + +void ChildThread::AddRoute(int32 routing_id, IPC::Channel::Listener* listener) { + DCHECK(MessageLoop::current() == message_loop()); + + router_.AddRoute(routing_id, listener); +} + +void ChildThread::RemoveRoute(int32 routing_id) { + DCHECK(MessageLoop::current() == message_loop()); + + router_.RemoveRoute(routing_id); +} + +void ChildThread::OnMessageReceived(const IPC::Message& msg) { +#ifndef CHROMIUM_MOZILLA_BUILD + // Resource responses are sent to the resource dispatcher. + if (resource_dispatcher_->OnMessageReceived(msg)) + return; + + if (msg.type() == PluginProcessMsg_AskBeforeShutdown::ID) { + check_with_browser_before_shutdown_ = true; + return; + } + + if (msg.type() == PluginProcessMsg_Shutdown::ID) { + owner_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + return; + } +#endif + + if (msg.routing_id() == MSG_ROUTING_CONTROL) { + OnControlMessageReceived(msg); + } else { + router_.OnMessageReceived(msg); + } +} + +ChildThread* ChildThread::current() { + return ChildProcess::current()->child_thread(); +} + +void ChildThread::Init() { +#ifndef CHROMIUM_MOZILLA_BUILD + channel_.reset(new IPC::SyncChannel(channel_name_, + IPC::Channel::MODE_CLIENT, this, NULL, owner_loop_, true, + ChildProcess::current()->GetShutDownEvent())); +#else + channel_.reset(new IPC::Channel(channel_name_, + IPC::Channel::MODE_CLIENT, + this)); +#endif + +#ifdef IPC_MESSAGE_LOG_ENABLED + IPC::Logging::current()->SetIPCSender(this); +#endif + +#ifndef CHROMIUM_MOZILLA_BUILD + resource_dispatcher_.reset(new ResourceDispatcher(this)); +#endif +} + +void ChildThread::CleanUp() { +#ifdef IPC_MESSAGE_LOG_ENABLED + IPC::Logging::current()->SetIPCSender(NULL); +#endif + // Need to destruct the SyncChannel to the browser before we go away because + // it caches a pointer to this thread. + channel_.reset(); +#ifndef CHROMIUM_MOZILLA_BUILD + resource_dispatcher_.reset(); +#endif +} + +void ChildThread::OnProcessFinalRelease() { + if (!check_with_browser_before_shutdown_) { + owner_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + return; + } + +#ifndef CHROMIUM_MOZILLA_BUILD + // The child process shutdown sequence is a request response based mechanism, + // where we send out an initial feeler request to the child process host + // instance in the browser to verify if it's ok to shutdown the child process. + // The browser then sends back a response if it's ok to shutdown. + Send(new PluginProcessHostMsg_ShutdownRequest); +#endif +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_thread.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_thread.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_thread.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/child_thread.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,108 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_CHILD_THREAD_H_ +#define CHROME_COMMON_CHILD_THREAD_H_ + +#include "base/thread.h" +#include "chrome/common/ipc_sync_channel.h" +#include "chrome/common/message_router.h" + +#ifdef CHROMIUM_MOZILLA_BUILD +class ResourceDispatcher; +#else +#include "chrome/common/resource_dispatcher.h" +#endif + +// Child processes's background thread should derive from this class. +class ChildThread : public IPC::Channel::Listener, + public IPC::Message::Sender, + public base::Thread { + public: + // Creates the thread. + ChildThread(Thread::Options options); + virtual ~ChildThread(); + + // IPC::Message::Sender implementation: + virtual bool Send(IPC::Message* msg); + + // See documentation on MessageRouter for AddRoute and RemoveRoute + void AddRoute(int32 routing_id, IPC::Channel::Listener* listener); + void RemoveRoute(int32 routing_id); + + MessageLoop* owner_loop() { return owner_loop_; } + +#ifndef CHROMIUM_MOZILLA_BUILD + ResourceDispatcher* resource_dispatcher() { + return resource_dispatcher_.get(); + } +#endif + + protected: + friend class ChildProcess; + + // Starts the thread. + bool Run(); + + // Overrides the channel name. Used for --single-process mode. + void SetChannelName(const std::wstring& name) { channel_name_ = name; } + + // Called when the process refcount is 0. + void OnProcessFinalRelease(); + + protected: + // The required stack size if V8 runs on a thread. + static const size_t kV8StackSize; + + virtual void OnControlMessageReceived(const IPC::Message& msg) { } + + // Returns the one child thread. + static ChildThread* current(); + +#ifndef CHROMIUM_MOZILLA_BUILD + IPC::SyncChannel* channel() { return channel_.get(); } +#else + IPC::Channel* channel() { return channel_.get(); } +#endif + + // Thread implementation. + virtual void Init(); + virtual void CleanUp(); + + private: + // IPC::Channel::Listener implementation: + virtual void OnMessageReceived(const IPC::Message& msg); + virtual void OnChannelError(); + + // The message loop used to run tasks on the thread that started this thread. + MessageLoop* owner_loop_; + + std::wstring channel_name_; +#ifndef CHROMIUM_MOZILLA_BUILD + scoped_ptr channel_; +#else + scoped_ptr channel_; +#endif + + // Used only on the background render thread to implement message routing + // functionality to the consumers of the ChildThread. + MessageRouter router_; + + Thread::Options options_; + +#ifndef CHROMIUM_MOZILLA_BUILD + // Handles resource loads for this process. + // NOTE: this object lives on the owner thread. + scoped_ptr resource_dispatcher_; +#endif + + // If true, checks with the browser process before shutdown. This avoids race + // conditions if the process refcount is 0 but there's an IPC message inflight + // that would addref it. + bool check_with_browser_before_shutdown_; + + DISALLOW_EVIL_CONSTRUCTORS(ChildThread); +}; + +#endif // CHROME_COMMON_CHILD_THREAD_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_constants.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_constants.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_constants.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_constants.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,95 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/chrome_constants.h" + +#include "base/file_path.h" + +#define FPL FILE_PATH_LITERAL + +namespace chrome { + +// The following should not be used for UI strings; they are meant +// for system strings only. UI changes should be made in the GRD. +#if defined(OS_WIN) +const wchar_t kBrowserProcessExecutableName[] = L"chrome.exe"; +#elif defined(OS_LINUX) +const wchar_t kBrowserProcessExecutableName[] = L"chrome"; +#elif defined(OS_MACOSX) +const wchar_t kBrowserProcessExecutableName[] = +#if defined(GOOGLE_CHROME_BUILD) + L"Chrome"; +#else + L"Chromium"; +#endif // GOOGLE_CHROME_BUILD +#endif // OS_* +#if defined(OS_WIN) +const wchar_t kBrowserProcessExecutablePath[] = L"chrome.exe"; +#elif defined(OS_LINUX) +const wchar_t kBrowserProcessExecutablePath[] = L"chrome"; +#elif defined(OS_MACOSX) +const wchar_t kBrowserProcessExecutablePath[] = +#if defined(GOOGLE_CHROME_BUILD) + L"Chrome.app/Contents/MacOS/Chrome"; +#else + L"Chromium.app/Contents/MacOS/Chromium"; +#endif // GOOGLE_CHROME_BUILD +#endif // OS_* +#if defined(GOOGLE_CHROME_BUILD) +const wchar_t kBrowserAppName[] = L"Chrome"; +const char kStatsFilename[] = "ChromeStats2"; +#else +const wchar_t kBrowserAppName[] = L"Chromium"; +const char kStatsFilename[] = "ChromiumStats2"; +#endif +const wchar_t kExternalTabWindowClass[] = L"Chrome_ExternalTabContainer"; +const wchar_t kMessageWindowClass[] = L"Chrome_MessageWindow"; +const wchar_t kCrashReportLog[] = L"Reported Crashes.txt"; +const wchar_t kTestingInterfaceDLL[] = L"testing_interface.dll"; +const wchar_t kNotSignedInProfile[] = L"Default"; +const wchar_t kNotSignedInID[] = L"not-signed-in"; +const wchar_t kBrowserResourcesDll[] = L"chrome.dll"; +const FilePath::CharType kExtensionFileExtension[] = FPL("crx"); + +// filenames +const FilePath::CharType kArchivedHistoryFilename[] = FPL("Archived History"); +const FilePath::CharType kCacheDirname[] = FPL("Cache"); +const FilePath::CharType kMediaCacheDirname[] = FPL("Media Cache"); +const FilePath::CharType kOffTheRecordMediaCacheDirname[] = + FPL("Incognito Media Cache"); +const wchar_t kChromePluginDataDirname[] = L"Plugin Data"; +const FilePath::CharType kCookieFilename[] = FPL("Cookies"); +const FilePath::CharType kHistoryFilename[] = FPL("History"); +const FilePath::CharType kLocalStateFilename[] = FPL("Local State"); +const FilePath::CharType kPreferencesFilename[] = FPL("Preferences"); +const FilePath::CharType kSafeBrowsingFilename[] = FPL("Safe Browsing"); +// WARNING: SingletonSocket can't contain spaces, because otherwise +// chrome_process_util_linux would be broken. +const FilePath::CharType kSingletonSocketFilename[] = FPL("SingletonSocket"); +const FilePath::CharType kThumbnailsFilename[] = FPL("Thumbnails"); +const wchar_t kUserDataDirname[] = L"User Data"; +const FilePath::CharType kUserScriptsDirname[] = FPL("User Scripts"); +const FilePath::CharType kWebDataFilename[] = FPL("Web Data"); +const FilePath::CharType kBookmarksFileName[] = FPL("Bookmarks"); +const FilePath::CharType kHistoryBookmarksFileName[] = + FPL("Bookmarks From History"); +const FilePath::CharType kCustomDictionaryFileName[] = + FPL("Custom Dictionary.txt"); + +// This number used to be limited to 32 in the past (see b/535234). +const unsigned int kMaxRendererProcessCount = 42; +const int kStatsMaxThreads = 32; +const int kStatsMaxCounters = 300; + +// We don't enable record mode in the released product because users could +// potentially be tricked into running a product in record mode without +// knowing it. Enable in debug builds. Playback mode is allowed always, +// because it is useful for testing and not hazardous by itself. +#ifndef NDEBUG +const bool kRecordModeEnabled = true; +#else +const bool kRecordModeEnabled = false; +#endif + +} // namespace chrome diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_constants.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_constants.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_constants.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_constants.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,55 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A handful of resource-like constants related to the Chrome application. + +#ifndef CHROME_COMMON_CHROME_CONSTANTS_H_ +#define CHROME_COMMON_CHROME_CONSTANTS_H_ + +#include "base/file_path.h" + +namespace chrome { + +extern const wchar_t kBrowserProcessExecutableName[]; +extern const wchar_t kBrowserProcessExecutablePath[]; +extern const wchar_t kBrowserAppName[]; +extern const wchar_t kMessageWindowClass[]; +extern const wchar_t kExternalTabWindowClass[]; +extern const wchar_t kCrashReportLog[]; +extern const wchar_t kTestingInterfaceDLL[]; +extern const wchar_t kNotSignedInProfile[]; +extern const wchar_t kNotSignedInID[]; +extern const char kStatsFilename[]; +extern const wchar_t kBrowserResourcesDll[]; +extern const FilePath::CharType kExtensionFileExtension[]; + +// filenames +extern const FilePath::CharType kArchivedHistoryFilename[]; +extern const FilePath::CharType kCacheDirname[]; +extern const FilePath::CharType kMediaCacheDirname[]; +extern const FilePath::CharType kOffTheRecordMediaCacheDirname[]; +extern const wchar_t kChromePluginDataDirname[]; +extern const FilePath::CharType kCookieFilename[]; +extern const FilePath::CharType kHistoryFilename[]; +extern const FilePath::CharType kLocalStateFilename[]; +extern const FilePath::CharType kPreferencesFilename[]; +extern const FilePath::CharType kSafeBrowsingFilename[]; +extern const FilePath::CharType kSingletonSocketFilename[]; +extern const FilePath::CharType kThumbnailsFilename[]; +extern const wchar_t kUserDataDirname[]; +extern const FilePath::CharType kUserScriptsDirname[]; +extern const FilePath::CharType kWebDataFilename[]; +extern const FilePath::CharType kBookmarksFileName[]; +extern const FilePath::CharType kHistoryBookmarksFileName[]; +extern const FilePath::CharType kCustomDictionaryFileName[]; + +extern const unsigned int kMaxRendererProcessCount; +extern const int kStatsMaxThreads; +extern const int kStatsMaxCounters; + +extern const bool kRecordModeEnabled; + +} // namespace chrome + +#endif // CHROME_COMMON_CHROME_CONSTANTS_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_counters.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_counters.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_counters.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_counters.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,57 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/chrome_counters.h" + +#include "base/stats_counters.h" + +namespace chrome { + +// Note: We use the construct-on-first-use pattern here, because we don't +// want to fight with any static initializer ordering problems later. +// The downside of this is that the objects don't ever get cleaned up. +// But they are small and this is okay. + +// Note: Because these are constructed on-first-use, there is a slight +// race condition - two threads could initialize the same counter. +// If this happened, the stats table would still work just fine; +// we'd leak the extraneous StatsCounter object once, and that +// would be it. But these are small objects, so this is ok. + +StatsCounter& Counters::ipc_send_counter() { + static StatsCounter* ctr = new StatsCounter("IPC.SendMsgCount"); + return *ctr; +} + +StatsCounterTimer& Counters::chrome_main() { + static StatsCounterTimer* ctr = new StatsCounterTimer("Chrome.Init"); + return *ctr; +} + +StatsCounterTimer& Counters::renderer_main() { + static StatsCounterTimer* ctr = new StatsCounterTimer("Chrome.RendererInit"); + return *ctr; +} + +StatsCounterTimer& Counters::spellcheck_init() { + static StatsCounterTimer* ctr = new StatsCounterTimer("SpellCheck.Init"); + return *ctr; +} + +StatsRate& Counters::spellcheck_lookup() { + static StatsRate* ctr = new StatsRate("SpellCheck.Lookup"); + return *ctr; +} + +StatsCounterTimer& Counters::plugin_load() { + static StatsCounterTimer* ctr = new StatsCounterTimer("ChromePlugin.Load"); + return *ctr; +} + +StatsRate& Counters::plugin_intercept() { + static StatsRate* ctr = new StatsRate("ChromePlugin.Intercept"); + return *ctr; +} + +} // namespace chrome diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_counters.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_counters.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_counters.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_counters.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,42 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Counters used within the browser. + +#ifndef CHROME_COMMON_CHROME_COUNTERS_H_ +#define CHROME_COMMON_CHROME_COUNTERS_H_ + +class StatsCounter; +class StatsCounterTimer; +class StatsRate; + +namespace chrome { + +class Counters { + public: + // The number of messages sent on IPC channels. + static StatsCounter& ipc_send_counter(); + + // The amount of time spent in chrome initialization. + static StatsCounterTimer& chrome_main(); + + // The amount of time spent in renderer initialization. + static StatsCounterTimer& renderer_main(); + + // Time spent in spellchecker initialization. + static StatsCounterTimer& spellcheck_init(); + + // Time/Count of spellcheck lookups. + static StatsRate& spellcheck_lookup(); + + // Time spent loading the Chrome plugins. + static StatsCounterTimer& plugin_load(); + + // Time/Count of plugin network interception. + static StatsRate& plugin_intercept(); +}; + +} // namespace chrome + +#endif // CHROME_COMMON_CHROME_COUNTERS_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,225 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/chrome_paths.h" + +#include "base/command_line.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "base/sys_info.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_paths_internal.h" +#include "chrome/common/chrome_switches.h" + +namespace chrome { + +bool GetGearsPluginPathFromCommandLine(FilePath* path) { +#ifndef NDEBUG + // for debugging, support a cmd line based override + std::wstring plugin_path = CommandLine::ForCurrentProcess()->GetSwitchValue( + switches::kGearsPluginPathOverride); + // TODO(tc): After GetSwitchNativeValue lands, we don't need to use + // FromWStringHack. + *path = FilePath::FromWStringHack(plugin_path); + return !plugin_path.empty(); +#else + return false; +#endif +} + +bool PathProvider(int key, FilePath* result) { +#ifndef CHROMIUM_MOZILLA_BUILD + // Some keys are just aliases... + switch (key) { + case chrome::DIR_APP: + return PathService::Get(base::DIR_MODULE, result); + case chrome::DIR_LOGS: +#ifndef NDEBUG + return PathService::Get(chrome::DIR_USER_DATA, result); +#else + return PathService::Get(base::DIR_EXE, result); +#endif + case chrome::FILE_RESOURCE_MODULE: + return PathService::Get(base::FILE_MODULE, result); + } + + // Assume that we will not need to create the directory if it does not exist. + // This flag can be set to true for the cases where we want to create it. + bool create_dir = false; + + FilePath cur; + switch (key) { + case chrome::DIR_USER_DATA: + if (!GetDefaultUserDataDirectory(&cur)) + return false; + create_dir = true; + break; + case chrome::DIR_USER_DOCUMENTS: + if (!GetUserDocumentsDirectory(&cur)) + return false; + create_dir = true; + break; + case chrome::DIR_DEFAULT_DOWNLOADS: + if (!GetUserDownloadsDirectory(&cur)) + return false; + break; + case chrome::DIR_CRASH_DUMPS: + // The crash reports are always stored relative to the default user data + // directory. This avoids the problem of having to re-initialize the + // exception handler after parsing command line options, which may + // override the location of the app's profile directory. + if (!GetDefaultUserDataDirectory(&cur)) + return false; + cur = cur.Append(FILE_PATH_LITERAL("Crash Reports")); + create_dir = true; + break; + case chrome::DIR_USER_DESKTOP: + if (!GetUserDesktop(&cur)) + return false; + break; + case chrome::DIR_RESOURCES: + if (!PathService::Get(chrome::DIR_APP, &cur)) + return false; + cur = cur.Append(FILE_PATH_LITERAL("resources")); + create_dir = true; + break; + case chrome::DIR_INSPECTOR: + if (!PathService::Get(chrome::DIR_APP, &cur)) + return false; + cur = cur.Append(FILE_PATH_LITERAL("resources")); + cur = cur.Append(FILE_PATH_LITERAL("inspector")); + break; + case chrome::DIR_THEMES: + if (!PathService::Get(chrome::DIR_APP, &cur)) + return false; + cur = cur.Append(FILE_PATH_LITERAL("themes")); + create_dir = true; + break; + case chrome::DIR_LOCALES: + if (!PathService::Get(chrome::DIR_APP, &cur)) + return false; +#if defined(OS_MACOSX) + // On Mac, locale files are in Contents/Resources, a sibling of the + // App dir. + cur = cur.DirName(); + cur = cur.Append(FILE_PATH_LITERAL("Resources")); +#else + cur = cur.Append(FILE_PATH_LITERAL("locales")); +#endif + create_dir = true; + break; + case chrome::DIR_APP_DICTIONARIES: +#if defined(OS_LINUX) + // We can't write into the EXE dir on Linux, so keep dictionaries + // alongside the safe browsing database in the user data dir. + if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) + return false; +#else + if (!PathService::Get(base::DIR_EXE, &cur)) + return false; +#endif + cur = cur.Append(FILE_PATH_LITERAL("Dictionaries")); + create_dir = true; + break; + case chrome::FILE_LOCAL_STATE: + if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) + return false; +#ifdef CHROMIUM_MOZILLA_BUILD + cur = cur.Append(FILE_PATH_LITERAL("Local State")); +#else + cur = cur.Append(chrome::kLocalStateFilename); +#endif + break; + case chrome::FILE_RECORDED_SCRIPT: + if (!PathService::Get(chrome::DIR_USER_DATA, &cur)) + return false; + cur = cur.Append(FILE_PATH_LITERAL("script.log")); + break; + case chrome::FILE_GEARS_PLUGIN: + if (!GetGearsPluginPathFromCommandLine(&cur)) { +#if defined(OS_WIN) + // Search for gears.dll alongside chrome.dll first. This new model + // allows us to package gears.dll with the Chrome installer and update + // it while Chrome is running. + if (!PathService::Get(base::DIR_MODULE, &cur)) + return false; + cur = cur.Append(FILE_PATH_LITERAL("gears.dll")); + + if (!file_util::PathExists(cur)) { + if (!PathService::Get(base::DIR_EXE, &cur)) + return false; + cur = cur.Append(FILE_PATH_LITERAL("plugins")); + cur = cur.Append(FILE_PATH_LITERAL("gears")); + cur = cur.Append(FILE_PATH_LITERAL("gears.dll")); + } +#else + // No gears.dll on non-Windows systems. + return false; +#endif + } + break; + // The following are only valid in the development environment, and + // will fail if executed from an installed executable (because the + // generated path won't exist). + case chrome::DIR_TEST_DATA: + if (!PathService::Get(base::DIR_SOURCE_ROOT, &cur)) + return false; + cur = cur.Append(FILE_PATH_LITERAL("chrome")); + cur = cur.Append(FILE_PATH_LITERAL("test")); + cur = cur.Append(FILE_PATH_LITERAL("data")); + if (!file_util::PathExists(cur)) // we don't want to create this + return false; + break; + case chrome::DIR_TEST_TOOLS: + if (!PathService::Get(base::DIR_SOURCE_ROOT, &cur)) + return false; + cur = cur.Append(FILE_PATH_LITERAL("chrome")); + cur = cur.Append(FILE_PATH_LITERAL("tools")); + cur = cur.Append(FILE_PATH_LITERAL("test")); + if (!file_util::PathExists(cur)) // we don't want to create this + return false; + break; + case chrome::FILE_PYTHON_RUNTIME: + if (!PathService::Get(base::DIR_SOURCE_ROOT, &cur)) + return false; + cur = cur.Append(FILE_PATH_LITERAL("third_party")); + cur = cur.Append(FILE_PATH_LITERAL("python_24")); + cur = cur.Append(FILE_PATH_LITERAL("python.exe")); + if (!file_util::PathExists(cur)) // we don't want to create this + return false; + break; + case chrome::FILE_TEST_SERVER: + if (!PathService::Get(base::DIR_SOURCE_ROOT, &cur)) + return false; + cur = cur.Append(FILE_PATH_LITERAL("net")); + cur = cur.Append(FILE_PATH_LITERAL("tools")); + cur = cur.Append(FILE_PATH_LITERAL("test")); + cur = cur.Append(FILE_PATH_LITERAL("testserver")); + cur = cur.Append(FILE_PATH_LITERAL("testserver.py")); + if (!file_util::PathExists(cur)) // we don't want to create this + return false; + break; + default: + return false; + } + + if (create_dir && !file_util::PathExists(cur) && + !file_util::CreateDirectory(cur)) + return false; + + *result = cur; +#endif + return true; +} + +// This cannot be done as a static initializer sadly since Visual Studio will +// eliminate this object file if there is no direct entry point into it. +void RegisterPathProvider() { + PathService::RegisterProvider(PathProvider, PATH_START, PATH_END); +} + +} // namespace chrome diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,53 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_CHROME_PATHS_H__ +#define CHROME_COMMON_CHROME_PATHS_H__ + +// This file declares path keys for the chrome module. These can be used with +// the PathService to access various special directories and files. + +namespace chrome { + +enum { + PATH_START = 1000, + + DIR_APP = PATH_START, // directory where dlls and data reside + DIR_LOGS, // directory where logs should be written + DIR_USER_DATA, // directory where user data can be written + DIR_CRASH_DUMPS, // directory where crash dumps are written + DIR_USER_DESKTOP, // directory that correspond to the desktop + DIR_RESOURCES, // directory where application resources are stored + DIR_INSPECTOR, // directory where web inspector is located + DIR_THEMES, // directory where theme dll files are stored + DIR_LOCALES, // directory where locale resources are stored + DIR_APP_DICTIONARIES, // directory where the global dictionaries are + DIR_USER_DOCUMENTS, // directory for a user's "My Documents" + DIR_DEFAULT_DOWNLOADS, // directory for a user's "My Documents/Downloads" + FILE_RESOURCE_MODULE, // full path and filename of the module that contains + // embedded resources (version, strings, images, etc.) + FILE_LOCAL_STATE, // path and filename to the file in which machine/ + // installation-specific state is saved + FILE_RECORDED_SCRIPT, // full path to the script.log file that contains + // recorded browser events for playback. + FILE_GEARS_PLUGIN, // full path to the gears.dll plugin file. + FILE_LIBAVCODEC, // full path to libavcodec media decoding library. + FILE_LIBAVFORMAT, // full path to libavformat media parsing library. + FILE_LIBAVUTIL, // full path to libavutil media utility library. + + // Valid only in development environment; TODO(darin): move these + DIR_TEST_DATA, // directory where unit test data resides + DIR_TEST_TOOLS, // directory where unit test tools reside + FILE_TEST_SERVER, // simple HTTP server for testing the network stack + FILE_PYTHON_RUNTIME, // Python runtime, used for running the test server + + PATH_END +}; + +// Call once to register the provider for the path keys defined above. +void RegisterPathProvider(); + +} // namespace chrome + +#endif // CHROME_COMMON_CHROME_PATHS_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths_internal.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths_internal.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths_internal.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths_internal.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,28 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_CHROME_PATHS_INTERNAL_H_ +#define CHROME_COMMON_CHROME_PATHS_INTERNAL_H_ + +class FilePath; + +namespace chrome { + +// Get the path to the user's data directory, regardless of whether +// DIR_USER_DATA has been overridden by a command-line option. +bool GetDefaultUserDataDirectory(FilePath* result); + +// Get the path to the user's documents directory. +bool GetUserDocumentsDirectory(FilePath* result); + +// Get the path to the user's downloads directory. +bool GetUserDownloadsDirectory(FilePath* result); + +// The path to the user's desktop. +bool GetUserDesktop(FilePath* result); + +} // namespace chrome + + +#endif // CHROME_COMMON_CHROME_PATHS_INTERNAL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths_linux.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths_linux.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths_linux.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths_linux.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,109 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/chrome_paths_internal.h" + +#include +#include + +#include "base/file_path.h" +#include "base/path_service.h" +#ifndef CHROMIUM_MOZILLA_BUILD +#include "chrome/third_party/xdg_user_dirs/xdg_user_dir_lookup.h" +#endif // ifndef CHROMIUM_MOZILLA_BUILD + +namespace { + +FilePath GetHomeDir() { + const char *home_dir = getenv("HOME"); + + if (home_dir && home_dir[0]) + return FilePath(home_dir); + + home_dir = g_get_home_dir(); + if (home_dir && home_dir[0]) + return FilePath(home_dir); + + FilePath rv; + if (PathService::Get(base::DIR_TEMP, &rv)) + return rv; + + /* last resort */ + return FilePath("/tmp/"); +} + +// Wrapper around xdg_user_dir_lookup() from +// src/chrome/third_party/xdg-user-dirs +FilePath GetXDGUserDirectory(const char* env_name, const char* fallback_dir) { +#ifndef CHROMIUM_MOZILLA_BUILD + char* xdg_dir = xdg_user_dir_lookup(env_name); + if (xdg_dir) { + FilePath rv(xdg_dir); + free(xdg_dir); + return rv; + } +#endif // ifndef CHROMIUM_MOZILLA_BUILD + return GetHomeDir().Append(fallback_dir); +} + +// |env_name| is the name of an environment variable that we want to use to get +// a directory path. |fallback_dir| is the directory relative to $HOME that we +// use if |env_name| cannot be found or is empty. |fallback_dir| may be NULL. +FilePath GetXDGDirectory(const char* env_name, const char* fallback_dir) { + const char* env_value = getenv(env_name); + if (env_value && env_value[0]) + return FilePath(env_value); + return GetHomeDir().Append(fallback_dir); +} + +} // namespace + +namespace chrome { + +// See http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html +// for a spec on where config files go. The net effect for most +// systems is we use ~/.config/chromium/ for Chromium and +// ~/.config/google-chrome/ for official builds. +// (This also helps us sidestep issues with other apps grabbing ~/.chromium .) +bool GetDefaultUserDataDirectory(FilePath* result) { + FilePath config_dir(GetXDGDirectory("XDG_CONFIG_HOME", ".config")); +#if defined(GOOGLE_CHROME_BUILD) + *result = config_dir.Append("google-chrome"); +#else + *result = config_dir.Append("chromium"); +#endif + return true; +} + +bool GetUserDocumentsDirectory(FilePath* result) { + *result = GetXDGUserDirectory("DOCUMENTS", "Documents"); + return true; +} + +// We respect the user's preferred download location, unless it is +// ~ or their desktop directory, in which case we default to ~/Downloads. +bool GetUserDownloadsDirectory(FilePath* result) { + *result = GetXDGUserDirectory("DOWNLOAD", "Downloads"); + + FilePath home = GetHomeDir(); + if (*result == home) { + *result = home.Append("Downloads"); + return true; + } + + FilePath desktop; + GetUserDesktop(&desktop); + if (*result == desktop) { + *result = home.Append("Downloads"); + } + + return true; +} + +bool GetUserDesktop(FilePath* result) { + *result = GetXDGUserDirectory("DESKTOP", "Desktop"); + return true; +} + +} // namespace chrome diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths_mac.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths_mac.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths_mac.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths_mac.mm 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,75 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#import "chrome/common/chrome_paths_internal.h" + +#import + +#import "base/base_paths.h" +#import "base/file_path.h" +#import "base/logging.h" +#import "base/path_service.h" + +namespace chrome { + +bool GetDefaultUserDataDirectory(FilePath* result) { + bool success = false; + NSArray* dirs = + NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, + NSUserDomainMask, YES); + if ([dirs count] && result) { + NSString* base = [dirs objectAtIndex:0]; +#if defined(GOOGLE_CHROME_BUILD) + base = [base stringByAppendingPathComponent:@"Google"]; + NSString* tail = @"Chrome"; +#else + NSString* tail = @"Chromium"; +#endif + NSString* path = [base stringByAppendingPathComponent:tail]; + *result = FilePath([path fileSystemRepresentation]); + success = true; + } + return success; +} + +bool GetUserDocumentsDirectory(FilePath* result) { + bool success = false; + NSArray* docArray = + NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, + NSUserDomainMask, + YES); + if ([docArray count] && result) { + *result = FilePath([[docArray objectAtIndex:0] fileSystemRepresentation]); + success = true; + } + return success; +} + +bool GetUserDownloadsDirectory(FilePath* result) { + bool success = false; + NSArray* docArray = + NSSearchPathForDirectoriesInDomains(NSDownloadsDirectory, + NSUserDomainMask, + YES); + if ([docArray count] && result) { + *result = FilePath([[docArray objectAtIndex:0] fileSystemRepresentation]); + success = true; + } + return success; +} + +bool GetUserDesktop(FilePath* result) { + bool success = false; + NSArray* docArray = + NSSearchPathForDirectoriesInDomains(NSDesktopDirectory, + NSUserDomainMask, + YES); + if ([docArray count] && result) { + *result = FilePath([[docArray objectAtIndex:0] fileSystemRepresentation]); + success = true; + } + return success; +} + +} // namespace chrome diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_paths_win.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,66 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/chrome_paths_internal.h" + +#include +#include +#include + +#include "base/file_path.h" +#include "base/path_service.h" +#include "chrome/common/chrome_constants.h" + +namespace chrome { + +bool GetDefaultUserDataDirectory(FilePath* result) { + if (!PathService::Get(base::DIR_LOCAL_APP_DATA, result)) + return false; +#if defined(GOOGLE_CHROME_BUILD) + *result = result->Append(FILE_PATH_LITERAL("Google")); +#endif + *result = result->Append(chrome::kBrowserAppName); + *result = result->Append(chrome::kUserDataDirname); + return true; +} + +bool GetUserDocumentsDirectory(FilePath* result) { + wchar_t path_buf[MAX_PATH]; + if (FAILED(SHGetFolderPath(NULL, CSIDL_MYDOCUMENTS, NULL, + SHGFP_TYPE_CURRENT, path_buf))) + return false; + *result = FilePath(path_buf); + return true; +} + +// On Vista, we can get the download path using a Win API +// (http://msdn.microsoft.com/en-us/library/bb762584(VS.85).aspx), +// but it can be set to Desktop, which is dangerous. Instead, +// we just use 'Downloads' under DIR_USER_DOCUMENTS. Localizing +// 'downloads' is not a good idea because Chrome's UI language +// can be changed. +bool GetUserDownloadsDirectory(FilePath* result) { + if (!GetUserDocumentsDirectory(result)) + return false; + + *result = result->Append(L"Downloads"); + return true; +} + +bool GetUserDesktop(FilePath* result) { + // We need to go compute the value. It would be nice to support paths + // with names longer than MAX_PATH, but the system functions don't seem + // to be designed for it either, with the exception of GetTempPath + // (but other things will surely break if the temp path is too long, + // so we don't bother handling it. + wchar_t system_buffer[MAX_PATH]; + system_buffer[0] = 0; + if (FAILED(SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY, NULL, + SHGFP_TYPE_CURRENT, system_buffer))) + return false; + *result = FilePath(system_buffer); + return true; +} + +} // namespace chrome diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_api.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_api.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_api.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_api.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,525 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// This header specifies the Chrome Plugin API. It is based heavily on NPAPI. +// The key difference is that Chrome plugins can be loaded for the lifetime +// of the browser, and are not tied to a specific web page. +// +// NOTE: This API is not final and may change or go away at any point. +// +// All strings in the API are UTF8-encoded unless otherwise noted. + +#ifndef CHROME_COMMON_CHROME_PLUGIN_API_H__ +#define CHROME_COMMON_CHROME_PLUGIN_API_H__ + +#include "base/basictypes.h" + +#ifndef STDCALL +#ifdef WIN32 +#define STDCALL __stdcall +#else +#define STDCALL +#endif // WIN32 +#endif // STDCALL + +#ifdef __cplusplus +extern "C" { +#endif + +// The current version of the API, used by the 'version' field of CPPluginFuncs +// and CPBrowserFuncs. +#define CP_MAJOR_VERSION 0 +#define CP_MINOR_VERSION 9 +#define CP_VERSION ((CP_MAJOR_VERSION << 8) | (CP_MINOR_VERSION)) + +#define CP_GET_MAJOR_VERSION(version) ((version & 0xff00) >> 8) +#define CP_GET_MINOR_VERSION(version) (version & 0x00ff) + +typedef unsigned char CPBool; + +// Chrome plugins can be loaded into different process types. +typedef enum { + CP_PROCESS_BROWSER = 0, + CP_PROCESS_PLUGIN, + CP_PROCESS_RENDERER, +} CPProcessType; + +// Return codes. Error values are negative. +typedef enum { + // No error + CPERR_SUCCESS = 0, + + // (network) An asynchronous IO operation is not complete + CPERR_IO_PENDING = -1, + + // Generic failure + CPERR_FAILURE = -2, + + // The API versions used by plugin and host are incompatible + CPERR_INVALID_VERSION = -3, + + // The operation was cancelled + CPERR_CANCELLED = -4, + + // An invalid parameter was passed + CPERR_INVALID_PARAMETER = -5, +} CPError; + +// Types of response info metadata to query using CPP_GetResponseInfo. +typedef enum { + // HTTP status code. + CPRESPONSEINFO_HTTP_STATUS = 0, + + // Raw headers from the server, including the status line. Headers should + // be delimited by "\0", and end with "\0\0" (a blank line). + CPRESPONSEINFO_HTTP_RAW_HEADERS = 1, +} CPResponseInfoType; + +// An identifier for the plugin used by the browser. +typedef struct _CPID_t { + int unused; +} CPID_t; +typedef struct _CPID_t* CPID; + +// An identifier that encapsulates the browsing context needed by various APIs. +// This includes information about what tab a request was made from, and what +// profile is active. Note that this ID is global to all processes, so it can +// be passed from one process to another. The value 0 is reserved for an +// undefined context. +typedef uint32 CPBrowsingContext; + +// Types of context info to query using CPB_GetBrowsingContextInfo. +typedef enum { + // The data directory for the profile associated with this context as a + // pointer to a null-terminated string. The plugin can save persistent data + // to this directory. The returned pointer should be freed using CPB_Free. + CPBROWSINGCONTEXT_DATA_DIR_PTR = 0, + + // The locale language code used for the browser UI. The returned pointer + // should be freed using CPB_Free. + CPBROWSINGCONTEXT_UI_LOCALE_PTR = 1, +} CPBrowsingContextInfoType; + +// A network request object. +typedef struct _CPRequest { + void* pdata; // plugin private data + const char* url; // the URL being requested + const char* method; // the request method as an uppercase string (ex: "GET") + CPBrowsingContext context; // context in which this request was made +} CPRequest; + +typedef enum { + CPREQUESTLOAD_NORMAL = 0, + + // This is "normal reload", meaning an if-none-match/if-modified-since query + CPREQUESTLOAD_VALIDATE_CACHE = 1 << 0, + + // This is "shift-reload", meaning a "pragma: no-cache" end-to-end fetch + CPREQUESTLOAD_BYPASS_CACHE = 1 << 1, + + // This is a back/forward style navigation where the cached content should + // be preferred over any protocol specific cache validation. + CPREQUESTLOAD_PREFERRING_CACHE = 1 << 2, + + // This is a navigation that will fail if it cannot serve the requested + // resource from the cache (or some equivalent local store). + CPREQUESTLOAD_ONLY_FROM_CACHE = 1 << 3, + + // This is a navigation that will not use the cache at all. It does not + // impact the HTTP request headers. + CPREQUESTLOAD_DISABLE_CACHE = 1 << 4, + + // This navigation should not be intercepted by plugins. + CPREQUESTLOAD_DISABLE_INTERCEPT = 1 << 5, + + // This request should be loaded synchronously. What this means is that + // CPR_StartRequest and CPR_Read will never return CPERR_IO_PENDING - they + // will block until a response is available, and return success or failure. + CPREQUESTLOAD_SYNCHRONOUS = 1 << 20, +} CPRequestLoadFlags; + +// +// Functions provided by plugin to host. +// + +// Called when the browser is unloading the plugin. +typedef CPError (STDCALL *CPP_ShutdownFunc)(void); + +// Returns true if the plugin is interested in handling this request. +typedef CPBool (STDCALL *CPP_ShouldInterceptRequestFunc)(CPRequest* request); + +// Called when an HTML dialog was closed. json_retval is the JSON string +// containing the return value sent back by the dialog (using Chrome's +// JavaScript DOM bindings). +typedef void (STDCALL *CPP_HtmlDialogClosedFunc)( + void* plugin_context, const char* json_retval); + +// Asks the plugin to handle the given command. 'command_data' is command- +// specific data used for some builtin commands: see gears_api.h for +// possible types. It is only valid for the duration of this call. +typedef CPError (STDCALL *CPP_HandleCommandFunc)( + CPBrowsingContext context, int command, void* command_data); + +// +// Functions provided by host to plugin. +// + +// Asks the host to handle the given command. 'command_data' is +// command-specific data used for some builtin commands: see gears_api.h for +// possible types. It is only valid for the duration of this call. +typedef CPError (STDCALL *CPB_HandleCommandFunc)( + CPID id, CPBrowsingContext context, int command, void* command_data); + +// Asks the browser to enable/disable request interception for this plugin for +// the given schemes. 'schemes' is an array of strings containing the scheme +// names the plugin wishes to handle; case is ignored. If 'schemes' is NULL or +// empty, request interception is disabled for this plugin. Multiple calls to +// this function will add to the existing set of enabled schemes. The browser +// should call the plugin's CPP_ShouldInterceptRequestFunc for any network +// requests it makes that match a given scheme. The browser may choose not to +// allow the plugin to intercept certain protocols. +typedef void (STDCALL *CPB_EnableRequestInterceptFunc)( + CPID id, const char** schemes, uint32 num_schemes); + +// Asks the browser to create a request object for the given method/url. +// Returns CPERR_SUCCESS and puts the new object into the 'request' field on +// success, or an error code on failure. The plugin must call CPR_EndRequest +// to clean up the request object when it is done with it. +typedef CPError (STDCALL *CPB_CreateRequestFunc)( + CPID id, CPBrowsingContext context, const char* method, const char* url, + CPRequest** request); + +// Queries the browser's cookie store for cookies set for the given URL. +// Sets 'cookies' to an allocated string containing the cookies as +// semicolon-delimited "name=value" pairs on success, NULL on failure. +// The memory should be freed using CPB_Free when done. +typedef CPError (STDCALL *CPB_GetCookiesFunc)( + CPID id, CPBrowsingContext context, const char* url, char** cookies); + +// Allocates memory for the given size using the browser's allocator. Call +// CPB_Free when done. +typedef void* (STDCALL *CPB_AllocFunc)(uint32 size); + +// Frees a pointer allocated by CPB_Alloc. +typedef void (STDCALL *CPB_FreeFunc)(void* memory); + + +// Sets a flag that influences when the plugin process created to host +// the plugin is shutdown. Generally, the plugin process is terminated +// when no more plugin instances exist, this is the default behavior. +// If keep_alive is non-zero, the process will not be terminated when +// the instance count goes to zero. Note: a non-zero keep_alive value +// does not prevent the plugin process from being terminated upon +// overall browser shutdown. +typedef void (STDCALL *CPB_SetKeepProcessAliveFunc)(CPID id, + CPBool keep_alive); + +// Asks the browser to show an HTML dialog to the user. The dialog contents +// should be loaded from the given URL. The 'json_arguments' is a JSON string +// that the dialog can fetch using Chrome's JavaScript DOM bindings. This call +// will block until the dialog is closed. On success, 'json_retval' will +// contain the JSON string sent back by the dialog (using Chrome's JavaScript +// DOM bindings), and CPERR_SUCCESS is returned. 'json_retval' should be freed +// using CPB_Free when done. +typedef CPError (STDCALL *CPB_ShowHtmlDialogModalFunc)( + CPID id, CPBrowsingContext context, const char* url, int width, int height, + const char* json_arguments, char** json_retval); + +// Similar to CPB_ShowHtmlDialogModalFunc, but does not block. When the dialog +// is closed, CPP_HtmlDialogClosed is called with the JSON return value and the +// given 'plugin_context', which may be used by the plugin to associate other +// data with the dialog. +typedef CPError (STDCALL *CPB_ShowHtmlDialogFunc)( + CPID id, CPBrowsingContext context, const char* url, int width, int height, + const char* json_arguments, void* plugin_context); + +// Get the browsing context associated with the given NPAPI instance. +typedef CPBrowsingContext (STDCALL *CPB_GetBrowsingContextFromNPPFunc)( + struct _NPP* npp); + +// Queries for some meta data associated with the given browsing context. See +// CPBrowsingContextInfoType for possible queries. If buf_size is too small to +// contain the entire data, the return value will indicate the size required. +// Otherwise, the return value is a CPError or CPERR_SUCCESS. +typedef int (STDCALL *CPB_GetBrowsingContextInfoFunc)( + CPID id, CPBrowsingContext context, CPBrowsingContextInfoType type, + void* buf, uint32 buf_size); + +// Given an URL string, returns the string of command-line arguments that should +// be passed to start the browser at the given URL. 'arguments' should be freed +// using CPB_Free when done. +typedef CPError (STDCALL *CPB_GetCommandLineArgumentsFunc)( + CPID id, CPBrowsingContext context, const char* url, char** arguments); + +// Asks the browser to let the plugin handle the given UI command. When the +// command is invoked, the browser will call CPP_HandleCommand. 'command' +// should be an integer identifier. Currently only builtin commands are +// supported, but in the future we may want to let plugins add custom menu +// commands with their own descriptions. +typedef CPError (STDCALL *CPB_AddUICommandFunc)(CPID id, int command); + +// +// Functions related to making network requests. +// Both the host and plugin will implement their own versions of these. +// + +// Starts the request. Returns CPERR_SUCCESS if the request could be started +// immediately, at which point the response info is available to be read. +// Returns CPERR_IO_PENDING if an asynchronous operation was started, and the +// caller should wait for CPRR_StartCompleted to be called before reading the +// response info. Returns an error code on failure. +typedef CPError (STDCALL *CPR_StartRequestFunc)(CPRequest* request); + +// Stops or cancels the request. The caller should not access the request +// object after this call. If an asynchronous IO operation is pending, the +// operation is aborted and the caller will not receive a callback for it. +typedef void (STDCALL *CPR_EndRequestFunc)(CPRequest* request, CPError reason); + +// Sets the additional request headers to append to the standard headers that +// would normally be made with this request. Headers should be \r\n-delimited, +// with no terminating \r\n. Extra headers are not checked against the standard +// headers for duplicates. Must be called before CPRR_StartCompletedFunc. +// Plugins should avoid setting the following headers: User-Agent, +// Content-Length. +typedef void (STDCALL *CPR_SetExtraRequestHeadersFunc)(CPRequest* request, + const char* headers); + +// Sets the load flags for this request. 'flags' is a bitwise-OR of +// CPRequestLoadFlags. Must be called before CPRR_StartCompletedFunc. +typedef void (STDCALL *CPR_SetRequestLoadFlagsFunc)(CPRequest* request, + uint32 flags); + +// Appends binary data to the request body of a POST or PUT request. The caller +// should set the "Content-Type" header to the appropriate mime type using +// CPR_SetExtraRequestHeadersFunc. This can be called multiple times to append +// a sequence of data segments to upload. Must be called before +// CPR_StartRequestFunc. +typedef void (STDCALL *CPR_AppendDataToUploadFunc)( + CPRequest* request, const char* bytes, int bytes_len); + +// Appends the contents of a file to the request body of a POST or PUT request. +// 'offset' and 'length' can be used to append a subset of the file. Pass zero +// for 'length' and 'offset' to upload the entire file. 'offset' +// indicates where the data to upload begins in the file. 'length' indicates +// how much of the file to upload. A 'length' of zero is interpretted as to +// end-of-file. If 'length' and 'offset' indicate a range beyond end of file, +// the amount sent is clipped at eof. +// See CPR_AppendDataToUploadFunc for additional usage information. +// (added in v0.4) +typedef CPError (STDCALL *CPR_AppendFileToUploadFunc)( + CPRequest* request, const char* filepath, uint64 offset, uint64 length); + +// Queries for some response meta data. See CPResponseInfoType for possible +// queries. If buf_size is too small to contain the entire data, the return +// value will indicate the size required. Otherwise, the return value is a +// CPError or CPERR_SUCCESS. +typedef int (STDCALL *CPR_GetResponseInfoFunc)( + CPRequest* request, CPResponseInfoType type, + void* buf, uint32 buf_size); + +// Attempts to read a request's response data. The number of bytes read is +// returned; 0 indicates an EOF. CPERR_IO_PENDING is returned if an +// asynchronous operation was started, and CPRR_ReadCompletedFunc will be called +// when it completes; 'buf' must be available until the operation completes. +// Returns an error code on failure. +typedef int (STDCALL *CPR_ReadFunc)( + CPRequest* request, void* buf, uint32 buf_size); + +// +// Functions related to serving network requests. +// Both the host and plugin will implement their own versions of these. +// + +// Called upon a server-initiated redirect. The request will still hold the +// original URL, and 'new_url' will be the redirect destination. +typedef void (STDCALL *CPRR_ReceivedRedirectFunc)(CPRequest* request, + const char* new_url); + +// Called when an asynchronous CPR_StartRequest call has completed, once all +// redirects are followed. On success, 'result' holds CPERR_SUCCESS and the +// response info is available to be read via CPR_GetResponseInfo. On error, +// 'result' holds the error code. +typedef void (STDCALL *CPRR_StartCompletedFunc)(CPRequest* request, + CPError result); + +// Called when an asynchronous CPR_Read call has completed. On success, +// 'bytes_read' will hold the number of bytes read into the buffer that was +// passed to CPR_Read; 0 indicates an EOF, and the request object will be +// destroyed after the call completes. On failure, 'bytes_read' holds the error +// code. +typedef void (STDCALL *CPRR_ReadCompletedFunc)(CPRequest* request, + int bytes_read); + +// Called as upload progress is being made for async POST requests. +// (added in v0.5) +typedef void (STDCALL *CPRR_UploadProgressFunc)(CPRequest* request, + uint64 position, + uint64 size); + +// +// Functions to support the sending and receipt of messages between processes. +// + +// Returns true if the plugin process is running +typedef CPBool (STDCALL *CPB_IsPluginProcessRunningFunc)(CPID id); + +// Returns the type of the current process. +typedef CPProcessType (STDCALL *CPB_GetProcessTypeFunc)(CPID id); + +// Asks the browser to send raw data to the other process hosting an instance of +// this plugin. If needed, the plugin process will be started prior to sending +// the message. +typedef CPError (STDCALL *CPB_SendMessageFunc)(CPID id, + const void *data, + uint32 data_len); + +// Asks the browser to send raw data to the other process hosting an instance of +// this plugin. This function only works from the plugin or renderer process. +// This function blocks until the message is processed. The memory should be +// freed using CPB_Free when done. +typedef CPError (STDCALL *CPB_SendSyncMessageFunc)(CPID id, + const void *data, + uint32 data_len, + void **retval, + uint32 *retval_len); + +// This function asynchronously calls the provided function on the plugin +// thread. user_data is passed as the argument to the function. +typedef CPError (STDCALL *CPB_PluginThreadAsyncCallFunc)(CPID id, + void (*func)(void *), + void *user_data); + +// This function creates an open file dialog. The process is granted access +// to any files that are selected. |multiple_files| determines if more than +// one file can be selected. +typedef CPError (STDCALL *CPB_OpenFileDialogFunc)(CPID id, + CPBrowsingContext context, + bool multiple_files, + const char *title, + const char *filter, + void *user_data); + +// Informs the plugin of raw data having been sent from another process. +typedef void (STDCALL *CPP_OnMessageFunc)(void *data, uint32 data_len); + +// Informs the plugin of raw data having been sent from another process. +typedef void (STDCALL *CPP_OnSyncMessageFunc)(void *data, uint32 data_len, + void **retval, + uint32 *retval_len); + +// Informs the plugin that the file dialog has completed, and contains the +// results. +typedef void (STDCALL *CPP_OnFileDialogResultFunc)(void *data, + const char **files, + uint32 files_len); + +// Function table for issuing requests using via the other side's network stack. +// For the plugin, this functions deal with issuing requests through the +// browser. For the browser, these functions deal with allowing the plugin to +// intercept requests. +typedef struct _CPRequestFuncs { + uint16 size; + CPR_SetExtraRequestHeadersFunc set_extra_request_headers; + CPR_SetRequestLoadFlagsFunc set_request_load_flags; + CPR_AppendDataToUploadFunc append_data_to_upload; + CPR_StartRequestFunc start_request; + CPR_EndRequestFunc end_request; + CPR_GetResponseInfoFunc get_response_info; + CPR_ReadFunc read; + CPR_AppendFileToUploadFunc append_file_to_upload; +} CPRequestFuncs; + +// Function table for handling requests issued by the other side. For the +// plugin, these deal with serving requests that the plugin has intercepted. For +// the browser, these deal with serving requests that the plugin has issued +// through us. +typedef struct _CPResponseFuncs { + uint16 size; + CPRR_ReceivedRedirectFunc received_redirect; + CPRR_StartCompletedFunc start_completed; + CPRR_ReadCompletedFunc read_completed; + CPRR_UploadProgressFunc upload_progress; +} CPResponseFuncs; + +// Function table of CPP functions (functions provided by plugin to host). This +// structure is filled in by the plugin in the CP_Initialize call, except for +// the 'size' field, which is set by the browser. The version fields should be +// set to those that the plugin was compiled using. +typedef struct _CPPluginFuncs { + uint16 size; + uint16 version; + CPRequestFuncs* request_funcs; + CPResponseFuncs* response_funcs; + CPP_ShutdownFunc shutdown; + CPP_ShouldInterceptRequestFunc should_intercept_request; + CPP_OnMessageFunc on_message; + CPP_HtmlDialogClosedFunc html_dialog_closed; + CPP_HandleCommandFunc handle_command; + CPP_OnSyncMessageFunc on_sync_message; + CPP_OnFileDialogResultFunc on_file_dialog_result; +} CPPluginFuncs; + +// Function table CPB functions (functions provided by host to plugin). +// This structure is filled in by the browser and provided to the plugin. The +// plugin will likely want to save a copy of this structure to make calls +// back to the browser. +typedef struct _CPBrowserFuncs { + uint16 size; + uint16 version; + CPRequestFuncs* request_funcs; + CPResponseFuncs* response_funcs; + CPB_EnableRequestInterceptFunc enable_request_intercept; + CPB_CreateRequestFunc create_request; + CPB_GetCookiesFunc get_cookies; + CPB_AllocFunc alloc; + CPB_FreeFunc free; + CPB_SetKeepProcessAliveFunc set_keep_process_alive; + CPB_ShowHtmlDialogModalFunc show_html_dialog_modal; + CPB_ShowHtmlDialogFunc show_html_dialog; + CPB_IsPluginProcessRunningFunc is_plugin_process_running; + CPB_GetProcessTypeFunc get_process_type; + CPB_SendMessageFunc send_message; + CPB_GetBrowsingContextFromNPPFunc get_browsing_context_from_npp; + CPB_GetBrowsingContextInfoFunc get_browsing_context_info; + CPB_GetCommandLineArgumentsFunc get_command_line_arguments; + CPB_AddUICommandFunc add_ui_command; + CPB_HandleCommandFunc handle_command; + CPB_SendSyncMessageFunc send_sync_message; + CPB_PluginThreadAsyncCallFunc plugin_thread_async_call; + CPB_OpenFileDialogFunc open_file_dialog; +} CPBrowserFuncs; + + +// +// DLL exports +// + +// This export is optional. +// Prior to calling CP_Initialize, the browser may negotiate with the plugin +// regarding which version of the CPAPI to utilize. 'min_version' is the +// lowest version of the interface supported by the browser, 'max_version' is +// the highest supported version. The plugin can specify which version within +// the range should be used. This version will be reflected in the version field +// of the CPBrowserFuncs struct passed to CP_Initialize. If this function +// returns an error code, CP_Initialize will not be called. If function is not +// exported by the chrome plugin module, CP_Initiailize will be called with +// a version of the host's choosing. +typedef CPError (STDCALL *CP_VersionNegotiateFunc)( + uint16 min_version, uint16 max_version, uint16 *selected_version); + +// 'bfuncs' are the browser functions provided to the plugin. 'id' is the +// plugin identifier that the plugin should use when calling browser functions. +// The plugin should initialize 'pfuncs' with pointers to its own functions, +// or return an error code. +// All functions and entry points should be called on the same thread. The +// plugin should not attempt to call a browser function from a thread other +// than the one CP_InitializeFunc is called from. +typedef CPError (STDCALL *CP_InitializeFunc)( + CPID id, const CPBrowserFuncs* bfuncs, CPPluginFuncs* pfuncs); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // CHROME_COMMON_CHROME_PLUGIN_API_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_lib.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_lib.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_lib.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_lib.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,306 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/chrome_plugin_lib.h" + +#include "base/command_line.h" +#include "base/hash_tables.h" +#include "base/histogram.h" +#include "base/message_loop.h" +#include "base/path_service.h" +#include "base/perftimer.h" +#include "base/thread.h" +#if defined(OS_WIN) +#include "base/registry.h" +#endif +#include "base/string_util.h" +#include "chrome/common/chrome_counters.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/chrome_paths.h" +#include "webkit/glue/plugins/plugin_list.h" + +using base::TimeDelta; + +// TODO(port): revisit when plugins happier +#if defined(OS_WIN) +const TCHAR ChromePluginLib::kRegistryChromePlugins[] = + _T("Software\\Google\\Chrome\\Plugins"); +static const TCHAR kRegistryLoadOnStartup[] = _T("LoadOnStartup"); +static const TCHAR kRegistryPath[] = _T("Path"); +#endif + +typedef base::hash_map > + PluginMap; + +// A map of all the instantiated plugins. +static PluginMap* g_loaded_libs; + +// The thread plugins are loaded and used in, lazily initialized upon +// the first creation call. +static PlatformThreadId g_plugin_thread_id = 0; +static MessageLoop* g_plugin_thread_loop = NULL; + +#ifdef GEARS_STATIC_LIB +// defined in gears/base/chrome/module_cr.cc +CPError STDCALL Gears_CP_Initialize(CPID id, const CPBrowserFuncs *bfuncs, + CPPluginFuncs *pfuncs); +#endif + +static bool IsSingleProcessMode() { + // We don't support ChromePlugins in single-process mode. + return CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess); +} + +// static +bool ChromePluginLib::IsInitialized() { + return (g_loaded_libs != NULL); +} + +// static +ChromePluginLib* ChromePluginLib::Create(const FilePath& filename, + const CPBrowserFuncs* bfuncs) { + // Keep a map of loaded plugins to ensure we only load each library once. + if (!g_loaded_libs) { + g_loaded_libs = new PluginMap(); + g_plugin_thread_id = PlatformThread::CurrentId(); + g_plugin_thread_loop = MessageLoop::current(); + } + DCHECK(IsPluginThread()); + + PluginMap::const_iterator iter = g_loaded_libs->find(filename); + if (iter != g_loaded_libs->end()) + return iter->second; + + scoped_refptr plugin(new ChromePluginLib(filename)); + if (!plugin->CP_Initialize(bfuncs)) + return NULL; + + (*g_loaded_libs)[filename] = plugin; + return plugin; +} + +// static +ChromePluginLib* ChromePluginLib::Find(const FilePath& filename) { + if (g_loaded_libs) { + PluginMap::const_iterator iter = g_loaded_libs->find(filename); + if (iter != g_loaded_libs->end()) + return iter->second; + } + return NULL; +} + +// static +void ChromePluginLib::Destroy(const FilePath& filename) { + DCHECK(g_loaded_libs); + PluginMap::iterator iter = g_loaded_libs->find(filename); + if (iter != g_loaded_libs->end()) { + iter->second->Unload(); + g_loaded_libs->erase(iter); + } +} + +// static +bool ChromePluginLib::IsPluginThread() { + return PlatformThread::CurrentId() == g_plugin_thread_id; +} + +// static +MessageLoop* ChromePluginLib::GetPluginThreadLoop() { + return g_plugin_thread_loop; +} + +// static +void ChromePluginLib::RegisterPluginsWithNPAPI() { + // We don't support ChromePlugins in single-process mode. + if (IsSingleProcessMode()) + return; + + FilePath path; + if (!PathService::Get(chrome::FILE_GEARS_PLUGIN, &path)) + return; + // Note: we can only access the NPAPI list because the PluginService has done + // the locking for us. We should not touch it anywhere else. + NPAPI::PluginList::AddExtraPluginPath(path); +} + +static void LogPluginLoadTime(const TimeDelta &time) { + UMA_HISTOGRAM_TIMES("Gears.LoadTime", time); +} + +// static +void ChromePluginLib::LoadChromePlugins(const CPBrowserFuncs* bfuncs) { + static bool loaded = false; + if (loaded) + return; + loaded = true; + + // We don't support ChromePlugins in single-process mode. + if (IsSingleProcessMode()) + return; + + FilePath path; + if (!PathService::Get(chrome::FILE_GEARS_PLUGIN, &path)) + return; + + PerfTimer timer; + ChromePluginLib::Create(path, bfuncs); + LogPluginLoadTime(timer.Elapsed()); + + // TODO(mpcomplete): disabled loading of plugins from the registry until we + // phase out registry keys from the gears installer. +#if 0 + for (RegistryKeyIterator iter(HKEY_CURRENT_USER, kRegistryChromePlugins); + iter.Valid(); ++iter) { + // Use the registry to gather plugin across the file system. + std::wstring reg_path = kRegistryChromePlugins; + reg_path.append(L"\\"); + reg_path.append(iter.Name()); + RegKey key(HKEY_CURRENT_USER, reg_path.c_str()); + + DWORD is_persistent; + if (key.ReadValueDW(kRegistryLoadOnStartup, &is_persistent) && + is_persistent) { + std::wstring path; + if (key.ReadValue(kRegistryPath, &path)) { + ChromePluginLib::Create(path, bfuncs); + } + } + } +#endif +} + +// static +void ChromePluginLib::UnloadAllPlugins() { + if (g_loaded_libs) { + PluginMap::iterator it; + for (PluginMap::iterator it = g_loaded_libs->begin(); + it != g_loaded_libs->end(); ++it) { + it->second->Unload(); + } + delete g_loaded_libs; + g_loaded_libs = NULL; + } +} + +const CPPluginFuncs& ChromePluginLib::functions() const { + DCHECK(initialized_); + DCHECK(IsPluginThread()); + return plugin_funcs_; +} + +ChromePluginLib::ChromePluginLib(const FilePath& filename) + : filename_(filename), +#if defined(OS_WIN) + module_(0), +#endif + initialized_(false), + CP_VersionNegotiate_(NULL), + CP_Initialize_(NULL) { + memset((void*)&plugin_funcs_, 0, sizeof(plugin_funcs_)); +} + +ChromePluginLib::~ChromePluginLib() { +} + +bool ChromePluginLib::CP_Initialize(const CPBrowserFuncs* bfuncs) { + LOG(INFO) << "ChromePluginLib::CP_Initialize(" << filename_.value() << + "): initialized=" << initialized_; + if (initialized_) + return true; + + if (!Load()) + return false; + + if (CP_VersionNegotiate_) { + uint16 selected_version = 0; + CPError rv = CP_VersionNegotiate_(CP_VERSION, CP_VERSION, + &selected_version); + if ( (rv != CPERR_SUCCESS) || (selected_version != CP_VERSION)) + return false; + } + + plugin_funcs_.size = sizeof(plugin_funcs_); + CPError rv = CP_Initialize_(cpid(), bfuncs, &plugin_funcs_); + initialized_ = (rv == CPERR_SUCCESS) && + (CP_GET_MAJOR_VERSION(plugin_funcs_.version) == CP_MAJOR_VERSION) && + (CP_GET_MINOR_VERSION(plugin_funcs_.version) <= CP_MINOR_VERSION); + LOG(INFO) << "ChromePluginLib::CP_Initialize(" << filename_.value() << + "): initialized=" << initialized_ << + "): result=" << rv; + + return initialized_; +} + +void ChromePluginLib::CP_Shutdown() { + DCHECK(initialized_); + functions().shutdown(); + initialized_ = false; + memset((void*)&plugin_funcs_, 0, sizeof(plugin_funcs_)); +} + +int ChromePluginLib::CP_Test(void* param) { + DCHECK(initialized_); + if (!CP_Test_) + return -1; + return CP_Test_(param); +} + +bool ChromePluginLib::Load() { +#if !defined(OS_WIN) + // TODO(port): plugins not yet implemented + NOTIMPLEMENTED() << " -- gears loading code."; + return false; +#else + DCHECK(module_ == 0); +#ifdef GEARS_STATIC_LIB + FilePath path; + if (filename_.BaseName().value().find(FILE_PATH_LITERAL("gears")) == 0) { + CP_Initialize_ = &Gears_CP_Initialize; + return true; + } +#endif + + module_ = LoadLibrary(filename_.value().c_str()); + if (module_ == 0) + return false; + + // required initialization function + CP_Initialize_ = reinterpret_cast + (GetProcAddress(module_, "CP_Initialize")); + + if (!CP_Initialize_) { + FreeLibrary(module_); + module_ = 0; + return false; + } + + // optional version negotiation function + CP_VersionNegotiate_ = reinterpret_cast + (GetProcAddress(module_, "CP_VersionNegotiate")); + + // optional test function + CP_Test_ = reinterpret_cast + (GetProcAddress(module_, "CP_Test")); + + return true; +#endif +} + +void ChromePluginLib::Unload() { + NotificationService::current()->Notify( + NotificationType::CHROME_PLUGIN_UNLOADED, + Source(this), + NotificationService::NoDetails()); + + if (initialized_) + CP_Shutdown(); + +#if defined(OS_WIN) + if (module_) { + FreeLibrary(module_); + module_ = 0; + } +#endif +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_lib.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_lib.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_lib.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_lib.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,106 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_CHROME_PLUGIN_LIB_H_ +#define CHROME_COMMON_CHROME_PLUGIN_LIB_H_ + +#include + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/ref_counted.h" +#include "chrome/common/chrome_plugin_api.h" + +class MessageLoop; + +// A ChromePluginLib is a single Chrome Plugin Library. +// This class is used in the browser process (IO thread), and the plugin process +// (plugin thread). It should not be accessed on other threads, because it +// issues a NOTIFY_CHROME_PLUGIN_UNLOADED notification. +class ChromePluginLib : public base::RefCounted { + public: + static bool IsInitialized(); + static ChromePluginLib* Create(const FilePath& filename, + const CPBrowserFuncs* bfuncs); + static ChromePluginLib* Find(const FilePath& filename); + static void Destroy(const FilePath& filename); + static bool IsPluginThread(); + static MessageLoop* GetPluginThreadLoop(); + + static ChromePluginLib* FromCPID(CPID id) { + return reinterpret_cast(id); + } + + // Adds Chrome plugins to the NPAPI plugin list. + static void RegisterPluginsWithNPAPI(); + + // Loads all the plugins that are marked as "LoadOnStartup" in the + // registry. This should only be called in the browser process. + static void LoadChromePlugins(const CPBrowserFuncs* bfuncs); + + // Unloads all the loaded plugins and cleans up the plugin map. + static void UnloadAllPlugins(); + + // Returns true if the plugin is currently loaded. + const bool is_loaded() const { return initialized_; } + + // Get the Plugin's function pointer table. + const CPPluginFuncs& functions() const; + + CPID cpid() { return reinterpret_cast(this); } + + const FilePath& filename() { return filename_; } + + // Plugin API functions + + // Method to call a test function in the plugin, used for unit tests. + int CP_Test(void* param); + +#if defined(OS_WIN) + // The registry path to search for Chrome Plugins/ + static const TCHAR kRegistryChromePlugins[]; +#endif // defined(OS_WIN) + + private: + friend class base::RefCounted; + + ChromePluginLib(const FilePath& filename); + ~ChromePluginLib(); + + // Method to initialize a Plugin. + // Initialize can be safely called multiple times. + bool CP_Initialize(const CPBrowserFuncs* bfuncs); + + // Method to shutdown a Plugin. + void CP_Shutdown(); + + // Attempts to load the plugin. + // Returns true if it is a legitimate plugin, false otherwise + bool Load(); + + // Unloads the plugin. + void Unload(); + + FilePath filename_; // the path to the plugin +#if defined(OS_WIN) + // TODO(port): Remove ifdefs when we have portable replacement for HMODULE. + HMODULE module_; // the opened plugin handle +#endif // defined(OS_WIN) + bool initialized_; // is the plugin initialized + + // Exported symbols from the plugin, looked up by name. + CP_VersionNegotiateFunc CP_VersionNegotiate_; + CP_InitializeFunc CP_Initialize_; + + // Additional function pointers provided by the plugin. + CPPluginFuncs plugin_funcs_; + + // Used for unit tests. + typedef int (STDCALL *CP_TestFunc)(void*); + CP_TestFunc CP_Test_; + + DISALLOW_COPY_AND_ASSIGN(ChromePluginLib); +}; + +#endif // CHROME_COMMON_CHROME_PLUGIN_LIB_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_unittest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,287 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// Tests exercising the Chrome Plugin API. + +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "chrome/browser/chrome_plugin_host.h" +#include "chrome/browser/profile.h" +#include "chrome/common/chrome_plugin_lib.h" +#include "chrome/test/chrome_plugin/test_chrome_plugin.h" +#include "net/base/io_buffer.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/url_request_test_job.h" +#include "net/url_request/url_request_unittest.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const wchar_t kDocRoot[] = L"chrome/test/data"; +const char kPluginFilename[] = "test_chrome_plugin.dll"; +const int kResponseBufferSize = 4096; + +class ChromePluginTest : public testing::Test, public URLRequest::Delegate { + public: + ChromePluginTest() + : request_(NULL), + response_buffer_(new net::IOBuffer(kResponseBufferSize)), + plugin_(NULL), + expected_payload_(NULL), + request_context_(new TestURLRequestContext()) { + } + + // Loads/unloads the chrome test plugin. + void LoadPlugin(); + void UnloadPlugin(); + + // Runs the test and expects the given payload as a response. If expectation + // is NULL, the request is expected to fail. + void RunTest(const GURL& url, const TestResponsePayload* expected_payload); + + // URLRequest::Delegate implementations + virtual void OnReceivedRedirect(URLRequest* request, + const GURL& new_url) { } + virtual void OnResponseStarted(URLRequest* request); + virtual void OnReadCompleted(URLRequest* request, int bytes_read); + + // Helper called when the URLRequest is done. + void OnURLRequestComplete(); + + // testing::Test + virtual void SetUp() { + LoadPlugin(); + URLRequest::RegisterProtocolFactory("test", &URLRequestTestJob::Factory); + + // We need to setup a default request context in order to issue HTTP + // requests. + DCHECK(!Profile::GetDefaultRequestContext()); + Profile::set_default_request_context(request_context_.get()); + } + virtual void TearDown() { + UnloadPlugin(); + URLRequest::RegisterProtocolFactory("test", NULL); + + Profile::set_default_request_context(NULL); + + // Clear the request before flushing the message loop since killing the + // request can result in the generation of more tasks. + request_.reset(); + + // Flush the message loop to make Purify happy. + message_loop_.RunAllPending(); + } + protected: + MessageLoopForIO message_loop_; + + // Note: we use URLRequest (instead of URLFetcher) because this allows the + // request to be intercepted. + scoped_ptr request_; + scoped_refptr response_buffer_; + std::string response_data_; + + ChromePluginLib* plugin_; + TestFuncParams::PluginFuncs test_funcs_; + const TestResponsePayload* expected_payload_; + scoped_refptr request_context_; +}; + +static void STDCALL CPT_Complete(CPRequest* request, bool success, + const std::string& raw_headers, + const std::string& body) { + GURL url(request->url); + if (url == GURL(kChromeTestPluginPayloads[0].url)) { + // This URL should fail, because the plugin should not have intercepted it. + EXPECT_FALSE(success); + MessageLoop::current()->Quit(); + return; + } + + scoped_refptr headers( + new net::HttpResponseHeaders(raw_headers)); + EXPECT_TRUE(success); + EXPECT_EQ(200, headers->response_code()); + + if (url == URLRequestTestJob::test_url_1()) { + EXPECT_EQ(URLRequestTestJob::test_data_1(), body); + } else if (url == URLRequestTestJob::test_url_2()) { + EXPECT_EQ(URLRequestTestJob::test_data_2(), body); + } else if (url.spec().find("echo") != std::string::npos) { + EXPECT_EQ(kChromeTestPluginPostData, body); + } + + MessageLoop::current()->Quit(); +} + +static void STDCALL CPT_InvokeLater(TestFuncParams::CallbackFunc callback, + void* callback_data, int delay_ms) { + MessageLoop::current()->PostDelayedTask(FROM_HERE, + NewRunnableFunction(callback, callback_data), delay_ms); +} + +void ChromePluginTest::LoadPlugin() { + FilePath path; + PathService::Get(base::DIR_EXE, &path); + path = path.AppendASCII(kPluginFilename); + plugin_ = ChromePluginLib::Create(path, GetCPBrowserFuncsForBrowser()); + + // Exchange test APIs with the plugin. + TestFuncParams params; + params.bfuncs.test_complete = CPT_Complete; + params.bfuncs.invoke_later = CPT_InvokeLater; + EXPECT_EQ(CPERR_SUCCESS, plugin_->CP_Test(¶ms)); + test_funcs_ = params.pfuncs; + + EXPECT_TRUE(plugin_); +} + +void ChromePluginTest::UnloadPlugin() { + ChromePluginLib::UnloadAllPlugins(); + plugin_ = NULL; +} + +void ChromePluginTest::RunTest(const GURL& url, + const TestResponsePayload* expected_payload) { + expected_payload_ = expected_payload; + + response_data_.clear(); + request_.reset(new URLRequest(url, this)); + request_->set_context(new TestURLRequestContext()); + request_->Start(); + + MessageLoop::current()->Run(); +} + +void ChromePluginTest::OnResponseStarted(URLRequest* request) { + DCHECK(request == request_); + + int bytes_read = 0; + if (request_->status().is_success()) + request_->Read(response_buffer_, kResponseBufferSize, &bytes_read); + OnReadCompleted(request_.get(), bytes_read); +} + +void ChromePluginTest::OnReadCompleted(URLRequest* request, int bytes_read) { + DCHECK(request == request_); + + do { + if (!request_->status().is_success() || bytes_read <= 0) + break; + response_data_.append(response_buffer_->data(), bytes_read); + } while (request_->Read(response_buffer_, kResponseBufferSize, &bytes_read)); + + if (!request_->status().is_io_pending()) { + OnURLRequestComplete(); + } +} + +void ChromePluginTest::OnURLRequestComplete() { + if (expected_payload_) { + EXPECT_TRUE(request_->status().is_success()); + + EXPECT_EQ(expected_payload_->status, request_->GetResponseCode()); + if (expected_payload_->mime_type) { + std::string mime_type; + EXPECT_TRUE(request_->response_headers()->GetMimeType(&mime_type)); + EXPECT_EQ(expected_payload_->mime_type, mime_type); + } + if (expected_payload_->body) { + EXPECT_EQ(expected_payload_->body, response_data_); + } + } else { + EXPECT_FALSE(request_->status().is_success()); + } + + MessageLoop::current()->Quit(); + // If MessageLoop::current() != main_loop_, it will be shut down when the + // main loop returns and this thread subsequently goes out of scope. +} + +}; // namespace + +// Tests that the plugin can intercept URLs. +TEST_F(ChromePluginTest, DoesIntercept) { + for (int i = 0; i < arraysize(kChromeTestPluginPayloads); ++i) { + RunTest(GURL(kChromeTestPluginPayloads[i].url), + &kChromeTestPluginPayloads[i]); + } +} + +// Tests that non-intercepted URLs are handled normally. +TEST_F(ChromePluginTest, DoesNotIntercept) { + TestResponsePayload about_blank = { + "about:blank", + false, + -1, + NULL, + "" + }; + RunTest(GURL(about_blank.url), &about_blank); +} + +// Tests that unloading the plugin correctly unregisters URL interception. +TEST_F(ChromePluginTest, UnregisterIntercept) { + UnloadPlugin(); + + RunTest(GURL(kChromeTestPluginPayloads[0].url), NULL); +} + +static void ProcessAllPendingMessages() { + while (URLRequestTestJob::ProcessOnePendingMessage()); +} + +// Tests that the plugin can issue a GET request and receives the data when +// it comes back synchronously. +TEST_F(ChromePluginTest, CanMakeGETRequestSync) { + // test_url_1 has a synchronous response + EXPECT_EQ(CPERR_SUCCESS, test_funcs_.test_make_request( + "GET", URLRequestTestJob::test_url_1())); + + // Note: we must add this task after we make the request, so that + // URLRequestTestJob's StartAsync task is added and run first. + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableFunction(&ProcessAllPendingMessages)); + MessageLoop::current()->Run(); +} + +// Tests that the plugin can issue a GET request and receives the data when +// it comes back asynchronously. +TEST_F(ChromePluginTest, CanMakeGETRequestAsync) { + // test_url_2 has an asynchronous response + EXPECT_EQ(CPERR_SUCCESS, test_funcs_.test_make_request( + "GET", URLRequestTestJob::test_url_2())); + + // Note: we must add this task after we make the request, so that + // URLRequestTestJob's StartAsync task is added and run first. + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableFunction(&ProcessAllPendingMessages)); + MessageLoop::current()->Run(); +} + +// Tests that the plugin can issue a POST request. +TEST_F(ChromePluginTest, CanMakePOSTRequest) { + scoped_refptr server = + HTTPTestServer::CreateServer(kDocRoot, NULL); + ASSERT_TRUE(NULL != server.get()); + + GURL url = server->TestServerPage("echo"); + + EXPECT_EQ(CPERR_SUCCESS, test_funcs_.test_make_request("POST", url)); + + // Note: we must add this task after we make the request, so that + // URLRequestTestJob's StartAsync task is added and run first. + MessageLoop::current()->PostTask(FROM_HERE, + NewRunnableFunction(&ProcessAllPendingMessages)); + MessageLoop::current()->Run(); +} + +// Tests that the plugin does not intercept its own requests. +TEST_F(ChromePluginTest, DoesNotInterceptOwnRequest) { + const TestResponsePayload& payload = kChromeTestPluginPayloads[0]; + + EXPECT_EQ(CPERR_SUCCESS, test_funcs_.test_make_request( + "GET", GURL(payload.url))); + + MessageLoop::current()->Run(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_util.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_util.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_util.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_util.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,165 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/chrome_plugin_util.h" + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/message_loop.h" +#include "base/string_util.h" +#include "chrome/common/chrome_plugin_lib.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/notification_service.h" +#include "net/base/load_flags.h" +#include "net/http/http_response_headers.h" + +// +// ScopableCPRequest +// + +ScopableCPRequest::ScopableCPRequest(const char* u, const char* m, + CPBrowsingContext c) { + pdata = NULL; + data = NULL; +#if defined(OS_WIN) + url = _strdup(u); + method = _strdup(m); +#else + url = strdup(u); + method = strdup(m); +#endif + context = c; +} + +ScopableCPRequest::~ScopableCPRequest() { + pdata = NULL; + data = NULL; + free(const_cast(url)); + free(const_cast(method)); +} + +// +// PluginHelper +// + +// static +void PluginHelper::DestroyAllHelpersForPlugin(ChromePluginLib* plugin) { + NotificationService::current()->Notify( + NotificationType::CHROME_PLUGIN_UNLOADED, + Source(plugin), + NotificationService::NoDetails()); +} + +PluginHelper::PluginHelper(ChromePluginLib* plugin) : plugin_(plugin) { + DCHECK(CalledOnValidThread()); + NotificationService::current()->AddObserver( + this, NotificationType::CHROME_PLUGIN_UNLOADED, + Source(plugin_)); +} + +PluginHelper::~PluginHelper() { + DCHECK(CalledOnValidThread()); + NotificationService::current()->RemoveObserver( + this, NotificationType::CHROME_PLUGIN_UNLOADED, + Source(plugin_)); +} + +void PluginHelper::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(CalledOnValidThread()); + DCHECK(type == NotificationType::CHROME_PLUGIN_UNLOADED); + DCHECK(plugin_ == Source(source).ptr()); + + delete this; +} + +// +// PluginResponseUtils +// + +uint32 PluginResponseUtils::CPLoadFlagsToNetFlags(uint32 flags) { + uint32 net_flags = 0; +#define HANDLE_FLAG(name) \ + if (flags & CPREQUEST##name) \ + net_flags |= net::name + + HANDLE_FLAG(LOAD_VALIDATE_CACHE); + HANDLE_FLAG(LOAD_BYPASS_CACHE); + HANDLE_FLAG(LOAD_PREFERRING_CACHE); + HANDLE_FLAG(LOAD_ONLY_FROM_CACHE); + HANDLE_FLAG(LOAD_DISABLE_CACHE); + HANDLE_FLAG(LOAD_DISABLE_INTERCEPT); + + net_flags |= net::LOAD_ENABLE_UPLOAD_PROGRESS; + + return net_flags; +} + +int PluginResponseUtils::GetResponseInfo( + const net::HttpResponseHeaders* response_headers, + CPResponseInfoType type, void* buf, size_t buf_size) { + if (!response_headers) + return CPERR_FAILURE; + + switch (type) { + case CPRESPONSEINFO_HTTP_STATUS: + if (buf && buf_size) { + int status = response_headers->response_code(); + memcpy(buf, &status, std::min(buf_size, sizeof(int))); + } + break; + case CPRESPONSEINFO_HTTP_RAW_HEADERS: { + const std::string& headers = response_headers->raw_headers(); + if (buf_size < headers.size()+1) + return static_cast(headers.size()+1); + if (buf) + memcpy(buf, headers.c_str(), headers.size()+1); + break; + } + default: + return CPERR_INVALID_VERSION; + } + + return CPERR_SUCCESS; +} + +CPError CPB_GetCommandLineArgumentsCommon(const char* url, + std::string* arguments) { + const CommandLine cmd = *CommandLine::ForCurrentProcess(); + std::wstring arguments_w; + + // Use the same UserDataDir for new launches that we currently have set. + std::wstring user_data_dir = cmd.GetSwitchValue(switches::kUserDataDir); + if (!user_data_dir.empty()) { + // Make sure user_data_dir is an absolute path. + if (file_util::AbsolutePath(&user_data_dir) && + file_util::PathExists(user_data_dir)) { + arguments_w += std::wstring(L"--") + switches::kUserDataDir + + L"=\"" + user_data_dir + L"\" "; + } + } + + // Use '--app=url' instead of just 'url' to launch the browser with minimal + // chrome. + // Note: Do not change this flag! Old Gears shortcuts will break if you do! + std::wstring url_w = UTF8ToWide(url); + arguments_w += std::wstring(L"--") + switches::kApp + L'=' + url_w; + + *arguments = WideToUTF8(arguments_w); + + return CPERR_SUCCESS; +} + +// +// Host functions shared by browser and plugin processes +// + +void* STDCALL CPB_Alloc(uint32 size) { + return malloc(size); +} + +void STDCALL CPB_Free(void* memory) { + free(memory); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_plugin_util.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,85 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_CHROME_PLUGIN_UTIL_H_ +#define CHROME_COMMON_CHROME_PLUGIN_UTIL_H_ + +#include + +#include "base/basictypes.h" +#include "base/non_thread_safe.h" +#include "base/ref_counted.h" +#include "chrome/common/chrome_plugin_api.h" +#include "chrome/common/notification_observer.h" + +class ChromePluginLib; +class MessageLoop; +namespace net { +class HttpResponseHeaders; +} + +// A helper struct to ensure the CPRequest data is cleaned up when done. +// This class is reused for requests made by the browser (and intercepted by the +// plugin) as well as those made by the plugin. +struct ScopableCPRequest : public CPRequest { + template + static T GetData(CPRequest* request) { + return static_cast(static_cast(request)->data); + } + + ScopableCPRequest(const char* url, const char* method, + CPBrowsingContext context); + ~ScopableCPRequest(); + + void* data; +}; + +// This is a base class for plugin-related objects that need to go away when +// the plugin unloads. This object also verifies that it is created and +// destroyed on the same thread. +class PluginHelper : public NotificationObserver, public NonThreadSafe { + public: + static void DestroyAllHelpersForPlugin(ChromePluginLib* plugin); + + PluginHelper(ChromePluginLib* plugin); + virtual ~PluginHelper(); + + // NotificationObserver + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + protected: + scoped_refptr plugin_; + + DISALLOW_COPY_AND_ASSIGN(PluginHelper); +}; + +// A class of utility functions for dealing with request responses. +class PluginResponseUtils { +public: + // Helper to convert request load flags from the plugin API to the net API + // versions. + static uint32 CPLoadFlagsToNetFlags(uint32 flags); + + // Common implementation of a CPR_GetResponseInfo call. + static int GetResponseInfo( + const net::HttpResponseHeaders* response_headers, + CPResponseInfoType type, void* buf, size_t buf_size); +}; + +// Helper to allocate a string using the given CPB_Alloc function. +inline char* CPB_StringDup(CPB_AllocFunc alloc, const std::string& str) { + char* cstr = static_cast(alloc(static_cast(str.length() + 1))); + memcpy(cstr, str.c_str(), str.length() + 1); // Include null terminator. + return cstr; +} + +CPError CPB_GetCommandLineArgumentsCommon(const char* url, + std::string* arguments); + +void* STDCALL CPB_Alloc(uint32 size); +void STDCALL CPB_Free(void* memory); + +#endif // CHROME_COMMON_CHROME_PLUGIN_UTIL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_switches.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_switches.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_switches.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/chrome_switches.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,439 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/chrome_switches.h" + +#include "base/base_switches.h" + +namespace switches { + +// Can't find the switch you are looking for? try looking in +// base/base_switches.cc instead. + +// Suppresses hang monitor dialogs in renderer processes. +const wchar_t kDisableHangMonitor[] = L"disable-hang-monitor"; + +// Completely disables UMA metrics system. +const wchar_t kDisableMetrics[] = L"disable-metrics"; + +// Enables the recording of metrics reports but disables reporting. +// In contrast to kDisableMetrics, this executes all the code that a normal +// client would use for reporting, except the report is dropped rather than sent +// to the server. This is useful for finding issues in the metrics code during +// UI and performance tests. +const wchar_t kMetricsRecordingOnly[] = L"metrics-recording-only"; + +// Causes the browser process to throw an assertion on startup. +const wchar_t kBrowserAssertTest[] = L"assert-test"; + +// Causes the renderer process to throw an assertion on launch. +const wchar_t kRendererAssertTest[] = L"renderer-assert-test"; + +// Causes the browser process to crash on startup. +const wchar_t kBrowserCrashTest[] = L"crash-test"; + +// Causes the renderer process to crash on launch. +const wchar_t kRendererCrashTest[] = L"renderer-crash-test"; + +// Causes the renderer process to display a dialog on launch. +const wchar_t kRendererStartupDialog[] = L"renderer-startup-dialog"; + +// Causes the plugin process to display a dialog on launch. +const wchar_t kPluginStartupDialog[] = L"plugin-startup-dialog"; + +// Specifies a command that should be used to launch the plugin process. Useful +// for running the plugin process through purify or quantify. Ex: +// --plugin-launcher="path\to\purify /Run=yes" +const wchar_t kPluginLauncher[] = L"plugin-launcher"; + +// The value of this switch tells the child process which +// IPC channel the browser expects to use to communicate with it. +const wchar_t kProcessChannelID[] = L"channel"; + +// The value of this switch tells the app to listen for and broadcast +// testing-related messages on IPC channel with the given ID. +const wchar_t kTestingChannelID[] = L"testing-channel"; + +// The value of this switch specifies which page will be displayed +// in newly-opened tabs. We need this for testing purposes so +// that the UI tests don't depend on what comes up for http://google.com. +const wchar_t kHomePage[] = L"homepage"; + +// Causes the process to run as renderer instead of as browser. +const wchar_t kRendererProcess[] = L"renderer"; + +// Path to the exe to run for the renderer and plugin subprocesses. +const wchar_t kBrowserSubprocessPath[] = L"browser-subprocess-path"; + +// Causes the process to run as a plugin subprocess. +const wchar_t kPluginProcess[] = L"plugin"; + +// Causes the process to run as a worker subprocess. +const wchar_t kWorkerProcess[] = L"worker"; + +// Runs the renderer and plugins in the same process as the browser +const wchar_t kSingleProcess[] = L"single-process"; + +// Runs each set of script-connected tabs (i.e., a BrowsingInstance) in its own +// renderer process. We default to using a renderer process for each +// site instance (i.e., group of pages from the same registered domain with +// script connections to each other). +const wchar_t kProcessPerTab[] = L"process-per-tab"; + +// Runs a single process for each site (i.e., group of pages from the same +// registered domain) the user visits. We default to using a renderer process +// for each site instance (i.e., group of pages from the same registered +// domain with script connections to each other). +const wchar_t kProcessPerSite[] = L"process-per-site"; + +// Runs plugins inside the renderer process +const wchar_t kInProcessPlugins[] = L"in-process-plugins"; + +// Runs the renderer outside the sandbox. +const wchar_t kNoSandbox[] = L"no-sandbox"; + +// Runs the plugin processes inside the sandbox. +const wchar_t kSafePlugins[] = L"safe-plugins"; + +// Excludes these plugins from the plugin sandbox. +// This is a comma-separated list of plugin library names and activex clsid. +const wchar_t kTrustedPlugins[] = L"trusted-plugins"; + +// Runs the security test for the sandbox. +const wchar_t kTestSandbox[] = L"test-sandbox"; + +// Specifies the user data directory, which is where the browser will look +// for all of its state. +const wchar_t kUserDataDir[] = L"user-data-dir"; + +// Specifies the plugin data directory, which is where plugins (Gears +// specifically) will store its state. +const wchar_t kPluginDataDir[] = L"plugin-data-dir"; + +// Use a specific disk cache location, rather than one derived from the +// UserDatadir. +const wchar_t kDiskCacheDir[] = L"disk-cache-dir"; + +// Whether the multiple profiles feature based on the user-data-dir flag is +// enabled or not. +const wchar_t kEnableUserDataDirProfiles[] = L"enable-udd-profiles"; + +// Specifies the path to the user data folder for the parent profile. +const wchar_t kParentProfile[] = L"parent-profile"; + +// Specifies that the associated value should be launched in "application" mode. +const wchar_t kApp[] = L"app"; + +// Specifies if the dom_automation_controller_ needs to be bound in the +// renderer. This binding happens on per-frame basis and hence can potentially +// be a performance bottleneck. One should only enable it when automating +// dom based tests. +const wchar_t kDomAutomationController[] = L"dom-automation"; + +// Tells the plugin process the path of the plugin to load +const wchar_t kPluginPath[] = L"plugin-path"; + +// A string used to override the default user agent with a custom one. +const wchar_t kUserAgent[] = L"user-agent"; + +// Specifies the flags passed to JS engine +const wchar_t kJavaScriptFlags[] = L"js-flags"; + +// The Country we should use. This is normally obtained from the operating +// system during first run and cached in the preferences afterwards. This is a +// string value, the 2 letter code from ISO 3166-1. +const wchar_t kCountry[] = L"country"; + +// The language file that we want to try to open. Of the form +// language[-country] where language is the 2 letter code from ISO-639. +const wchar_t kLang[] = L"lang"; + +// Will add kDebugOnStart to every child processes. If a value is passed, it +// will be used as a filter to determine if the child process should have the +// kDebugOnStart flag passed on or not. +const wchar_t kDebugChildren[] = L"debug-children"; + +// Will add kWaitForDebugger to every child processes. If a value is passed, it +// will be used as a filter to determine if the child process should have the +// kWaitForDebugger flag passed on or not. +const wchar_t kWaitForDebuggerChildren[] = L"wait-for-debugger-children"; + +// Will filter log messages to show only the messages that are prefixed +// with the specified value +const wchar_t kLogFilterPrefix[] = L"log-filter-prefix"; + +// Force logging to be enabled. Logging is disabled by default in release +// builds. +const wchar_t kEnableLogging[] = L"enable-logging"; + +// Force logging to be disabled. Logging is enabled by default in debug +// builds. +const wchar_t kDisableLogging[] = L"disable-logging"; + +// Sets the minimum log level. Valid values are from 0 to 3: +// INFO = 0, WARNING = 1, LOG_ERROR = 2, LOG_FATAL = 3. +const wchar_t kLoggingLevel[] = L"log-level"; + +// Make plugin processes log their sent and received messages to LOG(INFO). +const wchar_t kLogPluginMessages[] = L"log-plugin-messages"; + +// Dump any accumualted histograms to the log when browser terminates (requires +// logging to be enabled to really do anything). Used by developers and test +// scripts. +const wchar_t kDumpHistogramsOnExit[] = L"dump-histograms-on-exit"; + +// enable remote debug / automation shell on the specified port +const wchar_t kRemoteShellPort[] = L"remote-shell-port"; + +// Runs un-installation steps that were done by chrome first-run. +const wchar_t kUninstall[] = L"uninstall"; + +// Number of entries to show in the omnibox popup. +const wchar_t kOmniBoxPopupCount[] = L"omnibox-popup-count"; + +// The value of this switch tells the app to listen for and broadcast +// automation-related messages on IPC channel with the given ID. +const wchar_t kAutomationClientChannelID[] = L"automation-channel"; + +// Indicates the last session should be restored on startup. This overrides +// the preferences value and is primarily intended for testing. The value of +// this switch is the number of tabs to wait until loaded before +// 'load completed' is sent to the ui_test. +const wchar_t kRestoreLastSession[] = L"restore-last-session"; + +// Chrome supports a playback and record mode. Record mode saves *everything* +// to the cache. Playback mode reads data exclusively from the cache. This +// allows us to record a session into the cache and then replay it at will. +const wchar_t kRecordMode[] = L"record-mode"; +const wchar_t kPlaybackMode[] = L"playback-mode"; + +// Don't record/playback events when using record & playback. +const wchar_t kNoEvents[] = L"no-events"; + +// Support a separate switch that enables the v8 playback extension. +// The extension causes javascript calls to Date.now() and Math.random() +// to return consistent values, such that subsequent loads of the same +// page will result in consistent js-generated data and XHR requests. +// Pages may still be able to generate inconsistent data from plugins. +const wchar_t kNoJsRandomness[] = L"no-js-randomness"; + +// Make Windows happy by allowing it to show "Enable access to this program" +// checkbox in Add/Remove Programs->Set Program Access and Defaults. This +// only shows an error box because the only way to hide Chrome is by +// uninstalling it. +const wchar_t kHideIcons[] = L"hide-icons"; + +const wchar_t kShowIcons[] = L"show-icons"; + +// Make Chrome default browser +const wchar_t kMakeDefaultBrowser[] = L"make-default-browser"; + +// Use a specified proxy server, overrides system settings. This switch only +// affects HTTP and HTTPS requests. +const wchar_t kProxyServer[] = L"proxy-server"; + +// Use WinHTTP to fetch and evaluate PAC scripts. Otherwise the default is +// to use Chromium's network stack to fetch, and V8 to evaluate. +const wchar_t kWinHttpProxyResolver[] = L"winhttp-proxy-resolver"; + +// Chrome will support prefetching of DNS information. Until this becomes +// the default, we'll provide a command line switch. +extern const wchar_t kDnsLogDetails[] = L"dns-log-details"; +extern const wchar_t kDnsPrefetchDisable[] = L"dns-prefetch-disable"; + +// Enables support to debug printing subsystem. +const wchar_t kDebugPrint[] = L"debug-print"; + +// Allow initialization of all activex controls. This is only to help website +// developers test their controls to see if they are compatible in Chrome. +// Note there's a duplicate value in activex_shared.cc (to avoid +// dependency on chrome module). Please change both locations at the same time. +const wchar_t kAllowAllActiveX[] = L"allow-all-activex"; + +// Browser flag to disable the web inspector for all renderers. +const wchar_t kDisableDevTools[] = L"disable-dev-tools"; + +// Enable web inspector for all windows, even if they're part of the browser. +// Allows us to use our dev tools to debug browser windows itself. +const wchar_t kAlwaysEnableDevTools[] = L"always-enable-dev-tools"; + +// Used to set the value of SessionRestore::num_tabs_to_load_. See +// session_restore.h for details. +const wchar_t kTabCountToLoadOnSessionRestore[] = + L"tab-count-to-load-on-session-restore"; + +// Enable dynamic loading of the Memory Profiler DLL, which will trace +// all memory allocations during the run. +const wchar_t kMemoryProfiling[] = L"memory-profile"; + +// Configure Chrome's memory model. +// Does chrome really need multiple memory models? No. But we get a lot +// of concerns from individuals about how the changes work on *their* +// system, and we need to be able to experiment with a few choices. +const wchar_t kMemoryModel[] = L"memory-model"; + +// By default, cookies are not allowed on file://. They are needed in for +// testing, for example page cycler and layout tests. See bug 1157243. +const wchar_t kEnableFileCookies[] = L"enable-file-cookies"; + +// Start the browser maximized, regardless of any previous settings. +const wchar_t kStartMaximized[] = L"start-maximized"; + +// Spawn threads to watch for excessive delays in specified message loops. +// User should set breakpoints on Alarm() to examine problematic thread. +// Usage: -enable-watchdog=[ui][io] +// Order of the listed sub-arguments does not matter. +const wchar_t kEnableWatchdog[] = L"enable-watchdog"; + +// Display the First Run experience when the browser is started, regardless of +// whether or not it's actually the first run. +const wchar_t kFirstRun[] = L"first-run"; + +// Bypass the First Run experience when the browser is started, regardless of +// whether or not it's actually the first run. Overrides kFirstRun in case +// you're for some reason tempted to pass them both. +const wchar_t kNoFirstRun[] = L"no-first-run"; + +// Enable histograming of tasks served by MessageLoop. See about:histograms/Loop +// for results, which show frequency of messages on each thread, including APC +// count, object signalling count, etc. +const wchar_t kMessageLoopHistogrammer[] = L"message-loop-histogrammer"; + +// Perform importing from another browser. The value associated with this +// setting encodes the target browser and what items to import. +const wchar_t kImport[] = L"import"; + +// Change the DCHECKS to dump memory and continue instead of displaying error +// dialog. This is valid only in Release mode when --enable-dcheck is +// specified. +const wchar_t kSilentDumpOnDCHECK[] = L"silent-dump-on-dcheck"; + +// Normally when the user attempts to navigate to a page that was the result of +// a post we prompt to make sure they want to. This switch may be used to +// disable that check. This switch is used during automated testing. +const wchar_t kDisablePromptOnRepost[] = L"disable-prompt-on-repost"; + +// Disable pop-up blocking. +const wchar_t kDisablePopupBlocking[] = L"disable-popup-blocking"; + +// Don't execute JavaScript (browser JS like the new tab page still runs). +const wchar_t kDisableJavaScript[] = L"disable-javascript"; + +// Don't enforce the same-origin policy. (Used by people testing their sites.) +const wchar_t kDisableWebSecurity[] = L"disable-web-security"; + +// Prevent Java from running. +const wchar_t kDisableJava[] = L"disable-java"; + +// Prevent plugins from running. +const wchar_t kDisablePlugins[] = L"disable-plugins"; + +// Prevent images from loading. +const wchar_t kDisableImages[] = L"disable-images"; + +// Use the low fragmentation heap for the CRT. +const wchar_t kUseLowFragHeapCrt[] = L"use-lf-heap"; + +#ifndef NDEBUG +// Debug only switch to specify which gears plugin dll to load. +const wchar_t kGearsPluginPathOverride[] = L"gears-plugin-path"; +#endif + +// Enable the fastback page cache. +const wchar_t kEnableFastback[] = L"enable-fastback"; + +// Allow loading of the javascript debugger UI from the filesystem. +const wchar_t kJavaScriptDebuggerPath[] = L"javascript-debugger-path"; + +const wchar_t kDisableP13n[] = L"disable-p13n"; + +// Enable support for SDCH filtering (dictionary based expansion of content). +// Optional argument is *the* only domain name that will have SDCH suppport. +// Default is "-enable-sdch" to advertise SDCH on all domains. +// Sample usage with argument: "-enable-sdch=.google.com" +// SDCH is currently only supported server-side for searches on google.com. +const wchar_t kSdchFilter[] = L"enable-sdch"; + +// Enable user script support. +const wchar_t kEnableUserScripts[] = L"enable-user-scripts"; + +// Enable extensions. +const wchar_t kEnableExtensions[] = L"enable-extensions"; + +// Install the extension specified in the argument. This is for MIME type +// handling so that users can double-click on an extension. +const wchar_t kInstallExtension[] = L"install-extension"; + +// Load an extension from the specified directory. +const wchar_t kLoadExtension[] = L"load-extension"; + +// Load an NPAPI plugin from the specified path. +const wchar_t kLoadPlugin[] = L"load-plugin"; + +// directory to locate user scripts in as an over-ride of the default +const wchar_t kUserScriptsDir[] = L"user-scripts-dir"; + +// Causes the browser to launch directly in incognito mode. +const wchar_t kIncognito[] = L"incognito"; + +// Turns on the accessibility in the renderer. Off by default until +// http://b/issue?id=1432077 is fixed. +const wchar_t kEnableRendererAccessibility[] = L"enable-renderer-accessibility"; + +// Pass the name of the current running automated test to Chrome. +const wchar_t kTestName[] = L"test-name"; + +// On POSIX only: the contents of this flag are prepended to the renderer +// command line. (Useful values might be "valgrind" or "gdb --args") +const wchar_t kRendererCmdPrefix[] = L"renderer-cmd-prefix"; + +// Temparary option for new ftp implemetation. +const wchar_t kNewFtp[] = L"new-ftp"; + +// On POSIX only: use FIFO for IPC channels so that "unrelated" process +// can connect to a channel, provided it knows its name. For debugging purposes. +const wchar_t kIPCUseFIFO[] = L"ipc-use-fifo"; + +// If this flag is set open out of process developer tools window instead of +// Console Debugger when user clicks "Debug JavaScript". +const wchar_t kEnableOutOfProcessDevTools[] = L"enable-oop-devtools"; + +// Enable HTML5 Worker support +const wchar_t kEnableWebWorkers[] = L"enable-web-workers"; + +// Causes the worker process allocation to use as many processes as cores. +const wchar_t kWebWorkerProcessPerCore[] = L"web-worker-process-per-core"; + +// Causes workers to run together in one process, depending on their domains. +// Note this is duplicated in webworkerclient_impl.cc +const wchar_t kWebWorkerShareProcesses[] = L"web-worker-share-processes"; + +// Enables experimental views under gtk. +const wchar_t kViewsGtk[] = L"views-gtk"; + +// Enables the bookmark menu. +const wchar_t kBookmarkMenu[] = L"bookmark-menu"; + +// Enables StatsTable, logging statistics to a global named shared memory table. +const wchar_t kEnableStatsTable[] = L"enable-stats-table"; + +// Enables the Omnibox2 popup and functionality. +const wchar_t kEnableOmnibox2[] = L"enable-omnibox2"; + +// Replaces the audio IPC layer for
    (); + } + const Header* header() const { + return headerT
    (); + } + +#if !defined(CHROMIUM_MOZILLA_BUILD) + void InitLoggingVariables(); +#else + void InitLoggingVariables(const char* const name="???"); +#endif + +#if defined(OS_POSIX) + // The set of file descriptors associated with this message. + scoped_refptr file_descriptor_set_; + + // Ensure that a FileDescriptorSet is allocated + void EnsureFileDescriptorSet(); + + FileDescriptorSet* file_descriptor_set() { + EnsureFileDescriptorSet(); + return file_descriptor_set_.get(); + } + const FileDescriptorSet* file_descriptor_set() const { + return file_descriptor_set_.get(); + } +#endif + +#if defined(CHROMIUM_MOZILLA_BUILD) + const char* name_; +#endif + +#ifdef IPC_MESSAGE_LOG_ENABLED + // Used for logging. + mutable int64 received_time_; + mutable std::wstring output_params_; + mutable LogData* log_data_; + mutable bool dont_log_; +#endif +}; + +//------------------------------------------------------------------------------ + +} // namespace IPC + +enum SpecialRoutingIDs { + // indicates that we don't have a routing ID yet. + MSG_ROUTING_NONE = kint32min, + + // indicates a general message not sent to a particular tab. + MSG_ROUTING_CONTROL = kint32max, +}; + +#define IPC_REPLY_ID 0xFFF0 // Special message id for replies +#define IPC_LOGGING_ID 0xFFF1 // Special message id for logging + +#endif // CHROME_COMMON_IPC_MESSAGE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_message_macros.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_message_macros.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_message_macros.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_message_macros.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,1156 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This header is meant to be included in multiple passes, hence no traditional +// header guard. +// +// In the first pass, IPC_MESSAGE_MACROS_ENUMS should be defined, which will +// create enums for each of the messages defined with the IPC_MESSAGE_* macros. +// +// In the second pass, either IPC_MESSAGE_MACROS_DEBUGSTRINGS or +// IPC_MESSAGE_MACROS_CLASSES should be defined (if both, DEBUGSTRINGS takes +// precedence). Only one .cc file should have DEBUGSTRINGS defined, as this +// will create helper functions mapping message types to strings. Having +// CLASSES defined will create classes for each of the messages defined with +// the IPC_MESSAGE_* macros. +// +// "Sync" messages are just synchronous calls, the Send() call doesn't return +// until a reply comes back. Input parameters are first (const TYPE&), and +// To declare a sync message, use the IPC_SYNC_ macros. The numbers at the +// end show how many input/output parameters there are (i.e. 1_2 is 1 in, 2 +// out). The caller does a Send([route id, ], in1, &out1, &out2). +// The receiver's handler function will be +// void OnSyncMessageName(const type1& in1, type2* out1, type3* out2) +// +// +// A caller can also send a synchronous message, while the receiver can respond +// at a later time. This is transparent from the sender's size. The receiver +// needs to use a different handler that takes in a IPC::Message* as the output +// type, stash the message, and when it has the data it can Send the message. +// +// Use the IPC_MESSAGE_HANDLER_DELAY_REPLY macro instead of IPC_MESSAGE_HANDLER +// IPC_MESSAGE_HANDLER_DELAY_REPLY(ViewHostMsg_SyncMessageName, +// OnSyncMessageName) +// +// The handler function will look like: +// void OnSyncMessageName(const type1& in1, IPC::Message* reply_msg); +// +// Receiver stashes the IPC::Message* pointer, and when it's ready, it does: +// ViewHostMsg_SyncMessageName::WriteReplyParams(reply_msg, out1, out2); +// Send(reply_msg); + +#ifndef CHROMIUM_MOZILLA_BUILD +#include "IPC/IPCMessageUtils.h" +#else +#include "chrome/common/ipc_message_utils.h" +#endif + + +#ifndef MESSAGES_INTERNAL_FILE +#error This file should only be included by X_messages.h, which needs to define MESSAGES_INTERNAL_FILE first. +#endif + +// Trick scons and xcode into seeing the possible real dependencies since they +// don't understand #include MESSAGES_INTERNAL_FILE. See http://crbug.com/7828 +#if 0 +#include "chrome/common/ipc_sync_message_unittest.h" +#include "chrome/common/plugin_messages_internal.h" +#include "chrome/common/render_messages_internal.h" +#include "chrome/common/devtools_messages_internal.h" +#include "chrome/test/automation/automation_messages_internal.h" +#include "chrome/common/worker_messages_internal.h" +#endif + +#ifndef IPC_MESSAGE_MACROS_INCLUDE_BLOCK +#define IPC_MESSAGE_MACROS_INCLUDE_BLOCK + +// Multi-pass include of X_messages_internal.h. Preprocessor magic allows +// us to use 1 header to define the enums and classes for our render messages. +#define IPC_MESSAGE_MACROS_ENUMS +#include MESSAGES_INTERNAL_FILE + +#define IPC_MESSAGE_MACROS_CLASSES +#include MESSAGES_INTERNAL_FILE + +#ifdef IPC_MESSAGE_MACROS_LOG_ENABLED +#define IPC_MESSAGE_MACROS_LOG +#include MESSAGES_INTERNAL_FILE +#endif + +#undef MESSAGES_INTERNAL_FILE +#undef IPC_MESSAGE_MACROS_INCLUDE_BLOCK + +#endif + + +// Undefine the macros from the previous pass (if any). +#undef IPC_BEGIN_MESSAGES +#undef IPC_END_MESSAGES +#undef IPC_MESSAGE_CONTROL0 +#undef IPC_MESSAGE_CONTROL1 +#undef IPC_MESSAGE_CONTROL2 +#undef IPC_MESSAGE_CONTROL3 +#undef IPC_MESSAGE_CONTROL4 +#undef IPC_MESSAGE_CONTROL5 +#undef IPC_MESSAGE_ROUTED0 +#undef IPC_MESSAGE_ROUTED1 +#undef IPC_MESSAGE_ROUTED2 +#undef IPC_MESSAGE_ROUTED3 +#undef IPC_MESSAGE_ROUTED4 +#undef IPC_MESSAGE_ROUTED5 +#undef IPC_MESSAGE_ROUTED6 +#undef IPC_SYNC_MESSAGE_CONTROL0_0 +#undef IPC_SYNC_MESSAGE_CONTROL0_1 +#undef IPC_SYNC_MESSAGE_CONTROL0_2 +#undef IPC_SYNC_MESSAGE_CONTROL0_3 +#undef IPC_SYNC_MESSAGE_CONTROL1_0 +#undef IPC_SYNC_MESSAGE_CONTROL1_1 +#undef IPC_SYNC_MESSAGE_CONTROL1_2 +#undef IPC_SYNC_MESSAGE_CONTROL1_3 +#undef IPC_SYNC_MESSAGE_CONTROL2_0 +#undef IPC_SYNC_MESSAGE_CONTROL2_1 +#undef IPC_SYNC_MESSAGE_CONTROL2_2 +#undef IPC_SYNC_MESSAGE_CONTROL2_3 +#undef IPC_SYNC_MESSAGE_CONTROL3_1 +#undef IPC_SYNC_MESSAGE_CONTROL3_2 +#undef IPC_SYNC_MESSAGE_CONTROL3_3 +#undef IPC_SYNC_MESSAGE_CONTROL4_1 +#undef IPC_SYNC_MESSAGE_CONTROL4_2 +#undef IPC_SYNC_MESSAGE_ROUTED0_0 +#undef IPC_SYNC_MESSAGE_ROUTED0_1 +#undef IPC_SYNC_MESSAGE_ROUTED0_2 +#undef IPC_SYNC_MESSAGE_ROUTED0_3 +#undef IPC_SYNC_MESSAGE_ROUTED1_0 +#undef IPC_SYNC_MESSAGE_ROUTED1_1 +#undef IPC_SYNC_MESSAGE_ROUTED1_2 +#undef IPC_SYNC_MESSAGE_ROUTED1_3 +#undef IPC_SYNC_MESSAGE_ROUTED1_4 +#undef IPC_SYNC_MESSAGE_ROUTED2_0 +#undef IPC_SYNC_MESSAGE_ROUTED2_1 +#undef IPC_SYNC_MESSAGE_ROUTED2_2 +#undef IPC_SYNC_MESSAGE_ROUTED2_3 +#undef IPC_SYNC_MESSAGE_ROUTED3_0 +#undef IPC_SYNC_MESSAGE_ROUTED3_1 +#undef IPC_SYNC_MESSAGE_ROUTED3_2 +#undef IPC_SYNC_MESSAGE_ROUTED3_3 +#undef IPC_SYNC_MESSAGE_ROUTED4_0 +#undef IPC_SYNC_MESSAGE_ROUTED4_1 +#undef IPC_SYNC_MESSAGE_ROUTED4_2 + +#if defined(IPC_MESSAGE_MACROS_ENUMS) +#undef IPC_MESSAGE_MACROS_ENUMS + +// TODO(jabdelmalek): we're using the lowest 12 bits of type for the message +// id, and the highest 4 bits for the channel type. This constrains us to +// 16 channel types (currently using 8) and 4K messages per type. Should +// really make type be 32 bits, but then we break automation with older Chrome +// builds.. +// Do label##PreStart so that automation messages keep the same id as before. +#define IPC_BEGIN_MESSAGES(label) \ + enum label##MsgType { \ + label##Start = label##MsgStart << 12, \ + label##PreStart = (label##MsgStart << 12) - 1, + +#define IPC_END_MESSAGES(label) \ + label##End \ + }; + +#define IPC_MESSAGE_CONTROL0(msg_class) \ + msg_class##__ID, + +#define IPC_MESSAGE_CONTROL1(msg_class, type1) \ + msg_class##__ID, + +#define IPC_MESSAGE_CONTROL2(msg_class, type1, type2) \ + msg_class##__ID, + +#define IPC_MESSAGE_CONTROL3(msg_class, type1, type2, type3) \ + msg_class##__ID, + +#define IPC_MESSAGE_CONTROL4(msg_class, type1, type2, type3, type4) \ + msg_class##__ID, + +#define IPC_MESSAGE_CONTROL5(msg_class, type1, type2, type3, type4, type5) \ + msg_class##__ID, + +#define IPC_MESSAGE_ROUTED0(msg_class) \ + msg_class##__ID, + +#define IPC_MESSAGE_ROUTED1(msg_class, type1) \ + msg_class##__ID, + +#define IPC_MESSAGE_ROUTED2(msg_class, type1, type2) \ + msg_class##__ID, + +#define IPC_MESSAGE_ROUTED3(msg_class, type1, type2, type3) \ + msg_class##__ID, + +#define IPC_MESSAGE_ROUTED4(msg_class, type1, type2, type3, type4) \ + msg_class##__ID, + +#define IPC_MESSAGE_ROUTED5(msg_class, type1, type2, type3, type4, type5) \ + msg_class##__ID, + +#define IPC_MESSAGE_ROUTED6(msg_class, type1, type2, type3, type4, type5, type6) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL0_0(msg_class) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL0_1(msg_class, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL0_2(msg_class, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL0_3(msg_class, type1_out, type2_out, type3_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL1_0(msg_class, type1_in) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL1_1(msg_class, type1_in, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL1_2(msg_class, type1_in, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL2_0(msg_class, type1_in, type2_in) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL2_1(msg_class, type1_in, type2_in, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_CONTROL4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED0_0(msg_class) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED0_1(msg_class, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED0_2(msg_class, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED0_3(msg_class, type1_out, type2_out, type3_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED1_0(msg_class, type1_in) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED1_1(msg_class, type1_in, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED1_2(msg_class, type1_in, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED1_4(msg_class, type1_in, type1_out, type2_out, type3_out, type4_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED2_0(msg_class, type1_in, type2_in) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED2_1(msg_class, type1_in, type2_in, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED3_0(msg_class, type1_in, type2_in, type3_in) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \ + msg_class##__ID, + +#define IPC_SYNC_MESSAGE_ROUTED4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \ + msg_class##__ID, + +// Message crackers and handlers. +// Prefer to use the IPC_BEGIN_MESSAGE_MAP_EX to the older macros since they +// allow you to detect when a message could not be de-serialized. Usage: +// +// void MyClass::OnMessageReceived(const IPC::Message& msg) { +// bool msg_is_good = false; +// IPC_BEGIN_MESSAGE_MAP_EX(MyClass, msg, msg_is_good) +// IPC_MESSAGE_HANDLER(MsgClassOne, OnMsgClassOne) +// ...more handlers here ... +// IPC_MESSAGE_HANDLER(MsgClassTen, OnMsgClassTen) +// IPC_END_MESSAGE_MAP_EX() +// if (!msg_is_good) { +// // Signal error here or terminate offending process. +// } +// } + +#define IPC_DEFINE_MESSAGE_MAP(class_name) \ +void class_name::OnMessageReceived(const IPC::Message& msg) \ + IPC_BEGIN_MESSAGE_MAP(class_name, msg) + +#define IPC_BEGIN_MESSAGE_MAP_EX(class_name, msg, msg_is_ok) \ + { \ + typedef class_name _IpcMessageHandlerClass; \ + const IPC::Message& ipc_message__ = msg; \ + bool& msg_is_ok__ = msg_is_ok; \ + switch (ipc_message__.type()) { \ + +#define IPC_BEGIN_MESSAGE_MAP(class_name, msg) \ + { \ + typedef class_name _IpcMessageHandlerClass; \ + const IPC::Message& ipc_message__ = msg; \ + bool msg_is_ok__ = true; \ + switch (ipc_message__.type()) { \ + +#define IPC_MESSAGE_FORWARD(msg_class, obj, member_func) \ + case msg_class::ID: \ + msg_is_ok__ = msg_class::Dispatch(&ipc_message__, obj, &member_func); \ + break; + +#define IPC_MESSAGE_HANDLER(msg_class, member_func) \ + IPC_MESSAGE_FORWARD(msg_class, this, _IpcMessageHandlerClass::member_func) + +#define IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, obj, member_func) \ + case msg_class::ID: \ + msg_class::DispatchDelayReply(&ipc_message__, obj, &member_func); \ + break; + +#define IPC_MESSAGE_HANDLER_DELAY_REPLY(msg_class, member_func) \ + IPC_MESSAGE_FORWARD_DELAY_REPLY(msg_class, this, _IpcMessageHandlerClass::member_func) + +#define IPC_MESSAGE_HANDLER_GENERIC(msg_class, code) \ + case msg_class::ID: \ + code; \ + break; + +#define IPC_REPLY_HANDLER(func) \ + case IPC_REPLY_ID: \ + func(ipc_message__); \ + break; + + +#define IPC_MESSAGE_UNHANDLED(code) \ + default: \ + code; \ + break; + +#define IPC_MESSAGE_UNHANDLED_ERROR() \ + IPC_MESSAGE_UNHANDLED(NOTREACHED() << \ + "Invalid message with type = " << \ + ipc_message__.type()) + +#define IPC_END_MESSAGE_MAP() \ + DCHECK(msg_is_ok__); \ + } \ +} + +#define IPC_END_MESSAGE_MAP_EX() \ + } \ +} + +#elif defined(IPC_MESSAGE_MACROS_LOG) +#undef IPC_MESSAGE_MACROS_LOG + +#ifndef IPC_LOG_TABLE_CREATED +#define IPC_LOG_TABLE_CREATED +typedef void (*LogFunction)(uint16 type, + std::wstring* name, + const IPC::Message* msg, + std::wstring* params); + +LogFunction g_log_function_mapping[LastMsgIndex]; +#endif + + +#define IPC_BEGIN_MESSAGES(label) \ + void label##MsgLog(uint16 type, std::wstring* name, const IPC::Message* msg, std::wstring* params) { \ + switch (type) { + +#define IPC_END_MESSAGES(label) \ + default: \ + if (name) \ + *name = L"[UNKNOWN " L ## #label L" MSG"; \ + } \ + } \ + class LoggerRegisterHelper##label { \ + public: \ + LoggerRegisterHelper##label() { \ + g_log_function_mapping[label##MsgStart] = label##MsgLog; \ + } \ + }; \ + LoggerRegisterHelper##label g_LoggerRegisterHelper##label; + +#define IPC_MESSAGE_LOG(msg_class) \ + case msg_class##__ID: \ + if (name) \ + *name = L ## #msg_class; \ + if (msg && params) \ + msg_class::Log(msg, params); \ + break; + +#define IPC_MESSAGE_CONTROL0(msg_class) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_CONTROL1(msg_class, type1) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_CONTROL2(msg_class, type1, type2) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_CONTROL3(msg_class, type1, type2, type3) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_CONTROL4(msg_class, type1, type2, type3, type4) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_CONTROL5(msg_class, type1, type2, type3, type4, type5) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_ROUTED0(msg_class) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_ROUTED1(msg_class, type1) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_ROUTED2(msg_class, type1, type2) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_ROUTED3(msg_class, type1, type2, type3) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_ROUTED4(msg_class, type1, type2, type3, type4) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_ROUTED5(msg_class, type1, type2, type3, type4, type5) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_MESSAGE_ROUTED6(msg_class, type1, type2, type3, type4, type5, type6) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL0_0(msg_class) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL0_1(msg_class, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL0_2(msg_class, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL0_3(msg_class, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL1_0(msg_class, type1_in) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL1_1(msg_class, type1_in, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL1_2(msg_class, type1_in, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL2_0(msg_class, type1_in, type2_in) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL2_1(msg_class, type1_in, type2_in, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_CONTROL4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED0_0(msg_class) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED0_1(msg_class, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED0_2(msg_class, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED0_3(msg_class, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED1_0(msg_class, type1_in) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED1_1(msg_class, type1_in, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED1_2(msg_class, type1_in, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED1_4(msg_class, type1_in, type1_out, type2_out, type3_out, type4_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED2_0(msg_class, type1_in, type2_in) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED2_1(msg_class, type1_in, type2_in, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED3_0(msg_class, type1_in, type2_in, type3_in) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \ + IPC_MESSAGE_LOG(msg_class) + +#define IPC_SYNC_MESSAGE_ROUTED4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \ + IPC_MESSAGE_LOG(msg_class) + +#elif defined(IPC_MESSAGE_MACROS_CLASSES) +#undef IPC_MESSAGE_MACROS_CLASSES + +#define IPC_BEGIN_MESSAGES(label) +#define IPC_END_MESSAGES(label) + +#define IPC_MESSAGE_CONTROL0(msg_class) \ + class msg_class : public IPC::Message { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class() \ + : IPC::Message(MSG_ROUTING_CONTROL, \ + ID, \ + PRIORITY_NORMAL) {} \ + }; + +#define IPC_MESSAGE_CONTROL1(msg_class, type1) \ + class msg_class : public IPC::MessageWithTuple { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1& arg1) \ + : IPC::MessageWithTuple(MSG_ROUTING_CONTROL, \ + ID, \ + arg1) {} \ + }; + +#define IPC_MESSAGE_CONTROL2(msg_class, type1, type2) \ + class msg_class : public IPC::MessageWithTuple< Tuple2 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1& arg1, const type2& arg2) \ + : IPC::MessageWithTuple< Tuple2 >( \ + MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(arg1, arg2)) {} \ + }; + +#define IPC_MESSAGE_CONTROL3(msg_class, type1, type2, type3) \ + class msg_class : \ + public IPC::MessageWithTuple< Tuple3 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1& arg1, const type2& arg2, const type3& arg3) \ + : IPC::MessageWithTuple< Tuple3 >( \ + MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(arg1, arg2, arg3)) {} \ + }; + +#define IPC_MESSAGE_CONTROL4(msg_class, type1, type2, type3, type4) \ + class msg_class : \ + public IPC::MessageWithTuple< Tuple4 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1& arg1, const type2& arg2, const type3& arg3, \ + const type4& arg4) \ + : IPC::MessageWithTuple< Tuple4 >( \ + MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(arg1, arg2, arg3, arg4)) {} \ + }; + +#define IPC_MESSAGE_CONTROL5(msg_class, type1, type2, type3, type4, type5) \ + class msg_class : \ + public IPC::MessageWithTuple< Tuple5 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1& arg1, const type2& arg2, \ + const type3& arg3, const type4& arg4, const type5& arg5) \ + : IPC::MessageWithTuple< Tuple5 >( \ + MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(arg1, arg2, arg3, arg4, arg5)) {} \ + }; + +#define IPC_MESSAGE_ROUTED0(msg_class) \ + class msg_class : public IPC::Message { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int32 routing_id) \ + : IPC::Message(routing_id, ID, PRIORITY_NORMAL) {} \ + }; + +#define IPC_MESSAGE_ROUTED1(msg_class, type1) \ + class msg_class : public IPC::MessageWithTuple { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int32 routing_id, const type1& arg1) \ + : IPC::MessageWithTuple(routing_id, ID, arg1) {} \ + }; + +#define IPC_MESSAGE_ROUTED2(msg_class, type1, type2) \ + class msg_class : public IPC::MessageWithTuple< Tuple2 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int32 routing_id, const type1& arg1, const type2& arg2) \ + : IPC::MessageWithTuple< Tuple2 >( \ + routing_id, ID, MakeTuple(arg1, arg2)) {} \ + }; + +#define IPC_MESSAGE_ROUTED3(msg_class, type1, type2, type3) \ + class msg_class : \ + public IPC::MessageWithTuple< Tuple3 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int32 routing_id, const type1& arg1, const type2& arg2, \ + const type3& arg3) \ + : IPC::MessageWithTuple< Tuple3 >( \ + routing_id, ID, MakeTuple(arg1, arg2, arg3)) {} \ + }; + +#define IPC_MESSAGE_ROUTED4(msg_class, type1, type2, type3, type4) \ + class msg_class : \ + public IPC::MessageWithTuple< Tuple4 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int32 routing_id, const type1& arg1, const type2& arg2, \ + const type3& arg3, const type4& arg4) \ + : IPC::MessageWithTuple< Tuple4 >( \ + routing_id, ID, MakeTuple(arg1, arg2, arg3, arg4)) {} \ + }; + +#define IPC_MESSAGE_ROUTED5(msg_class, type1, type2, type3, type4, type5) \ + class msg_class : \ + public IPC::MessageWithTuple< Tuple5 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int32 routing_id, const type1& arg1, const type2& arg2, \ + const type3& arg3, const type4& arg4, const type5& arg5) \ + : IPC::MessageWithTuple< Tuple5 >( \ + routing_id, ID, MakeTuple(arg1, arg2, arg3, arg4, arg5)) {} \ + }; + +#define IPC_MESSAGE_ROUTED6(msg_class, type1, type2, type3, type4, type5, \ + type6) \ + class msg_class : \ + public IPC::MessageWithTuple< Tuple6 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int32 routing_id, const type1& arg1, const type2& arg2, \ + const type3& arg3, const type4& arg4, const type5& arg5, \ + const type6& arg6) \ + : IPC::MessageWithTuple< Tuple6 >( \ + routing_id, ID, MakeTuple(arg1, arg2, arg3, arg4, arg5, arg6)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL0_0(msg_class) \ + class msg_class : public IPC::MessageWithReply { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class() \ + : IPC::MessageWithReply( \ + MSG_ROUTING_CONTROL, ID, \ + MakeTuple(), MakeTuple()) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL0_1(msg_class, type1_out) \ + class msg_class : public IPC::MessageWithReply > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(type1_out* arg1) \ + : IPC::MessageWithReply >( \ + MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(), MakeRefTuple(*arg1)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL0_2(msg_class, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(type1_out* arg1, type2_out* arg2) \ + : IPC::MessageWithReply >( \ + MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(), MakeRefTuple(*arg1, *arg2)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL0_3(msg_class, type1_out, type2_out, type3_out) \ + class msg_class : \ + public IPC::MessageWithReply >{ \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(type1_out* arg1, type2_out* arg2, type3_out* arg3) \ + : IPC::MessageWithReply >(MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(), MakeRefTuple(*arg1, *arg2, *arg3)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL1_0(msg_class, type1_in) \ + class msg_class : \ + public IPC::MessageWithReply { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1) \ + : IPC::MessageWithReply( \ + MSG_ROUTING_CONTROL, ID, \ + arg1, MakeTuple()) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL1_1(msg_class, type1_in, type1_out) \ + class msg_class : \ + public IPC::MessageWithReply > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, type1_out* arg2) \ + : IPC::MessageWithReply >( \ + MSG_ROUTING_CONTROL, ID, \ + arg1, MakeRefTuple(*arg2)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL1_2(msg_class, type1_in, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, type1_out* arg2, type2_out* arg3) \ + : IPC::MessageWithReply >( \ + MSG_ROUTING_CONTROL, ID, \ + arg1, MakeRefTuple(*arg2, *arg3)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \ + class msg_class : \ + public IPC::MessageWithReply >{ \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, type1_out* arg2, type2_out* arg3, type3_out* arg4) \ + : IPC::MessageWithReply >(MSG_ROUTING_CONTROL, \ + ID, \ + arg1, MakeRefTuple(*arg2, *arg3, *arg4)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL2_0(msg_class, type1_in, type2_in) \ + class msg_class : \ + public IPC::MessageWithReply, Tuple0 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2) \ + : IPC::MessageWithReply, Tuple0 >( \ + MSG_ROUTING_CONTROL, ID, \ + MakeTuple(arg1, arg2), MakeTuple()) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL2_1(msg_class, type1_in, type2_in, type1_out) \ + class msg_class : \ + public IPC::MessageWithReply, Tuple1 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2, type1_out* arg3) \ + : IPC::MessageWithReply, Tuple1 >( \ + MSG_ROUTING_CONTROL, ID, \ + MakeTuple(arg1, arg2), MakeRefTuple(*arg3)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply, \ + Tuple2 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2, type1_out* arg3, type2_out* arg4) \ + : IPC::MessageWithReply, \ + Tuple2 >(MSG_ROUTING_CONTROL, ID, \ + MakeTuple(arg1, arg2), MakeRefTuple(*arg3, *arg4)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \ + class msg_class : \ + public IPC::MessageWithReply, \ + Tuple3 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2, type1_out* arg3, type2_out* arg4, type3_out* arg5) \ + : IPC::MessageWithReply, \ + Tuple3 >(MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(arg1, arg2), MakeRefTuple(*arg3, *arg4, *arg5)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \ + class msg_class : \ + public IPC::MessageWithReply, \ + Tuple1 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4) \ + : IPC::MessageWithReply, \ + Tuple1 >(MSG_ROUTING_CONTROL, ID, \ + MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply, \ + Tuple2 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4, type2_out* arg5) \ + : IPC::MessageWithReply, \ + Tuple2 >(MSG_ROUTING_CONTROL, ID, \ + MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4, *arg5)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \ + class msg_class : \ + public IPC::MessageWithReply, \ + Tuple3 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4, type2_out* arg5, type3_out* arg6) \ + : IPC::MessageWithReply, \ + Tuple3 >(MSG_ROUTING_CONTROL, \ + ID, \ + MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4, *arg5, *arg6)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \ + class msg_class : \ + public IPC::MessageWithReply, \ + Tuple1 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, type1_out* arg6) \ + : IPC::MessageWithReply, \ + Tuple1 >(MSG_ROUTING_CONTROL, ID, \ + MakeTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg6)) {} \ + }; + +#define IPC_SYNC_MESSAGE_CONTROL4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply, \ + Tuple2 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, type1_out* arg5, type2_out* arg6) \ + : IPC::MessageWithReply, \ + Tuple2 >(MSG_ROUTING_CONTROL, ID, \ + MakeTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg5, *arg6)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED0_1(msg_class, type1_out) \ + class msg_class : public IPC::MessageWithReply > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, type1_out* arg1) \ + : IPC::MessageWithReply >( \ + routing_id, ID, \ + MakeTuple(), MakeRefTuple(*arg1)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED0_0(msg_class) \ + class msg_class : public IPC::MessageWithReply { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id) \ + : IPC::MessageWithReply( \ + routing_id, ID, \ + MakeTuple(), MakeTuple()) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED0_2(msg_class, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, type1_out* arg1, type2_out* arg2) \ + : IPC::MessageWithReply >( \ + routing_id, ID, \ + MakeTuple(), MakeRefTuple(*arg1, *arg2)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED0_3(msg_class, type1_out, type2_out, type3_out) \ + class msg_class : \ + public IPC::MessageWithReply >{ \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, type1_out* arg1, type2_out* arg2, type3_out* arg3) \ + : IPC::MessageWithReply >(routing_id, ID, \ + MakeTuple(), MakeRefTuple(*arg1, *arg2, *arg3)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED1_0(msg_class, type1_in) \ + class msg_class : \ + public IPC::MessageWithReply { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1) \ + : IPC::MessageWithReply( \ + routing_id, ID, \ + arg1, MakeTuple()) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED1_1(msg_class, type1_in, type1_out) \ + class msg_class : \ + public IPC::MessageWithReply > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, type1_out* arg2) \ + : IPC::MessageWithReply >( \ + routing_id, ID, \ + arg1, MakeRefTuple(*arg2)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED1_2(msg_class, type1_in, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, type1_out* arg2, type2_out* arg3) \ + : IPC::MessageWithReply >( \ + routing_id, ID, \ + arg1, MakeRefTuple(*arg2, *arg3)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED1_3(msg_class, type1_in, type1_out, type2_out, type3_out) \ + class msg_class : \ + public IPC::MessageWithReply >{ \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, type1_out* arg2, type2_out* arg3, type3_out* arg4) \ + : IPC::MessageWithReply >(routing_id, ID, \ + arg1, MakeRefTuple(*arg2, *arg3, *arg4)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED1_4(msg_class, type1_in, type1_out, type2_out, type3_out, type4_out) \ + class msg_class : \ + public IPC::MessageWithReply >{ \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, type1_out* arg2, type2_out* arg3, type3_out* arg4, type4_out* arg5) \ + : IPC::MessageWithReply >(routing_id, ID, \ + arg1, MakeRefTuple(*arg2, *arg3, *arg4, *arg5)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED2_0(msg_class, type1_in, type2_in) \ + class msg_class : \ + public IPC::MessageWithReply, Tuple0 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2) \ + : IPC::MessageWithReply, Tuple0 >( \ + routing_id, ID, \ + MakeTuple(arg1, arg2), MakeTuple()) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED2_1(msg_class, type1_in, type2_in, type1_out) \ + class msg_class : \ + public IPC::MessageWithReply, Tuple1 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, type1_out* arg3) \ + : IPC::MessageWithReply, Tuple1 >( \ + routing_id, ID, \ + MakeTuple(arg1, arg2), MakeRefTuple(*arg3)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED2_2(msg_class, type1_in, type2_in, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply, \ + Tuple2 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, type1_out* arg3, type2_out* arg4) \ + : IPC::MessageWithReply, \ + Tuple2 >(routing_id, ID, \ + MakeTuple(arg1, arg2), MakeRefTuple(*arg3, *arg4)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED2_3(msg_class, type1_in, type2_in, type1_out, type2_out, type3_out) \ + class msg_class : \ + public IPC::MessageWithReply, \ + Tuple3 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, type1_out* arg3, type2_out* arg4, type3_out* arg5) \ + : IPC::MessageWithReply, \ + Tuple3 >(routing_id, ID, \ + MakeTuple(arg1, arg2), MakeRefTuple(*arg3, *arg4, *arg5)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED3_0(msg_class, type1_in, type2_in, type3_in) \ + class msg_class : \ + public IPC::MessageWithReply, Tuple0 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3) \ + : IPC::MessageWithReply, Tuple0>( \ + routing_id, ID, \ + MakeTuple(arg1, arg2, arg3), MakeTuple()) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED3_1(msg_class, type1_in, type2_in, type3_in, type1_out) \ + class msg_class : \ + public IPC::MessageWithReply, \ + Tuple1 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4) \ + : IPC::MessageWithReply, \ + Tuple1 >(routing_id, ID, \ + MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED3_2(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply, \ + Tuple2 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4, type2_out* arg5) \ + : IPC::MessageWithReply, \ + Tuple2 >(routing_id, ID, \ + MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4, *arg5)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED3_3(msg_class, type1_in, type2_in, type3_in, type1_out, type2_out, type3_out) \ + class msg_class : \ + public IPC::MessageWithReply, \ + Tuple3 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, type1_out* arg4, type2_out* arg5, type3_out* arg6) \ + : IPC::MessageWithReply, \ + Tuple3 >(routing_id, ID, \ + MakeTuple(arg1, arg2, arg3), MakeRefTuple(*arg4, *arg5, *arg6)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED4_0(msg_class, type1_in, type2_in, type3_in, type4_in) \ + class msg_class : \ + public IPC::MessageWithReply, \ + Tuple0 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4) \ + : IPC::MessageWithReply, \ + Tuple0 >(routing_id, ID, \ + MakeTuple(arg1, arg2, arg3, arg4), MakeTuple()) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED4_1(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out) \ + class msg_class : \ + public IPC::MessageWithReply, \ + Tuple1 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, type1_out* arg6) \ + : IPC::MessageWithReply, \ + Tuple1 >(routing_id, ID, \ + MakeTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg6)) {} \ + }; + +#define IPC_SYNC_MESSAGE_ROUTED4_2(msg_class, type1_in, type2_in, type3_in, type4_in, type1_out, type2_out) \ + class msg_class : \ + public IPC::MessageWithReply, \ + Tuple2 > { \ + public: \ + enum { ID = msg_class##__ID }; \ + msg_class(int routing_id, const type1_in& arg1, const type2_in& arg2, const type3_in& arg3, const type4_in& arg4, type1_out* arg5, type2_out* arg6) \ + : IPC::MessageWithReply, \ + Tuple2 >(routing_id, ID, \ + MakeTuple(arg1, arg2, arg3, arg4), MakeRefTuple(*arg5, *arg6)) {} \ + }; + +#endif // #if defined() diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_message_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_message_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_message_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_message_unittest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,94 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "chrome/common/ipc_message.h" +#include "chrome/common/ipc_message_utils.h" +#include "base/scoped_ptr.h" +#include "googleurl/src/gurl.h" +#include "testing/gtest/include/gtest/gtest.h" + +#include "SkBitmap.h" + +// Tests that serialize/deserialize correctly understand each other +TEST(IPCMessageTest, Serialize) { + const char* serialize_cases[] = { + "http://www.google.com/", + "http://user:pass@host.com:888/foo;bar?baz#nop", + "#inva://idurl/", + }; + + for (size_t i = 0; i < arraysize(serialize_cases); i++) { + GURL input(serialize_cases[i]); + IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); + IPC::ParamTraits::Write(&msg, input); + + GURL output; + void* iter = NULL; + EXPECT_TRUE(IPC::ParamTraits::Read(&msg, &iter, &output)); + + // We want to test each component individually to make sure its range was + // correctly serialized and deserialized, not just the spec. + EXPECT_EQ(input.possibly_invalid_spec(), output.possibly_invalid_spec()); + EXPECT_EQ(input.is_valid(), output.is_valid()); + EXPECT_EQ(input.scheme(), output.scheme()); + EXPECT_EQ(input.username(), output.username()); + EXPECT_EQ(input.password(), output.password()); + EXPECT_EQ(input.host(), output.host()); + EXPECT_EQ(input.port(), output.port()); + EXPECT_EQ(input.path(), output.path()); + EXPECT_EQ(input.query(), output.query()); + EXPECT_EQ(input.ref(), output.ref()); + } + + // Also test the corrupt case. + IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); + msg.WriteInt(99); + GURL output; + void* iter = NULL; + EXPECT_FALSE(IPC::ParamTraits::Read(&msg, &iter, &output)); +} + +// Tests bitmap serialization. +TEST(IPCMessageTest, Bitmap) { + SkBitmap bitmap; + + bitmap.setConfig(SkBitmap::kARGB_8888_Config, 10, 5); + bitmap.allocPixels(); + memset(bitmap.getPixels(), 'A', bitmap.getSize()); + + IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); + IPC::ParamTraits::Write(&msg, bitmap); + + SkBitmap output; + void* iter = NULL; + EXPECT_TRUE(IPC::ParamTraits::Read(&msg, &iter, &output)); + + EXPECT_EQ(bitmap.config(), output.config()); + EXPECT_EQ(bitmap.width(), output.width()); + EXPECT_EQ(bitmap.height(), output.height()); + EXPECT_EQ(bitmap.rowBytes(), output.rowBytes()); + EXPECT_EQ(bitmap.getSize(), output.getSize()); + EXPECT_EQ(memcmp(bitmap.getPixels(), output.getPixels(), bitmap.getSize()), + 0); + + // Also test the corrupt case. + IPC::Message bad_msg(1, 2, IPC::Message::PRIORITY_NORMAL); + // Copy the first message block over to |bad_msg|. + const char* fixed_data; + int fixed_data_size; + iter = NULL; + msg.ReadData(&iter, &fixed_data, &fixed_data_size); + bad_msg.WriteData(fixed_data, fixed_data_size); + // Add some bogus pixel data. + const size_t bogus_pixels_size = bitmap.getSize() * 2; + scoped_array bogus_pixels(new char[bogus_pixels_size]); + memset(bogus_pixels.get(), 'B', bogus_pixels_size); + bad_msg.WriteData(bogus_pixels.get(), bogus_pixels_size); + // Make sure we don't read out the bitmap! + SkBitmap bad_output; + iter = NULL; + EXPECT_FALSE(IPC::ParamTraits::Read(&bad_msg, &iter, &bad_output)); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_message_utils.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_message_utils.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_message_utils.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_message_utils.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,231 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/ipc_message_utils.h" + +#include "base/gfx/rect.h" +#ifndef CHROMIUM_MOZILLA_BUILD +#include "googleurl/src/gurl.h" +#endif +#ifndef EXCLUDE_SKIA_DEPENDENCIES +#include "SkBitmap.h" +#endif +#ifndef CHROMIUM_MOZILLA_BUILD +#include "webkit/glue/dom_operations.h" +#endif + +namespace IPC { + +#ifndef EXCLUDE_SKIA_DEPENDENCIES + +namespace { + +struct SkBitmap_Data { + // The configuration for the bitmap (bits per pixel, etc). + SkBitmap::Config fConfig; + + // The width of the bitmap in pixels. + uint32 fWidth; + + // The height of the bitmap in pixels. + uint32 fHeight; + + // The number of bytes between subsequent rows of the bitmap. + uint32 fRowBytes; + + void InitSkBitmapDataForTransfer(const SkBitmap& bitmap) { + fConfig = bitmap.config(); + fWidth = bitmap.width(); + fHeight = bitmap.height(); + fRowBytes = bitmap.rowBytes(); + } + + // Returns whether |bitmap| successfully initialized. + bool InitSkBitmapFromData(SkBitmap* bitmap, const char* pixels, + size_t total_pixels) const { + if (total_pixels) { + bitmap->setConfig(fConfig, fWidth, fHeight, fRowBytes); + if (!bitmap->allocPixels()) + return false; + if (total_pixels > bitmap->getSize()) + return false; + memcpy(bitmap->getPixels(), pixels, total_pixels); + } + return true; + } +}; + +} // namespace + + +void ParamTraits::Write(Message* m, const SkBitmap& p) { + size_t fixed_size = sizeof(SkBitmap_Data); + SkBitmap_Data bmp_data; + bmp_data.InitSkBitmapDataForTransfer(p); + m->WriteData(reinterpret_cast(&bmp_data), + static_cast(fixed_size)); + size_t pixel_size = p.getSize(); + SkAutoLockPixels p_lock(p); + m->WriteData(reinterpret_cast(p.getPixels()), + static_cast(pixel_size)); +} + +bool ParamTraits::Read(const Message* m, void** iter, SkBitmap* r) { + const char* fixed_data; + int fixed_data_size = 0; + if (!m->ReadData(iter, &fixed_data, &fixed_data_size) || + (fixed_data_size <= 0)) { + NOTREACHED(); + return false; + } + if (fixed_data_size != sizeof(SkBitmap_Data)) + return false; // Message is malformed. + + const char* variable_data; + int variable_data_size = 0; + if (!m->ReadData(iter, &variable_data, &variable_data_size) || + (variable_data_size < 0)) { + NOTREACHED(); + return false; + } + const SkBitmap_Data* bmp_data = + reinterpret_cast(fixed_data); + return bmp_data->InitSkBitmapFromData(r, variable_data, variable_data_size); +} + +void ParamTraits::Log(const SkBitmap& p, std::wstring* l) { + l->append(StringPrintf(L"")); +} + +#endif // EXCLUDE_SKIA_DEPENDENCIES + +#ifndef CHROMIUM_MOZILLA_BUILD +void ParamTraits::Write(Message* m, const GURL& p) { + m->WriteString(p.possibly_invalid_spec()); + // TODO(brettw) bug 684583: Add encoding for query params. +} + +bool ParamTraits::Read(const Message* m, void** iter, GURL* p) { + std::string s; + if (!m->ReadString(iter, &s)) { + *p = GURL(); + return false; + } + *p = GURL(s); + return true; +} + +void ParamTraits::Log(const GURL& p, std::wstring* l) { + l->append(UTF8ToWide(p.spec())); +} + +void ParamTraits::Write(Message* m, const gfx::Point& p) { + m->WriteInt(p.x()); + m->WriteInt(p.y()); +} + +bool ParamTraits::Read(const Message* m, void** iter, + gfx::Point* r) { + int x, y; + if (!m->ReadInt(iter, &x) || + !m->ReadInt(iter, &y)) + return false; + r->set_x(x); + r->set_y(y); + return true; +} + +void ParamTraits::Log(const gfx::Point& p, std::wstring* l) { + l->append(StringPrintf(L"(%d, %d)", p.x(), p.y())); +} + + +void ParamTraits::Write(Message* m, const gfx::Rect& p) { + m->WriteInt(p.x()); + m->WriteInt(p.y()); + m->WriteInt(p.width()); + m->WriteInt(p.height()); +} + +bool ParamTraits::Read(const Message* m, void** iter, gfx::Rect* r) { + int x, y, w, h; + if (!m->ReadInt(iter, &x) || + !m->ReadInt(iter, &y) || + !m->ReadInt(iter, &w) || + !m->ReadInt(iter, &h)) + return false; + r->set_x(x); + r->set_y(y); + r->set_width(w); + r->set_height(h); + return true; +} + +void ParamTraits::Log(const gfx::Rect& p, std::wstring* l) { + l->append(StringPrintf(L"(%d, %d, %d, %d)", p.x(), p.y(), + p.width(), p.height())); +} + + +void ParamTraits::Write(Message* m, const gfx::Size& p) { + m->WriteInt(p.width()); + m->WriteInt(p.height()); +} + +bool ParamTraits::Read(const Message* m, void** iter, gfx::Size* r) { + int w, h; + if (!m->ReadInt(iter, &w) || + !m->ReadInt(iter, &h)) + return false; + r->set_width(w); + r->set_height(h); + return true; +} + +void ParamTraits::Log(const gfx::Size& p, std::wstring* l) { + l->append(StringPrintf(L"(%d, %d)", p.width(), p.height())); +} + +void ParamTraits::Write( + Message* m, const webkit_glue::WebApplicationInfo& p) { + WriteParam(m, p.title); + WriteParam(m, p.description); + WriteParam(m, p.app_url); + WriteParam(m, p.icons.size()); + for (size_t i = 0; i < p.icons.size(); ++i) { + WriteParam(m, p.icons[i].url); + WriteParam(m, p.icons[i].width); + WriteParam(m, p.icons[i].height); + } +} + +bool ParamTraits::Read( + const Message* m, void** iter, webkit_glue::WebApplicationInfo* r) { + size_t icon_count; + bool result = + ReadParam(m, iter, &r->title) && + ReadParam(m, iter, &r->description) && + ReadParam(m, iter, &r->app_url) && + ReadParam(m, iter, &icon_count); + if (!result) + return false; + for (size_t i = 0; i < icon_count && result; ++i) { + param_type::IconInfo icon_info; + result = + ReadParam(m, iter, &icon_info.url) && + ReadParam(m, iter, &icon_info.width) && + ReadParam(m, iter, &icon_info.height); + r->icons.push_back(icon_info); + } + return result; +} + +void ParamTraits::Log( + const webkit_glue::WebApplicationInfo& p, std::wstring* l) { + l->append(L""); +} + +#endif // CHROMIUM_MOZILLA_BUILD + +} // namespace IPC diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_message_utils.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_message_utils.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_message_utils.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_message_utils.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,1442 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_IPC_MESSAGE_UTILS_H_ +#define CHROME_COMMON_IPC_MESSAGE_UTILS_H_ + +#include +#include +#include + +#include "base/file_path.h" +#include "base/string_util.h" +#include "base/string16.h" +#include "base/tuple.h" + +#if defined(OS_POSIX) +#include "chrome/common/file_descriptor_set_posix.h" +#endif +#include "chrome/common/ipc_sync_message.h" +#include "chrome/common/thumbnail_score.h" +#include "chrome/common/transport_dib.h" +#ifndef CHROMIUM_MOZILLA_BUILD +#include "webkit/glue/webcursor.h" +#include "webkit/glue/window_open_disposition.h" + +// Forward declarations. +class GURL; +class SkBitmap; + +namespace gfx { +class Point; +class Rect; +class Size; +} // namespace gfx + +namespace webkit_glue { +struct WebApplicationInfo; +} // namespace webkit_glue + +// Used by IPC_BEGIN_MESSAGES so that each message class starts from a unique +// base. Messages have unique IDs across channels in order for the IPC logging +// code to figure out the message class from its ID. +enum IPCMessageStart { + // By using a start value of 0 for automation messages, we keep backward + // compatibility with old builds. + AutomationMsgStart = 0, + ViewMsgStart, + ViewHostMsgStart, + PluginProcessMsgStart, + PluginProcessHostMsgStart, + PluginMsgStart, + PluginHostMsgStart, + NPObjectMsgStart, + TestMsgStart, + DevToolsAgentMsgStart, + DevToolsClientMsgStart, + WorkerProcessMsgStart, + WorkerProcessHostMsgStart, + WorkerMsgStart, + WorkerHostMsgStart, + // NOTE: When you add a new message class, also update + // IPCStatusView::IPCStatusView to ensure logging works. + // NOTE: this enum is used by IPC_MESSAGE_MACRO to generate a unique message + // id. Only 4 bits are used for the message type, so if this enum needs more + // than 16 entries, that code needs to be updated. + LastMsgIndex +}; + +COMPILE_ASSERT(LastMsgIndex <= 16, need_to_update_IPC_MESSAGE_MACRO); + +#endif /* CHROMIUM_MOZILLA_BUILD */ + +namespace IPC { + +//----------------------------------------------------------------------------- +// An iterator class for reading the fields contained within a Message. + +class MessageIterator { + public: + explicit MessageIterator(const Message& m) : msg_(m), iter_(NULL) { + } + int NextInt() const { + int val; + if (!msg_.ReadInt(&iter_, &val)) + NOTREACHED(); + return val; + } + intptr_t NextIntPtr() const { + intptr_t val; + if (!msg_.ReadIntPtr(&iter_, &val)) + NOTREACHED(); + return val; + } + const std::string NextString() const { + std::string val; + if (!msg_.ReadString(&iter_, &val)) + NOTREACHED(); + return val; + } + const std::wstring NextWString() const { + std::wstring val; + if (!msg_.ReadWString(&iter_, &val)) + NOTREACHED(); + return val; + } + const void NextData(const char** data, int* length) const { + if (!msg_.ReadData(&iter_, data, length)) { + NOTREACHED(); + } + } + private: + const Message& msg_; + mutable void* iter_; +}; + +//----------------------------------------------------------------------------- +// ParamTraits specializations, etc. + +template struct ParamTraits {}; + +template +static inline void WriteParam(Message* m, const P& p) { + ParamTraits

    ::Write(m, p); +} + +template +static inline bool WARN_UNUSED_RESULT ReadParam(const Message* m, void** iter, + P* p) { + return ParamTraits

    ::Read(m, iter, p); +} + +template +static inline void LogParam(const P& p, std::wstring* l) { + ParamTraits

    ::Log(p, l); +} + +template <> +struct ParamTraits { + typedef bool param_type; + static void Write(Message* m, const param_type& p) { + m->WriteBool(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadBool(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(p ? L"true" : L"false"); + } +}; + +template <> +struct ParamTraits { + typedef int16 param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadInt16(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%hd", p)); + } +}; + +template <> +struct ParamTraits { + typedef uint16 param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadUInt16(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%hu", p)); + } +}; + +template <> +struct ParamTraits { + typedef int param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadInt(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%d", p)); + } +}; + +template <> +struct ParamTraits { + typedef long param_type; + static void Write(Message* m, const param_type& p) { + m->WriteLong(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadLong(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%l", p)); + } +}; + +template <> +struct ParamTraits { + typedef unsigned long param_type; + static void Write(Message* m, const param_type& p) { + m->WriteULong(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadULong(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%ul", p)); + } +}; + +#if !(defined(OS_MACOSX) || (defined(CHROMIUM_MOZILLA_BUILD) && (defined(OS_LINUX) || defined(OS_WIN)) && defined(ARCH_CPU_64_BITS))) +// There size_t is a synonym for |unsigned long| ... +template <> +struct ParamTraits { + typedef size_t param_type; + static void Write(Message* m, const param_type& p) { + m->WriteSize(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadSize(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%u", p)); + } +}; + +#elif !defined(OS_MACOSX) +// ... so we need to define traits for |unsigned int|. +// XXX duplicating OS_MACOSX version below so as not to conflict +template <> +struct ParamTraits { + typedef uint32 param_type; + static void Write(Message* m, const param_type& p) { + m->WriteUInt32(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadUInt32(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%u", p)); + } +}; + +#endif // if !((defined(OS_LINUX) || defined(OS_WIN)) && defined(ARCH_CPU_64_BITS)) + +#if defined(OS_MACOSX) +// On Linux size_t & uint32 can be the same type. +// TODO(playmobil): Fix compilation if this is not the case. +template <> +struct ParamTraits { + typedef uint32 param_type; + static void Write(Message* m, const param_type& p) { + m->WriteUInt32(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadUInt32(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%u", p)); + } +}; +#endif // defined(OS_MACOSX) + +#if !(defined(CHROMIUM_MOZILLA_BUILD) && defined(OS_LINUX) && defined(ARCH_CPU_64_BITS)) +// int64 is |long int| on 64-bit systems, uint64 is |unsigned long| +template <> +struct ParamTraits { + typedef int64 param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt64(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadInt64(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { +#ifndef CHROMIUM_MOZILLA_BUILD + l->append(StringPrintf(L"%I64d", p)); +#else + l->append(StringPrintf(L"%" PRId64L, p)); +#endif // ifndef CHROMIUM_MOZILLA_BUILD + } +}; + +template <> +struct ParamTraits { + typedef uint64 param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt64(static_cast(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadInt64(iter, reinterpret_cast(r)); + } + static void Log(const param_type& p, std::wstring* l) { +#ifndef CHROMIUM_MOZILLA_BUILD + l->append(StringPrintf(L"%I64u", p)); +#else + l->append(StringPrintf(L"%" PRIu64L, p)); +#endif // ifndef CHROMIUM_MOZILLA_BUILD + } +}; +#endif // if !(defined(CHROMIUM_MOZILLA_BUILD) && defined(OS_LINUX) && defined(ARCH_CPU_64_BITS)) + +template <> +struct ParamTraits { + typedef double param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast(&p), sizeof(param_type)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(param_type)) { + memcpy(r, data, sizeof(param_type)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"e", p)); + } +}; + +#ifndef CHROMIUM_MOZILLA_BUILD +template <> +struct ParamTraits { + typedef wchar_t param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast(&p), sizeof(param_type)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(param_type)) { + memcpy(r, data, sizeof(param_type)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%lc", p)); + } +}; +#endif /* CHROMIUM_MOZILLA_BUILD */ + +template <> +struct ParamTraits { + typedef base::Time param_type; + static void Write(Message* m, const param_type& p) { + ParamTraits::Write(m, p.ToInternalValue()); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int64 value; + if (!ParamTraits::Read(m, iter, &value)) + return false; + *r = base::Time::FromInternalValue(value); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + ParamTraits::Log(p.ToInternalValue(), l); + } +}; + +#if defined(OS_WIN) +template <> +struct ParamTraits { + typedef LOGFONT param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast(&p), sizeof(LOGFONT)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(LOGFONT)) { + memcpy(r, data, sizeof(LOGFONT)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"")); + } +}; + +template <> +struct ParamTraits { + typedef MSG param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast(&p), sizeof(MSG)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(MSG)) { + memcpy(r, data, sizeof(MSG)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } +}; +#endif // defined(OS_WIN) + +#ifndef CHROMIUM_MOZILLA_BUILD +template <> +struct ParamTraits { + typedef SkBitmap param_type; + static void Write(Message* m, const param_type& p); + + // Note: This function expects parameter |r| to be of type &SkBitmap since + // r->SetConfig() and r->SetPixels() are called. + static bool Read(const Message* m, void** iter, param_type* r); + + static void Log(const param_type& p, std::wstring* l); +}; +#endif /* CHROMIUM_MOZILLA_BUILD */ + +template <> +struct ParamTraits { + typedef std::string param_type; + static void Write(Message* m, const param_type& p) { + m->WriteString(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadString(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(UTF8ToWide(p)); + } +}; + +template <> +struct ParamTraits > { + typedef std::vector param_type; + static void Write(Message* m, const param_type& p) { + if (p.size() == 0) { + m->WriteData(NULL, 0); + } else { + m->WriteData(reinterpret_cast(&p.front()), + static_cast(p.size())); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + if (!m->ReadData(iter, &data, &data_size) || data_size < 0) + return false; + r->resize(data_size); + if (data_size) + memcpy(&r->front(), data, data_size); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + for (size_t i = 0; i < p.size(); ++i) + l->push_back(p[i]); + } +}; + +template <> +struct ParamTraits > { + typedef std::vector param_type; + static void Write(Message* m, const param_type& p) { + if (p.size() == 0) { + m->WriteData(NULL, 0); + } else { + m->WriteData(&p.front(), static_cast(p.size())); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + if (!m->ReadData(iter, &data, &data_size) || data_size < 0) + return false; + r->resize(data_size); + if (data_size) + memcpy(&r->front(), data, data_size); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + for (size_t i = 0; i < p.size(); ++i) + l->push_back(p[i]); + } +}; + +template +struct ParamTraits > { + typedef std::vector

    param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast(p.size())); + for (size_t i = 0; i < p.size(); i++) + WriteParam(m, p[i]); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int size; + if (!m->ReadLength(iter, &size)) + return false; + // Resizing beforehand is not safe, see BUG 1006367 for details. + if (m->IteratorHasRoomFor(*iter, size * sizeof(P))) { + r->resize(size); + for (int i = 0; i < size; i++) { + if (!ReadParam(m, iter, &(*r)[i])) + return false; + } + } else { + for (int i = 0; i < size; i++) { + P element; + if (!ReadParam(m, iter, &element)) + return false; + r->push_back(element); + } + } + return true; + } + static void Log(const param_type& p, std::wstring* l) { + for (size_t i = 0; i < p.size(); ++i) { + if (i != 0) + l->append(L" "); + + LogParam((p[i]), l); + } + } +}; + +template +struct ParamTraits > { + typedef std::map param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast(p.size())); + typename param_type::const_iterator iter; + for (iter = p.begin(); iter != p.end(); ++iter) { + WriteParam(m, iter->first); + WriteParam(m, iter->second); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + int size; + if (!ReadParam(m, iter, &size) || size < 0) + return false; + for (int i = 0; i < size; ++i) { + K k; + if (!ReadParam(m, iter, &k)) + return false; + V& value = (*r)[k]; + if (!ReadParam(m, iter, &value)) + return false; + } + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + + +template <> +struct ParamTraits { + typedef std::wstring param_type; + static void Write(Message* m, const param_type& p) { + m->WriteWString(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadWString(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(p); + } +}; + +// If WCHAR_T_IS_UTF16 is defined, then string16 is a std::wstring so we don't +// need this trait. +#if !defined(WCHAR_T_IS_UTF16) +template <> +struct ParamTraits { + typedef string16 param_type; + static void Write(Message* m, const param_type& p) { + m->WriteString16(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return m->ReadString16(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(UTF16ToWide(p)); + } +}; +#endif + +#ifndef CHROMIUM_MOZILLA_BUILD +template <> +struct ParamTraits { + typedef GURL param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* p); + static void Log(const param_type& p, std::wstring* l); +}; +#endif /* CHROMIUM_MOZILLA_BUILD */ + +// and, a few more useful types... +#if defined(OS_WIN) +template <> +struct ParamTraits { + typedef HANDLE param_type; + static void Write(Message* m, const param_type& p) { + m->WriteIntPtr(reinterpret_cast(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + DCHECK_EQ(sizeof(param_type), sizeof(intptr_t)); + return m->ReadIntPtr(iter, reinterpret_cast(r)); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"0x%X", p)); + } +}; + +template <> +struct ParamTraits { + typedef HCURSOR param_type; + static void Write(Message* m, const param_type& p) { + m->WriteIntPtr(reinterpret_cast(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + DCHECK_EQ(sizeof(param_type), sizeof(intptr_t)); + return m->ReadIntPtr(iter, reinterpret_cast(r)); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"0x%X", p)); + } +}; + +template <> +struct ParamTraits { + typedef HWND param_type; + static void Write(Message* m, const param_type& p) { + m->WriteIntPtr(reinterpret_cast(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + DCHECK_EQ(sizeof(param_type), sizeof(intptr_t)); + return m->ReadIntPtr(iter, reinterpret_cast(r)); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"0x%X", p)); + } +}; + +template <> +struct ParamTraits { + typedef HACCEL param_type; + static void Write(Message* m, const param_type& p) { + m->WriteIntPtr(reinterpret_cast(p)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + DCHECK_EQ(sizeof(param_type), sizeof(intptr_t)); + return m->ReadIntPtr(iter, reinterpret_cast(r)); + } +}; + +template <> +struct ParamTraits { + typedef POINT param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p.x); + m->WriteInt(p.y); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int x, y; + if (!m->ReadInt(iter, &x) || !m->ReadInt(iter, &y)) + return false; + r->x = x; + r->y = y; + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"(%d, %d)", p.x, p.y)); + } +}; +#endif // defined(OS_WIN) + +template <> +struct ParamTraits { + typedef FilePath param_type; + static void Write(Message* m, const param_type& p) { + ParamTraits::Write(m, p.value()); + } + static bool Read(const Message* m, void** iter, param_type* r) { + FilePath::StringType value; + if (!ParamTraits::Read(m, iter, &value)) + return false; + *r = FilePath(value); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + ParamTraits::Log(p.value(), l); + } +}; + +#ifndef CHROMIUM_MOZILLA_BUILD +template <> +struct ParamTraits { + typedef gfx::Point param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::wstring* l); +}; + +template <> +struct ParamTraits { + typedef gfx::Rect param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::wstring* l); +}; + +template <> +struct ParamTraits { + typedef gfx::Size param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::wstring* l); +}; +#endif /* CHROMIUM_MOZILLA_BUILD */ + +#if defined(OS_POSIX) +// FileDescriptors may be serialised over IPC channels on POSIX. On the +// receiving side, the FileDescriptor is a valid duplicate of the file +// descriptor which was transmitted: *it is not just a copy of the integer like +// HANDLEs on Windows*. The only exception is if the file descriptor is < 0. In +// this case, the receiving end will see a value of -1. *Zero is a valid file +// descriptor*. +// +// The received file descriptor will have the |auto_close| flag set to true. The +// code which handles the message is responsible for taking ownership of it. +// File descriptors are OS resources and must be closed when no longer needed. +// +// When sending a file descriptor, the file descriptor must be valid at the time +// of transmission. Since transmission is not synchronous, one should consider +// dup()ing any file descriptors to be transmitted and setting the |auto_close| +// flag, which causes the file descriptor to be closed after writing. +template<> +struct ParamTraits { + typedef base::FileDescriptor param_type; + static void Write(Message* m, const param_type& p) { + const bool valid = p.fd >= 0; + WriteParam(m, valid); + + if (valid) { + if (!m->WriteFileDescriptor(p)) + NOTREACHED(); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + bool valid; + if (!ReadParam(m, iter, &valid)) + return false; + + if (!valid) { + r->fd = -1; + r->auto_close = false; + return true; + } + + return m->ReadFileDescriptor(iter, r); + } + static void Log(const param_type& p, std::wstring* l) { + if (p.auto_close) { + l->append(StringPrintf(L"FD(%d auto-close)", p.fd)); + } else { + l->append(StringPrintf(L"FD(%d)", p.fd)); + } + } +}; +#endif // defined(OS_POSIX) + +template<> +struct ParamTraits { + typedef ThumbnailScore param_type; + static void Write(Message* m, const param_type& p) { + IPC::ParamTraits::Write(m, p.boring_score); + IPC::ParamTraits::Write(m, p.good_clipping); + IPC::ParamTraits::Write(m, p.at_top); + IPC::ParamTraits::Write(m, p.time_at_snapshot); + } + static bool Read(const Message* m, void** iter, param_type* r) { + double boring_score; + bool good_clipping, at_top; + base::Time time_at_snapshot; + if (!IPC::ParamTraits::Read(m, iter, &boring_score) || + !IPC::ParamTraits::Read(m, iter, &good_clipping) || + !IPC::ParamTraits::Read(m, iter, &at_top) || + !IPC::ParamTraits::Read(m, iter, &time_at_snapshot)) + return false; + + r->boring_score = boring_score; + r->good_clipping = good_clipping; + r->at_top = at_top; + r->time_at_snapshot = time_at_snapshot; + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"(%f, %d, %d)", + p.boring_score, p.good_clipping, p.at_top)); + } +}; + +#ifndef CHROMIUM_MOZILLA_BUILD +template <> +struct ParamTraits { + typedef WindowOpenDisposition param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int temp; + bool res = m->ReadInt(iter, &temp); + *r = static_cast(temp); + return res; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"%d", p)); + } +}; +#endif + +#if defined(OS_WIN) +template <> +struct ParamTraits { + typedef XFORM param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast(&p), sizeof(XFORM)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (result && data_size == sizeof(XFORM)) { + memcpy(r, data, sizeof(XFORM)); + } else { + result = false; + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; +#endif // defined(OS_WIN) + +#ifndef CHROMIUM_MOZILLA_BUILD +template <> +struct ParamTraits { + typedef WebCursor param_type; + static void Write(Message* m, const param_type& p) { + p.Serialize(m); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return r->Deserialize(m, iter); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; +#endif + +struct LogData { + std::wstring channel; + int32 routing_id; + uint16 type; + std::wstring flags; + int64 sent; // Time that the message was sent (i.e. at Send()). + int64 receive; // Time before it was dispatched (i.e. before calling + // OnMessageReceived). + int64 dispatch; // Time after it was dispatched (i.e. after calling + // OnMessageReceived). + std::wstring message_name; + std::wstring params; +}; + +template <> +struct ParamTraits { + typedef LogData param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.channel); + WriteParam(m, p.routing_id); + WriteParam(m, static_cast(p.type)); + WriteParam(m, p.flags); + WriteParam(m, p.sent); + WriteParam(m, p.receive); + WriteParam(m, p.dispatch); + WriteParam(m, p.params); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int type; + bool result = + ReadParam(m, iter, &r->channel) && + ReadParam(m, iter, &r->routing_id); + ReadParam(m, iter, &type) && + ReadParam(m, iter, &r->flags) && + ReadParam(m, iter, &r->sent) && + ReadParam(m, iter, &r->receive) && + ReadParam(m, iter, &r->dispatch) && + ReadParam(m, iter, &r->params); + r->type = static_cast(type); + return result; + } + static void Log(const param_type& p, std::wstring* l) { + // Doesn't make sense to implement this! + } +}; + +#ifndef CHROMIUM_MOZILLA_BUILD +template <> +struct ParamTraits { + typedef webkit_glue::WebApplicationInfo param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* r); + static void Log(const param_type& p, std::wstring* l); +}; +#endif /* CHROMIUM_MOZILLA_BUILD */ + +#if defined(OS_WIN) +template<> +struct ParamTraits { + typedef TransportDIB::Id param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.handle); + WriteParam(m, p.sequence_num); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->handle) && + ReadParam(m, iter, &r->sequence_num)); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"TransportDIB("); + LogParam(p.handle, l); + l->append(L", "); + LogParam(p.sequence_num, l); + l->append(L")"); + } +}; +#endif + +template <> +struct ParamTraits { + static void Write(Message* m, const Message& p) { + m->WriteInt(p.size()); + m->WriteData(reinterpret_cast(p.data()), p.size()); + } + static bool Read(const Message* m, void** iter, Message* r) { + int size; + if (!m->ReadInt(iter, &size)) + return false; + const char* data; + if (!m->ReadData(iter, &data, &size)) + return false; + *r = Message(data, size); + return true; + } + static void Log(const Message& p, std::wstring* l) { + l->append(L""); + } +}; + +template <> +struct ParamTraits { + typedef Tuple0 param_type; + static void Write(Message* m, const param_type& p) { + } + static bool Read(const Message* m, void** iter, param_type* r) { + return true; + } + static void Log(const param_type& p, std::wstring* l) { + } +}; + +template +struct ParamTraits< Tuple1 > { + typedef Tuple1 param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return ReadParam(m, iter, &r->a); + } + static void Log(const param_type& p, std::wstring* l) { + LogParam(p.a, l); + } +}; + +template +struct ParamTraits< Tuple2 > { + typedef Tuple2 param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b)); + } + static void Log(const param_type& p, std::wstring* l) { + LogParam(p.a, l); + l->append(L", "); + LogParam(p.b, l); + } +}; + +template +struct ParamTraits< Tuple3 > { + typedef Tuple3 param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + WriteParam(m, p.c); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b) && + ReadParam(m, iter, &r->c)); + } + static void Log(const param_type& p, std::wstring* l) { + LogParam(p.a, l); + l->append(L", "); + LogParam(p.b, l); + l->append(L", "); + LogParam(p.c, l); + } +}; + +template +struct ParamTraits< Tuple4 > { + typedef Tuple4 param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + WriteParam(m, p.c); + WriteParam(m, p.d); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b) && + ReadParam(m, iter, &r->c) && + ReadParam(m, iter, &r->d)); + } + static void Log(const param_type& p, std::wstring* l) { + LogParam(p.a, l); + l->append(L", "); + LogParam(p.b, l); + l->append(L", "); + LogParam(p.c, l); + l->append(L", "); + LogParam(p.d, l); + } +}; + +template +struct ParamTraits< Tuple5 > { + typedef Tuple5 param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + WriteParam(m, p.c); + WriteParam(m, p.d); + WriteParam(m, p.e); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b) && + ReadParam(m, iter, &r->c) && + ReadParam(m, iter, &r->d) && + ReadParam(m, iter, &r->e)); + } + static void Log(const param_type& p, std::wstring* l) { + LogParam(p.a, l); + l->append(L", "); + LogParam(p.b, l); + l->append(L", "); + LogParam(p.c, l); + l->append(L", "); + LogParam(p.d, l); + l->append(L", "); + LogParam(p.e, l); + } +}; + +template +struct ParamTraits< Tuple6 > { + typedef Tuple6 param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.a); + WriteParam(m, p.b); + WriteParam(m, p.c); + WriteParam(m, p.d); + WriteParam(m, p.e); + WriteParam(m, p.f); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return (ReadParam(m, iter, &r->a) && + ReadParam(m, iter, &r->b) && + ReadParam(m, iter, &r->c) && + ReadParam(m, iter, &r->d) && + ReadParam(m, iter, &r->e) && + ReadParam(m, iter, &r->f)); + } + static void Log(const param_type& p, std::wstring* l) { + LogParam(p.a, l); + l->append(L", "); + LogParam(p.b, l); + l->append(L", "); + LogParam(p.c, l); + l->append(L", "); + LogParam(p.d, l); + l->append(L", "); + LogParam(p.e, l); + l->append(L", "); + LogParam(p.f, l); + } +}; + + + +//----------------------------------------------------------------------------- +// Generic message subclasses + +// Used for asynchronous messages. +template +class MessageWithTuple : public Message { + public: + typedef ParamType Param; + + MessageWithTuple(int32 routing_id, uint16 type, const Param& p) + : Message(routing_id, type, PRIORITY_NORMAL) { + WriteParam(this, p); + } + + static bool Read(const Message* msg, Param* p) { + void* iter = NULL; + bool rv = ReadParam(msg, &iter, p); + DCHECK(rv) << "Error deserializing message " << msg->type(); + return rv; + } + + // Generic dispatcher. Should cover most cases. + template + static bool Dispatch(const Message* msg, T* obj, Method func) { + Param p; + if (Read(msg, &p)) { + DispatchToMethod(obj, func, p); + return true; + } + return false; + } + + // The following dispatchers exist for the case where the callback function + // needs the message as well. They assume that "Param" is a type of Tuple + // (except the one arg case, as there is no Tuple1). + template + static bool Dispatch(const Message* msg, T* obj, + void (T::*func)(const Message&, TA)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p); + return true; + } + return false; + } + + template + static bool Dispatch(const Message* msg, T* obj, + void (T::*func)(const Message&, TA, TB)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a, p.b); + return true; + } + return false; + } + + template + static bool Dispatch(const Message* msg, T* obj, + void (T::*func)(const Message&, TA, TB, TC)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a, p.b, p.c); + return true; + } + return false; + } + + template + static bool Dispatch(const Message* msg, T* obj, + void (T::*func)(const Message&, TA, TB, TC, TD)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a, p.b, p.c, p.d); + return true; + } + return false; + } + + template + static bool Dispatch(const Message* msg, T* obj, + void (T::*func)(const Message&, TA, TB, TC, TD, TE)) { + Param p; + if (Read(msg, &p)) { + (obj->*func)(*msg, p.a, p.b, p.c, p.d, p.e); + return true; + } + return false; + } + + static void Log(const Message* msg, std::wstring* l) { + Param p; + if (Read(msg, &p)) + LogParam(p, l); + } + + // Functions used to do manual unpacking. Only used by the automation code, + // these should go away once that code uses SyncChannel. + template + static bool Read(const IPC::Message* msg, TA* a, TB* b) { + ParamType params; + if (!Read(msg, ¶ms)) + return false; + *a = params.a; + *b = params.b; + return true; + } + + template + static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c) { + ParamType params; + if (!Read(msg, ¶ms)) + return false; + *a = params.a; + *b = params.b; + *c = params.c; + return true; + } + + template + static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c, TD* d) { + ParamType params; + if (!Read(msg, ¶ms)) + return false; + *a = params.a; + *b = params.b; + *c = params.c; + *d = params.d; + return true; + } + + template + static bool Read(const IPC::Message* msg, TA* a, TB* b, TC* c, TD* d, TE* e) { + ParamType params; + if (!Read(msg, ¶ms)) + return false; + *a = params.a; + *b = params.b; + *c = params.c; + *d = params.d; + *e = params.e; + return true; + } +}; + +// This class assumes that its template argument is a RefTuple (a Tuple with +// reference elements). +template +class ParamDeserializer : public MessageReplyDeserializer { + public: + explicit ParamDeserializer(const RefTuple& out) : out_(out) { } + + bool SerializeOutputParameters(const IPC::Message& msg, void* iter) { + return ReadParam(&msg, &iter, &out_); + } + + RefTuple out_; +}; + +// defined in ipc_logging.cc +void GenerateLogData(const std::wstring& channel, const Message& message, + LogData* data); + +// Used for synchronous messages. +template +class MessageWithReply : public SyncMessage { + public: + typedef SendParamType SendParam; + typedef ReplyParamType ReplyParam; + + MessageWithReply(int32 routing_id, uint16 type, + const SendParam& send, const ReplyParam& reply) + : SyncMessage(routing_id, type, PRIORITY_NORMAL, + new ParamDeserializer(reply)) { + WriteParam(this, send); + } + + static void Log(const Message* msg, std::wstring* l) { + if (msg->is_sync()) { + SendParam p; + void* iter = SyncMessage::GetDataIterator(msg); + if (ReadParam(msg, &iter, &p)) + LogParam(p, l); + +#if defined(IPC_MESSAGE_LOG_ENABLED) + const std::wstring& output_params = msg->output_params(); + if (!l->empty() && !output_params.empty()) + l->append(L", "); + + l->append(output_params); +#endif + } else { + // This is an outgoing reply. Now that we have the output parameters, we + // can finally log the message. + typename ReplyParam::ValueTuple p; + void* iter = SyncMessage::GetDataIterator(msg); + if (ReadParam(msg, &iter, &p)) + LogParam(p, l); + } + } + + template + static bool Dispatch(const Message* msg, T* obj, Method func) { + SendParam send_params; + void* iter = GetDataIterator(msg); + Message* reply = GenerateReply(msg); + bool error; + if (ReadParam(msg, &iter, &send_params)) { + typename ReplyParam::ValueTuple reply_params; + DispatchToMethod(obj, func, send_params, &reply_params); + WriteParam(reply, reply_params); + error = false; +#ifdef IPC_MESSAGE_LOG_ENABLED + if (msg->received_time() != 0) { + std::wstring output_params; + LogParam(reply_params, &output_params); + msg->set_output_params(output_params); + } +#endif + } else { + NOTREACHED() << "Error deserializing message " << msg->type(); + reply->set_reply_error(); + error = true; + } + + obj->Send(reply); + return !error; + } + + template + static bool DispatchDelayReply(const Message* msg, T* obj, Method func) { + SendParam send_params; + void* iter = GetDataIterator(msg); + Message* reply = GenerateReply(msg); + bool error; + if (ReadParam(msg, &iter, &send_params)) { + Tuple1 t = MakeRefTuple(*reply); + +#ifdef IPC_MESSAGE_LOG_ENABLED + if (msg->sent_time()) { + // Don't log the sync message after dispatch, as we don't have the + // output parameters at that point. Instead, save its data and log it + // with the outgoing reply message when it's sent. + LogData* data = new LogData; + GenerateLogData(L"", *msg, data); + msg->set_dont_log(); + reply->set_sync_log_data(data); + } +#endif + DispatchToMethod(obj, func, send_params, &t); + error = false; + } else { + NOTREACHED() << "Error deserializing message " << msg->type(); + reply->set_reply_error(); + obj->Send(reply); + error = true; + } + return !error; + } + + template + static void WriteReplyParams(Message* reply, TA a) { + ReplyParam p(a); + WriteParam(reply, p); + } + + template + static void WriteReplyParams(Message* reply, TA a, TB b) { + ReplyParam p(a, b); + WriteParam(reply, p); + } + + template + static void WriteReplyParams(Message* reply, TA a, TB b, TC c) { + ReplyParam p(a, b, c); + WriteParam(reply, p); + } + + template + static void WriteReplyParams(Message* reply, TA a, TB b, TC c, TD d) { + ReplyParam p(a, b, c, d); + WriteParam(reply, p); + } + + template + static void WriteReplyParams(Message* reply, TA a, TB b, TC c, TD d, TE e) { + ReplyParam p(a, b, c, d, e); + WriteParam(reply, p); + } +}; + +//----------------------------------------------------------------------------- + +} // namespace IPC + +#endif // CHROME_COMMON_IPC_MESSAGE_UTILS_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_send_fds_test.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_send_fds_test.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_send_fds_test.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_send_fds_test.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,187 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "build/build_config.h" + +#include "chrome/common/ipc_tests.h" + +#if defined(OS_MACOSX) +extern "C" { +#include +} +#endif +#include +#include + +#include "base/eintr_wrapper.h" +#include "base/message_loop.h" +#include "chrome/common/ipc_channel.h" +#include "chrome/common/ipc_message_utils.h" + +#if defined(OS_POSIX) + +namespace { + +const unsigned kNumFDsToSend = 20; +const char* kDevZeroPath = "/dev/zero"; + +static void VerifyAndCloseDescriptor(int fd, ino_t inode_num) { + // Check that we can read from the FD. + char buf; + ssize_t amt_read = read(fd, &buf, 1); + ASSERT_EQ(amt_read, 1); + ASSERT_EQ(buf, 0); // /dev/zero always reads NUL bytes. + + struct stat st; + ASSERT_EQ(fstat(fd, &st), 0); + + ASSERT_EQ(close(fd), 0); + + // We compare iNode numbers to check that the file sent over the wire + // was actually the same physical file as the one we were expecting. + ASSERT_EQ(inode_num, st.st_ino); +} + +class MyChannelDescriptorListener : public IPC::Channel::Listener { + public: + MyChannelDescriptorListener(ino_t expected_inode_num) + : expected_inode_num_(expected_inode_num), + num_fds_received_(0) {} + + virtual void OnMessageReceived(const IPC::Message& message) { + void* iter = NULL; + + ++num_fds_received_; + base::FileDescriptor descriptor; + + ASSERT_TRUE( + IPC::ParamTraits::Read( + &message, &iter, &descriptor)); + + VerifyAndCloseDescriptor(descriptor.fd, expected_inode_num_); + if (num_fds_received_ == kNumFDsToSend) { + MessageLoop::current()->Quit(); + } + } + + virtual void OnChannelError() { + MessageLoop::current()->Quit(); + } + private: + ino_t expected_inode_num_; + unsigned num_fds_received_; +}; + +void TestDescriptorServer(IPC::Channel &chan, + base::ProcessHandle process_handle) { + ASSERT_TRUE(process_handle); + + for (unsigned i = 0; i < kNumFDsToSend; ++i) { + base::FileDescriptor descriptor; + const int fd = open(kDevZeroPath, O_RDONLY); + ASSERT_GE(fd, 0); + descriptor.auto_close = true; + descriptor.fd = fd; + + IPC::Message* message = new IPC::Message(0, // routing_id + 3, // message type + IPC::Message::PRIORITY_NORMAL); + IPC::ParamTraits::Write(message, descriptor); + chan.Send(message); + } + + // Run message loop. + MessageLoop::current()->Run(); + + // Close Channel so client gets its OnChannelError() callback fired. + chan.Close(); + + // Cleanup child process. + EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000)); +} + +int TestDescriptorClient(ino_t expected_inode_num) { + MessageLoopForIO main_message_loop; + MyChannelDescriptorListener listener(expected_inode_num); + + // Setup IPC channel. + IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT, + &listener); + chan.Connect(); + MessageLoop::current()->Run(); + + return 0; +} + +} // namespace + +// --------------------------------------------------------------------------- +#if defined(OS_MACOSX) +// TODO(port): Make this test cross-platform. +MULTIPROCESS_TEST_MAIN(RunTestDescriptorClientSandboxed) { + struct stat st; + const int fd = open(kDevZeroPath, O_RDONLY); + fstat(fd, &st); + HANDLE_EINTR(close(fd)); + + // Enable the Sandbox. + char* error_buff = NULL; + int error = sandbox_init(kSBXProfilePureComputation, SANDBOX_NAMED, + &error_buff); + bool success = (error == 0 && error_buff == NULL); + if (!success) { + return -1; + } + + sandbox_free_error(error_buff); + + // Make sure Sandbox is really enabled. + if (open(kDevZeroPath, O_RDONLY) != -1) { + LOG(ERROR) << "Sandbox wasn't properly enabled"; + return -1; + } + + // See if we can receive a file descriptor. + return TestDescriptorClient(st.st_ino); +} + +// Test that FDs are correctly sent to a sandboxed process. +TEST_F(IPCChannelTest, DescriptorTestSandboxed) { + // Setup IPC channel. + MyChannelDescriptorListener listener(-1); + + IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER, + &listener); + chan.Connect(); + + base::ProcessHandle process_handle = SpawnChild( + TEST_DESCRIPTOR_CLIENT_SANDBOXED, + &chan); + TestDescriptorServer(chan, process_handle); +} +#endif // defined(OS_MACOSX) + +MULTIPROCESS_TEST_MAIN(RunTestDescriptorClient) { + struct stat st; + const int fd = open(kDevZeroPath, O_RDONLY); + fstat(fd, &st); + HANDLE_EINTR(close(fd)); + + return TestDescriptorClient(st.st_ino); +} + +TEST_F(IPCChannelTest, DescriptorTest) { + // Setup IPC channel. + MyChannelDescriptorListener listener(-1); + + IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER, + &listener); + chan.Connect(); + + base::ProcessHandle process_handle = SpawnChild(TEST_DESCRIPTOR_CLIENT, + &chan); + TestDescriptorServer(chan, process_handle); +} + +#endif // defined(OS_POSIX) diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_channel.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_channel.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_channel.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_channel.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,453 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/ipc_sync_channel.h" + +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "base/thread_local.h" +#include "base/message_loop.h" +#include "base/waitable_event.h" +#include "base/waitable_event_watcher.h" +#include "chrome/common/ipc_sync_message.h" + +using base::TimeDelta; +using base::TimeTicks; +using base::WaitableEvent; + +namespace IPC { +// When we're blocked in a Send(), we need to process incoming synchronous +// messages right away because it could be blocking our reply (either +// directly from the same object we're calling, or indirectly through one or +// more other channels). That means that in SyncContext's OnMessageReceived, +// we need to process sync message right away if we're blocked. However a +// simple check isn't sufficient, because the listener thread can be in the +// process of calling Send. +// To work around this, when SyncChannel filters a sync message, it sets +// an event that the listener thread waits on during its Send() call. This +// allows us to dispatch incoming sync messages when blocked. The race +// condition is handled because if Send is in the process of being called, it +// will check the event. In case the listener thread isn't sending a message, +// we queue a task on the listener thread to dispatch the received messages. +// The messages are stored in this queue object that's shared among all +// SyncChannel objects on the same thread (since one object can receive a +// sync message while another one is blocked). + +class SyncChannel::ReceivedSyncMsgQueue : + public base::RefCountedThreadSafe { + public: + // Returns the ReceivedSyncMsgQueue instance for this thread, creating one + // if necessary. Call RemoveContext on the same thread when done. + static ReceivedSyncMsgQueue* AddContext() { + // We want one ReceivedSyncMsgQueue per listener thread (i.e. since multiple + // SyncChannel objects can block the same thread). + ReceivedSyncMsgQueue* rv = lazy_tls_ptr_.Pointer()->Get(); + if (!rv) { + rv = new ReceivedSyncMsgQueue(); + ReceivedSyncMsgQueue::lazy_tls_ptr_.Pointer()->Set(rv); + } + rv->listener_count_++; + return rv; + } + + ~ReceivedSyncMsgQueue() { + } + + // Called on IPC thread when a synchronous message or reply arrives. + void QueueMessage(const Message& msg, SyncChannel::SyncContext* context) { + bool was_task_pending; + { + AutoLock auto_lock(message_lock_); + + was_task_pending = task_pending_; + task_pending_ = true; + + // We set the event in case the listener thread is blocked (or is about + // to). In case it's not, the PostTask dispatches the messages. + message_queue_.push_back(QueuedMessage(new Message(msg), context)); + } + + dispatch_event_.Signal(); + if (!was_task_pending) { + listener_message_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &ReceivedSyncMsgQueue::DispatchMessagesTask)); + } + } + + void QueueReply(const Message &msg, SyncChannel::SyncContext* context) { + received_replies_.push_back(QueuedMessage(new Message(msg), context)); + } + + // Called on the listener's thread to process any queues synchronous + // messages. + void DispatchMessagesTask() { + { + AutoLock auto_lock(message_lock_); + task_pending_ = false; + } + DispatchMessages(); + } + + void DispatchMessages() { + while (true) { + Message* message; + scoped_refptr context; + { + AutoLock auto_lock(message_lock_); + if (message_queue_.empty()) + break; + + message = message_queue_.front().message; + context = message_queue_.front().context; + message_queue_.pop_front(); + } + + context->OnDispatchMessage(*message); + delete message; + } + } + + // SyncChannel calls this in its destructor. + void RemoveContext(SyncContext* context) { + AutoLock auto_lock(message_lock_); + + SyncMessageQueue::iterator iter = message_queue_.begin(); + while (iter != message_queue_.end()) { + if (iter->context == context) { + delete iter->message; + iter = message_queue_.erase(iter); + } else { + iter++; + } + } + + if (--listener_count_ == 0) { + DCHECK(lazy_tls_ptr_.Pointer()->Get()); + lazy_tls_ptr_.Pointer()->Set(NULL); + } + } + + WaitableEvent* dispatch_event() { return &dispatch_event_; } + MessageLoop* listener_message_loop() { return listener_message_loop_; } + + // Holds a pointer to the per-thread ReceivedSyncMsgQueue object. + static base::LazyInstance > + lazy_tls_ptr_; + + // Called on the ipc thread to check if we can unblock any current Send() + // calls based on a queued reply. + void DispatchReplies() { + for (size_t i = 0; i < received_replies_.size(); ++i) { + Message* message = received_replies_[i].message; + if (received_replies_[i].context->TryToUnblockListener(message)) { + delete message; + received_replies_.erase(received_replies_.begin() + i); + return; + } + } + } + + private: + // See the comment in SyncChannel::SyncChannel for why this event is created + // as manual reset. + ReceivedSyncMsgQueue() : + dispatch_event_(true, false), + listener_message_loop_(MessageLoop::current()), + task_pending_(false), + listener_count_(0) { + } + + // Holds information about a queued synchronous message or reply. + struct QueuedMessage { + QueuedMessage(Message* m, SyncContext* c) : message(m), context(c) { } + Message* message; + scoped_refptr context; + }; + + typedef std::deque SyncMessageQueue; + SyncMessageQueue message_queue_; + + std::vector received_replies_; + + // Set when we got a synchronous message that we must respond to as the + // sender needs its reply before it can reply to our original synchronous + // message. + WaitableEvent dispatch_event_; + MessageLoop* listener_message_loop_; + Lock message_lock_; + bool task_pending_; + int listener_count_; +}; + +base::LazyInstance > + SyncChannel::ReceivedSyncMsgQueue::lazy_tls_ptr_(base::LINKER_INITIALIZED); + +SyncChannel::SyncContext::SyncContext( + Channel::Listener* listener, + MessageFilter* filter, + MessageLoop* ipc_thread, + WaitableEvent* shutdown_event) + : ChannelProxy::Context(listener, filter, ipc_thread), + received_sync_msgs_(ReceivedSyncMsgQueue::AddContext()), + shutdown_event_(shutdown_event) { +} + +SyncChannel::SyncContext::~SyncContext() { + while (!deserializers_.empty()) + Pop(); +} + +// Adds information about an outgoing sync message to the context so that +// we know how to deserialize the reply. Returns a handle that's set when +// the reply has arrived. +void SyncChannel::SyncContext::Push(SyncMessage* sync_msg) { + // The event is created as manual reset because in between Signal and + // OnObjectSignalled, another Send can happen which would stop the watcher + // from being called. The event would get watched later, when the nested + // Send completes, so the event will need to remain set. + PendingSyncMsg pending(SyncMessage::GetMessageId(*sync_msg), + sync_msg->GetReplyDeserializer(), + new WaitableEvent(true, false)); + AutoLock auto_lock(deserializers_lock_); + deserializers_.push_back(pending); +} + +bool SyncChannel::SyncContext::Pop() { + bool result; + { + AutoLock auto_lock(deserializers_lock_); + PendingSyncMsg msg = deserializers_.back(); + delete msg.deserializer; + delete msg.done_event; + msg.done_event = NULL; + deserializers_.pop_back(); + result = msg.send_result; + } + + // We got a reply to a synchronous Send() call that's blocking the listener + // thread. However, further down the call stack there could be another + // blocking Send() call, whose reply we received after we made this last + // Send() call. So check if we have any queued replies available that + // can now unblock the listener thread. + ipc_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + received_sync_msgs_.get(), &ReceivedSyncMsgQueue::DispatchReplies)); + + return result; +} + +WaitableEvent* SyncChannel::SyncContext::GetSendDoneEvent() { + AutoLock auto_lock(deserializers_lock_); + return deserializers_.back().done_event; +} + +WaitableEvent* SyncChannel::SyncContext::GetDispatchEvent() { + return received_sync_msgs_->dispatch_event(); +} + +void SyncChannel::SyncContext::DispatchMessages() { + received_sync_msgs_->DispatchMessages(); +} + +bool SyncChannel::SyncContext::TryToUnblockListener(const Message* msg) { + AutoLock auto_lock(deserializers_lock_); + if (deserializers_.empty() || + !SyncMessage::IsMessageReplyTo(*msg, deserializers_.back().id)) { + return false; + } + + if (!msg->is_reply_error()) { + deserializers_.back().send_result = deserializers_.back().deserializer-> + SerializeOutputParameters(*msg); + } + deserializers_.back().done_event->Signal(); + + return true; +} + +void SyncChannel::SyncContext::Clear() { + CancelPendingSends(); + received_sync_msgs_->RemoveContext(this); + + Context::Clear(); +} + +void SyncChannel::SyncContext::OnMessageReceived(const Message& msg) { + // Give the filters a chance at processing this message. + if (TryFilters(msg)) + return; + + if (TryToUnblockListener(&msg)) + return; + + if (msg.should_unblock()) { + received_sync_msgs_->QueueMessage(msg, this); + return; + } + + if (msg.is_reply()) { + received_sync_msgs_->QueueReply(msg, this); + return; + } + + return Context::OnMessageReceivedNoFilter(msg); +} + +void SyncChannel::SyncContext::OnChannelError() { + CancelPendingSends(); + shutdown_watcher_.StopWatching(); + Context::OnChannelError(); +} + +void SyncChannel::SyncContext::OnChannelOpened() { + shutdown_watcher_.StartWatching(shutdown_event_, this); + Context::OnChannelOpened(); +} + +void SyncChannel::SyncContext::OnChannelClosed() { + shutdown_watcher_.StopWatching(); + Context::OnChannelClosed(); +} + +void SyncChannel::SyncContext::OnSendTimeout(int message_id) { + AutoLock auto_lock(deserializers_lock_); + PendingSyncMessageQueue::iterator iter; + for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++) { + if (iter->id == message_id) { + iter->done_event->Signal(); + break; + } + } +} + +void SyncChannel::SyncContext::CancelPendingSends() { + AutoLock auto_lock(deserializers_lock_); + PendingSyncMessageQueue::iterator iter; + for (iter = deserializers_.begin(); iter != deserializers_.end(); iter++) + iter->done_event->Signal(); +} + +void SyncChannel::SyncContext::OnWaitableEventSignaled(WaitableEvent* event) { + DCHECK(event == shutdown_event_); + // Process shut down before we can get a reply to a synchronous message. + // Cancel pending Send calls, which will end up setting the send done event. + CancelPendingSends(); +} + + +SyncChannel::SyncChannel( + const std::wstring& channel_id, Channel::Mode mode, + Channel::Listener* listener, MessageFilter* filter, + MessageLoop* ipc_message_loop, bool create_pipe_now, + WaitableEvent* shutdown_event) + : ChannelProxy( + channel_id, mode, ipc_message_loop, + new SyncContext(listener, filter, ipc_message_loop, shutdown_event), + create_pipe_now), + sync_messages_with_no_timeout_allowed_(true) { + // Ideally we only want to watch this object when running a nested message + // loop. However, we don't know when it exits if there's another nested + // message loop running under it or not, so we wouldn't know whether to + // stop or keep watching. So we always watch it, and create the event as + // manual reset since the object watcher might otherwise reset the event + // when we're doing a WaitMany. + dispatch_watcher_.StartWatching(sync_context()->GetDispatchEvent(), this); +} + +SyncChannel::~SyncChannel() { +} + +bool SyncChannel::Send(Message* message) { + return SendWithTimeout(message, base::kNoTimeout); +} + +bool SyncChannel::SendWithTimeout(Message* message, int timeout_ms) { + if (!message->is_sync()) { + ChannelProxy::Send(message); + return true; + } + + // *this* might get deleted in WaitForReply. + scoped_refptr context(sync_context()); + if (context->shutdown_event()->IsSignaled()) { + delete message; + return false; + } + + DCHECK(sync_messages_with_no_timeout_allowed_ || + timeout_ms != base::kNoTimeout); + SyncMessage* sync_msg = static_cast(message); + context->Push(sync_msg); + int message_id = SyncMessage::GetMessageId(*sync_msg); + WaitableEvent* pump_messages_event = sync_msg->pump_messages_event(); + + ChannelProxy::Send(message); + + if (timeout_ms != base::kNoTimeout) { + // We use the sync message id so that when a message times out, we don't + // confuse it with another send that is either above/below this Send in + // the call stack. + context->ipc_message_loop()->PostDelayedTask(FROM_HERE, + NewRunnableMethod(context.get(), + &SyncContext::OnSendTimeout, message_id), timeout_ms); + } + + // Wait for reply, or for any other incoming synchronous messages. + WaitForReply(pump_messages_event); + + return context->Pop(); +} + +void SyncChannel::WaitForReply(WaitableEvent* pump_messages_event) { + while (true) { + WaitableEvent* objects[] = { + sync_context()->GetDispatchEvent(), + sync_context()->GetSendDoneEvent(), + pump_messages_event + }; + + unsigned count = pump_messages_event ? 3: 2; + unsigned result = WaitableEvent::WaitMany(objects, count); + if (result == 0 /* dispatch event */) { + // We're waiting for a reply, but we received a blocking synchronous + // call. We must process it or otherwise a deadlock might occur. + sync_context()->GetDispatchEvent()->Reset(); + sync_context()->DispatchMessages(); + continue; + } + + if (result == 2 /* pump_messages_event */) + WaitForReplyWithNestedMessageLoop(); // Start a nested message loop. + + break; + } +} + +void SyncChannel::WaitForReplyWithNestedMessageLoop() { + WaitableEvent* old_done_event = send_done_watcher_.GetWatchedEvent(); + send_done_watcher_.StopWatching(); + send_done_watcher_.StartWatching(sync_context()->GetSendDoneEvent(), this); + bool old_state = MessageLoop::current()->NestableTasksAllowed(); + MessageLoop::current()->SetNestableTasksAllowed(true); + MessageLoop::current()->Run(); + MessageLoop::current()->SetNestableTasksAllowed(old_state); + if (old_done_event) + send_done_watcher_.StartWatching(old_done_event, this); +} + +void SyncChannel::OnWaitableEventSignaled(WaitableEvent* event) { + WaitableEvent* dispatch_event = sync_context()->GetDispatchEvent(); + if (event == dispatch_event) { + // The call to DispatchMessages might delete this object, so reregister + // the object watcher first. + dispatch_event->Reset(); + dispatch_watcher_.StartWatching(dispatch_event, this); + sync_context()->DispatchMessages(); + } else { + // We got the reply, timed out or the process shutdown. + DCHECK(event == sync_context()->GetSendDoneEvent()); + MessageLoop::current()->Quit(); + } +} + +} // namespace IPC diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_channel.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_channel.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_channel.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_channel.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,159 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_IPC_SYNC_SENDER_H__ +#define CHROME_COMMON_IPC_SYNC_SENDER_H__ + +#include +#include +#include "base/basictypes.h" +#include "base/lock.h" +#include "base/ref_counted.h" +#include "base/scoped_handle.h" +#include "base/waitable_event.h" +#include "base/waitable_event_watcher.h" +#include "chrome/common/ipc_channel_proxy.h" + +namespace IPC { + +class SyncMessage; +class MessageReplyDeserializer; + +// This is similar to IPC::ChannelProxy, with the added feature of supporting +// sending synchronous messages. +// Note that care must be taken that the lifetime of the ipc_thread argument +// is more than this object. If the message loop goes away while this object +// is running and it's used to send a message, then it will use the invalid +// message loop pointer to proxy it to the ipc thread. +class SyncChannel : public ChannelProxy, + public base::WaitableEventWatcher::Delegate { + public: + SyncChannel(const std::wstring& channel_id, Channel::Mode mode, + Channel::Listener* listener, MessageFilter* filter, + MessageLoop* ipc_message_loop, bool create_pipe_now, + base::WaitableEvent* shutdown_event); + ~SyncChannel(); + + virtual bool Send(Message* message); + virtual bool SendWithTimeout(Message* message, int timeout_ms); + + // Whether we allow sending messages with no time-out. + void set_sync_messages_with_no_timeout_allowed(bool value) { + sync_messages_with_no_timeout_allowed_ = value; + } + + protected: + class ReceivedSyncMsgQueue; + friend class ReceivedSyncMsgQueue; + + // SyncContext holds the per object data for SyncChannel, so that SyncChannel + // can be deleted while it's being used in a different thread. See + // ChannelProxy::Context for more information. + class SyncContext : public Context, + public base::WaitableEventWatcher::Delegate { + public: + SyncContext(Channel::Listener* listener, + MessageFilter* filter, + MessageLoop* ipc_thread, + base::WaitableEvent* shutdown_event); + + ~SyncContext(); + + // Adds information about an outgoing sync message to the context so that + // we know how to deserialize the reply. + void Push(IPC::SyncMessage* sync_msg); + + // Cleanly remove the top deserializer (and throw it away). Returns the + // result of the Send call for that message. + bool Pop(); + + // Returns an event that's set when the send is complete, timed out or the + // process shut down. + base::WaitableEvent* GetSendDoneEvent(); + + // Returns an event that's set when an incoming message that's not the reply + // needs to get dispatched (by calling SyncContext::DispatchMessages). + base::WaitableEvent* GetDispatchEvent(); + + void DispatchMessages(); + + // Checks if the given message is blocking the listener thread because of a + // synchronous send. If it is, the thread is unblocked and true is + // returned. Otherwise the function returns false. + bool TryToUnblockListener(const Message* msg); + + // Called on the IPC thread when a sync send that runs a nested message loop + // times out. + void OnSendTimeout(int message_id); + + base::WaitableEvent* shutdown_event() { return shutdown_event_; } + + private: + // IPC::ChannelProxy methods that we override. + + // Called on the listener thread. + virtual void Clear(); + + // Called on the IPC thread. + virtual void OnMessageReceived(const Message& msg); + virtual void OnChannelError(); + virtual void OnChannelOpened(); + virtual void OnChannelClosed(); + + // Cancels all pending Send calls. + void CancelPendingSends(); + + // WaitableEventWatcher::Delegate implementation. + virtual void OnWaitableEventSignaled(base::WaitableEvent* arg); + + // When sending a synchronous message, this structure contains an object + // that knows how to deserialize the response. + struct PendingSyncMsg { + PendingSyncMsg(int id, IPC::MessageReplyDeserializer* d, + base::WaitableEvent* e) : + id(id), deserializer(d), done_event(e), send_result(false) { } + int id; + IPC::MessageReplyDeserializer* deserializer; + base::WaitableEvent* done_event; + bool send_result; + }; + + typedef std::deque PendingSyncMessageQueue; + PendingSyncMessageQueue deserializers_; + Lock deserializers_lock_; + + scoped_refptr received_sync_msgs_; + + base::WaitableEvent* shutdown_event_; + base::WaitableEventWatcher shutdown_watcher_; + }; + + private: + // WaitableEventWatcher::Delegate implementation. + virtual void OnWaitableEventSignaled(base::WaitableEvent* arg); + + SyncContext* sync_context() { + return reinterpret_cast(context()); + } + + // Both these functions wait for a reply, timeout or process shutdown. The + // latter one also runs a nested message loop in the meantime. + void WaitForReply(base::WaitableEvent* pump_messages_event); + + // Runs a nested message loop until a reply arrives, times out, or the process + // shuts down. + void WaitForReplyWithNestedMessageLoop(); + + bool sync_messages_with_no_timeout_allowed_; + + // Used to signal events between the IPC and listener threads. + base::WaitableEventWatcher send_done_watcher_; + base::WaitableEventWatcher dispatch_watcher_; + + DISALLOW_EVIL_CONSTRUCTORS(SyncChannel); +}; + +} // namespace IPC + +#endif // CHROME_COMMON_IPC_SYNC_SENDER_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_channel_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_channel_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_channel_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_channel_unittest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,1009 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Unit test for SyncChannel. + +#include +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/platform_thread.h" +#include "base/stl_util-inl.h" +#include "base/string_util.h" +#include "base/thread.h" +#include "base/waitable_event.h" +#include "chrome/common/ipc_message.h" +#include "chrome/common/ipc_sync_channel.h" +#include "testing/gtest/include/gtest/gtest.h" + + +#define MESSAGES_INTERNAL_FILE "chrome/common/ipc_sync_message_unittest.h" +#include "chrome/common/ipc_message_macros.h" + +using namespace IPC; +using base::WaitableEvent; + +namespace { + +// Base class for a "process" with listener and IPC threads. +class Worker : public Channel::Listener, public Message::Sender { + public: + // Will create a channel without a name. + Worker(Channel::Mode mode, const std::string& thread_name) + : done_(new WaitableEvent(false, false)), + channel_created_(new WaitableEvent(false, false)), + mode_(mode), + ipc_thread_((thread_name + "_ipc").c_str()), + listener_thread_((thread_name + "_listener").c_str()), + overrided_thread_(NULL), + shutdown_event_(true, false) { } + + // Will create a named channel and use this name for the threads' name. + Worker(const std::wstring& channel_name, Channel::Mode mode) + : done_(new WaitableEvent(false, false)), + channel_created_(new WaitableEvent(false, false)), + channel_name_(channel_name), + mode_(mode), + ipc_thread_((WideToUTF8(channel_name) + "_ipc").c_str()), + listener_thread_((WideToUTF8(channel_name) + "_listener").c_str()), + overrided_thread_(NULL), + shutdown_event_(true, false) { } + + // The IPC thread needs to outlive SyncChannel, so force the correct order of + // destruction. + virtual ~Worker() { + WaitableEvent listener_done(false, false), ipc_done(false, false); + ListenerThread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &Worker::OnListenerThreadShutdown1, &listener_done, + &ipc_done)); + listener_done.Wait(); + ipc_done.Wait(); + ipc_thread_.Stop(); + listener_thread_.Stop(); + } + void AddRef() { } + void Release() { } + bool Send(Message* msg) { return channel_->Send(msg); } + bool SendWithTimeout(Message* msg, int timeout_ms) { + return channel_->SendWithTimeout(msg, timeout_ms); + } + void WaitForChannelCreation() { channel_created_->Wait(); } + void CloseChannel() { + DCHECK(MessageLoop::current() == ListenerThread()->message_loop()); + channel_->Close(); + } + void Start() { + StartThread(&listener_thread_, MessageLoop::TYPE_DEFAULT); + ListenerThread()->message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &Worker::OnStart)); + } + void OverrideThread(base::Thread* overrided_thread) { + DCHECK(overrided_thread_ == NULL); + overrided_thread_ = overrided_thread; + } + bool SendAnswerToLife(bool pump, int timeout, bool succeed) { + int answer = 0; + SyncMessage* msg = new SyncChannelTestMsg_AnswerToLife(&answer); + if (pump) + msg->EnableMessagePumping(); + bool result = SendWithTimeout(msg, timeout); + DCHECK(result == succeed); + DCHECK(answer == (succeed ? 42 : 0)); + return result; + } + bool SendDouble(bool pump, bool succeed) { + int answer = 0; + SyncMessage* msg = new SyncChannelTestMsg_Double(5, &answer); + if (pump) + msg->EnableMessagePumping(); + bool result = Send(msg); + DCHECK(result == succeed); + DCHECK(answer == (succeed ? 10 : 0)); + return result; + } + Channel::Mode mode() { return mode_; } + WaitableEvent* done_event() { return done_.get(); } + + protected: + // Derived classes need to call this when they've completed their part of + // the test. + void Done() { done_->Signal(); } + // Functions for dervied classes to implement if they wish. + virtual void Run() { } + virtual void OnAnswer(int* answer) { NOTREACHED(); } + virtual void OnAnswerDelay(Message* reply_msg) { + // The message handler map below can only take one entry for + // SyncChannelTestMsg_AnswerToLife, so since some classes want + // the normal version while other want the delayed reply, we + // call the normal version if the derived class didn't override + // this function. + int answer; + OnAnswer(&answer); + SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, answer); + Send(reply_msg); + } + virtual void OnDouble(int in, int* out) { NOTREACHED(); } + virtual void OnDoubleDelay(int in, Message* reply_msg) { + int result; + OnDouble(in, &result); + SyncChannelTestMsg_Double::WriteReplyParams(reply_msg, result); + Send(reply_msg); + } + + private: + base::Thread* ListenerThread() { + return overrided_thread_ ? overrided_thread_ : &listener_thread_; + } + // Called on the listener thread to create the sync channel. + void OnStart() { + // Link ipc_thread_, listener_thread_ and channel_ altogether. + StartThread(&ipc_thread_, MessageLoop::TYPE_IO); + channel_.reset(new SyncChannel( + channel_name_, mode_, this, NULL, ipc_thread_.message_loop(), true, + &shutdown_event_)); + channel_created_->Signal(); + Run(); + } + + void OnListenerThreadShutdown1(WaitableEvent* listener_event, + WaitableEvent* ipc_event) { + // SyncChannel needs to be destructed on the thread that it was created on. + channel_.reset(); + + MessageLoop::current()->RunAllPending(); + + ipc_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &Worker::OnIPCThreadShutdown, listener_event, ipc_event)); + } + + void OnIPCThreadShutdown(WaitableEvent* listener_event, + WaitableEvent* ipc_event) { + MessageLoop::current()->RunAllPending(); + ipc_event->Signal(); + + listener_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &Worker::OnListenerThreadShutdown2, listener_event)); + } + + void OnListenerThreadShutdown2(WaitableEvent* listener_event) { + MessageLoop::current()->RunAllPending(); + listener_event->Signal(); + } + + void OnMessageReceived(const Message& message) { + IPC_BEGIN_MESSAGE_MAP(Worker, message) + IPC_MESSAGE_HANDLER_DELAY_REPLY(SyncChannelTestMsg_Double, OnDoubleDelay) + IPC_MESSAGE_HANDLER_DELAY_REPLY(SyncChannelTestMsg_AnswerToLife, + OnAnswerDelay) + IPC_END_MESSAGE_MAP() + } + + void StartThread(base::Thread* thread, MessageLoop::Type type) { + base::Thread::Options options; + options.message_loop_type = type; + thread->StartWithOptions(options); + } + + scoped_ptr done_; + scoped_ptr channel_created_; + std::wstring channel_name_; + Channel::Mode mode_; + scoped_ptr channel_; + base::Thread ipc_thread_; + base::Thread listener_thread_; + base::Thread* overrided_thread_; + + base::WaitableEvent shutdown_event_; + + DISALLOW_EVIL_CONSTRUCTORS(Worker); +}; + + +// Starts the test with the given workers. This function deletes the workers +// when it's done. +void RunTest(std::vector workers) { + // First we create the workers that are channel servers, or else the other + // workers' channel initialization might fail because the pipe isn't created.. + for (size_t i = 0; i < workers.size(); ++i) { + if (workers[i]->mode() == Channel::MODE_SERVER) { + workers[i]->Start(); + workers[i]->WaitForChannelCreation(); + } + } + + // now create the clients + for (size_t i = 0; i < workers.size(); ++i) { + if (workers[i]->mode() == Channel::MODE_CLIENT) + workers[i]->Start(); + } + + // wait for all the workers to finish + for (size_t i = 0; i < workers.size(); ++i) + workers[i]->done_event()->Wait(); + + STLDeleteContainerPointers(workers.begin(), workers.end()); +} + +} // namespace + +class IPCSyncChannelTest : public testing::Test { + private: + MessageLoop message_loop_; +}; + +//----------------------------------------------------------------------------- + +namespace { + +class SimpleServer : public Worker { + public: + SimpleServer(bool pump_during_send) + : Worker(Channel::MODE_SERVER, "simpler_server"), + pump_during_send_(pump_during_send) { } + void Run() { + SendAnswerToLife(pump_during_send_, base::kNoTimeout, true); + Done(); + } + + bool pump_during_send_; +}; + +class SimpleClient : public Worker { + public: + SimpleClient() : Worker(Channel::MODE_CLIENT, "simple_client") { } + + void OnAnswer(int* answer) { + *answer = 42; + Done(); + } +}; + +void Simple(bool pump_during_send) { + std::vector workers; + workers.push_back(new SimpleServer(pump_during_send)); + workers.push_back(new SimpleClient()); + RunTest(workers); +} + +} // namespace + +// Tests basic synchronous call +TEST_F(IPCSyncChannelTest, Simple) { + Simple(false); + Simple(true); +} + +//----------------------------------------------------------------------------- + +namespace { + +class DelayClient : public Worker { + public: + DelayClient() : Worker(Channel::MODE_CLIENT, "delay_client") { } + + void OnAnswerDelay(Message* reply_msg) { + SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42); + Send(reply_msg); + Done(); + } +}; + +void DelayReply(bool pump_during_send) { + std::vector workers; + workers.push_back(new SimpleServer(pump_during_send)); + workers.push_back(new DelayClient()); + RunTest(workers); +} + +} // namespace + +// Tests that asynchronous replies work +TEST_F(IPCSyncChannelTest, DelayReply) { + DelayReply(false); + DelayReply(true); +} + +//----------------------------------------------------------------------------- + +namespace { + +class NoHangServer : public Worker { + public: + explicit NoHangServer(WaitableEvent* got_first_reply, bool pump_during_send) + : Worker(Channel::MODE_SERVER, "no_hang_server"), + got_first_reply_(got_first_reply), + pump_during_send_(pump_during_send) { } + void Run() { + SendAnswerToLife(pump_during_send_, base::kNoTimeout, true); + got_first_reply_->Signal(); + + SendAnswerToLife(pump_during_send_, base::kNoTimeout, false); + Done(); + } + + WaitableEvent* got_first_reply_; + bool pump_during_send_; +}; + +class NoHangClient : public Worker { + public: + explicit NoHangClient(WaitableEvent* got_first_reply) + : Worker(Channel::MODE_CLIENT, "no_hang_client"), + got_first_reply_(got_first_reply) { } + + virtual void OnAnswerDelay(Message* reply_msg) { + // Use the DELAY_REPLY macro so that we can force the reply to be sent + // before this function returns (when the channel will be reset). + SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42); + Send(reply_msg); + got_first_reply_->Wait(); + CloseChannel(); + Done(); + } + + WaitableEvent* got_first_reply_; +}; + +void NoHang(bool pump_during_send) { + WaitableEvent got_first_reply(false, false); + std::vector workers; + workers.push_back(new NoHangServer(&got_first_reply, pump_during_send)); + workers.push_back(new NoHangClient(&got_first_reply)); + RunTest(workers); +} + +} // namespace + +// Tests that caller doesn't hang if receiver dies +TEST_F(IPCSyncChannelTest, NoHang) { + NoHang(false); + NoHang(true); +} + +//----------------------------------------------------------------------------- + +namespace { + +class UnblockServer : public Worker { + public: + UnblockServer(bool pump_during_send) + : Worker(Channel::MODE_SERVER, "unblock_server"), + pump_during_send_(pump_during_send) { } + void Run() { + SendAnswerToLife(pump_during_send_, base::kNoTimeout, true); + Done(); + } + + void OnDouble(int in, int* out) { + *out = in * 2; + } + + bool pump_during_send_; +}; + +class UnblockClient : public Worker { + public: + UnblockClient(bool pump_during_send) + : Worker(Channel::MODE_CLIENT, "unblock_client"), + pump_during_send_(pump_during_send) { } + + void OnAnswer(int* answer) { + SendDouble(pump_during_send_, true); + *answer = 42; + Done(); + } + + bool pump_during_send_; +}; + +void Unblock(bool server_pump, bool client_pump) { + std::vector workers; + workers.push_back(new UnblockServer(server_pump)); + workers.push_back(new UnblockClient(client_pump)); + RunTest(workers); +} + +} // namespace + +// Tests that the caller unblocks to answer a sync message from the receiver. +TEST_F(IPCSyncChannelTest, Unblock) { + Unblock(false, false); + Unblock(false, true); + Unblock(true, false); + Unblock(true, true); +} + +//----------------------------------------------------------------------------- + +namespace { + +class RecursiveServer : public Worker { + public: + explicit RecursiveServer( + bool expected_send_result, bool pump_first, bool pump_second) + : Worker(Channel::MODE_SERVER, "recursive_server"), + expected_send_result_(expected_send_result), + pump_first_(pump_first), pump_second_(pump_second) { } + void Run() { + SendDouble(pump_first_, expected_send_result_); + Done(); + } + + void OnDouble(int in, int* out) { + *out = in * 2; + SendAnswerToLife(pump_second_, base::kNoTimeout, expected_send_result_); + } + + bool expected_send_result_, pump_first_, pump_second_; +}; + +class RecursiveClient : public Worker { + public: + explicit RecursiveClient(bool pump_during_send, bool close_channel) + : Worker(Channel::MODE_CLIENT, "recursive_client"), + pump_during_send_(pump_during_send), close_channel_(close_channel) { } + + void OnDoubleDelay(int in, Message* reply_msg) { + SendDouble(pump_during_send_, !close_channel_); + if (close_channel_) { + delete reply_msg; + } else { + SyncChannelTestMsg_Double::WriteReplyParams(reply_msg, in * 2); + Send(reply_msg); + } + Done(); + } + + void OnAnswerDelay(Message* reply_msg) { + if (close_channel_) { + delete reply_msg; + CloseChannel(); + } else { + SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42); + Send(reply_msg); + } + } + + bool pump_during_send_, close_channel_; +}; + +void Recursive( + bool server_pump_first, bool server_pump_second, bool client_pump) { + std::vector workers; + workers.push_back( + new RecursiveServer(true, server_pump_first, server_pump_second)); + workers.push_back(new RecursiveClient(client_pump, false)); + RunTest(workers); +} + +} // namespace + +// Tests a server calling Send while another Send is pending. +TEST_F(IPCSyncChannelTest, Recursive) { + Recursive(false, false, false); + Recursive(false, false, true); + Recursive(false, true, false); + Recursive(false, true, true); + Recursive(true, false, false); + Recursive(true, false, true); + Recursive(true, true, false); + Recursive(true, true, true); +} + +//----------------------------------------------------------------------------- + +namespace { + +void RecursiveNoHang( + bool server_pump_first, bool server_pump_second, bool client_pump) { + std::vector workers; + workers.push_back( + new RecursiveServer(false, server_pump_first, server_pump_second)); + workers.push_back(new RecursiveClient(client_pump, true)); + RunTest(workers); +} + +} // namespace + +// Tests that if a caller makes a sync call during an existing sync call and +// the receiver dies, neither of the Send() calls hang. +TEST_F(IPCSyncChannelTest, RecursiveNoHang) { + RecursiveNoHang(false, false, false); + RecursiveNoHang(false, false, true); + RecursiveNoHang(false, true, false); + RecursiveNoHang(false, true, true); + RecursiveNoHang(true, false, false); + RecursiveNoHang(true, false, true); + RecursiveNoHang(true, true, false); + RecursiveNoHang(true, true, true); +} + +//----------------------------------------------------------------------------- + +namespace { + +class MultipleServer1 : public Worker { + public: + MultipleServer1(bool pump_during_send) + : Worker(L"test_channel1", Channel::MODE_SERVER), + pump_during_send_(pump_during_send) { } + + void Run() { + SendDouble(pump_during_send_, true); + Done(); + } + + bool pump_during_send_; +}; + +class MultipleClient1 : public Worker { + public: + MultipleClient1(WaitableEvent* client1_msg_received, + WaitableEvent* client1_can_reply) : + Worker(L"test_channel1", Channel::MODE_CLIENT), + client1_msg_received_(client1_msg_received), + client1_can_reply_(client1_can_reply) { } + + void OnDouble(int in, int* out) { + client1_msg_received_->Signal(); + *out = in * 2; + client1_can_reply_->Wait(); + Done(); + } + + private: + WaitableEvent *client1_msg_received_, *client1_can_reply_; +}; + +class MultipleServer2 : public Worker { + public: + MultipleServer2() : Worker(L"test_channel2", Channel::MODE_SERVER) { } + + void OnAnswer(int* result) { + *result = 42; + Done(); + } +}; + +class MultipleClient2 : public Worker { + public: + MultipleClient2( + WaitableEvent* client1_msg_received, WaitableEvent* client1_can_reply, + bool pump_during_send) + : Worker(L"test_channel2", Channel::MODE_CLIENT), + client1_msg_received_(client1_msg_received), + client1_can_reply_(client1_can_reply), + pump_during_send_(pump_during_send) { } + + void Run() { + client1_msg_received_->Wait(); + SendAnswerToLife(pump_during_send_, base::kNoTimeout, true); + client1_can_reply_->Signal(); + Done(); + } + + private: + WaitableEvent *client1_msg_received_, *client1_can_reply_; + bool pump_during_send_; +}; + +void Multiple(bool server_pump, bool client_pump) { + std::vector workers; + + // A shared worker thread so that server1 and server2 run on one thread. + base::Thread worker_thread("Multiple"); + worker_thread.Start(); + + // Server1 sends a sync msg to client1, which blocks the reply until + // server2 (which runs on the same worker thread as server1) responds + // to a sync msg from client2. + WaitableEvent client1_msg_received(false, false); + WaitableEvent client1_can_reply(false, false); + + Worker* worker; + + worker = new MultipleServer2(); + worker->OverrideThread(&worker_thread); + workers.push_back(worker); + + worker = new MultipleClient2( + &client1_msg_received, &client1_can_reply, client_pump); + workers.push_back(worker); + + worker = new MultipleServer1(server_pump); + worker->OverrideThread(&worker_thread); + workers.push_back(worker); + + worker = new MultipleClient1( + &client1_msg_received, &client1_can_reply); + workers.push_back(worker); + + RunTest(workers); +} + +} // namespace + +// Tests that multiple SyncObjects on the same listener thread can unblock each +// other. +TEST_F(IPCSyncChannelTest, Multiple) { + Multiple(false, false); + Multiple(false, true); + Multiple(true, false); + Multiple(true, true); +} + +//----------------------------------------------------------------------------- + +namespace { + +class QueuedReplyServer1 : public Worker { + public: + QueuedReplyServer1(bool pump_during_send) + : Worker(L"test_channel1", Channel::MODE_SERVER), + pump_during_send_(pump_during_send) { } + void Run() { + SendDouble(pump_during_send_, true); + Done(); + } + + bool pump_during_send_; +}; + +class QueuedReplyClient1 : public Worker { + public: + QueuedReplyClient1(WaitableEvent* client1_msg_received, + WaitableEvent* server2_can_reply) : + Worker(L"test_channel1", Channel::MODE_CLIENT), + client1_msg_received_(client1_msg_received), + server2_can_reply_(server2_can_reply) { } + + void OnDouble(int in, int* out) { + client1_msg_received_->Signal(); + *out = in * 2; + server2_can_reply_->Wait(); + Done(); + } + + private: + WaitableEvent *client1_msg_received_, *server2_can_reply_; +}; + +class QueuedReplyServer2 : public Worker { + public: + explicit QueuedReplyServer2(WaitableEvent* server2_can_reply) : + Worker(L"test_channel2", Channel::MODE_SERVER), + server2_can_reply_(server2_can_reply) { } + + void OnAnswer(int* result) { + server2_can_reply_->Signal(); + + // give client1's reply time to reach the server listener thread + PlatformThread::Sleep(200); + + *result = 42; + Done(); + } + + WaitableEvent *server2_can_reply_; +}; + +class QueuedReplyClient2 : public Worker { + public: + explicit QueuedReplyClient2( + WaitableEvent* client1_msg_received, bool pump_during_send) + : Worker(L"test_channel2", Channel::MODE_CLIENT), + client1_msg_received_(client1_msg_received), + pump_during_send_(pump_during_send){ } + + void Run() { + client1_msg_received_->Wait(); + SendAnswerToLife(pump_during_send_, base::kNoTimeout, true); + Done(); + } + + WaitableEvent *client1_msg_received_; + bool pump_during_send_; +}; + +void QueuedReply(bool server_pump, bool client_pump) { + std::vector workers; + + // A shared worker thread so that server1 and server2 run on one thread. + base::Thread worker_thread("QueuedReply"); + worker_thread.Start(); + + WaitableEvent client1_msg_received(false, false); + WaitableEvent server2_can_reply(false, false); + + Worker* worker; + + worker = new QueuedReplyServer2(&server2_can_reply); + worker->OverrideThread(&worker_thread); + workers.push_back(worker); + + worker = new QueuedReplyClient2(&client1_msg_received, client_pump); + workers.push_back(worker); + + worker = new QueuedReplyServer1(server_pump); + worker->OverrideThread(&worker_thread); + workers.push_back(worker); + + worker = new QueuedReplyClient1( + &client1_msg_received, &server2_can_reply); + workers.push_back(worker); + + RunTest(workers); +} + +} // namespace + +// While a blocking send is in progress, the listener thread might answer other +// synchronous messages. This tests that if during the response to another +// message the reply to the original messages comes, it is queued up correctly +// and the original Send is unblocked later. +TEST_F(IPCSyncChannelTest, QueuedReply) { + QueuedReply(false, false); + QueuedReply(false, true); + QueuedReply(true, false); + QueuedReply(true, true); +} + +//----------------------------------------------------------------------------- + +namespace { + +class BadServer : public Worker { + public: + BadServer(bool pump_during_send) + : Worker(Channel::MODE_SERVER, "simpler_server"), + pump_during_send_(pump_during_send) { } + void Run() { + int answer = 0; + + SyncMessage* msg = new SyncMessage( + MSG_ROUTING_CONTROL, SyncChannelTestMsg_Double::ID, + Message::PRIORITY_NORMAL, NULL); + if (pump_during_send_) + msg->EnableMessagePumping(); + + // Temporarily set the minimum logging very high so that the assertion + // in ipc_message_utils doesn't fire. + int log_level = logging::GetMinLogLevel(); + logging::SetMinLogLevel(kint32max); + bool result = Send(msg); + logging::SetMinLogLevel(log_level); + DCHECK(!result); + + // Need to send another message to get the client to call Done(). + result = Send(new SyncChannelTestMsg_AnswerToLife(&answer)); + DCHECK(result); + DCHECK(answer == 42); + + Done(); + } + + bool pump_during_send_; +}; + +void BadMessage(bool pump_during_send) { + std::vector workers; + workers.push_back(new BadServer(pump_during_send)); + workers.push_back(new SimpleClient()); + RunTest(workers); +} + +} // namespace + +// Tests that if a message is not serialized correctly, the Send() will fail. +TEST_F(IPCSyncChannelTest, BadMessage) { + BadMessage(false); + BadMessage(true); +} + +//----------------------------------------------------------------------------- + +namespace { + +class ChattyClient : public Worker { + public: + ChattyClient() : + Worker(Channel::MODE_CLIENT, "chatty_client") { } + + void OnAnswer(int* answer) { + // The PostMessage limit is 10k. Send 20% more than that. + const int kMessageLimit = 10000; + const int kMessagesToSend = kMessageLimit * 120 / 100; + for (int i = 0; i < kMessagesToSend; ++i) { + if (!SendDouble(false, true)) + break; + } + *answer = 42; + Done(); + } +}; + +void ChattyServer(bool pump_during_send) { + std::vector workers; + workers.push_back(new UnblockServer(pump_during_send)); + workers.push_back(new ChattyClient()); + RunTest(workers); +} + +} // namespace + +// Tests http://b/1093251 - that sending lots of sync messages while +// the receiver is waiting for a sync reply does not overflow the PostMessage +// queue. +TEST_F(IPCSyncChannelTest, ChattyServer) { + ChattyServer(false); + ChattyServer(true); +} + +//------------------------------------------------------------------------------ + +namespace { + +class TimeoutServer : public Worker { + public: + TimeoutServer(int timeout_ms, + std::vector timeout_seq, + bool pump_during_send) + : Worker(Channel::MODE_SERVER, "timeout_server"), + timeout_ms_(timeout_ms), + timeout_seq_(timeout_seq), + pump_during_send_(pump_during_send) { + } + + void Run() { + for (std::vector::const_iterator iter = timeout_seq_.begin(); + iter != timeout_seq_.end(); ++iter) { + SendAnswerToLife(pump_during_send_, timeout_ms_, !*iter); + } + Done(); + } + + private: + int timeout_ms_; + std::vector timeout_seq_; + bool pump_during_send_; +}; + +class UnresponsiveClient : public Worker { + public: + UnresponsiveClient(std::vector timeout_seq) + : Worker(Channel::MODE_CLIENT, "unresponsive_client"), + timeout_seq_(timeout_seq) { + } + + void OnAnswerDelay(Message* reply_msg) { + DCHECK(!timeout_seq_.empty()); + if (!timeout_seq_[0]) { + SyncChannelTestMsg_AnswerToLife::WriteReplyParams(reply_msg, 42); + Send(reply_msg); + } else { + // Don't reply. + delete reply_msg; + } + timeout_seq_.erase(timeout_seq_.begin()); + if (timeout_seq_.empty()) + Done(); + } + + private: + // Whether we should time-out or respond to the various messages we receive. + std::vector timeout_seq_; +}; + +void SendWithTimeoutOK(bool pump_during_send) { + std::vector workers; + std::vector timeout_seq; + timeout_seq.push_back(false); + timeout_seq.push_back(false); + timeout_seq.push_back(false); + workers.push_back(new TimeoutServer(5000, timeout_seq, pump_during_send)); + workers.push_back(new SimpleClient()); + RunTest(workers); +} + +void SendWithTimeoutTimeout(bool pump_during_send) { + std::vector workers; + std::vector timeout_seq; + timeout_seq.push_back(true); + timeout_seq.push_back(false); + timeout_seq.push_back(false); + workers.push_back(new TimeoutServer(100, timeout_seq, pump_during_send)); + workers.push_back(new UnresponsiveClient(timeout_seq)); + RunTest(workers); +} + +void SendWithTimeoutMixedOKAndTimeout(bool pump_during_send) { + std::vector workers; + std::vector timeout_seq; + timeout_seq.push_back(true); + timeout_seq.push_back(false); + timeout_seq.push_back(false); + timeout_seq.push_back(true); + timeout_seq.push_back(false); + workers.push_back(new TimeoutServer(100, timeout_seq, pump_during_send)); + workers.push_back(new UnresponsiveClient(timeout_seq)); + RunTest(workers); +} + +} // namespace + +// Tests that SendWithTimeout does not time-out if the response comes back fast +// enough. +TEST_F(IPCSyncChannelTest, SendWithTimeoutOK) { + SendWithTimeoutOK(false); + SendWithTimeoutOK(true); +} + +// Tests that SendWithTimeout does time-out. +TEST_F(IPCSyncChannelTest, SendWithTimeoutTimeout) { + SendWithTimeoutTimeout(false); + SendWithTimeoutTimeout(true); +} + +// Sends some message that time-out and some that succeed. +TEST_F(IPCSyncChannelTest, SendWithTimeoutMixedOKAndTimeout) { + SendWithTimeoutMixedOKAndTimeout(false); + SendWithTimeoutMixedOKAndTimeout(true); +} + +//------------------------------------------------------------------------------ + +namespace { + +class NestedTask : public Task { + public: + NestedTask(Worker* server) : server_(server) { } + void Run() { + // Sleep a bit so that we wake up after the reply has been received. + PlatformThread::Sleep(250); + server_->SendAnswerToLife(true, base::kNoTimeout, true); + } + + Worker* server_; +}; + +static bool timeout_occured = false; + +class TimeoutTask : public Task { + public: + void Run() { + timeout_occured = true; + } +}; + +class DoneEventRaceServer : public Worker { + public: + DoneEventRaceServer() + : Worker(Channel::MODE_SERVER, "done_event_race_server") { } + + void Run() { + MessageLoop::current()->PostTask(FROM_HERE, new NestedTask(this)); + MessageLoop::current()->PostDelayedTask(FROM_HERE, new TimeoutTask(), 9000); + // Even though we have a timeout on the Send, it will succeed since for this + // bug, the reply message comes back and is deserialized, however the done + // event wasn't set. So we indirectly use the timeout task to notice if a + // timeout occurred. + SendAnswerToLife(true, 10000, true); + DCHECK(!timeout_occured); + Done(); + } +}; + +} // namespace + +// Tests http://b/1474092 - that if after the done_event is set but before +// OnObjectSignaled is called another message is sent out, then after its +// reply comes back OnObjectSignaled will be called for the first message. +TEST_F(IPCSyncChannelTest, DoneEventRace) { + std::vector workers; + workers.push_back(new DoneEventRaceServer()); + workers.push_back(new SimpleClient()); + RunTest(workers); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_message.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_message.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_message.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_message.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,126 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#endif +#include + +#include "base/logging.h" +#include "base/waitable_event.h" +#include "chrome/common/ipc_sync_message.h" + +namespace IPC { + +uint32 SyncMessage::next_id_ = 0; +#define kSyncMessageHeaderSize 4 + +base::WaitableEvent* dummy_event = new base::WaitableEvent(true, true); + +SyncMessage::SyncMessage( + int32 routing_id, + uint16 type, + PriorityValue priority, + MessageReplyDeserializer* deserializer) + : Message(routing_id, type, priority), + deserializer_(deserializer), + pump_messages_event_(NULL) + { + set_sync(); + set_unblock(true); + + // Add synchronous message data before the message payload. + SyncHeader header; + header.message_id = ++next_id_; + WriteSyncHeader(this, header); +} + +MessageReplyDeserializer* SyncMessage::GetReplyDeserializer() { + MessageReplyDeserializer* rv = deserializer_; + DCHECK(rv); + deserializer_ = NULL; + return rv; +} + +void SyncMessage::EnableMessagePumping() { + DCHECK(!pump_messages_event_); + set_pump_messages_event(dummy_event); +} + +bool SyncMessage::IsMessageReplyTo(const Message& msg, int request_id) { + if (!msg.is_reply()) + return false; + + return GetMessageId(msg) == request_id; +} + +void* SyncMessage::GetDataIterator(const Message* msg) { + void* iter = const_cast(msg->payload()); + UpdateIter(&iter, kSyncMessageHeaderSize); + return iter; +} + +int SyncMessage::GetMessageId(const Message& msg) { + if (!msg.is_sync() && !msg.is_reply()) + return 0; + + SyncHeader header; + if (!ReadSyncHeader(msg, &header)) + return 0; + + return header.message_id; +} + +Message* SyncMessage::GenerateReply(const Message* msg) { + DCHECK(msg->is_sync()); + + Message* reply = new Message(msg->routing_id(), IPC_REPLY_ID, + msg->priority()); + reply->set_reply(); + + SyncHeader header; + + // use the same message id, but this time reply bit is set + header.message_id = GetMessageId(*msg); + WriteSyncHeader(reply, header); + + return reply; +} + +bool SyncMessage::ReadSyncHeader(const Message& msg, SyncHeader* header) { + DCHECK(msg.is_sync() || msg.is_reply()); + + void* iter = NULL; + bool result = msg.ReadInt(&iter, &header->message_id); + if (!result) { + NOTREACHED(); + return false; + } + + return true; +} + +bool SyncMessage::WriteSyncHeader(Message* msg, const SyncHeader& header) { + DCHECK(msg->is_sync() || msg->is_reply()); + DCHECK(msg->payload_size() == 0); + bool result = msg->WriteInt(header.message_id); + if (!result) { + NOTREACHED(); + return false; + } + + // Note: if you add anything here, you need to update kSyncMessageHeaderSize. + DCHECK(kSyncMessageHeaderSize == msg->payload_size()); + + return true; +} + + +bool MessageReplyDeserializer::SerializeOutputParameters(const Message& msg) { + return SerializeOutputParameters(msg, SyncMessage::GetDataIterator(&msg)); +} + +} // namespace IPC diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_message.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_message.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_message.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_message.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,96 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_IPC_SYNC_MESSAGE_H__ +#define CHROME_COMMON_IPC_SYNC_MESSAGE_H__ + +#if defined(OS_WIN) +#include +#endif +#include +#include "base/basictypes.h" +#include "chrome/common/ipc_message.h" + +namespace base { +class WaitableEvent; +} + +namespace IPC { + +class MessageReplyDeserializer; + +class SyncMessage : public Message { + public: + SyncMessage(int32 routing_id, uint16 type, PriorityValue priority, + MessageReplyDeserializer* deserializer); + + // Call this to get a deserializer for the output parameters. + // Note that this can only be called once, and the caller is responsible + // for deleting the deserializer when they're done. + MessageReplyDeserializer* GetReplyDeserializer(); + + // If this message can cause the receiver to block while waiting for user + // input (i.e. by calling MessageBox), then the caller needs to pump window + // messages and dispatch asynchronous messages while waiting for the reply. + // If this event is passed in, then window messages will start being pumped + // when it's set. Note that this behavior will continue even if the event is + // later reset. The event must be valid until after the Send call returns. + void set_pump_messages_event(base::WaitableEvent* event) { + pump_messages_event_ = event; + if (event) { + header()->flags |= PUMPING_MSGS_BIT; + } else { + header()->flags &= ~PUMPING_MSGS_BIT; + } + } + + // Call this if you always want to pump messages. You can call this method + // or set_pump_messages_event but not both. + void EnableMessagePumping(); + + base::WaitableEvent* pump_messages_event() const { + return pump_messages_event_; + } + + // Returns true if the message is a reply to the given request id. + static bool IsMessageReplyTo(const Message& msg, int request_id); + + // Given a reply message, returns an iterator to the beginning of the data + // (i.e. skips over the synchronous specific data). + static void* GetDataIterator(const Message* msg); + + // Given a synchronous message (or its reply), returns its id. + static int GetMessageId(const Message& msg); + + // Generates a reply message to the given message. + static Message* GenerateReply(const Message* msg); + + private: + struct SyncHeader { + // unique ID (unique per sender) + int message_id; + }; + + static bool ReadSyncHeader(const Message& msg, SyncHeader* header); + static bool WriteSyncHeader(Message* msg, const SyncHeader& header); + + MessageReplyDeserializer* deserializer_; + base::WaitableEvent* pump_messages_event_; + + static uint32 next_id_; // for generation of unique ids +}; + +// Used to deserialize parameters from a reply to a synchronous message +class MessageReplyDeserializer { + public: + bool SerializeOutputParameters(const Message& msg); + private: + // Derived classes need to implement this, using the given iterator (which + // is skipped past the header for synchronous messages). + virtual bool SerializeOutputParameters(const Message& msg, void* iter) = 0; +}; + +} // namespace IPC + +#endif // CHROME_COMMON_IPC_SYNC_MESSAGE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_message_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_message_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_message_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_message_unittest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,249 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// Unit test to make sure that the serialization of synchronous IPC messages +// works. This ensures that the macros and templates were defined correctly. +// Doesn't test the IPC channel mechanism. + +#include + +#include "base/basictypes.h" +#include "chrome/common/ipc_message.h" +#include "chrome/common/ipc_message_utils.h" +#include "base/logging.h" +#include "testing/gtest/include/gtest/gtest.h" + + +#define MESSAGES_INTERNAL_FILE "chrome/common/ipc_sync_message_unittest.h" +#include "chrome/common/ipc_message_macros.h" + +static IPC::Message* g_reply; + +class TestMessageReceiver { + public: + + void On_0_1(bool* out1) { + *out1 = false; + } + + void On_0_2(bool* out1, int* out2) { + *out1 = true; + *out2 = 2; + } + + void On_0_3(bool* out1, int* out2, std::string* out3) { + *out1 = false; + *out2 = 3; + *out3 = "0_3"; + } + + void On_1_1(int in1, bool* out1) { + DCHECK(in1 == 1); + *out1 = true; + } + + void On_1_2(bool in1, bool* out1, int* out2) { + DCHECK(!in1); + *out1 = true; + *out2 = 12; + } + + void On_1_3(int in1, std::string* out1, int* out2, bool* out3) { + DCHECK(in1 == 3); + *out1 = "1_3"; + *out2 = 13; + *out3 = false; + } + + void On_2_1(int in1, bool in2, bool* out1) { + DCHECK(in1 == 1 && !in2); + *out1 = true; + } + + void On_2_2(bool in1, int in2, bool* out1, int* out2) { + DCHECK(!in1 && in2 == 2); + *out1 = true; + *out2 = 22; + } + + void On_2_3(int in1, bool in2, std::string* out1, int* out2, bool* out3) { + DCHECK(in1 == 3 && in2); + *out1 = "2_3"; + *out2 = 23; + *out3 = false; + } + + void On_3_1(int in1, bool in2, std::string in3, bool* out1) { + DCHECK(in1 == 1 && !in2 && in3 == "3_1"); + *out1 = true; + } + + void On_3_2(std::string in1, bool in2, int in3, bool* out1, int* out2) { + DCHECK(in1 == "3_2" && !in2 && in3 == 2); + *out1 = true; + *out2 = 32; + } + + void On_3_3(int in1, std::string in2, bool in3, std::string* out1, int* out2, + bool* out3) { + DCHECK(in1 == 3 && in2 == "3_3" && in3); + *out1 = "3_3"; + *out2 = 33; + *out3 = false; + } + + bool Send(IPC::Message* message) { + // gets the reply message, stash in global + DCHECK(g_reply == NULL); + g_reply = message; + return true; + } + + void OnMessageReceived(const IPC::Message& msg) { + IPC_BEGIN_MESSAGE_MAP(TestMessageReceiver, msg) + IPC_MESSAGE_HANDLER(Msg_C_0_1, On_0_1) + IPC_MESSAGE_HANDLER(Msg_C_0_2, On_0_2) + IPC_MESSAGE_HANDLER(Msg_C_0_3, On_0_3) + IPC_MESSAGE_HANDLER(Msg_C_1_1, On_1_1) + IPC_MESSAGE_HANDLER(Msg_C_1_2, On_1_2) + IPC_MESSAGE_HANDLER(Msg_C_1_3, On_1_3) + IPC_MESSAGE_HANDLER(Msg_C_2_1, On_2_1) + IPC_MESSAGE_HANDLER(Msg_C_2_2, On_2_2) + IPC_MESSAGE_HANDLER(Msg_C_2_3, On_2_3) + IPC_MESSAGE_HANDLER(Msg_C_3_1, On_3_1) + IPC_MESSAGE_HANDLER(Msg_C_3_2, On_3_2) + IPC_MESSAGE_HANDLER(Msg_C_3_3, On_3_3) + IPC_MESSAGE_HANDLER(Msg_R_0_1, On_0_1) + IPC_MESSAGE_HANDLER(Msg_R_0_2, On_0_2) + IPC_MESSAGE_HANDLER(Msg_R_0_3, On_0_3) + IPC_MESSAGE_HANDLER(Msg_R_1_1, On_1_1) + IPC_MESSAGE_HANDLER(Msg_R_1_2, On_1_2) + IPC_MESSAGE_HANDLER(Msg_R_1_3, On_1_3) + IPC_MESSAGE_HANDLER(Msg_R_2_1, On_2_1) + IPC_MESSAGE_HANDLER(Msg_R_2_2, On_2_2) + IPC_MESSAGE_HANDLER(Msg_R_2_3, On_2_3) + IPC_MESSAGE_HANDLER(Msg_R_3_1, On_3_1) + IPC_MESSAGE_HANDLER(Msg_R_3_2, On_3_2) + IPC_MESSAGE_HANDLER(Msg_R_3_3, On_3_3) + IPC_END_MESSAGE_MAP() + } + +}; + +void Send(IPC::SyncMessage* msg) { + static TestMessageReceiver receiver; + + IPC::MessageReplyDeserializer* reply_serializer = msg->GetReplyDeserializer(); + DCHECK(reply_serializer != NULL); + + // "send" the message + receiver.OnMessageReceived(*msg); + delete msg; + + // get the reply message from the global, and deserialize the output + // parameters into the output pointers. + DCHECK(g_reply != NULL); + bool result = reply_serializer->SerializeOutputParameters(*g_reply); + DCHECK(result); + delete g_reply; + g_reply = NULL; + delete reply_serializer; +} + +TEST(IPCSyncMessageTest, Main) { + bool bool1 = true; + int int1 = 0; + std::string string1; + + Send(new Msg_C_0_1(&bool1)); + DCHECK(!bool1); + + Send(new Msg_C_0_2(&bool1, &int1)); + DCHECK(bool1 && int1 == 2); + + Send(new Msg_C_0_3(&bool1, &int1, &string1)); + DCHECK(!bool1 && int1 == 3 && string1 == "0_3"); + + bool1 = false; + Send(new Msg_C_1_1(1, &bool1)); + DCHECK(bool1); + + bool1 = false; + Send(new Msg_C_1_2(false, &bool1, &int1)); + DCHECK(bool1 && int1 == 12); + + bool1 = true; + Send(new Msg_C_1_3(3, &string1, &int1, &bool1)); + DCHECK(string1 == "1_3" && int1 == 13 && !bool1); + + bool1 = false; + Send(new Msg_C_2_1(1, false, &bool1)); + DCHECK(bool1); + + bool1 = false; + Send(new Msg_C_2_2(false, 2, &bool1, &int1)); + DCHECK(bool1 && int1 == 22); + + bool1 = true; + Send(new Msg_C_2_3(3, true, &string1, &int1, &bool1)); + DCHECK(string1 == "2_3" && int1 == 23 && !bool1); + + bool1 = false; + Send(new Msg_C_3_1(1, false, "3_1", &bool1)); + DCHECK(bool1); + + bool1 = false; + Send(new Msg_C_3_2("3_2", false, 2, &bool1, &int1)); + DCHECK(bool1 && int1 == 32); + + bool1 = true; + Send(new Msg_C_3_3(3, "3_3", true, &string1, &int1, &bool1)); + DCHECK(string1 == "3_3" && int1 == 33 && !bool1); + + // Routed messages, just a copy of the above but with extra routing paramater + Send(new Msg_R_0_1(0, &bool1)); + DCHECK(!bool1); + + Send(new Msg_R_0_2(0, &bool1, &int1)); + DCHECK(bool1 && int1 == 2); + + Send(new Msg_R_0_3(0, &bool1, &int1, &string1)); + DCHECK(!bool1 && int1 == 3 && string1 == "0_3"); + + bool1 = false; + Send(new Msg_R_1_1(0, 1, &bool1)); + DCHECK(bool1); + + bool1 = false; + Send(new Msg_R_1_2(0, false, &bool1, &int1)); + DCHECK(bool1 && int1 == 12); + + bool1 = true; + Send(new Msg_R_1_3(0, 3, &string1, &int1, &bool1)); + DCHECK(string1 == "1_3" && int1 == 13 && !bool1); + + bool1 = false; + Send(new Msg_R_2_1(0, 1, false, &bool1)); + DCHECK(bool1); + + bool1 = false; + Send(new Msg_R_2_2(0, false, 2, &bool1, &int1)); + DCHECK(bool1 && int1 == 22); + + bool1 = true; + Send(new Msg_R_2_3(0, 3, true, &string1, &int1, &bool1)); + DCHECK(string1 == "2_3" && int1 == 23 && !bool1); + + bool1 = false; + Send(new Msg_R_3_1(0, 1, false, "3_1", &bool1)); + DCHECK(bool1); + + bool1 = false; + Send(new Msg_R_3_2(0, "3_2", false, 2, &bool1, &int1)); + DCHECK(bool1 && int1 == 32); + + bool1 = true; + Send(new Msg_R_3_3(0, 3, "3_3", true, &string1, &int1, &bool1)); + DCHECK(string1 == "3_3" && int1 == 33 && !bool1); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_message_unittest.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_message_unittest.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_message_unittest.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_sync_message_unittest.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,98 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/ipc_message_macros.h" + +IPC_BEGIN_MESSAGES(Test) + IPC_SYNC_MESSAGE_CONTROL0_0(SyncChannelTestMsg_NoArgs) + + IPC_SYNC_MESSAGE_CONTROL0_1(SyncChannelTestMsg_AnswerToLife, + int /* answer */) + + IPC_SYNC_MESSAGE_CONTROL1_1(SyncChannelTestMsg_Double, + int /* in */, + int /* out */) + + // out1 is false + IPC_SYNC_MESSAGE_CONTROL0_1(Msg_C_0_1, bool) + + // out1 is true, out2 is 2 + IPC_SYNC_MESSAGE_CONTROL0_2(Msg_C_0_2, bool, int) + + // out1 is false, out2 is 3, out3 is "0_3" + IPC_SYNC_MESSAGE_CONTROL0_3(Msg_C_0_3, bool, int, std::string) + + // in1 must be 1, out1 is true + IPC_SYNC_MESSAGE_CONTROL1_1(Msg_C_1_1, int, bool) + + // in1 must be false, out1 is true, out2 is 12 + IPC_SYNC_MESSAGE_CONTROL1_2(Msg_C_1_2, bool, bool, int) + + // in1 must be 3, out1 is "1_3", out2 is 13, out3 is false + IPC_SYNC_MESSAGE_CONTROL1_3(Msg_C_1_3, int, std::string, int, bool) + + // in1 must be 1, in2 must be false, out1 is true + IPC_SYNC_MESSAGE_CONTROL2_1(Msg_C_2_1, int, bool, bool) + + // in1 must be false, in2 must be 2, out1 is true, out2 is 22 + IPC_SYNC_MESSAGE_CONTROL2_2(Msg_C_2_2, bool, int, bool, int) + + // in1 must be 3, in2 must be true, out1 is "2_3", out2 is 23, out3 is false + IPC_SYNC_MESSAGE_CONTROL2_3(Msg_C_2_3, int, bool, std::string, int, bool) + + // in1 must be 1, in2 must be false, in3 must be "3_1", out1 is true + IPC_SYNC_MESSAGE_CONTROL3_1(Msg_C_3_1, int, bool, std::string, bool) + + // in1 must be "3_3", in2 must be false, in3 must be 2, out1 is true, out2 is + // 32 + IPC_SYNC_MESSAGE_CONTROL3_2(Msg_C_3_2, std::string, bool, int, bool, int) + + // in1 must be 3, in2 must be "3_3", in3 must be true, out1 is "3_3", out2 is + // 33, out3 is false + IPC_SYNC_MESSAGE_CONTROL3_3(Msg_C_3_3, int, std::string, bool, std::string, + int, bool) + + + // NOTE: routed messages are just a copy of the above... + + // out1 is false + IPC_SYNC_MESSAGE_ROUTED0_1(Msg_R_0_1, bool) + + // out1 is true, out2 is 2 + IPC_SYNC_MESSAGE_ROUTED0_2(Msg_R_0_2, bool, int) + + // out1 is false, out2 is 3, out3 is "0_3" + IPC_SYNC_MESSAGE_ROUTED0_3(Msg_R_0_3, bool, int, std::string) + + // in1 must be 1, out1 is true + IPC_SYNC_MESSAGE_ROUTED1_1(Msg_R_1_1, int, bool) + + // in1 must be false, out1 is true, out2 is 12 + IPC_SYNC_MESSAGE_ROUTED1_2(Msg_R_1_2, bool, bool, int) + + // in1 must be 3, out1 is "1_3", out2 is 13, out3 is false + IPC_SYNC_MESSAGE_ROUTED1_3(Msg_R_1_3, int, std::string, int, bool) + + // in1 must be 1, in2 must be false, out1 is true + IPC_SYNC_MESSAGE_ROUTED2_1(Msg_R_2_1, int, bool, bool) + + // in1 must be false, in2 must be 2, out1 is true, out2 is 22 + IPC_SYNC_MESSAGE_ROUTED2_2(Msg_R_2_2, bool, int, bool, int) + + // in1 must be 3, in2 must be true, out1 is "2_3", out2 is 23, out3 is false + IPC_SYNC_MESSAGE_ROUTED2_3(Msg_R_2_3, int, bool, std::string, int, bool) + + // in1 must be 1, in2 must be false, in3 must be "3_1", out1 is true + IPC_SYNC_MESSAGE_ROUTED3_1(Msg_R_3_1, int, bool, std::string, bool) + + // in1 must be "3_3", in2 must be false, in3 must be 2, out1 is true, out2 + // is 32 + IPC_SYNC_MESSAGE_ROUTED3_2(Msg_R_3_2, std::string, bool, int, bool, int) + + // in1 must be 3, in2 must be "3_3", in3 must be true, out1 is "3_3", out2 is + // 33, out3 is false + IPC_SYNC_MESSAGE_ROUTED3_3(Msg_R_3_3, int, std::string, bool, std::string, + int, bool) + +IPC_END_MESSAGES(TestMsg) diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_tests.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_tests.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_tests.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_tests.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,475 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#elif defined(OS_POSIX) +#include +#include +#endif + +#include +#include +#include + +#include "chrome/common/ipc_tests.h" + +#include "base/at_exit.h" +#include "base/base_switches.h" +#include "base/command_line.h" +#include "base/debug_on_start.h" +#include "base/perftimer.h" +#include "base/perf_test_suite.h" +#include "base/test_suite.h" +#include "base/thread.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/ipc_channel.h" +#include "chrome/common/ipc_channel_proxy.h" +#include "chrome/common/ipc_message_utils.h" +#include "testing/multiprocess_func_list.h" + +// Define to enable IPC performance testing instead of the regular unit tests +// #define PERFORMANCE_TEST + +const wchar_t kTestClientChannel[] = L"T1"; +const wchar_t kReflectorChannel[] = L"T2"; +const wchar_t kFuzzerChannel[] = L"F3"; + +const size_t kLongMessageStringNumBytes = 50000; + +#ifndef PERFORMANCE_TEST + +void IPCChannelTest::SetUp() { + MultiProcessTest::SetUp(); + + // Construct a fresh IO Message loop for the duration of each test. + message_loop_ = new MessageLoopForIO(); +} + +void IPCChannelTest::TearDown() { + delete message_loop_; + message_loop_ = NULL; + + MultiProcessTest::TearDown(); +} + +#if defined(OS_WIN) +base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type, + IPC::Channel *channel) { + // kDebugChildren support. + bool debug_on_start = + CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren); + + switch (child_type) { + case TEST_CLIENT: + return MultiProcessTest::SpawnChild(L"RunTestClient", debug_on_start); + break; + case TEST_REFLECTOR: + return MultiProcessTest::SpawnChild(L"RunReflector", debug_on_start); + break; + case FUZZER_SERVER: + return MultiProcessTest::SpawnChild(L"RunFuzzServer", debug_on_start); + break; + default: + return NULL; + break; + } +} +#elif defined(OS_POSIX) +base::ProcessHandle IPCChannelTest::SpawnChild(ChildType child_type, + IPC::Channel *channel) { + // kDebugChildren support. + bool debug_on_start = + CommandLine::ForCurrentProcess()->HasSwitch(switches::kDebugChildren); + + base::file_handle_mapping_vector fds_to_map; + int src_fd; + int dest_fd; + channel->GetClientFileDescriptorMapping(&src_fd, &dest_fd); + if (src_fd > -1) { + fds_to_map.push_back(std::pair(src_fd, dest_fd)); + } + + base::ProcessHandle ret = NULL; + switch (child_type) { + case TEST_CLIENT: + ret = MultiProcessTest::SpawnChild(L"RunTestClient", + fds_to_map, + debug_on_start); + break; + case TEST_DESCRIPTOR_CLIENT: + ret = MultiProcessTest::SpawnChild(L"RunTestDescriptorClient", + fds_to_map, + debug_on_start); + break; + case TEST_DESCRIPTOR_CLIENT_SANDBOXED: + ret = MultiProcessTest::SpawnChild(L"RunTestDescriptorClientSandboxed", + fds_to_map, + debug_on_start); + break; + case TEST_REFLECTOR: + ret = MultiProcessTest::SpawnChild(L"RunReflector", + fds_to_map, + debug_on_start); + break; + case FUZZER_SERVER: + ret = MultiProcessTest::SpawnChild(L"RunFuzzServer", + fds_to_map, + debug_on_start); + break; + default: + return NULL; + break; + } + return ret; +} +#endif // defined(OS_POSIX) + +TEST_F(IPCChannelTest, BasicMessageTest) { + int v1 = 10; + std::string v2("foobar"); + std::wstring v3(L"hello world"); + + IPC::Message m(0, 1, IPC::Message::PRIORITY_NORMAL); + EXPECT_TRUE(m.WriteInt(v1)); + EXPECT_TRUE(m.WriteString(v2)); + EXPECT_TRUE(m.WriteWString(v3)); + + void* iter = NULL; + + int vi; + std::string vs; + std::wstring vw; + + EXPECT_TRUE(m.ReadInt(&iter, &vi)); + EXPECT_EQ(v1, vi); + + EXPECT_TRUE(m.ReadString(&iter, &vs)); + EXPECT_EQ(v2, vs); + + EXPECT_TRUE(m.ReadWString(&iter, &vw)); + EXPECT_EQ(v3, vw); + + // should fail + EXPECT_FALSE(m.ReadInt(&iter, &vi)); + EXPECT_FALSE(m.ReadString(&iter, &vs)); + EXPECT_FALSE(m.ReadWString(&iter, &vw)); +} + +static void Send(IPC::Message::Sender* sender, const char* text) { + static int message_index = 0; + + IPC::Message* message = new IPC::Message(0, + 2, + IPC::Message::PRIORITY_NORMAL); + message->WriteInt(message_index++); + message->WriteString(std::string(text)); + + // Make sure we can handle large messages. + char junk[kLongMessageStringNumBytes]; + memset(junk, 'a', sizeof(junk)-1); + junk[sizeof(junk)-1] = 0; + message->WriteString(std::string(junk)); + + // DEBUG: printf("[%u] sending message [%s]\n", GetCurrentProcessId(), text); + sender->Send(message); +} + +class MyChannelListener : public IPC::Channel::Listener { + public: + virtual void OnMessageReceived(const IPC::Message& message) { + IPC::MessageIterator iter(message); + + iter.NextInt(); + const std::string data = iter.NextString(); + const std::string big_string = iter.NextString(); + EXPECT_EQ(kLongMessageStringNumBytes - 1, big_string.length()); + + + if (--messages_left_ == 0) { + MessageLoop::current()->Quit(); + } else { + Send(sender_, "Foo"); + } + } + + virtual void OnChannelError() { + // There is a race when closing the channel so the last message may be lost. + EXPECT_LE(messages_left_, 1); + MessageLoop::current()->Quit(); + } + + void Init(IPC::Message::Sender* s) { + sender_ = s; + messages_left_ = 50; + } + + private: + IPC::Message::Sender* sender_; + int messages_left_; +}; + +TEST_F(IPCChannelTest, ChannelTest) { + MyChannelListener channel_listener; + // Setup IPC channel. + IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_SERVER, + &channel_listener); + chan.Connect(); + + channel_listener.Init(&chan); + + base::ProcessHandle process_handle = SpawnChild(TEST_CLIENT, &chan); + ASSERT_TRUE(process_handle); + + Send(&chan, "hello from parent"); + + // Run message loop. + MessageLoop::current()->Run(); + + // Close Channel so client gets its OnChannelError() callback fired. + chan.Close(); + + // Cleanup child process. + EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000)); + base::CloseProcessHandle(process_handle); +} + +TEST_F(IPCChannelTest, ChannelProxyTest) { + MyChannelListener channel_listener; + + // The thread needs to out-live the ChannelProxy. + base::Thread thread("ChannelProxyTestServer"); + base::Thread::Options options; + options.message_loop_type = MessageLoop::TYPE_IO; + thread.StartWithOptions(options); + { + // setup IPC channel proxy + IPC::ChannelProxy chan(kTestClientChannel, IPC::Channel::MODE_SERVER, + &channel_listener, NULL, thread.message_loop()); + + channel_listener.Init(&chan); + +#if defined(OS_WIN) + base::ProcessHandle process_handle = SpawnChild(TEST_CLIENT, NULL); +#elif defined(OS_POSIX) + bool debug_on_start = CommandLine::ForCurrentProcess()->HasSwitch( + switches::kDebugChildren); + base::file_handle_mapping_vector fds_to_map; + int src_fd; + int dest_fd; + chan.GetClientFileDescriptorMapping(&src_fd, &dest_fd); + if (src_fd > -1) { + fds_to_map.push_back(std::pair(src_fd, dest_fd)); + } + + base::ProcessHandle process_handle = MultiProcessTest::SpawnChild( + L"RunTestClient", + fds_to_map, + debug_on_start); +#endif // defined(OS_POXIX) + + ASSERT_TRUE(process_handle); + + Send(&chan, "hello from parent"); + + // run message loop + MessageLoop::current()->Run(); + + // cleanup child process + EXPECT_TRUE(base::WaitForSingleProcess(process_handle, 5000)); + base::CloseProcessHandle(process_handle); + } + thread.Stop(); +} + +MULTIPROCESS_TEST_MAIN(RunTestClient) { + MessageLoopForIO main_message_loop; + MyChannelListener channel_listener; + + // setup IPC channel + IPC::Channel chan(kTestClientChannel, IPC::Channel::MODE_CLIENT, + &channel_listener); + chan.Connect(); + channel_listener.Init(&chan); + Send(&chan, "hello from child"); + // run message loop + MessageLoop::current()->Run(); + // return true; + return NULL; +} + +#endif // !PERFORMANCE_TEST + +#ifdef PERFORMANCE_TEST + +//----------------------------------------------------------------------------- +// Manually performance test +// +// This test times the roundtrip IPC message cycle. It is enabled with a +// special preprocessor define to enable it instead of the standard IPC +// unit tests. This works around some funny termination conditions in the +// regular unit tests. +// +// This test is not automated. To test, you will want to vary the message +// count and message size in TEST to get the numbers you want. +// +// FIXME(brettw): Automate this test and have it run by default. + +// This channel listener just replies to all messages with the exact same +// message. It assumes each message has one string parameter. When the string +// "quit" is sent, it will exit. +class ChannelReflectorListener : public IPC::Channel::Listener { + public: + explicit ChannelReflectorListener(IPC::Channel *channel) : + channel_(channel), + count_messages_(0), + latency_messages_(0) { + std::cout << "Reflector up" << std::endl; + } + + ~ChannelReflectorListener() { + std::cout << "Client Messages: " << count_messages_ << std::endl; + std::cout << "Client Latency: " << latency_messages_ << std::endl; + } + + virtual void OnMessageReceived(const IPC::Message& message) { + count_messages_++; + IPC::MessageIterator iter(message); + int time = iter.NextInt(); + int msgid = iter.NextInt(); + std::string payload = iter.NextString(); + latency_messages_ += GetTickCount() - time; + + // cout << "reflector msg received: " << msgid << endl; + if (payload == "quit") + MessageLoop::current()->Quit(); + + IPC::Message* msg = new IPC::Message(0, + 2, + IPC::Message::PRIORITY_NORMAL); + msg->WriteInt(GetTickCount()); + msg->WriteInt(msgid); + msg->WriteString(payload); + channel_->Send(msg); + } + private: + IPC::Channel *channel_; + int count_messages_; + int latency_messages_; +}; + +class ChannelPerfListener : public IPC::Channel::Listener { + public: + ChannelPerfListener(IPC::Channel* channel, int msg_count, int msg_size) : + count_down_(msg_count), + channel_(channel), + count_messages_(0), + latency_messages_(0) { + payload_.resize(msg_size); + for (int i = 0; i < static_cast(payload_.size()); i++) + payload_[i] = 'a'; + std::cout << "perflistener up" << std::endl; + } + + ~ChannelPerfListener() { + std::cout << "Server Messages: " << count_messages_ << std::endl; + std::cout << "Server Latency: " << latency_messages_ << std::endl; + } + + virtual void OnMessageReceived(const IPC::Message& message) { + count_messages_++; + // decode the string so this gets counted in the total time + IPC::MessageIterator iter(message); + int time = iter.NextInt(); + int msgid = iter.NextInt(); + std::string cur = iter.NextString(); + latency_messages_ += GetTickCount() - time; + + // cout << "perflistener got message" << endl; + + count_down_--; + if (count_down_ == 0) { + IPC::Message* msg = new IPC::Message(0, + 2, + IPC::Message::PRIORITY_NORMAL); + msg->WriteInt(GetTickCount()); + msg->WriteInt(count_down_); + msg->WriteString("quit"); + channel_->Send(msg); + SetTimer(NULL, 1, 250, (TIMERPROC) PostQuitMessage); + return; + } + + IPC::Message* msg = new IPC::Message(0, + 2, + IPC::Message::PRIORITY_NORMAL); + msg->WriteInt(GetTickCount()); + msg->WriteInt(count_down_); + msg->WriteString(payload_); + channel_->Send(msg); + } + + private: + int count_down_; + std::string payload_; + IPC::Channel *channel_; + int count_messages_; + int latency_messages_; +}; + +TEST_F(IPCChannelTest, Performance) { + // setup IPC channel + IPC::Channel chan(kReflectorChannel, IPC::Channel::MODE_SERVER, NULL); + ChannelPerfListener perf_listener(&chan, 10000, 100000); + chan.set_listener(&perf_listener); + chan.Connect(); + + HANDLE process = SpawnChild(TEST_REFLECTOR, &chan); + ASSERT_TRUE(process); + + PlatformThread::Sleep(1000); + + PerfTimeLogger logger("IPC_Perf"); + + // this initial message will kick-start the ping-pong of messages + IPC::Message* message = new IPC::Message(0, + 2, + IPC::Message::PRIORITY_NORMAL); + message->WriteInt(GetTickCount()); + message->WriteInt(-1); + message->WriteString("Hello"); + chan.Send(message); + + // run message loop + MessageLoop::current()->Run(); + + // cleanup child process + WaitForSingleObject(process, 5000); + CloseHandle(process); +} + +// This message loop bounces all messages back to the sender +MULTIPROCESS_TEST_MAIN(RunReflector) { + MessageLoopForIO main_message_loop; + IPC::Channel chan(kReflectorChannel, IPC::Channel::MODE_CLIENT, NULL); + ChannelReflectorListener channel_reflector_listener(&chan); + chan.set_listener(&channel_reflector_listener); + chan.Connect(); + + MessageLoop::current()->Run(); + return true; +} + +#endif // PERFORMANCE_TEST + +int main(int argc, char** argv) { +#ifdef PERFORMANCE_TEST + int retval = PerfTestSuite(argc, argv).Run(); +#else + int retval = TestSuite(argc, argv).Run(); +#endif + return retval; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_tests.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_tests.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_tests.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_tests.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,47 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_IPC_TESTS_H__ +#define CHROME_COMMON_IPC_TESTS_H__ + +#include "base/multiprocess_test.h" +#include "base/process.h" + +// This unit test uses 3 types of child processes, a regular pipe client, +// a client reflector and a IPC server used for fuzzing tests. +enum ChildType { + TEST_CLIENT, + TEST_DESCRIPTOR_CLIENT, + TEST_DESCRIPTOR_CLIENT_SANDBOXED, + TEST_REFLECTOR, + FUZZER_SERVER +}; + +// The different channel names for the child processes. +extern const wchar_t kTestClientChannel[]; +extern const wchar_t kReflectorChannel[]; +extern const wchar_t kFuzzerChannel[]; + +class MessageLoopForIO; +namespace IPC { +class Channel; +} // namespace IPC + +//Base class to facilitate Spawning IPC Client processes. +class IPCChannelTest : public MultiProcessTest { + protected: + + // Create a new MessageLoopForIO For each test. + virtual void SetUp(); + virtual void TearDown(); + + // Spawns a child process of the specified type + base::ProcessHandle SpawnChild(ChildType child_type, + IPC::Channel *channel); + + // Created around each test instantiation. + MessageLoopForIO *message_loop_; +}; + +#endif // CHROME_COMMON_IPC_TESTS_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_test_sink.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_test_sink.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_test_sink.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_test_sink.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,51 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/ipc_test_sink.h" + +namespace IPC { + +TestSink::TestSink() { +} + +TestSink::~TestSink() { +} + +void TestSink::OnMessageReceived(const Message& msg) { + messages_.push_back(Message(msg)); +} + +void TestSink::ClearMessages() { + messages_.clear(); +} + +const Message* TestSink::GetMessageAt(size_t index) const { + if (index >= messages_.size()) + return NULL; + return &messages_[index]; +} + +const Message* TestSink::GetFirstMessageMatching(uint16 id) const { + for (size_t i = 0; i < messages_.size(); i++) { + if (messages_[i].type() == id) + return &messages_[i]; + } + return NULL; +} + +const Message* TestSink::GetUniqueMessageMatching(uint16 id) const { + size_t found_index = 0; + size_t found_count = 0; + for (size_t i = 0; i < messages_.size(); i++) { + if (messages_[i].type() == id) { + found_count++; + found_index = i; + } + } + if (found_count != 1) + return NULL; // Didn't find a unique one. + return &messages_[found_index]; +} + +} // namespace IPC diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_test_sink.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_test_sink.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_test_sink.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_test_sink.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,84 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_IPC_TEST_SINK_H_ +#define CHROME_COMMON_IPC_TEST_SINK_H_ + +#include +#include + +#include "base/basictypes.h" +#include "chrome/common/ipc_message.h" + +namespace IPC { + +// This test sink provides a "sink" for IPC messages that are sent. It allows +// the caller to query messages received in various different ways. It is +// designed for tests for objects that use the IPC system. +// +// Typical usage: +// +// test_sink.ClearMessages(); +// do_something(); +// +// // We should have gotten exactly one update state message. +// EXPECT_TRUE(test_sink.GetUniqeMessageMatching(ViewHostMsg_Update::ID)); +// // ...and no start load messages. +// EXPECT_FALSE(test_sink.GetFirstMessageMatching(ViewHostMsg_Start::ID)); +// +// // Now inspect a message. This assumes a message that was declared like +// // this: IPC_MESSAGE_ROUTED2(ViewMsg_Foo, bool, int) +// IPC::Message* msg = test_sink.GetFirstMessageMatching(ViewMsg_Foo::ID)); +// ASSERT_TRUE(msg); +// bool first_param; +// int second_param; +// ViewMsg_Foo::Read(msg, &first_param, &second_param); +// +// // Go on to the next phase of the test. +// test_sink.ClearMessages(); +// +// To hook up the sink, all you need to do is call OnMessageReceived when a +// message is recieved. +class TestSink { + public: + TestSink(); + ~TestSink(); + + // Used by the source of the messages to send the message to the sink. This + // will make a copy of the message and store it in the list. + void OnMessageReceived(const Message& msg); + + // Returns the number of messages in the queue. + size_t message_count() const { return messages_.size(); } + + // Clears the message queue of saved messages. + void ClearMessages(); + + // Returns the message at the given index in the queue. The index may be out + // of range, in which case the return value is NULL. The returned pointer will + // only be valid until another message is received or the list is cleared. + const Message* GetMessageAt(size_t index) const; + + // Returns the first message with the given ID in the queue. If there is no + // message with the given ID, returns NULL. The returned pointer will only be + // valid until another message is received or the list is cleared. + const Message* GetFirstMessageMatching(uint16 id) const; + + // Returns the message with the given ID in the queue. If there is no such + // message or there is more than one of that message, this will return NULL + // (with the expectation that you'll do an ASSERT_TRUE() on the result). + // The returned pointer will only be valid until another message is received + // or the list is cleared. + const Message* GetUniqueMessageMatching(uint16 id) const; + + private: + // The actual list of received messages. + std::vector messages_; + + DISALLOW_COPY_AND_ASSIGN(TestSink); +}; + +} // namespace IPC + +#endif // CHROME_COMMON_IPC_TEST_SINK_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_tests.vcproj firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_tests.vcproj --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_tests.vcproj 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ipc_tests.vcproj 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/json_value_serializer.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/json_value_serializer.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/json_value_serializer.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/json_value_serializer.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,58 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/json_value_serializer.h" + +#include "base/file_util.h" +#include "base/json_reader.h" +#include "base/json_writer.h" +#include "base/string_util.h" +#include "chrome/common/logging_chrome.h" + +JSONStringValueSerializer::~JSONStringValueSerializer() {} + +bool JSONStringValueSerializer::Serialize(const Value& root) { + if (!json_string_ || initialized_with_const_string_) + return false; + + JSONWriter::Write(&root, pretty_print_, json_string_); + + return true; +} + +Value* JSONStringValueSerializer::Deserialize(std::string* error_message) { + if (!json_string_) + return NULL; + + return JSONReader::ReadAndReturnError(*json_string_, allow_trailing_comma_, + error_message); +} + +/******* File Serializer *******/ + +bool JSONFileValueSerializer::Serialize(const Value& root) { + std::string json_string; + JSONStringValueSerializer serializer(&json_string); + serializer.set_pretty_print(true); + bool result = serializer.Serialize(root); + if (!result) + return false; + + int data_size = static_cast(json_string.size()); + if (file_util::WriteFile(json_file_path_, + json_string.data(), + data_size) != data_size) + return false; + + return true; +} + +Value* JSONFileValueSerializer::Deserialize(std::string* error_message) { + std::string json_string; + if (!file_util::ReadFileToString(json_file_path_, &json_string)) { + return NULL; + } + JSONStringValueSerializer serializer(json_string); + return serializer.Deserialize(error_message); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/json_value_serializer.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/json_value_serializer.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/json_value_serializer.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/json_value_serializer.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,98 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_JSON_VALUE_SERIALIZER_H_ +#define CHROME_COMMON_JSON_VALUE_SERIALIZER_H_ + +#include + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/values.h" + +class JSONStringValueSerializer : public ValueSerializer { + public: + // json_string is the string that will be source of the deserialization + // or the destination of the serialization. The caller of the constructor + // retains ownership of the string. + JSONStringValueSerializer(std::string* json_string) + : json_string_(json_string), + initialized_with_const_string_(false), + pretty_print_(false), + allow_trailing_comma_(false) { + } + + // This version allows initialization with a const string reference for + // deserialization only. + JSONStringValueSerializer(const std::string& json_string) + : json_string_(&const_cast(json_string)), + initialized_with_const_string_(true), + allow_trailing_comma_(false) { + } + + ~JSONStringValueSerializer(); + + // Attempt to serialize the data structure represented by Value into + // JSON. If the return value is true, the result will have been written + // into the string passed into the constructor. + bool Serialize(const Value& root); + + // Attempt to deserialize the data structure encoded in the string passed + // in to the constructor into a structure of Value objects. If the return + // value is NULL and |error_message| is non-null, |error_message| will contain + // a string describing the error. + Value* Deserialize(std::string* error_message); + + void set_pretty_print(bool new_value) { pretty_print_ = new_value; } + bool pretty_print() { return pretty_print_; } + + void set_allow_trailing_comma(bool new_value) { + allow_trailing_comma_ = new_value; + } + + private: + std::string* json_string_; + bool initialized_with_const_string_; + bool pretty_print_; // If true, serialization will span multiple lines. + // If true, deserialization will allow trailing commas. + bool allow_trailing_comma_; + + DISALLOW_EVIL_CONSTRUCTORS(JSONStringValueSerializer); +}; + +class JSONFileValueSerializer : public ValueSerializer { + public: + // json_file_patch is the path of a file that will be source of the + // deserialization or the destination of the serialization. + // When deserializing, the file should exist, but when serializing, the + // serializer will attempt to create the file at the specified location. + explicit JSONFileValueSerializer(const FilePath& json_file_path) + : json_file_path_(json_file_path) {} + + ~JSONFileValueSerializer() {} + + // DO NOT USE except in unit tests to verify the file was written properly. + // We should never serialize directly to a file since this will block the + // thread. Instead, serialize to a string and write to the file you want on + // the file thread. + // + // Attempt to serialize the data structure represented by Value into + // JSON. If the return value is true, the result will have been written + // into the file whose name was passed into the constructor. + bool Serialize(const Value& root); + + // Attempt to deserialize the data structure encoded in the file passed + // in to the constructor into a structure of Value objects. If the return + // value is NULL, and if |error_message| is non-null, |error_message| will + // contain a string describing the error. The caller takes ownership of the + // returned value. + Value* Deserialize(std::string* error_message); + + private: + FilePath json_file_path_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(JSONFileValueSerializer); +}; + +#endif // CHROME_COMMON_JSON_VALUE_SERIALIZER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/json_value_serializer_perftest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/json_value_serializer_perftest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/json_value_serializer_perftest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/json_value_serializer_perftest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,90 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/perftimer.h" +#include "base/string_util.h" +#include "base/values.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/json_value_serializer.h" +#include "chrome/common/logging_chrome.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +class JSONValueSerializerTests : public testing::Test { + protected: + virtual void SetUp() { + static const wchar_t* const kTestFilenames[] = { + L"serializer_nested_test.js", + L"serializer_test.js", + L"serializer_test_nowhitespace.js", + }; + + // Load test cases + for (size_t i = 0; i < arraysize(kTestFilenames); ++i) { + std::wstring filename; + EXPECT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &filename)); + file_util::AppendToPath(&filename, kTestFilenames[i]); + + std::string test_case; + EXPECT_TRUE(file_util::ReadFileToString(filename, &test_case)); + test_cases_.push_back(test_case); + } + } + + // Holds json strings to be tested. + std::vector test_cases_; +}; + +} // namespace + +// Test deserialization of a json string into a Value object. We run the test +// using 3 sample strings for both the current decoder and jsoncpp's decoder. +TEST_F(JSONValueSerializerTests, Reading) { + printf("\n"); + const int kIterations = 100000; + + // Test chrome json implementation + PerfTimeLogger chrome_timer("chrome"); + for (int i = 0; i < kIterations; ++i) { + for (size_t j = 0; j < test_cases_.size(); ++j) { + JSONStringValueSerializer reader(test_cases_[j]); + scoped_ptr root(reader.Deserialize(NULL)); + ASSERT_TRUE(root.get()); + } + } + chrome_timer.Done(); +} + +TEST_F(JSONValueSerializerTests, CompactWriting) { + printf("\n"); + const int kIterations = 100000; + // Convert test cases to Value objects. + std::vector test_cases; + for (size_t i = 0; i < test_cases_.size(); ++i) { + JSONStringValueSerializer reader(test_cases_[i]); + Value* root = reader.Deserialize(NULL); + ASSERT_TRUE(root); + test_cases.push_back(root); + } + + PerfTimeLogger chrome_timer("chrome"); + for (int i = 0; i < kIterations; ++i) { + for (size_t j = 0; j < test_cases.size(); ++j) { + std::string json; + JSONStringValueSerializer reader(&json); + ASSERT_TRUE(reader.Serialize(*test_cases[j])); + } + } + chrome_timer.Done(); + + // Clean up test cases. + for (size_t i = 0; i < test_cases.size(); ++i) { + delete test_cases[i]; + test_cases[i] = NULL; + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/json_value_serializer_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/json_value_serializer_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/json_value_serializer_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/json_value_serializer_unittest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,333 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "base/file_util.h" +#include "base/json_reader.h" +#include "base/json_writer.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "base/values.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/json_value_serializer.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(JSONValueSerializerTest, Roundtrip) { + const std::string original_serialization = + "{\"bool\":true,\"int\":42,\"list\":[1,2],\"null\":null,\"real\":3.14}"; + JSONStringValueSerializer serializer(original_serialization); + scoped_ptr root(serializer.Deserialize(NULL)); + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY)); + + DictionaryValue* root_dict = static_cast(root.get()); + + Value* null_value = NULL; + ASSERT_TRUE(root_dict->Get(L"null", &null_value)); + ASSERT_TRUE(null_value); + ASSERT_TRUE(null_value->IsType(Value::TYPE_NULL)); + + bool bool_value = false; + ASSERT_TRUE(root_dict->GetBoolean(L"bool", &bool_value)); + ASSERT_TRUE(bool_value); + + int int_value = 0; + ASSERT_TRUE(root_dict->GetInteger(L"int", &int_value)); + ASSERT_EQ(42, int_value); + + double real_value = 0.0; + ASSERT_TRUE(root_dict->GetReal(L"real", &real_value)); + ASSERT_DOUBLE_EQ(3.14, real_value); + + // We shouldn't be able to write using this serializer, since it was + // initialized with a const string. + ASSERT_FALSE(serializer.Serialize(*root_dict)); + + std::string test_serialization = ""; + JSONStringValueSerializer mutable_serializer(&test_serialization); + ASSERT_TRUE(mutable_serializer.Serialize(*root_dict)); + ASSERT_EQ(original_serialization, test_serialization); + + mutable_serializer.set_pretty_print(true); + ASSERT_TRUE(mutable_serializer.Serialize(*root_dict)); + const std::string pretty_serialization = + "{\r\n" + " \"bool\": true,\r\n" + " \"int\": 42,\r\n" + " \"list\": [ 1, 2 ],\r\n" + " \"null\": null,\r\n" + " \"real\": 3.14\r\n" + "}\r\n"; + ASSERT_EQ(pretty_serialization, test_serialization); +} + +TEST(JSONValueSerializerTest, StringEscape) { + std::wstring all_chars; + for (int i = 1; i < 256; ++i) { + all_chars += static_cast(i); + } + // Generated in in Firefox using the following js (with an extra backslash for + // double quote): + // var s = ''; + // for (var i = 1; i < 256; ++i) { s += String.fromCharCode(i); } + // uneval(s).replace(/\\/g, "\\\\"); + std::string all_chars_expected = + "\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\b\\t\\n\\v\\f\\r\\x0E\\x0F\\x10" + "\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1A\\x1B\\x1C\\x1D\\x1E" + "\\x1F !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\" + "\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x7F\\x80\\x81\\x82\\x83\\x84\\x85" + "\\x86\\x87\\x88\\x89\\x8A\\x8B\\x8C\\x8D\\x8E\\x8F\\x90\\x91\\x92\\x93" + "\\x94\\x95\\x96\\x97\\x98\\x99\\x9A\\x9B\\x9C\\x9D\\x9E\\x9F\\xA0\\xA1" + "\\xA2\\xA3\\xA4\\xA5\\xA6\\xA7\\xA8\\xA9\\xAA\\xAB\\xAC\\xAD\\xAE\\xAF" + "\\xB0\\xB1\\xB2\\xB3\\xB4\\xB5\\xB6\\xB7\\xB8\\xB9\\xBA\\xBB\\xBC\\xBD" + "\\xBE\\xBF\\xC0\\xC1\\xC2\\xC3\\xC4\\xC5\\xC6\\xC7\\xC8\\xC9\\xCA\\xCB" + "\\xCC\\xCD\\xCE\\xCF\\xD0\\xD1\\xD2\\xD3\\xD4\\xD5\\xD6\\xD7\\xD8\\xD9" + "\\xDA\\xDB\\xDC\\xDD\\xDE\\xDF\\xE0\\xE1\\xE2\\xE3\\xE4\\xE5\\xE6\\xE7" + "\\xE8\\xE9\\xEA\\xEB\\xEC\\xED\\xEE\\xEF\\xF0\\xF1\\xF2\\xF3\\xF4\\xF5" + "\\xF6\\xF7\\xF8\\xF9\\xFA\\xFB\\xFC\\xFD\\xFE\\xFF"; + + std::string expected_output = "{\"all_chars\":\"" + all_chars_expected + + "\"}"; + // Test JSONWriter interface + std::string output_js; + DictionaryValue valueRoot; + valueRoot.SetString(L"all_chars", all_chars); + JSONWriter::Write(&valueRoot, false, &output_js); + ASSERT_EQ(expected_output, output_js); + + // Test JSONValueSerializer interface (uses JSONWriter). + JSONStringValueSerializer serializer(&output_js); + ASSERT_TRUE(serializer.Serialize(valueRoot)); + ASSERT_EQ(expected_output, output_js); +} + +TEST(JSONValueSerializerTest, UnicodeStrings) { + // unicode string json -> escaped ascii text + DictionaryValue root; + std::wstring test(L"\x7F51\x9875"); + root.SetString(L"web", test); + + std::string expected = "{\"web\":\"\\u7F51\\u9875\"}"; + + std::string actual; + JSONStringValueSerializer serializer(&actual); + ASSERT_TRUE(serializer.Serialize(root)); + ASSERT_EQ(expected, actual); + + // escaped ascii text -> json + JSONStringValueSerializer deserializer(expected); + scoped_ptr deserial_root(deserializer.Deserialize(NULL)); + ASSERT_TRUE(deserial_root.get()); + DictionaryValue* dict_root = + static_cast(deserial_root.get()); + std::wstring web_value; + ASSERT_TRUE(dict_root->GetString(L"web", &web_value)); + ASSERT_EQ(test, web_value); +} + +TEST(JSONValueSerializerTest, HexStrings) { + // hex string json -> escaped ascii text + DictionaryValue root; + std::wstring test(L"\x01\x02"); + root.SetString(L"test", test); + + std::string expected = "{\"test\":\"\\x01\\x02\"}"; + + std::string actual; + JSONStringValueSerializer serializer(&actual); + ASSERT_TRUE(serializer.Serialize(root)); + ASSERT_EQ(expected, actual); + + // escaped ascii text -> json + JSONStringValueSerializer deserializer(expected); + scoped_ptr deserial_root(deserializer.Deserialize(NULL)); + ASSERT_TRUE(deserial_root.get()); + DictionaryValue* dict_root = + static_cast(deserial_root.get()); + std::wstring test_value; + ASSERT_TRUE(dict_root->GetString(L"test", &test_value)); + ASSERT_EQ(test, test_value); + + // Test converting escaped regular chars + std::string escaped_chars = "{\"test\":\"\\x67\\x6f\"}"; + JSONStringValueSerializer deserializer2(escaped_chars); + deserial_root.reset(deserializer2.Deserialize(NULL)); + ASSERT_TRUE(deserial_root.get()); + dict_root = static_cast(deserial_root.get()); + ASSERT_TRUE(dict_root->GetString(L"test", &test_value)); + ASSERT_EQ(std::wstring(L"go"), test_value); +} + +TEST(JSONValueSerializerTest, AllowTrailingComma) { + scoped_ptr root; + scoped_ptr root_expected; + std::string test_with_commas("{\"key\": [true,],}"); + std::string test_no_commas("{\"key\": [true]}"); + + JSONStringValueSerializer serializer(test_with_commas); + serializer.set_allow_trailing_comma(true); + JSONStringValueSerializer serializer_expected(test_no_commas); + root.reset(serializer.Deserialize(NULL)); + ASSERT_TRUE(root.get()); + root_expected.reset(serializer_expected.Deserialize(NULL)); + ASSERT_TRUE(root_expected.get()); + ASSERT_TRUE(root->Equals(root_expected.get())); +} + +namespace { + +void ValidateJsonList(const std::string& json) { + scoped_ptr root(JSONReader::Read(json, false)); + ASSERT_TRUE(root.get() && root->IsType(Value::TYPE_LIST)); + ListValue* list = static_cast(root.get()); + ASSERT_EQ(1U, list->GetSize()); + Value* elt = NULL; + ASSERT_TRUE(list->Get(0, &elt)); + int value = 0; + ASSERT_TRUE(elt && elt->GetAsInteger(&value)); + ASSERT_EQ(1, value); +} + +} // namespace + +TEST(JSONValueSerializerTest, JSONReaderComments) { + ValidateJsonList("[ // 2, 3, ignore me ] \n1 ]"); + ValidateJsonList("[ /* 2, \n3, ignore me ]*/ \n1 ]"); + ValidateJsonList("//header\n[ // 2, \n// 3, \n1 ]// footer"); + ValidateJsonList("/*\n[ // 2, \n// 3, \n1 ]*/[1]"); + ValidateJsonList("[ 1 /* one */ ] /* end */"); + ValidateJsonList("[ 1 //// ,2\r\n ]"); + + scoped_ptr root; + + // It's ok to have a comment in a string. + root.reset(JSONReader::Read("[\"// ok\\n /* foo */ \"]", false)); + ASSERT_TRUE(root.get() && root->IsType(Value::TYPE_LIST)); + ListValue* list = static_cast(root.get()); + ASSERT_EQ(1U, list->GetSize()); + Value* elt = NULL; + ASSERT_TRUE(list->Get(0, &elt)); + std::wstring value; + ASSERT_TRUE(elt && elt->GetAsString(&value)); + ASSERT_EQ(L"// ok\n /* foo */ ", value); + + // You can't nest comments. + root.reset(JSONReader::Read("/* /* inner */ outer */ [ 1 ]", false)); + ASSERT_FALSE(root.get()); + + // Not a open comment token. + root.reset(JSONReader::Read("/ * * / [1]", false)); + ASSERT_FALSE(root.get()); +} + +class JSONFileValueSerializerTest : public testing::Test { +protected: + virtual void SetUp() { + // Name a subdirectory of the temp directory. + ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_)); + test_dir_ = + test_dir_.Append(FILE_PATH_LITERAL("JSONFileValueSerializerTest")); + + // Create a fresh, empty copy of this directory. + file_util::Delete(test_dir_, true); + file_util::CreateDirectory(test_dir_); + } + virtual void TearDown() { + // Clean up test directory + ASSERT_TRUE(file_util::Delete(test_dir_, false)); + ASSERT_FALSE(file_util::PathExists(test_dir_)); + } + + // the path to temporary directory used to contain the test operations + FilePath test_dir_; +}; + +TEST_F(JSONFileValueSerializerTest, Roundtrip) { + FilePath original_file_path; + ASSERT_TRUE( + PathService::Get(chrome::DIR_TEST_DATA, &original_file_path)); + original_file_path = + original_file_path.Append(FILE_PATH_LITERAL("serializer_test.js")); + + ASSERT_TRUE(file_util::PathExists(original_file_path)); + + JSONFileValueSerializer deserializer(original_file_path); + scoped_ptr root; + root.reset(deserializer.Deserialize(NULL)); + + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY)); + + DictionaryValue* root_dict = static_cast(root.get()); + + Value* null_value = NULL; + ASSERT_TRUE(root_dict->Get(L"null", &null_value)); + ASSERT_TRUE(null_value); + ASSERT_TRUE(null_value->IsType(Value::TYPE_NULL)); + + bool bool_value = false; + ASSERT_TRUE(root_dict->GetBoolean(L"bool", &bool_value)); + ASSERT_TRUE(bool_value); + + int int_value = 0; + ASSERT_TRUE(root_dict->GetInteger(L"int", &int_value)); + ASSERT_EQ(42, int_value); + + std::wstring string_value; + ASSERT_TRUE(root_dict->GetString(L"string", &string_value)); + ASSERT_EQ(L"hello", string_value); + + // Now try writing. + const FilePath written_file_path = + test_dir_.Append(FILE_PATH_LITERAL("test_output.js")); + + ASSERT_FALSE(file_util::PathExists(written_file_path)); + JSONFileValueSerializer serializer(written_file_path); + ASSERT_TRUE(serializer.Serialize(*root)); + ASSERT_TRUE(file_util::PathExists(written_file_path)); + + // Now compare file contents. + EXPECT_TRUE(file_util::ContentsEqual(original_file_path, written_file_path)); + EXPECT_TRUE(file_util::Delete(written_file_path, false)); +} + +TEST_F(JSONFileValueSerializerTest, RoundtripNested) { + FilePath original_file_path; + ASSERT_TRUE( + PathService::Get(chrome::DIR_TEST_DATA, &original_file_path)); + original_file_path = + original_file_path.Append(FILE_PATH_LITERAL("serializer_nested_test.js")); + + ASSERT_TRUE(file_util::PathExists(original_file_path)); + + JSONFileValueSerializer deserializer(original_file_path); + scoped_ptr root; + root.reset(deserializer.Deserialize(NULL)); + ASSERT_TRUE(root.get()); + + // Now try writing. + FilePath written_file_path = + test_dir_.Append(FILE_PATH_LITERAL("test_output.js")); + + ASSERT_FALSE(file_util::PathExists(written_file_path)); + JSONFileValueSerializer serializer(written_file_path); + ASSERT_TRUE(serializer.Serialize(*root)); + ASSERT_TRUE(file_util::PathExists(written_file_path)); + + // Now compare file contents. + EXPECT_TRUE(file_util::ContentsEqual(original_file_path, written_file_path)); + EXPECT_TRUE(file_util::Delete(written_file_path, false)); +} + +TEST_F(JSONFileValueSerializerTest, NoWhitespace) { + FilePath source_file_path; + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &source_file_path)); + source_file_path = source_file_path.Append( + FILE_PATH_LITERAL("serializer_test_nowhitespace.js")); + ASSERT_TRUE(file_util::PathExists(source_file_path)); + JSONFileValueSerializer serializer(source_file_path); + scoped_ptr root; + root.reset(serializer.Deserialize(NULL)); + ASSERT_TRUE(root.get()); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/jstemplate_builder.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/jstemplate_builder.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/jstemplate_builder.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/jstemplate_builder.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,53 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A helper function for using JsTemplate. See jstemplate_builder.h for more +// info. + +#include "chrome/common/jstemplate_builder.h" + +#include "app/resource_bundle.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "chrome/common/json_value_serializer.h" + +#include "grit/common_resources.h" + +namespace jstemplate_builder { + +std::string GetTemplateHtml(const StringPiece& html_template, + const DictionaryValue* json, + const StringPiece& template_id) { + // fetch and cache the pointer of the jstemplate resource source text. + static const StringPiece jstemplate_src( + ResourceBundle::GetSharedInstance().GetRawDataResource(IDR_JSTEMPLATE_JS)); + + if (jstemplate_src.empty()) { + NOTREACHED() << "Unable to get jstemplate src"; + return std::string(); + } + + // Convert the template data to a json string. + DCHECK(json) << "must include json data structure"; + + std::string jstext; + JSONStringValueSerializer serializer(&jstext); + serializer.Serialize(*json); + // tag. So we + // replace "); + output.append(jstemplate_src.data(), jstemplate_src.size()); + output.append("var tp = document.getElementById('"); + output.append(template_id.data(), template_id.size()); + output.append("'); var cx = new JsExprContext("); + output.append(jstext); + output.append("); jstProcess(cx, tp);"); + + return output; +} + +} // namespace jstemplate_builder diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/jstemplate_builder.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/jstemplate_builder.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/jstemplate_builder.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/jstemplate_builder.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,30 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This provides some helper methods for building and rendering an +// internal html page. The flow is as follows: +// - instantiate a builder given a webframe that we're going to render content +// into +// - load the template html and load the jstemplate javascript into the frame +// - given a json data object, run the jstemplate javascript which fills in +// template values + +#ifndef CHROME_COMMON_JSTEMPLATE_BUILDER_H_ +#define CHROME_COMMON_JSTEMPLATE_BUILDER_H_ + +#include + +#include "base/values.h" + +class StringPiece; + +namespace jstemplate_builder { + // A helper function that generates a string of HTML to be loaded. The + // string includes the HTML and the javascript code necessary to generate the + // full page. + std::string GetTemplateHtml(const StringPiece& html_template, + const DictionaryValue* json, + const StringPiece& template_id); +} // namespace jstemplate_builder +#endif // CHROME_COMMON_JSTEMPLATE_BUILDER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/libxml_utils.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/libxml_utils.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/libxml_utils.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/libxml_utils.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,144 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/libxml_utils.h" + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/string_util.h" + +#include "libxml/xmlreader.h" + +std::string XmlStringToStdString(const xmlChar* xmlstring) { + // xmlChar*s are UTF-8, so this cast is safe. + if (xmlstring) + return std::string(reinterpret_cast(xmlstring)); + else + return ""; +} + +XmlReader::XmlReader() + : reader_(NULL), + ALLOW_THIS_IN_INITIALIZER_LIST( + error_func_(this, &XmlReader::GenericErrorCallback)) { +} + +XmlReader::~XmlReader() { + if (reader_) + xmlFreeTextReader(reader_); +} + +/* static */ void XmlReader::GenericErrorCallback(void* context, + const char* msg, ...) { + va_list args; + va_start(args, msg); + + XmlReader* reader = static_cast(context); + reader->errors_.append(StringPrintf(msg, args)); +} + +bool XmlReader::Load(const std::string& input) { + const int kParseOptions = + XML_PARSE_RECOVER | // recover on errors + XML_PARSE_NONET; // forbid network access + // TODO(evanm): Verify it's OK to pass NULL for the URL and encoding. + // The libxml code allows for these, but it's unclear what effect is has. + reader_ = xmlReaderForMemory(input.data(), static_cast(input.size()), + NULL, NULL, kParseOptions); + return reader_ != NULL; +} + +bool XmlReader::LoadFile(const std::string& file_path) { + + const int kParseOptions = + XML_PARSE_RECOVER | // recover on errors + XML_PARSE_NONET; // forbid network access + reader_ = xmlReaderForFile(file_path.c_str(), NULL, kParseOptions); + return reader_ != NULL; +} + +bool XmlReader::NodeAttribute(const char* name, std::string* out) { + xmlChar* value = xmlTextReaderGetAttribute(reader_, BAD_CAST name); + if (!value) + return false; + *out = XmlStringToStdString(value); + xmlFree(value); + return true; +} + +bool XmlReader::ReadElementContent(std::string* content) { + DCHECK(NodeType() == XML_READER_TYPE_ELEMENT); + const int start_depth = Depth(); + + if (xmlTextReaderIsEmptyElement(reader_)) { + // Empty tag. We succesfully read the content, but it's + // empty. + *content = ""; + // Advance past this empty tag. + if (!Read()) + return false; + return true; + } + + // Advance past opening element tag. + if (!Read()) + return false; + + // Read the content. We read up until we hit a closing tag at the + // same level as our starting point. + while (NodeType() != XML_READER_TYPE_END_ELEMENT || Depth() != start_depth) { + *content += XmlStringToStdString(xmlTextReaderConstValue(reader_)); + if (!Read()) + return false; + } + + // Advance past ending element tag. + DCHECK_EQ(NodeType(), XML_READER_TYPE_END_ELEMENT); + if (!Read()) + return false; + + return true; +} + +bool XmlReader::SkipToElement() { + do { + switch (NodeType()) { + case XML_READER_TYPE_ELEMENT: + return true; + case XML_READER_TYPE_END_ELEMENT: + return false; + default: + // Skip all other node types. + continue; + } + } while (Read()); + return false; +} + + +// XmlWriter functions + +XmlWriter::XmlWriter() : + writer_(NULL), + buffer_(NULL) {} + +XmlWriter::~XmlWriter() { + if (writer_) + xmlFreeTextWriter(writer_); + if (buffer_) + xmlBufferFree(buffer_); +} + +void XmlWriter::StartWriting() { + buffer_ = xmlBufferCreate(); + writer_ = xmlNewTextWriterMemory(buffer_, 0); + xmlTextWriterSetIndent(writer_, 1); + xmlTextWriterStartDocument(writer_, NULL, NULL, NULL); +} + +void XmlWriter::StopWriting() { + xmlTextWriterEndDocument(writer_); + xmlFreeTextWriter(writer_); + writer_ = NULL; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/libxml_utils.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/libxml_utils.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/libxml_utils.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/libxml_utils.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,182 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_LIBXML_UTILS_H__ +#define CHROME_COMMON_LIBXML_UTILS_H__ + +#include + +#include "libxml/xmlreader.h" +#include "libxml/xmlwriter.h" + +// Converts a libxml xmlChar* into a UTF-8 std::string. +// NULL inputs produce an empty string. +std::string XmlStringToStdString(const xmlChar* xmlstring); + +// libxml uses a global error function pointer for reporting errors. +// A ScopedXmlErrorFunc object lets you change the global error pointer +// for the duration of the object's lifetime. +class ScopedXmlErrorFunc { + public: + ScopedXmlErrorFunc(void* context, xmlGenericErrorFunc func) { + old_error_func_ = xmlGenericError; + old_error_context_ = xmlGenericErrorContext; + xmlSetGenericErrorFunc(context, func); + } + ~ScopedXmlErrorFunc() { + xmlSetGenericErrorFunc(old_error_context_, old_error_func_); + } + + private: + xmlGenericErrorFunc old_error_func_; + void* old_error_context_; +}; + +// XmlReader is a wrapper class around libxml's xmlReader, +// providing a simplified C++ API. +class XmlReader { + public: + XmlReader(); + ~XmlReader(); + + // Load a document into the reader from memory. |input| must be UTF-8 and + // exist for the lifetime of this object. Returns false on error. + // TODO(evanm): handle encodings other than UTF-8? + bool Load(const std::string& input); + + // Load a document into the reader from a file. Returns false on error. + bool LoadFile(const std::string& file_path); + + // Wrappers around libxml functions ----------------------------------------- + + // Read() advances to the next node. Returns false on EOF or error. + bool Read() { return xmlTextReaderRead(reader_) == 1; } + + // Next(), when pointing at an opening tag, advances to the node after + // the matching closing tag. Returns false on EOF or error. + bool Next() { return xmlTextReaderNext(reader_) == 1; } + + // Return the depth in the tree of the current node. + int Depth() { return xmlTextReaderDepth(reader_); } + + // Returns the "local" name of the current node. + // For a tag like , this is the string "foo:bar". + std::string NodeName() { + return XmlStringToStdString(xmlTextReaderConstLocalName(reader_)); + } + + // When pointing at a tag, retrieves the value of an attribute. + // Returns false on failure. + // E.g. for , NodeAttribute("bar:baz", &value) + // returns true and |value| is set to "a". + bool NodeAttribute(const char* name, std::string* value); + + // Helper functions not provided by libxml ---------------------------------- + + // Return the string content within an element. + // "bar" is a sequence of three nodes: + // (1) open tag, (2) text, (3) close tag. + // With the reader currently at (1), this returns the text of (2), + // and advances past (3). + // Returns false on error. + bool ReadElementContent(std::string* content); + + // Skip to the next opening tag, returning false if we reach a closing + // tag or EOF first. + // If currently on an opening tag, doesn't advance at all. + bool SkipToElement(); + + // Returns the errors reported by libxml, if any. + // (libxml normally just dumps these errors to stderr.) + const std::string& errors() const { return errors_; } + + private: + // A callback for libxml to report errors. + static void GenericErrorCallback(void* context, const char* msg, ...); + + // Returns the libxml node type of the current node. + int NodeType() { return xmlTextReaderNodeType(reader_); } + + // The underlying libxml xmlTextReader. + xmlTextReaderPtr reader_; + + // error_func_ is used to reassign libxml's global error function + // to report errors into |errors_| for the lifetime of this object. + ScopedXmlErrorFunc error_func_; + std::string errors_; +}; + +// XmlWriter is a wrapper class around libxml's xmlWriter, +// providing a simplified C++ API. +// StartWriting must be called before other methods, and StopWriting +// must be called before GetWrittenString() will return results. +class XmlWriter { + public: + XmlWriter(); + ~XmlWriter(); + + // Allocates the xmlTextWriter and an xmlBuffer and starts an XML document. + // This must be called before any other functions. By default, indenting is + // set to true. + void StartWriting(); + + // Ends the XML document and frees the xmlTextWriter. + // This must be called before GetWrittenString() is called. + void StopWriting(); + // Wrappers around libxml functions ----------------------------------------- + + // All following elements will be indented to match their depth. + void StartIndenting() { xmlTextWriterSetIndent(writer_, 1); } + + // All follow elements will not be indented. + void StopIndenting() { xmlTextWriterSetIndent(writer_, 0); } + + // Start an element with the given name. All future elements added will be + // children of this element, until it is ended. Returns false on error. + bool StartElement(const std::string& element_name) { + return xmlTextWriterStartElement(writer_, + BAD_CAST element_name.c_str()) >= 0; + } + + // Ends the current open element. Returns false on error. + bool EndElement() { + return xmlTextWriterEndElement(writer_) >= 0; + } + + // Adds an attribute to the current open element. Returns false on error. + bool AddAttribute(const std::string& attribute_name, + const std::string& attribute_value) { + return xmlTextWriterWriteAttribute(writer_, + BAD_CAST attribute_name.c_str(), + BAD_CAST attribute_value.c_str()) >= 0; + } + + // Adds a new element with name |element_name| and content |content| + // to the buffer. Example: <|element_name|>|content| + // Returns false on errors. + bool WriteElement(const std::string& element_name, + const std::string& content) { + return xmlTextWriterWriteElement(writer_, + BAD_CAST element_name.c_str(), + BAD_CAST content.c_str()) >= 0; + } + + // Helper functions not provided by xmlTextWriter --------------------------- + + // Returns the string that has been written to the buffer. + std::string GetWrittenString() { + if (buffer_ == NULL) + return ""; + return XmlStringToStdString(buffer_->content); + } + + private: + // The underlying libxml xmlTextWriter. + xmlTextWriterPtr writer_; + + // Stores the output. + xmlBufferPtr buffer_; +}; + +#endif // CHROME_COMMON_LIBXML_UTILS_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/logging_chrome.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/logging_chrome.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/logging_chrome.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/logging_chrome.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,202 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "build/build_config.h" + +#if defined(OS_WIN) +#include +#endif + +#include +#include + +#include "chrome/common/logging_chrome.h" + +#include "base/command_line.h" +#include "base/compiler_specific.h" +#include "base/debug_util.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/string_util.h" +#include "base/sys_info.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/env_vars.h" + +// When true, this means that error dialogs should not be shown. +static bool dialogs_are_suppressed_ = false; + +// This should be true for exactly the period between the end of +// InitChromeLogging() and the beginning of CleanupChromeLogging(). +static bool chrome_logging_initialized_ = false; + +// Assertion handler for logging errors that occur when dialogs are +// silenced. To record a new error, pass the log string associated +// with that error in the str parameter. +MSVC_DISABLE_OPTIMIZE(); +static void SilentRuntimeAssertHandler(const std::string& str) { + DebugUtil::BreakDebugger(); +} +static void SilentRuntimeReportHandler(const std::string& str) { +} +MSVC_ENABLE_OPTIMIZE(); + +// Suppresses error/assertion dialogs and enables the logging of +// those errors into silenced_errors_. +static void SuppressDialogs() { + if (dialogs_are_suppressed_) + return; + + logging::SetLogAssertHandler(SilentRuntimeAssertHandler); + logging::SetLogReportHandler(SilentRuntimeReportHandler); + +#if defined(OS_WIN) + UINT new_flags = SEM_FAILCRITICALERRORS | + SEM_NOGPFAULTERRORBOX | + SEM_NOOPENFILEERRORBOX; + + // Preserve existing error mode, as discussed at http://t/dmea + UINT existing_flags = SetErrorMode(new_flags); + SetErrorMode(existing_flags | new_flags); +#endif + + dialogs_are_suppressed_ = true; +} + +namespace logging { + +void InitChromeLogging(const CommandLine& command_line, + OldFileDeletionState delete_old_log_file) { + DCHECK(!chrome_logging_initialized_) << + "Attempted to initialize logging when it was already initialized."; + + // only use OutputDebugString in debug mode +#ifdef NDEBUG + bool enable_logging = false; + const wchar_t *kInvertLoggingSwitch = switches::kEnableLogging; + const logging::LoggingDestination kDefaultLoggingMode = + logging::LOG_ONLY_TO_FILE; +#else + bool enable_logging = true; + const wchar_t *kInvertLoggingSwitch = switches::kDisableLogging; + const logging::LoggingDestination kDefaultLoggingMode = + logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG; +#endif + + if (command_line.HasSwitch(kInvertLoggingSwitch)) + enable_logging = !enable_logging; + + logging::LoggingDestination log_mode; + if (enable_logging) { + log_mode = kDefaultLoggingMode; + } else { + log_mode = logging::LOG_NONE; + } + +#if defined(OS_POSIX) + std::string log_file_name = WideToUTF8(GetLogFileName()); +#elif defined(OS_WIN) + std::wstring log_file_name = GetLogFileName(); +#endif + + logging::InitLogging(log_file_name.c_str(), + log_mode, + logging::LOCK_LOG_FILE, + delete_old_log_file); + + // we want process and thread IDs because we have a lot of things running + logging::SetLogItems(true, true, false, true); + + // We call running in unattended mode "headless", and allow + // headless mode to be configured either by the Environment + // Variable or by the Command Line Switch. This is for + // automated test purposes. + if (base::SysInfo::HasEnvVar(env_vars::kHeadless) || + command_line.HasSwitch(switches::kNoErrorDialogs)) + SuppressDialogs(); + + std::wstring log_filter_prefix = + command_line.GetSwitchValue(switches::kLogFilterPrefix); + logging::SetLogFilterPrefix(WideToUTF8(log_filter_prefix).c_str()); + + // Use a minimum log level if the command line has one, otherwise set the + // default to LOG_WARNING. + std::wstring log_level = command_line.GetSwitchValue(switches::kLoggingLevel); + int level = 0; + if (StringToInt(WideToUTF16Hack(log_level), &level)) { + if ((level >= 0) && (level < LOG_NUM_SEVERITIES)) + logging::SetMinLogLevel(level); + } else { + logging::SetMinLogLevel(LOG_WARNING); + } + + chrome_logging_initialized_ = true; +} + +// This is a no-op, but we'll keep it around in case +// we need to do more cleanup in the future. +void CleanupChromeLogging() { + DCHECK(chrome_logging_initialized_) << + "Attempted to clean up logging when it wasn't initialized."; + + CloseLogFile(); + + chrome_logging_initialized_ = false; +} + +std::wstring GetLogFileName() { + std::wstring filename = base::SysInfo::GetEnvVar(env_vars::kLogFileName); + if (filename != L"") + return filename; + + const std::wstring log_filename(L"chrome_debug.log"); + std::wstring log_path; + + if (PathService::Get(chrome::DIR_LOGS, &log_path)) { + file_util::AppendToPath(&log_path, log_filename); + return log_path; + } else { + // error with path service, just use some default file somewhere + return log_filename; + } +} + +bool DialogsAreSuppressed() { + return dialogs_are_suppressed_; +} + +size_t GetFatalAssertions(AssertionList* assertions) { + // In this function, we don't assume that assertions is non-null, so + // that if you just want an assertion count, you can pass in NULL. + if (assertions) + assertions->clear(); + size_t assertion_count = 0; + + std::ifstream log_file; +#if defined(OS_WIN) + log_file.open(GetLogFileName().c_str()); +#elif defined(OS_POSIX) + log_file.open(WideToUTF8(GetLogFileName()).c_str()); +#endif + if (!log_file.is_open()) + return 0; + + std::string utf8_line; + std::wstring wide_line; + while(!log_file.eof()) { + getline(log_file, utf8_line); + if (utf8_line.find(":FATAL:") != std::string::npos) { + wide_line = UTF8ToWide(utf8_line); + if (assertions) + assertions->push_back(wide_line); + ++assertion_count; + } + } + log_file.close(); + + return assertion_count; +} + +} // namespace logging diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/logging_chrome.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/logging_chrome.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/logging_chrome.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/logging_chrome.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,57 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_LOGGING_CHROME_H__ +#define CHROME_COMMON_LOGGING_CHROME_H__ + +#include +#include + +#include "base/logging.h" + +class CommandLine; + +namespace logging { + +// Call to initialize logging for Chrome. This sets up the chrome-specific +// logfile naming scheme and might do other things like log modules and +// setting levels in the future. +// +// The main process might want to delete any old log files on startup by +// setting delete_old_log_file, but the renderer processes should not, or +// they will delete each others' logs. +// +// XXX +// Setting suppress_error_dialogs to true disables any dialogs that would +// normally appear for assertions and crashes, and makes any catchable +// errors (namely assertions) available via GetSilencedErrorCount() +// and GetSilencedError(). +void InitChromeLogging(const CommandLine& command_line, + OldFileDeletionState delete_old_log_file); + +// Call when done using logging for Chrome. +void CleanupChromeLogging(); + +// Returns the fully-qualified name of the log file. +std::wstring GetLogFileName(); + +// Returns true when error/assertion dialogs are to be shown, +// false otherwise. +bool DialogsAreSuppressed(); + +typedef std::vector AssertionList; + +// Gets the list of fatal assertions in the current log file, and +// returns the number of fatal assertions. (If you don't care +// about the actual list of assertions, you can pass in NULL.) +// NOTE: Since this reads the log file to determine the assertions, +// this operation is O(n) over the length of the log. +// NOTE: This can fail if the file is locked for writing. However, +// this is unlikely as this function is most useful after +// the program writing the log has terminated. +size_t GetFatalAssertions(AssertionList* assertions); + +} // namespace logging + +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/logging_chrome_uitest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/logging_chrome_uitest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/logging_chrome_uitest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/logging_chrome_uitest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,134 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include + +#include "base/command_line.h" +#include "base/basictypes.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/env_vars.h" +#include "chrome/common/logging_chrome.h" +#include "chrome/test/ui/ui_test.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + class ChromeLoggingTest : public testing::Test { + public: + // Stores the current value of the log file name environment + // variable and sets the variable to new_value. + void SaveEnvironmentVariable(std::wstring new_value) { + unsigned status = GetEnvironmentVariable(env_vars::kLogFileName, + environment_filename_, + MAX_PATH); + if (!status) { + wcscpy_s(environment_filename_, L""); + } + + SetEnvironmentVariable(env_vars::kLogFileName, new_value.c_str()); + } + + // Restores the value of the log file nave environment variable + // previously saved by SaveEnvironmentVariable(). + void RestoreEnvironmentVariable() { + SetEnvironmentVariable(env_vars::kLogFileName, environment_filename_); + } + + private: + wchar_t environment_filename_[MAX_PATH]; // Saves real environment value. + }; +}; + +// Tests the log file name getter without an environment variable. +TEST_F(ChromeLoggingTest, LogFileName) { + SaveEnvironmentVariable(std::wstring()); + + std::wstring filename = logging::GetLogFileName(); + ASSERT_NE(std::wstring::npos, filename.find(L"chrome_debug.log")); + + RestoreEnvironmentVariable(); +} + +// Tests the log file name getter with an environment variable. +TEST_F(ChromeLoggingTest, EnvironmentLogFileName) { + SaveEnvironmentVariable(std::wstring(L"test value")); + + std::wstring filename = logging::GetLogFileName(); + ASSERT_EQ(std::wstring(L"test value"), filename); + + RestoreEnvironmentVariable(); +} + +#ifndef NDEBUG // We don't have assertions in release builds. +// Tests whether we correctly fail on browser assertions during tests. +class AssertionTest : public UITest { + protected: + AssertionTest() : UITest() + { + // Initial loads will never complete due to assertion. + wait_for_initial_loads_ = false; + + // We're testing the renderer rather than the browser assertion here, + // because the browser assertion would flunk the test during SetUp() + // (since TAU wouldn't be able to find the browser window). + launch_arguments_.AppendSwitch(switches::kRendererAssertTest); + } +}; + +// Launch the app in assertion test mode, then close the app. +TEST_F(AssertionTest, Assertion) { + if (UITest::in_process_renderer()) { + // in process mode doesn't do the crashing. + expected_errors_ = 0; + expected_crashes_ = 0; + } else { + expected_errors_ = 1; + expected_crashes_ = 1; + } +} +#endif // NDEBUG + +// Tests whether we correctly fail on browser crashes during UI Tests. +class RendererCrashTest : public UITest { + protected: + RendererCrashTest() : UITest() + { + // Initial loads will never complete due to crash. + wait_for_initial_loads_ = false; + + launch_arguments_.AppendSwitch(switches::kRendererCrashTest); + } +}; + +// Launch the app in renderer crash test mode, then close the app. +TEST_F(RendererCrashTest, Crash) { + if (UITest::in_process_renderer()) { + // in process mode doesn't do the crashing. + expected_crashes_ = 0; + } else { + // Wait while the process is writing the crash dump. + PlatformThread::Sleep(5000); + expected_crashes_ = 1; + } +} + +// Tests whether we correctly fail on browser crashes during UI Tests. +class BrowserCrashTest : public UITest { + protected: + BrowserCrashTest() : UITest() + { + // Initial loads will never complete due to crash. + wait_for_initial_loads_ = false; + + launch_arguments_.AppendSwitch(switches::kBrowserCrashTest); + } +}; + +// Launch the app in browser crash test mode. +// This test is disabled. See bug 1198934. +TEST_F(BrowserCrashTest, DISABLED_Crash) { + // Wait while the process is writing the crash dump. + PlatformThread::Sleep(5000); + expected_crashes_ = 1; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/mach_ipc_mac.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/mach_ipc_mac.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/mach_ipc_mac.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/mach_ipc_mac.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,320 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_MACH_IPC_MAC_H_ +#define BASE_MACH_IPC_MAC_H_ + +#include +#include +#include +#include + +#include + +#include "base/basictypes.h" + +//============================================================================== +// DISCUSSION: +// +// The three main classes of interest are +// +// MachMessage: a wrapper for a mach message of the following form +// mach_msg_header_t +// mach_msg_body_t +// optional descriptors +// optional extra message data +// +// MachReceiveMessage and MachSendMessage subclass MachMessage +// and are used instead of MachMessage which is an abstract base class +// +// ReceivePort: +// Represents a mach port for which we have receive rights +// +// MachPortSender: +// Represents a mach port for which we have send rights +// +// Here's an example to receive a message on a server port: +// +// // This creates our named server port +// ReceivePort receivePort("com.Google.MyService"); +// +// MachReceiveMessage message; +// kern_return_t result = receivePort.WaitForMessage(&message, 0); +// +// if (result == KERN_SUCCESS && message.GetMessageID() == 57) { +// mach_port_t task = message.GetTranslatedPort(0); +// mach_port_t thread = message.GetTranslatedPort(1); +// +// char *messageString = message.GetData(); +// +// printf("message string = %s\n", messageString); +// } +// +// Here is an example of using these classes to send a message to this port: +// +// // send to already named port +// MachPortSender sender("com.Google.MyService"); +// MachSendMessage message(57); // our message ID is 57 +// +// // add some ports to be translated for us +// message.AddDescriptor(mach_task_self()); // our task +// message.AddDescriptor(mach_thread_self()); // this thread +// +// char messageString[] = "Hello server!\n"; +// message.SetData(messageString, strlen(messageString)+1); +// // timeout 1000ms +// kern_return_t result = sender.SendMessage(message, 1000); +// + +#define PRINT_MACH_RESULT(result_, message_) \ + printf(message_" %s (%d)\n", mach_error_string(result_), result_ ); + +//============================================================================== +// A wrapper class for mach_msg_port_descriptor_t (with same memory layout) +// with convenient constructors and accessors +class MachMsgPortDescriptor : public mach_msg_port_descriptor_t { + public: + // General-purpose constructor + MachMsgPortDescriptor(mach_port_t in_name, + mach_msg_type_name_t in_disposition) { + name = in_name; + pad1 = 0; + pad2 = 0; + disposition = in_disposition; + type = MACH_MSG_PORT_DESCRIPTOR; + } + + // For passing send rights to a port + MachMsgPortDescriptor(mach_port_t in_name) { + name = in_name; + pad1 = 0; + pad2 = 0; + disposition = MACH_MSG_TYPE_PORT_SEND; + type = MACH_MSG_PORT_DESCRIPTOR; + } + + // Copy constructor + MachMsgPortDescriptor(const MachMsgPortDescriptor& desc) { + name = desc.name; + pad1 = desc.pad1; + pad2 = desc.pad2; + disposition = desc.disposition; + type = desc.type; + } + + mach_port_t GetMachPort() const { + return name; + } + + mach_msg_type_name_t GetDisposition() const { + return disposition; + } + + // We're just a simple wrapper for mach_msg_port_descriptor_t + // and have the same memory layout + operator mach_msg_port_descriptor_t&() { + return *this; + } + + // For convenience + operator mach_port_t() const { + return GetMachPort(); + } +}; + +//============================================================================== +// MachMessage: a wrapper for a mach message +// (mach_msg_header_t, mach_msg_body_t, extra data) +// +// This considerably simplifies the construction of a message for sending +// and the getting at relevant data and descriptors for the receiver. +// +// This class can be initialized using external storage of an arbitrary size +// or it can manage storage internally. +// 1. If storage is allocated internally, the combined size of the descriptors +// plus data must be less than 1024. But as a benefit no memory allocation is +// necessary. +// 2. For external storage, a buffer of at least EmptyMessageSize() must be +// provided. +// +// A MachMessage object is used by ReceivePort::WaitForMessage +// and MachPortSender::SendMessage +// +class MachMessage { + public: + + virtual ~MachMessage(); + + // The receiver of the message can retrieve the raw data this way + u_int8_t *GetData() { + return GetDataLength() > 0 ? GetDataPacket()->data : NULL; + } + + u_int32_t GetDataLength() { + return EndianU32_LtoN(GetDataPacket()->data_length); + } + + // The message ID may be used as a code identifying the type of message + void SetMessageID(int32_t message_id) { + GetDataPacket()->id = EndianU32_NtoL(message_id); + } + + int32_t GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); } + + // Adds a descriptor (typically a mach port) to be translated + // returns true if successful, otherwise not enough space + bool AddDescriptor(const MachMsgPortDescriptor &desc); + + int GetDescriptorCount() const { + return storage_->body.msgh_descriptor_count; + } + MachMsgPortDescriptor *GetDescriptor(int n); + + // Convenience method which gets the mach port described by the descriptor + mach_port_t GetTranslatedPort(int n); + + // A simple message is one with no descriptors + bool IsSimpleMessage() const { return GetDescriptorCount() == 0; } + + // Sets raw data for the message (returns false if not enough space) + bool SetData(const void* data, int32_t data_length); + + protected: + // Consider this an abstract base class - must create an actual instance + // of MachReceiveMessage or MachSendMessage + MachMessage(); + + // Constructor for use with preallocate storage. + // storage_length must be >= EmptyMessageSize() + MachMessage(void *storage, size_t storage_length); + + friend class ReceivePort; + friend class MachPortSender; + + // Represents raw data in our message + struct MessageDataPacket { + int32_t id; // little-endian + int32_t data_length; // little-endian + u_int8_t data[1]; // actual size limited by storage_length_bytes_ + }; + + MessageDataPacket* GetDataPacket(); + + void SetDescriptorCount(int n); + void SetDescriptor(int n, const MachMsgPortDescriptor &desc); + + // Returns total message size setting msgh_size in the header to this value + int CalculateSize(); + + // Returns total storage size that this object can grow to, this is inclusive + // of the mach header. + size_t MaxSize() const { return storage_length_bytes_; } + + protected: + mach_msg_header_t *Head() { return &(storage_->head); } + + private: + struct MachMessageData { + mach_msg_header_t head; + mach_msg_body_t body; + // descriptors and data may be embedded here. + u_int8_t padding[1024]; + }; + + // kEmptyMessageSize needs to have the definition of MachMessageData before + // it. + public: + // The size of an empty message with no data. + static const size_t kEmptyMessageSize = sizeof(mach_msg_header_t) + + sizeof(mach_msg_body_t) + + sizeof(MessageDataPacket); + + private: + MachMessageData *storage_; + size_t storage_length_bytes_; + bool own_storage_; // Is storage owned by this object? +}; + +//============================================================================== +// MachReceiveMessage and MachSendMessage are useful to separate the idea +// of a mach message being sent and being received, and adds increased type +// safety: +// ReceivePort::WaitForMessage() only accepts a MachReceiveMessage +// MachPortSender::SendMessage() only accepts a MachSendMessage + +//============================================================================== +class MachReceiveMessage : public MachMessage { + public: + MachReceiveMessage() : MachMessage() {} + MachReceiveMessage(void *storage, size_t storage_length) + : MachMessage(storage, storage_length) {} + + private: + DISALLOW_COPY_AND_ASSIGN(MachReceiveMessage); +}; + +//============================================================================== +class MachSendMessage : public MachMessage { + public: + MachSendMessage(int32_t message_id); + MachSendMessage(void *storage, size_t storage_length, int32_t message_id); + + private: + void Initialize(int32_t message_id); + + DISALLOW_COPY_AND_ASSIGN(MachSendMessage); +}; + +//============================================================================== +// Represents a mach port for which we have receive rights +class ReceivePort { + public: + // Creates a new mach port for receiving messages and registers a name for it + ReceivePort(const char *receive_port_name); + + // Given an already existing mach port, use it. We take ownership of the + // port and deallocate it in our destructor. + ReceivePort(mach_port_t receive_port); + + // Create a new mach port for receiving messages + ReceivePort(); + + ~ReceivePort(); + + // Waits on the mach port until message received or timeout + kern_return_t WaitForMessage(MachReceiveMessage *out_message, + mach_msg_timeout_t timeout); + + // The underlying mach port that we wrap + mach_port_t GetPort() const { return port_; } + + private: + mach_port_t port_; + kern_return_t init_result_; + + DISALLOW_COPY_AND_ASSIGN(ReceivePort); +}; + +//============================================================================== +// Represents a mach port for which we have send rights +class MachPortSender { + public: + // get a port with send rights corresponding to a named registered service + MachPortSender(const char *receive_port_name); + + + // Given an already existing mach port, use it. + MachPortSender(mach_port_t send_port); + + kern_return_t SendMessage(MachSendMessage &message, + mach_msg_timeout_t timeout); + + private: + mach_port_t send_port_; + kern_return_t init_result_; + + DISALLOW_COPY_AND_ASSIGN(MachPortSender); +}; + +#endif // BASE_MACH_IPC_MAC_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/mach_ipc_mac.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/mach_ipc_mac.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/mach_ipc_mac.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/mach_ipc_mac.mm 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,308 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/mach_ipc_mac.h" + +#import + +#include +#include "base/logging.h" + +//============================================================================== +MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() { + Initialize(message_id); +} + +MachSendMessage::MachSendMessage(void *storage, size_t storage_length, + int32_t message_id) + : MachMessage(storage, storage_length) { + Initialize(message_id); +} + +void MachSendMessage::Initialize(int32_t message_id) { + Head()->msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); + + // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage() + Head()->msgh_local_port = MACH_PORT_NULL; + Head()->msgh_reserved = 0; + Head()->msgh_id = 0; + + SetDescriptorCount(0); // start out with no descriptors + + SetMessageID(message_id); + SetData(NULL, 0); // client may add data later +} + +//============================================================================== +MachMessage::MachMessage() + : storage_(new MachMessageData), // Allocate storage_ ourselves + storage_length_bytes_(sizeof(MachMessageData)), + own_storage_(true) { + memset(storage_, 0, storage_length_bytes_); +} + +//============================================================================== +MachMessage::MachMessage(void *storage, size_t storage_length) + : storage_(static_cast(storage)), + storage_length_bytes_(storage_length), + own_storage_(false) { + DCHECK(storage); + DCHECK(storage_length >= kEmptyMessageSize); +} + +//============================================================================== +MachMessage::~MachMessage() { + if (own_storage_) { + delete storage_; + storage_ = NULL; + } +} + +//============================================================================== +// returns true if successful +bool MachMessage::SetData(const void* data, + int32_t data_length) { + // Enforce the fact that it's only safe to call this method once on a + // message. + DCHECK(GetDataPacket()->data_length == 0); + + // first check to make sure we have enough space + int size = CalculateSize(); + int new_size = size + data_length; + + if ((unsigned)new_size > storage_length_bytes_) { + return false; // not enough space + } + + GetDataPacket()->data_length = EndianU32_NtoL(data_length); + if (data) memcpy(GetDataPacket()->data, data, data_length); + + // Update the Mach header with the new aligned size of the message. + CalculateSize(); + + return true; +} + +//============================================================================== +// calculates and returns the total size of the message +// Currently, the entire message MUST fit inside of the MachMessage +// messsage size <= EmptyMessageSize() +int MachMessage::CalculateSize() { + int size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t); + + // add space for MessageDataPacket + int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3; + size += 2*sizeof(int32_t) + alignedDataLength; + + // add space for descriptors + size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor); + + Head()->msgh_size = size; + + return size; +} + +//============================================================================== +MachMessage::MessageDataPacket *MachMessage::GetDataPacket() { + int desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount(); + MessageDataPacket *packet = + reinterpret_cast(storage_->padding + desc_size); + + return packet; +} + +//============================================================================== +void MachMessage::SetDescriptor(int n, + const MachMsgPortDescriptor &desc) { + MachMsgPortDescriptor *desc_array = + reinterpret_cast(storage_->padding); + desc_array[n] = desc; +} + +//============================================================================== +// returns true if successful otherwise there was not enough space +bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) { + // first check to make sure we have enough space + int size = CalculateSize(); + int new_size = size + sizeof(MachMsgPortDescriptor); + + if ((unsigned)new_size > storage_length_bytes_) { + return false; // not enough space + } + + // unfortunately, we need to move the data to allow space for the + // new descriptor + u_int8_t *p = reinterpret_cast(GetDataPacket()); + bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t)); + + SetDescriptor(GetDescriptorCount(), desc); + SetDescriptorCount(GetDescriptorCount() + 1); + + CalculateSize(); + + return true; +} + +//============================================================================== +void MachMessage::SetDescriptorCount(int n) { + storage_->body.msgh_descriptor_count = n; + + if (n > 0) { + Head()->msgh_bits |= MACH_MSGH_BITS_COMPLEX; + } else { + Head()->msgh_bits &= ~MACH_MSGH_BITS_COMPLEX; + } +} + +//============================================================================== +MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) { + if (n < GetDescriptorCount()) { + MachMsgPortDescriptor *desc = + reinterpret_cast(storage_->padding); + return desc + n; + } + + return nil; +} + +//============================================================================== +mach_port_t MachMessage::GetTranslatedPort(int n) { + if (n < GetDescriptorCount()) { + return GetDescriptor(n)->GetMachPort(); + } + return MACH_PORT_NULL; +} + +#pragma mark - + +//============================================================================== +// create a new mach port for receiving messages and register a name for it +ReceivePort::ReceivePort(const char *receive_port_name) { + mach_port_t current_task = mach_task_self(); + + init_result_ = mach_port_allocate(current_task, + MACH_PORT_RIGHT_RECEIVE, + &port_); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = mach_port_insert_right(current_task, + port_, + port_, + MACH_MSG_TYPE_MAKE_SEND); + + if (init_result_ != KERN_SUCCESS) + return; + + NSPort *ns_port = [NSMachPort portWithMachPort:port_]; + NSString *port_name = [NSString stringWithUTF8String:receive_port_name]; + [[NSMachBootstrapServer sharedInstance] registerPort:ns_port name:port_name]; +} + +//============================================================================== +// create a new mach port for receiving messages +ReceivePort::ReceivePort() { + mach_port_t current_task = mach_task_self(); + + init_result_ = mach_port_allocate(current_task, + MACH_PORT_RIGHT_RECEIVE, + &port_); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = mach_port_insert_right(current_task, + port_, + port_, + MACH_MSG_TYPE_MAKE_SEND); +} + +//============================================================================== +// Given an already existing mach port, use it. We take ownership of the +// port and deallocate it in our destructor. +ReceivePort::ReceivePort(mach_port_t receive_port) + : port_(receive_port), + init_result_(KERN_SUCCESS) { +} + +//============================================================================== +ReceivePort::~ReceivePort() { + if (init_result_ == KERN_SUCCESS) + mach_port_deallocate(mach_task_self(), port_); +} + +//============================================================================== +kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message, + mach_msg_timeout_t timeout) { + if (!out_message) { + return KERN_INVALID_ARGUMENT; + } + + // return any error condition encountered in constructor + if (init_result_ != KERN_SUCCESS) + return init_result_; + + out_message->Head()->msgh_bits = 0; + out_message->Head()->msgh_local_port = port_; + out_message->Head()->msgh_remote_port = MACH_PORT_NULL; + out_message->Head()->msgh_reserved = 0; + out_message->Head()->msgh_id = 0; + + kern_return_t result = mach_msg(out_message->Head(), + MACH_RCV_MSG | MACH_RCV_TIMEOUT, + 0, + out_message->MaxSize(), + port_, + timeout, // timeout in ms + MACH_PORT_NULL); + + return result; +} + +#pragma mark - + +//============================================================================== +// get a port with send rights corresponding to a named registered service +MachPortSender::MachPortSender(const char *receive_port_name) { + mach_port_t bootstrap_port = 0; + init_result_ = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = bootstrap_look_up(bootstrap_port, + const_cast(receive_port_name), + &send_port_); +} + +//============================================================================== +MachPortSender::MachPortSender(mach_port_t send_port) + : send_port_(send_port), + init_result_(KERN_SUCCESS) { +} + +//============================================================================== +kern_return_t MachPortSender::SendMessage(MachSendMessage &message, + mach_msg_timeout_t timeout) { + if (message.Head()->msgh_size == 0) { + NOTREACHED(); + return KERN_INVALID_VALUE; // just for safety -- never should occur + }; + + if (init_result_ != KERN_SUCCESS) + return init_result_; + + message.Head()->msgh_remote_port = send_port_; + + kern_return_t result = mach_msg(message.Head(), + MACH_SEND_MSG | MACH_SEND_TIMEOUT, + message.Head()->msgh_size, + 0, + MACH_PORT_NULL, + timeout, // timeout in ms + MACH_PORT_NULL); + + return result; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/mach_message_source_mac.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/mach_message_source_mac.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/mach_message_source_mac.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/mach_message_source_mac.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,66 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/mach_message_source_mac.h" + +#include "base/logging.h" + +MachMessageSource::MachMessageSource(mach_port_t port, + MachPortListener* msg_listener, + bool* success) { + DCHECK(msg_listener); + DCHECK(success); + DCHECK(port != MACH_PORT_NULL); + + CFMachPortContext port_context = {0}; + port_context.info = msg_listener; + + scoped_cftyperef cf_mach_port_ref( + CFMachPortCreateWithPort(kCFAllocatorDefault, + port, + MachMessageSource::OnReceiveMachMessage, + &port_context, + NULL)); + + if (cf_mach_port_ref.get() == NULL) { + LOG(WARNING) << "CFMachPortCreate failed"; + *success = false; + return; + } + + // Create a RL source. + machport_runloop_ref_.reset( + CFMachPortCreateRunLoopSource(kCFAllocatorDefault, + cf_mach_port_ref.get(), + 0)); + + if (machport_runloop_ref_.get() == NULL) { + LOG(WARNING) << "CFMachPortCreateRunLoopSource failed"; + *success = false; + return; + } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), + machport_runloop_ref_.get(), + kCFRunLoopCommonModes); + *success = true; +} + +MachMessageSource::~MachMessageSource() { + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), + machport_runloop_ref_.get(), + kCFRunLoopCommonModes); +} + +// static +void MachMessageSource::OnReceiveMachMessage(CFMachPortRef port, void* msg, + CFIndex size, void* closure) { + MachPortListener *msg_listener = static_cast(closure); + size_t msg_size = (size < 0) ? 0 : static_cast(size); + DCHECK(msg && msg_size > 0); // this should never happen! + + if (msg_listener && msg && msg_size > 0) { + msg_listener->OnMachMessageReceived(msg, msg_size); + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/mach_message_source_mac.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/mach_message_source_mac.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/mach_message_source_mac.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/mach_message_source_mac.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,59 @@ +// Copyright (c) 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_MACH_MESSAGE_SOURCE_MAC_H_ +#define CHROME_COMMON_MACH_MESSAGE_SOURCE_MAC_H_ + +#include + +#include "base/scoped_cftyperef.h" + +// Handles registering and cleaning up after a CFRunloopSource for a Mach port. +// Messages received on the port are piped through to a delegate. +// +// Example: +// class MyListener : public MachMessageSource::MachPortListener { +// public: +// void OnMachMessageReceived(void* mach_msg, size_t size) { +// printf("received message on Mach port\n"); +// } +// }; +// +// mach_port_t a_port = ...; +// MyListener listener; +// bool success = false; +// MachMessageSource message_source(port, listener, &success); +// +// if (!success) { +// exit(1); // Couldn't register mach runloop source. +// } +// +// CFRunLoopRun(); // Process messages on runloop... +class MachMessageSource { + public: + // Classes that want to listen on a Mach port can implement + // OnMachMessageReceived, |mach_msg| is a pointer to the raw message data and + // |size| is the buffer size; + class MachPortListener { + public: + virtual void OnMachMessageReceived(void* mach_msg, size_t size) = 0; + }; + + // |listener| is a week reference passed to CF, it needs to remain in + // existence till this object is destroeyd. + MachMessageSource(mach_port_t port, + MachPortListener* listener, + bool* success); + ~MachMessageSource(); + + private: + // Called by CF when a new message arrives on the Mach port. + static void OnReceiveMachMessage(CFMachPortRef port, void* msg, CFIndex size, + void* closure); + + scoped_cftyperef machport_runloop_ref_; + DISALLOW_COPY_AND_ASSIGN(MachMessageSource); +}; + +#endif // CHROME_COMMON_MACH_MESSAGE_SOURCE_MAC_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/main_function_params.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/main_function_params.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/main_function_params.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/main_function_params.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,33 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Wrapper to the parameter list for the "main" entry points (browser, renderer, +// plugin) to shield the call sites from the differences between platforms +// (e.g., POSIX doesn't need to pass any sandbox information). + +#ifndef CHROME_COMMON_MAIN_FUNCTINON_PARAMS_H_ +#define CHROME_COMMON_MAIN_FUNCTINON_PARAMS_H_ + +#include "base/command_line.h" +#include "chrome/common/sandbox_init_wrapper.h" + +namespace base { +class ScopedNSAutoreleasePool; +}; +class Task; + +struct MainFunctionParams { + MainFunctionParams(const CommandLine& cl, const SandboxInitWrapper& sb, + base::ScopedNSAutoreleasePool* pool) + : command_line_(cl), sandbox_info_(sb), autorelease_pool_(pool), + ui_task(NULL) { } + const CommandLine& command_line_; + const SandboxInitWrapper& sandbox_info_; + base::ScopedNSAutoreleasePool* autorelease_pool_; + // Used by InProcessBrowserTest. If non-null BrowserMain schedules this + // task to run on the MessageLoop and BrowserInit is not invoked. + Task* ui_task; +}; + +#endif // CHROME_COMMON_MAIN_FUNCTINON_PARAMS_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/message_router.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/message_router.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/message_router.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/message_router.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,45 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/message_router.h" +#ifndef CHROMIUM_MOZILLA_BUILD +#include "chrome/common/render_messages.h" +#endif + +void MessageRouter::OnControlMessageReceived(const IPC::Message& msg) { + NOTREACHED() << + "should override in subclass if you care about control messages"; +} + +bool MessageRouter::Send(IPC::Message* msg) { + NOTREACHED() << + "should override in subclass if you care about sending messages"; + return false; +} + +void MessageRouter::AddRoute(int32 routing_id, + IPC::Channel::Listener* listener) { + routes_.AddWithID(listener, routing_id); +} + +void MessageRouter::RemoveRoute(int32 routing_id) { + routes_.Remove(routing_id); +} + +void MessageRouter::OnMessageReceived(const IPC::Message& msg) { + if (msg.routing_id() == MSG_ROUTING_CONTROL) { + OnControlMessageReceived(msg); + } else { + RouteMessage(msg); + } +} + +bool MessageRouter::RouteMessage(const IPC::Message& msg) { + IPC::Channel::Listener* listener = routes_.Lookup(msg.routing_id()); + if (!listener) + return false; + + listener->OnMessageReceived(msg); + return true; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/message_router.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/message_router.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/message_router.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/message_router.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,60 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_MESSAGE_ROUTER_H__ +#define CHROME_COMMON_MESSAGE_ROUTER_H__ + +#include "base/id_map.h" +#include "chrome/common/ipc_channel.h" + +// The MessageRouter handles all incoming messages sent to it by routing them +// to the correct listener. Routing is based on the Message's routing ID. +// Since routing IDs are typically assigned asynchronously by the browser +// process, the MessageRouter has the notion of pending IDs for listeners that +// have not yet been assigned a routing ID. +// +// When a message arrives, the routing ID is used to index the set of routes to +// find a listener. If a listener is found, then the message is passed to it. +// Otherwise, the message is ignored if its routing ID is not equal to +// MSG_ROUTING_CONTROL. +// +// The MessageRouter supports the IPC::Message::Sender interface for outgoing +// messages, but does not define a meaningful implementation of it. The +// subclass of MessageRouter is intended to provide that if appropriate. +// +// The MessageRouter can be used as a concrete class provided its Send method +// is not called and it does not receive any control messages. + +class MessageRouter : public IPC::Channel::Listener, + public IPC::Message::Sender { + public: + MessageRouter() {} + virtual ~MessageRouter() {} + + // Implemented by subclasses to handle control messages + virtual void OnControlMessageReceived(const IPC::Message& msg); + + // IPC::Channel::Listener implementation: + virtual void OnMessageReceived(const IPC::Message& msg); + + // Like OnMessageReceived, except it only handles routed messages. Returns + // true if the message was dispatched, or false if there was no listener for + // that route id. + virtual bool RouteMessage(const IPC::Message& msg); + + // IPC::Message::Sender implementation: + virtual bool Send(IPC::Message* msg); + + // Called to add/remove a listener for a particular message routing ID. + void AddRoute(int32 routing_id, IPC::Channel::Listener* listener); + void RemoveRoute(int32 routing_id); + + private: + // A list of all listeners with assigned routing IDs. + IDMap routes_; + + DISALLOW_EVIL_CONSTRUCTORS(MessageRouter); +}; + +#endif // CHROME_COMMON_MESSAGE_ROUTER_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/modal_dialog_event.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/modal_dialog_event.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/modal_dialog_event.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/modal_dialog_event.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,20 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_MODAL_DIALOG_EVENT_H_ +#define CHROME_COMMON_MODAL_DIALOG_EVENT_H_ + +// This structure is passed around where we need a modal dialog event, which +// is currently not plumbed on Mac & Linux. +// +// TODO(port) Fix this. This structure should probably go away and we should +// do the modal dialog event in some portable way. If you remove this, be +// sure to also remove the ParamTraits for it in resource_messages.h +struct ModalDialogEvent { +#if defined(OS_WIN) + HANDLE event; +#endif +}; + +#endif // CHROME_COMMON_MODAL_DIALOG_EVENT_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/mru_cache.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/mru_cache.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/mru_cache.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/mru_cache.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,243 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file contains a template for a Most Recently Used cache that allows +// constant-time access to items using a key, but easy identification of the +// least-recently-used items for removal. Each key can only be associated with +// one payload item at a time. +// +// The key object will be stored twice, so it should support efficient copying. +// +// NOTE: While all operations are O(1), this code is written for +// legibility rather than optimality. If future profiling identifies this as +// a bottleneck, there is room for smaller values of 1 in the O(1). :] + +#ifndef CHROME_COMMON_MRU_CACHE_H__ +#define CHROME_COMMON_MRU_CACHE_H__ + +#include +#include +#include + +#include "base/basictypes.h" +#include "chrome/common/logging_chrome.h" + +// MRUCacheBase ---------------------------------------------------------------- + +// Base class for the MRU cache specializations defined below. +// The deletor will get called on all payloads that are being removed or +// replaced. +template +class MRUCacheBase { + public: + // The payload of the list. This maintains a copy of the key so we can + // efficiently delete things given an element of the list. + typedef std::pair value_type; + + private: + typedef std::list PayloadList; + typedef std::map KeyIndex; + + public: + typedef typename PayloadList::size_type size_type; + + typedef typename PayloadList::iterator iterator; + typedef typename PayloadList::const_iterator const_iterator; + typedef typename PayloadList::reverse_iterator reverse_iterator; + typedef typename PayloadList::const_reverse_iterator const_reverse_iterator; + + enum { NO_AUTO_EVICT = 0 }; + + // The max_size is the size at which the cache will prune its members to when + // a new item is inserted. If the caller wants to manager this itself (for + // example, maybe it has special work to do when something is evicted), it + // can pass NO_AUTO_EVICT to not restrict the cache size. + MRUCacheBase(size_type max_size) : max_size_(max_size) { + } + + virtual ~MRUCacheBase() { + iterator i = begin(); + while (i != end()) + i = Erase(i); + } + + // Inserts a payload item with the given key. If an existing item has + // the same key, it is removed prior to insertion. An iterator indicating the + // inserted item will be returned (this will always be the front of the list). + // + // The payload will be copied. In the case of an OwningMRUCache, this function + // will take ownership of the pointer. + iterator Put(const KeyType& key, const PayloadType& payload) { + // Remove any existing payload with that key. + typename KeyIndex::iterator index_iter = index_.find(key); + if (index_iter != index_.end()) { + // Erase the reference to it. This will call the deletor on the removed + // element. The index reference will be replaced in the code below. + Erase(index_iter->second); + } else if (max_size_ != NO_AUTO_EVICT) { + // New item is being inserted which might make it larger than the maximum + // size: kick the oldest thing out if necessary. + ShrinkToSize(max_size_ - 1); + } + + ordering_.push_front(value_type(key, payload)); + index_[key] = ordering_.begin(); + return ordering_.begin(); + } + + // Retrieves the contents of the given key, or end() if not found. This method + // has the side effect of moving the requested item to the front of the + // recency list. + // + // TODO(brettw) We may want a const version of this function in the future. + iterator Get(const KeyType& key) { + typename KeyIndex::iterator index_iter = index_.find(key); + if (index_iter == index_.end()) + return end(); + typename PayloadList::iterator iter = index_iter->second; + + // Move the touched item to the front of the recency ordering. + ordering_.splice(ordering_.begin(), ordering_, iter); + return ordering_.begin(); + } + + // Retrieves the payload associated with a given key and returns it via + // result without affecting the ordering (unlike Get). + // + // TODO(brettw) We may want a const version of this function in the future. + iterator Peek(const KeyType& key) { + typename KeyIndex::const_iterator index_iter = index_.find(key); + if (index_iter == index_.end()) + return end(); + return index_iter->second; + } + + // Erases the item referenced by the given iterator. An iterator to the item + // following it will be returned. The iterator must be valid. + iterator Erase(iterator pos) { + deletor_(pos->second); + index_.erase(pos->first); + return ordering_.erase(pos); + } + + // MRUCache entries are often processed in reverse order, so we add this + // convenience function (not typically defined by STL containers). + reverse_iterator Erase(reverse_iterator pos) { + // We have to actually give it the incremented iterator to delete, since + // the forward iterator that base() returns is actually one past the item + // being iterated over. + return reverse_iterator(Erase((++pos).base())); + } + + // Shrinks the cache so it only holds |new_size| items. If |new_size| is + // bigger or equal to the current number of items, this will do nothing. + void ShrinkToSize(size_type new_size) { + for (size_type i = size(); i > new_size; i--) + Erase(rbegin()); + } + + // Returns the number of elements in the cache. + size_type size() const { + // We don't use ordering_.size() for the return value because + // (as a linked list) it can be O(n). + DCHECK(index_.size() == ordering_.size()); + return index_.size(); + } + + // Allows iteration over the list. Forward iteration starts with the most + // recent item and works backwards. + // + // Note that since these iterators are actually iterators over a list, you + // can keep them as you insert or delete things (as long as you don't delete + // the one you are pointing to) and they will still be valid. + iterator begin() { return ordering_.begin(); } + const_iterator begin() const { ordering_.begin(); } + iterator end() { return ordering_.end(); } + const_iterator end() const { return ordering_.end(); } + + reverse_iterator rbegin() { return ordering_.rbegin(); } + const_reverse_iterator rbegin() const { ordering_.rbegin(); } + reverse_iterator rend() { return ordering_.rend(); } + const_reverse_iterator rend() const { return ordering_.rend(); } + + bool empty() const { return ordering_.empty(); } + + private: + PayloadList ordering_; + KeyIndex index_; + + size_type max_size_; + + DeletorType deletor_; + + DISALLOW_EVIL_CONSTRUCTORS(MRUCacheBase); +}; + +// MRUCache -------------------------------------------------------------------- + +// A functor that does nothing. Used by the MRUCache. +template +class MRUCacheNullDeletor { + public: + void operator()(PayloadType& payload) { + } +}; + +// A container that does not do anything to free its data. Use this when storing +// value types (as opposed to pointers) in the list. +template +class MRUCache : public MRUCacheBase > { + private: + typedef MRUCacheBase > ParentType; + + public: + // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT. + MRUCache(typename ParentType::size_type max_size) + : ParentType(max_size) { + } + virtual ~MRUCache() { + } + + private: + DISALLOW_COPY_AND_ASSIGN(MRUCache); +}; + +// OwningMRUCache -------------------------------------------------------------- + +template +class MRUCachePointerDeletor { + public: + void operator()(PayloadType& payload) { + delete payload; + } +}; + +// A cache that owns the payload type, which must be a non-const pointer type. +// The pointers will be deleted when they are removed, replaced, or when the +// cache is destroyed. +template +class OwningMRUCache + : public MRUCacheBase > { + private: + typedef MRUCacheBase > ParentType; + + public: + // See MRUCacheBase, noting the possibility of using NO_AUTO_EVICT. + OwningMRUCache(typename ParentType::size_type max_size) + : ParentType(max_size) { + } + virtual ~OwningMRUCache() { + } + + private: + DISALLOW_COPY_AND_ASSIGN(OwningMRUCache); +}; + +#endif // CHROME_COMMON_MRU_CACHE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/mru_cache_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/mru_cache_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/mru_cache_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/mru_cache_unittest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,236 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "chrome/common/mru_cache.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +int cached_item_live_count = 0; + +struct CachedItem { + CachedItem() : value(0) { + cached_item_live_count++; + } + + CachedItem(int new_value) : value(new_value) { + cached_item_live_count++; + } + + CachedItem(const CachedItem& other) : value(other.value) { + cached_item_live_count++; + } + + ~CachedItem() { + cached_item_live_count--; + } + + int value; +}; + +} // namespace + +TEST(MRUCacheTest, Basic) { + typedef MRUCache Cache; + Cache cache(Cache::NO_AUTO_EVICT); + + // Check failure conditions + { + CachedItem test_item; + EXPECT_TRUE(cache.Get(0) == cache.end()); + EXPECT_TRUE(cache.Peek(0) == cache.end()); + } + + static const int kItem1Key = 5; + CachedItem item1(10); + Cache::iterator inserted_item = cache.Put(kItem1Key, item1); + EXPECT_EQ(1U, cache.size()); + + // Check that item1 was properly inserted. + { + Cache::iterator found = cache.Get(kItem1Key); + EXPECT_TRUE(inserted_item == cache.begin()); + EXPECT_TRUE(found != cache.end()); + + found = cache.Peek(kItem1Key); + EXPECT_TRUE(found != cache.end()); + + EXPECT_EQ(kItem1Key, found->first); + EXPECT_EQ(item1.value, found->second.value); + } + + static const int kItem2Key = 7; + CachedItem item2(12); + cache.Put(kItem2Key, item2); + EXPECT_EQ(2U, cache.size()); + + // Check that item1 is the oldest since item2 was added afterwards. + { + Cache::reverse_iterator oldest = cache.rbegin(); + ASSERT_TRUE(oldest != cache.rend()); + EXPECT_EQ(kItem1Key, oldest->first); + EXPECT_EQ(item1.value, oldest->second.value); + } + + // Check that item1 is still accessible by key. + { + Cache::iterator test_item = cache.Get(kItem1Key); + ASSERT_TRUE(test_item != cache.end()); + EXPECT_EQ(kItem1Key, test_item->first); + EXPECT_EQ(item1.value, test_item->second.value); + } + + // Check that retrieving item1 pushed item2 to oldest. + { + Cache::reverse_iterator oldest = cache.rbegin(); + ASSERT_TRUE(oldest != cache.rend()); + EXPECT_EQ(kItem2Key, oldest->first); + EXPECT_EQ(item2.value, oldest->second.value); + } + + // Remove the oldest item and check that item1 is now the only member. + { + Cache::reverse_iterator next = cache.Erase(cache.rbegin()); + + EXPECT_EQ(1U, cache.size()); + + EXPECT_TRUE(next == cache.rbegin()); + EXPECT_EQ(kItem1Key, next->first); + EXPECT_EQ(item1.value, next->second.value); + + cache.Erase(cache.begin()); + EXPECT_EQ(0U, cache.size()); + } +} + +TEST(MRUCacheTest, GetVsPeek) { + typedef MRUCache Cache; + Cache cache(Cache::NO_AUTO_EVICT); + + static const int kItem1Key = 1; + CachedItem item1(10); + cache.Put(kItem1Key, item1); + + static const int kItem2Key = 2; + CachedItem item2(20); + cache.Put(kItem2Key, item2); + + // This should do nothing since the size is bigger than the number of items. + cache.ShrinkToSize(100); + + // Check that item1 starts out as oldest + { + Cache::reverse_iterator iter = cache.rbegin(); + ASSERT_TRUE(iter != cache.rend()); + EXPECT_EQ(kItem1Key, iter->first); + EXPECT_EQ(item1.value, iter->second.value); + } + + // Check that Peek doesn't change ordering + { + Cache::iterator peekiter = cache.Peek(kItem1Key); + ASSERT_TRUE(peekiter != cache.end()); + + Cache::reverse_iterator iter = cache.rbegin(); + ASSERT_TRUE(iter != cache.rend()); + EXPECT_EQ(kItem1Key, iter->first); + EXPECT_EQ(item1.value, iter->second.value); + } +} + +TEST(MRUCacheTest, KeyReplacement) { + typedef MRUCache Cache; + Cache cache(Cache::NO_AUTO_EVICT); + + static const int kItem1Key = 1; + CachedItem item1(10); + cache.Put(kItem1Key, item1); + + static const int kItem2Key = 2; + CachedItem item2(20); + cache.Put(kItem2Key, item2); + + static const int kItem3Key = 3; + CachedItem item3(30); + cache.Put(kItem3Key, item3); + + static const int kItem4Key = 4; + CachedItem item4(40); + cache.Put(kItem4Key, item4); + + CachedItem item5(50); + cache.Put(kItem3Key, item5); + + EXPECT_EQ(4U, cache.size()); + for (int i = 0; i < 3; ++i) { + Cache::reverse_iterator iter = cache.rbegin(); + ASSERT_TRUE(iter != cache.rend()); + } + + // Make it so only the most important element is there. + cache.ShrinkToSize(1); + + Cache::iterator iter = cache.begin(); + EXPECT_EQ(kItem3Key, iter->first); + EXPECT_EQ(item5.value, iter->second.value); +} + +// Make sure that the owning version release its pointers properly. +TEST(MRUCacheTest, Owning) { + typedef OwningMRUCache Cache; + Cache cache(Cache::NO_AUTO_EVICT); + + int initial_count = cached_item_live_count; + + // First insert and item and then overwrite it. + static const int kItem1Key = 1; + cache.Put(kItem1Key, new CachedItem(20)); + cache.Put(kItem1Key, new CachedItem(22)); + + // There should still be one item, and one extra live item. + Cache::iterator iter = cache.Get(kItem1Key); + EXPECT_EQ(1U, cache.size()); + EXPECT_TRUE(iter != cache.end()); + EXPECT_EQ(initial_count + 1, cached_item_live_count); + + // Now remove it. + cache.Erase(cache.begin()); + EXPECT_EQ(initial_count, cached_item_live_count); + + // Now try another cache that goes out of scope to make sure its pointers + // go away. + { + Cache cache2(Cache::NO_AUTO_EVICT); + cache2.Put(1, new CachedItem(20)); + cache2.Put(2, new CachedItem(20)); + } + + // There should be no objects leaked. + EXPECT_EQ(initial_count, cached_item_live_count); +} + +TEST(MRUCacheTest, AutoEvict) { + typedef OwningMRUCache Cache; + static const Cache::size_type kMaxSize = 3; + + int initial_count = cached_item_live_count; + + { + Cache cache(kMaxSize); + + static const int kItem1Key = 1, kItem2Key = 2, kItem3Key = 3, kItem4Key = 4; + cache.Put(kItem1Key, new CachedItem(20)); + cache.Put(kItem2Key, new CachedItem(21)); + cache.Put(kItem3Key, new CachedItem(22)); + cache.Put(kItem4Key, new CachedItem(23)); + + // The cache should only have kMaxSize items in it even though we inserted + // more. + EXPECT_EQ(kMaxSize, cache.size()); + } + + // There should be no objects leaked. + EXPECT_EQ(initial_count, cached_item_live_count); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/native_web_keyboard_event.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/native_web_keyboard_event.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/native_web_keyboard_event.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/native_web_keyboard_event.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,50 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_NATIVE_WEB_KEYBOARD_EVENT_H_ +#define CHROME_COMMON_NATIVE_WEB_KEYBOARD_EVENT_H_ + +#include "base/basictypes.h" +#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h" + +#if defined(OS_WIN) +#include +#elif defined(OS_MACOSX) +#ifdef __OBJC__ +@class NSEvent; +#else +class NSEvent; +#endif // __OBJC__ +#elif defined(OS_LINUX) +#include +#endif + +// Owns a platform specific event; used to pass own and pass event through +// platform independent code. +struct NativeWebKeyboardEvent : public WebKit::WebKeyboardEvent { + NativeWebKeyboardEvent(); + +#if defined(OS_WIN) + NativeWebKeyboardEvent(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); +#elif defined(OS_MACOSX) + explicit NativeWebKeyboardEvent(NSEvent *event); +#elif defined(OS_LINUX) + explicit NativeWebKeyboardEvent(const GdkEventKey* event); +#endif + + NativeWebKeyboardEvent(const NativeWebKeyboardEvent& event); + ~NativeWebKeyboardEvent(); + + NativeWebKeyboardEvent& operator=(const NativeWebKeyboardEvent& event); + +#if defined(OS_WIN) + MSG os_event; +#elif defined(OS_MACOSX) + NSEvent* os_event; +#elif defined(OS_LINUX) + GdkEventKey* os_event; +#endif +}; + +#endif // CHROME_COMMON_NATIVE_WEB_KEYBOARD_EVENT_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/native_web_keyboard_event_linux.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/native_web_keyboard_event_linux.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/native_web_keyboard_event_linux.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/native_web_keyboard_event_linux.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,57 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/native_web_keyboard_event.h" + +#include "third_party/WebKit/WebKit/chromium/public/gtk/WebInputEventFactory.h" + +using WebKit::WebInputEventFactory; + +namespace { + +void CopyEventTo(const GdkEventKey* in, GdkEventKey** out) { + if (in) { + *out = reinterpret_cast( + gdk_event_copy( + reinterpret_cast(const_cast(in)))); + } else { + *out = NULL; + } +} + +void FreeEvent(GdkEventKey* event) { + if (event) { + gdk_event_free(reinterpret_cast(event)); + } +} + +} // namespace + + +NativeWebKeyboardEvent::NativeWebKeyboardEvent() + : os_event(NULL) { +} + +NativeWebKeyboardEvent::NativeWebKeyboardEvent(const GdkEventKey* native_event) + : WebKeyboardEvent(WebInputEventFactory::keyboardEvent(native_event)) { + CopyEventTo(native_event, &os_event); +} + +NativeWebKeyboardEvent::NativeWebKeyboardEvent( + const NativeWebKeyboardEvent& other) : WebKeyboardEvent(other) { + CopyEventTo(other.os_event, &os_event); +} + +NativeWebKeyboardEvent& NativeWebKeyboardEvent::operator=( + const NativeWebKeyboardEvent& other) { + WebKeyboardEvent::operator=(other); + + FreeEvent(os_event); + CopyEventTo(other.os_event, &os_event); + return *this; +} + +NativeWebKeyboardEvent::~NativeWebKeyboardEvent() { + FreeEvent(os_event); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/native_web_keyboard_event_mac.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/native_web_keyboard_event_mac.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/native_web_keyboard_event_mac.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/native_web_keyboard_event_mac.mm 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,41 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/native_web_keyboard_event.h" + +#import + +#include "third_party/WebKit/WebKit/chromium/public/mac/WebInputEventFactory.h" + +using WebKit::WebInputEventFactory; + +NativeWebKeyboardEvent::NativeWebKeyboardEvent() + : os_event(NULL) { +} + +NativeWebKeyboardEvent::NativeWebKeyboardEvent(NSEvent* event) + : WebKeyboardEvent(WebInputEventFactory::keyboardEvent(event)), + os_event([event retain]) { +} + +NativeWebKeyboardEvent::NativeWebKeyboardEvent( + const NativeWebKeyboardEvent& other) + : WebKeyboardEvent(other), + os_event([other.os_event retain]) { +} + +NativeWebKeyboardEvent& NativeWebKeyboardEvent::operator=( + const NativeWebKeyboardEvent& other) { + WebKeyboardEvent::operator=(other); + + NSObject* previous = os_event; + os_event = [other.os_event retain]; + [previous release]; + + return *this; +} + +NativeWebKeyboardEvent::~NativeWebKeyboardEvent() { + [os_event release]; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/native_web_keyboard_event_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/native_web_keyboard_event_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/native_web_keyboard_event_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/native_web_keyboard_event_win.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,47 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/native_web_keyboard_event.h" + +#include "third_party/WebKit/WebKit/chromium/public/win/WebInputEventFactory.h" + +using WebKit::WebInputEventFactory; +using WebKit::WebKeyboardEvent; + +NativeWebKeyboardEvent::NativeWebKeyboardEvent() { +} + +NativeWebKeyboardEvent::NativeWebKeyboardEvent( + HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) + : WebKeyboardEvent( + WebInputEventFactory::keyboardEvent(hwnd, message, wparam, lparam)) { + os_event.hwnd = hwnd; + os_event.message = message; + os_event.wParam = wparam; + os_event.lParam = lparam; +} + +NativeWebKeyboardEvent::NativeWebKeyboardEvent( + const NativeWebKeyboardEvent& other) + : WebKeyboardEvent(other) { + os_event.hwnd = other.os_event.hwnd; + os_event.message = other.os_event.message; + os_event.wParam = other.os_event.wParam; + os_event.lParam = other.os_event.lParam; +} + +NativeWebKeyboardEvent& NativeWebKeyboardEvent::operator=( + const NativeWebKeyboardEvent& other) { + WebKeyboardEvent::operator=(other); + + os_event.hwnd = other.os_event.hwnd; + os_event.message = other.os_event.message; + os_event.wParam = other.os_event.wParam; + os_event.lParam = other.os_event.lParam; + return *this; +} + +NativeWebKeyboardEvent::~NativeWebKeyboardEvent() { + // Noop under windows +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/navigation_types.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/navigation_types.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/navigation_types.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/navigation_types.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,63 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_NAVIGATION_TYPES_H_ +#define CHROME_COMMON_NAVIGATION_TYPES_H_ + +#include "base/basictypes.h" + +// Indicates different types of navigations that can occur that we will handle +// separately. +class NavigationType { + public: + enum Type { + // A new page was navigated in the main frame. + NEW_PAGE, + + // Renavigating to an existing navigation entry. The entry is guaranteed to + // exist in the list, or else it would be a new page or IGNORE navigation. + EXISTING_PAGE, + + // The same page has been reloaded as a result of the user requesting + // navigation to that same page (like pressing Enter in the URL bar). This + // is not the same as an in-page navigation because we'll actually have a + // pending entry for the load, which is then meaningless. + SAME_PAGE, + + // In page navigations are when the reference fragment changes. This will + // be in the main frame only (we won't even get notified of in-page + // subframe navigations). It may be for any page, not necessarily the last + // committed one (for example, whey going back to a page with a ref). + IN_PAGE, + + // A new subframe was manually navigated by the user. We will create a new + // NavigationEntry so they can go back to the previous subframe content + // using the back button. + NEW_SUBFRAME, + + // A subframe in the page was automatically loaded or navigated to such that + // a new navigation entry should not be created. There are two cases: + // 1. Stuff like iframes containing ads that the page loads automatically. + // The user doesn't want to see these, so we just update the existing + // navigation entry. + // 2. Going back/forward to previous subframe navigations. We don't create + // a new entry here either, just update the last committed entry. + // These two cases are actually pretty different, they just happen to + // require almost the same code to handle. + AUTO_SUBFRAME, + + // Nothing happened. This happens when we get information about a page we + // don't know anything about. It can also happen when an iframe in a popup + // navigated to about:blank is navigated. Nothing needs to be done. + NAV_IGNORE, + }; + + private: + // This class is for scoping only, so you shouldn't create an instance of it. + NavigationType() {} + + DISALLOW_COPY_AND_ASSIGN(NavigationType); +}; + +#endif // CHROME_COMMON_NAVIGATION_TYPES_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/cache_uitest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/cache_uitest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/cache_uitest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/cache_uitest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,179 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/platform_thread.h" +#include "base/string_util.h" +#include "chrome/test/ui/ui_test.h" +#include "chrome/test/automation/automation_proxy.h" +#include "chrome/test/automation/browser_proxy.h" +#include "chrome/test/automation/tab_proxy.h" +#include "net/url_request/url_request_unittest.h" + +// The CacheTest class extends the UITest class and provides functions to +// get a new tab and to run the tests on a particular path +// +// Typical usage: +// +// 1. Provide this class as the TestCase for TEST_F macro +// 2. Then run the cache test on a specific path using the function +// RunCacheTest +// +// For example: +// +// TEST_F(CacheTest, NoCacheMaxAge) { +// RunCacheTest(L"nocachetime/maxage", false, false); +// } +// +// Note that delays used in running the test is initialized to defaults +class CacheTest : public UITest { + protected: + + // Runs the cache test for the specified path. + // Can specify the test to check if a new tab is loaded from the cache + // and also if a delayed reload is required. A true value passed to the + // third parameter causes a delayed reload of the path in a new tab. + // The amount of delay is set by a class constant. + void RunCacheTest(const std::wstring &url, + bool expect_new_tab_cached, + bool expect_delayed_reload); + + private: + // Class constants + static const int kWaitForCacheUpdateMsec = 2000; + static const int kCacheWaitMultiplier = 4; // Used to increase delay + + // Appends a new tab to the test chrome window and loads the specified + // URL. The new tab will try to get the URL from the cache before requesting + // the server for it. + void GetNewTab(AutomationProxy* automationProxy, const GURL& tab_url); +}; + +// Runs the cache test for the specified path. +void CacheTest::RunCacheTest(const std::wstring &url, + bool expect_new_tab_cached, + bool expect_delayed_reload) { + scoped_refptr server = + HTTPTestServer::CreateServer(L"chrome/test/data", NULL); + ASSERT_TRUE(NULL != server.get()); + GURL test_page(server->TestServerPageW(url)); + + NavigateToURL(test_page); + std::wstring original_time = GetActiveTabTitle(); + + PlatformThread::Sleep(kWaitForCacheUpdateMsec); + + GetNewTab(automation(), test_page); + std::wstring revisit_time = GetActiveTabTitle(); + + if (expect_new_tab_cached) { + EXPECT_EQ(original_time, revisit_time); + }else { + EXPECT_NE(original_time, revisit_time); + } + + PlatformThread::Sleep(kWaitForCacheUpdateMsec); + + // Force reload, overriding the caching behavior + NavigateToURL(test_page); + std::wstring reload_time = GetActiveTabTitle(); + + EXPECT_NE(revisit_time, reload_time); + + if (expect_delayed_reload) { + PlatformThread::Sleep(kWaitForCacheUpdateMsec * kCacheWaitMultiplier); + + GetNewTab(automation(), test_page); + revisit_time = GetActiveTabTitle(); + + EXPECT_NE(reload_time, revisit_time); + } +} + +// Appends a new tab to the test chrome window and loads the specified URL. +void CacheTest::GetNewTab(AutomationProxy* automationProxy, + const GURL& tab_url) { + scoped_ptr window_proxy(automationProxy->GetBrowserWindow(0)); + ASSERT_TRUE(window_proxy.get()); + ASSERT_TRUE(window_proxy->AppendTab(tab_url)); +} + +// Tests that a cached copy of the page is not used when max-age=0 headers +// are specified. +TEST_F(CacheTest, NoCacheMaxAge) { + RunCacheTest(L"nocachetime/maxage", false, false); +} + +// Tests that a cached copy of the page is not used when no-cache header +// is specified. +TEST_F(CacheTest, NoCache) { + RunCacheTest(L"nocachetime", false, false); +} + +// Tests that a cached copy of a page is used when its headers specify +// that it should be cached for 60 seconds. +TEST_F(CacheTest, Cache) { + RunCacheTest(L"cachetime", true, false); +} + +// Tests that a cached copy of the page is used when expires header +// specifies that the page has not yet expired. +TEST_F(CacheTest, Expires) { + RunCacheTest(L"cache/expires", true, false); +} + +// Tests that a cached copy of the page is used when proxy-revalidate header +// is specified and the page has not yet expired. +TEST_F(CacheTest, ProxyRevalidate) { + RunCacheTest(L"cache/proxy-revalidate", true, false); +} + +// Tests that a cached copy of the page is used when private header +// is specified and the page has not yet expired. +TEST_F(CacheTest, Private) { + RunCacheTest(L"cache/private", true, true); +} + +// Tests that a cached copy of the page is used when public header +// is specified and the page has not yet expired. +TEST_F(CacheTest, Public) { + RunCacheTest(L"cache/public", true, true); +} + +// Tests that a cached copy of the page is not used when s-maxage header +// is specified. +TEST_F(CacheTest, SMaxAge) { + RunCacheTest(L"cache/s-maxage", false, false); +} + +// Tests that a cached copy of the page is not used when must-revalidate header +// is specified. +TEST_F(CacheTest, MustRevalidate) { + RunCacheTest(L"cache/must-revalidate", false, false); +} + +// Tests that a cached copy of the page is not used when must-revalidate header +// is specified, even though the page has not yet expired. +TEST_F(CacheTest, MustRevalidateMaxAge) { + RunCacheTest(L"cache/must-revalidate/max-age", false, false); +} + +// Tests that a cached copy of the page is not used when no-store header +// is specified. +TEST_F(CacheTest, NoStore) { + RunCacheTest(L"cache/no-store", false, false); +} + +// Tests that a cached copy of the page is not used when no-store header +// is specified, even though the page has not yet expired. +TEST_F(CacheTest, NoStoreMaxAge) { + RunCacheTest(L"cache/no-store/max-age", false, false); +} + +// Tests that a cached copy of the page is not transformed when no-transform +// header is specified. +TEST_F(CacheTest, NoTransform) { + RunCacheTest(L"cache/no-transform", false, false); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/cookie_monster_sqlite.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/cookie_monster_sqlite.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/cookie_monster_sqlite.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/cookie_monster_sqlite.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,436 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/net/cookie_monster_sqlite.h" + +#include + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/ref_counted.h" +#include "base/string_util.h" +#include "base/thread.h" +#include "chrome/common/sqlite_compiled_statement.h" +#include "chrome/common/sqlite_utils.h" + +using base::Time; + +// This class is designed to be shared between any calling threads and the +// database thread. It batches operations and commits them on a timer. +class SQLitePersistentCookieStore::Backend + : public base::RefCountedThreadSafe { + public: + // The passed database pointer must be already-initialized. This object will + // take ownership. + explicit Backend(sqlite3* db, MessageLoop* loop) + : db_(db), + background_loop_(loop), + cache_(new SqliteStatementCache(db)), + num_pending_(0) { + DCHECK(db_) << "Database must exist."; + } + + // You should call Close() before destructing this object. + ~Backend() { + DCHECK(!db_) << "Close should have already been called."; + DCHECK(num_pending_ == 0 && pending_.empty()); + } + + // Batch a cookie addition. + void AddCookie(const std::string& key, + const net::CookieMonster::CanonicalCookie& cc); + + // Batch a cookie access time update. + void UpdateCookieAccessTime(const net::CookieMonster::CanonicalCookie& cc); + + // Batch a cookie deletion. + void DeleteCookie(const net::CookieMonster::CanonicalCookie& cc); + + // Commit any pending operations and close the database. This must be called + // before the object is destructed. + void Close(); + + private: + class PendingOperation { + public: + typedef enum { + COOKIE_ADD, + COOKIE_UPDATEACCESS, + COOKIE_DELETE, + } OperationType; + + PendingOperation(OperationType op, + const std::string& key, + const net::CookieMonster::CanonicalCookie& cc) + : op_(op), key_(key), cc_(cc) { } + + OperationType op() const { return op_; } + const std::string& key() const { return key_; } + const net::CookieMonster::CanonicalCookie& cc() const { return cc_; } + + private: + OperationType op_; + std::string key_; // Only used for OP_ADD + net::CookieMonster::CanonicalCookie cc_; + }; + + private: + // Batch a cookie operation (add or delete) + void BatchOperation(PendingOperation::OperationType op, + const std::string& key, + const net::CookieMonster::CanonicalCookie& cc); + // Commit our pending operations to the database. + void Commit(); + // Close() executed on the background thread. + void InternalBackgroundClose(); + + sqlite3* db_; + MessageLoop* background_loop_; + SqliteStatementCache* cache_; + + typedef std::list PendingOperationsList; + PendingOperationsList pending_; + PendingOperationsList::size_type num_pending_; + Lock pending_lock_; // Guard pending_ and num_pending_ + + DISALLOW_EVIL_CONSTRUCTORS(Backend); +}; + +void SQLitePersistentCookieStore::Backend::AddCookie( + const std::string& key, + const net::CookieMonster::CanonicalCookie& cc) { + BatchOperation(PendingOperation::COOKIE_ADD, key, cc); +} + +void SQLitePersistentCookieStore::Backend::UpdateCookieAccessTime( + const net::CookieMonster::CanonicalCookie& cc) { + BatchOperation(PendingOperation::COOKIE_UPDATEACCESS, std::string(), cc); +} + +void SQLitePersistentCookieStore::Backend::DeleteCookie( + const net::CookieMonster::CanonicalCookie& cc) { + BatchOperation(PendingOperation::COOKIE_DELETE, std::string(), cc); +} + +void SQLitePersistentCookieStore::Backend::BatchOperation( + PendingOperation::OperationType op, + const std::string& key, + const net::CookieMonster::CanonicalCookie& cc) { + // Commit every 30 seconds. + static const int kCommitIntervalMs = 30 * 1000; + // Commit right away if we have more than 512 outstanding operations. + static const size_t kCommitAfterBatchSize = 512; + DCHECK(MessageLoop::current() != background_loop_); + + // We do a full copy of the cookie here, and hopefully just here. + scoped_ptr po(new PendingOperation(op, key, cc)); + CHECK(po.get()); + + PendingOperationsList::size_type num_pending; + { + AutoLock locked(pending_lock_); + pending_.push_back(po.release()); + num_pending = ++num_pending_; + } + + // TODO(abarth): What if the DB thread is being destroyed on the UI thread? + if (num_pending == 1) { + // We've gotten our first entry for this batch, fire off the timer. + background_loop_->PostDelayedTask(FROM_HERE, + NewRunnableMethod(this, &Backend::Commit), kCommitIntervalMs); + } else if (num_pending == kCommitAfterBatchSize) { + // We've reached a big enough batch, fire off a commit now. + background_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &Backend::Commit)); + } +} + +void SQLitePersistentCookieStore::Backend::Commit() { + DCHECK(MessageLoop::current() == background_loop_); + PendingOperationsList ops; + { + AutoLock locked(pending_lock_); + pending_.swap(ops); + num_pending_ = 0; + } + + // Maybe an old timer fired or we are already Close()'ed. + if (!db_ || ops.empty()) + return; + + SQLITE_UNIQUE_STATEMENT(add_smt, *cache_, + "INSERT INTO cookies (creation_utc, host_key, name, value, path, " + "expires_utc, secure, httponly, last_access_utc) " + "VALUES (?,?,?,?,?,?,?,?,?)"); + if (!add_smt.is_valid()) { + NOTREACHED(); + return; + } + + SQLITE_UNIQUE_STATEMENT(update_access_smt, *cache_, + "UPDATE cookies SET last_access_utc=? " + "WHERE creation_utc=?"); + if (!update_access_smt.is_valid()) { + NOTREACHED(); + return; + } + + SQLITE_UNIQUE_STATEMENT(del_smt, *cache_, + "DELETE FROM cookies WHERE creation_utc=?"); + if (!del_smt.is_valid()) { + NOTREACHED(); + return; + } + + SQLTransaction transaction(db_); + transaction.Begin(); + for (PendingOperationsList::iterator it = ops.begin(); + it != ops.end(); ++it) { + // Free the cookies as we commit them to the database. + scoped_ptr po(*it); + switch (po->op()) { + case PendingOperation::COOKIE_ADD: + add_smt->reset(); + add_smt->bind_int64(0, po->cc().CreationDate().ToInternalValue()); + add_smt->bind_string(1, po->key()); + add_smt->bind_string(2, po->cc().Name()); + add_smt->bind_string(3, po->cc().Value()); + add_smt->bind_string(4, po->cc().Path()); + add_smt->bind_int64(5, po->cc().ExpiryDate().ToInternalValue()); + add_smt->bind_int(6, po->cc().IsSecure()); + add_smt->bind_int(7, po->cc().IsHttpOnly()); + add_smt->bind_int64(8, po->cc().LastAccessDate().ToInternalValue()); + if (add_smt->step() != SQLITE_DONE) { + NOTREACHED() << "Could not add a cookie to the DB."; + } + break; + + case PendingOperation::COOKIE_UPDATEACCESS: + update_access_smt->reset(); + update_access_smt->bind_int64(0, + po->cc().LastAccessDate().ToInternalValue()); + update_access_smt->bind_int64(1, + po->cc().CreationDate().ToInternalValue()); + if (update_access_smt->step() != SQLITE_DONE) { + NOTREACHED() << "Could not update cookie last access time in the DB."; + } + break; + + case PendingOperation::COOKIE_DELETE: + del_smt->reset(); + del_smt->bind_int64(0, po->cc().CreationDate().ToInternalValue()); + if (del_smt->step() != SQLITE_DONE) { + NOTREACHED() << "Could not delete a cookie from the DB."; + } + break; + + default: + NOTREACHED(); + break; + } + } + transaction.Commit(); +} + +// Fire off a close message to the background thread. We could still have a +// pending commit timer that will be holding a reference on us, but if/when +// this fires we will already have been cleaned up and it will be ignored. +void SQLitePersistentCookieStore::Backend::Close() { + DCHECK(MessageLoop::current() != background_loop_); + // Must close the backend on the background thread. + // TODO(abarth): What if the DB thread is being destroyed on the UI thread? + background_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &Backend::InternalBackgroundClose)); +} + +void SQLitePersistentCookieStore::Backend::InternalBackgroundClose() { + DCHECK(MessageLoop::current() == background_loop_); + // Commit any pending operations + Commit(); + // We must destroy the cache before closing the database. + delete cache_; + cache_ = NULL; + sqlite3_close(db_); + db_ = NULL; +} + +SQLitePersistentCookieStore::SQLitePersistentCookieStore( + const std::wstring& path, MessageLoop* background_loop) + : path_(path), + background_loop_(background_loop) { + DCHECK(background_loop) << "SQLitePersistentCookieStore needs a MessageLoop"; +} + +SQLitePersistentCookieStore::~SQLitePersistentCookieStore() { + if (backend_.get()) { + backend_->Close(); + // Release our reference, it will probably still have a reference if the + // background thread has not run Close() yet. + backend_ = NULL; + } +} + +// Version number of the database. +static const int kCurrentVersionNumber = 3; +static const int kCompatibleVersionNumber = 3; + +namespace { + +// Initializes the cookies table, returning true on success. +bool InitTable(sqlite3* db) { + if (!DoesSqliteTableExist(db, "cookies")) { + if (sqlite3_exec(db, "CREATE TABLE cookies (" + "creation_utc INTEGER NOT NULL UNIQUE PRIMARY KEY," + "host_key TEXT NOT NULL," + "name TEXT NOT NULL," + "value TEXT NOT NULL," + "path TEXT NOT NULL," + // We only store persistent, so we know it expires + "expires_utc INTEGER NOT NULL," + "secure INTEGER NOT NULL," + "httponly INTEGER NOT NULL," + "last_access_utc INTEGER NOT NULL)", + NULL, NULL, NULL) != SQLITE_OK) + return false; + } + + // Try to create the index every time. Older versions did not have this index, + // so we want those people to get it. Ignore errors, since it may exist. + sqlite3_exec(db, "CREATE INDEX cookie_times ON cookies (creation_utc)", + NULL, NULL, NULL); + + return true; +} + +void PrimeCache(sqlite3* db) { + // A statement must be open for the preload command to work. If the meta + // table can't be read, it probably means this is a new database and there + // is nothing to preload (so it's OK we do nothing). + SQLStatement dummy; + if (dummy.prepare(db, "SELECT * from meta") != SQLITE_OK) + return; + if (dummy.step() != SQLITE_ROW) + return; + + sqlite3Preload(db); +} + +} // namespace + +bool SQLitePersistentCookieStore::Load( + std::vector* cookies) { + DCHECK(!path_.empty()); + sqlite3* db; + if (sqlite3_open(WideToUTF8(path_).c_str(), &db) != SQLITE_OK) { + NOTREACHED() << "Unable to open cookie DB."; + return false; + } + + if (!EnsureDatabaseVersion(db) || !InitTable(db)) { + NOTREACHED() << "Unable to initialize cookie DB."; + sqlite3_close(db); + return false; + } + + PrimeCache(db); + + // Slurp all the cookies into the out-vector. + SQLStatement smt; + if (smt.prepare(db, + "SELECT creation_utc, host_key, name, value, path, expires_utc, secure, " + "httponly, last_access_utc FROM cookies") != SQLITE_OK) { + NOTREACHED() << "select statement prep failed"; + sqlite3_close(db); + return false; + } + + while (smt.step() == SQLITE_ROW) { + std::string key = smt.column_string(1); + scoped_ptr cc( + new net::CookieMonster::CanonicalCookie( + smt.column_string(2), // name + smt.column_string(3), // value + smt.column_string(4), // path + smt.column_int(6) != 0, // secure + smt.column_int(7) != 0, // httponly + Time::FromInternalValue(smt.column_int64(0)), // creation_utc + Time::FromInternalValue(smt.column_int64(8)), // last_access_utc + true, // has_expires + Time::FromInternalValue(smt.column_int64(5)))); // expires_utc + // Memory allocation failed. + if (!cc.get()) + break; + + DLOG_IF(WARNING, + cc->CreationDate() > Time::Now()) << L"CreationDate too recent"; + cookies->push_back( + net::CookieMonster::KeyedCanonicalCookie(smt.column_string(1), + cc.release())); + } + + // Create the backend, this will take ownership of the db pointer. + backend_ = new Backend(db, background_loop_); + + return true; +} + +bool SQLitePersistentCookieStore::EnsureDatabaseVersion(sqlite3* db) { + // Version check. + if (!meta_table_.Init(std::string(), kCurrentVersionNumber, + kCompatibleVersionNumber, db)) + return false; + + if (meta_table_.GetCompatibleVersionNumber() > kCurrentVersionNumber) { + LOG(WARNING) << "Cookie database is too new."; + return false; + } + + int cur_version = meta_table_.GetVersionNumber(); + if (cur_version == 2) { + SQLTransaction transaction(db); + transaction.Begin(); + if ((sqlite3_exec(db, + "ALTER TABLE cookies ADD COLUMN last_access_utc " + "INTEGER DEFAULT 0", NULL, NULL, NULL) != SQLITE_OK) || + (sqlite3_exec(db, + "UPDATE cookies SET last_access_utc = creation_utc", + NULL, NULL, NULL) != SQLITE_OK)) { + LOG(WARNING) << "Unable to update cookie database to version 3."; + return false; + } + ++cur_version; + meta_table_.SetVersionNumber(cur_version); + meta_table_.SetCompatibleVersionNumber( + std::min(cur_version, kCompatibleVersionNumber)); + transaction.Commit(); + } + + // Put future migration cases here. + + // When the version is too old, we just try to continue anyway, there should + // not be a released product that makes a database too old for us to handle. + LOG_IF(WARNING, cur_version < kCurrentVersionNumber) << + "Cookie database version " << cur_version << " is too old to handle."; + + return true; +} + +void SQLitePersistentCookieStore::AddCookie( + const std::string& key, + const net::CookieMonster::CanonicalCookie& cc) { + if (backend_.get()) + backend_->AddCookie(key, cc); +} + +void SQLitePersistentCookieStore::UpdateCookieAccessTime( + const net::CookieMonster::CanonicalCookie& cc) { + if (backend_.get()) + backend_->UpdateCookieAccessTime(cc); +} + +void SQLitePersistentCookieStore::DeleteCookie( + const net::CookieMonster::CanonicalCookie& cc) { + if (backend_.get()) + backend_->DeleteCookie(cc); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/cookie_monster_sqlite.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/cookie_monster_sqlite.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/cookie_monster_sqlite.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/cookie_monster_sqlite.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,52 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// A sqlite implementation of a cookie monster persistent store. + +#ifndef CHROME_COMMON_NET_COOKIE_MONSTER_SQLITE_H__ +#define CHROME_COMMON_NET_COOKIE_MONSTER_SQLITE_H__ + +#include +#include + +#include "base/ref_counted.h" +#include "base/message_loop.h" +#include "chrome/browser/meta_table_helper.h" +#include "net/base/cookie_monster.h" + +struct sqlite3; + +class SQLitePersistentCookieStore + : public net::CookieMonster::PersistentCookieStore { + public: + SQLitePersistentCookieStore(const std::wstring& path, + MessageLoop* background_loop); + ~SQLitePersistentCookieStore(); + + virtual bool Load(std::vector*); + + virtual void AddCookie(const std::string&, + const net::CookieMonster::CanonicalCookie&); + virtual void UpdateCookieAccessTime( + const net::CookieMonster::CanonicalCookie&); + virtual void DeleteCookie(const net::CookieMonster::CanonicalCookie&); + + private: + class Backend; + + // Database upgrade statements. + bool EnsureDatabaseVersion(sqlite3* db); + + std::wstring path_; + scoped_refptr backend_; + + // Background MessageLoop on which to access the backend_; + MessageLoop* background_loop_; + + MetaTableHelper meta_table_; + + DISALLOW_EVIL_CONSTRUCTORS(SQLitePersistentCookieStore); +}; + +#endif // CHROME_COMMON_NET_COOKIE_MONSTER_SQLITE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/dns.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/dns.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/dns.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/dns.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,24 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file has shared types used across IPC between render_dns_master.cc +// and dns_master.cc + + +#ifndef CHROME_COMMON_DNS_H_ +#define CHROME_COMMON_DNS_H_ + +#include +#include + +namespace chrome_common_net { + +// IPC messages are passed from the renderer to the browser in the form of +// Namelist instances. +// Each element of this vector is a hostname that needs to be looked up. +// The hostnames should never be empty strings. +typedef std::vector NameList; +} + +#endif // CHROME_COMMON_DNS_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/url_request_intercept_job.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/url_request_intercept_job.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/url_request_intercept_job.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/url_request_intercept_job.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,216 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// This job type handles Chrome plugin network interception. When a plugin +// wants to intercept a request, a job of this type is created. The intercept +// job communicates with the plugin to retrieve the response headers and data. + +#include "chrome/common/net/url_request_intercept_job.h" + +#include "base/message_loop.h" +#include "base/string_util.h" +#include "chrome/common/chrome_plugin_lib.h" +#include "chrome/common/notification_service.h" +#include "net/base/net_errors.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/url_request.h" + +using base::Time; +using base::TimeDelta; + +// +// URLRequestInterceptJob +// + +URLRequestInterceptJob::URLRequestInterceptJob(URLRequest* request, + ChromePluginLib* plugin, + ScopableCPRequest* cprequest) + : URLRequestJob(request), + cprequest_(cprequest), + plugin_(plugin), + got_headers_(false), + read_buffer_(NULL) { + cprequest_->data = this; // see FromCPRequest(). + + NotificationService::current()->AddObserver( + this, NotificationType::CHROME_PLUGIN_UNLOADED, + Source(plugin_)); +} + +URLRequestInterceptJob::~URLRequestInterceptJob() { + if (plugin_) { + plugin_->functions().request_funcs->end_request(cprequest_.get(), + CPERR_SUCCESS); + DetachPlugin(); + } +} + +void URLRequestInterceptJob::DetachPlugin() { + NotificationService::current()->RemoveObserver( + this, NotificationType::CHROME_PLUGIN_UNLOADED, + Source(plugin_)); + plugin_ = NULL; +} + +void URLRequestInterceptJob::Start() { + // Start reading asynchronously so that all error reporting and data + // callbacks happen as they would for network requests. + MessageLoop::current()->PostTask(FROM_HERE, NewRunnableMethod( + this, &URLRequestInterceptJob::StartAsync)); +} + +void URLRequestInterceptJob::Kill() { + if (plugin_) { + plugin_->functions().request_funcs->end_request(cprequest_.get(), + CPERR_CANCELLED); + DetachPlugin(); + } + URLRequestJob::Kill(); +} + +bool URLRequestInterceptJob::ReadRawData(net::IOBuffer* dest, int dest_size, + int* bytes_read) { + DCHECK_NE(dest_size, 0); + DCHECK(bytes_read); + + if (!plugin_) + return false; + + int rv = plugin_->functions().request_funcs->read(cprequest_.get(), + dest->data(), dest_size); + if (rv >= 0) { + *bytes_read = rv; + return true; + } + + if (rv == CPERR_IO_PENDING) { + read_buffer_ = dest; + read_buffer_size_ = dest_size; + SetStatus(URLRequestStatus(URLRequestStatus::IO_PENDING, 0)); + } else { + // TODO(mpcomplete): better error code + NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, net::ERR_FAILED)); + } + + return false; +} + +bool URLRequestInterceptJob::GetMimeType(std::string* mime_type) const { + return request_->response_headers()->GetMimeType(mime_type); +} + +bool URLRequestInterceptJob::GetCharset(std::string* charset) { + return request_->response_headers()->GetCharset(charset); +} + +bool URLRequestInterceptJob::GetContentEncoding(std::string* encoding_type) { + // TODO(darin): what if there are multiple content encodings? + return request_->response_headers()->EnumerateHeader(NULL, "Content-Encoding", + encoding_type); +} + +void URLRequestInterceptJob::GetResponseInfo(net::HttpResponseInfo* info) { + if (!plugin_) + return; + + std::string raw_headers; + int size = plugin_->functions().request_funcs->get_response_info( + cprequest_.get(), CPRESPONSEINFO_HTTP_RAW_HEADERS, NULL, 0); + int rv = size < 0 ? size : + plugin_->functions().request_funcs->get_response_info( + cprequest_.get(), CPRESPONSEINFO_HTTP_RAW_HEADERS, + WriteInto(&raw_headers, size+1), size); + if (rv != CPERR_SUCCESS) { + // TODO(mpcomplete): what should we do here? + raw_headers = "HTTP/1.1 404 Not Found" + '\0'; + } + + info->headers = new net::HttpResponseHeaders(raw_headers); + + if (request_->url().SchemeIsSecure()) { + // Make up a fake certificate for intercepted data since we don't have + // access to the real SSL info. + // TODO(mpcomplete): we should probably have the interception API transmit + // the SSL info, but the only consumer of this API (Gears) doesn't keep that + // around. We should change that. + const char* kCertIssuer = "Chrome Internal"; + const int kLifetimeDays = 100; + + DLOG(WARNING) << "Issuing a fake SSL certificate for interception of URL " + << request_->url(); + + info->ssl_info.cert = + new net::X509Certificate(request_->url().GetWithEmptyPath().spec(), + kCertIssuer, + Time::Now(), + Time::Now() + + TimeDelta::FromDays(kLifetimeDays)); + info->ssl_info.cert_status = 0; + info->ssl_info.security_bits = 0; + } +} + +int URLRequestInterceptJob::GetResponseCode() const { + if (!plugin_) + return -1; + + int status = 200; + plugin_->functions().request_funcs->get_response_info( + cprequest_.get(), CPRESPONSEINFO_HTTP_STATUS, &status, sizeof(status)); + + return status; +} + +bool URLRequestInterceptJob::IsRedirectResponse(GURL* location, + int* http_status_code) { + if (!request_->response_headers()) + return false; + + std::string value; + if (!request_->response_headers()->IsRedirect(&value)) + return false; + + *location = request_->url().Resolve(value); + *http_status_code = request_->response_headers()->response_code(); + return true; +} + +void URLRequestInterceptJob::StartAsync() { + // We may have been orphaned... + if (!request_ || !plugin_) + return; + + int rv = plugin_->functions().request_funcs->start_request(cprequest_.get()); + if (rv != CPERR_IO_PENDING) + OnStartCompleted(rv); +} + +void URLRequestInterceptJob::OnStartCompleted(int result) { + if (result != CPERR_SUCCESS) { + NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, + net::ERR_CONNECTION_FAILED)); + return; + } + + NotifyHeadersComplete(); +} + +void URLRequestInterceptJob::OnReadCompleted(int bytes_read) { + if (bytes_read < 0) { + NotifyDone(URLRequestStatus(URLRequestStatus::FAILED, net::ERR_FAILED)); + return; + } + + SetStatus(URLRequestStatus()); // clear the async flag + NotifyReadComplete(bytes_read); +} + +void URLRequestInterceptJob::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NotificationType::CHROME_PLUGIN_UNLOADED); + DCHECK(plugin_ == Source(source).ptr()); + + DetachPlugin(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/url_request_intercept_job.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/url_request_intercept_job.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/url_request_intercept_job.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/url_request_intercept_job.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,64 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_NET_URL_REQUEST_INTERCEPT_JOB_H_ +#define CHROME_COMMON_NET_URL_REQUEST_INTERCEPT_JOB_H_ + +#include + +#include "base/scoped_ptr.h" +#include "net/url_request/url_request_job.h" +#include "chrome/browser/chrome_plugin_host.h" +#include "chrome/common/chrome_plugin_api.h" +#include "chrome/common/chrome_plugin_util.h" +#include "chrome/common/notification_observer.h" + +class ChromePluginLib; +class URLRequest; + +// A request job that handles network requests intercepted by a Chrome plugin. +class URLRequestInterceptJob + : public URLRequestJob, public NotificationObserver { + public: + static URLRequestInterceptJob* FromCPRequest(CPRequest* request) { + return ScopableCPRequest::GetData(request); + } + + URLRequestInterceptJob(URLRequest* request, ChromePluginLib* plugin, + ScopableCPRequest* cprequest); + virtual ~URLRequestInterceptJob(); + + // Plugin callbacks. + void OnStartCompleted(int result); + void OnReadCompleted(int bytes_read); + + // URLRequestJob + virtual void Start(); + virtual void Kill(); + virtual bool ReadRawData(net::IOBuffer* buf, int buf_size, int* bytes_read); + virtual bool GetMimeType(std::string* mime_type) const; + virtual bool GetCharset(std::string* charset); + virtual void GetResponseInfo(net::HttpResponseInfo* info); + virtual int GetResponseCode() const; + virtual bool GetContentEncoding(std::string* encoding_type); + virtual bool IsRedirectResponse(GURL* location, int* http_status_code); + + // NotificationObserver + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + private: + void StartAsync(); + void DetachPlugin(); + + scoped_ptr cprequest_; + ChromePluginLib* plugin_; + bool got_headers_; + net::IOBuffer* read_buffer_; + int read_buffer_size_; + + DISALLOW_COPY_AND_ASSIGN(URLRequestInterceptJob); +}; + +#endif // CHROME_COMMON_NET_URL_REQUEST_INTERCEPT_JOB_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/url_util_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/url_util_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/url_util_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/net/url_util_unittest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,52 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/basictypes.h" +#include "googleurl/src/url_util.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(URLUtil, Scheme) { + enum SchemeType { + NO_SCHEME = 0, + SCHEME = 1 << 0, + STANDARD_SCHEME = 1 << 1, + }; + const struct { + const wchar_t* url; + const char* try_scheme; + const bool scheme_matches; + const int flags; + } tests[] = { + {L" ", "hello", false, NO_SCHEME}, + {L"foo", NULL, false, NO_SCHEME}, + {L"google.com/foo:bar", NULL, false, NO_SCHEME}, + {L"Garbage:foo.com", "garbage", true, SCHEME}, + {L"Garbage:foo.com", "trash", false, SCHEME}, + {L"gopher:", "gopher", true, SCHEME | STANDARD_SCHEME}, + {L"About:blank", "about", true, SCHEME}, + {L"http://foo.com:123", "foo", false, SCHEME | STANDARD_SCHEME}, + {L"file://c/", "file", true, SCHEME | STANDARD_SCHEME}, + }; + + for (size_t i = 0; i < arraysize(tests); i++) { + int url_len = static_cast(wcslen(tests[i].url)); + + url_parse::Component parsed_scheme; + bool has_scheme = url_parse::ExtractScheme(tests[i].url, url_len, + &parsed_scheme); + + EXPECT_EQ(!!(tests[i].flags & STANDARD_SCHEME), + url_util::IsStandard(tests[i].url, url_len)); + EXPECT_EQ(!!(tests[i].flags & STANDARD_SCHEME), + url_util::IsStandardScheme(tests[i].url, parsed_scheme.len)); + + url_parse::Component found_scheme; + EXPECT_EQ(tests[i].scheme_matches, + url_util::FindAndCompareScheme(tests[i].url, url_len, + tests[i].try_scheme, + &found_scheme)); + EXPECT_EQ(parsed_scheme.begin, found_scheme.begin); + EXPECT_EQ(parsed_scheme.len, found_scheme.len); + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_details.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_details.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_details.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_details.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,52 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file defines the type used to provide details for NotificationService +// notifications. + +#ifndef CHROME_COMMON_NOTIFICATION_DETAILS_H__ +#define CHROME_COMMON_NOTIFICATION_DETAILS_H__ + +#include "base/basictypes.h" + +// Do not declare a NotificationDetails directly--use either +// "Details(detailsclasspointer)" or +// NotificationService::NoDetails(). +class NotificationDetails { + public: + NotificationDetails() : ptr_(NULL) {} + NotificationDetails(const NotificationDetails& other) : ptr_(other.ptr_) {} + ~NotificationDetails() {} + + // NotificationDetails can be used as the index for a map; this method + // returns the pointer to the current details as an identifier, for use as a + // map index. + uintptr_t map_key() const { return reinterpret_cast(ptr_); } + + bool operator!=(const NotificationDetails& other) const { + return ptr_ != other.ptr_; + } + + bool operator==(const NotificationDetails& other) const { + return ptr_ == other.ptr_; + } + + protected: + NotificationDetails(void* ptr) : ptr_(ptr) {} + + void* ptr_; +}; + +template +class Details : public NotificationDetails { + public: + Details(T* ptr) : NotificationDetails(ptr) {} + Details(const NotificationDetails& other) + : NotificationDetails(other) {} + + T* operator->() const { return ptr(); } + T* ptr() const { return static_cast(ptr_); } +}; + +#endif // CHROME_COMMON_NOTIFICATION_DETAILS_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_observer.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_observer.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_observer.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_observer.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,23 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_NOTIFICATION_OBSERVER_H_ +#define CHROME_COMMON_NOTIFICATION_OBSERVER_H_ + +class NotificationDetails; +class NotificationSource; +class NotificationType; + +// This is the base class for notification observers. When a matching +// notification is posted to the notification service, Observe is called. +class NotificationObserver { + public: + virtual ~NotificationObserver(); + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) = 0; +}; + +#endif // CHROME_COMMON_NOTIFICATION_OBSERVER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_registrar.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_registrar.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_registrar.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_registrar.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,71 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/notification_registrar.h" + +#include + +#include "base/logging.h" +#include "chrome/common/notification_service.h" + +struct NotificationRegistrar::Record { + bool operator==(const Record& other) const; + + NotificationObserver* observer; + NotificationType type; + NotificationSource source; +}; + +bool NotificationRegistrar::Record::operator==(const Record& other) const { + return observer == other.observer && + type == other.type && + source == other.source; +} + +NotificationRegistrar::NotificationRegistrar() { +} + +NotificationRegistrar::~NotificationRegistrar() { + RemoveAll(); +} + +void NotificationRegistrar::Add(NotificationObserver* observer, + NotificationType type, + const NotificationSource& source) { + Record record = { observer, type, source }; + DCHECK(std::find(registered_.begin(), registered_.end(), record) == + registered_.end()) << "Duplicate registration."; + registered_.push_back(record); + + NotificationService::current()->AddObserver(observer, type, source); +} + +void NotificationRegistrar::Remove(NotificationObserver* observer, + NotificationType type, + const NotificationSource& source) { + Record record = { observer, type, source }; + RecordVector::iterator found = std::find( + registered_.begin(), registered_.end(), record); + if (found != registered_.end()) { + registered_.erase(found); + } else { + // Fall through to passing the removal through to the notification service. + // If it really isn't registered, it will also assert and do nothing, but + // we might as well catch the case where the class is trying to unregister + // for something they registered without going through us. + NOTREACHED(); + } + + NotificationService::current()->RemoveObserver(observer, type, source); +} + +void NotificationRegistrar::RemoveAll() { + NotificationService* service = NotificationService::current(); + for (size_t i = 0; i < registered_.size(); i++) { + service->RemoveObserver(registered_[i].observer, + registered_[i].type, + registered_[i].source); + } + registered_.clear(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_registrar.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_registrar.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_registrar.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_registrar.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,53 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_NOTIFICATION_REGISTRAR_H_ +#define CHROME_COMMON_NOTIFICATION_REGISTRAR_H_ + +#include + +#include "base/basictypes.h" +#include "chrome/common/notification_observer.h" + +// Aids in registering for notifications and ensures that all registered +// notifications are unregistered when the class is destroyed. +// +// The intended use is that you make a NotificationRegistrar member in your +// class and use it to register your notifications instead of going through the +// notification service directly. It will automatically unregister them for +// you. +class NotificationRegistrar { + public: + // This class must not be derived from (we don't have a virtual destructor so + // it won't work). Instead, use it as a member in your class. + NotificationRegistrar(); + ~NotificationRegistrar(); + + // Wrappers around NotificationService::[Add|Remove]Observer. + void Add(NotificationObserver* observer, + NotificationType type, + const NotificationSource& source); + void Remove(NotificationObserver* observer, + NotificationType type, + const NotificationSource& source); + + // Unregisters all notifications. + void RemoveAll(); + + private: + struct Record; + + // We keep registered notifications in a simple vector. This means we'll do + // brute-force searches when removing them individually, but individual + // removal is uncommon, and there will typically only be a couple of + // notifications anyway. + typedef std::vector RecordVector; + + // Lists all notifications we're currently registered for. + RecordVector registered_; + + DISALLOW_COPY_AND_ASSIGN(NotificationRegistrar); +}; + +#endif // CHROME_COMMON_NOTIFICATION_REGISTRAR_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_service.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_service.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_service.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_service.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,143 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/notification_service.h" + +#include "base/lazy_instance.h" +#include "base/thread_local.h" + +static base::LazyInstance > + lazy_tls_ptr(base::LINKER_INITIALIZED); + +// static +NotificationService* NotificationService::current() { + return lazy_tls_ptr.Pointer()->Get(); +} + +// static +bool NotificationService::HasKey(const NotificationSourceMap& map, + const NotificationSource& source) { + return map.find(source.map_key()) != map.end(); +} + +NotificationService::NotificationService() { + DCHECK(current() == NULL); +#ifndef NDEBUG + memset(observer_counts_, 0, sizeof(observer_counts_)); +#endif + + lazy_tls_ptr.Pointer()->Set(this); +} + +void NotificationService::AddObserver(NotificationObserver* observer, + NotificationType type, + const NotificationSource& source) { + DCHECK(type.value < NotificationType::NOTIFICATION_TYPE_COUNT); + + // We have gotten some crashes where the observer pointer is NULL. The problem + // is that this happens when we actually execute a notification, so have no + // way of knowing who the bad observer was. We want to know when this happens + // in release mode so we know what code to blame the crash on (since this is + // guaranteed to crash later). + CHECK(observer); + + NotificationObserverList* observer_list; + if (HasKey(observers_[type.value], source)) { + observer_list = observers_[type.value][source.map_key()]; + } else { + observer_list = new NotificationObserverList; + observers_[type.value][source.map_key()] = observer_list; + } + + observer_list->AddObserver(observer); +#ifndef NDEBUG + ++observer_counts_[type.value]; +#endif +} + +void NotificationService::RemoveObserver(NotificationObserver* observer, + NotificationType type, + const NotificationSource& source) { + DCHECK(type.value < NotificationType::NOTIFICATION_TYPE_COUNT); + DCHECK(HasKey(observers_[type.value], source)); + + NotificationObserverList* observer_list = + observers_[type.value][source.map_key()]; + if (observer_list) { + observer_list->RemoveObserver(observer); +#ifndef NDEBUG + --observer_counts_[type.value]; +#endif + } + + // TODO(jhughes): Remove observer list from map if empty? +} + +void NotificationService::Notify(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type.value > NotificationType::ALL) << + "Allowed for observing, but not posting."; + DCHECK(type.value < NotificationType::NOTIFICATION_TYPE_COUNT); + + // There's no particular reason for the order in which the different + // classes of observers get notified here. + + // Notify observers of all types and all sources + if (HasKey(observers_[NotificationType::ALL], AllSources()) && + source != AllSources()) { + FOR_EACH_OBSERVER(NotificationObserver, + *observers_[NotificationType::ALL][AllSources().map_key()], + Observe(type, source, details)); + } + + // Notify observers of all types and the given source + if (HasKey(observers_[NotificationType::ALL], source)) { + FOR_EACH_OBSERVER(NotificationObserver, + *observers_[NotificationType::ALL][source.map_key()], + Observe(type, source, details)); + } + + // Notify observers of the given type and all sources + if (HasKey(observers_[type.value], AllSources()) && + source != AllSources()) { + FOR_EACH_OBSERVER(NotificationObserver, + *observers_[type.value][AllSources().map_key()], + Observe(type, source, details)); + } + + // Notify observers of the given type and the given source + if (HasKey(observers_[type.value], source)) { + FOR_EACH_OBSERVER(NotificationObserver, + *observers_[type.value][source.map_key()], + Observe(type, source, details)); + } +} + + +NotificationService::~NotificationService() { + lazy_tls_ptr.Pointer()->Set(NULL); + +#ifndef NDEBUG + for (int i = 0; i < NotificationType::NOTIFICATION_TYPE_COUNT; i++) { + if (observer_counts_[i] > 0) { + // This may not be completely fixable -- see + // http://code.google.com/p/chromium/issues/detail?id=11010 . + // But any new leaks should be fixed. + LOG(WARNING) << observer_counts_[i] << " notification observer(s) leaked" + << " of notification type " << i; + } + } +#endif + + for (int i = 0; i < NotificationType::NOTIFICATION_TYPE_COUNT; i++) { + NotificationSourceMap omap = observers_[i]; + for (NotificationSourceMap::iterator it = omap.begin(); + it != omap.end(); ++it) { + delete it->second; + } + } +} + +NotificationObserver::~NotificationObserver() {} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_service.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_service.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_service.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_service.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,100 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file describes a central switchboard for notifications that might +// happen in various parts of the application, and allows users to register +// observers for various classes of events that they're interested in. + +#ifndef CHROME_COMMON_NOTIFICATION_SERVICE_H_ +#define CHROME_COMMON_NOTIFICATION_SERVICE_H_ + +#include + +#include "base/observer_list.h" +#include "chrome/common/notification_details.h" +#include "chrome/common/notification_observer.h" +#include "chrome/common/notification_source.h" +#include "chrome/common/notification_type.h" + +class NotificationObserver; + +class NotificationService { + public: + // Returns the NotificationService object for the current thread, or NULL if + // none. + static NotificationService* current(); + + // Normally instantiated when the thread is created. Not all threads have + // a NotificationService. Only one instance should be created per thread. + NotificationService(); + ~NotificationService(); + + // Registers a NotificationObserver to be called whenever a matching + // notification is posted. Observer is a pointer to an object subclassing + // NotificationObserver to be notified when an event matching the other two + // parameters is posted to this service. Type is the type of events to + // be notified about (or NOTIFY_ALL to receive events of all types). + // Source is a NotificationSource object (created using + // "Source(pointer)"), if this observer only wants to + // receive events from that object, or NotificationService::AllSources() + // to receive events from all sources. + // + // A given observer can be registered only once for each combination of + // type and source. If the same object is registered more than once, + // it must be removed for each of those combinations of type and source later. + // + // The caller retains ownership of the object pointed to by observer. + void AddObserver(NotificationObserver* observer, + NotificationType type, const NotificationSource& source); + + // Removes the object pointed to by observer from receiving notifications + // that match type and source. If no object matching the parameters is + // currently registered, this method is a no-op. + void RemoveObserver(NotificationObserver* observer, + NotificationType type, const NotificationSource& source); + + // Synchronously posts a notification to all interested observers. + // Source is a reference to a NotificationSource object representing + // the object originating the notification (can be + // NotificationService::AllSources(), in which case + // only observers interested in all sources will be notified). + // Details is a reference to an object containing additional data about + // the notification. If no additional data is needed, NoDetails() is used. + // There is no particular order in which the observers will be notified. + void Notify(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + // Returns a NotificationSource that represents all notification sources + // (for the purpose of registering an observer for events from all sources). + static Source AllSources() { return Source(NULL); } + + // Returns a NotificationDetails object that represents a lack of details + // associated with a notification. (This is effectively a null pointer.) + static Details NoDetails() { return Details(NULL); } + + private: + typedef ObserverList NotificationObserverList; + typedef std::map NotificationSourceMap; + + // Convenience function to determine whether a source has a + // NotificationObserverList in the given map; + static bool HasKey(const NotificationSourceMap& map, + const NotificationSource& source); + + // Keeps track of the observers for each type of notification. + // Until we get a prohibitively large number of notification types, + // a simple array is probably the fastest way to dispatch. + NotificationSourceMap observers_[NotificationType::NOTIFICATION_TYPE_COUNT]; + +#ifndef NDEBUG + // Used to check to see that AddObserver and RemoveObserver calls are + // balanced. + int observer_counts_[NotificationType::NOTIFICATION_TYPE_COUNT]; +#endif + + DISALLOW_COPY_AND_ASSIGN(NotificationService); +}; + +#endif // CHROME_COMMON_NOTIFICATION_SERVICE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_service_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_service_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_service_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_service_unittest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,185 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/notification_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class NotificationServiceTest: public testing::Test { +}; + +class TestObserver : public NotificationObserver { +public: + TestObserver() : notification_count_(0) {} + + int notification_count() { return notification_count_; } + + void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + ++notification_count_; + } + +private: + int notification_count_; +}; + +// Bogus class to act as a NotificationSource for the messages. +class TestSource {}; + +} // namespace + + +TEST(NotificationServiceTest, Basic) { + TestSource test_source; + TestSource other_source; + + // Check the equality operators defined for NotificationSource + EXPECT_TRUE( + Source(&test_source) == Source(&test_source)); + EXPECT_TRUE( + Source(&test_source) != Source(&other_source)); + + TestObserver all_types_all_sources; + TestObserver idle_all_sources; + TestObserver all_types_test_source; + TestObserver idle_test_source; + + NotificationService* service = NotificationService::current(); + + // Make sure it doesn't freak out when there are no observers. + service->Notify(NotificationType::IDLE, + Source(&test_source), + NotificationService::NoDetails()); + + service->AddObserver(&all_types_all_sources, + NotificationType::ALL, + NotificationService::AllSources()); + service->AddObserver(&idle_all_sources, + NotificationType::IDLE, + NotificationService::AllSources()); + service->AddObserver(&all_types_test_source, + NotificationType::ALL, + Source(&test_source)); + service->AddObserver(&idle_test_source, + NotificationType::IDLE, + Source(&test_source)); + + EXPECT_EQ(0, all_types_all_sources.notification_count()); + EXPECT_EQ(0, idle_all_sources.notification_count()); + EXPECT_EQ(0, all_types_test_source.notification_count()); + EXPECT_EQ(0, idle_test_source.notification_count()); + + service->Notify(NotificationType::IDLE, + Source(&test_source), + NotificationService::NoDetails()); + + EXPECT_EQ(1, all_types_all_sources.notification_count()); + EXPECT_EQ(1, idle_all_sources.notification_count()); + EXPECT_EQ(1, all_types_test_source.notification_count()); + EXPECT_EQ(1, idle_test_source.notification_count()); + + service->Notify(NotificationType::BUSY, + Source(&test_source), + NotificationService::NoDetails()); + + EXPECT_EQ(2, all_types_all_sources.notification_count()); + EXPECT_EQ(1, idle_all_sources.notification_count()); + EXPECT_EQ(2, all_types_test_source.notification_count()); + EXPECT_EQ(1, idle_test_source.notification_count()); + + service->Notify(NotificationType::IDLE, + Source(&other_source), + NotificationService::NoDetails()); + + EXPECT_EQ(3, all_types_all_sources.notification_count()); + EXPECT_EQ(2, idle_all_sources.notification_count()); + EXPECT_EQ(2, all_types_test_source.notification_count()); + EXPECT_EQ(1, idle_test_source.notification_count()); + + service->Notify(NotificationType::BUSY, + Source(&other_source), + NotificationService::NoDetails()); + + EXPECT_EQ(4, all_types_all_sources.notification_count()); + EXPECT_EQ(2, idle_all_sources.notification_count()); + EXPECT_EQ(2, all_types_test_source.notification_count()); + EXPECT_EQ(1, idle_test_source.notification_count()); + + // Try send with NULL source. + service->Notify(NotificationType::IDLE, + NotificationService::AllSources(), + NotificationService::NoDetails()); + + EXPECT_EQ(5, all_types_all_sources.notification_count()); + EXPECT_EQ(3, idle_all_sources.notification_count()); + EXPECT_EQ(2, all_types_test_source.notification_count()); + EXPECT_EQ(1, idle_test_source.notification_count()); + + service->RemoveObserver(&all_types_all_sources, + NotificationType::ALL, + NotificationService::AllSources()); + service->RemoveObserver(&idle_all_sources, + NotificationType::IDLE, + NotificationService::AllSources()); + service->RemoveObserver(&all_types_test_source, + NotificationType::ALL, + Source(&test_source)); + service->RemoveObserver(&idle_test_source, + NotificationType::IDLE, + Source(&test_source)); + + service->Notify(NotificationType::IDLE, + Source(&test_source), + NotificationService::NoDetails()); + + EXPECT_EQ(5, all_types_all_sources.notification_count()); + EXPECT_EQ(3, idle_all_sources.notification_count()); + EXPECT_EQ(2, all_types_test_source.notification_count()); + EXPECT_EQ(1, idle_test_source.notification_count()); + + // Removing an observer that isn't there is a no-op, this should be fine. + service->RemoveObserver(&all_types_all_sources, + NotificationType::ALL, + NotificationService::AllSources()); +} + +TEST(NotificationServiceTest, MultipleRegistration) { + TestSource test_source; + + TestObserver idle_test_source; + + NotificationService* service = NotificationService::current(); + + service->AddObserver(&idle_test_source, + NotificationType::IDLE, + Source(&test_source)); + service->AddObserver(&idle_test_source, + NotificationType::ALL, + Source(&test_source)); + + service->Notify(NotificationType::IDLE, + Source(&test_source), + NotificationService::NoDetails()); + EXPECT_EQ(2, idle_test_source.notification_count()); + + service->RemoveObserver(&idle_test_source, + NotificationType::IDLE, + Source(&test_source)); + + service->Notify(NotificationType::IDLE, + Source(&test_source), + NotificationService::NoDetails()); + EXPECT_EQ(3, idle_test_source.notification_count()); + + service->RemoveObserver(&idle_test_source, + NotificationType::ALL, + Source(&test_source)); + + service->Notify(NotificationType::IDLE, + Source(&test_source), + NotificationService::NoDetails()); + EXPECT_EQ(3, idle_test_source.notification_count()); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_source.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_source.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_source.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_source.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,51 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file defines the type used to provide sources for NotificationService +// notifications. + +#ifndef CHROME_COMMON_NOTIFICATION_SOURCE_H__ +#define CHROME_COMMON_NOTIFICATION_SOURCE_H__ + +#include "base/basictypes.h" + +// Do not declare a NotificationSource directly--use either +// "Source(sourceclasspointer)" or +// NotificationService::AllSources(). +class NotificationSource { + public: + NotificationSource(const NotificationSource& other) : ptr_(other.ptr_) { } + ~NotificationSource() {} + + // NotificationSource can be used as the index for a map; this method + // returns the pointer to the current source as an identifier, for use as a + // map index. + uintptr_t map_key() const { return reinterpret_cast(ptr_); } + + bool operator!=(const NotificationSource& other) const { + return ptr_ != other.ptr_; + } + bool operator==(const NotificationSource& other) const { + return ptr_ == other.ptr_; + } + + protected: + NotificationSource(void* ptr) : ptr_(ptr) {} + + void* ptr_; +}; + +template +class Source : public NotificationSource { + public: + Source(T* ptr) : NotificationSource(ptr) {} + + Source(const NotificationSource& other) + : NotificationSource(other) {} + + T* operator->() const { return ptr(); } + T* ptr() const { return static_cast(ptr_); } +}; + +#endif // CHROME_COMMON_NOTIFICATION_SOURCE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_type.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_type.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_type.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/notification_type.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,574 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_NOTIFICATION_TYPE_H_ +#define CHROME_COMMON_NOTIFICATION_TYPE_H_ + +// This file describes various types used to describe and filter notifications +// that pass through the NotificationService. +// +// It is written as an enum inside a class so that it can be forward declared. +// You're not allowed to forward declare an enum, and we want to forward +// declare this since it's required by NotificationObserver which is included +// by a lot of header files. +// +// Since this class encapsulates an integral value, it should be passed by +// value. +class NotificationType { + public: + enum Type { + // General ----------------------------------------------------------------- + + // Special signal value to represent an interest in all notifications. + // Not valid when posting a notification. + ALL = 0, + + // The app is done processing user actions, now is a good time to do + // some background work. + IDLE, + + // Means that the app has just started doing something in response to a + // user action, and that background processes shouldn't run if avoidable. + BUSY, + + // This is sent when the user does a gesture resulting in a noteworthy + // action taking place. This is typically used for logging. The source is + // the profile, and the details is a wstring identifying the action. + USER_ACTION, + + // NavigationController ---------------------------------------------------- + + // A new pending navigation has been created. Pending entries are created + // when the user requests the navigation. We don't know if it will actually + // happen until it does (at this point, it will be "committed." Note that + // renderer- initiated navigations such as link clicks will never be + // pending. + // + // This notification is called after the pending entry is created, but + // before we actually try to navigate. The source will be the + // NavigationController that owns the pending entry, and there are no + // details. + NAV_ENTRY_PENDING, + + // A new non-pending navigation entry has been created. This will + // correspond to one NavigationController entry being created (in the case + // of new navigations) or renavigated to (for back/forward navigations). + // + // The source will be the navigation controller doing the commit. The + // details will be NavigationController::LoadCommittedDetails. + NAV_ENTRY_COMMITTED, + + // Indicates that the NavigationController given in the Source has + // decreased its back/forward list count by removing entries from either + // the front or back of its list. This is usually the result of going back + // and then doing a new navigation, meaning all the "forward" items are + // deleted. + // + // This normally happens as a result of a new navigation. It will be + // followed by a NAV_ENTRY_COMMITTED message for the new page that + // caused the pruning. It could also be a result of removing an item from + // the list to fix up after interstitials. + // + // The details are NavigationController::PrunedDetails. + NAV_LIST_PRUNED, + + // Indicates that a NavigationEntry has changed. The source will be the + // NavigationController that owns the NavigationEntry. The details will be + // a NavigationController::EntryChangedDetails struct. + // + // This will NOT be sent on navigation, interested parties should also + // listen for NAV_ENTRY_COMMITTED to handle that case. This will be + // sent when the entry is updated outside of navigation (like when a new + // title comes). + NAV_ENTRY_CHANGED, + + // Other load-related (not from NavigationController) ---------------------- + + // A content load is starting. The source will be a + // Source corresponding to the tab in which the load + // is occurring. No details are expected for this notification. + LOAD_START, + + // A content load has stopped. The source will be a + // Source corresponding to the tab in which the load + // is occurring. Details in the form of a LoadNotificationDetails object + // are optional. + LOAD_STOP, + + // A frame is staring a provisional load. The source is a + // Source corresponding to the tab in which the load + // occurs. Details is a bool specifying if the load occurs in the main + // frame (or a sub-frame if false). + FRAME_PROVISIONAL_LOAD_START, + + // Content was loaded from an in-memory cache. The source will be a + // Source corresponding to the tab in which the load + // occurred. Details in the form of a LoadFromMemoryCacheDetails object + // are provided. + LOAD_FROM_MEMORY_CACHE, + + // A provisional content load has failed with an error. The source will be + // a Source corresponding to the tab in which the + // load occurred. Details in the form of a ProvisionalLoadDetails object + // are provided. + FAIL_PROVISIONAL_LOAD_WITH_ERROR, + + // A response has been received for a resource request. The source will be + // a Source corresponding to the tab in which the + // request was issued. Details in the form of a ResourceRequestDetails + // object are provided. + RESOURCE_RESPONSE_STARTED, + + // The response to a resource request has completed. The source will be a + // Source corresponding to the tab in which the + // request was issued. Details in the form of a ResourceRequestDetails + // object are provided. + RESOURCE_RESPONSE_COMPLETED, + + // A redirect was received while requesting a resource. The source will be + // a Source corresponding to the tab in which the + // request was issued. Details in the form of a ResourceRedirectDetails + // are provided. + RESOURCE_RECEIVED_REDIRECT, + + // The SSL state of a page has changed in some visible way. For example, + // if an insecure resource is loaded on a secure page. Note that a + // toplevel load commit will also update the SSL state (since the + // NavigationEntry is new) and this message won't always be sent in that + // case. Listen to this notification if you need to refresh SSL-related UI + // elements. + // + // The source will be the navigation controller associated with the load. + // There are no details. The entry changed will be the active entry of the + // controller. + SSL_VISIBLE_STATE_CHANGED, + + // The SSL state of the browser has changed in some internal way. For + // example, the user might have explicitly allowed some broken certificate + // or a secure origin might have included some insecure content. Listen to + // this notifiation if you need to keep track of our internal SSL state. + // + // The source will be the navigation controller associated with the state + // change. There are no details. + SSL_INTERNAL_STATE_CHANGED, + + // Lets resource handlers and other interested observers know when the + // message filter is being deleted and can no longer be used. + RESOURCE_MESSAGE_FILTER_SHUTDOWN, + + // Views ------------------------------------------------------------------- + + // Notification that a view was removed from a view hierarchy. The source + // is the view, the details is the parent view. + VIEW_REMOVED, + + // Browser-window ---------------------------------------------------------- + + // This message is sent after a window has been opened. The source is a + // Source with a pointer to the new window. No details are + // expected. + BROWSER_OPENED, + + // This message is sent after a window has been closed. The source is a + // Source with a pointer to the closed window. Details is a + // boolean that if true indicates that the application will be closed as a + // result of this browser window closure (i.e. this was the last opened + // browser window). Note that the boolean pointed to by Details is only + // valid for the duration of this call. + BROWSER_CLOSED, + + // This message is sent when the last window considered to be an + // "application window" has been closed. Dependent/dialog/utility windows + // can use this as a way to know that they should also close. No source or + // details are passed. + ALL_APPWINDOWS_CLOSED, + + // Indicates a new top window has been created. The source is the + // WindowWin. + WINDOW_CREATED, + + // Indicates that a top window has been closed. The source is the HWND + // that was closed, no details are expected. + WINDOW_CLOSED, + + // Sent when an info bubble has been created but not yet shown. The source + // is the InfoBubble. + INFO_BUBBLE_CREATED, + + // Tabs -------------------------------------------------------------------- + + // This notification is sent after a tab has been appended to the + // tab_strip. The source is a Source with a pointer + // to controller for the added tab. There are no details. + TAB_PARENTED, + + // This message is sent before a tab has been closed. The source is a + // Source with a pointer to the controller for the + // closed tab. No details are expected. + // + // See also TAB_CLOSED. + TAB_CLOSING, + + // Notification that a tab has been closed. The source is the + // NavigationController with no details. + TAB_CLOSED, + + // This notification is sent when a render view host has connected to a + // renderer process. The source is a Source with a pointer to + // the TabContents. A TAB_CONTENTS_DISCONNECTED notification is + // guaranteed before the source pointer becomes junk. No details are + // expected. + TAB_CONTENTS_CONNECTED, + + // This notification is sent when a TabContents swaps its render view host + // with another one, possibly changing processes. The source is a + // Source with a pointer to the TabContents. A + // TAB_CONTENTS_DISCONNECTED notification is guaranteed before the + // source pointer becomes junk. No details are expected. + TAB_CONTENTS_SWAPPED, + + // This message is sent after a TabContents is disconnected from the + // renderer process. The source is a Source with a pointer to + // the TabContents (the pointer is usable). No details are expected. + TAB_CONTENTS_DISCONNECTED, + + // This message is sent when a new InfoBar has been added to a TabContents. + // The source is a Source with a pointer to the TabContents + // the InfoBar was added to. The details is a Details with + // a pointer to an object implementing the InfoBarDelegate interface for + // the InfoBar that was added. + TAB_CONTENTS_INFOBAR_ADDED, + + // This message is sent when an InfoBar is about to be removed from a + // TabContents. The source is a Source with a pointer to the + // TabContents the InfoBar was removed from. The details is a + // Details with a pointer to an object implementing the + // InfoBarDelegate interface for the InfoBar that was removed. + TAB_CONTENTS_INFOBAR_REMOVED, + + // This is sent when an externally hosted tab is created. The details + // contain the ExternalTabContainer that contains the tab + EXTERNAL_TAB_CREATED, + + // This is sent when an externally hosted tab is closed. No details are + // expected. + EXTERNAL_TAB_CLOSED, + + // Indicates that the new page tab has finished loading. This is used for + // performance testing to see how fast we can load it after startup, and is + // only called once for the lifetime of the browser. The source is unused. + // Details is an integer: the number of milliseconds elapsed between + // starting and finishing all painting. + INITIAL_NEW_TAB_UI_LOAD, + + // This notification is sent when a TabContents is being hidden, e.g. due + // to switching away from this tab. The source is a Source. + TAB_CONTENTS_HIDDEN, + + // This notification is sent when a TabContents is being destroyed. Any + // object holding a reference to a TabContents can listen to that + // notification to properly reset the reference. The source is a + // Source. + TAB_CONTENTS_DESTROYED, + + // Stuff inside the tabs --------------------------------------------------- + + // This message is sent after a constrained window has been closed. The + // source is a Source with a pointer to the closed child + // window. (The pointer isn't usable, except for identification.) No + // details are expected. + CWINDOW_CLOSED, + + // Indicates that a RenderProcessHost is destructing. The source will be the + // RenderProcessHost that corresponds to the process. + RENDERER_PROCESS_TERMINATED, + + // Indicates that a render process was closed (meaning it exited, but the + // RenderProcessHost might be reused). The source will be the corresponding + // RenderProcessHost. The details will be a bool which is true if the + // process crashed. This may get sent along with + // RENDERER_PROCESS_TERMINATED. + RENDERER_PROCESS_CLOSED, + + // Indicates that a render process has become unresponsive for a period of + // time. The source will be the RenderWidgetHost that corresponds to the + // hung view, and no details are expected. + RENDERER_PROCESS_HANG, + + // Indicates that a render process is created in the sandbox. The source + // will be the RenderProcessHost that corresponds to the created process + // and the detail is a bool telling us if the process got created on the + // sandbox desktop or not. + RENDERER_PROCESS_IN_SBOX, + + // This is sent to notify that the RenderViewHost displayed in a + // TabContents has changed. Source is the TabContents for which the change + // happened, details is the previous RenderViewHost (can be NULL when the + // first RenderViewHost is set). + RENDER_VIEW_HOST_CHANGED, + + // This is sent when a RenderWidgetHost is being destroyed. The source is + // the RenderWidgetHost, the details are not used. + RENDER_WIDGET_HOST_DESTROYED, + + // Notification from TabContents that we have received a response from the + // renderer after using the dom inspector. + DOM_INSPECT_ELEMENT_RESPONSE, + + // Notification from TabContents that we have received a response from the + // renderer in response to a dom automation controller action. + DOM_OPERATION_RESPONSE, + + // Sent when the bookmark bubble hides. The source is the profile, the + // details unused. + BOOKMARK_BUBBLE_HIDDEN, + + // This notification is sent when the result of a find-in-page search is + // available with the browser process. The source is a Source + // with a pointer to the TabContents. Details encompass a + // FindNotificationDetail object that tells whether the match was found or + // not found. + FIND_RESULT_AVAILABLE, + + // This is sent when the users preference for when the bookmark bar should + // be shown changes. The source is the profile, and the details are + // NoDetails. + BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, + + // Used to monitor web cache usage by notifying whenever the + // CacheManagerHost observes new UsageStats. The source will be the + // RenderProcessHost that corresponds to the new statistics. Details are a + // UsageStats object sent by the renderer, and should be copied - ptr not + // guaranteed to be valid after the notification. + WEB_CACHE_STATS_OBSERVED, + + // Child Processes --------------------------------------------------------- + + // This notification is sent when a child process host has connected to a + // child process. There is no usable source, since it is sent from an + // ephemeral task; register for AllSources() to receive this notification. + // The details are in a Details. + CHILD_PROCESS_HOST_CONNECTED, + + // This message is sent after a ChildProcessHost is disconnected from the + // child process. There is no usable source, since it is sent from an + // ephemeral task; register for AllSources() to receive this notification. + // The details are in a Details. + CHILD_PROCESS_HOST_DISCONNECTED, + + // This message is sent when a child process disappears unexpectedly. + // There is no usable source, since it is sent from an ephemeral task; + // register for AllSources() to receive this notification. The details are + // in a Details. + CHILD_PROCESS_CRASHED, + + // This message indicates that an instance of a particular child was + // created in a page. (If one page contains several regions rendered by + // the same child, this notification will occur once for each region + // during the page load.) + // + // There is no usable source, since it is sent from an ephemeral task; + // register for AllSources() to receive this notification. The details are + // in a Details. + CHILD_INSTANCE_CREATED, + + // This is sent when network interception is disabled for a plugin, or the + // plugin is unloaded. This should only be sent/received on the browser IO + // thread or the plugin thread. The source is the plugin that is disabling + // interception. No details are expected. + CHROME_PLUGIN_UNLOADED, + + // This is sent when a login prompt is shown. The source is the + // Source for the tab in which the prompt is shown. + // Details are a LoginNotificationDetails which provide the LoginHandler + // that should be given authentication. + AUTH_NEEDED, + + // This is sent when authentication credentials have been supplied (either + // by the user or by an automation service), but before we've actually + // received another response from the server. The source is the + // Source for the tab in which the prompt was shown. + // No details are expected. + AUTH_SUPPLIED, + + // History ----------------------------------------------------------------- + + // Sent when a history service is created on the main thread. This is sent + // after history is created, but before it has finished loading. Use + // HISTORY_LOADED is you need to know when loading has completed. + // The source is the profile that the history service belongs to, and the + // details is the pointer to the newly created HistoryService object. + HISTORY_CREATED, + + // Sent when a history service has finished loading. The source is the + // profile that the history service belongs to, and the details is the + // HistoryService. + HISTORY_LOADED, + + // Sent when a URL that has been typed has been added or modified. This is + // used by the in-memory URL database (used by autocomplete) to track + // changes to the main history system. + // + // The source is the profile owning the history service that changed, and + // the details is history::URLsModifiedDetails that lists the modified or + // added URLs. + HISTORY_TYPED_URLS_MODIFIED, + + // Sent when the user visits a URL. + // + // The source is the profile owning the history service that changed, and + // the details is history::URLVisitedDetails. + HISTORY_URL_VISITED, + + // Sent when one or more URLs are deleted. + // + // The source is the profile owning the history service that changed, and + // the details is history::URLsDeletedDetails that lists the deleted URLs. + HISTORY_URLS_DELETED, + + // Sent by history when the favicon of a URL changes. The source is the + // profile, and the details is history::FavIconChangeDetails (see + // history_notifications.h). + FAVICON_CHANGED, + + // Bookmarks --------------------------------------------------------------- + + // Sent when the starred state of a URL changes. A URL is starred if there + // is at least one bookmark for it. The source is a Profile and the details + // is history::URLsStarredDetails that contains the list of URLs and + // whether they were starred or unstarred. + URLS_STARRED, + + // Sent when the bookmark bar model finishes loading. This source is the + // Profile, and the details aren't used. + BOOKMARK_MODEL_LOADED, + + // Sent when the spellchecker object changes. Note that this is not sent + // the first time the spellchecker gets initialized. The source is the + // profile, the details is SpellcheckerReinitializedDetails defined in + // profile. + SPELLCHECKER_REINITIALIZED, + + // Sent when the bookmark bubble is shown for a particular URL. The source + // is the profile, the details the URL. + BOOKMARK_BUBBLE_SHOWN, + + // Non-history storage services -------------------------------------------- + + // Notification that the TemplateURLModel has finished loading from the + // database. The source is the TemplateURLModel, and the details are + // NoDetails. + TEMPLATE_URL_MODEL_LOADED, + + // Notification triggered when a web application has been installed or + // uninstalled. Any application view should reload its data. The source is + // the profile. No details are provided. + WEB_APP_INSTALL_CHANGED, + + // This is sent to a pref observer when a pref is changed. + PREF_CHANGED, + + // Sent when a default request context has been created, so calling + // Profile::GetDefaultRequestContext() will not return NULL. This is sent + // on the thread where Profile::GetRequestContext() is first called, which + // should be the UI thread. + DEFAULT_REQUEST_CONTEXT_AVAILABLE, + + // Autocomplete ------------------------------------------------------------ + + // Sent by the autocomplete controller at least once per query, each time + // new matches are available, subject to rate-limiting/coalescing to reduce + // the number of updates. There are no details. + AUTOCOMPLETE_CONTROLLER_RESULT_UPDATED, + + // Sent by the autocomplete controller once per query, immediately after + // synchronous matches become available. There are no details. + AUTOCOMPLETE_CONTROLLER_SYNCHRONOUS_MATCHES_AVAILABLE, + + // This is sent when an item of the Omnibox popup is selected. The source + // is the profile. + OMNIBOX_OPENED_URL, + + // Sent by the autocomplete edit when it is destroyed. + AUTOCOMPLETE_EDIT_DESTROYED, + + // Sent when the main Google URL has been updated. Some services cache + // this value and need to update themselves when it changes. See + // google_util::GetGoogleURLAndUpdateIfNecessary(). + GOOGLE_URL_UPDATED, + + // Printing ---------------------------------------------------------------- + + // Notification from a PrintedDocument that it has been updated. It may be + // that a printed page has just been generated or that the document's + // number of pages has been calculated. Details is the new page or NULL if + // only the number of pages in the document has been updated. + PRINTED_DOCUMENT_UPDATED, + + // Notification from PrintJob that an event occured. It can be that a page + // finished printing or that the print job failed. Details is + // PrintJob::EventDetails. + PRINT_JOB_EVENT, + + // Shutdown ---------------------------------------------------------------- + + // Sent on the browser IO thread when an URLRequestContext is released by + // its owning Profile. The source is a pointer to the URLRequestContext. + URL_REQUEST_CONTEXT_RELEASED, + + // Sent when WM_ENDSESSION has been received, after the browsers have been + // closed but before browser process has been shutdown. The source/details + // are all source and no details. + SESSION_END, + + // Personalization --------------------------------------------------------- + + PERSONALIZATION, + PERSONALIZATION_CREATED, + + // User Scripts ------------------------------------------------------------ + + // Sent when there are new user scripts available. The details are a + // pointer to SharedMemory containing the new scripts. + USER_SCRIPTS_LOADED, + + // Extensions -------------------------------------------------------------- + + // Sent when new extensions are loaded. The details are an ExtensionList*. + EXTENSIONS_LOADED, + + // Sent when new extensions are installed. The details are a FilePath. + EXTENSION_INSTALLED, + + // Debugging --------------------------------------------------------------- + + // Sent from ~RenderViewHost. The source is the RenderViewHost. + RENDER_VIEW_HOST_DELETED, + + // Count (must be last) ---------------------------------------------------- + // Used to determine the number of notification types. Not valid as + // a type parameter when registering for or posting notifications. + NOTIFICATION_TYPE_COUNT + }; + + NotificationType(Type v) : value(v) {} + + bool operator==(NotificationType t) const { return value == t.value; } + bool operator!=(NotificationType t) const { return value != t.value; } + + // Comparison to explicit enum values. + bool operator==(Type v) const { return value == v; } + bool operator!=(Type v) const { return value != v; } + + Type value; +}; + +inline bool operator==(NotificationType::Type a, NotificationType b) { + return a == b.value; +} +inline bool operator!=(NotificationType::Type a, NotificationType b) { + return a != b.value; +} + +#endif // CHROME_COMMON_NOTIFICATION_TYPE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/owned_widget_gtk.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/owned_widget_gtk.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/owned_widget_gtk.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/owned_widget_gtk.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,40 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/owned_widget_gtk.h" + +#include + +#include "base/logging.h" + +OwnedWidgetGtk::~OwnedWidgetGtk() { + DCHECK(!widget_) << "You must explicitly call OwnerWidgetGtk::Destroy()."; +} + +void OwnedWidgetGtk::Own(GtkWidget* widget) { + DCHECK(!widget_); + // We want to make sure that Own() was called properly, right after the + // widget was created. We should have a floating refcount of 1. + DCHECK(g_object_is_floating(widget)); + // NOTE: Assumes some implementation details about glib internals. + DCHECK(G_OBJECT(widget)->ref_count == 1); + + // Sink the floating reference, we should now own this reference. + g_object_ref_sink(widget); + widget_ = widget; +} + +void OwnedWidgetGtk::Destroy() { + if (!widget_) + return; + + GtkWidget* widget = widget_; + widget_ = NULL; + gtk_widget_destroy(widget); + + DCHECK(!g_object_is_floating(widget)); + // NOTE: Assumes some implementation details about glib internals. + DCHECK(G_OBJECT(widget)->ref_count == 1); + g_object_unref(widget); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/owned_widget_gtk.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/owned_widget_gtk.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/owned_widget_gtk.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/owned_widget_gtk.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,88 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This class assists you in dealing with a specific situation when managing +// ownership between a C++ object and a GTK widget. It is common to have a +// C++ object which encapsulates a GtkWidget, and that widget is exposed from +// the object for use outside of the class. In this situation, you commonly +// want the GtkWidget's lifetime to match its C++ object's lifetime. Using an +// OwnedWigetGtk will take ownership over the initial reference of the +// GtkWidget, so that it is "owned" by the C++ object. Example usage: +// +// class FooViewGtk() { +// public: +// FooViewGtk() { } +// ~FooViewGtk() { widget_.Destroy(); } +// void Init() { vbox_.Own(gtk_vbox_new()); } +// GtkWidget* widget() { return vbox_.get() }; // Host my widget! +// private: +// OwnedWidgetGtk vbox_; +// }; +// +// This design will ensure that the widget stays alive from the call to Own() +// until the call to Destroy(). +// +// - Details of the problem and OwnedWidgetGtk's solution: +// In order to make passing ownership more convenient for newly created +// widgets, GTK has a concept of a "floating" reference. All GtkObjects (and +// thus GtkWidgets) inherit from GInitiallyUnowned. When they are created, the +// object starts with a reference count of 1, but has its floating flag set. +// When it is put into a container for the first time, that container will +// "sink" the floating reference, and the count will still be 1. Now the +// container owns the widget, and if we remove the widget from the container, +// the widget is destroyed. This style of ownership often causes problems when +// you have an object encapsulating the widget. If we just use a raw +// GtkObject* with no specific ownership management, we push the widget's +// ownership onto the user of the class. Now the C++ object can't depend on +// the widget being valid, since it doesn't manage its lifetime. If the widget +// was removed from a container, removing its only reference, it would be +// destroyed (from the C++ object's perspective) unexpectantly destroyed. The +// solution is fairly simple, make sure that the C++ object owns the widget, +// and thus it is also responsible for destroying it. This boils down to: +// GtkWidget* widget = gtk_widget_new(); +// g_object_ref_sink(widget); // Claim the initial floating reference. +// ... +// gtk_destroy_widget(widget); // Ask all code to destroy their references. +// g_object_unref(widget); // Destroy the initial reference we had claimed. + +#ifndef BASE_OWNED_WIDGET_GTK_H_ +#define BASE_OWNED_WIDGET_GTK_H_ + +#include "base/basictypes.h" + +typedef struct _GtkWidget GtkWidget; + +class OwnedWidgetGtk { + public: + // Create an instance that isn't managing any ownership. + OwnedWidgetGtk() : widget_(NULL) { } + // Create an instance that owns |widget|. + explicit OwnedWidgetGtk(GtkWidget* widget) : widget_(NULL) { Own(widget); } + + ~OwnedWidgetGtk(); + + // Return the currently owned widget, or NULL if no widget is owned. + GtkWidget* get() const { return widget_; } + + // Takes ownership of a widget, by taking the initial floating reference of + // the GtkWidget. It is expected that Own() is called right after the widget + // has been created, and before any other references to the widget might have + // been added. It is valid to never call Own(), in which case Destroy() will + // do nothing. If Own() has been called, you must explicitly call Destroy(). + void Own(GtkWidget* widget); + + // You must call Destroy() after you have called Own(). Calling Destroy() + // will call gtk_widget_destroy(), and drop our reference to the widget. + // After a call to Destroy(), you may call Own() again. NOTE: It is expected + // that after gtk_widget_destroy we will be holding the only reference left + // on the object. We assert this in debug mode to help catch any leaks. + void Destroy(); + + private: + GtkWidget* widget_; + + DISALLOW_COPY_AND_ASSIGN(OwnedWidgetGtk); +}; + +#endif // BASE_OWNED_WIDGET_GTK_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/page_action.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/page_action.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/page_action.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/page_action.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,12 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/page_action.h" + +PageAction::PageAction() + : type_(PERMANENT) { +} + +PageAction::~PageAction() { +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/page_action.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/page_action.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/page_action.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/page_action.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,71 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_PAGE_ACTION_H_ +#define CHROME_COMMON_PAGE_ACTION_H_ + +#include +#include + +#include "base/file_path.h" +#include "googleurl/src/gurl.h" + +class PageAction { + public: + PageAction(); + virtual ~PageAction(); + + typedef enum { + PERMANENT = 0, + TAB = 1, + } PageActionType; + + std::string id() const { return id_; } + void set_id(std::string id) { id_ = id; } + + PageActionType type() const { return type_; } + void set_type(PageActionType type) { type_ = type; } + + std::string extension_id() const { return extension_id_; } + void set_extension_id(std::string extension_id) { + extension_id_ = extension_id; + } + + std::string name() const { return name_; } + void set_name(const std::string& name) { name_ = name; } + + FilePath icon_path() const { return icon_path_; } + void set_icon_path(const FilePath& icon_path) { + icon_path_ = icon_path; + } + + std::string tooltip() const { return tooltip_; } + void set_tooltip(const std::string& tooltip) { + tooltip_ = tooltip; + } + + private: + // The id for the PageAction, for example: "RssPageAction". + std::string id_; + + // The type of the PageAction. + PageActionType type_; + + // The id for the extension this PageAction belongs to (as defined in the + // extension manifest). + std::string extension_id_; + + // The name of the PageAction. + std::string name_; + + // The icon that represents the PageIcon. + FilePath icon_path_; + + // The tooltip to show when the mouse hovers over the icon of the page action. + std::string tooltip_; +}; + +typedef std::map PageActionMap; + +#endif // CHROME_COMMON_PAGE_ACTION_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/page_transition_types.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/page_transition_types.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/page_transition_types.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/page_transition_types.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,167 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_PAGE_TRANSITION_TYPES_H__ +#define CHROME_COMMON_PAGE_TRANSITION_TYPES_H__ + +#include "base/basictypes.h" +#include "base/logging.h" + +// This class is for scoping only. +class PageTransition { + public: + // Types of transitions between pages. These are stored in the history + // database to separate visits, and are reported by the renderer for page + // navigations. + // + // WARNING: don't change these numbers. They are written directly into the + // history database, so future versions will need the same values to match + // the enums. + // + // A type is made of a core value and a set of qualifiers. A type has one + // core value and 0 or or more qualifiers. + enum { + // User got to this page by clicking a link on another page. + LINK = 0, + + // User got this page by typing the URL in the URL bar. This should not be + // used for cases where the user selected a choice that didn't look at all + // like a URL; see GENERATED below. + // + // We also use this for other "explicit" navigation actions. + TYPED = 1, + + // User got to this page through a suggestion in the UI, for example, + // through the destinations page. + AUTO_BOOKMARK = 2, + + // This is a subframe navigation. This is any content that is automatically + // loaded in a non-toplevel frame. For example, if a page consists of + // several frames containing ads, those ad URLs will have this transition + // type. The user may not even realize the content in these pages is a + // separate frame, so may not care about the URL (see MANUAL below). + AUTO_SUBFRAME = 3, + + // For subframe navigations that are explicitly requested by the user and + // generate new navigation entries in the back/forward list. These are + // probably more important than frames that were automatically loaded in + // the background because the user probably cares about the fact that this + // link was loaded. + MANUAL_SUBFRAME = 4, + + // User got to this page by typing in the URL bar and selecting an entry + // that did not look like a URL. For example, a match might have the URL + // of a Google search result page, but appear like "Search Google for ...". + // These are not quite the same as TYPED navigations because the user + // didn't type or see the destination URL. + // See also KEYWORD. + GENERATED = 5, + + // The page was specified in the command line or is the start page. + START_PAGE = 6, + + // The user filled out values in a form and submitted it. NOTE that in + // some situations submitting a form does not result in this transition + // type. This can happen if the form uses script to submit the contents. + FORM_SUBMIT = 7, + + // The user "reloaded" the page, either by hitting the reload button or by + // hitting enter in the address bar. NOTE: This is distinct from the + // concept of whether a particular load uses "reload semantics" (i.e. + // bypasses cached data). For this reason, lots of code needs to pass + // around the concept of whether a load should be treated as a "reload" + // separately from their tracking of this transition type, which is mainly + // used for proper scoring for consumers who care about how frequently a + // user typed/visited a particular URL. + // + // SessionRestore and undo tab close use this transition type too. + RELOAD = 8, + + // The url was generated from a replaceable keyword other than the default + // search provider. If the user types a keyword (which also applies to + // tab-to-search) in the omnibox this qualifier is applied to the transition + // type of the generated url. TemplateURLModel then may generate an + // additional visit with a transition type of KEYWORD_GENERATED against the + // url 'http://' + keyword. For example, if you do a tab-to-search against + // wikipedia the generated url has a transition qualifer of KEYWORD, and + // TemplateURLModel generates a visit for 'wikipedia.org' with a transition + // type of KEYWORD_GENERATED. + KEYWORD = 9, + + // Corresponds to a visit generated for a keyword. See description of + // KEYWORD for more details. + KEYWORD_GENERATED = 10, + + // ADDING NEW CORE VALUE? Be sure to update the LAST_CORE and CORE_MASK + // values below. + LAST_CORE = KEYWORD_GENERATED, + CORE_MASK = 0xFF, + + // Qualifiers + // Any of the core values above can be augmented by one or more qualifiers. + // These qualifiers further define the transition. + + // The beginning of a navigation chain. + CHAIN_START = 0x10000000, + + // The last transition in a redirect chain. + CHAIN_END = 0x20000000, + + // Redirects caused by JavaScript or a meta refresh tag on the page. + CLIENT_REDIRECT = 0x40000000, + + // Redirects sent from the server by HTTP headers. It might be nice to + // break this out into 2 types in the future, permanent or temporary, if we + // can get that information from WebKit. + SERVER_REDIRECT = 0x80000000, + + // Used to test whether a transition involves a redirect. + IS_REDIRECT_MASK = 0xC0000000, + + // General mask defining the bits used for the qualifiers. + QUALIFIER_MASK = 0xFFFFFF00 + }; + + // The type used for the bitfield. + typedef unsigned int Type; + + static bool ValidType(int32 type) { + Type t = StripQualifier(static_cast(type)); + return (t >= 0 && t <= LAST_CORE); + } + + static Type FromInt(int32 type) { + if (!ValidType(type)) { + NOTREACHED() << "Invalid transition type " << type; + + // Return a safe default so we don't have corrupt data in release mode. + return LINK; + } + return static_cast(type); + } + + // Returns true if the given transition is a top-level frame transition, or + // false if the transition was for a subframe. + static bool IsMainFrame(Type type) { + int32 t = StripQualifier(type); + return (t != AUTO_SUBFRAME && t != MANUAL_SUBFRAME); + } + + // Returns whether a transition involves a redirection + static bool IsRedirect(Type type) { + return (type & IS_REDIRECT_MASK) != 0; + } + + // Simplifies the provided transition by removing any qualifier + static Type StripQualifier(Type type) { + return static_cast(type & ~QUALIFIER_MASK); + } + + // Return the qualifier + static int32 GetQualifier(Type type) { + return type & QUALIFIER_MASK; + } +}; + +#endif // CHROME_COMMON_PAGE_TRANSITION_TYPES_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/page_zoom.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/page_zoom.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/page_zoom.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/page_zoom.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,22 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_PAGE_ZOOM_H_ +#define CHROME_COMMON_PAGE_ZOOM_H_ + +// This enum is the parameter to various text/page zoom commands so we know +// what the specific zoom command is. +class PageZoom { + public: + enum Function { + SMALLER = -1, + STANDARD = 0, + LARGER = 1, + }; + + private: + PageZoom() {} // For scoping only. +}; + +#endif // CHROME_COMMON_PAGE_ZOOM_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/platform_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/platform_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/platform_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/platform_util.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,29 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_PLATFORM_UTIL_H_ +#define CHROME_COMMON_PLATFORM_UTIL_H_ + +#include "base/gfx/native_widget_types.h" +#include "base/string16.h" + +class FilePath; + +namespace platform_util { + +// Show the given file in a file manager. If possible, select the file. +void ShowItemInFolder(const FilePath& full_path); + +// Get the top level window for the native view. This can return NULL. +gfx::NativeWindow GetTopLevel(gfx::NativeView view); + +// Get the title of the window. +string16 GetWindowTitle(gfx::NativeWindow window); + +// Returns true if |window| is the foreground top level window. +bool IsWindowActive(gfx::NativeWindow window); + +} + +#endif // CHROME_COMMON_PLATFORM_UTIL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/platform_util_linux.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/platform_util_linux.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/platform_util_linux.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/platform_util_linux.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,44 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/platform_util.h" + +#include + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/process_util.h" +#include "base/string_util.h" + +namespace platform_util { + +// TODO(estade): It would be nice to be able to select the file in the file +// manager, but that probably requires extending xdg-open. For now just +// show the folder. +void ShowItemInFolder(const FilePath& full_path) { + FilePath dir = full_path.DirName(); + if (!file_util::DirectoryExists(dir)) + return; + + std::vector argv; + argv.push_back("xdg-open"); + argv.push_back(dir.value()); + base::file_handle_mapping_vector no_files; + base::LaunchApp(argv, no_files, false, NULL); +} + +gfx::NativeWindow GetTopLevel(gfx::NativeView view) { + return GTK_WINDOW(gtk_widget_get_toplevel(view)); +} + +string16 GetWindowTitle(gfx::NativeWindow window) { + const gchar* title = gtk_window_get_title(window); + return UTF8ToUTF16(title); +} + +bool IsWindowActive(gfx::NativeWindow window) { + return gtk_window_is_active(window); +} + +} // namespace platform_util diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/platform_util_mac.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/platform_util_mac.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/platform_util_mac.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/platform_util_mac.mm 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,56 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/platform_util.h" + +#import + +#ifndef CHROMIUM_MOZILLA_BUILD +#include "app/l10n_util.h" +#endif +#include "base/file_path.h" +#include "base/logging.h" +#include "base/sys_string_conversions.h" +#ifndef CHROMIUM_MOZILLA_BUILD +#include "chrome/browser/cocoa/tab_window_controller.h" +#include "grit/generated_resources.h" +#endif + +namespace platform_util { + +void ShowItemInFolder(const FilePath& full_path) { + NSString* path_string = base::SysUTF8ToNSString(full_path.value()); + [[NSWorkspace sharedWorkspace] selectFile:path_string + inFileViewerRootedAtPath:nil]; +} + +gfx::NativeWindow GetTopLevel(gfx::NativeView view) { + return [view window]; +} + +string16 GetWindowTitle(gfx::NativeWindow window) { +#ifdef CHROMIUM_MOZILLA_BUILD + std::string str("Untitled"); + return string16(str.begin(), str.end()); +#else + NSString* title = nil; + if ([[window delegate] isKindOfClass:[TabWindowController class]]) + title = [[window delegate] selectedTabTitle]; + else + title = [window title]; + // If we don't yet have a title, use "Untitled". + if (![title length]) + return WideToUTF16(l10n_util::GetString( + IDS_BROWSER_WINDOW_MAC_TAB_UNTITLED)); + + return base::SysNSStringToUTF16(title); +#endif +} + +bool IsWindowActive(gfx::NativeWindow window) { + NOTIMPLEMENTED(); + return false; +} + +} // namespace platform_util diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/platform_util_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/platform_util_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/platform_util_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/platform_util_win.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,100 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/platform_util.h" + +#include +#include +#include +#include +#include +#include + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/gfx/native_widget_types.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "chrome/common/win_util.h" + +namespace platform_util { + +void ShowItemInFolder(const FilePath& full_path) { + FilePath dir = full_path.DirName(); + // ParseDisplayName will fail if the directory is "C:", it must be "C:\\". + if (dir.value() == L"" || !file_util::EnsureEndsWithSeparator(&dir)) + return; + + typedef HRESULT (WINAPI *SHOpenFolderAndSelectItemsFuncPtr)( + PCIDLIST_ABSOLUTE pidl_Folder, + UINT cidl, + PCUITEMID_CHILD_ARRAY pidls, + DWORD flags); + + static SHOpenFolderAndSelectItemsFuncPtr open_folder_and_select_itemsPtr = + NULL; + static bool initialize_open_folder_proc = true; + if (initialize_open_folder_proc) { + initialize_open_folder_proc = false; + // The SHOpenFolderAndSelectItems API is exposed by shell32 version 6 + // and does not exist in Win2K. We attempt to retrieve this function export + // from shell32 and if it does not exist, we just invoke ShellExecute to + // open the folder thus losing the functionality to select the item in + // the process. + HMODULE shell32_base = GetModuleHandle(L"shell32.dll"); + if (!shell32_base) { + NOTREACHED(); + return; + } + open_folder_and_select_itemsPtr = + reinterpret_cast + (GetProcAddress(shell32_base, "SHOpenFolderAndSelectItems")); + } + if (!open_folder_and_select_itemsPtr) { + ShellExecute(NULL, _T("open"), dir.value().c_str(), NULL, NULL, SW_SHOW); + return; + } + + CComPtr desktop; + HRESULT hr = SHGetDesktopFolder(&desktop); + if (FAILED(hr)) + return; + + win_util::CoMemReleaser dir_item; + hr = desktop->ParseDisplayName(NULL, NULL, + const_cast(dir.value().c_str()), + NULL, &dir_item, NULL); + if (FAILED(hr)) + return; + + win_util::CoMemReleaser file_item; + hr = desktop->ParseDisplayName(NULL, NULL, + const_cast(full_path.value().c_str()), + NULL, &file_item, NULL); + if (FAILED(hr)) + return; + + const ITEMIDLIST* highlight[] = { + {file_item}, + }; + (*open_folder_and_select_itemsPtr)(dir_item, arraysize(highlight), + highlight, NULL); +} + +gfx::NativeWindow GetTopLevel(gfx::NativeView view) { + return GetAncestor(view, GA_ROOT); +} + +string16 GetWindowTitle(gfx::NativeWindow window_handle) { + std::wstring result; + int length = ::GetWindowTextLength(window_handle) + 1; + ::GetWindowText(window_handle, WriteInto(&result, length), length); + return WideToUTF16(result); +} + +bool IsWindowActive(gfx::NativeWindow window) { + return ::GetForegroundWindow() == window; +} + +} // namespace platform_util diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/plugin_messages.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/plugin_messages.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/plugin_messages.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/plugin_messages.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,454 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Defines messages between the browser and plugin process, as well as between +// the renderer and plugin process. +// +// See render_message* for information about the multi-pass include of headers. + +#ifndef CHROME_COMMON_PLUGIN_MESSAGES_H__ +#define CHROME_COMMON_PLUGIN_MESSAGES_H__ + +#include +#include + +#include "base/gfx/native_widget_types.h" +#include "base/gfx/rect.h" +#include "base/basictypes.h" +#include "chrome/common/ipc_message_utils.h" +#include "googleurl/src/gurl.h" +#include "third_party/npapi/bindings/npapi.h" +#include "webkit/glue/npruntime_util.h" + +// Name prefix of the event handle when a message box is displayed. +#define kMessageBoxEventPrefix L"message_box_active" + +// Structures for messages that have too many parameters to be put in a +// predefined IPC message. + +struct PluginMsg_Init_Params { + gfx::NativeViewId containing_window; + GURL url; + std::vector arg_names; + std::vector arg_values; +#if defined(OS_WIN) + HANDLE modal_dialog_event; +#endif + bool load_manually; +}; + +struct PluginHostMsg_URLRequest_Params { + std::string method; + bool is_javascript_url; + std::string target; + std::vector buffer; + bool is_file_data; + bool notify; + std::string url; + intptr_t notify_data; + bool popups_allowed; +}; + +struct PluginMsg_URLRequestReply_Params { + int resource_id; + std::string url; + bool notify_needed; + intptr_t notify_data; + intptr_t stream; +}; + +struct PluginMsg_DidReceiveResponseParams { + int id; + std::string mime_type; + std::string headers; + uint32 expected_length; + uint32 last_modified; + bool request_is_seekable; +}; + +struct NPIdentifier_Param { + NPIdentifier identifier; +}; + +enum NPVariant_ParamEnum { + NPVARIANT_PARAM_VOID, + NPVARIANT_PARAM_NULL, + NPVARIANT_PARAM_BOOL, + NPVARIANT_PARAM_INT, + NPVARIANT_PARAM_DOUBLE, + NPVARIANT_PARAM_STRING, + // Used when when the NPObject is running in the caller's process, so we + // create an NPObjectProxy in the other process. + NPVARIANT_PARAM_OBJECT_ROUTING_ID, + // Used when the NPObject we're sending is running in the callee's process + // (i.e. we have an NPObjectProxy for it). In that case we want the callee + // to just use the raw pointer. + NPVARIANT_PARAM_OBJECT_POINTER, +}; + +struct NPVariant_Param { + NPVariant_ParamEnum type; + bool bool_value; + int int_value; + double double_value; + std::string string_value; + int npobject_routing_id; + intptr_t npobject_pointer; +}; + + +namespace IPC { + +// Traits for PluginMsg_Init_Params structure to pack/unpack. +template <> +struct ParamTraits { + typedef PluginMsg_Init_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.containing_window); + WriteParam(m, p.url); + DCHECK(p.arg_names.size() == p.arg_values.size()); + WriteParam(m, p.arg_names); + WriteParam(m, p.arg_values); +#if defined(OS_WIN) + WriteParam(m, p.modal_dialog_event); +#endif + WriteParam(m, p.load_manually); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return ReadParam(m, iter, &p->containing_window) && + ReadParam(m, iter, &p->url) && + ReadParam(m, iter, &p->arg_names) && + ReadParam(m, iter, &p->arg_values) && +#if defined(OS_WIN) + ReadParam(m, iter, &p->modal_dialog_event) && +#endif + ReadParam(m, iter, &p->load_manually); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.containing_window, l); + l->append(L", "); + LogParam(p.url, l); + l->append(L", "); + LogParam(p.arg_names, l); + l->append(L", "); + LogParam(p.arg_values, l); + l->append(L", "); +#if defined(OS_WIN) + LogParam(p.modal_dialog_event, l); + l->append(L", "); +#endif + LogParam(p.load_manually, l); + l->append(L")"); + } +}; + +template <> +struct ParamTraits { + typedef PluginHostMsg_URLRequest_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.method); + WriteParam(m, p.is_javascript_url); + WriteParam(m, p.target); + WriteParam(m, p.buffer); + WriteParam(m, p.is_file_data); + WriteParam(m, p.notify); + WriteParam(m, p.url); + WriteParam(m, p.notify_data); + WriteParam(m, p.popups_allowed); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->method) && + ReadParam(m, iter, &p->is_javascript_url) && + ReadParam(m, iter, &p->target) && + ReadParam(m, iter, &p->buffer) && + ReadParam(m, iter, &p->is_file_data) && + ReadParam(m, iter, &p->notify) && + ReadParam(m, iter, &p->url) && + ReadParam(m, iter, &p->notify_data) && + ReadParam(m, iter, &p->popups_allowed); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.method, l); + l->append(L", "); + LogParam(p.is_javascript_url, l); + l->append(L", "); + LogParam(p.target, l); + l->append(L", "); + LogParam(p.buffer, l); + l->append(L", "); + LogParam(p.is_file_data, l); + l->append(L", "); + LogParam(p.notify, l); + l->append(L", "); + LogParam(p.url, l); + l->append(L", "); + LogParam(p.notify_data, l); + l->append(L", "); + LogParam(p.popups_allowed, l); + l->append(L")"); + } +}; + +template <> +struct ParamTraits { + typedef PluginMsg_URLRequestReply_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.resource_id); + WriteParam(m, p.url); + WriteParam(m, p.notify_needed); + WriteParam(m, p.notify_data); + WriteParam(m, p.stream); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->resource_id) && + ReadParam(m, iter, &p->url) && + ReadParam(m, iter, &p->notify_needed) && + ReadParam(m, iter, &p->notify_data) && + ReadParam(m, iter, &p->stream); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.resource_id, l); + l->append(L", "); + LogParam(p.url, l); + l->append(L", "); + LogParam(p.notify_needed, l); + l->append(L", "); + LogParam(p.notify_data, l); + l->append(L", "); + LogParam(p.stream, l); + l->append(L")"); + } +}; + +template <> +struct ParamTraits { + typedef PluginMsg_DidReceiveResponseParams param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.id); + WriteParam(m, p.mime_type); + WriteParam(m, p.headers); + WriteParam(m, p.expected_length); + WriteParam(m, p.last_modified); + WriteParam(m, p.request_is_seekable); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ReadParam(m, iter, &r->id) && + ReadParam(m, iter, &r->mime_type) && + ReadParam(m, iter, &r->headers) && + ReadParam(m, iter, &r->expected_length) && + ReadParam(m, iter, &r->last_modified) && + ReadParam(m, iter, &r->request_is_seekable); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.id, l); + l->append(L", "); + LogParam(p.mime_type, l); + l->append(L", "); + LogParam(p.headers, l); + l->append(L", "); + LogParam(p.expected_length, l); + l->append(L", "); + LogParam(p.last_modified, l); + l->append(L", "); + LogParam(p.request_is_seekable, l); + l->append(L")"); + } +}; + +template <> +struct ParamTraits { + typedef NPEvent param_type; + static void Write(Message* m, const param_type& p) { + m->WriteData(reinterpret_cast(&p), sizeof(NPEvent)); + } + static bool Read(const Message* m, void** iter, param_type* r) { + const char *data; + int data_size = 0; + bool result = m->ReadData(iter, &data, &data_size); + if (!result || data_size != sizeof(NPEvent)) { + NOTREACHED(); + return false; + } + + memcpy(r, data, sizeof(NPEvent)); + return true; + } + static void Log(const param_type& p, std::wstring* l) { +#if defined(OS_WIN) + std::wstring event, wparam, lparam; + lparam = StringPrintf(L"(%d, %d)", LOWORD(p.lParam), HIWORD(p.lParam)); + switch(p.event) { + case WM_KEYDOWN: + event = L"WM_KEYDOWN"; + wparam = StringPrintf(L"%d", p.wParam); + lparam = StringPrintf(L"%d", p.lParam); + break; + case WM_KEYUP: + event = L"WM_KEYDOWN"; + wparam = StringPrintf(L"%d", p.wParam); + lparam = StringPrintf(L"%x", p.lParam); + break; + case WM_MOUSEMOVE: + event = L"WM_MOUSEMOVE"; + if (p.wParam & MK_LBUTTON) { + wparam = L"MK_LBUTTON"; + } else if (p.wParam & MK_MBUTTON) { + wparam = L"MK_MBUTTON"; + } else if (p.wParam & MK_RBUTTON) { + wparam = L"MK_RBUTTON"; + } + break; + case WM_LBUTTONDOWN: + event = L"WM_LBUTTONDOWN"; + break; + case WM_MBUTTONDOWN: + event = L"WM_MBUTTONDOWN"; + break; + case WM_RBUTTONDOWN: + event = L"WM_RBUTTONDOWN"; + break; + case WM_LBUTTONUP: + event = L"WM_LBUTTONUP"; + break; + case WM_MBUTTONUP: + event = L"WM_MBUTTONUP"; + break; + case WM_RBUTTONUP: + event = L"WM_RBUTTONUP"; + break; + } + + if (p.wParam & MK_CONTROL) { + if (!wparam.empty()) + wparam += L" "; + wparam += L"MK_CONTROL"; + } + + if (p.wParam & MK_SHIFT) { + if (!wparam.empty()) + wparam += L" "; + wparam += L"MK_SHIFT"; + } + + l->append(L"("); + LogParam(event, l); + l->append(L", "); + LogParam(wparam, l); + l->append(L", "); + LogParam(lparam, l); + l->append(L")"); +#else + l->append(L""); +#endif + } +}; + +template <> +struct ParamTraits { + typedef NPIdentifier_Param param_type; + static void Write(Message* m, const param_type& p) { + webkit_glue::SerializeNPIdentifier(p.identifier, m); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return webkit_glue::DeserializeNPIdentifier(*m, iter, &r->identifier); + } + static void Log(const param_type& p, std::wstring* l) { + if (NPN_IdentifierIsString(p.identifier)) { + NPUTF8* str = NPN_UTF8FromIdentifier(p.identifier); + l->append(UTF8ToWide(str)); + NPN_MemFree(str); + } else { + l->append(IntToWString(NPN_IntFromIdentifier(p.identifier))); + } + } +}; + +template <> +struct ParamTraits { + typedef NPVariant_Param param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast(p.type)); + if (p.type == NPVARIANT_PARAM_BOOL) { + WriteParam(m, p.bool_value); + } else if (p.type == NPVARIANT_PARAM_INT) { + WriteParam(m, p.int_value); + } else if (p.type == NPVARIANT_PARAM_DOUBLE) { + WriteParam(m, p.double_value); + } else if (p.type == NPVARIANT_PARAM_STRING) { + WriteParam(m, p.string_value); + } else if (p.type == NPVARIANT_PARAM_OBJECT_ROUTING_ID) { + // This is the routing id used to connect NPObjectProxy in the other + // process with NPObjectStub in this process. + WriteParam(m, p.npobject_routing_id); + // The actual NPObject pointer, in case it's passed back to this process. + WriteParam(m, p.npobject_pointer); + } else if (p.type == NPVARIANT_PARAM_OBJECT_POINTER) { + // The NPObject resides in the other process, so just send its pointer. + WriteParam(m, p.npobject_pointer); + } else { + DCHECK(p.type == NPVARIANT_PARAM_VOID || p.type == NPVARIANT_PARAM_NULL); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + int type; + if (!ReadParam(m, iter, &type)) + return false; + + bool result = false; + r->type = static_cast(type); + if (r->type == NPVARIANT_PARAM_BOOL) { + result = ReadParam(m, iter, &r->bool_value); + } else if (r->type == NPVARIANT_PARAM_INT) { + result = ReadParam(m, iter, &r->int_value); + } else if (r->type == NPVARIANT_PARAM_DOUBLE) { + result = ReadParam(m, iter, &r->double_value); + } else if (r->type == NPVARIANT_PARAM_STRING) { + result = ReadParam(m, iter, &r->string_value); + } else if (r->type == NPVARIANT_PARAM_OBJECT_ROUTING_ID) { + result = + ReadParam(m, iter, &r->npobject_routing_id) && + ReadParam(m, iter, &r->npobject_pointer); + } else if (r->type == NPVARIANT_PARAM_OBJECT_POINTER) { + result = ReadParam(m, iter, &r->npobject_pointer); + } else if ((r->type == NPVARIANT_PARAM_VOID) || + (r->type == NPVARIANT_PARAM_NULL)) { + result = true; + } else { + NOTREACHED(); + } + + return result; + } + static void Log(const param_type& p, std::wstring* l) { + if (p.type == NPVARIANT_PARAM_BOOL) { + LogParam(p.bool_value, l); + } else if (p.type == NPVARIANT_PARAM_INT) { + LogParam(p.int_value, l); + } else if (p.type == NPVARIANT_PARAM_DOUBLE) { + LogParam(p.double_value, l); + } else if (p.type == NPVARIANT_PARAM_STRING) { + LogParam(p.string_value, l); + } else if (p.type == NPVARIANT_PARAM_OBJECT_ROUTING_ID) { + LogParam(p.npobject_routing_id, l); + LogParam(p.npobject_pointer, l); + } else if (p.type == NPVARIANT_PARAM_OBJECT_POINTER) { + LogParam(p.npobject_pointer, l); + } + } +}; + +} // namespace IPC + + +#define MESSAGES_INTERNAL_FILE "chrome/common/plugin_messages_internal.h" +#include "chrome/common/ipc_message_macros.h" + +#endif // CHROME_COMMON_PLUGIN_MESSAGES_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/plugin_messages_internal.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/plugin_messages_internal.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/plugin_messages_internal.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/plugin_messages_internal.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,330 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/shared_memory.h" +#include "chrome/common/ipc_message_macros.h" + +// PluginProcess messages +// These are messages sent from the browser to the plugin process. +IPC_BEGIN_MESSAGES(PluginProcess) + // Tells the plugin process to create a new channel for communication with a + // renderer. The channel name is returned in a + // PluginProcessHostMsg_ChannelCreated message. + IPC_MESSAGE_CONTROL1(PluginProcessMsg_CreateChannel, + bool /* off_the_record */) + + // Allows a chrome plugin loaded in the browser process to send arbitrary + // data to an instance of the same plugin loaded in a plugin process. + IPC_MESSAGE_CONTROL1(PluginProcessMsg_PluginMessage, + std::vector /* opaque data */) + + // The following messages are used by all child processes, even though they + // are listed under PluginProcess. It seems overkill to define ChildProcess. + // Tells the child process it should stop. + IPC_MESSAGE_CONTROL0(PluginProcessMsg_AskBeforeShutdown) + + // Sent in response to PluginProcessHostMsg_ShutdownRequest to tell the child + // process that it's safe to shutdown. + IPC_MESSAGE_CONTROL0(PluginProcessMsg_Shutdown) + +IPC_END_MESSAGES(PluginProcess) + + +//----------------------------------------------------------------------------- +// PluginProcessHost messages +// These are messages sent from the plugin process to the browser process. +IPC_BEGIN_MESSAGES(PluginProcessHost) + // Response to a PluginProcessMsg_CreateChannel message. + IPC_MESSAGE_CONTROL1(PluginProcessHostMsg_ChannelCreated, + std::wstring /* channel_name */) + + IPC_SYNC_MESSAGE_CONTROL0_1(PluginProcessHostMsg_GetPluginFinderUrl, + std::string /* plugin finder URL */) + + IPC_MESSAGE_CONTROL0(PluginProcessHostMsg_ShutdownRequest) + + // Allows a chrome plugin loaded in a plugin process to send arbitrary + // data to an instance of the same plugin loaded in the browser process. + IPC_MESSAGE_CONTROL1(PluginProcessHostMsg_PluginMessage, + std::vector /* opaque data */) + + // Allows a chrome plugin loaded in a plugin process to send arbitrary + // data to an instance of the same plugin loaded in the browser process. + IPC_SYNC_MESSAGE_CONTROL1_1(PluginProcessHostMsg_PluginSyncMessage, + std::vector /* opaque data */, + std::vector /* opaque data response */) + + // Used to get cookies for the given URL. The request_context is a + // CPBrowsingContext, but is passed as int32 to avoid compilation errors. + IPC_SYNC_MESSAGE_CONTROL2_1(PluginProcessHostMsg_GetCookies, + int32 /* request_context */, + GURL /* url */, + std::string /* cookies */) + + // Get the list of proxies to use for |url|, as a semicolon delimited list + // of " :" | "DIRECT". See also ViewHostMsg_ResolveProxy + // which does the same thing. + IPC_SYNC_MESSAGE_CONTROL1_2(PluginProcessHostMsg_ResolveProxy, + GURL /* url */, + int /* network error */, + std::string /* proxy list */) + +#if defined(OS_WIN) + // Creates a child window of the given parent window on the UI thread. + IPC_SYNC_MESSAGE_CONTROL1_1(PluginProcessHostMsg_CreateWindow, + HWND /* parent */, + HWND /* child */) + + // Destroys the given window's parent on the UI thread. + IPC_MESSAGE_CONTROL2(PluginProcessHostMsg_PluginWindowDestroyed, + HWND /* window */, + HWND /* parent */) + + IPC_MESSAGE_ROUTED3(PluginProcessHostMsg_DownloadUrl, + std::string /* URL */, + int /* process id */, + HWND /* caller window */) +#endif + +IPC_END_MESSAGES(PluginProcessHost) + + +//----------------------------------------------------------------------------- +// Plugin messages +// These are messages sent from the renderer process to the plugin process. +IPC_BEGIN_MESSAGES(Plugin) + // Tells the plugin process to create a new plugin instance with the given + // id. A corresponding WebPluginDelegateStub is created which hosts the + // WebPluginDelegateImpl. + IPC_SYNC_MESSAGE_CONTROL1_1(PluginMsg_CreateInstance, + std::string /* mime_type */, + int /* instance_id */) + + // The WebPluginDelegateProxy sends this to the WebPluginDelegateStub in its + // destructor, so that the stub deletes the actual WebPluginDelegateImpl + // object that it's hosting. + IPC_SYNC_MESSAGE_CONTROL1_0(PluginMsg_DestroyInstance, + int /* instance_id */) + + IPC_SYNC_MESSAGE_CONTROL0_1(PluginMsg_GenerateRouteID, + int /* id */) + + // The messages below all map to WebPluginDelegate methods. + IPC_SYNC_MESSAGE_ROUTED1_1(PluginMsg_Init, + PluginMsg_Init_Params, + bool /* result */) + + // Used to synchronously request a paint for windowless plugins. + IPC_SYNC_MESSAGE_ROUTED1_0(PluginMsg_Paint, + gfx::Rect /* damaged_rect */) + + // Sent by the renderer after it paints from its backing store so that the + // plugin knows it can send more invalidates. + IPC_MESSAGE_ROUTED0(PluginMsg_DidPaint) + + IPC_SYNC_MESSAGE_ROUTED0_2(PluginMsg_Print, + base::SharedMemoryHandle /* shared_memory*/, + size_t /* size */) + + IPC_SYNC_MESSAGE_ROUTED0_2(PluginMsg_GetPluginScriptableObject, + int /* route_id */, + intptr_t /* npobject_ptr */) + + IPC_SYNC_MESSAGE_ROUTED1_0(PluginMsg_DidFinishLoadWithReason, + int /* reason */) + + // Updates the plugin location. For windowless plugins, windowless_buffer + // contains a buffer that the plugin draws into. background_buffer is used + // for transparent windowless plugins, and holds the background of the plugin + // rectangle. + IPC_MESSAGE_ROUTED4(PluginMsg_UpdateGeometry, + gfx::Rect /* window_rect */, + gfx::Rect /* clip_rect */, + TransportDIB::Id /* windowless_buffer */, + TransportDIB::Id /* background_buffer */) + + IPC_SYNC_MESSAGE_ROUTED0_0(PluginMsg_SetFocus) + + IPC_SYNC_MESSAGE_ROUTED1_2(PluginMsg_HandleEvent, + NPEvent /* event */, + bool /* handled */, + WebCursor /* cursor type*/) + + IPC_SYNC_MESSAGE_ROUTED2_0(PluginMsg_WillSendRequest, + int /* id */, + GURL /* url */) + + IPC_SYNC_MESSAGE_ROUTED1_1(PluginMsg_DidReceiveResponse, + PluginMsg_DidReceiveResponseParams, + bool /* cancel */) + + IPC_SYNC_MESSAGE_ROUTED3_0(PluginMsg_DidReceiveData, + int /* id */, + std::vector /* buffer */, + int /* data_offset */) + + IPC_SYNC_MESSAGE_ROUTED1_0(PluginMsg_DidFinishLoading, + int /* id */) + + IPC_SYNC_MESSAGE_ROUTED1_0(PluginMsg_DidFail, + int /* id */) + + IPC_MESSAGE_ROUTED5(PluginMsg_SendJavaScriptStream, + std::string /* url */, + std::wstring /* result */, + bool /* success */, + bool /* notify required */, + intptr_t /* notify data */) + + IPC_MESSAGE_ROUTED2(PluginMsg_DidReceiveManualResponse, + std::string /* url */, + PluginMsg_DidReceiveResponseParams) + + IPC_MESSAGE_ROUTED1(PluginMsg_DidReceiveManualData, + std::vector /* buffer */) + + IPC_MESSAGE_ROUTED0(PluginMsg_DidFinishManualLoading) + + IPC_MESSAGE_ROUTED0(PluginMsg_DidManualLoadFail) + + IPC_MESSAGE_ROUTED0(PluginMsg_InstallMissingPlugin) + + IPC_SYNC_MESSAGE_ROUTED1_0(PluginMsg_HandleURLRequestReply, + PluginMsg_URLRequestReply_Params) + + IPC_SYNC_MESSAGE_ROUTED3_0(PluginMsg_URLRequestRouted, + std::string /* url */, + bool /* notify_needed */, + intptr_t /* notify data */) +IPC_END_MESSAGES(Plugin) + + +//----------------------------------------------------------------------------- +// PluginHost messages +// These are messages sent from the plugin process to the renderer process. +// They all map to the corresponding WebPlugin methods. +IPC_BEGIN_MESSAGES(PluginHost) + // Sends the plugin window information to the renderer. + // The window parameter is a handle to the window if the plugin is a windowed + // plugin. It is NULL for windowless plugins. + IPC_SYNC_MESSAGE_ROUTED1_0(PluginHostMsg_SetWindow, + gfx::NativeViewId /* window */) + +#if defined(OS_WIN) + // The modal_loop_pump_messages_event parameter is an event handle which is + // passed in for windowless plugins and is used to indicate if messages + // are to be pumped in sync calls to the plugin process. Currently used + // in HandleEvent calls. + IPC_SYNC_MESSAGE_ROUTED1_0(PluginHostMsg_SetWindowlessPumpEvent, + HANDLE /* modal_loop_pump_messages_event */) +#endif + + IPC_MESSAGE_ROUTED1(PluginHostMsg_URLRequest, + PluginHostMsg_URLRequest_Params) + + IPC_SYNC_MESSAGE_ROUTED1_0(PluginHostMsg_CancelResource, + int /* id */) + + IPC_MESSAGE_ROUTED1(PluginHostMsg_InvalidateRect, + gfx::Rect /* rect */) + + IPC_SYNC_MESSAGE_ROUTED1_2(PluginHostMsg_GetWindowScriptNPObject, + int /* route id */, + bool /* success */, + intptr_t /* npobject_ptr */) + + IPC_SYNC_MESSAGE_ROUTED1_2(PluginHostMsg_GetPluginElement, + int /* route id */, + bool /* success */, + intptr_t /* npobject_ptr */) + + IPC_MESSAGE_ROUTED3(PluginHostMsg_SetCookie, + GURL /* url */, + GURL /* policy_url */, + std::string /* cookie */) + + IPC_SYNC_MESSAGE_ROUTED2_1(PluginHostMsg_GetCookies, + GURL /* url */, + GURL /* policy_url */, + std::string /* cookies */) + + // Asks the browser to show a modal HTML dialog. The dialog is passed the + // given arguments as a JSON string, and returns its result as a JSON string + // through json_retval. + IPC_SYNC_MESSAGE_ROUTED4_1(PluginHostMsg_ShowModalHTMLDialog, + GURL /* url */, + int /* width */, + int /* height */, + std::string /* json_arguments */, + std::string /* json_retval */) + + IPC_MESSAGE_ROUTED1(PluginHostMsg_MissingPluginStatus, + int /* status */) + + IPC_SYNC_MESSAGE_ROUTED0_1(PluginHostMsg_GetCPBrowsingContext, + uint32 /* context */) + + IPC_MESSAGE_ROUTED0(PluginHostMsg_CancelDocumentLoad) + + IPC_MESSAGE_ROUTED5(PluginHostMsg_InitiateHTTPRangeRequest, + std::string /* url */, + std::string /* range_info */, + intptr_t /* existing_stream */, + bool /* notify_needed */, + intptr_t /* notify_data */) + +IPC_END_MESSAGES(PluginHost) + +//----------------------------------------------------------------------------- +// NPObject messages +// These are messages used to marshall NPObjects. They are sent both from the +// plugin to the renderer and from the renderer to the plugin. +IPC_BEGIN_MESSAGES(NPObject) + IPC_SYNC_MESSAGE_ROUTED0_0(NPObjectMsg_Release) + + IPC_SYNC_MESSAGE_ROUTED1_1(NPObjectMsg_HasMethod, + NPIdentifier_Param /* name */, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED3_2(NPObjectMsg_Invoke, + bool /* is_default */, + NPIdentifier_Param /* method */, + std::vector /* args */, + NPVariant_Param /* result_param */, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED1_1(NPObjectMsg_HasProperty, + NPIdentifier_Param /* name */, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED1_2(NPObjectMsg_GetProperty, + NPIdentifier_Param /* name */, + NPVariant_Param /* property */, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED2_1(NPObjectMsg_SetProperty, + NPIdentifier_Param /* name */, + NPVariant_Param /* property */, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED1_1(NPObjectMsg_RemoveProperty, + NPIdentifier_Param /* name */, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED0_0(NPObjectMsg_Invalidate) + + IPC_SYNC_MESSAGE_ROUTED0_2(NPObjectMsg_Enumeration, + std::vector /* value */, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED2_2(NPObjectMsg_Evaluate, + std::string /* script */, + bool /* popups_allowed */, + NPVariant_Param /* result_param */, + bool /* result */) + + IPC_SYNC_MESSAGE_ROUTED1_0(NPObjectMsg_SetException, + std::string /* message */) + +IPC_END_MESSAGES(NPObject) diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_member.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_member.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_member.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_member.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,87 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/pref_member.h" + +#include "base/logging.h" +#include "chrome/common/notification_type.h" +#include "chrome/common/pref_service.h" + +namespace subtle { + +PrefMemberBase::PrefMemberBase() + : observer_(NULL), + prefs_(NULL), + is_synced_(false), + setting_value_(false) { +} + +PrefMemberBase::~PrefMemberBase() { + if (!pref_name_.empty()) + prefs_->RemovePrefObserver(pref_name_.c_str(), this); +} + + +void PrefMemberBase::Init(const wchar_t* pref_name, PrefService* prefs, + NotificationObserver* observer) { + DCHECK(pref_name); + DCHECK(prefs); + DCHECK(pref_name_.empty()); // Check that Init is only called once. + observer_ = observer; + prefs_ = prefs; + pref_name_ = pref_name; + DCHECK(!pref_name_.empty()); + + // Add ourself as a pref observer so we can keep our local value in sync. + prefs_->AddPrefObserver(pref_name, this); +} + +void PrefMemberBase::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(!pref_name_.empty()); + DCHECK(NotificationType::PREF_CHANGED == type); + UpdateValueFromPref(); + is_synced_ = true; + if (!setting_value_ && observer_) + observer_->Observe(type, source, details); +} + +void PrefMemberBase::VerifyValuePrefName() { + DCHECK(!pref_name_.empty()); +} + +} // namespace subtle + +void BooleanPrefMember::UpdateValueFromPref() { + value_ = prefs()->GetBoolean(pref_name().c_str()); +} + +void BooleanPrefMember::UpdatePref(const bool& value) { + prefs()->SetBoolean(pref_name().c_str(), value); +} + +void IntegerPrefMember::UpdateValueFromPref() { + value_ = prefs()->GetInteger(pref_name().c_str()); +} + +void IntegerPrefMember::UpdatePref(const int& value) { + prefs()->SetInteger(pref_name().c_str(), value); +} + +void RealPrefMember::UpdateValueFromPref() { + value_ = prefs()->GetReal(pref_name().c_str()); +} + +void RealPrefMember::UpdatePref(const double& value) { + prefs()->SetReal(pref_name().c_str(), value); +} + +void StringPrefMember::UpdateValueFromPref() { + value_ = prefs()->GetString(pref_name().c_str()); +} + +void StringPrefMember::UpdatePref(const std::wstring& value) { + prefs()->SetString(pref_name().c_str(), value); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_member.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_member.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_member.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_member.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,176 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// A helper class that stays in sync with a preference (bool, int, real, or +// string). For example: +// +// class MyClass { +// public: +// MyClass(PrefService* prefs) { +// my_string_.Init(prefs::kHomePage, prefs, NULL /* no observer */); +// } +// private: +// StringPrefMember my_string_; +// }; +// +// my_string_ should stay in sync with the prefs::kHomePage pref and will +// update if either the pref changes or if my_string_.SetValue is called. +// +// An optional observer can be passed into the Init method which can be used to +// notify MyClass of changes. Note that if you use SetValue(), the observer +// will not be notified. + +#ifndef CHROME_COMMON_PREF_MEMBER_H_ +#define CHROME_COMMON_PREF_MEMBER_H_ + +#include + +#include "base/basictypes.h" +#include "chrome/common/notification_observer.h" + +class PrefService; + +namespace subtle { + +class PrefMemberBase : public NotificationObserver { + protected: + PrefMemberBase(); + virtual ~PrefMemberBase(); + + // See PrefMember<> for description. + void Init(const wchar_t* pref_name, PrefService* prefs, + NotificationObserver* observer); + + // NotificationObserver + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + void VerifyValuePrefName(); + + // This methods is used to do the actual sync with pref of the specified type. + virtual void UpdateValueFromPref() = 0; + + const std::wstring& pref_name() const { return pref_name_; } + PrefService* prefs() { return prefs_; } + + // Ordered the members to compact the class instance. + private: + std::wstring pref_name_; + NotificationObserver* observer_; + PrefService* prefs_; + + protected: + bool is_synced_; + bool setting_value_; +}; + +} // namespace subtle + + +template +class PrefMember : public subtle::PrefMemberBase { + public: + // Defer initialization to an Init method so it's easy to make this class be + // a member variable. + PrefMember() { } + virtual ~PrefMember() { } + + // Do the actual initialization of the class. |observer| may be null if you + // don't want any notifications of changes. + void Init(const wchar_t* pref_name, PrefService* prefs, + NotificationObserver* observer) { + subtle::PrefMemberBase::Init(pref_name, prefs, observer); + } + + // Retrieve the value of the member variable. + ValueType GetValue() { + VerifyValuePrefName(); + // We lazily fetch the value from the pref service the first time GetValue + // is called. + if (!is_synced_) { + UpdateValueFromPref(); + is_synced_ = true; + } + return value_; + } + + // Provided as a convenience. + ValueType operator*() { + return GetValue(); + } + + // Set the value of the member variable. + void SetValue(const ValueType& value) { + VerifyValuePrefName(); + setting_value_ = true; + UpdatePref(value); + setting_value_ = false; + } + + protected: + // This methods is used to do the actual sync with pref of the specified type. + virtual void UpdatePref(const ValueType& value) = 0; + + // We cache the value of the pref so we don't have to keep walking the pref + // tree. + ValueType value_; +}; + +/////////////////////////////////////////////////////////////////////////////// +// Implementations of Boolean, Integer, Real, and String PrefMember below. + +class BooleanPrefMember : public PrefMember { + public: + BooleanPrefMember() : PrefMember() { } + virtual ~BooleanPrefMember() { } + + protected: + virtual void UpdateValueFromPref(); + virtual void UpdatePref(const bool& value); + + private: + DISALLOW_COPY_AND_ASSIGN(BooleanPrefMember); +}; + +class IntegerPrefMember : public PrefMember { + public: + IntegerPrefMember() : PrefMember() { } + virtual ~IntegerPrefMember() { } + + protected: + virtual void UpdateValueFromPref(); + virtual void UpdatePref(const int& value); + + private: + DISALLOW_COPY_AND_ASSIGN(IntegerPrefMember); +}; + +class RealPrefMember : public PrefMember { + public: + RealPrefMember() : PrefMember() { } + virtual ~RealPrefMember() { } + + protected: + virtual void UpdateValueFromPref(); + virtual void UpdatePref(const double& value); + + private: + DISALLOW_COPY_AND_ASSIGN(RealPrefMember); +}; + +class StringPrefMember : public PrefMember { + public: + StringPrefMember() : PrefMember() { } + virtual ~StringPrefMember() { } + + protected: + virtual void UpdateValueFromPref(); + virtual void UpdatePref(const std::wstring& value); + + private: + DISALLOW_COPY_AND_ASSIGN(StringPrefMember); +}; + +#endif // CHROME_COMMON_PREF_MEMBER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_member_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_member_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_member_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_member_unittest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,192 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_path.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/pref_member.h" +#include "chrome/common/pref_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +static const wchar_t kBoolPref[] = L"bool"; +static const wchar_t kIntPref[] = L"int"; +static const wchar_t kRealPref[] = L"real"; +static const wchar_t kStringPref[] = L"string"; + +void RegisterTestPrefs(PrefService* prefs) { + prefs->RegisterBooleanPref(kBoolPref, false); + prefs->RegisterIntegerPref(kIntPref, 0); + prefs->RegisterRealPref(kRealPref, 0.0); + prefs->RegisterStringPref(kStringPref, L"default"); +} + +class PrefMemberTestClass : public NotificationObserver { + public: + PrefMemberTestClass(PrefService* prefs) : observe_cnt_(0), prefs_(prefs) { + str_.Init(kStringPref, prefs, this); + } + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(NotificationType::PREF_CHANGED == type); + PrefService* prefs_in = Source(source).ptr(); + EXPECT_EQ(prefs_in, prefs_); + std::wstring* pref_name_in = Details(details).ptr(); + EXPECT_EQ(*pref_name_in, kStringPref); + EXPECT_EQ(str_.GetValue(), prefs_->GetString(kStringPref)); + ++observe_cnt_; + } + + StringPrefMember str_; + int observe_cnt_; + + private: + PrefService* prefs_; +}; + +} // anonymous namespace + +TEST(PrefMemberTest, BasicGetAndSet) { + PrefService prefs(FilePath(), NULL); + RegisterTestPrefs(&prefs); + + // Test bool + BooleanPrefMember boolean; + boolean.Init(kBoolPref, &prefs, NULL); + + // Check the defaults + EXPECT_FALSE(prefs.GetBoolean(kBoolPref)); + EXPECT_FALSE(boolean.GetValue()); + EXPECT_FALSE(*boolean); + + // Try changing through the member variable. + boolean.SetValue(true); + EXPECT_TRUE(boolean.GetValue()); + EXPECT_TRUE(prefs.GetBoolean(kBoolPref)); + EXPECT_TRUE(*boolean); + + // Try changing back through the pref. + prefs.SetBoolean(kBoolPref, false); + EXPECT_FALSE(prefs.GetBoolean(kBoolPref)); + EXPECT_FALSE(boolean.GetValue()); + EXPECT_FALSE(*boolean); + + // Test int + IntegerPrefMember integer; + integer.Init(kIntPref, &prefs, NULL); + + // Check the defaults + EXPECT_EQ(0, prefs.GetInteger(kIntPref)); + EXPECT_EQ(0, integer.GetValue()); + EXPECT_EQ(0, *integer); + + // Try changing through the member variable. + integer.SetValue(5); + EXPECT_EQ(5, integer.GetValue()); + EXPECT_EQ(5, prefs.GetInteger(kIntPref)); + EXPECT_EQ(5, *integer); + + // Try changing back through the pref. + prefs.SetInteger(kIntPref, 2); + EXPECT_EQ(2, prefs.GetInteger(kIntPref)); + EXPECT_EQ(2, integer.GetValue()); + EXPECT_EQ(2, *integer); + + // Test real (double) + RealPrefMember real; + real.Init(kRealPref, &prefs, NULL); + + // Check the defaults + EXPECT_EQ(0.0, prefs.GetReal(kRealPref)); + EXPECT_EQ(0.0, real.GetValue()); + EXPECT_EQ(0.0, *real); + + // Try changing through the member variable. + real.SetValue(1.0); + EXPECT_EQ(1.0, real.GetValue()); + EXPECT_EQ(1.0, prefs.GetReal(kRealPref)); + EXPECT_EQ(1.0, *real); + + // Try changing back through the pref. + prefs.SetReal(kRealPref, 3.0); + EXPECT_EQ(3.0, prefs.GetReal(kRealPref)); + EXPECT_EQ(3.0, real.GetValue()); + EXPECT_EQ(3.0, *real); + + // Test string + StringPrefMember string; + string.Init(kStringPref, &prefs, NULL); + + // Check the defaults + EXPECT_EQ(L"default", prefs.GetString(kStringPref)); + EXPECT_EQ(L"default", string.GetValue()); + EXPECT_EQ(L"default", *string); + + // Try changing through the member variable. + string.SetValue(L"foo"); + EXPECT_EQ(L"foo", string.GetValue()); + EXPECT_EQ(L"foo", prefs.GetString(kStringPref)); + EXPECT_EQ(L"foo", *string); + + // Try changing back through the pref. + prefs.SetString(kStringPref, L"bar"); + EXPECT_EQ(L"bar", prefs.GetString(kStringPref)); + EXPECT_EQ(L"bar", string.GetValue()); + EXPECT_EQ(L"bar", *string); +} + +TEST(PrefMemberTest, TwoPrefs) { + // Make sure two RealPrefMembers stay in sync. + PrefService prefs(FilePath(), NULL); + RegisterTestPrefs(&prefs); + + RealPrefMember pref1; + pref1.Init(kRealPref, &prefs, NULL); + RealPrefMember pref2; + pref2.Init(kRealPref, &prefs, NULL); + + pref1.SetValue(2.3); + EXPECT_EQ(2.3, *pref2); + + pref2.SetValue(3.5); + EXPECT_EQ(3.5, *pref1); + + prefs.SetReal(kRealPref, 4.2); + EXPECT_EQ(4.2, *pref1); + EXPECT_EQ(4.2, *pref2); +} + +TEST(PrefMemberTest, Observer) { + PrefService prefs(FilePath(), NULL); + RegisterTestPrefs(&prefs); + + PrefMemberTestClass test_obj(&prefs); + EXPECT_EQ(L"default", *test_obj.str_); + + // Calling SetValue should not fire the observer. + test_obj.str_.SetValue(L"hello"); + EXPECT_EQ(0, test_obj.observe_cnt_); + EXPECT_EQ(L"hello", prefs.GetString(kStringPref)); + + // Changing the pref does fire the observer. + prefs.SetString(kStringPref, L"world"); + EXPECT_EQ(1, test_obj.observe_cnt_); + EXPECT_EQ(L"world", *(test_obj.str_)); + + // Not changing the value should not fire the observer. + prefs.SetString(kStringPref, L"world"); + EXPECT_EQ(1, test_obj.observe_cnt_); + EXPECT_EQ(L"world", *(test_obj.str_)); + + prefs.SetString(kStringPref, L"hello"); + EXPECT_EQ(2, test_obj.observe_cnt_); + EXPECT_EQ(L"hello", prefs.GetString(kStringPref)); +} + +TEST(PrefMemberTest, NoInit) { + // Make sure not calling Init on a PrefMember doesn't cause problems. + IntegerPrefMember pref; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_names.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_names.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_names.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_names.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,507 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/pref_names.h" + +namespace prefs { + +// *************** PROFILE PREFS *************** +// These are attached to the user profile + +// A boolean specifying whether the New Tab page is the home page or not. +const wchar_t kHomePageIsNewTabPage[] = L"homepage_is_newtabpage"; + +// This is the URL of the page to load when opening new tabs. +const wchar_t kHomePage[] = L"homepage"; + +// This is the human-readable name of the current profile. +const wchar_t kProfileName[] = L"profile.name"; + +// This is the abbreviated human-readable name of the current profile +const wchar_t kProfileNickname[] = L"profile.nickname"; + +// This is a short, preferably human-readable ID for the current profile. +// Its value should be unique within the current user data directory. +const wchar_t kProfileID[] = L"profile.id"; + +// A boolean value that when true stipulates that the history of the active +// tab should be shown in its entirety at the start of the list, rather than +// being mingled based on view time. +const wchar_t kRecentlyViewedModelBiasActiveTabHistory[] = + L"recently_viewed_model.bias_active_tab_history"; + +// This is a value that defines the selection mode used by the recently +// viewed pages model to determine how to load the selected item. values are: +// 0 - the tab that contains or contained the item should be activated, and +// the item navigated to within that tab. +// 1 - if the tab that contains or contained the item is active, the item +// should be navigated to, otherwise a new tab is opened with the +// selected item only. +// +// ... future +const wchar_t kRecentlyViewedModelSelectionMode[] = + L"recently_viewed_model.selection_mode"; + +// Used to determine if the last session exited cleanly. Set to false when +// first opened, and to true when closing. On startup if the value is false, +// it means the profile didn't exit cleanly. +const wchar_t kSessionExitedCleanly[] = L"profile.exited_cleanly"; + +// This is one of three integer values: +// 0: (or empty) don't do anything special on startup. +// 1: restore the last session. +// 2: this was used to indicate a specific session should be restored. It is +// no longer used, but saved to avoid conflict with old preferences. +// 3: unused, previously indicated the user wants to restore a saved session. +// 4: restore the URLs defined in kURLsToRestoreOnStartup. +const wchar_t kRestoreOnStartup[] = L"session.restore_on_startup"; + +// The URLs to restore on startup or when the home button is pressed. The URLs +// are only restored on startup if kRestoreOnStartup is 4. +const wchar_t kURLsToRestoreOnStartup[] = + L"session.urls_to_restore_on_startup"; + +// The application locale. +const wchar_t kApplicationLocale[] = L"intl.app_locale"; + +// The default character encoding to assume for a web page in the +// absence of MIME charset specification +const wchar_t kDefaultCharset[] = L"intl.charset_default"; + +// The value to use for Accept-Languages HTTP header when making an HTTP +// request. +const wchar_t kAcceptLanguages[] = L"intl.accept_languages"; + +// The value to use for showing locale-dependent encoding list for different +// locale, it's initialized from the corresponding string resource that is +// stored in non-translatable part of the resource bundle. +const wchar_t kStaticEncodings[] = L"intl.static_encodings"; + +// WebKit preferences. +// A boolean flag to indicate whether WebKit standard font family is +// serif or sans-serif. We don't have a UI for setting standard family. +// Instead, we use this pref to map either serif or sans_serif to WebKit +// standard font family. At the moment, we don't have a UI for this +// flag, either. +const wchar_t kWebKitStandardFontIsSerif[] = + L"webkit.webprefs.standard_font_is_serif"; +const wchar_t kWebKitFixedFontFamily[] = L"webkit.webprefs.fixed_font_family"; +const wchar_t kWebKitSerifFontFamily[] = L"webkit.webprefs.serif_font_family"; +const wchar_t kWebKitSansSerifFontFamily[] = + L"webkit.webprefs.sansserif_font_family"; +const wchar_t kWebKitCursiveFontFamily[] = + L"webkit.webprefs.cursive_font_family"; +const wchar_t kWebKitFantasyFontFamily[] = + L"webkit.webprefs.fantasy_font_family"; +const wchar_t kWebKitDefaultFontSize[] = L"webkit.webprefs.default_font_size"; +const wchar_t kWebKitDefaultFixedFontSize[] = + L"webkit.webprefs.default_fixed_font_size"; +const wchar_t kWebKitMinimumFontSize[] = L"webkit.webprefs.minimum_font_size"; +const wchar_t kWebKitMinimumLogicalFontSize[] = + L"webkit.webprefs.minimum_logical_font_size"; +const wchar_t kWebKitJavascriptEnabled[] = + L"webkit.webprefs.javascript_enabled"; +const wchar_t kWebKitWebSecurityEnabled[] = + L"webkit.webprefs.web_security_enabled"; +const wchar_t kWebKitJavascriptCanOpenWindowsAutomatically[] = + L"webkit.webprefs.javascript_can_open_windows_automatically"; +const wchar_t kWebKitLoadsImagesAutomatically[] = + L"webkit.webprefs.loads_images_automatically"; +const wchar_t kWebKitPluginsEnabled[] = L"webkit.webprefs.plugins_enabled"; +const wchar_t kWebKitDomPasteEnabled[] = L"webkit.webprefs.dom_paste_enabled"; +const wchar_t kWebKitShrinksStandaloneImagesToFit[] = + L"webkit.webprefs.shrinks_standalone_images_to_fit"; +const wchar_t kWebKitDeveloperExtrasEnabled[] = + L"webkit.webprefs.developer_extras_enabled"; +const wchar_t kWebKitUsesUniversalDetector[] = + L"webkit.webprefs.uses_universal_detector"; +const wchar_t kWebKitTextAreasAreResizable[] = + L"webkit.webprefs.text_areas_are_resizable"; +const wchar_t kWebKitJavaEnabled[] = + L"webkit.webprefs.java_enabled"; + +// Boolean which specifies whether the bookmark bar is visible on all tabs. +const wchar_t kShowBookmarkBar[] = L"bookmark_bar.show_on_all_tabs"; + +// Boolean which specifies whether the destinations tab should always be on. +const wchar_t kAlwaysCreateDestinationsTab[] = + L"profile.always_create_destinations_tab"; + +// Boolean that is true if the password manager is on (will record new +// passwords and fill in known passwords). +const wchar_t kPasswordManagerEnabled[] = L"profile.password_manager_enabled"; + +// Boolean that is true if the form autofill is on (will record values entered +// in text inputs in forms and shows them in a popup when user type in a text +// input with the same name later on). +const wchar_t kFormAutofillEnabled[] = L"profile.form_autofill_enabled"; + +// Boolean that is true when SafeBrowsing is enabled. +const wchar_t kSafeBrowsingEnabled[] = L"safebrowsing.enabled"; + +// Boolean that is true when Suggest support is enabled. +const wchar_t kSearchSuggestEnabled[] = L"search.suggest_enabled"; + +// Enum that specifies whether to enforce a third-party cookie blocking policy. +// 0 - allow all cookies. +// 1 - block third-party cookies +// 2 - block all cookies +const wchar_t kCookieBehavior[] = L"security.cookie_behavior"; + +// Boolean that is true if mixed content should be filtered. +// TODO(jcampan): http://b/1084034: at some point this will become an enum +// (int): don't filter, filter everything, filter images only. +const wchar_t kMixedContentFiltering[] = L"security.mixed_content_filtering"; + +// The URL (as understood by TemplateURLRef) the default search provider uses +// for searches. +const wchar_t kDefaultSearchProviderSearchURL[] = + L"default_search_provider.search_url"; + +// The URL (as understood by TemplateURLRef) the default search provider uses +// for suggestions. +const wchar_t kDefaultSearchProviderSuggestURL[] = + L"default_search_provider.suggest_url"; + +// The name of the default search provider. +const wchar_t kDefaultSearchProviderName[] = L"default_search_provider.name"; + +// The id of the default search provider. +const wchar_t kDefaultSearchProviderID[] = L"default_search_provider.id"; + +// Boolean of whether or not popups should be completely blocked (true), or +// just opened "minimized" (default, false). +const wchar_t kBlockPopups[] = L"browser.block_popups"; + +// Boolean which specifies whether we should ask the user if we should download +// a file (true) or just download it automatically. +const wchar_t kPromptForDownload[] = L"download.prompt_for_download"; + +// A boolean pref set to true if we're using Link Doctor error pages. +const wchar_t kAlternateErrorPagesEnabled[] = L"alternate_error_pages.enabled"; + +// A boolean pref set to true if DNS pre-fetching is being done in browser. +const wchar_t kDnsPrefetchingEnabled[] = L"dns_prefetching.enabled"; + +// An adaptively identified list of domain names to be pre-fetched during the +// next startup, based on what was actually needed during this startup. +const wchar_t kDnsStartupPrefetchList[] = L"StartupDNSPrefetchList"; + +// A list of host names used to fetch web pages, and their commonly used +// sub-resource hostnames (and expected latency benefits from pre-resolving such +// sub-resource hostnames). +// This list is adaptively grown and pruned. +extern const wchar_t kDnsHostReferralList[] = L"HostReferralList"; + +// The disabled messages in IPC logging. +const wchar_t kIpcDisabledMessages[] = L"ipc_log_disabled_messages"; + +// A boolean pref set to true if a Home button to open the Home pages should be +// visible on the toolbar. +const wchar_t kShowHomeButton[] = L"browser.show_home_button"; + +// A boolean pref set to true if the Page and Options menu buttons should be +// visible on the toolbar. This is only used for Mac where the default is to +// have these menu in the main menubar, not as buttons on the toolbar. +const wchar_t kShowPageOptionsButtons[] = L"browser.show_page_options_buttons"; + +// A string value which saves short list of recently user selected encodings +// separated with comma punctuation mark. +const wchar_t kRecentlySelectedEncoding[] = + L"profile.recently_selected_encodings"; + +// Clear Browsing Data dialog preferences. +const wchar_t kDeleteBrowsingHistory[] = L"browser.clear_data.browsing_history"; +const wchar_t kDeleteDownloadHistory[] = + L"browser.clear_data.download_history"; +const wchar_t kDeleteCache[] = L"browser.clear_data.cache"; +const wchar_t kDeleteCookies[] = L"browser.clear_data.cookies"; +const wchar_t kDeletePasswords[] = L"browser.clear_data.passwords"; +const wchar_t kDeleteFormData[] = L"browser.clear_data.form_data"; +const wchar_t kDeleteTimePeriod[] = L"browser.clear_data.time_period"; + +// Integer prefs giving the widths of the columns in the bookmark table. Two +// configs are saved, one with the path column and one without. +const wchar_t kBookmarkTableNameWidth1[] = L"bookmark_table.name_width_1"; +const wchar_t kBookmarkTableURLWidth1[] = L"bookmark_table.url_width_1"; +const wchar_t kBookmarkTableNameWidth2[] = L"bookmark_table.name_width_2"; +const wchar_t kBookmarkTableURLWidth2[] = L"bookmark_table.url_width_2"; +const wchar_t kBookmarkTablePathWidth[] = L"bookmark_table.path_width"; + +// Bounds of the bookmark manager. +const wchar_t kBookmarkManagerPlacement[] = + L"bookmark_manager.window_placement"; + +// Integer location of the split bar in the bookmark manager. +const wchar_t kBookmarkManagerSplitLocation[] = + L"bookmark_manager.split_location"; + +// Boolean pref to define the default values for using spellchecker. +const wchar_t kEnableSpellCheck[] = L"browser.enable_spellchecking"; + +// String pref to define the default values for print overlays. +const wchar_t kPrintingPageHeaderLeft[] = L"printing.page.header.left"; +const wchar_t kPrintingPageHeaderCenter[] = L"printing.page.header.center"; +const wchar_t kPrintingPageHeaderRight[] = L"printing.page.header.right"; +const wchar_t kPrintingPageFooterLeft[] = L"printing.page.footer.left"; +const wchar_t kPrintingPageFooterCenter[] = L"printing.page.footer.center"; +const wchar_t kPrintingPageFooterRight[] = L"printing.page.footer.right"; + +// Boolean that indicates whether we should check if we are the default browser +// on start-up. +const wchar_t kCheckDefaultBrowser[] = L"browser.check_default_browser"; + +// *************** LOCAL STATE *************** +// These are attached to the machine/installation + +// List of profiles that the app knows about from last run. +const wchar_t kAvailableProfiles[] = L"profiles.available"; + +// The metrics client GUID and session ID. +const wchar_t kMetricsClientID[] = L"user_experience_metrics.client_id"; +const wchar_t kMetricsSessionID[] = L"user_experience_metrics.session_id"; + +// Boolean set to true when we're recording user metrics. +const wchar_t kMetricsIsRecording[] = L"user_experience_metrics.record"; + +// Date/time when the current metrics profile ID was created +// (which hopefully corresponds to first run). +const wchar_t kMetricsClientIDTimestamp[] = + L"user_experience_metrics.client_id_timestamp"; + +// Boolean that specifies whether or not crash reporting and metrics reporting +// are sent over the network for analysis. +const wchar_t kMetricsReportingEnabled[] = + L"user_experience_metrics.reporting_enabled"; + +// Array of strings that are each UMA logs that were supposed to be sent in the +// first minute of a browser session. These logs include things like crash count +// info, etc. +const wchar_t kMetricsInitialLogs[] = + L"user_experience_metrics.initial_logs"; + +// Array of strings that are each UMA logs that were not sent because the +// browser terminated before these accumulated metrics could be sent. These +// logs typically include histograms and memory reports, as well as ongoing +// user activities. +const wchar_t kMetricsOngoingLogs[] = + L"user_experience_metrics.ongoing_logs"; + +// Where profile specific metrics are placed. +const wchar_t kProfileMetrics[] = L"user_experience_metrics.profiles"; + +// The metrics for a profile are stored as dictionary values under the +// path kProfileMetrics. The individual metrics are placed under the path +// kProfileMetrics.kProfilePrefix. +const wchar_t kProfilePrefix[] = L"profile-"; + +// True if the previous run of the program exited cleanly. +const wchar_t kStabilityExitedCleanly[] = + L"user_experience_metrics.stability.exited_cleanly"; + +// Version string of previous run, which is used to assure that stability +// metrics reported under current version reflect stability of the same version. +const wchar_t kStabilityStatsVersion[] = + L"user_experience_metrics.stability.stats_version"; + +// False if we received a session end and either we crashed during processing +// the session end or ran out of time and windows terminated us. +const wchar_t kStabilitySessionEndCompleted[] = + L"user_experience_metrics.stability.session_end_completed"; + +// Number of times the application was launched since last report. +const wchar_t kStabilityLaunchCount[] = + L"user_experience_metrics.stability.launch_count"; + +// Number of times the application exited uncleanly since the last report. +const wchar_t kStabilityCrashCount[] = + L"user_experience_metrics.stability.crash_count"; + +// Number of times the session end did not complete. +const wchar_t kStabilityIncompleteSessionEndCount[] = + L"user_experience_metrics.stability.incomplete_session_end_count"; + +// Number of times a page load event occurred since the last report. +const wchar_t kStabilityPageLoadCount[] = + L"user_experience_metrics.stability.page_load_count"; + +// Number of times a renderer process crashed since the last report. +const wchar_t kStabilityRendererCrashCount[] = + L"user_experience_metrics.stability.renderer_crash_count"; + +// Number of times a renderer started in the sandbox and successfully +// used the sandbox desktop. +const wchar_t kSecurityRendererOnSboxDesktop[] = + L"user_experience_metrics.security.renderer_on_sbox_desktop"; + +// Number of times a renderer started in the sandbox and failed to +// used the sandbox desktop. +const wchar_t kSecurityRendererOnDefaultDesktop[] = + L"user_experience_metrics.security.renderer_on_default_desktop"; + +// Time when the app was last launched, in seconds since the epoch. +const wchar_t kStabilityLaunchTimeSec[] = + L"user_experience_metrics.stability.launch_time_sec"; + +// Time when the app was last known to be running, in seconds since +// the epoch. +const wchar_t kStabilityLastTimestampSec[] = + L"user_experience_metrics.stability.last_timestamp_sec"; + +// Number of milliseconds that the main application process was up since +// the last report. +const wchar_t kStabilityUptimeSec[] = + L"user_experience_metrics.stability.uptime_sec"; + +// This is the location of a list of dictionaries of plugin stability stats. +const wchar_t kStabilityPluginStats[] = + L"user_experience_metrics.stability.plugin_stats2"; + +// Number of times the renderer has become non-responsive since the last +// report. +const wchar_t kStabilityRendererHangCount[] = + L"user_experience_metrics.stability.renderer_hang_count"; + +// Number of times the browser has been able to register crash reporting. +const wchar_t kStabilityBreakpadRegistrationSuccess[] = + L"user_experience_metrics.stability.breakpad_registration_ok"; + +// Number of times the browser has failed to register crash reporting. +const wchar_t kStabilityBreakpadRegistrationFail[] = + L"user_experience_metrics.stability.breakpad_registration_fail"; + +// Number of times the browser has been run under a debugger. +const wchar_t kStabilityDebuggerPresent[] = + L"user_experience_metrics.stability.debugger_present"; + +// Number of times the browser has not been run under a debugger. +const wchar_t kStabilityDebuggerNotPresent[] = + L"user_experience_metrics.stability.debugger_not_present"; + +// The keys below are used for the dictionaries in the +// kStabilityPluginStats list. +const wchar_t kStabilityPluginName[] = L"name"; +const wchar_t kStabilityPluginLaunches[] = L"launches"; +const wchar_t kStabilityPluginInstances[] = L"instances"; +const wchar_t kStabilityPluginCrashes[] = L"crashes"; + +// The keys below are strictly increasing counters over the lifetime of +// a chrome installation. They are (optionally) sent up to the uninstall +// survey in the event of uninstallation. +const wchar_t kUninstallMetricsPageLoadCount[] = + L"uninstall_metrics.page_load_count"; +const wchar_t kUninstallLaunchCount[] = L"uninstall_metrics.launch_count"; +const wchar_t kUninstallMetricsInstallDate[] = + L"uninstall_metrics.installation_date2"; +const wchar_t kUninstallMetricsUptimeSec[] = L"uninstall_metrics.uptime_sec"; +const wchar_t kUninstallLastLaunchTimeSec[] = + L"uninstall_metrics.last_launch_time_sec"; +const wchar_t kUninstallLastObservedRunTimeSec[] = + L"uninstall_metrics.last_observed_running_time_sec"; + +// A collection of position, size, and other data relating to the browser +// window to restore on startup. +const wchar_t kBrowserWindowPlacement[] = L"browser.window_placement"; + +// A collection of position, size, and other data relating to the task +// manager window to restore on startup. +const wchar_t kTaskManagerWindowPlacement[] = L"task_manager.window_placement"; + +// A collection of position, size, and other data relating to the page info +// window to restore on startup. +const wchar_t kPageInfoWindowPlacement[] = L"page_info.window_placement"; + +// An integer specifying the total number of bytes to be used by the +// renderer's in-memory cache of objects. +const wchar_t kMemoryCacheSize[] = L"renderer.memory_cache.size"; + +// String which specifies where to download files to by default. +const wchar_t kDownloadDefaultDirectory[] = L"download.default_directory"; + +// Boolean that records if the download directory was changed by an +// upgrade a unsafe location to a safe location. +const wchar_t kDownloadDirUpgraded[] = L"download.directory_upgrade"; + +// String which specifies where to save html files to by default. +const wchar_t kSaveFileDefaultDirectory[] = L"savefile.default_directory"; + +// Extensions which should be opened upon completion. +const wchar_t kDownloadExtensionsToOpen[] = L"download.extensions_to_open"; + +// Integer which specifies the frequency in milliseconds for detecting whether +// plugin windows are hung. +const wchar_t kHungPluginDetectFrequency[] = + L"browser.hung_plugin_detect_freq"; + +// Integer which specifies the timeout value to be used for SendMessageTimeout +// to detect a hung plugin window. +const wchar_t kPluginMessageResponseTimeout[] = + L"browser.plugin_message_response_timeout"; + +// String which represents the dictionary name for our spell-checker. +const wchar_t kSpellCheckDictionary[] = L"spellcheck.dictionary"; + +// Dictionary of schemes used by the external protocol handler. +// The value is true if the scheme must be ignored. +const wchar_t kExcludedSchemes[] = L"protocol_handler.excluded_schemes"; + +// Keys used for MAC handling of SafeBrowsing requests. +const wchar_t kSafeBrowsingClientKey[] = L"safe_browsing.client_key"; +const wchar_t kSafeBrowsingWrappedKey[] = L"safe_browsing.wrapped_key"; + +// Integer that specifies the index of the tab the user was on when they +// last visited the options window. +const wchar_t kOptionsWindowLastTabIndex[] = L"options_window.last_tab_index"; + +// The mere fact that this pref is registered signals that we should show the +// First Run Search Information bubble when the first browser window appears. +// This preference is only registered by the first-run procedure. +const wchar_t kShouldShowFirstRunBubble[] = L"show-first-run-bubble"; + +// Signal that we should show the welcome page when we launch Chrome. +const wchar_t kShouldShowWelcomePage[] = L"show-welcome-page"; + +// String containing the last known Google URL. We re-detect this on startup in +// most cases, and use it to send traffic to the correct Google host or with the +// correct Google domain/country code for whatever location the user is in. +const wchar_t kLastKnownGoogleURL[] = L"browser.last_known_google_url"; + +// Integer containing the system Country ID the first time we checked the +// template URL prepopulate data. This is used to avoid adding a whole bunch of +// new search engine choices if prepopulation runs when the user's Country ID +// differs from their previous Country ID. This pref does not exist until +// prepopulation has been run at least once. +const wchar_t kCountryIDAtInstall[] = L"countryid_at_install"; +// OBSOLETE. Same as above, but uses the Windows-specific GeoID value instead. +// Updated if found to the above key. +const wchar_t kGeoIDAtInstall[] = L"geoid_at_install"; + +// An enum value of how the browser was shut down (see browser_shutdown.h). +const wchar_t kShutdownType[] = L"shutdown.type"; +// Number of processes that were open when the user shut down. +const wchar_t kShutdownNumProcesses[] = L"shutdown.num_processes"; +// Number of processes that were shut down using the slow path. +const wchar_t kShutdownNumProcessesSlow[] = L"shutdown.num_processes_slow"; + +// Number of bookmarks/folders on the bookmark bar/other bookmark folder. +const wchar_t kNumBookmarksOnBookmarkBar[] = + L"user_experience_metrics.num_bookmarks_on_bookmark_bar"; +const wchar_t kNumFoldersOnBookmarkBar[] = + L"user_experience_metrics.num_folders_on_bookmark_bar"; +const wchar_t kNumBookmarksInOtherBookmarkFolder[] = + L"user_experience_metrics.num_bookmarks_in_other_bookmark_folder"; +const wchar_t kNumFoldersInOtherBookmarkFolder[] = + L"user_experience_metrics.num_folders_in_other_bookmark_folder"; + +// Number of keywords. +const wchar_t kNumKeywords[] = L"user_experience_metrics.num_keywords"; + +// Whether Extensions or User Scripts are enabled. +const wchar_t kEnableExtensions[] = L"extensions.enabled"; +const wchar_t kEnableUserScripts[] = L"extensions.user_scripts_enabled"; + +// New Tab Page URLs that should not be shown as most visited thumbnails. +const wchar_t kNTPMostVisitedURLsBlacklist[] = L"ntp.most_visited_blacklist"; + +} // namespace prefs diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_names.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_names.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_names.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_names.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,189 @@ +// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Constants for the names of various preferences, for easier changing. + +#ifndef CHROME_COMMON_PREF_NAMES_H_ +#define CHROME_COMMON_PREF_NAMES_H_ + +namespace prefs { + +// Profile prefs +extern const wchar_t kHomePageIsNewTabPage[]; +extern const wchar_t kHomePage[]; +extern const wchar_t kProfileName[]; +extern const wchar_t kProfileNickname[]; +extern const wchar_t kProfileID[]; +extern const wchar_t kRecentlyViewedModelBiasActiveTabHistory[]; +extern const wchar_t kRecentlyViewedModelSelectionMode[]; +extern const wchar_t kSessionExitedCleanly[]; +extern const wchar_t kRestoreOnStartup[]; +extern const wchar_t kURLsToRestoreOnStartup[]; +extern const wchar_t kApplicationLocale[]; +extern const wchar_t kDefaultCharset[]; +extern const wchar_t kAcceptLanguages[]; +extern const wchar_t kStaticEncodings[]; +extern const wchar_t kShowBookmarkBar[]; +extern const wchar_t kWebKitStandardFontIsSerif[]; +extern const wchar_t kWebKitFixedFontFamily[]; +extern const wchar_t kWebKitSerifFontFamily[]; +extern const wchar_t kWebKitSansSerifFontFamily[]; +extern const wchar_t kWebKitCursiveFontFamily[]; +extern const wchar_t kWebKitFantasyFontFamily[]; +extern const wchar_t kWebKitDefaultFontSize[]; +extern const wchar_t kWebKitDefaultFixedFontSize[]; +extern const wchar_t kWebKitMinimumFontSize[]; +extern const wchar_t kWebKitMinimumLogicalFontSize[]; +extern const wchar_t kWebKitJavascriptEnabled[]; +extern const wchar_t kWebKitWebSecurityEnabled[]; +extern const wchar_t kWebKitJavascriptCanOpenWindowsAutomatically[]; +extern const wchar_t kWebKitLoadsImagesAutomatically[]; +extern const wchar_t kWebKitPluginsEnabled[]; +extern const wchar_t kWebKitDomPasteEnabled[]; +extern const wchar_t kWebKitShrinksStandaloneImagesToFit[]; +extern const wchar_t kWebKitDeveloperExtrasEnabled[]; +extern const wchar_t kWebKitUsesUniversalDetector[]; +extern const wchar_t kWebKitTextAreasAreResizable[]; +extern const wchar_t kWebKitJavaEnabled[]; +extern const wchar_t kAlwaysCreateDestinationsTab[]; +extern const wchar_t kPasswordManagerEnabled[]; +extern const wchar_t kFormAutofillEnabled[]; +extern const wchar_t kSafeBrowsingEnabled[]; +extern const wchar_t kSearchSuggestEnabled[]; +extern const wchar_t kCookieBehavior[]; +extern const wchar_t kMixedContentFiltering[]; +extern const wchar_t kDefaultSearchProviderSearchURL[]; +extern const wchar_t kDefaultSearchProviderSuggestURL[]; +extern const wchar_t kDefaultSearchProviderName[]; +extern const wchar_t kDefaultSearchProviderID[]; +extern const wchar_t kBlockPopups[]; +extern const wchar_t kPromptForDownload[]; +extern const wchar_t kAlternateErrorPagesEnabled[]; +extern const wchar_t kDnsPrefetchingEnabled[]; +extern const wchar_t kDnsStartupPrefetchList[]; +extern const wchar_t kDnsHostReferralList[]; +extern const wchar_t kIpcDisabledMessages[]; +extern const wchar_t kShowHomeButton[]; +extern const wchar_t kShowPageOptionsButtons[]; +extern const wchar_t kRecentlySelectedEncoding[]; +extern const wchar_t kDeleteBrowsingHistory[]; +extern const wchar_t kDeleteDownloadHistory[]; +extern const wchar_t kDeleteCache[]; +extern const wchar_t kDeleteCookies[]; +extern const wchar_t kDeletePasswords[]; +extern const wchar_t kDeleteFormData[]; +extern const wchar_t kBookmarkTableNameWidth1[]; +extern const wchar_t kBookmarkTableURLWidth1[]; +extern const wchar_t kBookmarkTableNameWidth2[]; +extern const wchar_t kBookmarkTableURLWidth2[]; +extern const wchar_t kBookmarkTablePathWidth[]; +extern const wchar_t kBookmarkManagerPlacement[]; +extern const wchar_t kBookmarkManagerSplitLocation[]; +extern const wchar_t kEnableSpellCheck[]; +extern const wchar_t kDeleteTimePeriod[]; +extern const wchar_t kPrintingPageHeaderLeft[]; +extern const wchar_t kPrintingPageHeaderCenter[]; +extern const wchar_t kPrintingPageHeaderRight[]; +extern const wchar_t kPrintingPageFooterLeft[]; +extern const wchar_t kPrintingPageFooterCenter[]; +extern const wchar_t kPrintingPageFooterRight[]; +extern const wchar_t kCheckDefaultBrowser[]; + +// Local state +extern const wchar_t kAvailableProfiles[]; + +extern const wchar_t kMetricsClientID[]; +extern const wchar_t kMetricsSessionID[]; +extern const wchar_t kMetricsIsRecording[]; +extern const wchar_t kMetricsClientIDTimestamp[]; +extern const wchar_t kMetricsReportingEnabled[]; +extern const wchar_t kMetricsInitialLogs[]; +extern const wchar_t kMetricsOngoingLogs[]; + +extern const wchar_t kProfileMetrics[]; +extern const wchar_t kProfilePrefix[]; + +extern const wchar_t kStabilityExitedCleanly[]; +extern const wchar_t kStabilityStatsVersion[]; +extern const wchar_t kStabilitySessionEndCompleted[]; +extern const wchar_t kStabilityLaunchCount[]; +extern const wchar_t kStabilityCrashCount[]; +extern const wchar_t kStabilityIncompleteSessionEndCount[]; +extern const wchar_t kStabilityPageLoadCount[]; +extern const wchar_t kStabilityRendererCrashCount[]; +extern const wchar_t kStabilityLaunchTimeSec[]; +extern const wchar_t kStabilityLastTimestampSec[]; +extern const wchar_t kStabilityUptimeSec[]; +extern const wchar_t kStabilityRendererHangCount[]; + +extern const wchar_t kStabilityBreakpadRegistrationSuccess[]; +extern const wchar_t kStabilityBreakpadRegistrationFail[]; +extern const wchar_t kStabilityDebuggerPresent[]; +extern const wchar_t kStabilityDebuggerNotPresent[]; + +extern const wchar_t kSecurityRendererOnSboxDesktop[]; +extern const wchar_t kSecurityRendererOnDefaultDesktop[]; + +extern const wchar_t kStabilityPluginStats[]; +extern const wchar_t kStabilityPluginName[]; +extern const wchar_t kStabilityPluginLaunches[]; +extern const wchar_t kStabilityPluginInstances[]; +extern const wchar_t kStabilityPluginCrashes[]; + +extern const wchar_t kUninstallMetricsPageLoadCount[]; +extern const wchar_t kUninstallLaunchCount[]; + +extern const wchar_t kUninstallMetricsInstallDate[]; +extern const wchar_t kUninstallMetricsUptimeSec[]; +extern const wchar_t kUninstallLastLaunchTimeSec[]; +extern const wchar_t kUninstallLastObservedRunTimeSec[]; + +extern const wchar_t kBrowserWindowPlacement[]; +extern const wchar_t kTaskManagerWindowPlacement[]; +extern const wchar_t kPageInfoWindowPlacement[]; +extern const wchar_t kMemoryCacheSize[]; + +extern const wchar_t kDownloadDefaultDirectory[]; +extern const wchar_t kDownloadExtensionsToOpen[]; +extern const wchar_t kDownloadDirUpgraded[]; + +extern const wchar_t kSaveFileDefaultDirectory[]; + +extern const wchar_t kHungPluginDetectFrequency[]; +extern const wchar_t kPluginMessageResponseTimeout[]; + +extern const wchar_t kSpellCheckDictionary[]; + +extern const wchar_t kExcludedSchemes[]; + +extern const wchar_t kSafeBrowsingClientKey[]; +extern const wchar_t kSafeBrowsingWrappedKey[]; + +extern const wchar_t kOptionsWindowLastTabIndex[]; +extern const wchar_t kShouldShowFirstRunBubble[]; +extern const wchar_t kShouldShowWelcomePage[]; + +extern const wchar_t kLastKnownGoogleURL[]; + +extern const wchar_t kCountryIDAtInstall[]; +extern const wchar_t kGeoIDAtInstall[]; // OBSOLETE + +extern const wchar_t kShutdownType[]; +extern const wchar_t kShutdownNumProcesses[]; +extern const wchar_t kShutdownNumProcessesSlow[]; + +extern const wchar_t kNumBookmarksOnBookmarkBar[]; +extern const wchar_t kNumFoldersOnBookmarkBar[]; +extern const wchar_t kNumBookmarksInOtherBookmarkFolder[]; +extern const wchar_t kNumFoldersInOtherBookmarkFolder[]; + +extern const wchar_t kNumKeywords[]; + +extern const wchar_t kEnableExtensions[]; +extern const wchar_t kEnableUserScripts[]; + +extern const wchar_t kNTPMostVisitedURLsBlacklist[]; +} + +#endif // CHROME_COMMON_PREF_NAMES_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_service.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_service.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_service.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_service.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,696 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/pref_service.h" + +#include "app/l10n_util.h" +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/stl_util-inl.h" +#include "base/string_util.h" +#include "base/thread.h" +#include "chrome/common/json_value_serializer.h" +#include "chrome/common/notification_service.h" +#include "grit/generated_resources.h" + +namespace { + +// A helper function for RegisterLocalized*Pref that creates a Value* based on +// the string value in the locale dll. Because we control the values in a +// locale dll, this should always return a Value of the appropriate type. +Value* CreateLocaleDefaultValue(Value::ValueType type, int message_id) { + std::wstring resource_string = l10n_util::GetString(message_id); + DCHECK(!resource_string.empty()); + switch (type) { + case Value::TYPE_BOOLEAN: { + if (L"true" == resource_string) + return Value::CreateBooleanValue(true); + if (L"false" == resource_string) + return Value::CreateBooleanValue(false); + break; + } + + case Value::TYPE_INTEGER: { + return Value::CreateIntegerValue( + StringToInt(WideToUTF16Hack(resource_string))); + break; + } + + case Value::TYPE_REAL: { + return Value::CreateRealValue( + StringToDouble(WideToUTF16Hack(resource_string))); + break; + } + + case Value::TYPE_STRING: { + return Value::CreateStringValue(resource_string); + break; + } + + default: { + NOTREACHED() << + "list and dictionary types can not have default locale values"; + } + } + NOTREACHED(); + return Value::CreateNullValue(); +} + +} // namespace + +PrefService::PrefService(const FilePath& pref_filename, + const base::Thread* backend_thread) + : persistent_(new DictionaryValue), + transient_(new DictionaryValue), + writer_(pref_filename, backend_thread) { + ReloadPersistentPrefs(); +} + +PrefService::~PrefService() { + DCHECK(CalledOnValidThread()); + + // Verify that there are no pref observers when we shut down. + for (PrefObserverMap::iterator it = pref_observers_.begin(); + it != pref_observers_.end(); ++it) { + NotificationObserverList::Iterator obs_iterator(*(it->second)); + if (obs_iterator.GetNext()) { + LOG(WARNING) << "pref observer found at shutdown " << it->first; + } + } + + STLDeleteContainerPointers(prefs_.begin(), prefs_.end()); + prefs_.clear(); + STLDeleteContainerPairSecondPointers(pref_observers_.begin(), + pref_observers_.end()); + pref_observers_.clear(); +} + +bool PrefService::ReloadPersistentPrefs() { + DCHECK(CalledOnValidThread()); + + JSONFileValueSerializer serializer(writer_.path()); + scoped_ptr root(serializer.Deserialize(NULL)); + if (!root.get()) + return false; + + // Preferences should always have a dictionary root. + if (!root->IsType(Value::TYPE_DICTIONARY)) + return false; + + persistent_.reset(static_cast(root.release())); + for (PreferenceSet::iterator it = prefs_.begin(); + it != prefs_.end(); ++it) { + (*it)->root_pref_ = persistent_.get(); + } + + return true; +} + +bool PrefService::SavePersistentPrefs() { + DCHECK(CalledOnValidThread()); + + std::string data; + if (!SerializePrefData(&data)) + return false; + + writer_.WriteNow(data); + return true; +} + +bool PrefService::ScheduleSavePersistentPrefs() { + DCHECK(CalledOnValidThread()); + + std::string data; + if (!SerializePrefData(&data)) + return false; + + writer_.ScheduleWrite(data); + return true; +} + +void PrefService::RegisterBooleanPref(const wchar_t* path, + bool default_value) { + Preference* pref = new Preference(persistent_.get(), path, + Value::CreateBooleanValue(default_value)); + RegisterPreference(pref); +} + +void PrefService::RegisterIntegerPref(const wchar_t* path, + int default_value) { + Preference* pref = new Preference(persistent_.get(), path, + Value::CreateIntegerValue(default_value)); + RegisterPreference(pref); +} + +void PrefService::RegisterRealPref(const wchar_t* path, + double default_value) { + Preference* pref = new Preference(persistent_.get(), path, + Value::CreateRealValue(default_value)); + RegisterPreference(pref); +} + +void PrefService::RegisterStringPref(const wchar_t* path, + const std::wstring& default_value) { + Preference* pref = new Preference(persistent_.get(), path, + Value::CreateStringValue(default_value)); + RegisterPreference(pref); +} + +void PrefService::RegisterFilePathPref(const wchar_t* path, + const FilePath& default_value) { + Preference* pref = new Preference(persistent_.get(), path, + Value::CreateStringValue(default_value.value())); + RegisterPreference(pref); +} + +void PrefService::RegisterListPref(const wchar_t* path) { + Preference* pref = new Preference(persistent_.get(), path, + new ListValue); + RegisterPreference(pref); +} + +void PrefService::RegisterDictionaryPref(const wchar_t* path) { + Preference* pref = new Preference(persistent_.get(), path, + new DictionaryValue()); + RegisterPreference(pref); +} + +void PrefService::RegisterLocalizedBooleanPref(const wchar_t* path, + int locale_default_message_id) { + Preference* pref = new Preference(persistent_.get(), path, + CreateLocaleDefaultValue(Value::TYPE_BOOLEAN, locale_default_message_id)); + RegisterPreference(pref); +} + +void PrefService::RegisterLocalizedIntegerPref(const wchar_t* path, + int locale_default_message_id) { + Preference* pref = new Preference(persistent_.get(), path, + CreateLocaleDefaultValue(Value::TYPE_INTEGER, locale_default_message_id)); + RegisterPreference(pref); +} + +void PrefService::RegisterLocalizedRealPref(const wchar_t* path, + int locale_default_message_id) { + Preference* pref = new Preference(persistent_.get(), path, + CreateLocaleDefaultValue(Value::TYPE_REAL, locale_default_message_id)); + RegisterPreference(pref); +} + +void PrefService::RegisterLocalizedStringPref(const wchar_t* path, + int locale_default_message_id) { + Preference* pref = new Preference(persistent_.get(), path, + CreateLocaleDefaultValue(Value::TYPE_STRING, locale_default_message_id)); + RegisterPreference(pref); +} + +bool PrefService::IsPrefRegistered(const wchar_t* path) { + DCHECK(CalledOnValidThread()); + // TODO(tc): We can remove this method and just use FindPreference. + return FindPreference(path) ? true : false; +} + +bool PrefService::GetBoolean(const wchar_t* path) const { + DCHECK(CalledOnValidThread()); + + bool result = false; + if (transient_->GetBoolean(path, &result)) + return result; + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return result; + } + bool rv = pref->GetValue()->GetAsBoolean(&result); + DCHECK(rv); + return result; +} + +int PrefService::GetInteger(const wchar_t* path) const { + DCHECK(CalledOnValidThread()); + + int result = 0; + if (transient_->GetInteger(path, &result)) + return result; + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return result; + } + bool rv = pref->GetValue()->GetAsInteger(&result); + DCHECK(rv); + return result; +} + +double PrefService::GetReal(const wchar_t* path) const { + DCHECK(CalledOnValidThread()); + + double result = 0.0; + if (transient_->GetReal(path, &result)) + return result; + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return result; + } + bool rv = pref->GetValue()->GetAsReal(&result); + DCHECK(rv); + return result; +} + +std::wstring PrefService::GetString(const wchar_t* path) const { + DCHECK(CalledOnValidThread()); + + std::wstring result; + if (transient_->GetString(path, &result)) + return result; + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return result; + } + bool rv = pref->GetValue()->GetAsString(&result); + DCHECK(rv); + return result; +} + +FilePath PrefService::GetFilePath(const wchar_t* path) const { + DCHECK(CalledOnValidThread()); + + FilePath::StringType result; + if (transient_->GetString(path, &result)) + return FilePath(result); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return FilePath(result); + } + bool rv = pref->GetValue()->GetAsString(&result); + DCHECK(rv); + return FilePath(result); +} + +bool PrefService::HasPrefPath(const wchar_t* path) const { + Value* value = NULL; + return (transient_->Get(path, &value) || persistent_->Get(path, &value)); +} + +const PrefService::Preference* PrefService::FindPreference( + const wchar_t* pref_name) const { + DCHECK(CalledOnValidThread()); + Preference p(NULL, pref_name, NULL); + PreferenceSet::const_iterator it = prefs_.find(&p); + return it == prefs_.end() ? NULL : *it; +} + +const DictionaryValue* PrefService::GetDictionary(const wchar_t* path) const { + DCHECK(CalledOnValidThread()); + + DictionaryValue* result = NULL; + if (transient_->GetDictionary(path, &result)) + return result; + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return NULL; + } + const Value* value = pref->GetValue(); + if (value->GetType() == Value::TYPE_NULL) + return NULL; + return static_cast(value); +} + +const ListValue* PrefService::GetList(const wchar_t* path) const { + DCHECK(CalledOnValidThread()); + + ListValue* result = NULL; + if (transient_->GetList(path, &result)) + return result; + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return NULL; + } + const Value* value = pref->GetValue(); + if (value->GetType() == Value::TYPE_NULL) + return NULL; + return static_cast(value); +} + +void PrefService::AddPrefObserver(const wchar_t* path, + NotificationObserver* obs) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to add an observer for an unregistered pref: " + << path; + return; + } + + // Get the pref observer list associated with the path. + NotificationObserverList* observer_list = NULL; + PrefObserverMap::iterator observer_iterator = pref_observers_.find(path); + if (observer_iterator == pref_observers_.end()) { + observer_list = new NotificationObserverList; + pref_observers_[path] = observer_list; + } else { + observer_list = observer_iterator->second; + } + + // Verify that this observer doesn't already exist. + NotificationObserverList::Iterator it(*observer_list); + NotificationObserver* existing_obs; + while ((existing_obs = it.GetNext()) != NULL) { + DCHECK(existing_obs != obs) << path << " observer already registered"; + if (existing_obs == obs) + return; + } + + // Ok, safe to add the pref observer. + observer_list->AddObserver(obs); +} + +void PrefService::RemovePrefObserver(const wchar_t* path, + NotificationObserver* obs) { + DCHECK(CalledOnValidThread()); + + PrefObserverMap::iterator observer_iterator = pref_observers_.find(path); + if (observer_iterator == pref_observers_.end()) { + return; + } + + NotificationObserverList* observer_list = observer_iterator->second; + observer_list->RemoveObserver(obs); +} + +void PrefService::RegisterPreference(Preference* pref) { + DCHECK(CalledOnValidThread()); + + if (FindPreference(pref->name().c_str())) { + NOTREACHED() << "Tried to register duplicate pref " << pref->name(); + delete pref; + return; + } + prefs_.insert(pref); +} + +void PrefService::ClearPref(const wchar_t* path) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to clear an unregistered pref: " << path; + return; + } + + transient_->Remove(path, NULL); + Value* value; + bool has_old_value = persistent_->Get(path, &value); + persistent_->Remove(path, NULL); + + if (has_old_value) + FireObservers(path); +} + +void PrefService::SetBoolean(const wchar_t* path, bool value) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to write an unregistered pref: " << path; + return; + } + if (pref->type() != Value::TYPE_BOOLEAN) { + NOTREACHED() << "Wrong type for SetBoolean: " << path; + return; + } + + scoped_ptr old_value(GetPrefCopy(path)); + bool rv = persistent_->SetBoolean(path, value); + DCHECK(rv); + + FireObserversIfChanged(path, old_value.get()); +} + +void PrefService::SetInteger(const wchar_t* path, int value) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to write an unregistered pref: " << path; + return; + } + if (pref->type() != Value::TYPE_INTEGER) { + NOTREACHED() << "Wrong type for SetInteger: " << path; + return; + } + + scoped_ptr old_value(GetPrefCopy(path)); + bool rv = persistent_->SetInteger(path, value); + DCHECK(rv); + + FireObserversIfChanged(path, old_value.get()); +} + +void PrefService::SetReal(const wchar_t* path, double value) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to write an unregistered pref: " << path; + return; + } + if (pref->type() != Value::TYPE_REAL) { + NOTREACHED() << "Wrong type for SetReal: " << path; + return; + } + + scoped_ptr old_value(GetPrefCopy(path)); + bool rv = persistent_->SetReal(path, value); + DCHECK(rv); + + FireObserversIfChanged(path, old_value.get()); +} + +void PrefService::SetString(const wchar_t* path, const std::wstring& value) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to write an unregistered pref: " << path; + return; + } + if (pref->type() != Value::TYPE_STRING) { + NOTREACHED() << "Wrong type for SetString: " << path; + return; + } + + scoped_ptr old_value(GetPrefCopy(path)); + bool rv = persistent_->SetString(path, value); + DCHECK(rv); + + FireObserversIfChanged(path, old_value.get()); +} + +void PrefService::SetFilePath(const wchar_t* path, const FilePath& value) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to write an unregistered pref: " << path; + return; + } + if (pref->type() != Value::TYPE_STRING) { + NOTREACHED() << "Wrong type for SetFilePath: " << path; + return; + } + + scoped_ptr old_value(GetPrefCopy(path)); + bool rv = persistent_->SetString(path, value.value()); + DCHECK(rv); + + FireObserversIfChanged(path, old_value.get()); +} + +void PrefService::SetInt64(const wchar_t* path, int64 value) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to write an unregistered pref: " << path; + return; + } + if (pref->type() != Value::TYPE_STRING) { + NOTREACHED() << "Wrong type for SetInt64: " << path; + return; + } + + scoped_ptr old_value(GetPrefCopy(path)); + bool rv = persistent_->SetString(path, Int64ToWString(value)); + DCHECK(rv); + + FireObserversIfChanged(path, old_value.get()); +} + +int64 PrefService::GetInt64(const wchar_t* path) const { + DCHECK(CalledOnValidThread()); + + std::wstring result; + if (transient_->GetString(path, &result)) + return StringToInt64(WideToUTF16Hack(result)); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to read an unregistered pref: " << path; + return StringToInt64(WideToUTF16Hack(result)); + } + bool rv = pref->GetValue()->GetAsString(&result); + DCHECK(rv); + return StringToInt64(WideToUTF16Hack(result)); +} + +void PrefService::RegisterInt64Pref(const wchar_t* path, int64 default_value) { + Preference* pref = new Preference(persistent_.get(), path, + Value::CreateStringValue(Int64ToWString(default_value))); + RegisterPreference(pref); +} + +DictionaryValue* PrefService::GetMutableDictionary(const wchar_t* path) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to get an unregistered pref: " << path; + return NULL; + } + if (pref->type() != Value::TYPE_DICTIONARY) { + NOTREACHED() << "Wrong type for GetMutableDictionary: " << path; + return NULL; + } + + DictionaryValue* dict = NULL; + bool rv = persistent_->GetDictionary(path, &dict); + if (!rv) { + dict = new DictionaryValue; + rv = persistent_->Set(path, dict); + DCHECK(rv); + } + return dict; +} + +ListValue* PrefService::GetMutableList(const wchar_t* path) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + if (!pref) { + NOTREACHED() << "Trying to get an unregistered pref: " << path; + return NULL; + } + if (pref->type() != Value::TYPE_LIST) { + NOTREACHED() << "Wrong type for GetMutableList: " << path; + return NULL; + } + + ListValue* list = NULL; + bool rv = persistent_->GetList(path, &list); + if (!rv) { + list = new ListValue; + rv = persistent_->Set(path, list); + DCHECK(rv); + } + return list; +} + +Value* PrefService::GetPrefCopy(const wchar_t* path) { + DCHECK(CalledOnValidThread()); + + const Preference* pref = FindPreference(path); + DCHECK(pref); + return pref->GetValue()->DeepCopy(); +} + +void PrefService::FireObserversIfChanged(const wchar_t* path, + const Value* old_value) { + Value* new_value = NULL; + persistent_->Get(path, &new_value); + if (!old_value->Equals(new_value)) + FireObservers(path); +} + +void PrefService::FireObservers(const wchar_t* path) { + DCHECK(CalledOnValidThread()); + + // Convert path to a std::wstring because the Details constructor requires a + // class. + std::wstring path_str(path); + PrefObserverMap::iterator observer_iterator = pref_observers_.find(path_str); + if (observer_iterator == pref_observers_.end()) + return; + + NotificationObserverList::Iterator it(*(observer_iterator->second)); + NotificationObserver* observer; + while ((observer = it.GetNext()) != NULL) { + observer->Observe(NotificationType::PREF_CHANGED, + Source(this), + Details(&path_str)); + } +} + +bool PrefService::SerializePrefData(std::string* output) const { + // TODO(tc): Do we want to prune webkit preferences that match the default + // value? + JSONStringValueSerializer serializer(output); + serializer.set_pretty_print(true); + return serializer.Serialize(*(persistent_.get())); +} + +/////////////////////////////////////////////////////////////////////////////// +// PrefService::Preference + +PrefService::Preference::Preference(DictionaryValue* root_pref, + const wchar_t* name, + Value* default_value) + : type_(Value::TYPE_NULL), + name_(name), + default_value_(default_value), + root_pref_(root_pref) { + DCHECK(name); + + if (default_value) { + type_ = default_value->GetType(); + DCHECK(type_ != Value::TYPE_NULL && type_ != Value::TYPE_BINARY) << + "invalid preference type: " << type_; + } + + // We set the default value of lists and dictionaries to be null so it's + // easier for callers to check for empty list/dict prefs. + if (Value::TYPE_LIST == type_ || Value::TYPE_DICTIONARY == type_) + default_value_.reset(Value::CreateNullValue()); +} + +const Value* PrefService::Preference::GetValue() const { + DCHECK(NULL != root_pref_) << + "Must register pref before getting its value"; + + Value* temp_value = NULL; + if (root_pref_->Get(name_.c_str(), &temp_value) && + temp_value->GetType() == type_) { + return temp_value; + } + + // Pref not found, just return the app default. + return default_value_.get(); +} + +bool PrefService::Preference::IsDefaultValue() const { + DCHECK(default_value_.get()); + return default_value_->Equals(GetValue()); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_service.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_service.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_service.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_service.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,234 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This provides a way to access the application's current preferences. +// This service has two preference stores, one for "persistent" preferences, +// which get serialized for use in the next session, and one for "transient" +// preferences, which are in effect for only the current session +// (this usually encodes things like command-line switches). +// +// Calling the getter functions in this class basically looks at both the +// persistent and transient stores, where any corresponding value in the +// transient store overrides the one in the persistent store. + +#ifndef CHROME_COMMON_PREF_SERVICE_H_ +#define CHROME_COMMON_PREF_SERVICE_H_ + +#include + +#include "base/file_path.h" +#include "base/hash_tables.h" +#include "base/non_thread_safe.h" +#include "base/observer_list.h" +#include "base/scoped_ptr.h" +#include "base/values.h" +#include "chrome/common/important_file_writer.h" + +class NotificationObserver; +class Preference; + +namespace base { +class Thread; +} + +class PrefService : public NonThreadSafe { + public: + + // A helper class to store all the information associated with a preference. + class Preference { + public: + + // The type of the preference is determined by the type of |default_value|. + // Therefore, the type needs to be a boolean, integer, real, string, + // dictionary (a branch), or list. You shouldn't need to construct this on + // your own, use the PrefService::Register*Pref methods instead. + // |default_value| will be owned by the Preference object. + Preference(DictionaryValue* root_pref, + const wchar_t* name, + Value* default_value); + ~Preference() {} + + Value::ValueType type() const { return type_; } + + // Returns the name of the Preference (i.e., the key, e.g., + // browser.window_placement). + const std::wstring name() const { return name_; } + + // Returns the value of the Preference. If there is no user specified + // value, it returns the default value. + const Value* GetValue() const; + + // Returns true if the current value matches the default value. + bool IsDefaultValue() const; + + private: + friend class PrefService; + + Value::ValueType type_; + std::wstring name_; + scoped_ptr default_value_; + + // A reference to the pref service's persistent prefs. + DictionaryValue* root_pref_; + + DISALLOW_COPY_AND_ASSIGN(Preference); + }; + + // |pref_filename| is the path to the prefs file we will try to load or save + // to. Saves will be executed on |backend_thread|. It should be the file + // thread in Chrome. You can pass NULL for unit tests, and then no separate + // thread will be used. + PrefService(const FilePath& pref_filename, + const base::Thread* backend_thread); + ~PrefService(); + + // Reloads the data from file. This should only be called when the importer + // is running during first run, and the main process may not change pref + // values while the importer process is running. Returns true on success. + bool ReloadPersistentPrefs(); + + // Writes the data to disk. The return value only reflects whether + // serialization was successful; we don't know whether the data actually made + // it on disk (since it's on a different thread). This should only be used if + // we need to save immediately (basically, during shutdown). Otherwise, you + // should use ScheduleSavePersistentPrefs. + bool SavePersistentPrefs(); + + // Serializes the data and schedules save using ImportantFileWriter. + bool ScheduleSavePersistentPrefs(); + + DictionaryValue* transient() { return transient_.get(); } + + // Make the PrefService aware of a pref. + void RegisterBooleanPref(const wchar_t* path, + bool default_value); + void RegisterIntegerPref(const wchar_t* path, + int default_value); + void RegisterRealPref(const wchar_t* path, + double default_value); + void RegisterStringPref(const wchar_t* path, + const std::wstring& default_value); + void RegisterFilePathPref(const wchar_t* path, + const FilePath& default_value); + void RegisterListPref(const wchar_t* path); + void RegisterDictionaryPref(const wchar_t* path); + + // These varients use a default value from the locale dll instead. + void RegisterLocalizedBooleanPref(const wchar_t* path, + int locale_default_message_id); + void RegisterLocalizedIntegerPref(const wchar_t* path, + int locale_default_message_id); + void RegisterLocalizedRealPref(const wchar_t* path, + int locale_default_message_id); + void RegisterLocalizedStringPref(const wchar_t* path, + int locale_default_message_id); + + // Returns whether the specified pref has been registered. + bool IsPrefRegistered(const wchar_t* path); + + // If the path is valid and the value at the end of the path matches the type + // specified, it will return the specified value. Otherwise, the default + // value (set when the pref was registered) will be returned. + bool GetBoolean(const wchar_t* path) const; + int GetInteger(const wchar_t* path) const; + double GetReal(const wchar_t* path) const; + std::wstring GetString(const wchar_t* path) const; + FilePath GetFilePath(const wchar_t* path) const; + + // Returns the branch if it exists. If it's not a branch or the branch does + // not exist, returns NULL. This does + const DictionaryValue* GetDictionary(const wchar_t* path) const; + const ListValue* GetList(const wchar_t* path) const; + + // If the pref at the given path changes, we call the observer's Observe + // method with NOTIFY_PREF_CHANGED. + void AddPrefObserver(const wchar_t* path, NotificationObserver* obs); + void RemovePrefObserver(const wchar_t* path, NotificationObserver* obs); + + // Removes a user pref and restores the pref to its default value. + void ClearPref(const wchar_t* path); + + // If the path is valid (i.e., registered), update the pref value. + void SetBoolean(const wchar_t* path, bool value); + void SetInteger(const wchar_t* path, int value); + void SetReal(const wchar_t* path, double value); + void SetString(const wchar_t* path, const std::wstring& value); + void SetFilePath(const wchar_t* path, const FilePath& value); + + // Int64 helper methods that actually store the given value as a string. + // Note that if obtaining the named value via GetDictionary or GetList, the + // Value type will be TYPE_STRING. + void SetInt64(const wchar_t* path, int64 value); + int64 GetInt64(const wchar_t* path) const; + void RegisterInt64Pref(const wchar_t* path, int64 default_value); + + // Used to set the value of dictionary or list values in the pref tree. This + // will create a dictionary or list if one does not exist in the pref tree. + // This method returns NULL only if you're requesting an unregistered pref or + // a non-dict/non-list pref. + // WARNING: Changes to the dictionary or list will not automatically notify + // pref observers. TODO(tc): come up with a way to still fire observers. + DictionaryValue* GetMutableDictionary(const wchar_t* path); + ListValue* GetMutableList(const wchar_t* path); + + // Returns true if a value has been set for the specified path. + // NOTE: this is NOT the same as IsPrefRegistered. In particular + // IsPrefRegistered returns whether RegisterXXX has been invoked, where as + // this checks if a value exists for the path. + bool HasPrefPath(const wchar_t* path) const; + + class PreferencePathComparator { + public: + bool operator() (Preference* lhs, Preference* rhs) const { + return lhs->name() < rhs->name(); + } + }; + typedef std::set PreferenceSet; + const PreferenceSet& preference_set() const { return prefs_; } + + // A helper method to quickly look up a preference. Returns NULL if the + // preference is not registered. + const Preference* FindPreference(const wchar_t* pref_name) const; + + private: + // Add a preference to the PreferenceMap. If the pref already exists, return + // false. This method takes ownership of |pref|. + void RegisterPreference(Preference* pref); + + // Returns a copy of the current pref value. The caller is responsible for + // deleting the returned object. + Value* GetPrefCopy(const wchar_t* pref_name); + + // For the given pref_name, fire any observer of the pref. + void FireObservers(const wchar_t* pref_name); + + // For the given pref_name, fire any observer of the pref only if |old_value| + // is different from the current value. + void FireObserversIfChanged(const wchar_t* pref_name, + const Value* old_value); + + // Serializes stored data to string. |output| is modified only + // if serialization was successful. Returns true on success. + bool SerializePrefData(std::string* output) const; + + scoped_ptr persistent_; + scoped_ptr transient_; + + // Helper for safe writing pref data. + ImportantFileWriter writer_; + + // A set of all the registered Preference objects. + PreferenceSet prefs_; + + // A map from pref names to a list of observers. Observers get fired in the + // order they are added. + typedef ObserverList NotificationObserverList; + typedef base::hash_map + PrefObserverMap; + PrefObserverMap pref_observers_; + + DISALLOW_COPY_AND_ASSIGN(PrefService); +}; + +#endif // CHROME_COMMON_PREF_SERVICE_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_service_uitest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_service_uitest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_service_uitest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_service_uitest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,134 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include + +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/values.h" +#include "build/build_config.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/common/json_value_serializer.h" +#include "chrome/common/pref_names.h" +#include "chrome/test/automation/browser_proxy.h" +#include "chrome/test/automation/window_proxy.h" +#include "chrome/test/ui/ui_test.h" + +class PreferenceServiceTest : public UITest { +public: + void SetUp() { + PathService::Get(base::DIR_TEMP, &tmp_profile_); + tmp_profile_ = tmp_profile_.AppendASCII("tmp_profile"); + + // Create a fresh, empty copy of this directory. + file_util::Delete(tmp_profile_, true); + file_util::CreateDirectory(tmp_profile_); + + FilePath reference_pref_file = + test_data_directory_ + .AppendASCII("profiles") + .AppendASCII("window_placement") + .Append(chrome::kLocalStateFilename); + + tmp_pref_file_ = tmp_profile_.Append(chrome::kLocalStateFilename); + + ASSERT_TRUE(file_util::PathExists(reference_pref_file)); + + // Copy only the Local State file, the rest will be automatically created + ASSERT_TRUE(file_util::CopyFile(reference_pref_file, tmp_pref_file_)); + +#if defined(OS_WIN) + // Make the copy writable. On POSIX we assume the umask allows files + // we create to be writable. + ASSERT_TRUE(::SetFileAttributesW(tmp_pref_file_.value().c_str(), + FILE_ATTRIBUTE_NORMAL)); +#endif + + launch_arguments_.AppendSwitchWithValue(switches::kUserDataDir, + tmp_profile_.ToWStringHack()); + } + + bool LaunchAppWithProfile() { + if (!file_util::PathExists(tmp_pref_file_)) + return false; + UITest::SetUp(); + return true; + } + + void TearDown() { + UITest::TearDown(); + + EXPECT_TRUE(DieFileDie(tmp_profile_, true)); + } + +public: + FilePath tmp_pref_file_; + FilePath tmp_profile_; +}; + +#if defined(OS_WIN) +// This test verifies that the window position from the prefs file is restored +// when the app restores. This doesn't really make sense on Linux, where +// the window manager might fight with you over positioning. However, we +// might be able to make this work on buildbots. +// Also, not sure what should happen on the mac. In any case, the code below +// (minus the Windows bits) compiles fine on my Linux box now. +// TODO(port): revisit this. +TEST_F(PreferenceServiceTest, PreservedWindowPlacementIsLoaded) { + // The window should open with the reference profile + ASSERT_TRUE(LaunchAppWithProfile()); + + ASSERT_TRUE(file_util::PathExists(tmp_pref_file_)); + + JSONFileValueSerializer deserializer(tmp_pref_file_); + scoped_ptr root(deserializer.Deserialize(NULL)); + + ASSERT_TRUE(root.get()); + ASSERT_TRUE(root->IsType(Value::TYPE_DICTIONARY)); + + DictionaryValue* root_dict = static_cast(root.get()); + + // Retrieve the screen rect for the launched window + scoped_ptr browser(automation()->GetBrowserWindow(0)); + ASSERT_TRUE(browser.get()); + scoped_ptr window(browser->GetWindow()); + + HWND hWnd; + ASSERT_TRUE(window->GetHWND(&hWnd)); + + WINDOWPLACEMENT window_placement; + ASSERT_TRUE(GetWindowPlacement(hWnd, &window_placement)); + + // Retrieve the expected rect values from "Preferences" + int bottom = 0; + std::wstring kBrowserWindowPlacement(prefs::kBrowserWindowPlacement); + EXPECT_TRUE(root_dict->GetInteger(kBrowserWindowPlacement + L".bottom", + &bottom)); + EXPECT_EQ(bottom, window_placement.rcNormalPosition.bottom); + + int top = 0; + EXPECT_TRUE(root_dict->GetInteger(kBrowserWindowPlacement + L".top", + &top)); + EXPECT_EQ(top, window_placement.rcNormalPosition.top); + + int left = 0; + EXPECT_TRUE(root_dict->GetInteger(kBrowserWindowPlacement + L".left", + &left)); + EXPECT_EQ(left, window_placement.rcNormalPosition.left); + + int right = 0; + EXPECT_TRUE(root_dict->GetInteger(kBrowserWindowPlacement + L".right", + &right)); + EXPECT_EQ(right, window_placement.rcNormalPosition.right); + + // Find if launched window is maximized + bool is_window_maximized = (window_placement.showCmd == SW_MAXIMIZE); + + bool is_maximized = false; + EXPECT_TRUE(root_dict->GetBoolean(kBrowserWindowPlacement + L".maximized", + &is_maximized)); + EXPECT_EQ(is_maximized, is_window_maximized); +} +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_service_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_service_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_service_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/pref_service_unittest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,405 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_util.h" +#include "base/path_service.h" +#include "chrome/common/chrome_paths.h" +#include "chrome/common/json_value_serializer.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/notification_type.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/pref_service.h" +#include "chrome/test/data/resource.h" +#include "testing/gtest/include/gtest/gtest.h" + +class PrefServiceTest : public testing::Test { + protected: + virtual void SetUp() { + // Name a subdirectory of the temp directory. + ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_)); + test_dir_ = test_dir_.AppendASCII("PrefServiceTest"); + + // Create a fresh, empty copy of this directory. + file_util::Delete(test_dir_, true); + file_util::CreateDirectory(test_dir_); + + ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &data_dir_)); + data_dir_ = data_dir_.AppendASCII("pref_service"); + ASSERT_TRUE(file_util::PathExists(data_dir_)); + } + virtual void TearDown() { + // Clean up test directory + ASSERT_TRUE(file_util::Delete(test_dir_, true)); + ASSERT_FALSE(file_util::PathExists(test_dir_)); + } + + // the path to temporary directory used to contain the test operations + FilePath test_dir_; + // the path to the directory where the test data is stored + FilePath data_dir_; +}; + +class TestPrefObserver : public NotificationObserver { + public: + TestPrefObserver(const PrefService* prefs, const std::wstring& pref_name, + const std::wstring& new_pref_value) + : observer_fired_(false), + prefs_(prefs), + pref_name_(pref_name), + new_pref_value_(new_pref_value) { + } + virtual ~TestPrefObserver() {} + + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + EXPECT_EQ(type.value, NotificationType::PREF_CHANGED); + PrefService* prefs_in = Source(source).ptr(); + EXPECT_EQ(prefs_in, prefs_); + std::wstring* pref_name_in = Details(details).ptr(); + EXPECT_EQ(*pref_name_in, pref_name_); + EXPECT_EQ(new_pref_value_, prefs_in->GetString(L"homepage")); + observer_fired_ = true; + } + + bool observer_fired() { return observer_fired_; } + + void Reset(const std::wstring& new_pref_value) { + observer_fired_ = false; + new_pref_value_ = new_pref_value; + } + + private: + bool observer_fired_; + const PrefService* prefs_; + const std::wstring pref_name_; + std::wstring new_pref_value_; +}; + +TEST_F(PrefServiceTest, Basic) { + { + // Test that it fails on nonexistent file. + FilePath bogus_input_file = data_dir_.AppendASCII("read.txt"); + PrefService prefs(bogus_input_file, NULL); + EXPECT_FALSE(prefs.ReloadPersistentPrefs()); + } + + ASSERT_TRUE(file_util::CopyFile(data_dir_.AppendASCII("read.json"), + test_dir_.AppendASCII("write.json"))); + + // Test that the persistent value can be loaded. + FilePath input_file = test_dir_.AppendASCII("write.json"); + ASSERT_TRUE(file_util::PathExists(input_file)); + PrefService prefs(input_file, NULL); + ASSERT_TRUE(prefs.ReloadPersistentPrefs()); + + // Register test prefs. + const wchar_t kNewWindowsInTabs[] = L"tabs.new_windows_in_tabs"; + const wchar_t kMaxTabs[] = L"tabs.max_tabs"; + const wchar_t kLongIntPref[] = L"long_int.pref"; + prefs.RegisterStringPref(prefs::kHomePage, L""); + prefs.RegisterBooleanPref(kNewWindowsInTabs, false); + prefs.RegisterIntegerPref(kMaxTabs, 0); + prefs.RegisterStringPref(kLongIntPref, L"2147483648"); + + std::wstring microsoft(L"http://www.microsoft.com"); + std::wstring cnn(L"http://www.cnn.com"); + std::wstring homepage(L"http://www.example.com"); + + EXPECT_EQ(cnn, prefs.GetString(prefs::kHomePage)); + + const wchar_t kSomeDirectory[] = L"some_directory"; + FilePath some_path(FILE_PATH_LITERAL("/usr/sbin/")); + prefs.RegisterFilePathPref(kSomeDirectory, FilePath()); + + // Now test that the transient value overrides the persistent value, + // without actually altering the persistent store. + EXPECT_TRUE(prefs.transient()->SetString(prefs::kHomePage, microsoft)); + EXPECT_TRUE(prefs.transient()->GetString(prefs::kHomePage, &homepage)); + EXPECT_EQ(microsoft, homepage); + + EXPECT_EQ(microsoft, prefs.GetString(prefs::kHomePage)); + + // Test reading some other data types from sub-dictionaries, and + // writing to the persistent store. + EXPECT_TRUE(prefs.GetBoolean(kNewWindowsInTabs)); + prefs.SetBoolean(kNewWindowsInTabs, false); + EXPECT_FALSE(prefs.GetBoolean(kNewWindowsInTabs)); + + EXPECT_EQ(20, prefs.GetInteger(kMaxTabs)); + prefs.SetInteger(kMaxTabs, 10); + EXPECT_EQ(10, prefs.GetInteger(kMaxTabs)); + + EXPECT_EQ(2147483648LL, prefs.GetInt64(kLongIntPref)); + prefs.SetInt64(kLongIntPref, 214748364842LL); + EXPECT_EQ(214748364842LL, prefs.GetInt64(kLongIntPref)); + + EXPECT_EQ(FilePath::StringType(FILE_PATH_LITERAL("/usr/local/")), + prefs.GetFilePath(kSomeDirectory).value()); + prefs.SetFilePath(kSomeDirectory, some_path); + EXPECT_EQ(some_path.value(), prefs.GetFilePath(kSomeDirectory).value()); + + // Serialize and compare to expected output. + FilePath output_file = test_dir_.AppendASCII("write.json"); + FilePath golden_output_file = data_dir_.AppendASCII("write.golden.json"); + ASSERT_TRUE(file_util::PathExists(golden_output_file)); + ASSERT_TRUE(prefs.SavePersistentPrefs()); + EXPECT_TRUE(file_util::ContentsEqual(golden_output_file, output_file)); + ASSERT_TRUE(file_util::Delete(output_file, false)); +} + +TEST_F(PrefServiceTest, Overlay) { + const std::string transient = + "{\"bool\":true, \"int\":2, \"real\":2.0, \"string\":\"transient\"," + "\"dictionary\":{\"value\":\"transient\"}," + "\"list\":[\"transient\"]}"; + + std::wstring persistent_string(L"persistent"); + std::wstring transient_string(L"transient"); + + FilePath persistent_file = data_dir_.AppendASCII("overlay.json"); + PrefService prefs(persistent_file, NULL); + EXPECT_TRUE(prefs.ReloadPersistentPrefs()); + + Value* transient_value; + { + JSONStringValueSerializer serializer(transient); + transient_value = serializer.Deserialize(NULL); + ASSERT_TRUE(transient_value); + } + prefs.transient()->Set(transient_string, transient_value); + + Value* both_transient_value; + { + JSONStringValueSerializer serializer(transient); + both_transient_value = serializer.Deserialize(NULL); + ASSERT_TRUE(both_transient_value); + } + prefs.transient()->Set(L"both", both_transient_value); + + // Register test prefs + static const std::wstring kTypes[] = + { L"neither.", L"persistent.", L"transient.", L"both." }; + for (size_t i = 0; i < arraysize(kTypes); ++i) { + prefs.RegisterBooleanPref((kTypes[i] + L"bool").c_str(), false); + prefs.RegisterIntegerPref((kTypes[i] + L"int").c_str(), 0); + prefs.RegisterRealPref((kTypes[i] + L"real").c_str(), 0.0); + prefs.RegisterStringPref((kTypes[i] + L"string").c_str(), L""); + prefs.RegisterListPref((kTypes[i] + L"list").c_str()); + prefs.RegisterDictionaryPref((kTypes[i] + L"dictionary").c_str()); + } + + ASSERT_FALSE(prefs.GetBoolean(L"neither.bool")); + ASSERT_FALSE(prefs.GetBoolean(L"persistent.bool")); + ASSERT_TRUE(prefs.GetBoolean(L"transient.bool")); + ASSERT_TRUE(prefs.GetBoolean(L"both.bool")); + + ASSERT_EQ(0, prefs.GetInteger(L"neither.int")); + ASSERT_EQ(1, prefs.GetInteger(L"persistent.int")); + ASSERT_EQ(2, prefs.GetInteger(L"transient.int")); + ASSERT_EQ(2, prefs.GetInteger(L"both.int")); + + ASSERT_EQ(0.0, prefs.GetReal(L"neither.real")); + ASSERT_EQ(1.0, prefs.GetReal(L"persistent.real")); + ASSERT_EQ(2.0, prefs.GetReal(L"transient.real")); + ASSERT_EQ(2.0, prefs.GetReal(L"both.real")); + + ASSERT_EQ(std::wstring(), prefs.GetString(L"neither.string")); + ASSERT_EQ(persistent_string, prefs.GetString(L"persistent.string")); + ASSERT_EQ(transient_string, prefs.GetString(L"transient.string")); + ASSERT_EQ(transient_string, prefs.GetString(L"both.string")); + + { + const DictionaryValue* result_value = + prefs.GetDictionary(L"neither.dictionary"); + ASSERT_FALSE(result_value); + } + + { + const DictionaryValue* result_value = + prefs.GetDictionary(L"persistent.dictionary"); + ASSERT_TRUE(result_value); + std::wstring result_string; + ASSERT_TRUE(result_value->GetString(L"value", &result_string)); + ASSERT_EQ(persistent_string, result_string); + } + + { + const DictionaryValue* result_value = + prefs.GetDictionary(L"transient.dictionary"); + ASSERT_TRUE(result_value); + std::wstring result_string; + ASSERT_TRUE(result_value->GetString(L"value", &result_string)); + ASSERT_EQ(transient_string, result_string); + } + + { + const DictionaryValue* result_value = + prefs.GetDictionary(L"both.dictionary"); + ASSERT_TRUE(result_value); + std::wstring result_string; + ASSERT_TRUE(result_value->GetString(L"value", &result_string)); + ASSERT_EQ(transient_string, result_string); + } + + { + const ListValue* result_value = prefs.GetList(L"neither.list"); + ASSERT_FALSE(result_value); + } + + { + const ListValue* result_value = prefs.GetList(L"persistent.list"); + ASSERT_TRUE(result_value); + Value* member_value; + ASSERT_TRUE(result_value->Get(0, &member_value)); + ASSERT_TRUE(member_value); + std::wstring result_string; + ASSERT_TRUE(member_value->GetAsString(&result_string)); + ASSERT_EQ(persistent_string, result_string); + } + + { + const ListValue* result_value = prefs.GetList(L"transient.list"); + ASSERT_TRUE(result_value); + Value* member_value; + ASSERT_TRUE(result_value->Get(0, &member_value)); + ASSERT_TRUE(member_value); + std::wstring result_string; + ASSERT_TRUE(member_value->GetAsString(&result_string)); + ASSERT_EQ(transient_string, result_string); + } + + { + const ListValue* result_value = prefs.GetList(L"both.list"); + ASSERT_TRUE(result_value); + Value* member_value; + ASSERT_TRUE(result_value->Get(0, &member_value)); + ASSERT_TRUE(member_value); + std::wstring result_string; + ASSERT_TRUE(member_value->GetAsString(&result_string)); + ASSERT_EQ(transient_string, result_string); + } +} + +TEST_F(PrefServiceTest, Observers) { + FilePath input_file = data_dir_.AppendASCII("read.json"); + EXPECT_TRUE(file_util::PathExists(input_file)); + + PrefService prefs(input_file, NULL); + + EXPECT_TRUE(prefs.ReloadPersistentPrefs()); + + const wchar_t pref_name[] = L"homepage"; + prefs.RegisterStringPref(pref_name, L""); + EXPECT_EQ(std::wstring(L"http://www.cnn.com"), prefs.GetString(pref_name)); + + const std::wstring new_pref_value(L"http://www.google.com/"); + TestPrefObserver obs(&prefs, pref_name, new_pref_value); + prefs.AddPrefObserver(pref_name, &obs); + // This should fire the checks in TestPrefObserver::Observe. + prefs.SetString(pref_name, new_pref_value); + + // Make sure the tests were actually run. + EXPECT_TRUE(obs.observer_fired()); + + // Now try adding a second pref observer. + const std::wstring new_pref_value2(L"http://www.youtube.com/"); + obs.Reset(new_pref_value2); + TestPrefObserver obs2(&prefs, pref_name, new_pref_value2); + prefs.AddPrefObserver(pref_name, &obs2); + // This should fire the checks in obs and obs2. + prefs.SetString(pref_name, new_pref_value2); + EXPECT_TRUE(obs.observer_fired()); + EXPECT_TRUE(obs2.observer_fired()); + + // Make sure obs2 still works after removing obs. + prefs.RemovePrefObserver(pref_name, &obs); + obs.Reset(L""); + obs2.Reset(new_pref_value); + // This should only fire the observer in obs2. + prefs.SetString(pref_name, new_pref_value); + EXPECT_FALSE(obs.observer_fired()); + EXPECT_TRUE(obs2.observer_fired()); + + // Ok, clean up. + prefs.RemovePrefObserver(pref_name, &obs2); +} + +// TODO(port): port this test to POSIX. +#if defined(OS_WIN) +TEST_F(PrefServiceTest, LocalizedPrefs) { + PrefService prefs(FilePath(), NULL); + const wchar_t kBoolean[] = L"boolean"; + const wchar_t kInteger[] = L"integer"; + const wchar_t kString[] = L"string"; + prefs.RegisterLocalizedBooleanPref(kBoolean, IDS_LOCALE_BOOL); + prefs.RegisterLocalizedIntegerPref(kInteger, IDS_LOCALE_INT); + prefs.RegisterLocalizedStringPref(kString, IDS_LOCALE_STRING); + + // The locale default should take preference over the user default. + EXPECT_FALSE(prefs.GetBoolean(kBoolean)); + EXPECT_EQ(1, prefs.GetInteger(kInteger)); + EXPECT_EQ(L"hello", prefs.GetString(kString)); + + prefs.SetBoolean(kBoolean, true); + EXPECT_TRUE(prefs.GetBoolean(kBoolean)); + prefs.SetInteger(kInteger, 5); + EXPECT_EQ(5, prefs.GetInteger(kInteger)); + prefs.SetString(kString, L"foo"); + EXPECT_EQ(L"foo", prefs.GetString(kString)); +} +#endif + +TEST_F(PrefServiceTest, NoObserverFire) { + PrefService prefs(FilePath(), NULL); + + const wchar_t pref_name[] = L"homepage"; + prefs.RegisterStringPref(pref_name, L""); + + const std::wstring new_pref_value(L"http://www.google.com/"); + TestPrefObserver obs(&prefs, pref_name, new_pref_value); + prefs.AddPrefObserver(pref_name, &obs); + // This should fire the checks in TestPrefObserver::Observe. + prefs.SetString(pref_name, new_pref_value); + + // Make sure the observer was actually fired. + EXPECT_TRUE(obs.observer_fired()); + + // Setting the pref to the same value should not set the pref value a second + // time. + obs.Reset(new_pref_value); + prefs.SetString(pref_name, new_pref_value); + EXPECT_FALSE(obs.observer_fired()); + + // Clearing the pref should cause the pref to fire. + obs.Reset(L""); + prefs.ClearPref(pref_name); + EXPECT_TRUE(obs.observer_fired()); + + // Clearing the pref again should not cause the pref to fire. + obs.Reset(L""); + prefs.ClearPref(pref_name); + EXPECT_FALSE(obs.observer_fired()); + + // Ok, clean up. + prefs.RemovePrefObserver(pref_name, &obs); +} + +TEST_F(PrefServiceTest, HasPrefPath) { + PrefService prefs(FilePath(), NULL); + + const wchar_t path[] = L"fake.path"; + + // Shouldn't initially have a path. + EXPECT_FALSE(prefs.HasPrefPath(path)); + + // Register the path. This doesn't set a value, so the path still shouldn't + // exist. + prefs.RegisterStringPref(path, std::wstring()); + EXPECT_FALSE(prefs.HasPrefPath(path)); + + // Set a value and make sure we have a path. + prefs.SetString(path, L"blah"); + EXPECT_TRUE(prefs.HasPrefPath(path)); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/process_watcher.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/process_watcher.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/process_watcher.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/process_watcher.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,39 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_PROCESS_WATCHER_H_ +#define CHROME_COMMON_PROCESS_WATCHER_H_ + +#include "base/basictypes.h" +#include "base/process_util.h" + +class ProcessWatcher { + public: + // This method ensures that the specified process eventually terminates, and + // then it closes the given process handle. + // + // It assumes that the process has already been signalled to exit, and it + // begins by waiting a small amount of time for it to exit. If the process + // does not appear to have exited, then this function starts to become + // aggressive about ensuring that the process terminates. + // + // This method does not block the calling thread. + // + // NOTE: The process handle must have been opened with the PROCESS_TERMINATE + // and SYNCHRONIZE permissions. + // + static void EnsureProcessTerminated(base::ProcessHandle process_handle +#if defined(CHROMIUM_MOZILLA_BUILD) + , bool force=true +#endif + ); + + private: + // Do not instantiate this class. + ProcessWatcher(); + + DISALLOW_COPY_AND_ASSIGN(ProcessWatcher); +}; + +#endif // CHROME_COMMON_PROCESS_WATCHER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/process_watcher_posix.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/process_watcher_posix.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/process_watcher_posix.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/process_watcher_posix.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,77 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/process_watcher.h" + +#include +#include +#include +#include + +#include "base/eintr_wrapper.h" +#include "base/platform_thread.h" + +// Return true if the given child is dead. This will also reap the process. +// Doesn't block. +static bool IsChildDead(pid_t child) { + const int result = HANDLE_EINTR(waitpid(child, NULL, WNOHANG)); + if (result == -1) { + NOTREACHED(); + } else if (result > 0) { + // The child has died. + return true; + } + + return false; +} + +// A thread class which waits for the given child to exit and reaps it. +// If the child doesn't exit within a couple of seconds, kill it. +class BackgroundReaper : public PlatformThread::Delegate { + public: + explicit BackgroundReaper(pid_t child) + : child_(child) { + } + + void ThreadMain() { + WaitForChildToDie(); + delete this; + } + + void WaitForChildToDie() { + // There's no good way to wait for a specific child to exit in a timed + // fashion. (No kqueue on Linux), so we just loop and sleep. + + // Waits 0.5 * 4 = 2 seconds. + for (unsigned i = 0; i < 4; ++i) { + PlatformThread::Sleep(500); // 0.5 seconds + if (IsChildDead(child_)) + return; + } + + if (kill(child_, SIGKILL) == 0) { + // SIGKILL is uncatchable. Since the signal was delivered, we can + // just wait for the process to die now in a blocking manner. + HANDLE_EINTR(waitpid(child_, NULL, 0)); + } else { + LOG(ERROR) << "While waiting for " << child_ << " to terminate we" + << " failed to deliver a SIGKILL signal (" << errno << ")."; + } + } + + private: + const pid_t child_; + + DISALLOW_COPY_AND_ASSIGN(BackgroundReaper); +}; + +// static +void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process) { + // If the child is already dead, then there's nothing to do + if (IsChildDead(process)) + return; + + BackgroundReaper* reaper = new BackgroundReaper(process); + PlatformThread::CreateNonJoinable(0, reaper); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/process_watcher_posix_sigchld.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,259 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 et sw=2 tw=80: + */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla IPC. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Chris Jones + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +//----------------------------------------------------------------------------- +// XXXXXXXXXXXXXXXX +// +// How is this code supposed to be licensed? I don't /think/ that +// this code is doing anything different than, say, +// GeckoChildProcess.h/cpp, so I /think/ this gets a MoFo copyright +// and license. Yes? +// +// XXXXXXXXXXXXXXXX +//----------------------------------------------------------------------------- + +#include +#include +#include +#include + +#include "base/eintr_wrapper.h" +#include "base/message_loop.h" +#include "base/process_util.h" + +#include "chrome/common/process_watcher.h" + +// Maximum amount of time (in milliseconds) to wait for the process to exit. +// XXX/cjones: fairly arbitrary, chosen to match process_watcher_win.cc +static const int kMaxWaitMs = 2000; + +namespace { + +bool +IsProcessDead(pid_t process) +{ + bool exited = false; + // don't care if the process crashed, just if it exited + base::DidProcessCrash(&exited, process); + return exited; +} + + +class ChildReaper : public base::MessagePumpLibevent::SignalEvent, + public base::MessagePumpLibevent::SignalWatcher +{ +public: + explicit ChildReaper(pid_t process) : process_(process) + { + } + + virtual ~ChildReaper() + { + // subclasses should have cleaned up |process_| already + DCHECK(!process_); + + // StopCatching() is implicit + } + + // @override + virtual void OnSignal(int sig) + { + DCHECK(SIGCHLD == sig); + DCHECK(process_); + + // this may be the SIGCHLD for a process other than |process_| + if (IsProcessDead(process_)) { + process_ = 0; + StopCatching(); + } + } + +protected: + void WaitForChildExit() + { + DCHECK(process_); + HANDLE_EINTR(waitpid(process_, NULL, 0)); + } + + pid_t process_; + +private: + DISALLOW_EVIL_CONSTRUCTORS(ChildReaper); +}; + + +// Fear the reaper +class ChildGrimReaper : public ChildReaper, + public Task +{ +public: + explicit ChildGrimReaper(pid_t process) : ChildReaper(process) + { + } + + virtual ~ChildGrimReaper() + { + if (process_) + KillProcess(); + } + + // @override + virtual void Run() + { + // we may have already been signaled by the time this runs + if (process_) + KillProcess(); + } + +private: + void KillProcess() + { + DCHECK(process_); + + if (IsProcessDead(process_)) { + process_ = 0; + return; + } + + if (0 == kill(process_, SIGKILL)) { + // XXX this will block for whatever amount of time it takes the + // XXX OS to tear down the process's resources. might need to + // XXX rethink this if it proves expensive + WaitForChildExit(); + } + else { + LOG(ERROR) << "Failed to deliver SIGKILL to " << process_ << "!" + << "("<< errno << ")."; + } + process_ = 0; + } + + DISALLOW_EVIL_CONSTRUCTORS(ChildGrimReaper); +}; + + +class ChildLaxReaper : public ChildReaper, + public MessageLoop::DestructionObserver +{ +public: + explicit ChildLaxReaper(pid_t process) : ChildReaper(process) + { + } + + virtual ~ChildLaxReaper() + { + // WillDestroyCurrentMessageLoop() should have reaped process_ already + DCHECK(!process_); + } + + // @override + virtual void OnSignal(int sig) + { + ChildReaper::OnSignal(sig); + + if (!process_) { + MessageLoop::current()->RemoveDestructionObserver(this); + delete this; + } + } + + // @override + virtual void WillDestroyCurrentMessageLoop() + { + DCHECK(process_); + + WaitForChildExit(); + process_ = 0; + + // XXX don't think this is necessary, since destruction can only + // be observed once, but can't hurt + MessageLoop::current()->RemoveDestructionObserver(this); + delete this; + } + +private: + DISALLOW_EVIL_CONSTRUCTORS(ChildLaxReaper); +}; + +} // namespace + + +/** + * Do everything possible to ensure that |process| has been reaped + * before this process exits. + * + * |grim| decides how strict to be with the child's shutdown. + * + * | child exit timeout | upon parent shutdown: + * +--------------------+---------------------------------- + * force=true | 2 seconds | kill(child, SIGKILL) + * force=false | infinite | waitpid(child) + * + * If a child process doesn't shut down properly, and |grim=false| + * used, then the parent will wait on the child forever. So, + * |force=false| is expected to be used when an external entity can be + * responsible for terminating hung processes, e.g. automated test + * harnesses. + */ +void +ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process, + bool force) +{ + DCHECK(process != base::GetCurrentProcId()); + DCHECK(process > 0); + + if (IsProcessDead(process)) + return; + + MessageLoopForIO* loop = MessageLoopForIO::current(); + if (force) { + ChildGrimReaper* reaper = new ChildGrimReaper(process); + + loop->CatchSignal(SIGCHLD, reaper, reaper); + // |loop| takes ownership of |reaper| + loop->PostDelayedTask(FROM_HERE, reaper, kMaxWaitMs); + } else { + ChildLaxReaper* reaper = new ChildLaxReaper(process); + + loop->CatchSignal(SIGCHLD, reaper, reaper); + // |reaper| destroys itself after destruction notification + loop->AddDestructionObserver(reaper); + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/process_watcher_win.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/process_watcher_win.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/process_watcher_win.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/process_watcher_win.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,105 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/process_watcher.h" + +#include "base/message_loop.h" +#include "base/object_watcher.h" +#include "base/sys_info.h" +#include "chrome/common/env_vars.h" +#include "chrome/common/result_codes.h" + +// Maximum amount of time (in milliseconds) to wait for the process to exit. +static const int kWaitInterval = 2000; + +namespace { + +class TimerExpiredTask : public Task, public base::ObjectWatcher::Delegate { + public: + explicit TimerExpiredTask(base::ProcessHandle process) : process_(process) { + watcher_.StartWatching(process_, this); + } + + virtual ~TimerExpiredTask() { + if (process_) { + KillProcess(); + DCHECK(!process_) << "Make sure to close the handle."; + } + } + + // Task --------------------------------------------------------------------- + + virtual void Run() { + if (process_) + KillProcess(); + } + + // MessageLoop::Watcher ----------------------------------------------------- + + virtual void OnObjectSignaled(HANDLE object) { + // When we're called from KillProcess, the ObjectWatcher may still be + // watching. the process handle, so make sure it has stopped. + watcher_.StopWatching(); + + CloseHandle(process_); + process_ = NULL; + } + + private: + void KillProcess() { + if (base::SysInfo::HasEnvVar(env_vars::kHeadless)) { + // If running the distributed tests, give the renderer a little time + // to figure out that the channel is shutdown and unwind. + if (WaitForSingleObject(process_, kWaitInterval) == WAIT_OBJECT_0) { + OnObjectSignaled(process_); + return; + } + } + + // OK, time to get frisky. We don't actually care when the process + // terminates. We just care that it eventually terminates, and that's what + // TerminateProcess should do for us. Don't check for the result code since + // it fails quite often. This should be investigated eventually. + TerminateProcess(process_, ResultCodes::HUNG); + + // Now, just cleanup as if the process exited normally. + OnObjectSignaled(process_); + } + + // The process that we are watching. + base::ProcessHandle process_; + + base::ObjectWatcher watcher_; + + DISALLOW_EVIL_CONSTRUCTORS(TimerExpiredTask); +}; + +} // namespace + +// static +void ProcessWatcher::EnsureProcessTerminated(base::ProcessHandle process +#ifdef CHROMIUM_MOZILLA_BUILD + , bool force +#endif +) { + DCHECK(process != GetCurrentProcess()); + +#ifdef CHROMIUM_MOZILLA_BUILD + if (!force) { + WaitForSingleObject(process, INFINITE); + CloseHandle(process); + return; + } +#endif + + // If already signaled, then we are done! + if (WaitForSingleObject(process, 0) == WAIT_OBJECT_0) { + CloseHandle(process); + return; + } + + MessageLoop::current()->PostDelayedTask(FROM_HERE, + new TimerExpiredTask(process), + kWaitInterval); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/property_bag.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/property_bag.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/property_bag.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/property_bag.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,55 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/property_bag.h" + +PropertyBag::PropertyBag() { +} + +PropertyBag::PropertyBag(const PropertyBag& other) { + operator=(other); +} + +PropertyBag::~PropertyBag() { +} + +PropertyBag& PropertyBag::operator=(const PropertyBag& other) { + props_.clear(); + + // We need to make copies of each property using the virtual copy() method. + for (PropertyMap::const_iterator i = other.props_.begin(); + i != other.props_.end(); ++i) + props_[i->first] = linked_ptr(i->second->copy()); + return *this; +} + +void PropertyBag::SetProperty(PropID id, Prop* prop) { + props_[id] = linked_ptr(prop); +} + +PropertyBag::Prop* PropertyBag::GetProperty(PropID id) { + PropertyMap::const_iterator found = props_.find(id); + if (found == props_.end()) + return NULL; + return found->second.get(); +} + +const PropertyBag::Prop* PropertyBag::GetProperty(PropID id) const { + PropertyMap::const_iterator found = props_.find(id); + if (found == props_.end()) + return NULL; + return found->second.get(); +} + +void PropertyBag::DeleteProperty(PropID id) { + PropertyMap::iterator found = props_.find(id); + if (found == props_.end()) + return; // Not found, nothing to do. + props_.erase(found); +} + +PropertyAccessorBase::PropertyAccessorBase() { + static PropertyBag::PropID next_id = 1; + prop_id_ = next_id++; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/property_bag.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/property_bag.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/property_bag.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/property_bag.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,175 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_PROPERTY_BAG_H_ +#define CHROME_COMMON_PROPERTY_BAG_H_ + +#include + +#include "base/basictypes.h" +#include "base/linked_ptr.h" + +class PropertyAccessorBase; + +// A property bag holds a generalized list of arbitrary metadata called +// properties. Each property is a class type derived from PropertyBag::Prop +// that can bet set and retrieved. +// +// The property bag is not read or written directly. Instead, callers go +// through a PropertyAccessor. The Accessor generates the unique IDs that +// identify different properties, so uniquely identify a property. The Accessor +// is templatized to also provide typesafety to the callers. +// +// Example: +// // Note: you don't want to use Singleton for your Accessor if you're using +// // a simple type like int or string as the data, since it will enforce that +// // there is only one singleton for that type, which will conflict. If +// // you're putting in some struct that's unique to you, go ahead. +// PropertyAccessor* my_accessor() const { +// static PropertyAccessor* accessor = NULL; +// if (!accessor) accessor = new PropertyAccessor; +// return accessor; +// } +// +// void doit(SomeObjectThatImplementsPropertyBag* object) { +// PropertyAccessor* accessor = my_accessor(); +// int* property = accessor.GetProperty(object); +// if (property) +// ... use property ... +// +// accessor.SetProperty(object, 22); +// } +class PropertyBag { + public: + // The type that uniquely identifies a property type. + typedef int PropID; + enum { NULL_PROP_ID = -1 }; // Invalid property ID. + + // Properties are all derived from this class. They must be deletable and + // copyable + class Prop { + public: + virtual ~Prop() {} + + // Copies the property and returns a pointer to the new one. The caller is + // responsible for managing the lifetime. + virtual Prop* copy() = 0; + }; + + PropertyBag(); + PropertyBag(const PropertyBag& other); + virtual ~PropertyBag(); + + PropertyBag& operator=(const PropertyBag& other); + + private: + friend class PropertyAccessorBase; + + typedef std::map > PropertyMap; + + // Used by the PropertyAccessor to set the property with the given ID. + // Ownership of the given pointer will be transferred to us. Any existing + // property matching the given ID will be deleted. + void SetProperty(PropID id, Prop* prop); + + // Used by the PropertyAccessor to retrieve the property with the given ID. + // The returned pointer will be NULL if there is no match. Ownership of the + // pointer will stay with the property bag. + Prop* GetProperty(PropID id); + const Prop* GetProperty(PropID id) const; + + // Deletes the property with the given ID from the bag if it exists. + void DeleteProperty(PropID id); + + PropertyMap props_; + + // Copy and assign is explicitly allowed for this class. +}; + +// PropertyAccessorBase ------------------------------------------------------- + +// Manages getting the unique IDs to identify a property. Callers should use +// PropertyAccessor below instead. +class PropertyAccessorBase { + public: + PropertyAccessorBase(); + virtual ~PropertyAccessorBase() {} + + // Removes our property, if any, from the given property bag. + void DeleteProperty(PropertyBag* bag) { + bag->DeleteProperty(prop_id_); + } + + protected: + void SetPropertyInternal(PropertyBag* bag, PropertyBag::Prop* prop) { + bag->SetProperty(prop_id_, prop); + } + PropertyBag::Prop* GetPropertyInternal(PropertyBag* bag) { + return bag->GetProperty(prop_id_); + } + const PropertyBag::Prop* GetPropertyInternal(const PropertyBag* bag) const { + return bag->GetProperty(prop_id_); + } + + private: + // Identifier for this property. + PropertyBag::PropID prop_id_; + + DISALLOW_COPY_AND_ASSIGN(PropertyAccessorBase); +}; + +// PropertyAccessor ----------------------------------------------------------- + +// Provides typesafe accessor functions for a property bag, and manages the +// unique identifiers for properties via the PropertyAccessorBase. +// +// Note that class T must be derived from PropertyBag::Prop. +template +class PropertyAccessor : public PropertyAccessorBase { + public: + PropertyAccessor() : PropertyAccessorBase() {} + virtual ~PropertyAccessor() {} + + // Takes ownership of the |prop| pointer. + void SetProperty(PropertyBag* bag, const T& prop) { + SetPropertyInternal(bag, new Container(prop)); + } + + // Returns our property in the given bag or NULL if there is no match. The + // returned pointer's ownership will stay with the property bag. + T* GetProperty(PropertyBag* bag) { + PropertyBag::Prop* prop = GetPropertyInternal(bag); + if (!prop) + return NULL; + return static_cast(prop)->get(); + } + const T* GetProperty(const PropertyBag* bag) const { + const PropertyBag::Prop* prop = GetPropertyInternal(bag); + if (!prop) + return NULL; + return static_cast(prop)->get(); + } + + // See also DeleteProperty on thn PropertyAccessorBase. + + private: + class Container : public PropertyBag::Prop { + public: + Container(const T& data) : data_(data) {} + + T* get() { return &data_; } + const T* get() const { return &data_; } + + private: + virtual Prop* copy() { + return new Container(data_); + } + + T data_; + }; + + DISALLOW_COPY_AND_ASSIGN(PropertyAccessor); +}; + +#endif // CHROME_COMMON_PROPERTY_BAG_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/property_bag_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/property_bag_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/property_bag_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/property_bag_unittest.cc 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,62 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/property_bag.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(PropertyBagTest, AddQueryRemove) { + PropertyBag bag; + PropertyAccessor adaptor; + + // Should be no match initially. + EXPECT_EQ(NULL, adaptor.GetProperty(&bag)); + + // Add the value and make sure we get it back. + const int kFirstValue = 1; + adaptor.SetProperty(&bag, kFirstValue); + ASSERT_TRUE(adaptor.GetProperty(&bag)); + EXPECT_EQ(kFirstValue, *adaptor.GetProperty(&bag)); + + // Set it to a new value. + const int kSecondValue = 2; + adaptor.SetProperty(&bag, kSecondValue); + ASSERT_TRUE(adaptor.GetProperty(&bag)); + EXPECT_EQ(kSecondValue, *adaptor.GetProperty(&bag)); + + // Remove the value and make sure it's gone. + adaptor.DeleteProperty(&bag); + EXPECT_EQ(NULL, adaptor.GetProperty(&bag)); +} + +TEST(PropertyBagTest, Copy) { + PropertyAccessor adaptor1; + PropertyAccessor adaptor2; + + // Create a bag with property type 1 in it. + PropertyBag copy; + adaptor1.SetProperty(©, 22); + + const int kType1Value = 10; + const double kType2Value = 2.7; + { + // Create a bag with property types 1 and 2 in it. + PropertyBag initial; + adaptor1.SetProperty(&initial, kType1Value); + adaptor2.SetProperty(&initial, kType2Value); + + // Assign to the original. + copy = initial; + } + + // Verify the copy got the two properties. + ASSERT_TRUE(adaptor1.GetProperty(©)); + ASSERT_TRUE(adaptor2.GetProperty(©)); + EXPECT_EQ(kType1Value, *adaptor1.GetProperty(©)); + EXPECT_EQ(kType2Value, *adaptor2.GetProperty(©)); + + // Clear it out, neither property should be left. + copy = PropertyBag(); + EXPECT_EQ(NULL, adaptor1.GetProperty(©)); + EXPECT_EQ(NULL, adaptor2.GetProperty(©)); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/quarantine_mac.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/quarantine_mac.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/quarantine_mac.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/quarantine_mac.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,22 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_QUARANTINE_MAC_H_ +#define CHROME_COMMON_QUARANTINE_MAC_H_ + +class FilePath; +class GURL; + +namespace quarantine_mac { + +// Adds quarantine metadata to the file, assuming it has already been +// quarantined by the OS. +// |source| should be the source URL for the download, and |referrer| should be +// the URL the user initiated the download from. +void AddQuarantineMetadataToFile(const FilePath& file, const GURL& source, + const GURL& referrer); + +} // namespace quarantine_mac + +#endif // CHROME_COMMON_QUARANTINE_MAC_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/quarantine_mac.mm firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/quarantine_mac.mm --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/quarantine_mac.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/quarantine_mac.mm 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,90 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/common/quarantine_mac.h" + +#include +#import + +#include "base/file_path.h" +#include "base/logging.h" +#include "base/mac_util.h" +#include "googleurl/src/gurl.h" + +namespace quarantine_mac { + +// The OS will automatically quarantine files due to the +// LSFileQuarantineEnabled entry in our Info.plist, but it knows relatively +// little about the files. We add more information about the download to +// improve the UI shown by the OS when the users tries to open the file. +void AddQuarantineMetadataToFile(const FilePath& file, const GURL& source, + const GURL& referrer) { + FSRef file_ref; + if (!mac_util::FSRefFromPath(file.value(), &file_ref)) + return; + + NSMutableDictionary* quarantine_properties = nil; + CFTypeRef quarantine_properties_base = NULL; + if (::LSCopyItemAttribute(&file_ref, kLSRolesAll, kLSItemQuarantineProperties, + &quarantine_properties_base) == noErr) { + if (::CFGetTypeID(quarantine_properties_base) == + ::CFDictionaryGetTypeID()) { + // Quarantine properties will already exist if LSFileQuarantineEnabled + // is on and the file doesn't match an exclusion. + quarantine_properties = + [[(NSDictionary*)quarantine_properties_base mutableCopy] autorelease]; + } else { + LOG(WARNING) << "kLSItemQuarantineProperties is not a dictionary on file " + << file.value(); + } + ::CFRelease(quarantine_properties_base); + } + + if (!quarantine_properties) { + // If there are no quarantine properties, then the file isn't quarantined + // (e.g., because the user has set up exclusions for certain file types). + // We don't want to add any metadata, because that will cause the file to + // be quarantined against the user's wishes. + return; + } + + // kLSQuarantineAgentNameKey, kLSQuarantineAgentBundleIdentifierKey, and + // kLSQuarantineTimeStampKey are set for us (see LSQuarantine.h), so we only + // need to set the values that the OS can't infer. + + if (![quarantine_properties valueForKey:(NSString*)kLSQuarantineTypeKey]) { + CFStringRef type = (source.SchemeIs("http") || source.SchemeIs("https")) + ? kLSQuarantineTypeWebDownload + : kLSQuarantineTypeOtherDownload; + [quarantine_properties setValue:(NSString*)type + forKey:(NSString*)kLSQuarantineTypeKey]; + } + + if (![quarantine_properties + valueForKey:(NSString*)kLSQuarantineOriginURLKey] && + referrer.is_valid()) { + NSString* referrer_url = + [NSString stringWithUTF8String:referrer.spec().c_str()]; + [quarantine_properties setValue:referrer_url + forKey:(NSString*)kLSQuarantineOriginURLKey]; + } + + if (![quarantine_properties valueForKey:(NSString*)kLSQuarantineDataURLKey] && + source.is_valid()) { + NSString* origin_url = + [NSString stringWithUTF8String:source.spec().c_str()]; + [quarantine_properties setValue:origin_url + forKey:(NSString*)kLSQuarantineDataURLKey]; + } + + OSStatus os_error = ::LSSetItemAttribute(&file_ref, kLSRolesAll, + kLSItemQuarantineProperties, + quarantine_properties); + if (os_error != noErr) { + LOG(WARNING) << "Unable to set quarantine attributes on file " + << file.value(); + } +} + +} // namespace quarantine_mac diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ref_counted_util.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ref_counted_util.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/ref_counted_util.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/ref_counted_util.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,30 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_REF_COUNTED_UTIL_H__ +#define CHROME_COMMON_REF_COUNTED_UTIL_H__ + +#include "base/ref_counted.h" +#include + +// RefCountedVector is just a vector wrapped up with +// RefCountedThreadSafe. +template +class RefCountedVector : + public base::RefCountedThreadSafe > { + public: + RefCountedVector() {} + RefCountedVector(const std::vector& initializer) + : data(initializer) {} + + std::vector data; + + DISALLOW_EVIL_CONSTRUCTORS(RefCountedVector); +}; + +// RefCountedThreadSafeBytes represent a ref-counted blob of bytes. +// Useful for passing data between threads without copying. +typedef RefCountedVector RefCountedBytes; + +#endif // CHROME_COMMON_REF_COUNTED_UTIL_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/render_messages.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/render_messages.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/render_messages.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/render_messages.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,1881 @@ +// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_COMMON_RENDER_MESSAGES_H_ +#define CHROME_COMMON_RENDER_MESSAGES_H_ + +#include +#include +#include + +#include "base/basictypes.h" +#include "base/gfx/native_widget_types.h" +#include "base/ref_counted.h" +#include "base/shared_memory.h" +#include "chrome/browser/renderer_host/resource_handler.h" +#include "chrome/common/filter_policy.h" +#include "chrome/common/ipc_message_utils.h" +#include "chrome/common/modal_dialog_event.h" +#include "chrome/common/page_transition_types.h" +#include "chrome/common/transport_dib.h" +#include "chrome/common/webkit_param_traits.h" +#include "googleurl/src/gurl.h" +#include "media/audio/audio_output.h" +#include "net/base/upload_data.h" +#include "net/http/http_response_headers.h" +#include "net/url_request/url_request_status.h" +#include "webkit/glue/autofill_form.h" +#include "webkit/glue/context_menu.h" +#include "webkit/glue/form_data.h" +#include "webkit/glue/password_form.h" +#include "webkit/glue/password_form_dom_manager.h" +#include "webkit/glue/resource_loader_bridge.h" +#include "webkit/glue/webaccessibility.h" +#include "webkit/glue/webappcachecontext.h" +#include "webkit/glue/webdropdata.h" +#include "webkit/glue/webplugin.h" +#include "webkit/glue/webplugininfo.h" +#include "webkit/glue/webpreferences.h" +#include "webkit/glue/webview_delegate.h" + +#if defined(OS_POSIX) +#include "skia/include/SkBitmap.h" +#endif + +namespace base { +class Time; +} + +// Parameters structure for ViewMsg_Navigate, which has too many data +// parameters to be reasonably put in a predefined IPC message. +struct ViewMsg_Navigate_Params { + // The page_id for this navigation, or -1 if it is a new navigation. Back, + // Forward, and Reload navigations should have a valid page_id. If the load + // succeeds, then this page_id will be reflected in the resultant + // ViewHostMsg_FrameNavigate message. + int32 page_id; + + // The URL to load. + GURL url; + + // The URL to send in the "Referer" header field. Can be empty if there is + // no referrer. + GURL referrer; + + // The type of transition. + PageTransition::Type transition; + + // Opaque history state (received by ViewHostMsg_UpdateState). + std::string state; + + // Specifies if the URL should be loaded using 'reload' semantics (i.e., + // bypassing any locally cached content). + bool reload; + + // The time the request was created + base::Time request_time; +}; + +// Parameters structure for ViewHostMsg_FrameNavigate, which has too many data +// parameters to be reasonably put in a predefined IPC message. +struct ViewHostMsg_FrameNavigate_Params { + // Page ID of this navigation. The renderer creates a new unique page ID + // anytime a new session history entry is created. This means you'll get new + // page IDs for user actions, and the old page IDs will be reloaded when + // iframes are loaded automatically. + int32 page_id; + + // URL of the page being loaded. + GURL url; + + // URL of the referrer of this load. WebKit generates this based on the + // source of the event that caused the load. + GURL referrer; + + // The type of transition. + PageTransition::Type transition; + + // Lists the redirects that occurred on the way to the current page. This + // vector has the same format as reported by the WebDataSource in the glue, + // with the current page being the last one in the list (so even when + // there's no redirect, there will be one entry in the list. + std::vector redirects; + + // Set to false if we want to update the session history but not update + // the browser history. E.g., on unreachable urls. + bool should_update_history; + + // See SearchableFormData for a description of these. + GURL searchable_form_url; + std::wstring searchable_form_element_name; + std::string searchable_form_encoding; + + // See password_form.h. + PasswordForm password_form; + + // Information regarding the security of the connection (empty if the + // connection was not secure). + std::string security_info; + + // The gesture that initiated this navigation. + NavigationGesture gesture; + + // Contents MIME type of main frame. + std::string contents_mime_type; + + // True if this was a post request. + bool is_post; + + // Whether the content of the frame was replaced with some alternate content + // (this can happen if the resource was insecure). + bool is_content_filtered; + + // The status code of the HTTP request. + int http_status_code; +}; + +// Values that may be OR'd together to form the 'flags' parameter of a +// ViewHostMsg_PaintRect message. +struct ViewHostMsg_PaintRect_Flags { + enum { + IS_RESIZE_ACK = 1 << 0, + IS_RESTORE_ACK = 1 << 1, + IS_REPAINT_ACK = 1 << 2, + }; + static bool is_resize_ack(int flags) { + return (flags & IS_RESIZE_ACK) != 0; + } + static bool is_restore_ack(int flags) { + return (flags & IS_RESTORE_ACK) != 0; + } + + static bool is_repaint_ack(int flags) { + return (flags & IS_REPAINT_ACK) != 0; + } +}; + +struct ViewHostMsg_PaintRect_Params { + // The bitmap to be painted into the rect given by bitmap_rect. + TransportDIB::Id bitmap; + + // The position and size of the bitmap. + gfx::Rect bitmap_rect; + + // The size of the RenderView when this message was generated. This is + // included so the host knows how large the view is from the perspective of + // the renderer process. This is necessary in case a resize operation is in + // progress. + gfx::Size view_size; + + // New window locations for plugin child windows. + std::vector plugin_window_moves; + + // The following describes the various bits that may be set in flags: + // + // ViewHostMsg_PaintRect_Flags::IS_RESIZE_ACK + // Indicates that this is a response to a ViewMsg_Resize message. + // + // ViewHostMsg_PaintRect_Flags::IS_RESTORE_ACK + // Indicates that this is a response to a ViewMsg_WasRestored message. + // + // If flags is zero, then this message corresponds to an unsoliticed paint + // request by the render view. Both of the above bits may be set in flags, + // which would indicate that this paint message is an ACK for multiple + // request messages. + int flags; +}; + +// Parameters structure for ViewHostMsg_ScrollRect, which has too many data +// parameters to be reasonably put in a predefined IPC message. +struct ViewHostMsg_ScrollRect_Params { + // The bitmap to be painted into the rect exposed by scrolling. + TransportDIB::Id bitmap; + + // The position and size of the bitmap. + gfx::Rect bitmap_rect; + + // The scroll offset. Only one of these can be non-zero. + int dx; + int dy; + + // The rectangular region to scroll. + gfx::Rect clip_rect; + + // The size of the RenderView when this message was generated. + gfx::Size view_size; + + // New window locations for plugin child windows. + std::vector plugin_window_moves; +}; + +// Parameters structure for ViewMsg_UploadFile. +struct ViewMsg_UploadFile_Params { + // See TabContents::StartFileUpload for a description of these fields. + std::wstring file_path; + std::wstring form; + std::wstring file; + std::wstring submit; + std::wstring other_values; +}; + +// Parameters for a resource request. +struct ViewHostMsg_Resource_Request { + // The request method: GET, POST, etc. + std::string method; + + // The requested URL. + GURL url; + + // The URL of the document in the top-level window, which may be checked by + // the third-party cookie blocking policy. Leaving it empty may lead to + // undesired cookie blocking. Third-party cookie blocking can be bypassed by + // setting policy_url = url, but this should ideally only be done if there + // really is no way to determine the correct value. + GURL policy_url; + + // The referrer to use (may be empty). + GURL referrer; + + // The origin of the frame that is associated with this request. This is used + // to update our mixed content state. + std::string frame_origin; + + // The origin of the main frame (top-level frame) that is associated with this + // request. This is used to update our mixed content state. + std::string main_frame_origin; + + // Additional HTTP request headers. + std::string headers; + + // URLRequest load flags (0 by default). + int load_flags; + + // Process ID of process that originated this request. + int origin_pid; + + // What this resource load is for (main frame, sub-frame, sub-resource, + // object). + ResourceType::Type resource_type; + + // Used by plugin->browser requests to get the correct URLRequestContext. + uint32 request_context; + + // Indicates which frame (or worker context) the request is being loaded into, + // or kNoAppCacheContextId. + int32 app_cache_context_id; + + // Optional upload data (may be null). + scoped_refptr upload_data; +}; + +// Parameters for a render request. +struct ViewMsg_Print_Params { + // In pixels according to dpi_x and dpi_y. + gfx::Size printable_size; + + // Specifies dots per inch. + double dpi; + + // Minimum shrink factor. See PrintSettings::min_shrink for more information. + double min_shrink; + + // Maximum shrink factor. See PrintSettings::max_shrink for more information. + double max_shrink; + + // Desired apparent dpi on paper. + int desired_dpi; + + // Cookie for the document to ensure correctness. + int document_cookie; + + // Warning: do not compare document_cookie. + bool Equals(const ViewMsg_Print_Params& rhs) const { + return printable_size == rhs.printable_size && + dpi == rhs.dpi && + min_shrink == rhs.min_shrink && + max_shrink == rhs.max_shrink && + desired_dpi == rhs.desired_dpi; + } + + // Checking if the current params is empty. Just initialized after a memset. + bool IsEmpty() const { + return !document_cookie && !desired_dpi && !max_shrink && !min_shrink && + !dpi && printable_size.IsEmpty(); + } +}; + +struct ViewMsg_PrintPage_Params { + // Parameters to render the page as a printed page. It must always be the same + // value for all the document. + ViewMsg_Print_Params params; + + // The page number is the indicator of the square that should be rendered + // according to the layout specified in ViewMsg_Print_Params. + int page_number; +}; + +struct ViewMsg_PrintPages_Params { + // Parameters to render the page as a printed page. It must always be the same + // value for all the document. + ViewMsg_Print_Params params; + + // If empty, this means a request to render all the printed pages. + std::vector pages; +}; + +// Parameters to describe a rendered page. +struct ViewHostMsg_DidPrintPage_Params { + // A shared memory handle to the EMF data. This data can be quite large so a + // memory map needs to be used. + base::SharedMemoryHandle emf_data_handle; + + // Size of the EMF data. + unsigned data_size; + + // Cookie for the document to ensure correctness. + int document_cookie; + + // Page number. + int page_number; + + // Shrink factor used to render this page. + double actual_shrink; +}; + +// The first parameter for the ViewHostMsg_ImeUpdateStatus message. +enum ViewHostMsg_ImeControl { + IME_DISABLE = 0, + IME_MOVE_WINDOWS, + IME_COMPLETE_COMPOSITION, +}; + +// Parameters for creating an audio output stream. +struct ViewHostMsg_Audio_CreateStream { + // Format request for the stream. + AudioManager::Format format; + + // Number of channels. + int channels; + + // Sampling rate (frequency) of the output stream. + int sample_rate; + + // Number of bits per sample; + int bits_per_sample; + + // Number of bytes per packet. + size_t packet_size; +}; + +// This message is used for supporting popup menus on Mac OS X using native +// Cocoa controls. The renderer sends us this message which we use to populate +// the popup menu. +struct ViewHostMsg_ShowPopup_Params { + // Position on the screen. + gfx::Rect bounds; + + // The height of each item in the menu. + int item_height; + + // The currently selected (displayed) item in the menu. + int selected_item; + + // The entire list of items in the popup menu. + std::vector popup_items; +}; + +namespace IPC { + +template <> +struct ParamTraits { + typedef ResourceType::Type param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int type; + if (!m->ReadInt(iter, &type) || !ResourceType::ValidType(type)) + return false; + *p = ResourceType::FromInt(type); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring type; + switch (p) { + case ResourceType::MAIN_FRAME: + type = L"MAIN_FRAME"; + break; + case ResourceType::SUB_FRAME: + type = L"SUB_FRAME"; + break; + case ResourceType::SUB_RESOURCE: + type = L"SUB_RESOURCE"; + break; + case ResourceType::OBJECT: + type = L"OBJECT"; + break; + case ResourceType::MEDIA: + type = L"MEDIA"; + break; + default: + type = L"UNKNOWN"; + break; + } + + LogParam(type, l); + } +}; + +template <> +struct ParamTraits { + typedef FilterPolicy::Type param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int type; + if (!m->ReadInt(iter, &type) || !FilterPolicy::ValidType(type)) + return false; + *p = FilterPolicy::FromInt(type); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring type; + switch (p) { + case FilterPolicy::DONT_FILTER: + type = L"DONT_FILTER"; + break; + case FilterPolicy::FILTER_ALL: + type = L"FILTER_ALL"; + break; + case FilterPolicy::FILTER_ALL_EXCEPT_IMAGES: + type = L"FILTER_ALL_EXCEPT_IMAGES"; + break; + default: + type = L"UNKNOWN"; + break; + } + + LogParam(type, l); + } +}; + +template <> +struct ParamTraits { + typedef ContextNode param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p.type); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int type; + if (!m->ReadInt(iter, &type)) + return false; + *p = ContextNode(type); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring event = L""; + + if (!p.type) { + event.append(L"NONE"); + } else { + event.append(L"("); + if (p.type & ContextNode::PAGE) + event.append(L"PAGE|"); + if (p.type & ContextNode::FRAME) + event.append(L"FRAME|"); + if (p.type & ContextNode::LINK) + event.append(L"LINK|"); + if (p.type & ContextNode::IMAGE) + event.append(L"IMAGE|"); + if (p.type & ContextNode::SELECTION) + event.append(L"SELECTION|"); + if (p.type & ContextNode::EDITABLE) + event.append(L"EDITABLE|"); + if (p.type & ContextNode::MISSPELLED_WORD) + event.append(L"MISSPELLED_WORD|"); + event.append(L")"); + } + + LogParam(event, l); + } +}; + +template <> +struct ParamTraits { + typedef webkit_glue::WebAccessibility::InParams param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.object_id); + WriteParam(m, p.function_id); + WriteParam(m, p.child_id); + WriteParam(m, p.input_long1); + WriteParam(m, p.input_long2); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->object_id) && + ReadParam(m, iter, &p->function_id) && + ReadParam(m, iter, &p->child_id) && + ReadParam(m, iter, &p->input_long1) && + ReadParam(m, iter, &p->input_long2); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.object_id, l); + l->append(L", "); + LogParam(p.function_id, l); + l->append(L", "); + LogParam(p.child_id, l); + l->append(L", "); + LogParam(p.input_long1, l); + l->append(L", "); + LogParam(p.input_long2, l); + l->append(L")"); + } +}; + +template <> +struct ParamTraits { + typedef webkit_glue::WebAccessibility::OutParams param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.object_id); + WriteParam(m, p.output_long1); + WriteParam(m, p.output_long2); + WriteParam(m, p.output_long3); + WriteParam(m, p.output_long4); + WriteParam(m, p.output_string); + WriteParam(m, p.return_code); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->object_id) && + ReadParam(m, iter, &p->output_long1) && + ReadParam(m, iter, &p->output_long2) && + ReadParam(m, iter, &p->output_long3) && + ReadParam(m, iter, &p->output_long4) && + ReadParam(m, iter, &p->output_string) && + ReadParam(m, iter, &p->return_code); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.object_id, l); + l->append(L", "); + LogParam(p.output_long1, l); + l->append(L", "); + LogParam(p.output_long2, l); + l->append(L", "); + LogParam(p.output_long3, l); + l->append(L", "); + LogParam(p.output_long4, l); + l->append(L", "); + LogParam(p.output_string, l); + l->append(L", "); + LogParam(p.return_code, l); + l->append(L")"); + } +}; + +template <> +struct ParamTraits { + typedef ViewHostMsg_ImeControl param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int type; + if (!m->ReadInt(iter, &type)) + return false; + *p = static_cast(type); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring control; + switch (p) { + case IME_DISABLE: + control = L"IME_DISABLE"; + break; + case IME_MOVE_WINDOWS: + control = L"IME_MOVE_WINDOWS"; + break; + case IME_COMPLETE_COMPOSITION: + control = L"IME_COMPLETE_COMPOSITION"; + break; + default: + control = L"UNKNOWN"; + break; + } + + LogParam(control, l); + } +}; + +// Traits for ViewMsg_Navigate_Params structure to pack/unpack. +template <> +struct ParamTraits { + typedef ViewMsg_Navigate_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.page_id); + WriteParam(m, p.url); + WriteParam(m, p.referrer); + WriteParam(m, p.transition); + WriteParam(m, p.state); + WriteParam(m, p.reload); + WriteParam(m, p.request_time); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->page_id) && + ReadParam(m, iter, &p->url) && + ReadParam(m, iter, &p->referrer) && + ReadParam(m, iter, &p->transition) && + ReadParam(m, iter, &p->state) && + ReadParam(m, iter, &p->reload) && + ReadParam(m, iter, &p->request_time); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.page_id, l); + l->append(L", "); + LogParam(p.url, l); + l->append(L", "); + LogParam(p.transition, l); + l->append(L", "); + LogParam(p.state, l); + l->append(L", "); + LogParam(p.reload, l); + l->append(L", "); + LogParam(p.request_time, l); + l->append(L")"); + } +}; + +// Traits for PasswordForm_Params structure to pack/unpack. +template <> +struct ParamTraits { + typedef PasswordForm param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.signon_realm); + WriteParam(m, p.origin); + WriteParam(m, p.action); + WriteParam(m, p.submit_element); + WriteParam(m, p.username_element); + WriteParam(m, p.username_value); + WriteParam(m, p.password_element); + WriteParam(m, p.password_value); + WriteParam(m, p.old_password_element); + WriteParam(m, p.old_password_value); + WriteParam(m, p.ssl_valid); + WriteParam(m, p.preferred); + WriteParam(m, p.blacklisted_by_user); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->signon_realm) && + ReadParam(m, iter, &p->origin) && + ReadParam(m, iter, &p->action) && + ReadParam(m, iter, &p->submit_element) && + ReadParam(m, iter, &p->username_element) && + ReadParam(m, iter, &p->username_value) && + ReadParam(m, iter, &p->password_element) && + ReadParam(m, iter, &p->password_value) && + ReadParam(m, iter, &p->old_password_element) && + ReadParam(m, iter, &p->old_password_value) && + ReadParam(m, iter, &p->ssl_valid) && + ReadParam(m, iter, &p->preferred) && + ReadParam(m, iter, &p->blacklisted_by_user); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + +// Traits for AutofillForm_Params structure to pack/unpack. +template <> +struct ParamTraits { + typedef AutofillForm param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.elements.size()); + for (std::vector::const_iterator itr = + p.elements.begin(); + itr != p.elements.end(); + itr++) { + WriteParam(m, itr->name); + WriteParam(m, itr->value); + } + } + static bool Read(const Message* m, void** iter, param_type* p) { + bool result = true; + size_t elements_size = 0; + result = result && ReadParam(m, iter, &elements_size); + p->elements.resize(elements_size); + for (size_t i = 0; i < elements_size; i++) { + result = result && ReadParam(m, iter, &(p->elements[i].name)); + result = result && ReadParam(m, iter, &(p->elements[i].value)); + } + return result; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + +// Traits for ViewHostMsg_FrameNavigate_Params structure to pack/unpack. +template <> +struct ParamTraits { + typedef ViewHostMsg_FrameNavigate_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.page_id); + WriteParam(m, p.url); + WriteParam(m, p.referrer); + WriteParam(m, p.transition); + WriteParam(m, p.redirects); + WriteParam(m, p.should_update_history); + WriteParam(m, p.searchable_form_url); + WriteParam(m, p.searchable_form_element_name); + WriteParam(m, p.searchable_form_encoding); + WriteParam(m, p.password_form); + WriteParam(m, p.security_info); + WriteParam(m, p.gesture); + WriteParam(m, p.contents_mime_type); + WriteParam(m, p.is_post); + WriteParam(m, p.is_content_filtered); + WriteParam(m, p.http_status_code); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->page_id) && + ReadParam(m, iter, &p->url) && + ReadParam(m, iter, &p->referrer) && + ReadParam(m, iter, &p->transition) && + ReadParam(m, iter, &p->redirects) && + ReadParam(m, iter, &p->should_update_history) && + ReadParam(m, iter, &p->searchable_form_url) && + ReadParam(m, iter, &p->searchable_form_element_name) && + ReadParam(m, iter, &p->searchable_form_encoding) && + ReadParam(m, iter, &p->password_form) && + ReadParam(m, iter, &p->security_info) && + ReadParam(m, iter, &p->gesture) && + ReadParam(m, iter, &p->contents_mime_type) && + ReadParam(m, iter, &p->is_post) && + ReadParam(m, iter, &p->is_content_filtered) && + ReadParam(m, iter, &p->http_status_code); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.page_id, l); + l->append(L", "); + LogParam(p.url, l); + l->append(L", "); + LogParam(p.referrer, l); + l->append(L", "); + LogParam(p.transition, l); + l->append(L", "); + LogParam(p.redirects, l); + l->append(L", "); + LogParam(p.should_update_history, l); + l->append(L", "); + LogParam(p.searchable_form_url, l); + l->append(L", "); + LogParam(p.searchable_form_element_name, l); + l->append(L", "); + LogParam(p.searchable_form_encoding, l); + l->append(L", "); + LogParam(p.password_form, l); + l->append(L", "); + LogParam(p.security_info, l); + l->append(L", "); + LogParam(p.gesture, l); + l->append(L", "); + LogParam(p.contents_mime_type, l); + l->append(L", "); + LogParam(p.is_post, l); + l->append(L", "); + LogParam(p.is_content_filtered, l); + l->append(L", "); + LogParam(p.http_status_code, l); + l->append(L")"); + } +}; + +template <> +struct ParamTraits { + typedef ContextMenuParams param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.node); + WriteParam(m, p.x); + WriteParam(m, p.y); + WriteParam(m, p.link_url); + WriteParam(m, p.unfiltered_link_url); + WriteParam(m, p.image_url); + WriteParam(m, p.page_url); + WriteParam(m, p.frame_url); + WriteParam(m, p.selection_text); + WriteParam(m, p.misspelled_word); + WriteParam(m, p.dictionary_suggestions); + WriteParam(m, p.spellcheck_enabled); + WriteParam(m, p.edit_flags); + WriteParam(m, p.security_info); + WriteParam(m, p.frame_charset); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->node) && + ReadParam(m, iter, &p->x) && + ReadParam(m, iter, &p->y) && + ReadParam(m, iter, &p->link_url) && + ReadParam(m, iter, &p->unfiltered_link_url) && + ReadParam(m, iter, &p->image_url) && + ReadParam(m, iter, &p->page_url) && + ReadParam(m, iter, &p->frame_url) && + ReadParam(m, iter, &p->selection_text) && + ReadParam(m, iter, &p->misspelled_word) && + ReadParam(m, iter, &p->dictionary_suggestions) && + ReadParam(m, iter, &p->spellcheck_enabled) && + ReadParam(m, iter, &p->edit_flags) && + ReadParam(m, iter, &p->security_info) && + ReadParam(m, iter, &p->frame_charset); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + +// Traits for ViewHostMsg_PaintRect_Params structure to pack/unpack. +template <> +struct ParamTraits { + typedef ViewHostMsg_PaintRect_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.bitmap); + WriteParam(m, p.bitmap_rect); + WriteParam(m, p.view_size); + WriteParam(m, p.plugin_window_moves); + WriteParam(m, p.flags); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->bitmap) && + ReadParam(m, iter, &p->bitmap_rect) && + ReadParam(m, iter, &p->view_size) && + ReadParam(m, iter, &p->plugin_window_moves) && + ReadParam(m, iter, &p->flags); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.bitmap, l); + l->append(L", "); + LogParam(p.bitmap_rect, l); + l->append(L", "); + LogParam(p.view_size, l); + l->append(L", "); + LogParam(p.plugin_window_moves, l); + l->append(L", "); + LogParam(p.flags, l); + l->append(L")"); + } +}; + +// Traits for ViewHostMsg_ScrollRect_Params structure to pack/unpack. +template <> +struct ParamTraits { + typedef ViewHostMsg_ScrollRect_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.bitmap); + WriteParam(m, p.bitmap_rect); + WriteParam(m, p.dx); + WriteParam(m, p.dy); + WriteParam(m, p.clip_rect); + WriteParam(m, p.view_size); + WriteParam(m, p.plugin_window_moves); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->bitmap) && + ReadParam(m, iter, &p->bitmap_rect) && + ReadParam(m, iter, &p->dx) && + ReadParam(m, iter, &p->dy) && + ReadParam(m, iter, &p->clip_rect) && + ReadParam(m, iter, &p->view_size) && + ReadParam(m, iter, &p->plugin_window_moves); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.bitmap, l); + l->append(L", "); + LogParam(p.bitmap_rect, l); + l->append(L", "); + LogParam(p.dx, l); + l->append(L", "); + LogParam(p.dy, l); + l->append(L", "); + LogParam(p.clip_rect, l); + l->append(L", "); + LogParam(p.view_size, l); + l->append(L", "); + LogParam(p.plugin_window_moves, l); + l->append(L")"); + } +}; + +template <> +struct ParamTraits { + typedef WebPluginGeometry param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.window); + WriteParam(m, p.window_rect); + WriteParam(m, p.clip_rect); + WriteParam(m, p.cutout_rects); + WriteParam(m, p.visible); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->window) && + ReadParam(m, iter, &p->window_rect) && + ReadParam(m, iter, &p->clip_rect) && + ReadParam(m, iter, &p->cutout_rects) && + ReadParam(m, iter, &p->visible); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.window, l); + l->append(L", "); + LogParam(p.window_rect, l); + l->append(L", "); + LogParam(p.clip_rect, l); + l->append(L", "); + LogParam(p.cutout_rects, l); + l->append(L", "); + LogParam(p.visible, l); + l->append(L")"); + } +}; + +// Traits for ViewMsg_GetPlugins_Reply structure to pack/unpack. +template <> +struct ParamTraits { + typedef WebPluginMimeType param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.mime_type); + WriteParam(m, p.file_extensions); + WriteParam(m, p.description); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ReadParam(m, iter, &r->mime_type) && + ReadParam(m, iter, &r->file_extensions) && + ReadParam(m, iter, &r->description); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.mime_type, l); + l->append(L", "); + LogParam(p.file_extensions, l); + l->append(L", "); + LogParam(p.description, l); + l->append(L")"); + } +}; + + +template <> +struct ParamTraits { + typedef WebPluginInfo param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.name); + WriteParam(m, p.path); + WriteParam(m, p.version); + WriteParam(m, p.desc); + WriteParam(m, p.mime_types); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ReadParam(m, iter, &r->name) && + ReadParam(m, iter, &r->path) && + ReadParam(m, iter, &r->version) && + ReadParam(m, iter, &r->desc) && + ReadParam(m, iter, &r->mime_types); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.name, l); + l->append(L", "); + l->append(L", "); + LogParam(p.path, l); + l->append(L", "); + LogParam(p.version, l); + l->append(L", "); + LogParam(p.desc, l); + l->append(L", "); + LogParam(p.mime_types, l); + l->append(L")"); + } +}; + +// Traits for ViewMsg_UploadFile_Params structure to pack/unpack. +template <> +struct ParamTraits { + typedef ViewMsg_UploadFile_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.file_path); + WriteParam(m, p.form); + WriteParam(m, p.file); + WriteParam(m, p.submit); + WriteParam(m, p.other_values); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->file_path) && + ReadParam(m, iter, &p->form) && + ReadParam(m, iter, &p->file) && + ReadParam(m, iter, &p->submit) && + ReadParam(m, iter, &p->other_values); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + +// Traits for net::UploadData::Element. +template <> +struct ParamTraits { + typedef net::UploadData::Element param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast(p.type())); + if (p.type() == net::UploadData::TYPE_BYTES) { + m->WriteData(&p.bytes()[0], static_cast(p.bytes().size())); + } else { + WriteParam(m, p.file_path()); + WriteParam(m, p.file_range_offset()); + WriteParam(m, p.file_range_length()); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + int type; + if (!ReadParam(m, iter, &type)) + return false; + if (type == net::UploadData::TYPE_BYTES) { + const char* data; + int len; + if (!m->ReadData(iter, &data, &len)) + return false; + r->SetToBytes(data, len); + } else { + DCHECK(type == net::UploadData::TYPE_FILE); + FilePath file_path; + uint64 offset, length; + if (!ReadParam(m, iter, &file_path)) + return false; + if (!ReadParam(m, iter, &offset)) + return false; + if (!ReadParam(m, iter, &length)) + return false; + r->SetToFilePathRange(file_path, offset, length); + } + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + +// Traits for net::UploadData. +template <> +struct ParamTraits > { + typedef scoped_refptr param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.get() != NULL); + if (p) { + WriteParam(m, p->elements()); + WriteParam(m, p->identifier()); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + bool has_object; + if (!ReadParam(m, iter, &has_object)) + return false; + if (!has_object) + return true; + std::vector elements; + if (!ReadParam(m, iter, &elements)) + return false; + int identifier; + if (!ReadParam(m, iter, &identifier)) + return false; + *r = new net::UploadData; + (*r)->swap_elements(&elements); + (*r)->set_identifier(identifier); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + +// Traits for PasswordFormDomManager::FillData. +template <> +struct ParamTraits { + typedef PasswordFormDomManager::FillData param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.basic_data); + WriteParam(m, p.additional_logins); + WriteParam(m, p.wait_for_username); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ReadParam(m, iter, &r->basic_data) && + ReadParam(m, iter, &r->additional_logins) && + ReadParam(m, iter, &r->wait_for_username); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + +template<> +struct ParamTraits { + typedef NavigationGesture param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int type; + if (!m->ReadInt(iter, &type)) + return false; + *p = static_cast(type); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring event; + switch (p) { + case NavigationGestureUser: + event = L"GESTURE_USER"; + break; + case NavigationGestureAuto: + event = L"GESTURE_AUTO"; + break; + default: + event = L"GESTURE_UNKNOWN"; + break; + } + LogParam(event, l); + } +}; + +// Traits for ViewHostMsg_Resource_Request +template <> +struct ParamTraits { + typedef ViewHostMsg_Resource_Request param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.method); + WriteParam(m, p.url); + WriteParam(m, p.policy_url); + WriteParam(m, p.referrer); + WriteParam(m, p.frame_origin); + WriteParam(m, p.main_frame_origin); + WriteParam(m, p.headers); + WriteParam(m, p.load_flags); + WriteParam(m, p.origin_pid); + WriteParam(m, p.resource_type); + WriteParam(m, p.request_context); + WriteParam(m, p.app_cache_context_id); + WriteParam(m, p.upload_data); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ReadParam(m, iter, &r->method) && + ReadParam(m, iter, &r->url) && + ReadParam(m, iter, &r->policy_url) && + ReadParam(m, iter, &r->referrer) && + ReadParam(m, iter, &r->frame_origin) && + ReadParam(m, iter, &r->main_frame_origin) && + ReadParam(m, iter, &r->headers) && + ReadParam(m, iter, &r->load_flags) && + ReadParam(m, iter, &r->origin_pid) && + ReadParam(m, iter, &r->resource_type) && + ReadParam(m, iter, &r->request_context) && + ReadParam(m, iter, &r->app_cache_context_id) && + ReadParam(m, iter, &r->upload_data); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.method, l); + l->append(L", "); + LogParam(p.url, l); + l->append(L", "); + LogParam(p.referrer, l); + l->append(L", "); + LogParam(p.frame_origin, l); + l->append(L", "); + LogParam(p.main_frame_origin, l); + l->append(L", "); + LogParam(p.load_flags, l); + l->append(L", "); + LogParam(p.origin_pid, l); + l->append(L", "); + LogParam(p.resource_type, l); + l->append(L", "); + LogParam(p.request_context, l); + l->append(L", "); + LogParam(p.app_cache_context_id, l); + l->append(L")"); + } +}; + +// Traits for URLRequestStatus +template <> +struct ParamTraits { + typedef URLRequestStatus param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, static_cast(p.status())); + WriteParam(m, p.os_error()); + } + static bool Read(const Message* m, void** iter, param_type* r) { + int status, os_error; + if (!ReadParam(m, iter, &status) || + !ReadParam(m, iter, &os_error)) + return false; + r->set_status(static_cast(status)); + r->set_os_error(os_error); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring status; + switch (p.status()) { + case URLRequestStatus::SUCCESS: + status = L"SUCCESS"; + break; + case URLRequestStatus::IO_PENDING: + status = L"IO_PENDING "; + break; + case URLRequestStatus::HANDLED_EXTERNALLY: + status = L"HANDLED_EXTERNALLY"; + break; + case URLRequestStatus::CANCELED: + status = L"CANCELED"; + break; + case URLRequestStatus::FAILED: + status = L"FAILED"; + break; + default: + status = L"UNKNOWN"; + break; + } + if (p.status() == URLRequestStatus::FAILED) + l->append(L"("); + + LogParam(status, l); + + if (p.status() == URLRequestStatus::FAILED) { + l->append(L", "); + LogParam(p.os_error(), l); + l->append(L")"); + } + } +}; + +template <> +struct ParamTraits > { + typedef scoped_refptr param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.get() != NULL); + if (p) { + // Do not disclose Set-Cookie headers over IPC. + p->Persist(m, net::HttpResponseHeaders::PERSIST_SANS_COOKIES); + } + } + static bool Read(const Message* m, void** iter, param_type* r) { + bool has_object; + if (!ReadParam(m, iter, &has_object)) + return false; + if (has_object) + *r = new net::HttpResponseHeaders(*m, iter); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + +// Traits for webkit_glue::ResourceLoaderBridge::ResponseInfo +template <> +struct ParamTraits { + typedef webkit_glue::ResourceLoaderBridge::ResponseInfo param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.request_time); + WriteParam(m, p.response_time); + WriteParam(m, p.headers); + WriteParam(m, p.mime_type); + WriteParam(m, p.charset); + WriteParam(m, p.security_info); + WriteParam(m, p.content_length); + WriteParam(m, p.app_cache_id); + WriteParam(m, p.response_data_file); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ReadParam(m, iter, &r->request_time) && + ReadParam(m, iter, &r->response_time) && + ReadParam(m, iter, &r->headers) && + ReadParam(m, iter, &r->mime_type) && + ReadParam(m, iter, &r->charset) && + ReadParam(m, iter, &r->security_info) && + ReadParam(m, iter, &r->content_length) && + ReadParam(m, iter, &r->app_cache_id) && + ReadParam(m, iter, &r->response_data_file); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.request_time, l); + l->append(L", "); + LogParam(p.response_time, l); + l->append(L", "); + LogParam(p.headers, l); + l->append(L", "); + LogParam(p.mime_type, l); + l->append(L", "); + LogParam(p.charset, l); + l->append(L", "); + LogParam(p.security_info, l); + l->append(L", "); + LogParam(p.content_length, l); + l->append(L", "); + LogParam(p.app_cache_id, l); + l->append(L")"); + } +}; + +template <> +struct ParamTraits { + typedef ResourceResponseHead param_type; + static void Write(Message* m, const param_type& p) { + ParamTraits::Write(m, p); + WriteParam(m, p.status); + WriteParam(m, p.filter_policy); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ParamTraits::Read(m, + iter, + r) && + ReadParam(m, iter, &r->status) && + ReadParam(m, iter, &r->filter_policy); + } + static void Log(const param_type& p, std::wstring* l) { + // log more? + ParamTraits::Log(p, l); + } +}; + +template <> +struct ParamTraits { + typedef SyncLoadResult param_type; + static void Write(Message* m, const param_type& p) { + ParamTraits::Write(m, p); + WriteParam(m, p.final_url); + WriteParam(m, p.data); + } + static bool Read(const Message* m, void** iter, param_type* r) { + return + ParamTraits::Read(m, iter, r) && + ReadParam(m, iter, &r->final_url) && + ReadParam(m, iter, &r->data); + } + static void Log(const param_type& p, std::wstring* l) { + // log more? + ParamTraits::Log(p, l); + } +}; + +// Traits for FormData structure to pack/unpack. +template <> +struct ParamTraits { + typedef FormData param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.origin); + WriteParam(m, p.action); + WriteParam(m, p.elements); + WriteParam(m, p.values); + WriteParam(m, p.submit); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->origin) && + ReadParam(m, iter, &p->action) && + ReadParam(m, iter, &p->elements) && + ReadParam(m, iter, &p->values) && + ReadParam(m, iter, &p->submit); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + +// Traits for ViewMsg_Print_Params +template <> +struct ParamTraits { + typedef ViewMsg_Print_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.printable_size); + WriteParam(m, p.dpi); + WriteParam(m, p.min_shrink); + WriteParam(m, p.max_shrink); + WriteParam(m, p.desired_dpi); + WriteParam(m, p.document_cookie); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return ReadParam(m, iter, &p->printable_size) && + ReadParam(m, iter, &p->dpi) && + ReadParam(m, iter, &p->min_shrink) && + ReadParam(m, iter, &p->max_shrink) && + ReadParam(m, iter, &p->desired_dpi) && + ReadParam(m, iter, &p->document_cookie); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + +// Traits for ViewMsg_PrintPage_Params +template <> +struct ParamTraits { + typedef ViewMsg_PrintPage_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.params); + WriteParam(m, p.page_number); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return ReadParam(m, iter, &p->params) && + ReadParam(m, iter, &p->page_number); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + +// Traits for ViewMsg_PrintPages_Params +template <> +struct ParamTraits { + typedef ViewMsg_PrintPages_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.params); + WriteParam(m, p.pages); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return ReadParam(m, iter, &p->params) && + ReadParam(m, iter, &p->pages); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + +// Traits for ViewHostMsg_DidPrintPage_Params +template <> +struct ParamTraits { + typedef ViewHostMsg_DidPrintPage_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.emf_data_handle); + WriteParam(m, p.data_size); + WriteParam(m, p.document_cookie); + WriteParam(m, p.page_number); + WriteParam(m, p.actual_shrink); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return ReadParam(m, iter, &p->emf_data_handle) && + ReadParam(m, iter, &p->data_size) && + ReadParam(m, iter, &p->document_cookie) && + ReadParam(m, iter, &p->page_number) && + ReadParam(m, iter, &p->actual_shrink); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + +// Traits for WebPreferences structure to pack/unpack. +template <> +struct ParamTraits { + typedef WebPreferences param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.standard_font_family); + WriteParam(m, p.fixed_font_family); + WriteParam(m, p.serif_font_family); + WriteParam(m, p.sans_serif_font_family); + WriteParam(m, p.cursive_font_family); + WriteParam(m, p.fantasy_font_family); + WriteParam(m, p.default_font_size); + WriteParam(m, p.default_fixed_font_size); + WriteParam(m, p.minimum_font_size); + WriteParam(m, p.minimum_logical_font_size); + WriteParam(m, p.default_encoding); + WriteParam(m, p.javascript_enabled); + WriteParam(m, p.web_security_enabled); + WriteParam(m, p.javascript_can_open_windows_automatically); + WriteParam(m, p.loads_images_automatically); + WriteParam(m, p.plugins_enabled); + WriteParam(m, p.dom_paste_enabled); + WriteParam(m, p.developer_extras_enabled); + WriteParam(m, p.shrinks_standalone_images_to_fit); + WriteParam(m, p.uses_universal_detector); + WriteParam(m, p.text_areas_are_resizable); + WriteParam(m, p.java_enabled); + WriteParam(m, p.user_style_sheet_enabled); + WriteParam(m, p.user_style_sheet_location); + WriteParam(m, p.uses_page_cache); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->standard_font_family) && + ReadParam(m, iter, &p->fixed_font_family) && + ReadParam(m, iter, &p->serif_font_family) && + ReadParam(m, iter, &p->sans_serif_font_family) && + ReadParam(m, iter, &p->cursive_font_family) && + ReadParam(m, iter, &p->fantasy_font_family) && + ReadParam(m, iter, &p->default_font_size) && + ReadParam(m, iter, &p->default_fixed_font_size) && + ReadParam(m, iter, &p->minimum_font_size) && + ReadParam(m, iter, &p->minimum_logical_font_size) && + ReadParam(m, iter, &p->default_encoding) && + ReadParam(m, iter, &p->javascript_enabled) && + ReadParam(m, iter, &p->web_security_enabled) && + ReadParam(m, iter, &p->javascript_can_open_windows_automatically) && + ReadParam(m, iter, &p->loads_images_automatically) && + ReadParam(m, iter, &p->plugins_enabled) && + ReadParam(m, iter, &p->dom_paste_enabled) && + ReadParam(m, iter, &p->developer_extras_enabled) && + ReadParam(m, iter, &p->shrinks_standalone_images_to_fit) && + ReadParam(m, iter, &p->uses_universal_detector) && + ReadParam(m, iter, &p->text_areas_are_resizable) && + ReadParam(m, iter, &p->java_enabled) && + ReadParam(m, iter, &p->user_style_sheet_enabled) && + ReadParam(m, iter, &p->user_style_sheet_location) && + ReadParam(m, iter, &p->uses_page_cache); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + +// Traits for WebDropData +template <> +struct ParamTraits { + typedef WebDropData param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.identity); + WriteParam(m, p.url); + WriteParam(m, p.url_title); + WriteParam(m, p.file_extension); + WriteParam(m, p.filenames); + WriteParam(m, p.plain_text); + WriteParam(m, p.text_html); + WriteParam(m, p.html_base_url); + WriteParam(m, p.file_description_filename); + WriteParam(m, p.file_contents); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->identity) && + ReadParam(m, iter, &p->url) && + ReadParam(m, iter, &p->url_title) && + ReadParam(m, iter, &p->file_extension) && + ReadParam(m, iter, &p->filenames) && + ReadParam(m, iter, &p->plain_text) && + ReadParam(m, iter, &p->text_html) && + ReadParam(m, iter, &p->html_base_url) && + ReadParam(m, iter, &p->file_description_filename) && + ReadParam(m, iter, &p->file_contents); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + +template<> +struct ParamTraits { + typedef ModalDialogEvent param_type; +#if defined(OS_WIN) + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.event); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return ReadParam(m, iter, &p->event); + } +#else + static void Write(Message* m, const param_type& p) { + } + static bool Read(const Message* m, void** iter, param_type* p) { + return true; + } +#endif + + static void Log(const param_type& p, std::wstring* l) { + l->append(L""); + } +}; + +// Traits for AudioManager::Format. +template <> +struct ParamTraits { + typedef AudioManager::Format param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int type; + if (!m->ReadInt(iter, &type)) + return false; + *p = static_cast(type); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring format; + switch (p) { + case AudioManager::AUDIO_PCM_LINEAR: + format = L"AUDIO_PCM_LINEAR"; + break; + case AudioManager::AUDIO_PCM_DELTA: + format = L"AUDIO_PCM_DELTA"; + break; + case AudioManager::AUDIO_MOCK: + format = L"AUDIO_MOCK"; + break; + default: + format = L"AUDIO_LAST_FORMAT"; + break; + } + LogParam(format, l); + } +}; + +// Traits for ViewHostMsg_Audio_CreateStream. +template <> +struct ParamTraits { + typedef ViewHostMsg_Audio_CreateStream param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.format); + WriteParam(m, p.channels); + WriteParam(m, p.sample_rate); + WriteParam(m, p.bits_per_sample); + WriteParam(m, p.packet_size); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->format) && + ReadParam(m, iter, &p->channels) && + ReadParam(m, iter, &p->sample_rate) && + ReadParam(m, iter, &p->bits_per_sample) && + ReadParam(m, iter, &p->packet_size); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.format, l); + l->append(L", "); + LogParam(p.channels, l); + l->append(L", "); + LogParam(p.sample_rate, l); + l->append(L", "); + LogParam(p.bits_per_sample, l); + l->append(L", "); + LogParam(p.packet_size, l); + l->append(L")"); + } +}; + + +#if defined(OS_POSIX) + +// TODO(port): this shouldn't exist. However, the plugin stuff is really using +// HWNDS (NativeView), and making Windows calls based on them. I've not figured +// out the deal with plugins yet. +template <> +struct ParamTraits { + typedef gfx::NativeView param_type; + static void Write(Message* m, const param_type& p) { + NOTIMPLEMENTED(); + } + + static bool Read(const Message* m, void** iter, param_type* p) { + NOTIMPLEMENTED(); + *p = NULL; + return true; + } + + static void Log(const param_type& p, std::wstring* l) { + l->append(StringPrintf(L"")); + } +}; + +#endif // defined(OS_POSIX) + +template <> +struct ParamTraits { + typedef AudioOutputStream::State param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int type; + if (!m->ReadInt(iter, &type)) + return false; + *p = static_cast(type); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring state; + switch (p) { + case AudioOutputStream::STATE_CREATED: + state = L"AudioOutputStream::STATE_CREATED"; + break; + case AudioOutputStream::STATE_STARTED: + state = L"AudioOutputStream::STATE_STARTED"; + break; + case AudioOutputStream::STATE_PAUSED: + state = L"AudioOutputStream::STATE_PAUSED"; + break; + case AudioOutputStream::STATE_STOPPED: + state = L"AudioOutputStream::STATE_STOPPED"; + break; + case AudioOutputStream::STATE_CLOSED: + state = L"AudioOutputStream::STATE_CLOSED"; + break; + case AudioOutputStream::STATE_ERROR: + state = L"AudioOutputStream::STATE_ERROR"; + break; + default: + state = L"UNKNOWN"; + break; + } + + LogParam(state, l); + } +}; + +template <> +struct ParamTraits { + typedef WebAppCacheContext::ContextType param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(static_cast(p)); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int type; + if (!m->ReadInt(iter, &type)) + return false; + *p = static_cast(type); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring state; + switch (p) { + case WebAppCacheContext::MAIN_FRAME: + state = L"MAIN_FRAME"; + break; + case WebAppCacheContext::CHILD_FRAME: + state = L"CHILD_FRAME"; + break; + case WebAppCacheContext::DEDICATED_WORKER: + state = L"DECICATED_WORKER"; + break; + default: + state = L"UNKNOWN"; + break; + } + + LogParam(state, l); + } +}; + +template<> +struct ParamTraits { + typedef WebMenuItem::Type param_type; + static void Write(Message* m, const param_type& p) { + m->WriteInt(p); + } + static bool Read(const Message* m, void** iter, param_type* p) { + int type; + if (!m->ReadInt(iter, &type)) + return false; + *p = static_cast(type); + return true; + } + static void Log(const param_type& p, std::wstring* l) { + std::wstring type; + switch (p) { + case WebMenuItem::OPTION: + type = L"OPTION"; + break; + case WebMenuItem::GROUP: + type = L"GROUP"; + break; + case WebMenuItem::SEPARATOR: + type = L"SEPARATOR"; + break; + default: + type = L"UNKNOWN"; + break; + } + LogParam(type, l); + } +}; + +template<> +struct ParamTraits { + typedef WebMenuItem param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.label); + WriteParam(m, p.type); + WriteParam(m, p.enabled); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->label) && + ReadParam(m, iter, &p->type) && + ReadParam(m, iter, &p->enabled); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.label, l); + l->append(L", "); + LogParam(p.type, l); + l->append(L", "); + LogParam(p.enabled, l); + l->append(L")"); + } +}; + +// Traits for ViewHostMsg_ShowPopup_Params. +template <> +struct ParamTraits { + typedef ViewHostMsg_ShowPopup_Params param_type; + static void Write(Message* m, const param_type& p) { + WriteParam(m, p.bounds); + WriteParam(m, p.item_height); + WriteParam(m, p.selected_item); + WriteParam(m, p.popup_items); + } + static bool Read(const Message* m, void** iter, param_type* p) { + return + ReadParam(m, iter, &p->bounds) && + ReadParam(m, iter, &p->item_height) && + ReadParam(m, iter, &p->selected_item) && + ReadParam(m, iter, &p->popup_items); + } + static void Log(const param_type& p, std::wstring* l) { + l->append(L"("); + LogParam(p.bounds, l); + l->append(L", "); + LogParam(p.item_height, l); + l->append(L", "); + LogParam(p.selected_item, l); + l->append(L", "); + LogParam(p.popup_items, l); + l->append(L")"); + } +}; + +} // namespace IPC + + +#define MESSAGES_INTERNAL_FILE "chrome/common/render_messages_internal.h" +#include "chrome/common/ipc_message_macros.h" + +#endif // CHROME_COMMON_RENDER_MESSAGES_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/render_messages_internal.h firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/render_messages_internal.h --- firefox-3.6.3+nobinonly/mozilla/ipc/chromium/src/chrome/common/render_messages_internal.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/ipc/chromium/src/chrome/common/render_messages_internal.h 2010-04-16 17:31:52.000000000 +0100 @@ -0,0 +1,1356 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This header is meant to be included in multiple passes, hence no traditional +// header guard. +// See ipc_message_macros.h for explanation of the macros and passes. + +#include +#include + +#include "build/build_config.h" + +#include "base/clipboard.h" +#include "base/file_path.h" +#include "base/gfx/rect.h" +#include "base/gfx/native_widget_types.h" +#include "base/shared_memory.h" +#include "chrome/common/ipc_message_macros.h" +#include "chrome/common/transport_dib.h" +#include "skia/include/SkBitmap.h" +#include "webkit/glue/dom_operations.h" +#include "webkit/glue/webappcachecontext.h" +#include "webkit/glue/webcursor.h" +#include "webkit/glue/webplugin.h" + +// TODO(mpcomplete): rename ViewMsg and ViewHostMsg to something that makes +// more sense with our current design. + +//----------------------------------------------------------------------------- +// RenderView messages +// These are messages sent from the browser to the renderer process. + +IPC_BEGIN_MESSAGES(View) + // Used typically when recovering from a crash. The new rendering process + // sets its global "next page id" counter to the given value. + IPC_MESSAGE_CONTROL1(ViewMsg_SetNextPageID, + int32 /* next_page_id */) + + // Tells the renderer to create a new view. + // This message is slightly different, the view it takes is the view to + // create, the message itself is sent as a non-view control message. + IPC_MESSAGE_CONTROL4(ViewMsg_New, + gfx::NativeViewId, /* parent window */ + ModalDialogEvent, /* model dialog box event */ + WebPreferences, + int32 /* view id */) + + // Tells the renderer to set its maximum cache size to the supplied value + IPC_MESSAGE_CONTROL3(ViewMsg_SetCacheCapacities, + size_t /* min_dead_capacity */, + size_t /* max_dead_capacity */, + size_t /* capacity */) + + // Reply in response to ViewHostMsg_ShowView or ViewHostMsg_ShowWidget. + // similar to the new command, but used when the renderer created a view + // first, and we need to update it + IPC_MESSAGE_ROUTED1(ViewMsg_CreatingNew_ACK, + gfx::NativeViewId /* parent_hwnd */) + + // Tells the render view to close. + IPC_MESSAGE_ROUTED0(ViewMsg_Close) + + // Tells the render view to change its size. A ViewHostMsg_PaintRect message + // is generated in response provided new_size is not empty and not equal to + // the view's current size. The generated ViewHostMsg_PaintRect message will + // have the IS_RESIZE_ACK flag set. It also receives the resizer rect so that + // we don't have to fetch it every time WebKit asks for it. + IPC_MESSAGE_ROUTED2(ViewMsg_Resize, + gfx::Size /* new_size */, + gfx::Rect /* resizer_rect */) + + // Sent to inform the view that it was hidden. This allows it to reduce its + // resource utilization. + IPC_MESSAGE_ROUTED0(ViewMsg_WasHidden) + + // Tells the render view that it is no longer hidden (see WasHidden), and the + // render view is expected to respond with a full repaint if needs_repainting + // is true. In that case, the generated ViewHostMsg_PaintRect message will + // have the IS_RESTORE_ACK flag set. If needs_repainting is false, then this + // message does not trigger a message in response. + IPC_MESSAGE_ROUTED1(ViewMsg_WasRestored, + bool /* needs_repainting */) + + // Tells the render view to capture a thumbnail image of the page. The + // render view responds with a ViewHostMsg_Thumbnail. + IPC_MESSAGE_ROUTED0(ViewMsg_CaptureThumbnail) + + // Tells the render view that a ViewHostMsg_PaintRect message was processed. + // This signals the render view that it can send another PaintRect message. + IPC_MESSAGE_ROUTED0(ViewMsg_PaintRect_ACK) + + // Tells the render view to switch the CSS to print media type, renders every + // requested pages and switch back the CSS to display media type. + IPC_MESSAGE_ROUTED0(ViewMsg_PrintPages) + + // Tells the render view that a ViewHostMsg_ScrollRect message was processed. + // This signals the render view that it can send another ScrollRect message. + IPC_MESSAGE_ROUTED0(ViewMsg_ScrollRect_ACK) + + // Message payload is a blob that should be cast to WebInputEvent + IPC_MESSAGE_ROUTED0(ViewMsg_HandleInputEvent) + + IPC_MESSAGE_ROUTED0(ViewMsg_MouseCaptureLost) + + // TODO(darin): figure out how this meshes with RestoreFocus + IPC_MESSAGE_ROUTED1(ViewMsg_SetFocus, bool /* enable */) + + // Tells the renderer to focus the first (last if reverse is true) focusable + // node. + IPC_MESSAGE_ROUTED1(ViewMsg_SetInitialFocus, bool /* reverse */) + + // Tells the renderer to perform the specified navigation, interrupting any + // existing navigation. + IPC_MESSAGE_ROUTED1(ViewMsg_Navigate, ViewMsg_Navigate_Params) + + IPC_MESSAGE_ROUTED0(ViewMsg_Stop) + + // Tells the renderer to load the specified html text and report a navigation + // to display_url if passing true for new navigation. + IPC_MESSAGE_ROUTED4(ViewMsg_LoadAlternateHTMLText, + std::string /* utf8 html text */, + bool, /* new navigation */ + GURL /* display url */, + std::string /* security info */) + + // This message notifies the renderer that the user has closed the FindInPage + // window (and that the selection should be cleared and the tick-marks + // erased). If |clear_selection| is true, it will also clear the current + // selection. + IPC_MESSAGE_ROUTED1(ViewMsg_StopFinding, bool /* clear_selection */) + + // These messages are typically generated from context menus and request the + // renderer to apply the specified operation to the current selection. + IPC_MESSAGE_ROUTED0(ViewMsg_Undo) + IPC_MESSAGE_ROUTED0(ViewMsg_Redo) + IPC_MESSAGE_ROUTED0(ViewMsg_Cut) + IPC_MESSAGE_ROUTED0(ViewMsg_Copy) + IPC_MESSAGE_ROUTED0(ViewMsg_Paste) + IPC_MESSAGE_ROUTED1(ViewMsg_Replace, std::wstring) + IPC_MESSAGE_ROUTED0(ViewMsg_ToggleSpellCheck) + IPC_MESSAGE_ROUTED0(ViewMsg_Delete) + IPC_MESSAGE_ROUTED0(ViewMsg_SelectAll) + + // Copies the image at location x, y to the clipboard (if there indeed is an + // image at that location). + IPC_MESSAGE_ROUTED2(ViewMsg_CopyImageAt, + int /* x */, + int /* y */) + + // History system notification that the visited link database has been + // replaced. It has one SharedMemoryHandle argument consisting of the table + // handle. This handle is valid in the context of the renderer + IPC_MESSAGE_CONTROL1(ViewMsg_VisitedLink_NewTable, base::SharedMemoryHandle) + + // Notification that the user scripts have been updated. It has one + // SharedMemoryHandle argument consisting of the pickled script data. This + // handle is valid in the context of the renderer. + IPC_MESSAGE_CONTROL1(ViewMsg_UserScripts_NewScripts, base::SharedMemoryHandle) + + // Sent when the user wants to search for a word on the page (find in page). + IPC_MESSAGE_ROUTED3(ViewMsg_Find, + int /* request_id */, + string16 /* search_text */, + WebKit::WebFindOptions) + + // Sent when the headers are available for a resource request. + IPC_MESSAGE_ROUTED2(ViewMsg_Resource_ReceivedResponse, + int /* request_id */, + ResourceResponseHead) + + // Sent as download progress is being made, size of the resource may be + // unknown, in that case |size| is -1. + IPC_MESSAGE_ROUTED3(ViewMsg_Resource_DownloadProgress, + int /* request_id */, + int64 /* position */, + int64 /* size */) + + // Sent as upload progress is being made. + IPC_MESSAGE_ROUTED3(ViewMsg_Resource_UploadProgress, + int /* request_id */, + int64 /* position */, + int64 /* size */) + + // Sent when the request has been redirected. + IPC_MESSAGE_ROUTED2(ViewMsg_Resource_ReceivedRedirect, + int /* request_id */, + GURL /* new_url */) + + // Sent when some data from a resource request is ready. The handle should + // already be mapped into the process that receives this message. + IPC_MESSAGE_ROUTED3(ViewMsg_Resource_DataReceived, + int /* request_id */, + base::SharedMemoryHandle /* data */, + int /* data_len */) + + // Sent when the request has been completed. + IPC_MESSAGE_ROUTED3(ViewMsg_Resource_RequestComplete, + int /* request_id */, + URLRequestStatus /* status */, + std::string /* security info */) + + // Request for the renderer to evaluate an xpath to a frame and execute a + // javascript: url in that frame's context. The message is completely + // asynchronous and no corresponding response message is sent back. + // + // frame_xpath contains the modified xpath notation to identify an inner + // subframe (starting from the root frame). It is a concatenation of + // number of smaller xpaths delimited by '\n'. Each chunk in the string can + // be evaluated to a frame in its parent-frame's context. + // + // Example: /html/body/iframe/\n/html/body/div/iframe/\n/frameset/frame[0] + // can be broken into 3 xpaths + // /html/body/iframe evaluates to an iframe within the root frame + // /html/body/div/iframe evaluates to an iframe within the level-1 iframe + // /frameset/frame[0] evaluates to first frame within the level-2 iframe + // + // jscript_url is the string containing the javascript: url to be executed + // in the target frame's context. The string should start with "javascript:" + // and continue with a valid JS text. + IPC_MESSAGE_ROUTED2(ViewMsg_ScriptEvalRequest, + std::wstring, /* frame_xpath */ + std::wstring /* jscript_url */) + + // Request for the renderer to evaluate an xpath to a frame and insert css + // into that frame's document. See ViewMsg_ScriptEvalRequest for details on + // allowed xpath expressions. + IPC_MESSAGE_ROUTED2(ViewMsg_CSSInsertRequest, + std::wstring, /* frame_xpath */ + std::string /* css string */) + + // Log a message to the console of the target frame + IPC_MESSAGE_ROUTED3(ViewMsg_AddMessageToConsole, + string16 /* frame_xpath */, + string16 /* message */, + WebKit::WebConsoleMessage::Level /* message_level */) + + // Initialize the V8 debugger in the renderer. + IPC_MESSAGE_ROUTED0(ViewMsg_DebugAttach) + + // Shutdown the V8 debugger in the renderer. + IPC_MESSAGE_ROUTED0(ViewMsg_DebugDetach) + + // Break V8 execution. + IPC_MESSAGE_ROUTED1(ViewMsg_DebugBreak, + bool /* force */) + + // Send a command to the V8 debugger. + IPC_MESSAGE_ROUTED1(ViewMsg_DebugCommand, + std::wstring /* cmd */) + + // RenderViewHostDelegate::RenderViewCreated method sends this message to a + // new renderer to notify it that it will host developer tools UI and should + // set up all neccessary bindings and create DevToolsClient instance that + // will handle communication with inspected page DevToolsAgent. + IPC_MESSAGE_ROUTED0(ViewMsg_SetupDevToolsClient) + + // Change the zoom level in the renderer. + IPC_MESSAGE_ROUTED1(ViewMsg_Zoom, + int /* One of PageZoom::Function */) + + // Insert text in the currently focused input area. + IPC_MESSAGE_ROUTED1(ViewMsg_InsertText, + string16 /* text */) + + // Change encoding of page in the renderer. + IPC_MESSAGE_ROUTED1(ViewMsg_SetPageEncoding, + std::wstring /*new encoding name*/) + + // Inspect the element at the specified coordinates + IPC_MESSAGE_ROUTED2(ViewMsg_InspectElement, + int /* x */, + int /* y */) + + // Show the JavaScript console + IPC_MESSAGE_ROUTED0(ViewMsg_ShowJavaScriptConsole) + + // Requests the renderer to reserve a range of page ids. + IPC_MESSAGE_ROUTED1(ViewMsg_ReservePageIDRange, + int /* size_of_range */) + + // Fill a form with data and optionally submit it + IPC_MESSAGE_ROUTED1(ViewMsg_FormFill, + FormData /* form */) + + // Fill a password form and prepare field autocomplete for multiple + // matching logins. + IPC_MESSAGE_ROUTED1(ViewMsg_FillPasswordForm, + PasswordFormDomManager::FillData /* form_data */) + + // D&d drop target messages. + IPC_MESSAGE_ROUTED3(ViewMsg_DragTargetDragEnter, + WebDropData /* drop_data */, + gfx::Point /* client_pt */, + gfx::Point /* screen_pt */) + IPC_MESSAGE_ROUTED2(ViewMsg_DragTargetDragOver, + gfx::Point /* client_pt */, + gfx::Point /* screen_pt */) + IPC_MESSAGE_ROUTED0(ViewMsg_DragTargetDragLeave) + IPC_MESSAGE_ROUTED2(ViewMsg_DragTargetDrop, + gfx::Point /* client_pt */, + gfx::Point /* screen_pt */) + + IPC_MESSAGE_ROUTED1(ViewMsg_UploadFile, ViewMsg_UploadFile_Params) + + // Notifies the renderer of updates in mouse position of an in-progress + // drag. if |ended| is true, then the user has ended the drag operation. + IPC_MESSAGE_ROUTED3(ViewMsg_DragSourceEndedOrMoved, + gfx::Point /* client_pt */, + gfx::Point /* screen_pt */, + bool /* ended */) + + // Notifies the renderer that the system DoDragDrop call has ended. + IPC_MESSAGE_ROUTED0(ViewMsg_DragSourceSystemDragEnded) + + // Used to tell a render view whether it should expose various bindings + // that allow JS content extended privileges. See BindingsPolicy for valid + // flag values. + IPC_MESSAGE_ROUTED1(ViewMsg_AllowBindings, + int /* enabled_bindings_flags */) + + // Tell the renderer to add a property to the DOMUI binding object. This + // only works if we allowed DOMUI bindings. + IPC_MESSAGE_ROUTED2(ViewMsg_SetDOMUIProperty, + std::string /* property_name */, + std::string /* property_value_json */) + + // This message starts/stop monitoring the status of the focused edit + // control of a renderer process. + // Parameters + // * is_active (bool) + // Represents whether or not the IME is active in a browser process. + // The possible actions when a renderer process receives this message are + // listed below: + // Value Action + // true Start sending IPC messages, ViewHostMsg_ImeUpdateStatus + // to notify the status of the focused edit control. + // false Stop sending IPC messages, ViewHostMsg_ImeUpdateStatus. + IPC_MESSAGE_ROUTED1(ViewMsg_ImeSetInputMode, + bool /* is_active */) + + // This message sends a string being composed with IME. + // Parameters + // * string_type (int) + // Represents the type of the 'ime_string' parameter. + // Its possible values and description are listed below: + // Value Description + // -1 The parameter is not used. + // 1 The parameter represents a result string. + // 0 The parameter represents a composition string. + // * cursor_position (int) + // Represents the position of the cursor + // * target_start (int) + // Represents the position of the beginning of the selection + // * target_end (int) + // Represents the position of the end of the selection + // * ime_string (std::wstring) + // Represents the string retrieved from IME (Input Method Editor) + IPC_MESSAGE_ROUTED5(ViewMsg_ImeSetComposition, + int, /* string_type */ + int, /* cursor_position */ + int, /* target_start */ + int, /* target_end */ + std::wstring /* ime_string */ ) + + // This passes a set of webkit preferences down to the renderer. + IPC_MESSAGE_ROUTED1(ViewMsg_UpdateWebPreferences, WebPreferences) + + // Used to notify the render-view that the browser has received a reply for + // the Find operation and is interested in receiving the next one. This is + // used to prevent the renderer from spamming the browser process with + // results. + IPC_MESSAGE_ROUTED0(ViewMsg_FindReplyACK) + + // Used to notify the render-view that we have received a target URL. Used + // to prevent target URLs spamming the browser. + IPC_MESSAGE_ROUTED0(ViewMsg_UpdateTargetURL_ACK) + + // Sets the alternate error page URL (link doctor) for the renderer process. + IPC_MESSAGE_ROUTED1(ViewMsg_SetAltErrorPageURL, GURL) + + // Install the first missing pluign. + IPC_MESSAGE_ROUTED0(ViewMsg_InstallMissingPlugin) + + // Tells the renderer to empty its plugin list cache. + IPC_MESSAGE_CONTROL0(ViewMsg_PurgePluginListCache) + + IPC_MESSAGE_ROUTED1(ViewMsg_RunFileChooserResponse, + std::vector /* selected files */) + + // Used to instruct the RenderView to go into "view source" mode. + IPC_MESSAGE_ROUTED0(ViewMsg_EnableViewSourceMode) + + IPC_MESSAGE_ROUTED2(ViewMsg_UpdateBackForwardListCount, + int /* back_list_count */, + int /* forward_list_count */) + + // Retreive information from the MSAA DOM subtree, for accessibility purposes. + IPC_SYNC_MESSAGE_ROUTED1_1(ViewMsg_GetAccessibilityInfo, + webkit_glue::WebAccessibility::InParams + /* input parameters */, + webkit_glue::WebAccessibility::OutParams + /* output parameters */) + + // Requests the renderer to clear cashed accessibility information. Takes an + // id to clear a specific hashmap entry, and a bool; true clears all, false + // does not. + IPC_MESSAGE_ROUTED2(ViewMsg_ClearAccessibilityInfo, + int /* iaccessible_id */, + bool /* clear_all */) + + // Get all savable resource links from current webpage, include main + // frame and sub-frame. + IPC_MESSAGE_ROUTED1(ViewMsg_GetAllSavableResourceLinksForCurrentPage, + GURL /* url of page which is needed to save */) + + // Get html data by serializing all frames of current page with lists + // which contain all resource links that have local copy. + IPC_MESSAGE_ROUTED3(ViewMsg_GetSerializedHtmlDataForCurrentPageWithLocalLinks, + std::vector /* urls that have local copy */, + std::vector /* paths of local copy */, + FilePath /* local directory path */) + + // Requests application info for the page. The renderer responds back with + // ViewHostMsg_DidGetApplicationInfo. + IPC_MESSAGE_ROUTED1(ViewMsg_GetApplicationInfo, int32 /*page_id*/) + + // Requests the renderer to download the specified image encode it as PNG + // and send the PNG data back ala ViewHostMsg_DidDownloadImage. + IPC_MESSAGE_ROUTED3(ViewMsg_DownloadImage, + int /* identifier for the request */, + GURL /* URL of the image */, + int /* Size of the image. Normally 0, but set if you have + a preferred image size to request, such as when + downloading the favicon */) + + // When a renderer sends a ViewHostMsg_Focus to the browser process, + // the browser has the option of sending a ViewMsg_CantFocus back to + // the renderer. + IPC_MESSAGE_ROUTED0(ViewMsg_CantFocus) + + // Instructs the renderer to invoke the frame's shouldClose method, which + // runs the onbeforeunload event handler. Expects the result to be returned + // via ViewHostMsg_ShouldClose. + IPC_MESSAGE_ROUTED0(ViewMsg_ShouldClose) + + // Instructs the renderer to close the current page, including running the + // onunload event handler. Expects a ClosePage_ACK message when finished. + IPC_MESSAGE_ROUTED2(ViewMsg_ClosePage, + int /* new_render_process_host_id */, + int /* new_request_id */) + + // Asks the renderer to send back stats on the WebCore cache broken down by + // resource types. + IPC_MESSAGE_CONTROL0(ViewMsg_GetCacheResourceStats) + + // Asks the renderer to send back Histograms. + IPC_MESSAGE_CONTROL0(ViewMsg_GetRendererHistograms) + + // Notifies the renderer about ui theme changes + IPC_MESSAGE_ROUTED0(ViewMsg_ThemeChanged) + + // Notifies the renderer that a paint is to be generated for the rectangle + // passed in. + IPC_MESSAGE_ROUTED1(ViewMsg_Repaint, + gfx::Size /* The view size to be repainted */) + + // Posts a message to the renderer. + IPC_MESSAGE_ROUTED3(ViewMsg_HandleMessageFromExternalHost, + std::string /* The message */, + std::string /* The origin */, + std::string /* The target*/) + + // Sent to the renderer when a popup window should no longer count against + // the current popup count (either because it's not a popup or because it was + // a generated by a user action or because a constrained popup got turned + // into a full window). + IPC_MESSAGE_ROUTED0(ViewMsg_DisassociateFromPopupCount) + + // Notifies the renderer of the AppCache that has been selected for a + // a particular context (or frame). This is sent in reply to + // one of the two AppCacheMsg_SelectAppCache messages. + IPC_MESSAGE_CONTROL3(AppCacheMsg_AppCacheSelected, + int /* context_id */, + int /* select_request_id */, + int64 /* cache_id */) + + // Reply to the ViewHostMsg_QueryFormFieldAutofill message with the autofill + // suggestions. + IPC_MESSAGE_ROUTED4(ViewMsg_AutofillSuggestions, + int64 /* id of the text input field */, + int /* id of the request message */, + std::vector /* suggestions */, + int /* index of default suggestion */) + + // Sent by the Browser process to alert a window about whether a blocked + // popup notification is visible. The renderer assumes every new window is a + // blocked popup until notified otherwise. + IPC_MESSAGE_ROUTED1(ViewMsg_PopupNotificationVisiblityChanged, + bool /* Whether it is visible */) + + // Sent by AudioRendererHost to renderer to request an audio packet. + IPC_MESSAGE_ROUTED1(ViewMsg_RequestAudioPacket, + int /* stream id */) + + // Tell the renderer process that the audio stream has been created, renderer + // process would be given a ShareMemoryHandle that it should write to from + // then on. + IPC_MESSAGE_ROUTED3(ViewMsg_NotifyAudioStreamCreated, + int /* stream id */, + base::SharedMemoryHandle /* handle */, + int /* length */) + + // Notification message sent from AudioRendererHost to renderer for state + // update after the renderer has requested a Create/Start/Close. + IPC_MESSAGE_ROUTED3(ViewMsg_NotifyAudioStreamStateChanged, + int /* stream id */, + AudioOutputStream::State /* new state */, + int /* additional information (e.g. platform specific + error code*/) + + IPC_MESSAGE_ROUTED3(ViewMsg_NotifyAudioStreamVolume, + int /* stream id */, + double /* left channel */, + double /* right channel */) + + // Notification that a move or resize renderer's containing window has + // started. + IPC_MESSAGE_ROUTED0(ViewMsg_MoveOrResizeStarted) + + // The browser sends this message when an extension API has a response. + IPC_MESSAGE_ROUTED2(ViewMsg_ExtensionResponse, + int /* callback id */, + std::string /* response */) + + // Tell the extension process about a new channel that has been opened from a + // renderer. source_port_id identifies the port that the extension can + // respond to. tab_json is a JSON value for the tab that opened the + // connection, if any. + IPC_MESSAGE_CONTROL2(ViewMsg_ExtensionHandleConnect, + int /* source_port_id */, + std::string /* tab_json */) + + // Send a javascript message to a renderer from the given port. + IPC_MESSAGE_CONTROL2(ViewMsg_ExtensionHandleMessage, + std::string /* message */, + int /* source_port_id */) + + // Route a browser event to all extension renderers within this process. + IPC_MESSAGE_CONTROL2(ViewMsg_ExtensionHandleEvent, + std::string /* event_name */, + std::string /* event_data */) + + // Tell the renderer process all known extension function names. + IPC_MESSAGE_CONTROL1(ViewMsg_Extension_SetFunctionNames, + std::vector) + + // Changes the text direction of a selected input field. + // * direction (int) + // Represents the new text direction. + // Its possible values are listed below: + // Value New Text Direction + // WEB_TEXT_DIRECTION_DEFAULT NaturalWritingDirection ("inherit") + // WEB_TEXT_DIRECTION_LTR LeftToRightWritingDirection ("rtl") + // WEB_TEXT_DIRECTION_RTL RightToLeftWritingDirection ("ltr") + IPC_MESSAGE_ROUTED1(ViewMsg_SetTextDirection, + int /* direction */) + + // Tells the renderer to clear the focused node (if any). + IPC_MESSAGE_ROUTED0(ViewMsg_ClearFocusedNode) + + // Make the RenderView transparent and render it onto a custom background. The + // background will be tiled in both directions if it is not large enough. + IPC_MESSAGE_ROUTED1(ViewMsg_SetBackground, + SkBitmap /* background */) +IPC_END_MESSAGES(View) + + +//----------------------------------------------------------------------------- +// TabContents messages +// These are messages sent from the renderer to the browser process. + +IPC_BEGIN_MESSAGES(ViewHost) + // Sent by the renderer when it is creating a new window. The browser creates + // a tab for it and responds with a ViewMsg_CreatingNew_ACK. If route_id is + // MSG_ROUTING_NONE, the view couldn't be created. modal_dialog_event is set + // by the browser when a modal dialog is shown. + IPC_SYNC_MESSAGE_CONTROL2_2(ViewHostMsg_CreateWindow, + int /* opener_id */, + bool /* user_gesture */, + int /* route_id */, + ModalDialogEvent /* modal_dialog_event */) + + // Similar to ViewHostMsg_CreateView, except used for sub-widgets, like + // + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_crashing2.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_crashing2.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_crashing2.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_crashing2.html 2010-04-16 17:32:25.000000000 +0100 @@ -0,0 +1,80 @@ + + Plugin crashing + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_crashing.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_crashing.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_crashing.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_crashing.html 2010-04-16 17:32:25.000000000 +0100 @@ -0,0 +1,59 @@ + + Plugin crashing + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_crash_notify_no_report.xul firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_crash_notify_no_report.xul --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_crash_notify_no_report.xul 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_crash_notify_no_report.xul 2010-04-16 17:32:25.000000000 +0100 @@ -0,0 +1,127 @@ + + + + + Plugin Crash Notification Test + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_crash_notify.xul firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_crash_notify.xul --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_crash_notify.xul 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_crash_notify.xul 2010-04-16 17:32:25.000000000 +0100 @@ -0,0 +1,119 @@ + + + + + Plugin Crash Notification Test + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_fullpage.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_fullpage.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_fullpage.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_fullpage.html 2010-04-16 17:32:25.000000000 +0100 @@ -0,0 +1,35 @@ + + Full-page seekable stream + + + + + + +

    + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_GCrace.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_GCrace.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_GCrace.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_GCrace.html 2010-04-16 17:32:25.000000000 +0100 @@ -0,0 +1,59 @@ + + GC race with actors on the parent + + + + + +

    + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_getauthenticationinfo.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_getauthenticationinfo.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_getauthenticationinfo.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_getauthenticationinfo.html 2010-04-16 17:32:25.000000000 +0100 @@ -0,0 +1,75 @@ + + + + Test for Login Manager + + + + + +Test for NPN_GetAuthenticationInfo +

    + +
    + +
    + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_hanging.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_hanging.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_hanging.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_hanging.html 2010-04-16 17:32:25.000000000 +0100 @@ -0,0 +1,75 @@ + + Plugin hanging + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_instantiation.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_instantiation.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_instantiation.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_instantiation.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,32 @@ + + Plugin instantiation + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_multipleinstanceobjects.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_multipleinstanceobjects.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_multipleinstanceobjects.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_multipleinstanceobjects.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,23 @@ + + NPNV*NPObject accessibility tests + + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_newstreamondestroy.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_newstreamondestroy.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_newstreamondestroy.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_newstreamondestroy.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,34 @@ + + NPN_GetURL called from NPP_Destroy + + + + + + +

    + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_npn_asynccall.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_npn_asynccall.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_npn_asynccall.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_npn_asynccall.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,33 @@ + + + NPN_AsyncCallback Tests + + + + + +

    + + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_npn_timers.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_npn_timers.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_npn_timers.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_npn_timers.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,33 @@ + + + NPN_Timer Tests + + + + + +

    + + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_npruntime_npnevaluate.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_npruntime_npnevaluate.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_npruntime_npnevaluate.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_npruntime_npnevaluate.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,98 @@ + + + NPN_Evaluate Tests + + + + + +

    + + + + + + +
    +
    +
    +
    + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_npruntime_npninvokedefault.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_npruntime_npninvokedefault.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_npruntime_npninvokedefault.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_npruntime_npninvokedefault.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,154 @@ + + + NPN_Invoke_Default Tests + + + + + +

    + + + + + + +
    +
    + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_npruntime_npninvoke.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_npruntime_npninvoke.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_npruntime_npninvoke.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_npruntime_npninvoke.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,161 @@ + + + NPN_Invoke Tests + + + + + +

    + + + + + + +
    +
    + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_npruntime_npnsetexception.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_npruntime_npnsetexception.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_npruntime_npnsetexception.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_npruntime_npnsetexception.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,59 @@ + + + NPN_SetException Tests + + + + + +

    + + + + + + +
    +
    + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_err.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_err.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_err.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_err.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,152 @@ + + + + + NPAPI Stream Error Tests + + + + + +
    + Mozilla Bug 517078 - Plugin Stream Error Tests +

    + +
    + +
    + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_geturl.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_geturl.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_geturl.html 2010-04-02 16:58:51.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_geturl.html 2010-04-16 17:32:26.000000000 +0100 @@ -26,3 +26,4 @@ type="application/x-test"> + \ No newline at end of file diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_newstream.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_newstream.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_newstream.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_newstream.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,30 @@ + + + NPAPI NPN_NewStream NPStream Test + + + + + + +

    + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_poststream.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_poststream.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_poststream.html 2010-04-02 16:58:51.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_poststream.html 2010-04-16 17:32:26.000000000 +0100 @@ -26,4 +26,4 @@ id="embedtest" style="width: 400px; height: 100px;" type="application/x-test"> - + \ No newline at end of file diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_seek.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_seek.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_seek.html 2010-04-02 16:58:51.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_seek.html 2010-04-16 17:32:26.000000000 +0100 @@ -28,4 +28,4 @@ id="embedtest" style="width: 400px; height: 100px;" type="application/x-test"> - + \ No newline at end of file diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_src.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_src.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_src.html 2010-04-02 16:58:51.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_pluginstream_src.html 2010-04-16 17:32:26.000000000 +0100 @@ -28,4 +28,4 @@ id="embedtest" style="width: 400px; height: 100px;" type="application/x-test"> - + \ No newline at end of file diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_propertyAndMethod.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_propertyAndMethod.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_propertyAndMethod.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_propertyAndMethod.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,50 @@ + + + NPObject with property and method with the same name + + + + + + + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_streamatclose.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_streamatclose.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_streamatclose.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_streamatclose.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,44 @@ + + + Stream open at NPP_Destroy + + + + + +

    + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_streamNotify.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_streamNotify.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_streamNotify.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_streamNotify.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,93 @@ + + NPN_Get/PostURLNotify tests + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_twostreams.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_twostreams.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/mochitest/test_twostreams.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/mochitest/test_twostreams.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,46 @@ + + + Dual NPAPI NP_ASFILEONLY NPStream Test + + + + + +

    + + + + + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/reftest/div-alpha-opacity.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/reftest/div-alpha-opacity.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/reftest/div-alpha-opacity.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/reftest/div-alpha-opacity.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,27 @@ + + + + + + +
    +
    + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/reftest/plugin-alpha-opacity.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/reftest/plugin-alpha-opacity.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/reftest/plugin-alpha-opacity.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/reftest/plugin-alpha-opacity.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,26 @@ + + + + + + +
    + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/reftest/pluginproblemui-direction-1.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/reftest/pluginproblemui-direction-1.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/reftest/pluginproblemui-direction-1.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/reftest/pluginproblemui-direction-1.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,21 @@ + + + + Plugin Problem UI directionality test + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/reftest/pluginproblemui-direction-ref.html firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/reftest/pluginproblemui-direction-ref.html --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/reftest/pluginproblemui-direction-ref.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/reftest/pluginproblemui-direction-ref.html 2010-04-16 17:32:26.000000000 +0100 @@ -0,0 +1,21 @@ + + + + Plugin Problem UI directionality test + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/reftest/reftest.list firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/reftest/reftest.list --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/reftest/reftest.list 2010-04-02 16:58:51.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/reftest/reftest.list 2010-04-16 17:32:26.000000000 +0100 @@ -2,8 +2,10 @@ random-if(!haveTestPlugin) != plugin-sanity.html about:blank fails-if(!haveTestPlugin) == plugin-sanity.html div-sanity.html fails-if(!haveTestPlugin) == plugin-alpha-zindex.html div-alpha-zindex.html +fails-if(!haveTestPlugin) == plugin-alpha-opacity.html div-alpha-opacity.html fails-if(!haveTestPlugin) == windowless-clipping-1.html windowless-clipping-1-ref.html fails-if(!haveTestPlugin) == border-padding-1.html border-padding-1-ref.html fails-if(!haveTestPlugin) == border-padding-2.html border-padding-2-ref.html +fails-if(!haveTestPlugin) skip-if(!prefs.getBoolPref("dom.ipc.plugins.enabled")) == pluginproblemui-direction-1.html pluginproblemui-direction-ref.html # Disabled for now to investigate Windows/Linux test failures # fails-if(!haveTestPlugin) == border-padding-3.html border-padding-3-ref.html diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/Makefile.in 2010-04-02 16:58:51.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/Makefile.in 2010-04-16 17:32:26.000000000 +0100 @@ -46,9 +46,6 @@ LIBRARY_NAME = nptest MODULE_NAME = TestPlugin -REQUIRES = \ - plugin \ - $(NULL) # Need to custom install plugins NO_DIST_INSTALL = 1 @@ -80,10 +77,17 @@ RCFILE = nptest.rc RESFILE = nptest.res DEFFILE = $(win_srcdir)/nptest.def +OS_LIBS += $(call EXPAND_LIBNAME,msimg32) endif include $(topsrcdir)/config/rules.mk +ifndef __LP64__ +ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa) +EXTRA_DSO_LDOPTS += -framework Carbon +endif +endif + ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2) CXXFLAGS += $(MOZ_GTK2_CFLAGS) CFLAGS += $(MOZ_GTK2_CFLAGS) diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/nptest.cpp firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/nptest.cpp --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/nptest.cpp 2010-04-02 16:58:51.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/nptest.cpp 2010-04-16 17:32:26.000000000 +0100 @@ -44,9 +44,17 @@ #include #ifdef XP_WIN +#include #include +#include +#define getpid _getpid +#else +#include +#include #endif + using namespace std; + #define PLUGIN_NAME "Test Plug-in" #define PLUGIN_DESCRIPTION "Plug-in for testing purposes." #define PLUGIN_VERSION "1.0.0.0" @@ -54,12 +62,54 @@ #define ARRAY_LENGTH(a) (sizeof(a)/sizeof(a[0])) // +// Intentional crash +// + +int gCrashCount = 0; + +static void +NoteIntentionalCrash() +{ + char* bloatLog = getenv("XPCOM_MEM_BLOAT_LOG"); + if (bloatLog) { + char* logExt = strstr(bloatLog, ".log"); + if (logExt) { + bloatLog[strlen(bloatLog) - strlen(logExt)] = '\0'; + } + ostringstream bloatName; + bloatName << bloatLog << "_plugin_pid" << getpid(); + if (logExt) { + bloatName << ".log"; + } + FILE* processfd = fopen(bloatName.str().c_str(), "a"); + fprintf(processfd, "==> process %d will purposefully crash\n", getpid()); + fclose(processfd); + } +} + +static void +IntentionalCrash() +{ + NoteIntentionalCrash(); + + int *pi = NULL; + *pi = 55; // Crash dereferencing null pointer + ++gCrashCount; +} + +// // static data // static NPNetscapeFuncs* sBrowserFuncs = NULL; static NPClass sNPClass; +static void +testplugin_URLNotify(NPP instance, const char* url, NPReason reason, + void* notifyData); +void +asyncCallback(void* cookie); + // // identifiers // @@ -67,8 +117,12 @@ typedef bool (* ScriptableFunction) (NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool npnEvaluateTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool npnInvokeTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool npnInvokeDefaultTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static bool setUndefinedValueTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static bool identifierToStringTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool timerTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static bool queryPrivateModeState(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static bool lastReportedPrivateModeState(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static bool hasWidget(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); @@ -81,13 +135,37 @@ static bool getLastMouseX(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static bool getLastMouseY(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static bool getPaintCount(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool getWidthAtLastPaint(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool getError(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static bool doInternalConsistencyCheck(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static bool setColor(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool throwExceptionNextInvoke(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool convertPointX(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool convertPointY(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool streamTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool crashPlugin(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool crashOnDestroy(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool getObjectValue(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool checkObjectValue(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static bool enableFPExceptions(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool setCookie(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool getCookie(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool getAuthInfo(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool asyncCallbackTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool checkGCRace(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool hangPlugin(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool getClipboardText(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool callOnDestroy(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool reinitWidget(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); +static bool propertyAndMethod(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static const NPUTF8* sPluginMethodIdentifierNames[] = { + "npnEvaluateTest", + "npnInvokeTest", + "npnInvokeDefaultTest", "setUndefinedValueTest", "identifierToStringTest", + "timerTest", "queryPrivateModeState", "lastReportedPrivateModeState", "hasWidget", @@ -100,14 +178,38 @@ "getLastMouseX", "getLastMouseY", "getPaintCount", + "getWidthAtLastPaint", + "getError", "doInternalConsistencyCheck", "setColor", + "throwExceptionNextInvoke", + "convertPointX", + "convertPointY", + "streamTest", + "crash", + "crashOnDestroy", + "getObjectValue", + "checkObjectValue", "enableFPExceptions", + "setCookie", + "getCookie", + "getAuthInfo", + "asyncCallbackTest", + "checkGCRace", + "hang", + "getClipboardText", + "callOnDestroy", + "reinitWidget", + "propertyAndMethod" }; static NPIdentifier sPluginMethodIdentifiers[ARRAY_LENGTH(sPluginMethodIdentifierNames)]; static const ScriptableFunction sPluginMethodFunctions[ARRAY_LENGTH(sPluginMethodIdentifierNames)] = { + npnEvaluateTest, + npnInvokeTest, + npnInvokeDefaultTest, setUndefinedValueTest, identifierToStringTest, + timerTest, queryPrivateModeState, lastReportedPrivateModeState, hasWidget, @@ -120,15 +222,82 @@ getLastMouseX, getLastMouseY, getPaintCount, + getWidthAtLastPaint, + getError, doInternalConsistencyCheck, setColor, + throwExceptionNextInvoke, + convertPointX, + convertPointY, + streamTest, + crashPlugin, + crashOnDestroy, + getObjectValue, + checkObjectValue, enableFPExceptions, + setCookie, + getCookie, + getAuthInfo, + asyncCallbackTest, + checkGCRace, + hangPlugin, + getClipboardText, + callOnDestroy, + reinitWidget, + propertyAndMethod +}; +static const NPUTF8* sPluginPropertyIdentifierNames[] = { + "propertyAndMethod" +}; +static NPIdentifier sPluginPropertyIdentifiers[ARRAY_LENGTH(sPluginPropertyIdentifierNames)]; +static NPVariant sPluginPropertyValues[ARRAY_LENGTH(sPluginPropertyIdentifierNames)]; + +struct URLNotifyData +{ + const char* cookie; + NPObject* callback; + uint32_t size; + char* data; +}; + +static URLNotifyData kNotifyData = { + "static-cookie", + NULL, + 0, + NULL }; -static char* NPN_GetURLNotifyCookie = "NPN_GetURLNotify_Cookie"; +static const char* SUCCESS_STRING = "pass"; static bool sIdentifiersInitialized = false; +#ifdef NPAPI_TIMERS +static uint32_t timerEventCount = 0; + +struct timerEvent { + int32_t timerIdReceive; + int32_t timerIdSchedule; + uint32_t timerInterval; + bool timerRepeat; + int32_t timerIdUnschedule; +}; +static timerEvent timerEvents[] = { + {-1, 0, 200, false, -1}, + {0, 0, 400, false, -1}, + {0, 0, 200, true, -1}, + {0, 1, 100, true, -1}, + {1, -1, 0, false, -1}, + {0, -1, 0, false, -1}, + {1, -1, 0, false, -1}, + {1, -1, 0, false, -1}, + {0, -1, 0, false, 0}, + {1, 2, 600, false, 1}, + {2, -1, 0, false, 2}, +}; +static uint32_t totalTimerEvents = sizeof(timerEvents) / sizeof(timerEvent); + +#endif // NPAPI_TIMERS + /** * Incremented for every startWatchingInstanceCount. */ @@ -149,7 +318,15 @@ if (!sIdentifiersInitialized) { NPN_GetStringIdentifiers(sPluginMethodIdentifierNames, ARRAY_LENGTH(sPluginMethodIdentifierNames), sPluginMethodIdentifiers); + NPN_GetStringIdentifiers(sPluginPropertyIdentifierNames, + ARRAY_LENGTH(sPluginPropertyIdentifierNames), sPluginPropertyIdentifiers); + sIdentifiersInitialized = true; + + // Check whether NULL is handled in NPN_GetStringIdentifiers + NPIdentifier IDList[2]; + static char const *const kIDNames[2] = { NULL, "setCookie" }; + NPN_GetStringIdentifiers(const_cast(kIDNames), 2, IDList); } } @@ -157,6 +334,9 @@ { memset(sPluginMethodIdentifiers, 0, ARRAY_LENGTH(sPluginMethodIdentifiers) * sizeof(NPIdentifier)); + memset(sPluginPropertyIdentifiers, 0, + ARRAY_LENGTH(sPluginPropertyIdentifiers) * sizeof(NPIdentifier)); + sIdentifiersInitialized = false; } @@ -198,12 +378,13 @@ outbuf.append("Error: no data in buffer"); } - if (instanceData->npnNewStream) { + if (instanceData->npnNewStream && + instanceData->err.str().length() == 0) { + char typeHTML[] = "text/html"; NPStream* stream; printf("calling NPN_NewStream..."); - NPError err = NPN_NewStream(instance, "text/html", - instanceData->frame.c_str(), - &stream); + NPError err = NPN_NewStream(instance, typeHTML, + instanceData->frame.c_str(), &stream); printf("return value %d\n", err); if (err != NPERR_NO_ERROR) { instanceData->err << "NPN_NewStream returned " << err; @@ -216,15 +397,12 @@ int32_t numBytes = (bytesToWrite - bytesWritten) < instanceData->streamChunkSize ? bytesToWrite - bytesWritten : instanceData->streamChunkSize; - printf("calling NPN_Write, %d bytes\n", numBytes); int32_t written = NPN_Write(instance, stream, numBytes, (void*)(outbuf.c_str() + bytesWritten)); - /* Ignore this for now, NPN_Write always returns 0, see bug 484729 if (written <= 0) { instanceData->err << "NPN_Write returned " << written; break; } - */ bytesWritten += numBytes; printf("%d bytes written, total %d\n", written, bytesWritten); } @@ -235,7 +413,7 @@ } else { // Convert CRLF to LF, and escape most other non-alphanumeric chars. - for (int i = 0; i < outbuf.length(); i++) { + for (size_t i = 0; i < outbuf.length(); i++) { if (outbuf[i] == '\n') { outbuf.replace(i, 1, "%0a"); i += 2; @@ -265,6 +443,45 @@ } } +TestFunction +getFuncFromString(const char* funcname) +{ + FunctionTable funcTable[] = + { + { FUNCTION_NPP_NEWSTREAM, "npp_newstream" }, + { FUNCTION_NPP_WRITEREADY, "npp_writeready" }, + { FUNCTION_NPP_WRITE, "npp_write" }, + { FUNCTION_NPP_DESTROYSTREAM, "npp_destroystream" }, + { FUNCTION_NPP_WRITE_RPC, "npp_write_rpc" }, + { FUNCTION_NONE, NULL } + }; + int32_t i = 0; + while(funcTable[i].funcName) { + if (!strcmp(funcname, funcTable[i].funcName)) return funcTable[i].funcId; + i++; + } + return FUNCTION_NONE; +} + +static void +DuplicateNPVariant(NPVariant& aDest, const NPVariant& aSrc) +{ + if (NPVARIANT_IS_STRING(aSrc)) { + NPString src = NPVARIANT_TO_STRING(aSrc); + char* buf = new char[src.UTF8Length]; + strncpy(buf, src.UTF8Characters, src.UTF8Length); + STRINGN_TO_NPVARIANT(buf, src.UTF8Length, aDest); + } + else if (NPVARIANT_IS_OBJECT(aSrc)) { + NPObject* obj = + NPN_RetainObject(NPVARIANT_TO_OBJECT(aSrc)); + OBJECT_TO_NPVARIANT(obj, aDest); + } + else { + aDest = aSrc; + } +} + // // function signatures // @@ -335,7 +552,7 @@ pFuncs->write = NPP_Write; pFuncs->print = NPP_Print; pFuncs->event = NPP_HandleEvent; - pFuncs->urlnotify = NPP_URLNotify; + pFuncs->urlnotify = testplugin_URLNotify; pFuncs->getvalue = NPP_GetValue; pFuncs->setvalue = NPP_SetValue; } @@ -352,6 +569,10 @@ initializeIdentifiers(); + for (int i = 0; i < ARRAY_LENGTH(sPluginPropertyValues); i++) { + VOID_TO_NPVARIANT(sPluginPropertyValues[i]); + } + memset(&sNPClass, 0, sizeof(NPClass)); sNPClass.structVersion = NP_CLASS_STRUCT_VERSION; sNPClass.allocate = (NPAllocateFunctionPtr)scriptableAllocate; @@ -394,12 +615,23 @@ { clearIdentifiers(); + for (int i = 0; i < ARRAY_LENGTH(sPluginPropertyValues); i++) { + NPN_ReleaseVariantValue(&sPluginPropertyValues[i]); + } + return NPERR_NO_ERROR; } NPError NPP_New(NPMIMEType pluginType, NPP instance, uint16_t mode, int16_t argc, char* argn[], char* argv[], NPSavedData* saved) { + // Make sure our pdata field is NULL at this point. If it isn't, that + // probably means the browser gave us uninitialized memory. + if (instance->pdata) { + printf("NPP_New called with non-NULL NPP->pdata pointer!\n"); + return NPERR_GENERIC_ERROR; + } + // Make sure we can render this plugin NPBool browserSupportsWindowless = false; NPN_GetValue(instance, NPNVSupportsWindowless, &browserSupportsWindowless); @@ -415,15 +647,22 @@ instanceData->npp = instance; instanceData->streamMode = NP_ASFILEONLY; instanceData->testFunction = FUNCTION_NONE; + instanceData->functionToFail = FUNCTION_NONE; + instanceData->failureCode = 0; + instanceData->callOnDestroy = NULL; instanceData->streamChunkSize = 1024; instanceData->streamBuf = NULL; instanceData->streamBufSize = 0; instanceData->fileBuf = NULL; instanceData->fileBufSize = 0; + instanceData->throwOnNextInvoke = false; instanceData->testrange = NULL; instanceData->hasWidget = false; instanceData->npnNewStream = false; + instanceData->writeCount = 0; + instanceData->writeReadyCount = 0; memset(&instanceData->window, 0, sizeof(instanceData->window)); + instanceData->crashOnDestroy = false; instance->pdata = instanceData; TestNPObject* scriptableObject = (TestNPObject*)NPN_CreateObject(instance, &sNPClass); @@ -432,14 +671,19 @@ free(instanceData); return NPERR_GENERIC_ERROR; } - NPN_RetainObject(scriptableObject); scriptableObject->npp = instance; scriptableObject->drawMode = DM_DEFAULT; scriptableObject->drawColor = 0; instanceData->scriptableObject = scriptableObject; instanceData->instanceCountWatchGeneration = sCurrentInstanceCountWatchGeneration; - + + if (NP_FULL == mode) { + instanceData->streamMode = NP_SEEK; + instanceData->frame = "testframe"; + addRange(instanceData, "100,100"); + } + bool requestWindow = false; // handle extra params for (int i = 0; i < argc; i++) { @@ -473,6 +717,12 @@ if (strcmp(argn[i], "streamchunksize") == 0) { instanceData->streamChunkSize = atoi(argv[i]); } + if (strcmp(argn[i], "failurecode") == 0) { + instanceData->failureCode = atoi(argv[i]); + } + if (strcmp(argn[i], "functiontofail") == 0) { + instanceData->functionToFail = getFuncFromString(argv[i]); + } if (strcmp(argn[i], "geturl") == 0) { instanceData->testUrl = argv[i]; instanceData->testFunction = FUNCTION_NPP_GETURL; @@ -498,7 +748,7 @@ } if (strcmp(argn[i], "range") == 0) { string range = argv[i]; - int16_t semicolon = range.find(';'); + size_t semicolon = range.find(';'); while (semicolon != string::npos) { addRange(instanceData, range.substr(0, semicolon).c_str()); if (semicolon == range.length()) { @@ -514,6 +764,9 @@ strcmp(argv[i], "true") == 0) { instanceData->npnNewStream = true; } + if (strcmp(argn[i], "newcrash") == 0) { + IntentionalCrash(); + } } if (!browserSupportsWindowless || !pluginSupportsWindowlessMode()) { @@ -536,6 +789,7 @@ instanceData->lastReportedPrivateModeState = false; instanceData->lastMouseX = instanceData->lastMouseY = -1; + instanceData->widthAtLastPaint = -1; instanceData->paintCount = 0; // do platform-specific initialization @@ -578,7 +832,7 @@ } else if (instanceData->testFunction == FUNCTION_NPP_GETURLNOTIFY) { NPError err = NPN_GetURLNotify(instance, instanceData->testUrl.c_str(), - NULL, (void *)NPN_GetURLNotifyCookie); + NULL, static_cast(&kNotifyData)); if (err != NPERR_NO_ERROR) { instanceData->err << "NPN_GetURLNotify returned " << err; } @@ -590,8 +844,19 @@ NPError NPP_Destroy(NPP instance, NPSavedData** save) { + printf("NPP_Destroy\n"); InstanceData* instanceData = (InstanceData*)(instance->pdata); + if (instanceData->crashOnDestroy) + IntentionalCrash(); + + if (instanceData->callOnDestroy) { + NPVariant result; + NPN_InvokeDefault(instance, instanceData->callOnDestroy, NULL, 0, &result); + NPN_ReleaseVariantValue(&result); + NPN_ReleaseObject(instanceData->callOnDestroy); + } + if (instanceData->streamBuf) { free(instanceData->streamBuf); } @@ -635,14 +900,31 @@ { printf("NPP_NewStream\n"); InstanceData* instanceData = (InstanceData*)(instance->pdata); - *stype = instanceData->streamMode; + + if (instanceData->functionToFail == FUNCTION_NPP_NEWSTREAM && + instanceData->failureCode) { + instanceData->err << SUCCESS_STRING; + if (instanceData->frame.length() > 0) { + sendBufferToFrame(instance); + } + return instanceData->failureCode; + } - if (instanceData->streamBufSize) { - free(instanceData->streamBuf); - instanceData->streamBufSize = 0; - if (instanceData->testFunction == FUNCTION_NPP_POSTURL && - instanceData->postMode == POSTMODE_STREAM) { - instanceData->testFunction = FUNCTION_NPP_GETURL; + if (stream->notifyData && + static_cast(stream->notifyData) != &kNotifyData) { + // stream from streamTest + *stype = NP_NORMAL; + } + else { + *stype = instanceData->streamMode; + + if (instanceData->streamBufSize) { + free(instanceData->streamBuf); + instanceData->streamBufSize = 0; + if (instanceData->testFunction == FUNCTION_NPP_POSTURL && + instanceData->postMode == POSTMODE_STREAM) { + instanceData->testFunction = FUNCTION_NPP_GETURL; + } } } return NPERR_NO_ERROR; @@ -653,9 +935,48 @@ { printf("NPP_DestroyStream\n"); InstanceData* instanceData = (InstanceData*)(instance->pdata); - if (instanceData->streamMode == NP_ASFILE) { + + if (instanceData->functionToFail == FUNCTION_NPP_NEWSTREAM) { + instanceData->err << "NPP_DestroyStream called"; + } + + if (instanceData->functionToFail == FUNCTION_NPP_WRITE) { + if (instanceData->writeCount == 1) + instanceData->err << SUCCESS_STRING; + else + instanceData->err << "NPP_Write called after returning -1"; + } + + if (instanceData->functionToFail == FUNCTION_NPP_DESTROYSTREAM && + instanceData->failureCode) { + instanceData->err << SUCCESS_STRING; + if (instanceData->frame.length() > 0) { + sendBufferToFrame(instance); + } + return instanceData->failureCode; + } + + URLNotifyData* nd = static_cast(stream->notifyData); + if (nd && nd != &kNotifyData) { + return NPERR_NO_ERROR; + } + + if (instanceData->streamMode == NP_ASFILE && + instanceData->functionToFail == FUNCTION_NONE) { + if (!instanceData->streamBuf) { + instanceData->err << + "Error: no data written with NPP_Write"; + return NPERR_GENERIC_ERROR; + } + + if (!instanceData->fileBuf) { + instanceData->err << + "Error: no data written with NPP_StreamAsFile"; + return NPERR_GENERIC_ERROR; + } + if (strcmp(reinterpret_cast(instanceData->fileBuf), - reinterpret_cast(instanceData->streamBuf)) != 0) { + reinterpret_cast(instanceData->streamBuf))) { instanceData->err << "Error: data passed to NPP_Write and NPP_StreamAsFile differed"; } @@ -679,7 +1000,18 @@ int32_t NPP_WriteReady(NPP instance, NPStream* stream) { + printf("NPP_WriteReady\n"); InstanceData* instanceData = (InstanceData*)(instance->pdata); + instanceData->writeReadyCount++; + if (instanceData->functionToFail == FUNCTION_NPP_NEWSTREAM) { + instanceData->err << "NPP_WriteReady called"; + } + + // temporarily disabled per bug 519870 + //if (instanceData->writeReadyCount == 1) { + // return 0; + //} + return instanceData->streamChunkSize; } @@ -688,12 +1020,46 @@ { printf("NPP_Write, offset=%d, len=%d, end=%d\n", offset, len, stream->end); InstanceData* instanceData = (InstanceData*)(instance->pdata); + instanceData->writeCount++; + + // temporarily disabled per bug 519870 + //if (instanceData->writeReadyCount == 1) { + // instanceData->err << "NPP_Write called even though NPP_WriteReady " << + // "returned 0"; + //} + + if (instanceData->functionToFail == FUNCTION_NPP_WRITE_RPC) { + // Make an RPC call and pretend to consume the data + NPObject* windowObject = NULL; + NPN_GetValue(instance, NPNVWindowNPObject, &windowObject); + if (windowObject) + NPN_ReleaseObject(windowObject); + + return len; + } + + if (instanceData->functionToFail == FUNCTION_NPP_NEWSTREAM) { + instanceData->err << "NPP_Write called"; + } + + if (instanceData->functionToFail == FUNCTION_NPP_WRITE) { + return -1; + } + + URLNotifyData* nd = static_cast(stream->notifyData); + if (nd && nd != &kNotifyData) { + uint32_t newsize = nd->size + len; + nd->data = (char*) realloc(nd->data, newsize); + memcpy(nd->data + nd->size, buffer, len); + nd->size = newsize; + return len; + } // If the complete stream has been written, and we're doing a seek test, // then call NPN_RequestRead. - if (instanceData->streamMode == NP_SEEK && + if (instanceData->streamMode == NP_SEEK && stream->end != 0 && - stream->end == (instanceData->streamBufSize + len)) { + stream->end == ((uint32_t)instanceData->streamBufSize + len)) { // prevent recursion instanceData->streamMode = NP_NORMAL; @@ -719,7 +1085,7 @@ bool stillwaiting = false; while(range != NULL) { if (offset == range->offset && - len == range->length) { + (uint32_t)len == range->length) { range->waiting = false; } if (range->waiting) stillwaiting = true; @@ -730,9 +1096,6 @@ if (err != NPERR_NO_ERROR) { instanceData->err << "Error: NPN_DestroyStream returned " << err; } - if (instanceData->frame.length() > 0) { - sendBufferToFrame(instance); - } } } else { @@ -761,6 +1124,11 @@ InstanceData* instanceData = (InstanceData*)(instance->pdata); + if (instanceData->functionToFail == FUNCTION_NPP_NEWSTREAM || + instanceData->functionToFail == FUNCTION_NPP_WRITE) { + instanceData->err << "NPP_StreamAsFile called"; + } + if (!fname) return; @@ -770,15 +1138,15 @@ size = ftell(file); instanceData->fileBuf = malloc((int32_t)size + 1); char* buf = reinterpret_cast(instanceData->fileBuf); - if (fseek(file, 0, SEEK_SET)) printf("error during fseek %d\n", ferror(file)); + fseek(file, 0, SEEK_SET); fread(instanceData->fileBuf, 1, size, file); fclose(file); buf[size] = '\0'; instanceData->fileBufSize = (int32_t)size; } else { - instanceData->err << "Unable to open file " << fname; printf("Unable to open file\n"); + instanceData->err << "Unable to open file " << fname; } } @@ -795,17 +1163,39 @@ } void -NPP_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData) +testplugin_URLNotify(NPP instance, const char* url, NPReason reason, void* notifyData) { InstanceData* instanceData = (InstanceData*)(instance->pdata); + URLNotifyData* ndata = static_cast(notifyData); + printf("NPP_URLNotify called\n"); - if (strcmp((char*)notifyData, NPN_GetURLNotifyCookie) != 0) { + if (&kNotifyData == ndata) { + if (instanceData->frame.length() > 0) { + sendBufferToFrame(instance); + } + } + else if (!strcmp(ndata->cookie, "dynamic-cookie")) { + NPVariant args[2]; + NPVariant result; + INT32_TO_NPVARIANT(reason, args[0]); + + if (ndata->data) + STRINGN_TO_NPVARIANT(ndata->data, ndata->size, args[1]); + else + STRINGN_TO_NPVARIANT("", 0, args[1]); + + NPN_InvokeDefault(instance, ndata->callback, args, 2, &result); + NPN_ReleaseVariantValue(&result); + + // clean up the URLNotifyData + NPN_ReleaseObject(ndata->callback); + free(ndata->data); + delete ndata; + } + else { printf("ERROR! NPP_URLNotify called with wrong cookie\n"); instanceData->err << "Error: NPP_URLNotify called with wrong cookie"; } - if (instanceData->frame.length() > 0) { - sendBufferToFrame(instance); - } } NPError @@ -914,6 +1304,12 @@ return sBrowserFuncs->invoke(npp, obj, methodName, args, argCount, result); } +bool +NPN_InvokeDefault(NPP npp, NPObject* obj, const NPVariant *args, uint32_t argCount, NPVariant *result) +{ + return sBrowserFuncs->invokeDefault(npp, obj, args, argCount, result); +} + const char* NPN_UserAgent(NPP instance) { @@ -944,6 +1340,20 @@ return sBrowserFuncs->memfree(ptr); } +#ifdef NPAPI_TIMERS +uint32_t +NPN_ScheduleTimer(NPP instance, uint32_t interval, NPBool repeat, void (*timerFunc)(NPP npp, uint32_t timerID)) +{ + return sBrowserFuncs->scheduletimer(instance, interval, repeat, timerFunc); +} + +void +NPN_UnscheduleTimer(NPP instance, uint32_t timerID) +{ + return sBrowserFuncs->unscheduletimer(instance, timerID); +} +#endif // NPAPI_TIMERS + void NPN_ReleaseVariantValue(NPVariant *variant) { @@ -1008,6 +1418,77 @@ return sBrowserFuncs->write(instance, stream, len, buf); } +bool +NPN_Enumerate(NPP instance, + NPObject *npobj, + NPIdentifier **identifiers, + uint32_t *identifierCount) +{ + return sBrowserFuncs->enumerate(instance, npobj, identifiers, + identifierCount); +} + +bool +NPN_GetProperty(NPP instance, + NPObject *npobj, + NPIdentifier propertyName, + NPVariant *result) +{ + return sBrowserFuncs->getproperty(instance, npobj, propertyName, result); +} + +bool +NPN_Evaluate(NPP instance, NPObject *npobj, NPString *script, NPVariant *result) +{ + return sBrowserFuncs->evaluate(instance, npobj, script, result); +} + +void +NPN_SetException(NPObject *npobj, const NPUTF8 *message) +{ + return sBrowserFuncs->setexception(npobj, message); +} + +#ifdef NPAPI_CONVERTPOINT +NPBool +NPN_ConvertPoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace) +{ + return sBrowserFuncs->convertpoint(instance, sourceX, sourceY, sourceSpace, destX, destY, destSpace); +} +#endif // NPAPI_CONVERTPOINT + +NPError +NPN_SetValueForURL(NPP instance, NPNURLVariable variable, const char *url, const char *value, uint32_t len) +{ + return sBrowserFuncs->setvalueforurl(instance, variable, url, value, len); +} + +NPError +NPN_GetValueForURL(NPP instance, NPNURLVariable variable, const char *url, char **value, uint32_t *len) +{ + return sBrowserFuncs->getvalueforurl(instance, variable, url, value, len); +} + +NPError +NPN_GetAuthenticationInfo(NPP instance, + const char *protocol, + const char *host, int32_t port, + const char *scheme, + const char *realm, + char **username, uint32_t *ulen, + char **password, + uint32_t *plen) +{ + return sBrowserFuncs->getauthenticationinfo(instance, protocol, host, port, scheme, realm, + username, ulen, password, plen); +} + +void +NPN_PluginThreadAsyncCall(NPP plugin, void (*func)(void*), void* userdata) +{ + return sBrowserFuncs->pluginthreadasynccall(plugin, func, userdata); +} + // // npruntime object functions // @@ -1046,6 +1527,22 @@ bool scriptableInvoke(NPObject* npobj, NPIdentifier name, const NPVariant* args, uint32_t argCount, NPVariant* result) { + NPP npp = static_cast(npobj)->npp; + InstanceData* id = static_cast(npp->pdata); + if (id->throwOnNextInvoke) { + id->throwOnNextInvoke = false; + if (argCount == 0) { + NPN_SetException(npobj, NULL); + } + else { + for (uint32_t i = 0; i < argCount; i++) { + const NPString* argstr = &NPVARIANT_TO_STRING(args[i]); + NPN_SetException(npobj, argstr->UTF8Characters); + } + } + return false; + } + for (int i = 0; i < int(ARRAY_LENGTH(sPluginMethodIdentifiers)); i++) { if (name == sPluginMethodIdentifiers[i]) return sPluginMethodFunctions[i](npobj, args, argCount, result); @@ -1056,30 +1553,92 @@ bool scriptableInvokeDefault(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) { - return false; + NPP npp = static_cast(npobj)->npp; + InstanceData* id = static_cast(npp->pdata); + if (id->throwOnNextInvoke) { + id->throwOnNextInvoke = false; + if (argCount == 0) { + NPN_SetException(npobj, NULL); + } + else { + for (uint32_t i = 0; i < argCount; i++) { + const NPString* argstr = &NPVARIANT_TO_STRING(args[i]); + NPN_SetException(npobj, argstr->UTF8Characters); + } + } + return false; + } + + ostringstream value; + value << PLUGIN_NAME; + for (uint32_t i = 0; i < argCount; i++) { + switch(args[i].type) { + case NPVariantType_Int32: + value << ";" << NPVARIANT_TO_INT32(args[i]); + break; + case NPVariantType_String: { + const NPString* argstr = &NPVARIANT_TO_STRING(args[i]); + value << ";" << argstr->UTF8Characters; + break; + } + case NPVariantType_Void: + value << ";undefined"; + break; + case NPVariantType_Null: + value << ";null"; + break; + default: + value << ";other"; + } + } + STRINGZ_TO_NPVARIANT(strdup(value.str().c_str()), *result); + return true; } bool scriptableHasProperty(NPObject* npobj, NPIdentifier name) { + for (int i = 0; i < int(ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) { + if (name == sPluginPropertyIdentifiers[i]) + return true; + } return false; } bool scriptableGetProperty(NPObject* npobj, NPIdentifier name, NPVariant* result) { + for (int i = 0; i < int(ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) { + if (name == sPluginPropertyIdentifiers[i]) { + DuplicateNPVariant(*result, sPluginPropertyValues[i]); + return true; + } + } return false; } bool scriptableSetProperty(NPObject* npobj, NPIdentifier name, const NPVariant* value) { + for (int i = 0; i < int(ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) { + if (name == sPluginPropertyIdentifiers[i]) { + NPN_ReleaseVariantValue(&sPluginPropertyValues[i]); + DuplicateNPVariant(sPluginPropertyValues[i], *value); + return true; + } + } return false; } bool scriptableRemoveProperty(NPObject* npobj, NPIdentifier name) { + for (int i = 0; i < int(ARRAY_LENGTH(sPluginPropertyIdentifiers)); i++) { + if (name == sPluginPropertyIdentifiers[i]) { + NPN_ReleaseVariantValue(&sPluginPropertyValues[i]); + return true; + } + } return false; } @@ -1100,58 +1659,265 @@ // static bool -setUndefinedValueTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +compareVariants(NPP instance, const NPVariant* var1, const NPVariant* var2) { - NPP npp = static_cast(npobj)->npp; - NPError err = NPN_SetValue(npp, (NPPVariable)0x0, 0x0); - BOOLEAN_TO_NPVARIANT((err == NPERR_NO_ERROR), *result); - return true; + bool success = true; + InstanceData* id = static_cast(instance->pdata); + if (var1->type != var2->type) { + id->err << "Variant types don't match; got " << var1->type << + " expected " << var2->type; + return false; + } + + switch (var1->type) { + case NPVariantType_Int32: { + int32_t result = NPVARIANT_TO_INT32(*var1); + int32_t expected = NPVARIANT_TO_INT32(*var2); + if (result != expected) { + id->err << "Variant values don't match; got " << result << + " expected " << expected; + success = false; + } + break; + } + case NPVariantType_Double: { + double result = NPVARIANT_TO_DOUBLE(*var1); + double expected = NPVARIANT_TO_DOUBLE(*var2); + if (result != expected) { + id->err << "Variant values don't match (double)"; + success = false; + } + break; + } + case NPVariantType_Void: { + // void values are always equivalent + break; + } + case NPVariantType_Null: { + // null values are always equivalent + break; + } + case NPVariantType_Bool: { + bool result = NPVARIANT_TO_BOOLEAN(*var1); + bool expected = NPVARIANT_TO_BOOLEAN(*var2); + if (result != expected) { + id->err << "Variant values don't match (bool)"; + success = false; + } + break; + } + case NPVariantType_String: { + const NPString* result = &NPVARIANT_TO_STRING(*var1); + const NPString* expected = &NPVARIANT_TO_STRING(*var2); + if (strcmp(result->UTF8Characters, expected->UTF8Characters) || + strlen(result->UTF8Characters) != strlen(expected->UTF8Characters)) { + id->err << "Variant values don't match; got " << + result->UTF8Characters << " expected " << + expected->UTF8Characters; + success = false; + } + break; + } + case NPVariantType_Object: { + uint32_t i, identifierCount = 0; + NPIdentifier* identifiers; + NPObject* result = NPVARIANT_TO_OBJECT(*var1); + NPObject* expected = NPVARIANT_TO_OBJECT(*var2); + bool enumerate_result = NPN_Enumerate(instance, expected, + &identifiers, &identifierCount); + if (!enumerate_result) { + id->err << "NPN_Enumerate failed"; + success = false; + } + for (i = 0; i < identifierCount; i++) { + NPVariant resultVariant, expectedVariant; + if (!NPN_GetProperty(instance, expected, identifiers[i], + &expectedVariant)) { + id->err << "NPN_GetProperty returned false"; + success = false; + } + else { + if (!NPN_HasProperty(instance, result, identifiers[i])) { + id->err << "NPN_HasProperty returned false"; + success = false; + } + else { + if (!NPN_GetProperty(instance, result, identifiers[i], + &resultVariant)) { + id->err << "NPN_GetProperty 2 returned false"; + success = false; + } + else { + success = compareVariants(instance, &resultVariant, + &expectedVariant); + NPN_ReleaseVariantValue(&expectedVariant); + } + } + NPN_ReleaseVariantValue(&resultVariant); + } + } + break; + } + default: + id->err << "Unknown variant type"; + success = false; + } + + return success; } static bool -identifierToStringTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +throwExceptionNextInvoke(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) { - if (argCount != 1) - return false; - NPIdentifier identifier = variantToIdentifier(args[0]); - if (!identifier) - return false; - - NPUTF8* utf8String = NPN_UTF8FromIdentifier(identifier); - if (!utf8String) - return false; - STRINGZ_TO_NPVARIANT(utf8String, *result); - return true; + NPP npp = static_cast(npobj)->npp; + InstanceData* id = static_cast(npp->pdata); + id->throwOnNextInvoke = true; + BOOLEAN_TO_NPVARIANT(true, *result); + return true; } static bool -queryPrivateModeState(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +npnInvokeDefaultTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) { - if (argCount != 0) - return false; + bool success = false; + NPP npp = static_cast(npobj)->npp; + + NPObject* windowObject; + NPN_GetValue(npp, NPNVWindowNPObject, &windowObject); + if (!windowObject) + return false; + + NPIdentifier objectIdentifier = variantToIdentifier(args[0]); + if (!objectIdentifier) + return false; + + NPVariant objectVariant; + if (NPN_GetProperty(npp, windowObject, objectIdentifier, + &objectVariant)) { + if (NPVARIANT_IS_OBJECT(objectVariant)) { + NPObject* selfObject = NPVARIANT_TO_OBJECT(objectVariant); + if (selfObject != NULL) { + NPVariant resultVariant; + if (NPN_InvokeDefault(npp, selfObject, argCount > 1 ? &args[1] : NULL, + argCount - 1, &resultVariant)) { + *result = resultVariant; + success = true; + } + } + } + NPN_ReleaseVariantValue(&objectVariant); + } - NPBool pms = false; - NPN_GetValue(static_cast(npobj)->npp, NPNVprivateModeBool, &pms); - BOOLEAN_TO_NPVARIANT(pms, *result); - return true; + NPN_ReleaseObject(windowObject); + return success; } static bool -lastReportedPrivateModeState(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +npnInvokeTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) { - if (argCount != 0) + NPP npp = static_cast(npobj)->npp; + InstanceData* id = static_cast(npp->pdata); + id->err.str(""); + if (argCount < 2) return false; - InstanceData* id = static_cast(static_cast(npobj)->npp->pdata); - BOOLEAN_TO_NPVARIANT(id->lastReportedPrivateModeState, *result); + NPIdentifier function = variantToIdentifier(args[0]); + if (!function) + return false; + + NPObject* windowObject; + NPN_GetValue(npp, NPNVWindowNPObject, &windowObject); + if (!windowObject) + return false; + + NPVariant invokeResult; + bool invokeReturn = NPN_Invoke(npp, windowObject, function, + argCount > 2 ? &args[2] : NULL, argCount - 2, &invokeResult); + + bool compareResult = compareVariants(npp, &invokeResult, &args[1]); + + NPN_ReleaseObject(windowObject); + NPN_ReleaseVariantValue(&invokeResult); + BOOLEAN_TO_NPVARIANT(invokeReturn && compareResult, *result); return true; } static bool -hasWidget(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +npnEvaluateTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) { - if (argCount != 0) - return false; + bool success = false; + NPP npp = static_cast(npobj)->npp; + + if (argCount != 1) + return false; + + if (!NPVARIANT_IS_STRING(args[0])) + return false; + + NPObject* windowObject; + NPN_GetValue(npp, NPNVWindowNPObject, &windowObject); + if (!windowObject) + return false; + + success = NPN_Evaluate(npp, windowObject, (NPString*)&NPVARIANT_TO_STRING(args[0]), result); + + NPN_ReleaseObject(windowObject); + return success; +} + +static bool +setUndefinedValueTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + NPP npp = static_cast(npobj)->npp; + NPError err = NPN_SetValue(npp, (NPPVariable)0x0, 0x0); + BOOLEAN_TO_NPVARIANT((err == NPERR_NO_ERROR), *result); + return true; +} + +static bool +identifierToStringTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + if (argCount != 1) + return false; + NPIdentifier identifier = variantToIdentifier(args[0]); + if (!identifier) + return false; + + NPUTF8* utf8String = NPN_UTF8FromIdentifier(identifier); + if (!utf8String) + return false; + STRINGZ_TO_NPVARIANT(utf8String, *result); + return true; +} + +static bool +queryPrivateModeState(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + if (argCount != 0) + return false; + + NPBool pms = false; + NPN_GetValue(static_cast(npobj)->npp, NPNVprivateModeBool, &pms); + BOOLEAN_TO_NPVARIANT(pms, *result); + return true; +} + +static bool +lastReportedPrivateModeState(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + if (argCount != 0) + return false; + + InstanceData* id = static_cast(static_cast(npobj)->npp->pdata); + BOOLEAN_TO_NPVARIANT(id->lastReportedPrivateModeState, *result); + return true; +} + +static bool +hasWidget(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + if (argCount != 0) + return false; InstanceData* id = static_cast(static_cast(npobj)->npp->pdata); BOOLEAN_TO_NPVARIANT(id->hasWidget, *result); @@ -1290,6 +2056,33 @@ } static bool +getWidthAtLastPaint(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + if (argCount != 0) + return false; + + NPP npp = static_cast(npobj)->npp; + InstanceData* id = static_cast(npp->pdata); + INT32_TO_NPVARIANT(id->widthAtLastPaint, *result); + return true; +} + +static bool +getError(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + if (argCount != 0) + return false; + + NPP npp = static_cast(npobj)->npp; + InstanceData* id = static_cast(npp->pdata); + if (id->err.str().length() == 0) + STRINGZ_TO_NPVARIANT(strdup(SUCCESS_STRING), *result); + else + STRINGZ_TO_NPVARIANT(strdup(id->err.str().c_str()), *result); + return true; +} + +static bool doInternalConsistencyCheck(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) { if (argCount != 0) @@ -1309,6 +2102,161 @@ } static bool +convertPointX(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ +#ifdef NPAPI_CONVERTPOINT + if (argCount != 4) + return false; + + NPP npp = static_cast(npobj)->npp; + + if (!NPVARIANT_IS_INT32(args[0])) + return false; + int32_t sourceSpace = NPVARIANT_TO_INT32(args[0]); + + if (!NPVARIANT_IS_INT32(args[1])) + return false; + double sourceX = static_cast(NPVARIANT_TO_INT32(args[1])); + + if (!NPVARIANT_IS_INT32(args[2])) + return false; + double sourceY = static_cast(NPVARIANT_TO_INT32(args[2])); + + if (!NPVARIANT_IS_INT32(args[3])) + return false; + int32_t destSpace = NPVARIANT_TO_INT32(args[3]); + + double resultX, resultY; + NPN_ConvertPoint(npp, sourceX, sourceY, (NPCoordinateSpace)sourceSpace, &resultX, &resultY, (NPCoordinateSpace)destSpace); + + DOUBLE_TO_NPVARIANT(resultX, *result); + return true; +#else + return false; +#endif +} + +static bool +convertPointY(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ +#ifdef NPAPI_CONVERTPOINT + if (argCount != 4) + return false; + + NPP npp = static_cast(npobj)->npp; + + if (!NPVARIANT_IS_INT32(args[0])) + return false; + int32_t sourceSpace = NPVARIANT_TO_INT32(args[0]); + + if (!NPVARIANT_IS_INT32(args[1])) + return false; + double sourceX = static_cast(NPVARIANT_TO_INT32(args[1])); + + if (!NPVARIANT_IS_INT32(args[2])) + return false; + double sourceY = static_cast(NPVARIANT_TO_INT32(args[2])); + + if (!NPVARIANT_IS_INT32(args[3])) + return false; + int32_t destSpace = NPVARIANT_TO_INT32(args[3]); + + double resultX, resultY; + NPN_ConvertPoint(npp, sourceX, sourceY, (NPCoordinateSpace)sourceSpace, &resultX, &resultY, (NPCoordinateSpace)destSpace); + + DOUBLE_TO_NPVARIANT(resultY, *result); + return true; +#else + return false; +#endif +} + +static bool +streamTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + // .streamTest(url, doPost, doNull, callback) + if (4 != argCount) + return false; + + NPP npp = static_cast(npobj)->npp; + + if (!NPVARIANT_IS_STRING(args[0])) + return false; + NPString url = NPVARIANT_TO_STRING(args[0]); + + if (!NPVARIANT_IS_BOOLEAN(args[1])) + return false; + bool doPost = NPVARIANT_TO_BOOLEAN(args[1]); + + NPString postData = { NULL, 0 }; + if (NPVARIANT_IS_NULL(args[2])) { + } + else if (NPVARIANT_IS_STRING(args[2])) { + postData = NPVARIANT_TO_STRING(args[2]); + } + else { + return false; + } + + if (!NPVARIANT_IS_OBJECT(args[3])) + return false; + NPObject* callback = NPVARIANT_TO_OBJECT(args[3]); + + URLNotifyData* ndata = new URLNotifyData; + ndata->cookie = "dynamic-cookie"; + ndata->callback = callback; + ndata->size = 0; + ndata->data = NULL; + + /* null-terminate "url" */ + char* urlstr = (char*) malloc(url.UTF8Length + 1); + strncpy(urlstr, url.UTF8Characters, url.UTF8Length); + urlstr[url.UTF8Length] = '\0'; + + NPError err; + if (doPost) { + err = NPN_PostURLNotify(npp, urlstr, NULL, + postData.UTF8Length, postData.UTF8Characters, + false, ndata); + } + else { + err = NPN_GetURLNotify(npp, urlstr, NULL, ndata); + } + + free(urlstr); + + if (NPERR_NO_ERROR == err) { + NPN_RetainObject(ndata->callback); + BOOLEAN_TO_NPVARIANT(true, *result); + } + else { + delete ndata; + BOOLEAN_TO_NPVARIANT(false, *result); + } + + return true; +} + +static bool +crashPlugin(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + IntentionalCrash(); + VOID_TO_NPVARIANT(*result); + return true; +} + +static bool +crashOnDestroy(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + NPP npp = static_cast(npobj)->npp; + InstanceData* id = static_cast(npp->pdata); + + id->crashOnDestroy = true; + VOID_TO_NPVARIANT(*result); + return true; +} + +static bool setColor(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) { if (argCount != 1) @@ -1330,6 +2278,47 @@ r.bottom = id->window.height; NPN_InvalidateRect(npp, &r); + VOID_TO_NPVARIANT(*result); + return true; +} + +void notifyDidPaint(InstanceData* instanceData) +{ + ++instanceData->paintCount; + instanceData->widthAtLastPaint = instanceData->window.width; +} + +static const NPClass kTestSharedNPClass = { + NP_CLASS_STRUCT_VERSION, + // Everything else is NULL +}; + +static bool getObjectValue(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + NPP npp = static_cast(npobj)->npp; + + NPObject* o = NPN_CreateObject(npp, + const_cast(&kTestSharedNPClass)); + if (!o) + return false; + + OBJECT_TO_NPVARIANT(o, *result); + return true; +} + +static bool checkObjectValue(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + VOID_TO_NPVARIANT(*result); + + if (1 != argCount) + return false; + + if (!NPVARIANT_IS_OBJECT(args[0])) + return false; + + NPObject* o = NPVARIANT_TO_OBJECT(args[0]); + + BOOLEAN_TO_NPVARIANT(o->_class == &kTestSharedNPClass, *result); return true; } @@ -1344,3 +2333,456 @@ return false; #endif } + +// caller is responsible for freeing return buffer +static char* URLForInstanceWindow(NPP instance) { + char *outString = NULL; + + NPObject* windowObject = NULL; + NPError err = NPN_GetValue(instance, NPNVWindowNPObject, &windowObject); + if (err != NPERR_NO_ERROR || !windowObject) + return NULL; + + NPIdentifier locationIdentifier = NPN_GetStringIdentifier("location"); + NPVariant locationVariant; + if (NPN_GetProperty(instance, windowObject, locationIdentifier, &locationVariant)) { + NPObject *locationObject = locationVariant.value.objectValue; + if (locationObject) { + NPIdentifier hrefIdentifier = NPN_GetStringIdentifier("href"); + NPVariant hrefVariant; + if (NPN_GetProperty(instance, locationObject, hrefIdentifier, &hrefVariant)) { + const NPString* hrefString = &NPVARIANT_TO_STRING(hrefVariant); + if (hrefString) { + outString = (char *)malloc(hrefString->UTF8Length + 1); + if (outString) { + strcpy(outString, hrefString->UTF8Characters); + outString[hrefString->UTF8Length] = '\0'; + } + } + NPN_ReleaseVariantValue(&hrefVariant); + } + } + NPN_ReleaseVariantValue(&locationVariant); + } + + NPN_ReleaseObject(windowObject); + + return outString; +} + +static bool +setCookie(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + if (argCount != 1) + return false; + if (!NPVARIANT_IS_STRING(args[0])) + return false; + const NPString* cookie = &NPVARIANT_TO_STRING(args[0]); + + NPP npp = static_cast(npobj)->npp; + + char* url = URLForInstanceWindow(npp); + if (!url) + return false; + NPError err = NPN_SetValueForURL(npp, NPNURLVCookie, url, cookie->UTF8Characters, cookie->UTF8Length); + free(url); + + return (err == NPERR_NO_ERROR); +} + +static bool +getCookie(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + if (argCount != 0) + return false; + + NPP npp = static_cast(npobj)->npp; + + char* url = URLForInstanceWindow(npp); + if (!url) + return false; + char* cookie = NULL; + unsigned int length = 0; + NPError err = NPN_GetValueForURL(npp, NPNURLVCookie, url, &cookie, &length); + free(url); + if (err != NPERR_NO_ERROR || !cookie) + return false; + + STRINGZ_TO_NPVARIANT(cookie, *result); + return true; +} + +static bool +getAuthInfo(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + if (argCount != 5) + return false; + + NPP npp = static_cast(npobj)->npp; + + if (!NPVARIANT_IS_STRING(args[0]) || !NPVARIANT_IS_STRING(args[1]) || + !NPVARIANT_IS_INT32(args[2]) || !NPVARIANT_IS_STRING(args[3]) || + !NPVARIANT_IS_STRING(args[4])) + return false; + + const NPString* protocol = &NPVARIANT_TO_STRING(args[0]); + const NPString* host = &NPVARIANT_TO_STRING(args[1]); + uint32_t port = NPVARIANT_TO_INT32(args[2]); + const NPString* scheme = &NPVARIANT_TO_STRING(args[3]); + const NPString* realm = &NPVARIANT_TO_STRING(args[4]); + + char* username = NULL; + char* password = NULL; + uint32_t ulen = 0, plen = 0; + + NPError err = NPN_GetAuthenticationInfo(npp, + protocol->UTF8Characters, + host->UTF8Characters, + port, + scheme->UTF8Characters, + realm->UTF8Characters, + &username, + &ulen, + &password, + &plen); + + if (err != NPERR_NO_ERROR) { + return false; + } + + char* outstring = (char*)NPN_MemAlloc(ulen + plen + 2); + memset(outstring, 0, ulen + plen + 2); + strncpy(outstring, username, ulen); + strcat(outstring, "|"); + strncat(outstring, password, plen); + + STRINGZ_TO_NPVARIANT(outstring, *result); + + NPN_MemFree(username); + NPN_MemFree(password); + + return true; +} + +#ifdef NPAPI_TIMERS +static void timerCallback(NPP npp, uint32_t timerID) +{ + InstanceData* id = static_cast(npp->pdata); + timerEventCount++; + timerEvent event = timerEvents[timerEventCount]; + + NPObject* windowObject; + NPN_GetValue(npp, NPNVWindowNPObject, &windowObject); + if (!windowObject) + return; + + NPVariant rval; + if (timerID != id->timerID[event.timerIdReceive]) + id->timerTestResult = false; + + if (timerEventCount == totalTimerEvents - 1) { + NPVariant arg; + BOOLEAN_TO_NPVARIANT(id->timerTestResult, arg); + NPN_Invoke(npp, windowObject, NPN_GetStringIdentifier(id->timerTestScriptCallback.c_str()), &arg, 1, &rval); + NPN_ReleaseVariantValue(&arg); + } + + NPN_ReleaseObject(windowObject); + + if (event.timerIdSchedule > -1) { + id->timerID[event.timerIdSchedule] = NPN_ScheduleTimer(npp, event.timerInterval, event.timerRepeat, timerCallback); + } + if (event.timerIdUnschedule > -1) { + NPN_UnscheduleTimer(npp, id->timerID[event.timerIdUnschedule]); + } +} +#endif // NPAPI_TIMERS + +static bool +timerTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ +#ifdef NPAPI_TIMERS + NPP npp = static_cast(npobj)->npp; + InstanceData* id = static_cast(npp->pdata); + timerEventCount = 0; + + if (argCount < 1 || !NPVARIANT_IS_STRING(args[0])) + return false; + const NPString* argstr = &NPVARIANT_TO_STRING(args[0]); + id->timerTestScriptCallback = argstr->UTF8Characters; + + id->timerTestResult = true; + timerEvent event = timerEvents[timerEventCount]; + + id->timerID[event.timerIdSchedule] = NPN_ScheduleTimer(npp, event.timerInterval, event.timerRepeat, timerCallback); + + return id->timerID[event.timerIdSchedule] != 0; +#else + return false; +#endif +} + +#ifdef XP_WIN +void +ThreadProc(void* cookie) +#else +void* +ThreadProc(void* cookie) +#endif +{ + NPObject* npobj = (NPObject*)cookie; + NPP npp = static_cast(npobj)->npp; + InstanceData* id = static_cast(npp->pdata); + id->asyncTestPhase = 1; + NPN_PluginThreadAsyncCall(npp, asyncCallback, (void*)npobj); +#ifndef XP_WIN + return NULL; +#endif +} + +void +asyncCallback(void* cookie) +{ + NPObject* npobj = (NPObject*)cookie; + NPP npp = static_cast(npobj)->npp; + InstanceData* id = static_cast(npp->pdata); + + switch (id->asyncTestPhase) { + // async callback triggered from same thread + case 0: +#ifdef XP_WIN + if (_beginthread(ThreadProc, 0, (void*)npobj) == -1) + id->asyncCallbackResult = false; +#else + pthread_t tid; + if (pthread_create(&tid, 0, ThreadProc, (void*)npobj)) + id->asyncCallbackResult = false; +#endif + break; + + // async callback triggered from different thread + default: + NPObject* windowObject; + NPN_GetValue(npp, NPNVWindowNPObject, &windowObject); + if (!windowObject) + return; + NPVariant arg, rval; + BOOLEAN_TO_NPVARIANT(id->asyncCallbackResult, arg); + NPN_Invoke(npp, windowObject, NPN_GetStringIdentifier(id->asyncTestScriptCallback.c_str()), &arg, 1, &rval); + NPN_ReleaseVariantValue(&arg); + NPN_ReleaseObject(windowObject); + break; + } +} + +static bool +asyncCallbackTest(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + NPP npp = static_cast(npobj)->npp; + InstanceData* id = static_cast(npp->pdata); + + if (argCount < 1 || !NPVARIANT_IS_STRING(args[0])) + return false; + const NPString* argstr = &NPVARIANT_TO_STRING(args[0]); + id->asyncTestScriptCallback = argstr->UTF8Characters; + + id->asyncTestPhase = 0; + id->asyncCallbackResult = true; + NPN_PluginThreadAsyncCall(npp, asyncCallback, (void*)npobj); + + return true; +} + +static bool +GCRaceInvoke(NPObject*, NPIdentifier, const NPVariant*, uint32_t, NPVariant*) +{ + return false; +} + +static bool +GCRaceInvokeDefault(NPObject* o, const NPVariant* args, uint32_t argCount, + NPVariant* result) +{ + if (1 != argCount || !NPVARIANT_IS_INT32(args[0]) || + 35 != NPVARIANT_TO_INT32(args[0])) + return false; + + return true; +} + +static const NPClass kGCRaceClass = { + NP_CLASS_STRUCT_VERSION, + NULL, + NULL, + NULL, + NULL, + GCRaceInvoke, + GCRaceInvokeDefault, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL +}; + +struct GCRaceData +{ + GCRaceData(NPP npp, NPObject* callback, NPObject* localFunc) + : npp_(npp) + , callback_(callback) + , localFunc_(localFunc) + { + NPN_RetainObject(callback_); + NPN_RetainObject(localFunc_); + } + + ~GCRaceData() + { + NPN_ReleaseObject(callback_); + NPN_ReleaseObject(localFunc_); + } + + NPP npp_; + NPObject* callback_; + NPObject* localFunc_; +}; + +static void +FinishGCRace(void* closure) +{ + GCRaceData* rd = static_cast(closure); + +#ifdef XP_WIN + Sleep(5000); +#else + sleep(5); +#endif + + NPVariant arg; + OBJECT_TO_NPVARIANT(rd->localFunc_, arg); + + NPVariant result; + bool ok = NPN_InvokeDefault(rd->npp_, rd->callback_, &arg, 1, &result); + if (!ok) + return; + + NPN_ReleaseVariantValue(&result); + delete rd; +} + +bool +checkGCRace(NPObject* npobj, const NPVariant* args, uint32_t argCount, + NPVariant* result) +{ + if (1 != argCount || !NPVARIANT_IS_OBJECT(args[0])) + return false; + + NPP npp = static_cast(npobj)->npp; + + NPObject* localFunc = + NPN_CreateObject(npp, const_cast(&kGCRaceClass)); + + GCRaceData* rd = + new GCRaceData(npp, NPVARIANT_TO_OBJECT(args[0]), localFunc); + NPN_PluginThreadAsyncCall(npp, FinishGCRace, rd); + + OBJECT_TO_NPVARIANT(localFunc, *result); + return true; +} + +bool +hangPlugin(NPObject* npobj, const NPVariant* args, uint32_t argCount, + NPVariant* result) +{ + NoteIntentionalCrash(); + +#ifdef XP_WIN + Sleep(100000000); +#else + pause(); +#endif + // NB: returning true here means that we weren't terminated, and + // thus the hang detection/handling didn't work correctly. The + // test harness will succeed in calling this function, and the + // test will fail. + return true; +} + +#if defined(MOZ_WIDGET_GTK2) +bool +getClipboardText(NPObject* npobj, const NPVariant* args, uint32_t argCount, + NPVariant* result) +{ + NPP npp = static_cast(npobj)->npp; + InstanceData* id = static_cast(npp->pdata); + string sel = pluginGetClipboardText(id); + + uint32 len = sel.size(); + char* selCopy = static_cast(NPN_MemAlloc(1 + len)); + if (!selCopy) + return false; + + memcpy(selCopy, sel.c_str(), len); + selCopy[len] = '\0'; + + STRINGN_TO_NPVARIANT(selCopy, len, *result); + // *result owns str now + + return true; +} + +#else +bool +getClipboardText(NPObject* npobj, const NPVariant* args, uint32_t argCount, + NPVariant* result) +{ + /// XXX Not implemented! + return false; +} +#endif + +bool +callOnDestroy(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result) +{ + NPP npp = static_cast(npobj)->npp; + InstanceData* id = static_cast(npp->pdata); + + if (id->callOnDestroy) + return false; + + if (1 != argCount || !NPVARIANT_IS_OBJECT(args[0])) + return false; + + id->callOnDestroy = NPVARIANT_TO_OBJECT(args[0]); + NPN_RetainObject(id->callOnDestroy); + + return true; +} + +// On Linux at least, a windowed plugin resize causes Flash Player to +// reconnect to the browser window. This method simulates that. +bool +reinitWidget(NPObject* npobj, const NPVariant* args, uint32_t argCount, + NPVariant* result) +{ + if (argCount != 0) + return false; + + NPP npp = static_cast(npobj)->npp; + InstanceData* id = static_cast(npp->pdata); + + if (!id->hasWidget) + return false; + + pluginWidgetInit(id, id->window.window); + return true; +} + +bool +propertyAndMethod(NPObject* npobj, const NPVariant* args, uint32_t argCount, + NPVariant* result) +{ + INT32_TO_NPVARIANT(5, *result); + return true; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_gtk2.cpp firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_gtk2.cpp --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_gtk2.cpp 2010-04-02 16:58:51.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_gtk2.cpp 2010-04-16 17:32:26.000000000 +0100 @@ -42,6 +42,8 @@ #endif #include + using namespace std; + struct _PlatformData { #ifdef MOZ_X11 Display* display; @@ -154,13 +156,12 @@ int width = window.width; int height = window.height; - ++instanceData->paintCount; - if (instanceData->scriptableObject->drawMode == DM_SOLID_COLOR) { // drawing a solid color for reftests pluginDrawSolid(instanceData, gdkWindow, invalidRect.x, invalidRect.y, invalidRect.width, invalidRect.height); + notifyDidPaint(instanceData); return; } @@ -206,6 +207,8 @@ g_object_unref(pangoTextLayout); g_object_unref(gdkContext); + + notifyDidPaint(instanceData); } static gboolean @@ -618,3 +621,17 @@ void pluginDoInternalConsistencyCheck(InstanceData* instanceData, string& error) { } + +string +pluginGetClipboardText(InstanceData* instanceData) +{ + GtkClipboard* cb = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD); + // XXX this is a BAD WAY to interact with GtkClipboard. We use this + // deprecated interface only to test nested event loop handling. + gchar* text = gtk_clipboard_wait_for_text(cb); + string retText = text ? text : ""; + + g_free(text); + + return retText; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/nptest.h firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/nptest.h --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/nptest.h 2010-04-02 16:58:51.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/nptest.h 2010-04-16 17:32:26.000000000 +0100 @@ -43,8 +43,6 @@ #include #include -using namespace std; - typedef enum { DM_DEFAULT, DM_SOLID_COLOR @@ -55,9 +53,19 @@ FUNCTION_NPP_GETURL, FUNCTION_NPP_GETURLNOTIFY, FUNCTION_NPP_POSTURL, - FUNCTION_NPP_POSTURLNOTIFY + FUNCTION_NPP_POSTURLNOTIFY, + FUNCTION_NPP_NEWSTREAM, + FUNCTION_NPP_WRITEREADY, + FUNCTION_NPP_WRITE, + FUNCTION_NPP_DESTROYSTREAM, + FUNCTION_NPP_WRITE_RPC } TestFunction; +typedef struct FunctionTable { + TestFunction funcId; + const char* funcName; +} FunctionTable; + typedef enum { POSTMODE_FRAME, POSTMODE_STREAM @@ -80,18 +88,33 @@ NPWindow window; TestNPObject* scriptableObject; PlatformData* platformData; - uint32_t instanceCountWatchGeneration; + int32_t instanceCountWatchGeneration; bool lastReportedPrivateModeState; bool hasWidget; bool npnNewStream; + bool throwOnNextInvoke; + uint32_t timerID[2]; + bool timerTestResult; + bool asyncCallbackResult; + int32_t winX; + int32_t winY; int32_t lastMouseX; int32_t lastMouseY; + int32_t widthAtLastPaint; int32_t paintCount; + int32_t writeCount; + int32_t writeReadyCount; + int32_t asyncTestPhase; TestFunction testFunction; + TestFunction functionToFail; + NPError failureCode; + NPObject* callOnDestroy; PostMode postMode; - string testUrl; - string frame; - ostringstream err; + std::string testUrl; + std::string frame; + std::string timerTestScriptCallback; + std::string asyncTestScriptCallback; + std::ostringstream err; uint16_t streamMode; int32_t streamChunkSize; int32_t streamBufSize; @@ -99,6 +122,9 @@ TestRange* testrange; void* streamBuf; void* fileBuf; + bool crashOnDestroy; } InstanceData; +void notifyDidPaint(InstanceData* instanceData); + #endif // nptest_h_ diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_macosx.mm firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_macosx.mm --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_macosx.mm 2010-04-02 16:58:51.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_macosx.mm 2010-04-16 17:32:26.000000000 +0100 @@ -34,6 +34,13 @@ #include "nptest_platform.h" #include + using namespace std; + +#ifdef __LP64__ +// 64-bit requires the Cocoa event model +#define USE_COCOA_NPAPI 1 +#endif + bool pluginSupportsWindowMode() { @@ -50,14 +57,27 @@ pluginInstanceInit(InstanceData* instanceData) { NPP npp = instanceData->npp; - // select the right drawing model if necessary + NPBool supportsCoreGraphics = false; - if (NPN_GetValue(npp, NPNVsupportsCoreGraphicsBool, &supportsCoreGraphics) == NPERR_NO_ERROR && supportsCoreGraphics) { + if ((NPN_GetValue(npp, NPNVsupportsCoreGraphicsBool, &supportsCoreGraphics) == NPERR_NO_ERROR) && + supportsCoreGraphics) { NPN_SetValue(npp, NPPVpluginDrawingModel, (void*)NPDrawingModelCoreGraphics); } else { printf("CoreGraphics drawing model not supported, can't create a plugin instance.\n"); return NPERR_INCOMPATIBLE_VERSION_ERROR; } + +#ifdef USE_COCOA_NPAPI + NPBool supportsCocoaEvents = false; + if ((NPN_GetValue(npp, NPNVsupportsCocoaBool, &supportsCocoaEvents) == NPERR_NO_ERROR) && + supportsCocoaEvents) { + NPN_SetValue(npp, NPPVpluginEventModel, (void*)NPEventModelCocoa); + } else { + printf("Cocoa event model not supported, can't create a plugin instance.\n"); + return NPERR_INCOMPATIBLE_VERSION_ERROR; + } +#endif + return NPERR_NO_ERROR; } @@ -106,7 +126,11 @@ } static void +#ifdef USE_COCOA_NPAPI +pluginDraw(InstanceData* instanceData, NPCocoaEvent* event) +#else pluginDraw(InstanceData* instanceData) +#endif { if (!instanceData) return; @@ -120,7 +144,12 @@ return; NPWindow window = instanceData->window; + +#ifdef USE_COCOA_NPAPI + CGContextRef cgContext = event->data.draw.context; +#else CGContextRef cgContext = ((NP_CGContext*)(window.window))->context; +#endif float windowWidth = window.width; float windowHeight = window.height; @@ -146,7 +175,35 @@ CGContextSetLineWidth(cgContext, 6.0); CGContextStrokePath(cgContext); - // draw the UA string using ATSUI +#ifdef USE_COCOA_NPAPI // draw the UA string using Core Text + CGContextSetTextMatrix(cgContext, CGAffineTransformIdentity); + + // Initialize a rectangular path. + CGMutablePathRef path = CGPathCreateMutable(); + CGRect bounds = CGRectMake(10.0, 10.0, windowWidth - 20.0, windowHeight - 20.0); + CGPathAddRect(path, NULL, bounds); + + // Initialize an attributed string. + CFMutableAttributedStringRef attrString = CFAttributedStringCreateMutable(kCFAllocatorDefault, 0); + CFAttributedStringReplaceString(attrString, CFRangeMake(0, 0), uaCFString); + + // Create a color and add it as an attribute to the string. + CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); + CGFloat components[] = { 0.0, 0.0, 0.0, 1.0 }; + CGColorRef red = CGColorCreate(rgbColorSpace, components); + CGColorSpaceRelease(rgbColorSpace); + CFAttributedStringSetAttribute(attrString, CFRangeMake(0, 50), kCTForegroundColorAttributeName, red); + + // Create the framesetter with the attributed string. + CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString(attrString); + CFRelease(attrString); + + // Create the frame and draw it into the graphics context + CTFrameRef frame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, 0), path, NULL); + CFRelease(framesetter); + CTFrameDraw(frame, cgContext); + CFRelease(frame); +#else // draw the UA string using ATSUI CGContextSetGrayFillColor(cgContext, 0.0, 1.0); ATSUStyle atsuStyle; ATSUCreateStyle(&atsuStyle); @@ -199,13 +256,16 @@ UniCharArrayOffset currentDrawOffset = kATSUFromTextBeginning; unsigned int i = 0; while (i < softBreakCount) { - ATSUDrawText(atsuLayout, currentDrawOffset, softBreaks[i], FloatToFixed(5.0), FloatToFixed(windowHeight - 5.0 - (lineHeight * (i + 1.0)))); + ATSUDrawText(atsuLayout, currentDrawOffset, softBreaks[i], FloatToFixed(5.0), + FloatToFixed(windowHeight - 5.0 - (lineHeight * (i + 1.0)))); currentDrawOffset = softBreaks[i]; i++; } - ATSUDrawText(atsuLayout, currentDrawOffset, kATSUToTextEnd, FloatToFixed(5.0), FloatToFixed(windowHeight - 5.0 - (lineHeight * (i + 1.0)))); + ATSUDrawText(atsuLayout, currentDrawOffset, kATSUToTextEnd, FloatToFixed(5.0), + FloatToFixed(windowHeight - 5.0 - (lineHeight * (i + 1.0)))); free(unicharBuffer); free(softBreaks); +#endif // restore the cgcontext gstate CGContextRestoreGState(cgContext); @@ -233,12 +293,31 @@ } } - ++instanceData->paintCount; + notifyDidPaint(instanceData); } int16_t pluginHandleEvent(InstanceData* instanceData, void* event) { +#ifdef USE_COCOA_NPAPI + NPCocoaEvent* cocoaEvent = (NPCocoaEvent*)event; + if (!cocoaEvent) + return 0; + + switch (cocoaEvent->type) { + case NPCocoaEventDrawRect: + pluginDraw(instanceData, cocoaEvent); + return 1; + case NPCocoaEventMouseDown: + case NPCocoaEventMouseUp: + case NPCocoaEventMouseMoved: + instanceData->lastMouseX = (int32_t)cocoaEvent->data.mouse.pluginX; + instanceData->lastMouseY = (int32_t)cocoaEvent->data.mouse.pluginY; + return 1; + default: + return 0; + } +#else EventRecord* carbonEvent = (EventRecord*)event; if (!carbonEvent) return 0; @@ -251,12 +330,19 @@ case mouseDown: case mouseUp: case osEvt: - instanceData->lastMouseX = carbonEvent->where.h - w->x; - instanceData->lastMouseY = carbonEvent->where.v - w->y; + { + Rect globalBounds = {0}; + WindowRef nativeWindow = static_cast(static_cast(w->window)->window); + if (nativeWindow) + ::GetWindowBounds(nativeWindow, kWindowStructureRgn, &globalBounds); + instanceData->lastMouseX = carbonEvent->where.h - w->x - globalBounds.left; + instanceData->lastMouseY = carbonEvent->where.v - w->y - globalBounds.top; return 1; + } default: return 0; } +#endif } int32_t pluginGetEdge(InstanceData* instanceData, RectEdge edge) diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_os2.cpp firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_os2.cpp --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_os2.cpp 2010-04-02 16:58:51.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_os2.cpp 2010-04-16 17:32:26.000000000 +0100 @@ -33,6 +33,8 @@ #include "nptest_platform.h" + using namespace std; + bool pluginSupportsWindowMode() { diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_platform.h firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_platform.h --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_platform.h 2010-04-02 16:58:51.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_platform.h 2010-04-16 17:32:26.000000000 +0100 @@ -118,6 +118,12 @@ * Just return if everything is OK, otherwise append error messages * to 'error' separated by \n. */ -void pluginDoInternalConsistencyCheck(InstanceData* instanceData, string& error); +void pluginDoInternalConsistencyCheck(InstanceData* instanceData, std::string& error); + +/** + * Get the current clipboard item as text. If the clipboard item + * isn't text, the returned value is undefined. + */ +std::string pluginGetClipboardText(InstanceData* instanceData); #endif // nptest_platform_h_ diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_qt.cpp firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_qt.cpp --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_qt.cpp 2010-04-02 16:58:51.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_qt.cpp 2010-04-16 17:32:26.000000000 +0100 @@ -33,6 +33,8 @@ #include "nptest_platform.h" + using namespace std; + bool pluginSupportsWindowMode() { diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_windows.cpp firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_windows.cpp --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_windows.cpp 2010-04-02 16:58:51.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/nptest_windows.cpp 2010-04-16 17:32:26.000000000 +0100 @@ -36,8 +36,9 @@ #include #include +#include -#pragma comment(lib, "msimg32.lib") + using namespace std; void SetSubclass(HWND hWnd, InstanceData* instanceData); void ClearSubclass(HWND hWnd); @@ -91,6 +92,7 @@ { HWND hWnd = (HWND)instanceData->window.window; if (oldWindow) { + // chrashtests/539897-1.html excercises this code HWND hWndOld = (HWND)oldWindow; ClearSubclass(hWndOld); if (instanceData->platformData->childWindow) { @@ -226,7 +228,7 @@ if (instanceData->hasWidget) ::EndPaint((HWND)instanceData->window.window, &ps); - ++instanceData->paintCount; + notifyDidPaint(instanceData); } /* script interface */ @@ -394,13 +396,35 @@ /* windowless plugin events */ static bool -handleEventInternal(InstanceData* instanceData, NPEvent* pe) +handleEventInternal(InstanceData* instanceData, NPEvent* pe, LRESULT* result) { switch ((UINT)pe->event) { case WM_PAINT: pluginDraw(instanceData); return true; + case WM_MOUSEACTIVATE: + if (instanceData->hasWidget) { + ::SetFocus((HWND)instanceData->window.window); + *result = MA_ACTIVATEANDEAT; + return true; + } + return false; + + case WM_MOUSEWHEEL: + return true; + + case WM_WINDOWPOSCHANGED: { + WINDOWPOS* pPos = (WINDOWPOS*)pe->lParam; + instanceData->winX = instanceData->winY = 0; + if (pPos) { + instanceData->winX = pPos->x; + instanceData->winY = pPos->y; + return true; + } + return false; + } + case WM_MOUSEMOVE: case WM_LBUTTONDOWN: case WM_LBUTTONUP: @@ -408,8 +432,8 @@ case WM_MBUTTONUP: case WM_RBUTTONDOWN: case WM_RBUTTONUP: { - int x = instanceData->hasWidget ? 0 : instanceData->window.x; - int y = instanceData->hasWidget ? 0 : instanceData->window.y; + int x = instanceData->hasWidget ? 0 : instanceData->winX; + int y = instanceData->hasWidget ? 0 : instanceData->winY; instanceData->lastMouseX = GET_X_LPARAM(pe->lParam) - x; instanceData->lastMouseY = GET_Y_LPARAM(pe->lParam) - y; return true; @@ -429,7 +453,8 @@ instanceData->window.type != NPWindowTypeDrawable) return 0; - return handleEventInternal(instanceData, pe); + LRESULT result = 0; + return handleEventInternal(instanceData, pe, &result); } /* windowed plugin events */ @@ -445,8 +470,9 @@ NPEvent event = { uMsg, wParam, lParam }; - if (handleEventInternal(pInstance, &event)) - return 0; + LRESULT result = 0; + if (handleEventInternal(pInstance, &event, &result)) + return result; if (uMsg == WM_CLOSE) { ClearSubclass((HWND)pInstance->window.window); diff -Nru firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/README firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/README --- firefox-3.6.3+nobinonly/mozilla/modules/plugin/test/testplugin/README 2010-04-02 16:58:51.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/modules/plugin/test/testplugin/README 2010-04-16 17:32:26.000000000 +0100 @@ -25,14 +25,77 @@ Attempts to set the value of an undefined variable (0x0) via NPN_SetValue, returns true if it succeeds and false if it doesn't. It should never succeed. +== NPN_ConvertPoint testing == + +* convertPointX(sourceSpace, sourceX, sourceY, destSpace) +* convertPointY(sourceSpace, sourceX, sourceY, destSpace) +The plugin uses NPN_ConvertPoint to convert sourceX and sourceY from the source +to dest space and returns the X or Y result based on the call. + == NPRuntime testing == -The test plugin object supports the following scriptable method: +The test plugin object supports the following scriptable methods: * identifierToStringTest(ident) Converts a string, int32 or double parameter 'ident' to an NPIdentifier and then to a string, which is returned. +* npnEvaluateTest(script) +Calls NPN_Evaluate on the 'script' argument, which is a string containing +some script to be executed. Returns the result of the evaluation. + +* npnInvokeTest(method, expected, args...) +Causes the plugin to call the specified script method using NPN_Invoke, +passing it 1 or more arguments specified in args. The return value of this +call is compared against 'expected', and if they match, npnInvokeTest will +return true. Otherwise, it will return false. + +* npnInvokeDefaultTest(object, argument) +Causes the plugin to call NPN_InvokeDefault on the specified object, +with the specified argument. Returns the result of the invocation. + +* getError() +If an error has occurred during the last stream or npruntime function, +this will return a string error message, otherwise it returns "pass". + +* throwExceptionNextInvoke() +Sets a flag which causes the next call to a scriptable method to throw +one or more exceptions. If no parameters are passed to the next +scriptable method call, it will cause a generic exception to be thrown. +Otherwise there will be one exception thrown per argument, with the argument +used as the exception message. Example: + + plugin.throwExceptionNextInvoke(); + plugin.npnInvokeTest("first exception message", "second exception message"); + +* () - default method +Returns a string consisting of the plugin name, concatenated with any +arguments passed to the method. + +* .crash() - Crashes the plugin + +* getObjectValue() - Returns a custom plugin-implemented scriptable object. +* checkObjectValue(obj) - Returns true if the object from setObjectValue() is + the same object passed into this function. It should return true when + the object is passed to the same plugin instance, and false when passed + to other plugin instances, see bug 532246 and + test_multipleinstanceobjects.html. + +* callOnDestroy(fn) - Calls `fn` when the plugin instance is being destroyed + +* getAuthInfo(protocol, host, port, scheme, realm) - a wrapper for +NPN_GetAuthenticationInfo(). Returns a string "username|password" for +the specified auth criteria, or throws an exception if no data is +available. + +* timerTest(callback) - initiates tests of NPN_ScheduleTimer & +NPN_UnscheduleTimer. When finished, calls the script callback +with a boolean value, indicating whether the tests were successful. + +* asyncCallbackTest(callback) - initiates tests of +NPN_PluginThreadAsyncCall. When finished, calls the script callback +with a boolean value, indicating whether the tests were successful. + == Private browsing == The test plugin object supports the following scriptable methods: @@ -71,6 +134,9 @@ This can be used to detect whether painting has happened in a plugin's window. +* getWidthAtLastPaint() +Returns the window width that was current when the plugin last painted. + == Plugin geometry == The test plugin supports the following scriptable methods: @@ -144,6 +210,12 @@ * stopWatchingInstanceCount() Stops watching. Throws an exception if there is no watch. +== NPAPI Timers == + +* unscheduleAllTimers() +Instructs the plugin instance to cancel all timers created via +NPN_ScheduleTimer. + == Stream Functionality == The test plugin enables a variety of NPAPI streaming tests, which are @@ -213,6 +285,32 @@ contents of that stream will be passed back to the browser and displayed in the specified frame via NPN_GetURL. +"newstream": if "true", then any stream which is sent to a frame in the browser + is sent via calls to NPN_NewStream and NPN_Write. Doing so will cause + the browser to store the stream data in a file, and set the frame's + location to the corresponding file:// url. + +"functiontofail": one of "npp_newstream", "npp_write", "npp_destroystream". + When specified, the given function will return an error code (-1 for + NPP_Write, or else the value of the "failurecode" attribute) the first time + it is called by the browser. + +"failurecode": one of the NPError constants. Used to specify the error + that will be returned by the "functiontofail". + +If the plugin is instantiated as a full-page plugin, the following defaults +are used: + streammode="seek" frame="testframe" range="100,100" + +The .streamTest(url, doPost, postData, callback) function will test how +NPN_GetURLNotify and NPN_PostURLNotify behave when they are called with +arbitrary (malformed) URLs. The function will return `true` if NPN_GetURLNotify +succeeds, and `false` if it fails. +@param doPost whether to call NPN_PostURLNotify +@param postData null, or a string to send a postdata +@callback which will be called when the urlnotify is received with the notify +result + == Internal consistency == * doInternalConsistencyCheck() @@ -221,6 +319,26 @@ mode, this checks that the position of the plugin's internal child window has not been disturbed relative to the plugin window. +== Windows native widget message behaviour == + +* Mouse events are handled (saving the last mouse event coordinate) and not +passed to the overridden windowproc. + +* WM_MOUSEWHEEL events are handled and not passed to the parent window or the +overridden windowproc. + +* WM_MOUSEACTIVATE events are handled by calling SetFocus on the plugin's +widget, if the plugin is windowed. If it's not windowed they're passed to +the overriden windowproc (but hopefully never sent by the browser anyway). + +== Getting and Setting Cookies == + +* setCookie(string) +Sets the given string as the cookie for window's URL. + +* getCookie() +Returns the cookie string for the window's URL, the cookie set by setCookie. + == FPU Control == x86-only on some OSes: diff -Nru firefox-3.6.3+nobinonly/mozilla/netwerk/cache/src/nsCacheEntryDescriptor.cpp firefox-3.6.4+build1+nobinonly/mozilla/netwerk/cache/src/nsCacheEntryDescriptor.cpp --- firefox-3.6.3+nobinonly/mozilla/netwerk/cache/src/nsCacheEntryDescriptor.cpp 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/netwerk/cache/src/nsCacheEntryDescriptor.cpp 2010-04-16 17:32:26.000000000 +0100 @@ -89,14 +89,20 @@ NS_IMETHODIMP -nsCacheEntryDescriptor::GetDeviceID(char ** result) +nsCacheEntryDescriptor::GetDeviceID(char ** aDeviceID) { - NS_ENSURE_ARG_POINTER(result); + NS_ENSURE_ARG_POINTER(aDeviceID); nsCacheServiceAutoLock lock; if (!mCacheEntry) return NS_ERROR_NOT_AVAILABLE; - *result = NS_strdup(mCacheEntry->GetDeviceID()); - return *result ? NS_OK : NS_ERROR_OUT_OF_MEMORY; + const char* deviceID = mCacheEntry->GetDeviceID(); + if (!deviceID) { + *aDeviceID = nsnull; + return NS_OK; + } + + *aDeviceID = NS_strdup(deviceID); + return *aDeviceID ? NS_OK : NS_ERROR_OUT_OF_MEMORY; } diff -Nru firefox-3.6.3+nobinonly/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp firefox-3.6.4+build1+nobinonly/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp --- firefox-3.6.3+nobinonly/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp 2010-04-02 16:58:54.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.cpp 2010-04-16 17:32:26.000000000 +0100 @@ -48,6 +48,7 @@ #include "nsCRT.h" #include "nsIHttpChannelInternal.h" #include "nsURLHelper.h" +#include "nsIStreamConverterService.h" // // Helper function for determining the length of data bytes up to @@ -67,7 +68,10 @@ return len; } -nsPartChannel::nsPartChannel(nsIChannel *aMultipartChannel, PRUint32 aPartID) : +nsPartChannel::nsPartChannel(nsIChannel *aMultipartChannel, PRUint32 aPartID, + nsIStreamListener* aListener) : + mMultipartChannel(aMultipartChannel), + mListener(aListener), mStatus(NS_OK), mContentLength(LL_MAXUINT), mIsByteRangeRequest(PR_FALSE), @@ -96,6 +100,26 @@ mByteRangeEnd = aEnd; } +nsresult nsPartChannel::SendOnStartRequest(nsISupports* aContext) +{ + return mListener->OnStartRequest(this, aContext); +} + +nsresult nsPartChannel::SendOnDataAvailable(nsISupports* aContext, + nsIInputStream* aStream, + PRUint32 aOffset, PRUint32 aLen) +{ + return mListener->OnDataAvailable(this, aContext, aStream, aOffset, aLen); +} + +nsresult nsPartChannel::SendOnStopRequest(nsISupports* aContext, + nsresult aStatus) +{ + // Drop the listener + nsCOMPtr listener; + listener.swap(mListener); + return listener->OnStopRequest(this, aContext, aStatus); +} // // nsISupports implementation... @@ -734,15 +758,30 @@ nsMultiMixedConv::SendStart(nsIChannel *aChannel) { nsresult rv = NS_OK; - if (mContentType.IsEmpty()) + nsCOMPtr partListener(mFinalListener); + if (mContentType.IsEmpty()) { mContentType.AssignLiteral(UNKNOWN_CONTENT_TYPE); + nsCOMPtr serv = + do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) { + nsCOMPtr converter; + rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE, + "*/*", + mFinalListener, + mContext, + getter_AddRefs(converter)); + if (NS_SUCCEEDED(rv)) { + partListener = converter; + } + } + } // if we already have an mPartChannel, that means we never sent a Stop() // before starting up another "part." that would be bad. NS_ASSERTION(!mPartChannel, "tisk tisk, shouldn't be overwriting a channel"); nsPartChannel *newChannel; - newChannel = new nsPartChannel(aChannel, mCurrentPartID++); + newChannel = new nsPartChannel(aChannel, mCurrentPartID++, partListener); if (!newChannel) return NS_ERROR_OUT_OF_MEMORY; @@ -780,7 +819,7 @@ // Let's start off the load. NOTE: we don't forward on the channel passed // into our OnDataAvailable() as it's the root channel for the raw stream. - return mFinalListener->OnStartRequest(mPartChannel, mContext); + return mPartChannel->SendOnStartRequest(mContext); } @@ -789,7 +828,7 @@ nsresult rv = NS_OK; if (mPartChannel) { - rv = mFinalListener->OnStopRequest(mPartChannel, mContext, aStatus); + rv = mPartChannel->SendOnStopRequest(mContext, aStatus); // don't check for failure here, we need to remove the channel from // the loadgroup. @@ -836,7 +875,7 @@ nsCOMPtr inStream(do_QueryInterface(ss, &rv)); if (NS_FAILED(rv)) return rv; - return mFinalListener->OnDataAvailable(mPartChannel, mContext, inStream, offset, aLen); + return mPartChannel->SendOnDataAvailable(mContext, inStream, offset, aLen); } PRInt32 diff -Nru firefox-3.6.3+nobinonly/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.h firefox-3.6.4+build1+nobinonly/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.h --- firefox-3.6.3+nobinonly/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.h 2010-04-02 16:58:54.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/netwerk/streamconv/converters/nsMultiMixedConv.h 2010-04-16 17:32:26.000000000 +0100 @@ -68,10 +68,15 @@ public nsIMultiPartChannel { public: - nsPartChannel(nsIChannel *aMultipartChannel, PRUint32 aPartID); + nsPartChannel(nsIChannel *aMultipartChannel, PRUint32 aPartID, + nsIStreamListener* aListener); void InitializeByteRange(PRInt64 aStart, PRInt64 aEnd); void SetIsLastPart() { mIsLastPart = PR_TRUE; } + nsresult SendOnStartRequest(nsISupports* aContext); + nsresult SendOnDataAvailable(nsISupports* aContext, nsIInputStream* aStream, + PRUint32 aOffset, PRUint32 aLen); + nsresult SendOnStopRequest(nsISupports* aContext, nsresult aStatus); NS_DECL_ISUPPORTS NS_DECL_NSIREQUEST @@ -84,6 +89,7 @@ protected: nsCOMPtr mMultipartChannel; + nsCOMPtr mListener; nsresult mStatus; nsLoadFlags mLoadFlags; diff -Nru firefox-3.6.3+nobinonly/mozilla/netwerk/test/unit/test_bug540566.js firefox-3.6.4+build1+nobinonly/mozilla/netwerk/test/unit/test_bug540566.js --- firefox-3.6.3+nobinonly/mozilla/netwerk/test/unit/test_bug540566.js 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/netwerk/test/unit/test_bug540566.js 2010-04-16 17:32:27.000000000 +0100 @@ -0,0 +1,15 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +function run_test() { + var cs = Components.classes["@mozilla.org/network/cache-service;1"]. + getService(Components.interfaces.nsICacheService); + var nsICache = Components.interfaces.nsICache; + var session = cs.createSession("client", + nsICache.STORE_ANYWHERE, + true); + var entry = session.openCacheEntry("key", nsICache.STORE_ON_DISK, true); + entry.deviceID; + // if the above line does not crash, the test was successful +} diff -Nru firefox-3.6.3+nobinonly/mozilla/netwerk/test/unit/test_multipart_streamconv.js firefox-3.6.4+build1+nobinonly/mozilla/netwerk/test/unit/test_multipart_streamconv.js --- firefox-3.6.3+nobinonly/mozilla/netwerk/test/unit/test_multipart_streamconv.js 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/netwerk/test/unit/test_multipart_streamconv.js 2010-04-16 17:32:27.000000000 +0100 @@ -0,0 +1,93 @@ +do_load_httpd_js(); + +var httpserver = null; +var uri = "http://localhost:4444/multipart"; + +function make_channel(url) { + var ios = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + return ios.newChannel(url, "", null); +} + +var multipartBody = "--boundary\r\n\r\nSome text\r\n--boundary\r\n\r\n\r\n--boundary--"; + +function make_channel(url) { + var ios = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + return ios.newChannel(url, "", null); +} + +function contentHandler(metadata, response) +{ + response.setHeader("Content-Type", 'multipart/mixed; boundary="boundary"'); + response.bodyOutputStream.write(multipartBody, multipartBody.length); +} + +var numTests = 2; +var testNum = 0; + +var testData = + [ + { data: "Some text", type: "text/plain" }, + { data: "", type: "text/xml" } + ]; + +function responseHandler(request, buffer) +{ + do_check_eq(buffer, testData[testNum].data); + do_check_eq(request.QueryInterface(Ci.nsIChannel).contentType, + testData[testNum].type); + if (++testNum == numTests) + httpserver.stop(do_test_finished); +} + +var multipartListener = { + _buffer: "", + + QueryInterface: function(iid) { + if (iid.equals(Components.interfaces.nsIStreamListener) || + iid.equals(Components.interfaces.nsIRequestObserver) || + iid.equals(Components.interfaces.nsISupports)) + return this; + throw Components.results.NS_ERROR_NO_INTERFACE; + }, + + onStartRequest: function(request, context) { + this._buffer = ""; + }, + + onDataAvailable: function(request, context, stream, offset, count) { + try { + this._buffer = this._buffer.concat(read_stream(stream, count)); + dump("BUFFEEE: " + this._buffer + "\n\n"); + } catch (ex) { + do_throw("Error in onDataAvailable: " + ex); + } + }, + + onStopRequest: function(request, context, status) { + try { + responseHandler(request, this._buffer); + } catch (ex) { + do_throw("Error in closure function: " + ex); + } + } +}; + +function run_test() +{ + httpserver = new nsHttpServer(); + httpserver.registerPathHandler("/multipart", contentHandler); + httpserver.start(4444); + + var streamConv = Cc["@mozilla.org/streamConverters;1"] + .getService(Ci.nsIStreamConverterService); + var conv = streamConv.asyncConvertData("multipart/mixed", + "*/*", + multipartListener, + null); + + var chan = make_channel(uri); + chan.asyncOpen(conv, null); + do_test_pending(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/other-licenses/bsdiff/bsdiff.c firefox-3.6.4+build1+nobinonly/mozilla/other-licenses/bsdiff/bsdiff.c --- firefox-3.6.3+nobinonly/mozilla/other-licenses/bsdiff/bsdiff.c 2010-04-02 16:58:57.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/other-licenses/bsdiff/bsdiff.c 2010-04-16 17:32:29.000000000 +0100 @@ -35,10 +35,11 @@ #undef MIN #define MIN(x,y) (((x)<(y)) ? (x) : (y)) -//----------------------------------------------------------------------------- +/*---------------------------------------------------------------------------*/ -// This variable lives in libbz2. It's declared in bzlib_private.h, so we just -// declare it here to avoid including that entire header file. +/* This variable lives in libbz2. It's declared in bzlib_private.h, so we just + * declare it here to avoid including that entire header file. + */ extern unsigned int BZ2_crc32Table[256]; static unsigned int @@ -54,7 +55,7 @@ return crc; } -//----------------------------------------------------------------------------- +/*---------------------------------------------------------------------------*/ static void reporterr(int e, const char *fmt, ...) @@ -400,3 +401,4 @@ return 0; } + diff -Nru firefox-3.6.3+nobinonly/mozilla/plugin/oji/JEP/JavaEmbeddingPlugin.bundle/Contents/Info.plist firefox-3.6.4+build1+nobinonly/mozilla/plugin/oji/JEP/JavaEmbeddingPlugin.bundle/Contents/Info.plist --- firefox-3.6.3+nobinonly/mozilla/plugin/oji/JEP/JavaEmbeddingPlugin.bundle/Contents/Info.plist 2010-04-02 16:59:31.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/plugin/oji/JEP/JavaEmbeddingPlugin.bundle/Contents/Info.plist 2010-04-16 17:32:32.000000000 +0100 @@ -7,7 +7,7 @@ CFBundleExecutable JavaEmbeddingPlugin CFBundleGetInfoString - Java Embedding Plugin 0.9.7.2, Copyright (c) 2009 Steven Michaud + Java Embedding Plugin 0.9.7.3, Copyright (c) 2010 Steven Michaud CFBundleInfoDictionaryVersion 6.0 CFBundleName @@ -15,11 +15,11 @@ CFBundlePackageType BNDL CFBundleShortVersionString - 0.9.7.2 + 0.9.7.3 CFBundleSignature ???? CFBundleVersion - 0.9.7.2 + 0.9.7.3 NSJavaNeeded YES NSJavaPath Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/plugin/oji/JEP/JavaEmbeddingPlugin.bundle/Contents/Resources/English.lproj/InfoPlist.strings and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/plugin/oji/JEP/JavaEmbeddingPlugin.bundle/Contents/Resources/English.lproj/InfoPlist.strings differ diff -Nru firefox-3.6.3+nobinonly/mozilla/plugin/oji/JEP/MRJPlugin.plugin/Contents/Info.plist firefox-3.6.4+build1+nobinonly/mozilla/plugin/oji/JEP/MRJPlugin.plugin/Contents/Info.plist --- firefox-3.6.3+nobinonly/mozilla/plugin/oji/JEP/MRJPlugin.plugin/Contents/Info.plist 2010-04-02 16:59:32.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/plugin/oji/JEP/MRJPlugin.plugin/Contents/Info.plist 2010-04-16 17:32:32.000000000 +0100 @@ -7,7 +7,7 @@ CFBundleExecutable MRJPlugin CFBundleGetInfoString - MRJ Plugin 1.0-JEP-0.9.7.2, Copyright (c) 2002 The Mozilla Organization + MRJ Plugin 1.0-JEP-0.9.7.3, Copyright (c) 2002 The Mozilla Organization CFBundleIdentifier com.netscape.MRJPlugin CFBundleInfoDictionaryVersion @@ -17,11 +17,11 @@ CFBundlePackageType NSPL CFBundleShortVersionString - 1.0-JEP-0.9.7.2 + 1.0-JEP-0.9.7.3 CFBundleSignature MOSS CFBundleVersion - 1.0-JEP-0.9.7.2 + 1.0-JEP-0.9.7.3 CSResourcesFileMapped WebPluginDescription @@ -174,6 +174,6 @@ WebPluginName - Java Embedding Plugin 0.9.7.2 + Java Embedding Plugin 0.9.7.3 Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/plugin/oji/JEP/MRJPlugin.plugin/Contents/Resources/English.lproj/InfoPlist.strings and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/plugin/oji/JEP/MRJPlugin.plugin/Contents/Resources/English.lproj/InfoPlist.strings differ diff -Nru firefox-3.6.3+nobinonly/mozilla/profile/dirserviceprovider/src/nsProfileLock.cpp firefox-3.6.4+build1+nobinonly/mozilla/profile/dirserviceprovider/src/nsProfileLock.cpp --- firefox-3.6.3+nobinonly/mozilla/profile/dirserviceprovider/src/nsProfileLock.cpp 2010-04-02 16:58:59.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/profile/dirserviceprovider/src/nsProfileLock.cpp 2010-04-16 17:32:33.000000000 +0100 @@ -159,7 +159,8 @@ static struct sigaction SIGSEGV_oldact; static struct sigaction SIGTERM_oldact; -void nsProfileLock::FatalSignalHandler(int signo) +void nsProfileLock::FatalSignalHandler(int signo, siginfo_t *info, + void *context) { // Remove any locks still held. RemovePidLockFiles(); @@ -211,6 +212,10 @@ raise(signo); } + else if (oldact->sa_sigaction && + (oldact->sa_flags & SA_SIGINFO) == SA_SIGINFO) { + oldact->sa_sigaction(signo, info, context); + } else if (oldact->sa_handler && oldact->sa_handler != SIG_IGN) { oldact->sa_handler(signo); @@ -387,8 +392,8 @@ // because mozilla is run via nohup. if (!sDisableSignalHandling) { struct sigaction act, oldact; - act.sa_handler = FatalSignalHandler; - act.sa_flags = 0; + act.sa_sigaction = FatalSignalHandler; + act.sa_flags = SA_SIGINFO; sigfillset(&act.sa_mask); #define CATCH_SIGNAL(signame) \ diff -Nru firefox-3.6.3+nobinonly/mozilla/profile/dirserviceprovider/src/nsProfileLock.h firefox-3.6.4+build1+nobinonly/mozilla/profile/dirserviceprovider/src/nsProfileLock.h --- firefox-3.6.3+nobinonly/mozilla/profile/dirserviceprovider/src/nsProfileLock.h 2010-04-02 16:58:59.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/profile/dirserviceprovider/src/nsProfileLock.h 2010-04-16 17:32:33.000000000 +0100 @@ -55,6 +55,7 @@ #endif #if defined (XP_UNIX) +#include #include "prclist.h" #endif @@ -92,7 +93,8 @@ LHANDLE mLockFileHandle; #elif defined (XP_UNIX) static void RemovePidLockFiles(); - static void FatalSignalHandler(int signo); + static void FatalSignalHandler(int signo, siginfo_t *info, + void *context); static PRCList mPidLockList; nsresult LockWithFcntl(const nsACString& lockFilePath); diff -Nru firefox-3.6.3+nobinonly/mozilla/storage/test/unit/test_storage_combined_sharing.js firefox-3.6.4+build1+nobinonly/mozilla/storage/test/unit/test_storage_combined_sharing.js --- firefox-3.6.3+nobinonly/mozilla/storage/test/unit/test_storage_combined_sharing.js 2010-04-02 16:59:05.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/storage/test/unit/test_storage_combined_sharing.js 1970-01-01 01:00:00.000000000 +0100 @@ -1,227 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (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.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Storage Test Code. - * - * The Initial Developer of the Original Code is - * Mozilla Corporation. - * Portions created by the Initial Developer are Copyright (C) 2008 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Ondrej Brablc (Original Author) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -// List of connections to be tested. Shared property means that the -// connection should be created with shared cache. -// The name of the property is used as a key in the hash arrays of -// databases and connections to them. Any key name can be used here. -const CONN_LIST = -{ - S1: { shared: true }, - P1: { shared: false }, - S2: { shared: true }, - P2: { shared: false } -}; - -/** -* New testing class. It holds database connections. -*/ -function TestBody() -{ - this.conn = new Array(); - this.dbFile = new Array(); - - for (var curConn in CONN_LIST) { - var db = dirSvc.get("CurProcD", Ci.nsIFile); - db.append("test_storage_" + curConn + ".sqlite"); - this.dbFile[curConn] = db; - } - - this.cleanUp(); -} - -/** -* Remove databases if they exist -*/ -TestBody.prototype.cleanUp = function cleanUp() -{ - for (var curConn in CONN_LIST) { - if (this.dbFile[curConn].exists()) { - try { - this.dbFile[curConn].remove(false); - } - catch(e) { - /* stupid windows box */ - } - } - } -} - -/** -* Open connection to database (create database) -*/ -TestBody.prototype.test_initialize_database = -function test_initialize_database() -{ - for (var curConn in CONN_LIST) { - if (CONN_LIST[curConn].shared) - this.conn[curConn] = getService().openDatabase(this.dbFile[curConn]); - else - this.conn[curConn] = getService().openUnsharedDatabase( - this.dbFile[curConn]); - do_check_true(this.conn[curConn].connectionReady); - } -} - -/** -* Create normal table "test" and table "book" - if possible as virtual table. -* Creating virtual table in shared database is expected to fail and throw. -* This is the most important test in this module. The rest of the test just -* tests that it is possible to use tables properly. -*/ -TestBody.prototype.test_create_tables = -function test_create_tables() -{ - var realSql = "CREATE TABLE book (author TEXT, title TEXT)"; - var virtSql = "CREATE VIRTUAL TABLE book USING fts3(author, title)"; - - for (var curConn in CONN_LIST) { - this.conn[curConn].createTable("test", "id INTEGER PRIMARY KEY, name TEXT"); - do_check_true(this.conn[curConn].tableExists("test")); - - try { - this.conn[curConn].executeSimpleSQL(virtSql); - if (CONN_LIST[curConn].shared) // Statement above must throw in this case - do_throw("We shouldn't be able to create virtual tables on " + - curConn + " database!"); - } - catch (e) { - // If the try threw, we have shared database and create the - // table as non virtual. - this.conn[curConn].executeSimpleSQL(realSql); - } - - do_check_true(this.conn[curConn].tableExists("book")); - } -} - -/** -* Open transaction to all our databases, perform INSERT followed by SELECT -* and commit. Uses the normal table "test". -*/ -TestBody.prototype.test_real_table_insert_select = -function test_real_table_insert_select() -{ - var stmts = new Array(); - - for (var curConn in CONN_LIST) - this.conn[curConn].beginTransaction(); - - for (var curConn in CONN_LIST) - this.conn[curConn].executeSimpleSQL( - "INSERT INTO test (name) VALUES ('Test')"); - - for (var curConn in CONN_LIST) - stmts[curConn] = this.conn[curConn].createStatement("SELECT * FROM test"); - - for (var curConn in CONN_LIST) - stmts[curConn].executeStep(); - - for (var curConn in CONN_LIST) - do_check_eq(1, stmts[curConn].getInt32(0)); - - for (var curConn in CONN_LIST) - stmts[curConn].reset(); - - for (var curConn in CONN_LIST) - stmts[curConn].finalize(); - - for (var curConn in CONN_LIST) - this.conn[curConn].commitTransaction(); -} - -/** -* Open transaction to all our databases, perform INSERT followed by SELECT -* and commit. Uses the table "book" which is virtual for non-shared databases. -*/ -TestBody.prototype.test_virtual_table_insert_select = -function test_virtual_table_insert_select() -{ - var stmts = new Array(); - - for (var curConn in CONN_LIST) - this.conn[curConn].beginTransaction(); - - for (var curConn in CONN_LIST) - this.conn[curConn].executeSimpleSQL( - "INSERT INTO book VALUES ('Frank Herbert', 'The Dune')"); - - for (var curConn in CONN_LIST) - stmts[curConn] = this.conn[curConn].createStatement( - "SELECT * FROM book WHERE author >= 'Frank'"); - - for (var curConn in CONN_LIST) - stmts[curConn].executeStep(); - - for (var curConn in CONN_LIST) - do_check_eq("Frank Herbert", stmts[curConn].getString(0)); - - for (var curConn in CONN_LIST) - stmts[curConn].reset(); - - for (var curConn in CONN_LIST) - stmts[curConn].finalize(); - - for (var curConn in CONN_LIST) - this.conn[curConn].commitTransaction(); -} - -var tests = [ - "test_initialize_database", - "test_create_tables", - "test_real_table_insert_select", - "test_virtual_table_insert_select" -]; - -function run_test() -{ - var tb = new TestBody; - - try { - for (var i = 0; i < tests.length; ++i) - tb[tests[i]](); - } - finally { - for (var curConn in CONN_LIST) { - var errStr = tb.conn[curConn].lastErrorString; - if (errStr != "not an error") // If we had an error, we want to see it - print("*** Database error: " + errStr); - tb.conn[curConn].close(); - } - tb.cleanUp(); // We always want to cleanup our databases - } -} diff -Nru firefox-3.6.3+nobinonly/mozilla/testing/crashtest/crashtests.list firefox-3.6.4+build1+nobinonly/mozilla/testing/crashtest/crashtests.list --- firefox-3.6.3+nobinonly/mozilla/testing/crashtest/crashtests.list 2010-04-02 16:59:05.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/testing/crashtest/crashtests.list 2010-04-16 17:32:38.000000000 +0100 @@ -50,6 +50,7 @@ include ../../gfx/thebes/crashtests/crashtests.list include ../../modules/libpr0n/test/crashtests/crashtests.list +include ../../modules/plugin/test/crashtests/crashtests.list include ../../parser/htmlparser/tests/crashtests/crashtests.list diff -Nru firefox-3.6.3+nobinonly/mozilla/testing/mochitest/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/testing/mochitest/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/testing/mochitest/Makefile.in 2010-04-02 16:59:05.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/testing/mochitest/Makefile.in 2010-04-16 17:32:38.000000000 +0100 @@ -98,8 +98,15 @@ pk12util$(BIN_SUFFIX) \ $(NULL) +ifeq ($(OS_ARCH),WINNT) +TEST_HARNESS_BINS += \ + crashinject$(BIN_SUFFIX) \ + crashinjectdll$(DLL_SUFFIX) \ + $(NULL) +endif + ifeq ($(OS_ARCH),Darwin) -TEST_HARNESS_BINS += fix-macosx-stack.pl +TEST_HARNESS_BINS += fix-macosx-stack.pl fix_macosx_stack.py endif ifeq ($(OS_ARCH),Linux) diff -Nru firefox-3.6.3+nobinonly/mozilla/testing/mochitest/runtests.py.in firefox-3.6.4+build1+nobinonly/mozilla/testing/mochitest/runtests.py.in --- firefox-3.6.3+nobinonly/mozilla/testing/mochitest/runtests.py.in 2010-04-02 16:59:05.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/testing/mochitest/runtests.py.in 2010-04-16 17:32:38.000000000 +0100 @@ -52,55 +52,9 @@ from urllib import quote_plus as encodeURIComponent import urllib2 import commands -import automation -from automationutils import addCommonOptions, processLeakLog - -# Path to the test script on the server -TEST_SERVER_HOST = "localhost:8888" -TEST_PATH = "/tests/" -CHROME_PATH = "/redirect.html"; -A11Y_PATH = "/redirect-a11y.html" -TESTS_URL = "http://" + TEST_SERVER_HOST + TEST_PATH -CHROMETESTS_URL = "http://" + TEST_SERVER_HOST + CHROME_PATH -A11YTESTS_URL = "http://" + TEST_SERVER_HOST + A11Y_PATH -SERVER_SHUTDOWN_URL = "http://" + TEST_SERVER_HOST + "/server/shutdown" -# main browser chrome URL, same as browser.chromeURL pref -#ifdef MOZ_SUITE -BROWSER_CHROME_URL = "chrome://navigator/content/navigator.xul" -#else -BROWSER_CHROME_URL = "chrome://browser/content/browser.xul" -#endif - -# Max time in seconds to wait for server startup before tests will fail -- if -# this seems big, it's mostly for debug machines where cold startup -# (particularly after a build) takes forever. -SERVER_STARTUP_TIMEOUT = 45 - -oldcwd = os.getcwd() -SCRIPT_DIRECTORY = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0]))) -os.chdir(SCRIPT_DIRECTORY) - -PROFILE_DIRECTORY = os.path.abspath("./mochitesttestingprofile") - -LEAK_REPORT_FILE = os.path.join(PROFILE_DIRECTORY, "runtests_leaks.log") - -# Map of debugging programs to information about them, like default arguments -# and whether or not they are interactive. -DEBUGGER_INFO = { - # gdb requires that you supply the '--args' flag in order to pass arguments - # after the executable name to the executable. - "gdb": { - "interactive": True, - "args": "-q --args" - }, - - # valgrind doesn't explain much about leaks unless you set the - # '--leak-check=full' flag. - "valgrind": { - "interactive": False, - "args": "--leak-check=full" - } -} +from automation import Automation +from automationutils import * +import tempfile ####################### # COMMANDLINE OPTIONS # @@ -108,12 +62,15 @@ class MochitestOptions(optparse.OptionParser): """Parses Mochitest commandline options.""" - def __init__(self, **kwargs): + def __init__(self, automation, scriptdir, **kwargs): + self._automation = automation optparse.OptionParser.__init__(self, **kwargs) defaults = {} - # we want to pass down everything from automation.__all__ - addCommonOptions(self, defaults=dict(zip(automation.__all__, [getattr(automation, x) for x in automation.__all__]))) + # we want to pass down everything from self._automation.__all__ + addCommonOptions(self, defaults=dict(zip(self._automation.__all__, + [getattr(self._automation, x) for x in self._automation.__all__]))) + self._automation.addCommonOptions(self) self.add_option("--close-when-done", action = "store_true", dest = "closeWhenDone", @@ -123,17 +80,17 @@ self.add_option("--appname", action = "store", type = "string", dest = "app", help = "absolute path to application, overriding default") - defaults["app"] = os.path.join(SCRIPT_DIRECTORY, automation.DEFAULT_APP) + defaults["app"] = os.path.join(scriptdir, self._automation.DEFAULT_APP) self.add_option("--utility-path", action = "store", type = "string", dest = "utilityPath", help = "absolute path to directory containing utility programs (xpcshell, ssltunnel, certutil)") - defaults["utilityPath"] = automation.DIST_BIN + defaults["utilityPath"] = self._automation.DIST_BIN self.add_option("--certificate-path", action = "store", type = "string", dest = "certPath", help = "absolute path to directory containing certificate store to use testing profile") - defaults["certPath"] = automation.CERTS_SRC_DIR + defaults["certPath"] = self._automation.CERTS_SRC_DIR self.add_option("--log-file", action = "store", type = "string", @@ -145,6 +102,32 @@ action = "store_true", dest = "autorun", help = "start running tests when the application starts") defaults["autorun"] = False + + self.add_option("--timeout", + type = "int", dest = "timeout", + help = "per-test timeout in seconds") + defaults["timeout"] = None + + self.add_option("--total-chunks", + type = "int", dest = "totalChunks", + help = "how many chunks to split the tests up into") + defaults["totalChunks"] = None + + self.add_option("--this-chunk", + type = "int", dest = "thisChunk", + help = "which chunk to run") + defaults["thisChunk"] = None + + self.add_option("--chunk-by-dir", + type = "int", dest = "chunkByDir", + help = "group tests together in the same chunk that are in the same top chunkByDir directories") + defaults["chunkByDir"] = 0 + + self.add_option("--shuffle", + dest = "shuffle", + action = "store_true", + help = "randomize test order") + defaults["shuffle"] = False LOG_LEVELS = ("DEBUG", "INFO", "WARNING", "ERROR", "FATAL") LEVEL_STRING = ", ".join(LOG_LEVELS) @@ -216,20 +199,6 @@ help = "copy specified files/dirs to testing profile") defaults["extraProfileFiles"] = [] - self.add_option("--debugger", - action = "store", dest = "debugger", - help = "use the given debugger to launch the application") - - self.add_option("--debugger-args", - action = "store", dest = "debuggerArgs", - help = "pass the given args to the debugger _before_ " - "the application on the command line") - - self.add_option("--debugger-interactive", - action = "store_true", dest = "debuggerInteractive", - help = "prevents the test harness from redirecting stdout " - "and stderr for interactive debuggers") - # -h, --help are automatically handled by OptionParser self.set_defaults(**defaults) @@ -243,7 +212,6 @@ self.set_usage(usage) - ####################### # HTTP SERVER SUPPORT # ####################### @@ -251,22 +219,20 @@ class MochitestServer: "Web server used to serve Mochitests, for closer fidelity to the real web." - def __init__(self, options): + def __init__(self, automation, options, profileDir, shutdownURL): + self._automation = automation self._closeWhenDone = options.closeWhenDone self._utilityPath = options.utilityPath self._xrePath = options.xrePath + self._profileDir = profileDir + self.shutdownURL = shutdownURL def start(self): "Run the Mochitest server, returning the process ID of the server." - env = dict(os.environ) - if automation.UNIXISH: - env["LD_LIBRARY_PATH"] = self._xrePath - env["MOZILLA_FIVE_HOME"] = self._xrePath - env["XPCOM_DEBUG_BREAK"] = "warn" - elif automation.IS_MAC: - env["DYLD_LIBRARY_PATH"] = self._xrePath - elif automation.IS_WIN32: + env = self._automation.environment(xrePath = self._xrePath) + env["XPCOM_DEBUG_BREAK"] = "warn" + if self._automation.IS_WIN32: env["PATH"] = env["PATH"] + ";" + self._xrePath args = ["-g", self._xrePath, @@ -275,19 +241,18 @@ "-f", "./" + "server.js"] xpcshell = os.path.join(self._utilityPath, - "xpcshell" + automation.BIN_SUFFIX) - self._process = automation.Process([xpcshell] + args, env = env) + "xpcshell" + self._automation.BIN_SUFFIX) + self._process = self._automation.Process([xpcshell] + args, env = env) pid = self._process.pid if pid < 0: print "Error starting server." sys.exit(2) - automation.log.info("INFO | runtests.py | Server pid: %d", pid) - + self._automation.log.info("INFO | runtests.py | Server pid: %d", pid) def ensureReady(self, timeout): assert timeout >= 0 - aliveFile = os.path.join(PROFILE_DIRECTORY, "server_alive.txt") + aliveFile = os.path.join(self._profileDir, "server_alive.txt") i = 0 while i < timeout: if os.path.exists(aliveFile): @@ -301,229 +266,233 @@ def stop(self): try: - c = urllib2.urlopen(SERVER_SHUTDOWN_URL) + c = urllib2.urlopen(self.shutdownURL) c.read() c.close() self._process.wait() except: self._process.kill() -def getFullPath(path): - "Get an absolute path relative to oldcwd." - return os.path.normpath(os.path.join(oldcwd, os.path.expanduser(path))) - -def searchPath(path): - "Go one step beyond getFullPath and try the various folders in PATH" - # Try looking in the current working directory first. - newpath = getFullPath(path) - if os.path.exists(newpath): - return newpath - - # At this point we have to fail if a directory was given (to prevent cases - # like './gdb' from matching '/usr/bin/./gdb'). - if not os.path.dirname(path): - for dir in os.environ['PATH'].split(os.pathsep): - newpath = os.path.join(dir, path) - if os.path.exists(newpath): - return newpath - return None - -################# -# MAIN FUNCTION # -################# - -def main(): - parser = MochitestOptions() - options, args = parser.parse_args() - if options.xrePath is None: - # default xrePath to the app path if not provided - # but only if an app path was explicitly provided - if options.app != parser.defaults['app']: - options.xrePath = os.path.dirname(options.app) +class Mochitest(object): + # Path to the test script on the server + TEST_SERVER_HOST = "localhost:8888" + TEST_PATH = "/tests/" + CHROME_PATH = "/redirect.html"; + A11Y_PATH = "/redirect-a11y.html" + urlOpts = [] + runSSLTunnel = True + + oldcwd = os.getcwd() + + def __init__(self, automation): + self.automation = automation + + # Max time in seconds to wait for server startup before tests will fail -- if + # this seems big, it's mostly for debug machines where cold startup + # (particularly after a build) takes forever. + if self.automation.IS_DEBUG_BUILD: + self.SERVER_STARTUP_TIMEOUT = 180 else: - # otherwise default to dist/bin - options.xrePath = automation.DIST_BIN + self.SERVER_STARTUP_TIMEOUT = 90 - # allow relative paths - options.xrePath = getFullPath(options.xrePath) + self.SCRIPT_DIRECTORY = os.path.abspath(os.path.realpath(os.path.dirname(__file__))) + os.chdir(self.SCRIPT_DIRECTORY) - options.app = getFullPath(options.app) - if not os.path.exists(options.app): - msg = """\ -Error: Path %(app)s doesn't exist. -Are you executing $objdir/_tests/testing/mochitest/runtests.py?""" - print msg % {"app": options.app} - sys.exit(1) - - options.utilityPath = getFullPath(options.utilityPath) - options.certPath = getFullPath(options.certPath) - options.symbolsPath = getFullPath(options.symbolsPath) - - debuggerInfo = None - - if options.debugger: - debuggerPath = searchPath(options.debugger) - if not debuggerPath: - print "Error: Path %s doesn't exist." % options.debugger - sys.exit(1) + self.PROFILE_DIRECTORY = os.path.abspath("./mochitesttestingprofile") - debuggerName = os.path.basename(debuggerPath).lower() + self.LEAK_REPORT_FILE = os.path.join(self.PROFILE_DIRECTORY, "runtests_leaks.log") - def getDebuggerInfo(type, default): - if debuggerName in DEBUGGER_INFO and type in DEBUGGER_INFO[debuggerName]: - return DEBUGGER_INFO[debuggerName][type] - return default - - debuggerInfo = { - "path": debuggerPath, - "interactive" : getDebuggerInfo("interactive", False), - "args": getDebuggerInfo("args", "").split() - } - - if options.debuggerArgs: - debuggerInfo["args"] = options.debuggerArgs.split() - if options.debuggerInteractive: - debuggerInfo["interactive"] = options.debuggerInteractive - - # browser environment - browserEnv = dict(os.environ) - - # These variables are necessary for correct application startup; change - # via the commandline at your own risk. - # NO_EM_RESTART: will do a '-silent' run instead. - browserEnv["NO_EM_RESTART"] = "1" - browserEnv["XPCOM_DEBUG_BREAK"] = "stack" - appDir = os.path.dirname(options.app) - if automation.UNIXISH: - browserEnv["LD_LIBRARY_PATH"] = appDir - browserEnv["MOZILLA_FIVE_HOME"] = appDir - browserEnv["GNOME_DISABLE_CRASH_DIALOG"] = "1" - - for v in options.environment: - ix = v.find("=") - if ix <= 0: - print "Error: syntax error in --setenv=" + v - sys.exit(1) - browserEnv[v[:ix]] = v[ix + 1:] - - automation.initializeProfile(PROFILE_DIRECTORY) - manifest = addChromeToProfile(options) - copyExtraFilesToProfile(options) - server = MochitestServer(options) - server.start() - - # If we're lucky, the server has fully started by now, and all paths are - # ready, etc. However, xpcshell cold start times suck, at least for debug - # builds. We'll try to connect to the server for awhile, and if we fail, - # we'll try to kill the server and exit with an error. - server.ensureReady(SERVER_STARTUP_TIMEOUT) - - # URL parameters to test URL: - # - # autorun -- kick off tests automatically - # closeWhenDone -- runs quit.js after tests - # logFile -- logs test run to an absolute path - # + def getFullPath(self, path): + "Get an absolute path relative to self.oldcwd." + return os.path.normpath(os.path.join(self.oldcwd, os.path.expanduser(path))) + + def buildTestPath(self, options): + """ build the url path to the specific test harness and test file or directory """ + testHost = "http://" + self.TEST_SERVER_HOST + testURL = testHost + self.TEST_PATH + options.testPath + if options.chrome: + testURL = testHost + self.CHROME_PATH + if options.testPath: + self.urlOpts.append("testPath=" + encodeURIComponent(options.testPath)) + elif options.a11y: + testURL = testHost + self.A11Y_PATH + if options.testPath: + self.urlOpts.append("testPath=" + encodeURIComponent(options.testPath)) + elif options.browserChrome: + testURL = "about:blank" + return testURL + + def startWebServer(self, options): + """ create the webserver and start it up """ + shutdownURL = "http://" + self.TEST_SERVER_HOST + "/server/shutdown" + self.server = MochitestServer(self.automation, options, self.PROFILE_DIRECTORY, shutdownURL) + self.server.start() + + # If we're lucky, the server has fully started by now, and all paths are + # ready, etc. However, xpcshell cold start times suck, at least for debug + # builds. We'll try to connect to the server for awhile, and if we fail, + # we'll try to kill the server and exit with an error. + self.server.ensureReady(self.SERVER_STARTUP_TIMEOUT) + + def stopWebServer(self): + """ Server's no longer needed, and perhaps more importantly, anything it might + spew to console shouldn't disrupt the leak information table we print next. + """ + self.server.stop() + + def getLogFilePath(self, logFile): + """ return the log file path relative to the device we are testing on, in most cases + it will be the full path on the local system + """ + return self.getFullPath(logFile) + + def buildProfile(self, options): + """ create the profile and add optional chrome bits and files if requested """ + self.automation.initializeProfile(self.PROFILE_DIRECTORY, options.extraPrefs) + manifest = self.addChromeToProfile(options) + self.copyExtraFilesToProfile(options) + return manifest + + def buildBrowserEnv(self, options): + """ build the environment variables for the specific test and operating system """ + browserEnv = self.automation.environment(xrePath = options.xrePath) + + # These variables are necessary for correct application startup; change + # via the commandline at your own risk. + browserEnv["XPCOM_DEBUG_BREAK"] = "stack" + + for v in options.environment: + ix = v.find("=") + if ix <= 0: + print "Error: syntax error in --setenv=" + v + return None + browserEnv[v[:ix]] = v[ix + 1:] + + browserEnv["XPCOM_MEM_BLOAT_LOG"] = self.LEAK_REPORT_FILE + + if options.fatalAssertions: + browserEnv["XPCOM_DEBUG_BREAK"] = "stack-and-abort" + + return browserEnv + + def runExtensionRegistration(self, options, browserEnv): + """ run once with -silent to let the extension manager do its thing + and then exit the app + """ + self.automation.log.info("INFO | runtests.py | Performing extension manager registration: start.\n") + # Don't care about this |status|: |runApp()| reporting it should be enough. + status = self.automation.runApp(None, browserEnv, options.app, + self.PROFILE_DIRECTORY, ["-silent"], + utilityPath = options.utilityPath, + xrePath = options.xrePath, + symbolsPath=options.symbolsPath) + # We don't care to call |processLeakLog()| for this step. + self.automation.log.info("\nINFO | runtests.py | Performing extension manager registration: end.") + + def buildURLOptions(self, options): + """ Add test control options from the command line to the url + + URL parameters to test URL: + + autorun -- kick off tests automatically + closeWhenDone -- runs quit.js after tests + logFile -- logs test run to an absolute path + totalChunks -- how many chunks to split tests into + thisChunk -- which chunk to run + timeout -- per-test timeout in seconds + """ - # consoleLevel, fileLevel: set the logging level of the console and - # file logs, if activated. - # - - testURL = TESTS_URL + options.testPath - urlOpts = [] - if options.chrome: - testURL = CHROMETESTS_URL - if options.testPath: - urlOpts.append("testPath=" + encodeURIComponent(options.testPath)) - elif options.a11y: - testURL = A11YTESTS_URL - if options.testPath: - urlOpts.append("testPath=" + encodeURIComponent(options.testPath)) - elif options.browserChrome: - testURL = "about:blank" - - # allow relative paths for logFile - if options.logFile: - options.logFile = getFullPath(options.logFile) - if options.browserChrome: - makeTestConfig(options) - else: - if options.autorun: - urlOpts.append("autorun=1") - if options.closeWhenDone: - urlOpts.append("closeWhenDone=1") + # allow relative paths for logFile if options.logFile: - urlOpts.append("logFile=" + encodeURIComponent(options.logFile)) - urlOpts.append("fileLevel=" + encodeURIComponent(options.fileLevel)) - if options.consoleLevel: - urlOpts.append("consoleLevel=" + encodeURIComponent(options.consoleLevel)) - if len(urlOpts) > 0: - testURL += "?" + "&".join(urlOpts) - - browserEnv["XPCOM_MEM_BLOAT_LOG"] = LEAK_REPORT_FILE - - if options.fatalAssertions: - browserEnv["XPCOM_DEBUG_BREAK"] = "stack-and-abort" - - # run once with -silent to let the extension manager do its thing - # and then exit the app - automation.log.info("INFO | runtests.py | Performing extension manager registration: start.\n") - # Don't care about this |status|: |runApp()| reporting it should be enough. - status = automation.runApp(None, browserEnv, options.app, - PROFILE_DIRECTORY, ["-silent"], - utilityPath = options.utilityPath, - xrePath = options.xrePath, - symbolsPath=options.symbolsPath) - # We don't care to call |processLeakLog()| for this step. - automation.log.info("\nINFO | runtests.py | Performing extension manager registration: end.") - - # Remove the leak detection file so it can't "leak" to the tests run. - # The file is not there if leak logging was not enabled in the application build. - if os.path.exists(LEAK_REPORT_FILE): - os.remove(LEAK_REPORT_FILE) - - # then again to actually run reftest - automation.log.info("INFO | runtests.py | Running tests: start.\n") - status = automation.runApp(testURL, browserEnv, options.app, - PROFILE_DIRECTORY, options.browserArgs, - runSSLTunnel = True, - utilityPath = options.utilityPath, - xrePath = options.xrePath, - certPath=options.certPath, - debuggerInfo=debuggerInfo, - symbolsPath=options.symbolsPath) - - # Server's no longer needed, and perhaps more importantly, anything it might - # spew to console shouldn't disrupt the leak information table we print next. - server.stop() - - processLeakLog(LEAK_REPORT_FILE, options.leakThreshold) - automation.log.info("\nINFO | runtests.py | Running tests: end.") - - # delete the profile and manifest - os.remove(manifest) - - # hanging due to non-halting threads is no fun; assume we hit the errors we - # were going to hit already and exit. - sys.exit(status) - - - -####################### -# CONFIGURATION SETUP # -####################### - -def makeTestConfig(options): - "Creates a test configuration file for customizing test execution." - def boolString(b): - if b: - return "true" - return "false" - - logFile = options.logFile.replace("\\", "\\\\") - testPath = options.testPath.replace("\\", "\\\\") - content = """\ + options.logFile = self.getFullPath(options.logFile) + if options.browserChrome: + self.makeTestConfig(options) + else: + if options.autorun: + self.urlOpts.append("autorun=1") + if options.timeout: + self.urlOpts.append("timeout=%d" % options.timeout) + if options.closeWhenDone: + self.urlOpts.append("closeWhenDone=1") + if options.logFile: + self.urlOpts.append("logFile=" + encodeURIComponent(options.logFile)) + self.urlOpts.append("fileLevel=" + encodeURIComponent(options.fileLevel)) + if options.consoleLevel: + self.urlOpts.append("consoleLevel=" + encodeURIComponent(options.consoleLevel)) + if options.totalChunks: + self.urlOpts.append("totalChunks=%d" % options.totalChunks) + self.urlOpts.append("thisChunk=%d" % options.thisChunk) + if options.chunkByDir: + self.urlOpts.append("chunkByDir=%d" % options.chunkByDir) + if options.shuffle: + self.urlOpts.append("shuffle=1") + + def cleanup(self, manifest): + """ remove temporary files and profile """ + os.remove(manifest) + shutil.rmtree(self.PROFILE_DIRECTORY) + + def runTests(self, options): + """ Prepare, configure, run tests and cleanup """ + debuggerInfo = getDebuggerInfo(self.oldcwd, options.debugger, options.debuggerArgs, + options.debuggerInteractive); + + browserEnv = self.buildBrowserEnv(options) + if (browserEnv == None): + return 1 + + manifest = self.buildProfile(options) + self.startWebServer(options) + + + testURL = self.buildTestPath(options) + self.buildURLOptions(options) + if (len(self.urlOpts) > 0): + testURL += "?" + "&".join(self.urlOpts) + + self.runExtensionRegistration(options, browserEnv) + + # Remove the leak detection file so it can't "leak" to the tests run. + # The file is not there if leak logging was not enabled in the application build. + if os.path.exists(self.LEAK_REPORT_FILE): + os.remove(self.LEAK_REPORT_FILE) + + # then again to actually run mochitest + if options.timeout: + timeout = options.timeout + 30 + elif not options.autorun: + timeout = None + else: + timeout = 330.0 # default JS harness timeout is 300 seconds + self.automation.log.info("INFO | runtests.py | Running tests: start.\n") + status = self.automation.runApp(testURL, browserEnv, options.app, + self.PROFILE_DIRECTORY, options.browserArgs, + runSSLTunnel = self.runSSLTunnel, + utilityPath = options.utilityPath, + xrePath = options.xrePath, + certPath=options.certPath, + debuggerInfo=debuggerInfo, + symbolsPath=options.symbolsPath, + timeout = timeout) + + self.stopWebServer() + processLeakLog(self.LEAK_REPORT_FILE, options.leakThreshold) + self.automation.log.info("\nINFO | runtests.py | Running tests: end.") + + self.cleanup(manifest) + return status + + def makeTestConfig(self, options): + "Creates a test configuration file for customizing test execution." + def boolString(b): + if b: + return "true" + return "false" + + logFile = options.logFile.replace("\\", "\\\\") + testPath = options.testPath.replace("\\", "\\\\") + content = """\ ({ autoRun: %(autorun)s, closeWhenDone: %(closeWhenDone)s, @@ -534,20 +503,18 @@ "logPath": logFile, "testPath": testPath} - config = open(os.path.join(PROFILE_DIRECTORY, "testConfig.js"), "w") - config.write(content) - config.close() + config = open(os.path.join(self.PROFILE_DIRECTORY, "testConfig.js"), "w") + config.write(content) + config.close() -def addChromeToProfile(options): - "Adds MochiKit chrome tests to the profile." + def addChromeToProfile(self, options): + "Adds MochiKit chrome tests to the profile." - chromedir = os.path.join(PROFILE_DIRECTORY, "chrome") - os.mkdir(chromedir) + chromedir = os.path.join(self.PROFILE_DIRECTORY, "chrome") + os.mkdir(chromedir) - chrome = [] - - part = """ + chrome = """ @namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); /* set default namespace to XUL */ toolbar, toolbarpalette { @@ -557,47 +524,86 @@ background-image: none !important; } """ - chrome.append(part) - + # write userChrome.css + chromeFile = open(os.path.join(self.PROFILE_DIRECTORY, "userChrome.css"), "a") + chromeFile.write(chrome) + chromeFile.close() + + + # register our chrome dir + chrometestDir = os.path.abspath(".") + "/" + if self.automation.IS_WIN32: + chrometestDir = "file:///" + chrometestDir.replace("\\", "/") + + temp_file = os.path.join(tempfile.mkdtemp(), "mochikit.manifest") + manifestFile = open(temp_file, "w") + manifestFile.write("content mochikit " + chrometestDir + " contentaccessible=yes\n") + + if options.browserChrome: + manifestFile.write("""overlay chrome://navigator/content/navigator.xul chrome://mochikit/content/browser-test-overlay.xul +overlay chrome://browser/content/browser.xul chrome://mochikit/content/browser-test-overlay.xul +""") + manifestFile.close() + + return self.installChromeFile(temp_file, options) + + def installChromeFile(self, filename, options): + (p, file) = os.path.split(filename) + (path, leaf) = os.path.split(options.app) + manifest = os.path.join(path, "chrome", file) + shutil.copy(filename, manifest) + return manifest + + def copyExtraFilesToProfile(self, options): + "Copy extra files or dirs specified on the command line to the testing profile." + for f in options.extraProfileFiles: + abspath = self.getFullPath(f) + dest = os.path.join(self.PROFILE_DIRECTORY, os.path.basename(abspath)) + if os.path.isdir(abspath): + shutil.copytree(abspath, dest) + else: + shutil.copy(abspath, dest) - # write userChrome.css - chromeFile = open(os.path.join(PROFILE_DIRECTORY, "userChrome.css"), "a") - chromeFile.write("".join(chrome)) - chromeFile.close() +def main(): + automation = Automation() + mochitest = Mochitest(automation) + parser = MochitestOptions(automation, mochitest.SCRIPT_DIRECTORY) + options, args = parser.parse_args() + if options.totalChunks is not None and options.thisChunk is None: + parser.error("thisChunk must be specified when totalChunks is specified") - # register our chrome dir - chrometestDir = os.path.abspath(".") + "/" - if automation.IS_WIN32: - chrometestDir = "file:///" + chrometestDir.replace("\\", "/") + if options.totalChunks: + if not 1 <= options.thisChunk <= options.totalChunks: + parser.error("thisChunk must be between 1 and totalChunks") + if options.xrePath is None: + # default xrePath to the app path if not provided + # but only if an app path was explicitly provided + if options.app != parser.defaults['app']: + options.xrePath = os.path.dirname(options.app) + else: + # otherwise default to dist/bin + options.xrePath = automation.DIST_BIN - (path, leaf) = os.path.split(options.app) - manifest = os.path.join(path, "chrome", "mochikit.manifest") - manifestFile = open(manifest, "w") - manifestFile.write("content mochikit " + chrometestDir + " contentaccessible=yes\n") - if options.browserChrome: - overlayLine = "overlay " + BROWSER_CHROME_URL + " " \ - "chrome://mochikit/content/browser-test-overlay.xul\n" - manifestFile.write(overlayLine) - manifestFile.close() + # allow relative paths + options.xrePath = mochitest.getFullPath(options.xrePath) - return manifest + options.app = mochitest.getFullPath(options.app) + if not os.path.exists(options.app): + msg = """\ + Error: Path %(app)s doesn't exist. + Are you executing $objdir/_tests/testing/mochitest/runtests.py?""" + print msg % {"app": options.app} + sys.exit(1) -def copyExtraFilesToProfile(options): - "Copy extra files or dirs specified on the command line to the testing profile." - for f in options.extraProfileFiles: - abspath = getFullPath(f) - dest = os.path.join(PROFILE_DIRECTORY, os.path.basename(abspath)) - if os.path.isdir(abspath): - shutil.copytree(abspath, dest) - else: - shutil.copy(abspath, dest) + options.utilityPath = mochitest.getFullPath(options.utilityPath) + options.certPath = mochitest.getFullPath(options.certPath) + if options.symbolsPath: + options.symbolsPath = mochitest.getFullPath(options.symbolsPath) -######### -# DO IT # -######### + sys.exit(mochitest.runTests(options)) if __name__ == "__main__": main() diff -Nru firefox-3.6.3+nobinonly/mozilla/testing/mochitest/server.js firefox-3.6.4+build1+nobinonly/mozilla/testing/mochitest/server.js --- firefox-3.6.3+nobinonly/mozilla/testing/mochitest/server.js 2010-04-02 16:59:05.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/testing/mochitest/server.js 2010-04-16 17:32:38.000000000 +0100 @@ -198,6 +198,7 @@ server.registerDirectory("/", serverBasePath); server.registerPathHandler("/server/shutdown", serverShutdown); + server.registerPathHandler("/server/debug", serverDebug); server.registerContentType("sjs", "sjs"); // .sjs == CGI-like functionality server.registerContentType("jar", "application/x-jar"); server.registerContentType("ogg", "application/ogg"); @@ -205,6 +206,21 @@ server.registerContentType("oga", "audio/ogg"); server.setIndexHandler(defaultDirHandler); + var serverRoot = + { + getFile: function getFile(path) + { + var file = serverBasePath.clone().QueryInterface(Ci.nsILocalFile); + path.split("/").forEach(function(p) { + file.appendRelativePath(p); + }); + return file; + }, + QueryInterface: function(aIID) { return this; } + }; + + server.setObjectState("SERVER_ROOT", serverRoot); + processLocations(server); return server; @@ -297,6 +313,39 @@ server.stop(serverStopped); } +// /server/debug?[012] +function serverDebug(metadata, response) +{ + response.setStatusLine(metadata.httpVersion, 400, "Bad debugging level"); + if (metadata.queryString.length !== 1) + return; + + var mode; + if (metadata.queryString === "0") { + // do this now so it gets logged with the old mode + dumpn("Server debug logs disabled."); + DEBUG = false; + DEBUG_TIMESTAMP = false; + mode = "disabled"; + } else if (metadata.queryString === "1") { + DEBUG = true; + DEBUG_TIMESTAMP = false; + mode = "enabled"; + } else if (metadata.queryString === "2") { + DEBUG = true; + DEBUG_TIMESTAMP = true; + mode = "enabled, with timestamps"; + } else { + return; + } + + response.setStatusLine(metadata.httpVersion, 200, "OK"); + response.setHeader("Content-type", "text/plain", false); + var body = "Server debug logs " + mode + "."; + response.bodyOutputStream.write(body, body.length); + dumpn(body); +} + // // DIRECTORY LISTINGS // diff -Nru firefox-3.6.3+nobinonly/mozilla/testing/testsuite-targets.mk firefox-3.6.4+build1+nobinonly/mozilla/testing/testsuite-targets.mk --- firefox-3.6.3+nobinonly/mozilla/testing/testsuite-targets.mk 2010-04-02 16:59:06.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/testing/testsuite-targets.mk 2010-04-16 17:32:39.000000000 +0100 @@ -47,7 +47,7 @@ # Usage: |make [TEST_PATH=...] [EXTRA_TEST_ARGS=...] mochitest*|. -mochitest:: mochitest-plain mochitest-chrome mochitest-a11y +mochitest:: mochitest-plain mochitest-chrome mochitest-a11y mochitest-ipcplugins RUN_MOCHITEST = \ rm -f ./$@.log && \ @@ -81,6 +81,10 @@ $(CHECK_TEST_ERROR) +mochitest-ipcplugins: + $(RUN_MOCHITEST) --setpref=dom.ipc.plugins.enabled=true --test-path=modules/plugin/test + $(CHECK_TEST_ERROR) + # Usage: |make [EXTRA_TEST_ARGS=...] *test|. RUN_REFTEST = rm -f ./$@.log && $(PYTHON) _tests/reftest/runreftest.py $(EXTRA_TEST_ARGS) $(1) | tee ./$@.log @@ -142,7 +146,7 @@ $(MAKE) -C $(DEPTH)/js/src/tests stage-package .PHONY: \ - mochitest mochitest-plain mochitest-chrome mochitest-a11y \ + mochitest mochitest-plain mochitest-chrome mochitest-a11y mochitest-ipcplugins \ reftest crashtest \ xpcshell-tests \ jstestbrowser \ diff -Nru firefox-3.6.3+nobinonly/mozilla/testing/xpcshell/runxpcshelltests.py firefox-3.6.4+build1+nobinonly/mozilla/testing/xpcshell/runxpcshelltests.py --- firefox-3.6.3+nobinonly/mozilla/testing/xpcshell/runxpcshelltests.py 2010-04-02 16:59:06.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/testing/xpcshell/runxpcshelltests.py 2010-04-16 17:32:39.000000000 +0100 @@ -22,6 +22,7 @@ # Contributor(s): # Serge Gautherie # Ted Mielczarek +# Joel Maher # # Alternatively, the contents of this file may be used under the terms of # either the GNU General Public License Version 2 or later (the "GPL"), or @@ -43,233 +44,248 @@ from subprocess import Popen, PIPE, STDOUT from tempfile import mkdtemp -from automationutils import addCommonOptions, checkForCrashes, dumpLeakLog +from automationutils import * -# Init logging -log = logging.getLogger() -handler = logging.StreamHandler(sys.stdout) -log.setLevel(logging.INFO) -log.addHandler(handler) - -def readManifest(manifest): - """Given a manifest file containing a list of test directories, - return a list of absolute paths to the directories contained within.""" - manifestdir = os.path.dirname(manifest) - testdirs = [] - try: - f = open(manifest, "r") - for line in f: - dir = line.rstrip() - path = os.path.join(manifestdir, dir) - if os.path.isdir(path): - testdirs.append(path) - f.close() - except: - pass # just eat exceptions - return testdirs - -def runTests(xpcshell, xrePath=None, symbolsPath=None, - manifest=None, testdirs=[], testPath=None, - interactive=False, logfiles=True): - """Run xpcshell tests. - - |xpcshell|, is the xpcshell executable to use to run the tests. - |xrePath|, if provided, is the path to the XRE to use. - |symbolsPath|, if provided is the path to a directory containing - breakpad symbols for processing crashes in tests. - |manifest|, if provided, is a file containing a list of - test directories to run. - |testdirs|, if provided, is a list of absolute paths of test directories. - No-manifest only option. - |testPath|, if provided, indicates a single path and/or test to run. - |interactive|, if set to True, indicates to provide an xpcshell prompt - instead of automatically executing the test. - |logfiles|, if set to False, indicates not to save output to log files. - Non-interactive only option. - """ - - if not testdirs and not manifest: - # nothing to test! - print >>sys.stderr, "Error: No test dirs or test manifest specified!" - return False - - passCount = 0 - failCount = 0 - - testharnessdir = os.path.dirname(os.path.abspath(__file__)) - xpcshell = os.path.abspath(xpcshell) - # we assume that httpd.js lives in components/ relative to xpcshell - httpdJSPath = os.path.join(os.path.dirname(xpcshell), "components", "httpd.js").replace("\\", "/"); - - env = dict(os.environ) - # Make assertions fatal - env["XPCOM_DEBUG_BREAK"] = "stack-and-abort" - # Don't launch the crash reporter client - env["MOZ_CRASHREPORTER_NO_REPORT"] = "1" - - if xrePath is None: - xrePath = os.path.dirname(xpcshell) - else: - xrePath = os.path.abspath(xrePath) - if sys.platform == 'win32': - env["PATH"] = env["PATH"] + ";" + xrePath - elif sys.platform in ('os2emx', 'os2knix'): - os.environ["BEGINLIBPATH"] = xrePath + ";" + env["BEGINLIBPATH"] - os.environ["LIBPATHSTRICT"] = "T" - elif sys.platform == 'osx': - env["DYLD_LIBRARY_PATH"] = xrePath - else: # unix or linux? - env["LD_LIBRARY_PATH"] = xrePath - - # xpcsRunArgs: function to call to run the test. - # pStdout, pStderr: Parameter values for later |Popen()| call. - if interactive: - xpcsRunArgs = [ +class XPCShellTests(object): + + log = logging.getLogger() + oldcwd = os.getcwd() + + def __init__(self): + # Init logging + handler = logging.StreamHandler(sys.stdout) + self.log.setLevel(logging.INFO) + self.log.addHandler(handler) + + def readManifest(self, manifest): + """Given a manifest file containing a list of test directories, + return a list of absolute paths to the directories contained within.""" + manifestdir = os.path.dirname(manifest) + testdirs = [] + try: + f = open(manifest, "r") + for line in f: + dir = line.rstrip() + path = os.path.join(manifestdir, dir) + if os.path.isdir(path): + testdirs.append(path) + f.close() + except: + pass # just eat exceptions + return testdirs + + def runTests(self, xpcshell, xrePath=None, symbolsPath=None, + manifest=None, testdirs=[], testPath=None, + interactive=False, logfiles=True, + debuggerInfo=None): + """Run xpcshell tests. + + |xpcshell|, is the xpcshell executable to use to run the tests. + |xrePath|, if provided, is the path to the XRE to use. + |symbolsPath|, if provided is the path to a directory containing + breakpad symbols for processing crashes in tests. + |manifest|, if provided, is a file containing a list of + test directories to run. + |testdirs|, if provided, is a list of absolute paths of test directories. + No-manifest only option. + |testPath|, if provided, indicates a single path and/or test to run. + |interactive|, if set to True, indicates to provide an xpcshell prompt + instead of automatically executing the test. + |logfiles|, if set to False, indicates not to save output to log files. + Non-interactive only option. + |debuggerInfo|, if set, specifies the debugger and debugger arguments + that will be used to launch xpcshell. + """ + + if not testdirs and not manifest: + # nothing to test! + print >>sys.stderr, "Error: No test dirs or test manifest specified!" + return False + + passCount = 0 + failCount = 0 + + testharnessdir = os.path.dirname(os.path.abspath(__file__)) + xpcshell = os.path.abspath(xpcshell) + # we assume that httpd.js lives in components/ relative to xpcshell + httpdJSPath = os.path.join(os.path.dirname(xpcshell), "components", "httpd.js").replace("\\", "/"); + + env = dict(os.environ) + # Make assertions fatal + env["XPCOM_DEBUG_BREAK"] = "stack-and-abort" + # Don't launch the crash reporter client + env["MOZ_CRASHREPORTER_NO_REPORT"] = "1" + + if xrePath is None: + xrePath = os.path.dirname(xpcshell) + else: + xrePath = os.path.abspath(xrePath) + if sys.platform == 'win32': + env["PATH"] = env["PATH"] + ";" + xrePath + elif sys.platform in ('os2emx', 'os2knix'): + os.environ["BEGINLIBPATH"] = xrePath + ";" + env["BEGINLIBPATH"] + os.environ["LIBPATHSTRICT"] = "T" + elif sys.platform == 'osx': + env["DYLD_LIBRARY_PATH"] = xrePath + else: # unix or linux? + env["LD_LIBRARY_PATH"] = xrePath + + # xpcsRunArgs: function to call to run the test. + # pStdout, pStderr: Parameter values for later |Popen()| call. + if interactive: + xpcsRunArgs = [ '-e', 'print("To start the test, type |_execute_test();|.");', '-i'] - pStdout = None - pStderr = None - else: - xpcsRunArgs = ['-e', '_execute_test();'] - if sys.platform == 'os2emx': pStdout = None + pStderr = None else: - pStdout = PIPE - pStderr = STDOUT + xpcsRunArgs = ['-e', '_execute_test();'] + if (debuggerInfo and debuggerInfo["interactive"]): + pStdout = None + pStderr = None + else: + if sys.platform == 'os2emx': + pStdout = None + else: + pStdout = PIPE + pStderr = STDOUT - # has to be loaded by xpchell: it can't load itself. - xpcsCmd = [xpcshell, '-g', xrePath, '-j', '-s'] + \ - ['-e', 'const _HTTPD_JS_PATH = "%s";' % httpdJSPath, - '-f', os.path.join(testharnessdir, 'head.js')] - - # |testPath| will be the optional path only, or |None|. - # |singleFile| will be the optional test only, or |None|. - singleFile = None - if testPath: - if testPath.endswith('.js'): - # Split into path and file. - if testPath.find('/') == -1: - # Test only. - singleFile = testPath - testPath = None + # has to be loaded by xpchell: it can't load itself. + xpcsCmd = [xpcshell, '-g', xrePath, '-j', '-s'] + \ + ['-e', 'const _HTTPD_JS_PATH = "%s";' % httpdJSPath, + '-f', os.path.join(testharnessdir, 'head.js')] + + if debuggerInfo: + xpcsCmd = [debuggerInfo["path"]] + debuggerInfo["args"] + xpcsCmd + + # |testPath| will be the optional path only, or |None|. + # |singleFile| will be the optional test only, or |None|. + singleFile = None + if testPath: + if testPath.endswith('.js'): + # Split into path and file. + if testPath.find('/') == -1: + # Test only. + singleFile = testPath + testPath = None + else: + # Both path and test. + # Reuse |testPath| temporarily. + testPath = testPath.rsplit('/', 1) + singleFile = testPath[1] + testPath = testPath[0] else: - # Both path and test. - # Reuse |testPath| temporarily. - testPath = testPath.rsplit('/', 1) - singleFile = testPath[1] - testPath = testPath[0] - else: - # Path only. - # Simply remove optional ending separator. - testPath = testPath.rstrip("/") - - # Override testdirs. - if manifest is not None: - testdirs = readManifest(os.path.abspath(manifest)) - - # Process each test directory individually. - for testdir in testdirs: - if testPath and not testdir.endswith(testPath): - continue - - testdir = os.path.abspath(testdir) - - # get the list of head and tail files from the directory - testHeadFiles = [] - for f in sorted(glob(os.path.join(testdir, "head_*.js"))): - if os.path.isfile(f): - testHeadFiles += [f] - testTailFiles = [] - # Tails are executed in the reverse order, to "match" heads order, - # as in "h1-h2-h3 then t3-t2-t1". - for f in reversed(sorted(glob(os.path.join(testdir, "tail_*.js")))): - if os.path.isfile(f): - testTailFiles += [f] - - # if a single test file was specified, we only want to execute that test - testfiles = sorted(glob(os.path.join(testdir, "test_*.js"))) - if singleFile: - if singleFile in [os.path.basename(x) for x in testfiles]: - testfiles = [os.path.join(testdir, singleFile)] - else: # not in this dir? skip it + # Path only. + # Simply remove optional ending separator. + testPath = testPath.rstrip("/") + + # Override testdirs. + if manifest is not None: + testdirs = self.readManifest(os.path.abspath(manifest)) + + # Process each test directory individually. + for testdir in testdirs: + if testPath and not testdir.endswith(testPath): continue - cmdH = ", ".join(['"' + f.replace('\\', '/') + '"' - for f in testHeadFiles]) - cmdT = ", ".join(['"' + f.replace('\\', '/') + '"' - for f in testTailFiles]) - cmdH = xpcsCmd + \ - ['-e', 'const _HEAD_FILES = [%s];' % cmdH] + \ - ['-e', 'const _TAIL_FILES = [%s];' % cmdT] - - # Now execute each test individually. - for test in testfiles: - # The test file will have to be loaded after the head files. - cmdT = ['-e', 'const _TEST_FILE = ["%s"];' % - os.path.join(testdir, test).replace('\\', '/')] - - # create a temp dir that the JS harness can stick a profile in - profileDir = None - try: - profileDir = mkdtemp() - env["XPCSHELL_TEST_PROFILE_DIR"] = profileDir - - # Enable leaks (only) detection to its own log file. - leakLogFile = os.path.join(profileDir, "runxpcshelltests_leaks.log") - env["XPCOM_MEM_LEAK_LOG"] = leakLogFile - - proc = Popen(cmdH + cmdT + xpcsRunArgs, - stdout=pStdout, stderr=pStderr, env=env, cwd=testdir) - - # allow user to kill hung subprocess with SIGINT w/o killing this script - # - don't move this line above Popen, or child will inherit the SIG_IGN - signal.signal(signal.SIGINT, signal.SIG_IGN) - # |stderr == None| as |pStderr| was either |None| or redirected to |stdout|. - stdout, stderr = proc.communicate() - signal.signal(signal.SIGINT, signal.SIG_DFL) - - if interactive: - # Not sure what else to do here... - return True + testdir = os.path.abspath(testdir) + + # get the list of head and tail files from the directory + testHeadFiles = [] + for f in sorted(glob(os.path.join(testdir, "head_*.js"))): + if os.path.isfile(f): + testHeadFiles += [f] + testTailFiles = [] + # Tails are executed in the reverse order, to "match" heads order, + # as in "h1-h2-h3 then t3-t2-t1". + for f in reversed(sorted(glob(os.path.join(testdir, "tail_*.js")))): + if os.path.isfile(f): + testTailFiles += [f] + + # if a single test file was specified, we only want to execute that test + testfiles = sorted(glob(os.path.join(testdir, "test_*.js"))) + if singleFile: + if singleFile in [os.path.basename(x) for x in testfiles]: + testfiles = [os.path.join(testdir, singleFile)] + else: # not in this dir? skip it + continue + + cmdH = ", ".join(['"' + f.replace('\\', '/') + '"' + for f in testHeadFiles]) + cmdT = ", ".join(['"' + f.replace('\\', '/') + '"' + for f in testTailFiles]) + cmdH = xpcsCmd + \ + ['-e', 'const _HEAD_FILES = [%s];' % cmdH] + \ + ['-e', 'const _TAIL_FILES = [%s];' % cmdT] + + # Now execute each test individually. + for test in testfiles: + # The test file will have to be loaded after the head files. + cmdT = ['-e', 'const _TEST_FILE = ["%s"];' % + os.path.join(testdir, test).replace('\\', '/')] + + # create a temp dir that the JS harness can stick a profile in + profileDir = None + try: + profileDir = mkdtemp() + env["XPCSHELL_TEST_PROFILE_DIR"] = profileDir + + # Enable leaks (only) detection to its own log file. + leakLogFile = os.path.join(profileDir, "runxpcshelltests_leaks.log") + env["XPCOM_MEM_LEAK_LOG"] = leakLogFile + + proc = Popen(cmdH + cmdT + xpcsRunArgs, + stdout=pStdout, stderr=pStderr, env=env, cwd=testdir) + + # allow user to kill hung subprocess with SIGINT w/o killing this script + # - don't move this line above Popen, or child will inherit the SIG_IGN + signal.signal(signal.SIGINT, signal.SIG_IGN) + # |stderr == None| as |pStderr| was either |None| or redirected to |stdout|. + stdout, stderr = proc.communicate() + signal.signal(signal.SIGINT, signal.SIG_DFL) + + if interactive: + # Not sure what else to do here... + return True - if proc.returncode != 0 or (stdout and re.search("^TEST-UNEXPECTED-FAIL", stdout, re.MULTILINE)): - print """TEST-UNEXPECTED-FAIL | %s | test failed (with xpcshell return code: %d), see following log: + if proc.returncode != 0 or (stdout and re.search("^TEST-UNEXPECTED-FAIL", stdout, re.MULTILINE)): + print """TEST-UNEXPECTED-FAIL | %s | test failed (with xpcshell return code: %d), see following log: >>>>>>> %s <<<<<<<""" % (test, proc.returncode, stdout) - checkForCrashes(testdir, symbolsPath, testName=test) - failCount += 1 - else: - print "TEST-PASS | %s | test passed" % test - passCount += 1 + failCount += 1 + else: + print "TEST-PASS | %s | test passed" % test + passCount += 1 - dumpLeakLog(leakLogFile, True) + checkForCrashes(testdir, symbolsPath, testName=test) + dumpLeakLog(leakLogFile, True) - if logfiles and stdout: - try: - f = open(test + ".log", "w") - f.write(stdout) - - if os.path.exists(leakLogFile): - leaks = open(leakLogFile, "r") - f.write(leaks.read()) - leaks.close() - finally: - if f: - f.close() - finally: - if profileDir: - shutil.rmtree(profileDir) - - if passCount == 0 and failCount == 0: - print "TEST-UNEXPECTED-FAIL | runxpcshelltests.py | No tests run. Did you pass an invalid --test-path?" - failCount = 1 + if logfiles and stdout: + try: + f = open(test + ".log", "w") + f.write(stdout) + + if os.path.exists(leakLogFile): + leaks = open(leakLogFile, "r") + f.write(leaks.read()) + leaks.close() + finally: + if f: + f.close() + finally: + if profileDir: + shutil.rmtree(profileDir) + + if passCount == 0 and failCount == 0: + print "TEST-UNEXPECTED-FAIL | runxpcshelltests.py | No tests run. Did you pass an invalid --test-path?" + failCount = 1 - print """INFO | Result summary: + print """INFO | Result summary: INFO | Passed: %d INFO | Failed: %d""" % (passCount, failCount) - return failCount == 0 + return failCount == 0 def main(): """Process command line arguments and call runTests() to do the real work.""" @@ -295,23 +311,29 @@ if len(args) < 2 and options.manifest is None or \ (len(args) < 1 and options.manifest is not None): - print >>sys.stderr, """Usage: %s - or: %s --manifest=test.manifest """ % (sys.argv[0], + print >>sys.stderr, """Usage: %s + or: %s --manifest=test.manifest """ % (sys.argv[0], sys.argv[0]) - sys.exit(1) + sys.exit(1) + + xpcsh = XPCShellTests() + debuggerInfo = getDebuggerInfo(xpcsh.oldcwd, options.debugger, options.debuggerArgs, + options.debuggerInteractive); if options.interactive and not options.testPath: print >>sys.stderr, "Error: You must specify a test filename in interactive mode!" sys.exit(1) - if not runTests(args[0], - xrePath=options.xrePath, - symbolsPath=options.symbolsPath, - manifest=options.manifest, - testdirs=args[1:], - testPath=options.testPath, - interactive=options.interactive, - logfiles=options.logfiles): + + if not xpcsh.runTests(args[0], + xrePath=options.xrePath, + symbolsPath=options.symbolsPath, + manifest=options.manifest, + testdirs=args[1:], + testPath=options.testPath, + interactive=options.interactive, + logfiles=options.logfiles, + debuggerInfo=debuggerInfo): sys.exit(1) if __name__ == '__main__': diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/components/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/components/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/components/Makefile.in 2010-04-02 16:59:06.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/components/Makefile.in 2010-04-16 17:32:39.000000000 +0100 @@ -122,9 +122,4 @@ EXTRA_PP_COMPONENTS = nsDefaultCLH.js -ifeq ($(MOZ_BUILD_APP),camino) -DIRS += autocomplete/public -DIRS += downloads/public -endif - include $(topsrcdir)/config/rules.mk diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/components/passwordmgr/test/authenticate.sjs firefox-3.6.4+build1+nobinonly/mozilla/toolkit/components/passwordmgr/test/authenticate.sjs --- firefox-3.6.3+nobinonly/mozilla/toolkit/components/passwordmgr/test/authenticate.sjs 2010-04-02 16:59:09.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/components/passwordmgr/test/authenticate.sjs 2010-04-16 17:32:41.000000000 +0100 @@ -19,7 +19,7 @@ var expected_user = "", expected_pass = "", realm = "mochitest"; var proxy_expected_user = "", proxy_expected_pass = "", proxy_realm = "mochi-proxy"; - var huge = false; + var huge = false, plugin = false; var authHeaderCount = 1; // user=xxx match = /user=([^&]*)/.exec(query); @@ -56,6 +56,11 @@ if (match) huge = true; + // plugin=1 + match = /plugin=1/.exec(query); + if (match) + plugin = true; + // multiple=1 match = /multiple=([^&]*)/.exec(query); if (match) @@ -139,6 +144,11 @@ response.write("This is a footnote after the huge content fill"); } + if (plugin) { + response.write("\n"); + } + response.write(""); } diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/components/places/src/nsNavBookmarks.cpp firefox-3.6.4+build1+nobinonly/mozilla/toolkit/components/places/src/nsNavBookmarks.cpp --- firefox-3.6.3+nobinonly/mozilla/toolkit/components/places/src/nsNavBookmarks.cpp 2010-04-02 16:59:09.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/components/places/src/nsNavBookmarks.cpp 2010-04-16 17:32:42.000000000 +0100 @@ -940,9 +940,7 @@ (void)mDBIsRealBookmark->BindInt64Parameter(0, aPlaceId); (void)mDBIsRealBookmark->BindInt32Parameter(1, TYPE_BOOKMARK); - (void)mDBIsRealBookmark->BindUTF8StringParameter( - 2, NS_LITERAL_CSTRING(LMANNO_FEEDURI) - ); + (void)BindStatementURLCString(mDBIsRealBookmark, 2, NS_LITERAL_CSTRING(LMANNO_FEEDURI)); // If we get any rows, then there exists at least one bookmark corresponding // to aPlaceId that is not a livemark item. diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/components/places/src/nsNavHistory.cpp firefox-3.6.4+build1+nobinonly/mozilla/toolkit/components/places/src/nsNavHistory.cpp --- firefox-3.6.3+nobinonly/mozilla/toolkit/components/places/src/nsNavHistory.cpp 2010-04-02 16:59:09.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/components/places/src/nsNavHistory.cpp 2010-04-16 17:32:42.000000000 +0100 @@ -2054,7 +2054,7 @@ // check the main DB mozStorageStatementScoper scoper(mDBIsPageVisited); - nsresult rv = mDBIsPageVisited->BindUTF8StringParameter(0, aURIString); + nsresult rv = BindStatementURLCString(mDBIsPageVisited, 0, aURIString); NS_ENSURE_SUCCESS(rv, PR_FALSE); PRBool hasMore = PR_FALSE; @@ -2514,7 +2514,7 @@ getter_AddRefs(dbUpdateStatement)); NS_ENSURE_SUCCESS(rv, rv); - rv = dbUpdateStatement->BindUTF8StringParameter(0, NS_LITERAL_CSTRING(LMANNO_FEEDURI)); + rv = BindStatementURLCString(dbUpdateStatement, 0, NS_LITERAL_CSTRING(LMANNO_FEEDURI)); NS_ENSURE_SUCCESS(rv, rv); rv = dbUpdateStatement->Execute(); @@ -6264,8 +6264,7 @@ nsCAutoString uriString; aQuery->Uri()->GetSpec(uriString); uriString.Append(char(0x7F)); // MAX_UTF8 - rv = statement->BindUTF8StringParameter(index.For("uri_upper"), - StringHead(uriString, HISTORY_URI_LENGTH_MAX)); + rv = BindStatementURLCString(statement, index.For("uri_upper"), uriString); NS_ENSURE_SUCCESS(rv, rv); } } @@ -6501,7 +6500,8 @@ // Convert title and url for the current node to UTF16 strings. NS_ConvertUTF8toUTF16 nodeTitle(aSet[nodeIndex]->mTitle); // Unescape the URL for search terms matching. - NS_ConvertUTF8toUTF16 nodeURL(NS_UnescapeURL(aSet[nodeIndex]->mURI)); + nsCAutoString cNodeURL(aSet[nodeIndex]->mURI); + NS_ConvertUTF8toUTF16 nodeURL(NS_UnescapeURL(cNodeURL)); // Determine if every search term matches anywhere in the title, url or // tag. @@ -7259,7 +7259,7 @@ // update historyvisits so they are remapped to the retained uri rv = updateStatement->BindInt64Parameter(0, id); NS_ENSURE_SUCCESS(rv, rv); - rv = updateStatement->BindUTF8StringParameter(1, url); + rv = BindStatementURLCString(updateStatement, 1, url); NS_ENSURE_SUCCESS(rv, rv); rv = updateStatement->Execute(); NS_ENSURE_SUCCESS(rv, rv); @@ -7267,7 +7267,7 @@ // remap bookmarks to the retained id rv = bookmarkStatement->BindInt64Parameter(0, id); NS_ENSURE_SUCCESS(rv, rv); - rv = bookmarkStatement->BindUTF8StringParameter(1, url); + rv = BindStatementURLCString(bookmarkStatement, 1, url); NS_ENSURE_SUCCESS(rv, rv); rv = bookmarkStatement->Execute(); NS_ENSURE_SUCCESS(rv, rv); @@ -7275,13 +7275,13 @@ // remap annotations to the retained id rv = annoStatement->BindInt64Parameter(0, id); NS_ENSURE_SUCCESS(rv, rv); - rv = annoStatement->BindUTF8StringParameter(1, url); + rv = BindStatementURLCString(annoStatement, 1, url); NS_ENSURE_SUCCESS(rv, rv); rv = annoStatement->Execute(); NS_ENSURE_SUCCESS(rv, rv); // remove duplicate uris from moz_places - rv = deleteStatement->BindUTF8StringParameter(0, url); + rv = BindStatementURLCString(deleteStatement, 0, url); NS_ENSURE_SUCCESS(rv, rv); rv = deleteStatement->BindInt64Parameter(1, id); NS_ENSURE_SUCCESS(rv, rv); @@ -7483,11 +7483,6 @@ } -// BindStatementURI -// -// Binds the specified URI as the parameter 'index' for the statment. -// URIs are always bound as UTF8 - nsresult BindStatementURI(mozIStorageStatement* statement, PRInt32 index, nsIURI* aURI) { @@ -7498,12 +7493,26 @@ nsresult rv = aURI->GetSpec(utf8URISpec); NS_ENSURE_SUCCESS(rv, rv); - rv = statement->BindUTF8StringParameter(index, - StringHead(utf8URISpec, HISTORY_URI_LENGTH_MAX)); + rv = BindStatementURLCString(statement, index, utf8URISpec); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; +} + + +nsresult +BindStatementURLCString(mozIStorageStatement* statement, + PRInt32 index, + const nsACString& aURLString) +{ + NS_ASSERTION(statement, "Must have non-null statement"); + + nsresult rv = statement->BindUTF8StringParameter( + index, StringHead(aURLString, HISTORY_URI_LENGTH_MAX)); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } + nsresult nsNavHistory::UpdateFrecency(PRInt64 aPlaceId, PRBool aIsBookmarked) { diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/components/places/src/nsNavHistory.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/components/places/src/nsNavHistory.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/components/places/src/nsNavHistory.h 2010-04-02 16:59:09.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/components/places/src/nsNavHistory.h 2010-04-16 17:32:42.000000000 +0100 @@ -702,12 +702,17 @@ }; /** - * Shared between the places components, this function binds the given URI as - * UTF8 to the given parameter for the statement. + * These utils bind a specified URI (or URL) to a statement, at the specified + * index. + * @note URIs are always bound as UTF8. */ -nsresult BindStatementURI(mozIStorageStatement* statement, PRInt32 index, +nsresult BindStatementURI(mozIStorageStatement* statement, + PRInt32 index, nsIURI* aURI); - +nsresult BindStatementURLCString(mozIStorageStatement* statement, + PRInt32 index, + const nsACString& aURLString); + #define PLACES_URI_PREFIX "place:" /* Returns true if the given URI represents a history query. */ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/components/places/tests/unit/test_536081.js firefox-3.6.4+build1+nobinonly/mozilla/toolkit/components/places/tests/unit/test_536081.js --- firefox-3.6.3+nobinonly/mozilla/toolkit/components/places/tests/unit/test_536081.js 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/components/places/tests/unit/test_536081.js 2010-04-16 17:32:43.000000000 +0100 @@ -0,0 +1,80 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Places unit test code. + * + * The Initial Developer of the Original Code is the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Marco Bonardo (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let hs = Cc["@mozilla.org/browser/nav-history-service;1"]. + getService(Ci.nsINavHistoryService); +let bh = hs.QueryInterface(Ci.nsIBrowserHistory); +let db = hs.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection; + +const URLS = [ + { u: "http://www.google.com/search?q=testing%3Bthis&ie=utf-8&oe=utf-8&aq=t&rls=org.mozilla:en-US:unofficial&client=firefox-a", + s: "goog" }, +]; + +function run_test() { + URLS.forEach(test_url); +} + +function test_url(aURL) { + print("Testing url: " + aURL.u); + hs.addVisit(uri(aURL.u), Date.now() * 1000, null, hs.TRANSITION_TYPED, false, 0); + let query = hs.getNewQuery(); + query.searchTerms = aURL.s; + let options = hs.getNewQueryOptions(); + let root = hs.executeQuery(query, options).root; + root.containerOpen = true; + let cc = root.childCount; + do_check_eq(cc, 1); + print("Checking url is in the query."); + let node = root.getChild(0); + print("Found " + node.uri); + root.containerOpen = false; + bh.removePage(uri(node.uri)); +} + +function check_empty_table(table_name) { + print("Checking url has been removed."); + let stmt = db.createStatement("SELECT count(*) FROM " + table_name); + try { + stmt.executeStep(); + do_check_eq(stmt.getInt32(0), 0); + } + finally { + stmt.finalize(); + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/content/license.html firefox-3.6.4+build1+nobinonly/mozilla/toolkit/content/license.html --- firefox-3.6.3+nobinonly/mozilla/toolkit/content/license.html 2010-04-02 16:59:11.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/content/license.html 2010-04-16 17:32:46.000000000 +0100 @@ -164,15 +164,18 @@
  • Breakpad License
  • bspatch License
  • Cairo Component Licenses
  • +
  • Chromium License
  • CSIRO Australia License
  • Dutch Spellchecking Dictionary License
  • Expat License
  • +
  • gfxFontList License
  • Google Gears License
  • Google Gears/iStumbler License
  • Growl License
  • Japan Network Information Center License
  • Java Embedding Plugin License
  • jemalloc License
  • +
  • libevent License
  • libffi License
  • Lithuanian Spellchecking Dictionary License
  • OpenVision License
  • @@ -2084,6 +2087,44 @@
    +
    + +

    Chromium License

    + +

    This license applies to some files in the directory + ipc/chromium/. +

    + +
    +Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
    +
    +Redistribution and use in source and binary forms, with or without
    +modification, are permitted provided that the following conditions are
    +met:
    +
    +   * Redistributions of source code must retain the above copyright
    +notice, this list of conditions and the following disclaimer.
    +   * Redistributions in binary form must reproduce the above
    +copyright notice, this list of conditions and the following disclaimer
    +in the documentation and/or other materials provided with the
    +distribution.
    +   * Neither the name of Google Inc. nor the names of its
    +contributors may be used to endorse or promote products derived from
    +this software without specific prior written permission.
    +
    +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    +
    +
    @@ -2209,6 +2250,45 @@
    +

    gfxFontList License

    + +

    This license applies to the files + gfx/thebes/src/gfxMacPlatformFontList.mm and + gfx/thebes/src/gfxPlatformFontList.cpp. +

    + +
    +Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
    +
    +Redistribution and use in source and binary forms, with or without
    +modification, are permitted provided that the following conditions
    +are met:
    +
    +1.  Redistributions of source code must retain the above copyright
    +    notice, this list of conditions and the following disclaimer.
    +2.  Redistributions in binary form must reproduce the above copyright
    +    notice, this list of conditions and the following disclaimer in the
    +    documentation and/or other materials provided with the distribution.
    +3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
    +    its contributors may be used to endorse or promote products derived
    +    from this software without specific prior written permission.
    +
    +THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
    +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    +DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
    +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    +
    + + + +
    +

    Google Gears

    This license applies to the file @@ -2472,6 +2552,42 @@ +


    + + +

    libevent License

    + +

    This license applies to files in the directory + ipc/chromium/src/third_party/libevent/. +

    + +
    +Copyright 2000-2002 Niels Provos <provos@citi.umich.edu>
    +All rights reserved.
    +
    +Redistribution and use in source and binary forms, with or without
    +modification, are permitted provided that the following conditions
    +are met:
    +1. Redistributions of source code must retain the above copyright
    +   notice, this list of conditions and the following disclaimer.
    +2. Redistributions in binary form must reproduce the above copyright
    +   notice, this list of conditions and the following disclaimer in the
    +   documentation and/or other materials provided with the distribution.
    +3. The name of the author may not be used to endorse or promote products
    +   derived from this software without specific prior written permission.
    +
    +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
    +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
    +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
    +IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
    +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
    +NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    +
    +
    diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/content/widgets/notification.xml firefox-3.6.4+build1+nobinonly/mozilla/toolkit/content/widgets/notification.xml --- firefox-3.6.3+nobinonly/mozilla/toolkit/content/widgets/notification.xml 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/content/widgets/notification.xml 2010-04-16 17:32:46.000000000 +0100 @@ -141,10 +141,11 @@ + + @@ -204,6 +206,7 @@ + . + * + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Benjamin Smedberg + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "crashreporter.h" + +#include +#include +#include +#include + +using namespace CrashReporter; +using std::string; +using std::vector; +using std::sort; + +struct FileData +{ + time_t timestamp; + string path; +}; + +static bool CompareFDTime(const FileData& fd1, const FileData& fd2) +{ + return fd1.timestamp > fd2.timestamp; +} + +void UIPruneSavedDumps(const std::string& directory) +{ + DIR *dirfd = opendir(directory.c_str()); + if (!dirfd) + return; + + vector dumpfiles; + + while (dirent *dir = readdir(dirfd)) { + FileData fd; + fd.path = directory + '/' + dir->d_name; + if (fd.path.size() < 5) + continue; + + if (fd.path.compare(fd.path.size() - 4, 4, ".dmp") != 0) + continue; + + struct stat st; + if (stat(fd.path.c_str(), &st)) { + closedir(dirfd); + return; + } + + fd.timestamp = st.st_mtime; + + dumpfiles.push_back(fd); + } + + sort(dumpfiles.begin(), dumpfiles.end(), CompareFDTime); + + while (dumpfiles.size() > kSaveCount) { + // get the path of the oldest file + string path = dumpfiles[dumpfiles.size() - 1].path; + UIDeleteFile(path.c_str()); + + // s/.dmp/.extra/ + path.replace(path.size() - 4, 4, ".extra"); + UIDeleteFile(path.c_str()); + + dumpfiles.pop_back(); + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/client/crashreporter_win.cpp firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/client/crashreporter_win.cpp --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/client/crashreporter_win.cpp 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/client/crashreporter_win.cpp 2010-04-16 17:32:46.000000000 +0100 @@ -51,6 +51,7 @@ #include #include #include +#include #include "resource.h" #include "client/windows/sender/crash_report_sender.h" #include "common/windows/string_utils-inl.h" @@ -180,6 +181,10 @@ const wchar_t* valueName, bool* enabled) { + /* + * NOTE! This code needs to stay in sync with the preference checking + * code in in nsExceptionHandler.cpp. + */ *enabled = false; bool found = false; HKEY hRegKey; @@ -207,6 +212,10 @@ static void SetBoolKey(const wchar_t* key, const wchar_t* value, bool enabled) { + /* + * NOTE! This code needs to stay in sync with the preference setting + * code in in nsExceptionHandler.cpp. + */ HKEY hRegKey; // remove the old value from the registry if it exists @@ -1468,3 +1477,46 @@ return file; } + +struct FileData +{ + FILETIME timestamp; + wstring path; +}; + +static bool CompareFDTime(const FileData& fd1, const FileData& fd2) +{ + return CompareFileTime(&fd1.timestamp, &fd2.timestamp) > 0; +} + +void UIPruneSavedDumps(const std::string& directory) +{ + wstring wdirectory = UTF8ToWide(directory); + + WIN32_FIND_DATA fdata; + wstring findpath = wdirectory + L"\\*.dmp"; + HANDLE dirlist = FindFirstFile(findpath.c_str(), &fdata); + if (dirlist == INVALID_HANDLE_VALUE) + return; + + vector dumpfiles; + + for (BOOL ok = true; ok; ok = FindNextFile(dirlist, &fdata)) { + FileData fd = {fdata.ftLastWriteTime, wdirectory + L"\\" + fdata.cFileName}; + dumpfiles.push_back(fd); + } + + sort(dumpfiles.begin(), dumpfiles.end(), CompareFDTime); + + while (dumpfiles.size() > kSaveCount) { + // get the path of the oldest file + wstring path = (--dumpfiles.end())->path; + DeleteFile(path.c_str()); + + // s/.dmp/.extra/ + path.replace(path.size() - 4, 4, L".extra"); + DeleteFile(path.c_str()); + + dumpfiles.pop_back(); + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/client/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/client/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/client/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/client/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in 2010-04-16 17:32:46.000000000 +0100 @@ -2,4 +2,3 @@ CFBundleName = "Crash Reporter"; CFBundleDisplayName = "Crash Reporter"; -NSHumanReadableCopyright = "Copyright © 2007-2010 Mozilla Foundation"; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/client/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/client/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/client/Makefile.in 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/client/Makefile.in 2010-04-16 17:32:46.000000000 +0100 @@ -46,8 +46,6 @@ PROGRAM = crashreporter$(BIN_SUFFIX) DIST_PROGRAM = crashreporter$(BIN_SUFFIX) -REQUIRES = \ - $(NULL) LOCAL_INCLUDES = -I$(srcdir)/../google-breakpad/src @@ -67,6 +65,7 @@ endif ifeq ($(OS_ARCH),Darwin) +CPPSRCS += crashreporter_unix.cpp CMMSRCS += crashreporter_osx.mm OS_LIBS += -framework Cocoa LIBS += \ @@ -78,26 +77,24 @@ endif ifeq ($(OS_ARCH),Linux) -CPPSRCS += crashreporter_linux.cpp +CPPSRCS += crashreporter_linux.cpp crashreporter_unix.cpp LIBS += \ $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/linux/$(LIB_PREFIX)breakpad_linux_common_s.$(LIB_SUFFIX) \ $(NULL) LOCAL_INCLUDES += -I$(srcdir) OS_CXXFLAGS += $(MOZ_GTK2_CFLAGS) $(MOZ_GTHREAD_CFLAGS) OS_LIBS += $(MOZ_GTK2_LIBS) $(MOZ_GTHREAD_LIBS) -CPPSRCS += http_upload.cc FORCE_USE_PIC=1 endif ifeq ($(OS_ARCH),SunOS) -CPPSRCS += crashreporter_linux.cpp +CPPSRCS += crashreporter_linux.cpp crashreporter_unix.cpp LIBS += \ $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/solaris/$(LIB_PREFIX)breakpad_solaris_common_s.$(LIB_SUFFIX) \ $(NULL) LOCAL_INCLUDES += -I$(srcdir) OS_CXXFLAGS += $(MOZ_GTK2_CFLAGS) $(MOZ_GTHREAD_CFLAGS) OS_LIBS += $(MOZ_GTK2_LIBS) $(MOZ_GTHREAD_LIBS) -CPPSRCS += http_upload.cc FORCE_USE_PIC=1 endif @@ -115,9 +112,6 @@ endif ifeq (,$(filter-out Linux SunOS,$(OS_ARCH))) -export:: $(srcdir)/../google-breakpad/src/common/linux/http_upload.cc - $(INSTALL) $^ . - libs:: $(topsrcdir)/toolkit/themes/winstripe/global/throbber/Throbber-small.gif $(INSTALL) $^ $(DIST)/bin endif diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/content/crashes.js firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/content/crashes.js --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/content/crashes.js 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/content/crashes.js 2010-04-16 17:32:46.000000000 +0100 @@ -1,254 +1,82 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dave Townsend (original author) + * Ted Mielczarek + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + const Cc = Components.classes; const Ci = Components.interfaces; -var reportURL = null; var reportsDir, pendingDir; -var strings = null; -var myListener = null; -function parseKeyValuePairs(text) { - var lines = text.split('\n'); - var data = {}; - for (let i = 0; i < lines.length; i++) { - if (lines[i] == '') - continue; - - // can't just .split() because the value might contain = characters - let eq = lines[i].indexOf('='); - if (eq != -1) { - let [key, value] = [lines[i].substring(0, eq), - lines[i].substring(eq + 1)]; - if (key && value) - data[key] = value.replace("\\n", "\n", "g").replace("\\\\", "\\", "g"); - } - } - return data; -} +Components.utils.import("resource://gre/modules/CrashSubmit.jsm"); -function parseKeyValuePairsFromFile(file) { - var fstream = Cc["@mozilla.org/network/file-input-stream;1"]. - createInstance(Ci.nsIFileInputStream); - fstream.init(file, -1, 0, 0); - var is = Cc["@mozilla.org/intl/converter-input-stream;1"]. - createInstance(Ci.nsIConverterInputStream); - is.init(fstream, "UTF-8", 1024, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); - var str = {}; - var contents = ''; - while (is.readString(4096, str) != 0) { - contents += str.value; - } - is.close(); - fstream.close(); - return parseKeyValuePairs(contents); -} - -function parseINIStrings(file) { - var factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"]. - getService(Ci.nsIINIParserFactory); - var parser = factory.createINIParser(file); - var obj = {}; - var en = parser.getKeys("Strings"); - while (en.hasMore()) { - var key = en.getNext(); - obj[key] = parser.getString("Strings", key); - } - return obj; -} - -// Since we're basically re-implementing part of the crashreporter -// client here, we'll just steal the strings we need from crashreporter.ini -function getL10nStrings() { - let dirSvc = Cc["@mozilla.org/file/directory_service;1"]. - getService(Ci.nsIProperties); - let path = dirSvc.get("GreD", Ci.nsIFile); - path.append("crashreporter.ini"); - if (!path.exists()) { - // see if we're on a mac - path = path.parent; - path.append("crashreporter.app"); - path.append("Contents"); - path.append("MacOS"); - path.append("crashreporter.ini"); - if (!path.exists()) { - // very bad, but I don't know how to recover - return; +function submitSuccess(dumpid, ret) { + let link = document.getElementById(dumpid); + if (link) { + link.className = ""; + // reset the link to point at our new crash report. this way, if the + // user clicks "Back", the link will be correct. + let CrashID = ret.CrashID; + link.firstChild.textContent = CrashID; + link.setAttribute("id", CrashID); + link.removeEventListener("click", submitPendingReport, true); + + if (reportURL) { + link.setAttribute("href", reportURL + CrashID); + // redirect the user to their brand new crash report + window.location.href = reportURL + CrashID; } } - let crstrings = parseINIStrings(path); - strings = { - 'crashid': crstrings.CrashID, - 'reporturl': crstrings.CrashDetailsURL - }; - - path = dirSvc.get("XCurProcD", Ci.nsIFile); - path.append("crashreporter-override.ini"); - if (path.exists()) { - crstrings = parseINIStrings(path); - if ('CrashID' in crstrings) - strings['crashid'] = crstrings.CrashID; - if ('CrashDetailsURL' in crstrings) - strings['reporturl'] = crstrings.CrashDetailsURL; - } -} - -function getPendingMinidump(id) { - let directoryService = Cc["@mozilla.org/file/directory_service;1"]. - getService(Ci.nsIProperties); - let dump = pendingDir.clone(); - let extra = pendingDir.clone(); - dump.append(id + ".dmp"); - extra.append(id + ".extra"); - return [dump, extra]; -} - -function addFormEntry(doc, form, name, value) { - var input = doc.createElement("input"); - input.type = "hidden"; - input.name = name; - input.value = value; - form.appendChild(input); -} - -function writeSubmittedReport(crashID, viewURL) { - let reportFile = reportsDir.clone(); - reportFile.append(crashID + ".txt"); - var fstream = Cc["@mozilla.org/network/file-output-stream;1"]. - createInstance(Ci.nsIFileOutputStream); - // open, write, truncate - fstream.init(reportFile, -1, -1, 0); - var os = Cc["@mozilla.org/intl/converter-output-stream;1"]. - createInstance(Ci.nsIConverterOutputStream); - os.init(fstream, "UTF-8", 0, 0x0000); - - var data = strings.crashid.replace("%s", crashID); - if (viewURL) - data += "\n" + strings.reporturl.replace("%s", viewURL); - - os.writeString(data); - os.close(); - fstream.close(); -} - -function submitSuccess(ret, link, dump, extra) { - if (!ret.CrashID) - return; - // Write out the details file to submitted/ - writeSubmittedReport(ret.CrashID, ret.ViewURL); - - // Delete from pending dir - try { - dump.remove(false); - extra.remove(false); - } - catch (ex) { - // report an error? not much the user can do here. - } - - // reset the link to point at our new crash report. this way, if the - // user clicks "Back", the link will be correct. - let CrashID = ret.CrashID; - link.firstChild.textContent = CrashID; - link.setAttribute("id", CrashID); - link.removeEventListener("click", submitPendingReport, true); - - if (reportURL) { - link.setAttribute("href", reportURL + CrashID); - // redirect the user to their brand new crash report - window.location.href = reportURL + CrashID; - } -} - -function submitForm(iframe, dump, extra, link) -{ - let reportData = parseKeyValuePairsFromFile(extra); - let form = iframe.contentDocument.forms[0]; - if ('ServerURL' in reportData) { - form.action = reportData.ServerURL; - delete reportData.ServerURL; - } - else { - return false; - } - // add the other data - for (let [name, value] in Iterator(reportData)) { - addFormEntry(iframe.contentDocument, form, name, value); - } - // tell the server not to throttle this, since it was manually submitted - addFormEntry(iframe.contentDocument, form, "Throttleable", "0"); - // add the minidump - iframe.contentDocument.getElementById('minidump').value = dump.path; - - // web progress listener - const STATE_START = Ci.nsIWebProgressListener.STATE_START; - const STATE_STOP = Ci.nsIWebProgressListener.STATE_STOP; - myListener = { - QueryInterface: function(aIID) { - if (aIID.equals(Ci.nsIWebProgressListener) || - aIID.equals(Ci.nsISupportsWeakReference) || - aIID.equals(Ci.nsISupports)) - return this; - throw Components.results.NS_NOINTERFACE; - }, - - onStateChange: function(aWebProgress, aRequest, aFlag, aStatus) { - if(aFlag & STATE_STOP) { - iframe.docShell.removeProgressListener(myListener); - myListener = null; - link.className = ""; - - //XXX: give some indication of failure? - // check general request status first - if (!Components.isSuccessCode(aStatus)) { - document.body.removeChild(iframe); - return 0; - } - // check HTTP status - if (aRequest instanceof Ci.nsIHttpChannel && - aRequest.responseStatus != 200) { - document.body.removeChild(iframe); - return 0; - } - - var ret = parseKeyValuePairs(iframe.contentDocument.documentElement.textContent); - document.body.removeChild(iframe); - submitSuccess(ret, link, dump, extra); - } - return 0; - }, - - onLocationChange: function(aProgress, aRequest, aURI) {return 0;}, - onProgressChange: function() {return 0;}, - onStatusChange: function() {return 0;}, - onSecurityChange: function() {return 0;}, - onLinkIconAvailable: function() {return 0;} - }; - iframe.docShell.QueryInterface(Ci.nsIWebProgress); - iframe.docShell.addProgressListener(myListener, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT); - form.submit(); - return true; } -function createAndSubmitForm(id, link) { - let [dump, extra] = getPendingMinidump(id); - if (!dump.exists() || !extra.exists()) - return false; - let iframe = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "iframe"); - iframe.setAttribute("type", "content"); - iframe.onload = function() { - if (iframe.contentWindow.location == "about:blank") - return; - iframe.onload = null; - submitForm(iframe, dump, extra, link); - }; - document.body.appendChild(iframe); - iframe.webNavigation.loadURI("chrome://global/content/crash-submit-form.xhtml", 0, null, null, null); - return true; +function submitError(dumpid) { + //XXX: do something more useful here + let link = document.getElementById(dumpid); + if (link) + link.className = ""; + // dispatch an event, useful for testing + let event = document.createEvent("Events"); + event.initEvent("CrashSubmitFailed", true, false); + document.dispatchEvent(event); } function submitPendingReport(event) { var link = event.target; var id = link.firstChild.textContent; - if (createAndSubmitForm(id, link)) + if (CrashSubmit.submit(id, document.body, submitSuccess, submitError)) link.className = "submitting"; event.preventDefault(); return false; @@ -417,6 +245,5 @@ } function init() { - getL10nStrings(); populateReportList(); } diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/CrashSubmit.jsm firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/CrashSubmit.jsm --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/CrashSubmit.jsm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/CrashSubmit.jsm 2010-04-16 17:32:46.000000000 +0100 @@ -0,0 +1,404 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ted Mielczarek (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +let EXPORTED_SYMBOLS = [ + "CrashSubmit" +]; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const STATE_START = Ci.nsIWebProgressListener.STATE_START; +const STATE_STOP = Ci.nsIWebProgressListener.STATE_STOP; + +const SUCCESS = "success"; +const FAILED = "failed"; +const SUBMITTING = "submitting"; + +let reportURL = null; +let strings = null; +let myListener = null; + +function parseKeyValuePairs(text) { + var lines = text.split('\n'); + var data = {}; + for (let i = 0; i < lines.length; i++) { + if (lines[i] == '') + continue; + + // can't just .split() because the value might contain = characters + let eq = lines[i].indexOf('='); + if (eq != -1) { + let [key, value] = [lines[i].substring(0, eq), + lines[i].substring(eq + 1)]; + if (key && value) + data[key] = value.replace("\\n", "\n", "g").replace("\\\\", "\\", "g"); + } + } + return data; +} + +function parseKeyValuePairsFromFile(file) { + var fstream = Cc["@mozilla.org/network/file-input-stream;1"]. + createInstance(Ci.nsIFileInputStream); + fstream.init(file, -1, 0, 0); + var is = Cc["@mozilla.org/intl/converter-input-stream;1"]. + createInstance(Ci.nsIConverterInputStream); + is.init(fstream, "UTF-8", 1024, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); + var str = {}; + var contents = ''; + while (is.readString(4096, str) != 0) { + contents += str.value; + } + is.close(); + fstream.close(); + return parseKeyValuePairs(contents); +} + +function parseINIStrings(file) { + var factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"]. + getService(Ci.nsIINIParserFactory); + var parser = factory.createINIParser(file); + var obj = {}; + var en = parser.getKeys("Strings"); + while (en.hasMore()) { + var key = en.getNext(); + obj[key] = parser.getString("Strings", key); + } + return obj; +} + +// Since we're basically re-implementing part of the crashreporter +// client here, we'll just steal the strings we need from crashreporter.ini +function getL10nStrings() { + let dirSvc = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties); + let path = dirSvc.get("GreD", Ci.nsIFile); + path.append("crashreporter.ini"); + if (!path.exists()) { + // see if we're on a mac + path = path.parent; + path.append("crashreporter.app"); + path.append("Contents"); + path.append("MacOS"); + path.append("crashreporter.ini"); + if (!path.exists()) { + // very bad, but I don't know how to recover + return; + } + } + let crstrings = parseINIStrings(path); + strings = { + 'crashid': crstrings.CrashID, + 'reporturl': crstrings.CrashDetailsURL + }; + + path = dirSvc.get("XCurProcD", Ci.nsIFile); + path.append("crashreporter-override.ini"); + if (path.exists()) { + crstrings = parseINIStrings(path); + if ('CrashID' in crstrings) + strings['crashid'] = crstrings.CrashID; + if ('CrashDetailsURL' in crstrings) + strings['reporturl'] = crstrings.CrashDetailsURL; + } +} + +function getPendingMinidump(id) { + let directoryService = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties); + let pendingDir = directoryService.get("UAppData", Ci.nsIFile); + pendingDir.append("Crash Reports"); + pendingDir.append("pending"); + let dump = pendingDir.clone(); + let extra = pendingDir.clone(); + dump.append(id + ".dmp"); + extra.append(id + ".extra"); + return [dump, extra]; +} + +function addFormEntry(doc, form, name, value) { + var input = doc.createElement("input"); + input.type = "hidden"; + input.name = name; + input.value = value; + form.appendChild(input); +} + +function writeSubmittedReport(crashID, viewURL) { + let directoryService = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties); + let reportFile = directoryService.get("UAppData", Ci.nsIFile); + reportFile.append("Crash Reports"); + reportFile.append("submitted"); + if (!reportFile.exists()) + reportFile.create(Ci.nsIFile.DIRECTORY_TYPE, 0700); + reportFile.append(crashID + ".txt"); + var fstream = Cc["@mozilla.org/network/file-output-stream;1"]. + createInstance(Ci.nsIFileOutputStream); + // open, write, truncate + fstream.init(reportFile, -1, -1, 0); + var os = Cc["@mozilla.org/intl/converter-output-stream;1"]. + createInstance(Ci.nsIConverterOutputStream); + os.init(fstream, "UTF-8", 0, 0x0000); + + var data = strings.crashid.replace("%s", crashID); + if (viewURL) + data += "\n" + strings.reporturl.replace("%s", viewURL); + + os.writeString(data); + os.close(); + fstream.close(); +} + +// the Submitter class represents an individual submission. +function Submitter(id, element, submitSuccess, submitError) { + this.id = id; + this.element = element; + this.document = element.ownerDocument; + this.successCallback = submitSuccess; + this.errorCallback = submitError; +} + +Submitter.prototype = { + submitSuccess: function Submitter_submitSuccess(ret) + { + if (!ret.CrashID) { + this.notifyStatus(FAILED); + this.cleanup(); + return; + } + + // Write out the details file to submitted/ + writeSubmittedReport(ret.CrashID, ret.ViewURL); + + // Delete from pending dir + try { + this.dump.remove(false); + this.extra.remove(false); + } + catch (ex) { + // report an error? not much the user can do here. + } + + this.notifyStatus(SUCCESS, ret); + this.cleanup(); + }, + + cleanup: function Submitter_cleanup() { + // drop some references just to be nice + this.element = null; + this.document = null; + this.successCallback = null; + this.errorCallback = null; + this.iframe = null; + this.dump = null; + this.extra = null; + // remove this object from the list of active submissions + let idx = CrashSubmit._activeSubmissions.indexOf(this); + if (idx != -1) + CrashSubmit._activeSubmissions.splice(idx, 1); + }, + + submitForm: function Submitter_submitForm() + { + let reportData = parseKeyValuePairsFromFile(this.extra); + let form = this.iframe.contentDocument.forms[0]; + if ('ServerURL' in reportData) { + form.action = reportData.ServerURL; + delete reportData.ServerURL; + } + else { + return false; + } + // add the other data + for (let [name, value] in Iterator(reportData)) { + addFormEntry(this.iframe.contentDocument, form, name, value); + } + // tell the server not to throttle this, since it was manually submitted + addFormEntry(this.iframe.contentDocument, form, "Throttleable", "0"); + // add the minidump + this.iframe.contentDocument.getElementById('minidump').value + = this.dump.path; + this.iframe.docShell.QueryInterface(Ci.nsIWebProgress); + this.iframe.docShell.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_DOCUMENT); + form.submit(); + return true; + }, + + // web progress listener + QueryInterface: function(aIID) + { + if (aIID.equals(Ci.nsIWebProgressListener) || + aIID.equals(Ci.nsISupportsWeakReference) || + aIID.equals(Ci.nsISupports)) + return this; + throw Components.results.NS_NOINTERFACE; + }, + + onStateChange: function(aWebProgress, aRequest, aFlag, aStatus) + { + if(aFlag & STATE_STOP) { + this.iframe.docShell.QueryInterface(Ci.nsIWebProgress); + this.iframe.docShell.removeProgressListener(this); + + // check general request status first + if (!Components.isSuccessCode(aStatus)) { + this.element.removeChild(this.iframe); + this.notifyStatus(FAILED); + this.cleanup(); + return 0; + } + // check HTTP status + if (aRequest instanceof Ci.nsIHttpChannel && + aRequest.responseStatus != 200) { + this.element.removeChild(this.iframe); + this.notifyStatus(FAILED); + this.cleanup(); + return 0; + } + + var ret = parseKeyValuePairs(this.iframe.contentDocument.documentElement.textContent); + this.element.removeChild(this.iframe); + this.submitSuccess(ret); + } + return 0; + }, + + onLocationChange: function(aProgress, aRequest, aURI) {return 0;}, + onProgressChange: function() {return 0;}, + onStatusChange: function() {return 0;}, + onSecurityChange: function() {return 0;}, + + notifyStatus: function Submitter_notify(status, ret) + { + let propBag = Cc["@mozilla.org/hash-property-bag;1"]. + createInstance(Ci.nsIWritablePropertyBag2); + propBag.setPropertyAsAString("minidumpID", this.id); + + Cc["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService) + .notifyObservers(propBag, "crash-report-status", status); + + switch (status) { + case SUCCESS: + if (this.successCallback) + this.successCallback(this.id, ret); + break; + case FAILED: + if (this.errorCallback) + this.errorCallback(this.id); + break; + default: + // no callbacks invoked. + } + }, + + submit: function Submitter_submit() + { + let [dump, extra] = getPendingMinidump(this.id); + if (!dump.exists() || !extra.exists()) { + this.notifyStatus(FAILED); + this.cleanup(); + return false; + } + + this.notifyStatus(SUBMITTING); + + this.dump = dump; + this.extra = extra; + let iframe = this.document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", "iframe"); + iframe.setAttribute("type", "content"); + iframe.style.width = 0; + iframe.style.minWidth = 0; + + let self = this; + function loadHandler() { + if (iframe.contentWindow.location == "about:blank") + return; + iframe.removeEventListener("load", loadHandler, true); + if (!self.submitForm()) { + this.notifyStatus(FAILED); + self.cleanup(); + } + } + + iframe.addEventListener("load", loadHandler, true); + this.element.appendChild(iframe); + this.iframe = iframe; + iframe.webNavigation.loadURI("chrome://global/content/crash-submit-form.xhtml", 0, null, null, null); + return true; + } +}; + +//=================================== +// External API goes here +let CrashSubmit = { + /** + * Submit the crash report named id.dmp from the "pending" directory. + * + * @param id + * Filename (minus .dmp extension) of the minidump to submit. + * @param element + * A DOM element to which an iframe can be appended as a child, + * used for form submission. + * @param submitSuccess + * A function that will be called if the report is submitted + * successfully with two parameters: the id that was passed + * to this function, and an object containing the key/value + * data returned from the server in its properties. + * @param submitError + * A function that will be called with one parameter if the + * report fails to submit: the id that was passed to this + * function. + * + * @return true if the submission began successfully, or false if + * it failed for some reason. (If the dump file does not + * exist, for example.) + */ + submit: function CrashSubmit_submit(id, element, submitSuccess, submitError) + { + let submitter = new Submitter(id, element, submitSuccess, submitError); + CrashSubmit._activeSubmissions.push(submitter); + return submitter.submit(); + }, + + // List of currently active submit objects + _activeSubmissions: [] +}; + +// Run this when first loaded +getL10nStrings(); diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/aclocal.m4 firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/aclocal.m4 --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/aclocal.m4 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/aclocal.m4 2010-04-16 17:32:47.000000000 +0100 @@ -1,7 +1,7 @@ -# generated automatically by aclocal 1.10 -*- Autoconf -*- +# generated automatically by aclocal 1.11.1 -*- Autoconf -*- # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, -# 2005, 2006 Free Software Foundation, Inc. +# 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. @@ -11,108 +11,196 @@ # even the implied warranty of MERCHANTABILITY or FITNESS FOR A # PARTICULAR PURPOSE. -m4_if(m4_PACKAGE_VERSION, [2.61],, -[m4_fatal([this file was generated for autoconf 2.61. -You have another version of autoconf. If you want to use that, -you should regenerate the build system entirely.], [63])]) +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.65],, +[m4_warning([this file was generated for autoconf 2.65. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically `autoreconf'.])]) # libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008 Free Software Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +m4_define([_LT_COPYING], [dnl +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008 Free Software Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# GNU Libtool is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +]) -# serial 48 AC_PROG_LIBTOOL +# serial 56 LT_INIT -# AC_PROVIDE_IFELSE(MACRO-NAME, IF-PROVIDED, IF-NOT-PROVIDED) -# ----------------------------------------------------------- -# If this macro is not defined by Autoconf, define it here. -m4_ifdef([AC_PROVIDE_IFELSE], - [], - [m4_define([AC_PROVIDE_IFELSE], - [m4_ifdef([AC_PROVIDE_$1], - [$2], [$3])])]) +# LT_PREREQ(VERSION) +# ------------------ +# Complain and exit if this libtool version is less that VERSION. +m4_defun([LT_PREREQ], +[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, + [m4_default([$3], + [m4_fatal([Libtool version $1 or higher is required], + 63)])], + [$2])]) -# AC_PROG_LIBTOOL -# --------------- -AC_DEFUN([AC_PROG_LIBTOOL], -[AC_REQUIRE([_AC_PROG_LIBTOOL])dnl -dnl If AC_PROG_CXX has already been expanded, run AC_LIBTOOL_CXX -dnl immediately, otherwise, hook it in at the end of AC_PROG_CXX. - AC_PROVIDE_IFELSE([AC_PROG_CXX], - [AC_LIBTOOL_CXX], - [define([AC_PROG_CXX], defn([AC_PROG_CXX])[AC_LIBTOOL_CXX - ])]) -dnl And a similar setup for Fortran 77 support - AC_PROVIDE_IFELSE([AC_PROG_F77], - [AC_LIBTOOL_F77], - [define([AC_PROG_F77], defn([AC_PROG_F77])[AC_LIBTOOL_F77 -])]) - -dnl Quote A][M_PROG_GCJ so that aclocal doesn't bring it in needlessly. -dnl If either AC_PROG_GCJ or A][M_PROG_GCJ have already been expanded, run -dnl AC_LIBTOOL_GCJ immediately, otherwise, hook it in at the end of both. - AC_PROVIDE_IFELSE([AC_PROG_GCJ], - [AC_LIBTOOL_GCJ], - [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], - [AC_LIBTOOL_GCJ], - [AC_PROVIDE_IFELSE([LT_AC_PROG_GCJ], - [AC_LIBTOOL_GCJ], - [ifdef([AC_PROG_GCJ], - [define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[AC_LIBTOOL_GCJ])]) - ifdef([A][M_PROG_GCJ], - [define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[AC_LIBTOOL_GCJ])]) - ifdef([LT_AC_PROG_GCJ], - [define([LT_AC_PROG_GCJ], - defn([LT_AC_PROG_GCJ])[AC_LIBTOOL_GCJ])])])]) -])])# AC_PROG_LIBTOOL +# _LT_CHECK_BUILDDIR +# ------------------ +# Complain if the absolute build directory name contains unusual characters +m4_defun([_LT_CHECK_BUILDDIR], +[case `pwd` in + *\ * | *\ *) + AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; +esac +]) -# _AC_PROG_LIBTOOL -# ---------------- -AC_DEFUN([_AC_PROG_LIBTOOL], -[AC_REQUIRE([AC_LIBTOOL_SETUP])dnl -AC_BEFORE([$0],[AC_LIBTOOL_CXX])dnl -AC_BEFORE([$0],[AC_LIBTOOL_F77])dnl -AC_BEFORE([$0],[AC_LIBTOOL_GCJ])dnl +# LT_INIT([OPTIONS]) +# ------------------ +AC_DEFUN([LT_INIT], +[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT +AC_BEFORE([$0], [LT_LANG])dnl +AC_BEFORE([$0], [LT_OUTPUT])dnl +AC_BEFORE([$0], [LTDL_INIT])dnl +m4_require([_LT_CHECK_BUILDDIR])dnl + +dnl Autoconf doesn't catch unexpanded LT_ macros by default: +m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl +m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl +dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 +dnl unless we require an AC_DEFUNed macro: +AC_REQUIRE([LTOPTIONS_VERSION])dnl +AC_REQUIRE([LTSUGAR_VERSION])dnl +AC_REQUIRE([LTVERSION_VERSION])dnl +AC_REQUIRE([LTOBSOLETE_VERSION])dnl +m4_require([_LT_PROG_LTMAIN])dnl + +dnl Parse OPTIONS +_LT_SET_OPTIONS([$0], [$1]) # This can be used to rebuild libtool when needed -LIBTOOL_DEPS="$ac_aux_dir/ltmain.sh" +LIBTOOL_DEPS="$ltmain" # Always use our own libtool. LIBTOOL='$(SHELL) $(top_builddir)/libtool' AC_SUBST(LIBTOOL)dnl -# Prevent multiple expansion -define([AC_PROG_LIBTOOL], []) -])# _AC_PROG_LIBTOOL +_LT_SETUP +# Only expand once: +m4_define([LT_INIT]) +])# LT_INIT + +# Old names: +AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) +AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PROG_LIBTOOL], []) +dnl AC_DEFUN([AM_PROG_LIBTOOL], []) -# AC_LIBTOOL_SETUP -# ---------------- -AC_DEFUN([AC_LIBTOOL_SETUP], -[AC_PREREQ(2.50)dnl -AC_REQUIRE([AC_ENABLE_SHARED])dnl -AC_REQUIRE([AC_ENABLE_STATIC])dnl -AC_REQUIRE([AC_ENABLE_FAST_INSTALL])dnl -AC_REQUIRE([AC_CANONICAL_HOST])dnl + +# _LT_CC_BASENAME(CC) +# ------------------- +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +m4_defun([_LT_CC_BASENAME], +[for cc_temp in $1""; do + case $cc_temp in + compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; + distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` +]) + + +# _LT_FILEUTILS_DEFAULTS +# ---------------------- +# It is okay to use these file commands and assume they have been set +# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'. +m4_defun([_LT_FILEUTILS_DEFAULTS], +[: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} +])# _LT_FILEUTILS_DEFAULTS + + +# _LT_SETUP +# --------- +m4_defun([_LT_SETUP], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl AC_REQUIRE([AC_CANONICAL_BUILD])dnl +_LT_DECL([], [host_alias], [0], [The host system])dnl +_LT_DECL([], [host], [0])dnl +_LT_DECL([], [host_os], [0])dnl +dnl +_LT_DECL([], [build_alias], [0], [The build system])dnl +_LT_DECL([], [build], [0])dnl +_LT_DECL([], [build_os], [0])dnl +dnl AC_REQUIRE([AC_PROG_CC])dnl -AC_REQUIRE([AC_PROG_LD])dnl -AC_REQUIRE([AC_PROG_LD_RELOAD_FLAG])dnl -AC_REQUIRE([AC_PROG_NM])dnl - +AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +dnl AC_REQUIRE([AC_PROG_LN_S])dnl -AC_REQUIRE([AC_DEPLIBS_CHECK_METHOD])dnl -# Autoconf 2.13's AC_OBJEXT and AC_EXEEXT macros only works for C compilers! -AC_REQUIRE([AC_OBJEXT])dnl -AC_REQUIRE([AC_EXEEXT])dnl +test -z "$LN_S" && LN_S="ln -s" +_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl dnl +AC_REQUIRE([LT_CMD_MAX_LEN])dnl +_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl +_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl +dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_CMD_RELOAD])dnl +m4_require([_LT_CHECK_MAGIC_METHOD])dnl +m4_require([_LT_CMD_OLD_ARCHIVE])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl + +_LT_CONFIG_LIBTOOL_INIT([ +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi +]) +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi -AC_LIBTOOL_SYS_MAX_CMD_LEN -AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE -AC_LIBTOOL_OBJDIR +_LT_CHECK_OBJDIR -AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl -_LT_AC_PROG_ECHO_BACKSLASH +m4_require([_LT_TAG_COMPILER])dnl +_LT_PROG_ECHO_BACKSLASH case $host_os in aix3*) @@ -128,6272 +216,7756 @@ # Sed substitution that helps us do robust quoting. It backslashifies # metacharacters that are still active within double-quoted strings. -Xsed='sed -e 1s/^X//' -[sed_quote_subst='s/\([\\"\\`$\\\\]\)/\\\1/g'] +sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' # Same as above, but do not quote variable references. -[double_quote_subst='s/\([\\"\\`\\\\]\)/\\\1/g'] +double_quote_subst='s/\([["`\\]]\)/\\\1/g' # Sed substitution to delay expansion of an escaped shell variable in a # double_quote_subst'ed string. delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + # Sed substitution to avoid accidental globbing in evaled expressions no_glob_subst='s/\*/\\\*/g' -# Constants: -rm="rm -f" - # Global variables: -default_ofile=libtool +ofile=libtool can_build_shared=yes # All known linkers require a `.a' archive for static linking (except MSVC, # which needs '.lib'). libext=a -ltmain="$ac_aux_dir/ltmain.sh" -ofile="$default_ofile" -with_gnu_ld="$lt_cv_prog_gnu_ld" -AC_CHECK_TOOL(AR, ar, false) -AC_CHECK_TOOL(RANLIB, ranlib, :) -AC_CHECK_TOOL(STRIP, strip, :) +with_gnu_ld="$lt_cv_prog_gnu_ld" old_CC="$CC" old_CFLAGS="$CFLAGS" # Set sane defaults for various variables -test -z "$AR" && AR=ar -test -z "$AR_FLAGS" && AR_FLAGS=cru -test -z "$AS" && AS=as test -z "$CC" && CC=cc test -z "$LTCC" && LTCC=$CC test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS -test -z "$DLLTOOL" && DLLTOOL=dlltool test -z "$LD" && LD=ld -test -z "$LN_S" && LN_S="ln -s" -test -z "$MAGIC_CMD" && MAGIC_CMD=file -test -z "$NM" && NM=nm -test -z "$SED" && SED=sed -test -z "$OBJDUMP" && OBJDUMP=objdump -test -z "$RANLIB" && RANLIB=: -test -z "$STRIP" && STRIP=: test -z "$ac_objext" && ac_objext=o -# Determine commands to create old-style static archives. -old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs' -old_postinstall_cmds='chmod 644 $oldlib' -old_postuninstall_cmds= - -if test -n "$RANLIB"; then - case $host_os in - openbsd*) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" - ;; - *) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" - ;; - esac - old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" -fi - _LT_CC_BASENAME([$compiler]) # Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file case $deplibs_check_method in file_magic*) if test "$file_magic_cmd" = '$MAGIC_CMD'; then - AC_PATH_MAGIC + _LT_PATH_MAGIC fi ;; esac -AC_PROVIDE_IFELSE([AC_LIBTOOL_DLOPEN], enable_dlopen=yes, enable_dlopen=no) -AC_PROVIDE_IFELSE([AC_LIBTOOL_WIN32_DLL], -enable_win32_dll=yes, enable_win32_dll=no) - -AC_ARG_ENABLE([libtool-lock], - [AC_HELP_STRING([--disable-libtool-lock], - [avoid locking (might break parallel builds)])]) -test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes +# Use C for the default configuration in the libtool script +LT_SUPPORTED_TAG([CC]) +_LT_LANG_C_CONFIG +_LT_LANG_DEFAULT_CONFIG +_LT_CONFIG_COMMANDS +])# _LT_SETUP -AC_ARG_WITH([pic], - [AC_HELP_STRING([--with-pic], - [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], - [pic_mode="$withval"], - [pic_mode=default]) -test -z "$pic_mode" && pic_mode=default -# Use C for the default configuration in the libtool script -tagname= -AC_LIBTOOL_LANG_C_CONFIG -_LT_AC_TAGCONFIG -])# AC_LIBTOOL_SETUP +# _LT_PROG_LTMAIN +# --------------- +# Note that this code is called both from `configure', and `config.status' +# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, +# `config.status' has no value for ac_aux_dir unless we are using Automake, +# so we pass a copy along to make sure it has a sensible value anyway. +m4_defun([_LT_PROG_LTMAIN], +[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl +_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) +ltmain="$ac_aux_dir/ltmain.sh" +])# _LT_PROG_LTMAIN -# _LT_AC_SYS_COMPILER -# ------------------- -AC_DEFUN([_LT_AC_SYS_COMPILER], -[AC_REQUIRE([AC_PROG_CC])dnl -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} +# So that we can recreate a full libtool script including additional +# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS +# in macros and then make a single call at the end using the `libtool' +# label. -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} -# Allow CC to be a program name with arguments. -compiler=$CC -])# _LT_AC_SYS_COMPILER +# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) +# ---------------------------------------- +# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL_INIT], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_INIT], + [$1 +])])]) +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_INIT]) -# _LT_CC_BASENAME(CC) -# ------------------- -# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. -AC_DEFUN([_LT_CC_BASENAME], -[for cc_temp in $1""; do - case $cc_temp in - compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; - distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` + +# _LT_CONFIG_LIBTOOL([COMMANDS]) +# ------------------------------ +# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) + + +# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) +# ----------------------------------------------------- +m4_defun([_LT_CONFIG_SAVE_COMMANDS], +[_LT_CONFIG_LIBTOOL([$1]) +_LT_CONFIG_LIBTOOL_INIT([$2]) ]) -# _LT_COMPILER_BOILERPLATE -# ------------------------ -# Check for compiler boilerplate output or warnings with -# the simple compiler test code. -AC_DEFUN([_LT_COMPILER_BOILERPLATE], -[ac_outfile=conftest.$ac_objext -printf "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$rm conftest* -])# _LT_COMPILER_BOILERPLATE +# _LT_FORMAT_COMMENT([COMMENT]) +# ----------------------------- +# Add leading comment marks to the start of each line, and a trailing +# full-stop to the whole comment if one is not present already. +m4_define([_LT_FORMAT_COMMENT], +[m4_ifval([$1], [ +m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], + [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) +)]) -# _LT_LINKER_BOILERPLATE -# ---------------------- -# Check for linker boilerplate output or warnings with -# the simple link test code. -AC_DEFUN([_LT_LINKER_BOILERPLATE], -[ac_outfile=conftest.$ac_objext -printf "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$rm conftest* -])# _LT_LINKER_BOILERPLATE -# _LT_AC_SYS_LIBPATH_AIX -# ---------------------- -# Links a minimal program and checks the executable -# for the system default hardcoded library path. In most cases, -# this is /usr/lib:/lib, but when the MPI compilers are used -# the location of the communication and MPI libs are included too. -# If we don't find anything, use the default library path according -# to the aix ld manual. -AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX], -[AC_LINK_IFELSE(AC_LANG_PROGRAM,[ -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi],[]) -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi -])# _LT_AC_SYS_LIBPATH_AIX +# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) +# ------------------------------------------------------------------- +# CONFIGNAME is the name given to the value in the libtool script. +# VARNAME is the (base) name used in the configure script. +# VALUE may be 0, 1 or 2 for a computed quote escaped value based on +# VARNAME. Any other value will be used directly. +m4_define([_LT_DECL], +[lt_if_append_uniq([lt_decl_varnames], [$2], [, ], + [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], + [m4_ifval([$1], [$1], [$2])]) + lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) + m4_ifval([$4], + [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) + lt_dict_add_subkey([lt_decl_dict], [$2], + [tagged?], [m4_ifval([$5], [yes], [no])])]) +]) -# _LT_AC_SHELL_INIT(ARG) -# ---------------------- -AC_DEFUN([_LT_AC_SHELL_INIT], -[ifdef([AC_DIVERSION_NOTICE], - [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)], - [AC_DIVERT_PUSH(NOTICE)]) -$1 -AC_DIVERT_POP -])# _LT_AC_SHELL_INIT +# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) +# -------------------------------------------------------- +m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) + + +# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_tag_varnames], +[_lt_decl_filter([tagged?], [yes], $@)]) + + +# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) +# --------------------------------------------------------- +m4_define([_lt_decl_filter], +[m4_case([$#], + [0], [m4_fatal([$0: too few arguments: $#])], + [1], [m4_fatal([$0: too few arguments: $#: $1])], + [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], + [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], + [lt_dict_filter([lt_decl_dict], $@)])[]dnl +]) -# _LT_AC_PROG_ECHO_BACKSLASH -# -------------------------- -# Add some code to the start of the generated configure script which -# will find an echo command which doesn't interpret backslashes. -AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH], -[_LT_AC_SHELL_INIT([ -# Check that we are running under the correct shell. -SHELL=${CONFIG_SHELL-/bin/sh} -case X$ECHO in -X*--fallback-echo) - # Remove one level of quotation (which was required for Make). - ECHO=`echo "$ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','` - ;; -esac +# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) +# -------------------------------------------------- +m4_define([lt_decl_quote_varnames], +[_lt_decl_filter([value], [1], $@)]) + + +# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_dquote_varnames], +[_lt_decl_filter([value], [2], $@)]) + + +# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_varnames_tagged], +[m4_assert([$# <= 2])dnl +_$0(m4_quote(m4_default([$1], [[, ]])), + m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), + m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) +m4_define([_lt_decl_varnames_tagged], +[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) + + +# lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_all_varnames], +[_$0(m4_quote(m4_default([$1], [[, ]])), + m4_if([$2], [], + m4_quote(lt_decl_varnames), + m4_quote(m4_shift($@))))[]dnl +]) +m4_define([_lt_decl_all_varnames], +[lt_join($@, lt_decl_varnames_tagged([$1], + lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl +]) -echo=${ECHO-echo} -if test "X[$]1" = X--no-reexec; then - # Discard the --no-reexec flag, and continue. - shift -elif test "X[$]1" = X--fallback-echo; then - # Avoid inline document here, it may be left over - : -elif test "X`($echo '\t') 2>/dev/null`" = 'X\t' ; then - # Yippee, $echo works! - : -else - # Restart under the correct shell. - exec $SHELL "[$]0" --no-reexec ${1+"[$]@"} -fi -if test "X[$]1" = X--fallback-echo; then - # used as fallback echo - shift - cat </dev/null 2>&1 && unset CDPATH -if test -z "$ECHO"; then -if test "X${echo_test_string+set}" != Xset; then -# find a string as large as possible, as long as the shell can cope with it - for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do - # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ... - if (echo_test_string=`eval $cmd`) 2>/dev/null && - echo_test_string=`eval $cmd` && - (test "X$echo_test_string" = "X$echo_test_string") 2>/dev/null - then - break - fi - done -fi +# _LT_CONFIG_STATUS_DECLARATIONS +# ------------------------------ +# We delimit libtool config variables with single quotes, so when +# we write them to config.status, we have to be sure to quote all +# embedded single quotes properly. In configure, this macro expands +# each variable declared with _LT_DECL (and _LT_TAGDECL) into: +# +# ='`$ECHO "X$" | $Xsed -e "$delay_single_quote_subst"`' +m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], +[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), + [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) -if test "X`($echo '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - : -else - # The Solaris, AIX, and Digital Unix default echo programs unquote - # backslashes. This makes it impossible to quote backslashes using - # echo "$something" | sed 's/\\/\\\\/g' - # - # So, first we look for a working echo in the user's PATH. - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for dir in $PATH /usr/ucb; do - IFS="$lt_save_ifs" - if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && - test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - echo="$dir/echo" - break - fi - done - IFS="$lt_save_ifs" +# _LT_LIBTOOL_TAGS +# ---------------- +# Output comment and list of tags supported by the script +m4_defun([_LT_LIBTOOL_TAGS], +[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl +available_tags="_LT_TAGS"dnl +]) - if test "X$echo" = Xecho; then - # We didn't find a better echo, so look for alternatives. - if test "X`(print -r '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`(print -r "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - # This shell has a builtin print -r that does the trick. - echo='print -r' - elif (test -f /bin/ksh || test -f /bin/ksh$ac_exeext) && - test "X$CONFIG_SHELL" != X/bin/ksh; then - # If we have ksh, try running configure again with it. - ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} - export ORIGINAL_CONFIG_SHELL - CONFIG_SHELL=/bin/ksh - export CONFIG_SHELL - exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"} - else - # Try using printf. - echo='printf %s\n' - if test "X`($echo '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - # Cool, printf works - : - elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && - test "X$echo_testing_string" = 'X\t' && - echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL - export CONFIG_SHELL - SHELL="$CONFIG_SHELL" - export SHELL - echo="$CONFIG_SHELL [$]0 --fallback-echo" - elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && - test "X$echo_testing_string" = 'X\t' && - echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - echo="$CONFIG_SHELL [$]0 --fallback-echo" - else - # maybe with a smaller string... - prev=: - for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do - if (test "X$echo_test_string" = "X`eval $cmd`") 2>/dev/null - then - break - fi - prev="$cmd" - done +# _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) +# ----------------------------------- +# Extract the dictionary values for VARNAME (optionally with TAG) and +# expand to a commented shell variable setting: +# +# # Some comment about what VAR is for. +# visible_name=$lt_internal_name +m4_define([_LT_LIBTOOL_DECLARE], +[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], + [description])))[]dnl +m4_pushdef([_libtool_name], + m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl +m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), + [0], [_libtool_name=[$]$1], + [1], [_libtool_name=$lt_[]$1], + [2], [_libtool_name=$lt_[]$1], + [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl +m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl +]) - if test "$prev" != 'sed 50q "[$]0"'; then - echo_test_string=`eval $prev` - export echo_test_string - exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"} - else - # Oops. We lost completely, so just stick with echo. - echo=echo - fi - fi - fi - fi -fi -fi -# Copy echo and quote the copy suitably for passing to libtool from -# the Makefile, instead of quoting the original, which is used later. -ECHO=$echo -if test "X$ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then - ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo" -fi +# _LT_LIBTOOL_CONFIG_VARS +# ----------------------- +# Produce commented declarations of non-tagged libtool config variables +# suitable for insertion in the LIBTOOL CONFIG section of the `libtool' +# script. Tagged libtool config variables (even for the LIBTOOL CONFIG +# section) are produced by _LT_LIBTOOL_TAG_VARS. +m4_defun([_LT_LIBTOOL_CONFIG_VARS], +[m4_foreach([_lt_var], + m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) -AC_SUBST(ECHO) -])])# _LT_AC_PROG_ECHO_BACKSLASH +# _LT_LIBTOOL_TAG_VARS(TAG) +# ------------------------- +m4_define([_LT_LIBTOOL_TAG_VARS], +[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) -# _LT_AC_LOCK -# ----------- -AC_DEFUN([_LT_AC_LOCK], -[AC_ARG_ENABLE([libtool-lock], - [AC_HELP_STRING([--disable-libtool-lock], - [avoid locking (might break parallel builds)])]) -test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes -# Some flags need to be propagated to the compiler or linker for good -# libtool support. -case $host in -ia64-*-hpux*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if AC_TRY_EVAL(ac_compile); then - case `/usr/bin/file conftest.$ac_objext` in - *ELF-32*) - HPUX_IA64_MODE="32" - ;; - *ELF-64*) - HPUX_IA64_MODE="64" - ;; - esac - fi - rm -rf conftest* - ;; -*-*-irix6*) - # Find out which ABI we are using. - echo '[#]line __oline__ "configure"' > conftest.$ac_ext - if AC_TRY_EVAL(ac_compile); then - if test "$lt_cv_prog_gnu_ld" = yes; then - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - LD="${LD-ld} -melf32bsmip" - ;; - *N32*) - LD="${LD-ld} -melf32bmipn32" +# _LT_TAGVAR(VARNAME, [TAGNAME]) +# ------------------------------ +m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) + + +# _LT_CONFIG_COMMANDS +# ------------------- +# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of +# variables for single and double quote escaping we saved from calls +# to _LT_DECL, we can put quote escaped variables declarations +# into `config.status', and then the shell code to quote escape them in +# for loops in `config.status'. Finally, any additional code accumulated +# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. +m4_defun([_LT_CONFIG_COMMANDS], +[AC_PROVIDE_IFELSE([LT_OUTPUT], + dnl If the libtool generation code has been placed in $CONFIG_LT, + dnl instead of duplicating it all over again into config.status, + dnl then we will have config.status run $CONFIG_LT later, so it + dnl needs to know what name is stored there: + [AC_CONFIG_COMMANDS([libtool], + [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], + dnl If the libtool generation code is destined for config.status, + dnl expand the accumulated commands and init code now: + [AC_CONFIG_COMMANDS([libtool], + [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) +])#_LT_CONFIG_COMMANDS + + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], +[ + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +_LT_CONFIG_STATUS_DECLARATIONS +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# Quote evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_quote_varnames); do + case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" ;; - *64-bit*) - LD="${LD-ld} -melf64bmip" + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac - else - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - LD="${LD-ld} -32" - ;; - *N32*) - LD="${LD-ld} -n32" +done + +# Double-quote double-evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_dquote_varnames); do + case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" ;; - *64-bit*) - LD="${LD-ld} -64" + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" ;; esac - fi - fi - rm -rf conftest* - ;; - -x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*|s390*-*linux*|sparc*-*linux*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if AC_TRY_EVAL(ac_compile); then - case `/usr/bin/file conftest.o` in - *32-bit*) - case $host in - x86_64-*linux*) - LD="${LD-ld} -m elf_i386" - ;; - ppc64-*linux*|powerpc64-*linux*) - LD="${LD-ld} -m elf32ppclinux" - ;; - s390x-*linux*) - LD="${LD-ld} -m elf_s390" - ;; - sparc64-*linux*) - LD="${LD-ld} -m elf32_sparc" - ;; - esac - ;; - *64-bit*) - case $host in - x86_64-*linux*) - LD="${LD-ld} -m elf_x86_64" - ;; - ppc*-*linux*|powerpc*-*linux*) - LD="${LD-ld} -m elf64ppc" - ;; - s390*-*linux*) - LD="${LD-ld} -m elf64_s390" - ;; - sparc*-*linux*) - LD="${LD-ld} -m elf64_sparc" - ;; - esac - ;; - esac - fi - rm -rf conftest* - ;; - -*-*-sco3.2v5*) - # On SCO OpenServer 5, we need -belf to get full-featured binaries. - SAVE_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -belf" - AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, - [AC_LANG_PUSH(C) - AC_TRY_LINK([],[],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) - AC_LANG_POP]) - if test x"$lt_cv_cc_needs_belf" != x"yes"; then - # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf - CFLAGS="$SAVE_CFLAGS" - fi - ;; -sparc*-*solaris*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if AC_TRY_EVAL(ac_compile); then - case `/usr/bin/file conftest.o` in - *64-bit*) - case $lt_cv_prog_gnu_ld in - yes*) LD="${LD-ld} -m elf64_sparc" ;; - *) LD="${LD-ld} -64" ;; - esac - ;; - esac - fi - rm -rf conftest* - ;; +done -AC_PROVIDE_IFELSE([AC_LIBTOOL_WIN32_DLL], -[*-*-cygwin* | *-*-mingw* | *-*-pw32*) - AC_CHECK_TOOL(DLLTOOL, dlltool, false) - AC_CHECK_TOOL(AS, as, false) - AC_CHECK_TOOL(OBJDUMP, objdump, false) +# Fix-up fallback echo if it was mangled by the above quoting rules. +case \$lt_ECHO in +*'\\\[$]0 --fallback-echo"')dnl " + lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\[$]0 --fallback-echo"\[$]/\[$]0 --fallback-echo"/'\` ;; - ]) esac -need_locks="$enable_libtool_lock" +_LT_OUTPUT_LIBTOOL_INIT +]) -])# _LT_AC_LOCK +# LT_OUTPUT +# --------- +# This macro allows early generation of the libtool script (before +# AC_OUTPUT is called), incase it is used in configure for compilation +# tests. +AC_DEFUN([LT_OUTPUT], +[: ${CONFIG_LT=./config.lt} +AC_MSG_NOTICE([creating $CONFIG_LT]) +cat >"$CONFIG_LT" <<_LTEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate a libtool stub with the current configuration. + +lt_cl_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_LTEOF + +cat >>"$CONFIG_LT" <<\_LTEOF +AS_SHELL_SANITIZE +_AS_PREPARE -# AC_LIBTOOL_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, -# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) -# ---------------------------------------------------------------- -# Check whether the given compiler option works -AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], -[AC_REQUIRE([LT_AC_PROG_SED]) -AC_CACHE_CHECK([$1], [$2], - [$2=no - ifelse([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$3" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&AS_MESSAGE_LOG_FD - echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - $2=yes - fi - fi - $rm conftest* -]) +exec AS_MESSAGE_FD>&1 +exec AS_MESSAGE_LOG_FD>>config.log +{ + echo + AS_BOX([Running $as_me.]) +} >&AS_MESSAGE_LOG_FD + +lt_cl_help="\ +\`$as_me' creates a local libtool stub from the current configuration, +for use in further configure time tests before the real libtool is +generated. + +Usage: $[0] [[OPTIONS]] + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + +Report bugs to ." + +lt_cl_version="\ +m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl +m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) +configured by $[0], generated by m4_PACKAGE_STRING. + +Copyright (C) 2008 Free Software Foundation, Inc. +This config.lt script is free software; the Free Software Foundation +gives unlimited permision to copy, distribute and modify it." -if test x"[$]$2" = xyes; then - ifelse([$5], , :, [$5]) -else - ifelse([$6], , :, [$6]) -fi -])# AC_LIBTOOL_COMPILER_OPTION +while test $[#] != 0 +do + case $[1] in + --version | --v* | -V ) + echo "$lt_cl_version"; exit 0 ;; + --help | --h* | -h ) + echo "$lt_cl_help"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --quiet | --q* | --silent | --s* | -q ) + lt_cl_silent=: ;; + -*) AC_MSG_ERROR([unrecognized option: $[1] +Try \`$[0] --help' for more information.]) ;; -# AC_LIBTOOL_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, -# [ACTION-SUCCESS], [ACTION-FAILURE]) -# ------------------------------------------------------------ -# Check whether the given compiler option works -AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], -[AC_CACHE_CHECK([$1], [$2], - [$2=no - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $3" - printf "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&AS_MESSAGE_LOG_FD - $echo "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - $2=yes - fi - else - $2=yes - fi - fi - $rm conftest* - LDFLAGS="$save_LDFLAGS" -]) + *) AC_MSG_ERROR([unrecognized argument: $[1] +Try \`$[0] --help' for more information.]) ;; + esac + shift +done -if test x"[$]$2" = xyes; then - ifelse([$4], , :, [$4]) -else - ifelse([$5], , :, [$5]) +if $lt_cl_silent; then + exec AS_MESSAGE_FD>/dev/null fi -])# AC_LIBTOOL_LINKER_OPTION +_LTEOF +cat >>"$CONFIG_LT" <<_LTEOF +_LT_OUTPUT_LIBTOOL_COMMANDS_INIT +_LTEOF + +cat >>"$CONFIG_LT" <<\_LTEOF +AC_MSG_NOTICE([creating $ofile]) +_LT_OUTPUT_LIBTOOL_COMMANDS +AS_EXIT(0) +_LTEOF +chmod +x "$CONFIG_LT" + +# configure is writing to config.log, but config.lt does its own redirection, +# appending to config.log, which fails on DOS, as config.log is still kept +# open by configure. Here we exec the FD to /dev/null, effectively closing +# config.log, so it can be properly (re)opened and appended to by config.lt. +if test "$no_create" != yes; then + lt_cl_success=: + test "$silent" = yes && + lt_config_lt_args="$lt_config_lt_args --quiet" + exec AS_MESSAGE_LOG_FD>/dev/null + $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false + exec AS_MESSAGE_LOG_FD>>config.log + $lt_cl_success || AS_EXIT(1) +fi +])# LT_OUTPUT -# AC_LIBTOOL_SYS_MAX_CMD_LEN -# -------------------------- -AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], -[# find the maximum length of command line arguments -AC_MSG_CHECKING([the maximum length of command line arguments]) -AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl - i=0 - teststring="ABCD" - case $build_os in - msdosdjgpp*) - # On DJGPP, this test can blow up pretty badly due to problems in libc - # (any single argument exceeding 2000 bytes causes a buffer overrun - # during glob expansion). Even if it were fixed, the result of this - # check would be larger than it should be. - lt_cv_sys_max_cmd_len=12288; # 12K is about right - ;; +# _LT_CONFIG(TAG) +# --------------- +# If TAG is the built-in tag, create an initial libtool script with a +# default configuration from the untagged config vars. Otherwise add code +# to config.status for appending the configuration named by TAG from the +# matching tagged config vars. +m4_defun([_LT_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_CONFIG_SAVE_COMMANDS([ + m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl + m4_if(_LT_TAG, [C], [ + # See if we are running on zsh, and set the options which allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi - gnu*) - # Under GNU Hurd, this test is not required because there is - # no limit to the length of command line arguments. - # Libtool will interpret -1 as no limit whatsoever - lt_cv_sys_max_cmd_len=-1; - ;; + cfgfile="${ofile}T" + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" - cygwin* | mingw*) - # On Win9x/ME, this test blows up -- it succeeds, but takes - # about 5 minutes as the teststring grows exponentially. - # Worse, since 9x/ME are not pre-emptively multitasking, - # you end up with a "frozen" computer, even though with patience - # the test eventually succeeds (with a max line length of 256k). - # Instead, let's just punt: use the minimum linelength reported by - # all of the supported platforms: 8192 (on NT/2K/XP). - lt_cv_sys_max_cmd_len=8192; - ;; + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL - amigaos*) - # On AmigaOS with pdksh, this test takes hours, literally. - # So we just punt and use a minimum line length of 8192. - lt_cv_sys_max_cmd_len=8192; - ;; +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +_LT_COPYING +_LT_LIBTOOL_TAGS - netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) - # This has been around since 386BSD, at least. Likely further. - if test -x /sbin/sysctl; then - lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` - elif test -x /usr/sbin/sysctl; then - lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` - else - lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs - fi - # And add a safety zone - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` - ;; +# ### BEGIN LIBTOOL CONFIG +_LT_LIBTOOL_CONFIG_VARS +_LT_LIBTOOL_TAG_VARS +# ### END LIBTOOL CONFIG - interix*) - # We know the value 262144 and hardcode it with a safety zone (like BSD) - lt_cv_sys_max_cmd_len=196608 - ;; +_LT_EOF - osf*) - # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure - # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not - # nice to cause kernel panics so lets avoid the loop below. - # First set a reasonable default. - lt_cv_sys_max_cmd_len=16384 - # - if test -x /sbin/sysconfig; then - case `/sbin/sysconfig -q proc exec_disable_arg_limit` in - *1*) lt_cv_sys_max_cmd_len=-1 ;; - esac - fi - ;; - sco3.2v5*) - lt_cv_sys_max_cmd_len=102400 - ;; - sysv5* | sco5v6* | sysv4.2uw2*) - kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` - if test -n "$kargmax"; then - lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` - else - lt_cv_sys_max_cmd_len=32768 - fi - ;; - *) - # If test is not a shell built-in, we'll probably end up computing a - # maximum length that is only half of the actual maximum length, but - # we can't tell. - SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} - while (test "X"`$SHELL [$]0 --fallback-echo "X$teststring" 2>/dev/null` \ - = "XX$teststring") >/dev/null 2>&1 && - new_result=`expr "X$teststring" : ".*" 2>&1` && - lt_cv_sys_max_cmd_len=$new_result && - test $i != 17 # 1/2 MB should be enough - do - i=`expr $i + 1` - teststring=$teststring$teststring - done - teststring= - # Add a significant safety factor because C++ compilers can tack on massive - # amounts of additional arguments before passing them to the linker. - # It appears as though 1/2 is a usable value. - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF ;; esac -]) -if test -n $lt_cv_sys_max_cmd_len ; then - AC_MSG_RESULT($lt_cv_sys_max_cmd_len) -else - AC_MSG_RESULT(none) -fi -])# AC_LIBTOOL_SYS_MAX_CMD_LEN + + _LT_PROG_LTMAIN + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + _LT_PROG_XSI_SHELLFNS + + sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" +], +[cat <<_LT_EOF >> "$ofile" + +dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded +dnl in a comment (ie after a #). +# ### BEGIN LIBTOOL TAG CONFIG: $1 +_LT_LIBTOOL_TAG_VARS(_LT_TAG) +# ### END LIBTOOL TAG CONFIG: $1 +_LT_EOF +])dnl /m4_if +], +[m4_if([$1], [], [ + PACKAGE='$PACKAGE' + VERSION='$VERSION' + TIMESTAMP='$TIMESTAMP' + RM='$RM' + ofile='$ofile'], []) +])dnl /_LT_CONFIG_SAVE_COMMANDS +])# _LT_CONFIG -# _LT_AC_CHECK_DLFCN +# LT_SUPPORTED_TAG(TAG) +# --------------------- +# Trace this macro to discover what tags are supported by the libtool +# --tag option, using: +# autoconf --trace 'LT_SUPPORTED_TAG:$1' +AC_DEFUN([LT_SUPPORTED_TAG], []) + + +# C support is built-in for now +m4_define([_LT_LANG_C_enabled], []) +m4_define([_LT_TAGS], []) + + +# LT_LANG(LANG) +# ------------- +# Enable libtool support for the given language if not already enabled. +AC_DEFUN([LT_LANG], +[AC_BEFORE([$0], [LT_OUTPUT])dnl +m4_case([$1], + [C], [_LT_LANG(C)], + [C++], [_LT_LANG(CXX)], + [Java], [_LT_LANG(GCJ)], + [Fortran 77], [_LT_LANG(F77)], + [Fortran], [_LT_LANG(FC)], + [Windows Resource], [_LT_LANG(RC)], + [m4_ifdef([_LT_LANG_]$1[_CONFIG], + [_LT_LANG($1)], + [m4_fatal([$0: unsupported language: "$1"])])])dnl +])# LT_LANG + + +# _LT_LANG(LANGNAME) # ------------------ -AC_DEFUN([_LT_AC_CHECK_DLFCN], -[AC_CHECK_HEADERS(dlfcn.h)dnl -])# _LT_AC_CHECK_DLFCN +m4_defun([_LT_LANG], +[m4_ifdef([_LT_LANG_]$1[_enabled], [], + [LT_SUPPORTED_TAG([$1])dnl + m4_append([_LT_TAGS], [$1 ])dnl + m4_define([_LT_LANG_]$1[_enabled], [])dnl + _LT_LANG_$1_CONFIG($1)])dnl +])# _LT_LANG -# _LT_AC_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, -# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) -# --------------------------------------------------------------------- -AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF], -[AC_REQUIRE([_LT_AC_CHECK_DLFCN])dnl -if test "$cross_compiling" = yes; then : - [$4] -else - lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 - lt_status=$lt_dlunknown - cat > conftest.$ac_ext < -#endif -#include +# _LT_TAG_COMPILER +# ---------------- +m4_defun([_LT_TAG_COMPILER], +[AC_REQUIRE([AC_PROG_CC])dnl -#ifdef RTLD_GLOBAL -# define LT_DLGLOBAL RTLD_GLOBAL -#else -# ifdef DL_GLOBAL -# define LT_DLGLOBAL DL_GLOBAL -# else -# define LT_DLGLOBAL 0 -# endif -#endif +_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl +_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl +_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl +_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl -/* We may have to define LT_DLLAZY_OR_NOW in the command line if we - find out it does not work in some platform. */ -#ifndef LT_DLLAZY_OR_NOW -# ifdef RTLD_LAZY -# define LT_DLLAZY_OR_NOW RTLD_LAZY -# else -# ifdef DL_LAZY -# define LT_DLLAZY_OR_NOW DL_LAZY -# else -# ifdef RTLD_NOW -# define LT_DLLAZY_OR_NOW RTLD_NOW -# else -# ifdef DL_NOW -# define LT_DLLAZY_OR_NOW DL_NOW -# else -# define LT_DLLAZY_OR_NOW 0 -# endif -# endif -# endif -# endif -#endif +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} -#ifdef __cplusplus -extern "C" void exit (int); -#endif +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} -void fnord() { int i=42;} -int main () -{ - void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); - int status = $lt_dlunknown; +# Allow CC to be a program name with arguments. +compiler=$CC +])# _LT_TAG_COMPILER - if (self) - { - if (dlsym (self,"fnord")) status = $lt_dlno_uscore; - else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; - /* dlclose (self); */ - } - else - puts (dlerror ()); - exit (status); -}] -EOF - if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then - (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null - lt_status=$? - case x$lt_status in - x$lt_dlno_uscore) $1 ;; - x$lt_dlneed_uscore) $2 ;; - x$lt_dlunknown|x*) $3 ;; - esac - else : - # compilation failed - $3 - fi -fi -rm -fr conftest* -])# _LT_AC_TRY_DLOPEN_SELF +# _LT_COMPILER_BOILERPLATE +# ------------------------ +# Check for compiler boilerplate output or warnings with +# the simple compiler test code. +m4_defun([_LT_COMPILER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* +])# _LT_COMPILER_BOILERPLATE -# AC_LIBTOOL_DLOPEN_SELF +# _LT_LINKER_BOILERPLATE # ---------------------- -AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], -[AC_REQUIRE([_LT_AC_CHECK_DLFCN])dnl -if test "x$enable_dlopen" != xyes; then - enable_dlopen=unknown - enable_dlopen_self=unknown - enable_dlopen_self_static=unknown -else - lt_cv_dlopen=no - lt_cv_dlopen_libs= +# Check for linker boilerplate output or warnings with +# the simple link test code. +m4_defun([_LT_LINKER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* +])# _LT_LINKER_BOILERPLATE +# _LT_REQUIRED_DARWIN_CHECKS +# ------------------------- +m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ case $host_os in - beos*) - lt_cv_dlopen="load_add_on" - lt_cv_dlopen_libs= - lt_cv_dlopen_self=yes - ;; - - mingw* | pw32*) - lt_cv_dlopen="LoadLibrary" - lt_cv_dlopen_libs= - ;; - - cygwin*) - lt_cv_dlopen="dlopen" - lt_cv_dlopen_libs= - ;; - - darwin*) - # if libdl is installed we need to link against it - AC_CHECK_LIB([dl], [dlopen], - [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ - lt_cv_dlopen="dyld" - lt_cv_dlopen_libs= - lt_cv_dlopen_self=yes + rhapsody* | darwin*) + AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) + AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) + AC_CHECK_TOOL([LIPO], [lipo], [:]) + AC_CHECK_TOOL([OTOOL], [otool], [:]) + AC_CHECK_TOOL([OTOOL64], [otool64], [:]) + _LT_DECL([], [DSYMUTIL], [1], + [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) + _LT_DECL([], [NMEDIT], [1], + [Tool to change global to local symbols on Mac OS X]) + _LT_DECL([], [LIPO], [1], + [Tool to manipulate fat objects and archives on Mac OS X]) + _LT_DECL([], [OTOOL], [1], + [ldd/readelf like tool for Mach-O binaries on Mac OS X]) + _LT_DECL([], [OTOOL64], [1], + [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) + + AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], + [lt_cv_apple_cc_single_mod=no + if test -z "${LT_MULTI_MODULE}"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi]) + AC_CACHE_CHECK([for -exported_symbols_list linker flag], + [lt_cv_ld_exported_symbols_list], + [lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [lt_cv_ld_exported_symbols_list=yes], + [lt_cv_ld_exported_symbols_list=no]) + LDFLAGS="$save_LDFLAGS" ]) - ;; - - *) - AC_CHECK_FUNC([shl_load], - [lt_cv_dlopen="shl_load"], - [AC_CHECK_LIB([dld], [shl_load], - [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-dld"], - [AC_CHECK_FUNC([dlopen], - [lt_cv_dlopen="dlopen"], - [AC_CHECK_LIB([dl], [dlopen], - [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], - [AC_CHECK_LIB([svld], [dlopen], - [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], - [AC_CHECK_LIB([dld], [dld_link], - [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-dld"]) - ]) - ]) - ]) - ]) - ]) + case $host_os in + rhapsody* | darwin1.[[012]]) + _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + 10.[[012]]*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test "$lt_cv_apple_cc_single_mod" = "yes"; then + _lt_dar_single_mod='$single_module' + fi + if test "$lt_cv_ld_exported_symbols_list" = "yes"; then + _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + if test "$DSYMUTIL" != ":"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi ;; esac +]) - if test "x$lt_cv_dlopen" != xno; then - enable_dlopen=yes + +# _LT_DARWIN_LINKER_FEATURES +# -------------------------- +# Checks for linker and compiler features on darwin +m4_defun([_LT_DARWIN_LINKER_FEATURES], +[ + m4_require([_LT_REQUIRED_DARWIN_CHECKS]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_automatic, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_TAGVAR(whole_archive_flag_spec, $1)='' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=echo + _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + m4_if([$1], [CXX], +[ if test "$lt_cv_apple_cc_single_mod" != "yes"; then + _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi +],[]) else - enable_dlopen=no + _LT_TAGVAR(ld_shlibs, $1)=no fi +]) - case $lt_cv_dlopen in - dlopen) - save_CPPFLAGS="$CPPFLAGS" - test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" - - save_LDFLAGS="$LDFLAGS" - wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" +# _LT_SYS_MODULE_PATH_AIX +# ----------------------- +# Links a minimal program and checks the executable +# for the system default hardcoded library path. In most cases, +# this is /usr/lib:/lib, but when the MPI compilers are used +# the location of the communication and MPI libs are included too. +# If we don't find anything, use the default library path according +# to the aix ld manual. +m4_defun([_LT_SYS_MODULE_PATH_AIX], +[m4_require([_LT_DECL_SED])dnl +AC_LINK_IFELSE(AC_LANG_PROGRAM,[ +lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\(.*\)$/\1/ + p + } + }' +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +fi],[]) +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi +])# _LT_SYS_MODULE_PATH_AIX - save_LIBS="$LIBS" - LIBS="$lt_cv_dlopen_libs $LIBS" - AC_CACHE_CHECK([whether a program can dlopen itself], - lt_cv_dlopen_self, [dnl - _LT_AC_TRY_DLOPEN_SELF( - lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, - lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) - ]) +# _LT_SHELL_INIT(ARG) +# ------------------- +m4_define([_LT_SHELL_INIT], +[ifdef([AC_DIVERSION_NOTICE], + [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)], + [AC_DIVERT_PUSH(NOTICE)]) +$1 +AC_DIVERT_POP +])# _LT_SHELL_INIT - if test "x$lt_cv_dlopen_self" = xyes; then - wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" - AC_CACHE_CHECK([whether a statically linked program can dlopen itself], - lt_cv_dlopen_self_static, [dnl - _LT_AC_TRY_DLOPEN_SELF( - lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, - lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) - ]) - fi - CPPFLAGS="$save_CPPFLAGS" - LDFLAGS="$save_LDFLAGS" - LIBS="$save_LIBS" - ;; - esac +# _LT_PROG_ECHO_BACKSLASH +# ----------------------- +# Add some code to the start of the generated configure script which +# will find an echo command which doesn't interpret backslashes. +m4_defun([_LT_PROG_ECHO_BACKSLASH], +[_LT_SHELL_INIT([ +# Check that we are running under the correct shell. +SHELL=${CONFIG_SHELL-/bin/sh} - case $lt_cv_dlopen_self in - yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; - *) enable_dlopen_self=unknown ;; - esac +case X$lt_ECHO in +X*--fallback-echo) + # Remove one level of quotation (which was required for Make). + ECHO=`echo "$lt_ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','` + ;; +esac - case $lt_cv_dlopen_self_static in - yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; - *) enable_dlopen_self_static=unknown ;; - esac +ECHO=${lt_ECHO-echo} +if test "X[$]1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift +elif test "X[$]1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then + # Yippee, $ECHO works! + : +else + # Restart under the correct shell. + exec $SHELL "[$]0" --no-reexec ${1+"[$]@"} fi -])# AC_LIBTOOL_DLOPEN_SELF +if test "X[$]1" = X--fallback-echo; then + # used as fallback echo + shift + cat <<_LT_EOF +[$]* +_LT_EOF + exit 0 +fi -# AC_LIBTOOL_PROG_CC_C_O([TAGNAME]) -# --------------------------------- -# Check to see if options -c and -o are simultaneously supported by compiler -AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O], -[AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl -AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], - [_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)], - [_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no - $rm -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&AS_MESSAGE_LOG_FD - echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - _LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes - fi - fi - chmod u+w . 2>&AS_MESSAGE_LOG_FD - $rm conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files - $rm out/* && rmdir out - cd .. - rmdir conftest - $rm conftest* -]) -])# AC_LIBTOOL_PROG_CC_C_O +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH +if test -z "$lt_ECHO"; then + if test "X${echo_test_string+set}" != Xset; then + # find a string as large as possible, as long as the shell can cope with it + for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do + # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ... + if { echo_test_string=`eval $cmd`; } 2>/dev/null && + { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null + then + break + fi + done + fi -# AC_LIBTOOL_SYS_HARD_LINK_LOCKS([TAGNAME]) -# ----------------------------------------- -# Check to see if we can do hard links to lock some files if needed -AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], -[AC_REQUIRE([_LT_AC_LOCK])dnl + if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && + echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + : + else + # The Solaris, AIX, and Digital Unix default echo programs unquote + # backslashes. This makes it impossible to quote backslashes using + # echo "$something" | sed 's/\\/\\\\/g' + # + # So, first we look for a working echo in the user's PATH. -hard_links="nottested" -if test "$_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then - # do not overwrite the value of need_locks provided by the user - AC_MSG_CHECKING([if we can lock with hard links]) - hard_links=yes - $rm conftest* - ln conftest.a conftest.b 2>/dev/null && hard_links=no - touch conftest.a - ln conftest.a conftest.b 2>&5 || hard_links=no - ln conftest.a conftest.b 2>/dev/null && hard_links=no - AC_MSG_RESULT([$hard_links]) - if test "$hard_links" = no; then - AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) - need_locks=warn - fi -else - need_locks=no -fi -])# AC_LIBTOOL_SYS_HARD_LINK_LOCKS - - -# AC_LIBTOOL_OBJDIR -# ----------------- -AC_DEFUN([AC_LIBTOOL_OBJDIR], -[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], -[rm -f .libs 2>/dev/null -mkdir .libs 2>/dev/null -if test -d .libs; then - lt_cv_objdir=.libs -else - # MS-DOS does not allow filenames that begin with a dot. - lt_cv_objdir=_libs -fi -rmdir .libs 2>/dev/null]) -objdir=$lt_cv_objdir -])# AC_LIBTOOL_OBJDIR + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for dir in $PATH /usr/ucb; do + IFS="$lt_save_ifs" + if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && + test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && + echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + ECHO="$dir/echo" + break + fi + done + IFS="$lt_save_ifs" + if test "X$ECHO" = Xecho; then + # We didn't find a better echo, so look for alternatives. + if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' && + echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + # This shell has a builtin print -r that does the trick. + ECHO='print -r' + elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } && + test "X$CONFIG_SHELL" != X/bin/ksh; then + # If we have ksh, try running configure again with it. + ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} + export ORIGINAL_CONFIG_SHELL + CONFIG_SHELL=/bin/ksh + export CONFIG_SHELL + exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"} + else + # Try using printf. + ECHO='printf %s\n' + if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && + echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + # Cool, printf works + : + elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && + test "X$echo_testing_string" = 'X\t' && + echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL + export CONFIG_SHELL + SHELL="$CONFIG_SHELL" + export SHELL + ECHO="$CONFIG_SHELL [$]0 --fallback-echo" + elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && + test "X$echo_testing_string" = 'X\t' && + echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + ECHO="$CONFIG_SHELL [$]0 --fallback-echo" + else + # maybe with a smaller string... + prev=: -# AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH([TAGNAME]) -# ---------------------------------------------- -# Check hardcoding attributes. -AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], -[AC_MSG_CHECKING([how to hardcode library paths into programs]) -_LT_AC_TAGVAR(hardcode_action, $1)= -if test -n "$_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)" || \ - test -n "$_LT_AC_TAGVAR(runpath_var, $1)" || \ - test "X$_LT_AC_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then + for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do + if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null + then + break + fi + prev="$cmd" + done - # We can hardcode non-existant directories. - if test "$_LT_AC_TAGVAR(hardcode_direct, $1)" != no && - # If the only mechanism to avoid hardcoding is shlibpath_var, we - # have to relink, otherwise we might link with an installed library - # when we should be linking with a yet-to-be-installed one - ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, $1)" != no && - test "$_LT_AC_TAGVAR(hardcode_minus_L, $1)" != no; then - # Linking always hardcodes the temporary library directory. - _LT_AC_TAGVAR(hardcode_action, $1)=relink - else - # We can link without hardcoding, and we can hardcode nonexisting dirs. - _LT_AC_TAGVAR(hardcode_action, $1)=immediate + if test "$prev" != 'sed 50q "[$]0"'; then + echo_test_string=`eval $prev` + export echo_test_string + exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"} + else + # Oops. We lost completely, so just stick with echo. + ECHO=echo + fi + fi + fi + fi fi -else - # We cannot hardcode anything, or else we can only hardcode existing - # directories. - _LT_AC_TAGVAR(hardcode_action, $1)=unsupported fi -AC_MSG_RESULT([$_LT_AC_TAGVAR(hardcode_action, $1)]) -if test "$_LT_AC_TAGVAR(hardcode_action, $1)" = relink; then - # Fast installation is not supported - enable_fast_install=no -elif test "$shlibpath_overrides_runpath" = yes || - test "$enable_shared" = no; then - # Fast installation is not necessary - enable_fast_install=needless +# Copy echo and quote the copy suitably for passing to libtool from +# the Makefile, instead of quoting the original, which is used later. +lt_ECHO=$ECHO +if test "X$lt_ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then + lt_ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo" fi -])# AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH +AC_SUBST(lt_ECHO) +]) +_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) +_LT_DECL([], [ECHO], [1], + [An echo program that does not interpret backslashes]) +])# _LT_PROG_ECHO_BACKSLASH -# AC_LIBTOOL_SYS_LIB_STRIP -# ------------------------ -AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP], -[striplib= -old_striplib= -AC_MSG_CHECKING([whether stripping libraries is possible]) -if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then - test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" - test -z "$striplib" && striplib="$STRIP --strip-unneeded" - AC_MSG_RESULT([yes]) -else -# FIXME - insert some real tests, host_os isn't really good enough - case $host_os in - darwin*) - if test -n "$STRIP" ; then - striplib="$STRIP -x" - AC_MSG_RESULT([yes]) - else - AC_MSG_RESULT([no]) -fi - ;; - *) - AC_MSG_RESULT([no]) - ;; - esac -fi -])# AC_LIBTOOL_SYS_LIB_STRIP +# _LT_ENABLE_LOCK +# --------------- +m4_defun([_LT_ENABLE_LOCK], +[AC_ARG_ENABLE([libtool-lock], + [AS_HELP_STRING([--disable-libtool-lock], + [avoid locking (might break parallel builds)])]) +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes -# AC_LIBTOOL_SYS_DYNAMIC_LINKER -# ----------------------------- -# PORTME Fill in your ld.so characteristics -AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER], -[AC_MSG_CHECKING([dynamic linker characteristics]) -library_names_spec= -libname_spec='lib$name' -soname_spec= -shrext_cmds=".so" -postinstall_cmds= -postuninstall_cmds= -finish_cmds= -finish_eval= -shlibpath_var= -shlibpath_overrides_runpath=unknown -version_type=none -dynamic_linker="$host_os ld.so" -sys_lib_dlsearch_path_spec="/lib /usr/lib" -if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then - # if the path contains ";" then we assume it to be the separator - # otherwise default to the standard path separator (i.e. ":") - it is - # assumed that no part of a normal pathname contains ";" but that should - # okay in the real world where ";" in dirpaths is itself problematic. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE="32" + ;; + *ELF-64*) + HPUX_IA64_MODE="64" + ;; + esac fi -else - sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" -fi -need_lib_prefix=unknown -hardcode_into_libs=no - -# when you set need_version to no, make sure it does not cause -set_version -# flags to be left without arguments -need_version=unknown - -case $host_os in -aix3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' - shlibpath_var=LIBPATH - - # AIX 3 has no versioning support, so we append a major version to the name. - soname_spec='${libname}${release}${shared_ext}$major' + rm -rf conftest* ;; - -aix4* | aix5*) - version_type=linux - need_lib_prefix=no - need_version=no - hardcode_into_libs=yes - if test "$host_cpu" = ia64; then - # AIX 5 supports IA64 - library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - else - # With GCC up to 2.95.x, collect2 would create an import file - # for dependence libraries. The import file would start with - # the line `#! .'. This would cause the generated library to - # depend on `.', always an invalid library. This was fixed in - # development snapshots of GCC prior to 3.0. - case $host_os in - aix4 | aix4.[[01]] | aix4.[[01]].*) - if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' - echo ' yes ' - echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then - : - else - can_build_shared=no - fi - ;; - esac - # AIX (on Power*) has no versioning support, so currently we can not hardcode correct - # soname into executable. Probably we can add versioning support to - # collect2, so additional links can be useful in future. - if test "$aix_use_runtimelinking" = yes; then - # If using run time linking (on AIX 4.2 or later) use lib.so - # instead of lib.a to let people know that these are not - # typical AIX shared libraries. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' +*-*-irix6*) + # Find out which ABI we are using. + echo '[#]line __oline__ "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac else - # We preserve .a as extension for shared libraries through AIX4.2 - # and later when we are not doing run time linking. - library_names_spec='${libname}${release}.a $libname.a' - soname_spec='${libname}${release}${shared_ext}$major' + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac fi - shlibpath_var=LIBPATH fi + rm -rf conftest* ;; -amigaos*) - library_names_spec='$libname.ixlibrary $libname.a' - # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' +x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_i386" + ;; + ppc64-*linux*|powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + ppc*-*linux*|powerpc*-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* ;; -beos*) - library_names_spec='${libname}${shared_ext}' - dynamic_linker="$host_os ld.so" - shlibpath_var=LIBRARY_PATH +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, + [AC_LANG_PUSH(C) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) + AC_LANG_POP]) + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi ;; - -bsdi[[45]]*) - version_type=linux - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" - sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" - # the default ld.so.conf also contains /usr/contrib/lib and - # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow - # libtool to hard-code these into programs +sparc*-*solaris*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) LD="${LD-ld} -m elf64_sparc" ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* ;; +esac -cygwin* | mingw* | pw32*) - version_type=windows - shrext_cmds=".dll" - need_version=no - need_lib_prefix=no +need_locks="$enable_libtool_lock" +])# _LT_ENABLE_LOCK - case $GCC,$host_os in - yes,cygwin* | yes,mingw* | yes,pw32*) - library_names_spec='$libname.dll.a' - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $rm \$dlpath' - shlibpath_overrides_runpath=yes - case $host_os in - cygwin*) - # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" - ;; - mingw*) - # MinGW DLLs use traditional 'lib' prefix - soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | [grep ';[c-zC-Z]:/' >/dev/null]; then - # It is most probably a Windows format PATH printed by - # mingw gcc, but we are running on Cygwin. Gcc prints its search - # path with ; separators, and with drive letters. We can handle the - # drive letters (cygwin fileutils understands them), so leave them, - # especially as we might pass files found there to a mingw objdump, - # which wouldn't understand a cygwinified path. Ahh. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi - ;; - pw32*) - # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' - ;; - esac - ;; +# _LT_CMD_OLD_ARCHIVE +# ------------------- +m4_defun([_LT_CMD_OLD_ARCHIVE], +[AC_CHECK_TOOL(AR, ar, false) +test -z "$AR" && AR=ar +test -z "$AR_FLAGS" && AR_FLAGS=cru +_LT_DECL([], [AR], [1], [The archiver]) +_LT_DECL([], [AR_FLAGS], [1]) + +AC_CHECK_TOOL(STRIP, strip, :) +test -z "$STRIP" && STRIP=: +_LT_DECL([], [STRIP], [1], [A symbol stripping program]) +AC_CHECK_TOOL(RANLIB, ranlib, :) +test -z "$RANLIB" && RANLIB=: +_LT_DECL([], [RANLIB], [1], + [Commands used to install an old-style archive]) + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" + ;; *) - library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" ;; esac - dynamic_linker='Win32 ld.exe' - # FIXME: first we should search . and the directory the executable is in - shlibpath_var=PATH - ;; + old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" +fi +_LT_DECL([], [old_postinstall_cmds], [2]) +_LT_DECL([], [old_postuninstall_cmds], [2]) +_LT_TAGDECL([], [old_archive_cmds], [2], + [Commands used to build an old-style archive]) +])# _LT_CMD_OLD_ARCHIVE -darwin* | rhapsody*) - dynamic_linker="$host_os dyld" - version_type=darwin - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext' - soname_spec='${libname}${release}${major}$shared_ext' - shlibpath_overrides_runpath=yes - shlibpath_var=DYLD_LIBRARY_PATH - shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' - # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. - if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` - else - sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib' - fi - sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' - ;; -dgux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; +# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------------------- +# Check whether the given compiler option works +AC_DEFUN([_LT_COMPILER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$3" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + fi + $RM conftest* +]) -freebsd1*) - dynamic_linker=no - ;; +if test x"[$]$2" = xyes; then + m4_if([$5], , :, [$5]) +else + m4_if([$6], , :, [$6]) +fi +])# _LT_COMPILER_OPTION -kfreebsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; +# Old name: +AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) + + +# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------- +# Check whether the given linker option works +AC_DEFUN([_LT_LINKER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $3" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&AS_MESSAGE_LOG_FD + $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + else + $2=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" +]) -freebsd* | dragonfly*) - # DragonFly does not have aout. When/if they implement a new - # versioning mechanism, adjust this. - if test -x /usr/bin/objformat; then - objformat=`/usr/bin/objformat` - else - case $host_os in - freebsd[[123]]*) objformat=aout ;; - *) objformat=elf ;; - esac - fi - version_type=freebsd-$objformat - case $version_type in - freebsd-elf*) - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - need_version=no - need_lib_prefix=no - ;; - freebsd-*) - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' - need_version=yes - ;; - esac - shlibpath_var=LD_LIBRARY_PATH - case $host_os in - freebsd2*) - shlibpath_overrides_runpath=yes - ;; - freebsd3.[[01]]* | freebsdelf3.[[01]]*) - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes +if test x"[$]$2" = xyes; then + m4_if([$4], , :, [$4]) +else + m4_if([$5], , :, [$5]) +fi +])# _LT_LINKER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) + + +# LT_CMD_MAX_LEN +#--------------- +AC_DEFUN([LT_CMD_MAX_LEN], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +# find the maximum length of command line arguments +AC_MSG_CHECKING([the maximum length of command line arguments]) +AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl + i=0 + teststring="ABCD" + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right ;; - freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ - freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) - shlibpath_overrides_runpath=no - hardcode_into_libs=yes + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; ;; - freebsd*) # from 4.6 on - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; ;; - esac - ;; -gnu*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - ;; + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; -hpux9* | hpux10* | hpux11*) - # Give a soname corresponding to the major version so that dld.sl refuses to - # link against other versions. - version_type=sunos - need_lib_prefix=no - need_version=no - case $host_cpu in - ia64*) - shrext_cmds='.so' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.so" - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - if test "X$HPUX_IA64_MODE" = X32; then - sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` else - sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs fi - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - hppa*64*) - shrext_cmds='.sl' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.sl" - shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - *) - shrext_cmds='.sl' - dynamic_linker="$host_os dld.sl" - shlibpath_var=SHLIB_PATH - shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; - esac - # HP-UX runs *really* slowly unless shared libraries are mode 555. - postinstall_cmds='chmod 555 $lib' - ;; -interix3*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; -irix5* | irix6* | nonstopux*) - case $host_os in - nonstopux*) version_type=nonstopux ;; - *) - if test "$lt_cv_prog_gnu_ld" = yes; then - version_type=linux - else - version_type=irix - fi ;; - esac - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' - case $host_os in - irix5* | nonstopux*) - libsuff= shlibsuff= + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi ;; *) - case $LD in # libtool.m4 will add one of these switches to LD - *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") - libsuff= shlibsuff= libmagic=32-bit;; - *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") - libsuff=32 shlibsuff=N32 libmagic=N32;; - *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") - libsuff=64 shlibsuff=64 libmagic=64-bit;; - *) libsuff= shlibsuff= libmagic=never-match;; - esac + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8 ; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test "X"`$SHELL [$]0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \ + = "XX$teststring$teststring"; } >/dev/null 2>&1 && + test $i != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi ;; esac - shlibpath_var=LD_LIBRARY${shlibsuff}_PATH - shlibpath_overrides_runpath=no - sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" - sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" - hardcode_into_libs=yes - ;; +]) +if test -n $lt_cv_sys_max_cmd_len ; then + AC_MSG_RESULT($lt_cv_sys_max_cmd_len) +else + AC_MSG_RESULT(none) +fi +max_cmd_len=$lt_cv_sys_max_cmd_len +_LT_DECL([], [max_cmd_len], [0], + [What is the maximum length of a command?]) +])# LT_CMD_MAX_LEN -# No shared lib support for Linux oldld, aout, or coff. -linux*oldld* | linux*aout* | linux*coff*) - dynamic_linker=no - ;; +# Old name: +AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) -# This must be Linux ELF. -linux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes - # Append ld.so.conf contents to the search path - if test -f /etc/ld.so.conf; then - lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" - fi +# _LT_HEADER_DLFCN +# ---------------- +m4_defun([_LT_HEADER_DLFCN], +[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl +])# _LT_HEADER_DLFCN - # We used to test for /lib/ld.so.1 and disable shared libraries on - # powerpc, because MkLinux only supported shared libraries with the - # GNU dynamic linker. Since this was broken with cross compilers, - # most powerpc-linux boxes support dynamic linking these days and - # people can always --disable-shared, the test was removed, and we - # assume the GNU/Linux dynamic linker is in use. - dynamic_linker='GNU/Linux ld.so' - ;; -knetbsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; +# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, +# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) +# ---------------------------------------------------------------- +m4_defun([_LT_TRY_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test "$cross_compiling" = yes; then : + [$4] +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +[#line __oline__ "configure" +#include "confdefs.h" -netbsd*) - version_type=sunos - need_lib_prefix=no - need_version=no - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - dynamic_linker='NetBSD (a.out) ld.so' +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +void fnord() { int i=42;} +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + /* dlclose (self); */ + } else - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='NetBSD ld.elf_so' + puts (dlerror ()); + + return status; +}] +_LT_EOF + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) $1 ;; + x$lt_dlneed_uscore) $2 ;; + x$lt_dlunknown|x*) $3 ;; + esac + else : + # compilation failed + $3 fi - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; +fi +rm -fr conftest* +])# _LT_TRY_DLOPEN_SELF -newsos6) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; -nto-qnx*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; +# LT_SYS_DLOPEN_SELF +# ------------------ +AC_DEFUN([LT_SYS_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= -openbsd*) - version_type=sunos - sys_lib_dlsearch_path_spec="/usr/lib" - need_lib_prefix=no - # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. case $host_os in - openbsd3.3 | openbsd3.3.*) need_version=yes ;; - *) need_version=no ;; - esac - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - shlibpath_var=LD_LIBRARY_PATH - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - case $host_os in - openbsd2.[[89]] | openbsd2.[[89]].*) - shlibpath_overrides_runpath=no - ;; - *) - shlibpath_overrides_runpath=yes - ;; - esac - else - shlibpath_overrides_runpath=yes - fi - ;; - -os2*) - libname_spec='$name' - shrext_cmds=".dll" - need_lib_prefix=no - library_names_spec='$libname${shared_ext} $libname.a' - dynamic_linker='OS/2 ld.exe' - shlibpath_var=LIBPATH - ;; + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; -osf3* | osf4* | osf5*) - version_type=osf - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" - sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" - ;; + mingw* | pw32* | cegcc*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; -solaris*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - # ldd complains unless libraries are executable - postinstall_cmds='chmod +x $lib' - ;; + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; -sunos4*) - version_type=sunos - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - if test "$with_gnu_ld" = yes; then - need_lib_prefix=no - fi - need_version=yes - ;; + darwin*) + # if libdl is installed we need to link against it + AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ]) + ;; -sysv4 | sysv4.3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - case $host_vendor in - sni) - shlibpath_overrides_runpath=no - need_lib_prefix=no - export_dynamic_flag_spec='${wl}-Blargedynsym' - runpath_var=LD_RUN_PATH - ;; - siemens) - need_lib_prefix=no - ;; - motorola) - need_lib_prefix=no - need_version=no - shlibpath_overrides_runpath=no - sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' - ;; + *) + AC_CHECK_FUNC([shl_load], + [lt_cv_dlopen="shl_load"], + [AC_CHECK_LIB([dld], [shl_load], + [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"], + [AC_CHECK_FUNC([dlopen], + [lt_cv_dlopen="dlopen"], + [AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], + [AC_CHECK_LIB([svld], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], + [AC_CHECK_LIB([dld], [dld_link], + [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"]) + ]) + ]) + ]) + ]) + ]) + ;; esac - ;; - -sysv4*MP*) - if test -d /usr/nec ;then - version_type=linux - library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' - soname_spec='$libname${shared_ext}.$major' - shlibpath_var=LD_LIBRARY_PATH - fi - ;; -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=freebsd-elf - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - if test "$with_gnu_ld" = yes; then - sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' - shlibpath_overrides_runpath=no + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes else - sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' - shlibpath_overrides_runpath=yes - case $host_os in - sco3.2v5*) - sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" - ;; - esac + enable_dlopen=no fi - sys_lib_dlsearch_path_spec='/usr/lib' - ;; - -uts4*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; -*) - dynamic_linker=no - ;; -esac -AC_MSG_RESULT([$dynamic_linker]) -test "$dynamic_linker" = no && can_build_shared=no + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" -variables_saved_for_relink="PATH $shlibpath_var $runpath_var" -if test "$GCC" = yes; then - variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" -fi -])# AC_LIBTOOL_SYS_DYNAMIC_LINKER + save_LDFLAGS="$LDFLAGS" + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" -# _LT_AC_TAGCONFIG -# ---------------- -AC_DEFUN([_LT_AC_TAGCONFIG], -[AC_ARG_WITH([tags], - [AC_HELP_STRING([--with-tags@<:@=TAGS@:>@], - [include additional configurations @<:@automatic@:>@])], - [tagnames="$withval"]) - -if test -f "$ltmain" && test -n "$tagnames"; then - if test ! -f "${ofile}"; then - AC_MSG_WARN([output file `$ofile' does not exist]) - fi + AC_CACHE_CHECK([whether a program can dlopen itself], + lt_cv_dlopen_self, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, + lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) + ]) - if test -z "$LTCC"; then - eval "`$SHELL ${ofile} --config | grep '^LTCC='`" - if test -z "$LTCC"; then - AC_MSG_WARN([output file `$ofile' does not look like a libtool script]) - else - AC_MSG_WARN([using `LTCC=$LTCC', extracted from `$ofile']) + if test "x$lt_cv_dlopen_self" = xyes; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + AC_CACHE_CHECK([whether a statically linked program can dlopen itself], + lt_cv_dlopen_self_static, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, + lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) + ]) fi - fi - if test -z "$LTCFLAGS"; then - eval "`$SHELL ${ofile} --config | grep '^LTCFLAGS='`" - fi - # Extract list of available tagged configurations in $ofile. - # Note that this assumes the entire list is on one line. - available_tags=`grep "^available_tags=" "${ofile}" | $SED -e 's/available_tags=\(.*$\)/\1/' -e 's/\"//g'` + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for tagname in $tagnames; do - IFS="$lt_save_ifs" - # Check whether tagname contains only valid characters - case `$echo "X$tagname" | $Xsed -e 's:[[-_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890,/]]::g'` in - "") ;; - *) AC_MSG_ERROR([invalid tag name: $tagname]) - ;; - esac + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac - if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "${ofile}" > /dev/null - then - AC_MSG_ERROR([tag name \"$tagname\" already exists]) - fi + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi +_LT_DECL([dlopen_support], [enable_dlopen], [0], + [Whether dlopen is supported]) +_LT_DECL([dlopen_self], [enable_dlopen_self], [0], + [Whether dlopen of programs is supported]) +_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], + [Whether dlopen of statically linked programs is supported]) +])# LT_SYS_DLOPEN_SELF + +# Old name: +AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) - # Update the list of available tags. - if test -n "$tagname"; then - echo appending configuration tag \"$tagname\" to $ofile - - case $tagname in - CXX) - if test -n "$CXX" && ( test "X$CXX" != "Xno" && - ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || - (test "X$CXX" != "Xg++"))) ; then - AC_LIBTOOL_LANG_CXX_CONFIG - else - tagname="" - fi - ;; - F77) - if test -n "$F77" && test "X$F77" != "Xno"; then - AC_LIBTOOL_LANG_F77_CONFIG - else - tagname="" - fi - ;; +# _LT_COMPILER_C_O([TAGNAME]) +# --------------------------- +# Check to see if options -c and -o are simultaneously supported by compiler. +# This macro does not hard code the compiler like AC_PROG_CC_C_O. +m4_defun([_LT_COMPILER_C_O], +[m4_require([_LT_DECL_SED])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext - GCJ) - if test -n "$GCJ" && test "X$GCJ" != "Xno"; then - AC_LIBTOOL_LANG_GCJ_CONFIG - else - tagname="" - fi - ;; + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + fi + fi + chmod u+w . 2>&AS_MESSAGE_LOG_FD + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* +]) +_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], + [Does compiler simultaneously support -c and -o options?]) +])# _LT_COMPILER_C_O - RC) - AC_LIBTOOL_LANG_RC_CONFIG - ;; - *) - AC_MSG_ERROR([Unsupported tag name: $tagname]) - ;; - esac +# _LT_COMPILER_FILE_LOCKS([TAGNAME]) +# ---------------------------------- +# Check to see if we can do hard links to lock some files if needed +m4_defun([_LT_COMPILER_FILE_LOCKS], +[m4_require([_LT_ENABLE_LOCK])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_COMPILER_C_O([$1]) - # Append the new tag name to the list of available tags. - if test -n "$tagname" ; then - available_tags="$available_tags $tagname" - fi - fi - done - IFS="$lt_save_ifs" - - # Now substitute the updated list of available tags. - if eval "sed -e 's/^available_tags=.*\$/available_tags=\"$available_tags\"/' \"$ofile\" > \"${ofile}T\""; then - mv "${ofile}T" "$ofile" - chmod +x "$ofile" - else - rm -f "${ofile}T" - AC_MSG_ERROR([unable to update list of available tagged configurations.]) +hard_links="nottested" +if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + AC_MSG_CHECKING([if we can lock with hard links]) + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + AC_MSG_RESULT([$hard_links]) + if test "$hard_links" = no; then + AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) + need_locks=warn fi +else + need_locks=no fi -])# _LT_AC_TAGCONFIG +_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) +])# _LT_COMPILER_FILE_LOCKS -# AC_LIBTOOL_DLOPEN -# ----------------- -# enable checks for dlopen support -AC_DEFUN([AC_LIBTOOL_DLOPEN], - [AC_BEFORE([$0],[AC_LIBTOOL_SETUP]) -])# AC_LIBTOOL_DLOPEN +# _LT_CHECK_OBJDIR +# ---------------- +m4_defun([_LT_CHECK_OBJDIR], +[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], +[rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null]) +objdir=$lt_cv_objdir +_LT_DECL([], [objdir], [0], + [The name of the directory that contains temporary libtool files])dnl +m4_pattern_allow([LT_OBJDIR])dnl +AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/", + [Define to the sub-directory in which libtool stores uninstalled libraries.]) +])# _LT_CHECK_OBJDIR -# AC_LIBTOOL_WIN32_DLL -# -------------------- -# declare package support for building win32 DLLs -AC_DEFUN([AC_LIBTOOL_WIN32_DLL], -[AC_BEFORE([$0], [AC_LIBTOOL_SETUP]) -])# AC_LIBTOOL_WIN32_DLL +# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) +# -------------------------------------- +# Check hardcoding attributes. +m4_defun([_LT_LINKER_HARDCODE_LIBPATH], +[AC_MSG_CHECKING([how to hardcode library paths into programs]) +_LT_TAGVAR(hardcode_action, $1)= +if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || + test -n "$_LT_TAGVAR(runpath_var, $1)" || + test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then + # We can hardcode non-existent directories. + if test "$_LT_TAGVAR(hardcode_direct, $1)" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no && + test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then + # Linking always hardcodes the temporary library directory. + _LT_TAGVAR(hardcode_action, $1)=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + _LT_TAGVAR(hardcode_action, $1)=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + _LT_TAGVAR(hardcode_action, $1)=unsupported +fi +AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) -# AC_ENABLE_SHARED([DEFAULT]) -# --------------------------- -# implement the --enable-shared flag -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. -AC_DEFUN([AC_ENABLE_SHARED], -[define([AC_ENABLE_SHARED_DEFAULT], ifelse($1, no, no, yes))dnl -AC_ARG_ENABLE([shared], - [AC_HELP_STRING([--enable-shared@<:@=PKGS@:>@], - [build shared libraries @<:@default=]AC_ENABLE_SHARED_DEFAULT[@:>@])], - [p=${PACKAGE-default} - case $enableval in - yes) enable_shared=yes ;; - no) enable_shared=no ;; - *) - enable_shared=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_shared=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac], - [enable_shared=]AC_ENABLE_SHARED_DEFAULT) -])# AC_ENABLE_SHARED +if test "$_LT_TAGVAR(hardcode_action, $1)" = relink || + test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi +_LT_TAGDECL([], [hardcode_action], [0], + [How to hardcode a shared library path into an executable]) +])# _LT_LINKER_HARDCODE_LIBPATH -# AC_DISABLE_SHARED -# ----------------- -# set the default shared flag to --disable-shared -AC_DEFUN([AC_DISABLE_SHARED], -[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl -AC_ENABLE_SHARED(no) -])# AC_DISABLE_SHARED +# _LT_CMD_STRIPLIB +# ---------------- +m4_defun([_LT_CMD_STRIPLIB], +[m4_require([_LT_DECL_EGREP]) +striplib= +old_striplib= +AC_MSG_CHECKING([whether stripping libraries is possible]) +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac +fi +_LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) +_LT_DECL([], [striplib], [1]) +])# _LT_CMD_STRIPLIB -# AC_ENABLE_STATIC([DEFAULT]) -# --------------------------- -# implement the --enable-static flag -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. -AC_DEFUN([AC_ENABLE_STATIC], -[define([AC_ENABLE_STATIC_DEFAULT], ifelse($1, no, no, yes))dnl -AC_ARG_ENABLE([static], - [AC_HELP_STRING([--enable-static@<:@=PKGS@:>@], - [build static libraries @<:@default=]AC_ENABLE_STATIC_DEFAULT[@:>@])], - [p=${PACKAGE-default} - case $enableval in - yes) enable_static=yes ;; - no) enable_static=no ;; - *) - enable_static=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_static=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac], - [enable_static=]AC_ENABLE_STATIC_DEFAULT) -])# AC_ENABLE_STATIC +# _LT_SYS_DYNAMIC_LINKER([TAG]) +# ----------------------------- +# PORTME Fill in your ld.so characteristics +m4_defun([_LT_SYS_DYNAMIC_LINKER], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_OBJDUMP])dnl +m4_require([_LT_DECL_SED])dnl +AC_MSG_CHECKING([dynamic linker characteristics]) +m4_if([$1], + [], [ +if test "$GCC" = yes; then + case $host_os in + darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; + *) lt_awk_arg="/^libraries:/" ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'` + else + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary. + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path/$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" + else + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk ' +BEGIN {RS=" "; FS="/|\n";} { + lt_foo=""; + lt_count=0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo="/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[[lt_foo]]++; } + if (lt_freq[[lt_foo]] == 1) { print lt_foo; } +}'` + sys_lib_search_path_spec=`$ECHO $lt_search_path_spec` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi]) +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown -# AC_DISABLE_STATIC -# ----------------- -# set the default static flag to --disable-static -AC_DEFUN([AC_DISABLE_STATIC], -[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl -AC_ENABLE_STATIC(no) -])# AC_DISABLE_STATIC +case $host_os in +aix3*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; -# AC_ENABLE_FAST_INSTALL([DEFAULT]) -# --------------------------------- -# implement the --enable-fast-install flag -# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. -AC_DEFUN([AC_ENABLE_FAST_INSTALL], -[define([AC_ENABLE_FAST_INSTALL_DEFAULT], ifelse($1, no, no, yes))dnl -AC_ARG_ENABLE([fast-install], - [AC_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], - [optimize for fast installation @<:@default=]AC_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], - [p=${PACKAGE-default} - case $enableval in - yes) enable_fast_install=yes ;; - no) enable_fast_install=no ;; - *) - enable_fast_install=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_fast_install=yes - fi - done - IFS="$lt_save_ifs" +aix[[4-9]]*) + version_type=linux + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[[01]] | aix4.[[01]].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi ;; - esac], - [enable_fast_install=]AC_ENABLE_FAST_INSTALL_DEFAULT) -])# AC_ENABLE_FAST_INSTALL - - -# AC_DISABLE_FAST_INSTALL -# ----------------------- -# set the default to --disable-fast-install -AC_DEFUN([AC_DISABLE_FAST_INSTALL], -[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl -AC_ENABLE_FAST_INSTALL(no) -])# AC_DISABLE_FAST_INSTALL - + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; -# AC_LIBTOOL_PICMODE([MODE]) -# -------------------------- -# implement the --with-pic flag -# MODE is either `yes' or `no'. If omitted, it defaults to `both'. -AC_DEFUN([AC_LIBTOOL_PICMODE], -[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl -pic_mode=ifelse($#,1,$1,default) -])# AC_LIBTOOL_PICMODE +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; -# AC_PROG_EGREP -# ------------- -# This is predefined starting with Autoconf 2.54, so this conditional -# definition can be removed once we require Autoconf 2.54 or later. -m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP], -[AC_CACHE_CHECK([for egrep], [ac_cv_prog_egrep], - [if echo a | (grep -E '(a|b)') >/dev/null 2>&1 - then ac_cv_prog_egrep='grep -E' - else ac_cv_prog_egrep='egrep' - fi]) - EGREP=$ac_cv_prog_egrep - AC_SUBST([EGREP]) -])]) +bsdi[[45]]*) + version_type=linux + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no -# AC_PATH_TOOL_PREFIX -# ------------------- -# find a file program which can recognise shared library -AC_DEFUN([AC_PATH_TOOL_PREFIX], -[AC_REQUIRE([AC_PROG_EGREP])dnl -AC_MSG_CHECKING([for $1]) -AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, -[case $MAGIC_CMD in -[[\\/*] | ?:[\\/]*]) - lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. - ;; -*) - lt_save_MAGIC_CMD="$MAGIC_CMD" - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR -dnl $ac_dummy forces splitting on constant user-supplied paths. -dnl POSIX.2 word splitting is done only on the output of word expansions, -dnl not every word. This closes a longstanding sh security hole. - ac_dummy="ifelse([$2], , $PATH, [$2])" - for ac_dir in $ac_dummy; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/$1; then - lt_cv_path_MAGIC_CMD="$ac_dir/$1" - if test -n "$file_magic_test_file"; then - case $deplibs_check_method in - "file_magic "*) - file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` - MAGIC_CMD="$lt_cv_path_MAGIC_CMD" - if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | - $EGREP "$file_magic_regex" > /dev/null; then - : - else - cat <&2 - -*** Warning: the command libtool uses to detect shared libraries, -*** $file_magic_cmd, produces output that libtool cannot recognize. -*** The result is that libtool may fail to recognize shared libraries -*** as such. This will affect the creation of libtool libraries that -*** depend on shared libraries, but programs linked with such libtool -*** libraries will work regardless of this problem. Nevertheless, you -*** may want to report the problem to your system manager and/or to -*** bug-libtool@gnu.org + case $GCC,$host_os in + yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*) + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes -EOF - fi ;; - esac + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then + # It is most probably a Windows format PATH printed by + # mingw gcc, but we are running on Cygwin. Gcc prints its search + # path with ; separators, and with drive letters. We can handle the + # drive letters (cygwin fileutils understands them), so leave them, + # especially as we might pass files found there to a mingw objdump, + # which wouldn't understand a cygwinified path. Ahh. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi - break - fi - done - IFS="$lt_save_ifs" - MAGIC_CMD="$lt_save_MAGIC_CMD" - ;; -esac]) -MAGIC_CMD="$lt_cv_path_MAGIC_CMD" -if test -n "$MAGIC_CMD"; then - AC_MSG_RESULT($MAGIC_CMD) -else - AC_MSG_RESULT(no) -fi -])# AC_PATH_TOOL_PREFIX - - -# AC_PATH_MAGIC -# ------------- -# find a file program which can recognise a shared library -AC_DEFUN([AC_PATH_MAGIC], -[AC_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) -if test -z "$lt_cv_path_MAGIC_CMD"; then - if test -n "$ac_tool_prefix"; then - AC_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) - else - MAGIC_CMD=: - fi -fi -])# AC_PATH_MAGIC - - -# AC_PROG_LD -# ---------- -# find the pathname to the GNU or non-GNU linker -AC_DEFUN([AC_PROG_LD], -[AC_ARG_WITH([gnu-ld], - [AC_HELP_STRING([--with-gnu-ld], - [assume the C compiler uses GNU ld @<:@default=no@:>@])], - [test "$withval" = no || with_gnu_ld=yes], - [with_gnu_ld=no]) -AC_REQUIRE([LT_AC_PROG_SED])dnl -AC_REQUIRE([AC_PROG_CC])dnl -AC_REQUIRE([AC_CANONICAL_HOST])dnl -AC_REQUIRE([AC_CANONICAL_BUILD])dnl -ac_prog=ld -if test "$GCC" = yes; then - # Check if gcc -print-prog-name=ld gives a path. - AC_MSG_CHECKING([for ld used by $CC]) - case $host in - *-*-mingw*) - # gcc leaves a trailing carriage return which upsets mingw - ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; - *) - ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; - esac - case $ac_prog in - # Accept absolute paths. - [[\\/]]* | ?:[[\\/]]*) - re_direlt='/[[^/]][[^/]]*/\.\./' - # Canonicalize the pathname of ld - ac_prog=`echo $ac_prog| $SED 's%\\\\%/%g'` - while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do - ac_prog=`echo $ac_prog| $SED "s%$re_direlt%/%"` - done - test -z "$LD" && LD="$ac_prog" ;; - "") - # If it fails, then pretend we aren't using GCC. - ac_prog=ld + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + ;; + esac ;; + *) - # If it is relative, then search for the first ld in PATH. - with_gnu_ld=unknown + library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' ;; esac -elif test "$with_gnu_ld" = yes; then - AC_MSG_CHECKING([for GNU ld]) -else - AC_MSG_CHECKING([for non-GNU ld]) -fi -AC_CACHE_VAL(lt_cv_path_LD, -[if test -z "$LD"; then - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for ac_dir in $PATH; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then - lt_cv_path_LD="$ac_dir/$ac_prog" - # Check to see if the program is GNU ld. I'd rather use --version, - # but apparently some variants of GNU ld only accept -v. - # Break only if it was the GNU/non-GNU ld that we prefer. - case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null; then - case $host_cpu in - i*86 ) - # Not sure whether the presence of OpenBSD here was a mistake. - # Let's accept both of them until this is cleared up. - lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' - lt_cv_file_magic_cmd=/usr/bin/file - lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` - ;; - esac +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` else - lt_cv_deplibs_check_method=pass_all + case $host_os in + freebsd[[123]]*) objformat=aout ;; + *) objformat=elf ;; + esac fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[[01]]* | freebsdelf3.[[01]]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ + freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac ;; gnu*) - lt_cv_deplibs_check_method=pass_all + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + hardcode_into_libs=yes ;; -hpux10.20* | hpux11*) - lt_cv_file_magic_cmd=/usr/bin/file +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no case $host_cpu in ia64*) - lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' - lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; hppa*64*) - [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'] - lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; *) - lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library' - lt_cv_file_magic_test_file=/usr/lib/libc.sl + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' ;; esac + # HP-UX runs *really* slowly unless shared libraries are mode 555. + postinstall_cmds='chmod 555 $lib' ;; -interix3*) - # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' +interix[[3-9]]*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes ;; irix5* | irix6* | nonstopux*) - case $LD in - *-32|*"-32 ") libmagic=32-bit;; - *-n32|*"-n32 ") libmagic=N32;; - *-64|*"-64 ") libmagic=64-bit;; - *) libmagic=never-match;; + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux + else + version_type=irix + fi ;; esac - lt_cv_deplibs_check_method=pass_all + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no ;; # This must be Linux ELF. -linux*) - lt_cv_deplibs_check_method=pass_all +linux* | k*bsd*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + # Some binutils ld are patched to set DT_RUNPATH + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ + LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], + [shlibpath_overrides_runpath=yes])]) + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' ;; netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' else - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes ;; -newos6*) - lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' - lt_cv_file_magic_cmd=/usr/bin/file - lt_cv_file_magic_test_file=/usr/lib/libnls.so +newsos6) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes ;; -nto-qnx*) - lt_cv_deplibs_check_method=unknown +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' ;; openbsd*) - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[[89]] | openbsd2.[[89]].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac else - lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + shlibpath_overrides_runpath=yes fi ;; +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + osf3* | osf4* | osf5*) - lt_cv_deplibs_check_method=pass_all + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" ;; -solaris*) - lt_cv_deplibs_check_method=pass_all +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes ;; sysv4 | sysv4.3*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH case $host_vendor in - motorola) - lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' - lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` - ;; - ncr) - lt_cv_deplibs_check_method=pass_all - ;; - sequent) - lt_cv_file_magic_cmd='/bin/file' - lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' - ;; - sni) - lt_cv_file_magic_cmd='/bin/file' - lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" - lt_cv_file_magic_test_file=/lib/libc.so - ;; - siemens) - lt_cv_deplibs_check_method=pass_all - ;; - pc) - lt_cv_deplibs_check_method=pass_all - ;; + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; esac ;; -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - lt_cv_deplibs_check_method=pass_all +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi ;; -esac -]) -file_magic_cmd=$lt_cv_file_magic_cmd -deplibs_check_method=$lt_cv_deplibs_check_method -test -z "$deplibs_check_method" && deplibs_check_method=unknown -])# AC_DEPLIBS_CHECK_METHOD - -# AC_PROG_NM -# ---------- -# find the pathname to a BSD-compatible name lister -AC_DEFUN([AC_PROG_NM], -[AC_CACHE_CHECK([for BSD-compatible nm], lt_cv_path_NM, -[if test -n "$NM"; then - # Let the user override the test. - lt_cv_path_NM="$NM" -else - lt_nm_to_check="${ac_tool_prefix}nm" - if test -n "$ac_tool_prefix" && test "$build" = "$host"; then - lt_nm_to_check="$lt_nm_to_check nm" +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=freebsd-elf + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test "$with_gnu_ld" = yes; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac fi - for lt_tmp_nm in $lt_nm_to_check; do - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - tmp_nm="$ac_dir/$lt_tmp_nm" - if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then - # Check to see if the nm accepts a BSD-compat flag. - # Adding the `sed 1q' prevents false positives on HP-UX, which says: - # nm: unknown option "B" ignored - # Tru64's nm complains that /dev/null is an invalid object file - case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in - */dev/null* | *'Invalid file or object type'*) - lt_cv_path_NM="$tmp_nm -B" - break - ;; - *) - case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in - */dev/null*) - lt_cv_path_NM="$tmp_nm -p" - break - ;; - *) - lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but - continue # so that we can try to find one that supports BSD flags - ;; - esac - ;; - esac - fi - done - IFS="$lt_save_ifs" - done - test -z "$lt_cv_path_NM" && lt_cv_path_NM=nm -fi]) -NM="$lt_cv_path_NM" -])# AC_PROG_NM - + sys_lib_dlsearch_path_spec='/usr/lib' + ;; -# AC_CHECK_LIBM -# ------------- -# check for math library -AC_DEFUN([AC_CHECK_LIBM], -[AC_REQUIRE([AC_CANONICAL_HOST])dnl -LIBM= -case $host in -*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*) - # These system don't have libm, or don't need it +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes ;; -*-ncr-sysv4.3*) - AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") - AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") + +uts4*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH ;; + *) - AC_CHECK_LIB(m, cos, LIBM="-lm") + dynamic_linker=no ;; esac -])# AC_CHECK_LIBM - - -# AC_LIBLTDL_CONVENIENCE([DIRECTORY]) -# ----------------------------------- -# sets LIBLTDL to the link flags for the libltdl convenience library and -# LTDLINCL to the include flags for the libltdl header and adds -# --enable-ltdl-convenience to the configure arguments. Note that -# AC_CONFIG_SUBDIRS is not called here. If DIRECTORY is not provided, -# it is assumed to be `libltdl'. LIBLTDL will be prefixed with -# '${top_builddir}/' and LTDLINCL will be prefixed with '${top_srcdir}/' -# (note the single quotes!). If your package is not flat and you're not -# using automake, define top_builddir and top_srcdir appropriately in -# the Makefiles. -AC_DEFUN([AC_LIBLTDL_CONVENIENCE], -[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl - case $enable_ltdl_convenience in - no) AC_MSG_ERROR([this package needs a convenience libltdl]) ;; - "") enable_ltdl_convenience=yes - ac_configure_args="$ac_configure_args --enable-ltdl-convenience" ;; - esac - LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdlc.la - LTDLINCL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl']) - # For backwards non-gettext consistent compatibility... - INCLTDL="$LTDLINCL" -])# AC_LIBLTDL_CONVENIENCE +AC_MSG_RESULT([$dynamic_linker]) +test "$dynamic_linker" = no && can_build_shared=no +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi -# AC_LIBLTDL_INSTALLABLE([DIRECTORY]) -# ----------------------------------- -# sets LIBLTDL to the link flags for the libltdl installable library and -# LTDLINCL to the include flags for the libltdl header and adds -# --enable-ltdl-install to the configure arguments. Note that -# AC_CONFIG_SUBDIRS is not called here. If DIRECTORY is not provided, -# and an installed libltdl is not found, it is assumed to be `libltdl'. -# LIBLTDL will be prefixed with '${top_builddir}/'# and LTDLINCL with -# '${top_srcdir}/' (note the single quotes!). If your package is not -# flat and you're not using automake, define top_builddir and top_srcdir -# appropriately in the Makefiles. -# In the future, this macro may have to be called after AC_PROG_LIBTOOL. -AC_DEFUN([AC_LIBLTDL_INSTALLABLE], -[AC_BEFORE([$0],[AC_LIBTOOL_SETUP])dnl - AC_CHECK_LIB(ltdl, lt_dlinit, - [test x"$enable_ltdl_install" != xyes && enable_ltdl_install=no], - [if test x"$enable_ltdl_install" = xno; then - AC_MSG_WARN([libltdl not installed, but installation disabled]) - else - enable_ltdl_install=yes - fi - ]) - if test x"$enable_ltdl_install" = x"yes"; then - ac_configure_args="$ac_configure_args --enable-ltdl-install" - LIBLTDL='${top_builddir}/'ifelse($#,1,[$1],['libltdl'])/libltdl.la - LTDLINCL='-I${top_srcdir}/'ifelse($#,1,[$1],['libltdl']) - else - ac_configure_args="$ac_configure_args --enable-ltdl-install=no" - LIBLTDL="-lltdl" - LTDLINCL= - fi - # For backwards non-gettext consistent compatibility... - INCLTDL="$LTDLINCL" -])# AC_LIBLTDL_INSTALLABLE +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi +_LT_DECL([], [variables_saved_for_relink], [1], + [Variables whose values should be saved in libtool wrapper scripts and + restored at link time]) +_LT_DECL([], [need_lib_prefix], [0], + [Do we need the "lib" prefix for modules?]) +_LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) +_LT_DECL([], [version_type], [0], [Library versioning type]) +_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) +_LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) +_LT_DECL([], [shlibpath_overrides_runpath], [0], + [Is shlibpath searched before the hard-coded library search path?]) +_LT_DECL([], [libname_spec], [1], [Format of library name prefix]) +_LT_DECL([], [library_names_spec], [1], + [[List of archive names. First name is the real one, the rest are links. + The last name is the one that the linker finds with -lNAME]]) +_LT_DECL([], [soname_spec], [1], + [[The coded name of the library, if different from the real name]]) +_LT_DECL([], [postinstall_cmds], [2], + [Command to use after installation of a shared archive]) +_LT_DECL([], [postuninstall_cmds], [2], + [Command to use after uninstallation of a shared archive]) +_LT_DECL([], [finish_cmds], [2], + [Commands used to finish a libtool library installation in a directory]) +_LT_DECL([], [finish_eval], [1], + [[As "finish_cmds", except a single script fragment to be evaled but + not shown]]) +_LT_DECL([], [hardcode_into_libs], [0], + [Whether we should hardcode library paths into libraries]) +_LT_DECL([], [sys_lib_search_path_spec], [2], + [Compile-time system search path for libraries]) +_LT_DECL([], [sys_lib_dlsearch_path_spec], [2], + [Run-time system search path for libraries]) +])# _LT_SYS_DYNAMIC_LINKER -# AC_LIBTOOL_CXX -# -------------- -# enable support for C++ libraries -AC_DEFUN([AC_LIBTOOL_CXX], -[AC_REQUIRE([_LT_AC_LANG_CXX]) -])# AC_LIBTOOL_CXX +# _LT_PATH_TOOL_PREFIX(TOOL) +# -------------------------- +# find a file program which can recognize shared library +AC_DEFUN([_LT_PATH_TOOL_PREFIX], +[m4_require([_LT_DECL_EGREP])dnl +AC_MSG_CHECKING([for $1]) +AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, +[case $MAGIC_CMD in +[[\\/*] | ?:[\\/]*]) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR +dnl $ac_dummy forces splitting on constant user-supplied paths. +dnl POSIX.2 word splitting is done only on the output of word expansions, +dnl not every word. This closes a longstanding sh security hole. + ac_dummy="m4_if([$2], , $PATH, [$2])" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$1; then + lt_cv_path_MAGIC_CMD="$ac_dir/$1" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 -# _LT_AC_LANG_CXX -# --------------- -AC_DEFUN([_LT_AC_LANG_CXX], -[AC_REQUIRE([AC_PROG_CXX]) -AC_REQUIRE([_LT_AC_PROG_CXXCPP]) -_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}CXX]) -])# _LT_AC_LANG_CXX +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org -# _LT_AC_PROG_CXXCPP -# ------------------ -AC_DEFUN([_LT_AC_PROG_CXXCPP], -[ -AC_REQUIRE([AC_PROG_CXX]) -if test -n "$CXX" && ( test "X$CXX" != "Xno" && - ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || - (test "X$CXX" != "Xg++"))) ; then - AC_PROG_CXXCPP +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac]) +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + AC_MSG_RESULT($MAGIC_CMD) +else + AC_MSG_RESULT(no) fi -])# _LT_AC_PROG_CXXCPP - -# AC_LIBTOOL_F77 -# -------------- -# enable support for Fortran 77 libraries -AC_DEFUN([AC_LIBTOOL_F77], -[AC_REQUIRE([_LT_AC_LANG_F77]) -])# AC_LIBTOOL_F77 - +_LT_DECL([], [MAGIC_CMD], [0], + [Used to examine libraries when file_magic_cmd begins with "file"])dnl +])# _LT_PATH_TOOL_PREFIX -# _LT_AC_LANG_F77 -# --------------- -AC_DEFUN([_LT_AC_LANG_F77], -[AC_REQUIRE([AC_PROG_F77]) -_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}F77]) -])# _LT_AC_LANG_F77 +# Old name: +AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) -# AC_LIBTOOL_GCJ +# _LT_PATH_MAGIC # -------------- -# enable support for GCJ libraries -AC_DEFUN([AC_LIBTOOL_GCJ], -[AC_REQUIRE([_LT_AC_LANG_GCJ]) -])# AC_LIBTOOL_GCJ +# find a file program which can recognize a shared library +m4_defun([_LT_PATH_MAGIC], +[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) + else + MAGIC_CMD=: + fi +fi +])# _LT_PATH_MAGIC -# _LT_AC_LANG_GCJ -# --------------- -AC_DEFUN([_LT_AC_LANG_GCJ], -[AC_PROVIDE_IFELSE([AC_PROG_GCJ],[], - [AC_PROVIDE_IFELSE([A][M_PROG_GCJ],[], - [AC_PROVIDE_IFELSE([LT_AC_PROG_GCJ],[], - [ifdef([AC_PROG_GCJ],[AC_REQUIRE([AC_PROG_GCJ])], - [ifdef([A][M_PROG_GCJ],[AC_REQUIRE([A][M_PROG_GCJ])], - [AC_REQUIRE([A][C_PROG_GCJ_OR_A][M_PROG_GCJ])])])])])]) -_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}GCJ]) -])# _LT_AC_LANG_GCJ +# LT_PATH_LD +# ---------- +# find the pathname to the GNU or non-GNU linker +AC_DEFUN([LT_PATH_LD], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +AC_ARG_WITH([gnu-ld], + [AS_HELP_STRING([--with-gnu-ld], + [assume the C compiler uses GNU ld @<:@default=no@:>@])], + [test "$withval" = no || with_gnu_ld=yes], + [with_gnu_ld=no])dnl -# AC_LIBTOOL_RC -# ------------- -# enable support for Windows resource files -AC_DEFUN([AC_LIBTOOL_RC], -[AC_REQUIRE([LT_AC_PROG_RC]) -_LT_AC_SHELL_INIT([tagnames=${tagnames+${tagnames},}RC]) -])# AC_LIBTOOL_RC +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by $CC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]]* | ?:[[\\/]]*) + re_direlt='/[[^/]][[^/]]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(lt_cv_path_LD, +[if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' fi ;; -aix4* | aix5*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no +cegcc) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all fi - ;; -esac -AC_MSG_RESULT([$enable_shared]) + ;; -AC_MSG_CHECKING([whether to build static libraries]) -# Make sure either enable_shared or enable_static is yes. -test "$enable_shared" = yes || enable_static=yes -AC_MSG_RESULT([$enable_static]) +gnu*) + lt_cv_deplibs_check_method=pass_all + ;; -AC_LIBTOOL_CONFIG($1) +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'] + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; -AC_LANG_POP -CC="$lt_save_CC" -])# AC_LIBTOOL_LANG_C_CONFIG +interix[[3-9]]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' + ;; +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; -# AC_LIBTOOL_LANG_CXX_CONFIG -# -------------------------- -# Ensure that the configuration vars for the C compiler are -# suitably defined. Those variables are subsequently used by -# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. -AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG], [_LT_AC_LANG_CXX_CONFIG(CXX)]) -AC_DEFUN([_LT_AC_LANG_CXX_CONFIG], -[AC_LANG_PUSH(C++) -AC_REQUIRE([AC_PROG_CXX]) -AC_REQUIRE([_LT_AC_PROG_CXXCPP]) - -_LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no -_LT_AC_TAGVAR(allow_undefined_flag, $1)= -_LT_AC_TAGVAR(always_export_symbols, $1)=no -_LT_AC_TAGVAR(archive_expsym_cmds, $1)= -_LT_AC_TAGVAR(export_dynamic_flag_spec, $1)= -_LT_AC_TAGVAR(hardcode_direct, $1)=no -_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)= -_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= -_LT_AC_TAGVAR(hardcode_libdir_separator, $1)= -_LT_AC_TAGVAR(hardcode_minus_L, $1)=no -_LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported -_LT_AC_TAGVAR(hardcode_automatic, $1)=no -_LT_AC_TAGVAR(module_cmds, $1)= -_LT_AC_TAGVAR(module_expsym_cmds, $1)= -_LT_AC_TAGVAR(link_all_deplibs, $1)=unknown -_LT_AC_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds -_LT_AC_TAGVAR(no_undefined_flag, $1)= -_LT_AC_TAGVAR(whole_archive_flag_spec, $1)= -_LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=no +# This must be Linux ELF. +linux* | k*bsd*-gnu) + lt_cv_deplibs_check_method=pass_all + ;; -# Dependencies to place before and after the object being linked: -_LT_AC_TAGVAR(predep_objects, $1)= -_LT_AC_TAGVAR(postdep_objects, $1)= -_LT_AC_TAGVAR(predeps, $1)= -_LT_AC_TAGVAR(postdeps, $1)= -_LT_AC_TAGVAR(compiler_lib_search_path, $1)= +netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' + fi + ;; -# Source file extension for C++ test sources. -ac_ext=cpp +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; -# Object file extension for compiled C++ test sources. -objext=o -_LT_AC_TAGVAR(objext, $1)=$objext +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; -# Code to be used in simple compile tests -lt_simple_compile_test_code="int some_variable = 0;\n" +openbsd*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + fi + ;; -# Code to be used in simple link tests -lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }\n' +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; -# ltmain only uses $CC for tagged configurations so make sure $CC is set. -_LT_AC_SYS_COMPILER +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; -# save warnings/boilerplate of simple test code -_LT_COMPILER_BOILERPLATE -_LT_LINKER_BOILERPLATE +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +esac +]) +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + +_LT_DECL([], [deplibs_check_method], [1], + [Method to check whether dependent libraries are shared objects]) +_LT_DECL([], [file_magic_cmd], [1], + [Command to use when deplibs_check_method == "file_magic"]) +])# _LT_CHECK_MAGIC_METHOD -# Allow CC to be a program name with arguments. -lt_save_CC=$CC -lt_save_LD=$LD -lt_save_GCC=$GCC -GCC=$GXX -lt_save_with_gnu_ld=$with_gnu_ld -lt_save_path_LD=$lt_cv_path_LD -if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then - lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx -else - $as_unset lt_cv_prog_gnu_ld -fi -if test -n "${lt_cv_path_LDCXX+set}"; then - lt_cv_path_LD=$lt_cv_path_LDCXX -else - $as_unset lt_cv_path_LD -fi -test -z "${LDCXX+set}" || LD=$LDCXX -CC=${CXX-"c++"} -compiler=$CC -_LT_AC_TAGVAR(compiler, $1)=$CC -_LT_CC_BASENAME([$compiler]) -# We don't want -fno-exception wen compiling C++ code, so set the -# no_builtin_flag separately -if test "$GXX" = yes; then - _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' +# LT_PATH_NM +# ---------- +# find the pathname to a BSD- or MS-compatible name lister +AC_DEFUN([LT_PATH_NM], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM="$NM" +else + lt_nm_to_check="${ac_tool_prefix}nm" + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + tmp_nm="$ac_dir/$lt_tmp_nm" + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in + */dev/null* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS="$lt_save_ifs" + done + : ${lt_cv_path_NM=no} +fi]) +if test "$lt_cv_path_NM" != "no"; then + NM="$lt_cv_path_NM" else - _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + # Didn't find any BSD compatible name lister, look for dumpbin. + AC_CHECK_TOOLS(DUMPBIN, ["dumpbin -symbols" "link -dump -symbols"], :) + AC_SUBST([DUMPBIN]) + if test "$DUMPBIN" != ":"; then + NM="$DUMPBIN" + fi fi +test -z "$NM" && NM=nm +AC_SUBST([NM]) +_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl -if test "$GXX" = yes; then - # Set up default GNU C++ configuration +AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], + [lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:__oline__: $ac_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:__oline__: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:__oline__: output\"" >&AS_MESSAGE_LOG_FD) + cat conftest.out >&AS_MESSAGE_LOG_FD + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest*]) +])# LT_PATH_NM + +# Old names: +AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) +AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_PROG_NM], []) +dnl AC_DEFUN([AC_PROG_NM], []) - AC_PROG_LD - # Check if GNU C++ uses GNU ld as the underlying linker, since the - # archiving commands below assume that GNU ld is being used. - if test "$with_gnu_ld" = yes; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' +# LT_LIB_M +# -------- +# check for math library +AC_DEFUN([LT_LIB_M], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +LIBM= +case $host in +*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*) + # These system don't have libm, or don't need it + ;; +*-ncr-sysv4.3*) + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") + AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") + ;; +*) + AC_CHECK_LIB(m, cos, LIBM="-lm") + ;; +esac +AC_SUBST([LIBM]) +])# LT_LIB_M - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' +# Old name: +AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_CHECK_LIBM], []) - # If archive_cmds runs LD, not CC, wlarc should be empty - # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to - # investigate it a little bit more. (MM) - wlarc='${wl}' - # ancient GNU ld didn't support --whole-archive et. al. - if eval "`$CC -print-prog-name=ld` --help 2>&1" | \ - grep 'no-whole-archive' > /dev/null; then - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)= - fi - else - with_gnu_ld=no - wlarc= +# _LT_COMPILER_NO_RTTI([TAGNAME]) +# ------------------------------- +m4_defun([_LT_COMPILER_NO_RTTI], +[m4_require([_LT_TAG_COMPILER])dnl - # A generic and very simple default shared library creation - # command for GNU C++ for the case where it uses the native - # linker, instead of GNU ld. If possible, this setting should - # overridden to take advantage of the native linker features on - # the platform it is being used on. - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - fi +_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' +if test "$GCC" = yes; then + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' -else - GXX=no - with_gnu_ld=no - wlarc= + _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], + lt_cv_prog_compiler_rtti_exceptions, + [-fno-rtti -fno-exceptions], [], + [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) fi +_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], + [Compiler flag to turn off builtin functions]) +])# _LT_COMPILER_NO_RTTI -# PORTME: fill in a description of your system's C++ link characteristics -AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) -_LT_AC_TAGVAR(ld_shlibs, $1)=yes -case $host_os in - aix3*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - aix4* | aix5*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - aix_use_runtimelinking=no - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[[23]]|aix4.[[23]].*|aix5*) - for ld_flag in $LDFLAGS; do - case $ld_flag in - *-brtl*) - aix_use_runtimelinking=yes - break - ;; - esac - done - ;; - esac +# _LT_CMD_GLOBAL_SYMBOLS +# ---------------------- +m4_defun([_LT_CMD_GLOBAL_SYMBOLS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([LT_PATH_NM])dnl +AC_REQUIRE([LT_PATH_LD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_TAG_COMPILER])dnl - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi +# Check for command to grab the raw symbol name followed by C symbol from nm. +AC_MSG_CHECKING([command to parse $NM output from $compiler object]) +AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], +[ +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - _LT_AC_TAGVAR(archive_cmds, $1)='' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':' - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes +# Character class describing NM global symbol codes. +symcode='[[BCDEGRST]]' - if test "$GXX" = yes; then - case $host_os in aix4.[[012]]|aix4.[[012]].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && \ - strings "$collect2name" | grep resolve_lib_name >/dev/null - then - # We have reworked collect2 - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - else - # We have old collect2 - _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)= - fi - ;; - esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi +# Regexp to match symbols that can be accessed directly from C. +sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to export. - _LT_AC_TAGVAR(always_export_symbols, $1)=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - _LT_AC_TAGVAR(allow_undefined_flag, $1)='-berok' - # Determine the default libpath from the value encoded in an empty executable. - _LT_AC_SYS_LIBPATH_AIX - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" +# Define system-specific variables. +case $host_os in +aix*) + symcode='[[BCDT]]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[[ABCDGISTW]]' + ;; +hpux*) + if test "$host_cpu" = ia64; then + symcode='[[ABCDEGRST]]' + fi + ;; +irix* | nonstopux*) + symcode='[[BCDEGRST]]' + ;; +osf*) + symcode='[[BCDEGQRST]]' + ;; +solaris*) + symcode='[[BDRT]]' + ;; +sco3.2v5*) + symcode='[[DT]]' + ;; +sysv4.2uw2*) + symcode='[[DT]]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[[ABDT]]' + ;; +sysv4) + symcode='[[DFNSTU]]' + ;; +esac - _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' - _LT_AC_TAGVAR(allow_undefined_flag, $1)="-z nodefs" - _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an empty executable. - _LT_AC_SYS_LIBPATH_AIX - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' - # Exported symbols can be pulled into shared objects from archives - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='$convenience' - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes - # This is similar to how AIX traditionally builds its shared libraries. - _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' - fi - fi - ;; +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[[ABCDGIRSTW]]' ;; +esac - beos*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" - chorus*) - case $cc_basename in - *) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'" +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'" - cygwin* | mingw* | pw32*) - # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, - # as there is no search path for DLLs. - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_AC_TAGVAR(always_export_symbols, $1)=no - _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - - if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; - darwin* | rhapsody*) - case $host_os in - rhapsody* | darwin1.[[012]]) - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-undefined ${wl}suppress' - ;; - *) # Darwin 1.3 on - if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - else - case ${MACOSX_DEPLOYMENT_TARGET} in - 10.[[012]]) - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - ;; - 10.*) - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-undefined ${wl}dynamic_lookup' - ;; - esac - fi - ;; - esac - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_AC_TAGVAR(hardcode_direct, $1)=no - _LT_AC_TAGVAR(hardcode_automatic, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='' - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes - - if test "$GXX" = yes ; then - lt_int_apple_cc_single_mod=no - output_verbose_link_cmd='echo' - if $CC -dumpspecs 2>&1 | $EGREP 'single_module' >/dev/null ; then - lt_int_apple_cc_single_mod=yes - fi - if test "X$lt_int_apple_cc_single_mod" = Xyes ; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' - else - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -r -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring' - fi - _LT_AC_TAGVAR(module_cmds, $1)='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - if test "X$lt_int_apple_cc_single_mod" = Xyes ; then - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - else - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - fi - _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - else - case $cc_basename in - xlc*) - output_verbose_link_cmd='echo' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj ${wl}-single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}`echo $rpath/$soname` $verstring' - _LT_AC_TAGVAR(module_cmds, $1)='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -qmkshrobj ${wl}-single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}$rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - ;; - *) - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - esac - fi - ;; +esac - dgux*) - case $cc_basename in - ec++*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - ghcx*) - # Green Hills C++ Compiler - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - *) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - esac - ;; - freebsd[[12]]*) - # C++ shared libraries reported to be fairly broken before switch to ELF - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - freebsd-elf*) - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - ;; - freebsd* | kfreebsd*-gnu | dragonfly*) - # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF - # conventions - _LT_AC_TAGVAR(ld_shlibs, $1)=yes - ;; - gnu*) - ;; - hpux9*) - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, - # but as the default - # location of the library. - - case $cc_basename in - CC*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - aCC*) - _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "[[-]]L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - ;; - *) - if test "$GXX" = yes; then - _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function + # and D for any global variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK ['"\ +" {last_section=section; section=\$ 3};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ +" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ +" s[1]~/^[@?]/{print s[1], s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx]" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if AC_TRY_EVAL(ac_compile); then + # Now try to grab the symbols. + nlist=conftest.nm + if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +const struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[[]] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_save_LIBS="$LIBS" + lt_save_CFLAGS="$CFLAGS" + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS="$lt_save_LIBS" + CFLAGS="$lt_save_CFLAGS" + else + echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD + fi + else + echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done +]) +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + AC_MSG_RESULT(failed) +else + AC_MSG_RESULT(ok) +fi + +_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], + [Take the output of nm and produce a listing of raw symbols and C names]) +_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], + [Transform the output of nm in a proper C declaration]) +_LT_DECL([global_symbol_to_c_name_address], + [lt_cv_sys_global_symbol_to_c_name_address], [1], + [Transform the output of nm in a C name address pair]) +_LT_DECL([global_symbol_to_c_name_address_lib_prefix], + [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], + [Transform the output of nm in a C name address pair when lib prefix is needed]) +]) # _LT_CMD_GLOBAL_SYMBOLS + + +# _LT_COMPILER_PIC([TAGNAME]) +# --------------------------- +m4_defun([_LT_COMPILER_PIC], +[m4_require([_LT_TAG_COMPILER])dnl +_LT_TAGVAR(lt_prog_compiler_wl, $1)= +_LT_TAGVAR(lt_prog_compiler_pic, $1)= +_LT_TAGVAR(lt_prog_compiler_static, $1)= + +AC_MSG_CHECKING([for $compiler option to produce PIC]) +m4_if([$1], [CXX], [ + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' fi ;; - esac - ;; - hpux10*|hpux11*) - if test $with_gnu_ld = no; then - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: + amigaos*) case $host_cpu in - hppa*64*|ia64*) - _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; - *) - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' ;; esac - fi - case $host_cpu in - hppa*64*|ia64*) - _LT_AC_TAGVAR(hardcode_direct, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' ;; *) - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, - # but as the default - # location of the library. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; esac - - case $cc_basename in - CC*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no + else + case $host_os in + aix[[4-9]]*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi ;; - aCC*) - case $host_cpu in - hppa*64*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - ia64*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - *) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" ;; esac - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' ;; - *) - if test "$GXX" = yes; then - if test $with_gnu_ld = no; then + dgux*) + case $cc_basename in + ec++*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + if test "$host_cpu" != ia64; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + fi + ;; + aCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' case $host_cpu in - hppa*64*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - ia64*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + hppa*64*|ia64*) + # +Z the default ;; *) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; esac - fi - else - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - ;; - interix3*) - _LT_AC_TAGVAR(hardcode_direct, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - irix5* | irix6*) - case $cc_basename in - CC*) - # SGI C++ - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - - # Archives containing C++ object files must be created using - # "CC -ar", where "CC" is the IRIX C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + ;; + esac ;; - *) - if test "$GXX" = yes; then - if test "$with_gnu_ld" = no; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` -o $lib' - fi - fi - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? ;; - esac - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - ;; - linux*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - _LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | grep "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath,$libdir' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - - # Archives containing C++ object files must be created using - # "CC -Bstatic", where "CC" is the KAI C++ compiler. - _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' - ;; - icpc*) - # Intel C++ - with_gnu_ld=yes - # version 8.0 and above of icpc choke on multiply defined symbols - # if we add $predep_objects and $postdep_objects, however 7.1 and - # earlier do not add the objects themselves. - case `$CC -V 2>&1` in - *"Version 7."*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - ;; - *) # Version 8.0 or newer - tmp_idyn= - case $host_cpu in - ia64*) tmp_idyn=' -i_dynamic';; - esac - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; esac - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' - ;; - pgCC*) - # Portland Group C++ compiler - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - ;; - cxx*) - # Compaq C++ - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' - - runpath_var=LD_RUN_PATH - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' ;; - esac - ;; - lynxos*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - m88k*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - mvs*) - case $cc_basename in - cxx*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no + linux* | k*bsd*-gnu) + case $cc_basename in + KCC*) + # KAI C++ Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64 which still supported -KPIC. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xlc* | xlC*) + # IBM XL 8.0 on PPC + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + esac + ;; + esac ;; - *) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no + lynxos*) ;; - esac - ;; - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' - wlarc= - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - fi - # Workaround some broken pre-1.5 toolchains - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' - ;; - openbsd2*) - # C++ shared libraries are fairly broken - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - openbsd*) - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - fi - output_verbose_link_cmd='echo' - ;; - osf3*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - _LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - # Archives containing C++ object files must be created using - # "CC -Bstatic", where "CC" is the KAI C++ compiler. - _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' - - ;; - RCC*) - # Rational C++ 2.4.1 - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - cxx*) - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && echo ${wl}-set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + m88k*) ;; - *) - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' - - else - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi + mvs*) + case $cc_basename in + cxx*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' + ;; + *) + ;; + esac ;; - esac - ;; - osf4* | osf5*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - _LT_AC_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - # Archives containing C++ object files must be created using - # the KAI C++ compiler. - _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' - ;; - RCC*) - # Rational C++ 2.4.1 - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - cxx*) - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ - echo "-hidden">> $lib.exp~ - $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname -Wl,-input -Wl,$lib.exp `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib~ - $rm $lib.exp' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' + netbsd*) ;; - *) - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' - - else - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + cxx*) + # Digital/Compaq C++ + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + *) + ;; + esac ;; - esac - ;; - psos*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - sunos4*) - case $cc_basename in - CC*) - # Sun C++ 4.x - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - lcc*) - # Lucid - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no + psos*) ;; - *) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no + solaris*) + case $cc_basename in + CC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + ;; + *) + ;; + esac ;; - esac - ;; - solaris*) - case $cc_basename in - CC*) - # Sun C++ 4.2, 5.x and Centerline C++ - _LT_AC_TAGVAR(archive_cmds_need_lc,$1)=yes - _LT_AC_TAGVAR(no_undefined_flag, $1)=' -zdefs' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' - - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - case $host_os in - solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + lcc*) + # Lucid + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; *) - # The C++ compiler is used as linker so we must use $wl - # flag to pass the commands to the underlying system - # linker. We must also pass each convience library through - # to the system linker between allextract/defaultextract. - # The C++ compiler will combine linker options so we - # cannot just pass the convience library names through - # without $wl. - # Supported since Solaris 2.6 (maybe 2.5.1?) - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}-z ${wl}defaultextract' ;; esac - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes + ;; + vxworks*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +], +[ + if test "$GCC" = yes; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' - output_verbose_link_cmd='echo' + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; - # Archives containing C++ object files must be created using - # "CC -xar", where "CC" is the Sun C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' - ;; - gcx*) - # Green Hills C++ Compiler - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; - # The C++ compiler must be used to create the archive. - _LT_AC_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' - ;; - *) - # GNU C++ compiler with Solaris linker - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' - if $CC --version | grep -v '^2\.7' > /dev/null; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd="$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\"" - else - # g++ 2.7 appears to require `-G' NOT `-shared' on this - # platform. - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd="$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\"" - fi + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' - fi - ;; - esac - ;; - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) - _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - runpath_var='LD_RUN_PATH' - - case $cc_basename in - CC*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default ;; *) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' ;; + esac + ;; + + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; esac - ;; - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - # For security reasons, it is highly recommended that you always - # use absolute paths for naming shared libraries, and exclude the - # DT_RUNPATH tag from executables and libraries. But doing so - # requires that you compile everything twice, which is a pain. - # So that behaviour is only enabled if SCOABSPATH is set to a - # non-empty value in the environment. Most likely only useful for - # creating official distributions of packages. - # This is a hack until libtool officially supports absolute path - # names for shared libraries. - _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':' - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' - runpath_var='LD_RUN_PATH' - - case $cc_basename in - CC*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + + hpux9* | hpux10* | hpux11*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default ;; *) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' ;; - esac - ;; - tandem*) - case $cc_basename in - NCC*) - # NonStop-UX NCC 3.20 - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC (with -KPIC) is the default. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + linux* | k*bsd*-gnu) + case $cc_basename in + # old Intel for x86_64 which still supported -KPIC. + ecc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' + _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' + ;; + pgcc* | pgf77* | pgf90* | pgf95*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + ccc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All Alpha code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xl*) + # IBM XL C 8.0/Fortran 10.1 on PPC + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' ;; *) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + ;; + *Sun\ F*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='' + ;; + esac ;; + esac + ;; + + newsos6) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All OSF/1 code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + rdos*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + solaris*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + case $cc_basename in + f77* | f90* | f95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; + *) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; + esac + ;; + + sunos4*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + unicos*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + + uts4*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; esac - ;; - vxworks*) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no + fi +]) +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)= ;; *) - # FIXME: insert proper C++ library support - _LT_AC_TAGVAR(ld_shlibs, $1)=no + _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" ;; esac -AC_MSG_RESULT([$_LT_AC_TAGVAR(ld_shlibs, $1)]) -test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no +AC_MSG_RESULT([$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) +_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], + [How to pass a linker flag through the compiler]) -_LT_AC_TAGVAR(GCC, $1)="$GXX" -_LT_AC_TAGVAR(LD, $1)="$LD" - -AC_LIBTOOL_POSTDEP_PREDEP($1) -AC_LIBTOOL_PROG_COMPILER_PIC($1) -AC_LIBTOOL_PROG_CC_C_O($1) -AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1) -AC_LIBTOOL_PROG_LD_SHLIBS($1) -AC_LIBTOOL_SYS_DYNAMIC_LINKER($1) -AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1) +# +# Check to make sure the PIC flag actually works. +# +if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], + [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], + [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], + [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in + "" | " "*) ;; + *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; + esac], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) +fi +_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], + [Additional compiler flags for building library objects]) -AC_LIBTOOL_CONFIG($1) +# +# Check to make sure the static flag actually works. +# +wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" +_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], + _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), + $lt_tmp_static_flag, + [], + [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) +_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], + [Compiler flag to prevent dynamic linking]) +])# _LT_COMPILER_PIC -AC_LANG_POP -CC=$lt_save_CC -LDCXX=$LD -LD=$lt_save_LD -GCC=$lt_save_GCC -with_gnu_ldcxx=$with_gnu_ld -with_gnu_ld=$lt_save_with_gnu_ld -lt_cv_path_LDCXX=$lt_cv_path_LD -lt_cv_path_LD=$lt_save_path_LD -lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld -lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld -])# AC_LIBTOOL_LANG_CXX_CONFIG -# AC_LIBTOOL_POSTDEP_PREDEP([TAGNAME]) -# ------------------------------------ -# Figure out "hidden" library dependencies from verbose -# compiler output when linking a shared library. -# Parse the compiler output and extract the necessary -# objects, libraries and library flags. -AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP],[ -dnl we can't use the lt_simple_compile_test_code here, -dnl because it contains code intended for an executable, -dnl not a library. It's possible we should let each -dnl tag define a new lt_????_link_test_code variable, -dnl but it's only used here... -ifelse([$1],[],[cat > conftest.$ac_ext < conftest.$ac_ext < conftest.$ac_ext < conftest.$ac_ext < $export_symbols' + case $host_os in + aix[[4-9]]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" + ;; + cygwin* | mingw* | cegcc*) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;/^.*[[ ]]__nm__/s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] +], [ + runpath_var= + _LT_TAGVAR(allow_undefined_flag, $1)= + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(archive_cmds, $1)= + _LT_TAGVAR(archive_expsym_cmds, $1)= + _LT_TAGVAR(compiler_needs_object, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(hardcode_automatic, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= + _LT_TAGVAR(hardcode_libdir_separator, $1)= + _LT_TAGVAR(hardcode_minus_L, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_TAGVAR(inherit_rpath, $1)=no + _LT_TAGVAR(link_all_deplibs, $1)=unknown + _LT_TAGVAR(module_cmds, $1)= + _LT_TAGVAR(module_expsym_cmds, $1)= + _LT_TAGVAR(old_archive_from_new_cmds, $1)= + _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= + _LT_TAGVAR(thread_safe_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + _LT_TAGVAR(include_expsyms, $1)= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. +dnl Note also adjust exclude_expsyms for C++ above. + extract_expsyms_cmds= - # Sentinel used to keep track of whether or not we are before - # the conftest object file. - pre_test_object_deps_done=no + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; + esac - # The `*' in the case matches for architectures that use `case' in - # $output_verbose_cmd can trigger glob expansion during the loop - # eval without this substitution. - output_verbose_link_cmd=`$echo "X$output_verbose_link_cmd" | $Xsed -e "$no_glob_subst"` + _LT_TAGVAR(ld_shlibs, $1)=yes + if test "$with_gnu_ld" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' - for p in `eval $output_verbose_link_cmd`; do - case $p in + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + supports_anon_versioning=no + case `$LD -v 2>&1` in + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac - -L* | -R* | -l*) - # Some compilers place space between "-{L,R}" and the path. - # Remove the space. - if test $p = "-L" \ - || test $p = "-R"; then - prev=$p - continue - else - prev= - fi + # See if GNU ld supports shared libraries. + case $host_os in + aix[[3-9]]*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 - if test "$pre_test_object_deps_done" = no; then - case $p in - -L* | -R*) - # Internal compiler library paths should come after those - # provided the user. The postdeps already come after the - # user supplied libs so there is no need to process them. - if test -z "$_LT_AC_TAGVAR(compiler_lib_search_path, $1)"; then - _LT_AC_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" - else - _LT_AC_TAGVAR(compiler_lib_search_path, $1)="${_LT_AC_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" - fi - ;; - # The "-l" case would never come before the object being - # linked, so don't bother handling this case. - esac - else - if test -z "$_LT_AC_TAGVAR(postdeps, $1)"; then - _LT_AC_TAGVAR(postdeps, $1)="${prev}${p}" - else - _LT_AC_TAGVAR(postdeps, $1)="${_LT_AC_TAGVAR(postdeps, $1)} ${prev}${p}" - fi - fi - ;; +*** Warning: the GNU linker, at least up to release 2.9.1, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to modify your PATH +*** so that a non-GNU linker is found, and then restart. - *.$objext) - # This assumes that the test object file only shows up - # once in the compiler output. - if test "$p" = "conftest.$objext"; then - pre_test_object_deps_done=yes - continue - fi +_LT_EOF + fi + ;; - if test "$pre_test_object_deps_done" = no; then - if test -z "$_LT_AC_TAGVAR(predep_objects, $1)"; then - _LT_AC_TAGVAR(predep_objects, $1)="$p" - else - _LT_AC_TAGVAR(predep_objects, $1)="$_LT_AC_TAGVAR(predep_objects, $1) $p" - fi - else - if test -z "$_LT_AC_TAGVAR(postdep_objects, $1)"; then - _LT_AC_TAGVAR(postdep_objects, $1)="$p" - else - _LT_AC_TAGVAR(postdep_objects, $1)="$_LT_AC_TAGVAR(postdep_objects, $1) $p" - fi - fi - ;; + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; - *) ;; # Ignore the rest. + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; - esac - done + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' - # Clean up. - rm -f a.out a.exe -else - echo "libtool.m4: error: problem compiling $1 test program" -fi + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; -$rm -f confest.$objext + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; -# PORTME: override above test on systems where it is broken -ifelse([$1],[CXX], -[case $host_os in -interix3*) - # Interix 3.5 installs completely hosed .la files for C++, so rather than - # hack all around it, let's just trust "g++" to DTRT. - _LT_AC_TAGVAR(predep_objects,$1)= - _LT_AC_TAGVAR(postdep_objects,$1)= - _LT_AC_TAGVAR(postdeps,$1)= - ;; + gnu* | linux* | tpf* | k*bsd*-gnu) + tmp_diet=no + if test "$host_os" = linux-dietlibc; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test "$tmp_diet" = no + then + tmp_addflag= + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + _LT_TAGVAR(whole_archive_flag_spec, $1)= + tmp_sharedflag='--shared' ;; + xl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' -solaris*) - case $cc_basename in - CC*) - # Adding this requires a known-good setup of shared libraries for - # Sun compiler versions before 5.6, else PIC objects from an old - # archive will be linked into the output, leading to subtle bugs. - _LT_AC_TAGVAR(postdeps,$1)='-lCstd -lCrun' - ;; - esac - ;; -esac -]) + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi -case " $_LT_AC_TAGVAR(postdeps, $1) " in -*" -lc "*) _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no ;; -esac -])# AC_LIBTOOL_POSTDEP_PREDEP + case $cc_basename in + xlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir' + _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; -# AC_LIBTOOL_LANG_F77_CONFIG -# -------------------------- -# Ensure that the configuration vars for the C compiler are -# suitably defined. Those variables are subsequently used by -# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. -AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG], [_LT_AC_LANG_F77_CONFIG(F77)]) -AC_DEFUN([_LT_AC_LANG_F77_CONFIG], -[AC_REQUIRE([AC_PROG_F77]) -AC_LANG_PUSH(Fortran 77) + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; -_LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no -_LT_AC_TAGVAR(allow_undefined_flag, $1)= -_LT_AC_TAGVAR(always_export_symbols, $1)=no -_LT_AC_TAGVAR(archive_expsym_cmds, $1)= -_LT_AC_TAGVAR(export_dynamic_flag_spec, $1)= -_LT_AC_TAGVAR(hardcode_direct, $1)=no -_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)= -_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= -_LT_AC_TAGVAR(hardcode_libdir_separator, $1)= -_LT_AC_TAGVAR(hardcode_minus_L, $1)=no -_LT_AC_TAGVAR(hardcode_automatic, $1)=no -_LT_AC_TAGVAR(module_cmds, $1)= -_LT_AC_TAGVAR(module_expsym_cmds, $1)= -_LT_AC_TAGVAR(link_all_deplibs, $1)=unknown -_LT_AC_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds -_LT_AC_TAGVAR(no_undefined_flag, $1)= -_LT_AC_TAGVAR(whole_archive_flag_spec, $1)= -_LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=no + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 -# Source file extension for f77 test sources. -ac_ext=f +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. -# Object file extension for compiled f77 test sources. -objext=o -_LT_AC_TAGVAR(objext, $1)=$objext +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; -# Code to be used in simple compile tests -lt_simple_compile_test_code=" subroutine t\n return\n end\n" + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 -# Code to be used in simple link tests -lt_simple_link_test_code=" program t\n end\n" +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. -# ltmain only uses $CC for tagged configurations so make sure $CC is set. -_LT_AC_SYS_COMPILER +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; -# save warnings/boilerplate of simple test code -_LT_COMPILER_BOILERPLATE -_LT_LINKER_BOILERPLATE + sunos4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; -# Allow CC to be a program name with arguments. -lt_save_CC="$CC" -CC=${F77-"f77"} -compiler=$CC -_LT_AC_TAGVAR(compiler, $1)=$CC -_LT_CC_BASENAME([$compiler]) + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac -AC_MSG_CHECKING([if libtool supports shared libraries]) -AC_MSG_RESULT([$can_build_shared]) + if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then + runpath_var= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + _LT_TAGVAR(hardcode_direct, $1)=unsupported + fi + ;; -AC_MSG_CHECKING([whether to build shared libraries]) -test "$can_build_shared" = "no" && enable_shared=no + aix[[4-9]]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no -# On AIX, shared libraries and static libraries use the same namespace, and -# are all built from PIC. -case $host_os in -aix3*) - test "$enable_shared" = yes && enable_static=no - if test -n "$RANLIB"; then - archive_cmds="$archive_cmds~\$RANLIB \$lib" - postinstall_cmds='$RANLIB $lib' - fi - ;; -aix4* | aix5*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no - fi - ;; -esac -AC_MSG_RESULT([$enable_shared]) + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac -AC_MSG_CHECKING([whether to build static libraries]) -# Make sure either enable_shared or enable_static is yes. -test "$enable_shared" = yes || enable_static=yes -AC_MSG_RESULT([$enable_static]) - -_LT_AC_TAGVAR(GCC, $1)="$G77" -_LT_AC_TAGVAR(LD, $1)="$LD" - -AC_LIBTOOL_PROG_COMPILER_PIC($1) -AC_LIBTOOL_PROG_CC_C_O($1) -AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1) -AC_LIBTOOL_PROG_LD_SHLIBS($1) -AC_LIBTOOL_SYS_DYNAMIC_LINKER($1) -AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1) + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi -AC_LIBTOOL_CONFIG($1) + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. -AC_LANG_POP -CC="$lt_save_CC" -])# AC_LIBTOOL_LANG_F77_CONFIG + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' + if test "$GCC" = yes; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi -# AC_LIBTOOL_LANG_GCJ_CONFIG -# -------------------------- -# Ensure that the configuration vars for the C compiler are -# suitably defined. Those variables are subsequently used by -# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. -AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG], [_LT_AC_LANG_GCJ_CONFIG(GCJ)]) -AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG], -[AC_LANG_SAVE + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds its shared libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; -# Source file extension for Java test sources. -ac_ext=java + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; -# Object file extension for compiled Java test sources. -objext=o -_LT_AC_TAGVAR(objext, $1)=$objext + bsdi[[45]]*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic + ;; -# Code to be used in simple compile tests -lt_simple_compile_test_code="class foo {}\n" + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + # FIXME: Should let the user specify the lib program. + _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' + _LT_TAGVAR(fix_srcfile_path, $1)='`cygpath -w "$srcfile"`' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; -# Code to be used in simple link tests -lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }\n' + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; -# ltmain only uses $CC for tagged configurations so make sure $CC is set. -_LT_AC_SYS_COMPILER + dgux*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; -# save warnings/boilerplate of simple test code -_LT_COMPILER_BOILERPLATE -_LT_LINKER_BOILERPLATE + freebsd1*) + _LT_TAGVAR(ld_shlibs, $1)=no + ;; -# Allow CC to be a program name with arguments. -lt_save_CC="$CC" -CC=${GCJ-"gcj"} -compiler=$CC -_LT_AC_TAGVAR(compiler, $1)=$CC -_LT_CC_BASENAME([$compiler]) + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; -# GCJ did not exist at the time GCC didn't implicitly link libc in. -_LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; -_LT_AC_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; -AC_LIBTOOL_PROG_COMPILER_NO_RTTI($1) -AC_LIBTOOL_PROG_COMPILER_PIC($1) -AC_LIBTOOL_PROG_CC_C_O($1) -AC_LIBTOOL_SYS_HARD_LINK_LOCKS($1) -AC_LIBTOOL_PROG_LD_SHLIBS($1) -AC_LIBTOOL_SYS_DYNAMIC_LINKER($1) -AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH($1) + hpux9*) + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes -AC_LIBTOOL_CONFIG($1) + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; -AC_LANG_RESTORE -CC="$lt_save_CC" -])# AC_LIBTOOL_LANG_GCJ_CONFIG + hpux10*) + if test "$GCC" = yes -a "$with_gnu_ld" = no; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + fi + ;; + hpux11*) + if test "$GCC" = yes -a "$with_gnu_ld" = no; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + fi + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: -# AC_LIBTOOL_LANG_RC_CONFIG -# ------------------------- -# Ensure that the configuration vars for the Windows resource compiler are -# suitably defined. Those variables are subsequently used by -# AC_LIBTOOL_CONFIG to write the compiler configuration to `libtool'. -AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG], [_LT_AC_LANG_RC_CONFIG(RC)]) -AC_DEFUN([_LT_AC_LANG_RC_CONFIG], -[AC_LANG_SAVE + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' -# Source file extension for RC test sources. -ac_ext=rc + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + fi + ;; -# Object file extension for compiled RC test sources. -objext=o -_LT_AC_TAGVAR(objext, $1)=$objext + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" + AC_LINK_IFELSE(int foo(void) {}, + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' + ) + LDFLAGS="$save_LDFLAGS" + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; -# Code to be used in simple compile tests -lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }\n' + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; -# Code to be used in simple link tests -lt_simple_link_test_code="$lt_simple_compile_test_code" + newsos6) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; -# ltmain only uses $CC for tagged configurations so make sure $CC is set. -_LT_AC_SYS_COMPILER + *nto* | *qnx*) + ;; -# save warnings/boilerplate of simple test code -_LT_COMPILER_BOILERPLATE -_LT_LINKER_BOILERPLATE + openbsd*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + else + case $host_os in + openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + ;; + esac + fi + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; -# Allow CC to be a program name with arguments. -lt_save_CC="$CC" -CC=${RC-"windres"} -compiler=$CC -_LT_AC_TAGVAR(compiler, $1)=$CC -_LT_CC_BASENAME([$compiler]) -_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; -AC_LIBTOOL_CONFIG($1) + osf3*) + if test "$GCC" = yes; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; -AC_LANG_RESTORE -CC="$lt_save_CC" -])# AC_LIBTOOL_LANG_RC_CONFIG + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' + # Both c and cxx compiler support -rpath directly + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; -# AC_LIBTOOL_CONFIG([TAGNAME]) -# ---------------------------- -# If TAGNAME is not passed, then create an initial libtool script -# with a default configuration from the untagged config vars. Otherwise -# add code to config.status for appending the configuration named by -# TAGNAME from the matching tagged config vars. -AC_DEFUN([AC_LIBTOOL_CONFIG], -[# The else clause should only fire when bootstrapping the -# libtool distribution, otherwise you forgot to ship ltmain.sh -# with your package, and you will get complaints that there are -# no rules to generate ltmain.sh. -if test -f "$ltmain"; then - # See if we are running on zsh, and set the options which allow our commands through - # without removal of \ escapes. - if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST - fi - # Now quote all the things that may contain metacharacters while being - # careful not to overquote the AC_SUBSTed values. We take copies of the - # variables and quote the copies for generation of the libtool script. - for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC LTCFLAGS NM \ - SED SHELL STRIP \ - libname_spec library_names_spec soname_spec extract_expsyms_cmds \ - old_striplib striplib file_magic_cmd finish_cmds finish_eval \ - deplibs_check_method reload_flag reload_cmds need_locks \ - lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \ - lt_cv_sys_global_symbol_to_c_name_address \ - sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ - old_postinstall_cmds old_postuninstall_cmds \ - _LT_AC_TAGVAR(compiler, $1) \ - _LT_AC_TAGVAR(CC, $1) \ - _LT_AC_TAGVAR(LD, $1) \ - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1) \ - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1) \ - _LT_AC_TAGVAR(lt_prog_compiler_static, $1) \ - _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) \ - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1) \ - _LT_AC_TAGVAR(thread_safe_flag_spec, $1) \ - _LT_AC_TAGVAR(whole_archive_flag_spec, $1) \ - _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1) \ - _LT_AC_TAGVAR(old_archive_cmds, $1) \ - _LT_AC_TAGVAR(old_archive_from_new_cmds, $1) \ - _LT_AC_TAGVAR(predep_objects, $1) \ - _LT_AC_TAGVAR(postdep_objects, $1) \ - _LT_AC_TAGVAR(predeps, $1) \ - _LT_AC_TAGVAR(postdeps, $1) \ - _LT_AC_TAGVAR(compiler_lib_search_path, $1) \ - _LT_AC_TAGVAR(archive_cmds, $1) \ - _LT_AC_TAGVAR(archive_expsym_cmds, $1) \ - _LT_AC_TAGVAR(postinstall_cmds, $1) \ - _LT_AC_TAGVAR(postuninstall_cmds, $1) \ - _LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1) \ - _LT_AC_TAGVAR(allow_undefined_flag, $1) \ - _LT_AC_TAGVAR(no_undefined_flag, $1) \ - _LT_AC_TAGVAR(export_symbols_cmds, $1) \ - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) \ - _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1) \ - _LT_AC_TAGVAR(hardcode_libdir_separator, $1) \ - _LT_AC_TAGVAR(hardcode_automatic, $1) \ - _LT_AC_TAGVAR(module_cmds, $1) \ - _LT_AC_TAGVAR(module_expsym_cmds, $1) \ - _LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1) \ - _LT_AC_TAGVAR(exclude_expsyms, $1) \ - _LT_AC_TAGVAR(include_expsyms, $1); do - - case $var in - _LT_AC_TAGVAR(old_archive_cmds, $1) | \ - _LT_AC_TAGVAR(old_archive_from_new_cmds, $1) | \ - _LT_AC_TAGVAR(archive_cmds, $1) | \ - _LT_AC_TAGVAR(archive_expsym_cmds, $1) | \ - _LT_AC_TAGVAR(module_cmds, $1) | \ - _LT_AC_TAGVAR(module_expsym_cmds, $1) | \ - _LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1) | \ - _LT_AC_TAGVAR(export_symbols_cmds, $1) | \ - extract_expsyms_cmds | reload_cmds | finish_cmds | \ - postinstall_cmds | postuninstall_cmds | \ - old_postinstall_cmds | old_postuninstall_cmds | \ - sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) - # Double-quote double-evaled strings. - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" + solaris*) + _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' + if test "$GCC" = yes; then + wlarc='${wl}' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='${wl}' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. GCC discards it without `$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test "$GCC" = yes; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + fi + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes ;; - *) - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no ;; - esac - done - case $lt_echo in - *'\[$]0 --fallback-echo"') - lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\[$]0 --fallback-echo"[$]/[$]0 --fallback-echo"/'` - ;; - esac + sysv4) + case $host_vendor in + sni) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' + _LT_TAGVAR(hardcode_direct, $1)=no + ;; + motorola) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; -ifelse([$1], [], - [cfgfile="${ofile}T" - trap "$rm \"$cfgfile\"; exit 1" 1 2 15 - $rm -f "$cfgfile" - AC_MSG_NOTICE([creating $ofile])], - [cfgfile="$ofile"]) + sysv4.3*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' + ;; - cat <<__EOF__ >> "$cfgfile" -ifelse([$1], [], -[#! $SHELL + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + _LT_TAGVAR(ld_shlibs, $1)=yes + fi + ;; -# `$echo "$cfgfile" | sed 's%^.*/%%'` - Provide generalized library-building support services. -# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP) -# NOTE: Changes made to this file will be lost: look at ltmain.sh. -# -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001 -# Free Software Foundation, Inc. -# -# This file is part of GNU Libtool: -# Originally by Gordon Matzigkeit , 1996 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# 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 the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' -# A sed program that does not truncate output. -SED=$lt_SED + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; -# Sed that helps us avoid accidentally triggering echo(1) options like -n. -Xsed="$SED -e 1s/^X//" + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var='LD_RUN_PATH' -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; -# The names of the tagged configurations supported by this script. -available_tags= + uts4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; -# ### BEGIN LIBTOOL CONFIG], -[# ### BEGIN LIBTOOL TAG CONFIG: $tagname]) + *) + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: + if test x$host_vendor = xsni; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym' + ;; + esac + fi + fi +]) +AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) +test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no -# Shell to use when invoking shell scripts. -SHELL=$lt_SHELL +_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld -# Whether or not to build shared libraries. -build_libtool_libs=$enable_shared +_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl +_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl +_LT_DECL([], [extract_expsyms_cmds], [2], + [The commands to extract the exported symbol list from a shared archive]) -# Whether or not to build static libraries. -build_old_libs=$enable_static +# +# Do we need to explicitly link libc? +# +case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in +x|xyes) + # Assume -lc should be added + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes -# Whether or not to add -lc for building shared libraries. -build_libtool_need_lc=$_LT_AC_TAGVAR(archive_cmds_need_lc, $1) + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $_LT_TAGVAR(archive_cmds, $1) in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + AC_MSG_CHECKING([whether -lc should be explicitly linked in]) + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext -# Whether or not to disallow shared libs when runtime libs are static -allow_libtool_libs_with_static_runtimes=$_LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1) + if AC_TRY_EVAL(ac_compile) 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) + pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) + _LT_TAGVAR(allow_undefined_flag, $1)= + if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) + then + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + else + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + fi + _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + AC_MSG_RESULT([$_LT_TAGVAR(archive_cmds_need_lc, $1)]) + ;; + esac + fi + ;; +esac -# Whether or not to optimize for fast installation. -fast_install=$enable_fast_install +_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], + [Whether or not to add -lc for building shared libraries]) +_LT_TAGDECL([allow_libtool_libs_with_static_runtimes], + [enable_shared_with_static_runtimes], [0], + [Whether or not to disallow shared libs when runtime libs are static]) +_LT_TAGDECL([], [export_dynamic_flag_spec], [1], + [Compiler flag to allow reflexive dlopens]) +_LT_TAGDECL([], [whole_archive_flag_spec], [1], + [Compiler flag to generate shared objects directly from archives]) +_LT_TAGDECL([], [compiler_needs_object], [1], + [Whether the compiler copes with passing no objects directly]) +_LT_TAGDECL([], [old_archive_from_new_cmds], [2], + [Create an old-style archive from a shared archive]) +_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], + [Create a temporary old-style archive to link instead of a shared archive]) +_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) +_LT_TAGDECL([], [archive_expsym_cmds], [2]) +_LT_TAGDECL([], [module_cmds], [2], + [Commands used to build a loadable module if different from building + a shared archive.]) +_LT_TAGDECL([], [module_expsym_cmds], [2]) +_LT_TAGDECL([], [with_gnu_ld], [1], + [Whether we are building with GNU ld or not]) +_LT_TAGDECL([], [allow_undefined_flag], [1], + [Flag that allows shared libraries with undefined symbols to be built]) +_LT_TAGDECL([], [no_undefined_flag], [1], + [Flag that enforces no undefined symbols]) +_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], + [Flag to hardcode $libdir into a binary during linking. + This must work even if $libdir does not exist]) +_LT_TAGDECL([], [hardcode_libdir_flag_spec_ld], [1], + [[If ld is used when linking, flag to hardcode $libdir into a binary + during linking. This must work even if $libdir does not exist]]) +_LT_TAGDECL([], [hardcode_libdir_separator], [1], + [Whether we need a single "-rpath" flag with a separated argument]) +_LT_TAGDECL([], [hardcode_direct], [0], + [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes + DIR into the resulting binary]) +_LT_TAGDECL([], [hardcode_direct_absolute], [0], + [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes + DIR into the resulting binary and the resulting library dependency is + "absolute", i.e impossible to change by setting ${shlibpath_var} if the + library is relocated]) +_LT_TAGDECL([], [hardcode_minus_L], [0], + [Set to "yes" if using the -LDIR flag during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_shlibpath_var], [0], + [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_automatic], [0], + [Set to "yes" if building a shared library automatically hardcodes DIR + into the library and all subsequent libraries and executables linked + against it]) +_LT_TAGDECL([], [inherit_rpath], [0], + [Set to yes if linker adds runtime paths of dependent libraries + to runtime path list]) +_LT_TAGDECL([], [link_all_deplibs], [0], + [Whether libtool must link a program against all its dependency libraries]) +_LT_TAGDECL([], [fix_srcfile_path], [1], + [Fix the shell variable $srcfile for the compiler]) +_LT_TAGDECL([], [always_export_symbols], [0], + [Set to "yes" if exported symbols are required]) +_LT_TAGDECL([], [export_symbols_cmds], [2], + [The commands to list exported symbols]) +_LT_TAGDECL([], [exclude_expsyms], [1], + [Symbols that should not be listed in the preloaded symbols]) +_LT_TAGDECL([], [include_expsyms], [1], + [Symbols that must always be exported]) +_LT_TAGDECL([], [prelink_cmds], [2], + [Commands necessary for linking programs (against libraries) with templates]) +_LT_TAGDECL([], [file_list_spec], [1], + [Specify filename containing input files]) +dnl FIXME: Not yet implemented +dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], +dnl [Compiler flag to generate thread safe objects]) +])# _LT_LINKER_SHLIBS -# The host system. -host_alias=$host_alias -host=$host -host_os=$host_os -# The build system. -build_alias=$build_alias -build=$build -build_os=$build_os +# _LT_LANG_C_CONFIG([TAG]) +# ------------------------ +# Ensure that the configuration variables for a C compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to `libtool'. +m4_defun([_LT_LANG_C_CONFIG], +[m4_require([_LT_DECL_EGREP])dnl +lt_save_CC="$CC" +AC_LANG_PUSH(C) -# An echo program that does not interpret backslashes. -echo=$lt_echo +# Source file extension for C test sources. +ac_ext=c -# The archiver. -AR=$lt_AR -AR_FLAGS=$lt_AR_FLAGS +# Object file extension for compiled C test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext -# A C compiler. -LTCC=$lt_LTCC +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" -# LTCC compiler flags. -LTCFLAGS=$lt_LTCFLAGS +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' -# A language-specific compiler. -CC=$lt_[]_LT_AC_TAGVAR(compiler, $1) +_LT_TAG_COMPILER +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC -# Is the compiler the GNU C compiler? -with_gcc=$_LT_AC_TAGVAR(GCC, $1) +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE -# An ERE matcher. -EGREP=$lt_EGREP +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + LT_SYS_DLOPEN_SELF + _LT_CMD_STRIPLIB + + # Report which library types will actually be built + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) -# The linker used to build libraries. -LD=$lt_[]_LT_AC_TAGVAR(LD, $1) + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no -# Whether we need hard or soft links. -LN_S=$lt_LN_S + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; -# A BSD-compatible nm program. -NM=$lt_NM + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) -# A symbol stripping program -STRIP=$lt_STRIP + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) -# Used to examine libraries when file_magic_cmd begins "file" -MAGIC_CMD=$MAGIC_CMD + _LT_CONFIG($1) +fi +AC_LANG_POP +CC="$lt_save_CC" +])# _LT_LANG_C_CONFIG -# Used on cygwin: DLL creation program. -DLLTOOL="$DLLTOOL" -# Used on cygwin: object dumper. -OBJDUMP="$OBJDUMP" +# _LT_PROG_CXX +# ------------ +# Since AC_PROG_CXX is broken, in that it returns g++ if there is no c++ +# compiler, we have our own version here. +m4_defun([_LT_PROG_CXX], +[ +pushdef([AC_MSG_ERROR], [_lt_caught_CXX_error=yes]) +AC_PROG_CXX +if test -n "$CXX" && ( test "X$CXX" != "Xno" && + ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || + (test "X$CXX" != "Xg++"))) ; then + AC_PROG_CXXCPP +else + _lt_caught_CXX_error=yes +fi +popdef([AC_MSG_ERROR]) +])# _LT_PROG_CXX -# Used on cygwin: assembler. -AS="$AS" +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([_LT_PROG_CXX], []) -# The name of the directory that contains temporary libtool files. -objdir=$objdir -# How to create reloadable object files. -reload_flag=$lt_reload_flag -reload_cmds=$lt_reload_cmds +# _LT_LANG_CXX_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a C++ compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to `libtool'. +m4_defun([_LT_LANG_CXX_CONFIG], +[AC_REQUIRE([_LT_PROG_CXX])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl + +AC_LANG_PUSH(C++) +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(compiler_needs_object, $1)=no +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no -# How to pass a linker flag through the compiler. -wl=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_wl, $1) +# Source file extension for C++ test sources. +ac_ext=cpp -# Object file suffix (normally "o"). -objext="$ac_objext" +# Object file extension for compiled C++ test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext -# Old archive suffix (normally "a"). -libext="$libext" +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_caught_CXX_error" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) -# Shared library suffix (normally ".so"). -shrext_cmds='$shrext_cmds' + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test "$GXX" = yes; then + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + else + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + fi -# Executable file suffix (normally ""). -exeext="$exeext" + if test "$GXX" = yes; then + # Set up default GNU C++ configuration -# Additional compiler flags for building library objects. -pic_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) -pic_mode=$pic_mode + LT_PATH_LD -# What is the maximum length of a command? -max_cmd_len=$lt_cv_sys_max_cmd_len + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test "$with_gnu_ld" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='${wl}' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + with_gnu_ld=no + wlarc= -# Does compiler simultaneously support -c and -o options? -compiler_c_o=$lt_[]_LT_AC_TAGVAR(lt_cv_prog_compiler_c_o, $1) + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi -# Must we lock files when doing compilation? -need_locks=$lt_need_locks + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' -# Do we need the lib prefix for modules? -need_lib_prefix=$need_lib_prefix + else + GXX=no + with_gnu_ld=no + wlarc= + fi -# Do we need a version for libraries? -need_version=$need_version + # PORTME: fill in a description of your system's C++ link characteristics + AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) + _LT_TAGVAR(ld_shlibs, $1)=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aix[[4-9]]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + aix_use_runtimelinking=no -# Whether dlopen is supported. -dlopen_support=$enable_dlopen + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + ;; + esac -# Whether dlopen of programs is supported. -dlopen_self=$enable_dlopen_self + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi -# Whether dlopen of statically linked programs is supported. -dlopen_self_static=$enable_dlopen_self_static + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' + + if test "$GXX" = yes; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi -# Compiler flag to prevent dynamic linking. -link_static_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_static, $1) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an empty + # executable. + _LT_SYS_MODULE_PATH_AIX + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" -# Compiler flag to turn off builtin functions. -no_builtin_flag=$lt_[]_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds its shared + # libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; -# Compiler flag to allow reflexive dlopens. -export_dynamic_flag_spec=$lt_[]_LT_AC_TAGVAR(export_dynamic_flag_spec, $1) + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; -# Compiler flag to generate shared objects directly from archives. -whole_archive_flag_spec=$lt_[]_LT_AC_TAGVAR(whole_archive_flag_spec, $1) + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; -# Compiler flag to generate thread-safe objects. -thread_safe_flag_spec=$lt_[]_LT_AC_TAGVAR(thread_safe_flag_spec, $1) + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; -# Library versioning type. -version_type=$version_type - -# Format of library name prefix. -libname_spec=$lt_libname_spec + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; -# List of archive names. First name is the real one, the rest are links. -# The last name is the one that the linker finds with -lNAME. -library_names_spec=$lt_library_names_spec + freebsd[[12]]*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + _LT_TAGVAR(ld_shlibs, $1)=no + ;; -# The coded name of the library, if different from the real name. -soname_spec=$lt_soname_spec + freebsd-elf*) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + ;; -# Commands used to build and install an old-style archive. -RANLIB=$lt_RANLIB -old_archive_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_cmds, $1) -old_postinstall_cmds=$lt_old_postinstall_cmds -old_postuninstall_cmds=$lt_old_postuninstall_cmds + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; -# Create an old-style archive from a shared archive. -old_archive_from_new_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_from_new_cmds, $1) + gnu*) + ;; -# Create a temporary old-style archive to link instead of a shared archive. -old_archive_from_expsyms_cmds=$lt_[]_LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1) + hpux9*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + ;; + *) + if test "$GXX" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; -# Commands used to build and install a shared archive. -archive_cmds=$lt_[]_LT_AC_TAGVAR(archive_cmds, $1) -archive_expsym_cmds=$lt_[]_LT_AC_TAGVAR(archive_expsym_cmds, $1) -postinstall_cmds=$lt_postinstall_cmds -postuninstall_cmds=$lt_postuninstall_cmds + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac -# Commands used to build a loadable module (assumed same as above if empty) -module_cmds=$lt_[]_LT_AC_TAGVAR(module_cmds, $1) -module_expsym_cmds=$lt_[]_LT_AC_TAGVAR(module_expsym_cmds, $1) + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + ;; + *) + if test "$GXX" = yes; then + if test $with_gnu_ld = no; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; -# Commands to strip libraries. -old_striplib=$lt_old_striplib -striplib=$lt_striplib + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` -o $lib' + fi + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + esac + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + ;; -# Dependencies to place before the objects being linked to create a -# shared library. -predep_objects=$lt_[]_LT_AC_TAGVAR(predep_objects, $1) + linux* | k*bsd*-gnu) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [[1-5]]* | *pgcpp\ [[1-5]]*) + _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"' + _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~ + $RANLIB $oldlib' + _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + *) # Version 6 will use weak symbols + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + esac -# Dependencies to place after the objects being linked to create a -# shared library. -postdep_objects=$lt_[]_LT_AC_TAGVAR(postdep_objects, $1) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + ;; + cxx*) + # Compaq C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' -# Dependencies to place before the objects being linked to create a -# shared library. -predeps=$lt_[]_LT_AC_TAGVAR(predeps, $1) + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: -# Dependencies to place after the objects being linked to create a -# shared library. -postdeps=$lt_[]_LT_AC_TAGVAR(postdeps, $1) + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + ;; + xl*) + # IBM XL 8.0 on PPC, with GNU ld + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='echo' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; -# The library search path used internally by the compiler when linking -# a shared library. -compiler_lib_search_path=$lt_[]_LT_AC_TAGVAR(compiler_lib_search_path, $1) + lynxos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; -# Method to check whether dependent libraries are shared objects. -deplibs_check_method=$lt_deplibs_check_method + m88k*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; -# Command to use when deplibs_check_method == file_magic. -file_magic_cmd=$lt_file_magic_cmd + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; -# Flag that allows shared libraries with undefined symbols to be built. -allow_undefined_flag=$lt_[]_LT_AC_TAGVAR(allow_undefined_flag, $1) + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; -# Flag that forces no undefined symbols. -no_undefined_flag=$lt_[]_LT_AC_TAGVAR(no_undefined_flag, $1) + *nto* | *qnx*) + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; -# Commands used to finish a libtool library installation in a directory. -finish_cmds=$lt_finish_cmds + openbsd2*) + # C++ shared libraries are fairly broken + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + fi + output_verbose_link_cmd=echo + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; -# Same as above, but a single script fragment to be evaled but not shown. -finish_eval=$lt_finish_eval + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + cxx*) + case $host in + osf3*) + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && $ECHO "X${wl}-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + ;; + *) + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~ + $RM $lib.exp' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + ;; + esac -# Take the output of nm and produce a listing of raw symbols and C names. -global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + _LT_TAGVAR(hardcode_libdir_separator, $1)=: -# Transform the output of nm in a proper C declaration -global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + case $host in + osf3*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; -# Transform the output of nm in a C name address pair -global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + psos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; -# This is the shared library runtime path variable. -runpath_var=$runpath_var + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; -# This is the shared library path variable. -shlibpath_var=$shlibpath_var + solaris*) + case $cc_basename in + CC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(archive_cmds_need_lc,$1)=yes + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes -# Is shlibpath searched before the hard-coded library search path? -shlibpath_overrides_runpath=$shlibpath_overrides_runpath + output_verbose_link_cmd='echo' -# How to hardcode a shared library path into an executable. -hardcode_action=$_LT_AC_TAGVAR(hardcode_action, $1) + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' -# Whether we should hardcode library paths into libraries. -hardcode_into_libs=$hardcode_into_libs + # The C++ compiler must be used to create the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' + else + # g++ 2.7 appears to require `-G' NOT `-shared' on this + # platform. + _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' + fi + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; -# Flag to hardcode \$libdir into a binary during linking. -# This must work even if \$libdir does not exist. -hardcode_libdir_flag_spec=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' -# If ld is used when linking, flag to hardcode \$libdir into -# a binary during linking. This must work even if \$libdir does -# not exist. -hardcode_libdir_flag_spec_ld=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1) + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; -# Whether we need a single -rpath flag with a separated argument. -hardcode_libdir_separator=$lt_[]_LT_AC_TAGVAR(hardcode_libdir_separator, $1) + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var='LD_RUN_PATH' -# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the -# resulting binary. -hardcode_direct=$_LT_AC_TAGVAR(hardcode_direct, $1) + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; -# Set to yes if using the -LDIR flag during linking hardcodes DIR into the -# resulting binary. -hardcode_minus_L=$_LT_AC_TAGVAR(hardcode_minus_L, $1) + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; -# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into -# the resulting binary. -hardcode_shlibpath_var=$_LT_AC_TAGVAR(hardcode_shlibpath_var, $1) + vxworks*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; -# Set to yes if building a shared library automatically hardcodes DIR into the library -# and all subsequent libraries and executables linked against it. -hardcode_automatic=$_LT_AC_TAGVAR(hardcode_automatic, $1) + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac -# Variables whose values should be saved in libtool wrapper scripts and -# restored at relink time. -variables_saved_for_relink="$variables_saved_for_relink" + AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) + test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no -# Whether libtool must link a program against all its dependency libraries. -link_all_deplibs=$_LT_AC_TAGVAR(link_all_deplibs, $1) + _LT_TAGVAR(GCC, $1)="$GXX" + _LT_TAGVAR(LD, $1)="$LD" -# Compile-time system search path for libraries -sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + CC=$lt_save_CC + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test "$_lt_caught_CXX_error" != yes -# Run-time system search path for libraries -sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec +AC_LANG_POP +])# _LT_LANG_CXX_CONFIG -# Fix the shell variable \$srcfile for the compiler. -fix_srcfile_path="$_LT_AC_TAGVAR(fix_srcfile_path, $1)" -# Set to yes if exported symbols are required. -always_export_symbols=$_LT_AC_TAGVAR(always_export_symbols, $1) +# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) +# --------------------------------- +# Figure out "hidden" library dependencies from verbose +# compiler output when linking a shared library. +# Parse the compiler output and extract the necessary +# objects, libraries and library flags. +m4_defun([_LT_SYS_HIDDEN_LIBDEPS], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +# Dependencies to place before and after the object being linked: +_LT_TAGVAR(predep_objects, $1)= +_LT_TAGVAR(postdep_objects, $1)= +_LT_TAGVAR(predeps, $1)= +_LT_TAGVAR(postdeps, $1)= +_LT_TAGVAR(compiler_lib_search_path, $1)= -# The commands to list exported symbols. -export_symbols_cmds=$lt_[]_LT_AC_TAGVAR(export_symbols_cmds, $1) +dnl we can't use the lt_simple_compile_test_code here, +dnl because it contains code intended for an executable, +dnl not a library. It's possible we should let each +dnl tag define a new lt_????_link_test_code variable, +dnl but it's only used here... +m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF +int a; +void foo (void) { a = 0; } +_LT_EOF +], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF +], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer*4 a + a=0 + return + end +_LT_EOF +], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer a + a=0 + return + end +_LT_EOF +], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF +public class foo { + private int a; + public void bar (void) { + a = 0; + } +}; +_LT_EOF +]) +dnl Parse the compiler output and extract the necessary +dnl objects, libraries and library flags. +if AC_TRY_EVAL(ac_compile); then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. -# The commands to extract the exported symbol list from a shared archive. -extract_expsyms_cmds=$lt_extract_expsyms_cmds + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no -# Symbols that should not be listed in the preloaded symbols. -exclude_expsyms=$lt_[]_LT_AC_TAGVAR(exclude_expsyms, $1) + for p in `eval "$output_verbose_link_cmd"`; do + case $p in -# Symbols that must always be exported. -include_expsyms=$lt_[]_LT_AC_TAGVAR(include_expsyms, $1) + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test $p = "-L" || + test $p = "-R"; then + prev=$p + continue + else + prev= + fi -ifelse([$1],[], -[# ### END LIBTOOL CONFIG], -[# ### END LIBTOOL TAG CONFIG: $tagname]) + if test "$pre_test_object_deps_done" = no; then + case $p in + -L* | -R*) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then + _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" + else + _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$_LT_TAGVAR(postdeps, $1)"; then + _LT_TAGVAR(postdeps, $1)="${prev}${p}" + else + _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}" + fi + fi + ;; -__EOF__ + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi -ifelse([$1],[], [ - case $host_os in - aix3*) - cat <<\EOF >> "$cfgfile" + if test "$pre_test_object_deps_done" = no; then + if test -z "$_LT_TAGVAR(predep_objects, $1)"; then + _LT_TAGVAR(predep_objects, $1)="$p" + else + _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" + fi + else + if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then + _LT_TAGVAR(postdep_objects, $1)="$p" + else + _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" + fi + fi + ;; -# AIX sometimes has problems with the GCC collect2 program. For some -# reason, if we set the COLLECT_NAMES environment variable, the problems -# vanish in a puff of smoke. -if test "X${COLLECT_NAMES+set}" != Xset; then - COLLECT_NAMES= - export COLLECT_NAMES -fi -EOF - ;; - esac + *) ;; # Ignore the rest. - # We use sed instead of cat because bash on DJGPP gets confused if - # if finds mixed CR/LF and LF-only lines. Since sed operates in - # text mode, it properly converts lines to CR/LF. This bash problem - # is reportedly fixed, but why not run on old versions too? - sed '$q' "$ltmain" >> "$cfgfile" || (rm -f "$cfgfile"; exit 1) + esac + done - mv -f "$cfgfile" "$ofile" || \ - (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") - chmod +x "$ofile" -]) + # Clean up. + rm -f a.out a.exe else - # If there is no Makefile yet, we rely on a make rule to execute - # `config.status --recheck' to rerun these tests and create the - # libtool script then. - ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` - if test -f "$ltmain_in"; then - test -f Makefile && make "$ltmain" - fi + echo "libtool.m4: error: problem compiling $1 test program" fi -])# AC_LIBTOOL_CONFIG +$RM -f confest.$objext -# AC_LIBTOOL_PROG_COMPILER_NO_RTTI([TAGNAME]) -# ------------------------------------------- -AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], -[AC_REQUIRE([_LT_AC_SYS_COMPILER])dnl +# PORTME: override above test on systems where it is broken +m4_if([$1], [CXX], +[case $host_os in +interix[[3-9]]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + _LT_TAGVAR(predep_objects,$1)= + _LT_TAGVAR(postdep_objects,$1)= + _LT_TAGVAR(postdeps,$1)= + ;; -_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= +linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac -if test "$GCC" = yes; then - _LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + if test "$solaris_use_stlport4" != yes; then + _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' + fi + ;; + esac + ;; - AC_LIBTOOL_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], - lt_cv_prog_compiler_rtti_exceptions, - [-fno-rtti -fno-exceptions], [], - [_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) -fi -])# AC_LIBTOOL_PROG_COMPILER_NO_RTTI +solaris*) + case $cc_basename in + CC*) + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + # Adding this requires a known-good setup of shared libraries for + # Sun compiler versions before 5.6, else PIC objects from an old + # archive will be linked into the output, leading to subtle bugs. + if test "$solaris_use_stlport4" != yes; then + _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' + fi + ;; + esac + ;; +esac +]) -# AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE -# --------------------------------- -AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], -[AC_REQUIRE([AC_CANONICAL_HOST]) -AC_REQUIRE([AC_PROG_NM]) -AC_REQUIRE([AC_OBJEXT]) -# Check for command to grab the raw symbol name followed by C symbol from nm. -AC_MSG_CHECKING([command to parse $NM output from $compiler object]) -AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], +case " $_LT_TAGVAR(postdeps, $1) " in +*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; +esac + _LT_TAGVAR(compiler_lib_search_dirs, $1)= +if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then + _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` +fi +_LT_TAGDECL([], [compiler_lib_search_dirs], [1], + [The directories searched by this compiler when creating a shared library]) +_LT_TAGDECL([], [predep_objects], [1], + [Dependencies to place before and after the objects being linked to + create a shared library]) +_LT_TAGDECL([], [postdep_objects], [1]) +_LT_TAGDECL([], [predeps], [1]) +_LT_TAGDECL([], [postdeps], [1]) +_LT_TAGDECL([], [compiler_lib_search_path], [1], + [The library search path used internally by the compiler when linking + a shared library]) +])# _LT_SYS_HIDDEN_LIBDEPS + + +# _LT_PROG_F77 +# ------------ +# Since AC_PROG_F77 is broken, in that it returns the empty string +# if there is no fortran compiler, we have our own version here. +m4_defun([_LT_PROG_F77], [ -# These are sane defaults that work on at least a few old systems. -# [They come from Ultrix. What could be older than Ultrix?!! ;)] +pushdef([AC_MSG_ERROR], [_lt_disable_F77=yes]) +AC_PROG_F77 +if test -z "$F77" || test "X$F77" = "Xno"; then + _lt_disable_F77=yes +fi +popdef([AC_MSG_ERROR]) +])# _LT_PROG_F77 -# Character class describing NM global symbol codes. -symcode='[[BCDEGRST]]' +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([_LT_PROG_F77], []) -# Regexp to match symbols that can be accessed directly from C. -sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' -# Transform an extracted symbol line into a proper C declaration -lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^. .* \(.*\)$/extern int \1;/p'" +# _LT_LANG_F77_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a Fortran 77 compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_F77_CONFIG], +[AC_REQUIRE([_LT_PROG_F77])dnl +AC_LANG_PUSH(Fortran 77) -# Transform an extracted symbol line into symbol name and symbol address -lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no -# Define system-specific variables. -case $host_os in -aix*) - symcode='[[BCDT]]' - ;; -cygwin* | mingw* | pw32*) - symcode='[[ABCDGISTW]]' - ;; -hpux*) # Its linker distinguishes data from code symbols - if test "$host_cpu" = ia64; then - symcode='[[ABCDEGRST]]' - fi - lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" - lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" - ;; -linux*) - if test "$host_cpu" = ia64; then - symcode='[[ABCDGIRSTW]]' - lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" - lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" - fi - ;; -irix* | nonstopux*) - symcode='[[BCDEGRST]]' - ;; -osf*) - symcode='[[BCDEGQRST]]' - ;; -solaris*) - symcode='[[BDRT]]' - ;; -sco3.2v5*) - symcode='[[DT]]' - ;; -sysv4.2uw2*) - symcode='[[DT]]' - ;; -sysv5* | sco5v6* | unixware* | OpenUNIX*) - symcode='[[ABDT]]' - ;; -sysv4) - symcode='[[DFNSTU]]' - ;; -esac +# Source file extension for f77 test sources. +ac_ext=f -# Handle CRLF in mingw tool chain -opt_cr= -case $build_os in -mingw*) - opt_cr=`echo 'x\{0,1\}' | tr x '\015'` # option cr in regexp - ;; -esac +# Object file extension for compiled f77 test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext -# If we're using GNU nm, then use its standard symbol codes. -case `$NM -V 2>&1` in -*GNU* | *'with BFD'*) - symcode='[[ABCDGIRSTW]]' ;; -esac +# No sense in running all these tests if we already determined that +# the F77 compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_disable_F77" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" -# Try without a prefix undercore, then with it. -for ac_symprfx in "" "_"; do + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" - # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. - symxfrm="\\1 $ac_symprfx\\2 \\2" + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER - # Write the raw and C identifiers. - lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC="$CC" + lt_save_GCC=$GCC + CC=${F77-"f77"} + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + GCC=$G77 + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) - # Check to see that the pipe works correctly. - pipe_works=no + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no - rm -f conftest* - cat > conftest.$ac_ext < $nlist) && test -s "$nlist"; then - # Try sorting and uniquifying the output. - if sort "$nlist" | uniq > "$nlist"T; then - mv -f "$nlist"T "$nlist" - else - rm -f "$nlist"T - fi + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)="$G77" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC="$lt_save_CC" +fi # test "$_lt_disable_F77" != yes - # Make sure that we snagged all the symbols we need. - if grep ' nm_test_var$' "$nlist" >/dev/null; then - if grep ' nm_test_func$' "$nlist" >/dev/null; then - cat < conftest.$ac_ext -#ifdef __cplusplus -extern "C" { -#endif +AC_LANG_POP +])# _LT_LANG_F77_CONFIG -EOF - # Now generate the symbol file. - eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | grep -v main >> conftest.$ac_ext' - cat <> conftest.$ac_ext -#if defined (__STDC__) && __STDC__ -# define lt_ptr_t void * -#else -# define lt_ptr_t char * -# define const -#endif +# _LT_PROG_FC +# ----------- +# Since AC_PROG_FC is broken, in that it returns the empty string +# if there is no fortran compiler, we have our own version here. +m4_defun([_LT_PROG_FC], +[ +pushdef([AC_MSG_ERROR], [_lt_disable_FC=yes]) +AC_PROG_FC +if test -z "$FC" || test "X$FC" = "Xno"; then + _lt_disable_FC=yes +fi +popdef([AC_MSG_ERROR]) +])# _LT_PROG_FC -/* The mapping between symbol names and symbols. */ -const struct { - const char *name; - lt_ptr_t address; -} -lt_preloaded_symbols[[]] = -{ -EOF - $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (lt_ptr_t) \&\2},/" < "$nlist" | grep -v main >> conftest.$ac_ext - cat <<\EOF >> conftest.$ac_ext - {0, (lt_ptr_t) 0} -}; +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([_LT_PROG_FC], []) -#ifdef __cplusplus -} -#endif -EOF - # Now try linking the two files. - mv conftest.$ac_objext conftstm.$ac_objext - lt_save_LIBS="$LIBS" - lt_save_CFLAGS="$CFLAGS" - LIBS="conftstm.$ac_objext" - CFLAGS="$CFLAGS$_LT_AC_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" - if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then - pipe_works=yes - fi - LIBS="$lt_save_LIBS" - CFLAGS="$lt_save_CFLAGS" - else - echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD - fi - else - echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD - fi - else - echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD - fi - else - echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD - cat conftest.$ac_ext >&5 - fi - rm -f conftest* conftst* - # Do not use the global_symbol_pipe unless it works. - if test "$pipe_works" = yes; then - break - else - lt_cv_sys_global_symbol_pipe= - fi -done -]) -if test -z "$lt_cv_sys_global_symbol_pipe"; then - lt_cv_sys_global_symbol_to_cdecl= -fi -if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then - AC_MSG_RESULT(failed) -else - AC_MSG_RESULT(ok) -fi -]) # AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE +# _LT_LANG_FC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for a Fortran compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_FC_CONFIG], +[AC_REQUIRE([_LT_PROG_FC])dnl +AC_LANG_PUSH(Fortran) + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no +# Source file extension for fc test sources. +ac_ext=${ac_fc_srcext-f} -# AC_LIBTOOL_PROG_COMPILER_PIC([TAGNAME]) -# --------------------------------------- -AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC], -[_LT_AC_TAGVAR(lt_prog_compiler_wl, $1)= -_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= -_LT_AC_TAGVAR(lt_prog_compiler_static, $1)= +# Object file extension for compiled fc test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext -AC_MSG_CHECKING([for $compiler option to produce PIC]) - ifelse([$1],[CXX],[ - # C++ specific cases for pic, static, wl, etc. - if test "$GXX" = yes; then - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' +# No sense in running all these tests if we already determined that +# the FC compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_disable_FC" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC="$CC" + lt_save_GCC=$GCC + CC=${FC-"f95"} + compiler=$CC + GCC=$ac_cv_fc_compiler_gnu + + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - fi - ;; - amigaos*) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' - ;; - beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - mingw* | os2* | pw32*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT' - ;; - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' - ;; - *djgpp*) - # DJGPP does not support shared libraries at all - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= - ;; - interix3*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; - sysv4*MP*) - if test -d /usr/nec; then - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic - fi - ;; - hpux*) - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - ;; - *) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - esac - ;; - *) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; esac - else - case $host_os in - aix4* | aix5*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - else - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' - fi - ;; - chorus*) - case $cc_basename in - cxch68*) - # Green Hills C++ Compiler - # _LT_AC_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" - ;; - esac - ;; - darwin*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - case $cc_basename in - xlc*) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-qnocommon' - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - ;; - esac - ;; - dgux*) - case $cc_basename in - ec++*) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - ;; - ghcx*) - # Green Hills C++ Compiler - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - ;; - *) - ;; - esac - ;; - freebsd* | kfreebsd*-gnu | dragonfly*) - # FreeBSD uses GNU C++ - ;; - hpux9* | hpux10* | hpux11*) - case $cc_basename in - CC*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' - if test "$host_cpu" != ia64; then - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z' - fi - ;; - aCC*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z' - ;; - esac - ;; - *) - ;; - esac - ;; - interix*) - # This is c89, which is MS Visual C++ (no shared libs) - # Anyone wants to do a port? - ;; - irix5* | irix6* | nonstopux*) - case $cc_basename in - CC*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - # CC pic flag -KPIC is the default. - ;; - *) - ;; - esac - ;; - linux*) - case $cc_basename in - KCC*) - # KAI C++ Compiler - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - icpc* | ecpc*) - # Intel C++ - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' - ;; - pgCC*) - # Portland Group C++ compiler. - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - cxx*) - # Compaq C++ - # Make sure the PIC flag is empty. It appears that all Alpha - # Linux and Compaq Tru64 Unix objects are PIC. - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - *) - ;; - esac - ;; - lynxos*) - ;; - m88k*) - ;; - mvs*) - case $cc_basename in - cxx*) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' - ;; - *) - ;; - esac - ;; - netbsd*) - ;; - osf3* | osf4* | osf5*) - case $cc_basename in - KCC*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' - ;; - RCC*) - # Rational C++ 2.4.1 - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - ;; - cxx*) - # Digital/Compaq C++ - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # Make sure the PIC flag is empty. It appears that all Alpha - # Linux and Compaq Tru64 Unix objects are PIC. - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - *) - ;; - esac - ;; - psos*) - ;; - solaris*) - case $cc_basename in - CC*) - # Sun C++ 4.2, 5.x and Centerline C++ - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' - ;; - gcx*) - # Green Hills C++ Compiler - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' - ;; - *) - ;; - esac - ;; - sunos4*) - case $cc_basename in - CC*) - # Sun C++ 4.x - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - lcc*) - # Lucid - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - ;; - *) - ;; - esac - ;; - tandem*) - case $cc_basename in - NCC*) - # NonStop-UX NCC 3.20 - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - ;; - *) - ;; - esac - ;; - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - case $cc_basename in - CC*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - esac - ;; - vxworks*) - ;; - *) - _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no - ;; - esac - fi -], -[ - if test "$GCC" = yes; then - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' + AC_MSG_RESULT([$enable_shared]) - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - fi - ;; + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC="$lt_save_CC" +fi # test "$_lt_disable_FC" != yes - amigaos*) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' - ;; +AC_LANG_POP +])# _LT_LANG_FC_CONFIG - beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - mingw* | pw32* | os2*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT' - ;; +# _LT_LANG_GCJ_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Java Compiler compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_GCJ_CONFIG], +[AC_REQUIRE([LT_PROG_GCJ])dnl +AC_LANG_SAVE - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' - ;; +# Source file extension for Java test sources. +ac_ext=java - interix3*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; +# Object file extension for compiled Java test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext - msdosdjgpp*) - # Just because we use GCC doesn't mean we suddenly get shared libraries - # on systems that don't support them. - _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no - enable_shared=no - ;; +# Code to be used in simple compile tests +lt_simple_compile_test_code="class foo {}" - sysv4*MP*) - if test -d /usr/nec; then - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic - fi - ;; +# Code to be used in simple link tests +lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' - hpux*) - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - esac - ;; +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER - *) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' - ;; - esac - else - # PORTME Check for flag to pass linker flags through the system compiler. - case $host_os in - aix*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - else - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC="$CC" +lt_save_GCC=$GCC +GCC=yes +CC=${GCJ-"gcj"} +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)="$LD" +_LT_CC_BASENAME([$compiler]) + +# GCJ did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds + +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC="$lt_save_CC" +])# _LT_LANG_GCJ_CONFIG + + +# _LT_LANG_RC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for the Windows resource compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_RC_CONFIG], +[AC_REQUIRE([LT_PROG_RC])dnl +AC_LANG_SAVE + +# Source file extension for RC test sources. +ac_ext=rc + +# Object file extension for compiled RC test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' + +# Code to be used in simple link tests +lt_simple_link_test_code="$lt_simple_compile_test_code" + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC="$CC" +lt_save_GCC=$GCC +GCC= +CC=${RC-"windres"} +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_CC_BASENAME([$compiler]) +_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + +if test -n "$compiler"; then + : + _LT_CONFIG($1) +fi + +GCC=$lt_save_GCC +AC_LANG_RESTORE +CC="$lt_save_CC" +])# _LT_LANG_RC_CONFIG + + +# LT_PROG_GCJ +# ----------- +AC_DEFUN([LT_PROG_GCJ], +[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], + [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], + [AC_CHECK_TOOL(GCJ, gcj,) + test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" + AC_SUBST(GCJFLAGS)])])[]dnl +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_GCJ], []) + + +# LT_PROG_RC +# ---------- +AC_DEFUN([LT_PROG_RC], +[AC_CHECK_TOOL(RC, windres,) +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_RC], []) + + +# _LT_DECL_EGREP +# -------------- +# If we don't have a new enough Autoconf to choose the best grep +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_EGREP], +[AC_REQUIRE([AC_PROG_EGREP])dnl +AC_REQUIRE([AC_PROG_FGREP])dnl +test -z "$GREP" && GREP=grep +_LT_DECL([], [GREP], [1], [A grep program that handles long lines]) +_LT_DECL([], [EGREP], [1], [An ERE matcher]) +_LT_DECL([], [FGREP], [1], [A literal string matcher]) +dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too +AC_SUBST([GREP]) +]) + + +# _LT_DECL_OBJDUMP +# -------------- +# If we don't have a new enough Autoconf to choose the best objdump +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_OBJDUMP], +[AC_CHECK_TOOL(OBJDUMP, objdump, false) +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) +AC_SUBST([OBJDUMP]) +]) + + +# _LT_DECL_SED +# ------------ +# Check for a fully-functional sed program, that truncates +# as few characters as possible. Prefer GNU sed if found. +m4_defun([_LT_DECL_SED], +[AC_PROG_SED +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" +_LT_DECL([], [SED], [1], [A sed program that does not truncate output]) +_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], + [Sed that helps us avoid accidentally triggering echo(1) options like -n]) +])# _LT_DECL_SED + +m4_ifndef([AC_PROG_SED], [ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_SED. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # + +m4_defun([AC_PROG_SED], +[AC_MSG_CHECKING([for a sed that does not truncate output]) +AC_CACHE_VAL(lt_cv_path_SED, +[# Loop through the user's path and test for sed and gsed. +# Then use that list of sed's as ones to test for truncation. +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for lt_ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then + lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" fi - ;; - darwin*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - case $cc_basename in - xlc*) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-qnocommon' - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - ;; - esac - ;; + done + done +done +IFS=$as_save_IFS +lt_ac_max=0 +lt_ac_count=0 +# Add /usr/xpg4/bin/sed as it is typically found on Solaris +# along with /bin/sed that truncates output. +for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do + test ! -f $lt_ac_sed && continue + cat /dev/null > conftest.in + lt_ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >conftest.in + # Check for GNU sed and select it if it is found. + if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then + lt_cv_path_SED=$lt_ac_sed + break + fi + while true; do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo >>conftest.nl + $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break + cmp -s conftest.out conftest.nl || break + # 10000 chars as input seems more than enough + test $lt_ac_count -gt 10 && break + lt_ac_count=`expr $lt_ac_count + 1` + if test $lt_ac_count -gt $lt_ac_max; then + lt_ac_max=$lt_ac_count + lt_cv_path_SED=$lt_ac_sed + fi + done +done +]) +SED=$lt_cv_path_SED +AC_SUBST([SED]) +AC_MSG_RESULT([$SED]) +])#AC_PROG_SED +])#m4_ifndef - mingw* | pw32* | os2*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT' - ;; +# Old name: +AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_SED], []) - hpux9* | hpux10* | hpux11*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='+Z' - ;; - esac - # Is there a better lt_prog_compiler_static that works with the bundled CC? - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' - ;; - irix5* | irix6* | nonstopux*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # PIC (with -KPIC) is the default. - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; +# _LT_CHECK_SHELL_FEATURES +# ------------------------ +# Find out whether the shell is Bourne or XSI compatible, +# or has some other useful features. +m4_defun([_LT_CHECK_SHELL_FEATURES], +[AC_MSG_CHECKING([whether the shell understands some XSI constructs]) +# Try some XSI features +xsi_shell=no +( _lt_dummy="a/b/c" + test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \ + = c,a/b,, \ + && eval 'test $(( 1 + 1 )) -eq 2 \ + && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ + && xsi_shell=yes +AC_MSG_RESULT([$xsi_shell]) +_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell']) + +AC_MSG_CHECKING([whether the shell understands "+="]) +lt_shell_append=no +( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \ + >/dev/null 2>&1 \ + && lt_shell_append=yes +AC_MSG_RESULT([$lt_shell_append]) +_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append']) + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi +_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac +_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl +_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl +])# _LT_CHECK_SHELL_FEATURES + + +# _LT_PROG_XSI_SHELLFNS +# --------------------- +# Bourne and XSI compatible variants of some useful shell functions. +m4_defun([_LT_PROG_XSI_SHELLFNS], +[case $xsi_shell in + yes) + cat << \_LT_EOF >> "$cfgfile" + +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac +} + +# func_basename file +func_basename () +{ + func_basename_result="${1##*/}" +} + +# func_dirname_and_basename file append nondir_replacement +# perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# Implementation must be kept synchronized with func_dirname +# and func_basename. For efficiency, we do not delegate to +# those functions but instead duplicate the functionality here. +func_dirname_and_basename () +{ + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac + func_basename_result="${1##*/}" +} + +# func_stripname prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +func_stripname () +{ + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are + # positional parameters, so assign one to ordinary parameter first. + func_stripname_result=${3} + func_stripname_result=${func_stripname_result#"${1}"} + func_stripname_result=${func_stripname_result%"${2}"} +} + +# func_opt_split +func_opt_split () +{ + func_opt_split_opt=${1%%=*} + func_opt_split_arg=${1#*=} +} + +# func_lo2o object +func_lo2o () +{ + case ${1} in + *.lo) func_lo2o_result=${1%.lo}.${objext} ;; + *) func_lo2o_result=${1} ;; + esac +} - newsos6) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=${1%.*}.lo +} - linux*) - case $cc_basename in - icc* | ecc*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-static' - ;; - pgcc* | pgf77* | pgf90* | pgf95*) - # Portland Group compilers (*not* the Pentium gcc compiler, - # which looks to be a dead project) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; - ccc*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # All Alpha code is PIC. - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; - esac - ;; +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=$(( $[*] )) +} - osf3* | osf4* | osf5*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - # All OSF/1 code is PIC. - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' - ;; +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=${#1} +} - solaris*) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - case $cc_basename in - f77* | f90* | f95*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; - *) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; - esac - ;; +_LT_EOF + ;; + *) # Bourne compatible functions. + cat << \_LT_EOF >> "$cfgfile" - sunos4*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + # Extract subdirectory from the argument. + func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi +} - sysv4 | sysv4.2uw2* | sysv4.3*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; +# func_basename file +func_basename () +{ + func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"` +} - sysv4*MP*) - if test -d /usr/nec ;then - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - fi - ;; +dnl func_dirname_and_basename +dnl A portable version of this function is already defined in general.m4sh +dnl so there is no need for it here. + +# func_stripname prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# func_strip_suffix prefix name +func_stripname () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "X${3}" \ + | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "X${3}" \ + | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;; + esac +} - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; +# sed scripts: +my_sed_long_opt='1s/^\(-[[^=]]*\)=.*/\1/;q' +my_sed_long_arg='1s/^-[[^=]]*=//' - unicos*) - _LT_AC_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' - _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no - ;; +# func_opt_split +func_opt_split () +{ + func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"` + func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"` +} - uts4*) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)='-pic' - _LT_AC_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' - ;; +# func_lo2o object +func_lo2o () +{ + func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"` +} - *) - _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no - ;; - esac - fi -]) -AC_MSG_RESULT([$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)]) +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[[^.]]*$/.lo/'` +} -# -# Check to make sure the PIC flag actually works. -# -if test -n "$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)"; then - AC_LIBTOOL_COMPILER_OPTION([if $compiler PIC flag $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) works], - _LT_AC_TAGVAR(lt_prog_compiler_pic_works, $1), - [$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)ifelse([$1],[],[ -DPIC],[ifelse([$1],[CXX],[ -DPIC],[])])], [], - [case $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) in - "" | " "*) ;; - *) _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)" ;; - esac], - [_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= - _LT_AC_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) -fi -case $host_os in - # For platforms which do not support PIC, -DPIC is meaningless: - *djgpp*) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)= - ;; - *) - _LT_AC_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1)ifelse([$1],[],[ -DPIC],[ifelse([$1],[CXX],[ -DPIC],[])])" - ;; -esac +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=`expr "$[@]"` +} -# -# Check to make sure the static flag actually works. -# -wl=$_LT_AC_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_AC_TAGVAR(lt_prog_compiler_static, $1)\" -AC_LIBTOOL_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], - _LT_AC_TAGVAR(lt_prog_compiler_static_works, $1), - $lt_tmp_static_flag, - [], - [_LT_AC_TAGVAR(lt_prog_compiler_static, $1)=]) -]) +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=`expr "$[1]" : ".*" 2>/dev/null || echo $max_cmd_len` +} +_LT_EOF +esac -# AC_LIBTOOL_PROG_LD_SHLIBS([TAGNAME]) -# ------------------------------------ -# See if the linker supports building shared libraries. -AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS], -[AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) -ifelse([$1],[CXX],[ - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - case $host_os in - aix4* | aix5*) - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - if $NM -V 2>&1 | grep 'GNU' > /dev/null; then - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols' - else - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols' - fi +case $lt_shell_append in + yes) + cat << \_LT_EOF >> "$cfgfile" + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "$[1]+=\$[2]" +} +_LT_EOF ;; - pw32*) - _LT_AC_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" - ;; - cygwin* | mingw*) - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]] /s/.* \([[^ ]]*\)/\1 DATA/;/^.* __nm__/s/^.* __nm__\([[^ ]]*\) [[^ ]]*/\1 DATA/;/^I /d;/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols' - ;; *) - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - ;; - esac -],[ - runpath_var= - _LT_AC_TAGVAR(allow_undefined_flag, $1)= - _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=no - _LT_AC_TAGVAR(archive_cmds, $1)= - _LT_AC_TAGVAR(archive_expsym_cmds, $1)= - _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)= - _LT_AC_TAGVAR(old_archive_from_expsyms_cmds, $1)= - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)= - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)= - _LT_AC_TAGVAR(thread_safe_flag_spec, $1)= - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)= - _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)= - _LT_AC_TAGVAR(hardcode_direct, $1)=no - _LT_AC_TAGVAR(hardcode_minus_L, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported - _LT_AC_TAGVAR(link_all_deplibs, $1)=unknown - _LT_AC_TAGVAR(hardcode_automatic, $1)=no - _LT_AC_TAGVAR(module_cmds, $1)= - _LT_AC_TAGVAR(module_expsym_cmds, $1)= - _LT_AC_TAGVAR(always_export_symbols, $1)=no - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - # include_expsyms should be a list of space-separated symbols to be *always* - # included in the symbol list - _LT_AC_TAGVAR(include_expsyms, $1)= - # exclude_expsyms can be an extended regexp of symbols to exclude - # it will be wrapped by ` (' and `)$', so one must not match beginning or - # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', - # as well as any symbol that contains `d'. - _LT_AC_TAGVAR(exclude_expsyms, $1)="_GLOBAL_OFFSET_TABLE_" - # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out - # platforms (ab)use it in PIC code, but their linkers get confused if - # the symbol is explicitly referenced. Since portable code cannot - # rely on this symbol name, it's probably fine to never include it in - # preloaded symbol tables. - extract_expsyms_cmds= - # Just being paranoid about ensuring that cc_basename is set. - _LT_CC_BASENAME([$compiler]) - case $host_os in - cygwin* | mingw* | pw32*) - # FIXME: the MSVC++ port hasn't been tested in a loooong time - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - if test "$GCC" != yes; then - with_gnu_ld=no - fi - ;; - interix*) - # we just hope/assume this is gcc and not c89 (= MSVC++) - with_gnu_ld=yes - ;; - openbsd*) - with_gnu_ld=no + cat << \_LT_EOF >> "$cfgfile" + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "$[1]=\$$[1]\$[2]" +} + +_LT_EOF ;; esac +]) - _LT_AC_TAGVAR(ld_shlibs, $1)=yes - if test "$with_gnu_ld" = yes; then - # If archive_cmds runs LD, not CC, wlarc should be empty - wlarc='${wl}' +# Helper functions for option handling. -*- Autoconf -*- +# +# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. - # Set some defaults for GNU ld with shared library support. These - # are reset later if shared libraries are not supported. Putting them - # here allows them to be overridden if necessary. - runpath_var=LD_RUN_PATH - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' - # ancient GNU ld didn't support --whole-archive et. al. - if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)= - fi - supports_anon_versioning=no - case `$LD -v 2>/dev/null` in - *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 - *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... - *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... - *\ 2.11.*) ;; # other 2.11 versions - *) supports_anon_versioning=yes ;; - esac +# serial 6 ltoptions.m4 - # See if GNU ld supports shared libraries. - case $host_os in - aix3* | aix4* | aix5*) - # On AIX/PPC, the GNU linker is very broken - if test "$host_cpu" != ia64; then - _LT_AC_TAGVAR(ld_shlibs, $1)=no - cat <&2 +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) -*** Warning: the GNU linker, at least up to release 2.9.1, is reported -*** to be unable to reliably create shared libraries on AIX. -*** Therefore, libtool is disabling shared libraries support. If you -*** really care for shared libraries, you may want to modify your PATH -*** so that a non-GNU linker is found, and then restart. -EOF - fi - ;; +# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) +# ------------------------------------------ +m4_define([_LT_MANGLE_OPTION], +[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) - amigaos*) - _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - - # Samuel A. Falvo II reports - # that the semantics of dynamic libraries on AmigaOS, at least up - # to version 4, is to share data among multiple programs linked - # with the same dynamic library. Since this doesn't match the - # behavior of shared libraries on other platforms, we can't use - # them. - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - beos*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; +# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) +# --------------------------------------- +# Set option OPTION-NAME for macro MACRO-NAME, and if there is a +# matching handler defined, dispatch to it. Other OPTION-NAMEs are +# saved as a flag. +m4_define([_LT_SET_OPTION], +[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl +m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), + _LT_MANGLE_DEFUN([$1], [$2]), + [m4_warning([Unknown $1 option `$2'])])[]dnl +]) - cygwin* | mingw* | pw32*) - # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, - # as there is no search path for DLLs. - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_AC_TAGVAR(always_export_symbols, $1)=no - _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]] /s/.* \([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]] /s/.* //'\'' | sort | uniq > $export_symbols' - if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; +# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) +# ------------------------------------------------------------ +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +m4_define([_LT_IF_OPTION], +[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) - interix3*) - _LT_AC_TAGVAR(hardcode_direct, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - linux*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - tmp_addflag= - case $cc_basename,$host_cpu in - pgcc*) # Portland Group C compiler - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag' - ;; - pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag -Mnomain' ;; - ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 - tmp_addflag=' -i_dynamic' ;; - efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 - tmp_addflag=' -i_dynamic -nofor_main' ;; - ifc* | ifort*) # Intel Fortran compiler - tmp_addflag=' -nofor_main' ;; - esac - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' +# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) +# ------------------------------------------------------- +# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME +# are set. +m4_define([_LT_UNLESS_OPTIONS], +[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), + [m4_define([$0_found])])])[]dnl +m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 +])[]dnl +]) - if test $supports_anon_versioning = yes; then - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - $echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' - fi - else - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' - wlarc= - else - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - fi - ;; +# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) +# ---------------------------------------- +# OPTION-LIST is a space-separated list of Libtool options associated +# with MACRO-NAME. If any OPTION has a matching handler declared with +# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about +# the unknown option and exit. +m4_defun([_LT_SET_OPTIONS], +[# Set options +m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [_LT_SET_OPTION([$1], _LT_Option)]) + +m4_if([$1],[LT_INIT],[ + dnl + dnl Simply set some default values (i.e off) if boolean options were not + dnl specified: + _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no + ]) + _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no + ]) + dnl + dnl If no reference was made to various pairs of opposing options, then + dnl we run the default mode handler for the pair. For example, if neither + dnl `shared' nor `disable-shared' was passed, we enable building of shared + dnl archives by default: + _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) + _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], + [_LT_ENABLE_FAST_INSTALL]) + ]) +])# _LT_SET_OPTIONS - solaris*) - if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then - _LT_AC_TAGVAR(ld_shlibs, $1)=no - cat <&2 -*** Warning: The releases 2.8.* of the GNU linker cannot reliably -*** create shared libraries on Solaris systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.9.1 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. -EOF - elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; +# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) +# ----------------------------------------- +m4_define([_LT_MANGLE_DEFUN], +[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) - sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) - case `$LD -v 2>&1` in - *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) - _LT_AC_TAGVAR(ld_shlibs, $1)=no - cat <<_LT_EOF 1>&2 -*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not -*** reliably create shared libraries on SCO systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.16.91.0.3 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. +# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) +# ----------------------------------------------- +m4_define([LT_OPTION_DEFINE], +[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl +])# LT_OPTION_DEFINE + + +# dlopen +# ------ +LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes +]) -_LT_EOF - ;; - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname,-retain-symbols-file,$export_symbols -o $lib' - else - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - ;; +AU_DEFUN([AC_LIBTOOL_DLOPEN], +[_LT_SET_OPTION([LT_INIT], [dlopen]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `dlopen' option into LT_INIT's first parameter.]) +]) - sunos4*) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' - wlarc= - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - _LT_AC_TAGVAR(ld_shlibs, $1)=no - fi - ;; - esac - if test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no; then - runpath_var= - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)= - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)= - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)= - fi - else - # PORTME fill in a description of your system's linker (not GNU ld) - case $host_os in - aix3*) - _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_AC_TAGVAR(always_export_symbols, $1)=yes - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' - # Note: this linker hardcodes the directories in LIBPATH if there - # are no directories specified by -L. - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then - # Neither direct hardcoding nor static linking is supported with a - # broken collect2. - _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported - fi - ;; +# win32-dll +# --------- +# Declare package support for building win32 dll's. +LT_OPTION_DEFINE([LT_INIT], [win32-dll], +[enable_win32_dll=yes - aix4* | aix5*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - if $NM -V 2>&1 | grep 'GNU' > /dev/null; then - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols' - else - _LT_AC_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\[$]2 == "T") || (\[$]2 == "D") || (\[$]2 == "B")) && ([substr](\[$]3,1,1) != ".")) { print \[$]3 } }'\'' | sort -u > $export_symbols' - fi - aix_use_runtimelinking=no +case $host in +*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*) + AC_CHECK_TOOL(AS, as, false) + AC_CHECK_TOOL(DLLTOOL, dlltool, false) + AC_CHECK_TOOL(OBJDUMP, objdump, false) + ;; +esac - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[[23]]|aix4.[[23]].*|aix5*) - for ld_flag in $LDFLAGS; do - if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then - aix_use_runtimelinking=yes - break - fi - done - ;; - esac +test -z "$AS" && AS=as +_LT_DECL([], [AS], [0], [Assembler program])dnl - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl +])# win32-dll - _LT_AC_TAGVAR(archive_cmds, $1)='' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':' - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes +AU_DEFUN([AC_LIBTOOL_WIN32_DLL], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +_LT_SET_OPTION([LT_INIT], [win32-dll]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `win32-dll' option into LT_INIT's first parameter.]) +]) - if test "$GCC" = yes; then - case $host_os in aix4.[[012]]|aix4.[[012]].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && \ - strings "$collect2name" | grep resolve_lib_name >/dev/null - then - # We have reworked collect2 - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - else - # We have old collect2 - _LT_AC_TAGVAR(hardcode_direct, $1)=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)= - fi - ;; - esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to export. - _LT_AC_TAGVAR(always_export_symbols, $1)=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - _LT_AC_TAGVAR(allow_undefined_flag, $1)='-berok' - # Determine the default libpath from the value encoded in an empty executable. - _LT_AC_SYS_LIBPATH_AIX - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" - _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' - _LT_AC_TAGVAR(allow_undefined_flag, $1)="-z nodefs" - _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an empty executable. - _LT_AC_SYS_LIBPATH_AIX - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - _LT_AC_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' - # Exported symbols can be pulled into shared objects from archives - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='$convenience' - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes - # This is similar to how AIX traditionally builds its shared libraries. - _LT_AC_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + +# _LT_ENABLE_SHARED([DEFAULT]) +# ---------------------------- +# implement the --enable-shared flag, and supports the `shared' and +# `disable-shared' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_SHARED], +[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([shared], + [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], + [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_shared=yes fi - fi + done + IFS="$lt_save_ifs" ;; + esac], + [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) - amigaos*) - _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - # see comment about different semantics on the GNU ld section - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; + _LT_DECL([build_libtool_libs], [enable_shared], [0], + [Whether or not to build shared libraries]) +])# _LT_ENABLE_SHARED - bsdi[[45]]*) - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic - ;; +LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) - cygwin* | mingw* | pw32*) - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' - _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" - # FIXME: Setting linknames here is a bad hack. - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames=' - # The linker will automatically build a .lib file if we build a DLL. - _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)='true' - # FIXME: Should let the user specify the lib program. - _LT_AC_TAGVAR(old_archive_cmds, $1)='lib /OUT:$oldlib$oldobjs$old_deplibs' - _LT_AC_TAGVAR(fix_srcfile_path, $1)='`cygpath -w "$srcfile"`' - _LT_AC_TAGVAR(enable_shared_with_static_runtimes, $1)=yes - ;; +# Old names: +AC_DEFUN([AC_ENABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) +]) - darwin* | rhapsody*) - case $host_os in - rhapsody* | darwin1.[[012]]) - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-undefined ${wl}suppress' - ;; - *) # Darwin 1.3 on - if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - else - case ${MACOSX_DEPLOYMENT_TARGET} in - 10.[[012]]) - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - ;; - 10.*) - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-undefined ${wl}dynamic_lookup' - ;; - esac - fi - ;; - esac - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_AC_TAGVAR(hardcode_direct, $1)=no - _LT_AC_TAGVAR(hardcode_automatic, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=unsupported - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='' - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes - if test "$GCC" = yes ; then - output_verbose_link_cmd='echo' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' - _LT_AC_TAGVAR(module_cmds, $1)='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - else - case $cc_basename in - xlc*) - output_verbose_link_cmd='echo' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}`echo $rpath/$soname` $verstring' - _LT_AC_TAGVAR(module_cmds, $1)='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}$rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - _LT_AC_TAGVAR(module_expsym_cmds, $1)='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - ;; - *) - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - esac - fi - ;; +AC_DEFUN([AC_DISABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], [disable-shared]) +]) - dgux*) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; +AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) +AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) - freebsd1*) - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_SHARED], []) +dnl AC_DEFUN([AM_DISABLE_SHARED], []) - # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor - # support. Future versions do this automatically, but an explicit c++rt0.o - # does not break anything, and helps significantly (at the cost of a little - # extra space). - freebsd2.2*) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - # Unfortunately, older versions of FreeBSD 2 do not have this feature. - freebsd2*) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - # FreeBSD 3 and greater uses gcc -shared to do shared libraries. - freebsd* | kfreebsd*-gnu | dragonfly*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no +# _LT_ENABLE_STATIC([DEFAULT]) +# ---------------------------- +# implement the --enable-static flag, and support the `static' and +# `disable-static' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_STATIC], +[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([static], + [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], + [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS="$lt_save_ifs" ;; + esac], + [enable_static=]_LT_ENABLE_STATIC_DEFAULT) - hpux9*) - if test "$GCC" = yes; then - _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - _LT_AC_TAGVAR(archive_cmds, $1)='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - fi - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_AC_TAGVAR(hardcode_direct, $1)=yes + _LT_DECL([build_old_libs], [enable_static], [0], + [Whether or not to build static libraries]) +])# _LT_ENABLE_STATIC - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - ;; +LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) - hpux10*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - else - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' - fi - if test "$with_gnu_ld" = no; then - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: +# Old names: +AC_DEFUN([AC_ENABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) +]) - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' +AC_DEFUN([AC_DISABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], [disable-static]) +]) - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - fi - ;; +AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) +AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) - hpux11*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - case $host_cpu in - hppa*64*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - else - case $host_cpu in - hppa*64*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - fi - if test "$with_gnu_ld" = no; then - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_STATIC], []) +dnl AC_DEFUN([AM_DISABLE_STATIC], []) - case $host_cpu in - hppa*64*|ia64*) - _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' - _LT_AC_TAGVAR(hardcode_direct, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - *) - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - ;; - esac - fi - ;; - irix5* | irix6* | nonstopux*) - if test "$GCC" = yes; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir' - fi - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes +# _LT_ENABLE_FAST_INSTALL([DEFAULT]) +# ---------------------------------- +# implement the --enable-fast-install flag, and support the `fast-install' +# and `disable-fast-install' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_FAST_INSTALL], +[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([fast-install], + [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], + [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$lt_save_ifs" ;; + esac], + [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out - else - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF - fi - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; +_LT_DECL([fast_install], [enable_fast_install], [0], + [Whether or not to optimize for fast installation])dnl +])# _LT_ENABLE_FAST_INSTALL + +LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) + +# Old names: +AU_DEFUN([AC_ENABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the `fast-install' option into LT_INIT's first parameter.]) +]) - newsos6) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; +AU_DEFUN([AC_DISABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], [disable-fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the `disable-fast-install' option into LT_INIT's first parameter.]) +]) - openbsd*) - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' - else - case $host_os in - openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - ;; - *) - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' - ;; - esac - fi - ;; +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) +dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) - os2*) - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - _LT_AC_TAGVAR(allow_undefined_flag, $1)=unsupported - _LT_AC_TAGVAR(archive_cmds, $1)='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' - _LT_AC_TAGVAR(old_archive_From_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' - ;; - osf3*) - if test "$GCC" = yes; then - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - fi - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - ;; +# _LT_WITH_PIC([MODE]) +# -------------------- +# implement the --with-pic flag, and support the `pic-only' and `no-pic' +# LT_INIT options. +# MODE is either `yes' or `no'. If omitted, it defaults to `both'. +m4_define([_LT_WITH_PIC], +[AC_ARG_WITH([pic], + [AS_HELP_STRING([--with-pic], + [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], + [pic_mode="$withval"], + [pic_mode=default]) - osf4* | osf5*) # as osf3* with the addition of -msym flag - if test "$GCC" = yes; then - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' - else - _LT_AC_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~ - $LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib~$rm $lib.exp' +test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) - # Both c and cxx compiler support -rpath directly - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' - fi - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=: - ;; +_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl +])# _LT_WITH_PIC - solaris*) - _LT_AC_TAGVAR(no_undefined_flag, $1)=' -z text' - if test "$GCC" = yes; then - wlarc='${wl}' - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp' - else - wlarc='' - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' - fi - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - case $host_os in - solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; - *) - # The compiler driver will combine linker options so we - # cannot just pass the convience library names through - # without $wl, iff we do not link with $LD. - # Luckily, gcc supports the same syntax we need for Sun Studio. - # Supported since Solaris 2.6 (maybe 2.5.1?) - case $wlarc in - '') - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' ;; - *) - _LT_AC_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}-z ${wl}defaultextract' ;; - esac ;; - esac - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes - ;; +LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) - sunos4*) - if test "x$host_vendor" = xsequent; then - # Use $CC to link under sequent, because it throws in some extra .o - # files that make .init and .fini sections work. - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' - else - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' - fi - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes - _LT_AC_TAGVAR(hardcode_minus_L, $1)=yes - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; +# Old name: +AU_DEFUN([AC_LIBTOOL_PICMODE], +[_LT_SET_OPTION([LT_INIT], [pic-only]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `pic-only' option into LT_INIT's first parameter.]) +]) - sysv4) - case $host_vendor in - sni) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_direct, $1)=yes # is this really true??? - ;; - siemens) - ## LD is ld it makes a PLAMLIB - ## CC just makes a GrossModule. - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' - _LT_AC_TAGVAR(hardcode_direct, $1)=no - ;; - motorola) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie - ;; - esac - runpath_var='LD_RUN_PATH' - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) - sysv4.3*) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' - ;; - sysv4*MP*) - if test -d /usr/nec; then - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - runpath_var=LD_RUN_PATH - hardcode_runpath_var=yes - _LT_AC_TAGVAR(ld_shlibs, $1)=yes - fi - ;; +m4_define([_LTDL_MODE], []) +LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], + [m4_define([_LTDL_MODE], [nonrecursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [recursive], + [m4_define([_LTDL_MODE], [recursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [subproject], + [m4_define([_LTDL_MODE], [subproject])]) - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7*) - _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - runpath_var='LD_RUN_PATH' +m4_define([_LTDL_TYPE], []) +LT_OPTION_DEFINE([LTDL_INIT], [installable], + [m4_define([_LTDL_TYPE], [installable])]) +LT_OPTION_DEFINE([LTDL_INIT], [convenience], + [m4_define([_LTDL_TYPE], [convenience])]) - if test "$GCC" = yes; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - else - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; +# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- +# +# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - _LT_AC_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' - _LT_AC_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' - _LT_AC_TAGVAR(hardcode_libdir_separator, $1)=':' - _LT_AC_TAGVAR(link_all_deplibs, $1)=yes - _LT_AC_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' - runpath_var='LD_RUN_PATH' +# serial 6 ltsugar.m4 - if test "$GCC" = yes; then - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - else - _LT_AC_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - _LT_AC_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) - uts4*) - _LT_AC_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - _LT_AC_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' - _LT_AC_TAGVAR(hardcode_shlibpath_var, $1)=no - ;; - *) - _LT_AC_TAGVAR(ld_shlibs, $1)=no - ;; - esac - fi -]) -AC_MSG_RESULT([$_LT_AC_TAGVAR(ld_shlibs, $1)]) -test "$_LT_AC_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no +# lt_join(SEP, ARG1, [ARG2...]) +# ----------------------------- +# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their +# associated separator. +# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier +# versions in m4sugar had bugs. +m4_define([lt_join], +[m4_if([$#], [1], [], + [$#], [2], [[$2]], + [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) +m4_define([_lt_join], +[m4_if([$#$2], [2], [], + [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) -# -# Do we need to explicitly link libc? -# -case "x$_LT_AC_TAGVAR(archive_cmds_need_lc, $1)" in -x|xyes) - # Assume -lc should be added - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes - if test "$enable_shared" = yes && test "$GCC" = yes; then - case $_LT_AC_TAGVAR(archive_cmds, $1) in - *'~'*) - # FIXME: we may have to deal with multi-command sequences. - ;; - '$CC '*) - # Test whether the compiler implicitly links with -lc since on some - # systems, -lgcc has to come before -lc. If gcc already passes -lc - # to ld, don't add -lc before -lgcc. - AC_MSG_CHECKING([whether -lc should be explicitly linked in]) - $rm conftest* - printf "$lt_simple_compile_test_code" > conftest.$ac_ext +# lt_car(LIST) +# lt_cdr(LIST) +# ------------ +# Manipulate m4 lists. +# These macros are necessary as long as will still need to support +# Autoconf-2.59 which quotes differently. +m4_define([lt_car], [[$1]]) +m4_define([lt_cdr], +[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], + [$#], 1, [], + [m4_dquote(m4_shift($@))])]) +m4_define([lt_unquote], $1) + + +# lt_append(MACRO-NAME, STRING, [SEPARATOR]) +# ------------------------------------------ +# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. +# Note that neither SEPARATOR nor STRING are expanded; they are appended +# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). +# No SEPARATOR is output if MACRO-NAME was previously undefined (different +# than defined and empty). +# +# This macro is needed until we can rely on Autoconf 2.62, since earlier +# versions of m4sugar mistakenly expanded SEPARATOR but not STRING. +m4_define([lt_append], +[m4_define([$1], + m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) - if AC_TRY_EVAL(ac_compile) 2>conftest.err; then - soname=conftest - lib=conftest - libobjs=conftest.$ac_objext - deplibs= - wl=$_LT_AC_TAGVAR(lt_prog_compiler_wl, $1) - pic_flag=$_LT_AC_TAGVAR(lt_prog_compiler_pic, $1) - compiler_flags=-v - linker_flags=-v - verstring= - output_objdir=. - libname=conftest - lt_save_allow_undefined_flag=$_LT_AC_TAGVAR(allow_undefined_flag, $1) - _LT_AC_TAGVAR(allow_undefined_flag, $1)= - if AC_TRY_EVAL(_LT_AC_TAGVAR(archive_cmds, $1) 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) - then - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=no - else - _LT_AC_TAGVAR(archive_cmds_need_lc, $1)=yes - fi - _LT_AC_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag - else - cat conftest.err 1>&5 - fi - $rm conftest* - AC_MSG_RESULT([$_LT_AC_TAGVAR(archive_cmds_need_lc, $1)]) - ;; - esac - fi - ;; -esac -])# AC_LIBTOOL_PROG_LD_SHLIBS -# _LT_AC_FILE_LTDLL_C -# ------------------- -# Be careful that the start marker always follows a newline. -AC_DEFUN([_LT_AC_FILE_LTDLL_C], [ -# /* ltdll.c starts here */ -# #define WIN32_LEAN_AND_MEAN -# #include -# #undef WIN32_LEAN_AND_MEAN -# #include -# -# #ifndef __CYGWIN__ -# # ifdef __CYGWIN32__ -# # define __CYGWIN__ __CYGWIN32__ -# # endif -# #endif -# -# #ifdef __cplusplus -# extern "C" { -# #endif -# BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved); -# #ifdef __cplusplus -# } -# #endif -# -# #ifdef __CYGWIN__ -# #include -# DECLARE_CYGWIN_DLL( DllMain ); -# #endif -# HINSTANCE __hDllInstance_base; -# -# BOOL APIENTRY -# DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved) -# { -# __hDllInstance_base = hInst; -# return TRUE; -# } -# /* ltdll.c ends here */ -])# _LT_AC_FILE_LTDLL_C +# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) +# ---------------------------------------------------------- +# Produce a SEP delimited list of all paired combinations of elements of +# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list +# has the form PREFIXmINFIXSUFFIXn. +# Needed until we can rely on m4_combine added in Autoconf 2.62. +m4_define([lt_combine], +[m4_if(m4_eval([$# > 3]), [1], + [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl +[[m4_foreach([_Lt_prefix], [$2], + [m4_foreach([_Lt_suffix], + ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, + [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) + + +# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) +# ----------------------------------------------------------------------- +# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited +# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. +m4_define([lt_if_append_uniq], +[m4_ifdef([$1], + [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], + [lt_append([$1], [$2], [$3])$4], + [$5])], + [lt_append([$1], [$2], [$3])$4])]) -# _LT_AC_TAGVAR(VARNAME, [TAGNAME]) -# --------------------------------- -AC_DEFUN([_LT_AC_TAGVAR], [ifelse([$2], [], [$1], [$1_$2])]) +# lt_dict_add(DICT, KEY, VALUE) +# ----------------------------- +m4_define([lt_dict_add], +[m4_define([$1($2)], [$3])]) -# old names -AC_DEFUN([AM_PROG_LIBTOOL], [AC_PROG_LIBTOOL]) -AC_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) -AC_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) -AC_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) -AC_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) -AC_DEFUN([AM_PROG_LD], [AC_PROG_LD]) -AC_DEFUN([AM_PROG_NM], [AC_PROG_NM]) +# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) +# -------------------------------------------- +m4_define([lt_dict_add_subkey], +[m4_define([$1($2:$3)], [$4])]) -# This is just to silence aclocal about the macro not being used -ifelse([AC_DISABLE_FAST_INSTALL]) -AC_DEFUN([LT_AC_PROG_GCJ], -[AC_CHECK_TOOL(GCJ, gcj, no) - test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" - AC_SUBST(GCJFLAGS) +# lt_dict_fetch(DICT, KEY, [SUBKEY]) +# ---------------------------------- +m4_define([lt_dict_fetch], +[m4_ifval([$3], + m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), + m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) + + +# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) +# ----------------------------------------------------------------- +m4_define([lt_if_dict_fetch], +[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], + [$5], + [$6])]) + + +# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) +# -------------------------------------------------------------- +m4_define([lt_dict_filter], +[m4_if([$5], [], [], + [lt_join(m4_quote(m4_default([$4], [[, ]])), + lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), + [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl ]) -AC_DEFUN([LT_AC_PROG_RC], -[AC_CHECK_TOOL(RC, windres, no) -]) +# ltversion.m4 -- version numbers -*- Autoconf -*- +# +# Copyright (C) 2004 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. -# NOTE: This macro has been submitted for inclusion into # -# GNU Autoconf as AC_PROG_SED. When it is available in # -# a released version of Autoconf we should remove this # -# macro and use it instead. # -# LT_AC_PROG_SED -# -------------- -# Check for a fully-functional sed program, that truncates -# as few characters as possible. Prefer GNU sed if found. -AC_DEFUN([LT_AC_PROG_SED], -[AC_MSG_CHECKING([for a sed that does not truncate output]) -AC_CACHE_VAL(lt_cv_path_SED, -[# Loop through the user's path and test for sed and gsed. -# Then use that list of sed's as ones to test for truncation. -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for lt_ac_prog in sed gsed; do - for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then - lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" - fi - done - done -done -lt_ac_max=0 -lt_ac_count=0 -# Add /usr/xpg4/bin/sed as it is typically found on Solaris -# along with /bin/sed that truncates output. -for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do - test ! -f $lt_ac_sed && continue - cat /dev/null > conftest.in - lt_ac_count=0 - echo $ECHO_N "0123456789$ECHO_C" >conftest.in - # Check for GNU sed and select it if it is found. - if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then - lt_cv_path_SED=$lt_ac_sed - break - fi - while true; do - cat conftest.in conftest.in >conftest.tmp - mv conftest.tmp conftest.in - cp conftest.in conftest.nl - echo >>conftest.nl - $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break - cmp -s conftest.out conftest.nl || break - # 10000 chars as input seems more than enough - test $lt_ac_count -gt 10 && break - lt_ac_count=`expr $lt_ac_count + 1` - if test $lt_ac_count -gt $lt_ac_max; then - lt_ac_max=$lt_ac_count - lt_cv_path_SED=$lt_ac_sed - fi - done -done -]) -SED=$lt_cv_path_SED -AC_MSG_RESULT([$SED]) +# Generated from ltversion.in. + +# serial 3017 ltversion.m4 +# This file is part of GNU Libtool + +m4_define([LT_PACKAGE_VERSION], [2.2.6b]) +m4_define([LT_PACKAGE_REVISION], [1.3017]) + +AC_DEFUN([LTVERSION_VERSION], +[macro_version='2.2.6b' +macro_revision='1.3017' +_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) +_LT_DECL(, macro_revision, 0) ]) -# Copyright (C) 2002, 2003, 2005, 2006 Free Software Foundation, Inc. +# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- +# +# Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004. +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 4 lt~obsolete.m4 + +# These exist entirely to fool aclocal when bootstrapping libtool. +# +# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) +# which have later been changed to m4_define as they aren't part of the +# exported API, or moved to Autoconf or Automake where they belong. +# +# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN +# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us +# using a macro with the same name in our local m4/libtool.m4 it'll +# pull the old libtool.m4 in (it doesn't see our shiny new m4_define +# and doesn't know about Autoconf macros at all.) +# +# So we provide this file, which has a silly filename so it's always +# included after everything else. This provides aclocal with the +# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything +# because those macros already exist, or will be overwritten later. +# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. +# +# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. +# Yes, that means every name once taken will need to remain here until +# we give up compatibility with versions before 1.7, at which point +# we need to keep only those names which we still refer to. + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) + +m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) +m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) +m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) +m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) +m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) +m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) +m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) +m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) +m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) +m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) +m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) +m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) +m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) +m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) +m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) +m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) +m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) +m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) +m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) +m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) +m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) +m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) +m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) +m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) +m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) +m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) +m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) +m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) +m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) +m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) +m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) +m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) +m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) +m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) +m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) +m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) +m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) +m4_ifndef([AC_LIBTOOL_RC], [AC_DEFUN([AC_LIBTOOL_RC])]) +m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) +m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) +m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) +m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) +m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) +m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) +m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) +m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) + +# Copyright (C) 2002, 2003, 2005, 2006, 2007, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -6405,10 +7977,10 @@ # generated from the m4 files accompanying Automake X.Y. # (This private macro should not be called outside this file.) AC_DEFUN([AM_AUTOMAKE_VERSION], -[am__api_version='1.10' +[am__api_version='1.11' dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to dnl require some minimum version. Point them to the right macro. -m4_if([$1], [1.10], [], +m4_if([$1], [1.11.1], [], [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl ]) @@ -6422,10 +7994,12 @@ # AM_SET_CURRENT_AUTOMAKE_VERSION # ------------------------------- # Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. -# This function is AC_REQUIREd by AC_INIT_AUTOMAKE. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], -[AM_AUTOMAKE_VERSION([1.10])dnl -_AM_AUTOCONF_VERSION(m4_PACKAGE_VERSION)]) +[AM_AUTOMAKE_VERSION([1.11.1])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) # AM_AUX_DIR_EXPAND -*- Autoconf -*- @@ -6482,14 +8056,14 @@ # AM_CONDITIONAL -*- Autoconf -*- -# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006 +# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. -# serial 8 +# serial 9 # AM_CONDITIONAL(NAME, SHELL-CONDITION) # ------------------------------------- @@ -6502,6 +8076,7 @@ AC_SUBST([$1_FALSE])dnl _AM_SUBST_NOTMAKE([$1_TRUE])dnl _AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl if $2; then $1_TRUE= $1_FALSE='#' @@ -6515,14 +8090,14 @@ Usually this means the macro was only invoked conditionally.]]) fi])]) -# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2009 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. -# serial 9 +# serial 10 # There are a few dirty hacks below to avoid letting `AC_PROG_CC' be # written in clear, in which case automake, when reading aclocal.m4, @@ -6579,6 +8154,16 @@ if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` fi + am__universal=false + m4_case([$1], [CC], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac], + [CXX], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac]) + for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and @@ -6596,7 +8181,17 @@ done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; nosideeffect) # after this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested @@ -6606,19 +8201,23 @@ break fi ;; + msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; none) break ;; esac - # We check with `-c' and `-o' for the sake of the "dashmstdout" - # mode. It turns out that the SunPro C++ compiler does not properly - # handle `-M -o', and we need to detect this. if depmode=$depmode \ - source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ - $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && - grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message @@ -6675,57 +8274,68 @@ # Generate code to set up dependency tracking. -*- Autoconf -*- -# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005 +# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. -#serial 3 +#serial 5 # _AM_OUTPUT_DEPENDENCY_COMMANDS # ------------------------------ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], -[for mf in $CONFIG_FILES; do - # Strip MF so we end up with the name of the file. - mf=`echo "$mf" | sed -e 's/:.*$//'` - # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named `Makefile.in', but - # some people rename them; so instead we look at the file content. - # Grep'ing the first line is not enough: some people post-process - # each Makefile.in and add a new line on top of each file to say so. - # Grep'ing the whole file is not good either: AIX grep has a line - # limit of 2048, but all sed's we know have understand at least 4000. - if sed 10q "$mf" | grep '^#.*generated by automake' > /dev/null 2>&1; then - dirpart=`AS_DIRNAME("$mf")` - else - continue - fi - # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running `make'. - DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` - test -z "$DEPDIR" && continue - am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "am__include" && continue - am__quote=`sed -n 's/^am__quote = //p' < "$mf"` - # When using ansi2knr, U may be empty or an underscore; expand it - U=`sed -n 's/^U = //p' < "$mf"` - # Find all dependency output files, they are included files with - # $(DEPDIR) in their names. We invoke sed twice because it is the - # simplest approach to changing $(DEPDIR) to its actual value in the - # expansion. - for file in `sed -n " - s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do - # Make sure the directory exists. - test -f "$dirpart/$file" && continue - fdir=`AS_DIRNAME(["$file"])` - AS_MKDIR_P([$dirpart/$fdir]) - # echo "creating $dirpart/$file" - echo '# dummy' > "$dirpart/$file" +[{ + # Autoconf 2.62 quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`AS_DIRNAME("$mf")` + else + continue + fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`AS_DIRNAME(["$file"])` + AS_MKDIR_P([$dirpart/$fdir]) + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done done -done +} ])# _AM_OUTPUT_DEPENDENCY_COMMANDS @@ -6757,13 +8367,13 @@ # Do all the work for Automake. -*- Autoconf -*- # Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, -# 2005, 2006 Free Software Foundation, Inc. +# 2005, 2006, 2008, 2009 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. -# serial 12 +# serial 16 # This macro actually does too much. Some checks are only needed if # your package does certain things. But this isn't really a big deal. @@ -6780,7 +8390,7 @@ # arguments mandatory, and then we can depend on a new Autoconf # release and drop the old call support. AC_DEFUN([AM_INIT_AUTOMAKE], -[AC_PREREQ([2.60])dnl +[AC_PREREQ([2.62])dnl dnl Autoconf wants to disallow AM_ names. We explicitly allow dnl the ones we care about. m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl @@ -6831,8 +8441,8 @@ AM_MISSING_PROG(AUTOMAKE, automake-${am__api_version}) AM_MISSING_PROG(AUTOHEADER, autoheader) AM_MISSING_PROG(MAKEINFO, makeinfo) -AM_PROG_INSTALL_SH -AM_PROG_INSTALL_STRIP +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl AC_REQUIRE([AM_PROG_MKDIR_P])dnl # We need awk for the "check" target. The system "awk" is bad on # some platforms. @@ -6840,24 +8450,37 @@ AC_REQUIRE([AC_PROG_MAKE_SET])dnl AC_REQUIRE([AM_SET_LEADING_DOT])dnl _AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], - [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], - [_AM_PROG_TAR([v7])])]) + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) _AM_IF_OPTION([no-dependencies],, [AC_PROVIDE_IFELSE([AC_PROG_CC], - [_AM_DEPENDENCIES(CC)], - [define([AC_PROG_CC], - defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl + [_AM_DEPENDENCIES(CC)], + [define([AC_PROG_CC], + defn([AC_PROG_CC])[_AM_DEPENDENCIES(CC)])])dnl AC_PROVIDE_IFELSE([AC_PROG_CXX], - [_AM_DEPENDENCIES(CXX)], - [define([AC_PROG_CXX], - defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl + [_AM_DEPENDENCIES(CXX)], + [define([AC_PROG_CXX], + defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl AC_PROVIDE_IFELSE([AC_PROG_OBJC], - [_AM_DEPENDENCIES(OBJC)], - [define([AC_PROG_OBJC], - defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl + [_AM_DEPENDENCIES(OBJC)], + [define([AC_PROG_OBJC], + defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl ]) +_AM_IF_OPTION([silent-rules], [AC_REQUIRE([AM_SILENT_RULES])])dnl +dnl The `parallel-tests' driver may need to know about EXEEXT, so add the +dnl `am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This macro +dnl is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl ]) +dnl Hook into `_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + # When config.status generates a header, we must update the stamp-h file. # This file resides in the same directory as the config header @@ -6868,18 +8491,19 @@ # our stamp files there. AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], [# Compute $1's index in $config_headers. +_am_arg=$1 _am_stamp_count=1 for _am_header in $config_headers :; do case $_am_header in - $1 | $1:* ) + $_am_arg | $_am_arg:* ) break ;; * ) _am_stamp_count=`expr $_am_stamp_count + 1` ;; esac done -echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count]) +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) -# Copyright (C) 2001, 2003, 2005 Free Software Foundation, Inc. +# Copyright (C) 2001, 2003, 2005, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, @@ -6890,7 +8514,14 @@ # Define $install_sh. AC_DEFUN([AM_PROG_INSTALL_SH], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl -install_sh=${install_sh-"\$(SHELL) $am_aux_dir/install-sh"} +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi AC_SUBST(install_sh)]) # Copyright (C) 2003, 2005 Free Software Foundation, Inc. @@ -6916,13 +8547,13 @@ # Check to see how 'make' treats includes. -*- Autoconf -*- -# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. +# Copyright (C) 2001, 2002, 2003, 2005, 2009 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. -# serial 3 +# serial 4 # AM_MAKE_INCLUDE() # ----------------- @@ -6931,7 +8562,7 @@ [am_make=${MAKE-make} cat > confinc << 'END' am__doit: - @echo done + @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. @@ -6941,24 +8572,24 @@ _am_result=none # First try GNU make style include. echo "include confinc" > confmf -# We grep out `Entering directory' and `Leaving directory' -# messages which can occur if `w' ends up in MAKEFLAGS. -# In particular we don't look at `^make:' because GNU make might -# be invoked under some other name (usually "gmake"), in which -# case it prints its new name instead of `make'. -if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then - am__include=include - am__quote= - _am_result=GNU -fi +# Ignore all kinds of additional output from `make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf - if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then - am__include=.include - am__quote="\"" - _am_result=BSD - fi + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac fi AC_SUBST([am__include]) AC_SUBST([am__quote]) @@ -6968,14 +8599,14 @@ # Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- -# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005 +# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. -# serial 5 +# serial 6 # AM_MISSING_PROG(NAME, PROGRAM) # ------------------------------ @@ -6992,7 +8623,14 @@ AC_DEFUN([AM_MISSING_HAS_RUN], [AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl AC_REQUIRE_AUX_FILE([missing])dnl -test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi # Use eval to expand $SHELL if eval "$MISSING --run true"; then am_missing_run="$MISSING --run " @@ -7030,13 +8668,13 @@ # Helper functions for option handling. -*- Autoconf -*- -# Copyright (C) 2001, 2002, 2003, 2005 Free Software Foundation, Inc. +# Copyright (C) 2001, 2002, 2003, 2005, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. -# serial 3 +# serial 4 # _AM_MANGLE_OPTION(NAME) # ----------------------- @@ -7053,7 +8691,7 @@ # ---------------------------------- # OPTIONS is a space-separated list of Automake options. AC_DEFUN([_AM_SET_OPTIONS], -[AC_FOREACH([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) # _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) # ------------------------------------------- @@ -7080,14 +8718,14 @@ # Check to make sure that the build environment is sane. -*- Autoconf -*- -# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005 +# Copyright (C) 1996, 1997, 2000, 2001, 2003, 2005, 2008 # Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. -# serial 4 +# serial 5 # AM_SANITY_CHECK # --------------- @@ -7096,16 +8734,29 @@ # Just in case sleep 1 echo timestamp > conftest.file +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: `$srcdir']);; +esac + # Do `set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( - set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$[*]" = "X"; then # -L didn't work. - set X `ls -t $srcdir/configure conftest.file` + set X `ls -t "$srcdir/configure" conftest.file` fi rm -f conftest.file if test "$[*]" != "X $srcdir/configure conftest.file" \ @@ -7158,18 +8809,25 @@ INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" AC_SUBST([INSTALL_STRIP_PROGRAM])]) -# Copyright (C) 2006 Free Software Foundation, Inc. +# Copyright (C) 2006, 2008 Free Software Foundation, Inc. # # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, # with or without modifications, as long as this notice is preserved. +# serial 2 + # _AM_SUBST_NOTMAKE(VARIABLE) # --------------------------- -# Prevent Automake from outputing VARIABLE = @VARIABLE@ in Makefile.in. +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. # This macro is traced by Automake. AC_DEFUN([_AM_SUBST_NOTMAKE]) +# AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + # Check how to create a tarball. -*- Autoconf -*- # Copyright (C) 2004, 2005 Free Software Foundation, Inc. diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/autotools/ltmain.sh firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/autotools/ltmain.sh --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/autotools/ltmain.sh 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/autotools/ltmain.sh 2010-04-16 17:32:47.000000000 +0100 @@ -1,30 +1,174 @@ -# ltmain.sh - Provide generalized library-building support services. -# NOTE: Changing this file will not affect anything until you rerun configure. -# -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 -# Free Software Foundation, Inc. -# Originally by Gordon Matzigkeit , 1996 -# -# This program is free software; you can redistribute it and/or modify +# Generated from ltmain.m4sh. + +# ltmain.sh (GNU libtool) 2.2.6b +# Written by Gordon Matzigkeit , 1996 + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 2008 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # -# This program is distributed in the hope that it will be useful, but +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# along with GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, +# or obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Usage: $progname [OPTION]... [MODE-ARG]... +# +# Provide generalized library-building support services. # -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. +# --config show all configuration variables +# --debug enable verbose shell tracing +# -n, --dry-run display commands without modifying any files +# --features display basic configuration information and exit +# --mode=MODE use operation mode MODE +# --preserve-dup-deps don't remove duplicate dependency libraries +# --quiet, --silent don't print informational messages +# --tag=TAG use configuration variables from tag TAG +# -v, --verbose print informational messages (default) +# --version print version information +# -h, --help print short or long help message +# +# MODE must be one of the following: +# +# clean remove files from the build directory +# compile compile a source file into a libtool object +# execute automatically set library path, then run a program +# finish complete the installation of libtool libraries +# install install libraries or executables +# link create a library or an executable +# uninstall remove libraries from an installed directory +# +# MODE-ARGS vary depending on the MODE. +# Try `$progname --help --mode=MODE' for a more detailed description of MODE. +# +# When reporting a bug, please describe a test case to reproduce it and +# include the following information: +# +# host-triplet: $host +# shell: $SHELL +# compiler: $LTCC +# compiler flags: $LTCFLAGS +# linker: $LD (gnu? $with_gnu_ld) +# $progname: (GNU libtool) 2.2.6b +# automake: $automake_version +# autoconf: $autoconf_version +# +# Report bugs to . + +PROGRAM=ltmain.sh +PACKAGE=libtool +VERSION=2.2.6b +TIMESTAMP="" +package_revision=1.3017 + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# NLS nuisances: We save the old values to restore during execute mode. +# Only set LANG and LC_ALL to C if already set. +# These must not be set unconditionally because not all systems understand +# e.g. LANG=C (notably SCO). +lt_user_locale= +lt_safe_locale= +for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES +do + eval "if test \"\${$lt_var+set}\" = set; then + save_$lt_var=\$$lt_var + $lt_var=C + export $lt_var + lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" + lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" + fi" +done + +$lt_unset CDPATH + + + + + +: ${CP="cp -f"} +: ${ECHO="echo"} +: ${EGREP="/bin/grep -E"} +: ${FGREP="/bin/grep -F"} +: ${GREP="/bin/grep"} +: ${LN_S="ln -s"} +: ${MAKE="make"} +: ${MKDIR="mkdir"} +: ${MV="mv -f"} +: ${RM="rm -f"} +: ${SED="/bin/sed"} +: ${SHELL="${CONFIG_SHELL-/bin/sh}"} +: ${Xsed="$SED -e 1s/^X//"} -basename="s,^.*/,,g" +# Global variables: +EXIT_SUCCESS=0 +EXIT_FAILURE=1 +EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. +EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. + +exit_status=$EXIT_SUCCESS + +# Make sure IFS has a sensible default +lt_nl=' +' +IFS=" $lt_nl" + +dirname="s,/[^/]*$,," +basename="s,^.*/,," + +# func_dirname_and_basename file append nondir_replacement +# perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# Implementation must be kept synchronized with func_dirname +# and func_basename. For efficiency, we do not delegate to +# those functions but instead duplicate the functionality here. +func_dirname_and_basename () +{ + # Extract subdirectory from the argument. + func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi + func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"` +} + +# Generated shell functions inserted here. # Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh # is ksh but when the shell is invoked as "sh" and the current value of @@ -34,112 +178,174 @@ progpath="$0" # The name of this program: -progname=`echo "$progpath" | $SED $basename` -modename="$progname" +# In the unlikely event $progname began with a '-', it would play havoc with +# func_echo (imagine progname=-n), so we prepend ./ in that case: +func_dirname_and_basename "$progpath" +progname=$func_basename_result +case $progname in + -*) progname=./$progname ;; +esac -# Global variables: -EXIT_SUCCESS=0 -EXIT_FAILURE=1 +# Make sure we have an absolute path for reexecution: +case $progpath in + [\\/]*|[A-Za-z]:\\*) ;; + *[\\/]*) + progdir=$func_dirname_result + progdir=`cd "$progdir" && pwd` + progpath="$progdir/$progname" + ;; + *) + save_IFS="$IFS" + IFS=: + for progdir in $PATH; do + IFS="$save_IFS" + test -x "$progdir/$progname" && break + done + IFS="$save_IFS" + test -n "$progdir" || progdir=`pwd` + progpath="$progdir/$progname" + ;; +esac -PROGRAM=ltmain.sh -PACKAGE=libtool -VERSION=1.5.22 -TIMESTAMP=" (1.1220.2.365 2005/12/18 22:14:06)" +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed="${SED}"' -e 1s/^X//' +sed_quote_subst='s/\([`"$\\]\)/\\\1/g' -# See if we are running on zsh, and set the options which allow our -# commands through without removal of \ escapes. -if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST -fi +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' -# Check that we have a working $echo. -if test "X$1" = X--no-reexec; then - # Discard the --no-reexec flag, and continue. - shift -elif test "X$1" = X--fallback-echo; then - # Avoid inline document here, it may be left over - : -elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then - # Yippee, $echo works! - : -else - # Restart under the correct shell, and then maybe $echo will work. - exec $SHELL "$progpath" --no-reexec ${1+"$@"} -fi +# Re-`\' parameter expansions in output of double_quote_subst that were +# `\'-ed in input to the same. If an odd number of `\' preceded a '$' +# in input to double_quote_subst, that '$' was protected from expansion. +# Since each input `\' is now two `\'s, look for any number of runs of +# four `\'s followed by two `\'s and then a '$'. `\' that '$'. +bs='\\' +bs2='\\\\' +bs4='\\\\\\\\' +dollar='\$' +sed_double_backslash="\ + s/$bs4/&\\ +/g + s/^$bs2$dollar/$bs&/ + s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g + s/\n//g" + +# Standard options: +opt_dry_run=false +opt_help=false +opt_quiet=false +opt_verbose=false +opt_warning=: + +# func_echo arg... +# Echo program name prefixed message, along with the current mode +# name if it has been set yet. +func_echo () +{ + $ECHO "$progname${mode+: }$mode: $*" +} -if test "X$1" = X--fallback-echo; then - # used as fallback echo - shift - cat <&2 +} -# NLS nuisances. -# Only set LANG and LC_ALL to C if already set. -# These must not be set unconditionally because not all systems understand -# e.g. LANG=C (notably SCO). -# We save the old values to restore during execute mode. -if test "${LC_ALL+set}" = set; then - save_LC_ALL="$LC_ALL"; LC_ALL=C; export LC_ALL -fi -if test "${LANG+set}" = set; then - save_LANG="$LANG"; LANG=C; export LANG -fi +# func_warning arg... +# Echo program name prefixed warning message to standard error. +func_warning () +{ + $opt_warning && $ECHO "$progname${mode+: }$mode: warning: "${1+"$@"} 1>&2 -# Make sure IFS has a sensible default -lt_nl=' -' -IFS=" $lt_nl" + # bash bug again: + : +} -if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then - $echo "$modename: not configured to build any kind of library" 1>&2 - $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 - exit $EXIT_FAILURE -fi +# func_fatal_error arg... +# Echo program name prefixed message to standard error, and exit. +func_fatal_error () +{ + func_error ${1+"$@"} + exit $EXIT_FAILURE +} -# Global variables. -mode=$default_mode -nonopt= -prev= -prevopt= -run= -show="$echo" -show_help= -execute_dlfiles= -duplicate_deps=no -preserve_args= -lo2o="s/\\.lo\$/.${objext}/" -o2lo="s/\\.${objext}\$/.lo/" +# func_fatal_help arg... +# Echo program name prefixed message to standard error, followed by +# a help hint, and exit. +func_fatal_help () +{ + func_error ${1+"$@"} + func_fatal_error "$help" +} +help="Try \`$progname --help' for more information." ## default + + +# func_grep expression filename +# Check whether EXPRESSION matches any line of FILENAME, without output. +func_grep () +{ + $GREP "$1" "$2" >/dev/null 2>&1 +} + + +# func_mkdir_p directory-path +# Make sure the entire path to DIRECTORY-PATH is available. +func_mkdir_p () +{ + my_directory_path="$1" + my_dir_list= + + if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then + + # Protect directory names starting with `-' + case $my_directory_path in + -*) my_directory_path="./$my_directory_path" ;; + esac + + # While some portion of DIR does not yet exist... + while test ! -d "$my_directory_path"; do + # ...make a list in topmost first order. Use a colon delimited + # list incase some portion of path contains whitespace. + my_dir_list="$my_directory_path:$my_dir_list" + + # If the last portion added has no slash in it, the list is done + case $my_directory_path in */*) ;; *) break ;; esac + + # ...otherwise throw away the child directory and loop + my_directory_path=`$ECHO "X$my_directory_path" | $Xsed -e "$dirname"` + done + my_dir_list=`$ECHO "X$my_dir_list" | $Xsed -e 's,:*$,,'` + + save_mkdir_p_IFS="$IFS"; IFS=':' + for my_dir in $my_dir_list; do + IFS="$save_mkdir_p_IFS" + # mkdir can fail with a `File exist' error if two processes + # try to create one of the directories concurrently. Don't + # stop in that case! + $MKDIR "$my_dir" 2>/dev/null || : + done + IFS="$save_mkdir_p_IFS" + + # Bail out if we (or some other process) failed to create a directory. + test -d "$my_directory_path" || \ + func_fatal_error "Failed to create \`$1'" + fi +} -##################################### -# Shell function definitions: -# This seems to be the best place for them # func_mktempdir [string] # Make a temporary directory that won't clash with other running @@ -149,7 +355,7 @@ { my_template="${TMPDIR-/tmp}/${1-$progname}" - if test "$run" = ":"; then + if test "$opt_dry_run" = ":"; then # Return a directory name, but don't create it in dry-run mode my_tmpdir="${my_template}-$$" else @@ -158,546 +364,818 @@ my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` if test ! -d "$my_tmpdir"; then - # Failing that, at least try and use $RANDOM to avoid a race - my_tmpdir="${my_template}-${RANDOM-0}$$" + # Failing that, at least try and use $RANDOM to avoid a race + my_tmpdir="${my_template}-${RANDOM-0}$$" - save_mktempdir_umask=`umask` - umask 0077 - $mkdir "$my_tmpdir" - umask $save_mktempdir_umask + save_mktempdir_umask=`umask` + umask 0077 + $MKDIR "$my_tmpdir" + umask $save_mktempdir_umask fi # If we're not in dry-run mode, bomb out on failure - test -d "$my_tmpdir" || { - $echo "cannot create temporary directory \`$my_tmpdir'" 1>&2 - exit $EXIT_FAILURE - } + test -d "$my_tmpdir" || \ + func_fatal_error "cannot create temporary directory \`$my_tmpdir'" fi - $echo "X$my_tmpdir" | $Xsed + $ECHO "X$my_tmpdir" | $Xsed } -# func_win32_libid arg -# return the library type of file 'arg' -# -# Need a lot of goo to handle *both* DLLs and import libs -# Has to be a shell function in order to 'eat' the argument -# that is supplied when $file_magic_command is called. -func_win32_libid () +# func_quote_for_eval arg +# Aesthetically quote ARG to be evaled later. +# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT +# is double-quoted, suitable for a subsequent eval, whereas +# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters +# which are still active within double quotes backslashified. +func_quote_for_eval () { - win32_libid_type="unknown" - win32_fileres=`file -L $1 2>/dev/null` - case $win32_fileres in - *ar\ archive\ import\ library*) # definitely import - win32_libid_type="x86 archive import" - ;; - *ar\ archive*) # could be an import, or static - if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | \ - $EGREP -e 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then - win32_nmres=`eval $NM -f posix -A $1 | \ - $SED -n -e '1,100{/ I /{s,.*,import,;p;q;};}'` - case $win32_nmres in - import*) win32_libid_type="x86 archive import";; - *) win32_libid_type="x86 archive static";; - esac - fi - ;; - *DLL*) - win32_libid_type="x86 DLL" - ;; - *executable*) # but shell scripts are "executable" too... - case $win32_fileres in - *MS\ Windows\ PE\ Intel*) - win32_libid_type="x86 DLL" - ;; + case $1 in + *[\\\`\"\$]*) + func_quote_for_eval_unquoted_result=`$ECHO "X$1" | $Xsed -e "$sed_quote_subst"` ;; + *) + func_quote_for_eval_unquoted_result="$1" ;; + esac + + case $func_quote_for_eval_unquoted_result in + # Double-quote args containing shell metacharacters to delay + # word splitting, command substitution and and variable + # expansion for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" + ;; + *) + func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" esac - ;; - esac - $echo $win32_libid_type } -# func_infer_tag arg -# Infer tagged configuration to use if any are available and -# if one wasn't chosen via the "--tag" command line option. -# Only attempt this if the compiler in the base compile -# command doesn't match the default compiler. -# arg is usually of the form 'gcc ...' -func_infer_tag () +# func_quote_for_expand arg +# Aesthetically quote ARG to be evaled later; same as above, +# but do not quote variable references. +func_quote_for_expand () { - if test -n "$available_tags" && test -z "$tagname"; then - CC_quoted= - for arg in $CC; do - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - CC_quoted="$CC_quoted $arg" - done - case $@ in - # Blanks in the command may have been stripped by the calling shell, - # but not from the CC environment variable when configure was run. - " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) ;; - # Blanks at the start of $base_compile will cause this to fail - # if we don't check for them as well. + case $1 in + *[\\\`\"]*) + my_arg=`$ECHO "X$1" | $Xsed \ + -e "$double_quote_subst" -e "$sed_double_backslash"` ;; *) - for z in $available_tags; do - if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then - # Evaluate the configuration. - eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" - CC_quoted= - for arg in $CC; do - # Double-quote args containing other shell metacharacters. - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - CC_quoted="$CC_quoted $arg" - done - case "$@ " in - " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) - # The compiler in the base compile command matches - # the one in the tagged configuration. - # Assume this is the tagged configuration we want. - tagname=$z - break - ;; - esac - fi - done - # If $tagname still isn't set, then no tagged configuration - # was found and let the user know that the "--tag" command - # line option must be used. - if test -z "$tagname"; then - $echo "$modename: unable to infer tagged configuration" - $echo "$modename: specify a tag with \`--tag'" 1>&2 - exit $EXIT_FAILURE -# else -# $echo "$modename: using $tagname tagged configuration" - fi - ;; - esac - fi + my_arg="$1" ;; + esac + + case $my_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting and command substitution for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + my_arg="\"$my_arg\"" + ;; + esac + + func_quote_for_expand_result="$my_arg" } -# func_extract_an_archive dir oldlib -func_extract_an_archive () +# func_show_eval cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. +func_show_eval () { - f_ex_an_ar_dir="$1"; shift - f_ex_an_ar_oldlib="$1" + my_cmd="$1" + my_fail_exp="${2-:}" - $show "(cd $f_ex_an_ar_dir && $AR x $f_ex_an_ar_oldlib)" - $run eval "(cd \$f_ex_an_ar_dir && $AR x \$f_ex_an_ar_oldlib)" || exit $? - if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then - : - else - $echo "$modename: ERROR: object name conflicts: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" 1>&2 - exit $EXIT_FAILURE + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$my_cmd" + my_status=$? + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi fi } -# func_extract_archives gentop oldlib ... -func_extract_archives () + +# func_show_eval_locale cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. Use the saved locale for evaluation. +func_show_eval_locale () { - my_gentop="$1"; shift - my_oldlibs=${1+"$@"} - my_oldobjs="" - my_xlib="" - my_xabs="" - my_xdir="" - my_status="" + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } - $show "${rm}r $my_gentop" - $run ${rm}r "$my_gentop" - $show "$mkdir $my_gentop" - $run $mkdir "$my_gentop" - my_status=$? - if test "$my_status" -ne 0 && test ! -d "$my_gentop"; then - exit $my_status + if ${opt_dry_run-false}; then :; else + eval "$lt_user_locale + $my_cmd" + my_status=$? + eval "$lt_safe_locale" + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi fi +} - for my_xlib in $my_oldlibs; do - # Extract the objects. - case $my_xlib in - [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; - *) my_xabs=`pwd`"/$my_xlib" ;; - esac - my_xlib=`$echo "X$my_xlib" | $Xsed -e 's%^.*/%%'` - my_xdir="$my_gentop/$my_xlib" - $show "${rm}r $my_xdir" - $run ${rm}r "$my_xdir" - $show "$mkdir $my_xdir" - $run $mkdir "$my_xdir" - exit_status=$? - if test "$exit_status" -ne 0 && test ! -d "$my_xdir"; then - exit $exit_status - fi - case $host in - *-darwin*) - $show "Extracting $my_xabs" - # Do not bother doing anything if just a dry run - if test -z "$run"; then - darwin_orig_dir=`pwd` - cd $my_xdir || exit $? - darwin_archive=$my_xabs - darwin_curdir=`pwd` - darwin_base_archive=`$echo "X$darwin_archive" | $Xsed -e 's%^.*/%%'` - darwin_arches=`lipo -info "$darwin_archive" 2>/dev/null | $EGREP Architectures 2>/dev/null` - if test -n "$darwin_arches"; then - darwin_arches=`echo "$darwin_arches" | $SED -e 's/.*are://'` - darwin_arch= - $show "$darwin_base_archive has multiple architectures $darwin_arches" - for darwin_arch in $darwin_arches ; do - mkdir -p "unfat-$$/${darwin_base_archive}-${darwin_arch}" - lipo -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" - cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" - func_extract_an_archive "`pwd`" "${darwin_base_archive}" - cd "$darwin_curdir" - $rm "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" - done # $darwin_arches - ## Okay now we have a bunch of thin objects, gotta fatten them up :) - darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print| xargs basename | sort -u | $NL2SP` - darwin_file= - darwin_files= - for darwin_file in $darwin_filelist; do - darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP` - lipo -create -output "$darwin_file" $darwin_files - done # $darwin_filelist - ${rm}r unfat-$$ - cd "$darwin_orig_dir" - else - cd "$darwin_orig_dir" - func_extract_an_archive "$my_xdir" "$my_xabs" - fi # $darwin_arches - fi # $run - ;; - *) - func_extract_an_archive "$my_xdir" "$my_xabs" - ;; - esac - my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` - done - func_extract_archives_result="$my_oldobjs" + + + +# func_version +# Echo version message to standard output and exit. +func_version () +{ + $SED -n '/^# '$PROGRAM' (GNU /,/# warranty; / { + s/^# // + s/^# *$// + s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ + p + }' < "$progpath" + exit $? } -# End of Shell function definitions -##################################### -# Darwin sucks -eval std_shrext=\"$shrext_cmds\" +# func_usage +# Echo short help message to standard output and exit. +func_usage () +{ + $SED -n '/^# Usage:/,/# -h/ { + s/^# // + s/^# *$// + s/\$progname/'$progname'/ + p + }' < "$progpath" + $ECHO + $ECHO "run \`$progname --help | more' for full usage" + exit $? +} -disable_libs=no +# func_help +# Echo long help message to standard output and exit. +func_help () +{ + $SED -n '/^# Usage:/,/# Report bugs to/ { + s/^# // + s/^# *$// + s*\$progname*'$progname'* + s*\$host*'"$host"'* + s*\$SHELL*'"$SHELL"'* + s*\$LTCC*'"$LTCC"'* + s*\$LTCFLAGS*'"$LTCFLAGS"'* + s*\$LD*'"$LD"'* + s/\$with_gnu_ld/'"$with_gnu_ld"'/ + s/\$automake_version/'"`(automake --version) 2>/dev/null |$SED 1q`"'/ + s/\$autoconf_version/'"`(autoconf --version) 2>/dev/null |$SED 1q`"'/ + p + }' < "$progpath" + exit $? +} -# Parse our command line options once, thoroughly. -while test "$#" -gt 0 -do - arg="$1" +# func_missing_arg argname +# Echo program name prefixed message to standard error and set global +# exit_cmd. +func_missing_arg () +{ + func_error "missing argument for $1" + exit_cmd=exit +} + +exit_cmd=: + + + + + +# Check that we have a working $ECHO. +if test "X$1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. shift +elif test "X$1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t'; then + # Yippee, $ECHO works! + : +else + # Restart under the correct shell, and then maybe $ECHO will work. + exec $SHELL "$progpath" --no-reexec ${1+"$@"} +fi - case $arg in - -*=*) optarg=`$echo "X$arg" | $Xsed -e 's/[-_a-zA-Z0-9]*=//'` ;; - *) optarg= ;; - esac +if test "X$1" = X--fallback-echo; then + # used as fallback echo + shift + cat <&2 - exit $EXIT_FAILURE - ;; - esac +magic="%%%MAGIC variable%%%" +magic_exe="%%%MAGIC EXE variable%%%" - case $tagname in - CC) - # Don't test for the "default" C tag, as we know, it's there, but - # not specially marked. - ;; - *) - if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "$progpath" > /dev/null; then - taglist="$taglist $tagname" - # Evaluate the configuration. - eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$tagname'$/,/^# ### END LIBTOOL TAG CONFIG: '$tagname'$/p' < $progpath`" - else - $echo "$progname: ignoring unknown tag $tagname" 1>&2 - fi - ;; - esac - ;; - *) - eval "$prev=\$arg" - ;; - esac +# Global variables. +# $mode is unset +nonopt= +execute_dlfiles= +preserve_args= +lo2o="s/\\.lo\$/.${objext}/" +o2lo="s/\\.${objext}\$/.lo/" +extracted_archives= +extracted_serial=0 - prev= - prevopt= - continue - fi +opt_dry_run=false +opt_duplicate_deps=false +opt_silent=false +opt_debug=: - # Have we seen a non-optional argument yet? - case $arg in - --help) - show_help=yes - ;; +# If this variable is set in any of the actions, the command in it +# will be execed at the end. This prevents here-documents from being +# left over by shells. +exec_cmd= - --version) - $echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP" - $echo - $echo "Copyright (C) 2005 Free Software Foundation, Inc." - $echo "This is free software; see the source for copying conditions. There is NO" - $echo "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." - exit $? - ;; +# func_fatal_configuration arg... +# Echo program name prefixed message to standard error, followed by +# a configuration failure hint, and exit. +func_fatal_configuration () +{ + func_error ${1+"$@"} + func_error "See the $PACKAGE documentation for more information." + func_fatal_error "Fatal configuration error." +} + + +# func_config +# Display the configuration for all the tags in this script. +func_config () +{ + re_begincf='^# ### BEGIN LIBTOOL' + re_endcf='^# ### END LIBTOOL' + + # Default configuration. + $SED "1,/$re_begincf CONFIG/d;/$re_endcf CONFIG/,\$d" < "$progpath" - --config) - ${SED} -e '1,/^# ### BEGIN LIBTOOL CONFIG/d' -e '/^# ### END LIBTOOL CONFIG/,$d' $progpath # Now print the configurations for the tags. for tagname in $taglist; do - ${SED} -n -e "/^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$/,/^# ### END LIBTOOL TAG CONFIG: $tagname$/p" < "$progpath" + $SED -n "/$re_begincf TAG CONFIG: $tagname\$/,/$re_endcf TAG CONFIG: $tagname\$/p" < "$progpath" done - exit $? - ;; - --debug) - $echo "$progname: enabling shell trace mode" - set -x - preserve_args="$preserve_args $arg" - ;; - - --dry-run | -n) - run=: - ;; + exit $? +} - --features) - $echo "host: $host" +# func_features +# Display the features supported by this script. +func_features () +{ + $ECHO "host: $host" if test "$build_libtool_libs" = yes; then - $echo "enable shared libraries" + $ECHO "enable shared libraries" else - $echo "disable shared libraries" + $ECHO "disable shared libraries" fi if test "$build_old_libs" = yes; then - $echo "enable static libraries" + $ECHO "enable static libraries" else - $echo "disable static libraries" + $ECHO "disable static libraries" fi + exit $? - ;; +} - --finish) mode="finish" ;; +# func_enable_tag tagname +# Verify that TAGNAME is valid, and either flag an error and exit, or +# enable the TAGNAME tag. We also add TAGNAME to the global $taglist +# variable here. +func_enable_tag () +{ + # Global variable: + tagname="$1" - --mode) prevopt="--mode" prev=mode ;; - --mode=*) mode="$optarg" ;; + re_begincf="^# ### BEGIN LIBTOOL TAG CONFIG: $tagname\$" + re_endcf="^# ### END LIBTOOL TAG CONFIG: $tagname\$" + sed_extractcf="/$re_begincf/,/$re_endcf/p" + + # Validate tagname. + case $tagname in + *[!-_A-Za-z0-9,/]*) + func_fatal_error "invalid tag name: $tagname" + ;; + esac - --preserve-dup-deps) duplicate_deps="yes" ;; + # Don't test for the "default" C tag, as we know it's + # there but not specially marked. + case $tagname in + CC) ;; + *) + if $GREP "$re_begincf" "$progpath" >/dev/null 2>&1; then + taglist="$taglist $tagname" - --quiet | --silent) - show=: - preserve_args="$preserve_args $arg" - ;; + # Evaluate the configuration. Be careful to quote the path + # and the sed script, to avoid splitting on whitespace, but + # also don't use non-portable quotes within backquotes within + # quotes we have to do it in 2 steps: + extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` + eval "$extractedcf" + else + func_error "ignoring unknown tag $tagname" + fi + ;; + esac +} + +# Parse options once, thoroughly. This comes as soon as possible in +# the script to make things like `libtool --version' happen quickly. +{ - --tag) - prevopt="--tag" - prev=tag - preserve_args="$preserve_args --tag" + # Shorthand for --mode=foo, only valid as the first argument + case $1 in + clean|clea|cle|cl) + shift; set dummy --mode clean ${1+"$@"}; shift ;; - --tag=*) - set tag "$optarg" ${1+"$@"} - shift - prev=tag - preserve_args="$preserve_args --tag" + compile|compil|compi|comp|com|co|c) + shift; set dummy --mode compile ${1+"$@"}; shift ;; - - -dlopen) - prevopt="-dlopen" - prev=execute_dlfiles + execute|execut|execu|exec|exe|ex|e) + shift; set dummy --mode execute ${1+"$@"}; shift ;; - - -*) - $echo "$modename: unrecognized option \`$arg'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE + finish|finis|fini|fin|fi|f) + shift; set dummy --mode finish ${1+"$@"}; shift ;; - - *) - nonopt="$arg" - break + install|instal|insta|inst|ins|in|i) + shift; set dummy --mode install ${1+"$@"}; shift + ;; + link|lin|li|l) + shift; set dummy --mode link ${1+"$@"}; shift + ;; + uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) + shift; set dummy --mode uninstall ${1+"$@"}; shift ;; esac -done -if test -n "$prevopt"; then - $echo "$modename: option \`$prevopt' requires an argument" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE -fi + # Parse non-mode specific arguments: + while test "$#" -gt 0; do + opt="$1" + shift -case $disable_libs in -no) - ;; -shared) - build_libtool_libs=no - build_old_libs=yes - ;; -static) - build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` - ;; -esac + case $opt in + --config) func_config ;; -# If this variable is set in any of the actions, the command in it -# will be execed at the end. This prevents here-documents from being -# left over by shells. -exec_cmd= + --debug) preserve_args="$preserve_args $opt" + func_echo "enabling shell trace mode" + opt_debug='set -x' + $opt_debug + ;; + + -dlopen) test "$#" -eq 0 && func_missing_arg "$opt" && break + execute_dlfiles="$execute_dlfiles $1" + shift + ;; -if test -z "$show_help"; then + --dry-run | -n) opt_dry_run=: ;; + --features) func_features ;; + --finish) mode="finish" ;; + + --mode) test "$#" -eq 0 && func_missing_arg "$opt" && break + case $1 in + # Valid mode arguments: + clean) ;; + compile) ;; + execute) ;; + finish) ;; + install) ;; + link) ;; + relink) ;; + uninstall) ;; + + # Catch anything else as an error + *) func_error "invalid argument for $opt" + exit_cmd=exit + break + ;; + esac - # Infer the operation mode. - if test -z "$mode"; then - $echo "*** Warning: inferring the mode of operation is deprecated." 1>&2 - $echo "*** Future versions of Libtool will require --mode=MODE be specified." 1>&2 - case $nonopt in - *cc | cc* | *++ | gcc* | *-gcc* | g++* | xlc*) - mode=link - for arg - do - case $arg in - -c) - mode=compile - break - ;; - esac - done - ;; - *db | *dbx | *strace | *truss) - mode=execute - ;; - *install*|cp|mv) - mode=install - ;; - *rm) - mode=uninstall + mode="$1" + shift + ;; + + --preserve-dup-deps) + opt_duplicate_deps=: ;; + + --quiet|--silent) preserve_args="$preserve_args $opt" + opt_silent=: + ;; + + --verbose| -v) preserve_args="$preserve_args $opt" + opt_silent=false + ;; + + --tag) test "$#" -eq 0 && func_missing_arg "$opt" && break + preserve_args="$preserve_args $opt $1" + func_enable_tag "$1" # tagname is set here + shift + ;; + + # Separate optargs to long options: + -dlopen=*|--mode=*|--tag=*) + func_opt_split "$opt" + set dummy "$func_opt_split_opt" "$func_opt_split_arg" ${1+"$@"} + shift + ;; + + -\?|-h) func_usage ;; + --help) opt_help=: ;; + --version) func_version ;; + + -*) func_fatal_help "unrecognized option \`$opt'" ;; + + *) nonopt="$opt" + break + ;; + esac + done + + + case $host in + *cygwin* | *mingw* | *pw32* | *cegcc*) + # don't eliminate duplications in $postdeps and $predeps + opt_duplicate_compiler_generated_deps=: ;; *) - # If we have no mode, but dlfiles were specified, then do execute mode. - test -n "$execute_dlfiles" && mode=execute + opt_duplicate_compiler_generated_deps=$opt_duplicate_deps + ;; + esac - # Just use the default operation mode. - if test -z "$mode"; then - if test -n "$nonopt"; then - $echo "$modename: warning: cannot infer operation mode from \`$nonopt'" 1>&2 - else - $echo "$modename: warning: cannot infer operation mode without MODE-ARGS" 1>&2 - fi + # Having warned about all mis-specified options, bail out if + # anything was wrong. + $exit_cmd $EXIT_FAILURE +} + +# func_check_version_match +# Ensure that we are using m4 macros, and libtool script from the same +# release of libtool. +func_check_version_match () +{ + if test "$package_revision" != "$macro_revision"; then + if test "$VERSION" != "$macro_version"; then + if test -z "$macro_version"; then + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from an older release. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from $PACKAGE $macro_version. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF fi - ;; - esac + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, +$progname: but the definition of this LT_INIT comes from revision $macro_revision. +$progname: You should recreate aclocal.m4 with macros from revision $package_revision +$progname: of $PACKAGE $VERSION and run autoconf again. +_LT_EOF + fi + + exit $EXIT_MISMATCH + fi +} + + +## ----------- ## +## Main. ## +## ----------- ## + +$opt_help || { + # Sanity checks first: + func_check_version_match + + if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then + func_fatal_configuration "not configured to build any kind of library" fi + test -z "$mode" && func_fatal_error "error: you must specify a MODE." + + + # Darwin sucks + eval std_shrext=\"$shrext_cmds\" + + # Only execute mode is allowed to have -dlopen flags. if test -n "$execute_dlfiles" && test "$mode" != execute; then - $echo "$modename: unrecognized option \`-dlopen'" 1>&2 - $echo "$help" 1>&2 + func_error "unrecognized option \`-dlopen'" + $ECHO "$help" 1>&2 exit $EXIT_FAILURE fi # Change the help message to a mode-specific one. generic_help="$help" - help="Try \`$modename --help --mode=$mode' for more information." + help="Try \`$progname --help --mode=$mode' for more information." +} - # These modes are in order of execution frequency so that they run quickly. - case $mode in - # libtool compile mode - compile) - modename="$modename: compile" - # Get the compilation command and the source file. - base_compile= - srcfile="$nonopt" # always keep a non-empty value in "srcfile" - suppress_opt=yes - suppress_output= - arg_mode=normal - libobj= - later= - for arg - do - case $arg_mode in - arg ) - # do not "continue". Instead, add this to base_compile - lastarg="$arg" - arg_mode=normal - ;; +# func_lalib_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_lalib_p () +{ + test -f "$1" && + $SED -e 4q "$1" 2>/dev/null \ + | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 +} - target ) - libobj="$arg" - arg_mode=normal - continue - ;; +# func_lalib_unsafe_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function implements the same check as func_lalib_p without +# resorting to external programs. To this end, it redirects stdin and +# closes it afterwards, without saving the original file descriptor. +# As a safety measure, use it only where a negative result would be +# fatal anyway. Works if `file' does not exist. +func_lalib_unsafe_p () +{ + lalib_p=no + if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then + for lalib_p_l in 1 2 3 4 + do + read lalib_p_line + case "$lalib_p_line" in + \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; + esac + done + exec 0<&5 5<&- + fi + test "$lalib_p" = yes +} - normal ) - # Accept any command-line options. - case $arg in - -o) - if test -n "$libobj" ; then - $echo "$modename: you cannot specify \`-o' more than once" 1>&2 - exit $EXIT_FAILURE - fi - arg_mode=target - continue - ;; +# func_ltwrapper_script_p file +# True iff FILE is a libtool wrapper script +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_script_p () +{ + func_lalib_p "$1" +} - -static | -prefer-pic | -prefer-non-pic) - later="$later $arg" - continue - ;; +# func_ltwrapper_executable_p file +# True iff FILE is a libtool wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_executable_p () +{ + func_ltwrapper_exec_suffix= + case $1 in + *.exe) ;; + *) func_ltwrapper_exec_suffix=.exe ;; + esac + $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 +} - -no-suppress) - suppress_opt=no - continue - ;; +# func_ltwrapper_scriptname file +# Assumes file is an ltwrapper_executable +# uses $file to determine the appropriate filename for a +# temporary ltwrapper_script. +func_ltwrapper_scriptname () +{ + func_ltwrapper_scriptname_result="" + if func_ltwrapper_executable_p "$1"; then + func_dirname_and_basename "$1" "" "." + func_stripname '' '.exe' "$func_basename_result" + func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" + fi +} - -Xcompiler) - arg_mode=arg # the next one goes into the "base_compile" arg list - continue # The current "srcfile" will either be retained or - ;; # replaced later. I would guess that would be a bug. +# func_ltwrapper_p file +# True iff FILE is a libtool wrapper script or wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_p () +{ + func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" +} - -Wc,*) - args=`$echo "X$arg" | $Xsed -e "s/^-Wc,//"` - lastarg= - save_ifs="$IFS"; IFS=',' - for arg in $args; do - IFS="$save_ifs" - # Double-quote args containing other shell metacharacters. - # Many Bourne shells cannot handle close brackets correctly - # in scan sets, so we specify it separately. - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" +# func_execute_cmds commands fail_cmd +# Execute tilde-delimited COMMANDS. +# If FAIL_CMD is given, eval that upon failure. +# FAIL_CMD may read-access the current command in variable CMD! +func_execute_cmds () +{ + $opt_debug + save_ifs=$IFS; IFS='~' + for cmd in $1; do + IFS=$save_ifs + eval cmd=\"$cmd\" + func_show_eval "$cmd" "${2-:}" + done + IFS=$save_ifs +} + + +# func_source file +# Source FILE, adding directory component if necessary. +# Note that it is not necessary on cygwin/mingw to append a dot to +# FILE even if both FILE and FILE.exe exist: automatic-append-.exe +# behavior happens only for exec(3), not for open(2)! Also, sourcing +# `FILE.' does not work on cygwin managed mounts. +func_source () +{ + $opt_debug + case $1 in + */* | *\\*) . "$1" ;; + *) . "./$1" ;; + esac +} + + +# func_infer_tag arg +# Infer tagged configuration to use if any are available and +# if one wasn't chosen via the "--tag" command line option. +# Only attempt this if the compiler in the base compile +# command doesn't match the default compiler. +# arg is usually of the form 'gcc ...' +func_infer_tag () +{ + $opt_debug + if test -n "$available_tags" && test -z "$tagname"; then + CC_quoted= + for arg in $CC; do + func_quote_for_eval "$arg" + CC_quoted="$CC_quoted $func_quote_for_eval_result" + done + case $@ in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" + CC_quoted= + for arg in $CC; do + # Double-quote args containing other shell metacharacters. + func_quote_for_eval "$arg" + CC_quoted="$CC_quoted $func_quote_for_eval_result" + done + case "$@ " in + " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*) + # The compiler in the base compile command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break ;; esac - lastarg="$lastarg $arg" + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + func_echo "unable to infer tagged configuration" + func_fatal_error "specify a tag with \`--tag'" +# else +# func_verbose "using $tagname tagged configuration" + fi + ;; + esac + fi +} + + + +# func_write_libtool_object output_name pic_name nonpic_name +# Create a libtool object file (analogous to a ".la" file), +# but don't create it if we're doing a dry run. +func_write_libtool_object () +{ + write_libobj=${1} + if test "$build_libtool_libs" = yes; then + write_lobj=\'${2}\' + else + write_lobj=none + fi + + if test "$build_old_libs" = yes; then + write_oldobj=\'${3}\' + else + write_oldobj=none + fi + + $opt_dry_run || { + cat >${write_libobj}T <\?\'\ \ ]*|*]*|"") - lastarg="\"$lastarg\"" - ;; - esac - - base_compile="$base_compile $lastarg" + func_quote_for_eval "$lastarg" + base_compile="$base_compile $func_quote_for_eval_result" done # for arg case $arg_mode in arg) - $echo "$modename: you must specify an argument for -Xcompile" - exit $EXIT_FAILURE + func_fatal_error "you must specify an argument for -Xcompile" ;; target) - $echo "$modename: you must specify a target with \`-o'" 1>&2 - exit $EXIT_FAILURE + func_fatal_error "you must specify a target with \`-o'" ;; *) # Get the name of the library object. - [ -z "$libobj" ] && libobj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%'` + test -z "$libobj" && { + func_basename "$srcfile" + libobj="$func_basename_result" + } ;; esac # Recognize several different file suffixes. # If the user specifies -o file.o, it is replaced with file.lo - xform='[cCFSifmso]' case $libobj in - *.ada) xform=ada ;; - *.adb) xform=adb ;; - *.ads) xform=ads ;; - *.asm) xform=asm ;; - *.c++) xform=c++ ;; - *.cc) xform=cc ;; - *.ii) xform=ii ;; - *.class) xform=class ;; - *.cpp) xform=cpp ;; - *.cxx) xform=cxx ;; - *.f90) xform=f90 ;; - *.for) xform=for ;; - *.java) xform=java ;; + *.[cCFSifmso] | \ + *.ada | *.adb | *.ads | *.asm | \ + *.c++ | *.cc | *.ii | *.class | *.cpp | *.cxx | \ + *.[fF][09]? | *.for | *.java | *.obj | *.sx) + func_xform "$libobj" + libobj=$func_xform_result + ;; esac - libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"` - case $libobj in - *.lo) obj=`$echo "X$libobj" | $Xsed -e "$lo2o"` ;; + *.lo) func_lo2o "$libobj"; obj=$func_lo2o_result ;; *) - $echo "$modename: cannot determine name of library object from \`$libobj'" 1>&2 - exit $EXIT_FAILURE + func_fatal_error "cannot determine name of library object from \`$libobj'" ;; esac @@ -774,7 +1230,15 @@ for arg in $later; do case $arg in + -shared) + test "$build_libtool_libs" != yes && \ + func_fatal_configuration "can not build a shared library" + build_old_libs=no + continue + ;; + -static) + build_libtool_libs=no build_old_libs=yes continue ;; @@ -791,28 +1255,17 @@ esac done - qlibobj=`$echo "X$libobj" | $Xsed -e "$sed_quote_subst"` - case $qlibobj in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - qlibobj="\"$qlibobj\"" ;; - esac - test "X$libobj" != "X$qlibobj" \ - && $echo "X$libobj" | grep '[]~#^*{};<>?"'"'"' &()|`$[]' \ - && $echo "$modename: libobj name \`$libobj' may not contain shell special characters." - objname=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` - xdir=`$echo "X$obj" | $Xsed -e 's%/[^/]*$%%'` - if test "X$xdir" = "X$obj"; then - xdir= - else - xdir=$xdir/ - fi + func_quote_for_eval "$libobj" + test "X$libobj" != "X$func_quote_for_eval_result" \ + && $ECHO "X$libobj" | $GREP '[]~#^*{};<>?"'"'"' &()|`$[]' \ + && func_warning "libobj name \`$libobj' may not contain shell special characters." + func_dirname_and_basename "$obj" "/" "" + objname="$func_basename_result" + xdir="$func_dirname_result" lobj=${xdir}$objdir/$objname - if test -z "$base_compile"; then - $echo "$modename: you must specify a compilation command" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi + test -z "$base_compile" && \ + func_fatal_help "you must specify a compilation command" # Delete any leftover library objects. if test "$build_old_libs" = yes; then @@ -821,12 +1274,9 @@ removelist="$lobj $libobj ${libobj}T" fi - $run $rm $removelist - trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15 - # On Cygwin there's no "real" PIC flag so we must build both object types case $host_os in - cygwin* | mingw* | pw32* | os2*) + cygwin* | mingw* | pw32* | os2* | cegcc*) pic_mode=default ;; esac @@ -838,10 +1288,8 @@ # Calculate the filename of the output object if compiler does # not support -o with -c if test "$compiler_c_o" = no; then - output_obj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext} + output_obj=`$ECHO "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext} lockfile="$output_obj.lock" - removelist="$removelist $output_obj $lockfile" - trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15 else output_obj= need_locks=no @@ -851,13 +1299,13 @@ # Lock this critical section if it is needed # We use this script file to make the link, it avoids creating a new file if test "$need_locks" = yes; then - until $run ln "$progpath" "$lockfile" 2>/dev/null; do - $show "Waiting for $lockfile to be removed" + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" sleep 2 done elif test "$need_locks" = warn; then if test -f "$lockfile"; then - $echo "\ + $ECHO "\ *** ERROR, $lockfile exists and contains: `cat $lockfile 2>/dev/null` @@ -868,34 +1316,22 @@ avoid parallel builds (make -j) in this platform, or get a better compiler." - $run $rm $removelist + $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi - $echo "$srcfile" > "$lockfile" + removelist="$removelist $output_obj" + $ECHO "$srcfile" > "$lockfile" fi + $opt_dry_run || $RM $removelist + removelist="$removelist $lockfile" + trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 + if test -n "$fix_srcfile_path"; then eval srcfile=\"$fix_srcfile_path\" fi - qsrcfile=`$echo "X$srcfile" | $Xsed -e "$sed_quote_subst"` - case $qsrcfile in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - qsrcfile="\"$qsrcfile\"" ;; - esac - - $run $rm "$libobj" "${libobj}T" - - # Create a libtool object file (analogous to a ".la" file), - # but don't create it if we're doing a dry run. - test -z "$run" && cat > ${libobj}T </dev/null`" != "X$srcfile"; then - $echo "\ + $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` @@ -948,45 +1371,27 @@ avoid parallel builds (make -j) in this platform, or get a better compiler." - $run $rm $removelist + $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed, then go on to compile the next one if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then - $show "$mv $output_obj $lobj" - if $run $mv $output_obj $lobj; then : - else - error=$? - $run $rm $removelist - exit $error - fi + func_show_eval '$MV "$output_obj" "$lobj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi - # Append the name of the PIC object to the libtool object file. - test -z "$run" && cat >> ${libobj}T <> ${libobj}T </dev/null`" != "X$srcfile"; then - $echo "\ + $ECHO "\ *** ERROR, $lockfile contains: `cat $lockfile 2>/dev/null` @@ -1020,5423 +1420,6777 @@ avoid parallel builds (make -j) in this platform, or get a better compiler." - $run $rm $removelist + $opt_dry_run || $RM $removelist exit $EXIT_FAILURE fi # Just move the object if needed if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then - $show "$mv $output_obj $obj" - if $run $mv $output_obj $obj; then : - else - error=$? - $run $rm $removelist - exit $error - fi + func_show_eval '$MV "$output_obj" "$obj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' fi - - # Append the name of the non-PIC object the libtool object file. - # Only append if the libtool object file exists. - test -z "$run" && cat >> ${libobj}T <> ${libobj}T <&2 - fi - if test -n "$link_static_flag"; then - dlopen_self=$dlopen_self_static - fi - prefer_static_libs=yes - else - if test -z "$pic_flag" && test -n "$link_static_flag"; then - dlopen_self=$dlopen_self_static - fi - prefer_static_libs=built - fi - build_libtool_libs=no - build_old_libs=yes - break - ;; - esac - done +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. - # See if our shared archives depend on static archives. - test -n "$old_archive_from_new_cmds" && build_old_libs=yes +If FILE is a libtool library, object or program, all the files associated +with it are deleted. Otherwise, only FILE itself is deleted using RM." + ;; - # Go through the arguments, transforming them on the way. - while test "$#" -gt 0; do - arg="$1" - shift - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - qarg=\"`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`\" ### testsuite: skip nested quoting test - ;; - *) qarg=$arg ;; - esac - libtool_args="$libtool_args $qarg" + compile) + $ECHO \ +"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE - # If the previous option needs an argument, assign it. - if test -n "$prev"; then - case $prev in - output) - compile_command="$compile_command @OUTPUT@" - finalize_command="$finalize_command @OUTPUT@" - ;; - esac +Compile a source file into a libtool library object. - case $prev in - dlfiles|dlprefiles) - if test "$preload" = no; then - # Add the symbol object into the linking commands. - compile_command="$compile_command @SYMFILE@" - finalize_command="$finalize_command @SYMFILE@" - preload=yes - fi - case $arg in - *.la | *.lo) ;; # We handle these cases below. - force) - if test "$dlself" = no; then - dlself=needless - export_dynamic=yes - fi - prev= - continue - ;; - self) - if test "$prev" = dlprefiles; then - dlself=yes - elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then - dlself=yes - else - dlself=needless - export_dynamic=yes - fi - prev= - continue - ;; - *) - if test "$prev" = dlfiles; then - dlfiles="$dlfiles $arg" - else - dlprefiles="$dlprefiles $arg" - fi - prev= - continue - ;; - esac - ;; - expsyms) - export_symbols="$arg" - if test ! -f "$arg"; then - $echo "$modename: symbol file \`$arg' does not exist" - exit $EXIT_FAILURE - fi - prev= - continue - ;; - expsyms_regex) - export_symbols_regex="$arg" - prev= - continue - ;; - inst_prefix) - inst_prefix_dir="$arg" - prev= - continue - ;; - precious_regex) - precious_files_regex="$arg" - prev= - continue - ;; - release) - release="-$arg" - prev= - continue - ;; - objectlist) - if test -f "$arg"; then - save_arg=$arg - moreargs= - for fil in `cat $save_arg` - do -# moreargs="$moreargs $fil" - arg=$fil - # A libtool-controlled object. +This mode accepts the following additional options: - # Check to see that this really is a libtool object. - if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - pic_object= - non_pic_object= + -o OUTPUT-FILE set the output file name to OUTPUT-FILE + -no-suppress do not suppress compiler output for multiple passes + -prefer-pic try to building PIC objects only + -prefer-non-pic try to building non-PIC objects only + -shared do not build a \`.o' file suitable for static linking + -static only build a \`.o' file suitable for static linking - # Read the .lo file - # If there is no directory component, then add one. - case $arg in - */* | *\\*) . $arg ;; - *) . ./$arg ;; - esac +COMPILE-COMMAND is a command to be used in creating a \`standard' object file +from the given SOURCEFILE. - if test -z "$pic_object" || \ - test -z "$non_pic_object" || - test "$pic_object" = none && \ - test "$non_pic_object" = none; then - $echo "$modename: cannot find name of object for \`$arg'" 1>&2 - exit $EXIT_FAILURE - fi +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix \`.c' with the +library object suffix, \`.lo'." + ;; - # Extract subdirectory from the argument. - xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` - if test "X$xdir" = "X$arg"; then - xdir= - else - xdir="$xdir/" - fi + execute) + $ECHO \ +"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... - if test "$pic_object" != none; then - # Prepend the subdirectory the object is found in. - pic_object="$xdir$pic_object" +Automatically set library path, then run a program. - if test "$prev" = dlfiles; then - if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then - dlfiles="$dlfiles $pic_object" - prev= - continue - else - # If libtool objects are unsupported, then we need to preload. - prev=dlprefiles - fi - fi +This mode accepts the following additional options: - # CHECK ME: I think I busted this. -Ossama - if test "$prev" = dlprefiles; then - # Preload the old-style object. - dlprefiles="$dlprefiles $pic_object" - prev= - fi + -dlopen FILE add the directory containing FILE to the library path - # A PIC object. - libobjs="$libobjs $pic_object" - arg="$pic_object" - fi +This mode sets the library path environment variable according to \`-dlopen' +flags. - # Non-PIC object. - if test "$non_pic_object" != none; then - # Prepend the subdirectory the object is found in. - non_pic_object="$xdir$non_pic_object" +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. - # A standard non-PIC object - non_pic_objects="$non_pic_objects $non_pic_object" - if test -z "$pic_object" || test "$pic_object" = none ; then - arg="$non_pic_object" - fi - else - # If the PIC object exists, use it instead. - # $xdir was prepended to $pic_object above. - non_pic_object="$pic_object" - non_pic_objects="$non_pic_objects $non_pic_object" - fi - else - # Only an error if not doing a dry-run. - if test -z "$run"; then - $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 - exit $EXIT_FAILURE - else - # Dry-run case. +Then, COMMAND is executed, with ARGS as arguments." + ;; - # Extract subdirectory from the argument. - xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` - if test "X$xdir" = "X$arg"; then - xdir= - else - xdir="$xdir/" - fi + finish) + $ECHO \ +"Usage: $progname [OPTION]... --mode=finish [LIBDIR]... - pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` - non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` - libobjs="$libobjs $pic_object" - non_pic_objects="$non_pic_objects $non_pic_object" - fi - fi - done - else - $echo "$modename: link input file \`$save_arg' does not exist" - exit $EXIT_FAILURE - fi - arg=$save_arg - prev= - continue - ;; - rpath | xrpath) - # We need an absolute path. - case $arg in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - $echo "$modename: only absolute run-paths are allowed" 1>&2 - exit $EXIT_FAILURE - ;; - esac - if test "$prev" = rpath; then - case "$rpath " in - *" $arg "*) ;; - *) rpath="$rpath $arg" ;; - esac - else - case "$xrpath " in - *" $arg "*) ;; - *) xrpath="$xrpath $arg" ;; - esac - fi - prev= - continue - ;; - xcompiler) - compiler_flags="$compiler_flags $qarg" - prev= - compile_command="$compile_command $qarg" - finalize_command="$finalize_command $qarg" - continue - ;; - xlinker) - linker_flags="$linker_flags $qarg" - compiler_flags="$compiler_flags $wl$qarg" - prev= - compile_command="$compile_command $wl$qarg" - finalize_command="$finalize_command $wl$qarg" - continue - ;; - xcclinker) - linker_flags="$linker_flags $qarg" - compiler_flags="$compiler_flags $qarg" - prev= - compile_command="$compile_command $qarg" - finalize_command="$finalize_command $qarg" - continue - ;; - shrext) - shrext_cmds="$arg" - prev= - continue - ;; - darwin_framework|darwin_framework_skip) - test "$prev" = "darwin_framework" && compiler_flags="$compiler_flags $arg" - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - prev= - continue - ;; - *) - eval "$prev=\"\$arg\"" - prev= - continue - ;; - esac - fi # test -n "$prev" +Complete the installation of libtool libraries. - prevarg="$arg" +Each LIBDIR is a directory that contains libtool libraries. - case $arg in - -all-static) - if test -n "$link_static_flag"; then - compile_command="$compile_command $link_static_flag" - finalize_command="$finalize_command $link_static_flag" - fi - continue - ;; +The commands that this mode executes may require superuser privileges. Use +the \`--dry-run' option if you just want to see what would be executed." + ;; - -allow-undefined) - # FIXME: remove this flag sometime in the future. - $echo "$modename: \`-allow-undefined' is deprecated because it is the default" 1>&2 - continue - ;; + install) + $ECHO \ +"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... - -avoid-version) - avoid_version=yes - continue - ;; +Install executables or libraries. - -dlopen) - prev=dlfiles - continue - ;; +INSTALL-COMMAND is the installation command. The first component should be +either the \`install' or \`cp' program. - -dlpreopen) - prev=dlprefiles - continue - ;; +The following components of INSTALL-COMMAND are treated specially: - -export-dynamic) - export_dynamic=yes - continue - ;; + -inst-prefix PREFIX-DIR Use PREFIX-DIR as a staging area for installation - -export-symbols | -export-symbols-regex) - if test -n "$export_symbols" || test -n "$export_symbols_regex"; then - $echo "$modename: more than one -exported-symbols argument is not allowed" - exit $EXIT_FAILURE - fi - if test "X$arg" = "X-export-symbols"; then - prev=expsyms - else - prev=expsyms_regex - fi - continue - ;; +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; - -framework|-arch|-isysroot) - case " $CC " in - *" ${arg} ${1} "* | *" ${arg} ${1} "*) - prev=darwin_framework_skip ;; - *) compiler_flags="$compiler_flags $arg" - prev=darwin_framework ;; - esac - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - continue - ;; + link) + $ECHO \ +"Usage: $progname [OPTION]... --mode=link LINK-COMMAND... - -inst-prefix-dir) - prev=inst_prefix - continue - ;; +Link object files or libraries together to form another library, or to +create an executable program. - # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* - # so, if we see these flags be careful not to treat them like -L - -L[A-Z][A-Z]*:*) - case $with_gcc/$host in - no/*-*-irix* | /*-*-irix*) - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - ;; - esac - continue - ;; +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. - -L*) - dir=`$echo "X$arg" | $Xsed -e 's/^-L//'` - # We need an absolute path. - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - absdir=`cd "$dir" && pwd` - if test -z "$absdir"; then - $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2 - absdir="$dir" - notinst_path="$notinst_path $dir" - fi - dir="$absdir" - ;; - esac - case "$deplibs " in - *" -L$dir "*) ;; - *) - deplibs="$deplibs -L$dir" - lib_search_path="$lib_search_path $dir" - ;; - esac - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) - testbindir=`$echo "X$dir" | $Xsed -e 's*/lib$*/bin*'` - case :$dllsearchpath: in - *":$dir:"*) ;; - *) dllsearchpath="$dllsearchpath:$dir";; - esac - case :$dllsearchpath: in - *":$testbindir:"*) ;; - *) dllsearchpath="$dllsearchpath:$testbindir";; - esac - ;; - esac - continue - ;; +The following components of LINK-COMMAND are treated specially: - -l*) - if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos*) - # These systems don't actually have a C or math library (as such) - continue - ;; - *-*-os2*) - # These systems don't actually have a C library (as such) - test "X$arg" = "X-lc" && continue - ;; - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) - # Do not include libc due to us having libc/libc_r. - test "X$arg" = "X-lc" && continue - ;; - *-*-rhapsody* | *-*-darwin1.[012]) - # Rhapsody C and math libraries are in the System framework - deplibs="$deplibs -framework System" - continue - ;; - *-*-sco3.2v5* | *-*-sco5v6*) - # Causes problems with __ctype - test "X$arg" = "X-lc" && continue - ;; - *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) - # Compiler inserts libc in the correct place for threads to work - test "X$arg" = "X-lc" && continue - ;; - esac - elif test "X$arg" = "X-lc_r"; then - case $host in - *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) - # Do not include libc_r directly, use -pthread flag. - continue - ;; - esac - fi - deplibs="$deplibs $arg" - continue - ;; + -all-static do not do any dynamic linking at all + -avoid-version do not add a version suffix if possible + -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -export-symbols SYMFILE + try to export only the symbols listed in SYMFILE + -export-symbols-regex REGEX + try to export only the symbols matching REGEX + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -module build a library that can dlopened + -no-fast-install disable the fast-install mode + -no-install link a not-installable executable + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -objectlist FILE Use a list of object files found in FILE to specify objects + -precious-files-regex REGEX + don't remove output files matching REGEX + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries + -shared only do dynamic linking of libtool libraries + -shrext SUFFIX override the standard shared library file extension + -static do not do any dynamic linking of uninstalled libtool libraries + -static-libtool-libs + do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + -weak LIBNAME declare that the target provides the LIBNAME interface - # Tru64 UNIX uses -model [arg] to determine the layout of C++ - # classes, name mangling, and exception handling. - -model) - compile_command="$compile_command $arg" - compiler_flags="$compiler_flags $arg" - finalize_command="$finalize_command $arg" - prev=xcompiler - continue - ;; +All other options (arguments beginning with \`-') are ignored. - -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) - compiler_flags="$compiler_flags $arg" - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - continue - ;; +Every other argument is treated as a filename. Files ending in \`.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. - -module) - module=yes - continue - ;; +If the OUTPUT-FILE ends in \`.la', then a libtool library is created, +only library objects (\`.lo' files) may be specified, and \`-rpath' is +required, except when creating a convenience library. - # -64, -mips[0-9] enable 64-bit mode on the SGI compiler - # -r[0-9][0-9]* specifies the processor on the SGI compiler - # -xarch=*, -xtarget=* enable 64-bit mode on the Sun compiler - # +DA*, +DD* enable 64-bit mode on the HP compiler - # -q* pass through compiler args for the IBM compiler - # -m* pass through architecture-specific compiler args for GCC - # -m*, -t[45]*, -txscale* pass through architecture-specific - # compiler args for GCC - # -pg pass through profiling flag for GCC - # @file GCC response files - -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*|-pg| \ - -t[45]*|-txscale*|@*) +If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created +using \`ar' and \`ranlib', or on Windows using \`lib'. - # Unknown arguments in both finalize_command and compile_command need - # to be aesthetically quoted because they are evaled later. - arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - compiler_flags="$compiler_flags $arg" - continue +If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file +is created, otherwise an executable program is created." ;; - -shrext) - prev=shrext - continue - ;; + uninstall) + $ECHO \ +"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... - -no-fast-install) - fast_install=no - continue - ;; +Remove libraries from an installation directory. - -no-install) - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) - # The PATH hackery in wrapper scripts is required on Windows - # in order for the loader to find any dlls it needs. - $echo "$modename: warning: \`-no-install' is ignored for $host" 1>&2 - $echo "$modename: warning: assuming \`-no-fast-install' instead" 1>&2 - fast_install=no - ;; - *) no_install=yes ;; - esac - continue - ;; +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. - -no-undefined) - allow_undefined=no - continue - ;; +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; - -objectlist) - prev=objectlist - continue - ;; + *) + func_fatal_help "invalid operation mode \`$mode'" + ;; + esac - -o) prev=output ;; + $ECHO + $ECHO "Try \`$progname --help' for more information about other modes." - -precious-files-regex) - prev=precious_regex - continue - ;; + exit $? +} - -release) - prev=release - continue - ;; + # Now that we've collected a possible --mode arg, show help if necessary + $opt_help && func_mode_help - -rpath) - prev=rpath - continue - ;; - -R) - prev=xrpath - continue - ;; +# func_mode_execute arg... +func_mode_execute () +{ + $opt_debug + # The first argument is the command name. + cmd="$nonopt" + test -z "$cmd" && \ + func_fatal_help "you must specify a COMMAND" - -R*) - dir=`$echo "X$arg" | $Xsed -e 's/^-R//'` - # We need an absolute path. - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - $echo "$modename: only absolute run-paths are allowed" 1>&2 - exit $EXIT_FAILURE - ;; - esac - case "$xrpath " in - *" $dir "*) ;; - *) xrpath="$xrpath $dir" ;; - esac - continue - ;; - - -static) - # The effects of -static are defined in a previous loop. - # We used to do the same as -all-static on platforms that - # didn't have a PIC flag, but the assumption that the effects - # would be equivalent was wrong. It would break on at least - # Digital Unix and AIX. - continue - ;; + # Handle -dlopen flags immediately. + for file in $execute_dlfiles; do + test -f "$file" \ + || func_fatal_help "\`$file' is not a file" - -thread-safe) - thread_safe=yes - continue - ;; + dir= + case $file in + *.la) + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$lib' is not a valid libtool archive" - -version-info) - prev=vinfo - continue - ;; - -version-number) - prev=vinfo - vinfo_number=yes - continue - ;; + # Read the libtool library. + dlname= + library_names= + func_source "$file" - -Wc,*) - args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wc,//'` - arg= - save_ifs="$IFS"; IFS=',' - for flag in $args; do - IFS="$save_ifs" - case $flag in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - flag="\"$flag\"" - ;; - esac - arg="$arg $wl$flag" - compiler_flags="$compiler_flags $flag" - done - IFS="$save_ifs" - arg=`$echo "X$arg" | $Xsed -e "s/^ //"` - ;; + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && \ + func_warning "\`$file' was not linked with \`-export-dynamic'" + continue + fi - -Wl,*) - args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wl,//'` - arg= - save_ifs="$IFS"; IFS=',' - for flag in $args; do - IFS="$save_ifs" - case $flag in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - flag="\"$flag\"" - ;; - esac - arg="$arg $wl$flag" - compiler_flags="$compiler_flags $wl$flag" - linker_flags="$linker_flags $flag" - done - IFS="$save_ifs" - arg=`$echo "X$arg" | $Xsed -e "s/^ //"` - ;; + func_dirname "$file" "" "." + dir="$func_dirname_result" - -Xcompiler) - prev=xcompiler - continue + if test -f "$dir/$objdir/$dlname"; then + dir="$dir/$objdir" + else + if test ! -f "$dir/$dlname"; then + func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" + fi + fi ;; - -Xlinker) - prev=xlinker - continue + *.lo) + # Just add the directory containing the .lo file. + func_dirname "$file" "" "." + dir="$func_dirname_result" ;; - -XCClinker) - prev=xcclinker + *) + func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" continue ;; + esac - # Some other compiler flag. - -* | +*) - # Unknown arguments in both finalize_command and compile_command need - # to be aesthetically quoted because they are evaled later. - arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - ;; + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir="$absdir" - *.$objext) - # A standard object. - objs="$objs $arg" - ;; + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done - *.lo) - # A libtool-controlled object. + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic="$magic" - # Check to see that this really is a libtool object. - if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - pic_object= - non_pic_object= + # Check if any of the arguments is a wrapper script. + args= + for file + do + case $file in + -*) ;; + *) + # Do a test to see if this is really a libtool program. + if func_ltwrapper_script_p "$file"; then + func_source "$file" + # Transform arg to wrapped name. + file="$progdir/$program" + elif func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + func_source "$func_ltwrapper_scriptname_result" + # Transform arg to wrapped name. + file="$progdir/$program" + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + func_quote_for_eval "$file" + args="$args $func_quote_for_eval_result" + done - # Read the .lo file - # If there is no directory component, then add one. - case $arg in - */* | *\\*) . $arg ;; - *) . ./$arg ;; - esac + if test "X$opt_dry_run" = Xfalse; then + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi - if test -z "$pic_object" || \ - test -z "$non_pic_object" || - test "$pic_object" = none && \ - test "$non_pic_object" = none; then - $echo "$modename: cannot find name of object for \`$arg'" 1>&2 - exit $EXIT_FAILURE - fi + # Restore saved environment variables + for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES + do + eval "if test \"\${save_$lt_var+set}\" = set; then + $lt_var=\$save_$lt_var; export $lt_var + else + $lt_unset $lt_var + fi" + done - # Extract subdirectory from the argument. - xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` - if test "X$xdir" = "X$arg"; then - xdir= - else - xdir="$xdir/" - fi + # Now prepare to actually exec the command. + exec_cmd="\$cmd$args" + else + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" + $ECHO "export $shlibpath_var" + fi + $ECHO "$cmd$args" + exit $EXIT_SUCCESS + fi +} - if test "$pic_object" != none; then - # Prepend the subdirectory the object is found in. - pic_object="$xdir$pic_object" +test "$mode" = execute && func_mode_execute ${1+"$@"} - if test "$prev" = dlfiles; then - if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then - dlfiles="$dlfiles $pic_object" - prev= - continue - else - # If libtool objects are unsupported, then we need to preload. - prev=dlprefiles - fi - fi - # CHECK ME: I think I busted this. -Ossama - if test "$prev" = dlprefiles; then - # Preload the old-style object. - dlprefiles="$dlprefiles $pic_object" - prev= - fi +# func_mode_finish arg... +func_mode_finish () +{ + $opt_debug + libdirs="$nonopt" + admincmds= - # A PIC object. - libobjs="$libobjs $pic_object" - arg="$pic_object" - fi + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for dir + do + libdirs="$libdirs $dir" + done - # Non-PIC object. - if test "$non_pic_object" != none; then - # Prepend the subdirectory the object is found in. - non_pic_object="$xdir$non_pic_object" + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + func_execute_cmds "$finish_cmds" 'admincmds="$admincmds +'"$cmd"'"' + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $opt_dry_run || eval "$cmds" || admincmds="$admincmds + $cmds" + fi + done + fi - # A standard non-PIC object - non_pic_objects="$non_pic_objects $non_pic_object" - if test -z "$pic_object" || test "$pic_object" = none ; then - arg="$non_pic_object" - fi - else - # If the PIC object exists, use it instead. - # $xdir was prepended to $pic_object above. - non_pic_object="$pic_object" - non_pic_objects="$non_pic_objects $non_pic_object" - fi - else - # Only an error if not doing a dry-run. - if test -z "$run"; then - $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 - exit $EXIT_FAILURE - else - # Dry-run case. + # Exit here if they wanted silent mode. + $opt_silent && exit $EXIT_SUCCESS - # Extract subdirectory from the argument. - xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` - if test "X$xdir" = "X$arg"; then - xdir= - else - xdir="$xdir/" - fi + $ECHO "X----------------------------------------------------------------------" | $Xsed + $ECHO "Libraries have been installed in:" + for libdir in $libdirs; do + $ECHO " $libdir" + done + $ECHO + $ECHO "If you ever happen to want to link against installed libraries" + $ECHO "in a given directory, LIBDIR, you must either use libtool, and" + $ECHO "specify the full pathname of the library, or use the \`-LLIBDIR'" + $ECHO "flag during linking and do at least one of the following:" + if test -n "$shlibpath_var"; then + $ECHO " - add LIBDIR to the \`$shlibpath_var' environment variable" + $ECHO " during execution" + fi + if test -n "$runpath_var"; then + $ECHO " - add LIBDIR to the \`$runpath_var' environment variable" + $ECHO " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" - pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` - non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` - libobjs="$libobjs $pic_object" - non_pic_objects="$non_pic_objects $non_pic_object" - fi - fi - ;; + $ECHO " - use the \`$flag' linker flag" + fi + if test -n "$admincmds"; then + $ECHO " - have your system administrator run these commands:$admincmds" + fi + if test -f /etc/ld.so.conf; then + $ECHO " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" + fi + $ECHO - *.$libext) - # An archive. - deplibs="$deplibs $arg" - old_deplibs="$old_deplibs $arg" - continue + $ECHO "See any operating system documentation about shared libraries for" + case $host in + solaris2.[6789]|solaris2.1[0-9]) + $ECHO "more information, such as the ld(1), crle(1) and ld.so(8) manual" + $ECHO "pages." ;; + *) + $ECHO "more information, such as the ld(1) and ld.so(8) manual pages." + ;; + esac + $ECHO "X----------------------------------------------------------------------" | $Xsed + exit $EXIT_SUCCESS +} - *.la) - # A libtool-controlled library. +test "$mode" = finish && func_mode_finish ${1+"$@"} - if test "$prev" = dlfiles; then - # This library was specified with -dlopen. - dlfiles="$dlfiles $arg" - prev= - elif test "$prev" = dlprefiles; then - # The library was specified with -dlpreopen. - dlprefiles="$dlprefiles $arg" - prev= - else - deplibs="$deplibs $arg" - fi - continue - ;; - # Some other compiler argument. - *) - # Unknown arguments in both finalize_command and compile_command need - # to be aesthetically quoted because they are evaled later. - arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - ;; - esac # arg +# func_mode_install arg... +func_mode_install () +{ + $opt_debug + # There may be an optional sh(1) argument at the beginning of + # install_prog (especially on Windows NT). + if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || + # Allow the use of GNU shtool's install command. + $ECHO "X$nonopt" | $GREP shtool >/dev/null; then + # Aesthetically quote it. + func_quote_for_eval "$nonopt" + install_prog="$func_quote_for_eval_result " + arg=$1 + shift + else + install_prog= + arg=$nonopt + fi - # Now actually substitute the argument into the commands. - if test -n "$arg"; then - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + func_quote_for_eval "$arg" + install_prog="$install_prog$func_quote_for_eval_result" + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=no + stripme= + for arg + do + if test -n "$dest"; then + files="$files $dest" + dest=$arg + continue fi - done # argument parsing loop - if test -n "$prev"; then - $echo "$modename: the \`$prevarg' option requires an argument" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi + case $arg in + -d) isdir=yes ;; + -f) + case " $install_prog " in + *[\\\ /]cp\ *) ;; + *) prev=$arg ;; + esac + ;; + -g | -m | -o) + prev=$arg + ;; + -s) + stripme=" -s" + continue + ;; + -*) + ;; + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + prev= + else + dest=$arg + continue + fi + ;; + esac - if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then - eval arg=\"$export_dynamic_flag_spec\" - compile_command="$compile_command $arg" - finalize_command="$finalize_command $arg" - fi + # Aesthetically quote the argument. + func_quote_for_eval "$arg" + install_prog="$install_prog $func_quote_for_eval_result" + done - oldlibs= - # calculate the name of the file, without its directory - outputname=`$echo "X$output" | $Xsed -e 's%^.*/%%'` - libobjs_save="$libobjs" + test -z "$install_prog" && \ + func_fatal_help "you must specify an install program" - if test -n "$shlibpath_var"; then - # get the directories listed in $shlibpath_var - eval shlib_search_path=\`\$echo \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\` - else - shlib_search_path= - fi - eval sys_lib_search_path=\"$sys_lib_search_path_spec\" - eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + test -n "$prev" && \ + func_fatal_help "the \`$prev' option requires an argument" - output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'` - if test "X$output_objdir" = "X$output"; then - output_objdir="$objdir" - else - output_objdir="$output_objdir/$objdir" - fi - # Create the object directory. - if test ! -d "$output_objdir"; then - $show "$mkdir $output_objdir" - $run $mkdir $output_objdir - exit_status=$? - if test "$exit_status" -ne 0 && test ! -d "$output_objdir"; then - exit $exit_status + if test -z "$files"; then + if test -z "$dest"; then + func_fatal_help "no file or destination specified" + else + func_fatal_help "you must specify a destination" fi fi - # Determine the type of output - case $output in - "") - $echo "$modename: you must specify an output file" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - ;; - *.$libext) linkmode=oldlib ;; - *.lo | *.$objext) linkmode=obj ;; - *.la) linkmode=lib ;; - *) linkmode=prog ;; # Anything else should be a program. - esac + # Strip any trailing slash from the destination. + func_stripname '' '/' "$dest" + dest=$func_stripname_result - case $host in - *cygwin* | *mingw* | *pw32*) - # don't eliminate duplications in $postdeps and $predeps - duplicate_compiler_generated_deps=yes - ;; + # Check to see that the destination is a directory. + test -d "$dest" && isdir=yes + if test "$isdir" = yes; then + destdir="$dest" + destname= + else + func_dirname_and_basename "$dest" "" "." + destdir="$func_dirname_result" + destname="$func_basename_result" + + # Not a directory, so check to see that there is only one file specified. + set dummy $files; shift + test "$#" -gt 1 && \ + func_fatal_help "\`$dest' is not a directory" + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; *) - duplicate_compiler_generated_deps=$duplicate_deps + for file in $files; do + case $file in + *.lo) ;; + *) + func_fatal_help "\`$destdir' must be an absolute directory name" + ;; + esac + done ;; esac - specialdeplibs= - libs= - # Find all interdependent deplibs by searching for libraries - # that are linked more than once (e.g. -la -lb -la) - for deplib in $deplibs; do - if test "X$duplicate_deps" = "Xyes" ; then - case "$libs " in - *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; - esac - fi - libs="$libs $deplib" - done + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" - if test "$linkmode" = lib; then - libs="$predeps $libs $compiler_lib_search_path $postdeps" + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do - # Compute libraries that are listed more than once in $predeps - # $postdeps and mark them as special (i.e., whose duplicates are - # not to be eliminated). - pre_post_deps= - if test "X$duplicate_compiler_generated_deps" = "Xyes" ; then - for pre_post_dep in $predeps $postdeps; do - case "$pre_post_deps " in - *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;; + # Do each installation. + case $file in + *.$libext) + # Do the static libraries later. + staticlibs="$staticlibs $file" + ;; + + *.la) + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$file' is not a valid libtool archive" + + library_names= + old_library= + relink_command= + func_source "$file" + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) current_libdirs="$current_libdirs $libdir" ;; esac - pre_post_deps="$pre_post_deps $pre_post_dep" - done - fi - pre_post_deps= - fi + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) future_libdirs="$future_libdirs $libdir" ;; + esac + fi - deplibs= - newdependency_libs= - newlib_search_path= - need_relink=no # whether we're linking any uninstalled libtool libraries - notinst_deplibs= # not-installed libtool libraries - case $linkmode in - lib) - passes="conv link" - for file in $dlfiles $dlprefiles; do - case $file in - *.la) ;; - *) - $echo "$modename: libraries can \`-dlopen' only libtool libraries: $file" 1>&2 - exit $EXIT_FAILURE + func_dirname "$file" "/" "" + dir="$func_dirname_result" + dir="$dir$objdir" + + if test -n "$relink_command"; then + # Determine the prefix the user has applied to our future dir. + inst_prefix_dir=`$ECHO "X$destdir" | $Xsed -e "s%$libdir\$%%"` + + # Don't allow the user to place us outside of our expected + # location b/c this prevents finding dependent libraries that + # are installed to the same prefix. + # At present, this check doesn't affect windows .dll's that + # are installed into $libdir/../bin (currently, that works fine) + # but it's something to keep an eye on. + test "$inst_prefix_dir" = "$destdir" && \ + func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" + + if test -n "$inst_prefix_dir"; then + # Stick the inst_prefix_dir data into the link command. + relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` + else + relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%%"` + fi + + func_warning "relinking \`$file'" + func_show_eval "$relink_command" \ + 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' + fi + + # See the names of the shared library. + set dummy $library_names; shift + if test -n "$1"; then + realname="$1" + shift + + srcname="$realname" + test -n "$relink_command" && srcname="$realname"T + + # Install the shared library and build the symlinks. + func_show_eval "$install_prog $dir/$srcname $destdir/$realname" \ + 'exit $?' + tstripme="$stripme" + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + case $realname in + *.dll.a) + tstripme="" + ;; + esac ;; esac - done - ;; - prog) - compile_deplibs= - finalize_deplibs= - alldeplibs=no - newdlfiles= - newdlprefiles= - passes="conv scan dlopen dlpreopen link" - ;; - *) passes="conv" - ;; - esac - for pass in $passes; do - if test "$linkmode,$pass" = "lib,link" || - test "$linkmode,$pass" = "prog,scan"; then - libs="$deplibs" - deplibs= - fi - if test "$linkmode" = prog; then - case $pass in - dlopen) libs="$dlfiles" ;; - dlpreopen) libs="$dlprefiles" ;; - link) libs="$deplibs %DEPLIBS% $dependency_libs" ;; - esac - fi - if test "$pass" = dlopen; then - # Collect dlpreopened libraries - save_deplibs="$deplibs" - deplibs= - fi - for deplib in $libs; do - lib= - found=no - case $deplib in - -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - compiler_flags="$compiler_flags $deplib" + if test -n "$tstripme" && test -n "$striplib"; then + func_show_eval "$striplib $destdir/$realname" 'exit $?' fi - continue - ;; - -l*) - if test "$linkmode" != lib && test "$linkmode" != prog; then - $echo "$modename: warning: \`-l' is ignored for archives/objects" 1>&2 - continue - fi - name=`$echo "X$deplib" | $Xsed -e 's/^-l//'` - for searchdir in $newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path; do - for search_ext in .la $std_shrext .so .a; do - # Search the libtool library - lib="$searchdir/lib${name}${search_ext}" - if test -f "$lib"; then - if test "$search_ext" = ".la"; then - found=yes - else - found=no - fi - break 2 - fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + # Try `ln -sf' first, because the `ln' binary might depend on + # the symlink we replace! Solaris /bin/ln does not understand -f, + # so we also need to try rm && ln -s. + for linkname + do + test "$linkname" != "$realname" \ + && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" done - done - if test "$found" != yes; then - # deplib doesn't seem to be a libtool library - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - deplibs="$deplib $deplibs" - test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" - fi - continue - else # deplib is a libtool library - # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, - # We need to do some special things here, and not later. - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then - case " $predeps $postdeps " in - *" $deplib "*) - if (${SED} -e '2q' $lib | - grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - library_names= - old_library= - case $lib in - */* | *\\*) . $lib ;; - *) . ./$lib ;; - esac - for l in $old_library $library_names; do - ll="$l" - done - if test "X$ll" = "X$old_library" ; then # only static version available - found=no - ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` - test "X$ladir" = "X$lib" && ladir="." - lib=$ladir/$old_library - if test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - deplibs="$deplib $deplibs" - test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" - fi - continue - fi - fi - ;; - *) ;; - esac - fi - fi - ;; # -l - -L*) - case $linkmode in - lib) - deplibs="$deplib $deplibs" - test "$pass" = conv && continue - newdependency_libs="$deplib $newdependency_libs" - newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` - ;; - prog) - if test "$pass" = conv; then - deplibs="$deplib $deplibs" - continue - fi - if test "$pass" = scan; then - deplibs="$deplib $deplibs" - else - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - fi - newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` - ;; - *) - $echo "$modename: warning: \`-L' is ignored for archives/objects" 1>&2 - ;; - esac # linkmode - continue - ;; # -L - -R*) - if test "$pass" = link; then - dir=`$echo "X$deplib" | $Xsed -e 's/^-R//'` - # Make sure the xrpath contains only unique directories. - case "$xrpath " in - *" $dir "*) ;; - *) xrpath="$xrpath $dir" ;; - esac - fi - deplibs="$deplib $deplibs" - continue - ;; - *.la) lib="$deplib" ;; - *.$libext) - if test "$pass" = conv; then - deplibs="$deplib $deplibs" - continue - fi - case $linkmode in - lib) - valid_a_lib=no - case $deplibs_check_method in - match_pattern*) - set dummy $deplibs_check_method - match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` - if eval $echo \"$deplib\" 2>/dev/null \ - | $SED 10q \ - | $EGREP "$match_pattern_regex" > /dev/null; then - valid_a_lib=yes - fi - ;; - pass_all) - valid_a_lib=yes - ;; - esac - if test "$valid_a_lib" != yes; then - $echo - $echo "*** Warning: Trying to link with static lib archive $deplib." - $echo "*** I have the capability to make that library automatically link in when" - $echo "*** you link to this library. But I can only do this if you have a" - $echo "*** shared version of the library, which you do not appear to have" - $echo "*** because the file extensions .$libext of this argument makes me believe" - $echo "*** that it is just a static archive that I should not used here." - else - $echo - $echo "*** Warning: Linking the shared library $output against the" - $echo "*** static library $deplib is not portable!" - deplibs="$deplib $deplibs" - fi - continue - ;; - prog) - if test "$pass" != link; then - deplibs="$deplib $deplibs" - else - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - fi - continue - ;; - esac # linkmode - ;; # *.$libext - *.lo | *.$objext) - if test "$pass" = conv; then - deplibs="$deplib $deplibs" - elif test "$linkmode" = prog; then - if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then - # If there is no dlopen support or we're linking statically, - # we need to preload. - newdlprefiles="$newdlprefiles $deplib" - compile_deplibs="$deplib $compile_deplibs" - finalize_deplibs="$deplib $finalize_deplibs" - else - newdlfiles="$newdlfiles $deplib" - fi fi - continue - ;; - %DEPLIBS%) - alldeplibs=yes - continue - ;; - esac # case $deplib - if test "$found" = yes || test -f "$lib"; then : - else - $echo "$modename: cannot find the library \`$lib' or unhandled argument \`$deplib'" 1>&2 - exit $EXIT_FAILURE - fi - # Check to see that this really is a libtool archive. - if (${SED} -e '2q' $lib | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : - else - $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 - exit $EXIT_FAILURE + # Do each command in the postinstall commands. + lib="$destdir/$realname" + func_execute_cmds "$postinstall_cmds" 'exit $?' fi - ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` - test "X$ladir" = "X$lib" && ladir="." + # Install the pseudo-library for information purposes. + func_basename "$file" + name="$func_basename_result" + instname="$dir/$name"i + func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' - dlname= - dlopen= - dlpreopen= - libdir= - library_names= - old_library= - # If the library was installed with an old release of libtool, - # it will not redefine variables installed, or shouldnotlink - installed=yes - shouldnotlink=no - avoidtemprpath= + # Maybe install the static library, too. + test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" + ;; + *.lo) + # Install (i.e. copy) a libtool object. - # Read the .la file - case $lib in - */* | *\\*) . $lib ;; - *) . ./$lib ;; + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + func_basename "$file" + destfile="$func_basename_result" + destfile="$destdir/$destfile" + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + func_lo2o "$destfile" + staticdest=$func_lo2o_result + ;; + *.$objext) + staticdest="$destfile" + destfile= + ;; + *) + func_fatal_help "cannot copy a libtool object to \`$destfile'" + ;; esac - if test "$linkmode,$pass" = "lib,link" || - test "$linkmode,$pass" = "prog,scan" || - { test "$linkmode" != prog && test "$linkmode" != lib; }; then - test -n "$dlopen" && dlfiles="$dlfiles $dlopen" - test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen" + # Install the libtool object if requested. + test -n "$destfile" && \ + func_show_eval "$install_prog $file $destfile" 'exit $?' + + # Install the old object if enabled. + if test "$build_old_libs" = yes; then + # Deduce the name of the old-style object file. + func_lo2o "$file" + staticobj=$func_lo2o_result + func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' fi + exit $EXIT_SUCCESS + ;; - if test "$pass" = conv; then - # Only check for convenience libraries - deplibs="$lib $deplibs" - if test -z "$libdir"; then - if test -z "$old_library"; then - $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 - exit $EXIT_FAILURE + *) + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + func_basename "$file" + destfile="$func_basename_result" + destfile="$destdir/$destfile" + fi + + # If the file is missing, and there is a .exe on the end, strip it + # because it is most likely a libtool script we actually want to + # install + stripped_ext="" + case $file in + *.exe) + if test ! -f "$file"; then + func_stripname '' '.exe' "$file" + file=$func_stripname_result + stripped_ext=".exe" fi - # It is a libtool convenience library, so add in its objects. - convenience="$convenience $ladir/$objdir/$old_library" - old_convenience="$old_convenience $ladir/$objdir/$old_library" - tmp_libs= - for deplib in $dependency_libs; do - deplibs="$deplib $deplibs" - if test "X$duplicate_deps" = "Xyes" ; then - case "$tmp_libs " in - *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; - esac - fi - tmp_libs="$tmp_libs $deplib" - done - elif test "$linkmode" != prog && test "$linkmode" != lib; then - $echo "$modename: \`$lib' is not a convenience library" 1>&2 - exit $EXIT_FAILURE - fi - continue - fi # $pass = conv + ;; + esac + # Do a test to see if this is really a libtool program. + case $host in + *cygwin* | *mingw*) + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + wrapper=$func_ltwrapper_scriptname_result + else + func_stripname '' '.exe' "$file" + wrapper=$func_stripname_result + fi + ;; + *) + wrapper=$file + ;; + esac + if func_ltwrapper_script_p "$wrapper"; then + notinst_deplibs= + relink_command= - # Get the name of the library we link against. - linklib= - for l in $old_library $library_names; do - linklib="$l" - done - if test -z "$linklib"; then - $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 - exit $EXIT_FAILURE - fi + func_source "$wrapper" - # This library was specified with -dlopen. - if test "$pass" = dlopen; then - if test -z "$libdir"; then - $echo "$modename: cannot -dlopen a convenience library: \`$lib'" 1>&2 - exit $EXIT_FAILURE - fi - if test -z "$dlname" || - test "$dlopen_support" != yes || - test "$build_libtool_libs" = no; then - # If there is no dlname, no dlopen support or we're linking - # statically, we need to preload. We also need to preload any - # dependent libraries so libltdl's deplib preloader doesn't - # bomb out in the load deplibs phase. - dlprefiles="$dlprefiles $lib $dependency_libs" + # Check the variables that should have been set. + test -z "$generated_by_libtool_version" && \ + func_fatal_error "invalid libtool wrapper script \`$wrapper'" + + finalize=yes + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + func_source "$lib" + fi + libfile="$libdir/"`$ECHO "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test + if test -n "$libdir" && test ! -f "$libfile"; then + func_warning "\`$lib' has not been installed in \`$libdir'" + finalize=no + fi + done + + relink_command= + func_source "$wrapper" + + outputname= + if test "$fast_install" = no && test -n "$relink_command"; then + $opt_dry_run || { + if test "$finalize" = yes; then + tmpdir=`func_mktempdir` + func_basename "$file$stripped_ext" + file="$func_basename_result" + outputname="$tmpdir/$file" + # Replace the output file specification. + relink_command=`$ECHO "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'` + + $opt_silent || { + func_quote_for_expand "$relink_command" + eval "func_echo $func_quote_for_expand_result" + } + if eval "$relink_command"; then : + else + func_error "error: relink \`$file' with the above command before installing it" + $opt_dry_run || ${RM}r "$tmpdir" + continue + fi + file="$outputname" + else + func_warning "cannot relink \`$file'" + fi + } else - newdlfiles="$newdlfiles $lib" + # Install the binary that we compiled earlier. + file=`$ECHO "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"` fi - continue - fi # $pass = dlopen + fi - # We need an absolute path. - case $ladir in - [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; - *) - abs_ladir=`cd "$ladir" && pwd` - if test -z "$abs_ladir"; then - $echo "$modename: warning: cannot determine absolute directory name of \`$ladir'" 1>&2 - $echo "$modename: passing it literally to the linker, although it might fail" 1>&2 - abs_ladir="$ladir" - fi + # remove .exe since cygwin /usr/bin/install will append another + # one anyway + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + func_stripname '' '.exe' "$destfile" + destfile=$func_stripname_result + ;; + esac ;; esac - laname=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' + $opt_dry_run || if test -n "$outputname"; then + ${RM}r "$tmpdir" + fi + ;; + esac + done - # Find the relevant object directory and library name. - if test "X$installed" = Xyes; then - if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then - $echo "$modename: warning: library \`$lib' was moved." 1>&2 - dir="$ladir" - absdir="$abs_ladir" - libdir="$abs_ladir" - else - dir="$libdir" - absdir="$libdir" - fi - test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes - else - if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then - dir="$ladir" - absdir="$abs_ladir" - # Remove this search path later - notinst_path="$notinst_path $abs_ladir" - else - dir="$ladir/$objdir" - absdir="$abs_ladir/$objdir" - # Remove this search path later - notinst_path="$notinst_path $abs_ladir" - fi - fi # $installed = yes - name=`$echo "X$laname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` + for file in $staticlibs; do + func_basename "$file" + name="$func_basename_result" - # This library was specified with -dlpreopen. - if test "$pass" = dlpreopen; then - if test -z "$libdir"; then - $echo "$modename: cannot -dlpreopen a convenience library: \`$lib'" 1>&2 - exit $EXIT_FAILURE - fi - # Prefer using a static library (so that no silly _DYNAMIC symbols - # are required to link). - if test -n "$old_library"; then - newdlprefiles="$newdlprefiles $dir/$old_library" - # Otherwise, use the dlname, so that lt_dlopen finds it. - elif test -n "$dlname"; then - newdlprefiles="$newdlprefiles $dir/$dlname" - else - newdlprefiles="$newdlprefiles $dir/$linklib" - fi - fi # $pass = dlpreopen + # Set up the ranlib parameters. + oldlib="$destdir/$name" - if test -z "$libdir"; then - # Link the convenience library - if test "$linkmode" = lib; then - deplibs="$dir/$old_library $deplibs" - elif test "$linkmode,$pass" = "prog,link"; then - compile_deplibs="$dir/$old_library $compile_deplibs" - finalize_deplibs="$dir/$old_library $finalize_deplibs" - else - deplibs="$lib $deplibs" # used for prog,scan pass - fi - continue - fi + func_show_eval "$install_prog \$file \$oldlib" 'exit $?' + if test -n "$stripme" && test -n "$old_striplib"; then + func_show_eval "$old_striplib $oldlib" 'exit $?' + fi - if test "$linkmode" = prog && test "$pass" != link; then - newlib_search_path="$newlib_search_path $ladir" - deplibs="$lib $deplibs" + # Do each command in the postinstall commands. + func_execute_cmds "$old_postinstall_cmds" 'exit $?' + done - linkalldeplibs=no - if test "$link_all_deplibs" != no || test -z "$library_names" || - test "$build_libtool_libs" = no; then - linkalldeplibs=yes - fi + test -n "$future_libdirs" && \ + func_warning "remember to run \`$progname --finish$future_libdirs'" - tmp_libs= - for deplib in $dependency_libs; do - case $deplib in - -L*) newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'`;; ### testsuite: skip nested quoting test - esac - # Need to link against all dependency_libs? - if test "$linkalldeplibs" = yes; then - deplibs="$deplib $deplibs" - else - # Need to hardcode shared library paths - # or/and link against static libraries - newdependency_libs="$deplib $newdependency_libs" - fi - if test "X$duplicate_deps" = "Xyes" ; then - case "$tmp_libs " in - *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; - esac - fi - tmp_libs="$tmp_libs $deplib" - done # for deplib - continue - fi # $linkmode = prog... + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + $opt_dry_run && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' + else + exit $EXIT_SUCCESS + fi +} - if test "$linkmode,$pass" = "prog,link"; then - if test -n "$library_names" && - { test "$prefer_static_libs" = no || test -z "$old_library"; }; then - # We need to hardcode the library path - if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then - # Make sure the rpath contains only unique directories. - case "$temp_rpath " in - *" $dir "*) ;; - *" $absdir "*) ;; - *) temp_rpath="$temp_rpath $absdir" ;; - esac - fi +test "$mode" = install && func_mode_install ${1+"$@"} - # Hardcode the library path. - # Skip directories that are in the system default run-time - # search path. - case " $sys_lib_dlsearch_path " in - *" $absdir "*) ;; - *) - case "$compile_rpath " in - *" $absdir "*) ;; - *) compile_rpath="$compile_rpath $absdir" - esac - ;; - esac - case " $sys_lib_dlsearch_path " in - *" $libdir "*) ;; - *) - case "$finalize_rpath " in - *" $libdir "*) ;; - *) finalize_rpath="$finalize_rpath $libdir" - esac - ;; - esac - fi # $linkmode,$pass = prog,link... - if test "$alldeplibs" = yes && - { test "$deplibs_check_method" = pass_all || - { test "$build_libtool_libs" = yes && - test -n "$library_names"; }; }; then - # We only need to search for static libraries - continue - fi - fi +# func_generate_dlsyms outputname originator pic_p +# Extract symbols from dlprefiles and create ${outputname}S.o with +# a dlpreopen symbol table. +func_generate_dlsyms () +{ + $opt_debug + my_outputname="$1" + my_originator="$2" + my_pic_p="${3-no}" + my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` + my_dlsyms= + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + my_dlsyms="${my_outputname}S.c" + else + func_error "not configured to extract global symbols from dlpreopened files" + fi + fi - link_static=no # Whether the deplib will be linked statically - use_static_libs=$prefer_static_libs - if test "$use_static_libs" = built && test "$installed" = yes ; then - use_static_libs=no - fi - if test -n "$library_names" && - { test "$use_static_libs" = no || test -z "$old_library"; }; then - if test "$installed" = no; then - notinst_deplibs="$notinst_deplibs $lib" - need_relink=yes + if test -n "$my_dlsyms"; then + case $my_dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist="$output_objdir/${my_outputname}.nm" + + func_show_eval "$RM $nlist ${nlist}S ${nlist}T" + + # Parse the name list into a source file. + func_verbose "creating $output_objdir/$my_dlsyms" + + $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ +/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ +/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +/* External symbol declarations for the compiler. */\ +" + + if test "$dlself" = yes; then + func_verbose "generating symbol list for \`$output'" + + $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$ECHO "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + for progfile in $progfiles; do + func_verbose "extracting global C symbols from \`$progfile'" + $opt_dry_run || eval "$NM $progfile | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $opt_dry_run || { + eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } fi - # This is a shared library - # Warn about portability, can't link against -module's on - # some systems (darwin) - if test "$shouldnotlink" = yes && test "$pass" = link ; then - $echo - if test "$linkmode" = prog; then - $echo "*** Warning: Linking the executable $output against the loadable module" - else - $echo "*** Warning: Linking the shared library $output against the loadable module" - fi - $echo "*** $linklib is not portable!" + if test -n "$export_symbols_regex"; then + $opt_dry_run || { + eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } fi - if test "$linkmode" = lib && - test "$hardcode_into_libs" = yes; then - # Hardcode the library path. - # Skip directories that are in the system default run-time - # search path. - case " $sys_lib_dlsearch_path " in - *" $absdir "*) ;; - *) - case "$compile_rpath " in - *" $absdir "*) ;; - *) compile_rpath="$compile_rpath $absdir" + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols="$output_objdir/$outputname.exp" + $opt_dry_run || { + $RM $export_symbols + eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' + ;; esac - ;; - esac - case " $sys_lib_dlsearch_path " in - *" $libdir "*) ;; - *) - case "$finalize_rpath " in - *" $libdir "*) ;; - *) finalize_rpath="$finalize_rpath $libdir" + } + else + $opt_dry_run || { + eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' + eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + case $host in + *cygwin | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' + ;; esac - ;; - esac + } fi + fi - if test -n "$old_archive_from_expsyms_cmds"; then - # figure out the soname - set dummy $library_names - realname="$2" - shift; shift - libname=`eval \\$echo \"$libname_spec\"` - # use dlname if we got it. it's perfectly good, no? - if test -n "$dlname"; then - soname="$dlname" - elif test -n "$soname_spec"; then - # bleh windows - case $host in - *cygwin* | mingw*) - major=`expr $current - $age` - versuffix="-$major" - ;; - esac - eval soname=\"$soname_spec\" - else - soname="$realname" - fi + for dlprefile in $dlprefiles; do + func_verbose "extracting global C symbols from \`$dlprefile'" + func_basename "$dlprefile" + name="$func_basename_result" + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + eval "$NM $dlprefile 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + done - # Make a new name for the extract_expsyms_cmds to use - soroot="$soname" - soname=`$echo $soroot | ${SED} -e 's/^.*\///'` - newlib="libimp-`$echo $soname | ${SED} 's/^lib//;s/\.dll$//'`.a" + $opt_dry_run || { + # Make sure we have at least an empty file. + test -f "$nlist" || : > "$nlist" + + if test -n "$exclude_expsyms"; then + $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T + $MV "$nlist"T "$nlist" + fi - # If the library has no export list, then create one now - if test -f "$output_objdir/$soname-def"; then : - else - $show "extracting exported symbol list from \`$soname'" - save_ifs="$IFS"; IFS='~' - cmds=$extract_expsyms_cmds - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - fi + # Try sorting and uniquifying the output. + if $GREP -v "^: " < "$nlist" | + if sort -k 3 /dev/null 2>&1; then + sort -k 3 + else + sort +2 + fi | + uniq > "$nlist"S; then + : + else + $GREP -v "^: " < "$nlist" > "$nlist"S + fi - # Create $newlib - if test -f "$output_objdir/$newlib"; then :; else - $show "generating import library for \`$soname'" - save_ifs="$IFS"; IFS='~' - cmds=$old_archive_from_expsyms_cmds - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - fi - # make sure the library variables are pointing to the new library - dir=$output_objdir - linklib=$newlib - fi # test -n "$old_archive_from_expsyms_cmds" + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' + else + $ECHO '/* NONE */' >> "$output_objdir/$my_dlsyms" + fi - if test "$linkmode" = prog || test "$mode" != relink; then - add_shlibpath= - add_dir= - add= - lib_linked=yes - case $hardcode_action in - immediate | unsupported) - if test "$hardcode_direct" = no; then - add="$dir/$linklib" - case $host in - *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; - *-*-sysv4*uw2*) add_dir="-L$dir" ;; - *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ - *-*-unixware7*) add_dir="-L$dir" ;; - *-*-darwin* ) - # if the lib is a module then we can not link against - # it, someone is ignoring the new warnings I added - if /usr/bin/file -L $add 2> /dev/null | - $EGREP ": [^:]* bundle" >/dev/null ; then - $echo "** Warning, lib $linklib is a module, not a shared library" - if test -z "$old_library" ; then - $echo - $echo "** And there doesn't seem to be a static archive available" - $echo "** The link will probably fail, sorry" - else - add="$dir/$old_library" - fi - fi - esac - elif test "$hardcode_minus_L" = no; then - case $host in - *-*-sunos*) add_shlibpath="$dir" ;; - esac - add_dir="-L$dir" - add="-l$name" - elif test "$hardcode_shlibpath_var" = no; then - add_shlibpath="$dir" - add="-l$name" - else - lib_linked=no - fi - ;; - relink) - if test "$hardcode_direct" = yes; then - add="$dir/$linklib" - elif test "$hardcode_minus_L" = yes; then - add_dir="-L$dir" - # Try looking first in the location we're being installed to. - if test -n "$inst_prefix_dir"; then - case $libdir in - [\\/]*) - add_dir="$add_dir -L$inst_prefix_dir$libdir" - ;; - esac - fi - add="-l$name" - elif test "$hardcode_shlibpath_var" = yes; then - add_shlibpath="$dir" - add="-l$name" - else - lib_linked=no - fi - ;; - *) lib_linked=no ;; - esac + $ECHO >> "$output_objdir/$my_dlsyms" "\ - if test "$lib_linked" != yes; then - $echo "$modename: configuration error: unsupported hardcode properties" - exit $EXIT_FAILURE - fi +/* The mapping between symbol names and symbols. */ +typedef struct { + const char *name; + void *address; +} lt_dlsymlist; +" + case $host in + *cygwin* | *mingw* | *cegcc* ) + $ECHO >> "$output_objdir/$my_dlsyms" "\ +/* DATA imports from DLLs on WIN32 con't be const, because + runtime relocations are performed -- see ld's documentation + on pseudo-relocs. */" + lt_dlsym_const= ;; + *osf5*) + echo >> "$output_objdir/$my_dlsyms" "\ +/* This system does not cope well with relocations in const data */" + lt_dlsym_const= ;; + *) + lt_dlsym_const=const ;; + esac - if test -n "$add_shlibpath"; then - case :$compile_shlibpath: in - *":$add_shlibpath:"*) ;; - *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;; - esac - fi - if test "$linkmode" = prog; then - test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" - test -n "$add" && compile_deplibs="$add $compile_deplibs" - else - test -n "$add_dir" && deplibs="$add_dir $deplibs" - test -n "$add" && deplibs="$add $deplibs" - if test "$hardcode_direct" != yes && \ - test "$hardcode_minus_L" != yes && \ - test "$hardcode_shlibpath_var" = yes; then - case :$finalize_shlibpath: in - *":$libdir:"*) ;; - *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; - esac - fi - fi - fi + $ECHO >> "$output_objdir/$my_dlsyms" "\ +extern $lt_dlsym_const lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[]; +$lt_dlsym_const lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[] = +{\ + { \"$my_originator\", (void *) 0 }," - if test "$linkmode" = prog || test "$mode" = relink; then - add_shlibpath= - add_dir= - add= - # Finalize command for both is simple: just hardcode it. - if test "$hardcode_direct" = yes; then - add="$libdir/$linklib" - elif test "$hardcode_minus_L" = yes; then - add_dir="-L$libdir" - add="-l$name" - elif test "$hardcode_shlibpath_var" = yes; then - case :$finalize_shlibpath: in - *":$libdir:"*) ;; - *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; - esac - add="-l$name" - elif test "$hardcode_automatic" = yes; then - if test -n "$inst_prefix_dir" && - test -f "$inst_prefix_dir$libdir/$linklib" ; then - add="$inst_prefix_dir$libdir/$linklib" - else - add="$libdir/$linklib" - fi - else - # We cannot seem to hardcode it, guess we'll fake it. - add_dir="-L$libdir" - # Try looking first in the location we're being installed to. - if test -n "$inst_prefix_dir"; then - case $libdir in - [\\/]*) - add_dir="$add_dir -L$inst_prefix_dir$libdir" - ;; - esac - fi - add="-l$name" - fi + case $need_lib_prefix in + no) + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + *) + eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + esac + $ECHO >> "$output_objdir/$my_dlsyms" "\ + {0, (void *) 0} +}; - if test "$linkmode" = prog; then - test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" - test -n "$add" && finalize_deplibs="$add $finalize_deplibs" - else - test -n "$add_dir" && deplibs="$add_dir $deplibs" - test -n "$add" && deplibs="$add $deplibs" - fi - fi - elif test "$linkmode" = prog; then - # Here we assume that one of hardcode_direct or hardcode_minus_L - # is not unsupported. This is valid on all known static and - # shared platforms. - if test "$hardcode_direct" != unsupported; then - test -n "$old_library" && linklib="$old_library" - compile_deplibs="$dir/$linklib $compile_deplibs" - finalize_deplibs="$dir/$linklib $finalize_deplibs" - else - compile_deplibs="-l$name -L$dir $compile_deplibs" - finalize_deplibs="-l$name -L$dir $finalize_deplibs" - fi - elif test "$build_libtool_libs" = yes; then - # Not a shared library - if test "$deplibs_check_method" != pass_all; then - # We're trying link a shared library against a static one - # but the system doesn't support it. +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_${my_prefix}_LTX_preloaded_symbols; +} +#endif - # Just print a warning and add the library to dependency_libs so - # that the program can be linked against the static library. - $echo - $echo "*** Warning: This system can not link to static lib archive $lib." - $echo "*** I have the capability to make that library automatically link in when" - $echo "*** you link to this library. But I can only do this if you have a" - $echo "*** shared version of the library, which you do not appear to have." - if test "$module" = yes; then - $echo "*** But as you try to build a module library, libtool will still create " - $echo "*** a static module, that should work as long as the dlopening application" - $echo "*** is linked with the -dlopen flag to resolve symbols at runtime." - if test -z "$global_symbol_pipe"; then - $echo - $echo "*** However, this would only work if libtool was able to extract symbol" - $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" - $echo "*** not find such a program. So, this module is probably useless." - $echo "*** \`nm' from GNU binutils and a full rebuild may help." - fi - if test "$build_old_libs" = no; then - build_libtool_libs=module - build_old_libs=yes - else - build_libtool_libs=no - fi +#ifdef __cplusplus +} +#endif\ +" + } # !$opt_dry_run + + pic_flag_for_symtable= + case "$compile_command " in + *" -static "*) ;; + *) + case $host in + # compiling the symbol table file with pic_flag works around + # a FreeBSD bug that causes programs to crash when -lm is + # linked before any other PIC object. But we must not use + # pic_flag when linking with -static. The problem exists in + # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. + *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; + *-*-hpux*) + pic_flag_for_symtable=" $pic_flag" ;; + *) + if test "X$my_pic_p" != Xno; then + pic_flag_for_symtable=" $pic_flag" fi + ;; + esac + ;; + esac + symtab_cflags= + for arg in $LTCFLAGS; do + case $arg in + -pie | -fpie | -fPIE) ;; + *) symtab_cflags="$symtab_cflags $arg" ;; + esac + done + + # Now compile the dynamic symbol file. + func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' + + # Clean up the generated files. + func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' + + # Transform the symbol file into the correct name. + symfileobj="$output_objdir/${my_outputname}S.$objext" + case $host in + *cygwin* | *mingw* | *cegcc* ) + if test -f "$output_objdir/$my_outputname.def"; then + compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` else - deplibs="$dir/$old_library $deplibs" - link_static=yes + compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` fi - fi # link shared/static library? + ;; + *) + compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` + ;; + esac + ;; + *) + func_fatal_error "unknown suffix for \`$my_dlsyms'" + ;; + esac + else + # We keep going just in case the user didn't refer to + # lt_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + + # Nullify the symbol file. + compile_command=`$ECHO "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"` + finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"` + fi +} - if test "$linkmode" = lib; then - if test -n "$dependency_libs" && - { test "$hardcode_into_libs" != yes || - test "$build_old_libs" = yes || - test "$link_static" = yes; }; then - # Extract -R from dependency_libs - temp_deplibs= - for libdir in $dependency_libs; do - case $libdir in - -R*) temp_xrpath=`$echo "X$libdir" | $Xsed -e 's/^-R//'` - case " $xrpath " in - *" $temp_xrpath "*) ;; - *) xrpath="$xrpath $temp_xrpath";; - esac;; - *) temp_deplibs="$temp_deplibs $libdir";; - esac - done - dependency_libs="$temp_deplibs" - fi - - newlib_search_path="$newlib_search_path $absdir" - # Link against this library - test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" - # ... and its dependency_libs - tmp_libs= - for deplib in $dependency_libs; do - newdependency_libs="$deplib $newdependency_libs" - if test "X$duplicate_deps" = "Xyes" ; then - case "$tmp_libs " in - *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; - esac - fi - tmp_libs="$tmp_libs $deplib" - done - - if test "$link_all_deplibs" != no; then - # Add the search paths of all dependency libraries - for deplib in $dependency_libs; do - case $deplib in - -L*) path="$deplib" ;; - *.la) - dir=`$echo "X$deplib" | $Xsed -e 's%/[^/]*$%%'` - test "X$dir" = "X$deplib" && dir="." - # We need an absolute path. - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; - *) - absdir=`cd "$dir" && pwd` - if test -z "$absdir"; then - $echo "$modename: warning: cannot determine absolute directory name of \`$dir'" 1>&2 - absdir="$dir" - fi - ;; - esac - if grep "^installed=no" $deplib > /dev/null; then - path="$absdir/$objdir" - else - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` - if test -z "$libdir"; then - $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 - exit $EXIT_FAILURE - fi - if test "$absdir" != "$libdir"; then - $echo "$modename: warning: \`$deplib' seems to be moved" 1>&2 - fi - path="$absdir" - fi - depdepl= - case $host in - *-*-darwin*) - # we do not want to link against static libs, - # but need to link against shared - eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` - if test -n "$deplibrary_names" ; then - for tmp in $deplibrary_names ; do - depdepl=$tmp - done - if test -f "$path/$depdepl" ; then - depdepl="$path/$depdepl" - fi - # do not add paths which are already there - case " $newlib_search_path " in - *" $path "*) ;; - *) newlib_search_path="$newlib_search_path $path";; - esac - fi - path="" - ;; - *) - path="-L$path" - ;; - esac - ;; - -l*) - case $host in - *-*-darwin*) - # Again, we only want to link against shared libraries - eval tmp_libs=`$echo "X$deplib" | $Xsed -e "s,^\-l,,"` - for tmp in $newlib_search_path ; do - if test -f "$tmp/lib$tmp_libs.dylib" ; then - eval depdepl="$tmp/lib$tmp_libs.dylib" - break - fi - done - path="" - ;; - *) continue ;; - esac - ;; - *) continue ;; - esac - case " $deplibs " in - *" $path "*) ;; - *) deplibs="$path $deplibs" ;; - esac - case " $deplibs " in - *" $depdepl "*) ;; - *) deplibs="$depdepl $deplibs" ;; - esac - done - fi # link_all_deplibs != no - fi # linkmode = lib - done # for deplib in $libs - dependency_libs="$newdependency_libs" - if test "$pass" = dlpreopen; then - # Link the dlpreopened libraries before other libraries - for deplib in $save_deplibs; do - deplibs="$deplib $deplibs" - done - fi - if test "$pass" != dlopen; then - if test "$pass" != conv; then - # Make sure lib_search_path contains only unique directories. - lib_search_path= - for dir in $newlib_search_path; do - case "$lib_search_path " in - *" $dir "*) ;; - *) lib_search_path="$lib_search_path $dir" ;; - esac - done - newlib_search_path= - fi - - if test "$linkmode,$pass" != "prog,link"; then - vars="deplibs" - else - vars="compile_deplibs finalize_deplibs" - fi - for var in $vars dependency_libs; do - # Add libraries to $var in reverse order - eval tmp_libs=\"\$$var\" - new_libs= - for deplib in $tmp_libs; do - # FIXME: Pedantically, this is the right thing to do, so - # that some nasty dependency loop isn't accidentally - # broken: - #new_libs="$deplib $new_libs" - # Pragmatically, this seems to cause very few problems in - # practice: - case $deplib in - -L*) new_libs="$deplib $new_libs" ;; - -R*) ;; - *) - # And here is the reason: when a library appears more - # than once as an explicit dependence of a library, or - # is implicitly linked in more than once by the - # compiler, it is considered special, and multiple - # occurrences thereof are not removed. Compare this - # with having the same library being listed as a - # dependency of multiple other libraries: in this case, - # we know (pedantically, we assume) the library does not - # need to be listed more than once, so we keep only the - # last copy. This is not always right, but it is rare - # enough that we require users that really mean to play - # such unportable linking tricks to link the library - # using -Wl,-lname, so that libtool does not consider it - # for duplicate removal. - case " $specialdeplibs " in - *" $deplib "*) new_libs="$deplib $new_libs" ;; - *) - case " $new_libs " in - *" $deplib "*) ;; - *) new_libs="$deplib $new_libs" ;; - esac - ;; - esac - ;; - esac - done - tmp_libs= - for deplib in $new_libs; do - case $deplib in - -L*) - case " $tmp_libs " in - *" $deplib "*) ;; - *) tmp_libs="$tmp_libs $deplib" ;; - esac - ;; - *) tmp_libs="$tmp_libs $deplib" ;; - esac - done - eval $var=\"$tmp_libs\" - done # for var - fi - # Last step: remove runtime libs from dependency_libs - # (they stay in deplibs) - tmp_libs= - for i in $dependency_libs ; do - case " $predeps $postdeps $compiler_lib_search_path " in - *" $i "*) - i="" - ;; - esac - if test -n "$i" ; then - tmp_libs="$tmp_libs $i" - fi - done - dependency_libs=$tmp_libs - done # for pass - if test "$linkmode" = prog; then - dlfiles="$newdlfiles" - dlprefiles="$newdlprefiles" +# func_win32_libid arg +# return the library type of file 'arg' +# +# Need a lot of goo to handle *both* DLLs and import libs +# Has to be a shell function in order to 'eat' the argument +# that is supplied when $file_magic_command is called. +func_win32_libid () +{ + $opt_debug + win32_libid_type="unknown" + win32_fileres=`file -L $1 2>/dev/null` + case $win32_fileres in + *ar\ archive\ import\ library*) # definitely import + win32_libid_type="x86 archive import" + ;; + *ar\ archive*) # could be an import, or static + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | + $EGREP 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then + win32_nmres=`eval $NM -f posix -A $1 | + $SED -n -e ' + 1,100{ + / I /{ + s,.*,import, + p + q + } + }'` + case $win32_nmres in + import*) win32_libid_type="x86 archive import";; + *) win32_libid_type="x86 archive static";; + esac fi + ;; + *DLL*) + win32_libid_type="x86 DLL" + ;; + *executable*) # but shell scripts are "executable" too... + case $win32_fileres in + *MS\ Windows\ PE\ Intel*) + win32_libid_type="x86 DLL" + ;; + esac + ;; + esac + $ECHO "$win32_libid_type" +} - case $linkmode in - oldlib) - if test -n "$deplibs"; then - $echo "$modename: warning: \`-l' and \`-L' are ignored for archives" 1>&2 - fi - - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - $echo "$modename: warning: \`-dlopen' is ignored for archives" 1>&2 - fi - if test -n "$rpath"; then - $echo "$modename: warning: \`-rpath' is ignored for archives" 1>&2 - fi - if test -n "$xrpath"; then - $echo "$modename: warning: \`-R' is ignored for archives" 1>&2 - fi +# func_extract_an_archive dir oldlib +func_extract_an_archive () +{ + $opt_debug + f_ex_an_ar_dir="$1"; shift + f_ex_an_ar_oldlib="$1" + func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" 'exit $?' + if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then + : + else + func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" + fi +} - if test -n "$vinfo"; then - $echo "$modename: warning: \`-version-info/-version-number' is ignored for archives" 1>&2 - fi - if test -n "$release"; then - $echo "$modename: warning: \`-release' is ignored for archives" 1>&2 - fi +# func_extract_archives gentop oldlib ... +func_extract_archives () +{ + $opt_debug + my_gentop="$1"; shift + my_oldlibs=${1+"$@"} + my_oldobjs="" + my_xlib="" + my_xabs="" + my_xdir="" - if test -n "$export_symbols" || test -n "$export_symbols_regex"; then - $echo "$modename: warning: \`-export-symbols' is ignored for archives" 1>&2 - fi + for my_xlib in $my_oldlibs; do + # Extract the objects. + case $my_xlib in + [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; + *) my_xabs=`pwd`"/$my_xlib" ;; + esac + func_basename "$my_xlib" + my_xlib="$func_basename_result" + my_xlib_u=$my_xlib + while :; do + case " $extracted_archives " in + *" $my_xlib_u "*) + func_arith $extracted_serial + 1 + extracted_serial=$func_arith_result + my_xlib_u=lt$extracted_serial-$my_xlib ;; + *) break ;; + esac + done + extracted_archives="$extracted_archives $my_xlib_u" + my_xdir="$my_gentop/$my_xlib_u" - # Now set the variables for building old libraries. - build_libtool_libs=no - oldlibs="$output" - objs="$objs$old_deplibs" - ;; + func_mkdir_p "$my_xdir" - lib) - # Make sure we only generate libraries of the form `libNAME.la'. - case $outputname in - lib*) - name=`$echo "X$outputname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` - eval shared_ext=\"$shrext_cmds\" - eval libname=\"$libname_spec\" - ;; - *) - if test "$module" = no; then - $echo "$modename: libtool library \`$output' must begin with \`lib'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - if test "$need_lib_prefix" != no; then - # Add the "lib" prefix for modules if required - name=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` - eval shared_ext=\"$shrext_cmds\" - eval libname=\"$libname_spec\" - else - libname=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` - fi + case $host in + *-darwin*) + func_verbose "Extracting $my_xabs" + # Do not bother doing anything if just a dry run + $opt_dry_run || { + darwin_orig_dir=`pwd` + cd $my_xdir || exit $? + darwin_archive=$my_xabs + darwin_curdir=`pwd` + darwin_base_archive=`basename "$darwin_archive"` + darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` + if test -n "$darwin_arches"; then + darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` + darwin_arch= + func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" + for darwin_arch in $darwin_arches ; do + func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" + $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" + cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" + func_extract_an_archive "`pwd`" "${darwin_base_archive}" + cd "$darwin_curdir" + $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" + done # $darwin_arches + ## Okay now we've a bunch of thin objects, gotta fatten them up :) + darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` + darwin_file= + darwin_files= + for darwin_file in $darwin_filelist; do + darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP` + $LIPO -create -output "$darwin_file" $darwin_files + done # $darwin_filelist + $RM -rf unfat-$$ + cd "$darwin_orig_dir" + else + cd $darwin_orig_dir + func_extract_an_archive "$my_xdir" "$my_xabs" + fi # $darwin_arches + } # !$opt_dry_run + ;; + *) + func_extract_an_archive "$my_xdir" "$my_xabs" ;; esac + my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` + done - if test -n "$objs"; then - if test "$deplibs_check_method" != pass_all; then - $echo "$modename: cannot build libtool library \`$output' from non-libtool objects on this host:$objs" 2>&1 - exit $EXIT_FAILURE - else - $echo - $echo "*** Warning: Linking the shared library $output against the non-libtool" - $echo "*** objects $objs is not portable!" - libobjs="$libobjs $objs" - fi - fi + func_extract_archives_result="$my_oldobjs" +} - if test "$dlself" != no; then - $echo "$modename: warning: \`-dlopen self' is ignored for libtool libraries" 1>&2 - fi - set dummy $rpath - if test "$#" -gt 2; then - $echo "$modename: warning: ignoring multiple \`-rpath's for a libtool library" 1>&2 - fi - install_libdir="$2" - oldlibs= - if test -z "$rpath"; then - if test "$build_libtool_libs" = yes; then - # Building a libtool convenience library. - # Some compilers have problems with a `.al' extension so - # convenience libraries should have the same extension an - # archive normally would. - oldlibs="$output_objdir/$libname.$libext $oldlibs" - build_libtool_libs=convenience - build_old_libs=yes +# func_emit_wrapper_part1 [arg=no] +# +# Emit the first part of a libtool wrapper script on stdout. +# For more information, see the description associated with +# func_emit_wrapper(), below. +func_emit_wrapper_part1 () +{ + func_emit_wrapper_part1_arg1=no + if test -n "$1" ; then + func_emit_wrapper_part1_arg1=$1 fi - if test -n "$vinfo"; then - $echo "$modename: warning: \`-version-info/-version-number' is ignored for convenience libraries" 1>&2 - fi + $ECHO "\ +#! $SHELL - if test -n "$release"; then - $echo "$modename: warning: \`-release' is ignored for convenience libraries" 1>&2 - fi - else +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. - # Parse the version information argument. - save_ifs="$IFS"; IFS=':' - set dummy $vinfo 0 0 0 - IFS="$save_ifs" +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='${SED} -e 1s/^X//' +sed_quote_subst='$sed_quote_subst' - if test -n "$8"; then - $echo "$modename: too many parameters to \`-version-info'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi +# Be Bourne compatible +if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh - # convert absolute version numbers to libtool ages - # this retains compatibility with .la files and attempts - # to make the code below a bit more comprehensible +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH - case $vinfo_number in - yes) - number_major="$2" - number_minor="$3" - number_revision="$4" - # - # There are really only two kinds -- those that - # use the current revision as the major version - # and those that subtract age and use age as - # a minor version. But, then there is irix - # which has an extra 1 added just for fun - # - case $version_type in - darwin|linux|osf|windows) - current=`expr $number_major + $number_minor` - age="$number_minor" - revision="$number_revision" - ;; - freebsd-aout|freebsd-elf|sunos) - current="$number_major" - revision="$number_minor" - age="0" - ;; - irix|nonstopux) - current=`expr $number_major + $number_minor - 1` - age="$number_minor" - revision="$number_minor" - ;; - esac - ;; - no) - current="$2" - revision="$3" - age="$4" - ;; - esac +relink_command=\"$relink_command\" - # Check that each of the things are valid numbers. - case $current in - 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; - *) - $echo "$modename: CURRENT \`$current' must be a nonnegative integer" 1>&2 - $echo "$modename: \`$vinfo' is not valid version information" 1>&2 - exit $EXIT_FAILURE - ;; - esac +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variables: + generated_by_libtool_version='$macro_version' + notinst_deplibs='$notinst_deplibs' +else + # When we are sourced in execute mode, \$file and \$ECHO are already set. + if test \"\$libtool_execute_magic\" != \"$magic\"; then + ECHO=\"$qecho\" + file=\"\$0\" + # Make sure echo works. + if test \"X\$1\" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift + elif test \"X\`{ \$ECHO '\t'; } 2>/dev/null\`\" = 'X\t'; then + # Yippee, \$ECHO works! + : + else + # Restart under the correct shell, and then maybe \$ECHO will work. + exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"} + fi + fi\ +" + $ECHO "\ - case $revision in - 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; - *) - $echo "$modename: REVISION \`$revision' must be a nonnegative integer" 1>&2 - $echo "$modename: \`$vinfo' is not valid version information" 1>&2 - exit $EXIT_FAILURE - ;; - esac + # Find the directory that this script lives in. + thisdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. - case $age in - 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; - *) - $echo "$modename: AGE \`$age' must be a nonnegative integer" 1>&2 - $echo "$modename: \`$vinfo' is not valid version information" 1>&2 - exit $EXIT_FAILURE - ;; - esac + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\` - if test "$age" -gt "$current"; then - $echo "$modename: AGE \`$age' is greater than the current interface number \`$current'" 1>&2 - $echo "$modename: \`$vinfo' is not valid version information" 1>&2 - exit $EXIT_FAILURE - fi + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi - # Calculate the version variables. - major= - versuffix= - verstring= - case $version_type in - none) ;; + file=\`\$ECHO \"X\$file\" | \$Xsed -e 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\` + done +" +} +# end: func_emit_wrapper_part1 - darwin) - # Like Linux, but with the current version available in - # verstring for coding it into the library header - major=.`expr $current - $age` - versuffix="$major.$age.$revision" - # Darwin ld doesn't like 0 for these options... - minor_current=`expr $current + 1` - verstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" - ;; +# func_emit_wrapper_part2 [arg=no] +# +# Emit the second part of a libtool wrapper script on stdout. +# For more information, see the description associated with +# func_emit_wrapper(), below. +func_emit_wrapper_part2 () +{ + func_emit_wrapper_part2_arg1=no + if test -n "$1" ; then + func_emit_wrapper_part2_arg1=$1 + fi + + $ECHO "\ + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_part2_arg1 + if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then + # special case for '.' + if test \"\$thisdir\" = \".\"; then + thisdir=\`pwd\` + fi + # remove .libs from thisdir + case \"\$thisdir\" in + *[\\\\/]$objdir ) thisdir=\`\$ECHO \"X\$thisdir\" | \$Xsed -e 's%[\\\\/][^\\\\/]*$%%'\` ;; + $objdir ) thisdir=. ;; + esac + fi - freebsd-aout) - major=".$current" - versuffix=".$current.$revision"; - ;; + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" - freebsd-elf) - major=".$current" - versuffix=".$current"; - ;; + if test "$fast_install" = yes; then + $ECHO "\ + program=lt-'$outputname'$exeext + progdir=\"\$thisdir/$objdir\" - irix | nonstopux) - major=`expr $current - $age + 1` + if test ! -f \"\$progdir/\$program\" || + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ + test \"X\$file\" != \"X\$progdir/\$program\"; }; then - case $version_type in - nonstopux) verstring_prefix=nonstopux ;; - *) verstring_prefix=sgi ;; - esac - verstring="$verstring_prefix$major.$revision" + file=\"\$\$-\$program\" - # Add in all the interfaces that we are compatible with. - loop=$revision - while test "$loop" -ne 0; do - iface=`expr $revision - $loop` - loop=`expr $loop - 1` - verstring="$verstring_prefix$major.$iface:$verstring" - done + if test ! -d \"\$progdir\"; then + $MKDIR \"\$progdir\" + else + $RM \"\$progdir/\$file\" + fi" - # Before this point, $major must not contain `.'. - major=.$major - versuffix="$major.$revision" - ;; + $ECHO "\ - linux) - major=.`expr $current - $age` - versuffix="$major.$age.$revision" - ;; + # relink executable if necessary + if test -n \"\$relink_command\"; then + if relink_command_output=\`eval \$relink_command 2>&1\`; then : + else + $ECHO \"\$relink_command_output\" >&2 + $RM \"\$progdir/\$file\" + exit 1 + fi + fi - osf) - major=.`expr $current - $age` - versuffix=".$current.$age.$revision" - verstring="$current.$age.$revision" + $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $RM \"\$progdir/\$program\"; + $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $RM \"\$progdir/\$file\" + fi" + else + $ECHO "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi - # Add in all the interfaces that we are compatible with. - loop=$age - while test "$loop" -ne 0; do - iface=`expr $current - $loop` - loop=`expr $loop - 1` - verstring="$verstring:${iface}.0" - done + $ECHO "\ - # Make executables depend on our current version. - verstring="$verstring:${current}.0" - ;; + if test -f \"\$progdir/\$program\"; then" - sunos) - major=".$current" - versuffix=".$current.$revision" - ;; + # Export our shlibpath_var if we have one. + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $ECHO "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" - windows) - # Use '-' rather than '.', since we only want one - # extension on DOS 8.3 filesystems. - major=`expr $current - $age` - versuffix="-$major" - ;; + # Some systems cannot cope with colon-terminated $shlibpath_var + # The second colon is a workaround for a bug in BeOS R4 sed + $shlibpath_var=\`\$ECHO \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\` + + export $shlibpath_var +" + fi + + # fixup the dll searchpath if we need to. + if test -n "$dllsearchpath"; then + $ECHO "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + $ECHO "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2* | *-cegcc*) + $ECHO "\ + exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} +" + ;; *) - $echo "$modename: unknown library version type \`$version_type'" 1>&2 - $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 - exit $EXIT_FAILURE + $ECHO "\ + exec \"\$progdir/\$program\" \${1+\"\$@\"} +" ;; esac + $ECHO "\ + \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 + exit 1 + fi + else + # The program doesn't exist. + \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 + \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 + $ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 + exit 1 + fi +fi\ +" +} +# end: func_emit_wrapper_part2 - # Clear the version info if we defaulted, and they specified a release. - if test -z "$vinfo" && test -n "$release"; then - major= - case $version_type in - darwin) - # we can't check for "0.0" in archive_cmds due to quoting - # problems, so we reset it completely - verstring= - ;; - *) - verstring="0.0" - ;; - esac - if test "$need_version" = no; then - versuffix= - else - versuffix=".0.0" - fi - fi - - # Remove version info from name if versioning should be avoided - if test "$avoid_version" = yes && test "$need_version" = no; then - major= - versuffix= - verstring="" - fi - # Check to see if the archive will have undefined symbols. - if test "$allow_undefined" = yes; then - if test "$allow_undefined_flag" = unsupported; then - $echo "$modename: warning: undefined symbols not allowed in $host shared libraries" 1>&2 - build_libtool_libs=no - build_old_libs=yes - fi - else - # Don't allow undefined symbols. - allow_undefined_flag="$no_undefined_flag" +# func_emit_wrapper [arg=no] +# +# Emit a libtool wrapper script on stdout. +# Don't directly open a file because we may want to +# incorporate the script contents within a cygwin/mingw +# wrapper executable. Must ONLY be called from within +# func_mode_link because it depends on a number of variables +# set therein. +# +# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR +# variable will take. If 'yes', then the emitted script +# will assume that the directory in which it is stored is +# the $objdir directory. This is a cygwin/mingw-specific +# behavior. +func_emit_wrapper () +{ + func_emit_wrapper_arg1=no + if test -n "$1" ; then + func_emit_wrapper_arg1=$1 fi - fi - if test "$mode" != relink; then - # Remove our outputs, but don't remove object files since they - # may have been created when compiling PIC objects. - removelist= - tempremovelist=`$echo "$output_objdir/*"` - for p in $tempremovelist; do - case $p in - *.$objext) - ;; - $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) - if test "X$precious_files_regex" != "X"; then - if echo $p | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 - then - continue - fi - fi - removelist="$removelist $p" - ;; - *) ;; - esac - done - if test -n "$removelist"; then - $show "${rm}r $removelist" - $run ${rm}r $removelist - fi - fi + # split this up so that func_emit_cwrapperexe_src + # can call each part independently. + func_emit_wrapper_part1 "${func_emit_wrapper_arg1}" + func_emit_wrapper_part2 "${func_emit_wrapper_arg1}" +} - # Now set the variables for building old libraries. - if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then - oldlibs="$oldlibs $output_objdir/$libname.$libext" - # Transform .lo files to .o files. - oldobjs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP` - fi +# func_to_host_path arg +# +# Convert paths to host format when used with build tools. +# Intended for use with "native" mingw (where libtool itself +# is running under the msys shell), or in the following cross- +# build environments: +# $build $host +# mingw (msys) mingw [e.g. native] +# cygwin mingw +# *nix + wine mingw +# where wine is equipped with the `winepath' executable. +# In the native mingw case, the (msys) shell automatically +# converts paths for any non-msys applications it launches, +# but that facility isn't available from inside the cwrapper. +# Similar accommodations are necessary for $host mingw and +# $build cygwin. Calling this function does no harm for other +# $host/$build combinations not listed above. +# +# ARG is the path (on $build) that should be converted to +# the proper representation for $host. The result is stored +# in $func_to_host_path_result. +func_to_host_path () +{ + func_to_host_path_result="$1" + if test -n "$1" ; then + case $host in + *mingw* ) + lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + case $build in + *mingw* ) # actually, msys + # awkward: cmd appends spaces to result + lt_sed_strip_trailing_spaces="s/[ ]*\$//" + func_to_host_path_tmp1=`( cmd //c echo "$1" |\ + $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo ""` + func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\ + $SED -e "$lt_sed_naive_backslashify"` + ;; + *cygwin* ) + func_to_host_path_tmp1=`cygpath -w "$1"` + func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\ + $SED -e "$lt_sed_naive_backslashify"` + ;; + * ) + # Unfortunately, winepath does not exit with a non-zero + # error code, so we are forced to check the contents of + # stdout. On the other hand, if the command is not + # found, the shell will set an exit code of 127 and print + # *an error message* to stdout. So we must check for both + # error code of zero AND non-empty stdout, which explains + # the odd construction: + func_to_host_path_tmp1=`winepath -w "$1" 2>/dev/null` + if test "$?" -eq 0 && test -n "${func_to_host_path_tmp1}"; then + func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\ + $SED -e "$lt_sed_naive_backslashify"` + else + # Allow warning below. + func_to_host_path_result="" + fi + ;; + esac + if test -z "$func_to_host_path_result" ; then + func_error "Could not determine host path corresponding to" + func_error " '$1'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback: + func_to_host_path_result="$1" + fi + ;; + esac + fi +} +# end: func_to_host_path - # Eliminate all temporary directories. - for path in $notinst_path; do - lib_search_path=`$echo "$lib_search_path " | ${SED} -e "s% $path % %g"` - deplibs=`$echo "$deplibs " | ${SED} -e "s% -L$path % %g"` - dependency_libs=`$echo "$dependency_libs " | ${SED} -e "s% -L$path % %g"` - done +# func_to_host_pathlist arg +# +# Convert pathlists to host format when used with build tools. +# See func_to_host_path(), above. This function supports the +# following $build/$host combinations (but does no harm for +# combinations not listed here): +# $build $host +# mingw (msys) mingw [e.g. native] +# cygwin mingw +# *nix + wine mingw +# +# Path separators are also converted from $build format to +# $host format. If ARG begins or ends with a path separator +# character, it is preserved (but converted to $host format) +# on output. +# +# ARG is a pathlist (on $build) that should be converted to +# the proper representation on $host. The result is stored +# in $func_to_host_pathlist_result. +func_to_host_pathlist () +{ + func_to_host_pathlist_result="$1" + if test -n "$1" ; then + case $host in + *mingw* ) + lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + # Remove leading and trailing path separator characters from + # ARG. msys behavior is inconsistent here, cygpath turns them + # into '.;' and ';.', and winepath ignores them completely. + func_to_host_pathlist_tmp2="$1" + # Once set for this call, this variable should not be + # reassigned. It is used in tha fallback case. + func_to_host_pathlist_tmp1=`echo "$func_to_host_pathlist_tmp2" |\ + $SED -e 's|^:*||' -e 's|:*$||'` + case $build in + *mingw* ) # Actually, msys. + # Awkward: cmd appends spaces to result. + lt_sed_strip_trailing_spaces="s/[ ]*\$//" + func_to_host_pathlist_tmp2=`( cmd //c echo "$func_to_host_pathlist_tmp1" |\ + $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo ""` + func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp2" |\ + $SED -e "$lt_sed_naive_backslashify"` + ;; + *cygwin* ) + func_to_host_pathlist_tmp2=`cygpath -w -p "$func_to_host_pathlist_tmp1"` + func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp2" |\ + $SED -e "$lt_sed_naive_backslashify"` + ;; + * ) + # unfortunately, winepath doesn't convert pathlists + func_to_host_pathlist_result="" + func_to_host_pathlist_oldIFS=$IFS + IFS=: + for func_to_host_pathlist_f in $func_to_host_pathlist_tmp1 ; do + IFS=$func_to_host_pathlist_oldIFS + if test -n "$func_to_host_pathlist_f" ; then + func_to_host_path "$func_to_host_pathlist_f" + if test -n "$func_to_host_path_result" ; then + if test -z "$func_to_host_pathlist_result" ; then + func_to_host_pathlist_result="$func_to_host_path_result" + else + func_to_host_pathlist_result="$func_to_host_pathlist_result;$func_to_host_path_result" + fi + fi + fi + IFS=: + done + IFS=$func_to_host_pathlist_oldIFS + ;; + esac + if test -z "$func_to_host_pathlist_result" ; then + func_error "Could not determine the host path(s) corresponding to" + func_error " '$1'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback. This may break if $1 contains DOS-style drive + # specifications. The fix is not to complicate the expression + # below, but for the user to provide a working wine installation + # with winepath so that path translation in the cross-to-mingw + # case works properly. + lt_replace_pathsep_nix_to_dos="s|:|;|g" + func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp1" |\ + $SED -e "$lt_replace_pathsep_nix_to_dos"` + fi + # Now, add the leading and trailing path separators back + case "$1" in + :* ) func_to_host_pathlist_result=";$func_to_host_pathlist_result" + ;; + esac + case "$1" in + *: ) func_to_host_pathlist_result="$func_to_host_pathlist_result;" + ;; + esac + ;; + esac + fi +} +# end: func_to_host_pathlist - if test -n "$xrpath"; then - # If the user specified any rpath flags, then add them. - temp_xrpath= - for libdir in $xrpath; do - temp_xrpath="$temp_xrpath -R$libdir" - case "$finalize_rpath " in - *" $libdir "*) ;; - *) finalize_rpath="$finalize_rpath $libdir" ;; - esac - done - if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then - dependency_libs="$temp_xrpath $dependency_libs" - fi - fi +# func_emit_cwrapperexe_src +# emit the source code for a wrapper executable on stdout +# Must ONLY be called from within func_mode_link because +# it depends on a number of variable set therein. +func_emit_cwrapperexe_src () +{ + cat < conftest.c < +#include +#ifdef _MSC_VER +# include +# include +# include +# define setmode _setmode +#else +# include +# include +# ifdef __CYGWIN__ +# include +# define HAVE_SETENV +# ifdef __STRICT_ANSI__ +char *realpath (const char *, char *); +int putenv (char *); +int setenv (const char *, const char *, int); +# endif +# endif +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(PATH_MAX) +# define LT_PATHMAX PATH_MAX +#elif defined(MAXPATHLEN) +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef S_IXOTH +# define S_IXOTH 0 +#endif +#ifndef S_IXGRP +# define S_IXGRP 0 +#endif + +#ifdef _MSC_VER +# define S_IXUSR _S_IEXEC +# define stat _stat +# ifndef _INTPTR_T_DEFINED +# define intptr_t int +# endif +#endif + +#ifndef DIR_SEPARATOR +# define DIR_SEPARATOR '/' +# define PATH_SEPARATOR ':' +#endif + +#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ + defined (__OS2__) +# define HAVE_DOS_BASED_FILE_SYSTEM +# define FOPEN_WB "wb" +# ifndef DIR_SEPARATOR_2 +# define DIR_SEPARATOR_2 '\\' +# endif +# ifndef PATH_SEPARATOR_2 +# define PATH_SEPARATOR_2 ';' +# endif +#endif + +#ifndef DIR_SEPARATOR_2 +# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) +#else /* DIR_SEPARATOR_2 */ +# define IS_DIR_SEPARATOR(ch) \ + (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) +#endif /* DIR_SEPARATOR_2 */ + +#ifndef PATH_SEPARATOR_2 +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) +#else /* PATH_SEPARATOR_2 */ +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) +#endif /* PATH_SEPARATOR_2 */ + +#ifdef __CYGWIN__ +# define FOPEN_WB "wb" +#endif + +#ifndef FOPEN_WB +# define FOPEN_WB "w" +#endif +#ifndef _O_BINARY +# define _O_BINARY 0 +#endif + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free ((void *) stale); stale = 0; } \ +} while (0) + +#undef LTWRAPPER_DEBUGPRINTF +#if defined DEBUGWRAPPER +# define LTWRAPPER_DEBUGPRINTF(args) ltwrapper_debugprintf args +static void +ltwrapper_debugprintf (const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + (void) vfprintf (stderr, fmt, args); + va_end (args); +} +#else +# define LTWRAPPER_DEBUGPRINTF(args) +#endif + +const char *program_name = NULL; + +void *xmalloc (size_t num); +char *xstrdup (const char *string); +const char *base_name (const char *name); +char *find_executable (const char *wrapper); +char *chase_symlinks (const char *pathspec); +int make_executable (const char *path); +int check_executable (const char *path); +char *strendzap (char *str, const char *pat); +void lt_fatal (const char *message, ...); +void lt_setenv (const char *name, const char *value); +char *lt_extend_str (const char *orig_value, const char *add, int to_end); +void lt_opt_process_env_set (const char *arg); +void lt_opt_process_env_prepend (const char *arg); +void lt_opt_process_env_append (const char *arg); +int lt_split_name_value (const char *arg, char** name, char** value); +void lt_update_exe_path (const char *name, const char *value); +void lt_update_lib_path (const char *name, const char *value); + +static const char *script_text_part1 = +EOF + + func_emit_wrapper_part1 yes | + $SED -e 's/\([\\"]\)/\\\1/g' \ + -e 's/^/ "/' -e 's/$/\\n"/' + echo ";" + cat </dev/null` - for potent_lib in $potential_libs; do - # Follow soft links. - if ls -lLd "$potent_lib" 2>/dev/null \ - | grep " -> " >/dev/null; then - continue - fi - # The statement above tries to avoid entering an - # endless loop below, in case of cyclic links. - # We might still enter an endless loop, since a link - # loop can be closed while we follow links, - # but so what? - potlib="$potent_lib" - while test -h "$potlib" 2>/dev/null; do - potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` - case $potliblink in - [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; - *) potlib=`$echo "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";; - esac - done - if eval $file_magic_cmd \"\$potlib\" 2>/dev/null \ - | ${SED} 10q \ - | $EGREP "$file_magic_regex" > /dev/null; then - newdeplibs="$newdeplibs $a_deplib" - a_deplib="" - break 2 - fi - done - done - fi - if test -n "$a_deplib" ; then - droppeddeps=yes - $echo - $echo "*** Warning: linker path does not have real file for library $a_deplib." - $echo "*** I have the capability to make that library automatically link in when" - $echo "*** you link to this library. But I can only do this if you have a" - $echo "*** shared version of the library, which you do not appear to have" - $echo "*** because I did check the linker path looking for a file starting" - if test -z "$potlib" ; then - $echo "*** with $libname but no candidates were found. (...for file magic test)" - else - $echo "*** with $libname and none of the candidates passed a file format test" - $echo "*** using a file magic. Last file checked: $potlib" - fi - fi else - # Add a -L argument. - newdeplibs="$newdeplibs $a_deplib" + cat <<"EOF" +const char * LIB_PATH_VALUE = ""; +EOF fi - done # Gone through all deplibs. - ;; - match_pattern*) - set dummy $deplibs_check_method - match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` - for a_deplib in $deplibs; do - name=`expr $a_deplib : '-l\(.*\)'` - # If $name is empty we are operating on a -L argument. - if test -n "$name" && test "$name" != "0"; then - if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then - case " $predeps $postdeps " in - *" $a_deplib "*) - newdeplibs="$newdeplibs $a_deplib" - a_deplib="" - ;; - esac - fi - if test -n "$a_deplib" ; then - libname=`eval \\$echo \"$libname_spec\"` - for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do - potential_libs=`ls $i/$libname[.-]* 2>/dev/null` - for potent_lib in $potential_libs; do - potlib="$potent_lib" # see symlink-check above in file_magic test - if eval $echo \"$potent_lib\" 2>/dev/null \ - | ${SED} 10q \ - | $EGREP "$match_pattern_regex" > /dev/null; then - newdeplibs="$newdeplibs $a_deplib" - a_deplib="" - break 2 - fi - done - done - fi - if test -n "$a_deplib" ; then - droppeddeps=yes - $echo - $echo "*** Warning: linker path does not have real file for library $a_deplib." - $echo "*** I have the capability to make that library automatically link in when" - $echo "*** you link to this library. But I can only do this if you have a" - $echo "*** shared version of the library, which you do not appear to have" - $echo "*** because I did check the linker path looking for a file starting" - if test -z "$potlib" ; then - $echo "*** with $libname but no candidates were found. (...for regex pattern test)" - else - $echo "*** with $libname and none of the candidates passed a file format test" - $echo "*** using a regex pattern. Last file checked: $potlib" - fi - fi + + if test -n "$dllsearchpath"; then + func_to_host_pathlist "$dllsearchpath:" + cat </dev/null; then - $echo - if test "X$deplibs_check_method" = "Xnone"; then - $echo "*** Warning: inter-library dependencies are not supported in this platform." + + if test "$fast_install" = yes; then + cat < \"${export_symbols}T\"" - $run eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' - $show "$mv \"${export_symbols}T\" \"$export_symbols\"" - $run eval '$mv "${export_symbols}T" "$export_symbols"' - fi - fi - fi + newargz = XMALLOC (char *, argc + 1); + tmp_pathspec = find_executable (argv[0]); + if (tmp_pathspec == NULL) + lt_fatal ("Couldn't find %s", argv[0]); + LTWRAPPER_DEBUGPRINTF (("(main) found exe (before symlink chase) at : %s\n", + tmp_pathspec)); + + actual_cwrapper_path = chase_symlinks (tmp_pathspec); + LTWRAPPER_DEBUGPRINTF (("(main) found exe (after symlink chase) at : %s\n", + actual_cwrapper_path)); + XFREE (tmp_pathspec); + + actual_cwrapper_name = xstrdup( base_name (actual_cwrapper_path)); + strendzap (actual_cwrapper_path, actual_cwrapper_name); + + /* wrapper name transforms */ + strendzap (actual_cwrapper_name, ".exe"); + tmp_pathspec = lt_extend_str (actual_cwrapper_name, ".exe", 1); + XFREE (actual_cwrapper_name); + actual_cwrapper_name = tmp_pathspec; + tmp_pathspec = 0; + + /* target_name transforms -- use actual target program name; might have lt- prefix */ + target_name = xstrdup (base_name (TARGET_PROGRAM_NAME)); + strendzap (target_name, ".exe"); + tmp_pathspec = lt_extend_str (target_name, ".exe", 1); + XFREE (target_name); + target_name = tmp_pathspec; + tmp_pathspec = 0; - if test -n "$export_symbols" && test -n "$include_expsyms"; then - $run eval '$echo "X$include_expsyms" | $SP2NL >> "$export_symbols"' - fi + LTWRAPPER_DEBUGPRINTF (("(main) libtool target name: %s\n", + target_name)); +EOF - tmp_deplibs= - for test_deplib in $deplibs; do - case " $convenience " in - *" $test_deplib "*) ;; - *) - tmp_deplibs="$tmp_deplibs $test_deplib" - ;; - esac - done - deplibs="$tmp_deplibs" + cat </dev/null` && - test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then - : - else - # The command line is too long to link in one step, link piecewise. - $echo "creating reloadable object files..." + LTWRAPPER_DEBUGPRINTF (("(main) lt_argv_zero : %s\n", (lt_argv_zero ? lt_argv_zero : ""))); + for (i = 0; i < newargc; i++) + { + LTWRAPPER_DEBUGPRINTF (("(main) newargz[%d] : %s\n", i, (newargz[i] ? newargz[i] : ""))); + } - # Save the value of $output and $libobjs because we want to - # use them later. If we have whole_archive_flag_spec, we - # want to use save_libobjs as it was before - # whole_archive_flag_spec was expanded, because we can't - # assume the linker understands whole_archive_flag_spec. - # This may have to be revisited, in case too many - # convenience libraries get linked in and end up exceeding - # the spec. - if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then - save_libobjs=$libobjs - fi - save_output=$output - output_la=`$echo "X$output" | $Xsed -e "$basename"` +EOF - # Clear the reloadable object creation command queue and - # initialize k to one. - test_cmds= - concat_cmds= - objlist= - delfiles= - last_robj= - k=1 - output=$output_objdir/$output_la-${k}.$objext - # Loop over the list of objects to be linked. - for obj in $save_libobjs - do - eval test_cmds=\"$reload_cmds $objlist $last_robj\" - if test "X$objlist" = X || - { len=`expr "X$test_cmds" : ".*" 2>/dev/null` && - test "$len" -le "$max_cmd_len"; }; then - objlist="$objlist $obj" - else - # The command $test_cmds is almost too long, add a - # command to the queue. - if test "$k" -eq 1 ; then - # The first file doesn't have a previous command to add. - eval concat_cmds=\"$reload_cmds $objlist $last_robj\" - else - # All subsequent reloadable object files will link in - # the last one created. - eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj\" - fi - last_robj=$output_objdir/$output_la-${k}.$objext - k=`expr $k + 1` - output=$output_objdir/$output_la-${k}.$objext - objlist=$obj - len=1 - fi - done - # Handle the remaining objects by creating one last - # reloadable object file. All subsequent reloadable object - # files will link in the last one created. - test -z "$concat_cmds" || concat_cmds=$concat_cmds~ - eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\" + case $host_os in + mingw*) + cat <<"EOF" + /* execv doesn't actually work on mingw as expected on unix */ + rval = _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz); + if (rval == -1) + { + /* failed to start process */ + LTWRAPPER_DEBUGPRINTF (("(main) failed to launch target \"%s\": errno = %d\n", lt_argv_zero, errno)); + return 127; + } + return rval; +EOF + ;; + *) + cat <<"EOF" + execv (lt_argv_zero, newargz); + return rval; /* =127, but avoids unused variable warning */ +EOF + ;; + esac - if ${skipped_export-false}; then - $show "generating symbol list for \`$libname.la'" - export_symbols="$output_objdir/$libname.exp" - $run $rm $export_symbols - libobjs=$output - # Append the command to create the export file. - eval concat_cmds=\"\$concat_cmds~$export_symbols_cmds\" - fi - - # Set up a command to remove the reloadable object files - # after they are used. - i=0 - while test "$i" -lt "$k" - do - i=`expr $i + 1` - delfiles="$delfiles $output_objdir/$output_la-${i}.$objext" - done + cat <<"EOF" +} - $echo "creating a temporary reloadable object file: $output" +void * +xmalloc (size_t num) +{ + void *p = (void *) malloc (num); + if (!p) + lt_fatal ("Memory exhausted"); - # Loop through the commands generated above and execute them. - save_ifs="$IFS"; IFS='~' - for cmd in $concat_cmds; do - IFS="$save_ifs" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" + return p; +} - libobjs=$output - # Restore the value of output. - output=$save_output +char * +xstrdup (const char *string) +{ + return string ? strcpy ((char *) xmalloc (strlen (string) + 1), + string) : NULL; +} - if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then - eval libobjs=\"\$libobjs $whole_archive_flag_spec\" - fi - # Expand the library linking commands again to reset the - # value of $libobjs for piecewise linking. +const char * +base_name (const char *name) +{ + const char *base; - # Do each of the archive commands. - if test "$module" = yes && test -n "$module_cmds" ; then - if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then - cmds=$module_expsym_cmds - else - cmds=$module_cmds - fi - else - if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then - cmds=$archive_expsym_cmds - else - cmds=$archive_cmds - fi - fi +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + /* Skip over the disk name in MSDOS pathnames. */ + if (isalpha ((unsigned char) name[0]) && name[1] == ':') + name += 2; +#endif - # Append the command to remove the reloadable object files - # to the just-reset $cmds. - eval cmds=\"\$cmds~\$rm $delfiles\" - fi - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || { - lt_exit=$? + for (base = name; *name; name++) + if (IS_DIR_SEPARATOR (*name)) + base = name + 1; + return base; +} - # Restore the uninstalled library and exit - if test "$mode" = relink; then - $run eval '(cd $output_objdir && $rm ${realname}T && $mv ${realname}U $realname)' - fi +int +check_executable (const char *path) +{ + struct stat st; - exit $lt_exit - } - done - IFS="$save_ifs" + LTWRAPPER_DEBUGPRINTF (("(check_executable) : %s\n", + path ? (*path ? path : "EMPTY!") : "NULL!")); + if ((!path) || (!*path)) + return 0; - # Restore the uninstalled library and exit - if test "$mode" = relink; then - $run eval '(cd $output_objdir && $rm ${realname}T && $mv $realname ${realname}T && $mv "$realname"U $realname)' || exit $? + if ((stat (path, &st) >= 0) + && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return 1; + else + return 0; +} - if test -n "$convenience"; then - if test -z "$whole_archive_flag_spec"; then - $show "${rm}r $gentop" - $run ${rm}r "$gentop" - fi - fi +int +make_executable (const char *path) +{ + int rval = 0; + struct stat st; - exit $EXIT_SUCCESS - fi + LTWRAPPER_DEBUGPRINTF (("(make_executable) : %s\n", + path ? (*path ? path : "EMPTY!") : "NULL!")); + if ((!path) || (!*path)) + return 0; - # Create links to the real library. - for linkname in $linknames; do - if test "$realname" != "$linkname"; then - $show "(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)" - $run eval '(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)' || exit $? - fi - done + if (stat (path, &st) >= 0) + { + rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); + } + return rval; +} - # If -module or -export-dynamic was specified, set the dlname. - if test "$module" = yes || test "$export_dynamic" = yes; then - # On all known operating systems, these are identical. - dlname="$soname" - fi - fi - ;; +/* Searches for the full path of the wrapper. Returns + newly allocated full path name if found, NULL otherwise + Does not chase symlinks, even on platforms that support them. +*/ +char * +find_executable (const char *wrapper) +{ + int has_slash = 0; + const char *p; + const char *p_next; + /* static buffer for getcwd */ + char tmp[LT_PATHMAX + 1]; + int tmp_len; + char *concat_name; - obj) - if test -n "$deplibs"; then - $echo "$modename: warning: \`-l' and \`-L' are ignored for objects" 1>&2 - fi + LTWRAPPER_DEBUGPRINTF (("(find_executable) : %s\n", + wrapper ? (*wrapper ? wrapper : "EMPTY!") : "NULL!")); - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - $echo "$modename: warning: \`-dlopen' is ignored for objects" 1>&2 - fi + if ((wrapper == NULL) || (*wrapper == '\0')) + return NULL; - if test -n "$rpath"; then - $echo "$modename: warning: \`-rpath' is ignored for objects" 1>&2 - fi + /* Absolute path? */ +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + else + { +#endif + if (IS_DIR_SEPARATOR (wrapper[0])) + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + } +#endif - if test -n "$xrpath"; then - $echo "$modename: warning: \`-R' is ignored for objects" 1>&2 - fi + for (p = wrapper; *p; p++) + if (*p == '/') + { + has_slash = 1; + break; + } + if (!has_slash) + { + /* no slashes; search PATH */ + const char *path = getenv ("PATH"); + if (path != NULL) + { + for (p = path; *p; p = p_next) + { + const char *q; + size_t p_len; + for (q = p; *q; q++) + if (IS_PATH_SEPARATOR (*q)) + break; + p_len = q - p; + p_next = (*q == '\0' ? q : q + 1); + if (p_len == 0) + { + /* empty path: current directory */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal ("getcwd failed"); + tmp_len = strlen (tmp); + concat_name = + XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + } + else + { + concat_name = + XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, p, p_len); + concat_name[p_len] = '/'; + strcpy (concat_name + p_len + 1, wrapper); + } + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + } + /* not found in PATH; assume curdir */ + } + /* Relative path | not found in path: prepend cwd */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal ("getcwd failed"); + tmp_len = strlen (tmp); + concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); - if test -n "$vinfo"; then - $echo "$modename: warning: \`-version-info' is ignored for objects" 1>&2 - fi + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + return NULL; +} - if test -n "$release"; then - $echo "$modename: warning: \`-release' is ignored for objects" 1>&2 - fi +char * +chase_symlinks (const char *pathspec) +{ +#ifndef S_ISLNK + return xstrdup (pathspec); +#else + char buf[LT_PATHMAX]; + struct stat s; + char *tmp_pathspec = xstrdup (pathspec); + char *p; + int has_symlinks = 0; + while (strlen (tmp_pathspec) && !has_symlinks) + { + LTWRAPPER_DEBUGPRINTF (("checking path component for symlinks: %s\n", + tmp_pathspec)); + if (lstat (tmp_pathspec, &s) == 0) + { + if (S_ISLNK (s.st_mode) != 0) + { + has_symlinks = 1; + break; + } - case $output in - *.lo) - if test -n "$objs$old_deplibs"; then - $echo "$modename: cannot build library object \`$output' from non-libtool objects" 1>&2 - exit $EXIT_FAILURE - fi - libobj="$output" - obj=`$echo "X$output" | $Xsed -e "$lo2o"` - ;; - *) - libobj= - obj="$output" - ;; - esac + /* search backwards for last DIR_SEPARATOR */ + p = tmp_pathspec + strlen (tmp_pathspec) - 1; + while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + p--; + if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + { + /* no more DIR_SEPARATORS left */ + break; + } + *p = '\0'; + } + else + { + char *errstr = strerror (errno); + lt_fatal ("Error accessing file %s (%s)", tmp_pathspec, errstr); + } + } + XFREE (tmp_pathspec); - # Delete the old objects. - $run $rm $obj $libobj + if (!has_symlinks) + { + return xstrdup (pathspec); + } - # Objects from convenience libraries. This assumes - # single-version convenience libraries. Whenever we create - # different ones for PIC/non-PIC, this we'll have to duplicate - # the extraction. - reload_conv_objs= - gentop= - # reload_cmds runs $LD directly, so let us get rid of - # -Wl from whole_archive_flag_spec - wl= + tmp_pathspec = realpath (pathspec, buf); + if (tmp_pathspec == 0) + { + lt_fatal ("Could not follow symlinks for %s", pathspec); + } + return xstrdup (tmp_pathspec); +#endif +} - if test -n "$convenience"; then - if test -n "$whole_archive_flag_spec"; then - eval reload_conv_objs=\"\$reload_objs $whole_archive_flag_spec\" - else - gentop="$output_objdir/${obj}x" - generated="$generated $gentop" +char * +strendzap (char *str, const char *pat) +{ + size_t len, patlen; - func_extract_archives $gentop $convenience - reload_conv_objs="$reload_objs $func_extract_archives_result" - fi - fi + assert (str != NULL); + assert (pat != NULL); - # Create the old-style object. - reload_objs="$objs$old_deplibs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test + len = strlen (str); + patlen = strlen (pat); - output="$obj" - cmds=$reload_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" + if (patlen <= len) + { + str += len - patlen; + if (strcmp (str, pat) == 0) + *str = '\0'; + } + return str; +} - # Exit if we aren't doing a library object file. - if test -z "$libobj"; then - if test -n "$gentop"; then - $show "${rm}r $gentop" - $run ${rm}r $gentop - fi +static void +lt_error_core (int exit_status, const char *mode, + const char *message, va_list ap) +{ + fprintf (stderr, "%s: %s: ", program_name, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); - exit $EXIT_SUCCESS - fi + if (exit_status >= 0) + exit (exit_status); +} - if test "$build_libtool_libs" != yes; then - if test -n "$gentop"; then - $show "${rm}r $gentop" - $run ${rm}r $gentop - fi +void +lt_fatal (const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, "FATAL", message, ap); + va_end (ap); +} - # Create an invalid libtool object if no PIC, so that we don't - # accidentally link it into a program. - # $show "echo timestamp > $libobj" - # $run eval "echo timestamp > $libobj" || exit $? - exit $EXIT_SUCCESS - fi +void +lt_setenv (const char *name, const char *value) +{ + LTWRAPPER_DEBUGPRINTF (("(lt_setenv) setting '%s' to '%s'\n", + (name ? name : ""), + (value ? value : ""))); + { +#ifdef HAVE_SETENV + /* always make a copy, for consistency with !HAVE_SETENV */ + char *str = xstrdup (value); + setenv (name, str, 1); +#else + int len = strlen (name) + 1 + strlen (value) + 1; + char *str = XMALLOC (char, len); + sprintf (str, "%s=%s", name, value); + if (putenv (str) != EXIT_SUCCESS) + { + XFREE (str); + } +#endif + } +} - if test -n "$pic_flag" || test "$pic_mode" != default; then - # Only do commands if we really have different PIC objects. - reload_objs="$libobjs $reload_conv_objs" - output="$libobj" - cmds=$reload_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - fi +char * +lt_extend_str (const char *orig_value, const char *add, int to_end) +{ + char *new_value; + if (orig_value && *orig_value) + { + int orig_value_len = strlen (orig_value); + int add_len = strlen (add); + new_value = XMALLOC (char, add_len + orig_value_len + 1); + if (to_end) + { + strcpy (new_value, orig_value); + strcpy (new_value + orig_value_len, add); + } + else + { + strcpy (new_value, add); + strcpy (new_value + add_len, orig_value); + } + } + else + { + new_value = xstrdup (add); + } + return new_value; +} - if test -n "$gentop"; then - $show "${rm}r $gentop" - $run ${rm}r $gentop - fi +int +lt_split_name_value (const char *arg, char** name, char** value) +{ + const char *p; + int len; + if (!arg || !*arg) + return 1; - exit $EXIT_SUCCESS - ;; + p = strchr (arg, (int)'='); - prog) - case $host in - *cygwin*) output=`$echo $output | ${SED} -e 's,.exe$,,;s,$,.exe,'` ;; - esac - if test -n "$vinfo"; then - $echo "$modename: warning: \`-version-info' is ignored for programs" 1>&2 - fi + if (!p) + return 1; - if test -n "$release"; then - $echo "$modename: warning: \`-release' is ignored for programs" 1>&2 - fi + *value = xstrdup (++p); - if test "$preload" = yes; then - if test "$dlopen_support" = unknown && test "$dlopen_self" = unknown && - test "$dlopen_self_static" = unknown; then - $echo "$modename: warning: \`AC_LIBTOOL_DLOPEN' not used. Assuming no dlopen support." - fi - fi + len = strlen (arg) - strlen (*value); + *name = XMALLOC (char, len); + strncpy (*name, arg, len-1); + (*name)[len - 1] = '\0'; - case $host in - *-*-rhapsody* | *-*-darwin1.[012]) - # On Rhapsody replace the C library is the System framework - compile_deplibs=`$echo "X $compile_deplibs" | $Xsed -e 's/ -lc / -framework System /'` - finalize_deplibs=`$echo "X $finalize_deplibs" | $Xsed -e 's/ -lc / -framework System /'` - ;; - esac - - case $host in - *darwin*) - # Don't allow lazy linking, it breaks C++ global constructors - if test "$tagname" = CXX ; then - compile_command="$compile_command ${wl}-bind_at_load" - finalize_command="$finalize_command ${wl}-bind_at_load" - fi - ;; - esac + return 0; +} +void +lt_opt_process_env_set (const char *arg) +{ + char *name = NULL; + char *value = NULL; - # move library search paths that coincide with paths to not yet - # installed libraries to the beginning of the library search list - new_libs= - for path in $notinst_path; do - case " $new_libs " in - *" -L$path/$objdir "*) ;; - *) - case " $compile_deplibs " in - *" -L$path/$objdir "*) - new_libs="$new_libs -L$path/$objdir" ;; - esac - ;; - esac - done - for deplib in $compile_deplibs; do - case $deplib in - -L*) - case " $new_libs " in - *" $deplib "*) ;; - *) new_libs="$new_libs $deplib" ;; - esac - ;; - *) new_libs="$new_libs $deplib" ;; - esac - done - compile_deplibs="$new_libs" + if (lt_split_name_value (arg, &name, &value) != 0) + { + XFREE (name); + XFREE (value); + lt_fatal ("bad argument for %s: '%s'", env_set_opt, arg); + } + lt_setenv (name, value); + XFREE (name); + XFREE (value); +} - compile_command="$compile_command $compile_deplibs" - finalize_command="$finalize_command $finalize_deplibs" +void +lt_opt_process_env_prepend (const char *arg) +{ + char *name = NULL; + char *value = NULL; + char *new_value = NULL; - if test -n "$rpath$xrpath"; then - # If the user specified any rpath flags, then add them. - for libdir in $rpath $xrpath; do - # This is the magic to use -rpath. - case "$finalize_rpath " in - *" $libdir "*) ;; - *) finalize_rpath="$finalize_rpath $libdir" ;; - esac - done - fi + if (lt_split_name_value (arg, &name, &value) != 0) + { + XFREE (name); + XFREE (value); + lt_fatal ("bad argument for %s: '%s'", env_prepend_opt, arg); + } - # Now hardcode the library paths - rpath= - hardcode_libdirs= - for libdir in $compile_rpath $finalize_rpath; do - if test -n "$hardcode_libdir_flag_spec"; then - if test -n "$hardcode_libdir_separator"; then - if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" - else - # Just accumulate the unique libdirs. - case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in - *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) - ;; - *) - hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" - ;; - esac - fi - else - eval flag=\"$hardcode_libdir_flag_spec\" - rpath="$rpath $flag" - fi - elif test -n "$runpath_var"; then - case "$perm_rpath " in - *" $libdir "*) ;; - *) perm_rpath="$perm_rpath $libdir" ;; - esac - fi - case $host in - *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) - testbindir=`$echo "X$libdir" | $Xsed -e 's*/lib$*/bin*'` - case :$dllsearchpath: in - *":$libdir:"*) ;; - *) dllsearchpath="$dllsearchpath:$libdir";; - esac - case :$dllsearchpath: in - *":$testbindir:"*) ;; - *) dllsearchpath="$dllsearchpath:$testbindir";; - esac - ;; - esac - done - # Substitute the hardcoded libdirs into the rpath. - if test -n "$hardcode_libdir_separator" && - test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" - eval rpath=\" $hardcode_libdir_flag_spec\" - fi - compile_rpath="$rpath" + new_value = lt_extend_str (getenv (name), value, 0); + lt_setenv (name, new_value); + XFREE (new_value); + XFREE (name); + XFREE (value); +} - rpath= - hardcode_libdirs= - for libdir in $finalize_rpath; do - if test -n "$hardcode_libdir_flag_spec"; then - if test -n "$hardcode_libdir_separator"; then - if test -z "$hardcode_libdirs"; then - hardcode_libdirs="$libdir" - else - # Just accumulate the unique libdirs. - case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in - *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) - ;; - *) - hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" - ;; - esac - fi - else - eval flag=\"$hardcode_libdir_flag_spec\" - rpath="$rpath $flag" - fi - elif test -n "$runpath_var"; then - case "$finalize_perm_rpath " in - *" $libdir "*) ;; - *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;; - esac - fi - done - # Substitute the hardcoded libdirs into the rpath. - if test -n "$hardcode_libdir_separator" && - test -n "$hardcode_libdirs"; then - libdir="$hardcode_libdirs" - eval rpath=\" $hardcode_libdir_flag_spec\" - fi - finalize_rpath="$rpath" +void +lt_opt_process_env_append (const char *arg) +{ + char *name = NULL; + char *value = NULL; + char *new_value = NULL; - if test -n "$libobjs" && test "$build_old_libs" = yes; then - # Transform all the library objects into standard objects. - compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` - finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` - fi + if (lt_split_name_value (arg, &name, &value) != 0) + { + XFREE (name); + XFREE (value); + lt_fatal ("bad argument for %s: '%s'", env_append_opt, arg); + } - dlsyms= - if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then - if test -n "$NM" && test -n "$global_symbol_pipe"; then - dlsyms="${outputname}S.c" - else - $echo "$modename: not configured to extract global symbols from dlpreopened files" 1>&2 - fi - fi + new_value = lt_extend_str (getenv (name), value, 1); + lt_setenv (name, new_value); + XFREE (new_value); + XFREE (name); + XFREE (value); +} - if test -n "$dlsyms"; then - case $dlsyms in - "") ;; - *.c) - # Discover the nlist of each of the dlfiles. - nlist="$output_objdir/${outputname}.nm" +void +lt_update_exe_path (const char *name, const char *value) +{ + LTWRAPPER_DEBUGPRINTF (("(lt_update_exe_path) modifying '%s' by prepending '%s'\n", + (name ? name : ""), + (value ? value : ""))); - $show "$rm $nlist ${nlist}S ${nlist}T" - $run $rm "$nlist" "${nlist}S" "${nlist}T" + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + /* some systems can't cope with a ':'-terminated path #' */ + int len = strlen (new_value); + while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1])) + { + new_value[len-1] = '\0'; + } + lt_setenv (name, new_value); + XFREE (new_value); + } +} - # Parse the name list into a source file. - $show "creating $output_objdir/$dlsyms" +void +lt_update_lib_path (const char *name, const char *value) +{ + LTWRAPPER_DEBUGPRINTF (("(lt_update_lib_path) modifying '%s' by prepending '%s'\n", + (name ? name : ""), + (value ? value : ""))); - test -z "$run" && $echo > "$output_objdir/$dlsyms" "\ -/* $dlsyms - symbol resolution table for \`$outputname' dlsym emulation. */ -/* Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP */ + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + lt_setenv (name, new_value); + XFREE (new_value); + } +} -#ifdef __cplusplus -extern \"C\" { -#endif -/* Prevent the only kind of declaration conflicts we can make. */ -#define lt_preloaded_symbols some_other_symbol +EOF +} +# end: func_emit_cwrapperexe_src -/* External symbol declarations for the compiler. */\ -" +# func_mode_link arg... +func_mode_link () +{ + $opt_debug + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + # It is impossible to link a dll without this setting, and + # we shouldn't force the makefile maintainer to figure out + # which system we are compiling for in order to pass an extra + # flag for every libtool invocation. + # allow_undefined=no - if test "$dlself" = yes; then - $show "generating symbol list for \`$output'" + # FIXME: Unfortunately, there are problems with the above when trying + # to make a dll which has undefined symbols, in which case not + # even a static library is built. For now, we need to specify + # -no-undefined on the libtool link line when we can be certain + # that all symbols are satisfied, otherwise we get a static library. + allow_undefined=yes + ;; + *) + allow_undefined=yes + ;; + esac + libtool_args=$nonopt + base_compile="$nonopt $@" + compile_command=$nonopt + finalize_command=$nonopt - test -z "$run" && $echo ': @PROGRAM@ ' > "$nlist" + compile_rpath= + finalize_rpath= + compile_shlibpath= + finalize_shlibpath= + convenience= + old_convenience= + deplibs= + old_deplibs= + compiler_flags= + linker_flags= + dllsearchpath= + lib_search_path=`pwd` + inst_prefix_dir= + new_inherited_linker_flags= - # Add our own program objects to the symbol list. - progfiles=`$echo "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` - for arg in $progfiles; do - $show "extracting global C symbols from \`$arg'" - $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" - done + avoid_version=no + dlfiles= + dlprefiles= + dlself=no + export_dynamic=no + export_symbols= + export_symbols_regex= + generated= + libobjs= + ltlibs= + module=no + no_install=no + objs= + non_pic_objects= + precious_files_regex= + prefer_static_libs=no + preload=no + prev= + prevarg= + release= + rpath= + xrpath= + perm_rpath= + temp_rpath= + thread_safe=no + vinfo= + vinfo_number=no + weak_libs= + single_module="${wl}-single_module" + func_infer_tag $base_compile - if test -n "$exclude_expsyms"; then - $run eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' - $run eval '$mv "$nlist"T "$nlist"' - fi - - if test -n "$export_symbols_regex"; then - $run eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' - $run eval '$mv "$nlist"T "$nlist"' - fi - - # Prepare the list of exported symbols - if test -z "$export_symbols"; then - export_symbols="$output_objdir/$outputname.exp" - $run $rm $export_symbols - $run eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' - case $host in - *cygwin* | *mingw* ) - $run eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' - $run eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' - ;; - esac - else - $run eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' - $run eval 'grep -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' - $run eval 'mv "$nlist"T "$nlist"' - case $host in - *cygwin* | *mingw* ) - $run eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' - $run eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' - ;; - esac - fi + # We need to know -static, to get the right output filenames. + for arg + do + case $arg in + -shared) + test "$build_libtool_libs" != yes && \ + func_fatal_configuration "can not build a shared library" + build_old_libs=no + break + ;; + -all-static | -static | -static-libtool-libs) + case $arg in + -all-static) + if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then + func_warning "complete static linking is impossible in this configuration" fi - - for arg in $dlprefiles; do - $show "extracting global C symbols from \`$arg'" - name=`$echo "$arg" | ${SED} -e 's%^.*/%%'` - $run eval '$echo ": $name " >> "$nlist"' - $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" - done - - if test -z "$run"; then - # Make sure we have at least an empty file. - test -f "$nlist" || : > "$nlist" - - if test -n "$exclude_expsyms"; then - $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T - $mv "$nlist"T "$nlist" - fi - - # Try sorting and uniquifying the output. - if grep -v "^: " < "$nlist" | - if sort -k 3 /dev/null 2>&1; then - sort -k 3 - else - sort +2 - fi | - uniq > "$nlist"S; then - : - else - grep -v "^: " < "$nlist" > "$nlist"S - fi - - if test -f "$nlist"S; then - eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$dlsyms"' - else - $echo '/* NONE */' >> "$output_objdir/$dlsyms" - fi - - $echo >> "$output_objdir/$dlsyms" "\ - -#undef lt_preloaded_symbols - -#if defined (__STDC__) && __STDC__ -# define lt_ptr void * -#else -# define lt_ptr char * -# define const -#endif - -/* The mapping between symbol names and symbols. */ -" - - case $host in - *cygwin* | *mingw* ) - $echo >> "$output_objdir/$dlsyms" "\ -/* DATA imports from DLLs on WIN32 can't be const, because - runtime relocations are performed -- see ld's documentation - on pseudo-relocs */ -struct { -" - ;; - * ) - $echo >> "$output_objdir/$dlsyms" "\ -const struct { -" - ;; - esac - - - $echo >> "$output_objdir/$dlsyms" "\ - const char *name; - lt_ptr address; -} -lt_preloaded_symbols[] = -{\ -" - - eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$dlsyms" - - $echo >> "$output_objdir/$dlsyms" "\ - {0, (lt_ptr) 0} -}; - -/* This works around a problem in FreeBSD linker */ -#ifdef FREEBSD_WORKAROUND -static const void *lt_preloaded_setup() { - return lt_preloaded_symbols; -} -#endif - -#ifdef __cplusplus -} -#endif\ -" + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static fi - - pic_flag_for_symtable= - case $host in - # compiling the symbol table file with pic_flag works around - # a FreeBSD bug that causes programs to crash when -lm is - # linked before any other PIC object. But we must not use - # pic_flag when linking with -static. The problem exists in - # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. - *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) - case "$compile_command " in - *" -static "*) ;; - *) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND";; - esac;; - *-*-hpux*) - case "$compile_command " in - *" -static "*) ;; - *) pic_flag_for_symtable=" $pic_flag";; - esac - esac - - # Now compile the dynamic symbol file. - $show "(cd $output_objdir && $LTCC $LTCFLAGS -c$no_builtin_flag$pic_flag_for_symtable \"$dlsyms\")" - $run eval '(cd $output_objdir && $LTCC $LTCFLAGS -c$no_builtin_flag$pic_flag_for_symtable "$dlsyms")' || exit $? - - # Clean up the generated files. - $show "$rm $output_objdir/$dlsyms $nlist ${nlist}S ${nlist}T" - $run $rm "$output_objdir/$dlsyms" "$nlist" "${nlist}S" "${nlist}T" - - # Transform the symbol file into the correct name. - case $host in - *cygwin* | *mingw* ) - if test -f "$output_objdir/${outputname}.def" ; then - compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}.def $output_objdir/${outputname}S.${objext}%"` - finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}.def $output_objdir/${outputname}S.${objext}%"` - else - compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` - finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` - fi - ;; - * ) - compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` - finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` - ;; - esac + prefer_static_libs=yes ;; - *) - $echo "$modename: unknown suffix for \`$dlsyms'" 1>&2 - exit $EXIT_FAILURE + -static) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=built + ;; + -static-libtool-libs) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes ;; esac - else - # We keep going just in case the user didn't refer to - # lt_preloaded_symbols. The linker will fail if global_symbol_pipe - # really was required. - - # Nullify the symbol file. - compile_command=`$echo "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"` - finalize_command=`$echo "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"` - fi - - if test "$need_relink" = no || test "$build_libtool_libs" != yes; then - # Replace the output file specification. - compile_command=`$echo "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` - link_command="$compile_command$compile_rpath" + build_libtool_libs=no + build_old_libs=yes + break + ;; + esac + done - # We have no uninstalled library dependencies, so finalize right now. - $show "$link_command" - $run eval "$link_command" - exit_status=$? + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes - # Delete the generated files. - if test -n "$dlsyms"; then - $show "$rm $output_objdir/${outputname}S.${objext}" - $run $rm "$output_objdir/${outputname}S.${objext}" - fi + # Go through the arguments, transforming them on the way. + while test "$#" -gt 0; do + arg="$1" + shift + func_quote_for_eval "$arg" + qarg=$func_quote_for_eval_unquoted_result + func_append libtool_args " $func_quote_for_eval_result" - exit $exit_status - fi + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + func_append compile_command " @OUTPUT@" + func_append finalize_command " @OUTPUT@" + ;; + esac - if test -n "$shlibpath_var"; then - # We should set the shlibpath_var - rpath= - for dir in $temp_rpath; do - case $dir in - [\\/]* | [A-Za-z]:[\\/]*) - # Absolute path. - rpath="$rpath$dir:" + case $prev in + dlfiles|dlprefiles) + if test "$preload" = no; then + # Add the symbol object into the linking commands. + func_append compile_command " @SYMFILE@" + func_append finalize_command " @SYMFILE@" + preload=yes + fi + case $arg in + *.la | *.lo) ;; # We handle these cases below. + force) + if test "$dlself" = no; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test "$prev" = dlprefiles; then + dlself=yes + elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue ;; *) - # Relative path: add a thisdir entry. - rpath="$rpath\$thisdir/$dir:" + if test "$prev" = dlfiles; then + dlfiles="$dlfiles $arg" + else + dlprefiles="$dlprefiles $arg" + fi + prev= + continue ;; esac - done - temp_rpath="$rpath" - fi - - if test -n "$compile_shlibpath$finalize_shlibpath"; then - compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" - fi - if test -n "$finalize_shlibpath"; then - finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" - fi + ;; + expsyms) + export_symbols="$arg" + test -f "$arg" \ + || func_fatal_error "symbol file \`$arg' does not exist" + prev= + continue + ;; + expsyms_regex) + export_symbols_regex="$arg" + prev= + continue + ;; + framework) + case $host in + *-*-darwin*) + case "$deplibs " in + *" $qarg.ltframework "*) ;; + *) deplibs="$deplibs $qarg.ltframework" # this is fixed later + ;; + esac + ;; + esac + prev= + continue + ;; + inst_prefix) + inst_prefix_dir="$arg" + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat "$save_arg"` + do +# moreargs="$moreargs $fil" + arg=$fil + # A libtool-controlled object. - compile_var= - finalize_var= - if test -n "$runpath_var"; then - if test -n "$perm_rpath"; then - # We should set the runpath_var. - rpath= - for dir in $perm_rpath; do - rpath="$rpath$dir:" - done - compile_var="$runpath_var=\"$rpath\$$runpath_var\" " - fi - if test -n "$finalize_perm_rpath"; then - # We should set the runpath_var. - rpath= - for dir in $finalize_perm_rpath; do - rpath="$rpath$dir:" - done - finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " - fi - fi + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= - if test "$no_install" = yes; then - # We don't need to create a wrapper script. - link_command="$compile_var$compile_command$compile_rpath" - # Replace the output file specification. - link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` - # Delete the old output file. - $run $rm $output - # Link the executable and exit - $show "$link_command" - $run eval "$link_command" || exit $? - exit $EXIT_SUCCESS - fi + # Read the .lo file + func_source "$arg" - if test "$hardcode_action" = relink; then - # Fast installation is not supported - link_command="$compile_var$compile_command$compile_rpath" - relink_command="$finalize_var$finalize_command$finalize_rpath" + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi - $echo "$modename: warning: this platform does not like uninstalled shared libraries" 1>&2 - $echo "$modename: \`$output' will be relinked during installation" 1>&2 - else - if test "$fast_install" != no; then - link_command="$finalize_var$compile_command$finalize_rpath" - if test "$fast_install" = yes; then - relink_command=`$echo "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'` - else - # fast_install is set to needless - relink_command= - fi - else - link_command="$compile_var$compile_command$compile_rpath" - relink_command="$finalize_var$finalize_command$finalize_rpath" - fi - fi + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" - # Replace the output file specification. - link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" - # Delete the old output files. - $run $rm $output $output_objdir/$outputname $output_objdir/lt-$outputname + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi - $show "$link_command" - $run eval "$link_command" || exit $? + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi - # Now create the wrapper script. - $show "creating $output" + # A PIC object. + func_append libobjs " $pic_object" + arg="$pic_object" + fi - # Quote the relink command for shipping. - if test -n "$relink_command"; then - # Preserve any variables that may affect compiler behavior - for var in $variables_saved_for_relink; do - if eval test -z \"\${$var+set}\"; then - relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" - elif eval var_value=\$$var; test -z "$var_value"; then - relink_command="$var=; export $var; $relink_command" - else - var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` - relink_command="$var=\"$var_value\"; export $var; $relink_command" - fi - done - relink_command="(cd `pwd`; $relink_command)" - relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"` - fi + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" - # Quote $echo for shipping. - if test "X$echo" = "X$SHELL $progpath --fallback-echo"; then - case $progpath in - [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";; - *) qecho="$SHELL `pwd`/$progpath --fallback-echo";; - esac - qecho=`$echo "X$qecho" | $Xsed -e "$sed_quote_subst"` - else - qecho=`$echo "X$echo" | $Xsed -e "$sed_quote_subst"` - fi + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" - # Only actually do things if our run command is non-null. - if test -z "$run"; then - # win32 will think the script is a binary if it has - # a .exe suffix, so we strip it off here. - case $output in - *.exe) output=`$echo $output|${SED} 's,.exe$,,'` ;; - esac - # test for cygwin because mv fails w/o .exe extensions - case $host in - *cygwin*) - exeext=.exe - outputname=`$echo $outputname|${SED} 's,.exe$,,'` ;; - *) exeext= ;; + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + done + else + func_fatal_error "link input file \`$arg' does not exist" + fi + arg=$save_arg + prev= + continue + ;; + precious_regex) + precious_files_regex="$arg" + prev= + continue + ;; + release) + release="-$arg" + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + if test "$prev" = rpath; then + case "$rpath " in + *" $arg "*) ;; + *) rpath="$rpath $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) xrpath="$xrpath $arg" ;; + esac + fi + prev= + continue + ;; + shrext) + shrext_cmds="$arg" + prev= + continue + ;; + weak) + weak_libs="$weak_libs $arg" + prev= + continue + ;; + xcclinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xcompiler) + compiler_flags="$compiler_flags $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xlinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $wl$qarg" + prev= + func_append compile_command " $wl$qarg" + func_append finalize_command " $wl$qarg" + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; esac - case $host in - *cygwin* | *mingw* ) - output_name=`basename $output` - output_path=`dirname $output` - cwrappersource="$output_path/$objdir/lt-$output_name.c" - cwrapper="$output_path/$output_name.exe" - $rm $cwrappersource $cwrapper - trap "$rm $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 - - cat > $cwrappersource <> $cwrappersource<<"EOF" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(PATH_MAX) -# define LT_PATHMAX PATH_MAX -#elif defined(MAXPATHLEN) -# define LT_PATHMAX MAXPATHLEN -#else -# define LT_PATHMAX 1024 -#endif + fi # test -n "$prev" -#ifndef DIR_SEPARATOR -# define DIR_SEPARATOR '/' -# define PATH_SEPARATOR ':' -#endif + prevarg="$arg" -#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ - defined (__OS2__) -# define HAVE_DOS_BASED_FILE_SYSTEM -# ifndef DIR_SEPARATOR_2 -# define DIR_SEPARATOR_2 '\\' -# endif -# ifndef PATH_SEPARATOR_2 -# define PATH_SEPARATOR_2 ';' -# endif -#endif + case $arg in + -all-static) + if test -n "$link_static_flag"; then + # See comment for -static flag below, for more details. + func_append compile_command " $link_static_flag" + func_append finalize_command " $link_static_flag" + fi + continue + ;; -#ifndef DIR_SEPARATOR_2 -# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) -#else /* DIR_SEPARATOR_2 */ -# define IS_DIR_SEPARATOR(ch) \ - (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) -#endif /* DIR_SEPARATOR_2 */ + -allow-undefined) + # FIXME: remove this flag sometime in the future. + func_fatal_error "\`-allow-undefined' must not be used because it is the default" + ;; -#ifndef PATH_SEPARATOR_2 -# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) -#else /* PATH_SEPARATOR_2 */ -# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) -#endif /* PATH_SEPARATOR_2 */ + -avoid-version) + avoid_version=yes + continue + ;; -#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) -#define XFREE(stale) do { \ - if (stale) { free ((void *) stale); stale = 0; } \ -} while (0) + -dlopen) + prev=dlfiles + continue + ;; -/* -DDEBUG is fairly common in CFLAGS. */ -#undef DEBUG -#if defined DEBUGWRAPPER -# define DEBUG(format, ...) fprintf(stderr, format, __VA_ARGS__) -#else -# define DEBUG(format, ...) -#endif + -dlpreopen) + prev=dlprefiles + continue + ;; -const char *program_name = NULL; + -export-dynamic) + export_dynamic=yes + continue + ;; -void * xmalloc (size_t num); -char * xstrdup (const char *string); -const char * base_name (const char *name); -char * find_executable(const char *wrapper); -int check_executable(const char *path); -char * strendzap(char *str, const char *pat); -void lt_fatal (const char *message, ...); + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + func_fatal_error "more than one -exported-symbols argument is not allowed" + fi + if test "X$arg" = "X-export-symbols"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; -int -main (int argc, char *argv[]) -{ - char **newargz; - int i; + -framework) + prev=framework + continue + ;; - program_name = (char *) xstrdup (base_name (argv[0])); - DEBUG("(main) argv[0] : %s\n",argv[0]); - DEBUG("(main) program_name : %s\n",program_name); - newargz = XMALLOC(char *, argc+2); -EOF + -inst-prefix-dir) + prev=inst_prefix + continue + ;; - cat >> $cwrappersource <> $cwrappersource <<"EOF" - newargz[1] = find_executable(argv[0]); - if (newargz[1] == NULL) - lt_fatal("Couldn't find %s", argv[0]); - DEBUG("(main) found exe at : %s\n",newargz[1]); - /* we know the script has the same name, without the .exe */ - /* so make sure newargz[1] doesn't end in .exe */ - strendzap(newargz[1],".exe"); - for (i = 1; i < argc; i++) - newargz[i+1] = xstrdup(argv[i]); - newargz[argc+1] = NULL; + -L*) + func_stripname '-L' '' "$arg" + dir=$func_stripname_result + if test -z "$dir"; then + if test "$#" -gt 0; then + func_fatal_error "require no space between \`-L' and \`$1'" + else + func_fatal_error "need path for \`-L' option" + fi + fi + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + test -z "$absdir" && \ + func_fatal_error "cannot determine absolute directory name of \`$dir'" + dir="$absdir" + ;; + esac + case "$deplibs " in + *" -L$dir "*) ;; + *) + deplibs="$deplibs -L$dir" + lib_search_path="$lib_search_path $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`$ECHO "X$dir" | $Xsed -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$dir:"*) ;; + ::) dllsearchpath=$dir;; + *) dllsearchpath="$dllsearchpath:$dir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) dllsearchpath="$dllsearchpath:$testbindir";; + esac + ;; + esac + continue + ;; - for (i=0; i> $cwrappersource <> $cwrappersource <> $cwrappersource <<"EOF" - return 127; -} + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads) + compiler_flags="$compiler_flags $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + case "$new_inherited_linker_flags " in + *" $arg "*) ;; + * ) new_inherited_linker_flags="$new_inherited_linker_flags $arg" ;; + esac + continue + ;; -void * -xmalloc (size_t num) -{ - void * p = (void *) malloc (num); - if (!p) - lt_fatal ("Memory exhausted"); + -multi_module) + single_module="${wl}-multi_module" + continue + ;; - return p; -} + -no-fast-install) + fast_install=no + continue + ;; -char * -xstrdup (const char *string) -{ - return string ? strcpy ((char *) xmalloc (strlen (string) + 1), string) : NULL -; -} + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) + # The PATH hackery in wrapper scripts is required on Windows + # and Darwin in order for the loader to find any dlls it needs. + func_warning "\`-no-install' is ignored for $host" + func_warning "assuming \`-no-fast-install' instead" + fast_install=no + ;; + *) no_install=yes ;; + esac + continue + ;; -const char * -base_name (const char *name) -{ - const char *base; + -no-undefined) + allow_undefined=no + continue + ;; -#if defined (HAVE_DOS_BASED_FILE_SYSTEM) - /* Skip over the disk name in MSDOS pathnames. */ - if (isalpha ((unsigned char)name[0]) && name[1] == ':') - name += 2; -#endif + -objectlist) + prev=objectlist + continue + ;; - for (base = name; *name; name++) - if (IS_DIR_SEPARATOR (*name)) - base = name + 1; - return base; -} + -o) prev=output ;; -int -check_executable(const char * path) -{ - struct stat st; + -precious-files-regex) + prev=precious_regex + continue + ;; - DEBUG("(check_executable) : %s\n", path ? (*path ? path : "EMPTY!") : "NULL!"); - if ((!path) || (!*path)) - return 0; + -release) + prev=release + continue + ;; - if ((stat (path, &st) >= 0) && - ( - /* MinGW & native WIN32 do not support S_IXOTH or S_IXGRP */ -#if defined (S_IXOTH) - ((st.st_mode & S_IXOTH) == S_IXOTH) || -#endif -#if defined (S_IXGRP) - ((st.st_mode & S_IXGRP) == S_IXGRP) || -#endif - ((st.st_mode & S_IXUSR) == S_IXUSR)) - ) - return 1; - else - return 0; -} + -rpath) + prev=rpath + continue + ;; -/* Searches for the full path of the wrapper. Returns - newly allocated full path name if found, NULL otherwise */ -char * -find_executable (const char* wrapper) -{ - int has_slash = 0; - const char* p; - const char* p_next; - /* static buffer for getcwd */ - char tmp[LT_PATHMAX + 1]; - int tmp_len; - char* concat_name; + -R) + prev=xrpath + continue + ;; - DEBUG("(find_executable) : %s\n", wrapper ? (*wrapper ? wrapper : "EMPTY!") : "NULL!"); + -R*) + func_stripname '-R' '' "$arg" + dir=$func_stripname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + continue + ;; - if ((wrapper == NULL) || (*wrapper == '\0')) - return NULL; + -shared) + # The effects of -shared are defined in a previous loop. + continue + ;; - /* Absolute path? */ -#if defined (HAVE_DOS_BASED_FILE_SYSTEM) - if (isalpha ((unsigned char)wrapper[0]) && wrapper[1] == ':') - { - concat_name = xstrdup (wrapper); - if (check_executable(concat_name)) - return concat_name; - XFREE(concat_name); - } - else - { -#endif - if (IS_DIR_SEPARATOR (wrapper[0])) - { - concat_name = xstrdup (wrapper); - if (check_executable(concat_name)) - return concat_name; - XFREE(concat_name); - } -#if defined (HAVE_DOS_BASED_FILE_SYSTEM) - } -#endif + -shrext) + prev=shrext + continue + ;; - for (p = wrapper; *p; p++) - if (*p == '/') - { - has_slash = 1; - break; - } - if (!has_slash) - { - /* no slashes; search PATH */ - const char* path = getenv ("PATH"); - if (path != NULL) - { - for (p = path; *p; p = p_next) - { - const char* q; - size_t p_len; - for (q = p; *q; q++) - if (IS_PATH_SEPARATOR(*q)) - break; - p_len = q - p; - p_next = (*q == '\0' ? q : q + 1); - if (p_len == 0) - { - /* empty path: current directory */ - if (getcwd (tmp, LT_PATHMAX) == NULL) - lt_fatal ("getcwd failed"); - tmp_len = strlen(tmp); - concat_name = XMALLOC(char, tmp_len + 1 + strlen(wrapper) + 1); - memcpy (concat_name, tmp, tmp_len); - concat_name[tmp_len] = '/'; - strcpy (concat_name + tmp_len + 1, wrapper); - } - else - { - concat_name = XMALLOC(char, p_len + 1 + strlen(wrapper) + 1); - memcpy (concat_name, p, p_len); - concat_name[p_len] = '/'; - strcpy (concat_name + p_len + 1, wrapper); - } - if (check_executable(concat_name)) - return concat_name; - XFREE(concat_name); - } - } - /* not found in PATH; assume curdir */ - } - /* Relative path | not found in path: prepend cwd */ - if (getcwd (tmp, LT_PATHMAX) == NULL) - lt_fatal ("getcwd failed"); - tmp_len = strlen(tmp); - concat_name = XMALLOC(char, tmp_len + 1 + strlen(wrapper) + 1); - memcpy (concat_name, tmp, tmp_len); - concat_name[tmp_len] = '/'; - strcpy (concat_name + tmp_len + 1, wrapper); + -static | -static-libtool-libs) + # The effects of -static are defined in a previous loop. + # We used to do the same as -all-static on platforms that + # didn't have a PIC flag, but the assumption that the effects + # would be equivalent was wrong. It would break on at least + # Digital Unix and AIX. + continue + ;; - if (check_executable(concat_name)) - return concat_name; - XFREE(concat_name); - return NULL; -} + -thread-safe) + thread_safe=yes + continue + ;; -char * -strendzap(char *str, const char *pat) -{ - size_t len, patlen; + -version-info) + prev=vinfo + continue + ;; - assert(str != NULL); - assert(pat != NULL); + -version-number) + prev=vinfo + vinfo_number=yes + continue + ;; - len = strlen(str); - patlen = strlen(pat); + -weak) + prev=weak + continue + ;; - if (patlen <= len) - { - str += len - patlen; - if (strcmp(str, pat) == 0) - *str = '\0'; - } - return str; -} + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + arg="$arg $wl$func_quote_for_eval_result" + compiler_flags="$compiler_flags $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; -static void -lt_error_core (int exit_status, const char * mode, - const char * message, va_list ap) -{ - fprintf (stderr, "%s: %s: ", program_name, mode); - vfprintf (stderr, message, ap); - fprintf (stderr, ".\n"); + -Wl,*) + func_stripname '-Wl,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + arg="$arg $wl$func_quote_for_eval_result" + compiler_flags="$compiler_flags $wl$func_quote_for_eval_result" + linker_flags="$linker_flags $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; - if (exit_status >= 0) - exit (exit_status); -} + -Xcompiler) + prev=xcompiler + continue + ;; -void -lt_fatal (const char *message, ...) -{ - va_list ap; - va_start (ap, message); - lt_error_core (EXIT_FAILURE, "FATAL", message, ap); - va_end (ap); -} -EOF - # we should really use a build-platform specific compiler - # here, but OTOH, the wrappers (shell script and this C one) - # are only useful if you want to execute the "real" binary. - # Since the "real" binary is built for $host, then this - # wrapper might as well be built for $host, too. - $run $LTCC $LTCFLAGS -s -o $cwrapper $cwrappersource - ;; - esac - $rm $output - trap "$rm $output; exit $EXIT_FAILURE" 1 2 15 + -Xlinker) + prev=xlinker + continue + ;; - $echo > $output "\ -#! $SHELL + -XCClinker) + prev=xcclinker + continue + ;; -# $output - temporary wrapper script for $objdir/$outputname -# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP -# -# The $output program cannot be directly executed until all the libtool -# libraries that it depends on are installed. -# -# This wrapper script should never be moved out of the build directory. -# If it is, it will not operate correctly. + # -msg_* for osf cc + -msg_*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; -# Sed substitution that helps us do robust quoting. It backslashifies -# metacharacters that are still active within double-quoted strings. -Xsed='${SED} -e 1s/^X//' -sed_quote_subst='$sed_quote_subst' + # -64, -mips[0-9] enable 64-bit mode on the SGI compiler + # -r[0-9][0-9]* specifies the processor on the SGI compiler + # -xarch=*, -xtarget=* enable 64-bit mode on the Sun compiler + # +DA*, +DD* enable 64-bit mode on the HP compiler + # -q* pass through compiler args for the IBM compiler + # -m*, -t[45]*, -txscale* pass through architecture-specific + # compiler args for GCC + # -F/path gives path to uninstalled frameworks, gcc on darwin + # -p, -pg, --coverage, -fprofile-* pass through profiling flag for GCC + # @file GCC response files + -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ + -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + func_append compile_command " $arg" + func_append finalize_command " $arg" + compiler_flags="$compiler_flags $arg" + continue + ;; -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + # Some other compiler flag. + -* | +*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; -relink_command=\"$relink_command\" + *.$objext) + # A standard object. + objs="$objs $arg" + ;; -# This environment variable determines our operation mode. -if test \"\$libtool_install_magic\" = \"$magic\"; then - # install mode needs the following variable: - notinst_deplibs='$notinst_deplibs' -else - # When we are sourced in execute mode, \$file and \$echo are already set. - if test \"\$libtool_execute_magic\" != \"$magic\"; then - echo=\"$qecho\" - file=\"\$0\" - # Make sure echo works. - if test \"X\$1\" = X--no-reexec; then - # Discard the --no-reexec flag, and continue. - shift - elif test \"X\`(\$echo '\t') 2>/dev/null\`\" = 'X\t'; then - # Yippee, \$echo works! - : + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + ;; + + *.$libext) + # An archive. + deplibs="$deplibs $arg" + old_deplibs="$old_deplibs $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + if test "$prev" = dlfiles; then + # This library was specified with -dlopen. + dlfiles="$dlfiles $arg" + prev= + elif test "$prev" = dlprefiles; then + # The library was specified with -dlpreopen. + dlprefiles="$dlprefiles $arg" + prev= + else + deplibs="$deplibs $arg" + fi + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + done # argument parsing loop + + test -n "$prev" && \ + func_fatal_help "the \`$prevarg' option requires an argument" + + if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + func_basename "$output" + outputname="$func_basename_result" + libobjs_save="$libobjs" + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$ECHO \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\` else - # Restart under the correct shell, and then maybe \$echo will work. - exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"} + shlib_search_path= fi - fi\ -" - $echo >> $output "\ + eval sys_lib_search_path=\"$sys_lib_search_path_spec\" + eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" - # Find the directory that this script lives in. - thisdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\` - test \"x\$thisdir\" = \"x\$file\" && thisdir=. + func_dirname "$output" "/" "" + output_objdir="$func_dirname_result$objdir" + # Create the object directory. + func_mkdir_p "$output_objdir" - # Follow symbolic links until we get to the real thisdir. - file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\` - while test -n \"\$file\"; do - destdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\` + # Determine the type of output + case $output in + "") + func_fatal_help "you must specify an output file" + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + esac - # If there was a directory component, then change thisdir. - if test \"x\$destdir\" != \"x\$file\"; then - case \"\$destdir\" in - [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; - *) thisdir=\"\$thisdir/\$destdir\" ;; - esac + specialdeplibs= + + libs= + # Find all interdependent deplibs by searching for libraries + # that are linked more than once (e.g. -la -lb -la) + for deplib in $deplibs; do + if $opt_duplicate_deps ; then + case "$libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + libs="$libs $deplib" + done + + if test "$linkmode" = lib; then + libs="$predeps $libs $compiler_lib_search_path $postdeps" + + # Compute libraries that are listed more than once in $predeps + # $postdeps and mark them as special (i.e., whose duplicates are + # not to be eliminated). + pre_post_deps= + if $opt_duplicate_compiler_generated_deps; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;; + esac + pre_post_deps="$pre_post_deps $pre_post_dep" + done + fi + pre_post_deps= fi - file=\`\$echo \"X\$file\" | \$Xsed -e 's%^.*/%%'\` - file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\` - done + deplibs= + newdependency_libs= + newlib_search_path= + need_relink=no # whether we're linking any uninstalled libtool libraries + notinst_deplibs= # not-installed libtool libraries + notinst_path= # paths that contain not-installed libtool libraries + + case $linkmode in + lib) + passes="conv dlpreopen link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" + ;; + esac + done + ;; + prog) + compile_deplibs= + finalize_deplibs= + alldeplibs=no + newdlfiles= + newdlprefiles= + passes="conv scan dlopen dlpreopen link" + ;; + *) passes="conv" + ;; + esac + + for pass in $passes; do + # The preopen pass in lib mode reverses $deplibs; put it back here + # so that -L comes before libs that need it for instance... + if test "$linkmode,$pass" = "lib,link"; then + ## FIXME: Find the place where the list is rebuilt in the wrong + ## order, and fix it there properly + tmp_deplibs= + for deplib in $deplibs; do + tmp_deplibs="$deplib $tmp_deplibs" + done + deplibs="$tmp_deplibs" + fi + + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan"; then + libs="$deplibs" + deplibs= + fi + if test "$linkmode" = prog; then + case $pass in + dlopen) libs="$dlfiles" ;; + dlpreopen) libs="$dlprefiles" ;; + link) libs="$deplibs %DEPLIBS% $dependency_libs" ;; + esac + fi + if test "$linkmode,$pass" = "lib,dlpreopen"; then + # Collect and forward deplibs of preopened libtool libs + for lib in $dlprefiles; do + # Ignore non-libtool-libs + dependency_libs= + case $lib in + *.la) func_source "$lib" ;; + esac + + # Collect preopened libtool deplibs, except any this library + # has declared as weak libs + for deplib in $dependency_libs; do + deplib_base=`$ECHO "X$deplib" | $Xsed -e "$basename"` + case " $weak_libs " in + *" $deplib_base "*) ;; + *) deplibs="$deplibs $deplib" ;; + esac + done + done + libs="$dlprefiles" + fi + if test "$pass" = dlopen; then + # Collect dlpreopened libraries + save_deplibs="$deplibs" + deplibs= + fi + + for deplib in $libs; do + lib= + found=no + case $deplib in + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + compiler_flags="$compiler_flags $deplib" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;; + esac + fi + fi + continue + ;; + -l*) + if test "$linkmode" != lib && test "$linkmode" != prog; then + func_warning "\`-l' is ignored for archives/objects" + continue + fi + func_stripname '-l' '' "$deplib" + name=$func_stripname_result + if test "$linkmode" = lib; then + searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" + else + searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" + fi + for searchdir in $searchdirs; do + for search_ext in .la $std_shrext .so .a; do + # Search the libtool library + lib="$searchdir/lib${name}${search_ext}" + if test -f "$lib"; then + if test "$search_ext" = ".la"; then + found=yes + else + found=no + fi + break 2 + fi + done + done + if test "$found" != yes; then + # deplib doesn't seem to be a libtool library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + else # deplib is a libtool library + # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, + # We need to do some special things here, and not later. + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $deplib "*) + if func_lalib_p "$lib"; then + library_names= + old_library= + func_source "$lib" + for l in $old_library $library_names; do + ll="$l" + done + if test "X$ll" = "X$old_library" ; then # only static version available + found=no + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + lib=$ladir/$old_library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + fi + ;; + *) ;; + esac + fi + fi + ;; # -l + *.ltframework) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;; + esac + fi + fi + continue + ;; + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test "$pass" = conv && continue + newdependency_libs="$deplib $newdependency_libs" + func_stripname '-L' '' "$deplib" + newlib_search_path="$newlib_search_path $func_stripname_result" + ;; + prog) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + if test "$pass" = scan; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + func_stripname '-L' '' "$deplib" + newlib_search_path="$newlib_search_path $func_stripname_result" + ;; + *) + func_warning "\`-L' is ignored for archives/objects" + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test "$pass" = link; then + func_stripname '-R' '' "$deplib" + dir=$func_stripname_result + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) lib="$deplib" ;; + *.$libext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + # Linking convenience modules into shared libraries is allowed, + # but linking other static libraries is non-portable. + case " $dlpreconveniencelibs " in + *" $deplib "*) ;; + *) + valid_a_lib=no + case $deplibs_check_method in + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + if eval "\$ECHO \"X$deplib\"" 2>/dev/null | $Xsed -e 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + valid_a_lib=yes + fi + ;; + pass_all) + valid_a_lib=yes + ;; + esac + if test "$valid_a_lib" != yes; then + $ECHO + $ECHO "*** Warning: Trying to link with static lib archive $deplib." + $ECHO "*** I have the capability to make that library automatically link in when" + $ECHO "*** you link to this library. But I can only do this if you have a" + $ECHO "*** shared version of the library, which you do not appear to have" + $ECHO "*** because the file extensions .$libext of this argument makes me believe" + $ECHO "*** that it is just a static archive that I should not use here." + else + $ECHO + $ECHO "*** Warning: Linking the shared library $output against the" + $ECHO "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + fi + ;; + esac + continue + ;; + prog) + if test "$pass" != link; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + continue + ;; + esac # linkmode + ;; # *.$libext + *.lo | *.$objext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + elif test "$linkmode" = prog; then + if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then + # If there is no dlopen support or we're linking statically, + # we need to preload. + newdlprefiles="$newdlprefiles $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + newdlfiles="$newdlfiles $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=yes + continue + ;; + esac # case $deplib + + if test "$found" = yes || test -f "$lib"; then : + else + func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" + fi + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$lib" \ + || func_fatal_error "\`$lib' is not a valid libtool archive" + + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + inherited_linker_flags= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + avoidtemprpath= + + + # Read the .la file + func_source "$lib" + + # Convert "-framework foo" to "foo.ltframework" + if test -n "$inherited_linker_flags"; then + tmp_inherited_linker_flags=`$ECHO "X$inherited_linker_flags" | $Xsed -e 's/-framework \([^ $]*\)/\1.ltframework/g'` + for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do + case " $new_inherited_linker_flags " in + *" $tmp_inherited_linker_flag "*) ;; + *) new_inherited_linker_flags="$new_inherited_linker_flags $tmp_inherited_linker_flag";; + esac + done + fi + dependency_libs=`$ECHO "X $dependency_libs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan" || + { test "$linkmode" != prog && test "$linkmode" != lib; }; then + test -n "$dlopen" && dlfiles="$dlfiles $dlopen" + test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen" + fi + + if test "$pass" = conv; then + # Only check for convenience libraries + deplibs="$lib $deplibs" + if test -z "$libdir"; then + if test -z "$old_library"; then + func_fatal_error "cannot find name of link library for \`$lib'" + fi + # It is a libtool convenience library, so add in its objects. + convenience="$convenience $ladir/$objdir/$old_library" + old_convenience="$old_convenience $ladir/$objdir/$old_library" + elif test "$linkmode" != prog && test "$linkmode" != lib; then + func_fatal_error "\`$lib' is not a convenience library" + fi + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if $opt_duplicate_deps ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + for l in $old_library $library_names; do + linklib="$l" + done + if test -z "$linklib"; then + func_fatal_error "cannot find name of link library for \`$lib'" + fi + + # This library was specified with -dlopen. + if test "$pass" = dlopen; then + if test -z "$libdir"; then + func_fatal_error "cannot -dlopen a convenience library: \`$lib'" + fi + if test -z "$dlname" || + test "$dlopen_support" != yes || + test "$build_libtool_libs" = no; then + # If there is no dlname, no dlopen support or we're linking + # statically, we need to preload. We also need to preload any + # dependent libraries so libltdl's deplib preloader doesn't + # bomb out in the load deplibs phase. + dlprefiles="$dlprefiles $lib $dependency_libs" + else + newdlfiles="$newdlfiles $lib" + fi + continue + fi # $pass = dlopen + + # We need an absolute path. + case $ladir in + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; + *) + abs_ladir=`cd "$ladir" && pwd` + if test -z "$abs_ladir"; then + func_warning "cannot determine absolute directory name of \`$ladir'" + func_warning "passing it literally to the linker, although it might fail" + abs_ladir="$ladir" + fi + ;; + esac + func_basename "$lib" + laname="$func_basename_result" + + # Find the relevant object directory and library name. + if test "X$installed" = Xyes; then + if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + func_warning "library \`$lib' was moved." + dir="$ladir" + absdir="$abs_ladir" + libdir="$abs_ladir" + else + dir="$libdir" + absdir="$libdir" + fi + test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes + else + if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then + dir="$ladir" + absdir="$abs_ladir" + # Remove this search path later + notinst_path="$notinst_path $abs_ladir" + else + dir="$ladir/$objdir" + absdir="$abs_ladir/$objdir" + # Remove this search path later + notinst_path="$notinst_path $abs_ladir" + fi + fi # $installed = yes + func_stripname 'lib' '.la' "$laname" + name=$func_stripname_result + + # This library was specified with -dlpreopen. + if test "$pass" = dlpreopen; then + if test -z "$libdir" && test "$linkmode" = prog; then + func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" + fi + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + newdlprefiles="$newdlprefiles $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + dlpreconveniencelibs="$dlpreconveniencelibs $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + newdlprefiles="$newdlprefiles $dir/$dlname" + else + newdlprefiles="$newdlprefiles $dir/$linklib" + fi + fi # $pass = dlpreopen + + if test -z "$libdir"; then + # Link the convenience library + if test "$linkmode" = lib; then + deplibs="$dir/$old_library $deplibs" + elif test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$dir/$old_library $compile_deplibs" + finalize_deplibs="$dir/$old_library $finalize_deplibs" + else + deplibs="$lib $deplibs" # used for prog,scan pass + fi + continue + fi + + + if test "$linkmode" = prog && test "$pass" != link; then + newlib_search_path="$newlib_search_path $ladir" + deplibs="$lib $deplibs" + + linkalldeplibs=no + if test "$link_all_deplibs" != no || test -z "$library_names" || + test "$build_libtool_libs" = no; then + linkalldeplibs=yes + fi + + tmp_libs= + for deplib in $dependency_libs; do + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + newlib_search_path="$newlib_search_path $func_stripname_result" + ;; + esac + # Need to link against all dependency_libs? + if test "$linkalldeplibs" = yes; then + deplibs="$deplib $deplibs" + else + # Need to hardcode shared library paths + # or/and link against static libraries + newdependency_libs="$deplib $newdependency_libs" + fi + if $opt_duplicate_deps ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done # for deplib + continue + fi # $linkmode = prog... + + if test "$linkmode,$pass" = "prog,link"; then + if test -n "$library_names" && + { { test "$prefer_static_libs" = no || + test "$prefer_static_libs,$installed" = "built,yes"; } || + test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath:" in + *"$absdir:"*) ;; + *) temp_rpath="$temp_rpath$absdir:" ;; + esac + fi + + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi # $linkmode,$pass = prog,link... + + if test "$alldeplibs" = yes && + { test "$deplibs_check_method" = pass_all || + { test "$build_libtool_libs" = yes && + test -n "$library_names"; }; }; then + # We only need to search for static libraries + continue + fi + fi + + link_static=no # Whether the deplib will be linked statically + use_static_libs=$prefer_static_libs + if test "$use_static_libs" = built && test "$installed" = yes; then + use_static_libs=no + fi + if test -n "$library_names" && + { test "$use_static_libs" = no || test -z "$old_library"; }; then + case $host in + *cygwin* | *mingw* | *cegcc*) + # No point in relinking DLLs because paths are not encoded + notinst_deplibs="$notinst_deplibs $lib" + need_relink=no + ;; + *) + if test "$installed" = no; then + notinst_deplibs="$notinst_deplibs $lib" + need_relink=yes + fi + ;; + esac + # This is a shared library + + # Warn about portability, can't link against -module's on some + # systems (darwin). Don't bleat about dlopened modules though! + dlopenmodule="" + for dlpremoduletest in $dlprefiles; do + if test "X$dlpremoduletest" = "X$lib"; then + dlopenmodule="$dlpremoduletest" + break + fi + done + if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then + $ECHO + if test "$linkmode" = prog; then + $ECHO "*** Warning: Linking the executable $output against the loadable module" + else + $ECHO "*** Warning: Linking the shared library $output against the loadable module" + fi + $ECHO "*** $linklib is not portable!" + fi + if test "$linkmode" = lib && + test "$hardcode_into_libs" = yes; then + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + shift + realname="$1" + shift + libname=`eval "\\$ECHO \"$libname_spec\""` + # use dlname if we got it. it's perfectly good, no? + if test -n "$dlname"; then + soname="$dlname" + elif test -n "$soname_spec"; then + # bleh windows + case $host in + *cygwin* | mingw* | *cegcc*) + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + esac + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot="$soname" + func_basename "$soroot" + soname="$func_basename_result" + func_stripname 'lib' '.dll' "$soname" + newlib=libimp-$func_stripname_result.a + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + func_verbose "extracting exported symbol list from \`$soname'" + func_execute_cmds "$extract_expsyms_cmds" 'exit $?' + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + func_verbose "generating import library for \`$soname'" + func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' + fi + # make sure the library variables are pointing to the new library + dir=$output_objdir + linklib=$newlib + fi # test -n "$old_archive_from_expsyms_cmds" + + if test "$linkmode" = prog || test "$mode" != relink; then + add_shlibpath= + add_dir= + add= + lib_linked=yes + case $hardcode_action in + immediate | unsupported) + if test "$hardcode_direct" = no; then + add="$dir/$linklib" + case $host in + *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; + *-*-sysv4*uw2*) add_dir="-L$dir" ;; + *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ + *-*-unixware7*) add_dir="-L$dir" ;; + *-*-darwin* ) + # if the lib is a (non-dlopened) module then we can not + # link against it, someone is ignoring the earlier warnings + if /usr/bin/file -L $add 2> /dev/null | + $GREP ": [^:]* bundle" >/dev/null ; then + if test "X$dlopenmodule" != "X$lib"; then + $ECHO "*** Warning: lib $linklib is a module, not a shared library" + if test -z "$old_library" ; then + $ECHO + $ECHO "*** And there doesn't seem to be a static archive available" + $ECHO "*** The link will probably fail, sorry" + else + add="$dir/$old_library" + fi + elif test -n "$old_library"; then + add="$dir/$old_library" + fi + fi + esac + elif test "$hardcode_minus_L" = no; then + case $host in + *-*-sunos*) add_shlibpath="$dir" ;; + esac + add_dir="-L$dir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = no; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + relink) + if test "$hardcode_direct" = yes && + test "$hardcode_direct_absolute" = no; then + add="$dir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$dir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + *) lib_linked=no ;; + esac + + if test "$lib_linked" != yes; then + func_fatal_configuration "unsupported hardcode properties" + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;; + esac + fi + if test "$linkmode" = prog; then + test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" + test -n "$add" && compile_deplibs="$add $compile_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + if test "$hardcode_direct" != yes && + test "$hardcode_minus_L" != yes && + test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + fi + fi + fi + + if test "$linkmode" = prog || test "$mode" = relink; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes && + test "$hardcode_direct_absolute" = no; then + add="$libdir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$libdir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + add="-l$name" + elif test "$hardcode_automatic" = yes; then + if test -n "$inst_prefix_dir" && + test -f "$inst_prefix_dir$libdir/$linklib" ; then + add="$inst_prefix_dir$libdir/$linklib" + else + add="$libdir/$linklib" + fi + else + # We cannot seem to hardcode it, guess we'll fake it. + add_dir="-L$libdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + fi + + if test "$linkmode" = prog; then + test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" + test -n "$add" && finalize_deplibs="$add $finalize_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + fi + fi + elif test "$linkmode" = prog; then + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test "$hardcode_direct" != unsupported; then + test -n "$old_library" && linklib="$old_library" + compile_deplibs="$dir/$linklib $compile_deplibs" + finalize_deplibs="$dir/$linklib $finalize_deplibs" + else + compile_deplibs="-l$name -L$dir $compile_deplibs" + finalize_deplibs="-l$name -L$dir $finalize_deplibs" + fi + elif test "$build_libtool_libs" = yes; then + # Not a shared library + if test "$deplibs_check_method" != pass_all; then + # We're trying link a shared library against a static one + # but the system doesn't support it. + + # Just print a warning and add the library to dependency_libs so + # that the program can be linked against the static library. + $ECHO + $ECHO "*** Warning: This system can not link to static lib archive $lib." + $ECHO "*** I have the capability to make that library automatically link in when" + $ECHO "*** you link to this library. But I can only do this if you have a" + $ECHO "*** shared version of the library, which you do not appear to have." + if test "$module" = yes; then + $ECHO "*** But as you try to build a module library, libtool will still create " + $ECHO "*** a static module, that should work as long as the dlopening application" + $ECHO "*** is linked with the -dlopen flag to resolve symbols at runtime." + if test -z "$global_symbol_pipe"; then + $ECHO + $ECHO "*** However, this would only work if libtool was able to extract symbol" + $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could" + $ECHO "*** not find such a program. So, this module is probably useless." + $ECHO "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + else + deplibs="$dir/$old_library $deplibs" + link_static=yes + fi + fi # link shared/static library? + + if test "$linkmode" = lib; then + if test -n "$dependency_libs" && + { test "$hardcode_into_libs" != yes || + test "$build_old_libs" = yes || + test "$link_static" = yes; }; then + # Extract -R from dependency_libs + temp_deplibs= + for libdir in $dependency_libs; do + case $libdir in + -R*) func_stripname '-R' '' "$libdir" + temp_xrpath=$func_stripname_result + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) xrpath="$xrpath $temp_xrpath";; + esac;; + *) temp_deplibs="$temp_deplibs $libdir";; + esac + done + dependency_libs="$temp_deplibs" + fi + + newlib_search_path="$newlib_search_path $absdir" + # Link against this library + test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" + # ... and its dependency_libs + tmp_libs= + for deplib in $dependency_libs; do + newdependency_libs="$deplib $newdependency_libs" + if $opt_duplicate_deps ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + + if test "$link_all_deplibs" != no; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + case $deplib in + -L*) path="$deplib" ;; + *.la) + func_dirname "$deplib" "" "." + dir="$func_dirname_result" + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + func_warning "cannot determine absolute directory name of \`$dir'" + absdir="$dir" + fi + ;; + esac + if $GREP "^installed=no" $deplib > /dev/null; then + case $host in + *-*-darwin*) + depdepl= + eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + if test -n "$deplibrary_names" ; then + for tmp in $deplibrary_names ; do + depdepl=$tmp + done + if test -f "$absdir/$objdir/$depdepl" ; then + depdepl="$absdir/$objdir/$depdepl" + darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + if test -z "$darwin_install_name"; then + darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + fi + compiler_flags="$compiler_flags ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" + linker_flags="$linker_flags -dylib_file ${darwin_install_name}:${depdepl}" + path= + fi + fi + ;; + *) + path="-L$absdir/$objdir" + ;; + esac + else + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + test "$absdir" != "$libdir" && \ + func_warning "\`$deplib' seems to be moved" + + path="-L$absdir" + fi + ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$path $deplibs" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + if test "$pass" = link; then + if test "$linkmode" = "prog"; then + compile_deplibs="$new_inherited_linker_flags $compile_deplibs" + finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" + else + compiler_flags="$compiler_flags "`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + fi + fi + dependency_libs="$newdependency_libs" + if test "$pass" = dlpreopen; then + # Link the dlpreopened libraries before other libraries + for deplib in $save_deplibs; do + deplibs="$deplib $deplibs" + done + fi + if test "$pass" != dlopen; then + if test "$pass" != conv; then + # Make sure lib_search_path contains only unique directories. + lib_search_path= + for dir in $newlib_search_path; do + case "$lib_search_path " in + *" $dir "*) ;; + *) lib_search_path="$lib_search_path $dir" ;; + esac + done + newlib_search_path= + fi + + if test "$linkmode,$pass" != "prog,link"; then + vars="deplibs" + else + vars="compile_deplibs finalize_deplibs" + fi + for var in $vars dependency_libs; do + # Add libraries to $var in reverse order + eval tmp_libs=\"\$$var\" + new_libs= + for deplib in $tmp_libs; do + # FIXME: Pedantically, this is the right thing to do, so + # that some nasty dependency loop isn't accidentally + # broken: + #new_libs="$deplib $new_libs" + # Pragmatically, this seems to cause very few problems in + # practice: + case $deplib in + -L*) new_libs="$deplib $new_libs" ;; + -R*) ;; + *) + # And here is the reason: when a library appears more + # than once as an explicit dependence of a library, or + # is implicitly linked in more than once by the + # compiler, it is considered special, and multiple + # occurrences thereof are not removed. Compare this + # with having the same library being listed as a + # dependency of multiple other libraries: in this case, + # we know (pedantically, we assume) the library does not + # need to be listed more than once, so we keep only the + # last copy. This is not always right, but it is rare + # enough that we require users that really mean to play + # such unportable linking tricks to link the library + # using -Wl,-lname, so that libtool does not consider it + # for duplicate removal. + case " $specialdeplibs " in + *" $deplib "*) new_libs="$deplib $new_libs" ;; + *) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$deplib $new_libs" ;; + esac + ;; + esac + ;; + esac + done + tmp_libs= + for deplib in $new_libs; do + case $deplib in + -L*) + case " $tmp_libs " in + *" $deplib "*) ;; + *) tmp_libs="$tmp_libs $deplib" ;; + esac + ;; + *) tmp_libs="$tmp_libs $deplib" ;; + esac + done + eval $var=\"$tmp_libs\" + done # for var + fi + # Last step: remove runtime libs from dependency_libs + # (they stay in deplibs) + tmp_libs= + for i in $dependency_libs ; do + case " $predeps $postdeps $compiler_lib_search_path " in + *" $i "*) + i="" + ;; + esac + if test -n "$i" ; then + tmp_libs="$tmp_libs $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test "$linkmode" = prog; then + dlfiles="$newdlfiles" + fi + if test "$linkmode" = prog || test "$linkmode" = lib; then + dlprefiles="$newdlprefiles" + fi + + case $linkmode in + oldlib) + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for archives" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for archives" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for archives" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for archives" + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for archives" + + test -n "$release" && \ + func_warning "\`-release' is ignored for archives" + + test -n "$export_symbols$export_symbols_regex" && \ + func_warning "\`-export-symbols' is ignored for archives" + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs="$output" + objs="$objs$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form `libNAME.la'. + case $outputname in + lib*) + func_stripname 'lib' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + ;; + *) + test "$module" = no && \ + func_fatal_help "libtool library \`$output' must begin with \`lib'" + + if test "$need_lib_prefix" != no; then + # Add the "lib" prefix for modules if required + func_stripname '' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + else + func_stripname '' '.la' "$outputname" + libname=$func_stripname_result + fi + ;; + esac + + if test -n "$objs"; then + if test "$deplibs_check_method" != pass_all; then + func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" + else + $ECHO + $ECHO "*** Warning: Linking the shared library $output against the non-libtool" + $ECHO "*** objects $objs is not portable!" + libobjs="$libobjs $objs" + fi + fi + + test "$dlself" != no && \ + func_warning "\`-dlopen self' is ignored for libtool libraries" + + set dummy $rpath + shift + test "$#" -gt 1 && \ + func_warning "ignoring multiple \`-rpath's for a libtool library" + + install_libdir="$1" + + oldlibs= + if test -z "$rpath"; then + if test "$build_libtool_libs" = yes; then + # Building a libtool convenience library. + # Some compilers have problems with a `.al' extension so + # convenience libraries should have the same extension an + # archive normally would. + oldlibs="$output_objdir/$libname.$libext $oldlibs" + build_libtool_libs=convenience + build_old_libs=yes + fi + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for convenience libraries" + + test -n "$release" && \ + func_warning "\`-release' is ignored for convenience libraries" + else + + # Parse the version information argument. + save_ifs="$IFS"; IFS=':' + set dummy $vinfo 0 0 0 + shift + IFS="$save_ifs" + + test -n "$7" && \ + func_fatal_help "too many parameters to \`-version-info'" + + # convert absolute version numbers to libtool ages + # this retains compatibility with .la files and attempts + # to make the code below a bit more comprehensible + + case $vinfo_number in + yes) + number_major="$1" + number_minor="$2" + number_revision="$3" + # + # There are really only two kinds -- those that + # use the current revision as the major version + # and those that subtract age and use age as + # a minor version. But, then there is irix + # which has an extra 1 added just for fun + # + case $version_type in + darwin|linux|osf|windows|none) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_revision" + ;; + freebsd-aout|freebsd-elf|sunos) + current="$number_major" + revision="$number_minor" + age="0" + ;; + irix|nonstopux) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_minor" + lt_irix_increment=no + ;; + esac + ;; + no) + current="$1" + revision="$2" + age="$3" + ;; + esac + + # Check that each of the things are valid numbers. + case $current in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "CURRENT \`$current' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $revision in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "REVISION \`$revision' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $age in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "AGE \`$age' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + if test "$age" -gt "$current"; then + func_error "AGE \`$age' is greater than the current interface number \`$current'" + func_fatal_error "\`$vinfo' is not valid version information" + fi + + # Calculate the version variables. + major= + versuffix= + verstring= + case $version_type in + none) ;; + + darwin) + # Like Linux, but with the current version available in + # verstring for coding it into the library header + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + # Darwin ld doesn't like 0 for these options... + func_arith $current + 1 + minor_current=$func_arith_result + xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + ;; + + freebsd-aout) + major=".$current" + versuffix=".$current.$revision"; + ;; + + freebsd-elf) + major=".$current" + versuffix=".$current" + ;; + + irix | nonstopux) + if test "X$lt_irix_increment" = "Xno"; then + func_arith $current - $age + else + func_arith $current - $age + 1 + fi + major=$func_arith_result + + case $version_type in + nonstopux) verstring_prefix=nonstopux ;; + *) verstring_prefix=sgi ;; + esac + verstring="$verstring_prefix$major.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$revision + while test "$loop" -ne 0; do + func_arith $revision - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring_prefix$major.$iface:$verstring" + done + + # Before this point, $major must not contain `.'. + major=.$major + versuffix="$major.$revision" + ;; + + linux) + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + ;; + + osf) + func_arith $current - $age + major=.$func_arith_result + versuffix=".$current.$age.$revision" + verstring="$current.$age.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$age + while test "$loop" -ne 0; do + func_arith $current - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring:${iface}.0" + done + + # Make executables depend on our current version. + verstring="$verstring:${current}.0" + ;; + + qnx) + major=".$current" + versuffix=".$current" + ;; + + sunos) + major=".$current" + versuffix=".$current.$revision" + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 filesystems. + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + + *) + func_fatal_configuration "unknown library version type \`$version_type'" + ;; + esac + + # Clear the version info if we defaulted, and they specified a release. + if test -z "$vinfo" && test -n "$release"; then + major= + case $version_type in + darwin) + # we can't check for "0.0" in archive_cmds due to quoting + # problems, so we reset it completely + verstring= + ;; + *) + verstring="0.0" + ;; + esac + if test "$need_version" = no; then + versuffix= + else + versuffix=".0.0" + fi + fi + + # Remove version info from name if versioning should be avoided + if test "$avoid_version" = yes && test "$need_version" = no; then + major= + versuffix= + verstring="" + fi + + # Check to see if the archive will have undefined symbols. + if test "$allow_undefined" = yes; then + if test "$allow_undefined_flag" = unsupported; then + func_warning "undefined symbols not allowed in $host shared libraries" + build_libtool_libs=no + build_old_libs=yes + fi + else + # Don't allow undefined symbols. + allow_undefined_flag="$no_undefined_flag" + fi + + fi + + func_generate_dlsyms "$libname" "$libname" "yes" + libobjs="$libobjs $symfileobj" + test "X$libobjs" = "X " && libobjs= + + if test "$mode" != relink; then + # Remove our outputs, but don't remove object files since they + # may have been created when compiling PIC objects. + removelist= + tempremovelist=`$ECHO "$output_objdir/*"` + for p in $tempremovelist; do + case $p in + *.$objext | *.gcno) + ;; + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) + if test "X$precious_files_regex" != "X"; then + if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 + then + continue + fi + fi + removelist="$removelist $p" + ;; + *) ;; + esac + done + test -n "$removelist" && \ + func_show_eval "${RM}r \$removelist" + fi + + # Now set the variables for building old libraries. + if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then + oldlibs="$oldlibs $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. + #for path in $notinst_path; do + # lib_search_path=`$ECHO "X$lib_search_path " | $Xsed -e "s% $path % %g"` + # deplibs=`$ECHO "X$deplibs " | $Xsed -e "s% -L$path % %g"` + # dependency_libs=`$ECHO "X$dependency_libs " | $Xsed -e "s% -L$path % %g"` + #done + + if test -n "$xrpath"; then + # If the user specified any rpath flags, then add them. + temp_xrpath= + for libdir in $xrpath; do + temp_xrpath="$temp_xrpath -R$libdir" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then + dependency_libs="$temp_xrpath $dependency_libs" + fi + fi + + # Make sure dlfiles contains only unique files that won't be dlpreopened + old_dlfiles="$dlfiles" + dlfiles= + for lib in $old_dlfiles; do + case " $dlprefiles $dlfiles " in + *" $lib "*) ;; + *) dlfiles="$dlfiles $lib" ;; + esac + done + + # Make sure dlprefiles contains only unique files + old_dlprefiles="$dlprefiles" + dlprefiles= + for lib in $old_dlprefiles; do + case "$dlprefiles " in + *" $lib "*) ;; + *) dlprefiles="$dlprefiles $lib" ;; + esac + done + + if test "$build_libtool_libs" = yes; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + deplibs="$deplibs System.ltframework" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test "$build_libtool_need_lc" = "yes"; then + deplibs="$deplibs -lc" + fi + ;; + esac + fi + + # Transform deplibs into only deplibs that can be linked in shared. + name_save=$name + libname_save=$libname + release_save=$release + versuffix_save=$versuffix + major_save=$major + # I'm not sure if I'm treating the release correctly. I think + # release should show up in the -l (ie -lgmp5) so we don't want to + # add it in twice. Is that correct? + release="" + versuffix="" + major="" + newdeplibs= + droppeddeps=no + case $deplibs_check_method in + pass_all) + # Don't check for shared/static. Everything works. + # This might be a little naive. We might want to check + # whether the library exists or not. But this is on + # osf3 & osf4 and I'm not really sure... Just + # implementing what was already the behavior. + newdeplibs=$deplibs + ;; + test_compile) + # This code stresses the "libraries are programs" paradigm to its + # limits. Maybe even breaks it. We compile a program, linking it + # against the deplibs as a proxy for the library. Then we can check + # whether they linked in statically or dynamically with ldd. + $opt_dry_run || $RM conftest.c + cat > conftest.c </dev/null` + for potent_lib in $potential_libs; do + # Follow soft links. + if ls -lLd "$potent_lib" 2>/dev/null | + $GREP " -> " >/dev/null; then + continue + fi + # The statement above tries to avoid entering an + # endless loop below, in case of cyclic links. + # We might still enter an endless loop, since a link + # loop can be closed while we follow links, + # but so what? + potlib="$potent_lib" + while test -h "$potlib" 2>/dev/null; do + potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` + case $potliblink in + [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; + *) potlib=`$ECHO "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | + $SED -e 10q | + $EGREP "$file_magic_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + $ECHO + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + $ECHO "*** I have the capability to make that library automatically link in when" + $ECHO "*** you link to this library. But I can only do this if you have a" + $ECHO "*** shared version of the library, which you do not appear to have" + $ECHO "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $ECHO "*** with $libname but no candidates were found. (...for file magic test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a file magic. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + for a_deplib in $deplibs; do + case $a_deplib in + -l*) + func_stripname -l '' "$a_deplib" + name=$func_stripname_result + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + ;; + esac + fi + if test -n "$a_deplib" ; then + libname=`eval "\\$ECHO \"$libname_spec\""` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + potential_libs=`ls $i/$libname[.-]* 2>/dev/null` + for potent_lib in $potential_libs; do + potlib="$potent_lib" # see symlink-check above in file_magic test + if eval "\$ECHO \"X$potent_lib\"" 2>/dev/null | $Xsed -e 10q | \ + $EGREP "$match_pattern_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + $ECHO + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + $ECHO "*** I have the capability to make that library automatically link in when" + $ECHO "*** you link to this library. But I can only do this if you have a" + $ECHO "*** shared version of the library, which you do not appear to have" + $ECHO "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a regex pattern. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs="" + tmp_deplibs=`$ECHO "X $deplibs" | $Xsed \ + -e 's/ -lc$//' -e 's/ -[LR][^ ]*//g'` + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + for i in $predeps $postdeps ; do + # can't use Xsed below, because $i might contain '/' + tmp_deplibs=`$ECHO "X $tmp_deplibs" | $Xsed -e "s,$i,,"` + done + fi + if $ECHO "X $tmp_deplibs" | $Xsed -e 's/[ ]//g' | + $GREP . >/dev/null; then + $ECHO + if test "X$deplibs_check_method" = "Xnone"; then + $ECHO "*** Warning: inter-library dependencies are not supported in this platform." + else + $ECHO "*** Warning: inter-library dependencies are not known to be supported." + fi + $ECHO "*** All declared inter-library dependencies are being dropped." + droppeddeps=yes + fi + ;; + esac + versuffix=$versuffix_save + major=$major_save + release=$release_save + libname=$libname_save + name=$name_save + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library with the System framework + newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's/ -lc / System.ltframework /'` + ;; + esac + + if test "$droppeddeps" = yes; then + if test "$module" = yes; then + $ECHO + $ECHO "*** Warning: libtool could not satisfy all declared inter-library" + $ECHO "*** dependencies of module $libname. Therefore, libtool will create" + $ECHO "*** a static module, that should work as long as the dlopening" + $ECHO "*** application is linked with the -dlopen flag." + if test -z "$global_symbol_pipe"; then + $ECHO + $ECHO "*** However, this would only work if libtool was able to extract symbol" + $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could" + $ECHO "*** not find such a program. So, this module is probably useless." + $ECHO "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + else + $ECHO "*** The inter-library dependencies that have been dropped here will be" + $ECHO "*** automatically added whenever a program is linked with this library" + $ECHO "*** or is declared to -dlopen it." + + if test "$allow_undefined" = no; then + $ECHO + $ECHO "*** Since this library must not contain undefined symbols," + $ECHO "*** because either the platform does not support them or" + $ECHO "*** it was explicitly requested with -no-undefined," + $ECHO "*** libtool will only create a static version of it." + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + fi + fi + # Done checking deplibs! + deplibs=$newdeplibs + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + case $host in + *-*-darwin*) + newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + new_inherited_linker_flags=`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + deplibs=`$ECHO "X $deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $deplibs " in + *" -L$path/$objdir "*) + new_libs="$new_libs -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$new_libs $deplib" ;; + esac + ;; + *) new_libs="$new_libs $deplib" ;; + esac + done + deplibs="$new_libs" + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + + # Test again, we may have decided not to build it any more + if test "$build_libtool_libs" = yes; then + if test "$hardcode_into_libs" = yes; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath="$finalize_rpath" + test "$mode" != relink && rpath="$compile_rpath$rpath" + for libdir in $rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + dep_rpath="$dep_rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + if test -n "$hardcode_libdir_flag_spec_ld"; then + eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\" + else + eval dep_rpath=\"$hardcode_libdir_flag_spec\" + fi + fi + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" + fi + test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" + fi - # Try to get the absolute directory name. - absdir=\`cd \"\$thisdir\" && pwd\` - test -n \"\$absdir\" && thisdir=\"\$absdir\" -" + shlibpath="$finalize_shlibpath" + test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath" + if test -n "$shlibpath"; then + eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" + fi - if test "$fast_install" = yes; then - $echo >> $output "\ - program=lt-'$outputname'$exeext - progdir=\"\$thisdir/$objdir\" + # Get the real and link names of the library. + eval shared_ext=\"$shrext_cmds\" + eval library_names=\"$library_names_spec\" + set dummy $library_names + shift + realname="$1" + shift - if test ! -f \"\$progdir/\$program\" || \\ - { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ - test \"X\$file\" != \"X\$progdir/\$program\"; }; then + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + if test -z "$dlname"; then + dlname=$soname + fi - file=\"\$\$-\$program\" + lib="$output_objdir/$realname" + linknames= + for link + do + linknames="$linknames $link" + done - if test ! -d \"\$progdir\"; then - $mkdir \"\$progdir\" - else - $rm \"\$progdir/\$file\" - fi" + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$ECHO "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + test "X$libobjs" = "X " && libobjs= - $echo >> $output "\ + delfiles= + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" + export_symbols="$output_objdir/$libname.uexp" + delfiles="$delfiles $export_symbols" + fi + + orig_export_symbols= + case $host_os in + cygwin* | mingw* | cegcc*) + if test -n "$export_symbols" && test -z "$export_symbols_regex"; then + # exporting using user supplied symfile + if test "x`$SED 1q $export_symbols`" != xEXPORTS; then + # and it's NOT already a .def file. Must figure out + # which of the given symbols are data symbols and tag + # them as such. So, trigger use of export_symbols_cmds. + # export_symbols gets reassigned inside the "prepare + # the list of exported symbols" if statement, so the + # include_expsyms logic still works. + orig_export_symbols="$export_symbols" + export_symbols= + always_export_symbols=yes + fi + fi + ;; + esac - # relink executable if necessary - if test -n \"\$relink_command\"; then - if relink_command_output=\`eval \$relink_command 2>&1\`; then : - else - $echo \"\$relink_command_output\" >&2 - $rm \"\$progdir/\$file\" - exit $EXIT_FAILURE - fi - fi + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + cmds=$export_symbols_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + func_len " $cmd" + len=$func_len_result + if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + func_show_eval "$cmd" 'exit $?' + skipped_export=false + else + # The command line is too long to execute in one step. + func_verbose "using reloadable object file for export list..." + skipped_export=: + # Break out early, otherwise skipped_export may be + # set to false by a later but shorter cmd. + break + fi + done + IFS="$save_ifs" + if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + fi - $mv \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || - { $rm \"\$progdir/\$program\"; - $mv \"\$progdir/\$file\" \"\$progdir/\$program\"; } - $rm \"\$progdir/\$file\" - fi" - else - $echo >> $output "\ - program='$outputname' - progdir=\"\$thisdir/$objdir\" -" + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"' + fi + + if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + delfiles="$delfiles $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols fi - $echo >> $output "\ + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + tmp_deplibs="$tmp_deplibs $test_deplib" + ;; + esac + done + deplibs="$tmp_deplibs" - if test -f \"\$progdir/\$program\"; then" + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec" && + test "$compiler_needs_object" = yes && + test -z "$libobjs"; then + # extract the archives, so we have objects to list. + # TODO: could optimize this to just extract one archive. + whole_archive_flag_spec= + fi + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + else + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" - # Export our shlibpath_var if we have one. - if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then - $echo >> $output "\ - # Add our own library path to $shlibpath_var - $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + func_extract_archives $gentop $convenience + libobjs="$libobjs $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + fi - # Some systems cannot cope with colon-terminated $shlibpath_var - # The second colon is a workaround for a bug in BeOS R4 sed - $shlibpath_var=\`\$echo \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\` + if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + linker_flags="$linker_flags $flag" + fi - export $shlibpath_var -" + # Make a backup of the uninstalled library when relinking + if test "$mode" = relink; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? fi - # fixup the dll searchpath if we need to. - if test -n "$dllsearchpath"; then - $echo >> $output "\ - # Add the dll search path components to the executable PATH - PATH=$dllsearchpath:\$PATH -" + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + eval test_cmds=\"$module_expsym_cmds\" + cmds=$module_expsym_cmds + else + eval test_cmds=\"$module_cmds\" + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval test_cmds=\"$archive_expsym_cmds\" + cmds=$archive_expsym_cmds + else + eval test_cmds=\"$archive_cmds\" + cmds=$archive_cmds + fi fi - $echo >> $output "\ - if test \"\$libtool_execute_magic\" != \"$magic\"; then - # Run the actual program with our arguments. -" - case $host in - # Backslashes separate directories on plain windows - *-*-mingw | *-*-os2*) - $echo >> $output "\ - exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} -" - ;; + if test "X$skipped_export" != "X:" && + func_len " $test_cmds" && + len=$func_len_result && + test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise + # or, if using GNU ld and skipped_export is not :, use a linker + # script. - *) - $echo >> $output "\ - exec \"\$progdir/\$program\" \${1+\"\$@\"} -" - ;; - esac - $echo >> $output "\ - \$echo \"\$0: cannot exec \$program \${1+\"\$@\"}\" - exit $EXIT_FAILURE - fi - else - # The program doesn't exist. - \$echo \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 - \$echo \"This script is just a wrapper for \$program.\" 1>&2 - $echo \"See the $PACKAGE documentation for more information.\" 1>&2 - exit $EXIT_FAILURE - fi -fi\ -" - chmod +x $output - fi - exit $EXIT_SUCCESS - ;; - esac + # Save the value of $output and $libobjs because we want to + # use them later. If we have whole_archive_flag_spec, we + # want to use save_libobjs as it was before + # whole_archive_flag_spec was expanded, because we can't + # assume the linker understands whole_archive_flag_spec. + # This may have to be revisited, in case too many + # convenience libraries get linked in and end up exceeding + # the spec. + if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + fi + save_output=$output + output_la=`$ECHO "X$output" | $Xsed -e "$basename"` - # See if we need to build an old-fashioned archive. - for oldlib in $oldlibs; do + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + last_robj= + k=1 - if test "$build_libtool_libs" = convenience; then - oldobjs="$libobjs_save" - addlibs="$convenience" - build_libtool_libs=no - else - if test "$build_libtool_libs" = module; then - oldobjs="$libobjs_save" - build_libtool_libs=no - else - oldobjs="$old_deplibs $non_pic_objects" - fi - addlibs="$old_convenience" - fi + if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then + output=${output_objdir}/${output_la}.lnkscript + func_verbose "creating GNU ld script: $output" + $ECHO 'INPUT (' > $output + for obj in $save_libobjs + do + $ECHO "$obj" >> $output + done + $ECHO ')' >> $output + delfiles="$delfiles $output" + elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then + output=${output_objdir}/${output_la}.lnk + func_verbose "creating linker input file list: $output" + : > $output + set x $save_libobjs + shift + firstobj= + if test "$compiler_needs_object" = yes; then + firstobj="$1 " + shift + fi + for obj + do + $ECHO "$obj" >> $output + done + delfiles="$delfiles $output" + output=$firstobj\"$file_list_spec$output\" + else + if test -n "$save_libobjs"; then + func_verbose "creating reloadable object files..." + output=$output_objdir/$output_la-${k}.$objext + eval test_cmds=\"$reload_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + if test "X$objlist" = X || + test "$len" -lt "$max_cmd_len"; then + func_append objlist " $obj" + else + # The command $test_cmds is almost too long, add a + # command to the queue. + if test "$k" -eq 1 ; then + # The first file doesn't have a previous command to add. + eval concat_cmds=\"$reload_cmds $objlist $last_robj\" + else + # All subsequent reloadable object files will link in + # the last one created. + eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj~\$RM $last_robj\" + fi + last_robj=$output_objdir/$output_la-${k}.$objext + func_arith $k + 1 + k=$func_arith_result + output=$output_objdir/$output_la-${k}.$objext + objlist=$obj + func_len " $last_robj" + func_arith $len0 + $func_len_result + len=$func_arith_result + fi + done + # Handle the remaining objects by creating one last + # reloadable object file. All subsequent reloadable object + # files will link in the last one created. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\" + if test -n "$last_robj"; then + eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" + fi + delfiles="$delfiles $output" + + else + output= + fi - if test -n "$addlibs"; then - gentop="$output_objdir/${outputname}x" - generated="$generated $gentop" + if ${skipped_export-false}; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + libobjs=$output + # Append the command to create the export file. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" + fi + fi - func_extract_archives $gentop $addlibs - oldobjs="$oldobjs $func_extract_archives_result" - fi + test -n "$save_libobjs" && + func_verbose "creating a temporary reloadable object file: $output" - # Do each command in the archive commands. - if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then - cmds=$old_archive_from_new_cmds - else - # POSIX demands no paths to be encoded in archives. We have - # to avoid creating archives with duplicate basenames if we - # might have to extract them afterwards, e.g., when creating a - # static archive out of a convenience library, or when linking - # the entirety of a libtool archive into another (currently - # not supported by libtool). - if (for obj in $oldobjs - do - $echo "X$obj" | $Xsed -e 's%^.*/%%' - done | sort | sort -uc >/dev/null 2>&1); then - : - else - $echo "copying selected object files to avoid basename conflicts..." + # Loop through the commands generated above and execute them. + save_ifs="$IFS"; IFS='~' + for cmd in $concat_cmds; do + IFS="$save_ifs" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi - if test -z "$gentop"; then - gentop="$output_objdir/${outputname}x" - generated="$generated $gentop" + exit $lt_exit + } + done + IFS="$save_ifs" - $show "${rm}r $gentop" - $run ${rm}r "$gentop" - $show "$mkdir $gentop" - $run $mkdir "$gentop" - exit_status=$? - if test "$exit_status" -ne 0 && test ! -d "$gentop"; then - exit $exit_status + if test -n "$export_symbols_regex" && ${skipped_export-false}; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' fi fi - save_oldobjs=$oldobjs - oldobjs= - counter=1 - for obj in $save_oldobjs - do - objbase=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` - case " $oldobjs " in - " ") oldobjs=$obj ;; - *[\ /]"$objbase "*) - while :; do - # Make sure we don't pick an alternate name that also - # overlaps. - newobj=lt$counter-$objbase - counter=`expr $counter + 1` - case " $oldobjs " in - *[\ /]"$newobj "*) ;; - *) if test ! -f "$gentop/$newobj"; then break; fi ;; - esac - done - $show "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" - $run ln "$obj" "$gentop/$newobj" || - $run cp "$obj" "$gentop/$newobj" - oldobjs="$oldobjs $gentop/$newobj" - ;; - *) oldobjs="$oldobjs $obj" ;; - esac - done - fi + if ${skipped_export-false}; then + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"' + fi + + if test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + delfiles="$delfiles $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + fi - eval cmds=\"$old_archive_cmds\" + libobjs=$output + # Restore the value of output. + output=$save_output - if len=`expr "X$cmds" : ".*"` && - test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then - cmds=$old_archive_cmds - else - # the command line is too long to link in one step, link in parts - $echo "using piecewise archive linking..." - save_RANLIB=$RANLIB - RANLIB=: - objlist= - concat_cmds= - save_oldobjs=$oldobjs + if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + fi + # Expand the library linking commands again to reset the + # value of $libobjs for piecewise linking. - # Is there a better way of finding the last object in the list? - for obj in $save_oldobjs - do - last_oldobj=$obj - done - for obj in $save_oldobjs - do - oldobjs="$objlist $obj" - objlist="$objlist $obj" - eval test_cmds=\"$old_archive_cmds\" - if len=`expr "X$test_cmds" : ".*" 2>/dev/null` && - test "$len" -le "$max_cmd_len"; then - : + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + cmds=$module_expsym_cmds else - # the above command should be used before it gets too long - oldobjs=$objlist - if test "$obj" = "$last_oldobj" ; then - RANLIB=$save_RANLIB - fi - test -z "$concat_cmds" || concat_cmds=$concat_cmds~ - eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" - objlist= + cmds=$module_cmds fi - done - RANLIB=$save_RANLIB - oldobjs=$objlist - if test "X$oldobjs" = "X" ; then - eval cmds=\"\$concat_cmds\" else - eval cmds=\"\$concat_cmds~\$old_archive_cmds\" + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + cmds=$archive_expsym_cmds + else + cmds=$archive_cmds + fi fi fi - fi - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - eval cmd=\"$cmd\" - IFS="$save_ifs" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - done - if test -n "$generated"; then - $show "${rm}r$generated" - $run ${rm}r$generated - fi + if test -n "$delfiles"; then + # Append the command to remove temporary files to $cmds. + eval cmds=\"\$cmds~\$RM $delfiles\" + fi - # Now create the libtool archive. - case $output in - *.la) - old_library= - test "$build_old_libs" = yes && old_library="$libname.$libext" - $show "creating $output" + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" - # Preserve any variables that may affect compiler behavior - for var in $variables_saved_for_relink; do - if eval test -z \"\${$var+set}\"; then - relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" - elif eval var_value=\$$var; test -z "$var_value"; then - relink_command="$var=; export $var; $relink_command" - else - var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` - relink_command="$var=\"$var_value\"; export $var; $relink_command" + func_extract_archives $gentop $dlprefiles + libobjs="$libobjs $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= fi - done - # Quote the link command for shipping. - relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" - relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"` - if test "$hardcode_automatic" = yes ; then - relink_command= - fi + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? - # Only create the output if not a dry run. - if test -z "$run"; then - for installed in no yes; do - if test "$installed" = yes; then - if test -z "$install_libdir"; then - break + # Restore the uninstalled library and exit + if test "$mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) fi - output="$output_objdir/$outputname"i - # Replace all uninstalled libtool libraries with the installed ones - newdependency_libs= - for deplib in $dependency_libs; do - case $deplib in - *.la) - name=`$echo "X$deplib" | $Xsed -e 's%^.*/%%'` - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` - if test -z "$libdir"; then - $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 - exit $EXIT_FAILURE - fi - newdependency_libs="$newdependency_libs $libdir/$name" - ;; - *) newdependency_libs="$newdependency_libs $deplib" ;; - esac - done - dependency_libs="$newdependency_libs" - newdlfiles= - for lib in $dlfiles; do - name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` - if test -z "$libdir"; then - $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 - exit $EXIT_FAILURE - fi - newdlfiles="$newdlfiles $libdir/$name" - done - dlfiles="$newdlfiles" - newdlprefiles= - for lib in $dlprefiles; do - name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` - eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` - if test -z "$libdir"; then - $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 - exit $EXIT_FAILURE - fi - newdlprefiles="$newdlprefiles $libdir/$name" - done - dlprefiles="$newdlprefiles" - else - newdlfiles= - for lib in $dlfiles; do - case $lib in - [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; - *) abs=`pwd`"/$lib" ;; - esac - newdlfiles="$newdlfiles $abs" - done - dlfiles="$newdlfiles" - newdlprefiles= - for lib in $dlprefiles; do - case $lib in - [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; - *) abs=`pwd`"/$lib" ;; - esac - newdlprefiles="$newdlprefiles $abs" - done - dlprefiles="$newdlprefiles" - fi - $rm $output - # place dlname in correct position for cygwin - tdlname=$dlname - case $host,$output,$installed,$module,$dlname in - *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;; - esac - $echo > $output "\ -# $outputname - a libtool library file -# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP -# -# Please DO NOT delete this file! -# It is necessary for linking the library. - -# The name that we can dlopen(3). -dlname='$tdlname' -# Names of this library. -library_names='$library_names' - -# The name of the static archive. -old_library='$old_library' - -# Libraries that this one depends upon. -dependency_libs='$dependency_libs' - -# Version information for $libname. -current=$current -age=$age -revision=$revision + exit $lt_exit + } + done + IFS="$save_ifs" -# Is this an already installed library? -installed=$installed + # Restore the uninstalled library and exit + if test "$mode" = relink; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? -# Should we warn about portability when linking against -modules? -shouldnotlink=$module + if test -n "$convenience"; then + if test -z "$whole_archive_flag_spec"; then + func_show_eval '${RM}r "$gentop"' + fi + fi -# Files to dlopen/dlpreopen -dlopen='$dlfiles' -dlpreopen='$dlprefiles' + exit $EXIT_SUCCESS + fi -# Directory that this library needs to be installed in: -libdir='$install_libdir'" - if test "$installed" = no && test "$need_relink" = yes; then - $echo >> $output "\ -relink_command=\"$relink_command\"" + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' fi done - fi - - # Do a symbolic link so that the libtool archive can be found in - # LD_LIBRARY_PATH before the program is installed. - $show "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)" - $run eval '(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)' || exit $? - ;; - esac - exit $EXIT_SUCCESS - ;; - - # libtool install mode - install) - modename="$modename: install" - - # There may be an optional sh(1) argument at the beginning of - # install_prog (especially on Windows NT). - if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || - # Allow the use of GNU shtool's install command. - $echo "X$nonopt" | grep shtool > /dev/null; then - # Aesthetically quote it. - arg=`$echo "X$nonopt" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - install_prog="$arg " - arg="$1" - shift - else - install_prog= - arg=$nonopt - fi - # The real first argument should be the name of the installation program. - # Aesthetically quote it. - arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" + # If -module or -export-dynamic was specified, set the dlname. + if test "$module" = yes || test "$export_dynamic" = yes; then + # On all known operating systems, these are identical. + dlname="$soname" + fi + fi ;; - esac - install_prog="$install_prog$arg" - # We need to accept at least all the BSD install flags. - dest= - files= - opts= - prev= - install_type= - isdir=no - stripme= - for arg - do - if test -n "$dest"; then - files="$files $dest" - dest=$arg - continue + obj) + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for objects" fi - case $arg in - -d) isdir=yes ;; - -f) - case " $install_prog " in - *[\\\ /]cp\ *) ;; - *) prev=$arg ;; - esac - ;; - -g | -m | -o) prev=$arg ;; - -s) - stripme=" -s" - continue - ;; - -*) + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for objects" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for objects" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for objects" + + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for objects" + + test -n "$release" && \ + func_warning "\`-release' is ignored for objects" + + case $output in + *.lo) + test -n "$objs$old_deplibs" && \ + func_fatal_error "cannot build library object \`$output' from non-libtool objects" + + libobj=$output + func_lo2o "$libobj" + obj=$func_lo2o_result ;; *) - # If the previous option needed an argument, then skip it. - if test -n "$prev"; then - prev= - else - dest=$arg - continue - fi + libobj= + obj="$output" ;; esac - # Aesthetically quote the argument. - arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` - case $arg in - *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") - arg="\"$arg\"" - ;; - esac - install_prog="$install_prog $arg" - done + # Delete the old objects. + $opt_dry_run || $RM $obj $libobj - if test -z "$install_prog"; then - $echo "$modename: you must specify an install program" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi + # Objects from convenience libraries. This assumes + # single-version convenience libraries. Whenever we create + # different ones for PIC/non-PIC, this we'll have to duplicate + # the extraction. + reload_conv_objs= + gentop= + # reload_cmds runs $LD directly, so let us get rid of + # -Wl from whole_archive_flag_spec and hope we can get by with + # turning comma into space.. + wl= - if test -n "$prev"; then - $echo "$modename: the \`$prev' option requires an argument" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" + reload_conv_objs=$reload_objs\ `$ECHO "X$tmp_whole_archive_flags" | $Xsed -e 's|,| |g'` + else + gentop="$output_objdir/${obj}x" + generated="$generated $gentop" - if test -z "$files"; then - if test -z "$dest"; then - $echo "$modename: no file or destination specified" 1>&2 - else - $echo "$modename: you must specify a destination" 1>&2 + func_extract_archives $gentop $convenience + reload_conv_objs="$reload_objs $func_extract_archives_result" + fi fi - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi - # Strip any trailing slash from the destination. - dest=`$echo "X$dest" | $Xsed -e 's%/$%%'` + # Create the old-style object. + reload_objs="$objs$old_deplibs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test - # Check to see that the destination is a directory. - test -d "$dest" && isdir=yes - if test "$isdir" = yes; then - destdir="$dest" - destname= - else - destdir=`$echo "X$dest" | $Xsed -e 's%/[^/]*$%%'` - test "X$destdir" = "X$dest" && destdir=. - destname=`$echo "X$dest" | $Xsed -e 's%^.*/%%'` + output="$obj" + func_execute_cmds "$reload_cmds" 'exit $?' - # Not a directory, so check to see that there is only one file specified. - set dummy $files - if test "$#" -gt 2; then - $echo "$modename: \`$dest' is not a directory" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS fi - fi - case $destdir in - [\\/]* | [A-Za-z]:[\\/]*) ;; - *) - for file in $files; do - case $file in - *.lo) ;; - *) - $echo "$modename: \`$destdir' must be an absolute directory name" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - ;; - esac - done + + if test "$build_libtool_libs" != yes; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + # $show "echo timestamp > $libobj" + # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? + exit $EXIT_SUCCESS + fi + + if test -n "$pic_flag" || test "$pic_mode" != default; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs $reload_conv_objs" + output="$libobj" + func_execute_cmds "$reload_cmds" 'exit $?' + fi + + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS ;; - esac - # This variable tells wrapper scripts just to set variables rather - # than running their programs. - libtool_install_magic="$magic" + prog) + case $host in + *cygwin*) func_stripname '' '.exe' "$output" + output=$func_stripname_result.exe;; + esac + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for programs" - staticlibs= - future_libdirs= - current_libdirs= - for file in $files; do + test -n "$release" && \ + func_warning "\`-release' is ignored for programs" - # Do each installation. - case $file in - *.$libext) - # Do the static libraries later. - staticlibs="$staticlibs $file" + test "$preload" = yes \ + && test "$dlopen_support" = unknown \ + && test "$dlopen_self" = unknown \ + && test "$dlopen_self_static" = unknown && \ + func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'` + finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'` ;; + esac - *.la) - # Check to see that this really is a libtool archive. - if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : - else - $echo "$modename: \`$file' is not a valid libtool archive" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE + case $host in + *-*-darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + # But is supposedly fixed on 10.4 or later (yay!). + if test "$tagname" = CXX ; then + case ${MACOSX_DEPLOYMENT_TARGET-10.0} in + 10.[0123]) + compile_command="$compile_command ${wl}-bind_at_load" + finalize_command="$finalize_command ${wl}-bind_at_load" + ;; + esac fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac - library_names= - old_library= - relink_command= - # If there is no directory component, then add one. - case $file in - */* | *\\*) . $file ;; - *) . ./$file ;; - esac - # Add the libdir to current_libdirs if it is the destination. - if test "X$destdir" = "X$libdir"; then - case "$current_libdirs " in - *" $libdir "*) ;; - *) current_libdirs="$current_libdirs $libdir" ;; + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $compile_deplibs " in + *" -L$path/$objdir "*) + new_libs="$new_libs -L$path/$objdir" ;; esac - else - # Note the libdir as a future libdir. - case "$future_libdirs " in - *" $libdir "*) ;; - *) future_libdirs="$future_libdirs $libdir" ;; + ;; + esac + done + for deplib in $compile_deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$new_libs $deplib" ;; esac - fi + ;; + *) new_libs="$new_libs $deplib" ;; + esac + done + compile_deplibs="$new_libs" - dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`/ - test "X$dir" = "X$file/" && dir= - dir="$dir$objdir" - if test -n "$relink_command"; then - # Determine the prefix the user has applied to our future dir. - inst_prefix_dir=`$echo "$destdir" | $SED "s%$libdir\$%%"` + compile_command="$compile_command $compile_deplibs" + finalize_command="$finalize_command $finalize_deplibs" - # Don't allow the user to place us outside of our expected - # location b/c this prevents finding dependent libraries that - # are installed to the same prefix. - # At present, this check doesn't affect windows .dll's that - # are installed into $libdir/../bin (currently, that works fine) - # but it's something to keep an eye on. - if test "$inst_prefix_dir" = "$destdir"; then - $echo "$modename: error: cannot install \`$file' to a directory not ending in $libdir" 1>&2 - exit $EXIT_FAILURE - fi + if test -n "$rpath$xrpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath $xrpath; do + # This is the magic to use -rpath. + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + fi - if test -n "$inst_prefix_dir"; then - # Stick the inst_prefix_dir data into the link command. - relink_command=`$echo "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` + # Now hardcode the library paths + rpath= + hardcode_libdirs= + for libdir in $compile_rpath $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi else - relink_command=`$echo "$relink_command" | $SED "s%@inst_prefix_dir@%%"` + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$libdir:"*) ;; + ::) dllsearchpath=$libdir;; + *) dllsearchpath="$dllsearchpath:$libdir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) dllsearchpath="$dllsearchpath:$testbindir";; + esac + ;; + esac + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + compile_rpath="$rpath" - $echo "$modename: warning: relinking \`$file'" 1>&2 - $show "$relink_command" - if $run eval "$relink_command"; then : + rpath= + hardcode_libdirs= + for libdir in $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi else - $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 - exit $EXIT_FAILURE + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;; + esac fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + finalize_rpath="$rpath" - # See the names of the shared library. - set dummy $library_names - if test -n "$2"; then - realname="$2" - shift - shift + if test -n "$libobjs" && test "$build_old_libs" = yes; then + # Transform all the library objects into standard objects. + compile_command=`$ECHO "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + finalize_command=`$ECHO "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + fi - srcname="$realname" - test -n "$relink_command" && srcname="$realname"T + func_generate_dlsyms "$outputname" "@PROGRAM@" "no" - # Install the shared library and build the symlinks. - $show "$install_prog $dir/$srcname $destdir/$realname" - $run eval "$install_prog $dir/$srcname $destdir/$realname" || exit $? - if test -n "$stripme" && test -n "$striplib"; then - $show "$striplib $destdir/$realname" - $run eval "$striplib $destdir/$realname" || exit $? - fi + # template prelinking step + if test -n "$prelink_cmds"; then + func_execute_cmds "$prelink_cmds" 'exit $?' + fi - if test "$#" -gt 0; then - # Delete the old symlinks, and create new ones. - # Try `ln -sf' first, because the `ln' binary might depend on - # the symlink we replace! Solaris /bin/ln does not understand -f, - # so we also need to try rm && ln -s. - for linkname - do - if test "$linkname" != "$realname"; then - $show "(cd $destdir && { $LN_S -f $realname $linkname || { $rm $linkname && $LN_S $realname $linkname; }; })" - $run eval "(cd $destdir && { $LN_S -f $realname $linkname || { $rm $linkname && $LN_S $realname $linkname; }; })" - fi - done - fi + wrappers_required=yes + case $host in + *cygwin* | *mingw* ) + if test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + *cegcc) + # Disable wrappers for cegcc, we are cross compiling anyway. + wrappers_required=no + ;; + *) + if test "$need_relink" = no || test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + esac + if test "$wrappers_required" = no; then + # Replace the output file specification. + compile_command=`$ECHO "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + link_command="$compile_command$compile_rpath" - # Do each command in the postinstall commands. - lib="$destdir/$realname" - cmds=$postinstall_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || { - lt_exit=$? - - # Restore the uninstalled library and exit - if test "$mode" = relink; then - $run eval '(cd $output_objdir && $rm ${realname}T && $mv ${realname}U $realname)' - fi + # We have no uninstalled library dependencies, so finalize right now. + exit_status=0 + func_show_eval "$link_command" 'exit_status=$?' - exit $lt_exit - } - done - IFS="$save_ifs" + # Delete the generated files. + if test -f "$output_objdir/${outputname}S.${objext}"; then + func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' fi - # Install the pseudo-library for information purposes. - name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` - instname="$dir/$name"i - $show "$install_prog $instname $destdir/$name" - $run eval "$install_prog $instname $destdir/$name" || exit $? - - # Maybe install the static library, too. - test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" - ;; + exit $exit_status + fi - *.lo) - # Install (i.e. copy) a libtool object. + if test -n "$compile_shlibpath$finalize_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi - # Figure out destination file name, if it wasn't already specified. - if test -n "$destname"; then - destfile="$destdir/$destname" - else - destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` - destfile="$destdir/$destfile" + compile_var= + finalize_var= + if test -n "$runpath_var"; then + if test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + compile_var="$runpath_var=\"$rpath\$$runpath_var\" " fi - - # Deduce the name of the destination old-style object file. - case $destfile in - *.lo) - staticdest=`$echo "X$destfile" | $Xsed -e "$lo2o"` - ;; - *.$objext) - staticdest="$destfile" - destfile= - ;; - *) - $echo "$modename: cannot copy a libtool object to \`$destfile'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - ;; - esac - - # Install the libtool object if requested. - if test -n "$destfile"; then - $show "$install_prog $file $destfile" - $run eval "$install_prog $file $destfile" || exit $? + if test -n "$finalize_perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $finalize_perm_rpath; do + rpath="$rpath$dir:" + done + finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " fi + fi - # Install the old object if enabled. - if test "$build_old_libs" = yes; then - # Deduce the name of the old-style object file. - staticobj=`$echo "X$file" | $Xsed -e "$lo2o"` - - $show "$install_prog $staticobj $staticdest" - $run eval "$install_prog \$staticobj \$staticdest" || exit $? - fi + if test "$no_install" = yes; then + # We don't need to create a wrapper script. + link_command="$compile_var$compile_command$compile_rpath" + # Replace the output file specification. + link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $opt_dry_run || $RM $output + # Link the executable and exit + func_show_eval "$link_command" 'exit $?' exit $EXIT_SUCCESS - ;; + fi - *) - # Figure out destination file name, if it wasn't already specified. - if test -n "$destname"; then - destfile="$destdir/$destname" + if test "$hardcode_action" = relink; then + # Fast installation is not supported + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + + func_warning "this platform does not like uninstalled shared libraries" + func_warning "\`$output' will be relinked during installation" + else + if test "$fast_install" != no; then + link_command="$finalize_var$compile_command$finalize_rpath" + if test "$fast_install" = yes; then + relink_command=`$ECHO "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'` + else + # fast_install is set to needless + relink_command= + fi else - destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` - destfile="$destdir/$destfile" + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" fi + fi - # If the file is missing, and there is a .exe on the end, strip it - # because it is most likely a libtool script we actually want to - # install - stripped_ext="" - case $file in - *.exe) - if test ! -f "$file"; then - file=`$echo $file|${SED} 's,.exe$,,'` - stripped_ext=".exe" - fi - ;; - esac - - # Do a test to see if this is really a libtool program. - case $host in - *cygwin*|*mingw*) - wrapper=`$echo $file | ${SED} -e 's,.exe$,,'` - ;; - *) - wrapper=$file - ;; - esac - if (${SED} -e '4q' $wrapper | grep "^# Generated by .*$PACKAGE")>/dev/null 2>&1; then - notinst_deplibs= - relink_command= - - # Note that it is not necessary on cygwin/mingw to append a dot to - # foo even if both foo and FILE.exe exist: automatic-append-.exe - # behavior happens only for exec(3), not for open(2)! Also, sourcing - # `FILE.' does not work on cygwin managed mounts. - # - # If there is no directory component, then add one. - case $wrapper in - */* | *\\*) . ${wrapper} ;; - *) . ./${wrapper} ;; - esac - - # Check the variables that should have been set. - if test -z "$notinst_deplibs"; then - $echo "$modename: invalid libtool wrapper script \`$wrapper'" 1>&2 - exit $EXIT_FAILURE - fi + # Replace the output file specification. + link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` - finalize=yes - for lib in $notinst_deplibs; do - # Check to see that each library is installed. - libdir= - if test -f "$lib"; then - # If there is no directory component, then add one. - case $lib in - */* | *\\*) . $lib ;; - *) . ./$lib ;; - esac - fi - libfile="$libdir/"`$echo "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test - if test -n "$libdir" && test ! -f "$libfile"; then - $echo "$modename: warning: \`$lib' has not been installed in \`$libdir'" 1>&2 - finalize=no - fi - done + # Delete the old output files. + $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname - relink_command= - # Note that it is not necessary on cygwin/mingw to append a dot to - # foo even if both foo and FILE.exe exist: automatic-append-.exe - # behavior happens only for exec(3), not for open(2)! Also, sourcing - # `FILE.' does not work on cygwin managed mounts. - # - # If there is no directory component, then add one. - case $wrapper in - */* | *\\*) . ${wrapper} ;; - *) . ./${wrapper} ;; - esac + func_show_eval "$link_command" 'exit $?' - outputname= - if test "$fast_install" = no && test -n "$relink_command"; then - if test "$finalize" = yes && test -z "$run"; then - tmpdir=`func_mktempdir` - file=`$echo "X$file$stripped_ext" | $Xsed -e 's%^.*/%%'` - outputname="$tmpdir/$file" - # Replace the output file specification. - relink_command=`$echo "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'` + # Now create the wrapper script. + func_verbose "creating $output" - $show "$relink_command" - if $run eval "$relink_command"; then : - else - $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 - ${rm}r "$tmpdir" - continue - fi - file="$outputname" - else - $echo "$modename: warning: cannot relink \`$file'" 1>&2 - fi + # Quote the relink command for shipping. + if test -n "$relink_command"; then + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" else - # Install the binary that we compiled earlier. - file=`$echo "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"` + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi - fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"` + fi - # remove .exe since cygwin /usr/bin/install will append another - # one anyway - case $install_prog,$host in - */usr/bin/install*,*cygwin*) - case $file:$destfile in - *.exe:*.exe) - # this is ok - ;; - *.exe:*) - destfile=$destfile.exe - ;; - *:*.exe) - destfile=`$echo $destfile | ${SED} -e 's,.exe$,,'` - ;; - esac - ;; + # Quote $ECHO for shipping. + if test "X$ECHO" = "X$SHELL $progpath --fallback-echo"; then + case $progpath in + [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";; + *) qecho="$SHELL `pwd`/$progpath --fallback-echo";; + esac + qecho=`$ECHO "X$qecho" | $Xsed -e "$sed_quote_subst"` + else + qecho=`$ECHO "X$ECHO" | $Xsed -e "$sed_quote_subst"` + fi + + # Only actually do things if not in dry run mode. + $opt_dry_run || { + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) func_stripname '' '.exe' "$output" + output=$func_stripname_result ;; esac - $show "$install_prog$stripme $file $destfile" - $run eval "$install_prog\$stripme \$file \$destfile" || exit $? - test -n "$outputname" && ${rm}r "$tmpdir" - ;; - esac - done + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + func_stripname '' '.exe' "$outputname" + outputname=$func_stripname_result ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + func_dirname_and_basename "$output" "" "." + output_name=$func_basename_result + output_path=$func_dirname_result + cwrappersource="$output_path/$objdir/lt-$output_name.c" + cwrapper="$output_path/$output_name.exe" + $RM $cwrappersource $cwrapper + trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 + + func_emit_cwrapperexe_src > $cwrappersource + + # The wrapper executable is built using the $host compiler, + # because it contains $host paths and files. If cross- + # compiling, it, like the target executable, must be + # executed on the $host or under an emulation environment. + $opt_dry_run || { + $LTCC $LTCFLAGS -o $cwrapper $cwrappersource + $STRIP $cwrapper + } - for file in $staticlibs; do - name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + # Now, create the wrapper script for func_source use: + func_ltwrapper_scriptname $cwrapper + $RM $func_ltwrapper_scriptname_result + trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 + $opt_dry_run || { + # note: this script will not be executed, so do not chmod. + if test "x$build" = "x$host" ; then + $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result + else + func_emit_wrapper no > $func_ltwrapper_scriptname_result + fi + } + ;; + * ) + $RM $output + trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 - # Set up the ranlib parameters. - oldlib="$destdir/$name" + func_emit_wrapper no > $output + chmod +x $output + ;; + esac + } + exit $EXIT_SUCCESS + ;; + esac - $show "$install_prog $file $oldlib" - $run eval "$install_prog \$file \$oldlib" || exit $? + # See if we need to build an old-fashioned archive. + for oldlib in $oldlibs; do - if test -n "$stripme" && test -n "$old_striplib"; then - $show "$old_striplib $oldlib" - $run eval "$old_striplib $oldlib" || exit $? + if test "$build_libtool_libs" = convenience; then + oldobjs="$libobjs_save $symfileobj" + addlibs="$convenience" + build_libtool_libs=no + else + if test "$build_libtool_libs" = module; then + oldobjs="$libobjs_save" + build_libtool_libs=no + else + oldobjs="$old_deplibs $non_pic_objects" + if test "$preload" = yes && test -f "$symfileobj"; then + oldobjs="$oldobjs $symfileobj" + fi + fi + addlibs="$old_convenience" fi - # Do each command in the postinstall commands. - cmds=$old_postinstall_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || exit $? - done - IFS="$save_ifs" - done + if test -n "$addlibs"; then + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" - if test -n "$future_libdirs"; then - $echo "$modename: warning: remember to run \`$progname --finish$future_libdirs'" 1>&2 - fi + func_extract_archives $gentop $addlibs + oldobjs="$oldobjs $func_extract_archives_result" + fi - if test -n "$current_libdirs"; then - # Maybe just do a dry run. - test -n "$run" && current_libdirs=" -n$current_libdirs" - exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' - else - exit $EXIT_SUCCESS - fi - ;; + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then + cmds=$old_archive_from_new_cmds + else - # libtool finish mode - finish) - modename="$modename: finish" - libdirs="$nonopt" - admincmds= + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" - if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then - for dir - do - libdirs="$libdirs $dir" - done + func_extract_archives $gentop $dlprefiles + oldobjs="$oldobjs $func_extract_archives_result" + fi - for libdir in $libdirs; do - if test -n "$finish_cmds"; then - # Do each command in the finish commands. - cmds=$finish_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" || admincmds="$admincmds - $cmd" + # POSIX demands no paths to be encoded in archives. We have + # to avoid creating archives with duplicate basenames if we + # might have to extract them afterwards, e.g., when creating a + # static archive out of a convenience library, or when linking + # the entirety of a libtool archive into another (currently + # not supported by libtool). + if (for obj in $oldobjs + do + func_basename "$obj" + $ECHO "$func_basename_result" + done | sort | sort -uc >/dev/null 2>&1); then + : + else + $ECHO "copying selected object files to avoid basename conflicts..." + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + func_mkdir_p "$gentop" + save_oldobjs=$oldobjs + oldobjs= + counter=1 + for obj in $save_oldobjs + do + func_basename "$obj" + objbase="$func_basename_result" + case " $oldobjs " in + " ") oldobjs=$obj ;; + *[\ /]"$objbase "*) + while :; do + # Make sure we don't pick an alternate name that also + # overlaps. + newobj=lt$counter-$objbase + func_arith $counter + 1 + counter=$func_arith_result + case " $oldobjs " in + *[\ /]"$newobj "*) ;; + *) if test ! -f "$gentop/$newobj"; then break; fi ;; + esac + done + func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" + oldobjs="$oldobjs $gentop/$newobj" + ;; + *) oldobjs="$oldobjs $obj" ;; + esac done - IFS="$save_ifs" - fi - if test -n "$finish_eval"; then - # Do the single finish_eval. - eval cmds=\"$finish_eval\" - $run eval "$cmds" || admincmds="$admincmds - $cmds" fi - done - fi - - # Exit here if they wanted silent mode. - test "$show" = : && exit $EXIT_SUCCESS + eval cmds=\"$old_archive_cmds\" - $echo "X----------------------------------------------------------------------" | $Xsed - $echo "Libraries have been installed in:" - for libdir in $libdirs; do - $echo " $libdir" + func_len " $cmds" + len=$func_len_result + if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + cmds=$old_archive_cmds + else + # the command line is too long to link in one step, link in parts + func_verbose "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + oldobjs= + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + eval test_cmds=\"$old_archive_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + for obj in $save_oldobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + func_append objlist " $obj" + if test "$len" -lt "$max_cmd_len"; then + : + else + # the above command should be used before it gets too long + oldobjs=$objlist + if test "$obj" = "$last_oldobj" ; then + RANLIB=$save_RANLIB + fi + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" + objlist= + len=$len0 + fi + done + RANLIB=$save_RANLIB + oldobjs=$objlist + if test "X$oldobjs" = "X" ; then + eval cmds=\"\$concat_cmds\" + else + eval cmds=\"\$concat_cmds~\$old_archive_cmds\" + fi + fi + fi + func_execute_cmds "$cmds" 'exit $?' done - $echo - $echo "If you ever happen to want to link against installed libraries" - $echo "in a given directory, LIBDIR, you must either use libtool, and" - $echo "specify the full pathname of the library, or use the \`-LLIBDIR'" - $echo "flag during linking and do at least one of the following:" - if test -n "$shlibpath_var"; then - $echo " - add LIBDIR to the \`$shlibpath_var' environment variable" - $echo " during execution" - fi - if test -n "$runpath_var"; then - $echo " - add LIBDIR to the \`$runpath_var' environment variable" - $echo " during linking" - fi - if test -n "$hardcode_libdir_flag_spec"; then - libdir=LIBDIR - eval flag=\"$hardcode_libdir_flag_spec\" - - $echo " - use the \`$flag' linker flag" - fi - if test -n "$admincmds"; then - $echo " - have your system administrator run these commands:$admincmds" - fi - if test -f /etc/ld.so.conf; then - $echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" - fi - $echo - $echo "See any operating system documentation about shared libraries for" - $echo "more information, such as the ld(1) and ld.so(8) manual pages." - $echo "X----------------------------------------------------------------------" | $Xsed - exit $EXIT_SUCCESS - ;; - # libtool execute mode - execute) - modename="$modename: execute" + test -n "$generated" && \ + func_show_eval "${RM}r$generated" - # The first argument is the command name. - cmd="$nonopt" - if test -z "$cmd"; then - $echo "$modename: you must specify a COMMAND" 1>&2 - $echo "$help" - exit $EXIT_FAILURE - fi - - # Handle -dlopen flags immediately. - for file in $execute_dlfiles; do - if test ! -f "$file"; then - $echo "$modename: \`$file' is not a file" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi + # Now create the libtool archive. + case $output in + *.la) + old_library= + test "$build_old_libs" = yes && old_library="$libname.$libext" + func_verbose "creating $output" - dir= - case $file in - *.la) - # Check to see that this really is a libtool archive. - if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" else - $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" fi + done + # Quote the link command for shipping. + relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"` + if test "$hardcode_automatic" = yes ; then + relink_command= + fi - # Read the libtool library. - dlname= - library_names= + # Only create the output if not a dry run. + $opt_dry_run || { + for installed in no yes; do + if test "$installed" = yes; then + if test -z "$install_libdir"; then + break + fi + output="$output_objdir/$outputname"i + # Replace all uninstalled libtool libraries with the installed ones + newdependency_libs= + for deplib in $dependency_libs; do + case $deplib in + *.la) + func_basename "$deplib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + newdependency_libs="$newdependency_libs $libdir/$name" + ;; + *) newdependency_libs="$newdependency_libs $deplib" ;; + esac + done + dependency_libs="$newdependency_libs" + newdlfiles= + + for lib in $dlfiles; do + case $lib in + *.la) + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + newdlfiles="$newdlfiles $libdir/$name" + ;; + *) newdlfiles="$newdlfiles $lib" ;; + esac + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + *.la) + # Only pass preopened files to the pseudo-archive (for + # eventual linking with the app. that links it) if we + # didn't already link the preopened objects directly into + # the library: + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + newdlprefiles="$newdlprefiles $libdir/$name" + ;; + esac + done + dlprefiles="$newdlprefiles" + else + newdlfiles= + for lib in $dlfiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + newdlfiles="$newdlfiles $abs" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + newdlprefiles="$newdlprefiles $abs" + done + dlprefiles="$newdlprefiles" + fi + $RM $output + # place dlname in correct position for cygwin + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;; + esac + $ECHO > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# Please DO NOT delete this file! +# It is necessary for linking the library. - # If there is no directory component, then add one. - case $file in - */* | *\\*) . $file ;; - *) . ./$file ;; - esac +# The name that we can dlopen(3). +dlname='$tdlname' - # Skip this library if it cannot be dlopened. - if test -z "$dlname"; then - # Warn if it was a shared library. - test -n "$library_names" && $echo "$modename: warning: \`$file' was not linked with \`-export-dynamic'" - continue - fi +# Names of this library. +library_names='$library_names' - dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` - test "X$dir" = "X$file" && dir=. +# The name of the static archive. +old_library='$old_library' - if test -f "$dir/$objdir/$dlname"; then - dir="$dir/$objdir" - else - $echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2 - exit $EXIT_FAILURE - fi - ;; +# Linker flags that can not go in dependency_libs. +inherited_linker_flags='$new_inherited_linker_flags' - *.lo) - # Just add the directory containing the .lo file. - dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` - test "X$dir" = "X$file" && dir=. - ;; +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' - *) - $echo "$modename: warning \`-dlopen' is ignored for non-libtool libraries and objects" 1>&2 - continue - ;; - esac +# Names of additional weak libraries provided by this library +weak_library_names='$weak_libs' - # Get the absolute pathname. - absdir=`cd "$dir" && pwd` - test -n "$absdir" && dir="$absdir" +# Version information for $libname. +current=$current +age=$age +revision=$revision - # Now add the directory to shlibpath_var. - if eval "test -z \"\$$shlibpath_var\""; then - eval "$shlibpath_var=\"\$dir\"" - else - eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" - fi - done +# Is this an already installed library? +installed=$installed - # This variable tells wrapper scripts just to set shlibpath_var - # rather than running their programs. - libtool_execute_magic="$magic" +# Should we warn about portability when linking against -modules? +shouldnotlink=$module - # Check if any of the arguments is a wrapper script. - args= - for file - do - case $file in - -*) ;; - *) - # Do a test to see if this is really a libtool program. - if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - # If there is no directory component, then add one. - case $file in - */* | *\\*) . $file ;; - *) . ./$file ;; - esac +# Files to dlopen/dlpreopen +dlopen='$dlfiles' +dlpreopen='$dlprefiles' - # Transform arg to wrapped name. - file="$progdir/$program" - fi - ;; - esac - # Quote arguments (to preserve shell metacharacters). - file=`$echo "X$file" | $Xsed -e "$sed_quote_subst"` - args="$args \"$file\"" - done +# Directory that this library needs to be installed in: +libdir='$install_libdir'" + if test "$installed" = no && test "$need_relink" = yes; then + $ECHO >> $output "\ +relink_command=\"$relink_command\"" + fi + done + } - if test -z "$run"; then - if test -n "$shlibpath_var"; then - # Export the shlibpath_var. - eval "export $shlibpath_var" - fi + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' + ;; + esac + exit $EXIT_SUCCESS +} - # Restore saved environment variables - if test "${save_LC_ALL+set}" = set; then - LC_ALL="$save_LC_ALL"; export LC_ALL - fi - if test "${save_LANG+set}" = set; then - LANG="$save_LANG"; export LANG - fi +{ test "$mode" = link || test "$mode" = relink; } && + func_mode_link ${1+"$@"} - # Now prepare to actually exec the command. - exec_cmd="\$cmd$args" - else - # Display what would be done. - if test -n "$shlibpath_var"; then - eval "\$echo \"\$shlibpath_var=\$$shlibpath_var\"" - $echo "export $shlibpath_var" - fi - $echo "$cmd$args" - exit $EXIT_SUCCESS - fi - ;; - # libtool clean and uninstall mode - clean | uninstall) - modename="$modename: $mode" - rm="$nonopt" +# func_mode_uninstall arg... +func_mode_uninstall () +{ + $opt_debug + RM="$nonopt" files= rmforce= exit_status=0 @@ -6448,30 +8202,28 @@ for arg do case $arg in - -f) rm="$rm $arg"; rmforce=yes ;; - -*) rm="$rm $arg" ;; + -f) RM="$RM $arg"; rmforce=yes ;; + -*) RM="$RM $arg" ;; *) files="$files $arg" ;; esac done - if test -z "$rm"; then - $echo "$modename: you must specify an RM program" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - fi + test -z "$RM" && \ + func_fatal_help "you must specify an RM program" rmdirs= origobjdir="$objdir" for file in $files; do - dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` - if test "X$dir" = "X$file"; then - dir=. + func_dirname "$file" "" "." + dir="$func_dirname_result" + if test "X$dir" = X.; then objdir="$origobjdir" else objdir="$dir/$origobjdir" fi - name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + func_basename "$file" + name="$func_basename_result" test "$mode" = uninstall && objdir="$dir" # Remember objdir for removal later, being careful to avoid duplicates @@ -6483,9 +8235,9 @@ fi # Don't error if the file doesn't exist and rm -f was used. - if (test -L "$file") >/dev/null 2>&1 \ - || (test -h "$file") >/dev/null 2>&1 \ - || test -f "$file"; then + if { test -L "$file"; } >/dev/null 2>&1 || + { test -h "$file"; } >/dev/null 2>&1 || + test -f "$file"; then : elif test -d "$file"; then exit_status=1 @@ -6499,8 +8251,8 @@ case $name in *.la) # Possibly a libtool archive, so verify it. - if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - . $dir/$name + if func_lalib_p "$file"; then + func_source $dir/$name # Delete the libtool libraries and symlinks. for n in $library_names; do @@ -6515,39 +8267,17 @@ *" $dlname "*) ;; *) rmfiles="$rmfiles $objdir/$dlname" ;; esac - test -n "$libdir" && rmfiles="$rmfiles $objdir/$name $objdir/${name}i" + test -n "$libdir" && rmfiles="$rmfiles $objdir/$name $objdir/${name}i" ;; uninstall) if test -n "$library_names"; then # Do each command in the postuninstall commands. - cmds=$postuninstall_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" - if test "$?" -ne 0 && test "$rmforce" != yes; then - exit_status=1 - fi - done - IFS="$save_ifs" + func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' fi if test -n "$old_library"; then # Do each command in the old_postuninstall commands. - cmds=$old_postuninstall_cmds - save_ifs="$IFS"; IFS='~' - for cmd in $cmds; do - IFS="$save_ifs" - eval cmd=\"$cmd\" - $show "$cmd" - $run eval "$cmd" - if test "$?" -ne 0 && test "$rmforce" != yes; then - exit_status=1 - fi - done - IFS="$save_ifs" + func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' fi # FIXME: should reinstall the best remaining shared library. ;; @@ -6557,20 +8287,20 @@ *.lo) # Possibly a libtool object, so verify it. - if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + if func_lalib_p "$file"; then # Read the .lo file - . $dir/$name + func_source $dir/$name # Add PIC object to the list of files to remove. - if test -n "$pic_object" \ - && test "$pic_object" != none; then + if test -n "$pic_object" && + test "$pic_object" != none; then rmfiles="$rmfiles $dir/$pic_object" fi # Add non-PIC object to the list of files to remove. - if test -n "$non_pic_object" \ - && test "$non_pic_object" != none; then + if test -n "$non_pic_object" && + test "$non_pic_object" != none; then rmfiles="$rmfiles $dir/$non_pic_object" fi fi @@ -6581,17 +8311,26 @@ noexename=$name case $file in *.exe) - file=`$echo $file|${SED} 's,.exe$,,'` - noexename=`$echo $name|${SED} 's,.exe$,,'` + func_stripname '' '.exe' "$file" + file=$func_stripname_result + func_stripname '' '.exe' "$name" + noexename=$func_stripname_result # $file with .exe has already been added to rmfiles, # add $file without .exe rmfiles="$rmfiles $file" ;; esac # Do a test to see if this is a libtool program. - if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then - relink_command= - . $dir/$noexename + if func_ltwrapper_p "$file"; then + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + relink_command= + func_source $func_ltwrapper_scriptname_result + rmfiles="$rmfiles $func_ltwrapper_scriptname_result" + else + relink_command= + func_source $dir/$noexename + fi # note $name still contains .exe if it was in $file originally # as does the version of $file that was added into $rmfiles @@ -6606,237 +8345,38 @@ fi ;; esac - $show "$rm $rmfiles" - $run $rm $rmfiles || exit_status=1 + func_show_eval "$RM $rmfiles" 'exit_status=1' done objdir="$origobjdir" # Try to remove the ${objdir}s in the directories where we deleted files for dir in $rmdirs; do if test -d "$dir"; then - $show "rmdir $dir" - $run rmdir $dir >/dev/null 2>&1 + func_show_eval "rmdir $dir >/dev/null 2>&1" fi done exit $exit_status - ;; +} - "") - $echo "$modename: you must specify a MODE" 1>&2 - $echo "$generic_help" 1>&2 - exit $EXIT_FAILURE - ;; - esac +{ test "$mode" = uninstall || test "$mode" = clean; } && + func_mode_uninstall ${1+"$@"} - if test -z "$exec_cmd"; then - $echo "$modename: invalid operation mode \`$mode'" 1>&2 - $echo "$generic_help" 1>&2 - exit $EXIT_FAILURE - fi -fi # test -z "$show_help" +test -z "$mode" && { + help="$generic_help" + func_fatal_help "you must specify a MODE" +} + +test -z "$exec_cmd" && \ + func_fatal_help "invalid operation mode \`$mode'" if test -n "$exec_cmd"; then - eval exec $exec_cmd + eval exec "$exec_cmd" exit $EXIT_FAILURE fi -# We need to display help for each of the modes. -case $mode in -"") $echo \ -"Usage: $modename [OPTION]... [MODE-ARG]... - -Provide generalized library-building support services. - - --config show all configuration variables - --debug enable verbose shell tracing --n, --dry-run display commands without modifying any files - --features display basic configuration information and exit - --finish same as \`--mode=finish' - --help display this help message and exit - --mode=MODE use operation mode MODE [default=inferred from MODE-ARGS] - --quiet same as \`--silent' - --silent don't print informational messages - --tag=TAG use configuration variables from tag TAG - --version print version information - -MODE must be one of the following: - - clean remove files from the build directory - compile compile a source file into a libtool object - execute automatically set library path, then run a program - finish complete the installation of libtool libraries - install install libraries or executables - link create a library or an executable - uninstall remove libraries from an installed directory - -MODE-ARGS vary depending on the MODE. Try \`$modename --help --mode=MODE' for -a more detailed description of MODE. - -Report bugs to ." - exit $EXIT_SUCCESS - ;; - -clean) - $echo \ -"Usage: $modename [OPTION]... --mode=clean RM [RM-OPTION]... FILE... - -Remove files from the build directory. - -RM is the name of the program to use to delete files associated with each FILE -(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed -to RM. - -If FILE is a libtool library, object or program, all the files associated -with it are deleted. Otherwise, only FILE itself is deleted using RM." - ;; - -compile) - $echo \ -"Usage: $modename [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE - -Compile a source file into a libtool library object. - -This mode accepts the following additional options: - - -o OUTPUT-FILE set the output file name to OUTPUT-FILE - -prefer-pic try to building PIC objects only - -prefer-non-pic try to building non-PIC objects only - -static always build a \`.o' file suitable for static linking - -COMPILE-COMMAND is a command to be used in creating a \`standard' object file -from the given SOURCEFILE. - -The output file name is determined by removing the directory component from -SOURCEFILE, then substituting the C source code suffix \`.c' with the -library object suffix, \`.lo'." - ;; - -execute) - $echo \ -"Usage: $modename [OPTION]... --mode=execute COMMAND [ARGS]... - -Automatically set library path, then run a program. - -This mode accepts the following additional options: - - -dlopen FILE add the directory containing FILE to the library path - -This mode sets the library path environment variable according to \`-dlopen' -flags. - -If any of the ARGS are libtool executable wrappers, then they are translated -into their corresponding uninstalled binary, and any of their required library -directories are added to the library path. - -Then, COMMAND is executed, with ARGS as arguments." - ;; - -finish) - $echo \ -"Usage: $modename [OPTION]... --mode=finish [LIBDIR]... - -Complete the installation of libtool libraries. - -Each LIBDIR is a directory that contains libtool libraries. - -The commands that this mode executes may require superuser privileges. Use -the \`--dry-run' option if you just want to see what would be executed." - ;; - -install) - $echo \ -"Usage: $modename [OPTION]... --mode=install INSTALL-COMMAND... - -Install executables or libraries. - -INSTALL-COMMAND is the installation command. The first component should be -either the \`install' or \`cp' program. - -The rest of the components are interpreted as arguments to that command (only -BSD-compatible install options are recognized)." - ;; - -link) - $echo \ -"Usage: $modename [OPTION]... --mode=link LINK-COMMAND... - -Link object files or libraries together to form another library, or to -create an executable program. - -LINK-COMMAND is a command using the C compiler that you would use to create -a program from several object files. - -The following components of LINK-COMMAND are treated specially: - - -all-static do not do any dynamic linking at all - -avoid-version do not add a version suffix if possible - -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime - -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols - -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) - -export-symbols SYMFILE - try to export only the symbols listed in SYMFILE - -export-symbols-regex REGEX - try to export only the symbols matching REGEX - -LLIBDIR search LIBDIR for required installed libraries - -lNAME OUTPUT-FILE requires the installed library libNAME - -module build a library that can dlopened - -no-fast-install disable the fast-install mode - -no-install link a not-installable executable - -no-undefined declare that a library does not refer to external symbols - -o OUTPUT-FILE create OUTPUT-FILE from the specified objects - -objectlist FILE Use a list of object files found in FILE to specify objects - -precious-files-regex REGEX - don't remove output files matching REGEX - -release RELEASE specify package release information - -rpath LIBDIR the created library will eventually be installed in LIBDIR - -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries - -static do not do any dynamic linking of libtool libraries - -version-info CURRENT[:REVISION[:AGE]] - specify library version info [each variable defaults to 0] - -All other options (arguments beginning with \`-') are ignored. - -Every other argument is treated as a filename. Files ending in \`.la' are -treated as uninstalled libtool libraries, other files are standard or library -object files. - -If the OUTPUT-FILE ends in \`.la', then a libtool library is created, -only library objects (\`.lo' files) may be specified, and \`-rpath' is -required, except when creating a convenience library. - -If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created -using \`ar' and \`ranlib', or on Windows using \`lib'. - -If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file -is created, otherwise an executable program is created." - ;; - -uninstall) - $echo \ -"Usage: $modename [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... - -Remove libraries from an installation directory. - -RM is the name of the program to use to delete files associated with each FILE -(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed -to RM. +exit $exit_status -If FILE is a libtool library, all the files associated with it are deleted. -Otherwise, only FILE itself is deleted using RM." - ;; - -*) - $echo "$modename: invalid operation mode \`$mode'" 1>&2 - $echo "$help" 1>&2 - exit $EXIT_FAILURE - ;; -esac - -$echo -$echo "Try \`$modename --help' for more information about other modes." - -exit $? # The TAGs below are defined such that we never get into a situation # in which we disable both kinds of libraries. Given conflicting @@ -6850,14 +8390,17 @@ # configuration. But we'll never go from static-only to shared-only. # ### BEGIN LIBTOOL TAG CONFIG: disable-shared -disable_libs=shared +build_libtool_libs=no +build_old_libs=yes # ### END LIBTOOL TAG CONFIG: disable-shared # ### BEGIN LIBTOOL TAG CONFIG: disable-static -disable_libs=static +build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` # ### END LIBTOOL TAG CONFIG: disable-static # Local Variables: # mode:shell-script # sh-indentation:2 # End: +# vi:sw=2 + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/configure firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/configure --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/configure 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/configure 2010-04-16 17:32:47.000000000 +0100 @@ -1,62 +1,85 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.61 for breakpad 0.1. +# Generated by GNU Autoconf 2.65 for breakpad 0.1. # # Report bugs to . # +# # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, -# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +# 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +# Inc. +# +# # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. -## --------------------- ## -## M4sh Initialization. ## -## --------------------- ## +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else - case `(set -o) 2>/dev/null` in - *posix*) set -o posix ;; + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; esac - fi - - -# PATH needs CR -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits - -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - echo "#! /bin/sh" >conf$$.sh - echo "exit 0" >>conf$$.sh - chmod +x conf$$.sh - if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then - PATH_SEPARATOR=';' +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' else - PATH_SEPARATOR=: + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' fi - rm -f conf$$.sh + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' fi -# Support unset when possible. -if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then - as_unset=unset -else - as_unset=false +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } fi @@ -65,20 +88,18 @@ # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) -as_nl=' -' IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. -case $0 in +case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break -done + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done IFS=$as_save_IFS ;; @@ -89,32 +110,271 @@ as_myself=$0 fi if test ! -f "$as_myself"; then - echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - { (exit 1); exit 1; } + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 fi -# Work around bugs in pre-3.0 UWIN ksh. -for as_var in ENV MAIL MAILPATH -do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. -for as_var in \ - LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ - LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ - LC_TELEPHONE LC_TIME +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do - if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then - eval $as_var=C; export $as_var + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + # We cannot yet assume a decent shell, so we have to provide a + # neutralization value for shells without unset; and this also + # works around shells that cannot unset nonexistent variables. + BASH_ENV=/dev/null + ENV=/dev/null + (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV + export CONFIG_SHELL + exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." else - ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var + $as_echo "$0: Please tell bug-autoconf@gnu.org and +$0: opensource@google.com about your system, including any +$0: error possibly output before this message. Then install +$0: a modern shell, or manually run the script under such a +$0: shell if you do have one." fi -done + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error ERROR [LINENO LOG_FD] +# --------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with status $?, using 1 if that was 0. +as_fn_error () +{ + as_status=$?; test $as_status -eq 0 && as_status=1 + if test "$3"; then + as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3 + fi + $as_echo "$as_me: error: $1" >&2 + as_fn_exit $as_status +} # as_fn_error -# Required to use basename. if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr @@ -128,13 +388,17 @@ as_basename=false fi +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi -# Name of the executable. as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || -echo X/"$0" | +$as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q @@ -149,428 +413,142 @@ } s/.*/./; q'` -# CDPATH. -$as_unset CDPATH +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits -if test "x$CONFIG_SHELL" = x; then - if (eval ":") 2>/dev/null; then - as_have_required=yes -else - as_have_required=no -fi + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } - if test $as_have_required = yes && (eval ": -(as_func_return () { - (exit \$1) -} -as_func_success () { - as_func_return 0 -} -as_func_failure () { - as_func_return 1 -} -as_func_ret_success () { - return 0 -} -as_func_ret_failure () { - return 1 + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit } -exitcode=0 -if as_func_success; then - : -else - exitcode=1 - echo as_func_success failed. -fi +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac -if as_func_failure; then - exitcode=1 - echo as_func_failure succeeded. +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null fi - -if as_func_ret_success; then - : +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi else - exitcode=1 - echo as_func_ret_success failed. + as_ln_s='cp -p' fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null -if as_func_ret_failure; then - exitcode=1 - echo as_func_ret_failure succeeded. +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false fi -if ( set x; as_func_ret_success y && test x = \"\$1\" ); then - : +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' else - exitcode=1 - echo positional parameters were not saved. + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' fi +as_executable_p=$as_test_x -test \$exitcode = 0) || { (exit 1); exit 1; } - -( - as_lineno_1=\$LINENO - as_lineno_2=\$LINENO - test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" && - test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; } -") 2> /dev/null; then - : -else - as_candidate_shells= - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - case $as_dir in - /*) - for as_base in sh bash ksh sh5; do - as_candidate_shells="$as_candidate_shells $as_dir/$as_base" - done;; - esac -done -IFS=$as_save_IFS +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - for as_shell in $as_candidate_shells $SHELL; do - # Try only shells that exist, to save several forks. - if { test -f "$as_shell" || test -f "$as_shell.exe"; } && - { ("$as_shell") 2> /dev/null <<\_ASEOF -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in - *posix*) set -o posix ;; -esac -fi +# Check that we are running under the correct shell. +SHELL=${CONFIG_SHELL-/bin/sh} -: -_ASEOF -}; then - CONFIG_SHELL=$as_shell - as_have_required=yes - if { "$as_shell" 2> /dev/null <<\_ASEOF -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in - *posix*) set -o posix ;; +case X$lt_ECHO in +X*--fallback-echo) + # Remove one level of quotation (which was required for Make). + ECHO=`echo "$lt_ECHO" | sed 's,\\\\\$\\$0,'$0','` + ;; esac -fi - - -: -(as_func_return () { - (exit $1) -} -as_func_success () { - as_func_return 0 -} -as_func_failure () { - as_func_return 1 -} -as_func_ret_success () { - return 0 -} -as_func_ret_failure () { - return 1 -} - -exitcode=0 -if as_func_success; then - : -else - exitcode=1 - echo as_func_success failed. -fi - -if as_func_failure; then - exitcode=1 - echo as_func_failure succeeded. -fi - -if as_func_ret_success; then - : -else - exitcode=1 - echo as_func_ret_success failed. -fi - -if as_func_ret_failure; then - exitcode=1 - echo as_func_ret_failure succeeded. -fi - -if ( set x; as_func_ret_success y && test x = "$1" ); then - : -else - exitcode=1 - echo positional parameters were not saved. -fi - -test $exitcode = 0) || { (exit 1); exit 1; } - -( - as_lineno_1=$LINENO - as_lineno_2=$LINENO - test "x$as_lineno_1" != "x$as_lineno_2" && - test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; } - -_ASEOF -}; then - break -fi - -fi - - done - - if test "x$CONFIG_SHELL" != x; then - for as_var in BASH_ENV ENV - do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var - done - export CONFIG_SHELL - exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"} -fi - - - if test $as_have_required = no; then - echo This script requires a shell more modern than all the - echo shells that I found on your system. Please install a - echo modern shell, or manually run the script under such a - echo shell if you do have one. - { (exit 1); exit 1; } -fi - - -fi - -fi - - - -(eval "as_func_return () { - (exit \$1) -} -as_func_success () { - as_func_return 0 -} -as_func_failure () { - as_func_return 1 -} -as_func_ret_success () { - return 0 -} -as_func_ret_failure () { - return 1 -} - -exitcode=0 -if as_func_success; then - : -else - exitcode=1 - echo as_func_success failed. -fi - -if as_func_failure; then - exitcode=1 - echo as_func_failure succeeded. -fi - -if as_func_ret_success; then - : -else - exitcode=1 - echo as_func_ret_success failed. -fi - -if as_func_ret_failure; then - exitcode=1 - echo as_func_ret_failure succeeded. -fi - -if ( set x; as_func_ret_success y && test x = \"\$1\" ); then - : -else - exitcode=1 - echo positional parameters were not saved. -fi - -test \$exitcode = 0") || { - echo No shell found that supports shell functions. - echo Please tell autoconf@gnu.org about your system, - echo including any error possibly output before this - echo message -} - - - - as_lineno_1=$LINENO - as_lineno_2=$LINENO - test "x$as_lineno_1" != "x$as_lineno_2" && - test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { - - # Create $as_me.lineno as a copy of $as_myself, but with $LINENO - # uniformly replaced by the line number. The first 'sed' inserts a - # line-number line after each line using $LINENO; the second 'sed' - # does the real work. The second script uses 'N' to pair each - # line-number line with the line containing $LINENO, and appends - # trailing '-' during substitution so that $LINENO is not a special - # case at line end. - # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the - # scripts with optimization help from Paolo Bonzini. Blame Lee - # E. McMahon (1931-1989) for sed's syntax. :-) - sed -n ' - p - /[$]LINENO/= - ' <$as_myself | - sed ' - s/[$]LINENO.*/&-/ - t lineno - b - :lineno - N - :loop - s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ - t loop - s/-\n.*// - ' >$as_me.lineno && - chmod +x "$as_me.lineno" || - { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 - { (exit 1); exit 1; }; } - - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensitive to this). - . "./$as_me.lineno" - # Exit status is that of the last command. - exit -} - - -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi - -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in --n*) - case `echo 'x\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - *) ECHO_C='\c';; - esac;; -*) - ECHO_N='-n';; -esac - -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi - -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir -fi -echo >conf$$.file -if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -p'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -p' -elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln -else - as_ln_s='cp -p' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null - -if mkdir -p . 2>/dev/null; then - as_mkdir_p=: -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi - -if test -x / >/dev/null 2>&1; then - as_test_x='test -x' -else - if ls -dL / >/dev/null 2>&1; then - as_ls_L_option=L - else - as_ls_L_option= - fi - as_test_x=' - eval sh -c '\'' - if test -d "$1"; then - test -d "$1/."; - else - case $1 in - -*)set "./$1";; - esac; - case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in - ???[sx]*):;;*)false;;esac;fi - '\'' sh - ' -fi -as_executable_p=$as_test_x - -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" - -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" - - - - -# Check that we are running under the correct shell. -SHELL=${CONFIG_SHELL-/bin/sh} - -case X$ECHO in -X*--fallback-echo) - # Remove one level of quotation (which was required for Make). - ECHO=`echo "$ECHO" | sed 's,\\\\\$\\$0,'$0','` - ;; -esac - -echo=${ECHO-echo} +ECHO=${lt_ECHO-echo} if test "X$1" = X--no-reexec; then # Discard the --no-reexec flag, and continue. shift elif test "X$1" = X--fallback-echo; then # Avoid inline document here, it may be left over : -elif test "X`($echo '\t') 2>/dev/null`" = 'X\t' ; then - # Yippee, $echo works! +elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then + # Yippee, $ECHO works! : else # Restart under the correct shell. @@ -580,9 +558,9 @@ if test "X$1" = X--fallback-echo; then # used as fallback echo shift - cat </dev/null 2>&1 && unset CDPATH -if test -z "$ECHO"; then -if test "X${echo_test_string+set}" != Xset; then -# find a string as large as possible, as long as the shell can cope with it - for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do - # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ... - if (echo_test_string=`eval $cmd`) 2>/dev/null && - echo_test_string=`eval $cmd` && - (test "X$echo_test_string" = "X$echo_test_string") 2>/dev/null - then - break - fi - done -fi +if test -z "$lt_ECHO"; then + if test "X${echo_test_string+set}" != Xset; then + # find a string as large as possible, as long as the shell can cope with it + for cmd in 'sed 50q "$0"' 'sed 20q "$0"' 'sed 10q "$0"' 'sed 2q "$0"' 'echo test'; do + # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ... + if { echo_test_string=`eval $cmd`; } 2>/dev/null && + { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null + then + break + fi + done + fi -if test "X`($echo '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - : -else - # The Solaris, AIX, and Digital Unix default echo programs unquote - # backslashes. This makes it impossible to quote backslashes using - # echo "$something" | sed 's/\\/\\\\/g' - # - # So, first we look for a working echo in the user's PATH. + if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && + echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + : + else + # The Solaris, AIX, and Digital Unix default echo programs unquote + # backslashes. This makes it impossible to quote backslashes using + # echo "$something" | sed 's/\\/\\\\/g' + # + # So, first we look for a working echo in the user's PATH. - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for dir in $PATH /usr/ucb; do + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for dir in $PATH /usr/ucb; do + IFS="$lt_save_ifs" + if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && + test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && + echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + ECHO="$dir/echo" + break + fi + done IFS="$lt_save_ifs" - if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && - test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - echo="$dir/echo" - break - fi - done - IFS="$lt_save_ifs" - if test "X$echo" = Xecho; then - # We didn't find a better echo, so look for alternatives. - if test "X`(print -r '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`(print -r "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - # This shell has a builtin print -r that does the trick. - echo='print -r' - elif (test -f /bin/ksh || test -f /bin/ksh$ac_exeext) && - test "X$CONFIG_SHELL" != X/bin/ksh; then - # If we have ksh, try running configure again with it. - ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} - export ORIGINAL_CONFIG_SHELL - CONFIG_SHELL=/bin/ksh - export CONFIG_SHELL - exec $CONFIG_SHELL "$0" --no-reexec ${1+"$@"} - else - # Try using printf. - echo='printf %s\n' - if test "X`($echo '\t') 2>/dev/null`" = 'X\t' && - echo_testing_string=`($echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - # Cool, printf works - : - elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` && - test "X$echo_testing_string" = 'X\t' && - echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL - export CONFIG_SHELL - SHELL="$CONFIG_SHELL" - export SHELL - echo="$CONFIG_SHELL $0 --fallback-echo" - elif echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` && - test "X$echo_testing_string" = 'X\t' && - echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` && - test "X$echo_testing_string" = "X$echo_test_string"; then - echo="$CONFIG_SHELL $0 --fallback-echo" + if test "X$ECHO" = Xecho; then + # We didn't find a better echo, so look for alternatives. + if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' && + echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + # This shell has a builtin print -r that does the trick. + ECHO='print -r' + elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } && + test "X$CONFIG_SHELL" != X/bin/ksh; then + # If we have ksh, try running configure again with it. + ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} + export ORIGINAL_CONFIG_SHELL + CONFIG_SHELL=/bin/ksh + export CONFIG_SHELL + exec $CONFIG_SHELL "$0" --no-reexec ${1+"$@"} else - # maybe with a smaller string... - prev=: + # Try using printf. + ECHO='printf %s\n' + if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && + echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + # Cool, printf works + : + elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` && + test "X$echo_testing_string" = 'X\t' && + echo_testing_string=`($ORIGINAL_CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL + export CONFIG_SHELL + SHELL="$CONFIG_SHELL" + export SHELL + ECHO="$CONFIG_SHELL $0 --fallback-echo" + elif echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo '\t') 2>/dev/null` && + test "X$echo_testing_string" = 'X\t' && + echo_testing_string=`($CONFIG_SHELL "$0" --fallback-echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + ECHO="$CONFIG_SHELL $0 --fallback-echo" + else + # maybe with a smaller string... + prev=: - for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do - if (test "X$echo_test_string" = "X`eval $cmd`") 2>/dev/null - then - break - fi - prev="$cmd" - done + for cmd in 'echo test' 'sed 2q "$0"' 'sed 10q "$0"' 'sed 20q "$0"' 'sed 50q "$0"'; do + if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null + then + break + fi + prev="$cmd" + done - if test "$prev" != 'sed 50q "$0"'; then - echo_test_string=`eval $prev` - export echo_test_string - exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "$0" ${1+"$@"} - else - # Oops. We lost completely, so just stick with echo. - echo=echo - fi + if test "$prev" != 'sed 50q "$0"'; then + echo_test_string=`eval $prev` + export echo_test_string + exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "$0" ${1+"$@"} + else + # Oops. We lost completely, so just stick with echo. + ECHO=echo + fi + fi fi fi fi fi -fi # Copy echo and quote the copy suitably for passing to libtool from # the Makefile, instead of quoting the original, which is used later. -ECHO=$echo -if test "X$ECHO" = "X$CONFIG_SHELL $0 --fallback-echo"; then - ECHO="$CONFIG_SHELL \\\$\$0 --fallback-echo" +lt_ECHO=$ECHO +if test "X$lt_ECHO" = "X$CONFIG_SHELL $0 --fallback-echo"; then + lt_ECHO="$CONFIG_SHELL \\\$\$0 --fallback-echo" fi -tagnames=${tagnames+${tagnames},}CXX - -tagnames=${tagnames+${tagnames},}F77 - -exec 7<&0 &1 +test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, Linux) returns a bogus exit status, @@ -723,7 +697,6 @@ subdirs= MFLAGS= MAKEFLAGS= -SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='breakpad' @@ -731,6 +704,7 @@ PACKAGE_VERSION='0.1' PACKAGE_STRING='breakpad 0.1' PACKAGE_BUGREPORT='opensource@google.com' +PACKAGE_URL='' ac_unique_file="README" # Factoring default headers for most tests. @@ -769,113 +743,142 @@ # include #endif" -ac_subst_vars='SHELL -PATH_SEPARATOR -PACKAGE_NAME -PACKAGE_TARNAME -PACKAGE_VERSION -PACKAGE_STRING -PACKAGE_BUGREPORT -exec_prefix -prefix -program_transform_name -bindir -sbindir -libexecdir -datarootdir -datadir -sysconfdir -sharedstatedir -localstatedir -includedir -oldincludedir -docdir -infodir -htmldir -dvidir -pdfdir -psdir -libdir -localedir -mandir -DEFS -ECHO_C -ECHO_N -ECHO_T -LIBS -build_alias -host_alias -target_alias -INSTALL_PROGRAM -INSTALL_SCRIPT -INSTALL_DATA -am__isrc -CYGPATH_W -PACKAGE -VERSION -ACLOCAL -AUTOCONF -AUTOMAKE -AUTOHEADER -MAKEINFO -install_sh -STRIP -INSTALL_STRIP_PROGRAM -mkdir_p -AWK -SET_MAKE -am__leading_dot -AMTAR -am__tar -am__untar -CC -CFLAGS -LDFLAGS -CPPFLAGS -ac_ct_CC -EXEEXT -OBJEXT -DEPDIR -am__include -am__quote -AMDEP_TRUE -AMDEP_FALSE -AMDEPBACKSLASH -CCDEPMODE -am__fastdepCC_TRUE -am__fastdepCC_FALSE -CPP -CXX -CXXFLAGS -ac_ct_CXX -CXXDEPMODE -am__fastdepCXX_TRUE -am__fastdepCXX_FALSE -build -build_cpu -build_vendor -build_os -host -host_cpu -host_vendor -host_os -GREP -EGREP -LN_S -ECHO -AR -RANLIB +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +LIBOBJS +SELFTEST_FALSE +SELFTEST_TRUE +PTHREAD_CFLAGS +PTHREAD_LIBS +PTHREAD_CC +ax_pthread_config +LIBTOOL_DEPS CXXCPP -F77 -FFLAGS -ac_ct_F77 +OTOOL64 +OTOOL +LIPO +NMEDIT +DSYMUTIL +lt_ECHO +RANLIB +AR +OBJDUMP +LN_S +NM +ac_ct_DUMPBIN +DUMPBIN +LD +FGREP +EGREP +GREP +SED +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build LIBTOOL -LIBTOOL_DEPS -SELFTEST_TRUE -SELFTEST_FALSE -LIBOBJS -LTLIBOBJS' +am__fastdepCXX_FALSE +am__fastdepCXX_TRUE +CXXDEPMODE +ac_ct_CXX +CXXFLAGS +CXX +CPP +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__quote +am__include +DEPDIR +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +AWK +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +STRIP +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_dependency_tracking +enable_shared +enable_static +with_pic +enable_fast_install +with_gnu_ld +enable_libtool_lock +enable_m32 +enable_selftest +' ac_precious_vars='build_alias host_alias target_alias @@ -888,14 +891,14 @@ CXX CXXFLAGS CCC -CXXCPP -F77 -FFLAGS' +CXXCPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null @@ -994,13 +997,20 @@ datarootdir=$ac_optarg ;; -disable-* | --disable-*) - ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. - expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid feature name: $ac_feature" >&2 - { (exit 1); exit 1; }; } - ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` - eval enable_$ac_feature=no ;; + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; @@ -1013,13 +1023,20 @@ dvidir=$ac_optarg ;; -enable-* | --enable-*) - ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. - expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid feature name: $ac_feature" >&2 - { (exit 1); exit 1; }; } - ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'` - eval enable_$ac_feature=\$ac_optarg ;; + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ @@ -1210,22 +1227,36 @@ ac_init_version=: ;; -with-* | --with-*) - ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. - expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid package name: $ac_package" >&2 - { (exit 1); exit 1; }; } - ac_package=`echo $ac_package | sed 's/[-.]/_/g'` - eval with_$ac_package=\$ac_optarg ;; + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) - ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'` + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. - expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid package name: $ac_package" >&2 - { (exit 1); exit 1; }; } - ac_package=`echo $ac_package | sed 's/[-.]/_/g'` - eval with_$ac_package=no ;; + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. @@ -1245,25 +1276,25 @@ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; - -*) { echo "$as_me: error: unrecognized option: $ac_option -Try \`$0 --help' for more information." >&2 - { (exit 1); exit 1; }; } + -*) as_fn_error "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information." ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. - expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null && - { echo "$as_me: error: invalid variable name: $ac_envvar" >&2 - { (exit 1); exit 1; }; } + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error "invalid variable name: \`$ac_envvar'" ;; + esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. - echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && - echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option} ;; @@ -1272,23 +1303,36 @@ if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` - { echo "$as_me: error: missing argument to $ac_option" >&2 - { (exit 1); exit 1; }; } + as_fn_error "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac fi -# Be sure to have absolute directory names. +# Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac - { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2 - { (exit 1); exit 1; }; } + as_fn_error "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' @@ -1302,7 +1346,7 @@ if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe - echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. + $as_echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used." >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes @@ -1318,23 +1362,21 @@ ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || - { echo "$as_me: error: Working directory cannot be determined" >&2 - { (exit 1); exit 1; }; } + as_fn_error "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || - { echo "$as_me: error: pwd does not report name of working directory" >&2 - { (exit 1); exit 1; }; } + as_fn_error "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. - ac_confdir=`$as_dirname -- "$0" || -$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$0" : 'X\(//\)[^/]' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -echo X"$0" | + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q @@ -1361,13 +1403,11 @@ fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." - { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2 - { (exit 1); exit 1; }; } + as_fn_error "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( - cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2 - { (exit 1); exit 1; }; } + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then @@ -1415,9 +1455,9 @@ Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX - [$ac_default_prefix] + [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX - [PREFIX] + [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify @@ -1427,25 +1467,25 @@ For better control, use the options below. Fine tuning of the installation directories: - --bindir=DIR user executables [EPREFIX/bin] - --sbindir=DIR system admin executables [EPREFIX/sbin] - --libexecdir=DIR program executables [EPREFIX/libexec] - --sysconfdir=DIR read-only single-machine data [PREFIX/etc] - --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] - --localstatedir=DIR modifiable single-machine data [PREFIX/var] - --libdir=DIR object code libraries [EPREFIX/lib] - --includedir=DIR C header files [PREFIX/include] - --oldincludedir=DIR C header files for non-gcc [/usr/include] - --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] - --datadir=DIR read-only architecture-independent data [DATAROOTDIR] - --infodir=DIR info documentation [DATAROOTDIR/info] - --localedir=DIR locale-dependent data [DATAROOTDIR/locale] - --mandir=DIR man documentation [DATAROOTDIR/man] - --docdir=DIR documentation root [DATAROOTDIR/doc/breakpad] - --htmldir=DIR html documentation [DOCDIR] - --dvidir=DIR dvi documentation [DOCDIR] - --pdfdir=DIR pdf documentation [DOCDIR] - --psdir=DIR ps documentation [DOCDIR] + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/breakpad] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF @@ -1468,6 +1508,7 @@ cat <<\_ACEOF Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --disable-dependency-tracking speeds up one-time build @@ -1477,16 +1518,16 @@ --enable-fast-install[=PKGS] optimize for fast installation [default=yes] --disable-libtool-lock avoid locking (might break parallel builds) + --enable-m32 Compile/build with -m32 (default is no) --enable-selftest Run extra tests with "make check" (may conflict with optimizations) (default is no) Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) - --with-gnu-ld assume the C compiler uses GNU ld [default=no] --with-pic try to use only PIC/non-PIC objects [default=use both] - --with-tags[=TAGS] include additional configurations [automatic] + --with-gnu-ld assume the C compiler uses GNU ld [default=no] Some influential environment variables: CC C compiler command @@ -1494,14 +1535,12 @@ LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l - CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I if + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor CXX C++ compiler command CXXFLAGS C++ compiler flags CXXCPP C++ preprocessor - F77 Fortran 77 compiler command - FFLAGS Fortran 77 compiler flags Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. @@ -1514,15 +1553,17 @@ if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue - test -d "$ac_dir" || continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) - ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; @@ -1558,7 +1599,7 @@ echo && $SHELL "$ac_srcdir/configure" --help=recursive else - echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done @@ -1568,61 +1609,446 @@ if $ac_init_version; then cat <<\_ACEOF breakpad configure 0.1 -generated by GNU Autoconf 2.61 +generated by GNU Autoconf 2.65 -Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, -2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc. +Copyright (C) 2009 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi -cat >config.log <<_ACEOF -This file contains any messages produced by compilers while -running configure, to aid debugging if configure makes a mistake. -It was created by breakpad $as_me 0.1, which was -generated by GNU Autoconf 2.61. Invocation command line was +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 - $ $0 $@ + ac_retval=1 +fi + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + as_fn_set_status $ac_retval -_ACEOF -exec 5>>config.log -{ -cat <<_ASUNAME -## --------- ## -## Platform. ## -## --------- ## +} # ac_fn_c_try_compile -hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` -uname -m = `(uname -m) 2>/dev/null || echo unknown` -uname -r = `(uname -r) 2>/dev/null || echo unknown` -uname -s = `(uname -s) 2>/dev/null || echo unknown` -uname -v = `(uname -v) 2>/dev/null || echo unknown` +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } >/dev/null && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 -/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` -/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + ac_retval=1 +fi + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + as_fn_set_status $ac_retval -/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` -/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` -/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` -/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` -/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` -/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` -/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` +} # ac_fn_c_try_cpp -_ASUNAME +# ac_fn_cxx_try_compile LINENO +# ---------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - echo "PATH: $as_dir" -done -IFS=$as_save_IFS + ac_retval=1 +fi + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + as_fn_set_status $ac_retval -} >&5 +} # ac_fn_cxx_try_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if { as_var=$3; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + +} # ac_fn_c_check_func + +# ac_fn_cxx_try_cpp LINENO +# ------------------------ +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } >/dev/null && { + test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_cpp + +# ac_fn_cxx_try_link LINENO +# ------------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + $as_test_x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; test "x$as_lineno_stack" = x && { as_lineno=; unset as_lineno;} + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_link +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by breakpad $as_me 0.1, which was +generated by GNU Autoconf 2.65. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 cat >&5 <<_ACEOF @@ -1653,12 +2079,12 @@ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) - ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in - 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;; + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) - ac_configure_args1="$ac_configure_args1 '$ac_arg'" + as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else @@ -1674,13 +2100,13 @@ -* ) ac_must_keep_next=true ;; esac fi - ac_configure_args="$ac_configure_args '$ac_arg'" + as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done -$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; } -$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; } +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there @@ -1705,12 +2131,13 @@ case $ac_val in #( *${as_nl}*) case $ac_var in #( - *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 -echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( - *) $as_unset $ac_var ;; + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done @@ -1739,9 +2166,9 @@ do eval ac_val=\$$ac_var case $ac_val in - *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac - echo "$ac_var='\''$ac_val'\''" + $as_echo "$ac_var='\''$ac_val'\''" done | sort echo @@ -1756,9 +2183,9 @@ do eval ac_val=\$$ac_var case $ac_val in - *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac - echo "$ac_var='\''$ac_val'\''" + $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi @@ -1774,83 +2201,88 @@ echo fi test "$ac_signal" != 0 && - echo "$as_me: caught signal $ac_signal" - echo "$as_me: exit $exit_status" + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do - trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h +$as_echo "/* confdefs.h */" > confdefs.h + # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF - cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF - cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF - cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF - cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + # Let the site file select an alternate cache file if it wants to. -# Prefer explicitly selected file to automatically selected ones. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE if test -n "$CONFIG_SITE"; then - set x "$CONFIG_SITE" + ac_site_file1=$CONFIG_SITE elif test "x$prefix" != xNONE; then - set x "$prefix/share/config.site" "$prefix/etc/config.site" + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site else - set x "$ac_default_prefix/share/config.site" \ - "$ac_default_prefix/etc/config.site" + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site fi -shift -for ac_site_file +for ac_site_file in "$ac_site_file1" "$ac_site_file2" do - if test -r "$ac_site_file"; then - { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5 -echo "$as_me: loading site script $ac_site_file" >&6;} + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" fi done if test -r "$cache_file"; then - # Some versions of bash will fail to source /dev/null (special - # files actually), so we avoid doing that. - if test -f "$cache_file"; then - { echo "$as_me:$LINENO: loading cache $cache_file" >&5 -echo "$as_me: loading cache $cache_file" >&6;} + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else - { echo "$as_me:$LINENO: creating cache $cache_file" >&5 -echo "$as_me: creating cache $cache_file" >&6;} + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi @@ -1864,68 +2296,56 @@ eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) - { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 -echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) - { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5 -echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then - { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5 -echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} - { echo "$as_me:$LINENO: former value: $ac_old_val" >&5 -echo "$as_me: former value: $ac_old_val" >&2;} - { echo "$as_me:$LINENO: current value: $ac_new_val" >&5 -echo "$as_me: current value: $ac_new_val" >&2;} - ac_cache_corrupted=: + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in - *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. - *) ac_configure_args="$ac_configure_args '$ac_arg'" ;; + *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then - { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5 -echo "$as_me: error: changes in the environment can compromise the build" >&2;} - { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5 -echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;} - { (exit 1); exit 1; }; } -fi - - - - - - - - - - - - - - - - - - - - - - - - + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -1937,24 +2357,16 @@ ac_aux_dir= for ac_dir in autotools "$srcdir"/autotools; do - if test -f "$ac_dir/install-sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install-sh -c" - break - elif test -f "$ac_dir/install.sh"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/install.sh -c" - break - elif test -f "$ac_dir/shtool"; then - ac_aux_dir=$ac_dir - ac_install_sh="$ac_aux_dir/shtool install -c" - break - fi + for ac_t in install-sh install.sh shtool; do + if test -f "$ac_dir/$ac_t"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/$ac_t -c" + break 2 + fi + done done if test -z "$ac_aux_dir"; then - { { echo "$as_me:$LINENO: error: cannot find install-sh or install.sh in autotools \"$srcdir\"/autotools" >&5 -echo "$as_me: error: cannot find install-sh or install.sh in autotools \"$srcdir\"/autotools" >&2;} - { (exit 1); exit 1; }; } + as_fn_error "cannot find install-sh, install.sh, or shtool in autotools \"$srcdir\"/autotools" "$LINENO" 5 fi # These three variables are undocumented and unsupported, @@ -1967,7 +2379,7 @@ -am__api_version='1.10' +am__api_version='1.11' # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or @@ -1982,22 +2394,23 @@ # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. -{ echo "$as_me:$LINENO: checking for a BSD-compatible install" >&5 -echo $ECHO_N "checking for a BSD-compatible install... $ECHO_C" >&6; } +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then -if test "${ac_cv_path_install+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +if test "${ac_cv_path_install+set}" = set; then : + $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - # Account for people who put trailing slashes in PATH elements. -case $as_dir/ in - ./ | .// | /cC/* | \ + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ - ?:\\/os2\\/install\\/* | ?:\\/OS2\\/INSTALL\\/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. @@ -2015,17 +2428,29 @@ # program-specific install script used by HP pwplus--don't use. : else - ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" - break 3 + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi fi fi done done ;; esac -done + + done IFS=$as_save_IFS +rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then @@ -2038,8 +2463,8 @@ INSTALL=$ac_install_sh fi fi -{ echo "$as_me:$LINENO: result: $INSTALL" >&5 -echo "${ECHO_T}$INSTALL" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. @@ -2049,21 +2474,34 @@ test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' -{ echo "$as_me:$LINENO: checking whether build environment is sane" >&5 -echo $ECHO_N "checking whether build environment is sane... $ECHO_C" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } # Just in case sleep 1 echo timestamp > conftest.file +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error "unsafe srcdir value: \`$srcdir'" "$LINENO" 5;; +esac + # Do `set' in a subshell so we don't clobber the current shell's # arguments. Must try -L first in case configure is actually a # symlink; some systems play weird games with the mod time of symlinks # (eg FreeBSD returns the mod time of the symlink's containing # directory). if ( - set X `ls -Lt $srcdir/configure conftest.file 2> /dev/null` + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` if test "$*" = "X"; then # -L didn't work. - set X `ls -t $srcdir/configure conftest.file` + set X `ls -t "$srcdir/configure" conftest.file` fi rm -f conftest.file if test "$*" != "X $srcdir/configure conftest.file" \ @@ -2073,11 +2511,8 @@ # if, for instance, CONFIG_SHELL is bash and it inherits a # broken ls alias from the environment. This has actually # happened. Such a system could not be considered "sane". - { { echo "$as_me:$LINENO: error: ls -t appears to fail. Make sure there is not a broken -alias in your environment" >&5 -echo "$as_me: error: ls -t appears to fail. Make sure there is not a broken -alias in your environment" >&2;} - { (exit 1); exit 1; }; } + as_fn_error "ls -t appears to fail. Make sure there is not a broken +alias in your environment" "$LINENO" 5 fi test "$2" = conftest.file @@ -2086,83 +2521,193 @@ # Ok. : else - { { echo "$as_me:$LINENO: error: newly created file is older than distributed files! -Check your system clock" >&5 -echo "$as_me: error: newly created file is older than distributed files! -Check your system clock" >&2;} - { (exit 1); exit 1; }; } + as_fn_error "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 fi -{ echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } test "$program_prefix" != NONE && program_transform_name="s&^&$program_prefix&;$program_transform_name" # Use a double $ so make ignores it. test "$program_suffix" != NONE && program_transform_name="s&\$&$program_suffix&;$program_transform_name" -# Double any \ or $. echo might interpret backslashes. +# Double any \ or $. # By default was `s,x,x', remove it if useless. -cat <<\_ACEOF >conftest.sed -s/[\\$]/&&/g;s/;s,x,x,$// -_ACEOF -program_transform_name=`echo $program_transform_name | sed -f conftest.sed` -rm -f conftest.sed +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` # expand $ac_aux_dir to an absolute path am_aux_dir=`cd $ac_aux_dir && pwd` -test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing" +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi # Use eval to expand $SHELL if eval "$MISSING --run true"; then am_missing_run="$MISSING --run " else am_missing_run= - { echo "$as_me:$LINENO: WARNING: \`missing' script is too old or missing" >&5 -echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: \`missing' script is too old or missing" >&2;} fi -{ echo "$as_me:$LINENO: checking for a thread-safe mkdir -p" >&5 -echo $ECHO_N "checking for a thread-safe mkdir -p... $ECHO_C" >&6; } -if test -z "$MKDIR_P"; then - if test "${ac_cv_path_mkdir+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +if test x"${install_sh}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using `strip' when the user +# run `make install-strip'. However `strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the `STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_STRIP+set}" = set; then : + $as_echo_n "(cached) " >&6 else - as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_prog in mkdir gmkdir; do - for ac_exec_ext in '' $ac_executable_extensions; do - { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue - case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( - 'mkdir (GNU coreutils) '* | \ - 'mkdir (coreutils) '* | \ - 'mkdir (fileutils) '4.1*) - ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext - break 3;; - esac - done - done + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi done + done IFS=$as_save_IFS fi - - if test "${ac_cv_path_mkdir+set}" = set; then - MKDIR_P="$ac_cv_path_mkdir -p" - else - # As a last resort, use the slow shell script. Don't cache a - # value for MKDIR_P within a source directory, because that will - # break other packages using the cache if that directory is - # removed, or if the value is a relative name. - test -d ./--version && rmdir ./--version - MKDIR_P="$ac_install_sh -d" - fi fi -{ echo "$as_me:$LINENO: result: $MKDIR_P" >&5 -echo "${ECHO_T}$MKDIR_P" >&6; } - -mkdir_p="$MKDIR_P" +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if test "${ac_cv_path_mkdir+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; } || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +mkdir_p="$MKDIR_P" case $mkdir_p in [\\/$]* | ?:[\\/]*) ;; */*) mkdir_p="\$(top_builddir)/$mkdir_p" ;; @@ -2172,10 +2717,10 @@ do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_AWK+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_AWK+set}" = set; then : + $as_echo_n "(cached) " >&6 else if test -n "$AWK"; then ac_cv_prog_AWK="$AWK" # Let the user override the test. @@ -2185,36 +2730,37 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_AWK="$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi AWK=$ac_cv_prog_AWK if test -n "$AWK"; then - { echo "$as_me:$LINENO: result: $AWK" >&5 -echo "${ECHO_T}$AWK" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi test -n "$AWK" && break done -{ echo "$as_me:$LINENO: checking whether ${MAKE-make} sets \$(MAKE)" >&5 -echo $ECHO_N "checking whether ${MAKE-make} sets \$(MAKE)... $ECHO_C" >&6; } -set x ${MAKE-make}; ac_make=`echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` -if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if { as_var=ac_cv_prog_make_${ac_make}_set; eval "test \"\${$as_var+set}\" = set"; }; then : + $as_echo_n "(cached) " >&6 else cat >conftest.make <<\_ACEOF SHELL = /bin/sh @@ -2231,12 +2777,12 @@ rm -f conftest.make fi if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then - { echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } SET_MAKE= else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } SET_MAKE="MAKE=${MAKE-make}" fi @@ -2255,9 +2801,7 @@ am__isrc=' -I$(srcdir)' # test to see if srcdir already configured if test -f $srcdir/config.status; then - { { echo "$as_me:$LINENO: error: source directory already configured; run \"make distclean\" there first" >&5 -echo "$as_me: error: source directory already configured; run \"make distclean\" there first" >&2;} - { (exit 1); exit 1; }; } + as_fn_error "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 fi fi @@ -2301,112 +2845,6 @@ MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} -install_sh=${install_sh-"\$(SHELL) $am_aux_dir/install-sh"} - -# Installed binaries are usually stripped using `strip' when the user -# run `make install-strip'. However `strip' might not be the right -# tool to use in cross-compilation environments, therefore Automake -# will honor the `STRIP' environment variable to overrule this program. -if test "$cross_compiling" != no; then - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. -set dummy ${ac_tool_prefix}strip; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_STRIP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$STRIP"; then - ac_cv_prog_STRIP="$STRIP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_STRIP="${ac_tool_prefix}strip" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done -IFS=$as_save_IFS - -fi -fi -STRIP=$ac_cv_prog_STRIP -if test -n "$STRIP"; then - { echo "$as_me:$LINENO: result: $STRIP" >&5 -echo "${ECHO_T}$STRIP" >&6; } -else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi - - -fi -if test -z "$ac_cv_prog_STRIP"; then - ac_ct_STRIP=$STRIP - # Extract the first word of "strip", so it can be a program name with args. -set dummy strip; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -n "$ac_ct_STRIP"; then - ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do - if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_ac_ct_STRIP="strip" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done -done -IFS=$as_save_IFS - -fi -fi -ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP -if test -n "$ac_ct_STRIP"; then - { echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5 -echo "${ECHO_T}$ac_ct_STRIP" >&6; } -else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi - - if test "x$ac_ct_STRIP" = x; then - STRIP=":" - else - case $cross_compiling:$ac_tool_warned in -yes:) -{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&5 -echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&2;} -ac_tool_warned=yes ;; -esac - STRIP=$ac_ct_STRIP - fi -else - STRIP="$ac_cv_prog_STRIP" -fi - -fi -INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" - # We need awk for the "check" target. The system "awk" is bad on # some platforms. # Always define AMTAR for backward compatibility. @@ -2414,8 +2852,8 @@ AMTAR=${AMTAR-"${am_missing_run}tar"} -{ echo "$as_me:$LINENO: checking how to create a ustar tar archive" >&5 -echo $ECHO_N "checking how to create a ustar tar archive... $ECHO_C" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to create a ustar tar archive" >&5 +$as_echo_n "checking how to create a ustar tar archive... " >&6; } # Loop over all known methods to create a tar archive until one works. _am_tools='gnutar plaintar pax cpio none' _am_tools=${am_cv_prog_tar_ustar-$_am_tools} @@ -2487,14 +2925,14 @@ done rm -rf conftest.dir -if test "${am_cv_prog_tar_ustar+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +if test "${am_cv_prog_tar_ustar+set}" = set; then : + $as_echo_n "(cached) " >&6 else am_cv_prog_tar_ustar=$_am_tool fi -{ echo "$as_me:$LINENO: result: $am_cv_prog_tar_ustar" >&5 -echo "${ECHO_T}$am_cv_prog_tar_ustar" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_tar_ustar" >&5 +$as_echo "$am_cv_prog_tar_ustar" >&6; } @@ -2511,10 +2949,10 @@ if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then : + $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. @@ -2524,25 +2962,25 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}gcc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then - { echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi @@ -2551,10 +2989,10 @@ ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : + $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. @@ -2564,25 +3002,25 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="gcc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then - { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 -echo "${ECHO_T}$ac_ct_CC" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then @@ -2590,12 +3028,8 @@ else case $cross_compiling:$ac_tool_warned in yes:) -{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&5 -echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&2;} +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC @@ -2608,10 +3042,10 @@ if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then : + $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. @@ -2621,25 +3055,25 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}cc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then - { echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi @@ -2648,10 +3082,10 @@ if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then : + $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. @@ -2662,18 +3096,18 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then @@ -2692,11 +3126,11 @@ fi CC=$ac_cv_prog_CC if test -n "$CC"; then - { echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi @@ -2707,10 +3141,10 @@ do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CC+set}" = set; then : + $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. @@ -2720,25 +3154,25 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then - { echo "$as_me:$LINENO: result: $CC" >&5 -echo "${ECHO_T}$CC" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi @@ -2751,10 +3185,10 @@ do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_ac_ct_CC+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CC+set}" = set; then : + $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. @@ -2764,25 +3198,25 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then - { echo "$as_me:$LINENO: result: $ac_ct_CC" >&5 -echo "${ECHO_T}$ac_ct_CC" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi @@ -2794,12 +3228,8 @@ else case $cross_compiling:$ac_tool_warned in yes:) -{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&5 -echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&2;} +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC @@ -2809,51 +3239,37 @@ fi -test -z "$CC" && { { echo "$as_me:$LINENO: error: no acceptable C compiler found in \$PATH -See \`config.log' for more details." >&5 -echo "$as_me: error: no acceptable C compiler found in \$PATH -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "no acceptable C compiler found in \$PATH +See \`config.log' for more details." "$LINENO" 5; } # Provide some information about the compiler. -echo "$as_me:$LINENO: checking for C compiler version" >&5 -ac_compiler=`set X $ac_compile; echo $2` -{ (ac_try="$ac_compiler --version >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler --version >&5") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (ac_try="$ac_compiler -v >&5" +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler -v >&5") 2>&5 +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (ac_try="$ac_compiler -V >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler -V >&5") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -2865,42 +3281,38 @@ } _ACEOF ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files a.out a.exe b.out" +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. -{ echo "$as_me:$LINENO: checking for C compiler default output file name" >&5 -echo $ECHO_N "checking for C compiler default output file name... $ECHO_C" >&6; } -ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` -# -# List of possible output files, starting from the most likely. -# The algorithm is not robust to junk in `.', hence go to wildcards (a.*) -# only as a last resort. b.out is created by i960 compilers. -ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out' -# -# The IRIX 6 linker writes into existing files which may not be -# executable, retaining their permissions. Remove them first so a -# subsequent execution test works. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + ac_rmfiles= for ac_file in $ac_files do case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles -if { (ac_try="$ac_link_default" +if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, @@ -2910,14 +3322,14 @@ do test -f "$ac_file" || continue case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) - if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi @@ -2936,78 +3348,42 @@ else ac_file='' fi - -{ echo "$as_me:$LINENO: result: $ac_file" >&5 -echo "${ECHO_T}$ac_file" >&6; } -if test -z "$ac_file"; then - echo "$as_me: failed program was:" >&5 +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 -{ { echo "$as_me:$LINENO: error: C compiler cannot create executables -See \`config.log' for more details." >&5 -echo "$as_me: error: C compiler cannot create executables -See \`config.log' for more details." >&2;} - { (exit 77); exit 77; }; } -fi - +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +{ as_fn_set_status 77 +as_fn_error "C compiler cannot create executables +See \`config.log' for more details." "$LINENO" 5; }; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ echo "$as_me:$LINENO: checking whether the C compiler works" >&5 -echo $ECHO_N "checking whether the C compiler works... $ECHO_C" >&6; } -# FIXME: These cross compiler hacks should be removed for Autoconf 3.0 -# If not cross compiling, check that we can run a simple program. -if test "$cross_compiling" != yes; then - if { ac_try='./$ac_file' - { (case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - cross_compiling=no - else - if test "$cross_compiling" = maybe; then - cross_compiling=yes - else - { { echo "$as_me:$LINENO: error: cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot run C compiled programs. -If you meant to cross compile, use \`--host'. -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } - fi - fi -fi -{ echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6; } - -rm -f a.out a.exe conftest$ac_cv_exeext b.out +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save -# Check that the compiler produces executables we can run. If not, either -# the compiler is broken, or we cross compile. -{ echo "$as_me:$LINENO: checking whether we are cross compiling" >&5 -echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; } -{ echo "$as_me:$LINENO: result: $cross_compiling" >&5 -echo "${ECHO_T}$cross_compiling" >&6; } - -{ echo "$as_me:$LINENO: checking for suffix of executables" >&5 -echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; } -if { (ac_try="$ac_link" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with @@ -3015,154 +3391,182 @@ for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;; + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else - { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot compute suffix of executables: cannot compile and link -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } -fi - -rm -f conftest$ac_cv_exeext -{ echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5 -echo "${ECHO_T}$ac_cv_exeext" >&6; } + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details." "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT -{ echo "$as_me:$LINENO: checking for suffix of object files" >&5 -echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; } -if test "${ac_cv_objext+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ - +#include int main () { +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF -rm -f conftest.o conftest.obj -if { (ac_try="$ac_compile" +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>&5 +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - for ac_file in conftest.o conftest.obj conftest.*; do - test -f "$ac_file" || continue; - case $ac_file in - *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;; - *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` - break;; - esac -done -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - -{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile -See \`config.log' for more details." >&5 -echo "$as_me: error: cannot compute suffix of object files: cannot compile -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } -fi - -rm -f conftest.$ac_cv_objext conftest.$ac_ext -fi -{ echo "$as_me:$LINENO: result: $ac_cv_objext" >&5 -echo "${ECHO_T}$ac_cv_objext" >&6; } -OBJEXT=$ac_cv_objext -ac_objext=$OBJEXT -{ echo "$as_me:$LINENO: checking whether we are using the GNU C compiler" >&5 -echo $ECHO_N "checking whether we are using the GNU C compiler... $ECHO_C" >&6; } -if test "${ac_cv_c_compiler_gnu+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details." "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if test "${ac_cv_objext+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { -#ifndef __GNUC__ - choke me -#endif ; return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_compiler_gnu=yes + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done else - echo "$as_me: failed program was:" >&5 + $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 - ac_compiler_gnu=no +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "cannot compute suffix of object files: cannot compile +See \`config.log' for more details." "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if test "${ac_cv_c_compiler_gnu+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi -{ echo "$as_me:$LINENO: result: $ac_cv_c_compiler_gnu" >&5 -echo "${ECHO_T}$ac_cv_c_compiler_gnu" >&6; } -GCC=`test $ac_compiler_gnu = yes && echo yes` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS -{ echo "$as_me:$LINENO: checking whether $CC accepts -g" >&5 -echo $ECHO_N "checking whether $CC accepts -g... $ECHO_C" >&6; } -if test "${ac_cv_prog_cc_g+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if test "${ac_cv_prog_cc_g+set}" = set; then : + $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -3173,34 +3577,11 @@ return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then +if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - CFLAGS="" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -3211,35 +3592,12 @@ return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +if ac_fn_c_try_compile "$LINENO"; then : - ac_c_werror_flag=$ac_save_c_werror_flag +else + ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -3250,42 +3608,18 @@ return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then +if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - fi - rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi - rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi - rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi -{ echo "$as_me:$LINENO: result: $ac_cv_prog_cc_g" >&5 -echo "${ECHO_T}$ac_cv_prog_cc_g" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then @@ -3301,18 +3635,14 @@ CFLAGS= fi fi -{ echo "$as_me:$LINENO: checking for $CC option to accept ISO C89" >&5 -echo $ECHO_N "checking for $CC option to accept ISO C89... $ECHO_C" >&6; } -if test "${ac_cv_prog_cc_c89+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if test "${ac_cv_prog_cc_c89+set}" = set; then : + $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF +cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include @@ -3369,31 +3699,9 @@ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" - rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then + if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - fi - rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done @@ -3404,17 +3712,19 @@ # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) - { echo "$as_me:$LINENO: result: none needed" >&5 -echo "${ECHO_T}none needed" >&6; } ;; + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; xno) - { echo "$as_me:$LINENO: result: unsupported" >&5 -echo "${ECHO_T}unsupported" >&6; } ;; + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" - { echo "$as_me:$LINENO: result: $ac_cv_prog_cc_c89" >&5 -echo "${ECHO_T}$ac_cv_prog_cc_c89" >&6; } ;; + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac +if test "x$ac_cv_prog_cc_c89" != xno; then : +fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -3429,44 +3739,44 @@ am_make=${MAKE-make} cat > confinc << 'END' am__doit: - @echo done + @echo this is the am__doit target .PHONY: am__doit END # If we don't find an include directive, just comment out the code. -{ echo "$as_me:$LINENO: checking for style of include used by $am_make" >&5 -echo $ECHO_N "checking for style of include used by $am_make... $ECHO_C" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for style of include used by $am_make" >&5 +$as_echo_n "checking for style of include used by $am_make... " >&6; } am__include="#" am__quote= _am_result=none # First try GNU make style include. echo "include confinc" > confmf -# We grep out `Entering directory' and `Leaving directory' -# messages which can occur if `w' ends up in MAKEFLAGS. -# In particular we don't look at `^make:' because GNU make might -# be invoked under some other name (usually "gmake"), in which -# case it prints its new name instead of `make'. -if test "`$am_make -s -f confmf 2> /dev/null | grep -v 'ing directory'`" = "done"; then - am__include=include - am__quote= - _am_result=GNU -fi +# Ignore all kinds of additional output from `make'. +case `$am_make -s -f confmf 2> /dev/null` in #( +*the\ am__doit\ target*) + am__include=include + am__quote= + _am_result=GNU + ;; +esac # Now try BSD make style include. if test "$am__include" = "#"; then echo '.include "confinc"' > confmf - if test "`$am_make -s -f confmf 2> /dev/null`" = "done"; then - am__include=.include - am__quote="\"" - _am_result=BSD - fi + case `$am_make -s -f confmf 2> /dev/null` in #( + *the\ am__doit\ target*) + am__include=.include + am__quote="\"" + _am_result=BSD + ;; + esac fi -{ echo "$as_me:$LINENO: result: $_am_result" >&5 -echo "${ECHO_T}$_am_result" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $_am_result" >&5 +$as_echo "$_am_result" >&6; } rm -f confinc confmf # Check whether --enable-dependency-tracking was given. -if test "${enable_dependency_tracking+set}" = set; then +if test "${enable_dependency_tracking+set}" = set; then : enableval=$enable_dependency_tracking; fi @@ -3486,10 +3796,10 @@ depcc="$CC" am_compiler_list= -{ echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 -echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6; } -if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if test "${am_cv_CC_dependencies_compiler_type+set}" = set; then : + $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up @@ -3514,6 +3824,11 @@ if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and @@ -3531,7 +3846,17 @@ done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; nosideeffect) # after this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested @@ -3541,19 +3866,23 @@ break fi ;; + msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; none) break ;; esac - # We check with `-c' and `-o' for the sake of the "dashmstdout" - # mode. It turns out that the SunPro C++ compiler does not properly - # handle `-M -o', and we need to detect this. if depmode=$depmode \ - source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ - $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && - grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message @@ -3577,8 +3906,8 @@ fi fi -{ echo "$as_me:$LINENO: result: $am_cv_CC_dependencies_compiler_type" >&5 -echo "${ECHO_T}$am_cv_CC_dependencies_compiler_type" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type if @@ -3597,15 +3926,15 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ echo "$as_me:$LINENO: checking how to run the C preprocessor" >&5 -echo $ECHO_N "checking how to run the C preprocessor... $ECHO_C" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then - if test "${ac_cv_prog_CPP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 + if test "${ac_cv_prog_CPP+set}" = set; then : + $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" @@ -3619,11 +3948,7 @@ # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include @@ -3632,76 +3957,34 @@ #endif Syntax error _ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +if ac_fn_c_try_cpp "$LINENO"; then : +else # Broken: fails on valid input. continue fi - rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then +if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - # Passes both tests. ac_preproc_ok=: break fi - rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext -if $ac_preproc_ok; then +if $ac_preproc_ok; then : break fi @@ -3713,8 +3996,8 @@ else ac_cv_prog_CPP=$CPP fi -{ echo "$as_me:$LINENO: result: $CPP" >&5 -echo "${ECHO_T}$CPP" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do @@ -3724,11 +4007,7 @@ # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include @@ -3737,83 +4016,40 @@ #endif Syntax error _ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +if ac_fn_c_try_cpp "$LINENO"; then : +else # Broken: fails on valid input. continue fi - rm -f conftest.err conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then +if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - # Passes both tests. ac_preproc_ok=: break fi - rm -f conftest.err conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.err conftest.$ac_ext -if $ac_preproc_ok; then - : +if $ac_preproc_ok; then : + else - { { echo "$as_me:$LINENO: error: C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details." >&5 -echo "$as_me: error: C preprocessor \"$CPP\" fails sanity check -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details." "$LINENO" 5; } fi ac_ext=c @@ -3836,10 +4072,10 @@ do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_CXX+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CXX+set}" = set; then : + $as_echo_n "(cached) " >&6 else if test -n "$CXX"; then ac_cv_prog_CXX="$CXX" # Let the user override the test. @@ -3849,25 +4085,25 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi CXX=$ac_cv_prog_CXX if test -n "$CXX"; then - { echo "$as_me:$LINENO: result: $CXX" >&5 -echo "${ECHO_T}$CXX" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi @@ -3880,10 +4116,10 @@ do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then : + $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CXX"; then ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. @@ -3893,25 +4129,25 @@ do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CXX="$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi ac_ct_CXX=$ac_cv_prog_ac_ct_CXX if test -n "$ac_ct_CXX"; then - { echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5 -echo "${ECHO_T}$ac_ct_CXX" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi @@ -3923,12 +4159,8 @@ else case $cross_compiling:$ac_tool_warned in yes:) -{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&5 -echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&2;} +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CXX=$ac_ct_CXX @@ -3938,49 +4170,36 @@ fi fi # Provide some information about the compiler. -echo "$as_me:$LINENO: checking for C++ compiler version" >&5 -ac_compiler=`set X $ac_compile; echo $2` -{ (ac_try="$ac_compiler --version >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler --version >&5") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (ac_try="$ac_compiler -v >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler -v >&5") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (ac_try="$ac_compiler -V >&5" +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler -V >&5") 2>&5 +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } - -{ echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5 -echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6; } -if test "${ac_cv_cxx_compiler_gnu+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if test "${ac_cv_cxx_compiler_gnu+set}" = set; then : + $as_echo_n "(cached) " >&6 else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -3994,54 +4213,34 @@ return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then +if ac_fn_cxx_try_compile "$LINENO"; then : ac_compiler_gnu=yes else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_compiler_gnu=no + ac_compiler_gnu=no fi - rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_cxx_compiler_gnu=$ac_compiler_gnu fi -{ echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5 -echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6; } -GXX=`test $ac_compiler_gnu = yes && echo yes` +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes +else + GXX= +fi ac_test_CXXFLAGS=${CXXFLAGS+set} ac_save_CXXFLAGS=$CXXFLAGS -{ echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5 -echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6; } -if test "${ac_cv_prog_cxx_g+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if test "${ac_cv_prog_cxx_g+set}" = set; then : + $as_echo_n "(cached) " >&6 else ac_save_cxx_werror_flag=$ac_cxx_werror_flag ac_cxx_werror_flag=yes ac_cv_prog_cxx_g=no CXXFLAGS="-g" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -4052,34 +4251,11 @@ return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then +if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - CXXFLAGS="" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -4090,35 +4266,12 @@ return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - : -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +if ac_fn_cxx_try_compile "$LINENO"; then : - ac_cxx_werror_flag=$ac_save_cxx_werror_flag +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag CXXFLAGS="-g" - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF + cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int @@ -4129,42 +4282,18 @@ return 0; } _ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then +if ac_fn_cxx_try_compile "$LINENO"; then : ac_cv_prog_cxx_g=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - fi - rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi - rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi - rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cxx_werror_flag=$ac_save_cxx_werror_flag fi -{ echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5 -echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } if test "$ac_test_CXXFLAGS" = set; then CXXFLAGS=$ac_save_CXXFLAGS elif test $ac_cv_prog_cxx_g = yes; then @@ -4188,10 +4317,10 @@ depcc="$CXX" am_compiler_list= -{ echo "$as_me:$LINENO: checking dependency style of $depcc" >&5 -echo $ECHO_N "checking dependency style of $depcc... $ECHO_C" >&6; } -if test "${am_cv_CXX_dependencies_compiler_type+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if test "${am_cv_CXX_dependencies_compiler_type+set}" = set; then : + $as_echo_n "(cached) " >&6 else if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then # We make a subdir and do the tests there. Otherwise we can end up @@ -4216,6 +4345,11 @@ if test "$am_compiler_list" = ""; then am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + for depmode in $am_compiler_list; do # Setup a source with many dependencies, because some compilers # like to wrap large dependency lists on column 80 (with \), and @@ -4233,7 +4367,17 @@ done echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; nosideeffect) # after this tag, mechanisms are not by side-effect, so they'll # only be used when explicitly requested @@ -4243,19 +4387,23 @@ break fi ;; + msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; none) break ;; esac - # We check with `-c' and `-o' for the sake of the "dashmstdout" - # mode. It turns out that the SunPro C++ compiler does not properly - # handle `-M -o', and we need to detect this. if depmode=$depmode \ - source=sub/conftest.c object=sub/conftest.${OBJEXT-o} \ + source=sub/conftest.c object=$am__obj \ depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ - $SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ >/dev/null 2>conftest.err && grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && - grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && ${MAKE-make} -s -f confmf > /dev/null 2>&1; then # icc doesn't choke on unknown options, it will just issue warnings # or remarks (even with -Werror). So we grep stderr for any message @@ -4279,8 +4427,8 @@ fi fi -{ echo "$as_me:$LINENO: result: $am_cv_CXX_dependencies_compiler_type" >&5 -echo "${ECHO_T}$am_cv_CXX_dependencies_compiler_type" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type if @@ -4295,109 +4443,54 @@ -# Check whether --enable-shared was given. -if test "${enable_shared+set}" = set; then - enableval=$enable_shared; p=${PACKAGE-default} - case $enableval in - yes) enable_shared=yes ;; - no) enable_shared=no ;; - *) - enable_shared=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_shared=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac -else - enable_shared=yes -fi +case `pwd` in + *\ * | *\ *) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&5 +$as_echo "$as_me: WARNING: Libtool does not cope well with whitespace in \`pwd\`" >&2;} ;; +esac -# Check whether --enable-static was given. -if test "${enable_static+set}" = set; then - enableval=$enable_static; p=${PACKAGE-default} - case $enableval in - yes) enable_static=yes ;; - no) enable_static=no ;; - *) - enable_static=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_static=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac -else - enable_static=yes -fi +macro_version='2.2.6b' +macro_revision='1.3017' -# Check whether --enable-fast-install was given. -if test "${enable_fast_install+set}" = set; then - enableval=$enable_fast_install; p=${PACKAGE-default} - case $enableval in - yes) enable_fast_install=yes ;; - no) enable_fast_install=no ;; - *) - enable_fast_install=no - # Look at the argument we got. We use all the common list separators. - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for pkg in $enableval; do - IFS="$lt_save_ifs" - if test "X$pkg" = "X$p"; then - enable_fast_install=yes - fi - done - IFS="$lt_save_ifs" - ;; - esac -else - enable_fast_install=yes -fi + + + + + + + + + + +ltmain="$ac_aux_dir/ltmain.sh" + # Make sure we can run config.sub. $SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || - { { echo "$as_me:$LINENO: error: cannot run $SHELL $ac_aux_dir/config.sub" >&5 -echo "$as_me: error: cannot run $SHELL $ac_aux_dir/config.sub" >&2;} - { (exit 1); exit 1; }; } - -{ echo "$as_me:$LINENO: checking build system type" >&5 -echo $ECHO_N "checking build system type... $ECHO_C" >&6; } -if test "${ac_cv_build+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 + as_fn_error "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if test "${ac_cv_build+set}" = set; then : + $as_echo_n "(cached) " >&6 else ac_build_alias=$build_alias test "x$ac_build_alias" = x && ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` test "x$ac_build_alias" = x && - { { echo "$as_me:$LINENO: error: cannot guess build type; you must specify one" >&5 -echo "$as_me: error: cannot guess build type; you must specify one" >&2;} - { (exit 1); exit 1; }; } + as_fn_error "cannot guess build type; you must specify one" "$LINENO" 5 ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || - { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&5 -echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $ac_build_alias failed" >&2;} - { (exit 1); exit 1; }; } + as_fn_error "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 fi -{ echo "$as_me:$LINENO: result: $ac_cv_build" >&5 -echo "${ECHO_T}$ac_cv_build" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } case $ac_cv_build in *-*-*) ;; -*) { { echo "$as_me:$LINENO: error: invalid value of canonical build" >&5 -echo "$as_me: error: invalid value of canonical build" >&2;} - { (exit 1); exit 1; }; };; +*) as_fn_error "invalid value of canonical build" "$LINENO" 5;; esac build=$ac_cv_build ac_save_IFS=$IFS; IFS='-' @@ -4413,28 +4506,24 @@ case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac -{ echo "$as_me:$LINENO: checking host system type" >&5 -echo $ECHO_N "checking host system type... $ECHO_C" >&6; } -if test "${ac_cv_host+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if test "${ac_cv_host+set}" = set; then : + $as_echo_n "(cached) " >&6 else if test "x$host_alias" = x; then ac_cv_host=$ac_cv_build else ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || - { { echo "$as_me:$LINENO: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&5 -echo "$as_me: error: $SHELL $ac_aux_dir/config.sub $host_alias failed" >&2;} - { (exit 1); exit 1; }; } + as_fn_error "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 fi fi -{ echo "$as_me:$LINENO: result: $ac_cv_host" >&5 -echo "${ECHO_T}$ac_cv_host" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } case $ac_cv_host in *-*-*) ;; -*) { { echo "$as_me:$LINENO: error: invalid value of canonical host" >&5 -echo "$as_me: error: invalid value of canonical host" >&2;} - { (exit 1); exit 1; }; };; +*) as_fn_error "invalid value of canonical host" "$LINENO" 5;; esac host=$ac_cv_host ac_save_IFS=$IFS; IFS='-' @@ -4450,102 +4539,122 @@ case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac -{ echo "$as_me:$LINENO: checking for a sed that does not truncate output" >&5 -echo $ECHO_N "checking for a sed that does not truncate output... $ECHO_C" >&6; } -if test "${lt_cv_path_SED+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - # Loop through the user's path and test for sed and gsed. -# Then use that list of sed's as ones to test for truncation. -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 +$as_echo_n "checking for a sed that does not truncate output... " >&6; } +if test "${ac_cv_path_SED+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ + for ac_i in 1 2 3 4 5 6 7; do + ac_script="$ac_script$as_nl$ac_script" + done + echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed + { ac_script=; unset ac_script;} + if test -z "$SED"; then + ac_path_SED_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for lt_ac_prog in sed gsed; do + for ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do - if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then - lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" - fi + ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue +# Check for GNU ac_path_SED and select it if it is found. + # Check for GNU $ac_path_SED +case `"$ac_path_SED" --version 2>&1` in +*GNU*) + ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo '' >> "conftest.nl" + "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_SED_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_SED="$ac_path_SED" + ac_path_SED_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_SED_found && break 3 done done -done -lt_ac_max=0 -lt_ac_count=0 -# Add /usr/xpg4/bin/sed as it is typically found on Solaris -# along with /bin/sed that truncates output. -for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do - test ! -f $lt_ac_sed && continue - cat /dev/null > conftest.in - lt_ac_count=0 - echo $ECHO_N "0123456789$ECHO_C" >conftest.in - # Check for GNU sed and select it if it is found. - if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then - lt_cv_path_SED=$lt_ac_sed - break - fi - while true; do - cat conftest.in conftest.in >conftest.tmp - mv conftest.tmp conftest.in - cp conftest.in conftest.nl - echo >>conftest.nl - $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break - cmp -s conftest.out conftest.nl || break - # 10000 chars as input seems more than enough - test $lt_ac_count -gt 10 && break - lt_ac_count=`expr $lt_ac_count + 1` - if test $lt_ac_count -gt $lt_ac_max; then - lt_ac_max=$lt_ac_count - lt_cv_path_SED=$lt_ac_sed - fi done -done +IFS=$as_save_IFS + if test -z "$ac_cv_path_SED"; then + as_fn_error "no acceptable sed could be found in \$PATH" "$LINENO" 5 + fi +else + ac_cv_path_SED=$SED +fi fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 +$as_echo "$ac_cv_path_SED" >&6; } + SED="$ac_cv_path_SED" + rm -f conftest.sed + +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" + + -SED=$lt_cv_path_SED -{ echo "$as_me:$LINENO: result: $SED" >&5 -echo "${ECHO_T}$SED" >&6; } - -{ echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5 -echo $ECHO_N "checking for grep that handles long lines and -e... $ECHO_C" >&6; } -if test "${ac_cv_path_GREP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - # Extract the first word of "grep ggrep" to use in msg output -if test -z "$GREP"; then -set dummy grep ggrep; ac_prog_name=$2 -if test "${ac_cv_path_GREP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 + + + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if test "${ac_cv_path_GREP+set}" = set; then : + $as_echo_n "(cached) " >&6 else + if test -z "$GREP"; then ac_path_GREP_found=false -# Loop through the user's path and test for each of PROGNAME-LIST -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_prog in grep ggrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" - { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue - # Check for GNU ac_path_GREP and select it if it is found. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue +# Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 - echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" + $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" - echo 'GREP' >> "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - ac_count=`expr $ac_count + 1` + as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" @@ -4557,77 +4666,61 @@ rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac - - $ac_path_GREP_found && break 3 + $ac_path_GREP_found && break 3 + done + done done -done - -done IFS=$as_save_IFS - - -fi - -GREP="$ac_cv_path_GREP" -if test -z "$GREP"; then - { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 -echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} - { (exit 1); exit 1; }; } -fi - + if test -z "$ac_cv_path_GREP"; then + as_fn_error "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi else ac_cv_path_GREP=$GREP fi - fi -{ echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5 -echo "${ECHO_T}$ac_cv_path_GREP" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" -{ echo "$as_me:$LINENO: checking for egrep" >&5 -echo $ECHO_N "checking for egrep... $ECHO_C" >&6; } -if test "${ac_cv_path_EGREP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if test "${ac_cv_path_EGREP+set}" = set; then : + $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else - # Extract the first word of "egrep" to use in msg output -if test -z "$EGREP"; then -set dummy egrep; ac_prog_name=$2 -if test "${ac_cv_path_EGREP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else + if test -z "$EGREP"; then ac_path_EGREP_found=false -# Loop through the user's path and test for each of PROGNAME-LIST -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_prog in egrep; do - for ac_exec_ext in '' $ac_executable_extensions; do - ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" - { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue - # Check for GNU ac_path_EGREP and select it if it is found. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue +# Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 - echo $ECHO_N "0123456789$ECHO_C" >"conftest.in" + $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" - echo 'EGREP' >> "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break - ac_count=`expr $ac_count + 1` + as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" @@ -4639,39 +4732,114 @@ rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac - - $ac_path_EGREP_found && break 3 + $ac_path_EGREP_found && break 3 + done + done done -done - -done IFS=$as_save_IFS - - + if test -z "$ac_cv_path_EGREP"; then + as_fn_error "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP fi -EGREP="$ac_cv_path_EGREP" -if test -z "$EGREP"; then - { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5 -echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;} - { (exit 1); exit 1; }; } + fi fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for fgrep" >&5 +$as_echo_n "checking for fgrep... " >&6; } +if test "${ac_cv_path_FGREP+set}" = set; then : + $as_echo_n "(cached) " >&6 else - ac_cv_path_EGREP=$EGREP -fi + if echo 'ab*c' | $GREP -F 'ab*c' >/dev/null 2>&1 + then ac_cv_path_FGREP="$GREP -F" + else + if test -z "$FGREP"; then + ac_path_FGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in fgrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_FGREP="$as_dir/$ac_prog$ac_exec_ext" + { test -f "$ac_path_FGREP" && $as_test_x "$ac_path_FGREP"; } || continue +# Check for GNU ac_path_FGREP and select it if it is found. + # Check for GNU $ac_path_FGREP +case `"$ac_path_FGREP" --version 2>&1` in +*GNU*) + ac_cv_path_FGREP="$ac_path_FGREP" ac_path_FGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'FGREP' >> "conftest.nl" + "$ac_path_FGREP" FGREP < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_FGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_FGREP="$ac_path_FGREP" + ac_path_FGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + $ac_path_FGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_FGREP"; then + as_fn_error "no acceptable fgrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_FGREP=$FGREP +fi fi fi -{ echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5 -echo "${ECHO_T}$ac_cv_path_EGREP" >&6; } - EGREP="$ac_cv_path_EGREP" +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_FGREP" >&5 +$as_echo "$ac_cv_path_FGREP" >&6; } + FGREP="$ac_cv_path_FGREP" + + +test -z "$GREP" && GREP=grep + + + + + + + + + + + + + + + + # Check whether --with-gnu-ld was given. -if test "${with_gnu_ld+set}" = set; then +if test "${with_gnu_ld+set}" = set; then : withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes else with_gnu_ld=no @@ -4680,8 +4848,8 @@ ac_prog=ld if test "$GCC" = yes; then # Check if gcc -print-prog-name=ld gives a path. - { echo "$as_me:$LINENO: checking for ld used by $CC" >&5 -echo $ECHO_N "checking for ld used by $CC... $ECHO_C" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } case $host in *-*-mingw*) # gcc leaves a trailing carriage return which upsets mingw @@ -4694,9 +4862,9 @@ [\\/]* | ?:[\\/]*) re_direlt='/[^/][^/]*/\.\./' # Canonicalize the pathname of ld - ac_prog=`echo $ac_prog| $SED 's%\\\\%/%g'` - while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do - ac_prog=`echo $ac_prog| $SED "s%$re_direlt%/%"` + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` done test -z "$LD" && LD="$ac_prog" ;; @@ -4710,14 +4878,14 @@ ;; esac elif test "$with_gnu_ld" = yes; then - { echo "$as_me:$LINENO: checking for GNU ld" >&5 -echo $ECHO_N "checking for GNU ld... $ECHO_C" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } else - { echo "$as_me:$LINENO: checking for non-GNU ld" >&5 -echo $ECHO_N "checking for non-GNU ld... $ECHO_C" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } fi -if test "${lt_cv_path_LD+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +if test "${lt_cv_path_LD+set}" = set; then : + $as_echo_n "(cached) " >&6 else if test -z "$LD"; then lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR @@ -4747,19 +4915,17 @@ LD="$lt_cv_path_LD" if test -n "$LD"; then - { echo "$as_me:$LINENO: result: $LD" >&5 -echo "${ECHO_T}$LD" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LD" >&5 +$as_echo "$LD" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi -test -z "$LD" && { { echo "$as_me:$LINENO: error: no acceptable ld found in \$PATH" >&5 -echo "$as_me: error: no acceptable ld found in \$PATH" >&2;} - { (exit 1); exit 1; }; } -{ echo "$as_me:$LINENO: checking if the linker ($LD) is GNU ld" >&5 -echo $ECHO_N "checking if the linker ($LD) is GNU ld... $ECHO_C" >&6; } -if test "${lt_cv_prog_gnu_ld+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +test -z "$LD" && as_fn_error "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if test "${lt_cv_prog_gnu_ld+set}" = set; then : + $as_echo_n "(cached) " >&6 else # I'd rather use --version here, but apparently some GNU lds only accept -v. case `$LD -v 2>&1 &5 -echo "${ECHO_T}$lt_cv_prog_gnu_ld" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } with_gnu_ld=$lt_cv_prog_gnu_ld -{ echo "$as_me:$LINENO: checking for $LD option to reload object files" >&5 -echo $ECHO_N "checking for $LD option to reload object files... $ECHO_C" >&6; } -if test "${lt_cv_ld_reload_flag+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_cv_ld_reload_flag='-r' -fi -{ echo "$as_me:$LINENO: result: $lt_cv_ld_reload_flag" >&5 -echo "${ECHO_T}$lt_cv_ld_reload_flag" >&6; } -reload_flag=$lt_cv_ld_reload_flag -case $reload_flag in -"" | " "*) ;; -*) reload_flag=" $reload_flag" ;; -esac -reload_cmds='$LD$reload_flag -o $output$reload_objs' -case $host_os in - darwin*) - if test "$GCC" = yes; then - reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs' - else - reload_cmds='$LD$reload_flag -o $output$reload_objs' - fi - ;; -esac -{ echo "$as_me:$LINENO: checking for BSD-compatible nm" >&5 -echo $ECHO_N "checking for BSD-compatible nm... $ECHO_C" >&6; } -if test "${lt_cv_path_NM+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 + + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for BSD- or MS-compatible name lister (nm)" >&5 +$as_echo_n "checking for BSD- or MS-compatible name lister (nm)... " >&6; } +if test "${lt_cv_path_NM+set}" = set; then : + $as_echo_n "(cached) " >&6 else if test -n "$NM"; then # Let the user override the test. @@ -4847,1441 +4995,1070 @@ done IFS="$lt_save_ifs" done - test -z "$lt_cv_path_NM" && lt_cv_path_NM=nm + : ${lt_cv_path_NM=no} fi fi -{ echo "$as_me:$LINENO: result: $lt_cv_path_NM" >&5 -echo "${ECHO_T}$lt_cv_path_NM" >&6; } -NM="$lt_cv_path_NM" +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_path_NM" >&5 +$as_echo "$lt_cv_path_NM" >&6; } +if test "$lt_cv_path_NM" != "no"; then + NM="$lt_cv_path_NM" +else + # Didn't find any BSD compatible name lister, look for dumpbin. + if test -n "$ac_tool_prefix"; then + for ac_prog in "dumpbin -symbols" "link -dump -symbols" + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_DUMPBIN+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$DUMPBIN"; then + ac_cv_prog_DUMPBIN="$DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_DUMPBIN="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS -{ echo "$as_me:$LINENO: checking whether ln -s works" >&5 -echo $ECHO_N "checking whether ln -s works... $ECHO_C" >&6; } -LN_S=$as_ln_s -if test "$LN_S" = "ln -s"; then - { echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6; } +fi +fi +DUMPBIN=$ac_cv_prog_DUMPBIN +if test -n "$DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DUMPBIN" >&5 +$as_echo "$DUMPBIN" >&6; } else - { echo "$as_me:$LINENO: result: no, using $LN_S" >&5 -echo "${ECHO_T}no, using $LN_S" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi -{ echo "$as_me:$LINENO: checking how to recognise dependent libraries" >&5 -echo $ECHO_N "checking how to recognise dependent libraries... $ECHO_C" >&6; } -if test "${lt_cv_deplibs_check_method+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_cv_file_magic_cmd='$MAGIC_CMD' -lt_cv_file_magic_test_file= -lt_cv_deplibs_check_method='unknown' -# Need to set the preceding variable on all platforms that support -# interlibrary dependencies. -# 'none' -- dependencies not supported. -# `unknown' -- same as none, but documents that we really don't know. -# 'pass_all' -- all dependencies passed with no checks. -# 'test_compile' -- check by making test program. -# 'file_magic [[regex]]' -- check by looking for files in library path -# which responds to the $file_magic_cmd with a given extended regex. -# If you have `file' or equivalent on your system and you're not sure -# whether `pass_all' will *always* work, you probably want this one. -case $host_os in -aix4* | aix5*) - lt_cv_deplibs_check_method=pass_all - ;; + test -n "$DUMPBIN" && break + done +fi +if test -z "$DUMPBIN"; then + ac_ct_DUMPBIN=$DUMPBIN + for ac_prog in "dumpbin -symbols" "link -dump -symbols" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_DUMPBIN+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_DUMPBIN"; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_ct_DUMPBIN" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_DUMPBIN="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS -beos*) - lt_cv_deplibs_check_method=pass_all - ;; +fi +fi +ac_ct_DUMPBIN=$ac_cv_prog_ac_ct_DUMPBIN +if test -n "$ac_ct_DUMPBIN"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DUMPBIN" >&5 +$as_echo "$ac_ct_DUMPBIN" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi -bsdi[45]*) - lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' - lt_cv_file_magic_cmd='/usr/bin/file -L' - lt_cv_file_magic_test_file=/shlib/libc.so - ;; -cygwin*) - # func_win32_libid is a shell function defined in ltmain.sh - lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' - lt_cv_file_magic_cmd='func_win32_libid' - ;; + test -n "$ac_ct_DUMPBIN" && break +done -mingw* | pw32*) - # Base MSYS/MinGW do not provide the 'file' command needed by - # func_win32_libid shell function, so use a weaker test based on 'objdump'. - lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?' - lt_cv_file_magic_cmd='$OBJDUMP -f' - ;; + if test "x$ac_ct_DUMPBIN" = x; then + DUMPBIN=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + DUMPBIN=$ac_ct_DUMPBIN + fi +fi -darwin* | rhapsody*) - lt_cv_deplibs_check_method=pass_all - ;; -freebsd* | kfreebsd*-gnu | dragonfly*) - if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then - case $host_cpu in - i*86 ) - # Not sure whether the presence of OpenBSD here was a mistake. - # Let's accept both of them until this is cleared up. - lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' - lt_cv_file_magic_cmd=/usr/bin/file - lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` - ;; - esac - else - lt_cv_deplibs_check_method=pass_all + if test "$DUMPBIN" != ":"; then + NM="$DUMPBIN" fi - ;; +fi +test -z "$NM" && NM=nm -gnu*) - lt_cv_deplibs_check_method=pass_all - ;; -hpux10.20* | hpux11*) - lt_cv_file_magic_cmd=/usr/bin/file - case $host_cpu in - ia64*) - lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' - lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so - ;; - hppa*64*) - lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]' - lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl - ;; - *) - lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9].[0-9]) shared library' - lt_cv_file_magic_test_file=/usr/lib/libc.sl - ;; - esac - ;; -interix3*) - # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' - ;; -irix5* | irix6* | nonstopux*) - case $LD in - *-32|*"-32 ") libmagic=32-bit;; - *-n32|*"-n32 ") libmagic=N32;; - *-64|*"-64 ") libmagic=64-bit;; - *) libmagic=never-match;; - esac - lt_cv_deplibs_check_method=pass_all - ;; -# This must be Linux ELF. -linux*) - lt_cv_deplibs_check_method=pass_all - ;; -netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ > /dev/null; then - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' - else - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the name lister ($NM) interface" >&5 +$as_echo_n "checking the name lister ($NM) interface... " >&6; } +if test "${lt_cv_nm_interface+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:5126: $ac_compile\"" >&5) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&5 + (eval echo "\"\$as_me:5129: $NM \\\"conftest.$ac_objext\\\"\"" >&5) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&5 + (eval echo "\"\$as_me:5132: output\"" >&5) + cat conftest.out >&5 + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" fi - ;; + rm -f conftest* +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_nm_interface" >&5 +$as_echo "$lt_cv_nm_interface" >&6; } -newos6*) - lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' - lt_cv_file_magic_cmd=/usr/bin/file - lt_cv_file_magic_test_file=/usr/lib/libnls.so - ;; +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ln -s works" >&5 +$as_echo_n "checking whether ln -s works... " >&6; } +LN_S=$as_ln_s +if test "$LN_S" = "ln -s"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no, using $LN_S" >&5 +$as_echo "no, using $LN_S" >&6; } +fi -nto-qnx*) - lt_cv_deplibs_check_method=unknown - ;; +# find the maximum length of command line arguments +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the maximum length of command line arguments" >&5 +$as_echo_n "checking the maximum length of command line arguments... " >&6; } +if test "${lt_cv_sys_max_cmd_len+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + i=0 + teststring="ABCD" -openbsd*) - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' - else - lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' - fi - ;; + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; -osf3* | osf4* | osf5*) - lt_cv_deplibs_check_method=pass_all - ;; + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; -solaris*) - lt_cv_deplibs_check_method=pass_all - ;; + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; -sysv4 | sysv4.3*) - case $host_vendor in - motorola) - lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' - lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; ;; - ncr) - lt_cv_deplibs_check_method=pass_all + + netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` ;; - sequent) - lt_cv_file_magic_cmd='/bin/file' - lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 ;; - sni) - lt_cv_file_magic_cmd='/bin/file' - lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" - lt_cv_file_magic_test_file=/lib/libc.so + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi ;; - siemens) - lt_cv_deplibs_check_method=pass_all + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 ;; - pc) - lt_cv_deplibs_check_method=pass_all + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8 ; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test "X"`$SHELL $0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \ + = "XX$teststring$teststring"; } >/dev/null 2>&1 && + test $i != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi ;; esac - ;; -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - lt_cv_deplibs_check_method=pass_all - ;; -esac +fi +if test -n $lt_cv_sys_max_cmd_len ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_sys_max_cmd_len" >&5 +$as_echo "$lt_cv_sys_max_cmd_len" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none" >&5 +$as_echo "none" >&6; } fi -{ echo "$as_me:$LINENO: result: $lt_cv_deplibs_check_method" >&5 -echo "${ECHO_T}$lt_cv_deplibs_check_method" >&6; } -file_magic_cmd=$lt_cv_file_magic_cmd -deplibs_check_method=$lt_cv_deplibs_check_method -test -z "$deplibs_check_method" && deplibs_check_method=unknown +max_cmd_len=$lt_cv_sys_max_cmd_len -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} -# Allow CC to be a program name with arguments. -compiler=$CC +: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands some XSI constructs" >&5 +$as_echo_n "checking whether the shell understands some XSI constructs... " >&6; } +# Try some XSI features +xsi_shell=no +( _lt_dummy="a/b/c" + test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \ + = c,a/b,, \ + && eval 'test $(( 1 + 1 )) -eq 2 \ + && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ + && xsi_shell=yes +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $xsi_shell" >&5 +$as_echo "$xsi_shell" >&6; } + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the shell understands \"+=\"" >&5 +$as_echo_n "checking whether the shell understands \"+=\"... " >&6; } +lt_shell_append=no +( foo=bar; set foo baz; eval "$1+=\$2" && test "$foo" = barbaz ) \ + >/dev/null 2>&1 \ + && lt_shell_append=yes +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_shell_append" >&5 +$as_echo "$lt_shell_append" >&6; } -# Check whether --enable-libtool-lock was given. -if test "${enable_libtool_lock+set}" = set; then - enableval=$enable_libtool_lock; +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false fi -test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes -# Some flags need to be propagated to the compiler or linker for good -# libtool support. -case $host in -ia64-*-hpux*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - case `/usr/bin/file conftest.$ac_objext` in - *ELF-32*) - HPUX_IA64_MODE="32" - ;; - *ELF-64*) - HPUX_IA64_MODE="64" - ;; - esac - fi - rm -rf conftest* + + + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' ;; -*-*-irix6*) - # Find out which ABI we are using. - echo '#line 5094 "configure"' > conftest.$ac_ext - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - if test "$lt_cv_prog_gnu_ld" = yes; then - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - LD="${LD-ld} -melf32bsmip" - ;; - *N32*) - LD="${LD-ld} -melf32bmipn32" - ;; - *64-bit*) - LD="${LD-ld} -melf64bmip" - ;; - esac - else - case `/usr/bin/file conftest.$ac_objext` in - *32-bit*) - LD="${LD-ld} -32" - ;; - *N32*) - LD="${LD-ld} -n32" - ;; - *64-bit*) - LD="${LD-ld} -64" - ;; - esac - fi - fi - rm -rf conftest* + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' ;; +esac + -x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*|s390*-*linux*|sparc*-*linux*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - case `/usr/bin/file conftest.o` in - *32-bit*) - case $host in - x86_64-*linux*) - LD="${LD-ld} -m elf_i386" - ;; - ppc64-*linux*|powerpc64-*linux*) - LD="${LD-ld} -m elf32ppclinux" - ;; - s390x-*linux*) - LD="${LD-ld} -m elf_s390" - ;; - sparc64-*linux*) - LD="${LD-ld} -m elf32_sparc" - ;; - esac - ;; - *64-bit*) - case $host in - x86_64-*linux*) - LD="${LD-ld} -m elf_x86_64" - ;; - ppc*-*linux*|powerpc*-*linux*) - LD="${LD-ld} -m elf64ppc" - ;; - s390*-*linux*) - LD="${LD-ld} -m elf64_s390" - ;; - sparc*-*linux*) - LD="${LD-ld} -m elf64_sparc" - ;; - esac - ;; - esac - fi - rm -rf conftest* - ;; -*-*-sco3.2v5*) - # On SCO OpenServer 5, we need -belf to get full-featured binaries. - SAVE_CFLAGS="$CFLAGS" - CFLAGS="$CFLAGS -belf" - { echo "$as_me:$LINENO: checking whether the C compiler needs -belf" >&5 -echo $ECHO_N "checking whether the C compiler needs -belf... $ECHO_C" >&6; } -if test "${lt_cv_cc_needs_belf+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -int -main () -{ - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - lt_cv_cc_needs_belf=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - lt_cv_cc_needs_belf=no -fi -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext - ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $LD option to reload object files" >&5 +$as_echo_n "checking for $LD option to reload object files... " >&6; } +if test "${lt_cv_ld_reload_flag+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_reload_flag='-r' fi -{ echo "$as_me:$LINENO: result: $lt_cv_cc_needs_belf" >&5 -echo "${ECHO_T}$lt_cv_cc_needs_belf" >&6; } - if test x"$lt_cv_cc_needs_belf" != x"yes"; then - # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf - CFLAGS="$SAVE_CFLAGS" - fi - ;; -sparc*-*solaris*) - # Find out which ABI we are using. - echo 'int i;' > conftest.$ac_ext - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - case `/usr/bin/file conftest.o` in - *64-bit*) - case $lt_cv_prog_gnu_ld in - yes*) LD="${LD-ld} -m elf64_sparc" ;; - *) LD="${LD-ld} -64" ;; - esac - ;; - esac - fi - rm -rf conftest* - ;; +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_reload_flag" >&5 +$as_echo "$lt_cv_ld_reload_flag" >&6; } +reload_flag=$lt_cv_ld_reload_flag +case $reload_flag in +"" | " "*) ;; +*) reload_flag=" $reload_flag" ;; +esac +reload_cmds='$LD$reload_flag -o $output$reload_objs' +case $host_os in + darwin*) + if test "$GCC" = yes; then + reload_cmds='$LTCC $LTCFLAGS -nostdlib ${wl}-r -o $output$reload_objs' + else + reload_cmds='$LD$reload_flag -o $output$reload_objs' + fi + ;; +esac -esac -need_locks="$enable_libtool_lock" -{ echo "$as_me:$LINENO: checking for ANSI C header files" >&5 -echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; } -if test "${ac_cv_header_stdc+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#include -#include -int -main () -{ - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_header_stdc=yes + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}objdump", so it can be a program name with args. +set dummy ${ac_tool_prefix}objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_OBJDUMP+set}" = set; then : + $as_echo_n "(cached) " >&6 else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 + if test -n "$OBJDUMP"; then + ac_cv_prog_OBJDUMP="$OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_OBJDUMP="${ac_tool_prefix}objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS - ac_cv_header_stdc=no +fi +fi +OBJDUMP=$ac_cv_prog_OBJDUMP +if test -n "$OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OBJDUMP" >&5 +$as_echo "$OBJDUMP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include +fi +if test -z "$ac_cv_prog_OBJDUMP"; then + ac_ct_OBJDUMP=$OBJDUMP + # Extract the first word of "objdump", so it can be a program name with args. +set dummy objdump; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_OBJDUMP+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OBJDUMP"; then + ac_cv_prog_ac_ct_OBJDUMP="$ac_ct_OBJDUMP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_OBJDUMP="objdump" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then - : +fi +fi +ac_ct_OBJDUMP=$ac_cv_prog_ac_ct_OBJDUMP +if test -n "$ac_ct_OBJDUMP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OBJDUMP" >&5 +$as_echo "$ac_ct_OBJDUMP" >&6; } else - ac_cv_header_stdc=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi -rm -f conftest* + if test "x$ac_ct_OBJDUMP" = x; then + OBJDUMP="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OBJDUMP=$ac_ct_OBJDUMP + fi +else + OBJDUMP="$ac_cv_prog_OBJDUMP" fi -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include +test -z "$OBJDUMP" && OBJDUMP=objdump -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then - : -else - ac_cv_header_stdc=no -fi -rm -f conftest* -fi -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then - : -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) -#endif -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) -int -main () -{ - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - return 2; - return 0; -} -_ACEOF -rm -f conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - : + + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to recognize dependent libraries" >&5 +$as_echo_n "checking how to recognize dependent libraries... " >&6; } +if test "${lt_cv_deplibs_check_method+set}" = set; then : + $as_echo_n "(cached) " >&6 else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 + lt_cv_file_magic_cmd='$MAGIC_CMD' +lt_cv_file_magic_test_file= +lt_cv_deplibs_check_method='unknown' +# Need to set the preceding variable on all platforms that support +# interlibrary dependencies. +# 'none' -- dependencies not supported. +# `unknown' -- same as none, but documents that we really don't know. +# 'pass_all' -- all dependencies passed with no checks. +# 'test_compile' -- check by making test program. +# 'file_magic [[regex]]' -- check by looking for files in library path +# which responds to the $file_magic_cmd with a given extended regex. +# If you have `file' or equivalent on your system and you're not sure +# whether `pass_all' will *always* work, you probably want this one. -( exit $ac_status ) -ac_cv_header_stdc=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi +case $host_os in +aix[4-9]*) + lt_cv_deplibs_check_method=pass_all + ;; +beos*) + lt_cv_deplibs_check_method=pass_all + ;; -fi -fi -{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 -echo "${ECHO_T}$ac_cv_header_stdc" >&6; } -if test $ac_cv_header_stdc = yes; then +bsdi[45]*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib)' + lt_cv_file_magic_cmd='/usr/bin/file -L' + lt_cv_file_magic_test_file=/shlib/libc.so + ;; -cat >>confdefs.h <<\_ACEOF -#define STDC_HEADERS 1 -_ACEOF +cygwin*) + # func_win32_libid is a shell function defined in ltmain.sh + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + ;; -fi +mingw* | pw32*) + # Base MSYS/MinGW do not provide the 'file' command needed by + # func_win32_libid shell function, so use a weaker test based on 'objdump', + # unless we find 'file', for example because we are cross-compiling. + if ( file / ) >/dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; -# On IRIX 5.3, sys/types and inttypes.h are conflicting. +cegcc) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[3-9]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; +gnu*) + lt_cv_deplibs_check_method=pass_all + ;; +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]' + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|PA-RISC[0-9].[0-9]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; +interix[3-9]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|\.a)$' + ;; +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; +# This must be Linux ELF. +linux* | k*bsd*-gnu) + lt_cv_deplibs_check_method=pass_all + ;; +netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so|_pic\.a)$' + fi + ;; -for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ - inttypes.h stdint.h unistd.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -{ echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - eval "$as_ac_Header=yes" -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; - eval "$as_ac_Header=no" -fi +openbsd*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[^/]+(\.so\.[0-9]+\.[0-9]+|_pic\.a)$' + fi + ;; -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -fi -ac_res=`eval echo '${'$as_ac_Header'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; -fi +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; -done +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [ML]SB (shared object|dynamic lib) M[0-9][0-9]* Version [0-9]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [0-9][0-9]*-bit [LM]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [0-9][0-9]*-bit [LM]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; -for ac_header in dlfcn.h -do -as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh` -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - { echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -fi -ac_res=`eval echo '${'$as_ac_Header'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -else - # Is the header compilable? -{ echo "$as_me:$LINENO: checking $ac_header usability" >&5 -echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; } -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -$ac_includes_default -#include <$ac_header> -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_header_compiler=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - ac_header_compiler=no fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_deplibs_check_method" >&5 +$as_echo "$lt_cv_deplibs_check_method" >&6; } +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5 -echo "${ECHO_T}$ac_header_compiler" >&6; } -# Is the header present? -{ echo "$as_me:$LINENO: checking $ac_header presence" >&5 -echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; } -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include <$ac_header> -_ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || - test ! -s conftest.err - }; then - ac_header_preproc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - ac_header_preproc=no -fi -rm -f conftest.err conftest.$ac_ext -{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5 -echo "${ECHO_T}$ac_header_preproc" >&6; } -# So? What about this header? -case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in - yes:no: ) - { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5 -echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;} - ac_header_preproc=yes - ;; - no:yes:* ) - { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5 -echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5 -echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5 -echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5 -echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5 -echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;} - { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5 -echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;} - ( cat <<\_ASBOX -## ------------------------------------ ## -## Report this to opensource@google.com ## -## ------------------------------------ ## -_ASBOX - ) | sed "s/^/$as_me: WARNING: /" >&2 - ;; -esac -{ echo "$as_me:$LINENO: checking for $ac_header" >&5 -echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; } -if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - eval "$as_ac_Header=\$ac_header_preproc" -fi -ac_res=`eval echo '${'$as_ac_Header'}'` - { echo "$as_me:$LINENO: result: $ac_res" >&5 -echo "${ECHO_T}$ac_res" >&6; } -fi -if test `eval echo '${'$as_ac_Header'}'` = yes; then - cat >>confdefs.h <<_ACEOF -#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1 -_ACEOF -fi -done -if test -n "$CXX" && ( test "X$CXX" != "Xno" && - ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || - (test "X$CXX" != "Xg++"))) ; then - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu -{ echo "$as_me:$LINENO: checking how to run the C++ preprocessor" >&5 -echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6; } -if test -z "$CXXCPP"; then - if test "${ac_cv_prog_CXXCPP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. +set dummy ${ac_tool_prefix}ar; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_AR+set}" = set; then : + $as_echo_n "(cached) " >&6 else - # Double quotes because CXXCPP needs to be expanded - for CXXCPP in "$CXX -E" "/lib/cpp" - do - ac_preproc_ok=false -for ac_cxx_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || - test ! -s conftest.err - }; then - : + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_AR="${ac_tool_prefix}ar" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS - # Broken: fails on valid input. -continue fi - -rm -f conftest.err conftest.$ac_ext - - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -_ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || - test ! -s conftest.err - }; then - # Broken: success on invalid input. -continue +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Passes both tests. -ac_preproc_ok=: -break + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi -rm -f conftest.err conftest.$ac_ext -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.err conftest.$ac_ext -if $ac_preproc_ok; then - break fi - - done - ac_cv_prog_CXXCPP=$CXXCPP +if test -z "$ac_cv_prog_AR"; then + ac_ct_AR=$AR + # Extract the first word of "ar", so it can be a program name with args. +set dummy ar; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_AR+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_AR="ar" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS fi - CXXCPP=$ac_cv_prog_CXXCPP +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } else - ac_cv_prog_CXXCPP=$CXXCPP + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi -{ echo "$as_me:$LINENO: result: $CXXCPP" >&5 -echo "${ECHO_T}$CXXCPP" >&6; } -ac_preproc_ok=false -for ac_cxx_preproc_warn_flag in '' yes -do - # Use a header file that comes with gcc, so configuring glibc - # with a fresh cross-compiler works. - # Prefer to if __STDC__ is defined, since - # exists even on freestanding compilers. - # On the NeXT, cc -E runs the code through the compiler's parser, - # not just through cpp. "Syntax error" is here to catch this case. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#ifdef __STDC__ -# include -#else -# include -#endif - Syntax error -_ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || - test ! -s conftest.err - }; then - : + AR=$ac_ct_AR + fi else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - # Broken: fails on valid input. -continue + AR="$ac_cv_prog_AR" fi -rm -f conftest.err conftest.$ac_ext +test -z "$AR" && AR=ar +test -z "$AR_FLAGS" && AR_FLAGS=cru + + + - # OK, works on sane cases. Now check whether nonexistent headers - # can be detected and how. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -_ACEOF -if { (ac_try="$ac_cpp conftest.$ac_ext" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } >/dev/null && { - test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" || - test ! -s conftest.err - }; then - # Broken: success on invalid input. -continue -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - # Passes both tests. -ac_preproc_ok=: -break -fi -rm -f conftest.err conftest.$ac_ext -done -# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. -rm -f conftest.err conftest.$ac_ext -if $ac_preproc_ok; then - : -else - { { echo "$as_me:$LINENO: error: C++ preprocessor \"$CXXCPP\" fails sanity check -See \`config.log' for more details." >&5 -echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check -See \`config.log' for more details." >&2;} - { (exit 1); exit 1; }; } -fi -ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu -fi -ac_ext=f -ac_compile='$F77 -c $FFLAGS conftest.$ac_ext >&5' -ac_link='$F77 -o conftest$ac_exeext $FFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_f77_compiler_gnu if test -n "$ac_tool_prefix"; then - for ac_prog in g77 xlf f77 frt pgf77 cf77 fort77 fl32 af77 xlf90 f90 pgf90 pghpf epcf90 gfortran g95 xlf95 f95 fort ifort ifc efc pgf95 lf95 ftn - do - # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. -set dummy $ac_tool_prefix$ac_prog; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_F77+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_STRIP+set}" = set; then : + $as_echo_n "(cached) " >&6 else - if test -n "$F77"; then - ac_cv_prog_F77="$F77" # Let the user override the test. + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_F77="$ac_tool_prefix$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi -F77=$ac_cv_prog_F77 -if test -n "$F77"; then - { echo "$as_me:$LINENO: result: $F77" >&5 -echo "${ECHO_T}$F77" >&6; } +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi - test -n "$F77" && break - done fi -if test -z "$F77"; then - ac_ct_F77=$F77 - for ac_prog in g77 xlf f77 frt pgf77 cf77 fort77 fl32 af77 xlf90 f90 pgf90 pghpf epcf90 gfortran g95 xlf95 f95 fort ifort ifc efc pgf95 lf95 ftn -do - # Extract the first word of "$ac_prog", so it can be a program name with args. -set dummy $ac_prog; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_ac_ct_F77+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then : + $as_echo_n "(cached) " >&6 else - if test -n "$ac_ct_F77"; then - ac_cv_prog_ac_ct_F77="$ac_ct_F77" # Let the user override the test. + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_ac_ct_F77="$ac_prog" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi -ac_ct_F77=$ac_cv_prog_ac_ct_F77 -if test -n "$ac_ct_F77"; then - { echo "$as_me:$LINENO: result: $ac_ct_F77" >&5 -echo "${ECHO_T}$ac_ct_F77" >&6; } +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi - - test -n "$ac_ct_F77" && break -done - - if test "x$ac_ct_F77" = x; then - F77="" + if test "x$ac_ct_STRIP" = x; then + STRIP=":" else case $cross_compiling:$ac_tool_warned in yes:) -{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&5 -echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&2;} +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac - F77=$ac_ct_F77 + STRIP=$ac_ct_STRIP fi +else + STRIP="$ac_cv_prog_STRIP" fi +test -z "$STRIP" && STRIP=: -# Provide some information about the compiler. -echo "$as_me:$LINENO: checking for Fortran 77 compiler version" >&5 -ac_compiler=`set X $ac_compile; echo $2` -{ (ac_try="$ac_compiler --version >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler --version >&5") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (ac_try="$ac_compiler -v >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler -v >&5") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -{ (ac_try="$ac_compiler -V >&5" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compiler -V >&5") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } -rm -f a.out - -# If we don't use `.F' as extension, the preprocessor is not run on the -# input file. (Note that this only needs to work for GNU compilers.) -ac_save_ext=$ac_ext -ac_ext=F -{ echo "$as_me:$LINENO: checking whether we are using the GNU Fortran 77 compiler" >&5 -echo $ECHO_N "checking whether we are using the GNU Fortran 77 compiler... $ECHO_C" >&6; } -if test "${ac_cv_f77_compiler_gnu+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF - program main -#ifndef __GNUC__ - choke me -#endif - end -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_f77_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_compiler_gnu=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - ac_compiler_gnu=no -fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -ac_cv_f77_compiler_gnu=$ac_compiler_gnu -fi -{ echo "$as_me:$LINENO: result: $ac_cv_f77_compiler_gnu" >&5 -echo "${ECHO_T}$ac_cv_f77_compiler_gnu" >&6; } -ac_ext=$ac_save_ext -ac_test_FFLAGS=${FFLAGS+set} -ac_save_FFLAGS=$FFLAGS -FFLAGS= -{ echo "$as_me:$LINENO: checking whether $F77 accepts -g" >&5 -echo $ECHO_N "checking whether $F77 accepts -g... $ECHO_C" >&6; } -if test "${ac_cv_prog_f77_g+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - FFLAGS=-g -cat >conftest.$ac_ext <<_ACEOF - program main - end -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_f77_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_prog_f77_g=yes +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_RANLIB+set}" = set; then : + $as_echo_n "(cached) " >&6 else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS - ac_cv_prog_f77_g=no +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi -{ echo "$as_me:$LINENO: result: $ac_cv_prog_f77_g" >&5 -echo "${ECHO_T}$ac_cv_prog_f77_g" >&6; } -if test "$ac_test_FFLAGS" = set; then - FFLAGS=$ac_save_FFLAGS -elif test $ac_cv_prog_f77_g = yes; then - if test "x$ac_cv_f77_compiler_gnu" = xyes; then - FFLAGS="-g -O2" - else - FFLAGS="-g" +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } else - if test "x$ac_cv_f77_compiler_gnu" = xyes; then - FFLAGS="-O2" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" else - FFLAGS= + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB fi +else + RANLIB="$ac_cv_prog_RANLIB" fi -G77=`test $ac_compiler_gnu = yes && echo yes` -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu +test -z "$RANLIB" && RANLIB=: -# Autoconf 2.13's AC_OBJEXT and AC_EXEEXT macros only works for C compilers! -# find the maximum length of command line arguments -{ echo "$as_me:$LINENO: checking the maximum length of command line arguments" >&5 -echo $ECHO_N "checking the maximum length of command line arguments... $ECHO_C" >&6; } -if test "${lt_cv_sys_max_cmd_len+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - i=0 - teststring="ABCD" - case $build_os in - msdosdjgpp*) - # On DJGPP, this test can blow up pretty badly due to problems in libc - # (any single argument exceeding 2000 bytes causes a buffer overrun - # during glob expansion). Even if it were fixed, the result of this - # check would be larger than it should be. - lt_cv_sys_max_cmd_len=12288; # 12K is about right - ;; - gnu*) - # Under GNU Hurd, this test is not required because there is - # no limit to the length of command line arguments. - # Libtool will interpret -1 as no limit whatsoever - lt_cv_sys_max_cmd_len=-1; - ;; +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= - cygwin* | mingw*) - # On Win9x/ME, this test blows up -- it succeeds, but takes - # about 5 minutes as the teststring grows exponentially. - # Worse, since 9x/ME are not pre-emptively multitasking, - # you end up with a "frozen" computer, even though with patience - # the test eventually succeeds (with a max line length of 256k). - # Instead, let's just punt: use the minimum linelength reported by - # all of the supported platforms: 8192 (on NT/2K/XP). - lt_cv_sys_max_cmd_len=8192; +if test -n "$RANLIB"; then + case $host_os in + openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" ;; - - amigaos*) - # On AmigaOS with pdksh, this test takes hours, literally. - # So we just punt and use a minimum line length of 8192. - lt_cv_sys_max_cmd_len=8192; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" +fi + + + + + + + + + + + + + + + + + + + - netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) - # This has been around since 386BSD, at least. Likely further. - if test -x /sbin/sysctl; then - lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` - elif test -x /usr/sbin/sysctl; then - lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` - else - lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs - fi - # And add a safety zone - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` - ;; - interix*) - # We know the value 262144 and hardcode it with a safety zone (like BSD) - lt_cv_sys_max_cmd_len=196608 - ;; - osf*) - # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure - # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not - # nice to cause kernel panics so lets avoid the loop below. - # First set a reasonable default. - lt_cv_sys_max_cmd_len=16384 - # - if test -x /sbin/sysconfig; then - case `/sbin/sysconfig -q proc exec_disable_arg_limit` in - *1*) lt_cv_sys_max_cmd_len=-1 ;; - esac - fi - ;; - sco3.2v5*) - lt_cv_sys_max_cmd_len=102400 - ;; - sysv5* | sco5v6* | sysv4.2uw2*) - kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` - if test -n "$kargmax"; then - lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[ ]//'` - else - lt_cv_sys_max_cmd_len=32768 - fi - ;; - *) - # If test is not a shell built-in, we'll probably end up computing a - # maximum length that is only half of the actual maximum length, but - # we can't tell. - SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} - while (test "X"`$SHELL $0 --fallback-echo "X$teststring" 2>/dev/null` \ - = "XX$teststring") >/dev/null 2>&1 && - new_result=`expr "X$teststring" : ".*" 2>&1` && - lt_cv_sys_max_cmd_len=$new_result && - test $i != 17 # 1/2 MB should be enough - do - i=`expr $i + 1` - teststring=$teststring$teststring - done - teststring= - # Add a significant safety factor because C++ compilers can tack on massive - # amounts of additional arguments before passing them to the linker. - # It appears as though 1/2 is a usable value. - lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` - ;; - esac -fi -if test -n $lt_cv_sys_max_cmd_len ; then - { echo "$as_me:$LINENO: result: $lt_cv_sys_max_cmd_len" >&5 -echo "${ECHO_T}$lt_cv_sys_max_cmd_len" >&6; } -else - { echo "$as_me:$LINENO: result: none" >&5 -echo "${ECHO_T}none" >&6; } -fi + + + + + + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC + + # Check for command to grab the raw symbol name followed by C symbol from nm. -{ echo "$as_me:$LINENO: checking command to parse $NM output from $compiler object" >&5 -echo $ECHO_N "checking command to parse $NM output from $compiler object... $ECHO_C" >&6; } -if test "${lt_cv_sys_global_symbol_pipe+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking command to parse $NM output from $compiler object" >&5 +$as_echo_n "checking command to parse $NM output from $compiler object... " >&6; } +if test "${lt_cv_sys_global_symbol_pipe+set}" = set; then : + $as_echo_n "(cached) " >&6 else # These are sane defaults that work on at least a few old systems. @@ -6293,33 +6070,18 @@ # Regexp to match symbols that can be accessed directly from C. sympat='\([_A-Za-z][_A-Za-z0-9]*\)' -# Transform an extracted symbol line into a proper C declaration -lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^. .* \(.*\)$/extern int \1;/p'" - -# Transform an extracted symbol line into symbol name and symbol address -lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode \([^ ]*\) \([^ ]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" - # Define system-specific variables. case $host_os in aix*) symcode='[BCDT]' ;; -cygwin* | mingw* | pw32*) +cygwin* | mingw* | pw32* | cegcc*) symcode='[ABCDGISTW]' ;; -hpux*) # Its linker distinguishes data from code symbols +hpux*) if test "$host_cpu" = ia64; then symcode='[ABCDEGRST]' fi - lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" - lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" - ;; -linux*) - if test "$host_cpu" = ia64; then - symcode='[ABCDGIRSTW]' - lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" - lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (lt_ptr) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (lt_ptr) \&\2},/p'" - fi ;; irix* | nonstopux*) symcode='[BCDEGRST]' @@ -6344,57 +6106,85 @@ ;; esac +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[ABCDGIRSTW]' ;; +esac + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"\2\", (void *) \&\2},/p'" +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([^ ]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([^ ]*\) \(lib[^ ]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([^ ]*\) \([^ ]*\)$/ {\"lib\2\", (void *) \&\2},/p'" + # Handle CRLF in mingw tool chain opt_cr= case $build_os in mingw*) - opt_cr=`echo 'x\{0,1\}' | tr x '\015'` # option cr in regexp + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp ;; esac -# If we're using GNU nm, then use its standard symbol codes. -case `$NM -V 2>&1` in -*GNU* | *'with BFD'*) - symcode='[ABCDGIRSTW]' ;; -esac - -# Try without a prefix undercore, then with it. +# Try without a prefix underscore, then with it. for ac_symprfx in "" "_"; do # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. symxfrm="\\1 $ac_symprfx\\2 \\2" # Write the raw and C identifiers. - lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function + # and D for any global variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK '"\ +" {last_section=section; section=\$ 3};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ +" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ +" s[1]~/^[@?]/{print s[1], s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[ ]\($symcode$symcode*\)[ ][ ]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi # Check to see that the pipe works correctly. pipe_works=no rm -f conftest* - cat > conftest.$ac_ext < conftest.$ac_ext <<_LT_EOF #ifdef __cplusplus extern "C" { #endif char nm_test_var; -void nm_test_func(){} +void nm_test_func(void); +void nm_test_func(void){} #ifdef __cplusplus } #endif int main(){nm_test_var='a';nm_test_func();return(0);} -EOF +_LT_EOF - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then # Now try to grab the symbols. nlist=conftest.nm - if { (eval echo "$as_me:$LINENO: \"$NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist\"") >&5 + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist\""; } >&5 (eval $NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) 2>&5 ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && test -s "$nlist"; then + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s "$nlist"; then # Try sorting and uniquifying the output. if sort "$nlist" | uniq > "$nlist"T; then mv -f "$nlist"T "$nlist" @@ -6403,53 +6193,55 @@ fi # Make sure that we snagged all the symbols we need. - if grep ' nm_test_var$' "$nlist" >/dev/null; then - if grep ' nm_test_func$' "$nlist" >/dev/null; then - cat < conftest.$ac_ext + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext #ifdef __cplusplus extern "C" { #endif -EOF +_LT_EOF # Now generate the symbol file. - eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | grep -v main >> conftest.$ac_ext' + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' - cat <> conftest.$ac_ext -#if defined (__STDC__) && __STDC__ -# define lt_ptr_t void * -#else -# define lt_ptr_t char * -# define const -#endif + cat <<_LT_EOF >> conftest.$ac_ext -/* The mapping between symbol names and symbols. */ +/* The mapping between symbol names and symbols. */ const struct { const char *name; - lt_ptr_t address; + void *address; } -lt_preloaded_symbols[] = +lt__PROGRAM__LTX_preloaded_symbols[] = { -EOF - $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (lt_ptr_t) \&\2},/" < "$nlist" | grep -v main >> conftest.$ac_ext - cat <<\EOF >> conftest.$ac_ext - {0, (lt_ptr_t) 0} + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} }; +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + #ifdef __cplusplus } #endif -EOF +_LT_EOF # Now try linking the two files. mv conftest.$ac_objext conftstm.$ac_objext lt_save_LIBS="$LIBS" lt_save_CFLAGS="$CFLAGS" LIBS="conftstm.$ac_objext" CFLAGS="$CFLAGS$lt_prog_compiler_no_builtin_flag" - if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 (eval $ac_link) 2>&5 ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && test -s conftest${ac_exeext}; then + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext}; then pipe_works=yes fi LIBS="$lt_save_LIBS" @@ -6467,7 +6259,7 @@ echo "$progname: failed program was:" >&5 cat conftest.$ac_ext >&5 fi - rm -f conftest* conftst* + rm -rf conftest* conftst* # Do not use the global_symbol_pipe unless it works. if test "$pipe_works" = yes; then @@ -6483,6009 +6275,3819 @@ lt_cv_sys_global_symbol_to_cdecl= fi if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then - { echo "$as_me:$LINENO: result: failed" >&5 -echo "${ECHO_T}failed" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: failed" >&5 +$as_echo "failed" >&6; } else - { echo "$as_me:$LINENO: result: ok" >&5 -echo "${ECHO_T}ok" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ok" >&5 +$as_echo "ok" >&6; } fi -{ echo "$as_me:$LINENO: checking for objdir" >&5 -echo $ECHO_N "checking for objdir... $ECHO_C" >&6; } -if test "${lt_cv_objdir+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - rm -f .libs 2>/dev/null -mkdir .libs 2>/dev/null -if test -d .libs; then - lt_cv_objdir=.libs -else - # MS-DOS does not allow filenames that begin with a dot. - lt_cv_objdir=_libs -fi -rmdir .libs 2>/dev/null -fi -{ echo "$as_me:$LINENO: result: $lt_cv_objdir" >&5 -echo "${ECHO_T}$lt_cv_objdir" >&6; } -objdir=$lt_cv_objdir -case $host_os in -aix3*) - # AIX sometimes has problems with the GCC collect2 program. For some - # reason, if we set the COLLECT_NAMES environment variable, the problems - # vanish in a puff of smoke. - if test "X${COLLECT_NAMES+set}" != Xset; then - COLLECT_NAMES= - export COLLECT_NAMES + + + + + + + + + + + + + + + + + +# Check whether --enable-libtool-lock was given. +if test "${enable_libtool_lock+set}" = set; then : + enableval=$enable_libtool_lock; +fi + +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE="32" + ;; + *ELF-64*) + HPUX_IA64_MODE="64" + ;; + esac fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out which ABI we are using. + echo '#line 6338 "configure"' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* ;; -esac -# Sed substitution that helps us do robust quoting. It backslashifies -# metacharacters that are still active within double-quoted strings. -Xsed='sed -e 1s/^X//' -sed_quote_subst='s/\([\\"\\`$\\\\]\)/\\\1/g' +x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_i386" + ;; + ppc64-*linux*|powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + ppc*-*linux*|powerpc*-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; -# Same as above, but do not quote variable references. -double_quote_subst='s/\([\\"\\`\\\\]\)/\\\1/g' +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler needs -belf" >&5 +$as_echo_n "checking whether the C compiler needs -belf... " >&6; } +if test "${lt_cv_cc_needs_belf+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu -# Sed substitution to delay expansion of an escaped shell variable in a -# double_quote_subst'ed string. -delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ -# Sed substitution to avoid accidental globbing in evaled expressions -no_glob_subst='s/\*/\\\*/g' +int +main () +{ -# Constants: -rm="rm -f" + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_cc_needs_belf=yes +else + lt_cv_cc_needs_belf=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu -# Global variables: -default_ofile=libtool -can_build_shared=yes +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_cc_needs_belf" >&5 +$as_echo "$lt_cv_cc_needs_belf" >&6; } + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +sparc*-*solaris*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) LD="${LD-ld} -m elf64_sparc" ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac -# All known linkers require a `.a' archive for static linking (except MSVC, -# which needs '.lib'). -libext=a -ltmain="$ac_aux_dir/ltmain.sh" -ofile="$default_ofile" -with_gnu_ld="$lt_cv_prog_gnu_ld" +need_locks="$enable_libtool_lock" -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args. -set dummy ${ac_tool_prefix}ar; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_AR+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 + + case $host_os in + rhapsody* | darwin*) + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}dsymutil", so it can be a program name with args. +set dummy ${ac_tool_prefix}dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_DSYMUTIL+set}" = set; then : + $as_echo_n "(cached) " >&6 else - if test -n "$AR"; then - ac_cv_prog_AR="$AR" # Let the user override the test. + if test -n "$DSYMUTIL"; then + ac_cv_prog_DSYMUTIL="$DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_AR="${ac_tool_prefix}ar" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + ac_cv_prog_DSYMUTIL="${ac_tool_prefix}dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi -AR=$ac_cv_prog_AR -if test -n "$AR"; then - { echo "$as_me:$LINENO: result: $AR" >&5 -echo "${ECHO_T}$AR" >&6; } +DSYMUTIL=$ac_cv_prog_DSYMUTIL +if test -n "$DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $DSYMUTIL" >&5 +$as_echo "$DSYMUTIL" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi fi -if test -z "$ac_cv_prog_AR"; then - ac_ct_AR=$AR - # Extract the first word of "ar", so it can be a program name with args. -set dummy ar; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_ac_ct_AR+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +if test -z "$ac_cv_prog_DSYMUTIL"; then + ac_ct_DSYMUTIL=$DSYMUTIL + # Extract the first word of "dsymutil", so it can be a program name with args. +set dummy dsymutil; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_DSYMUTIL+set}" = set; then : + $as_echo_n "(cached) " >&6 else - if test -n "$ac_ct_AR"; then - ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. + if test -n "$ac_ct_DSYMUTIL"; then + ac_cv_prog_ac_ct_DSYMUTIL="$ac_ct_DSYMUTIL" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_ac_ct_AR="ar" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + ac_cv_prog_ac_ct_DSYMUTIL="dsymutil" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi -ac_ct_AR=$ac_cv_prog_ac_ct_AR -if test -n "$ac_ct_AR"; then - { echo "$as_me:$LINENO: result: $ac_ct_AR" >&5 -echo "${ECHO_T}$ac_ct_AR" >&6; } +ac_ct_DSYMUTIL=$ac_cv_prog_ac_ct_DSYMUTIL +if test -n "$ac_ct_DSYMUTIL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_DSYMUTIL" >&5 +$as_echo "$ac_ct_DSYMUTIL" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi - if test "x$ac_ct_AR" = x; then - AR="false" + if test "x$ac_ct_DSYMUTIL" = x; then + DSYMUTIL=":" else case $cross_compiling:$ac_tool_warned in yes:) -{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&5 -echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&2;} +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac - AR=$ac_ct_AR + DSYMUTIL=$ac_ct_DSYMUTIL fi else - AR="$ac_cv_prog_AR" + DSYMUTIL="$ac_cv_prog_DSYMUTIL" fi -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. -set dummy ${ac_tool_prefix}ranlib; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_RANLIB+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}nmedit", so it can be a program name with args. +set dummy ${ac_tool_prefix}nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_NMEDIT+set}" = set; then : + $as_echo_n "(cached) " >&6 else - if test -n "$RANLIB"; then - ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. + if test -n "$NMEDIT"; then + ac_cv_prog_NMEDIT="$NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + ac_cv_prog_NMEDIT="${ac_tool_prefix}nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi -RANLIB=$ac_cv_prog_RANLIB -if test -n "$RANLIB"; then - { echo "$as_me:$LINENO: result: $RANLIB" >&5 -echo "${ECHO_T}$RANLIB" >&6; } +NMEDIT=$ac_cv_prog_NMEDIT +if test -n "$NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $NMEDIT" >&5 +$as_echo "$NMEDIT" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi fi -if test -z "$ac_cv_prog_RANLIB"; then - ac_ct_RANLIB=$RANLIB - # Extract the first word of "ranlib", so it can be a program name with args. -set dummy ranlib; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_ac_ct_RANLIB+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +if test -z "$ac_cv_prog_NMEDIT"; then + ac_ct_NMEDIT=$NMEDIT + # Extract the first word of "nmedit", so it can be a program name with args. +set dummy nmedit; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_NMEDIT+set}" = set; then : + $as_echo_n "(cached) " >&6 else - if test -n "$ac_ct_RANLIB"; then - ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. + if test -n "$ac_ct_NMEDIT"; then + ac_cv_prog_ac_ct_NMEDIT="$ac_ct_NMEDIT" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_ac_ct_RANLIB="ranlib" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + ac_cv_prog_ac_ct_NMEDIT="nmedit" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi -ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB -if test -n "$ac_ct_RANLIB"; then - { echo "$as_me:$LINENO: result: $ac_ct_RANLIB" >&5 -echo "${ECHO_T}$ac_ct_RANLIB" >&6; } +ac_ct_NMEDIT=$ac_cv_prog_ac_ct_NMEDIT +if test -n "$ac_ct_NMEDIT"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_NMEDIT" >&5 +$as_echo "$ac_ct_NMEDIT" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi - if test "x$ac_ct_RANLIB" = x; then - RANLIB=":" + if test "x$ac_ct_NMEDIT" = x; then + NMEDIT=":" else case $cross_compiling:$ac_tool_warned in yes:) -{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&5 -echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&2;} +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac - RANLIB=$ac_ct_RANLIB + NMEDIT=$ac_ct_NMEDIT fi else - RANLIB="$ac_cv_prog_RANLIB" + NMEDIT="$ac_cv_prog_NMEDIT" fi -if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. -set dummy ${ac_tool_prefix}strip; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_STRIP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}lipo", so it can be a program name with args. +set dummy ${ac_tool_prefix}lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_LIPO+set}" = set; then : + $as_echo_n "(cached) " >&6 else - if test -n "$STRIP"; then - ac_cv_prog_STRIP="$STRIP" # Let the user override the test. + if test -n "$LIPO"; then + ac_cv_prog_LIPO="$LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_STRIP="${ac_tool_prefix}strip" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + ac_cv_prog_LIPO="${ac_tool_prefix}lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi -STRIP=$ac_cv_prog_STRIP -if test -n "$STRIP"; then - { echo "$as_me:$LINENO: result: $STRIP" >&5 -echo "${ECHO_T}$STRIP" >&6; } +LIPO=$ac_cv_prog_LIPO +if test -n "$LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIPO" >&5 +$as_echo "$LIPO" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi fi -if test -z "$ac_cv_prog_STRIP"; then - ac_ct_STRIP=$STRIP - # Extract the first word of "strip", so it can be a program name with args. -set dummy strip; ac_word=$2 -{ echo "$as_me:$LINENO: checking for $ac_word" >&5 -echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; } -if test "${ac_cv_prog_ac_ct_STRIP+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +if test -z "$ac_cv_prog_LIPO"; then + ac_ct_LIPO=$LIPO + # Extract the first word of "lipo", so it can be a program name with args. +set dummy lipo; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_LIPO+set}" = set; then : + $as_echo_n "(cached) " >&6 else - if test -n "$ac_ct_STRIP"; then - ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. + if test -n "$ac_ct_LIPO"; then + ac_cv_prog_ac_ct_LIPO="$ac_ct_LIPO" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. - for ac_exec_ext in '' $ac_executable_extensions; do + for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then - ac_cv_prog_ac_ct_STRIP="strip" - echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5 + ac_cv_prog_ac_ct_LIPO="lipo" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done -done + done IFS=$as_save_IFS fi fi -ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP -if test -n "$ac_ct_STRIP"; then - { echo "$as_me:$LINENO: result: $ac_ct_STRIP" >&5 -echo "${ECHO_T}$ac_ct_STRIP" >&6; } +ac_ct_LIPO=$ac_cv_prog_ac_ct_LIPO +if test -n "$ac_ct_LIPO"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_LIPO" >&5 +$as_echo "$ac_ct_LIPO" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi - if test "x$ac_ct_STRIP" = x; then - STRIP=":" + if test "x$ac_ct_LIPO" = x; then + LIPO=":" else case $cross_compiling:$ac_tool_warned in yes:) -{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&5 -echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools -whose name does not start with the host triplet. If you think this -configuration is useful to you, please write to autoconf@gnu.org." >&2;} +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac - STRIP=$ac_ct_STRIP + LIPO=$ac_ct_LIPO fi else - STRIP="$ac_cv_prog_STRIP" + LIPO="$ac_cv_prog_LIPO" fi + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_OTOOL+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$OTOOL"; then + ac_cv_prog_OTOOL="$OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_OTOOL="${ac_tool_prefix}otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS -old_CC="$CC" -old_CFLAGS="$CFLAGS" +fi +fi +OTOOL=$ac_cv_prog_OTOOL +if test -n "$OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL" >&5 +$as_echo "$OTOOL" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi -# Set sane defaults for various variables -test -z "$AR" && AR=ar -test -z "$AR_FLAGS" && AR_FLAGS=cru -test -z "$AS" && AS=as -test -z "$CC" && CC=cc -test -z "$LTCC" && LTCC=$CC -test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS -test -z "$DLLTOOL" && DLLTOOL=dlltool -test -z "$LD" && LD=ld -test -z "$LN_S" && LN_S="ln -s" -test -z "$MAGIC_CMD" && MAGIC_CMD=file -test -z "$NM" && NM=nm -test -z "$SED" && SED=sed -test -z "$OBJDUMP" && OBJDUMP=objdump -test -z "$RANLIB" && RANLIB=: -test -z "$STRIP" && STRIP=: -test -z "$ac_objext" && ac_objext=o - -# Determine commands to create old-style static archives. -old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs$old_deplibs' -old_postinstall_cmds='chmod 644 $oldlib' -old_postuninstall_cmds= -if test -n "$RANLIB"; then - case $host_os in - openbsd*) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" - ;; - *) - old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" - ;; - esac - old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" fi - -for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac +if test -z "$ac_cv_prog_OTOOL"; then + ac_ct_OTOOL=$OTOOL + # Extract the first word of "otool", so it can be a program name with args. +set dummy otool; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_OTOOL+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL"; then + ac_cv_prog_ac_ct_OTOOL="$ac_ct_OTOOL" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_OTOOL="otool" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` - + done +IFS=$as_save_IFS -# Only perform the check for file, if the check method requires it -case $deplibs_check_method in -file_magic*) - if test "$file_magic_cmd" = '$MAGIC_CMD'; then - { echo "$as_me:$LINENO: checking for ${ac_tool_prefix}file" >&5 -echo $ECHO_N "checking for ${ac_tool_prefix}file... $ECHO_C" >&6; } -if test "${lt_cv_path_MAGIC_CMD+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +fi +ac_ct_OTOOL=$ac_cv_prog_ac_ct_OTOOL +if test -n "$ac_ct_OTOOL"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL" >&5 +$as_echo "$ac_ct_OTOOL" >&6; } else - case $MAGIC_CMD in -[\\/*] | ?:[\\/]*) - lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. - ;; -*) - lt_save_MAGIC_CMD="$MAGIC_CMD" - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" - for ac_dir in $ac_dummy; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/${ac_tool_prefix}file; then - lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file" - if test -n "$file_magic_test_file"; then - case $deplibs_check_method in - "file_magic "*) - file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` - MAGIC_CMD="$lt_cv_path_MAGIC_CMD" - if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | - $EGREP "$file_magic_regex" > /dev/null; then - : - else - cat <&2 - -*** Warning: the command libtool uses to detect shared libraries, -*** $file_magic_cmd, produces output that libtool cannot recognize. -*** The result is that libtool may fail to recognize shared libraries -*** as such. This will affect the creation of libtool libraries that -*** depend on shared libraries, but programs linked with such libtool -*** libraries will work regardless of this problem. Nevertheless, you -*** may want to report the problem to your system manager and/or to -*** bug-libtool@gnu.org + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi -EOF - fi ;; - esac - fi - break - fi - done - IFS="$lt_save_ifs" - MAGIC_CMD="$lt_save_MAGIC_CMD" - ;; + if test "x$ac_ct_OTOOL" = x; then + OTOOL=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; esac + OTOOL=$ac_ct_OTOOL + fi +else + OTOOL="$ac_cv_prog_OTOOL" fi -MAGIC_CMD="$lt_cv_path_MAGIC_CMD" -if test -n "$MAGIC_CMD"; then - { echo "$as_me:$LINENO: result: $MAGIC_CMD" >&5 -echo "${ECHO_T}$MAGIC_CMD" >&6; } + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}otool64", so it can be a program name with args. +set dummy ${ac_tool_prefix}otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_OTOOL64+set}" = set; then : + $as_echo_n "(cached) " >&6 else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi + if test -n "$OTOOL64"; then + ac_cv_prog_OTOOL64="$OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_OTOOL64="${ac_tool_prefix}otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS -if test -z "$lt_cv_path_MAGIC_CMD"; then - if test -n "$ac_tool_prefix"; then - { echo "$as_me:$LINENO: checking for file" >&5 -echo $ECHO_N "checking for file... $ECHO_C" >&6; } -if test "${lt_cv_path_MAGIC_CMD+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +fi +fi +OTOOL64=$ac_cv_prog_OTOOL64 +if test -n "$OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $OTOOL64" >&5 +$as_echo "$OTOOL64" >&6; } else - case $MAGIC_CMD in -[\\/*] | ?:[\\/]*) - lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. - ;; -*) - lt_save_MAGIC_CMD="$MAGIC_CMD" - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" - for ac_dir in $ac_dummy; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f $ac_dir/file; then - lt_cv_path_MAGIC_CMD="$ac_dir/file" - if test -n "$file_magic_test_file"; then - case $deplibs_check_method in - "file_magic "*) - file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` - MAGIC_CMD="$lt_cv_path_MAGIC_CMD" - if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | - $EGREP "$file_magic_regex" > /dev/null; then - : - else - cat <&2 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi -*** Warning: the command libtool uses to detect shared libraries, -*** $file_magic_cmd, produces output that libtool cannot recognize. -*** The result is that libtool may fail to recognize shared libraries -*** as such. This will affect the creation of libtool libraries that -*** depend on shared libraries, but programs linked with such libtool -*** libraries will work regardless of this problem. Nevertheless, you -*** may want to report the problem to your system manager and/or to -*** bug-libtool@gnu.org -EOF - fi ;; - esac - fi - break - fi - done - IFS="$lt_save_ifs" - MAGIC_CMD="$lt_save_MAGIC_CMD" - ;; -esac fi +if test -z "$ac_cv_prog_OTOOL64"; then + ac_ct_OTOOL64=$OTOOL64 + # Extract the first word of "otool64", so it can be a program name with args. +set dummy otool64; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_OTOOL64+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_OTOOL64"; then + ac_cv_prog_ac_ct_OTOOL64="$ac_ct_OTOOL64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_OTOOL64="otool64" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS -MAGIC_CMD="$lt_cv_path_MAGIC_CMD" -if test -n "$MAGIC_CMD"; then - { echo "$as_me:$LINENO: result: $MAGIC_CMD" >&5 -echo "${ECHO_T}$MAGIC_CMD" >&6; } +fi +fi +ac_ct_OTOOL64=$ac_cv_prog_ac_ct_OTOOL64 +if test -n "$ac_ct_OTOOL64"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_OTOOL64" >&5 +$as_echo "$ac_ct_OTOOL64" >&6; } else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi + if test "x$ac_ct_OTOOL64" = x; then + OTOOL64=":" else - MAGIC_CMD=: + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + OTOOL64=$ac_ct_OTOOL64 fi +else + OTOOL64="$ac_cv_prog_OTOOL64" fi - fi - ;; -esac -enable_dlopen=no -enable_win32_dll=no -# Check whether --enable-libtool-lock was given. -if test "${enable_libtool_lock+set}" = set; then - enableval=$enable_libtool_lock; -fi -test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes -# Check whether --with-pic was given. -if test "${with_pic+set}" = set; then - withval=$with_pic; pic_mode="$withval" -else - pic_mode=default -fi -test -z "$pic_mode" && pic_mode=default -# Use C for the default configuration in the libtool script -tagname= -lt_save_CC="$CC" -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -# Source file extension for C test sources. -ac_ext=c -# Object file extension for compiled C test sources. -objext=o -objext=$objext -# Code to be used in simple compile tests -lt_simple_compile_test_code="int some_variable = 0;\n" -# Code to be used in simple link tests -lt_simple_link_test_code='int main(){return(0);}\n' -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} -# Allow CC to be a program name with arguments. -compiler=$CC -# save warnings/boilerplate of simple test code -ac_outfile=conftest.$ac_objext -printf "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$rm conftest* -ac_outfile=conftest.$ac_objext -printf "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$rm conftest* -lt_prog_compiler_no_builtin_flag= -if test "$GCC" = yes; then - lt_prog_compiler_no_builtin_flag=' -fno-builtin' -{ echo "$as_me:$LINENO: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 -echo $ECHO_N "checking if $compiler supports -fno-rtti -fno-exceptions... $ECHO_C" >&6; } -if test "${lt_cv_prog_compiler_rtti_exceptions+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -single_module linker flag" >&5 +$as_echo_n "checking for -single_module linker flag... " >&6; } +if test "${lt_cv_apple_cc_single_mod+set}" = set; then : + $as_echo_n "(cached) " >&6 else - lt_cv_prog_compiler_rtti_exceptions=no - ac_outfile=conftest.$ac_objext - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="-fno-rtti -fno-exceptions" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7123: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:7127: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_cv_prog_compiler_rtti_exceptions=yes - fi - fi - $rm conftest* - + lt_cv_apple_cc_single_mod=no + if test -z "${LT_MULTI_MODULE}"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&5 + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&5 + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi fi -{ echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 -echo "${ECHO_T}$lt_cv_prog_compiler_rtti_exceptions" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_apple_cc_single_mod" >&5 +$as_echo "$lt_cv_apple_cc_single_mod" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for -exported_symbols_list linker flag" >&5 +$as_echo_n "checking for -exported_symbols_list linker flag... " >&6; } +if test "${lt_cv_ld_exported_symbols_list+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ -if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then - lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + lt_cv_ld_exported_symbols_list=yes else - : + lt_cv_ld_exported_symbols_list=no fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS="$save_LDFLAGS" fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_ld_exported_symbols_list" >&5 +$as_echo "$lt_cv_ld_exported_symbols_list" >&6; } + case $host_os in + rhapsody* | darwin1.[012]) + _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[91]*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + 10.[012]*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test "$lt_cv_apple_cc_single_mod" = "yes"; then + _lt_dar_single_mod='$single_module' + fi + if test "$lt_cv_ld_exported_symbols_list" = "yes"; then + _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + if test "$DSYMUTIL" != ":"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac -lt_prog_compiler_wl= -lt_prog_compiler_pic= -lt_prog_compiler_static= -{ echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5 -echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if test "${ac_cv_header_stdc+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include - if test "$GCC" = yes; then - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_static='-static' +int +main () +{ - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static='-Bstatic' - fi - ;; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext - amigaos*) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' - ;; +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include - beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : - mingw* | pw32* | os2*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic='-DDLL_EXPORT' - ;; +else + ac_cv_header_stdc=no +fi +rm -f conftest* - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - lt_prog_compiler_pic='-fno-common' - ;; +fi - interix3*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include - msdosdjgpp*) - # Just because we use GCC doesn't mean we suddenly get shared libraries - # on systems that don't support them. - lt_prog_compiler_can_build_shared=no - enable_shared=no - ;; +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : - sysv4*MP*) - if test -d /usr/nec; then - lt_prog_compiler_pic=-Kconform_pic - fi - ;; +else + ac_cv_header_stdc=no +fi +rm -f conftest* - hpux*) - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic='-fPIC' - ;; - esac - ;; +fi - *) - lt_prog_compiler_pic='-fPIC' - ;; - esac - else - # PORTME Check for flag to pass linker flags through the system compiler. - case $host_os in - aix*) - lt_prog_compiler_wl='-Wl,' - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static='-Bstatic' - else - lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' - fi - ;; - darwin*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - case $cc_basename in - xlc*) - lt_prog_compiler_pic='-qnocommon' - lt_prog_compiler_wl='-Wl,' - ;; - esac - ;; +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif - mingw* | pw32* | os2*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic='-DDLL_EXPORT' - ;; +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : - hpux9* | hpux10* | hpux11*) - lt_prog_compiler_wl='-Wl,' - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic='+Z' - ;; - esac - # Is there a better lt_prog_compiler_static that works with the bundled CC? - lt_prog_compiler_static='${wl}-a ${wl}archive' - ;; +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi - irix5* | irix6* | nonstopux*) - lt_prog_compiler_wl='-Wl,' - # PIC (with -KPIC) is the default. - lt_prog_compiler_static='-non_shared' - ;; +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then - newsos6) - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - ;; +$as_echo "#define STDC_HEADERS 1" >>confdefs.h - linux*) - case $cc_basename in - icc* | ecc*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-static' - ;; - pgcc* | pgf77* | pgf90* | pgf95*) - # Portland Group compilers (*not* the Pentium gcc compiler, - # which looks to be a dead project) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-fpic' - lt_prog_compiler_static='-Bstatic' - ;; - ccc*) - lt_prog_compiler_wl='-Wl,' - # All Alpha code is PIC. - lt_prog_compiler_static='-non_shared' - ;; - esac - ;; +fi - osf3* | osf4* | osf5*) - lt_prog_compiler_wl='-Wl,' - # All OSF/1 code is PIC. - lt_prog_compiler_static='-non_shared' - ;; +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +eval as_val=\$$as_ac_Header + if test "x$as_val" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF - solaris*) - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - case $cc_basename in - f77* | f90* | f95*) - lt_prog_compiler_wl='-Qoption ld ';; - *) - lt_prog_compiler_wl='-Wl,';; - esac - ;; +fi - sunos4*) - lt_prog_compiler_wl='-Qoption ld ' - lt_prog_compiler_pic='-PIC' - lt_prog_compiler_static='-Bstatic' - ;; +done - sysv4 | sysv4.2uw2* | sysv4.3*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - ;; - sysv4*MP*) - if test -d /usr/nec ;then - lt_prog_compiler_pic='-Kconform_pic' - lt_prog_compiler_static='-Bstatic' - fi - ;; +for ac_header in dlfcn.h +do : + ac_fn_c_check_header_compile "$LINENO" "dlfcn.h" "ac_cv_header_dlfcn_h" "$ac_includes_default +" +if test "x$ac_cv_header_dlfcn_h" = x""yes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_DLFCN_H 1 +_ACEOF - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_pic='-KPIC' - lt_prog_compiler_static='-Bstatic' - ;; +fi - unicos*) - lt_prog_compiler_wl='-Wl,' - lt_prog_compiler_can_build_shared=no - ;; +done - uts4*) - lt_prog_compiler_pic='-pic' - lt_prog_compiler_static='-Bstatic' - ;; - *) - lt_prog_compiler_can_build_shared=no - ;; - esac + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_CXX+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 fi +done + done +IFS=$as_save_IFS -{ echo "$as_me:$LINENO: result: $lt_prog_compiler_pic" >&5 -echo "${ECHO_T}$lt_prog_compiler_pic" >&6; } +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi -# -# Check to make sure the PIC flag actually works. -# -if test -n "$lt_prog_compiler_pic"; then -{ echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 -echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic works... $ECHO_C" >&6; } -if test "${lt_prog_compiler_pic_works+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then : + $as_echo_n "(cached) " >&6 else - lt_prog_compiler_pic_works=no - ac_outfile=conftest.$ac_objext - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$lt_prog_compiler_pic -DPIC" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7391: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:7395: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_prog_compiler_pic_works=yes - fi - fi - $rm conftest* + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS fi -{ echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_works" >&5 -echo "${ECHO_T}$lt_prog_compiler_pic_works" >&6; } - -if test x"$lt_prog_compiler_pic_works" = xyes; then - case $lt_prog_compiler_pic in - "" | " "*) ;; - *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; - esac +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } else - lt_prog_compiler_pic= - lt_prog_compiler_can_build_shared=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi -fi -case $host_os in - # For platforms which do not support PIC, -DPIC is meaningless: - *djgpp*) - lt_prog_compiler_pic= - ;; - *) - lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" - ;; + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; esac + CXX=$ac_ct_CXX + fi +fi -# -# Check to make sure the static flag actually works. -# -wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" -{ echo "$as_me:$LINENO: checking if $compiler static flag $lt_tmp_static_flag works" >&5 -echo $ECHO_N "checking if $compiler static flag $lt_tmp_static_flag works... $ECHO_C" >&6; } -if test "${lt_prog_compiler_static_works+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 + fi +fi +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if test "${ac_cv_cxx_compiler_gnu+set}" = set; then : + $as_echo_n "(cached) " >&6 else - lt_prog_compiler_static_works=no - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $lt_tmp_static_flag" - printf "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&5 - $echo "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - lt_prog_compiler_static_works=yes - fi - else - lt_prog_compiler_static_works=yes - fi - fi - $rm conftest* - LDFLAGS="$save_LDFLAGS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no fi -{ echo "$as_me:$LINENO: result: $lt_prog_compiler_static_works" >&5 -echo "${ECHO_T}$lt_prog_compiler_static_works" >&6; } +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu -if test x"$lt_prog_compiler_static_works" = xyes; then - : +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes else - lt_prog_compiler_static= + GXX= fi +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if test "${ac_cv_prog_cxx_g+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int +main () +{ -{ echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5 -echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6; } -if test "${lt_cv_prog_compiler_c_o+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes else - lt_cv_prog_compiler_c_o=no - $rm -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - printf "$lt_simple_compile_test_code" > conftest.$ac_ext + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:7495: $lt_compile\"" >&5) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&5 - echo "$as_me:7499: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - lt_cv_prog_compiler_c_o=yes - fi - fi - chmod u+w . 2>&5 - $rm conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files - $rm out/* && rmdir out - cd .. - rmdir conftest - $rm conftest* +int +main () +{ -fi -{ echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o" >&5 -echo "${ECHO_T}$lt_cv_prog_compiler_c_o" >&6; } + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ -hard_links="nottested" -if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then - # do not overwrite the value of need_locks provided by the user - { echo "$as_me:$LINENO: checking if we can lock with hard links" >&5 -echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6; } - hard_links=yes - $rm conftest* - ln conftest.a conftest.b 2>/dev/null && hard_links=no - touch conftest.a - ln conftest.a conftest.b 2>&5 || hard_links=no - ln conftest.a conftest.b 2>/dev/null && hard_links=no - { echo "$as_me:$LINENO: result: $hard_links" >&5 -echo "${ECHO_T}$hard_links" >&6; } - if test "$hard_links" = no; then - { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 -echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} - need_locks=warn +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" fi else - need_locks=no + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu -{ echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5 -echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6; } +depcc="$CXX" am_compiler_list= - runpath_var= - allow_undefined_flag= - enable_shared_with_static_runtimes=no - archive_cmds= - archive_expsym_cmds= - old_archive_From_new_cmds= - old_archive_from_expsyms_cmds= - export_dynamic_flag_spec= - whole_archive_flag_spec= - thread_safe_flag_spec= - hardcode_libdir_flag_spec= - hardcode_libdir_flag_spec_ld= - hardcode_libdir_separator= - hardcode_direct=no - hardcode_minus_L=no - hardcode_shlibpath_var=unsupported - link_all_deplibs=unknown - hardcode_automatic=no - module_cmds= - module_expsym_cmds= - always_export_symbols=no - export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - # include_expsyms should be a list of space-separated symbols to be *always* - # included in the symbol list - include_expsyms= - # exclude_expsyms can be an extended regexp of symbols to exclude - # it will be wrapped by ` (' and `)$', so one must not match beginning or - # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', - # as well as any symbol that contains `d'. - exclude_expsyms="_GLOBAL_OFFSET_TABLE_" - # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out - # platforms (ab)use it in PIC code, but their linkers get confused if - # the symbol is explicitly referenced. Since portable code cannot - # rely on this symbol name, it's probably fine to never include it in - # preloaded symbol tables. - extract_expsyms_cmds= - # Just being paranoid about ensuring that cc_basename is set. - for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if test "${am_cv_CXX_dependencies_compiler_type+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named `D' -- because `-MD' means `put the output + # in D'. + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub - case $host_os in - cygwin* | mingw* | pw32*) - # FIXME: the MSVC++ port hasn't been tested in a loooong time - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - if test "$GCC" != yes; then - with_gnu_ld=no - fi - ;; - interix*) - # we just hope/assume this is gcc and not c89 (= MSVC++) - with_gnu_ld=yes - ;; - openbsd*) - with_gnu_ld=no - ;; - esac + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac - ld_shlibs=yes - if test "$with_gnu_ld" = yes; then - # If archive_cmds runs LD, not CC, wlarc should be empty - wlarc='${wl}' + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using `: > sub/conftst$i.h' creates only sub/conftst1.h with + # Solaris 8's {/usr,}/bin/sh. + touch sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf - # Set some defaults for GNU ld with shared library support. These - # are reset later if shared libraries are not supported. Putting them - # here allows them to be overridden if necessary. - runpath_var=LD_RUN_PATH - hardcode_libdir_flag_spec='${wl}--rpath ${wl}$libdir' - export_dynamic_flag_spec='${wl}--export-dynamic' - # ancient GNU ld didn't support --whole-archive et. al. - if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then - whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + # We check with `-c' and `-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle `-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # after this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested + if test "x$enable_dependency_tracking" = xyes; then + continue else - whole_archive_flag_spec= - fi - supports_anon_versioning=no - case `$LD -v 2>/dev/null` in - *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 - *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... - *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... - *\ 2.11.*) ;; # other 2.11 versions - *) supports_anon_versioning=yes ;; - esac - - # See if GNU ld supports shared libraries. - case $host_os in - aix3* | aix4* | aix5*) - # On AIX/PPC, the GNU linker is very broken - if test "$host_cpu" != ia64; then - ld_shlibs=no - cat <&2 - -*** Warning: the GNU linker, at least up to release 2.9.1, is reported -*** to be unable to reliably create shared libraries on AIX. -*** Therefore, libtool is disabling shared libraries support. If you -*** really care for shared libraries, you may want to modify your PATH -*** so that a non-GNU linker is found, and then restart. - -EOF + break fi ;; - - amigaos*) - archive_cmds='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - - # Samuel A. Falvo II reports - # that the semantics of dynamic libraries on AmigaOS, at least up - # to version 4, is to share data among multiple programs linked - # with the same dynamic library. Since this doesn't match the - # behavior of shared libraries on other platforms, we can't use - # them. - ld_shlibs=no + msvisualcpp | msvcmsys) + # This compiler won't grok `-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= ;; - - beos*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - allow_undefined_flag=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - ld_shlibs=no + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CXX_dependencies_compiler_type=$depmode + break fi - ;; - - cygwin* | mingw* | pw32*) - # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, - # as there is no search path for DLLs. - hardcode_libdir_flag_spec='-L$libdir' - allow_undefined_flag=unsupported - always_export_symbols=no - enable_shared_with_static_runtimes=yes - export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS] /s/.* \([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW] /s/.* //'\'' | sort | uniq > $export_symbols' + fi + done - if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - ld_shlibs=no - fi - ;; + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi - interix3*) - hardcode_direct=no - hardcode_shlibpath_var=no - hardcode_libdir_flag_spec='${wl}-rpath,$libdir' - export_dynamic_flag_spec='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } +CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type - linux*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - tmp_addflag= - case $cc_basename,$host_cpu in - pgcc*) # Portland Group C compiler - whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag' - ;; - pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers - whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag -Mnomain' ;; - ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 - tmp_addflag=' -i_dynamic' ;; - efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 - tmp_addflag=' -i_dynamic -nofor_main' ;; - ifc* | ifort*) # Intel Fortran compiler - tmp_addflag=' -nofor_main' ;; - esac - archive_cmds='$CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then + am__fastdepCXX_TRUE= + am__fastdepCXX_FALSE='#' +else + am__fastdepCXX_TRUE='#' + am__fastdepCXX_FALSE= +fi - if test $supports_anon_versioning = yes; then - archive_expsym_cmds='$echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - $echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' - fi - else - ld_shlibs=no - fi - ;; - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' - wlarc= - else - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - fi - ;; +if test -n "$CXX" && ( test "X$CXX" != "Xno" && + ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || + (test "X$CXX" != "Xg++"))) ; then + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C++ preprocessor" >&5 +$as_echo_n "checking how to run the C++ preprocessor... " >&6; } +if test -z "$CXXCPP"; then + if test "${ac_cv_prog_CXXCPP+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CXXCPP needs to be expanded + for CXXCPP in "$CXX -E" "/lib/cpp" + do + ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : - solaris*) - if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then - ld_shlibs=no - cat <&2 +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext -*** Warning: The releases 2.8.* of the GNU linker cannot reliably -*** create shared libraries on Solaris systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.9.1 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext -EOF - elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs=no - fi - ;; +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi - sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) - case `$LD -v 2>&1` in - *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) - ld_shlibs=no - cat <<_LT_EOF 1>&2 + done + ac_cv_prog_CXXCPP=$CXXCPP -*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not -*** reliably create shared libraries on SCO systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.16.91.0.3 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. +fi + CXXCPP=$ac_cv_prog_CXXCPP +else + ac_cv_prog_CXXCPP=$CXXCPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXXCPP" >&5 +$as_echo "$CXXCPP" >&6; } +ac_preproc_ok=false +for ac_cxx_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : -_LT_EOF - ;; - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib' - archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname,-retain-symbols-file,$export_symbols -o $lib' - else - ld_shlibs=no - fi - ;; - esac - ;; +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.$ac_ext - sunos4*) - archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' - wlarc= - hardcode_direct=yes - hardcode_shlibpath_var=no - ;; + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_cxx_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.$ac_ext - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs=no - fi - ;; - esac +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : - if test "$ld_shlibs" = no; then - runpath_var= - hardcode_libdir_flag_spec= - export_dynamic_flag_spec= - whole_archive_flag_spec= - fi - else - # PORTME fill in a description of your system's linker (not GNU ld) - case $host_os in - aix3*) - allow_undefined_flag=unsupported - always_export_symbols=yes - archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' - # Note: this linker hardcodes the directories in LIBPATH if there - # are no directories specified by -L. - hardcode_minus_L=yes - if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then - # Neither direct hardcoding nor static linking is supported with a - # broken collect2. - hardcode_direct=unsupported - fi - ;; +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +_lt_caught_CXX_error=yes; } +fi - aix4* | aix5*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - if $NM -V 2>&1 | grep 'GNU' > /dev/null; then - export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' - else - export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' - fi - aix_use_runtimelinking=no +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[23]|aix4.[23].*|aix5*) - for ld_flag in $LDFLAGS; do - if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then - aix_use_runtimelinking=yes - break - fi - done - ;; - esac +else + _lt_caught_CXX_error=yes +fi - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - archive_cmds='' - hardcode_direct=yes - hardcode_libdir_separator=':' - link_all_deplibs=yes - if test "$GCC" = yes; then - case $host_os in aix4.[012]|aix4.[012].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && \ - strings "$collect2name" | grep resolve_lib_name >/dev/null - then - # We have reworked collect2 - hardcode_direct=yes - else - # We have old collect2 - hardcode_direct=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - hardcode_minus_L=yes - hardcode_libdir_flag_spec='-L$libdir' - hardcode_libdir_separator= - fi - ;; - esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to export. - always_export_symbols=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - allow_undefined_flag='-berok' - # Determine the default libpath from the value encoded in an empty executable. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ +# Set options -int -main () -{ - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 + enable_dlopen=no + + enable_win32_dll=no + + # Check whether --enable-shared was given. +if test "${enable_shared+set}" = set; then : + enableval=$enable_shared; p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac +else + enable_shared=yes fi -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" - archive_expsym_cmds="\$CC"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' - allow_undefined_flag="-z nodefs" - archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an empty executable. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -int -main () -{ - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 -fi -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - no_undefined_flag=' ${wl}-bernotok' - allow_undefined_flag=' ${wl}-berok' - # Exported symbols can be pulled into shared objects from archives - whole_archive_flag_spec='$convenience' - archive_cmds_need_lc=yes - # This is similar to how AIX traditionally builds its shared libraries. - archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + # Check whether --enable-static was given. +if test "${enable_static+set}" = set; then : + enableval=$enable_static; p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_static=yes fi - fi + done + IFS="$lt_save_ifs" ;; + esac +else + enable_static=yes +fi - amigaos*) - archive_cmds='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - # see comment about different semantics on the GNU ld section - ld_shlibs=no - ;; - bsdi[45]*) - export_dynamic_flag_spec=-rdynamic - ;; - cygwin* | mingw* | pw32*) - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - hardcode_libdir_flag_spec=' ' - allow_undefined_flag=unsupported - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" - # FIXME: Setting linknames here is a bad hack. - archive_cmds='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames=' - # The linker will automatically build a .lib file if we build a DLL. - old_archive_From_new_cmds='true' - # FIXME: Should let the user specify the lib program. - old_archive_cmds='lib /OUT:$oldlib$oldobjs$old_deplibs' - fix_srcfile_path='`cygpath -w "$srcfile"`' - enable_shared_with_static_runtimes=yes - ;; - darwin* | rhapsody*) - case $host_os in - rhapsody* | darwin1.[012]) - allow_undefined_flag='${wl}-undefined ${wl}suppress' - ;; - *) # Darwin 1.3 on - if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then - allow_undefined_flag='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - else - case ${MACOSX_DEPLOYMENT_TARGET} in - 10.[012]) - allow_undefined_flag='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - ;; - 10.*) - allow_undefined_flag='${wl}-undefined ${wl}dynamic_lookup' - ;; - esac - fi - ;; - esac - archive_cmds_need_lc=no - hardcode_direct=no - hardcode_automatic=yes - hardcode_shlibpath_var=unsupported - whole_archive_flag_spec='' - link_all_deplibs=yes - if test "$GCC" = yes ; then - output_verbose_link_cmd='echo' - archive_cmds='$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' - module_cmds='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - archive_expsym_cmds='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - module_expsym_cmds='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - else - case $cc_basename in - xlc*) - output_verbose_link_cmd='echo' - archive_cmds='$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}`echo $rpath/$soname` $verstring' - module_cmds='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - archive_expsym_cmds='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}$rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - module_expsym_cmds='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - ;; - *) - ld_shlibs=no - ;; - esac - fi - ;; - dgux*) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec='-L$libdir' - hardcode_shlibpath_var=no - ;; - freebsd1*) - ld_shlibs=no - ;; - # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor - # support. Future versions do this automatically, but an explicit c++rt0.o - # does not break anything, and helps significantly (at the cost of a little - # extra space). - freebsd2.2*) - archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' - hardcode_libdir_flag_spec='-R$libdir' - hardcode_direct=yes - hardcode_shlibpath_var=no - ;; - # Unfortunately, older versions of FreeBSD 2 do not have this feature. - freebsd2*) - archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct=yes - hardcode_minus_L=yes - hardcode_shlibpath_var=no - ;; - # FreeBSD 3 and greater uses gcc -shared to do shared libraries. - freebsd* | kfreebsd*-gnu | dragonfly*) - archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' - hardcode_libdir_flag_spec='-R$libdir' - hardcode_direct=yes - hardcode_shlibpath_var=no - ;; - hpux9*) - if test "$GCC" = yes; then - archive_cmds='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - archive_cmds='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - fi - hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' - hardcode_libdir_separator=: - hardcode_direct=yes +# Check whether --with-pic was given. +if test "${with_pic+set}" = set; then : + withval=$with_pic; pic_mode="$withval" +else + pic_mode=default +fi - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L=yes - export_dynamic_flag_spec='${wl}-E' - ;; - hpux10*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' - fi - if test "$with_gnu_ld" = no; then - hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' - hardcode_libdir_separator=: +test -z "$pic_mode" && pic_mode=default - hardcode_direct=yes - export_dynamic_flag_spec='${wl}-E' - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L=yes - fi - ;; - hpux11*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - case $host_cpu in - hppa*64*) - archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - archive_cmds='$CC -shared ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - else - case $host_cpu in - hppa*64*) - archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - fi - if test "$with_gnu_ld" = no; then - hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' - hardcode_libdir_separator=: - case $host_cpu in - hppa*64*|ia64*) - hardcode_libdir_flag_spec_ld='+b $libdir' - hardcode_direct=no - hardcode_shlibpath_var=no - ;; - *) - hardcode_direct=yes - export_dynamic_flag_spec='${wl}-E' - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L=yes - ;; - esac - fi - ;; - irix5* | irix6* | nonstopux*) - if test "$GCC" = yes; then - archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - archive_cmds='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - hardcode_libdir_flag_spec_ld='-rpath $libdir' - fi - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator=: - link_all_deplibs=yes - ;; - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out - else - archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF - fi - hardcode_libdir_flag_spec='-R$libdir' - hardcode_direct=yes - hardcode_shlibpath_var=no + # Check whether --enable-fast-install was given. +if test "${enable_fast_install+set}" = set; then : + enableval=$enable_fast_install; p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$lt_save_ifs" ;; + esac +else + enable_fast_install=yes +fi - newsos6) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct=yes - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator=: - hardcode_shlibpath_var=no - ;; - openbsd*) - hardcode_direct=yes - hardcode_shlibpath_var=no - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' - hardcode_libdir_flag_spec='${wl}-rpath,$libdir' - export_dynamic_flag_spec='${wl}-E' - else - case $host_os in - openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) - archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec='-R$libdir' - ;; - *) - archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - hardcode_libdir_flag_spec='${wl}-rpath,$libdir' - ;; - esac - fi - ;; - os2*) - hardcode_libdir_flag_spec='-L$libdir' - hardcode_minus_L=yes - allow_undefined_flag=unsupported - archive_cmds='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' - old_archive_From_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' - ;; - osf3*) - if test "$GCC" = yes; then - allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - allow_undefined_flag=' -expect_unresolved \*' - archive_cmds='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - fi - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator=: - ;; - osf4* | osf5*) # as osf3* with the addition of -msym flag - if test "$GCC" = yes; then - allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' - else - allow_undefined_flag=' -expect_unresolved \*' - archive_cmds='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~ - $LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib~$rm $lib.exp' - # Both c and cxx compiler support -rpath directly - hardcode_libdir_flag_spec='-rpath $libdir' - fi - hardcode_libdir_separator=: - ;; - solaris*) - no_undefined_flag=' -z text' - if test "$GCC" = yes; then - wlarc='${wl}' - archive_cmds='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp' - else - wlarc='' - archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' - archive_expsym_cmds='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' - fi - hardcode_libdir_flag_spec='-R$libdir' - hardcode_shlibpath_var=no - case $host_os in - solaris2.[0-5] | solaris2.[0-5].*) ;; - *) - # The compiler driver will combine linker options so we - # cannot just pass the convience library names through - # without $wl, iff we do not link with $LD. - # Luckily, gcc supports the same syntax we need for Sun Studio. - # Supported since Solaris 2.6 (maybe 2.5.1?) - case $wlarc in - '') - whole_archive_flag_spec='-z allextract$convenience -z defaultextract' ;; - *) - whole_archive_flag_spec='${wl}-z ${wl}allextract`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}-z ${wl}defaultextract' ;; - esac ;; - esac - link_all_deplibs=yes - ;; - sunos4*) - if test "x$host_vendor" = xsequent; then - # Use $CC to link under sequent, because it throws in some extra .o - # files that make .init and .fini sections work. - archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' - fi - hardcode_libdir_flag_spec='-L$libdir' - hardcode_direct=yes - hardcode_minus_L=yes - hardcode_shlibpath_var=no - ;; - sysv4) - case $host_vendor in - sni) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct=yes # is this really true??? - ;; - siemens) - ## LD is ld it makes a PLAMLIB - ## CC just makes a GrossModule. - archive_cmds='$LD -G -o $lib $libobjs $deplibs $linker_flags' - reload_cmds='$CC -r -o $output$reload_objs' - hardcode_direct=no - ;; - motorola) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct=no #Motorola manual says yes, but my tests say they lie - ;; - esac - runpath_var='LD_RUN_PATH' - hardcode_shlibpath_var=no - ;; - sysv4.3*) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_shlibpath_var=no - export_dynamic_flag_spec='-Bexport' - ;; - sysv4*MP*) - if test -d /usr/nec; then - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_shlibpath_var=no - runpath_var=LD_RUN_PATH - hardcode_runpath_var=yes - ld_shlibs=yes - fi - ;; +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ltmain" - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7*) - no_undefined_flag='${wl}-z,text' - archive_cmds_need_lc=no - hardcode_shlibpath_var=no - runpath_var='LD_RUN_PATH' +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' - if test "$GCC" = yes; then - archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - no_undefined_flag='${wl}-z,text' - allow_undefined_flag='${wl}-z,nodefs' - archive_cmds_need_lc=no - hardcode_shlibpath_var=no - hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' - hardcode_libdir_separator=':' - link_all_deplibs=yes - export_dynamic_flag_spec='${wl}-Bexport' - runpath_var='LD_RUN_PATH' - if test "$GCC" = yes; then - archive_cmds='$CC -shared ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds='$CC -G ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - uts4*) - archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec='-L$libdir' - hardcode_shlibpath_var=no - ;; - *) - ld_shlibs=no - ;; - esac - fi -{ echo "$as_me:$LINENO: result: $ld_shlibs" >&5 -echo "${ECHO_T}$ld_shlibs" >&6; } -test "$ld_shlibs" = no && can_build_shared=no -# -# Do we need to explicitly link libc? -# -case "x$archive_cmds_need_lc" in -x|xyes) - # Assume -lc should be added - archive_cmds_need_lc=yes - if test "$enable_shared" = yes && test "$GCC" = yes; then - case $archive_cmds in - *'~'*) - # FIXME: we may have to deal with multi-command sequences. - ;; - '$CC '*) - # Test whether the compiler implicitly links with -lc since on some - # systems, -lgcc has to come before -lc. If gcc already passes -lc - # to ld, don't add -lc before -lgcc. - { echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5 -echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6; } - $rm conftest* - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } 2>conftest.err; then - soname=conftest - lib=conftest - libobjs=conftest.$ac_objext - deplibs= - wl=$lt_prog_compiler_wl - pic_flag=$lt_prog_compiler_pic - compiler_flags=-v - linker_flags=-v - verstring= - output_objdir=. - libname=conftest - lt_save_allow_undefined_flag=$allow_undefined_flag - allow_undefined_flag= - if { (eval echo "$as_me:$LINENO: \"$archive_cmds 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5 - (eval $archive_cmds 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } - then - archive_cmds_need_lc=no - else - archive_cmds_need_lc=yes - fi - allow_undefined_flag=$lt_save_allow_undefined_flag - else - cat conftest.err 1>&5 - fi - $rm conftest* - { echo "$as_me:$LINENO: result: $archive_cmds_need_lc" >&5 -echo "${ECHO_T}$archive_cmds_need_lc" >&6; } - ;; - esac - fi - ;; -esac -{ echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5 -echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6; } -library_names_spec= -libname_spec='lib$name' -soname_spec= -shrext_cmds=".so" -postinstall_cmds= -postuninstall_cmds= -finish_cmds= -finish_eval= -shlibpath_var= -shlibpath_overrides_runpath=unknown -version_type=none -dynamic_linker="$host_os ld.so" -sys_lib_dlsearch_path_spec="/lib /usr/lib" -if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then - # if the path contains ";" then we assume it to be the separator - # otherwise default to the standard path separator (i.e. ":") - it is - # assumed that no part of a normal pathname contains ";" but that should - # okay in the real world where ";" in dirpaths is itself problematic. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi -else - sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" -fi -need_lib_prefix=unknown -hardcode_into_libs=no -# when you set need_version to no, make sure it does not cause -set_version -# flags to be left without arguments -need_version=unknown -case $host_os in -aix3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' - shlibpath_var=LIBPATH - # AIX 3 has no versioning support, so we append a major version to the name. - soname_spec='${libname}${release}${shared_ext}$major' - ;; -aix4* | aix5*) - version_type=linux - need_lib_prefix=no - need_version=no - hardcode_into_libs=yes - if test "$host_cpu" = ia64; then - # AIX 5 supports IA64 - library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - else - # With GCC up to 2.95.x, collect2 would create an import file - # for dependence libraries. The import file would start with - # the line `#! .'. This would cause the generated library to - # depend on `.', always an invalid library. This was fixed in - # development snapshots of GCC prior to 3.0. - case $host_os in - aix4 | aix4.[01] | aix4.[01].*) - if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' - echo ' yes ' - echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then - : - else - can_build_shared=no - fi - ;; - esac - # AIX (on Power*) has no versioning support, so currently we can not hardcode correct - # soname into executable. Probably we can add versioning support to - # collect2, so additional links can be useful in future. - if test "$aix_use_runtimelinking" = yes; then - # If using run time linking (on AIX 4.2 or later) use lib.so - # instead of lib.a to let people know that these are not - # typical AIX shared libraries. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - else - # We preserve .a as extension for shared libraries through AIX4.2 - # and later when we are not doing run time linking. - library_names_spec='${libname}${release}.a $libname.a' - soname_spec='${libname}${release}${shared_ext}$major' - fi - shlibpath_var=LIBPATH - fi - ;; -amigaos*) - library_names_spec='$libname.ixlibrary $libname.a' - # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' - ;; -beos*) - library_names_spec='${libname}${shared_ext}' - dynamic_linker="$host_os ld.so" - shlibpath_var=LIBRARY_PATH - ;; -bsdi[45]*) - version_type=linux - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" - sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" - # the default ld.so.conf also contains /usr/contrib/lib and - # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow - # libtool to hard-code these into programs - ;; -cygwin* | mingw* | pw32*) - version_type=windows - shrext_cmds=".dll" - need_version=no - need_lib_prefix=no - case $GCC,$host_os in - yes,cygwin* | yes,mingw* | yes,pw32*) - library_names_spec='$libname.dll.a' - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $rm \$dlpath' - shlibpath_overrides_runpath=yes - case $host_os in - cygwin*) - # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" - ;; - mingw*) - # MinGW DLLs use traditional 'lib' prefix - soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';[c-zC-Z]:/' >/dev/null; then - # It is most probably a Windows format PATH printed by - # mingw gcc, but we are running on Cygwin. Gcc prints its search - # path with ; separators, and with drive letters. We can handle the - # drive letters (cygwin fileutils understands them), so leave them, - # especially as we might pass files found there to a mingw objdump, - # which wouldn't understand a cygwinified path. Ahh. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi - ;; - pw32*) - # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - ;; - esac - ;; - *) - library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' - ;; - esac - dynamic_linker='Win32 ld.exe' - # FIXME: first we should search . and the directory the executable is in - shlibpath_var=PATH - ;; -darwin* | rhapsody*) - dynamic_linker="$host_os dyld" - version_type=darwin - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext' - soname_spec='${libname}${release}${major}$shared_ext' - shlibpath_overrides_runpath=yes - shlibpath_var=DYLD_LIBRARY_PATH - shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' - # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. - if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` - else - sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib' - fi - sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' - ;; -dgux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; -freebsd1*) - dynamic_linker=no - ;; -kfreebsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; +test -z "$LN_S" && LN_S="ln -s" -freebsd* | dragonfly*) - # DragonFly does not have aout. When/if they implement a new - # versioning mechanism, adjust this. - if test -x /usr/bin/objformat; then - objformat=`/usr/bin/objformat` - else - case $host_os in - freebsd[123]*) objformat=aout ;; - *) objformat=elf ;; - esac - fi - version_type=freebsd-$objformat - case $version_type in - freebsd-elf*) - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - need_version=no - need_lib_prefix=no - ;; - freebsd-*) - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' - need_version=yes - ;; - esac - shlibpath_var=LD_LIBRARY_PATH - case $host_os in - freebsd2*) - shlibpath_overrides_runpath=yes - ;; - freebsd3.[01]* | freebsdelf3.[01]*) - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ - freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - freebsd*) # from 4.6 on - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - esac - ;; -gnu*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - ;; -hpux9* | hpux10* | hpux11*) - # Give a soname corresponding to the major version so that dld.sl refuses to - # link against other versions. - version_type=sunos - need_lib_prefix=no - need_version=no - case $host_cpu in - ia64*) - shrext_cmds='.so' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.so" - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - if test "X$HPUX_IA64_MODE" = X32; then - sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" - else - sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" - fi - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - hppa*64*) - shrext_cmds='.sl' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.sl" - shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - *) - shrext_cmds='.sl' - dynamic_linker="$host_os dld.sl" - shlibpath_var=SHLIB_PATH - shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - ;; - esac - # HP-UX runs *really* slowly unless shared libraries are mode 555. - postinstall_cmds='chmod 555 $lib' - ;; -interix3*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; -irix5* | irix6* | nonstopux*) - case $host_os in - nonstopux*) version_type=nonstopux ;; - *) - if test "$lt_cv_prog_gnu_ld" = yes; then - version_type=linux - else - version_type=irix - fi ;; - esac - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' - case $host_os in - irix5* | nonstopux*) - libsuff= shlibsuff= - ;; - *) - case $LD in # libtool.m4 will add one of these switches to LD - *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") - libsuff= shlibsuff= libmagic=32-bit;; - *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") - libsuff=32 shlibsuff=N32 libmagic=N32;; - *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") - libsuff=64 shlibsuff=64 libmagic=64-bit;; - *) libsuff= shlibsuff= libmagic=never-match;; - esac - ;; - esac - shlibpath_var=LD_LIBRARY${shlibsuff}_PATH - shlibpath_overrides_runpath=no - sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" - sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" - hardcode_into_libs=yes - ;; -# No shared lib support for Linux oldld, aout, or coff. -linux*oldld* | linux*aout* | linux*coff*) - dynamic_linker=no - ;; -# This must be Linux ELF. -linux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes - # Append ld.so.conf contents to the search path - if test -f /etc/ld.so.conf; then - lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" - fi - # We used to test for /lib/ld.so.1 and disable shared libraries on - # powerpc, because MkLinux only supported shared libraries with the - # GNU dynamic linker. Since this was broken with cross compilers, - # most powerpc-linux boxes support dynamic linking these days and - # people can always --disable-shared, the test was removed, and we - # assume the GNU/Linux dynamic linker is in use. - dynamic_linker='GNU/Linux ld.so' - ;; -knetbsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; -netbsd*) - version_type=sunos - need_lib_prefix=no - need_version=no - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - dynamic_linker='NetBSD (a.out) ld.so' - else - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='NetBSD ld.elf_so' - fi - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; -newsos6) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; -nto-qnx*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; -openbsd*) - version_type=sunos - sys_lib_dlsearch_path_spec="/usr/lib" - need_lib_prefix=no - # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. - case $host_os in - openbsd3.3 | openbsd3.3.*) need_version=yes ;; - *) need_version=no ;; - esac - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - shlibpath_var=LD_LIBRARY_PATH - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - case $host_os in - openbsd2.[89] | openbsd2.[89].*) - shlibpath_overrides_runpath=no - ;; - *) - shlibpath_overrides_runpath=yes - ;; - esac - else - shlibpath_overrides_runpath=yes - fi - ;; +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi -os2*) - libname_spec='$name' - shrext_cmds=".dll" - need_lib_prefix=no - library_names_spec='$libname${shared_ext} $libname.a' - dynamic_linker='OS/2 ld.exe' - shlibpath_var=LIBPATH - ;; +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for objdir" >&5 +$as_echo_n "checking for objdir... " >&6; } +if test "${lt_cv_objdir+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_objdir" >&5 +$as_echo "$lt_cv_objdir" >&6; } +objdir=$lt_cv_objdir -osf3* | osf4* | osf5*) - version_type=osf - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" - sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" - ;; -solaris*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - # ldd complains unless libraries are executable - postinstall_cmds='chmod +x $lib' - ;; -sunos4*) - version_type=sunos - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - if test "$with_gnu_ld" = yes; then - need_lib_prefix=no - fi - need_version=yes - ;; -sysv4 | sysv4.3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - case $host_vendor in - sni) - shlibpath_overrides_runpath=no - need_lib_prefix=no - export_dynamic_flag_spec='${wl}-Blargedynsym' - runpath_var=LD_RUN_PATH - ;; - siemens) - need_lib_prefix=no - ;; - motorola) - need_lib_prefix=no - need_version=no - shlibpath_overrides_runpath=no - sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' - ;; - esac - ;; -sysv4*MP*) - if test -d /usr/nec ;then - version_type=linux - library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' - soname_spec='$libname${shared_ext}.$major' - shlibpath_var=LD_LIBRARY_PATH - fi - ;; +cat >>confdefs.h <<_ACEOF +#define LT_OBJDIR "$lt_cv_objdir/" +_ACEOF -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=freebsd-elf - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - if test "$with_gnu_ld" = yes; then - sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' - shlibpath_overrides_runpath=no - else - sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' - shlibpath_overrides_runpath=yes - case $host_os in - sco3.2v5*) - sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" - ;; - esac - fi - sys_lib_dlsearch_path_spec='/usr/lib' - ;; -uts4*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; -*) - dynamic_linker=no - ;; -esac -{ echo "$as_me:$LINENO: result: $dynamic_linker" >&5 -echo "${ECHO_T}$dynamic_linker" >&6; } -test "$dynamic_linker" = no && can_build_shared=no -variables_saved_for_relink="PATH $shlibpath_var $runpath_var" -if test "$GCC" = yes; then - variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" -fi -{ echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5 -echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6; } -hardcode_action= -if test -n "$hardcode_libdir_flag_spec" || \ - test -n "$runpath_var" || \ - test "X$hardcode_automatic" = "Xyes" ; then - # We can hardcode non-existant directories. - if test "$hardcode_direct" != no && - # If the only mechanism to avoid hardcoding is shlibpath_var, we - # have to relink, otherwise we might link with an installed library - # when we should be linking with a yet-to-be-installed one - ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, )" != no && - test "$hardcode_minus_L" != no; then - # Linking always hardcodes the temporary library directory. - hardcode_action=relink - else - # We can link without hardcoding, and we can hardcode nonexisting dirs. - hardcode_action=immediate - fi -else - # We cannot hardcode anything, or else we can only hardcode existing - # directories. - hardcode_action=unsupported -fi -{ echo "$as_me:$LINENO: result: $hardcode_action" >&5 -echo "${ECHO_T}$hardcode_action" >&6; } -if test "$hardcode_action" = relink; then - # Fast installation is not supported - enable_fast_install=no -elif test "$shlibpath_overrides_runpath" = yes || - test "$enable_shared" = no; then - # Fast installation is not necessary - enable_fast_install=needless -fi -striplib= -old_striplib= -{ echo "$as_me:$LINENO: checking whether stripping libraries is possible" >&5 -echo $ECHO_N "checking whether stripping libraries is possible... $ECHO_C" >&6; } -if test -n "$STRIP" && $STRIP -V 2>&1 | grep "GNU strip" >/dev/null; then - test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" - test -z "$striplib" && striplib="$STRIP --strip-unneeded" - { echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6; } -else -# FIXME - insert some real tests, host_os isn't really good enough - case $host_os in - darwin*) - if test -n "$STRIP" ; then - striplib="$STRIP -x" - { echo "$as_me:$LINENO: result: yes" >&5 -echo "${ECHO_T}yes" >&6; } - else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi - ;; - *) - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } - ;; - esac -fi -if test "x$enable_dlopen" != xyes; then - enable_dlopen=unknown - enable_dlopen_self=unknown - enable_dlopen_self_static=unknown -else - lt_cv_dlopen=no - lt_cv_dlopen_libs= - case $host_os in - beos*) - lt_cv_dlopen="load_add_on" - lt_cv_dlopen_libs= - lt_cv_dlopen_self=yes - ;; - mingw* | pw32*) - lt_cv_dlopen="LoadLibrary" - lt_cv_dlopen_libs= - ;; - cygwin*) - lt_cv_dlopen="dlopen" - lt_cv_dlopen_libs= - ;; - darwin*) - # if libdl is installed we need to link against it - { echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 -echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6; } -if test "${ac_cv_lib_dl_dlopen+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldl $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (); -int -main () -{ -return dlopen (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; + + + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - ac_cv_lib_dl_dlopen=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - ac_cv_lib_dl_dlopen=no -fi +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s/\(["`$\\]\)/\\\1/g' -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 -echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6; } -if test $ac_cv_lib_dl_dlopen = yes; then - lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" -else +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' - lt_cv_dlopen="dyld" - lt_cv_dlopen_libs= - lt_cv_dlopen_self=yes +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' -fi +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' - ;; +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' - *) - { echo "$as_me:$LINENO: checking for shl_load" >&5 -echo $ECHO_N "checking for shl_load... $ECHO_C" >&6; } -if test "${ac_cv_func_shl_load+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Define shl_load to an innocuous variant, in case declares shl_load. - For example, HP-UX 11i declares gettimeofday. */ -#define shl_load innocuous_shl_load +# Global variables: +ofile=libtool +can_build_shared=yes -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char shl_load (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ +# All known linkers require a `.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a -#ifdef __STDC__ -# include -#else -# include -#endif +with_gnu_ld="$lt_cv_prog_gnu_ld" -#undef shl_load +old_CC="$CC" +old_CFLAGS="$CFLAGS" -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char shl_load (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined __stub_shl_load || defined __stub___shl_load -choke me -#endif +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o -int -main () -{ -return shl_load (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - ac_cv_func_shl_load=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +for cc_temp in $compiler""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` - ac_cv_func_shl_load=no -fi -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -fi -{ echo "$as_me:$LINENO: result: $ac_cv_func_shl_load" >&5 -echo "${ECHO_T}$ac_cv_func_shl_load" >&6; } -if test $ac_cv_func_shl_load = yes; then - lt_cv_dlopen="shl_load" -else - { echo "$as_me:$LINENO: checking for shl_load in -ldld" >&5 -echo $ECHO_N "checking for shl_load in -ldld... $ECHO_C" >&6; } -if test "${ac_cv_lib_dld_shl_load+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${ac_tool_prefix}file" >&5 +$as_echo_n "checking for ${ac_tool_prefix}file... " >&6; } +if test "${lt_cv_path_MAGIC_CMD+set}" = set; then : + $as_echo_n "(cached) " >&6 else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldld $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/${ac_tool_prefix}file; then + lt_cv_path_MAGIC_CMD="$ac_dir/${ac_tool_prefix}file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char shl_load (); -int -main () -{ -return shl_load (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - ac_cv_lib_dld_shl_load=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org - ac_cv_lib_dld_shl_load=no +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac fi -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_dld_shl_load" >&5 -echo "${ECHO_T}$ac_cv_lib_dld_shl_load" >&6; } -if test $ac_cv_lib_dld_shl_load = yes; then - lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-dld" -else - { echo "$as_me:$LINENO: checking for dlopen" >&5 -echo $ECHO_N "checking for dlopen... $ECHO_C" >&6; } -if test "${ac_cv_func_dlopen+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Define dlopen to an innocuous variant, in case declares dlopen. - For example, HP-UX 11i declares gettimeofday. */ -#define dlopen innocuous_dlopen + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi -/* System header to define __stub macros and hopefully few prototypes, - which can conflict with char dlopen (); below. - Prefer to if __STDC__ is defined, since - exists even on freestanding compilers. */ -#ifdef __STDC__ -# include -#else -# include -#endif -#undef dlopen -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (); -/* The GNU C library defines this for functions which it implements - to always fail with ENOSYS. Some functions are actually named - something starting with __ and the normal name is an alias. */ -#if defined __stub_dlopen || defined __stub___dlopen -choke me -#endif -int -main () -{ -return dlopen (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - ac_cv_func_dlopen=yes +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for file" >&5 +$as_echo_n "checking for file... " >&6; } +if test "${lt_cv_path_MAGIC_CMD+set}" = set; then : + $as_echo_n "(cached) " >&6 else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 + case $MAGIC_CMD in +[\\/*] | ?:[\\/]*) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + ac_dummy="/usr/bin$PATH_SEPARATOR$PATH" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/file; then + lt_cv_path_MAGIC_CMD="$ac_dir/file" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 - ac_cv_func_dlopen=no -fi +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac fi -{ echo "$as_me:$LINENO: result: $ac_cv_func_dlopen" >&5 -echo "${ECHO_T}$ac_cv_func_dlopen" >&6; } -if test $ac_cv_func_dlopen = yes; then - lt_cv_dlopen="dlopen" -else - { echo "$as_me:$LINENO: checking for dlopen in -ldl" >&5 -echo $ECHO_N "checking for dlopen in -ldl... $ECHO_C" >&6; } -if test "${ac_cv_lib_dl_dlopen+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldl $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (); -int -main () -{ -return dlopen (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - ac_cv_lib_dl_dlopen=yes +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MAGIC_CMD" >&5 +$as_echo "$MAGIC_CMD" >&6; } else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - ac_cv_lib_dl_dlopen=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } fi -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS + + else + MAGIC_CMD=: + fi fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_dl_dlopen" >&5 -echo "${ECHO_T}$ac_cv_lib_dl_dlopen" >&6; } -if test $ac_cv_lib_dl_dlopen = yes; then - lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" -else - { echo "$as_me:$LINENO: checking for dlopen in -lsvld" >&5 -echo $ECHO_N "checking for dlopen in -lsvld... $ECHO_C" >&6; } -if test "${ac_cv_lib_svld_dlopen+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-lsvld $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char dlopen (); -int -main () -{ -return dlopen (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; + fi + ;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - ac_cv_lib_svld_dlopen=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - ac_cv_lib_svld_dlopen=no -fi +# Use C for the default configuration in the libtool script -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_svld_dlopen" >&5 -echo "${ECHO_T}$ac_cv_lib_svld_dlopen" >&6; } -if test $ac_cv_lib_svld_dlopen = yes; then - lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" -else - { echo "$as_me:$LINENO: checking for dld_link in -ldld" >&5 -echo $ECHO_N "checking for dld_link in -ldld... $ECHO_C" >&6; } -if test "${ac_cv_lib_dld_dld_link+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - ac_check_lib_save_LIBS=$LIBS -LIBS="-ldld $LIBS" -cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ +lt_save_CC="$CC" +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu -/* Override any GCC internal prototype to avoid an error. - Use char because int might match the return type of a GCC - builtin and then its argument prototype would still apply. */ -#ifdef __cplusplus -extern "C" -#endif -char dld_link (); -int -main () -{ -return dld_link (); - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - ac_cv_lib_dld_dld_link=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - ac_cv_lib_dld_dld_link=no -fi +# Source file extension for C test sources. +ac_ext=c -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -LIBS=$ac_check_lib_save_LIBS -fi -{ echo "$as_me:$LINENO: result: $ac_cv_lib_dld_dld_link" >&5 -echo "${ECHO_T}$ac_cv_lib_dld_dld_link" >&6; } -if test $ac_cv_lib_dld_dld_link = yes; then - lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-dld" -fi +# Object file extension for compiled C test sources. +objext=o +objext=$objext +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" -fi +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' -fi -fi -fi +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} -fi +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - ;; - esac +# Allow CC to be a program name with arguments. +compiler=$CC - if test "x$lt_cv_dlopen" != xno; then - enable_dlopen=yes - else - enable_dlopen=no - fi +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC - case $lt_cv_dlopen in - dlopen) - save_CPPFLAGS="$CPPFLAGS" - test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" +# save warnings/boilerplate of simple test code +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* - save_LDFLAGS="$LDFLAGS" - wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* - save_LIBS="$LIBS" - LIBS="$lt_cv_dlopen_libs $LIBS" - { echo "$as_me:$LINENO: checking whether a program can dlopen itself" >&5 -echo $ECHO_N "checking whether a program can dlopen itself... $ECHO_C" >&6; } -if test "${lt_cv_dlopen_self+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then : - lt_cv_dlopen_self=cross -else - lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 - lt_status=$lt_dlunknown - cat > conftest.$ac_ext < -#endif +lt_prog_compiler_no_builtin_flag= -#include - -#ifdef RTLD_GLOBAL -# define LT_DLGLOBAL RTLD_GLOBAL -#else -# ifdef DL_GLOBAL -# define LT_DLGLOBAL DL_GLOBAL -# else -# define LT_DLGLOBAL 0 -# endif -#endif - -/* We may have to define LT_DLLAZY_OR_NOW in the command line if we - find out it does not work in some platform. */ -#ifndef LT_DLLAZY_OR_NOW -# ifdef RTLD_LAZY -# define LT_DLLAZY_OR_NOW RTLD_LAZY -# else -# ifdef DL_LAZY -# define LT_DLLAZY_OR_NOW DL_LAZY -# else -# ifdef RTLD_NOW -# define LT_DLLAZY_OR_NOW RTLD_NOW -# else -# ifdef DL_NOW -# define LT_DLLAZY_OR_NOW DL_NOW -# else -# define LT_DLLAZY_OR_NOW 0 -# endif -# endif -# endif -# endif -#endif - -#ifdef __cplusplus -extern "C" void exit (int); -#endif - -void fnord() { int i=42;} -int main () -{ - void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); - int status = $lt_dlunknown; +if test "$GCC" = yes; then + lt_prog_compiler_no_builtin_flag=' -fno-builtin' - if (self) - { - if (dlsym (self,"fnord")) status = $lt_dlno_uscore; - else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; - /* dlclose (self); */ - } - else - puts (dlerror ()); + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 +$as_echo_n "checking if $compiler supports -fno-rtti -fno-exceptions... " >&6; } +if test "${lt_cv_prog_compiler_rtti_exceptions+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_rtti_exceptions=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="-fno-rtti -fno-exceptions" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:8254: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:8258: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_rtti_exceptions=yes + fi + fi + $RM conftest* - exit (status); -} -EOF - if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then - (./conftest; exit; ) >&5 2>/dev/null - lt_status=$? - case x$lt_status in - x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; - x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; - x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; - esac - else : - # compilation failed - lt_cv_dlopen_self=no - fi fi -rm -fr conftest* - +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 +$as_echo "$lt_cv_prog_compiler_rtti_exceptions" >&6; } +if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then + lt_prog_compiler_no_builtin_flag="$lt_prog_compiler_no_builtin_flag -fno-rtti -fno-exceptions" +else + : fi -{ echo "$as_me:$LINENO: result: $lt_cv_dlopen_self" >&5 -echo "${ECHO_T}$lt_cv_dlopen_self" >&6; } - if test "x$lt_cv_dlopen_self" = xyes; then - wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" - { echo "$as_me:$LINENO: checking whether a statically linked program can dlopen itself" >&5 -echo $ECHO_N "checking whether a statically linked program can dlopen itself... $ECHO_C" >&6; } -if test "${lt_cv_dlopen_self_static+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test "$cross_compiling" = yes; then : - lt_cv_dlopen_self_static=cross -else - lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 - lt_status=$lt_dlunknown - cat > conftest.$ac_ext < -#endif -#include -#ifdef RTLD_GLOBAL -# define LT_DLGLOBAL RTLD_GLOBAL -#else -# ifdef DL_GLOBAL -# define LT_DLGLOBAL DL_GLOBAL -# else -# define LT_DLGLOBAL 0 -# endif -#endif -/* We may have to define LT_DLLAZY_OR_NOW in the command line if we - find out it does not work in some platform. */ -#ifndef LT_DLLAZY_OR_NOW -# ifdef RTLD_LAZY -# define LT_DLLAZY_OR_NOW RTLD_LAZY -# else -# ifdef DL_LAZY -# define LT_DLLAZY_OR_NOW DL_LAZY -# else -# ifdef RTLD_NOW -# define LT_DLLAZY_OR_NOW RTLD_NOW -# else -# ifdef DL_NOW -# define LT_DLLAZY_OR_NOW DL_NOW -# else -# define LT_DLLAZY_OR_NOW 0 -# endif -# endif -# endif -# endif -#endif -#ifdef __cplusplus -extern "C" void exit (int); -#endif -void fnord() { int i=42;} -int main () -{ - void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); - int status = $lt_dlunknown; + lt_prog_compiler_wl= +lt_prog_compiler_pic= +lt_prog_compiler_static= - if (self) - { - if (dlsym (self,"fnord")) status = $lt_dlno_uscore; - else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; - /* dlclose (self); */ - } - else - puts (dlerror ()); +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } - exit (status); -} -EOF - if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5 - (eval $ac_link) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && test -s conftest${ac_exeext} 2>/dev/null; then - (./conftest; exit; ) >&5 2>/dev/null - lt_status=$? - case x$lt_status in - x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; - x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; - x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; - esac - else : - # compilation failed - lt_cv_dlopen_self_static=no - fi -fi -rm -fr conftest* + if test "$GCC" = yes; then + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_static='-static' + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + fi + ;; -fi -{ echo "$as_me:$LINENO: result: $lt_cv_dlopen_self_static" >&5 -echo "${ECHO_T}$lt_cv_dlopen_self_static" >&6; } - fi + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + lt_prog_compiler_pic='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; - CPPFLAGS="$save_CPPFLAGS" - LDFLAGS="$save_LDFLAGS" - LIBS="$save_LIBS" - ;; - esac + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; - case $lt_cv_dlopen_self in - yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; - *) enable_dlopen_self=unknown ;; - esac + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; - case $lt_cv_dlopen_self_static in - yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; - *) enable_dlopen_self_static=unknown ;; - esac -fi + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + lt_prog_compiler_pic='-fno-common' + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='-fPIC' + ;; + esac + ;; -# Report which library types will actually be built -{ echo "$as_me:$LINENO: checking if libtool supports shared libraries" >&5 -echo $ECHO_N "checking if libtool supports shared libraries... $ECHO_C" >&6; } -{ echo "$as_me:$LINENO: result: $can_build_shared" >&5 -echo "${ECHO_T}$can_build_shared" >&6; } - -{ echo "$as_me:$LINENO: checking whether to build shared libraries" >&5 -echo $ECHO_N "checking whether to build shared libraries... $ECHO_C" >&6; } -test "$can_build_shared" = "no" && enable_shared=no + interix[3-9]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; -# On AIX, shared libraries and static libraries use the same namespace, and -# are all built from PIC. -case $host_os in -aix3*) - test "$enable_shared" = yes && enable_static=no - if test -n "$RANLIB"; then - archive_cmds="$archive_cmds~\$RANLIB \$lib" - postinstall_cmds='$RANLIB $lib' - fi - ;; + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + lt_prog_compiler_can_build_shared=no + enable_shared=no + ;; -aix4* | aix5*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no - fi - ;; -esac -{ echo "$as_me:$LINENO: result: $enable_shared" >&5 -echo "${ECHO_T}$enable_shared" >&6; } + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; -{ echo "$as_me:$LINENO: checking whether to build static libraries" >&5 -echo $ECHO_N "checking whether to build static libraries... $ECHO_C" >&6; } -# Make sure either enable_shared or enable_static is yes. -test "$enable_shared" = yes || enable_static=yes -{ echo "$as_me:$LINENO: result: $enable_static" >&5 -echo "${ECHO_T}$enable_static" >&6; } - -# The else clause should only fire when bootstrapping the -# libtool distribution, otherwise you forgot to ship ltmain.sh -# with your package, and you will get complaints that there are -# no rules to generate ltmain.sh. -if test -f "$ltmain"; then - # See if we are running on zsh, and set the options which allow our commands through - # without removal of \ escapes. - if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST - fi - # Now quote all the things that may contain metacharacters while being - # careful not to overquote the AC_SUBSTed values. We take copies of the - # variables and quote the copies for generation of the libtool script. - for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC LTCFLAGS NM \ - SED SHELL STRIP \ - libname_spec library_names_spec soname_spec extract_expsyms_cmds \ - old_striplib striplib file_magic_cmd finish_cmds finish_eval \ - deplibs_check_method reload_flag reload_cmds need_locks \ - lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \ - lt_cv_sys_global_symbol_to_c_name_address \ - sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ - old_postinstall_cmds old_postuninstall_cmds \ - compiler \ - CC \ - LD \ - lt_prog_compiler_wl \ - lt_prog_compiler_pic \ - lt_prog_compiler_static \ - lt_prog_compiler_no_builtin_flag \ - export_dynamic_flag_spec \ - thread_safe_flag_spec \ - whole_archive_flag_spec \ - enable_shared_with_static_runtimes \ - old_archive_cmds \ - old_archive_from_new_cmds \ - predep_objects \ - postdep_objects \ - predeps \ - postdeps \ - compiler_lib_search_path \ - archive_cmds \ - archive_expsym_cmds \ - postinstall_cmds \ - postuninstall_cmds \ - old_archive_from_expsyms_cmds \ - allow_undefined_flag \ - no_undefined_flag \ - export_symbols_cmds \ - hardcode_libdir_flag_spec \ - hardcode_libdir_flag_spec_ld \ - hardcode_libdir_separator \ - hardcode_automatic \ - module_cmds \ - module_expsym_cmds \ - lt_cv_prog_compiler_c_o \ - exclude_expsyms \ - include_expsyms; do - - case $var in - old_archive_cmds | \ - old_archive_from_new_cmds | \ - archive_cmds | \ - archive_expsym_cmds | \ - module_cmds | \ - module_expsym_cmds | \ - old_archive_from_expsyms_cmds | \ - export_symbols_cmds | \ - extract_expsyms_cmds | reload_cmds | finish_cmds | \ - postinstall_cmds | postuninstall_cmds | \ - old_postinstall_cmds | old_postuninstall_cmds | \ - sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) - # Double-quote double-evaled strings. - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" + sysv4*MP*) + if test -d /usr/nec; then + lt_prog_compiler_pic=-Kconform_pic + fi ;; + *) - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" + lt_prog_compiler_pic='-fPIC' ;; esac - done - - case $lt_echo in - *'\$0 --fallback-echo"') - lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'` - ;; - esac - -cfgfile="${ofile}T" - trap "$rm \"$cfgfile\"; exit 1" 1 2 15 - $rm -f "$cfgfile" - { echo "$as_me:$LINENO: creating $ofile" >&5 -echo "$as_me: creating $ofile" >&6;} - - cat <<__EOF__ >> "$cfgfile" -#! $SHELL + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + lt_prog_compiler_wl='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static='-Bstatic' + else + lt_prog_compiler_static='-bnso -bI:/lib/syscalls.exp' + fi + ;; -# `$echo "$cfgfile" | sed 's%^.*/%%'` - Provide generalized library-building support services. -# Generated automatically by $PROGRAM (GNU $PACKAGE $VERSION$TIMESTAMP) -# NOTE: Changes made to this file will be lost: look at ltmain.sh. -# -# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001 -# Free Software Foundation, Inc. -# -# This file is part of GNU Libtool: -# Originally by Gordon Matzigkeit , 1996 -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# 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 the GNU -# General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# As a special exception to the GNU General Public License, if you -# distribute this file as part of a program that contains a -# configuration script generated by Autoconf, you may include it under -# the same distribution terms that you use for the rest of that program. + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + lt_prog_compiler_pic='-DDLL_EXPORT' + ;; -# A sed program that does not truncate output. -SED=$lt_SED + hpux9* | hpux10* | hpux11*) + lt_prog_compiler_wl='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + lt_prog_compiler_static='${wl}-a ${wl}archive' + ;; -# Sed that helps us avoid accidentally triggering echo(1) options like -n. -Xsed="$SED -e 1s/^X//" + irix5* | irix6* | nonstopux*) + lt_prog_compiler_wl='-Wl,' + # PIC (with -KPIC) is the default. + lt_prog_compiler_static='-non_shared' + ;; -# The HP-UX ksh and POSIX shell print the target directory to stdout -# if CDPATH is set. -(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + linux* | k*bsd*-gnu) + case $cc_basename in + # old Intel for x86_64 which still supported -KPIC. + ecc*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fPIC' + lt_prog_compiler_static='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='--shared' + lt_prog_compiler_static='--static' + ;; + pgcc* | pgf77* | pgf90* | pgf95*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-fpic' + lt_prog_compiler_static='-Bstatic' + ;; + ccc*) + lt_prog_compiler_wl='-Wl,' + # All Alpha code is PIC. + lt_prog_compiler_static='-non_shared' + ;; + xl*) + # IBM XL C 8.0/Fortran 10.1 on PPC + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-qpic' + lt_prog_compiler_static='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C 5.9 + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='-Wl,' + ;; + *Sun\ F*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + lt_prog_compiler_wl='' + ;; + esac + ;; + esac + ;; -# The names of the tagged configurations supported by this script. -available_tags= + newsos6) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; -# ### BEGIN LIBTOOL CONFIG + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic='-fPIC -shared' + ;; -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: + osf3* | osf4* | osf5*) + lt_prog_compiler_wl='-Wl,' + # All OSF/1 code is PIC. + lt_prog_compiler_static='-non_shared' + ;; -# Shell to use when invoking shell scripts. -SHELL=$lt_SHELL + rdos*) + lt_prog_compiler_static='-non_shared' + ;; -# Whether or not to build shared libraries. -build_libtool_libs=$enable_shared + solaris*) + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + case $cc_basename in + f77* | f90* | f95*) + lt_prog_compiler_wl='-Qoption ld ';; + *) + lt_prog_compiler_wl='-Wl,';; + esac + ;; -# Whether or not to build static libraries. -build_old_libs=$enable_static + sunos4*) + lt_prog_compiler_wl='-Qoption ld ' + lt_prog_compiler_pic='-PIC' + lt_prog_compiler_static='-Bstatic' + ;; -# Whether or not to add -lc for building shared libraries. -build_libtool_need_lc=$archive_cmds_need_lc + sysv4 | sysv4.2uw2* | sysv4.3*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; -# Whether or not to disallow shared libs when runtime libs are static -allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes + sysv4*MP*) + if test -d /usr/nec ;then + lt_prog_compiler_pic='-Kconform_pic' + lt_prog_compiler_static='-Bstatic' + fi + ;; -# Whether or not to optimize for fast installation. -fast_install=$enable_fast_install + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_pic='-KPIC' + lt_prog_compiler_static='-Bstatic' + ;; -# The host system. -host_alias=$host_alias -host=$host -host_os=$host_os + unicos*) + lt_prog_compiler_wl='-Wl,' + lt_prog_compiler_can_build_shared=no + ;; -# The build system. -build_alias=$build_alias -build=$build -build_os=$build_os + uts4*) + lt_prog_compiler_pic='-pic' + lt_prog_compiler_static='-Bstatic' + ;; -# An echo program that does not interpret backslashes. -echo=$lt_echo + *) + lt_prog_compiler_can_build_shared=no + ;; + esac + fi -# The archiver. -AR=$lt_AR -AR_FLAGS=$lt_AR_FLAGS +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic= + ;; + *) + lt_prog_compiler_pic="$lt_prog_compiler_pic -DPIC" + ;; +esac +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_prog_compiler_pic" >&5 +$as_echo "$lt_prog_compiler_pic" >&6; } -# A C compiler. -LTCC=$lt_LTCC -# LTCC compiler flags. -LTCFLAGS=$lt_LTCFLAGS -# A language-specific compiler. -CC=$lt_compiler -# Is the compiler the GNU C compiler? -with_gcc=$GCC -# An ERE matcher. -EGREP=$lt_EGREP -# The linker used to build libraries. -LD=$lt_LD +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic works... " >&6; } +if test "${lt_cv_prog_compiler_pic_works+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic -DPIC" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:8593: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:8597: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works=yes + fi + fi + $RM conftest* -# Whether we need hard or soft links. -LN_S=$lt_LN_S +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works" >&6; } -# A BSD-compatible nm program. -NM=$lt_NM +if test x"$lt_cv_prog_compiler_pic_works" = xyes; then + case $lt_prog_compiler_pic in + "" | " "*) ;; + *) lt_prog_compiler_pic=" $lt_prog_compiler_pic" ;; + esac +else + lt_prog_compiler_pic= + lt_prog_compiler_can_build_shared=no +fi -# A symbol stripping program -STRIP=$lt_STRIP +fi -# Used to examine libraries when file_magic_cmd begins "file" -MAGIC_CMD=$MAGIC_CMD -# Used on cygwin: DLL creation program. -DLLTOOL="$DLLTOOL" -# Used on cygwin: object dumper. -OBJDUMP="$OBJDUMP" -# Used on cygwin: assembler. -AS="$AS" -# The name of the directory that contains temporary libtool files. -objdir=$objdir -# How to create reloadable object files. -reload_flag=$lt_reload_flag -reload_cmds=$lt_reload_cmds +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl eval lt_tmp_static_flag=\"$lt_prog_compiler_static\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if test "${lt_cv_prog_compiler_static_works+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works=yes + fi + else + lt_cv_prog_compiler_static_works=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" -# How to pass a linker flag through the compiler. -wl=$lt_lt_prog_compiler_wl +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works" >&5 +$as_echo "$lt_cv_prog_compiler_static_works" >&6; } -# Object file suffix (normally "o"). -objext="$ac_objext" +if test x"$lt_cv_prog_compiler_static_works" = xyes; then + : +else + lt_prog_compiler_static= +fi -# Old archive suffix (normally "a"). -libext="$libext" -# Shared library suffix (normally ".so"). -shrext_cmds='$shrext_cmds' -# Executable file suffix (normally ""). -exeext="$exeext" - -# Additional compiler flags for building library objects. -pic_flag=$lt_lt_prog_compiler_pic -pic_mode=$pic_mode - -# What is the maximum length of a command? -max_cmd_len=$lt_cv_sys_max_cmd_len -# Does compiler simultaneously support -c and -o options? -compiler_c_o=$lt_lt_cv_prog_compiler_c_o - -# Must we lock files when doing compilation? -need_locks=$lt_need_locks - -# Do we need the lib prefix for modules? -need_lib_prefix=$need_lib_prefix -# Do we need a version for libraries? -need_version=$need_version -# Whether dlopen is supported. -dlopen_support=$enable_dlopen -# Whether dlopen of programs is supported. -dlopen_self=$enable_dlopen_self + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if test "${lt_cv_prog_compiler_c_o+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext -# Whether dlopen of statically linked programs is supported. -dlopen_self_static=$enable_dlopen_self_static + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:8698: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:8702: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* -# Compiler flag to prevent dynamic linking. -link_static_flag=$lt_lt_prog_compiler_static +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } -# Compiler flag to turn off builtin functions. -no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag -# Compiler flag to allow reflexive dlopens. -export_dynamic_flag_spec=$lt_export_dynamic_flag_spec -# Compiler flag to generate shared objects directly from archives. -whole_archive_flag_spec=$lt_whole_archive_flag_spec -# Compiler flag to generate thread-safe objects. -thread_safe_flag_spec=$lt_thread_safe_flag_spec -# Library versioning type. -version_type=$version_type -# Format of library name prefix. -libname_spec=$lt_libname_spec + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if test "${lt_cv_prog_compiler_c_o+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext -# List of archive names. First name is the real one, the rest are links. -# The last name is the one that the linker finds with -lNAME. -library_names_spec=$lt_library_names_spec + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:8753: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:8757: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* -# The coded name of the library, if different from the real name. -soname_spec=$lt_soname_spec +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o" >&5 +$as_echo "$lt_cv_prog_compiler_c_o" >&6; } -# Commands used to build and install an old-style archive. -RANLIB=$lt_RANLIB -old_archive_cmds=$lt_old_archive_cmds -old_postinstall_cmds=$lt_old_postinstall_cmds -old_postuninstall_cmds=$lt_old_postuninstall_cmds -# Create an old-style archive from a shared archive. -old_archive_from_new_cmds=$lt_old_archive_from_new_cmds -# Create a temporary old-style archive to link instead of a shared archive. -old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds -# Commands used to build and install a shared archive. -archive_cmds=$lt_archive_cmds -archive_expsym_cmds=$lt_archive_expsym_cmds -postinstall_cmds=$lt_postinstall_cmds -postuninstall_cmds=$lt_postuninstall_cmds +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test "$hard_links" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi -# Commands used to build a loadable module (assumed same as above if empty) -module_cmds=$lt_module_cmds -module_expsym_cmds=$lt_module_expsym_cmds -# Commands to strip libraries. -old_striplib=$lt_old_striplib -striplib=$lt_striplib -# Dependencies to place before the objects being linked to create a -# shared library. -predep_objects=$lt_predep_objects -# Dependencies to place after the objects being linked to create a -# shared library. -postdep_objects=$lt_postdep_objects -# Dependencies to place before the objects being linked to create a -# shared library. -predeps=$lt_predeps -# Dependencies to place after the objects being linked to create a -# shared library. -postdeps=$lt_postdeps + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } -# The library search path used internally by the compiler when linking -# a shared library. -compiler_lib_search_path=$lt_compiler_lib_search_path + runpath_var= + allow_undefined_flag= + always_export_symbols=no + archive_cmds= + archive_expsym_cmds= + compiler_needs_object=no + enable_shared_with_static_runtimes=no + export_dynamic_flag_spec= + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + hardcode_automatic=no + hardcode_direct=no + hardcode_direct_absolute=no + hardcode_libdir_flag_spec= + hardcode_libdir_flag_spec_ld= + hardcode_libdir_separator= + hardcode_minus_L=no + hardcode_shlibpath_var=unsupported + inherit_rpath=no + link_all_deplibs=unknown + module_cmds= + module_expsym_cmds= + old_archive_from_new_cmds= + old_archive_from_expsyms_cmds= + thread_safe_flag_spec= + whole_archive_flag_spec= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + include_expsyms= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + exclude_expsyms='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. + extract_expsyms_cmds= -# Method to check whether dependent libraries are shared objects. -deplibs_check_method=$lt_deplibs_check_method + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; + esac -# Command to use when deplibs_check_method == file_magic. -file_magic_cmd=$lt_file_magic_cmd + ld_shlibs=yes + if test "$with_gnu_ld" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' -# Flag that allows shared libraries with undefined symbols to be built. -allow_undefined_flag=$lt_allow_undefined_flag + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec= + fi + supports_anon_versioning=no + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac -# Flag that forces no undefined symbols. -no_undefined_flag=$lt_no_undefined_flag + # See if GNU ld supports shared libraries. + case $host_os in + aix[3-9]*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 -# Commands used to finish a libtool library installation in a directory. -finish_cmds=$lt_finish_cmds +*** Warning: the GNU linker, at least up to release 2.9.1, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to modify your PATH +*** so that a non-GNU linker is found, and then restart. -# Same as above, but a single script fragment to be evaled but not shown. -finish_eval=$lt_finish_eval +_LT_EOF + fi + ;; -# Take the output of nm and produce a listing of raw symbols and C names. -global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; -# Transform the output of nm in a proper C declaration -global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs=no + fi + ;; -# Transform the output of nm in a C name address pair -global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, ) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + allow_undefined_flag=unsupported + always_export_symbols=no + enable_shared_with_static_runtimes=yes + export_symbols_cmds='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW][ ]/s/.*[ ]//'\'' | sort | uniq > $export_symbols' -# This is the shared library runtime path variable. -runpath_var=$runpath_var - -# This is the shared library path variable. -shlibpath_var=$shlibpath_var + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs=no + fi + ;; -# Is shlibpath searched before the hard-coded library search path? -shlibpath_overrides_runpath=$shlibpath_overrides_runpath + interix[3-9]*) + hardcode_direct=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; -# How to hardcode a shared library path into an executable. -hardcode_action=$hardcode_action + gnu* | linux* | tpf* | k*bsd*-gnu) + tmp_diet=no + if test "$host_os" = linux-dietlibc; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test "$tmp_diet" = no + then + tmp_addflag= + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers + whole_archive_flag_spec='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + whole_archive_flag_spec= + tmp_sharedflag='--shared' ;; + xl[cC]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + whole_archive_flag_spec='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + compiler_needs_object=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + archive_cmds='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' -# Whether we should hardcode library paths into libraries. -hardcode_into_libs=$hardcode_into_libs + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi -# Flag to hardcode \$libdir into a binary during linking. -# This must work even if \$libdir does not exist. -hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec + case $cc_basename in + xlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + whole_archive_flag_spec='--whole-archive$convenience --no-whole-archive' + hardcode_libdir_flag_spec= + hardcode_libdir_flag_spec_ld='-rpath $libdir' + archive_cmds='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + ld_shlibs=no + fi + ;; -# If ld is used when linking, flag to hardcode \$libdir into -# a binary during linking. This must work even if \$libdir does -# not exist. -hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; -# Whether we need a single -rpath flag with a separated argument. -hardcode_libdir_separator=$lt_hardcode_libdir_separator + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + ld_shlibs=no + cat <<_LT_EOF 1>&2 -# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the -# resulting binary. -hardcode_direct=$hardcode_direct +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. -# Set to yes if using the -LDIR flag during linking hardcodes DIR into the -# resulting binary. -hardcode_minus_L=$hardcode_minus_L +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; -# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into -# the resulting binary. -hardcode_shlibpath_var=$hardcode_shlibpath_var + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) + ld_shlibs=no + cat <<_LT_EOF 1>&2 -# Set to yes if building a shared library automatically hardcodes DIR into the library -# and all subsequent libraries and executables linked against it. -hardcode_automatic=$hardcode_automatic +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. -# Variables whose values should be saved in libtool wrapper scripts and -# restored at relink time. -variables_saved_for_relink="$variables_saved_for_relink" +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac + ;; -# Whether libtool must link a program against all its dependency libraries. -link_all_deplibs=$link_all_deplibs + sunos4*) + archive_cmds='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; -# Compile-time system search path for libraries -sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + ld_shlibs=no + fi + ;; + esac -# Run-time system search path for libraries -sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + if test "$ld_shlibs" = no; then + runpath_var= + hardcode_libdir_flag_spec= + export_dynamic_flag_spec= + whole_archive_flag_spec= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + allow_undefined_flag=unsupported + always_export_symbols=yes + archive_expsym_cmds='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; -# Fix the shell variable \$srcfile for the compiler. -fix_srcfile_path="$fix_srcfile_path" + aix[4-9]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no -# Set to yes if exported symbols are required. -always_export_symbols=$always_export_symbols + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac -# The commands to list exported symbols. -export_symbols_cmds=$lt_export_symbols_cmds + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi -# The commands to extract the exported symbol list from a shared archive. -extract_expsyms_cmds=$lt_extract_expsyms_cmds + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. -# Symbols that should not be listed in the preloaded symbols. -exclude_expsyms=$lt_exclude_expsyms + archive_cmds='' + hardcode_direct=yes + hardcode_direct_absolute=yes + hardcode_libdir_separator=':' + link_all_deplibs=yes + file_list_spec='${wl}-f,' -# Symbols that must always be exported. -include_expsyms=$lt_include_expsyms + if test "$GCC" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi -# ### END LIBTOOL CONFIG + export_dynamic_flag_spec='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + always_export_symbols=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ -__EOF__ +int +main () +{ + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : - case $host_os in - aix3*) - cat <<\EOF >> "$cfgfile" +lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\(.*\)$/\1/ + p + } + }' +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi -# AIX sometimes has problems with the GCC collect2 program. For some -# reason, if we set the COLLECT_NAMES environment variable, the problems -# vanish in a puff of smoke. -if test "X${COLLECT_NAMES+set}" != Xset; then - COLLECT_NAMES= - export COLLECT_NAMES -fi -EOF - ;; - esac + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + archive_expsym_cmds='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag="-z nodefs" + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ - # We use sed instead of cat because bash on DJGPP gets confused if - # if finds mixed CR/LF and LF-only lines. Since sed operates in - # text mode, it properly converts lines to CR/LF. This bash problem - # is reportedly fixed, but why not run on old versions too? - sed '$q' "$ltmain" >> "$cfgfile" || (rm -f "$cfgfile"; exit 1) +int +main () +{ - mv -f "$cfgfile" "$ofile" || \ - (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") - chmod +x "$ofile" + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : -else - # If there is no Makefile yet, we rely on a make rule to execute - # `config.status --recheck' to rerun these tests and create the - # libtool script then. - ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` - if test -f "$ltmain_in"; then - test -f Makefile && make "$ltmain" - fi +lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\(.*\)$/\1/ + p + } + }' +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +fi fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag=' ${wl}-bernotok' + allow_undefined_flag=' ${wl}-berok' + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec='$convenience' + archive_cmds_need_lc=yes + # This is similar to how AIX traditionally builds its shared libraries. + archive_expsym_cmds="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds='' + ;; + m68k) + archive_cmds='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + esac + ;; -CC="$lt_save_CC" + bsdi[45]*) + export_dynamic_flag_spec=-rdynamic + ;; + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec=' ' + allow_undefined_flag=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + archive_cmds='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + old_archive_from_new_cmds='true' + # FIXME: Should let the user specify the lib program. + old_archive_cmds='lib -OUT:$oldlib$oldobjs$old_deplibs' + fix_srcfile_path='`cygpath -w "$srcfile"`' + enable_shared_with_static_runtimes=yes + ;; -# Check whether --with-tags was given. -if test "${with_tags+set}" = set; then - withval=$with_tags; tagnames="$withval" -fi + darwin* | rhapsody*) -if test -f "$ltmain" && test -n "$tagnames"; then - if test ! -f "${ofile}"; then - { echo "$as_me:$LINENO: WARNING: output file \`$ofile' does not exist" >&5 -echo "$as_me: WARNING: output file \`$ofile' does not exist" >&2;} - fi + archive_cmds_need_lc=no + hardcode_direct=no + hardcode_automatic=yes + hardcode_shlibpath_var=unsupported + whole_archive_flag_spec='' + link_all_deplibs=yes + allow_undefined_flag="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=echo + archive_cmds="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + module_cmds="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + archive_expsym_cmds="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + module_expsym_cmds="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" - if test -z "$LTCC"; then - eval "`$SHELL ${ofile} --config | grep '^LTCC='`" - if test -z "$LTCC"; then - { echo "$as_me:$LINENO: WARNING: output file \`$ofile' does not look like a libtool script" >&5 -echo "$as_me: WARNING: output file \`$ofile' does not look like a libtool script" >&2;} - else - { echo "$as_me:$LINENO: WARNING: using \`LTCC=$LTCC', extracted from \`$ofile'" >&5 -echo "$as_me: WARNING: using \`LTCC=$LTCC', extracted from \`$ofile'" >&2;} - fi - fi - if test -z "$LTCFLAGS"; then - eval "`$SHELL ${ofile} --config | grep '^LTCFLAGS='`" + else + ld_shlibs=no fi - # Extract list of available tagged configurations in $ofile. - # Note that this assumes the entire list is on one line. - available_tags=`grep "^available_tags=" "${ofile}" | $SED -e 's/available_tags=\(.*$\)/\1/' -e 's/\"//g'` - - lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," - for tagname in $tagnames; do - IFS="$lt_save_ifs" - # Check whether tagname contains only valid characters - case `$echo "X$tagname" | $Xsed -e 's:[-_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890,/]::g'` in - "") ;; - *) { { echo "$as_me:$LINENO: error: invalid tag name: $tagname" >&5 -echo "$as_me: error: invalid tag name: $tagname" >&2;} - { (exit 1); exit 1; }; } - ;; - esac - - if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "${ofile}" > /dev/null - then - { { echo "$as_me:$LINENO: error: tag name \"$tagname\" already exists" >&5 -echo "$as_me: error: tag name \"$tagname\" already exists" >&2;} - { (exit 1); exit 1; }; } - fi + ;; - # Update the list of available tags. - if test -n "$tagname"; then - echo appending configuration tag \"$tagname\" to $ofile - - case $tagname in - CXX) - if test -n "$CXX" && ( test "X$CXX" != "Xno" && - ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || - (test "X$CXX" != "Xg++"))) ; then - ac_ext=cpp -ac_cpp='$CXXCPP $CPPFLAGS' -ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + dgux*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; + freebsd1*) + ld_shlibs=no + ;; + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no + ;; -archive_cmds_need_lc_CXX=no -allow_undefined_flag_CXX= -always_export_symbols_CXX=no -archive_expsym_cmds_CXX= -export_dynamic_flag_spec_CXX= -hardcode_direct_CXX=no -hardcode_libdir_flag_spec_CXX= -hardcode_libdir_flag_spec_ld_CXX= -hardcode_libdir_separator_CXX= -hardcode_minus_L_CXX=no -hardcode_shlibpath_var_CXX=unsupported -hardcode_automatic_CXX=no -module_cmds_CXX= -module_expsym_cmds_CXX= -link_all_deplibs_CXX=unknown -old_archive_cmds_CXX=$old_archive_cmds -no_undefined_flag_CXX= -whole_archive_flag_spec_CXX= -enable_shared_with_static_runtimes_CXX=no + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + archive_cmds='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; -# Dependencies to place before and after the object being linked: -predep_objects_CXX= -postdep_objects_CXX= -predeps_CXX= -postdeps_CXX= -compiler_lib_search_path_CXX= + hpux9*) + if test "$GCC" = yes; then + archive_cmds='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + archive_cmds='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes -# Source file extension for C++ test sources. -ac_ext=cpp + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + export_dynamic_flag_spec='${wl}-E' + ;; -# Object file extension for compiled C++ test sources. -objext=o -objext_CXX=$objext + hpux10*) + if test "$GCC" = yes -a "$with_gnu_ld" = no; then + archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_flag_spec_ld='+b $libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='${wl}-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + fi + ;; -# Code to be used in simple compile tests -lt_simple_compile_test_code="int some_variable = 0;\n" + hpux11*) + if test "$GCC" = yes -a "$with_gnu_ld" = no; then + case $host_cpu in + hppa*64*) + archive_cmds='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + archive_cmds='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + fi + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: -# Code to be used in simple link tests -lt_simple_link_test_code='int main(int, char *[]) { return(0); }\n' + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct=no + hardcode_shlibpath_var=no + ;; + *) + hardcode_direct=yes + hardcode_direct_absolute=yes + export_dynamic_flag_spec='${wl}-E' -# ltmain only uses $CC for tagged configurations so make sure $CC is set. + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int foo(void) {} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS="$save_LDFLAGS" + else + archive_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + inherit_rpath=yes + link_all_deplibs=yes + ;; -# Allow CC to be a program name with arguments. -compiler=$CC + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + archive_cmds='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + hardcode_shlibpath_var=no + ;; + newsos6) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_direct=yes + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_shlibpath_var=no + ;; -# save warnings/boilerplate of simple test code -ac_outfile=conftest.$ac_objext -printf "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$rm conftest* - -ac_outfile=conftest.$ac_objext -printf "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$rm conftest* - + *nto* | *qnx*) + ;; -# Allow CC to be a program name with arguments. -lt_save_CC=$CC -lt_save_LD=$LD -lt_save_GCC=$GCC -GCC=$GXX -lt_save_with_gnu_ld=$with_gnu_ld -lt_save_path_LD=$lt_cv_path_LD -if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then - lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx -else - $as_unset lt_cv_prog_gnu_ld -fi -if test -n "${lt_cv_path_LDCXX+set}"; then - lt_cv_path_LD=$lt_cv_path_LDCXX -else - $as_unset lt_cv_path_LD -fi -test -z "${LDCXX+set}" || LD=$LDCXX -CC=${CXX-"c++"} -compiler=$CC -compiler_CXX=$CC -for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct=yes + hardcode_shlibpath_var=no + hardcode_direct_absolute=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + export_dynamic_flag_spec='${wl}-E' + else + case $host_os in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + archive_cmds='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-R$libdir' + ;; + *) + archive_cmds='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + esac + fi + else + ld_shlibs=no + fi + ;; + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + allow_undefined_flag=unsupported + archive_cmds='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + old_archive_from_new_cmds='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; -# We don't want -fno-exception wen compiling C++ code, so set the -# no_builtin_flag separately -if test "$GXX" = yes; then - lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' -else - lt_prog_compiler_no_builtin_flag_CXX= -fi + osf3*) + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + fi + archive_cmds_need_lc='no' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; -if test "$GXX" = yes; then - # Set up default GNU C++ configuration + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + allow_undefined_flag=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + else + allow_undefined_flag=' -expect_unresolved \*' + archive_cmds='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' + # Both c and cxx compiler support -rpath directly + hardcode_libdir_flag_spec='-rpath $libdir' + fi + archive_cmds_need_lc='no' + hardcode_libdir_separator=: + ;; -# Check whether --with-gnu-ld was given. -if test "${with_gnu_ld+set}" = set; then - withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes -else - with_gnu_ld=no -fi + solaris*) + no_undefined_flag=' -z defs' + if test "$GCC" = yes; then + wlarc='${wl}' + archive_cmds='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + archive_cmds='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='${wl}' + archive_cmds='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + hardcode_libdir_flag_spec='-R$libdir' + hardcode_shlibpath_var=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. GCC discards it without `$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test "$GCC" = yes; then + whole_archive_flag_spec='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + else + whole_archive_flag_spec='-z allextract$convenience -z defaultextract' + fi + ;; + esac + link_all_deplibs=yes + ;; -ac_prog=ld -if test "$GCC" = yes; then - # Check if gcc -print-prog-name=ld gives a path. - { echo "$as_me:$LINENO: checking for ld used by $CC" >&5 -echo $ECHO_N "checking for ld used by $CC... $ECHO_C" >&6; } - case $host in - *-*-mingw*) - # gcc leaves a trailing carriage return which upsets mingw - ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; - *) - ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; - esac - case $ac_prog in - # Accept absolute paths. - [\\/]* | ?:[\\/]*) - re_direlt='/[^/][^/]*/\.\./' - # Canonicalize the pathname of ld - ac_prog=`echo $ac_prog| $SED 's%\\\\%/%g'` - while echo $ac_prog | grep "$re_direlt" > /dev/null 2>&1; do - ac_prog=`echo $ac_prog| $SED "s%$re_direlt%/%"` - done - test -z "$LD" && LD="$ac_prog" + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + archive_cmds='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + hardcode_shlibpath_var=no ;; - "") - # If it fails, then pretend we aren't using GCC. - ac_prog=ld - ;; - *) - # If it is relative, then search for the first ld in PATH. - with_gnu_ld=unknown - ;; - esac -elif test "$with_gnu_ld" = yes; then - { echo "$as_me:$LINENO: checking for GNU ld" >&5 -echo $ECHO_N "checking for GNU ld... $ECHO_C" >&6; } -else - { echo "$as_me:$LINENO: checking for non-GNU ld" >&5 -echo $ECHO_N "checking for non-GNU ld... $ECHO_C" >&6; } -fi -if test "${lt_cv_path_LD+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - if test -z "$LD"; then - lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR - for ac_dir in $PATH; do - IFS="$lt_save_ifs" - test -z "$ac_dir" && ac_dir=. - if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then - lt_cv_path_LD="$ac_dir/$ac_prog" - # Check to see if the program is GNU ld. I'd rather use --version, - # but apparently some variants of GNU ld only accept -v. - # Break only if it was the GNU/non-GNU ld that we prefer. - case `"$lt_cv_path_LD" -v 2>&1 &5 -echo "${ECHO_T}$LD" >&6; } -else - { echo "$as_me:$LINENO: result: no" >&5 -echo "${ECHO_T}no" >&6; } -fi -test -z "$LD" && { { echo "$as_me:$LINENO: error: no acceptable ld found in \$PATH" >&5 -echo "$as_me: error: no acceptable ld found in \$PATH" >&2;} - { (exit 1); exit 1; }; } -{ echo "$as_me:$LINENO: checking if the linker ($LD) is GNU ld" >&5 -echo $ECHO_N "checking if the linker ($LD) is GNU ld... $ECHO_C" >&6; } -if test "${lt_cv_prog_gnu_ld+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - # I'd rather use --version here, but apparently some GNU lds only accept -v. -case `$LD -v 2>&1 &5 -echo "${ECHO_T}$lt_cv_prog_gnu_ld" >&6; } -with_gnu_ld=$lt_cv_prog_gnu_ld + sysv4.3*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + export_dynamic_flag_spec='-Bexport' + ;; + sysv4*MP*) + if test -d /usr/nec; then + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_shlibpath_var=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + ld_shlibs=yes + fi + ;; + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag='${wl}-z,text' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + runpath_var='LD_RUN_PATH' - # Check if GNU C++ uses GNU ld as the underlying linker, since the - # archiving commands below assume that GNU ld is being used. - if test "$with_gnu_ld" = yes; then - archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; - hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir' - export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag='${wl}-z,text' + allow_undefined_flag='${wl}-z,nodefs' + archive_cmds_need_lc=no + hardcode_shlibpath_var=no + hardcode_libdir_flag_spec='${wl}-R,$libdir' + hardcode_libdir_separator=':' + link_all_deplibs=yes + export_dynamic_flag_spec='${wl}-Bexport' + runpath_var='LD_RUN_PATH' - # If archive_cmds runs LD, not CC, wlarc should be empty - # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to - # investigate it a little bit more. (MM) - wlarc='${wl}' + if test "$GCC" = yes; then + archive_cmds='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + archive_cmds='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; - # ancient GNU ld didn't support --whole-archive et. al. - if eval "`$CC -print-prog-name=ld` --help 2>&1" | \ - grep 'no-whole-archive' > /dev/null; then - whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - whole_archive_flag_spec_CXX= - fi - else - with_gnu_ld=no - wlarc= + uts4*) + archive_cmds='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + hardcode_libdir_flag_spec='-L$libdir' + hardcode_shlibpath_var=no + ;; - # A generic and very simple default shared library creation - # command for GNU C++ for the case where it uses the native - # linker, instead of GNU ld. If possible, this setting should - # overridden to take advantage of the native linker features on - # the platform it is being used on. - archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - fi - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' - -else - GXX=no - with_gnu_ld=no - wlarc= -fi - -# PORTME: fill in a description of your system's C++ link characteristics -{ echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5 -echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6; } -ld_shlibs_CXX=yes -case $host_os in - aix3*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - aix4* | aix5*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - aix_use_runtimelinking=no - - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[23]|aix4.[23].*|aix5*) - for ld_flag in $LDFLAGS; do - case $ld_flag in - *-brtl*) - aix_use_runtimelinking=yes - break - ;; - esac - done - ;; - esac - - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - archive_cmds_CXX='' - hardcode_direct_CXX=yes - hardcode_libdir_separator_CXX=':' - link_all_deplibs_CXX=yes + *) + ld_shlibs=no + ;; + esac - if test "$GXX" = yes; then - case $host_os in aix4.[012]|aix4.[012].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && \ - strings "$collect2name" | grep resolve_lib_name >/dev/null - then - # We have reworked collect2 - hardcode_direct_CXX=yes - else - # We have old collect2 - hardcode_direct_CXX=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - hardcode_minus_L_CXX=yes - hardcode_libdir_flag_spec_CXX='-L$libdir' - hardcode_libdir_separator_CXX= - fi + if test x$host_vendor = xsni; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + export_dynamic_flag_spec='${wl}-Blargedynsym' ;; esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi fi + fi - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to export. - always_export_symbols_CXX=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - allow_undefined_flag_CXX='-berok' - # Determine the default libpath from the value encoded in an empty executable. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs" >&5 +$as_echo "$ld_shlibs" >&6; } +test "$ld_shlibs" = no && can_build_shared=no -int -main () -{ +with_gnu_ld=$with_gnu_ld - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 -fi -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" - archive_expsym_cmds_CXX="\$CC"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - hardcode_libdir_flag_spec_CXX='${wl}-R $libdir:/usr/lib:/lib' - allow_undefined_flag_CXX="-z nodefs" - archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an empty executable. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -int -main () -{ - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_cxx_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 -fi -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - no_undefined_flag_CXX=' ${wl}-bernotok' - allow_undefined_flag_CXX=' ${wl}-berok' - # Exported symbols can be pulled into shared objects from archives - whole_archive_flag_spec_CXX='$convenience' - archive_cmds_need_lc_CXX=yes - # This is similar to how AIX traditionally builds its shared libraries. - archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' - fi - fi - ;; - beos*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - allow_undefined_flag_CXX=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - ld_shlibs_CXX=no - fi - ;; +# +# Do we need to explicitly link libc? +# +case "x$archive_cmds_need_lc" in +x|xyes) + # Assume -lc should be added + archive_cmds_need_lc=yes - chorus*) - case $cc_basename in - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $archive_cmds in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext - cygwin* | mingw* | pw32*) - # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, - # as there is no search path for DLLs. - hardcode_libdir_flag_spec_CXX='-L$libdir' - allow_undefined_flag_CXX=unsupported - always_export_symbols_CXX=no - enable_shared_with_static_runtimes_CXX=yes - - if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then - archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - ld_shlibs_CXX=no - fi - ;; - darwin* | rhapsody*) - case $host_os in - rhapsody* | darwin1.[012]) - allow_undefined_flag_CXX='${wl}-undefined ${wl}suppress' - ;; - *) # Darwin 1.3 on - if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then - allow_undefined_flag_CXX='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - else - case ${MACOSX_DEPLOYMENT_TARGET} in - 10.[012]) - allow_undefined_flag_CXX='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - ;; - 10.*) - allow_undefined_flag_CXX='${wl}-undefined ${wl}dynamic_lookup' - ;; - esac - fi - ;; - esac - archive_cmds_need_lc_CXX=no - hardcode_direct_CXX=no - hardcode_automatic_CXX=yes - hardcode_shlibpath_var_CXX=unsupported - whole_archive_flag_spec_CXX='' - link_all_deplibs_CXX=yes - - if test "$GXX" = yes ; then - lt_int_apple_cc_single_mod=no - output_verbose_link_cmd='echo' - if $CC -dumpspecs 2>&1 | $EGREP 'single_module' >/dev/null ; then - lt_int_apple_cc_single_mod=yes - fi - if test "X$lt_int_apple_cc_single_mod" = Xyes ; then - archive_cmds_CXX='$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' - else - archive_cmds_CXX='$CC -r -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring' + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$lt_prog_compiler_wl + pic_flag=$lt_prog_compiler_pic + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$allow_undefined_flag + allow_undefined_flag= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + then + archive_cmds_need_lc=no + else + archive_cmds_need_lc=yes fi - module_cmds_CXX='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - if test "X$lt_int_apple_cc_single_mod" = Xyes ; then - archive_expsym_cmds_CXX='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib -single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - else - archive_expsym_cmds_CXX='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -r -keep_private_externs -nostdlib -o ${lib}-master.o $libobjs~$CC -dynamiclib $allow_undefined_flag -o $lib ${lib}-master.o $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - fi - module_expsym_cmds_CXX='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - else - case $cc_basename in - xlc*) - output_verbose_link_cmd='echo' - archive_cmds_CXX='$CC -qmkshrobj ${wl}-single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}`echo $rpath/$soname` $verstring' - module_cmds_CXX='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - archive_expsym_cmds_CXX='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -qmkshrobj ${wl}-single_module $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}$rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - module_expsym_cmds_CXX='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - ;; - *) - ld_shlibs_CXX=no - ;; - esac - fi - ;; - - dgux*) - case $cc_basename in - ec++*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - ghcx*) - # Green Hills C++ Compiler - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - freebsd[12]*) - # C++ shared libraries reported to be fairly broken before switch to ELF - ld_shlibs_CXX=no - ;; - freebsd-elf*) - archive_cmds_need_lc_CXX=no - ;; - freebsd* | kfreebsd*-gnu | dragonfly*) - # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF - # conventions - ld_shlibs_CXX=yes - ;; - gnu*) - ;; - hpux9*) - hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_CXX=: - export_dynamic_flag_spec_CXX='${wl}-E' - hardcode_direct_CXX=yes - hardcode_minus_L_CXX=yes # Not in the search PATH, - # but as the default - # location of the library. - - case $cc_basename in - CC*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - aCC*) - archive_cmds_CXX='$rm $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "[-]L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - ;; - *) - if test "$GXX" = yes; then - archive_cmds_CXX='$rm $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + allow_undefined_flag=$lt_save_allow_undefined_flag else - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no + cat conftest.err 1>&5 fi + $RM conftest* + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $archive_cmds_need_lc" >&5 +$as_echo "$archive_cmds_need_lc" >&6; } ;; esac - ;; - hpux10*|hpux11*) - if test $with_gnu_ld = no; then - hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_CXX=: + fi + ;; +esac + + + + + + + + + + + + + + + + + + + + + + + + + + + - case $host_cpu in - hppa*64*|ia64*) - hardcode_libdir_flag_spec_ld_CXX='+b $libdir' - ;; - *) - export_dynamic_flag_spec_CXX='${wl}-E' - ;; - esac - fi - case $host_cpu in - hppa*64*|ia64*) - hardcode_direct_CXX=no - hardcode_shlibpath_var_CXX=no - ;; - *) - hardcode_direct_CXX=yes - hardcode_minus_L_CXX=yes # Not in the search PATH, - # but as the default - # location of the library. - ;; - esac - case $cc_basename in - CC*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - aCC*) - case $host_cpu in - hppa*64*) - archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - ia64*) - archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - *) - archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - esac - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | grep "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - ;; - *) - if test "$GXX" = yes; then - if test $with_gnu_ld = no; then - case $host_cpu in - hppa*64*) - archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - ia64*) - archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - *) - archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - ;; - esac - fi - else - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - fi - ;; - esac - ;; - interix3*) - hardcode_direct_CXX=no - hardcode_shlibpath_var_CXX=no - hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' - export_dynamic_flag_spec_CXX='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - archive_expsym_cmds_CXX='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - irix5* | irix6*) - case $cc_basename in - CC*) - # SGI C++ - archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - - # Archives containing C++ object files must be created using - # "CC -ar", where "CC" is the IRIX C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' - ;; - *) - if test "$GXX" = yes; then - if test "$with_gnu_ld" = no; then - archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` -o $lib' - fi - fi - link_all_deplibs_CXX=yes - ;; - esac - hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_CXX=: - ;; - linux*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | grep "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - - hardcode_libdir_flag_spec_CXX='${wl}--rpath,$libdir' - export_dynamic_flag_spec_CXX='${wl}--export-dynamic' - - # Archives containing C++ object files must be created using - # "CC -Bstatic", where "CC" is the KAI C++ compiler. - old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' - ;; - icpc*) - # Intel C++ - with_gnu_ld=yes - # version 8.0 and above of icpc choke on multiply defined symbols - # if we add $predep_objects and $postdep_objects, however 7.1 and - # earlier do not add the objects themselves. - case `$CC -V 2>&1` in - *"Version 7."*) - archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - ;; - *) # Version 8.0 or newer - tmp_idyn= - case $host_cpu in - ia64*) tmp_idyn=' -i_dynamic';; - esac - archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - ;; - esac - archive_cmds_need_lc_CXX=no - hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' - export_dynamic_flag_spec_CXX='${wl}--export-dynamic' - whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' - ;; - pgCC*) - # Portland Group C++ compiler - archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' - - hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir' - export_dynamic_flag_spec_CXX='${wl}--export-dynamic' - whole_archive_flag_spec_CXX='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - ;; - cxx*) - # Compaq C++ - archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' - runpath_var=LD_RUN_PATH - hardcode_libdir_flag_spec_CXX='-rpath $libdir' - hardcode_libdir_separator_CXX=: - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - ;; - esac - ;; - lynxos*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - m88k*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - mvs*) - case $cc_basename in - cxx*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' - wlarc= - hardcode_libdir_flag_spec_CXX='-R$libdir' - hardcode_direct_CXX=yes - hardcode_shlibpath_var_CXX=no - fi - # Workaround some broken pre-1.5 toolchains - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' - ;; - openbsd2*) - # C++ shared libraries are fairly broken - ld_shlibs_CXX=no - ;; - openbsd*) - hardcode_direct_CXX=yes - hardcode_shlibpath_var_CXX=no - archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' - hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' - export_dynamic_flag_spec_CXX='${wl}-E' - whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - fi - output_verbose_link_cmd='echo' - ;; - osf3*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' - hardcode_libdir_separator_CXX=: - # Archives containing C++ object files must be created using - # "CC -Bstatic", where "CC" is the KAI C++ compiler. - old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' - ;; - RCC*) - # Rational C++ 2.4.1 - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - cxx*) - allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && echo ${wl}-set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - - hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_CXX=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - ;; - *) - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_CXX=: - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' - else - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - fi - ;; - esac - ;; - osf4* | osf5*) - case $cc_basename in - KCC*) - # Kuck and Associates, Inc. (KAI) C++ Compiler - - # KCC will only create a shared library if the output file - # ends with ".so" (or ".sl" for HP-UX), so rename the library - # to its proper name (with version) after linking. - archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' - hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' - hardcode_libdir_separator_CXX=: - # Archives containing C++ object files must be created using - # the KAI C++ compiler. - old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' - ;; - RCC*) - # Rational C++ 2.4.1 - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - cxx*) - allow_undefined_flag_CXX=' -expect_unresolved \*' - archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ - echo "-hidden">> $lib.exp~ - $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname -Wl,-input -Wl,$lib.exp `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib~ - $rm $lib.exp' - - hardcode_libdir_flag_spec_CXX='-rpath $libdir' - hardcode_libdir_separator_CXX=: - - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - # - # There doesn't appear to be a way to prevent this compiler from - # explicitly linking system object files so we need to strip them - # from the output so that they don't get included in the library - # dependencies. - output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "ld" | grep -v "ld:"`; templist=`echo $templist | $SED "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; echo $list' - ;; - *) - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_CXX=: - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep "\-L"' - else - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - fi - ;; - esac - ;; - psos*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - sunos4*) - case $cc_basename in - CC*) - # Sun C++ 4.x - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - lcc*) - # Lucid - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - solaris*) - case $cc_basename in - CC*) - # Sun C++ 4.2, 5.x and Centerline C++ - archive_cmds_need_lc_CXX=yes - no_undefined_flag_CXX=' -zdefs' - archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' - archive_expsym_cmds_CXX='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' - hardcode_libdir_flag_spec_CXX='-R$libdir' - hardcode_shlibpath_var_CXX=no - case $host_os in - solaris2.[0-5] | solaris2.[0-5].*) ;; - *) - # The C++ compiler is used as linker so we must use $wl - # flag to pass the commands to the underlying system - # linker. We must also pass each convience library through - # to the system linker between allextract/defaultextract. - # The C++ compiler will combine linker options so we - # cannot just pass the convience library names through - # without $wl. - # Supported since Solaris 2.6 (maybe 2.5.1?) - whole_archive_flag_spec_CXX='${wl}-z ${wl}allextract`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}-z ${wl}defaultextract' - ;; - esac - link_all_deplibs_CXX=yes - output_verbose_link_cmd='echo' - # Archives containing C++ object files must be created using - # "CC -xar", where "CC" is the Sun C++ compiler. This is - # necessary to make sure instantiated templates are included - # in the archive. - old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' - ;; - gcx*) - # Green Hills C++ Compiler - archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - # The C++ compiler must be used to create the archive. - old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' - ;; - *) - # GNU C++ compiler with Solaris linker - if test "$GXX" = yes && test "$with_gnu_ld" = no; then - no_undefined_flag_CXX=' ${wl}-z ${wl}defs' - if $CC --version | grep -v '^2\.7' > /dev/null; then - archive_cmds_CXX='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - archive_expsym_cmds_CXX='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd="$CC -shared $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\"" - else - # g++ 2.7 appears to require `-G' NOT `-shared' on this - # platform. - archive_cmds_CXX='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' - archive_expsym_cmds_CXX='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$rm $lib.exp' - # Commands to make compiler produce verbose output that lists - # what "hidden" libraries, object files and flags are used when - # linking a shared library. - output_verbose_link_cmd="$CC -G $CFLAGS -v conftest.$objext 2>&1 | grep \"\-L\"" - fi - hardcode_libdir_flag_spec_CXX='${wl}-R $wl$libdir' - fi - ;; - esac - ;; - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) - no_undefined_flag_CXX='${wl}-z,text' - archive_cmds_need_lc_CXX=no - hardcode_shlibpath_var_CXX=no - runpath_var='LD_RUN_PATH' - - case $cc_basename in - CC*) - archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - ;; - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - # For security reasons, it is highly recommended that you always - # use absolute paths for naming shared libraries, and exclude the - # DT_RUNPATH tag from executables and libraries. But doing so - # requires that you compile everything twice, which is a pain. - # So that behaviour is only enabled if SCOABSPATH is set to a - # non-empty value in the environment. Most likely only useful for - # creating official distributions of packages. - # This is a hack until libtool officially supports absolute path - # names for shared libraries. - no_undefined_flag_CXX='${wl}-z,text' - allow_undefined_flag_CXX='${wl}-z,nodefs' - archive_cmds_need_lc_CXX=no - hardcode_shlibpath_var_CXX=no - hardcode_libdir_flag_spec_CXX='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' - hardcode_libdir_separator_CXX=':' - link_all_deplibs_CXX=yes - export_dynamic_flag_spec_CXX='${wl}-Bexport' - runpath_var='LD_RUN_PATH' - - case $cc_basename in - CC*) - archive_cmds_CXX='$CC -G ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds_CXX='$CC -shared ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - ;; - tandem*) - case $cc_basename in - NCC*) - # NonStop-UX NCC 3.20 - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - esac - ;; - vxworks*) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; - *) - # FIXME: insert proper C++ library support - ld_shlibs_CXX=no - ;; -esac -{ echo "$as_me:$LINENO: result: $ld_shlibs_CXX" >&5 -echo "${ECHO_T}$ld_shlibs_CXX" >&6; } -test "$ld_shlibs_CXX" = no && can_build_shared=no -GCC_CXX="$GXX" -LD_CXX="$LD" -cat > conftest.$ac_ext <&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; then - # Parse the compiler output and extract the necessary - # objects, libraries and library flags. - # Sentinel used to keep track of whether or not we are before - # the conftest object file. - pre_test_object_deps_done=no - # The `*' in the case matches for architectures that use `case' in - # $output_verbose_cmd can trigger glob expansion during the loop - # eval without this substitution. - output_verbose_link_cmd=`$echo "X$output_verbose_link_cmd" | $Xsed -e "$no_glob_subst"` - for p in `eval $output_verbose_link_cmd`; do - case $p in - -L* | -R* | -l*) - # Some compilers place space between "-{L,R}" and the path. - # Remove the space. - if test $p = "-L" \ - || test $p = "-R"; then - prev=$p - continue - else - prev= - fi - if test "$pre_test_object_deps_done" = no; then - case $p in - -L* | -R*) - # Internal compiler library paths should come after those - # provided the user. The postdeps already come after the - # user supplied libs so there is no need to process them. - if test -z "$compiler_lib_search_path_CXX"; then - compiler_lib_search_path_CXX="${prev}${p}" - else - compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} ${prev}${p}" - fi - ;; - # The "-l" case would never come before the object being - # linked, so don't bother handling this case. - esac - else - if test -z "$postdeps_CXX"; then - postdeps_CXX="${prev}${p}" - else - postdeps_CXX="${postdeps_CXX} ${prev}${p}" - fi - fi - ;; - *.$objext) - # This assumes that the test object file only shows up - # once in the compiler output. - if test "$p" = "conftest.$objext"; then - pre_test_object_deps_done=yes - continue - fi - if test "$pre_test_object_deps_done" = no; then - if test -z "$predep_objects_CXX"; then - predep_objects_CXX="$p" - else - predep_objects_CXX="$predep_objects_CXX $p" - fi - else - if test -z "$postdep_objects_CXX"; then - postdep_objects_CXX="$p" - else - postdep_objects_CXX="$postdep_objects_CXX $p" - fi - fi - ;; - *) ;; # Ignore the rest. - esac - done - # Clean up. - rm -f a.out a.exe -else - echo "libtool.m4: error: problem compiling CXX test program" -fi -$rm -f confest.$objext -# PORTME: override above test on systems where it is broken -case $host_os in -interix3*) - # Interix 3.5 installs completely hosed .la files for C++, so rather than - # hack all around it, let's just trust "g++" to DTRT. - predep_objects_CXX= - postdep_objects_CXX= - postdeps_CXX= - ;; -solaris*) - case $cc_basename in - CC*) - # Adding this requires a known-good setup of shared libraries for - # Sun compiler versions before 5.6, else PIC objects from an old - # archive will be linked into the output, leading to subtle bugs. - postdeps_CXX='-lCstd -lCrun' - ;; - esac - ;; -esac -case " $postdeps_CXX " in -*" -lc "*) archive_cmds_need_lc_CXX=no ;; -esac -lt_prog_compiler_wl_CXX= -lt_prog_compiler_pic_CXX= -lt_prog_compiler_static_CXX= -{ echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5 -echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6; } - # C++ specific cases for pic, static, wl, etc. - if test "$GXX" = yes; then - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_static_CXX='-static' - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static_CXX='-Bstatic' - fi - ;; - amigaos*) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' - ;; - beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; - mingw* | os2* | pw32*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic_CXX='-DDLL_EXPORT' - ;; - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - lt_prog_compiler_pic_CXX='-fno-common' - ;; - *djgpp*) - # DJGPP does not support shared libraries at all - lt_prog_compiler_pic_CXX= - ;; - interix3*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; - sysv4*MP*) - if test -d /usr/nec; then - lt_prog_compiler_pic_CXX=-Kconform_pic - fi - ;; - hpux*) - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - ;; - *) - lt_prog_compiler_pic_CXX='-fPIC' - ;; - esac - ;; - *) - lt_prog_compiler_pic_CXX='-fPIC' - ;; - esac - else - case $host_os in - aix4* | aix5*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static_CXX='-Bstatic' - else - lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' - fi - ;; - chorus*) - case $cc_basename in - cxch68*) - # Green Hills C++ Compiler - # _LT_AC_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" - ;; - esac - ;; - darwin*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - case $cc_basename in - xlc*) - lt_prog_compiler_pic_CXX='-qnocommon' - lt_prog_compiler_wl_CXX='-Wl,' - ;; - esac - ;; - dgux*) - case $cc_basename in - ec++*) - lt_prog_compiler_pic_CXX='-KPIC' - ;; - ghcx*) - # Green Hills C++ Compiler - lt_prog_compiler_pic_CXX='-pic' - ;; - *) - ;; - esac - ;; - freebsd* | kfreebsd*-gnu | dragonfly*) - # FreeBSD uses GNU C++ - ;; - hpux9* | hpux10* | hpux11*) - case $cc_basename in - CC*) - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' - if test "$host_cpu" != ia64; then - lt_prog_compiler_pic_CXX='+Z' - fi - ;; - aCC*) - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic_CXX='+Z' - ;; - esac - ;; - *) - ;; - esac - ;; - interix*) - # This is c89, which is MS Visual C++ (no shared libs) - # Anyone wants to do a port? - ;; - irix5* | irix6* | nonstopux*) - case $cc_basename in - CC*) - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_static_CXX='-non_shared' - # CC pic flag -KPIC is the default. - ;; - *) - ;; - esac - ;; - linux*) - case $cc_basename in - KCC*) - # KAI C++ Compiler - lt_prog_compiler_wl_CXX='--backend -Wl,' - lt_prog_compiler_pic_CXX='-fPIC' - ;; - icpc* | ecpc*) - # Intel C++ - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_pic_CXX='-KPIC' - lt_prog_compiler_static_CXX='-static' - ;; - pgCC*) - # Portland Group C++ compiler. - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_pic_CXX='-fpic' - lt_prog_compiler_static_CXX='-Bstatic' - ;; - cxx*) - # Compaq C++ - # Make sure the PIC flag is empty. It appears that all Alpha - # Linux and Compaq Tru64 Unix objects are PIC. - lt_prog_compiler_pic_CXX= - lt_prog_compiler_static_CXX='-non_shared' - ;; - *) - ;; - esac - ;; - lynxos*) - ;; - m88k*) - ;; - mvs*) - case $cc_basename in - cxx*) - lt_prog_compiler_pic_CXX='-W c,exportall' - ;; - *) - ;; - esac - ;; - netbsd*) - ;; - osf3* | osf4* | osf5*) - case $cc_basename in - KCC*) - lt_prog_compiler_wl_CXX='--backend -Wl,' - ;; - RCC*) - # Rational C++ 2.4.1 - lt_prog_compiler_pic_CXX='-pic' - ;; - cxx*) - # Digital/Compaq C++ - lt_prog_compiler_wl_CXX='-Wl,' - # Make sure the PIC flag is empty. It appears that all Alpha - # Linux and Compaq Tru64 Unix objects are PIC. - lt_prog_compiler_pic_CXX= - lt_prog_compiler_static_CXX='-non_shared' - ;; - *) - ;; - esac - ;; - psos*) - ;; - solaris*) - case $cc_basename in - CC*) - # Sun C++ 4.2, 5.x and Centerline C++ - lt_prog_compiler_pic_CXX='-KPIC' - lt_prog_compiler_static_CXX='-Bstatic' - lt_prog_compiler_wl_CXX='-Qoption ld ' - ;; - gcx*) - # Green Hills C++ Compiler - lt_prog_compiler_pic_CXX='-PIC' - ;; - *) - ;; - esac - ;; - sunos4*) - case $cc_basename in - CC*) - # Sun C++ 4.x - lt_prog_compiler_pic_CXX='-pic' - lt_prog_compiler_static_CXX='-Bstatic' - ;; - lcc*) - # Lucid - lt_prog_compiler_pic_CXX='-pic' - ;; - *) - ;; - esac - ;; - tandem*) - case $cc_basename in - NCC*) - # NonStop-UX NCC 3.20 - lt_prog_compiler_pic_CXX='-KPIC' - ;; - *) - ;; - esac - ;; - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - case $cc_basename in - CC*) - lt_prog_compiler_wl_CXX='-Wl,' - lt_prog_compiler_pic_CXX='-KPIC' - lt_prog_compiler_static_CXX='-Bstatic' - ;; - esac - ;; - vxworks*) - ;; - *) - lt_prog_compiler_can_build_shared_CXX=no - ;; - esac - fi -{ echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_CXX" >&5 -echo "${ECHO_T}$lt_prog_compiler_pic_CXX" >&6; } -# -# Check to make sure the PIC flag actually works. -# -if test -n "$lt_prog_compiler_pic_CXX"; then -{ echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 -echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... $ECHO_C" >&6; } -if test "${lt_prog_compiler_pic_works_CXX+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_prog_compiler_pic_works_CXX=no - ac_outfile=conftest.$ac_objext - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:12239: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:12243: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_prog_compiler_pic_works_CXX=yes - fi - fi - $rm conftest* -fi -{ echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_works_CXX" >&5 -echo "${ECHO_T}$lt_prog_compiler_pic_works_CXX" >&6; } -if test x"$lt_prog_compiler_pic_works_CXX" = xyes; then - case $lt_prog_compiler_pic_CXX in - "" | " "*) ;; - *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; - esac -else - lt_prog_compiler_pic_CXX= - lt_prog_compiler_can_build_shared_CXX=no -fi -fi -case $host_os in - # For platforms which do not support PIC, -DPIC is meaningless: - *djgpp*) - lt_prog_compiler_pic_CXX= - ;; - *) - lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" - ;; -esac -# -# Check to make sure the static flag actually works. -# -wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" -{ echo "$as_me:$LINENO: checking if $compiler static flag $lt_tmp_static_flag works" >&5 -echo $ECHO_N "checking if $compiler static flag $lt_tmp_static_flag works... $ECHO_C" >&6; } -if test "${lt_prog_compiler_static_works_CXX+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_prog_compiler_static_works_CXX=no - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $lt_tmp_static_flag" - printf "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&5 - $echo "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - lt_prog_compiler_static_works_CXX=yes - fi - else - lt_prog_compiler_static_works_CXX=yes - fi - fi - $rm conftest* - LDFLAGS="$save_LDFLAGS" -fi -{ echo "$as_me:$LINENO: result: $lt_prog_compiler_static_works_CXX" >&5 -echo "${ECHO_T}$lt_prog_compiler_static_works_CXX" >&6; } -if test x"$lt_prog_compiler_static_works_CXX" = xyes; then - : -else - lt_prog_compiler_static_CXX= -fi -{ echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5 -echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6; } -if test "${lt_cv_prog_compiler_c_o_CXX+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_cv_prog_compiler_c_o_CXX=no - $rm -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:12343: $lt_compile\"" >&5) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&5 - echo "$as_me:12347: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - lt_cv_prog_compiler_c_o_CXX=yes - fi - fi - chmod u+w . 2>&5 - $rm conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files - $rm out/* && rmdir out - cd .. - rmdir conftest - $rm conftest* -fi -{ echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o_CXX" >&5 -echo "${ECHO_T}$lt_cv_prog_compiler_c_o_CXX" >&6; } -hard_links="nottested" -if test "$lt_cv_prog_compiler_c_o_CXX" = no && test "$need_locks" != no; then - # do not overwrite the value of need_locks provided by the user - { echo "$as_me:$LINENO: checking if we can lock with hard links" >&5 -echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6; } - hard_links=yes - $rm conftest* - ln conftest.a conftest.b 2>/dev/null && hard_links=no - touch conftest.a - ln conftest.a conftest.b 2>&5 || hard_links=no - ln conftest.a conftest.b 2>/dev/null && hard_links=no - { echo "$as_me:$LINENO: result: $hard_links" >&5 -echo "${ECHO_T}$hard_links" >&6; } - if test "$hard_links" = no; then - { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 -echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} - need_locks=warn - fi -else - need_locks=no -fi -{ echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5 -echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6; } - export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - case $host_os in - aix4* | aix5*) - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - if $NM -V 2>&1 | grep 'GNU' > /dev/null; then - export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' - else - export_symbols_cmds_CXX='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' - fi - ;; - pw32*) - export_symbols_cmds_CXX="$ltdll_cmds" - ;; - cygwin* | mingw*) - export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS] /s/.* \([^ ]*\)/\1 DATA/;/^.* __nm__/s/^.* __nm__\([^ ]*\) [^ ]*/\1 DATA/;/^I /d;/^[AITW] /s/.* //'\'' | sort | uniq > $export_symbols' - ;; - *) - export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - ;; - esac -{ echo "$as_me:$LINENO: result: $ld_shlibs_CXX" >&5 -echo "${ECHO_T}$ld_shlibs_CXX" >&6; } -test "$ld_shlibs_CXX" = no && can_build_shared=no -# -# Do we need to explicitly link libc? -# -case "x$archive_cmds_need_lc_CXX" in -x|xyes) - # Assume -lc should be added - archive_cmds_need_lc_CXX=yes - if test "$enable_shared" = yes && test "$GCC" = yes; then - case $archive_cmds_CXX in - *'~'*) - # FIXME: we may have to deal with multi-command sequences. - ;; - '$CC '*) - # Test whether the compiler implicitly links with -lc since on some - # systems, -lgcc has to come before -lc. If gcc already passes -lc - # to ld, don't add -lc before -lgcc. - { echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5 -echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6; } - $rm conftest* - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } 2>conftest.err; then - soname=conftest - lib=conftest - libobjs=conftest.$ac_objext - deplibs= - wl=$lt_prog_compiler_wl_CXX - pic_flag=$lt_prog_compiler_pic_CXX - compiler_flags=-v - linker_flags=-v - verstring= - output_objdir=. - libname=conftest - lt_save_allow_undefined_flag=$allow_undefined_flag_CXX - allow_undefined_flag_CXX= - if { (eval echo "$as_me:$LINENO: \"$archive_cmds_CXX 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5 - (eval $archive_cmds_CXX 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } - then - archive_cmds_need_lc_CXX=no - else - archive_cmds_need_lc_CXX=yes - fi - allow_undefined_flag_CXX=$lt_save_allow_undefined_flag - else - cat conftest.err 1>&5 - fi - $rm conftest* - { echo "$as_me:$LINENO: result: $archive_cmds_need_lc_CXX" >&5 -echo "${ECHO_T}$archive_cmds_need_lc_CXX" >&6; } - ;; - esac - fi - ;; -esac -{ echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5 -echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6; } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + +if test "$GCC" = yes; then + case $host_os in + darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; + *) lt_awk_arg="/^libraries:/" ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'` + else + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary. + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path/$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" + else + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk ' +BEGIN {RS=" "; FS="/|\n";} { + lt_foo=""; + lt_count=0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo="/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[lt_foo]++; } + if (lt_freq[lt_foo] == 1) { print lt_foo; } +}'` + sys_lib_search_path_spec=`$ECHO $lt_search_path_spec` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi library_names_spec= libname_spec='lib$name' soname_spec= @@ -12499,20 +10101,6 @@ version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" -if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then - # if the path contains ";" then we assume it to be the separator - # otherwise default to the standard path separator (i.e. ":") - it is - # assumed that no part of a normal pathname contains ";" but that should - # okay in the real world where ";" in dirpaths is itself problematic. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi -else - sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" -fi need_lib_prefix=unknown hardcode_into_libs=no @@ -12530,7 +10118,7 @@ soname_spec='${libname}${release}${shared_ext}$major' ;; -aix4* | aix5*) +aix[4-9]*) version_type=linux need_lib_prefix=no need_version=no @@ -12549,7 +10137,7 @@ aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' - echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then : else can_build_shared=no @@ -12575,9 +10163,18 @@ ;; amigaos*) - library_names_spec='$libname.ixlibrary $libname.a' - # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac ;; beos*) @@ -12600,25 +10197,28 @@ # libtool to hard-code these into programs ;; -cygwin* | mingw* | pw32*) +cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=".dll" need_version=no need_lib_prefix=no case $GCC,$host_os in - yes,cygwin* | yes,mingw* | yes,pw32*) + yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*) library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname' + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ - $rm \$dlpath' + $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in @@ -12627,20 +10227,20 @@ soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" ;; - mingw*) + mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';[c-zC-Z]:/' >/dev/null; then + sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH printed by # mingw gcc, but we are running on Cygwin. Gcc prints its search # path with ; separators, and with drive letters. We can handle the # drive letters (cygwin fileutils understands them), so leave them, # especially as we might pass files found there to a mingw objdump, # which wouldn't understand a cygwinified path. Ahh. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi ;; pw32*) @@ -12664,17 +10264,13 @@ version_type=darwin need_lib_prefix=no need_version=no - library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext' + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' soname_spec='${libname}${release}${major}$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' - # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. - if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` - else - sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib' - fi + + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib" sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; @@ -12691,18 +10287,6 @@ dynamic_linker=no ;; -kfreebsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; - freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. @@ -12740,7 +10324,7 @@ shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; - freebsd*) # from 4.6 on + *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; @@ -12779,18 +10363,18 @@ fi sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; - hppa*64*) - shrext_cmds='.sl' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.sl" - shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - *) + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH @@ -12803,7 +10387,7 @@ postinstall_cmds='chmod 555 $lib' ;; -interix3*) +interix[3-9]*) version_type=linux need_lib_prefix=no need_version=no @@ -12858,7 +10442,7 @@ ;; # This must be Linux ELF. -linux*) +linux* | k*bsd*-gnu) version_type=linux need_lib_prefix=no need_version=no @@ -12867,14 +10451,40 @@ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes + # Some binutils ld are patched to set DT_RUNPATH + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ - # Append ld.so.conf contents to the search path +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path if test -f /etc/ld.so.conf; then - lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi @@ -12887,23 +10497,11 @@ dynamic_linker='GNU/Linux ld.so' ;; -knetbsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; - netbsd*) version_type=sunos need_lib_prefix=no need_version=no - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' @@ -12924,14 +10522,16 @@ shlibpath_overrides_runpath=yes ;; -nto-qnx*) - version_type=linux +*nto* | *qnx*) + version_type=qnx need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' ;; openbsd*) @@ -12940,13 +10540,13 @@ need_lib_prefix=no # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. case $host_os in - openbsd3.3 | openbsd3.3.*) need_version=yes ;; - *) need_version=no ;; + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; esac library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then case $host_os in openbsd2.[89] | openbsd2.[89].*) shlibpath_overrides_runpath=no @@ -12980,6 +10580,10 @@ sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" ;; +rdos*) + dynamic_linker=no + ;; + solaris*) version_type=linux need_lib_prefix=no @@ -13014,7 +10618,6 @@ sni) shlibpath_overrides_runpath=no need_lib_prefix=no - export_dynamic_flag_spec='${wl}-Blargedynsym' runpath_var=LD_RUN_PATH ;; siemens) @@ -13045,13 +10648,12 @@ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test "$with_gnu_ld" = yes; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' - shlibpath_overrides_runpath=no else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' - shlibpath_overrides_runpath=yes case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" @@ -13061,6 +10663,17 @@ sys_lib_dlsearch_path_spec='/usr/lib' ;; +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + uts4*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' @@ -13072,8 +10685,8 @@ dynamic_linker=no ;; esac -{ echo "$as_me:$LINENO: result: $dynamic_linker" >&5 -echo "${ECHO_T}$dynamic_linker" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } test "$dynamic_linker" = no && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" @@ -13081,4529 +10694,2735 @@ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi -{ echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5 -echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6; } -hardcode_action_CXX= -if test -n "$hardcode_libdir_flag_spec_CXX" || \ - test -n "$runpath_var_CXX" || \ - test "X$hardcode_automatic_CXX" = "Xyes" ; then - - # We can hardcode non-existant directories. - if test "$hardcode_direct_CXX" != no && - # If the only mechanism to avoid hardcoding is shlibpath_var, we - # have to relink, otherwise we might link with an installed library - # when we should be linking with a yet-to-be-installed one - ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, CXX)" != no && - test "$hardcode_minus_L_CXX" != no; then - # Linking always hardcodes the temporary library directory. - hardcode_action_CXX=relink - else - # We can link without hardcoding, and we can hardcode nonexisting dirs. - hardcode_action_CXX=immediate - fi -else - # We cannot hardcode anything, or else we can only hardcode existing - # directories. - hardcode_action_CXX=unsupported +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" fi -{ echo "$as_me:$LINENO: result: $hardcode_action_CXX" >&5 -echo "${ECHO_T}$hardcode_action_CXX" >&6; } - -if test "$hardcode_action_CXX" = relink; then - # Fast installation is not supported - enable_fast_install=no -elif test "$shlibpath_overrides_runpath" = yes || - test "$enable_shared" = no; then - # Fast installation is not necessary - enable_fast_install=needless +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" fi -# The else clause should only fire when bootstrapping the -# libtool distribution, otherwise you forgot to ship ltmain.sh -# with your package, and you will get complaints that there are -# no rules to generate ltmain.sh. -if test -f "$ltmain"; then - # See if we are running on zsh, and set the options which allow our commands through - # without removal of \ escapes. - if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST - fi - # Now quote all the things that may contain metacharacters while being - # careful not to overquote the AC_SUBSTed values. We take copies of the - # variables and quote the copies for generation of the libtool script. - for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC LTCFLAGS NM \ - SED SHELL STRIP \ - libname_spec library_names_spec soname_spec extract_expsyms_cmds \ - old_striplib striplib file_magic_cmd finish_cmds finish_eval \ - deplibs_check_method reload_flag reload_cmds need_locks \ - lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \ - lt_cv_sys_global_symbol_to_c_name_address \ - sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ - old_postinstall_cmds old_postuninstall_cmds \ - compiler_CXX \ - CC_CXX \ - LD_CXX \ - lt_prog_compiler_wl_CXX \ - lt_prog_compiler_pic_CXX \ - lt_prog_compiler_static_CXX \ - lt_prog_compiler_no_builtin_flag_CXX \ - export_dynamic_flag_spec_CXX \ - thread_safe_flag_spec_CXX \ - whole_archive_flag_spec_CXX \ - enable_shared_with_static_runtimes_CXX \ - old_archive_cmds_CXX \ - old_archive_from_new_cmds_CXX \ - predep_objects_CXX \ - postdep_objects_CXX \ - predeps_CXX \ - postdeps_CXX \ - compiler_lib_search_path_CXX \ - archive_cmds_CXX \ - archive_expsym_cmds_CXX \ - postinstall_cmds_CXX \ - postuninstall_cmds_CXX \ - old_archive_from_expsyms_cmds_CXX \ - allow_undefined_flag_CXX \ - no_undefined_flag_CXX \ - export_symbols_cmds_CXX \ - hardcode_libdir_flag_spec_CXX \ - hardcode_libdir_flag_spec_ld_CXX \ - hardcode_libdir_separator_CXX \ - hardcode_automatic_CXX \ - module_cmds_CXX \ - module_expsym_cmds_CXX \ - lt_cv_prog_compiler_c_o_CXX \ - exclude_expsyms_CXX \ - include_expsyms_CXX; do - - case $var in - old_archive_cmds_CXX | \ - old_archive_from_new_cmds_CXX | \ - archive_cmds_CXX | \ - archive_expsym_cmds_CXX | \ - module_cmds_CXX | \ - module_expsym_cmds_CXX | \ - old_archive_from_expsyms_cmds_CXX | \ - export_symbols_cmds_CXX | \ - extract_expsyms_cmds | reload_cmds | finish_cmds | \ - postinstall_cmds | postuninstall_cmds | \ - old_postinstall_cmds | old_postuninstall_cmds | \ - sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) - # Double-quote double-evaled strings. - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" - ;; - *) - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" - ;; - esac - done - - case $lt_echo in - *'\$0 --fallback-echo"') - lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'` - ;; - esac - -cfgfile="$ofile" - - cat <<__EOF__ >> "$cfgfile" -# ### BEGIN LIBTOOL TAG CONFIG: $tagname -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: -# Shell to use when invoking shell scripts. -SHELL=$lt_SHELL -# Whether or not to build shared libraries. -build_libtool_libs=$enable_shared -# Whether or not to build static libraries. -build_old_libs=$enable_static -# Whether or not to add -lc for building shared libraries. -build_libtool_need_lc=$archive_cmds_need_lc_CXX -# Whether or not to disallow shared libs when runtime libs are static -allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX -# Whether or not to optimize for fast installation. -fast_install=$enable_fast_install -# The host system. -host_alias=$host_alias -host=$host -host_os=$host_os -# The build system. -build_alias=$build_alias -build=$build -build_os=$build_os -# An echo program that does not interpret backslashes. -echo=$lt_echo -# The archiver. -AR=$lt_AR -AR_FLAGS=$lt_AR_FLAGS -# A C compiler. -LTCC=$lt_LTCC -# LTCC compiler flags. -LTCFLAGS=$lt_LTCFLAGS -# A language-specific compiler. -CC=$lt_compiler_CXX -# Is the compiler the GNU C compiler? -with_gcc=$GCC_CXX -# An ERE matcher. -EGREP=$lt_EGREP -# The linker used to build libraries. -LD=$lt_LD_CXX -# Whether we need hard or soft links. -LN_S=$lt_LN_S -# A BSD-compatible nm program. -NM=$lt_NM -# A symbol stripping program -STRIP=$lt_STRIP -# Used to examine libraries when file_magic_cmd begins "file" -MAGIC_CMD=$MAGIC_CMD -# Used on cygwin: DLL creation program. -DLLTOOL="$DLLTOOL" -# Used on cygwin: object dumper. -OBJDUMP="$OBJDUMP" -# Used on cygwin: assembler. -AS="$AS" -# The name of the directory that contains temporary libtool files. -objdir=$objdir -# How to create reloadable object files. -reload_flag=$lt_reload_flag -reload_cmds=$lt_reload_cmds -# How to pass a linker flag through the compiler. -wl=$lt_lt_prog_compiler_wl_CXX -# Object file suffix (normally "o"). -objext="$ac_objext" -# Old archive suffix (normally "a"). -libext="$libext" -# Shared library suffix (normally ".so"). -shrext_cmds='$shrext_cmds' -# Executable file suffix (normally ""). -exeext="$exeext" -# Additional compiler flags for building library objects. -pic_flag=$lt_lt_prog_compiler_pic_CXX -pic_mode=$pic_mode -# What is the maximum length of a command? -max_cmd_len=$lt_cv_sys_max_cmd_len -# Does compiler simultaneously support -c and -o options? -compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX -# Must we lock files when doing compilation? -need_locks=$lt_need_locks -# Do we need the lib prefix for modules? -need_lib_prefix=$need_lib_prefix -# Do we need a version for libraries? -need_version=$need_version -# Whether dlopen is supported. -dlopen_support=$enable_dlopen -# Whether dlopen of programs is supported. -dlopen_self=$enable_dlopen_self -# Whether dlopen of statically linked programs is supported. -dlopen_self_static=$enable_dlopen_self_static -# Compiler flag to prevent dynamic linking. -link_static_flag=$lt_lt_prog_compiler_static_CXX -# Compiler flag to turn off builtin functions. -no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX -# Compiler flag to allow reflexive dlopens. -export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX -# Compiler flag to generate shared objects directly from archives. -whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX -# Compiler flag to generate thread-safe objects. -thread_safe_flag_spec=$lt_thread_safe_flag_spec_CXX -# Library versioning type. -version_type=$version_type -# Format of library name prefix. -libname_spec=$lt_libname_spec -# List of archive names. First name is the real one, the rest are links. -# The last name is the one that the linker finds with -lNAME. -library_names_spec=$lt_library_names_spec -# The coded name of the library, if different from the real name. -soname_spec=$lt_soname_spec -# Commands used to build and install an old-style archive. -RANLIB=$lt_RANLIB -old_archive_cmds=$lt_old_archive_cmds_CXX -old_postinstall_cmds=$lt_old_postinstall_cmds -old_postuninstall_cmds=$lt_old_postuninstall_cmds -# Create an old-style archive from a shared archive. -old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX -# Create a temporary old-style archive to link instead of a shared archive. -old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX -# Commands used to build and install a shared archive. -archive_cmds=$lt_archive_cmds_CXX -archive_expsym_cmds=$lt_archive_expsym_cmds_CXX -postinstall_cmds=$lt_postinstall_cmds -postuninstall_cmds=$lt_postuninstall_cmds -# Commands used to build a loadable module (assumed same as above if empty) -module_cmds=$lt_module_cmds_CXX -module_expsym_cmds=$lt_module_expsym_cmds_CXX -# Commands to strip libraries. -old_striplib=$lt_old_striplib -striplib=$lt_striplib -# Dependencies to place before the objects being linked to create a -# shared library. -predep_objects=$lt_predep_objects_CXX -# Dependencies to place after the objects being linked to create a -# shared library. -postdep_objects=$lt_postdep_objects_CXX -# Dependencies to place before the objects being linked to create a -# shared library. -predeps=$lt_predeps_CXX -# Dependencies to place after the objects being linked to create a -# shared library. -postdeps=$lt_postdeps_CXX -# The library search path used internally by the compiler when linking -# a shared library. -compiler_lib_search_path=$lt_compiler_lib_search_path_CXX -# Method to check whether dependent libraries are shared objects. -deplibs_check_method=$lt_deplibs_check_method -# Command to use when deplibs_check_method == file_magic. -file_magic_cmd=$lt_file_magic_cmd -# Flag that allows shared libraries with undefined symbols to be built. -allow_undefined_flag=$lt_allow_undefined_flag_CXX -# Flag that forces no undefined symbols. -no_undefined_flag=$lt_no_undefined_flag_CXX -# Commands used to finish a libtool library installation in a directory. -finish_cmds=$lt_finish_cmds -# Same as above, but a single script fragment to be evaled but not shown. -finish_eval=$lt_finish_eval -# Take the output of nm and produce a listing of raw symbols and C names. -global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe -# Transform the output of nm in a proper C declaration -global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl -# Transform the output of nm in a C name address pair -global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address -# This is the shared library runtime path variable. -runpath_var=$runpath_var -# This is the shared library path variable. -shlibpath_var=$shlibpath_var -# Is shlibpath searched before the hard-coded library search path? -shlibpath_overrides_runpath=$shlibpath_overrides_runpath -# How to hardcode a shared library path into an executable. -hardcode_action=$hardcode_action_CXX -# Whether we should hardcode library paths into libraries. -hardcode_into_libs=$hardcode_into_libs -# Flag to hardcode \$libdir into a binary during linking. -# This must work even if \$libdir does not exist. -hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX -# If ld is used when linking, flag to hardcode \$libdir into -# a binary during linking. This must work even if \$libdir does -# not exist. -hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_CXX -# Whether we need a single -rpath flag with a separated argument. -hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX -# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the -# resulting binary. -hardcode_direct=$hardcode_direct_CXX -# Set to yes if using the -LDIR flag during linking hardcodes DIR into the -# resulting binary. -hardcode_minus_L=$hardcode_minus_L_CXX -# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into -# the resulting binary. -hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX -# Set to yes if building a shared library automatically hardcodes DIR into the library -# and all subsequent libraries and executables linked against it. -hardcode_automatic=$hardcode_automatic_CXX -# Variables whose values should be saved in libtool wrapper scripts and -# restored at relink time. -variables_saved_for_relink="$variables_saved_for_relink" -# Whether libtool must link a program against all its dependency libraries. -link_all_deplibs=$link_all_deplibs_CXX -# Compile-time system search path for libraries -sys_lib_search_path_spec=$lt_sys_lib_search_path_spec -# Run-time system search path for libraries -sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action= +if test -n "$hardcode_libdir_flag_spec" || + test -n "$runpath_var" || + test "X$hardcode_automatic" = "Xyes" ; then -# Fix the shell variable \$srcfile for the compiler. -fix_srcfile_path="$fix_srcfile_path_CXX" + # We can hardcode non-existent directories. + if test "$hardcode_direct" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, )" != no && + test "$hardcode_minus_L" != no; then + # Linking always hardcodes the temporary library directory. + hardcode_action=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + hardcode_action=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + hardcode_action=unsupported +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action" >&5 +$as_echo "$hardcode_action" >&6; } -# Set to yes if exported symbols are required. -always_export_symbols=$always_export_symbols_CXX +if test "$hardcode_action" = relink || + test "$inherit_rpath" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi -# The commands to list exported symbols. -export_symbols_cmds=$lt_export_symbols_cmds_CXX -# The commands to extract the exported symbol list from a shared archive. -extract_expsyms_cmds=$lt_extract_expsyms_cmds -# Symbols that should not be listed in the preloaded symbols. -exclude_expsyms=$lt_exclude_expsyms_CXX -# Symbols that must always be exported. -include_expsyms=$lt_include_expsyms_CXX - -# ### END LIBTOOL TAG CONFIG: $tagname - -__EOF__ + if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown else - # If there is no Makefile yet, we rely on a make rule to execute - # `config.status --recheck' to rerun these tests and create the - # libtool script then. - ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` - if test -f "$ltmain_in"; then - test -f Makefile && make "$ltmain" - fi -fi + lt_cv_dlopen=no + lt_cv_dlopen_libs= + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu + mingw* | pw32* | cegcc*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; -CC=$lt_save_CC -LDCXX=$LD -LD=$lt_save_LD -GCC=$lt_save_GCC -with_gnu_ldcxx=$with_gnu_ld -with_gnu_ld=$lt_save_with_gnu_ld -lt_cv_path_LDCXX=$lt_cv_path_LD -lt_cv_path_LD=$lt_save_path_LD -lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld -lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; - else - tagname="" - fi - ;; + darwin*) + # if libdl is installed we need to link against it + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if test "${ac_cv_lib_dl_dlopen+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ - F77) - if test -n "$F77" && test "X$F77" != "Xno"; then +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = x""yes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else -ac_ext=f -ac_compile='$F77 -c $FFLAGS conftest.$ac_ext >&5' -ac_link='$F77 -o conftest$ac_exeext $FFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_f77_compiler_gnu - - -archive_cmds_need_lc_F77=no -allow_undefined_flag_F77= -always_export_symbols_F77=no -archive_expsym_cmds_F77= -export_dynamic_flag_spec_F77= -hardcode_direct_F77=no -hardcode_libdir_flag_spec_F77= -hardcode_libdir_flag_spec_ld_F77= -hardcode_libdir_separator_F77= -hardcode_minus_L_F77=no -hardcode_automatic_F77=no -module_cmds_F77= -module_expsym_cmds_F77= -link_all_deplibs_F77=unknown -old_archive_cmds_F77=$old_archive_cmds -no_undefined_flag_F77= -whole_archive_flag_spec_F77= -enable_shared_with_static_runtimes_F77=no + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes -# Source file extension for f77 test sources. -ac_ext=f +fi -# Object file extension for compiled f77 test sources. -objext=o -objext_F77=$objext + ;; -# Code to be used in simple compile tests -lt_simple_compile_test_code=" subroutine t\n return\n end\n" + *) + ac_fn_c_check_func "$LINENO" "shl_load" "ac_cv_func_shl_load" +if test "x$ac_cv_func_shl_load" = x""yes; then : + lt_cv_dlopen="shl_load" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for shl_load in -ldld" >&5 +$as_echo_n "checking for shl_load in -ldld... " >&6; } +if test "${ac_cv_lib_dld_shl_load+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ -# Code to be used in simple link tests -lt_simple_link_test_code=" program t\n end\n" +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char shl_load (); +int +main () +{ +return shl_load (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_shl_load=yes +else + ac_cv_lib_dld_shl_load=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_shl_load" >&5 +$as_echo "$ac_cv_lib_dld_shl_load" >&6; } +if test "x$ac_cv_lib_dld_shl_load" = x""yes; then : + lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld" +else + ac_fn_c_check_func "$LINENO" "dlopen" "ac_cv_func_dlopen" +if test "x$ac_cv_func_dlopen" = x""yes; then : + lt_cv_dlopen="dlopen" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -ldl" >&5 +$as_echo_n "checking for dlopen in -ldl... " >&6; } +if test "${ac_cv_lib_dl_dlopen+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldl $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ -# ltmain only uses $CC for tagged configurations so make sure $CC is set. +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dl_dlopen=yes +else + ac_cv_lib_dl_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dl_dlopen" >&5 +$as_echo "$ac_cv_lib_dl_dlopen" >&6; } +if test "x$ac_cv_lib_dl_dlopen" = x""yes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dlopen in -lsvld" >&5 +$as_echo_n "checking for dlopen in -lsvld... " >&6; } +if test "${ac_cv_lib_svld_dlopen+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-lsvld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dlopen (); +int +main () +{ +return dlopen (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_svld_dlopen=yes +else + ac_cv_lib_svld_dlopen=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_svld_dlopen" >&5 +$as_echo "$ac_cv_lib_svld_dlopen" >&6; } +if test "x$ac_cv_lib_svld_dlopen" = x""yes; then : + lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld" +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for dld_link in -ldld" >&5 +$as_echo_n "checking for dld_link in -ldld... " >&6; } +if test "${ac_cv_lib_dld_dld_link+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ldld $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char dld_link (); +int +main () +{ +return dld_link (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_dld_dld_link=yes +else + ac_cv_lib_dld_dld_link=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_dld_dld_link" >&5 +$as_echo "$ac_cv_lib_dld_dld_link" >&6; } +if test "x$ac_cv_lib_dld_dld_link" = x""yes; then : + lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld" +fi -# Allow CC to be a program name with arguments. -compiler=$CC +fi -# save warnings/boilerplate of simple test code -ac_outfile=conftest.$ac_objext -printf "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$rm conftest* -ac_outfile=conftest.$ac_objext -printf "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$rm conftest* +fi -# Allow CC to be a program name with arguments. -lt_save_CC="$CC" -CC=${F77-"f77"} -compiler=$CC -compiler_F77=$CC -for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` +fi -{ echo "$as_me:$LINENO: checking if libtool supports shared libraries" >&5 -echo $ECHO_N "checking if libtool supports shared libraries... $ECHO_C" >&6; } -{ echo "$as_me:$LINENO: result: $can_build_shared" >&5 -echo "${ECHO_T}$can_build_shared" >&6; } +fi -{ echo "$as_me:$LINENO: checking whether to build shared libraries" >&5 -echo $ECHO_N "checking whether to build shared libraries... $ECHO_C" >&6; } -test "$can_build_shared" = "no" && enable_shared=no -# On AIX, shared libraries and static libraries use the same namespace, and -# are all built from PIC. -case $host_os in -aix3*) - test "$enable_shared" = yes && enable_static=no - if test -n "$RANLIB"; then - archive_cmds="$archive_cmds~\$RANLIB \$lib" - postinstall_cmds='$RANLIB $lib' - fi - ;; -aix4* | aix5*) - if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then - test "$enable_shared" = yes && enable_static=no - fi - ;; -esac -{ echo "$as_me:$LINENO: result: $enable_shared" >&5 -echo "${ECHO_T}$enable_shared" >&6; } +fi -{ echo "$as_me:$LINENO: checking whether to build static libraries" >&5 -echo $ECHO_N "checking whether to build static libraries... $ECHO_C" >&6; } -# Make sure either enable_shared or enable_static is yes. -test "$enable_shared" = yes || enable_static=yes -{ echo "$as_me:$LINENO: result: $enable_static" >&5 -echo "${ECHO_T}$enable_static" >&6; } - -GCC_F77="$G77" -LD_F77="$LD" - -lt_prog_compiler_wl_F77= -lt_prog_compiler_pic_F77= -lt_prog_compiler_static_F77= + ;; + esac -{ echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5 -echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6; } + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi - if test "$GCC" = yes; then - lt_prog_compiler_wl_F77='-Wl,' - lt_prog_compiler_static_F77='-static' + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static_F77='-Bstatic' - fi - ;; + save_LDFLAGS="$LDFLAGS" + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" - amigaos*) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - lt_prog_compiler_pic_F77='-m68020 -resident32 -malways-restore-a4' - ;; + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" - beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) - # PIC is the default for these OSes. - ;; + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a program can dlopen itself" >&5 +$as_echo_n "checking whether a program can dlopen itself... " >&6; } +if test "${lt_cv_dlopen_self+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line 11120 "configure" +#include "confdefs.h" - mingw* | pw32* | os2*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic_F77='-DDLL_EXPORT' - ;; +#if HAVE_DLFCN_H +#include +#endif - darwin* | rhapsody*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - lt_prog_compiler_pic_F77='-fno-common' - ;; +#include - interix3*) - # Interix 3.x gcc -fpic/-fPIC options generate broken code. - # Instead, we relocate shared libraries at runtime. - ;; +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif - msdosdjgpp*) - # Just because we use GCC doesn't mean we suddenly get shared libraries - # on systems that don't support them. - lt_prog_compiler_can_build_shared_F77=no - enable_shared=no - ;; +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif - sysv4*MP*) - if test -d /usr/nec; then - lt_prog_compiler_pic_F77=-Kconform_pic - fi - ;; +void fnord() { int i=42;} +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; - hpux*) - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic_F77='-fPIC' - ;; - esac - ;; + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + /* dlclose (self); */ + } + else + puts (dlerror ()); - *) - lt_prog_compiler_pic_F77='-fPIC' - ;; + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self=no ;; esac + else : + # compilation failed + lt_cv_dlopen_self=no + fi +fi +rm -fr conftest* + + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self" >&5 +$as_echo "$lt_cv_dlopen_self" >&6; } + + if test "x$lt_cv_dlopen_self" = xyes; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether a statically linked program can dlopen itself" >&5 +$as_echo_n "checking whether a statically linked program can dlopen itself... " >&6; } +if test "${lt_cv_dlopen_self_static+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test "$cross_compiling" = yes; then : + lt_cv_dlopen_self_static=cross +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +#line 11216 "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +void fnord() { int i=42;} +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + /* dlclose (self); */ + } else - # PORTME Check for flag to pass linker flags through the system compiler. - case $host_os in - aix*) - lt_prog_compiler_wl_F77='-Wl,' - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static_F77='-Bstatic' - else - lt_prog_compiler_static_F77='-bnso -bI:/lib/syscalls.exp' - fi - ;; - darwin*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - case $cc_basename in - xlc*) - lt_prog_compiler_pic_F77='-qnocommon' - lt_prog_compiler_wl_F77='-Wl,' - ;; - esac - ;; + puts (dlerror ()); - mingw* | pw32* | os2*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic_F77='-DDLL_EXPORT' - ;; + return status; +} +_LT_EOF + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_link\""; } >&5 + (eval $ac_link) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&5 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlneed_uscore) lt_cv_dlopen_self_static=yes ;; + x$lt_dlunknown|x*) lt_cv_dlopen_self_static=no ;; + esac + else : + # compilation failed + lt_cv_dlopen_self_static=no + fi +fi +rm -fr conftest* - hpux9* | hpux10* | hpux11*) - lt_prog_compiler_wl_F77='-Wl,' - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default - ;; - *) - lt_prog_compiler_pic_F77='+Z' - ;; - esac - # Is there a better lt_prog_compiler_static that works with the bundled CC? - lt_prog_compiler_static_F77='${wl}-a ${wl}archive' - ;; - irix5* | irix6* | nonstopux*) - lt_prog_compiler_wl_F77='-Wl,' - # PIC (with -KPIC) is the default. - lt_prog_compiler_static_F77='-non_shared' - ;; +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_dlopen_self_static" >&5 +$as_echo "$lt_cv_dlopen_self_static" >&6; } + fi - newsos6) - lt_prog_compiler_pic_F77='-KPIC' - lt_prog_compiler_static_F77='-Bstatic' - ;; + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac - linux*) - case $cc_basename in - icc* | ecc*) - lt_prog_compiler_wl_F77='-Wl,' - lt_prog_compiler_pic_F77='-KPIC' - lt_prog_compiler_static_F77='-static' - ;; - pgcc* | pgf77* | pgf90* | pgf95*) - # Portland Group compilers (*not* the Pentium gcc compiler, - # which looks to be a dead project) - lt_prog_compiler_wl_F77='-Wl,' - lt_prog_compiler_pic_F77='-fpic' - lt_prog_compiler_static_F77='-Bstatic' - ;; - ccc*) - lt_prog_compiler_wl_F77='-Wl,' - # All Alpha code is PIC. - lt_prog_compiler_static_F77='-non_shared' - ;; - esac - ;; + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac - osf3* | osf4* | osf5*) - lt_prog_compiler_wl_F77='-Wl,' - # All OSF/1 code is PIC. - lt_prog_compiler_static_F77='-non_shared' - ;; + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi - solaris*) - lt_prog_compiler_pic_F77='-KPIC' - lt_prog_compiler_static_F77='-Bstatic' - case $cc_basename in - f77* | f90* | f95*) - lt_prog_compiler_wl_F77='-Qoption ld ';; - *) - lt_prog_compiler_wl_F77='-Wl,';; - esac - ;; - sunos4*) - lt_prog_compiler_wl_F77='-Qoption ld ' - lt_prog_compiler_pic_F77='-PIC' - lt_prog_compiler_static_F77='-Bstatic' - ;; - sysv4 | sysv4.2uw2* | sysv4.3*) - lt_prog_compiler_wl_F77='-Wl,' - lt_prog_compiler_pic_F77='-KPIC' - lt_prog_compiler_static_F77='-Bstatic' - ;; - sysv4*MP*) - if test -d /usr/nec ;then - lt_prog_compiler_pic_F77='-Kconform_pic' - lt_prog_compiler_static_F77='-Bstatic' - fi - ;; - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - lt_prog_compiler_wl_F77='-Wl,' - lt_prog_compiler_pic_F77='-KPIC' - lt_prog_compiler_static_F77='-Bstatic' - ;; - unicos*) - lt_prog_compiler_wl_F77='-Wl,' - lt_prog_compiler_can_build_shared_F77=no - ;; - uts4*) - lt_prog_compiler_pic_F77='-pic' - lt_prog_compiler_static_F77='-Bstatic' - ;; - *) - lt_prog_compiler_can_build_shared_F77=no - ;; - esac - fi -{ echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_F77" >&5 -echo "${ECHO_T}$lt_prog_compiler_pic_F77" >&6; } -# -# Check to make sure the PIC flag actually works. -# -if test -n "$lt_prog_compiler_pic_F77"; then -{ echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic_F77 works" >&5 -echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic_F77 works... $ECHO_C" >&6; } -if test "${lt_prog_compiler_pic_works_F77+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_prog_compiler_pic_works_F77=no - ac_outfile=conftest.$ac_objext - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$lt_prog_compiler_pic_F77" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:13913: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:13917: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_prog_compiler_pic_works_F77=yes - fi - fi - $rm conftest* -fi -{ echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_works_F77" >&5 -echo "${ECHO_T}$lt_prog_compiler_pic_works_F77" >&6; } -if test x"$lt_prog_compiler_pic_works_F77" = xyes; then - case $lt_prog_compiler_pic_F77 in - "" | " "*) ;; - *) lt_prog_compiler_pic_F77=" $lt_prog_compiler_pic_F77" ;; - esac -else - lt_prog_compiler_pic_F77= - lt_prog_compiler_can_build_shared_F77=no -fi -fi -case $host_os in - # For platforms which do not support PIC, -DPIC is meaningless: - *djgpp*) - lt_prog_compiler_pic_F77= + + + +striplib= +old_striplib= +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether stripping libraries is possible" >&5 +$as_echo_n "checking whether stripping libraries is possible... " >&6; } +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + fi ;; *) - lt_prog_compiler_pic_F77="$lt_prog_compiler_pic_F77" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } ;; -esac + esac +fi -# -# Check to make sure the static flag actually works. -# -wl=$lt_prog_compiler_wl_F77 eval lt_tmp_static_flag=\"$lt_prog_compiler_static_F77\" -{ echo "$as_me:$LINENO: checking if $compiler static flag $lt_tmp_static_flag works" >&5 -echo $ECHO_N "checking if $compiler static flag $lt_tmp_static_flag works... $ECHO_C" >&6; } -if test "${lt_prog_compiler_static_works_F77+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_prog_compiler_static_works_F77=no - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $lt_tmp_static_flag" - printf "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&5 - $echo "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - lt_prog_compiler_static_works_F77=yes - fi - else - lt_prog_compiler_static_works_F77=yes - fi - fi - $rm conftest* - LDFLAGS="$save_LDFLAGS" -fi -{ echo "$as_me:$LINENO: result: $lt_prog_compiler_static_works_F77" >&5 -echo "${ECHO_T}$lt_prog_compiler_static_works_F77" >&6; } -if test x"$lt_prog_compiler_static_works_F77" = xyes; then - : -else - lt_prog_compiler_static_F77= -fi -{ echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5 -echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6; } -if test "${lt_cv_prog_compiler_c_o_F77+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_cv_prog_compiler_c_o_F77=no - $rm -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:14017: $lt_compile\"" >&5) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&5 - echo "$as_me:14021: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - lt_cv_prog_compiler_c_o_F77=yes - fi - fi - chmod u+w . 2>&5 - $rm conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files - $rm out/* && rmdir out - cd .. - rmdir conftest - $rm conftest* -fi -{ echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o_F77" >&5 -echo "${ECHO_T}$lt_cv_prog_compiler_c_o_F77" >&6; } -hard_links="nottested" -if test "$lt_cv_prog_compiler_c_o_F77" = no && test "$need_locks" != no; then - # do not overwrite the value of need_locks provided by the user - { echo "$as_me:$LINENO: checking if we can lock with hard links" >&5 -echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6; } - hard_links=yes - $rm conftest* - ln conftest.a conftest.b 2>/dev/null && hard_links=no - touch conftest.a - ln conftest.a conftest.b 2>&5 || hard_links=no - ln conftest.a conftest.b 2>/dev/null && hard_links=no - { echo "$as_me:$LINENO: result: $hard_links" >&5 -echo "${ECHO_T}$hard_links" >&6; } - if test "$hard_links" = no; then - { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 -echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} - need_locks=warn - fi -else - need_locks=no -fi -{ echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5 -echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6; } - runpath_var= - allow_undefined_flag_F77= - enable_shared_with_static_runtimes_F77=no - archive_cmds_F77= - archive_expsym_cmds_F77= - old_archive_From_new_cmds_F77= - old_archive_from_expsyms_cmds_F77= - export_dynamic_flag_spec_F77= - whole_archive_flag_spec_F77= - thread_safe_flag_spec_F77= - hardcode_libdir_flag_spec_F77= - hardcode_libdir_flag_spec_ld_F77= - hardcode_libdir_separator_F77= - hardcode_direct_F77=no - hardcode_minus_L_F77=no - hardcode_shlibpath_var_F77=unsupported - link_all_deplibs_F77=unknown - hardcode_automatic_F77=no - module_cmds_F77= - module_expsym_cmds_F77= - always_export_symbols_F77=no - export_symbols_cmds_F77='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - # include_expsyms should be a list of space-separated symbols to be *always* - # included in the symbol list - include_expsyms_F77= - # exclude_expsyms can be an extended regexp of symbols to exclude - # it will be wrapped by ` (' and `)$', so one must not match beginning or - # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', - # as well as any symbol that contains `d'. - exclude_expsyms_F77="_GLOBAL_OFFSET_TABLE_" - # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out - # platforms (ab)use it in PIC code, but their linkers get confused if - # the symbol is explicitly referenced. Since portable code cannot - # rely on this symbol name, it's probably fine to never include it in - # preloaded symbol tables. - extract_expsyms_cmds= - # Just being paranoid about ensuring that cc_basename is set. - for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` + # Report which library types will actually be built + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if libtool supports shared libraries" >&5 +$as_echo_n "checking if libtool supports shared libraries... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $can_build_shared" >&5 +$as_echo "$can_build_shared" >&6; } + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build shared libraries" >&5 +$as_echo_n "checking whether to build shared libraries... " >&6; } + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. case $host_os in - cygwin* | mingw* | pw32*) - # FIXME: the MSVC++ port hasn't been tested in a loooong time - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - if test "$GCC" != yes; then - with_gnu_ld=no + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' fi ;; - interix*) - # we just hope/assume this is gcc and not c89 (= MSVC++) - with_gnu_ld=yes - ;; - openbsd*) - with_gnu_ld=no + + aix[4-9]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi ;; esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_shared" >&5 +$as_echo "$enable_shared" >&6; } - ld_shlibs_F77=yes - if test "$with_gnu_ld" = yes; then - # If archive_cmds runs LD, not CC, wlarc should be empty - wlarc='${wl}' + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to build static libraries" >&5 +$as_echo_n "checking whether to build static libraries... " >&6; } + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_static" >&5 +$as_echo "$enable_static" >&6; } - # Set some defaults for GNU ld with shared library support. These - # are reset later if shared libraries are not supported. Putting them - # here allows them to be overridden if necessary. - runpath_var=LD_RUN_PATH - hardcode_libdir_flag_spec_F77='${wl}--rpath ${wl}$libdir' - export_dynamic_flag_spec_F77='${wl}--export-dynamic' - # ancient GNU ld didn't support --whole-archive et. al. - if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then - whole_archive_flag_spec_F77="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - whole_archive_flag_spec_F77= - fi - supports_anon_versioning=no - case `$LD -v 2>/dev/null` in - *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 - *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... - *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... - *\ 2.11.*) ;; # other 2.11 versions - *) supports_anon_versioning=yes ;; - esac - # See if GNU ld supports shared libraries. - case $host_os in - aix3* | aix4* | aix5*) - # On AIX/PPC, the GNU linker is very broken - if test "$host_cpu" != ia64; then - ld_shlibs_F77=no - cat <&2 -*** Warning: the GNU linker, at least up to release 2.9.1, is reported -*** to be unable to reliably create shared libraries on AIX. -*** Therefore, libtool is disabling shared libraries support. If you -*** really care for shared libraries, you may want to modify your PATH -*** so that a non-GNU linker is found, and then restart. -EOF - fi - ;; +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu - amigaos*) - archive_cmds_F77='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - hardcode_libdir_flag_spec_F77='-L$libdir' - hardcode_minus_L_F77=yes - - # Samuel A. Falvo II reports - # that the semantics of dynamic libraries on AmigaOS, at least up - # to version 4, is to share data among multiple programs linked - # with the same dynamic library. Since this doesn't match the - # behavior of shared libraries on other platforms, we can't use - # them. - ld_shlibs_F77=no - ;; +CC="$lt_save_CC" - beos*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - allow_undefined_flag_F77=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - archive_cmds_F77='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - ld_shlibs_F77=no - fi - ;; - cygwin* | mingw* | pw32*) - # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, F77) is actually meaningless, - # as there is no search path for DLLs. - hardcode_libdir_flag_spec_F77='-L$libdir' - allow_undefined_flag_F77=unsupported - always_export_symbols_F77=no - enable_shared_with_static_runtimes_F77=yes - export_symbols_cmds_F77='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS] /s/.* \([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW] /s/.* //'\'' | sort | uniq > $export_symbols' +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu - if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then - archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - archive_expsym_cmds_F77='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - ld_shlibs_F77=no - fi - ;; +archive_cmds_need_lc_CXX=no +allow_undefined_flag_CXX= +always_export_symbols_CXX=no +archive_expsym_cmds_CXX= +compiler_needs_object_CXX=no +export_dynamic_flag_spec_CXX= +hardcode_direct_CXX=no +hardcode_direct_absolute_CXX=no +hardcode_libdir_flag_spec_CXX= +hardcode_libdir_flag_spec_ld_CXX= +hardcode_libdir_separator_CXX= +hardcode_minus_L_CXX=no +hardcode_shlibpath_var_CXX=unsupported +hardcode_automatic_CXX=no +inherit_rpath_CXX=no +module_cmds_CXX= +module_expsym_cmds_CXX= +link_all_deplibs_CXX=unknown +old_archive_cmds_CXX=$old_archive_cmds +no_undefined_flag_CXX= +whole_archive_flag_spec_CXX= +enable_shared_with_static_runtimes_CXX=no - interix3*) - hardcode_direct_F77=no - hardcode_shlibpath_var_F77=no - hardcode_libdir_flag_spec_F77='${wl}-rpath,$libdir' - export_dynamic_flag_spec_F77='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - archive_cmds_F77='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - archive_expsym_cmds_F77='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; +# Source file extension for C++ test sources. +ac_ext=cpp - linux*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - tmp_addflag= - case $cc_basename,$host_cpu in - pgcc*) # Portland Group C compiler - whole_archive_flag_spec_F77='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag' - ;; - pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers - whole_archive_flag_spec_F77='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag -Mnomain' ;; - ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 - tmp_addflag=' -i_dynamic' ;; - efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 - tmp_addflag=' -i_dynamic -nofor_main' ;; - ifc* | ifort*) # Intel Fortran compiler - tmp_addflag=' -nofor_main' ;; - esac - archive_cmds_F77='$CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' +# Object file extension for compiled C++ test sources. +objext=o +objext_CXX=$objext - if test $supports_anon_versioning = yes; then - archive_expsym_cmds_F77='$echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - $echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' - fi - else - ld_shlibs_F77=no - fi - ;; +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_caught_CXX_error" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - archive_cmds_F77='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' - wlarc= - else - archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - fi - ;; + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[]) { return(0); }' - solaris*) - if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then - ld_shlibs_F77=no - cat <&2 + # ltmain only uses $CC for tagged configurations so make sure $CC is set. -*** Warning: The releases 2.8.* of the GNU linker cannot reliably -*** create shared libraries on Solaris systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.9.1 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. -EOF - elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs_F77=no - fi - ;; - sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) - case `$LD -v 2>&1` in - *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) - ld_shlibs_F77=no - cat <<_LT_EOF 1>&2 -*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not -*** reliably create shared libraries on SCO systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.16.91.0.3 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. -_LT_EOF - ;; - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - hardcode_libdir_flag_spec_F77='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' - archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib' - archive_expsym_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname,-retain-symbols-file,$export_symbols -o $lib' - else - ld_shlibs_F77=no - fi - ;; - esac - ;; - sunos4*) - archive_cmds_F77='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' - wlarc= - hardcode_direct_F77=yes - hardcode_shlibpath_var_F77=no - ;; +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs_F77=no - fi - ;; - esac +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - if test "$ld_shlibs_F77" = no; then - runpath_var= - hardcode_libdir_flag_spec_F77= - export_dynamic_flag_spec_F77= - whole_archive_flag_spec_F77= - fi - else - # PORTME fill in a description of your system's linker (not GNU ld) - case $host_os in - aix3*) - allow_undefined_flag_F77=unsupported - always_export_symbols_F77=yes - archive_expsym_cmds_F77='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' - # Note: this linker hardcodes the directories in LIBPATH if there - # are no directories specified by -L. - hardcode_minus_L_F77=yes - if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then - # Neither direct hardcoding nor static linking is supported with a - # broken collect2. - hardcode_direct_F77=unsupported - fi - ;; +# Allow CC to be a program name with arguments. +compiler=$CC - aix4* | aix5*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - if $NM -V 2>&1 | grep 'GNU' > /dev/null; then - export_symbols_cmds_F77='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' - else - export_symbols_cmds_F77='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' - fi - aix_use_runtimelinking=no - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[23]|aix4.[23].*|aix5*) - for ld_flag in $LDFLAGS; do - if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then - aix_use_runtimelinking=yes - break - fi - done - ;; - esac + # save warnings/boilerplate of simple test code + ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi + ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - archive_cmds_F77='' - hardcode_direct_F77=yes - hardcode_libdir_separator_F77=':' - link_all_deplibs_F77=yes + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + compiler=$CC + compiler_CXX=$CC + for cc_temp in $compiler""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` - if test "$GCC" = yes; then - case $host_os in aix4.[012]|aix4.[012].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && \ - strings "$collect2name" | grep resolve_lib_name >/dev/null - then - # We have reworked collect2 - hardcode_direct_F77=yes - else - # We have old collect2 - hardcode_direct_F77=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - hardcode_minus_L_F77=yes - hardcode_libdir_flag_spec_F77='-L$libdir' - hardcode_libdir_separator_F77= - fi - ;; - esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to export. - always_export_symbols_F77=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - allow_undefined_flag_F77='-berok' - # Determine the default libpath from the value encoded in an empty executable. - cat >conftest.$ac_ext <<_ACEOF - program main + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test "$GXX" = yes; then + lt_prog_compiler_no_builtin_flag_CXX=' -fno-builtin' + else + lt_prog_compiler_no_builtin_flag_CXX= + fi - end -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_f77_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then + if test "$GXX" = yes; then + # Set up default GNU C++ configuration -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +# Check whether --with-gnu-ld was given. +if test "${with_gnu_ld+set}" = set; then : + withval=$with_gnu_ld; test "$withval" = no || with_gnu_ld=yes +else + with_gnu_ld=no fi -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - - hardcode_libdir_flag_spec_F77='${wl}-blibpath:$libdir:'"$aix_libpath" - archive_expsym_cmds_F77="\$CC"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - hardcode_libdir_flag_spec_F77='${wl}-R $libdir:/usr/lib:/lib' - allow_undefined_flag_F77="-z nodefs" - archive_expsym_cmds_F77="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an empty executable. - cat >conftest.$ac_ext <<_ACEOF - program main +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ld used by $CC" >&5 +$as_echo_n "checking for ld used by $CC... " >&6; } + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [\\/]* | ?:[\\/]*) + re_direlt='/[^/][^/]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU ld" >&5 +$as_echo_n "checking for GNU ld... " >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for non-GNU ld" >&5 +$as_echo_n "checking for non-GNU ld... " >&6; } +fi +if test "${lt_cv_path_LD+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &5 +$as_echo "$LD" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +test -z "$LD" && as_fn_error "no acceptable ld found in \$PATH" "$LINENO" 5 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if the linker ($LD) is GNU ld" >&5 +$as_echo_n "checking if the linker ($LD) is GNU ld... " >&6; } +if test "${lt_cv_prog_gnu_ld+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + # I'd rather use --version here, but apparently some GNU lds only accept -v. +case `$LD -v 2>&1 &5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_f77_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_gnu_ld" >&5 +$as_echo "$lt_cv_prog_gnu_ld" >&6; } +with_gnu_ld=$lt_cv_prog_gnu_ld -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 -fi -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - hardcode_libdir_flag_spec_F77='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - no_undefined_flag_F77=' ${wl}-bernotok' - allow_undefined_flag_F77=' ${wl}-berok' - # Exported symbols can be pulled into shared objects from archives - whole_archive_flag_spec_F77='$convenience' - archive_cmds_need_lc_F77=yes - # This is similar to how AIX traditionally builds its shared libraries. - archive_expsym_cmds_F77="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' - fi - fi - ;; - amigaos*) - archive_cmds_F77='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - hardcode_libdir_flag_spec_F77='-L$libdir' - hardcode_minus_L_F77=yes - # see comment about different semantics on the GNU ld section - ld_shlibs_F77=no - ;; - bsdi[45]*) - export_dynamic_flag_spec_F77=-rdynamic - ;; + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test "$with_gnu_ld" = yes; then + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='${wl}' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + whole_archive_flag_spec_CXX= + fi + else + with_gnu_ld=no + wlarc= - cygwin* | mingw* | pw32*) - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - hardcode_libdir_flag_spec_F77=' ' - allow_undefined_flag_F77=unsupported - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" - # FIXME: Setting linknames here is a bad hack. - archive_cmds_F77='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames=' - # The linker will automatically build a .lib file if we build a DLL. - old_archive_From_new_cmds_F77='true' - # FIXME: Should let the user specify the lib program. - old_archive_cmds_F77='lib /OUT:$oldlib$oldobjs$old_deplibs' - fix_srcfile_path_F77='`cygpath -w "$srcfile"`' - enable_shared_with_static_runtimes_F77=yes - ;; + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' - darwin* | rhapsody*) - case $host_os in - rhapsody* | darwin1.[012]) - allow_undefined_flag_F77='${wl}-undefined ${wl}suppress' - ;; - *) # Darwin 1.3 on - if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then - allow_undefined_flag_F77='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - else - case ${MACOSX_DEPLOYMENT_TARGET} in - 10.[012]) - allow_undefined_flag_F77='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - ;; - 10.*) - allow_undefined_flag_F77='${wl}-undefined ${wl}dynamic_lookup' - ;; - esac - fi - ;; - esac - archive_cmds_need_lc_F77=no - hardcode_direct_F77=no - hardcode_automatic_F77=yes - hardcode_shlibpath_var_F77=unsupported - whole_archive_flag_spec_F77='' - link_all_deplibs_F77=yes - if test "$GCC" = yes ; then - output_verbose_link_cmd='echo' - archive_cmds_F77='$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' - module_cmds_F77='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - archive_expsym_cmds_F77='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - module_expsym_cmds_F77='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' else - case $cc_basename in - xlc*) - output_verbose_link_cmd='echo' - archive_cmds_F77='$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}`echo $rpath/$soname` $verstring' - module_cmds_F77='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - archive_expsym_cmds_F77='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}$rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - module_expsym_cmds_F77='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - ;; - *) - ld_shlibs_F77=no - ;; - esac + GXX=no + with_gnu_ld=no + wlarc= fi - ;; - dgux*) - archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec_F77='-L$libdir' - hardcode_shlibpath_var_F77=no - ;; + # PORTME: fill in a description of your system's C++ link characteristics + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } + ld_shlibs_CXX=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aix[4-9]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + aix_use_runtimelinking=no - freebsd1*) - ld_shlibs_F77=no - ;; + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix[5-9]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + ;; + esac - # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor - # support. Future versions do this automatically, but an explicit c++rt0.o - # does not break anything, and helps significantly (at the cost of a little - # extra space). - freebsd2.2*) - archive_cmds_F77='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' - hardcode_libdir_flag_spec_F77='-R$libdir' - hardcode_direct_F77=yes - hardcode_shlibpath_var_F77=no - ;; + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi - # Unfortunately, older versions of FreeBSD 2 do not have this feature. - freebsd2*) - archive_cmds_F77='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct_F77=yes - hardcode_minus_L_F77=yes - hardcode_shlibpath_var_F77=no - ;; + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + archive_cmds_CXX='' + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + file_list_spec_CXX='${wl}-f,' + + if test "$GXX" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + hardcode_direct_CXX=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + hardcode_minus_L_CXX=yes + hardcode_libdir_flag_spec_CXX='-L$libdir' + hardcode_libdir_separator_CXX= + fi + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi - # FreeBSD 3 and greater uses gcc -shared to do shared libraries. - freebsd* | kfreebsd*-gnu | dragonfly*) - archive_cmds_F77='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' - hardcode_libdir_flag_spec_F77='-R$libdir' - hardcode_direct_F77=yes - hardcode_shlibpath_var_F77=no - ;; + export_dynamic_flag_spec_CXX='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + always_export_symbols_CXX=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + allow_undefined_flag_CXX='-berok' + # Determine the default libpath from the value encoded in an empty + # executable. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ - hpux9*) - if test "$GCC" = yes; then - archive_cmds_F77='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - archive_cmds_F77='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - fi - hardcode_libdir_flag_spec_F77='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_F77=: - hardcode_direct_F77=yes +int +main () +{ - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L_F77=yes - export_dynamic_flag_spec_F77='${wl}-E' - ;; + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : - hpux10*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - archive_cmds_F77='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds_F77='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' - fi - if test "$with_gnu_ld" = no; then - hardcode_libdir_flag_spec_F77='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_F77=: +lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\(.*\)$/\1/ + p + } + }' +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - hardcode_direct_F77=yes - export_dynamic_flag_spec_F77='${wl}-E' + hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L_F77=yes - fi - ;; + archive_expsym_cmds_CXX='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec_CXX='${wl}-R $libdir:/usr/lib:/lib' + allow_undefined_flag_CXX="-z nodefs" + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ - hpux11*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - case $host_cpu in - hppa*64*) - archive_cmds_F77='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - archive_cmds_F77='$CC -shared ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds_F77='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - else - case $host_cpu in - hppa*64*) - archive_cmds_F77='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - archive_cmds_F77='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds_F77='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - fi - if test "$with_gnu_ld" = no; then - hardcode_libdir_flag_spec_F77='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_F77=: +int +main () +{ - case $host_cpu in - hppa*64*|ia64*) - hardcode_libdir_flag_spec_ld_F77='+b $libdir' - hardcode_direct_F77=no - hardcode_shlibpath_var_F77=no - ;; - *) - hardcode_direct_F77=yes - export_dynamic_flag_spec_F77='${wl}-E' + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L_F77=yes - ;; - esac - fi - ;; +lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\(.*\)$/\1/ + p + } + }' +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - irix5* | irix6* | nonstopux*) - if test "$GCC" = yes; then - archive_cmds_F77='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - archive_cmds_F77='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - hardcode_libdir_flag_spec_ld_F77='-rpath $libdir' - fi - hardcode_libdir_flag_spec_F77='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_F77=: - link_all_deplibs_F77=yes - ;; + hardcode_libdir_flag_spec_CXX='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + no_undefined_flag_CXX=' ${wl}-bernotok' + allow_undefined_flag_CXX=' ${wl}-berok' + # Exported symbols can be pulled into shared objects from archives + whole_archive_flag_spec_CXX='$convenience' + archive_cmds_need_lc_CXX=yes + # This is similar to how AIX traditionally builds its shared + # libraries. + archive_expsym_cmds_CXX="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - archive_cmds_F77='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out - else - archive_cmds_F77='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF - fi - hardcode_libdir_flag_spec_F77='-R$libdir' - hardcode_direct_F77=yes - hardcode_shlibpath_var_F77=no - ;; + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + allow_undefined_flag_CXX=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + archive_cmds_CXX='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + ld_shlibs_CXX=no + fi + ;; - newsos6) - archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct_F77=yes - hardcode_libdir_flag_spec_F77='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_F77=: - hardcode_shlibpath_var_F77=no - ;; + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; - openbsd*) - hardcode_direct_F77=yes - hardcode_shlibpath_var_F77=no - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - archive_cmds_F77='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_F77='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' - hardcode_libdir_flag_spec_F77='${wl}-rpath,$libdir' - export_dynamic_flag_spec_F77='${wl}-E' - else - case $host_os in - openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) - archive_cmds_F77='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec_F77='-R$libdir' - ;; - *) - archive_cmds_F77='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - hardcode_libdir_flag_spec_F77='${wl}-rpath,$libdir' - ;; - esac - fi - ;; + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, CXX) is actually meaningless, + # as there is no search path for DLLs. + hardcode_libdir_flag_spec_CXX='-L$libdir' + allow_undefined_flag_CXX=unsupported + always_export_symbols_CXX=no + enable_shared_with_static_runtimes_CXX=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + archive_expsym_cmds_CXX='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + ld_shlibs_CXX=no + fi + ;; + darwin* | rhapsody*) - os2*) - hardcode_libdir_flag_spec_F77='-L$libdir' - hardcode_minus_L_F77=yes - allow_undefined_flag_F77=unsupported - archive_cmds_F77='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' - old_archive_From_new_cmds_F77='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' - ;; - osf3*) - if test "$GCC" = yes; then - allow_undefined_flag_F77=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds_F77='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - allow_undefined_flag_F77=' -expect_unresolved \*' - archive_cmds_F77='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - fi - hardcode_libdir_flag_spec_F77='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_F77=: - ;; + archive_cmds_need_lc_CXX=no + hardcode_direct_CXX=no + hardcode_automatic_CXX=yes + hardcode_shlibpath_var_CXX=unsupported + whole_archive_flag_spec_CXX='' + link_all_deplibs_CXX=yes + allow_undefined_flag_CXX="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=echo + archive_cmds_CXX="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + module_cmds_CXX="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + module_expsym_cmds_CXX="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + if test "$lt_cv_apple_cc_single_mod" != "yes"; then + archive_cmds_CXX="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" + archive_expsym_cmds_CXX="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi - osf4* | osf5*) # as osf3* with the addition of -msym flag - if test "$GCC" = yes; then - allow_undefined_flag_F77=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds_F77='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - hardcode_libdir_flag_spec_F77='${wl}-rpath ${wl}$libdir' - else - allow_undefined_flag_F77=' -expect_unresolved \*' - archive_cmds_F77='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - archive_expsym_cmds_F77='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~ - $LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib~$rm $lib.exp' + else + ld_shlibs_CXX=no + fi - # Both c and cxx compiler support -rpath directly - hardcode_libdir_flag_spec_F77='-rpath $libdir' - fi - hardcode_libdir_separator_F77=: - ;; + ;; - solaris*) - no_undefined_flag_F77=' -z text' - if test "$GCC" = yes; then - wlarc='${wl}' - archive_cmds_F77='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_F77='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp' - else - wlarc='' - archive_cmds_F77='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' - archive_expsym_cmds_F77='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' - fi - hardcode_libdir_flag_spec_F77='-R$libdir' - hardcode_shlibpath_var_F77=no - case $host_os in - solaris2.[0-5] | solaris2.[0-5].*) ;; - *) - # The compiler driver will combine linker options so we - # cannot just pass the convience library names through - # without $wl, iff we do not link with $LD. - # Luckily, gcc supports the same syntax we need for Sun Studio. - # Supported since Solaris 2.6 (maybe 2.5.1?) - case $wlarc in - '') - whole_archive_flag_spec_F77='-z allextract$convenience -z defaultextract' ;; - *) - whole_archive_flag_spec_F77='${wl}-z ${wl}allextract`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}-z ${wl}defaultextract' ;; - esac ;; - esac - link_all_deplibs_F77=yes - ;; + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; - sunos4*) - if test "x$host_vendor" = xsequent; then - # Use $CC to link under sequent, because it throws in some extra .o - # files that make .init and .fini sections work. - archive_cmds_F77='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds_F77='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' - fi - hardcode_libdir_flag_spec_F77='-L$libdir' - hardcode_direct_F77=yes - hardcode_minus_L_F77=yes - hardcode_shlibpath_var_F77=no - ;; + freebsd[12]*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + ld_shlibs_CXX=no + ;; - sysv4) - case $host_vendor in - sni) - archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct_F77=yes # is this really true??? - ;; - siemens) - ## LD is ld it makes a PLAMLIB - ## CC just makes a GrossModule. - archive_cmds_F77='$LD -G -o $lib $libobjs $deplibs $linker_flags' - reload_cmds_F77='$CC -r -o $output$reload_objs' - hardcode_direct_F77=no + freebsd-elf*) + archive_cmds_need_lc_CXX=no ;; - motorola) - archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct_F77=no #Motorola manual says yes, but my tests say they lie - ;; - esac - runpath_var='LD_RUN_PATH' - hardcode_shlibpath_var_F77=no - ;; - sysv4.3*) - archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_shlibpath_var_F77=no - export_dynamic_flag_spec_F77='-Bexport' - ;; + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + ld_shlibs_CXX=yes + ;; - sysv4*MP*) - if test -d /usr/nec; then - archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_shlibpath_var_F77=no - runpath_var=LD_RUN_PATH - hardcode_runpath_var=yes - ld_shlibs_F77=yes - fi - ;; + gnu*) + ;; - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7*) - no_undefined_flag_F77='${wl}-z,text' - archive_cmds_need_lc_F77=no - hardcode_shlibpath_var_F77=no - runpath_var='LD_RUN_PATH' + hpux9*) + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_CXX=: + export_dynamic_flag_spec_CXX='${wl}-E' + hardcode_direct_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC*) + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + ;; + *) + if test "$GXX" = yes; then + archive_cmds_CXX='$RM $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; - if test "$GCC" = yes; then - archive_cmds_F77='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_F77='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds_F77='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_F77='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + hardcode_libdir_flag_spec_CXX='${wl}+b ${wl}$libdir' + hardcode_libdir_separator_CXX=: - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - no_undefined_flag_F77='${wl}-z,text' - allow_undefined_flag_F77='${wl}-z,nodefs' - archive_cmds_need_lc_F77=no - hardcode_shlibpath_var_F77=no - hardcode_libdir_flag_spec_F77='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' - hardcode_libdir_separator_F77=':' - link_all_deplibs_F77=yes - export_dynamic_flag_spec_F77='${wl}-Bexport' - runpath_var='LD_RUN_PATH' + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + export_dynamic_flag_spec_CXX='${wl}-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + ;; + *) + hardcode_direct_CXX=yes + hardcode_direct_absolute_CXX=yes + hardcode_minus_L_CXX=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac - if test "$GCC" = yes; then - archive_cmds_F77='$CC -shared ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_F77='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds_F77='$CC -G ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_F77='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + ;; + *) + if test "$GXX" = yes; then + if test $with_gnu_ld = no; then + case $host_cpu in + hppa*64*) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; - uts4*) - archive_cmds_F77='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec_F77='-L$libdir' - hardcode_shlibpath_var_F77=no - ;; + interix[3-9]*) + hardcode_direct_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + archive_cmds_CXX='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + archive_expsym_cmds_CXX='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + archive_cmds_CXX='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + archive_cmds_CXX='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` -o $lib' + fi + fi + link_all_deplibs_CXX=yes + ;; + esac + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + inherit_rpath_CXX=yes + ;; - *) - ld_shlibs_F77=no - ;; - esac - fi + linux* | k*bsd*-gnu) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + archive_expsym_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + archive_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + archive_cmds_need_lc_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + whole_archive_flag_spec_CXX='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [1-5]* | *pgcpp\ [1-5]*) + prelink_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"' + old_archive_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~ + $RANLIB $oldlib' + archive_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + archive_expsym_cmds_CXX='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + *) # Version 6 will use weak symbols + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + esac -{ echo "$as_me:$LINENO: result: $ld_shlibs_F77" >&5 -echo "${ECHO_T}$ld_shlibs_F77" >&6; } -test "$ld_shlibs_F77" = no && can_build_shared=no + hardcode_libdir_flag_spec_CXX='${wl}--rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + whole_archive_flag_spec_CXX='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + ;; + cxx*) + # Compaq C++ + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + archive_expsym_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' -# -# Do we need to explicitly link libc? -# -case "x$archive_cmds_need_lc_F77" in -x|xyes) - # Assume -lc should be added - archive_cmds_need_lc_F77=yes + runpath_var=LD_RUN_PATH + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + hardcode_libdir_separator_CXX=: - if test "$enable_shared" = yes && test "$GCC" = yes; then - case $archive_cmds_F77 in - *'~'*) - # FIXME: we may have to deal with multi-command sequences. - ;; - '$CC '*) - # Test whether the compiler implicitly links with -lc since on some - # systems, -lgcc has to come before -lc. If gcc already passes -lc - # to ld, don't add -lc before -lgcc. - { echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5 -echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6; } - $rm conftest* - printf "$lt_simple_compile_test_code" > conftest.$ac_ext + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + ;; + xl*) + # IBM XL 8.0 on PPC, with GNU ld + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + export_dynamic_flag_spec_CXX='${wl}--export-dynamic' + archive_cmds_CXX='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + archive_expsym_cmds_CXX='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' + hardcode_libdir_flag_spec_CXX='-R$libdir' + whole_archive_flag_spec_CXX='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + compiler_needs_object_CXX=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='echo' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 - (eval $ac_compile) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } 2>conftest.err; then - soname=conftest - lib=conftest - libobjs=conftest.$ac_objext - deplibs= - wl=$lt_prog_compiler_wl_F77 - pic_flag=$lt_prog_compiler_pic_F77 - compiler_flags=-v - linker_flags=-v - verstring= - output_objdir=. - libname=conftest - lt_save_allow_undefined_flag=$allow_undefined_flag_F77 - allow_undefined_flag_F77= - if { (eval echo "$as_me:$LINENO: \"$archive_cmds_F77 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5 - (eval $archive_cmds_F77 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } - then - archive_cmds_need_lc_F77=no - else - archive_cmds_need_lc_F77=yes - fi - allow_undefined_flag_F77=$lt_save_allow_undefined_flag - else - cat conftest.err 1>&5 - fi - $rm conftest* - { echo "$as_me:$LINENO: result: $archive_cmds_need_lc_F77" >&5 -echo "${ECHO_T}$archive_cmds_need_lc_F77" >&6; } - ;; - esac - fi - ;; -esac + lynxos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; -{ echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5 -echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6; } -library_names_spec= -libname_spec='lib$name' -soname_spec= -shrext_cmds=".so" -postinstall_cmds= -postuninstall_cmds= -finish_cmds= -finish_eval= -shlibpath_var= -shlibpath_overrides_runpath=unknown -version_type=none -dynamic_linker="$host_os ld.so" -sys_lib_dlsearch_path_spec="/lib /usr/lib" -if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then - # if the path contains ";" then we assume it to be the separator - # otherwise default to the standard path separator (i.e. ":") - it is - # assumed that no part of a normal pathname contains ";" but that should - # okay in the real world where ";" in dirpaths is itself problematic. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi -else - sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" -fi -need_lib_prefix=unknown -hardcode_into_libs=no + m88k*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; -# when you set need_version to no, make sure it does not cause -set_version -# flags to be left without arguments -need_version=unknown + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; -case $host_os in -aix3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' - shlibpath_var=LIBPATH + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + archive_cmds_CXX='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; - # AIX 3 has no versioning support, so we append a major version to the name. - soname_spec='${libname}${release}${shared_ext}$major' - ;; + *nto* | *qnx*) + ld_shlibs_CXX=yes + ;; -aix4* | aix5*) - version_type=linux - need_lib_prefix=no - need_version=no - hardcode_into_libs=yes - if test "$host_cpu" = ia64; then - # AIX 5 supports IA64 - library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - else - # With GCC up to 2.95.x, collect2 would create an import file - # for dependence libraries. The import file would start with - # the line `#! .'. This would cause the generated library to - # depend on `.', always an invalid library. This was fixed in - # development snapshots of GCC prior to 3.0. - case $host_os in - aix4 | aix4.[01] | aix4.[01].*) - if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' - echo ' yes ' - echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then - : - else - can_build_shared=no - fi - ;; - esac - # AIX (on Power*) has no versioning support, so currently we can not hardcode correct - # soname into executable. Probably we can add versioning support to - # collect2, so additional links can be useful in future. - if test "$aix_use_runtimelinking" = yes; then - # If using run time linking (on AIX 4.2 or later) use lib.so - # instead of lib.a to let people know that these are not - # typical AIX shared libraries. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - else - # We preserve .a as extension for shared libraries through AIX4.2 - # and later when we are not doing run time linking. - library_names_spec='${libname}${release}.a $libname.a' - soname_spec='${libname}${release}${shared_ext}$major' - fi - shlibpath_var=LIBPATH - fi - ;; + openbsd2*) + # C++ shared libraries are fairly broken + ld_shlibs_CXX=no + ;; -amigaos*) - library_names_spec='$libname.ixlibrary $libname.a' - # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' - ;; + openbsd*) + if test -f /usr/libexec/ld.so; then + hardcode_direct_CXX=yes + hardcode_shlibpath_var_CXX=no + hardcode_direct_absolute_CXX=yes + archive_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + archive_expsym_cmds_CXX='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' + export_dynamic_flag_spec_CXX='${wl}-E' + whole_archive_flag_spec_CXX="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + fi + output_verbose_link_cmd=echo + else + ld_shlibs_CXX=no + fi + ;; -beos*) - library_names_spec='${libname}${shared_ext}' - dynamic_linker="$host_os ld.so" - shlibpath_var=LIBRARY_PATH - ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + archive_cmds_CXX='tempext=`echo $shared_ext | $SED -e '\''s/\([^()0-9A-Za-z{}]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + hardcode_libdir_flag_spec_CXX='${wl}-rpath,$libdir' + hardcode_libdir_separator_CXX=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) old_archive_cmds_CXX='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) old_archive_cmds_CXX='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + cxx*) + case $host in + osf3*) + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && $ECHO "X${wl}-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + ;; + *) + allow_undefined_flag_CXX=' -expect_unresolved \*' + archive_cmds_CXX='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + archive_expsym_cmds_CXX='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~ + $RM $lib.exp' + hardcode_libdir_flag_spec_CXX='-rpath $libdir' + ;; + esac -bsdi[45]*) - version_type=linux - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" - sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" - # the default ld.so.conf also contains /usr/contrib/lib and - # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow - # libtool to hard-code these into programs - ;; + hardcode_libdir_separator_CXX=: -cygwin* | mingw* | pw32*) - version_type=windows - shrext_cmds=".dll" - need_version=no - need_lib_prefix=no + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + allow_undefined_flag_CXX=' ${wl}-expect_unresolved ${wl}\*' + case $host in + osf3*) + archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + *) + archive_cmds_CXX='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + esac + + hardcode_libdir_flag_spec_CXX='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator_CXX=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + fi + ;; + esac + ;; - case $GCC,$host_os in - yes,cygwin* | yes,mingw* | yes,pw32*) - library_names_spec='$libname.dll.a' - # DLL is installed to $(libdir)/../bin by postinstall_cmds - postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ - dldir=$destdir/`dirname \$dlpath`~ - test -d \$dldir || mkdir -p \$dldir~ - $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname' - postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ - dlpath=$dir/\$dldll~ - $rm \$dlpath' - shlibpath_overrides_runpath=yes + psos*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; - case $host_os in - cygwin*) - # Cygwin DLLs use 'cyg' prefix rather than 'lib' - soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" - ;; - mingw*) - # MinGW DLLs use traditional 'lib' prefix - soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';[c-zC-Z]:/' >/dev/null; then - # It is most probably a Windows format PATH printed by - # mingw gcc, but we are running on Cygwin. Gcc prints its search - # path with ; separators, and with drive letters. We can handle the - # drive letters (cygwin fileutils understands them), so leave them, - # especially as we might pass files found there to a mingw objdump, - # which wouldn't understand a cygwinified path. Ahh. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC*) + # Sun C++ 4.2, 5.x and Centerline C++ + archive_cmds_need_lc_CXX=yes + no_undefined_flag_CXX=' -zdefs' + archive_cmds_CXX='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + hardcode_libdir_flag_spec_CXX='-R$libdir' + hardcode_shlibpath_var_CXX=no + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + whole_archive_flag_spec_CXX='-z allextract$convenience -z defaultextract' + ;; + esac + link_all_deplibs_CXX=yes + + output_verbose_link_cmd='echo' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + old_archive_cmds_CXX='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + archive_cmds_CXX='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + old_archive_cmds_CXX='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + no_undefined_flag_CXX=' ${wl}-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + archive_cmds_CXX='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' + else + # g++ 2.7 appears to require `-G' NOT `-shared' on this + # platform. + archive_cmds_CXX='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + archive_expsym_cmds_CXX='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' + fi + + hardcode_libdir_flag_spec_CXX='${wl}-R $wl$libdir' + case $host_os in + solaris2.[0-5] | solaris2.[0-5].*) ;; + *) + whole_archive_flag_spec_CXX='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*) + no_undefined_flag_CXX='${wl}-z,text' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac ;; - pw32*) - # pw32 DLLs use 'pw' prefix rather than 'lib' - library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + no_undefined_flag_CXX='${wl}-z,text' + allow_undefined_flag_CXX='${wl}-z,nodefs' + archive_cmds_need_lc_CXX=no + hardcode_shlibpath_var_CXX=no + hardcode_libdir_flag_spec_CXX='${wl}-R,$libdir' + hardcode_libdir_separator_CXX=':' + link_all_deplibs_CXX=yes + export_dynamic_flag_spec_CXX='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + archive_cmds_CXX='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + archive_cmds_CXX='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + archive_expsym_cmds_CXX='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; + + *) + # FIXME: insert proper C++ library support + ld_shlibs_CXX=no + ;; esac - ;; - *) - library_names_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext} $libname.lib' - ;; - esac - dynamic_linker='Win32 ld.exe' - # FIXME: first we should search . and the directory the executable is in - shlibpath_var=PATH - ;; + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } + test "$ld_shlibs_CXX" = no && can_build_shared=no + + GCC_CXX="$GXX" + LD_CXX="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + # Dependencies to place before and after the object being linked: +predep_objects_CXX= +postdep_objects_CXX= +predeps_CXX= +postdeps_CXX= +compiler_lib_search_path_CXX= -darwin* | rhapsody*) - dynamic_linker="$host_os dyld" - version_type=darwin - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext' - soname_spec='${libname}${release}${major}$shared_ext' - shlibpath_overrides_runpath=yes - shlibpath_var=DYLD_LIBRARY_PATH - shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' - # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. - if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` - else - sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib' - fi - sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' - ;; +cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF -dgux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; +if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 + (eval $ac_compile) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. -freebsd1*) - dynamic_linker=no - ;; + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no -kfreebsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; + for p in `eval "$output_verbose_link_cmd"`; do + case $p in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test $p = "-L" || + test $p = "-R"; then + prev=$p + continue + else + prev= + fi + + if test "$pre_test_object_deps_done" = no; then + case $p in + -L* | -R*) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$compiler_lib_search_path_CXX"; then + compiler_lib_search_path_CXX="${prev}${p}" + else + compiler_lib_search_path_CXX="${compiler_lib_search_path_CXX} ${prev}${p}" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$postdeps_CXX"; then + postdeps_CXX="${prev}${p}" + else + postdeps_CXX="${postdeps_CXX} ${prev}${p}" + fi + fi + ;; + + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test "$pre_test_object_deps_done" = no; then + if test -z "$predep_objects_CXX"; then + predep_objects_CXX="$p" + else + predep_objects_CXX="$predep_objects_CXX $p" + fi + else + if test -z "$postdep_objects_CXX"; then + postdep_objects_CXX="$p" + else + postdep_objects_CXX="$postdep_objects_CXX $p" + fi + fi + ;; + + *) ;; # Ignore the rest. -freebsd* | dragonfly*) - # DragonFly does not have aout. When/if they implement a new - # versioning mechanism, adjust this. - if test -x /usr/bin/objformat; then - objformat=`/usr/bin/objformat` - else - case $host_os in - freebsd[123]*) objformat=aout ;; - *) objformat=elf ;; esac - fi - version_type=freebsd-$objformat - case $version_type in - freebsd-elf*) - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - need_version=no - need_lib_prefix=no - ;; - freebsd-*) - library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' - need_version=yes + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling CXX test program" +fi + +$RM -f confest.$objext + +# PORTME: override above test on systems where it is broken +case $host_os in +interix[3-9]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + predep_objects_CXX= + postdep_objects_CXX= + postdeps_CXX= + ;; + +linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes ;; - esac - shlibpath_var=LD_LIBRARY_PATH - case $host_os in - freebsd2*) - shlibpath_overrides_runpath=yes - ;; - freebsd3.[01]* | freebsdelf3.[01]*) - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; - freebsd3.[2-9]* | freebsdelf3.[2-9]* | \ - freebsd4.[0-5] | freebsdelf4.[0-5] | freebsd4.1.1 | freebsdelf4.1.1) - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; - freebsd*) # from 4.6 on - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes + esac + + if test "$solaris_use_stlport4" != yes; then + postdeps_CXX='-library=Cstd -library=Crun' + fi ;; esac ;; -gnu*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - ;; +solaris*) + case $cc_basename in + CC*) + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac -hpux9* | hpux10* | hpux11*) - # Give a soname corresponding to the major version so that dld.sl refuses to - # link against other versions. - version_type=sunos - need_lib_prefix=no - need_version=no - case $host_cpu in - ia64*) - shrext_cmds='.so' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.so" - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - if test "X$HPUX_IA64_MODE" = X32; then - sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" - else - sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + # Adding this requires a known-good setup of shared libraries for + # Sun compiler versions before 5.6, else PIC objects from an old + # archive will be linked into the output, leading to subtle bugs. + if test "$solaris_use_stlport4" != yes; then + postdeps_CXX='-library=Cstd -library=Crun' fi - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - hppa*64*) - shrext_cmds='.sl' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.sl" - shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - *) - shrext_cmds='.sl' - dynamic_linker="$host_os dld.sl" - shlibpath_var=SHLIB_PATH - shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' ;; esac - # HP-UX runs *really* slowly unless shared libraries are mode 555. - postinstall_cmds='chmod 555 $lib' ;; +esac -interix3*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - ;; -irix5* | irix6* | nonstopux*) - case $host_os in - nonstopux*) version_type=nonstopux ;; - *) - if test "$lt_cv_prog_gnu_ld" = yes; then - version_type=linux - else - version_type=irix - fi ;; - esac - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' - case $host_os in - irix5* | nonstopux*) - libsuff= shlibsuff= - ;; - *) - case $LD in # libtool.m4 will add one of these switches to LD - *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") - libsuff= shlibsuff= libmagic=32-bit;; - *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") - libsuff=32 shlibsuff=N32 libmagic=N32;; - *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") - libsuff=64 shlibsuff=64 libmagic=64-bit;; - *) libsuff= shlibsuff= libmagic=never-match;; - esac - ;; - esac - shlibpath_var=LD_LIBRARY${shlibsuff}_PATH - shlibpath_overrides_runpath=no - sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" - sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" - hardcode_into_libs=yes - ;; +case " $postdeps_CXX " in +*" -lc "*) archive_cmds_need_lc_CXX=no ;; +esac + compiler_lib_search_dirs_CXX= +if test -n "${compiler_lib_search_path_CXX}"; then + compiler_lib_search_dirs_CXX=`echo " ${compiler_lib_search_path_CXX}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` +fi -# No shared lib support for Linux oldld, aout, or coff. -linux*oldld* | linux*aout* | linux*coff*) - dynamic_linker=no - ;; -# This must be Linux ELF. -linux*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - # This implies no fast_install, which is unacceptable. - # Some rework will be needed to allow for fast_install - # before this can be enabled. - hardcode_into_libs=yes - # Append ld.so.conf contents to the search path - if test -f /etc/ld.so.conf; then - lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` - sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" - fi - # We used to test for /lib/ld.so.1 and disable shared libraries on - # powerpc, because MkLinux only supported shared libraries with the - # GNU dynamic linker. Since this was broken with cross compilers, - # most powerpc-linux boxes support dynamic linking these days and - # people can always --disable-shared, the test was removed, and we - # assume the GNU/Linux dynamic linker is in use. - dynamic_linker='GNU/Linux ld.so' - ;; -knetbsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; -netbsd*) - version_type=sunos - need_lib_prefix=no - need_version=no - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - dynamic_linker='NetBSD (a.out) ld.so' - else - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - dynamic_linker='NetBSD ld.elf_so' - fi - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - ;; -newsos6) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; -nto-qnx*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - ;; -openbsd*) - version_type=sunos - sys_lib_dlsearch_path_spec="/usr/lib" - need_lib_prefix=no - # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. - case $host_os in - openbsd3.3 | openbsd3.3.*) need_version=yes ;; - *) need_version=no ;; - esac - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' - shlibpath_var=LD_LIBRARY_PATH - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - case $host_os in - openbsd2.[89] | openbsd2.[89].*) - shlibpath_overrides_runpath=no - ;; - *) - shlibpath_overrides_runpath=yes - ;; - esac - else - shlibpath_overrides_runpath=yes - fi - ;; -os2*) - libname_spec='$name' - shrext_cmds=".dll" - need_lib_prefix=no - library_names_spec='$libname${shared_ext} $libname.a' - dynamic_linker='OS/2 ld.exe' - shlibpath_var=LIBPATH - ;; -osf3* | osf4* | osf5*) - version_type=osf - need_lib_prefix=no - need_version=no - soname_spec='${libname}${release}${shared_ext}$major' - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - shlibpath_var=LD_LIBRARY_PATH - sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" - sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" - ;; -solaris*) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - hardcode_into_libs=yes - # ldd complains unless libraries are executable - postinstall_cmds='chmod +x $lib' - ;; -sunos4*) - version_type=sunos - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' - finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes - if test "$with_gnu_ld" = yes; then - need_lib_prefix=no - fi - need_version=yes - ;; -sysv4 | sysv4.3*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - case $host_vendor in - sni) - shlibpath_overrides_runpath=no - need_lib_prefix=no - export_dynamic_flag_spec='${wl}-Blargedynsym' - runpath_var=LD_RUN_PATH - ;; - siemens) - need_lib_prefix=no - ;; - motorola) - need_lib_prefix=no - need_version=no - shlibpath_overrides_runpath=no - sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' - ;; - esac - ;; -sysv4*MP*) - if test -d /usr/nec ;then - version_type=linux - library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' - soname_spec='$libname${shared_ext}.$major' - shlibpath_var=LD_LIBRARY_PATH - fi - ;; -sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) - version_type=freebsd-elf - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - hardcode_into_libs=yes - if test "$with_gnu_ld" = yes; then - sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' - shlibpath_overrides_runpath=no - else - sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' - shlibpath_overrides_runpath=yes - case $host_os in - sco3.2v5*) - sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" - ;; - esac - fi - sys_lib_dlsearch_path_spec='/usr/lib' - ;; - -uts4*) - version_type=linux - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - ;; - -*) - dynamic_linker=no - ;; -esac -{ echo "$as_me:$LINENO: result: $dynamic_linker" >&5 -echo "${ECHO_T}$dynamic_linker" >&6; } -test "$dynamic_linker" = no && can_build_shared=no - -variables_saved_for_relink="PATH $shlibpath_var $runpath_var" -if test "$GCC" = yes; then - variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" -fi - -{ echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5 -echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6; } -hardcode_action_F77= -if test -n "$hardcode_libdir_flag_spec_F77" || \ - test -n "$runpath_var_F77" || \ - test "X$hardcode_automatic_F77" = "Xyes" ; then - - # We can hardcode non-existant directories. - if test "$hardcode_direct_F77" != no && - # If the only mechanism to avoid hardcoding is shlibpath_var, we - # have to relink, otherwise we might link with an installed library - # when we should be linking with a yet-to-be-installed one - ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, F77)" != no && - test "$hardcode_minus_L_F77" != no; then - # Linking always hardcodes the temporary library directory. - hardcode_action_F77=relink - else - # We can link without hardcoding, and we can hardcode nonexisting dirs. - hardcode_action_F77=immediate - fi -else - # We cannot hardcode anything, or else we can only hardcode existing - # directories. - hardcode_action_F77=unsupported -fi -{ echo "$as_me:$LINENO: result: $hardcode_action_F77" >&5 -echo "${ECHO_T}$hardcode_action_F77" >&6; } - -if test "$hardcode_action_F77" = relink; then - # Fast installation is not supported - enable_fast_install=no -elif test "$shlibpath_overrides_runpath" = yes || - test "$enable_shared" = no; then - # Fast installation is not necessary - enable_fast_install=needless -fi - - -# The else clause should only fire when bootstrapping the -# libtool distribution, otherwise you forgot to ship ltmain.sh -# with your package, and you will get complaints that there are -# no rules to generate ltmain.sh. -if test -f "$ltmain"; then - # See if we are running on zsh, and set the options which allow our commands through - # without removal of \ escapes. - if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST - fi - # Now quote all the things that may contain metacharacters while being - # careful not to overquote the AC_SUBSTed values. We take copies of the - # variables and quote the copies for generation of the libtool script. - for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC LTCFLAGS NM \ - SED SHELL STRIP \ - libname_spec library_names_spec soname_spec extract_expsyms_cmds \ - old_striplib striplib file_magic_cmd finish_cmds finish_eval \ - deplibs_check_method reload_flag reload_cmds need_locks \ - lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \ - lt_cv_sys_global_symbol_to_c_name_address \ - sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ - old_postinstall_cmds old_postuninstall_cmds \ - compiler_F77 \ - CC_F77 \ - LD_F77 \ - lt_prog_compiler_wl_F77 \ - lt_prog_compiler_pic_F77 \ - lt_prog_compiler_static_F77 \ - lt_prog_compiler_no_builtin_flag_F77 \ - export_dynamic_flag_spec_F77 \ - thread_safe_flag_spec_F77 \ - whole_archive_flag_spec_F77 \ - enable_shared_with_static_runtimes_F77 \ - old_archive_cmds_F77 \ - old_archive_from_new_cmds_F77 \ - predep_objects_F77 \ - postdep_objects_F77 \ - predeps_F77 \ - postdeps_F77 \ - compiler_lib_search_path_F77 \ - archive_cmds_F77 \ - archive_expsym_cmds_F77 \ - postinstall_cmds_F77 \ - postuninstall_cmds_F77 \ - old_archive_from_expsyms_cmds_F77 \ - allow_undefined_flag_F77 \ - no_undefined_flag_F77 \ - export_symbols_cmds_F77 \ - hardcode_libdir_flag_spec_F77 \ - hardcode_libdir_flag_spec_ld_F77 \ - hardcode_libdir_separator_F77 \ - hardcode_automatic_F77 \ - module_cmds_F77 \ - module_expsym_cmds_F77 \ - lt_cv_prog_compiler_c_o_F77 \ - exclude_expsyms_F77 \ - include_expsyms_F77; do - - case $var in - old_archive_cmds_F77 | \ - old_archive_from_new_cmds_F77 | \ - archive_cmds_F77 | \ - archive_expsym_cmds_F77 | \ - module_cmds_F77 | \ - module_expsym_cmds_F77 | \ - old_archive_from_expsyms_cmds_F77 | \ - export_symbols_cmds_F77 | \ - extract_expsyms_cmds | reload_cmds | finish_cmds | \ - postinstall_cmds | postuninstall_cmds | \ - old_postinstall_cmds | old_postuninstall_cmds | \ - sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) - # Double-quote double-evaled strings. - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" - ;; - *) - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" - ;; - esac - done - - case $lt_echo in - *'\$0 --fallback-echo"') - lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'` - ;; - esac - -cfgfile="$ofile" - - cat <<__EOF__ >> "$cfgfile" -# ### BEGIN LIBTOOL TAG CONFIG: $tagname - -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: - -# Shell to use when invoking shell scripts. -SHELL=$lt_SHELL - -# Whether or not to build shared libraries. -build_libtool_libs=$enable_shared - -# Whether or not to build static libraries. -build_old_libs=$enable_static - -# Whether or not to add -lc for building shared libraries. -build_libtool_need_lc=$archive_cmds_need_lc_F77 - -# Whether or not to disallow shared libs when runtime libs are static -allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_F77 - -# Whether or not to optimize for fast installation. -fast_install=$enable_fast_install - -# The host system. -host_alias=$host_alias -host=$host -host_os=$host_os - -# The build system. -build_alias=$build_alias -build=$build -build_os=$build_os - -# An echo program that does not interpret backslashes. -echo=$lt_echo - -# The archiver. -AR=$lt_AR -AR_FLAGS=$lt_AR_FLAGS - -# A C compiler. -LTCC=$lt_LTCC - -# LTCC compiler flags. -LTCFLAGS=$lt_LTCFLAGS - -# A language-specific compiler. -CC=$lt_compiler_F77 - -# Is the compiler the GNU C compiler? -with_gcc=$GCC_F77 - -# An ERE matcher. -EGREP=$lt_EGREP - -# The linker used to build libraries. -LD=$lt_LD_F77 - -# Whether we need hard or soft links. -LN_S=$lt_LN_S - -# A BSD-compatible nm program. -NM=$lt_NM - -# A symbol stripping program -STRIP=$lt_STRIP - -# Used to examine libraries when file_magic_cmd begins "file" -MAGIC_CMD=$MAGIC_CMD - -# Used on cygwin: DLL creation program. -DLLTOOL="$DLLTOOL" - -# Used on cygwin: object dumper. -OBJDUMP="$OBJDUMP" - -# Used on cygwin: assembler. -AS="$AS" - -# The name of the directory that contains temporary libtool files. -objdir=$objdir - -# How to create reloadable object files. -reload_flag=$lt_reload_flag -reload_cmds=$lt_reload_cmds - -# How to pass a linker flag through the compiler. -wl=$lt_lt_prog_compiler_wl_F77 - -# Object file suffix (normally "o"). -objext="$ac_objext" - -# Old archive suffix (normally "a"). -libext="$libext" - -# Shared library suffix (normally ".so"). -shrext_cmds='$shrext_cmds' - -# Executable file suffix (normally ""). -exeext="$exeext" - -# Additional compiler flags for building library objects. -pic_flag=$lt_lt_prog_compiler_pic_F77 -pic_mode=$pic_mode - -# What is the maximum length of a command? -max_cmd_len=$lt_cv_sys_max_cmd_len - -# Does compiler simultaneously support -c and -o options? -compiler_c_o=$lt_lt_cv_prog_compiler_c_o_F77 - -# Must we lock files when doing compilation? -need_locks=$lt_need_locks - -# Do we need the lib prefix for modules? -need_lib_prefix=$need_lib_prefix - -# Do we need a version for libraries? -need_version=$need_version - -# Whether dlopen is supported. -dlopen_support=$enable_dlopen - -# Whether dlopen of programs is supported. -dlopen_self=$enable_dlopen_self - -# Whether dlopen of statically linked programs is supported. -dlopen_self_static=$enable_dlopen_self_static - -# Compiler flag to prevent dynamic linking. -link_static_flag=$lt_lt_prog_compiler_static_F77 - -# Compiler flag to turn off builtin functions. -no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_F77 - -# Compiler flag to allow reflexive dlopens. -export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_F77 - -# Compiler flag to generate shared objects directly from archives. -whole_archive_flag_spec=$lt_whole_archive_flag_spec_F77 - -# Compiler flag to generate thread-safe objects. -thread_safe_flag_spec=$lt_thread_safe_flag_spec_F77 - -# Library versioning type. -version_type=$version_type - -# Format of library name prefix. -libname_spec=$lt_libname_spec - -# List of archive names. First name is the real one, the rest are links. -# The last name is the one that the linker finds with -lNAME. -library_names_spec=$lt_library_names_spec - -# The coded name of the library, if different from the real name. -soname_spec=$lt_soname_spec - -# Commands used to build and install an old-style archive. -RANLIB=$lt_RANLIB -old_archive_cmds=$lt_old_archive_cmds_F77 -old_postinstall_cmds=$lt_old_postinstall_cmds -old_postuninstall_cmds=$lt_old_postuninstall_cmds - -# Create an old-style archive from a shared archive. -old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_F77 - -# Create a temporary old-style archive to link instead of a shared archive. -old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_F77 - -# Commands used to build and install a shared archive. -archive_cmds=$lt_archive_cmds_F77 -archive_expsym_cmds=$lt_archive_expsym_cmds_F77 -postinstall_cmds=$lt_postinstall_cmds -postuninstall_cmds=$lt_postuninstall_cmds - -# Commands used to build a loadable module (assumed same as above if empty) -module_cmds=$lt_module_cmds_F77 -module_expsym_cmds=$lt_module_expsym_cmds_F77 - -# Commands to strip libraries. -old_striplib=$lt_old_striplib -striplib=$lt_striplib - -# Dependencies to place before the objects being linked to create a -# shared library. -predep_objects=$lt_predep_objects_F77 - -# Dependencies to place after the objects being linked to create a -# shared library. -postdep_objects=$lt_postdep_objects_F77 - -# Dependencies to place before the objects being linked to create a -# shared library. -predeps=$lt_predeps_F77 - -# Dependencies to place after the objects being linked to create a -# shared library. -postdeps=$lt_postdeps_F77 - -# The library search path used internally by the compiler when linking -# a shared library. -compiler_lib_search_path=$lt_compiler_lib_search_path_F77 - -# Method to check whether dependent libraries are shared objects. -deplibs_check_method=$lt_deplibs_check_method - -# Command to use when deplibs_check_method == file_magic. -file_magic_cmd=$lt_file_magic_cmd -# Flag that allows shared libraries with undefined symbols to be built. -allow_undefined_flag=$lt_allow_undefined_flag_F77 - -# Flag that forces no undefined symbols. -no_undefined_flag=$lt_no_undefined_flag_F77 - -# Commands used to finish a libtool library installation in a directory. -finish_cmds=$lt_finish_cmds -# Same as above, but a single script fragment to be evaled but not shown. -finish_eval=$lt_finish_eval -# Take the output of nm and produce a listing of raw symbols and C names. -global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe -# Transform the output of nm in a proper C declaration -global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl -# Transform the output of nm in a C name address pair -global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address -# This is the shared library runtime path variable. -runpath_var=$runpath_var -# This is the shared library path variable. -shlibpath_var=$shlibpath_var -# Is shlibpath searched before the hard-coded library search path? -shlibpath_overrides_runpath=$shlibpath_overrides_runpath -# How to hardcode a shared library path into an executable. -hardcode_action=$hardcode_action_F77 -# Whether we should hardcode library paths into libraries. -hardcode_into_libs=$hardcode_into_libs -# Flag to hardcode \$libdir into a binary during linking. -# This must work even if \$libdir does not exist. -hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_F77 -# If ld is used when linking, flag to hardcode \$libdir into -# a binary during linking. This must work even if \$libdir does -# not exist. -hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_F77 - -# Whether we need a single -rpath flag with a separated argument. -hardcode_libdir_separator=$lt_hardcode_libdir_separator_F77 - -# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the -# resulting binary. -hardcode_direct=$hardcode_direct_F77 - -# Set to yes if using the -LDIR flag during linking hardcodes DIR into the -# resulting binary. -hardcode_minus_L=$hardcode_minus_L_F77 - -# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into -# the resulting binary. -hardcode_shlibpath_var=$hardcode_shlibpath_var_F77 - -# Set to yes if building a shared library automatically hardcodes DIR into the library -# and all subsequent libraries and executables linked against it. -hardcode_automatic=$hardcode_automatic_F77 -# Variables whose values should be saved in libtool wrapper scripts and -# restored at relink time. -variables_saved_for_relink="$variables_saved_for_relink" -# Whether libtool must link a program against all its dependency libraries. -link_all_deplibs=$link_all_deplibs_F77 -# Compile-time system search path for libraries -sys_lib_search_path_spec=$lt_sys_lib_search_path_spec + lt_prog_compiler_wl_CXX= +lt_prog_compiler_pic_CXX= +lt_prog_compiler_static_CXX= -# Run-time system search path for libraries -sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $compiler option to produce PIC" >&5 +$as_echo_n "checking for $compiler option to produce PIC... " >&6; } -# Fix the shell variable \$srcfile for the compiler. -fix_srcfile_path="$fix_srcfile_path_F77" + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-static' -# Set to yes if exported symbols are required. -always_export_symbols=$always_export_symbols_F77 + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + fi + ;; -# The commands to list exported symbols. -export_symbols_cmds=$lt_export_symbols_cmds_F77 + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + lt_prog_compiler_pic_CXX='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + lt_prog_compiler_pic_CXX='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; -# The commands to extract the exported symbol list from a shared archive. -extract_expsyms_cmds=$lt_extract_expsyms_cmds - -# Symbols that should not be listed in the preloaded symbols. -exclude_expsyms=$lt_exclude_expsyms_F77 - -# Symbols that must always be exported. -include_expsyms=$lt_include_expsyms_F77 - -# ### END LIBTOOL TAG CONFIG: $tagname - -__EOF__ - - -else - # If there is no Makefile yet, we rely on a make rule to execute - # `config.status --recheck' to rerun these tests and create the - # libtool script then. - ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` - if test -f "$ltmain_in"; then - test -f Makefile && make "$ltmain" - fi -fi - - -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu - -CC="$lt_save_CC" - - else - tagname="" - fi - ;; - - GCJ) - if test -n "$GCJ" && test "X$GCJ" != "Xno"; then - - -# Source file extension for Java test sources. -ac_ext=java - -# Object file extension for compiled Java test sources. -objext=o -objext_GCJ=$objext - -# Code to be used in simple compile tests -lt_simple_compile_test_code="class foo {}\n" - -# Code to be used in simple link tests -lt_simple_link_test_code='public class conftest { public static void main(String[] argv) {}; }\n' - -# ltmain only uses $CC for tagged configurations so make sure $CC is set. - -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} - -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} - -# Allow CC to be a program name with arguments. -compiler=$CC - - -# save warnings/boilerplate of simple test code -ac_outfile=conftest.$ac_objext -printf "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$rm conftest* - -ac_outfile=conftest.$ac_objext -printf "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$rm conftest* - - -# Allow CC to be a program name with arguments. -lt_save_CC="$CC" -CC=${GCJ-"gcj"} -compiler=$CC -compiler_GCJ=$CC -for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` - - -# GCJ did not exist at the time GCC didn't implicitly link libc in. -archive_cmds_need_lc_GCJ=no - -old_archive_cmds_GCJ=$old_archive_cmds - - -lt_prog_compiler_no_builtin_flag_GCJ= - -if test "$GCC" = yes; then - lt_prog_compiler_no_builtin_flag_GCJ=' -fno-builtin' - - -{ echo "$as_me:$LINENO: checking if $compiler supports -fno-rtti -fno-exceptions" >&5 -echo $ECHO_N "checking if $compiler supports -fno-rtti -fno-exceptions... $ECHO_C" >&6; } -if test "${lt_cv_prog_compiler_rtti_exceptions+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_cv_prog_compiler_rtti_exceptions=no - ac_outfile=conftest.$ac_objext - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="-fno-rtti -fno-exceptions" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:16215: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:16219: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_cv_prog_compiler_rtti_exceptions=yes - fi - fi - $rm conftest* - -fi -{ echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_rtti_exceptions" >&5 -echo "${ECHO_T}$lt_cv_prog_compiler_rtti_exceptions" >&6; } - -if test x"$lt_cv_prog_compiler_rtti_exceptions" = xyes; then - lt_prog_compiler_no_builtin_flag_GCJ="$lt_prog_compiler_no_builtin_flag_GCJ -fno-rtti -fno-exceptions" -else - : -fi - -fi - -lt_prog_compiler_wl_GCJ= -lt_prog_compiler_pic_GCJ= -lt_prog_compiler_static_GCJ= - -{ echo "$as_me:$LINENO: checking for $compiler option to produce PIC" >&5 -echo $ECHO_N "checking for $compiler option to produce PIC... $ECHO_C" >&6; } - - if test "$GCC" = yes; then - lt_prog_compiler_wl_GCJ='-Wl,' - lt_prog_compiler_static_GCJ='-static' - - case $host_os in - aix*) - # All AIX code is PIC. - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static_GCJ='-Bstatic' - fi - ;; - - amigaos*) - # FIXME: we need at least 68020 code to build shared libraries, but - # adding the `-m68020' flag to GCC prevents building anything better, - # like `-m68040'. - lt_prog_compiler_pic_GCJ='-m68020 -resident32 -malways-restore-a4' - ;; - - beos* | cygwin* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) # PIC is the default for these OSes. ;; - - mingw* | pw32* | os2*) + mingw* | cygwin* | os2* | pw32* | cegcc*) # This hack is so that the source file can tell whether it is being # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic_GCJ='-DDLL_EXPORT' + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + lt_prog_compiler_pic_CXX='-DDLL_EXPORT' ;; - darwin* | rhapsody*) # PIC is the default on this platform # Common symbols not allowed in MH_DYLIB files - lt_prog_compiler_pic_GCJ='-fno-common' + lt_prog_compiler_pic_CXX='-fno-common' ;; - - interix3*) + *djgpp*) + # DJGPP does not support shared libraries at all + lt_prog_compiler_pic_CXX= + ;; + interix[3-9]*) # Interix 3.x gcc -fpic/-fPIC options generate broken code. # Instead, we relocate shared libraries at runtime. ;; - - msdosdjgpp*) - # Just because we use GCC doesn't mean we suddenly get shared libraries - # on systems that don't support them. - lt_prog_compiler_can_build_shared_GCJ=no - enable_shared=no - ;; - sysv4*MP*) if test -d /usr/nec; then - lt_prog_compiler_pic_GCJ=-Kconform_pic + lt_prog_compiler_pic_CXX=-Kconform_pic fi ;; - hpux*) - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. case $host_cpu in - hppa*64*|ia64*) - # +Z the default + hppa*64*) ;; *) - lt_prog_compiler_pic_GCJ='-fPIC' + lt_prog_compiler_pic_CXX='-fPIC' ;; esac ;; - + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; *) - lt_prog_compiler_pic_GCJ='-fPIC' + lt_prog_compiler_pic_CXX='-fPIC' ;; esac else - # PORTME Check for flag to pass linker flags through the system compiler. case $host_os in - aix*) - lt_prog_compiler_wl_GCJ='-Wl,' - if test "$host_cpu" = ia64; then - # AIX 5 now supports IA64 processor - lt_prog_compiler_static_GCJ='-Bstatic' - else - lt_prog_compiler_static_GCJ='-bnso -bI:/lib/syscalls.exp' - fi - ;; - darwin*) - # PIC is the default on this platform - # Common symbols not allowed in MH_DYLIB files - case $cc_basename in - xlc*) - lt_prog_compiler_pic_GCJ='-qnocommon' - lt_prog_compiler_wl_GCJ='-Wl,' - ;; - esac - ;; - - mingw* | pw32* | os2*) - # This hack is so that the source file can tell whether it is being - # built for inclusion in a dll (and should export symbols for example). - lt_prog_compiler_pic_GCJ='-DDLL_EXPORT' - ;; - - hpux9* | hpux10* | hpux11*) - lt_prog_compiler_wl_GCJ='-Wl,' - # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but - # not for PA HP-UX. - case $host_cpu in - hppa*64*|ia64*) - # +Z the default + aix[4-9]*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + lt_prog_compiler_static_CXX='-Bstatic' + else + lt_prog_compiler_static_CXX='-bnso -bI:/lib/syscalls.exp' + fi ;; - *) - lt_prog_compiler_pic_GCJ='+Z' + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, CXX)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac ;; - esac - # Is there a better lt_prog_compiler_static that works with the bundled CC? - lt_prog_compiler_static_GCJ='${wl}-a ${wl}archive' - ;; - - irix5* | irix6* | nonstopux*) - lt_prog_compiler_wl_GCJ='-Wl,' - # PIC (with -KPIC) is the default. - lt_prog_compiler_static_GCJ='-non_shared' - ;; - - newsos6) - lt_prog_compiler_pic_GCJ='-KPIC' - lt_prog_compiler_static_GCJ='-Bstatic' - ;; - - linux*) - case $cc_basename in - icc* | ecc*) - lt_prog_compiler_wl_GCJ='-Wl,' - lt_prog_compiler_pic_GCJ='-KPIC' - lt_prog_compiler_static_GCJ='-static' - ;; - pgcc* | pgf77* | pgf90* | pgf95*) - # Portland Group compilers (*not* the Pentium gcc compiler, - # which looks to be a dead project) - lt_prog_compiler_wl_GCJ='-Wl,' - lt_prog_compiler_pic_GCJ='-fpic' - lt_prog_compiler_static_GCJ='-Bstatic' - ;; - ccc*) - lt_prog_compiler_wl_GCJ='-Wl,' - # All Alpha code is PIC. - lt_prog_compiler_static_GCJ='-non_shared' - ;; - esac - ;; - - osf3* | osf4* | osf5*) - lt_prog_compiler_wl_GCJ='-Wl,' - # All OSF/1 code is PIC. - lt_prog_compiler_static_GCJ='-non_shared' - ;; - - solaris*) - lt_prog_compiler_pic_GCJ='-KPIC' - lt_prog_compiler_static_GCJ='-Bstatic' - case $cc_basename in - f77* | f90* | f95*) - lt_prog_compiler_wl_GCJ='-Qoption ld ';; - *) - lt_prog_compiler_wl_GCJ='-Wl,';; - esac - ;; - - sunos4*) - lt_prog_compiler_wl_GCJ='-Qoption ld ' - lt_prog_compiler_pic_GCJ='-PIC' - lt_prog_compiler_static_GCJ='-Bstatic' - ;; - - sysv4 | sysv4.2uw2* | sysv4.3*) - lt_prog_compiler_wl_GCJ='-Wl,' - lt_prog_compiler_pic_GCJ='-KPIC' - lt_prog_compiler_static_GCJ='-Bstatic' - ;; - - sysv4*MP*) - if test -d /usr/nec ;then - lt_prog_compiler_pic_GCJ='-Kconform_pic' - lt_prog_compiler_static_GCJ='-Bstatic' - fi - ;; - - sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) - lt_prog_compiler_wl_GCJ='-Wl,' - lt_prog_compiler_pic_GCJ='-KPIC' - lt_prog_compiler_static_GCJ='-Bstatic' - ;; - - unicos*) - lt_prog_compiler_wl_GCJ='-Wl,' - lt_prog_compiler_can_build_shared_GCJ=no - ;; - - uts4*) - lt_prog_compiler_pic_GCJ='-pic' - lt_prog_compiler_static_GCJ='-Bstatic' - ;; - - *) - lt_prog_compiler_can_build_shared_GCJ=no - ;; - esac - fi - -{ echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_GCJ" >&5 -echo "${ECHO_T}$lt_prog_compiler_pic_GCJ" >&6; } - -# -# Check to make sure the PIC flag actually works. -# -if test -n "$lt_prog_compiler_pic_GCJ"; then - -{ echo "$as_me:$LINENO: checking if $compiler PIC flag $lt_prog_compiler_pic_GCJ works" >&5 -echo $ECHO_N "checking if $compiler PIC flag $lt_prog_compiler_pic_GCJ works... $ECHO_C" >&6; } -if test "${lt_prog_compiler_pic_works_GCJ+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_prog_compiler_pic_works_GCJ=no - ac_outfile=conftest.$ac_objext - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - lt_compiler_flag="$lt_prog_compiler_pic_GCJ" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - # The option is referenced via a variable to avoid confusing sed. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:16483: $lt_compile\"" >&5) - (eval "$lt_compile" 2>conftest.err) - ac_status=$? - cat conftest.err >&5 - echo "$as_me:16487: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s "$ac_outfile"; then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings other than the usual output. - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then - lt_prog_compiler_pic_works_GCJ=yes - fi - fi - $rm conftest* - -fi -{ echo "$as_me:$LINENO: result: $lt_prog_compiler_pic_works_GCJ" >&5 -echo "${ECHO_T}$lt_prog_compiler_pic_works_GCJ" >&6; } - -if test x"$lt_prog_compiler_pic_works_GCJ" = xyes; then - case $lt_prog_compiler_pic_GCJ in - "" | " "*) ;; - *) lt_prog_compiler_pic_GCJ=" $lt_prog_compiler_pic_GCJ" ;; - esac -else - lt_prog_compiler_pic_GCJ= - lt_prog_compiler_can_build_shared_GCJ=no -fi - -fi -case $host_os in - # For platforms which do not support PIC, -DPIC is meaningless: - *djgpp*) - lt_prog_compiler_pic_GCJ= - ;; - *) - lt_prog_compiler_pic_GCJ="$lt_prog_compiler_pic_GCJ" - ;; -esac - -# -# Check to make sure the static flag actually works. -# -wl=$lt_prog_compiler_wl_GCJ eval lt_tmp_static_flag=\"$lt_prog_compiler_static_GCJ\" -{ echo "$as_me:$LINENO: checking if $compiler static flag $lt_tmp_static_flag works" >&5 -echo $ECHO_N "checking if $compiler static flag $lt_tmp_static_flag works... $ECHO_C" >&6; } -if test "${lt_prog_compiler_static_works_GCJ+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_prog_compiler_static_works_GCJ=no - save_LDFLAGS="$LDFLAGS" - LDFLAGS="$LDFLAGS $lt_tmp_static_flag" - printf "$lt_simple_link_test_code" > conftest.$ac_ext - if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then - # The linker can only warn and ignore the option if not recognized - # So say no if there are warnings - if test -s conftest.err; then - # Append any errors to the config.log. - cat conftest.err 1>&5 - $echo "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp - $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 - if diff conftest.exp conftest.er2 >/dev/null; then - lt_prog_compiler_static_works_GCJ=yes - fi - else - lt_prog_compiler_static_works_GCJ=yes - fi - fi - $rm conftest* - LDFLAGS="$save_LDFLAGS" - -fi -{ echo "$as_me:$LINENO: result: $lt_prog_compiler_static_works_GCJ" >&5 -echo "${ECHO_T}$lt_prog_compiler_static_works_GCJ" >&6; } - -if test x"$lt_prog_compiler_static_works_GCJ" = xyes; then - : -else - lt_prog_compiler_static_GCJ= -fi - - -{ echo "$as_me:$LINENO: checking if $compiler supports -c -o file.$ac_objext" >&5 -echo $ECHO_N "checking if $compiler supports -c -o file.$ac_objext... $ECHO_C" >&6; } -if test "${lt_cv_prog_compiler_c_o_GCJ+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - lt_cv_prog_compiler_c_o_GCJ=no - $rm -r conftest 2>/dev/null - mkdir conftest - cd conftest - mkdir out - printf "$lt_simple_compile_test_code" > conftest.$ac_ext - - lt_compiler_flag="-o out/conftest2.$ac_objext" - # Insert the option either (1) after the last *FLAGS variable, or - # (2) before a word containing "conftest.", or (3) at the end. - # Note that $ac_compile itself does not contain backslashes and begins - # with a dollar sign (not a hyphen), so the echo should work correctly. - lt_compile=`echo "$ac_compile" | $SED \ - -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ - -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ - -e 's:$: $lt_compiler_flag:'` - (eval echo "\"\$as_me:16587: $lt_compile\"" >&5) - (eval "$lt_compile" 2>out/conftest.err) - ac_status=$? - cat out/conftest.err >&5 - echo "$as_me:16591: \$? = $ac_status" >&5 - if (exit $ac_status) && test -s out/conftest2.$ac_objext - then - # The compiler can only warn and ignore the option if not recognized - # So say no if there are warnings - $echo "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp - $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 - if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then - lt_cv_prog_compiler_c_o_GCJ=yes - fi - fi - chmod u+w . 2>&5 - $rm conftest* - # SGI C++ compiler will create directory out/ii_files/ for - # template instantiation - test -d out/ii_files && $rm out/ii_files/* && rmdir out/ii_files - $rm out/* && rmdir out - cd .. - rmdir conftest - $rm conftest* - -fi -{ echo "$as_me:$LINENO: result: $lt_cv_prog_compiler_c_o_GCJ" >&5 -echo "${ECHO_T}$lt_cv_prog_compiler_c_o_GCJ" >&6; } - - -hard_links="nottested" -if test "$lt_cv_prog_compiler_c_o_GCJ" = no && test "$need_locks" != no; then - # do not overwrite the value of need_locks provided by the user - { echo "$as_me:$LINENO: checking if we can lock with hard links" >&5 -echo $ECHO_N "checking if we can lock with hard links... $ECHO_C" >&6; } - hard_links=yes - $rm conftest* - ln conftest.a conftest.b 2>/dev/null && hard_links=no - touch conftest.a - ln conftest.a conftest.b 2>&5 || hard_links=no - ln conftest.a conftest.b 2>/dev/null && hard_links=no - { echo "$as_me:$LINENO: result: $hard_links" >&5 -echo "${ECHO_T}$hard_links" >&6; } - if test "$hard_links" = no; then - { echo "$as_me:$LINENO: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 -echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} - need_locks=warn - fi -else - need_locks=no -fi - -{ echo "$as_me:$LINENO: checking whether the $compiler linker ($LD) supports shared libraries" >&5 -echo $ECHO_N "checking whether the $compiler linker ($LD) supports shared libraries... $ECHO_C" >&6; } - - runpath_var= - allow_undefined_flag_GCJ= - enable_shared_with_static_runtimes_GCJ=no - archive_cmds_GCJ= - archive_expsym_cmds_GCJ= - old_archive_From_new_cmds_GCJ= - old_archive_from_expsyms_cmds_GCJ= - export_dynamic_flag_spec_GCJ= - whole_archive_flag_spec_GCJ= - thread_safe_flag_spec_GCJ= - hardcode_libdir_flag_spec_GCJ= - hardcode_libdir_flag_spec_ld_GCJ= - hardcode_libdir_separator_GCJ= - hardcode_direct_GCJ=no - hardcode_minus_L_GCJ=no - hardcode_shlibpath_var_GCJ=unsupported - link_all_deplibs_GCJ=unknown - hardcode_automatic_GCJ=no - module_cmds_GCJ= - module_expsym_cmds_GCJ= - always_export_symbols_GCJ=no - export_symbols_cmds_GCJ='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' - # include_expsyms should be a list of space-separated symbols to be *always* - # included in the symbol list - include_expsyms_GCJ= - # exclude_expsyms can be an extended regexp of symbols to exclude - # it will be wrapped by ` (' and `)$', so one must not match beginning or - # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', - # as well as any symbol that contains `d'. - exclude_expsyms_GCJ="_GLOBAL_OFFSET_TABLE_" - # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out - # platforms (ab)use it in PIC code, but their linkers get confused if - # the symbol is explicitly referenced. Since portable code cannot - # rely on this symbol name, it's probably fine to never include it in - # preloaded symbol tables. - extract_expsyms_cmds= - # Just being paranoid about ensuring that cc_basename is set. - for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` - - case $host_os in - cygwin* | mingw* | pw32*) - # FIXME: the MSVC++ port hasn't been tested in a loooong time - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - if test "$GCC" != yes; then - with_gnu_ld=no - fi - ;; - interix*) - # we just hope/assume this is gcc and not c89 (= MSVC++) - with_gnu_ld=yes - ;; - openbsd*) - with_gnu_ld=no - ;; - esac - - ld_shlibs_GCJ=yes - if test "$with_gnu_ld" = yes; then - # If archive_cmds runs LD, not CC, wlarc should be empty - wlarc='${wl}' - - # Set some defaults for GNU ld with shared library support. These - # are reset later if shared libraries are not supported. Putting them - # here allows them to be overridden if necessary. - runpath_var=LD_RUN_PATH - hardcode_libdir_flag_spec_GCJ='${wl}--rpath ${wl}$libdir' - export_dynamic_flag_spec_GCJ='${wl}--export-dynamic' - # ancient GNU ld didn't support --whole-archive et. al. - if $LD --help 2>&1 | grep 'no-whole-archive' > /dev/null; then - whole_archive_flag_spec_GCJ="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' - else - whole_archive_flag_spec_GCJ= - fi - supports_anon_versioning=no - case `$LD -v 2>/dev/null` in - *\ [01].* | *\ 2.[0-9].* | *\ 2.10.*) ;; # catch versions < 2.11 - *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... - *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... - *\ 2.11.*) ;; # other 2.11 versions - *) supports_anon_versioning=yes ;; - esac - - # See if GNU ld supports shared libraries. - case $host_os in - aix3* | aix4* | aix5*) - # On AIX/PPC, the GNU linker is very broken - if test "$host_cpu" != ia64; then - ld_shlibs_GCJ=no - cat <&2 - -*** Warning: the GNU linker, at least up to release 2.9.1, is reported -*** to be unable to reliably create shared libraries on AIX. -*** Therefore, libtool is disabling shared libraries support. If you -*** really care for shared libraries, you may want to modify your PATH -*** so that a non-GNU linker is found, and then restart. - -EOF - fi - ;; - - amigaos*) - archive_cmds_GCJ='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - hardcode_libdir_flag_spec_GCJ='-L$libdir' - hardcode_minus_L_GCJ=yes - - # Samuel A. Falvo II reports - # that the semantics of dynamic libraries on AmigaOS, at least up - # to version 4, is to share data among multiple programs linked - # with the same dynamic library. Since this doesn't match the - # behavior of shared libraries on other platforms, we can't use - # them. - ld_shlibs_GCJ=no - ;; - - beos*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - allow_undefined_flag_GCJ=unsupported - # Joseph Beckenbach says some releases of gcc - # support --undefined. This deserves some investigation. FIXME - archive_cmds_GCJ='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - else - ld_shlibs_GCJ=no - fi - ;; - - cygwin* | mingw* | pw32*) - # _LT_AC_TAGVAR(hardcode_libdir_flag_spec, GCJ) is actually meaningless, - # as there is no search path for DLLs. - hardcode_libdir_flag_spec_GCJ='-L$libdir' - allow_undefined_flag_GCJ=unsupported - always_export_symbols_GCJ=no - enable_shared_with_static_runtimes_GCJ=yes - export_symbols_cmds_GCJ='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS] /s/.* \([^ ]*\)/\1 DATA/'\'' | $SED -e '\''/^[AITW] /s/.* //'\'' | sort | uniq > $export_symbols' - - if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then - archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - # If the export-symbols file already is a .def file (1st line - # is EXPORTS), use it as is; otherwise, prepend... - archive_expsym_cmds_GCJ='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then - cp $export_symbols $output_objdir/$soname.def; - else - echo EXPORTS > $output_objdir/$soname.def; - cat $export_symbols >> $output_objdir/$soname.def; - fi~ - $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' - else - ld_shlibs_GCJ=no - fi - ;; - - interix3*) - hardcode_direct_GCJ=no - hardcode_shlibpath_var_GCJ=no - hardcode_libdir_flag_spec_GCJ='${wl}-rpath,$libdir' - export_dynamic_flag_spec_GCJ='${wl}-E' - # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. - # Instead, shared libraries are loaded at an image base (0x10000000 by - # default) and relocated if they conflict, which is a slow very memory - # consuming and fragmenting process. To avoid this, we pick a random, - # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link - # time. Moving up from 0x10000000 also allows more sbrk(2) space. - archive_cmds_GCJ='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - archive_expsym_cmds_GCJ='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' - ;; - - linux*) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - tmp_addflag= - case $cc_basename,$host_cpu in - pgcc*) # Portland Group C compiler - whole_archive_flag_spec_GCJ='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag' - ;; - pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers - whole_archive_flag_spec_GCJ='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}--no-whole-archive' - tmp_addflag=' $pic_flag -Mnomain' ;; - ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 - tmp_addflag=' -i_dynamic' ;; - efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 - tmp_addflag=' -i_dynamic -nofor_main' ;; - ifc* | ifort*) # Intel Fortran compiler - tmp_addflag=' -nofor_main' ;; + dgux*) + case $cc_basename in + ec++*) + lt_prog_compiler_pic_CXX='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' + if test "$host_cpu" != ia64; then + lt_prog_compiler_pic_CXX='+Z' + fi + ;; + aCC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='${wl}-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + lt_prog_compiler_pic_CXX='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_static_CXX='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu) + case $cc_basename in + KCC*) + # KAI C++ Compiler + lt_prog_compiler_wl_CXX='--backend -Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64 which still supported -KPIC. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fPIC' + lt_prog_compiler_static_CXX='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-fpic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + xlc* | xlC*) + # IBM XL 8.0 on PPC + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-qpic' + lt_prog_compiler_static_CXX='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + esac + ;; esac - archive_cmds_GCJ='$CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - - if test $supports_anon_versioning = yes; then - archive_expsym_cmds_GCJ='$echo "{ global:" > $output_objdir/$libname.ver~ - cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ - $echo "local: *; };" >> $output_objdir/$libname.ver~ - $CC -shared'"$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' - fi - else - ld_shlibs_GCJ=no - fi - ;; - - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - archive_cmds_GCJ='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' - wlarc= - else - archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - fi - ;; - - solaris*) - if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then - ld_shlibs_GCJ=no - cat <&2 - -*** Warning: The releases 2.8.* of the GNU linker cannot reliably -*** create shared libraries on Solaris systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.9.1 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -EOF - elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs_GCJ=no - fi - ;; - - sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) - case `$LD -v 2>&1` in - *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) - ld_shlibs_GCJ=no - cat <<_LT_EOF 1>&2 - -*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not -*** reliably create shared libraries on SCO systems. Therefore, libtool -*** is disabling shared libraries support. We urge you to upgrade GNU -*** binutils to release 2.16.91.0.3 or newer. Another option is to modify -*** your PATH or compiler configuration so that the native linker is -*** used, and then restart. - -_LT_EOF ;; - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - hardcode_libdir_flag_spec_GCJ='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' - archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib' - archive_expsym_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname,\${SCOABSPATH:+${install_libdir}/}$soname,-retain-symbols-file,$export_symbols -o $lib' - else - ld_shlibs_GCJ=no - fi + lynxos*) ;; - esac - ;; - - sunos4*) - archive_cmds_GCJ='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' - wlarc= - hardcode_direct_GCJ=yes - hardcode_shlibpath_var_GCJ=no - ;; - - *) - if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then - archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' - archive_expsym_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' - else - ld_shlibs_GCJ=no - fi - ;; - esac - - if test "$ld_shlibs_GCJ" = no; then - runpath_var= - hardcode_libdir_flag_spec_GCJ= - export_dynamic_flag_spec_GCJ= - whole_archive_flag_spec_GCJ= - fi - else - # PORTME fill in a description of your system's linker (not GNU ld) - case $host_os in - aix3*) - allow_undefined_flag_GCJ=unsupported - always_export_symbols_GCJ=yes - archive_expsym_cmds_GCJ='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' - # Note: this linker hardcodes the directories in LIBPATH if there - # are no directories specified by -L. - hardcode_minus_L_GCJ=yes - if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then - # Neither direct hardcoding nor static linking is supported with a - # broken collect2. - hardcode_direct_GCJ=unsupported - fi - ;; - - aix4* | aix5*) - if test "$host_cpu" = ia64; then - # On IA64, the linker does run time linking by default, so we don't - # have to do anything special. - aix_use_runtimelinking=no - exp_sym_flag='-Bexport' - no_entry_flag="" - else - # If we're using GNU nm, then we don't want the "-C" option. - # -C means demangle to AIX nm, but means don't demangle with GNU nm - if $NM -V 2>&1 | grep 'GNU' > /dev/null; then - export_symbols_cmds_GCJ='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' - else - export_symbols_cmds_GCJ='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$2 == "T") || (\$2 == "D") || (\$2 == "B")) && (substr(\$3,1,1) != ".")) { print \$3 } }'\'' | sort -u > $export_symbols' - fi - aix_use_runtimelinking=no - - # Test if we are trying to use run time linking or normal - # AIX style linking. If -brtl is somewhere in LDFLAGS, we - # need to do runtime linking. - case $host_os in aix4.[23]|aix4.[23].*|aix5*) - for ld_flag in $LDFLAGS; do - if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then - aix_use_runtimelinking=yes - break - fi - done - ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + lt_prog_compiler_pic_CXX='-W c,exportall' + ;; + *) + ;; esac - - exp_sym_flag='-bexport' - no_entry_flag='-bnoentry' - fi - - # When large executables or shared objects are built, AIX ld can - # have problems creating the table of contents. If linking a library - # or program results in "error TOC overflow" add -mminimal-toc to - # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not - # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. - - archive_cmds_GCJ='' - hardcode_direct_GCJ=yes - hardcode_libdir_separator_GCJ=':' - link_all_deplibs_GCJ=yes - - if test "$GCC" = yes; then - case $host_os in aix4.[012]|aix4.[012].*) - # We only want to do this on AIX 4.2 and lower, the check - # below for broken collect2 doesn't work under 4.3+ - collect2name=`${CC} -print-prog-name=collect2` - if test -f "$collect2name" && \ - strings "$collect2name" | grep resolve_lib_name >/dev/null - then - # We have reworked collect2 - hardcode_direct_GCJ=yes - else - # We have old collect2 - hardcode_direct_GCJ=unsupported - # It fails to find uninstalled libraries when the uninstalled - # path is not listed in the libpath. Setting hardcode_minus_L - # to unsupported forces relinking - hardcode_minus_L_GCJ=yes - hardcode_libdir_flag_spec_GCJ='-L$libdir' - hardcode_libdir_separator_GCJ= - fi - ;; + ;; + netbsd*) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + lt_prog_compiler_pic_CXX='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + lt_prog_compiler_wl_CXX='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + lt_prog_compiler_pic_CXX='-pic' + ;; + cxx*) + # Digital/Compaq C++ + lt_prog_compiler_wl_CXX='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + lt_prog_compiler_pic_CXX= + lt_prog_compiler_static_CXX='-non_shared' + ;; + *) + ;; esac - shared_flag='-shared' - if test "$aix_use_runtimelinking" = yes; then - shared_flag="$shared_flag "'${wl}-G' - fi - else - # not using gcc - if test "$host_cpu" = ia64; then - # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release - # chokes on -Wl,-G. The following line is correct: - shared_flag='-G' - else - if test "$aix_use_runtimelinking" = yes; then - shared_flag='${wl}-G' - else - shared_flag='${wl}-bM:SRE' - fi - fi - fi - - # It seems that -bexpall does not export symbols beginning with - # underscore (_), so it is better to generate a list of symbols to export. - always_export_symbols_GCJ=yes - if test "$aix_use_runtimelinking" = yes; then - # Warning - without using the other runtime loading flags (-brtl), - # -berok will link without error, but may produce a broken library. - allow_undefined_flag_GCJ='-berok' - # Determine the default libpath from the value encoded in an empty executable. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ - - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - - -fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - - hardcode_libdir_flag_spec_GCJ='${wl}-blibpath:$libdir:'"$aix_libpath" - archive_expsym_cmds_GCJ="\$CC"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then echo "${wl}${allow_undefined_flag}"; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" - else - if test "$host_cpu" = ia64; then - hardcode_libdir_flag_spec_GCJ='${wl}-R $libdir:/usr/lib:/lib' - allow_undefined_flag_GCJ="-z nodefs" - archive_expsym_cmds_GCJ="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" - else - # Determine the default libpath from the value encoded in an empty executable. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ - -int -main () -{ + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC*) + # Sun C++ 4.2, 5.x and Centerline C++ + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + lt_prog_compiler_wl_CXX='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + lt_prog_compiler_pic_CXX='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + lt_prog_compiler_pic_CXX='-pic' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + lcc*) + # Lucid + lt_prog_compiler_pic_CXX='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + lt_prog_compiler_wl_CXX='-Wl,' + lt_prog_compiler_pic_CXX='-KPIC' + lt_prog_compiler_static_CXX='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + lt_prog_compiler_pic_CXX='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + lt_prog_compiler_can_build_shared_CXX=no + ;; + esac + fi - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + lt_prog_compiler_pic_CXX= + ;; + *) + lt_prog_compiler_pic_CXX="$lt_prog_compiler_pic_CXX -DPIC" + ;; esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest$ac_exeext && - $as_test_x conftest$ac_exeext; then - -aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'` -# Check for a 64-bit object if we didn't find anything. -if test -z "$aix_libpath"; then aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } -}'`; fi -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 - +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_prog_compiler_pic_CXX" >&5 +$as_echo "$lt_prog_compiler_pic_CXX" >&6; } -fi - -rm -f core conftest.err conftest.$ac_objext conftest_ipa8_conftest.oo \ - conftest$ac_exeext conftest.$ac_ext -if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi - - hardcode_libdir_flag_spec_GCJ='${wl}-blibpath:$libdir:'"$aix_libpath" - # Warning - without using the other run time loading flags, - # -berok will link without error, but may produce a broken library. - no_undefined_flag_GCJ=' ${wl}-bernotok' - allow_undefined_flag_GCJ=' ${wl}-berok' - # Exported symbols can be pulled into shared objects from archives - whole_archive_flag_spec_GCJ='$convenience' - archive_cmds_need_lc_GCJ=yes - # This is similar to how AIX traditionally builds its shared libraries. - archive_expsym_cmds_GCJ="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' - fi - fi - ;; - - amigaos*) - archive_cmds_GCJ='$rm $output_objdir/a2ixlibrary.data~$echo "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$echo "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$echo "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$echo "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' - hardcode_libdir_flag_spec_GCJ='-L$libdir' - hardcode_minus_L_GCJ=yes - # see comment about different semantics on the GNU ld section - ld_shlibs_GCJ=no - ;; - bsdi[45]*) - export_dynamic_flag_spec_GCJ=-rdynamic - ;; - cygwin* | mingw* | pw32*) - # When not using gcc, we currently assume that we are using - # Microsoft Visual C++. - # hardcode_libdir_flag_spec is actually meaningless, as there is - # no search path for DLLs. - hardcode_libdir_flag_spec_GCJ=' ' - allow_undefined_flag_GCJ=unsupported - # Tell ltmain to make .lib files, not .a files. - libext=lib - # Tell ltmain to make .dll files, not .so files. - shrext_cmds=".dll" - # FIXME: Setting linknames here is a bad hack. - archive_cmds_GCJ='$CC -o $lib $libobjs $compiler_flags `echo "$deplibs" | $SED -e '\''s/ -lc$//'\''` -link -dll~linknames=' - # The linker will automatically build a .lib file if we build a DLL. - old_archive_From_new_cmds_GCJ='true' - # FIXME: Should let the user specify the lib program. - old_archive_cmds_GCJ='lib /OUT:$oldlib$oldobjs$old_deplibs' - fix_srcfile_path_GCJ='`cygpath -w "$srcfile"`' - enable_shared_with_static_runtimes_GCJ=yes - ;; +# +# Check to make sure the PIC flag actually works. +# +if test -n "$lt_prog_compiler_pic_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works" >&5 +$as_echo_n "checking if $compiler PIC flag $lt_prog_compiler_pic_CXX works... " >&6; } +if test "${lt_cv_prog_compiler_pic_works_CXX+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_pic_works_CXX=no + ac_outfile=conftest.$ac_objext + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$lt_prog_compiler_pic_CXX -DPIC" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:13172: $lt_compile\"" >&5) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&5 + echo "$as_me:13176: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_pic_works_CXX=yes + fi + fi + $RM conftest* - darwin* | rhapsody*) - case $host_os in - rhapsody* | darwin1.[012]) - allow_undefined_flag_GCJ='${wl}-undefined ${wl}suppress' - ;; - *) # Darwin 1.3 on - if test -z ${MACOSX_DEPLOYMENT_TARGET} ; then - allow_undefined_flag_GCJ='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - else - case ${MACOSX_DEPLOYMENT_TARGET} in - 10.[012]) - allow_undefined_flag_GCJ='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' - ;; - 10.*) - allow_undefined_flag_GCJ='${wl}-undefined ${wl}dynamic_lookup' - ;; - esac - fi - ;; - esac - archive_cmds_need_lc_GCJ=no - hardcode_direct_GCJ=no - hardcode_automatic_GCJ=yes - hardcode_shlibpath_var_GCJ=unsupported - whole_archive_flag_spec_GCJ='' - link_all_deplibs_GCJ=yes - if test "$GCC" = yes ; then - output_verbose_link_cmd='echo' - archive_cmds_GCJ='$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring' - module_cmds_GCJ='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - archive_expsym_cmds_GCJ='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -dynamiclib $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags -install_name $rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - module_expsym_cmds_GCJ='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - else - case $cc_basename in - xlc*) - output_verbose_link_cmd='echo' - archive_cmds_GCJ='$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}`echo $rpath/$soname` $verstring' - module_cmds_GCJ='$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags' - # Don't fix this by using the ld -exported_symbols_list flag, it doesn't exist in older darwin lds - archive_expsym_cmds_GCJ='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC -qmkshrobj $allow_undefined_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-install_name ${wl}$rpath/$soname $verstring~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - module_expsym_cmds_GCJ='sed -e "s,#.*,," -e "s,^[ ]*,," -e "s,^\(..*\),_&," < $export_symbols > $output_objdir/${libname}-symbols.expsym~$CC $allow_undefined_flag -o $lib -bundle $libobjs $deplibs$compiler_flags~nmedit -s $output_objdir/${libname}-symbols.expsym ${lib}' - ;; - *) - ld_shlibs_GCJ=no - ;; - esac - fi - ;; +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_pic_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_pic_works_CXX" >&6; } - dgux*) - archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec_GCJ='-L$libdir' - hardcode_shlibpath_var_GCJ=no - ;; +if test x"$lt_cv_prog_compiler_pic_works_CXX" = xyes; then + case $lt_prog_compiler_pic_CXX in + "" | " "*) ;; + *) lt_prog_compiler_pic_CXX=" $lt_prog_compiler_pic_CXX" ;; + esac +else + lt_prog_compiler_pic_CXX= + lt_prog_compiler_can_build_shared_CXX=no +fi - freebsd1*) - ld_shlibs_GCJ=no - ;; +fi - # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor - # support. Future versions do this automatically, but an explicit c++rt0.o - # does not break anything, and helps significantly (at the cost of a little - # extra space). - freebsd2.2*) - archive_cmds_GCJ='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' - hardcode_libdir_flag_spec_GCJ='-R$libdir' - hardcode_direct_GCJ=yes - hardcode_shlibpath_var_GCJ=no - ;; - # Unfortunately, older versions of FreeBSD 2 do not have this feature. - freebsd2*) - archive_cmds_GCJ='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct_GCJ=yes - hardcode_minus_L_GCJ=yes - hardcode_shlibpath_var_GCJ=no - ;; - # FreeBSD 3 and greater uses gcc -shared to do shared libraries. - freebsd* | kfreebsd*-gnu | dragonfly*) - archive_cmds_GCJ='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' - hardcode_libdir_flag_spec_GCJ='-R$libdir' - hardcode_direct_GCJ=yes - hardcode_shlibpath_var_GCJ=no - ;; +# +# Check to make sure the static flag actually works. +# +wl=$lt_prog_compiler_wl_CXX eval lt_tmp_static_flag=\"$lt_prog_compiler_static_CXX\" +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler static flag $lt_tmp_static_flag works" >&5 +$as_echo_n "checking if $compiler static flag $lt_tmp_static_flag works... " >&6; } +if test "${lt_cv_prog_compiler_static_works_CXX+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_static_works_CXX=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $lt_tmp_static_flag" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&5 + $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + lt_cv_prog_compiler_static_works_CXX=yes + fi + else + lt_cv_prog_compiler_static_works_CXX=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" - hpux9*) - if test "$GCC" = yes; then - archive_cmds_GCJ='$rm $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - else - archive_cmds_GCJ='$rm $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' - fi - hardcode_libdir_flag_spec_GCJ='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_GCJ=: - hardcode_direct_GCJ=yes +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_static_works_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_static_works_CXX" >&6; } - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L_GCJ=yes - export_dynamic_flag_spec_GCJ='${wl}-E' - ;; +if test x"$lt_cv_prog_compiler_static_works_CXX" = xyes; then + : +else + lt_prog_compiler_static_CXX= +fi - hpux10*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - archive_cmds_GCJ='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds_GCJ='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' - fi - if test "$with_gnu_ld" = no; then - hardcode_libdir_flag_spec_GCJ='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_GCJ=: - hardcode_direct_GCJ=yes - export_dynamic_flag_spec_GCJ='${wl}-E' - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L_GCJ=yes - fi - ;; - hpux11*) - if test "$GCC" = yes -a "$with_gnu_ld" = no; then - case $host_cpu in - hppa*64*) - archive_cmds_GCJ='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - archive_cmds_GCJ='$CC -shared ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds_GCJ='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - else - case $host_cpu in - hppa*64*) - archive_cmds_GCJ='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - ;; - ia64*) - archive_cmds_GCJ='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' - ;; - *) - archive_cmds_GCJ='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' - ;; - esac - fi - if test "$with_gnu_ld" = no; then - hardcode_libdir_flag_spec_GCJ='${wl}+b ${wl}$libdir' - hardcode_libdir_separator_GCJ=: + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if test "${lt_cv_prog_compiler_c_o_CXX+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext - case $host_cpu in - hppa*64*|ia64*) - hardcode_libdir_flag_spec_ld_GCJ='+b $libdir' - hardcode_direct_GCJ=no - hardcode_shlibpath_var_GCJ=no - ;; - *) - hardcode_direct_GCJ=yes - export_dynamic_flag_spec_GCJ='${wl}-E' + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:13271: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:13275: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* - # hardcode_minus_L: Not really in the search PATH, - # but as the default location of the library. - hardcode_minus_L_GCJ=yes - ;; - esac - fi - ;; +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } - irix5* | irix6* | nonstopux*) - if test "$GCC" = yes; then - archive_cmds_GCJ='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - archive_cmds_GCJ='$LD -shared $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - hardcode_libdir_flag_spec_ld_GCJ='-rpath $libdir' - fi - hardcode_libdir_flag_spec_GCJ='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_GCJ=: - link_all_deplibs_GCJ=yes - ;; - netbsd*) - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then - archive_cmds_GCJ='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out - else - archive_cmds_GCJ='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF - fi - hardcode_libdir_flag_spec_GCJ='-R$libdir' - hardcode_direct_GCJ=yes - hardcode_shlibpath_var_GCJ=no - ;; - newsos6) - archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct_GCJ=yes - hardcode_libdir_flag_spec_GCJ='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_GCJ=: - hardcode_shlibpath_var_GCJ=no - ;; + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if $compiler supports -c -o file.$ac_objext" >&5 +$as_echo_n "checking if $compiler supports -c -o file.$ac_objext... " >&6; } +if test "${lt_cv_prog_compiler_c_o_CXX+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + lt_cv_prog_compiler_c_o_CXX=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext - openbsd*) - hardcode_direct_GCJ=yes - hardcode_shlibpath_var_GCJ=no - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then - archive_cmds_GCJ='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_GCJ='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' - hardcode_libdir_flag_spec_GCJ='${wl}-rpath,$libdir' - export_dynamic_flag_spec_GCJ='${wl}-E' - else - case $host_os in - openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) - archive_cmds_GCJ='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec_GCJ='-R$libdir' - ;; - *) - archive_cmds_GCJ='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' - hardcode_libdir_flag_spec_GCJ='${wl}-rpath,$libdir' - ;; - esac - fi - ;; + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:13323: $lt_compile\"" >&5) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&5 + echo "$as_me:13327: \$? = $ac_status" >&5 + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + lt_cv_prog_compiler_c_o_CXX=yes + fi + fi + chmod u+w . 2>&5 + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* - os2*) - hardcode_libdir_flag_spec_GCJ='-L$libdir' - hardcode_minus_L_GCJ=yes - allow_undefined_flag_GCJ=unsupported - archive_cmds_GCJ='$echo "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$echo "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$echo DATA >> $output_objdir/$libname.def~$echo " SINGLE NONSHARED" >> $output_objdir/$libname.def~$echo EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' - old_archive_From_new_cmds_GCJ='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' - ;; +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $lt_cv_prog_compiler_c_o_CXX" >&5 +$as_echo "$lt_cv_prog_compiler_c_o_CXX" >&6; } - osf3*) - if test "$GCC" = yes; then - allow_undefined_flag_GCJ=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds_GCJ='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - else - allow_undefined_flag_GCJ=' -expect_unresolved \*' - archive_cmds_GCJ='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - fi - hardcode_libdir_flag_spec_GCJ='${wl}-rpath ${wl}$libdir' - hardcode_libdir_separator_GCJ=: - ;; - osf4* | osf5*) # as osf3* with the addition of -msym flag - if test "$GCC" = yes; then - allow_undefined_flag_GCJ=' ${wl}-expect_unresolved ${wl}\*' - archive_cmds_GCJ='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && echo ${wl}-set_version ${wl}$verstring` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' - hardcode_libdir_flag_spec_GCJ='${wl}-rpath ${wl}$libdir' - else - allow_undefined_flag_GCJ=' -expect_unresolved \*' - archive_cmds_GCJ='$LD -shared${allow_undefined_flag} $libobjs $deplibs $linker_flags -msym -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib' - archive_expsym_cmds_GCJ='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; echo "-hidden">> $lib.exp~ - $LD -shared${allow_undefined_flag} -input $lib.exp $linker_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && echo -set_version $verstring` -update_registry ${output_objdir}/so_locations -o $lib~$rm $lib.exp' - # Both c and cxx compiler support -rpath directly - hardcode_libdir_flag_spec_GCJ='-rpath $libdir' - fi - hardcode_libdir_separator_GCJ=: - ;; - solaris*) - no_undefined_flag_GCJ=' -z text' - if test "$GCC" = yes; then - wlarc='${wl}' - archive_cmds_GCJ='$CC -shared ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_GCJ='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $CC -shared ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$rm $lib.exp' - else - wlarc='' - archive_cmds_GCJ='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' - archive_expsym_cmds_GCJ='$echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~$echo "local: *; };" >> $lib.exp~ - $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$rm $lib.exp' - fi - hardcode_libdir_flag_spec_GCJ='-R$libdir' - hardcode_shlibpath_var_GCJ=no - case $host_os in - solaris2.[0-5] | solaris2.[0-5].*) ;; - *) - # The compiler driver will combine linker options so we - # cannot just pass the convience library names through - # without $wl, iff we do not link with $LD. - # Luckily, gcc supports the same syntax we need for Sun Studio. - # Supported since Solaris 2.6 (maybe 2.5.1?) - case $wlarc in - '') - whole_archive_flag_spec_GCJ='-z allextract$convenience -z defaultextract' ;; - *) - whole_archive_flag_spec_GCJ='${wl}-z ${wl}allextract`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $echo \"$new_convenience\"` ${wl}-z ${wl}defaultextract' ;; - esac ;; - esac - link_all_deplibs_GCJ=yes - ;; +hard_links="nottested" +if test "$lt_cv_prog_compiler_c_o_CXX" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if we can lock with hard links" >&5 +$as_echo_n "checking if we can lock with hard links... " >&6; } + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $hard_links" >&5 +$as_echo "$hard_links" >&6; } + if test "$hard_links" = no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&5 +$as_echo "$as_me: WARNING: \`$CC' does not support \`-c -o', so \`make -j' may be unsafe" >&2;} + need_locks=warn + fi +else + need_locks=no +fi - sunos4*) - if test "x$host_vendor" = xsequent; then - # Use $CC to link under sequent, because it throws in some extra .o - # files that make .init and .fini sections work. - archive_cmds_GCJ='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds_GCJ='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' - fi - hardcode_libdir_flag_spec_GCJ='-L$libdir' - hardcode_direct_GCJ=yes - hardcode_minus_L_GCJ=yes - hardcode_shlibpath_var_GCJ=no - ;; - sysv4) - case $host_vendor in - sni) - archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct_GCJ=yes # is this really true??? - ;; - siemens) - ## LD is ld it makes a PLAMLIB - ## CC just makes a GrossModule. - archive_cmds_GCJ='$LD -G -o $lib $libobjs $deplibs $linker_flags' - reload_cmds_GCJ='$CC -r -o $output$reload_objs' - hardcode_direct_GCJ=no - ;; - motorola) - archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_direct_GCJ=no #Motorola manual says yes, but my tests say they lie - ;; - esac - runpath_var='LD_RUN_PATH' - hardcode_shlibpath_var_GCJ=no - ;; - sysv4.3*) - archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_shlibpath_var_GCJ=no - export_dynamic_flag_spec_GCJ='-Bexport' - ;; + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the $compiler linker ($LD) supports shared libraries" >&5 +$as_echo_n "checking whether the $compiler linker ($LD) supports shared libraries... " >&6; } - sysv4*MP*) - if test -d /usr/nec; then - archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_shlibpath_var_GCJ=no - runpath_var=LD_RUN_PATH - hardcode_runpath_var=yes - ld_shlibs_GCJ=yes - fi - ;; + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + case $host_os in + aix[4-9]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + export_symbols_cmds_CXX='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + export_symbols_cmds_CXX='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && (substr(\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + export_symbols_cmds_CXX="$ltdll_cmds" + ;; + cygwin* | mingw* | cegcc*) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[BCDGRS][ ]/s/.*[ ]\([^ ]*\)/\1 DATA/;/^.*[ ]__nm__/s/^.*[ ]__nm__\([^ ]*\)[ ][^ ]*/\1 DATA/;/^I[ ]/d;/^[AITW][ ]/s/.* //'\'' | sort | uniq > $export_symbols' + ;; + *) + export_symbols_cmds_CXX='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac + exclude_expsyms_CXX='_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*' - sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7*) - no_undefined_flag_GCJ='${wl}-z,text' - archive_cmds_need_lc_GCJ=no - hardcode_shlibpath_var_GCJ=no - runpath_var='LD_RUN_PATH' +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ld_shlibs_CXX" >&5 +$as_echo "$ld_shlibs_CXX" >&6; } +test "$ld_shlibs_CXX" = no && can_build_shared=no - if test "$GCC" = yes; then - archive_cmds_GCJ='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_GCJ='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds_GCJ='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_GCJ='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; +with_gnu_ld_CXX=$with_gnu_ld - sysv5* | sco3.2v5* | sco5v6*) - # Note: We can NOT use -z defs as we might desire, because we do not - # link with -lc, and that would cause any symbols used from libc to - # always be unresolved, which means just about no library would - # ever link correctly. If we're not using GNU ld we use -z text - # though, which does catch some bad symbols but isn't as heavy-handed - # as -z defs. - no_undefined_flag_GCJ='${wl}-z,text' - allow_undefined_flag_GCJ='${wl}-z,nodefs' - archive_cmds_need_lc_GCJ=no - hardcode_shlibpath_var_GCJ=no - hardcode_libdir_flag_spec_GCJ='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' - hardcode_libdir_separator_GCJ=':' - link_all_deplibs_GCJ=yes - export_dynamic_flag_spec_GCJ='${wl}-Bexport' - runpath_var='LD_RUN_PATH' - if test "$GCC" = yes; then - archive_cmds_GCJ='$CC -shared ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_GCJ='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - else - archive_cmds_GCJ='$CC -G ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - archive_expsym_cmds_GCJ='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,\${SCOABSPATH:+${install_libdir}/}$soname -o $lib $libobjs $deplibs $compiler_flags' - fi - ;; - uts4*) - archive_cmds_GCJ='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' - hardcode_libdir_flag_spec_GCJ='-L$libdir' - hardcode_shlibpath_var_GCJ=no - ;; - *) - ld_shlibs_GCJ=no - ;; - esac - fi -{ echo "$as_me:$LINENO: result: $ld_shlibs_GCJ" >&5 -echo "${ECHO_T}$ld_shlibs_GCJ" >&6; } -test "$ld_shlibs_GCJ" = no && can_build_shared=no # # Do we need to explicitly link libc? # -case "x$archive_cmds_need_lc_GCJ" in +case "x$archive_cmds_need_lc_CXX" in x|xyes) # Assume -lc should be added - archive_cmds_need_lc_GCJ=yes + archive_cmds_need_lc_CXX=yes if test "$enable_shared" = yes && test "$GCC" = yes; then - case $archive_cmds_GCJ in + case $archive_cmds_CXX in *'~'*) # FIXME: we may have to deal with multi-command sequences. ;; @@ -17611,54 +13430,118 @@ # Test whether the compiler implicitly links with -lc since on some # systems, -lgcc has to come before -lc. If gcc already passes -lc # to ld, don't add -lc before -lgcc. - { echo "$as_me:$LINENO: checking whether -lc should be explicitly linked in" >&5 -echo $ECHO_N "checking whether -lc should be explicitly linked in... $ECHO_C" >&6; } - $rm conftest* - printf "$lt_simple_compile_test_code" > conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether -lc should be explicitly linked in" >&5 +$as_echo_n "checking whether -lc should be explicitly linked in... " >&6; } + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext - if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5 + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_compile\""; } >&5 (eval $ac_compile) 2>&5 ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } 2>conftest.err; then + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } 2>conftest.err; then soname=conftest lib=conftest libobjs=conftest.$ac_objext deplibs= - wl=$lt_prog_compiler_wl_GCJ - pic_flag=$lt_prog_compiler_pic_GCJ + wl=$lt_prog_compiler_wl_CXX + pic_flag=$lt_prog_compiler_pic_CXX compiler_flags=-v linker_flags=-v verstring= output_objdir=. libname=conftest - lt_save_allow_undefined_flag=$allow_undefined_flag_GCJ - allow_undefined_flag_GCJ= - if { (eval echo "$as_me:$LINENO: \"$archive_cmds_GCJ 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1\"") >&5 - (eval $archive_cmds_GCJ 2\>\&1 \| grep \" -lc \" \>/dev/null 2\>\&1) 2>&5 + lt_save_allow_undefined_flag=$allow_undefined_flag_CXX + allow_undefined_flag_CXX= + if { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1\""; } >&5 + (eval $archive_cmds_CXX 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) 2>&5 ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } then - archive_cmds_need_lc_GCJ=no + archive_cmds_need_lc_CXX=no else - archive_cmds_need_lc_GCJ=yes + archive_cmds_need_lc_CXX=yes fi - allow_undefined_flag_GCJ=$lt_save_allow_undefined_flag + allow_undefined_flag_CXX=$lt_save_allow_undefined_flag else cat conftest.err 1>&5 fi - $rm conftest* - { echo "$as_me:$LINENO: result: $archive_cmds_need_lc_GCJ" >&5 -echo "${ECHO_T}$archive_cmds_need_lc_GCJ" >&6; } + $RM conftest* + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $archive_cmds_need_lc_CXX" >&5 +$as_echo "$archive_cmds_need_lc_CXX" >&6; } ;; esac fi ;; esac -{ echo "$as_me:$LINENO: checking dynamic linker characteristics" >&5 -echo $ECHO_N "checking dynamic linker characteristics... $ECHO_C" >&6; } + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking dynamic linker characteristics" >&5 +$as_echo_n "checking dynamic linker characteristics... " >&6; } + library_names_spec= libname_spec='lib$name' soname_spec= @@ -17672,20 +13555,6 @@ version_type=none dynamic_linker="$host_os ld.so" sys_lib_dlsearch_path_spec="/lib /usr/lib" -if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';' >/dev/null ; then - # if the path contains ";" then we assume it to be the separator - # otherwise default to the standard path separator (i.e. ":") - it is - # assumed that no part of a normal pathname contains ";" but that should - # okay in the real world where ";" in dirpaths is itself problematic. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` - else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` - fi -else - sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" -fi need_lib_prefix=unknown hardcode_into_libs=no @@ -17703,7 +13572,7 @@ soname_spec='${libname}${release}${shared_ext}$major' ;; -aix4* | aix5*) +aix[4-9]*) version_type=linux need_lib_prefix=no need_version=no @@ -17722,7 +13591,7 @@ aix4 | aix4.[01] | aix4.[01].*) if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' echo ' yes ' - echo '#endif'; } | ${CC} -E - | grep yes > /dev/null; then + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then : else can_build_shared=no @@ -17748,9 +13617,18 @@ ;; amigaos*) - library_names_spec='$libname.ixlibrary $libname.a' - # Create ${libname}_ixlibrary.a entries in /sys/libs. - finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$echo "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $rm /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([^/]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac ;; beos*) @@ -17773,25 +13651,28 @@ # libtool to hard-code these into programs ;; -cygwin* | mingw* | pw32*) +cygwin* | mingw* | pw32* | cegcc*) version_type=windows shrext_cmds=".dll" need_version=no need_lib_prefix=no case $GCC,$host_os in - yes,cygwin* | yes,mingw* | yes,pw32*) + yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*) library_names_spec='$libname.dll.a' # DLL is installed to $(libdir)/../bin by postinstall_cmds postinstall_cmds='base_file=`basename \${file}`~ - dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i;echo \$dlname'\''`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ dldir=$destdir/`dirname \$dlpath`~ test -d \$dldir || mkdir -p \$dldir~ $install_prog $dir/$dlname \$dldir/$dlname~ - chmod a+x \$dldir/$dlname' + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ dlpath=$dir/\$dldll~ - $rm \$dlpath' + $RM \$dlpath' shlibpath_overrides_runpath=yes case $host_os in @@ -17800,20 +13681,20 @@ soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" ;; - mingw*) + mingw* | cegcc*) # MinGW DLLs use traditional 'lib' prefix soname_spec='${libname}`echo ${release} | $SED -e 's/[.]/-/g'`${versuffix}${shared_ext}' - sys_lib_search_path_spec=`$CC -print-search-dirs | grep "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` - if echo "$sys_lib_search_path_spec" | grep ';[c-zC-Z]:/' >/dev/null; then + sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if $ECHO "$sys_lib_search_path_spec" | $GREP ';[c-zC-Z]:/' >/dev/null; then # It is most probably a Windows format PATH printed by # mingw gcc, but we are running on Cygwin. Gcc prints its search # path with ; separators, and with drive letters. We can handle the # drive letters (cygwin fileutils understands them), so leave them, # especially as we might pass files found there to a mingw objdump, # which wouldn't understand a cygwinified path. Ahh. - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` else - sys_lib_search_path_spec=`echo "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` fi ;; pw32*) @@ -17837,17 +13718,12 @@ version_type=darwin need_lib_prefix=no need_version=no - library_names_spec='${libname}${release}${versuffix}$shared_ext ${libname}${release}${major}$shared_ext ${libname}$shared_ext' + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' soname_spec='${libname}${release}${major}$shared_ext' shlibpath_overrides_runpath=yes shlibpath_var=DYLD_LIBRARY_PATH shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' - # Apple's gcc prints 'gcc -print-search-dirs' doesn't operate the same. - if test "$GCC" = yes; then - sys_lib_search_path_spec=`$CC -print-search-dirs | tr "\n" "$PATH_SEPARATOR" | sed -e 's/libraries:/@libraries:/' | tr "@" "\n" | grep "^libraries:" | sed -e "s/^libraries://" -e "s,=/,/,g" -e "s,$PATH_SEPARATOR, ,g" -e "s,.*,& /lib /usr/lib /usr/local/lib,g"` - else - sys_lib_search_path_spec='/lib /usr/lib /usr/local/lib' - fi + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' ;; @@ -17864,18 +13740,6 @@ dynamic_linker=no ;; -kfreebsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; - freebsd* | dragonfly*) # DragonFly does not have aout. When/if they implement a new # versioning mechanism, adjust this. @@ -17913,7 +13777,7 @@ shlibpath_overrides_runpath=no hardcode_into_libs=yes ;; - freebsd*) # from 4.6 on + *) # from 4.6 on, and DragonFly shlibpath_overrides_runpath=yes hardcode_into_libs=yes ;; @@ -17952,18 +13816,18 @@ fi sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec ;; - hppa*64*) - shrext_cmds='.sl' - hardcode_into_libs=yes - dynamic_linker="$host_os dld.sl" - shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH - shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" - sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec - ;; - *) + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) shrext_cmds='.sl' dynamic_linker="$host_os dld.sl" shlibpath_var=SHLIB_PATH @@ -17976,7 +13840,7 @@ postinstall_cmds='chmod 555 $lib' ;; -interix3*) +interix[3-9]*) version_type=linux need_lib_prefix=no need_version=no @@ -18031,7 +13895,7 @@ ;; # This must be Linux ELF. -linux*) +linux* | k*bsd*-gnu) version_type=linux need_lib_prefix=no need_version=no @@ -18040,6 +13904,32 @@ finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' shlibpath_var=LD_LIBRARY_PATH shlibpath_overrides_runpath=no + # Some binutils ld are patched to set DT_RUNPATH + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$lt_prog_compiler_wl_CXX\"; \ + LDFLAGS=\"\$LDFLAGS $hardcode_libdir_flag_spec_CXX\"" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_link "$LINENO"; then : + if ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null; then : + shlibpath_overrides_runpath=yes +fi +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + # This implies no fast_install, which is unacceptable. # Some rework will be needed to allow for fast_install # before this can be enabled. @@ -18047,7 +13937,7 @@ # Append ld.so.conf contents to the search path if test -f /etc/ld.so.conf; then - lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \$2)); skip = 1; } { if (!skip) print \$0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" fi @@ -18060,23 +13950,11 @@ dynamic_linker='GNU/Linux ld.so' ;; -knetbsd*-gnu) - version_type=linux - need_lib_prefix=no - need_version=no - library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' - soname_spec='${libname}${release}${shared_ext}$major' - shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=no - hardcode_into_libs=yes - dynamic_linker='GNU ld.so' - ;; - netbsd*) version_type=sunos need_lib_prefix=no need_version=no - if echo __ELF__ | $CC -E - | grep __ELF__ >/dev/null; then + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' dynamic_linker='NetBSD (a.out) ld.so' @@ -18097,14 +13975,16 @@ shlibpath_overrides_runpath=yes ;; -nto-qnx*) - version_type=linux +*nto* | *qnx*) + version_type=qnx need_lib_prefix=no need_version=no library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH - shlibpath_overrides_runpath=yes + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' ;; openbsd*) @@ -18113,13 +13993,13 @@ need_lib_prefix=no # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. case $host_os in - openbsd3.3 | openbsd3.3.*) need_version=yes ;; - *) need_version=no ;; + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; esac library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' shlibpath_var=LD_LIBRARY_PATH - if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then case $host_os in openbsd2.[89] | openbsd2.[89].*) shlibpath_overrides_runpath=no @@ -18153,6 +14033,10 @@ sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" ;; +rdos*) + dynamic_linker=no + ;; + solaris*) version_type=linux need_lib_prefix=no @@ -18187,7 +14071,6 @@ sni) shlibpath_overrides_runpath=no need_lib_prefix=no - export_dynamic_flag_spec='${wl}-Blargedynsym' runpath_var=LD_RUN_PATH ;; siemens) @@ -18218,13 +14101,12 @@ library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' soname_spec='${libname}${release}${shared_ext}$major' shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes hardcode_into_libs=yes if test "$with_gnu_ld" = yes; then sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' - shlibpath_overrides_runpath=no else sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' - shlibpath_overrides_runpath=yes case $host_os in sco3.2v5*) sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" @@ -18234,6 +14116,17 @@ sys_lib_dlsearch_path_spec='/usr/lib' ;; +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + uts4*) version_type=linux library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' @@ -18245,8 +14138,8 @@ dynamic_linker=no ;; esac -{ echo "$as_me:$LINENO: result: $dynamic_linker" >&5 -echo "${ECHO_T}$dynamic_linker" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $dynamic_linker" >&5 +$as_echo "$dynamic_linker" >&6; } test "$dynamic_linker" = no && can_build_shared=no variables_saved_for_relink="PATH $shlibpath_var $runpath_var" @@ -18254,35 +14147,78 @@ variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" fi -{ echo "$as_me:$LINENO: checking how to hardcode library paths into programs" >&5 -echo $ECHO_N "checking how to hardcode library paths into programs... $ECHO_C" >&6; } -hardcode_action_GCJ= -if test -n "$hardcode_libdir_flag_spec_GCJ" || \ - test -n "$runpath_var_GCJ" || \ - test "X$hardcode_automatic_GCJ" = "Xyes" ; then +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + + + + + + + + + + + - # We can hardcode non-existant directories. - if test "$hardcode_direct_GCJ" != no && + + + + + + + + + + + + + + + + + + + + + + + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to hardcode library paths into programs" >&5 +$as_echo_n "checking how to hardcode library paths into programs... " >&6; } +hardcode_action_CXX= +if test -n "$hardcode_libdir_flag_spec_CXX" || + test -n "$runpath_var_CXX" || + test "X$hardcode_automatic_CXX" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$hardcode_direct_CXX" != no && # If the only mechanism to avoid hardcoding is shlibpath_var, we # have to relink, otherwise we might link with an installed library # when we should be linking with a yet-to-be-installed one - ## test "$_LT_AC_TAGVAR(hardcode_shlibpath_var, GCJ)" != no && - test "$hardcode_minus_L_GCJ" != no; then + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, CXX)" != no && + test "$hardcode_minus_L_CXX" != no; then # Linking always hardcodes the temporary library directory. - hardcode_action_GCJ=relink + hardcode_action_CXX=relink else # We can link without hardcoding, and we can hardcode nonexisting dirs. - hardcode_action_GCJ=immediate + hardcode_action_CXX=immediate fi else # We cannot hardcode anything, or else we can only hardcode existing # directories. - hardcode_action_GCJ=unsupported + hardcode_action_CXX=unsupported fi -{ echo "$as_me:$LINENO: result: $hardcode_action_GCJ" >&5 -echo "${ECHO_T}$hardcode_action_GCJ" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $hardcode_action_CXX" >&5 +$as_echo "$hardcode_action_CXX" >&6; } -if test "$hardcode_action_GCJ" = relink; then +if test "$hardcode_action_CXX" = relink || + test "$inherit_rpath_CXX" = yes; then # Fast installation is not supported enable_fast_install=no elif test "$shlibpath_overrides_runpath" = yes || @@ -18292,2548 +14228,3235 @@ fi -# The else clause should only fire when bootstrapping the -# libtool distribution, otherwise you forgot to ship ltmain.sh -# with your package, and you will get complaints that there are -# no rules to generate ltmain.sh. -if test -f "$ltmain"; then - # See if we are running on zsh, and set the options which allow our commands through - # without removal of \ escapes. - if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST - fi - # Now quote all the things that may contain metacharacters while being - # careful not to overquote the AC_SUBSTed values. We take copies of the - # variables and quote the copies for generation of the libtool script. - for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC LTCFLAGS NM \ - SED SHELL STRIP \ - libname_spec library_names_spec soname_spec extract_expsyms_cmds \ - old_striplib striplib file_magic_cmd finish_cmds finish_eval \ - deplibs_check_method reload_flag reload_cmds need_locks \ - lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \ - lt_cv_sys_global_symbol_to_c_name_address \ - sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ - old_postinstall_cmds old_postuninstall_cmds \ - compiler_GCJ \ - CC_GCJ \ - LD_GCJ \ - lt_prog_compiler_wl_GCJ \ - lt_prog_compiler_pic_GCJ \ - lt_prog_compiler_static_GCJ \ - lt_prog_compiler_no_builtin_flag_GCJ \ - export_dynamic_flag_spec_GCJ \ - thread_safe_flag_spec_GCJ \ - whole_archive_flag_spec_GCJ \ - enable_shared_with_static_runtimes_GCJ \ - old_archive_cmds_GCJ \ - old_archive_from_new_cmds_GCJ \ - predep_objects_GCJ \ - postdep_objects_GCJ \ - predeps_GCJ \ - postdeps_GCJ \ - compiler_lib_search_path_GCJ \ - archive_cmds_GCJ \ - archive_expsym_cmds_GCJ \ - postinstall_cmds_GCJ \ - postuninstall_cmds_GCJ \ - old_archive_from_expsyms_cmds_GCJ \ - allow_undefined_flag_GCJ \ - no_undefined_flag_GCJ \ - export_symbols_cmds_GCJ \ - hardcode_libdir_flag_spec_GCJ \ - hardcode_libdir_flag_spec_ld_GCJ \ - hardcode_libdir_separator_GCJ \ - hardcode_automatic_GCJ \ - module_cmds_GCJ \ - module_expsym_cmds_GCJ \ - lt_cv_prog_compiler_c_o_GCJ \ - exclude_expsyms_GCJ \ - include_expsyms_GCJ; do - - case $var in - old_archive_cmds_GCJ | \ - old_archive_from_new_cmds_GCJ | \ - archive_cmds_GCJ | \ - archive_expsym_cmds_GCJ | \ - module_cmds_GCJ | \ - module_expsym_cmds_GCJ | \ - old_archive_from_expsyms_cmds_GCJ | \ - export_symbols_cmds_GCJ | \ - extract_expsyms_cmds | reload_cmds | finish_cmds | \ - postinstall_cmds | postuninstall_cmds | \ - old_postinstall_cmds | old_postuninstall_cmds | \ - sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) - # Double-quote double-evaled strings. - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" - ;; - *) - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" - ;; - esac - done - case $lt_echo in - *'\$0 --fallback-echo"') - lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'` - ;; - esac -cfgfile="$ofile" - cat <<__EOF__ >> "$cfgfile" -# ### BEGIN LIBTOOL TAG CONFIG: $tagname -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: -# Shell to use when invoking shell scripts. -SHELL=$lt_SHELL + fi # test -n "$compiler" -# Whether or not to build shared libraries. -build_libtool_libs=$enable_shared + CC=$lt_save_CC + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test "$_lt_caught_CXX_error" != yes -# Whether or not to build static libraries. -build_old_libs=$enable_static +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu -# Whether or not to add -lc for building shared libraries. -build_libtool_need_lc=$archive_cmds_need_lc_GCJ -# Whether or not to disallow shared libs when runtime libs are static -allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_GCJ -# Whether or not to optimize for fast installation. -fast_install=$enable_fast_install -# The host system. -host_alias=$host_alias -host=$host -host_os=$host_os -# The build system. -build_alias=$build_alias -build=$build -build_os=$build_os -# An echo program that does not interpret backslashes. -echo=$lt_echo -# The archiver. -AR=$lt_AR -AR_FLAGS=$lt_AR_FLAGS -# A C compiler. -LTCC=$lt_LTCC -# LTCC compiler flags. -LTCFLAGS=$lt_LTCFLAGS -# A language-specific compiler. -CC=$lt_compiler_GCJ -# Is the compiler the GNU C compiler? -with_gcc=$GCC_GCJ -# An ERE matcher. -EGREP=$lt_EGREP -# The linker used to build libraries. -LD=$lt_LD_GCJ + ac_config_commands="$ac_config_commands libtool" -# Whether we need hard or soft links. -LN_S=$lt_LN_S -# A BSD-compatible nm program. -NM=$lt_NM -# A symbol stripping program -STRIP=$lt_STRIP -# Used to examine libraries when file_magic_cmd begins "file" -MAGIC_CMD=$MAGIC_CMD +# Only expand once: -# Used on cygwin: DLL creation program. -DLLTOOL="$DLLTOOL" -# Used on cygwin: object dumper. -OBJDUMP="$OBJDUMP" -# Used on cygwin: assembler. -AS="$AS" -# The name of the directory that contains temporary libtool files. -objdir=$objdir +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if test "${ac_cv_header_stdc+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include -# How to create reloadable object files. -reload_flag=$lt_reload_flag -reload_cmds=$lt_reload_cmds +int +main () +{ -# How to pass a linker flag through the compiler. -wl=$lt_lt_prog_compiler_wl_GCJ + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext -# Object file suffix (normally "o"). -objext="$ac_objext" +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include -# Old archive suffix (normally "a"). -libext="$libext" +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : -# Shared library suffix (normally ".so"). -shrext_cmds='$shrext_cmds' +else + ac_cv_header_stdc=no +fi +rm -f conftest* -# Executable file suffix (normally ""). -exeext="$exeext" +fi -# Additional compiler flags for building library objects. -pic_flag=$lt_lt_prog_compiler_pic_GCJ -pic_mode=$pic_mode +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include -# What is the maximum length of a command? -max_cmd_len=$lt_cv_sys_max_cmd_len +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# =========================================================================== +# http://www.nongnu.org/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC to any special C compiler that is needed for +# multi-threaded programs (defaults to the value of CC otherwise). (This +# is necessary on AIX to use the special cc_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also link it with them as well. e.g. you should link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threads programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name +# (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# 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 the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 6 + +# This is what autoupdate's m4 run will expand. It fires +# the warning (with _au_warn_XXX), outputs it into the +# updated configure.ac (with AC_DIAGNOSE), and then outputs +# the replacement expansion. + + +# This is an auxiliary macro that is also run when +# autoupdate runs m4. It simply calls m4_warning, but +# we need a wrapper so that each warning is emitted only +# once. We break the quoting in m4_warning's argument in +# order to expand this macro's arguments, not AU_DEFUN's. + + +# Finally, this is the expansion that is picked up by +# autoconf. It tells the user to run autoupdate, and +# then outputs the replacement expansion. We do not care +# about autoupdate's warning because that contains +# information on what to do *after* running autoupdate. + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS" >&5 +$as_echo_n "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_join (); +int +main () +{ +return pthread_join (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ax_pthread_ok=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 +$as_echo "$ax_pthread_ok" >&6; } + if test x"$ax_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" + ;; -# Does compiler simultaneously support -c and -o options? -compiler_c_o=$lt_lt_cv_prog_compiler_c_o_GCJ + *-darwin*) + acx_pthread_flags="-pthread $acx_pthread_flags" + ;; +esac -# Must we lock files when doing compilation? -need_locks=$lt_need_locks +if test x"$ax_pthread_ok" = xno; then +for flag in $ax_pthread_flags; do -# Do we need the lib prefix for modules? -need_lib_prefix=$need_lib_prefix + case $flag in + none) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5 +$as_echo_n "checking whether pthreads work without any flags... " >&6; } + ;; + + -*) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $flag" >&5 +$as_echo_n "checking whether pthreads work with $flag... " >&6; } + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + # Extract the first word of "pthread-config", so it can be a program name with args. +set dummy pthread-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_ax_pthread_config+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ax_pthread_config"; then + ac_cv_prog_ax_pthread_config="$ax_pthread_config" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_ax_pthread_config="yes" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS -# Do we need a version for libraries? -need_version=$need_version + test -z "$ac_cv_prog_ax_pthread_config" && ac_cv_prog_ax_pthread_config="no" +fi +fi +ax_pthread_config=$ac_cv_prog_ax_pthread_config +if test -n "$ax_pthread_config"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_config" >&5 +$as_echo "$ax_pthread_config" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi -# Whether dlopen is supported. -dlopen_support=$enable_dlopen -# Whether dlopen of programs is supported. -dlopen_self=$enable_dlopen_self + if test x"$ax_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; -# Whether dlopen of statically linked programs is supported. -dlopen_self_static=$enable_dlopen_self_static + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$flag" >&5 +$as_echo_n "checking for the pthreads library -l$flag... " >&6; } + PTHREAD_LIBS="-l$flag" + ;; + esac -# Compiler flag to prevent dynamic linking. -link_static_flag=$lt_lt_prog_compiler_static_GCJ + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + static void routine(void* a) {a=0;} + static void* start_routine(void* a) {return a;} +int +main () +{ +pthread_t th; pthread_attr_t attr; + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_create(&th,0,start_routine,0); + pthread_cleanup_pop(0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ax_pthread_ok=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext -# Compiler flag to turn off builtin functions. -no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_GCJ + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" -# Compiler flag to allow reflexive dlopens. -export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_GCJ + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 +$as_echo "$ax_pthread_ok" >&6; } + if test "x$ax_pthread_ok" = xyes; then + break; + fi -# Compiler flag to generate shared objects directly from archives. -whole_archive_flag_spec=$lt_whole_archive_flag_spec_GCJ + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi -# Compiler flag to generate thread-safe objects. -thread_safe_flag_spec=$lt_thread_safe_flag_spec_GCJ +# Various other checks: +if test "x$ax_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5 +$as_echo_n "checking for joinable pthread attribute... " >&6; } + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +int attr=$attr; return attr; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + attr_name=$attr; break +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $attr_name" >&5 +$as_echo "$attr_name" >&6; } + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then -# Library versioning type. -version_type=$version_type +cat >>confdefs.h <<_ACEOF +#define PTHREAD_CREATE_JOINABLE $attr_name +_ACEOF -# Format of library name prefix. -libname_spec=$lt_libname_spec + fi -# List of archive names. First name is the real one, the rest are links. -# The last name is the one that the linker finds with -lNAME. -library_names_spec=$lt_library_names_spec + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if more special flags are required for pthreads" >&5 +$as_echo_n "checking if more special flags are required for pthreads... " >&6; } + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${flag}" >&5 +$as_echo "${flag}" >&6; } + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi -# The coded name of the library, if different from the real name. -soname_spec=$lt_soname_spec + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" -# Commands used to build and install an old-style archive. -RANLIB=$lt_RANLIB -old_archive_cmds=$lt_old_archive_cmds_GCJ -old_postinstall_cmds=$lt_old_postinstall_cmds -old_postuninstall_cmds=$lt_old_postuninstall_cmds + # More AIX lossage: must compile with xlc_r or cc_r + if test x"$GCC" != xyes; then + for ac_prog in xlc_r cc_r +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if test "${ac_cv_prog_PTHREAD_CC+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$PTHREAD_CC"; then + ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then + ac_cv_prog_PTHREAD_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS -# Create an old-style archive from a shared archive. -old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_GCJ +fi +fi +PTHREAD_CC=$ac_cv_prog_PTHREAD_CC +if test -n "$PTHREAD_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5 +$as_echo "$PTHREAD_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi -# Create a temporary old-style archive to link instead of a shared archive. -old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_GCJ -# Commands used to build and install a shared archive. -archive_cmds=$lt_archive_cmds_GCJ -archive_expsym_cmds=$lt_archive_expsym_cmds_GCJ -postinstall_cmds=$lt_postinstall_cmds -postuninstall_cmds=$lt_postuninstall_cmds + test -n "$PTHREAD_CC" && break +done +test -n "$PTHREAD_CC" || PTHREAD_CC="${CC}" -# Commands used to build a loadable module (assumed same as above if empty) -module_cmds=$lt_module_cmds_GCJ -module_expsym_cmds=$lt_module_expsym_cmds_GCJ + else + PTHREAD_CC=$CC + fi +else + PTHREAD_CC="$CC" +fi -# Commands to strip libraries. -old_striplib=$lt_old_striplib -striplib=$lt_striplib -# Dependencies to place before the objects being linked to create a -# shared library. -predep_objects=$lt_predep_objects_GCJ - -# Dependencies to place after the objects being linked to create a -# shared library. -postdep_objects=$lt_postdep_objects_GCJ - -# Dependencies to place before the objects being linked to create a -# shared library. -predeps=$lt_predeps_GCJ - -# Dependencies to place after the objects being linked to create a -# shared library. -postdeps=$lt_postdeps_GCJ -# The library search path used internally by the compiler when linking -# a shared library. -compiler_lib_search_path=$lt_compiler_lib_search_path_GCJ -# Method to check whether dependent libraries are shared objects. -deplibs_check_method=$lt_deplibs_check_method -# Command to use when deplibs_check_method == file_magic. -file_magic_cmd=$lt_file_magic_cmd +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$ax_pthread_ok" = xyes; then -# Flag that allows shared libraries with undefined symbols to be built. -allow_undefined_flag=$lt_allow_undefined_flag_GCJ +$as_echo "#define HAVE_PTHREAD 1" >>confdefs.h -# Flag that forces no undefined symbols. -no_undefined_flag=$lt_no_undefined_flag_GCJ + : +else + ax_pthread_ok=no -# Commands used to finish a libtool library installation in a directory. -finish_cmds=$lt_finish_cmds +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu -# Same as above, but a single script fragment to be evaled but not shown. -finish_eval=$lt_finish_eval -# Take the output of nm and produce a listing of raw symbols and C names. -global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe -# Transform the output of nm in a proper C declaration -global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl +# Check whether --enable-m32 was given. +if test "${enable_m32+set}" = set; then : + enableval=$enable_m32; case "${enableval}" in + yes) + CFLAGS=$(CFLAGS) -m32 + usem32=true + ;; + no) + usem32=false + ;; + *) + as_fn_error "bad value ${enableval} for --enable-m32" "$LINENO" 5 + ;; + esac +else + usem32=false +fi -# Transform the output of nm in a C name address pair -global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address -# This is the shared library runtime path variable. -runpath_var=$runpath_var +# Check whether --enable-selftest was given. +if test "${enable_selftest+set}" = set; then : + enableval=$enable_selftest; case "${enableval}" in + yes) + selftest=true + ;; + no) + selftest=false + ;; + *) + as_fn_error "bad value ${enableval} for --enable-selftest" "$LINENO" 5 + ;; + esac +else + selftest=false +fi -# This is the shared library path variable. -shlibpath_var=$shlibpath_var + if test x$selftest = xtrue; then + SELFTEST_TRUE= + SELFTEST_FALSE='#' +else + SELFTEST_TRUE='#' + SELFTEST_FALSE= +fi -# Is shlibpath searched before the hard-coded library search path? -shlibpath_overrides_runpath=$shlibpath_overrides_runpath -# How to hardcode a shared library path into an executable. -hardcode_action=$hardcode_action_GCJ +ac_config_files="$ac_config_files Makefile" -# Whether we should hardcode library paths into libraries. -hardcode_into_libs=$hardcode_into_libs +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. -# Flag to hardcode \$libdir into a binary during linking. -# This must work even if \$libdir does not exist. -hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_GCJ +_ACEOF -# If ld is used when linking, flag to hardcode \$libdir into -# a binary during linking. This must work even if \$libdir does -# not exist. -hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_GCJ - -# Whether we need a single -rpath flag with a separated argument. -hardcode_libdir_separator=$lt_hardcode_libdir_separator_GCJ - -# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the -# resulting binary. -hardcode_direct=$hardcode_direct_GCJ - -# Set to yes if using the -LDIR flag during linking hardcodes DIR into the -# resulting binary. -hardcode_minus_L=$hardcode_minus_L_GCJ - -# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into -# the resulting binary. -hardcode_shlibpath_var=$hardcode_shlibpath_var_GCJ - -# Set to yes if building a shared library automatically hardcodes DIR into the library -# and all subsequent libraries and executables linked against it. -hardcode_automatic=$hardcode_automatic_GCJ +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done -# Variables whose values should be saved in libtool wrapper scripts and -# restored at relink time. -variables_saved_for_relink="$variables_saved_for_relink" + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + test "x$cache_file" != "x/dev/null" && + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + cat confcache >$cache_file + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache -# Whether libtool must link a program against all its dependency libraries. -link_all_deplibs=$link_all_deplibs_GCJ +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' -# Compile-time system search path for libraries -sys_lib_search_path_spec=$lt_sys_lib_search_path_spec +DEFS=-DHAVE_CONFIG_H -# Run-time system search path for libraries -sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs -# Fix the shell variable \$srcfile for the compiler. -fix_srcfile_path="$fix_srcfile_path_GCJ" +LTLIBOBJS=$ac_ltlibobjs -# Set to yes if exported symbols are required. -always_export_symbols=$always_export_symbols_GCJ -# The commands to list exported symbols. -export_symbols_cmds=$lt_export_symbols_cmds_GCJ + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi -# The commands to extract the exported symbol list from a shared archive. -extract_expsyms_cmds=$lt_extract_expsyms_cmds +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + as_fn_error "conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + as_fn_error "conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${SELFTEST_TRUE}" && test -z "${SELFTEST_FALSE}"; then + as_fn_error "conditional \"SELFTEST\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi -# Symbols that should not be listed in the preloaded symbols. -exclude_expsyms=$lt_exclude_expsyms_GCJ +: ${CONFIG_STATUS=./config.status} +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. -# Symbols that must always be exported. -include_expsyms=$lt_include_expsyms_GCJ +debug=false +ac_cs_recheck=false +ac_cs_silent=false -# ### END LIBTOOL TAG CONFIG: $tagname +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## -__EOF__ +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi -else - # If there is no Makefile yet, we rely on a make rule to execute - # `config.status --recheck' to rerun these tests and create the - # libtool script then. - ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` - if test -f "$ltmain_in"; then - test -f Makefile && make "$ltmain" +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' fi +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu -CC="$lt_save_CC" +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" - else - tagname="" - fi - ;; +# Find who we are. Look in the path if we contain no directory separator. +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS - RC) + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' -# Source file extension for RC test sources. -ac_ext=rc +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE -# Object file extension for compiled RC test sources. -objext=o -objext_RC=$objext +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH -# Code to be used in simple compile tests -lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }\n' -# Code to be used in simple link tests -lt_simple_link_test_code="$lt_simple_compile_test_code" +# as_fn_error ERROR [LINENO LOG_FD] +# --------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with status $?, using 1 if that was 0. +as_fn_error () +{ + as_status=$?; test $as_status -eq 0 && as_status=1 + if test "$3"; then + as_lineno=${as_lineno-"$2"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $1" >&$3 + fi + $as_echo "$as_me: error: $1" >&2 + as_fn_exit $as_status +} # as_fn_error -# ltmain only uses $CC for tagged configurations so make sure $CC is set. -# If no C compiler was specified, use CC. -LTCC=${LTCC-"$CC"} +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status -# If no C compiler flags were specified, use CFLAGS. -LTCFLAGS=${LTCFLAGS-"$CFLAGS"} +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith -# Allow CC to be a program name with arguments. -compiler=$CC +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi -# save warnings/boilerplate of simple test code -ac_outfile=conftest.$ac_objext -printf "$lt_simple_compile_test_code" >conftest.$ac_ext -eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_compiler_boilerplate=`cat conftest.err` -$rm conftest* +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi -ac_outfile=conftest.$ac_objext -printf "$lt_simple_link_test_code" >conftest.$ac_ext -eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err -_lt_linker_boilerplate=`cat conftest.err` -$rm conftest* +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits -# Allow CC to be a program name with arguments. -lt_save_CC="$CC" -CC=${RC-"windres"} -compiler=$CC -compiler_RC=$CC -for cc_temp in $compiler""; do - case $cc_temp in - compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; - distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; - \-*) ;; - *) break;; - esac -done -cc_basename=`$echo "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -p'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -p' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -p' + fi +else + as_ln_s='cp -p' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null -lt_cv_prog_compiler_c_o_RC=yes -# The else clause should only fire when bootstrapping the -# libtool distribution, otherwise you forgot to ship ltmain.sh -# with your package, and you will get complaints that there are -# no rules to generate ltmain.sh. -if test -f "$ltmain"; then - # See if we are running on zsh, and set the options which allow our commands through - # without removal of \ escapes. - if test -n "${ZSH_VERSION+set}" ; then - setopt NO_GLOB_SUBST - fi - # Now quote all the things that may contain metacharacters while being - # careful not to overquote the AC_SUBSTed values. We take copies of the - # variables and quote the copies for generation of the libtool script. - for var in echo old_CC old_CFLAGS AR AR_FLAGS EGREP RANLIB LN_S LTCC LTCFLAGS NM \ - SED SHELL STRIP \ - libname_spec library_names_spec soname_spec extract_expsyms_cmds \ - old_striplib striplib file_magic_cmd finish_cmds finish_eval \ - deplibs_check_method reload_flag reload_cmds need_locks \ - lt_cv_sys_global_symbol_pipe lt_cv_sys_global_symbol_to_cdecl \ - lt_cv_sys_global_symbol_to_c_name_address \ - sys_lib_search_path_spec sys_lib_dlsearch_path_spec \ - old_postinstall_cmds old_postuninstall_cmds \ - compiler_RC \ - CC_RC \ - LD_RC \ - lt_prog_compiler_wl_RC \ - lt_prog_compiler_pic_RC \ - lt_prog_compiler_static_RC \ - lt_prog_compiler_no_builtin_flag_RC \ - export_dynamic_flag_spec_RC \ - thread_safe_flag_spec_RC \ - whole_archive_flag_spec_RC \ - enable_shared_with_static_runtimes_RC \ - old_archive_cmds_RC \ - old_archive_from_new_cmds_RC \ - predep_objects_RC \ - postdep_objects_RC \ - predeps_RC \ - postdeps_RC \ - compiler_lib_search_path_RC \ - archive_cmds_RC \ - archive_expsym_cmds_RC \ - postinstall_cmds_RC \ - postuninstall_cmds_RC \ - old_archive_from_expsyms_cmds_RC \ - allow_undefined_flag_RC \ - no_undefined_flag_RC \ - export_symbols_cmds_RC \ - hardcode_libdir_flag_spec_RC \ - hardcode_libdir_flag_spec_ld_RC \ - hardcode_libdir_separator_RC \ - hardcode_automatic_RC \ - module_cmds_RC \ - module_expsym_cmds_RC \ - lt_cv_prog_compiler_c_o_RC \ - exclude_expsyms_RC \ - include_expsyms_RC; do - - case $var in - old_archive_cmds_RC | \ - old_archive_from_new_cmds_RC | \ - archive_cmds_RC | \ - archive_expsym_cmds_RC | \ - module_cmds_RC | \ - module_expsym_cmds_RC | \ - old_archive_from_expsyms_cmds_RC | \ - export_symbols_cmds_RC | \ - extract_expsyms_cmds | reload_cmds | finish_cmds | \ - postinstall_cmds | postuninstall_cmds | \ - old_postinstall_cmds | old_postuninstall_cmds | \ - sys_lib_search_path_spec | sys_lib_dlsearch_path_spec) - # Double-quote double-evaled strings. - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$double_quote_subst\" -e \"\$sed_quote_subst\" -e \"\$delay_variable_subst\"\`\\\"" - ;; - *) - eval "lt_$var=\\\"\`\$echo \"X\$$var\" | \$Xsed -e \"\$sed_quote_subst\"\`\\\"" - ;; - esac - done +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ - case $lt_echo in - *'\$0 --fallback-echo"') - lt_echo=`$echo "X$lt_echo" | $Xsed -e 's/\\\\\\\$0 --fallback-echo"$/$0 --fallback-echo"/'` - ;; + case $as_dir in #( + -*) as_dir=./$as_dir;; esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error "cannot create directory $as_dir" -cfgfile="$ofile" - - cat <<__EOF__ >> "$cfgfile" -# ### BEGIN LIBTOOL TAG CONFIG: $tagname -# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi -# Shell to use when invoking shell scripts. -SHELL=$lt_SHELL +if test -x / >/dev/null 2>&1; then + as_test_x='test -x' +else + if ls -dL / >/dev/null 2>&1; then + as_ls_L_option=L + else + as_ls_L_option= + fi + as_test_x=' + eval sh -c '\'' + if test -d "$1"; then + test -d "$1/."; + else + case $1 in #( + -*)set "./$1";; + esac; + case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( + ???[sx]*):;;*)false;;esac;fi + '\'' sh + ' +fi +as_executable_p=$as_test_x -# Whether or not to build shared libraries. -build_libtool_libs=$enable_shared +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" -# Whether or not to build static libraries. -build_old_libs=$enable_static +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" -# Whether or not to add -lc for building shared libraries. -build_libtool_need_lc=$archive_cmds_need_lc_RC -# Whether or not to disallow shared libs when runtime libs are static -allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_RC +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 -# Whether or not to optimize for fast installation. -fast_install=$enable_fast_install +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by breakpad $as_me 0.1, which was +generated by GNU Autoconf 2.65. Invocation command line was -# The host system. -host_alias=$host_alias -host=$host -host_os=$host_os + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ -# The build system. -build_alias=$build_alias -build=$build -build_os=$build_os +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" -# An echo program that does not interpret backslashes. -echo=$lt_echo +_ACEOF -# The archiver. -AR=$lt_AR -AR_FLAGS=$lt_AR_FLAGS +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac -# A C compiler. -LTCC=$lt_LTCC +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac -# LTCC compiler flags. -LTCFLAGS=$lt_LTCFLAGS -# A language-specific compiler. -CC=$lt_compiler_RC +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" -# Is the compiler the GNU C compiler? -with_gcc=$GCC_RC +_ACEOF -# An ERE matcher. -EGREP=$lt_EGREP +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. -# The linker used to build libraries. -LD=$lt_LD_RC +Usage: $0 [OPTION]... [TAG]... -# Whether we need hard or soft links. -LN_S=$lt_LN_S + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE -# A BSD-compatible nm program. -NM=$lt_NM +Configuration files: +$config_files -# A symbol stripping program -STRIP=$lt_STRIP +Configuration headers: +$config_headers -# Used to examine libraries when file_magic_cmd begins "file" -MAGIC_CMD=$MAGIC_CMD +Configuration commands: +$config_commands -# Used on cygwin: DLL creation program. -DLLTOOL="$DLLTOOL" +Report bugs to ." -# Used on cygwin: object dumper. -OBJDUMP="$OBJDUMP" +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +breakpad config.status 0.1 +configured by $0, generated by GNU Autoconf 2.65, + with options \\"\$ac_cs_config\\" -# Used on cygwin: assembler. -AS="$AS" +Copyright (C) 2009 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." -# The name of the directory that contains temporary libtool files. -objdir=$objdir +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF -# How to create reloadable object files. -reload_flag=$lt_reload_flag -reload_cmds=$lt_reload_cmds +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac -# How to pass a linker flag through the compiler. -wl=$lt_lt_prog_compiler_wl_RC + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; -# Object file suffix (normally "o"). -objext="$ac_objext" + # This is an error. + -*) as_fn_error "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; -# Old archive suffix (normally "a"). -libext="$libext" + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; -# Shared library suffix (normally ".so"). -shrext_cmds='$shrext_cmds' + esac + shift +done -# Executable file suffix (normally ""). -exeext="$exeext" +ac_configure_extra_args= -# Additional compiler flags for building library objects. -pic_flag=$lt_lt_prog_compiler_pic_RC -pic_mode=$pic_mode +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi -# What is the maximum length of a command? -max_cmd_len=$lt_cv_sys_max_cmd_len +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi -# Does compiler simultaneously support -c and -o options? -compiler_c_o=$lt_lt_cv_prog_compiler_c_o_RC +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 -# Must we lock files when doing compilation? -need_locks=$lt_need_locks +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# +AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" -# Do we need the lib prefix for modules? -need_lib_prefix=$need_lib_prefix -# Do we need a version for libraries? -need_version=$need_version +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH -# Whether dlopen is supported. -dlopen_support=$enable_dlopen +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +macro_version='`$ECHO "X$macro_version" | $Xsed -e "$delay_single_quote_subst"`' +macro_revision='`$ECHO "X$macro_revision" | $Xsed -e "$delay_single_quote_subst"`' +enable_shared='`$ECHO "X$enable_shared" | $Xsed -e "$delay_single_quote_subst"`' +enable_static='`$ECHO "X$enable_static" | $Xsed -e "$delay_single_quote_subst"`' +pic_mode='`$ECHO "X$pic_mode" | $Xsed -e "$delay_single_quote_subst"`' +enable_fast_install='`$ECHO "X$enable_fast_install" | $Xsed -e "$delay_single_quote_subst"`' +host_alias='`$ECHO "X$host_alias" | $Xsed -e "$delay_single_quote_subst"`' +host='`$ECHO "X$host" | $Xsed -e "$delay_single_quote_subst"`' +host_os='`$ECHO "X$host_os" | $Xsed -e "$delay_single_quote_subst"`' +build_alias='`$ECHO "X$build_alias" | $Xsed -e "$delay_single_quote_subst"`' +build='`$ECHO "X$build" | $Xsed -e "$delay_single_quote_subst"`' +build_os='`$ECHO "X$build_os" | $Xsed -e "$delay_single_quote_subst"`' +SED='`$ECHO "X$SED" | $Xsed -e "$delay_single_quote_subst"`' +Xsed='`$ECHO "X$Xsed" | $Xsed -e "$delay_single_quote_subst"`' +GREP='`$ECHO "X$GREP" | $Xsed -e "$delay_single_quote_subst"`' +EGREP='`$ECHO "X$EGREP" | $Xsed -e "$delay_single_quote_subst"`' +FGREP='`$ECHO "X$FGREP" | $Xsed -e "$delay_single_quote_subst"`' +LD='`$ECHO "X$LD" | $Xsed -e "$delay_single_quote_subst"`' +NM='`$ECHO "X$NM" | $Xsed -e "$delay_single_quote_subst"`' +LN_S='`$ECHO "X$LN_S" | $Xsed -e "$delay_single_quote_subst"`' +max_cmd_len='`$ECHO "X$max_cmd_len" | $Xsed -e "$delay_single_quote_subst"`' +ac_objext='`$ECHO "X$ac_objext" | $Xsed -e "$delay_single_quote_subst"`' +exeext='`$ECHO "X$exeext" | $Xsed -e "$delay_single_quote_subst"`' +lt_unset='`$ECHO "X$lt_unset" | $Xsed -e "$delay_single_quote_subst"`' +lt_SP2NL='`$ECHO "X$lt_SP2NL" | $Xsed -e "$delay_single_quote_subst"`' +lt_NL2SP='`$ECHO "X$lt_NL2SP" | $Xsed -e "$delay_single_quote_subst"`' +reload_flag='`$ECHO "X$reload_flag" | $Xsed -e "$delay_single_quote_subst"`' +reload_cmds='`$ECHO "X$reload_cmds" | $Xsed -e "$delay_single_quote_subst"`' +OBJDUMP='`$ECHO "X$OBJDUMP" | $Xsed -e "$delay_single_quote_subst"`' +deplibs_check_method='`$ECHO "X$deplibs_check_method" | $Xsed -e "$delay_single_quote_subst"`' +file_magic_cmd='`$ECHO "X$file_magic_cmd" | $Xsed -e "$delay_single_quote_subst"`' +AR='`$ECHO "X$AR" | $Xsed -e "$delay_single_quote_subst"`' +AR_FLAGS='`$ECHO "X$AR_FLAGS" | $Xsed -e "$delay_single_quote_subst"`' +STRIP='`$ECHO "X$STRIP" | $Xsed -e "$delay_single_quote_subst"`' +RANLIB='`$ECHO "X$RANLIB" | $Xsed -e "$delay_single_quote_subst"`' +old_postinstall_cmds='`$ECHO "X$old_postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`' +old_postuninstall_cmds='`$ECHO "X$old_postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`' +old_archive_cmds='`$ECHO "X$old_archive_cmds" | $Xsed -e "$delay_single_quote_subst"`' +CC='`$ECHO "X$CC" | $Xsed -e "$delay_single_quote_subst"`' +CFLAGS='`$ECHO "X$CFLAGS" | $Xsed -e "$delay_single_quote_subst"`' +compiler='`$ECHO "X$compiler" | $Xsed -e "$delay_single_quote_subst"`' +GCC='`$ECHO "X$GCC" | $Xsed -e "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_pipe='`$ECHO "X$lt_cv_sys_global_symbol_pipe" | $Xsed -e "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_cdecl='`$ECHO "X$lt_cv_sys_global_symbol_to_cdecl" | $Xsed -e "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address" | $Xsed -e "$delay_single_quote_subst"`' +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix='`$ECHO "X$lt_cv_sys_global_symbol_to_c_name_address_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`' +objdir='`$ECHO "X$objdir" | $Xsed -e "$delay_single_quote_subst"`' +SHELL='`$ECHO "X$SHELL" | $Xsed -e "$delay_single_quote_subst"`' +ECHO='`$ECHO "X$ECHO" | $Xsed -e "$delay_single_quote_subst"`' +MAGIC_CMD='`$ECHO "X$MAGIC_CMD" | $Xsed -e "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag='`$ECHO "X$lt_prog_compiler_no_builtin_flag" | $Xsed -e "$delay_single_quote_subst"`' +lt_prog_compiler_wl='`$ECHO "X$lt_prog_compiler_wl" | $Xsed -e "$delay_single_quote_subst"`' +lt_prog_compiler_pic='`$ECHO "X$lt_prog_compiler_pic" | $Xsed -e "$delay_single_quote_subst"`' +lt_prog_compiler_static='`$ECHO "X$lt_prog_compiler_static" | $Xsed -e "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o='`$ECHO "X$lt_cv_prog_compiler_c_o" | $Xsed -e "$delay_single_quote_subst"`' +need_locks='`$ECHO "X$need_locks" | $Xsed -e "$delay_single_quote_subst"`' +DSYMUTIL='`$ECHO "X$DSYMUTIL" | $Xsed -e "$delay_single_quote_subst"`' +NMEDIT='`$ECHO "X$NMEDIT" | $Xsed -e "$delay_single_quote_subst"`' +LIPO='`$ECHO "X$LIPO" | $Xsed -e "$delay_single_quote_subst"`' +OTOOL='`$ECHO "X$OTOOL" | $Xsed -e "$delay_single_quote_subst"`' +OTOOL64='`$ECHO "X$OTOOL64" | $Xsed -e "$delay_single_quote_subst"`' +libext='`$ECHO "X$libext" | $Xsed -e "$delay_single_quote_subst"`' +shrext_cmds='`$ECHO "X$shrext_cmds" | $Xsed -e "$delay_single_quote_subst"`' +extract_expsyms_cmds='`$ECHO "X$extract_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`' +archive_cmds_need_lc='`$ECHO "X$archive_cmds_need_lc" | $Xsed -e "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes='`$ECHO "X$enable_shared_with_static_runtimes" | $Xsed -e "$delay_single_quote_subst"`' +export_dynamic_flag_spec='`$ECHO "X$export_dynamic_flag_spec" | $Xsed -e "$delay_single_quote_subst"`' +whole_archive_flag_spec='`$ECHO "X$whole_archive_flag_spec" | $Xsed -e "$delay_single_quote_subst"`' +compiler_needs_object='`$ECHO "X$compiler_needs_object" | $Xsed -e "$delay_single_quote_subst"`' +old_archive_from_new_cmds='`$ECHO "X$old_archive_from_new_cmds" | $Xsed -e "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds='`$ECHO "X$old_archive_from_expsyms_cmds" | $Xsed -e "$delay_single_quote_subst"`' +archive_cmds='`$ECHO "X$archive_cmds" | $Xsed -e "$delay_single_quote_subst"`' +archive_expsym_cmds='`$ECHO "X$archive_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`' +module_cmds='`$ECHO "X$module_cmds" | $Xsed -e "$delay_single_quote_subst"`' +module_expsym_cmds='`$ECHO "X$module_expsym_cmds" | $Xsed -e "$delay_single_quote_subst"`' +with_gnu_ld='`$ECHO "X$with_gnu_ld" | $Xsed -e "$delay_single_quote_subst"`' +allow_undefined_flag='`$ECHO "X$allow_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`' +no_undefined_flag='`$ECHO "X$no_undefined_flag" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec='`$ECHO "X$hardcode_libdir_flag_spec" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec_ld='`$ECHO "X$hardcode_libdir_flag_spec_ld" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_libdir_separator='`$ECHO "X$hardcode_libdir_separator" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_direct='`$ECHO "X$hardcode_direct" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_direct_absolute='`$ECHO "X$hardcode_direct_absolute" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_minus_L='`$ECHO "X$hardcode_minus_L" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_shlibpath_var='`$ECHO "X$hardcode_shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_automatic='`$ECHO "X$hardcode_automatic" | $Xsed -e "$delay_single_quote_subst"`' +inherit_rpath='`$ECHO "X$inherit_rpath" | $Xsed -e "$delay_single_quote_subst"`' +link_all_deplibs='`$ECHO "X$link_all_deplibs" | $Xsed -e "$delay_single_quote_subst"`' +fix_srcfile_path='`$ECHO "X$fix_srcfile_path" | $Xsed -e "$delay_single_quote_subst"`' +always_export_symbols='`$ECHO "X$always_export_symbols" | $Xsed -e "$delay_single_quote_subst"`' +export_symbols_cmds='`$ECHO "X$export_symbols_cmds" | $Xsed -e "$delay_single_quote_subst"`' +exclude_expsyms='`$ECHO "X$exclude_expsyms" | $Xsed -e "$delay_single_quote_subst"`' +include_expsyms='`$ECHO "X$include_expsyms" | $Xsed -e "$delay_single_quote_subst"`' +prelink_cmds='`$ECHO "X$prelink_cmds" | $Xsed -e "$delay_single_quote_subst"`' +file_list_spec='`$ECHO "X$file_list_spec" | $Xsed -e "$delay_single_quote_subst"`' +variables_saved_for_relink='`$ECHO "X$variables_saved_for_relink" | $Xsed -e "$delay_single_quote_subst"`' +need_lib_prefix='`$ECHO "X$need_lib_prefix" | $Xsed -e "$delay_single_quote_subst"`' +need_version='`$ECHO "X$need_version" | $Xsed -e "$delay_single_quote_subst"`' +version_type='`$ECHO "X$version_type" | $Xsed -e "$delay_single_quote_subst"`' +runpath_var='`$ECHO "X$runpath_var" | $Xsed -e "$delay_single_quote_subst"`' +shlibpath_var='`$ECHO "X$shlibpath_var" | $Xsed -e "$delay_single_quote_subst"`' +shlibpath_overrides_runpath='`$ECHO "X$shlibpath_overrides_runpath" | $Xsed -e "$delay_single_quote_subst"`' +libname_spec='`$ECHO "X$libname_spec" | $Xsed -e "$delay_single_quote_subst"`' +library_names_spec='`$ECHO "X$library_names_spec" | $Xsed -e "$delay_single_quote_subst"`' +soname_spec='`$ECHO "X$soname_spec" | $Xsed -e "$delay_single_quote_subst"`' +postinstall_cmds='`$ECHO "X$postinstall_cmds" | $Xsed -e "$delay_single_quote_subst"`' +postuninstall_cmds='`$ECHO "X$postuninstall_cmds" | $Xsed -e "$delay_single_quote_subst"`' +finish_cmds='`$ECHO "X$finish_cmds" | $Xsed -e "$delay_single_quote_subst"`' +finish_eval='`$ECHO "X$finish_eval" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_into_libs='`$ECHO "X$hardcode_into_libs" | $Xsed -e "$delay_single_quote_subst"`' +sys_lib_search_path_spec='`$ECHO "X$sys_lib_search_path_spec" | $Xsed -e "$delay_single_quote_subst"`' +sys_lib_dlsearch_path_spec='`$ECHO "X$sys_lib_dlsearch_path_spec" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_action='`$ECHO "X$hardcode_action" | $Xsed -e "$delay_single_quote_subst"`' +enable_dlopen='`$ECHO "X$enable_dlopen" | $Xsed -e "$delay_single_quote_subst"`' +enable_dlopen_self='`$ECHO "X$enable_dlopen_self" | $Xsed -e "$delay_single_quote_subst"`' +enable_dlopen_self_static='`$ECHO "X$enable_dlopen_self_static" | $Xsed -e "$delay_single_quote_subst"`' +old_striplib='`$ECHO "X$old_striplib" | $Xsed -e "$delay_single_quote_subst"`' +striplib='`$ECHO "X$striplib" | $Xsed -e "$delay_single_quote_subst"`' +compiler_lib_search_dirs='`$ECHO "X$compiler_lib_search_dirs" | $Xsed -e "$delay_single_quote_subst"`' +predep_objects='`$ECHO "X$predep_objects" | $Xsed -e "$delay_single_quote_subst"`' +postdep_objects='`$ECHO "X$postdep_objects" | $Xsed -e "$delay_single_quote_subst"`' +predeps='`$ECHO "X$predeps" | $Xsed -e "$delay_single_quote_subst"`' +postdeps='`$ECHO "X$postdeps" | $Xsed -e "$delay_single_quote_subst"`' +compiler_lib_search_path='`$ECHO "X$compiler_lib_search_path" | $Xsed -e "$delay_single_quote_subst"`' +LD_CXX='`$ECHO "X$LD_CXX" | $Xsed -e "$delay_single_quote_subst"`' +old_archive_cmds_CXX='`$ECHO "X$old_archive_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' +compiler_CXX='`$ECHO "X$compiler_CXX" | $Xsed -e "$delay_single_quote_subst"`' +GCC_CXX='`$ECHO "X$GCC_CXX" | $Xsed -e "$delay_single_quote_subst"`' +lt_prog_compiler_no_builtin_flag_CXX='`$ECHO "X$lt_prog_compiler_no_builtin_flag_CXX" | $Xsed -e "$delay_single_quote_subst"`' +lt_prog_compiler_wl_CXX='`$ECHO "X$lt_prog_compiler_wl_CXX" | $Xsed -e "$delay_single_quote_subst"`' +lt_prog_compiler_pic_CXX='`$ECHO "X$lt_prog_compiler_pic_CXX" | $Xsed -e "$delay_single_quote_subst"`' +lt_prog_compiler_static_CXX='`$ECHO "X$lt_prog_compiler_static_CXX" | $Xsed -e "$delay_single_quote_subst"`' +lt_cv_prog_compiler_c_o_CXX='`$ECHO "X$lt_cv_prog_compiler_c_o_CXX" | $Xsed -e "$delay_single_quote_subst"`' +archive_cmds_need_lc_CXX='`$ECHO "X$archive_cmds_need_lc_CXX" | $Xsed -e "$delay_single_quote_subst"`' +enable_shared_with_static_runtimes_CXX='`$ECHO "X$enable_shared_with_static_runtimes_CXX" | $Xsed -e "$delay_single_quote_subst"`' +export_dynamic_flag_spec_CXX='`$ECHO "X$export_dynamic_flag_spec_CXX" | $Xsed -e "$delay_single_quote_subst"`' +whole_archive_flag_spec_CXX='`$ECHO "X$whole_archive_flag_spec_CXX" | $Xsed -e "$delay_single_quote_subst"`' +compiler_needs_object_CXX='`$ECHO "X$compiler_needs_object_CXX" | $Xsed -e "$delay_single_quote_subst"`' +old_archive_from_new_cmds_CXX='`$ECHO "X$old_archive_from_new_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' +old_archive_from_expsyms_cmds_CXX='`$ECHO "X$old_archive_from_expsyms_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' +archive_cmds_CXX='`$ECHO "X$archive_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' +archive_expsym_cmds_CXX='`$ECHO "X$archive_expsym_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' +module_cmds_CXX='`$ECHO "X$module_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' +module_expsym_cmds_CXX='`$ECHO "X$module_expsym_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' +with_gnu_ld_CXX='`$ECHO "X$with_gnu_ld_CXX" | $Xsed -e "$delay_single_quote_subst"`' +allow_undefined_flag_CXX='`$ECHO "X$allow_undefined_flag_CXX" | $Xsed -e "$delay_single_quote_subst"`' +no_undefined_flag_CXX='`$ECHO "X$no_undefined_flag_CXX" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec_CXX='`$ECHO "X$hardcode_libdir_flag_spec_CXX" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_libdir_flag_spec_ld_CXX='`$ECHO "X$hardcode_libdir_flag_spec_ld_CXX" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_libdir_separator_CXX='`$ECHO "X$hardcode_libdir_separator_CXX" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_direct_CXX='`$ECHO "X$hardcode_direct_CXX" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_direct_absolute_CXX='`$ECHO "X$hardcode_direct_absolute_CXX" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_minus_L_CXX='`$ECHO "X$hardcode_minus_L_CXX" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_shlibpath_var_CXX='`$ECHO "X$hardcode_shlibpath_var_CXX" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_automatic_CXX='`$ECHO "X$hardcode_automatic_CXX" | $Xsed -e "$delay_single_quote_subst"`' +inherit_rpath_CXX='`$ECHO "X$inherit_rpath_CXX" | $Xsed -e "$delay_single_quote_subst"`' +link_all_deplibs_CXX='`$ECHO "X$link_all_deplibs_CXX" | $Xsed -e "$delay_single_quote_subst"`' +fix_srcfile_path_CXX='`$ECHO "X$fix_srcfile_path_CXX" | $Xsed -e "$delay_single_quote_subst"`' +always_export_symbols_CXX='`$ECHO "X$always_export_symbols_CXX" | $Xsed -e "$delay_single_quote_subst"`' +export_symbols_cmds_CXX='`$ECHO "X$export_symbols_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' +exclude_expsyms_CXX='`$ECHO "X$exclude_expsyms_CXX" | $Xsed -e "$delay_single_quote_subst"`' +include_expsyms_CXX='`$ECHO "X$include_expsyms_CXX" | $Xsed -e "$delay_single_quote_subst"`' +prelink_cmds_CXX='`$ECHO "X$prelink_cmds_CXX" | $Xsed -e "$delay_single_quote_subst"`' +file_list_spec_CXX='`$ECHO "X$file_list_spec_CXX" | $Xsed -e "$delay_single_quote_subst"`' +hardcode_action_CXX='`$ECHO "X$hardcode_action_CXX" | $Xsed -e "$delay_single_quote_subst"`' +compiler_lib_search_dirs_CXX='`$ECHO "X$compiler_lib_search_dirs_CXX" | $Xsed -e "$delay_single_quote_subst"`' +predep_objects_CXX='`$ECHO "X$predep_objects_CXX" | $Xsed -e "$delay_single_quote_subst"`' +postdep_objects_CXX='`$ECHO "X$postdep_objects_CXX" | $Xsed -e "$delay_single_quote_subst"`' +predeps_CXX='`$ECHO "X$predeps_CXX" | $Xsed -e "$delay_single_quote_subst"`' +postdeps_CXX='`$ECHO "X$postdeps_CXX" | $Xsed -e "$delay_single_quote_subst"`' +compiler_lib_search_path_CXX='`$ECHO "X$compiler_lib_search_path_CXX" | $Xsed -e "$delay_single_quote_subst"`' + +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# Quote evaled strings. +for var in SED \ +GREP \ +EGREP \ +FGREP \ +LD \ +NM \ +LN_S \ +lt_SP2NL \ +lt_NL2SP \ +reload_flag \ +OBJDUMP \ +deplibs_check_method \ +file_magic_cmd \ +AR \ +AR_FLAGS \ +STRIP \ +RANLIB \ +CC \ +CFLAGS \ +compiler \ +lt_cv_sys_global_symbol_pipe \ +lt_cv_sys_global_symbol_to_cdecl \ +lt_cv_sys_global_symbol_to_c_name_address \ +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix \ +SHELL \ +ECHO \ +lt_prog_compiler_no_builtin_flag \ +lt_prog_compiler_wl \ +lt_prog_compiler_pic \ +lt_prog_compiler_static \ +lt_cv_prog_compiler_c_o \ +need_locks \ +DSYMUTIL \ +NMEDIT \ +LIPO \ +OTOOL \ +OTOOL64 \ +shrext_cmds \ +export_dynamic_flag_spec \ +whole_archive_flag_spec \ +compiler_needs_object \ +with_gnu_ld \ +allow_undefined_flag \ +no_undefined_flag \ +hardcode_libdir_flag_spec \ +hardcode_libdir_flag_spec_ld \ +hardcode_libdir_separator \ +fix_srcfile_path \ +exclude_expsyms \ +include_expsyms \ +file_list_spec \ +variables_saved_for_relink \ +libname_spec \ +library_names_spec \ +soname_spec \ +finish_eval \ +old_striplib \ +striplib \ +compiler_lib_search_dirs \ +predep_objects \ +postdep_objects \ +predeps \ +postdeps \ +compiler_lib_search_path \ +LD_CXX \ +compiler_CXX \ +lt_prog_compiler_no_builtin_flag_CXX \ +lt_prog_compiler_wl_CXX \ +lt_prog_compiler_pic_CXX \ +lt_prog_compiler_static_CXX \ +lt_cv_prog_compiler_c_o_CXX \ +export_dynamic_flag_spec_CXX \ +whole_archive_flag_spec_CXX \ +compiler_needs_object_CXX \ +with_gnu_ld_CXX \ +allow_undefined_flag_CXX \ +no_undefined_flag_CXX \ +hardcode_libdir_flag_spec_CXX \ +hardcode_libdir_flag_spec_ld_CXX \ +hardcode_libdir_separator_CXX \ +fix_srcfile_path_CXX \ +exclude_expsyms_CXX \ +include_expsyms_CXX \ +file_list_spec_CXX \ +compiler_lib_search_dirs_CXX \ +predep_objects_CXX \ +postdep_objects_CXX \ +predeps_CXX \ +postdeps_CXX \ +compiler_lib_search_path_CXX; do + case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done -# Whether dlopen of programs is supported. -dlopen_self=$enable_dlopen_self +# Double-quote double-evaled strings. +for var in reload_cmds \ +old_postinstall_cmds \ +old_postuninstall_cmds \ +old_archive_cmds \ +extract_expsyms_cmds \ +old_archive_from_new_cmds \ +old_archive_from_expsyms_cmds \ +archive_cmds \ +archive_expsym_cmds \ +module_cmds \ +module_expsym_cmds \ +export_symbols_cmds \ +prelink_cmds \ +postinstall_cmds \ +postuninstall_cmds \ +finish_cmds \ +sys_lib_search_path_spec \ +sys_lib_dlsearch_path_spec \ +old_archive_cmds_CXX \ +old_archive_from_new_cmds_CXX \ +old_archive_from_expsyms_cmds_CXX \ +archive_cmds_CXX \ +archive_expsym_cmds_CXX \ +module_cmds_CXX \ +module_expsym_cmds_CXX \ +export_symbols_cmds_CXX \ +prelink_cmds_CXX; do + case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in + *[\\\\\\\`\\"\\\$]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done -# Whether dlopen of statically linked programs is supported. -dlopen_self_static=$enable_dlopen_self_static +# Fix-up fallback echo if it was mangled by the above quoting rules. +case \$lt_ECHO in +*'\\\$0 --fallback-echo"') lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\$0 --fallback-echo"\$/\$0 --fallback-echo"/'\` + ;; +esac -# Compiler flag to prevent dynamic linking. -link_static_flag=$lt_lt_prog_compiler_static_RC +ac_aux_dir='$ac_aux_dir' +xsi_shell='$xsi_shell' +lt_shell_append='$lt_shell_append' -# Compiler flag to turn off builtin functions. -no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_RC +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi -# Compiler flag to allow reflexive dlopens. -export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_RC -# Compiler flag to generate shared objects directly from archives. -whole_archive_flag_spec=$lt_whole_archive_flag_spec_RC + PACKAGE='$PACKAGE' + VERSION='$VERSION' + TIMESTAMP='$TIMESTAMP' + RM='$RM' + ofile='$ofile' -# Compiler flag to generate thread-safe objects. -thread_safe_flag_spec=$lt_thread_safe_flag_spec_RC -# Library versioning type. -version_type=$version_type -# Format of library name prefix. -libname_spec=$lt_libname_spec -# List of archive names. First name is the real one, the rest are links. -# The last name is the one that the linker finds with -lNAME. -library_names_spec=$lt_library_names_spec -# The coded name of the library, if different from the real name. -soname_spec=$lt_soname_spec -# Commands used to build and install an old-style archive. -RANLIB=$lt_RANLIB -old_archive_cmds=$lt_old_archive_cmds_RC -old_postinstall_cmds=$lt_old_postinstall_cmds -old_postuninstall_cmds=$lt_old_postuninstall_cmds +_ACEOF -# Create an old-style archive from a shared archive. -old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_RC +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 -# Create a temporary old-style archive to link instead of a shared archive. -old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_RC +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "src/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;; + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "libtool") CONFIG_COMMANDS="$CONFIG_COMMANDS libtool" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; -# Commands used to build and install a shared archive. -archive_cmds=$lt_archive_cmds_RC -archive_expsym_cmds=$lt_archive_expsym_cmds_RC -postinstall_cmds=$lt_postinstall_cmds -postuninstall_cmds=$lt_postuninstall_cmds + *) as_fn_error "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done -# Commands used to build a loadable module (assumed same as above if empty) -module_cmds=$lt_module_cmds_RC -module_expsym_cmds=$lt_module_expsym_cmds_RC -# Commands to strip libraries. -old_striplib=$lt_old_striplib -striplib=$lt_striplib +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi -# Dependencies to place before the objects being linked to create a -# shared library. -predep_objects=$lt_predep_objects_RC - -# Dependencies to place after the objects being linked to create a -# shared library. -postdep_objects=$lt_postdep_objects_RC - -# Dependencies to place before the objects being linked to create a -# shared library. -predeps=$lt_predeps_RC - -# Dependencies to place after the objects being linked to create a -# shared library. -postdeps=$lt_postdeps_RC +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= + trap 'exit_status=$? + { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. -# The library search path used internally by the compiler when linking -# a shared library. -compiler_lib_search_path=$lt_compiler_lib_search_path_RC +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -n "$tmp" && test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error "cannot create a temporary directory in ." "$LINENO" 5 -# Method to check whether dependent libraries are shared objects. -deplibs_check_method=$lt_deplibs_check_method +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then -# Command to use when deplibs_check_method == file_magic. -file_magic_cmd=$lt_file_magic_cmd -# Flag that allows shared libraries with undefined symbols to be built. -allow_undefined_flag=$lt_allow_undefined_flag_RC +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\r' +else + ac_cs_awk_cr=$ac_cr +fi -# Flag that forces no undefined symbols. -no_undefined_flag=$lt_no_undefined_flag_RC +echo 'BEGIN {' >"$tmp/subs1.awk" && +_ACEOF -# Commands used to finish a libtool library installation in a directory. -finish_cmds=$lt_finish_cmds -# Same as above, but a single script fragment to be evaled but not shown. -finish_eval=$lt_finish_eval +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '$'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 -# Take the output of nm and produce a listing of raw symbols and C names. -global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh -# Transform the output of nm in a proper C declaration -global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" -# Transform the output of nm in a C name address pair -global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } -# This is the shared library runtime path variable. -runpath_var=$runpath_var + print line +} -# This is the shared library path variable. -shlibpath_var=$shlibpath_var +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$tmp/subs1.awk" > "$tmp/subs.awk" \ + || as_fn_error "could not setup config files machinery" "$LINENO" 5 +_ACEOF -# Is shlibpath searched before the hard-coded library search path? -shlibpath_overrides_runpath=$shlibpath_overrides_runpath +# VPATH may cause trouble with some makes, so we remove $(srcdir), +# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=/{ +s/:*\$(srcdir):*/:/ +s/:*\${srcdir}:*/:/ +s/:*@srcdir@:*/:/ +s/^\([^=]*=[ ]*\):*/\1/ +s/:*$// +s/^[^=]*=[ ]*$// +}' +fi -# How to hardcode a shared library path into an executable. -hardcode_action=$hardcode_action_RC +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" -# Whether we should hardcode library paths into libraries. -hardcode_into_libs=$hardcode_into_libs +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF -# Flag to hardcode \$libdir into a binary during linking. -# This must work even if \$libdir does not exist. -hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_RC +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. -# If ld is used when linking, flag to hardcode \$libdir into -# a binary during linking. This must work even if \$libdir does -# not exist. -hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_RC - -# Whether we need a single -rpath flag with a separated argument. -hardcode_libdir_separator=$lt_hardcode_libdir_separator_RC - -# Set to yes if using DIR/libNAME${shared_ext} during linking hardcodes DIR into the -# resulting binary. -hardcode_direct=$hardcode_direct_RC - -# Set to yes if using the -LDIR flag during linking hardcodes DIR into the -# resulting binary. -hardcode_minus_L=$hardcode_minus_L_RC - -# Set to yes if using SHLIBPATH_VAR=DIR during linking hardcodes DIR into -# the resulting binary. -hardcode_shlibpath_var=$hardcode_shlibpath_var_RC - -# Set to yes if building a shared library automatically hardcodes DIR into the library -# and all subsequent libraries and executables linked against it. -hardcode_automatic=$hardcode_automatic_RC +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_t=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_t"; then + break + elif $ac_last_try; then + as_fn_error "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done -# Variables whose values should be saved in libtool wrapper scripts and -# restored at relink time. -variables_saved_for_relink="$variables_saved_for_relink" +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. -# Whether libtool must link a program against all its dependency libraries. -link_all_deplibs=$link_all_deplibs_RC +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" -# Compile-time system search path for libraries -sys_lib_search_path_spec=$lt_sys_lib_search_path_spec -# Run-time system search path for libraries -sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift -# Fix the shell variable \$srcfile for the compiler. -fix_srcfile_path="$fix_srcfile_path_RC" + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done -# Set to yes if exported symbols are required. -always_export_symbols=$always_export_symbols_RC + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac -# The commands to list exported symbols. -export_symbols_cmds=$lt_export_symbols_cmds_RC + case $ac_tag in + *:-:* | *:-) cat >"$tmp/stdin" \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac -# The commands to extract the exported symbol list from a shared archive. -extract_expsyms_cmds=$lt_extract_expsyms_cmds + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. -# Symbols that should not be listed in the preloaded symbols. -exclude_expsyms=$lt_exclude_expsyms_RC +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix -# Symbols that must always be exported. -include_expsyms=$lt_include_expsyms_RC +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix -# ### END LIBTOOL TAG CONFIG: $tagname -__EOF__ + case $ac_mode in + :F) + # + # CONFIG_FILE + # + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF -else - # If there is no Makefile yet, we rely on a make rule to execute - # `config.status --recheck' to rerun these tests and create the - # libtool script then. - ltmain_in=`echo $ltmain | sed -e 's/\.sh$/.in/'` - if test -f "$ltmain_in"; then - test -f Makefile && make "$ltmain" - fi -fi +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$tmp/subs.awk" >$tmp/out \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 -ac_ext=c -ac_cpp='$CPP $CPPFLAGS' -ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' -ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' -ac_compiler_gnu=$ac_cv_c_compiler_gnu +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined." >&2;} -CC="$lt_save_CC" + rm -f "$tmp/stdin" + case $ac_file in + -) cat "$tmp/out" && rm -f "$tmp/out";; + *) rm -f "$ac_file" && mv "$tmp/out" "$ac_file";; + esac \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" + } >"$tmp/config.h" \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$tmp/config.h" "$ac_file" \ + || as_fn_error "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; - ;; + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac - *) - { { echo "$as_me:$LINENO: error: Unsupported tag name: $tagname" >&5 -echo "$as_me: error: Unsupported tag name: $tagname" >&2;} - { (exit 1); exit 1; }; } - ;; - esac - # Append the new tag name to the list of available tags. - if test -n "$tagname" ; then - available_tags="$available_tags $tagname" - fi + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Autoconf 2.62 quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + case $CONFIG_FILES in + *\'*) eval set x "$CONFIG_FILES" ;; + *) set x $CONFIG_FILES ;; + esac + shift + for mf + do + # Strip MF so we end up with the name of the file. + mf=`echo "$mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile or not. + # We used to match only the files named `Makefile.in', but + # some people rename them; so instead we look at the file content. + # Grep'ing the first line is not enough: some people post-process + # each Makefile.in and add a new line on top of each file to say so. + # Grep'ing the whole file is not good either: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + if sed -n 's,^#.*generated by automake.*,X,p' "$mf" | grep X >/dev/null 2>&1; then + dirpart=`$as_dirname -- "$mf" || +$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$mf" : 'X\(//\)[^/]' \| \ + X"$mf" : 'X\(//\)$' \| \ + X"$mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + else + continue fi + # Extract the definition of DEPDIR, am__include, and am__quote + # from the Makefile without running `make'. + DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` + test -z "$DEPDIR" && continue + am__include=`sed -n 's/^am__include = //p' < "$mf"` + test -z "am__include" && continue + am__quote=`sed -n 's/^am__quote = //p' < "$mf"` + # When using ansi2knr, U may be empty or an underscore; expand it + U=`sed -n 's/^U = //p' < "$mf"` + # Find all dependency output files, they are included files with + # $(DEPDIR) in their names. We invoke sed twice because it is the + # simplest approach to changing $(DEPDIR) to its actual value in the + # expansion. + for file in `sed -n " + s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ + sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do + # Make sure the directory exists. + test -f "$dirpart/$file" && continue + fdir=`$as_dirname -- "$file" || +$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$file" : 'X\(//\)[^/]' \| \ + X"$file" : 'X\(//\)$' \| \ + X"$file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir=$dirpart/$fdir; as_fn_mkdir_p + # echo "creating $dirpart/$file" + echo '# dummy' > "$dirpart/$file" + done done - IFS="$lt_save_ifs" - - # Now substitute the updated list of available tags. - if eval "sed -e 's/^available_tags=.*\$/available_tags=\"$available_tags\"/' \"$ofile\" > \"${ofile}T\""; then - mv "${ofile}T" "$ofile" - chmod +x "$ofile" - else - rm -f "${ofile}T" - { { echo "$as_me:$LINENO: error: unable to update list of available tagged configurations." >&5 -echo "$as_me: error: unable to update list of available tagged configurations." >&2;} - { (exit 1); exit 1; }; } - fi -fi - - +} + ;; + "libtool":C) -# This can be used to rebuild libtool when needed -LIBTOOL_DEPS="$ac_aux_dir/ltmain.sh" + # See if we are running on zsh, and set the options which allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi -# Always use our own libtool. -LIBTOOL='$(SHELL) $(top_builddir)/libtool' + cfgfile="${ofile}T" + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" -# Prevent multiple expansion + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008 Free Software Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# GNU Libtool is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool 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 the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# The names of the tagged configurations supported by this script. +available_tags="CXX " +# ### BEGIN LIBTOOL CONFIG +# Which release of libtool.m4 was used? +macro_version=$macro_version +macro_revision=$macro_revision +# Whether or not to build shared libraries. +build_libtool_libs=$enable_shared +# Whether or not to build static libraries. +build_old_libs=$enable_static +# What type of objects to build. +pic_mode=$pic_mode +# Whether or not to optimize for fast installation. +fast_install=$enable_fast_install +# The host system. +host_alias=$host_alias +host=$host +host_os=$host_os +# The build system. +build_alias=$build_alias +build=$build +build_os=$build_os +# A sed program that does not truncate output. +SED=$lt_SED +# Sed that helps us avoid accidentally triggering echo(1) options like -n. +Xsed="\$SED -e 1s/^X//" +# A grep program that handles long lines. +GREP=$lt_GREP +# An ERE matcher. +EGREP=$lt_EGREP +# A literal string matcher. +FGREP=$lt_FGREP +# A BSD- or MS-compatible name lister. +NM=$lt_NM +# Whether we need soft or hard links. +LN_S=$lt_LN_S +# What is the maximum length of a command? +max_cmd_len=$max_cmd_len +# Object file suffix (normally "o"). +objext=$ac_objext +# Executable file suffix (normally ""). +exeext=$exeext -{ echo "$as_me:$LINENO: checking for ANSI C header files" >&5 -echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; } -if test "${ac_cv_header_stdc+set}" = set; then - echo $ECHO_N "(cached) $ECHO_C" >&6 -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#include -#include +# whether the shell understands "unset". +lt_unset=$lt_unset -int -main () -{ +# turn spaces into newlines. +SP2NL=$lt_lt_SP2NL - ; - return 0; -} -_ACEOF -rm -f conftest.$ac_objext -if { (ac_try="$ac_compile" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_compile") 2>conftest.er1 - ac_status=$? - grep -v '^ *+' conftest.er1 >conftest.err - rm -f conftest.er1 - cat conftest.err >&5 - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { - test -z "$ac_c_werror_flag" || - test ! -s conftest.err - } && test -s conftest.$ac_objext; then - ac_cv_header_stdc=yes -else - echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +# turn newlines into spaces. +NL2SP=$lt_lt_NL2SP - ac_cv_header_stdc=no -fi +# How to create reloadable object files. +reload_flag=$lt_reload_flag +reload_cmds=$lt_reload_cmds -rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +# An object symbol dumper. +OBJDUMP=$lt_OBJDUMP -if test $ac_cv_header_stdc = yes; then - # SunOS 4.x string.h does not declare mem*, contrary to ANSI. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include +# Method to check whether dependent libraries are shared objects. +deplibs_check_method=$lt_deplibs_check_method -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "memchr" >/dev/null 2>&1; then - : -else - ac_cv_header_stdc=no -fi -rm -f conftest* +# Command to use when deplibs_check_method == "file_magic". +file_magic_cmd=$lt_file_magic_cmd -fi +# The archiver. +AR=$lt_AR +AR_FLAGS=$lt_AR_FLAGS -if test $ac_cv_header_stdc = yes; then - # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include +# A symbol stripping program. +STRIP=$lt_STRIP -_ACEOF -if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | - $EGREP "free" >/dev/null 2>&1; then - : -else - ac_cv_header_stdc=no -fi -rm -f conftest* +# Commands used to install an old-style archive. +RANLIB=$lt_RANLIB +old_postinstall_cmds=$lt_old_postinstall_cmds +old_postuninstall_cmds=$lt_old_postuninstall_cmds -fi +# A C compiler. +LTCC=$lt_CC -if test $ac_cv_header_stdc = yes; then - # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. - if test "$cross_compiling" = yes; then - : -else - cat >conftest.$ac_ext <<_ACEOF -/* confdefs.h. */ -_ACEOF -cat confdefs.h >>conftest.$ac_ext -cat >>conftest.$ac_ext <<_ACEOF -/* end confdefs.h. */ -#include -#include -#if ((' ' & 0x0FF) == 0x020) -# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') -# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) -#else -# define ISLOWER(c) \ - (('a' <= (c) && (c) <= 'i') \ - || ('j' <= (c) && (c) <= 'r') \ - || ('s' <= (c) && (c) <= 'z')) -# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) -#endif +# LTCC compiler flags. +LTCFLAGS=$lt_CFLAGS -#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) -int -main () -{ - int i; - for (i = 0; i < 256; i++) - if (XOR (islower (i), ISLOWER (i)) - || toupper (i) != TOUPPER (i)) - return 2; - return 0; -} -_ACEOF -rm -f conftest$ac_exeext -if { (ac_try="$ac_link" -case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_link") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); } && { ac_try='./conftest$ac_exeext' - { (case "(($ac_try" in - *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; - *) ac_try_echo=$ac_try;; -esac -eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5 - (eval "$ac_try") 2>&5 - ac_status=$? - echo "$as_me:$LINENO: \$? = $ac_status" >&5 - (exit $ac_status); }; }; then - : -else - echo "$as_me: program exited with status $ac_status" >&5 -echo "$as_me: failed program was:" >&5 -sed 's/^/| /' conftest.$ac_ext >&5 +# Take the output of nm and produce a listing of raw symbols and C names. +global_symbol_pipe=$lt_lt_cv_sys_global_symbol_pipe -( exit $ac_status ) -ac_cv_header_stdc=no -fi -rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext -fi +# Transform the output of nm in a proper C declaration. +global_symbol_to_cdecl=$lt_lt_cv_sys_global_symbol_to_cdecl +# Transform the output of nm in a C name address pair. +global_symbol_to_c_name_address=$lt_lt_cv_sys_global_symbol_to_c_name_address -fi -fi -{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5 -echo "${ECHO_T}$ac_cv_header_stdc" >&6; } -if test $ac_cv_header_stdc = yes; then +# Transform the output of nm in a C name address pair when lib prefix is needed. +global_symbol_to_c_name_address_lib_prefix=$lt_lt_cv_sys_global_symbol_to_c_name_address_lib_prefix -cat >>confdefs.h <<\_ACEOF -#define STDC_HEADERS 1 -_ACEOF +# The name of the directory that contains temporary libtool files. +objdir=$objdir -fi +# Shell to use when invoking shell scripts. +SHELL=$lt_SHELL +# An echo program that does not interpret backslashes. +ECHO=$lt_ECHO -# Check whether --enable-selftest was given. -if test "${enable_selftest+set}" = set; then - enableval=$enable_selftest; case "${enableval}" in - yes) - selftest=true - ;; - no) - selftest=false - ;; - *) - { { echo "$as_me:$LINENO: error: bad value ${enableval} for --enable-selftest" >&5 -echo "$as_me: error: bad value ${enableval} for --enable-selftest" >&2;} - { (exit 1); exit 1; }; } - ;; - esac -else - selftest=false -fi +# Used to examine libraries when file_magic_cmd begins with "file". +MAGIC_CMD=$MAGIC_CMD - if test x$selftest = xtrue; then - SELFTEST_TRUE= - SELFTEST_FALSE='#' -else - SELFTEST_TRUE='#' - SELFTEST_FALSE= -fi +# Must we lock files when doing compilation? +need_locks=$lt_need_locks +# Tool to manipulate archived DWARF debug symbol files on Mac OS X. +DSYMUTIL=$lt_DSYMUTIL -ac_config_files="$ac_config_files Makefile" +# Tool to change global to local symbols on Mac OS X. +NMEDIT=$lt_NMEDIT -cat >confcache <<\_ACEOF -# This file is a shell script that caches the results of configure -# tests run on this system so they can be shared between configure -# scripts and configure runs, see configure's option --config-cache. -# It is not useful on other systems. If it contains results you don't -# want to keep, you may remove or edit it. -# -# config.status only pays attention to the cache file if you give it -# the --recheck option to rerun configure. -# -# `ac_cv_env_foo' variables (set or unset) will be overridden when -# loading this file, other *unset* `ac_cv_foo' will be assigned the -# following values. +# Tool to manipulate fat objects and archives on Mac OS X. +LIPO=$lt_LIPO -_ACEOF +# ldd/readelf like tool for Mach-O binaries on Mac OS X. +OTOOL=$lt_OTOOL -# The following way of writing the cache mishandles newlines in values, -# but we know of no workaround that is simple, portable, and efficient. -# So, we kill variables containing newlines. -# Ultrix sh set writes to stderr and can't be redirected directly, -# and sets the high bit in the cache file unless we assign to the vars. -( - for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do - eval ac_val=\$$ac_var - case $ac_val in #( - *${as_nl}*) - case $ac_var in #( - *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5 -echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;; - esac - case $ac_var in #( - _ | IFS | as_nl) ;; #( - *) $as_unset $ac_var ;; - esac ;; - esac - done +# ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4. +OTOOL64=$lt_OTOOL64 - (set) 2>&1 | - case $as_nl`(ac_space=' '; set) 2>&1` in #( - *${as_nl}ac_space=\ *) - # `set' does not quote correctly, so add quotes (double-quote - # substitution turns \\\\ into \\, and sed turns \\ into \). - sed -n \ - "s/'/'\\\\''/g; - s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" - ;; #( - *) - # `set' quotes correctly as required by POSIX, so do not add quotes. - sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" - ;; - esac | - sort -) | - sed ' - /^ac_cv_env_/b end - t clear - :clear - s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ - t end - s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ - :end' >>confcache -if diff "$cache_file" confcache >/dev/null 2>&1; then :; else - if test -w "$cache_file"; then - test "x$cache_file" != "x/dev/null" && - { echo "$as_me:$LINENO: updating cache $cache_file" >&5 -echo "$as_me: updating cache $cache_file" >&6;} - cat confcache >$cache_file - else - { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5 -echo "$as_me: not updating unwritable cache $cache_file" >&6;} - fi -fi -rm -f confcache +# Old archive suffix (normally "a"). +libext=$libext -test "x$prefix" = xNONE && prefix=$ac_default_prefix -# Let make expand exec_prefix. -test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' +# Shared library suffix (normally ".so"). +shrext_cmds=$lt_shrext_cmds -DEFS=-DHAVE_CONFIG_H +# The commands to extract the exported symbol list from a shared archive. +extract_expsyms_cmds=$lt_extract_expsyms_cmds -ac_libobjs= -ac_ltlibobjs= -for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue - # 1. Remove the extension, and $U if already installed. - ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' - ac_i=`echo "$ac_i" | sed "$ac_script"` - # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR - # will be set to the directory where LIBOBJS objects are built. - ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext" - ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo' -done -LIBOBJS=$ac_libobjs +# Variables whose values should be saved in libtool wrapper scripts and +# restored at link time. +variables_saved_for_relink=$lt_variables_saved_for_relink -LTLIBOBJS=$ac_ltlibobjs +# Do we need the "lib" prefix for modules? +need_lib_prefix=$need_lib_prefix +# Do we need a version for libraries? +need_version=$need_version -if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then - { { echo "$as_me:$LINENO: error: conditional \"AMDEP\" was never defined. -Usually this means the macro was only invoked conditionally." >&5 -echo "$as_me: error: conditional \"AMDEP\" was never defined. -Usually this means the macro was only invoked conditionally." >&2;} - { (exit 1); exit 1; }; } -fi -if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then - { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCC\" was never defined. -Usually this means the macro was only invoked conditionally." >&5 -echo "$as_me: error: conditional \"am__fastdepCC\" was never defined. -Usually this means the macro was only invoked conditionally." >&2;} - { (exit 1); exit 1; }; } -fi -if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then - { { echo "$as_me:$LINENO: error: conditional \"am__fastdepCXX\" was never defined. -Usually this means the macro was only invoked conditionally." >&5 -echo "$as_me: error: conditional \"am__fastdepCXX\" was never defined. -Usually this means the macro was only invoked conditionally." >&2;} - { (exit 1); exit 1; }; } -fi -if test -z "${SELFTEST_TRUE}" && test -z "${SELFTEST_FALSE}"; then - { { echo "$as_me:$LINENO: error: conditional \"SELFTEST\" was never defined. -Usually this means the macro was only invoked conditionally." >&5 -echo "$as_me: error: conditional \"SELFTEST\" was never defined. -Usually this means the macro was only invoked conditionally." >&2;} - { (exit 1); exit 1; }; } -fi +# Library versioning type. +version_type=$version_type -: ${CONFIG_STATUS=./config.status} -ac_clean_files_save=$ac_clean_files -ac_clean_files="$ac_clean_files $CONFIG_STATUS" -{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5 -echo "$as_me: creating $CONFIG_STATUS" >&6;} -cat >$CONFIG_STATUS <<_ACEOF -#! $SHELL -# Generated by $as_me. -# Run this file to recreate the current configuration. -# Compiler output produced by configure, useful for debugging -# configure, is in config.log if it exists. +# Shared library runtime path variable. +runpath_var=$runpath_var -debug=false -ac_cs_recheck=false -ac_cs_silent=false -SHELL=\${CONFIG_SHELL-$SHELL} -_ACEOF +# Shared library path variable. +shlibpath_var=$shlibpath_var -cat >>$CONFIG_STATUS <<\_ACEOF -## --------------------- ## -## M4sh Initialization. ## -## --------------------- ## +# Is shlibpath searched before the hard-coded library search path? +shlibpath_overrides_runpath=$shlibpath_overrides_runpath -# Be more Bourne compatible -DUALCASE=1; export DUALCASE # for MKS sh -if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then - emulate sh - NULLCMD=: - # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which - # is contrary to our usage. Disable this feature. - alias -g '${1+"$@"}'='"$@"' - setopt NO_GLOB_SUBST -else - case `(set -o) 2>/dev/null` in - *posix*) set -o posix ;; -esac +# Format of library name prefix. +libname_spec=$lt_libname_spec -fi +# List of archive names. First name is the real one, the rest are links. +# The last name is the one that the linker finds with -lNAME +library_names_spec=$lt_library_names_spec +# The coded name of the library, if different from the real name. +soname_spec=$lt_soname_spec +# Command to use after installation of a shared archive. +postinstall_cmds=$lt_postinstall_cmds +# Command to use after uninstallation of a shared archive. +postuninstall_cmds=$lt_postuninstall_cmds -# PATH needs CR -# Avoid depending upon Character Ranges. -as_cr_letters='abcdefghijklmnopqrstuvwxyz' -as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' -as_cr_Letters=$as_cr_letters$as_cr_LETTERS -as_cr_digits='0123456789' -as_cr_alnum=$as_cr_Letters$as_cr_digits +# Commands used to finish a libtool library installation in a directory. +finish_cmds=$lt_finish_cmds -# The user is always right. -if test "${PATH_SEPARATOR+set}" != set; then - echo "#! /bin/sh" >conf$$.sh - echo "exit 0" >>conf$$.sh - chmod +x conf$$.sh - if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then - PATH_SEPARATOR=';' - else - PATH_SEPARATOR=: - fi - rm -f conf$$.sh -fi +# As "finish_cmds", except a single script fragment to be evaled but +# not shown. +finish_eval=$lt_finish_eval -# Support unset when possible. -if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then - as_unset=unset -else - as_unset=false -fi +# Whether we should hardcode library paths into libraries. +hardcode_into_libs=$hardcode_into_libs +# Compile-time system search path for libraries. +sys_lib_search_path_spec=$lt_sys_lib_search_path_spec -# IFS -# We need space, tab and new line, in precisely that order. Quoting is -# there to prevent editors from complaining about space-tab. -# (If _AS_PATH_WALK were called with IFS unset, it would disable word -# splitting by setting IFS to empty value.) -as_nl=' -' -IFS=" "" $as_nl" +# Run-time system search path for libraries. +sys_lib_dlsearch_path_spec=$lt_sys_lib_dlsearch_path_spec -# Find who we are. Look in the path if we contain no directory separator. -case $0 in - *[\\/]* ) as_myself=$0 ;; - *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH -do - IFS=$as_save_IFS - test -z "$as_dir" && as_dir=. - test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break -done -IFS=$as_save_IFS +# Whether dlopen is supported. +dlopen_support=$enable_dlopen - ;; -esac -# We did not find ourselves, most probably we were run as `sh COMMAND' -# in which case we are not to be found in the path. -if test "x$as_myself" = x; then - as_myself=$0 -fi -if test ! -f "$as_myself"; then - echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 - { (exit 1); exit 1; } -fi +# Whether dlopen of programs is supported. +dlopen_self=$enable_dlopen_self -# Work around bugs in pre-3.0 UWIN ksh. -for as_var in ENV MAIL MAILPATH -do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var -done -PS1='$ ' -PS2='> ' -PS4='+ ' +# Whether dlopen of statically linked programs is supported. +dlopen_self_static=$enable_dlopen_self_static -# NLS nuisances. -for as_var in \ - LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \ - LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \ - LC_TELEPHONE LC_TIME -do - if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then - eval $as_var=C; export $as_var - else - ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var - fi -done +# Commands to strip libraries. +old_striplib=$lt_old_striplib +striplib=$lt_striplib -# Required to use basename. -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi -if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then - as_basename=basename -else - as_basename=false -fi +# The linker used to build libraries. +LD=$lt_LD +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds -# Name of the executable. -as_me=`$as_basename -- "$0" || -$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ - X"$0" : 'X\(//\)$' \| \ - X"$0" : 'X\(/\)' \| . 2>/dev/null || -echo X/"$0" | - sed '/^.*\/\([^/][^/]*\)\/*$/{ - s//\1/ - q - } - /^X\/\(\/\/\)$/{ - s//\1/ - q - } - /^X\/\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` +# A language specific compiler. +CC=$lt_compiler + +# Is the compiler the GNU compiler? +with_gcc=$GCC + +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag + +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl + +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic + +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static + +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o + +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc -# CDPATH. -$as_unset CDPATH +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec - as_lineno_1=$LINENO - as_lineno_2=$LINENO - test "x$as_lineno_1" != "x$as_lineno_2" && - test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || { - - # Create $as_me.lineno as a copy of $as_myself, but with $LINENO - # uniformly replaced by the line number. The first 'sed' inserts a - # line-number line after each line using $LINENO; the second 'sed' - # does the real work. The second script uses 'N' to pair each - # line-number line with the line containing $LINENO, and appends - # trailing '-' during substitution so that $LINENO is not a special - # case at line end. - # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the - # scripts with optimization help from Paolo Bonzini. Blame Lee - # E. McMahon (1931-1989) for sed's syntax. :-) - sed -n ' - p - /[$]LINENO/= - ' <$as_myself | - sed ' - s/[$]LINENO.*/&-/ - t lineno - b - :lineno - N - :loop - s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ - t loop - s/-\n.*// - ' >$as_me.lineno && - chmod +x "$as_me.lineno" || - { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2 - { (exit 1); exit 1; }; } +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object - # Don't try to exec as it changes $[0], causing all sort of problems - # (the dirname of $[0] is not the place where we might find the - # original and so on. Autoconf is especially sensitive to this). - . "./$as_me.lineno" - # Exit status is that of the last command. - exit -} +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds -if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then - as_dirname=dirname -else - as_dirname=false -fi +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds +archive_expsym_cmds=$lt_archive_expsym_cmds -ECHO_C= ECHO_N= ECHO_T= -case `echo -n x` in --n*) - case `echo 'x\c'` in - *c*) ECHO_T=' ';; # ECHO_T is single tab character. - *) ECHO_C='\c';; - esac;; -*) - ECHO_N='-n';; -esac +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds +module_expsym_cmds=$lt_module_expsym_cmds -if expr a : '\(a\)' >/dev/null 2>&1 && - test "X`expr 00001 : '.*\(...\)'`" = X001; then - as_expr=expr -else - as_expr=false -fi +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld -rm -f conf$$ conf$$.exe conf$$.file -if test -d conf$$.dir; then - rm -f conf$$.dir/conf$$.file -else - rm -f conf$$.dir - mkdir conf$$.dir -fi -echo >conf$$.file -if ln -s conf$$.file conf$$ 2>/dev/null; then - as_ln_s='ln -s' - # ... but there are two gotchas: - # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. - # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. - # In both cases, we have to default to `cp -p'. - ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || - as_ln_s='cp -p' -elif ln conf$$.file conf$$ 2>/dev/null; then - as_ln_s=ln -else - as_ln_s='cp -p' -fi -rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file -rmdir conf$$.dir 2>/dev/null +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag -if mkdir -p . 2>/dev/null; then - as_mkdir_p=: -else - test -d ./-p && rmdir ./-p - as_mkdir_p=false -fi +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag -if test -x / >/dev/null 2>&1; then - as_test_x='test -x' -else - if ls -dL / >/dev/null 2>&1; then - as_ls_L_option=L - else - as_ls_L_option= - fi - as_test_x=' - eval sh -c '\'' - if test -d "$1"; then - test -d "$1/."; - else - case $1 in - -*)set "./$1";; - esac; - case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in - ???[sx]*):;;*)false;;esac;fi - '\'' sh - ' -fi -as_executable_p=$as_test_x +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec -# Sed expression to map a string onto a valid CPP name. -as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" +# If ld is used when linking, flag to hardcode \$libdir into a binary +# during linking. This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld -# Sed expression to map a string onto a valid variable name. -as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct -exec 6>&1 +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \${shlibpath_var} if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute -# Save the log message, to keep $[0] and so on meaningful, and to -# report actual input values of CONFIG_FILES etc. instead of their -# values after options handling. -ac_log=" -This file was extended by breakpad $as_me 0.1, which was -generated by GNU Autoconf 2.61. Invocation command line was +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L - CONFIG_FILES = $CONFIG_FILES - CONFIG_HEADERS = $CONFIG_HEADERS - CONFIG_LINKS = $CONFIG_LINKS - CONFIG_COMMANDS = $CONFIG_COMMANDS - $ $0 $@ +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var -on `(hostname || uname -n) 2>/dev/null | sed 1q` -" +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic -_ACEOF +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath -cat >>$CONFIG_STATUS <<_ACEOF -# Files that config.status was made for. -config_files="$ac_config_files" -config_headers="$ac_config_headers" -config_commands="$ac_config_commands" +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs -_ACEOF +# Fix the shell variable \$srcfile for the compiler. +fix_srcfile_path=$lt_fix_srcfile_path -cat >>$CONFIG_STATUS <<\_ACEOF -ac_cs_usage="\ -\`$as_me' instantiates files from templates according to the -current configuration. +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols -Usage: $0 [OPTIONS] [FILE]... +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds - -h, --help print this help, then exit - -V, --version print version number and configuration settings, then exit - -q, --quiet do not print progress messages - -d, --debug don't remove temporary files - --recheck update $as_me by reconfiguring in the same conditions - --file=FILE[:TEMPLATE] - instantiate the configuration file FILE - --header=FILE[:TEMPLATE] - instantiate the configuration header FILE +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms -Configuration files: -$config_files +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms -Configuration headers: -$config_headers +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds -Configuration commands: -$config_commands +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec -Report bugs to ." +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF -ac_cs_version="\\ -breakpad config.status 0.1 -configured by $0, generated by GNU Autoconf 2.61, - with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs -Copyright (C) 2006 Free Software Foundation, Inc. -This config.status script is free software; the Free Software Foundation -gives unlimited permission to copy, distribute and modify it." +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects +postdep_objects=$lt_postdep_objects +predeps=$lt_predeps +postdeps=$lt_postdeps -ac_pwd='$ac_pwd' -srcdir='$srcdir' -INSTALL='$INSTALL' -MKDIR_P='$MKDIR_P' -_ACEOF +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path -cat >>$CONFIG_STATUS <<\_ACEOF -# If no file are specified by the user, then we need to provide default -# value. By we need to know if files were specified by the user. -ac_need_defaults=: -while test $# != 0 -do - case $1 in - --*=*) - ac_option=`expr "X$1" : 'X\([^=]*\)='` - ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` - ac_shift=: - ;; - *) - ac_option=$1 - ac_optarg=$2 - ac_shift=shift +# ### END LIBTOOL CONFIG + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF ;; esac - case $ac_option in - # Handling of the options. - -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) - ac_cs_recheck=: ;; - --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) - echo "$ac_cs_version"; exit ;; - --debug | --debu | --deb | --de | --d | -d ) - debug=: ;; - --file | --fil | --fi | --f ) - $ac_shift - CONFIG_FILES="$CONFIG_FILES $ac_optarg" - ac_need_defaults=false;; - --header | --heade | --head | --hea ) - $ac_shift - CONFIG_HEADERS="$CONFIG_HEADERS $ac_optarg" - ac_need_defaults=false;; - --he | --h) - # Conflict between --help and --header - { echo "$as_me: error: ambiguous option: $1 -Try \`$0 --help' for more information." >&2 - { (exit 1); exit 1; }; };; - --help | --hel | -h ) - echo "$ac_cs_usage"; exit ;; - -q | -quiet | --quiet | --quie | --qui | --qu | --q \ - | -silent | --silent | --silen | --sile | --sil | --si | --s) - ac_cs_silent=: ;; - # This is an error. - -*) { echo "$as_me: error: unrecognized option: $1 -Try \`$0 --help' for more information." >&2 - { (exit 1); exit 1; }; } ;; +ltmain="$ac_aux_dir/ltmain.sh" - *) ac_config_targets="$ac_config_targets $1" - ac_need_defaults=false ;; + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + case $xsi_shell in + yes) + cat << \_LT_EOF >> "$cfgfile" + +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; esac - shift -done +} -ac_configure_extra_args= +# func_basename file +func_basename () +{ + func_basename_result="${1##*/}" +} -if $ac_cs_silent; then - exec 6>/dev/null - ac_configure_extra_args="$ac_configure_extra_args --silent" -fi +# func_dirname_and_basename file append nondir_replacement +# perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# Implementation must be kept synchronized with func_dirname +# and func_basename. For efficiency, we do not delegate to +# those functions but instead duplicate the functionality here. +func_dirname_and_basename () +{ + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac + func_basename_result="${1##*/}" +} -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF -if \$ac_cs_recheck; then - echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6 - CONFIG_SHELL=$SHELL - export CONFIG_SHELL - exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion -fi +# func_stripname prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +func_stripname () +{ + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are + # positional parameters, so assign one to ordinary parameter first. + func_stripname_result=${3} + func_stripname_result=${func_stripname_result#"${1}"} + func_stripname_result=${func_stripname_result%"${2}"} +} -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF -exec 5>>config.log +# func_opt_split +func_opt_split () { - echo - sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX -## Running $as_me. ## -_ASBOX - echo "$ac_log" -} >&5 + func_opt_split_opt=${1%%=*} + func_opt_split_arg=${1#*=} +} + +# func_lo2o object +func_lo2o () +{ + case ${1} in + *.lo) func_lo2o_result=${1%.lo}.${objext} ;; + *) func_lo2o_result=${1} ;; + esac +} + +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=${1%.*}.lo +} + +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=$(( $* )) +} -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF -# -# INIT-COMMANDS -# -AMDEP_TRUE="$AMDEP_TRUE" ac_aux_dir="$ac_aux_dir" +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=${#1} +} -_ACEOF +_LT_EOF + ;; + *) # Bourne compatible functions. + cat << \_LT_EOF >> "$cfgfile" -cat >>$CONFIG_STATUS <<\_ACEOF +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + # Extract subdirectory from the argument. + func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi +} + +# func_basename file +func_basename () +{ + func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"` +} -# Handling of arguments. -for ac_config_target in $ac_config_targets -do - case $ac_config_target in - "src/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;; - "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; - "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; - *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5 -echo "$as_me: error: invalid argument: $ac_config_target" >&2;} - { (exit 1); exit 1; }; };; +# func_stripname prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# func_strip_suffix prefix name +func_stripname () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "X${3}" \ + | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "X${3}" \ + | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;; esac -done +} +# sed scripts: +my_sed_long_opt='1s/^\(-[^=]*\)=.*/\1/;q' +my_sed_long_arg='1s/^-[^=]*=//' -# If the user did not use the arguments to specify the items to instantiate, -# then the envvar interface is used. Set only those that are not. -# We use the long form for the default assignment because of an extremely -# bizarre bug on SunOS 4.1.3. -if $ac_need_defaults; then - test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files - test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers - test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands -fi +# func_opt_split +func_opt_split () +{ + func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"` + func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"` +} -# Have a temporary directory for convenience. Make it in the build tree -# simply because there is no reason against having it here, and in addition, -# creating and moving files from /tmp can sometimes cause problems. -# Hook for its removal unless debugging. -# Note that there is a small window in which the directory will not be cleaned: -# after its creation but before its name has been assigned to `$tmp'. -$debug || +# func_lo2o object +func_lo2o () { - tmp= - trap 'exit_status=$? - { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status -' 0 - trap '{ (exit 1); exit 1; }' 1 2 13 15 + func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"` } -# Create a (secure) tmp directory for tmp files. +# func_xform libobj-or-source +func_xform () { - tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && - test -n "$tmp" && test -d "$tmp" -} || + func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[^.]*$/.lo/'` +} + +# func_arith arithmetic-term... +func_arith () { - tmp=./conf$$-$RANDOM - (umask 077 && mkdir "$tmp") -} || + func_arith_result=`expr "$@"` +} + +# func_len string +# STRING may not start with a hyphen. +func_len () { - echo "$me: cannot create a temporary directory in ." >&2 - { (exit 1); exit 1; } + func_len_result=`expr "$1" : ".*" 2>/dev/null || echo $max_cmd_len` } -# -# Set up the sed scripts for CONFIG_FILES section. -# +_LT_EOF +esac -# No need to generate the scripts if there are no CONFIG_FILES. -# This happens for instance when ./config.status config.h -if test -n "$CONFIG_FILES"; then +case $lt_shell_append in + yes) + cat << \_LT_EOF >> "$cfgfile" + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "$1+=\$2" +} +_LT_EOF + ;; + *) + cat << \_LT_EOF >> "$cfgfile" -_ACEOF +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "$1=\$$1\$2" +} +_LT_EOF + ;; + esac -ac_delim='%!_!# ' -for ac_last_try in false false false false false :; do - cat >conf$$subs.sed <<_ACEOF -SHELL!$SHELL$ac_delim -PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim -PACKAGE_NAME!$PACKAGE_NAME$ac_delim -PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim -PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim -PACKAGE_STRING!$PACKAGE_STRING$ac_delim -PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim -exec_prefix!$exec_prefix$ac_delim -prefix!$prefix$ac_delim -program_transform_name!$program_transform_name$ac_delim -bindir!$bindir$ac_delim -sbindir!$sbindir$ac_delim -libexecdir!$libexecdir$ac_delim -datarootdir!$datarootdir$ac_delim -datadir!$datadir$ac_delim -sysconfdir!$sysconfdir$ac_delim -sharedstatedir!$sharedstatedir$ac_delim -localstatedir!$localstatedir$ac_delim -includedir!$includedir$ac_delim -oldincludedir!$oldincludedir$ac_delim -docdir!$docdir$ac_delim -infodir!$infodir$ac_delim -htmldir!$htmldir$ac_delim -dvidir!$dvidir$ac_delim -pdfdir!$pdfdir$ac_delim -psdir!$psdir$ac_delim -libdir!$libdir$ac_delim -localedir!$localedir$ac_delim -mandir!$mandir$ac_delim -DEFS!$DEFS$ac_delim -ECHO_C!$ECHO_C$ac_delim -ECHO_N!$ECHO_N$ac_delim -ECHO_T!$ECHO_T$ac_delim -LIBS!$LIBS$ac_delim -build_alias!$build_alias$ac_delim -host_alias!$host_alias$ac_delim -target_alias!$target_alias$ac_delim -INSTALL_PROGRAM!$INSTALL_PROGRAM$ac_delim -INSTALL_SCRIPT!$INSTALL_SCRIPT$ac_delim -INSTALL_DATA!$INSTALL_DATA$ac_delim -am__isrc!$am__isrc$ac_delim -CYGPATH_W!$CYGPATH_W$ac_delim -PACKAGE!$PACKAGE$ac_delim -VERSION!$VERSION$ac_delim -ACLOCAL!$ACLOCAL$ac_delim -AUTOCONF!$AUTOCONF$ac_delim -AUTOMAKE!$AUTOMAKE$ac_delim -AUTOHEADER!$AUTOHEADER$ac_delim -MAKEINFO!$MAKEINFO$ac_delim -install_sh!$install_sh$ac_delim -STRIP!$STRIP$ac_delim -INSTALL_STRIP_PROGRAM!$INSTALL_STRIP_PROGRAM$ac_delim -mkdir_p!$mkdir_p$ac_delim -AWK!$AWK$ac_delim -SET_MAKE!$SET_MAKE$ac_delim -am__leading_dot!$am__leading_dot$ac_delim -AMTAR!$AMTAR$ac_delim -am__tar!$am__tar$ac_delim -am__untar!$am__untar$ac_delim -CC!$CC$ac_delim -CFLAGS!$CFLAGS$ac_delim -LDFLAGS!$LDFLAGS$ac_delim -CPPFLAGS!$CPPFLAGS$ac_delim -ac_ct_CC!$ac_ct_CC$ac_delim -EXEEXT!$EXEEXT$ac_delim -OBJEXT!$OBJEXT$ac_delim -DEPDIR!$DEPDIR$ac_delim -am__include!$am__include$ac_delim -am__quote!$am__quote$ac_delim -AMDEP_TRUE!$AMDEP_TRUE$ac_delim -AMDEP_FALSE!$AMDEP_FALSE$ac_delim -AMDEPBACKSLASH!$AMDEPBACKSLASH$ac_delim -CCDEPMODE!$CCDEPMODE$ac_delim -am__fastdepCC_TRUE!$am__fastdepCC_TRUE$ac_delim -am__fastdepCC_FALSE!$am__fastdepCC_FALSE$ac_delim -CPP!$CPP$ac_delim -CXX!$CXX$ac_delim -CXXFLAGS!$CXXFLAGS$ac_delim -ac_ct_CXX!$ac_ct_CXX$ac_delim -CXXDEPMODE!$CXXDEPMODE$ac_delim -am__fastdepCXX_TRUE!$am__fastdepCXX_TRUE$ac_delim -am__fastdepCXX_FALSE!$am__fastdepCXX_FALSE$ac_delim -build!$build$ac_delim -build_cpu!$build_cpu$ac_delim -build_vendor!$build_vendor$ac_delim -build_os!$build_os$ac_delim -host!$host$ac_delim -host_cpu!$host_cpu$ac_delim -host_vendor!$host_vendor$ac_delim -host_os!$host_os$ac_delim -GREP!$GREP$ac_delim -EGREP!$EGREP$ac_delim -LN_S!$LN_S$ac_delim -ECHO!$ECHO$ac_delim -AR!$AR$ac_delim -RANLIB!$RANLIB$ac_delim -CXXCPP!$CXXCPP$ac_delim -_ACEOF + sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) - if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 97; then - break - elif $ac_last_try; then - { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 -echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} - { (exit 1); exit 1; }; } - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" -ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed` -if test -n "$ac_eof"; then - ac_eof=`echo "$ac_eof" | sort -nru | sed 1q` - ac_eof=`expr $ac_eof + 1` -fi -cat >>$CONFIG_STATUS <<_ACEOF -cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof -/@[a-zA-Z_][a-zA-Z_0-9]*@/!b -_ACEOF -sed ' -s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g -s/^/s,@/; s/!/@,|#_!!_#|/ -:n -t n -s/'"$ac_delim"'$/,g/; t -s/$/\\/; p -N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n -' >>$CONFIG_STATUS >$CONFIG_STATUS <<_ACEOF -CEOF$ac_eof -_ACEOF + cat <<_LT_EOF >> "$ofile" +# ### BEGIN LIBTOOL TAG CONFIG: CXX -ac_delim='%!_!# ' -for ac_last_try in false false false false false :; do - cat >conf$$subs.sed <<_ACEOF -F77!$F77$ac_delim -FFLAGS!$FFLAGS$ac_delim -ac_ct_F77!$ac_ct_F77$ac_delim -LIBTOOL!$LIBTOOL$ac_delim -LIBTOOL_DEPS!$LIBTOOL_DEPS$ac_delim -SELFTEST_TRUE!$SELFTEST_TRUE$ac_delim -SELFTEST_FALSE!$SELFTEST_FALSE$ac_delim -LIBOBJS!$LIBOBJS$ac_delim -LTLIBOBJS!$LTLIBOBJS$ac_delim -_ACEOF +# The linker used to build libraries. +LD=$lt_LD_CXX - if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 9; then - break - elif $ac_last_try; then - { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5 -echo "$as_me: error: could not make $CONFIG_STATUS" >&2;} - { (exit 1); exit 1; }; } - else - ac_delim="$ac_delim!$ac_delim _$ac_delim!! " - fi -done +# Commands used to build an old-style archive. +old_archive_cmds=$lt_old_archive_cmds_CXX -ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed` -if test -n "$ac_eof"; then - ac_eof=`echo "$ac_eof" | sort -nru | sed 1q` - ac_eof=`expr $ac_eof + 1` -fi - -cat >>$CONFIG_STATUS <<_ACEOF -cat >"\$tmp/subs-2.sed" <<\CEOF$ac_eof -/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end -_ACEOF -sed ' -s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g -s/^/s,@/; s/!/@,|#_!!_#|/ -:n -t n -s/'"$ac_delim"'$/,g/; t -s/$/\\/; p -N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n -' >>$CONFIG_STATUS >$CONFIG_STATUS <<_ACEOF -:end -s/|#_!!_#|//g -CEOF$ac_eof -_ACEOF +# A language specific compiler. +CC=$lt_compiler_CXX +# Is the compiler the GNU compiler? +with_gcc=$GCC_CXX -# VPATH may cause trouble with some makes, so we remove $(srcdir), -# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and -# trailing colons and then remove the whole line if VPATH becomes empty -# (actually we leave an empty line to preserve line numbers). -if test "x$srcdir" = x.; then - ac_vpsub='/^[ ]*VPATH[ ]*=/{ -s/:*\$(srcdir):*/:/ -s/:*\${srcdir}:*/:/ -s/:*@srcdir@:*/:/ -s/^\([^=]*=[ ]*\):*/\1/ -s/:*$// -s/^[^=]*=[ ]*$// -}' -fi +# Compiler flag to turn off builtin functions. +no_builtin_flag=$lt_lt_prog_compiler_no_builtin_flag_CXX -cat >>$CONFIG_STATUS <<\_ACEOF -fi # test -n "$CONFIG_FILES" +# How to pass a linker flag through the compiler. +wl=$lt_lt_prog_compiler_wl_CXX +# Additional compiler flags for building library objects. +pic_flag=$lt_lt_prog_compiler_pic_CXX -for ac_tag in :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS -do - case $ac_tag in - :[FHLC]) ac_mode=$ac_tag; continue;; - esac - case $ac_mode$ac_tag in - :[FHL]*:*);; - :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5 -echo "$as_me: error: Invalid tag $ac_tag." >&2;} - { (exit 1); exit 1; }; };; - :[FH]-) ac_tag=-:-;; - :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; - esac - ac_save_IFS=$IFS - IFS=: - set x $ac_tag - IFS=$ac_save_IFS - shift - ac_file=$1 - shift +# Compiler flag to prevent dynamic linking. +link_static_flag=$lt_lt_prog_compiler_static_CXX - case $ac_mode in - :L) ac_source=$1;; - :[FH]) - ac_file_inputs= - for ac_f - do - case $ac_f in - -) ac_f="$tmp/stdin";; - *) # Look for the file first in the build tree, then in the source tree - # (if the path is not absolute). The absolute path cannot be DOS-style, - # because $ac_f cannot contain `:'. - test -f "$ac_f" || - case $ac_f in - [\\/$]*) false;; - *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; - esac || - { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5 -echo "$as_me: error: cannot find input file: $ac_f" >&2;} - { (exit 1); exit 1; }; };; - esac - ac_file_inputs="$ac_file_inputs $ac_f" - done +# Does compiler simultaneously support -c and -o options? +compiler_c_o=$lt_lt_cv_prog_compiler_c_o_CXX - # Let's still pretend it is `configure' which instantiates (i.e., don't - # use $as_me), people would be surprised to read: - # /* config.h. Generated by config.status. */ - configure_input="Generated from "`IFS=: - echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure." - if test x"$ac_file" != x-; then - configure_input="$ac_file. $configure_input" - { echo "$as_me:$LINENO: creating $ac_file" >&5 -echo "$as_me: creating $ac_file" >&6;} - fi +# Whether or not to add -lc for building shared libraries. +build_libtool_need_lc=$archive_cmds_need_lc_CXX - case $ac_tag in - *:-:* | *:-) cat >"$tmp/stdin";; - esac - ;; - esac +# Whether or not to disallow shared libs when runtime libs are static. +allow_libtool_libs_with_static_runtimes=$enable_shared_with_static_runtimes_CXX - ac_dir=`$as_dirname -- "$ac_file" || -$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$ac_file" : 'X\(//\)[^/]' \| \ - X"$ac_file" : 'X\(//\)$' \| \ - X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || -echo X"$ac_file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - { as_dir="$ac_dir" - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 -echo "$as_me: error: cannot create directory $as_dir" >&2;} - { (exit 1); exit 1; }; }; } - ac_builddir=. +# Compiler flag to allow reflexive dlopens. +export_dynamic_flag_spec=$lt_export_dynamic_flag_spec_CXX + +# Compiler flag to generate shared objects directly from archives. +whole_archive_flag_spec=$lt_whole_archive_flag_spec_CXX + +# Whether the compiler copes with passing no objects directly. +compiler_needs_object=$lt_compiler_needs_object_CXX + +# Create an old-style archive from a shared archive. +old_archive_from_new_cmds=$lt_old_archive_from_new_cmds_CXX + +# Create a temporary old-style archive to link instead of a shared archive. +old_archive_from_expsyms_cmds=$lt_old_archive_from_expsyms_cmds_CXX + +# Commands used to build a shared archive. +archive_cmds=$lt_archive_cmds_CXX +archive_expsym_cmds=$lt_archive_expsym_cmds_CXX + +# Commands used to build a loadable module if different from building +# a shared archive. +module_cmds=$lt_module_cmds_CXX +module_expsym_cmds=$lt_module_expsym_cmds_CXX + +# Whether we are building with GNU ld or not. +with_gnu_ld=$lt_with_gnu_ld_CXX + +# Flag that allows shared libraries with undefined symbols to be built. +allow_undefined_flag=$lt_allow_undefined_flag_CXX -case "$ac_dir" in -.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; -*) - ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'` - # A ".." for each directory in $ac_dir_suffix. - ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'` - case $ac_top_builddir_sub in - "") ac_top_builddir_sub=. ac_top_build_prefix= ;; - *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; - esac ;; -esac -ac_abs_top_builddir=$ac_pwd -ac_abs_builddir=$ac_pwd$ac_dir_suffix -# for backward compatibility: -ac_top_builddir=$ac_top_build_prefix +# Flag that enforces no undefined symbols. +no_undefined_flag=$lt_no_undefined_flag_CXX -case $srcdir in - .) # We are building in place. - ac_srcdir=. - ac_top_srcdir=$ac_top_builddir_sub - ac_abs_top_srcdir=$ac_pwd ;; - [\\/]* | ?:[\\/]* ) # Absolute name. - ac_srcdir=$srcdir$ac_dir_suffix; - ac_top_srcdir=$srcdir - ac_abs_top_srcdir=$srcdir ;; - *) # Relative name. - ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix - ac_top_srcdir=$ac_top_build_prefix$srcdir - ac_abs_top_srcdir=$ac_pwd/$srcdir ;; -esac -ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist +hardcode_libdir_flag_spec=$lt_hardcode_libdir_flag_spec_CXX +# If ld is used when linking, flag to hardcode \$libdir into a binary +# during linking. This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec_ld=$lt_hardcode_libdir_flag_spec_ld_CXX - case $ac_mode in - :F) - # - # CONFIG_FILE - # +# Whether we need a single "-rpath" flag with a separated argument. +hardcode_libdir_separator=$lt_hardcode_libdir_separator_CXX - case $INSTALL in - [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; - *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; - esac - ac_MKDIR_P=$MKDIR_P - case $MKDIR_P in - [\\/$]* | ?:[\\/]* ) ;; - */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; - esac -_ACEOF +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary. +hardcode_direct=$hardcode_direct_CXX -cat >>$CONFIG_STATUS <<\_ACEOF -# If the template does not know about datarootdir, expand it. -# FIXME: This hack should be removed a few years after 2.60. -ac_datarootdir_hack=; ac_datarootdir_seen= +# Set to "yes" if using DIR/libNAME\${shared_ext} during linking hardcodes +# DIR into the resulting binary and the resulting library dependency is +# "absolute",i.e impossible to change by setting \${shlibpath_var} if the +# library is relocated. +hardcode_direct_absolute=$hardcode_direct_absolute_CXX -case `sed -n '/datarootdir/ { - p - q -} -/@datadir@/p -/@docdir@/p -/@infodir@/p -/@localedir@/p -/@mandir@/p -' $ac_file_inputs` in -*datarootdir*) ac_datarootdir_seen=yes;; -*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) - { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 -echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} -_ACEOF -cat >>$CONFIG_STATUS <<_ACEOF - ac_datarootdir_hack=' - s&@datadir@&$datadir&g - s&@docdir@&$docdir&g - s&@infodir@&$infodir&g - s&@localedir@&$localedir&g - s&@mandir@&$mandir&g - s&\\\${datarootdir}&$datarootdir&g' ;; -esac -_ACEOF +# Set to "yes" if using the -LDIR flag during linking hardcodes DIR +# into the resulting binary. +hardcode_minus_L=$hardcode_minus_L_CXX -# Neutralize VPATH when `$srcdir' = `.'. -# Shell code in configure.ac might set extrasub. -# FIXME: do we really want to maintain this feature? -cat >>$CONFIG_STATUS <<_ACEOF - sed "$ac_vpsub -$extrasub -_ACEOF -cat >>$CONFIG_STATUS <<\_ACEOF -:t -/@[a-zA-Z_][a-zA-Z_0-9]*@/!b -s&@configure_input@&$configure_input&;t t -s&@top_builddir@&$ac_top_builddir_sub&;t t -s&@srcdir@&$ac_srcdir&;t t -s&@abs_srcdir@&$ac_abs_srcdir&;t t -s&@top_srcdir@&$ac_top_srcdir&;t t -s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t -s&@builddir@&$ac_builddir&;t t -s&@abs_builddir@&$ac_abs_builddir&;t t -s&@abs_top_builddir@&$ac_abs_top_builddir&;t t -s&@INSTALL@&$ac_INSTALL&;t t -s&@MKDIR_P@&$ac_MKDIR_P&;t t -$ac_datarootdir_hack -" $ac_file_inputs | sed -f "$tmp/subs-1.sed" | sed -f "$tmp/subs-2.sed" >$tmp/out +# Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR +# into the resulting binary. +hardcode_shlibpath_var=$hardcode_shlibpath_var_CXX -test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && - { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } && - { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } && - { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined." >&5 -echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' -which seems to be undefined. Please make sure it is defined." >&2;} +# Set to "yes" if building a shared library automatically hardcodes DIR +# into the library and all subsequent libraries and executables linked +# against it. +hardcode_automatic=$hardcode_automatic_CXX - rm -f "$tmp/stdin" - case $ac_file in - -) cat "$tmp/out"; rm -f "$tmp/out";; - *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;; - esac - ;; - :H) - # - # CONFIG_HEADER - # -_ACEOF +# Set to yes if linker adds runtime paths of dependent libraries +# to runtime path list. +inherit_rpath=$inherit_rpath_CXX -# Transform confdefs.h into a sed script `conftest.defines', that -# substitutes the proper values into config.h.in to produce config.h. -rm -f conftest.defines conftest.tail -# First, append a space to every undef/define line, to ease matching. -echo 's/$/ /' >conftest.defines -# Then, protect against being on the right side of a sed subst, or in -# an unquoted here document, in config.status. If some macros were -# called several times there might be several #defines for the same -# symbol, which is useless. But do not sort them, since the last -# AC_DEFINE must be honored. -ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* -# These sed commands are passed to sed as "A NAME B PARAMS C VALUE D", where -# NAME is the cpp macro being defined, VALUE is the value it is being given. -# PARAMS is the parameter list in the macro definition--in most cases, it's -# just an empty string. -ac_dA='s,^\\([ #]*\\)[^ ]*\\([ ]*' -ac_dB='\\)[ (].*,\\1define\\2' -ac_dC=' ' -ac_dD=' ,' +# Whether libtool must link a program against all its dependency libraries. +link_all_deplibs=$link_all_deplibs_CXX -uniq confdefs.h | - sed -n ' - t rset - :rset - s/^[ ]*#[ ]*define[ ][ ]*// - t ok - d - :ok - s/[\\&,]/\\&/g - s/^\('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/ '"$ac_dA"'\1'"$ac_dB"'\2'"${ac_dC}"'\3'"$ac_dD"'/p - s/^\('"$ac_word_re"'\)[ ]*\(.*\)/'"$ac_dA"'\1'"$ac_dB$ac_dC"'\2'"$ac_dD"'/p - ' >>conftest.defines - -# Remove the space that was appended to ease matching. -# Then replace #undef with comments. This is necessary, for -# example, in the case of _POSIX_SOURCE, which is predefined and required -# on some systems where configure will not decide to define it. -# (The regexp can be short, since the line contains either #define or #undef.) -echo 's/ $// -s,^[ #]*u.*,/* & */,' >>conftest.defines - -# Break up conftest.defines: -ac_max_sed_lines=50 - -# First sed command is: sed -f defines.sed $ac_file_inputs >"$tmp/out1" -# Second one is: sed -f defines.sed "$tmp/out1" >"$tmp/out2" -# Third one will be: sed -f defines.sed "$tmp/out2" >"$tmp/out1" -# et cetera. -ac_in='$ac_file_inputs' -ac_out='"$tmp/out1"' -ac_nxt='"$tmp/out2"' +# Fix the shell variable \$srcfile for the compiler. +fix_srcfile_path=$lt_fix_srcfile_path_CXX -while : -do - # Write a here document: - cat >>$CONFIG_STATUS <<_ACEOF - # First, check the format of the line: - cat >"\$tmp/defines.sed" <<\\CEOF -/^[ ]*#[ ]*undef[ ][ ]*$ac_word_re[ ]*\$/b def -/^[ ]*#[ ]*define[ ][ ]*$ac_word_re[( ]/b def -b -:def -_ACEOF - sed ${ac_max_sed_lines}q conftest.defines >>$CONFIG_STATUS - echo 'CEOF - sed -f "$tmp/defines.sed"' "$ac_in >$ac_out" >>$CONFIG_STATUS - ac_in=$ac_out; ac_out=$ac_nxt; ac_nxt=$ac_in - sed 1,${ac_max_sed_lines}d conftest.defines >conftest.tail - grep . conftest.tail >/dev/null || break - rm -f conftest.defines - mv conftest.tail conftest.defines -done -rm -f conftest.defines conftest.tail +# Set to "yes" if exported symbols are required. +always_export_symbols=$always_export_symbols_CXX -echo "ac_result=$ac_in" >>$CONFIG_STATUS -cat >>$CONFIG_STATUS <<\_ACEOF - if test x"$ac_file" != x-; then - echo "/* $configure_input */" >"$tmp/config.h" - cat "$ac_result" >>"$tmp/config.h" - if diff $ac_file "$tmp/config.h" >/dev/null 2>&1; then - { echo "$as_me:$LINENO: $ac_file is unchanged" >&5 -echo "$as_me: $ac_file is unchanged" >&6;} - else - rm -f $ac_file - mv "$tmp/config.h" $ac_file - fi - else - echo "/* $configure_input */" - cat "$ac_result" - fi - rm -f "$tmp/out12" -# Compute $ac_file's index in $config_headers. -_am_stamp_count=1 -for _am_header in $config_headers :; do - case $_am_header in - $ac_file | $ac_file:* ) - break ;; - * ) - _am_stamp_count=`expr $_am_stamp_count + 1` ;; - esac -done -echo "timestamp for $ac_file" >`$as_dirname -- $ac_file || -$as_expr X$ac_file : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X$ac_file : 'X\(//\)[^/]' \| \ - X$ac_file : 'X\(//\)$' \| \ - X$ac_file : 'X\(/\)' \| . 2>/dev/null || -echo X$ac_file | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'`/stamp-h$_am_stamp_count - ;; +# The commands to list exported symbols. +export_symbols_cmds=$lt_export_symbols_cmds_CXX - :C) { echo "$as_me:$LINENO: executing $ac_file commands" >&5 -echo "$as_me: executing $ac_file commands" >&6;} - ;; - esac +# Symbols that should not be listed in the preloaded symbols. +exclude_expsyms=$lt_exclude_expsyms_CXX +# Symbols that must always be exported. +include_expsyms=$lt_include_expsyms_CXX + +# Commands necessary for linking programs (against libraries) with templates. +prelink_cmds=$lt_prelink_cmds_CXX + +# Specify filename containing input files. +file_list_spec=$lt_file_list_spec_CXX + +# How to hardcode a shared library path into an executable. +hardcode_action=$hardcode_action_CXX + +# The directories searched by this compiler when creating a shared library. +compiler_lib_search_dirs=$lt_compiler_lib_search_dirs_CXX + +# Dependencies to place before and after the objects being linked to +# create a shared library. +predep_objects=$lt_predep_objects_CXX +postdep_objects=$lt_postdep_objects_CXX +predeps=$lt_predeps_CXX +postdeps=$lt_postdeps_CXX + +# The library search path used internally by the compiler when linking +# a shared library. +compiler_lib_search_path=$lt_compiler_lib_search_path_CXX + +# ### END LIBTOOL TAG CONFIG: CXX +_LT_EOF - case $ac_file$ac_mode in - "depfiles":C) test x"$AMDEP_TRUE" != x"" || for mf in $CONFIG_FILES; do - # Strip MF so we end up with the name of the file. - mf=`echo "$mf" | sed -e 's/:.*$//'` - # Check whether this is an Automake generated Makefile or not. - # We used to match only the files named `Makefile.in', but - # some people rename them; so instead we look at the file content. - # Grep'ing the first line is not enough: some people post-process - # each Makefile.in and add a new line on top of each file to say so. - # Grep'ing the whole file is not good either: AIX grep has a line - # limit of 2048, but all sed's we know have understand at least 4000. - if sed 10q "$mf" | grep '^#.*generated by automake' > /dev/null 2>&1; then - dirpart=`$as_dirname -- "$mf" || -$as_expr X"$mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$mf" : 'X\(//\)[^/]' \| \ - X"$mf" : 'X\(//\)$' \| \ - X"$mf" : 'X\(/\)' \| . 2>/dev/null || -echo X"$mf" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - else - continue - fi - # Extract the definition of DEPDIR, am__include, and am__quote - # from the Makefile without running `make'. - DEPDIR=`sed -n 's/^DEPDIR = //p' < "$mf"` - test -z "$DEPDIR" && continue - am__include=`sed -n 's/^am__include = //p' < "$mf"` - test -z "am__include" && continue - am__quote=`sed -n 's/^am__quote = //p' < "$mf"` - # When using ansi2knr, U may be empty or an underscore; expand it - U=`sed -n 's/^U = //p' < "$mf"` - # Find all dependency output files, they are included files with - # $(DEPDIR) in their names. We invoke sed twice because it is the - # simplest approach to changing $(DEPDIR) to its actual value in the - # expansion. - for file in `sed -n " - s/^$am__include $am__quote\(.*(DEPDIR).*\)$am__quote"'$/\1/p' <"$mf" | \ - sed -e 's/\$(DEPDIR)/'"$DEPDIR"'/g' -e 's/\$U/'"$U"'/g'`; do - # Make sure the directory exists. - test -f "$dirpart/$file" && continue - fdir=`$as_dirname -- "$file" || -$as_expr X"$file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$file" : 'X\(//\)[^/]' \| \ - X"$file" : 'X\(//\)$' \| \ - X"$file" : 'X\(/\)' \| . 2>/dev/null || -echo X"$file" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - { as_dir=$dirpart/$fdir - case $as_dir in #( - -*) as_dir=./$as_dir;; - esac - test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || { - as_dirs= - while :; do - case $as_dir in #( - *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #( - *) as_qdir=$as_dir;; - esac - as_dirs="'$as_qdir' $as_dirs" - as_dir=`$as_dirname -- "$as_dir" || -$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$as_dir" : 'X\(//\)[^/]' \| \ - X"$as_dir" : 'X\(//\)$' \| \ - X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || -echo X"$as_dir" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q'` - test -d "$as_dir" && break - done - test -z "$as_dirs" || eval "mkdir $as_dirs" - } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5 -echo "$as_me: error: cannot create directory $as_dir" >&2;} - { (exit 1); exit 1; }; }; } - # echo "creating $dirpart/$file" - echo '# dummy' > "$dirpart/$file" - done -done ;; esac done # for ac_tag -{ (exit 0); exit 0; } +as_fn_exit 0 _ACEOF -chmod +x $CONFIG_STATUS ac_clean_files=$ac_clean_files_save +test $ac_write_fail = 0 || + as_fn_error "write failure creating $CONFIG_STATUS" "$LINENO" 5 + # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. @@ -20853,6 +17476,10 @@ exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. - $ac_cs_success || { (exit 1); exit 1; } + $ac_cs_success || as_fn_exit $? +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/configure.ac firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/configure.ac --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/configure.ac 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/configure.ac 2010-04-16 17:32:47.000000000 +0100 @@ -35,7 +35,7 @@ AC_CONFIG_SRCDIR(README) AC_CONFIG_AUX_DIR(autotools) -AM_INIT_AUTOMAKE(subdir-objects tar-ustar) +AM_INIT_AUTOMAKE(subdir-objects tar-ustar 1.11.1) AM_CONFIG_HEADER(src/config.h) AC_PROG_CC @@ -46,6 +46,26 @@ AC_SUBST(LIBTOOL_DEPS) AC_HEADER_STDC +m4_include(m4/ax_pthread.m4) +AX_PTHREAD + +AC_ARG_ENABLE(m32, + AS_HELP_STRING([--enable-m32], + [Compile/build with -m32] + [(default is no)]), + [case "${enableval}" in + yes) + CFLAGS=$(CFLAGS) -m32 + usem32=true + ;; + no) + usem32=false + ;; + *) + AC_MSG_ERROR(bad value ${enableval} for --enable-m32) + ;; + esac], + [usem32=false]) AC_ARG_ENABLE(selftest, AS_HELP_STRING([--enable-selftest], diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/m4/ax_pthread.m4 firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/m4/ax_pthread.m4 --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/m4/ax_pthread.m4 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/m4/ax_pthread.m4 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,283 @@ +# =========================================================================== +# http://www.nongnu.org/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC to any special C compiler that is needed for +# multi-threaded programs (defaults to the value of CC otherwise). (This +# is necessary on AIX to use the special cc_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also link it with them as well. e.g. you should link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threads programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name +# (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# 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 the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 6 + +AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) +AC_DEFUN([AX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_SAVE +AC_LANG_C +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) + AC_MSG_RESULT($ax_pthread_ok) + if test x"$ax_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" + ;; + + *-darwin*) + acx_pthread_flags="-pthread $acx_pthread_flags" + ;; +esac + +if test x"$ax_pthread_ok" = xno; then +for flag in $ax_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) + if test x"$ax_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include + static void routine(void* a) {a=0;} + static void* start_routine(void* a) {return a;}], + [pthread_t th; pthread_attr_t attr; + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_create(&th,0,start_routine,0); + pthread_cleanup_pop(0); ], + [ax_pthread_ok=yes]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT($ax_pthread_ok) + if test "x$ax_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$ax_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_MSG_CHECKING([for joinable pthread attribute]) + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_TRY_LINK([#include ], [int attr=$attr; return attr;], + [attr_name=$attr; break]) + done + AC_MSG_RESULT($attr_name) + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: must compile with xlc_r or cc_r + if test x"$GCC" != xyes; then + AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) + else + PTHREAD_CC=$CC + fi +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$ax_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + ax_pthread_ok=no + $2 +fi +AC_LANG_RESTORE +])dnl AX_PTHREAD \ No newline at end of file diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/Makefile.am firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/Makefile.am --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/Makefile.am 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/Makefile.am 2010-04-16 17:32:47.000000000 +0100 @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in -# Copyright (c) 2006, Google Inc. +# Copyright (c) 2010, Google Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -33,6 +33,8 @@ # This allows #includes to be relative to src/ AM_CPPFLAGS = -I$(top_srcdir)/src +# Specify include paths for ac macros +ACLOCAL_AMFLAGS = -I m4 ## Documentation docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION) @@ -47,7 +49,19 @@ ## Libraries -lib_LTLIBRARIES = src/libbreakpad.la +lib_LTLIBRARIES = src/libbreakpad.la src/client/linux/libbreakpad_client.la + +src_client_linux_libbreakpad_client_la_SOURCES = \ + src/client/linux/crash_generation/crash_generation_client.cc \ + src/client/linux/handler/exception_handler.cc \ + src/client/linux/minidump_writer/linux_dumper.cc \ + src/client/linux/minidump_writer/minidump_writer.cc \ + src/client/minidump_file_writer.cc \ + src/common/convert_UTF.c \ + src/common/md5.c \ + src/common/string_conversion.cc \ + src/common/linux/file_id.cc \ + src/common/linux/guid_creator.cc src_libbreakpad_la_SOURCES = \ src/google_breakpad/common/breakpad_types.h \ @@ -74,6 +88,8 @@ src/processor/basic_code_modules.h \ src/processor/basic_source_line_resolver.cc \ src/processor/call_stack.cc \ + src/processor/cfi_frame_info.cc \ + src/processor/cfi_frame_info.h \ src/processor/contained_range_map-inl.h \ src/processor/contained_range_map.h \ src/processor/linked_ptr.h \ @@ -91,10 +107,12 @@ src/processor/scoped_ptr.h \ src/processor/simple_symbol_supplier.cc \ src/processor/simple_symbol_supplier.h \ - src/processor/stack_frame_info.h \ + src/processor/windows_frame_info.h \ src/processor/stackwalker.cc \ src/processor/stackwalker_amd64.cc \ src/processor/stackwalker_amd64.h \ + src/processor/stackwalker_arm.cc \ + src/processor/stackwalker_arm.h \ src/processor/stackwalker_ppc.cc \ src/processor/stackwalker_ppc.h \ src/processor/stackwalker_sparc.cc \ @@ -105,19 +123,27 @@ ## Programs bin_PROGRAMS = \ + src/client/linux/linux_dumper_unittest_helper \ src/processor/minidump_dump \ src/processor/minidump_stackwalk - ## Tests check_PROGRAMS = \ + src/client/linux/linux_client_unittest \ src/processor/address_map_unittest \ src/processor/basic_source_line_resolver_unittest \ + src/processor/cfi_frame_info_unittest \ src/processor/contained_range_map_unittest \ src/processor/minidump_processor_unittest \ + src/processor/minidump_unittest \ src/processor/pathname_stripper_unittest \ src/processor/postfix_evaluator_unittest \ - src/processor/range_map_unittest + src/processor/range_map_unittest \ + src/processor/stackwalker_amd64_unittest \ + src/processor/stackwalker_arm_unittest \ + src/processor/stackwalker_x86_unittest \ + src/processor/synth_minidump_unittest \ + src/processor/test_assembler_unittest if SELFTEST check_PROGRAMS += \ @@ -132,6 +158,40 @@ TESTS = $(check_PROGRAMS) $(check_SCRIPTS) TESTS_ENVIRONMENT = +src_client_linux_linux_dumper_unittest_helper_SOURCES = \ + src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc +src_client_linux_linux_dumper_unittest_helper_CXXFLAGS=$(PTHREAD_CFLAGS) +src_client_linux_linux_dumper_unittest_helper_LDFLAGS=$(PTHREAD_CFLAGS) +src_client_linux_linux_dumper_unittest_helper_CC=$(PTHREAD_CC) + +src_client_linux_linux_client_unittest_SOURCES = \ + src/client/linux/handler/exception_handler_unittest.cc \ + src/client/linux/minidump_writer/directory_reader_unittest.cc \ + src/client/linux/minidump_writer/line_reader_unittest.cc \ + src/client/linux/minidump_writer/linux_dumper_unittest.cc \ + src/client/linux/minidump_writer/minidump_writer_unittest.cc \ + src/testing/gtest/src/gtest-all.cc \ + src/testing/gtest/src/gtest_main.cc \ + src/testing/src/gmock-all.cc + +src_client_linux_linux_client_unittest_CPPFLAGS = \ + -I$(top_srcdir)/src/testing/include \ + -I$(top_srcdir)/src/testing/gtest/include \ + -I$(top_srcdir)/src/testing/gtest \ + -I$(top_srcdir)/src/testing +src_client_linux_linux_client_unittest_LDADD = \ + src/client/linux/handler/exception_handler.lo \ + src/client/linux/crash_generation/crash_generation_client.lo \ + src/client/linux/minidump_writer/linux_dumper.lo \ + src/client/linux/minidump_writer/minidump_writer.lo \ + src/client/minidump_file_writer.lo \ + src/common/convert_UTF.lo \ + src/common/md5.lo \ + src/common/linux/file_id.lo \ + src/common/linux/guid_creator.lo \ + src/common/string_conversion.lo +src_client_linux_linux_client_unittest_DEPENDENCIES = src/client/linux/linux_dumper_unittest_helper src/client/linux/libbreakpad_client.la + src_processor_address_map_unittest_SOURCES = \ src/processor/address_map_unittest.cc src_processor_address_map_unittest_LDADD = \ @@ -142,9 +202,25 @@ src/processor/basic_source_line_resolver_unittest.cc src_processor_basic_source_line_resolver_unittest_LDADD = \ src/processor/basic_source_line_resolver.lo \ + src/processor/cfi_frame_info.lo \ src/processor/pathname_stripper.lo \ src/processor/logging.lo +src_processor_cfi_frame_info_unittest_SOURCES = \ + src/processor/cfi_frame_info_unittest.cc \ + src/testing/gtest/src/gtest-all.cc \ + src/testing/gtest/src/gtest_main.cc \ + src/testing/src/gmock-all.cc +src_processor_cfi_frame_info_unittest_LDADD = \ + src/processor/cfi_frame_info.lo \ + src/processor/logging.lo \ + src/processor/pathname_stripper.lo +src_processor_cfi_frame_info_unittest_CPPFLAGS = \ + -I$(top_srcdir)/src/testing/include \ + -I$(top_srcdir)/src/testing/gtest/include \ + -I$(top_srcdir)/src/testing/gtest \ + -I$(top_srcdir)/src/testing + src_processor_contained_range_map_unittest_SOURCES = \ src/processor/contained_range_map_unittest.cc src_processor_contained_range_map_unittest_LDADD = \ @@ -152,11 +228,19 @@ src/processor/pathname_stripper.lo src_processor_minidump_processor_unittest_SOURCES = \ - src/processor/minidump_processor_unittest.cc + src/processor/minidump_processor_unittest.cc \ + src/testing/gtest/src/gtest-all.cc \ + src/testing/src/gmock-all.cc +src_processor_minidump_processor_unittest_CPPFLAGS = \ + -I$(top_srcdir)/src/testing/include \ + -I$(top_srcdir)/src/testing/gtest/include \ + -I$(top_srcdir)/src/testing/gtest \ + -I$(top_srcdir)/src/testing src_processor_minidump_processor_unittest_LDADD = \ src/processor/basic_code_modules.lo \ src/processor/basic_source_line_resolver.lo \ src/processor/call_stack.lo \ + src/processor/cfi_frame_info.lo \ src/processor/logging.lo \ src/processor/minidump_processor.lo \ src/processor/minidump.lo \ @@ -164,10 +248,29 @@ src/processor/process_state.lo \ src/processor/stackwalker.lo \ src/processor/stackwalker_amd64.lo \ + src/processor/stackwalker_arm.lo \ src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_sparc.lo \ src/processor/stackwalker_x86.lo +src_processor_minidump_unittest_SOURCES = \ + src/processor/minidump_unittest.cc \ + src/processor/synth_minidump.cc \ + src/processor/test_assembler.cc \ + src/testing/gtest/src/gtest-all.cc \ + src/testing/gtest/src/gtest_main.cc \ + src/testing/src/gmock-all.cc +src_processor_minidump_unittest_CPPFLAGS = \ + -I$(top_srcdir)/src/testing/include \ + -I$(top_srcdir)/src/testing/gtest/include \ + -I$(top_srcdir)/src/testing/gtest \ + -I$(top_srcdir)/src/testing +src_processor_minidump_unittest_LDADD = \ + src/processor/basic_code_modules.lo \ + src/processor/logging.lo \ + src/processor/minidump.lo \ + src/processor/pathname_stripper.lo + src_processor_pathname_stripper_unittest_SOURCES = \ src/processor/pathname_stripper_unittest.cc src_processor_pathname_stripper_unittest_LDADD = \ @@ -196,10 +299,81 @@ src/processor/pathname_stripper.lo \ src/processor/stackwalker.lo \ src/processor/stackwalker_amd64.lo \ + src/processor/stackwalker_arm.lo \ src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_sparc.lo \ src/processor/stackwalker_x86.lo +src_processor_stackwalker_amd64_unittest_SOURCES = \ + src/processor/stackwalker_amd64_unittest.cc \ + src/processor/test_assembler.cc \ + src/testing/gtest/src/gtest-all.cc \ + src/testing/gtest/src/gtest_main.cc \ + src/testing/src/gmock-all.cc +src_processor_stackwalker_amd64_unittest_LDADD = \ + src/libbreakpad.la +src_processor_stackwalker_amd64_unittest_CPPFLAGS = \ + -I$(top_srcdir)/src/testing/include \ + -I$(top_srcdir)/src/testing/gtest/include \ + -I$(top_srcdir)/src/testing/gtest \ + -I$(top_srcdir)/src/testing + +src_processor_stackwalker_arm_unittest_SOURCES = \ + src/processor/stackwalker_arm_unittest.cc \ + src/processor/test_assembler.cc \ + src/testing/gtest/src/gtest-all.cc \ + src/testing/gtest/src/gtest_main.cc \ + src/testing/src/gmock-all.cc +src_processor_stackwalker_arm_unittest_LDADD = \ + src/libbreakpad.la +src_processor_stackwalker_arm_unittest_CPPFLAGS = \ + -I$(top_srcdir)/src/testing/include \ + -I$(top_srcdir)/src/testing/gtest/include \ + -I$(top_srcdir)/src/testing/gtest \ + -I$(top_srcdir)/src/testing + +src_processor_stackwalker_x86_unittest_SOURCES = \ + src/processor/stackwalker_x86_unittest.cc \ + src/processor/test_assembler.cc \ + src/testing/gtest/src/gtest-all.cc \ + src/testing/gtest/src/gtest_main.cc \ + src/testing/src/gmock-all.cc +src_processor_stackwalker_x86_unittest_LDADD = \ + src/libbreakpad.la +src_processor_stackwalker_x86_unittest_CPPFLAGS = \ + -I$(top_srcdir)/src/testing/include \ + -I$(top_srcdir)/src/testing/gtest/include \ + -I$(top_srcdir)/src/testing/gtest \ + -I$(top_srcdir)/src/testing + +src_processor_synth_minidump_unittest_SOURCES = \ + src/processor/synth_minidump_unittest.cc \ + src/testing/gtest/src/gtest-all.cc \ + src/testing/gtest/src/gtest_main.cc \ + src/testing/src/gmock-all.cc \ + src/processor/synth_minidump.cc \ + src/processor/synth_minidump.h \ + src/processor/test_assembler.cc \ + src/processor/test_assembler.h +src_processor_synth_minidump_unittest_CPPFLAGS = \ + -I$(top_srcdir)/src/testing/include \ + -I$(top_srcdir)/src/testing/gtest/include \ + -I$(top_srcdir)/src/testing/gtest \ + -I$(top_srcdir)/src/testing + +src_processor_test_assembler_unittest_SOURCES = \ + src/processor/test_assembler_unittest.cc \ + src/testing/gtest/src/gtest-all.cc \ + src/testing/gtest/src/gtest_main.cc \ + src/testing/src/gmock-all.cc \ + src/processor/test_assembler.cc \ + src/processor/test_assembler.h +src_processor_test_assembler_unittest_CPPFLAGS = \ + -I$(top_srcdir)/src/testing/include \ + -I$(top_srcdir)/src/testing/gtest/include \ + -I$(top_srcdir)/src/testing/gtest \ + -I$(top_srcdir)/src/testing + ## Non-installables noinst_PROGRAMS = noinst_SCRIPTS = $(check_SCRIPTS) @@ -218,6 +392,7 @@ src/processor/basic_code_modules.lo \ src/processor/basic_source_line_resolver.lo \ src/processor/call_stack.lo \ + src/processor/cfi_frame_info.lo \ src/processor/logging.lo \ src/processor/minidump.lo \ src/processor/minidump_processor.lo \ @@ -226,6 +401,7 @@ src/processor/simple_symbol_supplier.lo \ src/processor/stackwalker.lo \ src/processor/stackwalker_amd64.lo \ + src/processor/stackwalker_arm.lo \ src/processor/stackwalker_ppc.lo \ src/processor/stackwalker_sparc.lo \ src/processor/stackwalker_x86.lo @@ -371,3 +547,4 @@ ## Additional rules libtool: $(LIBTOOL_DEPS) $(SHELL) ./config.status --recheck + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/Makefile.in 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/Makefile.in 2010-04-16 17:32:47.000000000 +0100 @@ -147,8 +147,9 @@ src_processor_minidump_dump_DEPENDENCIES = \ src/processor/basic_code_modules.lo src/processor/logging.lo \ src/processor/minidump.lo src/processor/pathname_stripper.lo -am_src_processor_minidump_processor_unittest_OBJECTS = \ - src/processor/minidump_processor_unittest.$(OBJEXT) +am_src_processor_minidump_processor_unittest_OBJECTS = src/processor/src_processor_minidump_processor_unittest-minidump_processor_unittest.$(OBJEXT) \ + src/testing/gtest/src/src_processor_minidump_processor_unittest-gtest-all.$(OBJEXT) \ + src/testing/src/src_processor_minidump_processor_unittest-gmock-all.$(OBJEXT) src_processor_minidump_processor_unittest_OBJECTS = \ $(am_src_processor_minidump_processor_unittest_OBJECTS) src_processor_minidump_processor_unittest_DEPENDENCIES = \ @@ -289,6 +290,7 @@ CYGPATH_W = @CYGPATH_W@ DEFS = @DEFS@ DEPDIR = @DEPDIR@ +DSYMUTIL = @DSYMUTIL@ ECHO = @ECHO@ ECHO_C = @ECHO_C@ ECHO_N = @ECHO_N@ @@ -312,6 +314,7 @@ LTLIBOBJS = @LTLIBOBJS@ MAKEINFO = @MAKEINFO@ MKDIR_P = @MKDIR_P@ +NMEDIT = @NMEDIT@ OBJEXT = @OBJEXT@ PACKAGE = @PACKAGE@ PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ @@ -321,6 +324,7 @@ PACKAGE_VERSION = @PACKAGE_VERSION@ PATH_SEPARATOR = @PATH_SEPARATOR@ RANLIB = @RANLIB@ +SED = @SED@ SET_MAKE = @SET_MAKE@ SHELL = @SHELL@ STRIP = @STRIP@ @@ -471,7 +475,15 @@ src/processor/pathname_stripper.lo src_processor_minidump_processor_unittest_SOURCES = \ - src/processor/minidump_processor_unittest.cc + src/processor/minidump_processor_unittest.cc \ + src/testing/gtest/src/gtest-all.cc \ + src/testing/src/gmock-all.cc + +src_processor_minidump_processor_unittest_CPPFLAGS = \ + -I$(top_srcdir)/src/testing/include \ + -I$(top_srcdir)/src/testing/gtest/include \ + -I$(top_srcdir)/src/testing/gtest \ + -I$(top_srcdir)/src/testing src_processor_minidump_processor_unittest_LDADD = \ src/processor/basic_code_modules.lo \ @@ -871,9 +883,27 @@ src/processor/minidump_dump$(EXEEXT): $(src_processor_minidump_dump_OBJECTS) $(src_processor_minidump_dump_DEPENDENCIES) src/processor/$(am__dirstamp) @rm -f src/processor/minidump_dump$(EXEEXT) $(CXXLINK) $(src_processor_minidump_dump_OBJECTS) $(src_processor_minidump_dump_LDADD) $(LIBS) -src/processor/minidump_processor_unittest.$(OBJEXT): \ +src/processor/src_processor_minidump_processor_unittest-minidump_processor_unittest.$(OBJEXT): \ src/processor/$(am__dirstamp) \ src/processor/$(DEPDIR)/$(am__dirstamp) +src/testing/gtest/src/$(am__dirstamp): + @$(MKDIR_P) src/testing/gtest/src + @: > src/testing/gtest/src/$(am__dirstamp) +src/testing/gtest/src/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/testing/gtest/src/$(DEPDIR) + @: > src/testing/gtest/src/$(DEPDIR)/$(am__dirstamp) +src/testing/gtest/src/src_processor_minidump_processor_unittest-gtest-all.$(OBJEXT): \ + src/testing/gtest/src/$(am__dirstamp) \ + src/testing/gtest/src/$(DEPDIR)/$(am__dirstamp) +src/testing/src/$(am__dirstamp): + @$(MKDIR_P) src/testing/src + @: > src/testing/src/$(am__dirstamp) +src/testing/src/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/testing/src/$(DEPDIR) + @: > src/testing/src/$(DEPDIR)/$(am__dirstamp) +src/testing/src/src_processor_minidump_processor_unittest-gmock-all.$(OBJEXT): \ + src/testing/src/$(am__dirstamp) \ + src/testing/src/$(DEPDIR)/$(am__dirstamp) src/processor/minidump_processor_unittest$(EXEEXT): $(src_processor_minidump_processor_unittest_OBJECTS) $(src_processor_minidump_processor_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) @rm -f src/processor/minidump_processor_unittest$(EXEEXT) $(CXXLINK) $(src_processor_minidump_processor_unittest_OBJECTS) $(src_processor_minidump_processor_unittest_LDADD) $(LIBS) @@ -926,7 +956,6 @@ -rm -f src/processor/minidump_dump.$(OBJEXT) -rm -f src/processor/minidump_processor.$(OBJEXT) -rm -f src/processor/minidump_processor.lo - -rm -f src/processor/minidump_processor_unittest.$(OBJEXT) -rm -f src/processor/minidump_stackwalk.$(OBJEXT) -rm -f src/processor/pathname_stripper.$(OBJEXT) -rm -f src/processor/pathname_stripper.lo @@ -937,6 +966,7 @@ -rm -f src/processor/range_map_unittest.$(OBJEXT) -rm -f src/processor/simple_symbol_supplier.$(OBJEXT) -rm -f src/processor/simple_symbol_supplier.lo + -rm -f src/processor/src_processor_minidump_processor_unittest-minidump_processor_unittest.$(OBJEXT) -rm -f src/processor/stackwalker.$(OBJEXT) -rm -f src/processor/stackwalker.lo -rm -f src/processor/stackwalker_amd64.$(OBJEXT) @@ -948,6 +978,8 @@ -rm -f src/processor/stackwalker_sparc.lo -rm -f src/processor/stackwalker_x86.$(OBJEXT) -rm -f src/processor/stackwalker_x86.lo + -rm -f src/testing/gtest/src/src_processor_minidump_processor_unittest-gtest-all.$(OBJEXT) + -rm -f src/testing/src/src_processor_minidump_processor_unittest-gmock-all.$(OBJEXT) distclean-compile: -rm -f *.tab.c @@ -962,7 +994,6 @@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_dump.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_processor.Plo@am__quote@ -@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_processor_unittest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_stackwalk.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/pathname_stripper.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/pathname_stripper_unittest.Po@am__quote@ @@ -970,12 +1001,15 @@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/process_state.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/range_map_unittest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/simple_symbol_supplier.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/src_processor_minidump_processor_unittest-minidump_processor_unittest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_amd64.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_ppc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_selftest.Po@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_sparc.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_x86.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/testing/gtest/src/$(DEPDIR)/src_processor_minidump_processor_unittest-gtest-all.Po@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@src/testing/src/$(DEPDIR)/src_processor_minidump_processor_unittest-gmock-all.Po@am__quote@ .cc.o: @am__fastdepCXX_TRUE@ depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @@ -1001,6 +1035,48 @@ @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $< +src/processor/src_processor_minidump_processor_unittest-minidump_processor_unittest.o: src/processor/minidump_processor_unittest.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/src_processor_minidump_processor_unittest-minidump_processor_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/src_processor_minidump_processor_unittest-minidump_processor_unittest.Tpo -c -o src/processor/src_processor_minidump_processor_unittest-minidump_processor_unittest.o `test -f 'src/processor/minidump_processor_unittest.cc' || echo '$(srcdir)/'`src/processor/minidump_processor_unittest.cc +@am__fastdepCXX_TRUE@ mv -f src/processor/$(DEPDIR)/src_processor_minidump_processor_unittest-minidump_processor_unittest.Tpo src/processor/$(DEPDIR)/src_processor_minidump_processor_unittest-minidump_processor_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/processor/minidump_processor_unittest.cc' object='src/processor/src_processor_minidump_processor_unittest-minidump_processor_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/src_processor_minidump_processor_unittest-minidump_processor_unittest.o `test -f 'src/processor/minidump_processor_unittest.cc' || echo '$(srcdir)/'`src/processor/minidump_processor_unittest.cc + +src/processor/src_processor_minidump_processor_unittest-minidump_processor_unittest.obj: src/processor/minidump_processor_unittest.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/src_processor_minidump_processor_unittest-minidump_processor_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/src_processor_minidump_processor_unittest-minidump_processor_unittest.Tpo -c -o src/processor/src_processor_minidump_processor_unittest-minidump_processor_unittest.obj `if test -f 'src/processor/minidump_processor_unittest.cc'; then $(CYGPATH_W) 'src/processor/minidump_processor_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/minidump_processor_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ mv -f src/processor/$(DEPDIR)/src_processor_minidump_processor_unittest-minidump_processor_unittest.Tpo src/processor/$(DEPDIR)/src_processor_minidump_processor_unittest-minidump_processor_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/processor/minidump_processor_unittest.cc' object='src/processor/src_processor_minidump_processor_unittest-minidump_processor_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/src_processor_minidump_processor_unittest-minidump_processor_unittest.obj `if test -f 'src/processor/minidump_processor_unittest.cc'; then $(CYGPATH_W) 'src/processor/minidump_processor_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/minidump_processor_unittest.cc'; fi` + +src/testing/gtest/src/src_processor_minidump_processor_unittest-gtest-all.o: src/testing/gtest/src/gtest-all.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/gtest/src/src_processor_minidump_processor_unittest-gtest-all.o -MD -MP -MF src/testing/gtest/src/$(DEPDIR)/src_processor_minidump_processor_unittest-gtest-all.Tpo -c -o src/testing/gtest/src/src_processor_minidump_processor_unittest-gtest-all.o `test -f 'src/testing/gtest/src/gtest-all.cc' || echo '$(srcdir)/'`src/testing/gtest/src/gtest-all.cc +@am__fastdepCXX_TRUE@ mv -f src/testing/gtest/src/$(DEPDIR)/src_processor_minidump_processor_unittest-gtest-all.Tpo src/testing/gtest/src/$(DEPDIR)/src_processor_minidump_processor_unittest-gtest-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/testing/gtest/src/gtest-all.cc' object='src/testing/gtest/src/src_processor_minidump_processor_unittest-gtest-all.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/gtest/src/src_processor_minidump_processor_unittest-gtest-all.o `test -f 'src/testing/gtest/src/gtest-all.cc' || echo '$(srcdir)/'`src/testing/gtest/src/gtest-all.cc + +src/testing/gtest/src/src_processor_minidump_processor_unittest-gtest-all.obj: src/testing/gtest/src/gtest-all.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/gtest/src/src_processor_minidump_processor_unittest-gtest-all.obj -MD -MP -MF src/testing/gtest/src/$(DEPDIR)/src_processor_minidump_processor_unittest-gtest-all.Tpo -c -o src/testing/gtest/src/src_processor_minidump_processor_unittest-gtest-all.obj `if test -f 'src/testing/gtest/src/gtest-all.cc'; then $(CYGPATH_W) 'src/testing/gtest/src/gtest-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/gtest/src/gtest-all.cc'; fi` +@am__fastdepCXX_TRUE@ mv -f src/testing/gtest/src/$(DEPDIR)/src_processor_minidump_processor_unittest-gtest-all.Tpo src/testing/gtest/src/$(DEPDIR)/src_processor_minidump_processor_unittest-gtest-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/testing/gtest/src/gtest-all.cc' object='src/testing/gtest/src/src_processor_minidump_processor_unittest-gtest-all.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/gtest/src/src_processor_minidump_processor_unittest-gtest-all.obj `if test -f 'src/testing/gtest/src/gtest-all.cc'; then $(CYGPATH_W) 'src/testing/gtest/src/gtest-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/gtest/src/gtest-all.cc'; fi` + +src/testing/src/src_processor_minidump_processor_unittest-gmock-all.o: src/testing/src/gmock-all.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/src/src_processor_minidump_processor_unittest-gmock-all.o -MD -MP -MF src/testing/src/$(DEPDIR)/src_processor_minidump_processor_unittest-gmock-all.Tpo -c -o src/testing/src/src_processor_minidump_processor_unittest-gmock-all.o `test -f 'src/testing/src/gmock-all.cc' || echo '$(srcdir)/'`src/testing/src/gmock-all.cc +@am__fastdepCXX_TRUE@ mv -f src/testing/src/$(DEPDIR)/src_processor_minidump_processor_unittest-gmock-all.Tpo src/testing/src/$(DEPDIR)/src_processor_minidump_processor_unittest-gmock-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/testing/src/gmock-all.cc' object='src/testing/src/src_processor_minidump_processor_unittest-gmock-all.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/src/src_processor_minidump_processor_unittest-gmock-all.o `test -f 'src/testing/src/gmock-all.cc' || echo '$(srcdir)/'`src/testing/src/gmock-all.cc + +src/testing/src/src_processor_minidump_processor_unittest-gmock-all.obj: src/testing/src/gmock-all.cc +@am__fastdepCXX_TRUE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/src/src_processor_minidump_processor_unittest-gmock-all.obj -MD -MP -MF src/testing/src/$(DEPDIR)/src_processor_minidump_processor_unittest-gmock-all.Tpo -c -o src/testing/src/src_processor_minidump_processor_unittest-gmock-all.obj `if test -f 'src/testing/src/gmock-all.cc'; then $(CYGPATH_W) 'src/testing/src/gmock-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/src/gmock-all.cc'; fi` +@am__fastdepCXX_TRUE@ mv -f src/testing/src/$(DEPDIR)/src_processor_minidump_processor_unittest-gmock-all.Tpo src/testing/src/$(DEPDIR)/src_processor_minidump_processor_unittest-gmock-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='src/testing/src/gmock-all.cc' object='src/testing/src/src_processor_minidump_processor_unittest-gmock-all.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/src/src_processor_minidump_processor_unittest-gmock-all.obj `if test -f 'src/testing/src/gmock-all.cc'; then $(CYGPATH_W) 'src/testing/src/gmock-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/src/gmock-all.cc'; fi` + mostlyclean-libtool: -rm -f *.lo @@ -1309,6 +1385,10 @@ -rm -f src/$(am__dirstamp) -rm -f src/processor/$(DEPDIR)/$(am__dirstamp) -rm -f src/processor/$(am__dirstamp) + -rm -f src/testing/gtest/src/$(DEPDIR)/$(am__dirstamp) + -rm -f src/testing/gtest/src/$(am__dirstamp) + -rm -f src/testing/src/$(DEPDIR)/$(am__dirstamp) + -rm -f src/testing/src/$(am__dirstamp) maintainer-clean-generic: @echo "This command is intended for maintainers to use" @@ -1321,7 +1401,7 @@ distclean: distclean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) - -rm -rf src/processor/$(DEPDIR) + -rm -rf src/processor/$(DEPDIR) src/testing/gtest/src/$(DEPDIR) src/testing/src/$(DEPDIR) -rm -f Makefile distclean-am: clean-am distclean-compile distclean-generic \ distclean-hdr distclean-libtool distclean-tags @@ -1357,7 +1437,7 @@ maintainer-clean: maintainer-clean-am -rm -f $(am__CONFIG_DISTCLEAN_FILES) -rm -rf $(top_srcdir)/autom4te.cache - -rm -rf src/processor/$(DEPDIR) + -rm -rf src/processor/$(DEPDIR) src/testing/gtest/src/$(DEPDIR) src/testing/src/$(DEPDIR) -rm -f Makefile maintainer-clean-am: distclean-am maintainer-clean-generic diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/breakpad_googletest_includes.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/breakpad_googletest_includes.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/breakpad_googletest_includes.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/breakpad_googletest_includes.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,36 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef BREAKPAD_GOOGLETEST_INCLUDES_H__ +#define BREAKPAD_GOOGLETEST_INCLUDES_H__ + +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/include/gmock/gmock.h" + +#endif // BREAKPAD_GOOGLETEST_INCLUDES_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/client_info.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/client_info.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/client_info.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/client_info.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,46 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ +#define CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ + +namespace google_breakpad { + +class CrashGenerationServer; + +struct ClientInfo { + pid_t pid() const { return pid_; } + + CrashGenerationServer* crash_server_; + pid_t pid_; +}; + +} + +#endif // CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_client.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_client.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_client.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_client.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,88 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include + +#include + +#include "client/linux/crash_generation/crash_generation_client.h" +#include "common/linux/eintr_wrapper.h" +#include "common/linux/linux_libc_support.h" +#include "common/linux/linux_syscall_support.h" + +namespace google_breakpad { + +bool +CrashGenerationClient::RequestDump(const void* blob, size_t blob_size) +{ + int fds[2]; + sys_socketpair(AF_UNIX, SOCK_STREAM, 0, fds); + static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int)); + + struct kernel_msghdr msg; + my_memset(&msg, 0, sizeof(struct kernel_msghdr)); + struct kernel_iovec iov[1]; + iov[0].iov_base = const_cast(blob); + iov[0].iov_len = blob_size; + + msg.msg_iov = iov; + msg.msg_iovlen = sizeof(iov) / sizeof(iov[0]); + char cmsg[kControlMsgSize]; + my_memset(cmsg, 0, kControlMsgSize); + msg.msg_control = cmsg; + msg.msg_controllen = sizeof(cmsg); + + struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_SOCKET; + hdr->cmsg_type = SCM_RIGHTS; + hdr->cmsg_len = CMSG_LEN(sizeof(int)); + *((int*) CMSG_DATA(hdr)) = fds[1]; + + HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0)); + sys_close(fds[1]); + + // wait for an ACK from the server + char b; + HANDLE_EINTR(sys_read(fds[0], &b, 1)); + + return true; +} + +//static +CrashGenerationClient* +CrashGenerationClient::TryCreate(int server_fd) +{ + if (0 > server_fd) + return NULL; + return new CrashGenerationClient(server_fd); +} + +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_client.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_client.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_client.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_client.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,69 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ +#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ + +#include + +namespace google_breakpad { + +class CrashGenerationClient { +public: + ~CrashGenerationClient() + { + } + + // Request the crash server to generate a dump. |blob| is a hack, + // see exception_handler.h and minidump_writer.h + // + // Return true if the dump was successful; false otherwise. + bool RequestDump(const void* blob, size_t blob_size); + + // Return a new CrashGenerationClient if |server_fd| is valid and + // connects to a CrashGenerationServer. Otherwise, return NULL. + // The returned CrashGenerationClient* is owned by the caller of + // this function. + static CrashGenerationClient* TryCreate(int server_fd); + +private: + CrashGenerationClient(int server_fd) : server_fd_(server_fd) + { + } + + int server_fd_; + + // prevent copy construction and assignment + CrashGenerationClient(const CrashGenerationClient&); + CrashGenerationClient& operator=(const CrashGenerationClient&); +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_server.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_server.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_server.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_server.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,466 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client/linux/crash_generation/crash_generation_server.h" +#include "client/linux/crash_generation/client_info.h" +#include "client/linux/handler/exception_handler.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "common/linux/eintr_wrapper.h" +#include "common/linux/guid_creator.h" + +static const char kCommandQuit = 'x'; + +static bool +GetInodeForFileDescriptor(ino_t* inode_out, int fd) +{ + assert(inode_out); + + struct stat buf; + if (fstat(fd, &buf) < 0) + return false; + + if (!S_ISSOCK(buf.st_mode)) + return false; + + *inode_out = buf.st_ino; + return true; +} + +// expected prefix of the target of the /proc/self/fd/%d link for a socket +static const char kSocketLinkPrefix[] = "socket:["; + +// Parse a symlink in /proc/pid/fd/$x and return the inode number of the +// socket. +// inode_out: (output) set to the inode number on success +// path: e.g. /proc/1234/fd/5 (must be a UNIX domain socket descriptor) +static bool +GetInodeForProcPath(ino_t* inode_out, const char* path) +{ + assert(inode_out); + assert(path); + + char buf[256]; + const ssize_t n = readlink(path, buf, sizeof(buf) - 1); + if (n == -1) { + return false; + } + buf[n] = 0; + + if (0 != memcmp(kSocketLinkPrefix, buf, sizeof(kSocketLinkPrefix) - 1)) { + return false; + } + + char* endptr; + const u_int64_t inode_ul = + strtoull(buf + sizeof(kSocketLinkPrefix) - 1, &endptr, 10); + if (*endptr != ']') + return false; + + if (inode_ul == ULLONG_MAX) { + return false; + } + + *inode_out = inode_ul; + return true; +} + +static bool +FindProcessHoldingSocket(pid_t* pid_out, ino_t socket_inode) +{ + assert(pid_out); + bool already_found = false; + + DIR* proc = opendir("/proc"); + if (!proc) { + return false; + } + + std::vector pids; + + struct dirent* dent; + while ((dent = readdir(proc))) { + char* endptr; + const unsigned long int pid_ul = strtoul(dent->d_name, &endptr, 10); + if (pid_ul == ULONG_MAX || '\0' != *endptr) + continue; + pids.push_back(pid_ul); + } + closedir(proc); + + for (std::vector::const_iterator + i = pids.begin(); i != pids.end(); ++i) { + const pid_t current_pid = *i; + char buf[256]; + snprintf(buf, sizeof(buf), "/proc/%d/fd", current_pid); + DIR* fd = opendir(buf); + if (!fd) + continue; + + while ((dent = readdir(fd))) { + if (snprintf(buf, sizeof(buf), "/proc/%d/fd/%s", current_pid, + dent->d_name) >= static_cast(sizeof(buf))) { + continue; + } + + ino_t fd_inode; + if (GetInodeForProcPath(&fd_inode, buf) + && fd_inode == socket_inode) { + if (already_found) { + closedir(fd); + return false; + } + + already_found = true; + *pid_out = current_pid; + break; + } + } + + closedir(fd); + } + + return already_found; +} + +namespace google_breakpad { + +CrashGenerationServer::CrashGenerationServer( + const int listen_fd, + OnClientDumpRequestCallback dump_callback, + void* dump_context, + OnClientExitingCallback exit_callback, + void* exit_context, + bool generate_dumps, + const std::string* dump_path) : + server_fd_(listen_fd), + dump_callback_(dump_callback), + dump_context_(dump_context), + exit_callback_(exit_callback), + exit_context_(exit_context), + generate_dumps_(generate_dumps), + started_(false) +{ + if (dump_path) + dump_dir_ = *dump_path; + else + dump_dir_ = "/tmp"; +} + +CrashGenerationServer::~CrashGenerationServer() +{ + if (started_) + Stop(); +} + +bool +CrashGenerationServer::Start() +{ + if (started_ || 0 > server_fd_) + return false; + + int control_pipe[2]; + if (pipe(control_pipe)) + return false; + + if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC)) + return false; + if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC)) + return false; + + if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK)) + return false; + + control_pipe_in_ = control_pipe[0]; + control_pipe_out_ = control_pipe[1]; + + if (pthread_create(&thread_, NULL, + ThreadMain, reinterpret_cast(this))) + return false; + + started_ = true; + return true; +} + +void +CrashGenerationServer::Stop() +{ + assert(pthread_self() != thread_); + + if (!started_) + return; + + HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1)); + + void* dummy; + pthread_join(thread_, &dummy); + + started_ = false; +} + +//static +bool +CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd) +{ + int fds[2]; + + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)) + return false; + + static const int on = 1; + // Enable passcred on the server end of the socket + if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) + return false; + + if (fcntl(fds[1], F_SETFL, O_NONBLOCK)) + return false; + if (fcntl(fds[1], F_SETFD, FD_CLOEXEC)) + return false; + + *client_fd = fds[0]; + *server_fd = fds[1]; + return true; +} + +// The following methods/functions execute on the server thread + +void +CrashGenerationServer::Run() +{ + struct pollfd pollfds[2]; + memset(&pollfds, 0, sizeof(pollfds)); + + pollfds[0].fd = server_fd_; + pollfds[0].events = POLLIN; + + pollfds[1].fd = control_pipe_in_; + pollfds[1].events = POLLIN; + + while (true) { + // infinite timeout + int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1); + if (-1 == nevents) { + if (EINTR == errno) { + continue; + } else { + return; + } + } + + if (pollfds[0].revents && !ClientEvent(pollfds[0].revents)) + return; + + if (pollfds[1].revents && !ControlEvent(pollfds[1].revents)) + return; + } +} + +bool +CrashGenerationServer::ClientEvent(short revents) +{ + if (POLLHUP & revents) + return false; + assert(POLLIN & revents); + + // A process has crashed and has signaled us by writing a datagram + // to the death signal socket. The datagram contains the crash context needed + // for writing the minidump as well as a file descriptor and a credentials + // block so that they can't lie about their pid. + + // The length of the control message: + static const unsigned kControlMsgSize = + CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); + // The length of the regular payload: + static const unsigned kCrashContextSize = + sizeof(google_breakpad::ExceptionHandler::CrashContext); + + struct msghdr msg = {0}; + struct iovec iov[1]; + char crash_context[kCrashContextSize]; + char control[kControlMsgSize]; + const ssize_t expected_msg_size = sizeof(crash_context); + + iov[0].iov_base = crash_context; + iov[0].iov_len = sizeof(crash_context); + msg.msg_iov = iov; + msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]); + msg.msg_control = control; + msg.msg_controllen = kControlMsgSize; + + const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0)); + if (msg_size != expected_msg_size) + return true; + + if (msg.msg_controllen != kControlMsgSize || + msg.msg_flags & ~MSG_TRUNC) + return true; + + // Walk the control payload and extract the file descriptor and validated pid. + pid_t crashing_pid = -1; + int signal_fd = -1; + for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; + hdr = CMSG_NXTHDR(&msg, hdr)) { + if (hdr->cmsg_level != SOL_SOCKET) + continue; + if (hdr->cmsg_type == SCM_RIGHTS) { + const unsigned len = hdr->cmsg_len - + (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); + assert(len % sizeof(int) == 0u); + const unsigned num_fds = len / sizeof(int); + if (num_fds > 1 || num_fds == 0) { + // A nasty process could try and send us too many descriptors and + // force a leak. + for (unsigned i = 0; i < num_fds; ++i) + HANDLE_EINTR(close(reinterpret_cast(CMSG_DATA(hdr))[i])); + return true; + } else { + signal_fd = reinterpret_cast(CMSG_DATA(hdr))[0]; + } + } else if (hdr->cmsg_type == SCM_CREDENTIALS) { + const struct ucred *cred = + reinterpret_cast(CMSG_DATA(hdr)); + crashing_pid = cred->pid; + } + } + + if (crashing_pid == -1 || signal_fd == -1) { + if (signal_fd) + HANDLE_EINTR(close(signal_fd)); + return true; + } + + // Kernel bug workaround (broken in 2.6.30 at least): + // The kernel doesn't translate PIDs in SCM_CREDENTIALS across PID + // namespaces. Thus |crashing_pid| might be garbage from our point of view. + // In the future we can remove this workaround, but we have to wait a couple + // of years to be sure that it's worked its way out into the world. + + ino_t inode_number; + if (!GetInodeForFileDescriptor(&inode_number, signal_fd)) { + HANDLE_EINTR(close(signal_fd)); + return true; + } + + if (!FindProcessHoldingSocket(&crashing_pid, inode_number - 1)) { + HANDLE_EINTR(close(signal_fd)); + return true; + } + + std::string minidump_filename; + if (!MakeMinidumpFilename(minidump_filename)) + return true; + + if (!google_breakpad::WriteMinidump(minidump_filename.c_str(), + crashing_pid, crash_context, + kCrashContextSize)) { + HANDLE_EINTR(close(signal_fd)); + return true; + } + + if (dump_callback_) { + ClientInfo info; + + info.crash_server_ = this; + info.pid_ = crashing_pid; + + dump_callback_(dump_context_, &info, &minidump_filename); + } + + // Send the done signal to the process: it can exit now. + memset(&msg, 0, sizeof(msg)); + struct iovec done_iov; + done_iov.iov_base = const_cast("\x42"); + done_iov.iov_len = 1; + msg.msg_iov = &done_iov; + msg.msg_iovlen = 1; + + HANDLE_EINTR(sendmsg(signal_fd, &msg, MSG_DONTWAIT | MSG_NOSIGNAL)); + HANDLE_EINTR(close(signal_fd)); + + return true; +} + +bool +CrashGenerationServer::ControlEvent(short revents) +{ + if (POLLHUP & revents) + return false; + assert(POLLIN & revents); + + char command; + if (read(control_pipe_in_, &command, 1)) + return false; + + switch (command) { + case kCommandQuit: + return false; + default: + assert(0); + } + + return true; +} + +bool +CrashGenerationServer::MakeMinidumpFilename(std::string& outFilename) +{ + GUID guid; + char guidString[kGUIDStringLength+1]; + + if (!(CreateGUID(&guid) + && GUIDToString(&guid, guidString, sizeof(guidString)))) + return false; + + char path[PATH_MAX]; + snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString); + + outFilename = path; + return true; +} + +// static +void* +CrashGenerationServer::ThreadMain(void *arg) +{ + reinterpret_cast(arg)->Run(); + return NULL; +} + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_server.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_server.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_server.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/crash_generation_server.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,133 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ +#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ + +#include + +#include + +namespace google_breakpad { + +class ClientInfo; + +class CrashGenerationServer { +public: + // WARNING: callbacks may be invoked on a different thread + // than that which creates the CrashGenerationServer. They must + // be thread safe. + typedef void (*OnClientDumpRequestCallback)(void* context, + const ClientInfo* client_info, + const std::string* file_path); + + typedef void (*OnClientExitingCallback)(void* context, + const ClientInfo* client_info); + + // Create an instance with the given parameters. + // + // Parameter listen_fd: The server fd created by CreateReportChannel(). + // Parameter dump_callback: Callback for a client crash dump request. + // Parameter dump_context: Context for client crash dump request callback. + // Parameter exit_callback: Callback for client process exit. + // Parameter exit_context: Context for client exit callback. + // Parameter generate_dumps: Whether to automatically generate dumps. + // Client code of this class might want to generate dumps explicitly + // in the crash dump request callback. In that case, false can be + // passed for this parameter. + // Parameter dump_path: Path for generating dumps; required only if true is + // passed for generateDumps parameter; NULL can be passed otherwise. + CrashGenerationServer(const int listen_fd, + OnClientDumpRequestCallback dump_callback, + void* dump_context, + OnClientExitingCallback exit_callback, + void* exit_context, + bool generate_dumps, + const std::string* dump_path); + + ~CrashGenerationServer(); + + // Perform initialization steps needed to start listening to clients. + // + // Return true if initialization is successful; false otherwise. + bool Start(); + + // Stop the server. + void Stop(); + + // Create a "channel" that can be used by clients to report crashes + // to a CrashGenerationServer. |*server_fd| should be passed to + // this class's constructor, and |*client_fd| should be passed to + // the ExceptionHandler constructor in the client process. + static bool CreateReportChannel(int* server_fd, int* client_fd); + +private: + // Run the server's event loop + void Run(); + + // Invoked when an child process (client) event occurs + // Returning true => "keep running", false => "exit loop" + bool ClientEvent(short revents); + + // Invoked when the controlling thread (main) event occurs + // Returning true => "keep running", false => "exit loop" + bool ControlEvent(short revents); + + // Return a unique filename at which a minidump can be written + bool MakeMinidumpFilename(std::string& outFilename); + + // Trampoline to |Run()| + static void* ThreadMain(void* arg); + + int server_fd_; + + OnClientDumpRequestCallback dump_callback_; + void* dump_context_; + + OnClientExitingCallback exit_callback_; + void* exit_context_; + + bool generate_dumps_; + + std::string dump_dir_; + + bool started_; + + pthread_t thread_; + int control_pipe_in_; + int control_pipe_out_; + + // disable these + CrashGenerationServer(const CrashGenerationServer&); + CrashGenerationServer& operator=(const CrashGenerationServer&); +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/Makefile.in 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/Makefile.in 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,64 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (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.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Breakpad integration +# +# The Initial Developer of the Original Code is +# The Mozilla Foundation +# Portions created by the Initial Developer are Copyright (C) 2008 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Chris Jones +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../../../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = crash_generation +LIBRARY_NAME = crash_generation_s + +LOCAL_INCLUDES = -I$(topsrcdir)/toolkit/crashreporter/google-breakpad/src + +CPPSRCS = \ + crash_generation_client.cc \ + crash_generation_server.cc \ + $(NULL) + +# need static lib +FORCE_STATIC_LIB = 1 +FORCE_USE_PIC = 1 + +include $(topsrcdir)/config/rules.mk +ifeq ($(OS_ARCH),Linux) +include $(topsrcdir)/config/config.mk +# need this to suppress errors when compiling common/linux/eintr_wrapper.h +OS_CXXFLAGS := $(filter-out -pedantic,$(OS_CXXFLAGS)) +endif diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-amd.sym firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-amd.sym --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-amd.sym 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-amd.sym 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,3 @@ +MODULE Linux x86 B8CFDE93002D54DA1900A40AA1BD67690 linux-gate.so +PUBLIC 400 0 __kernel_vsyscall +STACK WIN 4 400 100 1 1 0 0 0 0 0 1 diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-intel.sym firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-intel.sym --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-intel.sym 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/data/linux-gate-intel.sym 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,3 @@ +MODULE Linux x86 4FBDA58B5A1DF5A379E3CF19A235EA090 linux-gate.so +PUBLIC 400 0 __kernel_vsyscall +STACK WIN 4 400 200 3 3 0 0 0 0 0 1 \ No newline at end of file diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc 2010-04-16 17:32:47.000000000 +0100 @@ -1,8 +1,6 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // -// Author: Li Liu -// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -29,249 +27,422 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// The ExceptionHandler object installs signal handlers for a number of +// signals. We rely on the signal handler running on the thread which crashed +// in order to identify it. This is true of the synchronous signals (SEGV etc), +// but not true of ABRT. Thus, if you send ABRT to yourself in a program which +// uses ExceptionHandler, you need to use tgkill to direct it to the current +// thread. +// +// The signal flow looks like this: +// +// SignalHandler (uses a global stack of ExceptionHandler objects to find +// | one to handle the signal. If the first rejects it, try +// | the second etc...) +// V +// HandleSignal ----------------------------| (clones a new process which +// | | shares an address space with +// (wait for cloned | the crashed process. This +// process) | allows us to ptrace the crashed +// | | process) +// V V +// (set signal handler to ThreadEntry (static function to bounce +// SIG_DFL and rethrow, | back into the object) +// killing the crashed | +// process) V +// DoDump (writes minidump) +// | +// V +// sys_exit +// + +// This code is a little fragmented. Different functions of the ExceptionHandler +// class run in a number of different contexts. Some of them run in a normal +// context and are easy to code, others run in a compromised context and the +// restrictions at the top of minidump_writer.cc apply: no libc and use the +// alternative malloc. Each function should have comment above it detailing the +// context which it runs in. + +#include "client/linux/handler/exception_handler.h" + +#include +#include +#include +#include #include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include -#include -#include -#include -#include -#include +#include +#include -#include "client/linux/handler/exception_handler.h" +#include "common/linux/linux_libc_support.h" +#include "common/linux/linux_syscall_support.h" +#include "common/linux/memory.h" +#include "client/linux/minidump_writer/minidump_writer.h" #include "common/linux/guid_creator.h" -#include "google_breakpad/common/minidump_format.h" + +// A wrapper for the tgkill syscall: send a signal to a specific thread. +static int tgkill(pid_t tgid, pid_t tid, int sig) { + return syscall(__NR_tgkill, tgid, tid, sig); + return 0; +} namespace google_breakpad { -// Signals that we are interested. -int SigTable[] = { -#if defined(SIGSEGV) - SIGSEGV, -#endif -#ifdef SIGABRT - SIGABRT, -#endif -#ifdef SIGFPE - SIGFPE, -#endif -#ifdef SIGILL - SIGILL, -#endif -#ifdef SIGBUS - SIGBUS, -#endif +// The list of signals which we consider to be crashes. The default action for +// all these signals must be Core (see man 7 signal) because we rethrow the +// signal after handling it and expect that it'll be fatal. +static const int kExceptionSignals[] = { + SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, -1 }; -std::vector *ExceptionHandler::handler_stack_ = NULL; -int ExceptionHandler::handler_stack_index_ = 0; +// We can stack multiple exception handlers. In that case, this is the global +// which holds the stack. +std::vector* ExceptionHandler::handler_stack_ = NULL; +unsigned ExceptionHandler::handler_stack_index_ = 0; pthread_mutex_t ExceptionHandler::handler_stack_mutex_ = -PTHREAD_MUTEX_INITIALIZER; + PTHREAD_MUTEX_INITIALIZER; -ExceptionHandler::ExceptionHandler(const string &dump_path, +// Runs before crashing: normal context. +ExceptionHandler::ExceptionHandler(const std::string &dump_path, FilterCallback filter, MinidumpCallback callback, void *callback_context, bool install_handler) - : filter_(filter), - callback_(callback), - callback_context_(callback_context), - dump_path_(), - installed_handler_(install_handler) { - set_dump_path(dump_path); - - if (install_handler) { - SetupHandler(); - pthread_mutex_lock(&handler_stack_mutex_); - if (handler_stack_ == NULL) - handler_stack_ = new std::vector; - handler_stack_->push_back(this); - pthread_mutex_unlock(&handler_stack_mutex_); - } + : filter_(filter), + callback_(callback), + callback_context_(callback_context), + handler_installed_(install_handler) +{ + Init(dump_path, -1); } -ExceptionHandler::~ExceptionHandler() { - TeardownAllHandler(); - pthread_mutex_lock(&handler_stack_mutex_); - if (handler_stack_->back() == this) { - handler_stack_->pop_back(); - } else { - fprintf(stderr, "warning: removing Breakpad handler out of order\n"); - for (std::vector::iterator iterator = - handler_stack_->begin(); - iterator != handler_stack_->end(); - ++iterator) { - if (*iterator == this) { - handler_stack_->erase(iterator); - } - } - } - - if (handler_stack_->empty()) { - // When destroying the last ExceptionHandler that installed a handler, - // clean up the handler stack. - delete handler_stack_; - handler_stack_ = NULL; - } - pthread_mutex_unlock(&handler_stack_mutex_); +ExceptionHandler::ExceptionHandler(const std::string &dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + bool install_handler, + const int server_fd) + : filter_(filter), + callback_(callback), + callback_context_(callback_context), + handler_installed_(install_handler) +{ + Init(dump_path, server_fd); } -bool ExceptionHandler::WriteMinidump() { - bool success = InternalWriteMinidump(0, 0, NULL); - UpdateNextID(); - return success; +// Runs before crashing: normal context. +ExceptionHandler::~ExceptionHandler() { + UninstallHandlers(); } -// static -bool ExceptionHandler::WriteMinidump(const string &dump_path, - MinidumpCallback callback, - void *callback_context) { - ExceptionHandler handler(dump_path, NULL, callback, - callback_context, false); - return handler.InternalWriteMinidump(0, 0, NULL); -} +void ExceptionHandler::Init(const std::string &dump_path, + const int server_fd) +{ + crash_handler_ = NULL; + if (0 <= server_fd) + crash_generation_client_ + .reset(CrashGenerationClient::TryCreate(server_fd)); -void ExceptionHandler::SetupHandler() { - // Signal on a different stack to avoid using the stack - // of the crashing thread. - struct sigaltstack sig_stack; - sig_stack.ss_sp = malloc(MINSIGSTKSZ); - if (sig_stack.ss_sp == NULL) - return; - sig_stack.ss_size = MINSIGSTKSZ; - sig_stack.ss_flags = 0; + if (handler_installed_) + InstallHandlers(); - if (sigaltstack(&sig_stack, NULL) < 0) - return; - for (size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i) - SetupHandler(SigTable[i]); + if (!IsOutOfProcess()) + set_dump_path(dump_path); + + pthread_mutex_lock(&handler_stack_mutex_); + if (handler_stack_ == NULL) + handler_stack_ = new std::vector; + handler_stack_->push_back(this); + pthread_mutex_unlock(&handler_stack_mutex_); } -void ExceptionHandler::SetupHandler(int signo) { - struct sigaction act, old_act; - act.sa_handler = HandleException; - act.sa_flags = SA_ONSTACK; - if (sigaction(signo, &act, &old_act) < 0) - return; - old_handlers_[signo] = old_act.sa_handler; +// Runs before crashing: normal context. +bool ExceptionHandler::InstallHandlers() { + // We run the signal handlers on an alternative stack because we might have + // crashed because of a stack overflow. + + // We use this value rather than SIGSTKSZ because we would end up overrunning + // such a small stack. + static const unsigned kSigStackSize = 8192; + + signal_stack = malloc(kSigStackSize); + stack_t stack; + memset(&stack, 0, sizeof(stack)); + stack.ss_sp = signal_stack; + stack.ss_size = kSigStackSize; + + if (sigaltstack(&stack, NULL) == -1) + return false; + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + + // mask all exception signals when we're handling one of them. + for (unsigned i = 0; kExceptionSignals[i] != -1; ++i) + sigaddset(&sa.sa_mask, kExceptionSignals[i]); + + sa.sa_sigaction = SignalHandler; + sa.sa_flags = SA_ONSTACK | SA_SIGINFO; + + for (unsigned i = 0; kExceptionSignals[i] != -1; ++i) { + struct sigaction* old = new struct sigaction; + if (sigaction(kExceptionSignals[i], &sa, old) == -1) + return false; + old_handlers_.push_back(std::make_pair(kExceptionSignals[i], old)); + } + return true; } -void ExceptionHandler::TeardownHandler(int signo) { - if (old_handlers_.find(signo) != old_handlers_.end()) { - struct sigaction act; - act.sa_handler = old_handlers_[signo]; - act.sa_flags = 0; - sigaction(signo, &act, 0); +// Runs before crashing: normal context. +void ExceptionHandler::UninstallHandlers() { + for (unsigned i = 0; i < old_handlers_.size(); ++i) { + struct sigaction *action = + reinterpret_cast(old_handlers_[i].second); + sigaction(old_handlers_[i].first, action, NULL); + delete action; } + pthread_mutex_lock(&handler_stack_mutex_); + std::vector::iterator handler = + std::find(handler_stack_->begin(), handler_stack_->end(), this); + handler_stack_->erase(handler); + pthread_mutex_unlock(&handler_stack_mutex_); + old_handlers_.clear(); } -void ExceptionHandler::TeardownAllHandler() { - for (size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i) { - TeardownHandler(SigTable[i]); +// Runs before crashing: normal context. +void ExceptionHandler::UpdateNextID() { + GUID guid; + char guid_str[kGUIDStringLength + 1]; + if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) { + next_minidump_id_ = guid_str; + next_minidump_id_c_ = next_minidump_id_.c_str(); + + char minidump_path[PATH_MAX]; + snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp", + dump_path_c_, + guid_str); + + next_minidump_path_ = minidump_path; + next_minidump_path_c_ = next_minidump_path_.c_str(); } } -// static -void ExceptionHandler::HandleException(int signo) { - // In Linux, the context information about the signal is put on the stack of - // the signal handler frame as value parameter. For some reasons, the - // prototype of the handler doesn't declare this information as parameter, we - // will do it by hand. It is the second parameter above the signal number. - // However, if we are being called by another signal handler passing the - // signal up the chain, then we may not have this random extra parameter, - // so we may have to walk the stack to find it. We do the actual work - // on another thread, where it's a little safer, but we want the ebp - // from this frame to find it. - uintptr_t current_ebp = 0; - asm volatile ("movl %%ebp, %0" - :"=m"(current_ebp)); +// void ExceptionHandler::set_crash_handler(HandlerCallback callback) { +// crash_handler_ = callback; +// } +// This function runs in a compromised context: see the top of the file. +// Runs on the crashing thread. +// static +void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { + // All the exception signals are blocked at this point. pthread_mutex_lock(&handler_stack_mutex_); - ExceptionHandler *current_handler = - handler_stack_->at(handler_stack_->size() - ++handler_stack_index_); - pthread_mutex_unlock(&handler_stack_mutex_); - // Restore original handler. - current_handler->TeardownHandler(signo); + if (!handler_stack_->size()) { + pthread_mutex_unlock(&handler_stack_mutex_); + return; + } - struct sigcontext *sig_ctx = NULL; - if (current_handler->InternalWriteMinidump(signo, current_ebp, &sig_ctx)) { - // Fully handled this exception, safe to exit. - exit(EXIT_FAILURE); - } else { - // Exception not fully handled, will call the next handler in stack to - // process it. - typedef void (*SignalHandler)(int signo, struct sigcontext); - SignalHandler old_handler = - reinterpret_cast(current_handler->old_handlers_[signo]); - if (old_handler != NULL && sig_ctx != NULL) - old_handler(signo, *sig_ctx); + for (int i = handler_stack_->size() - 1; i >= 0; --i) { + if ((*handler_stack_)[i]->HandleSignal(sig, info, uc)) { + // successfully handled: We are in an invalid state since an exception + // signal has been delivered. We don't call the exit handlers because + // they could end up corrupting on-disk state. + break; + } } - pthread_mutex_lock(&handler_stack_mutex_); - current_handler->SetupHandler(signo); - --handler_stack_index_; - // All the handlers in stack have been invoked to handle the exception, - // normally the process should be terminated and should not reach here. - // In case we got here, ask the OS to handle it to avoid endless loop, - // normally the OS will generate a core and termiate the process. This - // may be desired to debug the program. - if (handler_stack_index_ == 0) - signal(signo, SIG_DFL); pthread_mutex_unlock(&handler_stack_mutex_); + + // Terminate ourselves with the same signal so that our parent knows that we + // crashed. The default action for all the signals which we catch is Core, so + // this is the end of us. + signal(sig, SIG_DFL); + tgkill(getpid(), sys_gettid(), sig); + + // not reached. } -bool ExceptionHandler::InternalWriteMinidump(int signo, - uintptr_t sighandler_ebp, - struct sigcontext **sig_ctx) { +struct ThreadArgument { + pid_t pid; // the crashing process + ExceptionHandler* handler; + const void* context; // a CrashContext structure + size_t context_size; +}; + +// This is the entry function for the cloned process. We are in a compromised +// context here: see the top of the file. +// static +int ExceptionHandler::ThreadEntry(void *arg) { + const ThreadArgument *thread_arg = reinterpret_cast(arg); + return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context, + thread_arg->context_size) == false; +} + +// This function runs in a compromised context: see the top of the file. +// Runs on the crashing thread. +bool ExceptionHandler::HandleSignal(int sig, siginfo_t* info, void* uc) { if (filter_ && !filter_(callback_context_)) return false; - bool success = false; - // Block all the signals we want to process when writting minidump. - // We don't want it to be interrupted. - sigset_t sig_blocked, sig_old; - bool blocked = true; - sigfillset(&sig_blocked); - for (size_t i = 0; i < sizeof(SigTable) / sizeof(SigTable[0]); ++i) - sigdelset(&sig_blocked, SigTable[i]); - if (sigprocmask(SIG_BLOCK, &sig_blocked, &sig_old) != 0) { - blocked = false; - fprintf(stderr, "google_breakpad::ExceptionHandler::HandleException: " - "failed to block signals.\n"); + // Allow ourselves to be dumped. + sys_prctl(PR_SET_DUMPABLE, 1); + CrashContext context; + memcpy(&context.siginfo, info, sizeof(siginfo_t)); + memcpy(&context.context, uc, sizeof(struct ucontext)); +#if !defined(__ARM_EABI__) + // FP state is not part of user ABI on ARM Linux. + struct ucontext *uc_ptr = (struct ucontext*)uc; + if (uc_ptr->uc_mcontext.fpregs) { + memcpy(&context.float_state, + uc_ptr->uc_mcontext.fpregs, + sizeof(context.float_state)); } +#endif + context.tid = sys_gettid(); + if (crash_handler_ != NULL) { + if (crash_handler_(&context, sizeof(context), + callback_context_)) { + return true; + } + } + return GenerateDump(&context); +} - success = minidump_generator_.WriteMinidumpToFile( - next_minidump_path_c_, signo, sighandler_ebp, sig_ctx); - - // Unblock the signals. - if (blocked) { - sigprocmask(SIG_SETMASK, &sig_old, &sig_old); +// This function may run in a compromised context: see the top of the file. +bool ExceptionHandler::GenerateDump(CrashContext *context) { + if (IsOutOfProcess()) + return crash_generation_client_->RequestDump(context, sizeof(*context)); + + static const unsigned kChildStackSize = 8000; + PageAllocator allocator; + uint8_t* stack = (uint8_t*) allocator.Alloc(kChildStackSize); + if (!stack) + return false; + // clone() needs the top-most address. (scrub just to be safe) + stack += kChildStackSize; + my_memset(stack - 16, 0, 16); + + ThreadArgument thread_arg; + thread_arg.handler = this; + thread_arg.pid = getpid(); + thread_arg.context = context; + thread_arg.context_size = sizeof(*context); + + const pid_t child = sys_clone( + ThreadEntry, stack, CLONE_FILES | CLONE_FS | CLONE_UNTRACED, + &thread_arg, NULL, NULL, NULL); + int r, status; + do { + r = sys_waitpid(child, &status, __WALL); + } while (r == -1 && errno == EINTR); + + if (r == -1) { + static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:"; + sys_write(2, msg, sizeof(msg) - 1); + sys_write(2, strerror(errno), strlen(strerror(errno))); + sys_write(2, "\n", 1); } + bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0; + if (callback_) success = callback_(dump_path_c_, next_minidump_id_c_, - callback_context_, success); + callback_context_, success); + return success; } -void ExceptionHandler::UpdateNextID() { - GUID guid; - char guid_str[kGUIDStringLength + 1]; - if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) { - next_minidump_id_ = guid_str; - next_minidump_id_c_ = next_minidump_id_.c_str(); +// This function runs in a compromised context: see the top of the file. +// Runs on the cloned process. +bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, + size_t context_size) { + return google_breakpad::WriteMinidump( + next_minidump_path_c_, crashing_process, context, context_size); +} - char minidump_path[PATH_MAX]; - snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp", - dump_path_c_, - guid_str); +// static +bool ExceptionHandler::WriteMinidump(const std::string &dump_path, + MinidumpCallback callback, + void* callback_context) { + return WriteMinidump(dump_path, false, callback, callback_context); +} - next_minidump_path_ = minidump_path; - next_minidump_path_c_ = next_minidump_path_.c_str(); +// static +bool ExceptionHandler::WriteMinidump(const std::string &dump_path, + bool write_exception_stream, + MinidumpCallback callback, + void* callback_context) { + ExceptionHandler eh(dump_path, NULL, callback, callback_context, false); + return eh.WriteMinidump(write_exception_stream); +} + +bool ExceptionHandler::WriteMinidump() { + return WriteMinidump(false); +} + +bool ExceptionHandler::WriteMinidump(bool write_exception_stream) { +#if !defined(__ARM_EABI__) + // Allow ourselves to be dumped. + sys_prctl(PR_SET_DUMPABLE, 1); + + CrashContext context; + int getcontext_result = getcontext(&context.context); + if (getcontext_result) + return false; + memcpy(&context.float_state, context.context.uc_mcontext.fpregs, + sizeof(context.float_state)); + context.tid = sys_gettid(); + + if (write_exception_stream) { + memset(&context.siginfo, 0, sizeof(context.siginfo)); + context.siginfo.si_signo = SIGSTOP; +#if defined(__i386) + context.siginfo.si_addr = + reinterpret_cast(context.context.uc_mcontext.gregs[REG_EIP]); +#elif defined(__x86_64) + context.siginfo.si_addr = + reinterpret_cast(context.context.uc_mcontext.gregs[REG_RIP]); +#elif defined(__ARMEL__) + context.siginfo.si_addr = + reinterpret_cast(context.context.uc_mcontext.arm_ip); +#endif } + + bool success = GenerateDump(&context); + UpdateNextID(); + return success; +#else + return false; +#endif // !defined(__ARM_EABI__) +} + +// static +bool ExceptionHandler::WriteMinidumpForChild(pid_t child, + pid_t child_blamed_thread, + const std::string &dump_path, + MinidumpCallback callback, + void *callback_context) +{ + // This function is not run in a compromised context. + ExceptionHandler eh(dump_path, NULL, NULL, NULL, false); + if (!google_breakpad::WriteMinidump(eh.next_minidump_path_c_, + child, + child_blamed_thread)) + return false; + + return callback ? callback(eh.dump_path_c_, eh.next_minidump_id_c_, + callback_context, true) : true; } } // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h 2010-04-16 17:32:47.000000000 +0100 @@ -1,8 +1,6 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // -// Author: Li Liu -// // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: @@ -29,25 +27,24 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__ -#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__ - -#include +#ifndef CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ +#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ -#include -#include #include +#include + +#include +#include -#include "client/linux/handler/minidump_generator.h" +#include "client/linux/crash_generation/crash_generation_client.h" +#include "processor/scoped_ptr.h" -// Context information when exception occured. -struct sigcontex; +struct sigaction; namespace google_breakpad { -using std::string; +class ExceptionHandler; -// // ExceptionHandler // // ExceptionHandler can write a minidump file when an exception occurs, @@ -72,7 +69,6 @@ // // Caller should try to make the callbacks as crash-friendly as possible, // it should avoid use heap memory allocation as much as possible. -// class ExceptionHandler { public: // A callback function to run before Breakpad performs any substantial @@ -107,6 +103,15 @@ void *context, bool succeeded); + // In certain cases, a user may wish to handle the generation of the minidump + // themselves. In this case, they can install a handler callback which is + // called when a crash has occured. If this function returns true, no other + // processing of occurs and the process will shortly be crashed. If this + // returns false, the normal processing continues. + typedef bool (*HandlerCallback)(const void* crash_context, + size_t crash_context_size, + void* context); + // Creates a new ExceptionHandler instance to handle writing minidumps. // Before writing a minidump, the optional filter callback will be called. // Its return value determines whether or not Breakpad should write a @@ -115,103 +120,139 @@ // If install_handler is true, then a minidump will be written whenever // an unhandled exception occurs. If it is false, minidumps will only // be written when WriteMinidump is called. - ExceptionHandler(const string &dump_path, + ExceptionHandler(const std::string &dump_path, FilterCallback filter, MinidumpCallback callback, void *callback_context, bool install_handler); + + // Creates a new ExceptionHandler instance that can attempt to + // perform out-of-process dump generation if server_fd is valid. If + // server_fd is invalid, in-process dump generation will be + // used. See the above ctor for a description of the other + // parameters. + ExceptionHandler(const std::string& dump_path, + FilterCallback filter, MinidumpCallback callback, + void* callback_context, + bool install_handler, + const int server_fd); + ~ExceptionHandler(); // Get and set the minidump path. - string dump_path() const { return dump_path_; } - void set_dump_path(const string &dump_path) { + std::string dump_path() const { return dump_path_; } + void set_dump_path(const std::string &dump_path) { dump_path_ = dump_path; dump_path_c_ = dump_path_.c_str(); UpdateNextID(); } + void set_crash_handler(HandlerCallback callback) { + crash_handler_ = callback; + } + // Writes a minidump immediately. This can be used to capture the // execution state independently of a crash. Returns true on success. bool WriteMinidump(); + // Variant of WriteMinidump() above that optionally allows writing + // an artificial exception stream in the minidump. + bool WriteMinidump(bool write_exception_stream); + // Convenience form of WriteMinidump which does not require an // ExceptionHandler instance. - static bool WriteMinidump(const string &dump_path, + static bool WriteMinidump(const std::string &dump_path, MinidumpCallback callback, void *callback_context); - private: - // Setup crash handler. - void SetupHandler(); - // Setup signal handler for a signal. - void SetupHandler(int signo); - // Teardown the handler for a signal. - void TeardownHandler(int signo); - // Teardown all handlers. - void TeardownAllHandler(); - - // Signal handler. - static void HandleException(int signo); - - // If called from a signal handler, sighandler_ebp is the ebp of - // that signal handler's frame, and sig_ctx is an out parameter - // that will be set to point at the sigcontext that was placed - // on the stack by the kernel. You can pass zero and NULL - // for the second and third parameters if you are not calling - // this from a signal handler. - bool InternalWriteMinidump(int signo, uintptr_t sighandler_ebp, - struct sigcontext **sig_ctx); + // Variant of WriteMinidump() above that optionally allows writing + // an artificial exception stream in the minidump. + static bool WriteMinidump(const std::string &dump_path, + bool write_exception_stream, + MinidumpCallback callback, + void* callback_context); - // Generates a new ID and stores it in next_minidump_id, and stores the - // path of the next minidump to be written in next_minidump_path_. - void UpdateNextID(); + // Write a minidump of |child| immediately. This can be used to + // capture the execution state of |child| independently of a crash. + // Pass a meaningful |child_blamed_thread| to make that thread in + // the child process the one from which a crash signature is + // extracted. + // + // WARNING: the return of this function *must* be ordered + // happens-before the code that will eventually reap |child|. + // Otherwise there's a pernicious race condition in which |child| + // exits, is reaped, another process created with its pid, then that + // new process dumped. + static bool WriteMinidumpForChild(pid_t child, + pid_t child_blamed_thread, + const std::string &dump_path, + MinidumpCallback callback, + void *callback_context); + + // This structure is passed to minidump_writer.h:WriteMinidump via an opaque + // blob. It shouldn't be needed in any user code. + struct CrashContext { + siginfo_t siginfo; + pid_t tid; // the crashing thread. + struct ucontext context; +#if !defined(__ARM_EABI__) + // #ifdef this out because FP state is not part of user ABI for Linux ARM. + struct _libc_fpstate float_state; +#endif + }; + + // Returns whether out-of-process dump generation is used or not. + bool IsOutOfProcess() const { + return crash_generation_client_.get() != NULL; + } private: - FilterCallback filter_; - MinidumpCallback callback_; - void *callback_context_; - - // The directory in which a minidump will be written, set by the dump_path - // argument to the constructor, or set_dump_path. - string dump_path_; - - // The basename of the next minidump to be written, without the extension - string next_minidump_id_; - - // The full pathname of the next minidump to be written, including the file - // extension - string next_minidump_path_; + void Init(const std::string &dump_path, + const int server_fd); + bool InstallHandlers(); + void UninstallHandlers(); + void PreresolveSymbols(); + bool GenerateDump(CrashContext *context); + + void UpdateNextID(); + static void SignalHandler(int sig, siginfo_t* info, void* uc); + bool HandleSignal(int sig, siginfo_t* info, void* uc); + static int ThreadEntry(void* arg); + bool DoDump(pid_t crashing_process, const void* context, + size_t context_size); + + const FilterCallback filter_; + const MinidumpCallback callback_; + void* const callback_context_; + + scoped_ptr crash_generation_client_; + + std::string dump_path_; + std::string next_minidump_path_; + std::string next_minidump_id_; // Pointers to C-string representations of the above. These are set // when the above are set so we can avoid calling c_str during // an exception. - const char *dump_path_c_; - const char *next_minidump_id_c_; - const char *next_minidump_path_c_; - - // True if the ExceptionHandler installed an unhandled exception filter - // when created (with an install_handler parameter set to true). - bool installed_handler_; - - // Keep the previous handlers for the signal. - typedef void (*sighandler_t)(int); - std::map old_handlers_; + const char* dump_path_c_; + const char* next_minidump_path_c_; + const char* next_minidump_id_c_; + + const bool handler_installed_; + void* signal_stack; // the handler stack. + HandlerCallback crash_handler_; // The global exception handler stack. This is need becuase there may exist // multiple ExceptionHandler instances in a process. Each will have itself // registered in this stack. - static std::vector *handler_stack_; + static std::vector *handler_stack_; // The index of the handler that should handle the next exception. - static int handler_stack_index_; + static unsigned handler_stack_index_; static pthread_mutex_t handler_stack_mutex_; - // The minidump generator. - MinidumpGenerator minidump_generator_; - - // disallow copy ctor and operator= - explicit ExceptionHandler(const ExceptionHandler &); - void operator=(const ExceptionHandler &); + // A vector of the old signal handlers. + std::vector > old_handlers_; }; } // namespace google_breakpad -#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H__ +#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_test.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_test.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_test.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_test.cc 1970-01-01 01:00:00.000000000 +0100 @@ -1,124 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Author: Li Liu -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include -#include - -#include -#include -#include -#include - -#include "client/linux/handler/exception_handler.h" -#include "client/linux/handler/linux_thread.h" - -using namespace google_breakpad; - -// Thread use this to see if it should stop working. -static bool should_exit = false; - -static int foo2(int arg) { - // Stack variable, used for debugging stack dumps. - /*DDDebug*/printf("%s:%d\n", __FUNCTION__, __LINE__); - int c = 0xcccccccc; - fprintf(stderr, "Thread trying to crash: %x\n", getpid()); - c = *reinterpret_cast(0x5); - return c; -} - -static int foo(int arg) { - // Stack variable, used for debugging stack dumps. - int b = 0xbbbbbbbb; - b = foo2(b); - return b; -} - -static void *thread_crash(void *) { - // Stack variable, used for debugging stack dumps. - int a = 0xaaaaaaaa; - sleep(1); - a = foo(a); - printf("%x\n", a); - return NULL; -} - -static void *thread_main(void *) { - while (!should_exit) - sleep(1); - return NULL; -} - -static void CreateCrashThread() { - pthread_t h; - pthread_create(&h, NULL, thread_crash, NULL); - pthread_detach(h); -} - -// Create working threads. -static void CreateThread(int num) { - pthread_t h; - for (int i = 0; i < num; ++i) { - pthread_create(&h, NULL, thread_main, NULL); - pthread_detach(h); - } -} - -// Callback when minidump written. -static bool MinidumpCallback(const char *dump_path, - const char *minidump_id, - void *context, - bool succeeded) { - int index = reinterpret_cast(context); - printf("%d %s: %s is dumped\n", index, __FUNCTION__, minidump_id); - if (index == 0) { - should_exit = true; - return true; - } - // Don't process it. - return false; -} - -int main(int argc, char *argv[]) { - int handler_index = 0; - ExceptionHandler handler_ignore(".", NULL, MinidumpCallback, - (void*)handler_index, true); - ++handler_index; - ExceptionHandler handler_process(".", NULL, MinidumpCallback, - (void*)handler_index, true); - CreateCrashThread(); - CreateThread(10); - - while (true) - sleep(1); - should_exit = true; - - return 0; -} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,244 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include +#include +#include +#include +#include +#include + +#include "client/linux/handler/exception_handler.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "common/linux/eintr_wrapper.h" +#include "common/linux/linux_libc_support.h" +#include "common/linux/linux_syscall_support.h" +#include "breakpad_googletest_includes.h" + +using namespace google_breakpad; + +static void sigchld_handler(int signo) { } + +class ExceptionHandlerTest : public ::testing::Test { + protected: + void SetUp() { + // We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN. + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sigchld_handler; + ASSERT_NE(sigaction(SIGCHLD, &sa, &old_action), -1); + } + + void TearDown() { + sigaction(SIGCHLD, &old_action, NULL); + } + + struct sigaction old_action; +}; + +TEST(ExceptionHandlerTest, Simple) { + ExceptionHandler handler("/tmp", NULL, NULL, NULL, true); +} + +static bool DoneCallback(const char* dump_path, + const char* minidump_id, + void* context, + bool succeeded) { + if (!succeeded) + return succeeded; + + int fd = (intptr_t) context; + uint32_t len = my_strlen(minidump_id); + HANDLE_EINTR(sys_write(fd, &len, sizeof(len))); + HANDLE_EINTR(sys_write(fd, minidump_id, len)); + sys_close(fd); + + return true; +} + +TEST(ExceptionHandlerTest, ChildCrash) { + int fds[2]; + ASSERT_NE(pipe(fds), -1); + + const pid_t child = fork(); + if (child == 0) { + close(fds[0]); + ExceptionHandler handler("/tmp", NULL, DoneCallback, (void*) fds[1], + true); + *reinterpret_cast(NULL) = 0; + } + close(fds[1]); + + int status; + ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(WTERMSIG(status), SIGSEGV); + + struct pollfd pfd; + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fds[0]; + pfd.events = POLLIN | POLLERR; + + const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); + ASSERT_EQ(r, 1); + ASSERT_TRUE(pfd.revents & POLLIN); + + uint32_t len; + ASSERT_EQ(read(fds[0], &len, sizeof(len)), (ssize_t)sizeof(len)); + ASSERT_LT(len, (uint32_t)2048); + char* filename = reinterpret_cast(malloc(len + 1)); + ASSERT_EQ(read(fds[0], filename, len), len); + filename[len] = 0; + close(fds[0]); + + const std::string minidump_filename = std::string("/tmp/") + filename + + ".dmp"; + + struct stat st; + ASSERT_EQ(stat(minidump_filename.c_str(), &st), 0); + ASSERT_GT(st.st_size, 0u); + unlink(minidump_filename.c_str()); +} + +static const unsigned kControlMsgSize = + CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); + +static bool +CrashHandler(const void* crash_context, size_t crash_context_size, + void* context) { + const int fd = (intptr_t) context; + int fds[2]; + pipe(fds); + struct kernel_msghdr msg = {0}; + struct kernel_iovec iov; + iov.iov_base = const_cast(crash_context); + iov.iov_len = crash_context_size; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + char cmsg[kControlMsgSize]; + memset(cmsg, 0, kControlMsgSize); + msg.msg_control = cmsg; + msg.msg_controllen = sizeof(cmsg); + + struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_SOCKET; + hdr->cmsg_type = SCM_RIGHTS; + hdr->cmsg_len = CMSG_LEN(sizeof(int)); + *((int*) CMSG_DATA(hdr)) = fds[1]; + hdr = CMSG_NXTHDR((struct msghdr*) &msg, hdr); + hdr->cmsg_level = SOL_SOCKET; + hdr->cmsg_type = SCM_CREDENTIALS; + hdr->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + struct ucred *cred = reinterpret_cast(CMSG_DATA(hdr)); + cred->uid = getuid(); + cred->gid = getgid(); + cred->pid = getpid(); + + HANDLE_EINTR(sys_sendmsg(fd, &msg, 0)); + sys_close(fds[1]); + + char b; + HANDLE_EINTR(sys_read(fds[0], &b, 1)); + + return true; +} + +TEST(ExceptionHandlerTest, ExternalDumper) { + int fds[2]; + ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1); + static const int on = 1; + setsockopt(fds[0], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + + const pid_t child = fork(); + if (child == 0) { + close(fds[0]); + ExceptionHandler handler("/tmp1", NULL, NULL, (void*) fds[1], true); + handler.set_crash_handler(CrashHandler); + *reinterpret_cast(NULL) = 0; + } + close(fds[1]); + struct msghdr msg = {0}; + struct iovec iov; + static const unsigned kCrashContextSize = + sizeof(ExceptionHandler::CrashContext); + char context[kCrashContextSize]; + char control[kControlMsgSize]; + iov.iov_base = context; + iov.iov_len = kCrashContextSize; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = kControlMsgSize; + + const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0)); + ASSERT_EQ(n, kCrashContextSize); + ASSERT_EQ(msg.msg_controllen, kControlMsgSize); + ASSERT_EQ(msg.msg_flags, 0); + + pid_t crashing_pid = -1; + int signal_fd = -1; + for (struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg); hdr; + hdr = CMSG_NXTHDR(&msg, hdr)) { + if (hdr->cmsg_level != SOL_SOCKET) + continue; + if (hdr->cmsg_type == SCM_RIGHTS) { + const unsigned len = hdr->cmsg_len - + (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); + ASSERT_EQ(len, sizeof(int)); + signal_fd = *((int *) CMSG_DATA(hdr)); + } else if (hdr->cmsg_type == SCM_CREDENTIALS) { + const struct ucred *cred = + reinterpret_cast(CMSG_DATA(hdr)); + crashing_pid = cred->pid; + } + } + + ASSERT_NE(crashing_pid, -1); + ASSERT_NE(signal_fd, -1); + + char templ[] = "/tmp/exception-handler-unittest-XXXXXX"; + mktemp(templ); + ASSERT_TRUE(WriteMinidump(templ, crashing_pid, context, + kCrashContextSize)); + static const char b = 0; + HANDLE_EINTR(write(signal_fd, &b, 1)); + + int status; + ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(WTERMSIG(status), SIGSEGV); + + struct stat st; + ASSERT_EQ(stat(templ, &st), 0); + ASSERT_GT(st.st_size, 0u); + unlink(templ); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.cc 1970-01-01 01:00:00.000000000 +0100 @@ -1,411 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Author: Li Liu -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#include "client/linux/handler/linux_thread.h" - -using namespace google_breakpad; - -// This unamed namespace contains helper function. -namespace { - -// Context information for the callbacks when validating address by listing -// modules. -struct AddressValidatingContext { - uintptr_t address; - bool is_mapped; - - AddressValidatingContext() : address(0UL), is_mapped(false) { - } -}; - -// Convert from string to int. -bool LocalAtoi(char *s, int *r) { - assert(s != NULL); - assert(r != NULL); - char *endptr = NULL; - int ret = strtol(s, &endptr, 10); - if (endptr == s) - return false; - *r = ret; - return true; -} - -// Fill the proc path of a thread given its id. -void FillProcPath(int pid, char *path, int path_size) { - char pid_str[32]; - snprintf(pid_str, sizeof(pid_str), "%d", pid); - snprintf(path, path_size, "/proc/%s/", pid_str); -} - -// Read thread info from /proc/$pid/status. -bool ReadThreadInfo(int pid, ThreadInfo *info) { - assert(info != NULL); - char status_path[80]; - // Max size we want to read from status file. - static const int kStatusMaxSize = 1024; - char status_content[kStatusMaxSize]; - - FillProcPath(pid, status_path, sizeof(status_path)); - strcat(status_path, "status"); - int fd = open(status_path, O_RDONLY, 0); - if (fd < 0) - return false; - - int num_read = read(fd, status_content, kStatusMaxSize - 1); - if (num_read < 0) { - close(fd); - return false; - } - close(fd); - status_content[num_read] = '\0'; - - char *tgid_start = strstr(status_content, "Tgid:"); - if (tgid_start) - sscanf(tgid_start, "Tgid:\t%d\n", &(info->tgid)); - else - // tgid not supported by kernel?? - info->tgid = 0; - - tgid_start = strstr(status_content, "Pid:"); - if (tgid_start) { - sscanf(tgid_start, "Pid:\t%d\n" "PPid:\t%d\n", &(info->pid), - &(info->ppid)); - return true; - } - return false; -} - -// Callback invoked for each mapped module. -// It use the module's adderss range to validate the address. -bool IsAddressInModuleCallback(const ModuleInfo &module_info, - void *context) { - AddressValidatingContext *addr = - reinterpret_cast(context); - addr->is_mapped = ((addr->address >= module_info.start_addr) && - (addr->address <= module_info.start_addr + - module_info.size)); - return !addr->is_mapped; -} - -#if defined(__i386__) && !defined(NO_FRAME_POINTER) -void *GetNextFrame(void **last_ebp) { - void *sp = *last_ebp; - if ((unsigned long)sp == (unsigned long)last_ebp) - return NULL; - if ((unsigned long)sp & (sizeof(void *) - 1)) - return NULL; - if ((unsigned long)sp - (unsigned long)last_ebp > 100000) - return NULL; - return sp; -} -#else -void *GetNextFrame(void **last_ebp) { - return reinterpret_cast(last_ebp); -} -#endif - -// Suspend a thread by attaching to it. -bool SuspendThread(int pid, void *context) { - // This may fail if the thread has just died or debugged. - errno = 0; - if (ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 && - errno != 0) { - return false; - } - while (waitpid(pid, NULL, __WALL) < 0) { - if (errno != EINTR) { - ptrace(PTRACE_DETACH, pid, NULL, NULL); - return false; - } - } - return true; -} - -// Resume a thread by detaching from it. -bool ResumeThread(int pid, void *context) { - return ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; -} - -// Callback to get the thread information. -// Will be called for each thread found. -bool ThreadInfoCallback(int pid, void *context) { - CallbackParam *thread_callback = - reinterpret_cast *>(context); - ThreadInfo thread_info; - if (ReadThreadInfo(pid, &thread_info) && thread_callback) { - // Invoke callback from caller. - return (thread_callback->call_back)(thread_info, thread_callback->context); - } - return false; -} - -} // namespace - -namespace google_breakpad { - -LinuxThread::LinuxThread(int pid) : pid_(pid) , threads_suspened_(false) { -} - -LinuxThread::~LinuxThread() { - if (threads_suspened_) - ResumeAllThreads(); -} - -int LinuxThread::SuspendAllThreads() { - CallbackParam callback_param(SuspendThread, NULL); - int thread_count = 0; - if ((thread_count = IterateProcSelfTask(pid_, &callback_param)) > 0) - threads_suspened_ = true; - return thread_count; -} - -void LinuxThread::ResumeAllThreads() const { - CallbackParam callback_param(ResumeThread, NULL); - IterateProcSelfTask(pid_, &callback_param); -} - -int LinuxThread::GetThreadCount() const { - return IterateProcSelfTask(pid_, NULL); -} - -int LinuxThread::ListThreads( - CallbackParam *thread_callback_param) const { - CallbackParam callback_param(ThreadInfoCallback, - thread_callback_param); - return IterateProcSelfTask(pid_, &callback_param); -} - -bool LinuxThread::GetRegisters(int pid, user_regs_struct *regs) const { - assert(regs); - return (regs != NULL && - (ptrace(PTRACE_GETREGS, pid, NULL, regs) == 0) && - errno == 0); -} - -// Get the floating-point registers of a thread. -// The caller must get the thread pid by ListThreads. -bool LinuxThread::GetFPRegisters(int pid, user_fpregs_struct *regs) const { - assert(regs); - return (regs != NULL && - (ptrace(PTRACE_GETREGS, pid, NULL, regs) ==0) && - errno == 0); -} - -bool LinuxThread::GetFPXRegisters(int pid, user_fpxregs_struct *regs) const { - assert(regs); - return (regs != NULL && - (ptrace(PTRACE_GETFPREGS, pid, NULL, regs) != 0) && - errno == 0); -} - -bool LinuxThread::GetDebugRegisters(int pid, DebugRegs *regs) const { - assert(regs); - -#define GET_DR(name, num)\ - name->dr##num = ptrace(PTRACE_PEEKUSER, pid,\ - offsetof(struct user, u_debugreg[num]), NULL) - GET_DR(regs, 0); - GET_DR(regs, 1); - GET_DR(regs, 2); - GET_DR(regs, 3); - GET_DR(regs, 4); - GET_DR(regs, 5); - GET_DR(regs, 6); - GET_DR(regs, 7); - return true; -} - -int LinuxThread::GetThreadStackDump(uintptr_t current_ebp, - uintptr_t current_esp, - void *buf, - int buf_size) const { - assert(buf); - assert(buf_size > 0); - - uintptr_t stack_bottom = GetThreadStackBottom(current_ebp); - int size = stack_bottom - current_esp; - size = buf_size > size ? size : buf_size; - if (size > 0) - memcpy(buf, reinterpret_cast(current_esp), size); - return size; -} - -// Get the stack bottom of a thread by stack walking. It works -// unless the stack has been corrupted or the frame pointer has been omited. -// This is just a temporary solution before we get better ideas about how -// this can be done. -// -// We will check each frame address by checking into module maps. -// TODO(liuli): Improve it. -uintptr_t LinuxThread::GetThreadStackBottom(uintptr_t current_ebp) const { - void **sp = reinterpret_cast(current_ebp); - void **previous_sp = sp; - while (sp && IsAddressMapped((uintptr_t)sp)) { - previous_sp = sp; - sp = reinterpret_cast(GetNextFrame(sp)); - } - return (uintptr_t)previous_sp; -} - -int LinuxThread::GetModuleCount() const { - return ListModules(NULL); -} - -int LinuxThread::ListModules( - CallbackParam *callback_param) const { - char line[512]; - const char *maps_path = "/proc/self/maps"; - - int module_count = 0; - FILE *fp = fopen(maps_path, "r"); - if (fp == NULL) - return -1; - - uintptr_t start_addr; - uintptr_t end_addr; - while (fgets(line, sizeof(line), fp) != NULL) { - if (sscanf(line, "%x-%x", &start_addr, &end_addr) == 2) { - ModuleInfo module; - memset(&module, 0, sizeof(module)); - module.start_addr = start_addr; - module.size = end_addr - start_addr; - char *name = NULL; - assert(module.size > 0); - // Only copy name if the name is a valid path name. - if ((name = strchr(line, '/')) != NULL) { - // Get rid of the last '\n' in line - char *last_return = strchr(line, '\n'); - if (last_return != NULL) - *last_return = '\0'; - // Keep a space for the ending 0. - strncpy(module.name, name, sizeof(module.name) - 1); - ++module_count; - } - if (callback_param && - !(callback_param->call_back(module, callback_param->context))) - break; - } - } - fclose(fp); - return module_count; -} - -// Parse /proc/$pid/tasks to list all the threads of the process identified by -// pid. -int LinuxThread::IterateProcSelfTask(int pid, - CallbackParam *callback_param) const { - char task_path[80]; - FillProcPath(pid, task_path, sizeof(task_path)); - strcat(task_path, "task"); - - DIR *dir = opendir(task_path); - if (dir == NULL) - return -1; - - int pid_number = 0; - // Record the last pid we've found. This is used for duplicated thread - // removal. Duplicated thread information can be found in /proc/$pid/tasks. - int last_pid = -1; - struct dirent *entry = NULL; - while ((entry = readdir(dir)) != NULL) { - if (strcmp(entry->d_name, ".") && - strcmp(entry->d_name, "..")) { - int tpid = 0; - if (LocalAtoi(entry->d_name, &tpid) && - last_pid != tpid) { - last_pid = tpid; - ++pid_number; - // Invoke the callback. - if (callback_param && - !(callback_param->call_back)(tpid, callback_param->context)) - break; - } - } - } - closedir(dir); - return pid_number; -} - -// Check if the address is a valid virtual address. -// If the address is in any of the mapped modules, we take it as valid. -// Otherwise it is invalid. -bool LinuxThread::IsAddressMapped(uintptr_t address) const { - AddressValidatingContext addr; - addr.address = address; - CallbackParam callback_param(IsAddressInModuleCallback, - &addr); - ListModules(&callback_param); - return addr.is_mapped; -} - -bool LinuxThread::FindSigContext(uintptr_t sighandler_ebp, - struct sigcontext **sig_ctx) { - uintptr_t previous_ebp; - const int MAX_STACK_DEPTH = 10; - int depth_counter = 0; - - do { - // We're looking for a |struct sigcontext| as the second parameter - // to a signal handler function call. Luckily, the sigcontext - // has an ebp member which should match the ebp pointed to - // by the ebp of the signal handler frame. - previous_ebp = reinterpret_cast(GetNextFrame( - reinterpret_cast(sighandler_ebp))); - // The stack looks like this: - // | previous ebp | previous eip | first param | second param |, - // so we need to offset by 3 to get to the second parameter. - *sig_ctx = reinterpret_cast(sighandler_ebp + - 3 * sizeof(uintptr_t)); - sighandler_ebp = previous_ebp; - depth_counter++; - } while(previous_ebp != (*sig_ctx)->ebp && sighandler_ebp != 0 && - IsAddressMapped(sighandler_ebp) && depth_counter < MAX_STACK_DEPTH); - - return previous_ebp == (*sig_ctx)->ebp && previous_ebp != 0; -} - -} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,204 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Author: Li Liu -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -#ifndef CLIENT_LINUX_HANDLER_LINUX_THREAD_H__ -#define CLIENT_LINUX_HANDLER_LINUX_THREAD_H__ - -#include -#include - -namespace google_breakpad { - -// Max module path name length. -#define kMaxModuleNameLength 256 - -// Holding information about a thread in the process. -struct ThreadInfo { - // Id of the thread group. - int tgid; - // Id of the thread. - int pid; - // Id of the parent process. - int ppid; -}; - -// Holding infomaton about a module in the process. -struct ModuleInfo { - char name[kMaxModuleNameLength]; - uintptr_t start_addr; - int size; -}; - -// Holding debug registers. -struct DebugRegs { - int dr0; - int dr1; - int dr2; - int dr3; - int dr4; - int dr5; - int dr6; - int dr7; -}; - -// A callback to run when got a thread in the process. -// Return true will go on to the next thread while return false will stop the -// iteration. -typedef bool (*ThreadCallback)(const ThreadInfo &thread_info, void *context); - -// A callback to run when a new module is found in the process. -// Return true will go on to the next module while return false will stop the -// iteration. -typedef bool (*ModuleCallback)(const ModuleInfo &module_info, void *context); - -// Holding the callback information. -template -struct CallbackParam { - // Callback function address. - CallbackFunc call_back; - // Callback context; - void *context; - - CallbackParam() : call_back(NULL), context(NULL) { - } - - CallbackParam(CallbackFunc func, void *func_context) : - call_back(func), context(func_context) { - } -}; - -/////////////////////////////////////////////////////////////////////////////// - -// -// LinuxThread -// -// Provides handy support for operation on linux threads. -// It uses ptrace to get thread registers. Since ptrace only works in a -// different process other than the one being ptraced, user of this class -// should create another process before using the class. -// -// The process should be created in the following way: -// int cloned_pid = clone(ProcessEntryFunction, stack_address, -// CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED, -// (void*)&arguments); -// waitpid(cloned_pid, NULL, __WALL); -// -// If CLONE_VM is not used, GetThreadStackBottom, GetThreadStackDump -// will not work since it just use memcpy to get the stack dump. -// -class LinuxThread { - public: - // Create a LinuxThread instance to list all the threads in a process. - explicit LinuxThread(int pid); - ~LinuxThread(); - - // Stop all the threads in the process. - // Return the number of stopped threads in the process. - // Return -1 means failed to stop threads. - int SuspendAllThreads(); - - // Resume all the suspended threads. - void ResumeAllThreads() const; - - // Get the count of threads in the process. - // Return -1 means error. - int GetThreadCount() const; - - // List the threads of process. - // Whenever there is a thread found, the callback will be invoked to process - // the information. - // Return number of threads listed. - int ListThreads(CallbackParam *thread_callback_param) const; - - // Get the general purpose registers of a thread. - // The caller must get the thread pid by ListThreads. - bool GetRegisters(int pid, user_regs_struct *regs) const; - - // Get the floating-point registers of a thread. - // The caller must get the thread pid by ListThreads. - bool GetFPRegisters(int pid, user_fpregs_struct *regs) const; - - // Get all the extended floating-point registers. May not work on all - // machines. - // The caller must get the thread pid by ListThreads. - bool GetFPXRegisters(int pid, user_fpxregs_struct *regs) const; - - // Get the debug registers. - // The caller must get the thread pid by ListThreads. - bool GetDebugRegisters(int pid, DebugRegs *regs) const; - - // Get the stack memory dump. - int GetThreadStackDump(uintptr_t current_ebp, - uintptr_t current_esp, - void *buf, - int buf_size) const; - - // Get the module count of the current process. - int GetModuleCount() const; - - // Get the mapped modules in the address space. - // Whenever a module is found, the callback will be invoked to process the - // information. - // Return how may modules are found. - int ListModules(CallbackParam *callback_param) const; - - // Get the bottom of the stack from ebp. - uintptr_t GetThreadStackBottom(uintptr_t current_ebp) const; - - // Finds a sigcontext on the stack given the ebp of our signal handler. - bool FindSigContext(uintptr_t sighandler_ebp, struct sigcontext **sig_ctx); - - private: - // This callback will run when a new thread has been found. - typedef bool (*PidCallback)(int pid, void *context); - - // Read thread information from /proc/$pid/task. - // Whenever a thread has been found, and callback will be invoked with - // the pid of the thread. - // Return number of threads found. - // Return -1 means the directory doesn't exist. - int IterateProcSelfTask(int pid, - CallbackParam *callback_param) const; - - // Check if the address is a valid virtual address. - bool IsAddressMapped(uintptr_t address) const; - - private: - // The pid of the process we are listing threads. - int pid_; - - // Mark if we have suspended the threads. - bool threads_suspened_; -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_HANDLER_LINUX_THREAD_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread_test.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread_test.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread_test.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/linux_thread_test.cc 1970-01-01 01:00:00.000000000 +0100 @@ -1,224 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Author: Li Liu -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include -#include -#include -#include - -#include -#include -#include - -#include "client/linux/handler/linux_thread.h" - -using namespace google_breakpad; - -// Thread use this to see if it should stop working. -static bool should_exit = false; - -static void foo2(int *a) { - // Stack variable, used for debugging stack dumps. - int c = 0xcccccccc; - c = c; - while (!should_exit) - sleep(1); -} - -static void foo() { - // Stack variable, used for debugging stack dumps. - int a = 0xaaaaaaaa; - foo2(&a); -} - -static void *thread_main(void *) { - // Stack variable, used for debugging stack dumps. - int b = 0xbbbbbbbb; - b = b; - while (!should_exit) { - foo(); - } - return NULL; -} - -static void CreateThreads(int num) { - pthread_t handle; - for (int i = 0; i < num; i++) { - if (0 != pthread_create(&handle, NULL, thread_main, NULL)) - fprintf(stderr, "Failed to create thread.\n"); - else - pthread_detach(handle); - } -} - -static bool ProcessOneModule(const struct ModuleInfo &module_info, - void *context) { - printf("0x%x[%8d] %s\n", module_info.start_addr, module_info.size, - module_info.name); - return true; -} - -static bool ProcessOneThread(const struct ThreadInfo &thread_info, - void *context) { - printf("\n\nPID: %d, TGID: %d, PPID: %d\n", - thread_info.pid, - thread_info.tgid, - thread_info.ppid); - - struct user_regs_struct regs; - struct user_fpregs_struct fp_regs; - struct user_fpxregs_struct fpx_regs; - struct DebugRegs dbg_regs; - - LinuxThread *threads = reinterpret_cast(context); - memset(®s, 0, sizeof(regs)); - if (threads->GetRegisters(thread_info.pid, ®s)) { - printf(" gs = 0x%lx\n", regs.xgs); - printf(" fs = 0x%lx\n", regs.xfs); - printf(" es = 0x%lx\n", regs.xes); - printf(" ds = 0x%lx\n", regs.xds); - printf(" edi = 0x%lx\n", regs.edi); - printf(" esi = 0x%lx\n", regs.esi); - printf(" ebx = 0x%lx\n", regs.ebx); - printf(" edx = 0x%lx\n", regs.edx); - printf(" ecx = 0x%lx\n", regs.ecx); - printf(" eax = 0x%lx\n", regs.eax); - printf(" ebp = 0x%lx\n", regs.ebp); - printf(" eip = 0x%lx\n", regs.eip); - printf(" cs = 0x%lx\n", regs.xcs); - printf(" eflags = 0x%lx\n", regs.eflags); - printf(" esp = 0x%lx\n", regs.esp); - printf(" ss = 0x%lx\n", regs.xss); - } else { - fprintf(stderr, "ERROR: Failed to get general purpose registers\n"); - } - memset(&fp_regs, 0, sizeof(fp_regs)); - if (threads->GetFPRegisters(thread_info.pid, &fp_regs)) { - printf("\n Floating point registers:\n"); - printf(" fctl = 0x%lx\n", fp_regs.cwd); - printf(" fstat = 0x%lx\n", fp_regs.swd); - printf(" ftag = 0x%lx\n", fp_regs.twd); - printf(" fioff = 0x%lx\n", fp_regs.fip); - printf(" fiseg = 0x%lx\n", fp_regs.fcs); - printf(" fooff = 0x%lx\n", fp_regs.foo); - printf(" foseg = 0x%lx\n", fp_regs.fos); - int st_space_size = sizeof(fp_regs.st_space) / sizeof(fp_regs.st_space[0]); - printf(" st_space[%2d] = 0x", st_space_size); - for (int i = 0; i < st_space_size; ++i) - printf("%02lx", fp_regs.st_space[i]); - printf("\n"); - } else { - fprintf(stderr, "ERROR: Failed to get floating-point registers\n"); - } - memset(&fpx_regs, 0, sizeof(fpx_regs)); - if (threads->GetFPXRegisters(thread_info.pid, &fpx_regs)) { - printf("\n Extended floating point registers:\n"); - printf(" fctl = 0x%x\n", fpx_regs.cwd); - printf(" fstat = 0x%x\n", fpx_regs.swd); - printf(" ftag = 0x%x\n", fpx_regs.twd); - printf(" fioff = 0x%lx\n", fpx_regs.fip); - printf(" fiseg = 0x%lx\n", fpx_regs.fcs); - printf(" fooff = 0x%lx\n", fpx_regs.foo); - printf(" foseg = 0x%lx\n", fpx_regs.fos); - printf(" fop = 0x%x\n", fpx_regs.fop); - printf(" mxcsr = 0x%lx\n", fpx_regs.mxcsr); - int space_size = sizeof(fpx_regs.st_space) / sizeof(fpx_regs.st_space[0]); - printf(" st_space[%2d] = 0x", space_size); - for (int i = 0; i < space_size; ++i) - printf("%02lx", fpx_regs.st_space[i]); - printf("\n"); - space_size = sizeof(fpx_regs.xmm_space) / sizeof(fpx_regs.xmm_space[0]); - printf(" xmm_space[%2d] = 0x", space_size); - for (int i = 0; i < space_size; ++i) - printf("%02lx", fpx_regs.xmm_space[i]); - printf("\n"); - } - if (threads->GetDebugRegisters(thread_info.pid, &dbg_regs)) { - printf("\n Debug registers:\n"); - printf(" dr0 = 0x%x\n", dbg_regs.dr0); - printf(" dr1 = 0x%x\n", dbg_regs.dr1); - printf(" dr2 = 0x%x\n", dbg_regs.dr2); - printf(" dr3 = 0x%x\n", dbg_regs.dr3); - printf(" dr4 = 0x%x\n", dbg_regs.dr4); - printf(" dr5 = 0x%x\n", dbg_regs.dr5); - printf(" dr6 = 0x%x\n", dbg_regs.dr6); - printf(" dr7 = 0x%x\n", dbg_regs.dr7); - printf("\n"); - } - if (regs.esp != 0) { - // Print the stack content. - int size = 1024 * 2; - char *buf = new char[size]; - size = threads->GetThreadStackDump(regs.ebp, - regs.esp, - (void*)buf, size); - printf(" Stack content: = 0x"); - size /= sizeof(unsigned long); - unsigned long *p_buf = (unsigned long *)(buf); - for (int i = 0; i < size; i += 1) - printf("%.8lx ", p_buf[i]); - delete []buf; - printf("\n"); - } - return true; -} - -static int PrintAllThreads(void *argument) { - int pid = (int)argument; - - LinuxThread threads(pid); - int total_thread = threads.SuspendAllThreads(); - printf("There are %d threads in the process: %d\n", total_thread, pid); - int total_module = threads.GetModuleCount(); - printf("There are %d modules in the process: %d\n", total_module, pid); - CallbackParam module_callback(ProcessOneModule, &threads); - threads.ListModules(&module_callback); - CallbackParam thread_callback(ProcessOneThread, &threads); - threads.ListThreads(&thread_callback); - return 0; -} - -int main(int argc, char **argv) { - int pid = getpid(); - printf("Main thread is %d\n", pid); - CreateThreads(1); - // Create stack for the process. - char *stack = new char[1024 * 100]; - int cloned_pid = clone(PrintAllThreads, stack + 1024 * 100, - CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED, - (void*)getpid()); - waitpid(cloned_pid, NULL, __WALL); - should_exit = true; - printf("Test finished.\n"); - - delete []stack; - return 0; -} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/Makefile.in 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/Makefile.in 2010-04-16 17:32:47.000000000 +0100 @@ -49,8 +49,6 @@ CPPSRCS = \ exception_handler.cc \ - minidump_generator.cc \ - linux_thread.cc \ $(NULL) # need static lib diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.cc 1970-01-01 01:00:00.000000000 +0100 @@ -1,816 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Author: Li Liu -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "common/linux/file_id.h" -#include "client/linux/handler/linux_thread.h" -#include "client/minidump_file_writer.h" -#include "client/minidump_file_writer-inl.h" -#include "google_breakpad/common/minidump_format.h" -#include "client/linux/handler/minidump_generator.h" - -#ifndef CLONE_UNTRACED -#define CLONE_UNTRACED 0x00800000 -#endif - -// This unnamed namespace contains helper functions. -namespace { - -using namespace google_breakpad; - -// Argument for the writer function. -struct WriterArgument { - MinidumpFileWriter *minidump_writer; - - // Context for the callback. - void *version_context; - - // Pid of the thread who called WriteMinidumpToFile - int requester_pid; - - // The stack bottom of the thread which caused the dump. - // Mainly used to find the thread id of the crashed thread since signal - // handler may not be called in the thread who caused it. - uintptr_t crashed_stack_bottom; - - // Pid of the crashing thread. - int crashed_pid; - - // Signal number when crash happed. Can be 0 if this is a requested dump. - int signo; - - // The ebp of the signal handler frame. Can be zero if this - // is a requested dump. - uintptr_t sighandler_ebp; - - // Signal context when crash happed. Can be NULL if this is a requested dump. - // This is actually an out parameter, but it will be filled in at the start - // of the writer thread. - struct sigcontext *sig_ctx; - - // Used to get information about the threads. - LinuxThread *thread_lister; -}; - -// Holding context information for the callback of finding the crashing thread. -struct FindCrashThreadContext { - const LinuxThread *thread_lister; - uintptr_t crashing_stack_bottom; - int crashing_thread_pid; - - FindCrashThreadContext() : - thread_lister(NULL), - crashing_stack_bottom(0UL), - crashing_thread_pid(-1) { - } -}; - -// Callback for list threads. -// It will compare the stack bottom of the provided thread with the stack -// bottom of the crashed thread, it they are eqaul, this is thread is the one -// who crashed. -bool IsThreadCrashedCallback(const ThreadInfo &thread_info, void *context) { - FindCrashThreadContext *crashing_context = - static_cast(context); - const LinuxThread *thread_lister = crashing_context->thread_lister; - struct user_regs_struct regs; - if (thread_lister->GetRegisters(thread_info.pid, ®s)) { - uintptr_t last_ebp = regs.ebp; - uintptr_t stack_bottom = thread_lister->GetThreadStackBottom(last_ebp); - if (stack_bottom > last_ebp && - stack_bottom == crashing_context->crashing_stack_bottom) { - // Got it. Stop iteration. - crashing_context->crashing_thread_pid = thread_info.pid; - return false; - } - } - return true; -} - -// Find the crashing thread id. -// This is done based on stack bottom comparing. -int FindCrashingThread(uintptr_t crashing_stack_bottom, - int requester_pid, - const LinuxThread *thread_lister) { - FindCrashThreadContext context; - context.thread_lister = thread_lister; - context.crashing_stack_bottom = crashing_stack_bottom; - CallbackParam callback_param(IsThreadCrashedCallback, - &context); - thread_lister->ListThreads(&callback_param); - return context.crashing_thread_pid; -} - -// Write the thread stack info minidump. -bool WriteThreadStack(uintptr_t last_ebp, - uintptr_t last_esp, - const LinuxThread *thread_lister, - UntypedMDRVA *memory, - MDMemoryDescriptor *loc) { - // Maximum stack size for a thread. - uintptr_t stack_bottom = thread_lister->GetThreadStackBottom(last_ebp); - if (stack_bottom > last_esp) { - int size = stack_bottom - last_esp; - if (size > 0) { - if (!memory->Allocate(size)) - return false; - memory->Copy(reinterpret_cast(last_esp), size); - loc->start_of_memory_range = 0 | last_esp; - loc->memory = memory->location(); - } - return true; - } - return false; -} - -// Write CPU context based on signal context. -bool WriteContext(MDRawContextX86 *context, const struct sigcontext *sig_ctx, - const DebugRegs *debug_regs) { - assert(sig_ctx != NULL); - context->context_flags = MD_CONTEXT_X86_FULL; - context->gs = sig_ctx->gs; - context->fs = sig_ctx->fs; - context->es = sig_ctx->es; - context->ds = sig_ctx->ds; - context->cs = sig_ctx->cs; - context->ss = sig_ctx->ss; - context->edi = sig_ctx->edi; - context->esi = sig_ctx->esi; - context->ebp = sig_ctx->ebp; - context->esp = sig_ctx->esp; - context->ebx = sig_ctx->ebx; - context->edx = sig_ctx->edx; - context->ecx = sig_ctx->ecx; - context->eax = sig_ctx->eax; - context->eip = sig_ctx->eip; - context->eflags = sig_ctx->eflags; - if (sig_ctx->fpstate != NULL) { - context->context_flags = MD_CONTEXT_X86_FULL | - MD_CONTEXT_X86_FLOATING_POINT; - context->float_save.control_word = sig_ctx->fpstate->cw; - context->float_save.status_word = sig_ctx->fpstate->sw; - context->float_save.tag_word = sig_ctx->fpstate->tag; - context->float_save.error_offset = sig_ctx->fpstate->ipoff; - context->float_save.error_selector = sig_ctx->fpstate->cssel; - context->float_save.data_offset = sig_ctx->fpstate->dataoff; - context->float_save.data_selector = sig_ctx->fpstate->datasel; - memcpy(context->float_save.register_area, sig_ctx->fpstate->_st, - sizeof(context->float_save.register_area)); - } - - if (debug_regs != NULL) { - context->context_flags |= MD_CONTEXT_X86_DEBUG_REGISTERS; - context->dr0 = debug_regs->dr0; - context->dr1 = debug_regs->dr1; - context->dr2 = debug_regs->dr2; - context->dr3 = debug_regs->dr3; - context->dr6 = debug_regs->dr6; - context->dr7 = debug_regs->dr7; - } - return true; -} - -// Write CPU context based on provided registers. -bool WriteContext(MDRawContextX86 *context, - const struct user_regs_struct *regs, - const struct user_fpregs_struct *fp_regs, - const DebugRegs *dbg_regs) { - if (!context || !regs) - return false; - - context->context_flags = MD_CONTEXT_X86_FULL; - - context->cs = regs->xcs; - context->ds = regs->xds; - context->es = regs->xes; - context->fs = regs->xfs; - context->gs = regs->xgs; - context->ss = regs->xss; - context->edi = regs->edi; - context->esi = regs->esi; - context->ebx = regs->ebx; - context->edx = regs->edx; - context->ecx = regs->ecx; - context->eax = regs->eax; - context->ebp = regs->ebp; - context->eip = regs->eip; - context->esp = regs->esp; - context->eflags = regs->eflags; - - if (dbg_regs != NULL) { - context->context_flags |= MD_CONTEXT_X86_DEBUG_REGISTERS; - context->dr0 = dbg_regs->dr0; - context->dr1 = dbg_regs->dr1; - context->dr2 = dbg_regs->dr2; - context->dr3 = dbg_regs->dr3; - context->dr6 = dbg_regs->dr6; - context->dr7 = dbg_regs->dr7; - } - - if (fp_regs != NULL) { - context->context_flags |= MD_CONTEXT_X86_FLOATING_POINT; - context->float_save.control_word = fp_regs->cwd; - context->float_save.status_word = fp_regs->swd; - context->float_save.tag_word = fp_regs->twd; - context->float_save.error_offset = fp_regs->fip; - context->float_save.error_selector = fp_regs->fcs; - context->float_save.data_offset = fp_regs->foo; - context->float_save.data_selector = fp_regs->fos; - context->float_save.data_selector = fp_regs->fos; - - memcpy(context->float_save.register_area, fp_regs->st_space, - sizeof(context->float_save.register_area)); - } - return true; -} - -// Write information about a crashed thread. -// When a thread crash, kernel will write something on the stack for processing -// signal. This makes the current stack not reliable, and our stack walker -// won't figure out the whole call stack for this. So we write the stack at the -// time of the crash into the minidump file, not the current stack. -bool WriteCrashedThreadStream(MinidumpFileWriter *minidump_writer, - const WriterArgument *writer_args, - const ThreadInfo &thread_info, - MDRawThread *thread) { - assert(writer_args->sig_ctx != NULL); - - thread->thread_id = thread_info.pid; - - UntypedMDRVA memory(minidump_writer); - if (!WriteThreadStack(writer_args->sig_ctx->ebp, - writer_args->sig_ctx->esp, - writer_args->thread_lister, - &memory, - &thread->stack)) - return false; - - TypedMDRVA context(minidump_writer); - if (!context.Allocate()) - return false; - thread->thread_context = context.location(); - memset(context.get(), 0, sizeof(MDRawContextX86)); - return WriteContext(context.get(), writer_args->sig_ctx, NULL); -} - -// Write information about a thread. -// This function only processes thread running normally at the crash. -bool WriteThreadStream(MinidumpFileWriter *minidump_writer, - const LinuxThread *thread_lister, - const ThreadInfo &thread_info, - MDRawThread *thread) { - thread->thread_id = thread_info.pid; - - struct user_regs_struct regs; - memset(®s, 0, sizeof(regs)); - if (!thread_lister->GetRegisters(thread_info.pid, ®s)) { - perror(NULL); - return false; - } - - UntypedMDRVA memory(minidump_writer); - if (!WriteThreadStack(regs.ebp, - regs.esp, - thread_lister, - &memory, - &thread->stack)) - return false; - - struct user_fpregs_struct fp_regs; - DebugRegs dbg_regs; - memset(&fp_regs, 0, sizeof(fp_regs)); - // Get all the registers. - thread_lister->GetFPRegisters(thread_info.pid, &fp_regs); - thread_lister->GetDebugRegisters(thread_info.pid, &dbg_regs); - - // Write context - TypedMDRVA context(minidump_writer); - if (!context.Allocate()) - return false; - thread->thread_context = context.location(); - memset(context.get(), 0, sizeof(MDRawContextX86)); - return WriteContext(context.get(), ®s, &fp_regs, &dbg_regs); -} - -bool WriteCPUInformation(MDRawSystemInfo *sys_info) { - const char *proc_cpu_path = "/proc/cpuinfo"; - char line[128]; - char vendor_id[13]; - const char vendor_id_name[] = "vendor_id"; - const size_t vendor_id_name_length = sizeof(vendor_id_name) - 1; - - struct CpuInfoEntry { - const char *info_name; - int value; - } cpu_info_table[] = { - { "processor", -1 }, - { "model", 0 }, - { "stepping", 0 }, - { "cpuid level", 0 }, - { NULL, -1 }, - }; - - memset(vendor_id, 0, sizeof(vendor_id)); - - FILE *fp = fopen(proc_cpu_path, "r"); - if (fp != NULL) { - while (fgets(line, sizeof(line), fp)) { - CpuInfoEntry *entry = &cpu_info_table[0]; - while (entry->info_name != NULL) { - if (!strncmp(line, entry->info_name, strlen(entry->info_name))) { - char *value = strchr(line, ':'); - value++; - if (value != NULL) - sscanf(value, " %d", &(entry->value)); - } - entry++; - } - - // special case for vendor_id - if (!strncmp(line, vendor_id_name, vendor_id_name_length)) { - char *value = strchr(line, ':'); - if (value == NULL) - continue; - - value++; - while (*value && isspace(*value)) - value++; - if (*value) { - size_t length = strlen(value); - // we don't want the trailing newline - if (value[length - 1] == '\n') - length--; - // ensure we have space for the value - if (length < sizeof(vendor_id)) - strncpy(vendor_id, value, length); - } - } - } - fclose(fp); - } - - // /proc/cpuinfo contains cpu id, change it into number by adding one. - cpu_info_table[0].value++; - - sys_info->number_of_processors = cpu_info_table[0].value; - sys_info->processor_level = cpu_info_table[3].value; - sys_info->processor_revision = cpu_info_table[1].value << 8 | - cpu_info_table[2].value; - - sys_info->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN; - struct utsname uts; - if (uname(&uts) == 0) { - // Match i*86 and x86* as X86 architecture. - if ((strstr(uts.machine, "x86") == uts.machine) || - (strlen(uts.machine) == 4 && - uts.machine[0] == 'i' && - uts.machine[2] == '8' && - uts.machine[3] == '6')) { - sys_info->processor_architecture = MD_CPU_ARCHITECTURE_X86; - if (vendor_id[0] != '\0') - memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id, - sizeof(sys_info->cpu.x86_cpu_info.vendor_id)); - } - } - return true; -} - -bool WriteOSInformation(MinidumpFileWriter *minidump_writer, - MDRawSystemInfo *sys_info) { - sys_info->platform_id = MD_OS_LINUX; - - struct utsname uts; - if (uname(&uts) == 0) { - char os_version[512]; - size_t space_left = sizeof(os_version); - memset(os_version, 0, space_left); - const char *os_info_table[] = { - uts.sysname, - uts.release, - uts.version, - uts.machine, - "GNU/Linux", - NULL - }; - for (const char **cur_os_info = os_info_table; - *cur_os_info != NULL; - cur_os_info++) { - if (cur_os_info != os_info_table && space_left > 1) { - strcat(os_version, " "); - space_left--; - } - if (space_left > strlen(*cur_os_info)) { - strcat(os_version, *cur_os_info); - space_left -= strlen(*cur_os_info); - } else { - break; - } - } - - MDLocationDescriptor location; - if (!minidump_writer->WriteString(os_version, 0, &location)) - return false; - sys_info->csd_version_rva = location.rva; - } - return true; -} - -// Callback context for get writting thread information. -struct ThreadInfoCallbackCtx { - MinidumpFileWriter *minidump_writer; - const WriterArgument *writer_args; - TypedMDRVA *list; - int thread_index; -}; - -// Callback run for writing threads information in the process. -bool ThreadInfomationCallback(const ThreadInfo &thread_info, - void *context) { - ThreadInfoCallbackCtx *callback_context = - static_cast(context); - bool success = true; - MDRawThread thread; - memset(&thread, 0, sizeof(MDRawThread)); - if (thread_info.pid != callback_context->writer_args->crashed_pid || - callback_context->writer_args->sig_ctx == NULL) { - success = WriteThreadStream(callback_context->minidump_writer, - callback_context->writer_args->thread_lister, - thread_info, &thread); - } else { - success = WriteCrashedThreadStream(callback_context->minidump_writer, - callback_context->writer_args, - thread_info, &thread); - } - if (success) { - callback_context->list->CopyIndexAfterObject( - callback_context->thread_index++, - &thread, sizeof(MDRawThread)); - } - return success; -} - -// Stream writers -bool WriteThreadListStream(MinidumpFileWriter *minidump_writer, - const WriterArgument *writer_args, - MDRawDirectory *dir) { - // Get the thread information. - const LinuxThread *thread_lister = writer_args->thread_lister; - int thread_count = thread_lister->GetThreadCount(); - if (thread_count < 0) - return false; - TypedMDRVA list(minidump_writer); - if (!list.AllocateObjectAndArray(thread_count, sizeof(MDRawThread))) - return false; - dir->stream_type = MD_THREAD_LIST_STREAM; - dir->location = list.location(); - list.get()->number_of_threads = thread_count; - - ThreadInfoCallbackCtx context; - context.minidump_writer = minidump_writer; - context.writer_args = writer_args; - context.list = &list; - context.thread_index = 0; - CallbackParam callback_param(ThreadInfomationCallback, - &context); - int written = thread_lister->ListThreads(&callback_param); - return written == thread_count; -} - -bool WriteCVRecord(MinidumpFileWriter *minidump_writer, - MDRawModule *module, - const char *module_path) { - TypedMDRVA cv(minidump_writer); - - // Only return the last path component of the full module path - const char *module_name = strrchr(module_path, '/'); - // Increment past the slash - if (module_name) - ++module_name; - else - module_name = ""; - - size_t module_name_length = strlen(module_name); - if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(u_int8_t))) - return false; - if (!cv.CopyIndexAfterObject(0, const_cast(module_name), - module_name_length)) - return false; - - module->cv_record = cv.location(); - MDCVInfoPDB70 *cv_ptr = cv.get(); - memset(cv_ptr, 0, sizeof(MDCVInfoPDB70)); - cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE; - cv_ptr->age = 0; - - // Get the module identifier - FileID file_id(module_path); - unsigned char identifier[16]; - - if (file_id.ElfFileIdentifier(identifier)) { - cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 | - (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 | - (uint32_t)identifier[3]; - cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5]; - cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7]; - cv_ptr->signature.data4[0] = identifier[8]; - cv_ptr->signature.data4[1] = identifier[9]; - cv_ptr->signature.data4[2] = identifier[10]; - cv_ptr->signature.data4[3] = identifier[11]; - cv_ptr->signature.data4[4] = identifier[12]; - cv_ptr->signature.data4[5] = identifier[13]; - cv_ptr->signature.data4[6] = identifier[14]; - cv_ptr->signature.data4[7] = identifier[15]; - } - return true; -} - -struct ModuleInfoCallbackCtx { - MinidumpFileWriter *minidump_writer; - const WriterArgument *writer_args; - TypedMDRVA *list; - int module_index; -}; - -bool ModuleInfoCallback(const ModuleInfo &module_info, - void *context) { - ModuleInfoCallbackCtx *callback_context = - static_cast(context); - // Skip those modules without name, or those that are not modules. - if (strlen(module_info.name) == 0 || - !strchr(module_info.name, '/')) - return true; - - MDRawModule module; - memset(&module, 0, sizeof(module)); - MDLocationDescriptor loc; - if (!callback_context->minidump_writer->WriteString(module_info.name, 0, - &loc)) - return false; - module.base_of_image = (u_int64_t)module_info.start_addr; - module.size_of_image = module_info.size; - module.module_name_rva = loc.rva; - - if (!WriteCVRecord(callback_context->minidump_writer, &module, - module_info.name)) - return false; - callback_context->list->CopyIndexAfterObject( - callback_context->module_index++, &module, MD_MODULE_SIZE); - return true; -} - -bool WriteModuleListStream(MinidumpFileWriter *minidump_writer, - const WriterArgument *writer_args, - MDRawDirectory *dir) { - TypedMDRVA list(minidump_writer); - int module_count = writer_args->thread_lister->GetModuleCount(); - if (module_count <= 0 || - !list.AllocateObjectAndArray(module_count, MD_MODULE_SIZE)) - return false; - dir->stream_type = MD_MODULE_LIST_STREAM; - dir->location = list.location(); - list.get()->number_of_modules = module_count; - ModuleInfoCallbackCtx context; - context.minidump_writer = minidump_writer; - context.writer_args = writer_args; - context.list = &list; - context.module_index = 0; - CallbackParam callback(ModuleInfoCallback, &context); - return writer_args->thread_lister->ListModules(&callback) == module_count; -} - -bool WriteSystemInfoStream(MinidumpFileWriter *minidump_writer, - const WriterArgument *writer_args, - MDRawDirectory *dir) { - TypedMDRVA sys_info(minidump_writer); - if (!sys_info.Allocate()) - return false; - dir->stream_type = MD_SYSTEM_INFO_STREAM; - dir->location = sys_info.location(); - - return WriteCPUInformation(sys_info.get()) && - WriteOSInformation(minidump_writer, sys_info.get()); -} - -bool WriteExceptionStream(MinidumpFileWriter *minidump_writer, - const WriterArgument *writer_args, - MDRawDirectory *dir) { - // This happenes when this is not a crash, but a requested dump. - if (writer_args->sig_ctx == NULL) - return false; - - TypedMDRVA exception(minidump_writer); - if (!exception.Allocate()) - return false; - - dir->stream_type = MD_EXCEPTION_STREAM; - dir->location = exception.location(); - exception.get()->thread_id = writer_args->crashed_pid; - exception.get()->exception_record.exception_code = writer_args->signo; - exception.get()->exception_record.exception_flags = 0; - if (writer_args->sig_ctx != NULL) { - exception.get()->exception_record.exception_address = - writer_args->sig_ctx->eip; - } else { - return true; - } - - // Write context of the exception. - TypedMDRVA context(minidump_writer); - if (!context.Allocate()) - return false; - exception.get()->thread_context = context.location(); - memset(context.get(), 0, sizeof(MDRawContextX86)); - return WriteContext(context.get(), writer_args->sig_ctx, NULL); -} - -bool WriteMiscInfoStream(MinidumpFileWriter *minidump_writer, - const WriterArgument *writer_args, - MDRawDirectory *dir) { - TypedMDRVA info(minidump_writer); - if (!info.Allocate()) - return false; - - dir->stream_type = MD_MISC_INFO_STREAM; - dir->location = info.location(); - info.get()->size_of_info = sizeof(MDRawMiscInfo); - info.get()->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID; - info.get()->process_id = writer_args->requester_pid; - - return true; -} - -bool WriteBreakpadInfoStream(MinidumpFileWriter *minidump_writer, - const WriterArgument *writer_args, - MDRawDirectory *dir) { - TypedMDRVA info(minidump_writer); - if (!info.Allocate()) - return false; - - dir->stream_type = MD_BREAKPAD_INFO_STREAM; - dir->location = info.location(); - - info.get()->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | - MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; - info.get()->dump_thread_id = getpid(); - info.get()->requesting_thread_id = writer_args->requester_pid; - return true; -} - -// Prototype of writer functions. -typedef bool (*WriteStringFN)(MinidumpFileWriter *, - const WriterArgument *, - MDRawDirectory *); - -// Function table to writer a full minidump. -WriteStringFN writers[] = { - WriteThreadListStream, - WriteModuleListStream, - WriteSystemInfoStream, - WriteExceptionStream, - WriteMiscInfoStream, - WriteBreakpadInfoStream, -}; - -// Will call each writer function in the writers table. -// It runs in a different process from the crashing process, but sharing -// the same address space. This enables it to use ptrace functions. -int Write(void *argument) { - WriterArgument *writer_args = - static_cast(argument); - - if (!writer_args->thread_lister->SuspendAllThreads()) - return -1; - - if (writer_args->sighandler_ebp != 0 && - writer_args->thread_lister->FindSigContext(writer_args->sighandler_ebp, - &writer_args->sig_ctx)) { - writer_args->crashed_stack_bottom = - writer_args->thread_lister->GetThreadStackBottom( - writer_args->sig_ctx->ebp); - int crashed_pid = FindCrashingThread(writer_args->crashed_stack_bottom, - writer_args->requester_pid, - writer_args->thread_lister); - if (crashed_pid > 0) - writer_args->crashed_pid = crashed_pid; - } - - - MinidumpFileWriter *minidump_writer = writer_args->minidump_writer; - TypedMDRVA header(minidump_writer); - TypedMDRVA dir(minidump_writer); - if (!header.Allocate()) - return 0; - - int writer_count = sizeof(writers) / sizeof(writers[0]); - // Need directory space for all writers. - if (!dir.AllocateArray(writer_count)) - return 0; - header.get()->signature = MD_HEADER_SIGNATURE; - header.get()->version = MD_HEADER_VERSION; - header.get()->time_date_stamp = time(NULL); - header.get()->stream_count = writer_count; - header.get()->stream_directory_rva = dir.position(); - - int dir_index = 0; - MDRawDirectory local_dir; - for (int i = 0; i < writer_count; ++i) { - if (writers[i](minidump_writer, writer_args, &local_dir)) - dir.CopyIndex(dir_index++, &local_dir); - } - - writer_args->thread_lister->ResumeAllThreads(); - return 0; -} - -} // namespace - -namespace google_breakpad { - -MinidumpGenerator::MinidumpGenerator() { - AllocateStack(); -} - -MinidumpGenerator::~MinidumpGenerator() { -} - -void MinidumpGenerator::AllocateStack() { - stack_.reset(new char[kStackSize]); -} - -bool MinidumpGenerator::WriteMinidumpToFile(const char *file_pathname, - int signo, - uintptr_t sighandler_ebp, - struct sigcontext **sig_ctx) const { - assert(file_pathname != NULL); - assert(stack_ != NULL); - - if (stack_ == NULL || file_pathname == NULL) - return false; - - MinidumpFileWriter minidump_writer; - if (minidump_writer.Open(file_pathname)) { - WriterArgument argument; - memset(&argument, 0, sizeof(argument)); - LinuxThread thread_lister(getpid()); - argument.thread_lister = &thread_lister; - argument.minidump_writer = &minidump_writer; - argument.requester_pid = getpid(); - argument.crashed_pid = getpid(); - argument.signo = signo; - argument.sighandler_ebp = sighandler_ebp; - argument.sig_ctx = NULL; - - int cloned_pid = clone(Write, stack_.get() + kStackSize, - CLONE_VM | CLONE_FILES | CLONE_FS | CLONE_UNTRACED, - (void*)&argument); - waitpid(cloned_pid, NULL, __WALL); - if (sig_ctx != NULL) - *sig_ctx = argument.sig_ctx; - return true; - } - - return false; -} - -} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_generator.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,73 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Author: Li Liu -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__ -#define CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__ - -#include - -#include "google_breakpad/common/breakpad_types.h" -#include "processor/scoped_ptr.h" - -struct sigcontext; - -namespace google_breakpad { - -// -// MinidumpGenerator -// -// Write a minidump to file based on the signo and sig_ctx. -// A minidump generator should be created before any exception happen. -// -class MinidumpGenerator { - public: - MinidumpGenerator(); - - ~MinidumpGenerator(); - - // Write minidump. - bool WriteMinidumpToFile(const char *file_pathname, - int signo, - uintptr_t sighandler_ebp, - struct sigcontext **sig_ctx) const; - private: - // Allocate memory for stack. - void AllocateStack(); - - private: - // Stack size of the writer thread. - static const int kStackSize = 1024 * 1024; - scoped_array stack_; -}; - -} // namespace google_breakpad - -#endif // CLIENT_LINUX_HANDLER_MINIDUMP_GENERATOR_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_test.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_test.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_test.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/handler/minidump_test.cc 1970-01-01 01:00:00.000000000 +0100 @@ -1,86 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Author: Li Liu -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include -#include - -#include -#include -#include -#include - -#include "client/linux/handler/minidump_generator.h" - -using namespace google_breakpad; - -// Thread use this to see if it should stop working. -static bool should_exit = false; - -static void foo2(int arg) { - // Stack variable, used for debugging stack dumps. - int c = arg; - c = 0xcccccccc; - while (!should_exit) - sleep(1); -} - -static void foo(int arg) { - // Stack variable, used for debugging stack dumps. - int b = arg; - b = 0xbbbbbbbb; - foo2(b); -} - -static void *thread_main(void *) { - // Stack variable, used for debugging stack dumps. - int a = 0xaaaaaaaa; - foo(a); - return NULL; -} - -static void CreateThread(int num) { - pthread_t h; - for (int i = 0; i < num; ++i) { - pthread_create(&h, NULL, thread_main, NULL); - pthread_detach(h); - } -} - -int main(int argc, char *argv[]) { - CreateThread(10); - google_breakpad::MinidumpGenerator mg; - if (mg.WriteMinidumpToFile("minidump_test.out", -1, 0, NULL)) - printf("Succeeded written minidump\n"); - else - printf("Failed to write minidump\n"); - should_exit = true; - return 0; -} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,105 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ + +#include +#include +#include +#include +#include +#include + +#include "common/linux/linux_syscall_support.h" + +namespace google_breakpad { + +// A class for enumerating a directory without using diropen/readdir or other +// functions which may allocate memory. +class DirectoryReader { + public: + DirectoryReader(int fd) + : fd_(fd), + buf_used_(0) { + } + + // Return the next entry from the directory + // name: (output) the NUL terminated entry name + // + // Returns true iff successful (false on EOF). + // + // After calling this, one must call |PopEntry| otherwise you'll get the same + // entry over and over. + bool GetNextEntry(const char** name) { + struct kernel_dirent* const dent = + reinterpret_cast(buf_); + + if (buf_used_ == 0) { + // need to read more entries. + const int n = sys_getdents(fd_, dent, sizeof(buf_)); + if (n < 0) { + return false; + } else if (n == 0) { + hit_eof_ = true; + } else { + buf_used_ += n; + } + } + + if (buf_used_ == 0 && hit_eof_) + return false; + + assert(buf_used_ > 0); + + *name = dent->d_name; + return true; + } + + void PopEntry() { + if (!buf_used_) + return; + + const struct kernel_dirent* const dent = + reinterpret_cast(buf_); + + buf_used_ -= dent->d_reclen; + memmove(buf_, buf_ + dent->d_reclen, buf_used_); + } + + private: + const int fd_; + bool hit_eof_; + unsigned buf_used_; + uint8_t buf_[sizeof(struct kernel_dirent) + NAME_MAX + 1]; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,77 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include + +#include +#include +#include + +#include "client/linux/minidump_writer/directory_reader.h" +#include "breakpad_googletest_includes.h" + +using namespace google_breakpad; + +namespace { +typedef testing::Test DirectoryReaderTest; +} + +TEST(DirectoryReaderTest, CompareResults) { + std::set dent_set; + + DIR *const dir = opendir("/proc/self"); + ASSERT_TRUE(dir != NULL); + + struct dirent* dent; + while ((dent = readdir(dir))) + dent_set.insert(dent->d_name); + + closedir(dir); + + const int fd = open("/proc/self", O_DIRECTORY | O_RDONLY); + ASSERT_GE(fd, 0); + + DirectoryReader dir_reader(fd); + unsigned seen = 0; + + const char* name; + while (dir_reader.GetNextEntry(&name)) { + ASSERT_TRUE(dent_set.find(name) != dent_set.end()); + seen++; + dir_reader.PopEntry(); + } + + ASSERT_TRUE(dent_set.find("status") != dent_set.end()); + ASSERT_TRUE(dent_set.find("stat") != dent_set.end()); + ASSERT_TRUE(dent_set.find("cmdline") != dent_set.end()); + + ASSERT_EQ(dent_set.size(), seen); + close(fd); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,130 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ + +#include +#include +#include + +#include "common/linux/linux_syscall_support.h" + +namespace google_breakpad { + +// A class for reading a file, line by line, without using fopen/fgets or other +// functions which may allocate memory. +class LineReader { + public: + LineReader(int fd) + : fd_(fd), + hit_eof_(false), + buf_used_(0) { + } + + // The maximum length of a line. + static const size_t kMaxLineLen = 512; + + // Return the next line from the file. + // line: (output) a pointer to the start of the line. The line is NUL + // terminated. + // len: (output) the length of the line (not inc the NUL byte) + // + // Returns true iff successful (false on EOF). + // + // One must call |PopLine| after this function, otherwise you'll continue to + // get the same line over and over. + bool GetNextLine(const char **line, unsigned *len) { + for (;;) { + if (buf_used_ == 0 && hit_eof_) + return false; + + for (unsigned i = 0; i < buf_used_; ++i) { + if (buf_[i] == '\n' || buf_[i] == 0) { + buf_[i] = 0; + *len = i; + *line = buf_; + return true; + } + } + + if (buf_used_ == sizeof(buf_)) { + // we scanned the whole buffer and didn't find an end-of-line marker. + // This line is too long to process. + return false; + } + + // We didn't find any end-of-line terminators in the buffer. However, if + // this is the last line in the file it might not have one: + if (hit_eof_) { + assert(buf_used_); + // There's room for the NUL because of the buf_used_ == sizeof(buf_) + // check above. + buf_[buf_used_] = 0; + *len = buf_used_; + buf_used_ += 1; // since we appended the NUL. + *line = buf_; + return true; + } + + // Otherwise, we should pull in more data from the file + const ssize_t n = sys_read(fd_, buf_ + buf_used_, + sizeof(buf_) - buf_used_); + if (n < 0) { + return false; + } else if (n == 0) { + hit_eof_ = true; + } else { + buf_used_ += n; + } + + // At this point, we have either set the hit_eof_ flag, or we have more + // data to process... + } + } + + void PopLine(unsigned len) { + // len doesn't include the NUL byte at the end. + + assert(buf_used_ >= len + 1); + buf_used_ -= len + 1; + memmove(buf_, buf_ + len + 1, buf_used_); + } + + private: + const int fd_; + + bool hit_eof_; + unsigned buf_used_; + char buf_[kMaxLineLen]; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,184 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include + +#include "client/linux/minidump_writer/line_reader.h" +#include "breakpad_googletest_includes.h" + +using namespace google_breakpad; + +static int TemporaryFile() { + static const char templ[] = "/tmp/line-reader-unittest-XXXXXX"; + char templ_copy[sizeof(templ)]; + memcpy(templ_copy, templ, sizeof(templ)); + const int fd = mkstemp(templ_copy); + if (fd >= 0) + unlink(templ_copy); + + return fd; +} + +namespace { +typedef testing::Test LineReaderTest; +} + +TEST(LineReaderTest, EmptyFile) { + const int fd = TemporaryFile(); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} + +TEST(LineReaderTest, OneLineTerminated) { + const int fd = TemporaryFile(); + write(fd, "a\n", 2); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned int len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ(len, (unsigned int)1); + ASSERT_EQ(line[0], 'a'); + ASSERT_EQ(line[1], 0); + reader.PopLine(len); + + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} + +TEST(LineReaderTest, OneLine) { + const int fd = TemporaryFile(); + write(fd, "a", 1); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ(len, (unsigned)1); + ASSERT_EQ(line[0], 'a'); + ASSERT_EQ(line[1], 0); + reader.PopLine(len); + + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} + +TEST(LineReaderTest, TwoLinesTerminated) { + const int fd = TemporaryFile(); + write(fd, "a\nb\n", 4); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ(len, (unsigned)1); + ASSERT_EQ(line[0], 'a'); + ASSERT_EQ(line[1], 0); + reader.PopLine(len); + + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ(len, (unsigned)1); + ASSERT_EQ(line[0], 'b'); + ASSERT_EQ(line[1], 0); + reader.PopLine(len); + + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} + +TEST(LineReaderTest, TwoLines) { + const int fd = TemporaryFile(); + write(fd, "a\nb", 3); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ(len, (unsigned)1); + ASSERT_EQ(line[0], 'a'); + ASSERT_EQ(line[1], 0); + reader.PopLine(len); + + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ(len, (unsigned)1); + ASSERT_EQ(line[0], 'b'); + ASSERT_EQ(line[1], 0); + reader.PopLine(len); + + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} + +TEST(LineReaderTest, MaxLength) { + const int fd = TemporaryFile(); + char l[LineReader::kMaxLineLen - 1]; + memset(l, 'a', sizeof(l)); + write(fd, l, sizeof(l)); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ(len, sizeof(l)); + ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0); + ASSERT_EQ(line[len], 0); + + close(fd); +} + +TEST(LineReaderTest, TooLong) { + const int fd = TemporaryFile(); + char l[LineReader::kMaxLineLen]; + memset(l, 'a', sizeof(l)); + write(fd, l, sizeof(l)); + lseek(fd, 0, SEEK_SET); + LineReader reader(fd); + + const char *line; + unsigned len; + ASSERT_FALSE(reader.GetNextLine(&line, &len)); + + close(fd); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,453 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This code deals with the mechanics of getting information about a crashed +// process. Since this code may run in a compromised address space, the same +// rules apply as detailed at the top of minidump_writer.h: no libc calls and +// use the alternative allocator. + +#include "client/linux/minidump_writer/linux_dumper.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#include "client/linux/minidump_writer/directory_reader.h" +#include "client/linux/minidump_writer/line_reader.h" +#include "common/linux/file_id.h" +#include "common/linux/linux_libc_support.h" +#include "common/linux/linux_syscall_support.h" + +namespace google_breakpad { + +bool AttachThread(pid_t pid) { + // This may fail if the thread has just died or debugged. + errno = 0; + if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 && + errno != 0) { + return false; + } + while (sys_waitpid(pid, NULL, __WALL) < 0) { + if (errno != EINTR) { + sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); + return false; + } + } + return true; +} + +bool DetachThread(pid_t pid) { + return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; +} + +bool GetThreadRegisters(ThreadInfo* info) { + pid_t tid = info->tid; + + if (sys_ptrace(PTRACE_GETREGS, tid, NULL, &info->regs) == -1 || + sys_ptrace(PTRACE_GETFPREGS, tid, NULL, &info->fpregs) == -1) { + return false; + } + +#if defined(__i386) + if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) + return false; +#endif + +#if defined(__i386) || defined(__x86_64) + for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { + if (sys_ptrace( + PTRACE_PEEKUSER, tid, + reinterpret_cast (offsetof(struct user, + u_debugreg[0]) + i * + sizeof(debugreg_t)), + &info->dregs[i]) == -1) { + return false; + } + } +#endif + + return true; +} + +LinuxDumper::LinuxDumper(int pid) + : pid_(pid), + threads_suspended_(false), + threads_(&allocator_, 8), + mappings_(&allocator_) { +} + +bool LinuxDumper::Init() { + return EnumerateThreads(&threads_) && + EnumerateMappings(&mappings_); +} + +bool LinuxDumper::ThreadsAttach() { + if (threads_suspended_) + return true; + bool good = true; + for (size_t i = 0; i < threads_.size(); ++i) + good &= AttachThread(threads_[i]); + threads_suspended_ = true; + return good; +} + +bool LinuxDumper::ThreadsDetach() { + if (!threads_suspended_) + return false; + bool good = true; + for (size_t i = 0; i < threads_.size(); ++i) + good &= DetachThread(threads_[i]); + threads_suspended_ = false; + return good; +} + +void +LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const { + assert(path); + if (!path) { + return; + } + + path[0] = '\0'; + + const unsigned pid_len = my_int_len(pid); + + assert(node); + if (!node) { + return; + } + + size_t node_len = my_strlen(node); + assert(node_len < NAME_MAX); + if (node_len >= NAME_MAX) { + return; + } + + assert(node_len > 0); + if (node_len == 0) { + return; + } + + assert(pid > 0); + if (pid <= 0) { + return; + } + + const size_t total_length = 6 + pid_len + 1 + node_len; + + assert(total_length < NAME_MAX); + if (total_length >= NAME_MAX) { + return; + } + + memcpy(path, "/proc/", 6); + my_itos(path + 6, pid, pid_len); + memcpy(path + 6 + pid_len, "/", 1); + memcpy(path + 6 + pid_len + 1, node, node_len); + memcpy(path + total_length, "\0", 1); +} + +bool +LinuxDumper::ElfFileIdentifierForMapping(unsigned int mapping_id, + uint8_t identifier[sizeof(MDGUID)]) +{ + assert(mapping_id < mappings_.size()); + const MappingInfo* mapping = mappings_[mapping_id]; + int fd = sys_open(mapping->name, O_RDONLY, 0); + if (fd < 0) + return false; + struct kernel_stat st; + if (sys_fstat(fd, &st) != 0) { + sys_close(fd); + return false; + } +#if defined(__x86_64) +#define sys_mmap2 sys_mmap +#endif + void* base = sys_mmap2(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); + sys_close(fd); + if (base == MAP_FAILED) + return false; + + bool success = FileID::ElfFileIdentifierFromMappedFile(base, identifier); + sys_munmap(base, st.st_size); + return success; +} + +void* +LinuxDumper::FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const { + char auxv_path[80]; + BuildProcPath(auxv_path, pid, "auxv"); + + // If BuildProcPath errors out due to invalid input, we'll handle it when + // we try to sys_open the file. + + // Find the AT_SYSINFO_EHDR entry for linux-gate.so + // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more + // information. + int fd = sys_open(auxv_path, O_RDONLY, 0); + if (fd < 0) { + return NULL; + } + + elf_aux_entry one_aux_entry; + while (sys_read(fd, + &one_aux_entry, + sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) && + one_aux_entry.a_type != AT_NULL) { + if (one_aux_entry.a_type == AT_SYSINFO_EHDR) { + close(fd); + return reinterpret_cast(one_aux_entry.a_un.a_val); + } + } + close(fd); + return NULL; +} + +bool +LinuxDumper::EnumerateMappings(wasteful_vector* result) const { + char maps_path[80]; + BuildProcPath(maps_path, pid_, "maps"); + + // linux_gate_loc is the beginning of the kernel's mapping of + // linux-gate.so in the process. It doesn't actually show up in the + // maps list as a filename, so we use the aux vector to find it's + // load location and special case it's entry when creating the list + // of mappings. + const void* linux_gate_loc; + linux_gate_loc = FindBeginningOfLinuxGateSharedLibrary(pid_); + + const int fd = sys_open(maps_path, O_RDONLY, 0); + if (fd < 0) + return false; + LineReader* const line_reader = new(allocator_) LineReader(fd); + + const char* line; + unsigned line_len; + while (line_reader->GetNextLine(&line, &line_len)) { + uintptr_t start_addr, end_addr, offset; + + const char* i1 = my_read_hex_ptr(&start_addr, line); + if (*i1 == '-') { + const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1); + if (*i2 == ' ') { + const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); + if (*i3 == ' ') { + MappingInfo* const module = new(allocator_) MappingInfo; + memset(module, 0, sizeof(MappingInfo)); + module->start_addr = start_addr; + module->size = end_addr - start_addr; + module->offset = offset; + const char* name = NULL; + // Only copy name if the name is a valid path name, or if + // we've found the VDSO image + if ((name = my_strchr(line, '/')) != NULL) { + const unsigned l = my_strlen(name); + if (l < sizeof(module->name)) + memcpy(module->name, name, l); + } else if (linux_gate_loc && + reinterpret_cast(module->start_addr) == + linux_gate_loc) { + memcpy(module->name, + kLinuxGateLibraryName, + my_strlen(kLinuxGateLibraryName)); + module->offset = 0; + } + result->push_back(module); + } + } + } + line_reader->PopLine(line_len); + } + + sys_close(fd); + + return result->size() > 0; +} + +// Parse /proc/$pid/task to list all the threads of the process identified by +// pid. +bool LinuxDumper::EnumerateThreads(wasteful_vector* result) const { + char task_path[80]; + BuildProcPath(task_path, pid_, "task"); + + const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); + if (fd < 0) + return false; + DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd); + + // The directory may contain duplicate entries which we filter by assuming + // that they are consecutive. + int last_tid = -1; + const char* dent_name; + while (dir_reader->GetNextEntry(&dent_name)) { + if (my_strcmp(dent_name, ".") && + my_strcmp(dent_name, "..")) { + int tid = 0; + if (my_strtoui(&tid, dent_name) && + last_tid != tid) { + last_tid = tid; + result->push_back(tid); + } + } + dir_reader->PopEntry(); + } + + sys_close(fd); + return true; +} + +// Read thread info from /proc/$pid/status. +// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable, +// these members are set to -1. Returns true iff all three members are +// available. +bool LinuxDumper::ThreadInfoGet(ThreadInfo* info) { + assert(info != NULL); + pid_t tid = info->tid; + char status_path[80]; + BuildProcPath(status_path, tid, "status"); + + const int fd = open(status_path, O_RDONLY); + if (fd < 0) + return false; + + LineReader* const line_reader = new(allocator_) LineReader(fd); + const char* line; + unsigned line_len; + + info->ppid = info->tgid = -1; + + while (line_reader->GetNextLine(&line, &line_len)) { + if (my_strncmp("Tgid:\t", line, 6) == 0) { + my_strtoui(&info->tgid, line + 6); + } else if (my_strncmp("PPid:\t", line, 6) == 0) { + my_strtoui(&info->ppid, line + 6); + } + + line_reader->PopLine(line_len); + } + + if (info->ppid == -1 || info->tgid == -1) + return false; + + if (!GetThreadRegisters(info)) + return false; + + const uint8_t* stack_pointer; +#if defined(__i386) + memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); +#elif defined(__x86_64) + memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); +#elif defined(__ARM_EABI__) + memcpy(&stack_pointer, &info->regs.uregs[R13], sizeof(info->regs.uregs[R13])); +#else +#error "This code hasn't been ported to your platform yet." +#endif + + if (!GetStackInfo(&info->stack, &info->stack_len, + (uintptr_t) stack_pointer)) + return false; + + return true; +} + +// Get information about the stack, given the stack pointer. We don't try to +// walk the stack since we might not have all the information needed to do +// unwind. So we just grab, up to, 32k of stack. +bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, + uintptr_t int_stack_pointer) { + // Move the stack pointer to the bottom of the page that it's in. + const uintptr_t page_size = getpagesize(); + + uint8_t* const stack_pointer = + reinterpret_cast(int_stack_pointer & ~(page_size - 1)); + + // The number of bytes of stack which we try to capture. + static ptrdiff_t kStackToCapture = 32 * 1024; + + const MappingInfo* mapping = FindMapping(stack_pointer); + if (!mapping) + return false; + const ptrdiff_t offset = stack_pointer - (uint8_t*) mapping->start_addr; + const ptrdiff_t distance_to_end = + static_cast(mapping->size) - offset; + *stack_len = distance_to_end > kStackToCapture ? + kStackToCapture : distance_to_end; + *stack = stack_pointer; + return true; +} + +// static +void LinuxDumper::CopyFromProcess(void* dest, pid_t child, const void* src, + size_t length) { + unsigned long tmp = 55; + size_t done = 0; + static const size_t word_size = sizeof(tmp); + uint8_t* const local = (uint8_t*) dest; + uint8_t* const remote = (uint8_t*) src; + + while (done < length) { + const size_t l = length - done > word_size ? word_size : length - done; + if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) { + tmp = 0; + } + memcpy(local + done, &tmp, l); + done += l; + } +} + +// Find the mapping which the given memory address falls in. +const MappingInfo* LinuxDumper::FindMapping(const void* address) const { + const uintptr_t addr = (uintptr_t) address; + + for (size_t i = 0; i < mappings_.size(); ++i) { + const uintptr_t start = static_cast(mappings_[i]->start_addr); + if (addr >= start && addr - start < mappings_[i]->size) + return mappings_[i]; + } + + return NULL; +} + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,166 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ + +#include +#include +#include +#include +#include + +#include "common/linux/memory.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +typedef typeof(((struct user*) 0)->u_debugreg[0]) debugreg_t; + +// Typedef for our parsing of the auxv variables in /proc/pid/auxv. +#if defined(__i386) || defined(__ARM_EABI__) +typedef Elf32_auxv_t elf_aux_entry; +#elif defined(__x86_64__) +typedef Elf64_auxv_t elf_aux_entry; +#endif +// When we find the VDSO mapping in the process's address space, this +// is the name we use for it when writing it to the minidump. +// This should always be less than NAME_MAX! +const char kLinuxGateLibraryName[] = "linux-gate.so"; + +// We produce one of these structures for each thread in the crashed process. +struct ThreadInfo { + pid_t tid; // thread id + pid_t tgid; // thread group id + pid_t ppid; // parent process + + // Even on platforms where the stack grows down, the following will point to + // the smallest address in the stack. + const void* stack; // pointer to the stack area + size_t stack_len; // length of the stack to copy + + +#if defined(__i386) || defined(__x86_64) + user_regs_struct regs; + user_fpregs_struct fpregs; + static const unsigned kNumDebugRegisters = 8; + debugreg_t dregs[8]; +#if defined(__i386) + user_fpxregs_struct fpxregs; +#endif // defined(__i386) + +#elif defined(__ARM_EABI__) + // Mimicking how strace does this(see syscall.c, search for GETREGS) + struct user_regs regs; + struct user_fpregs fpregs; +#endif +}; + +// One of these is produced for each mapping in the process (i.e. line in +// /proc/$x/maps). +struct MappingInfo { + uintptr_t start_addr; + size_t size; + size_t offset; // offset into the backed file. + char name[NAME_MAX]; +}; + +// Suspend a thread by attaching to it. +bool AttachThread(pid_t pid); + +// Resume a thread by detaching from it. +bool DetachThread(pid_t pid); + +// Fill |info| with the register state of |info->tid|. The thread +// must be attached to the calling process. Return true on success. +bool GetThreadRegisters(ThreadInfo* info); + +class LinuxDumper { + public: + explicit LinuxDumper(pid_t pid); + + // Parse the data for |threads| and |mappings|. + bool Init(); + + // Attach/detach all threads in the given process. + bool ThreadsAttach(); + bool ThreadsDetach(); + + // Read information about the given thread. Returns true on success. One must + // have called |ThreadsAttach| first. + bool ThreadInfoGet(ThreadInfo* info); + + // These are only valid after a call to |Init|. + const wasteful_vector &threads() { return threads_; } + const wasteful_vector &mappings() { return mappings_; } + const MappingInfo* FindMapping(const void* address) const; + + // Find a block of memory to take as the stack given the top of stack pointer. + // stack: (output) the lowest address in the memory area + // stack_len: (output) the length of the memory area + // stack_top: the current top of the stack + bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top); + + PageAllocator* allocator() { return &allocator_; } + + // memcpy from a remote process. + static void CopyFromProcess(void* dest, pid_t child, const void* src, + size_t length); + + // Builds a proc path for a certain pid for a node. path is a + // character array that is overwritten, and node is the final node + // without any slashes. + void BuildProcPath(char* path, pid_t pid, const char* node) const; + + // Generate a File ID from the .text section of a mapped entry + bool ElfFileIdentifierForMapping(unsigned int mapping_id, + uint8_t identifier[sizeof(MDGUID)]); + + // Utility method to find the location of where the kernel has + // mapped linux-gate.so in memory(shows up in /proc/pid/maps as + // [vdso], but we can't guarantee that it's the only virtual dynamic + // shared object. Parsing the auxilary vector for AT_SYSINFO_EHDR + // is the safest way to go.) + void* FindBeginningOfLinuxGateSharedLibrary(const pid_t pid) const; + private: + bool EnumerateMappings(wasteful_vector* result) const; + bool EnumerateThreads(wasteful_vector* result) const; + + const pid_t pid_; + + mutable PageAllocator allocator_; + + bool threads_suspended_; + wasteful_vector threads_; // the ids of all the threads + wasteful_vector mappings_; // info from /proc//maps +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,236 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "client/linux/minidump_writer/linux_dumper.h" +#include "common/linux/file_id.h" +#include "common/linux/memory.h" + +using namespace google_breakpad; + +// This provides a wrapper around system calls which may be +// interrupted by a signal and return EINTR. See man 7 signal. +#define HANDLE_EINTR(x) ({ \ + typeof(x) __eintr_result__; \ + do { \ + __eintr_result__ = x; \ + } while (__eintr_result__ == -1 && errno == EINTR); \ + __eintr_result__;\ +}) + +namespace { +typedef testing::Test LinuxDumperTest; +} + +TEST(LinuxDumperTest, Setup) { + LinuxDumper dumper(getpid()); +} + +TEST(LinuxDumperTest, FindMappings) { + LinuxDumper dumper(getpid()); + ASSERT_TRUE(dumper.Init()); + + ASSERT_TRUE(dumper.FindMapping(reinterpret_cast(getpid))); + ASSERT_TRUE(dumper.FindMapping(reinterpret_cast(printf))); + ASSERT_FALSE(dumper.FindMapping(NULL)); +} + +TEST(LinuxDumperTest, ThreadList) { + LinuxDumper dumper(getpid()); + ASSERT_TRUE(dumper.Init()); + + ASSERT_GE(dumper.threads().size(), (size_t)1); + bool found = false; + for (size_t i = 0; i < dumper.threads().size(); ++i) { + if (dumper.threads()[i] == getpid()) { + found = true; + break; + } + } +} + +TEST(LinuxDumperTest, VerifyStackReadWithMultipleThreads) { + static const int kNumberOfThreadsInHelperProgram = 5; + char kNumberOfThreadsArgument[2]; + sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram); + + pid_t child_pid = fork(); + if (child_pid == 0) { + // Set the number of threads + execl("src/client/linux/linux_dumper_unittest_helper", + "linux_dumper_unittest_helper", + kNumberOfThreadsArgument, + NULL); + // Kill if we get here. + printf("Errno from exec: %d", errno); + FAIL() << "Exec failed: " << strerror(errno); + exit(0); + } + // The sleep is flaky, but prevents us from reading + // the child process before all threads have been created. + sleep(1); + LinuxDumper dumper(child_pid); + EXPECT_TRUE(dumper.Init()); + EXPECT_EQ((size_t)kNumberOfThreadsInHelperProgram, dumper.threads().size()); + EXPECT_TRUE(dumper.ThreadsSuspend()); + + ThreadInfo one_thread; + for(size_t i = 0; i < dumper.threads().size(); ++i) { + EXPECT_TRUE(dumper.ThreadInfoGet(dumper.threads()[i], &one_thread)); + // We know the threads are in a function which has allocated exactly + // one word off the stack to store its thread id. +#if defined(__ARM_EABI__) + void* process_tid_location = (void *)(one_thread.regs.uregs[11] - 8); +#elif defined(__i386) + void* process_tid_location = (void *)(one_thread.regs.ebp - 4); +#elif defined(__x86_64) + void* process_tid_location = (void *)(one_thread.regs.rbp - 4); +#else +#error Platform not supported! +#endif + pid_t one_thread_id; + dumper.CopyFromProcess(&one_thread_id, + dumper.threads()[i], + process_tid_location, + 4); + EXPECT_EQ(dumper.threads()[i], one_thread_id); + } + kill(child_pid, SIGKILL); +} + +TEST(LinuxDumperTest, BuildProcPath) { + const pid_t pid = getpid(); + LinuxDumper dumper(pid); + + char maps_path[256] = "dummymappath"; + char maps_path_expected[256]; + snprintf(maps_path_expected, sizeof(maps_path_expected), + "/proc/%d/maps", pid); + dumper.BuildProcPath(maps_path, pid, "maps"); + ASSERT_STREQ(maps_path, maps_path_expected); + + // In release mode, we expect BuildProcPath to handle the invalid + // parameters correctly and fill map_path with an empty + // NULL-terminated string. +#ifdef NDEBUG + snprintf(maps_path, sizeof(maps_path), "dummymappath"); + dumper.BuildProcPath(maps_path, 0, "maps"); + EXPECT_STREQ(maps_path, ""); + + snprintf(maps_path, sizeof(maps_path), "dummymappath"); + dumper.BuildProcPath(maps_path, getpid(), ""); + EXPECT_STREQ(maps_path, ""); + + snprintf(maps_path, sizeof(maps_path), "dummymappath"); + dumper.BuildProcPath(maps_path, getpid(), NULL); + EXPECT_STREQ(maps_path, ""); +#endif +} + +#if !defined(__ARM_EABI__) +TEST(LinuxDumperTest, MappingsIncludeLinuxGate) { + LinuxDumper dumper(getpid()); + ASSERT_TRUE(dumper.Init()); + + void* linux_gate_loc = dumper.FindBeginningOfLinuxGateSharedLibrary(getpid()); + ASSERT_TRUE(linux_gate_loc); + bool found_linux_gate = false; + + const wasteful_vector mappings = dumper.mappings(); + const MappingInfo* mapping; + for (unsigned i = 0; i < mappings.size(); ++i) { + mapping = mappings[i]; + if (!strcmp(mapping->name, kLinuxGateLibraryName)) { + found_linux_gate = true; + break; + } + } + EXPECT_TRUE(found_linux_gate); + EXPECT_EQ(linux_gate_loc, reinterpret_cast(mapping->start_addr)); + EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG)); +} +#endif + +TEST(LinuxDumperTest, FileIDsMatch) { + // Calculate the File ID of our binary using both + // FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping + // and ensure that we get the same result from both. + char exe_name[PATH_MAX]; + ssize_t len = readlink("/proc/self/exe", exe_name, PATH_MAX - 1); + ASSERT_NE(len, -1); + exe_name[len] = '\0'; + + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + // fork a child so we can ptrace it + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + // now wait forever for the parent + char b; + HANDLE_EINTR(read(fds[0], &b, sizeof(b))); + close(fds[0]); + syscall(__NR_exit); + } + close(fds[0]); + + LinuxDumper dumper(child); + ASSERT_TRUE(dumper.Init()); + const wasteful_vector mappings = dumper.mappings(); + bool found_exe = false; + unsigned i; + for (i = 0; i < mappings.size(); ++i) { + const MappingInfo* mapping = mappings[i]; + if (!strcmp(mapping->name, exe_name)) { + found_exe = true; + break; + } + } + ASSERT_TRUE(found_exe); + + uint8_t identifier1[sizeof(MDGUID)]; + uint8_t identifier2[sizeof(MDGUID)]; + EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(i, identifier1)); + FileID fileid(exe_name); + EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2)); + char identifier_string1[37]; + char identifier_string2[37]; + FileID::ConvertIdentifierToString(identifier1, identifier_string1, + 37); + FileID::ConvertIdentifierToString(identifier2, identifier_string2, + 37); + EXPECT_STREQ(identifier_string1, identifier_string2); + close(fds[1]); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,64 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Helper program for the linux_dumper class, which creates a bunch of +// threads. The first word of each thread's stack is set to the thread +// id. + +#include +#include +#include +#include +#include + +#pragma GCC optimize ("O0") +void *thread_function(void *data) __attribute__((noinline, optimize("O2"))); + +void *thread_function(void *data) { + pid_t thread_id = syscall(SYS_gettid); + while (true) ; + asm(""); +} + +int main(int argc, char *argv[]) { + int num_threads = atoi(argv[1]); + if (num_threads < 1) { + fprintf(stderr, "ERROR: number of threads is 0"); + return 1; + } + pthread_t threads[num_threads]; + pthread_attr_t thread_attributes; + pthread_attr_init(&thread_attributes); + pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED); + for (int i = 1; i < num_threads; i++) { + pthread_create(&threads[i], &thread_attributes, &thread_function, NULL); + } + thread_function(NULL); + return 0; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/Makefile.in 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/Makefile.in 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,60 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (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.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Breakpad integration +# +# The Initial Developer of the Original Code is +# The Mozilla Foundation. +# Portions created by the Initial Developer are Copyright (C) 2009 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Ted Mielczarek +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../../../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = writer +LIBRARY_NAME = minidump_writer_s +XPI_NAME = crashreporter + +LOCAL_INCLUDES = -I$(srcdir)/../../.. + +CPPSRCS = \ + linux_dumper.cc \ + minidump_writer.cc \ + $(NULL) + +# need static lib +FORCE_STATIC_LIB = 1 +FORCE_USE_PIC = 1 + +include $(topsrcdir)/config/rules.mk diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,995 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This code writes out minidump files: +// http://msdn.microsoft.com/en-us/library/ms680378(VS.85,loband).aspx +// +// Minidumps are a Microsoft format which Breakpad uses for recording crash +// dumps. This code has to run in a compromised environment (the address space +// may have received SIGSEGV), thus the following rules apply: +// * You may not enter the dynamic linker. This means that we cannot call +// any symbols in a shared library (inc libc). Because of this we replace +// libc functions in linux_libc_support.h. +// * You may not call syscalls via the libc wrappers. This rule is a subset +// of the first rule but it bears repeating. We have direct wrappers +// around the system calls in linux_syscall_support.h. +// * You may not malloc. There's an alternative allocator in memory.h and +// a canonical instance in the LinuxDumper object. We use the placement +// new form to allocate objects and we don't delete them. + +#include "client/linux/minidump_writer/minidump_writer.h" +#include "client/minidump_file_writer-inl.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "client/minidump_file_writer.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/common/minidump_cpu_amd64.h" +#include "google_breakpad/common/minidump_cpu_x86.h" + +#include "client/linux/handler/exception_handler.h" +#include "client/linux/minidump_writer/line_reader.h" +#include "client/linux/minidump_writer/linux_dumper.h" +#include "common/linux/linux_libc_support.h" +#include "common/linux/linux_syscall_support.h" + +using google_breakpad::ThreadInfo; + +// These are additional minidump stream values which are specific to the linux +// breakpad implementation. +enum { + MD_LINUX_CPU_INFO = 0x47670003, /* /proc/cpuinfo */ + MD_LINUX_PROC_STATUS = 0x47670004, /* /proc/$x/status */ + MD_LINUX_LSB_RELEASE = 0x47670005, /* /etc/lsb-release */ + MD_LINUX_CMD_LINE = 0x47670006, /* /proc/$x/cmdline */ + MD_LINUX_ENVIRON = 0x47670007, /* /proc/$x/environ */ + MD_LINUX_AUXV = 0x47670008 /* /proc/$x/auxv */ +}; + +// Minidump defines register structures which are different from the raw +// structures which we get from the kernel. These are platform specific +// functions to juggle the ucontext and user structures into minidump format. +#if defined(__i386) +typedef MDRawContextX86 RawContextCPU; + +// Write a uint16_t to memory +// out: memory location to write to +// v: value to write. +static void U16(void* out, uint16_t v) { + memcpy(out, &v, sizeof(v)); +} + +// Write a uint32_t to memory +// out: memory location to write to +// v: value to write. +static void U32(void* out, uint32_t v) { + memcpy(out, &v, sizeof(v)); +} + +// Juggle an x86 user_(fp|fpx|)regs_struct into minidump format +// out: the minidump structure +// info: the collection of register structures. +static void CPUFillFromThreadInfo(MDRawContextX86 *out, + const ThreadInfo &info) { + out->context_flags = MD_CONTEXT_X86_ALL; + + out->dr0 = info.dregs[0]; + out->dr1 = info.dregs[1]; + out->dr2 = info.dregs[2]; + out->dr3 = info.dregs[3]; + // 4 and 5 deliberatly omitted because they aren't included in the minidump + // format. + out->dr6 = info.dregs[6]; + out->dr7 = info.dregs[7]; + + out->gs = info.regs.xgs; + out->fs = info.regs.xfs; + out->es = info.regs.xes; + out->ds = info.regs.xds; + + out->edi = info.regs.edi; + out->esi = info.regs.esi; + out->ebx = info.regs.ebx; + out->edx = info.regs.edx; + out->ecx = info.regs.ecx; + out->eax = info.regs.eax; + + out->ebp = info.regs.ebp; + out->eip = info.regs.eip; + out->cs = info.regs.xcs; + out->eflags = info.regs.eflags; + out->esp = info.regs.esp; + out->ss = info.regs.xss; + + out->float_save.control_word = info.fpregs.cwd; + out->float_save.status_word = info.fpregs.swd; + out->float_save.tag_word = info.fpregs.twd; + out->float_save.error_offset = info.fpregs.fip; + out->float_save.error_selector = info.fpregs.fcs; + out->float_save.data_offset = info.fpregs.foo; + out->float_save.data_selector = info.fpregs.fos; + + // 8 registers * 10 bytes per register. + memcpy(out->float_save.register_area, info.fpregs.st_space, 10 * 8); + + // This matches the Intel fpsave format. + U16(out->extended_registers + 0, info.fpregs.cwd); + U16(out->extended_registers + 2, info.fpregs.swd); + U16(out->extended_registers + 4, info.fpregs.twd); + U16(out->extended_registers + 6, info.fpxregs.fop); + U32(out->extended_registers + 8, info.fpxregs.fip); + U16(out->extended_registers + 12, info.fpxregs.fcs); + U32(out->extended_registers + 16, info.fpregs.foo); + U16(out->extended_registers + 20, info.fpregs.fos); + U32(out->extended_registers + 24, info.fpxregs.mxcsr); + + memcpy(out->extended_registers + 32, &info.fpxregs.st_space, 128); + memcpy(out->extended_registers + 160, &info.fpxregs.xmm_space, 128); +} + +// Juggle an x86 ucontext into minidump format +// out: the minidump structure +// info: the collection of register structures. +static void CPUFillFromUContext(MDRawContextX86 *out, const ucontext *uc, + const struct _libc_fpstate* fp) { + const greg_t* regs = uc->uc_mcontext.gregs; + + out->context_flags = MD_CONTEXT_X86_FULL | + MD_CONTEXT_X86_FLOATING_POINT; + + out->gs = regs[REG_GS]; + out->fs = regs[REG_FS]; + out->es = regs[REG_ES]; + out->ds = regs[REG_DS]; + + out->edi = regs[REG_EDI]; + out->esi = regs[REG_ESI]; + out->ebx = regs[REG_EBX]; + out->edx = regs[REG_EDX]; + out->ecx = regs[REG_ECX]; + out->eax = regs[REG_EAX]; + + out->ebp = regs[REG_EBP]; + out->eip = regs[REG_EIP]; + out->cs = regs[REG_CS]; + out->eflags = regs[REG_EFL]; + out->esp = regs[REG_UESP]; + out->ss = regs[REG_SS]; + + out->float_save.control_word = fp->cw; + out->float_save.status_word = fp->sw; + out->float_save.tag_word = fp->tag; + out->float_save.error_offset = fp->ipoff; + out->float_save.error_selector = fp->cssel; + out->float_save.data_offset = fp->dataoff; + out->float_save.data_selector = fp->datasel; + + // 8 registers * 10 bytes per register. + memcpy(out->float_save.register_area, fp->_st, 10 * 8); +} + +static uintptr_t InstructionPointer(const ThreadInfo& info) { + return info.regs.eip; +} + +static uintptr_t StackPointer(const ThreadInfo& info) { + return info.regs.esp; +} + +static uintptr_t StackPointer(const ucontext* uc) { + return uc->uc_mcontext.gregs[REG_ESP]; +} + +#elif defined(__x86_64) +typedef MDRawContextAMD64 RawContextCPU; + +static void CPUFillFromThreadInfo(MDRawContextAMD64 *out, + const ThreadInfo &info) { + out->context_flags = MD_CONTEXT_AMD64_FULL | + MD_CONTEXT_AMD64_SEGMENTS; + + out->cs = info.regs.cs; + + out->ds = info.regs.ds; + out->es = info.regs.es; + out->fs = info.regs.fs; + out->gs = info.regs.gs; + + out->ss = info.regs.ss; + out->eflags = info.regs.eflags; + + out->dr0 = info.dregs[0]; + out->dr1 = info.dregs[1]; + out->dr2 = info.dregs[2]; + out->dr3 = info.dregs[3]; + // 4 and 5 deliberatly omitted because they aren't included in the minidump + // format. + out->dr6 = info.dregs[6]; + out->dr7 = info.dregs[7]; + + out->rax = info.regs.rax; + out->rcx = info.regs.rcx; + out->rdx = info.regs.rdx; + out->rbx = info.regs.rbx; + + out->rsp = info.regs.rsp; + + out->rbp = info.regs.rbp; + out->rsi = info.regs.rsi; + out->rdi = info.regs.rdi; + out->r8 = info.regs.r8; + out->r9 = info.regs.r9; + out->r10 = info.regs.r10; + out->r11 = info.regs.r11; + out->r12 = info.regs.r12; + out->r13 = info.regs.r13; + out->r14 = info.regs.r14; + out->r15 = info.regs.r15; + + out->rip = info.regs.rip; + + out->flt_save.control_word = info.fpregs.cwd; + out->flt_save.status_word = info.fpregs.swd; + out->flt_save.tag_word = info.fpregs.ftw; + out->flt_save.error_opcode = info.fpregs.fop; + out->flt_save.error_offset = info.fpregs.rip; + out->flt_save.error_selector = 0; // We don't have this. + out->flt_save.data_offset = info.fpregs.rdp; + out->flt_save.data_selector = 0; // We don't have this. + out->flt_save.mx_csr = info.fpregs.mxcsr; + out->flt_save.mx_csr_mask = info.fpregs.mxcr_mask; + memcpy(&out->flt_save.float_registers, &info.fpregs.st_space, 8 * 16); + memcpy(&out->flt_save.xmm_registers, &info.fpregs.xmm_space, 16 * 16); +} + +static void CPUFillFromUContext(MDRawContextAMD64 *out, const ucontext *uc, + const struct _libc_fpstate* fpregs) { + const greg_t* regs = uc->uc_mcontext.gregs; + + out->context_flags = MD_CONTEXT_AMD64_FULL; + + out->cs = regs[REG_CSGSFS] & 0xffff; + + out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff; + out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff; + + out->eflags = regs[REG_EFL]; + + out->rax = regs[REG_RAX]; + out->rcx = regs[REG_RCX]; + out->rdx = regs[REG_RDX]; + out->rbx = regs[REG_RBX]; + + out->rsp = regs[REG_RSP]; + out->rbp = regs[REG_RBP]; + out->rsi = regs[REG_RSI]; + out->rdi = regs[REG_RDI]; + out->r8 = regs[REG_R8]; + out->r9 = regs[REG_R9]; + out->r10 = regs[REG_R10]; + out->r11 = regs[REG_R11]; + out->r12 = regs[REG_R12]; + out->r13 = regs[REG_R13]; + out->r14 = regs[REG_R14]; + out->r15 = regs[REG_R15]; + + out->rip = regs[REG_RIP]; + + out->flt_save.control_word = fpregs->cwd; + out->flt_save.status_word = fpregs->swd; + out->flt_save.tag_word = fpregs->ftw; + out->flt_save.error_opcode = fpregs->fop; + out->flt_save.error_offset = fpregs->rip; + out->flt_save.data_offset = fpregs->rdp; + out->flt_save.error_selector = 0; // We don't have this. + out->flt_save.data_selector = 0; // We don't have this. + out->flt_save.mx_csr = fpregs->mxcsr; + out->flt_save.mx_csr_mask = fpregs->mxcr_mask; + memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16); + memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16); +} + +static uintptr_t InstructionPointer(const ThreadInfo& info) { + return info.regs.rip; +} + +static uintptr_t StackPointer(const ThreadInfo& info) { + return info.regs.rsp; +} + +static uintptr_t StackPointer(const ucontext* uc) { + return uc->uc_mcontext.gregs[REG_RSP]; +} + +#elif defined(__ARMEL__) +typedef MDRawContextARM RawContextCPU; + +static void CPUFillFromThreadInfo(MDRawContextARM *out, + const ThreadInfo &info) { + out->context_flags = MD_CONTEXT_ARM_FULL; + + for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i) + out->iregs[i] = info.regs.uregs[i]; + // No CPSR register in ThreadInfo(it's not accessible via ptrace) + out->cpsr = 0; + out->float_save.fpscr = info.fpregs.fpsr | + (static_cast(info.fpregs.fpcr) << 32); + //TODO: sort this out, actually collect floating point registers + memset(&out->float_save.regs, 0, sizeof(out->float_save.regs)); + memset(&out->float_save.extra, 0, sizeof(out->float_save.extra)); +} + +static void CPUFillFromUContext(MDRawContextARM *out, const ucontext *uc, + const struct _libc_fpstate* fpregs) { + out->context_flags = MD_CONTEXT_ARM_FULL; + + out->iregs[0] = uc->uc_mcontext.arm_r0; + out->iregs[1] = uc->uc_mcontext.arm_r1; + out->iregs[2] = uc->uc_mcontext.arm_r2; + out->iregs[3] = uc->uc_mcontext.arm_r3; + out->iregs[4] = uc->uc_mcontext.arm_r4; + out->iregs[5] = uc->uc_mcontext.arm_r5; + out->iregs[6] = uc->uc_mcontext.arm_r6; + out->iregs[7] = uc->uc_mcontext.arm_r7; + out->iregs[8] = uc->uc_mcontext.arm_r8; + out->iregs[9] = uc->uc_mcontext.arm_r9; + out->iregs[10] = uc->uc_mcontext.arm_r10; + + out->iregs[11] = uc->uc_mcontext.arm_fp; + out->iregs[12] = uc->uc_mcontext.arm_ip; + out->iregs[13] = uc->uc_mcontext.arm_sp; + out->iregs[14] = uc->uc_mcontext.arm_lr; + out->iregs[15] = uc->uc_mcontext.arm_pc; + + out->cpsr = uc->uc_mcontext.arm_cpsr; + + //TODO: fix this after fixing ExceptionHandler + out->float_save.fpscr = 0; + memset(&out->float_save.regs, 0, sizeof(out->float_save.regs)); + memset(&out->float_save.extra, 0, sizeof(out->float_save.extra)); +} + +static uintptr_t InstructionPointer(const ThreadInfo& info) { + return info.regs.uregs[R12]; +} + +static uintptr_t StackPointer(const ThreadInfo& info) { + return info.regs.uregs[R13]; +} + +static uintptr_t StackPointer(const ucontext* uc) { + return uc->uc_mcontext.arm_sp; +} + +#else +#error "This code has not been ported to your platform yet." +#endif + +namespace google_breakpad { + +// There are two uses of MinidumpWriter: +// +// (1) dumping a process in which a thread has crashed and is blocked +// in a signal handler +// (2) dumping a live process +// +// In case (1), we get the ucontext and fpstate of the crashing_tid_ +// from the signal handler. In case (2), we have to extract it using +// ptrace, and we can't assume that crashing_tid_ still exists (or +// ever did). +class MinidumpWriter { + public: + // case (1) above + MinidumpWriter(const char* filename, + pid_t crashing_pid, + const ExceptionHandler::CrashContext* context) + : filename_(filename), + siginfo_(&context->siginfo), + ucontext_(&context->context), +#if !defined(__ARM_EABI__) + float_state_(&context->float_state), +#else + //TODO: fix this after fixing ExceptionHandler + float_state_(NULL), +#endif + crashing_tid_(context->tid), + crashing_tid_pc_(0), + dumper_(crashing_pid) { + } + + // case (2) above + MinidumpWriter(const char* filename, + pid_t pid, + pid_t blame_thread) + : filename_(filename), + siginfo_(NULL), // we fill this in if we find blame_thread + ucontext_(NULL), + float_state_(NULL), + crashing_tid_(blame_thread), + crashing_tid_pc_(0), // set if we find blame_thread + dumper_(pid) { + } + + bool Init() { + return dumper_.Init() && minidump_writer_.Open(filename_) && + dumper_.ThreadsAttach(); + } + + ~MinidumpWriter() { + minidump_writer_.Close(); + dumper_.ThreadsDetach(); + } + + bool HaveCrashedThread() const { + return ucontext_ != NULL; + } + + bool Dump() { + // A minidump file contains a number of tagged streams. This is the number + // of stream which we write. + static const unsigned kNumWriters = 11; + + TypedMDRVA header(&minidump_writer_); + TypedMDRVA dir(&minidump_writer_); + if (!header.Allocate()) + return false; + if (!dir.AllocateArray(kNumWriters)) + return false; + memset(header.get(), 0, sizeof(MDRawHeader)); + + header.get()->signature = MD_HEADER_SIGNATURE; + header.get()->version = MD_HEADER_VERSION; + header.get()->time_date_stamp = time(NULL); + header.get()->stream_count = kNumWriters; + header.get()->stream_directory_rva = dir.position(); + + unsigned dir_index = 0; + MDRawDirectory dirent; + + if (!WriteThreadListStream(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + if (!WriteMappings(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + if (siginfo_ || crashing_tid_pc_) { + if (!WriteExceptionStream(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + } + + if (!WriteSystemInfoStream(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_CPU_INFO; + if (!WriteFile(&dirent.location, "/proc/cpuinfo")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_PROC_STATUS; + if (!WriteProcFile(&dirent.location, crashing_tid_, "status")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_LSB_RELEASE; + if (!WriteFile(&dirent.location, "/etc/lsb-release")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_CMD_LINE; + if (!WriteProcFile(&dirent.location, crashing_tid_, "cmdline")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_ENVIRON; + if (!WriteProcFile(&dirent.location, crashing_tid_, "environ")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_AUXV; + if (!WriteProcFile(&dirent.location, crashing_tid_, "auxv")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_AUXV; + if (!WriteProcFile(&dirent.location, crashing_tid_, "maps")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + // If you add more directory entries, don't forget to update kNumWriters, + // above. + + dumper_.ThreadsDetach(); + return true; + } + + // Write information about the threads. + bool WriteThreadListStream(MDRawDirectory* dirent) { + const unsigned num_threads = dumper_.threads().size(); + + TypedMDRVA list(&minidump_writer_); + if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread))) + return false; + + dirent->stream_type = MD_THREAD_LIST_STREAM; + dirent->location = list.location(); + + *list.get() = num_threads; + + for (unsigned i = 0; i < num_threads; ++i) { + MDRawThread thread; + my_memset(&thread, 0, sizeof(thread)); + thread.thread_id = dumper_.threads()[i]; + // We have a different source of information for the crashing thread. If + // we used the actual state of the thread we would find it running in the + // signal handler with the alternative stack, which would be deeply + // unhelpful. + if (HaveCrashedThread() && + (pid_t)thread.thread_id == crashing_tid_) { + const void* stack; + size_t stack_len; + if (!dumper_.GetStackInfo(&stack, &stack_len, StackPointer(ucontext_))) + return false; + UntypedMDRVA memory(&minidump_writer_); + if (!memory.Allocate(stack_len)) + return false; + uint8_t* stack_copy = (uint8_t*) dumper_.allocator()->Alloc(stack_len); + dumper_.CopyFromProcess(stack_copy, thread.thread_id, stack, stack_len); + memory.Copy(stack_copy, stack_len); + thread.stack.start_of_memory_range = (uintptr_t) (stack); + thread.stack.memory = memory.location(); + TypedMDRVA cpu(&minidump_writer_); + if (!cpu.Allocate()) + return false; + my_memset(cpu.get(), 0, sizeof(RawContextCPU)); + CPUFillFromUContext(cpu.get(), ucontext_, float_state_); + thread.thread_context = cpu.location(); + crashing_thread_context_ = cpu.location(); + } else { + ThreadInfo info; + info.tid = dumper_.threads()[i]; + if (!dumper_.ThreadInfoGet(&info)) + return false; + UntypedMDRVA memory(&minidump_writer_); + if (!memory.Allocate(info.stack_len)) + return false; + uint8_t* stack_copy = + (uint8_t*) dumper_.allocator()->Alloc(info.stack_len); + dumper_.CopyFromProcess(stack_copy, thread.thread_id, info.stack, + info.stack_len); + memory.Copy(stack_copy, info.stack_len); + thread.stack.start_of_memory_range = (uintptr_t)(info.stack); + thread.stack.memory = memory.location(); + TypedMDRVA cpu(&minidump_writer_); + if (!cpu.Allocate()) + return false; + my_memset(cpu.get(), 0, sizeof(RawContextCPU)); + CPUFillFromThreadInfo(cpu.get(), info); + thread.thread_context = cpu.location(); + + if ((pid_t)thread.thread_id == crashing_tid_) { + assert(!HaveCrashedThread()); + // we're dumping a live process and just found the thread + // that should be "blamed" for the dump. Grab its PC so we + // can write it to the exception stream. + crashing_tid_pc_ = InstructionPointer(info); + crashing_thread_context_ = cpu.location(); + } + } + + list.CopyIndexAfterObject(i, &thread, sizeof(thread)); + } + + return true; + } + + static bool ShouldIncludeMapping(const MappingInfo& mapping) { + if (mapping.name[0] == 0 || // we only want modules with filenames. + mapping.offset || // we only want to include one mapping per shared lib. + mapping.size < 4096) { // too small to get a signature for. + return false; + } + + return true; + } + + // Write information about the mappings in effect. Because we are using the + // minidump format, the information about the mappings is pretty limited. + // Because of this, we also include the full, unparsed, /proc/$x/maps file in + // another stream in the file. + bool WriteMappings(MDRawDirectory* dirent) { + const unsigned num_mappings = dumper_.mappings().size(); + unsigned num_output_mappings = 0; + + for (unsigned i = 0; i < dumper_.mappings().size(); ++i) { + const MappingInfo& mapping = *dumper_.mappings()[i]; + if (ShouldIncludeMapping(mapping)) + num_output_mappings++; + } + + TypedMDRVA list(&minidump_writer_); + if (!list.AllocateObjectAndArray(num_output_mappings, MD_MODULE_SIZE)) + return false; + + dirent->stream_type = MD_MODULE_LIST_STREAM; + dirent->location = list.location(); + *list.get() = num_output_mappings; + + for (unsigned i = 0, j = 0; i < num_mappings; ++i) { + const MappingInfo& mapping = *dumper_.mappings()[i]; + if (!ShouldIncludeMapping(mapping)) + continue; + + MDRawModule mod; + my_memset(&mod, 0, MD_MODULE_SIZE); + mod.base_of_image = mapping.start_addr; + mod.size_of_image = mapping.size; + const size_t filepath_len = my_strlen(mapping.name); + + // Figure out file name from path + const char* filename_ptr = mapping.name + filepath_len - 1; + while (filename_ptr >= mapping.name) { + if (*filename_ptr == '/') + break; + filename_ptr--; + } + filename_ptr++; + const size_t filename_len = mapping.name + filepath_len - filename_ptr; + uint8_t cv_buf[MDCVInfoPDB70_minsize + NAME_MAX]; + uint8_t* cv_ptr = cv_buf; + UntypedMDRVA cv(&minidump_writer_); + if (!cv.Allocate(MDCVInfoPDB70_minsize + filename_len + 1)) + return false; + + const uint32_t cv_signature = MD_CVINFOPDB70_SIGNATURE; + memcpy(cv_ptr, &cv_signature, sizeof(cv_signature)); + cv_ptr += sizeof(cv_signature); + uint8_t* signature = cv_ptr; + cv_ptr += sizeof(MDGUID); + dumper_.ElfFileIdentifierForMapping(i, signature); + my_memset(cv_ptr, 0, sizeof(uint32_t)); // Set age to 0 on Linux. + cv_ptr += sizeof(uint32_t); + + // Write pdb_file_name + memcpy(cv_ptr, filename_ptr, filename_len + 1); + cv.Copy(cv_buf, MDCVInfoPDB70_minsize + filename_len + 1); + + mod.cv_record = cv.location(); + + MDLocationDescriptor ld; + if (!minidump_writer_.WriteString(mapping.name, filepath_len, &ld)) + return false; + mod.module_name_rva = ld.rva; + + list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE); + } + + return true; + } + + bool WriteExceptionStream(MDRawDirectory* dirent) { + TypedMDRVA exc(&minidump_writer_); + if (!exc.Allocate()) + return false; + my_memset(exc.get(), 0, sizeof(MDRawExceptionStream)); + + dirent->stream_type = MD_EXCEPTION_STREAM; + dirent->location = exc.location(); + + int signo = HaveCrashedThread() ? siginfo_->si_signo : SIGSTOP; + uintptr_t crash_addr = HaveCrashedThread() ? + uintptr_t(siginfo_->si_addr) : crashing_tid_pc_; + + exc.get()->thread_id = crashing_tid_; + exc.get()->exception_record.exception_code = signo; + exc.get()->exception_record.exception_address = crash_addr; + exc.get()->thread_context = crashing_thread_context_; + + return true; + } + + bool WriteSystemInfoStream(MDRawDirectory* dirent) { + TypedMDRVA si(&minidump_writer_); + if (!si.Allocate()) + return false; + my_memset(si.get(), 0, sizeof(MDRawSystemInfo)); + + dirent->stream_type = MD_SYSTEM_INFO_STREAM; + dirent->location = si.location(); + + WriteCPUInformation(si.get()); + WriteOSInformation(si.get()); + + return true; + } + + private: + void NullifyDirectoryEntry(MDRawDirectory* dirent) { + dirent->stream_type = 0; + dirent->location.data_size = 0; + dirent->location.rva = 0; + } + + bool WriteCPUInformation(MDRawSystemInfo* sys_info) { + char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0}; + static const char vendor_id_name[] = "vendor_id"; + static const size_t vendor_id_name_length = sizeof(vendor_id_name) - 1; + + struct CpuInfoEntry { + const char* info_name; + int value; + bool found; + } cpu_info_table[] = { + { "processor", -1, false }, + { "model", 0, false }, + { "stepping", 0, false }, + { "cpu family", 0, false }, + }; + + // processor_architecture should always be set, do this first + sys_info->processor_architecture = +#if defined(__i386) + MD_CPU_ARCHITECTURE_X86; +#elif defined(__x86_64) + MD_CPU_ARCHITECTURE_AMD64; +#elif defined(__arm__) + MD_CPU_ARCHITECTURE_ARM; +#else +#error "Unknown CPU arch" +#endif + + const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0); + if (fd < 0) + return false; + + { + PageAllocator allocator; + LineReader* const line_reader = new(allocator) LineReader(fd); + const char* line; + unsigned line_len; + while (line_reader->GetNextLine(&line, &line_len)) { + for (size_t i = 0; + i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]); + i++) { + CpuInfoEntry* entry = &cpu_info_table[i]; + if (entry->found) + continue; + if (!strncmp(line, entry->info_name, strlen(entry->info_name))) { + const char* value = strchr(line, ':'); + if (!value) + continue; + + // the above strncmp only matches the prefix, it might be the wrong + // line. i.e. we matched "model name" instead of "model". + // check and make sure there is only spaces between the prefix and + // the colon. + const char* space_ptr = line + strlen(entry->info_name); + for (; space_ptr < value; space_ptr++) { + if (!isspace(*space_ptr)) { + break; + } + } + if (space_ptr != value) + continue; + + sscanf(++value, " %d", &(entry->value)); + entry->found = true; + } + } + + // special case for vendor_id + if (!strncmp(line, vendor_id_name, vendor_id_name_length)) { + const char* value = strchr(line, ':'); + if (!value) + goto popline; + + // skip ':" and all the spaces that follows + do { + value++; + } while (isspace(*value)); + + if (*value) { + size_t length = strlen(value); + if (length == 0) + goto popline; + // we don't want the trailing newline + if (value[length - 1] == '\n') + length--; + // ensure we have space for the value + if (length < sizeof(vendor_id)) + strncpy(vendor_id, value, length); + } + } + +popline: + line_reader->PopLine(line_len); + } + sys_close(fd); + } + + // make sure we got everything we wanted + for (size_t i = 0; + i < sizeof(cpu_info_table) / sizeof(cpu_info_table[0]); + i++) { + if (!cpu_info_table[i].found) { + return false; + } + } + // /proc/cpuinfo contains cpu id, change it into number by adding one. + cpu_info_table[0].value++; + + sys_info->number_of_processors = cpu_info_table[0].value; + sys_info->processor_level = cpu_info_table[3].value; + sys_info->processor_revision = cpu_info_table[1].value << 8 | + cpu_info_table[2].value; + + if (vendor_id[0] != '\0') { + memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id, + sizeof(sys_info->cpu.x86_cpu_info.vendor_id)); + } + return true; + } + + bool WriteFile(MDLocationDescriptor* result, const char* filename) { + const int fd = sys_open(filename, O_RDONLY, 0); + if (fd < 0) + return false; + + // We can't stat the files because several of the files that we want to + // read are kernel seqfiles, which always have a length of zero. So we have + // to read as much as we can into a buffer. + static const unsigned kMaxFileSize = 1024; + uint8_t* data = (uint8_t*) dumper_.allocator()->Alloc(kMaxFileSize); + + size_t done = 0; + while (done < kMaxFileSize) { + ssize_t r; + do { + r = sys_read(fd, data + done, kMaxFileSize - done); + } while (r == -1 && errno == EINTR); + + if (r < 1) + break; + done += r; + } + sys_close(fd); + + if (!done) + return false; + + UntypedMDRVA memory(&minidump_writer_); + if (!memory.Allocate(done)) + return false; + memory.Copy(data, done); + *result = memory.location(); + return true; + } + + bool WriteOSInformation(MDRawSystemInfo* sys_info) { + sys_info->platform_id = MD_OS_LINUX; + + struct utsname uts; + if (uname(&uts)) + return false; + + static const size_t buf_len = 512; + char buf[buf_len] = {0}; + size_t space_left = buf_len - 1; + const char* info_table[] = { + uts.sysname, + uts.release, + uts.version, + uts.machine, + NULL + }; + bool first_item = true; + for (const char** cur_info = info_table; *cur_info; cur_info++) { + static const char* separator = " "; + size_t separator_len = strlen(separator); + size_t info_len = strlen(*cur_info); + if (info_len == 0) + continue; + + if (space_left < info_len + (first_item ? 0 : separator_len)) + break; + + if (!first_item) { + strcat(buf, separator); + space_left -= separator_len; + } + + first_item = false; + strcat(buf, *cur_info); + space_left -= info_len; + } + + MDLocationDescriptor location; + if (!minidump_writer_.WriteString(buf, 0, &location)) + return false; + sys_info->csd_version_rva = location.rva; + + return true; + } + + bool WriteProcFile(MDLocationDescriptor* result, pid_t pid, + const char* filename) { + char buf[80]; + memcpy(buf, "/proc/", 6); + const unsigned pid_len = my_int_len(pid); + my_itos(buf + 6, pid, pid_len); + buf[6 + pid_len] = '/'; + memcpy(buf + 6 + pid_len + 1, filename, my_strlen(filename) + 1); + return WriteFile(result, buf); + } + + const char* const filename_; // output filename + const siginfo_t* const siginfo_; // from the signal handler (see sigaction) + const struct ucontext* const ucontext_; // also from the signal handler + const struct _libc_fpstate* const float_state_; // ditto + const pid_t crashing_tid_; // the process which actually crashed + uintptr_t crashing_tid_pc_; // set if we're dumping a live process + // and find crashing_tid_. used to + // write exception info. (if we're + // dumping a crash, this stays 0 and we + // use siginfo_) + LinuxDumper dumper_; + MinidumpFileWriter minidump_writer_; + MDLocationDescriptor crashing_thread_context_; +}; + +bool WriteMinidump(const char* filename, pid_t crashing_process, + const void* blob, size_t blob_size) { + if (blob_size != sizeof(ExceptionHandler::CrashContext)) + return false; + const ExceptionHandler::CrashContext* context = + reinterpret_cast(blob); + MinidumpWriter writer(filename, crashing_process, context); + if (!writer.Init()) + return false; + return writer.Dump(); +} + +bool WriteMinidump(const char* filename, pid_t process, + pid_t process_blamed_thread) { + MinidumpWriter writer(filename, process, process_blamed_thread); + if (!writer.Init()) + return false; + return writer.Dump(); +} + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,61 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ + +#include +#include + +namespace google_breakpad { + +// Write a minidump to the filesystem. This function does not malloc nor use +// libc functions which may. Thus, it can be used in contexts where the state +// of the heap may be corrupt. +// filename: the filename to write to. This is opened O_EXCL and fails if +// open fails. +// crashing_process: the pid of the crashing process. This must be trusted. +// blob: a blob of data from the crashing process. See exception_handler.h +// blob_size: the length of |blob|, in bytes +// +// Returns true iff successful. +bool WriteMinidump(const char* filename, pid_t crashing_process, + const void* blob, size_t blob_size); + +// Alternate form of WriteMinidump() that works with processes that +// are not expected to have crashed. If |process_blamed_thread| is +// meaningful, it will be the one from which a crash signature is +// extracted. It is not expected that this function will be called +// from a compromised context, but it is safe to do so. +bool WriteMinidump(const char* filename, pid_t process, + pid_t process_blamed_thread); + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,70 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include + +#include "client/linux/handler/exception_handler.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "common/linux/eintr_wrapper.h" +#include "breakpad_googletest_includes.h" + +using namespace google_breakpad; + +namespace { +typedef testing::Test MinidumpWriterTest; +} + +TEST(MinidumpWriterTest, Setup) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + HANDLE_EINTR(read(fds[0], &b, sizeof(b))); + close(fds[0]); + syscall(__NR_exit); + } + close(fds[0]); + + ExceptionHandler::CrashContext context; + memset(&context, 0, sizeof(context)); + + char templ[] = "/tmp/minidump-writer-unittest-XXXXXX"; + mktemp(templ); + ASSERT_TRUE(WriteMinidump(templ, child, &context, sizeof(context))); + struct stat st; + ASSERT_EQ(stat(templ, &st), 0); + ASSERT_GT(st.st_size, 0u); + unlink(templ); + + close(fds[1]); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/sender/google_crash_report_sender.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/sender/google_crash_report_sender.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/sender/google_crash_report_sender.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/linux/sender/google_crash_report_sender.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,102 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "common/linux/google_crashdump_uploader.h" +#include "third_party/linux/include/glog/logging.h" +#include "third_party/linux/include/gflags/gflags.h" +#include + +DEFINE_string(crash_server, "http://clients2.google.com/cr", + "The crash server to upload minidumps to."); +DEFINE_string(product_name, "", + "The product name that the minidump corresponds to."); +DEFINE_string(product_version, "", + "The version of the product that produced the minidump."); +DEFINE_string(client_id, "", + "The client GUID"); +DEFINE_string(minidump_path, "", + "The path of the minidump file."); +DEFINE_string(ptime, "", + "The process uptime in milliseconds."); +DEFINE_string(ctime, "", + "The cumulative process uptime in milliseconds."); +DEFINE_string(email, "", + "The user's email address."); +DEFINE_string(comments, "", + "Extra user comments"); +DEFINE_string(proxy_host, "", + "Proxy host"); +DEFINE_string(proxy_userpasswd, "", + "Proxy username/password in user:pass format."); + + +bool CheckForRequiredFlagsOrDie() { + std::string error_text = ""; + if (FLAGS_product_name.empty()) { + error_text.append("\nProduct name must be specified."); + } + + if (FLAGS_product_version.empty()) { + error_text.append("\nProduct version must be specified."); + } + + if (FLAGS_client_id.empty()) { + error_text.append("\nClient ID must be specified."); + } + + if (FLAGS_minidump_path.empty()) { + error_text.append("\nMinidump pathname must be specified."); + } + + if (!error_text.empty()) { + LOG(ERROR) << error_text; + return false; + } + return true; +} + +int main(int argc, char *argv[]) { + google::InitGoogleLogging(argv[0]); + google::ParseCommandLineFlags(&argc, &argv, true); + if (!CheckForRequiredFlagsOrDie()) { + return 1; + } + google_breakpad::GoogleCrashdumpUploader g(FLAGS_product_name, + FLAGS_product_version, + FLAGS_client_id, + FLAGS_ptime, + FLAGS_ctime, + FLAGS_email, + FLAGS_comments, + FLAGS_minidump_path, + FLAGS_crash_server, + FLAGS_proxy_host, + FLAGS_proxy_userpasswd); + g.Upload(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,2080 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 42; + objects = { + +/* Begin PBXAggregateTarget section */ + F94585840F782326009A47BF /* All */ = { + isa = PBXAggregateTarget; + buildConfigurationList = F94585930F78235C009A47BF /* Build configuration list for PBXAggregateTarget "All" */; + buildPhases = ( + ); + dependencies = ( + F94585880F78232B009A47BF /* PBXTargetDependency */, + F945858A0F78232E009A47BF /* PBXTargetDependency */, + F945858C0F782330009A47BF /* PBXTargetDependency */, + F945858E0F782333009A47BF /* PBXTargetDependency */, + F94585900F782336009A47BF /* PBXTargetDependency */, + F93DE3A70F830D1D00608B94 /* PBXTargetDependency */, + F95BB8B3101F94D300AA053B /* PBXTargetDependency */, + F95BB8B5101F94D300AA053B /* PBXTargetDependency */, + F95BB8B7101F94D300AA053B /* PBXTargetDependency */, + ); + name = All; + productName = All; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 3329D4ED0FA16D820007BBC5 /* Breakpad.nib in Resources */ = {isa = PBXBuildFile; fileRef = 3329D4EC0FA16D820007BBC5 /* Breakpad.nib */; }; + 33880C800F9E097100817F82 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 33880C7E0F9E097100817F82 /* InfoPlist.strings */; }; + 4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */; }; + 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; + F91AF5D00FD60393009D8BE2 /* BreakpadFramework_Test.mm in Sources */ = {isa = PBXBuildFile; fileRef = F91AF5CF0FD60393009D8BE2 /* BreakpadFramework_Test.mm */; }; + F91AF6210FD60784009D8BE2 /* Breakpad.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; }; + F9286B3A0F7EB25800A4DCC8 /* InspectorMain.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */; }; + F92C53B80ECCE7B3009BE4BA /* Inspector.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53B70ECCE7B3009BE4BA /* Inspector.mm */; }; + F92C554C0ECCF534009BE4BA /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; + F92C55D00ECD0064009BE4BA /* Breakpad.h in Headers */ = {isa = PBXBuildFile; fileRef = F92C55CE0ECD0064009BE4BA /* Breakpad.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F92C55D10ECD0064009BE4BA /* Breakpad.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C55CF0ECD0064009BE4BA /* Breakpad.mm */; }; + F92C56330ECD0DF1009BE4BA /* OnDemandServer.h in Headers */ = {isa = PBXBuildFile; fileRef = F92C56310ECD0DF1009BE4BA /* OnDemandServer.h */; }; + F92C56340ECD0DF1009BE4BA /* OnDemandServer.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C56320ECD0DF1009BE4BA /* OnDemandServer.mm */; }; + F92C563F0ECD10CA009BE4BA /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.c */; }; + F92C56400ECD10CA009BE4BA /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */; }; + F92C56410ECD10CA009BE4BA /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; }; + F92C56420ECD10CA009BE4BA /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; }; + F92C56430ECD10CA009BE4BA /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; }; + F92C56440ECD10CA009BE4BA /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; }; + F92C56450ECD10CA009BE4BA /* MachIPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53790ECCE635009BE4BA /* MachIPC.mm */; }; + F92C56460ECD10CA009BE4BA /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; }; + F92C56470ECD10CA009BE4BA /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */; }; + F92C56480ECD10CA009BE4BA /* SimpleStringDictionary.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53810ECCE635009BE4BA /* SimpleStringDictionary.mm */; }; + F92C56490ECD10CA009BE4BA /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; }; + F92C564A0ECD10CA009BE4BA /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; }; + F92C564C0ECD10DD009BE4BA /* breakpadUtilities.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; }; + F92C56570ECD113E009BE4BA /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C554A0ECCF530009BE4BA /* Carbon.framework */; }; + F92C565C0ECD1158009BE4BA /* breakpadUtilities.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; }; + F92C565F0ECD116B009BE4BA /* protected_memory_allocator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53720ECCE3FD009BE4BA /* protected_memory_allocator.cc */; }; + F92C56630ECD1179009BE4BA /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */; }; + F92C56650ECD1185009BE4BA /* breakpadUtilities.dylib in Resources */ = {isa = PBXBuildFile; fileRef = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; }; + F92C568A0ECD15F9009BE4BA /* Inspector in Resources */ = {isa = PBXBuildFile; fileRef = F92C53540ECCE349009BE4BA /* Inspector */; }; + F92C56A90ECE04C5009BE4BA /* crash_report_sender.m in Sources */ = {isa = PBXBuildFile; fileRef = F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */; }; + F93803CD0F8083B7004D428B /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */; }; + F93803CE0F8083B7004D428B /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */; }; + F93803CF0F8083B7004D428B /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */; }; + F93803D00F8083B7004D428B /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; }; + F93803D10F8083B7004D428B /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.c */; }; + F93803D20F8083B7004D428B /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; }; + F93803D30F8083B7004D428B /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; }; + F93803D40F8083B7004D428B /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; }; + F93803D50F8083B7004D428B /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; }; + F93803D60F8083B7004D428B /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; }; + F93803D70F8083B7004D428B /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; }; + F93803DA0F8083D8004D428B /* minidump_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F93803D90F8083D8004D428B /* minidump_generator_test.cc */; }; + F93DE2D80F82A70E00608B94 /* minidump_file_writer_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = F93DE2D70F82A70E00608B94 /* minidump_file_writer_unittest.cc */; }; + F93DE2D90F82A73500608B94 /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; }; + F93DE2DA0F82A73500608B94 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.c */; }; + F93DE2DB0F82A73500608B94 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; }; + F93DE3350F82C66B00608B94 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */; }; + F93DE3360F82C66B00608B94 /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */; }; + F93DE3370F82C66B00608B94 /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */; }; + F93DE3380F82C66B00608B94 /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; }; + F93DE3390F82C66B00608B94 /* convert_UTF.c in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.c */; }; + F93DE33A0F82C66B00608B94 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; }; + F93DE33B0F82C66B00608B94 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; }; + F93DE33C0F82C66B00608B94 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; }; + F93DE33D0F82C66B00608B94 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; }; + F93DE33E0F82C66B00608B94 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; }; + F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; }; + F93DE3410F82C68300608B94 /* exception_handler_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F93DE3400F82C68300608B94 /* exception_handler_test.cc */; }; + F945849E0F280E3C009A47BF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F945849C0F280E3C009A47BF /* Localizable.strings */; }; + F9B630A0100FF96B00D0F4AC /* goArrow.png in Resources */ = {isa = PBXBuildFile; fileRef = F9B6309F100FF96B00D0F4AC /* goArrow.png */; }; + F9C44DB20EF07288003AEBAA /* Controller.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DAC0EF07288003AEBAA /* Controller.m */; }; + F9C44DB30EF07288003AEBAA /* crashduringload in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DAD0EF07288003AEBAA /* crashduringload */; }; + F9C44DB40EF07288003AEBAA /* crashInMain in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DAE0EF07288003AEBAA /* crashInMain */; }; + F9C44DB60EF07288003AEBAA /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DB00EF07288003AEBAA /* main.m */; }; + F9C44DB70EF07288003AEBAA /* TestClass.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DB10EF07288003AEBAA /* TestClass.mm */; }; + F9C44DBC0EF072A0003AEBAA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DB80EF072A0003AEBAA /* InfoPlist.strings */; }; + F9C44DBD0EF072A0003AEBAA /* MainMenu.nib in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DBA0EF072A0003AEBAA /* MainMenu.nib */; }; + F9C44E000EF077CD003AEBAA /* Breakpad.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; }; + F9C44E3C0EF08B12003AEBAA /* Breakpad.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; }; + F9C44E980EF09F56003AEBAA /* crash_report_sender.app in Resources */ = {isa = PBXBuildFile; fileRef = F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */; }; + F9C44EA20EF09F93003AEBAA /* HTTPMultipartUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */; }; + F9C44EE50EF0A006003AEBAA /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */; }; + F9C44EE90EF0A3C1003AEBAA /* GTMLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44EE80EF0A3C1003AEBAA /* GTMLogger.m */; }; + F9C77DE20F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9C77DE10F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm */; }; + F9C77DE40F7DD82F0045F7DB /* SimpleStringDictionary.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53810ECCE635009BE4BA /* SimpleStringDictionary.mm */; }; + F9C77E130F7DDF810045F7DB /* GTMSenTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C77E120F7DDF810045F7DB /* GTMSenTestCase.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + F91AF6370FD60A74009D8BE2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8DC2EF4F0486A6940098B216; + remoteInfo = Breakpad; + }; + F92C564D0ECD10E5009BE4BA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C563B0ECD10B3009BE4BA; + remoteInfo = breakpadUtilities; + }; + F92C56850ECD15EF009BE4BA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C563B0ECD10B3009BE4BA; + remoteInfo = breakpadUtilities; + }; + F92C56870ECD15F1009BE4BA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C53530ECCE349009BE4BA; + remoteInfo = Inspector; + }; + F93DE2FB0F82C3C600608B94 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F93803BD0F80820F004D428B; + remoteInfo = generator_test; + }; + F93DE36F0F82CC1300608B94 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F93DE32B0F82C55600608B94; + remoteInfo = handler_test; + }; + F93DE3A60F830D1D00608B94 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9C77DD90F7DD5CF0045F7DB; + remoteInfo = UnitTests; + }; + F94585870F78232B009A47BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8DC2EF4F0486A6940098B216; + remoteInfo = Breakpad; + }; + F94585890F78232E009A47BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C53530ECCE349009BE4BA; + remoteInfo = Inspector; + }; + F945858B0F782330009A47BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C563B0ECD10B3009BE4BA; + remoteInfo = breakpadUtilities; + }; + F945858D0F782333009A47BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C569F0ECE04A7009BE4BA; + remoteInfo = crash_report_sender; + }; + F945858F0F782336009A47BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9C44DA40EF060A8003AEBAA; + remoteInfo = BreakpadTest; + }; + F95BB884101F949F00AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* crash_report */; + remoteInfo = crash_report; + }; + F95BB891101F94AC00AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* dump_syms */; + remoteInfo = dump_syms; + }; + F95BB89E101F94C000AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8DD76FA10486AA7600D96B5E /* symupload */; + remoteInfo = symupload; + }; + F95BB8A0101F94C000AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9BD835FB0B0544950055103E /* minidump_upload */; + remoteInfo = minidump_upload; + }; + F95BB8B2101F94D300AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* dump_syms */; + remoteInfo = dump_syms; + }; + F95BB8B4101F94D300AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* symupload */; + remoteInfo = symupload; + }; + F95BB8B6101F94D300AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 8DD76F960486AA7600D96B5E /* crash_report */; + remoteInfo = crash_report; + }; + F9C44E190EF0790F003AEBAA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8DC2EF4F0486A6940098B216; + remoteInfo = Breakpad; + }; + F9C44E960EF09F4B003AEBAA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C569F0ECE04A7009BE4BA; + remoteInfo = crash_report_sender; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + F9C44E410EF08B17003AEBAA /* Copy Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + F9C44E3C0EF08B12003AEBAA /* Breakpad.framework in Copy Frameworks */, + ); + name = "Copy Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Breakpad_Prefix.pch; path = Framework/Breakpad_Prefix.pch; sourceTree = ""; }; + 3329D4EC0FA16D820007BBC5 /* Breakpad.nib */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = Breakpad.nib; path = sender/Breakpad.nib; sourceTree = ""; }; + 33880C7F0F9E097100817F82 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/InfoPlist.strings; sourceTree = ""; }; + 4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = crash_report_sender.icns; path = sender/crash_report_sender.icns; sourceTree = ""; }; + 8DC2EF5B0486A6940098B216 /* Breakpad.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Breakpad.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F91AF5CF0FD60393009D8BE2 /* BreakpadFramework_Test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BreakpadFramework_Test.mm; path = tests/BreakpadFramework_Test.mm; sourceTree = ""; }; + F9286B380F7EB25800A4DCC8 /* Inspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Inspector.h; path = crash_generation/Inspector.h; sourceTree = ""; }; + F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = InspectorMain.mm; path = crash_generation/InspectorMain.mm; sourceTree = ""; }; + F92C53540ECCE349009BE4BA /* Inspector */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Inspector; sourceTree = BUILT_PRODUCTS_DIR; }; + F92C53670ECCE3FD009BE4BA /* breakpad_exc_server.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = breakpad_exc_server.c; path = handler/breakpad_exc_server.c; sourceTree = SOURCE_ROOT; }; + F92C53680ECCE3FD009BE4BA /* breakpad_exc_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = breakpad_exc_server.h; path = handler/breakpad_exc_server.h; sourceTree = SOURCE_ROOT; }; + F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = breakpad_nlist_64.cc; path = handler/breakpad_nlist_64.cc; sourceTree = SOURCE_ROOT; }; + F92C536A0ECCE3FD009BE4BA /* breakpad_nlist_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = breakpad_nlist_64.h; path = handler/breakpad_nlist_64.h; sourceTree = SOURCE_ROOT; }; + F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dynamic_images.cc; path = handler/dynamic_images.cc; sourceTree = SOURCE_ROOT; }; + F92C536C0ECCE3FD009BE4BA /* dynamic_images.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dynamic_images.h; path = handler/dynamic_images.h; sourceTree = SOURCE_ROOT; }; + F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = exception_handler.cc; path = handler/exception_handler.cc; sourceTree = SOURCE_ROOT; }; + F92C536E0ECCE3FD009BE4BA /* exception_handler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = exception_handler.h; path = handler/exception_handler.h; sourceTree = SOURCE_ROOT; }; + F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_generator.cc; path = handler/minidump_generator.cc; sourceTree = SOURCE_ROOT; }; + F92C53700ECCE3FD009BE4BA /* minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = minidump_generator.h; path = handler/minidump_generator.h; sourceTree = SOURCE_ROOT; }; + F92C53720ECCE3FD009BE4BA /* protected_memory_allocator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = protected_memory_allocator.cc; path = handler/protected_memory_allocator.cc; sourceTree = SOURCE_ROOT; }; + F92C53730ECCE3FD009BE4BA /* protected_memory_allocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = protected_memory_allocator.h; path = handler/protected_memory_allocator.h; sourceTree = SOURCE_ROOT; }; + F92C53740ECCE635009BE4BA /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = file_id.cc; path = ../../common/mac/file_id.cc; sourceTree = SOURCE_ROOT; }; + F92C53750ECCE635009BE4BA /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = file_id.h; path = ../../common/mac/file_id.h; sourceTree = SOURCE_ROOT; }; + F92C53760ECCE635009BE4BA /* HTTPMultipartUpload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTTPMultipartUpload.h; path = ../../common/mac/HTTPMultipartUpload.h; sourceTree = SOURCE_ROOT; }; + F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HTTPMultipartUpload.m; path = ../../common/mac/HTTPMultipartUpload.m; sourceTree = SOURCE_ROOT; }; + F92C53780ECCE635009BE4BA /* MachIPC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MachIPC.h; path = ../../common/mac/MachIPC.h; sourceTree = SOURCE_ROOT; }; + F92C53790ECCE635009BE4BA /* MachIPC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MachIPC.mm; path = ../../common/mac/MachIPC.mm; sourceTree = SOURCE_ROOT; }; + F92C537A0ECCE635009BE4BA /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_id.cc; path = ../../common/mac/macho_id.cc; sourceTree = SOURCE_ROOT; }; + F92C537B0ECCE635009BE4BA /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_id.h; path = ../../common/mac/macho_id.h; sourceTree = SOURCE_ROOT; }; + F92C537C0ECCE635009BE4BA /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_utilities.cc; path = ../../common/mac/macho_utilities.cc; sourceTree = SOURCE_ROOT; }; + F92C537D0ECCE635009BE4BA /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_utilities.h; path = ../../common/mac/macho_utilities.h; sourceTree = SOURCE_ROOT; }; + F92C537E0ECCE635009BE4BA /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_walker.cc; path = ../../common/mac/macho_walker.cc; sourceTree = SOURCE_ROOT; }; + F92C537F0ECCE635009BE4BA /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_walker.h; path = ../../common/mac/macho_walker.h; sourceTree = SOURCE_ROOT; }; + F92C53800ECCE635009BE4BA /* SimpleStringDictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SimpleStringDictionary.h; path = ../../common/mac/SimpleStringDictionary.h; sourceTree = SOURCE_ROOT; }; + F92C53810ECCE635009BE4BA /* SimpleStringDictionary.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SimpleStringDictionary.mm; path = ../../common/mac/SimpleStringDictionary.mm; sourceTree = SOURCE_ROOT; }; + F92C53820ECCE635009BE4BA /* string_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = string_utilities.cc; path = ../../common/mac/string_utilities.cc; sourceTree = SOURCE_ROOT; }; + F92C53830ECCE635009BE4BA /* string_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = string_utilities.h; path = ../../common/mac/string_utilities.h; sourceTree = SOURCE_ROOT; }; + F92C53850ECCE6AD009BE4BA /* string_conversion.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = string_conversion.cc; path = ../../common/string_conversion.cc; sourceTree = SOURCE_ROOT; }; + F92C53860ECCE6AD009BE4BA /* string_conversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = string_conversion.h; path = ../../common/string_conversion.h; sourceTree = SOURCE_ROOT; }; + F92C53870ECCE6C0009BE4BA /* convert_UTF.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = convert_UTF.c; path = ../../common/convert_UTF.c; sourceTree = SOURCE_ROOT; }; + F92C53880ECCE6C0009BE4BA /* convert_UTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = convert_UTF.h; path = ../../common/convert_UTF.h; sourceTree = SOURCE_ROOT; }; + F92C538E0ECCE70A009BE4BA /* minidump_file_writer-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "minidump_file_writer-inl.h"; path = "../minidump_file_writer-inl.h"; sourceTree = SOURCE_ROOT; }; + F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer.cc; path = ../minidump_file_writer.cc; sourceTree = SOURCE_ROOT; }; + F92C53900ECCE70A009BE4BA /* minidump_file_writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = minidump_file_writer.h; path = ../minidump_file_writer.h; sourceTree = SOURCE_ROOT; }; + F92C53B70ECCE7B3009BE4BA /* Inspector.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Inspector.mm; path = crash_generation/Inspector.mm; sourceTree = SOURCE_ROOT; }; + F92C554A0ECCF530009BE4BA /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = /System/Library/Frameworks/Carbon.framework; sourceTree = ""; }; + F92C55CE0ECD0064009BE4BA /* Breakpad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Breakpad.h; path = Framework/Breakpad.h; sourceTree = ""; }; + F92C55CF0ECD0064009BE4BA /* Breakpad.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Breakpad.mm; path = Framework/Breakpad.mm; sourceTree = ""; }; + F92C56310ECD0DF1009BE4BA /* OnDemandServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OnDemandServer.h; path = Framework/OnDemandServer.h; sourceTree = ""; }; + F92C56320ECD0DF1009BE4BA /* OnDemandServer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = OnDemandServer.mm; path = Framework/OnDemandServer.mm; sourceTree = ""; }; + F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = breakpadUtilities.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = crash_report_sender.app; sourceTree = BUILT_PRODUCTS_DIR; }; + F92C56A20ECE04A7009BE4BA /* crash_report_sender-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "crash_report_sender-Info.plist"; path = "sender/crash_report_sender-Info.plist"; sourceTree = ""; }; + F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = crash_report_sender.h; path = sender/crash_report_sender.h; sourceTree = ""; }; + F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = crash_report_sender.m; path = sender/crash_report_sender.m; sourceTree = ""; }; + F93803BE0F80820F004D428B /* generator_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = generator_test; sourceTree = BUILT_PRODUCTS_DIR; }; + F93803D90F8083D8004D428B /* minidump_generator_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_generator_test.cc; path = handler/minidump_generator_test.cc; sourceTree = ""; }; + F93DE2D10F82A67300608B94 /* minidump_file_writer_unittest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = minidump_file_writer_unittest; sourceTree = BUILT_PRODUCTS_DIR; }; + F93DE2D70F82A70E00608B94 /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer_unittest.cc; path = ../minidump_file_writer_unittest.cc; sourceTree = SOURCE_ROOT; }; + F93DE32C0F82C55600608B94 /* handler_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = handler_test; sourceTree = BUILT_PRODUCTS_DIR; }; + F93DE3400F82C68300608B94 /* exception_handler_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = exception_handler_test.cc; path = handler/exception_handler_test.cc; sourceTree = ""; }; + F945849D0F280E3C009A47BF /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/Localizable.strings; sourceTree = ""; }; + F945859D0F78241E009A47BF /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Framework/Info.plist; sourceTree = ""; }; + F95BB87C101F949F00AA053B /* crash_report.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = crash_report.xcodeproj; path = ../../tools/mac/crash_report/crash_report.xcodeproj; sourceTree = SOURCE_ROOT; }; + F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = dump_syms.xcodeproj; path = ../../tools/mac/dump_syms/dump_syms.xcodeproj; sourceTree = SOURCE_ROOT; }; + F95BB894101F94C000AA053B /* symupload.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = symupload.xcodeproj; path = ../../tools/mac/symupload/symupload.xcodeproj; sourceTree = SOURCE_ROOT; }; + F9B6309F100FF96B00D0F4AC /* goArrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = goArrow.png; path = sender/goArrow.png; sourceTree = ""; }; + F9C44DA50EF060A8003AEBAA /* BreakpadTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BreakpadTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + F9C44DAC0EF07288003AEBAA /* Controller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Controller.m; path = testapp/Controller.m; sourceTree = ""; }; + F9C44DAD0EF07288003AEBAA /* crashduringload */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = crashduringload; path = testapp/crashduringload; sourceTree = ""; }; + F9C44DAE0EF07288003AEBAA /* crashInMain */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = crashInMain; path = testapp/crashInMain; sourceTree = ""; }; + F9C44DAF0EF07288003AEBAA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = testapp/Info.plist; sourceTree = ""; }; + F9C44DB00EF07288003AEBAA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = testapp/main.m; sourceTree = ""; }; + F9C44DB10EF07288003AEBAA /* TestClass.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TestClass.mm; path = testapp/TestClass.mm; sourceTree = ""; }; + F9C44DB90EF072A0003AEBAA /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = testapp/English.lproj/InfoPlist.strings; sourceTree = ""; }; + F9C44DBB0EF072A0003AEBAA /* English */ = {isa = PBXFileReference; lastKnownFileType = wrapper.nib; name = English; path = testapp/English.lproj/MainMenu.nib; sourceTree = ""; }; + F9C44DBF0EF0778F003AEBAA /* Controller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Controller.h; path = testapp/Controller.h; sourceTree = ""; }; + F9C44DC00EF0778F003AEBAA /* TestClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TestClass.h; path = testapp/TestClass.h; sourceTree = ""; }; + F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = /System/Library/Frameworks/SystemConfiguration.framework; sourceTree = ""; }; + F9C44EE70EF0A3C1003AEBAA /* GTMLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMLogger.h; path = ../../common/mac/GTMLogger.h; sourceTree = SOURCE_ROOT; }; + F9C44EE80EF0A3C1003AEBAA /* GTMLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTMLogger.m; path = ../../common/mac/GTMLogger.m; sourceTree = SOURCE_ROOT; }; + F9C77DDA0F7DD5CF0045F7DB /* UnitTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; + F9C77DDB0F7DD5CF0045F7DB /* UnitTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "UnitTests-Info.plist"; sourceTree = ""; }; + F9C77DE00F7DD7E30045F7DB /* SimpleStringDictionaryTest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SimpleStringDictionaryTest.h; path = tests/SimpleStringDictionaryTest.h; sourceTree = ""; }; + F9C77DE10F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = SimpleStringDictionaryTest.mm; path = tests/SimpleStringDictionaryTest.mm; sourceTree = ""; }; + F9C77E110F7DDF810045F7DB /* GTMSenTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMSenTestCase.h; path = ../../common/mac/testing/GTMSenTestCase.h; sourceTree = SOURCE_ROOT; }; + F9C77E120F7DDF810045F7DB /* GTMSenTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTMSenTestCase.m; path = ../../common/mac/testing/GTMSenTestCase.m; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8DC2EF560486A6940098B216 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C565C0ECD1158009BE4BA /* breakpadUtilities.dylib in Frameworks */, + 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C53520ECCE349009BE4BA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C564C0ECD10DD009BE4BA /* breakpadUtilities.dylib in Frameworks */, + F92C554C0ECCF534009BE4BA /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C563A0ECD10B3009BE4BA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C56570ECD113E009BE4BA /* Carbon.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C569E0ECE04A7009BE4BA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44EE50EF0A006003AEBAA /* SystemConfiguration.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93803BC0F80820F004D428B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93DE2CF0F82A67300608B94 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93DE32A0F82C55600608B94 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C44DA30EF060A8003AEBAA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44E000EF077CD003AEBAA /* Breakpad.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C77DD70F7DD5CF0045F7DB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F91AF6210FD60784009D8BE2 /* Breakpad.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 034768DFFF38A50411DB9C8B /* Products */ = { + isa = PBXGroup; + children = ( + 8DC2EF5B0486A6940098B216 /* Breakpad.framework */, + F92C53540ECCE349009BE4BA /* Inspector */, + F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */, + F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */, + F9C44DA50EF060A8003AEBAA /* BreakpadTest.app */, + F9C77DDA0F7DD5CF0045F7DB /* UnitTests.octest */, + F93803BE0F80820F004D428B /* generator_test */, + F93DE2D10F82A67300608B94 /* minidump_file_writer_unittest */, + F93DE32C0F82C55600608B94 /* handler_test */, + ); + name = Products; + sourceTree = ""; + }; + 0867D691FE84028FC02AAC07 /* Breakpad */ = { + isa = PBXGroup; + children = ( + F95BB8A3101F94C300AA053B /* Tools */, + 32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */, + F92C538D0ECCE6F2009BE4BA /* client */, + F92C53600ECCE3D6009BE4BA /* common */, + 0867D69AFE84028FC02AAC07 /* Frameworks */, + 034768DFFF38A50411DB9C8B /* Products */, + F9C77DDB0F7DD5CF0045F7DB /* UnitTests-Info.plist */, + ); + name = Breakpad; + sourceTree = ""; + }; + 0867D69AFE84028FC02AAC07 /* Frameworks */ = { + isa = PBXGroup; + children = ( + F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */, + F92C554A0ECCF530009BE4BA /* Carbon.framework */, + 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */, + 0867D6A5FE840307C02AAC07 /* AppKit.framework */, + 0867D69BFE84028FC02AAC07 /* Foundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + F92C53590ECCE3BB009BE4BA /* handler */ = { + isa = PBXGroup; + children = ( + F93DE3400F82C68300608B94 /* exception_handler_test.cc */, + F93803D90F8083D8004D428B /* minidump_generator_test.cc */, + F92C53670ECCE3FD009BE4BA /* breakpad_exc_server.c */, + F92C53680ECCE3FD009BE4BA /* breakpad_exc_server.h */, + F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */, + F92C536A0ECCE3FD009BE4BA /* breakpad_nlist_64.h */, + F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */, + F92C536C0ECCE3FD009BE4BA /* dynamic_images.h */, + F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */, + F92C536E0ECCE3FD009BE4BA /* exception_handler.h */, + F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */, + F92C53700ECCE3FD009BE4BA /* minidump_generator.h */, + F92C53720ECCE3FD009BE4BA /* protected_memory_allocator.cc */, + F92C53730ECCE3FD009BE4BA /* protected_memory_allocator.h */, + ); + name = handler; + sourceTree = ""; + }; + F92C53600ECCE3D6009BE4BA /* common */ = { + isa = PBXGroup; + children = ( + F92C53870ECCE6C0009BE4BA /* convert_UTF.c */, + F92C53880ECCE6C0009BE4BA /* convert_UTF.h */, + F92C53850ECCE6AD009BE4BA /* string_conversion.cc */, + F92C53860ECCE6AD009BE4BA /* string_conversion.h */, + F92C53840ECCE68D009BE4BA /* mac */, + ); + name = common; + sourceTree = ""; + }; + F92C53840ECCE68D009BE4BA /* mac */ = { + isa = PBXGroup; + children = ( + F9C77E0F0F7DDF650045F7DB /* testing */, + F9C44EE70EF0A3C1003AEBAA /* GTMLogger.h */, + F9C44EE80EF0A3C1003AEBAA /* GTMLogger.m */, + F92C53740ECCE635009BE4BA /* file_id.cc */, + F92C53750ECCE635009BE4BA /* file_id.h */, + F92C53760ECCE635009BE4BA /* HTTPMultipartUpload.h */, + F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */, + F92C53780ECCE635009BE4BA /* MachIPC.h */, + F92C53790ECCE635009BE4BA /* MachIPC.mm */, + F92C537A0ECCE635009BE4BA /* macho_id.cc */, + F92C537B0ECCE635009BE4BA /* macho_id.h */, + F92C537C0ECCE635009BE4BA /* macho_utilities.cc */, + F92C537D0ECCE635009BE4BA /* macho_utilities.h */, + F92C537E0ECCE635009BE4BA /* macho_walker.cc */, + F92C537F0ECCE635009BE4BA /* macho_walker.h */, + F92C53800ECCE635009BE4BA /* SimpleStringDictionary.h */, + F92C53810ECCE635009BE4BA /* SimpleStringDictionary.mm */, + F92C53820ECCE635009BE4BA /* string_utilities.cc */, + F92C53830ECCE635009BE4BA /* string_utilities.h */, + ); + name = mac; + sourceTree = ""; + }; + F92C538D0ECCE6F2009BE4BA /* client */ = { + isa = PBXGroup; + children = ( + F92C53990ECCE78E009BE4BA /* mac */, + F92C538E0ECCE70A009BE4BA /* minidump_file_writer-inl.h */, + F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */, + F92C53900ECCE70A009BE4BA /* minidump_file_writer.h */, + F93DE2D70F82A70E00608B94 /* minidump_file_writer_unittest.cc */, + ); + name = client; + sourceTree = ""; + }; + F92C53990ECCE78E009BE4BA /* mac */ = { + isa = PBXGroup; + children = ( + F9C77DDF0F7DD7CF0045F7DB /* tests */, + F9C44DAB0EF0726F003AEBAA /* testapp */, + F92C56A60ECE04B6009BE4BA /* sender */, + F92C55CD0ECD0053009BE4BA /* Framework */, + F92C53B50ECCE799009BE4BA /* crash_generation */, + F92C53590ECCE3BB009BE4BA /* handler */, + ); + name = mac; + sourceTree = ""; + }; + F92C53B50ECCE799009BE4BA /* crash_generation */ = { + isa = PBXGroup; + children = ( + F9286B380F7EB25800A4DCC8 /* Inspector.h */, + F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */, + F92C53B70ECCE7B3009BE4BA /* Inspector.mm */, + ); + name = crash_generation; + sourceTree = ""; + }; + F92C55CD0ECD0053009BE4BA /* Framework */ = { + isa = PBXGroup; + children = ( + F945859D0F78241E009A47BF /* Info.plist */, + F92C56310ECD0DF1009BE4BA /* OnDemandServer.h */, + F92C56320ECD0DF1009BE4BA /* OnDemandServer.mm */, + F92C55CE0ECD0064009BE4BA /* Breakpad.h */, + F92C55CF0ECD0064009BE4BA /* Breakpad.mm */, + ); + name = Framework; + sourceTree = ""; + }; + F92C56A60ECE04B6009BE4BA /* sender */ = { + isa = PBXGroup; + children = ( + F9B6309F100FF96B00D0F4AC /* goArrow.png */, + F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */, + F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */, + F945849C0F280E3C009A47BF /* Localizable.strings */, + 33880C7E0F9E097100817F82 /* InfoPlist.strings */, + 3329D4EC0FA16D820007BBC5 /* Breakpad.nib */, + 4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */, + F92C56A20ECE04A7009BE4BA /* crash_report_sender-Info.plist */, + ); + name = sender; + sourceTree = ""; + }; + F95BB87D101F949F00AA053B /* Products */ = { + isa = PBXGroup; + children = ( + F95BB885101F949F00AA053B /* crash_report */, + ); + name = Products; + sourceTree = ""; + }; + F95BB88A101F94AC00AA053B /* Products */ = { + isa = PBXGroup; + children = ( + F95BB892101F94AC00AA053B /* dump_syms */, + ); + name = Products; + sourceTree = ""; + }; + F95BB895101F94C000AA053B /* Products */ = { + isa = PBXGroup; + children = ( + F95BB89F101F94C000AA053B /* symupload */, + F95BB8A1101F94C000AA053B /* minidump_upload */, + ); + name = Products; + sourceTree = ""; + }; + F95BB8A3101F94C300AA053B /* Tools */ = { + isa = PBXGroup; + children = ( + F95BB894101F94C000AA053B /* symupload.xcodeproj */, + F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */, + F95BB87C101F949F00AA053B /* crash_report.xcodeproj */, + ); + name = Tools; + sourceTree = ""; + }; + F9C44DAB0EF0726F003AEBAA /* testapp */ = { + isa = PBXGroup; + children = ( + F9C44DBF0EF0778F003AEBAA /* Controller.h */, + F9C44DC00EF0778F003AEBAA /* TestClass.h */, + F9C44DB80EF072A0003AEBAA /* InfoPlist.strings */, + F9C44DBA0EF072A0003AEBAA /* MainMenu.nib */, + F9C44DAC0EF07288003AEBAA /* Controller.m */, + F9C44DAD0EF07288003AEBAA /* crashduringload */, + F9C44DAE0EF07288003AEBAA /* crashInMain */, + F9C44DAF0EF07288003AEBAA /* Info.plist */, + F9C44DB00EF07288003AEBAA /* main.m */, + F9C44DB10EF07288003AEBAA /* TestClass.mm */, + ); + name = testapp; + sourceTree = ""; + }; + F9C77DDF0F7DD7CF0045F7DB /* tests */ = { + isa = PBXGroup; + children = ( + F9C77DE00F7DD7E30045F7DB /* SimpleStringDictionaryTest.h */, + F9C77DE10F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm */, + F91AF5CF0FD60393009D8BE2 /* BreakpadFramework_Test.mm */, + ); + name = tests; + sourceTree = ""; + }; + F9C77E0F0F7DDF650045F7DB /* testing */ = { + isa = PBXGroup; + children = ( + F9C77E110F7DDF810045F7DB /* GTMSenTestCase.h */, + F9C77E120F7DDF810045F7DB /* GTMSenTestCase.m */, + ); + name = testing; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 8DC2EF500486A6940098B216 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C55D00ECD0064009BE4BA /* Breakpad.h in Headers */, + F92C56330ECD0DF1009BE4BA /* OnDemandServer.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C56380ECD10B3009BE4BA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 8DC2EF4F0486A6940098B216 /* Breakpad */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */; + buildPhases = ( + F97A0E850ED4EC15008784D3 /* Change install name of breakpadUtilities */, + 8DC2EF500486A6940098B216 /* Headers */, + 8DC2EF520486A6940098B216 /* Resources */, + 8DC2EF540486A6940098B216 /* Sources */, + 8DC2EF560486A6940098B216 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + F92C56860ECD15EF009BE4BA /* PBXTargetDependency */, + F92C56880ECD15F1009BE4BA /* PBXTargetDependency */, + F9C44E970EF09F4B003AEBAA /* PBXTargetDependency */, + ); + name = Breakpad; + productInstallPath = "$(HOME)/Library/Frameworks"; + productName = Breakpad; + productReference = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; + productType = "com.apple.product-type.framework"; + }; + F92C53530ECCE349009BE4BA /* Inspector */ = { + isa = PBXNativeTarget; + buildConfigurationList = F92C53580ECCE36D009BE4BA /* Build configuration list for PBXNativeTarget "Inspector" */; + buildPhases = ( + F94584840F27FB40009A47BF /* Change install name of breakpadUtilities */, + F92C53510ECCE349009BE4BA /* Sources */, + F92C53520ECCE349009BE4BA /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + F92C564E0ECD10E5009BE4BA /* PBXTargetDependency */, + ); + name = Inspector; + productName = Inspector; + productReference = F92C53540ECCE349009BE4BA /* Inspector */; + productType = "com.apple.product-type.tool"; + }; + F92C563B0ECD10B3009BE4BA /* breakpadUtilities */ = { + isa = PBXNativeTarget; + buildConfigurationList = F92C56670ECD11A3009BE4BA /* Build configuration list for PBXNativeTarget "breakpadUtilities" */; + buildPhases = ( + F92C56380ECD10B3009BE4BA /* Headers */, + F92C56390ECD10B3009BE4BA /* Sources */, + F92C563A0ECD10B3009BE4BA /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = breakpadUtilities; + productName = breakpadUtilities; + productReference = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + F92C569F0ECE04A7009BE4BA /* crash_report_sender */ = { + isa = PBXNativeTarget; + buildConfigurationList = F92C56A50ECE04A8009BE4BA /* Build configuration list for PBXNativeTarget "crash_report_sender" */; + buildPhases = ( + F92C569C0ECE04A7009BE4BA /* Resources */, + F92C569D0ECE04A7009BE4BA /* Sources */, + F92C569E0ECE04A7009BE4BA /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = crash_report_sender; + productName = crash_report_sender; + productReference = F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */; + productType = "com.apple.product-type.application"; + }; + F93803BD0F80820F004D428B /* generator_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = F93803C40F80822E004D428B /* Build configuration list for PBXNativeTarget "generator_test" */; + buildPhases = ( + F93803BB0F80820F004D428B /* Sources */, + F93803BC0F80820F004D428B /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = generator_test; + productName = generator_test; + productReference = F93803BE0F80820F004D428B /* generator_test */; + productType = "com.apple.product-type.tool"; + }; + F93DE2D00F82A67300608B94 /* minidump_file_writer_unittest */ = { + isa = PBXNativeTarget; + buildConfigurationList = F93DE2D60F82A67700608B94 /* Build configuration list for PBXNativeTarget "minidump_file_writer_unittest" */; + buildPhases = ( + F93DE2CE0F82A67300608B94 /* Sources */, + F93DE2CF0F82A67300608B94 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = minidump_file_writer_unittest; + productName = minidump_file_writer_unittest; + productReference = F93DE2D10F82A67300608B94 /* minidump_file_writer_unittest */; + productType = "com.apple.product-type.tool"; + }; + F93DE32B0F82C55600608B94 /* handler_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = F93DE3320F82C5D800608B94 /* Build configuration list for PBXNativeTarget "handler_test" */; + buildPhases = ( + F93DE3290F82C55600608B94 /* Sources */, + F93DE32A0F82C55600608B94 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = handler_test; + productName = handler_test; + productReference = F93DE32C0F82C55600608B94 /* handler_test */; + productType = "com.apple.product-type.tool"; + }; + F9C44DA40EF060A8003AEBAA /* BreakpadTest */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9C44DAA0EF060A9003AEBAA /* Build configuration list for PBXNativeTarget "BreakpadTest" */; + buildPhases = ( + F9C44DA10EF060A8003AEBAA /* Resources */, + F9C44DA20EF060A8003AEBAA /* Sources */, + F9C44DA30EF060A8003AEBAA /* Frameworks */, + F9C44E410EF08B17003AEBAA /* Copy Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + F9C44E1A0EF0790F003AEBAA /* PBXTargetDependency */, + ); + name = BreakpadTest; + productName = BreakpadTest; + productReference = F9C44DA50EF060A8003AEBAA /* BreakpadTest.app */; + productType = "com.apple.product-type.application"; + }; + F9C77DD90F7DD5CF0045F7DB /* UnitTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9C77DDE0F7DD5D00045F7DB /* Build configuration list for PBXNativeTarget "UnitTests" */; + buildPhases = ( + F9C77DD50F7DD5CF0045F7DB /* Resources */, + F9C77DD60F7DD5CF0045F7DB /* Sources */, + F9C77DD70F7DD5CF0045F7DB /* Frameworks */, + F9C77DD80F7DD5CF0045F7DB /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + F93DE2FC0F82C3C600608B94 /* PBXTargetDependency */, + F93DE3700F82CC1300608B94 /* PBXTargetDependency */, + F91AF6380FD60A74009D8BE2 /* PBXTargetDependency */, + ); + name = UnitTests; + productName = UnitTests; + productReference = F9C77DDA0F7DD5CF0045F7DB /* UnitTests.octest */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0867D690FE84028FC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Breakpad" */; + compatibilityVersion = "Xcode 2.4"; + hasScannedForEncodings = 1; + mainGroup = 0867D691FE84028FC02AAC07 /* Breakpad */; + productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = F95BB87D101F949F00AA053B /* Products */; + ProjectRef = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */; + }, + { + ProductGroup = F95BB88A101F94AC00AA053B /* Products */; + ProjectRef = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + }, + { + ProductGroup = F95BB895101F94C000AA053B /* Products */; + ProjectRef = F95BB894101F94C000AA053B /* symupload.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 8DC2EF4F0486A6940098B216 /* Breakpad */, + F92C53530ECCE349009BE4BA /* Inspector */, + F92C563B0ECD10B3009BE4BA /* breakpadUtilities */, + F92C569F0ECE04A7009BE4BA /* crash_report_sender */, + F9C44DA40EF060A8003AEBAA /* BreakpadTest */, + F94585840F782326009A47BF /* All */, + F9C77DD90F7DD5CF0045F7DB /* UnitTests */, + F93803BD0F80820F004D428B /* generator_test */, + F93DE2D00F82A67300608B94 /* minidump_file_writer_unittest */, + F93DE32B0F82C55600608B94 /* handler_test */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + F95BB885101F949F00AA053B /* crash_report */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = crash_report; + remoteRef = F95BB884101F949F00AA053B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + F95BB892101F94AC00AA053B /* dump_syms */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = dump_syms; + remoteRef = F95BB891101F94AC00AA053B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + F95BB89F101F94C000AA053B /* symupload */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = symupload; + remoteRef = F95BB89E101F94C000AA053B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + F95BB8A1101F94C000AA053B /* minidump_upload */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = minidump_upload; + remoteRef = F95BB8A0101F94C000AA053B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 8DC2EF520486A6940098B216 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44E980EF09F56003AEBAA /* crash_report_sender.app in Resources */, + F92C568A0ECD15F9009BE4BA /* Inspector in Resources */, + F92C56650ECD1185009BE4BA /* breakpadUtilities.dylib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C569C0ECE04A7009BE4BA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F945849E0F280E3C009A47BF /* Localizable.strings in Resources */, + 4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */, + 33880C800F9E097100817F82 /* InfoPlist.strings in Resources */, + 3329D4ED0FA16D820007BBC5 /* Breakpad.nib in Resources */, + F9B630A0100FF96B00D0F4AC /* goArrow.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C44DA10EF060A8003AEBAA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44DB30EF07288003AEBAA /* crashduringload in Resources */, + F9C44DB40EF07288003AEBAA /* crashInMain in Resources */, + F9C44DBC0EF072A0003AEBAA /* InfoPlist.strings in Resources */, + F9C44DBD0EF072A0003AEBAA /* MainMenu.nib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C77DD50F7DD5CF0045F7DB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + F94584840F27FB40009A47BF /* Change install name of breakpadUtilities */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Change install name of breakpadUtilities"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "install_name_tool -id \"@executable_path/../Resources/breakpadUtilities.dylib\" \"${BUILT_PRODUCTS_DIR}/breakpadUtilities.dylib\"\n"; + }; + F97A0E850ED4EC15008784D3 /* Change install name of breakpadUtilities */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Change install name of breakpadUtilities"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/bin/bash\ninstall_name_tool -id \"@executable_path/../Frameworks/Breakpad.framework/Resources/breakpadUtilities.dylib\" \"${BUILT_PRODUCTS_DIR}/breakpadUtilities.dylib\"\n"; + }; + F9C77DD80F7DD5CF0045F7DB /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n\necho running minidump generator tests...\n\"${BUILT_PRODUCTS_DIR}/generator_test\"\necho Running exception handler tests...\n\"${BUILT_PRODUCTS_DIR}/handler_test\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8DC2EF540486A6940098B216 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C565F0ECD116B009BE4BA /* protected_memory_allocator.cc in Sources */, + F92C56630ECD1179009BE4BA /* exception_handler.cc in Sources */, + F92C55D10ECD0064009BE4BA /* Breakpad.mm in Sources */, + F92C56340ECD0DF1009BE4BA /* OnDemandServer.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C53510ECCE349009BE4BA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C53B80ECCE7B3009BE4BA /* Inspector.mm in Sources */, + F9286B3A0F7EB25800A4DCC8 /* InspectorMain.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C56390ECD10B3009BE4BA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C563F0ECD10CA009BE4BA /* convert_UTF.c in Sources */, + F92C56400ECD10CA009BE4BA /* dynamic_images.cc in Sources */, + F92C56410ECD10CA009BE4BA /* file_id.cc in Sources */, + F92C56420ECD10CA009BE4BA /* macho_id.cc in Sources */, + F92C56430ECD10CA009BE4BA /* macho_utilities.cc in Sources */, + F92C56440ECD10CA009BE4BA /* macho_walker.cc in Sources */, + F92C56450ECD10CA009BE4BA /* MachIPC.mm in Sources */, + F92C56460ECD10CA009BE4BA /* minidump_file_writer.cc in Sources */, + F92C56470ECD10CA009BE4BA /* minidump_generator.cc in Sources */, + F92C56480ECD10CA009BE4BA /* SimpleStringDictionary.mm in Sources */, + F92C56490ECD10CA009BE4BA /* string_utilities.cc in Sources */, + F92C564A0ECD10CA009BE4BA /* string_conversion.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C569D0ECE04A7009BE4BA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44EA20EF09F93003AEBAA /* HTTPMultipartUpload.m in Sources */, + F92C56A90ECE04C5009BE4BA /* crash_report_sender.m in Sources */, + F9C44EE90EF0A3C1003AEBAA /* GTMLogger.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93803BB0F80820F004D428B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F93803CD0F8083B7004D428B /* dynamic_images.cc in Sources */, + F93803CE0F8083B7004D428B /* exception_handler.cc in Sources */, + F93803CF0F8083B7004D428B /* minidump_generator.cc in Sources */, + F93803D00F8083B7004D428B /* minidump_file_writer.cc in Sources */, + F93803D10F8083B7004D428B /* convert_UTF.c in Sources */, + F93803D20F8083B7004D428B /* string_conversion.cc in Sources */, + F93803D30F8083B7004D428B /* file_id.cc in Sources */, + F93803D40F8083B7004D428B /* macho_id.cc in Sources */, + F93803D50F8083B7004D428B /* macho_utilities.cc in Sources */, + F93803D60F8083B7004D428B /* macho_walker.cc in Sources */, + F93803D70F8083B7004D428B /* string_utilities.cc in Sources */, + F93803DA0F8083D8004D428B /* minidump_generator_test.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93DE2CE0F82A67300608B94 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F93DE2D90F82A73500608B94 /* minidump_file_writer.cc in Sources */, + F93DE2DA0F82A73500608B94 /* convert_UTF.c in Sources */, + F93DE2DB0F82A73500608B94 /* string_conversion.cc in Sources */, + F93DE2D80F82A70E00608B94 /* minidump_file_writer_unittest.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93DE3290F82C55600608B94 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F93DE3350F82C66B00608B94 /* dynamic_images.cc in Sources */, + F93DE3360F82C66B00608B94 /* exception_handler.cc in Sources */, + F93DE3370F82C66B00608B94 /* minidump_generator.cc in Sources */, + F93DE3380F82C66B00608B94 /* minidump_file_writer.cc in Sources */, + F93DE3390F82C66B00608B94 /* convert_UTF.c in Sources */, + F93DE33A0F82C66B00608B94 /* string_conversion.cc in Sources */, + F93DE33B0F82C66B00608B94 /* file_id.cc in Sources */, + F93DE33C0F82C66B00608B94 /* macho_id.cc in Sources */, + F93DE33D0F82C66B00608B94 /* macho_utilities.cc in Sources */, + F93DE33E0F82C66B00608B94 /* macho_walker.cc in Sources */, + F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */, + F93DE3410F82C68300608B94 /* exception_handler_test.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C44DA20EF060A8003AEBAA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44DB20EF07288003AEBAA /* Controller.m in Sources */, + F9C44DB60EF07288003AEBAA /* main.m in Sources */, + F9C44DB70EF07288003AEBAA /* TestClass.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C77DD60F7DD5CF0045F7DB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C77DE40F7DD82F0045F7DB /* SimpleStringDictionary.mm in Sources */, + F9C77DE20F7DD7E30045F7DB /* SimpleStringDictionaryTest.mm in Sources */, + F9C77E130F7DDF810045F7DB /* GTMSenTestCase.m in Sources */, + F91AF5D00FD60393009D8BE2 /* BreakpadFramework_Test.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + F91AF6380FD60A74009D8BE2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8DC2EF4F0486A6940098B216 /* Breakpad */; + targetProxy = F91AF6370FD60A74009D8BE2 /* PBXContainerItemProxy */; + }; + F92C564E0ECD10E5009BE4BA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C563B0ECD10B3009BE4BA /* breakpadUtilities */; + targetProxy = F92C564D0ECD10E5009BE4BA /* PBXContainerItemProxy */; + }; + F92C56860ECD15EF009BE4BA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C563B0ECD10B3009BE4BA /* breakpadUtilities */; + targetProxy = F92C56850ECD15EF009BE4BA /* PBXContainerItemProxy */; + }; + F92C56880ECD15F1009BE4BA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C53530ECCE349009BE4BA /* Inspector */; + targetProxy = F92C56870ECD15F1009BE4BA /* PBXContainerItemProxy */; + }; + F93DE2FC0F82C3C600608B94 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F93803BD0F80820F004D428B /* generator_test */; + targetProxy = F93DE2FB0F82C3C600608B94 /* PBXContainerItemProxy */; + }; + F93DE3700F82CC1300608B94 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F93DE32B0F82C55600608B94 /* handler_test */; + targetProxy = F93DE36F0F82CC1300608B94 /* PBXContainerItemProxy */; + }; + F93DE3A70F830D1D00608B94 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9C77DD90F7DD5CF0045F7DB /* UnitTests */; + targetProxy = F93DE3A60F830D1D00608B94 /* PBXContainerItemProxy */; + }; + F94585880F78232B009A47BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8DC2EF4F0486A6940098B216 /* Breakpad */; + targetProxy = F94585870F78232B009A47BF /* PBXContainerItemProxy */; + }; + F945858A0F78232E009A47BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C53530ECCE349009BE4BA /* Inspector */; + targetProxy = F94585890F78232E009A47BF /* PBXContainerItemProxy */; + }; + F945858C0F782330009A47BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C563B0ECD10B3009BE4BA /* breakpadUtilities */; + targetProxy = F945858B0F782330009A47BF /* PBXContainerItemProxy */; + }; + F945858E0F782333009A47BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C569F0ECE04A7009BE4BA /* crash_report_sender */; + targetProxy = F945858D0F782333009A47BF /* PBXContainerItemProxy */; + }; + F94585900F782336009A47BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9C44DA40EF060A8003AEBAA /* BreakpadTest */; + targetProxy = F945858F0F782336009A47BF /* PBXContainerItemProxy */; + }; + F95BB8B3101F94D300AA053B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = dump_syms; + targetProxy = F95BB8B2101F94D300AA053B /* PBXContainerItemProxy */; + }; + F95BB8B5101F94D300AA053B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = symupload; + targetProxy = F95BB8B4101F94D300AA053B /* PBXContainerItemProxy */; + }; + F95BB8B7101F94D300AA053B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = crash_report; + targetProxy = F95BB8B6101F94D300AA053B /* PBXContainerItemProxy */; + }; + F9C44E1A0EF0790F003AEBAA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8DC2EF4F0486A6940098B216 /* Breakpad */; + targetProxy = F9C44E190EF0790F003AEBAA /* PBXContainerItemProxy */; + }; + F9C44E970EF09F4B003AEBAA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C569F0ECE04A7009BE4BA /* crash_report_sender */; + targetProxy = F9C44E960EF09F4B003AEBAA /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33880C7E0F9E097100817F82 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 33880C7F0F9E097100817F82 /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + F945849C0F280E3C009A47BF /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + F945849D0F280E3C009A47BF /* English */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + F9C44DB80EF072A0003AEBAA /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + F9C44DB90EF072A0003AEBAA /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + F9C44DBA0EF072A0003AEBAA /* MainMenu.nib */ = { + isa = PBXVariantGroup; + children = ( + F9C44DBB0EF072A0003AEBAA /* English */, + ); + name = MainMenu.nib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 1DEB91AE08733DA50010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Framework/Breakpad_Prefix.pch; + HEADER_SEARCH_PATHS = "../..//**"; + INFOPLIST_FILE = Framework/Info.plist; + INSTALL_PATH = "@executable_path/../Frameworks"; + PRODUCT_NAME = Breakpad; + WRAPPER_EXTENSION = framework; + }; + name = Debug; + }; + 1DEB91AF08733DA50010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Framework/Breakpad_Prefix.pch; + HEADER_SEARCH_PATHS = "../..//**"; + INFOPLIST_FILE = Framework/Info.plist; + INSTALL_PATH = "@executable_path/../Frameworks"; + PRODUCT_NAME = Breakpad; + WRAPPER_EXTENSION = framework; + }; + name = Release; + }; + 1DEB91B208733DA50010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ONLY_ACTIVE_ARCH_PRE_XCODE_3_1)"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH_PRE_XCODE_3_1 = "$(NATIVE_ARCH)"; + PREBINDING = NO; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + }; + name = Debug; + }; + 1DEB91B308733DA50010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1)"; + ARCHS_STANDARD_32_BIT_PRE_XCODE_3_1 = "ppc i386"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = "../..//**"; + PREBINDING = NO; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + }; + name = Release; + }; + F92C53560ECCE34A009BE4BA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = "../..//**"; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-lcrypto", + "$(inherited)", + ); + PREBINDING = NO; + PRODUCT_NAME = Inspector; + }; + name = Debug; + }; + F92C53570ECCE34A009BE4BA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEAD_CODE_STRIPPING = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + HEADER_SEARCH_PATHS = "../..//**"; + INSTALL_PATH = /usr/local/bin; + LD_GENERATE_MAP_FILE = YES; + OTHER_LDFLAGS = ( + "$(inherited)", + "-lcrypto", + ); + PREBINDING = NO; + PRODUCT_NAME = Inspector; + ZERO_LINK = NO; + }; + name = Release; + }; + F92C563D0ECD10B3009BE4BA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "../..//**"; + INSTALL_PATH = /usr/local/lib; + LD_DYLIB_INSTALL_NAME = "@executable_path/../Resources/$(EXECUTABLE_PATH)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-headerpad_max_install_names", + "-lcrypto", + ); + PREBINDING = NO; + PRODUCT_NAME = breakpadUtilities; + }; + name = Debug; + }; + F92C563E0ECD10B3009BE4BA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "../..//**"; + INSTALL_PATH = /usr/local/lib; + LD_DYLIB_INSTALL_NAME = "@executable_path/../Resources/$(EXECUTABLE_PATH)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-headerpad_max_install_names", + "-lcrypto", + ); + PREBINDING = NO; + PRODUCT_NAME = breakpadUtilities; + ZERO_LINK = NO; + }; + name = Release; + }; + F92C56A30ECE04A8009BE4BA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "../..//**"; + INFOPLIST_FILE = "sender/crash_report_sender-Info.plist"; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = crash_report_sender; + }; + name = Debug; + }; + F92C56A40ECE04A8009BE4BA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "../..//**"; + INFOPLIST_FILE = "sender/crash_report_sender-Info.plist"; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = crash_report_sender; + ZERO_LINK = NO; + }; + name = Release; + }; + F93803C00F808210004D428B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-lcrypto", + "-framework", + Foundation, + ); + PREBINDING = NO; + PRODUCT_NAME = generator_test; + USER_HEADER_SEARCH_PATHS = ../../; + }; + name = Debug; + }; + F93803C10F808210004D428B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-lcrypto", + "-framework", + Foundation, + ); + PREBINDING = NO; + PRODUCT_NAME = generator_test; + ZERO_LINK = NO; + }; + name = Release; + }; + F93DE2D30F82A67400608B94 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_CHAR_IS_UNSIGNED_CHAR = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = minidump_file_writer_unittest; + USER_HEADER_SEARCH_PATHS = ../../; + }; + name = Debug; + }; + F93DE2D40F82A67400608B94 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = minidump_file_writer_unittest; + ZERO_LINK = NO; + }; + name = Release; + }; + F93DE32E0F82C55700608B94 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-lcrypto", + "-framework", + Foundation, + ); + PREBINDING = NO; + PRODUCT_NAME = handler_test; + USER_HEADER_SEARCH_PATHS = ../../; + }; + name = Debug; + }; + F93DE32F0F82C55700608B94 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-lcrypto", + "-framework", + Foundation, + ); + PREBINDING = NO; + PRODUCT_NAME = handler_test; + ZERO_LINK = NO; + }; + name = Release; + }; + F93DE3B90F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ONLY_ACTIVE_ARCH_PRE_XCODE_3_1)"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH_PRE_XCODE_3_1 = "$(NATIVE_ARCH)"; + PREBINDING = NO; + SDKROOT = /Developer/SDKs/MacOSX10.4u.sdk; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BA0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Framework/Breakpad_Prefix.pch; + HEADER_SEARCH_PATHS = "../..//**"; + INFOPLIST_FILE = Framework/Info.plist; + INSTALL_PATH = "@executable_path/../Frameworks"; + PRODUCT_NAME = Breakpad; + WRAPPER_EXTENSION = framework; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BB0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = "../..//**"; + INSTALL_PATH = /usr/local/bin; + OTHER_LDFLAGS = ( + "-lcrypto", + "$(inherited)", + ); + PREBINDING = NO; + PRODUCT_NAME = Inspector; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BC0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "../..//**"; + INSTALL_PATH = /usr/local/lib; + LD_DYLIB_INSTALL_NAME = "@executable_path/../Resources/$(EXECUTABLE_PATH)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-headerpad_max_install_names", + "-lcrypto", + ); + PREBINDING = NO; + PRODUCT_NAME = breakpadUtilities; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BD0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + HEADER_SEARCH_PATHS = "../..//**"; + INFOPLIST_FILE = "sender/crash_report_sender-Info.plist"; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = crash_report_sender; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BE0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/build/$(CONFIGURATION)"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; + INFOPLIST_FILE = testapp/Info.plist; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = BreakpadTest; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BF0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = All; + }; + name = "Debug With Code Coverage"; + }; + F93DE3C00F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Cocoa.framework/Headers/Cocoa.h"; + INFOPLIST_FILE = "UnitTests-Info.plist"; + INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles"; + LIBRARY_SEARCH_PATHS = ./gcov; + OTHER_LDFLAGS = ( + "-lgcov", + "-framework", + Cocoa, + "-framework", + SenTestingKit, + ); + PREBINDING = NO; + PRODUCT_NAME = UnitTests; + USER_HEADER_SEARCH_PATHS = "../../ ../../common/mac/**"; + WRAPPER_EXTENSION = octest; + }; + name = "Debug With Code Coverage"; + }; + F93DE3C10F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + LIBRARY_SEARCH_PATHS = ./gcov; + OTHER_LDFLAGS = ( + "-lgcov", + "-lcrypto", + "-framework", + Foundation, + ); + PREBINDING = NO; + PRODUCT_NAME = generator_test; + USER_HEADER_SEARCH_PATHS = ../../; + }; + name = "Debug With Code Coverage"; + }; + F93DE3C20F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_CHAR_IS_UNSIGNED_CHAR = YES; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + LIBRARY_SEARCH_PATHS = ./gcov; + OTHER_LDFLAGS = ( + "-lgcov", + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = minidump_file_writer_unittest; + USER_HEADER_SEARCH_PATHS = ../../; + }; + name = "Debug With Code Coverage"; + }; + F93DE3C30F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_GENERATE_TEST_COVERAGE_FILES = YES; + GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + INSTALL_PATH = /usr/local/bin; + LIBRARY_SEARCH_PATHS = ./gcov; + OTHER_LDFLAGS = ( + "-lcrypto", + "-lgcov", + "-framework", + Foundation, + ); + PREBINDING = NO; + PRODUCT_NAME = handler_test; + USER_HEADER_SEARCH_PATHS = ../../; + }; + name = "Debug With Code Coverage"; + }; + F94585850F782326009A47BF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + PRODUCT_NAME = All; + }; + name = Debug; + }; + F94585860F782326009A47BF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + PRODUCT_NAME = All; + ZERO_LINK = NO; + }; + name = Release; + }; + F9C44DA80EF060A8003AEBAA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/build/$(CONFIGURATION)"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/AppKit.framework/Headers/AppKit.h"; + INFOPLIST_FILE = testapp/Info.plist; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = BreakpadTest; + }; + name = Debug; + }; + F9C44DA90EF060A8003AEBAA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/build/$(CONFIGURATION)"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + INFOPLIST_FILE = testapp/Info.plist; + INSTALL_PATH = "$(HOME)/Applications"; + OTHER_LDFLAGS = ( + "-framework", + Foundation, + "-framework", + AppKit, + ); + PREBINDING = NO; + PRODUCT_NAME = BreakpadTest; + ZERO_LINK = NO; + }; + name = Release; + }; + F9C77DDC0F7DD5D00045F7DB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + COPY_PHASE_STRIP = NO; + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/Cocoa.framework/Headers/Cocoa.h"; + INFOPLIST_FILE = "UnitTests-Info.plist"; + INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles"; + OTHER_LDFLAGS = ( + "-framework", + Cocoa, + "-framework", + SenTestingKit, + ); + PREBINDING = NO; + PRODUCT_NAME = UnitTests; + USER_HEADER_SEARCH_PATHS = "../../ ../../common/mac/**"; + WRAPPER_EXTENSION = octest; + }; + name = Debug; + }; + F9C77DDD0F7DD5D00045F7DB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_MODEL_TUNING = G5; + INFOPLIST_FILE = "UnitTests-Info.plist"; + INSTALL_PATH = "$(USER_LIBRARY_DIR)/Bundles"; + OTHER_LDFLAGS = ( + "-framework", + Cocoa, + "-framework", + SenTestingKit, + ); + PREBINDING = NO; + PRODUCT_NAME = UnitTests; + WRAPPER_EXTENSION = octest; + ZERO_LINK = NO; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB91AE08733DA50010E9CD /* Debug */, + F93DE3BA0F830E7000608B94 /* Debug With Code Coverage */, + 1DEB91AF08733DA50010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Breakpad" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB91B208733DA50010E9CD /* Debug */, + F93DE3B90F830E7000608B94 /* Debug With Code Coverage */, + 1DEB91B308733DA50010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F92C53580ECCE36D009BE4BA /* Build configuration list for PBXNativeTarget "Inspector" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F92C53560ECCE34A009BE4BA /* Debug */, + F93DE3BB0F830E7000608B94 /* Debug With Code Coverage */, + F92C53570ECCE34A009BE4BA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F92C56670ECD11A3009BE4BA /* Build configuration list for PBXNativeTarget "breakpadUtilities" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F92C563D0ECD10B3009BE4BA /* Debug */, + F93DE3BC0F830E7000608B94 /* Debug With Code Coverage */, + F92C563E0ECD10B3009BE4BA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F92C56A50ECE04A8009BE4BA /* Build configuration list for PBXNativeTarget "crash_report_sender" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F92C56A30ECE04A8009BE4BA /* Debug */, + F93DE3BD0F830E7000608B94 /* Debug With Code Coverage */, + F92C56A40ECE04A8009BE4BA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F93803C40F80822E004D428B /* Build configuration list for PBXNativeTarget "generator_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F93803C00F808210004D428B /* Debug */, + F93DE3C10F830E7000608B94 /* Debug With Code Coverage */, + F93803C10F808210004D428B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F93DE2D60F82A67700608B94 /* Build configuration list for PBXNativeTarget "minidump_file_writer_unittest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F93DE2D30F82A67400608B94 /* Debug */, + F93DE3C20F830E7000608B94 /* Debug With Code Coverage */, + F93DE2D40F82A67400608B94 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F93DE3320F82C5D800608B94 /* Build configuration list for PBXNativeTarget "handler_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F93DE32E0F82C55700608B94 /* Debug */, + F93DE3C30F830E7000608B94 /* Debug With Code Coverage */, + F93DE32F0F82C55700608B94 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F94585930F78235C009A47BF /* Build configuration list for PBXAggregateTarget "All" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F94585850F782326009A47BF /* Debug */, + F93DE3BF0F830E7000608B94 /* Debug With Code Coverage */, + F94585860F782326009A47BF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9C44DAA0EF060A9003AEBAA /* Build configuration list for PBXNativeTarget "BreakpadTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9C44DA80EF060A8003AEBAA /* Debug */, + F93DE3BE0F830E7000608B94 /* Debug With Code Coverage */, + F9C44DA90EF060A8003AEBAA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9C77DDE0F7DD5D00045F7DB /* Build configuration list for PBXNativeTarget "UnitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9C77DDC0F7DD5D00045F7DB /* Debug */, + F93DE3C00F830E7000608B94 /* Debug With Code Coverage */, + F9C77DDD0F7DD5D00045F7DB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0867D690FE84028FC02AAC07 /* Project object */; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,193 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Interface file between the Breakpad.framework and +// the Inspector process. + +#import "common/mac/SimpleStringDictionary.h" + +#import +#import "client/mac/handler/minidump_generator.h" + +#define VERBOSE 0 + +extern bool gDebugLog; + +#define DEBUGLOG if (gDebugLog) fprintf + +// Types of mach messsages (message IDs) +enum { + kMsgType_InspectorInitialInfo = 0, // data is InspectorInfo + kMsgType_InspectorKeyValuePair = 1, // data is KeyValueMessageData + kMsgType_InspectorAcknowledgement = 2 // no data sent +}; + +// Initial information sent from the crashed process by +// Breakpad.framework to the Inspector process +// The mach message with this struct as data will also include +// several descriptors for sending mach port rights to the crashed +// task, etc. +struct InspectorInfo { + int exception_type; + int exception_code; + int exception_subcode; + unsigned int parameter_count; // key-value pairs +}; + +// Key/value message data to be sent to the Inspector +struct KeyValueMessageData { + public: + KeyValueMessageData() {} + KeyValueMessageData(const google_breakpad::KeyValueEntry &inEntry) { + strlcpy(key, inEntry.GetKey(), sizeof(key) ); + strlcpy(value, inEntry.GetValue(), sizeof(value) ); + } + + char key[google_breakpad::KeyValueEntry::MAX_STRING_STORAGE_SIZE]; + char value[google_breakpad::KeyValueEntry::MAX_STRING_STORAGE_SIZE]; +}; + +using std::string; +using google_breakpad::MinidumpGenerator; + +namespace google_breakpad { + +static BOOL EnsureDirectoryPathExists(NSString *dirPath); + +//============================================================================= +class ConfigFile { + public: + ConfigFile() { + config_file_ = -1; + config_file_path_[0] = 0; + has_created_file_ = false; + }; + + ~ConfigFile() { + }; + + void WriteFile(const SimpleStringDictionary *configurationParameters, + const char *dump_dir, + const char *minidump_id); + + const char *GetFilePath() { return config_file_path_; } + + void Unlink() { + if (config_file_ != -1) + unlink(config_file_path_); + + config_file_ = -1; + } + + private: + BOOL WriteData(const void *data, size_t length); + + BOOL AppendConfigData(const char *key, + const void *data, + size_t length); + + BOOL AppendConfigString(const char *key, + const char *value); + + int config_file_; // descriptor for config file + char config_file_path_[PATH_MAX]; // Path to configuration file + bool has_created_file_; +}; + +//============================================================================= +class MinidumpLocation { + public: + MinidumpLocation(const NSString *minidumpDir) { + // Ensure that the path exists. Fallback to /tmp if unable to locate path. + assert(minidumpDir); + if (!EnsureDirectoryPathExists(minidumpDir)) { + DEBUGLOG(stderr, "Unable to create: %s\n", [minidumpDir UTF8String]); + minidumpDir = @"/tmp"; + } + + strlcpy(minidump_dir_path_, [minidumpDir fileSystemRepresentation], + sizeof(minidump_dir_path_)); + + // now generate a unique ID + string dump_path(minidump_dir_path_); + string next_minidump_id; + + string next_minidump_path_ = + (MinidumpGenerator::UniqueNameInDirectory(dump_path, &next_minidump_id)); + + strlcpy(minidump_id_, next_minidump_id.c_str(), sizeof(minidump_id_)); + }; + + const char *GetPath() { return minidump_dir_path_; } + const char *GetID() { return minidump_id_; } + + private: + char minidump_dir_path_[PATH_MAX]; // Path to minidump directory + char minidump_id_[128]; +}; + +//============================================================================= +class Inspector { + public: + Inspector() {}; + + // given a bootstrap service name, receives mach messages + // from a crashed process, then inspects it, creates a minidump file + // and asks the user if he wants to upload it to a server. + void Inspect(const char *receive_port_name); + + private: + kern_return_t ServiceCheckIn(const char *receive_port_name); + kern_return_t ServiceCheckOut(const char *receive_port_name); + + kern_return_t ReadMessages(); + + bool InspectTask(); + kern_return_t SendAcknowledgement(); + void LaunchReporter(const char *inConfigFilePath); + + void SetCrashTimeParameters(); + + mach_port_t service_rcv_port_; + + int exception_type_; + int exception_code_; + int exception_subcode_; + mach_port_t remote_task_; + mach_port_t crashing_thread_; + mach_port_t handler_thread_; + mach_port_t ack_port_; + + SimpleStringDictionary config_params_; + + ConfigFile config_file_; +}; + + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/InspectorMain.mm firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/InspectorMain.mm --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/InspectorMain.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/InspectorMain.mm 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,65 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Main driver for Inspector + +#import "client/mac/crash_generation/Inspector.h" +#import + +namespace google_breakpad { + +//============================================================================= +extern "C" { + +int main(int argc, char *const argv[]) { +#if DEBUG + // Since we're launched on-demand, this is necessary to see debugging + // output in the console window. + freopen("/dev/console", "w", stdout); + freopen("/dev/console", "w", stderr); +#endif + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + if (argc != 2) { + exit(0); + } + // Our first command-line argument contains the name of the service + // that we're providing. + google_breakpad::Inspector inspector; + inspector.Inspect(argv[1]); + + [pool release]; + + return 0; +} + +} // extern "C" + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.mm firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.mm --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.mm 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,553 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Utility that can inspect another process and write a crash dump + +#include +#include +#include +#include +#include +#include + +#import "client/mac/crash_generation/Inspector.h" + +#import "client/mac/Framework/Breakpad.h" +#import "client/mac/handler/minidump_generator.h" + +#import "common/mac/SimpleStringDictionary.h" +#import "common/mac/MachIPC.h" + +#import + +#if VERBOSE + bool gDebugLog = true; +#else + bool gDebugLog = false; +#endif + +namespace google_breakpad { + +//============================================================================= +static BOOL EnsureDirectoryPathExists(NSString *dirPath) { + NSFileManager *mgr = [NSFileManager defaultManager]; + + // If we got a relative path, prepend the current directory + if (![dirPath isAbsolutePath]) + dirPath = [[mgr currentDirectoryPath] stringByAppendingPathComponent:dirPath]; + + NSString *path = dirPath; + + // Ensure that no file exists within the path which would block creation + while (1) { + BOOL isDir; + if ([mgr fileExistsAtPath:path isDirectory:&isDir]) { + if (isDir) + break; + + return NO; + } + + path = [path stringByDeletingLastPathComponent]; + } + + // Path now contains the first valid directory (or is empty) + if (![path length]) + return NO; + + NSString *common = + [dirPath commonPrefixWithString:path options:NSLiteralSearch]; + + // If everything is good + if ([common isEqualToString:dirPath]) + return YES; + + // Break up the difference into components + NSString *diff = [dirPath substringFromIndex:[common length] + 1]; + NSArray *components = [diff pathComponents]; + unsigned count = [components count]; + + // Rebuild the path one component at a time + NSDictionary *attrs = + [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750] + forKey:NSFilePosixPermissions]; + path = common; + for (unsigned i = 0; i < count; ++i) { + path = [path stringByAppendingPathComponent:[components objectAtIndex:i]]; + + if (![mgr createDirectoryAtPath:path attributes:attrs]) + return NO; + } + + return YES; +} + +//============================================================================= +BOOL ConfigFile::WriteData(const void *data, size_t length) { + size_t result = write(config_file_, data, length); + + return result == length; +} + +//============================================================================= +BOOL ConfigFile::AppendConfigData(const char *key, + const void *data, size_t length) { + assert(config_file_ != -1); + + if (!key) { + DEBUGLOG(stderr, "Breakpad: Missing Key\n"); + return NO; + } + + if (!data) { + DEBUGLOG(stderr, "Breakpad: Missing data for key: %s\n", key ? key : + ""); + return NO; + } + + // Write the key, \n, length of data (ascii integer), \n, data + char buffer[16]; + char nl = '\n'; + BOOL result = WriteData(key, strlen(key)); + + snprintf(buffer, sizeof(buffer) - 1, "\n%lu\n", length); + result &= WriteData(buffer, strlen(buffer)); + result &= WriteData(data, length); + result &= WriteData(&nl, 1); + return result; +} + +//============================================================================= +BOOL ConfigFile::AppendConfigString(const char *key, + const char *value) { + return AppendConfigData(key, value, strlen(value)); +} + +//============================================================================= +void ConfigFile::WriteFile(const SimpleStringDictionary *configurationParameters, + const char *dump_dir, + const char *minidump_id) { + + assert(config_file_ == -1); + + // Open and write out configuration file preamble + strlcpy(config_file_path_, "/tmp/Config-XXXXXX", + sizeof(config_file_path_)); + config_file_ = mkstemp(config_file_path_); + + if (config_file_ == -1) { + DEBUGLOG(stderr, + "mkstemp(config_file_path_) == -1 (%s)\n", + strerror(errno)); + return; + } + else { + DEBUGLOG(stderr, "Writing config file to (%s)\n", config_file_path_); + } + + has_created_file_ = true; + + // Add the minidump dir + AppendConfigString(kReporterMinidumpDirectoryKey, dump_dir); + AppendConfigString(kReporterMinidumpIDKey, minidump_id); + + // Write out the configuration parameters + BOOL result = YES; + const SimpleStringDictionary &dictionary = *configurationParameters; + + const KeyValueEntry *entry = NULL; + SimpleStringDictionaryIterator iter(dictionary); + + while ((entry = iter.Next())) { + DEBUGLOG(stderr, + "config: (%s) -> (%s)\n", + entry->GetKey(), + entry->GetValue()); + result = AppendConfigString(entry->GetKey(), entry->GetValue()); + + if (!result) + break; + } + + close(config_file_); + config_file_ = -1; +} + +//============================================================================= +void Inspector::Inspect(const char *receive_port_name) { + kern_return_t result = ServiceCheckIn(receive_port_name); + + if (result == KERN_SUCCESS) { + result = ReadMessages(); + + if (result == KERN_SUCCESS) { + // Inspect the task and write a minidump file. + bool wrote_minidump = InspectTask(); + + // Send acknowledgement to the crashed process that the inspection + // has finished. It will then be able to cleanly exit. + // The return value is ignored because failure isn't fatal. If the process + // didn't get the message there's nothing we can do, and we still want to + // send the report. + SendAcknowledgement(); + + if (wrote_minidump) { + // Ask the user if he wants to upload the crash report to a server, + // and do so if he agrees. + LaunchReporter(config_file_.GetFilePath()); + } else { + fprintf(stderr, "Inspection of crashed process failed\n"); + } + + // Now that we're done reading messages, cleanup the service, but only + // if there was an actual exception + // Otherwise, it means the dump was generated on demand and the process + // lives on, and we might be needed again in the future. + if (exception_code_) { + ServiceCheckOut(receive_port_name); + } + } else { + PRINT_MACH_RESULT(result, "Inspector: WaitForMessage()"); + } + } +} + +//============================================================================= +kern_return_t Inspector::ServiceCheckIn(const char *receive_port_name) { + // We need to get the mach port representing this service, so we can + // get information from the crashed process. + kern_return_t kr = bootstrap_check_in(bootstrap_port, + (char*)receive_port_name, + &service_rcv_port_); + + if (kr != KERN_SUCCESS) { +#if VERBOSE + PRINT_MACH_RESULT(kr, "Inspector: bootstrap_check_in()"); +#endif + } + + return kr; +} + +//============================================================================= +kern_return_t Inspector::ServiceCheckOut(const char *receive_port_name) { + // We're done receiving mach messages from the crashed process, + // so clean up a bit. + kern_return_t kr; + + // DO NOT use mach_port_deallocate() here -- it will fail and the + // following bootstrap_register() will also fail leaving our service + // name hanging around forever (until reboot) + kr = mach_port_destroy(mach_task_self(), service_rcv_port_); + + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, + "Inspector: UNREGISTERING: service_rcv_port mach_port_deallocate()"); + return kr; + } + + // Unregister the service associated with the receive port. + kr = bootstrap_register(bootstrap_port, + (char*)receive_port_name, + MACH_PORT_NULL); + + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, "Inspector: UNREGISTERING: bootstrap_register()"); + } + + return kr; +} + +//============================================================================= +kern_return_t Inspector::ReadMessages() { + // Wait for an initial message from the crashed process containing basic + // information about the crash. + ReceivePort receive_port(service_rcv_port_); + + MachReceiveMessage message; + kern_return_t result = receive_port.WaitForMessage(&message, 1000); + + if (result == KERN_SUCCESS) { + InspectorInfo &info = (InspectorInfo &)*message.GetData(); + exception_type_ = info.exception_type; + exception_code_ = info.exception_code; + exception_subcode_ = info.exception_subcode; + +#if VERBOSE + printf("message ID = %d\n", message.GetMessageID()); +#endif + + remote_task_ = message.GetTranslatedPort(0); + crashing_thread_ = message.GetTranslatedPort(1); + handler_thread_ = message.GetTranslatedPort(2); + ack_port_ = message.GetTranslatedPort(3); + +#if VERBOSE + printf("exception_type = %d\n", exception_type_); + printf("exception_code = %d\n", exception_code_); + printf("exception_subcode = %d\n", exception_subcode_); + printf("remote_task = %d\n", remote_task_); + printf("crashing_thread = %d\n", crashing_thread_); + printf("handler_thread = %d\n", handler_thread_); + printf("ack_port_ = %d\n", ack_port_); + printf("parameter count = %d\n", info.parameter_count); +#endif + + // In certain situations where multiple crash requests come + // through quickly, we can end up with the mach IPC messages not + // coming through correctly. Since we don't know what parameters + // we've missed, we can't do much besides abort the crash dump + // situation in this case. + unsigned int parameters_read = 0; + // The initial message contains the number of key value pairs that + // we are expected to read. + // Read each key/value pair, one mach message per key/value pair. + for (unsigned int i = 0; i < info.parameter_count; ++i) { + MachReceiveMessage message; + result = receive_port.WaitForMessage(&message, 1000); + + if(result == KERN_SUCCESS) { + KeyValueMessageData &key_value_data = + (KeyValueMessageData&)*message.GetData(); + // If we get a blank key, make sure we don't increment the + // parameter count; in some cases (notably on-demand generation + // many times in a short period of time) caused the Mach IPC + // messages to not come through correctly. + if (strlen(key_value_data.key) == 0) { + continue; + } + parameters_read++; + + config_params_.SetKeyValue(key_value_data.key, key_value_data.value); + } else { + PRINT_MACH_RESULT(result, "Inspector: key/value message"); + break; + } + } + if (parameters_read != info.parameter_count) { + DEBUGLOG(stderr, "Only read %d parameters instead of %d, aborting crash " + "dump generation.", parameters_read, info.parameter_count); + return KERN_FAILURE; + } + } + + return result; +} + +//============================================================================= +// Sets keys in the parameters dictionary that are specific to process uptime. +// The two we set are process up time, and process crash time. +void Inspector::SetCrashTimeParameters() { + // Set process uptime parameter + struct timeval tv; + gettimeofday(&tv, NULL); + + char processUptimeString[32], processCrashtimeString[32]; + const char *processStartTimeString = + config_params_.GetValueForKey(BREAKPAD_PROCESS_START_TIME); + + // Set up time if we've received the start time. + if (processStartTimeString) { + time_t processStartTime = strtol(processStartTimeString, NULL, 10); + time_t processUptime = tv.tv_sec - processStartTime; + sprintf(processUptimeString, "%d", processUptime); + config_params_.SetKeyValue(BREAKPAD_PROCESS_UP_TIME, processUptimeString); + } + + sprintf(processCrashtimeString, "%d", tv.tv_sec); + config_params_.SetKeyValue(BREAKPAD_PROCESS_CRASH_TIME, + processCrashtimeString); +} + +bool Inspector::InspectTask() { + // keep the task quiet while we're looking at it + task_suspend(remote_task_); + DEBUGLOG(stderr, "Suspended Remote task\n"); + + NSString *minidumpDir; + + const char *minidumpDirectory = + config_params_.GetValueForKey(BREAKPAD_DUMP_DIRECTORY); + + SetCrashTimeParameters(); + // If the client app has not specified a minidump directory, + // use a default of Library// + if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) { + NSArray *libraryDirectories = + NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, + NSUserDomainMask, + YES); + + NSString *applicationSupportDirectory = + [libraryDirectories objectAtIndex:0]; + NSString *library_subdirectory = [NSString + stringWithUTF8String:kDefaultLibrarySubdirectory]; + NSString *breakpad_product = [NSString + stringWithUTF8String:config_params_.GetValueForKey(BREAKPAD_PRODUCT)]; + + NSArray *path_components = [NSArray + arrayWithObjects:applicationSupportDirectory, + library_subdirectory, + breakpad_product, + nil]; + + minidumpDir = [NSString pathWithComponents:path_components]; + } else { + minidumpDir = [[NSString stringWithUTF8String:minidumpDirectory] + stringByExpandingTildeInPath]; + } + DEBUGLOG(stderr, + "Writing minidump to directory (%s)\n", + [minidumpDir UTF8String]); + + MinidumpLocation minidumpLocation(minidumpDir); + + // Obscure bug alert: + // Don't use [NSString stringWithFormat] to build up the path here since it + // assumes system encoding and in RTL locales will prepend an LTR override + // character for paths beginning with '/' which fileSystemRepresentation does + // not remove. Filed as rdar://6889706 . + NSString *path_ns = [NSString + stringWithUTF8String:minidumpLocation.GetPath()]; + NSString *pathid_ns = [NSString + stringWithUTF8String:minidumpLocation.GetID()]; + NSString *minidumpPath = [path_ns stringByAppendingPathComponent:pathid_ns]; + minidumpPath = [minidumpPath + stringByAppendingPathExtension:@"dmp"]; + + DEBUGLOG(stderr, + "minidump path (%s)\n", + [minidumpPath UTF8String]); + + + config_file_.WriteFile( &config_params_, + minidumpLocation.GetPath(), + minidumpLocation.GetID()); + + + MinidumpGenerator generator(remote_task_, handler_thread_); + + if (exception_type_ && exception_code_) { + generator.SetExceptionInformation(exception_type_, + exception_code_, + exception_subcode_, + crashing_thread_); + } + + + bool result = generator.Write([minidumpPath fileSystemRepresentation]); + + if (result) { + DEBUGLOG(stderr, "Wrote minidump - OK\n"); + } else { + DEBUGLOG(stderr, "Error writing minidump - errno=%s\n", strerror(errno)); + } + + // let the task continue + task_resume(remote_task_); + DEBUGLOG(stderr, "Resumed remote task\n"); + + return result; +} + +//============================================================================= +// The crashed task needs to be told that the inspection has finished. +// It will wait on a mach port (with timeout) until we send acknowledgement. +kern_return_t Inspector::SendAcknowledgement() { + if (ack_port_ != MACH_PORT_DEAD) { + MachPortSender sender(ack_port_); + MachSendMessage ack_message(kMsgType_InspectorAcknowledgement); + + DEBUGLOG(stderr, "Inspector: trying to send acknowledgement to port %d\n", + ack_port_); + + kern_return_t result = sender.SendMessage(ack_message, 2000); + +#if VERBOSE + PRINT_MACH_RESULT(result, "Inspector: sent acknowledgement"); +#endif + + return result; + } + + DEBUGLOG(stderr, "Inspector: port translation failure!\n"); + return KERN_INVALID_NAME; +} + +//============================================================================= +void Inspector::LaunchReporter(const char *inConfigFilePath) { + // Extract the path to the reporter executable. + const char *reporterExecutablePath = + config_params_.GetValueForKey(BREAKPAD_REPORTER_EXE_LOCATION); + DEBUGLOG(stderr, "reporter path = %s\n", reporterExecutablePath); + + // Setup and launch the crash dump sender. + const char *argv[3]; + argv[0] = reporterExecutablePath; + argv[1] = inConfigFilePath; + argv[2] = NULL; + + // Launch the reporter + pid_t pid = fork(); + + // If we're in the child, load in our new executable and run. + // The parent will not wait for the child to complete. + if (pid == 0) { + execv(argv[0], (char * const *)argv); + config_file_.Unlink(); // launch failed - get rid of config file + DEBUGLOG(stderr, "Inspector: unable to launch reporter app\n"); + _exit(1); + } + + // Wait until the Reporter child process exits. + // + + // We'll use a timeout of one minute. + int timeoutCount = 60; // 60 seconds + + while (timeoutCount-- > 0) { + int status; + pid_t result = waitpid(pid, &status, WNOHANG); + + if (result == 0) { + // The child has not yet finished. + sleep(1); + } else if (result == -1) { + DEBUGLOG(stderr, "Inspector: waitpid error (%d) waiting for reporter app\n", + errno); + break; + } else { + // child has finished + break; + } + } +} + +} // namespace google_breakpad + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,310 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Framework to provide a simple C API to crash reporting for +// applications. By default, if any machine-level exception (e.g., +// EXC_BAD_ACCESS) occurs, it will be handled by the BreakpadRef +// object as follows: +// +// 1. Create a minidump file (see Breakpad for details) +// 2. Prompt the user (using CFUserNotification) +// 3. Invoke a command line reporting tool to send the minidump to a +// server +// +// By specifying parameters to the BreakpadCreate function, you can +// modify the default behavior to suit your needs and wants and +// desires. + +typedef void *BreakpadRef; + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + + // Keys for configuration file +#define kReporterMinidumpDirectoryKey "MinidumpDir" +#define kReporterMinidumpIDKey "MinidumpID" + +// The default subdirectory of the Library to put crash dumps in +// The subdirectory is +// ~/Library// +#define kDefaultLibrarySubdirectory "Breakpad" + +// Specify some special keys to be used in the configuration file that is +// generated by Breakpad and consumed by the crash_sender. +#define BREAKPAD_PRODUCT "BreakpadProduct" +#define BREAKPAD_PRODUCT_DISPLAY "BreakpadProductDisplay" +#define BREAKPAD_VERSION "BreakpadVersion" +#define BREAKPAD_VENDOR "BreakpadVendor" +#define BREAKPAD_URL "BreakpadURL" +#define BREAKPAD_REPORT_INTERVAL "BreakpadReportInterval" +#define BREAKPAD_SKIP_CONFIRM "BreakpadSkipConfirm" +#define BREAKPAD_CONFIRM_TIMEOUT "BreakpadConfirmTimeout" +#define BREAKPAD_SEND_AND_EXIT "BreakpadSendAndExit" +#define BREAKPAD_DUMP_DIRECTORY "BreakpadMinidumpLocation" +#define BREAKPAD_INSPECTOR_LOCATION "BreakpadInspectorLocation" +#define BREAKPAD_REPORTER_EXE_LOCATION \ + "BreakpadReporterExeLocation" +#define BREAKPAD_LOGFILES "BreakpadLogFiles" +#define BREAKPAD_LOGFILE_UPLOAD_SIZE "BreakpadLogFileTailSize" +#define BREAKPAD_REQUEST_COMMENTS "BreakpadRequestComments" +#define BREAKPAD_COMMENTS "BreakpadComments" +#define BREAKPAD_REQUEST_EMAIL "BreakpadRequestEmail" +#define BREAKPAD_EMAIL "BreakpadEmail" +#define BREAKPAD_SERVER_TYPE "BreakpadServerType" +#define BREAKPAD_SERVER_PARAMETER_DICT "BreakpadServerParameters" + +// The keys below are NOT user supplied, and are used internally. +#define BREAKPAD_PROCESS_START_TIME "BreakpadProcStartTime" +#define BREAKPAD_PROCESS_UP_TIME "BreakpadProcessUpTime" +#define BREAKPAD_PROCESS_CRASH_TIME "BreakpadProcessCrashTime" +#define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile" +#define BREAKPAD_SERVER_PARAMETER_PREFIX "BreakpadServerParameterPrefix_" +#define BREAKPAD_ON_DEMAND "BreakpadOnDemand" + +// Optional user-defined function to dec to decide if we should handle +// this crash or forward it along. +// Return true if you want Breakpad to handle it. +// Return false if you want Breakpad to skip it +// The exception handler always returns false, as if SEND_AND_EXIT were false +// (which means the next exception handler will take the exception) +typedef bool (*BreakpadFilterCallback)(int exception_type, + int exception_code, + mach_port_t crashing_thread, + void *context); + +// Create a new BreakpadRef object and install it as an exception +// handler. The |parameters| will typically be the contents of your +// bundle's Info.plist. +// +// You can also specify these additional keys for customizable behavior: +// Key: Value: +// BREAKPAD_PRODUCT Product name (e.g., "MyAwesomeProduct") +// This one is used as the key to identify +// the product when uploading +// REQUIRED +// +// BREAKPAD_PRODUCT_DISPLAY This is the display name, e.g. a pretty +// name for the product when the crash_sender +// pops up UI for the user. Falls back to +// BREAKPAD_PRODUCT if not specified. +// +// BREAKPAD_VERSION Product version (e.g., 1.2.3), used +// as metadata for crash report +// REQUIRED +// +// BREAKPAD_VENDOR Vendor name, used in UI (e.g. "A report has +// been created that you can send to ") +// +// BREAKPAD_URL URL destination for reporting +// REQUIRED +// +// BREAKPAD_REPORT_INTERVAL # of seconds between sending +// reports. If an additional report is +// generated within this time, it will +// be ignored. Default: 3600sec. +// Specify 0 to send all reports. +// +// BREAKPAD_SKIP_CONFIRM If true, the reporter will send the report +// without any user intervention. +// Defaults to NO +// +// BREAKPAD_CONFIRM_TIMEOUT Number of seconds before the upload +// confirmation dialog will be automatically +// dismissed (cancelling the upload). +// Default: 300 seconds (min of 60). +// Specify 0 to prevent timeout. +// +// BREAKPAD_SEND_AND_EXIT If true, the handler will exit after sending. +// This will prevent any other handler (e.g., +// CrashReporter) from getting the crash. +// Defaults TO YES +// +// BREAKPAD_DUMP_DIRECTORY The directory to store crash-dumps +// in. By default, we use +// ~/Library/Breakpad/ +// The path you specify here is tilde-expanded. +// +// BREAKPAD_INSPECTOR_LOCATION The full path to the Inspector executable. +// Defaults to /Inspector +// +// BREAKPAD_REPORTER_EXE_LOCATION The full path to the Reporter/sender +// executable. +// Default: +// /crash_report_sender.app +// +// BREAKPAD_LOGFILES Indicates an array of log file paths that +// should be uploaded at crash time. +// +// BREAKPAD_REQUEST_COMMENTS If true, the message dialog will have a +// text box for the user to enter comments. +// Default: NO +// +// BREAKPAD_REQUEST_EMAIL If true and BREAKPAD_REQUEST_COMMENTS is also +// true, the message dialog will have a text +// box for the user to enter their email address. +// Default: NO +// +// BREAKPAD_SERVER_TYPE A parameter that tells Breakpad how to +// rewrite the upload parameters for a specific +// server type. The currently valid values are +// 'socorro' or 'google'. If you want to add +// other types, see the function in +// crash_report_sender.m that maps parameters to +// URL parameters. Defaults to 'google'. +// +// BREAKPAD_SERVER_PARAMETER_DICT A plist dictionary of static +// parameters that are uploaded to the +// server. The parameters are sent as +// is to the crash server. Their +// content isn't added to the minidump +// but pass as URL parameters when +// uploading theminidump to the crash +// server. +//============================================================================= +// The BREAKPAD_PRODUCT, BREAKPAD_VERSION and BREAKPAD_URL are +// required to have non-NULL values. By default, the BREAKPAD_PRODUCT +// will be the CFBundleName and the BREAKPAD_VERSION will be the +// CFBundleVersion when these keys are present in the bundle's +// Info.plist, which is usually passed in to BreakpadCreate() as an +// NSDictionary (you could also pass in another dictionary that had +// the same keys configured). If the BREAKPAD_PRODUCT or +// BREAKPAD_VERSION are ultimately undefined, BreakpadCreate() will +// fail. You have been warned. +// +// If you are running in a debugger, Breakpad will not install, unless the +// BREAKPAD_IGNORE_DEBUGGER envionment variable is set and/or non-zero. +// +// The BREAKPAD_SKIP_CONFIRM and BREAKPAD_SEND_AND_EXIT default +// values are NO and YES. However, they can be controlled by setting their +// values in a user or global plist. +// +// It's easiest to use Breakpad via the Framework, but if you're compiling the +// code in directly, BREAKPAD_INSPECTOR_LOCATION and +// BREAKPAD_REPORTER_EXE_LOCATION allow you to specify custom paths +// to the helper executables. +// +//============================================================================= +// The following are NOT user-supplied but are documented here for +// completeness. They are calculated by Breakpad during initialization & +// crash-dump generation, or entered in by the user. +// +// BREAKPAD_PROCESS_START_TIME The time the process started. +// +// BREAKPAD_PROCESS_CRASH_TIME The time the process crashed. +// +// BREAKPAD_PROCESS_UP_TIME The total time the process has been +// running. This parameter is not set +// until the crash-dump-generation phase. +// +// BREAKPAD_LOGFILE_KEY_PREFIX Used to find out which parameters in the +// parameter dictionary correspond to log +// file paths. +// +// BREAKPAD_SERVER_PARAMETER_PREFIX This prefix is used by Breakpad +// internally, because Breakpad uses +// the same dictionary internally to +// track both its internal +// configuration parameters and +// parameters meant to be uploaded +// to the server. This string is +// used internally by Breakpad to +// prefix user-supplied parameter +// names so those can be sent to the +// server without leaking Breakpad's +// internal values. +// +// BREAKPAD_ON_DEMAND Used internally to indicate to the +// Reporter that we're sending on-demand, +// not as result of a crash. +// +// BREAKPAD_COMMENTS The text the user provided as comments. +// Only used in crash_report_sender. + +// Returns a new BreakpadRef object on success, NULL otherwise. +BreakpadRef BreakpadCreate(NSDictionary *parameters); + +// Uninstall and release the data associated with |ref|. +void BreakpadRelease(BreakpadRef ref); + +// Clients may set an optional callback which gets called when a crash +// occurs. The callback function should return |true| if we should +// handle the crash, generate a crash report, etc. or |false| if we +// should ignore it and forward the crash (normally to CrashReporter). +// Context is a pointer to arbitrary data to make the callback with. +void BreakpadSetFilterCallback(BreakpadRef ref, + BreakpadFilterCallback callback, + void *context); + +// User defined key and value string storage. Generally this is used +// to configure Breakpad's internal operation, such as whether the +// crash_sender should prompt the user, or the filesystem location for +// the minidump file. See Breakpad.h for some parameters that can be +// set. Anything longer than 255 bytes will be truncated. Note that +// the string is converted to UTF8 before truncation, so any multibyte +// character that straddles the 255(256 - 1 for terminator) byte limit +// will be mangled. +// +// A maximum number of 64 key/value pairs are supported. An assert() +// will fire if more than this number are set. Unfortunately, right +// now, the same dictionary is used for both Breakpad's parameters AND +// the Upload parameters. +// +// TODO (nealsid): Investigate how necessary this is if we don't +// automatically upload parameters to the server anymore. +// TODO (nealsid): separate server parameter dictionary from the +// dictionary used to configure Breakpad, and document limits for each +// independently. +void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value); +NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key); +void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key); + +// You can use this method to specify parameters that will be uploaded +// to the crash server. They will be automatically encoded as +// necessary. Note that as mentioned above there are limits on both +// the number of keys and their length. +void BreakpadAddUploadParameter(BreakpadRef ref, NSString *key, + NSString *value); + +// This method will remove a previously-added parameter from the +// upload parameter set. +void BreakpadRemoveUploadParameter(BreakpadRef ref, NSString *key); + +// Add a log file for Breakpad to read and send upon crash dump +void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname); + +// Generate a minidump and send +void BreakpadGenerateAndSendReport(BreakpadRef ref); + +#ifdef __cplusplus +} +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad.mm 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,993 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#define VERBOSE 0 + +#if VERBOSE + static bool gDebugLog = true; +#else + static bool gDebugLog = false; +#endif + +#define DEBUGLOG if (gDebugLog) fprintf +#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER" + +#import "common/mac/MachIPC.h" +#import "common/mac/SimpleStringDictionary.h" + +#import "client/mac/crash_generation/Inspector.h" +#import "client/mac/handler/exception_handler.h" +#import "client/mac/Framework/Breakpad.h" +#import "client/mac/Framework/OnDemandServer.h" +#import "client/mac/handler/protected_memory_allocator.h" + +#import +#import + +#import + + +using google_breakpad::KeyValueEntry; +using google_breakpad::SimpleStringDictionary; +using google_breakpad::SimpleStringDictionaryIterator; + +//============================================================================= +// We want any memory allocations which are used by breakpad during the +// exception handling process (after a crash has happened) to be read-only +// to prevent them from being smashed before a crash occurs. Unfortunately +// we cannot protect against smashes to our exception handling thread's +// stack. +// +// NOTE: Any memory allocations which are not used during the exception +// handling process may be allocated in the normal ways. +// +// The ProtectedMemoryAllocator class provides an Allocate() method which +// we'll using in conjunction with placement operator new() to control +// allocation of C++ objects. Note that we don't use operator delete() +// but instead call the objects destructor directly: object->~ClassName(); +// +ProtectedMemoryAllocator *gMasterAllocator = NULL; +ProtectedMemoryAllocator *gKeyValueAllocator = NULL; +ProtectedMemoryAllocator *gBreakpadAllocator = NULL; + +// Mutex for thread-safe access to the key/value dictionary used by breakpad. +// It's a global instead of an instance variable of Breakpad +// since it can't live in a protected memory area. +pthread_mutex_t gDictionaryMutex; + +//============================================================================= +// Stack-based object for thread-safe access to a memory-protected region. +// It's assumed that normally the memory block (allocated by the allocator) +// is protected (read-only). Creating a stack-based instance of +// ProtectedMemoryLocker will unprotect this block after taking the lock. +// Its destructor will first re-protect the memory then release the lock. +class ProtectedMemoryLocker { +public: + // allocator may be NULL, in which case no Protect() or Unprotect() calls + // will be made, but a lock will still be taken + ProtectedMemoryLocker(pthread_mutex_t *mutex, + ProtectedMemoryAllocator *allocator) + : mutex_(mutex), allocator_(allocator) { + // Lock the mutex + assert(pthread_mutex_lock(mutex_) == 0); + + // Unprotect the memory + if (allocator_ ) { + allocator_->Unprotect(); + } + } + + ~ProtectedMemoryLocker() { + // First protect the memory + if (allocator_) { + allocator_->Protect(); + } + + // Then unlock the mutex + assert(pthread_mutex_unlock(mutex_) == 0); + }; + +private: + // Keep anybody from ever creating one of these things not on the stack. + ProtectedMemoryLocker() { } + ProtectedMemoryLocker(const ProtectedMemoryLocker&); + ProtectedMemoryLocker & operator=(ProtectedMemoryLocker&); + + pthread_mutex_t *mutex_; + ProtectedMemoryAllocator *allocator_; +}; + +//============================================================================= +class Breakpad { + public: + // factory method + static Breakpad *Create(NSDictionary *parameters) { + // Allocate from our special allocation pool + Breakpad *breakpad = + new (gBreakpadAllocator->Allocate(sizeof(Breakpad))) + Breakpad(); + + if (!breakpad) + return NULL; + + if (!breakpad->Initialize(parameters)) { + // Don't use operator delete() here since we allocated from special pool + breakpad->~Breakpad(); + return NULL; + } + + return breakpad; + } + + ~Breakpad(); + + void SetKeyValue(NSString *key, NSString *value); + NSString *KeyValue(NSString *key); + void RemoveKeyValue(NSString *key); + + void GenerateAndSendReport(); + + void SetFilterCallback(BreakpadFilterCallback callback, void *context) { + filter_callback_ = callback; + filter_callback_context_ = context; + } + + private: + Breakpad() + : handler_(NULL), + config_params_(NULL), + send_and_exit_(true), + filter_callback_(NULL), + filter_callback_context_(NULL) { + inspector_path_[0] = 0; + } + + bool Initialize(NSDictionary *parameters); + + bool ExtractParameters(NSDictionary *parameters); + + // Dispatches to HandleException() + static bool ExceptionHandlerDirectCallback(void *context, + int exception_type, + int exception_code, + int exception_subcode, + mach_port_t crashing_thread); + + bool HandleException(int exception_type, + int exception_code, + int exception_subcode, + mach_port_t crashing_thread); + + // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's + // MachineExceptions.h, we have to explicitly name the handler. + google_breakpad::ExceptionHandler *handler_; // The actual handler (STRONG) + + char inspector_path_[PATH_MAX]; // Path to inspector tool + + SimpleStringDictionary *config_params_; // Create parameters (STRONG) + + OnDemandServer inspector_; + + bool send_and_exit_; // Exit after sending, if true + + BreakpadFilterCallback filter_callback_; + void *filter_callback_context_; +}; + +#pragma mark - +#pragma mark Helper functions + +//============================================================================= +// Helper functions + +//============================================================================= +static BOOL IsDebuggerActive() { + BOOL result = NO; + NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults]; + + // We check both defaults and the environment variable here + + BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER]; + + if (!ignoreDebugger) { + char *ignoreDebuggerStr = getenv(IGNORE_DEBUGGER); + ignoreDebugger = (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0; + } + + if (!ignoreDebugger) { + pid_t pid = getpid(); + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; + int mibSize = sizeof(mib) / sizeof(int); + size_t actualSize; + + if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) { + struct kinfo_proc *info = (struct kinfo_proc *)malloc(actualSize); + + if (info) { + // This comes from looking at the Darwin xnu Kernel + if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0) + result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO; + + free(info); + } + } + } + + return result; +} + +//============================================================================= +bool Breakpad::ExceptionHandlerDirectCallback(void *context, + int exception_type, + int exception_code, + int exception_subcode, + mach_port_t crashing_thread) { + Breakpad *breakpad = (Breakpad *)context; + + // If our context is damaged or something, just return false to indicate that + // the handler should continue without us. + if (!breakpad) + return false; + + return breakpad->HandleException( exception_type, + exception_code, + exception_subcode, + crashing_thread); +} + +//============================================================================= +#pragma mark - + +#include + +//============================================================================= +// Returns the pathname to the Resources directory for this version of +// Breakpad which we are now running. +// +// Don't make the function static, since _dyld_lookup_and_bind_fully needs a +// simple non-static C name +// +extern "C" { +NSString * GetResourcePath(); +NSString * GetResourcePath() { + NSString *resourcePath = nil; + + // If there are multiple breakpads installed then calling bundleWithIdentifier + // will not work properly, so only use that as a backup plan. + // We want to find the bundle containing the code where this function lives + // and work from there + // + + // Get the pathname to the code which contains this function + void *address = nil; + NSModule module = nil; + _dyld_lookup_and_bind_fully("_GetResourcePath", + &address, + &module); + + if (module && address) { + const char* moduleName = NSNameOfModule(module); + if (moduleName) { + // The "Resources" directory should be in the same directory as the + // executable code, since that's how the Breakpad framework is built. + resourcePath = [NSString stringWithUTF8String:moduleName]; + resourcePath = [resourcePath stringByDeletingLastPathComponent]; + resourcePath = [resourcePath stringByAppendingPathComponent:@"Resources/"]; + } else { + DEBUGLOG(stderr, "Missing moduleName\n"); + } + } else { + DEBUGLOG(stderr, "Could not find GetResourcePath\n"); + // fallback plan + NSBundle *bundle = + [NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"]; + resourcePath = [bundle resourcePath]; + } + + return resourcePath; +} +} // extern "C" + +//============================================================================= +bool Breakpad::Initialize(NSDictionary *parameters) { + // Initialize + config_params_ = NULL; + handler_ = NULL; + + // Check for debugger + if (IsDebuggerActive()) { + DEBUGLOG(stderr, "Debugger is active: Not installing handler\n"); + return true; + } + + // Gather any user specified parameters + if (!ExtractParameters(parameters)) { + return false; + } + + // Get path to Inspector executable. + NSString *inspectorPathString = KeyValue(@BREAKPAD_INSPECTOR_LOCATION); + + // Standardize path (resolve symlinkes, etc.) and escape spaces + inspectorPathString = [inspectorPathString stringByStandardizingPath]; + inspectorPathString = [[inspectorPathString componentsSeparatedByString:@" "] + componentsJoinedByString:@"\\ "]; + + // Create an on-demand server object representing the Inspector. + // In case of a crash, we simply need to call the LaunchOnDemand() + // method on it, then send a mach message to its service port. + // It will then launch and perform a process inspection of our crashed state. + // See the HandleException() method for the details. +#define RECEIVE_PORT_NAME "com.Breakpad.Inspector" + + name_t portName; + snprintf(portName, sizeof(name_t), "%s%d", RECEIVE_PORT_NAME, getpid()); + + // Save the location of the Inspector + strlcpy(inspector_path_, [inspectorPathString fileSystemRepresentation], + sizeof(inspector_path_)); + + // Append a single command-line argument to the Inspector path + // representing the bootstrap name of the launch-on-demand receive port. + // When the Inspector is launched, it can use this to lookup the port + // by calling bootstrap_check_in(). + strlcat(inspector_path_, " ", sizeof(inspector_path_)); + strlcat(inspector_path_, portName, sizeof(inspector_path_)); + + kern_return_t kr = inspector_.Initialize(inspector_path_, + portName, + true); // shutdown on exit + + if (kr != KERN_SUCCESS) { + return false; + } + + // Create the handler (allocating it in our special protected pool) + handler_ = + new (gBreakpadAllocator->Allocate(sizeof(google_breakpad::ExceptionHandler))) + google_breakpad::ExceptionHandler( + Breakpad::ExceptionHandlerDirectCallback, this, true); + return true; +} + +//============================================================================= +Breakpad::~Breakpad() { + // Note that we don't use operator delete() on these pointers, + // since they were allocated by ProtectedMemoryAllocator objects. + // + if (config_params_) { + config_params_->~SimpleStringDictionary(); + } + + if (handler_) + handler_->~ExceptionHandler(); +} + +//============================================================================= +bool Breakpad::ExtractParameters(NSDictionary *parameters) { + NSUserDefaults *stdDefaults = [NSUserDefaults standardUserDefaults]; + NSString *skipConfirm = [stdDefaults stringForKey:@BREAKPAD_SKIP_CONFIRM]; + NSString *sendAndExit = [stdDefaults stringForKey:@BREAKPAD_SEND_AND_EXIT]; + + NSString *serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE]; + NSString *display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; + NSString *product = [parameters objectForKey:@BREAKPAD_PRODUCT]; + NSString *version = [parameters objectForKey:@BREAKPAD_VERSION]; + NSString *urlStr = [parameters objectForKey:@BREAKPAD_URL]; + NSString *interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL]; + NSString *inspectorPathString = + [parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION]; + NSString *reporterPathString = + [parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION]; + NSString *timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT]; + NSArray *logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES]; + NSString *logFileTailSize = [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE]; + NSString *requestUserText = + [parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS]; + NSString *requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL]; + NSString *vendor = + [parameters objectForKey:@BREAKPAD_VENDOR]; + NSString *dumpSubdirectory = + [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY]; + + NSDictionary *serverParameters = + [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT]; + + // These may have been set above as user prefs, which take priority. + if (!skipConfirm) { + skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM]; + } + if (!sendAndExit) { + sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT]; + } + + if (!product) + product = [parameters objectForKey:@"CFBundleName"]; + + if (!display) + display = product; + + if (!version) + version = [parameters objectForKey:@"CFBundleVersion"]; + + if (!interval) + interval = @"3600"; + + if (!timeout) + timeout = @"300"; + + if (!logFileTailSize) + logFileTailSize = @"200000"; + + if (!vendor) { + vendor = @"Vendor not specified"; + } + + // Normalize the values. + if (skipConfirm) { + skipConfirm = [skipConfirm uppercaseString]; + + if ([skipConfirm isEqualToString:@"YES"] || + [skipConfirm isEqualToString:@"TRUE"] || + [skipConfirm isEqualToString:@"1"]) + skipConfirm = @"YES"; + else + skipConfirm = @"NO"; + } else { + skipConfirm = @"NO"; + } + + send_and_exit_ = true; + if (sendAndExit) { + sendAndExit = [sendAndExit uppercaseString]; + + if ([sendAndExit isEqualToString:@"NO"] || + [sendAndExit isEqualToString:@"FALSE"] || + [sendAndExit isEqualToString:@"0"]) + send_and_exit_ = false; + } + + if (requestUserText) { + requestUserText = [requestUserText uppercaseString]; + + if ([requestUserText isEqualToString:@"YES"] || + [requestUserText isEqualToString:@"TRUE"] || + [requestUserText isEqualToString:@"1"]) + requestUserText = @"YES"; + else + requestUserText = @"NO"; + } else { + requestUserText = @"NO"; + } + + // Find the helper applications if not specified in user config. + NSString *resourcePath = nil; + if (!inspectorPathString || !reporterPathString) { + resourcePath = GetResourcePath(); + if (!resourcePath) { + DEBUGLOG(stderr, "Could not get resource path\n"); + return false; + } + } + + // Find Inspector. + if (!inspectorPathString) { + inspectorPathString = + [resourcePath stringByAppendingPathComponent:@"Inspector"]; + } + + // Verify that there is an Inspector tool. + if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) { + DEBUGLOG(stderr, "Cannot find Inspector tool\n"); + return false; + } + + // Find Reporter. + if (!reporterPathString) { + reporterPathString = + [resourcePath stringByAppendingPathComponent:@"crash_report_sender.app"]; + reporterPathString = [[NSBundle bundleWithPath:reporterPathString] executablePath]; + } + + // Verify that there is a Reporter application. + if (![[NSFileManager defaultManager] + fileExistsAtPath:reporterPathString]) { + DEBUGLOG(stderr, "Cannot find Reporter tool\n"); + return false; + } + + if (!dumpSubdirectory) { + dumpSubdirectory = @""; + } + + // The product, version, and URL are required values. + if (![product length]) { + DEBUGLOG(stderr, "Missing required product key.\n"); + return false; + } + + if (![version length]) { + DEBUGLOG(stderr, "Missing required version key.\n"); + return false; + } + + if (![urlStr length]) { + DEBUGLOG(stderr, "Missing required URL key.\n"); + return false; + } + + config_params_ = + new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) ) + SimpleStringDictionary(); + + SimpleStringDictionary &dictionary = *config_params_; + + dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE, [serverType UTF8String]); + dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]); + dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]); + dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]); + dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]); + dictionary.SetKeyValue(BREAKPAD_REPORT_INTERVAL, [interval UTF8String]); + dictionary.SetKeyValue(BREAKPAD_SKIP_CONFIRM, [skipConfirm UTF8String]); + dictionary.SetKeyValue(BREAKPAD_CONFIRM_TIMEOUT, [timeout UTF8String]); + dictionary.SetKeyValue(BREAKPAD_INSPECTOR_LOCATION, + [inspectorPathString fileSystemRepresentation]); + dictionary.SetKeyValue(BREAKPAD_REPORTER_EXE_LOCATION, + [reporterPathString fileSystemRepresentation]); + dictionary.SetKeyValue(BREAKPAD_LOGFILE_UPLOAD_SIZE, + [logFileTailSize UTF8String]); + dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS, + [requestUserText UTF8String]); + dictionary.SetKeyValue(BREAKPAD_REQUEST_EMAIL, [requestEmail UTF8String]); + dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]); + dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY, + [dumpSubdirectory UTF8String]); + + struct timeval tv; + gettimeofday(&tv, NULL); + char timeStartedString[32]; + sprintf(timeStartedString, "%d", tv.tv_sec); + dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, + timeStartedString); + + if (logFilePaths) { + char logFileKey[255]; + for(unsigned int i = 0; i < [logFilePaths count]; i++) { + sprintf(logFileKey,"%s%d", BREAKPAD_LOGFILE_KEY_PREFIX, i); + dictionary.SetKeyValue(logFileKey, + [[logFilePaths objectAtIndex:i] + fileSystemRepresentation]); + } + } + + if (serverParameters) { + // For each key-value pair, call BreakpadAddUploadParameter() + NSEnumerator *keyEnumerator = [serverParameters keyEnumerator]; + NSString *aParameter; + while (aParameter = [keyEnumerator nextObject]) { + BreakpadAddUploadParameter(this, aParameter, + [serverParameters objectForKey:aParameter]); + } + } + return true; +} + +//============================================================================= +void Breakpad::SetKeyValue(NSString *key, NSString *value) { + // We allow nil values. This is the same as removing the keyvalue. + if (!config_params_ || !key) + return; + + config_params_->SetKeyValue([key UTF8String], [value UTF8String]); +} + +//============================================================================= +NSString * Breakpad::KeyValue(NSString *key) { + if (!config_params_ || !key) + return nil; + + const char *value = config_params_->GetValueForKey([key UTF8String]); + return value ? [NSString stringWithUTF8String:value] : nil; +} + +//============================================================================= +void Breakpad::RemoveKeyValue(NSString *key) { + if (!config_params_ || !key) + return; + + config_params_->RemoveKey([key UTF8String]); +} + +//============================================================================= +void Breakpad::GenerateAndSendReport() { + config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES"); + HandleException(0, 0, 0, mach_thread_self()); + config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO"); +} + +//============================================================================= +bool Breakpad::HandleException(int exception_type, + int exception_code, + int exception_subcode, + mach_port_t crashing_thread) { + DEBUGLOG(stderr, "Breakpad: an exception occurred\n"); + + if (filter_callback_) { + bool should_handle = filter_callback_(exception_type, + exception_code, + crashing_thread, + filter_callback_context_); + if (!should_handle) return false; + } + + // We need to reset the memory protections to be read/write, + // since LaunchOnDemand() requires changing state. + gBreakpadAllocator->Unprotect(); + // Configure the server to launch when we message the service port. + // The reason we do this here, rather than at startup, is that we + // can leak a bootstrap service entry if this method is called and + // there never ends up being a crash. + inspector_.LaunchOnDemand(); + gBreakpadAllocator->Protect(); + + // The Inspector should send a message to this port to verify it + // received our information and has finished the inspection. + ReceivePort acknowledge_port; + + // Send initial information to the Inspector. + MachSendMessage message(kMsgType_InspectorInitialInfo); + message.AddDescriptor(mach_task_self()); // our task + message.AddDescriptor(crashing_thread); // crashing thread + message.AddDescriptor(mach_thread_self()); // exception-handling thread + message.AddDescriptor(acknowledge_port.GetPort());// message receive port + + InspectorInfo info; + info.exception_type = exception_type; + info.exception_code = exception_code; + info.exception_subcode = exception_subcode; + info.parameter_count = config_params_->GetCount(); + message.SetData(&info, sizeof(info)); + + MachPortSender sender(inspector_.GetServicePort()); + + kern_return_t result = sender.SendMessage(message, 2000); + + if (result == KERN_SUCCESS) { + // Now, send a series of key-value pairs to the Inspector. + const KeyValueEntry *entry = NULL; + SimpleStringDictionaryIterator iter(*config_params_); + + while ( (entry = iter.Next()) ) { + KeyValueMessageData keyvalue_data(*entry); + + MachSendMessage keyvalue_message(kMsgType_InspectorKeyValuePair); + keyvalue_message.SetData(&keyvalue_data, sizeof(keyvalue_data)); + + result = sender.SendMessage(keyvalue_message, 2000); + + if (result != KERN_SUCCESS) { + break; + } + } + + if (result == KERN_SUCCESS) { + // Wait for acknowledgement that the inspection has finished. + MachReceiveMessage acknowledge_messsage; + result = acknowledge_port.WaitForMessage(&acknowledge_messsage, 5000); + } + } + +#if VERBOSE + PRINT_MACH_RESULT(result, "Breakpad: SendMessage "); + printf("Breakpad: Inspector service port = %#x\n", + inspector_.GetServicePort()); +#endif + + // If we don't want any forwarding, return true here to indicate that we've + // processed things as much as we want. + if (send_and_exit_) + return true; + + return false; +} + +//============================================================================= +//============================================================================= + +#pragma mark - +#pragma mark Public API + +//============================================================================= +BreakpadRef BreakpadCreate(NSDictionary *parameters) { + try { + // This is confusing. Our two main allocators for breakpad memory are: + // - gKeyValueAllocator for the key/value memory + // - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other + // breakpad allocations which are accessed at exception handling time. + // + // But in order to avoid these two allocators themselves from being smashed, + // we'll protect them as well by allocating them with gMasterAllocator. + // + // gMasterAllocator itself will NOT be protected, but this doesn't matter, + // since once it does its allocations and locks the memory, smashes to itself + // don't affect anything we care about. + gMasterAllocator = + new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2); + + gKeyValueAllocator = + new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) + ProtectedMemoryAllocator(sizeof(SimpleStringDictionary)); + + // Create a mutex for use in accessing the SimpleStringDictionary + int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL); + if (mutexResult == 0) { + + // With the current compiler, gBreakpadAllocator is allocating 1444 bytes. + // Let's round up to the nearest page size. + // + int breakpad_pool_size = 4096; + + /* + sizeof(Breakpad) + + sizeof(google_breakpad::ExceptionHandler) + + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler ) + */ + + gBreakpadAllocator = + new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) + ProtectedMemoryAllocator(breakpad_pool_size); + + // Stack-based autorelease pool for Breakpad::Create() obj-c code. + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + Breakpad *breakpad = Breakpad::Create(parameters); + + if (breakpad) { + // Make read-only to protect against memory smashers + gMasterAllocator->Protect(); + gKeyValueAllocator->Protect(); + gBreakpadAllocator->Protect(); + // Can uncomment this line to figure out how much space was actually + // allocated using this allocator + // printf("gBreakpadAllocator allocated size = %d\n", + // gBreakpadAllocator->GetAllocatedSize() ); + [pool release]; + return (BreakpadRef)breakpad; + } + + [pool release]; + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadCreate() : error\n"); + } + + if (gKeyValueAllocator) { + gKeyValueAllocator->~ProtectedMemoryAllocator(); + gKeyValueAllocator = NULL; + } + + if (gBreakpadAllocator) { + gBreakpadAllocator->~ProtectedMemoryAllocator(); + gBreakpadAllocator = NULL; + } + + delete gMasterAllocator; + gMasterAllocator = NULL; + + return NULL; +} + +//============================================================================= +void BreakpadRelease(BreakpadRef ref) { + try { + Breakpad *breakpad = (Breakpad *)ref; + + if (gMasterAllocator) { + gMasterAllocator->Unprotect(); + gKeyValueAllocator->Unprotect(); + gBreakpadAllocator->Unprotect(); + + breakpad->~Breakpad(); + + // Unfortunately, it's not possible to deallocate this stuff + // because the exception handling thread is still finishing up + // asynchronously at this point... OK, it could be done with + // locks, etc. But since BreakpadRelease() should usually only + // be called right before the process exits, it's not worth + // deallocating this stuff. +#if 0 + gKeyValueAllocator->~ProtectedMemoryAllocator(); + gBreakpadAllocator->~ProtectedMemoryAllocator(); + delete gMasterAllocator; + + gMasterAllocator = NULL; + gKeyValueAllocator = NULL; + gBreakpadAllocator = NULL; +#endif + + pthread_mutex_destroy(&gDictionaryMutex); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadRelease() : error\n"); + } +} + +//============================================================================= +void BreakpadSetKeyValue(BreakpadRef ref, NSString *key, NSString *value) { + try { + // Not called at exception time + Breakpad *breakpad = (Breakpad *)ref; + + if (breakpad && key && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + breakpad->SetKeyValue(key, value); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadSetKeyValue() : error\n"); + } +} + +void BreakpadAddUploadParameter(BreakpadRef ref, + NSString *key, + NSString *value) { + // The only difference, internally, between an upload parameter and + // a key value one that is set with BreakpadSetKeyValue is that we + // prepend the keyname with a special prefix. This informs the + // crash sender that the parameter should be sent along with the + // POST of the crash dump upload. + try { + Breakpad *breakpad = (Breakpad *)ref; + + if (breakpad && key && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + NSString *prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX + stringByAppendingString:key]; + breakpad->SetKeyValue(prefixedKey, value); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadSetKeyValue() : error\n"); + } +} + +void BreakpadRemoveUploadParameter(BreakpadRef ref, + NSString *key) { + try { + // Not called at exception time + Breakpad *breakpad = (Breakpad *)ref; + + if (breakpad && key && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + NSString *prefixedKey = [NSString stringWithFormat:@"%@%@", + @BREAKPAD_SERVER_PARAMETER_PREFIX, key]; + breakpad->RemoveKeyValue(prefixedKey); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadRemoveKeyValue() : error\n"); + } +} +//============================================================================= +NSString *BreakpadKeyValue(BreakpadRef ref, NSString *key) { + NSString *value = nil; + + try { + // Not called at exception time + Breakpad *breakpad = (Breakpad *)ref; + + if (!breakpad || !key || !gKeyValueAllocator) + return nil; + + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + value = breakpad->KeyValue(key); + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadKeyValue() : error\n"); + } + + return value; +} + +//============================================================================= +void BreakpadRemoveKeyValue(BreakpadRef ref, NSString *key) { + try { + // Not called at exception time + Breakpad *breakpad = (Breakpad *)ref; + + if (breakpad && key && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + breakpad->RemoveKeyValue(key); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadRemoveKeyValue() : error\n"); + } +} + +//============================================================================= +void BreakpadGenerateAndSendReport(BreakpadRef ref) { + try { + Breakpad *breakpad = (Breakpad *)ref; + + if (breakpad && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + gBreakpadAllocator->Unprotect(); + breakpad->GenerateAndSendReport(); + gBreakpadAllocator->Protect(); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n"); + } +} + +//============================================================================= +void BreakpadSetFilterCallback(BreakpadRef ref, + BreakpadFilterCallback callback, + void *context) { + + try { + Breakpad *breakpad = (Breakpad *)ref; + + if (breakpad && gBreakpadAllocator) { + // share the dictionary mutex here (we really don't need a mutex) + ProtectedMemoryLocker locker(&gDictionaryMutex, gBreakpadAllocator); + + breakpad->SetFilterCallback(callback, context); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadSetFilterCallback() : error\n"); + } +} + +//============================================================================ +void BreakpadAddLogFile(BreakpadRef ref, NSString *logPathname) { + int logFileCounter = 0; + + NSString *logFileKey = [NSString stringWithFormat:@"%@%d", + @BREAKPAD_LOGFILE_KEY_PREFIX, + logFileCounter]; + + NSString *existingLogFilename = nil; + existingLogFilename = BreakpadKeyValue(ref, logFileKey); + // Find the first log file key that we can use by testing for existence + while (existingLogFilename) { + if ([existingLogFilename isEqualToString:logPathname]) { + return; + } + logFileCounter++; + logFileKey = [NSString stringWithFormat:@"%@%d", + @BREAKPAD_LOGFILE_KEY_PREFIX, + logFileCounter]; + existingLogFilename = BreakpadKeyValue(ref, logFileKey); + } + + BreakpadSetKeyValue(ref, logFileKey, logPathname); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad_Prefix.pch firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad_Prefix.pch --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad_Prefix.pch 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Breakpad_Prefix.pch 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,8 @@ +// +// Prefix header for all source files of the 'Breakpad' target in the +// 'Breakpad' project. +// + +#ifdef __OBJC__ + #import +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Info.plist firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Info.plist --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Info.plist 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/Info.plist 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleName + ${PRODUCT_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.googlecode.google-breakpad + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + FMWK + CFBundleSignature + ???? + CFBundleVersion + 1.0 + NSPrincipalClass + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/OnDemandServer.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/OnDemandServer.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/OnDemandServer.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/OnDemandServer.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,146 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import +#import +#import +#import +#import +#import +#import + +//============================================================================== +// class OnDemandServer : +// A basic on-demand server launcher supporting a single named service port +// +// Example Usage : +// +// kern_return_t result; +// OnDemandServer *server = OnDemandServer::Create("/tmp/myserver", +// "com.MyCompany.MyServiceName", +// true, +// &result); +// +// if (server) { +// server->LaunchOnDemand(); +// mach_port_t service_port = GetServicePort(); +// +// // Send a mach message to service_port and "myserver" will be launched +// } +// +// +// ---- Now in the server code ---- +// +// // "myserver" should get the service port and read the message which +// // launched it: +// mach_port_t service_rcv_port_; +// kern_return_t kr = bootstrap_check_in(bootstrap_port, +// "com.MyCompany.MyServiceName", +// &service_rcv_port_); +// // mach_msg() read service_rcv_port_ .... +// +// .... +// +// // Later "myserver" may want to unregister the service if it doesn't +// // want its bootstrap service to stick around after it exits. +// +// // DO NOT use mach_port_deallocate() here -- it will fail and the +// // following bootstrap_register() will also fail leaving our service +// // name hanging around forever (until reboot) +// kern_return_t kr = mach_port_destroy(mach_task_self(), service_rcv_port_); +// +// kr = bootstrap_register(bootstrap_port, +// "com.MyCompany.MyServiceName", +// MACH_PORT_NULL); + +class OnDemandServer { + public: + // must call Initialize() to be useful + OnDemandServer() + : server_port_(MACH_PORT_NULL), + service_port_(MACH_PORT_NULL), + unregister_on_cleanup_(true) { + } + + // Creates the bootstrap server and service + kern_return_t Initialize(const char *server_command, + const char *service_name, + bool unregister_on_cleanup); + + // Returns an OnDemandServer object if successful, or NULL if there's + // an error. The error result will be returned in out_result. + // + // server_command : the full path name including optional command-line + // arguments to the executable representing the server + // + // service_name : represents service name + // something like "com.company.ServiceName" + // + // unregister_on_cleanup : if true, unregisters the service name + // when the OnDemandServer is deleted -- unregistering will + // ONLY be possible if LaunchOnDemand() has NOT been called. + // If false, then the service will continue to be registered + // even after the current process quits. + // + // out_result : if non-NULL, returns the result + // this value will be KERN_SUCCESS if Create() returns non-NULL + // + static OnDemandServer *Create(const char *server_command, + const char *service_name, + bool unregister_on_cleanup, + kern_return_t *out_result); + + // Cleans up and if LaunchOnDemand() has not yet been called then + // the bootstrap service will be unregistered. + ~OnDemandServer(); + + // This must be called if we intend to commit to launching the server + // by sending a mach message to our service port. Do not call it otherwise + // or it will be difficult (impossible?) to unregister the service name. + void LaunchOnDemand(); + + // This is the port we need to send a mach message to after calling + // LaunchOnDemand(). Sending a message causing an immediate launch + // of the server + mach_port_t GetServicePort() { return service_port_; }; + + private: + // Disallow copy constructor + OnDemandServer(const OnDemandServer&); + + // Cleans up and if LaunchOnDemand() has not yet been called then + // the bootstrap service will be unregistered. + void Unregister(); + + name_t service_name_; + + mach_port_t server_port_; + mach_port_t service_port_; + bool unregister_on_cleanup_; +}; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/OnDemandServer.mm firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/OnDemandServer.mm --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/OnDemandServer.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/Framework/OnDemandServer.mm 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,145 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import "OnDemandServer.h" + +#if DEBUG + #define PRINT_MACH_RESULT(result_, message_) \ + printf(message_"%s (%d)\n", mach_error_string(result_), result_ ); +#else + #define PRINT_MACH_RESULT(result_, message_) +#endif + +//============================================================================== +OnDemandServer *OnDemandServer::Create(const char *server_command, + const char *service_name, + bool unregister_on_cleanup, + kern_return_t *out_result) { + OnDemandServer *server = new OnDemandServer(); + + if (!server) return NULL; + + kern_return_t result = server->Initialize(server_command, + service_name, + unregister_on_cleanup); + + if (out_result) { + *out_result = result; + } + + if (result == KERN_SUCCESS) { + return server; + } + + delete server; + return NULL; +}; + +//============================================================================== +kern_return_t OnDemandServer::Initialize(const char *server_command, + const char *service_name, + bool unregister_on_cleanup) { + unregister_on_cleanup_ = unregister_on_cleanup; + + kern_return_t kr = + bootstrap_create_server(bootstrap_port, + const_cast(server_command), + geteuid(), // server uid + true, + &server_port_); + + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, "bootstrap_create_server() : "); + return kr; + } + + strlcpy(service_name_, service_name, sizeof(service_name_)); + + // Create a service called service_name, and return send rights to + // that port in service_port_. + kr = bootstrap_create_service(server_port_, + const_cast(service_name), + &service_port_); + + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, "bootstrap_create_service() : "); + + // perhaps the service has already been created - try to look it up + kr = bootstrap_look_up(bootstrap_port, (char*)service_name, &service_port_); + + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, "bootstrap_look_up() : "); + Unregister(); // clean up server port + return kr; + } + } + + return KERN_SUCCESS; +} + +//============================================================================== +OnDemandServer::~OnDemandServer() { + if (unregister_on_cleanup_) { + Unregister(); + } +} + +//============================================================================== +void OnDemandServer::LaunchOnDemand() { + // We need to do this, since the launched server is another process + // and holding on to this port delays launching until the current process + // exits! + mach_port_deallocate(mach_task_self(), server_port_); + server_port_ = NULL; + + // Now, the service is still registered and all we need to do is send + // a mach message to the service port in order to launch the server. +} + +//============================================================================== +void OnDemandServer::Unregister() { + if (service_port_ != MACH_PORT_NULL) { + mach_port_deallocate(mach_task_self(), service_port_); + service_port_ = MACH_PORT_NULL; + } + + if (server_port_ != MACH_PORT_NULL) { + // unregister the service + kern_return_t kr = bootstrap_register(server_port_, + service_name_, + MACH_PORT_NULL); + + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, "Breakpad UNREGISTER : bootstrap_register() : "); + } + + mach_port_deallocate(mach_task_self(), server_port_); + server_port_ = MACH_PORT_NULL; + } +} Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/gcov/libgcov.a and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/gcov/libgcov.a differ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler.cc 2010-04-16 17:32:47.000000000 +0100 @@ -92,12 +92,15 @@ boolean_t exc_server(mach_msg_header_t *request, mach_msg_header_t *reply); + // This symbol must be visible to dlsym() - see + // http://code.google.com/p/google-breakpad/issues/detail?id=345 for details. kern_return_t catch_exception_raise(mach_port_t target_port, mach_port_t failed_thread, mach_port_t task, exception_type_t exception, exception_data_t code, - mach_msg_type_number_t code_count); + mach_msg_type_number_t code_count) + __attribute__((visibility("default"))); kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread, @@ -322,8 +325,8 @@ if (filter_ && !filter_(callback_context_)) return false; - md.SetExceptionInformation(exception_type, exception_code, exception_subcode, - thread_name); + md.SetExceptionInformation(exception_type, exception_code, + exception_subcode, thread_name); } result = md.Write(next_minidump_path_c_); @@ -435,6 +438,9 @@ exception_type_t exception, exception_data_t code, mach_msg_type_number_t code_count) { + if (task != mach_task_self()) { + return KERN_FAILURE; + } return ForwardException(task, failed_thread, exception, code, code_count); } @@ -493,7 +499,6 @@ if (self->use_minidump_write_mutex_) pthread_mutex_unlock(&self->minidump_write_mutex_); } else { - // When forking a child process with the exception handler installed, // if the child crashes, it will send the exception back to the parent // process. The check for task == self_task() ensures that only diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler_test.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler_test.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler_test.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/exception_handler_test.cc 2010-04-16 17:32:47.000000000 +0100 @@ -28,15 +28,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /* -g++ -framework CoreFoundation -I../../.. \ - ../../minidump_file_writer.cc \ - ../../../common/convert_UTF.c \ - ../../../common/string_conversion.cc \ - ../../../common/mac/string_utilities.cc \ - exception_handler.cc \ - minidump_generator.cc \ - exception_handler_test.cc \ - -o exception_handler_test +g++ -framework CoreFoundation -I../../.. ../../minidump_file_writer.cc ../../../common/convert_UTF.c ../../../common/string_conversion.cc ../../../common/mac/string_utilities.cc exception_handler.cc minidump_generator.cc exception_handler_test.cc -o exception_handler_test -mmacosx-version-min=10.4 ../../../common/mac/file_id.cc dynamic_images.cc ../../../common/mac/macho_id.cc ../../../common/mac/macho_walker.cc -lcrypto ../../../common/mac/macho_utilities.cc */ #include @@ -55,6 +47,7 @@ while (1) { sleep(10000); } + return NULL; } static void Crasher() { @@ -77,15 +70,14 @@ fprintf(stdout, "Minidump: %s\n", path.c_str()); // Indicate that we've handled the callback - return true; + exit(0); } int main(int argc, char * const argv[]) { char buffer[PATH_MAX]; - struct passwd *user = getpwuid(getuid()); // Home dir - snprintf(buffer, sizeof(buffer), "/Users/%s/Desktop/", user->pw_name); + snprintf(buffer, sizeof(buffer), "/tmp/"); string path(buffer); ExceptionHandler eh(path, NULL, MDCallback, NULL, true); @@ -97,8 +89,8 @@ perror("pthread_create"); } - // Dump a test - eh.WriteMinidump(); +// // Dump a test +// eh.WriteMinidump(); // Test the handler SoonToCrash(); diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.cc 2010-04-16 17:32:47.000000000 +0100 @@ -174,7 +174,7 @@ // exception. &MinidumpGenerator::WriteExceptionStream, }; - bool result = true; + bool result = false; // If opening was successful, create the header, directory, and call each // writer. The destructor for the TypedMDRVAs will cause the data to be @@ -205,6 +205,7 @@ header_ptr->stream_directory_rva = dir.position(); MDRawDirectory local_dir; + result = true; for (int i = 0; (result) && (i < writer_count); ++i) { result = (this->*writers[i])(&local_dir); @@ -322,11 +323,7 @@ MDMemoryDescriptor *stack_location) { breakpad_thread_state_t *machine_state = reinterpret_cast(state); -#if TARGET_CPU_PPC - mach_vm_address_t start_addr = machine_state->r1; -#else - mach_vm_address_t start_addr = machine_state->__r1; -#endif + mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1); return WriteStackFromStartAddress(start_addr, stack_location); } @@ -335,11 +332,7 @@ breakpad_thread_state_t *machine_state = reinterpret_cast(state); -#if TARGET_CPU_PPC - return machine_state->srr0; -#else - return machine_state->__srr0; -#endif + return REGISTER_FROM_THREADSTATE(machine_state, srr0); } bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state, @@ -355,13 +348,8 @@ MinidumpContext *context_ptr = context.get(); context_ptr->context_flags = MD_CONTEXT_PPC_BASE; -#if TARGET_CPU_PPC64 -#define AddReg(a) context_ptr->a = machine_state->__ ## a -#define AddGPR(a) context_ptr->gpr[a] = machine_state->__r ## a -#else -#define AddReg(a) context_ptr->a = machine_state->a -#define AddGPR(a) context_ptr->gpr[a] = machine_state->r ## a -#endif +#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a) +#define AddGPR(a) context_ptr->gpr[a] = REGISTER_FROM_THREADSTATE(machine_state, r ## a) AddReg(srr0); AddReg(cr); @@ -420,9 +408,9 @@ reinterpret_cast(state); #if TARGET_CPU_X86_64 - mach_vm_address_t start_addr = machine_state->__rsp; + mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, rsp); #else - mach_vm_address_t start_addr = machine_state->esp; + mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, esp); #endif return WriteStackFromStartAddress(start_addr, stack_location); } @@ -433,9 +421,9 @@ reinterpret_cast(state); #if TARGET_CPU_X86_64 - return machine_state->__rip; + return REGISTER_FROM_THREADSTATE(machine_state, rip); #else - return machine_state->eip; + return REGISTER_FROM_THREADSTATE(machine_state, eip); #endif } @@ -451,10 +439,9 @@ *register_location = context.location(); MinidumpContext *context_ptr = context.get(); +#define AddReg(a) context_ptr->a = REGISTER_FROM_THREADSTATE(machine_state, a) #if TARGET_CPU_X86 context_ptr->context_flags = MD_CONTEXT_X86; - -#define AddReg(a) context_ptr->a = machine_state->a AddReg(eax); AddReg(ebx); AddReg(ecx); @@ -474,8 +461,6 @@ AddReg(eip); #else - -#define AddReg(a) context_ptr->a = machine_state->__ ## a context_ptr->context_flags = MD_CONTEXT_AMD64; AddReg(rax); AddReg(rbx); @@ -503,6 +488,7 @@ AddReg(fs); AddReg(gs); #endif +#undef AddReg(a) return true; } @@ -651,6 +637,7 @@ // get version and feature info cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2, info_ptr->cpu.x86_cpu_info.feature_information); + // family info_ptr->processor_level = (info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8; @@ -658,6 +645,20 @@ info_ptr->processor_revision = (info_ptr->cpu.x86_cpu_info.version_information & 0xF) | ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4); + + // decode extended model info + if (info_ptr->processor_level == 0xF || + info_ptr->processor_level == 0x6) { + info_ptr->processor_revision |= + ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0000) >> 4); + } + + // decode extended family info + if (info_ptr->processor_level == 0xF) { + info_ptr->processor_level += + ((info_ptr->cpu.x86_cpu_info.version_information & 0xFF00000) >> 20); + } + #endif // __i386__ break; default: diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h 2010-04-16 17:32:47.000000000 +0100 @@ -66,6 +66,18 @@ typedef MDRawContextPPC MinidumpContext; #endif +// Use the REGISTER_FROM_THREADSTATE to access a register name from the +// breakpad_thread_state_t structure. +#if __DARWIN_UNIX03 || TARGET_CPU_X86_64 || TARGET_CPU_PPC64 +// In The 10.5 SDK Headers Apple prepended __ to the variable names in the +// i386_thread_state_t structure. There's no good way to tell what version of +// the SDK we're compiling against so we just toggle on the same preprocessor +// symbol Apple's headers use. +#define REGISTER_FROM_THREADSTATE(a, b) ((a)->__ ## b) +#else +#define REGISTER_FROM_THREADSTATE(a, b) (a->b) +#endif + // Creates a minidump file of the current process. If there is exception data, // use SetExceptionInformation() to add this to the minidump. The minidump // file is generated by the Write() function. diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator_test.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator_test.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator_test.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator_test.cc 2010-04-16 17:32:47.000000000 +0100 @@ -45,14 +45,13 @@ static void *Reporter(void *) { char buffer[PATH_MAX]; MinidumpGenerator md; - struct passwd *user = getpwuid(getuid()); // Write it to the desktop snprintf(buffer, sizeof(buffer), - "/Users/%s/Desktop/test.dmp", - user->pw_name); - + "/tmp/test.dmp"); + + fprintf(stdout, "Writing %s\n", buffer); unlink(buffer); md.Write(buffer); diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_test.xcodeproj/project.pbxproj firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_test.xcodeproj/project.pbxproj --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_test.xcodeproj/project.pbxproj 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_test.xcodeproj/project.pbxproj 2010-04-16 17:32:47.000000000 +0100 @@ -133,9 +133,9 @@ F9721F380E8B0CFC00D7E813 /* dump_syms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dump_syms.h; path = ../../../common/mac/dump_syms.h; sourceTree = SOURCE_ROOT; }; F9721F390E8B0D0D00D7E813 /* dump_syms.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.mm; path = ../../../common/mac/dump_syms.mm; sourceTree = SOURCE_ROOT; }; F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; - F9721F760E8B0DC700D7E813 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/mac/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; }; - F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/mac/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; }; - F9721F780E8B0DC700D7E813 /* functioninfo.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = functioninfo.cc; path = ../../../common/mac/dwarf/functioninfo.cc; sourceTree = SOURCE_ROOT; }; + F9721F760E8B0DC700D7E813 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; }; + F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; }; + F9721F780E8B0DC700D7E813 /* functioninfo.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = functioninfo.cc; path = ../../../common/dwarf/functioninfo.cc; sourceTree = SOURCE_ROOT; }; F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = /System/Library/Frameworks/SenTestingKit.framework; sourceTree = ""; }; F9721FA80E8B0E4800D7E813 /* md5.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md5.c; path = ../../../common/md5.c; sourceTree = SOURCE_ROOT; }; F982089A0DB3280D0017AECA /* breakpad_nlist_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_test.h; sourceTree = ""; }; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/testcases/testdata/dump_syms_i386_breakpad.sym firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/testcases/testdata/dump_syms_i386_breakpad.sym --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/testcases/testdata/dump_syms_i386_breakpad.sym 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/handler/testcases/testdata/dump_syms_i386_breakpad.sym 2010-04-16 17:32:47.000000000 +0100 @@ -80,39 +80,39 @@ FILE 79 /Developer/SDKs/MacOSX10.4u.sdk/usr/include/c++/4.0.0/bits/stl_uninitialized.h FILE 80 /var/tmp/gcc/gcc-5484~1/src/gcc/libgcc2.c FUNC 162a 28 0 _OSSwapInt16 -162a 10 44 1 -163a 16 46 1 -1650 2 47 1 +162a 10 44 55 +163a 16 46 55 +1650 2 47 55 FUNC 1652 1c 0 _OSSwapInt32 -1652 f 53 1 -1661 8 55 1 -1669 3 56 1 -166c 2 57 1 +1652 f 53 55 +1661 8 55 55 +1669 3 56 55 +166c 2 57 55 FUNC 166e 2b 0 _OSSwapInt64 -166e 12 64 1 -1680 11 69 1 -1691 6 70 1 -1697 2 71 1 -1699 1 71 1 +166e 12 64 55 +1680 11 69 55 +1691 6 70 55 +1697 2 71 55 +1699 1 71 55 FUNC 169a 1e 0 NXSwapShort -169a 10 43 2 -16aa c 45 2 -16b6 2 46 2 +169a 10 43 56 +16aa c 45 56 +16b6 2 46 56 FUNC 16b8 19 0 NXSwapInt -16b8 f 52 2 -16c7 8 54 2 -16cf 2 55 2 -16d1 1 55 2 +16b8 f 52 56 +16c7 8 54 56 +16cf 2 55 56 +16d1 1 55 56 FUNC 16d2 19 0 NXSwapLong -16d2 f 61 2 -16e1 8 63 2 -16e9 2 64 2 -16eb 1 64 2 +16d2 f 61 56 +16e1 8 63 56 +16e9 2 64 56 +16eb 1 64 56 FUNC 16ec 1f 0 NXSwapLongLong -16ec 12 70 2 -16fe b 72 2 -1709 2 73 2 -170b 1 73 2 +16ec 12 70 56 +16fe b 72 56 +1709 2 73 56 +170b 1 73 56 FUNC 170c 181 0 -[DumpSymbols convertCPlusPlusSymbols:] 170c 14 128 3 1720 54 130 3 @@ -149,629 +149,628 @@ 1a26 6 18 4 1a2c 6 19 4 1a32 6 20 4 -1a38 6 185 4 -1a3e 28 186 4 -1a66 21 187 4 -1a87 1a 188 4 -1aa1 a 190 4 -1aab c 194 4 -1ab7 43 198 4 -1afa 21 199 4 -1b1b 20 202 4 -1b3b 2e 203 4 -1b69 1e 194 4 -1b87 c 184 4 -1b93 17 207 4 -1baa 7 208 4 -1bb1 1 208 4 +1a38 6 185 3 +1a3e 28 186 3 +1a66 21 187 3 +1a87 1a 188 3 +1aa1 a 190 3 +1aab c 194 3 +1ab7 43 198 3 +1afa 21 199 3 +1b1b 20 202 3 +1b3b 2e 203 3 +1b69 1e 194 3 +1b87 c 184 3 +1b93 17 207 3 +1baa 7 208 3 +1bb1 1 208 3 FUNC 1bb2 4a2 0 -[DumpSymbols addFunction:line:address:section:] -1bb2 21 211 4 -1bd3 2f 212 4 -1c02 e 214 4 -1c10 4 219 4 -1c14 2a 221 4 -1c3e 22 223 4 -1c60 6 224 4 -1c66 2a 225 4 -1c90 4 226 4 -1c94 2e 230 4 -1cc2 2e 233 4 -1cf0 2e 236 4 -1d1e a 239 4 -1d28 2b 253 4 -1d53 e 254 4 -1d61 3c 255 4 +1bb2 21 211 3 +1bd3 2f 212 3 +1c02 e 214 3 +1c10 4 219 3 +1c14 2a 221 3 +1c3e 22 223 3 +1c60 6 224 3 +1c66 2a 225 3 +1c90 4 226 3 +1c94 2e 230 3 +1cc2 2e 233 3 +1cf0 2e 236 3 +1d1e a 239 3 +1d28 2b 253 3 +1d53 e 254 3 +1d61 3c 255 3 1d9d 22 32 4 -1dbf 3 256 4 -1dc2 6 259 4 -1dc8 a 260 4 -1dd2 3c 261 4 -1e0e 25 262 4 -1e33 2a 263 4 -1e5d 22 265 4 -1e7f 26 270 4 -1ea5 6 272 4 -1eab 37 273 4 -1ee2 2a 274 4 -1f0c 17 275 4 -1f23 43 278 4 -1f66 2e 279 4 -1f94 23 282 4 -1fb7 43 285 4 -1ffa 52 287 4 -204c 8 289 4 +1dbf 3 256 3 +1dc2 6 259 3 +1dc8 a 260 3 +1dd2 3c 261 3 +1e0e 25 262 3 +1e33 2a 263 3 +1e5d 22 265 3 +1e7f 26 270 3 +1ea5 6 272 3 +1eab 37 273 3 +1ee2 2a 274 3 +1f0c 17 275 3 +1f23 43 278 3 +1f66 2e 279 3 +1f94 23 282 3 +1fb7 43 285 3 +1ffa 52 287 3 +204c 8 289 3 FUNC 2054 5a4 0 -[DumpSymbols processSymbolItem:stringTable:] -2054 18 292 4 -206c 8 293 4 -2074 4 294 4 -2078 16 297 4 -208e c 298 4 -209a f 300 4 -20a9 b 301 4 -20b4 16 303 4 -20ca 4d 309 4 -2117 38 311 4 -214f 30 315 4 -217f 60 317 4 -21df d 322 4 -21ec 2b 325 4 -2217 3a 327 4 -2251 f 332 4 -2260 2d 333 4 -228d 1a 334 4 -22a7 32 335 4 -22d9 20 342 4 -22f9 c 343 4 -2305 24 348 4 -2329 a 349 4 -2333 3c 350 4 -236f 2a 352 4 -2399 1c 353 4 -23b5 9 354 4 -23be f 356 4 -23cd 2d 357 4 -23fa 2f 358 4 -2429 20 360 4 -2449 c 361 4 -2455 7 363 4 -245c 21 365 4 -247d 4a 368 4 -24c7 9 370 4 -24d0 1a 371 4 -24ea 4b 372 4 -2535 4 373 4 -2539 5 371 4 -253e 29 374 4 -2567 2d 376 4 -2594 4b 378 4 -25df 4 379 4 -25e3 a 382 4 -25ed b 383 4 +2054 18 292 3 +206c 8 293 3 +2074 4 294 3 +2078 16 297 3 +208e c 298 3 +209a f 300 3 +20a9 b 301 3 +20b4 16 303 3 +20ca 4d 309 3 +2117 38 311 3 +214f 30 315 3 +217f 60 317 3 +21df d 322 3 +21ec 2b 325 3 +2217 3a 327 3 +2251 f 332 3 +2260 2d 333 3 +228d 1a 334 3 +22a7 32 335 3 +22d9 20 342 3 +22f9 c 343 3 +2305 24 348 3 +2329 a 349 3 +2333 3c 350 3 +236f 2a 352 3 +2399 1c 353 3 +23b5 9 354 3 +23be f 356 3 +23cd 2d 357 3 +23fa 2f 358 3 +2429 20 360 3 +2449 c 361 3 +2455 7 363 3 +245c 21 365 3 +247d 4a 368 3 +24c7 9 370 3 +24d0 1a 371 3 +24ea 4b 372 3 +2535 4 373 3 +2539 5 371 3 +253e 29 374 3 +2567 2d 376 3 +2594 4b 378 3 +25df 4 379 3 +25e3 a 382 3 +25ed b 383 3 FUNC 25f8 c9 0 -[DumpSymbols loadSymbolInfo:offset:] -25f8 13 391 4 -260b 2b 392 4 -2636 2a 393 4 -2660 2d 395 4 -268d 2e 398 4 -26bb 6 399 4 -26c1 1 399 4 +25f8 13 391 3 +260b 2b 392 3 +2636 2a 393 3 +2660 2d 395 3 +268d 2e 398 3 +26bb 6 399 3 +26c1 1 399 3 FUNC 26c2 2be 0 -[DumpSymbols loadSTABSSymbolInfo:offset:] -26c2 16 537 4 -26d8 9 538 4 -26e1 10 539 4 -26f1 2e 540 4 -271f 9 542 4 -2728 22 543 4 -274a 4 544 4 -274e a 546 4 -2758 3c 547 4 -2794 c 549 4 -27a0 e 550 4 -27ae 6 551 4 -27b4 25 552 4 -27d9 25 553 4 -27fe 25 554 4 -2823 c 555 4 -282f c 556 4 -283b c 559 4 -2847 23 562 4 -286a a 563 4 -2874 a 564 4 -287e 2e 565 4 -28ac 39 566 4 -28e5 2e 570 4 -2913 4 571 4 -2917 17 559 4 -292e 25 575 4 -2953 9 576 4 -295c 17 549 4 -2973 4 579 4 -2977 9 580 4 +26c2 16 537 3 +26d8 9 538 3 +26e1 10 539 3 +26f1 2e 540 3 +271f 9 542 3 +2728 22 543 3 +274a 4 544 3 +274e a 546 3 +2758 3c 547 3 +2794 c 549 3 +27a0 e 550 3 +27ae 6 551 3 +27b4 25 552 3 +27d9 25 553 3 +27fe 25 554 3 +2823 c 555 3 +282f c 556 3 +283b c 559 3 +2847 23 562 3 +286a a 563 3 +2874 a 564 3 +287e 2e 565 3 +28ac 39 566 3 +28e5 2e 570 3 +2913 4 571 3 +2917 17 559 3 +292e 25 575 3 +2953 9 576 3 +295c 17 549 3 +2973 4 579 3 +2977 9 580 3 FUNC 2980 28a 0 -[DumpSymbols loadSymbolInfo64:offset:] -2980 16 583 4 -2996 9 585 4 -299f 10 586 4 -29af 2e 587 4 -29dd 9 589 4 -29e6 22 590 4 -2a08 4 591 4 -2a0c c 593 4 -2a18 e 594 4 -2a26 6 595 4 -2a2c 25 596 4 -2a51 25 597 4 -2a76 25 598 4 -2a9b 9 599 4 -2aa4 c 600 4 -2ab0 c 603 4 -2abc 17 604 4 -2ad3 23 609 4 -2af6 a 610 4 -2b00 a 611 4 -2b0a 2e 612 4 -2b38 37 613 4 -2b6f 2e 615 4 -2b9d 4 616 4 -2ba1 17 603 4 -2bb8 25 620 4 -2bdd 9 621 4 -2be6 17 593 4 -2bfd 4 624 4 -2c01 9 625 4 +2980 16 583 3 +2996 9 585 3 +299f 10 586 3 +29af 2e 587 3 +29dd 9 589 3 +29e6 22 590 3 +2a08 4 591 3 +2a0c c 593 3 +2a18 e 594 3 +2a26 6 595 3 +2a2c 25 596 3 +2a51 25 597 3 +2a76 25 598 3 +2a9b 9 599 3 +2aa4 c 600 3 +2ab0 c 603 3 +2abc 17 604 3 +2ad3 23 609 3 +2af6 a 610 3 +2b00 a 611 3 +2b0a 2e 612 3 +2b38 37 613 3 +2b6f 2e 615 3 +2b9d 4 616 3 +2ba1 17 603 3 +2bb8 25 620 3 +2bdd 9 621 3 +2be6 17 593 3 +2bfd 4 624 3 +2c01 9 625 3 FUNC 2c0a 199 0 -[DumpSymbols loadSymbolInfoForArchitecture] -2c0a 13 628 4 -2c1d 41 630 4 -2c5e 2b 631 4 -2c89 1a 632 4 -2ca3 40 634 4 -2ce3 40 635 4 -2d23 5f 637 4 -2d82 17 639 4 -2d99 4 640 4 -2d9d 6 641 4 -2da3 1 641 4 +2c0a 13 628 3 +2c1d 41 630 3 +2c5e 2b 631 3 +2c89 1a 632 3 +2ca3 40 634 3 +2ce3 40 635 3 +2d23 5f 637 3 +2d82 17 639 3 +2d99 4 640 3 +2d9d 6 641 3 +2da3 1 641 3 FUNC 2da4 3e5 0 -[DumpSymbols loadHeader:offset:] -2da4 18 728 4 -2dbc 9 729 4 -2dc5 10 730 4 -2dd5 2e 731 4 -2e03 9 733 4 -2e0c 2b 734 4 -2e37 1e 736 4 -2e55 c 738 4 -2e61 e 739 4 -2e6f 6 740 4 -2e75 50 742 4 -2ec5 2e 743 4 -2ef3 2e 744 4 -2f21 2e 745 4 -2f4f 20 746 4 -2f6f 1b7 755 4 -3126 9 757 4 -312f 25 761 4 -3154 9 762 4 -315d 17 738 4 -3174 a 765 4 -317e b 766 4 -3189 1 766 4 +2da4 18 728 3 +2dbc 9 729 3 +2dc5 10 730 3 +2dd5 2e 731 3 +2e03 9 733 3 +2e0c 2b 734 3 +2e37 1e 736 3 +2e55 c 738 3 +2e61 e 739 3 +2e6f 6 740 3 +2e75 50 742 3 +2ec5 2e 743 3 +2ef3 2e 744 3 +2f21 2e 745 3 +2f4f 20 746 3 +2f6f 1b7 755 3 +3126 9 757 3 +312f 25 761 3 +3154 9 762 3 +315d 17 738 3 +3174 a 765 3 +317e b 766 3 +3189 1 766 3 FUNC 318a 41d 0 -[DumpSymbols loadHeader64:offset:] -318a 18 769 4 -31a2 9 771 4 -31ab 10 772 4 -31bb 2e 773 4 -31e9 9 775 4 -31f2 c 777 4 -31fe 2b 778 4 -3229 e 779 4 -3237 6 780 4 -323d 50 781 4 -328d 49 782 4 -32d6 49 783 4 -331f 2e 784 4 -334d 9 785 4 -3356 29 786 4 -337f 1c5 794 4 -3544 9 795 4 -354d 25 799 4 -3572 9 800 4 -357b 17 777 4 -3592 a 803 4 -359c b 804 4 -35a7 1 804 4 +318a 18 769 3 +31a2 9 771 3 +31ab 10 772 3 +31bb 2e 773 3 +31e9 9 775 3 +31f2 c 777 3 +31fe 2b 778 3 +3229 e 779 3 +3237 6 780 3 +323d 50 781 3 +328d 49 782 3 +32d6 49 783 3 +331f 2e 784 3 +334d 9 785 3 +3356 29 786 3 +337f 1c5 794 3 +3544 9 795 3 +354d 25 799 3 +3572 9 800 3 +357b 17 777 3 +3592 a 803 3 +359c b 804 3 +35a7 1 804 3 FUNC 35a8 52a 0 -[DumpSymbols loadModuleInfo] -35a8 14 807 4 -35bc e 808 4 -35ca 41 810 4 -360b 1a 811 4 -3625 6 812 4 -362b 6 814 4 -3631 17 815 4 -3648 c 816 4 -3654 29 820 4 -367d 29 821 4 -36a6 29 822 4 -36cf 35 824 4 -3704 12 826 4 -3716 17 827 4 -372d c 828 4 -3739 3c 832 4 -3775 a 834 4 -377f 9 836 4 -3788 25 837 4 -37ad c 839 4 -37b9 54 840 4 -380d 57 841 4 -3864 57 842 4 -38bb 57 843 4 -3912 57 844 4 -3969 1c 846 4 -3985 4b 847 4 -39d0 49 849 4 -3a19 13 839 4 -3a2c 6 851 4 -3a32 3c 852 4 -3a6e 3a 854 4 -3aa8 17 857 4 -3abf c 858 4 -3acb 7 859 4 +35a8 14 807 3 +35bc e 808 3 +35ca 41 810 3 +360b 1a 811 3 +3625 6 812 3 +362b 6 814 3 +3631 17 815 3 +3648 c 816 3 +3654 29 820 3 +367d 29 821 3 +36a6 29 822 3 +36cf 35 824 3 +3704 12 826 3 +3716 17 827 3 +372d c 828 3 +3739 3c 832 3 +3775 a 834 3 +377f 9 836 3 +3788 25 837 3 +37ad c 839 3 +37b9 54 840 3 +380d 57 841 3 +3864 57 842 3 +38bb 57 843 3 +3912 57 844 3 +3969 1c 846 3 +3985 4b 847 3 +39d0 49 849 3 +3a19 13 839 3 +3a2c 6 851 3 +3a32 3c 852 3 +3a6e 3a 854 3 +3aa8 17 857 3 +3abf c 858 3 +3acb 7 859 3 FUNC 3ad2 b6 0 WriteFormat -3ad2 10 862 4 -3ae2 6 867 4 -3ae8 24 868 4 -3b0c 27 869 4 -3b33 40 870 4 -3b73 c 873 4 -3b7f 9 874 4 +3ad2 10 862 3 +3ae2 6 867 3 +3ae8 24 868 3 +3b0c 27 869 3 +3b33 40 870 3 +3b73 c 873 3 +3b7f 9 874 3 FUNC 3b88 35 0 -[DumpSymbols availableArchitectures] -3b88 13 1140 4 -3b9b 1c 1141 4 -3bb7 6 1142 4 -3bbd 1 1142 4 +3b88 13 1140 3 +3b9b 1c 1141 3 +3bb7 6 1142 3 +3bbd 1 1142 3 FUNC 3bbe 1b4 0 -[DumpSymbols setArchitecture:] -3bbe 13 1158 4 -3bd1 1a 1159 4 -3beb 4 1160 4 -3bef 2a 1162 4 -3c19 9 1163 4 -3c22 2a 1165 4 -3c4c 9 1166 4 -3c55 9 1167 4 -3c5e 2a 1169 4 -3c88 6 1170 4 -3c8e 2a 1172 4 -3cb8 6 1173 4 -3cbe 2a 1175 4 -3ce8 4 1176 4 -3cec 6 1179 4 -3cf2 2c 1180 4 -3d1e 9 1181 4 -3d27 1c 1183 4 -3d43 1f 1184 4 -3d62 a 1187 4 -3d6c 6 1188 4 +3bbe 13 1158 3 +3bd1 1a 1159 3 +3beb 4 1160 3 +3bef 2a 1162 3 +3c19 9 1163 3 +3c22 2a 1165 3 +3c4c 9 1166 3 +3c55 9 1167 3 +3c5e 2a 1169 3 +3c88 6 1170 3 +3c8e 2a 1172 3 +3cb8 6 1173 3 +3cbe 2a 1175 3 +3ce8 4 1176 3 +3cec 6 1179 3 +3cf2 2c 1180 3 +3d1e 9 1181 3 +3d27 1c 1183 3 +3d43 1f 1184 3 +3d62 a 1187 3 +3d6c 6 1188 3 FUNC 3d72 14 0 -[DumpSymbols architecture] -3d72 c 1191 4 -3d7e 6 1192 4 -3d84 2 1193 4 +3d72 c 1191 3 +3d7e 6 1192 3 +3d84 2 1193 3 FUNC 3d86 e7 0 -[DumpSymbols writeSymbolFile:] -3d86 13 1196 4 -3d99 1a 1197 4 -3db3 48 1200 4 -3dfb 9 1201 4 -3e04 1e 1203 4 -3e22 6 1205 4 -3e28 9 1206 4 -3e31 21 1208 4 -3e52 b 1210 4 -3e5d a 1212 4 -3e67 6 1213 4 -3e6d 1 1213 4 +3d86 13 1196 3 +3d99 1a 1197 3 +3db3 48 1200 3 +3dfb 9 1201 3 +3e04 1e 1203 3 +3e22 6 1205 3 +3e28 9 1206 3 +3e31 21 1208 3 +3e52 b 1210 3 +3e5d a 1212 3 +3e67 6 1213 3 +3e6d 1 1213 3 FUNC 3e6e 65 0 -[MachSection initWithMachSection:andNumber:] -3e6e 13 1219 4 -3e81 37 1220 4 -3eb8 9 1221 4 -3ec1 9 1222 4 -3eca 3 1225 4 -3ecd 6 1226 4 -3ed3 1 1226 4 +3e6e 13 1219 3 +3e81 37 1220 3 +3eb8 9 1221 3 +3ec1 9 1222 3 +3eca 3 1225 3 +3ecd 6 1226 3 +3ed3 1 1226 3 FUNC 3ed4 14 0 -[MachSection sectionPointer] -3ed4 c 1228 4 -3ee0 6 1229 4 -3ee6 2 1230 4 +3ed4 c 1228 3 +3ee0 6 1229 3 +3ee6 2 1230 3 FUNC 3ee8 14 0 -[MachSection sectionNumber] -3ee8 c 1232 4 -3ef4 6 1233 4 -3efa 2 1234 4 +3ee8 c 1232 3 +3ef4 6 1233 3 +3efa 2 1234 3 FUNC 3efc 17c 0 -[DumpSymbols processDWARFSourceFileInfo:] -3efc 14 459 4 -3f10 a 460 4 -3f1a 3c 461 4 -3f56 20 463 4 -3f76 5 464 4 -3f7b 3a 465 4 -3fb5 1d 466 4 -3fd2 3a 467 4 -400c 2a 468 4 -4036 3b 464 4 -4071 7 471 4 -FUNC 4078 1d7 0 DumpFunctionMap -4078 15 82 4 -408d 13 83 4 -40a0 1e 85 4 -40be 42 89 4 -4100 20 90 4 -4120 2b 91 4 -414b 1a 92 4 -4165 23 93 4 -4188 46 96 4 -41ce 46 99 4 -4214 33 83 4 -4247 8 102 4 -424f 1 102 4 +3efc 14 459 3 +3f10 a 460 3 +3f1a 3c 461 3 +3f56 20 463 3 +3f76 5 464 3 +3f7b 3a 465 3 +3fb5 1d 466 3 +3fd2 3a 467 3 +400c 2a 468 3 +4036 3b 464 3 +4071 7 471 3 +FUNC 4078 1d7 0 DumpFunctionMap(std::map, std::allocator > >) +4078 15 82 3 +408d 13 83 3 +40a0 1e 85 3 +40be 42 89 3 +4100 20 90 3 +4120 2b 91 3 +414b 1a 92 3 +4165 23 93 3 +4188 46 96 3 +41ce 46 99 3 +4214 33 83 3 +4247 8 102 3 +424f 1 102 3 FUNC 4250 3ef 0 -[DumpSymbols processDWARFFunctionInfo:] -4250 15 473 4 -4265 25 474 4 -428a 1e 476 4 -42a8 a 480 4 -42b2 3c 481 4 -42ee 3d 483 4 -432b 23 485 4 -434e 26 487 4 -4374 6 489 4 -437a 37 490 4 -43b1 2a 491 4 -43db 17 492 4 -43f2 30 496 4 -4422 3d 497 4 -445f 2e 498 4 -448d 30 502 4 -44bd 64 504 4 -4521 34 507 4 -4555 9d 509 4 -45f2 45 474 4 -4637 8 513 4 -463f 1 513 4 +4250 15 473 3 +4265 25 474 3 +428a 1e 476 3 +42a8 a 480 3 +42b2 3c 481 3 +42ee 3d 483 3 +432b 23 485 3 +434e 26 487 3 +4374 6 489 3 +437a 37 490 3 +43b1 2a 491 3 +43db 17 492 3 +43f2 30 496 3 +4422 3d 497 3 +445f 2e 498 3 +448d 30 502 3 +44bd 64 504 3 +4521 34 507 3 +4555 9d 509 3 +45f2 45 474 3 +4637 8 513 3 +463f 1 513 3 FUNC 4640 1f5 0 -[DumpSymbols processDWARFLineNumberInfo:] -4640 15 515 4 -4655 25 516 4 -467a 39 520 4 -46b3 26 521 4 -46d9 6 523 4 -46df 37 524 4 -4716 2a 525 4 -4740 17 526 4 -4757 30 529 4 -4787 61 531 4 -47e8 45 516 4 -482d 8 534 4 -4835 1 534 4 +4640 15 515 3 +4655 25 516 3 +467a 39 520 3 +46b3 26 521 3 +46d9 6 523 3 +46df 37 524 3 +4716 2a 525 3 +4740 17 526 3 +4757 30 529 3 +4787 61 531 3 +47e8 45 516 3 +482d 8 534 3 +4835 1 534 3 FUNC 4836 10f 0 -[DumpSymbols dealloc] -4836 13 1145 4 -4849 1c 1146 4 -4865 1c 1147 4 -4881 1c 1148 4 -489d 1c 1149 4 -48b9 1c 1150 4 -48d5 1c 1151 4 -48f1 25 1152 4 -4916 29 1154 4 -493f 6 1155 4 -4945 1 1155 4 +4836 13 1145 3 +4849 1c 1146 3 +4865 1c 1147 3 +4881 1c 1148 3 +489d 1c 1149 3 +48b9 1c 1150 3 +48d5 1c 1151 3 +48f1 25 1152 3 +4916 29 1154 3 +493f 6 1155 3 +4945 1 1155 3 FUNC 4946 512 0 -[DumpSymbols loadDWARFSymbolInfo:offset:] -4946 17 402 4 -495d 9 405 4 -4966 10 406 4 -4976 2b 408 4 -49a1 38 409 4 -49d9 3a 410 4 -4a13 2e 411 4 -4a41 31 416 4 -4a72 e 418 4 -4a80 24 420 4 -4aa4 5 422 4 -4aa9 b 424 4 -4ab4 b 425 4 -4abf e 426 4 -4acd 2b 427 4 -4af8 2b 428 4 -4b23 2c 431 4 -4b4f 52 439 4 -4ba1 34 444 4 -4bd5 1a 446 4 -4bef 21 451 4 -4c10 1e 452 4 -4c2e 21 453 4 -4c4f 40 422 4 -4c8f 6 453 4 -4c95 170 422 4 -4e05 43 456 4 -4e48 10 457 4 +4946 17 402 3 +495d 9 405 3 +4966 10 406 3 +4976 2b 408 3 +49a1 38 409 3 +49d9 3a 410 3 +4a13 2e 411 3 +4a41 31 416 3 +4a72 e 418 3 +4a80 24 420 3 +4aa4 5 422 3 +4aa9 b 424 3 +4ab4 b 425 3 +4abf e 426 3 +4acd 2b 427 3 +4af8 2b 428 3 +4b23 2c 431 3 +4b4f 52 439 3 +4ba1 34 444 3 +4bd5 1a 446 3 +4bef 21 451 3 +4c10 1e 452 3 +4c2e 21 453 3 +4c4f 40 422 3 +4c8f 6 453 3 +4c95 170 422 3 +4e05 43 456 3 +4e48 10 457 3 FUNC 4e58 4fd 0 -[DumpSymbols generateSectionDictionary:] -4e58 18 663 4 -4e70 10 665 4 -4e80 2e 666 4 -4eae 9 668 4 -4eb7 2b 669 4 -4ee2 7 670 4 -4ee9 2e 672 4 -4f17 d 676 4 -4f24 32 678 4 -4f56 29 680 4 -4f7f a 684 4 -4f89 3c 685 4 -4fc5 31 688 4 -4ff6 5d 689 4 -5053 26 692 4 -5079 21 694 4 -509a c 698 4 -50a6 e 699 4 -50b4 6 700 4 -50ba 9 701 4 -50c3 2e 702 4 -50f1 c 704 4 -50fd 3c 706 4 -5139 66 709 4 -519f 1c 712 4 -51bb fb 714 4 -52b6 6 717 4 -52bc 5 718 4 -52c1 19 704 4 -52da 25 714 4 -52ff 2e 722 4 -532d 9 723 4 -5336 17 698 4 -534d 8 725 4 -5355 1 725 4 +4e58 18 663 3 +4e70 10 665 3 +4e80 2e 666 3 +4eae 9 668 3 +4eb7 2b 669 3 +4ee2 7 670 3 +4ee9 2e 672 3 +4f17 d 676 3 +4f24 32 678 3 +4f56 29 680 3 +4f7f a 684 3 +4f89 3c 685 3 +4fc5 31 688 3 +4ff6 5d 689 3 +5053 26 692 3 +5079 21 694 3 +509a c 698 3 +50a6 e 699 3 +50b4 6 700 3 +50ba 9 701 3 +50c3 2e 702 3 +50f1 c 704 3 +50fd 3c 706 3 +5139 66 709 3 +519f 1c 712 3 +51bb fb 714 3 +52b6 6 717 3 +52bc 5 718 3 +52c1 19 704 3 +52da 25 714 3 +52ff 2e 722 3 +532d 9 723 3 +5336 17 698 3 +534d 8 725 3 +5355 1 725 3 FUNC 5356 24a 0 -[DumpSymbols getSectionMapForArchitecture:] -5356 14 643 4 -536a 43 645 4 -53ad 1a 648 4 -53c7 1c 645 4 -53e3 18 648 4 -53fb 40 650 4 -543b 20 651 4 -545b 17 652 4 -5472 16 651 4 -5488 cb 652 4 -5553 11 654 4 -5564 32 657 4 -5596 a 658 4 +5356 14 643 3 +536a 43 645 3 +53ad 1a 648 3 +53c7 1c 645 3 +53e3 18 648 3 +53fb 40 650 3 +543b 20 651 3 +545b 17 652 3 +5472 16 651 3 +5488 cb 652 3 +5553 11 654 3 +5564 32 657 3 +5596 a 658 3 FUNC 55a0 3fe 0 -[DumpSymbols initWithContentsOfFile:] -55a0 14 1056 4 -55b4 3b 1057 4 -55ef 44 1059 4 -5633 17 1060 4 -564a c 1061 4 -5656 1f 1064 4 -5675 2b 1067 4 -56a0 a 1069 4 -56aa 35 1083 4 -56df 2 1087 4 -56e1 1a 1088 4 -56fb 3d 1087 4 -5738 33 1092 4 -576b 6 1094 4 -5771 e 1095 4 -577f 17 1096 4 -5796 c 1097 4 -57a2 1c 1101 4 -57be 1f 1103 4 -57dd 18 1104 4 -57f5 23 1107 4 -5818 25 1109 4 -583d 1c 1107 4 -5859 17 1110 4 -5870 c 1111 4 -587c 2a 1115 4 -58a6 8 1116 4 -58ae a 1118 4 -58b8 9 1119 4 -58c1 d 1122 4 -58ce 29 1124 4 -58f7 20 1126 4 -5917 20 1128 4 -5937 57 1132 4 -598e 9 1136 4 -5997 7 1137 4 +55a0 14 1056 3 +55b4 3b 1057 3 +55ef 44 1059 3 +5633 17 1060 3 +564a c 1061 3 +5656 1f 1064 3 +5675 2b 1067 3 +56a0 a 1069 3 +56aa 35 1083 3 +56df 2 1087 3 +56e1 1a 1088 3 +56fb 3d 1087 3 +5738 33 1092 3 +576b 6 1094 3 +5771 e 1095 3 +577f 17 1096 3 +5796 c 1097 3 +57a2 1c 1101 3 +57be 1f 1103 3 +57dd 18 1104 3 +57f5 23 1107 3 +5818 25 1109 3 +583d 1c 1107 3 +5859 17 1110 3 +5870 c 1111 3 +587c 2a 1115 3 +58a6 8 1116 3 +58ae a 1118 3 +58b8 9 1119 3 +58c1 d 1122 3 +58ce 29 1124 3 +58f7 20 1126 3 +5917 20 1128 3 +5937 57 1132 3 +598e 9 1136 3 +5997 7 1137 3 FUNC 599e d74 0 -[DumpSymbols outputSymbolFile:] -599e 18 877 4 -59b6 2e 879 4 -59e4 30 880 4 -5a14 5d 882 4 -5a71 30 883 4 -5aa1 5d 885 4 -5afe 2e 888 4 -5b2c 38 891 4 -5b64 46 892 4 -5baa 26 893 4 -5bd0 20 895 4 -5bf0 20 904 4 -5c10 30 898 4 -5c40 f 899 4 -5c4f 1e 904 4 -5c6d 17 907 4 -5c84 17 908 4 -5c9b 44 911 4 -5cdf 44 914 4 -5d23 a 917 4 -5d2d 36 921 4 -5d63 30 923 4 +599e 18 877 3 +59b6 2e 879 3 +59e4 30 880 3 +5a14 5d 882 3 +5a71 30 883 3 +5aa1 5d 885 3 +5afe 2e 888 3 +5b2c 38 891 3 +5b64 46 892 3 +5baa 26 893 3 +5bd0 20 895 3 +5bf0 20 904 3 +5c10 30 898 3 +5c40 f 899 3 +5c4f 1e 904 3 +5c6d 17 907 3 +5c84 17 908 3 +5c9b 44 911 3 +5cdf 44 914 3 +5d23 a 917 3 +5d2d 36 921 3 +5d63 30 923 3 5d93 9 18 4 5d9c 9 19 4 5da5 c 20 4 -5db1 56 923 4 -5e07 74 925 4 -5e7b f 927 4 -5e8a 44 932 4 -5ece 20 933 4 -5eee c 934 4 -5efa 4e 935 4 -5f48 41 936 4 -5f89 f 937 4 -5f98 14 934 4 -5fac 7 941 4 -5fb3 14 942 4 -5fc7 14 943 4 -5fdb 1d 946 4 -5ff8 c 948 4 -6004 24 949 4 -6028 29 950 4 -6051 9 953 4 -605a 28 954 4 -6082 2e 955 4 -60b0 1e 957 4 -60ce 7 959 4 -60d5 26 962 4 -60fb 2a 963 4 -6125 2a 964 4 -614f 6 966 4 -6155 2a 967 4 -617f e 971 4 -618d 43 972 4 -61d0 4c 974 4 -621c 8 975 4 -6224 2e 979 4 -6252 2e 982 4 -6280 2e 985 4 -62ae 2e 988 4 -62dc 2e 991 4 -630a 2e 994 4 -6338 2e 997 4 -6366 2e 1000 4 -6394 54 1004 4 -63e8 c 1005 4 -63f4 e 1007 4 -6402 27 1008 4 -6429 8 1009 4 -6431 34 1010 4 -6465 24 1012 4 -6489 2 1013 4 -648b 2a 1017 4 -64b5 a 1019 4 -64bf 14 1020 4 -64d3 1d 1021 4 -64f0 a 1025 4 -64fa 32 1026 4 -652c 33 1028 4 -655f c 1029 4 -656b 55 1034 4 -65c0 f 1036 4 -65cf 16 1040 4 -65e5 61 1041 4 -6646 f 1043 4 -6655 47 1046 4 -669c c 1048 4 -66a8 11 948 4 -66b9 4e 1052 4 -6707 b 1053 4 -FUNC 6712 11 0 operator new +5db1 56 923 3 +5e07 74 925 3 +5e7b f 927 3 +5e8a 44 932 3 +5ece 20 933 3 +5eee c 934 3 +5efa 4e 935 3 +5f48 41 936 3 +5f89 f 937 3 +5f98 14 934 3 +5fac 7 941 3 +5fb3 14 942 3 +5fc7 14 943 3 +5fdb 1d 946 3 +5ff8 c 948 3 +6004 24 949 3 +6028 29 950 3 +6051 9 953 3 +605a 28 954 3 +6082 2e 955 3 +60b0 1e 957 3 +60ce 7 959 3 +60d5 26 962 3 +60fb 2a 963 3 +6125 2a 964 3 +614f 6 966 3 +6155 2a 967 3 +617f e 971 3 +618d 43 972 3 +61d0 4c 974 3 +621c 8 975 3 +6224 2e 979 3 +6252 2e 982 3 +6280 2e 985 3 +62ae 2e 988 3 +62dc 2e 991 3 +630a 2e 994 3 +6338 2e 997 3 +6366 2e 1000 3 +6394 54 1004 3 +63e8 c 1005 3 +63f4 e 1007 3 +6402 27 1008 3 +6429 8 1009 3 +6431 34 1010 3 +6465 24 1012 3 +6489 2 1013 3 +648b 2a 1017 3 +64b5 a 1019 3 +64bf 14 1020 3 +64d3 1d 1021 3 +64f0 a 1025 3 +64fa 32 1026 3 +652c 33 1028 3 +655f c 1029 3 +656b 55 1034 3 +65c0 f 1036 3 +65cf 16 1040 3 +65e5 61 1041 3 +6646 f 1043 3 +6655 47 1046 3 +669c c 1048 3 +66a8 11 948 3 +66b9 4e 1052 3 +6707 b 1053 3 +FUNC 6712 11 0 operator new(unsigned long, void*) 6712 c 94 5 671e 5 94 5 6723 1 94 5 -FUNC 6724 e 0 operator delete +FUNC 6724 e 0 operator delete(void*, void*) 6724 c 98 5 6730 2 98 5 -6732 c 74 6 673e 7 76 6 6745 2 77 6 6747 1a 78 6 @@ -779,211 +778,235 @@ 676e 3 79 6 6771 2 80 6 6773 1 80 6 -6774 c 94 6 6780 d 95 6 678d 1 95 6 -678e 13 127 7 -67a1 2a 127 7 -67cb 1 127 7 -67cc 13 127 7 -67df 2a 127 7 -6809 1 127 7 -680a 13 127 7 -681d 2a 127 7 -6847 1 127 7 +678e 13 127 74 +67a1 2a 127 74 +67cb 1 127 74 +67cc 13 127 74 +67df 2a 127 74 +6809 1 127 74 +680a 13 127 74 +681d 2a 127 74 +6847 1 127 74 +FUNC 6848 e 0 dwarf2reader::LineInfoHandler::DefineDir(std::string const&, unsigned int) 6848 c 131 7 -6854 2 131 7 +6854 2 131 74 +FUNC 6856 26 0 dwarf2reader::LineInfoHandler::DefineFile(std::string const&, int, unsigned int, unsigned long long, unsigned long long) 6856 24 142 7 -687a 2 142 7 +687a 2 142 74 +FUNC 687c 1a 0 dwarf2reader::LineInfoHandler::AddLine(unsigned long long, unsigned int, unsigned int, unsigned int) 687c 18 150 7 -6894 2 150 7 -6896 12 299 7 -68a8 12 299 7 -68ba 13 301 7 -68cd 2a 301 7 -68f7 1 301 7 -68f8 13 301 7 -690b 2a 301 7 -6935 1 301 7 -6936 13 301 7 -6949 2a 301 7 -6973 1 301 7 +6894 2 150 74 +6896 12 299 74 +68a8 12 299 74 +68ba 13 301 74 +68cd 2a 301 74 +68f7 1 301 74 +68f8 13 301 74 +690b 2a 301 74 +6935 1 301 74 +6936 13 301 74 +6949 2a 301 74 +6973 1 301 74 +FUNC 6974 44 0 dwarf2reader::Dwarf2Handler::StartCompilationUnit(unsigned long long, unsigned char, unsigned char, unsigned long long, unsigned char) 6974 39 308 7 -69ad b 308 7 +69ad b 308 74 +FUNC 69b8 1f 0 dwarf2reader::Dwarf2Handler::StartDIE(unsigned long long, dwarf2reader::DwarfTag, std::list, std::allocator > > const&) 69b8 18 314 7 -69d0 7 314 7 -69d7 1 314 7 +69d0 7 314 74 +69d7 1 314 74 +FUNC 69d8 26 0 dwarf2reader::Dwarf2Handler::ProcessAttributeUnsigned(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, unsigned long long) 69d8 24 323 7 -69fc 2 323 7 +69fc 2 323 74 +FUNC 69fe 26 0 dwarf2reader::Dwarf2Handler::ProcessAttributeSigned(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, long long) 69fe 24 332 7 -6a22 2 332 7 +6a22 2 332 74 +FUNC 6a24 26 0 dwarf2reader::Dwarf2Handler::ProcessAttributeBuffer(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, char const*, unsigned long long) 6a24 24 345 7 -6a48 2 345 7 +6a48 2 345 74 +FUNC 6a4a 1a 0 dwarf2reader::Dwarf2Handler::ProcessAttributeString(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, std::string const&) 6a4a 18 354 7 -6a62 2 354 7 +6a62 2 354 74 +FUNC 6a64 1a 0 dwarf2reader::Dwarf2Handler::EndDIE(unsigned long long) 6a64 18 360 7 -6a7c 2 360 7 +6a7c 2 360 74 6a7e c 44 8 6a8a 2 44 8 -6a8c 13 55 9 -6a9f 35 55 9 -6ad4 13 91 9 -6ae7 73 96 9 -6b5a 13 98 9 -6b6d 35 98 9 -6ba2 c 74 9 -6bae 1a 75 9 -6bc8 2 76 9 +6a8c 13 55 32 +6a9f 35 55 32 +6ad4 13 91 32 +6ae7 73 96 32 +6b5a 13 98 32 +6b6d 35 98 32 +6bae 1a 75 3 +6bc8 2 76 3 +FUNC 6bca 20 0 std::_Rb_tree_const_iterator >::operator!=(std::_Rb_tree_const_iterator > const&) const 6bca c 287 10 -6bd6 14 288 10 +6bd6 14 288 40 +FUNC 6bea 16 0 std::_Rb_tree_const_iterator >::operator->() const 6bea c 249 10 -6bf6 a 250 10 -6c00 c 613 11 -6c0c 7 614 11 -6c13 1 614 11 -6c14 c 241 11 -6c20 c 242 11 +6bf6 a 250 40 +6c0c 7 614 72 +6c13 1 614 72 +6c14 c 241 40 +6c20 c 242 40 +FUNC 6c2c 16 0 std::_Rb_tree_const_iterator >::operator*() const 6c2c c 245 11 -6c38 a 246 11 -6c42 c 241 11 -6c4e c 242 11 +6c38 a 246 40 +6c42 c 241 40 +6c4e c 242 40 +FUNC 6c5a 20 0 std::_Rb_tree_const_iterator > >::operator!=(std::_Rb_tree_const_iterator > > const&) const 6c5a c 287 11 -6c66 14 288 11 +6c66 14 288 40 +FUNC 6c7a 16 0 std::_Rb_tree_const_iterator > >::operator->() const 6c7a c 249 11 -6c86 a 250 11 -6c90 c 185 12 -6c9c 18 186 12 -6cb4 c 203 12 -6cc0 14 204 12 -6cd4 c 69 13 -6ce0 d 69 13 -6ced 1 69 13 -6cee c 89 13 -6cfa 20 90 13 -6d1a c 69 13 -6d26 d 69 13 -6d33 1 69 13 -6d34 c 69 13 -6d40 d 69 13 -6d4d 1 69 13 +6c86 a 250 40 +6c90 c 185 34 +6c9c 18 186 34 +6cc0 14 204 34 +6cd4 c 69 70 +6ce0 d 69 70 +6ced 1 69 70 +6cee c 89 70 +6cfa 20 90 70 +6d1a c 69 70 +6d26 d 69 70 +6d33 1 69 70 +6d34 c 69 70 +6d40 d 69 70 +6d4d 1 69 70 +FUNC 6d4e 25 0 std::_Rb_tree_const_iterator >::operator++() 6d4e c 253 13 -6d5a 14 255 13 -6d6e 5 256 13 -6d73 1 256 13 +6d5a 14 255 40 +6d6e 5 256 40 +6d73 1 256 40 +FUNC 6d74 25 0 std::_Rb_tree_const_iterator > >::operator++() 6d74 c 253 13 -6d80 14 255 13 -6d94 5 256 13 -6d99 1 256 13 +6d80 14 255 40 +6d94 5 256 40 +6d99 1 256 40 +FUNC 6d9a 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_begin() 6d9a c 461 13 -6da6 8 462 13 +6da6 8 462 40 +FUNC 6dae 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_begin() 6dae c 461 13 -6dba 8 462 13 -6dc2 c 65 14 -6dce 2 65 14 -6dd0 c 72 14 -6ddc 2 72 14 -6dde c 97 15 -6dea d 97 15 -6df7 1 97 15 -6df8 c 105 15 -6e04 d 105 15 -6e11 1 105 15 -6e12 c 105 15 -6e1e d 105 15 -6e2b 1 105 15 -6e2c c 67 15 -6e38 2 67 15 -6e3a c 99 15 -6e46 14 100 15 -6e5a c 99 15 -6e66 14 100 15 +6dba 8 462 40 +6dc2 c 65 68 +6dce 2 65 68 +6dd0 c 72 68 +6ddc 2 72 68 +6dde c 97 69 +6dea d 97 69 +6df7 1 97 69 +6df8 c 105 69 +6e04 d 105 69 +6e11 1 105 69 +6e12 c 105 69 +6e1e d 105 69 +6e2b 1 105 69 +6e2c c 67 68 +6e38 2 67 68 +6e3a c 99 69 +6e46 14 100 69 +6e5a c 99 69 +6e66 14 100 69 +FUNC 6e7a 2b 0 std::_Vector_base >::get_allocator() const 6e7a 10 93 16 -6e8a 1b 94 16 -6ea5 1 94 16 -6ea6 c 65 16 -6eb2 2 65 16 -6eb4 c 72 16 -6ec0 2 72 16 -6ec2 c 97 16 -6ece d 97 16 -6edb 1 97 16 -6edc c 105 16 -6ee8 d 105 16 -6ef5 1 105 16 -6ef6 c 105 16 -6f02 d 105 16 -6f0f 1 105 16 -6f10 c 67 16 -6f1c 2 67 16 -6f1e c 99 16 -6f2a 14 100 16 -6f3e c 99 16 -6f4a 14 100 16 +6e8a 1b 94 71 +6ea5 1 94 71 +6ea6 c 65 68 +6eb2 2 65 68 +6eb4 c 72 68 +6ec0 2 72 68 +6ec2 c 97 69 +6ece d 97 69 +6edb 1 97 69 +6edc c 105 69 +6ee8 d 105 69 +6ef5 1 105 69 +6ef6 c 105 69 +6f02 d 105 69 +6f0f 1 105 69 +6f10 c 67 68 +6f1c 2 67 68 +6f1e c 99 69 +6f2a 14 100 69 +6f3e c 99 69 +6f4a 14 100 69 +FUNC 6f5e 2b 0 std::_Vector_base >::get_allocator() const 6f5e 10 93 16 -6f6e 1b 94 16 -6f89 1 94 16 -6f8a c 603 16 -6f96 c 603 16 +6f6e 1b 94 71 +6f89 1 94 71 +6f8a c 603 72 +6f96 c 603 72 +FUNC 6fa2 23 0 std::vector >::begin() 6fa2 c 333 16 -6fae 17 334 16 -6fc5 1 334 16 +6fae 17 334 71 +6fc5 1 334 71 +FUNC 6fc6 26 0 std::vector >::end() 6fc6 c 351 16 -6fd2 1a 352 16 -6fec c 665 16 -6ff8 5 666 16 -6ffd 1 666 16 -6ffe c 608 16 -700a 14 609 16 -701e c 665 16 -702a 5 666 16 -702f 1 666 16 +6fd2 1a 352 71 +6ff8 5 666 72 +6ffd 1 666 72 +6ffe c 608 72 +700a 14 609 72 +702a 5 666 72 +702f 1 666 72 +FUNC 7030 35 0 bool __gnu_cxx::operator!= > >(__gnu_cxx::__normal_iterator > > const&, __gnu_cxx::__normal_iterator > > const&) 7030 d 693 16 -703d 28 694 16 -7065 1 694 16 -7066 c 603 16 -7072 c 603 16 -707e c 628 16 -708a 27 629 16 -70b1 1 629 16 -70b2 c 84 16 -70be 1f 85 16 -70dd 1 85 16 +703d 28 694 72 +7065 1 694 72 +7066 c 603 72 +7072 c 603 72 +708a 27 629 72 +70b1 1 629 72 +70b2 c 84 70 +70be 1f 85 70 +70dd 1 85 70 +FUNC 70de 32 0 std::pair, __gnu_cxx::hash, std::equal_to, std::allocator > >*> std::make_pair, __gnu_cxx::hash, std::equal_to, std::allocator > >*>(std::string, __gnu_cxx::hash_map, __gnu_cxx::hash, std::equal_to, std::allocator > >*) 70de 10 144 16 -70ee 22 145 16 -7110 c 189 16 -711c a 190 16 -7126 c 193 16 -7132 d 194 16 -713f 1 194 16 -7140 c 84 16 -714c 17 85 16 -7163 1 85 16 +70ee 22 145 70 +711c a 190 34 +7132 d 194 34 +713f 1 194 34 +7140 c 84 70 +714c 17 85 70 +7163 1 85 70 +FUNC 7164 2d 0 std::pair std::make_pair(char const*, unsigned long) 7164 c 144 16 -7170 21 145 16 -7191 1 145 16 -7192 c 84 16 -719e 1d 85 16 -71bb 1 85 16 +7170 21 145 70 +7191 1 145 70 +7192 c 84 70 +719e 1d 85 70 +71bb 1 85 70 +FUNC 71bc 30 0 std::pair > std::make_pair >(char*, std::pair) 71bc 10 144 16 -71cc 20 145 16 -71ec c 89 16 -71f8 20 90 16 -7218 d 89 16 -7225 70 90 16 -7295 1 90 16 +71cc 20 145 70 +71ec c 89 70 +71f8 20 90 70 +7218 d 89 70 +7225 70 90 70 +7295 1 90 70 +FUNC 7296 12 0 std::iterator_traits::iterator_category std::__iterator_category(unsigned long const* const&) 7296 c 164 17 72a2 6 165 17 +FUNC 72a8 1d 0 std::iterator_traits::difference_type std::__distance(unsigned long const*, unsigned long const*, std::random_access_iterator_tag) 72a8 c 92 18 72b4 11 97 18 72c5 1 97 18 +FUNC 72c6 33 0 std::iterator_traits::difference_type std::distance(unsigned long const*, unsigned long const*) 72c6 c 114 18 72d2 27 118 18 72f9 1 118 18 +FUNC 72fa 20 0 void std::__advance(unsigned long const*&, int, std::random_access_iterator_tag) 72fa c 150 18 7306 14 155 18 +FUNC 731a 33 0 void std::advance(unsigned long const*&, int) 731a c 172 18 7326 27 175 18 734d 1 175 18 +FUNC 734e 7a 0 unsigned long const* std::lower_bound(unsigned long const*, unsigned long const*, unsigned long const&) 734e c 2625 19 735a 15 2642 19 736f 2 2646 19 @@ -997,1379 +1020,1320 @@ 73b7 6 2658 19 73bd 6 2646 19 73c3 5 2660 19 -73c8 13 225 19 -73db b 227 19 -73e6 e 228 19 -73f4 1c 229 19 -7410 20 230 19 -7430 6 231 19 -7436 c 72 19 -7442 2 72 19 -7444 c 105 19 -7450 d 105 19 -745d 1 105 19 -745e c 105 19 -746a d 105 19 -7477 1 105 19 -7478 c 80 19 -7484 d 80 19 -7491 1 80 19 -7492 c 67 19 -749e 2 67 19 -74a0 c 99 19 -74ac 14 100 19 +73db b 227 34 +73e6 e 228 34 +73f4 1c 229 34 +7410 20 230 34 +7430 6 231 34 +7436 c 72 68 +7442 2 72 68 +7444 c 105 69 +7450 d 105 69 +745d 1 105 69 +745e c 105 69 +746a d 105 69 +7477 1 105 69 +7478 c 80 71 +7484 d 80 71 +7491 1 80 71 +7492 c 67 68 +749e 2 67 68 +74a0 c 99 69 +74ac 14 100 69 +FUNC 74c0 2b 0 std::_Vector_base >::get_allocator() const 74c0 10 93 19 -74d0 1b 94 19 -74eb 1 94 19 -74ec c 238 19 -74f8 a 239 19 +74d0 1b 94 71 +74eb 1 94 71 +74ec c 238 40 +74f8 a 239 40 +FUNC 7502 26 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::begin() const 7502 c 585 19 -750e 1a 588 19 +750e 1a 588 40 +FUNC 7528 19 0 std::map, std::allocator > >::begin() const 7528 c 243 20 -7534 d 244 20 -7541 1 244 20 +7534 d 244 45 +7541 1 244 45 +FUNC 7542 26 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::end() const 7542 c 596 20 -754e 1a 597 20 +754e 1a 597 40 +FUNC 7568 19 0 std::map, std::allocator > >::end() const 7568 c 260 20 -7574 d 261 20 -7581 1 261 20 -7582 c 65 20 -758e 2 65 20 -7590 c 72 20 -759c 2 72 20 -759e c 97 20 -75aa d 97 20 -75b7 1 97 20 -75b8 c 105 20 -75c4 d 105 20 -75d1 1 105 20 -75d2 c 72 20 -75de 2 72 20 -75e0 c 105 20 -75ec d 105 20 -75f9 1 105 20 -75fa c 397 20 -7606 d 397 20 -7613 1 397 20 -7614 c 105 20 -7620 d 105 20 -762d 1 105 20 +7574 d 261 45 +7581 1 261 45 +7582 c 65 68 +758e 2 65 68 +7590 c 72 68 +759c 2 72 68 +759e c 97 69 +75aa d 97 69 +75b7 1 97 69 +75b8 c 105 69 +75c4 d 105 69 +75d1 1 105 69 +75d2 c 72 68 +75de 2 72 68 +75e0 c 105 69 +75ec d 105 69 +75f9 1 105 69 +75fa c 397 40 +7606 d 397 40 +7613 1 397 40 +7614 c 105 69 +7620 d 105 69 +762d 1 105 69 +FUNC 762e 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_right(std::_Rb_tree_node_base*) 762e c 496 20 -763a 8 497 20 +763a 8 497 40 +FUNC 7642 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_left(std::_Rb_tree_node_base*) 7642 c 488 20 -764e 8 489 20 -7656 c 65 20 -7662 2 65 20 -7664 c 72 20 -7670 2 72 20 -7672 c 97 20 -767e d 97 20 -768b 1 97 20 -768c c 105 20 -7698 d 105 20 -76a5 1 105 20 -76a6 c 72 20 -76b2 2 72 20 -76b4 c 105 20 -76c0 d 105 20 -76cd 1 105 20 -76ce c 397 20 -76da d 397 20 -76e7 1 397 20 -76e8 c 105 20 -76f4 d 105 20 -7701 1 105 20 +764e 8 489 40 +7656 c 65 68 +7662 2 65 68 +7664 c 72 68 +7670 2 72 68 +7672 c 97 69 +767e d 97 69 +768b 1 97 69 +768c c 105 69 +7698 d 105 69 +76a5 1 105 69 +76a6 c 72 68 +76b2 2 72 68 +76b4 c 105 69 +76c0 d 105 69 +76cd 1 105 69 +76ce c 397 40 +76da d 397 40 +76e7 1 397 40 +76e8 c 105 69 +76f4 d 105 69 +7701 1 105 69 +FUNC 7702 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_right(std::_Rb_tree_node_base*) 7702 c 496 20 -770e 8 497 20 +770e 8 497 40 +FUNC 7716 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_left(std::_Rb_tree_node_base*) 7716 c 488 20 -7722 8 489 20 -772a c 84 20 -7736 2f 85 20 -7765 2 86 20 -7767 1 86 20 -7768 c 80 20 -7774 d 80 20 -7781 1 80 20 -7782 c 96 20 -778e 12 97 20 -77a0 2 98 20 -77a2 c 84 20 -77ae 2f 85 20 -77dd 2 86 20 -77df 1 86 20 -77e0 c 80 20 -77ec d 80 20 -77f9 1 80 20 -77fa c 96 20 -7806 12 97 20 -7818 2 98 20 -781a c 107 20 -7826 d 107 20 -7833 1 107 20 +7722 8 489 40 +772a c 84 71 +7736 2f 85 71 +7765 2 86 71 +7767 1 86 71 +7768 c 80 71 +7774 d 80 71 +7781 1 80 71 +7782 c 96 71 +778e 12 97 71 +77a0 2 98 71 +77a2 c 84 71 +77ae 2f 85 71 +77dd 2 86 71 +77df 1 86 71 +77e0 c 80 71 +77ec d 80 71 +77f9 1 80 71 +77fa c 96 71 +7806 12 97 71 +7818 2 98 71 +7826 d 107 68 +7833 1 107 68 +FUNC 7834 2e 0 void std::_Destroy >(std::string*, std::string*, std::allocator) 7834 c 171 21 -7840 2 173 21 -7842 12 174 21 -7854 c 173 21 -7860 2 174 21 -7862 c 167 21 -786e a 168 21 +7840 2 173 73 +7842 12 174 73 +7854 c 173 73 +7860 2 174 73 +7862 c 167 40 +786e a 168 40 +FUNC 7878 26 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::begin() 7878 c 581 21 -7884 1a 582 21 +7884 1a 582 40 +FUNC 789e 19 0 std::map, std::allocator > >::begin() 789e c 234 21 -78aa d 235 21 -78b7 1 235 21 +78aa d 235 45 +78b7 1 235 45 +FUNC 78b8 26 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::end() 78b8 c 592 21 -78c4 1a 593 21 +78c4 1a 593 40 +FUNC 78de 19 0 std::map, std::allocator > >::end() 78de c 251 21 -78ea d 252 21 -78f7 1 252 21 -78f8 c 167 21 -7904 a 168 21 +78ea d 252 45 +78f7 1 252 45 +78f8 c 167 40 +7904 a 168 40 +FUNC 790e 26 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::begin() 790e c 581 21 -791a 1a 582 21 +791a 1a 582 40 +FUNC 7934 19 0 std::map, std::less, std::allocator > > >::begin() 7934 c 234 21 -7940 d 235 21 -794d 1 235 21 +7940 d 235 45 +794d 1 235 45 +FUNC 794e 26 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::end() 794e c 592 21 -795a 1a 593 21 +795a 1a 593 40 +FUNC 7974 19 0 std::map, std::less, std::allocator > > >::end() 7974 c 251 21 -7980 d 252 21 -798d 1 252 21 +7980 d 252 45 +798d 1 252 45 +FUNC 798e 11 0 std::_Select1st, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >::operator()(std::pair, __gnu_cxx::hash, std::equal_to, std::allocator > >*>&) const 798e c 546 22 -799a 5 547 22 -799f 1 547 22 -79a0 c 128 22 -79ac 13 129 22 -79bf 1 129 22 -79c0 c 395 22 -79cc 22 396 22 -79ee c 198 23 -79fa d 199 23 -7a07 1 199 23 -7a08 c 65 23 -7a14 2 65 23 -7a16 c 72 23 -7a22 2 72 23 -7a24 c 97 23 -7a30 d 97 23 -7a3d 1 97 23 -7a3e c 105 23 -7a4a d 105 23 -7a57 1 105 23 -7a58 c 65 23 -7a64 2 65 23 -7a66 c 72 23 -7a72 2 72 23 -7a74 c 105 23 -7a80 d 105 23 -7a8d 1 105 23 -7a8e c 97 23 -7a9a d 97 23 -7aa7 1 97 23 -7aa8 c 72 23 -7ab4 2 72 23 -7ab6 c 105 23 -7ac2 d 105 23 -7acf 1 105 23 -7ad0 c 93 23 -7adc d 94 23 -7ae9 1 94 23 +799a 5 547 41 +799f 1 547 41 +79a0 c 128 34 +79ac 13 129 34 +79bf 1 129 34 +79cc 22 396 34 +79fa d 199 42 +7a07 1 199 42 +7a08 c 65 68 +7a14 2 65 68 +7a16 c 72 68 +7a22 2 72 68 +7a24 c 97 69 +7a30 d 97 69 +7a3d 1 97 69 +7a3e c 105 69 +7a4a d 105 69 +7a57 1 105 69 +7a58 c 65 68 +7a64 2 65 68 +7a66 c 72 68 +7a72 2 72 68 +7a74 c 105 69 +7a80 d 105 69 +7a8d 1 105 69 +7a8e c 97 69 +7a9a d 97 69 +7aa7 1 97 69 +7aa8 c 72 68 +7ab4 2 72 68 +7ab6 c 105 69 +7ac2 d 105 69 +7acf 1 105 69 +7adc d 94 68 +7ae9 1 94 68 +FUNC 7aea 2f 0 std::_Vector_base >::_M_deallocate(dwarf2reader::CompilationUnit::Abbrev*, unsigned long) 7aea c 120 23 -7af6 6 122 23 -7afc 1d 123 23 -7b19 1 123 23 -7b1a c 108 23 -7b26 43 109 23 -7b69 1 109 23 -7b6a c 65 23 -7b76 2 65 23 -7b78 c 103 23 -7b84 d 103 23 -7b91 1 103 23 -7b92 c 65 23 -7b9e 2 65 23 -7ba0 c 103 23 -7bac d 103 23 -7bb9 1 103 23 -7bba c 93 23 -7bc6 d 94 23 -7bd3 1 94 23 +7af6 6 122 71 +7afc 1d 123 71 +7b19 1 123 71 +7b1a c 108 71 +7b26 43 109 71 +7b69 1 109 71 +7b6a c 65 68 +7b76 2 65 68 +7b78 c 103 69 +7b84 d 103 69 +7b91 1 103 69 +7b92 c 65 68 +7b9e 2 65 68 +7ba0 c 103 69 +7bac d 103 69 +7bb9 1 103 69 +7bc6 d 94 68 +7bd3 1 94 68 +FUNC 7bd4 2f 0 std::_Vector_base >::_M_deallocate(dwarf2reader::SourceFileInfo*, unsigned long) 7bd4 c 120 23 -7be0 6 122 23 -7be6 1d 123 23 -7c03 1 123 23 -7c04 c 108 23 -7c10 43 109 23 -7c53 1 109 23 -7c54 c 188 23 -7c60 12 189 23 -7c72 2 190 23 -7c74 c 35 23 -7c80 d 35 23 -7c8d 1 35 23 -7c8e c 107 23 -7c9a d 107 23 -7ca7 1 107 23 +7be0 6 122 71 +7be6 1d 123 71 +7c03 1 123 71 +7c04 c 108 71 +7c10 43 109 71 +7c53 1 109 71 +7c54 c 188 71 +7c60 12 189 71 +7c72 2 190 71 +7c74 c 35 32 +7c80 d 35 32 +7c8d 1 35 32 +7c9a d 107 68 +7ca7 1 107 68 +FUNC 7ca8 2e 0 void std::_Destroy >(dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*, std::allocator) 7ca8 c 171 23 -7cb4 2 173 23 -7cb6 12 174 23 -7cc8 c 173 23 -7cd4 2 174 23 -7cd6 d 272 23 -7ce3 8c 273 23 -7d6f 1 273 23 -7d70 c 93 23 -7d7c d 94 23 -7d89 1 94 23 +7cb4 2 173 73 +7cb6 12 174 73 +7cc8 c 173 73 +7cd4 2 174 73 +7cd6 d 272 71 +7ce3 8c 273 71 +7d6f 1 273 71 +7d7c d 94 68 +7d89 1 94 68 +FUNC 7d8a 2f 0 std::_Vector_base >::_M_deallocate(std::string*, unsigned long) 7d8a c 120 23 -7d96 6 122 23 -7d9c 1d 123 23 -7db9 1 123 23 -7dba c 108 23 -7dc6 3d 109 23 -7e03 1 109 23 -7e04 c 188 23 -7e10 12 189 23 -7e22 2 190 23 -7e24 d 272 23 -7e31 8c 273 23 -7ebd 1 273 23 -7ebe c 595 23 -7eca 2b 596 23 -7ef5 1 596 23 -7ef6 c 613 23 -7f02 7 614 23 -7f09 1 614 23 -7f0a c 65 23 -7f16 2 65 23 -7f18 c 72 23 -7f24 2 72 23 -7f26 c 103 23 -7f32 d 103 23 -7f3f 1 103 23 -7f40 c 105 23 -7f4c d 105 23 -7f59 1 105 23 -7f5a c 65 23 -7f66 2 65 23 -7f68 c 72 23 -7f74 2 72 23 -7f76 c 103 23 -7f82 d 103 23 -7f8f 1 103 23 -7f90 c 105 23 -7f9c d 105 23 -7fa9 1 105 23 -7faa c 105 23 -7fb6 d 105 23 -7fc3 1 105 23 -7fc4 c 574 23 -7fd0 d 575 23 -7fdd 1 575 23 -7fde c 574 23 -7fea d 575 23 -7ff7 1 575 23 +7d96 6 122 71 +7d9c 1d 123 71 +7db9 1 123 71 +7dba c 108 71 +7dc6 3d 109 71 +7e03 1 109 71 +7e04 c 188 71 +7e10 12 189 71 +7e22 2 190 71 +7e24 d 272 71 +7e31 8c 273 71 +7ebd 1 273 71 +7eca 2b 596 34 +7ef5 1 596 34 +7f02 7 614 72 +7f09 1 614 72 +7f0a c 65 68 +7f16 2 65 68 +7f18 c 72 68 +7f24 2 72 68 +7f26 c 103 69 +7f32 d 103 69 +7f3f 1 103 69 +7f40 c 105 69 +7f4c d 105 69 +7f59 1 105 69 +7f5a c 65 68 +7f66 2 65 68 +7f68 c 72 68 +7f74 2 72 68 +7f76 c 103 69 +7f82 d 103 69 +7f8f 1 103 69 +7f90 c 105 69 +7f9c d 105 69 +7fa9 1 105 69 +7faa c 105 69 +7fb6 d 105 69 +7fc3 1 105 69 +7fd0 d 575 34 +7fdd 1 575 34 +7fea d 575 34 +7ff7 1 575 34 +FUNC 7ff8 11 0 std::_Select1st, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >::operator()(std::pair, __gnu_cxx::hash, std::equal_to, std::allocator > >*> const&) const 7ff8 c 550 23 -8004 5 551 23 -8009 1 551 23 -800a c 599 23 -8016 2f 600 23 -8045 1 600 23 -8046 c 84 23 -8052 1e 85 23 +8004 5 551 41 +8009 1 551 41 +8016 2f 600 34 +8045 1 600 34 +8046 c 84 70 +8052 1e 85 70 +FUNC 8070 11 0 std::_Select1st > >::operator()(std::pair >&) const 8070 c 546 23 -807c 5 547 23 -8081 1 547 23 +807c 5 547 41 +8081 1 547 41 +FUNC 8082 11 0 std::_Select1st > >::operator()(std::pair > const&) const 8082 c 550 23 -808e 5 551 23 -8093 1 551 23 -8094 c 128 23 -80a0 13 129 23 -80b3 1 129 23 -80b4 c 84 23 -80c0 1e 85 23 -80de c 65 23 -80ea 2 65 23 -80ec c 103 23 -80f8 d 103 23 -8105 1 103 23 -8106 c 65 23 -8112 2 65 23 -8114 c 72 23 -8120 2 72 23 -8122 c 105 23 -812e d 105 23 -813b 1 105 23 -813c c 103 23 -8148 d 103 23 -8155 1 103 23 -8156 c 105 23 -8162 d 105 23 -816f 1 105 23 -8170 c 80 23 -817c d 80 23 -8189 1 80 23 -818a c 67 23 -8196 2 67 23 -8198 c 99 23 -81a4 14 100 23 +808e 5 551 41 +8093 1 551 41 +8094 c 128 34 +80a0 13 129 34 +80b3 1 129 34 +80b4 c 84 70 +80c0 1e 85 70 +80de c 65 68 +80ea 2 65 68 +80ec c 103 69 +80f8 d 103 69 +8105 1 103 69 +8106 c 65 68 +8112 2 65 68 +8114 c 72 68 +8120 2 72 68 +8122 c 105 69 +812e d 105 69 +813b 1 105 69 +813c c 103 69 +8148 d 103 69 +8155 1 103 69 +8156 c 105 69 +8162 d 105 69 +816f 1 105 69 +8170 c 80 71 +817c d 80 71 +8189 1 80 71 +818a c 67 68 +8196 2 67 68 +8198 c 99 69 +81a4 14 100 69 +FUNC 81b8 2b 0 std::_Vector_base<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::get_allocator() const 81b8 10 93 23 -81c8 1b 94 23 -81e3 1 94 23 -81e4 c 99 23 -81f0 14 100 23 -8204 c 107 23 -8210 2 107 23 +81c8 1b 94 71 +81e3 1 94 71 +81e4 c 99 69 +81f0 14 100 69 +8210 2 107 68 +FUNC 8212 2e 0 void std::_Destroy<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>) 8212 c 171 23 -821e 2 173 23 -8220 12 174 23 -8232 c 173 23 -823e 2 174 23 -8240 c 107 23 -824c d 107 23 -8259 1 107 23 -825a c 67 23 -8266 2 67 23 -8268 c 99 23 -8274 14 100 23 -8288 c 403 23 -8294 1c 404 23 -82b0 a 406 23 -82ba a 407 23 -82c4 c 408 23 -82d0 e 409 23 -82de c 553 23 -82ea 36 554 23 -8320 2 555 23 -8322 c 103 23 -832e d 103 23 -833b 1 103 23 +821e 2 173 73 +8220 12 174 73 +8232 c 173 73 +823e 2 174 73 +824c d 107 68 +8259 1 107 68 +825a c 67 68 +8266 2 67 68 +8268 c 99 69 +8274 14 100 69 +8288 c 403 40 +8294 1c 404 40 +82b0 a 406 40 +82ba a 407 40 +82c4 c 408 40 +82d0 e 409 40 +82de c 553 40 +82ea 36 554 40 +8320 2 555 40 +8322 c 103 69 +832e d 103 69 +833b 1 103 69 +FUNC 833c 2b 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::get_allocator() const 833c 10 350 23 -834c 1b 351 23 -8367 1 351 23 -8368 c 69 23 -8374 2 69 23 -8376 c 107 23 -8382 d 107 23 -838f 1 107 23 -8390 c 93 23 -839c d 94 23 -83a9 1 94 23 +834c 1b 351 40 +8367 1 351 40 +8368 c 69 70 +8374 2 69 70 +8382 d 107 68 +838f 1 107 68 +839c d 94 68 +83a9 1 94 68 +FUNC 83aa 2a 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_put_node(std::_Rb_tree_node >*) 83aa c 359 23 -83b6 1e 360 23 +83b6 1e 360 40 +FUNC 83d4 59 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::destroy_node(std::_Rb_tree_node >*) 83d4 d 387 23 -83e1 35 389 23 -8416 17 390 23 -842d 1 390 23 +83e1 35 389 40 +8416 17 390 40 +842d 1 390 40 +FUNC 842e 56 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_erase(std::_Rb_tree_node >*) 842e c 1051 23 -843a 2 1054 23 -843c 1a 1056 23 -8456 e 1057 23 -8464 12 1058 23 -8476 6 1059 23 -847c 6 1054 23 -8482 2 1059 23 -8484 d 569 23 -8491 58 570 23 -84e9 1 570 23 -84ea c 147 23 -84f6 31 148 23 -8527 1 148 23 -8528 c 92 23 -8534 d 92 23 -8541 1 92 23 -8542 c 67 23 -854e 2 67 23 -8550 c 99 23 -855c 14 100 23 -8570 c 403 23 -857c 1c 404 23 -8598 a 406 23 -85a2 a 407 23 -85ac c 408 23 -85b8 e 409 23 -85c6 c 553 23 -85d2 36 554 23 -8608 2 555 23 -860a c 103 23 -8616 d 103 23 -8623 1 103 23 +843a 2 1054 40 +843c 1a 1056 40 +8456 e 1057 40 +8464 12 1058 40 +8476 6 1059 40 +847c 6 1054 40 +8482 2 1059 40 +8484 d 569 40 +8491 58 570 40 +84e9 1 570 40 +84ea c 147 45 +84f6 31 148 45 +8527 1 148 45 +8528 c 92 45 +8534 d 92 45 +8541 1 92 45 +8542 c 67 68 +854e 2 67 68 +8550 c 99 69 +855c 14 100 69 +8570 c 403 40 +857c 1c 404 40 +8598 a 406 40 +85a2 a 407 40 +85ac c 408 40 +85b8 e 409 40 +85c6 c 553 40 +85d2 36 554 40 +8608 2 555 40 +860a c 103 69 +8616 d 103 69 +8623 1 103 69 +FUNC 8624 2b 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::get_allocator() const 8624 10 350 23 -8634 1b 351 23 -864f 1 351 23 -8650 c 69 23 -865c d 69 23 -8669 1 69 23 -866a c 69 23 -8676 30 69 23 -86a6 c 107 23 -86b2 d 107 23 -86bf 1 107 23 -86c0 c 93 23 -86cc d 94 23 -86d9 1 94 23 +8634 1b 351 40 +864f 1 351 40 +8650 c 69 70 +865c d 69 70 +8669 1 69 70 +866a c 69 70 +8676 30 69 70 +86b2 d 107 68 +86bf 1 107 68 +86cc d 94 68 +86d9 1 94 68 +FUNC 86da 2a 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_put_node(std::_Rb_tree_node > >*) 86da c 359 23 -86e6 1e 360 23 +86e6 1e 360 40 +FUNC 8704 59 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::destroy_node(std::_Rb_tree_node > >*) 8704 d 387 23 -8711 35 389 23 -8746 17 390 23 -875d 1 390 23 +8711 35 389 40 +8746 17 390 40 +875d 1 390 40 +FUNC 875e 56 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_erase(std::_Rb_tree_node > >*) 875e c 1051 23 -876a 2 1054 23 -876c 1a 1056 23 -8786 e 1057 23 -8794 12 1058 23 -87a6 6 1059 23 -87ac 6 1054 23 -87b2 2 1059 23 -87b4 d 569 23 -87c1 58 570 23 -8819 1 570 23 -881a c 147 23 -8826 31 148 23 -8857 1 148 23 -8858 c 92 23 -8864 d 92 23 -8871 1 92 23 -8872 c 603 23 -887e c 603 23 +876a 2 1054 40 +876c 1a 1056 40 +8786 e 1057 40 +8794 12 1058 40 +87a6 6 1059 40 +87ac 6 1054 40 +87b2 2 1059 40 +87b4 d 569 40 +87c1 58 570 40 +8819 1 570 40 +881a c 147 45 +8826 31 148 45 +8857 1 148 45 +8858 c 92 45 +8864 d 92 45 +8871 1 92 45 +8872 c 603 72 +887e c 603 72 +FUNC 888a 23 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::begin() 888a c 333 23 -8896 17 334 23 -88ad 1 334 23 -88ae c 653 23 -88ba 2a 654 23 +8896 17 334 71 +88ad 1 334 71 +88ba 2a 654 72 +FUNC 88e4 42 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::operator[](unsigned long) 88e4 c 494 23 -88f0 36 495 23 +88f0 36 495 71 +FUNC 8926 26 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::end() 8926 c 351 23 -8932 1a 352 23 +8932 1a 352 71 +FUNC 894c 28 0 bool std::operator==, std::allocator >(std::basic_string, std::allocator > const&, std::basic_string, std::allocator > const&) 894c c 2115 24 -8958 1c 2116 24 +8958 1c 2116 37 +FUNC 8974 23 0 std::equal_to::operator()(std::string const&, std::string const&) const 8974 c 199 24 -8980 17 200 24 -8997 1 200 24 -8998 c 80 24 -89a4 d 80 24 -89b1 1 80 24 -89b2 c 67 24 -89be 2 67 24 -89c0 c 99 24 -89cc 14 100 24 +8980 17 200 41 +8997 1 200 41 +8998 c 80 71 +89a4 d 80 71 +89b1 1 80 71 +89b2 c 67 68 +89be 2 67 68 +89c0 c 99 69 +89cc 14 100 69 +FUNC 89e0 2b 0 std::_Vector_base<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::get_allocator() const 89e0 10 93 24 -89f0 1b 94 24 -8a0b 1 94 24 -8a0c c 99 24 -8a18 14 100 24 -8a2c c 84 24 -8a38 2f 85 24 -8a67 2 86 24 -8a69 1 86 24 -8a6a c 96 24 -8a76 12 97 24 -8a88 2 98 24 -8a8a c 107 24 -8a96 2 107 24 +89f0 1b 94 71 +8a0b 1 94 71 +8a0c c 99 69 +8a18 14 100 69 +8a2c c 84 71 +8a38 2f 85 71 +8a67 2 86 71 +8a69 1 86 71 +8a6a c 96 71 +8a76 12 97 71 +8a88 2 98 71 +8a96 2 107 68 +FUNC 8a98 2e 0 void std::_Destroy<__gnu_cxx::_Hashtable_node > >**, std::allocator<__gnu_cxx::_Hashtable_node > >*> >(__gnu_cxx::_Hashtable_node > >**, __gnu_cxx::_Hashtable_node > >**, std::allocator<__gnu_cxx::_Hashtable_node > >*>) 8a98 c 171 24 -8aa4 2 173 24 -8aa6 12 174 24 -8ab8 c 173 24 -8ac4 2 174 24 +8aa4 2 173 73 +8aa6 12 174 73 +8ab8 c 173 73 +8ac4 2 174 73 +FUNC 8ac6 13 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::max_size() const 8ac6 c 407 24 -8ad2 7 408 24 -8ad9 1 408 24 -8ada c 603 24 -8ae6 c 603 24 +8ad2 7 408 71 +8ad9 1 408 71 +8ada c 603 72 +8ae6 c 603 72 +FUNC 8af2 26 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::end() 8af2 c 351 24 -8afe 1a 352 24 +8afe 1a 352 71 +FUNC 8b18 23 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::begin() 8b18 c 333 24 -8b24 17 334 24 -8b3b 1 334 24 -8b3c c 653 24 -8b48 2a 654 24 -8b72 c 613 24 -8b7e 7 614 24 -8b85 1 614 24 +8b24 17 334 71 +8b3b 1 334 71 +8b48 2a 654 72 +8b7e 7 614 72 +8b85 1 614 72 +FUNC 8b86 42 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::operator[](unsigned long) 8b86 c 494 24 -8b92 36 495 24 -8bc8 c 107 24 -8bd4 d 107 24 -8be1 1 107 24 +8b92 36 495 71 +8bd4 d 107 68 +8be1 1 107 68 +FUNC 8be2 28 0 void std::swap<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**>(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**&, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**&) 8be2 c 92 25 -8bee 8 97 25 -8bf6 a 98 25 -8c00 a 99 25 +8bee 8 97 61 +8bf6 a 98 61 +8c00 a 99 61 +FUNC 8c0a 50 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::swap(std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >&) 8c0a c 733 25 -8c16 12 735 25 -8c28 18 736 25 -8c40 1a 737 25 -8c5a c 595 25 -8c66 2b 596 25 -8c91 1 596 25 -8c92 c 599 25 -8c9e 2f 600 25 -8ccd 1 600 25 +8c16 12 735 71 +8c28 18 736 71 +8c40 1a 737 71 +8c66 2b 596 34 +8c91 1 596 34 +8c9e 2f 600 34 +8ccd 1 600 34 +FUNC 8cce 28 0 void std::swap<__gnu_cxx::_Hashtable_node > >**>(__gnu_cxx::_Hashtable_node > >**&, __gnu_cxx::_Hashtable_node > >**&) 8cce c 92 25 -8cda 8 97 25 -8ce2 a 98 25 -8cec a 99 25 +8cda 8 97 61 +8ce2 a 98 61 +8cec a 99 61 +FUNC 8cf6 50 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::swap(std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >&) 8cf6 c 733 25 -8d02 12 735 25 -8d14 18 736 25 -8d2c 1a 737 25 -8d46 c 84 25 -8d52 2f 85 25 -8d81 2 86 25 -8d83 1 86 25 -8d84 c 96 25 -8d90 12 97 25 -8da2 2 98 25 +8d02 12 735 71 +8d14 18 736 71 +8d2c 1a 737 71 +8d46 c 84 71 +8d52 2f 85 71 +8d81 2 86 71 +8d83 1 86 71 +8d84 c 96 71 +8d90 12 97 71 +8da2 2 98 71 +FUNC 8da4 13 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::max_size() const 8da4 c 407 25 -8db0 7 408 25 -8db7 1 408 25 -8db8 c 93 25 -8dc4 d 94 25 -8dd1 1 94 25 +8db0 7 408 71 +8db7 1 408 71 +8dc4 d 94 68 +8dd1 1 94 68 +FUNC 8dd2 2f 0 std::_Vector_base<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::_M_deallocate(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long) 8dd2 c 120 25 -8dde 6 122 25 -8de4 1d 123 25 -8e01 1 123 25 -8e02 c 108 25 -8e0e 3d 109 25 -8e4b 1 109 25 -8e4c c 272 25 -8e58 4b 273 25 -8ea3 1 273 25 -8ea4 c 188 25 -8eb0 12 189 25 -8ec2 2 190 25 -8ec4 c 603 25 -8ed0 c 603 25 +8dde 6 122 71 +8de4 1d 123 71 +8e01 1 123 71 +8e02 c 108 71 +8e0e 3d 109 71 +8e4b 1 109 71 +8e4c c 272 71 +8e58 4b 273 71 +8ea3 1 273 71 +8ea4 c 188 71 +8eb0 12 189 71 +8ec2 2 190 71 +8ec4 c 603 72 +8ed0 c 603 72 +FUNC 8edc 2b 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::begin() const 8edc c 342 25 -8ee8 1f 343 25 -8f07 1 343 25 +8ee8 1f 343 71 +8f07 1 343 71 +FUNC 8f08 2c 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::end() const 8f08 c 360 25 -8f14 20 361 25 -8f34 c 665 25 -8f40 5 666 25 -8f45 1 666 25 -8f46 d 758 25 -8f53 2b 759 25 +8f14 20 361 71 +8f40 5 666 72 +8f45 1 666 72 +8f53 2b 759 72 +FUNC 8f7e 3c 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::size() const 8f7e c 402 25 -8f8a 30 403 25 -8fba c 587 25 -8fc6 26 588 25 -8fec c 509 25 -8ff8 15 511 25 -900d 79 513 25 -9086 21 517 25 -90a7 1 517 25 -90a8 c 224 25 -90b4 14 225 25 -90c8 c 591 25 -90d4 26 592 25 +8f8a 30 403 71 +8fc6 26 588 34 +8ff8 15 511 34 +900d 79 513 34 +9086 21 517 34 +90a7 1 517 34 +90b4 14 225 42 +90d4 26 592 34 +FUNC 90fa 49 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::capacity() const 90fa c 449 25 -9106 3d 451 25 -9143 1 451 25 -9144 c 103 25 -9150 d 103 25 -915d 1 103 25 -915e 10 285 25 -916e 1b 286 25 -9189 1 286 25 -918a c 93 25 -9196 d 94 25 -91a3 1 94 25 -91a4 c 300 25 -91b0 1e 301 25 -91ce d 620 25 -91db 56 622 25 -9231 17 623 25 -9248 c 1078 25 -9254 9 1080 25 -925d 1a 1082 25 -9277 2 1083 25 -9279 8 1085 25 -9281 12 1086 25 -9293 6 1087 25 -9299 6 1083 25 -929f 1b 1089 25 -92ba 1d 1080 25 -92d7 c 1091 25 -92e3 1 1091 25 -92e4 d 360 25 -92f1 77 361 25 -9368 c 93 25 -9374 d 93 25 -9381 1 93 25 -9382 c 72 25 -938e 2 72 25 -9390 c 105 25 -939c d 105 25 -93a9 1 105 25 -93aa c 301 26 -93b6 d 301 26 -93c3 1 301 26 -93c4 c 93 26 -93d0 d 94 26 -93dd 1 94 26 +9106 3d 451 71 +9143 1 451 71 +9144 c 103 69 +9150 d 103 69 +915d 1 103 69 +916e 1b 286 34 +9189 1 286 34 +9196 d 94 68 +91a3 1 94 68 +91b0 1e 301 34 +91db 56 622 34 +9231 17 623 34 +9254 9 1080 34 +925d 1a 1082 34 +9277 2 1083 34 +9279 8 1085 34 +9281 12 1086 34 +9293 6 1087 34 +9299 6 1083 34 +929f 1b 1089 34 +92ba 1d 1080 34 +92d7 c 1091 34 +92e3 1 1091 34 +92e4 d 360 34 +92f1 77 361 34 +9368 c 93 42 +9374 d 93 42 +9381 1 93 42 +9382 c 72 68 +938e 2 72 68 +9390 c 105 69 +939c d 105 69 +93a9 1 105 69 +93aa c 301 66 +93b6 d 301 66 +93c3 1 301 66 +93d0 d 94 68 +93dd 1 94 68 +FUNC 93de 2f 0 std::_Vector_base<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::_M_deallocate(__gnu_cxx::_Hashtable_node > >**, unsigned long) 93de c 120 26 -93ea 6 122 26 -93f0 1d 123 26 -940d 1 123 26 -940e c 108 26 -941a 3d 109 26 -9457 1 109 26 -9458 c 188 26 -9464 12 189 26 -9476 2 190 26 -9478 c 272 26 -9484 4b 273 26 -94cf 1 273 26 -94d0 c 603 26 -94dc c 603 26 +93ea 6 122 71 +93f0 1d 123 71 +940d 1 123 71 +940e c 108 71 +941a 3d 109 71 +9457 1 109 71 +9458 c 188 71 +9464 12 189 71 +9476 2 190 71 +9478 c 272 71 +9484 4b 273 71 +94cf 1 273 71 +94d0 c 603 72 +94dc c 603 72 +FUNC 94e8 2b 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::begin() const 94e8 c 342 26 -94f4 1f 343 26 -9513 1 343 26 +94f4 1f 343 71 +9513 1 343 71 +FUNC 9514 2c 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::end() const 9514 c 360 26 -9520 20 361 26 -9540 c 661 26 -954c 2d 662 26 -9579 1 662 26 +9520 20 361 71 +954c 2d 662 72 +9579 1 662 72 +FUNC 957a 2d 0 unsigned long const& std::max(unsigned long const&, unsigned long const&) 957a c 206 26 -9586 e 211 26 -9594 8 212 26 -959c b 213 26 -95a7 1 213 26 -95a8 c 649 26 -95b4 19 650 26 -95cd 1 650 26 -95ce c 665 26 -95da 5 666 26 -95df 1 666 26 -95e0 d 758 26 -95ed 2b 759 26 -9618 c 665 26 -9624 5 666 26 -9629 1 666 26 -962a d 758 26 -9637 2b 759 26 +9586 e 211 61 +9594 8 212 61 +959c b 213 61 +95a7 1 213 61 +95b4 19 650 72 +95cd 1 650 72 +95da 5 666 72 +95df 1 666 72 +95ed 2b 759 72 +9624 5 666 72 +9629 1 666 72 +9637 2b 759 72 +FUNC 9662 49 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::capacity() const 9662 c 449 26 -966e 3d 451 26 -96ab 1 451 26 +966e 3d 451 71 +96ab 1 451 71 +FUNC 96ac 3c 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::size() const 96ac c 402 26 -96b8 30 403 26 -96e8 c 587 26 -96f4 26 588 26 -971a c 591 26 -9726 26 592 26 -974c c 103 26 -9758 d 103 26 -9765 1 103 26 -9766 10 285 26 -9776 1b 286 26 -9791 1 286 26 -9792 c 93 26 -979e d 94 26 -97ab 1 94 26 -97ac c 300 26 -97b8 1e 301 26 -97d6 d 620 26 -97e3 56 622 26 -9839 17 623 26 -9850 c 1078 26 -985c 9 1080 26 -9865 1a 1082 26 -987f 2 1083 26 -9881 8 1085 26 -9889 12 1086 26 -989b 6 1087 26 -98a1 6 1083 26 -98a7 1b 1089 26 -98c2 1d 1080 26 -98df c 1091 26 -98eb 1 1091 26 -98ec d 360 26 -98f9 77 361 26 -9970 c 69 26 -997c 20 69 26 -999c d 103 26 -99a9 5c 104 26 -9a05 1 104 26 -9a06 c 69 26 -9a12 2c 69 26 -9a3e d 103 26 -9a4b 5c 104 26 -9aa7 1 104 26 -9aa8 c 661 26 -9ab4 2d 662 26 -9ae1 1 662 26 -9ae2 c 649 26 -9aee 19 650 26 -9b07 1 650 26 -9b08 c 665 26 -9b14 5 666 26 -9b19 1 666 26 -9b1a d 758 26 -9b27 2b 759 26 -9b52 c 72 26 -9b5e 2 72 26 -9b60 c 105 26 -9b6c d 105 26 -9b79 1 105 26 -9b7a c 69 26 -9b86 2 69 26 -9b88 c 107 26 -9b94 d 107 26 -9ba1 1 107 26 -9ba2 c 93 26 -9bae d 94 26 -9bbb 1 94 26 +96b8 30 403 71 +96f4 26 588 34 +9726 26 592 34 +974c c 103 69 +9758 d 103 69 +9765 1 103 69 +9776 1b 286 34 +9791 1 286 34 +979e d 94 68 +97ab 1 94 68 +97b8 1e 301 34 +97e3 56 622 34 +9839 17 623 34 +985c 9 1080 34 +9865 1a 1082 34 +987f 2 1083 34 +9881 8 1085 34 +9889 12 1086 34 +989b 6 1087 34 +98a1 6 1083 34 +98a7 1b 1089 34 +98c2 1d 1080 34 +98df c 1091 34 +98eb 1 1091 34 +98ec d 360 34 +98f9 77 361 34 +9970 c 69 70 +997c 20 69 70 +99a9 5c 104 68 +9a05 1 104 68 +9a06 c 69 70 +9a12 2c 69 70 +9a4b 5c 104 68 +9aa7 1 104 68 +9ab4 2d 662 72 +9ae1 1 662 72 +9aee 19 650 72 +9b07 1 650 72 +9b14 5 666 72 +9b19 1 666 72 +9b27 2b 759 72 +9b52 c 72 68 +9b5e 2 72 68 +9b60 c 105 69 +9b6c d 105 69 +9b79 1 105 69 +9b7a c 69 70 +9b86 2 69 70 +9b94 d 107 68 +9ba1 1 107 68 +9bae d 94 68 +9bbb 1 94 68 +FUNC 9bbc 2a 0 std::_List_base, std::allocator > >::_M_put_node(std::_List_node >*) 9bbc c 315 26 -9bc8 1e 316 26 +9bc8 1e 316 66 +FUNC 9be6 35 0 bool __gnu_cxx::operator!=<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > > const&, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > > const&) 9be6 d 699 26 -9bf3 28 700 26 -9c1b 1 700 26 -9c1c c 621 26 -9c28 d 623 26 -9c35 5 624 26 -FUNC 9c3a 4b 0 fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::_Hashtable_node > >*> -9c3a c 539 26 -9c46 8 541 26 -9c4e 2 542 26 -9c50 12 543 26 -9c62 21 542 26 -9c83 2 543 26 -9c85 1 543 26 +9bf3 28 700 72 +9c1b 1 700 72 +9c28 d 623 72 +9c35 5 624 72 +FUNC 9c3a 4b 0 void std::__fill::fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::_Hashtable_node > >* const&) +9c3a c 539 61 +9c46 8 541 61 +9c4e 2 542 61 +9c50 12 543 61 +9c62 21 542 61 +9c83 2 543 61 +9c85 1 543 61 +FUNC 9c86 2b 0 void std::fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::_Hashtable_node > >* const&) 9c86 c 560 26 -9c92 4 567 26 -9c96 1b 568 26 -9cb1 1 568 26 +9c92 4 567 61 +9c96 1b 568 61 +9cb1 1 568 61 +FUNC 9cb2 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, std::allocator<__gnu_cxx::_Hashtable_node > >*> >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, std::allocator<__gnu_cxx::_Hashtable_node > >*>) 9cb2 c 171 26 -9cbe 2 173 26 -9cc0 1a 174 26 -9cda 21 173 26 -9cfb 2 174 26 -9cfd 1 174 26 -9cfe c 97 26 -9d0a 7 98 26 -9d11 1 98 26 -9d12 c 83 26 -9d1e 1d 85 26 -9d3b 5 86 26 -9d40 16 88 26 -9d56 c 296 26 -9d62 1d 297 26 -9d7f 1 297 26 -9d80 d 603 26 -9d8d e 605 26 -9d9b 9 606 26 -9da4 3c 609 26 -9de0 b 610 26 -9deb 11 609 26 -9dfc b 612 26 -9e07 12 614 26 -9e19 b 615 26 -9e24 13 612 26 -9e37 8 615 26 -9e3f 1 615 26 -9e40 d 749 26 -9e4d 15 751 26 -9e62 1a 752 26 -9e7c b 754 26 -9e87 49 755 26 -9ed0 3b 756 26 -9f0b 12 754 26 -9f1d 15 758 26 -9f32 8 759 26 -9f3a 1c 760 26 -9f56 f 761 26 -9f65 41 762 26 -9fa6 c 97 26 -9fb2 7 98 26 -9fb9 1 98 26 -9fba c 83 26 -9fc6 1d 85 26 -9fe3 5 86 26 -9fe8 17 88 26 -9fff 1 88 26 -a000 c 296 26 -a00c 1d 297 26 -a029 1 297 26 -a02a d 603 26 -a037 e 605 26 -a045 9 606 26 -a04e 3c 609 26 -a08a b 610 26 -a095 11 609 26 -a0a6 b 612 26 -a0b1 12 614 26 -a0c3 b 615 26 -a0ce 13 612 26 -a0e1 8 615 26 -a0e9 1 615 26 -a0ea d 749 26 -a0f7 15 751 26 -a10c 1a 752 26 -a126 b 754 26 -a131 49 755 26 -a17a 3b 756 26 -a1b5 12 754 26 -a1c7 15 758 26 -a1dc 8 759 26 -a1e4 1c 760 26 -a200 f 761 26 -a20f 41 762 26 +9cbe 2 173 73 +9cc0 1a 174 73 +9cda 21 173 73 +9cfb 2 174 73 +9cfd 1 174 73 +9d0a 7 98 68 +9d11 1 98 68 +9d1e 1d 85 68 +9d3b 5 86 68 +9d40 16 88 68 +9d62 1d 297 34 +9d7f 1 297 34 +9d8d e 605 34 +9d9b 9 606 34 +9da4 3c 609 34 +9de0 b 610 34 +9deb 11 609 34 +9dfc b 612 34 +9e07 12 614 34 +9e19 b 615 34 +9e24 13 612 34 +9e37 8 615 34 +9e3f 1 615 34 +9e4d 15 751 34 +9e62 1a 752 34 +9e7c b 754 34 +9e87 49 755 34 +9ed0 3b 756 34 +9f0b 12 754 34 +9f1d 15 758 34 +9f32 8 759 34 +9f3a 1c 760 34 +9f56 f 761 34 +9f65 41 762 34 +9fb2 7 98 68 +9fb9 1 98 68 +9fc6 1d 85 68 +9fe3 5 86 68 +9fe8 17 88 68 +9fff 1 88 68 +a00c 1d 297 34 +a029 1 297 34 +a037 e 605 34 +a045 9 606 34 +a04e 3c 609 34 +a08a b 610 34 +a095 11 609 34 +a0a6 b 612 34 +a0b1 12 614 34 +a0c3 b 615 34 +a0ce 13 612 34 +a0e1 8 615 34 +a0e9 1 615 34 +a0f7 15 751 34 +a10c 1a 752 34 +a126 b 754 34 +a131 49 755 34 +a17a 3b 756 34 +a1b5 12 754 34 +a1c7 15 758 34 +a1dc 8 759 34 +a1e4 1c 760 34 +a200 f 761 34 +a20f 41 762 34 +FUNC a250 35 0 bool __gnu_cxx::operator!=<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > > const&, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > > const&) a250 d 699 26 -a25d 28 700 26 -a285 1 700 26 -a286 c 621 26 -a292 d 623 26 -a29f 5 624 26 -FUNC a2a4 4b 0 fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node >**, std::vector<__gnu_cxx::_Hashtable_node >*, std::allocator<__gnu_cxx::_Hashtable_node >*> > >, __gnu_cxx::_Hashtable_node >*> -a2a4 c 539 26 -a2b0 8 541 26 -a2b8 2 542 26 -a2ba 12 543 26 -a2cc 21 542 26 -a2ed 2 543 26 -a2ef 1 543 26 +a25d 28 700 72 +a285 1 700 72 +a292 d 623 72 +a29f 5 624 72 +FUNC a2a4 4b 0 void std::__fill::fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) +a2a4 c 539 61 +a2b0 8 541 61 +a2b8 2 542 61 +a2ba 12 543 61 +a2cc 21 542 61 +a2ed 2 543 61 +a2ef 1 543 61 +FUNC a2f0 2b 0 void std::fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) a2f0 c 560 26 -a2fc 4 567 26 -a300 1b 568 26 -a31b 1 568 26 +a2fc 4 567 61 +a300 1b 568 61 +a31b 1 568 61 +FUNC a31c 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>) a31c c 171 26 -a328 2 173 26 -a32a 1a 174 26 -a344 21 173 26 -a365 2 174 26 -a367 1 174 26 -a368 c 65 26 -a374 2 65 26 -a376 c 103 26 -a382 d 103 26 -a38f 1 103 26 +a328 2 173 73 +a32a 1a 174 73 +a344 21 173 73 +a365 2 174 73 +a367 1 174 73 +a368 c 65 68 +a374 2 65 68 +a376 c 103 69 +a382 d 103 69 +a38f 1 103 69 +FUNC a390 2b 0 std::_List_base, std::allocator > >::get_allocator() const a390 10 322 26 -a3a0 1b 324 26 -a3bb 1 324 26 +a3a0 1b 324 66 +a3bb 1 324 66 +FUNC a3bc 7b 0 std::_List_base, std::allocator > >::_M_clear() a3bc d 69 27 -a3c9 8 72 27 -a3d1 2 73 27 -a3d3 6 75 27 -a3d9 8 76 27 -a3e1 35 77 27 -a416 12 78 27 -a428 a 73 27 -a432 5 78 27 -a437 1 78 27 -a438 c 331 27 -a444 18 332 27 -a45c c 392 27 -a468 d 392 27 -a475 1 392 27 -a476 c 211 27 -a482 10 211 27 -a492 c 107 27 -a49e d 107 27 -a4ab 1 107 27 +a3c9 8 72 77 +a3d1 2 73 77 +a3d3 6 75 77 +a3d9 8 76 77 +a3e1 35 77 77 +a416 12 78 77 +a428 a 73 77 +a432 5 78 77 +a437 1 78 77 +a438 c 331 66 +a444 18 332 66 +a45c c 392 66 +a468 d 392 66 +a475 1 392 66 +a476 c 211 74 +a482 10 211 74 +a49e d 107 68 +a4ab 1 107 68 +FUNC a4ac 2e 0 void std::_Destroy >(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, std::allocator) a4ac c 171 27 -a4b8 2 173 27 -a4ba 12 174 27 -a4cc c 173 27 -a4d8 2 174 27 -a4da c 272 27 -a4e6 4b 273 27 -a531 1 273 27 -a532 13 196 27 -a545 10 196 27 -a555 2f 197 27 -a584 1a 198 27 -a59e 13 196 27 -a5b1 10 196 27 -a5c1 2f 197 27 -a5f0 1a 198 27 -a60a c 97 27 -a616 7 98 27 -a61d 1 98 27 -a61e c 83 27 -a62a 1d 85 27 -a647 5 86 27 -a64c 10 88 27 +a4b8 2 173 73 +a4ba 12 174 73 +a4cc c 173 73 +a4d8 2 174 73 +a4da c 272 71 +a4e6 4b 273 71 +a531 1 273 71 +a532 13 196 74 +a545 10 196 74 +a555 2f 197 74 +a584 1a 198 74 +a59e 13 196 74 +a5b1 10 196 74 +a5c1 2f 197 74 +a5f0 1a 198 74 +a616 7 98 68 +a61d 1 98 68 +a62a 1d 85 68 +a647 5 86 68 +a64c 10 88 68 +FUNC a65c 2a 0 std::_Vector_base<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::_M_allocate(unsigned long) a65c c 116 27 -a668 1e 117 27 -a686 d 100 27 -a693 12 101 27 -a6a5 19 103 27 -a6be b 104 27 -a6c9 3a 105 27 -a703 1 105 27 -a704 c 97 27 -a710 7 98 27 -a717 1 98 27 -a718 c 83 27 -a724 1d 85 27 -a741 5 86 27 -a746 10 88 27 +a668 1e 117 71 +a686 d 100 71 +a693 12 101 71 +a6a5 19 103 71 +a6be b 104 71 +a6c9 3a 105 71 +a703 1 105 71 +a710 7 98 68 +a717 1 98 68 +a724 1d 85 68 +a741 5 86 68 +a746 10 88 68 +FUNC a756 2a 0 std::_Vector_base<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::_M_allocate(unsigned long) a756 c 116 27 -a762 1e 117 27 -a780 d 100 27 -a78d 12 101 27 -a79f 19 103 27 -a7b8 b 104 27 -a7c3 3a 105 27 -a7fd 1 105 27 -FUNC a7fe 60 0 copy_b<__gnu_cxx::_Hashtable_node > >*> -a7fe d 422 27 -a80b 12 424 27 -a81d 2e 425 27 -a84b 13 426 27 -a85e c 432 27 -a86a 4 440 27 -a86e 1b 443 27 -a889 1 443 27 -FUNC a88a 64 0 copy_b_n<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > > > -a88a e 480 27 -a898 56 482 27 -a8ee c 504 27 -a8fa 4 514 27 -a8fe 4 515 27 -a902 1b 517 27 -a91d 1 517 27 -FUNC a91e 32 0 fill_n<__gnu_cxx::_Hashtable_node >**, long unsigned int, __gnu_cxx::_Hashtable_node >*> -a91e c 614 27 -a92a 8 616 27 -a932 2 617 27 -a934 8 618 27 -a93c f 617 27 -a94b 5 619 27 -a950 c 636 27 -a95c 4 641 27 -a960 1b 642 27 -a97b 1 642 27 +a762 1e 117 71 +a780 d 100 71 +a78d 12 101 71 +a79f 19 103 71 +a7b8 b 104 71 +a7c3 3a 105 71 +a7fd 1 105 71 +a80b 12 424 61 +a81d 2e 425 61 +a84b 13 426 61 +a86a 4 440 61 +a86e 1b 443 61 +a889 1 443 61 +a898 56 482 61 +a8fa 4 514 61 +a8fe 4 515 61 +a902 1b 517 61 +a91d 1 517 61 +a92a 8 616 61 +a932 2 617 61 +a934 8 618 61 +a93c f 617 61 +a94b 5 619 61 +a95c 4 641 61 +a960 1b 642 61 +a97b 1 642 61 +FUNC a97c 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&, __true_type) a97c c 182 28 -a988 1b 183 28 -a9a3 1 183 28 +a988 1b 183 79 +a9a3 1 183 79 +FUNC a9a4 2f 0 void std::uninitialized_fill_n<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) a9a4 c 214 28 -a9b0 23 218 28 -a9d3 1 218 28 +a9b0 23 218 79 +a9d3 1 218 79 +FUNC a9d4 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>) a9d4 c 308 28 -a9e0 1b 310 28 -a9fb 1 310 28 -a9fc c 200 28 -aa08 19 201 28 -aa21 42 203 28 -aa63 15 205 28 -aa78 d 990 28 -aa85 11 992 28 -aa96 c 993 28 -aaa2 15 995 28 -aab7 c 996 28 -aac3 4a 998 28 -ab0d f 1001 28 -ab1c 1c 998 28 -ab38 1a 1003 28 -ab52 5 1004 28 -ab57 1f 1007 28 -ab76 1c 1008 28 -ab92 19 1009 28 -abab 19 1010 28 -abc4 1a 1011 28 -abde a 1004 28 -abe8 11 1001 28 -abf9 15 1014 28 -ac0e 13 1028 28 -ac21 b 1016 28 -ac2c 9 1018 28 -ac35 19 1023 28 -ac4e 23 1024 28 -ac71 19 1025 28 -ac8a 1d 1021 28 -aca7 1a 1018 28 -acc1 b 1028 28 -accc b 1016 28 -acd7 1e 1028 28 -acf5 1 1028 28 -acf6 10 436 28 -ad06 16 438 28 -ad1c 37 439 28 -ad53 1 439 28 -ad54 10 211 28 -ad64 37 212 28 -ad9b 1 212 28 -FUNC ad9c 32 0 fill_n<__gnu_cxx::_Hashtable_node > >**, long unsigned int, __gnu_cxx::_Hashtable_node > >*> -ad9c c 614 28 -ada8 8 616 28 -adb0 2 617 28 -adb2 8 618 28 -adba f 617 28 -adc9 5 619 28 -adce c 636 28 -adda 4 641 28 -adde 1b 642 28 -adf9 1 642 28 +a9e0 1b 310 79 +a9fb 1 310 79 +a9fc c 200 71 +aa08 19 201 71 +aa21 42 203 71 +aa63 15 205 71 +aa85 11 992 34 +aa96 c 993 34 +aaa2 15 995 34 +aab7 c 996 34 +aac3 4a 998 34 +ab0d f 1001 34 +ab1c 1c 998 34 +ab38 1a 1003 34 +ab52 5 1004 34 +ab57 1f 1007 34 +ab76 1c 1008 34 +ab92 19 1009 34 +abab 19 1010 34 +abc4 1a 1011 34 +abde a 1004 34 +abe8 11 1001 34 +abf9 15 1014 34 +ac0e 13 1028 34 +ac21 b 1016 34 +ac2c 9 1018 34 +ac35 19 1023 34 +ac4e 23 1024 34 +ac71 19 1025 34 +ac8a 1d 1021 34 +aca7 1a 1018 34 +acc1 b 1028 34 +accc b 1016 34 +acd7 1e 1028 34 +acf5 1 1028 34 +ad06 16 438 34 +ad1c 37 439 34 +ad53 1 439 34 +ad64 37 212 42 +ad9b 1 212 42 +ada8 8 616 61 +adb0 2 617 61 +adb2 8 618 61 +adba f 617 61 +adc9 5 619 61 +adda 4 641 61 +adde 1b 642 61 +adf9 1 642 61 +FUNC adfa 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >* const&, __true_type) adfa c 182 28 -ae06 1b 183 28 -ae21 1 183 28 +ae06 1b 183 79 +ae21 1 183 79 +FUNC ae22 2f 0 void std::uninitialized_fill_n<__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >* const&) ae22 c 214 28 -ae2e 23 218 28 -ae51 1 218 28 +ae2e 23 218 79 +ae51 1 218 79 +FUNC ae52 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >*, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >* const&, std::allocator<__gnu_cxx::_Hashtable_node > >*>) ae52 c 308 28 -ae5e 1b 310 28 -ae79 1 310 28 -ae7a c 200 28 -ae86 19 201 28 -ae9f 42 203 28 -aee1 15 205 28 -aef6 d 990 28 -af03 11 992 28 -af14 c 993 28 -af20 15 995 28 -af35 c 996 28 -af41 4a 998 28 -af8b f 1001 28 -af9a 1c 998 28 -afb6 1a 1003 28 -afd0 5 1004 28 -afd5 1f 1007 28 -aff4 1c 1008 28 -b010 19 1009 28 -b029 19 1010 28 -b042 1a 1011 28 -b05c a 1004 28 -b066 11 1001 28 -b077 15 1014 28 -b08c 13 1028 28 -b09f b 1016 28 -b0aa 9 1018 28 -b0b3 19 1023 28 -b0cc 23 1024 28 -b0ef 19 1025 28 -b108 1d 1021 28 -b125 1a 1018 28 -b13f b 1028 28 -b14a b 1016 28 -b155 1e 1028 28 -b173 1 1028 28 -b174 10 436 28 -b184 16 438 28 -b19a 37 439 28 -b1d1 1 439 28 -b1d2 10 211 28 -b1e2 37 212 28 -b219 1 212 28 -FUNC b21a 60 0 copy_b<__gnu_cxx::_Hashtable_node >*> -b21a d 422 28 -b227 12 424 28 -b239 2e 425 28 -b267 13 426 28 -b27a c 432 28 -b286 4 440 28 -b28a 1b 443 28 -b2a5 1 443 28 -FUNC b2a6 64 0 copy_b_n<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node >**, std::vector<__gnu_cxx::_Hashtable_node >*, std::allocator<__gnu_cxx::_Hashtable_node >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node >**, std::vector<__gnu_cxx::_Hashtable_node >*, std::allocator<__gnu_cxx::_Hashtable_node >*> > > > -b2a6 e 480 28 -b2b4 56 482 28 -b30a c 504 28 -b316 4 514 28 -b31a 4 515 28 -b31e 1b 517 28 -b339 1 517 28 -FUNC b33a 43 0 fill_n<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, long unsigned int, __gnu_cxx::_Hashtable_node > >*> -b33a c 614 28 -b346 8 616 28 -b34e 2 617 28 -b350 12 618 28 -b362 16 617 28 -b378 5 619 28 -b37d 1 619 28 -b37e c 636 28 -b38a 4 641 28 -b38e 1b 642 28 -b3a9 1 642 28 +ae5e 1b 310 79 +ae79 1 310 79 +ae7a c 200 71 +ae86 19 201 71 +ae9f 42 203 71 +aee1 15 205 71 +af03 11 992 34 +af14 c 993 34 +af20 15 995 34 +af35 c 996 34 +af41 4a 998 34 +af8b f 1001 34 +af9a 1c 998 34 +afb6 1a 1003 34 +afd0 5 1004 34 +afd5 1f 1007 34 +aff4 1c 1008 34 +b010 19 1009 34 +b029 19 1010 34 +b042 1a 1011 34 +b05c a 1004 34 +b066 11 1001 34 +b077 15 1014 34 +b08c 13 1028 34 +b09f b 1016 34 +b0aa 9 1018 34 +b0b3 19 1023 34 +b0cc 23 1024 34 +b0ef 19 1025 34 +b108 1d 1021 34 +b125 1a 1018 34 +b13f b 1028 34 +b14a b 1016 34 +b155 1e 1028 34 +b173 1 1028 34 +b184 16 438 34 +b19a 37 439 34 +b1d1 1 439 34 +b1e2 37 212 42 +b219 1 212 42 +b227 12 424 61 +b239 2e 425 61 +b267 13 426 61 +b286 4 440 61 +b28a 1b 443 61 +b2a5 1 443 61 +b2b4 56 482 61 +b316 4 514 61 +b31a 4 515 61 +b31e 1b 517 61 +b339 1 517 61 +b346 8 616 61 +b34e 2 617 61 +b350 12 618 61 +b362 16 617 61 +b378 5 619 61 +b37d 1 619 61 +b38a 4 641 61 +b38e 1b 642 61 +b3a9 1 642 61 +FUNC b3aa 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >* const&, __true_type) b3aa c 182 28 -b3b6 1b 183 28 -b3d1 1 183 28 +b3b6 1b 183 79 +b3d1 1 183 79 +FUNC b3d2 2f 0 void std::uninitialized_fill_n<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >* const&) b3d2 c 214 28 -b3de 23 218 28 -b401 1 218 28 +b3de 23 218 79 +b401 1 218 79 +FUNC b402 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >*, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >* const&, std::allocator<__gnu_cxx::_Hashtable_node > >*>) b402 c 308 28 -b40e 1b 310 28 -b429 1 310 28 -FUNC b42a 43 0 fill_n<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node >**, std::vector<__gnu_cxx::_Hashtable_node >*, std::allocator<__gnu_cxx::_Hashtable_node >*> > >, long unsigned int, __gnu_cxx::_Hashtable_node >*> -b42a c 614 28 -b436 8 616 28 -b43e 2 617 28 -b440 12 618 28 -b452 16 617 28 -b468 5 619 28 -b46d 1 619 28 -b46e c 636 28 -b47a 4 641 28 -b47e 1b 642 28 -b499 1 642 28 +b40e 1b 310 79 +b429 1 310 79 +b436 8 616 61 +b43e 2 617 61 +b440 12 618 61 +b452 16 617 61 +b468 5 619 61 +b46d 1 619 61 +b47a 4 641 61 +b47e 1b 642 61 +b499 1 642 61 +FUNC b49a 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&, __true_type) b49a c 182 28 -b4a6 1b 183 28 -b4c1 1 183 28 +b4a6 1b 183 79 +b4c1 1 183 79 +FUNC b4c2 2f 0 void std::uninitialized_fill_n<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) b4c2 c 214 28 -b4ce 23 218 28 -b4f1 1 218 28 +b4ce 23 218 79 +b4f1 1 218 79 +FUNC b4f2 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>) b4f2 c 308 28 -b4fe 1b 310 28 -b519 1 310 28 -FUNC b51a 3f 0 copy<__gnu_cxx::_Hashtable_node > >*> -b51a c 298 28 -b526 22 300 28 -b548 11 301 28 -b559 1 301 28 -b55a c 307 28 -b566 4 315 28 -b56a 1b 317 28 -b585 1 317 28 -FUNC b586 27 0 copy_n<__gnu_cxx::_Hashtable_node > >**, __gnu_cxx::_Hashtable_node > >**> -b586 c 325 28 -b592 1b 326 28 -b5ad 1 326 28 -b5ae c 376 28 -b5ba 4 384 28 -b5be 4 385 28 -b5c2 1b 387 28 -b5dd 1 387 28 -b5de c 73 28 -b5ea 1b 74 28 -b605 1 74 28 -b606 c 108 28 -b612 23 113 28 -b635 1 113 28 -b636 c 252 28 -b642 1b 254 28 -b65d 1 254 28 -FUNC b65e 66 0 _M_allocate_and_copy<__gnu_cxx::_Hashtable_node > >**> -b65e c 761 28 -b66a 15 763 28 -b67f 40 766 28 -b6bf 3 768 28 -b6c2 2 773 28 +b4fe 1b 310 79 +b519 1 310 79 +b526 22 300 61 +b548 11 301 61 +b559 1 301 61 +b566 4 315 61 +b56a 1b 317 61 +b585 1 317 61 +b592 1b 326 61 +b5ad 1 326 61 +b5ba 4 384 61 +b5be 4 385 61 +b5c2 1b 387 61 +b5dd 1 387 61 +b5ea 1b 74 79 +b605 1 74 79 +b612 23 113 79 +b635 1 113 79 +b642 1b 254 79 +b65d 1 254 79 +b66a 15 763 71 +b67f 40 766 71 +b6bf 3 768 71 +b6c2 2 773 71 +FUNC b6c4 124 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::reserve(unsigned long) b6c4 13 69 29 -b6d7 15 71 29 -b6ec e 72 29 -b6fa 19 73 29 -b713 e 75 29 -b721 28 78 29 -b749 3e 79 29 -b787 30 81 29 -b7b7 8 84 29 -b7bf 11 85 29 -b7d0 18 86 29 -FUNC b7e8 40 0 copy_n<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::_Hashtable_node > >**> -b7e8 d 334 29 -b7f5 33 335 29 -b828 c 376 29 -b834 4 384 29 -b838 4 385 29 -b83c 1b 387 29 -b857 1 387 29 -b858 c 73 29 -b864 1b 74 29 -b87f 1 74 29 -b880 c 108 29 -b88c 23 113 29 -b8af 1 113 29 -b8b0 c 252 29 -b8bc 1b 254 29 -b8d7 1 254 29 -FUNC b8d8 64 0 copy_n<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > > > -b8d8 e 352 29 -b8e6 56 354 29 -b93c c 376 29 -b948 4 384 29 -b94c 4 385 29 -b950 1b 387 29 -b96b 1 387 29 -b96c c 73 29 -b978 1b 74 29 -b993 1 74 29 -b994 c 108 29 -b9a0 23 113 29 -b9c3 1 113 29 -b9c4 c 252 29 -b9d0 1b 254 29 -b9eb 1 254 29 +b6d7 15 71 78 +b6ec e 72 78 +b6fa 19 73 78 +b713 e 75 78 +b721 28 78 78 +b749 3e 79 78 +b787 30 81 78 +b7b7 8 84 78 +b7bf 11 85 78 +b7d0 18 86 78 +b7f5 33 335 61 +b834 4 384 61 +b838 4 385 61 +b83c 1b 387 61 +b857 1 387 61 +b864 1b 74 79 +b87f 1 74 79 +b88c 23 113 79 +b8af 1 113 79 +b8bc 1b 254 79 +b8d7 1 254 79 +b8e6 56 354 61 +b948 4 384 61 +b94c 4 385 61 +b950 1b 387 61 +b96b 1 387 61 +b978 1b 74 79 +b993 1 74 79 +b9a0 23 113 79 +b9c3 1 113 79 +b9d0 1b 254 79 +b9eb 1 254 79 +FUNC b9ec 46e 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::_M_fill_insert(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >* const&) b9ec 14 311 29 -ba00 b 313 29 -ba0b 24 315 29 -ba2f 8 318 29 -ba37 23 319 29 -ba5a 15 320 29 -ba6f c 321 29 -ba7b 51 323 29 -bacc 14 327 29 -bae0 30 328 29 -bb10 35 330 29 -bb45 48 334 29 -bb8d 17 338 29 -bba4 43 339 29 -bbe7 14 342 29 -bbfb 1e 343 29 -bc19 e 348 29 -bc27 1e 349 29 -bc45 e 350 29 -bc53 1d 353 29 -bc70 8 354 29 -bc78 e 355 29 -bc86 27 357 29 -bcad 6 358 29 -bcb3 4d 361 29 -bd00 40 365 29 -bd40 18 367 29 -bd58 4d 368 29 -bda5 3e 379 29 -bde3 30 381 29 -be13 12 384 29 -be25 13 385 29 -be38 22 386 29 +ba00 b 313 78 +ba0b 24 315 78 +ba2f 8 318 78 +ba37 23 319 78 +ba5a 15 320 78 +ba6f c 321 78 +ba7b 51 323 78 +bacc 14 327 78 +bae0 30 328 78 +bb10 35 330 78 +bb45 48 334 78 +bb8d 17 338 78 +bba4 43 339 78 +bbe7 14 342 78 +bbfb 1e 343 78 +bc19 e 348 78 +bc27 1e 349 78 +bc45 e 350 78 +bc53 1d 353 78 +bc70 8 354 78 +bc78 e 355 78 +bc86 27 357 78 +bcad 6 358 78 +bcb3 4d 361 78 +bd00 40 365 78 +bd40 18 367 78 +bd58 4d 368 78 +bda5 3e 379 78 +bde3 30 381 78 +be13 12 384 78 +be25 13 385 78 +be38 22 386 78 +FUNC be5a 2e 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::insert(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >* const&) be5a c 657 29 -be66 22 658 29 -be88 c 578 29 -be94 15 580 29 -bea9 15 581 29 -bebe 37 582 29 -bef5 c 583 29 -bf01 1 583 29 -bf02 d 335 29 -bf0f 4e 337 29 -bf5d 4d 338 29 -bfaa d 134 29 -bfb7 65 135 29 -FUNC c01c 3f 0 copy<__gnu_cxx::_Hashtable_node >*> -c01c c 298 29 -c028 22 300 29 -c04a 11 301 29 -c05b 1 301 29 -c05c c 307 29 -c068 4 315 29 -c06c 1b 317 29 -c087 1 317 29 -FUNC c088 27 0 copy_n<__gnu_cxx::_Hashtable_node >**, __gnu_cxx::_Hashtable_node >**> -c088 c 325 29 -c094 1b 326 29 -c0af 1 326 29 -c0b0 c 376 29 -c0bc 4 384 29 -c0c0 4 385 29 -c0c4 1b 387 29 -c0df 1 387 29 -c0e0 c 73 29 -c0ec 1b 74 29 -c107 1 74 29 -c108 c 108 29 -c114 23 113 29 -c137 1 113 29 -c138 c 252 29 -c144 1b 254 29 -c15f 1 254 29 -FUNC c160 66 0 _M_allocate_and_copy<__gnu_cxx::_Hashtable_node >**> -c160 c 761 29 -c16c 15 763 29 -c181 40 766 29 -c1c1 3 768 29 -c1c4 2 773 29 +be66 22 658 71 +be94 15 580 34 +bea9 15 581 34 +bebe 37 582 34 +bef5 c 583 34 +bf01 1 583 34 +bf02 d 335 34 +bf0f 4e 337 34 +bf5d 4d 338 34 +bfaa d 134 42 +bfb7 65 135 42 +c028 22 300 61 +c04a 11 301 61 +c05b 1 301 61 +c068 4 315 61 +c06c 1b 317 61 +c087 1 317 61 +c094 1b 326 61 +c0af 1 326 61 +c0bc 4 384 61 +c0c0 4 385 61 +c0c4 1b 387 61 +c0df 1 387 61 +c0ec 1b 74 79 +c107 1 74 79 +c114 23 113 79 +c137 1 113 79 +c144 1b 254 79 +c15f 1 254 79 +c16c 15 763 71 +c181 40 766 71 +c1c1 3 768 71 +c1c4 2 773 71 +FUNC c1c6 124 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::reserve(unsigned long) c1c6 13 69 29 -c1d9 15 71 29 -c1ee e 72 29 -c1fc 19 73 29 -c215 e 75 29 -c223 28 78 29 -c24b 3e 79 29 -c289 30 81 29 -c2b9 8 84 29 -c2c1 11 85 29 -c2d2 18 86 29 -FUNC c2ea 40 0 copy_n<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node >**, std::vector<__gnu_cxx::_Hashtable_node >*, std::allocator<__gnu_cxx::_Hashtable_node >*> > >, __gnu_cxx::_Hashtable_node >**> -c2ea d 334 29 -c2f7 33 335 29 -c32a c 376 29 -c336 4 384 29 -c33a 4 385 29 -c33e 1b 387 29 -c359 1 387 29 -c35a c 73 29 -c366 1b 74 29 -c381 1 74 29 -c382 c 108 29 -c38e 23 113 29 -c3b1 1 113 29 -c3b2 c 252 29 -c3be 1b 254 29 -c3d9 1 254 29 -FUNC c3da 64 0 copy_n<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node >**, std::vector<__gnu_cxx::_Hashtable_node >*, std::allocator<__gnu_cxx::_Hashtable_node >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node >**, std::vector<__gnu_cxx::_Hashtable_node >*, std::allocator<__gnu_cxx::_Hashtable_node >*> > > > -c3da e 352 29 -c3e8 56 354 29 -c43e c 376 29 -c44a 4 384 29 -c44e 4 385 29 -c452 1b 387 29 -c46d 1 387 29 -c46e c 73 29 -c47a 1b 74 29 -c495 1 74 29 -c496 c 108 29 -c4a2 23 113 29 -c4c5 1 113 29 -c4c6 c 252 29 -c4d2 1b 254 29 -c4ed 1 254 29 +c1d9 15 71 78 +c1ee e 72 78 +c1fc 19 73 78 +c215 e 75 78 +c223 28 78 78 +c24b 3e 79 78 +c289 30 81 78 +c2b9 8 84 78 +c2c1 11 85 78 +c2d2 18 86 78 +c2f7 33 335 61 +c336 4 384 61 +c33a 4 385 61 +c33e 1b 387 61 +c359 1 387 61 +c366 1b 74 79 +c381 1 74 79 +c38e 23 113 79 +c3b1 1 113 79 +c3be 1b 254 79 +c3d9 1 254 79 +c3e8 56 354 61 +c44a 4 384 61 +c44e 4 385 61 +c452 1b 387 61 +c46d 1 387 61 +c47a 1b 74 79 +c495 1 74 79 +c4a2 23 113 79 +c4c5 1 113 79 +c4d2 1b 254 79 +c4ed 1 254 79 +FUNC c4ee 46e 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::_M_fill_insert(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) c4ee 14 311 29 -c502 b 313 29 -c50d 24 315 29 -c531 8 318 29 -c539 23 319 29 -c55c 15 320 29 -c571 c 321 29 -c57d 51 323 29 -c5ce 14 327 29 -c5e2 30 328 29 -c612 35 330 29 -c647 48 334 29 -c68f 17 338 29 -c6a6 43 339 29 -c6e9 14 342 29 -c6fd 1e 343 29 -c71b e 348 29 -c729 1e 349 29 -c747 e 350 29 -c755 1d 353 29 -c772 8 354 29 -c77a e 355 29 -c788 27 357 29 -c7af 6 358 29 -c7b5 4d 361 29 -c802 40 365 29 -c842 18 367 29 -c85a 4d 368 29 -c8a7 3e 379 29 -c8e5 30 381 29 -c915 12 384 29 -c927 13 385 29 -c93a 22 386 29 +c502 b 313 78 +c50d 24 315 78 +c531 8 318 78 +c539 23 319 78 +c55c 15 320 78 +c571 c 321 78 +c57d 51 323 78 +c5ce 14 327 78 +c5e2 30 328 78 +c612 35 330 78 +c647 48 334 78 +c68f 17 338 78 +c6a6 43 339 78 +c6e9 14 342 78 +c6fd 1e 343 78 +c71b e 348 78 +c729 1e 349 78 +c747 e 350 78 +c755 1d 353 78 +c772 8 354 78 +c77a e 355 78 +c788 27 357 78 +c7af 6 358 78 +c7b5 4d 361 78 +c802 40 365 78 +c842 18 367 78 +c85a 4d 368 78 +c8a7 3e 379 78 +c8e5 30 381 78 +c915 12 384 78 +c927 13 385 78 +c93a 22 386 78 +FUNC c95c 2e 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::insert(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) c95c c 657 29 -c968 22 658 29 -c98a c 578 29 -c996 15 580 29 -c9ab 15 581 29 -c9c0 37 582 29 -c9f7 c 583 29 -ca03 1 583 29 -ca04 d 335 29 -ca11 4e 337 29 -ca5f 4d 338 29 -caac d 134 29 -cab9 65 135 29 -cb1e 39 135 30 +c968 22 658 71 +c996 15 580 34 +c9ab 15 581 34 +c9c0 37 582 34 +c9f7 c 583 34 +ca03 1 583 34 +ca04 d 335 34 +ca11 4e 337 34 +ca5f 4d 338 34 +caac d 134 42 +cab9 65 135 42 +FUNC cb1e 44 0 dwarf2reader::CUFunctionInfoHandler::StartCompilationUnit(unsigned long long, unsigned char, unsigned char, unsigned long long, unsigned char) +cb1e 39 135 42 cb57 5 102 30 cb5c 6 103 30 +FUNC cb62 41 0 dwarf2reader::CUFunctionInfoHandler::ProcessAttributeString(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, std::string const&) cb62 18 136 30 cb7a 10 137 30 cb8a 17 138 30 cba1 2 139 30 cba3 1 139 30 +FUNC cba4 2a5 0 dwarf2reader::CUFunctionInfoHandler::ProcessAttributeUnsigned(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, unsigned long long) cba4 2d 144 30 cbd1 a 145 30 cbdb 58 146 30 @@ -2391,6 +2355,7 @@ ce15 2c 166 30 ce41 8 172 30 ce49 1 172 30 +FUNC ce4a 19c 0 dwarf2reader::CULineInfoHandler::AddLine(unsigned long long, unsigned int, unsigned int, unsigned int) ce4a 20 84 30 ce6a 1c 85 30 ce86 9c 87 30 @@ -2399,11 +2364,13 @@ cf8a 25 90 30 cfaf 30 93 30 cfdf 7 95 30 +FUNC cfe6 9f 0 dwarf2reader::CUFunctionInfoHandler::EndDIE(unsigned long long) cfe6 19 174 30 cfff 1c 175 30 d01b 65 177 30 d080 5 178 30 d085 1 178 30 +FUNC d086 164 0 dwarf2reader::CUFunctionInfoHandler::StartDIE(unsigned long long, dwarf2reader::DwarfTag, std::list, std::allocator > > const&) d086 20 111 30 d0a6 1c 112 30 d0c2 c 126 30 @@ -2417,11 +2384,13 @@ d1c4 1c 115 30 d1e0 3 126 30 d1e3 7 129 30 +FUNC d1ea 73 0 dwarf2reader::CULineInfoHandler::DefineDir(std::string const&, unsigned int) d1ea 13 52 30 d1fd 45 54 30 d242 15 55 30 d257 6 56 30 d25d 1 56 30 +FUNC d25e 23b 0 dwarf2reader::CULineInfoHandler::DefineFile(std::string const&, int, unsigned int, unsigned long long, unsigned long long) d25e 2c 60 30 d28a 45 62 30 d2cf 2f 65 30 @@ -2457,8 +2426,8 @@ d7db e 48 30 d7e9 3d 49 30 d826 20 50 30 -d846 12 125 31 -d858 12 125 31 +d846 12 125 74 +d858 12 125 74 d86a 13 55 32 d87d 35 55 32 d8b2 13 98 32 @@ -2469,526 +2438,554 @@ d914 d 22 32 d921 40 22 32 d961 1 22 32 -d962 c 89 33 -d96e 1e 90 33 -d98c c 207 34 +d962 c 89 70 +d96e 1e 90 70 d998 14 208 34 -d9ac c 190 35 -d9b8 a 190 35 -d9c2 c 259 35 -d9ce 21 259 35 -d9ef 1 259 35 +d9ac c 190 67 +d9b8 a 190 67 +d9c2 c 259 67 +d9ce 21 259 67 +d9ef 1 259 67 +FUNC d9f0 13 0 std::auto_ptr::operator->() const d9f0 c 283 35 -d9fc 7 286 35 -da03 1 286 35 -da04 d 103 36 -da11 5c 104 36 -da6d 1 104 36 +d9fc 7 286 67 +da03 1 286 67 +da11 5c 104 68 +da6d 1 104 68 +FUNC da6e 28 0 bool std::operator==, std::allocator >(std::basic_string, std::allocator > const&, char const*) da6e c 2139 37 da7a 1c 2140 37 +FUNC da96 5d 0 std::basic_string, std::allocator > std::operator+, std::allocator >(std::basic_string, std::allocator > const&, char const*) da96 d 2081 37 daa3 12 2083 37 dab5 1a 2084 37 dacf 24 2085 37 daf3 1 2085 37 +FUNC daf4 5d 0 std::basic_string, std::allocator > std::operator+, std::allocator >(std::basic_string, std::allocator > const&, std::basic_string, std::allocator > const&) daf4 d 2044 37 db01 12 2046 37 db13 1a 2047 37 db2d 24 2048 37 db51 1 2048 37 -db52 c 84 37 -db5e 17 85 37 -db75 1 85 37 +db52 c 84 70 +db5e 17 85 70 +db75 1 85 70 +FUNC db76 2d 0 std::pair std::make_pair(char const*, unsigned int) db76 c 144 37 -db82 21 145 37 -dba3 1 145 37 -dba4 c 84 37 -dbb0 23 85 37 -dbd3 1 85 37 +db82 21 145 70 +dba3 1 145 70 +dba4 c 84 70 +dbb0 23 85 70 +dbd3 1 85 70 +FUNC dbd4 3c 0 std::pair > std::make_pair >(unsigned long long, std::pair) dbd4 1c 144 37 -dbf0 20 145 37 -dc10 d 89 37 -dc1d 64 90 37 -dc81 1 90 37 -dc82 c 89 37 -dc8e 2a 90 37 -dcb8 c 84 37 -dcc4 1d 85 37 -dce1 1 85 37 +dbf0 20 145 70 +dc10 d 89 70 +dc1d 64 90 70 +dc81 1 90 70 +dc82 c 89 70 +dc8e 2a 90 70 +dcb8 c 84 70 +dcc4 1d 85 70 +dce1 1 85 70 +FUNC dce2 3c 0 std::pair std::make_pair(unsigned long long, dwarf2reader::FunctionInfo*) dce2 1c 144 37 -dcfe 20 145 37 -dd1e c 189 37 -dd2a a 190 37 -dd34 c 193 37 -dd40 d 194 37 -dd4d 1 194 37 -dd4e c 603 38 -dd5a c 603 38 +dcfe 20 145 70 +dd2a a 190 34 +dd40 d 194 34 +dd4d 1 194 34 +dd4e c 603 72 +dd5a c 603 72 +FUNC dd66 2b 0 std::vector >::begin() const dd66 c 342 39 -dd72 1f 343 39 -dd91 1 343 39 +dd72 1f 343 71 +dd91 1 343 71 +FUNC dd92 2c 0 std::vector >::end() const dd92 c 360 39 -dd9e 20 361 39 -ddbe c 665 39 -ddca 5 666 39 -ddcf 1 666 39 -ddd0 d 758 39 -dddd 2b 759 39 +dd9e 20 361 71 +ddca 5 666 72 +ddcf 1 666 72 +dddd 2b 759 72 +FUNC de08 3c 0 std::vector >::size() const de08 c 402 39 -de14 30 403 39 +de14 30 403 71 +FUNC de44 2b 0 std::vector >::begin() const de44 c 342 39 -de50 1f 343 39 -de6f 1 343 39 +de50 1f 343 71 +de6f 1 343 71 +FUNC de70 2c 0 std::vector >::end() const de70 c 360 39 -de7c 20 361 39 -de9c d 758 39 -dea9 31 759 39 +de7c 20 361 71 +dea9 31 759 72 +FUNC deda 3c 0 std::vector >::size() const deda c 402 39 -dee6 30 403 39 -df16 c 603 39 -df22 c 603 39 +dee6 30 403 71 +df16 c 603 72 +df22 c 603 72 +FUNC df2e 26 0 std::vector >::end() df2e c 351 39 -df3a 1a 352 39 -df54 c 613 39 -df60 7 614 39 -df67 1 614 39 +df3a 1a 352 71 +df60 7 614 72 +df67 1 614 72 +FUNC df68 13 0 std::vector >::max_size() const df68 c 407 39 -df74 7 408 39 -df7b 1 408 39 -df7c c 665 39 -df88 5 666 39 -df8d 1 666 39 -df8e c 621 39 -df9a d 623 39 -dfa7 5 624 39 +df74 7 408 71 +df7b 1 408 71 +df88 5 666 72 +df8d 1 666 72 +df9a d 623 72 +dfa7 5 624 72 +FUNC dfac 23 0 std::vector >::begin() dfac c 333 39 -dfb8 17 334 39 -dfcf 1 334 39 -dfd0 c 35 39 -dfdc 26 35 39 -e002 d 103 39 -e00f 5c 104 39 -e06b 1 104 39 -e06c c 613 39 -e078 7 614 39 -e07f 1 614 39 -FUNC e080 35 0 operator= -e080 c 0 39 -e08c 29 35 39 -e0b5 1 35 39 +dfb8 17 334 71 +dfcf 1 334 71 +dfd0 c 35 32 +dfdc 26 35 32 +e00f 5c 104 68 +e06b 1 104 68 +e078 7 614 72 +e07f 1 614 72 +FUNC e080 35 0 dwarf2reader::SourceFileInfo::operator=(dwarf2reader::SourceFileInfo const&) +e080 c 35 39 +e08c 29 35 32 +e0b5 1 35 32 +FUNC e0b6 13 0 std::vector >::max_size() const e0b6 c 407 39 -e0c2 7 408 39 -e0c9 1 408 39 -e0ca c 621 39 -e0d6 d 623 39 -e0e3 5 624 39 +e0c2 7 408 71 +e0c9 1 408 71 +e0d6 d 623 72 +e0e3 5 624 72 +FUNC e0e8 3c 0 std::vector >::_M_range_check(unsigned long) const e0e8 13 515 39 -e0fb 15 517 39 -e110 14 518 39 +e0fb 15 517 71 +e110 14 518 71 +FUNC e124 3c 0 std::vector >::_M_range_check(unsigned long) const e124 13 515 39 -e137 15 517 39 -e14c 14 518 39 -e160 c 653 39 -e16c 2a 654 39 +e137 15 517 71 +e14c 14 518 71 +e16c 2a 654 72 +FUNC e196 42 0 std::vector >::operator[](unsigned long) e196 c 494 39 -e1a2 36 495 39 +e1a2 36 495 71 +FUNC e1d8 32 0 std::vector >::at(unsigned long) e1d8 c 534 39 -e1e4 12 536 39 -e1f6 14 537 39 -e20a c 653 39 -e216 32 654 39 +e1e4 12 536 71 +e1f6 14 537 71 +e216 32 654 72 +FUNC e248 42 0 std::vector >::operator[](unsigned long) e248 c 494 39 -e254 36 495 39 +e254 36 495 71 +FUNC e28a 32 0 std::vector >::at(unsigned long) e28a c 534 39 -e296 12 536 39 -e2a8 14 537 39 +e296 12 536 71 +e2a8 14 537 71 +FUNC e2bc 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_end() e2bc c 472 40 e2c8 8 473 40 +FUNC e2d0 11 0 std::_Select1st > >::operator()(std::pair > const&) const e2d0 c 550 41 e2dc 5 551 41 e2e1 1 551 41 +FUNC e2e2 53 0 std::less::operator()(unsigned long long const&, unsigned long long const&) const e2e2 c 226 41 e2ee 47 227 41 e335 1 227 41 +FUNC e336 20 0 std::_Rb_tree_iterator > >::operator==(std::_Rb_tree_iterator > > const&) const e336 c 209 41 -e342 14 210 41 -e356 c 84 41 -e362 18 85 41 +e342 14 210 40 +e356 c 84 70 +e362 18 85 70 +FUNC e37a 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_end() e37a c 472 41 -e386 8 473 41 +e386 8 473 40 +FUNC e38e 11 0 std::_Select1st >::operator()(std::pair const&) const e38e c 550 41 e39a 5 551 41 e39f 1 551 41 +FUNC e3a0 20 0 std::_Rb_tree_iterator >::operator==(std::_Rb_tree_iterator > const&) const e3a0 c 209 41 -e3ac 14 210 41 -e3c0 c 84 41 -e3cc 18 85 41 -e3e4 c 180 41 -e3f0 13 181 41 -e403 1 181 41 -e404 c 408 41 -e410 22 409 41 -e432 c 206 42 +e3ac 14 210 40 +e3c0 c 84 70 +e3cc 18 85 70 +e3e4 c 180 34 +e3f0 13 181 34 +e403 1 181 34 +e410 22 409 34 e43e d 207 42 e44b 1 207 42 +FUNC e44c 35 0 bool __gnu_cxx::operator!= > >(__gnu_cxx::__normal_iterator > > const&, __gnu_cxx::__normal_iterator > > const&) e44c d 699 42 -e459 28 700 42 -e481 1 700 42 +e459 28 700 72 +e481 1 700 72 +FUNC e482 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator > >, std::allocator >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, std::allocator) e482 c 171 43 -e48e 2 173 43 -e490 1a 174 43 -e4aa 21 173 43 -e4cb 2 174 43 -e4cd 1 174 43 +e48e 2 173 73 +e490 1a 174 73 +e4aa 21 173 73 +e4cb 2 174 73 +e4cd 1 174 73 +FUNC e4ce 35 0 bool __gnu_cxx::operator!= > >(__gnu_cxx::__normal_iterator > > const&, __gnu_cxx::__normal_iterator > > const&) e4ce d 699 43 -e4db 28 700 43 -e503 1 700 43 +e4db 28 700 72 +e503 1 700 72 +FUNC e504 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator > >, std::allocator >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, std::allocator) e504 c 171 43 -e510 2 173 43 -e512 1a 174 43 -e52c 21 173 43 -e54d 2 174 43 -e54f 1 174 43 +e510 2 173 73 +e512 1a 174 73 +e52c 21 173 73 +e54d 2 174 73 +e54f 1 174 73 +FUNC e550 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_value(std::_Rb_tree_node > > const*) e550 c 480 43 -e55c 8 481 43 +e55c 8 481 40 +FUNC e564 28 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_key(std::_Rb_tree_node > > const*) e564 c 484 43 -e570 1c 485 43 +e570 1c 485 40 +FUNC e58c 25 0 std::_Rb_tree_iterator >::operator--() e58c c 194 43 -e598 14 196 43 -e5ac 5 197 43 -e5b1 1 197 43 +e598 14 196 40 +e5ac 5 197 40 +e5b1 1 197 40 +FUNC e5b2 25 0 std::_Rb_tree_iterator > >::operator--() e5b2 c 194 43 -e5be 14 196 43 -e5d2 5 197 43 -e5d7 1 197 43 +e5be 14 196 40 +e5d2 5 197 40 +e5d7 1 197 40 +FUNC e5d8 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_value(std::_Rb_tree_node_base const*) e5d8 c 504 43 -e5e4 8 505 43 +e5e4 8 505 40 +FUNC e5ec 28 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_key(std::_Rb_tree_node_base const*) e5ec c 508 43 -e5f8 1c 509 43 +e5f8 1c 509 40 +FUNC e614 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_value(std::_Rb_tree_node > const*) e614 c 480 43 -e620 8 481 43 +e620 8 481 40 +FUNC e628 28 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_key(std::_Rb_tree_node > const*) e628 c 484 43 -e634 1c 485 43 +e634 1c 485 40 +FUNC e650 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_value(std::_Rb_tree_node_base const*) e650 c 504 43 -e65c 8 505 43 +e65c 8 505 40 +FUNC e664 28 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_key(std::_Rb_tree_node_base const*) e664 c 508 43 -e670 1c 509 43 -e68c c 613 43 -e698 7 614 43 -e69f 1 614 43 -e6a0 c 97 43 -e6ac 7 98 43 -e6b3 1 98 43 -e6b4 c 83 43 -e6c0 1d 85 43 -e6dd 5 86 43 -e6e2 10 88 43 +e670 1c 509 40 +e698 7 614 72 +e69f 1 614 72 +e6ac 7 98 68 +e6b3 1 98 68 +e6c0 1d 85 68 +e6dd 5 86 68 +e6e2 10 88 68 +FUNC e6f2 2a 0 std::_Vector_base >::_M_allocate(unsigned long) e6f2 c 116 43 -e6fe 1e 117 43 -e71c c 97 43 -e728 7 98 43 -e72f 1 98 43 -e730 c 83 43 -e73c 1d 85 43 -e759 5 86 43 -e75e 16 88 43 +e6fe 1e 117 71 +e728 7 98 68 +e72f 1 98 68 +e73c 1d 85 68 +e759 5 86 68 +e75e 16 88 68 +FUNC e774 2a 0 std::_Vector_base >::_M_allocate(unsigned long) e774 c 116 43 -e780 1e 117 43 -e79e c 103 43 -e7aa 3a 104 43 -e7e4 c 653 43 -e7f0 2a 654 43 +e780 1e 117 71 +e7aa 3a 104 68 +e7f0 2a 654 72 +FUNC e81a 42 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::operator[](unsigned long) const e81a c 509 43 -e826 36 510 43 -FUNC e85c 4e 0 copy_b -e85c c 408 44 -e868 14 411 44 -e87c 1e 412 44 -e89a b 411 44 -e8a5 5 413 44 +e826 36 510 71 +FUNC e85c 4e 0 std::string* std::__copy_backward::copy_b(std::string*, std::string*, std::string*) +e85c c 408 61 +e868 14 411 61 +e87c 1e 412 61 +e89a b 411 61 +e8a5 5 413 61 +FUNC e8aa 2b 0 std::string* std::__copy_backward_aux(std::string*, std::string*, std::string*) e8aa c 432 44 -e8b6 4 440 44 -e8ba 1b 443 44 -e8d5 1 443 44 -FUNC e8d6 64 0 copy_b_n<__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > > > -e8d6 e 480 44 -e8e4 56 482 44 -e93a c 504 44 -e946 4 514 44 -e94a 4 515 44 -e94e 1b 517 44 -e969 1 517 44 +e8b6 4 440 61 +e8ba 1b 443 61 +e8d5 1 443 61 +e8e4 56 482 61 +e946 4 514 61 +e94a 4 515 61 +e94e 1b 517 61 +e969 1 517 61 +FUNC e96a 69 0 void std::_Construct(std::string*, std::string const&) e96a d 77 44 -e977 5c 81 44 -e9d3 1 81 44 -FUNC e9d4 54 0 copy_b -e9d4 c 408 44 -e9e0 1a 411 44 -e9fa 1e 412 44 -ea18 b 411 44 -ea23 5 413 44 +e977 5c 81 73 +e9d3 1 81 73 +FUNC e9d4 54 0 dwarf2reader::SourceFileInfo* std::__copy_backward::copy_b(dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*) +e9d4 c 408 61 +e9e0 1a 411 61 +e9fa 1e 412 61 +ea18 b 411 61 +ea23 5 413 61 +FUNC ea28 2b 0 dwarf2reader::SourceFileInfo* std::__copy_backward_aux(dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*) ea28 c 432 44 -ea34 4 440 44 -ea38 1b 443 44 -ea53 1 443 44 -FUNC ea54 64 0 copy_b_n<__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > > > -ea54 e 480 44 -ea62 56 482 44 -eab8 c 504 44 -eac4 4 514 44 -eac8 4 515 44 -eacc 1b 517 44 -eae7 1 517 44 +ea34 4 440 61 +ea38 1b 443 61 +ea53 1 443 61 +ea62 56 482 61 +eac4 4 514 61 +eac8 4 515 61 +eacc 1b 517 61 +eae7 1 517 61 +FUNC eae8 69 0 void std::_Construct(dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo const&) eae8 d 77 44 -eaf5 5c 81 44 -eb51 1 81 44 -eb52 c 69 44 -eb5e 20 69 44 -eb7e c 69 44 -eb8a 2a 69 44 -ebb4 d 103 44 -ebc1 5c 104 44 -ec1d 1 104 44 -ec1e c 521 44 -ec2a 15 523 44 -ec3f 79 525 44 -ecb8 21 529 44 -ecd9 1 529 44 -ecda c 228 44 -ece6 14 229 44 -ecfa c 97 44 -ed06 7 98 44 -ed0d 1 98 44 -ed0e c 83 44 -ed1a 1d 85 44 -ed37 5 86 44 -ed3c 10 88 44 +eaf5 5c 81 73 +eb51 1 81 73 +eb52 c 69 70 +eb5e 20 69 70 +eb7e c 69 70 +eb8a 2a 69 70 +ebc1 5c 104 68 +ec1d 1 104 68 +ec2a 15 523 34 +ec3f 79 525 34 +ecb8 21 529 34 +ecd9 1 529 34 +ece6 14 229 42 +ed06 7 98 68 +ed0d 1 98 68 +ed1a 1d 85 68 +ed37 5 86 68 +ed3c 10 88 68 +FUNC ed4c 29 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_get_node() ed4c c 355 44 -ed58 1d 356 44 -ed75 1 356 44 +ed58 1d 356 40 +ed75 1 356 40 +FUNC ed76 b6 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_create_node(std::pair > const&) ed76 d 363 44 -ed83 e 365 44 -ed91 3c 367 44 -edcd b 373 44 -edd8 11 367 44 -ede9 b 368 44 -edf4 12 370 44 -ee06 b 371 44 -ee11 13 368 44 -ee24 8 373 44 +ed83 e 365 40 +ed91 3c 367 40 +edcd b 373 40 +edd8 11 367 40 +ede9 b 368 40 +edf4 12 370 40 +ee06 b 371 40 +ee11 13 368 40 +ee24 8 373 40 +FUNC ee2c cd 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_insert(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*, std::pair > const&) ee2c d 787 44 -ee39 15 789 44 -ee4e 5d 792 44 -eeab 24 796 44 -eecf f 798 44 -eede 1b 799 44 -eef9 1 799 44 +ee39 15 789 40 +ee4e 5d 792 40 +eeab 24 796 40 +eecf f 798 40 +eede 1b 799 40 +eef9 1 799 40 +FUNC eefa 1ef 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::insert_unique(std::pair > const&) eefa d 869 44 -ef07 e 871 44 -ef15 e 872 44 -ef23 4 873 44 -ef27 2 874 44 -ef29 6 876 44 -ef2f 35 877 44 -ef64 2a 878 44 -ef8e 6 874 44 -ef94 12 880 44 -efa6 a 881 44 -efb0 24 882 44 -efd4 51 883 44 -f025 b 885 44 -f030 36 886 44 -f066 4e 887 44 -f0b4 35 888 44 -f0e9 1 888 44 +ef07 e 871 40 +ef15 e 872 40 +ef23 4 873 40 +ef27 2 874 40 +ef29 6 876 40 +ef2f 35 877 40 +ef64 2a 878 40 +ef8e 6 874 40 +ef94 12 880 40 +efa6 a 881 40 +efb0 24 882 40 +efd4 51 883 40 +f025 b 885 40 +f030 36 886 40 +f066 4e 887 40 +f0b4 35 888 40 +f0e9 1 888 40 +FUNC f0ea 20 0 std::map, std::less, std::allocator > > >::insert(std::pair > const&) f0ea c 359 45 f0f6 14 360 45 -f10a c 97 45 -f116 7 98 45 -f11d 1 98 45 -f11e c 83 45 -f12a 1d 85 45 -f147 5 86 45 -f14c 1d 88 45 -f169 1 88 45 +f116 7 98 68 +f11d 1 98 68 +f12a 1d 85 68 +f147 5 86 68 +f14c 1d 88 68 +f169 1 88 68 +FUNC f16a 29 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_get_node() f16a c 355 45 -f176 1d 356 45 -f193 1 356 45 +f176 1d 356 40 +f193 1 356 40 +FUNC f194 5f 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_create_node(std::pair const&) f194 d 363 45 -f1a1 e 365 45 -f1af 3c 367 45 -f1eb 8 373 45 -f1f3 1 373 45 +f1a1 e 365 40 +f1af 3c 367 40 +f1eb 8 373 40 +f1f3 1 373 40 +FUNC f1f4 cd 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_insert(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*, std::pair const&) f1f4 d 787 45 -f201 15 789 45 -f216 5d 792 45 -f273 24 796 45 -f297 f 798 45 -f2a6 1b 799 45 -f2c1 1 799 45 +f201 15 789 40 +f216 5d 792 40 +f273 24 796 40 +f297 f 798 40 +f2a6 1b 799 40 +f2c1 1 799 40 +FUNC f2c2 1ef 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::insert_unique(std::pair const&) f2c2 d 869 45 -f2cf e 871 45 -f2dd e 872 45 -f2eb 4 873 45 -f2ef 2 874 45 -f2f1 6 876 45 -f2f7 35 877 45 -f32c 2a 878 45 -f356 6 874 45 -f35c 12 880 45 -f36e a 881 45 -f378 24 882 45 -f39c 51 883 45 -f3ed b 885 45 -f3f8 36 886 45 -f42e 4e 887 45 -f47c 35 888 45 -f4b1 1 888 45 +f2cf e 871 40 +f2dd e 872 40 +f2eb 4 873 40 +f2ef 2 874 40 +f2f1 6 876 40 +f2f7 35 877 40 +f32c 2a 878 40 +f356 6 874 40 +f35c 12 880 40 +f36e a 881 40 +f378 24 882 40 +f39c 51 883 40 +f3ed b 885 40 +f3f8 36 886 40 +f42e 4e 887 40 +f47c 35 888 40 +f4b1 1 888 40 +FUNC f4b2 20 0 std::map, std::allocator > >::insert(std::pair const&) f4b2 c 359 45 f4be 14 360 45 +FUNC f4d2 19 0 void std::_Destroy(std::string*) f4d2 c 106 45 -f4de d 107 45 -f4eb 1 107 45 +f4de d 107 73 +f4eb 1 107 73 +FUNC f4ec 44 0 void std::__destroy_aux<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, __false_type) f4ec c 119 45 -f4f8 2 121 45 -f4fa 13 122 45 -f50d 21 121 45 -f52e 2 122 45 +f4f8 2 121 73 +f4fa 13 122 73 +f50d 21 121 73 +f52e 2 122 73 +FUNC f530 28 0 void std::_Destroy<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >) f530 c 148 45 -f53c 1c 155 45 -f558 d 80 46 -f565 6 82 46 -f56b 2 85 46 -f56d 24 86 46 -f591 2c 85 46 -f5bd b 87 46 -f5c8 b 89 46 -f5d3 12 91 46 -f5e5 b 92 46 -f5f0 13 89 46 -f603 9 92 46 -f60c c 108 46 -f618 23 113 46 -f63b 1 113 46 -f63c c 252 46 -f648 1b 254 46 -f663 1 254 46 +f53c 1c 155 73 +f565 6 82 79 +f56b 2 85 79 +f56d 24 86 79 +f591 2c 85 79 +f5bd b 87 79 +f5c8 b 89 79 +f5d3 12 91 79 +f5e5 b 92 79 +f5f0 13 89 79 +f603 9 92 79 +f618 23 113 79 +f63b 1 113 79 +f648 1b 254 79 +f663 1 254 79 +FUNC f664 430 0 std::vector >::_M_insert_aux(__gnu_cxx::__normal_iterator > >, std::string const&) f664 14 249 47 -f678 14 251 47 -f68c 22 253 47 -f6ae f 255 47 -f6bd 12 256 47 -f6cf 55 257 47 -f724 4b 260 47 -f76f e 264 47 -f77d 15 265 47 -f792 e 266 47 -f7a0 1d 271 47 -f7bd 8 272 47 -f7c5 e 273 47 -f7d3 27 275 47 -f7fa 6 276 47 -f800 55 279 47 -f855 25 284 47 -f87a b 285 47 -f885 4f 286 47 -f8d4 3 284 47 -f8d7 13 279 47 -f8ea e 286 47 -f8f8 4d 298 47 -f945 30 299 47 -f975 12 302 47 -f987 13 303 47 -f99a 23 304 47 -f9bd 3 298 47 -f9c0 13 286 47 -f9d3 b 292 47 -f9de 39 294 47 -fa17 23 295 47 -fa3a 8 296 47 -fa42 16 294 47 -fa58 3 296 47 -fa5b 19 292 47 -fa74 19 298 47 -fa8d 7 304 47 +f678 14 251 78 +f68c 22 253 78 +f6ae f 255 78 +f6bd 12 256 78 +f6cf 55 257 78 +f724 4b 260 78 +f76f e 264 78 +f77d 15 265 78 +f792 e 266 78 +f7a0 1d 271 78 +f7bd 8 272 78 +f7c5 e 273 78 +f7d3 27 275 78 +f7fa 6 276 78 +f800 55 279 78 +f855 25 284 78 +f87a b 285 78 +f885 4f 286 78 +f8d4 3 284 78 +f8d7 13 279 78 +f8ea e 286 78 +f8f8 4d 298 78 +f945 30 299 78 +f975 12 302 78 +f987 13 303 78 +f99a 23 304 78 +f9bd 3 298 78 +f9c0 13 286 78 +f9d3 b 292 78 +f9de 39 294 78 +fa17 23 295 78 +fa3a 8 296 78 +fa42 16 294 78 +fa58 3 296 78 +fa5b 19 292 78 +fa74 19 298 78 +fa8d 7 304 78 +FUNC fa94 70 0 std::vector >::push_back(std::string const&) fa94 c 602 47 -faa0 10 604 47 -fab0 1e 606 47 -face 11 607 47 -fadf 25 610 47 +faa0 10 604 71 +fab0 1e 606 71 +face 11 607 71 +fadf 25 610 71 +FUNC fb04 19 0 void std::_Destroy(dwarf2reader::SourceFileInfo*) fb04 c 106 47 -fb10 d 107 47 -fb1d 1 107 47 +fb10 d 107 73 +fb1d 1 107 73 +FUNC fb1e 44 0 void std::__destroy_aux<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, __false_type) fb1e c 119 47 -fb2a 2 121 47 -fb2c 13 122 47 -fb3f 21 121 47 -fb60 2 122 47 +fb2a 2 121 73 +fb2c 13 122 73 +fb3f 21 121 73 +fb60 2 122 73 +FUNC fb62 28 0 void std::_Destroy<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >) fb62 c 148 47 -fb6e 1c 155 47 -fb8a d 80 47 -fb97 6 82 47 -fb9d 2 85 47 -fb9f 24 86 47 -fbc3 2c 85 47 -fbef b 87 47 -fbfa b 89 47 -fc05 12 91 47 -fc17 b 92 47 -fc22 13 89 47 -fc35 9 92 47 -fc3e c 108 47 -fc4a 23 113 47 -fc6d 1 113 47 -fc6e c 252 47 -fc7a 1b 254 47 -fc95 1 254 47 +fb6e 1c 155 73 +fb97 6 82 79 +fb9d 2 85 79 +fb9f 24 86 79 +fbc3 2c 85 79 +fbef b 87 79 +fbfa b 89 79 +fc05 12 91 79 +fc17 b 92 79 +fc22 13 89 79 +fc35 9 92 79 +fc4a 23 113 79 +fc6d 1 113 79 +fc7a 1b 254 79 +fc95 1 254 79 +FUNC fc96 43d 0 std::vector >::_M_insert_aux(__gnu_cxx::__normal_iterator > >, dwarf2reader::SourceFileInfo const&) fc96 14 249 47 -fcaa 14 251 47 -fcbe 22 253 47 -fce0 f 255 47 -fcef 12 256 47 -fd01 55 257 47 -fd56 4b 260 47 -fda1 e 264 47 -fdaf 15 265 47 -fdc4 e 266 47 -fdd2 1d 271 47 -fdef 8 272 47 -fdf7 e 273 47 -fe05 27 275 47 -fe2c 6 276 47 -fe32 55 279 47 -fe87 25 284 47 -feac b 285 47 -feb7 4f 286 47 -ff06 3 284 47 -ff09 13 279 47 -ff1c e 286 47 -ff2a 4d 298 47 -ff77 36 299 47 -ffad 12 302 47 -ffbf 13 303 47 -ffd2 2a 304 47 -fffc 3 298 47 -ffff 13 286 47 -10012 b 292 47 -1001d 39 294 47 -10056 23 295 47 -10079 8 296 47 -10081 16 294 47 -10097 3 296 47 -1009a 19 292 47 -100b3 19 298 47 -100cc 7 304 47 -100d3 1 304 47 +fcaa 14 251 78 +fcbe 22 253 78 +fce0 f 255 78 +fcef 12 256 78 +fd01 55 257 78 +fd56 4b 260 78 +fda1 e 264 78 +fdaf 15 265 78 +fdc4 e 266 78 +fdd2 1d 271 78 +fdef 8 272 78 +fdf7 e 273 78 +fe05 27 275 78 +fe2c 6 276 78 +fe32 55 279 78 +fe87 25 284 78 +feac b 285 78 +feb7 4f 286 78 +ff06 3 284 78 +ff09 13 279 78 +ff1c e 286 78 +ff2a 4d 298 78 +ff77 36 299 78 +ffad 12 302 78 +ffbf 13 303 78 +ffd2 2a 304 78 +fffc 3 298 78 +ffff 13 286 78 +10012 b 292 78 +1001d 39 294 78 +10056 23 295 78 +10079 8 296 78 +10081 16 294 78 +10097 3 296 78 +1009a 19 292 78 +100b3 19 298 78 +100cc 7 304 78 +100d3 1 304 78 +FUNC 100d4 70 0 std::vector >::push_back(dwarf2reader::SourceFileInfo const&) 100d4 c 602 47 -100e0 10 604 47 -100f0 1e 606 47 -1010e 11 607 47 -1011f 25 610 47 +100e0 10 604 71 +100f0 1e 606 71 +1010e 11 607 71 +1011f 25 610 71 FUNC 10144 16c 0 Start -10144 17 610 48 +10144 17 610 71 1015b 40 49 48 1019b 6 51 48 101a1 3f 53 48 @@ -3054,6 +3051,7 @@ 10838 c 47 49 10844 1a 48 49 1085e 2 49 49 +FUNC 10860 cb 0 google_breakpad::FileID::FileIdentifier(unsigned char*) 10860 f 51 49 1086f 16 52 49 10885 6 53 49 @@ -3068,6 +3066,7 @@ 10919 10 70 49 10929 2 71 49 1092b 1 71 49 +FUNC 1092c f2 0 google_breakpad::FileID::MachoIdentifier(int, unsigned char*) 1092c 10 73 49 1093c 15 74 49 10951 20 76 49 @@ -3076,6 +3075,7 @@ 109a0 c 80 49 109ac 69 82 49 10a15 9 83 49 +FUNC 10a1e fb 0 google_breakpad::FileID::ConvertIdentifierToString(unsigned char const*, char*, int) 10a1e c 87 49 10a2a 7 88 49 10a31 c 89 49 @@ -3090,10 +3090,10 @@ 10b17 2 102 49 10b19 1 102 49 FUNC 10b1a 13 0 NXHostByteOrder -10b1a c 144 50 -10b26 5 147 50 -10b2b 2 153 50 -10b2d 1 153 50 +10b1a c 144 56 +10b26 5 147 56 +10b2b 2 153 56 +10b2d 1 153 56 10b2e c 56 51 10b3a 1a 57 51 10b54 1e 58 51 @@ -3112,6 +3112,7 @@ 10c02 11 63 51 10c13 2 64 51 10c15 1 64 51 +FUNC 10c16 477 0 MacFileUtilities::MachoID::UpdateCRC(unsigned char*, unsigned long) 10c16 c 74 51 10c22 11 82 51 10c33 14 83 51 @@ -3137,12 +3138,15 @@ 11077 14 110 51 1108b 2 112 51 1108d 1 112 51 +FUNC 1108e 2c 0 MacFileUtilities::MachoID::UpdateMD5(unsigned char*, unsigned long) 1108e c 114 51 1109a 1e 115 51 110b8 2 116 51 +FUNC 110ba 2c 0 MacFileUtilities::MachoID::UpdateSHA1(unsigned char*, unsigned long) 110ba c 118 51 110c6 1e 119 51 110e4 2 120 51 +FUNC 110e6 121 0 MacFileUtilities::MachoID::Update(MacFileUtilities::MachoWalker*, unsigned long, unsigned long) 110e6 f 122 51 110f5 1b 123 51 11110 e 129 51 @@ -3158,6 +3162,7 @@ 111fb a 130 51 11205 2 145 51 11207 1 145 51 +FUNC 11208 cf 0 MacFileUtilities::MachoID::UUIDCommand(int, unsigned char*) 11208 14 147 51 1121c 25 149 51 11241 7 151 51 @@ -3169,6 +3174,7 @@ 1129a 36 162 51 112d0 7 163 51 112d7 1 163 51 +FUNC 112d8 224 0 MacFileUtilities::MachoID::IDCommand(int, unsigned char*) 112d8 15 165 51 112ed 25 167 51 11312 7 169 51 @@ -3197,6 +3203,7 @@ 114b5 9 202 51 114be 36 205 51 114f4 8 206 51 +FUNC 114fc d1 0 MacFileUtilities::MachoID::Adler32(int) 114fc 14 208 51 11510 25 209 51 11535 27 210 51 @@ -3206,6 +3213,7 @@ 1158b 3b 216 51 115c6 7 217 51 115cd 1 217 51 +FUNC 115ce f8 0 MacFileUtilities::MachoID::MD5(int, unsigned char*) 115ce 14 219 51 115e2 25 220 51 11607 27 221 51 @@ -3216,6 +3224,7 @@ 11680 9 228 51 11689 36 231 51 116bf 7 232 51 +FUNC 116c6 f8 0 MacFileUtilities::MachoID::SHA1(int, unsigned char*) 116c6 14 234 51 116da 25 235 51 116ff 27 236 51 @@ -3226,6 +3235,7 @@ 11778 9 243 51 11781 36 246 51 117b7 7 247 51 +FUNC 117be 378 0 MacFileUtilities::MachoID::WalkerCB(MacFileUtilities::MachoWalker*, load_command*, long long, bool, void*) 117be 2b 251 51 117e9 6 252 51 117ef e 254 51 @@ -3264,6 +3274,7 @@ 11b0c 11 306 51 11b1d 10 323 51 11b2d 9 324 51 +FUNC 11b36 95 0 MacFileUtilities::MachoID::UUIDWalkerCB(MacFileUtilities::MachoWalker*, load_command*, long long, bool, void*) 11b36 1e 328 51 11b54 a 329 51 11b5e 6 331 51 @@ -3275,6 +3286,7 @@ 11bbf a 344 51 11bc9 2 345 51 11bcb 1 345 51 +FUNC 11bcc 95 0 MacFileUtilities::MachoID::IDWalkerCB(MacFileUtilities::MachoWalker*, load_command*, long long, bool, void*) 11bcc 1e 349 51 11bea a 350 51 11bf4 6 351 51 @@ -3287,20 +3299,20 @@ 11c5f 2 364 51 11c61 1 364 51 FUNC 11c62 1c 0 _OSSwapInt32 -11c62 f 53 52 -11c71 8 55 52 -11c79 3 56 52 -11c7c 2 57 52 +11c62 f 53 55 +11c71 8 55 55 +11c79 3 56 55 +11c7c 2 57 55 FUNC 11c7e 19 0 NXSwapInt -11c7e f 52 53 -11c8d 8 54 53 -11c95 2 55 53 -11c97 1 55 53 +11c7e f 52 56 +11c8d 8 54 56 +11c95 2 55 56 +11c97 1 55 56 FUNC 11c98 13 0 NXHostByteOrder -11c98 c 144 53 -11ca4 5 147 53 -11ca9 2 153 53 -11cab 1 153 53 +11c98 c 144 56 +11ca4 5 147 56 +11ca9 2 153 56 +11cab 1 153 56 11cac c 52 54 11cb8 12 54 54 11cca 1a 55 54 @@ -3319,6 +3331,7 @@ 11d5c d 60 54 11d69 2 61 54 11d6b 1 61 54 +FUNC 11d6c 37 0 MacFileUtilities::MachoWalker::ValidateCPUType(int) 11d6c c 63 54 11d78 6 66 54 11d7e 8 67 54 @@ -3328,9 +3341,11 @@ 11d9e 3 80 54 11da1 2 81 54 11da3 1 81 54 +FUNC 11da4 50 0 MacFileUtilities::MachoWalker::ReadBytes(void*, unsigned long, long long) 11da4 18 96 54 11dbc 36 97 54 11df2 2 98 54 +FUNC 11df4 73 0 MacFileUtilities::MachoWalker::CurrentHeader(mach_header_64*, long long*) 11df4 c 100 54 11e00 a 101 54 11e0a 37 102 54 @@ -3339,6 +3354,7 @@ 11e5b a 107 54 11e65 2 108 54 11e67 1 108 54 +FUNC 11e68 2a6 0 MacFileUtilities::MachoWalker::FindHeader(int, long long&) 11e68 c 110 54 11e74 15 111 54 11e89 31 114 54 @@ -3376,6 +3392,7 @@ 120f1 11 158 54 12102 a 174 54 1210c 2 175 54 +FUNC 1210e 109 0 MacFileUtilities::MachoWalker::WalkHeaderCore(long long, unsigned int, bool) 1210e 1e 224 54 1212c c 225 54 12138 2f 227 54 @@ -3388,6 +3405,7 @@ 1220b a 240 54 12215 2 241 54 12217 1 241 54 +FUNC 12218 10e 0 MacFileUtilities::MachoWalker::WalkHeader64AtOffset(long long) 12218 18 203 54 12230 2f 205 54 1225f c 206 54 @@ -3404,6 +3422,7 @@ 12309 11 219 54 1231a a 220 54 12324 2 221 54 +FUNC 12326 143 0 MacFileUtilities::MachoWalker::WalkHeaderAtOffset(long long) 12326 18 177 54 1233e 2f 179 54 1236d c 180 54 @@ -3423,6 +3442,7 @@ 1245d a 200 54 12467 2 201 54 12469 1 201 54 +FUNC 1246a 99 0 MacFileUtilities::MachoWalker::WalkHeader(int) 1246a c 83 54 12476 15 84 54 1248b 1d 86 54 @@ -3453,12 +3473,12 @@ 12578 b 72 56 12583 2 73 56 12585 1 73 56 -FUNC 12586 32 0 breakpad_swap_uuid_command +FUNC 12586 32 0 breakpad_swap_uuid_command(breakpad_uuid_command*, NXByteOrder) 12586 c 37 57 12592 11 39 57 125a3 13 40 57 125b6 2 41 57 -FUNC 125b8 da 0 breakpad_swap_segment_command_64 +FUNC 125b8 da 0 breakpad_swap_segment_command_64(segment_command_64*, NXByteOrder) 125b8 c 44 57 125c4 11 46 57 125d5 13 47 57 @@ -3471,7 +3491,7 @@ 1266a 13 56 57 1267d 13 57 57 12690 2 58 57 -FUNC 12692 a4 0 breakpad_swap_mach_header_64 +FUNC 12692 a4 0 breakpad_swap_mach_header_64(mach_header_64*, NXByteOrder) 12692 c 61 57 1269e 11 63 57 126af 13 64 57 @@ -3482,7 +3502,7 @@ 1270e 13 69 57 12721 13 70 57 12734 2 71 57 -FUNC 12736 1d1 0 breakpad_swap_section_64 +FUNC 12736 1d1 0 breakpad_swap_section_64(section_64*, unsigned int, NXByteOrder) 12736 d 75 57 12743 c 77 57 1274f 33 78 57 @@ -3514,6 +3534,7 @@ 12a4c 13 14 58 12a5f 2a 14 58 12a89 1 14 58 +FUNC 12a8a bb 0 dwarf2reader::ByteReader::SetOffsetSize(unsigned char) 12a8a 19 16 58 12aa3 a 17 58 12aad 48 18 58 @@ -3522,6 +3543,7 @@ 12b1e 21 22 58 12b3f 6 24 58 12b45 1 24 58 +FUNC 12b46 bb 0 dwarf2reader::ByteReader::SetAddressSize(unsigned char) 12b46 19 26 58 12b5f a 27 58 12b69 48 28 58 @@ -3530,29 +3552,32 @@ 12bda 21 32 58 12bfb 6 34 58 12c01 1 34 58 +FUNC 12c02 a2 0 dwarf2reader::ByteReader::ReadFourBytes(char const*) const 12c02 c 24 59 -12c0e c 25 59 -12c1a d 26 59 -12c27 f 27 59 -12c36 f 28 59 -12c45 b 29 59 -12c50 27 30 59 -12c77 2b 32 59 -12ca2 2 34 59 +12c0e c 25 64 +12c1a d 26 64 +12c27 f 27 64 +12c36 f 28 64 +12c45 b 29 64 +12c50 27 30 64 +12c77 2b 32 64 +12ca2 2 34 64 +FUNC 12ca4 40e 0 dwarf2reader::ByteReader::ReadEightBytes(char const*) const 12ca4 11 36 59 -12cb5 1a 37 59 -12ccf 1b 38 59 -12cea 1d 39 59 -12d07 1d 40 59 -12d24 1d 41 59 -12d41 1d 42 59 -12d5e 1d 43 59 -12d7b 1d 44 59 -12d98 f 45 59 -12da7 18f 47 59 -12f36 172 50 59 -130a8 a 52 59 -130b2 2 52 59 +12cb5 1a 37 64 +12ccf 1b 38 64 +12cea 1d 39 64 +12d07 1d 40 64 +12d24 1d 41 64 +12d41 1d 42 64 +12d5e 1d 43 64 +12d7b 1d 44 64 +12d98 f 45 64 +12da7 18f 47 64 +12f36 172 50 64 +130a8 a 52 64 +130b2 2 52 64 +FUNC 130b4 a6 0 ReadInitialLength 130b4 15 29 60 130c9 18 30 60 130e1 6 31 60 @@ -3568,6 +3593,7 @@ 13179 65 50 60 131de 1f 47 60 131fd 65 50 60 +FUNC 13262 393 0 dwarf2reader::CompilationUnit::SkipAttribute(char const*, dwarf2reader::DwarfForm) 13262 14 133 60 13276 82 136 60 132f8 1f 139 60 @@ -3599,6 +3625,7 @@ 135e4 a 209 60 135ee 7 210 60 135f5 1 210 60 +FUNC 135f6 29b 0 dwarf2reader::CompilationUnit::ReadHeader() 135f6 14 217 60 1360a 9 218 60 13613 4e 221 60 @@ -3619,6 +3646,7 @@ 1382a 60 245 60 1388a 7 247 60 13891 1 247 60 +FUNC 13892 a57 0 dwarf2reader::CompilationUnit::ProcessAttribute(unsigned long long, char const*, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm) 13892 24 299 60 138b6 8a 302 60 13940 1f 307 60 @@ -3683,6 +3711,7 @@ 143ad a 492 60 143b7 6 493 60 143bd 1 493 60 +FUNC 143be b5 0 dwarf2reader::CompilationUnit::ProcessDIE(unsigned long long, char const*, dwarf2reader::CompilationUnit::Abbrev const&) 143be 19 426 60 143d7 13 427 60 143ea 46 430 60 @@ -3690,6 +3719,7 @@ 1446a 3 432 60 1446d 6 433 60 14473 1 433 60 +FUNC 14474 85 0 dwarf2reader::CompilationUnit::SkipDIE(char const*, dwarf2reader::CompilationUnit::Abbrev const&) 14474 c 122 60 14480 13 123 60 14493 27 126 60 @@ -3697,6 +3727,7 @@ 144f4 3 128 60 144f7 2 129 60 144f9 1 129 60 +FUNC 144fa be4 0 dwarf2reader::LineInfo::ProcessOneOpcode(dwarf2reader::ByteReader*, dwarf2reader::LineInfoHandler*, dwarf2reader::LineInfoHeader const&, char const*, dwarf2reader::LineStateMachine*, unsigned long*, unsigned long, bool*) 144fa 18 593 60 14512 a 594 60 1451c 18 596 60 @@ -3783,6 +3814,7 @@ 150b8 b 769 60 150c3 10 770 60 150d3 b 771 60 +FUNC 150de 14b 0 dwarf2reader::LineInfo::ReadLines() 150de e 773 60 150ec 9 778 60 150f5 17 782 60 @@ -3801,6 +3833,7 @@ 1520e 14 801 60 15222 7 802 60 15229 1 802 60 +FUNC 1522a 4fd 0 dwarf2reader::CompilationUnit::ReadAbbrevs() 1522a 18 60 60 15242 e 61 60 15250 58 65 60 @@ -3843,6 +3876,7 @@ 156f8 24 79 60 1571c b 118 60 15727 1 118 60 +FUNC 15728 5dc 0 dwarf2reader::LineInfo::ReadHeader() 15728 18 503 60 15740 9 504 60 15749 17 508 60 @@ -3900,12 +3934,14 @@ 15ceb 5 580 60 15cf0 9 582 60 15cf9 b 583 60 +FUNC 15d04 3d 0 dwarf2reader::LineInfo::Start() 15d04 c 495 60 15d10 b 496 60 15d1b b 497 60 15d26 19 498 60 15d3f 2 499 60 15d41 1 499 60 +FUNC 15d42 304 0 dwarf2reader::CompilationUnit::ProcessDIEs() 15d42 11 435 60 15d53 9 436 60 15d5c 9 441 60 @@ -3935,6 +3971,7 @@ 15ffb 1d 455 60 16018 24 485 60 1603c a 486 60 +FUNC 16046 35f 0 dwarf2reader::CompilationUnit::Start() 16046 18 249 60 1605e 58 251 60 160b6 35 252 60 @@ -3959,21 +3996,27 @@ 16388 12 291 60 1639a b 292 60 163a5 1 292 60 +FUNC 163a6 3a 0 std::fill(unsigned char*, unsigned char*, unsigned char const&) 163a6 c 573 61 163b2 9 576 61 163bb 23 577 61 163de 2 578 61 +FUNC 163e0 33 0 std::__deque_buf_size(unsigned long) 163e0 c 83 62 163ec 27 84 62 16413 1 84 62 +FUNC 16414 18 0 dwarf2reader::ByteReader::OffsetSize() const 16414 c 38 63 16420 c 38 63 +FUNC 1642c 18 0 dwarf2reader::ByteReader::AddressSize() const 1642c c 41 63 16438 c 41 63 +FUNC 16444 17 0 dwarf2reader::ByteReader::ReadOneByte(char const*) const 16444 c 10 64 16450 9 11 64 16459 2 12 64 1645b 1 12 64 +FUNC 1645c 63 0 dwarf2reader::ByteReader::ReadTwoBytes(char const*) const 1645c c 14 64 16468 d 15 64 16475 e 16 64 @@ -3982,6 +4025,7 @@ 164a5 18 20 64 164bd 2 22 64 164bf 1 22 64 +FUNC 164c0 98 0 dwarf2reader::ByteReader::ReadUnsignedLEB128(char const*, unsigned long*) const 164c0 e 59 64 164ce e 60 64 164dc 7 61 64 @@ -3994,6 +4038,7 @@ 16543 8 75 64 1654b 6 77 64 16551 7 78 64 +FUNC 16558 ee 0 dwarf2reader::ByteReader::ReadSignedLEB128(char const*, unsigned long*) const 16558 e 84 64 16566 e 85 64 16574 7 86 64 @@ -4008,14 +4053,17 @@ 16631 8 99 64 16639 6 100 64 1663f 7 101 64 +FUNC 16646 a2 0 dwarf2reader::ByteReader::ReadOffset(char const*) const 16646 13 103 64 16659 3f 104 64 16698 4a 105 64 166e2 6 106 64 +FUNC 166e8 a2 0 dwarf2reader::ByteReader::ReadAddress(char const*) const 166e8 13 108 64 166fb 3f 109 64 1673a 4a 110 64 16784 6 111 64 +FUNC 1678a 61 0 dwarf2reader::LineStateMachine::Reset(bool) 1678a 12 12 65 1679c 9 13 65 167a5 11 14 65 @@ -4026,17 +4074,21 @@ 167e2 7 19 65 167e9 2 20 65 167eb 1 20 65 +FUNC 167ec 20 0 std::_List_const_iterator >::operator!=(std::_List_const_iterator > const&) const 167ec c 253 66 167f8 14 254 66 +FUNC 1680c 25 0 std::_List_const_iterator >::operator++(int) 1680c c 226 66 16818 8 228 66 16820 c 229 66 1682c 5 230 66 16831 1 230 66 +FUNC 16832 16 0 std::_List_const_iterator >::operator->() const 16832 c 215 66 1683e a 216 66 16848 c 190 67 16854 a 190 67 +FUNC 1685e 13 0 std::auto_ptr > > >::operator->() const 1685e c 283 67 1686a 7 286 67 16871 1 286 67 @@ -4053,198 +4105,213 @@ 168d4 c 84 70 168e0 17 85 70 168f7 1 85 70 +FUNC 168f8 2d 0 std::pair std::make_pair(dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm) 168f8 c 144 70 16904 21 145 70 16925 1 145 70 -16926 c 202 70 -16932 a 203 70 +16926 c 202 66 +16932 a 203 66 +FUNC 1693c 25 0 std::list, std::allocator > >::begin() const 1693c c 588 70 -16948 19 589 70 -16961 1 589 70 +16948 19 589 66 +16961 1 589 66 +FUNC 16962 23 0 std::list, std::allocator > >::end() const 16962 c 605 70 -1696e 17 606 70 -16985 1 606 70 -16986 c 65 70 -16992 2 65 70 -16994 c 72 70 -169a0 2 72 70 -169a2 c 97 70 -169ae d 97 70 -169bb 1 97 70 -169bc c 105 70 -169c8 d 105 70 -169d5 1 105 70 -169d6 c 105 70 -169e2 d 105 70 -169ef 1 105 70 -169f0 c 67 70 -169fc 2 67 70 -169fe c 99 70 -16a0a 14 100 70 -16a1e c 99 70 -16a2a 14 100 70 -16a3e c 129 70 -16a4a 30 131 70 -16a7a c 65 70 -16a86 2 65 70 -16a88 c 72 70 -16a94 2 72 70 -16a96 c 97 70 -16aa2 d 97 70 -16aaf 1 97 70 -16ab0 c 105 70 -16abc d 105 70 -16ac9 1 105 70 -16aca c 105 70 -16ad6 d 105 70 -16ae3 1 105 70 -16ae4 c 67 70 -16af0 2 67 70 -16af2 c 99 70 -16afe 14 100 70 -16b12 c 99 70 -16b1e 14 100 70 +1696e 17 606 66 +16985 1 606 66 +16986 c 65 68 +16992 2 65 68 +16994 c 72 68 +169a0 2 72 68 +169a2 c 97 69 +169ae d 97 69 +169bb 1 97 69 +169bc c 105 69 +169c8 d 105 69 +169d5 1 105 69 +169d6 c 105 69 +169e2 d 105 69 +169ef 1 105 69 +169f0 c 67 68 +169fc 2 67 68 +169fe c 99 69 +16a0a 14 100 69 +16a1e c 99 69 +16a2a 14 100 69 +16a3e c 129 62 +16a4a 30 131 62 +16a7a c 65 68 +16a86 2 65 68 +16a88 c 72 68 +16a94 2 72 68 +16a96 c 97 69 +16aa2 d 97 69 +16aaf 1 97 69 +16ab0 c 105 69 +16abc d 105 69 +16ac9 1 105 69 +16aca c 105 69 +16ad6 d 105 69 +16ae3 1 105 69 +16ae4 c 67 68 +16af0 2 67 68 +16af2 c 99 69 +16afe 14 100 69 +16b12 c 99 69 +16b1e 14 100 69 +FUNC 16b32 2b 0 std::_Vector_base >::get_allocator() const 16b32 10 93 71 16b42 1b 94 71 16b5d 1 94 71 -16b5e c 613 72 16b6a 7 614 72 16b71 1 614 72 -16b72 c 80 72 -16b7e d 80 72 -16b8b 1 80 72 -16b8c c 107 72 -16b98 2 107 72 +16b72 c 80 71 +16b7e d 80 71 +16b8b 1 80 71 +16b98 2 107 68 +FUNC 16b9a 2d 0 void std::_Destroy >(unsigned char*, unsigned char*, std::allocator) 16b9a c 171 73 16ba6 2 173 73 16ba8 12 174 73 16bba b 173 73 16bc5 2 174 73 16bc7 1 174 73 -16bc8 c 84 73 -16bd4 2f 85 73 -16c03 2 86 73 -16c05 1 86 73 -16c06 c 96 73 -16c12 12 97 73 -16c24 2 98 73 +16bc8 c 84 71 +16bd4 2f 85 71 +16c03 2 86 71 +16c05 1 86 71 +16c06 c 96 71 +16c12 12 97 71 +16c24 2 98 71 +FUNC 16c26 1f 0 std::_List_base, std::allocator > >::_M_init() 16c26 c 338 73 -16c32 8 340 73 -16c3a b 341 73 -16c45 1 341 73 -16c46 c 105 73 -16c52 d 105 73 -16c5f 1 105 73 -16c60 c 125 73 -16c6c a 126 73 +16c32 8 340 66 +16c3a b 341 66 +16c45 1 341 66 +16c46 c 105 69 +16c52 d 105 69 +16c5f 1 105 69 +16c60 c 125 66 +16c6c a 126 66 +FUNC 16c76 25 0 std::list, std::allocator > >::begin() 16c76 c 579 73 -16c82 19 580 73 -16c9b 1 580 73 +16c82 19 580 66 +16c9b 1 580 66 +FUNC 16c9c 23 0 std::list, std::allocator > >::end() 16c9c c 597 73 -16ca8 17 597 73 -16cbf 1 597 73 -16cc0 c 603 73 -16ccc c 603 73 +16ca8 17 597 66 +16cbf 1 597 66 +16cc0 c 603 72 +16ccc c 603 72 +FUNC 16cd8 2b 0 std::vector >::begin() const 16cd8 c 342 73 -16ce4 1f 343 73 -16d03 1 343 73 +16ce4 1f 343 71 +16d03 1 343 71 +FUNC 16d04 2c 0 std::vector >::end() const 16d04 c 360 73 -16d10 20 361 73 -16d30 c 665 73 -16d3c 5 666 73 -16d41 1 666 73 -16d42 d 758 73 -16d4f 31 759 73 +16d10 20 361 71 +16d3c 5 666 72 +16d41 1 666 72 +16d4f 31 759 72 +FUNC 16d80 3c 0 std::vector >::size() const 16d80 c 402 73 -16d8c 30 403 73 -16dbc c 603 73 -16dc8 c 603 73 +16d8c 30 403 71 +16dbc c 603 72 +16dc8 c 603 72 +FUNC 16dd4 23 0 std::vector >::begin() 16dd4 c 333 73 -16de0 17 334 73 -16df7 1 334 73 -16df8 c 653 73 -16e04 33 654 73 -16e37 1 654 73 +16de0 17 334 71 +16df7 1 334 71 +16e04 33 654 72 +16e37 1 654 72 +FUNC 16e38 26 0 std::vector >::end() 16e38 c 351 73 -16e44 1a 352 73 -16e5e c 613 73 -16e6a 7 614 73 -16e71 1 614 73 +16e44 1a 352 71 +16e6a 7 614 72 +16e71 1 614 72 +FUNC 16e72 42 0 std::vector >::operator[](unsigned long) 16e72 c 494 73 -16e7e 36 495 73 +16e7e 36 495 71 +FUNC 16eb4 13 0 std::vector >::max_size() const 16eb4 c 407 73 -16ec0 7 408 73 -16ec7 1 408 73 -16ec8 c 665 73 -16ed4 5 666 73 -16ed9 1 666 73 -16eda c 621 73 -16ee6 d 623 73 -16ef3 5 624 73 -16ef8 c 382 73 -16f04 d 382 73 -16f11 1 382 73 +16ec0 7 408 71 +16ec7 1 408 71 +16ed4 5 666 72 +16ed9 1 666 72 +16ee6 d 623 72 +16ef3 5 624 72 +16ef8 c 382 62 +16f04 d 382 62 +16f11 1 382 62 +FUNC 16f12 2b 0 std::_Deque_base >::get_allocator() const 16f12 10 360 73 -16f22 1b 361 73 -16f3d 1 361 73 +16f22 1b 361 62 +16f3d 1 361 62 +FUNC 16f3e 2d 0 std::deque >::get_allocator() const 16f3e 10 764 73 -16f4e 1d 765 73 -16f6b 1 765 73 +16f4e 1d 765 62 +16f6b 1 765 62 +FUNC 16f6c 13 0 std::_Deque_iterator::operator*() const 16f6c c 134 73 -16f78 7 135 73 -16f7f 1 135 73 -16f80 c 107 73 -16f8c 2 107 73 -16f8e c 129 73 -16f9a 30 131 73 +16f78 7 135 62 +16f7f 1 135 62 +16f8c 2 107 68 +16f8e c 129 62 +16f9a 30 131 62 +FUNC 16fca 2c 0 std::deque >::end() const 16fca 10 799 73 -16fda 1c 800 73 +16fda 1c 800 62 +FUNC 16ff6 2c 0 std::deque >::begin() const 16ff6 10 781 73 -17006 1c 782 73 +17006 1c 782 62 +FUNC 17022 2e 0 std::deque >::end() 17022 10 790 73 -17032 1e 791 73 +17032 1e 791 62 +FUNC 17050 3c 0 std::vector >::_M_range_check(unsigned long) const 17050 13 515 73 -17063 15 517 73 -17078 14 518 73 +17063 15 517 71 +17078 14 518 71 +FUNC 1708c 32 0 std::vector >::at(unsigned long) 1708c c 534 73 -17098 12 536 73 -170aa 14 537 73 -170be c 103 73 -170ca 2e 104 73 -170f8 c 84 73 -17104 2f 85 73 -17133 2 86 73 -17135 1 86 73 -17136 c 96 73 -17142 12 97 73 -17154 2 98 73 -17156 c 603 73 -17162 c 603 73 +17098 12 536 71 +170aa 14 537 71 +170ca 2e 104 68 +170f8 c 84 71 +17104 2f 85 71 +17133 2 86 71 +17135 1 86 71 +17136 c 96 71 +17142 12 97 71 +17154 2 98 71 +17156 c 603 72 +17162 c 603 72 +FUNC 1716e 23 0 std::vector >::begin() 1716e c 333 73 -1717a 17 334 73 -17191 1 334 73 -17192 c 653 73 -1719e 27 654 73 -171c5 1 654 73 +1717a 17 334 71 +17191 1 334 71 +1719e 27 654 72 +171c5 1 654 72 +FUNC 171c6 42 0 std::vector >::operator[](unsigned long) 171c6 c 494 73 -171d2 36 495 73 +171d2 36 495 71 +FUNC 17208 26 0 std::vector >::end() 17208 c 351 73 -17214 1a 352 73 -1722e c 93 73 -1723a d 94 73 -17247 1 94 73 +17214 1a 352 71 +1723a d 94 68 +17247 1 94 68 +FUNC 17248 2f 0 std::_Vector_base >::_M_deallocate(unsigned char*, unsigned long) 17248 c 120 73 -17254 6 122 73 -1725a 1d 123 73 -17277 1 123 73 -17278 c 108 73 -17284 3a 109 73 -172be c 188 73 -172ca 12 189 73 -172dc 2 190 73 -172de c 272 73 -172ea 4b 273 73 -17335 1 273 73 +17254 6 122 71 +1725a 1d 123 71 +17277 1 123 71 +17278 c 108 71 +17284 3a 109 71 +172be c 188 71 +172ca 12 189 71 +172dc 2 190 71 +172de c 272 71 +172ea 4b 273 71 +17335 1 273 71 17336 13 62 74 17349 10 62 74 17359 a 63 74 @@ -4255,339 +4322,365 @@ 173c5 a 63 74 173cf 25 64 74 173f4 1a 66 74 -1740e c 188 74 -1741a 12 189 74 -1742c 2 190 74 -1742e d 758 74 -1743b 31 759 74 -1746c c 65 74 -17478 2 65 74 -1747a c 103 74 -17486 d 103 74 -17493 1 103 74 +1740e c 188 71 +1741a 12 189 71 +1742c 2 190 71 +1743b 31 759 72 +1746c c 65 68 +17478 2 65 68 +1747a c 103 69 +17486 d 103 69 +17493 1 103 69 +FUNC 17494 2d 0 std::list, std::allocator > >::get_allocator() const 17494 10 570 74 -174a4 1d 571 74 -174c1 1 571 74 -174c2 c 103 74 -174ce 2e 104 74 +174a4 1d 571 66 +174c1 1 571 66 +174ce 2e 104 68 +FUNC 174fc 20 0 std::_List_iterator >::operator!=(std::_List_iterator > const&) const 174fc c 172 74 -17508 14 173 74 +17508 14 173 66 +FUNC 1751c 1d 0 std::_List_const_iterator >::operator++() 1751c c 219 74 -17528 c 221 74 -17534 5 222 74 -17539 1 222 74 +17528 c 221 66 +17534 5 222 66 +17539 1 222 66 +FUNC 1753a 1d 0 std::_List_iterator >::operator++() 1753a c 138 74 -17546 c 140 74 -17552 5 141 74 -17557 1 141 74 +17546 c 140 66 +17552 5 141 66 +17557 1 141 66 +FUNC 17558 16 0 std::_List_const_iterator >::operator*() const 17558 c 211 74 -17564 a 212 74 +17564 a 212 66 +FUNC 1756e 16 0 std::_List_iterator >::operator*() const 1756e c 130 74 -1757a a 131 74 +1757a a 131 66 +FUNC 17584 20 0 std::_List_const_iterator >::operator==(std::_List_const_iterator > const&) const 17584 c 249 74 -17590 14 250 74 +17590 14 250 66 +FUNC 175a4 35 0 bool __gnu_cxx::operator!= > >(__gnu_cxx::__normal_iterator > > const&, __gnu_cxx::__normal_iterator > > const&) 175a4 d 699 74 -175b1 28 700 74 -175d9 1 700 74 +175b1 28 700 72 +175d9 1 700 72 +FUNC 175da 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator > >, std::allocator >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, std::allocator) 175da c 171 74 -175e6 2 173 74 -175e8 1a 174 74 -17602 21 173 74 -17623 2 174 74 -17625 1 174 74 -17626 c 127 74 -17632 29 127 74 -1765b 1 127 74 -1765c c 388 74 -17668 41 389 74 -176a9 2 390 74 -176ab 1 390 74 -176ac c 93 74 -176b8 d 94 74 -176c5 1 94 74 +175e6 2 173 73 +175e8 1a 174 73 +17602 21 173 73 +17623 2 174 73 +17625 1 174 73 +17626 c 127 62 +17632 29 127 62 +1765b 1 127 62 +1765c c 388 62 +17668 41 389 62 +176a9 2 390 62 +176ab 1 390 62 +176b8 d 94 68 +176c5 1 94 68 +FUNC 176c6 20 0 bool std::operator==(std::_Deque_iterator const&, std::_Deque_iterator const&) 176c6 c 243 74 -176d2 14 244 74 +176d2 14 244 62 +FUNC 176e6 26 0 bool std::operator!=(std::_Deque_iterator const&, std::_Deque_iterator const&) 176e6 c 256 74 -176f2 1a 257 74 +176f2 1a 257 62 +FUNC 1770c 1a 0 std::_Deque_iterator::_S_buffer_size() 1770c c 106 74 -17718 e 107 74 +17718 e 107 62 +FUNC 17726 3e 0 std::_Deque_iterator::_M_set_node(unsigned long long**) 17726 d 229 74 -17733 9 231 74 -1773c b 232 74 -17747 1d 233 74 +17733 9 231 62 +1773c b 232 62 +17747 1d 233 62 +FUNC 17764 50 0 std::_Deque_iterator::operator++() 17764 c 142 74 -17770 d 144 74 -1777d f 145 74 -1778c 18 147 74 -177a4 b 148 74 -177af 5 150 74 +17770 d 144 62 +1777d f 145 62 +1778c 18 147 62 +177a4 b 148 62 +177af 5 150 62 +FUNC 177b4 4b 0 void std::_Destroy, std::allocator >(std::_Deque_iterator, std::_Deque_iterator, std::allocator) 177b4 c 171 74 -177c0 2 173 74 -177c2 1a 174 74 -177dc 21 173 74 -177fd 2 174 74 -177ff 1 174 74 +177c0 2 173 73 +177c2 1a 174 73 +177dc 21 173 73 +177fd 2 174 73 +177ff 1 174 73 +FUNC 17800 50 0 std::_Deque_iterator::operator--() 17800 c 162 74 -1780c f 164 74 -1781b 18 166 74 -17833 b 167 74 -1783e d 169 74 -1784b 5 170 74 +1780c f 164 62 +1781b 18 166 62 +17833 b 167 62 +1783e d 169 62 +1784b 5 170 62 +FUNC 17850 39 0 std::deque >::back() 17850 c 988 74 -1785c 15 990 74 -17871 b 991 74 -1787c d 992 74 -17889 1 992 74 +1785c 15 990 62 +17871 b 991 62 +1787c d 992 62 +17889 1 992 62 +FUNC 1788a 19 0 std::stack > >::top() 1788a c 163 75 17896 d 166 75 178a3 1 166 75 +FUNC 178a4 66 0 std::_Deque_iterator::difference_type std::operator-(std::_Deque_iterator const&, std::_Deque_iterator const&) 178a4 d 328 75 -178b1 59 333 75 +178b1 59 333 62 +FUNC 1790a 26 0 std::deque >::size() const 1790a c 840 75 -17916 1a 841 75 -17930 c 661 75 -1793c 36 662 75 -17972 c 649 75 -1797e 23 650 75 -179a1 1 650 75 -179a2 c 67 75 -179ae 2 67 75 -179b0 c 99 75 -179bc 14 100 75 -179d0 c 303 75 -179dc 12 304 75 -179ee 2 305 75 -179f0 c 326 75 -179fc 2f 327 75 -17a2b d 328 75 -17a38 c 457 75 -17a44 14 458 75 -17a58 c 211 75 -17a64 2d 211 75 -17a91 1 211 75 -17a92 c 97 75 -17a9e 7 98 75 -17aa5 1 98 75 -17aa6 c 83 75 -17ab2 1d 85 75 -17acf 5 86 75 -17ad4 17 88 75 -17aeb 1 88 75 +17916 1a 841 62 +1793c 36 662 72 +1797e 23 650 72 +179a1 1 650 72 +179a2 c 67 68 +179ae 2 67 68 +179b0 c 99 69 +179bc 14 100 69 +179d0 c 303 66 +179dc 12 304 66 +179ee 2 305 66 +179f0 c 326 66 +179fc 2f 327 66 +17a2b d 328 66 +17a38 c 457 66 +17a44 14 458 66 +17a58 c 211 74 +17a64 2d 211 74 +17a91 1 211 74 +17a9e 7 98 68 +17aa5 1 98 68 +17ab2 1d 85 68 +17acf 5 86 68 +17ad4 17 88 68 +17aeb 1 88 68 +FUNC 17aec 2a 0 std::_Vector_base >::_M_allocate(unsigned long) 17aec c 116 75 -17af8 1e 117 75 -17b16 c 93 75 -17b22 d 94 75 -17b2f 1 94 75 +17af8 1e 117 71 +17b22 d 94 68 +17b2f 1 94 68 +FUNC 17b30 34 0 std::_Deque_base >::_M_deallocate_node(unsigned long long*) 17b30 c 402 75 -17b3c 28 403 75 +17b3c 28 403 62 +FUNC 17b64 38 0 std::_Deque_base >::_M_destroy_nodes(unsigned long long**, unsigned long long**) 17b64 c 504 75 -17b70 8 506 75 -17b78 14 507 75 -17b8c e 506 75 -17b9a 2 507 75 +17b70 8 506 62 +17b78 14 507 62 +17b8c e 506 62 +17b9a 2 507 62 +FUNC 17b9c 62 0 std::deque >::_M_pop_back_aux() 17b9c c 391 76 17ba8 15 393 76 17bbd 1b 394 76 17bd8 f 395 76 17be7 17 396 76 +FUNC 17bfe 4f 0 std::deque >::pop_back() 17bfe c 1081 76 -17c0a 10 1083 76 -17c1a f 1086 76 -17c29 17 1087 76 -17c40 d 1090 76 -17c4d 1 1090 76 +17c0a 10 1083 62 +17c1a f 1086 62 +17c29 17 1087 62 +17c40 d 1090 62 +17c4d 1 1090 62 +FUNC 17c4e 19 0 std::stack > >::pop() 17c4e c 205 76 -17c5a d 208 76 -17c67 1 208 76 -17c68 c 72 76 -17c74 2 72 76 -17c76 c 105 76 -17c82 d 105 76 -17c8f 1 105 76 -17c90 c 603 76 -17c9c c 603 76 +17c5a d 208 75 +17c67 1 208 75 +17c68 c 72 68 +17c74 2 72 68 +17c76 c 105 69 +17c82 d 105 69 +17c8f 1 105 69 +17c90 c 603 72 +17c9c c 603 72 +FUNC 17ca8 2b 0 std::vector >::begin() const 17ca8 c 342 76 -17cb4 1f 343 76 -17cd3 1 343 76 +17cb4 1f 343 71 +17cd3 1 343 71 +FUNC 17cd4 2c 0 std::vector >::end() const 17cd4 c 360 76 -17ce0 20 361 76 -17d00 c 665 76 -17d0c 5 666 76 -17d11 1 666 76 -17d12 d 758 76 -17d1f 28 759 76 -17d47 1 759 76 +17ce0 20 361 71 +17d0c 5 666 72 +17d11 1 666 72 +17d1f 28 759 72 +17d47 1 759 72 +FUNC 17d48 3c 0 std::vector >::size() const 17d48 c 402 76 -17d54 30 403 76 -17d84 c 621 76 -17d90 d 623 76 -17d9d 5 624 76 -17da2 c 665 76 -17dae 5 666 76 -17db3 1 666 76 +17d54 30 403 71 +17d90 d 623 72 +17d9d 5 624 72 +17dae 5 666 72 +17db3 1 666 72 +FUNC 17db4 35 0 bool __gnu_cxx::operator!= > >(__gnu_cxx::__normal_iterator > > const&, __gnu_cxx::__normal_iterator > > const&) 17db4 d 699 76 -17dc1 28 700 76 -17de9 1 700 76 +17dc1 28 700 72 +17de9 1 700 72 +FUNC 17dea 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator > >, std::allocator >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, std::allocator) 17dea c 171 76 -17df6 2 173 76 -17df8 1a 174 76 -17e12 21 173 76 -17e33 2 174 76 -17e35 1 174 76 -17e36 d 758 76 -17e43 28 759 76 -17e6b 1 759 76 -17e6c c 661 76 -17e78 2a 662 76 +17df6 2 173 73 +17df8 1a 174 73 +17e12 21 173 73 +17e33 2 174 73 +17e35 1 174 73 +17e43 28 759 72 +17e6b 1 759 72 +17e78 2a 662 72 +FUNC 17ea2 13 0 std::vector >::max_size() const 17ea2 c 407 76 -17eae 7 408 76 -17eb5 1 408 76 -17eb6 c 649 76 -17ec2 16 650 76 -17ed8 c 97 76 -17ee4 7 98 76 -17eeb 1 98 76 -17eec c 83 76 -17ef8 1d 85 76 -17f15 5 86 76 -17f1a 10 88 76 +17eae 7 408 71 +17eb5 1 408 71 +17ec2 16 650 72 +17ee4 7 98 68 +17eeb 1 98 68 +17ef8 1d 85 68 +17f15 5 86 68 +17f1a 10 88 68 +FUNC 17f2a 29 0 std::_List_base, std::allocator > >::_M_get_node() 17f2a c 311 76 -17f36 1d 312 76 -17f53 1 312 76 +17f36 1d 312 66 +17f53 1 312 66 +FUNC 17f54 5f 0 std::list, std::allocator > >::_M_create_node(std::pair const&) 17f54 d 435 76 -17f61 e 437 76 -17f6f 3c 440 76 -17fab 8 447 76 -17fb3 1 447 76 +17f61 e 437 66 +17f6f 3c 440 66 +17fab 8 447 66 +17fb3 1 447 66 +FUNC 17fb4 35 0 std::list, std::allocator > >::_M_insert(std::_List_iterator >, std::pair const&) 17fb4 c 1149 76 -17fc0 15 1151 76 -17fd5 14 1152 76 -17fe9 1 1152 76 -FUNC 17fea 52 0 _M_insert_dispatch > > -17fea c 1126 76 -17ff6 2 1128 76 -17ff8 21 1129 76 -18019 21 1128 76 -1803a 2 1129 76 -FUNC 1803c 36 0 insert > > -1803c c 838 76 -18048 2a 842 76 -18072 e 491 76 -18080 32 492 76 -180b2 64 493 76 -18116 c 211 76 -18122 3d 211 76 -1815f 1 211 76 -18160 d 103 76 -1816d 5c 104 76 -181c9 1 104 76 +17fc0 15 1151 66 +17fd5 14 1152 66 +17fe9 1 1152 66 +FUNC 17fea 52 0 void std::list, std::allocator > >::_M_insert_dispatch > >(std::_List_iterator >, std::_List_const_iterator >, std::_List_const_iterator >, __false_type) +17fea c 1126 66 +17ff6 2 1128 66 +17ff8 21 1129 66 +18019 21 1128 66 +1803a 2 1129 66 +FUNC 1803c 36 0 void std::list, std::allocator > >::insert > >(std::_List_iterator >, std::_List_const_iterator >, std::_List_const_iterator >) +1803c c 838 66 +18048 2a 842 66 +18072 e 491 66 +18080 32 492 66 +180b2 64 493 66 +18116 c 211 74 +18122 3d 211 74 +1815f 1 211 74 +1816d 5c 104 68 +181c9 1 104 68 +FUNC 181ca 31 0 std::list, std::allocator > >::push_back(std::pair const&) 181ca c 772 76 -181d6 25 773 76 -181fb 1 773 76 +181d6 25 773 66 +181fb 1 773 66 +FUNC 181fc 69 0 void std::_Construct(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev const&) 181fc d 77 76 -18209 5c 81 76 -18265 1 81 76 -18266 c 97 76 -18272 7 98 76 -18279 1 98 76 -1827a c 83 76 -18286 1d 85 76 -182a3 5 86 76 -182a8 10 88 76 -182b8 c 65 76 -182c4 2 65 76 -182c6 c 103 76 -182d2 d 103 76 -182df 1 103 76 +18209 5c 81 73 +18265 1 81 73 +18272 7 98 68 +18279 1 98 68 +18286 1d 85 68 +182a3 5 86 68 +182a8 10 88 68 +182b8 c 65 68 +182c4 2 65 68 +182c6 c 103 69 +182d2 d 103 69 +182df 1 103 69 +FUNC 182e0 4d 0 std::_Deque_base >::_M_get_map_allocator() const 182e0 11 394 76 -182f1 3c 395 76 -1832d 1 395 76 +182f1 3c 395 62 +1832d 1 395 62 +FUNC 1832e 75 0 std::_Deque_base >::_M_allocate_map(unsigned long) 1832e d 406 76 -1833b 68 407 76 -183a3 1 407 76 +1833b 68 407 62 +183a3 1 407 62 +FUNC 183a4 47 0 std::_Deque_base >::_M_deallocate_map(unsigned long long**, unsigned long) 183a4 c 410 76 -183b0 3b 411 76 -183eb 1 411 76 -183ec c 424 76 -183f8 9 426 76 -18401 22 428 76 -18423 2b 430 76 -1844e c 714 76 -1845a 70 715 76 -184ca c 111 76 -184d6 d 111 76 -184e3 1 111 76 -184e4 c 259 76 -184f0 26 259 76 -18516 c 97 76 -18522 7 98 76 -18529 1 98 76 -1852a c 83 76 -18536 1d 85 76 -18553 5 86 76 -18558 10 88 76 +183b0 3b 411 62 +183eb 1 411 62 +183ec c 424 62 +183f8 9 426 62 +18401 22 428 62 +18423 2b 430 62 +1844e c 714 62 +1845a 70 715 62 +184ca c 111 75 +184d6 d 111 75 +184e3 1 111 75 +184e4 c 259 67 +184f0 26 259 67 +18522 7 98 68 +18529 1 98 68 +18536 1d 85 68 +18553 5 86 68 +18558 10 88 68 +FUNC 18568 33 0 std::_Deque_base >::_M_allocate_node() 18568 c 398 76 -18574 27 399 76 -1859b 1 399 76 +18574 27 399 62 +1859b 1 399 62 +FUNC 1859c 82 0 std::_Deque_base >::_M_create_nodes(unsigned long long**, unsigned long long**) 1859c d 486 76 -185a9 8 491 76 -185b1 12 492 76 -185c3 13 491 76 -185d6 b 494 76 -185e1 19 496 76 -185fa b 497 76 -18605 13 494 76 -18618 6 497 76 +185a9 8 491 62 +185b1 12 492 62 +185c3 13 491 62 +185d6 b 494 62 +185e1 19 496 62 +185fa b 497 62 +18605 13 494 62 +18618 6 497 62 +FUNC 1861e 17b 0 std::_Deque_base >::_M_initialize_map(unsigned long) 1861e d 447 76 -1862b 1e 450 76 -18649 2a 452 76 -18673 1c 454 76 -1868f 19 462 76 -186a8 c 463 76 -186b4 1e 466 76 -186d2 b 467 76 -186dd 1e 469 76 -186fb 9 470 76 -18704 a 471 76 -1870e b 472 76 -18719 13 467 76 -1872c 15 475 76 -18741 18 476 76 -18759 c 477 76 -18765 34 478 76 -18799 1 478 76 -1879a d 366 76 -187a7 12 367 76 -187b9 39 368 76 -187f2 c 645 76 -187fe 1c 646 76 -FUNC 1881a 4d 0 fill<__gnu_cxx::__normal_iterator > >, unsigned char> -1881a c 539 76 -18826 9 541 76 -1882f 2 542 76 -18831 13 543 76 -18844 21 542 76 -18865 2 543 76 -18867 1 543 76 +1862b 1e 450 62 +18649 2a 452 62 +18673 1c 454 62 +1868f 19 462 62 +186a8 c 463 62 +186b4 1e 466 62 +186d2 b 467 62 +186dd 1e 469 62 +186fb 9 470 62 +18704 a 471 62 +1870e b 472 62 +18719 13 467 62 +1872c 15 475 62 +18741 18 476 62 +18759 c 477 62 +18765 34 478 62 +18799 1 478 62 +1879a d 366 62 +187a7 12 367 62 +187b9 39 368 62 +187f2 c 645 62 +187fe 1c 646 62 +FUNC 1881a 4d 0 void std::__fill::fill<__gnu_cxx::__normal_iterator > >, unsigned char>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char const&) +1881a c 539 61 +18826 9 541 61 +1882f 2 542 61 +18831 13 543 61 +18844 21 542 61 +18865 2 543 61 +18867 1 543 61 +FUNC 18868 2b 0 void std::fill<__gnu_cxx::__normal_iterator > >, unsigned char>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char const&) 18868 c 560 76 -18874 4 567 76 -18878 1b 568 76 -18893 1 568 76 +18874 4 567 61 +18878 1b 568 61 +18893 1 568 61 +FUNC 18894 6a 0 std::list, std::allocator > >::_M_erase(std::_List_iterator >) 18894 d 1157 76 -188a1 b 1159 76 -188ac 6 1160 76 -188b2 35 1161 76 -188e7 17 1162 76 +188a1 b 1159 66 +188ac 6 1160 66 +188b2 35 1161 66 +188e7 17 1162 66 +FUNC 188fe 37 0 std::list, std::allocator > >::erase(std::_List_iterator >) 188fe c 95 77 1890a 14 97 77 1891e 12 98 77 18930 5 99 77 18935 1 99 77 +FUNC 18936 3e 0 std::list, std::allocator > >::erase(std::_List_iterator >, std::_List_iterator >) 18936 c 883 77 -18942 2 885 77 -18944 15 886 77 -18959 16 885 77 -1896f 5 887 77 +18942 2 885 66 +18944 15 886 66 +18959 16 885 66 +1896f 5 887 66 +FUNC 18974 129 0 std::list, std::allocator > >::operator=(std::list, std::allocator > > const&) 18974 e 120 77 18982 c 122 77 1898e e 124 77 @@ -4602,114 +4695,111 @@ 18a73 20 134 77 18a93 a 136 77 18a9d 1 136 77 -FUNC 18a9e 4c 0 operator= -18a9e c 0 77 -18aaa 40 211 77 -FUNC 18aea 52 0 copy -18aea c 280 77 -18af6 1a 283 77 -18b10 12 285 77 -18b22 4 286 77 -18b26 6 287 77 -18b2c b 283 77 -18b37 5 289 77 +FUNC 18a9e 4c 0 dwarf2reader::CompilationUnit::Abbrev::operator=(dwarf2reader::CompilationUnit::Abbrev const&) +18a9e c 211 77 +18aaa 40 211 74 +FUNC 18aea 52 0 dwarf2reader::CompilationUnit::Abbrev* std::__copy::copy(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) +18aea c 280 61 +18af6 1a 283 61 +18b10 12 285 61 +18b22 4 286 61 +18b26 6 287 61 +18b2c b 283 61 +18b37 5 289 61 +FUNC 18b3c 2b 0 dwarf2reader::CompilationUnit::Abbrev* std::__copy_aux(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) 18b3c c 307 77 -18b48 4 315 77 -18b4c 1b 317 77 -18b67 1 317 77 -FUNC 18b68 64 0 copy_n<__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > > > -18b68 e 352 77 -18b76 56 354 77 -18bcc c 376 77 -18bd8 4 384 77 -18bdc 4 385 77 -18be0 1b 387 77 -18bfb 1 387 77 +18b48 4 315 61 +18b4c 1b 317 61 +18b67 1 317 61 +18b76 56 354 61 +18bd8 4 384 61 +18bdc 4 385 61 +18be0 1b 387 61 +18bfb 1 387 61 +FUNC 18bfc ac 0 std::vector >::erase(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >) 18bfc d 122 78 18c09 26 124 78 18c2f 43 125 78 18c72 2e 126 78 18ca0 8 127 78 -FUNC 18ca8 54 0 copy_b -18ca8 c 408 78 -18cb4 1a 411 78 -18cce 1e 412 78 -18cec b 411 78 -18cf7 5 413 78 +FUNC 18ca8 54 0 dwarf2reader::CompilationUnit::Abbrev* std::__copy_backward::copy_b(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) +18ca8 c 408 61 +18cb4 1a 411 61 +18cce 1e 412 61 +18cec b 411 61 +18cf7 5 413 61 +FUNC 18cfc 2b 0 dwarf2reader::CompilationUnit::Abbrev* std::__copy_backward_aux(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) 18cfc c 432 78 -18d08 4 440 78 -18d0c 1b 443 78 -18d27 1 443 78 -FUNC 18d28 64 0 copy_b_n<__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > > > -18d28 e 480 78 -18d36 56 482 78 -18d8c c 504 78 -18d98 4 514 78 -18d9c 4 515 78 -18da0 1b 517 78 -18dbb 1 517 78 -FUNC 18dbc 4d 0 fill<__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev> -18dbc c 526 78 -18dc8 2 528 78 -18dca 1c 529 78 -18de6 21 528 78 -18e07 2 529 78 -18e09 1 529 78 +18d08 4 440 61 +18d0c 1b 443 61 +18d27 1 443 61 +18d36 56 482 61 +18d98 4 514 61 +18d9c 4 515 61 +18da0 1b 517 61 +18dbb 1 517 61 +FUNC 18dbc 4d 0 void std::__fill::fill<__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev const&) +18dbc c 526 61 +18dc8 2 528 61 +18dca 1c 529 61 +18de6 21 528 61 +18e07 2 529 61 +18e09 1 529 61 +FUNC 18e0a 2b 0 void std::fill<__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev const&) 18e0a c 560 78 -18e16 4 567 78 -18e1a 1b 568 78 -18e35 1 568 78 -FUNC 18e36 3f 0 copy -18e36 c 298 78 -18e42 22 300 78 -18e64 11 301 78 -18e75 1 301 78 +18e16 4 567 61 +18e1a 1b 568 61 +18e35 1 568 61 +FUNC 18e36 3f 0 unsigned char* std::__copy::copy(unsigned char const*, unsigned char const*, unsigned char*) +18e36 c 298 61 +18e42 22 300 61 +18e64 11 301 61 +18e75 1 301 61 +FUNC 18e76 2b 0 unsigned char* std::__copy_aux(unsigned char*, unsigned char*, unsigned char*) 18e76 c 307 78 -18e82 4 315 78 -18e86 1b 317 78 -18ea1 1 317 78 -FUNC 18ea2 64 0 copy_n<__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > > > -18ea2 e 352 78 -18eb0 56 354 78 -18f06 c 376 78 -18f12 4 384 78 -18f16 4 385 78 -18f1a 1b 387 78 -18f35 1 387 78 +18e82 4 315 61 +18e86 1b 317 61 +18ea1 1 317 61 +18eb0 56 354 61 +18f12 4 384 61 +18f16 4 385 61 +18f1a 1b 387 61 +18f35 1 387 61 +FUNC 18f36 a0 0 std::vector >::erase(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >) 18f36 d 122 78 18f43 26 124 78 18f69 43 125 78 18fac 22 126 78 18fce 8 127 78 -18fd6 c 97 78 -18fe2 7 98 78 -18fe9 1 98 78 -18fea c 83 78 -18ff6 1d 85 78 -19013 5 86 78 -19018 d 88 78 -19025 1 88 78 +18fe2 7 98 68 +18fe9 1 98 68 +18ff6 1d 85 68 +19013 5 86 68 +19018 d 88 68 +19025 1 88 68 +FUNC 19026 2a 0 std::_Vector_base >::_M_allocate(unsigned long) 19026 c 116 78 -19032 1e 117 78 -19050 c 73 79 +19032 1e 117 71 1905c 1b 74 79 19077 1 74 79 -19078 c 108 79 19084 23 113 79 190a7 1 113 79 -190a8 c 252 79 190b4 1b 254 79 190cf 1 254 79 +FUNC 190d0 19 0 void std::_Destroy(dwarf2reader::CompilationUnit::Abbrev*) 190d0 c 106 79 -190dc d 107 79 -190e9 1 107 79 +190dc d 107 73 +190e9 1 107 73 +FUNC 190ea 44 0 void std::__destroy_aux<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, __false_type) 190ea c 119 79 -190f6 2 121 79 -190f8 13 122 79 -1910b 21 121 79 -1912c 2 122 79 +190f6 2 121 73 +190f8 13 122 73 +1910b 21 121 73 +1912c 2 122 73 +FUNC 1912e 28 0 void std::_Destroy<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >) 1912e c 148 79 -1913a 1c 155 79 +1913a 1c 155 73 +FUNC 19156 8d 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&, __false_type) 19156 d 188 79 19163 6 190 79 19169 2 193 79 @@ -4721,13 +4811,14 @@ 191ca 13 196 79 191dd 6 199 79 191e3 1 199 79 +FUNC 191e4 2f 0 void std::uninitialized_fill_n<__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&) 191e4 c 214 79 191f0 23 218 79 19213 1 218 79 +FUNC 19214 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&, std::allocator) 19214 c 308 79 19220 1b 310 79 1923b 1 310 79 -1923c d 80 79 19249 6 82 79 1924f 2 85 79 19251 24 86 79 @@ -4738,89 +4829,94 @@ 192c9 b 92 79 192d4 13 89 79 192e7 9 92 79 -192f0 c 108 79 192fc 23 113 79 1931f 1 113 79 -19320 c 252 79 1932c 1b 254 79 19347 1 254 79 +FUNC 19348 409 0 std::vector >::_M_insert_aux(__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev const&) 19348 14 249 79 -1935c 14 251 79 -19370 22 253 79 -19392 f 255 79 -193a1 12 256 79 -193b3 55 257 79 -19408 4b 260 79 -19453 e 264 79 -19461 15 265 79 -19476 e 266 79 -19484 1d 271 79 -194a1 8 272 79 -194a9 e 273 79 -194b7 27 275 79 -194de 6 276 79 -194e4 55 279 79 -19539 25 284 79 -1955e b 285 79 -19569 4f 286 79 -195b8 3 284 79 -195bb 13 279 79 -195ce e 286 79 -195dc 4d 298 79 -19629 36 299 79 -1965f 12 302 79 -19671 13 303 79 -19684 2e 304 79 -196b2 13 286 79 -196c5 b 292 79 -196d0 39 294 79 -19709 23 295 79 -1972c b 296 79 -19737 13 292 79 -1974a 7 304 79 -19751 1 304 79 +1935c 14 251 78 +19370 22 253 78 +19392 f 255 78 +193a1 12 256 78 +193b3 55 257 78 +19408 4b 260 78 +19453 e 264 78 +19461 15 265 78 +19476 e 266 78 +19484 1d 271 78 +194a1 8 272 78 +194a9 e 273 78 +194b7 27 275 78 +194de 6 276 78 +194e4 55 279 78 +19539 25 284 78 +1955e b 285 78 +19569 4f 286 78 +195b8 3 284 78 +195bb 13 279 78 +195ce e 286 78 +195dc 4d 298 78 +19629 36 299 78 +1965f 12 302 78 +19671 13 303 78 +19684 2e 304 78 +196b2 13 286 78 +196c5 b 292 78 +196d0 39 294 78 +19709 23 295 78 +1972c b 296 78 +19737 13 292 78 +1974a 7 304 78 +19751 1 304 78 +FUNC 19752 70 0 std::vector >::push_back(dwarf2reader::CompilationUnit::Abbrev const&) 19752 c 602 79 -1975e 10 604 79 -1976e 1e 606 79 -1978c 11 607 79 -1979d 25 610 79 -FUNC 197c2 50 0 copy_b -197c2 d 422 79 -197cf f 424 79 -197de 24 425 79 -19802 10 426 79 +1975e 10 604 71 +1976e 1e 606 71 +1978c 11 607 71 +1979d 25 610 71 +FUNC 197c2 50 0 unsigned char* std::__copy_backward::copy_b(unsigned char const*, unsigned char const*, unsigned char*) +197c2 d 422 61 +197cf f 424 61 +197de 24 425 61 +19802 10 426 61 +FUNC 19812 2b 0 unsigned char* std::__copy_backward_aux(unsigned char*, unsigned char*, unsigned char*) 19812 c 432 79 -1981e 4 440 79 -19822 1b 443 79 -1983d 1 443 79 -FUNC 1983e 64 0 copy_b_n<__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > > > -1983e e 480 79 -1984c 56 482 79 -198a2 c 504 79 -198ae 4 514 79 -198b2 4 515 79 -198b6 1b 517 79 -198d1 1 517 79 +1981e 4 440 61 +19822 1b 443 61 +1983d 1 443 61 +1984c 56 482 61 +198ae 4 514 61 +198b2 4 515 61 +198b6 1b 517 61 +198d1 1 517 61 +FUNC 198d2 32 0 unsigned char* std::fill_n(unsigned char*, unsigned long, unsigned char const&) 198d2 c 647 79 -198de 1e 649 79 -198fc 8 650 79 +198de 1e 649 61 +198fc 8 650 61 +FUNC 19904 27 0 void std::__uninitialized_fill_n_aux(unsigned char*, unsigned long, unsigned char const&, __true_type) 19904 c 182 79 19910 1b 183 79 1992b 1 183 79 +FUNC 1992c 2f 0 void std::uninitialized_fill_n(unsigned char*, unsigned long, unsigned char const&) 1992c c 214 79 19938 23 218 79 1995b 1 218 79 +FUNC 1995c 27 0 void std::__uninitialized_fill_n_a(unsigned char*, unsigned long, unsigned char const&, std::allocator) 1995c c 308 79 19968 1b 310 79 19983 1 310 79 +FUNC 19984 27 0 void std::__destroy_aux(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, __false_type) 19984 c 119 79 -19990 2 121 79 -19992 b 122 79 -1999d c 121 79 -199a9 2 122 79 -199ab 1 122 79 +19990 2 121 73 +19992 b 122 73 +1999d c 121 73 +199a9 2 122 73 +199ab 1 122 73 +FUNC 199ac 28 0 void std::_Destroy(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) 199ac c 148 79 -199b8 1c 155 79 +199b8 1c 155 73 +FUNC 199d4 88 0 dwarf2reader::CompilationUnit::Abbrev* std::__uninitialized_copy_aux(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, __false_type) 199d4 d 80 79 199e1 6 82 79 199e7 2 85 79 @@ -4832,12 +4928,15 @@ 19a35 b 92 79 19a40 13 89 79 19a53 9 92 79 +FUNC 19a5c 2f 0 dwarf2reader::CompilationUnit::Abbrev* std::uninitialized_copy(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) 19a5c c 108 79 19a68 23 113 79 19a8b 1 113 79 +FUNC 19a8c 27 0 dwarf2reader::CompilationUnit::Abbrev* std::__uninitialized_copy_a(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, std::allocator) 19a8c c 252 79 19a98 1b 254 79 19ab3 1 254 79 +FUNC 19ab4 7e 0 void std::__uninitialized_fill_n_aux(dwarf2reader::CompilationUnit::Abbrev*, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&, __false_type) 19ab4 d 188 79 19ac1 6 190 79 19ac7 2 193 79 @@ -4848,12 +4947,15 @@ 19b0e b 199 79 19b19 13 196 79 19b2c 6 199 79 +FUNC 19b32 2f 0 void std::uninitialized_fill_n(dwarf2reader::CompilationUnit::Abbrev*, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&) 19b32 c 214 79 19b3e 23 218 79 19b61 1 218 79 +FUNC 19b62 27 0 void std::__uninitialized_fill_n_a(dwarf2reader::CompilationUnit::Abbrev*, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&, std::allocator) 19b62 c 308 79 19b6e 1b 310 79 19b89 1 310 79 +FUNC 19b8a a5 0 dwarf2reader::CompilationUnit::Abbrev* std::__uninitialized_copy_aux<__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*, __false_type) 19b8a d 80 79 19b97 6 82 79 19b9d 2 85 79 @@ -4866,291 +4968,328 @@ 19c13 13 89 79 19c26 9 92 79 19c2f 1 92 79 +FUNC 19c30 2f 0 dwarf2reader::CompilationUnit::Abbrev* std::uninitialized_copy<__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*) 19c30 c 108 79 19c3c 23 113 79 19c5f 1 113 79 +FUNC 19c60 27 0 dwarf2reader::CompilationUnit::Abbrev* std::__uninitialized_copy_a<__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*, std::allocator) 19c60 c 252 79 19c6c 1b 254 79 19c87 1 254 79 +FUNC 19c88 5f8 0 std::vector >::_M_fill_insert(__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&) 19c88 15 311 79 -19c9d b 313 79 -19ca8 2a 315 79 -19cd2 12 318 79 -19ce4 23 319 79 -19d07 15 320 79 -19d1c c 321 79 -19d28 5a 323 79 -19d82 1c 327 79 -19d9e 35 328 79 -19dd3 16 323 79 -19de9 30 330 79 -19e19 10 343 79 -19e29 48 334 79 -19e71 21 338 79 -19e92 3d 339 79 -19ecf 13 334 79 -19ee2 b 339 79 -19eed 1c 342 79 -19f09 1e 343 79 -19f27 13 339 79 -19f3a 24 343 79 -19f5e e 348 79 -19f6c 1e 349 79 -19f8a e 350 79 -19f98 1d 353 79 -19fb5 8 354 79 -19fbd e 355 79 -19fcb 27 357 79 -19ff2 6 358 79 -19ff8 4d 361 79 -1a045 40 365 79 -1a085 18 367 79 -1a09d 44 368 79 -1a0e1 3 365 79 -1a0e4 19 361 79 -1a0fd 13 365 79 -1a110 e 368 79 -1a11e 3e 379 79 -1a15c 36 381 79 -1a192 12 384 79 -1a1a4 13 385 79 -1a1b7 2e 386 79 -1a1e5 e 368 79 -1a1f3 b 372 79 -1a1fe 39 374 79 -1a237 23 376 79 -1a25a b 377 79 -1a265 13 372 79 -1a278 8 386 79 +19c9d b 313 78 +19ca8 2a 315 78 +19cd2 12 318 78 +19ce4 23 319 78 +19d07 15 320 78 +19d1c c 321 78 +19d28 5a 323 78 +19d82 1c 327 78 +19d9e 35 328 78 +19dd3 16 323 78 +19de9 30 330 78 +19e19 10 343 78 +19e29 48 334 78 +19e71 21 338 78 +19e92 3d 339 78 +19ecf 13 334 78 +19ee2 b 339 78 +19eed 1c 342 78 +19f09 1e 343 78 +19f27 13 339 78 +19f3a 24 343 78 +19f5e e 348 78 +19f6c 1e 349 78 +19f8a e 350 78 +19f98 1d 353 78 +19fb5 8 354 78 +19fbd e 355 78 +19fcb 27 357 78 +19ff2 6 358 78 +19ff8 4d 361 78 +1a045 40 365 78 +1a085 18 367 78 +1a09d 44 368 78 +1a0e1 3 365 78 +1a0e4 19 361 78 +1a0fd 13 365 78 +1a110 e 368 78 +1a11e 3e 379 78 +1a15c 36 381 78 +1a192 12 384 78 +1a1a4 13 385 78 +1a1b7 2e 386 78 +1a1e5 e 368 78 +1a1f3 b 372 78 +1a1fe 39 374 78 +1a237 23 376 78 +1a25a b 377 78 +1a265 13 372 78 +1a278 8 386 78 +FUNC 1a280 2e 0 std::vector >::insert(__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&) 1a280 c 657 79 -1a28c 22 658 79 +1a28c 22 658 71 +FUNC 1a2ae ab 0 std::vector >::resize(unsigned long, dwarf2reader::CompilationUnit::Abbrev const&) 1a2ae d 422 79 -1a2bb 15 424 79 -1a2d0 48 425 79 -1a318 41 427 79 -1a359 1 427 79 +1a2bb 15 424 71 +1a2d0 48 425 71 +1a318 41 427 71 +1a359 1 427 71 +FUNC 1a35a 63 0 std::vector >::resize(unsigned long) 1a35a d 441 79 -1a367 56 442 79 -1a3bd 1 442 79 +1a367 56 442 71 +1a3bd 1 442 71 +FUNC 1a3be 13 0 std::_Deque_iterator::operator*() const 1a3be c 134 79 -1a3ca 7 135 79 -1a3d1 1 135 79 -FUNC 1a3d2 3f 0 copy -1a3d2 c 298 79 -1a3de 22 300 79 -1a400 11 301 79 -1a411 1 301 79 +1a3ca 7 135 62 +1a3d1 1 135 62 +FUNC 1a3d2 3f 0 unsigned long long** std::__copy::copy(unsigned long long* const*, unsigned long long* const*, unsigned long long**) +1a3d2 c 298 61 +1a3de 22 300 61 +1a400 11 301 61 +1a411 1 301 61 +FUNC 1a412 2b 0 unsigned long long** std::__copy_aux(unsigned long long**, unsigned long long**, unsigned long long**) 1a412 c 307 79 -1a41e 4 315 79 -1a422 1b 317 79 -1a43d 1 317 79 -FUNC 1a43e 27 0 copy_n -1a43e c 325 79 -1a44a 1b 326 79 -1a465 1 326 79 +1a41e 4 315 61 +1a422 1b 317 61 +1a43d 1 317 61 +FUNC 1a43e 27 0 unsigned long long** std::__copy_normal::copy_n(unsigned long long**, unsigned long long**, unsigned long long**) +1a43e c 325 61 +1a44a 1b 326 61 +1a465 1 326 61 +FUNC 1a466 2f 0 unsigned long long** std::copy(unsigned long long**, unsigned long long**, unsigned long long**) 1a466 c 376 79 -1a472 4 384 79 -1a476 4 385 79 -1a47a 1b 387 79 -1a495 1 387 79 -FUNC 1a496 60 0 copy_b -1a496 d 422 79 -1a4a3 12 424 79 -1a4b5 2e 425 79 -1a4e3 13 426 79 +1a472 4 384 61 +1a476 4 385 61 +1a47a 1b 387 61 +1a495 1 387 61 +FUNC 1a496 60 0 unsigned long long** std::__copy_backward::copy_b(unsigned long long* const*, unsigned long long* const*, unsigned long long**) +1a496 d 422 61 +1a4a3 12 424 61 +1a4b5 2e 425 61 +1a4e3 13 426 61 +FUNC 1a4f6 2b 0 unsigned long long** std::__copy_backward_aux(unsigned long long**, unsigned long long**, unsigned long long**) 1a4f6 c 432 79 -1a502 4 440 79 -1a506 1b 443 79 -1a521 1 443 79 -FUNC 1a522 27 0 copy_b_n -1a522 c 451 79 -1a52e 1b 452 79 -1a549 1 452 79 +1a502 4 440 61 +1a506 1b 443 61 +1a521 1 443 61 +FUNC 1a522 27 0 unsigned long long** std::__copy_backward_normal::copy_b_n(unsigned long long**, unsigned long long**, unsigned long long**) +1a522 c 451 61 +1a52e 1b 452 61 +1a549 1 452 61 +FUNC 1a54a 2f 0 unsigned long long** std::copy_backward(unsigned long long**, unsigned long long**, unsigned long long**) 1a54a c 504 79 -1a556 4 514 79 -1a55a 4 515 79 -1a55e 1b 517 79 -1a579 1 517 79 +1a556 4 514 61 +1a55a 4 515 61 +1a55e 1b 517 61 +1a579 1 517 61 +FUNC 1a57a 1df 0 std::deque >::_M_reallocate_map(unsigned long, bool) 1a57a 13 723 79 -1a58d 1b 726 79 -1a5a8 9 727 79 -1a5b1 13 730 79 -1a5c4 39 732 79 -1a5fd b 735 79 -1a608 27 736 79 -1a62f 2f 740 79 -1a65e 26 748 79 -1a684 15 750 79 -1a699 36 751 79 -1a6cf 22 753 79 -1a6f1 1e 756 79 -1a70f 8 758 79 -1a717 9 759 79 -1a720 15 762 79 -1a735 24 763 79 -1a759 1 763 79 +1a58d 1b 726 76 +1a5a8 9 727 76 +1a5b1 13 730 76 +1a5c4 39 732 76 +1a5fd b 735 76 +1a608 27 736 76 +1a62f 2f 740 76 +1a65e 26 748 76 +1a684 15 750 76 +1a699 36 751 76 +1a6cf 22 753 76 +1a6f1 1e 756 76 +1a70f 8 758 76 +1a717 9 759 76 +1a720 15 762 76 +1a735 24 763 76 +1a759 1 763 76 +FUNC 1a75a 59 0 std::deque >::_M_reserve_map_at_back(unsigned long) 1a75a e 1443 79 -1a768 2a 1445 79 -1a792 21 1447 79 -1a7b3 1 1447 79 +1a768 2a 1445 62 +1a792 21 1447 62 +1a7b3 1 1447 62 +FUNC 1a7b4 8c 0 std::deque >::_M_push_back_aux(unsigned long long const&) 1a7b4 c 345 79 -1a7c0 e 347 79 -1a7ce 13 348 79 -1a7e1 18 349 79 -1a7f9 1e 352 79 -1a817 1b 353 79 -1a832 c 355 79 -1a83e 2 360 79 +1a7c0 e 347 76 +1a7ce 13 348 76 +1a7e1 18 349 76 +1a7f9 1e 352 76 +1a817 1b 353 76 +1a832 c 355 76 +1a83e 2 360 76 +FUNC 1a840 62 0 std::deque >::push_back(unsigned long long const&) 1a840 c 1039 79 -1a84c 13 1041 79 -1a85f 1e 1044 79 -1a87d 11 1045 79 -1a88e 14 1048 79 +1a84c 13 1041 62 +1a85f 1e 1044 62 +1a87d 11 1045 62 +1a88e 14 1048 62 +FUNC 1a8a2 20 0 std::stack > >::push(unsigned long long const&) 1a8a2 c 190 79 -1a8ae 14 191 79 -FUNC 1a8c2 27 0 copy_n -1a8c2 c 325 79 -1a8ce 1b 326 79 -1a8e9 1 326 79 +1a8ae 14 191 75 +FUNC 1a8c2 27 0 unsigned char* std::__copy_normal::copy_n(unsigned char*, unsigned char*, unsigned char*) +1a8c2 c 325 61 +1a8ce 1b 326 61 +1a8e9 1 326 61 +FUNC 1a8ea 2f 0 unsigned char* std::copy(unsigned char*, unsigned char*, unsigned char*) 1a8ea c 376 79 -1a8f6 4 384 79 -1a8fa 4 385 79 -1a8fe 1b 387 79 -1a919 1 387 79 +1a8f6 4 384 61 +1a8fa 4 385 61 +1a8fe 1b 387 61 +1a919 1 387 61 +FUNC 1a91a 27 0 unsigned char* std::__uninitialized_copy_aux(unsigned char*, unsigned char*, unsigned char*, __true_type) 1a91a c 73 79 1a926 1b 74 79 1a941 1 74 79 +FUNC 1a942 2f 0 unsigned char* std::uninitialized_copy(unsigned char*, unsigned char*, unsigned char*) 1a942 c 108 79 1a94e 23 113 79 1a971 1 113 79 +FUNC 1a972 27 0 unsigned char* std::__uninitialized_copy_a(unsigned char*, unsigned char*, unsigned char*, std::allocator) 1a972 c 252 79 1a97e 1b 254 79 1a999 1 254 79 -FUNC 1a99a 40 0 copy_n<__gnu_cxx::__normal_iterator > >, unsigned char*> -1a99a d 334 79 -1a9a7 33 335 79 +FUNC 1a99a 40 0 unsigned char* std::__copy_normal::copy_n<__gnu_cxx::__normal_iterator > >, unsigned char*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char*) +1a99a d 334 61 +1a9a7 33 335 61 +FUNC 1a9da 2f 0 unsigned char* std::copy<__gnu_cxx::__normal_iterator > >, unsigned char*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char*) 1a9da c 376 79 -1a9e6 4 384 79 -1a9ea 4 385 79 -1a9ee 1b 387 79 -1aa09 1 387 79 +1a9e6 4 384 61 +1a9ea 4 385 61 +1a9ee 1b 387 61 +1aa09 1 387 61 +FUNC 1aa0a 27 0 unsigned char* std::__uninitialized_copy_aux<__gnu_cxx::__normal_iterator > >, unsigned char*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char*, __true_type) 1aa0a c 73 79 1aa16 1b 74 79 1aa31 1 74 79 +FUNC 1aa32 2f 0 unsigned char* std::uninitialized_copy<__gnu_cxx::__normal_iterator > >, unsigned char*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char*) 1aa32 c 108 79 1aa3e 23 113 79 1aa61 1 113 79 +FUNC 1aa62 27 0 unsigned char* std::__uninitialized_copy_a<__gnu_cxx::__normal_iterator > >, unsigned char*, unsigned char>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char*, std::allocator) 1aa62 c 252 79 1aa6e 1b 254 79 1aa89 1 254 79 -FUNC 1aa8a 45 0 fill_n<__gnu_cxx::__normal_iterator > >, long unsigned int, unsigned char> -1aa8a c 614 79 -1aa96 9 616 79 -1aa9f 2 617 79 -1aaa1 13 618 79 -1aab4 16 617 79 -1aaca 5 619 79 -1aacf 1 619 79 -1aad0 c 636 79 -1aadc 4 641 79 -1aae0 1b 642 79 -1aafb 1 642 79 +1aa96 9 616 61 +1aa9f 2 617 61 +1aaa1 13 618 61 +1aab4 16 617 61 +1aaca 5 619 61 +1aacf 1 619 61 +1aadc 4 641 61 +1aae0 1b 642 61 +1aafb 1 642 61 +FUNC 1aafc 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char>(__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char const&, __true_type) 1aafc c 182 79 1ab08 1b 183 79 1ab23 1 183 79 +FUNC 1ab24 2f 0 void std::uninitialized_fill_n<__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char>(__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char const&) 1ab24 c 214 79 1ab30 23 218 79 1ab53 1 218 79 +FUNC 1ab54 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char, unsigned char>(__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char const&, std::allocator) 1ab54 c 308 79 1ab60 1b 310 79 1ab7b 1 310 79 +FUNC 1ab7c 45a 0 std::vector >::_M_fill_insert(__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char const&) 1ab7c 14 311 79 -1ab90 b 313 79 -1ab9b 21 315 79 -1abbc 9 318 79 -1abc5 23 319 79 -1abe8 15 320 79 -1abfd c 321 79 -1ac09 4e 323 79 -1ac57 11 327 79 -1ac68 30 328 79 -1ac98 35 330 79 -1accd 48 334 79 -1ad15 14 338 79 -1ad29 43 339 79 -1ad6c 11 342 79 -1ad7d 1e 343 79 -1ad9b e 348 79 -1ada9 1e 349 79 -1adc7 e 350 79 -1add5 1d 353 79 -1adf2 8 354 79 -1adfa e 355 79 -1ae08 27 357 79 -1ae2f 6 358 79 -1ae35 4d 361 79 -1ae82 40 365 79 -1aec2 18 367 79 -1aeda 4d 368 79 -1af27 3e 379 79 -1af65 2d 381 79 -1af92 12 384 79 -1afa4 13 385 79 -1afb7 1f 386 79 +1ab90 b 313 78 +1ab9b 21 315 78 +1abbc 9 318 78 +1abc5 23 319 78 +1abe8 15 320 78 +1abfd c 321 78 +1ac09 4e 323 78 +1ac57 11 327 78 +1ac68 30 328 78 +1ac98 35 330 78 +1accd 48 334 78 +1ad15 14 338 78 +1ad29 43 339 78 +1ad6c 11 342 78 +1ad7d 1e 343 78 +1ad9b e 348 78 +1ada9 1e 349 78 +1adc7 e 350 78 +1add5 1d 353 78 +1adf2 8 354 78 +1adfa e 355 78 +1ae08 27 357 78 +1ae2f 6 358 78 +1ae35 4d 361 78 +1ae82 40 365 78 +1aec2 18 367 78 +1aeda 4d 368 78 +1af27 3e 379 78 +1af65 2d 381 78 +1af92 12 384 78 +1afa4 13 385 78 +1afb7 1f 386 78 +FUNC 1afd6 2e 0 std::vector >::insert(__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char const&) 1afd6 c 657 79 -1afe2 22 658 79 +1afe2 22 658 71 +FUNC 1b004 ab 0 std::vector >::resize(unsigned long, unsigned char const&) 1b004 d 422 79 -1b011 15 424 79 -1b026 48 425 79 -1b06e 41 427 79 -1b0af 1 427 79 +1b011 15 424 71 +1b026 48 425 71 +1b06e 41 427 71 +1b0af 1 427 71 +FUNC 1b0b0 2b 0 std::vector >::resize(unsigned long) 1b0b0 c 441 79 -1b0bc 1f 442 79 -1b0db 1 442 79 +1b0bc 1f 442 71 +1b0db 1 442 71 +FUNC 1b0dc 1a 0 std::_Deque_iterator::_S_buffer_size() 1b0dc c 106 79 -1b0e8 e 107 79 +1b0e8 e 107 62 +FUNC 1b0f6 66 0 std::_Deque_iterator::difference_type std::operator-(std::_Deque_iterator const&, std::_Deque_iterator const&) 1b0f6 d 328 79 -1b103 59 333 79 +1b103 59 333 62 +FUNC 1b15c 3e 0 std::_Deque_iterator::_M_set_node(unsigned long long**) 1b15c d 229 79 -1b169 9 231 79 -1b172 b 232 79 -1b17d 1d 233 79 +1b169 9 231 62 +1b172 b 232 62 +1b17d 1d 233 62 +FUNC 1b19a 50 0 std::_Deque_iterator::operator++() 1b19a c 142 79 -1b1a6 d 144 79 -1b1b3 f 145 79 -1b1c2 18 147 79 -1b1da b 148 79 -1b1e5 5 150 79 -FUNC 1b1ea 84 0 copy, std::_Deque_iterator > -1b1ea e 280 79 -1b1f8 17 283 79 -1b20f 20 285 79 -1b22f b 286 79 -1b23a b 287 79 -1b245 b 283 79 -1b250 1e 289 79 +1b1a6 d 144 62 +1b1b3 f 145 62 +1b1c2 18 147 62 +1b1da b 148 62 +1b1e5 5 150 62 +FUNC 1b1ea 84 0 std::_Deque_iterator std::__copy::copy, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator) +1b1ea e 280 61 +1b1f8 17 283 61 +1b20f 20 285 61 +1b22f b 286 61 +1b23a b 287 61 +1b245 b 283 61 +1b250 1e 289 61 +FUNC 1b26e 7e 0 std::_Deque_iterator std::__copy_aux, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator) 1b26e 11 307 79 -1b27f 4 315 79 -1b283 69 317 79 -FUNC 1b2ec 7a 0 copy_n, std::_Deque_iterator > -1b2ec 11 325 79 -1b2fd 69 326 79 +1b27f 4 315 61 +1b283 69 317 61 +FUNC 1b2ec 7a 0 std::_Deque_iterator std::__copy_normal::copy_n, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator) +1b2ec 11 325 61 +1b2fd 69 326 61 +FUNC 1b366 82 0 std::_Deque_iterator std::copy, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator) 1b366 11 376 79 -1b377 4 384 79 -1b37b 4 385 79 -1b37f 69 387 79 +1b377 4 384 61 +1b37b 4 385 61 +1b37f 69 387 61 +FUNC 1b3e8 7a 0 std::_Deque_iterator std::__uninitialized_copy_aux, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator, __true_type) 1b3e8 11 73 79 1b3f9 69 74 79 +FUNC 1b462 82 0 std::_Deque_iterator std::uninitialized_copy, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator) 1b462 11 108 79 1b473 71 113 79 +FUNC 1b4e4 7a 0 std::_Deque_iterator std::__uninitialized_copy_a, std::_Deque_iterator, unsigned long long>(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator, std::allocator) 1b4e4 11 252 79 1b4f5 69 254 79 -1b55e 10 679 79 -1b56e 64 680 79 -1b5d2 e8 681 79 -1b6ba c 143 79 -1b6c6 14 144 79 -1b6da 6 144 79 +1b55e 10 679 62 +1b56e 64 680 62 +1b5d2 e8 681 62 +1b6ba c 143 75 +1b6c6 14 144 75 +1b6da 6 144 75 FUNC 1b6e0 4d 0 __eprintf 1b6e0 6 1826 80 1b6e6 3 1832 80 diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/classes.nib firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/classes.nib --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/classes.nib 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/classes.nib 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,69 @@ + + + + + IBClasses + + + CLASS + LengthLimitingTextField + LANGUAGE + ObjC + SUPERCLASS + NSTextField + + + ACTIONS + + cancel + id + sendReport + id + showPrivacyPolicy + id + + CLASS + Reporter + LANGUAGE + ObjC + OUTLETS + + alertWindow_ + NSWindow + cancelButton_ + NSButton + commentMessage_ + NSTextField + commentsEntryField_ + LengthLimitingTextField + countdownLabel_ + NSTextField + dialogTitle_ + NSTextField + emailEntryField_ + LengthLimitingTextField + emailLabel_ + NSTextField + emailMessage_ + NSTextField + emailSectionBox_ + NSBox + headerBox_ + NSBox + preEmailBox_ + NSBox + privacyLinkArrow_ + NSView + privacyLinkLabel_ + NSTextField + sendButton_ + NSButton + + SUPERCLASS + NSObject + + + IBVersion + 1 + + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/info.nib firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/info.nib --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/info.nib 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/info.nib 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,20 @@ + + + + + IBFramework Version + 676 + IBLastKnownRelativeProjectPath + ../Breakpad.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 132 + + IBSystem Version + 9J61 + targetFramework + IBCocoaFramework + + Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/keyedobjects.nib and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/Breakpad.nib/keyedobjects.nib differ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,139 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// This component uses the HTTPMultipartUpload of the breakpad project to send +// the minidump and associated data to the crash reporting servers. +// It will perform throttling based on the parameters passed to it and will +// prompt the user to send the minidump. + +#include + +#include "client/mac/Framework/Breakpad.h" + +#define kClientIdPreferenceKey @"clientid" + +extern NSString *const kGoogleServerType; +extern NSString *const kSocorroServerType; +extern NSString *const kDefaultServerType; + +// We're sublcassing NSTextField in order to override a particular +// method (see the implementation) that lets us reject changes if they +// are longer than a particular length. Bindings would normally solve +// this problem, but when we implemented a validation method, and +// returned NO for strings that were too long, the UI was not updated +// right away, which was a poor user experience. The UI would be +// updated as soon as the text field lost first responder status, +// which isn't soon enough. It is a known bug that the UI KVO didn't +// work in the middle of a validation. +@interface LengthLimitingTextField : NSTextField { + @private + unsigned int maximumLength_; +} + +- (void) setMaximumLength:(unsigned int)maxLength; +@end + +@interface Reporter : NSObject { + @public + IBOutlet NSWindow *alertWindow_; // The alert window + + // Grouping boxes used for resizing. + IBOutlet NSBox *headerBox_; + IBOutlet NSBox *preEmailBox_; + IBOutlet NSBox *emailSectionBox_; + // Localized elements (or things that need to be moved during localization). + IBOutlet NSTextField *dialogTitle_; + IBOutlet NSTextField *commentMessage_; + IBOutlet NSTextField *emailMessage_; + IBOutlet NSTextField *emailLabel_; + IBOutlet NSTextField *privacyLinkLabel_; + IBOutlet NSButton *sendButton_; + IBOutlet NSButton *cancelButton_; + IBOutlet LengthLimitingTextField *emailEntryField_; + IBOutlet LengthLimitingTextField *commentsEntryField_; + IBOutlet NSTextField *countdownLabel_; + IBOutlet NSView *privacyLinkArrow_; + + // Text field bindings, for user input. + NSString *commentsValue_; // Comments from the user + NSString *emailValue_; // Email from the user + NSString *countdownMessage_; // Message indicating time + // left for input. + @private + int configFile_; // File descriptor for config file + NSMutableDictionary *parameters_; // Key value pairs of data (STRONG) + NSData *minidumpContents_; // The data in the minidump (STRONG) + NSData *logFileData_; // An NSdata for the tar, + // bz2'd log file. + NSTimeInterval remainingDialogTime_; // Keeps track of how long + // we have until we cancel + // the dialog + NSTimer *messageTimer_; // Timer we use to update + // the dialog + NSMutableDictionary *serverDictionary_; // The dictionary mapping a + // server type name to a + // dictionary of server + // parameter names. + NSMutableDictionary *socorroDictionary_; // The dictionary for + // Socorro. + NSMutableDictionary *googleDictionary_; // The dictionary for + // Google. + NSMutableDictionary *extraServerVars_; // A dictionary containing + // extra key/value pairs + // that are uploaded to the + // crash server with the + // minidump. +} + +// Stops the modal panel with an NSAlertDefaultReturn value. This is the action +// invoked by the "Send Report" button. +- (IBAction)sendReport:(id)sender; +// Stops the modal panel with an NSAlertAlternateReturn value. This is the +// action invoked by the "Cancel" button. +- (IBAction)cancel:(id)sender; +// Opens the Privacy Policy url in the default web browser. +- (IBAction)showPrivacyPolicy:(id)sender; + +// Delegate methods for the NSTextField for comments. We want to capture the +// Return key and use it to send the message when no text has been entered. +// Otherwise, we want Return to add a carriage return to the comments field. +- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView + doCommandBySelector:(SEL)commandSelector; + +// Accessors to make bindings work +- (NSString *)commentsValue; +- (void)setCommentsValue:(NSString *)value; + +- (NSString *)emailValue; +- (void)setEmailValue:(NSString *)value; + +- (NSString *)countdownMessage; +- (void)setCountdownMessage:(NSString *)value; + +@end Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.icns and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.icns differ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender-Info.plist firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender-Info.plist --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender-Info.plist 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender-Info.plist 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + ${EXECUTABLE_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + crash_report_sender + CFBundleIdentifier + com.Breakpad.${PRODUCT_NAME:identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${EXECUTABLE_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSHasLocalizedDisplayName + + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/crash_report_sender.m 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,1221 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import +#import +#import + +#import +#import + +#import "common/mac/HTTPMultipartUpload.h" + +#import "crash_report_sender.h" +#import "common/mac/GTMLogger.h" + + +#define kLastSubmission @"LastSubmission" +const int kMinidumpFileLengthLimit = 800000; +const int kUserCommentsMaxLength = 1500; +const int kEmailMaxLength = 64; + +#define kApplePrefsSyncExcludeAllKey \ + @"com.apple.PreferenceSync.ExcludeAllSyncKeys" + +NSString *const kGoogleServerType = @"google"; +NSString *const kSocorroServerType = @"socorro"; +NSString *const kDefaultServerType = @"google"; + +#pragma mark - + +@interface NSView (ResizabilityExtentions) +// Shifts the view vertically by the given amount. +- (void)breakpad_shiftVertically:(float)offset; + +// Shifts the view horizontally by the given amount. +- (void)breakpad_shiftHorizontally:(float)offset; +@end + +@implementation NSView (ResizabilityExtentions) +- (void)breakpad_shiftVertically:(float)offset { + NSPoint origin = [self frame].origin; + origin.y += offset; + [self setFrameOrigin:origin]; +} + +- (void)breakpad_shiftHorizontally:(float)offset { + NSPoint origin = [self frame].origin; + origin.x += offset; + [self setFrameOrigin:origin]; +} +@end + +@interface NSWindow (ResizabilityExtentions) +// Adjusts the window height by heightDelta relative to its current height, +// keeping all the content at the same size. +- (void)breakpad_adjustHeight:(float)heightDelta; +@end + +@implementation NSWindow (ResizabilityExtentions) +- (void)breakpad_adjustHeight:(float)heightDelta { + [[self contentView] setAutoresizesSubviews:NO]; + + NSRect windowFrame = [self frame]; + windowFrame.size.height += heightDelta; + [self setFrame:windowFrame display:YES]; + // For some reason the content view is resizing, but not adjusting its origin, + // so correct it manually. + [[self contentView] setFrameOrigin:NSMakePoint(0, 0)]; + + [[self contentView] setAutoresizesSubviews:YES]; +} +@end + +@interface NSTextField (ResizabilityExtentions) +// Grows or shrinks the height of the field to the minimum required to show the +// current text, preserving the existing width and origin. +// Returns the change in height. +- (float)breakpad_adjustHeightToFit; + +// Grows or shrinks the width of the field to the minimum required to show the +// current text, preserving the existing height and origin. +// Returns the change in width. +- (float)breakpad_adjustWidthToFit; +@end + +@implementation NSTextField (ResizabilityExtentions) +- (float)breakpad_adjustHeightToFit { + NSRect oldFrame = [self frame]; + // Starting with the 10.5 SDK, height won't grow, so make it huge to start. + NSRect presizeFrame = oldFrame; + presizeFrame.size.height = MAXFLOAT; + // sizeToFit will blow out the width rather than making the field taller, so + // we do it manually. + NSSize newSize = [[self cell] cellSizeForBounds:presizeFrame]; + NSRect newFrame = NSMakeRect(oldFrame.origin.x, oldFrame.origin.y, + NSWidth(oldFrame), newSize.height); + [self setFrame:newFrame]; + + return newSize.height - NSHeight(oldFrame); +} + +- (float)breakpad_adjustWidthToFit { + NSRect oldFrame = [self frame]; + [self sizeToFit]; + return NSWidth([self frame]) - NSWidth(oldFrame); +} +@end + +@interface NSButton (ResizabilityExtentions) +// Resizes to fit the label using IB-style size-to-fit metrics and enforcing a +// minimum width of 70, while preserving the right edge location. +// Returns the change in width. +- (float)breakpad_smartSizeToFit; +@end + +@implementation NSButton (ResizabilityExtentions) +- (float)breakpad_smartSizeToFit { + NSRect oldFrame = [self frame]; + [self sizeToFit]; + NSRect newFrame = [self frame]; + // sizeToFit gives much worse results that IB's Size to Fit option. This is + // the amount of padding IB adds over a sizeToFit, empirically determined. + const float kExtraPaddingAmount = 12; + const float kMinButtonWidth = 70; // The default button size in IB. + newFrame.size.width = NSWidth(newFrame) + kExtraPaddingAmount; + if (NSWidth(newFrame) < kMinButtonWidth) + newFrame.size.width = kMinButtonWidth; + // Preserve the right edge location. + newFrame.origin.x = NSMaxX(oldFrame) - NSWidth(newFrame); + [self setFrame:newFrame]; + return NSWidth(newFrame) - NSWidth(oldFrame); +} +@end + +#pragma mark - + + +@interface Reporter(PrivateMethods) ++ (uid_t)consoleUID; + +- (id)initWithConfigurationFD:(int)fd; + +- (NSString *)readString; +- (NSData *)readData:(ssize_t)length; + +- (BOOL)readConfigurationData; +- (BOOL)readMinidumpData; +- (BOOL)readLogFileData; + +// Returns YES if it has been long enough since the last report that we should +// submit a report for this crash. +- (BOOL)reportIntervalElapsed; + +// Returns YES if we should send the report without asking the user first. +- (BOOL)shouldSubmitSilently; + +// Returns YES if the minidump was generated on demand. +- (BOOL)isOnDemand; + +// Returns YES if we should ask the user to provide comments. +- (BOOL)shouldRequestComments; + +// Returns YES if we should ask the user to provide an email address. +- (BOOL)shouldRequestEmail; + +// Shows UI to the user to ask for permission to send and any extra information +// we've been instructed to request. Returns YES if the user allows the report +// to be sent. +- (BOOL)askUserPermissionToSend; + +// Returns the short description of the crash, suitable for use as a dialog +// title (e.g., "The application Foo has quit unexpectedly"). +- (NSString*)shortDialogMessage; + +// Return explanatory text about the crash and the reporter, suitable for the +// body text of a dialog. +- (NSString*)explanatoryDialogText; + +// Returns the amount of time the UI should be shown before timing out. +- (NSTimeInterval)messageTimeout; + +// Preps the comment-prompting alert window for display: +// * localizes all the elements +// * resizes and adjusts layout as necessary for localization +// * removes the email section if includeEmail is NO +- (void)configureAlertWindowIncludingEmail:(BOOL)includeEmail; + +// Rmevoes the email section of the dialog, adjusting the rest of the window +// as necessary. +- (void)removeEmailPrompt; + +// Run an alert window with the given timeout. Returns +// NSRunStoppedResponse if the timeout is exceeded. A timeout of 0 +// queues the message immediately in the modal run loop. +- (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout; + +// Returns a unique client id (user-specific), creating a persistent +// one in the user defaults, if necessary. +- (NSString*)clientID; + +// Returns a dictionary that can be used to map Breakpad parameter names to +// URL parameter names. +- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType; + +// Helper method to set HTTP parameters based on server type. This is +// called right before the upload - crashParameters will contain, on exit, +// URL parameters that should be sent with the minidump. +- (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters; + +// Initialization helper to create dictionaries mapping Breakpad +// parameters to URL parameters +- (void)createServerParameterDictionaries; + +// Accessor method for the URL parameter dictionary +- (NSMutableDictionary *)urlParameterDictionary; + +// This method adds a key/value pair to the dictionary that +// will be uploaded to the crash server. +- (void)addServerParameter:(id)value forKey:(NSString *)key; + +// This method is used to periodically update the UI with how many +// seconds are left in the dialog display. +- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer; + +// When we receive this notification, it means that the user has +// begun editing the email address or comments field, and we disable +// the timers so that the user has as long as they want to type +// in their comments/email. +- (void)controlTextDidBeginEditing:(NSNotification *)aNotification; + +@end + +@implementation Reporter +//============================================================================= ++ (uid_t)consoleUID { + SCDynamicStoreRef store = + SCDynamicStoreCreate(kCFAllocatorDefault, CFSTR("Reporter"), NULL, NULL); + uid_t uid = -2; // Default to "nobody" + if (store) { + CFStringRef user = SCDynamicStoreCopyConsoleUser(store, &uid, NULL); + + if (user) + CFRelease(user); + else + uid = -2; + + CFRelease(store); + } + + return uid; +} + +//============================================================================= +- (id)initWithConfigurationFD:(int)fd { + if ((self = [super init])) { + configFile_ = fd; + remainingDialogTime_ = 0; + } + + // Because the reporter is embedded in the framework (and many copies + // of the framework may exist) its not completely certain that the OS + // will obey the com.apple.PreferenceSync.ExcludeAllSyncKeys in our + // Info.plist. To make sure, also set the key directly if needed. + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + if (![ud boolForKey:kApplePrefsSyncExcludeAllKey]) { + [ud setBool:YES forKey:kApplePrefsSyncExcludeAllKey]; + } + + [self createServerParameterDictionaries]; + + return self; +} + +//============================================================================= +- (NSString *)readString { + NSMutableString *str = [NSMutableString stringWithCapacity:32]; + char ch[2] = { 0 }; + + while (read(configFile_, &ch[0], 1) == 1) { + if (ch[0] == '\n') { + // Break if this is the first newline after reading some other string + // data. + if ([str length]) + break; + } else { + [str appendString:[NSString stringWithUTF8String:ch]]; + } + } + + return str; +} + +//============================================================================= +- (NSData *)readData:(ssize_t)length { + NSMutableData *data = [NSMutableData dataWithLength:length]; + char *bytes = (char *)[data bytes]; + + if (read(configFile_, bytes, length) != length) + return nil; + + return data; +} + +//============================================================================= +- (BOOL)readConfigurationData { + parameters_ = [[NSMutableDictionary alloc] init]; + + while (1) { + NSString *key = [self readString]; + + if (![key length]) + break; + + // Read the data. Try to convert to a UTF-8 string, or just save + // the data + NSString *lenStr = [self readString]; + ssize_t len = [lenStr intValue]; + NSData *data = [self readData:len]; + id value = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + + // If the keyname is prefixed by BREAKPAD_SERVER_PARAMETER_PREFIX + // that indicates that it should be uploaded to the server along + // with the minidump, so we treat it specially. + if ([key hasPrefix:@BREAKPAD_SERVER_PARAMETER_PREFIX]) { + NSString *urlParameterKey = + [key substringFromIndex:[@BREAKPAD_SERVER_PARAMETER_PREFIX length]]; + if ([urlParameterKey length]) { + if (value) { + [self addServerParameter:value + forKey:urlParameterKey]; + } else { + [self addServerParameter:data + forKey:urlParameterKey]; + } + } + } else { + [parameters_ setObject:(value ? value : data) forKey:key]; + } + [value release]; + } + + // generate a unique client ID based on this host's MAC address + // then add a key/value pair for it + NSString *clientID = [self clientID]; + [parameters_ setObject:clientID forKey:@"guid"]; + + close(configFile_); + configFile_ = -1; + + return YES; +} + +// Per user per machine +- (NSString *)clientID { + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + NSString *crashClientID = [ud stringForKey:kClientIdPreferenceKey]; + if (crashClientID) { + return crashClientID; + } + + // Otherwise, if we have no client id, generate one! + srandom([[NSDate date] timeIntervalSince1970]); + long clientId1 = random(); + long clientId2 = random(); + long clientId3 = random(); + crashClientID = [NSString stringWithFormat:@"%x%x%x", + clientId1, clientId2, clientId3]; + + [ud setObject:crashClientID forKey:kClientIdPreferenceKey]; + [ud synchronize]; + return crashClientID; +} + +//============================================================================= +- (BOOL)readLogFileData { + unsigned int logFileCounter = 0; + + NSString *logPath; + int logFileTailSize = [[parameters_ objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE] + intValue]; + + NSMutableArray *logFilenames; // An array of NSString, one per log file + logFilenames = [[NSMutableArray alloc] init]; + + char tmpDirTemplate[80] = "/tmp/CrashUpload-XXXXX"; + char *tmpDir = mkdtemp(tmpDirTemplate); + + // Construct key names for the keys we expect to contain log file paths + for(logFileCounter = 0;; logFileCounter++) { + NSString *logFileKey = [NSString stringWithFormat:@"%@%d", + @BREAKPAD_LOGFILE_KEY_PREFIX, + logFileCounter]; + + logPath = [parameters_ objectForKey:logFileKey]; + + // They should all be consecutive, so if we don't find one, assume + // we're done + + if (!logPath) { + break; + } + + NSData *entireLogFile = [[NSData alloc] initWithContentsOfFile:logPath]; + + if (entireLogFile == nil) { + continue; + } + + NSRange fileRange; + + // Truncate the log file, only if necessary + + if ([entireLogFile length] <= logFileTailSize) { + fileRange = NSMakeRange(0, [entireLogFile length]); + } else { + fileRange = NSMakeRange([entireLogFile length] - logFileTailSize, + logFileTailSize); + } + + char tmpFilenameTemplate[100]; + + // Generate a template based on the log filename + sprintf(tmpFilenameTemplate,"%s/%s-XXXX", tmpDir, + [[logPath lastPathComponent] fileSystemRepresentation]); + + char *tmpFile = mktemp(tmpFilenameTemplate); + + NSData *logSubdata = [entireLogFile subdataWithRange:fileRange]; + NSString *tmpFileString = [NSString stringWithUTF8String:tmpFile]; + [logSubdata writeToFile:tmpFileString atomically:NO]; + + [logFilenames addObject:[tmpFileString lastPathComponent]]; + [entireLogFile release]; + } + + if ([logFilenames count] == 0) { + [logFilenames release]; + logFileData_ = nil; + return NO; + } + + // now, bzip all files into one + NSTask *tarTask = [[NSTask alloc] init]; + + [tarTask setCurrentDirectoryPath:[NSString stringWithUTF8String:tmpDir]]; + [tarTask setLaunchPath:@"/usr/bin/tar"]; + + NSMutableArray *bzipArgs = [NSMutableArray arrayWithObjects:@"-cjvf", + @"log.tar.bz2",nil]; + [bzipArgs addObjectsFromArray:logFilenames]; + + [logFilenames release]; + + [tarTask setArguments:bzipArgs]; + [tarTask launch]; + [tarTask waitUntilExit]; + [tarTask release]; + + NSString *logTarFile = [NSString stringWithFormat:@"%s/log.tar.bz2",tmpDir]; + logFileData_ = [[NSData alloc] initWithContentsOfFile:logTarFile]; + if (logFileData_ == nil) { + GTMLoggerDebug(@"Cannot find temp tar log file: %@", logTarFile); + return NO; + } + return YES; + +} + +//============================================================================= +- (BOOL)readMinidumpData { + NSString *minidumpDir = [parameters_ objectForKey:@kReporterMinidumpDirectoryKey]; + NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey]; + + if (![minidumpID length]) + return NO; + + NSString *path = [minidumpDir stringByAppendingPathComponent:minidumpID]; + path = [path stringByAppendingPathExtension:@"dmp"]; + + // check the size of the minidump and limit it to a reasonable size + // before attempting to load into memory and upload + const char *fileName = [path fileSystemRepresentation]; + struct stat fileStatus; + + BOOL success = YES; + + if (!stat(fileName, &fileStatus)) { + if (fileStatus.st_size > kMinidumpFileLengthLimit) { + fprintf(stderr, "Breakpad Reporter: minidump file too large " \ + "to upload : %d\n", (int)fileStatus.st_size); + success = NO; + } + } else { + fprintf(stderr, "Breakpad Reporter: unable to determine minidump " \ + "file length\n"); + success = NO; + } + + if (success) { + minidumpContents_ = [[NSData alloc] initWithContentsOfFile:path]; + success = ([minidumpContents_ length] ? YES : NO); + } + + if (!success) { + // something wrong with the minidump file -- delete it + unlink(fileName); + } + + return success; +} + +//============================================================================= +- (BOOL)askUserPermissionToSend { + // Initialize Cocoa, needed to display the alert + NSApplicationLoad(); + + // Get the timeout value for the notification. + NSTimeInterval timeout = [self messageTimeout]; + + int buttonPressed = NSAlertAlternateReturn; + // Determine whether we should create a text box for user feedback. + if ([self shouldRequestComments]) { + BOOL didLoadNib = [NSBundle loadNibNamed:@"Breakpad" owner:self]; + if (!didLoadNib) { + return NO; + } + + [self configureAlertWindowIncludingEmail:[self shouldRequestEmail]]; + + buttonPressed = [self runModalWindow:alertWindow_ withTimeout:timeout]; + + // Extract info from the user into the parameters_ dictionary + if ([self commentsValue]) { + [parameters_ setObject:[self commentsValue] forKey:@BREAKPAD_COMMENTS]; + } + if ([self emailValue]) { + [parameters_ setObject:[self emailValue] forKey:@BREAKPAD_EMAIL]; + } + } else { + // Create an alert panel to tell the user something happened + NSPanel* alert = NSGetAlertPanel([self shortDialogMessage], + [self explanatoryDialogText], + NSLocalizedString(@"sendReportButton", @""), + NSLocalizedString(@"cancelButton", @""), + nil); + + // Pop the alert with an automatic timeout, and wait for the response + buttonPressed = [self runModalWindow:alert withTimeout:timeout]; + + // Release the panel memory + NSReleaseAlertPanel(alert); + } + return buttonPressed == NSAlertDefaultReturn; +} + +- (void)configureAlertWindowIncludingEmail:(BOOL)includeEmail { + // Swap in localized values, making size adjustments to impacted elements as + // we go. Remember that the origin is in the bottom left, so elements above + // "fall" as text areas are shrunk from their overly-large IB sizes. + + // Localize the header. No resizing needed, as it has plenty of room. + [dialogTitle_ setStringValue:[self shortDialogMessage]]; + + // Localize the explanatory text field. + [commentMessage_ setStringValue:[NSString stringWithFormat:@"%@\n\n%@", + [self explanatoryDialogText], + NSLocalizedString(@"commentsMsg", @"")]]; + float commentHeightDelta = [commentMessage_ breakpad_adjustHeightToFit]; + [headerBox_ breakpad_shiftVertically:commentHeightDelta]; + [alertWindow_ breakpad_adjustHeight:commentHeightDelta]; + + // Either localize the email explanation field or remove the whole email + // section depending on whether or not we are asking for email. + if (includeEmail) { + [emailMessage_ setStringValue:NSLocalizedString(@"emailMsg", @"")]; + float emailHeightDelta = [emailMessage_ breakpad_adjustHeightToFit]; + [preEmailBox_ breakpad_shiftVertically:emailHeightDelta]; + [alertWindow_ breakpad_adjustHeight:emailHeightDelta]; + } else { + [self removeEmailPrompt]; // Handles necessary resizing. + } + + // Localize the email label, and shift the associated text field. + [emailLabel_ setStringValue:NSLocalizedString(@"emailLabel", @"")]; + float emailLabelWidthDelta = [emailLabel_ breakpad_adjustWidthToFit]; + [emailEntryField_ breakpad_shiftHorizontally:emailLabelWidthDelta]; + + // Localize the placeholder text. + [[commentsEntryField_ cell] + setPlaceholderString:NSLocalizedString(@"commentsPlaceholder", @"")]; + [[emailEntryField_ cell] + setPlaceholderString:NSLocalizedString(@"emailPlaceholder", @"")]; + + // Localize the privacy policy label, and keep it right-aligned to the arrow. + [privacyLinkLabel_ setStringValue:NSLocalizedString(@"privacyLabel", @"")]; + float privacyLabelWidthDelta = [privacyLinkLabel_ breakpad_adjustWidthToFit]; + [privacyLinkLabel_ breakpad_shiftHorizontally:(-privacyLabelWidthDelta)]; + + // Localize the buttons, and keep the cancel button at the right distance. + [sendButton_ setTitle:NSLocalizedString(@"sendReportButton", @"")]; + float sendButtonWidthDelta = [sendButton_ breakpad_smartSizeToFit]; + [cancelButton_ breakpad_shiftHorizontally:(-sendButtonWidthDelta)]; + [cancelButton_ setTitle:NSLocalizedString(@"cancelButton", @"")]; + [cancelButton_ breakpad_smartSizeToFit]; +} + +- (void)removeEmailPrompt { + [emailSectionBox_ setHidden:YES]; + float emailSectionHeight = NSHeight([emailSectionBox_ frame]); + [preEmailBox_ breakpad_shiftVertically:(-emailSectionHeight)]; + [alertWindow_ breakpad_adjustHeight:(-emailSectionHeight)]; +} + +- (int)runModalWindow:(NSWindow*)window withTimeout:(NSTimeInterval)timeout { + // Queue a |stopModal| message to be performed in |timeout| seconds. + if (timeout > 0.001) { + remainingDialogTime_ = timeout; + SEL updateSelector = @selector(updateSecondsLeftInDialogDisplay:); + messageTimer_ = [NSTimer scheduledTimerWithTimeInterval:1.0 + target:self + selector:updateSelector + userInfo:nil + repeats:YES]; + } + + // Run the window modally and wait for either a |stopModal| message or a + // button click. + [NSApp activateIgnoringOtherApps:YES]; + int returnMethod = [NSApp runModalForWindow:window]; + + return returnMethod; +} + +- (IBAction)sendReport:(id)sender { + // Force the text fields to end editing so text for the currently focused + // field will be commited. + [alertWindow_ makeFirstResponder:alertWindow_]; + + [alertWindow_ orderOut:self]; + // Use NSAlertDefaultReturn so that the return value of |runModalWithWindow| + // matches the AppKit function NSRunAlertPanel() + [NSApp stopModalWithCode:NSAlertDefaultReturn]; +} + +// UI Button Actions +//============================================================================= +- (IBAction)cancel:(id)sender { + [alertWindow_ orderOut:self]; + // Use NSAlertDefaultReturn so that the return value of |runModalWithWindow| + // matches the AppKit function NSRunAlertPanel() + [NSApp stopModalWithCode:NSAlertAlternateReturn]; +} + +- (IBAction)showPrivacyPolicy:(id)sender { + // Get the localized privacy policy URL and open it in the default browser. + NSURL* privacyPolicyURL = + [NSURL URLWithString:NSLocalizedString(@"privacyPolicyURL", @"")]; + [[NSWorkspace sharedWorkspace] openURL:privacyPolicyURL]; +} + +// Text Field Delegate Methods +//============================================================================= +- (BOOL) control:(NSControl*)control + textView:(NSTextView*)textView +doCommandBySelector:(SEL)commandSelector { + BOOL result = NO; + // If the user has entered text on the comment field, don't end + // editing on "return". + if (control == commentsEntryField_ && + commandSelector == @selector(insertNewline:) + && [[textView string] length] > 0) { + [textView insertNewlineIgnoringFieldEditor:self]; + result = YES; + } + return result; +} + +- (void)controlTextDidBeginEditing:(NSNotification *)aNotification { + [messageTimer_ invalidate]; + [self setCountdownMessage:@""]; +} + +- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer { + remainingDialogTime_ -= 1; + + NSString *countdownMessage; + NSString *formatString; + + int displayedTimeLeft; // This can be either minutes or seconds. + + if (remainingDialogTime_ > 59) { + // calculate minutes remaining for UI purposes + displayedTimeLeft = (remainingDialogTime_ / 60); + + if (displayedTimeLeft == 1) { + formatString = NSLocalizedString(@"countdownMsgMinuteSingular", @""); + } else { + formatString = NSLocalizedString(@"countdownMsgMinutesPlural", @""); + } + } else { + displayedTimeLeft = remainingDialogTime_; + if (remainingDialogTime_ == 1) { + formatString = NSLocalizedString(@"countdownMsgSecondSingular", @""); + } else { + formatString = NSLocalizedString(@"countdownMsgSecondsPlural", @""); + } + } + countdownMessage = [NSString stringWithFormat:formatString, + displayedTimeLeft]; + if (remainingDialogTime_ <= 30) { + [countdownLabel_ setTextColor:[NSColor redColor]]; + } + [self setCountdownMessage:countdownMessage]; + if (remainingDialogTime_ <= 0) { + [messageTimer_ invalidate]; + [NSApp stopModal]; + } +} + + + +#pragma mark Accessors +#pragma mark - +//============================================================================= + +- (NSString *)commentsValue { + return [[commentsValue_ retain] autorelease]; +} + +- (void)setCommentsValue:(NSString *)value { + if (commentsValue_ != value) { + [commentsValue_ release]; + commentsValue_ = [value copy]; + } +} + +- (NSString *)emailValue { + return [[emailValue_ retain] autorelease]; +} + +- (void)setEmailValue:(NSString *)value { + if (emailValue_ != value) { + [emailValue_ release]; + emailValue_ = [value copy]; + } +} + +- (NSString *)countdownMessage { + return [[countdownMessage_ retain] autorelease]; +} + +- (void)setCountdownMessage:(NSString *)value { + if (countdownMessage_ != value) { + [countdownMessage_ release]; + countdownMessage_ = [value copy]; + } +} + +#pragma mark - +//============================================================================= +- (BOOL)reportIntervalElapsed { + float interval = [[parameters_ objectForKey:@BREAKPAD_REPORT_INTERVAL] + floatValue]; + NSString *program = [parameters_ objectForKey:@BREAKPAD_PRODUCT]; + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + NSMutableDictionary *programDict = + [NSMutableDictionary dictionaryWithDictionary:[ud dictionaryForKey:program]]; + NSNumber *lastTimeNum = [programDict objectForKey:kLastSubmission]; + NSTimeInterval lastTime = lastTimeNum ? [lastTimeNum floatValue] : 0; + NSTimeInterval now = CFAbsoluteTimeGetCurrent(); + NSTimeInterval spanSeconds = (now - lastTime); + + [programDict setObject:[NSNumber numberWithFloat:now] forKey:kLastSubmission]; + [ud setObject:programDict forKey:program]; + [ud synchronize]; + + // If we've specified an interval and we're within that time, don't ask the + // user if we should report + GTMLoggerDebug(@"Reporter Interval: %f", interval); + if (interval > spanSeconds) { + GTMLoggerDebug(@"Within throttling interval, not sending report"); + return NO; + } + return YES; +} + +- (BOOL)isOnDemand { + return [[parameters_ objectForKey:@BREAKPAD_ON_DEMAND] + isEqualToString:@"YES"]; +} + +- (BOOL)shouldSubmitSilently { + return [[parameters_ objectForKey:@BREAKPAD_SKIP_CONFIRM] + isEqualToString:@"YES"]; +} + +- (BOOL)shouldRequestComments { + return [[parameters_ objectForKey:@BREAKPAD_REQUEST_COMMENTS] + isEqualToString:@"YES"]; +} + +- (BOOL)shouldRequestEmail { + return [[parameters_ objectForKey:@BREAKPAD_REQUEST_EMAIL] + isEqualToString:@"YES"]; +} + +- (NSString*)shortDialogMessage { + NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; + if (![displayName length]) + displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT]; + + if ([self isOnDemand]) { + return [NSString + stringWithFormat:NSLocalizedString(@"noCrashDialogHeader", @""), + displayName]; + } else { + return [NSString + stringWithFormat:NSLocalizedString(@"crashDialogHeader", @""), + displayName]; + } +} + +- (NSString*)explanatoryDialogText { + NSString *displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; + if (![displayName length]) + displayName = [parameters_ objectForKey:@BREAKPAD_PRODUCT]; + + NSString *vendor = [parameters_ objectForKey:@BREAKPAD_VENDOR]; + if (![vendor length]) + vendor = @"unknown vendor"; + + if ([self isOnDemand]) { + return [NSString + stringWithFormat:NSLocalizedString(@"noCrashDialogMsg", @""), + vendor, displayName]; + } else { + return [NSString + stringWithFormat:NSLocalizedString(@"crashDialogMsg", @""), + vendor]; + } +} + +- (NSTimeInterval)messageTimeout { + // Get the timeout value for the notification. + NSTimeInterval timeout = [[parameters_ objectForKey:@BREAKPAD_CONFIRM_TIMEOUT] + floatValue]; + // Require a timeout of at least a minute (except 0, which means no timeout). + if (timeout > 0.001 && timeout < 60.0) { + timeout = 60.0; + } + return timeout; +} + +- (void)createServerParameterDictionaries { + serverDictionary_ = [[NSMutableDictionary alloc] init]; + socorroDictionary_ = [[NSMutableDictionary alloc] init]; + googleDictionary_ = [[NSMutableDictionary alloc] init]; + extraServerVars_ = [[NSMutableDictionary alloc] init]; + + [serverDictionary_ setObject:socorroDictionary_ forKey:kSocorroServerType]; + [serverDictionary_ setObject:googleDictionary_ forKey:kGoogleServerType]; + + [googleDictionary_ setObject:@"ptime" forKey:@BREAKPAD_PROCESS_UP_TIME]; + [googleDictionary_ setObject:@"email" forKey:@BREAKPAD_EMAIL]; + [googleDictionary_ setObject:@"comments" forKey:@BREAKPAD_COMMENTS]; + [googleDictionary_ setObject:@"prod" forKey:@BREAKPAD_PRODUCT]; + [googleDictionary_ setObject:@"ver" forKey:@BREAKPAD_VERSION]; + + [socorroDictionary_ setObject:@"Comments" forKey:@BREAKPAD_COMMENTS]; + [socorroDictionary_ setObject:@"CrashTime" + forKey:@BREAKPAD_PROCESS_CRASH_TIME]; + [socorroDictionary_ setObject:@"StartupTime" + forKey:@BREAKPAD_PROCESS_START_TIME]; + [socorroDictionary_ setObject:@"Version" + forKey:@BREAKPAD_VERSION]; + [socorroDictionary_ setObject:@"ProductName" + forKey:@BREAKPAD_PRODUCT]; + [socorroDictionary_ setObject:@"Email" + forKey:@BREAKPAD_EMAIL]; +} + +- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType { + if (serverType == nil || [serverType length] == 0) { + return [serverDictionary_ objectForKey:kDefaultServerType]; + } + return [serverDictionary_ objectForKey:serverType]; +} + +- (NSMutableDictionary *)urlParameterDictionary { + NSString *serverType = [parameters_ objectForKey:@BREAKPAD_SERVER_TYPE]; + return [self dictionaryForServerType:serverType]; + +} + +- (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters { + NSDictionary *urlParameterNames = [self urlParameterDictionary]; + + id key; + NSEnumerator *enumerator = [parameters_ keyEnumerator]; + + while ((key = [enumerator nextObject])) { + // The key from parameters_ corresponds to a key in + // urlParameterNames. The value in parameters_ gets stored in + // crashParameters with a key that is the value in + // urlParameterNames. + + // For instance, if parameters_ has [PRODUCT_NAME => "FOOBAR"] and + // urlParameterNames has [PRODUCT_NAME => "pname"] the final HTTP + // URL parameter becomes [pname => "FOOBAR"]. + NSString *breakpadParameterName = (NSString *)key; + NSString *urlParameter = [urlParameterNames + objectForKey:breakpadParameterName]; + if (urlParameter) { + [crashParameters setObject:[parameters_ objectForKey:key] + forKey:urlParameter]; + } + } + + // Now, add the parameters that were added by the application. + enumerator = [extraServerVars_ keyEnumerator]; + + while ((key = [enumerator nextObject])) { + NSString *urlParameterName = (NSString *)key; + NSString *urlParameterValue = + [extraServerVars_ objectForKey:urlParameterName]; + [crashParameters setObject:urlParameterValue + forKey:urlParameterName]; + } + return YES; +} + +- (void)addServerParameter:(id)value forKey:(NSString *)key { + [extraServerVars_ setObject:value forKey:key]; +} + +//============================================================================= +- (void)report { + NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]]; + HTTPMultipartUpload *upload = [[HTTPMultipartUpload alloc] initWithURL:url]; + NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary]; + + if (![self populateServerDictionary:uploadParameters]) { + return; + } + + [upload setParameters:uploadParameters]; + + // Add minidump file + if (minidumpContents_) { + [upload addFileContents:minidumpContents_ name:@"upload_file_minidump"]; + + // Send it + NSError *error = nil; + NSData *data = [upload send:&error]; + NSString *result = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + const char *reportID = "ERR"; + + if (error) { + fprintf(stderr, "Breakpad Reporter: Send Error: %s\n", + [[error description] UTF8String]); + } else { + NSCharacterSet *trimSet = [NSCharacterSet whitespaceAndNewlineCharacterSet]; + reportID = [[result stringByTrimmingCharactersInSet:trimSet] UTF8String]; + } + + // rename the minidump file according to the id returned from the server + NSString *minidumpDir = [parameters_ objectForKey:@kReporterMinidumpDirectoryKey]; + NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey]; + + NSString *srcString = [NSString stringWithFormat:@"%@/%@.dmp", + minidumpDir, minidumpID]; + NSString *destString = [NSString stringWithFormat:@"%@/%s.dmp", + minidumpDir, reportID]; + + const char *src = [srcString fileSystemRepresentation]; + const char *dest = [destString fileSystemRepresentation]; + + if (rename(src, dest) == 0) { + GTMLoggerInfo(@"Breakpad Reporter: Renamed %s to %s after successful " \ + "upload",src, dest); + } + else { + // can't rename - don't worry - it's not important for users + GTMLoggerDebug(@"Breakpad Reporter: successful upload report ID = %s\n", + reportID ); + } + [result release]; + } + + if (logFileData_) { + HTTPMultipartUpload *logUpload = [[HTTPMultipartUpload alloc] initWithURL:url]; + + [uploadParameters setObject:@"log" forKey:@"type"]; + [logUpload setParameters:uploadParameters]; + [logUpload addFileContents:logFileData_ name:@"log"]; + + NSError *error = nil; + NSData *data = [logUpload send:&error]; + NSString *result = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + [result release]; + [logUpload release]; + } + + [upload release]; +} + +//============================================================================= +- (void)dealloc { + [parameters_ release]; + [minidumpContents_ release]; + [logFileData_ release]; + [googleDictionary_ release]; + [socorroDictionary_ release]; + [serverDictionary_ release]; + [extraServerVars_ release]; + [super dealloc]; +} + +- (void)awakeFromNib { + [emailEntryField_ setMaximumLength:kEmailMaxLength]; + [commentsEntryField_ setMaximumLength:kUserCommentsMaxLength]; +} + +@end + +//============================================================================= +@implementation LengthLimitingTextField + +- (void) setMaximumLength:(unsigned int)maxLength { + maximumLength_ = maxLength; +} + +// This is the method we're overriding in NSTextField, which lets us +// limit the user's input if it makes the string too long. +- (BOOL) textView:(NSTextView *)textView +shouldChangeTextInRange:(NSRange)affectedCharRange + replacementString:(NSString *)replacementString { + + // Sometimes the range comes in invalid, so reject if we can't + // figure out if the replacement text is too long. + if (affectedCharRange.location == NSNotFound) { + return NO; + } + // Figure out what the new string length would be, taking into + // account user selections. + int newStringLength = + [[textView string] length] - affectedCharRange.length + + [replacementString length]; + if (newStringLength > maximumLength_) { + return NO; + } else { + return YES; + } +} + +// Cut, copy, and paste have to be caught specifically since there is no menu. +- (BOOL)performKeyEquivalent:(NSEvent*)event { + // Only handle the key equivalent if |self| is the text field with focus. + NSText* fieldEditor = [self currentEditor]; + if (fieldEditor != nil) { + // Check for a single "Command" modifier + unsigned int modifiers = [event modifierFlags]; + modifiers &= NSDeviceIndependentModifierFlagsMask; + if (modifiers == NSCommandKeyMask) { + // Now, check for Select All, Cut, Copy, or Paste key equivalents. + NSString* characters = [event characters]; + // Select All is Command-A. + if ([characters isEqualToString:@"a"]) { + [fieldEditor selectAll:self]; + return YES; + // Cut is Command-X. + } else if ([characters isEqualToString:@"x"]) { + [fieldEditor cut:self]; + return YES; + // Copy is Command-C. + } else if ([characters isEqualToString:@"c"]) { + [fieldEditor copy:self]; + return YES; + // Paste is Command-V. + } else if ([characters isEqualToString:@"v"]) { + [fieldEditor paste:self]; + return YES; + } + } + } + // Let the super class handle the rest (e.g. Command-Period will cancel). + return [super performKeyEquivalent:event]; +} + +@end + +//============================================================================= +int main(int argc, const char *argv[]) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; +#if DEBUG + // Log to stderr in debug builds. + [GTMLogger setSharedLogger:[GTMLogger standardLoggerWithStderr]]; +#endif + GTMLoggerDebug(@"Reporter Launched, argc=%d", argc); + // The expectation is that there will be one argument which is the path + // to the configuration file + if (argc != 2) { + exit(1); + } + + // Open the file before (potentially) switching to console user + int configFile = open(argv[1], O_RDONLY, 0600); + + if (configFile == -1) { + GTMLoggerDebug(@"Couldn't open config file %s - %s", + argv[1], + strerror(errno)); + } + + // we want to avoid a build-up of old config files even if they + // have been incorrectly written by the framework + unlink(argv[1]); + + if (configFile == -1) { + GTMLoggerDebug(@"Couldn't unlink config file %s - %s", + argv[1], + strerror(errno)); + exit(1); + } + + Reporter *reporter = [[Reporter alloc] initWithConfigurationFD:configFile]; + + // Gather the configuration data + if (![reporter readConfigurationData]) { + GTMLoggerDebug(@"reporter readConfigurationData failed"); + exit(1); + } + + // Read the minidump into memory before we (potentially) switch from the + // root user + [reporter readMinidumpData]; + + [reporter readLogFileData]; + + // only submit a report if we have not recently crashed in the past + BOOL shouldSubmitReport = [reporter reportIntervalElapsed]; + BOOL okayToSend = NO; + + // ask user if we should send + if (shouldSubmitReport) { + if ([reporter shouldSubmitSilently]) { + GTMLoggerDebug(@"Skipping confirmation and sending report"); + okayToSend = YES; + } else { + okayToSend = [reporter askUserPermissionToSend]; + } + } + + // If we're running as root, switch over to nobody + if (getuid() == 0 || geteuid() == 0) { + struct passwd *pw = getpwnam("nobody"); + + // If we can't get a non-root uid, don't send the report + if (!pw) { + GTMLoggerDebug(@"!pw - %s", strerror(errno)); + exit(0); + } + + if (setgid(pw->pw_gid) == -1) { + GTMLoggerDebug(@"setgid(pw->pw_gid) == -1 - %s", strerror(errno)); + exit(0); + } + + if (setuid(pw->pw_uid) == -1) { + GTMLoggerDebug(@"setuid(pw->pw_uid) == -1 - %s", strerror(errno)); + exit(0); + } + } + else { + GTMLoggerDebug(@"getuid() !=0 || geteuid() != 0"); + } + + if (okayToSend && shouldSubmitReport) { + GTMLoggerDebug(@"Sending Report"); + [reporter report]; + GTMLoggerDebug(@"Report Sent!"); + } else { + GTMLoggerDebug(@"Not sending crash report okayToSend=%d, "\ + "shouldSubmitReport=%d", okayToSend, shouldSubmitReport); + } + + GTMLoggerDebug(@"Exiting with no errors"); + // Cleanup + [reporter release]; + [pool release]; + return 0; +} Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/English.lproj/InfoPlist.strings and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/English.lproj/InfoPlist.strings differ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/English.lproj/Localizable.strings and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/English.lproj/Localizable.strings differ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/goArrow.png and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/goArrow.png differ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/ReporterIcon.graffle firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/ReporterIcon.graffle --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/ReporterIcon.graffle 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/sender/ReporterIcon.graffle 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,2489 @@ + + + + + ActiveLayerIndex + 0 + ApplicationVersion + + com.omnigroup.OmniGrafflePro + 137.6.0.106738 + + AutoAdjust + + BackgroundGraphic + + Bounds + {{0, 0}, {512, 512}} + Class + SolidGraphic + FontInfo + + Font + CalisMTBol + Size + 112 + + ID + 2 + Style + + fill + + Color + + a + 0 + b + 0 + g + 0.852018 + r + 0.998962 + + + shadow + + Draws + NO + + stroke + + Draws + NO + + + + CanvasOrigin + {0, 0} + CanvasSize + {512, 512} + ColumnAlign + 1 + ColumnSpacing + 36 + CreationDate + 2008-11-14 16:58:15 -0700 + Creator + John P. Developer + DisplayScale + 1 pt = 1 px + FileType + flat + GraphDocumentVersion + 6 + GraphicsList + + + Bounds + {{33.9443, 35.3885}, {444.111, 437.112}} + Class + ShapedGraphic + FontInfo + + Font + CalisMTBol + Size + 112 + + ID + 31 + Rotation + 270 + Shape + Bezier + ShapeData + + UnitPoints + + {-0.5, -0.439247} + {-0.5, -0.485429} + {-0.446294, -0.512626} + {-0.409932, -0.494153} + {-0.373569, -0.47568} + {0.436363, -0.0733799} + {0.472729, -0.0549059} + {0.50909, -0.0364333} + {0.509091, 0.0364345} + {0.472729, 0.0549059} + {0.436368, 0.0733802} + {-0.373569, 0.475681} + {-0.409932, 0.494153} + {-0.446294, 0.512626} + {-0.500001, 0.485429} + {-0.5, 0.439247} + {-0.49998, 0.393072} + {-0.500002, -0.393066} + + + Style + + fill + + Color + + b + 0 + g + 0.770962 + r + 0.997971 + + Draws + NO + FillType + 3 + GradientCenter + {-0.609524, 0} + GradientColor + + b + 0 + g + 0.911574 + r + 0.998779 + + MiddleFraction + 0.6111111044883728 + + shadow + + Color + + a + 0.43 + b + 0 + g + 0 + r + 0 + + Draws + NO + Fuzziness + 7.2213706970214844 + ShadowVector + {0, 6} + + stroke + + Color + + b + 0 + g + 0.766903 + r + 0.997925 + + Width + 7 + + + Text + + Pad + 0 + VerticalPad + 0 + + TextPlacement + 0 + TextRelativeArea + {{0.06, 0.17}, {0.88, 0.5}} + TextRotation + 90 + Wrap + NO + + + Bounds + {{3.89085, 67.8908}, {404.218, 332}} + Class + ShapedGraphic + FontInfo + + Font + CalisMTBol + Size + 112 + + ID + 30 + Rotation + 270 + Shape + Bezier + ShapeData + + UnitPoints + + {-0.5, -0.5} + {-0.459695, -0.475464} + {0.429465, 0.0537758} + {0.469773, 0.0783133} + {0.510074, 0.102849} + {0.510077, 0.198357} + {0.469773, 0.222892} + {0.429473, 0.247428} + {-0.00521517, 0.499998} + {-0.00521785, 0.5} + {-0.00521713, -0.113381} + {-0.44962, -0.458615} + + + Style + + fill + + Color + + a + 0 + b + 1 + g + 1 + r + 1 + + FillType + 2 + GradientAngle + 180 + GradientCenter + {-0.609524, 0} + GradientColor + + a + 0.5 + w + 1 + + MiddleFraction + 0.6111111044883728 + + shadow + + Color + + a + 0.51 + b + 0 + g + 0 + r + 0 + + Draws + NO + Fuzziness + 3.3371961116790771 + ShadowVector + {0, 2} + + stroke + + Color + + b + 0 + g + 0.766903 + r + 0.997925 + + Draws + NO + Width + 2 + + + Text + + Pad + 0 + VerticalPad + 0 + + TextPlacement + 0 + TextRelativeArea + {{0.06, 0.17}, {0.88, 0.5}} + TextRotation + 90 + Wrap + NO + + + Bounds + {{33.9443, 35.3886}, {444.112, 437.111}} + Class + ShapedGraphic + FontInfo + + Font + CalisMTBol + Size + 112 + + ID + 29 + Rotation + 270 + Shape + Bezier + ShapeData + + UnitPoints + + {-0.5, -0.439247} + {-0.500001, -0.485429} + {-0.446295, -0.512626} + {-0.409932, -0.494153} + {-0.373568, -0.475681} + {0.436363, -0.0733802} + {0.472729, -0.0549062} + {0.509089, -0.0364334} + {0.509092, 0.0364341} + {0.472729, 0.0549056} + {0.436369, 0.0733803} + {-0.373568, 0.475681} + {-0.409932, 0.494153} + {-0.446294, 0.512626} + {-0.500001, 0.485428} + {-0.5, 0.439248} + {-0.499978, 0.39307} + {-0.500003, -0.393066} + + + Style + + fill + + Color + + a + 0.2 + b + 1 + g + 1 + r + 1 + + FillType + 2 + GradientAngle + 90 + GradientCenter + {-0.609524, 0} + GradientColor + + a + 0 + w + 1 + + MiddleFraction + 0.6111111044883728 + + shadow + + Color + + a + 0.51 + b + 0 + g + 0 + r + 0 + + Draws + NO + Fuzziness + 3.3371961116790771 + ShadowVector + {0, 2} + + stroke + + Color + + b + 0 + g + 0.766903 + r + 0.997925 + + Draws + NO + Width + 2 + + + Text + + Pad + 0 + VerticalPad + 0 + + TextPlacement + 0 + TextRelativeArea + {{0.06, 0.17}, {0.88, 0.5}} + TextRotation + 90 + Wrap + NO + + + Bounds + {{176, 102.384}, {158.841, 537.616}} + Class + ShapedGraphic + FontInfo + + Font + CalisMTBol + Size + 425 + + ID + 26 + Shape + Rectangle + Style + + fill + + Draws + NO + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Pad + 0 + Text + {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf350 +{\fonttbl\f0\fnil\fcharset0 CalistoMT;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\b\fs850 \cf1 !} + VerticalPad + 0 + + Wrap + NO + + + Bounds + {{176, 104}, {158.841, 537.616}} + Class + ShapedGraphic + FontInfo + + Color + + b + 0 + g + 0.749523 + r + 0.997726 + + Font + CalisMTBol + Size + 425 + + ID + 27 + Shape + Rectangle + Style + + fill + + Draws + NO + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Pad + 0 + RTFD + + BAtzdHJlYW10eXBlZIHoA4QBQISEhBJOU0F0dHJpYnV0 + ZWRTdHJpbmcAhIQITlNPYmplY3QAhZKEhIQITlNTdHJp + bmcBlIQBKwEhhoQCaUkBAZKEhIQMTlNEaWN0aW9uYXJ5 + AJSEAWkEkoSWlhBOU1BhcmFncmFwaFN0eWxlhpKEhIQQ + TlNQYXJhZ3JhcGhTdHlsZQCUhARDQ0BTAgCEhIQHTlNB + cnJheQCUmQyShISECU5TVGV4dFRhYgCUhAJDZgAchpKE + n54AOIaShJ+eAFSGkoSfngBwhpKEn54AgYwAhpKEn54A + gagAhpKEn54AgcQAhpKEn54AgeAAhpKEn54AgfwAhpKE + n54AgRgBhpKEn54AgTQBhpKEn54AgVABhoYAhpKElpYG + TlNGb250hpKEhIQGTlNGb250HpSZIIQFWzMyY10GAAAA + FgAAAP/+QwBhAGwAaQBzAE0AVABCAG8AbAAAAIQBZoGp + AYQBYwCiAaIAogCGkoSWlg1OU1N0cm9rZVdpZHRohpKE + hIQITlNOdW1iZXIAhIQHTlNWYWx1ZQCUhAEqhIQBZKYD + hpKElpYHTlNDb2xvcoaShISEB05TQ29sb3IAlKIChARm + ZmZmAYN4dz8/AAGGhoY= + + Text + {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf350 +{\fonttbl\f0\fnil\fcharset0 CalistoMT;} +{\colortbl;\red255\green255\blue255;\red254\green191\blue0;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\b\fs850 \cf2 \outl\strokewidth60 \strokec2 !} + VerticalPad + 0 + + Wrap + NO + + + Bounds + {{33.9441, 35.3884}, {444.112, 437.111}} + Class + ShapedGraphic + FontInfo + + Font + CalisMTBol + Size + 112 + + ID + 16 + Rotation + 270 + Shape + Bezier + ShapeData + + UnitPoints + + {-0.5, -0.439247} + {-0.5, -0.485429} + {-0.446295, -0.512626} + {-0.409933, -0.494153} + {-0.373569, -0.47568} + {0.436363, -0.073379} + {0.472729, -0.0549049} + {0.50909, -0.0364324} + {0.509091, 0.0364344} + {0.472729, 0.0549058} + {0.436368, 0.0733801} + {-0.373569, 0.47568} + {-0.409933, 0.494153} + {-0.446295, 0.512626} + {-0.500001, 0.485429} + {-0.5, 0.439247} + {-0.49998, 0.393072} + {-0.500002, -0.393066} + + + Style + + fill + + Color + + b + 0 + g + 0.770962 + r + 0.997971 + + FillType + 3 + GradientCenter + {-0.609524, 0} + GradientColor + + b + 0 + g + 0.911574 + r + 0.998779 + + MiddleFraction + 0.6111111044883728 + + shadow + + Color + + a + 0.9 + b + 0 + g + 0 + r + 0 + + Fuzziness + 8.0632610321044922 + ShadowVector + {0, 9} + + stroke + + Color + + b + 0 + g + 0.766903 + r + 0.997925 + + Draws + NO + Width + 2 + + + Text + + Pad + 0 + VerticalPad + 0 + + TextPlacement + 0 + TextRelativeArea + {{0.06, 0.17}, {0.88, 0.5}} + TextRotation + 90 + Wrap + NO + + + GridInfo + + GridSpacing + 4 + ShowsGrid + YES + SnapsToGrid + YES + + GuidesLocked + NO + GuidesVisible + YES + HPages + 1 + ImageCounter + 2 + KeepToScale + + Layers + + + Lock + NO + Name + Layer 1 + Print + YES + View + YES + + + LayoutInfo + + Animate + NO + circoMinDist + 18 + circoSeparation + 0.0 + layoutEngine + dot + neatoSeparation + 0.0 + twopiSeparation + 0.0 + + LinksVisible + NO + MagnetsVisible + NO + MasterSheets + + ModificationDate + 2008-11-17 11:41:28 -0700 + Modifier + Preston Jackson + NotesVisible + NO + Orientation + 2 + OriginVisible + NO + PageBreaks + YES + PrintInfo + + NSBottomMargin + + float + 41 + + NSLeftMargin + + float + 18 + + NSPaperSize + + size + {612, 792} + + NSRightMargin + + float + 18 + + NSTopMargin + + float + 18 + + + PrintOnePage + + QuickLookPreview + + JVBERi0xLjMKJcTl8uXrp/Og0MTGCjQgMCBvYmoKPDwgL0xlbmd0aCA1IDAgUiAvRmls + dGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAGVlktvJEUQhO/9K5IbHFyuR9brioEV + nFh5JM5o5MWsPIA9IP4+X2b12gPrBbHWSt3lzkdFREb6Ud7Ko0R+asr+/+lOfpBfJIYW + /Z/8LNc35yTHsyROh/ZYxyTgfJTrN/zip7NcxVD3r1+e9oQbCd/J9fd3T8e7337/48cH + eSLjm3O2wEfR2ry8tiJ5hDJSVEk9pF7jkONJrr89JfnqV/p8uz1KK1YpJynNHlKVk2gJ + JZWqdqZz9iklBc21ZmkxlJoin0/vsEonTo6b9lC79iQzh6azSc7FYjINZWIkk4MCKtrG + ejhyRpZeRYcSNIbk7oXmtk5m4mRaD/NvYcOK1bKnpnkuu4qt6jqEVujLmtuOvFgdrXv7 + mcjWQxut71ds5LcbAIADIcTkDgpt4TKmyigWoXNzVAYtfYzdUe5fOT25ACxMtZAQiPeX + xEVTSCkNeaBLDbXWDhwfnxH1QHJX0sfiulDDhhpMgMnUJZAGDLkCea3T6b+9T3K+N/pf + F6qL8+ZW0hYDjM4ESlFubyTlAFd/kvfwRKilj3IFRdTQHJsk6EwzW5UvDwBY1xf2cNVL + SDWiyTa2AyL8JgXr8fBOPv/sCzm8l68PNERtwm0wGIb4yTrK2LiYt3+rI5+uY1df7JW8 + CD9tS/XNGUdxFSUs1e+yiQPuXPUMyVI9lL2qeh2bq16Rjet+qRdVLcWrceySz8+S30+A + zyTPTNiYWMQSe10Z64vY+/OoudZNus9dudwRqE+rVVty97v63bZd7iZHL7PkjnfYe5xw + vvTOAJtW5+gMv3vFB8RetF6yzYQ5x3/L08wKeQZ3t1pin5Fp1GpD0ORKy7AnlLN/kbPS + 2ofZwIlqwA1G35aT5d3JyGncLARwMKZb0Tt2gIAHLOBGpTJExgtaxZ/MjxbK+B8mYdQ0 + 5QuYoSumBgvBBXEsP0n9khlidnI8xrK6LZqBzVm2bFzEhIjMiIwcPyGGeQqjdjrwT7h+ + LYHiADxbwGHg6+Uux+3+4u1/I6yj5DSiaKw0CBBXpRSDluldCFM4zgHvPa9zujJygMR3 + RXlB+JWt1t0PvmNg35PwHxsOE4mw1Weu0cykNci2JJjJhX+sVUm1pt4BgIOOr6HBGsLd + eYUt0uRFYFIEgAl4n6yrBqw6QuzKxtA0wdf4g/OZ2QWMAd4DfUgXOqHaYjtc4/Gjshmh + y/PP/YQ62VDzj4dlZttYGh2ZHAwCzaCeVcoaJty3VGm2b4bnZwuhC2LommlOA9lxF2ub + WDS6QrjdWjcjNZJ3Uzh/OyA6IjK7cIVwj0t8fPwuD05ya6b+F7C1v1cKZW5kc3RyZWFt + CmVuZG9iago1IDAgb2JqCjk4MwplbmRvYmoKMiAwIG9iago8PCAvVHlwZSAvUGFnZSAv + UGFyZW50IDMgMCBSIC9SZXNvdXJjZXMgNiAwIFIgL0NvbnRlbnRzIDQgMCBSIC9NZWRp + YUJveCBbMCAwIDUxMiA1MTJdCj4+CmVuZG9iago2IDAgb2JqCjw8IC9Qcm9jU2V0IFsg + L1BERiAvVGV4dCAvSW1hZ2VCIC9JbWFnZUMgL0ltYWdlSSBdIC9Db2xvclNwYWNlIDw8 + IC9DczIgMTIgMCBSCi9DczEgNyAwIFIgPj4gL0V4dEdTdGF0ZSA8PCAvR3MxIDE3IDAg + UiAvR3MyIDE4IDAgUiA+PiAvRm9udCA8PCAvRjEuMCAxMSAwIFIKPj4gL1hPYmplY3Qg + PDwgL0ltMiAxMyAwIFIgL0ltMSA4IDAgUiAvSW0zIDE1IDAgUiA+PiAvU2hhZGluZyA8 + PCAvU2gxIDEwIDAgUgo+PiA+PgplbmRvYmoKMTAgMCBvYmoKPDwgL0NvbG9yU3BhY2Ug + NyAwIFIgL1NoYWRpbmdUeXBlIDMgL0Nvb3JkcyBbIC0yNzEuMzA2MyAwIDAgLTI3MS4z + MDYzIDAgNTQwLjI2NApdIC9Eb21haW4gWyAwIDEgXSAvRXh0ZW5kIFsgZmFsc2UgZmFs + c2UgXSAvRnVuY3Rpb24gMTkgMCBSID4+CmVuZG9iagoxMyAwIG9iago8PCAvTGVuZ3Ro + IDE0IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2UgL1dpZHRoIDI1NiAv + SGVpZ2h0IDI1NiAvQ29sb3JTcGFjZQo3IDAgUiAvU01hc2sgMjAgMCBSIC9CaXRzUGVy + Q29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB7dABAQAA + CAKg/p+2Bx4QJpBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg + wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM + GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg + wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM + GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg + wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM + GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg + wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM + GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg + wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM + GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDDQBg4DBgwYMGDA + wNjAA65NNU0KZW5kc3RyZWFtCmVuZG9iagoxNCAwIG9iago4ODMKZW5kb2JqCjggMCBv + YmoKPDwgL0xlbmd0aCA5IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2Ug + L1dpZHRoIDkxMiAvSGVpZ2h0IDkyNiAvQ29sb3JTcGFjZQoyMiAwIFIgL1NNYXNrIDIz + IDAgUiAvQml0c1BlckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0 + cmVhbQp4Ae3QgQAAAADDoPlTH+SFUGHAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + wMvAAKraAAEKZW5kc3RyZWFtCmVuZG9iago5IDAgb2JqCjExMDcwCmVuZG9iagoxNSAw + IG9iago8PCAvTGVuZ3RoIDE2IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1h + Z2UgL1dpZHRoIDI1NiAvSGVpZ2h0IDI1NiAvQ29sb3JTcGFjZQo3IDAgUiAvU01hc2sg + MjUgMCBSIC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4K + c3RyZWFtCngB7dKBDQAgDMMw/n+6SHBGvA+aeXMKFAucd8XlNiuw8U9BuQD/5e/bzj8D + 5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+ + vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889A + uQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/ + bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQ + LsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv + 284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyU + C/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7 + tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPl + AvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+ + 7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5 + AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79v + O/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1Au + wH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/b + zj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL + 8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2 + 889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC + /Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t + /DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA + /+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287 + /wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7A + f/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vO + PwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvw + X/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu3f/wUgwjJ6CmVu + ZHN0cmVhbQplbmRvYmoKMTYgMCBvYmoKMTIxNAplbmRvYmoKMjMgMCBvYmoKPDwgL0xl + bmd0aCAyNCAwIFIgL1R5cGUgL1hPYmplY3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCA5 + MTIgL0hlaWdodCA5MjYgL0NvbG9yU3BhY2UKL0RldmljZUdyYXkgL0JpdHNQZXJDb21w + b25lbnQgOCAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHsnYlfTV37/288 + j4xJUVJRGtCEoggliZCxyFQUlUiGNCgJIXOFyFTKlCljmUMlUaZK4XY/N31fv//jd621 + 9z57n6nxDPvU1ev1PHfts89a61zn+njvz7XW3uuvv/AHI4ARwAhgBDACGAGMQOeIQDfy + 0132hx7tHB8QPwVGQCciwOiwR48e//nPf/4r9QMH/gPHQaRwjk58FhwkRkCHI0ClSIT4 + 3//27Kmn10vuR0+vZ8///peoEjWpw98zDl0HIkAuT0GLIEWqxN59+vTt269ff8lPv379 + +vbt07s3iJSoEkSJmtSBrxWHqIsRoGKkWuzVqzcRYn/9AQMMDAwGDhxoyPwMHAh/GgzQ + 1+/fD1QJmtQDTTKc1MUPjGPGCIg1AqwYe8IFKmiRSNFgoKHRoEGDjY1NTIbAjyn5PxMT + Y+PBg4wMQZcD9PszmmQkiWZSrN8sjkv3IgBqJI6xJxEjaNHAAKRobDLE1HSombmFxbBh + w+nPsGHDLMzNzYaampqAKkGUA/SBk+TSFShJLlx174PjiDECoosAMY0gRj0qRuCi0WDj + IaZDzS2GWVpZjbC2sbG1tWN+bG1trEdYWVkOH2ZuBqIETRoYEEyCJOl1KypSdN8tDkjX + IsCqsVevPn37DwAwDjYBLYIUR9jYjRxlb+/g6OjkDD9j4H9OTo4O9vajR9nZgiyHW5iZ + DjEmmNTvB2aSgSQqUte+fhyvqCLAqJGiUZ+QcYipmYWllbXtyNH2js5jxrm4uI6f4Obu + 7j5xIvyfm9uE8a4u48Y6OznYj7KzGWE1zHyoKUjSYED/vqhIUX2vOBhdjAD1jWAbwTXq + GxgOMiFiHGEzcrSD0xgX1wnuEz0mT5nq6TVtmrf39One3t7TvLw8p0yePMndbbzLWGdH + +1G21lbDzU2HDDYayCmS+EhdjASOGSOg9QiQKg4p4vTpBxeqgEbz4VY2I+0dQYtuEydP + 8Zw23cd3pt/sOXPn+jM/c+fM9pvlO8PH22vqZA/3CS5jnQCT1pbDzACSAw0oI0llBy9a + tf7N4gB0LwIUjlDFoWwcPGSohaW13WgQ44SJk6dOmz5j1uy5/vMXLgoIDFyydGkQ+Vm6 + ZElgwOJFC+b5z/Hz9fH2nDzJzXUcSNKGQBIUSRlJa62ISN1LBxyxdiNA4QjGsS9cqYIa + hxE0Oo8bP3Gyp/eMWXP8FywOXLpsxargkNWhYWFr165bt27t2rDQNauDg1cuDwoMWDjf + f7avz7QpHm4uYxxH244gijQyGNCvDxR2EJHa/Waxdx2MAJEjgWP/AQMHmQy1sLIZ5TDG + xc3Dc7qvH2hxybKVwWvCwiPWb4jeFBOzecuWrVu3btmyOSYmemNUZPja0JBVy5cGLpo3 + Z6aP1+SJ4wGSdiOGw1WroYE+FHYAkegidTAlcMhaiwC5ViV1HICjkbGpuaU1qNF14uRp + Pn7+CwODVoaEhkdu2LR5a2x8QmJS8s6UXbt2pe7alZKyM2lHQnzcti0xG9eHr10dvHzJ + YpDk9KkeE1yc7G2thg0dAhetBJHURWrtw2HHGAHdigDI8T//Jc4R4DjEbPiIkfagxine + vnPmBwStXL02YkPM1u3xO3bu2r133/709IOHDh0mP4cOpqcf2Je2JzUlKSFu2+bo9eGh + wcsDF/r7+Xh6TBjnNNrGyoJctFJEkmtW3YoJjhYjoKUIsNaxT38DQ2NTCytbe2cXUOPM + uQsDV4SsjdgYExuflLI77UD64aPHMzNPnDyVnX0afrKzT508kZWZcezIwfR9e1KTE+O2 + booKX7MqaPG82T5eHhPGOoy0Hm4GiNTvR65ZUZBa+naxWx2LACtHcq1qYjbcepTjOLfJ + RI1LVq4J3wBiTE7de+DQ0Yysk9k5OecuXMzNu3QpH34uXcrLvXjh/NmcM6dOZB47nL5v + d8qOuC3RkWHBywLmASMnuY6Bi1Zz08GG5JoVBaljWYHD1VIEmEoOWEe4VjUHOI4Z7+E1 + g6gxNCJ6S3xSalr64eNZp86cvZB7Kf/y1WvXC2/cuHETfuA/NwqvX71SkJ938VzO6ROZ + Rw/u370zITZm/TqiyFnTp7i5OI4cMYyUdfqjILX07WK3OhaBbt2gsNqrN1jHwXCtOtJh + nNsUH7/5gSvXRERvjd+5+wCIMTvnQl7+lWuFN27dKbp3/wH8PCQ/8N/79+4W3b554/rV + gksXz505mXk0PS11x/aYqLXBQYvm+np5uDqPthk+1MSICFIPL1l1LDVwuFqIAJRyeuox + chw2YpSTyyQvX//Fy0LCN25NSNmbfjQTxHjp8jWQ4r0HDx6VPH7y9NmzZ8+ZH/jt6ZOS + 4ocP7hfdvnH9Sn7uuTMnjh/al5q0PWZ92KrA+X7TJ7uNtbe1NBtCBYmXrFr4erFL3YoA + raz27jtgoLHpMOvRY8ZP9vabv2TV2qjN8Tv3ph87ceZ8XsG1G7fvghSfPHv+ovTV6zdl + ZeXsT1nZm9evSl++ePb0cTFo8lbhlfyLOacyD+9P3RG7KXz1ssVzZ3i6uzjYWZmzgsQq + q24lB45W0xEg8456vcA7GpsOt7EfA9eqcxYtWx2xaXvSblBjzoX8qzdu33tQ/OTZi9LX + b8rL31a+q6p6//4D8/P+fdW7yrcV5WVvXr18/rTk4f2im9cLcs9mZx7atythS9TaVYHz + ZnpNcnEUChJnPTT9FWN/uhMBiRwHEzmOdZs6wz9gRVjUloTU/UeyzlzIv3ajiIjx5euy + 8rfvqj5UV9d8/Pjp8+fPX+AH/vPp08ea6uoP799VVpS/KX3+rOThvduFl/POnjp+cE/y + 9k3hwUsX+Hl7uDqOpISEaQ9YqYOC1J30wJFqOAK0ltNH32AwXKzaj3X39PUPDF4XHZu8 + 52BG9vn8azfvPighYqyoBC2CEL98ra2rl/qpq6v9+uUzqPL9u7dEkk8e3QdF5uacOLp/ + V/zmyDXLFs6ePpkI0swEpj16Y01Hw18wdqdLESDmEZasGgzi5DhvSXB4THzK/iMncvKu + 3Lj74PGz0jflle+raz59+QpK/NbQ0Pj9+w/Jz/fvjY0N3+rrQZSfP1Z/qHpb/vrF0+L7 + t6/nXzidkb57x9ao0OWL5hBB2lkONTYc0LeX3n96ICB1KUVwrBqMADGPvfsZGA2xGDF6 + jJun77ylIRExCanpx7MvFBTeATW+KntbRcRYW18PSvzx8+fff/+S/vn7758/vhNR1hFJ + vq8kinx07+bVvJysw2nJsRvDViyeM93DxcF2+NDBA/VhGhKvWDX4BWNXuhQBMtXRq+8A + QxMzq5HOE6b6zlsSErk5cffBzJzcqzfvFT8rLXv7vvojiPFbA2iRKPGf//3vf/8KfuDP + f/6B43//BE0SSdZ8eAeKfPLgTmH+uZNH9+3cvnHtikWzvSe52NsMMx1k0B+vWHUpQXCs + mowAKebA1erAwUMt7RzHT57hD3LckrjnUFbOpet3Hjx58QbY+AkuU0GMoMV//gEl/v79 + h/w00R/66+/fv//993+gSSLJb/W1oMjK8lfPiu/duHwh+9j+lO0bw5Yv8ps2cezoERZD + jAb0Q0Bq8ivGvnQoAlDMIVerYB5tHFwm+0ApJ2Jz4p7DJ87l37j76Nmr8ndEjd8af/wk + XAQtskL8P+EPEeafP6BJKskf3xuIIt+/ffPi8YNbV3NPHz9ABLlswSxPN+dRVmbG9IoV + V5brUJLgUDUVAcnVqvmI0eMmes8JWBUeQ+VYcONeyfM3bz/UfCFq/JsTIyhPqET+d4Ek + AZIN9V8/Vb8rL3368Pa13DPH9++M3bAmaN6MKeMdbYebDmauWLGko6kvGfvRmQh0I8Wc + /gaDh4J5dPOctXDFuk3xuw+dOFdw4/7jl2UAx9r6BkaNQEalYmRlSSlJIAmK/Fb3peZ9 + xetnj+5cA0LuT962PiTQ38fDxd6aXLH2xStWnUkRHKjmIsDg0cCIXK26Tp4xLyh0Y1zq + wayzIMcnpeVVBI7ffxI2UjXyNCS//T/mR+ogoSS9bP37RyMg8sNbRpDZR9N2bA5fFTB7 + mvsYesXavw9MQiIgNfdFY086EQHAY68++gNNyNXqpOlzA0PWx6akZ+bks3L8SuGoSI2s + GOUlKVEkILL2c3XlGyDk1YunjuxJiFm3fIEvc8U6yABLOjqRHzhIjUaA4pEUcyztnCZ4 + zlq0KnxL0r7jZy4V3nv8svz9x6/1jSwcpV2jlBjpH0JIMletcNH6vaGOCvLh7SvnTxxM + jdu4ZilcsY6zH2FuYgiTkAhIjX7X2Jn4I8Dg0XCIhbU91FbnBYVtit9zJDv3+t2SF+VV + rBzlLlXl1SgrSeai9dffP4gg4ZL1wc2Cs5kHdm6NDF4828vNeaQllHQQkOJPDxyhZiPA + 4pEUc8a4T5sTGBIVu+vgifNX7jx6XlZV87WeWEeQoxQclaiRHOYhSRBJ6jpEkB8qXj25 + f+PSmWNpiTFrl8+fMZmWdBCQmv2qsTcdiACHR1LMmeK7cEX45qT9GTn5Nx88e1NZ/YVc + rBLrKJRjM2qUU+RvIshvdZ/el78sKbp28eSh1LgNqwPnek+Eks5QBKQO5AcOUaMR4PE4 + auzE6f5L1myM330kO6/w3pNXb6s/K5BjC2qURSQI8m8o6nysKnv+8Ba5Yk3eEr5y4cwp + rg42w4YgIDX6XWNn4o+ABI+2juOnQjEnYuvO9Cy4Wi1+Uf7+U13DD9mL1ZblKFQkMZH/ + g6JO/ZfqytdP4Yr19NE98dGhS/2nTxqLgBR/duAINRwBDo9mVjDX4eO/NGxTwt5jZ/Jv + wNXqu5qv32Tl2Co1ygvyZ2P95w/kivXqhRPpKdsiVi2aNXW8IwJSw182dif6CFA8DjAa + MszWEeY6FgdHQjHn5MVrdx+XVhDz+Pev/wm9Y2vlKCjrACH/JRaSXLFCjTU/53hawqaw + IH+fSeNGo4MUfX7gADUaASEePXz8g8JiEvdl5BTcevgcpjrqGkgtR1DKab0cBYgka3XA + QsIV69tXj+9ev3jy4K5YmPPw85zgaIsOUqPfNnYm9ggw7pHgEZYC+C0OXh+beuhU7nUo + 5lRWw1TH3zDTIamstkmN0oL836+fDaTGSko6ORn7EmMIID0AkGZYYhV7iuD4NBcBaTzO + C1obs2NfxtnLtx/RYg6Z6oCrVXZGsa1ylFyzMles3799ra589eReYe6pQ6mx6xGQmvuW + sSddiYA0HgOC128HPOYV3n/6upIt5kiuVtsuR16QpMb6s5EA8sWj25fPZuzbEbM2aB4C + UlfSBMepmQjI4HEZwWMmg8cPMPX49y+yEKDdeOQvWdmSztcaMuehAJB4m4dmvm/sRdwR + EMw9gnukeDwsxGOHrlYpUBkxk5LOr79hzgMBKe6EwNFpMwLK8AhLATg8csWc9lytkvew + cKWrAn58EwByu5SDREBqMw+wb3FEAPCo10ffkJl7ZPAo5R55PLZXjpwgm5rInEdj/acP + 5S+KJQ4S5yDFkQc4ClFEQBEeSXGVw6NgrqP9emQJKQ9IpsQKj9Khc5AISFGkBA5CixGQ + xiPMPcoUV1WBRyEgqYMEQHIlVlyko8UvH7sWWwQ4PA6lK1f5uUd5PHaAjhILqRiQszy5 + VawISLHlB45HsxEQ4HE8vzSHn3tUDR45QRIHSQBJHCQAUrJIZ9I4vM1Ds9879ibKCEjj + kaxcZZbmcHiEuUe2uNoxPEquWMmiAGGJla5i5W/zQECKMk1wUBqKgASPNuS+R27lqhrw + yAqSAhLuu2IAya9ixfsgNfSFYzdijgCPx1Fj4b5H9sYOunL1wydmaY6K8MgD8l8OkE/u + Xc89eYgHpAV5UAACUsz5gmNTbwRk8Qj3PcKNHWrBIwdIskiHAJIs0oHbPI6nJW4Kg2c/ + 8oDs0V29nxlbxwiINQJkf7l+ZIcAIR4LmBs7yENzVOYeifvkVs3RG5PhNo/XFJDkPkj6 + oAAHGwSkWPMEx6WRCHB4tGDdI8HjSXLfI3tjB7kNmVFRR6s59P20KTrlIQ9IwZN0EJAa + +fKxE9FFoHsPHo/TyVNzEtOOw2MB6H2PDB5ZPapEjgoBSR4UsE0CSPKwcr3/dsfdPESX + Kjgg9UegW3dm5aqFjQN9qFzkNnhqDoPHauYhVirFI+8gufsgnzMOMmGT5FFzdDcPBKT6 + v3vsQXwRYPA4iLpHwGOoEI91wscCqAaP8g4SHhRAn6RDHjU3cyo8i9UCANlb7z8ISPEl + C45I3RGAyQ5yY4eJFB4vsk/Noc94VDEeFQCSPEmHPGqOAHIieRbrIAPYDxIBqe7vHtsX + XwTk8JjAuMfn5BHI6sCjUkDSZ7GSh5VbIyDFlyc4Io1EgOCxN4NH16kz4ZHkxD2qF48K + AQnPYt2bQB9WDrt5WJoiIDXy7WMnYosAwWNfA+oeYccOcI8MHuGZq2rDoxQguUfNXb/I + PqycBeTA/uggxZYrOB61R0CCR2vY0IriMUX9eFQCyGMEkEv8Ybsr2A8SAan27x47EF8E + WDyaWo4cQ/EYnbD3eE4+eSS5GvEoB8i3r57Aw8oBkFsjmO2uwEEiIMWXLTgiNUeA4rH/ + QBMLHo/pJy5ev0v2l/v67TvsZ6Xy4iozayJcpMM+rDw/59je+Og1S8h+kBJA4poANWcA + Ni+mCAjx6O2/JBTweEwDeJQGZP1XupvHNbLdFQtIewSkmNIEx6KZCEjjceHKiK0pgEfY + 0IrgsV6NeJRykLCbB9mgFba7kgPkf3sgIDWTCtiLCCLQjSmuUvfoPXfJmuh4gseb6naP + 5JJVcJsHt90VAeTOrbBhsu8UV3trc3SQIsgQHIIGI8Dj0R6KqwtXhhM8XmDw+EW9eJTo + kezm0VBH9oMUAtKdc5AISA3mA3al3QjAjVZk7lEej2VVsDu5cEMrVS1dFbQjC8jSx3d5 + QE52QUBqNzewd41HgMOjuTXg0ZfgcafG8MhfsEoB8szRPfEbSYmVAeSAvr0QkBrPC+xQ + OxGgeBxA8ehO3ONGcI9nwD0+K2O3Q5ZsaCXAmup+VQjIrPSdW8JXLPRFQGonJ7BXrUVA + gEeXySwes4h7LH1brW73SFTN6ZEH5IOb+RSQqwPneLs7kzlIBKTW0gM71nAE5PG45yjg + 8YGG8MgLEnbXoSXW0sdFVy8wgFxAADkCS6waTgnsTnsRkMbjghXhW3amaxKPvB4FgLwh + AeQ0d2c7S1MjBKT2MgR71mQEeDw6u0+bE7h6Y7yG8cgLUgjI81npyeAgWUAawyrWnvig + AE3mBfallQgAHnv27j/Q2HyEPbhHDo9Xi1j3+LfaVq4KCkJSDrKWzEE+kAbkcASkVpID + O9V4BBg8Gpla2gnxeENz7lFY0mEBWVFaUnSVA+QM6iCNDRCQGs8N7FDjEZDHY3J61nmC + xwpSXNUIHvkLVuogOUCePro7fgOUWKe5OdshIDWeGdihNiLA4XG4nbMbcY8bqHtk8Fir + 7qU5/CUrd8UqBcjMA8mbwUEiILWRGNinNiLA4NGAcY8ziHtk8FiiUTwqBuSl00ekAdkH + F+loI0ewT81FQB6Pu4+eztc4HnlBygNy+XwKSDN0kJpLC+xJOxGQxuP8FeGbkw9kgnvU + NB55PXIO8g2UWAkg4zaEBM72cnOiDhIBqZ00wV41FAGCxz4DjEwl7jFu95HTlwCPb6o+ + atA9SkqsTU1/KCA/V1e8LCm6cp44yHU8IPv1wjlIDWUGdqOFCFA89jMwNiNzjzPmL5fg + 8WVF9We2uMrsaMVXXtT1G1fRAUD+aKj9+A4BqYWEwC61GgEej05uXrMDQzZI8PhO03hk + r1ilAHnnyrnMA0kcIK3AQSIgtZov2LlaI0DwCPuvSvC4bnPSgcxzV+6UaAGPUg6SB2T2 + kdS4KMZB2pI5SHSQas0IbFybEZDFY5QUHn/8+p9673uUvfKlV6wSQH6oeFnMADIGHKQP + LNJBQGozWbBvdUdAgkcrcI8+85drF4+ygKyhDjIv+zABZMBsrwlOtsMQkOrOCWxfexGQ + 4NGWcY9RcalHsmlx9V1NbYPG8ShxkL+ZEisHyP1JMeuWzfPxGDfaymwwOkjt5Qv2rNYI + yOExJmk/cY/FLys+aLq4yly6Ckus32p5QG6PCmYBOcRIHx2kWrMCG9dWBDg8DrN1muA1 + OyAkanvq4ew8MveoJTxKAbKx/vOHcsZBAiDXIiC1lSbYr2YiwOJxsJnV6HEePvOWrZPg + sRzw2Ag3dvxu0tTcI1fZkQfk/RvEQRJA+nkSBznEEAGpmfzAXjQbAYpHfaMhBI+efgHB + UniE7ck1XFwVXLE2NREHyQHy8rnM/TsQkJpNDuxN0xGQxePamB2se9QeHqVLrMRBvn7K + AnI9BaQjA0g9fBarptMF+1NzBBg8GgIeHSke1zPu8f7T1+AetYVHeQf5ovj25bMISDUn + Azav7QgoxOPZy1Bc1SYeZQD5lQKyMO8UOMj1wYvBQSIgtZ042L9aIgB41OujL43HU3k3 + tI1HJYDM2AcOMoiZgxyKc5BqyQhsVIsRkMFjELjHfZlnL98ufqFdPMoBshIcZGHuqUOp + sRSQ4x1taIkVHaQWkwe7VnkEpPG4OBjc46FTeYWAx8qar9pzj6TIyq5ilZRYqYMkgAwL + 8veZBIt0EJAqTwdsULsR4PA4FOYeJ/n4UzxmiAKPzQNy1lQEpHYzB3tXRwQkeLRxHO85 + C/AYC3jMFQUeBYD8xcxBvngEJdaMfYkMIMeOQkCqIyWwTe1FQBaPYeAeCR4fse7xlxaW + 5nBLdJQCcldsZPBiBKT2sgZ7VlcEhHicCniMFBMehYD82Vj/6X05ALIgJyNNFpA9uqsr + PtguRkCDEeDxOGoscY9hMYn7MnIKKB4/wcpV7eJRASCf3CvMPXmQB6QFWcWKJVYNpgx2 + pcYIcHi0APfI4HGXeNwjX2L98/vfXxwgbxXkHE9L3BS21H/6JImDRECqMUmwaY1FoHsP + 8tScwUOtWDxuSkxj8fge8PhT23jkAfnvP79+fPtaXfn6yb3rBJDbIlctAgfpYMMCsns3 + jcUMO8IIqCkCMnhcFRm76+DJ3Ov3nrBzj5rYYI6v3ij8jZmDZABZRx0kC8hQDpCDyIMC + EJBqShFsVoMREOJxuv/SMMDj8ZyCW1Bc5fD4R9P3PcqKkrsPkgPkKwDkRSlAmhjq99bD + /Vk1mDbYlXoi0K07s3LVwsYB3OOiVZHbAI8XKR6rydIcEeCRK7H+IZsHNBJAPn9EAZmw + iQByInGQgwz6IiDVkyHYqiYjwOBxEHWPgMfQTQkMHp8DHuvAPUruQ5aFlib/VgLIlG0R + qxbNnOoKDhIBqcmkwb7UFQGY7NDrrW9oAnh0JXiM4PD4qlI8eFQAyIe38qHEygJyzCgr + UwSkulIE29VgBAge+xpQPE4ULR7lSqxvWQfJAHKKq4O1hcnA/uggNZg42JU6IkDx2J/B + 45SZBI8pjHsUFx6VAPLY3oTo0CX+3hPHjLREQKojP7BNzUaAxaOp5agxFI/RCXuP5+Tf + eigq90icqsBBfoc5SADk3esXT6SnbI1YuXAmAlKzWYO9qSkCDB4HmlhYO7iyeEw/QYqr + r96KyT1KBNkkKLESB0kAuWbJXCEgcU2AmnIFm1V/BCR4HDlmorf/klDA4zFR4lEakPVf + AJCP7167wAPSHh2k+tMFe1BvBKTxuHBlxNYUwOO1uxSP9d/FMffIzatwi3SYOciqsucP + bxJAxksDEh/9qN6UwdbVGIFuTHHV1JLgce6SNdHxBI83xeceJRes/0c2MP/1XQDInVvD + Vy70neJqb22OJVY15go2rfYIcHg0t7YH97hwZTjB44Vrdx+De/wiNjxKKjpkkU5D3ceq + smcsIDcSB+nOlVgRkGrPG+xAPRGAleRk7pHg0V0Kj2VVn+oaRLI0h7tclXeQpYyDZAAJ + +7MiINWTJtiqZiIgxONkX4LHnSLGI69HHpAPbuafObonXgDIAX17ISA1kz7Yi4ojQPE4 + gMfjRtY9Piur+iiFx//jGaXV34RzkMRBUkBmpe/cEr5igS8CUsXpgc1pNgICPLpweMwi + 7rFUjO5RWNGRB+TqwDnT3J3JIh0EpGazCHtTVQR4PDqDewxcszF+z9Ez+TcfiBaP/BUr + 3cCcArLo6gUBIEeYG8Mq1p54H6SqcgTb0VgEJHgcYQ94XLAifMvOdMBjEYtH2H+VvQ1Z + LFerQEjuglUAyBuMg2QAaWdpaoSA1FgKYUcqjIAEj3bO7tPmBK7WATzygmQBWVH6uOjq + +az0ZM5BIiBVmCHYlAYjAHjs2bv/QGNzKTxeFTUeeT1SQNaSOcgHDCA3EEC6OdsNR0Bq + MImwK5VFgMGjkelwKTzeoO6xVjj3KKLLVcEVqwSQJTwgZ0CJFQBpgA5SZVmCDWkoAgwe + DRg8ziDuMTk96zzBYwVZmiNK9yhXYuUAefro7ngEpIYyB7tRRwQEeHQj7nFD/G4orooe + j/wVqxQgMw8kbw5fPh8BqY5UwTbVHwE5PG5OPpAJeCwROR55PXIO8g04yEunj+yO2xDC + O8g+uEhH/TmEPaguAgrweOT0JR3AIyvIJnKbx9/kNo+KlyVFV84LAWlmbNAP5yBVlyvY + ktojwOHRjBRXZ8xfHq4zeJQD5DshIGd7uTnREisCUu05hB2oLgIEj30G0OKqm9fswJAN + cbsZPL6p+iji4iop6MgA8jMB5J0r5zIPJG1exzpIAsheuEhHdemCLak3AgSPsIOOsTQe + rxSVvKyo/swWV7W9RQCjPUX/L1ik86Oh9iMPyKiQQAJIWzIHiYBUbwph6yqMAI9HJxk8 + vgM8/uAfSS6uuUdOnMyDO1gHKQNIH5iDtEJAqjBZsCl1R0CCRyvGPa7bnHQg89yVO7qB + RykHCYCsYQCZfSQ1jgJyAgJS3QmE7as0AhI82jJ4jIpLPZJNiqtvdAKPsg7yQ8XLYuIg + 9yfFgIP08SCAHIwOUqUpg42pLwJSePSZv3xdDIPHYp1wj+SiVdpBMoDMyz6cuj0qJGC2 + FwBymKmRPjpI9aUQtqzCCAjwOMFrdkAI4PFwdh7FY40OuEeJIJuafsMcZGP95w/lPCCX + zfPxGDcaAanCfMGm1BoBFo+DzaxGj/PwmbcM8LifuEfA4wemuPq7SbzFVaamIwTkN9ZB + MoAMDvDzJIAcYoiAVGsaYeMqigDFo77RkGG2TgSPwVHbBXiE/Vcl+z2Ks7gqEKQ8IHfE + rEVAqihPsBmNREAWj2sleCwHPDbCjR3ix6O0gySAfP30/g0KyPUUkI4MIPXwUXMaySns + pP0RYPBoSPHo6RcQvJ7B4/1nr9/V1OoKHiUlVqGDvHw2cz8Csv2Zge/UQgTk8bgD3ONl + cI+6hEcZQH6lgCzMOwUlVgSkFrIKu2xvBACPen30CR4dJwjx+FS38CgPyBfFty+fzdgH + gAxiSqxDcQ6yvUmC79NUBBTgcV/m2cu3i1/oFh7lAFkJDrIw99Sh1Nj1wYv9PMc72tAS + KzpITWUW9tOeCEjjcTFxj4dO5RXeJ3j8qjvuUdEcpASQYUH+PpNgDhIB2Z4MwfdoMAIc + Hocyc49Ba2N27MvQSTw2D8hZUxGQGkwr7KqdEZDg0cZxvKcf4DEW8JhL8Fipa3gUOMhf + zCKdF4+og0yMoYAcOwoB2c4swbdpKAJCPE7y8Q8K02E8KgXkrtjI4MUISA2lFHbTgQgI + 8Th11uLgSF3GowwgP70vB0AW5GSkyQKyR/cOhAzfihFQVwR4PI4ay+AxcV9GzuXbj6C4 + +gmW5vzSiaU5zII5SUUHNjCHR839+Pa1pvL1k3uFuScP8oC0IKtYscSqrnzCdjsWAQ6P + FuAeGTzu0l33KBEk6PHfXz8b6zlAHk9L3BS21H/6JImDREB2LG/w3eqJQPce5Kk5g4da + SfCYlpFTQPD4HvD4U9fwyDvIf/8hgKwmgLxOALktctUicJAONiwgu3dTT0CxVYxA+yOg + AI8HT+Zev/dEF4urzFUr8yQdBpB1AMjnj24V5BBAhnKAHEQeFICAbH/W4DvVFQEhHqf7 + Lw3blJh2PKfglgCP7IaPvEMT+2/cfZAcIF8BIC9KAdLEUL+3Hu7Pqq6cwnbbHYFu3enK + VRMLGwdwj4tWRW7bdfDkRYrHarI0R4z7r7b47wEHyP+BgxQAMoECciJxkIMM+iIg2500 + +Ea1RYDB4yDqHgGPoZsSGDw+B/dYB+5Rch9yiyIQ0QlKAJmyLWLVoplTXcFBmhj2R0Cq + Lamw4fZGACY79HrrGxI8uhI8RnB4fFWpu3jk5iDJlAcLyIe38sFBsoAcM8rKFAHZ3pTB + 96kxAgSPfQ0oHid2GjzKlVjfsg6SAeQUVwdrC5OBCEg15hU23a4IUDz2H2hiYe3gOmUm + wWMK4x51G49KAHlsb0J06BJ/74ljRloiINuVMPgmtUaAxaOp5cgxFI/RCXuP5+TfeqjT + 7pEYWYGD/A5zkADIu9cvnkhP2RqxcuFMBKRakwobb28EFOAx/QQprr56q8vuUSJIumhO + 4CCP7Y2PXrNkrhCQuCagvcmD71N5BIR49PZfEgp4PNYp8CgNSNif9e2rx3evXeABaW9t + jg5S5fmEDXYoAjwe7cE9LlwZsTUF8HjtLsVj/XfdnHvkpl0Ec5ANdZ+qyp4/vJmfIwdI + fPRjhzII36zKCMBSOVJcpe7Re+6SNdHxBI83dd89Si5Y4TYPWKRDNjDnALlza/jKhb5T + XBGQqswkbEsFEeDwaG4NePRduDKc4PHCtbuPwT1+0XU8Sio6ZA6yoe5jVdkzFpAbiYN0 + pyXWAX17ISBVkEnYhCoiIMCjuxQey6o+1TXo6NIc7nJV3kGWEgeZlc4AEvZnRQepiiTC + NlQVASEeJ1M87uxEeOT1yAPywc38M0f3xDOAdCZzkAhIVWUTttPRCFA8DqDukeJxY/ye + Y2fAPT4rq/oohUcx76DD41D+N+EcJHGQHCC3hK9Y4IuA7Gj+4PtVGgEBHl0AjyvCt+xM + zyLusbQzuEdhRUcekKsD50xzR0CqNJ+wsY5FgMejs7v3nMA1gMejgMcHnQaP/BUr7M9K + S6ylj4uuEgfJAXKEuTGsYu2J90F2LJPw3SqIAOCxZ+/+A43NR9gDHhdweCxi8QgbzLG3 + Ievq1SoQkrtgpYCsJSXWBzcYB8kA0m64qRE6SBUkEzbR4QgweDQytbRzdp82J3B1J8Qj + L0gWkBUEkOez0pMJIGeAg0RAdjiPsAGVREAxHq92KjzyepQD5AYCSDdnBKRKkgkb6XAE + ODwOt3N24/F4g7rHWuHcow5frgquWCWALAFAZkoB0gAdZIezCRvoYAQYPBow7nEGcY/J + 6VnnrxaVlFaQpTmdwj3KlVg5B3n6yO54BGQHMwjfrsoIyOJxQ/zuo6fzOx0e+StWaUAe + SN4cvnw+dZBmxghIVWYWttWOCMjhcXPygcxOiEdej5yDfAMl1ksAyLgNIYFzvFgH2QdX + sbYjifAtKosAwWOfAUamnHvcELf7yOlLnRCPrCCbyG0edA6y4mVJ0ZXzmdKA7IdzkCpL + LWyo7RGgeOxnYGxG5h5nzF8ezuHxZedyjwoc5DshIGd7uTnREisCsu1JhO9QWQR4PDq5 + ec0ODJHg8c27j52ouErkKAPIz9UAyDtXzmUeSNq8jnGQVuAgYfMAXKSjsvTChtoWAYJH + 2EFHgsd1BI/nrhSVAB4/s8XVJrq2hclo3f5/wSKdHw21HzlApsZFhQQSQNqSRToIyLal + EJ6twgjI4jGKc48Ujz/4R5Lr9twj988IFSTnIAkgixlAxgAgfWCRDgJShbmFTbU5AhI8 + WoF79Jm/fN3mJILHO50Tj1IlVgBkDQPI7MMEkAGzvSYgINucQfgGVUZAgkdbpwnEPUbF + pR7JJsXVN+9qahs6HR4lDvI3LbF+/sACcn9SzLpl83w8xo22MhuMDlKVGYZttSECLB4H + mwEePQgeY5L2EzwWv6z40PncI7loFTrIbywg8wCQ26OCGUAOG2Kkjw6yDTmEp6ouAhSP + +kamwygeA0Kitqcezs7rxHiUAmRj/ecP5YyDBECuRUCqLrGwpfZEgMfj6HEePvOWSeOx + EVau/m7qPMVVpqYjD8j7Nygg1wcH+HmCgxw2xBAB2Z5swvd0NAIMHg2HEDx6+gUES+ER + 9l+V7PfYOYqrAkE2NREHyQHy8rnM/TsQkB1NJ3x/xyIgi8e1MTtY91gO7rFz4lHeQb5+ + KgVIRwaQevgs1o4lF767zREAPOr10Sd4dKR4XM+4x/tPX0NxtbPiUd5Bvii+ffls5j4E + ZJsTCN+gyggoxOPZy1Bc7cx4lAHk15p3AMjCvFOHUrevD14MDhIBqcokw7ZaHQFFeDyV + V9jZ8agEkBkEkEH+dA5yKM5BtjqL8EQVRYDD41ArWlwNAve4L/Ps5dvFLzo3HuUAWUkA + mQuAjKWAHO9oQ0us6CBVlGjYTKsiIMDjeE+/xcHrY1MPncoleKys+dp53aNkUYCgxPri + EThIAsgwAOQkWKSDgGxVCuFJqouAEI+TfPyDwgCPGV0Cj80AMjJ48aypCEjVZRm21NoI + SPBo4zh+6qwuhUeBg/wFc5CfPpQDIAtyMvYlMoAcOwoB2do0wvNUEwEej6PGMnhMpHh8 + xLrHX51waQ6zHkBywQr7s8KTdH58+1rDOchdsRwgLcgiHXSQqsk1bKXlCMjiMTJ2V1dx + jxJBEgf56ycA8j0LyLTETdRBSgDZo3vLkcQzMAIdj0D3HuSxAIOHWknwmJaRU3Ab8Pj+ + EyzN6dx4FDjIf1hAPrl3PffkQQRkxzMLW2hHBDg8WrDuEfB48GRu4b0uUFxlrlqZBwX8 + oYCso4C8VZBznAByqf/0SQSQg8h9kAjIdiQXvqXNERDicbr/0rBNiWnHeTz+7Ox45AH5 + LwVkdeVrAORFAOS2yFWLoMTqYGNhQh1k925tji2+ASPQxgh0686sXLWwcYDi6qJVDB6v + 33vyurKazD12hg3m+OqNwt84QP4PHCQB5PNHFJAJm0IRkG3MJjy9oxFg8DiIukfAYyiD + x1vUPdY1EjyyGz4qTOVOcZC7D5ID5CsOkBGrFs2c6soAsrcePvqxo7mG728xAjDZoddb + 39BEgsdt4B4vAh5fdRk8cnOQZMpDASAnMg6yLzrIFpMJT+hwBAge+xpQPE6keEwg7vHW + o+dQXCV4lNyH3ClIqORDKAFkyjYCyCmuDtYWJrCBOQKyw9mGDbQQAQEeXafOXLQqYltK + 18OjAkA+vJWfc3xvQjRxkBPHjLQ0HWSAgGwhl/DljkeAxaOp1agxPB7zbz3sUniUK7G+ + pQ7yRDoAciUCsuNZhi20MgIUj/0HmlhYO7hOkcLj265SXGUuYeVKrASQxwggl8z1RkC2 + Mp3wtA5GgMOj5cgxE739l4RGJ+w9ltP18CgFyO/1X6vfvnp899pFAOTWiJULwUHao4Ps + YKbh21sRAWk8LlwZsTUl/cTF63efvCJ4/N4V5h65Co88IG8SQMZHrxECEp9s1YqswlPa + G4FuTHHVlOJx7pI10fFdFI8ygPzCAPLCifSdW8NXLvQlgDTHEmt70wzf17oI8Hi0B/fI + 4vHCtbuPAY9f6rsUHqVKrA11n6rKnj8UANKdK7EiIFuXWnhWOyIAK8nJ3KMMHm8+fF5W + 1XXmHqUuWOE+SFik871eFpCTEZDtyC98S5siwOHR3Brw6LtwZfjWneknuioehbvr/Gyo + +1hV9gwAeebYnviNxEEygBzQtxcCsk05hie3PgIUjwMoHt29wT1upO7x5sNnZVUf6xq6 + yNIcDo/yDrIUSqwXstJ3bglfsdAX9mdFB9n61MIz2x4BAR5dJrN4zCJ4LO2C7hFkyS2a + I6tYGUA+AEAeBUCuDpzj7e5MFukgINueZ/iO1kVAHo97jp7Jv/lADo+daQcdHofyv3GC + pPuzgoMsfVzEAXIBAeQIc2NYxdoTb/NoXX7hWW2KgDQeF6wI37IzvSvjsQVATnN3trM0 + NUJAtinJ8ORWR4DHo7P7tDmBqzfGd3E88oIUAPLqecZBIiBbnVh4YnsiAHjs2bv/QGPz + EfbgHjk8Xi1i3SPsv8rehtxVrlZlHWQtKbE+uCFxkNPcnO2GIyDbk2v4npYjwODRyNTS + TohH2J5ctrjahfQoKemwgKwoLSkigEyGEuuCGYyDNEAH2XJy4RltjYA8HpPTs84TPFaQ + pTldEo/8BSstsXKAPH10d/wGKLEiINuaZHh+qyPA4XG4nbMbcY8bqHtk8FgrnHvsSnjk + BSkFyMwDyZvDV8xHQLY6u/DENkaAwaMB4x5nEPeYnJ4JeCzp0njk9SgE5KXTR3bHCQHZ + BxfptDHd8PQWIiCPx91HTud3eTzyguQA+ZI4SArI5QwgzYwN+uEcZAvphS+3LQIcHs1I + cXXG/OXhm5MPIB7JCgFuTQALyHdvoMTKADIkcLaXmxMtsSIg25ZueHYLESB47DPAyJS4 + R685gSEb4gCPlwCPb6o+dmH3KBFkE7nN4+/v9Z+rK16W3LlyjgBynQCQvXCRTgsZhi+3 + IQIUj/0MjKXxeKWo5CVfXG2ipCAZ2rV+BID80VD7kQdkFAKyDSmGp7YhAjwendy8Zgvx + +K6r45G9YpUHZBIBpA/MQVoRB4mAbEO64anNR4DgETaYMzazYtzjus1JBzLPXblD8PiZ + nXvsqniUcpAAyBoGkNlHUuMoICc42ZJFOuggm08xfLUNEZDg0ZbBY1RcKuseCR5/8I8k + 71pzj9x1Ob1ilQDyQ8XLYuIg9yfFEEB6EEAORkC2Id3w1OYjIIVHn/nL18UweCxGPFJJ + SjtIBpB52YdTt0eFBMz2AkAOMzXSR0A2n2T4aqsjIMDjBK/ZASGAx8PZtLj6rgbxCJJk + AfkbSqyN9Z8/lPOAXDbPx2PcaARkq3MNT2wxAiweB5tZjR7n4TNvGeBxP3GPgMcPjHv8 + 3dR13SMhpBCQ31gHyQAyOMDPkwByCOzPiot0Wkw1PKEVEaB41DcaMszWieAxOGo74DGP + zD0CHmH/VcmGVl3TPUoE2dQkC8gdMWsRkK3IMDylDRGQxeNaCR7LAY+NcGNHV8ejPCBf + P71/gwJyPQWkIwNIPXzUXBvyDk9VHAEGj4YUj55+AcHrGTzef/Ya8UjYSH8UOMjLZzP3 + IyAV5xQebXcE5PG4A9zjZXCPiEdOjTKA/FrzDgBZmHcKSqwIyHZnHr5RUQQAj3p99Ake + HScI8fgU8cjLUb7E+qL49uWzGfsAkEFMiXUozkEqSi881rYIcHgcyhZX18bs2Jd59vLt + 4heIRzk9wuYBcJvHj29fayoJIHNPHUqNXR+82M9zvKMNLbGig2xb9uHZshGQxuNi4h4P + ncorvE/w+BWLq7wkZR2kBJBhQf4+k2AOEgEpm1v4d5sjII1H/yCCxwzEIy9DyW9Sc5Ay + gJw1FQHZ5tTDNyiIgASPNo7jPf0Aj7GAx1yCx0rEo0SL9BcJIH8xi3RePKIOMjGGAnLs + KASkgvzCQ22KAI/HUeMm+fgHhbF4fITuUVqM8JcSQO6KjQxejIBsU9rhyUoiIMTj1FmL + gyMRj3I6lByQAuSn9+UAyIKcjDRZQPboriTYeBgj0EIEmPseBw+1GjWWwWPivoycgtsE + j59gac4vXJojUaMCQD65V5h78iAPSAuyihVLrC3kHL6sNAIcHi3APTJ43IXuUaBAmV8Z + QP75/e+vn431HCCPpyVuClvqP32SxEEiIJUmHL7QbAS69yCPBWDwOB3c46bENBaP7wGP + PxGP0oLkHCRsYA5zkNWVr5/cu04AuS1y1SJwkA42LCC7d2s26vgiRkBhBLp1Z5bmCPB4 + 8GTu9XtP2OLqP7+73g460gqU+UsIyDoA5PNHtwpyCCBDOUAOIg8KQEAqTDc82EIEGDwO + ou5xuv9SgsfjOQW3wD2+/1RH8cjqUSYtu+yfsoB8BYC8SAAZQQDpCoA0MdTvrYf7s7aQ + ePiyogjAZAdZuWpiYeMA7nHRqshtuw6evEjxWE2W5nTFDeZa+KeGAyRsYN4oAGQCBeRE + 4iAHGfRFQCrKNjzWUgRk8Bi6KYHB43MWj5L7kFtI0i70shJAphBAzpzCALI/ArKlzMPX + FUSA4LE3g0fXqTMXrYrg8PiqEvGo7N8YOUA+vJWfc3wvC8gxoyxNEZAKcg0PtRwBgse+ + BsQ9jpkI7pHD40PEozIxwnEZQL5lHGS6BJDWFiYDEZAtJx+eIRsBisf+A00srB1cp1A8 + pjDuEfHYjB7ZR82R265YB0kAeWxvQnToEn/viWNGIiBlEw3/blUEWDyaWo4cM9Eb8Bid + sPd4Tv4txGNzapQC5HeYgwRA3r1+8UR6ytaIlQvBQdojIFuVfXiSTARk8LgyYmtK+glS + XH31Ft1jc5JU6CCP7Y2PXrNkrhCQuCZAJuPwz+Yi0I1xjwwe5y4heDyGeGxOiOxrAgf5 + vf4LAPLx3WsXCCDDWUCao4NsLvHwNUUR4PFoD+5xIYvHa3cfEzzWf8e5R+XKFACyoe5T + VdnzhzeJg2QA6c45SHz0o6K0w2NKIgAryUlxlcPjmuh4gsebD5+XMUtzcO5RqSCVAHIn + AaTvZHCQCEglSYeHlUWAw6O5NeDRd+HK8K07009cYPD4BfGoVIvkBU6PdAPzuo9VZc9Y + QG4kDpIB5IC+uHmAstzD4/IREODR3XvuEgken5VVfapr+MlvEdBsZnbRFzlBwm0e1EGW + EgeZlc4C0gUBKZ9veKTZCAjw6DJZiMfSt9WIx5b+meH0yAPywc38M0f3xG9cEzjH292Z + zEEiIJtNQHxRKgIUjwOoe6R43Bi/5+gZcI+Ax49SeOy6O+g0J0pOkLD9HAvIIgrILeEr + FvjCBuYjqIPsibd5SCUd/qEsAjJ4XBG+ZWd6FnGPiMfmZCh5jdOjPCBXB86Z5u5sZwkb + mKODVJZ+eFwmAjwend295wSuZvH4APEokVzzv3CCFADyKnGQPCCNYRUrAlIm7/BPhREA + PPbs3X+gsfkIe3CPC1g8Xi1i8QgbzOFjAVqnRwrIWlJifXCDcZAEkG7OdsMRkApTDw8q + iACDRyNTSztn92kSPML+q+gem5ch/6oMICtKHxddPZ+VnkwAOYM6SGMDBKSC3MNDchGQ + x2NyetZ5gscKUlxFPPKyU/obp0cpQJ4+ujt+AwJSLuHwQLMR4PA43M7ZTRaPtcK5Ryyu + KtWjZFEA6yArSksAkJkHkjcjIJtNPnxRNgIMHg0Y9ziDuEcGjyWIR+Xyk3tFESAvnT6y + O04IyD64SEc2+/Bv2QjI4nFD/O6jp/MZ94h4lBOesgOcIOUBuXw+dZBmxgb9sMQqm334 + t0wEpPE4f3n45uQDmeAeEY/KlKf4OKdHzkG+gRIrA8iQwNlebk60xIqAlMk+/FM2AgSP + fQYYmbLuMWRD3O4jpy8BHt9UfUQ8KtaewqNUkE1NfyggP1dXvCwpukId5DoBIHvhHKRs + AuLfwghQPPYzMDYjc48zODxeKSp5icVVhbJTelAAyB8NtR/fISCFeYa/ty4CPB6d3Lxm + Bwrw+A7xqFR7Cl+QB+SdK+cyDyRtZgFpRRwkArJ1edlFzyJ4hB10JHhcR9zjuSt3CB4/ + s3OPTTTRFKYgHhREQCEgs4+kxkUxDtKWLNJBB9lFlda6jy3Boy2DxyiJeyR4/MHf94hz + jwLlKflVDpDFBJD7k2IAkD4eLvZWZoMRkK3Lyy56lgSPVuAefeYvXxeThHhUoraWD0sD + soY6yLzsw6nbo0ICZntNcLIdZmqkj4DsolprzccW4HECcY9RcalHsmlx9V0N4rFlBcqc + wQLyN5RYG+s/fyh/KQHksnk+HuNGIyBbk5Rd9xwWj4PNrEa7ePjMWwZ43E/cY/HLig/o + HmXE1oo/hYD8VisEZHCAnycBJNnAHBfpdF3FNf/JKR71jUyH2ToBHgOCo7anHs7OI3OP + gEfYYE7yUDl0j61QI5yiBJA7YtYiIJtPRXz1r78EeBwnjcdywGMj3NjxuwmLq61TInOW + LCBfP71/gzrI9RSQjgwg9fBZrCg/BRFg8Gg4hODR0w/x2BblKTlXASAvn83cj4BUkH54 + SDoCsnhcG7ODdY+IRyVya/GwFCC/1rwDQBbmnYISKwJSOvnwL7kIAB7J9uSAR0eKx/WM + e7z/9DW6xxaFp+wEWUC+KL4NgNwHgAxiSqxDcQ5SLhPxAERAIR7PXr5T/ALxqExtLR+X + AWQlAWTuqUOpseuDF/t5jne0oSVWdJAoQdkISONxcTDB46m8QorHr1hcbVl6is9QCMgM + AGRYkL/PJJiDREDKZiL+DRHg8DjUajQprgaBe9yXcfbybcSjYp219mhzgJw1FQGJ4lMc + AQkebRzHe/oBHmNTD53KJXisrEE8tlZ9Cs6TAPIXs0jnxSNwkBn7EhlAjh2FgFSckF37 + qBCPk3z8g8IQjwq01Z5DSgC5KzYyeDECsmurTvmnF+Jx6qzFwZGIx/aIT9F7pAD56X05 + ALIgJyNNFpA9uiv/dvCVLhYBHo+jxjJ4TKTu8RFbXP2FS3MUSa1Vx+QBea8w9+RBHpAW + ZBUrlli7mOSa/bgcHi3APTJ43IXusVVqa8VJPCB/NtZzgDyelrgpbKn/9EkSB4mAbDZF + u9SL3XuQxwIMHmolwWNaRk7BbcDj+0/1jT8Rj62QndJTOEDC/qw/vn2trnz95N51Asht + kasWgYN0sGEB2b1bl8o5/LBKI6AAjwdP5hbee4LFVaUqa8MLDCD//P7318/GOgrIWwU5 + BJChHCAHkQcFICCVJmgXe0GIx+n+S8M2JaYdzym4JcAju6NVG5IQT5VEQBaQrwCQFwkg + IzhAmhjq99bD/Vm7mOyUfdxu3enKVRMLGwdwj4tWRW7bdfDkxesEj9Vk7hE3mJNIq32/ + cID8HwvI548oIBMoICcSBznIoC8CUll+drXjDB4HUfcIeAzdlMDg8Tm4xzrqHhGP7RMi + +y4lgEwhgJw51RUcpIlhfwRkV9Odks8Lkx16vfUNCR5dp85ctCqCw+MrxGOHZMi/WQ6Q + D2/l5xzfywJyzChLUwSkkuzseocJHvsaUDxOVIRHyWM6+PzC39oWARlAvmUcZDoDyCmu + DtYWJrCBOTrIric++U9M8dh/oImFtYPrFIrHFMY9Ih7bJrrmzlYIyGN7E6JDl/h7Txwz + EgEpn5hd9AiLR1PLkWMoHqMT9h7Pyb/1kHOPiMfmhNbK1wSA/A5zkADIu9cvnkhP2Rqx + cuFMBGQXlZ6ijy2Dx5UR21LST5Di6qu3WFxtpdpacZoSQMZHr1kyVwhIXBOgKEm70DEh + Hr3nLgkFPB5DPLZCYW07RQjI+i8AyMd3r10ggAyngLS3NkcH2YVUp/Sj8ni0B/e4cGXE + VoLHa3cfEzzWf8e5x7bJTvnZ0oCsKnv+8GZ+zrG9DCDdOQeJj35Umqld4wVYKkeKq9Q9 + Ah7XRMcTPN5E96hcWe17RQkgdxJA+k52RUB2Db218Ck5PJpbAx59F64M37oz/cQFBo9f + EI/tk57idwkA2VD3sarsGQvIjcRBMoAc0Bc3D2ghYTv5ywI8ukvhsazqU13DT36LAMU5 + hkdbHwFZQJYSB5mVzgLSBQHZyaXWmo8nxONkxGPrxdWOMzk9/oZVrAwgH9zMP3N0T/zG + NYEASGcyB4mAbE3Wdt5zKB4HUPdI8bgxfs+xM+Aen5VVfZTCI+6g0w4FyryFEyRsP/ed + lFhLHxdRQG4JX7HAd7KL/QgssXZepbXqkwnw6AJ4XBG+ZWd6FnGPpW+r0T3KyKnDf3J6 + lAfk6sA509yd7RCQrcraznsSj0dnd+85gasBj0cBjw8Qjx0Wn6IGOEEKAHmVOEgekMaw + irUn3gfZeRXX7CcDPPbs3X+gsfkI2J7cdwGHxyIWj7DBHHufFV6tKpJXm49xeqSArCUl + 1gc3GAfJAnK4qRE6yGZTtlO/yODRyNTSztl9GuKxzfpq8xs4QbKArAAHefV8VnoyAeQM + 6iCNDRCQnVpzzXw4eTwmp2edv4p4bLPOWvsGTo8ygNwdv4EA0s3ZDgHZTL529pc4PA63 + c3bj8Qjbk0NxtVY494iXq60VXEvncYKUALIEAJmZnrwZAdnZ1dbi52PwaMC4xxnEPTJ4 + LCmtIMVVdI8tiasdr3N6FALy0ukj0oDsg4t0WkzeTniCLB43xO8+ejof8dgOmbX+LZwg + pQF5AAC5fD51kGbGBv2wxNoJ5dbSR5LG4/wV4ZuTD2SCe0Q8tl5dbT+T0yMHyDdQYiWA + jNsQEjjbi3WQCMiWkrcTvk7w2GeAkSnnHjfE7T5y+hLg8Q26x7brrNXvoIJsavpDAfm5 + uuJlSdGV85kAyHUCQPbCOchOqLhmPxLFYz8DYzMy9zhj/nIJHl+ie2y1uNpxogCQPxpq + P76TAaQTLbEiIJvN3c74Io9HJzev2YEhPB7fYXG1HTpr9VvkAXnnyrnMA0kcIK2Ig0RA + dkbNNfOZCB5hBx0JHtcR93juyp0SwONntrjaRFOn1YmGJ7YqAgoBmX0kNS6KOkgnWzIH + iYBsJnc740uyeIySuEeCxx/8fY8499gqlbXhJDlAFhNA7k+KAQfpA4t0rMwGIyA7o+aa + +UwSPFqBe/SZv3zd5iTEYxs01ZFTpQFZQx1kXvbh1O1RIQGzvSY42Q4zNdJHQDaTvZ3v + JQkebZ0mEPcYFZd6JJsWV9/VIB47orZWvJcF5G8osTbWf/5Q8VICyGXzfDzGjUZAdj7B + Nf+JWDwONrMa7eJB8BiTtJ+4x+KXFR/QPbZCUh06RQjIb7VCQAYH+HkSQJINzHGRTvM5 + 3JlepXjUNzIdRvEYEBK1PfVwdh6ZewQ8wgZzkkeSo3vskPKUvFkGkOUSQK5FQHYmmbX2 + swjwOM7DZ94yHo/lgMdGWLn6uwmLq0rEpILDsoB8/fT+Deog11NAOjKA1MNnsbY2oXX8 + PAaPhkMIHj39AoIRjyoQWVuaUADIy2cz9++IQUDquLTaM3xZPK6N2cG6R8RjW1TV/nMV + ALIw7xSUWBGQ7UloHX8P4FGvjz7BoyPF43rGPd5/+hrdY/s11qZ3ygLyRfFtAOQ+AGQQ + U2IdinOQOq6yVg9fIR7PXr5T/ALx2CZRdeBkKUB+rakEB1mYe+pQauz64MV+nuPRQbY6 + m3X/REV4PJVXSPH4FYurHVBZG96qEJAZFJD+PpNgDhIBqftKa9Un4PA41Go0Ka4GgXvc + l3H28m3EYxvk1OFTmwPkrKnjHW3oHCSWWFuV0jp9kgSPNo7jPf0WB6+PTT10KpfgsbIG + 8dhhobW2AQkgf9FFOuUvHoGDzNiXGBMWBIAcOwoBqdMqa/XghXic5OMfFIZ4bK2EVHqe + EkDuio0MXoyAbHU66/yJQjxOnYV4VKnI2tKYFCA/vSeALMiRB2SP7jqfcvgBlEeAx+Oo + sQweE6l7fMQWV3/h0py2iKoD58oD8l5h7slDPCAtyCpWdJDKc7kzvMLh0QLcI8FjZOwu + dI8dUFUH3soD8mdjPQfI42mJm8KW+k/nHSQCsjPoTtln6N6DPBZg8FArCR7TMnIKbgMe + 33+qb/yJeOyAwNr4Vg6Q//7z68e3r9WVr5/cu5578uCubZGrFoGDdLBhAdm9m7IvE4/r + egQU4PHgydzCe0+wuNpGNangdAaQf37/++tnYx0A8vmjWwU5BJChHCAHkQcFICB1XXXK + xy/E43T/pWGbEtOO5xTcEuCR3dFKBemGTbQQAVlAvgJAXpQCpImhfm893H5OeT7r+Cvd + utOVqyYWNg7gHhetity26+DJi9cJHqvJ3CNuMNeChFT7MgdI2MBcCMgECsiJZA5ykEFf + BKSOi66Z4TN4HETdI+AxdFMCg8fn4B7rqHtEPKpWcs22pgSQKdsiVi2aOdUVHKSJYX8E + ZDMJrdsvwWSHXm99Q4JHV4LHCA6PrxCPzQpHXS/KAfLhrXxwkCwgx4yyMkVA6rbkmh09 + wWNfA4rHiYrwKHlMh7ryD9uVjoAMIN+yDpIB5BRXB2sLE9jAHB1ks1mtsy9SPPYfaGJh + 7eA6ZSbBYwrjHhGP0jLR3F8KAXlsb0J06BJ/74ljRloiIHVWbi0OnMWjqeXIMRSP0Ql7 + j+fk33rIuUfEo+aEyPYkAOR3mIMEQN69fvFEesrWiJULZyIgW0xpXT5BAR7TT5Di6qu3 + WFzVuBKFgmz6Q7afY0qsxEEe2xsfvWbJXCEgcU2ALktP4diFePSeuyQU8HgM8agtIQr1 + +H9k+7lf3+u/ACAf3712gQAynALS3tocHaTCbNb5gzwe7cE9LlwZsTUF8Hjt7mOCx/rv + OPeoHWUKHGRD3aeqsucPb/KAdOccJD76Uef1J/sBYKkcKa5S9wh4XBMdT/B4E92jdnTI + 9Sp0kAJA7iSA9J3iioCUzeNO8jeHR3NrwKPvwpXhBI8XGDx+QTxy8tD8f6UA+bGq7BkL + yI3EQTKAHNAXNw/oJDKUfAwBHt2l8FhW9amu4Se/RYDmM7Jr9ygLyFLiILPSGUDC9nPo + ICU53Il+EeJxMsXjTsSjKP4h4PRISqwNdQSQD27mnzm6J37jmkAApDOZg0RAdiIp0o9C + 8TiAukeKx43xe46dAff4rKzqoxQecQcdTauUEyRXYuUAuSV8xQJfAOQILLF2NjX+JcCj + C+BxRfiWnelZxD2Wvq1G96hpBUr3x+lRHpCrA+dMc3e2s4QNzNFBdipN8nh0dveeE7gG + 8HgU8PgA8SitDa38xQkS9melc5Clj4uuEgfJA9IYVrH2xPsgO40kAY89e/cfaGw+ArYn + 913A4bGIxSNsMMfeZ4VXq1pQJKdHCsha6iBvMA6SBeRwBGSnkSL9IAwejUwt7Zzdp80J + XI141ILslHfJCZIFZAUB5Pms9GQCyBnUQSIgO5Mg5fGYnJ51/iriUblENPoKp0cZQO6O + 30AA6eZsh4DsTHL8i8PjcDtnNx6PsD05FFdrhXOPeLmqUR1KOuMEKQFkCQAyMz15Mw9I + A3SQnUWTDB4NGPc4g7hHBo8lpRWkuIruUaILbf3C6VEIyEunj0gDsg8u0ukcipTF44b4 + 3UdP5yMetaU+Bf1ygpQG5AEA5PL51EGaGRv0wxJrp9CjNB7nLw/fnHwgE9wj4lGBMLR0 + iNMjB8g3zx7cIICM2xASONvLzYk6SARk59Aj3NjRZ4CRKeseQzbE7T5y+hLg8Q26Ry3J + T75bKsgmch8kzEF+rq54WVJ05XwmAHKdAJC9cA5S9xVJ8djPwNiMzD3O4PB4pajkJbpH + eV1o64gMIN8hIHVfeYo/AXGPDB6d3LxmBwrw+A6Lq9qSn3y/8oC8c+Vc5oEkDpBWxEEi + IBXnuA4dJXiEHXQkeFxH3OO5K3cIHj+zxdUmmgzyOYJHNBcBASB/NNR+ZACZfSQ1Lopx + kLZkDhIdpA4pT/FQZfEYJXGPBI8/+Psece5Rc+JT1JMcIIsJIPcnxYCD9PFwsbcyG4yA + VJzjOnRUgkcrcI8+85ev25yEeFQkB60fkwZkDQVkXvbh1O1RIQGzvSY42Q4zNdJHQOqQ + 9hQNVYJHW6cJxD1GxaUeyabF1Xc1iEeti1A4ABaQv6HE2lj/+UPFSwkgl83z8Rg3GgGp + KMF16xiLx8FmVqNdPAgeY5L2E/dY/LLiA7pHoRq0/7sQkN9qhYAMDvDzJIAkG5jjIh3d + UqD0aCke9Y1Mh1E8BoREbU89nJ1H5h4Bj7DBnOSR5OgeRSLIpiYOkOUSQK5FQEqnta7+ + JcDjOA+fect4PJYDHhth5ervJiyual+JzAhkAfn66f0b1EGup4B0ZACph89i1VU50hs7 + +ugbDiF49PQLCEY8ikV7Csch4yAJIC+fzdy/IwYBqbMSFA5cFo9rY3aw7hHxqFAQWj6o + AJCFeaegxIqAFKa1zv4O7lGPwaMjxeN6xj3ef/oa3aOWpae4e1lAvii+DYDcB4AMYkqs + Q3EOUmfVSB4qR5bmkOIqdY8Uj2cv3yl+gXhUrAdtH5UC5NeaSnCQhbmnDhFALvbzHI8O + UnfFCCOXxuPiYILHU3mFFI9fsbiqbfEp6l8hIDMoIP19JsEcJAJSZyXJ4XEog8cgwOO+ + jLOXbyMeFSlBHMeUATIWADlr6nhHGzoHiSVWXRSlBI82juM9/QCPsamHTuUSPFbWIB7F + oT+5UUgA+Ysu0il/8QgcZMa+xJiwIADk2FEISF2UIhmzEI+TfPyDwhCPctkvvgNKALkr + NhIBqatKZMYtxOPUWYhH8WlP4YikAPnpPQFkQU5Gmiwge3TX7ezscqPn8ThqLIPHROoe + H7HF1V+4NEehHrR9UB6Q96DEepAHpAVZxYoOUtcEzeHRAtwjwWNk7C50j9oWW2v65wH5 + s7GeA+TxtMRNYUv9p/MOEgGpW4rs3oPOPQ61kuAxLSOn4Dbg8f2n+safiMfWaEMb53CA + hO3nfnz7Wl35+sm967knD+7aFrlqEZRYHWxYQHbvplsJ2bVHqwCPB0/mFt57gsVVbYis + LX0ygPzz+99fPxvrKCBvFeQQQIZygBxEHhSAgNQlhQvxON1/adimxLTjOQW3BHhkd7Rq + S6bguZqIgCwgXwEgLxJARnCANDHU762H28/pkB67dacrV00sbBzAPS5aFbltF+DxOsFj + NZl7xA3mNKGsdvbBARI2MKeAfP6IAjKBAnIimYMcZNAXAalDcvyLweMg6h4Bj6GbEhg8 + Pgf3WEfdI+KxnWpR/9uUADKFAHLmVFdwkCaG/RGQOqRHmOzQ661vSPDoSvAYQfB4EfD4 + CvGofj11uAc5QD68lQ8OkgXkmFFWpghIHVLjXxSPfQ0oHicqwqPkMR0dTh1sQA0RkAHk + W9ZBMoCc4upgbWECG5ijg9QVTVI89h9oYmHt4DplJsFjCuJRDbpRW5MKAXlsb0J06BJ/ + 74ljRloiIHVFi2ScxD0CHk0tR46heIxO2Hs8J//WQ849Ih7VpiTVNCwA5HeYgwRA3r1+ + 8UR6ytaIlQtnIiB1SYswVgV4TD9B3eNbLK6qRjDqbkUJIOOj1yyZKwQkrgnQAW0K8eg9 + d0ko4PEY4lHdElJp+0JA1n8BQD6+e+0CD0h7a3N0kDqgRDpEHo/24B4XrozYmgJ4vHb3 + 8SvAY/13nHtUqXLU1Jg0IKvKnj+8mZ9zbC8DSHfOQeKjH3VAlLBUTuIeAY9rouMJHm+i + e1STdNTSrBJA7twavnKh72RXBKQOCJEZIodHc2vAo+/CleEEjxcYPH5BPKpFPWpoVADI + hrqPVWXPWEBuJA6SAeSAvrh5gPhlKcCjuxQey6o+1TX85LcIUEMSYZMqi4AsIEuJg8xK + ZwHpgoAUvxLpCIV4nEzxuBPxqDKVaK4hTo+/YRUrA8gHN/PPHN0Tv3FNIADSmcxBIiDF + L0qKxwF07pHicWP8nmNnwD0+K6v6KIVH3EFHc+JqT0+cIGH7ue+kxFr6uIgCckv4igW+ + k13sR2CJVfxqlMw9gnt0ATyuCN+yMz2LuMfSt9XoHtujC229h9OjPCBXB86Z5u5sZwkb + mKODFLkmeTw6u3vPCVwDeDwKeHyAeNSWrtrdLydIASCvEgfJA9IYVrH2xPsgRSxJcI89 + e/cfaGw+guBxAYfHIhaPsMEce58VXq22WyeaeiOnRwrIWlJifXCDcZAsIIcjIEUsRTo0 + Bo9GppZ2zu7T5gSuRjxqSj1q6IcTJAvICnCQV89npScTQM6gDhIBKW5ByuMxOT3r/FXE + oxrUov4mOT3KAHJ3/AYCSDdnOwSkuOVI91/tO8DIdLidsxuPR9ieHIqrtcK5R7xcVb+c + Ot4DJ0gJIEsAkJnpyZt5QBqggxSvJhk8GjDucQZxjwweS0orSHEV3WPHJaLRFjg9CgF5 + 6fQRaUD2wUU6YlUk5x45PG6I3330dD7iUaMiUmVnnCClAXkAALl8PnWQZsYG/bDEKlI9 + SuNx/vLwzckHMsE9Ih5VqRFNtsXpkQPkGyixEkDGbQgJnO3l5kQdJAJSrHqEGzv68O4x + ZEPc7iOnLwEe36B71KSKVNgXFWRT0x8KyM/VFS9Liq6czwRArhMAshfOQYpRkRSP/QyM + zcjc4wwOj1eKSl6ie1ShRDTalAwg3yEgxag8xWMi7pHBo5Ob1+xAAR7fYXFVoypSYWfy + gLxz5VzmgSQOkFbEQSIgFStCq0cJHnsJ8LiOuMdzV+4QPH5mi6tN9OtVYbpgU2qOgACQ + PxpqPzKAzD6SGhfFOEhbMgeJDlKrylPcuSweoyTukeDxB3/fI849qllDKm1eDpDFBJD7 + k2LAQfrAIh0rs8EISMWK0OpRCR6twD36zF++bnMS4lGlytBOY9KArKGAzMs+nLo9KiRg + ttcEJ9thpkb6CEitak9R5xI82jpNIO4xKi71SDYtrr6rQTxqR0sq6ZUF5G8osTbWf/5Q + 8VICyGXzfDzGjUZAKpKDto+xeBxsZjXaxYPgMSZpP3GPxS8rPqB7VIkwtNSIEJDfaoWA + DA7w8ySAJBuY4yIdbStQun+KR30j02EUjwEhUdtTD2fnkblHwCNsMCd5JDm6Ry3pqt3d + ygCynAXkjpi1CEhpEYjnLwEex3n4zFvG47Ec8NgIK1d/N2Fxtd2S0OobZQH5+un9G9RB + rqeAdGQAqYfPYhWPHOmNHX30DYcQPHr6BQQjHrUqIdV2rgCQl89m7kdAikiA0kORxePa + mB2se0Q8qlYb2mhNASAL805BiRUBKS0D0fwF7lGPwaMjxeN6xj3ef/oa3aM2FKTiPmUB + +aL4NgByHwAyiCmxDsU5SNFoEQaiEI9nL98pfoF4VLE0tNKcFCC/1lSCgwRAHiKAXOzn + OR4dpJjESPSoAI+n8gopHr9icVUrGlJppwoBmUEB6e8zCeYgEZAikiSHx6FWo0lxNQjc + 476Ms5dvIx5VKgotNqYIkLkAyFgA5Kyp4x1t6BwklljFIUoJHm0cx3v6LQ5eH5t66FQu + wWNlDeJRizJSXdcSQP6ii3TKXzwCB5mxLzEmLAgAOXYUAlIcUiSjEOJxko9/UBjiUXVC + EElLSgEZiYAUjxKZkQjxOHUW4lEkElLtMKQA+ek9AWRBjjwge3QXW3Z2ufHweBw1lsFj + InWPj9ji6i9cmqNaaWilNXlA3isEB7krlgOkBVnFig5S+/Ln8GgB7pHgMTJ2F7pHrWhG + rZ3ygPzZWM8B8nha4qawpf7TeQeJgNS2Irv3II8FGDzUSoLHtIycgtuAx/ef6ht/Ih7V + KhONNc4B8t9/fv349rW68vWTe9dzTx7ctS1y1SIosTrYsIDs3k3bCdm1+1eAx4Mncwvv + PcHiqsa0opGOGED++f3vr5+NdRSQtwpyCCBDOUAOIg8KQEBq998DIR6n+y8N25SYdjyn + 4JYAj+yOVhpJGuxEbRGQBeQrAORFAsgIDpAmhvq99XD7Oa3qsVt3unLVxMLGAdzjolWR + 23YdPHnxOsFjNZl7xA3m1CYQTTfMARI2MKeAfP6IAjKBAnIimYMcZNAXAalVOf7F4HEQ + dY+Ax9BNCQwen4N7rKPuEfGoaeGoqT8lgEwhgJw51RUcpIlhfwSkVvUIkx16vfUNCR5d + p85ctCqCw+MrxKOaZKG9ZuUA+fBWPjhIFpBjRlmZIiC1qsa/KB77GlA8TlSER8ljOrSX + RdizqiIgA8i3rINkADnF1cHawgQ2MEcHqT1NUjz2H2hiYe3gOoXiMYVxj4hHVYlATO0o + BOSxvQnRoUv8vSeOGWmJgNSeFknPxD0CHk0tR46heIxO2Hs8J//WQ849Ih7FJKcOj0UA + yO8wBwmAvHv94on0lK0RKxfOREBqV4vQuwweV0ZsS0k/QYqrr95icbXD2S/CBpQAMj56 + zZK5QkDimgCtaFOIR++5S0IBj8cQjyLUkaqGJARk/RcA5OO71y7wgLS3NkcHqRUl0k55 + PNqDe1y4MmIrweO1u48JHuu/49yjqmQgnnakAVlV9vzhzfycY3sZQLpzDhIf/agVUcJS + OYl7BDyuiY4neLyJ7lE8+lH1SJQAcufW8JULfSe7IiC1IkSmUw6P5taAR9+FK8O37kw/ + cYHB4xfEo6qlII72BIBsqPtYVfaMBeRG4iAZQA7oi5sHaEOWAjy6S+GxrOpTXcNPfosA + cWQSjkIVEZAFZClxkFnpLCBdEJDaUCLtU4jHyYhHVSS7DrTB6fE3rGJlAPngZv6Zo3vi + N64JBEA6kzlIBKQ2REnxOIDOPVI8bozfc+wMuMdnZVUfpfCIO+jogM5aPUROkLD93HdS + Yi19XEQBuSV8xQJf2J91BJZYtaFGydwjuEcXwOOK8C0707OIeyx9W43usdXprXMncnqU + B+TqwDnT3J3tLGEDc3SQGtckj0dnd+85gasBj0cBjw8QjzonsbYNmBOkAJBXiYPkAWkM + q1h74n2QGpUkuMeevfsPNDYfQfC4gMNjEYtH2GCOvc8Kr1bblu6iP5vTIwVkLSmxPrjB + OEgWkMMRkBqVIu2MwaORqaWds/s0xKPoVaTCAXKCZAFZAQ7y6vms9GQCyBnUQRobICA1 + K0mFeDx/FfGowrwXa1OcHmUAuTt+AwGkm7MdAlKzYoTeODwOt3N24/EI25NDcbVWOPeI + l6tilVX7x8UJUgLIEgBkZnryZgSkxoXIdMjg0YBxjzOIe0xOzwI8lpRWkOIqusf2J7sO + vJPToxCQl04fkQZkH1ykozlxyuJxQ/zuo6fzEY86ICZVDJETpDQgDwAgl8+nDtLM2KAf + llg1pkdpPM5fEb45+UAm4lEVqa4TbXB65AD5BkqsBJBxG0ICZ3u5OVEHiYDUnB7hxo4+ + A4xMOfe4IW73kdOXAI9v0D3qhKA6OkgqyKamPxSQn6srXpYUXTmfCYBcJwBkL5yD1Iwi + KR77GRibkbnHGfOXS/D4Et1jRzNdN94vAOSPhtqP7xCQmlGe4l6Ie2Tw6OTmNTswhMfj + Oyyu6oagOjpKeUDeuXIu80ASB0gr4iARkIr1o+KjBI+wg44Ej+uIezx35U4J4PEzW1xt + ol9YR791fL9YI6AQkNlHUuOiGAdpS+Yg0UGqWHmKm5PFY5TEPRI8/uDve8S5R7HKqePj + kgNkMQHk/qQYcJA+Hi72VmaDEZCK9aPioxI8WoF79Jm/fN3mJMRjxxNcx1qQBmQNdZB5 + 2YdTt0eFBMz2muBkO8zUSB8BqWLtKWpOgkdbpwnEPUbFpR7JpsXVdzWIRx2TVfuHywLy + N5RYG+s/f6h4KQHksnk+HuNGIyAViUf1x1g8DjazGu3iQfAYk7SfuMfilxUf0D22P791 + 7Z1CQH6rFQIyOMDPkwCSbGCOi3RUr0DpFike9Y1Mh1E8BoREbU89nJ1H5h4Bj7DBnOSR + 5OgedU1ibRuvDCDLJYBci4CUlow6/xLgcZyHz7xlPB7LAY+NsHL1dxMWV9uW2bp5tiwg + Xz+9f4M6yPUUkI4MIPXwWazqlCO9saOPvuEQgkdPv4BgxKNuqkkFo1YAyMtnM/fviEFA + qlWCwsZl8bg2ZgfrHhGPKkhxnWpCASAL805BiRUBKZSMWn8H96jH4NGR4nE94x7vP32N + 7lGnxKSKwcoC8kXxbQDkPgBkEFNiHYpzkGpVI3moHFmaQ4qr1D1SPJ69fKf4BeJRFRmu + W21IAfJrTSU4yMLcU4dSY9cHL/bzHI8OUr1ihNYV4fFUXiHF41csruqWnDo+WoWAzKCA + 9PeZBHOQCEi1SpLD41AGj0GAx30ZZy/fRjx2PLd1sYXmADlr6nhHGzoHiSVWdYlSgkcb + x/GefouD18emHjqVS/BYWYN41EVFdXDMEkD+oot0yl88AgeZsS8xJiwIADl2FAJSXVIk + 7QrxOMnHPygM8djBfNb1tysB5K7YyODFCEh1apHqkSuuAh6nzkI86rqaVDB+KUB+ek8A + WZCTkSYLyB7d1Z2bXbB9Ho+jxjJ4TKTu8RFbXP2FS3NUkOG61YQ8IO8V5p48yAPSgqxi + RQepjn8uOPdoweIxMnYXukfdko/qR8sD8mdjPQfI42mJm8KW+k/nHSQCUvWK7N6Dzj0O + tZLgMS0jp+A24PH9p/rGn4hH1We7+FvkAPnvP79+fPtaXfn6yb3rBJDbIlctAgfpYMMC + sns31Sdk125RAR4PnswtvPcEi6vil436RsgA8s/vf3/9bKwDQD5/dKsghwAylAPkIPKg + AASkqv/1EOJxuv/SsE2JacdzCm4J8MjuaKW+7x5bFl8EZAH5CgB5UQqQJob6vfVw+zkV + 67Fbd7py1cTCxgGKq4tWRW7bdfDkxesEj9Vk7hE3mBOfVjQyIg6QsIG5EJAJFJATyRzk + IIO+CEgVy/EvBo+DqHsEPIZuSmDw+BzcYx11j4hHjeS/2DpRAsiUbRGrFs2c6goO0sSw + PwJSxXqEyQ693vqGBI+uBI8RHB5fIR7FphANj0cOkA9v5YODZAE5ZpSVKQJSxWr8i+Kx + rwHF40RFeJQ8pkPDyYDdaT0CMoB8yzpIBpBTXB2sLUxgA3N0kKrUJMVj/4EmFtYOrlNm + EjymMO4R8ah1PWh9AAoBeWxvQnToEn/viWNGWiIgValF0hZxj4BHU8uRYygeoxP2Hs/J + v/WQc4+IR62rQnsDEADyO8xBAiDvXr94Ij1la8TKhTMRkKrWIrSnAI/pJ0hx9dVbLK5q + Twhi6VkJIOOj1yyZKwQkrglQkTaFePT2XxIKeDyGeBSLHLQ+DiEg678AIB/fvXaBADKc + AtLe2hwdpIqUSJvh8WgP7nHhyoitKQSPdxk8fse5R60rQssDEACyoe5TVdnzhzfzc47t + ZQDpzjlIfPSjikQJS+Uk7tF77pI10fGIRy0rQFzdKwHkTgJI3ymuCEgVCZFphsOjuTXg + 0XfhynCCxwvX7j4G9/ilHvEoLm1oZTRSgPxYVfaMBeRG4iAZQA7oi5sHqEaWAjy683i8 + +fB5WdWnuoaf/BYBWkkF7FQEEZAFZClxkFnpDCAnuyAgVaNE2ooQj5MpHnciHkWgATEN + gdPjb1jF2lBHAPngZv6Zo3viN64JBEA6kzlIBKRqREnxOIDOPVI8bozfc+xM/s2Hz8qq + PkrhEXfQEZNENDsWTpBwH+R3UmLlALklfMUCXwDkCCyxqkaNkrlHcI8ugMcV4Vt2pmcR + 91iK7lGzOS/m3jg9ygNydeCcae7OdpawgTk6SBVoksejs7v3nMA1gMejgMcHiEcx60Pj + Y+MECfuzsoAsukocJA9IY1jF2hPvg+ygJME99uzdf6Cx+QiCxwUcHotYPMIGc+x9Vni1 + qnENiKlDTo8UkLXUQd5gHCQLyOEIyA5Kkb6dwaORqaWds/u0OYGrEY9iUoGIxsIJkgVk + Renjoqvns9KTCSBnUAeJgOy4IOXxmAzu8SriUURKEMdQOD3KAHJ3/AYCSDdnOwRkx+VI + 91/tO8DIdLidsxuPR9ieHIqrtcK5R7xcFYcstDcKTpASQJYAIDPTkzfzgDRAB9kxTTJ4 + NGDc4wziHgGP568WlZRWkKU56B61l/6i65nToxCQl04fkQZkH1yk0xFFcu6Rw+OG+N1H + T+cjHkUnBjEMiBOkNCAPACCXz6cO0szYoB+WWDugR2k8zl8Rvjn5QCbiUQzJL8IxcHrk + APnm2YMbBJBxG0ICZ3u5OVEHiYDsiB7hxo4+Ave4IW73kdOXAI9v0D2KUBDaHhIVZFPT + HwrIz9UVL0uKrpzPBECuEwCyF85BtleRFI/9DIzNyNzjjPnLJXh8ie5R27kvxv5lAPkO + Adle5Sl+H3GPDB6d3LxmB4bweHyHxVUxCkLbY5IH5J0r5zIPJHGAtCIOEgGpWG0tHiV4 + 7CXA4zriHs9duVMCePzMFleb6Feg7TzA/sURAQEgfzTUfmQAmX0kNS6KcZC2ZA4SHWSL + ylN8giweoyTukeDxB3/fI849ikMO2h+FHCCLCSD3J8WAg/TxcLG3MhuMgFSsthaPSvBo + Be7RZ/7ydZuTEI/aT3lRj0AakDUUkHnZh1O3R4UEzPaa4GQ7zNRIHwHZovYUnSDBo63T + BOIeo+JSj2TT4uq7GsSjqGWhvcGxgPwNJdbG+s8fKl5KALlsno/HuNEISEVSa80xFo+D + zaxGu3gQPMYk7SfusfhlxQd0j9rLeHH3LATkt1ohIIMD/DwJIMkG5rhIpzUKlD6H4lHf + yHQYxWNASNT21MPZeWTuEfAIG8xJHkmO7lHcEtHs6GQAWS4B5FoEpLTA2vaXAI/jPHzm + LePxWA54bISVq7+bsLiq2VzXhd5kAfn66f0b1EGup4B0ZACph89ibZsc6Y0dffQNhxA8 + evoFBCMedUENIhijAkBePpu5f0cMArKNEhSeLovHtTE7WPeIeBRB0ot4CAoAWZh3Ckqs + CEihwNr4O7hHPQaPjhSP6xn3eP/pa3SPIhaDGIYmC8gXxbcBkPsAkEFMiXUozkG2UY3k + oXJkaQ4prlL3SPF49vKd4heIRzHkvJjHIAXIrzWV4CALc08dIoBc7Oc5Hh1kW8UI50vj + cXEwweOpvEKKx69YXBWzHLQ/NoWAzKCA9PeZBHOQCMg2SpLD41AGj0GAx30ZZy/fRjxq + P9vFPwJlgIwFQM6aOt7Rhs5BYom19aKU4NHGcbynH+AxNvXQqVyCx8oaxKP4FaHlEUoA + +Ysu0il/8QgcZMa+xJiwIADk2FEIyNZLkZwpxOMkH/+gMMSjljNct7pXAshdsZEIyLYp + kTlbiMepsxCPuqUGEYxWCpCf3hNAFuRkpMkCskf39mRnl3sPj8dRYxk8JlL3+Igtrv7C + pTkiyHkxD0EekPegxHqQB6QFWcWKDrJ1/7RweLQA90jwGBm7C92jmNNffGPjAfmzsZ4D + 5PG0xE1hS/2n8w4SAdkaRXbvQeceh1pJ8JiWkVNwG/D4/lN940/Eo/jyX2wj4gAJ28/9 + +Pa1uvL1k3vXc08e3LUtctUiKLE62LCA7N6tNQnZtc9RgMeDJ3ML7z3B4qrY0l6842EA + +ef3v79+NtZRQN4qyCGADOUAOYg8KAAB2fK/NUI8TvdfGrYpMe14TsEtAR7ZHa3Emw04 + Mm1HQBaQrwCQ/7+9c3+oaev6+ME5HLdEKqko5VYpROVSoVCEKNcuRBclhVRUlFshVC7J + raTcKUohSipKN1QKx3OO4/1H3jHnWmvvtXflnEN7t9daY/3wPJ7zPNaec6wxns/8jjnm + HFcIIIM5QOqP0Bo4ANvP/WM89ulLK1f1jc0tQT2u9A3Ztf/omSs3CR4byN4jNpjrbV8X + xO9zgIQG5hSQzx5RQMZSQDqQPciR2oMRkP8Yjr8weBxJ1SPgMWB7LIPHZ6AeW6l6RDwK + IiJ6d5DdAHIfAeQiR1tQkPojhiIg/zEeYbNjwECtEQSPto6LVvoGc3h8gXjsXQ8X2K93 + AmTx3VxQkCwgbSaZGiAg/zEaf6F4HKxN8ejQFR5l13QIzD1wuGq2gBIgX7EKkgHkXFtL + M2N9aGCOCvL7MUnxOHS4vrGZpe1cisd9jHpEPKrZnwX/c10C8uTB2PCA1R7zHWwmmiAg + vx+L5L8l6hHwaGAy0YbiMTz24Kms3LvFnHpEPAo+TtQ1AR4gP8IeJACy8OaV0yn7dgb7 + eC5CQP5zLML/ogs8ppwmydUXrzC5qi5HFsvvdAPImPBNq5fyAYk1Ad3GJh+P8z1WBwAe + TyIexRIgap4HH5Bt7wCQjwtvXJYD0sLMCBVkt5FI/ws5Hi1APXr6BO/cB3i8UUjx2PYR + 9x7V7NEC/zlFQNZVPSu+k5t18qASIPHqx26DEkrlZOpx/tLVm8JjCB7voHoUeGD00vC7 + AWTCziAfz4VzbRGQ3QYi819weDQyAzwu9PQJIni8fKPwMajHd4jHXvJqAf8sD5DtrU11 + VWUsILcRBWlPU6zDBmPzgO7CkodHewU8VtU1t7Z/lrcIELCL4NDVaAFlQFYQBZmRwgBy + znQEZHeRSP85H49zKB4TEI9q9F7x/RQXj1+hipUBZNGd3PMnDsRs2+QNgLQme5AIyO6C + kuJxGN17pHjcFnPg5HlQj2VVdU0KeMQOOuILHdXMiAtIOAf5kaRYKx4XUEDuCNqwYiEA + chymWLuLRtneI6jH6YDHDUE7ElIyiHqsQPWoGm8V/1u5eOwMyI3eS+bZW08wgQbmqCC7 + jEk5Hq3t5y/x3gh4PAF4LEI8ij9uVDZDLiChPysHyOtEQcoBqQdVrP3xHGSnkAT12H/g + 0OF6RuMIHldweCxg8QgN5thzVrhaVZn3iu/FXDxSQLaQFGvRbUZBsoAci4DsFIr0HzB4 + 1DEwmWBtPw/xKL7Q6J0ZcQHJArIGFOT1Sxkp8QSQrlRBIiC7Csgu8XjpOuKxd9xYNL/K + xaMSIJNiwggg7awnICC7Ckfaf3XwMB2DsROs7eR4hPbkkFxt4e894nJVNLGilolwASkD + ZCkAMj0lPlIOSG1UkMoxyeBRm1GPrkQ9xqdkAB5LK2pIaQ6qR7X4rhh/hItHPiCvZqYq + AnIQFukoRiSnHjk8hsUkncjMRTyKMULUPCcuIBUBmQyAXL+cKkhDPQSkYjTSDjoDh3J4 + XL4hKDI+OR3xqGbPFefPcfHIAfIlpFgJIKPD/L3dne2mUAWJgFSISILHQTz1GBadlJp5 + FfEozghR86xoQH779jcF5NuGmvLSgvxL6QDIQDkg4W5k3IOUhSRVj0O09QzJ3qPr8vUy + PJajelSz84rw55QAWYuAlAVeN3+Q43GKnbO7t78Mjy9rMbkqwghR85Q6A/J+/sX05DgO + kKagIBGQ8tgkePydh8dAoh4v5t8vBTy+ZZOr36hR1fwh8edEYQEeID+1tzQxgDyXmhgd + yijI8WQPEhWkLCCV8RjKqUeKx0/yc4+49yiK+FD7JDoBsoQA8khcBChIFyjSMTXURUDK + w5HDoymoR5fl6wMj4xCPavdZMf+gIiAbKSBzzh0ngPRyd545ZfwYBKQsHGlpDk2ujp8y + k6jH0OjE1HMkufqytrGlHfEo5khR09xYQH6FFGtH29v6mnIZINctc5k9bTICkheODB51 + DQGPswkeI+KOEPVYUl5Tj+pRTQ4r8p/hA/JDixyQu0P9vNycCCBH6WihgqRBSdWjlo7B + GIpHL//Q3YnHz+UgHkUeIuqdnhIgq2WA3IKAlKOR/IlNrgIeJ0+b7bJsnRyP1YDHDqhc + /foNk6vq9V7x/ZoyICvLHt4mCnL3VhkgR2gNGoB3sTLqUWvEKIJHJzcvPwU8Qv9VWccO + TK6KL0zUN6MuAJl3If3I3ggEpAIflfG4JWIvqx4Rj+rzVvH/UidAPn14K+esDJBWoCAR + kGS52o+2Jwc8WlE8bmXU48OnlZBcRTyKP1DUNUNlQD4vuQeAPAyAXMukWEfjHmRn9Ujx + eCEPkquIR3V5qjR+RwGQ7xtrKykgjxEFucrNaQYCki5bu8Lj2ZxbFI/vEY/SCBX1zLJL + QKZRQHq4zII9SASkLLk6mkmurgU8Hk67kHev5DniUT1eKp1fUQLkawLI7LPHEqMAkIsB + kOaoIOXq0dxqhpPbKr+tUYnHzmYTPL5uRDxKJ1bUMtNOgHwECjLt8J6IzWsBkFMnISC5 + 5CrB4ywXj7WbEY9q8Uxp/ki3gAwBQDoiIHnJVcCj42LEozTjRG2zlgHyC1SxNtdXP390 + 71pWZ0D266uwIyed/yDH46SpDB73UPX4iFWPX7A0R22+KoUf6gzIB0RB7o/iAGks7T1I + WXKVxWNI1H5Uj1IIjN6aoxyQnwGQbxhAnjq0Z/vmNR4L5ApSqoDs249cC6A72lSGx0Np + WdfuAR7fNLd1fEY89pbfivV3OUBC+7lPH943vK588uBm9pmjAEhfoiAtzVlA9u0jnUWq + fKYcHo3leDx6JvvWgyeYXBVrQPT2vBhA/v31ry+fO1opIO9eyyKADOAAOZJcFCBNQPLx + uMBjzebtew6dUsQj29Gqt78i/r5YLKAMyBcAyCsAyF0hvisZQOqP0Bo4QJJXP/bpSytX + 9Y3NLSG5utI3ZNd+wONNgscGsveIDebEEgUaNA8OkNDAnALy2SMKyFg+IAdLE5AMHkdS + 9Qh4DGDwePfRM1CPrVQ9Ih41yJPFMZRuALlvV7DvykWOtqAgpQpI2OwYMFBrBMGjLcFj + MMHjFcDjC8SjOHxfI2fRCZDFPEA62EwyNRipLUlAEjwO1qZ4dKB4jCXqUY5H2Tlkjfys + OCiBWkAJkK9YBckAcq6tpZmxPjQwl56CpHgcCng0s7Sdu4jgcR/iUaA+LqhhdwHI3KyT + B2PDA1Z7zHewmWgiTUCyeDQwmWjD4vHgqazcu8WcekQ8CsrLhTNYHiA/wh4kALLw5pXT + Kft2Bvt4LpIsIBk8DufjMeU0VY+vMLkqHOcW4ki7BeSm1Uv5gJRUTQAfj/M9VgeExx48 + iXgUonsLbsx8QLa9A0A+LrxxWQ5ICykqSDkeLUA9evoE79xH8Fj45AXB40fcexSclwto + wIqArKt6VnyHKMiYcAVASurqxz5McpWqx/lLV28Kj0E8CsijhT3UbgCZsDPIx3PhXFsL + MyOppVg5PBqZAR4XevoEETxevlH4GPD4rg3xKGx/1/jR8wDZ3tpUV1XGAnIbAaQ9TbEO + G/y7hAAJleRk75Hg0V6OxzvFz6rqmlvbP8vvQNb4T4sDFKAFlAFZwShIBpDQfk5qgOTj + cQ7FYwLiUYB+LdQhc/H4FapYGUAW3ck9f+JAjEQBSfE4TI7HbTEHILl6p7isqq5JAY/Y + IkCoLq/Z4+YCEs5BfiQpVgrIjJSEHUEbViyUHCB5eJwOeNwQtDMhJYOoxwpUj5rtx2IZ + HRePnQG50XvJPHtrUqQjHQUpx6O1/fwl3psAjyfO594pQjyKxd81fh5cQEJ/VhaQBdcv + 8wA5zkgPqlj7S+IcJOCx/8Chw/WMxkF78oUrNgTtoHgsYPEIDebYc1a4WtV4vxbqALl4 + pIBsISnWotuMgmQAOcEEGphLJMXK4FHHwGSCtf28Jd4bEY9C9WoBj5sLSBaQNRWPC65f + ykiJ5xSkdADZNR6vIx4F7N3CGzoXj50AGUYAaWc9YaxUAMnhcewEazs5HqE9OSRXW/h7 + j7hcFZ6bC2fEXEDKAFkKgExnAOkKKVYApLYUFCSDR21GPboS9RifknGJ4LGGlOagehSO + Swt6pFw8KgAyMzUphg/IQeIv0lHGY1hM0onMXMSjoJ1biIPnAlIRkMnxkUHrl1NAGkoB + kJ3wGBmfnA54LEU8CtGpBTxmLh45QL6EFOtVAGR0mL/3EmdWQYoekASPg4bpGHDqEfCY + mnkV8Shgxxbq0GlAfvv2NwfI8tKC/EvpioAcIvI9SIrHIdp6hmTv0XX5+iDEo1DdWfDj + VgJkLR+Q7s52U2iKVeSAlONxip2zu7d/WDSLx5eYXBW8gwttAgqAfNtQU156P/9ienJc + ZCCjIE1BQULzABEX6RA8QgcdGR4DCR4v5heUltc0vGWTq9+omYT2bXG8wrMAD5Cf2lua + OEAmRof6exNAjid7kKIGZPd4rIW9x0/yc4+49yg89xbeiDsBsoQBZAQA0gX2IMUOSBke + TUE9uixfHxgZR/B4H/EoPF8Ww4gVAdnIAPLccQJIL3fnmVPGjxE3IGV4HD9lJlGPodGJ + qedIcvUl4lEM/i24ObCA/EpTrG/ra8opII/ERQSuW+Yye9pkU0NdEStIFo+6hoDH2QSP + EXFHCB5LymvqUT0KzplFMGA+ID+0MIDMAUDuDvVjATlKR0u0CpLiUUvHYAzFo5d/6O7E + 4+dyKB4bUT2KwL2FNwUeIDva3tZXywC5RfyAlONx8rTZLsvWKeKxAypXv37D5KrwfFrI + I+4MyIe3KSC3+nm5OREFOWqEWAHJ4HHEKIJHJzcvPwU8Qv9VWccOTK4K2cWFNfYuAJl3 + Mf3I3gjRA1IZj1si9rLqsRrUI+JRWH4sltEqA7LyqQIgrRhADhDhXayAxwGDtAgerSge + tzLq8eHTytrGFsSjWBxcaPNQBuTzknt5F/iAHC3OFGuXeLyQB8lVxKPQfFhM41UA5PvG + WgDkrZyzkGLd6rcKFKRoAdkVHs/m3EI8ism5hTiXLgGZdhgU5FoPugcpSkByeBxtSpOr + a0E9Hk6/kHev5DniUYheLJ4xKwHyNQFk9tljiVEAyMVOM6zMaYpVbAqSh8cZTm6r/LZG + JR47m03w+LrxPapH8bi38GbSCZCPQEESQG4GQM6aNslUhIDk43GWi8fazYDHNMSj8JxX + hCPuFpAhAEhHcQJShkdzqxmOixGPInRr4U5JBsgvf3S0NddXPwdAZqUd3sMAcqockL+I + 5ZHjcdJUBo97KB4fserxC5bmCNedBT/ybgC5P4oDpDEp0hGTglTGY0jUflSPgvdj0UxA + DsjPAMg3BJDXstIO7dkOCnLBLBkg+/UVCx/7QnvyIdq6o01leDyUlnXtHuDxTTOU5iAe + RePagpyIDJD/+/Lpw/vG15VPHtzMPnO0EyD79hFHQHJ4NGbVI+Dx6JnsWw8wuSpI9xXf + oBlA/v31ry+fO1opIO9eyzpFALmGBeRIcg5SLIDk43GBx5rN2/ccOiXH42fEo/g8XFgz + 4gAJ/VkBkA0UkFcAkLtCfFdCitXS3FifKEiR3GzVpy9TuWpsbgnJ1ZW+IbsIHm8+eFL5 + uoHsPWKDOWF5rwhHywESGphTQD57RAEZuz1AhIBk8DiSqkfAYwCDx7tUPbZ2EDyyDR9F + +KFxSoKwgDIgX4CCBEDu2xXsu3KRoy0DyIHiACRsdgwYqDVCXwGPVwCPLxCPgnBWKQyy + EyCLeYB0sJlkajBSe7A4FCTB42BtikcHisdYoh7vPnoGyVWCR9k5ZCl8d5yjZlrgu4Cc + a2tpZqwPDczFAEiKx6EUj7aOi1b6Bu/ad/QM4lEz3VK6o+oCkLlZJw/GhoOCnO9gM9FE + LIBk8WhgOslGjsfcu8WIR+k6vwbOXAmQr6iCPJ2yb2ewz8pFIgIkg8fh+sZmlrZzFfD4 + CpOrGuiXkh1S94BcvZQPSIHXBHB4NJlo4zDfY3VAeOzBk1mIR8m6vcZOnAfIj23vG169 + eFx44woDSE8ApIU4FKQiHj19gnfuSzl95WbhkxcEjx9x71Fj/VNyA1MEZF31s+I7REHG + hG/iA1LgN1v1YZKrBhSPS1dvCo9BPErO04UxYQVAvmMAefl0SsLOIB/PhQSQRsJPsXJ4 + NDKzAPXI4vHyjcLHgMd3bYhHYXiqREbJA2R7a1NdVRkfkPZcilXQgIRKcrL3qITHO8XP + qupw71Eibi6YaSoDsgIUpAyQ0H5O+IBUwONCT5+gnQkppxGPgvFQaQ2Ui8evUMUqA+T5 + EwdithEFyQBy2ODfBQxIisdhFI/280E9bqPq8U5xWVVdU2s7luZIy981frZcQMIxj49t + oCApIDNSEnYEbfBcKAJA8vA4fQ6LxwyCxwpUjxrvnBIcIBePckAW3cmlgNzovWSevTUp + 0hEyIOV4tAY8egMeD5w4n3unqBMesYOOBL1fA6fMBSTtz0oBWXD9MgPIFQSQ44z0oIq1 + v0DPQcrwOA7aky9csSFoR0IK4lED3RCHxFqAi0ceIG/zATnBBBqYC1ZByvA4wdp+3hLv + jYhHdHwNtwAXkHxAXspIiQcFKXhAAh77Dxw6XM9IAY/XC1j1CP1X2WPIuFrVcC+VzvC4 + eKSAbCF7kEU8QNpZTxgrXEAyeNQxGKuAR2hPDsnVFn5yFeNROg6v6TPlApIFZE1FacF1 + DpCujILUFqaCVMCjK1GP8SkZlwgea0hpDuJR011TkuPj4lEBkJknkmLCSIpVyIDk4dGO + qMewmCRIriIeJenmwpk0F5AKgExPjo8MWr9cyIBk8KjNqEcWj+mAx1LEo3CcU4Ij5eKR + A+RLUJBXM1OTosP85YAcJLwinS7wmJqJeJSghwtsylxAcoAsLy3Iv8QHpKGe9hDB7UFy + eDQkyVXX5euDIuOTEY8Cc01JDpeLRxaQtXxAujvbTaEpVsEBkuBx0DCaXLVzdvf2D4tO + Ss28CurxJSZXJenmwpk0Dchv3/6mgHzbUFNeej//YnpyXGQgqyAJIH8XVpEOxeMQbT1F + POYXlJbXNLxlk6vf6MSF851wpNKwAA+Qn9pbmuSADPX3JoAcT/YgBQZIOR6nKOGxFvce + peHWwp1ld4CMAEC6wB6kqeAASfAIDeb0DE0Z9RgYGZecfjH/PuJRuF4qnZErArKRAeS5 + 1MRoCsiZAgSkDI/jGTyGRiey6pHg8ZP8SnIszZGOmwtnpoqArK8pLyEK8kgcBeRsAkhd + QSlIBTy6LF8fGMHgsQTVo3CcUsIj7QqQOeeOJ+4O9fdydwZAjjHQ0RKQguThcaazu5c/ + 4PH4OZpcrW1EPErY0YUydRaQXyHF2tH2tr5aDsh1y1xmT5ssLECyeNQ1NJ08bbbLsnWA + xyNEPQIe65nk6tdvmFwVim9KcZx8QH5oYRQkA0g/LzcnAshR0J9VKEU6FI9aOqPGjJ9C + 8OgXuhvwmEP2HgGP0H9V1tAK1aMUnV0Ic+4GkHsjtggPkMp43CLDYzXgsQMOdiAeheCT + Uh6jMiArnz68TQG5lQLSigHkAEFcNcfgcQTFo5Obl99WBo8PyyoRj1L2cUHNXRmQz0vu + 511IPyJAQHbG415Qj3mgHhGPgnJJSQ9WAZDvG2sBkLdyzkKKVXCABDwOGKRF8Gg1k4/H + p4hHSXu4wCbfGZD38i6kHQZArmVSrKOFsQfZBR4Pp1/Iu1fyHPEoMJeU9HCVAPmaADL7 + 7LHEqK1+q9ycZliZ0xSr5itIRTyuIurx2NmcWw8JHt9jclXSPi6oyXcLyM1rPVxmwR6k + IADJ4XE03Xv0WLslYu/hNMSjoDwRB0ss8D1ALnYUCiBleDS3muHkBniMAjxmEzy+Rjyi + owvJAjJAfmGKdJ4/ogpyTwQF5NRJQgAkH4+zXDzWbmbx+AjVo5BcEccKFugGkPujQvxW + CQWQfDw6Ll7lF4J4RN8WqgXkgPzc0db8phoAeS0r7ZAyIPv1/UVjH+bco+5o00lTGTzu + OZyWde0ewWMzlOZ8wdIcoTqnBMfdCZBPHtzKPnNUDkhjUsWqySlWDo/GoB4ZPO5H9ShB + TxbJlBlA/v31ry88QJ46tGf75jUeC2bJFKTmArJvP3ItgByP2/ccYvH4BvD4GfEoEkeV + yDQ4QEJ/1k8f3je8rnzy4CYB5K4Q35WgIC3NWUD27aOZC9Y+fZnSHBaPviFR+4+eyb75 + 4AmbXMUOOhJxZLFMkw/IVlCQzx7dvZZFABnAAXIkuShAUwHJx+MCjzWbAY+nsq7dBfXI + 4ZHtaCWW74XzELcFlAH5AgB5hQAymADSFgCpP0Jr4AAN7c8Kmx2kclXf2NwS1ONK35Bd + gMcrFI8NpDQH8Shu7xXh7DhA/gkKkgfIWApIB6IgR2oP1lRAMngcSZOrgMeA7bEMHp8B + HltBPcrOIYvww+GURGmBbgC5jwBy0VwGkEM1FJAEjwMZPNo6LlrpG8zh8cVrxKMovVUC + k+oEyOK7uVmnDrKAtJlkYqCxgCR4HKxN8eiAeJSAr0phikqAfMUoyBQZIM2M9YdrJiAp + HocO1zc2s7SdS/G4j1GPiEcpOK5Y59glIE8ejA0PWO0x38FmosYCksWjgclEG4rH8NiD + p7Jy7xajehSrq0piXjxAfoQ9SABk4c0rp1P27Qz28SQKUlMB2QUeU06T5OqLV6geJeG5 + Ip1kN4CMCd+0eikfkBpWE8DH4/ylqwMAjycRjyL1USlNiw/ItncAyMeFNy7LAWlhZqSJ + ClKORwtQj54+wTv3AR5vFD4meGz7iHuPUnJhcc2VB8j21ua6qmfFd3KzTh5kAGnPKUgN + u/oRKslJcpWqR8DjpvAYgsc7qB7F5ZtSnE03gEzYGeTjuXCOrUYCksOjkRngcaGnTxDB + 42UGj+8Qj1J0Y9HMmYtH2sC8tamuqowF5DaiIBlADhusWc0DeHi0V8BjVV1zazuW5ojG + OaU4ES4g4ZjHR6IgK4iCzEhhATldAwHJx+MciscExKMUXVeUc+biUQ7Ioju5508ciNm2 + yRsAaU32IDULkBSPw6h6pHjcFnPg5HlQj2VVdU0KeMQOOqL0WJFPigtIaD/HArKAAnJH + 0IYVC6GB+TgNS7Hy8Dgd8LghaEdCSgZRjxWvGlA9itxZJTA9Lh47A3Kj95J59tYTNAyQ + cjxa289f4r0R8HgC8FiEeJSAs0philxA8gB5nShIOSD1oIq1v4acgwQ89h84dLie0TgL + wOMKDo8FLB6hwRx7DBlXq1JwXhHOkYtHCsgWkmItus0oSBaQYw10NEdBMnjUMTCZYG0/ + D/EoQn+U/JS4gGQBWVPxuOD6pYyUeAJIV6ogNQeQXeLx0nXEo+S9WDwG4OJRCZBJMWEE + kHbWEzQIkBwex06wtpPjEdqTQ3K1hb/3iMtV8Tio1GbCBaQMkKUAyPSU+Eg5ILU1Q0Ey + eNRm1KMrUY/xKRmAx9KKGpJcRfUoNdcV5Xy5eFQAZGaqBgJSGY9hMUknMnMRj6J0S+lO + igtIRUAmAyDXL6cK0lBPIwDZCY+R8cnpiEfpOq5IZ87FIwfIl5BivQqAjA7z93Z3ZhXk + oN6vYiV4HDRMx4BTj4DH1MyriEeRuqV0p0UD8tu3vykg3zbUlJcW5F9KVwTkkF7fg6R4 + HKKtZ0j2Hl2Xrw/i8FiO6lG6vivGmSsBslYJkFNoirXXASnH4xQ7Z3dv/7BoFo8vazG5 + Kka3lO6cOgPyfv7F9OS4yEBGQZqCgoTmAb1apEPwCB10ZHgMJHi8mF9QCnh8yyZXv9GJ + SPc74szFYQEeID+1tzQxgDyXmhgdShXklPFkD7KXAfl9PH6SX0mOe4/icEopz6ITIEsY + QEYAIF2gSKf3ASnDoymoR5fl6wMj4wge7yMepey3Yp27IiAbWUAeJ4D0cneeqQGAlOFx + PKMeQ6MTU8+R5OrL2saWdsSjWD1TovNiAfmVSbHW15RTQB6Jiwhct8xl9rTJpoa6vaog + O+ExIu4IwWNJeU09qkeJOq2Ip80H5IcWBpA5544n7g71YwA5ZpSOVi8qSIpHLR2DMeOn + zHR29/IP3Z14/FwO4lHELintqfEA2dH2tr5aBsgtmgBIFo+6hqaTp812WbYuUAGPHVC5 + +vUbJlel7cHimr0yICvLHt7mAOnmBApyzKgRvQdIBo8jRhE8Orl5+SngEfqvyvo9YnJV + XG4p3dl0Aci8i+lH9kZoACCV8bglYi+rHqtBPSIepeu14p15J0A+ZQG51c8LAGnFAHJA + r1xWDngk7ckBj1YUj1sZ9fjwaSUkVxGP4nVKKc9MGZDPS+7lXUg/rAGA7BKPF/IguYp4 + lLLHinvuCoB831hb+fThrZyzxxJ3b/Vb1buA7AqPZ3NuIR7F7ZBSn12XgEwjgFzL7EGO + 7p09SA6Po5nk6lpQj4fTL+TdK3mOeJS6z4p5/kqAfE0AmQ2AjKKAnGFlTlOs6leQinhc + 5Qfq8RiDx9eN71E9itklpT23bgG5ea2Hyywo0ukVQPLxOMvFg+IxDfEobVeVxOy/B8jF + jr0FSBkeza1mOC0GPEYBHrOJekQ8SsItpTtJGSC//NHR1lxf/fwRpFjTDu+JoICcOqk3 + ACnH46RpBI+bI/YcJnh8xKrHL1iaI12HFfnMuwHk/qgQv1UUkMakSEe9CpKPR0fAYwji + UeReiNOTWUAOyM8AyDcEkNey0g4pA7Jf31/U9jDXAuiONp00VYbHrGsUj81QmoN4lH08 + /IPoLNAJkE8e3Mw+c7QXAcnh0RjUI4PH/ageRed3OKFuLMAA8u+vf33hAHn3WtapQ3u2 + b17jsWCWTEGqD5B9+5Fbcxg8LgD1uH3PoTQGj28Aj58Rj918SPzHorAAB0hoYP7pw/uG + 15UsIHeF+K4EBWlpzirIvn3Us2Dt05epXGXx6BsStf/omeybD55Uvm4ge4/YYE4UboeT + 6M4CHCD/BEC2goJ89ogCMnZ7AAfIkeSiAHUBksHjSKoeF3isIXg8lXXtLiRX3zS3Ujyy + DR+7mw/+c7SAkC2gDMgXAMgroCB3BRNA2gIg9UdoDRygpqsfYbODHOzQNza3BPW40jdk + F+DxCuJRyB6GY/9PFvguIB2IghypPVhdgFTCY8D2WAaPz1g8ys4h/6cp4v8YLSAYC3QD + yH0EkIvmMoAcqiZAEjwOZPBo67hopW/wrn0MHl+gehSMQ+FAf84CnQBZfDc369TB2HCi + IB1sJpkYqA2QBI+DtUcawN6jA6hHDo/FiMef+8T4twVkASVAvqIK8nSKDJBmxvrD1QNI + isehw/WNzSxt5yIeBeRDONQetECXgDxJALnaY76DzUS1AZLDo8lEG4f5gMfw2IOnsnLv + Ih578GPjqzTeAjxAfoQ9SABk4c0rAMidwT6eREGqC5BKePQB9ZhymiRXX7zCvUeN9yIc + YI9ZoBtAxoRvWr2UD0gV1wQo4HHpaoLHk4jHHvvK+CKhWIAPyLZ3AMjHhTcuywFpYWak + DgUpx6MFqEdPn+CdBI83Ch8TPLZ9xNIcobgTjvNnLcADZHtrc13Vs+I7uVknDzKAtOcU + pIqvfoRKcppcpepx6epN4TEEj3dQPf7s18W/LzQLdAPIhJ1BPp4L59qqBZAcHo3MAI8L + PX2CCB4vM3h8h3gUmkvheH/CAlw8foUq1vbWprqqMhaQ24iCZAA5bPDvKgUkD4/28/l4 + rKprbm3/LG8R8BPzxL+KFhCEBbiAhGMeH4mCrCAKMiOFAST0Z1W9guTjcQ7FYwLiURC+ + g4PseQtw8SgHZNGd3PMnDsRs2+QNgLQme5CqBSTF47CRBqAeKR63xRw4eR7UY1lVXZMC + HrGDTs9/fnyjplmAC0g5IAsoIHcEbVixEAA5TsUpVh4epwMeNwTtSEjJIOqx4lUDqkdN + 8xYcj6otwMVjZ0Bu9F4yz956gooBKcejtf38Jd6bAI8nAI9FiEdVf3l8v0ZagAtI2sCc + KsiC60RBygGpB1Ws/VV0DlKGx3EWgMcVHB4LWDxC/1X2GDKuVjXSe3BQPW0BLh4pIFtI + irXoNqMgZYDUUZ2CZPCoY2Aywdp+3hLvjYjHnv6++D6BWYALSBaQNRWPC65fykiJJ4B0 + pQpSdYAEPPYfOHS4npECHq8jHgXmQzjcnrMAF49KgEyKCSOAtLOeMNZAZYDk8DhWAY+3 + qXps4e894nK15z44vkmzLcAFpAyQpQDIdAVAaqtGQTJ41Gbw6ErUY3xKxiWCxxqSXEX1 + qNmOg6NTiQW4eFQAZGaqGgDJw6MdUY9hMUknMnMRjyr5zPhSoViAC0hFQCbHRwatX04V + pKGeSgDZCY+R8cnpgMdSxKNQXAfHqQILcPHIAfIlpFivAiCjw/y93Z1ZBTmo56tYu8Bj + auZVxKMKPjG+UkgWoAH57dvfFJBvG2rKSwvyL6UrAnJIj+9Bcng0JMlV1+Xrgzg8lqN6 + FJL34Fh72gJKgKxVAuQUmmLtcUASPA4apmMwdsIUO2d3b/+w6CQGjy/rmjC52tPfGN8n + IAt0BuT9/IvpyXGRgYyCNAUFCc0DerRIh+AROujocXgMpHjMLygFPL5lk6vf6MAEZEgc + KlqgByzAA+Sn9pYmOSBDqYKcMp7sQfYwILvHYy3g8ZP83CPuPfbAF8ZXCMoC3wOkCxTp + 9DwgZXg0ZdRjYGRccvrF/PuIR0E5Dg5WJRZQBGQjA8hzqYnRFJAzVQBIGR7HM+oxNDox + 9RxJrr6sbUQ8quQj40uFYwFFQNbXlJcQBXkkLgIUpMtsAkjdHlWQCnh0Wb4+MILBYwmq + R+E4DY5UZRbgA/JDCwPInHPHE3eH+nu5OwMgxxjoaPWgguThcaazu5c/4PH4uRzEo8o+ + ML5YWBZgAfkV9iA72t7WV8sBuW6Zy+xpk3sWkCwedQ1NJ0+f7bJsHeDxCFGPgMd6Jrn6 + 9RsmV4XlQTjanrRA94D083JzIoAcNaLnAEnxqKUzasz4KQSPfqG7eXiE9uSyfo+YXO3J + j4zvEo4FugHk3ogtPQ9IHh6nETxukeGxGvDYAQc7EI/C8RwcqSosoAzIyqcPb1MFuZUC + 0ooB5IAeuYuVweMIikcnNxkeH9LkKuJRFZ8X3yk0CygD8nnJ/bwL6UdUAMjOeNwL6jEP + 1CPiUWheg+NVlQW6AOStnLOQYu1xQAIeBwzSIni0mknwuJVRjw+fVsLeI+JRVR8Y3yss + C3QG5L28C2mHAZBrmRTr6J7Zg+wSjxfy7pU8RzwKy2NwtKq0gAIg3ze+BgV5K/vsscSo + rX6r3Jxm9JiC7AqPZ3NuUTy+Rzyq8hPju4Vkge8A0sNlFuxB9gggOTyONp1Mkqtrt0Ts + PZyGeBSSo+BY1WKB7wFyMQDSnO5B/myKlYfHGU5uq/y2RiUeO5tN8Pi6EfGolg+NPyIM + C8gA+YUp0nn+iCrIPRGb1xJATuoJQPLxOMvFY+1mxKMwnANHqXYLdAPI/VEhfqsWO/YM + IGV4NLea4bgY8aj2j4w/KBwLKACyub4aAHktK+0wC8ipckD+8qOPHI+TpjJ43EPV4yM2 + ufoFS3OE4y44UhVboBMgnzwgKVY5II1JFevPKEhlPIZE7Uf1qOKviq8XrAXkgPzc0db8 + hgHkqUN7tm9eAwpSBsh+fX+Uj8ytObqjTXl4zLp2D/D4prmt4zPiUbCugwNXgQU4QEJ/ + 1k8f3je8rnzy4Gb2maMASF9GQbKA7NvnxwKSw6Mxqx4JHs9k33rwBJOrKvia+ErBW4AB + 5N9f//rCAfLutSwCyIA1Hgt+HpB9+5FL5Rg8LvBYs3n7nkOnEI+C9xqcgKos0BUgrwAg + d4X4roQUq6W5sT5RkD949WOfvkzlqrG5JUmu+gIej57Jvknw2ED2HrH/qqq+K75XoBbg + APknALIVFOSzRxSQsXJAjiQ36fyYgmTwOJKqR8BjAIPHu1Q9tlL1yPZDFqjxcNhogR62 + gDIgX4CCpIAM9l25yNGWAeTAHwMkbHaQgx36DB5X+obsAjxeQTz28CfE14nJAp0AWcwD + pANJsY7UHvxjgOyEx1iiHu8+egbJVYJH2TUdYrInzgUt8DMW6AaQ+3YRQM61tTQjCvKH + AEnwOJDBo63jopW+wRweX6B6/Jkvhn9X1BboApC5WacOxoaTFKuDzSQTgx8EJMHjYG2q + Hh2oemTwWIx4FLU/4eR+zgI8QH6EPchXVEGeTuEDcvjQHwCkDI9mlrZzKR73MeoR8fhz + Hwz/trgt0CUgTxJArvaY72Az8QcByeLRwGSiDcVjeOzBU1m5dxGP4vYmnN3PWoAPyDYK + yMIbVwCQO4N9PFkF+QOApHgcOlzfmMGjT/CufSmnSXL1xSvYe/yIe48/+9nw74vVAp0B + eSc36+TBmPBNq5fyAfmfiub4eJzvsToA8HgS8ShWF8J59aAFFAD5DhTk48Ibl+WAtIAU + 638GpCIePX2CdxI83iikeGxDPPbg58NXic0CPEC2tzbXVT0r7gqQ/+lu5D5McpWqx/lL + V28KjyF4vIPqUWy+g/PpeQt0A8iEnUE+ngvn2lqYGf1XQMrxaAHJVRaPl28UPgb1+A7x + 2POfEN8oIgtw8fgVqljbW5vqqspYQG4jCtKepliHDf79PwASDlqRvcfOeKyqa25tx9Ic + ETkPTqXnLcAFJJyD/NgGCrKCKMiMFAaQ0MD8vwKSw6ORGeBxoadP0M6ElNOIx57/cPhG + UVqAi0c5IIvu5J4/cSDmBwFJ8TiM4tGeqMdtMQdOngf1WFZV16SAR2wwJ0p/wkn9pAW4 + gFQG5I6gDZ4L/zMgeXicPofFYwbBYwWqx5/8UPjXJWEBLh67AKT30nn21qRI598ryC7w + eALwWIR4lIQz4SR/3gJcQEIDc1ZBFlwnChIAuYIActx/SLEq4XFD0I6EFMTjz38jfIN0 + LMDFIw+QtxkFudF7CQBygomBzr8FpByP1vbzlnoT9Yh4lI4r4Ux7wgJcQLKArKl4XHD9 + kgIg9aBIp/+/uElHhsdxFqAeV7B4vF7AqkdoT87e0oHJnJ74cPgOUVqAi0cKyBayB1n0 + g4Bk8KhjYDIB8LjEeyODx9uoHkXpNzgpFVmAC0gZIEspIOOJgnSlClJP+98AEvDYf+DQ + 4XpGMjzGp2RcInisIaU5iEcVfT98rbgswMWjAiAzYQ8yjChIO+sJY/+dgvweHlv4pTm4 + XBWXA+FsetYCXED+HCAZPGozeHQl6pHBYynisWc/F75N5Bbg4pEPyKuZJ5L+GyA5PI6d + YG1H1GMYJFczcxn1iHgUuQvh9HrSAjQgv337WwGQ6cnxkUHrl1MFafjPCrIrPKaDekQ8 + 9uSXwndJwQJKgKx9CSnWq5mpSdFh/nIFOej7xzwIHgcN0zGQ4zHpROZVxKMU/Afn2MMW + UADk24aacpJiJYAM/LeApHgcoq1nSJKrrsvXB0XGJyvj8Rv9mR4eOr4OLSA6C/AA+am9 + pUkBkO7OdlNoivW7gJTjcYqds7u3f1h0UirF48vaJlSPonMYnJBqLdAZkPfzL6Ynx3GA + NAUFCd11ui3SIXiEBnN6hqYMHgMj4wCP+QWl5TUNb9m9R8Sjar8hvl08FugGkInRof7e + BJDjx8Ae5HcASfGopWMwZjyDx9DoRB4eP8k7duDeo3icBmeiOgt0DcgjcRGB65a7zJ42 + 2dRQlwFk1/2S2fbkhqaTp812Wb4uMCLuSPrF/PuIR9V9MXyzmC2gCMhGRkGeO564O9TP + y81pptX4MaNIf9ZubtLh+q+OGW8108nNyy90d+LxcyS5StUj4lHMnoNzU4kFFAFZX1Ne + QhTkkb0RW9Yto4AcTQHZdX9Wcskj2558tsuydVsi9lI8lpTX1KN6VMnnwpeK3AJ8QH5o + IYB8eDuHAHKr3yo3xxlW5gSQ3XTXgWwO12COtCf32wp4PJtz+yHgsbGlHfEoctfB6anC + Aiwgv0KRTkfb2/rq8pJ7eRfSDu+N2LzWY8Esrj9rlwtWWirHXvK4wGPt5og9h9Mu5N0r + eV6NeFTFp8J3SsACyoCsfPrwVvbZY/ujQmT9WZnmAZ0zOtxBK3LJIzSYg/bkx85m33r4 + tBLw+AHxKAHnwSn2vAWUAfn80b1rWacO7dkesIa9G7mbLQ+yXB0Emx1QKgfXdECLALY9 + OcVjB5x7/PoN9x57/nvhG8VtAQVAvm98Xfnkwc0rZ46S9nPMzVZMTUC/Tt2uyHIVsjlM + LQActKIddEiDOaY9+Z9f8ZoOcXsOzk4lFlAAZPOb6mfFd2n7uW0boSZgJtQE0C2PX/sq + L1iZ5eooY3Oy2QGlcswtVrSDThviUSWfCl8qAQsoApJtP5eRAlXlsOXRfUYHNh/hZAfT + sgOyOVsiSKkcc4vVe1SPEvAbnKKKLMAHJGk/xxy7olsei+faWpoZwU1zv/dXXrAyy1Xm + ZMdCT9+QKKgFyGFvser4/AXVo4q+Fr5W7BbgAxK668CxK1ITcBgyOqvJXeXklAfU6Cgv + WKEYYODQEaOgdHUmnOwg2Ry62QGV5KTBHKpHsXsNzk9lFpABEtrPwbEruuVBMjpBG5gi + VqjRge5zfRUzOiS7CgeRTSZNneXisS4Q7iQ/fQWyOZWvoRYA8aiyb4UvFr8FZIAk7eeg + JoDJ6MBNc36rFjvSBSsUlSuXBIB8HAzZVXrLo6cv1OakwrU5xWXVb5rxkkfxuwzOUJUW + YAD5N9xs9enDe9IOktwTAEWsZMFKjiWTmjklAUlqV4cO14fs6gwnd++NdLnKnOx4R7M5 + uPeoyg+G7xa1BThAkqI56JdMLtLJgRqdnUHrmQwrNLtSFpBEPmqNoMUA8z3WbImMp8tV + WpvT/hlbBIjaXXByqraAHJB0wfr8EWxBnjgQvdVv5SJyVTmcgiQCkr8D2YfIx5EGzMnH + DcG72JNWVWS5Ks/mqHrc+H60gBgtIAPkl8/sgpVmWMOhJMAJDnkY6w8HAakQj31/lcnH + RSv9QqEhcta1e3DSqoEuV7E0R4xegnNSmwU4QNIFK8mw3rxyGkoCtpAaVtjxGDUCWnko + 7EBCOgcuzqHy0Q3k454jUAxQ+PgFza7KlqtqGz7+EFpAVBZgAQkZHViwvqkmJQFwCnIX + KyChGSQcglSOx6HDye6jHdSSb44g8hGOdsDFAK1QK4eV5KJyDpyM+i3AAvIvyLC+a6h+ + XgKHPE6CgPT1hJvKJ5uOpgkd3gYkm84xmWjjsMBjXdDO/cfoRR3Vb+BiAJCPeLBD/V8Q + f1FMFuDi8X+f25kdj/yLaYdiyQ7kXFs2ocOv0IF4JGetTCeRi6w2BO9OhI7Idx/BUat3 + bZ++cA1YxWQfnAtaQK0WYALyKwhIUqLz5MGNyxnJe7dv9HajCR1yD6tiPJL06mjTydAS + GaoBYg6eysqj98q9/0B2O+jL1Dp8/DG0gKgswMYjCEioKYdrdG5ln0mJh4oA6AUJZ66U + E6yw3cFW58DVAH5hsYfgnscCLp3zFeNRVK6Bk+kNC5CA/PY3P6FzbN+OLWs9SIIVzkAO + Vdjw6EO2O/QM4aoOx8Ve/ttIevVGIRSvknQOKx97Yw74m2gBsViAiUdI6DAlrHdyM0mC + dZ3HAgebiWMNYMPjt37yggCIR9juMDK3pNVy2+OSSTE5VOdgPIrFHXAevWwBumD9+y+o + CCAJVqjQOX8iMYq/4dFfOR5J9So9bAXbHWfgJquyl3W0OoepBujl6eDPowWEbQESkNCc + lVTowL3IZMMjaXcwPXI1yZRsQPLjkZQDQDX5eCs75yWrAyISSDwWlVXx4lHYtsDRowV6 + 2wJ0wfqVxuOripL7eVmnDkSH+KxwnQN9PAxGag3iF+hw8QjlAEugHCDhKFyEDPHIbD9S + Pvb2bPD30QICtwAE5DeIR7oBWXo/78Kpg7x4HNZdPJLyHIV4pNuPAjcFDh8t0OsWYOLx + D148xmwlfGQLdLrhI8Zjr384HIAoLfBj8YjrVVE6A06q1y3wnfUqKWBV4CPsdyjlc+jd + cvL8aq/PBgeAFhC2Bdh4pCcgO+dzFA5c0f3H7+53CNsUOHq0QG9bAMLx/779y/0O5XoA + clsHd9wK63N6+0vi74vBAiQe2XqAGubGju/WA0C9nJGZ5QxHNy9/OI6ccfkGXPaI9Tli + cAScgyZYgOIR4vFT27v66ufFUC+X+r16ObaeHFrNrYJ68sPpF6FXAL0egBxHJu/ShDnh + GNACQrUAE49w5aPsgoBj+3cEMvXk5MIOxXryfsx5KwvZeasLcN6q4lXDe3q7HHkXBqRQ + PQHHrQkW4OLxYxvTwwPOWyVEbKY3sHZx3grOI5Pr5bjzyEknsq7BeWTmOiv2PLImTArH + gBYQpgUo0r59/ZPcwAoXWkETyIzkuO2b4IK5meSCOaXzyL+Q61ehW8BE6BawbF0QXPeY + mXsHes2RTuVf/mSvlxOmIXDUaAENsACNx7/hfoD2lsbXL+gN5Ydjt/mTjgEW0OIKLmDl + 3w/wCxSwkm460Bt5/lK4Djnh6Jls+QYk3p+jAR8UhyBkCzB4pOlVeryDlJMfjAn181wI + FyKbQkcduKCcd58ViUdSEMAeuNq+N5lNsEI3HUjoICCF7As49t63ABuPNJ1DOurcyT2f + mhglP24FDQMU73ukFwQYjbOwnbsYEqwx0G0O2neQhM4H6G6F8dj7XxRHIGAL0HCE0x3k + ug4mnZNz9ug+kl5lrwdQbqjD9AugF1q5rvAJ2Z3Ev2COu/ARU6wCdgkcei9agFuuMvKR + vV4uLiLAe4nzTHqdleJ2xy+/9Ok3gEno2JD2j0E79x1jTkDWNdMLkRGQvfgx8aeFbgEW + j6QagN4OQE4/ph2KCaPpHNKxnDSAVOjfQS5E5i4oJzcExBEBWfiEaRggW7AiIIXuGTj+ + XrAAF45cuwAiH6E6Zze9HYCeftQerNSwvA9J6NCKObhhjghIuIGVbagDNyJju/Je+Ir4 + k2KxABePTP9HtmF5SsKOQOZ2OaZ9h0J69ZdfaIMrciPytDkgIIOjuIZzcIUOdPDgMjoI + SLH4CM5DbRbgwlHWH7n0PtNuDqoBnGdajR8zavjQ3/mnH8m9j5DQIQJyLNmB9Fi7hV2w + 0hJWyLBCRofp4YEBqbbPiD8kDgsw4Qh3IQMeaXcruttxIknejpXKR4XtDpLQkTcshyMe + 22IOQgdI2sKDlOjIAYkBKQ4vwVmoywI8PH4mxTmQXSXdHxN2BK3rtl05xCMISNIywBRK + ysmCddf+Y2fhDlbScq6lHbYguZoArCtX13fE3xGFBXh4hM1HOGsFd6/mZUFzq/BNsNtB + mnfQbqxK8pEIyAGkxRWzYF2zOWLvEejhQXvqvGMBiStWUTgITkKdFpCFI6hHwGNT7Uto + xpoDzVijQnxpsdw4ZrdDablKF6yw46FvbGZJM6yh0YmpmVdpDWtTKwBSnmLFFas6vyf+ + lqAtwIUjUY8Ej3A1Oc3mJMdFQC8dUpxjYjBy2GDYfeRXr9KETj+6YCUZ1tkuy9YHRsan + ZFy6XlBaAYeuFAGJASloF8HBq88CbDhCMofisbWpror0Ks9MTYoO8/diej+OGgHNyhUO + d9C+On3ognWEAfQsJ008Nm6LOQA1cwwgiYIkgGRXrBiQ6vui+EtCtgCHx7+/0uQq4LGi + tOD6pYyU+B1B65e7zIZWAYa6UJyjvNtBIrIvZFihJMBwHM3obAjakcAHJOxBylM6GJBC + dhIcu7oswIUjrFbhZAenHq+eP3EgZttGms2ZMNZAB852dF6uEgEJC1a4JMAEtiDnLfXe + RABJFeTL2saWD59gzwMDUl0fEn9HBBaQLVaZ1WoHafxYXnI/n8HjBtIpwAKyOcOHDuzf + eblKdjygJAAyOkZm9BYdHwJIkmIteV5dD1XlsGKFKh1uxYqEFIHD4BRUaQF+ONLV6vuG + 15VPH1L1SPE4z96ay+Z0yq4yGZ3+sAUJgJxoA7cEEAWZlHou59bDJy9ekZTOHwoSEgNS + ld8S3y14CyiGI12tvql+9uhe3oX05PjIoA0ryM0AZkb6gEfI5ihnV2k8Mhkd2PIggFyx + ISgy7kgaFOkUQyPIppYPHxkJiYQUvKvgBFRvAS4cSWoVxONnWK1CMudx4c0rZ48lRoeB + emTxCDfndJXNkWd0CCBBQS7x9g/dnXj0zGXY8yivftPc2s5KSFlAIiJV/1nxF4Rpgc7h + +A5Wq7DXkZt16tDeiMD1y4l6JHjU6jqbwyxYf4ManRGMgnRdvm5LROxBktKhK9a3bbDp + QXM6GJDC9BEctdoswIXjN4aOUEf+vrGuClar+RczUvbtCvHzcne2s54AtQBks6OrbA67 + YCVbHiTFOsXOyW2Vb/DO+OT0C9fuFJVVvgYJCTkdDEi1fVP8IaFagIvG/5OF44eWpjfV + 5aUFN7LPHk+KCQ9Y4+EymyRXGTx2mc1hAfn7IC1IsY6bPG3WAo/Vm8KiE4+evpR/79Gz + qtrGrgIS16xCdRoct6os0FU4NtfXVDyB3Or5k4f2Rgb5eC5ynGE1fizgEdRj19kcGSAH + a+tAkY7VjLkLV6yHFeuB1HPZNwpg06Ou8T1HSN6+B573UNVnxfcK0gKyaOTRsbW5/tWL + p0V3crPSkxN2bfXzXjLfYeokU0Oy9zjgt27xSGsCYA9SW2+06SQb+3nuq3yDdsQdPnX+ + 6q1CyOlwAfknKQyQi0hEpCD9BgetCgsoRCOpkvvyx8cPrc0NryCXczfvYsbRxOhtAWuW + uUAyx3zMKB2oJP8OHmlNAOxBDhsxiux5zHbx8PbfuishOS0r9/aDxyQg37W1f4J9SBqQ + GJGq+KD4TgFbQB6NFI5f//zfl88fP7QAHSvLiu/lXz5zPCl2+5b1sFqdOQWSObra/4BH + Akgo0iEpnbHjrWznui5bG7Bt9/6U9Au5tx+SgISkTjsUBvzvz6+KiMRVq4C9CIfeIxbg + BSOJRrrtSPYd2XC8f/3KudSDIB59V7nPc5g62dRIf4TWINh77LIWgKhH8vTpC1WskNLR + MzSdaG3ntNhz/ZbwmMSjGTQgn1fXNrxt+QBpVopIhUUrjKZHZoUvQQsI0QKK0Ui3OWCt + +qm97X3Tm5oXZcUkHE8eitsR4ue9lORWzcYY6MBex4Bfv6MemYD8lVmxQo51qgNISJ/A + iJjEYyQgH5Q+f/m6vvk9XbP+jyxalSMSQ1KIroRj/kkL8GPx/4CNEI1UOX7u+ND6tqGu + uuJp0b3r2ZknD8fv2rpxtYfrHFur8bLV6nfxyACSWbGOMQcJuWCJl29QRGwSDcjCkrLK + mjeN71o/dICK5CKSpyPZgf3k9PCvowWEYgHFUGSCkaxU//cFlCPAsbnhdVX5k6K7+Vcy + Tx5JiArdtHb5QseZ1hNJbpWsVrvf66DLVbpi/RVWrEO1dUebgIScAzkdPxKQR9Ozcm8W + FD+tgDVr83smImlIUkrC/ycojwz/M1pAUhaAGADVSNAI0fgHicaWt41val4+K314J+/y + uZOHE6LCAtatWOxkbzN5nBHNrf7japWuWPuRFSsjIafM4AIyEQIy58a9osfPX9bUQUS2 + fej4+PmPLyQk//oK2R180AIStwCEwV9//UnQ+McnEo3vmupfV1eUPSq8fe3SmROH40k4 + ero5O0wj4hEK5cjW4z+sVhkF2e+3Ab8PGQZ1rKaTrGfOdaWEjElMScu8knf7waOyiqpX + bwgj29o7PpGQ/PK///0Jz1/4oAUkawESAf+DUAQyQjB+ADY21ddWVz57XHTvZu6F08cP + xe8KC1jv6TZvFuw8jiVbHf9qtSoLSNj00BllPI4XkPuPnDx7MfcmILLsRTVEZNNbCMkP + 7R0fP33+/AcJS3zQAhK2wB9/QCh++tjR3gZobG6sr615Wf4U4JiffT796IG9O0MJHSEc + LcePHa07XItUAvxDbpUEI3ngpoD+REKONDAeN9mGEtI3MHx3wqHjGVnZ+bcLi588e1H1 + qraehGRLaxsEZXtHx0fyfMIHLSA5C1DX7+joaP/woa2t9T0EY8Ob19Uvy8tKHgIcL507 + lZIYGxmyce0KJhxNRuuNgMKcf7daZQKSSEheQLos9fLZHLZrb1JK2rmLuTfuFBY/Lqt4 + Wf2qrr6hsentu/cQla0Ql/igBSRrgdbWlpb37942NzVAMNZUvXj+tKTo/q287KyM1MP7 + YiKC/NesWEzpaGII4fhvxSMNR1oVAAGppa3LEHKOy5KV6zdtjYzZfzg14/zl3Jt3CotK + y55DSNa8rn1T39DQ2NjU1AzPW3zQApKzAPH85qamxgYIxbraV9VVleVlTx49vH87P+fC + 2VMpSXFR2wL9vJctcnaAxaqJoT4Tjv+81cEEI/lXUqYzAJKsEJBjQEPOmDPfbcUav6Dw + qLiklJOns67k3rhd8PDR46fPyl+8rKquefX6dW1tXV3dG3zQAlK0APh+be3rVzXVEIsV + z8uelBQV3r2Vl3PxXPoxgGPk1oANXh6uTvbTLGg4Qmr13+w8ysORF5BEQ06cYjtr3qJl + 3j6bQyNjEg4ePXUm63Ju/q27BQ+LSx4/LXtWXvHiReXLly+ryFOND1pAQhagTl/18mVl + 5YuK8ufPyp6UPip6cO/2jWvZF86lpx5J3LsrPMh/recSl7l2Uy3MTUZTOtJcTld3WPFj + UOHPMkJCQJpOsJru4Oi6dOU6/6BtO2P3Hzp68nTmxezc67fu3Ct8WFT8qPTxkydPn5bh + gxaQqgWePn3yuLTkUVHRg/t3b9/Iu3o562z68eSkuOiIrQE+3ssWz58zw2ay2ViSyqF0 + /Dc7j0oB+SuzZB05ysh0vMXUmXMWLF7mvWFT8PZde/YfTElNP5sFIZl3/ebtO3fvFxQ+ + ePDwYRE+aAFJWuDhw4cPCgvu37t7+9aN/Nycy1mZGaeOHUmMi44MDfRbu3Kpq5PD9CmT + xo0x+OFwpLseEJCQZdXRNxxrNtnadpaT6xLP1b6bQ7bviklIOnL0RPqZzKxLV3Jyr+Xl + X79x8+atW7fxQQtI0AK3bt26eePG9fxruVezL1/IOptx6njKwf17oyPDgvzXe3kAHGdO + s5xgamygO/wH6UhQySxZISBH6EFWZ6LlNLs58xd5rFzjGxAcviN6774DR1JST6WfPpuZ + lXXx0uXLV7Kzs3PwQQtIzgLg+FeuXL508ULW+XNnMtJOHEs+lBgfsysiNNB/vfdydxdH + h+nWk81NDEeN1NYaTFI5/3mxyqxcSUDCtseQYcN1DYxMzSdbT3eYu2AxRKTPpqDQiJ3R + exISDx5OOXr8xKm09PSM02fOnDmLD1pAchYAxz99OiM9Pe1k6rGU5ENJ++NioiK3BW/2 + WwfR6Oo8e+ZUy4njxozW14Fw/H3AD4cjJSQUBgwcoqU9Un80INLCxnaWI0Skp/d6v4Cg + 0O2RUTF74vYlJh08dPhIcgo8R5nnGD6aaAH26+C/8S3wMx+KfQ/x/OTkI4cOHkjcn7A3 + ZvfOiG0hWzb6rFm1DKJxjt30KQBHIwPdEaRmdcBvP0pHdsn6a3/YiKRrViMTs0mWU2fM + cpy/cMmyVavX+20KDA4Nj9ixa3d0TOyevXHx8fEJ8OzDBy0gGQsQj0+Ij4+L27snJmZ3 + 1M6I7WEhQZv9fdZ6e3osdiHRaG0xwRTgOJKRjr9BGcB/2uhglqrcv8KSlaRZyZoVEGls + aj7Jaqqtwxxnl8VLl6/0XrvBzz9gS1BwSGjYtvDt2yO4JxIfTbQA93nw33kW+JkPJX/N + 9vDwsNCtIUGBmzf5+axb7bXCw33hfMfZNBrHjTWkcOSk40+EIyxZGRE5kCBSd9ToMRCR + ljbT7WY7znNZ5O6xfKWX95p1G3z8/Pw3bty0KQCezfigBaRkAeL0mzZt9Pfz8/VZv3a1 + 96oVy5a6uS5wmuMwY9oUiwnjYKmqp6M9bMjPrlVZRPaRIVJLW0d3lCFE5ESLKdNs7WbP + dV7gsshticey5StWrvLy8oZnNT5oAclZgHi+t9eqlZ4rlnksdV/s6jLPcY7DzOk2lpNp + NJKl6lACR1ir/ovzx9zStNt/pwHZ/3eyaNXW0Rs12tjEbMJkS+tp02c6zJ7j6DzfxcV1 + 4aLFbm7u7kvgWYoPWkBCFiA+v8Td3c1t8aKFri4L5jnNnTPLbsZ0GyuLieamY40MIBrp + LgeTyPmptSoXoRwiYdEKEak7ysBorCmEpIWVzbTpM+zsZ82eM2euo6OTk7Oz8zx80AIS + swC4vbOTk+PcOXNmz3Kwm2k7faq1pcXE8eNMjA0N9Eg0kqVqf7iboyfgyMRkn75MXodG + 5IiRegajjceajhs/cbKF5RRriMrp021nzJg5c6YdPmgByVkAHH/GDFvb6dOnTbWZYmUx + edIEM1OTMYYGo3R1ZNFIdjl6BI4sJGlEwtbHwEGwGzlcR1ffYLTRGIhJs/ETJk6abGFh + YWlpZWU1BR+0gOQsAI5vZWkJMTB50sTx5uMgFoGMo/RGjtCmuhHY2NPRCEEJi1aGkb8P + GjwUQhIoCTFpCEFpYmoKcWlmZm5uPh4ftIDkLACOb25mNm6cqanJ2DHGRqMNRunr6kAw + wkJ1IFmpqiAaCSXZiKSQJCEJmBypq6c/ygDC0tDIyMgYnjH4oAUkZgHi98ZGRoaGo0cb + QCjqQSwOBzIOgZQqKcdRUTSSiKS7kb/++huEJFByyFCtYRCUI3QgLHX19PT0uWcUPmgB + aViAc3lwf13dkSMhEodr01gEMrJo7FndSMOQ9y8MJJmQBDEJQQlRCWFJn+H4oAWkaAHG + /YcN09ICLA5mYpGsU3s4icOLQ94faUhCGR1gEjj5+8CBgwYNGjx48BD5MxQfTbSA/APh + n2QW+JkPJXvJEIhCiMNBA4GKvw8Y0B9iEZapqiUjLyJh3UqyO/36wdr1t/79IS5JYOKD + FpCyBSAKIBZ+I6FIY7Entzf4wdfdn2lQsmH5Kzy/4YMWkKgFiP+TOCSRCFjs0a3G7uKv + m39Ofh0eGAc+aAHpWoAJA3VDsZugxH+MFkALoAXQAmgBtABaAC2AFkALoAXQAmgBtABa + AC2AFkALoAXQAmgBtABaoFct8P/b5umcCmVuZHN0cmVhbQplbmRvYmoKMjQgMCBvYmoK + NDQ4NjUKZW5kb2JqCjIwIDAgb2JqCjw8IC9MZW5ndGggMjEgMCBSIC9UeXBlIC9YT2Jq + ZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggMjU2IC9IZWlnaHQgMjU2IC9Db2xvclNw + YWNlCi9EZXZpY2VHcmF5IC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVE + ZWNvZGUgPj4Kc3RyZWFtCngB7dCFQQIAAABBsTswEAxsBQWD/XdzkL8b4Vartj8MGDBg + IDnwiwEDBgwkB34wYMCAgeTANwYMGDCQHFhiwIABA8mBBQYMGDCQHPjCgAEDBpIDnxgw + YMCAgeDAHAMGDBhIDswwYMCAgeTABwYMGDCQHHjHgAEDBpIDbxgwYMBAcuAVAwYMGEgO + vGDAgAEDBoIDzxgwYMBAcuAJAwYMGEgOPGLAgAEDyYEHDBgwYCA5MMWAAQMGkgP3GDBg + wEBy4A4DBgwYSA7cYsCAAQMGggM3GDBgwEByYIIBAwYMJAfGGDBgIDpwjQED2YERBgxk + B64wYCA7cIkBA9mBCwyEB84xYCA7MMSAgezAGQYMZAdOMWAgO3CCAQPZgWMMGMgOHGHA + QHbgEAMGsgMHGDCQHdjHQHhgDwMGsgO7GDCQHdjBgIHswDYGDGQHtjBgIDuwiQED2YEN + DBjIDqxjwEB2YIABA9mBNQyEB/4BLgXylgplbmRzdHJlYW0KZW5kb2JqCjIxIDAgb2Jq + CjM2NgplbmRvYmoKMjUgMCBvYmoKPDwgL0xlbmd0aCAyNiAwIFIgL1R5cGUgL1hPYmpl + Y3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCAyNTYgL0hlaWdodCAyNTYgL0NvbG9yU3Bh + Y2UKL0RldmljZUdyYXkgL0JpdHNQZXJDb21wb25lbnQgOCAvRmlsdGVyIC9GbGF0ZURl + Y29kZSA+PgpzdHJlYW0KeAHt3Qs/k2EYB+A5j2HOZpjz+TxzNkQSSSJJJJEkkkgStq/e + B+n6CPtte9/nuQ//K51KPT483N//vbv7c3v7++bm1/X11dXPy8sfFxffz8/Pzr6dnn49 + OflyfPz56Ojw8NPBwcf9/Q97e7u773d23m1vv93aerO5ubHxen391dray9XVFysry8vP + l5aeLS4+XViYn38yNzc7OzOTTCanp6empiYnJyYmxsfHxsZGR0dGEonE8HA8Hh8aGhwc + HBjo7+/v6+vt7e3p6e7u6urq7Ozo6Ghvb2tra21taWlpbm5qampsbGiIxWL19XV1dbW1 + 0Wi0piYSiVRXV1VVVVZWVJSXl5eVlZaWlpSEw+Hi4qKiosLCUChUUJCfHwwG8/Jyc3Nz + crKzs7OyMjMzMzICgbTP7/v3+/f/9/zz/Pf+8/53/nH+c/51/nf/cf9z/3X/V/9Q/1H/ + Uv9T/1T/Vf9W/9f/0P/R/9L/0//U/9X/1v83/2D+w/yL+R/zT+a/zL+Z/zP/aP7T/Kv5 + X/PP5r/Nv5v/t/9g/8P+i/0f+0/2v+y/2f+z/2j/0/6r/V/7z/a/7b/b/5d/IP9B/oX8 + D/kn8l/k38j/kX8k/0n+lfwv+Wfy3+Tfyf+Tfyj/Uf6l/E/5p/Jf5d/K/5V/LP9Z/rX8 + b/nn8t/l38v/5x/wH/gX/A/+Cf+Ff8P/4R/xn/hX/C/+Gf+Nf8f/4x/yH/mX/E/+Kf+V + f8v/5R/zn/nX/G/+Of89IxBIp1L8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8 + d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/ + zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf + +e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47 + /53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/n + vwfSqdTjw3/sv/8DK6twSQplbmRzdHJlYW0KZW5kb2JqCjI2IDAgb2JqCjgxNwplbmRv + YmoKMTcgMCBvYmoKPDwgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMCA+PgplbmRvYmoKMTgg + MCBvYmoKPDwgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgplbmRvYmoKMjcgMCBvYmoK + PDwgL0xlbmd0aCAyOCAwIFIgL04gMSAvQWx0ZXJuYXRlIC9EZXZpY2VHcmF5IC9GaWx0 + ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4AYVST0gUURz+zTYShIhBhXiIdwoJlSms + rKDadnVZlW1bldKiGGffuqOzM9Ob2TXFkwRdojx1D6JjdOzQoZuXosCsS9cgqSAIPHXo + +83s6iiEb3k73/v9/X7fe0RtnabvOylBVHNDlSulp25OTYuDHylFHdROWKYV+OlicYyx + 67mSv7vX1mfS2LLex7V2+/Y9tZVlYCHqLba3EPohkWYAH5mfKGWAs8Adlq/YPgE8WA6s + GvAjogMPmrkw09GcdKWyLZFT5qIoKq9iO0mu+/m5xr6LtYmD/lyPZtaOvbPqqtFM1LT3 + RKG8D65EGc9fVPZsNRSnDeOcSEMaKfKu1d8rTMcRkSsQSgZSNWS5n2pOnXXgdRi7XbqT + 4/j2EKU+yWCoibXpspkdhX0AdirL7BDwBejxsmIP54F7Yf9bUcOTwCdhP2SHedatH/YX + rlPge4Q9NeDOFK7F8dqKH14tAUP3VCNojHNNxNPXOXOkiO8x1BmY90Y5pgsxd5aqEzeA + O2EfWapmCrFd+67qJe57AnfT4zvRmzkLXKAcSXKxFdkU0DwJWBR9i7BJDjw+zh5V4Heo + mMAcuYnczSj3HtURG2ejUoFWeo1Xxk/jufHF+GVsGM+Afqx213t8/+njFXXXtj48+Y16 + 3DmuvZ0bVWFWcWUL3f/HMoSP2Sc5psHToVlYa9h25A+azEywDCjEfwU+l/qSE1Xc1e7t + uEUSzFA+LGwluktUbinU6j2DSqwcK9gAdnCSxCxaHLhTa7o5eHfYInpt+U1XsuuG/vr2 + evva8h5tyqgpKBPNs0RmlLFbo+TdeNv9ZpERnzg6vue9ilrJ/klFED+FOVoq8hRV9FZQ + 1sRvZw5+G7Z+XD+l5/VB/TwJPa2f0a/ooxG+DHRJz8JzUR+jSfCwaSHiEqCKgzPUTlRj + jQPiKfHytFtkkf0PQBn9ZgplbmRzdHJlYW0KZW5kb2JqCjI4IDAgb2JqCjcwNAplbmRv + YmoKMTIgMCBvYmoKWyAvSUNDQmFzZWQgMjcgMCBSIF0KZW5kb2JqCjI5IDAgb2JqCjw8 + IC9MZW5ndGggMzAgMCBSIC9OIDMgL0FsdGVybmF0ZSAvRGV2aWNlUkdCIC9GaWx0ZXIg + L0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Aa2TzWsTQRjGn02QCtZQi0jx4oJSPERdkhbb + W9t8SOwSlySlfhw02d1soslm3d1ErR561H+gFEQQPOjBmxc9tSeR4ieC9OBdUU9a6qGU + 9Z0Zd4Ng8eK7zMxvH555Z+adXSC+UXWcVgxA2/bd0qkZ+ey58/LAOiQcQgJJyFXdc6Y1 + TSXLDrH5gdwU74+xXF9iz7c+fdtz58Li7bXDSw9bO0wK5YRLCwJSkoT9luApxjXBFcbX + fMcnT4Ox3qgaxLeIk26llCF+RJywBD9lXBP8gnFPt9jcdWLFNpo2ENtNPGGYnk48RWwZ + nt4mpjwS2u0O5Y+znEd1x6W58bfER1hdaKS4mgAmv5J+r69dPAE8WQMOaH1tdBkYvgQ8 + m+xrP97wWkkjNa+eTvF00uBBYNdqEHwPgAHyb78Ogq3lINheojU2gJWi3nV73EsblF4B + /3oXZxbZgXd0Bgp+R39nURfuUoAHq0BlEVBpvEvj6E9g301AA+lUpnQ6bKKGJAN7szlV + lVNjynhem+fKf+zarS7dFY9h6gftWvEMjSPUPju+RpsS7PXKuZDrzXwhZKOanQ15oZEp + hlx386WQL1dPswPynKY9Vw7ZafFv//daM5Hf9HKRZ6FRYWfmHrdbmgv5Smc28htmNtqb + 3Sqy/4n7m34h2j+yyEGlR0YKY1AwjjyVfV58kzQDQ4+B+0PKyXR55eNLJvwRvnmd33Wm + 49xwm1bDl6fpjzSTcsHWjyfllKJM4BeDarM/CmVuZHN0cmVhbQplbmRvYmoKMzAgMCBv + YmoKNTY1CmVuZG9iagoyMiAwIG9iagpbIC9JQ0NCYXNlZCAyOSAwIFIgXQplbmRvYmoK + MzEgMCBvYmoKPDwgL0xlbmd0aCAzMiAwIFIgL04gMyAvQWx0ZXJuYXRlIC9EZXZpY2VS + R0IgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBhZRNSBRhGMf/s40EsQbR + lwjF0MEkVCYLUgLT9StTtmXVTAlinX13nRxnp5ndLUUihOiYdYwuVkSHiE7hoUOnOkQE + mXWJoKNFEAVeIrb/O5O7Y1S+MDO/eZ7/+3y9wwBVj1KOY0U0YMrOu8nemHZ6dEzb/BpV + qEYUXCnDczoSiQGfqZXP9Wv1LRRpWWqUsdb7NnyrdpkQUDQqd2QDPix5PODjki/knTw1 + ZyQbE6k02SE3uEPJTvIt8tZsiMdDnBaeAVS1U5MzHJdxIjvILUUjK2M+IOt22rTJ76U9 + 7RlT1LDfyDc5C9q48v1A2x5g04uKbcwDHtwDdtdVbPU1wM4RYPFQxfY96c9H2fXKyxxq + 9sMp0Rhr+lAqfa8DNt8Afl4vlX7cLpV+3mEO1vHUMgpu0deyMOUlENQb7Gb85Br9i4Oe + fFULsMA5jmwB+q8ANz8C+x8C2x8DiWpgqBWRy2w3uPLiIucCdOacadfMTuS1Zl0/onXw + aIXWZxtNDVrKsjTf5Wmu8IRbFOkmTFkFztlf23iPCnt4kE/2F7kkvO7frMylU12cJZrY + 1qe06OomN5DvZ8yePnI9r/cZt2c4YOWAme8bCjhyyrbiPBepidTY4/GTZMZXVCcfk/OQ + POcVB2VM334udSJBrqU9OZnrl5pd3Ns+MzHEM5KsWDMTnfHf/MYtJGXefdTcdSz/m2dt + kWcYhQUBEzbvNjQk0YsYGuHARQ4ZekwqTFqlX9BqwsPkX5UWEuVdFhW9WOGeFX/PeRS4 + W8Y/hVgccw3lCJr+Tv+iL+sL+l3983xtob7imXPPmsara18ZV2aW1ci4QY0yvqwpiG+w + 2g56LWRpneIV9OSV9Y3h6jL2fG3Zo8kc4mp8NdSlCGVqxDjjya5l90WyxTfh51vL9q/p + Uft89klNJdeyunhmKfp8NlwNa/+zq2DSsqvw5I2QLjxroe5VD6p9aovaCk09prarbWoX + 346qA+Udw5yViQus22X1KfZgY5reyklXZovg38Ivhv+lXmEL1zQ0+Q9NuLmMaQnfEdw2 + cIeU/8NfswMN3gplbmRzdHJlYW0KZW5kb2JqCjMyIDAgb2JqCjc5MgplbmRvYmoKNyAw + IG9iagpbIC9JQ0NCYXNlZCAzMSAwIFIgXQplbmRvYmoKMTkgMCBvYmoKPDwgL0xlbmd0 + aCAzMyAwIFIgL0Z1bmN0aW9uVHlwZSAwIC9CaXRzUGVyU2FtcGxlIDggL1NpemUgWyAx + MzY1IF0gL0RvbWFpbgpbIDAgMSBdIC9SYW5nZSBbIDAgMSAwIDEgMCAxIF0gL0ZpbHRl + ciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBzcLXcQJAEAXByT8vjLDCO4HwIoobfRPA + K7arW4f22V1akT1anX3ae/t85hfWOcAih1jnCIscY50TLHKKdX5jkTMsco51LrDIJda5 + wiLXWOcGQ7cYusPEPYYeMPEHQ4+YeMLQXww9Y+IFQ6+YeMPQO4Y+MPGJoX+Y+MLAf+tF + vN4KZW5kc3RyZWFtCmVuZG9iagozMyAwIG9iagoxMzAKZW5kb2JqCjMgMCBvYmoKPDwg + L1R5cGUgL1BhZ2VzIC9NZWRpYUJveCBbMCAwIDUxMiA1MTJdIC9Db3VudCAxIC9LaWRz + IFsgMiAwIFIgXSA+PgplbmRvYmoKMzQgMCBvYmoKPDwgL1R5cGUgL0NhdGFsb2cgL1Bh + Z2VzIDMgMCBSIC9WZXJzaW9uIC8xLjQgPj4KZW5kb2JqCjM1IDAgb2JqCjw8IC9MZW5n + dGggMzYgMCBSIC9MZW5ndGgxIDUwMzIgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3Ry + ZWFtCngB7TiNfxNVtufcOzOZNkDT0pbQAJl0aJWm2QIKlEJpaJKWNkILVDepsCb9kADF + Flo+n9Iqq2L4sK7a1d+u6K6rFaw6bZENRZevxdUVfn6ivLfig4X14z0R9Af7VoHOO5Nq + BZd9f8Gbk3vn3nPOveeej3vu3AACwBBoAw7u2mXhJpBhGmEOU7HWrmpR3tm342NqnwaQ + V9zetGhZ6hcziZ4QABDKFzWsvT0yatrbAEN/RzyvR+rDdWeXqscBhjVQf3KEEMPGCEQf + ZtDHRpa1rJH3Yxf1X6e+3NBYG4Y6SKc+8YC0LLymiT0q3k39Y9RX7ggvqx+59bl11P8H + 9dWmxuYWvQpuB0gaRf3sphX1TWeOuN6jvpfWEyYcEhjPEJCoADgMjNgLILbBGCojeA9k + 6hf0v1L5zCj9s4j2NCT31wDwEmpvi4+/qhJP0OyeAZT+L56rBlyzMzAQDE2Ncu2nHMxQ + AltABAsI8Az1zsJxuA8klGEyJEM+vIAfgQcq4R4shJ8TTwaUwQuwGz7ARfolEPSgfgCW + QidOIv0zoAAWwF44T3x/gp9CGNZAK96j30XWSYQxkAk3kDVXwDo4SRwJkAY2yMQsNo0f + gxE0sgHaYAPsFir0Z/XjNCaBoAKeheegH6sxou/STxEmH6aCD+bDHcT7AIqYJo7R/6Kf + 18/DRFrnQlhE8yyHx2AXmnA4OvEPrIh3wDjSsxGaIAod8Ev4M2bjm7xS3w4uggkwh+a7 + FWpozi3QB2/ANyixGraGvcB28RX8ogDCUfFtyae/ph+hyBXBSmMKaMRtFE+Lad4H4R34 + EP4KX8HXmIaZOAEn4wwsw6WsQTJLP9X30BgzjAaFbJADebTSAigmmEe2uBMehS54BV4l + OABfokJwHcE4vBWfwR48xZKYwg6zd9g3PJ/7+aOCVVgo3Ck+Jp6UcnSPvosslQJ2UGEK + zVhOFrsVamlVzWT9X0InwYuwE2LwHvwFTsHfaH0j0Iqj0IOlWI7nGGePs8/5ZL5OkPpH + 6xP19fGoTqLVziAoJ7gJZsNcuJk8GoB68vY6+De4C9bDPRQRD0I7gSFnQEof7IPXKeLe + JXv8B/n5FPwXnKOI+BYukz+SSLIDc/AGzMfp6CXwYQVBBFfj/bgZ2/Fh7CKNN7KH2Vc8 + kafycl7P7+ab+JN8Lz/IjwjZwiyhR+gVC8RScROBJh4TP5G45JEekj4zbTe9IlvlIrlM + Xiz/+tLOfug/2n9On6HP0bfoW/V2/b/j3pPIYjL5YwhFeCpFrRNupIgqp+iZD1UQ/M5+ + yyhSmwlWwipYC/fC/bCZYAs8RBH0G3geeuBl2A9/hNdIUwOO0c75mOA07aF/kK4ymnEo + aWwhGIVjcCzFnBNzMQ8nYSG6yf5zMIj1uAI34TZ8GV/Ft/AjBuRtG1NZAZvO6sgKz7Od + bC/55zryUD6/iS8iazwpJAluEcQO8TOpU3oV3sPptN+ufh6Br/RUYQucod2zGt7neXod + r8Xl0CLehk74lnbGbtJF5ZSLcRycE75AJ1vN0nAjS2OT2GnxFXykP4qJ5MuPIIdW5hM6 + 4N+hkM+gvbqW+4WF7PdCNu6kCK3BL9lC9ivmE9ewaijGF9GF5XwcHDFlSZ+wevycZ5pC + cBFXkRaV8Du2AzYyF/4Pm9a/T3KJE2G78BHzwHY4xjJMEkvjF9m77Am4gz1Ee+IRirpv + 4Be03qG4gnKOCwvwXYqmg/g+5d8XxDb9vHgnK2TT8R32N4ogEJ4QwgNc/yJx/j86bgF4 + iS0zzaKsO0I8IB4w2uwQa+Qu7mImOEJ1IzOxD4Ri+AXXTDoWiptQkd6U3sQlUCciZEOF + 8DPswK/xIPQKU+A8fg0opEAFZkvT8AlxGlSII+ExlgkP0Sn3Br7I18DnOJrGLGGHTLMw + X7gICzEffisUC5/wDvYImlkOnsT78O8EDZRdnbgW1ksvwWq+EfJ42PQ+OtjP2R5oYaWU + ZeewbKzkb8Eh2CMVsJvpnQ37+TYWoCyXJx+GM9ggbBA24AdQxuZBG2sVEJLwuHAJGqQW + uFP8EO7C1XEpfYYc2vO/IVmT8E902m2gHJNNZ8sewn4IRZBLuXsN5e0dlAOClCmQzsYz + KFFmMHLeKbYTGfweZsHD3Mct0sNwN/sWMyjD1BE2C5bBVlQpSxRS1jyIH9M5GqBT73pe + A3NN24HhcNqhIG6lk0eFE/AqlsNP4EPMoMyoAjKB34htcAmrWEd/jVACiXw53yAuxRLa + Ty8C6qnwZz0i0PeGe1L+lBsmjM/7iSvXmTPu+uuys8aqmQ7FPmb0KFvGSOuI9LTU4SnJ + lqRhQ4eYExNkkyQKnCHk+tSSkKJlhzQhW501y2X01TAhwlcgQppCqJKreTTFGBcm0lWc + buK8/Uec7gFO9yAnWpTpMN2Vq/hURTviVZUYVs8NUHuLVw0q2pl4e3a8LWTHO0Op43DQ + CMVnjXgVDUOKTyvVhCwtsSrg0MqCqyJRX8jrysVuc6JH9dQnunKhO9FMTTO1tBK1qRtL + ZmC8wUp8Bd0M5KGkrlauen1amUpDaUae5QvXaZVzAz6vzeEIunI19NSqNRqoxVqSM84C + nrgYTfJoprgYZbFGmsEmpTt3X3RzzAI1IeeQOrUuvCCg8TDN4dOSnVqp6tVK1522unJj + +GxVQEvwxBCqAruhXG/rLmvzeoOGtBRP4P4r2W086rMuVozR0ej9ivbU3MAVk9kcxpTB + IE3qyvXPCzho1apvs2KoMS8Q14AmRWseLdzAGWoOKFyv+gxMaImiJajFaiS6JER+y4hq + MG+toyej3L1bPwHlPiVaFVAdWpFNDYa9o7pTITpvbW+ZWym7muLK7bYkD1i6e1jSd40h + Q69s1JMXBmjxVpzdaNGqvzc1GitSyzQ3hVutQisJqBrLyjeq+nyI1uaTR+gJIll0Mdkv + FLUUkHaamGVRlegFoJhQz3xxNSb8HUbKslwAg2hEzmD0aRj+vq05nVpOjhEpJg+5llY2 + I96f5MpdpfnVJoui+clkUBmgQcGCPDK5w2F4eVPMDTXU0drmBgb6CtTYesCd5wxqLGRQ + 9n1PSbvZoLR9TxkcHlIpsnfGbxJpmpw9+EuypA/3RQo0TP8/yPUD9BLax9FoiaqUREPR + cExvq1EVixrt9vujTT7afwMrj+l9m2xayeagZglFkMynDfcEuI0ZsUctZuNBF31GUGKg + nERAX0smutQkO5KzqKLTFS4pfN8ltwgXQRH2ERcw6NKPMyvdY0wwHCrciRIKAJQeuRDD + ZW6b/PwwM98q3CfhSrO0MjmttLTc/HxyWeqCVVbnHMv52Zfn+Oq9n8y2nCY4fwaKLn9Z + hMkpU4HK1AnjcQTjKqNPn4mUwCQ1M3vSjZPx0wlTb1non3ggtTwSKS+LLBK3Zfe/cfkW + thyvL7z4noGKlJVHaG2GGgzgsulXoduSpl8Am2ysGA6VNvQMvmn1pOk24k2I8xsEGiex + y81wWt7f/1j/4/ITgxSDajxMIhTbT9/tQPfJOqoHZC0geQvITIzuU6kwnRgLhR6yokFF + +ko33jQ5fXeCd25xZdktTk+4YXHz7KriRrrC0lqNR7/JuHNe4zHoZNwYf7Rn+Uh7jF/s + pdeUGJrdGWNW22+hspCK0jq+1d3KX37Qa59iWWfpQw6MHd7VNNZ+6ORwewz7e0+OtOfN + TMAHoIgKg5PsIF3F7Oyge/5rqr2pta2VtZofND9pfsm81/yWme5hlgT2dsKJhHMJPInb + OftPfpbrnOcpaM+8LfOsoitCkpKnFCkVSqPSqrykmJIy7ZkVmbxtOVpmOlgTKFTGU3FT + aafyFBVpEGtgeJxWSS02iCd9qW9hTe5EtmNdpr1tHW7siumHe63p8bf7emv6U5+a7E99 + +owpfUnnnqyBquPp8faOpxPGpz/dmZMwUMVwQs/jZnsfToDH8Ua3xXwFLSVZdsdY/ss3 + 2xJsCe1/wOcoktvx1/Fachea2i+a2ttM7UtN7YtM7SFT+89M7QHTWDlTVuQx8ig5gy4c + 6XKqnCJb5GHyEDlRlmVJFmQmg5wa00+4JxiuT6V/HRDSJYvxkowdAkK8bSElKZTIsxQb + DGVG9xD0a/tqwV+jaH+fr8YwcW61JqrFqKX4wV9VbEVtOPcz//xiLd/pj8kwT5vi9GsJ + lbcGuhG3BgmrsY3x4yWGIw3UvTbjZNlNXy7z7t1iM976vVuCQUhfVWQtSpmRPLXEe40q + 9AMy5HX+82P9AYX+yrV7wY5nyWh2VOJ1aq/J/qTJ4PHPJ2J7nNgeJ7YjEdsHiNbRWod/ + fkDbMTqoTTQa+uhgb3XfhqM+OqRCqq+eSkjbtCpi1dpqFKV7Q59BoLMiO1RTGzHe4Xqt + T633ahtUr9JdHR/3I/JRg1ytervhqK8q0H3UXe/tqXZX+9SwN9jb2dLYdZWsBwZlNbZc + Q1aLMVmjIaszPu5HsroMcqchq8uQ1WXI6nR3xmWhb/H8YrJVoFuG4qBnwcC7l5kTyT0h + myNYHPfTNId1va2PXcACMNMpMoQ+PoZSMVzomumaSSQP88RJw4zvku9I1vXTHLY+LGAX + 4iQLoZMpaPAHLxmtZmdzc/PKFqpaVkLLSoJm4miOU5zQMsBMwfi/LcjbTgplbmRzdHJl + YW0KZW5kb2JqCjM2IDAgb2JqCjM0NTYKZW5kb2JqCjM3IDAgb2JqCjw8IC9UeXBlIC9G + b250RGVzY3JpcHRvciAvQXNjZW50IDc1MSAvQ2FwSGVpZ2h0IDY5NSAvRGVzY2VudCAt + MzE5IC9GbGFncyAzMgovRm9udEJCb3ggWy0xNzYgLTIyNyAxMDc2IDkxM10gL0ZvbnRO + YW1lIC9EUUJQSVYrQ2FsaXNNVEJvbCAvSXRhbGljQW5nbGUgMAovU3RlbVYgMCAvTGVh + ZGluZyAxMTEgL01heFdpZHRoIDEwOTQgL1hIZWlnaHQgNDcxIC9Gb250RmlsZTIgMzUg + MCBSID4+CmVuZG9iagozOCAwIG9iagpbIDM0NCBdCmVuZG9iagoxMSAwIG9iago8PCAv + VHlwZSAvRm9udCAvU3VidHlwZSAvVHJ1ZVR5cGUgL0Jhc2VGb250IC9EUUJQSVYrQ2Fs + aXNNVEJvbCAvRm9udERlc2NyaXB0b3IKMzcgMCBSIC9XaWR0aHMgMzggMCBSIC9GaXJz + dENoYXIgMzMgL0xhc3RDaGFyIDMzIC9FbmNvZGluZyAvTWFjUm9tYW5FbmNvZGluZwo+ + PgplbmRvYmoKMSAwIG9iago8PCAvVGl0bGUgKFVudGl0bGVkKSAvQXV0aG9yIChQcmVz + dG9uIEphY2tzb24pIC9DcmVhdG9yIChPbW5pR3JhZmZsZSBQcm9mZXNzaW9uYWwpCi9Q + cm9kdWNlciAoTWFjIE9TIFggMTAuNS41IFF1YXJ0eiBQREZDb250ZXh0KSAvQ3JlYXRp + b25EYXRlIChEOjIwMDgxMTE3MTg0NzE0WjAwJzAwJykKL01vZERhdGUgKEQ6MjAwODEx + MTcxODQ3MTRaMDAnMDAnKSA+PgplbmRvYmoKeHJlZgowIDM5CjAwMDAwMDAwMDAgNjU1 + MzUgZiAKMDAwMDA2OTExNCAwMDAwMCBuIAowMDAwMDAxMDk4IDAwMDAwIG4gCjAwMDAw + NjQ5NjMgMDAwMDAgbiAKMDAwMDAwMDAyMiAwMDAwMCBuIAowMDAwMDAxMDc5IDAwMDAw + IG4gCjAwMDAwMDEyMDIgMDAwMDAgbiAKMDAwMDA2NDYxNCAwMDAwMCBuIAowMDAwMDAy + Njk4IDAwMDAwIG4gCjAwMDAwMTM5NDkgMDAwMDAgbiAKMDAwMDAwMTQ1NSAwMDAwMCBu + IAowMDAwMDY4OTM5IDAwMDAwIG4gCjAwMDAwNjI5MzcgMDAwMDAgbiAKMDAwMDAwMTYx + MyAwMDAwMCBuIAowMDAwMDAyNjc4IDAwMDAwIG4gCjAwMDAwMTM5NzAgMDAwMDAgbiAK + MDAwMDAxNTM2NiAwMDAwMCBuIAowMDAwMDYyMDE5IDAwMDAwIG4gCjAwMDAwNjIwNjQg + MDAwMDAgbiAKMDAwMDA2NDY1MCAwMDAwMCBuIAowMDAwMDYwNDQ4IDAwMDAwIG4gCjAw + MDAwNjA5ODggMDAwMDAgbiAKMDAwMDA2MzY2MiAwMDAwMCBuIAowMDAwMDE1Mzg3IDAw + MDAwIG4gCjAwMDAwNjA0MjYgMDAwMDAgbiAKMDAwMDA2MTAwOCAwMDAwMCBuIAowMDAw + MDYxOTk5IDAwMDAwIG4gCjAwMDAwNjIxMDkgMDAwMDAgbiAKMDAwMDA2MjkxNyAwMDAw + MCBuIAowMDAwMDYyOTc0IDAwMDAwIG4gCjAwMDAwNjM2NDIgMDAwMDAgbiAKMDAwMDA2 + MzY5OSAwMDAwMCBuIAowMDAwMDY0NTk0IDAwMDAwIG4gCjAwMDAwNjQ5NDMgMDAwMDAg + biAKMDAwMDA2NTA0NiAwMDAwMCBuIAowMDAwMDY1MTEwIDAwMDAwIG4gCjAwMDAwNjg2 + NTYgMDAwMDAgbiAKMDAwMDA2ODY3NyAwMDAwMCBuIAowMDAwMDY4OTE1IDAwMDAwIG4g + CnRyYWlsZXIKPDwgL1NpemUgMzkgL1Jvb3QgMzQgMCBSIC9JbmZvIDEgMCBSIC9JRCBb + IDw0OWU2MjQzZGUwYzBiMTQ0NmRmMDQzNjRjNzc1ZGNlZj4KPDQ5ZTYyNDNkZTBjMGIx + NDQ2ZGYwNDM2NGM3NzVkY2VmPiBdID4+CnN0YXJ0eHJlZgo2OTMzNgolJUVPRgoxIDAg + b2JqCjw8L0F1dGhvciAoUHJlc3RvbiBKYWNrc29uKS9DcmVhdGlvbkRhdGUgKEQ6MjAw + ODExMTQyMzU4MDBaKS9DcmVhdG9yIChPbW5pR3JhZmZsZSBQcm9mZXNzaW9uYWwgNS4x + IHJjIDEpL01vZERhdGUgKEQ6MjAwODExMTcxODQxMDBaKS9Qcm9kdWNlciAoTWFjIE9T + IFggMTAuNS41IFF1YXJ0eiBQREZDb250ZXh0KS9UaXRsZSAoUmVwb3J0ZXJJY29uLmdy + YWZmbGUpPj4KZW5kb2JqCnhyZWYKMSAxCjAwMDAwNzAyNzQgMDAwMDAgbiAKdHJhaWxl + cgo8PC9JRCBbPDQ5ZTYyNDNkZTBjMGIxNDQ2ZGYwNDM2NGM3NzVkY2VmPiA8NDllNjI0 + M2RlMGMwYjE0NDZkZjA0MzY0Yzc3NWRjZWY+XSAvSW5mbyAxIDAgUiAvUHJldiA2OTMz + NiAvUm9vdCAzNCAwIFIgL1NpemUgMzk+PgpzdGFydHhyZWYKNzA0OTgKJSVFT0YK + + QuickLookThumbnail + + TU0AKgAALDSAACBQOCQWDQeEQmFQuGQ2HQ+IRGJROKQoViABCsQhoAiEKA8AhR2PF/ux + vuZ/t9quB/NWKy+YTGZTOaTWbTecTmdTucAYCAADHwwUEzlADGcLA8ABaISd/N9PLV9p + 5FKZ9op9PwAPqeV2vV+wWGxWOyWWCiAMAEQMRMgtihsJAANAABAKBAoEgB/gYDAAAgO7 + P9+v2/Pp9gB/PV7P4BP9/gJpt1+tMnHh8E5wOh/uCzZ3PZ/QaHRaOCT6gN5XA1vhsIv8 + NP8HA6BBII366wYAv+BbqB7x/P6BO94QJ4vMAMxsP1mDs0vcd1mt6TpdPqdXrdNIHEEp + I3lIBm0AbGBBQKwfgQjHboA+uB8DHQJ+ul2PwCPZ6gQ9Jl8npDqR9kO68AwFAcCQKh4G + LyBx2FsBp1r4AIDACDoQIEwC9Pe9iDt496/AC3qDH8wh8G2cR8sUf56BAKp6hAeh7gAe + kDRlGcaRqsxGDaBBJjmKwCDYAAIgmvQIAgv0jSO9MOt3DEMve3T1IOfZ2HcfgCnmeICD + uS58juqqrxtMEwzFMaIAUA4AAYdpcAadgEQjM4OhCgTbt2hbeQ/KEPTrPaCRCAB8m6cR + 9OKfx4BCKp7BCex8gAe0yUfSFIxkQw0gQRo8i0Ag5H+B64gCCIKIE3ML1FI9R1IgzeSa + 872Mc4E9ABKUqAKeksDkSZ8jkR5VH2R9JV/YFgs7NwAAVNQGnaBUIgQAAOBFCqfz4gkn + r1I1YWpKD0NwgZ+K0fRvnCfZ2ngfx2BEK57BEfCuHxYV3XfeCZj+MYDkOQAwAKPAAAhI + R/gi8sPT0f73IG9dryXUsLvVVdq1FgVq1gfR0nafYDHseACjaR58jaShXH2Sl45FkeSI + M0wEHcXIHHaBYDn+BR/g6Eq/AGAeCt288j4bhuGSVVGeYPVFW27WJvm+fZ1Hcfx0hHdA + RugrmS6lqdJDwLwDkAQ4ygKP4AAfIQAAkDE8YhDeISNalYT5D20oLbGdW2fJ0nSfQDnu + eIDDQRZ8DQTJZH4TOqcFwcZgLmwDzVlYGgSf4F2aEyBAIAsPz3DiC1hnODyTtW1WnVKB + rsgWiH2cBvH4cp1H8cgTCyewTH2wjD8J2faNIOQsgOPRGjUApCn+BshJCudSbTnudSfD + Gyc5gWCYd5GHc09+JHVup8bwMZDnwMZPFsfhPdr8HwrFCwC8SdoH8YBtmhRyK+z5O+2M + RuFp/hymd214/6/e+CtH4cI3X/DoH8OEE4Wh7AnH6cAwj4oGQNJoGoKYBg5iVDgAYRgA + HgECAmBxPCqlTtuaE8ZJbCyCNsYGz6EMHXnIfHwOcdA+QED6HkAcLwhB8BeFGLkfgo4H + Q9h8Q8ASHgCDsFwA8dgEQFD9KUBwFRenJPzIUbpV5DU7m9VVFZhL9FssOfsk8fZXB/Dj + G8P0bg5B/DcBUFwewKh/RSh/G+OBAgyBNAMGwTYdgDCTTQv0CgHW0J2Q1Fx+61FRJOZv + Id4jZyGJ3iqAAfA5RywwH4PMA4WA/j4CwKkXw/BUxxk8+JPQAx1C2AeOoCgDR+lxA2Cs + gQBX3QgQ4/GFDOWEIdka/dz0UZEPPj+3A3kXwAD9HIN0fw1xwD9GsC4L49wXNmk/M9qg + XAkAFDIKMPYBxNgAAXHwD8KJYwlZxFl+kWXLOeliteQktm1zgaE/pao9xyDiHwAkfo9A + EBTD2PcKYrhhD9FdNCgDJABDoFqA8dIFwHD9VCBsFjkUzv5Z252dMsn7SyQyb8hU6JCu + VkQnVtshx/j6UYP8csxBpGSGkDEMQ9wY0BpcsIKwQwCheFSIAA4oVilxH+BRZ6GZzLWi + 6zeNznXMNueS9GEjZFStBlxR9hI9hxjhHwAof09gnB3HuE4WYxh+izpfV9MYAhyiyAgO + YDQER+AXAABmhpQC8wpL0qyir+Jbl+mctKQNEYsPHYLIyQCsV2j+HNMQZpyRmg2DMPcG + 1YLGIzCeD0AgVxYCGAQKinJAgKgkkC/Wdzb6JV6kXFGRtTmfSGShaNgsHyBD2HEN+qY/ + x6gICQHMe4SBcjKH6LmxtuzrACHCLACA4wPATH4a8DILS/AGAVXmEEunPrai3R6Dz9rm + xUupZtgo+kXj/HONwf4xxqD9GODsNRzreXnNEEgG4BAoC3EYAgV4AAEm1H+BYE8UGFTe + ivUapLP6O17VHOeLE7rQRanVLijcWTgD1HENwe7jh7gJCGG8e4QxfDOH6L69GGyxgBG8 + K0CA4AQgWH4B4AAGLkAAAOAyv8Vb9zfv3f65kuSE1+kdAtdaFTAgML65y67lG1G8Hyo4 + AA6RuAAGCM8fowQgBuHuEDDmUSeBBBkAMJIvhIgJFsAABCRQAAXBTfxDrAqJ3Wc+5tVr + ya+tuH4YQAI+CtSOdlLQg60WvLMLpc+vipa/D0HCNse4DABYRB8GsewPhhjSH8MPKWjS + ZgBGyKoCA3ATAZH4s8DALiBYrrxOSutd8g56nYqhDg+XZZwdEzlO7oQHA5labUAA7KvE + FdDBih4BXQ6huciYgQ6sji7GWPwXYRg5D4CNo7ZBEwdgtAGEEYglgEi+H+AgpQAQL3Ic + 3qNs0W7SIfim0FnJ0B/6mMLqpkzwwJhKg0E3TdaiCpqIEOEQxAh/IvIKBSt8QWEPFlqh + keg3xsD2AYAOqYOQzj2ByMgaw/hkbJ4dRkaopwIjYBUBwfbkAL6avkUq6uNVU11Paed2 + BetyABH1nRPYAnHJACEQICIRyBANBhIIiA7xgkCG8HZOZuwJF5YDj+D8tx8HGABr4AAt + hjj8FsEsOw+Al8P6gQMGoKABg7GUJsBIxMVcc0zqKkCTIunn3EPkrQAeR3VADQ8B4OuX + BEIEA4HCFaHk1HWLEgQ4t5lA1Znh+GoMbEH3+NXgQBB9AKBoGQewNBmjZH8M3qOyAAjQ + FGBEaYLwQD7lYBbFN8682d5CQIedI8234uT28G5AgIBBIEA8HaFa3lhHEI4gQ6xVF3J+ + P8BLk2Db9xpyAgQ9zh6xG8AAWAwh+CwCiHsfAUfH5RBeCQAQNBnifAUMrtIDkPAYpaw2 + pzbU9OYN4O7IgArlnh1cvsH3pkK54NGNpH4AB5+Ogw+401EHPRTo9UDeiMBweCAWAGHy + ASBgDCHuBgpOH8Gk+at2ACGWFAAiGeBoBGH2BeAAAq2wASeC48qM5oN4Hg3sBKe+vkQo + QMGkCSIEH2Hi5czw30p+5+f2IUPOHwHeIEJMAAFWF+H4FWCsD8HwCtAUq+BSIwBcGqFE + AUGg7SAa+yBoIeeQYWYeWqVeHoUYAAA2EC9Q9aQEH1BmAAGo3YT2AkzwSazWwKWqzQqK + WoHqG8GmHsAUAGH0AQBYC8HsBYGuHCH+GvB+meACGME2AgGUByBQH5CWAo40AUVCwI46 + r+HsaiAgC+IEAwC0QGHmGeIEG2DcQqN2AeTPBc28/0VOrqbU5QHsHaIEHaG+AAFMF2H2 + FMC2EEHyC3DyjgBKA4ACBUGyFMAWGoQgAYQ8Ay9OT2o0z47BA2wO7IIEAMB+IEA+X0QE + HUFgIEHIEbGOdCAY7m88rw34o42+YSHoG6GeHsASAGH2AOBSjUBSG2HIH+G3FigcGCEu + AgGMB8BYH41dEILu3cwQxpE+unGIowVixMAABMEqQGHGEhBovivkWiAQcmues6VU7+qK + cuIGHqHWIEHdFOFAFsH2FADCEOHyDDHYfABCAyACBOG6FSAXDwAMAWPWAw/OzSv6nGue + SatGjaL86IAABcFkQGG2DiIEHoGcL0AW90AMZsz4bKl2rk36eYYSliPeHoHAGcHsAQAE + H4AMBMgMBMG+HOJTJCcGF2EkAgGCCIBiH4/SAmxSAWAy4+lg/1DKSYc606HiHqIEBQss + KAbAOoGoCnBPFI/mcjKM4826s8Z05QIQHqHQOCHCAAE2FkH2E2DMEWHyDNK8ZKA6AsAC + BGHAFYAYG2AEAKASNyAy9a93ESxmrm/yTy9A3sA+ES5jAoNIH5LoAAGmCYbcAgWYp8Ig + hOl8kQMIljBYQ5JtKgGaHsQiPoBICwHsBIHGHUH+HHMqXgFoEaAgF0CWBsH4CKH+AkuQ + ACAYeHA0kXH4xcnIIGHsXbAqDWPG6eNIHqGwIEG0DUL1KMACAdDCx+xkeOVZLiME5oly + N8HoHKMeHiHGACEqFcH0EqDYEeH0/fOiUkAyAmACA+HIFeAYG9M+ASLsA1GSz2IAAACA + AA/4NAoJBIPCYS/4IAYHBodA4HCoLDoE+n3BAWTIIGzNDJFI5JJZNJ5E7V3BHGiIIBAF + BAYB4S/oTEYLAoxFpHGIhF4nOYtO5/PIuAHq4GU9wIAH6AhCVnsIXO7X+55RWa1W65Xa + 9X7BYbFY7JJFciQetCiOn6SwAEhbBAaHaNJp3DZLO7vN7xPH2/IIAxjBBCgLLh5E5k9B + HUp4IB6aAASBZrYL1KL3dYVPoi83C/wC8XIAUgqn0kDik30ccRrddr9hsdlYgsEACG3O + sgY4gEBARMQ0QYfMYdQbzIoXFZzQeVDOLOYpBH7gAA+wrBBOmdnYnAg4I8GHBMnBchAu + bJH/NqJy4PEPPBPVfPRzou/QA9nAyHuAX8/n+EIqqmdR4H+dTtwPBEEwVBYAFQQgHlcK + 4fn6KIAAiuIAAaD75uczjMsyk71vg+R/vsAB5uoFhXuHBiTmyNCCHubyZJoACYIezbjM + wvrkR5HaSJsfx5G+fwBHmcwBEUUp9EUO5MH0O8WylKcqSqAAJgcAALnSWYHHKAYCgMAY + AA0ISHzG9cQPons2JGirkqEkp5nsggSk4x4MSshZpiehR8oKBoEIEmLLM3HyHoi9LzR4 + 5SDxEhTAPwZB8H8flKhAKp6hAdp5AAdsrVBUNRLGUI/AcVAvCKfwrwsFlABFRcOIan04 + qyjCFoQzUOome0/gADo/rkGUrHydaCGwLscIIBwFR44r2qLWqjADW9HxNXM3varSHH6e + Btn8AZ6nQARClCfRCj4Th9D5Ud23dd6CAgBkrnWWoHHSAgCALMYNCKwMx2krdqLq56H0 + c+WEJtOLlHufSCAoMmHiVKx5mgghvD0l6KgXQSR4UkVGrxNOFx7XWSrynabHub5iHwfZ + 9n+fQPioeoPngegAHheGd55BZNDyBxQjKJR/C9CwVrkEllYCrdn0RNc1TVQ1cgAfLqAZ + icyDFKx2FqghykqggDUIBMa6Yvbnujk017PH9HoOfp3GofoBnudwBj+Tp8j+QRQH2QWe + 8DwTEAaBIAAgdpcAedYCgIAYCH+DQjUGyNtq/XDj6W57mMq6rqAJYYABBjMqnKTaCHaW + GxMi8aj5BWfNWrzWEzogzAIG6h/9yjcRoK6k4owfs6Huc51HyfB8n+ewPinmp5nvE/B+ + l6atEmOgGk0Ngnn+MvD6QAAHBMu2EKH8jl/NtiGPfRWlvUfqbACfgLoIExJSsb/AROZS + CgQygApiR6jooY/noLUV8P8fB0k6EFgSQR3gAGPtvKArEoRBn4HNIGPseY9R+AFH0PoA + gehMj5D0IcUg+xDvUhVCtjj4B3C4AgOwAwBQAgFH+BsJJ5jKNUUeh12ZfVGsjfhDx1x7 + y+k7HqiYFRjoKILGyGogg+RyEcAMQQATCh/DxOkPMhQ9SFMOh4rci0F1sk5iGTeMrVCG + H9aWyQgp9h8DpHkPkeg9R/jzUwpoesCYvQrj8zwRobgGCTDkFUAAbAAAPBUvGRcRVDpu + jErJzLJ31NTkoQQej0AAJ2bEBJBRCxor+Oqp8AADIHu8Oax+NLA1lPskuUYzkln0yPOS + TYfcc4Oj7HyAQOolR8h1EYKgfYjI/zFVEAmKoCx3i5AgO4A5lIqgcLcAAAUO4xQCZDG4 + 5C0D0E+kirFXDBSdMIj2QQD53gAALBSgofA5iCDOBsSMCS8yhrabRGhEUrkQHKWuTibD + sCJEInuoiLEUR1DyH0PAeY/h3oAKmw0AEmpjUTQWIUNIDBGh6C0AAOQ/wHTrACBFV0rW + PldakyVtL61tRoIVSVDg93eAXDSQQCIQEFDwGMQQawVjAkVWY08gtLo1ENPijhtE3FnI + /VzK59Eb04y3ZcAYfg+gChvEiPkN4khWD7ftRSrxspnmSmWBEd0yB/qCA2R4AAA2zI5q + GrapUk2mnQqXUIfR1AHhQIIBaniCR0CmYuHZsTAIWvtkeSVRrCnMVARKwsopyZ8wRJPK + whg/T7D6HYPQfY6x3j+HUCMK49gRj5I2r6r9pyyB+DGAsQwgQwABDy+AFFNAXohlm2wn + Ftk2xEcufCWI/2XkCAQDkggHA2oKHAIYgg5hLGPh2AomlASIIguoiGVSPGFLRdhUue0k + nOyVaqO9hoBx/D7AMGoRo+A1CXFgPwS9qL4FeX2AAA47xdARHeAu8jhgNhNMCoJtM3ag + NMlYh+ult2SXSpVUZqY+h0wOk8AAFcTEEDYJCAAdwtiCAKiqACGcbaiNPwVdV11Aq6Uu + h6UKbNQcRLQMuTyNg+7Mj7HMOwf45gShYHsCUfZ9oH3xyASMO4XAFB/EQGcARhgGgnII + BIGd37wQVVotOw8r5tWKJItFW7Ch+IGOrg+CEYB7gLIIDMZKChohHIIPUapcmOgDYAtG + xVJzmofyxG3EjJbH5zsni8kg+B3j2H0Ah3QBgyCIHwGQTotR+CdyDo+tZMQC32AkO8mY + /AFgBA4n0f4A3DXaiOdCk59IBSXglqVN51MuwOHQQqB5yj1Dxi8AEGo2DA1tNgMmdZTo + uPgcNNRgD5yJ4FpZixkeCsoomxSfPAN27Hn2KIwOyGPh2wcHEOgf44QThaHsCd95TtIW + oDgFcBQdxHhsAES4Br4krg1J7qOb7lrcNMrgAAfg7oHDji+iSoRDB5M4AAC4Xh4gRmwH + 1vgAAzbauuAes2JsQK3HnMzsSWRD87wUz9LO6RIqhRGghREdzyHDD8AOF8Qg+AviiFyP + wUW4aJgCIGAQdwuQJjuAeAkfYDQAAbQqjbhzJ5/Ym3rONN2zrtxrgWPocB8GcYjbXOIA + I9YFgjMWhYH5sB5jPIINQJzBllz0YOojjNSnZMgUSfbj0QbHdlsR2zEh/t7DuHoP0bo5 + R/jcBUFwewKu4b95c4EMwUAEBuEyHMAgkZSglYeDqw+BuvQVqaTyb/Ht7EEH1vre2YGA + lEqPqhGMmgNLBAABgLhsB1orAANwNzGiCgK0/Bi7ERc6ocLuiDjqiSgyx8ks/Yca2E2M + uyQMew7R7D4AUAEfoCAsiAHwFkVAvR+Co7+4MioAx2i5AoO0CICh8gP52FMggBZ6LSOT + Eaxa294EE1W1UcJCTqbSyn8C7xInkZNDDOa2Jrxx+IAAOSYjDxgBsrp7iBhDtxkR8zsa + 3ZqD+JqiyQzTZo6QmxboeofwbAcIfwa4FoLwe4FreL6ZdoMIJYBANITwPAAi5oBhpQf4 + CgHyoaCRtbKsBQhqMAfAbaN5nS6bKjULoripXSD48QIZ+i5o14bgOYggdYVS5wggBDDr + 87FUDx87LTPL3yH7KxD6e6C7eaWBQwezkL4wAT5IKgPge4KgVoYIfoVsD5d4AQdYW4Cg + dYCgBgfKTwDYKT8L7yuY47UozAjAfiUgfIbrVz+beiAMFzLL9SB7xQAAFoWQ2AaiQxE4 + ZA8TDpxsQ53qbUBJ4C6yxxRhR7PkKrfr+L8qlqN7aAeIe4fwaQbsVIGIMQe4wcNRUILA + IgA4MAU4P4AoT6dJpQAACw4TZDd4rhqROBEwfMGx3RYziSRz3SpkSzj5NkUY+BEwe5jo + GgZw2AZqeIAAfQrCdJGrOCbSer3qbccaHjvyI8UR8q7Q4ruDUCMyNTtQnjFbj4eodofD + MYAYfwBIJwO4e4JwWYYwfoWcWJKYAQdAWoCodIC4Bwe4ChMgKgx4CK3ZbLUpp0eC7BnR + qobQ+CBplC70YCHzoUeL8wjAeSPoGrNqtbn4sAfyMAZMXgoQBzX6t8kMkbAScbBRkKC0 + mh18dEYKNTsalKgC7qCBIIeUVAZobAf0bIMwe8bUghBIKQH4AwLIVoQgAywABZWEXpyb + Fi3q3QhgfQcTyzpcGSbcQbycm8eJ8ZE6BYFoXDDbdgsJlYggaEFokSn8PJOBgjPEHccx + NyNJgLArxwgUUMQbEJRYeodge7MYAkfQJIOge4JIXAZIfsuEqA2QAQcoWYCwcwDICAew + C426voAwCcKMnsZ7oZ3SBga4gQfodiH7jbfkcryCR8ebqDFSMptIeqTQEh05CxMwsQeA + YIgga70qagiIBjT81D3joRRjFTZ0tI+C7JqZaLtRtKxbaK68S8LR9j+DizaAeQfAf4Y4 + agfwY4HYNQe4HczA14JYHIAwKYWgRQAwVgAABQEAggC6HKcEwiyhRKM4hSTQe4aQ+CBb + eUjxWricizykKal5XwDYPoggDBowsQdAUYggb50hxzDbXAuzF825Dkiiobi7jzEkaKCr + jpNJRTLUKi8AegdQe4ewBgAwf4BQIgOAe4IgXoZofoXs9osgAIcQWACwcgDoCYewDRMi + voA468sy3hkofk2CiIawi4jdFrEE6pZzibo7Ubo1LUn8H4t4L6cwPYsYcAQoggc4TBsS + HZ/sHTjibypTWE2wmqorjAm9E7Ok7c71L4o7ANOgpxEwehhwYIZ4foYIIANwe6m1IAr4 + IYGYAoJQXgSAA4Wof4BJDYAIDLrq3SHqb4fIb7y0QNTxEhWkdMHptsPMmtLQjC4M+8II + AAE0IYsIbKmbDAWkJcShytUxg1OUsBtik870wNPL2TeL2ySBacK5WTKYeYdQfAewBtGo + BQHwNYewHwYYaQfw8NRwrIAIbwVoC4cAEICweoD1JcJZ+boskD2oggfDWw6qKddk3Dob + d6AUJzKcnJQxka3QfhEwATdgFoWIsYaStQeoaMJbACGghDyj3aVpD0vjojozFEBFZLEr + Kkw0nrjYy78rF5SzNZhwXgZgfgXgIoOIfCUVbokgHYFoAgIQYgSwBFH4BNc4AADLnsQj + 2UUiiNArezLz872dnKMMBVB1i8Hlo0GDGBhQfLhwGgZgsYZQFw6Qd7DbX4AdhrWETNYp + hbF5NUc5NrPcGMLZk1osTUeQgYeYdIfAeoBoA4f4BYHIM4ewHIZAawf0SVlQh4bIVQC4 + bgEwDIepWADUSABIDaR0m7yp5TrT5DfFjtXtpKbs2jo4zaIboL2FiLK1yiNSDYggG1eA + AZjorIfjgAZaRooQBbhxQkcNzVhsA5HUCEBrjquU09YVO8HNxycET8mxkVj7qJhwWwY4 + fgWwJYOwfCadRwGwFIAgHYZITQBAYgAABADgj6vskLEwiYfyBoe1p6CDgDzb9Ir0vdBU + n9zMtBEL+IebgAF9H4yTgorQeteAaUrooQBjMjojZbEs6VslsUZ9/FVVnNPrxr2kZi7J + qVZwfNtltwBYGgMgewGgZobMpczAAIaoVADAbAFQDYeh8QDKvoBQul/17LNbM6CFA7tj + jViETDKl8F61yZ2MKrilpIeyBYEwUBeMu4rIdwXQggbJiIf5QgAIBTn9VBXV8TzlZUGM + nbtKgCfF/ji9VVEa3LKJRTaL9xEweojYWIYYfgWIKAPQfCvUNQF4EgAQGgZ4T4BQZQAI + BADRagDYLckS7CBIf4erM4/l70tWF1rks8i8Hd3GFsv1EK7lytiwnce4ggDoQk/QLIrY + c7RwAAcAQIl5QgBF0JzdhmF9h0/svtpMZrecBljj3UrzPRN86iSye73LAb31tOBAmdGw + GAMIe4GEVUVLlwAIaAUgDAagF4D4eiRYDMSABYEMHqBCTFvGOlzGF0kGQFKF8uItZdQG + TOZxgIfJXwCU44DJ7orQcoSAxj6QAESq+jDtoUBdo82seU5o91ndBFYNLOUbjTA5zVjM + wS7dfwggewwAVYX4fgVYKwPwfF6q+IFQEAAQF4agUYBQZ4AIA4DIgYDlMl3JPyTAYqLs + vlT9E58l/S6mIjYUnmc8tiV6cKoAjSKJP9fmZwA9Xd/lZY4+QmdEBVr8Kzx8K8LMAlpE + wYlFr6ygh1tIfQeoBdGoBIFoL4ewFoawcAf9Kq04AIZgUIDAZ4GYEQegF4f4DMiAAIBc + RSNZXweuiiCDXtBOsNP1dsr+sSWjPx9b4NMGPloJt9PSxlEE5+AauKpM1CNk5lrZk+ji + oV8Rttebx6x+uLyw6ge4+wUwXYfYUwLYQQfOOCigEoDgAIFQbIU4Bgah/4CwgYDwMYno + wAf4el6A/iLSerEFBktePW08cmTEnBbTFdYVL2s+uVswheu46c6eQIitDjSI+WGKNzAs + 7Y52tVsYk1ErUKMhkVFgorWGU1/eu25hg9yV2gvFZwfYeoBSGwBAFLvQFIbYcgf8GyP4 + Y4ToDAZgHAEwegGYf4DGq4Bi2Z3oegY46TL0mswMwh87Bmc+KW2VVmZNMGJUmmjW4ExG + 2xqqA92dyAjBfNNqNoistetEn2QY82tNo+I+tjUdEu0zzuOM6rOkxAhYfgjAe4wAUIW4 + fYUIMAQwfIMCFQEWNoFAbgVIBgax/4CggYDoM6oAewZaByKbyTBG1GPUJy29L21+Tmsl + 1dw9pAfxEwfCBohYhYA1dQBSRoBZ74ATX7Nme4ald0stVQA5ju3V/+PPDk6uUvCi2+nc + TdsFPzKWTOGNoPNoo4eYdm6oBIpoA4EzbgEwb4c4f9URwQYITIDAYwHwFQei4gDD8BDK + kYe5iyBEQNy9P+eJDu4+PmZiAO1sKtYabfSuj98Bascuu+Q51wBYGAggEYRTDerQsAfd + qgAAb70Qdr1D84A5GucOALUliWvXClinBjeMc7ANssUWULsUvpOFOU2T9RhWwoAATYWQ + fYTYMwRYfLC5eAD2zIEgcAVoBkjYAoCYiAD4NbyxGaiLrVD8w8vzUyld8ikxt1FybNFH + IJhXUYf4yIDb1bnZGAgTYI2IdoW5i5KI6rhAhgBLN91XIpXWnCw1VedvAFjOKJ9WE+ur + FaykxG4IAAegdgfgew8oAoEi0IEgcQdQf8sZdoXYSgDAYIIgF4ekFoC7noBVJRE48Ih2 + mt8ethH2FmPXS4zG4Qu+4EBMaPJ73R479Q6gEpsJK8/hKge79oAAaSUR4RpfgqK1YmnJ + WVQNBLj2mNFzBHYetdsztuU9o4fXJgmwSwVwfYSwNYR4fPcRUADXb4EAcYWABobyaoB/ + G2znjAXw+CVHndBk6G/pNvI9xG2VoHnN82kAoWu9GQggCa/yTb/hd1C5i7/IhgAqHY8r + K+UGJ0sHhXOA9G5nMWusmtemmcTIy4egdfjYAomIAYEQqQEQcwqydxKYWwR4C4XYJIGg + eoIkXtToAJT5EpTsKV1ejHTnCXwe+8oGdKx6SayL+SH/iCNIfRXwAMPAFwXL8MiRnoaq + jYAAeIYRpYBV0MecHviqCcHK3GsmttfKulY/YypHSXYvBDrw9M2htHso0B5IgABSKrfS + ROCSfRwAELhkNh0PiERhoXCIBDjmWIOcICAoNAQACo2hb7c8PAMMf7/iQAlMLk8Llsrl + UymENmcRmM3l8smYBl8xm0MAM3oMOoFEoUwmb4e0LDJphYdOcrqlVq1XhbwYsLaxXlEL + BIIhYCnc7mc6m9npAAoc0hj+l1mr8spNvmsutcxnd0nkmtUunFzvU7uGAllwejsfj2Ac + nAIhKr2ELpd7/dNYzEOV6LCy1KA4exKAARG8sAdNn0Nn9ph9ou8quGOiFnmsqvdGmW0u + dsoz/2Op1tK3eEusRfz9hb3e8LEqVhYTJGZ6XTiL7eELZYsuYHA0LAgE1uFiXil9m8XF + oHB39U3102243mz4XBtlIwes1+1tk/lD5fh/AEfQAAERhTn0Rg6ksfQ6uoqoKAeAAMnS + WYIHIAQCAQj4IhCwz4Pooq7w83a+LlEcTN7D6cMK4D0vlFLhoW47kuWAAYF+hYEA9Bsd + x4ZocIWfJxoWAwCu88D4r5D61tUwqdRHJcVpQnqfvOuazLWtq+yQo8Otc16exA2bkHqd + p/OWuB/BAKp6hAdh4gAdkeIcVRDAqV4qh6e4oAABoQRwBz5xc3jdS3JclylLqiqJMEQr + 5Q0wpMlEVyour2odFiZn8wp8uQAAbGsuM5VEzJrC8rJeoWAsjgM7stJtRkYog4irS5JE + SUQ/K8Rc8z2K+nS9vwvVcNuux/HzTUAwGQ5Rn0Q49E0fQ9TkCIGpAdZaggc4B22AYAAm + Ezi1wiT7TBR8UKuvNDsFYdiUdSEX1zQr5nupoABkZMhgpUd9qsaAhoWexsVTVoC26w1h + N3dVY0ut9YQ8/D40tYFBRBcj1w63CUvfLElp3TqWntMp7n65B+A+Kh6g+d55gAd7qFIQ + ILFULYhHsKoAAYDSwAqwSVSc2csXFRVZUJV+DpavcrxjKdG4q9Dw17S7VuSeqFhMTyFg + iH9+a4hx+nwhZlBOmB+IWA4DoXbcur+u+lP2u1aY5E9eSSAFJp437y0zuWmvpLMW761e + L6+44Bn2AABkET59EEP5PH0P7MAeBYAAkdhbAgdQCW6AZ/gmFK2AEj9XaKwMk7dRdE19 + pFMYx0q5vJoEYKNKstqFoufr4fR8qcN6Fg2NWu+EAB5mghZpiZXQAASBUTqBEss6M+qs + bZW75JTvL54nJUQ+jhXvOE+9I7gmy4Hudp/nwfMBHxNU2Hjqs3qoTo+AmUYxCOfIuAAB + YMoWAsCyjXqnsYU3VQLpkwrmgSa13K73pEOdqh5qZd1NFjA+QsFosSxpFeGqIbweyFjo + FCkZIcHCcMQXMix2rbjVLDYUpY+DdDgKObulp6Lpm/wKPQlEr4/B7j/H6ARsoAw+ibHy + H0Qgoh9iEIiA0BIAAIDsFuA4dQBgCABSK58lgAVurtMOuhdT2DeIldOz1UK8YDH0YhGV + L5dYctRjY9InY+EaAYDQQsDxU4OnUHgMQrgWCHgIie9NqRMHYohYgeWCLCYapJkaW9Tp + wG/u3RcwgvpsD9m3Z8oN1ZcWmKuh47QAA+B3D/HyPZsCbGUJ+IgH0MACBCCCDGASED/X + /wBaO6s96HyhwFXS2s3MEo0upbrA2Q0Z3AQQUrDWZLPx/tlAA+shYKhWELAaDCPZVR+M + sAANFf80RzJDbQ4h0cLIyF5ke+Q4o/mHENlCYOY7TZNwzfK+OGz33qw6dOAFKrGm3gAM + XM8AhyACBqEaPh4JDwBjeFYA8b4IQLj9A6AACgKyXJHhafOZLsi3TEhPPZwEloHonb6o + eRb2lxLtkOpZSw/lqgABEIlrIQJskMHuN8hY3HfPEGcqmDgA4OPbYfMt10657HkjWlZc + UKVJRnUcrAvShHtpZnZUUlcoZhoxHkpoYQ0R+jCIgA4fIvwGjsAMAgA6RQJAoJ5LuSsB + VETzds3WQ6UjYq2edVBRTHWoPjYkiWZrT1zxsmekBsBIJAAAA+Hw7zlEGkxHQJ8hY4RD + oxRoAVVoBITVNkIZhiSIFZ0frxPF6JJ65T6bbUs9ytob0odW9lvsyl5MRIYNoao/RxAW + IrS9AZDAMj+GEAwcYAQDALJOBFcDC1Dl/mM0KBjFJKwOudGqBClGMRme68qqtrHyQTsI + p0fjhy2KAT4DMhYCqLP8vSAOQY9RqkLvcQsejxpRjeJc6MAsHABMGnhF+0ZD3apev63Z + dEjT8UqPFO27ryrA2pV22yYhZSlV3koREZozB+DeBgCQAUF1UkMA+PEXQDRpgOAWhhb1 + 6Y0sRwTGCYOAFKzuXDa5VzpqPLEhhYCT9T1BXek/DCjOQCYKdgqABkmQ25nlYMAN0boo + BEmutCzGc8YY3WozjVLtWLUV/bg61hdc3rOnWCw6qWMl5K/IWOQbw/R3F0sOABVoAAQj + FEwAwXIOqLQXAk6BxGccbVwgPW8t1sWh4MxtaC7Fo1anjsE7kolK5E4LnvUSf6SZnaRk + RMDSuAczX/qwsO1V/56q2tRpbB6gW9aJwroxvxL8itVAALEYY/BcEMowB4KQPgBh8FaI + UBIYQAAIAmSwBoHNRaLP0bKS5dNlYNv7jnQKl2fSPerF5QMzq6pN1Xd5WaXoETDx/BHH + 5PVCUiyuTeutUG2JZy9ivb+DJmQontautx+t6xpbcW2MRPrvHFn5Da1o5Bwj9HuBwCQA + gEhTD2PcMRxQLlkAAEQbgqQFiYBABkAICgAgOBAScA6EI1aAyxYPQq8D4Uez/YJiNSVX + Rkr2rJhNz5/q1cBCrlTQcAbxqdugla7dFt8ulzrTOm3upQ5uoKvXMcHQ2OMX6UY9TfAH + H6P8AI1hwD+HGC4L49wZEQAYQwGQLAQgCDKM0ToCgtX534AwDZQwDgS5nswqtG9A77RZ + y+5rzoHaO1RzLHi8MvbP6FYTdONJLc25JMjA+iMv6hnpOrTVQ9REoU6uO65u6V6ftXPS + AZdK72mKFv8ho9x6KaAGPwlWRx+g2DKPcMA1Bvj+FeRBgwAANkMCGEIGQAwvivEMAgHm + JouD/AGAfqgBgHEnAHnHa3I0RXZ2j5bd27249HdVdLbHio4cux70qz3fOl95+m9xHdJ+ + TfU/HqYiWUldmtH7OweQ8iUgP40AId38h8hUD4PgR4vxnh+hLCGByiqHmjmCGAgAQAMA + Agjg/AwADAbAtgjACAJEiPHqawLwMQMwNQNwOQOwPF9h9B9jfBShdB+BwhBBQB9BQBwB + 0B/hYCGBtqbCsLyKYCGLzCFgXlqM5AUgPgBALAQgMgBAGFVPJnhO6QPwkQkwlQlwmQNB + +GShvhzh/B2urB/Brh4B6AABliGKeiFr7CFpuEGwCgAALsQLfCGQaITHRwmw2Q2w3Q3w + 4Q2DxLxAAB5CGJwrJiGB1CGF6muPapBgAQxpxrevLw4xDRDxERExFO5nyHeGAQZCFvKx + FxJxKRKxLRLxMRMxNRNxOROxPRPxQRQxRRRxSRSxTRTxURUxVRVxWRWxXRXxYRYxZRZx + aRaxbRbxcRcxdRdxeRexfRfxgRgxhRhxiRixjRjxkRkxlRlxmRmxnRnxoRoxpRpxqRqx + rRrxsRsxtRtxuRuxvRvxwRwxxRxxyRyxzRzx0R0x1R1x2R2x3R3x4R4x5R5x6R6x7R7x + 8R8x9R9x+R+x/R/yASAyBSByCSCyDSDyESEyFSFyGSGyHSHyISIyJSJx4iAgAA8BAAAD + AAAAAQBxAAABAQADAAAAAQCZAAABAgADAAAABAAALO4BAwADAAAAAQAFAAABBgADAAAA + AQACAAABEQAEAAAAAQAAAAgBEgADAAAAAQABAAABFQADAAAAAQAEAAABFgADAAAAAQEh + AAABFwAEAAAAAQAALCwBHAADAAAAAQABAAABPQADAAAAAQACAAABUgADAAAAAQABAAAB + UwADAAAABAAALPaHcwAHAAAD9AAALP4AAAAAAAgACAAIAAgAAQABAAEAAQAAA/RhcHBs + AgAAAG1udHJSR0IgWFlaIAfYAAEAHwAOACwAIGFjc3BBUFBMAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAD21gABAAAAANMtYXBwbOoCxvvn7AuJW4CIyiOWp2wAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAADnJYWVoAAAEsAAAAFGdYWVoAAAFAAAAAFGJYWVoA + AAFUAAAAFHd0cHQAAAFoAAAAFGNoYWQAAAF8AAAALHJUUkMAAAGoAAAADmdUUkMAAAG4 + AAAADmJUUkMAAAHIAAAADnZjZ3QAAAHYAAAAMG5kaW4AAAIIAAAAOGRlc2MAAAJAAAAA + Z2RzY20AAAKoAAABAG1tb2QAAAOoAAAAKGNwcnQAAAPQAAAAJFhZWiAAAAAAAABxDgAA + OesAAAOdWFlaIAAAAAAAAF8vAACzygAAFlBYWVogAAAAAAAAJpgAABJgAAC5OVhZWiAA + AAAAAADzzwABAAAAARhic2YzMgAAAAAAAQwaAAAFwP//8v8AAAdgAAD9zv//+5j///2W + AAAD9AAAv05jdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0AAGN1cnYAAAAAAAAA + AQHNAAB2Y2d0AAAAAAAAAAEAANF0AAAAAAABAAAAANF0AAAAAAABAAAAANF0AAAAAAAB + AABuZGluAAAAAAAAADAAAKPAAABUgAAATMAAAJuAAAAm9wAAEXsAAFAAAABUAAACMzMA + AjMzAAIzM2Rlc2MAAAAAAAAADURFTEwgMjQwNUZQVwAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAbWx1YwAAAAAAAAASAAAADG5iTk8AAAAYAAAA6HB0UFQAAAAYAAAA6HN2 + U0UAAAAYAAAA6GZpRkkAAAAYAAAA6GRhREsAAAAYAAAA6HpoQ04AAAAYAAAA6GZyRlIA + AAAYAAAA6GphSlAAAAAYAAAA6GVuVVMAAAAYAAAA6HBsUEwAAAAYAAAA6HB0QlIAAAAY + AAAA6GVzRVMAAAAYAAAA6HpoVFcAAAAYAAAA6HJ1UlUAAAAYAAAA6GtvS1IAAAAYAAAA + 6GRlREUAAAAYAAAA6G5sTkwAAAAYAAAA6Gl0SVQAAAAYAAAA6ABEAEUATABMACAAMgA0 + ADAANQBGAFAAV21tb2QAAAAAAAAQrAAAoBAwNzNTv9zMAAAAAAAAAAAAAAAAAAAAAAB0 + ZXh0AAAAAENvcHlyaWdodCBBcHBsZSwgSW5jLiwgMjAwOAA= + + ReadOnly + NO + RowAlign + 1 + RowSpacing + 36 + SheetTitle + Canvas 1 + SmartAlignmentGuidesActive + NO + SmartDistanceGuidesActive + NO + UniqueID + 1 + UseEntirePage + + VPages + 1 + WindowInfo + + CurrentSheet + 0 + ExpandedCanvases + + + name + Canvas 1 + + + Frame + {{2002, 20}, {1215, 1180}} + ListView + + OutlineWidth + 142 + RightSidebar + + Sidebar + + SidebarWidth + 157 + VisibleRegion + {{-4.5, 0.5}, {522, 535.5}} + Zoom + 2 + ZoomValues + + + Canvas 1 + 2 + 4 + + + + saveQuickLookFiles + YES + + Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/bomb.icns and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/bomb.icns differ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/Controller.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/Controller.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/Controller.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/Controller.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,65 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import + +#import + +enum BreakpadForkBehavior { + DONOTHING = 0, + UNINSTALL, + RESETEXCEPTIONPORT +}; + +enum BreakpadForkTestCrashPoint { + DURINGLAUNCH = 5, + AFTERLAUNCH = 6, + BETWEENFORKEXEC = 7 +}; + +@interface Controller : NSObject { + IBOutlet NSWindow *window_; + IBOutlet NSWindow *forkTestOptions_; + + BreakpadRef breakpad_; + + enum BreakpadForkBehavior bpForkOption; + + BOOL useVFork; + enum BreakpadForkTestCrashPoint progCrashPoint; +} + +- (IBAction)crash:(id)sender; +- (IBAction)forkTestOptions:(id)sender; +- (IBAction)forkTestGo:(id)sender; +- (IBAction)showForkTestWindow:(id) sender; +- (void)generateReportWithoutCrash:(id)sender; +- (void)awakeFromNib; + +@end diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/Controller.m firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/Controller.m --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/Controller.m 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/Controller.m 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,260 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import + +#import "Controller.h" +#import "TestClass.h" +#include +#include + +@implementation Controller + +- (void)causeCrash { + float *aPtr = nil; + NSLog(@"Crash!"); + NSLog(@"Bad programmer: %f", *aPtr); +} + +- (void)generateReportWithoutCrash:(id)sender { + BreakpadGenerateAndSendReport(breakpad_); +} + +- (IBAction)showForkTestWindow:(id) sender { + [forkTestOptions_ setIsVisible:YES]; +} + +- (IBAction)forkTestOptions:(id)sender { + int tag = [[sender selectedCell] tag]; + NSLog(@"sender tag: %d", tag); + if (tag <= 2) { + bpForkOption = tag; + } + + if (tag == 3) { + useVFork = NO; + } + + if (tag == 4) { + useVFork = YES; + } + + if (tag >= 5 && tag <= 7) { + progCrashPoint = tag; + } + +} + +- (IBAction)forkTestGo:(id)sender { + + NSString *resourcePath = [[NSBundle bundleForClass: + [self class]] resourcePath]; + NSString *execProgname; + if (progCrashPoint == DURINGLAUNCH) { + execProgname = [resourcePath stringByAppendingString:@"/crashduringload"]; + } else if (progCrashPoint == AFTERLAUNCH) { + execProgname = [resourcePath stringByAppendingString:@"/crashInMain"]; + } + + const char *progName = NULL; + if (progCrashPoint != BETWEENFORKEXEC) { + progName = [execProgname UTF8String]; + } + + int pid; + + if (bpForkOption == UNINSTALL) { + BreakpadRelease(breakpad_); + } + + if (useVFork) { + pid = vfork(); + } else { + pid = fork(); + } + + if (pid == 0) { + sleep(3); + NSLog(@"Child continuing"); + FILE *fd = fopen("/tmp/childlog.txt","wt"); + kern_return_t kr; + if (bpForkOption == RESETEXCEPTIONPORT) { + kr = task_set_exception_ports(mach_task_self(), + EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | + EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT, + MACH_PORT_NULL, + EXCEPTION_DEFAULT, + THREAD_STATE_NONE); + fprintf(fd,"task_set_exception_ports returned %d\n", kr); + } + + if (progCrashPoint == BETWEENFORKEXEC) { + fprintf(fd,"crashing post-fork\n"); + int *a = NULL; + printf("%d\n",*a++); + } + + fprintf(fd,"about to call exec with %s\n", progName); + fclose(fd); + int i = execl(progName, progName, NULL); + fprintf(fd, "exec returned! %d\n", i); + fclose(fd); + } +} + +- (IBAction)crash:(id)sender { + int tag = [sender tag]; + + if (tag == 1) { + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + [self performSelector:@selector(causeCrash) withObject:nil afterDelay:10]; + [sender setState:NSOnState]; + return; + } + + if (tag == 2 && breakpad_) { + BreakpadRelease(breakpad_); + breakpad_ = NULL; + return; + } + + [self causeCrash]; +} + +- (void)anotherThread { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + TestClass *tc = [[TestClass alloc] init]; + + [tc wait]; + + [pool release]; +} + +- (void)awakeFromNib { + NSBundle *bundle = [NSBundle mainBundle]; + NSDictionary *info = [bundle infoDictionary]; + + + breakpad_ = BreakpadCreate(info); + + // Do some unit tests with keys + // first a series of bogus values + BreakpadSetKeyValue(breakpad_, nil, @"bad2"); + BreakpadSetKeyValue(nil, @"bad3", @"bad3"); + + // Now some good ones + BreakpadSetKeyValue(breakpad_,@"key1", @"value1"); + BreakpadSetKeyValue(breakpad_,@"key2", @"value2"); + BreakpadSetKeyValue(breakpad_,@"key3", @"value3"); + + // Look for a bogus one that we didn't try to set + NSString *test = BreakpadKeyValue(breakpad_, @"bad4"); + if (test) { + NSLog(@"Bad BreakpadKeyValue (bad4)"); + } + + // Look for a bogus one we did try to set + test = BreakpadKeyValue(breakpad_, @"bad1"); + if (test) { + NSLog(@"Bad BreakpadKeyValue (bad1)"); + } + + // Test some bad args for BreakpadKeyValue + test = BreakpadKeyValue(nil, @"bad5"); + if (test) { + NSLog(@"Bad BreakpadKeyValue (bad5)"); + } + + test = BreakpadKeyValue(breakpad_, nil); + if (test) { + NSLog(@"Bad BreakpadKeyValue (nil)"); + } + + // Find some we did set + test = BreakpadKeyValue(breakpad_, @"key1"); + if (![test isEqualToString:@"value1"]) { + NSLog(@"Can't find BreakpadKeyValue (key1)"); + } + test = BreakpadKeyValue(breakpad_, @"key2"); + if (![test isEqualToString:@"value2"]) { + NSLog(@"Can't find BreakpadKeyValue (key2)"); + } + test = BreakpadKeyValue(breakpad_, @"key3"); + if (![test isEqualToString:@"value3"]) { + NSLog(@"Can't find BreakpadKeyValue (key3)"); + } + + // Bad args for BreakpadRemoveKeyValue + BreakpadRemoveKeyValue(nil, @"bad6"); + BreakpadRemoveKeyValue(breakpad_, nil); + + // Remove one that is valid + BreakpadRemoveKeyValue(breakpad_, @"key3"); + + // Try and find it + test = BreakpadKeyValue(breakpad_, @"key3"); + if (test) { + NSLog(@"Shouldn't find BreakpadKeyValue (key3)"); + } + + // Try and remove it again + BreakpadRemoveKeyValue(breakpad_, @"key3"); + + // Try removal by setting to nil + BreakpadSetKeyValue(breakpad_,@"key2", nil); + // Try and find it + test = BreakpadKeyValue(breakpad_, @"key2"); + if (test) { + NSLog(@"Shouldn't find BreakpadKeyValue (key2)"); + } + + BreakpadAddUploadParameter(breakpad_, + @"MeaningOfLife", + @"42"); + [NSThread detachNewThreadSelector:@selector(anotherThread) + toTarget:self withObject:nil]; + + NSUserDefaults *args = [NSUserDefaults standardUserDefaults]; + + // If the user specified autocrash on the command line, toggle + // Breakpad to not confirm and crash immediately. This is for + // automated testing. + if ([args boolForKey:@"autocrash"]) { + BreakpadSetKeyValue(breakpad_, + @BREAKPAD_SKIP_CONFIRM, + @"YES"); + [self causeCrash]; + } + + progCrashPoint = DURINGLAUNCH; + [window_ center]; + [window_ makeKeyAndOrderFront:self]; +} + +@end Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/crashduringload and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/crashduringload differ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/crashInMain and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/crashInMain differ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/English.lproj/InfoPlist.strings and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/English.lproj/InfoPlist.strings differ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/English.lproj/MainMenu.nib/classes.nib firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/English.lproj/MainMenu.nib/classes.nib --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/English.lproj/MainMenu.nib/classes.nib 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/English.lproj/MainMenu.nib/classes.nib 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,47 @@ + + + + + IBClasses + + + ACTIONS + + crash + id + forkTestGo + id + forkTestOptions + id + generateReportWithoutCrash + id + showForkTestWindow + id + + CLASS + Controller + LANGUAGE + ObjC + OUTLETS + + forkTestOptions_ + NSWindow + window_ + NSWindow + + SUPERCLASS + NSObject + + + CLASS + FirstResponder + LANGUAGE + ObjC + SUPERCLASS + NSObject + + + IBVersion + 1 + + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/English.lproj/MainMenu.nib/info.nib firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/English.lproj/MainMenu.nib/info.nib --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/English.lproj/MainMenu.nib/info.nib 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/English.lproj/MainMenu.nib/info.nib 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,22 @@ + + + + + IBFramework Version + 670 + IBLastKnownRelativeProjectPath + ../GoogleBreakpadTest.xcodeproj + IBOldestOS + 5 + IBOpenObjects + + 221 + 29 + 2 + + IBSystem Version + 9F33 + targetFramework + IBCocoaFramework + + Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/English.lproj/MainMenu.nib/keyedobjects.nib and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/English.lproj/MainMenu.nib/keyedobjects.nib differ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/Info.plist firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/Info.plist --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/Info.plist 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/Info.plist 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,55 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + bomb + CFBundleIdentifier + com.Google.BreakpadTest + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + BreakpadProductDisplay + Breakpad Tester + BreakpadProduct + Breakpad_Tester + BreakpadVersion + 1.2.3.4 + BreakpadReportInterval + 10 + BreakpadSkipConfirm + NO + BreakpadSendAndExit + YES + BreakpadRequestEmail + YES + BreakpadRequestComments + YES + BreakpadVendor + Foo Bar Corp, Incorporated, LTD, LLC + BreakpadServerParameters + + Param1 + Value1 + Param2 + Value2 + + LSUIElement + 1 + + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/main.m firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/main.m --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/main.m 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/main.m 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,34 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import + +int main(int argc, char *argv[]) { + return NSApplicationMain(argc, (const char **) argv); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/TestClass.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/TestClass.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/TestClass.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/TestClass.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,37 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import + +@interface TestClass : NSObject { +} + +- (void)wait; + +@end diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/TestClass.mm firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/TestClass.mm --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/TestClass.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/testapp/TestClass.mm 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,95 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#import "TestClass.h" + +struct AStruct { + int x; + float y; + double z; +}; + +class InternalTestClass { + public: + InternalTestClass(int a) : a_(a) {} + ~InternalTestClass() {} + + void snooze(float a); + void snooze(int a); + int snooze(int a, float b); + + protected: + int a_; + AStruct s_; + + static void InternalFunction(AStruct &s); + static float kStaticFloatValue; +}; + +void InternalTestClass::snooze(float a) { + InternalFunction(s_); + sleep(a_ * a); +} + +void InternalTestClass::snooze(int a) { + InternalFunction(s_); + sleep(a_ * a); +} + +int InternalTestClass::snooze(int a, float b) { + InternalFunction(s_); + sleep(a_ * a * b); + + return 33; +} + +void InternalTestClass::InternalFunction(AStruct &s) { + s.x = InternalTestClass::kStaticFloatValue; +} + +float InternalTestClass::kStaticFloatValue = 42; + +static float PlainOldFunction() { + return 3.14145; +} + +@implementation TestClass + +- (void)wait { + InternalTestClass t(10); + float z = PlainOldFunction(); + + while (1) { + t.snooze(z); + } +} + +@end diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/tests/BreakpadFramework_Test.mm firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/tests/BreakpadFramework_Test.mm --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/tests/BreakpadFramework_Test.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/tests/BreakpadFramework_Test.mm 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,217 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// BreakpadFramework_Test.mm +// Test case file for Breakpad.h/mm. +// + +#import "GTMSenTestCase.h" +#import "Breakpad.h" + +#include + +@interface BreakpadFramework_Test : GTMTestCase { + @private + int last_exception_code_; + int last_exception_type_; + mach_port_t last_exception_thread_; + // We're not using Obj-C BOOL because we need to interop with + // Breakpad's callback. + bool shouldHandleException_; +} + +// This method is used by a callback used by test cases to determine +// whether to return true or false to Breakpad when handling an +// exception. +- (bool)shouldHandleException; +// This method returns a minimal dictionary that has what +// Breakpad needs to initialize. +- (NSMutableDictionary *)breakpadInitializationDictionary; +// This method is used by the exception handling callback +// to communicate to test cases the properites of the last +// exception. +- (void)setLastExceptionType:(int)type andCode:(int)code + andThread:(mach_port_t)thread; +@end + +// Callback for Breakpad exceptions +bool myBreakpadCallback(int exception_type, + int exception_code, + mach_port_t crashing_thread, + void *context); + +bool myBreakpadCallback(int exception_type, + int exception_code, + mach_port_t crashing_thread, + void *context) { + BreakpadFramework_Test *testCaseClass = + (BreakpadFramework_Test *)context; + [testCaseClass setLastExceptionType:exception_type + andCode:exception_code + andThread:crashing_thread]; + bool shouldHandleException = + [testCaseClass shouldHandleException]; + NSLog(@"Callback returning %d", shouldHandleException); + return shouldHandleException; +} +const int kNoLastExceptionCode = -1; +const int kNoLastExceptionType = -1; +const mach_port_t kNoLastExceptionThread = MACH_PORT_NULL; + +@implementation BreakpadFramework_Test +- (void) initializeExceptionStateVariables { + last_exception_code_ = kNoLastExceptionCode; + last_exception_type_ = kNoLastExceptionType; + last_exception_thread_ = kNoLastExceptionThread; +} + +- (NSMutableDictionary *)breakpadInitializationDictionary { + NSMutableDictionary *breakpadParams = + [NSMutableDictionary dictionaryWithCapacity:3]; + + [breakpadParams setObject:@"UnitTests" forKey:@BREAKPAD_PRODUCT]; + [breakpadParams setObject:@"1.0" forKey:@BREAKPAD_VERSION]; + [breakpadParams setObject:@"http://staging" forKey:@BREAKPAD_URL]; + return breakpadParams; +} + +- (bool)shouldHandleException { + return shouldHandleException_; +} + +- (void)setLastExceptionType:(int)type + andCode:(int)code + andThread:(mach_port_t)thread { + last_exception_type_ = type; + last_exception_code_ = code; + last_exception_thread_ = thread; +} + +// Test that the parameters mark required actually enable Breakpad to +// be initialized. +- (void)testBreakpadInstantiationWithRequiredParameters { + BreakpadRef b = BreakpadCreate([self breakpadInitializationDictionary]); + STAssertNotNULL(b, @"BreakpadCreate failed with required parameters"); + BreakpadRelease(b); +} + +// Test that Breakpad fails to initialize cleanly when required +// parameters are not present. +- (void)testBreakpadInstantiationWithoutRequiredParameters { + NSMutableDictionary *breakpadDictionary = + [self breakpadInitializationDictionary]; + + // Skip setting version, so that BreakpadCreate fails. + [breakpadDictionary removeObjectForKey:@BREAKPAD_VERSION]; + BreakpadRef b = BreakpadCreate(breakpadDictionary); + STAssertNULL(b, @"BreakpadCreate did not fail when missing a required" + " parameter!"); + + breakpadDictionary = [self breakpadInitializationDictionary]; + // Now test with no product + [breakpadDictionary removeObjectForKey:@BREAKPAD_PRODUCT]; + b = BreakpadCreate(breakpadDictionary); + STAssertNULL(b, @"BreakpadCreate did not fail when missing a required" + " parameter!"); + + breakpadDictionary = [self breakpadInitializationDictionary]; + // Now test with no URL + [breakpadDictionary removeObjectForKey:@BREAKPAD_URL]; + b = BreakpadCreate(breakpadDictionary); + STAssertNULL(b, @"BreakpadCreate did not fail when missing a required" + " parameter!"); + BreakpadRelease(b); +} + +// Test to ensure that when we call BreakpadAddUploadParameter, +// it's added to the dictionary correctly(this test depends on +// some internal details of Breakpad, namely, the special prefix +// that it uses to figure out which key/value pairs to upload). +- (void)testAddingBreakpadServerVariable { + NSMutableDictionary *breakpadDictionary = + [self breakpadInitializationDictionary]; + + BreakpadRef b = BreakpadCreate(breakpadDictionary); + STAssertNotNULL(b, @"BreakpadCreate failed with valid dictionary!"); + + BreakpadAddUploadParameter(b, + @"key", + @"value"); + + // Test that it did not add the key/value directly, e.g. without + // prepending the key with the prefix. + STAssertNil(BreakpadKeyValue(b, @"key"), + @"AddUploadParameter added key directly to dictionary" + " instead of prepending it!"); + + NSString *prependedKeyname = + [@BREAKPAD_SERVER_PARAMETER_PREFIX stringByAppendingString:@"key"]; + + STAssertEqualStrings(BreakpadKeyValue(b, prependedKeyname), + @"value", + @"Calling BreakpadAddUploadParameter did not prepend " + "key name"); + BreakpadRelease(b); +} + +// Test that when we do on-demand minidump generation, +// the exception code/type/thread are set properly. +- (void)testFilterCallbackReturnsFalse { + NSMutableDictionary *breakpadDictionary = + [self breakpadInitializationDictionary]; + + BreakpadRef b = BreakpadCreate(breakpadDictionary); + STAssertNotNULL(b, @"BreakpadCreate failed with valid dictionary!"); + BreakpadSetFilterCallback(b, &myBreakpadCallback, self); + + // This causes the callback to return false, meaning + // Breakpad won't take the exception + shouldHandleException_ = false; + + [self initializeExceptionStateVariables]; + STAssertEquals(last_exception_type_, kNoLastExceptionType, + @"Last exception type not initialized correctly."); + STAssertEquals(last_exception_code_, kNoLastExceptionCode, + @"Last exception code not initialized correctly."); + STAssertEquals(last_exception_thread_, kNoLastExceptionThread, + @"Last exception thread is not initialized correctly."); + + // Cause Breakpad's exception handler to be invoked. + BreakpadGenerateAndSendReport(b); + + STAssertEquals(last_exception_type_, 0, + @"Last exception type is not 0 for on demand"); + STAssertEquals(last_exception_code_, 0, + @"Last exception code is not 0 for on demand"); + STAssertEquals(last_exception_thread_, mach_thread_self(), + @"Last exception thread is not mach_thread_self() " + "for on demand"); +} + +@end diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/tests/SimpleStringDictionaryTest.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/tests/SimpleStringDictionaryTest.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/tests/SimpleStringDictionaryTest.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/tests/SimpleStringDictionaryTest.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,40 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import +#import "SimpleStringDictionary.h" + +@interface SimpleStringDictionaryTest : GTMTestCase { + +} + +- (void)testKeyValueEntry; +- (void)testSimpleStringDictionary; +- (void)testSimpleStringDictionaryIterator; +@end diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/tests/SimpleStringDictionaryTest.mm firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/tests/SimpleStringDictionaryTest.mm --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/tests/SimpleStringDictionaryTest.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/tests/SimpleStringDictionaryTest.mm 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,243 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#import "SimpleStringDictionaryTest.h" +#import "SimpleStringDictionary.h" + +using google_breakpad::KeyValueEntry; +using google_breakpad::SimpleStringDictionary; +using google_breakpad::SimpleStringDictionaryIterator; + +@implementation SimpleStringDictionaryTest + +//============================================================================== +- (void)testKeyValueEntry { + KeyValueEntry entry; + + // Verify that initial state is correct + STAssertFalse(entry.IsActive(), @"Initial key value entry is active!"); + STAssertEquals(strlen(entry.GetKey()), (size_t)0, @"Empty key value did not " + @"have length 0"); + STAssertEquals(strlen(entry.GetValue()), (size_t)0, @"Empty key value did not " + @"have length 0"); + + // Try setting a key/value and then verify + entry.SetKeyValue("key1", "value1"); + STAssertEqualCStrings(entry.GetKey(), "key1", @"key was not equal to key1"); + STAssertEqualCStrings(entry.GetValue(), "value1", @"value was not equal"); + + // Try setting a new value + entry.SetValue("value3"); + + // Make sure the new value took + STAssertEqualCStrings(entry.GetValue(), "value3", @"value was not equal"); + + // Make sure the key didn't change + STAssertEqualCStrings(entry.GetKey(), "key1", @"key changed after setting " + @"value!"); + + // Try setting a new key/value and then verify + entry.SetKeyValue("key2", "value2"); + STAssertEqualCStrings(entry.GetKey(), "key2", @"New key was not equal to " + @"key2"); + STAssertEqualCStrings(entry.GetValue(), "value2", @"New value was not equal " + @"to value2"); + + // Clear the entry and verify the key and value are empty strings + entry.Clear(); + STAssertFalse(entry.IsActive(), @"Key value clear did not clear object"); + STAssertEquals(strlen(entry.GetKey()), (size_t)0, @"Length of cleared key " + @"was not 0"); + STAssertEquals(strlen(entry.GetValue()), (size_t)0, @"Length of cleared " + @"value was not 0!"); +} + +- (void)testEmptyKeyValueCombos { + KeyValueEntry entry; + entry.SetKeyValue(NULL, NULL); + STAssertEqualCStrings(entry.GetKey(), "", @"Setting NULL key did not return " + @"empty key!"); + STAssertEqualCStrings(entry.GetValue(), "", @"Setting NULL value did not " + @"set empty string value!"); +} + + +//============================================================================== +- (void)testSimpleStringDictionary { + // Make a new dictionary + SimpleStringDictionary *dict = new SimpleStringDictionary(); + STAssertTrue(dict != NULL, nil); + + // try passing in NULL for key + //dict->SetKeyValue(NULL, "bad"); // causes assert() to fire + + // Set three distinct values on three keys + dict->SetKeyValue("key1", "value1"); + dict->SetKeyValue("key2", "value2"); + dict->SetKeyValue("key3", "value3"); + + STAssertTrue(!strcmp(dict->GetValueForKey("key1"), "value1"), nil); + STAssertTrue(!strcmp(dict->GetValueForKey("key2"), "value2"), nil); + STAssertTrue(!strcmp(dict->GetValueForKey("key3"), "value3"), nil); + STAssertEquals(dict->GetCount(), 3, @"GetCount did not return 3"); + // try an unknown key + STAssertTrue(dict->GetValueForKey("key4") == NULL, nil); + + // try a NULL key + //STAssertTrue(dict->GetValueForKey(NULL) == NULL, nil); // asserts + + // Remove a key + dict->RemoveKey("key3"); + + // Now make sure it's not there anymore + STAssertTrue(dict->GetValueForKey("key3") == NULL, nil); + + // Remove a NULL key + //dict->RemoveKey(NULL); // will cause assert() to fire + + // Remove by setting value to NULL + dict->SetKeyValue("key2", NULL); + + // Now make sure it's not there anymore + STAssertTrue(dict->GetValueForKey("key2") == NULL, nil); +} + +//============================================================================== +// The idea behind this test is to add a bunch of values to the dictionary, +// remove some in the middle, then add a few more in. We then create a +// SimpleStringDictionaryIterator and iterate through the dictionary, taking +// note of the key/value pairs we see. We then verify that it iterates +// through exactly the number of key/value pairs we expect, and that they +// match one-for-one with what we would expect. In all cases we're setting +// key value pairs of the form: +// +// key/value (like key0/value0, key17,value17, etc.) +// +- (void)testSimpleStringDictionaryIterator { + SimpleStringDictionary *dict = new SimpleStringDictionary(); + STAssertTrue(dict != NULL, nil); + + char key[KeyValueEntry::MAX_STRING_STORAGE_SIZE]; + char value[KeyValueEntry::MAX_STRING_STORAGE_SIZE]; + + const int kDictionaryCapacity = SimpleStringDictionary::MAX_NUM_ENTRIES; + const int kPartitionIndex = kDictionaryCapacity - 5; + + // We assume at least this size in the tests below + STAssertTrue(kDictionaryCapacity >= 64, nil); + + // We'll keep track of the number of key/value pairs we think should + // be in the dictionary + int expectedDictionarySize = 0; + + // Set a bunch of key/value pairs like key0/value0, key1/value1, ... + for (int i = 0; i < kPartitionIndex; ++i) { + sprintf(key, "key%d", i); + sprintf(value, "value%d", i); + dict->SetKeyValue(key, value); + } + expectedDictionarySize = kPartitionIndex; + + // set a couple of the keys twice (with the same value) - should be nop + dict->SetKeyValue("key2", "value2"); + dict->SetKeyValue("key4", "value4"); + dict->SetKeyValue("key15", "value15"); + + // Remove some random elements in the middle + dict->RemoveKey("key7"); + dict->RemoveKey("key18"); + dict->RemoveKey("key23"); + dict->RemoveKey("key31"); + expectedDictionarySize -= 4; // we just removed four key/value pairs + + // Set some more key/value pairs like key59/value59, key60/value60, ... + for (int i = kPartitionIndex; i < kDictionaryCapacity; ++i) { + sprintf(key, "key%d", i); + sprintf(value, "value%d", i); + dict->SetKeyValue(key, value); + } + expectedDictionarySize += kDictionaryCapacity - kPartitionIndex; + + // Now create an iterator on the dictionary + SimpleStringDictionaryIterator iter(*dict); + + // We then verify that it iterates through exactly the number of + // key/value pairs we expect, and that they match one-for-one with what we + // would expect. The ordering of the iteration does not matter... + + // used to keep track of number of occurrences found for key/value pairs + int count[kDictionaryCapacity]; + memset(count, 0, sizeof(count)); + + int totalCount = 0; + + const KeyValueEntry *entry; + + while ((entry = iter.Next())) { + totalCount++; + + // Extract keyNumber from a string of the form key + int keyNumber; + sscanf(entry->GetKey(), "key%d", &keyNumber); + + // Extract valueNumber from a string of the form value + int valueNumber; + sscanf(entry->GetValue(), "value%d", &valueNumber); + + // The value number should equal the key number since that's how we set them + STAssertTrue(keyNumber == valueNumber, nil); + + // Key and value numbers should be in proper range: + // 0 <= keyNumber < kDictionaryCapacity + bool isKeyInGoodRange = + (keyNumber >= 0 && keyNumber < kDictionaryCapacity); + bool isValueInGoodRange = + (valueNumber >= 0 && valueNumber < kDictionaryCapacity); + STAssertTrue(isKeyInGoodRange, nil); + STAssertTrue(isValueInGoodRange, nil); + + if (isKeyInGoodRange && isValueInGoodRange) { + ++count[keyNumber]; + } + } + + // Make sure each of the key/value pairs showed up exactly one time, except + // for the ones which we removed. + for (int i = 0; i < kDictionaryCapacity; ++i) { + // Skip over key7, key18, key23, and key31, since we removed them + if (!(i == 7 || i == 18 || i == 23 || i == 31)) { + STAssertTrue(count[i] == 1, nil); + } + } + + // Make sure the number of iterations matches the expected dictionary size. + STAssertTrue(totalCount == expectedDictionarySize, nil); +} + +@end diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/UnitTests-Info.plist firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/UnitTests-Info.plist --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/UnitTests-Info.plist 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/mac/UnitTests-Info.plist 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.yourcompany.${PRODUCT_NAME:identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/minidump_file_writer.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/minidump_file_writer.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/minidump_file_writer.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/minidump_file_writer.cc 2010-04-16 17:32:47.000000000 +0100 @@ -37,6 +37,8 @@ #include #include +#include "common/linux/linux_syscall_support.h" +#include "common/linux/linux_libc_support.h" #include "client/minidump_file_writer-inl.h" #include "common/string_conversion.h" @@ -53,7 +55,11 @@ bool MinidumpFileWriter::Open(const char *path) { assert(file_ == -1); +#if __linux__ + file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); +#else file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); +#endif return file_ != -1; } @@ -63,7 +69,11 @@ if (file_ != -1) { ftruncate(file_, position_); +#if __linux__ + result = (sys_close(file_) == 0); +#else result = (close(file_) == 0); +#endif file_ = -1; } @@ -227,9 +237,16 @@ return false; // Seek and write the data - if (lseek(file_, position, SEEK_SET) == static_cast(position)) - if (write(file_, src, size) == size) +#if __linux__ + if (sys_lseek(file_, position, SEEK_SET) == static_cast(position)) { + if (sys_write(file_, src, size) == size) { +#else + if (lseek(file_, position, SEEK_SET) == static_cast(position)) { + if (write(file_, src, size) == size) { +#endif return true; + } + } return false; } diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/minidump_file_writer_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/minidump_file_writer_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/minidump_file_writer_unittest.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/minidump_file_writer_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -126,17 +126,22 @@ 0x00000007, 0x06000007, 0x00000008, 0x07000008, 0x00000009, 0x08000009, 0x0000000a, 0x0900000a, 0x0000000b, 0x00000000 #else - 0x0000beef, 0x0000001e, 0x00000018, 0x00000020, 0x00000038, 0x00000000, - 0x00000018, 0x00690046, 0x00730072, 0x00200074, 0x00740053, 0x00690072, - 0x0067006e, 0x00000000, 0x0000001a, 0x00650053, 0x006f0063, 0x0064006e, - 0x00530020, 0x00720074, 0x006e0069, 0x00000067, 0x0001da00, 0x00000002, - 0x0002da01, 0x00000003, 0x0003da02, 0x00000004, 0x0004da03, 0x00000005, - 0x0005da04, 0x00000006, 0x0006da05, 0x00000007, 0x0007da06, 0x00000008, - 0x0008da07, 0x00000009, 0x0009da08, 0x0000000a, 0x000ada09, 0x0000000b, - 0x0000000a, 0x00018700, 0x00000002, 0x00028701, 0x00000003, 0x00038702, - 0x00000004, 0x00048703, 0x00000005, 0x00058704, 0x00000006, 0x00068705, - 0x00000007, 0x00078706, 0x00000008, 0x00088707, 0x00000009, 0x00098708, - 0x0000000a, 0x000a8709, 0x0000000b, 0x00000000, + 0x0000beef, 0x0000001e, 0x00000018, 0x00000020, + 0x00000038, 0x00000000, 0x00000018, 0x00690046, + 0x00730072, 0x00200074, 0x00740053, 0x00690072, + 0x0067006e, 0x00000000, 0x0000001a, 0x00650053, + 0x006f0063, 0x0064006e, 0x00530020, 0x00720074, + 0x006e0069, 0x00000067, 0x00011e00, 0x00000002, + 0x00021e01, 0x00000003, 0x00031e02, 0x00000004, + 0x00041e03, 0x00000005, 0x00051e04, 0x00000006, + 0x00061e05, 0x00000007, 0x00071e06, 0x00000008, + 0x00081e07, 0x00000009, 0x00091e08, 0x0000000a, + 0x000a1e09, 0x0000000b, 0x0000000a, 0x00011c00, + 0x00000002, 0x00021c01, 0x00000003, 0x00031c02, + 0x00000004, 0x00041c03, 0x00000005, 0x00051c04, + 0x00000006, 0x00061c05, 0x00000007, 0x00071c06, + 0x00000008, 0x00081c07, 0x00000009, 0x00091c08, + 0x0000000a, 0x000a1c09, 0x0000000b, 0x00000000, #endif }; unsigned int expected_byte_count = sizeof(expected); @@ -156,7 +161,6 @@ printf("%d\n",b1 - (char*)buffer); - ASSERT_EQ(memcmp(buffer, expected, expected_byte_count), 0); return true; } diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/breakpad_client.sln firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/breakpad_client.sln --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/breakpad_client.sln 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/breakpad_client.sln 2010-04-16 17:32:47.000000000 +0100 @@ -10,6 +10,8 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "crash_generation", "crash_generation\crash_generation.vcproj", "{A820AF62-6239-4693-8430-4F516C1838F4}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "exception_handler_test", "handler\exception_handler_test\exception_handler_test.vcproj", "{89094A11-CF25-4037-AF43-EACFA751405E}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 @@ -42,6 +44,14 @@ {A820AF62-6239-4693-8430-4F516C1838F4}.Release|Win32.Build.0 = Release|Win32 {A820AF62-6239-4693-8430-4F516C1838F4}.ReleaseStaticCRT|Win32.ActiveCfg = ReleaseStaticCRT|Win32 {A820AF62-6239-4693-8430-4F516C1838F4}.ReleaseStaticCRT|Win32.Build.0 = ReleaseStaticCRT|Win32 + {89094A11-CF25-4037-AF43-EACFA751405E}.Debug|Win32.ActiveCfg = Debug|Win32 + {89094A11-CF25-4037-AF43-EACFA751405E}.Debug|Win32.Build.0 = Debug|Win32 + {89094A11-CF25-4037-AF43-EACFA751405E}.DebugStaticCRT|Win32.ActiveCfg = Debug|Win32 + {89094A11-CF25-4037-AF43-EACFA751405E}.DebugStaticCRT|Win32.Build.0 = Debug|Win32 + {89094A11-CF25-4037-AF43-EACFA751405E}.Release|Win32.ActiveCfg = Release|Win32 + {89094A11-CF25-4037-AF43-EACFA751405E}.Release|Win32.Build.0 = Release|Win32 + {89094A11-CF25-4037-AF43-EACFA751405E}.ReleaseStaticCRT|Win32.ActiveCfg = Release|Win32 + {89094A11-CF25-4037-AF43-EACFA751405E}.ReleaseStaticCRT|Win32.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/common/ipc_protocol.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/common/ipc_protocol.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/common/ipc_protocol.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/common/ipc_protocol.h 2010-04-16 17:32:47.000000000 +0100 @@ -95,7 +95,7 @@ struct CustomClientInfo { const CustomInfoEntry* entries; - int count; + size_t count; }; // Message structure for IPC between crash client and crash server. diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.cc 2010-04-16 17:32:47.000000000 +0100 @@ -129,6 +129,19 @@ // Indicate to existing threads that server is shutting down. shutting_down_ = true; + // Even if there are no current worker threads running, it is possible that + // an I/O request is pending on the pipe right now but not yet done. In fact, + // it's very likely this is the case unless we are in an ERROR state. If we + // don't wait for the pending I/O to be done, then when the I/O completes, + // it may write to invalid memory. AppVerifier will flag this problem too. + // So we disconnect from the pipe and then wait for the server to get into + // error state so that the pending I/O will fail and get cleared. + DisconnectNamedPipe(pipe_); + int num_tries = 100; + while (num_tries-- && server_state_ != IPC_SERVER_STATE_ERROR) { + Sleep(10); + } + // Unregister wait on the pipe. if (pipe_wait_handle_) { // Wait for already executing callbacks to finish. @@ -205,12 +218,14 @@ } // Register a callback with the thread pool for the client connection. - RegisterWaitForSingleObject(&pipe_wait_handle_, - overlapped_.hEvent, - OnPipeConnected, - this, - INFINITE, - kPipeIOThreadFlags); + if (!RegisterWaitForSingleObject(&pipe_wait_handle_, + overlapped_.hEvent, + OnPipeConnected, + this, + INFINITE, + kPipeIOThreadFlags)) { + return false; + } pipe_ = CreateNamedPipe(pipe_name_.c_str(), kPipeAttr, @@ -629,6 +644,14 @@ // implements the state machine described in ReadMe.txt along with the // helper methods HandleXXXState. void CrashGenerationServer::HandleConnectionRequest() { + // If we are shutting doen then get into ERROR state, reset the event so more + // workers don't run and return immediately. + if (shutting_down_) { + server_state_ = IPC_SERVER_STATE_ERROR; + ResetEvent(overlapped_.hEvent); + return; + } + switch (server_state_) { case IPC_SERVER_STATE_ERROR: HandleErrorState(); diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/crash_generation_server.h 2010-04-16 17:32:47.000000000 +0100 @@ -241,7 +241,7 @@ // Note that since we restrict the pipe to one instance, we // only need to keep one state of the server. Otherwise, server // would have one state per client it is talking to. - IPCServerState server_state_; + volatile IPCServerState server_state_; // Whether the server is shutting down. volatile bool shutting_down_; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/Makefile.in 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/Makefile.in 2010-04-16 17:32:47.000000000 +0100 @@ -48,10 +48,11 @@ LOCAL_INCLUDES = -I$(topsrcdir)/toolkit/crashreporter/google-breakpad/src DEFINES += -DUNICODE -D_UNICODE -#XXX: We're not currently building the other parts, -# which would only be needed on the server side of the equation. CPPSRCS = \ + client_info.cc \ crash_generation_client.cc \ + crash_generation_server.cc \ + minidump_generator.cc \ $(NULL) # need static lib diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc 2010-04-16 17:32:47.000000000 +0100 @@ -28,6 +28,8 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include +#include #include #include @@ -38,14 +40,98 @@ #include "client/windows/handler/exception_handler.h" #include "common/windows/guid_string.h" +namespace { + +// Helper for GetProcId() +bool GetProcIdViaGetProcessId(HANDLE process, DWORD* id) { + // Dynamically get a pointer to GetProcessId(). + typedef DWORD (WINAPI *GetProcessIdFunction)(HANDLE); + static GetProcessIdFunction GetProcessIdPtr = NULL; + static bool initialize_get_process_id = true; + if (initialize_get_process_id) { + initialize_get_process_id = false; + HMODULE kernel32_handle = GetModuleHandle(L"kernel32.dll"); + if (!kernel32_handle) { + return false; + } + GetProcessIdPtr = reinterpret_cast(GetProcAddress( + kernel32_handle, "GetProcessId")); + } + if (!GetProcessIdPtr) + return false; + // Ask for the process ID. + *id = (*GetProcessIdPtr)(process); + return true; +} + +// Helper for GetProcId() +bool GetProcIdViaNtQueryInformationProcess(HANDLE process, DWORD* id) { + // Dynamically get a pointer to NtQueryInformationProcess(). + typedef NTSTATUS (WINAPI *NtQueryInformationProcessFunction)( + HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG); + static NtQueryInformationProcessFunction NtQueryInformationProcessPtr = NULL; + static bool initialize_query_information_process = true; + if (initialize_query_information_process) { + initialize_query_information_process = false; + // According to nsylvain, ntdll.dll is guaranteed to be loaded, even though + // the Windows docs seem to imply that you should LoadLibrary() it. + HMODULE ntdll_handle = GetModuleHandle(L"ntdll.dll"); + if (!ntdll_handle) { + return false; + } + NtQueryInformationProcessPtr = + reinterpret_cast(GetProcAddress( + ntdll_handle, "NtQueryInformationProcess")); + } + if (!NtQueryInformationProcessPtr) + return false; + // Ask for the process ID. + PROCESS_BASIC_INFORMATION info; + ULONG bytes_returned; + NTSTATUS status = (*NtQueryInformationProcessPtr)(process, + ProcessBasicInformation, + &info, sizeof info, + &bytes_returned); + if (!SUCCEEDED(status) || (bytes_returned != (sizeof info))) + return false; + + *id = static_cast(info.UniqueProcessId); + return true; +} + +DWORD GetProcId(HANDLE process) { + // Get a handle to |process| that has PROCESS_QUERY_INFORMATION rights. + HANDLE current_process = GetCurrentProcess(); + HANDLE process_with_query_rights; + if (DuplicateHandle(current_process, process, current_process, + &process_with_query_rights, PROCESS_QUERY_INFORMATION, + false, 0)) { + // Try to use GetProcessId(), if it exists. Fall back on + // NtQueryInformationProcess() otherwise (< Win XP SP1). + DWORD id; + bool success = + GetProcIdViaGetProcessId(process_with_query_rights, &id) || + GetProcIdViaNtQueryInformationProcess(process_with_query_rights, &id); + CloseHandle(process_with_query_rights); + if (success) + return id; + } + + // We're screwed. + return 0; +} + +} // namespace + namespace google_breakpad { +static const int kWaitForHandlerThreadMs = 60000; static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024; vector* ExceptionHandler::handler_stack_ = NULL; LONG ExceptionHandler::handler_stack_index_ = 0; CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_; -bool ExceptionHandler::handler_stack_critical_section_initialized_ = false; +volatile LONG ExceptionHandler::instance_count_ = 0; ExceptionHandler::ExceptionHandler(const wstring& dump_path, FilterCallback filter, @@ -88,6 +174,7 @@ MINIDUMP_TYPE dump_type, const wchar_t* pipe_name, const CustomClientInfo* custom_info) { + LONG instance_count = InterlockedIncrement(&instance_count_); filter_ = filter; callback_ = callback; callback_context_ = callback_context; @@ -106,6 +193,7 @@ #endif // _MSC_VER >= 1400 previous_pch_ = NULL; handler_thread_ = NULL; + is_shutdown_ = false; handler_start_semaphore_ = NULL; handler_finish_semaphore_ = NULL; requesting_thread_id_ = 0; @@ -177,12 +265,22 @@ set_dump_path(dump_path); } - if (handler_types != HANDLER_NONE) { - if (!handler_stack_critical_section_initialized_) { - InitializeCriticalSection(&handler_stack_critical_section_); - handler_stack_critical_section_initialized_ = true; - } + // There is a race condition here. If the first instance has not yet + // initialized the critical section, the second (and later) instances may + // try to use uninitialized critical section object. The feature of multiple + // instances in one module is not used much, so leave it as is for now. + // One way to solve this in the current design (that is, keeping the static + // handler stack) is to use spin locks with volatile bools to synchronize + // the handler stack. This works only if the compiler guarantees to generate + // cache coherent code for volatile. + // TODO(munjal): Fix this in a better way by changing the design if possible. + + // Lazy initialization of the handler_stack_critical_section_ + if (instance_count == 1) { + InitializeCriticalSection(&handler_stack_critical_section_); + } + if (handler_types != HANDLER_NONE) { EnterCriticalSection(&handler_stack_critical_section_); // The first time an ExceptionHandler that installs a handler is @@ -236,12 +334,12 @@ // TODO(mmentovai): use advapi32!ReportEvent to log the warning to the // system's application event log. fprintf(stderr, "warning: removing Breakpad handler out of order\n"); - for (vector::iterator iterator = - handler_stack_->begin(); - iterator != handler_stack_->end(); - ++iterator) { + vector::iterator iterator = handler_stack_->begin(); + while (iterator != handler_stack_->end()) { if (*iterator == this) { - handler_stack_->erase(iterator); + iterator = handler_stack_->erase(iterator); + } else { + ++iterator; } } } @@ -259,12 +357,36 @@ // Some of the objects were only initialized if out of process // registration was not done. if (!IsOutOfProcess()) { - // Clean up the handler thread and synchronization primitives. +#ifdef BREAKPAD_NO_TERMINATE_THREAD + // Clean up the handler thread and synchronization primitives. The handler + // thread is either waiting on the semaphore to handle a crash or it is + // handling a crash. Coming out of the wait is fast but wait more in the + // eventuality a crash is handled. This compilation option results in a + // deadlock if the exception handler is destroyed while executing code + // inside DllMain. + is_shutdown_ = true; + ReleaseSemaphore(handler_start_semaphore_, 1, NULL); + WaitForSingleObject(handler_thread_, kWaitForHandlerThreadMs); +#else TerminateThread(handler_thread_, 1); +#endif // BREAKPAD_NO_TERMINATE_THREAD + + CloseHandle(handler_thread_); + handler_thread_ = NULL; DeleteCriticalSection(&handler_critical_section_); CloseHandle(handler_start_semaphore_); CloseHandle(handler_finish_semaphore_); } + + // There is a race condition in the code below: if this instance is + // deleting the static critical section and a new instance of the class + // is created, then there is a possibility that the critical section be + // initialized while the same critical section is being deleted. Given the + // usage pattern for the code, this race condition is unlikely to hit, but it + // is a race condition nonetheless. + if (InterlockedDecrement(&instance_count_) == 0) { + DeleteCriticalSection(&handler_stack_critical_section_); + } } // static @@ -278,16 +400,23 @@ if (WaitForSingleObject(self->handler_start_semaphore_, INFINITE) == WAIT_OBJECT_0) { // Perform the requested action. - self->handler_return_value_ = self->WriteMinidumpWithException( - self->requesting_thread_id_, self->exception_info_, self->assertion_); + if (self->is_shutdown_) { + // The instance of the exception handler is being destroyed. + break; + } else { + self->handler_return_value_ = + self->WriteMinidumpWithException(self->requesting_thread_id_, + self->exception_info_, + self->assertion_); + } // Allow the requesting thread to proceed. ReleaseSemaphore(self->handler_finish_semaphore_, 1, NULL); } } - // Not reached. This thread will be terminated by ExceptionHandler's - // destructor. + // This statement is not reached when the thread is unconditionally + // terminated by the ExceptionHandler destructor. return 0; } @@ -588,6 +717,96 @@ return handler.WriteMinidump(); } +// static +bool ExceptionHandler::WriteMinidump(const wstring &dump_path, + bool write_exception_stream, + MinidumpCallback callback, + void* callback_context) { + EXCEPTION_RECORD ex; + CONTEXT ctx; + EXCEPTION_POINTERS exinfo = { NULL, NULL }; + + if (write_exception_stream) { + // MSDN says that GetThreadContext(currentThread) doesn't return a + // valid context, so we just fill in the crash address so as to + // get a signature + bool (*signature) (const wstring&, bool, MinidumpCallback, void*) = + &ExceptionHandler::WriteMinidump; + + memset(&ex, 0, sizeof(ex)); + ex.ExceptionCode = EXCEPTION_BREAKPOINT; + ex.ExceptionAddress = reinterpret_cast(signature); + memset(&ctx, 0, sizeof(ctx)); + + exinfo.ExceptionRecord = &ex; + exinfo.ContextRecord = &ctx; + } + + ExceptionHandler handler(dump_path, NULL, callback, callback_context, + HANDLER_NONE); + return handler.WriteMinidumpForException(exinfo.ExceptionRecord ? + &exinfo : NULL); +} + +// static +bool ExceptionHandler::WriteMinidumpForChild(HANDLE child, + DWORD child_blamed_thread, + const wstring &dump_path, + MinidumpCallback callback, + void *callback_context) { + DWORD childId = GetProcId(child); + if (0 == childId) + return false; + + EXCEPTION_RECORD ex; + CONTEXT ctx; + EXCEPTION_POINTERS exinfo = { NULL, NULL }; + DWORD last_suspend_cnt = -1; + HANDLE child_thread_handle = OpenThread(THREAD_GET_CONTEXT | + THREAD_QUERY_INFORMATION | + THREAD_SUSPEND_RESUME, + FALSE, + child_blamed_thread); + // this thread may have died already, so not opening the handle is a + // non-fatal error + if (NULL != child_thread_handle) { + if (0 <= (last_suspend_cnt = SuspendThread(child_thread_handle))) { + ctx.ContextFlags = CONTEXT_ALL; + if (GetThreadContext(child_thread_handle, &ctx)) { + memset(&ex, 0, sizeof(ex)); + ex.ExceptionCode = EXCEPTION_BREAKPOINT; +#if defined(_M_IX86) + ex.ExceptionAddress = reinterpret_cast(ctx.Eip); +#elif defined(_M_X64) + ex.ExceptionAddress = reinterpret_cast(ctx.Rip); +#endif + exinfo.ExceptionRecord = &ex; + exinfo.ContextRecord = &ctx; + } + } + } + + ExceptionHandler handler(dump_path, NULL, callback, callback_context, + HANDLER_NONE); + bool success = handler.WriteMinidumpWithExceptionForProcess( + child_blamed_thread, + exinfo.ExceptionRecord ? &exinfo : NULL, + NULL, child, childId, false); + + if (0 <= last_suspend_cnt) { + ResumeThread(child_thread_handle); + } + + CloseHandle(child_thread_handle); + + if (callback) { + success = callback(handler.dump_path_c_, handler.next_minidump_id_c_, + callback_context, NULL, NULL, success); + } + + return success; +} + bool ExceptionHandler::WriteMinidumpWithException( DWORD requesting_thread_id, EXCEPTION_POINTERS* exinfo, @@ -612,61 +831,12 @@ success = crash_generation_client_->RequestDump(assertion); } } else { - if (minidump_write_dump_) { - HANDLE dump_file = CreateFile(next_minidump_path_c_, - GENERIC_WRITE, - 0, // no sharing - NULL, - CREATE_NEW, // fail if exists - FILE_ATTRIBUTE_NORMAL, - NULL); - if (dump_file != INVALID_HANDLE_VALUE) { - MINIDUMP_EXCEPTION_INFORMATION except_info; - except_info.ThreadId = requesting_thread_id; - except_info.ExceptionPointers = exinfo; - except_info.ClientPointers = FALSE; - - // Add an MDRawBreakpadInfo stream to the minidump, to provide additional - // information about the exception handler to the Breakpad processor. The - // information will help the processor determine which threads are - // relevant. The Breakpad processor does not require this information but - // can function better with Breakpad-generated dumps when it is present. - // The native debugger is not harmed by the presence of this information. - MDRawBreakpadInfo breakpad_info; - breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | - MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; - breakpad_info.dump_thread_id = GetCurrentThreadId(); - breakpad_info.requesting_thread_id = requesting_thread_id; - - // Leave room in user_stream_array for a possible assertion info stream. - MINIDUMP_USER_STREAM user_stream_array[2]; - user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM; - user_stream_array[0].BufferSize = sizeof(breakpad_info); - user_stream_array[0].Buffer = &breakpad_info; - - MINIDUMP_USER_STREAM_INFORMATION user_streams; - user_streams.UserStreamCount = 1; - user_streams.UserStreamArray = user_stream_array; - - if (assertion) { - user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM; - user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo); - user_stream_array[1].Buffer = assertion; - ++user_streams.UserStreamCount; - } - - // The explicit comparison to TRUE avoids a warning (C4800). - success = (minidump_write_dump_(GetCurrentProcess(), - GetCurrentProcessId(), - dump_file, - dump_type_, - exinfo ? &except_info : NULL, - &user_streams, - NULL) == TRUE); - - CloseHandle(dump_file); - } - } + success = WriteMinidumpWithExceptionForProcess(requesting_thread_id, + exinfo, + assertion, + GetCurrentProcess(), + GetCurrentProcessId(), + true); } if (callback_) { @@ -681,6 +851,81 @@ return success; } +bool ExceptionHandler::WriteMinidumpWithExceptionForProcess( + DWORD requesting_thread_id, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + HANDLE process, + DWORD processId, + bool write_requester_stream) { + bool success = false; + if (minidump_write_dump_) { + HANDLE dump_file = CreateFile(next_minidump_path_c_, + GENERIC_WRITE, + 0, // no sharing + NULL, + CREATE_NEW, // fail if exists + FILE_ATTRIBUTE_NORMAL, + NULL); + if (dump_file != INVALID_HANDLE_VALUE) { + MINIDUMP_EXCEPTION_INFORMATION except_info; + except_info.ThreadId = requesting_thread_id; + except_info.ExceptionPointers = exinfo; + except_info.ClientPointers = FALSE; + + // Leave room in user_stream_array for possible breakpad and + // assertion info streams. + MINIDUMP_USER_STREAM user_stream_array[2]; + MINIDUMP_USER_STREAM_INFORMATION user_streams; + user_streams.UserStreamCount = 0; + user_streams.UserStreamArray = user_stream_array; + + if (write_requester_stream) { + // Add an MDRawBreakpadInfo stream to the minidump, to provide + // additional information about the exception handler to the + // Breakpad processor. The information will help the + // processor determine which threads are relevant. The + // Breakpad processor does not require this information but + // can function better with Breakpad-generated dumps when it + // is present. The native debugger is not harmed by the + // presence of this information. + MDRawBreakpadInfo breakpad_info; + breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | + MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; + breakpad_info.dump_thread_id = GetCurrentThreadId(); + breakpad_info.requesting_thread_id = requesting_thread_id; + + int idx = user_streams.UserStreamCount; + user_stream_array[idx].Type = MD_BREAKPAD_INFO_STREAM; + user_stream_array[idx].BufferSize = sizeof(breakpad_info); + user_stream_array[idx].Buffer = &breakpad_info; + ++user_streams.UserStreamCount; + } + + if (assertion) { + int idx = user_streams.UserStreamCount; + user_stream_array[idx].Type = MD_ASSERTION_INFO_STREAM; + user_stream_array[idx].BufferSize = sizeof(MDRawAssertionInfo); + user_stream_array[idx].Buffer = assertion; + ++user_streams.UserStreamCount; + } + + // The explicit comparison to TRUE avoids a warning (C4800). + success = (minidump_write_dump_(process, + processId, + dump_file, + dump_type_, + exinfo ? &except_info : NULL, + &user_streams, + NULL) == TRUE); + + CloseHandle(dump_file); + } + } + + return success; +} + void ExceptionHandler::UpdateNextID() { assert(uuid_create_); UUID id = {0}; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.h 2010-04-16 17:32:47.000000000 +0100 @@ -190,6 +190,23 @@ static bool WriteMinidump(const wstring &dump_path, MinidumpCallback callback, void* callback_context); + // Variant of WriteMinidump() above that optionally allows writing + // an artificial exception stream in the minidump. + static bool WriteMinidump(const wstring &dump_path, + bool write_exception_stream, + MinidumpCallback callback, void* callback_context); + + // Write a minidump of |child| immediately. This can be used to + // capture the execution state of |child| independently of a crash. + // Pass a meaningful |child_blamed_thread| to make that thread in + // the child process the one from which a crash signature is + // extracted. + static bool WriteMinidumpForChild(HANDLE child, + DWORD child_blamed_thread, + const wstring &dump_path, + MinidumpCallback callback, + void *callback_context); + // Get the thread ID of the thread requesting the dump (either the exception // thread or any other thread that called WriteMinidump directly). This // may be useful if you want to include additional thread state in your @@ -268,8 +285,9 @@ bool WriteMinidumpOnHandlerThread(EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion); - // This function does the actual writing of a minidump. It is called - // on the handler thread. requesting_thread_id is the ID of the thread + // This function is called on the handler thread. It calls into + // WriteMinidumpWithExceptionForProcess() with a handle to the + // current process. requesting_thread_id is the ID of the thread // that requested the dump. If the dump is requested as a result of // an exception, exinfo contains exception information, otherwise, // it is NULL. @@ -277,6 +295,21 @@ EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion); + // This function does the actual writing of a minidump. It is + // called on the handler thread. requesting_thread_id is the ID of + // the thread that requested the dump, if that information is + // meaningful. If the dump is requested as a result of an + // exception, exinfo contains exception information, otherwise, it + // is NULL. process is the one that will be dumped. If + // requesting_thread_id is meaningful and should be added to the + // minidump, write_requester_stream is |true|. + bool WriteMinidumpWithExceptionForProcess(DWORD requesting_thread_id, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + HANDLE process, + DWORD processId, + bool write_requester_stream); + // Generates a new ID and stores it in next_minidump_id_, and stores the // path of the next minidump to be written in next_minidump_path_. void UpdateNextID(); @@ -340,6 +373,12 @@ // The exception handler thread. HANDLE handler_thread_; + // True if the exception handler is being destroyed. + // Starting with MSVC 2005, Visual C has stronger guarantees on volatile vars. + // It has release semantics on write and acquire semantics on reads. + // See the msdn documentation. + volatile bool is_shutdown_; + // The critical section enforcing the requirement that only one exception be // handled by a handler at a time. CRITICAL_SECTION handler_critical_section_; @@ -390,11 +429,12 @@ static LONG handler_stack_index_; // handler_stack_critical_section_ guards operations on handler_stack_ and - // handler_stack_index_. + // handler_stack_index_. The critical section is initialized by the + // first instance of the class and destroyed by the last instance of it. static CRITICAL_SECTION handler_stack_critical_section_; - // True when handler_stack_critical_section_ has been initialized. - static bool handler_stack_critical_section_initialized_; + // The number of instances of this class. + volatile static LONG instance_count_; // disallow copy ctor and operator= explicit ExceptionHandler(const ExceptionHandler &); diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,164 @@ +// Copyright 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "breakpad_googletest_includes.h" +#include "client/windows/crash_generation/crash_generation_server.h" +#include "client/windows/handler/exception_handler.h" +#include +#include +#include +#include +#include + +namespace { +const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer"; +const char kSuccessIndicator[] = "success"; +const char kFailureIndicator[] = "failure"; + +// Utility function to test for a path's existence. +BOOL DoesPathExist(const TCHAR *path_name); + +class ExceptionHandlerDeathTest : public ::testing::Test { +protected: + // Member variable for each test that they can use + // for temporary storage. + TCHAR temp_path_[MAX_PATH]; + // Actually constructs a temp path name. + virtual void SetUp(); + // A helper method that tests can use to crash. + void DoCrash(); +}; + +void ExceptionHandlerDeathTest::SetUp() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + TCHAR temp_path[MAX_PATH] = { '\0' }; + TCHAR test_name_wide[MAX_PATH] = { '\0' }; + // We want the temporary directory to be what the OS returns + // to us, + the test case name. + GetTempPath(MAX_PATH, temp_path); + // THe test case name is exposed to use as a c-style string, + // But we might be working in UNICODE here on Windows. + int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(), + (int)strlen(test_info->name()), test_name_wide, MAX_PATH); + if (!dwRet) { + assert(false); + } + StringCchPrintfW(temp_path_, MAX_PATH, L"%s%s", temp_path, test_name_wide); + CreateDirectory(temp_path_, NULL); +} + +BOOL DoesPathExist(const TCHAR *path_name) { + DWORD flags = GetFileAttributes(path_name); + if (flags == INVALID_FILE_ATTRIBUTES) { + return FALSE; + } + return TRUE; +} + +bool MinidumpWrittenCallback(const wchar_t* dump_path, + const wchar_t* minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + bool succeeded) { + if (succeeded && DoesPathExist(dump_path)) { + fprintf(stderr, kSuccessIndicator); + } else { + fprintf(stderr, kFailureIndicator); + } + // If we don't flush, the output doesn't get sent before + // this process dies. + fflush(stderr); + return succeeded; +} + +TEST_F(ExceptionHandlerDeathTest, InProcTest) { + // For the in-proc test, we just need to instantiate an exception + // handler in in-proc mode, and crash. Since the entire test is + // reexecuted in the child process, we don't have to worry about + // the semantics of the exception handler being inherited/not + // inherited across CreateProcess(). + ASSERT_TRUE(DoesPathExist(temp_path_)); + google_breakpad::ExceptionHandler *exc = + new google_breakpad::ExceptionHandler( + temp_path_, NULL, &MinidumpWrittenCallback, NULL, + google_breakpad::ExceptionHandler::HANDLER_ALL); + int *i = NULL; + ASSERT_DEATH((*i)++, kSuccessIndicator); + delete exc; +} + +static bool gDumpCallbackCalled = false; + +void clientDumpCallback(void *dump_context, + const google_breakpad::ClientInfo *client_info, + const std::wstring *dump_path){ + + gDumpCallbackCalled = true; +} + +void ExceptionHandlerDeathTest::DoCrash() { + google_breakpad::ExceptionHandler *exc = + new google_breakpad::ExceptionHandler( + temp_path_, NULL, NULL, NULL, + google_breakpad::ExceptionHandler::HANDLER_ALL, MiniDumpNormal, kPipeName, + NULL); + // Although this is executing in the child process of the death test, + // if it's not true we'll still get an error rather than the crash + // being expected. + ASSERT_TRUE(exc->IsOutOfProcess()); + int *i = NULL; + printf("%d\n", (*i)++); +} + +TEST_F(ExceptionHandlerDeathTest, OutOfProcTest) { + // We can take advantage of a detail of google test here to save some + // complexity in testing: when you do a death test, it actually forks. + // So we can make the main test harness the crash generation server, + // and call ASSERT_DEATH on a NULL dereference, it to expecting test + // the out of process scenario, since it's happening in a different + // process! This is different from the above because, above, we pass + // a NULL pipe name, and we also don't start a crash generation server. + + ASSERT_TRUE(DoesPathExist(temp_path_)); + std::wstring dump_path(temp_path_); + google_breakpad::CrashGenerationServer server( + kPipeName, NULL, NULL, NULL, &clientDumpCallback, NULL, NULL, NULL, true, + &dump_path); + + // This HAS to be EXPECT_, because when this test case is executed in the + // child process, the server registration will fail due to the named pipe + // being the same. + EXPECT_TRUE(server.Start()); + EXPECT_FALSE(gDumpCallbackCalled); + ASSERT_DEATH(this->DoCrash(), ""); + EXPECT_TRUE(gDumpCallbackCalled); +} +} \ No newline at end of file diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.vcproj firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.vcproj --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.vcproj 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler_test/exception_handler_test.vcproj 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,266 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/Makefile.in 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/handler/Makefile.in 2010-04-16 17:32:47.000000000 +0100 @@ -46,7 +46,7 @@ XPI_NAME = crashreporter LOCAL_INCLUDES = -I$(srcdir)/../../.. -DEFINES += -DUNICODE -D_UNICODE +DEFINES += -DUNICODE -D_UNICODE -DBREAKPAD_NO_TERMINATE_THREAD CPPSRCS = \ exception_handler.cc \ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/sender/crash_report_sender.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/sender/crash_report_sender.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/sender/crash_report_sender.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/sender/crash_report_sender.cc 2010-04-16 17:32:47.000000000 +0100 @@ -69,7 +69,7 @@ int http_response = 0; bool result = HTTPUpload::SendRequest( - url, parameters, dump_file_name, L"upload_file_minidump", report_code, + url, parameters, dump_file_name, L"upload_file_minidump", NULL, report_code, &http_response); if (result) { diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/sender/crash_report_sender.vcproj firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/sender/crash_report_sender.vcproj --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/sender/crash_report_sender.vcproj 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/client/windows/sender/crash_report_sender.vcproj 2010-04-16 17:32:47.000000000 +0100 @@ -46,8 +46,8 @@ BasicRuntimeChecks="3" RuntimeLibrary="3" UsePrecompiledHeader="0" - WarningLevel="3" - Detect64BitPortabilityProblems="true" + WarningLevel="4" + Detect64BitPortabilityProblems="false" DebugInformationFormat="4" /> +#include + +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/bytereader.h" + +namespace dwarf2reader { + +ByteReader::ByteReader(enum Endianness endian) + :offset_reader_(NULL), address_reader_(NULL), endian_(endian), + address_size_(0), offset_size_(0), + have_section_base_(), have_text_base_(), have_data_base_(), + have_function_base_() { } + +ByteReader::~ByteReader() { } + +void ByteReader::SetOffsetSize(uint8 size) { + offset_size_ = size; + assert(size == 4 || size == 8); + if (size == 4) { + this->offset_reader_ = &ByteReader::ReadFourBytes; + } else { + this->offset_reader_ = &ByteReader::ReadEightBytes; + } +} + +void ByteReader::SetAddressSize(uint8 size) { + address_size_ = size; + assert(size == 4 || size == 8); + if (size == 4) { + this->address_reader_ = &ByteReader::ReadFourBytes; + } else { + this->address_reader_ = &ByteReader::ReadEightBytes; + } +} + +uint64 ByteReader::ReadInitialLength(const char* start, size_t* len) { + const uint64 initial_length = ReadFourBytes(start); + start += 4; + + // In DWARF2/3, if the initial length is all 1 bits, then the offset + // size is 8 and we need to read the next 8 bytes for the real length. + if (initial_length == 0xffffffff) { + SetOffsetSize(8); + *len = 12; + return ReadOffset(start); + } else { + SetOffsetSize(4); + *len = 4; + } + return initial_length; +} + +bool ByteReader::ValidEncoding(DwarfPointerEncoding encoding) const { + if (encoding == DW_EH_PE_omit) return true; + if (encoding == DW_EH_PE_aligned) return true; + if ((encoding & 0x7) > DW_EH_PE_udata8) + return false; + if ((encoding & 0x70) > DW_EH_PE_funcrel) + return false; + return true; +} + +bool ByteReader::UsableEncoding(DwarfPointerEncoding encoding) const { + switch (encoding & 0x70) { + case DW_EH_PE_absptr: return true; + case DW_EH_PE_pcrel: return have_section_base_; + case DW_EH_PE_textrel: return have_text_base_; + case DW_EH_PE_datarel: return have_data_base_; + case DW_EH_PE_funcrel: return have_function_base_; + default: return false; + } +} + +uint64 ByteReader::ReadEncodedPointer(const char *buffer, + DwarfPointerEncoding encoding, + size_t *len) const { + // UsableEncoding doesn't approve of DW_EH_PE_omit, so we shouldn't + // see it here. + assert(encoding != DW_EH_PE_omit); + + // The Linux Standards Base 4.0 does not make this clear, but the + // GNU tools (gcc/unwind-pe.h; readelf/dwarf.c; gdb/dwarf2-frame.c) + // agree that aligned pointers are always absolute, machine-sized, + // machine-signed pointers. + if (encoding == DW_EH_PE_aligned) { + assert(have_section_base_); + + // We don't need to align BUFFER in *our* address space. Rather, we + // need to find the next position in our buffer that would be aligned + // when the .eh_frame section the buffer contains is loaded into the + // program's memory. So align assuming that buffer_base_ gets loaded at + // address section_base_, where section_base_ itself may or may not be + // aligned. + + // First, find the offset to START from the closest prior aligned + // address. + size_t skew = section_base_ & (AddressSize() - 1); + // Now find the offset from that aligned address to buffer. + size_t offset = skew + (buffer - buffer_base_); + // Round up to the next boundary. + size_t aligned = (offset + AddressSize() - 1) & -AddressSize(); + // Convert back to a pointer. + const char *aligned_buffer = buffer_base_ + (aligned - skew); + // Finally, store the length and actually fetch the pointer. + *len = aligned_buffer - buffer + AddressSize(); + return ReadAddress(aligned_buffer); + } + + // Extract the value first, ignoring whether it's a pointer or an + // offset relative to some base. + uint64 offset; + switch (encoding & 0x0f) { + case DW_EH_PE_absptr: + // DW_EH_PE_absptr is weird, as it is used as a meaningful value for + // both the high and low nybble of encoding bytes. When it appears in + // the high nybble, it means that the pointer is absolute, not an + // offset from some base address. When it appears in the low nybble, + // as here, it means that the pointer is stored as a normal + // machine-sized and machine-signed address. A low nybble of + // DW_EH_PE_absptr does not imply that the pointer is absolute; it is + // correct for us to treat the value as an offset from a base address + // if the upper nybble is not DW_EH_PE_absptr. + offset = ReadAddress(buffer); + *len = AddressSize(); + break; + + case DW_EH_PE_uleb128: + offset = ReadUnsignedLEB128(buffer, len); + break; + + case DW_EH_PE_udata2: + offset = ReadTwoBytes(buffer); + *len = 2; + break; + + case DW_EH_PE_udata4: + offset = ReadFourBytes(buffer); + *len = 4; + break; + + case DW_EH_PE_udata8: + offset = ReadEightBytes(buffer); + *len = 8; + break; + + case DW_EH_PE_sleb128: + offset = ReadSignedLEB128(buffer, len); + break; + + case DW_EH_PE_sdata2: + offset = ReadTwoBytes(buffer); + // Sign-extend from 16 bits. + offset = (offset ^ 0x8000) - 0x8000; + *len = 2; + break; + + case DW_EH_PE_sdata4: + offset = ReadFourBytes(buffer); + // Sign-extend from 32 bits. + offset = (offset ^ 0x80000000ULL) - 0x80000000ULL; + *len = 4; + break; + + case DW_EH_PE_sdata8: + // No need to sign-extend; this is the full width of our type. + offset = ReadEightBytes(buffer); + *len = 8; + break; + + default: + abort(); + } + + // Find the appropriate base address. + uint64 base; + switch (encoding & 0x70) { + case DW_EH_PE_absptr: + base = 0; + break; + + case DW_EH_PE_pcrel: + assert(have_section_base_); + base = section_base_ + (buffer - buffer_base_); + break; + + case DW_EH_PE_textrel: + assert(have_text_base_); + base = text_base_; + break; + + case DW_EH_PE_datarel: + assert(have_data_base_); + base = data_base_; + break; + + case DW_EH_PE_funcrel: + assert(have_function_base_); + base = function_base_; + break; + + default: + abort(); + } + + uint64 pointer = base + offset; + + // Remove inappropriate upper bits. + if (AddressSize() == 4) + pointer = pointer & 0xffffffff; + else + assert(AddressSize() == sizeof(uint64)); + + return pointer; +} + +} // namespace dwarf2reader diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,310 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef COMMON_DWARF_BYTEREADER_H__ +#define COMMON_DWARF_BYTEREADER_H__ + +#include +#include "common/dwarf/types.h" +#include "common/dwarf/dwarf2enums.h" + +namespace dwarf2reader { + +// We can't use the obvious name of LITTLE_ENDIAN and BIG_ENDIAN +// because it conflicts with a macro +enum Endianness { + ENDIANNESS_BIG, + ENDIANNESS_LITTLE +}; + +// A ByteReader knows how to read single- and multi-byte values of +// various endiannesses, sizes, and encodings, as used in DWARF +// debugging information and Linux C++ exception handling data. +class ByteReader { + public: + // Construct a ByteReader capable of reading one-, two-, four-, and + // eight-byte values according to ENDIANNESS, absolute machine-sized + // addresses, DWARF-style "initial length" values, signed and + // unsigned LEB128 numbers, and Linux C++ exception handling data's + // encoded pointers. + explicit ByteReader(enum Endianness endianness); + virtual ~ByteReader(); + + // Read a single byte from BUFFER and return it as an unsigned 8 bit + // number. + uint8 ReadOneByte(const char* buffer) const; + + // Read two bytes from BUFFER and return them as an unsigned 16 bit + // number, using this ByteReader's endianness. + uint16 ReadTwoBytes(const char* buffer) const; + + // Read four bytes from BUFFER and return them as an unsigned 32 bit + // number, using this ByteReader's endianness. This function returns + // a uint64 so that it is compatible with ReadAddress and + // ReadOffset. The number it returns will never be outside the range + // of an unsigned 32 bit integer. + uint64 ReadFourBytes(const char* buffer) const; + + // Read eight bytes from BUFFER and return them as an unsigned 64 + // bit number, using this ByteReader's endianness. + uint64 ReadEightBytes(const char* buffer) const; + + // Read an unsigned LEB128 (Little Endian Base 128) number from + // BUFFER and return it as an unsigned 64 bit integer. Set LEN to + // the number of bytes read. + // + // The unsigned LEB128 representation of an integer N is a variable + // number of bytes: + // + // - If N is between 0 and 0x7f, then its unsigned LEB128 + // representation is a single byte whose value is N. + // + // - Otherwise, its unsigned LEB128 representation is (N & 0x7f) | + // 0x80, followed by the unsigned LEB128 representation of N / + // 128, rounded towards negative infinity. + // + // In other words, we break VALUE into groups of seven bits, put + // them in little-endian order, and then write them as eight-bit + // bytes with the high bit on all but the last. + uint64 ReadUnsignedLEB128(const char* buffer, size_t* len) const; + + // Read a signed LEB128 number from BUFFER and return it as an + // signed 64 bit integer. Set LEN to the number of bytes read. + // + // The signed LEB128 representation of an integer N is a variable + // number of bytes: + // + // - If N is between -0x40 and 0x3f, then its signed LEB128 + // representation is a single byte whose value is N in two's + // complement. + // + // - Otherwise, its signed LEB128 representation is (N & 0x7f) | + // 0x80, followed by the signed LEB128 representation of N / 128, + // rounded towards negative infinity. + // + // In other words, we break VALUE into groups of seven bits, put + // them in little-endian order, and then write them as eight-bit + // bytes with the high bit on all but the last. + int64 ReadSignedLEB128(const char* buffer, size_t* len) const; + + // Indicate that addresses on this architecture are SIZE bytes long. SIZE + // must be either 4 or 8. (DWARF allows addresses to be any number of + // bytes in length from 1 to 255, but we only support 32- and 64-bit + // addresses at the moment.) You must call this before using the + // ReadAddress member function. + // + // For data in a .debug_info section, or something that .debug_info + // refers to like line number or macro data, the compilation unit + // header's address_size field indicates the address size to use. Call + // frame information doesn't indicate its address size (a shortcoming of + // the spec); you must supply the appropriate size based on the + // architecture of the target machine. + void SetAddressSize(uint8 size); + + // Return the current address size, in bytes. This is either 4, + // indicating 32-bit addresses, or 8, indicating 64-bit addresses. + uint8 AddressSize() const { return address_size_; } + + // Read an address from BUFFER and return it as an unsigned 64 bit + // integer, respecting this ByteReader's endianness and address size. You + // must call SetAddressSize before calling this function. + uint64 ReadAddress(const char* buffer) const; + + // DWARF actually defines two slightly different formats: 32-bit DWARF + // and 64-bit DWARF. This is *not* related to the size of registers or + // addresses on the target machine; it refers only to the size of section + // offsets and data lengths appearing in the DWARF data. One only needs + // 64-bit DWARF when the debugging data itself is larger than 4GiB. + // 32-bit DWARF can handle x86_64 or PPC64 code just fine, unless the + // debugging data itself is very large. + // + // DWARF information identifies itself as 32-bit or 64-bit DWARF: each + // compilation unit and call frame information entry begins with an + // "initial length" field, which, in addition to giving the length of the + // data, also indicates the size of section offsets and lengths appearing + // in that data. The ReadInitialLength member function, below, reads an + // initial length and sets the ByteReader's offset size as a side effect. + // Thus, in the normal process of reading DWARF data, the appropriate + // offset size is set automatically. So, you should only need to call + // SetOffsetSize if you are using the same ByteReader to jump from the + // midst of one block of DWARF data into another. + + // Read a DWARF "initial length" field from START, and return it as + // an unsigned 64 bit integer, respecting this ByteReader's + // endianness. Set *LEN to the length of the initial length in + // bytes, either four or twelve. As a side effect, set this + // ByteReader's offset size to either 4 (if we see a 32-bit DWARF + // initial length) or 8 (if we see a 64-bit DWARF initial length). + // + // A DWARF initial length is either: + // + // - a byte count stored as an unsigned 32-bit value less than + // 0xffffff00, indicating that the data whose length is being + // measured uses the 32-bit DWARF format, or + // + // - The 32-bit value 0xffffffff, followed by a 64-bit byte count, + // indicating that the data whose length is being measured uses + // the 64-bit DWARF format. + uint64 ReadInitialLength(const char* start, size_t* len); + + // Read an offset from BUFFER and return it as an unsigned 64 bit + // integer, respecting the ByteReader's endianness. In 32-bit DWARF, the + // offset is 4 bytes long; in 64-bit DWARF, the offset is eight bytes + // long. You must call ReadInitialLength or SetOffsetSize before calling + // this function; see the comments above for details. + uint64 ReadOffset(const char* buffer) const; + + // Return the current offset size, in bytes. + // A return value of 4 indicates that we are reading 32-bit DWARF. + // A return value of 8 indicates that we are reading 64-bit DWARF. + uint8 OffsetSize() const { return offset_size_; } + + // Indicate that section offsets and lengths are SIZE bytes long. SIZE + // must be either 4 (meaning 32-bit DWARF) or 8 (meaning 64-bit DWARF). + // Usually, you should not call this function yourself; instead, let a + // call to ReadInitialLength establish the data's offset size + // automatically. + void SetOffsetSize(uint8 size); + + // The Linux C++ ABI uses a variant of DWARF call frame information + // for exception handling. This data is included in the program's + // address space as the ".eh_frame" section, and intepreted at + // runtime to walk the stack, find exception handlers, and run + // cleanup code. The format is mostly the same as DWARF CFI, with + // some adjustments made to provide the additional + // exception-handling data, and to make the data easier to work with + // in memory --- for example, to allow it to be placed in read-only + // memory even when describing position-independent code. + // + // In particular, exception handling data can select a number of + // different encodings for pointers that appear in the data, as + // described by the DwarfPointerEncoding enum. There are actually + // four axes(!) to the encoding: + // + // - The pointer size: pointers can be 2, 4, or 8 bytes long, or use + // the DWARF LEB128 encoding. + // + // - The pointer's signedness: pointers can be signed or unsigned. + // + // - The pointer's base address: the data stored in the exception + // handling data can be the actual address (that is, an absolute + // pointer), or relative to one of a number of different base + // addreses --- including that of the encoded pointer itself, for + // a form of "pc-relative" addressing. + // + // - The pointer may be indirect: it may be the address where the + // true pointer is stored. (This is used to refer to things via + // global offset table entries, program linkage table entries, or + // other tricks used in position-independent code.) + // + // There are also two options that fall outside that matrix + // altogether: the pointer may be omitted, or it may have padding to + // align it on an appropriate address boundary. (That last option + // may seem like it should be just another axis, but it is not.) + + // Indicate that the exception handling data is loaded starting at + // SECTION_BASE, and that the start of its buffer in our own memory + // is BUFFER_BASE. This allows us to find the address that a given + // byte in our buffer would have when loaded into the program the + // data describes. We need this to resolve DW_EH_PE_pcrel pointers. + void SetCFIDataBase(uint64 section_base, const char *buffer_base); + + // Indicate that the base address of the program's ".text" section + // is TEXT_BASE. We need this to resolve DW_EH_PE_textrel pointers. + void SetTextBase(uint64 text_base); + + // Indicate that the base address for DW_EH_PE_datarel pointers is + // DATA_BASE. The proper value depends on the ABI; it is usually the + // address of the global offset table, held in a designated register in + // position-independent code. You will need to look at the startup code + // for the target system to be sure. I tried; my eyes bled. + void SetDataBase(uint64 data_base); + + // Indicate that the base address for the FDE we are processing is + // FUNCTION_BASE. This is the start address of DW_EH_PE_funcrel + // pointers. (This encoding does not seem to be used by the GNU + // toolchain.) + void SetFunctionBase(uint64 function_base); + + // Indicate that we are no longer processing any FDE, so any use of + // a DW_EH_PE_funcrel encoding is an error. + void ClearFunctionBase(); + + // Return true if ENCODING is a valid pointer encoding. + bool ValidEncoding(DwarfPointerEncoding encoding) const; + + // Return true if we have all the information we need to read a + // pointer that uses ENCODING. This checks that the appropriate + // SetFooBase function for ENCODING has been called. + bool UsableEncoding(DwarfPointerEncoding encoding) const; + + // Read an encoded pointer from BUFFER using ENCODING; return the + // absolute address it represents, and set *LEN to the pointer's + // length in bytes, including any padding for aligned pointers. + // + // This function calls 'abort' if ENCODING is invalid or refers to a + // base address this reader hasn't been given, so you should check + // with ValidEncoding and UsableEncoding first if you would rather + // die in a more helpful way. + uint64 ReadEncodedPointer(const char *buffer, DwarfPointerEncoding encoding, + size_t *len) const; + + private: + + // Function pointer type for our address and offset readers. + typedef uint64 (ByteReader::*AddressReader)(const char*) const; + + // Read an offset from BUFFER and return it as an unsigned 64 bit + // integer. DWARF2/3 define offsets as either 4 or 8 bytes, + // generally depending on the amount of DWARF2/3 info present. + // This function pointer gets set by SetOffsetSize. + AddressReader offset_reader_; + + // Read an address from BUFFER and return it as an unsigned 64 bit + // integer. DWARF2/3 allow addresses to be any size from 0-255 + // bytes currently. Internally we support 4 and 8 byte addresses, + // and will CHECK on anything else. + // This function pointer gets set by SetAddressSize. + AddressReader address_reader_; + + Endianness endian_; + uint8 address_size_; + uint8 offset_size_; + + // Base addresses for Linux C++ exception handling data's encoded pointers. + bool have_section_base_, have_text_base_, have_data_base_; + bool have_function_base_; + uint64 section_base_, text_base_, data_base_, function_base_; + const char *buffer_base_; +}; + +} // namespace dwarf2reader + +#endif // COMMON_DWARF_BYTEREADER_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader-inl.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader-inl.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader-inl.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader-inl.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,175 @@ +// Copyright 2006 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef UTIL_DEBUGINFO_BYTEREADER_INL_H__ +#define UTIL_DEBUGINFO_BYTEREADER_INL_H__ + +#include + +#include "common/dwarf/bytereader.h" + +namespace dwarf2reader { + +inline uint8 ByteReader::ReadOneByte(const char* buffer) const { + return buffer[0]; +} + +inline uint16 ByteReader::ReadTwoBytes(const char* signed_buffer) const { + const unsigned char *buffer + = reinterpret_cast(signed_buffer); + const uint16 buffer0 = buffer[0]; + const uint16 buffer1 = buffer[1]; + if (endian_ == ENDIANNESS_LITTLE) { + return buffer0 | buffer1 << 8; + } else { + return buffer1 | buffer0 << 8; + } +} + +inline uint64 ByteReader::ReadFourBytes(const char* signed_buffer) const { + const unsigned char *buffer + = reinterpret_cast(signed_buffer); + const uint32 buffer0 = buffer[0]; + const uint32 buffer1 = buffer[1]; + const uint32 buffer2 = buffer[2]; + const uint32 buffer3 = buffer[3]; + if (endian_ == ENDIANNESS_LITTLE) { + return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24; + } else { + return buffer3 | buffer2 << 8 | buffer1 << 16 | buffer0 << 24; + } +} + +inline uint64 ByteReader::ReadEightBytes(const char* signed_buffer) const { + const unsigned char *buffer + = reinterpret_cast(signed_buffer); + const uint64 buffer0 = buffer[0]; + const uint64 buffer1 = buffer[1]; + const uint64 buffer2 = buffer[2]; + const uint64 buffer3 = buffer[3]; + const uint64 buffer4 = buffer[4]; + const uint64 buffer5 = buffer[5]; + const uint64 buffer6 = buffer[6]; + const uint64 buffer7 = buffer[7]; + if (endian_ == ENDIANNESS_LITTLE) { + return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24 | + buffer4 << 32 | buffer5 << 40 | buffer6 << 48 | buffer7 << 56; + } else { + return buffer7 | buffer6 << 8 | buffer5 << 16 | buffer4 << 24 | + buffer3 << 32 | buffer2 << 40 | buffer1 << 48 | buffer0 << 56; + } +} + +// Read an unsigned LEB128 number. Each byte contains 7 bits of +// information, plus one bit saying whether the number continues or +// not. + +inline uint64 ByteReader::ReadUnsignedLEB128(const char* buffer, + size_t* len) const { + uint64 result = 0; + size_t num_read = 0; + unsigned int shift = 0; + unsigned char byte; + + do { + byte = *buffer++; + num_read++; + + result |= (static_cast(byte & 0x7f)) << shift; + + shift += 7; + + } while (byte & 0x80); + + *len = num_read; + + return result; +} + +// Read a signed LEB128 number. These are like regular LEB128 +// numbers, except the last byte may have a sign bit set. + +inline int64 ByteReader::ReadSignedLEB128(const char* buffer, + size_t* len) const { + int64 result = 0; + unsigned int shift = 0; + size_t num_read = 0; + unsigned char byte; + + do { + byte = *buffer++; + num_read++; + result |= (static_cast(byte & 0x7f) << shift); + shift += 7; + } while (byte & 0x80); + + if ((shift < 8 * sizeof (result)) && (byte & 0x40)) + result |= -((static_cast(1)) << shift); + *len = num_read; + return result; +} + +inline uint64 ByteReader::ReadOffset(const char* buffer) const { + assert(this->offset_reader_); + return (this->*offset_reader_)(buffer); +} + +inline uint64 ByteReader::ReadAddress(const char* buffer) const { + assert(this->address_reader_); + return (this->*address_reader_)(buffer); +} + +inline void ByteReader::SetCFIDataBase(uint64 section_base, + const char *buffer_base) { + section_base_ = section_base; + buffer_base_ = buffer_base; + have_section_base_ = true; +} + +inline void ByteReader::SetTextBase(uint64 text_base) { + text_base_ = text_base; + have_text_base_ = true; +} + +inline void ByteReader::SetDataBase(uint64 data_base) { + data_base_ = data_base; + have_data_base_ = true; +} + +inline void ByteReader::SetFunctionBase(uint64 function_base) { + function_base_ = function_base; + have_function_base_ = true; +} + +inline void ByteReader::ClearFunctionBase() { + have_function_base_ = false; +} + +} // namespace dwarf2reader + +#endif // UTIL_DEBUGINFO_BYTEREADER_INL_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/bytereader_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,697 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// bytereader_unittest.cc: Unit tests for dwarf2reader::ByteReader + +#include + +#include "breakpad_googletest_includes.h" +#include "common/dwarf/bytereader.h" +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/cfi_assembler.h" + +using dwarf2reader::ByteReader; +using dwarf2reader::DwarfPointerEncoding; +using dwarf2reader::ENDIANNESS_BIG; +using dwarf2reader::ENDIANNESS_LITTLE; +using google_breakpad::CFISection; +using google_breakpad::TestAssembler::Label; +using google_breakpad::TestAssembler::kBigEndian; +using google_breakpad::TestAssembler::kLittleEndian; +using google_breakpad::TestAssembler::Section; +using std::string; +using testing::Test; + +struct ReaderFixture { + string contents; + size_t pointer_size; +}; + +class Reader: public ReaderFixture, public Test { }; +class ReaderDeathTest: public ReaderFixture, public Test { }; + +TEST_F(Reader, SimpleConstructor) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + CFISection section(kBigEndian, 4); + section + .D8(0xc0) + .D16(0xcf0d) + .D32(0x96fdd219) + .D64(0xbbf55fef0825f117ULL) + .ULEB128(0xa0927048ba8121afULL) + .LEB128(-0x4f337badf4483f83LL) + .D32(0xfec319c9); + ASSERT_TRUE(section.GetContents(&contents)); + const char *data = contents.data(); + EXPECT_EQ(0xc0U, reader.ReadOneByte(data)); + EXPECT_EQ(0xcf0dU, reader.ReadTwoBytes(data + 1)); + EXPECT_EQ(0x96fdd219U, reader.ReadFourBytes(data + 3)); + EXPECT_EQ(0xbbf55fef0825f117ULL, reader.ReadEightBytes(data + 7)); + size_t leb128_size; + EXPECT_EQ(0xa0927048ba8121afULL, + reader.ReadUnsignedLEB128(data + 15, &leb128_size)); + EXPECT_EQ(10U, leb128_size); + EXPECT_EQ(-0x4f337badf4483f83LL, + reader.ReadSignedLEB128(data + 25, &leb128_size)); + EXPECT_EQ(10U, leb128_size); + EXPECT_EQ(0xfec319c9, reader.ReadAddress(data + 35)); +} + +TEST_F(Reader, ValidEncodings) { + ByteReader reader(ENDIANNESS_LITTLE); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_omit))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_aligned))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_absptr | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_uleb128 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata2 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata4 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_udata8 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sleb128 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata2 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata4 | + dwarf2reader::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect | + dwarf2reader::DW_EH_PE_sdata8 | + dwarf2reader::DW_EH_PE_funcrel))); + + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x05))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x07))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0d))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x0f))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x51))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x60))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0x70))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xf0))); + EXPECT_FALSE(reader.ValidEncoding(DwarfPointerEncoding(0xd0))); +} + +TEST_F(ReaderDeathTest, DW_EH_PE_omit) { + static const char data[1] = { 42 }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + EXPECT_DEATH(reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_omit, + &pointer_size), + "encoding != DW_EH_PE_omit"); +} + +TEST_F(Reader, DW_EH_PE_absptr4) { + static const char data[] = { 0x27, 0x57, 0xea, 0x40 }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(4); + EXPECT_EQ(0x40ea5727U, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_absptr, + &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_absptr8) { + static const char data[] = { + 0x60, 0x27, 0x57, 0xea, 0x40, 0xc2, 0x98, 0x05, 0x01, 0x50 + }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(8); + EXPECT_EQ(0x010598c240ea5727ULL, + reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_absptr, + &pointer_size)); + EXPECT_EQ(8U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_uleb128) { + static const char data[] = { 0x81, 0x84, 0x4c }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(4); + EXPECT_EQ(0x130201U, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_uleb128, + &pointer_size)); + EXPECT_EQ(3U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_udata2) { + static const char data[] = { 0xf4, 0x8d }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + EXPECT_EQ(0xf48dU, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_udata2, + &pointer_size)); + EXPECT_EQ(2U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_udata4) { + static const char data[] = { 0xb2, 0x68, 0xa5, 0x62, 0x8f, 0x8b }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(8); + EXPECT_EQ(0xa5628f8b, + reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_udata4, + &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_udata8Addr8) { + static const char data[] = { + 0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe + }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(8); + EXPECT_EQ(0x8fed199f69047304ULL, + reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8, + &pointer_size)); + EXPECT_EQ(8U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_udata8Addr4) { + static const char data[] = { + 0x27, 0x04, 0x73, 0x04, 0x69, 0x9f, 0x19, 0xed, 0x8f, 0xfe + }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(4); + EXPECT_EQ(0x69047304ULL, + reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_udata8, + &pointer_size)); + EXPECT_EQ(8U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_sleb128) { + static const char data[] = { 0x42, 0xff, 0xfb, 0x73 }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + EXPECT_EQ(-0x030201U & 0xffffffff, + reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sleb128, + &pointer_size)); + EXPECT_EQ(3U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_sdata2) { + static const char data[] = { 0xb9, 0xbf }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(8); + EXPECT_EQ(0xffffffffffffbfb9ULL, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_sdata2, + &pointer_size)); + EXPECT_EQ(2U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_sdata4) { + static const char data[] = { 0xa0, 0xca, 0xf2, 0xb8, 0xc2, 0xad }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(8); + EXPECT_EQ(0xffffffffadc2b8f2ULL, + reader.ReadEncodedPointer(data + 2, dwarf2reader::DW_EH_PE_sdata4, + &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_sdata8) { + static const char data[] = { + 0xf6, 0x66, 0x57, 0x79, 0xe0, 0x0c, 0x9b, 0x26, 0x87 + }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(8); + EXPECT_EQ(0x87269b0ce0795766ULL, + reader.ReadEncodedPointer(data + 1, dwarf2reader::DW_EH_PE_sdata8, + &pointer_size)); + EXPECT_EQ(8U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_pcrel) { + static const char data[] = { 0x4a, 0x8b, 0x1b, 0x14, 0xc8, 0xc4, 0x02, 0xce }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + DwarfPointerEncoding encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_pcrel + | dwarf2reader::DW_EH_PE_absptr); + reader.SetCFIDataBase(0x89951377, data); + EXPECT_EQ(0x89951377 + 3 + 0x14c8c402, + reader.ReadEncodedPointer(data + 3, encoding, &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_textrel) { + static const char data[] = { 0xd9, 0x0d, 0x05, 0x17, 0xc9, 0x7a, 0x42, 0x1e }; + ByteReader reader(ENDIANNESS_LITTLE); + reader.SetAddressSize(4); + reader.SetTextBase(0xb91beaf0); + DwarfPointerEncoding encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel + | dwarf2reader::DW_EH_PE_sdata2); + EXPECT_EQ((0xb91beaf0 + 0xffffc917) & 0xffffffff, + reader.ReadEncodedPointer(data + 3, encoding, &pointer_size)); + EXPECT_EQ(2U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_datarel) { + static const char data[] = { 0x16, 0xf2, 0xbb, 0x82, 0x68, 0xa7, 0xbc, 0x39 }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(8); + reader.SetDataBase(0xbef308bd25ce74f0ULL); + DwarfPointerEncoding encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_datarel + | dwarf2reader::DW_EH_PE_sleb128); + EXPECT_EQ(0xbef308bd25ce74f0ULL + 0xfffffffffffa013bULL, + reader.ReadEncodedPointer(data + 2, encoding, &pointer_size)); + EXPECT_EQ(3U, pointer_size); +} + +TEST_F(Reader, DW_EH_PE_funcrel) { + static const char data[] = { 0x84, 0xf8, 0x14, 0x01, 0x61, 0xd1, 0x48, 0xc9 }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetAddressSize(4); + reader.SetFunctionBase(0x823c3520); + DwarfPointerEncoding encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_funcrel + | dwarf2reader::DW_EH_PE_udata2); + EXPECT_EQ(0x823c3520 + 0xd148, + reader.ReadEncodedPointer(data + 5, encoding, &pointer_size)); + EXPECT_EQ(2U, pointer_size); +} + +TEST(UsableBase, CFI) { + static const char data[1] = { 0x42 }; + ByteReader reader(ENDIANNESS_BIG); + reader.SetCFIDataBase(0xb31cbd20, data); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +TEST(UsableBase, Text) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetTextBase(0xa899ccb9); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +TEST(UsableBase, Data) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetDataBase(0xf7b10bcd); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +TEST(UsableBase, Function) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetFunctionBase(0xc2c0ed81); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +TEST(UsableBase, ClearFunction) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetFunctionBase(0xc2c0ed81); + reader.ClearFunctionBase(); + EXPECT_TRUE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_absptr)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_pcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_textrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_datarel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(dwarf2reader::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +struct AlignedFixture { + AlignedFixture() : reader(ENDIANNESS_BIG) { reader.SetAddressSize(4); } + static const char data[10]; + ByteReader reader; + size_t pointer_size; +}; + +const char AlignedFixture::data[10] = { + 0xfe, 0x6e, 0x93, 0xd8, 0x34, 0xd5, 0x1c, 0xd3, 0xac, 0x2b +}; + +class Aligned: public AlignedFixture, public Test { }; + +TEST_F(Aligned, DW_EH_PE_aligned0) { + reader.SetCFIDataBase(0xb440305c, data); + EXPECT_EQ(0xfe6e93d8U, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned1) { + reader.SetCFIDataBase(0xb440305d, data); + EXPECT_EQ(0xd834d51cU, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(7U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned2) { + reader.SetCFIDataBase(0xb440305e, data); + EXPECT_EQ(0x93d834d5U, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(6U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned3) { + reader.SetCFIDataBase(0xb440305f, data); + EXPECT_EQ(0x6e93d834U, + reader.ReadEncodedPointer(data, dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(5U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned11) { + reader.SetCFIDataBase(0xb4403061, data); + EXPECT_EQ(0xd834d51cU, + reader.ReadEncodedPointer(data + 1, + dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(6U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned30) { + reader.SetCFIDataBase(0xb4403063, data); + EXPECT_EQ(0x6e93d834U, + reader.ReadEncodedPointer(data + 1, + dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(4U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned23) { + reader.SetCFIDataBase(0xb4403062, data); + EXPECT_EQ(0x1cd3ac2bU, + reader.ReadEncodedPointer(data + 3, + dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(7U, pointer_size); +} + +TEST_F(Aligned, DW_EH_PE_aligned03) { + reader.SetCFIDataBase(0xb4403064, data); + EXPECT_EQ(0x34d51cd3U, + reader.ReadEncodedPointer(data + 3, + dwarf2reader::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(5U, pointer_size); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/cfi_assembler.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/cfi_assembler.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/cfi_assembler.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/cfi_assembler.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,196 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// cfi_assembler.cc: Implementation of google_breakpad::CFISection class. +// See cfi_assembler.h for details. + +#include +#include + +#include "common/dwarf/cfi_assembler.h" + +namespace google_breakpad { + +using dwarf2reader::DwarfPointerEncoding; + +CFISection &CFISection::CIEHeader(u_int64_t code_alignment_factor, + int data_alignment_factor, + unsigned return_address_register, + u_int8_t version, + const string &augmentation, + bool dwarf64) { + assert(!entry_length_); + entry_length_ = new PendingLength(); + in_fde_ = false; + + if (dwarf64) { + D32(0xffffffff); + D64(entry_length_->length); + entry_length_->start = Here(); + // Write the CIE distinguished value. In .debug_frame sections, it's + // ~0; in .eh_frame sections, it's zero. + D64(eh_frame_ ? 0 : ~(u_int64_t)0); + } else { + D32(entry_length_->length); + entry_length_->start = Here(); + // Write the CIE distinguished value. In .debug_frame sections, it's + // ~0; in .eh_frame sections, it's zero. + D32(eh_frame_ ? 0 : ~(u_int32_t)0); + } + D8(version); + AppendCString(augmentation); + ULEB128(code_alignment_factor); + LEB128(data_alignment_factor); + if (version == 1) + D8(return_address_register); + else + ULEB128(return_address_register); + return *this; +} + +CFISection &CFISection::FDEHeader(Label cie_pointer, + u_int64_t initial_location, + u_int64_t address_range, + bool dwarf64) { + assert(!entry_length_); + entry_length_ = new PendingLength(); + in_fde_ = true; + fde_start_address_ = initial_location; + + if (dwarf64) { + D32(0xffffffff); + D64(entry_length_->length); + entry_length_->start = Here(); + if (eh_frame_) + D64(Here() - cie_pointer); + else + D64(cie_pointer); + } else { + D32(entry_length_->length); + entry_length_->start = Here(); + if (eh_frame_) + D32(Here() - cie_pointer); + else + D32(cie_pointer); + } + EncodedPointer(initial_location); + // The FDE length in an .eh_frame section uses the same encoding as the + // initial location, but ignores the base address (selected by the upper + // nybble of the encoding), as it's a length, not an address that can be + // made relative. + EncodedPointer(address_range, + DwarfPointerEncoding(pointer_encoding_ & 0x0f)); + return *this; +} + +CFISection &CFISection::FinishEntry() { + assert(entry_length_); + Align(address_size_, dwarf2reader::DW_CFA_nop); + entry_length_->length = Here() - entry_length_->start; + delete entry_length_; + entry_length_ = NULL; + in_fde_ = false; + return *this; +} + +CFISection &CFISection::EncodedPointer(u_int64_t address, + DwarfPointerEncoding encoding, + const EncodedPointerBases &bases) { + // Omitted data is extremely easy to emit. + if (encoding == dwarf2reader::DW_EH_PE_omit) + return *this; + + // If (encoding & dwarf2reader::DW_EH_PE_indirect) != 0, then we assume + // that ADDRESS is the address at which the pointer is stored --- in + // other words, that bit has no effect on how we write the pointer. + encoding = DwarfPointerEncoding(encoding & ~dwarf2reader::DW_EH_PE_indirect); + + // Find the base address to which this pointer is relative. The upper + // nybble of the encoding specifies this. + u_int64_t base; + switch (encoding & 0xf0) { + case dwarf2reader::DW_EH_PE_absptr: base = 0; break; + case dwarf2reader::DW_EH_PE_pcrel: base = bases.cfi + Size(); break; + case dwarf2reader::DW_EH_PE_textrel: base = bases.text; break; + case dwarf2reader::DW_EH_PE_datarel: base = bases.data; break; + case dwarf2reader::DW_EH_PE_funcrel: base = fde_start_address_; break; + case dwarf2reader::DW_EH_PE_aligned: base = 0; break; + default: abort(); + }; + + // Make ADDRESS relative. Yes, this is appropriate even for "absptr" + // values; see gcc/unwind-pe.h. + address -= base; + + // Align the pointer, if required. + if ((encoding & 0xf0) == dwarf2reader::DW_EH_PE_aligned) + Align(AddressSize()); + + // Append ADDRESS to this section in the appropriate form. For the + // fixed-width forms, we don't need to differentiate between signed and + // unsigned encodings, because ADDRESS has already been extended to 64 + // bits before it was passed to us. + switch (encoding & 0x0f) { + case dwarf2reader::DW_EH_PE_absptr: + Address(address); + break; + + case dwarf2reader::DW_EH_PE_uleb128: + ULEB128(address); + break; + + case dwarf2reader::DW_EH_PE_sleb128: + LEB128(address); + break; + + case dwarf2reader::DW_EH_PE_udata2: + case dwarf2reader::DW_EH_PE_sdata2: + D16(address); + break; + + case dwarf2reader::DW_EH_PE_udata4: + case dwarf2reader::DW_EH_PE_sdata4: + D32(address); + break; + + case dwarf2reader::DW_EH_PE_udata8: + case dwarf2reader::DW_EH_PE_sdata8: + D64(address); + break; + + default: + abort(); + } + + return *this; +}; + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/cfi_assembler.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/cfi_assembler.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/cfi_assembler.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/cfi_assembler.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,256 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// cfi-assembler.h: Define CFISection, a class for creating properly +// (and improperly) formatted DWARF CFI data for unit tests. + +#ifndef PROCESSOR_CFI_ASSEMBLER_H_ +#define PROCESSOR_CFI_ASSEMBLER_H_ + +#include + +#include "common/dwarf/dwarf2enums.h" +#include "google_breakpad/common/breakpad_types.h" +#include "processor/test_assembler.h" + +namespace google_breakpad { + +using dwarf2reader::DwarfPointerEncoding; +using google_breakpad::TestAssembler::Endianness; +using google_breakpad::TestAssembler::Label; +using google_breakpad::TestAssembler::Section; +using std::string; + +class CFISection: public Section { + public: + + // CFI augmentation strings beginning with 'z', defined by the + // Linux/IA-64 C++ ABI, can specify interesting encodings for + // addresses appearing in FDE headers and call frame instructions (and + // for additional fields whose presence the augmentation string + // specifies). In particular, pointers can be specified to be relative + // to various base address: the start of the .text section, the + // location holding the address itself, and so on. These allow the + // frame data to be position-independent even when they live in + // write-protected pages. These variants are specified at the + // following two URLs: + // + // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html + // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html + // + // CFISection leaves the production of well-formed 'z'-augmented CIEs and + // FDEs to the user, but does provide EncodedPointer, to emit + // properly-encoded addresses for a given pointer encoding. + // EncodedPointer uses an instance of this structure to find the base + // addresses it should use; you can establish a default for all encoded + // pointers appended to this section with SetEncodedPointerBases. + struct EncodedPointerBases { + EncodedPointerBases() : cfi(), text(), data() { } + + // The starting address of this CFI section in memory, for + // DW_EH_PE_pcrel. DW_EH_PE_pcrel pointers may only be used in data + // that has is loaded into the program's address space. + u_int64_t cfi; + + // The starting address of this file's .text section, for DW_EH_PE_textrel. + u_int64_t text; + + // The starting address of this file's .got or .eh_frame_hdr section, + // for DW_EH_PE_datarel. + u_int64_t data; + }; + + // Create a CFISection whose endianness is ENDIANNESS, and where + // machine addresses are ADDRESS_SIZE bytes long. If EH_FRAME is + // true, use the .eh_frame format, as described by the Linux + // Standards Base Core Specification, instead of the DWARF CFI + // format. + CFISection(Endianness endianness, size_t address_size, + bool eh_frame = false) + : Section(endianness), address_size_(address_size), eh_frame_(eh_frame), + pointer_encoding_(dwarf2reader::DW_EH_PE_absptr), + encoded_pointer_bases_(), entry_length_(NULL), in_fde_(false) { + // The 'start', 'Here', and 'Mark' members of a CFISection all refer + // to section offsets. + start() = 0; + } + + // Return this CFISection's address size. + size_t AddressSize() const { return address_size_; } + + // Return true if this CFISection uses the .eh_frame format, or + // false if it contains ordinary DWARF CFI data. + bool ContainsEHFrame() const { return eh_frame_; } + + // Use ENCODING for pointers in calls to FDEHeader and EncodedPointer. + void SetPointerEncoding(DwarfPointerEncoding encoding) { + pointer_encoding_ = encoding; + } + + // Use the addresses in BASES as the base addresses for encoded + // pointers in subsequent calls to FDEHeader or EncodedPointer. + // This function makes a copy of BASES. + void SetEncodedPointerBases(const EncodedPointerBases &bases) { + encoded_pointer_bases_ = bases; + } + + // Append a Common Information Entry header to this section with the + // given values. If dwarf64 is true, use the 64-bit DWARF initial + // length format for the CIE's initial length. Return a reference to + // this section. You should call FinishEntry after writing the last + // instruction for the CIE. + // + // Before calling this function, you will typically want to use Mark + // or Here to make a label to pass to FDEHeader that refers to this + // CIE's position in the section. + CFISection &CIEHeader(u_int64_t code_alignment_factor, + int data_alignment_factor, + unsigned return_address_register, + u_int8_t version = 3, + const string &augmentation = "", + bool dwarf64 = false); + + // Append a Frame Description Entry header to this section with the + // given values. If dwarf64 is true, use the 64-bit DWARF initial + // length format for the CIE's initial length. Return a reference to + // this section. You should call FinishEntry after writing the last + // instruction for the CIE. + // + // This function doesn't support entries that are longer than + // 0xffffff00 bytes. (The "initial length" is always a 32-bit + // value.) Nor does it support .debug_frame sections longer than + // 0xffffff00 bytes. + CFISection &FDEHeader(Label cie_pointer, + u_int64_t initial_location, + u_int64_t address_range, + bool dwarf64 = false); + + // Note the current position as the end of the last CIE or FDE we + // started, after padding with DW_CFA_nops for alignment. This + // defines the label representing the entry's length, cited in the + // entry's header. Return a reference to this section. + CFISection &FinishEntry(); + + // Append the contents of BLOCK as a DW_FORM_block value: an + // unsigned LEB128 length, followed by that many bytes of data. + CFISection &Block(const string &block) { + ULEB128(block.size()); + Append(block); + return *this; + } + + // Append ADDRESS to this section, in the appropriate size and + // endianness. Return a reference to this section. + CFISection &Address(u_int64_t address) { + Section::Append(endianness(), address_size_, address); + return *this; + } + CFISection &Address(Label address) { + Section::Append(endianness(), address_size_, address); + return *this; + } + + // Append ADDRESS to this section, using ENCODING and BASES. ENCODING + // defaults to this section's default encoding, established by + // SetPointerEncoding. BASES defaults to this section's bases, set by + // SetEncodedPointerBases. If the DW_EH_PE_indirect bit is set in the + // encoding, assume that ADDRESS is where the true address is stored. + // Return a reference to this section. + // + // (C++ doesn't let me use default arguments here, because I want to + // refer to members of *this in the default argument expression.) + CFISection &EncodedPointer(u_int64_t address) { + return EncodedPointer(address, pointer_encoding_, encoded_pointer_bases_); + } + CFISection &EncodedPointer(u_int64_t address, DwarfPointerEncoding encoding) { + return EncodedPointer(address, encoding, encoded_pointer_bases_); + } + CFISection &EncodedPointer(u_int64_t address, DwarfPointerEncoding encoding, + const EncodedPointerBases &bases); + + // Restate some member functions, to keep chaining working nicely. + CFISection &Mark(Label *label) { Section::Mark(label); return *this; } + CFISection &D8(u_int8_t v) { Section::D8(v); return *this; } + CFISection &D16(u_int16_t v) { Section::D16(v); return *this; } + CFISection &D16(Label v) { Section::D16(v); return *this; } + CFISection &D32(u_int32_t v) { Section::D32(v); return *this; } + CFISection &D32(const Label &v) { Section::D32(v); return *this; } + CFISection &D64(u_int64_t v) { Section::D64(v); return *this; } + CFISection &D64(const Label &v) { Section::D64(v); return *this; } + CFISection &LEB128(long long v) { Section::LEB128(v); return *this; } + CFISection &ULEB128(u_int64_t v) { Section::ULEB128(v); return *this; } + + private: + // A length value that we've appended to the section, but is not yet + // known. LENGTH is the appended value; START is a label referring + // to the start of the data whose length was cited. + struct PendingLength { + Label length; + Label start; + }; + + // The size of a machine address for the data in this section. + size_t address_size_; + + // If true, we are generating a Linux .eh_frame section, instead of + // a standard DWARF .debug_frame section. + bool eh_frame_; + + // The encoding to use for FDE pointers. + DwarfPointerEncoding pointer_encoding_; + + // The base addresses to use when emitting encoded pointers. + EncodedPointerBases encoded_pointer_bases_; + + // The length value for the current entry. + // + // Oddly, this must be dynamically allocated. Labels never get new + // values; they only acquire constraints on the value they already + // have, or assert if you assign them something incompatible. So + // each header needs truly fresh Label objects to cite in their + // headers and track their positions. The alternative is explicit + // destructor invocation and a placement new. Ick. + PendingLength *entry_length_; + + // True if we are currently emitting an FDE --- that is, we have + // called FDEHeader but have not yet called FinishEntry. + bool in_fde_; + + // If in_fde_ is true, this is its starting address. We use this for + // emitting DW_EH_PE_funcrel pointers. + u_int64_t fde_start_address_; +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_CFI_ASSEMBLER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,186 @@ +// Copyright (c) 2010 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// dwarf2diehandler.cc: Implement the dwarf2reader::DieDispatcher class. +// See dwarf2diehandler.h for details. + +#include + +#include "common/dwarf/dwarf2diehandler.h" + +namespace dwarf2reader { + +DIEDispatcher::~DIEDispatcher() { + while (!die_handlers_.empty()) { + HandlerStack &entry = die_handlers_.top(); + if (entry.handler_ != root_handler_) + delete entry.handler_; + die_handlers_.pop(); + } +} + +bool DIEDispatcher::StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version) { + return root_handler_->StartCompilationUnit(offset, address_size, + offset_size, cu_length, + dwarf_version); +} + +bool DIEDispatcher::StartDIE(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs) { + // The stack entry for the parent of this DIE, if there is one. + HandlerStack *parent = die_handlers_.empty() ? NULL : &die_handlers_.top(); + + // Does this call indicate that we're done receiving the parent's + // attributes' values? If so, call its EndAttributes member function. + if (parent && parent->handler_ && !parent->reported_attributes_end_) { + parent->reported_attributes_end_ = true; + if (!parent->handler_->EndAttributes()) { + // Finish off this handler now. and edit *PARENT to indicate that + // we don't want to visit any of the children. + parent->handler_->Finish(); + if (parent->handler_ != root_handler_) + delete parent->handler_; + parent->handler_ = NULL; + return false; + } + } + + // Find a handler for this DIE. + DIEHandler *handler; + if (parent) { + if (parent->handler_) + // Ask the parent to find a handler. + handler = parent->handler_->FindChildHandler(offset, tag, attrs); + else + // No parent handler means we're not interested in any of our + // children. + handler = NULL; + } else { + // This is the root DIE. For a non-root DIE, the parent's handler + // decides whether to visit it, but the root DIE has no parent + // handler, so we have a special method on the root DIE handler + // itself to decide. + if (root_handler_->StartRootDIE(offset, tag, attrs)) + handler = root_handler_; + else + handler = NULL; + } + + // Push a handler stack entry for this new handler. As an + // optimization, we don't push NULL-handler entries on top of other + // NULL-handler entries; we just let the oldest such entry stand for + // the whole subtree. + if (handler || !parent || parent->handler_) { + HandlerStack entry; + entry.offset_ = offset; + entry.handler_ = handler; + entry.reported_attributes_end_ = false; + die_handlers_.push(entry); + } + + return handler != NULL; +} + +void DIEDispatcher::EndDIE(uint64 offset) { + assert(!die_handlers_.empty()); + HandlerStack *entry = &die_handlers_.top(); + if (entry->handler_) { + // This entry had better be the handler for this DIE. + assert(entry->offset_ == offset); + // If a DIE has no children, this EndDIE call indicates that we're + // done receiving its attributes' values. + if (!entry->reported_attributes_end_) + entry->handler_->EndAttributes(); // Ignore return value: no children. + entry->handler_->Finish(); + if (entry->handler_ != root_handler_) + delete entry->handler_; + } else { + // If this DIE is within a tree we're ignoring, then don't pop the + // handler stack: that entry stands for the whole tree. + if (entry->offset_ != offset) + return; + } + die_handlers_.pop(); +} + +void DIEDispatcher::ProcessAttributeUnsigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeUnsigned(attr, form, data); +} + +void DIEDispatcher::ProcessAttributeSigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeSigned(attr, form, data); +} + +void DIEDispatcher::ProcessAttributeReference(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeReference(attr, form, data); +} + +void DIEDispatcher::ProcessAttributeBuffer(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const char* data, + uint64 len) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeBuffer(attr, form, data, len); +} + +void DIEDispatcher::ProcessAttributeString(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const string& data) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeString(attr, form, data); +} + +} // namespace dwarf2reader diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,358 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// dwarf2reader::CompilationUnit is a simple and direct parser for +// DWARF data, but its handler interface is not convenient to use. In +// particular: +// +// - CompilationUnit calls Dwarf2Handler's member functions to report +// every attribute's value, regardless of what sort of DIE it is. +// As a result, the ProcessAttributeX functions end up looking like +// this: +// +// switch (parent_die_tag) { +// case DW_TAG_x: +// switch (attribute_name) { +// case DW_AT_y: +// handle attribute y of DIE type x +// ... +// } break; +// ... +// } +// +// In C++ it's much nicer to use virtual function dispatch to find +// the right code for a given case than to switch on the DIE tag +// like this. +// +// - Processing different kinds of DIEs requires different sets of +// data: lexical block DIEs have start and end addresses, but struct +// type DIEs don't. It would be nice to be able to have separate +// handler classes for separate kinds of DIEs, each with the members +// appropriate to its role, instead of having one handler class that +// needs to hold data for every DIE type. +// +// - There should be a separate instance of the appropriate handler +// class for each DIE, instead of a single object with tables +// tracking all the dies in the compilation unit. +// +// - It's not convenient to take some action after all a DIE's +// attributes have been seen, but before visiting any of its +// children. The only indication you have that a DIE's attribute +// list is complete is that you get either a StartDIE or an EndDIE +// call. +// +// - It's not convenient to make use of the tree structure of the +// DIEs. Skipping all the children of a given die requires +// maintaining state and returning false from StartDIE until we get +// an EndDIE call with the appropriate offset. +// +// This interface tries to take care of all that. (You're shocked, I'm sure.) +// +// Using the classes here, you provide an initial handler for the root +// DIE of the compilation unit. Each handler receives its DIE's +// attributes, and provides fresh handler objects for children of +// interest, if any. The three classes are: +// +// - DIEHandler: the base class for your DIE-type-specific handler +// classes. +// +// - RootDIEHandler: derived from DIEHandler, the base class for your +// root DIE handler class. +// +// - DIEDispatcher: derived from Dwarf2Handler, an instance of this +// invokes your DIE-type-specific handler objects. +// +// In detail: +// +// - Define handler classes specialized for the DIE types you're +// interested in. These handler classes must inherit from +// DIEHandler. Thus: +// +// class My_DW_TAG_X_Handler: public DIEHandler { ... }; +// class My_DW_TAG_Y_Handler: public DIEHandler { ... }; +// +// DIEHandler subclasses needn't correspond exactly to single DIE +// types, as shown here; the point is that you can have several +// different classes appropriate to different kinds of DIEs. +// +// - In particular, define a handler class for the compilation +// unit's root DIE, that inherits from RootDIEHandler: +// +// class My_DW_TAG_compile_unit_Handler: public RootDIEHandler { ... }; +// +// RootDIEHandler inherits from DIEHandler, adding a few additional +// member functions for examining the compilation unit as a whole, +// and other quirks of rootness. +// +// - Then, create a DIEDispatcher instance, passing it an instance of +// your root DIE handler class, and use that DIEDispatcher as the +// dwarf2reader::CompilationUnit's handler: +// +// My_DW_TAG_compile_unit_Handler root_die_handler(...); +// DIEDispatcher die_dispatcher(&root_die_handler); +// CompilationUnit reader(sections, offset, bytereader, &die_dispatcher); +// +// Here, 'die_dispatcher' acts as a shim between 'reader' and the +// various DIE-specific handlers you have defined. +// +// - When you call reader.Start(), die_dispatcher behaves as follows, +// starting with your root die handler and the compilation unit's +// root DIE: +// +// - It calls the handler's ProcessAttributeX member functions for +// each of the DIE's attributes. +// +// - It calls the handler's EndAttributes member function. This +// should return true if any of the DIE's children should be +// visited, in which case: +// +// - For each of the DIE's children, die_dispatcher calls the +// DIE's handler's FindChildHandler member function. If that +// returns a pointer to a DIEHandler instance, then +// die_dispatcher uses that handler to process the child, using +// this procedure recursively. Alternatively, if +// FindChildHandler returns NULL, die_dispatcher ignores that +// child and its descendants. +// +// - When die_dispatcher has finished processing all the DIE's +// children, it invokes the handler's Finish() member function, +// and destroys the handler. (As a special case, it doesn't +// destroy the root DIE handler.) +// +// This allows the code for handling a particular kind of DIE to be +// gathered together in a single class, makes it easy to skip all the +// children or individual children of a particular DIE, and provides +// appropriate parental context for each die. + +#ifndef COMMON_DWARF_DWARF2DIEHANDLER_H__ +#define COMMON_DWARF_DWARF2DIEHANDLER_H__ + +#include + +#include "common/dwarf/types.h" +#include "common/dwarf/dwarf2enums.h" +#include "common/dwarf/dwarf2reader.h" + +namespace dwarf2reader { + +// A base class for handlers for specific DIE types. The series of +// calls made on a DIE handler is as follows: +// +// - for each attribute of the DIE: +// - ProcessAttributeX() +// - EndAttributes() +// - if that returned true, then for each child: +// - FindChildHandler() +// - if that returns a non-NULL pointer to a new handler: +// - recurse, with the new handler and the child die +// - Finish() +// - destruction +class DIEHandler { + public: + DIEHandler() { } + virtual ~DIEHandler() { } + + // When we visit a DIE, we first use these member functions to + // report the DIE's attributes and their values. These have the + // same restrictions as the corresponding member functions of + // dwarf2reader::Dwarf2Handler. + // + // Since DWARF does not specify in what order attributes must + // appear, avoid making decisions in these functions that would be + // affected by the presence of other attributes. The EndAttributes + // function is a more appropriate place for such work, as all the + // DIE's attributes have been seen at that point. + // + // The default definitions ignore the values they are passed. + virtual void ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { } + virtual void ProcessAttributeSigned(enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { } + virtual void ProcessAttributeReference(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { } + virtual void ProcessAttributeBuffer(enum DwarfAttribute attr, + enum DwarfForm form, + const char* data, + uint64 len) { } + virtual void ProcessAttributeString(enum DwarfAttribute attr, + enum DwarfForm form, + const string& data) { } + + // Once we have reported all the DIE's attributes' values, we call + // this member function. If it returns false, we skip all the DIE's + // children. If it returns true, we call FindChildHandler on each + // child. If that returns a handler object, we use that to visit + // the child; otherwise, we skip the child. + // + // This is a good place to make decisions that depend on more than + // one attribute. DWARF does not specify in what order attributes + // must appear, so only when the EndAttributes function is called + // does the handler have a complete picture of the DIE's attributes. + // + // The default definition elects to ignore the DIE's children. + // You'll need to override this if you override FindChildHandler, + // but at least the default behavior isn't to pass the children to + // FindChildHandler, which then ignores them all. + virtual bool EndAttributes() { return false; } + + // If EndAttributes returns true to indicate that some of the DIE's + // children might be of interest, then we apply this function to + // each of the DIE's children. If it returns a handler object, then + // we use that to visit the child DIE. If it returns NULL, we skip + // that child DIE (and all its descendants). + // + // OFFSET is the offset of the child; TAG indicates what kind of DIE + // it is; and ATTRS is the list of attributes the DIE will have, and + // their forms (their values are not provided). + // + // The default definition skips all children. + virtual DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag, + const AttributeList &attrs) { + return NULL; + } + + // When we are done processing a DIE, we call this member function. + // This happens after the EndAttributes call, all FindChildHandler + // calls (if any), and all operations on the children themselves (if + // any). We call Finish on every handler --- even if EndAttributes + // returns false. + virtual void Finish() { }; +}; + +// A subclass of DIEHandler, with additional kludges for handling the +// compilation unit's root die. +class RootDIEHandler: public DIEHandler { + public: + RootDIEHandler() { } + virtual ~RootDIEHandler() { } + + // We pass the values reported via Dwarf2Handler::StartCompilationUnit + // to this member function, and skip the entire compilation unit if it + // returns false. So the root DIE handler is actually also + // responsible for handling the compilation unit metadata. + // The default definition always visits the compilation unit. + virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version) { return true; } + + // For the root DIE handler only, we pass the offset, tag and + // attributes of the compilation unit's root DIE. This is the only + // way the root DIE handler can find the root DIE's tag. If this + // function returns true, we will visit the root DIE using the usual + // DIEHandler methods; otherwise, we skip the entire compilation + // unit. + // + // The default definition elects to visit the root DIE. + virtual bool StartRootDIE(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs) { return true; } +}; + +class DIEDispatcher: public Dwarf2Handler { + public: + // Create a Dwarf2Handler which uses ROOT_HANDLER as the handler for + // the compilation unit's root die, as described for the DIEHandler + // class. + DIEDispatcher(RootDIEHandler *root_handler) : root_handler_(root_handler) { } + // Destroying a DIEDispatcher destroys all active handler objects + // except the root handler. + ~DIEDispatcher(); + bool StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version); + bool StartDIE(uint64 offset, enum DwarfTag tag, + const AttributeList &attrs); + void ProcessAttributeUnsigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + void ProcessAttributeSigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data); + void ProcessAttributeReference(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + void ProcessAttributeBuffer(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const char* data, + uint64 len); + void ProcessAttributeString(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const string &data); + void EndDIE(uint64 offset); + + private: + + // The type of a handler stack entry. This includes some fields + // which don't really need to be on the stack --- they could just be + // single data members of DIEDispatcher --- but putting them here + // makes it easier to see that the code is correct. + struct HandlerStack { + // The offset of the DIE for this handler stack entry. + uint64 offset_; + + // The handler object interested in this DIE's attributes and + // children. If NULL, we're not interested in either. + DIEHandler *handler_; + + // Have we reported the end of this DIE's attributes to the handler? + bool reported_attributes_end_; + }; + + // Stack of DIE attribute handlers. At StartDIE(D), the top of the + // stack is the handler of D's parent, whom we may ask for a handler + // for D itself. At EndDIE(D), the top of the stack is D's handler. + // Special cases: + // + // - Before we've seen the compilation unit's root DIE, the stack is + // empty; we'll call root_handler_'s special member functions, and + // perhaps push root_handler_ on the stack to look at the root's + // immediate children. + // + // - When we decide to ignore a subtree, we only push an entry on + // the stack for the root of the tree being ignored, rather than + // pushing lots of stack entries with handler_ set to NULL. + stack die_handlers_; + + // The root handler. We don't push it on die_handlers_ until we + // actually get the StartDIE call for the root. + RootDIEHandler *root_handler_; +}; + +} // namespace dwarf2reader +#endif // COMMON_DWARF_DWARF2DIEHANDLER_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,560 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher. + +#include "breakpad_googletest_includes.h" + +#include "common/dwarf/dwarf2diehandler.h" + +using ::testing::_; +using ::testing::ContainerEq; +using ::testing::ElementsAreArray; +using ::testing::Eq; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::Sequence; +using ::testing::StrEq; + +using dwarf2reader::AttributeList; +using dwarf2reader::DIEDispatcher; +using dwarf2reader::DIEHandler; +using dwarf2reader::DwarfAttribute; +using dwarf2reader::DwarfForm; +using dwarf2reader::DwarfTag; +using dwarf2reader::RootDIEHandler; + +class MockDIEHandler: public DIEHandler { + public: + MOCK_METHOD3(ProcessAttributeUnsigned, + void(DwarfAttribute, DwarfForm, uint64)); + MOCK_METHOD3(ProcessAttributeSigned, + void(DwarfAttribute, DwarfForm, int64)); + MOCK_METHOD3(ProcessAttributeReference, + void(DwarfAttribute, DwarfForm, uint64)); + MOCK_METHOD4(ProcessAttributeBuffer, + void(DwarfAttribute, DwarfForm, const char *, uint64)); + MOCK_METHOD3(ProcessAttributeString, + void(DwarfAttribute, DwarfForm, const string &)); + MOCK_METHOD0(EndAttributes, bool()); + MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag, + const AttributeList &)); + MOCK_METHOD0(Finish, void()); +}; + +class MockRootDIEHandler: public RootDIEHandler { + public: + MOCK_METHOD3(ProcessAttributeUnsigned, + void(DwarfAttribute, DwarfForm, uint64)); + MOCK_METHOD3(ProcessAttributeSigned, + void(DwarfAttribute, DwarfForm, int64)); + MOCK_METHOD3(ProcessAttributeReference, + void(DwarfAttribute, DwarfForm, uint64)); + MOCK_METHOD4(ProcessAttributeBuffer, + void(DwarfAttribute, DwarfForm, const char *, uint64)); + MOCK_METHOD3(ProcessAttributeString, + void(DwarfAttribute, DwarfForm, const string &)); + MOCK_METHOD0(EndAttributes, bool()); + MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag, + const AttributeList &)); + MOCK_METHOD0(Finish, void()); + MOCK_METHOD5(StartCompilationUnit, bool(uint64, uint8, uint8, uint64, uint8)); + MOCK_METHOD3(StartRootDIE, bool(uint64, DwarfTag, const AttributeList &)); +}; + +// If the handler elects to skip the compilation unit, the dispatcher +// should tell the reader so. +TEST(Dwarf2DIEHandler, SkipCompilationUnit) { + Sequence s; + MockRootDIEHandler mock_root_handler; + DIEDispatcher die_dispatcher(&mock_root_handler); + + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0x8d42aed77cfccf3eLL, + 0x89, 0xdc, + 0x2ecb4dc778a80f21LL, + 0x66)) + .InSequence(s) + .WillOnce(Return(false)); + + EXPECT_FALSE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL, + 0x89, 0xdc, + 0x2ecb4dc778a80f21LL, + 0x66)); +} + +// If the handler elects to skip the root DIE, the dispatcher should +// tell the reader so. +TEST(Dwarf2DIEHandler, SkipRootDIE) { + Sequence s; + MockRootDIEHandler mock_root_handler; + DIEDispatcher die_dispatcher(&mock_root_handler); + + AttributeList mock_attribute_list; + mock_attribute_list.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_string)); + + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0xde8994029fc8b999LL, 0xf4, 0x02, + 0xb00febffa76e2b2bLL, 0x5c)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(mock_root_handler, + StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6, + ContainerEq(mock_attribute_list))) + .InSequence(s) + .WillOnce(Return(false)); + + EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0xde8994029fc8b999LL, + 0xf4, 0x02, + 0xb00febffa76e2b2bLL, 0x5c)); + EXPECT_FALSE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL, + (DwarfTag) 0xb4f98da6, + mock_attribute_list)); + die_dispatcher.EndDIE(0x7d08242b4b510cf2LL); +} + +// If the handler elects to skip the root DIE's children, the +// dispatcher should tell the reader so --- and avoid deleting the +// root handler. +TEST(Dwarf2DIEHandler, SkipRootDIEChildren) { + MockRootDIEHandler mock_root_handler; + DIEDispatcher die_dispatcher(&mock_root_handler); + + AttributeList mock_attribute_list; + + { + InSequence s; + + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0x15d6897480cc65a7LL, 0x26, 0xa0, + 0x09f8bf0767f91675LL, 0xdb)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_root_handler, + StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6, + ContainerEq(mock_attribute_list))) + .WillOnce(Return(true)); + // Please don't tell me about my children. + EXPECT_CALL(mock_root_handler, EndAttributes()) + .WillOnce(Return(false)); + EXPECT_CALL(mock_root_handler, Finish()) + .WillOnce(Return()); + } + + EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x15d6897480cc65a7LL, + 0x26, 0xa0, + 0x09f8bf0767f91675LL, 0xdb)); + EXPECT_TRUE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL, + (DwarfTag) 0xb4f98da6, + mock_attribute_list)); + EXPECT_FALSE(die_dispatcher.StartDIE(0x435150ceedccda18LL, + (DwarfTag) 0xc3a17bba, + mock_attribute_list)); + die_dispatcher.EndDIE(0x435150ceedccda18LL); + die_dispatcher.EndDIE(0x7d08242b4b510cf2LL); +} + +// The dispatcher should pass attribute values through to the die +// handler accurately. +TEST(Dwarf2DIEHandler, PassAttributeValues) { + MockRootDIEHandler mock_root_handler; + DIEDispatcher die_dispatcher(&mock_root_handler); + + AttributeList mock_attribute_list; + mock_attribute_list.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_string)); + const char buffer[10] = { 0x24, 0x24, 0x35, 0x9a, 0xca, + 0xcf, 0xa8, 0x84, 0xa7, 0x18 }; + string str = "\xc8\x26\x2e\x0d\xa4\x9c\x37\xd6\xfb\x1d"; + + // Set expectations. + { + InSequence s; + + // We'll like the compilation unit header. + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0x8d42aed77cfccf3eLL, 0x89, 0xdc, + 0x2ecb4dc778a80f21LL, 0x66)) + .WillOnce(Return(true)); + + // We'll like the root DIE. + EXPECT_CALL(mock_root_handler, + StartRootDIE(0xe2222da01e29f2a9LL, (DwarfTag) 0x9829445c, + ContainerEq(mock_attribute_list))) + .WillOnce(Return(true)); + + // Expect some attribute values. + EXPECT_CALL(mock_root_handler, + ProcessAttributeUnsigned((DwarfAttribute) 0x1cc0bfed, + (DwarfForm) 0x424f1468, + 0xa592571997facda1ULL)) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, + ProcessAttributeSigned((DwarfAttribute) 0x43694dc9, + (DwarfForm) 0xf6f78901L, + 0x92602a4e3bf1f446LL)) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, + ProcessAttributeReference((DwarfAttribute) 0x4033e8cL, + (DwarfForm) 0xf66fbe0bL, + 0x50fddef44734fdecULL)) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, + ProcessAttributeBuffer((DwarfAttribute) 0x25d7e0af, + (DwarfForm) 0xe99a539a, + buffer, sizeof(buffer))) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, + ProcessAttributeString((DwarfAttribute) 0x310ed065, + (DwarfForm) 0x15762fec, + StrEq(str))) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, EndAttributes()) + .WillOnce(Return(true)); + EXPECT_CALL(mock_root_handler, FindChildHandler(_, _, _)) + .Times(0); + EXPECT_CALL(mock_root_handler, Finish()) + .WillOnce(Return()); + } + + // Drive the dispatcher. + + // Report the CU header. + EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL, + 0x89, 0xdc, + 0x2ecb4dc778a80f21LL, + 0x66)); + // Report the root DIE. + EXPECT_TRUE(die_dispatcher.StartDIE(0xe2222da01e29f2a9LL, + (DwarfTag) 0x9829445c, + mock_attribute_list)); + + // Report some attribute values. + die_dispatcher.ProcessAttributeUnsigned(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x1cc0bfed, + (DwarfForm) 0x424f1468, + 0xa592571997facda1ULL); + die_dispatcher.ProcessAttributeSigned(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x43694dc9, + (DwarfForm) 0xf6f78901, + 0x92602a4e3bf1f446LL); + die_dispatcher.ProcessAttributeReference(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x4033e8c, + (DwarfForm) 0xf66fbe0b, + 0x50fddef44734fdecULL); + die_dispatcher.ProcessAttributeBuffer(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x25d7e0af, + (DwarfForm) 0xe99a539a, + buffer, sizeof(buffer)); + die_dispatcher.ProcessAttributeString(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x310ed065, + (DwarfForm) 0x15762fec, + str); + + // Finish the root DIE (and thus the CU). + die_dispatcher.EndDIE(0xe2222da01e29f2a9LL); +} + +TEST(Dwarf2DIEHandler, FindAndSkipChildren) { + MockRootDIEHandler mock_root_handler; + MockDIEHandler *mock_child1_handler = new(MockDIEHandler); + MockDIEHandler *mock_child3_handler = new(MockDIEHandler); + DIEDispatcher die_dispatcher(&mock_root_handler); + + AttributeList root_attribute_list; + root_attribute_list.push_back(make_pair((DwarfAttribute) 0xb01185df, + (DwarfForm) 0xbc97cee8)); + AttributeList child1_attribute_list; + child1_attribute_list.push_back(make_pair((DwarfAttribute) 0x41014e43, + (DwarfForm) 0x63155f4c)); + AttributeList grandchild1_attribute_list; + grandchild1_attribute_list.push_back(make_pair((DwarfAttribute) 0xf72f823c, + (DwarfForm) 0x0ff6a201)); + AttributeList greatgrandchild1_attribute_list; + greatgrandchild1_attribute_list + .push_back(make_pair((DwarfAttribute) 0xbe66e5f0, (DwarfForm) 0xb4b24ff7)); + AttributeList child2_attribute_list; + child1_attribute_list.push_back(make_pair((DwarfAttribute) 0xf22df14c, + (DwarfForm) 0x20676e7d)); + AttributeList child3_attribute_list; + child3_attribute_list.push_back(make_pair((DwarfAttribute) 0xe8bf1201, + (DwarfForm) 0x53a5b7a8)); + + { + InSequence s; + + // We'll like the compilation unit header. + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21, + 0x47dd3c764275a216LL, 0xa5)) + .WillOnce(Return(true)); + + // Root DIE. + { + EXPECT_CALL(mock_root_handler, + StartRootDIE(0x15f0e06bdfe3c372LL, (DwarfTag) 0xf5d60c59, + ContainerEq(root_attribute_list))) + .WillOnce(Return(true)); + EXPECT_CALL(mock_root_handler, + ProcessAttributeSigned((DwarfAttribute) 0xf779a642, + (DwarfForm) 0x2cb63027, + 0x18e744661769d08fLL)) + .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, EndAttributes()) + .WillOnce(Return(true)); + + // First child DIE. + EXPECT_CALL(mock_root_handler, + FindChildHandler(0x149f644f8116fe8cLL, + (DwarfTag) 0xac2cbd8c, + ContainerEq(child1_attribute_list))) + .WillOnce(Return(mock_child1_handler)); + { + EXPECT_CALL(*mock_child1_handler, + ProcessAttributeSigned((DwarfAttribute) 0xa6fd6f65, + (DwarfForm) 0xe4f64c41, + 0x1b04e5444a55fe67LL)) + .WillOnce(Return()); + EXPECT_CALL(*mock_child1_handler, EndAttributes()) + .WillOnce(Return(false)); + // Skip first grandchild DIE and first great-grandchild DIE. + EXPECT_CALL(*mock_child1_handler, Finish()) + .WillOnce(Return()); + } + + // Second child DIE. Root handler will decline to return a handler + // for this child. + EXPECT_CALL(mock_root_handler, + FindChildHandler(0x97412be24875de9dLL, + (DwarfTag) 0x505a068b, + ContainerEq(child2_attribute_list))) + .WillOnce(Return((DIEHandler *) NULL)); + + // Third child DIE. + EXPECT_CALL(mock_root_handler, + FindChildHandler(0x753c964c8ab538aeLL, + (DwarfTag) 0x8c22970e, + ContainerEq(child3_attribute_list))) + .WillOnce(Return(mock_child3_handler)); + { + EXPECT_CALL(*mock_child3_handler, + ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb, + (DwarfForm) 0x610b7ae1, + 0x3ea5c609d7d7560fLL)) + .WillOnce(Return()); + EXPECT_CALL(*mock_child3_handler, EndAttributes()) + .WillOnce(Return(true)); + EXPECT_CALL(*mock_child3_handler, Finish()) + .WillOnce(Return()); + } + + EXPECT_CALL(mock_root_handler, Finish()) + .WillOnce(Return()); + } + } + + + // Drive the dispatcher. + + // Report the CU header. + EXPECT_TRUE(die_dispatcher + .StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21, + 0x47dd3c764275a216LL, 0xa5)); + // Report the root DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0x15f0e06bdfe3c372LL, + (DwarfTag) 0xf5d60c59, + root_attribute_list)); + die_dispatcher.ProcessAttributeSigned(0x15f0e06bdfe3c372LL, + (DwarfAttribute) 0xf779a642, + (DwarfForm) 0x2cb63027, + 0x18e744661769d08fLL); + + // First child DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0x149f644f8116fe8cLL, + (DwarfTag) 0xac2cbd8c, + child1_attribute_list)); + die_dispatcher.ProcessAttributeSigned(0x149f644f8116fe8cLL, + (DwarfAttribute) 0xa6fd6f65, + (DwarfForm) 0xe4f64c41, + 0x1b04e5444a55fe67LL); + + // First grandchild DIE. Will be skipped. + { + EXPECT_FALSE(die_dispatcher.StartDIE(0xd68de1ee0bd29419LL, + (DwarfTag) 0x22f05a15, + grandchild1_attribute_list)); + // First great-grandchild DIE. Will be skipped without being + // mentioned to any handler. + { + EXPECT_FALSE(die_dispatcher + .StartDIE(0xb3076285d25cac25LL, + (DwarfTag) 0xcff4061b, + greatgrandchild1_attribute_list)); + die_dispatcher.EndDIE(0xb3076285d25cac25LL); + } + die_dispatcher.EndDIE(0xd68de1ee0bd29419LL); + } + die_dispatcher.EndDIE(0x149f644f8116fe8cLL); + } + + // Second child DIE. Root handler will decline to find a handler for it. + { + EXPECT_FALSE(die_dispatcher.StartDIE(0x97412be24875de9dLL, + (DwarfTag) 0x505a068b, + child2_attribute_list)); + die_dispatcher.EndDIE(0x97412be24875de9dLL); + } + + // Third child DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0x753c964c8ab538aeLL, + (DwarfTag) 0x8c22970e, + child3_attribute_list)); + die_dispatcher.ProcessAttributeSigned(0x753c964c8ab538aeLL, + (DwarfAttribute) 0x4e2b7cfb, + (DwarfForm) 0x610b7ae1, + 0x3ea5c609d7d7560fLL); + die_dispatcher.EndDIE(0x753c964c8ab538aeLL); + } + + // Finish the root DIE (and thus the CU). + die_dispatcher.EndDIE(0x15f0e06bdfe3c372LL); + } +} + +// The DIEDispatcher destructor is supposed to delete all handlers on +// the stack, except for the root. +TEST(Dwarf2DIEHandler, FreeHandlersOnStack) { + MockRootDIEHandler mock_root_handler; + MockDIEHandler *mock_child_handler = new(MockDIEHandler); + MockDIEHandler *mock_grandchild_handler = new(MockDIEHandler); + AttributeList empty_attribute_list; + + { + InSequence s; + + // We'll like the compilation unit header. + EXPECT_CALL(mock_root_handler, + StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89, + 0x76d392ff393ddda2LL, 0xbf)) + .WillOnce(Return(true)); + + // Root DIE. + { + EXPECT_CALL(mock_root_handler, + StartRootDIE(0xbf13b761691ddc91LL, (DwarfTag) 0x98980361, + ContainerEq(empty_attribute_list))) + .WillOnce(Return(true)); + EXPECT_CALL(mock_root_handler, EndAttributes()) + .WillOnce(Return(true)); + + // Child DIE. + EXPECT_CALL(mock_root_handler, + FindChildHandler(0x058f09240c5fc8c9LL, + (DwarfTag) 0x898bf0d0, + ContainerEq(empty_attribute_list))) + .WillOnce(Return(mock_child_handler)); + { + EXPECT_CALL(*mock_child_handler, EndAttributes()) + .WillOnce(Return(true)); + + // Grandchild DIE. + EXPECT_CALL(*mock_child_handler, + FindChildHandler(0x32dc00c9945dc0c8LL, + (DwarfTag) 0x2802d007, + ContainerEq(empty_attribute_list))) + .WillOnce(Return(mock_grandchild_handler)); + { + EXPECT_CALL(*mock_grandchild_handler, + ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb, + (DwarfForm) 0x610b7ae1, + 0x3ea5c609d7d7560fLL)) + .WillOnce(Return()); + + // At this point, we abandon the traversal, so none of the + // usual stuff should get called. + EXPECT_CALL(*mock_grandchild_handler, EndAttributes()) + .Times(0); + EXPECT_CALL(*mock_grandchild_handler, Finish()) + .Times(0); + } + + EXPECT_CALL(*mock_child_handler, Finish()) + .Times(0); + } + + EXPECT_CALL(mock_root_handler, Finish()) + .Times(0); + } + } + + // The dispatcher. + DIEDispatcher die_dispatcher(&mock_root_handler); + + // Report the CU header. + EXPECT_TRUE(die_dispatcher + .StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89, + 0x76d392ff393ddda2LL, 0xbf)); + // Report the root DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0xbf13b761691ddc91LL, + (DwarfTag) 0x98980361, + empty_attribute_list)); + + // Child DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0x058f09240c5fc8c9LL, + (DwarfTag) 0x898bf0d0, + empty_attribute_list)); + + // Grandchild DIE. + { + EXPECT_TRUE(die_dispatcher.StartDIE(0x32dc00c9945dc0c8LL, + (DwarfTag) 0x2802d007, + empty_attribute_list)); + die_dispatcher.ProcessAttributeSigned(0x32dc00c9945dc0c8LL, + (DwarfAttribute) 0x4e2b7cfb, + (DwarfForm) 0x610b7ae1, + 0x3ea5c609d7d7560fLL); + + // Stop the traversal abruptly, so that there will still be + // handlers on the stack when the dispatcher is destructed. + + // No EndDIE call... + } + // No EndDIE call... + } + // No EndDIE call... + } +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2enums.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2enums.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2enums.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2enums.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,613 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef COMMON_DWARF_DWARF2ENUMS_H__ +#define COMMON_DWARF_DWARF2ENUMS_H__ + +namespace dwarf2reader { + +// These enums do not follow the google3 style only because they are +// known universally (specs, other implementations) by the names in +// exactly this capitalization. +// Tag names and codes. +enum DwarfTag { + DW_TAG_padding = 0x00, + DW_TAG_array_type = 0x01, + DW_TAG_class_type = 0x02, + DW_TAG_entry_point = 0x03, + DW_TAG_enumeration_type = 0x04, + DW_TAG_formal_parameter = 0x05, + DW_TAG_imported_declaration = 0x08, + DW_TAG_label = 0x0a, + DW_TAG_lexical_block = 0x0b, + DW_TAG_member = 0x0d, + DW_TAG_pointer_type = 0x0f, + DW_TAG_reference_type = 0x10, + DW_TAG_compile_unit = 0x11, + DW_TAG_string_type = 0x12, + DW_TAG_structure_type = 0x13, + DW_TAG_subroutine_type = 0x15, + DW_TAG_typedef = 0x16, + DW_TAG_union_type = 0x17, + DW_TAG_unspecified_parameters = 0x18, + DW_TAG_variant = 0x19, + DW_TAG_common_block = 0x1a, + DW_TAG_common_inclusion = 0x1b, + DW_TAG_inheritance = 0x1c, + DW_TAG_inlined_subroutine = 0x1d, + DW_TAG_module = 0x1e, + DW_TAG_ptr_to_member_type = 0x1f, + DW_TAG_set_type = 0x20, + DW_TAG_subrange_type = 0x21, + DW_TAG_with_stmt = 0x22, + DW_TAG_access_declaration = 0x23, + DW_TAG_base_type = 0x24, + DW_TAG_catch_block = 0x25, + DW_TAG_const_type = 0x26, + DW_TAG_constant = 0x27, + DW_TAG_enumerator = 0x28, + DW_TAG_file_type = 0x29, + DW_TAG_friend = 0x2a, + DW_TAG_namelist = 0x2b, + DW_TAG_namelist_item = 0x2c, + DW_TAG_packed_type = 0x2d, + DW_TAG_subprogram = 0x2e, + DW_TAG_template_type_param = 0x2f, + DW_TAG_template_value_param = 0x30, + DW_TAG_thrown_type = 0x31, + DW_TAG_try_block = 0x32, + DW_TAG_variant_part = 0x33, + DW_TAG_variable = 0x34, + DW_TAG_volatile_type = 0x35, + // DWARF 3. + DW_TAG_dwarf_procedure = 0x36, + DW_TAG_restrict_type = 0x37, + DW_TAG_interface_type = 0x38, + DW_TAG_namespace = 0x39, + DW_TAG_imported_module = 0x3a, + DW_TAG_unspecified_type = 0x3b, + DW_TAG_partial_unit = 0x3c, + DW_TAG_imported_unit = 0x3d, + // SGI/MIPS Extensions. + DW_TAG_MIPS_loop = 0x4081, + // HP extensions. See: + // ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz + DW_TAG_HP_array_descriptor = 0x4090, + // GNU extensions. + DW_TAG_format_label = 0x4101, // For FORTRAN 77 and Fortran 90. + DW_TAG_function_template = 0x4102, // For C++. + DW_TAG_class_template = 0x4103, // For C++. + DW_TAG_GNU_BINCL = 0x4104, + DW_TAG_GNU_EINCL = 0x4105, + // Extensions for UPC. See: http://upc.gwu.edu/~upc. + DW_TAG_upc_shared_type = 0x8765, + DW_TAG_upc_strict_type = 0x8766, + DW_TAG_upc_relaxed_type = 0x8767, + // PGI (STMicroelectronics) extensions. No documentation available. + DW_TAG_PGI_kanji_type = 0xA000, + DW_TAG_PGI_interface_block = 0xA020 +}; + + +enum DwarfHasChild { + DW_children_no = 0, + DW_children_yes = 1 +}; + +// Form names and codes. +enum DwarfForm { + DW_FORM_addr = 0x01, + DW_FORM_block2 = 0x03, + DW_FORM_block4 = 0x04, + DW_FORM_data2 = 0x05, + DW_FORM_data4 = 0x06, + DW_FORM_data8 = 0x07, + DW_FORM_string = 0x08, + DW_FORM_block = 0x09, + DW_FORM_block1 = 0x0a, + DW_FORM_data1 = 0x0b, + DW_FORM_flag = 0x0c, + DW_FORM_sdata = 0x0d, + DW_FORM_strp = 0x0e, + DW_FORM_udata = 0x0f, + DW_FORM_ref_addr = 0x10, + DW_FORM_ref1 = 0x11, + DW_FORM_ref2 = 0x12, + DW_FORM_ref4 = 0x13, + DW_FORM_ref8 = 0x14, + DW_FORM_ref_udata = 0x15, + DW_FORM_indirect = 0x16 +}; + +// Attribute names and codes +enum DwarfAttribute { + DW_AT_sibling = 0x01, + DW_AT_location = 0x02, + DW_AT_name = 0x03, + DW_AT_ordering = 0x09, + DW_AT_subscr_data = 0x0a, + DW_AT_byte_size = 0x0b, + DW_AT_bit_offset = 0x0c, + DW_AT_bit_size = 0x0d, + DW_AT_element_list = 0x0f, + DW_AT_stmt_list = 0x10, + DW_AT_low_pc = 0x11, + DW_AT_high_pc = 0x12, + DW_AT_language = 0x13, + DW_AT_member = 0x14, + DW_AT_discr = 0x15, + DW_AT_discr_value = 0x16, + DW_AT_visibility = 0x17, + DW_AT_import = 0x18, + DW_AT_string_length = 0x19, + DW_AT_common_reference = 0x1a, + DW_AT_comp_dir = 0x1b, + DW_AT_const_value = 0x1c, + DW_AT_containing_type = 0x1d, + DW_AT_default_value = 0x1e, + DW_AT_inline = 0x20, + DW_AT_is_optional = 0x21, + DW_AT_lower_bound = 0x22, + DW_AT_producer = 0x25, + DW_AT_prototyped = 0x27, + DW_AT_return_addr = 0x2a, + DW_AT_start_scope = 0x2c, + DW_AT_stride_size = 0x2e, + DW_AT_upper_bound = 0x2f, + DW_AT_abstract_origin = 0x31, + DW_AT_accessibility = 0x32, + DW_AT_address_class = 0x33, + DW_AT_artificial = 0x34, + DW_AT_base_types = 0x35, + DW_AT_calling_convention = 0x36, + DW_AT_count = 0x37, + DW_AT_data_member_location = 0x38, + DW_AT_decl_column = 0x39, + DW_AT_decl_file = 0x3a, + DW_AT_decl_line = 0x3b, + DW_AT_declaration = 0x3c, + DW_AT_discr_list = 0x3d, + DW_AT_encoding = 0x3e, + DW_AT_external = 0x3f, + DW_AT_frame_base = 0x40, + DW_AT_friend = 0x41, + DW_AT_identifier_case = 0x42, + DW_AT_macro_info = 0x43, + DW_AT_namelist_items = 0x44, + DW_AT_priority = 0x45, + DW_AT_segment = 0x46, + DW_AT_specification = 0x47, + DW_AT_static_link = 0x48, + DW_AT_type = 0x49, + DW_AT_use_location = 0x4a, + DW_AT_variable_parameter = 0x4b, + DW_AT_virtuality = 0x4c, + DW_AT_vtable_elem_location = 0x4d, + // DWARF 3 values. + DW_AT_allocated = 0x4e, + DW_AT_associated = 0x4f, + DW_AT_data_location = 0x50, + DW_AT_stride = 0x51, + DW_AT_entry_pc = 0x52, + DW_AT_use_UTF8 = 0x53, + DW_AT_extension = 0x54, + DW_AT_ranges = 0x55, + DW_AT_trampoline = 0x56, + DW_AT_call_column = 0x57, + DW_AT_call_file = 0x58, + DW_AT_call_line = 0x59, + // SGI/MIPS extensions. + DW_AT_MIPS_fde = 0x2001, + DW_AT_MIPS_loop_begin = 0x2002, + DW_AT_MIPS_tail_loop_begin = 0x2003, + DW_AT_MIPS_epilog_begin = 0x2004, + DW_AT_MIPS_loop_unroll_factor = 0x2005, + DW_AT_MIPS_software_pipeline_depth = 0x2006, + DW_AT_MIPS_linkage_name = 0x2007, + DW_AT_MIPS_stride = 0x2008, + DW_AT_MIPS_abstract_name = 0x2009, + DW_AT_MIPS_clone_origin = 0x200a, + DW_AT_MIPS_has_inlines = 0x200b, + // HP extensions. + DW_AT_HP_block_index = 0x2000, + DW_AT_HP_unmodifiable = 0x2001, // Same as DW_AT_MIPS_fde. + DW_AT_HP_actuals_stmt_list = 0x2010, + DW_AT_HP_proc_per_section = 0x2011, + DW_AT_HP_raw_data_ptr = 0x2012, + DW_AT_HP_pass_by_reference = 0x2013, + DW_AT_HP_opt_level = 0x2014, + DW_AT_HP_prof_version_id = 0x2015, + DW_AT_HP_opt_flags = 0x2016, + DW_AT_HP_cold_region_low_pc = 0x2017, + DW_AT_HP_cold_region_high_pc = 0x2018, + DW_AT_HP_all_variables_modifiable = 0x2019, + DW_AT_HP_linkage_name = 0x201a, + DW_AT_HP_prof_flags = 0x201b, // In comp unit of procs_info for -g. + // GNU extensions. + DW_AT_sf_names = 0x2101, + DW_AT_src_info = 0x2102, + DW_AT_mac_info = 0x2103, + DW_AT_src_coords = 0x2104, + DW_AT_body_begin = 0x2105, + DW_AT_body_end = 0x2106, + DW_AT_GNU_vector = 0x2107, + // VMS extensions. + DW_AT_VMS_rtnbeg_pd_address = 0x2201, + // UPC extension. + DW_AT_upc_threads_scaled = 0x3210, + // PGI (STMicroelectronics) extensions. + DW_AT_PGI_lbase = 0x3a00, + DW_AT_PGI_soffset = 0x3a01, + DW_AT_PGI_lstride = 0x3a02 +}; + + +// Line number opcodes. +enum DwarfLineNumberOps { + DW_LNS_extended_op = 0, + DW_LNS_copy = 1, + DW_LNS_advance_pc = 2, + DW_LNS_advance_line = 3, + DW_LNS_set_file = 4, + DW_LNS_set_column = 5, + DW_LNS_negate_stmt = 6, + DW_LNS_set_basic_block = 7, + DW_LNS_const_add_pc = 8, + DW_LNS_fixed_advance_pc = 9, + // DWARF 3. + DW_LNS_set_prologue_end = 10, + DW_LNS_set_epilogue_begin = 11, + DW_LNS_set_isa = 12 +}; + +// Line number extended opcodes. +enum DwarfLineNumberExtendedOps { + DW_LNE_end_sequence = 1, + DW_LNE_set_address = 2, + DW_LNE_define_file = 3, + // HP extensions. + DW_LNE_HP_negate_is_UV_update = 0x11, + DW_LNE_HP_push_context = 0x12, + DW_LNE_HP_pop_context = 0x13, + DW_LNE_HP_set_file_line_column = 0x14, + DW_LNE_HP_set_routine_name = 0x15, + DW_LNE_HP_set_sequence = 0x16, + DW_LNE_HP_negate_post_semantics = 0x17, + DW_LNE_HP_negate_function_exit = 0x18, + DW_LNE_HP_negate_front_end_logical = 0x19, + DW_LNE_HP_define_proc = 0x20 +}; + +// Type encoding names and codes +enum DwarfEncoding { + DW_ATE_address =0x1, + DW_ATE_boolean =0x2, + DW_ATE_complex_float =0x3, + DW_ATE_float =0x4, + DW_ATE_signed =0x5, + DW_ATE_signed_char =0x6, + DW_ATE_unsigned =0x7, + DW_ATE_unsigned_char =0x8, + // DWARF3/DWARF3f + DW_ATE_imaginary_float =0x9, + DW_ATE_packed_decimal =0xa, + DW_ATE_numeric_string =0xb, + DW_ATE_edited =0xc, + DW_ATE_signed_fixed =0xd, + DW_ATE_unsigned_fixed =0xe, + DW_ATE_decimal_float =0xf, + DW_ATE_lo_user =0x80, + DW_ATE_hi_user =0xff +}; + +// Location virtual machine opcodes +enum DwarfOpcode { + DW_OP_addr =0x03, + DW_OP_deref =0x06, + DW_OP_const1u =0x08, + DW_OP_const1s =0x09, + DW_OP_const2u =0x0a, + DW_OP_const2s =0x0b, + DW_OP_const4u =0x0c, + DW_OP_const4s =0x0d, + DW_OP_const8u =0x0e, + DW_OP_const8s =0x0f, + DW_OP_constu =0x10, + DW_OP_consts =0x11, + DW_OP_dup =0x12, + DW_OP_drop =0x13, + DW_OP_over =0x14, + DW_OP_pick =0x15, + DW_OP_swap =0x16, + DW_OP_rot =0x17, + DW_OP_xderef =0x18, + DW_OP_abs =0x19, + DW_OP_and =0x1a, + DW_OP_div =0x1b, + DW_OP_minus =0x1c, + DW_OP_mod =0x1d, + DW_OP_mul =0x1e, + DW_OP_neg =0x1f, + DW_OP_not =0x20, + DW_OP_or =0x21, + DW_OP_plus =0x22, + DW_OP_plus_uconst =0x23, + DW_OP_shl =0x24, + DW_OP_shr =0x25, + DW_OP_shra =0x26, + DW_OP_xor =0x27, + DW_OP_bra =0x28, + DW_OP_eq =0x29, + DW_OP_ge =0x2a, + DW_OP_gt =0x2b, + DW_OP_le =0x2c, + DW_OP_lt =0x2d, + DW_OP_ne =0x2e, + DW_OP_skip =0x2f, + DW_OP_lit0 =0x30, + DW_OP_lit1 =0x31, + DW_OP_lit2 =0x32, + DW_OP_lit3 =0x33, + DW_OP_lit4 =0x34, + DW_OP_lit5 =0x35, + DW_OP_lit6 =0x36, + DW_OP_lit7 =0x37, + DW_OP_lit8 =0x38, + DW_OP_lit9 =0x39, + DW_OP_lit10 =0x3a, + DW_OP_lit11 =0x3b, + DW_OP_lit12 =0x3c, + DW_OP_lit13 =0x3d, + DW_OP_lit14 =0x3e, + DW_OP_lit15 =0x3f, + DW_OP_lit16 =0x40, + DW_OP_lit17 =0x41, + DW_OP_lit18 =0x42, + DW_OP_lit19 =0x43, + DW_OP_lit20 =0x44, + DW_OP_lit21 =0x45, + DW_OP_lit22 =0x46, + DW_OP_lit23 =0x47, + DW_OP_lit24 =0x48, + DW_OP_lit25 =0x49, + DW_OP_lit26 =0x4a, + DW_OP_lit27 =0x4b, + DW_OP_lit28 =0x4c, + DW_OP_lit29 =0x4d, + DW_OP_lit30 =0x4e, + DW_OP_lit31 =0x4f, + DW_OP_reg0 =0x50, + DW_OP_reg1 =0x51, + DW_OP_reg2 =0x52, + DW_OP_reg3 =0x53, + DW_OP_reg4 =0x54, + DW_OP_reg5 =0x55, + DW_OP_reg6 =0x56, + DW_OP_reg7 =0x57, + DW_OP_reg8 =0x58, + DW_OP_reg9 =0x59, + DW_OP_reg10 =0x5a, + DW_OP_reg11 =0x5b, + DW_OP_reg12 =0x5c, + DW_OP_reg13 =0x5d, + DW_OP_reg14 =0x5e, + DW_OP_reg15 =0x5f, + DW_OP_reg16 =0x60, + DW_OP_reg17 =0x61, + DW_OP_reg18 =0x62, + DW_OP_reg19 =0x63, + DW_OP_reg20 =0x64, + DW_OP_reg21 =0x65, + DW_OP_reg22 =0x66, + DW_OP_reg23 =0x67, + DW_OP_reg24 =0x68, + DW_OP_reg25 =0x69, + DW_OP_reg26 =0x6a, + DW_OP_reg27 =0x6b, + DW_OP_reg28 =0x6c, + DW_OP_reg29 =0x6d, + DW_OP_reg30 =0x6e, + DW_OP_reg31 =0x6f, + DW_OP_breg0 =0x70, + DW_OP_breg1 =0x71, + DW_OP_breg2 =0x72, + DW_OP_breg3 =0x73, + DW_OP_breg4 =0x74, + DW_OP_breg5 =0x75, + DW_OP_breg6 =0x76, + DW_OP_breg7 =0x77, + DW_OP_breg8 =0x78, + DW_OP_breg9 =0x79, + DW_OP_breg10 =0x7a, + DW_OP_breg11 =0x7b, + DW_OP_breg12 =0x7c, + DW_OP_breg13 =0x7d, + DW_OP_breg14 =0x7e, + DW_OP_breg15 =0x7f, + DW_OP_breg16 =0x80, + DW_OP_breg17 =0x81, + DW_OP_breg18 =0x82, + DW_OP_breg19 =0x83, + DW_OP_breg20 =0x84, + DW_OP_breg21 =0x85, + DW_OP_breg22 =0x86, + DW_OP_breg23 =0x87, + DW_OP_breg24 =0x88, + DW_OP_breg25 =0x89, + DW_OP_breg26 =0x8a, + DW_OP_breg27 =0x8b, + DW_OP_breg28 =0x8c, + DW_OP_breg29 =0x8d, + DW_OP_breg30 =0x8e, + DW_OP_breg31 =0x8f, + DW_OP_regX =0x90, + DW_OP_fbreg =0x91, + DW_OP_bregX =0x92, + DW_OP_piece =0x93, + DW_OP_deref_size =0x94, + DW_OP_xderef_size =0x95, + DW_OP_nop =0x96, + // DWARF3/DWARF3f + DW_OP_push_object_address =0x97, + DW_OP_call2 =0x98, + DW_OP_call4 =0x99, + DW_OP_call_ref =0x9a, + DW_OP_form_tls_address =0x9b, + DW_OP_call_frame_cfa =0x9c, + DW_OP_bit_piece =0x9d, + DW_OP_lo_user =0xe0, + DW_OP_hi_user =0xff, + // GNU extensions + DW_OP_GNU_push_tls_address =0xe0 +}; + +// Source languages. These are values for DW_AT_language. +enum DwarfLanguage + { + DW_LANG_none =0x0000, + DW_LANG_C89 =0x0001, + DW_LANG_C =0x0002, + DW_LANG_Ada83 =0x0003, + DW_LANG_C_plus_plus =0x0004, + DW_LANG_Cobol74 =0x0005, + DW_LANG_Cobol85 =0x0006, + DW_LANG_Fortran77 =0x0007, + DW_LANG_Fortran90 =0x0008, + DW_LANG_Pascal83 =0x0009, + DW_LANG_Modula2 =0x000a, + DW_LANG_Java =0x000b, + DW_LANG_C99 =0x000c, + DW_LANG_Ada95 =0x000d, + DW_LANG_Fortran95 =0x000e, + DW_LANG_PLI =0x000f, + DW_LANG_ObjC =0x0010, + DW_LANG_ObjC_plus_plus =0x0011, + DW_LANG_UPC =0x0012, + DW_LANG_D =0x0013, + // Implementation-defined language code range. + DW_LANG_lo_user = 0x8000, + DW_LANG_hi_user = 0xffff, + + // Extensions. + + // MIPS assembly language. The GNU toolchain uses this for all + // assembly languages, since there's no generic DW_LANG_ value for that. + // See include/dwarf2.h in the binutils, gdb, or gcc source trees. + DW_LANG_Mips_Assembler =0x8001, + DW_LANG_Upc =0x8765 // Unified Parallel C + }; + +// Inline codes. These are values for DW_AT_inline. +enum DwarfInline { + DW_INL_not_inlined =0x0, + DW_INL_inlined =0x1, + DW_INL_declared_not_inlined =0x2, + DW_INL_declared_inlined =0x3 +}; + +// Call Frame Info instructions. +enum DwarfCFI + { + DW_CFA_advance_loc = 0x40, + DW_CFA_offset = 0x80, + DW_CFA_restore = 0xc0, + DW_CFA_nop = 0x00, + DW_CFA_set_loc = 0x01, + DW_CFA_advance_loc1 = 0x02, + DW_CFA_advance_loc2 = 0x03, + DW_CFA_advance_loc4 = 0x04, + DW_CFA_offset_extended = 0x05, + DW_CFA_restore_extended = 0x06, + DW_CFA_undefined = 0x07, + DW_CFA_same_value = 0x08, + DW_CFA_register = 0x09, + DW_CFA_remember_state = 0x0a, + DW_CFA_restore_state = 0x0b, + DW_CFA_def_cfa = 0x0c, + DW_CFA_def_cfa_register = 0x0d, + DW_CFA_def_cfa_offset = 0x0e, + DW_CFA_def_cfa_expression = 0x0f, + DW_CFA_expression = 0x10, + DW_CFA_offset_extended_sf = 0x11, + DW_CFA_def_cfa_sf = 0x12, + DW_CFA_def_cfa_offset_sf = 0x13, + DW_CFA_val_offset = 0x14, + DW_CFA_val_offset_sf = 0x15, + DW_CFA_val_expression = 0x16, + + // Opcodes in this range are reserved for user extensions. + DW_CFA_lo_user = 0x1c, + DW_CFA_hi_user = 0x3f, + + // SGI/MIPS specific. + DW_CFA_MIPS_advance_loc8 = 0x1d, + + // GNU extensions. + DW_CFA_GNU_window_save = 0x2d, + DW_CFA_GNU_args_size = 0x2e, + DW_CFA_GNU_negative_offset_extended = 0x2f + }; + +// Exception handling frame description pointer formats, as described +// by the Linux Standard Base Core Specification 4.0, section 11.5, +// DWARF Extensions. +enum DwarfPointerEncoding + { + DW_EH_PE_absptr = 0x00, + DW_EH_PE_omit = 0xff, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + + // The GNU toolchain sources define this enum value as well, + // simply to help classify the lower nybble values into signed and + // unsigned groups. + DW_EH_PE_signed = 0x08, + + // This is not documented in LSB 4.0, but it is used in both the + // Linux and OS X toolchains. It can be added to any other + // encoding (except DW_EH_PE_aligned), and indicates that the + // encoded value represents the address at which the true address + // is stored, not the true address itself. + DW_EH_PE_indirect = 0x80 + }; + +} // namespace dwarf2reader +#endif // COMMON_DWARF_DWARF2ENUMS_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,2353 @@ +// Copyright (c) 2010 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// CFI reader author: Jim Blandy + +// Implementation of dwarf2reader::LineInfo, dwarf2reader::CompilationUnit, +// and dwarf2reader::CallFrameInfo. See dwarf2reader.h for details. + +#include +#include +#include +#include +#include +#include +#include + +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/dwarf2reader.h" +#include "common/dwarf/bytereader.h" +#include "common/dwarf/line_state_machine.h" + +namespace dwarf2reader { + +CompilationUnit::CompilationUnit(const SectionMap& sections, uint64 offset, + ByteReader* reader, Dwarf2Handler* handler) + : offset_from_section_start_(offset), reader_(reader), + sections_(sections), handler_(handler), abbrevs_(NULL), + string_buffer_(NULL), string_buffer_length_(0) {} + +// Read a DWARF2/3 abbreviation section. +// Each abbrev consists of a abbreviation number, a tag, a byte +// specifying whether the tag has children, and a list of +// attribute/form pairs. +// The list of forms is terminated by a 0 for the attribute, and a +// zero for the form. The entire abbreviation section is terminated +// by a zero for the code. + +void CompilationUnit::ReadAbbrevs() { + if (abbrevs_) + return; + + // First get the debug_abbrev section. ".debug_abbrev" is the name + // recommended in the DWARF spec, and used on Linux; + // "__debug_abbrev" is the name used in Mac OS X Mach-O files. + SectionMap::const_iterator iter = sections_.find(".debug_abbrev"); + if (iter == sections_.end()) + iter = sections_.find("__debug_abbrev"); + assert(iter != sections_.end()); + + abbrevs_ = new vector; + abbrevs_->resize(1); + + // The only way to check whether we are reading over the end of the + // buffer would be to first compute the size of the leb128 data by + // reading it, then go back and read it again. + const char* abbrev_start = iter->second.first + + header_.abbrev_offset; + const char* abbrevptr = abbrev_start; +#ifndef NDEBUG + const uint64 abbrev_length = iter->second.second - header_.abbrev_offset; +#endif + + while (1) { + CompilationUnit::Abbrev abbrev; + size_t len; + const uint32 number = reader_->ReadUnsignedLEB128(abbrevptr, &len); + + if (number == 0) + break; + abbrev.number = number; + abbrevptr += len; + + assert(abbrevptr < abbrev_start + abbrev_length); + const uint32 tag = reader_->ReadUnsignedLEB128(abbrevptr, &len); + abbrevptr += len; + abbrev.tag = static_cast(tag); + + assert(abbrevptr < abbrev_start + abbrev_length); + abbrev.has_children = reader_->ReadOneByte(abbrevptr); + abbrevptr += 1; + + assert(abbrevptr < abbrev_start + abbrev_length); + + while (1) { + const uint32 nametemp = reader_->ReadUnsignedLEB128(abbrevptr, &len); + abbrevptr += len; + + assert(abbrevptr < abbrev_start + abbrev_length); + const uint32 formtemp = reader_->ReadUnsignedLEB128(abbrevptr, &len); + abbrevptr += len; + if (nametemp == 0 && formtemp == 0) + break; + + const enum DwarfAttribute name = + static_cast(nametemp); + const enum DwarfForm form = static_cast(formtemp); + abbrev.attributes.push_back(make_pair(name, form)); + } + assert(abbrev.number == abbrevs_->size()); + abbrevs_->push_back(abbrev); + } +} + +// Skips a single DIE's attributes. +const char* CompilationUnit::SkipDIE(const char* start, + const Abbrev& abbrev) { + for (AttributeList::const_iterator i = abbrev.attributes.begin(); + i != abbrev.attributes.end(); + i++) { + start = SkipAttribute(start, i->second); + } + return start; +} + +// Skips a single attribute form's data. +const char* CompilationUnit::SkipAttribute(const char* start, + enum DwarfForm form) { + size_t len; + + switch (form) { + case DW_FORM_indirect: + form = static_cast(reader_->ReadUnsignedLEB128(start, + &len)); + start += len; + return SkipAttribute(start, form); + break; + + case DW_FORM_data1: + case DW_FORM_flag: + case DW_FORM_ref1: + return start + 1; + break; + case DW_FORM_ref2: + case DW_FORM_data2: + return start + 2; + break; + case DW_FORM_ref4: + case DW_FORM_data4: + return start + 4; + break; + case DW_FORM_ref8: + case DW_FORM_data8: + return start + 8; + break; + case DW_FORM_string: + return start + strlen(start) + 1; + break; + case DW_FORM_udata: + case DW_FORM_ref_udata: + reader_->ReadUnsignedLEB128(start, &len); + return start + len; + break; + + case DW_FORM_sdata: + reader_->ReadSignedLEB128(start, &len); + return start + len; + break; + case DW_FORM_addr: + return start + reader_->AddressSize(); + break; + case DW_FORM_ref_addr: + // DWARF2 and 3 differ on whether ref_addr is address size or + // offset size. + assert(header_.version == 2 || header_.version == 3); + if (header_.version == 2) { + return start + reader_->AddressSize(); + } else if (header_.version == 3) { + return start + reader_->OffsetSize(); + } + break; + + case DW_FORM_block1: + return start + 1 + reader_->ReadOneByte(start); + break; + case DW_FORM_block2: + return start + 2 + reader_->ReadTwoBytes(start); + break; + case DW_FORM_block4: + return start + 4 + reader_->ReadFourBytes(start); + break; + case DW_FORM_block: { + uint64 size = reader_->ReadUnsignedLEB128(start, &len); + return start + size + len; + } + break; + case DW_FORM_strp: + return start + reader_->OffsetSize(); + break; + default: + fprintf(stderr,"Unhandled form type"); + } + fprintf(stderr,"Unhandled form type"); + return NULL; +} + +// Read a DWARF2/3 header. +// The header is variable length in DWARF3 (and DWARF2 as extended by +// most compilers), and consists of an length field, a version number, +// the offset in the .debug_abbrev section for our abbrevs, and an +// address size. +void CompilationUnit::ReadHeader() { + const char* headerptr = buffer_; + size_t initial_length_size; + + assert(headerptr + 4 < buffer_ + buffer_length_); + const uint64 initial_length + = reader_->ReadInitialLength(headerptr, &initial_length_size); + headerptr += initial_length_size; + header_.length = initial_length; + + assert(headerptr + 2 < buffer_ + buffer_length_); + header_.version = reader_->ReadTwoBytes(headerptr); + headerptr += 2; + + assert(headerptr + reader_->OffsetSize() < buffer_ + buffer_length_); + header_.abbrev_offset = reader_->ReadOffset(headerptr); + headerptr += reader_->OffsetSize(); + + assert(headerptr + 1 < buffer_ + buffer_length_); + header_.address_size = reader_->ReadOneByte(headerptr); + reader_->SetAddressSize(header_.address_size); + headerptr += 1; + + after_header_ = headerptr; + + // This check ensures that we don't have to do checking during the + // reading of DIEs. header_.length does not include the size of the + // initial length. + assert(buffer_ + initial_length_size + header_.length <= + buffer_ + buffer_length_); +} + +uint64 CompilationUnit::Start() { + // First get the debug_info section. ".debug_info" is the name + // recommended in the DWARF spec, and used on Linux; "__debug_info" + // is the name used in Mac OS X Mach-O files. + SectionMap::const_iterator iter = sections_.find(".debug_info"); + if (iter == sections_.end()) + iter = sections_.find("__debug_info"); + assert(iter != sections_.end()); + + // Set up our buffer + buffer_ = iter->second.first + offset_from_section_start_; + buffer_length_ = iter->second.second - offset_from_section_start_; + + // Read the header + ReadHeader(); + + // Figure out the real length from the end of the initial length to + // the end of the compilation unit, since that is the value we + // return. + uint64 ourlength = header_.length; + if (reader_->OffsetSize() == 8) + ourlength += 12; + else + ourlength += 4; + + // See if the user wants this compilation unit, and if not, just return. + if (!handler_->StartCompilationUnit(offset_from_section_start_, + reader_->AddressSize(), + reader_->OffsetSize(), + header_.length, + header_.version)) + return ourlength; + + // Otherwise, continue by reading our abbreviation entries. + ReadAbbrevs(); + + // Set the string section if we have one. ".debug_str" is the name + // recommended in the DWARF spec, and used on Linux; "__debug_str" + // is the name used in Mac OS X Mach-O files. + iter = sections_.find(".debug_str"); + if (iter == sections_.end()) + iter = sections_.find("__debug_str"); + if (iter != sections_.end()) { + string_buffer_ = iter->second.first; + string_buffer_length_ = iter->second.second; + } + + // Now that we have our abbreviations, start processing DIE's. + ProcessDIEs(); + + return ourlength; +} + +// If one really wanted, you could merge SkipAttribute and +// ProcessAttribute +// This is all boring data manipulation and calling of the handler. +const char* CompilationUnit::ProcessAttribute( + uint64 dieoffset, const char* start, enum DwarfAttribute attr, + enum DwarfForm form) { + size_t len; + + switch (form) { + // DW_FORM_indirect is never used because it is such a space + // waster. + case DW_FORM_indirect: + form = static_cast(reader_->ReadUnsignedLEB128(start, + &len)); + start += len; + return ProcessAttribute(dieoffset, start, attr, form); + break; + + case DW_FORM_data1: + case DW_FORM_flag: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadOneByte(start)); + return start + 1; + break; + case DW_FORM_data2: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadTwoBytes(start)); + return start + 2; + break; + case DW_FORM_data4: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadFourBytes(start)); + return start + 4; + break; + case DW_FORM_data8: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadEightBytes(start)); + return start + 8; + break; + case DW_FORM_string: { + const char* str = start; + handler_->ProcessAttributeString(dieoffset, attr, form, + str); + return start + strlen(str) + 1; + } + break; + case DW_FORM_udata: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadUnsignedLEB128(start, + &len)); + return start + len; + break; + + case DW_FORM_sdata: + handler_->ProcessAttributeSigned(dieoffset, attr, form, + reader_->ReadSignedLEB128(start, &len)); + return start + len; + break; + case DW_FORM_addr: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadAddress(start)); + return start + reader_->AddressSize(); + break; + + case DW_FORM_ref1: + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadOneByte(start) + + offset_from_section_start_); + return start + 1; + break; + case DW_FORM_ref2: + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadTwoBytes(start) + + offset_from_section_start_); + return start + 2; + break; + case DW_FORM_ref4: + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadFourBytes(start) + + offset_from_section_start_); + return start + 4; + break; + case DW_FORM_ref8: + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadEightBytes(start) + + offset_from_section_start_); + return start + 8; + break; + case DW_FORM_ref_udata: + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadUnsignedLEB128(start, + &len) + + offset_from_section_start_); + return start + len; + break; + case DW_FORM_ref_addr: + // DWARF2 and 3 differ on whether ref_addr is address size or + // offset size. + assert(header_.version == 2 || header_.version == 3); + if (header_.version == 2) { + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadAddress(start)); + return start + reader_->AddressSize(); + } else if (header_.version == 3) { + handler_->ProcessAttributeReference(dieoffset, attr, form, + reader_->ReadOffset(start)); + return start + reader_->OffsetSize(); + } + break; + + case DW_FORM_block1: { + uint64 datalen = reader_->ReadOneByte(start); + handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 1, + datalen); + return start + 1 + datalen; + } + break; + case DW_FORM_block2: { + uint64 datalen = reader_->ReadTwoBytes(start); + handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 2, + datalen); + return start + 2 + datalen; + } + break; + case DW_FORM_block4: { + uint64 datalen = reader_->ReadFourBytes(start); + handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 4, + datalen); + return start + 4 + datalen; + } + break; + case DW_FORM_block: { + uint64 datalen = reader_->ReadUnsignedLEB128(start, &len); + handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + len, + datalen); + return start + datalen + len; + } + break; + case DW_FORM_strp: { + assert(string_buffer_ != NULL); + + const uint64 offset = reader_->ReadOffset(start); + assert(string_buffer_ + offset < string_buffer_ + string_buffer_length_); + + const char* str = string_buffer_ + offset; + handler_->ProcessAttributeString(dieoffset, attr, form, + str); + return start + reader_->OffsetSize(); + } + break; + default: + fprintf(stderr, "Unhandled form type"); + } + fprintf(stderr, "Unhandled form type"); + return NULL; +} + +const char* CompilationUnit::ProcessDIE(uint64 dieoffset, + const char* start, + const Abbrev& abbrev) { + for (AttributeList::const_iterator i = abbrev.attributes.begin(); + i != abbrev.attributes.end(); + i++) { + start = ProcessAttribute(dieoffset, start, i->first, i->second); + } + return start; +} + +void CompilationUnit::ProcessDIEs() { + const char* dieptr = after_header_; + size_t len; + + // lengthstart is the place the length field is based on. + // It is the point in the header after the initial length field + const char* lengthstart = buffer_; + + // In 64 bit dwarf, the initial length is 12 bytes, because of the + // 0xffffffff at the start. + if (reader_->OffsetSize() == 8) + lengthstart += 12; + else + lengthstart += 4; + + // we need semantics of boost scoped_ptr here - no intention of trasnferring + // ownership of the stack. use const, but then we limit ourselves to not + // ever being able to call .reset() on the smart pointer. + std::auto_ptr > const die_stack(new stack); + + while (dieptr < (lengthstart + header_.length)) { + // We give the user the absolute offset from the beginning of + // debug_info, since they need it to deal with ref_addr forms. + uint64 absolute_offset = (dieptr - buffer_) + offset_from_section_start_; + + uint64 abbrev_num = reader_->ReadUnsignedLEB128(dieptr, &len); + + dieptr += len; + + // Abbrev == 0 represents the end of a list of children. + if (abbrev_num == 0) { + const uint64 offset = die_stack->top(); + die_stack->pop(); + handler_->EndDIE(offset); + continue; + } + + const Abbrev& abbrev = abbrevs_->at(abbrev_num); + const enum DwarfTag tag = abbrev.tag; + if (!handler_->StartDIE(absolute_offset, tag, abbrev.attributes)) { + dieptr = SkipDIE(dieptr, abbrev); + } else { + dieptr = ProcessDIE(absolute_offset, dieptr, abbrev); + } + + if (abbrev.has_children) { + die_stack->push(absolute_offset); + } else { + handler_->EndDIE(absolute_offset); + } + } +} + +LineInfo::LineInfo(const char* buffer, uint64 buffer_length, + ByteReader* reader, LineInfoHandler* handler): + handler_(handler), reader_(reader), buffer_(buffer), + buffer_length_(buffer_length) { + header_.std_opcode_lengths = NULL; +} + +uint64 LineInfo::Start() { + ReadHeader(); + ReadLines(); + return after_header_ - buffer_; +} + +// The header for a debug_line section is mildly complicated, because +// the line info is very tightly encoded. +void LineInfo::ReadHeader() { + const char* lineptr = buffer_; + size_t initial_length_size; + + const uint64 initial_length + = reader_->ReadInitialLength(lineptr, &initial_length_size); + + lineptr += initial_length_size; + header_.total_length = initial_length; + assert(buffer_ + initial_length_size + header_.total_length <= + buffer_ + buffer_length_); + + // Address size *must* be set by CU ahead of time. + assert(reader_->AddressSize() != 0); + + header_.version = reader_->ReadTwoBytes(lineptr); + lineptr += 2; + + header_.prologue_length = reader_->ReadOffset(lineptr); + lineptr += reader_->OffsetSize(); + + header_.min_insn_length = reader_->ReadOneByte(lineptr); + lineptr += 1; + + header_.default_is_stmt = reader_->ReadOneByte(lineptr); + lineptr += 1; + + header_.line_base = *reinterpret_cast(lineptr); + lineptr += 1; + + header_.line_range = reader_->ReadOneByte(lineptr); + lineptr += 1; + + header_.opcode_base = reader_->ReadOneByte(lineptr); + lineptr += 1; + + header_.std_opcode_lengths = new vector; + header_.std_opcode_lengths->resize(header_.opcode_base + 1); + (*header_.std_opcode_lengths)[0] = 0; + for (int i = 1; i < header_.opcode_base; i++) { + (*header_.std_opcode_lengths)[i] = reader_->ReadOneByte(lineptr); + lineptr += 1; + } + + // It is legal for the directory entry table to be empty. + if (*lineptr) { + uint32 dirindex = 1; + while (*lineptr) { + const char* dirname = lineptr; + handler_->DefineDir(dirname, dirindex); + lineptr += strlen(dirname) + 1; + dirindex++; + } + } + lineptr++; + + // It is also legal for the file entry table to be empty. + if (*lineptr) { + uint32 fileindex = 1; + size_t len; + while (*lineptr) { + const char* filename = lineptr; + lineptr += strlen(filename) + 1; + + uint64 dirindex = reader_->ReadUnsignedLEB128(lineptr, &len); + lineptr += len; + + uint64 mod_time = reader_->ReadUnsignedLEB128(lineptr, &len); + lineptr += len; + + uint64 filelength = reader_->ReadUnsignedLEB128(lineptr, &len); + lineptr += len; + handler_->DefineFile(filename, fileindex, dirindex, mod_time, + filelength); + fileindex++; + } + } + lineptr++; + + after_header_ = lineptr; +} + +/* static */ +bool LineInfo::ProcessOneOpcode(ByteReader* reader, + LineInfoHandler* handler, + const struct LineInfoHeader &header, + const char* start, + struct LineStateMachine* lsm, + size_t* len, + uintptr pc, + bool *lsm_passes_pc) { + size_t oplen = 0; + size_t templen; + uint8 opcode = reader->ReadOneByte(start); + oplen++; + start++; + + // If the opcode is great than the opcode_base, it is a special + // opcode. Most line programs consist mainly of special opcodes. + if (opcode >= header.opcode_base) { + opcode -= header.opcode_base; + const int64 advance_address = (opcode / header.line_range) + * header.min_insn_length; + const int64 advance_line = (opcode % header.line_range) + + header.line_base; + + // Check if the lsm passes "pc". If so, mark it as passed. + if (lsm_passes_pc && + lsm->address <= pc && pc < lsm->address + advance_address) { + *lsm_passes_pc = true; + } + + lsm->address += advance_address; + lsm->line_num += advance_line; + lsm->basic_block = true; + *len = oplen; + return true; + } + + // Otherwise, we have the regular opcodes + switch (opcode) { + case DW_LNS_copy: { + lsm->basic_block = false; + *len = oplen; + return true; + } + + case DW_LNS_advance_pc: { + uint64 advance_address = reader->ReadUnsignedLEB128(start, &templen); + oplen += templen; + + // Check if the lsm passes "pc". If so, mark it as passed. + if (lsm_passes_pc && lsm->address <= pc && + pc < lsm->address + header.min_insn_length * advance_address) { + *lsm_passes_pc = true; + } + + lsm->address += header.min_insn_length * advance_address; + } + break; + case DW_LNS_advance_line: { + const int64 advance_line = reader->ReadSignedLEB128(start, &templen); + oplen += templen; + lsm->line_num += advance_line; + + // With gcc 4.2.1, we can get the line_no here for the first time + // since DW_LNS_advance_line is called after DW_LNE_set_address is + // called. So we check if the lsm passes "pc" here, not in + // DW_LNE_set_address. + if (lsm_passes_pc && lsm->address == pc) { + *lsm_passes_pc = true; + } + } + break; + case DW_LNS_set_file: { + const uint64 fileno = reader->ReadUnsignedLEB128(start, &templen); + oplen += templen; + lsm->file_num = fileno; + } + break; + case DW_LNS_set_column: { + const uint64 colno = reader->ReadUnsignedLEB128(start, &templen); + oplen += templen; + lsm->column_num = colno; + } + break; + case DW_LNS_negate_stmt: { + lsm->is_stmt = !lsm->is_stmt; + } + break; + case DW_LNS_set_basic_block: { + lsm->basic_block = true; + } + break; + case DW_LNS_fixed_advance_pc: { + const uint16 advance_address = reader->ReadTwoBytes(start); + oplen += 2; + + // Check if the lsm passes "pc". If so, mark it as passed. + if (lsm_passes_pc && + lsm->address <= pc && pc < lsm->address + advance_address) { + *lsm_passes_pc = true; + } + + lsm->address += advance_address; + } + break; + case DW_LNS_const_add_pc: { + const int64 advance_address = header.min_insn_length + * ((255 - header.opcode_base) + / header.line_range); + + // Check if the lsm passes "pc". If so, mark it as passed. + if (lsm_passes_pc && + lsm->address <= pc && pc < lsm->address + advance_address) { + *lsm_passes_pc = true; + } + + lsm->address += advance_address; + } + break; + case DW_LNS_extended_op: { + const size_t extended_op_len = reader->ReadUnsignedLEB128(start, + &templen); + start += templen; + oplen += templen + extended_op_len; + + const uint64 extended_op = reader->ReadOneByte(start); + start++; + + switch (extended_op) { + case DW_LNE_end_sequence: { + lsm->end_sequence = true; + *len = oplen; + return true; + } + break; + case DW_LNE_set_address: { + // With gcc 4.2.1, we cannot tell the line_no here since + // DW_LNE_set_address is called before DW_LNS_advance_line is + // called. So we do not check if the lsm passes "pc" here. See + // also the comment in DW_LNS_advance_line. + uint64 address = reader->ReadAddress(start); + lsm->address = address; + } + break; + case DW_LNE_define_file: { + const char* filename = start; + + templen = strlen(filename) + 1; + start += templen; + + uint64 dirindex = reader->ReadUnsignedLEB128(start, &templen); + oplen += templen; + + const uint64 mod_time = reader->ReadUnsignedLEB128(start, + &templen); + oplen += templen; + + const uint64 filelength = reader->ReadUnsignedLEB128(start, + &templen); + oplen += templen; + + if (handler) { + handler->DefineFile(filename, -1, dirindex, mod_time, + filelength); + } + } + break; + } + } + break; + + default: { + // Ignore unknown opcode silently + if (header.std_opcode_lengths) { + for (int i = 0; i < (*header.std_opcode_lengths)[opcode]; i++) { + size_t templen; + reader->ReadUnsignedLEB128(start, &templen); + start += templen; + oplen += templen; + } + } + } + break; + } + *len = oplen; + return false; +} + +void LineInfo::ReadLines() { + struct LineStateMachine lsm; + + // lengthstart is the place the length field is based on. + // It is the point in the header after the initial length field + const char* lengthstart = buffer_; + + // In 64 bit dwarf, the initial length is 12 bytes, because of the + // 0xffffffff at the start. + if (reader_->OffsetSize() == 8) + lengthstart += 12; + else + lengthstart += 4; + + const char* lineptr = after_header_; + lsm.Reset(header_.default_is_stmt); + + // The LineInfoHandler interface expects each line's length along + // with its address, but DWARF only provides addresses (sans + // length), and an end-of-sequence address; one infers the length + // from the next address. So we report a line only when we get the + // next line's address, or the end-of-sequence address. + bool have_pending_line = false; + uint64 pending_address = 0; + uint32 pending_file_num = 0, pending_line_num = 0, pending_column_num = 0; + + while (lineptr < lengthstart + header_.total_length) { + size_t oplength; + bool add_row = ProcessOneOpcode(reader_, handler_, header_, + lineptr, &lsm, &oplength, (uintptr)-1, + NULL); + if (add_row) { + if (have_pending_line) + handler_->AddLine(pending_address, lsm.address - pending_address, + pending_file_num, pending_line_num, + pending_column_num); + if (lsm.end_sequence) { + lsm.Reset(header_.default_is_stmt); + have_pending_line = false; + } else { + pending_address = lsm.address; + pending_file_num = lsm.file_num; + pending_line_num = lsm.line_num; + pending_column_num = lsm.column_num; + have_pending_line = true; + } + } + lineptr += oplength; + } + + after_header_ = lengthstart + header_.total_length; +} + +// A DWARF rule for recovering the address or value of a register, or +// computing the canonical frame address. There is one subclass of this for +// each '*Rule' member function in CallFrameInfo::Handler. +// +// It's annoying that we have to handle Rules using pointers (because +// the concrete instances can have an arbitrary size). They're small, +// so it would be much nicer if we could just handle them by value +// instead of fretting about ownership and destruction. +// +// It seems like all these could simply be instances of std::tr1::bind, +// except that we need instances to be EqualityComparable, too. +// +// This could logically be nested within State, but then the qualified names +// get horrendous. +class CallFrameInfo::Rule { + public: + virtual ~Rule() { } + + // Tell HANDLER that, at ADDRESS in the program, REGISTER can be + // recovered using this rule. If REGISTER is kCFARegister, then this rule + // describes how to compute the canonical frame address. Return what the + // HANDLER member function returned. + virtual bool Handle(Handler *handler, + uint64 address, int register) const = 0; + + // Equality on rules. We use these to decide which rules we need + // to report after a DW_CFA_restore_state instruction. + virtual bool operator==(const Rule &rhs) const = 0; + + bool operator!=(const Rule &rhs) const { return ! (*this == rhs); } + + // Return a pointer to a copy of this rule. + virtual Rule *Copy() const = 0; + + // If this is a base+offset rule, change its base register to REG. + // Otherwise, do nothing. (Ugly, but required for DW_CFA_def_cfa_register.) + virtual void SetBaseRegister(unsigned reg) { } + + // If this is a base+offset rule, change its offset to OFFSET. Otherwise, + // do nothing. (Ugly, but required for DW_CFA_def_cfa_offset.) + virtual void SetOffset(long long offset) { } +}; + +// Rule: the value the register had in the caller cannot be recovered. +class CallFrameInfo::UndefinedRule: public CallFrameInfo::Rule { + public: + UndefinedRule() { } + ~UndefinedRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->UndefinedRule(address, reg); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is prohibited by Google C++ Style Guide, but justified. + const UndefinedRule *our_rhs = dynamic_cast(&rhs); + return (our_rhs != NULL); + } + Rule *Copy() const { return new UndefinedRule(*this); } +}; + +// Rule: the register's value is the same as that it had in the caller. +class CallFrameInfo::SameValueRule: public CallFrameInfo::Rule { + public: + SameValueRule() { } + ~SameValueRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->SameValueRule(address, reg); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is prohibited by Google C++ Style Guide, but justified. + const SameValueRule *our_rhs = dynamic_cast(&rhs); + return (our_rhs != NULL); + } + Rule *Copy() const { return new SameValueRule(*this); } +}; + +// Rule: the register is saved at OFFSET from BASE_REGISTER. BASE_REGISTER +// may be CallFrameInfo::Handler::kCFARegister. +class CallFrameInfo::OffsetRule: public CallFrameInfo::Rule { + public: + OffsetRule(int base_register, long offset) + : base_register_(base_register), offset_(offset) { } + ~OffsetRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->OffsetRule(address, reg, base_register_, offset_); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is prohibited by Google C++ Style Guide, but justified. + const OffsetRule *our_rhs = dynamic_cast(&rhs); + return (our_rhs && + base_register_ == our_rhs->base_register_ && + offset_ == our_rhs->offset_); + } + Rule *Copy() const { return new OffsetRule(*this); } + // We don't actually need SetBaseRegister or SetOffset here, since they + // are only ever applied to CFA rules, for DW_CFA_def_cfa_offset, and it + // doesn't make sense to use OffsetRule for computing the CFA: it + // computes the address at which a register is saved, not a value. + private: + int base_register_; + int offset_; +}; + +// Rule: the value the register had in the caller is the value of +// BASE_REGISTER plus offset. BASE_REGISTER may be +// CallFrameInfo::Handler::kCFARegister. +class CallFrameInfo::ValOffsetRule: public CallFrameInfo::Rule { + public: + ValOffsetRule(int base_register, long offset) + : base_register_(base_register), offset_(offset) { } + ~ValOffsetRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->ValOffsetRule(address, reg, base_register_, offset_); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is prohibited by Google C++ Style Guide, but justified. + const ValOffsetRule *our_rhs = dynamic_cast(&rhs); + return (our_rhs && + base_register_ == our_rhs->base_register_ && + offset_ == our_rhs->offset_); + } + Rule *Copy() const { return new ValOffsetRule(*this); } + void SetBaseRegister(unsigned reg) { base_register_ = reg; } + void SetOffset(long long offset) { offset_ = offset; } + private: + int base_register_; + int offset_; +}; + +// Rule: the register has been saved in another register REGISTER_NUMBER_. +class CallFrameInfo::RegisterRule: public CallFrameInfo::Rule { + public: + explicit RegisterRule(int register_number) + : register_number_(register_number) { } + ~RegisterRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->RegisterRule(address, reg, register_number_); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is prohibited by Google C++ Style Guide, but justified. + const RegisterRule *our_rhs = dynamic_cast(&rhs); + return (our_rhs && register_number_ == our_rhs->register_number_); + } + Rule *Copy() const { return new RegisterRule(*this); } + private: + int register_number_; +}; + +// Rule: EXPRESSION evaluates to the address at which the register is saved. +class CallFrameInfo::ExpressionRule: public CallFrameInfo::Rule { + public: + explicit ExpressionRule(const string &expression) + : expression_(expression) { } + ~ExpressionRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->ExpressionRule(address, reg, expression_); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is prohibited by Google C++ Style Guide, but justified. + const ExpressionRule *our_rhs = dynamic_cast(&rhs); + return (our_rhs && expression_ == our_rhs->expression_); + } + Rule *Copy() const { return new ExpressionRule(*this); } + private: + string expression_; +}; + +// Rule: EXPRESSION evaluates to the address at which the register is saved. +class CallFrameInfo::ValExpressionRule: public CallFrameInfo::Rule { + public: + explicit ValExpressionRule(const string &expression) + : expression_(expression) { } + ~ValExpressionRule() { } + bool Handle(Handler *handler, uint64 address, int reg) const { + return handler->ValExpressionRule(address, reg, expression_); + } + bool operator==(const Rule &rhs) const { + // dynamic_cast is prohibited by Google C++ Style Guide, but justified. + const ValExpressionRule *our_rhs = + dynamic_cast(&rhs); + return (our_rhs && expression_ == our_rhs->expression_); + } + Rule *Copy() const { return new ValExpressionRule(*this); } + private: + string expression_; +}; + +// A map from register numbers to rules. +class CallFrameInfo::RuleMap { + public: + RuleMap() : cfa_rule_(NULL) { } + RuleMap(const RuleMap &rhs) : cfa_rule_(NULL) { *this = rhs; } + ~RuleMap() { Clear(); } + + RuleMap &operator=(const RuleMap &rhs); + + // Set the rule for computing the CFA to RULE. Take ownership of RULE. + void SetCFARule(Rule *rule) { delete cfa_rule_; cfa_rule_ = rule; } + + // Return the current CFA rule. Unlike RegisterRule, this RuleMap retains + // ownership of the rule. We use this for DW_CFA_def_cfa_offset and + // DW_CFA_def_cfa_register, and for detecting references to the CFA before + // a rule for it has been established. + Rule *CFARule() const { return cfa_rule_; } + + // Return the rule for REG, or NULL if there is none. The caller takes + // ownership of the result. + Rule *RegisterRule(int reg) const; + + // Set the rule for computing REG to RULE. Take ownership of RULE. + void SetRegisterRule(int reg, Rule *rule); + + // Make all the appropriate calls to HANDLER as if we were changing from + // this RuleMap to NEW_RULES at ADDRESS. We use this to implement + // DW_CFA_restore_state, where lots of rules can change simultaneously. + // Return true if all handlers returned true; otherwise, return false. + bool HandleTransitionTo(Handler *handler, uint64 address, + const RuleMap &new_rules) const; + + private: + // A map from register numbers to Rules. + typedef map RuleByNumber; + + // Remove all register rules and clear cfa_rule_. + void Clear(); + + // The rule for computing the canonical frame address. This RuleMap owns + // this rule. + Rule *cfa_rule_; + + // A map from register numbers to postfix expressions to recover + // their values. This RuleMap owns the Rules the map refers to. + RuleByNumber registers_; +}; + +CallFrameInfo::RuleMap &CallFrameInfo::RuleMap::operator=(const RuleMap &rhs) { + Clear(); + // Since each map owns the rules it refers to, assignment must copy them. + if (rhs.cfa_rule_) cfa_rule_ = rhs.cfa_rule_->Copy(); + for (RuleByNumber::const_iterator it = rhs.registers_.begin(); + it != rhs.registers_.end(); it++) + registers_[it->first] = it->second->Copy(); + return *this; +} + +CallFrameInfo::Rule *CallFrameInfo::RuleMap::RegisterRule(int reg) const { + assert(reg != Handler::kCFARegister); + RuleByNumber::const_iterator it = registers_.find(reg); + if (it != registers_.end()) + return it->second->Copy(); + else + return NULL; +} + +void CallFrameInfo::RuleMap::SetRegisterRule(int reg, Rule *rule) { + assert(reg != Handler::kCFARegister); + assert(rule); + Rule **slot = ®isters_[reg]; + delete *slot; + *slot = rule; +} + +bool CallFrameInfo::RuleMap::HandleTransitionTo( + Handler *handler, + uint64 address, + const RuleMap &new_rules) const { + // Transition from cfa_rule_ to new_rules.cfa_rule_. + if (cfa_rule_ && new_rules.cfa_rule_) { + if (*cfa_rule_ != *new_rules.cfa_rule_ && + !new_rules.cfa_rule_->Handle(handler, address, + Handler::kCFARegister)) + return false; + } else if (cfa_rule_) { + // this RuleMap has a CFA rule but new_rules doesn't. + // CallFrameInfo::Handler has no way to handle this --- and shouldn't; + // it's garbage input. The instruction interpreter should have + // detected this and warned, so take no action here. + } else if (new_rules.cfa_rule_) { + // This shouldn't be possible: NEW_RULES is some prior state, and + // there's no way to remove entries. + assert(0); + } else { + // Both CFA rules are empty. No action needed. + } + + // Traverse the two maps in order by register number, and report + // whatever differences we find. + RuleByNumber::const_iterator old_it = registers_.begin(); + RuleByNumber::const_iterator new_it = new_rules.registers_.begin(); + while (old_it != registers_.end() && new_it != new_rules.registers_.end()) { + if (old_it->first < new_it->first) { + // This RuleMap has an entry for old_it->first, but NEW_RULES + // doesn't. + // + // This isn't really the right thing to do, but since CFI generally + // only mentions callee-saves registers, and GCC's convention for + // callee-saves registers is that they are unchanged, it's a good + // approximation. + if (!handler->SameValueRule(address, old_it->first)) + return false; + old_it++; + } else if (old_it->first > new_it->first) { + // NEW_RULES has entry for new_it->first, but this RuleMap + // doesn't. This shouldn't be possible: NEW_RULES is some prior + // state, and there's no way to remove entries. + assert(0); + } else { + // Both maps have an entry for this register. Report the new + // rule if it is different. + if (*old_it->second != *new_it->second && + !new_it->second->Handle(handler, address, new_it->first)) + return false; + new_it++, old_it++; + } + } + // Finish off entries from this RuleMap with no counterparts in new_rules. + while (old_it != registers_.end()) { + if (!handler->SameValueRule(address, old_it->first)) + return false; + old_it++; + } + // Since we only make transitions from a rule set to some previously + // saved rule set, and we can only add rules to the map, NEW_RULES + // must have fewer rules than *this. + assert(new_it == new_rules.registers_.end()); + + return true; +} + +// Remove all register rules and clear cfa_rule_. +void CallFrameInfo::RuleMap::Clear() { + delete cfa_rule_; + cfa_rule_ = NULL; + for (RuleByNumber::iterator it = registers_.begin(); + it != registers_.end(); it++) + delete it->second; + registers_.clear(); +} + +// The state of the call frame information interpreter as it processes +// instructions from a CIE and FDE. +class CallFrameInfo::State { + public: + // Create a call frame information interpreter state with the given + // reporter, reader, handler, and initial call frame info address. + State(ByteReader *reader, Handler *handler, Reporter *reporter, + uint64 address) + : reader_(reader), handler_(handler), reporter_(reporter), + address_(address), entry_(NULL), cursor_(NULL) { } + + // Interpret instructions from CIE, save the resulting rule set for + // DW_CFA_restore instructions, and return true. On error, report + // the problem to reporter_ and return false. + bool InterpretCIE(const CIE &cie); + + // Interpret instructions from FDE, and return true. On error, + // report the problem to reporter_ and return false. + bool InterpretFDE(const FDE &fde); + + private: + // The operands of a CFI instruction, for ParseOperands. + struct Operands { + unsigned register_number; // A register number. + uint64 offset; // An offset or address. + long signed_offset; // A signed offset. + string expression; // A DWARF expression. + }; + + // Parse CFI instruction operands from STATE's instruction stream as + // described by FORMAT. On success, populate OPERANDS with the + // results, and return true. On failure, report the problem and + // return false. + // + // Each character of FORMAT should be one of the following: + // + // 'r' unsigned LEB128 register number (OPERANDS->register_number) + // 'o' unsigned LEB128 offset (OPERANDS->offset) + // 's' signed LEB128 offset (OPERANDS->signed_offset) + // 'a' machine-size address (OPERANDS->offset) + // (If the CIE has a 'z' augmentation string, 'a' uses the + // encoding specified by the 'R' argument.) + // '1' a one-byte offset (OPERANDS->offset) + // '2' a two-byte offset (OPERANDS->offset) + // '4' a four-byte offset (OPERANDS->offset) + // '8' an eight-byte offset (OPERANDS->offset) + // 'e' a DW_FORM_block holding a (OPERANDS->expression) + // DWARF expression + bool ParseOperands(const char *format, Operands *operands); + + // Interpret one CFI instruction from STATE's instruction stream, update + // STATE, report any rule changes to handler_, and return true. On + // failure, report the problem and return false. + bool DoInstruction(); + + // The following Do* member functions are subroutines of DoInstruction, + // factoring out the actual work of operations that have several + // different encodings. + + // Set the CFA rule to be the value of BASE_REGISTER plus OFFSET, and + // return true. On failure, report and return false. (Used for + // DW_CFA_def_cfa and DW_CFA_def_cfa_sf.) + bool DoDefCFA(unsigned base_register, long offset); + + // Change the offset of the CFA rule to OFFSET, and return true. On + // failure, report and return false. (Subroutine for + // DW_CFA_def_cfa_offset and DW_CFA_def_cfa_offset_sf.) + bool DoDefCFAOffset(long offset); + + // Specify that REG can be recovered using RULE, and return true. On + // failure, report and return false. + bool DoRule(unsigned reg, Rule *rule); + + // Specify that REG can be found at OFFSET from the CFA, and return true. + // On failure, report and return false. (Subroutine for DW_CFA_offset, + // DW_CFA_offset_extended, and DW_CFA_offset_extended_sf.) + bool DoOffset(unsigned reg, long offset); + + // Specify that the caller's value for REG is the CFA plus OFFSET, + // and return true. On failure, report and return false. (Subroutine + // for DW_CFA_val_offset and DW_CFA_val_offset_sf.) + bool DoValOffset(unsigned reg, long offset); + + // Restore REG to the rule established in the CIE, and return true. On + // failure, report and return false. (Subroutine for DW_CFA_restore and + // DW_CFA_restore_extended.) + bool DoRestore(unsigned reg); + + // Return the section offset of the instruction at cursor. For use + // in error messages. + uint64 CursorOffset() { return entry_->offset + (cursor_ - entry_->start); } + + // Report that entry_ is incomplete, and return false. For brevity. + bool ReportIncomplete() { + reporter_->Incomplete(entry_->offset, entry_->kind); + return false; + } + + // For reading multi-byte values with the appropriate endianness. + ByteReader *reader_; + + // The handler to which we should report the data we find. + Handler *handler_; + + // For reporting problems in the info we're parsing. + Reporter *reporter_; + + // The code address to which the next instruction in the stream applies. + uint64 address_; + + // The entry whose instructions we are currently processing. This is + // first a CIE, and then an FDE. + const Entry *entry_; + + // The next instruction to process. + const char *cursor_; + + // The current set of rules. + RuleMap rules_; + + // The set of rules established by the CIE, used by DW_CFA_restore + // and DW_CFA_restore_extended. We set this after interpreting the + // CIE's instructions. + RuleMap cie_rules_; + + // A stack of saved states, for DW_CFA_remember_state and + // DW_CFA_restore_state. + stack saved_rules_; +}; + +bool CallFrameInfo::State::InterpretCIE(const CIE &cie) { + entry_ = &cie; + cursor_ = entry_->instructions; + while (cursor_ < entry_->end) + if (!DoInstruction()) + return false; + // Note the rules established by the CIE, for use by DW_CFA_restore + // and DW_CFA_restore_extended. + cie_rules_ = rules_; + return true; +} + +bool CallFrameInfo::State::InterpretFDE(const FDE &fde) { + entry_ = &fde; + cursor_ = entry_->instructions; + while (cursor_ < entry_->end) + if (!DoInstruction()) + return false; + return true; +} + +bool CallFrameInfo::State::ParseOperands(const char *format, + Operands *operands) { + size_t len; + const char *operand; + + for (operand = format; *operand; operand++) { + size_t bytes_left = entry_->end - cursor_; + switch (*operand) { + case 'r': + operands->register_number = reader_->ReadUnsignedLEB128(cursor_, &len); + if (len > bytes_left) return ReportIncomplete(); + cursor_ += len; + break; + + case 'o': + operands->offset = reader_->ReadUnsignedLEB128(cursor_, &len); + if (len > bytes_left) return ReportIncomplete(); + cursor_ += len; + break; + + case 's': + operands->signed_offset = reader_->ReadSignedLEB128(cursor_, &len); + if (len > bytes_left) return ReportIncomplete(); + cursor_ += len; + break; + + case 'a': + operands->offset = + reader_->ReadEncodedPointer(cursor_, entry_->cie->pointer_encoding, + &len); + if (len > bytes_left) return ReportIncomplete(); + cursor_ += len; + break; + + case '1': + if (1 > bytes_left) return ReportIncomplete(); + operands->offset = static_cast(*cursor_++); + break; + + case '2': + if (2 > bytes_left) return ReportIncomplete(); + operands->offset = reader_->ReadTwoBytes(cursor_); + cursor_ += 2; + break; + + case '4': + if (4 > bytes_left) return ReportIncomplete(); + operands->offset = reader_->ReadFourBytes(cursor_); + cursor_ += 4; + break; + + case '8': + if (8 > bytes_left) return ReportIncomplete(); + operands->offset = reader_->ReadEightBytes(cursor_); + cursor_ += 8; + break; + + case 'e': { + size_t expression_length = reader_->ReadUnsignedLEB128(cursor_, &len); + if (len > bytes_left || expression_length > bytes_left - len) + return ReportIncomplete(); + cursor_ += len; + operands->expression = string(cursor_, expression_length); + cursor_ += expression_length; + break; + } + + default: + assert(0); + } + } + + return true; +} + +bool CallFrameInfo::State::DoInstruction() { + CIE *cie = entry_->cie; + Operands ops; + + // Our entry's kind should have been set by now. + assert(entry_->kind != kUnknown); + + // We shouldn't have been invoked unless there were more + // instructions to parse. + assert(cursor_ < entry_->end); + + unsigned opcode = *cursor_++; + if ((opcode & 0xc0) != 0) { + switch (opcode & 0xc0) { + // Advance the address. + case DW_CFA_advance_loc: { + size_t code_offset = opcode & 0x3f; + address_ += code_offset * cie->code_alignment_factor; + break; + } + + // Find a register at an offset from the CFA. + case DW_CFA_offset: + if (!ParseOperands("o", &ops) || + !DoOffset(opcode & 0x3f, ops.offset * cie->data_alignment_factor)) + return false; + break; + + // Restore the rule established for a register by the CIE. + case DW_CFA_restore: + if (!DoRestore(opcode & 0x3f)) return false; + break; + + // The 'if' above should have excluded this possibility. + default: + assert(0); + } + + // Return here, so the big switch below won't be indented. + return true; + } + + switch (opcode) { + // Set the address. + case DW_CFA_set_loc: + if (!ParseOperands("a", &ops)) return false; + address_ = ops.offset; + break; + + // Advance the address. + case DW_CFA_advance_loc1: + if (!ParseOperands("1", &ops)) return false; + address_ += ops.offset * cie->code_alignment_factor; + break; + + // Advance the address. + case DW_CFA_advance_loc2: + if (!ParseOperands("2", &ops)) return false; + address_ += ops.offset * cie->code_alignment_factor; + break; + + // Advance the address. + case DW_CFA_advance_loc4: + if (!ParseOperands("4", &ops)) return false; + address_ += ops.offset * cie->code_alignment_factor; + break; + + // Advance the address. + case DW_CFA_MIPS_advance_loc8: + if (!ParseOperands("8", &ops)) return false; + address_ += ops.offset * cie->code_alignment_factor; + break; + + // Compute the CFA by adding an offset to a register. + case DW_CFA_def_cfa: + if (!ParseOperands("ro", &ops) || + !DoDefCFA(ops.register_number, ops.offset)) + return false; + break; + + // Compute the CFA by adding an offset to a register. + case DW_CFA_def_cfa_sf: + if (!ParseOperands("rs", &ops) || + !DoDefCFA(ops.register_number, + ops.signed_offset * cie->data_alignment_factor)) + return false; + break; + + // Change the base register used to compute the CFA. + case DW_CFA_def_cfa_register: { + Rule *cfa_rule = rules_.CFARule(); + if (!cfa_rule) { + reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); + return false; + } + if (!ParseOperands("r", &ops)) return false; + cfa_rule->SetBaseRegister(ops.register_number); + if (!cfa_rule->Handle(handler_, address_, + Handler::kCFARegister)) + return false; + break; + } + + // Change the offset used to compute the CFA. + case DW_CFA_def_cfa_offset: + if (!ParseOperands("o", &ops) || + !DoDefCFAOffset(ops.offset)) + return false; + break; + + // Change the offset used to compute the CFA. + case DW_CFA_def_cfa_offset_sf: + if (!ParseOperands("s", &ops) || + !DoDefCFAOffset(ops.signed_offset * cie->data_alignment_factor)) + return false; + break; + + // Specify an expression whose value is the CFA. + case DW_CFA_def_cfa_expression: { + if (!ParseOperands("e", &ops)) + return false; + Rule *rule = new ValExpressionRule(ops.expression); + rules_.SetCFARule(rule); + if (!rule->Handle(handler_, address_, + Handler::kCFARegister)) + return false; + break; + } + + // The register's value cannot be recovered. + case DW_CFA_undefined: { + if (!ParseOperands("r", &ops) || + !DoRule(ops.register_number, new UndefinedRule())) + return false; + break; + } + + // The register's value is unchanged from its value in the caller. + case DW_CFA_same_value: { + if (!ParseOperands("r", &ops) || + !DoRule(ops.register_number, new SameValueRule())) + return false; + break; + } + + // Find a register at an offset from the CFA. + case DW_CFA_offset_extended: + if (!ParseOperands("ro", &ops) || + !DoOffset(ops.register_number, + ops.offset * cie->data_alignment_factor)) + return false; + break; + + // The register is saved at an offset from the CFA. + case DW_CFA_offset_extended_sf: + if (!ParseOperands("rs", &ops) || + !DoOffset(ops.register_number, + ops.signed_offset * cie->data_alignment_factor)) + return false; + break; + + // The register is saved at an offset from the CFA. + case DW_CFA_GNU_negative_offset_extended: + if (!ParseOperands("ro", &ops) || + !DoOffset(ops.register_number, + -ops.offset * cie->data_alignment_factor)) + return false; + break; + + // The register's value is the sum of the CFA plus an offset. + case DW_CFA_val_offset: + if (!ParseOperands("ro", &ops) || + !DoValOffset(ops.register_number, + ops.offset * cie->data_alignment_factor)) + return false; + break; + + // The register's value is the sum of the CFA plus an offset. + case DW_CFA_val_offset_sf: + if (!ParseOperands("rs", &ops) || + !DoValOffset(ops.register_number, + ops.signed_offset * cie->data_alignment_factor)) + return false; + break; + + // The register has been saved in another register. + case DW_CFA_register: { + if (!ParseOperands("ro", &ops) || + !DoRule(ops.register_number, new RegisterRule(ops.offset))) + return false; + break; + } + + // An expression yields the address at which the register is saved. + case DW_CFA_expression: { + if (!ParseOperands("re", &ops) || + !DoRule(ops.register_number, new ExpressionRule(ops.expression))) + return false; + break; + } + + // An expression yields the caller's value for the register. + case DW_CFA_val_expression: { + if (!ParseOperands("re", &ops) || + !DoRule(ops.register_number, new ValExpressionRule(ops.expression))) + return false; + break; + } + + // Restore the rule established for a register by the CIE. + case DW_CFA_restore_extended: + if (!ParseOperands("r", &ops) || + !DoRestore( ops.register_number)) + return false; + break; + + // Save the current set of rules on a stack. + case DW_CFA_remember_state: + saved_rules_.push(rules_); + break; + + // Pop the current set of rules off the stack. + case DW_CFA_restore_state: { + if (saved_rules_.empty()) { + reporter_->EmptyStateStack(entry_->offset, entry_->kind, + CursorOffset()); + return false; + } + const RuleMap &new_rules = saved_rules_.top(); + if (rules_.CFARule() && !new_rules.CFARule()) { + reporter_->ClearingCFARule(entry_->offset, entry_->kind, + CursorOffset()); + return false; + } + rules_.HandleTransitionTo(handler_, address_, new_rules); + rules_ = new_rules; + saved_rules_.pop(); + break; + } + + // No operation. (Padding instruction.) + case DW_CFA_nop: + break; + + // A SPARC register window save: Registers 8 through 15 (%o0-%o7) + // are saved in registers 24 through 31 (%i0-%i7), and registers + // 16 through 31 (%l0-%l7 and %i0-%i7) are saved at CFA offsets + // (0-15 * the register size). The register numbers must be + // hard-coded. A GNU extension, and not a pretty one. + case DW_CFA_GNU_window_save: { + // Save %o0-%o7 in %i0-%i7. + for (int i = 8; i < 16; i++) + if (!DoRule(i, new RegisterRule(i + 16))) + return false; + // Save %l0-%l7 and %i0-%i7 at the CFA. + for (int i = 16; i < 32; i++) + // Assume that the byte reader's address size is the same as + // the architecture's register size. !@#%*^ hilarious. + if (!DoRule(i, new OffsetRule(Handler::kCFARegister, + (i - 16) * reader_->AddressSize()))) + return false; + break; + } + + // I'm not sure what this is. GDB doesn't use it for unwinding. + case DW_CFA_GNU_args_size: + if (!ParseOperands("o", &ops)) return false; + break; + + // An opcode we don't recognize. + default: { + reporter_->BadInstruction(entry_->offset, entry_->kind, CursorOffset()); + return false; + } + } + + return true; +} + +bool CallFrameInfo::State::DoDefCFA(unsigned base_register, long offset) { + Rule *rule = new ValOffsetRule(base_register, offset); + rules_.SetCFARule(rule); + return rule->Handle(handler_, address_, + Handler::kCFARegister); +} + +bool CallFrameInfo::State::DoDefCFAOffset(long offset) { + Rule *cfa_rule = rules_.CFARule(); + if (!cfa_rule) { + reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); + return false; + } + cfa_rule->SetOffset(offset); + return cfa_rule->Handle(handler_, address_, + Handler::kCFARegister); +} + +bool CallFrameInfo::State::DoRule(unsigned reg, Rule *rule) { + rules_.SetRegisterRule(reg, rule); + return rule->Handle(handler_, address_, reg); +} + +bool CallFrameInfo::State::DoOffset(unsigned reg, long offset) { + if (!rules_.CFARule()) { + reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); + return false; + } + return DoRule(reg, + new OffsetRule(Handler::kCFARegister, offset)); +} + +bool CallFrameInfo::State::DoValOffset(unsigned reg, long offset) { + if (!rules_.CFARule()) { + reporter_->NoCFARule(entry_->offset, entry_->kind, CursorOffset()); + return false; + } + return DoRule(reg, + new ValOffsetRule(Handler::kCFARegister, offset)); +} + +bool CallFrameInfo::State::DoRestore(unsigned reg) { + // DW_CFA_restore and DW_CFA_restore_extended don't make sense in a CIE. + if (entry_->kind == kCIE) { + reporter_->RestoreInCIE(entry_->offset, CursorOffset()); + return false; + } + Rule *rule = cie_rules_.RegisterRule(reg); + if (!rule) { + // This isn't really the right thing to do, but since CFI generally + // only mentions callee-saves registers, and GCC's convention for + // callee-saves registers is that they are unchanged, it's a good + // approximation. + rule = new SameValueRule(); + } + return DoRule(reg, rule); +} + +bool CallFrameInfo::ReadEntryPrologue(const char *cursor, Entry *entry) { + const char *buffer_end = buffer_ + buffer_length_; + + // Initialize enough of ENTRY for use in error reporting. + entry->offset = cursor - buffer_; + entry->start = cursor; + entry->kind = kUnknown; + entry->end = NULL; + + // Read the initial length. This sets reader_'s offset size. + size_t length_size; + uint64 length = reader_->ReadInitialLength(cursor, &length_size); + if (length_size > size_t(buffer_end - cursor)) + return ReportIncomplete(entry); + cursor += length_size; + + // In a .eh_frame section, a length of zero marks the end of the series + // of entries. + if (length == 0 && eh_frame_) { + entry->kind = kTerminator; + entry->end = cursor; + return true; + } + + // Validate the length. + if (length > size_t(buffer_end - cursor)) + return ReportIncomplete(entry); + + // The length is the number of bytes after the initial length field; + // we have that position handy at this point, so compute the end + // now. (If we're parsing 64-bit-offset DWARF on a 32-bit machine, + // and the length didn't fit in a size_t, we would have rejected it + // above.) + entry->end = cursor + length; + + // Parse the next field: either the offset of a CIE or a CIE id. + size_t offset_size = reader_->OffsetSize(); + if (offset_size > size_t(entry->end - cursor)) return ReportIncomplete(entry); + entry->id = reader_->ReadOffset(cursor); + + // Don't advance cursor past id field yet; in .eh_frame data we need + // the id's position to compute the section offset of an FDE's CIE. + + // Now we can decide what kind of entry this is. + if (eh_frame_) { + // In .eh_frame data, an ID of zero marks the entry as a CIE, and + // anything else is an offset from the id field of the FDE to the start + // of the CIE. + if (entry->id == 0) { + entry->kind = kCIE; + } else { + entry->kind = kFDE; + // Turn the offset from the id into an offset from the buffer's start. + entry->id = (cursor - buffer_) - entry->id; + } + } else { + // In DWARF CFI data, an ID of ~0 (of the appropriate width, given the + // offset size for the entry) marks the entry as a CIE, and anything + // else is the offset of the CIE from the beginning of the section. + if (offset_size == 4) + entry->kind = (entry->id == 0xffffffff) ? kCIE : kFDE; + else { + assert(offset_size == 8); + entry->kind = (entry->id == 0xffffffffffffffffULL) ? kCIE : kFDE; + } + } + + // Now advance cursor past the id. + cursor += offset_size; + + // The fields specific to this kind of entry start here. + entry->fields = cursor; + + entry->cie = NULL; + + return true; +} + +bool CallFrameInfo::ReadCIEFields(CIE *cie) { + const char *cursor = cie->fields; + size_t len; + + assert(cie->kind == kCIE); + + // Prepare for early exit. + cie->version = 0; + cie->augmentation.clear(); + cie->code_alignment_factor = 0; + cie->data_alignment_factor = 0; + cie->return_address_register = 0; + cie->has_z_augmentation = false; + cie->pointer_encoding = DW_EH_PE_absptr; + cie->instructions = 0; + + // Parse the version number. + if (cie->end - cursor < 1) + return ReportIncomplete(cie); + cie->version = reader_->ReadOneByte(cursor); + cursor++; + + // If we don't recognize the version, we can't parse any more fields + // of the CIE. For DWARF CFI, we handle versions 1 through 3 (there + // was never a version 2 fo CFI data). For .eh_frame, we handle only + // version 1. + if (eh_frame_) { + if (cie->version != 1) { + reporter_->UnrecognizedVersion(cie->offset, cie->version); + return false; + } + } else { + if (cie->version < 1 || 3 < cie->version) { + reporter_->UnrecognizedVersion(cie->offset, cie->version); + return false; + } + } + + const char *augmentation_start = cursor; + const void *augmentation_end = + memchr(augmentation_start, '\0', cie->end - augmentation_start); + if (! augmentation_end) return ReportIncomplete(cie); + cursor = static_cast(augmentation_end); + cie->augmentation = string(augmentation_start, cursor - augmentation_start); + // Skip the terminating '\0'. + cursor++; + + // Is this an augmentation we recognize? + if (cie->augmentation.empty()) { + ; // Stock DWARF CFI. + } else if (cie->augmentation[0] == 'z') { + // Linux C++ ABI 'z' augmentation, used for exception handling data. + cie->has_z_augmentation = true; + } else { + // Not an augmentation we recognize. Augmentations can have + // arbitrary effects on the form of rest of the content, so we + // have to give up. + reporter_->UnrecognizedAugmentation(cie->offset, cie->augmentation); + return false; + } + + // Parse the code alignment factor. + cie->code_alignment_factor = reader_->ReadUnsignedLEB128(cursor, &len); + if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie); + cursor += len; + + // Parse the data alignment factor. + cie->data_alignment_factor = reader_->ReadSignedLEB128(cursor, &len); + if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie); + cursor += len; + + // Parse the return address register. This is a ubyte in version 1, and + // a ULEB128 in version 3. + if (cie->version == 1) { + if (cursor >= cie->end) return ReportIncomplete(cie); + cie->return_address_register = uint8(*cursor++); + } else { + cie->return_address_register = reader_->ReadUnsignedLEB128(cursor, &len); + if (size_t(cie->end - cursor) < len) return ReportIncomplete(cie); + cursor += len; + } + + // If we have a 'z' augmentation string, find the augmentation data and + // use the augmentation string to parse it. + if (cie->has_z_augmentation) { + size_t data_size = reader_->ReadUnsignedLEB128(cursor, &len); + if (size_t(cie->end - cursor) < len + data_size) + return ReportIncomplete(cie); + cursor += len; + const char *data = cursor; + cursor += data_size; + const char *data_end = cursor; + + cie->has_z_lsda = false; + cie->has_z_personality = false; + cie->has_z_signal_frame = false; + + // Walk the augmentation string, and extract values from the + // augmentation data as the string directs. + for (size_t i = 1; i < cie->augmentation.size(); i++) { + switch (cie->augmentation[i]) { + case 'L': + // The CIE's augmentation data holds the language-specific data + // area pointer's encoding, and the FDE's augmentation data holds + // the pointer itself. + cie->has_z_lsda = true; + // Fetch the LSDA encoding from the augmentation data. + if (data >= data_end) return ReportIncomplete(cie); + cie->lsda_encoding = DwarfPointerEncoding(*data++); + if (!reader_->ValidEncoding(cie->lsda_encoding)) { + reporter_->InvalidPointerEncoding(cie->offset, cie->lsda_encoding); + return false; + } + // Don't check if the encoding is usable here --- we haven't + // read the FDE's fields yet, so we're not prepared for + // DW_EH_PE_funcrel, although that's a fine encoding for the + // LSDA to use, since it appears in the FDE. + break; + + case 'P': + // The CIE's augmentation data holds the personality routine + // pointer's encoding, followed by the pointer itself. + cie->has_z_personality = true; + // Fetch the personality routine pointer's encoding from the + // augmentation data. + if (data >= data_end) return ReportIncomplete(cie); + cie->personality_encoding = DwarfPointerEncoding(*data++); + if (!reader_->ValidEncoding(cie->personality_encoding)) { + reporter_->InvalidPointerEncoding(cie->offset, + cie->personality_encoding); + return false; + } + if (!reader_->UsableEncoding(cie->personality_encoding)) { + reporter_->UnusablePointerEncoding(cie->offset, + cie->personality_encoding); + return false; + } + // Fetch the personality routine's pointer itself from the data. + cie->personality_address = + reader_->ReadEncodedPointer(data, cie->personality_encoding, + &len); + if (len > size_t(data_end - data)) + return ReportIncomplete(cie); + data += len; + break; + + case 'R': + // The CIE's augmentation data holds the pointer encoding to use + // for addresses in the FDE. + if (data >= data_end) return ReportIncomplete(cie); + cie->pointer_encoding = DwarfPointerEncoding(*data++); + if (!reader_->ValidEncoding(cie->pointer_encoding)) { + reporter_->InvalidPointerEncoding(cie->offset, + cie->pointer_encoding); + return false; + } + if (!reader_->UsableEncoding(cie->pointer_encoding)) { + reporter_->UnusablePointerEncoding(cie->offset, + cie->pointer_encoding); + return false; + } + break; + + case 'S': + // Frames using this CIE are signal delivery frames. + cie->has_z_signal_frame = true; + break; + + default: + // An augmentation we don't recognize. + reporter_->UnrecognizedAugmentation(cie->offset, cie->augmentation); + return false; + } + } + } + + // The CIE's instructions start here. + cie->instructions = cursor; + + return true; +} + +bool CallFrameInfo::ReadFDEFields(FDE *fde) { + const char *cursor = fde->fields; + size_t size; + + fde->address = reader_->ReadEncodedPointer(cursor, fde->cie->pointer_encoding, + &size); + if (size > size_t(fde->end - cursor)) + return ReportIncomplete(fde); + cursor += size; + reader_->SetFunctionBase(fde->address); + + // For the length, we strip off the upper nybble of the encoding used for + // the starting address. + DwarfPointerEncoding length_encoding = + DwarfPointerEncoding(fde->cie->pointer_encoding & 0x0f); + fde->size = reader_->ReadEncodedPointer(cursor, length_encoding, &size); + if (size > size_t(fde->end - cursor)) + return ReportIncomplete(fde); + cursor += size; + + // If the CIE has a 'z' augmentation string, then augmentation data + // appears here. + if (fde->cie->has_z_augmentation) { + size_t data_size = reader_->ReadUnsignedLEB128(cursor, &size); + if (size_t(fde->end - cursor) < size + data_size) + return ReportIncomplete(fde); + cursor += size; + + // In the abstract, we should walk the augmentation string, and extract + // items from the FDE's augmentation data as we encounter augmentation + // string characters that specify their presence: the ordering of items + // in the augmentation string determines the arrangement of values in + // the augmentation data. + // + // In practice, there's only ever one value in FDE augmentation data + // that we support --- the LSDA pointer --- and we have to bail if we + // see any unrecognized augmentation string characters. So if there is + // anything here at all, we know what it is, and where it starts. + if (fde->cie->has_z_lsda) { + // Check whether the LSDA's pointer encoding is usable now: only once + // we've parsed the FDE's starting address do we call reader_-> + // SetFunctionBase, so that the DW_EH_PE_funcrel encoding becomes + // usable. + if (!reader_->UsableEncoding(fde->cie->lsda_encoding)) { + reporter_->UnusablePointerEncoding(fde->cie->offset, + fde->cie->lsda_encoding); + return false; + } + + fde->lsda_address = + reader_->ReadEncodedPointer(cursor, fde->cie->lsda_encoding, &size); + if (size > data_size) + return ReportIncomplete(fde); + // Ideally, we would also complain here if there were unconsumed + // augmentation data. + } + + cursor += data_size; + } + + // The FDE's instructions start after those. + fde->instructions = cursor; + + return true; +} + +bool CallFrameInfo::Start() { + const char *buffer_end = buffer_ + buffer_length_; + const char *cursor; + bool all_ok = true; + const char *entry_end; + bool ok; + + // Traverse all the entries in buffer_, skipping CIEs and offering + // FDEs to the handler. + for (cursor = buffer_; cursor < buffer_end; + cursor = entry_end, all_ok = all_ok && ok) { + FDE fde; + + // Make it easy to skip this entry with 'continue': assume that + // things are not okay until we've checked all the data, and + // prepare the address of the next entry. + ok = false; + + // Read the entry's prologue. + if (!ReadEntryPrologue(cursor, &fde)) { + if (!fde.end) { + // If we couldn't even figure out this entry's extent, then we + // must stop processing entries altogether. + all_ok = false; + break; + } + entry_end = fde.end; + continue; + } + + // The next iteration picks up after this entry. + entry_end = fde.end; + + // Did we see an .eh_frame terminating mark? + if (fde.kind == kTerminator) { + // If there appears to be more data left in the section after the + // terminating mark, warn the user. But this is just a warning; + // we leave all_ok true. + if (fde.end < buffer_end) reporter_->EarlyEHTerminator(fde.offset); + break; + } + + // In this loop, we skip CIEs. We only parse them fully when we + // parse an FDE that refers to them. This limits our memory + // consumption (beyond the buffer itself) to that needed to + // process the largest single entry. + if (fde.kind != kFDE) { + ok = true; + continue; + } + + // Validate the CIE pointer. + if (fde.id > buffer_length_) { + reporter_->CIEPointerOutOfRange(fde.offset, fde.id); + continue; + } + + CIE cie; + + // Parse this FDE's CIE header. + if (!ReadEntryPrologue(buffer_ + fde.id, &cie)) + continue; + // This had better be an actual CIE. + if (cie.kind != kCIE) { + reporter_->BadCIEId(fde.offset, fde.id); + continue; + } + if (!ReadCIEFields(&cie)) + continue; + + // We now have the values that govern both the CIE and the FDE. + cie.cie = &cie; + fde.cie = &cie; + + // Parse the FDE's header. + if (!ReadFDEFields(&fde)) + continue; + + // Call Entry to ask the consumer if they're interested. + if (!handler_->Entry(fde.offset, fde.address, fde.size, + cie.version, cie.augmentation, + cie.return_address_register)) { + // The handler isn't interested in this entry. That's not an error. + ok = true; + continue; + } + + if (cie.has_z_augmentation) { + // Report the personality routine address, if we have one. + if (cie.has_z_personality) { + if (!handler_ + ->PersonalityRoutine(cie.personality_address, + IsIndirectEncoding(cie.personality_encoding))) + continue; + } + + // Report the language-specific data area address, if we have one. + if (cie.has_z_lsda) { + if (!handler_ + ->LanguageSpecificDataArea(fde.lsda_address, + IsIndirectEncoding(cie.lsda_encoding))) + continue; + } + + // If this is a signal-handling frame, report that. + if (cie.has_z_signal_frame) { + if (!handler_->SignalHandler()) + continue; + } + } + + // Interpret the CIE's instructions, and then the FDE's instructions. + State state(reader_, handler_, reporter_, fde.address); + ok = state.InterpretCIE(cie) && state.InterpretFDE(fde); + + // Tell the ByteReader that the function start address from the + // FDE header is no longer valid. + reader_->ClearFunctionBase(); + + // Report the end of the entry. + handler_->End(); + } + + return all_ok; +} + +const char *CallFrameInfo::KindName(EntryKind kind) { + if (kind == CallFrameInfo::kUnknown) + return "entry"; + else if (kind == CallFrameInfo::kCIE) + return "common information entry"; + else if (kind == CallFrameInfo::kFDE) + return "frame description entry"; + else { + assert (kind == CallFrameInfo::kTerminator); + return ".eh_frame sequence terminator"; + } +} + +bool CallFrameInfo::ReportIncomplete(Entry *entry) { + reporter_->Incomplete(entry->offset, entry->kind); + return false; +} + +void CallFrameInfo::Reporter::Incomplete(uint64 offset, + CallFrameInfo::EntryKind kind) { + fprintf(stderr, + "%s: CFI %s at offset 0x%llx in '%s': entry ends early\n", + filename_.c_str(), CallFrameInfo::KindName(kind), offset, + section_.c_str()); +} + +void CallFrameInfo::Reporter::EarlyEHTerminator(uint64 offset) { + fprintf(stderr, + "%s: CFI at offset 0x%llx in '%s': saw end-of-data marker" + " before end of section contents\n", + filename_.c_str(), offset, section_.c_str()); +} + +void CallFrameInfo::Reporter::CIEPointerOutOfRange(uint64 offset, + uint64 cie_offset) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%llx in '%s':" + " CIE pointer is out of range: 0x%llx\n", + filename_.c_str(), offset, section_.c_str(), cie_offset); +} + +void CallFrameInfo::Reporter::BadCIEId(uint64 offset, uint64 cie_offset) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%llx in '%s':" + " CIE pointer does not point to a CIE: 0x%llx\n", + filename_.c_str(), offset, section_.c_str(), cie_offset); +} + +void CallFrameInfo::Reporter::UnrecognizedVersion(uint64 offset, int version) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%llx in '%s':" + " CIE specifies unrecognized version: %d\n", + filename_.c_str(), offset, section_.c_str(), version); +} + +void CallFrameInfo::Reporter::UnrecognizedAugmentation(uint64 offset, + const string &aug) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%llx in '%s':" + " CIE specifies unrecognized augmentation: '%s'\n", + filename_.c_str(), offset, section_.c_str(), aug.c_str()); +} + +void CallFrameInfo::Reporter::InvalidPointerEncoding(uint64 offset, + uint8 encoding) { + fprintf(stderr, + "%s: CFI common information entry at offset 0x%llx in '%s':" + " 'z' augmentation specifies invalid pointer encoding: 0x%02x\n", + filename_.c_str(), offset, section_.c_str(), encoding); +} + +void CallFrameInfo::Reporter::UnusablePointerEncoding(uint64 offset, + uint8 encoding) { + fprintf(stderr, + "%s: CFI common information entry at offset 0x%llx in '%s':" + " 'z' augmentation specifies a pointer encoding for which we have no base address: 0x%02x\n", + filename_.c_str(), offset, section_.c_str(), encoding); +} + +void CallFrameInfo::Reporter::RestoreInCIE(uint64 offset, uint64 insn_offset) { + fprintf(stderr, + "%s: CFI common information entry at offset 0x%llx in '%s':" + " the DW_CFA_restore instruction at offset 0x%llx" + " cannot be used in a common information entry\n", + filename_.c_str(), offset, section_.c_str(), insn_offset); +} + +void CallFrameInfo::Reporter::BadInstruction(uint64 offset, + CallFrameInfo::EntryKind kind, + uint64 insn_offset) { + fprintf(stderr, + "%s: CFI %s at offset 0x%llx in section '%s':" + " the instruction at offset 0x%llx is unrecognized\n", + filename_.c_str(), CallFrameInfo::KindName(kind), + offset, section_.c_str(), insn_offset); +} + +void CallFrameInfo::Reporter::NoCFARule(uint64 offset, + CallFrameInfo::EntryKind kind, + uint64 insn_offset) { + fprintf(stderr, + "%s: CFI %s at offset 0x%llx in section '%s':" + " the instruction at offset 0x%llx assumes that a CFA rule has" + " been set, but none has been set\n", + filename_.c_str(), CallFrameInfo::KindName(kind), offset, + section_.c_str(), insn_offset); +} + +void CallFrameInfo::Reporter::EmptyStateStack(uint64 offset, + CallFrameInfo::EntryKind kind, + uint64 insn_offset) { + fprintf(stderr, + "%s: CFI %s at offset 0x%llx in section '%s':" + " the DW_CFA_restore_state instruction at offset 0x%llx" + " should pop a saved state from the stack, but the stack is empty\n", + filename_.c_str(), CallFrameInfo::KindName(kind), offset, + section_.c_str(), insn_offset); +} + +void CallFrameInfo::Reporter::ClearingCFARule(uint64 offset, + CallFrameInfo::EntryKind kind, + uint64 insn_offset) { + fprintf(stderr, + "%s: CFI %s at offset 0x%llx in section '%s':" + " the DW_CFA_restore_state instruction at offset 0x%llx" + " would clear the CFA rule in effect\n", + filename_.c_str(), CallFrameInfo::KindName(kind), offset, + section_.c_str(), insn_offset); +} + +} // namespace dwarf2reader diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,2449 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// dwarf2reader_cfi_unittest.cc: Unit tests for dwarf2reader::CallFrameInfo + +#include +#include + +// The '.eh_frame' format, used by the Linux C++ ABI for exception +// handling, is poorly specified. To help test our support for .eh_frame, +// if you #define WRITE_ELF while compiling this file, and add the +// 'include' directory from the binutils, gcc, or gdb source tree to the +// #include path, then each test that calls the +// PERHAPS_WRITE_DEBUG_FRAME_FILE or PERHAPS_WRITE_EH_FRAME_FILE will an +// ELF file containing a .debug_frame or .eh_frame section; you can then +// use tools like readelf to examine the test data, and check the tools' +// interpretation against the test's intentions. Each ELF file is named +// "cfitest-TEST", where TEST identifies the particular test. +#ifdef WRITE_ELF +#include +#include +#include +extern "C" { +// To compile with WRITE_ELF, you should add the 'include' directory +// of the binutils, gcc, or gdb source tree to your #include path; +// that directory contains this header. +#include "elf/common.h" +} +#endif + +#include "breakpad_googletest_includes.h" +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/cfi_assembler.h" +#include "common/dwarf/dwarf2reader.h" +#include "google_breakpad/common/breakpad_types.h" + +using google_breakpad::CFISection; +using google_breakpad::TestAssembler::Label; +using google_breakpad::TestAssembler::kBigEndian; +using google_breakpad::TestAssembler::kLittleEndian; +using google_breakpad::TestAssembler::Section; + +using dwarf2reader::DwarfPointerEncoding; +using dwarf2reader::ENDIANNESS_BIG; +using dwarf2reader::ENDIANNESS_LITTLE; +using dwarf2reader::ByteReader; +using dwarf2reader::CallFrameInfo; + +using std::vector; +using testing::InSequence; +using testing::Return; +using testing::Sequence; +using testing::Test; +using testing::_; + +#ifdef WRITE_ELF +void WriteELFFrameSection(const char *filename, const char *section_name, + const CFISection §ion); +#define PERHAPS_WRITE_DEBUG_FRAME_FILE(name, section) \ + WriteELFFrameSection("cfitest-" name, ".debug_frame", section); +#define PERHAPS_WRITE_EH_FRAME_FILE(name, section) \ + WriteELFFrameSection("cfitest-" name, ".eh_frame", section); +#else +#define PERHAPS_WRITE_DEBUG_FRAME_FILE(name, section) +#define PERHAPS_WRITE_EH_FRAME_FILE(name, section) +#endif + +class MockCallFrameInfoHandler: public CallFrameInfo::Handler { + public: + MOCK_METHOD6(Entry, bool(size_t offset, uint64 address, uint64 length, + uint8 version, const string &augmentation, + unsigned return_address)); + MOCK_METHOD2(UndefinedRule, bool(uint64 address, int reg)); + MOCK_METHOD2(SameValueRule, bool(uint64 address, int reg)); + MOCK_METHOD4(OffsetRule, bool(uint64 address, int reg, int base_register, + long offset)); + MOCK_METHOD4(ValOffsetRule, bool(uint64 address, int reg, int base_register, + long offset)); + MOCK_METHOD3(RegisterRule, bool(uint64 address, int reg, int base_register)); + MOCK_METHOD3(ExpressionRule, bool(uint64 address, int reg, + const string &expression)); + MOCK_METHOD3(ValExpressionRule, bool(uint64 address, int reg, + const string &expression)); + MOCK_METHOD0(End, bool()); + MOCK_METHOD2(PersonalityRoutine, bool(uint64 address, bool indirect)); + MOCK_METHOD2(LanguageSpecificDataArea, bool(uint64 address, bool indirect)); + MOCK_METHOD0(SignalHandler, bool()); +}; + +class MockCallFrameErrorReporter: public CallFrameInfo::Reporter { + public: + MockCallFrameErrorReporter() : Reporter("mock filename", "mock section") { } + MOCK_METHOD2(Incomplete, void(uint64, CallFrameInfo::EntryKind)); + MOCK_METHOD1(EarlyEHTerminator, void(uint64)); + MOCK_METHOD2(CIEPointerOutOfRange, void(uint64, uint64)); + MOCK_METHOD2(BadCIEId, void(uint64, uint64)); + MOCK_METHOD2(UnrecognizedVersion, void(uint64, int version)); + MOCK_METHOD2(UnrecognizedAugmentation, void(uint64, const string &)); + MOCK_METHOD2(InvalidPointerEncoding, void(uint64, uint8)); + MOCK_METHOD2(UnusablePointerEncoding, void(uint64, uint8)); + MOCK_METHOD2(RestoreInCIE, void(uint64, uint64)); + MOCK_METHOD3(BadInstruction, void(uint64, CallFrameInfo::EntryKind, uint64)); + MOCK_METHOD3(NoCFARule, void(uint64, CallFrameInfo::EntryKind, uint64)); + MOCK_METHOD3(EmptyStateStack, void(uint64, CallFrameInfo::EntryKind, uint64)); +}; + +struct CFIFixture { + + enum { kCFARegister = CallFrameInfo::Handler::kCFARegister }; + + CFIFixture() { + // Default expectations for the data handler. + // + // - Leave Entry and End without expectations, as it's probably a + // good idea to set those explicitly in each test. + // + // - Expect the *Rule functions to not be called, + // so that each test can simply list the calls they expect. + // + // I gather I could use StrictMock for this, but the manual seems + // to suggest using that only as a last resort, and this isn't so + // bad. + EXPECT_CALL(handler, UndefinedRule(_, _)).Times(0); + EXPECT_CALL(handler, SameValueRule(_, _)).Times(0); + EXPECT_CALL(handler, OffsetRule(_, _, _, _)).Times(0); + EXPECT_CALL(handler, ValOffsetRule(_, _, _, _)).Times(0); + EXPECT_CALL(handler, RegisterRule(_, _, _)).Times(0); + EXPECT_CALL(handler, ExpressionRule(_, _, _)).Times(0); + EXPECT_CALL(handler, ValExpressionRule(_, _, _)).Times(0); + EXPECT_CALL(handler, PersonalityRoutine(_, _)).Times(0); + EXPECT_CALL(handler, LanguageSpecificDataArea(_, _)).Times(0); + EXPECT_CALL(handler, SignalHandler()).Times(0); + + // Default expectations for the error/warning reporer. + EXPECT_CALL(reporter, Incomplete(_, _)).Times(0); + EXPECT_CALL(reporter, EarlyEHTerminator(_)).Times(0); + EXPECT_CALL(reporter, CIEPointerOutOfRange(_, _)).Times(0); + EXPECT_CALL(reporter, BadCIEId(_, _)).Times(0); + EXPECT_CALL(reporter, UnrecognizedVersion(_, _)).Times(0); + EXPECT_CALL(reporter, UnrecognizedAugmentation(_, _)).Times(0); + EXPECT_CALL(reporter, InvalidPointerEncoding(_, _)).Times(0); + EXPECT_CALL(reporter, UnusablePointerEncoding(_, _)).Times(0); + EXPECT_CALL(reporter, RestoreInCIE(_, _)).Times(0); + EXPECT_CALL(reporter, BadInstruction(_, _, _)).Times(0); + EXPECT_CALL(reporter, NoCFARule(_, _, _)).Times(0); + EXPECT_CALL(reporter, EmptyStateStack(_, _, _)).Times(0); + } + + MockCallFrameInfoHandler handler; + MockCallFrameErrorReporter reporter; +}; + +class CFI: public CFIFixture, public Test { }; + +TEST_F(CFI, EmptyRegion) { + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + static const char data[1] = { 42 }; + + ByteReader byte_reader(ENDIANNESS_BIG); + CallFrameInfo parser(data, 0, &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +TEST_F(CFI, IncompleteLength32) { + CFISection section(kBigEndian, 8); + section + // Not even long enough for an initial length. + .D16(0xa0f) + // Padding to keep valgrind happy. We subtract these off when we + // construct the parser. + .D16(0); + + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + + EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown)) + .WillOnce(Return()); + + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(8); + CallFrameInfo parser(contents.data(), contents.size() - 2, + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +TEST_F(CFI, IncompleteLength64) { + CFISection section(kLittleEndian, 4); + section + // An incomplete 64-bit DWARF initial length. + .D32(0xffffffff).D32(0x71fbaec2) + // Padding to keep valgrind happy. We subtract these off when we + // construct the parser. + .D32(0); + + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + + EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown)) + .WillOnce(Return()); + + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + + ByteReader byte_reader(ENDIANNESS_LITTLE); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(contents.data(), contents.size() - 4, + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +TEST_F(CFI, IncompleteId32) { + CFISection section(kBigEndian, 8); + section + .D32(3) // Initial length, not long enough for id + .D8(0xd7).D8(0xe5).D8(0xf1) // incomplete id + .CIEHeader(8727, 3983, 8889, 3, "") + .FinishEntry(); + + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + + EXPECT_CALL(reporter, Incomplete(_, CallFrameInfo::kUnknown)) + .WillOnce(Return()); + + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(8); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +TEST_F(CFI, BadId32) { + CFISection section(kBigEndian, 8); + section + .D32(0x100) // Initial length + .D32(0xe802fade) // bogus ID + .Append(0x100 - 4, 0x42); // make the length true + section + .CIEHeader(1672, 9872, 8529, 3, "") + .FinishEntry(); + + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + + EXPECT_CALL(reporter, CIEPointerOutOfRange(_, 0xe802fade)) + .WillOnce(Return()); + + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(8); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +// A lone CIE shouldn't cause any handler calls. +TEST_F(CFI, SingleCIE) { + CFISection section(kLittleEndian, 4); + section.CIEHeader(0xffe799a8, 0x3398dcdd, 0x6e9683de, 3, ""); + section.Append(10, dwarf2reader::DW_CFA_nop); + section.FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("SingleCIE", section); + + EXPECT_CALL(handler, Entry(_, _, _, _, _, _)).Times(0); + EXPECT_CALL(handler, End()).Times(0); + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_LITTLE); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +// One FDE, one CIE. +TEST_F(CFI, OneFDE) { + CFISection section(kBigEndian, 4); + Label cie; + section + .Mark(&cie) + .CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 3, "") + .FinishEntry() + .FDEHeader(cie, 0x7714740d, 0x3d5a10cd) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("OneFDE", section); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, 0x7714740d, 0x3d5a10cd, 3, "", 0x6b6efb87)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +// Two FDEs share a CIE. +TEST_F(CFI, TwoFDEsOneCIE) { + CFISection section(kBigEndian, 4); + Label cie; + section + // First FDE. readelf complains about this one because it makes + // a forward reference to its CIE. + .FDEHeader(cie, 0xa42744df, 0xa3b42121) + .FinishEntry() + // CIE. + .Mark(&cie) + .CIEHeader(0x04f7dc7b, 0x3d00c05f, 0xbd43cb59, 3, "") + .FinishEntry() + // Second FDE. + .FDEHeader(cie, 0x6057d391, 0x700f608d) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("TwoFDEsOneCIE", section); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, 0xa42744df, 0xa3b42121, 3, "", 0xbd43cb59)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, 0x6057d391, 0x700f608d, 3, "", 0xbd43cb59)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +// Two FDEs, two CIEs. +TEST_F(CFI, TwoFDEsTwoCIEs) { + CFISection section(kLittleEndian, 8); + Label cie1, cie2; + section + // First CIE. + .Mark(&cie1) + .CIEHeader(0x694d5d45, 0x4233221b, 0xbf45e65a, 3, "") + .FinishEntry() + // First FDE which cites second CIE. readelf complains about + // this one because it makes a forward reference to its CIE. + .FDEHeader(cie2, 0x778b27dfe5871f05ULL, 0x324ace3448070926ULL) + .FinishEntry() + // Second FDE, which cites first CIE. + .FDEHeader(cie1, 0xf6054ca18b10bf5fULL, 0x45fdb970d8bca342ULL) + .FinishEntry() + // Second CIE. + .Mark(&cie2) + .CIEHeader(0xfba3fad7, 0x6287e1fd, 0x61d2c581, 2, "") + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("TwoFDEsTwoCIEs", section); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, 0x778b27dfe5871f05ULL, 0x324ace3448070926ULL, 2, + "", 0x61d2c581)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, 0xf6054ca18b10bf5fULL, 0x45fdb970d8bca342ULL, 3, + "", 0xbf45e65a)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_LITTLE); + byte_reader.SetAddressSize(8); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +// An FDE whose CIE specifies a version we don't recognize. +TEST_F(CFI, BadVersion) { + CFISection section(kBigEndian, 4); + Label cie1, cie2; + section + .Mark(&cie1) + .CIEHeader(0xca878cf0, 0x7698ec04, 0x7b616f54, 0x52, "") + .FinishEntry() + // We should skip this entry, as its CIE specifies a version we + // don't recognize. + .FDEHeader(cie1, 0x08852292, 0x2204004a) + .FinishEntry() + // Despite the above, we should visit this entry. + .Mark(&cie2) + .CIEHeader(0x7c3ae7c9, 0xb9b9a512, 0x96cb3264, 3, "") + .FinishEntry() + .FDEHeader(cie2, 0x2094735a, 0x6e875501) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("BadVersion", section); + + EXPECT_CALL(reporter, UnrecognizedVersion(_, 0x52)) + .WillOnce(Return()); + + { + InSequence s; + // We should see no mention of the first FDE, but we should get + // a call to Entry for the second. + EXPECT_CALL(handler, Entry(_, 0x2094735a, 0x6e875501, 3, "", + 0x96cb3264)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +// An FDE whose CIE specifies an augmentation we don't recognize. +TEST_F(CFI, BadAugmentation) { + CFISection section(kBigEndian, 4); + Label cie1, cie2; + section + .Mark(&cie1) + .CIEHeader(0x4be22f75, 0x2492236e, 0x6b6efb87, 3, "spaniels!") + .FinishEntry() + // We should skip this entry, as its CIE specifies an + // augmentation we don't recognize. + .FDEHeader(cie1, 0x7714740d, 0x3d5a10cd) + .FinishEntry() + // Despite the above, we should visit this entry. + .Mark(&cie2) + .CIEHeader(0xf8bc4399, 0x8cf09931, 0xf2f519b2, 3, "") + .FinishEntry() + .FDEHeader(cie2, 0x7bf0fda0, 0xcbcd28d8) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("BadAugmentation", section); + + EXPECT_CALL(reporter, UnrecognizedAugmentation(_, "spaniels!")) + .WillOnce(Return()); + + { + InSequence s; + // We should see no mention of the first FDE, but we should get + // a call to Entry for the second. + EXPECT_CALL(handler, Entry(_, 0x7bf0fda0, 0xcbcd28d8, 3, "", + 0xf2f519b2)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_FALSE(parser.Start()); +} + +// The return address column field is a byte in CFI version 1 +// (DWARF2), but a ULEB128 value in version 3 (DWARF3). +TEST_F(CFI, CIEVersion1ReturnColumn) { + CFISection section(kBigEndian, 4); + Label cie; + section + // CIE, using the version 1 format: return column is a ubyte. + .Mark(&cie) + // Use a value for the return column that is parsed differently + // as a ubyte and as a ULEB128. + .CIEHeader(0xbcdea24f, 0x5be28286, 0x9f, 1, "") + .FinishEntry() + // FDE, citing that CIE. + .FDEHeader(cie, 0xb8d347b5, 0x825e55dc) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion1ReturnColumn", section); + + { + InSequence s; + EXPECT_CALL(handler, Entry(_, 0xb8d347b5, 0x825e55dc, 1, "", 0x9f)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +// The return address column field is a byte in CFI version 1 +// (DWARF2), but a ULEB128 value in version 3 (DWARF3). +TEST_F(CFI, CIEVersion3ReturnColumn) { + CFISection section(kBigEndian, 4); + Label cie; + section + // CIE, using the version 3 format: return column is a ULEB128. + .Mark(&cie) + // Use a value for the return column that is parsed differently + // as a ubyte and as a ULEB128. + .CIEHeader(0x0ab4758d, 0xc010fdf7, 0x89, 3, "") + .FinishEntry() + // FDE, citing that CIE. + .FDEHeader(cie, 0x86763f2b, 0x2a66dc23) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("CIEVersion3ReturnColumn", section); + + { + InSequence s; + EXPECT_CALL(handler, Entry(_, 0x86763f2b, 0x2a66dc23, 3, "", 0x89)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + string contents; + EXPECT_TRUE(section.GetContents(&contents)); + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + EXPECT_TRUE(parser.Start()); +} + +struct CFIInsnFixture: public CFIFixture { + CFIInsnFixture() : CFIFixture() { + data_factor = 0xb6f; + return_register = 0x9be1ed9f; + version = 3; + cfa_base_register = 0x383a3aa; + cfa_offset = 0xf748; + } + + // Prepare SECTION to receive FDE instructions. + // + // - Append a stock CIE header that establishes the fixture's + // code_factor, data_factor, return_register, version, and + // augmentation values. + // - Have the CIE set up a CFA rule using cfa_base_register and + // cfa_offset. + // - Append a stock FDE header, referring to the above CIE, for the + // fde_size bytes at fde_start. Choose fde_start and fde_size + // appropriately for the section's address size. + // - Set appropriate expectations on handler in sequence s for the + // frame description entry and the CIE's CFA rule. + // + // On return, SECTION is ready to have FDE instructions appended to + // it, and its FinishEntry member called. + void StockCIEAndFDE(CFISection *section) { + // Choose appropriate constants for our address size. + if (section->AddressSize() == 4) { + fde_start = 0xc628ecfbU; + fde_size = 0x5dee04a2; + code_factor = 0x60b; + } else { + assert(section->AddressSize() == 8); + fde_start = 0x0005c57ce7806bd3ULL; + fde_size = 0x2699521b5e333100ULL; + code_factor = 0x01008e32855274a8ULL; + } + + // Create the CIE. + (*section) + .Mark(&cie_label) + .CIEHeader(code_factor, data_factor, return_register, version, + "") + .D8(dwarf2reader::DW_CFA_def_cfa) + .ULEB128(cfa_base_register) + .ULEB128(cfa_offset) + .FinishEntry(); + + // Create the FDE. + section->FDEHeader(cie_label, fde_start, fde_size); + + // Expect an Entry call for the FDE and a ValOffsetRule call for the + // CIE's CFA rule. + EXPECT_CALL(handler, Entry(_, fde_start, fde_size, version, "", + return_register)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(fde_start, kCFARegister, + cfa_base_register, cfa_offset)) + .InSequence(s) + .WillOnce(Return(true)); + } + + // Run the contents of SECTION through a CallFrameInfo parser, + // expecting parser.Start to return SUCCEEDS + void ParseSection(CFISection *section, bool succeeds = true) { + string contents; + EXPECT_TRUE(section->GetContents(&contents)); + dwarf2reader::Endianness endianness; + if (section->endianness() == kBigEndian) + endianness = ENDIANNESS_BIG; + else { + assert(section->endianness() == kLittleEndian); + endianness = ENDIANNESS_LITTLE; + } + ByteReader byte_reader(endianness); + byte_reader.SetAddressSize(section->AddressSize()); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter); + if (succeeds) + EXPECT_TRUE(parser.Start()); + else + EXPECT_FALSE(parser.Start()); + } + + Label cie_label; + Sequence s; + uint64 code_factor; + int data_factor; + unsigned return_register; + unsigned version; + unsigned cfa_base_register; + int cfa_offset; + uint64 fde_start, fde_size; +}; + +class CFIInsn: public CFIInsnFixture, public Test { }; + +TEST_F(CFIInsn, DW_CFA_set_loc) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_set_loc).D32(0xb1ee3e7a) + // Use DW_CFA_def_cfa to force a handler call that we can use to + // check the effect of the DW_CFA_set_loc. + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x4defb431).ULEB128(0x6d17b0ee) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_set_loc", section); + + EXPECT_CALL(handler, + ValOffsetRule(0xb1ee3e7a, kCFARegister, 0x4defb431, 0x6d17b0ee)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_advance_loc) { + CFISection section(kBigEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_advance_loc | 0x2a) + // Use DW_CFA_def_cfa to force a handler call that we can use to + // check the effect of the DW_CFA_advance_loc. + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x5bbb3715).ULEB128(0x0186c7bf) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc", section); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start + 0x2a * code_factor, + kCFARegister, 0x5bbb3715, 0x0186c7bf)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_advance_loc1) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_advance_loc1).D8(0xd8) + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x69d5696a).ULEB128(0x1eb7fc93) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc1", section); + + EXPECT_CALL(handler, + ValOffsetRule((fde_start + 0xd8 * code_factor), + kCFARegister, 0x69d5696a, 0x1eb7fc93)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_advance_loc2) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_advance_loc2).D16(0x3adb) + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x3a368bed).ULEB128(0x3194ee37) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc2", section); + + EXPECT_CALL(handler, + ValOffsetRule((fde_start + 0x3adb * code_factor), + kCFARegister, 0x3a368bed, 0x3194ee37)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_advance_loc4) { + CFISection section(kBigEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_advance_loc4).D32(0x15813c88) + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x135270c5).ULEB128(0x24bad7cb) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc4", section); + + EXPECT_CALL(handler, + ValOffsetRule((fde_start + 0x15813c88ULL * code_factor), + kCFARegister, 0x135270c5, 0x24bad7cb)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_MIPS_advance_loc8) { + code_factor = 0x2d; + CFISection section(kBigEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_MIPS_advance_loc8).D64(0x3c4f3945b92c14ULL) + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0xe17ed602).ULEB128(0x3d162e7f) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_advance_loc8", section); + + EXPECT_CALL(handler, + ValOffsetRule((fde_start + 0x3c4f3945b92c14ULL * code_factor), + kCFARegister, 0xe17ed602, 0x3d162e7f)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x4e363a85).ULEB128(0x815f9aa7) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("DW_CFA_def_cfa", section); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x4e363a85, 0x815f9aa7)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa_sf) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_sf).ULEB128(0x8ccb32b7).LEB128(0x9ea) + .D8(dwarf2reader::DW_CFA_def_cfa_sf).ULEB128(0x9b40f5da).LEB128(-0x40a2) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x8ccb32b7, + 0x9ea * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x9b40f5da, + -0x40a2 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa_register) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_register).ULEB128(0x3e7e9363) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x3e7e9363, cfa_offset)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +// DW_CFA_def_cfa_register should have no effect when applied to a +// non-base/offset rule. +TEST_F(CFIInsn, DW_CFA_def_cfa_registerBadRule) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("needle in a haystack") + .D8(dwarf2reader::DW_CFA_def_cfa_register).ULEB128(0xf1b49e49) + .FinishEntry(); + + EXPECT_CALL(handler, + ValExpressionRule(fde_start, kCFARegister, + "needle in a haystack")) + .WillRepeatedly(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa_offset) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x1e8e3b9b) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, cfa_base_register, + 0x1e8e3b9b)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa_offset_sf) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_offset_sf).LEB128(0x970) + .D8(dwarf2reader::DW_CFA_def_cfa_offset_sf).LEB128(-0x2cd) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, cfa_base_register, + 0x970 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, cfa_base_register, + -0x2cd * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +// DW_CFA_def_cfa_offset should have no effect when applied to a +// non-base/offset rule. +TEST_F(CFIInsn, DW_CFA_def_cfa_offsetBadRule) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("six ways to Sunday") + .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x1e8e3b9b) + .FinishEntry(); + + EXPECT_CALL(handler, + ValExpressionRule(fde_start, kCFARegister, "six ways to Sunday")) + .WillRepeatedly(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_def_cfa_expression) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_def_cfa_expression).Block("eating crow") + .FinishEntry(); + + EXPECT_CALL(handler, ValExpressionRule(fde_start, kCFARegister, + "eating crow")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_undefined) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x300ce45d) + .FinishEntry(); + + EXPECT_CALL(handler, UndefinedRule(fde_start, 0x300ce45d)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_same_value) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x3865a760) + .FinishEntry(); + + EXPECT_CALL(handler, SameValueRule(fde_start, 0x3865a760)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_offset) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset | 0x2c).ULEB128(0x9f6) + .FinishEntry(); + + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x2c, kCFARegister, 0x9f6 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_offset_extended) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset_extended).ULEB128(0x402b).ULEB128(0xb48) + .FinishEntry(); + + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x402b, kCFARegister, 0xb48 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_offset_extended_sf) { + CFISection section(kBigEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset_extended_sf) + .ULEB128(0x997c23ee).LEB128(0x2d00) + .D8(dwarf2reader::DW_CFA_offset_extended_sf) + .ULEB128(0x9519eb82).LEB128(-0xa77) + .FinishEntry(); + + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x997c23ee, + kCFARegister, 0x2d00 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x9519eb82, + kCFARegister, -0xa77 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_val_offset) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x623562fe).ULEB128(0x673) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, 0x623562fe, + kCFARegister, 0x673 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_val_offset_sf) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_offset_sf).ULEB128(0x6f4f).LEB128(0xaab) + .D8(dwarf2reader::DW_CFA_val_offset_sf).ULEB128(0x2483).LEB128(-0x8a2) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, 0x6f4f, + kCFARegister, 0xaab * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, + ValOffsetRule(fde_start, 0x2483, + kCFARegister, -0x8a2 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_register) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_register).ULEB128(0x278d18f9).ULEB128(0x1a684414) + .FinishEntry(); + + EXPECT_CALL(handler, RegisterRule(fde_start, 0x278d18f9, 0x1a684414)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_expression) { + CFISection section(kBigEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0xa1619fb2) + .Block("plus ça change, plus c'est la même chose") + .FinishEntry(); + + EXPECT_CALL(handler, + ExpressionRule(fde_start, 0xa1619fb2, + "plus ça change, plus c'est la même chose")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_val_expression) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0xc5e4a9e3) + .Block("he who has the gold makes the rules") + .FinishEntry(); + + EXPECT_CALL(handler, + ValExpressionRule(fde_start, 0xc5e4a9e3, + "he who has the gold makes the rules")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_restore) { + CFISection section(kLittleEndian, 8); + code_factor = 0x01bd188a9b1fa083ULL; + data_factor = -0x1ac8; + return_register = 0x8c35b049; + version = 2; + fde_start = 0x2d70fe998298bbb1ULL; + fde_size = 0x46ccc2e63cf0b108ULL; + Label cie; + section + .Mark(&cie) + .CIEHeader(code_factor, data_factor, return_register, version, + "") + // Provide a CFA rule, because register rules require them. + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x6ca1d50e).ULEB128(0x372e38e8) + // Provide an offset(N) rule for register 0x3c. + .D8(dwarf2reader::DW_CFA_offset | 0x3c).ULEB128(0xb348) + .FinishEntry() + // In the FDE... + .FDEHeader(cie, fde_start, fde_size) + // At a second address, provide a new offset(N) rule for register 0x3c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0x13) + .D8(dwarf2reader::DW_CFA_offset | 0x3c).ULEB128(0x9a50) + // At a third address, restore the original rule for register 0x3c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0x01) + .D8(dwarf2reader::DW_CFA_restore | 0x3c) + .FinishEntry(); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, fde_start, fde_size, version, "", return_register)) + .WillOnce(Return(true)); + // CIE's CFA rule. + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x6ca1d50e, 0x372e38e8)) + .WillOnce(Return(true)); + // CIE's rule for register 0x3c. + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x3c, kCFARegister, 0xb348 * data_factor)) + .WillOnce(Return(true)); + // FDE's rule for register 0x3c. + EXPECT_CALL(handler, + OffsetRule(fde_start + 0x13 * code_factor, 0x3c, + kCFARegister, 0x9a50 * data_factor)) + .WillOnce(Return(true)); + // Restore CIE's rule for register 0x3c. + EXPECT_CALL(handler, + OffsetRule(fde_start + (0x13 + 0x01) * code_factor, 0x3c, + kCFARegister, 0xb348 * data_factor)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_restoreNoRule) { + CFISection section(kBigEndian, 4); + code_factor = 0x005f78143c1c3b82ULL; + data_factor = 0x25d0; + return_register = 0xe8; + version = 1; + fde_start = 0x4062e30f; + fde_size = 0x5302a389; + Label cie; + section + .Mark(&cie) + .CIEHeader(code_factor, data_factor, return_register, version, "") + // Provide a CFA rule, because register rules require them. + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x470aa334).ULEB128(0x099ef127) + .FinishEntry() + // In the FDE... + .FDEHeader(cie, fde_start, fde_size) + // At a second address, provide an offset(N) rule for register 0x2c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0x7) + .D8(dwarf2reader::DW_CFA_offset | 0x2c).ULEB128(0x1f47) + // At a third address, restore the (missing) CIE rule for register 0x2c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0xb) + .D8(dwarf2reader::DW_CFA_restore | 0x2c) + .FinishEntry(); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, fde_start, fde_size, version, "", return_register)) + .WillOnce(Return(true)); + // CIE's CFA rule. + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x470aa334, 0x099ef127)) + .WillOnce(Return(true)); + // FDE's rule for register 0x2c. + EXPECT_CALL(handler, + OffsetRule(fde_start + 0x7 * code_factor, 0x2c, + kCFARegister, 0x1f47 * data_factor)) + .WillOnce(Return(true)); + // Restore CIE's (missing) rule for register 0x2c. + EXPECT_CALL(handler, + SameValueRule(fde_start + (0x7 + 0xb) * code_factor, 0x2c)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_restore_extended) { + CFISection section(kBigEndian, 4); + code_factor = 0x126e; + data_factor = -0xd8b; + return_register = 0x77711787; + version = 3; + fde_start = 0x01f55a45; + fde_size = 0x452adb80; + Label cie; + section + .Mark(&cie) + .CIEHeader(code_factor, data_factor, return_register, version, + "", true /* dwarf64 */ ) + // Provide a CFA rule, because register rules require them. + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x56fa0edd).ULEB128(0x097f78a5) + // Provide an offset(N) rule for register 0x0f9b8a1c. + .D8(dwarf2reader::DW_CFA_offset_extended) + .ULEB128(0x0f9b8a1c).ULEB128(0xc979) + .FinishEntry() + // In the FDE... + .FDEHeader(cie, fde_start, fde_size) + // At a second address, provide a new offset(N) rule for reg 0x0f9b8a1c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0x3) + .D8(dwarf2reader::DW_CFA_offset_extended) + .ULEB128(0x0f9b8a1c).ULEB128(0x3b7b) + // At a third address, restore the original rule for register 0x0f9b8a1c. + .D8(dwarf2reader::DW_CFA_advance_loc | 0x04) + .D8(dwarf2reader::DW_CFA_restore_extended).ULEB128(0x0f9b8a1c) + .FinishEntry(); + + { + InSequence s; + EXPECT_CALL(handler, + Entry(_, fde_start, fde_size, version, "", return_register)) + .WillOnce(Return(true)); + // CIE's CFA rule. + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x56fa0edd, 0x097f78a5)) + .WillOnce(Return(true)); + // CIE's rule for register 0x0f9b8a1c. + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x0f9b8a1c, kCFARegister, + 0xc979 * data_factor)) + .WillOnce(Return(true)); + // FDE's rule for register 0x0f9b8a1c. + EXPECT_CALL(handler, + OffsetRule(fde_start + 0x3 * code_factor, 0x0f9b8a1c, + kCFARegister, 0x3b7b * data_factor)) + .WillOnce(Return(true)); + // Restore CIE's rule for register 0x0f9b8a1c. + EXPECT_CALL(handler, + OffsetRule(fde_start + (0x3 + 0x4) * code_factor, 0x0f9b8a1c, + kCFARegister, 0xc979 * data_factor)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + } + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_remember_and_restore_state) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + + // We create a state, save it, modify it, and then restore. We + // refer to the state that is overridden the restore as the + // "outgoing" state, and the restored state the "incoming" state. + // + // Register outgoing incoming expect + // 1 offset(N) no rule new "same value" rule + // 2 register(R) offset(N) report changed rule + // 3 offset(N) offset(M) report changed offset + // 4 offset(N) offset(N) no report + // 5 offset(N) no rule new "same value" rule + section + // Create the "incoming" state, which we will save and later restore. + .D8(dwarf2reader::DW_CFA_offset | 2).ULEB128(0x9806) + .D8(dwarf2reader::DW_CFA_offset | 3).ULEB128(0x995d) + .D8(dwarf2reader::DW_CFA_offset | 4).ULEB128(0x7055) + .D8(dwarf2reader::DW_CFA_remember_state) + // Advance to a new instruction; an implementation could legitimately + // ignore all but the final rule for a given register at a given address. + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + // Create the "outgoing" state, which we will discard. + .D8(dwarf2reader::DW_CFA_offset | 1).ULEB128(0xea1a) + .D8(dwarf2reader::DW_CFA_register).ULEB128(2).ULEB128(0x1d2a3767) + .D8(dwarf2reader::DW_CFA_offset | 3).ULEB128(0xdd29) + .D8(dwarf2reader::DW_CFA_offset | 5).ULEB128(0xf1ce) + // At a third address, restore the incoming state. + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + uint64 addr = fde_start; + + // Expect the incoming rules to be reported. + EXPECT_CALL(handler, OffsetRule(addr, 2, kCFARegister, 0x9806 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0x995d * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 4, kCFARegister, 0x7055 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + + addr += code_factor; + + // After the save, we establish the outgoing rule set. + EXPECT_CALL(handler, OffsetRule(addr, 1, kCFARegister, 0xea1a * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(addr, 2, 0x1d2a3767)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0xdd29 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 5, kCFARegister, 0xf1ce * data_factor)) + .InSequence(s).WillOnce(Return(true)); + + addr += code_factor; + + // Finally, after the restore, expect to see the differences from + // the outgoing to the incoming rules reported. + EXPECT_CALL(handler, SameValueRule(addr, 1)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 2, kCFARegister, 0x9806 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(addr, 3, kCFARegister, 0x995d * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, SameValueRule(addr, 5)) + .InSequence(s).WillOnce(Return(true)); + + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +// Check that restoring a rule set reports changes to the CFA rule. +TEST_F(CFIInsn, DW_CFA_remember_and_restore_stateCFA) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + + section + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_def_cfa_offset).ULEB128(0x90481102) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor, kCFARegister, + cfa_base_register, 0x90481102)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor * 2, kCFARegister, + cfa_base_register, cfa_offset)) + .InSequence(s).WillOnce(Return(true)); + + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_nop) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_nop) + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x3fb8d4f1).ULEB128(0x078dc67b) + .D8(dwarf2reader::DW_CFA_nop) + .FinishEntry(); + + EXPECT_CALL(handler, + ValOffsetRule(fde_start, kCFARegister, 0x3fb8d4f1, 0x078dc67b)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_GNU_window_save) { + CFISection section(kBigEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_GNU_window_save) + .FinishEntry(); + + // Don't include all the rules in any particular sequence. + + // The caller's %o0-%o7 have become the callee's %i0-%i7. This is + // the GCC register numbering. + for (int i = 8; i < 16; i++) + EXPECT_CALL(handler, RegisterRule(fde_start, i, i + 16)) + .WillOnce(Return(true)); + // The caller's %l0-%l7 and %i0-%i7 have been saved at the top of + // its frame. + for (int i = 16; i < 32; i++) + EXPECT_CALL(handler, OffsetRule(fde_start, i, kCFARegister, (i-16) * 4)) + .WillOnce(Return(true)); + + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_GNU_args_size) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_GNU_args_size).ULEB128(0xeddfa520) + // Verify that we see this, meaning we parsed the above properly. + .D8(dwarf2reader::DW_CFA_offset | 0x23).ULEB128(0x269) + .FinishEntry(); + + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x23, kCFARegister, 0x269 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIInsn, DW_CFA_GNU_negative_offset_extended) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_GNU_negative_offset_extended) + .ULEB128(0x430cc87a).ULEB128(0x613) + .FinishEntry(); + + EXPECT_CALL(handler, + OffsetRule(fde_start, 0x430cc87a, + kCFARegister, -0x613 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion); +} + +// Three FDEs: skip the second +TEST_F(CFIInsn, SkipFDE) { + CFISection section(kBigEndian, 4); + Label cie; + section + // CIE, used by all FDEs. + .Mark(&cie) + .CIEHeader(0x010269f2, 0x9177, 0xedca5849, 2, "") + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(0x42ed390b).ULEB128(0x98f43aad) + .FinishEntry() + // First FDE. + .FDEHeader(cie, 0xa870ebdd, 0x60f6aa4) + .D8(dwarf2reader::DW_CFA_register).ULEB128(0x3a860351).ULEB128(0x6c9a6bcf) + .FinishEntry() + // Second FDE. + .FDEHeader(cie, 0xc534f7c0, 0xf6552e9, true /* dwarf64 */) + .D8(dwarf2reader::DW_CFA_register).ULEB128(0x1b62c234).ULEB128(0x26586b18) + .FinishEntry() + // Third FDE. + .FDEHeader(cie, 0xf681cfc8, 0x7e4594e) + .D8(dwarf2reader::DW_CFA_register).ULEB128(0x26c53934).ULEB128(0x18eeb8a4) + .FinishEntry(); + + { + InSequence s; + + // Process the first FDE. + EXPECT_CALL(handler, Entry(_, 0xa870ebdd, 0x60f6aa4, 2, "", 0xedca5849)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(0xa870ebdd, kCFARegister, + 0x42ed390b, 0x98f43aad)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(0xa870ebdd, 0x3a860351, 0x6c9a6bcf)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .WillOnce(Return(true)); + + // Skip the second FDE. + EXPECT_CALL(handler, Entry(_, 0xc534f7c0, 0xf6552e9, 2, "", 0xedca5849)) + .WillOnce(Return(false)); + + // Process the third FDE. + EXPECT_CALL(handler, Entry(_, 0xf681cfc8, 0x7e4594e, 2, "", 0xedca5849)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(0xf681cfc8, kCFARegister, + 0x42ed390b, 0x98f43aad)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(0xf681cfc8, 0x26c53934, 0x18eeb8a4)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .WillOnce(Return(true)); + } + + ParseSection(§ion); +} + +// Quit processing in the middle of an entry's instructions. +TEST_F(CFIInsn, QuitMidentry) { + CFISection section(kLittleEndian, 8); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_register).ULEB128(0xe0cf850d).ULEB128(0x15aab431) + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x46750aa5).Block("meat") + .FinishEntry(); + + EXPECT_CALL(handler, RegisterRule(fde_start, 0xe0cf850d, 0x15aab431)) + .InSequence(s).WillOnce(Return(false)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseSection(§ion, false); +} + +class CFIRestore: public CFIInsnFixture, public Test { }; + +TEST_F(CFIRestore, RestoreUndefinedRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x0bac878e) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, UndefinedRule(fde_start, 0x0bac878e)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreUndefinedRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x7dedff5f) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x7dedff5f) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, UndefinedRule(fde_start, 0x7dedff5f)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, SameValueRule(fde_start + code_factor, 0x7dedff5f)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + 2 * code_factor, 0x7dedff5f)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreSameValueRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0xadbc9b3a) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, SameValueRule(fde_start, 0xadbc9b3a)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreSameValueRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_same_value).ULEB128(0x3d90dcb5) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x3d90dcb5) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, SameValueRule(fde_start, 0x3d90dcb5)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0x3d90dcb5)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, SameValueRule(fde_start + 2 * code_factor, 0x3d90dcb5)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreOffsetRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset | 0x14).ULEB128(0xb6f) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, OffsetRule(fde_start, 0x14, + kCFARegister, 0xb6f * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreOffsetRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0xeb7) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x21) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, OffsetRule(fde_start, 0x21, + kCFARegister, 0xeb7 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0x21)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(fde_start + 2 * code_factor, 0x21, + kCFARegister, 0xeb7 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreOffsetRuleChangedOffset) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0x134) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_offset | 0x21).ULEB128(0xf4f) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, OffsetRule(fde_start, 0x21, + kCFARegister, 0x134 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(fde_start + code_factor, 0x21, + kCFARegister, 0xf4f * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, OffsetRule(fde_start + 2 * code_factor, 0x21, + kCFARegister, 0x134 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValOffsetRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x829caee6).ULEB128(0xe4c) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ValOffsetRule(fde_start, 0x829caee6, + kCFARegister, 0xe4c * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValOffsetRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0xf17c36d6).ULEB128(0xeb7) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xf17c36d6) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ValOffsetRule(fde_start, 0xf17c36d6, + kCFARegister, 0xeb7 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xf17c36d6)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(fde_start + 2 * code_factor, 0xf17c36d6, + kCFARegister, 0xeb7 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValOffsetRuleChangedValOffset) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x2cf0ab1b).ULEB128(0x562) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_val_offset).ULEB128(0x2cf0ab1b).ULEB128(0xe88) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ValOffsetRule(fde_start, 0x2cf0ab1b, + kCFARegister, 0x562 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(fde_start + code_factor, 0x2cf0ab1b, + kCFARegister, 0xe88 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(fde_start + 2 * code_factor, 0x2cf0ab1b, + kCFARegister, 0x562 * data_factor)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreRegisterRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_register).ULEB128(0x77514acc).ULEB128(0x464de4ce) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, RegisterRule(fde_start, 0x77514acc, 0x464de4ce)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreRegisterRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_register).ULEB128(0xe39acce5).ULEB128(0x095f1559) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xe39acce5) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, RegisterRule(fde_start, 0xe39acce5, 0x095f1559)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xe39acce5)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(fde_start + 2 * code_factor, 0xe39acce5, + 0x095f1559)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreRegisterRuleChangedRegister) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_register).ULEB128(0xd40e21b1).ULEB128(0x16607d6a) + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_register).ULEB128(0xd40e21b1).ULEB128(0xbabb4742) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, RegisterRule(fde_start, 0xd40e21b1, 0x16607d6a)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(fde_start + code_factor, 0xd40e21b1, + 0xbabb4742)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, RegisterRule(fde_start + 2 * code_factor, 0xd40e21b1, + 0x16607d6a)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreExpressionRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x666ae152).Block("dwarf") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ExpressionRule(fde_start, 0x666ae152, "dwarf")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreExpressionRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0xb5ca5c46).Block("elf") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xb5ca5c46) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ExpressionRule(fde_start, 0xb5ca5c46, "elf")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xb5ca5c46)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ExpressionRule(fde_start + 2 * code_factor, 0xb5ca5c46, + "elf")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreExpressionRuleChangedExpression) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x500f5739).Block("smurf") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_expression).ULEB128(0x500f5739).Block("orc") + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ExpressionRule(fde_start, 0x500f5739, "smurf")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ExpressionRule(fde_start + code_factor, 0x500f5739, + "orc")) + .InSequence(s).WillOnce(Return(true)); + // Expectations are not wishes. + EXPECT_CALL(handler, ExpressionRule(fde_start + 2 * code_factor, 0x500f5739, + "smurf")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValExpressionRuleUnchanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x666ae152) + .Block("hideous") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + EXPECT_CALL(handler, ValExpressionRule(fde_start, 0x666ae152, "hideous")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValExpressionRuleChanged) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0xb5ca5c46) + .Block("revolting") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0xb5ca5c46) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("RestoreValExpressionRuleChanged", section); + + EXPECT_CALL(handler, ValExpressionRule(fde_start, 0xb5ca5c46, "revolting")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(fde_start + code_factor, 0xb5ca5c46)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValExpressionRule(fde_start + 2 * code_factor, 0xb5ca5c46, + "revolting")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +TEST_F(CFIRestore, RestoreValExpressionRuleChangedValExpression) { + CFISection section(kLittleEndian, 4); + StockCIEAndFDE(§ion); + section + .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x500f5739) + .Block("repulsive") + .D8(dwarf2reader::DW_CFA_remember_state) + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_val_expression).ULEB128(0x500f5739) + .Block("nauseous") + .D8(dwarf2reader::DW_CFA_advance_loc | 1) + .D8(dwarf2reader::DW_CFA_restore_state) + .FinishEntry(); + + PERHAPS_WRITE_DEBUG_FRAME_FILE("RestoreValExpressionRuleChangedValExpression", + section); + + EXPECT_CALL(handler, ValExpressionRule(fde_start, 0x500f5739, "repulsive")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValExpressionRule(fde_start + code_factor, 0x500f5739, + "nauseous")) + .InSequence(s).WillOnce(Return(true)); + // Expectations are not wishes. + EXPECT_CALL(handler, ValExpressionRule(fde_start + 2 * code_factor, 0x500f5739, + "repulsive")) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()).WillOnce(Return(true)); + + ParseSection(§ion); +} + +struct EHFrameFixture: public CFIInsnFixture { + EHFrameFixture() + : CFIInsnFixture(), section(kBigEndian, 4, true) { + encoded_pointer_bases.cfi = 0x7f496cb2; + encoded_pointer_bases.text = 0x540f67b6; + encoded_pointer_bases.data = 0xe3eab768; + section.SetEncodedPointerBases(encoded_pointer_bases); + } + CFISection section; + CFISection::EncodedPointerBases encoded_pointer_bases; + + // Parse CFIInsnFixture::ParseSection, but parse the section as + // .eh_frame data, supplying stock base addresses. + void ParseEHFrameSection(CFISection *section, bool succeeds = true) { + EXPECT_TRUE(section->ContainsEHFrame()); + string contents; + EXPECT_TRUE(section->GetContents(&contents)); + dwarf2reader::Endianness endianness; + if (section->endianness() == kBigEndian) + endianness = ENDIANNESS_BIG; + else { + assert(section->endianness() == kLittleEndian); + endianness = ENDIANNESS_LITTLE; + } + ByteReader byte_reader(endianness); + byte_reader.SetAddressSize(section->AddressSize()); + byte_reader.SetCFIDataBase(encoded_pointer_bases.cfi, contents.data()); + byte_reader.SetTextBase(encoded_pointer_bases.text); + byte_reader.SetDataBase(encoded_pointer_bases.data); + CallFrameInfo parser(contents.data(), contents.size(), + &byte_reader, &handler, &reporter, true); + if (succeeds) + EXPECT_TRUE(parser.Start()); + else + EXPECT_FALSE(parser.Start()); + } + +}; + +class EHFrame: public EHFrameFixture, public Test { }; + +// A simple CIE, an FDE, and a terminator. +TEST_F(EHFrame, Terminator) { + Label cie; + section + .Mark(&cie) + .CIEHeader(9968, 2466, 67, 1, "") + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(3772).ULEB128(1372) + .FinishEntry() + .FDEHeader(cie, 0x848037a1, 0x7b30475e) + .D8(dwarf2reader::DW_CFA_set_loc).D32(0x17713850) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(5721) + .FinishEntry() + .D32(0) // Terminate the sequence. + // This FDE should be ignored. + .FDEHeader(cie, 0xf19629fe, 0x439fb09b) + .FinishEntry(); + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.Terminator", section); + + EXPECT_CALL(handler, Entry(_, 0x848037a1, 0x7b30475e, 1, "", 67)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(0x848037a1, kCFARegister, 3772, 1372)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(0x17713850, 5721)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(reporter, EarlyEHTerminator(_)) + .InSequence(s).WillOnce(Return()); + + ParseEHFrameSection(§ion); +} + +// The parser should recognize the Linux Standards Base 'z' augmentations. +TEST_F(EHFrame, SimpleFDE) { + DwarfPointerEncoding lsda_encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_indirect + | dwarf2reader::DW_EH_PE_datarel + | dwarf2reader::DW_EH_PE_sdata2); + DwarfPointerEncoding fde_encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel + | dwarf2reader::DW_EH_PE_udata2); + + section.SetPointerEncoding(fde_encoding); + section.SetEncodedPointerBases(encoded_pointer_bases); + Label cie; + section + .Mark(&cie) + .CIEHeader(4873, 7012, 100, 1, "zSLPR") + .ULEB128(7) // Augmentation data length + .D8(lsda_encoding) // LSDA pointer format + .D8(dwarf2reader::DW_EH_PE_pcrel) // personality pointer format + .EncodedPointer(0x97baa00, dwarf2reader::DW_EH_PE_pcrel) // and value + .D8(fde_encoding) // FDE pointer format + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(6706).ULEB128(31) + .FinishEntry() + .FDEHeader(cie, 0x540f6b56, 0xf686) + .ULEB128(2) // Augmentation data length + .EncodedPointer(0xe3eab475, lsda_encoding) // LSDA pointer, signed + .D8(dwarf2reader::DW_CFA_set_loc) + .EncodedPointer(0x540fa4ce, fde_encoding) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(0x675e) + .FinishEntry() + .D32(0); // terminator + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.SimpleFDE", section); + + EXPECT_CALL(handler, Entry(_, 0x540f6b56, 0xf686, 1, "zSLPR", 100)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, PersonalityRoutine(0x97baa00, false)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, LanguageSpecificDataArea(0xe3eab475, true)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, SignalHandler()) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(0x540f6b56, kCFARegister, 6706, 31)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(0x540fa4ce, 0x675e)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +// Check that we can handle an empty 'z' augmentation. +TEST_F(EHFrame, EmptyZ) { + Label cie; + section + .Mark(&cie) + .CIEHeader(5955, 5805, 228, 1, "z") + .ULEB128(0) // Augmentation data length + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(3629).ULEB128(247) + .FinishEntry() + .FDEHeader(cie, 0xda007738, 0xfb55c641) + .ULEB128(0) // Augmentation data length + .D8(dwarf2reader::DW_CFA_advance_loc1).D8(11) + .D8(dwarf2reader::DW_CFA_undefined).ULEB128(3769) + .FinishEntry(); + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.EmptyZ", section); + + EXPECT_CALL(handler, Entry(_, 0xda007738, 0xfb55c641, 1, "z", 228)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, ValOffsetRule(0xda007738, kCFARegister, 3629, 247)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, UndefinedRule(0xda007738 + 11 * 5955, 3769)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +// Check that we recognize bad 'z' augmentation characters. +TEST_F(EHFrame, BadZ) { + Label cie; + section + .Mark(&cie) + .CIEHeader(6937, 1045, 142, 1, "zQ") + .ULEB128(0) // Augmentation data length + .D8(dwarf2reader::DW_CFA_def_cfa).ULEB128(9006).ULEB128(7725) + .FinishEntry() + .FDEHeader(cie, 0x1293efa8, 0x236f53f2) + .ULEB128(0) // Augmentation data length + .D8(dwarf2reader::DW_CFA_advance_loc | 12) + .D8(dwarf2reader::DW_CFA_register).ULEB128(5667).ULEB128(3462) + .FinishEntry(); + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.BadZ", section); + + EXPECT_CALL(reporter, UnrecognizedAugmentation(_, "zQ")) + .WillOnce(Return()); + + ParseEHFrameSection(§ion, false); +} + +TEST_F(EHFrame, zL) { + Label cie; + DwarfPointerEncoding lsda_encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_funcrel + | dwarf2reader::DW_EH_PE_udata2); + section + .Mark(&cie) + .CIEHeader(9285, 9959, 54, 1, "zL") + .ULEB128(1) // Augmentation data length + .D8(lsda_encoding) // encoding for LSDA pointer in FDE + + .FinishEntry() + .FDEHeader(cie, 0xd40091aa, 0x9aa6e746) + .ULEB128(2) // Augmentation data length + .EncodedPointer(0xd40099cd, lsda_encoding) // LSDA pointer + .FinishEntry() + .D32(0); // terminator + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zL", section); + + EXPECT_CALL(handler, Entry(_, 0xd40091aa, 0x9aa6e746, 1, "zL", 54)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, LanguageSpecificDataArea(0xd40099cd, false)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +TEST_F(EHFrame, zP) { + Label cie; + DwarfPointerEncoding personality_encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_datarel + | dwarf2reader::DW_EH_PE_udata2); + section + .Mark(&cie) + .CIEHeader(1097, 6313, 17, 1, "zP") + .ULEB128(3) // Augmentation data length + .D8(personality_encoding) // encoding for personality routine + .EncodedPointer(0xe3eaccac, personality_encoding) // value + .FinishEntry() + .FDEHeader(cie, 0x0c8350c9, 0xbef11087) + .ULEB128(0) // Augmentation data length + .FinishEntry() + .D32(0); // terminator + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zP", section); + + EXPECT_CALL(handler, Entry(_, 0x0c8350c9, 0xbef11087, 1, "zP", 17)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, PersonalityRoutine(0xe3eaccac, false)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +TEST_F(EHFrame, zR) { + Label cie; + DwarfPointerEncoding pointer_encoding = + DwarfPointerEncoding(dwarf2reader::DW_EH_PE_textrel + | dwarf2reader::DW_EH_PE_sdata2); + section.SetPointerEncoding(pointer_encoding); + section + .Mark(&cie) + .CIEHeader(8011, 5496, 75, 1, "zR") + .ULEB128(1) // Augmentation data length + .D8(pointer_encoding) // encoding for FDE addresses + .FinishEntry() + .FDEHeader(cie, 0x540f9431, 0xbd0) + .ULEB128(0) // Augmentation data length + .FinishEntry() + .D32(0); // terminator + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zR", section); + + EXPECT_CALL(handler, Entry(_, 0x540f9431, 0xbd0, 1, "zR", 75)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +TEST_F(EHFrame, zS) { + Label cie; + section + .Mark(&cie) + .CIEHeader(9217, 7694, 57, 1, "zS") + .ULEB128(0) // Augmentation data length + .FinishEntry() + .FDEHeader(cie, 0xd40091aa, 0x9aa6e746) + .ULEB128(0) // Augmentation data length + .FinishEntry() + .D32(0); // terminator + + PERHAPS_WRITE_EH_FRAME_FILE("EHFrame.zS", section); + + EXPECT_CALL(handler, Entry(_, 0xd40091aa, 0x9aa6e746, 1, "zS", 57)) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, SignalHandler()) + .InSequence(s).WillOnce(Return(true)); + EXPECT_CALL(handler, End()) + .InSequence(s).WillOnce(Return(true)); + + ParseEHFrameSection(§ion); +} + +// These tests require manual inspection of the test output. +struct CFIReporterFixture { + CFIReporterFixture() : reporter("test file name", "test section name") { } + CallFrameInfo::Reporter reporter; +}; + +class CFIReporter: public CFIReporterFixture, public Test { }; + +TEST_F(CFIReporter, Incomplete) { + reporter.Incomplete(0x0102030405060708ULL, CallFrameInfo::kUnknown); +} + +TEST_F(CFIReporter, EarlyEHTerminator) { + reporter.EarlyEHTerminator(0x0102030405060708ULL); +} + +TEST_F(CFIReporter, CIEPointerOutOfRange) { + reporter.CIEPointerOutOfRange(0x0123456789abcdefULL, 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, BadCIEId) { + reporter.BadCIEId(0x0123456789abcdefULL, 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, UnrecognizedVersion) { + reporter.UnrecognizedVersion(0x0123456789abcdefULL, 43); +} + +TEST_F(CFIReporter, UnrecognizedAugmentation) { + reporter.UnrecognizedAugmentation(0x0123456789abcdefULL, "poodles"); +} + +TEST_F(CFIReporter, InvalidPointerEncoding) { + reporter.InvalidPointerEncoding(0x0123456789abcdefULL, 0x42); +} + +TEST_F(CFIReporter, UnusablePointerEncoding) { + reporter.UnusablePointerEncoding(0x0123456789abcdefULL, 0x42); +} + +TEST_F(CFIReporter, RestoreInCIE) { + reporter.RestoreInCIE(0x0123456789abcdefULL, 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, BadInstruction) { + reporter.BadInstruction(0x0123456789abcdefULL, CallFrameInfo::kFDE, + 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, NoCFARule) { + reporter.NoCFARule(0x0123456789abcdefULL, CallFrameInfo::kCIE, + 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, EmptyStateStack) { + reporter.EmptyStateStack(0x0123456789abcdefULL, CallFrameInfo::kTerminator, + 0xfedcba9876543210ULL); +} + +TEST_F(CFIReporter, ClearingCFARule) { + reporter.ClearingCFARule(0x0123456789abcdefULL, CallFrameInfo::kFDE, + 0xfedcba9876543210ULL); +} + +#ifdef WRITE_ELF +// See comments at the top of the file mentioning WRITE_ELF for details. + +using google_breakpad::TestAssembler::Section; + +struct ELFSectionHeader { + ELFSectionHeader(unsigned int set_type) + : type(set_type), flags(0), address(0), link(0), info(0), + alignment(1), entry_size(0) { } + Label name; + unsigned int type; + u_int64_t flags; + u_int64_t address; + Label file_offset; + Label file_size; + unsigned int link; + unsigned int info; + u_int64_t alignment; + u_int64_t entry_size; +}; + +void AppendSectionHeader(CFISection *table, const ELFSectionHeader &header) { + (*table) + .D32(header.name) // name, index in string tbl + .D32(header.type) // type + .Address(header.flags) // flags + .Address(header.address) // address in memory + .Address(header.file_offset) // offset in ELF file + .Address(header.file_size) // length in bytes + .D32(header.link) // link to related section + .D32(header.info) // miscellaneous + .Address(header.alignment) // alignment + .Address(header.entry_size); // entry size +} + +void WriteELFFrameSection(const char *filename, const char *cfi_name, + const CFISection &cfi) { + int elf_class = cfi.AddressSize() == 4 ? ELFCLASS32 : ELFCLASS64; + int elf_data = (cfi.endianness() == kBigEndian + ? ELFDATA2MSB : ELFDATA2LSB); + CFISection elf(cfi.endianness(), cfi.AddressSize()); + Label elf_header_size, section_table_offset; + elf + .Append("\x7f" "ELF") + .D8(elf_class) // 32-bit or 64-bit ELF + .D8(elf_data) // endianness + .D8(1) // ELF version + .D8(ELFOSABI_LINUX) // Operating System/ABI indication + .D8(0) // ABI version + .Append(7, 0xda) // padding + .D16(ET_EXEC) // file type: executable file + .D16(EM_386) // architecture: Intel IA-32 + .D32(EV_CURRENT); // ELF version + elf + .Address(0x0123456789abcdefULL) // program entry point + .Address(0) // program header offset + .Address(section_table_offset) // section header offset + .D32(0) // processor-specific flags + .D16(elf_header_size) // ELF header size in bytes */ + .D16(elf_class == ELFCLASS32 ? 32 : 56) // program header entry size + .D16(0) // program header table entry count + .D16(elf_class == ELFCLASS32 ? 40 : 64) // section header entry size + .D16(3) // section count + .D16(1) // section name string table + .Mark(&elf_header_size); + + // The null section. Every ELF file has one, as the first entry in + // the section header table. + ELFSectionHeader null_header(SHT_NULL); + null_header.file_offset = 0; + null_header.file_size = 0; + + // The CFI section. The whole reason for writing out this ELF file + // is to put this in it so that we can run other dumping programs on + // it to check its contents. + ELFSectionHeader cfi_header(SHT_PROGBITS); + cfi_header.file_size = cfi.Size(); + + // The section holding the names of the sections. This is the + // section whose index appears in the e_shstrndx member of the ELF + // header. + ELFSectionHeader section_names_header(SHT_STRTAB); + CFISection section_names(cfi.endianness(), cfi.AddressSize()); + section_names + .Mark(&null_header.name) + .AppendCString("") + .Mark(§ion_names_header.name) + .AppendCString(".shstrtab") + .Mark(&cfi_header.name) + .AppendCString(cfi_name) + .Mark(§ion_names_header.file_size); + + // Create the section table. The ELF header's e_shoff member refers + // to this, and the e_shnum member gives the number of entries it + // contains. + CFISection section_table(cfi.endianness(), cfi.AddressSize()); + AppendSectionHeader(§ion_table, null_header); + AppendSectionHeader(§ion_table, section_names_header); + AppendSectionHeader(§ion_table, cfi_header); + + // Append the section table and the section contents to the ELF file. + elf + .Mark(§ion_table_offset) + .Append(section_table) + .Mark(§ion_names_header.file_offset) + .Append(section_names) + .Mark(&cfi_header.file_offset) + .Append(cfi); + + string contents; + if (!elf.GetContents(&contents)) { + fprintf(stderr, "failed to get ELF file contents\n"); + exit(1); + } + + FILE *out = fopen(filename, "w"); + if (!out) { + fprintf(stderr, "error opening ELF file '%s': %s\n", + filename, strerror(errno)); + exit(1); + } + + if (fwrite(contents.data(), 1, contents.size(), out) != contents.size()) { + fprintf(stderr, "error writing ELF data to '%s': %s\n", + filename, strerror(errno)); + exit(1); + } + + if (fclose(out) == EOF) { + fprintf(stderr, "error closing ELF file '%s': %s\n", + filename, strerror(errno)); + exit(1); + } +} +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,1042 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// CFI reader author: Jim Blandy + +// This file contains definitions related to the DWARF2/3 reader and +// it's handler interfaces. +// The DWARF2/3 specification can be found at +// http://dwarf.freestandards.org and should be considered required +// reading if you wish to modify the implementation. +// Only a cursory attempt is made to explain terminology that is +// used here, as it is much better explained in the standard documents +#ifndef COMMON_DWARF_DWARF2READER_H__ +#define COMMON_DWARF_DWARF2READER_H__ + +#include +#include +#include +#include +#include + +#include "common/dwarf/bytereader.h" +#include "common/dwarf/dwarf2enums.h" +#include "common/dwarf/types.h" + +using namespace std; + +namespace dwarf2reader { +struct LineStateMachine; +class Dwarf2Handler; +class LineInfoHandler; + +// This maps from a string naming a section to a pair containing a +// the data for the section, and the size of the section. +typedef map > SectionMap; +typedef list > AttributeList; +typedef AttributeList::iterator AttributeIterator; +typedef AttributeList::const_iterator ConstAttributeIterator; + +struct LineInfoHeader { + uint64 total_length; + uint16 version; + uint64 prologue_length; + uint8 min_insn_length; // insn stands for instructin + bool default_is_stmt; // stmt stands for statement + int8 line_base; + uint8 line_range; + uint8 opcode_base; + // Use a pointer so that signalsafe_addr2line is able to use this structure + // without heap allocation problem. + vector *std_opcode_lengths; +}; + +class LineInfo { + public: + + // Initializes a .debug_line reader. Buffer and buffer length point + // to the beginning and length of the line information to read. + // Reader is a ByteReader class that has the endianness set + // properly. + LineInfo(const char* buffer_, uint64 buffer_length, + ByteReader* reader, LineInfoHandler* handler); + + virtual ~LineInfo() { + if (header_.std_opcode_lengths) { + delete header_.std_opcode_lengths; + } + } + + // Start processing line info, and calling callbacks in the handler. + // Consumes the line number information for a single compilation unit. + // Returns the number of bytes processed. + uint64 Start(); + + // Process a single line info opcode at START using the state + // machine at LSM. Return true if we should define a line using the + // current state of the line state machine. Place the length of the + // opcode in LEN. + // If LSM_PASSES_PC is non-NULL, this function also checks if the lsm + // passes the address of PC. In other words, LSM_PASSES_PC will be + // set to true, if the following condition is met. + // + // lsm's old address < PC <= lsm's new address + static bool ProcessOneOpcode(ByteReader* reader, + LineInfoHandler* handler, + const struct LineInfoHeader &header, + const char* start, + struct LineStateMachine* lsm, + size_t* len, + uintptr pc, + bool *lsm_passes_pc); + + private: + // Reads the DWARF2/3 header for this line info. + void ReadHeader(); + + // Reads the DWARF2/3 line information + void ReadLines(); + + // The associated handler to call processing functions in + LineInfoHandler* handler_; + + // The associated ByteReader that handles endianness issues for us + ByteReader* reader_; + + // A DWARF2/3 line info header. This is not the same size as + // in the actual file, as the one in the file may have a 32 bit or + // 64 bit lengths + + struct LineInfoHeader header_; + + // buffer is the buffer for our line info, starting at exactly where + // the line info to read is. after_header is the place right after + // the end of the line information header. + const char* buffer_; + uint64 buffer_length_; + const char* after_header_; +}; + +// This class is the main interface between the line info reader and +// the client. The virtual functions inside this get called for +// interesting events that happen during line info reading. The +// default implementation does nothing + +class LineInfoHandler { + public: + LineInfoHandler() { } + + virtual ~LineInfoHandler() { } + + // Called when we define a directory. NAME is the directory name, + // DIR_NUM is the directory number + virtual void DefineDir(const string& name, uint32 dir_num) { } + + // Called when we define a filename. NAME is the filename, FILE_NUM + // is the file number which is -1 if the file index is the next + // index after the last numbered index (this happens when files are + // dynamically defined by the line program), DIR_NUM is the + // directory index for the directory name of this file, MOD_TIME is + // the modification time of the file, and LENGTH is the length of + // the file + virtual void DefineFile(const string& name, int32 file_num, + uint32 dir_num, uint64 mod_time, + uint64 length) { } + + // Called when the line info reader has a new line, address pair + // ready for us. ADDRESS is the address of the code, LENGTH is the + // length of its machine code in bytes, FILE_NUM is the file number + // containing the code, LINE_NUM is the line number in that file for + // the code, and COLUMN_NUM is the column number the code starts at, + // if we know it (0 otherwise). + virtual void AddLine(uint64 address, uint64 length, + uint32 file_num, uint32 line_num, uint32 column_num) { } +}; + +// The base of DWARF2/3 debug info is a DIE (Debugging Information +// Entry. +// DWARF groups DIE's into a tree and calls the root of this tree a +// "compilation unit". Most of the time, there is one compilation +// unit in the .debug_info section for each file that had debug info +// generated. +// Each DIE consists of + +// 1. a tag specifying a thing that is being described (ie +// DW_TAG_subprogram for functions, DW_TAG_variable for variables, etc +// 2. attributes (such as DW_AT_location for location in memory, +// DW_AT_name for name), and data for each attribute. +// 3. A flag saying whether the DIE has children or not + +// In order to gain some amount of compression, the format of +// each DIE (tag name, attributes and data forms for the attributes) +// are stored in a separate table called the "abbreviation table". +// This is done because a large number of DIEs have the exact same tag +// and list of attributes, but different data for those attributes. +// As a result, the .debug_info section is just a stream of data, and +// requires reading of the .debug_abbrev section to say what the data +// means. + +// As a warning to the user, it should be noted that the reason for +// using absolute offsets from the beginning of .debug_info is that +// DWARF2/3 supports referencing DIE's from other DIE's by their offset +// from either the current compilation unit start, *or* the beginning +// of the .debug_info section. This means it is possible to reference +// a DIE in one compilation unit from a DIE in another compilation +// unit. This style of reference is usually used to eliminate +// duplicated information that occurs across compilation +// units, such as base types, etc. GCC 3.4+ support this with +// -feliminate-dwarf2-dups. Other toolchains will sometimes do +// duplicate elimination in the linker. + +class CompilationUnit { + public: + + // Initialize a compilation unit. This requires a map of sections, + // the offset of this compilation unit in the .debug_info section, a + // ByteReader, and a Dwarf2Handler class to call callbacks in. + CompilationUnit(const SectionMap& sections, uint64 offset, + ByteReader* reader, Dwarf2Handler* handler); + virtual ~CompilationUnit() { + if (abbrevs_) delete abbrevs_; + } + + // Begin reading a Dwarf2 compilation unit, and calling the + // callbacks in the Dwarf2Handler + + // Return the full length of the compilation unit, including + // headers. This plus the starting offset passed to the constructor + // is the offset of the end of the compilation unit --- and the + // start of the next compilation unit, if there is one. + uint64 Start(); + + private: + + // This struct represents a single DWARF2/3 abbreviation + // The abbreviation tells how to read a DWARF2/3 DIE, and consist of a + // tag and a list of attributes, as well as the data form of each attribute. + struct Abbrev { + uint32 number; + enum DwarfTag tag; + bool has_children; + AttributeList attributes; + }; + + // A DWARF2/3 compilation unit header. This is not the same size as + // in the actual file, as the one in the file may have a 32 bit or + // 64 bit length. + struct CompilationUnitHeader { + uint64 length; + uint16 version; + uint64 abbrev_offset; + uint8 address_size; + } header_; + + // Reads the DWARF2/3 header for this compilation unit. + void ReadHeader(); + + // Reads the DWARF2/3 abbreviations for this compilation unit + void ReadAbbrevs(); + + // Processes a single DIE for this compilation unit and return a new + // pointer just past the end of it + const char* ProcessDIE(uint64 dieoffset, + const char* start, + const Abbrev& abbrev); + + // Processes a single attribute and return a new pointer just past the + // end of it + const char* ProcessAttribute(uint64 dieoffset, + const char* start, + enum DwarfAttribute attr, + enum DwarfForm form); + + // Processes all DIEs for this compilation unit + void ProcessDIEs(); + + // Skips the die with attributes specified in ABBREV starting at + // START, and return the new place to position the stream to. + const char* SkipDIE(const char* start, + const Abbrev& abbrev); + + // Skips the attribute starting at START, with FORM, and return the + // new place to position the stream to. + const char* SkipAttribute(const char* start, + enum DwarfForm form); + + // Offset from section start is the offset of this compilation unit + // from the beginning of the .debug_info section. + uint64 offset_from_section_start_; + + // buffer is the buffer for our CU, starting at .debug_info + offset + // passed in from constructor. + // after_header points to right after the compilation unit header. + const char* buffer_; + uint64 buffer_length_; + const char* after_header_; + + // The associated ByteReader that handles endianness issues for us + ByteReader* reader_; + + // The map of sections in our file to buffers containing their data + const SectionMap& sections_; + + // The associated handler to call processing functions in + Dwarf2Handler* handler_; + + // Set of DWARF2/3 abbreviations for this compilation unit. Indexed + // by abbreviation number, which means that abbrevs_[0] is not + // valid. + vector* abbrevs_; + + // String section buffer and length, if we have a string section. + // This is here to avoid doing a section lookup for strings in + // ProcessAttribute, which is in the hot path for DWARF2 reading. + const char* string_buffer_; + uint64 string_buffer_length_; +}; + +// This class is the main interface between the reader and the +// client. The virtual functions inside this get called for +// interesting events that happen during DWARF2 reading. +// The default implementation skips everything. + +class Dwarf2Handler { + public: + Dwarf2Handler() { } + + virtual ~Dwarf2Handler() { } + + // Start to process a compilation unit at OFFSET from the beginning of the + // .debug_info section. Return false if you would like to skip this + // compilation unit. + virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version) { return false; } + + // Start to process a DIE at OFFSET from the beginning of the .debug_info + // section. Return false if you would like to skip this DIE. + virtual bool StartDIE(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs) { return false; } + + // Called when we have an attribute with unsigned data to give to our + // handler. The attribute is for the DIE at OFFSET from the beginning of the + // .debug_info section. Its name is ATTR, its form is FORM, and its value is + // DATA. + virtual void ProcessAttributeUnsigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { } + + // Called when we have an attribute with signed data to give to our handler. + // The attribute is for the DIE at OFFSET from the beginning of the + // .debug_info section. Its name is ATTR, its form is FORM, and its value is + // DATA. + virtual void ProcessAttributeSigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { } + + // Called when we have an attribute whose value is a reference to + // another DIE. The attribute belongs to the DIE at OFFSET from the + // beginning of the .debug_info section. Its name is ATTR, its form + // is FORM, and the offset of the DIE being referred to from the + // beginning of the .debug_info section is DATA. + virtual void ProcessAttributeReference(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { } + + // Called when we have an attribute with a buffer of data to give to our + // handler. The attribute is for the DIE at OFFSET from the beginning of the + // .debug_info section. Its name is ATTR, its form is FORM, DATA points to + // the buffer's contents, and its length in bytes is LENGTH. The buffer is + // owned by the caller, not the callee, and may not persist for very long. + // If you want the data to be available later, it needs to be copied. + virtual void ProcessAttributeBuffer(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const char* data, + uint64 len) { } + + // Called when we have an attribute with string data to give to our handler. + // The attribute is for the DIE at OFFSET from the beginning of the + // .debug_info section. Its name is ATTR, its form is FORM, and its value is + // DATA. + virtual void ProcessAttributeString(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const string& data) { } + + // Called when finished processing the DIE at OFFSET. + // Because DWARF2/3 specifies a tree of DIEs, you may get starts + // before ends of the previous DIE, as we process children before + // ending the parent. + virtual void EndDIE(uint64 offset) { } + +}; + +// This class is a reader for DWARF's Call Frame Information. CFI +// describes how to unwind stack frames --- even for functions that do +// not follow fixed conventions for saving registers, whose frame size +// varies as they execute, etc. +// +// CFI describes, at each machine instruction, how to compute the +// stack frame's base address, how to find the return address, and +// where to find the saved values of the caller's registers (if the +// callee has stashed them somewhere to free up the registers for its +// own use). +// +// For example, suppose we have a function whose machine code looks +// like this (imagine an assembly language that looks like C, for a +// machine with 32-bit registers, and a stack that grows towards lower +// addresses): +// +// func: ; entry point; return address at sp +// func+0: sp = sp - 16 ; allocate space for stack frame +// func+1: sp[12] = r0 ; save r0 at sp+12 +// ... ; other code, not frame-related +// func+10: sp -= 4; *sp = x ; push some x on the stack +// ... ; other code, not frame-related +// func+20: r0 = sp[16] ; restore saved r0 +// func+21: sp += 20 ; pop whole stack frame +// func+22: pc = *sp; sp += 4 ; pop return address and jump to it +// +// DWARF CFI is (a very compressed representation of) a table with a +// row for each machine instruction address and a column for each +// register showing how to restore it, if possible. +// +// A special column named "CFA", for "Canonical Frame Address", tells how +// to compute the base address of the frame; registers' entries may +// refer to the CFA in describing where the registers are saved. +// +// Another special column, named "RA", represents the return address. +// +// For example, here is a complete (uncompressed) table describing the +// function above: +// +// insn cfa r0 r1 ... ra +// ======================================= +// func+0: sp cfa[0] +// func+1: sp+16 cfa[0] +// func+2: sp+16 cfa[-4] cfa[0] +// func+11: sp+20 cfa[-4] cfa[0] +// func+21: sp+20 cfa[0] +// func+22: sp cfa[0] +// +// Some things to note here: +// +// - Each row describes the state of affairs *before* executing the +// instruction at the given address. Thus, the row for func+0 +// describes the state before we allocate the stack frame. In the +// next row, the formula for computing the CFA has changed, +// reflecting that allocation. +// +// - The other entries are written in terms of the CFA; this allows +// them to remain unchanged as the stack pointer gets bumped around. +// For example, the rule for recovering the return address (the "ra" +// column) remains unchanged throughout the function, even as the +// stack pointer takes on three different offsets from the return +// address. +// +// - Although we haven't shown it, most calling conventions designate +// "callee-saves" and "caller-saves" registers. The callee must +// preserve the values of callee-saves registers; if it uses them, +// it must save their original values somewhere, and restore them +// before it returns. In contrast, the callee is free to trash +// caller-saves registers; if the callee uses these, it will +// probably not bother to save them anywhere, and the CFI will +// probably mark their values as "unrecoverable". +// +// (However, since the caller cannot assume the callee was going to +// save them, caller-saves registers are probably dead in the caller +// anyway, so compilers usually don't generate CFA for caller-saves +// registers.) +// +// - Exactly where the CFA points is a matter of convention that +// depends on the architecture and ABI in use. In the example, the +// CFA is the value the stack pointer had upon entry to the +// function, pointing at the saved return address. But on the x86, +// the call frame information generated by GCC follows the +// convention that the CFA is the address *after* the saved return +// address. +// +// But by definition, the CFA remains constant throughout the +// lifetime of the frame. This makes it a useful value for other +// columns to refer to. It is also gives debuggers a useful handle +// for identifying a frame. +// +// If you look at the table above, you'll notice that a given entry is +// often the same as the one immediately above it: most instructions +// change only one or two aspects of the stack frame, if they affect +// it at all. The DWARF format takes advantage of this fact, and +// reduces the size of the data by mentioning only the addresses and +// columns at which changes take place. So for the above, DWARF CFI +// data would only actually mention the following: +// +// insn cfa r0 r1 ... ra +// ======================================= +// func+0: sp cfa[0] +// func+1: sp+16 +// func+2: cfa[-4] +// func+11: sp+20 +// func+21: r0 +// func+22: sp +// +// In fact, this is the way the parser reports CFI to the consumer: as +// a series of statements of the form, "At address X, column Y changed +// to Z," and related conventions for describing the initial state. +// +// Naturally, it would be impractical to have to scan the entire +// program's CFI, noting changes as we go, just to recover the +// unwinding rules in effect at one particular instruction. To avoid +// this, CFI data is grouped into "entries", each of which covers a +// specified range of addresses and begins with a complete statement +// of the rules for all recoverable registers at that starting +// address. Each entry typically covers a single function. +// +// Thus, to compute the contents of a given row of the table --- that +// is, rules for recovering the CFA, RA, and registers at a given +// instruction --- the consumer should find the entry that covers that +// instruction's address, start with the initial state supplied at the +// beginning of the entry, and work forward until it has processed all +// the changes up to and including those for the present instruction. +// +// There are seven kinds of rules that can appear in an entry of the +// table: +// +// - "undefined": The given register is not preserved by the callee; +// its value cannot be recovered. +// +// - "same value": This register has the same value it did in the callee. +// +// - offset(N): The register is saved at offset N from the CFA. +// +// - val_offset(N): The value the register had in the caller is the +// CFA plus offset N. (This is usually only useful for describing +// the stack pointer.) +// +// - register(R): The register's value was saved in another register R. +// +// - expression(E): Evaluating the DWARF expression E using the +// current frame's registers' values yields the address at which the +// register was saved. +// +// - val_expression(E): Evaluating the DWARF expression E using the +// current frame's registers' values yields the value the register +// had in the caller. + +class CallFrameInfo { + public: + // The different kinds of entries one finds in CFI. Used internally, + // and for error reporting. + enum EntryKind { kUnknown, kCIE, kFDE, kTerminator }; + + // The handler class to which the parser hands the parsed call frame + // information. Defined below. + class Handler; + + // A reporter class, which CallFrameInfo uses to report errors + // encountered while parsing call frame information. Defined below. + class Reporter; + + // Create a DWARF CFI parser. BUFFER points to the contents of the + // .debug_frame section to parse; BUFFER_LENGTH is its length in bytes. + // REPORTER is an error reporter the parser should use to report + // problems. READER is a ByteReader instance that has the endianness and + // address size set properly. Report the data we find to HANDLER. + // + // This class can also parse Linux C++ exception handling data, as found + // in '.eh_frame' sections. This data is a variant of DWARF CFI that is + // placed in loadable segments so that it is present in the program's + // address space, and is interpreted by the C++ runtime to search the + // call stack for a handler interested in the exception being thrown, + // actually pop the frames, and find cleanup code to run. + // + // There are two differences between the call frame information described + // in the DWARF standard and the exception handling data Linux places in + // the .eh_frame section: + // + // - Exception handling data uses uses a different format for call frame + // information entry headers. The distinguished CIE id, the way FDEs + // refer to their CIEs, and the way the end of the series of entries is + // determined are all slightly different. + // + // If the constructor's EH_FRAME argument is true, then the + // CallFrameInfo parses the entry headers as Linux C++ exception + // handling data. If EH_FRAME is false or omitted, the CallFrameInfo + // parses standard DWARF call frame information. + // + // - Linux C++ exception handling data uses CIE augmentation strings + // beginning with 'z' to specify the presence of additional data after + // the CIE and FDE headers and special encodings used for addresses in + // frame description entries. + // + // CallFrameInfo can handle 'z' augmentations in either DWARF CFI or + // exception handling data if you have supplied READER with the base + // addresses needed to interpret the pointer encodings that 'z' + // augmentations can specify. See the ByteReader interface for details + // about the base addresses. See the CallFrameInfo::Handler interface + // for details about the additional information one might find in + // 'z'-augmented data. + // + // Thus: + // + // - If you are parsing standard DWARF CFI, as found in a .debug_frame + // section, you should pass false for the EH_FRAME argument, or omit + // it, and you need not worry about providing READER with the + // additional base addresses. + // + // - If you want to parse Linux C++ exception handling data from a + // .eh_frame section, you should pass EH_FRAME as true, and call + // READER's Set*Base member functions before calling our Start method. + // + // - If you want to parse DWARF CFI that uses the 'z' augmentations + // (although I don't think any toolchain ever emits such data), you + // could pass false for EH_FRAME, but call READER's Set*Base members. + // + // The extensions the Linux C++ ABI makes to DWARF for exception + // handling are described here, rather poorly: + // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/dwarfext.html + // http://refspecs.linux-foundation.org/LSB_4.0.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html + // + // The mechanics of C++ exception handling, personality routines, + // and language-specific data areas are described here, rather nicely: + // http://www.codesourcery.com/public/cxx-abi/abi-eh.html + CallFrameInfo(const char *buffer, size_t buffer_length, + ByteReader *reader, Handler *handler, Reporter *reporter, + bool eh_frame = false) + : buffer_(buffer), buffer_length_(buffer_length), + reader_(reader), handler_(handler), reporter_(reporter), + eh_frame_(eh_frame) { } + + ~CallFrameInfo() { } + + // Parse the entries in BUFFER, reporting what we find to HANDLER. + // Return true if we reach the end of the section successfully, or + // false if we encounter an error. + bool Start(); + + // Return the textual name of KIND. For error reporting. + static const char *KindName(EntryKind kind); + + private: + + struct CIE; + + // A CFI entry, either an FDE or a CIE. + struct Entry { + // The starting offset of the entry in the section, for error + // reporting. + size_t offset; + + // The start of this entry in the buffer. + const char *start; + + // Which kind of entry this is. + // + // We want to be able to use this for error reporting even while we're + // in the midst of parsing. Error reporting code may assume that kind, + // offset, and start fields are valid, although kind may be kUnknown. + EntryKind kind; + + // The end of this entry's common prologue (initial length and id), and + // the start of this entry's kind-specific fields. + const char *fields; + + // The start of this entry's instructions. + const char *instructions; + + // The address past the entry's last byte in the buffer. (Note that + // since offset points to the entry's initial length field, and the + // length field is the number of bytes after that field, this is not + // simply buffer_ + offset + length.) + const char *end; + + // For both DWARF CFI and .eh_frame sections, this is the CIE id in a + // CIE, and the offset of the associated CIE in an FDE. + uint64 id; + + // The CIE that applies to this entry, if we've parsed it. If this is a + // CIE, then this field points to this structure. + CIE *cie; + }; + + // A common information entry (CIE). + struct CIE: public Entry { + uint8 version; // CFI data version number + string augmentation; // vendor format extension markers + uint64 code_alignment_factor; // scale for code address adjustments + int data_alignment_factor; // scale for stack pointer adjustments + unsigned return_address_register; // which register holds the return addr + + // True if this CIE includes Linux C++ ABI 'z' augmentation data. + bool has_z_augmentation; + + // Parsed 'z' augmentation data. These are meaningful only if + // has_z_augmentation is true. + bool has_z_lsda; // The 'z' augmentation included 'L'. + bool has_z_personality; // The 'z' augmentation included 'P'. + bool has_z_signal_frame; // The 'z' augmentation included 'S'. + + // If has_z_lsda is true, this is the encoding to be used for language- + // specific data area pointers in FDEs. + DwarfPointerEncoding lsda_encoding; + + // If has_z_personality is true, this is the encoding used for the + // personality routine pointer in the augmentation data. + DwarfPointerEncoding personality_encoding; + + // If has_z_personality is true, this is the address of the personality + // routine --- or, if personality_encoding & DW_EH_PE_indirect, the + // address where the personality routine's address is stored. + uint64 personality_address; + + // This is the encoding used for addresses in the FDE header and + // in DW_CFA_set_loc instructions. This is always valid, whether + // or not we saw a 'z' augmentation string; its default value is + // DW_EH_PE_absptr, which is what normal DWARF CFI uses. + DwarfPointerEncoding pointer_encoding; + }; + + // A frame description entry (FDE). + struct FDE: public Entry { + uint64 address; // start address of described code + uint64 size; // size of described code, in bytes + + // If cie->has_z_lsda is true, then this is the language-specific data + // area's address --- or its address's address, if cie->lsda_encoding + // has the DW_EH_PE_indirect bit set. + uint64 lsda_address; + }; + + // Internal use. + class Rule; + class UndefinedRule; + class SameValueRule; + class OffsetRule; + class ValOffsetRule; + class RegisterRule; + class ExpressionRule; + class ValExpressionRule; + class RuleMap; + class State; + + // Parse the initial length and id of a CFI entry, either a CIE, an FDE, + // or a .eh_frame end-of-data mark. CURSOR points to the beginning of the + // data to parse. On success, populate ENTRY as appropriate, and return + // true. On failure, report the problem, and return false. Even if we + // return false, set ENTRY->end to the first byte after the entry if we + // were able to figure that out, or NULL if we weren't. + bool ReadEntryPrologue(const char *cursor, Entry *entry); + + // Parse the fields of a CIE after the entry prologue, including any 'z' + // augmentation data. Assume that the 'Entry' fields of CIE are + // populated; use CIE->fields and CIE->end as the start and limit for + // parsing. On success, populate the rest of *CIE, and return true; on + // failure, report the problem and return false. + bool ReadCIEFields(CIE *cie); + + // Parse the fields of an FDE after the entry prologue, including any 'z' + // augmentation data. Assume that the 'Entry' fields of *FDE are + // initialized; use FDE->fields and FDE->end as the start and limit for + // parsing. Assume that FDE->cie is fully initialized. On success, + // populate the rest of *FDE, and return true; on failure, report the + // problem and return false. + bool ReadFDEFields(FDE *fde); + + // Report that ENTRY is incomplete, and return false. This is just a + // trivial wrapper for invoking reporter_->Incomplete; it provides a + // little brevity. + bool ReportIncomplete(Entry *entry); + + // Return true if ENCODING has the DW_EH_PE_indirect bit set. + static bool IsIndirectEncoding(DwarfPointerEncoding encoding) { + return encoding & DW_EH_PE_indirect; + } + + // The contents of the DWARF .debug_info section we're parsing. + const char *buffer_; + size_t buffer_length_; + + // For reading multi-byte values with the appropriate endianness. + ByteReader *reader_; + + // The handler to which we should report the data we find. + Handler *handler_; + + // For reporting problems in the info we're parsing. + Reporter *reporter_; + + // True if we are processing .eh_frame-format data. + bool eh_frame_; +}; + +// The handler class for CallFrameInfo. The a CFI parser calls the +// member functions of a handler object to report the data it finds. +class CallFrameInfo::Handler { + public: + // The pseudo-register number for the canonical frame address. + enum { kCFARegister = -1 }; + + Handler() { } + virtual ~Handler() { } + + // The parser has found CFI for the machine code at ADDRESS, + // extending for LENGTH bytes. OFFSET is the offset of the frame + // description entry in the section, for use in error messages. + // VERSION is the version number of the CFI format. AUGMENTATION is + // a string describing any producer-specific extensions present in + // the data. RETURN_ADDRESS is the number of the register that holds + // the address to which the function should return. + // + // Entry should return true to process this CFI, or false to skip to + // the next entry. + // + // The parser invokes Entry for each Frame Description Entry (FDE) + // it finds. The parser doesn't report Common Information Entries + // to the handler explicitly; instead, if the handler elects to + // process a given FDE, the parser reiterates the appropriate CIE's + // contents at the beginning of the FDE's rules. + virtual bool Entry(size_t offset, uint64 address, uint64 length, + uint8 version, const string &augmentation, + unsigned return_address) = 0; + + // When the Entry function returns true, the parser calls these + // handler functions repeatedly to describe the rules for recovering + // registers at each instruction in the given range of machine code. + // Immediately after a call to Entry, the handler should assume that + // the rule for each callee-saves register is "unchanged" --- that + // is, that the register still has the value it had in the caller. + // + // If a *Rule function returns true, we continue processing this entry's + // instructions. If a *Rule function returns false, we stop evaluating + // instructions, and skip to the next entry. Either way, we call End + // before going on to the next entry. + // + // In all of these functions, if the REG parameter is kCFARegister, then + // the rule describes how to find the canonical frame address. + // kCFARegister may be passed as a BASE_REGISTER argument, meaning that + // the canonical frame address should be used as the base address for the + // computation. All other REG values will be positive. + + // At ADDRESS, register REG's value is not recoverable. + virtual bool UndefinedRule(uint64 address, int reg) = 0; + + // At ADDRESS, register REG's value is the same as that it had in + // the caller. + virtual bool SameValueRule(uint64 address, int reg) = 0; + + // At ADDRESS, register REG has been saved at offset OFFSET from + // BASE_REGISTER. + virtual bool OffsetRule(uint64 address, int reg, + int base_register, long offset) = 0; + + // At ADDRESS, the caller's value of register REG is the current + // value of BASE_REGISTER plus OFFSET. (This rule doesn't provide an + // address at which the register's value is saved.) + virtual bool ValOffsetRule(uint64 address, int reg, + int base_register, long offset) = 0; + + // At ADDRESS, register REG has been saved in BASE_REGISTER. This differs + // from ValOffsetRule(ADDRESS, REG, BASE_REGISTER, 0), in that + // BASE_REGISTER is the "home" for REG's saved value: if you want to + // assign to a variable whose home is REG in the calling frame, you + // should put the value in BASE_REGISTER. + virtual bool RegisterRule(uint64 address, int reg, int base_register) = 0; + + // At ADDRESS, the DWARF expression EXPRESSION yields the address at + // which REG was saved. + virtual bool ExpressionRule(uint64 address, int reg, + const string &expression) = 0; + + // At ADDRESS, the DWARF expression EXPRESSION yields the caller's + // value for REG. (This rule doesn't provide an address at which the + // register's value is saved.) + virtual bool ValExpressionRule(uint64 address, int reg, + const string &expression) = 0; + + // Indicate that the rules for the address range reported by the + // last call to Entry are complete. End should return true if + // everything is okay, or false if an error has occurred and parsing + // should stop. + virtual bool End() = 0; + + // Handler functions for Linux C++ exception handling data. These are + // only called if the data includes 'z' augmentation strings. + + // The Linux C++ ABI uses an extension of the DWARF CFI format to + // walk the stack to propagate exceptions from the throw to the + // appropriate catch, and do the appropriate cleanups along the way. + // CFI entries used for exception handling have two additional data + // associated with them: + // + // - The "language-specific data area" describes which exception + // types the function has 'catch' clauses for, and indicates how + // to go about re-entering the function at the appropriate catch + // clause. If the exception is not caught, it describes the + // destructors that must run before the frame is popped. + // + // - The "personality routine" is responsible for interpreting the + // language-specific data area's contents, and deciding whether + // the exception should continue to propagate down the stack, + // perhaps after doing some cleanup for this frame, or whether the + // exception will be caught here. + // + // In principle, the language-specific data area is opaque to + // everybody but the personality routine. In practice, these values + // may be useful or interesting to readers with extra context, and + // we have to at least skip them anyway, so we might as well report + // them to the handler. + + // This entry's exception handling personality routine's address is + // ADDRESS. If INDIRECT is true, then ADDRESS is the address at + // which the routine's address is stored. The default definition for + // this handler function simply returns true, allowing parsing of + // the entry to continue. + virtual bool PersonalityRoutine(uint64 address, bool indirect) { + return true; + } + + // This entry's language-specific data area (LSDA) is located at + // ADDRESS. If INDIRECT is true, then ADDRESS is the address at + // which the area's address is stored. The default definition for + // this handler function simply returns true, allowing parsing of + // the entry to continue. + virtual bool LanguageSpecificDataArea(uint64 address, bool indirect) { + return true; + } + + // This entry describes a signal trampoline --- this frame is the + // caller of a signal handler. The default definition for this + // handler function simply returns true, allowing parsing of the + // entry to continue. + // + // The best description of the rationale for and meaning of signal + // trampoline CFI entries seems to be in the GCC bug database: + // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=26208 + virtual bool SignalHandler() { return true; } +}; + +// The CallFrameInfo class makes calls on an instance of this class to +// report errors or warn about problems in the data it is parsing. The +// default definitions of these methods print a message to stderr, but +// you can make a derived class that overrides them. +class CallFrameInfo::Reporter { + public: + // Create an error reporter which attributes troubles to the section + // named SECTION in FILENAME. + // + // Normally SECTION would be .debug_frame, but the Mac puts CFI data + // in a Mach-O section named __debug_frame. If we support + // Linux-style exception handling data, we could be reading an + // .eh_frame section. + Reporter(const string &filename, + const string §ion = ".debug_frame") + : filename_(filename), section_(section) { } + virtual ~Reporter() { } + + // The CFI entry at OFFSET ends too early to be well-formed. KIND + // indicates what kind of entry it is; KIND can be kUnknown if we + // haven't parsed enough of the entry to tell yet. + virtual void Incomplete(uint64 offset, CallFrameInfo::EntryKind kind); + + // The .eh_frame data has a four-byte zero at OFFSET where the next + // entry's length would be; this is a terminator. However, the buffer + // length as given to the CallFrameInfo constructor says there should be + // more data. + virtual void EarlyEHTerminator(uint64 offset); + + // The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the + // section is not that large. + virtual void CIEPointerOutOfRange(uint64 offset, uint64 cie_offset); + + // The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the entry + // there is not a CIE. + virtual void BadCIEId(uint64 offset, uint64 cie_offset); + + // The FDE at OFFSET refers to a CIE with version number VERSION, + // which we don't recognize. We cannot parse DWARF CFI if it uses + // a version number we don't recognize. + virtual void UnrecognizedVersion(uint64 offset, int version); + + // The FDE at OFFSET refers to a CIE with augmentation AUGMENTATION, + // which we don't recognize. We cannot parse DWARF CFI if it uses + // augmentations we don't recognize. + virtual void UnrecognizedAugmentation(uint64 offset, + const string &augmentation); + + // The pointer encoding ENCODING, specified by the CIE at OFFSET, is not + // a valid encoding. + virtual void InvalidPointerEncoding(uint64 offset, uint8 encoding); + + // The pointer encoding ENCODING, specified by the CIE at OFFSET, depends + // on a base address which has not been supplied. + virtual void UnusablePointerEncoding(uint64 offset, uint8 encoding); + + // The CIE at OFFSET contains a DW_CFA_restore instruction at + // INSN_OFFSET, which may not appear in a CIE. + virtual void RestoreInCIE(uint64 offset, uint64 insn_offset); + + // The entry at OFFSET, of kind KIND, has an unrecognized + // instruction at INSN_OFFSET. + virtual void BadInstruction(uint64 offset, CallFrameInfo::EntryKind kind, + uint64 insn_offset); + + // The instruction at INSN_OFFSET in the entry at OFFSET, of kind + // KIND, establishes a rule that cites the CFA, but we have not + // established a CFA rule yet. + virtual void NoCFARule(uint64 offset, CallFrameInfo::EntryKind kind, + uint64 insn_offset); + + // The instruction at INSN_OFFSET in the entry at OFFSET, of kind + // KIND, is a DW_CFA_restore_state instruction, but the stack of + // saved states is empty. + virtual void EmptyStateStack(uint64 offset, CallFrameInfo::EntryKind kind, + uint64 insn_offset); + + // The DW_CFA_remember_state instruction at INSN_OFFSET in the entry + // at OFFSET, of kind KIND, would restore a state that has no CFA + // rule, whereas the current state does have a CFA rule. This is + // bogus input, which the CallFrameInfo::Handler interface doesn't + // (and shouldn't) have any way to report. + virtual void ClearingCFARule(uint64 offset, CallFrameInfo::EntryKind kind, + uint64 insn_offset); + + protected: + // The name of the file whose CFI we're reading. + string filename_; + + // The name of the CFI section in that file. + string section_; +}; + +} // namespace dwarf2reader + +#endif // UTIL_DEBUGINFO_DWARF2READER_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/functioninfo.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/functioninfo.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/functioninfo.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/functioninfo.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,223 @@ +// Copyright (c) 2010 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This is a client for the dwarf2reader to extract function and line +// information from the debug info. + +#include + +#include +#include +#include + +#include "common/dwarf/functioninfo.h" + +#include "common/dwarf/bytereader.h" + + +namespace dwarf2reader { + +CULineInfoHandler::CULineInfoHandler(vector* files, + vector* dirs, + LineMap* linemap):linemap_(linemap), + files_(files), + dirs_(dirs) { + // The dirs and files are 1 indexed, so just make sure we put + // nothing in the 0 vector. + assert(dirs->size() == 0); + assert(files->size() == 0); + dirs->push_back(""); + SourceFileInfo s; + s.name = ""; + s.lowpc = ULLONG_MAX; + files->push_back(s); +} + +void CULineInfoHandler::DefineDir(const string& name, uint32 dir_num) { + // These should never come out of order, actually + assert(dir_num == dirs_->size()); + dirs_->push_back(name); +} + +void CULineInfoHandler::DefineFile(const string& name, + int32 file_num, uint32 dir_num, + uint64 mod_time, uint64 length) { + assert(dir_num >= 0); + assert(dir_num < dirs_->size()); + + // These should never come out of order, actually. + if (file_num == (int32)files_->size() || file_num == -1) { + string dir = dirs_->at(dir_num); + + SourceFileInfo s; + s.lowpc = ULLONG_MAX; + + if (dir == "") { + s.name = name; + } else { + s.name = dir + "/" + name; + } + + files_->push_back(s); + } else { + fprintf(stderr, "error in DefineFile"); + } +} + +void CULineInfoHandler::AddLine(uint64 address, uint64 length, uint32 file_num, + uint32 line_num, uint32 column_num) { + if (file_num < files_->size()) { + linemap_->insert(make_pair(address, make_pair(files_->at(file_num).name.c_str(), + line_num))); + + if(address < files_->at(file_num).lowpc) { + files_->at(file_num).lowpc = address; + } + } else { + fprintf(stderr,"error in AddLine"); + } +} + +bool CUFunctionInfoHandler::StartCompilationUnit(uint64 offset, + uint8 address_size, + uint8 offset_size, + uint64 cu_length, + uint8 dwarf_version) { + current_compilation_unit_offset_ = offset; + return true; +} + + +// For function info, we only care about subprograms and inlined +// subroutines. For line info, the DW_AT_stmt_list lives in the +// compile unit tag. + +bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs) { + switch (tag) { + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: { + current_function_info_ = new FunctionInfo; + current_function_info_->lowpc = current_function_info_->highpc = 0; + current_function_info_->name = ""; + current_function_info_->line = 0; + current_function_info_->file = ""; + offset_to_funcinfo_->insert(make_pair(offset, current_function_info_)); + }; + // FALLTHROUGH + case DW_TAG_compile_unit: + return true; + default: + return false; + } + return false; +} + +// Only care about the name attribute for functions + +void CUFunctionInfoHandler::ProcessAttributeString(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const string &data) { + if (current_function_info_) { + if (attr == DW_AT_name) + current_function_info_->name = data; + else if(attr == DW_AT_MIPS_linkage_name) + current_function_info_->mangled_name = data; + } +} + +void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + if (attr == DW_AT_stmt_list) { + SectionMap::const_iterator iter = sections_.find("__debug_line"); + assert(iter != sections_.end()); + + // this should be a scoped_ptr but we dont' use boost :-( + auto_ptr lireader(new LineInfo(iter->second.first + data, + iter->second.second - data, + reader_, linehandler_)); + lireader->Start(); + } else if (current_function_info_) { + switch (attr) { + case DW_AT_low_pc: + current_function_info_->lowpc = data; + break; + case DW_AT_high_pc: + current_function_info_->highpc = data; + break; + case DW_AT_decl_line: + current_function_info_->line = data; + break; + case DW_AT_decl_file: + current_function_info_->file = files_->at(data).name; + break; + default: + break; + } + } +} + +void CUFunctionInfoHandler::ProcessAttributeReference(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + if (current_function_info_) { + switch (attr) { + case DW_AT_specification: { + // Some functions have a "specification" attribute + // which means they were defined elsewhere. The name + // attribute is not repeated, and must be taken from + // the specification DIE. Here we'll assume that + // any DIE referenced in this manner will already have + // been seen, but that's not really required by the spec. + FunctionMap::iterator iter = offset_to_funcinfo_->find(data); + if (iter != offset_to_funcinfo_->end()) { + current_function_info_->name = iter->second->name; + current_function_info_->mangled_name = iter->second->mangled_name; + } else { + // If you hit this, this code probably needs to be rewritten. + fprintf(stderr, "Error: DW_AT_specification was seen before the referenced DIE! (Looking for DIE at offset %08llx, in DIE at offset %08llx)\n", data, offset); + } + break; + } + default: + break; + } + } +} + +void CUFunctionInfoHandler::EndDIE(uint64 offset) { + if (current_function_info_ && current_function_info_->lowpc) + address_to_funcinfo_->insert(make_pair(current_function_info_->lowpc, + current_function_info_)); +} + +} // namespace dwarf2reader diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/functioninfo.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/functioninfo.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/functioninfo.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/functioninfo.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,188 @@ +// Copyright (c) 2010 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// This file contains the definitions for a DWARF2/3 information +// collector that uses the DWARF2/3 reader interface to build a mapping +// of addresses to files, lines, and functions. + +#ifndef COMMON_DWARF_FUNCTIONINFO_H__ +#define COMMON_DWARF_FUNCTIONINFO_H__ + +#include +#include +#include +#include + +#include "common/dwarf/dwarf2reader.h" + + +namespace dwarf2reader { + +struct FunctionInfo { + // Name of the function + string name; + // Mangled name of the function + string mangled_name; + // File containing this function + string file; + // Line number for start of function. + uint32 line; + // Beginning address for this function + uint64 lowpc; + // End address for this function. + uint64 highpc; +}; + +struct SourceFileInfo { + // Name of the source file name + string name; + // Low address of source file name + uint64 lowpc; +}; + +typedef map FunctionMap; +typedef map > LineMap; + +// This class is a basic line info handler that fills in the dirs, +// file, and linemap passed into it with the data produced from the +// LineInfoHandler. +class CULineInfoHandler: public LineInfoHandler { + public: + + // + CULineInfoHandler(vector* files, + vector* dirs, + LineMap* linemap); + virtual ~CULineInfoHandler() { } + + // Called when we define a directory. We just place NAME into dirs_ + // at position DIR_NUM. + virtual void DefineDir(const string& name, uint32 dir_num); + + // Called when we define a filename. We just place + // concat(dirs_[DIR_NUM], NAME) into files_ at position FILE_NUM. + virtual void DefineFile(const string& name, int32 file_num, + uint32 dir_num, uint64 mod_time, uint64 length); + + + // Called when the line info reader has a new line, address pair + // ready for us. ADDRESS is the address of the code, LENGTH is the + // length of its machine code in bytes, FILE_NUM is the file number + // containing the code, LINE_NUM is the line number in that file for + // the code, and COLUMN_NUM is the column number the code starts at, + // if we know it (0 otherwise). + virtual void AddLine(uint64 address, uint64 length, + uint32 file_num, uint32 line_num, uint32 column_num); + + private: + LineMap* linemap_; + vector* files_; + vector* dirs_; +}; + +class CUFunctionInfoHandler: public Dwarf2Handler { + public: + CUFunctionInfoHandler(vector* files, + vector* dirs, + LineMap* linemap, + FunctionMap* offset_to_funcinfo, + FunctionMap* address_to_funcinfo, + CULineInfoHandler* linehandler, + const SectionMap& sections, + ByteReader* reader) + : files_(files), dirs_(dirs), linemap_(linemap), + offset_to_funcinfo_(offset_to_funcinfo), + address_to_funcinfo_(address_to_funcinfo), + linehandler_(linehandler), sections_(sections), + reader_(reader), current_function_info_(NULL) { } + + virtual ~CUFunctionInfoHandler() { } + + // Start to process a compilation unit at OFFSET from the beginning of the + // .debug_info section. We want to see all compilation units, so we + // always return true. + + virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version); + + // Start to process a DIE at OFFSET from the beginning of the + // .debug_info section. We only care about function related DIE's. + virtual bool StartDIE(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs); + + // Called when we have an attribute with unsigned data to give to + // our handler. The attribute is for the DIE at OFFSET from the + // beginning of the .debug_info section, has a name of ATTR, a form of + // FORM, and the actual data of the attribute is in DATA. + virtual void ProcessAttributeUnsigned(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + + // Called when we have an attribute with a DIE reference to give to + // our handler. The attribute is for the DIE at OFFSET from the + // beginning of the .debug_info section, has a name of ATTR, a form of + // FORM, and the offset of the referenced DIE from the start of the + // .debug_info section is in DATA. + virtual void ProcessAttributeReference(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + + // Called when we have an attribute with string data to give to + // our handler. The attribute is for the DIE at OFFSET from the + // beginning of the .debug_info section, has a name of ATTR, a form of + // FORM, and the actual data of the attribute is in DATA. + virtual void ProcessAttributeString(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const string& data); + + // Called when finished processing the DIE at OFFSET. + // Because DWARF2/3 specifies a tree of DIEs, you may get starts + // before ends of the previous DIE, as we process children before + // ending the parent. + virtual void EndDIE(uint64 offset); + + private: + vector* files_; + vector* dirs_; + LineMap* linemap_; + FunctionMap* offset_to_funcinfo_; + FunctionMap* address_to_funcinfo_; + CULineInfoHandler* linehandler_; + const SectionMap& sections_; + ByteReader* reader_; + FunctionInfo* current_function_info_; + uint64 current_compilation_unit_offset_; +}; + +} // namespace dwarf2reader +#endif // COMMON_DWARF_FUNCTIONINFO_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/line_state_machine.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/line_state_machine.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/line_state_machine.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/line_state_machine.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,61 @@ +// Copyright 2008 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +#ifndef COMMON_DWARF_LINE_STATE_MACHINE_H__ +#define COMMON_DWARF_LINE_STATE_MACHINE_H__ + +namespace dwarf2reader { + +// This is the format of a DWARF2/3 line state machine that we process +// opcodes using. There is no need for anything outside the lineinfo +// processor to know how this works. +struct LineStateMachine { + void Reset(bool default_is_stmt) { + file_num = 1; + address = 0; + line_num = 1; + column_num = 0; + is_stmt = default_is_stmt; + basic_block = false; + end_sequence = false; + } + + uint32 file_num; + uint64 address; + uint64 line_num; + uint32 column_num; + bool is_stmt; // stmt means statement. + bool basic_block; + bool end_sequence; +}; + +} // namespace dwarf2reader + + +#endif // COMMON_DWARF_LINE_STATE_MACHINE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/Makefile.in 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/Makefile.in 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,67 @@ +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (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.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is Mozilla Breakpad integration +# +# The Initial Developer of the Original Code is +# The Mozilla Foundation +# Portions created by the Initial Developer are Copyright (C) 2008 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Ted Mielczarek +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +DEPTH = ../../../../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = breakpad_dwarf +HOST_LIBRARY_NAME = host_breakpad_dwarf_s + +LOCAL_INCLUDES = -I$(srcdir)/../.. + +HOST_CPPSRCS = \ + bytereader.cc \ + dwarf2diehandler.cc \ + dwarf2reader.cc \ + $(NULL) + +ifeq (Darwin,$(HOST_OS_ARCH)) +HOST_CPPSRCS += functioninfo.cc +endif + +# This code is only compiled for build-time tools, +# so enabling RTTI should be fine. +HOST_CXXFLAGS := -funsigned-char $(filter-out -fno-rtti,$(HOST_CXXFLAGS)) + +# need static lib +FORCE_STATIC_LIB = 1 + +include $(topsrcdir)/config/rules.mk diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/types.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/types.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/types.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/dwarf/types.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,53 @@ +// Copyright 2008 Google, Inc. All Rights reserved +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +// This file contains some typedefs for basic types + + +#ifndef _COMMON_DWARF_TYPES_H__ +#define _COMMON_DWARF_TYPES_H__ + +typedef signed char int8; +typedef short int16; +typedef int int32; +typedef long long int64; + +typedef unsigned char uint8; +typedef unsigned short uint16; +typedef unsigned int uint32; +typedef unsigned long long uint64; + +#ifdef __PTRDIFF_TYPE__ +typedef __PTRDIFF_TYPE__ intptr; +typedef unsigned __PTRDIFF_TYPE__ uintptr; +#else +#error "Can't find pointer-sized integral types." +#endif + +#endif // _COMMON_DWARF_TYPES_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_stabs.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_stabs.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_stabs.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_stabs.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,178 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// dump_stabs.cc --- implement the DumpStabsHandler class. + +#include +#include + +#include +#include + +#include "common/linux/dump_stabs.h" + +namespace google_breakpad { + +using std::string; + +// Demangle using abi call. +// Older GCC may not support it. +static string Demangle(const string &mangled) { + int status = 0; + char *demangled = abi::__cxa_demangle(mangled.c_str(), NULL, NULL, &status); + if (status == 0 && demangled != NULL) { + string str(demangled); + free(demangled); + return str; + } + return string(mangled); +} + +bool DumpStabsHandler::StartCompilationUnit(const char *name, uint64_t address, + const char *build_directory) { + assert(!in_compilation_unit_); + in_compilation_unit_ = true; + current_source_file_name_ = name; + current_source_file_ = module_->FindFile(name); + comp_unit_base_address_ = address; + boundaries_.push_back(static_cast(address)); + return true; +} + +bool DumpStabsHandler::EndCompilationUnit(uint64_t address) { + assert(in_compilation_unit_); + in_compilation_unit_ = false; + comp_unit_base_address_ = 0; + current_source_file_ = NULL; + current_source_file_name_ = NULL; + if (address) + boundaries_.push_back(static_cast(address)); + return true; +} + +bool DumpStabsHandler::StartFunction(const string &name, + uint64_t address) { + assert(!current_function_); + Module::Function *f = new Module::Function; + f->name = Demangle(name); + f->address = address; + f->size = 0; // We compute this in DumpStabsHandler::Finalize(). + f->parameter_size = 0; // We don't provide this information. + current_function_ = f; + boundaries_.push_back(static_cast(address)); + return true; +} + +bool DumpStabsHandler::EndFunction(uint64_t address) { + assert(current_function_); + // Functions in this compilation unit should have address bigger + // than the compilation unit's starting address. There may be a lot + // of duplicated entries for functions in the STABS data; only one + // entry can meet this requirement. + // + // (I don't really understand the above comment; just bringing it + // along from the previous code, and leaving the behaivor unchanged. + // If you know the whole story, please patch this comment. --jimb) + if (current_function_->address >= comp_unit_base_address_) + functions_.push_back(current_function_); + else + delete current_function_; + current_function_ = NULL; + if (address) + boundaries_.push_back(static_cast(address)); + return true; +} + +bool DumpStabsHandler::Line(uint64_t address, const char *name, int number) { + assert(current_function_); + assert(current_source_file_); + if (name != current_source_file_name_) { + current_source_file_ = module_->FindFile(name); + current_source_file_name_ = name; + } + Module::Line line; + line.address = address; + line.size = 0; // We compute this in DumpStabsHandler::Finalize(). + line.file = current_source_file_; + line.number = number; + current_function_->lines.push_back(line); + return true; +} + +void DumpStabsHandler::Warning(const char *format, ...) { + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); +} + +void DumpStabsHandler::Finalize() { + // Sort our boundary list, so we can search it quickly. + sort(boundaries_.begin(), boundaries_.end()); + // Sort all functions by address, just for neatness. + sort(functions_.begin(), functions_.end(), + Module::Function::CompareByAddress); + for (vector::iterator func_it = functions_.begin(); + func_it != functions_.end(); + func_it++) { + Module::Function *f = *func_it; + // Compute the function f's size. + vector::iterator boundary + = std::upper_bound(boundaries_.begin(), boundaries_.end(), f->address); + if (boundary != boundaries_.end()) + f->size = *boundary - f->address; + else + // If this is the last function in the module, and the STABS + // reader was unable to give us its ending address, then assign + // it a bogus, very large value. This will happen at most once + // per module: since we've added all functions' addresses to the + // boundary table, only one can be the last. + f->size = kFallbackSize; + + // Compute sizes for each of the function f's lines --- if it has any. + if (!f->lines.empty()) { + stable_sort(f->lines.begin(), f->lines.end(), + Module::Line::CompareByAddress); + vector::iterator last_line = f->lines.end() - 1; + for (vector::iterator line_it = f->lines.begin(); + line_it != last_line; line_it++) + line_it[0].size = line_it[1].address - line_it[0].address; + // Compute the size of the last line from f's end address. + last_line->size = (f->address + f->size) - last_line->address; + } + } + // Now that everything has a size, add our functions to the module, and + // dispose of our private list. + module_->AddFunctions(functions_.begin(), functions_.end()); + functions_.clear(); +} + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_stabs.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_stabs.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_stabs.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_stabs.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,139 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// dump_stabs.h: Define the DumpStabsHandler class, which receives +// STABS debugging information from a parser and adds it to a Breakpad +// symbol file. + +#ifndef COMMON_LINUX_DUMP_STABS_H__ +#define COMMON_LINUX_DUMP_STABS_H__ + +#include + +#include +#include + +#include "common/linux/module.h" +#include "common/linux/stabs_reader.h" + +namespace google_breakpad { + +using std::string; +using std::vector; + +// A DumpStabsHandler is a handler that receives parsed STABS +// debugging information from a StabsReader, and uses that to populate +// a Module. (All classes are in the google_breakpad namespace.) A +// Module represents the contents of a Breakpad symbol file, and knows +// how to write itself out as such. A DumpStabsHandler thus acts as +// the bridge between STABS and Breakpad data. +class DumpStabsHandler: public google_breakpad::StabsHandler { + public: + // Receive parsed debugging information from a StabsReader, and + // store it all in MODULE. + DumpStabsHandler(Module *module) : + module_(module), + in_compilation_unit_(false), + comp_unit_base_address_(0), + current_function_(NULL), + current_source_file_(NULL), + current_source_file_name_(NULL) { } + + // The standard StabsHandler virtual member functions. + bool StartCompilationUnit(const char *name, uint64_t address, + const char *build_directory); + bool EndCompilationUnit(uint64_t address); + bool StartFunction(const string &name, uint64_t address); + bool EndFunction(uint64_t address); + bool Line(uint64_t address, const char *name, int number); + void Warning(const char *format, ...); + + // Do any final processing necessary to make module_ contain all the + // data provided by the STABS reader. + // + // Because STABS does not provide reliable size information for + // functions and lines, we need to make a pass over the data after + // processing all the STABS to compute those sizes. We take care of + // that here. + void Finalize(); + + private: + + // An arbitrary, but very large, size to use for functions whose + // size we can't compute properly. + static const uint64_t kFallbackSize = 0x10000000; + + // The module we're contributing debugging info to. + Module *module_; + + // The functions we've generated so far. We don't add these to + // module_ as we parse them. Instead, we wait until we've computed + // their ending address, and their lines' ending addresses. + // + // We could just stick them in module_ from the outset, but if + // module_ already contains data gathered from other debugging + // formats, that would complicate the size computation. + vector functions_; + + // Boundary addresses. STABS doesn't necessarily supply sizes for + // functions and lines, so we need to compute them ourselves by + // finding the next object. + vector boundaries_; + + // True if we are currently within a compilation unit: we have gotten a + // StartCompilationUnit call, but no matching EndCompilationUnit call + // yet. We use this for sanity checks. + bool in_compilation_unit_; + + // The base address of the current compilation unit. We use this to + // recognize functions we should omit from the symbol file. (If you + // know the details of why we omit these, please patch this + // comment.) + Module::Address comp_unit_base_address_; + + // The function we're currently contributing lines to. + Module::Function *current_function_; + + // The last Module::File we got a line number in. + Module::File *current_source_file_; + + // The pointer in the .stabstr section of the name that + // current_source_file_ is built from. This allows us to quickly + // recognize when the current line is in the same file as the + // previous one (which it usually is). + const char *current_source_file_name_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DUMP_STABS_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_stabs_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_stabs_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_stabs_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_stabs_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,193 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// dump_stabs_unittest.cc: Unit tests for DumpStabsHandler. + +#include + +#include "breakpad_googletest_includes.h" +#include "common/linux/dump_stabs.h" + +using google_breakpad::DumpStabsHandler; +using google_breakpad::Module; +using std::vector; + +TEST(DumpStabsHandler, SimpleCU) { + Module m("name", "os", "arch", "id"); + DumpStabsHandler h(&m); + + // Feed in a simple compilation unit that defines a function with + // one line. + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0x9f4d1271e50db93bLL, + "build-directory")); + EXPECT_TRUE(h.StartFunction("function", 0xfde4abbed390c394LL)); + EXPECT_TRUE(h.Line(0xfde4abbed390c394LL, "source-file-name", 174823314)); + EXPECT_TRUE(h.EndFunction(0xfde4abbed390c3a4LL)); + EXPECT_TRUE(h.EndCompilationUnit(0xfee4abbed390c3a4LL)); + h.Finalize(); + + // Now check to see what has been added to the Module. + Module::File *file = m.FindExistingFile("source-file-name"); + ASSERT_TRUE(file != NULL); + + vector functions; + m.GetFunctions(&functions, functions.end()); + ASSERT_EQ((size_t) 1, functions.size()); + Module::Function *function = functions[0]; + EXPECT_STREQ("function", function->name.c_str()); + EXPECT_EQ(0xfde4abbed390c394LL, function->address); + EXPECT_EQ(0x10U, function->size); + EXPECT_EQ(0U, function->parameter_size); + ASSERT_EQ((size_t) 1, function->lines.size()); + Module::Line *line = &function->lines[0]; + EXPECT_EQ(0xfde4abbed390c394LL, line->address); + EXPECT_EQ(0x10U, line->size); // derived from EndFunction + EXPECT_TRUE(line->file == file); + EXPECT_EQ(174823314, line->number); +} + +TEST(InferSizes, LineSize) { + Module m("name", "os", "arch", "id"); + DumpStabsHandler h(&m); + + // Feed in a simple compilation unit that defines a function with + // one line. + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xb4513962eff94e92LL, + "build-directory")); + EXPECT_TRUE(h.StartFunction("function", 0xb4513962eff94e92LL)); + EXPECT_TRUE(h.Line(0xb4513962eff94e92LL, "source-file-name-1", 77396614)); + EXPECT_TRUE(h.Line(0xb4513963eff94e92LL, "source-file-name-2", 87660088)); + EXPECT_TRUE(h.EndFunction(0)); // unknown function end address + EXPECT_TRUE(h.EndCompilationUnit(0)); // unknown CU end address + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit-2", 0xb4523963eff94e92LL, + "build-directory-2")); // next boundary + EXPECT_TRUE(h.EndCompilationUnit(0)); + h.Finalize(); + + // Now check to see what has been added to the Module. + Module::File *file1 = m.FindExistingFile("source-file-name-1"); + ASSERT_TRUE(file1 != NULL); + Module::File *file2 = m.FindExistingFile("source-file-name-2"); + ASSERT_TRUE(file2 != NULL); + + vector functions; + m.GetFunctions(&functions, functions.end()); + ASSERT_EQ((size_t) 1, functions.size()); + + Module::Function *function = functions[0]; + EXPECT_STREQ("function", function->name.c_str()); + EXPECT_EQ(0xb4513962eff94e92LL, function->address); + EXPECT_EQ(0x1000100000000ULL, function->size); // inferred from CU end + EXPECT_EQ(0U, function->parameter_size); + ASSERT_EQ((size_t) 2, function->lines.size()); + + Module::Line *line1 = &function->lines[0]; + EXPECT_EQ(0xb4513962eff94e92LL, line1->address); + EXPECT_EQ(0x100000000ULL, line1->size); // derived from EndFunction + EXPECT_TRUE(line1->file == file1); + EXPECT_EQ(77396614, line1->number); + + Module::Line *line2 = &function->lines[1]; + EXPECT_EQ(0xb4513963eff94e92LL, line2->address); + EXPECT_EQ(0x1000000000000ULL, line2->size); // derived from EndFunction + EXPECT_TRUE(line2->file == file2); + EXPECT_EQ(87660088, line2->number); +} + +TEST(FunctionNames, Mangled) { + Module m("name", "os", "arch", "id"); + DumpStabsHandler h(&m); + + // Compilation unit with one function, mangled name. + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0xf2cfda63cef7f46cLL, + "build-directory")); + EXPECT_TRUE(h.StartFunction("_ZNSt6vectorIySaIyEE9push_backERKy", + 0xf2cfda63cef7f46dLL)); + EXPECT_TRUE(h.EndFunction(0)); + EXPECT_TRUE(h.EndCompilationUnit(0)); + + h.Finalize(); + + // Now check to see what has been added to the Module. + Module::File *file = m.FindExistingFile("compilation-unit"); + ASSERT_TRUE(file != NULL); + + vector functions; + m.GetFunctions(&functions, functions.end()); + ASSERT_EQ(1U, functions.size()); + + Module::Function *function = functions[0]; + // This is GCC-specific, but we shouldn't be seeing STABS data anywhere + // but Linux. + EXPECT_STREQ("std::vector >::" + "push_back(unsigned long long const&)", + function->name.c_str()); + EXPECT_EQ(0xf2cfda63cef7f46dLL, function->address); + EXPECT_LT(0U, function->size); // should have used dummy size + EXPECT_EQ(0U, function->parameter_size); + ASSERT_EQ(0U, function->lines.size()); +} + +// The GNU toolchain can omit functions that are not used; however, +// when it does so, it doesn't clean up the debugging information that +// refers to them. In STABS, this results in compilation units whose +// SO addresses are zero. +TEST(Omitted, Function) { + Module m("name", "os", "arch", "id"); + DumpStabsHandler h(&m); + + // The StartCompilationUnit and EndCompilationUnit calls may both have an + // address of zero if the compilation unit has had sections removed. + EXPECT_TRUE(h.StartCompilationUnit("compilation-unit", 0, "build-directory")); + EXPECT_TRUE(h.StartFunction("function", 0x2a133596)); + EXPECT_TRUE(h.EndFunction(0)); + EXPECT_TRUE(h.EndCompilationUnit(0)); +} + +// TODO --- if we actually cared about STABS. Even without these we've +// got full coverage of non-failure source lines in dump_stabs.cc. + +// Line size from next line +// Line size from function end +// Line size from next function start +// line size from cu end +// line size from next cu start +// fallback size is something plausible + +// function size from function end +// function size from next function start +// function size from cu end +// function size from next cu start +// fallback size is something plausible + +// omitting functions outside the compilation unit's address range +// zero-line, one-line, many-line functions diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.cc 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -27,126 +27,49 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include -#include -#include -#include -#include +// Restructured in 2009 by: Jim Blandy + +// dump_symbols.cc: implement google_breakpad::WriteSymbolFile: +// Find all the debugging info in a file and dump it as a Breakpad symbol file. + #include -#include #include #include #include -#include #include -#include #include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/dwarf2diehandler.h" +#include "common/linux/dump_stabs.h" #include "common/linux/dump_symbols.h" +#include "common/linux/dwarf_cfi_to_module.h" +#include "common/linux/dwarf_cu_to_module.h" +#include "common/linux/dwarf_line_to_module.h" #include "common/linux/file_id.h" -#include "common/linux/guid_creator.h" -#include "processor/scoped_ptr.h" +#include "common/linux/module.h" +#include "common/linux/stabs_reader.h" // This namespace contains helper functions. namespace { -// Infomation of a line. -struct LineInfo { - // The index into string table for the name of the source file which - // this line belongs to. - // Load from stab symbol. - uint32_t source_name_index; - // Offset from start of the function. - // Load from stab symbol. - ElfW(Off) rva_to_func; - // Offset from base of the loading binary. - ElfW(Off) rva_to_base; - // Size of the line. - // It is the difference of the starting address of the line and starting - // address of the next N_SLINE, N_FUN or N_SO. - uint32_t size; - // Line number. - uint32_t line_num; - // Id of the source file for this line. - int source_id; -}; - -typedef std::list LineInfoList; - -// Information of a function. -struct FuncInfo { - // Name of the function. - const char *name; - // Offset from the base of the loading address. - ElfW(Off) rva_to_base; - // Virtual address of the function. - // Load from stab symbol. - ElfW(Addr) addr; - // Size of the function. - // It is the difference of the starting address of the function and starting - // address of the next N_FUN or N_SO. - uint32_t size; - // Total size of stack parameters. - uint32_t stack_param_size; - // Is there any lines included from other files? - bool has_sol; - // Line information array. - LineInfoList line_info; -}; - -typedef std::list FuncInfoList; - -// Information of a source file. -struct SourceFileInfo { - // Name string index into the string table. - uint32_t name_index; - // Name of the source file. - const char *name; - // Starting address of the source file. - ElfW(Addr) addr; - // Id of the source file. - int source_id; - // Functions information. - FuncInfoList func_info; -}; - -typedef std::list SourceFileInfoList; - -// Information of a symbol table. -// This is the root of all types of symbol. -struct SymbolInfo { - SourceFileInfoList source_file_info; - - // The next source id for newly found source file. - int next_source_id; -}; - -// Stab section name. -static const char *kStabName = ".stab"; - -// Demangle using abi call. -// Older GCC may not support it. -static std::string Demangle(const char *mangled) { - int status = 0; - char *demangled = abi::__cxa_demangle(mangled, NULL, NULL, &status); - if (status == 0 && demangled != NULL) { - std::string str(demangled); - free(demangled); - return str; - } - return std::string(mangled); -} +using google_breakpad::DumpStabsHandler; +using google_breakpad::DwarfCFIToModule; +using google_breakpad::DwarfCUToModule; +using google_breakpad::DwarfLineToModule; +using google_breakpad::Module; // Fix offset into virtual address by adding the mapped base into offsets. // Make life easier when want to find something by offset. static void FixAddress(void *obj_base) { - ElfW(Word) base = reinterpret_cast(obj_base); + ElfW(Addr) base = reinterpret_cast(obj_base); ElfW(Ehdr) *elf_header = static_cast(obj_base); elf_header->e_phoff += base; elf_header->e_shoff += base; @@ -169,25 +92,13 @@ return 0; } -static bool WriteFormat(int fd, const char *fmt, ...) { - va_list list; - char buffer[4096]; - ssize_t expected, written; - va_start(list, fmt); - vsnprintf(buffer, sizeof(buffer), fmt, list); - expected = strlen(buffer); - written = write(fd, buffer, strlen(buffer)); - va_end(list); - return expected == written; -} - static bool IsValidElf(const ElfW(Ehdr) *elf_header) { return memcmp(elf_header, ELFMAG, SELFMAG) == 0; } static const ElfW(Shdr) *FindSectionByName(const char *name, const ElfW(Shdr) *sections, - const ElfW(Shdr) *strtab, + const ElfW(Shdr) *section_names, int nsection) { assert(name != NULL); assert(sections != NULL); @@ -197,502 +108,321 @@ if (name_len == 0) return NULL; + // Find the end of the section name section, to make sure that + // comparisons don't run off the end of the section. + const char *names_end = + reinterpret_cast(section_names->sh_offset + section_names->sh_size); + for (int i = 0; i < nsection; ++i) { const char *section_name = - (char*)(strtab->sh_offset + sections[i].sh_name); - if (!strncmp(name, section_name, name_len)) + reinterpret_cast(section_names->sh_offset + sections[i].sh_name); + if (names_end - section_name >= name_len + 1 && + strcmp(name, section_name) == 0) return sections + i; } return NULL; } -// TODO(liuli): Computer the stack parameter size. -// Expect parameter variables are immediately following the N_FUN symbol. -// Will need to parse the type information to get a correct size. -static int LoadStackParamSize(struct nlist *list, - struct nlist *list_end, - struct FuncInfo *func_info) { - struct nlist *cur_list = list; - assert(cur_list->n_type == N_FUN); - ++cur_list; - int step = 1; - while (cur_list < list_end && cur_list->n_type == N_PSYM) { - ++cur_list; - ++step; - } - func_info->stack_param_size = 0; - return step; -} - -static int LoadLineInfo(struct nlist *list, - struct nlist *list_end, - const struct SourceFileInfo &source_file_info, - struct FuncInfo *func_info) { - struct nlist *cur_list = list; - func_info->has_sol = false; - // Records which source file the following lines belongs. Default - // to the file we are handling. This helps us handling inlined source. - // When encountering N_SOL, we will change this to the source file - // specified by N_SOL. - int current_source_name_index = source_file_info.name_index; - do { - // Skip non line information. - while (cur_list < list_end && cur_list->n_type != N_SLINE) { - // Only exit when got another function, or source file. - if (cur_list->n_type == N_FUN || cur_list->n_type == N_SO) - return cur_list - list; - // N_SOL means source lines following it will be from - // another source file. - if (cur_list->n_type == N_SOL) { - func_info->has_sol = true; - - if (cur_list->n_un.n_strx > 0 && - cur_list->n_un.n_strx != current_source_name_index) { - // The following lines will be from this source file. - current_source_name_index = cur_list->n_un.n_strx; - } - } - ++cur_list; - } - struct LineInfo line; - while (cur_list < list_end && cur_list->n_type == N_SLINE) { - line.source_name_index = current_source_name_index; - line.rva_to_func = cur_list->n_value; - // n_desc is a signed short - line.line_num = (unsigned short)cur_list->n_desc; - // Don't set it here. - // Will be processed in later pass. - line.source_id = -1; - func_info->line_info.push_back(line); - ++cur_list; - } - } while (list < list_end); - - return cur_list - list; +static bool LoadStabs(const ElfW(Shdr) *stab_section, + const ElfW(Shdr) *stabstr_section, + Module *module) { + // A callback object to handle data from the STABS reader. + DumpStabsHandler handler(module); + // Find the addresses of the STABS data, and create a STABS reader object. + uint8_t *stabs = reinterpret_cast(stab_section->sh_offset); + uint8_t *stabstr = reinterpret_cast(stabstr_section->sh_offset); + google_breakpad::StabsReader reader(stabs, stab_section->sh_size, + stabstr, stabstr_section->sh_size, + &handler); + // Read the STABS data, and do post-processing. + if (!reader.Process()) + return false; + handler.Finalize(); + return true; } -static int LoadFuncSymbols(struct nlist *list, - struct nlist *list_end, - const ElfW(Shdr) *stabstr_section, - struct SourceFileInfo *source_file_info) { - struct nlist *cur_list = list; - assert(cur_list->n_type == N_SO); - ++cur_list; - source_file_info->func_info.clear(); - while (cur_list < list_end) { - // Go until the function symbol. - while (cur_list < list_end && cur_list->n_type != N_FUN) { - if (cur_list->n_type == N_SO) { - return cur_list - list; - } - ++cur_list; - continue; - } - if (cur_list->n_type == N_FUN) { - struct FuncInfo func_info; - func_info.name = - reinterpret_cast(cur_list->n_un.n_strx + - stabstr_section->sh_offset); - func_info.addr = cur_list->n_value; - func_info.rva_to_base = 0; - func_info.size = 0; - func_info.stack_param_size = 0; - func_info.has_sol = 0; - - // Stack parameter size. - cur_list += LoadStackParamSize(cur_list, list_end, &func_info); - // Line info. - cur_list += LoadLineInfo(cur_list, - list_end, - *source_file_info, - &func_info); - - // Functions in this module should have address bigger than the module - // startring address. - // There maybe a lot of duplicated entry for a function in the symbol, - // only one of them can met this. - if (func_info.addr >= source_file_info->addr) { - source_file_info->func_info.push_back(func_info); - } - } +// A line-to-module loader that accepts line number info parsed by +// dwarf2reader::LineInfo and populates a Module and a line vector +// with the results. +class DumperLineToModule: public DwarfCUToModule::LineToModuleFunctor { + public: + // Create a line-to-module converter using BYTE_READER. + DumperLineToModule(dwarf2reader::ByteReader *byte_reader) + : byte_reader_(byte_reader) { } + void operator()(const char *program, uint64 length, + Module *module, vector *lines) { + DwarfLineToModule handler(module, lines); + dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler); + parser.Start(); } - return cur_list - list; -} + private: + dwarf2reader::ByteReader *byte_reader_; +}; -// Comapre the address. -// The argument should have a memeber named "addr" -template -static bool CompareAddress(T1 *a, T2 *b) { - return a->addr < b->addr; -} - -// Sort the array into increasing ordered array based on the virtual address. -// Return vector of pointers to the elements in the incoming array. So caller -// should make sure the returned vector lives longer than the incoming vector. -template -static std::vector SortByAddress( - Container *container) { - typedef typename Container::iterator It; - typedef typename Container::value_type T; - std::vector sorted_array_ptr; - sorted_array_ptr.reserve(container->size()); - for (It it = container->begin(); it != container->end(); it++) - sorted_array_ptr.push_back(&(*it)); - std::sort(sorted_array_ptr.begin(), - sorted_array_ptr.end(), - std::ptr_fun(CompareAddress)); - - return sorted_array_ptr; -} - -// Find the address of the next function or source file symbol in the symbol -// table. The address should be bigger than the current function's address. -static ElfW(Addr) NextAddress( - std::vector *sorted_functions, - std::vector *sorted_files, - const struct FuncInfo &func_info) { - std::vector::iterator next_func_iter = - std::find_if(sorted_functions->begin(), - sorted_functions->end(), - std::bind1st( - std::ptr_fun( - CompareAddress - ), - &func_info) - ); - if (next_func_iter != sorted_functions->end()) - return (*next_func_iter)->addr; - - std::vector::iterator next_file_iter = - std::find_if(sorted_files->begin(), - sorted_files->end(), - std::bind1st( - std::ptr_fun( - CompareAddress - ), - &func_info) - ); - if (next_file_iter != sorted_files->end()) { - return (*next_file_iter)->addr; +static bool LoadDwarf(const string &dwarf_filename, + const ElfW(Ehdr) *elf_header, + Module *module) { + // Figure out what endianness this file is. + dwarf2reader::Endianness endianness; + if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) + endianness = dwarf2reader::ENDIANNESS_LITTLE; + else if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) + endianness = dwarf2reader::ENDIANNESS_BIG; + else { + fprintf(stderr, "bad data encoding in ELF header: %d\n", + elf_header->e_ident[EI_DATA]); + return false; } - return 0; + dwarf2reader::ByteReader byte_reader(endianness); + + // Construct a context for this file. + DwarfCUToModule::FileContext file_context(dwarf_filename, module); + + // Build a map of the ELF file's sections. + const ElfW(Shdr) *sections + = reinterpret_cast(elf_header->e_shoff); + int num_sections = elf_header->e_shnum; + const ElfW(Shdr) *section_names = sections + elf_header->e_shstrndx; + for (int i = 0; i < num_sections; i++) { + const ElfW(Shdr) *section = §ions[i]; + string name = reinterpret_cast(section_names->sh_offset + + section->sh_name); + const char *contents = reinterpret_cast(section->sh_offset); + uint64 length = section->sh_size; + file_context.section_map[name] = std::make_pair(contents, length); + } + + // Parse all the compilation units in the .debug_info section. + DumperLineToModule line_to_module(&byte_reader); + std::pair debug_info_section + = file_context.section_map[".debug_info"]; + // We should never have been called if the file doesn't have a + // .debug_info section. + assert(debug_info_section.first); + uint64 debug_info_length = debug_info_section.second; + for (uint64 offset = 0; offset < debug_info_length;) { + // Make a handler for the root DIE that populates MODULE with the + // data we find. + DwarfCUToModule::WarningReporter reporter(dwarf_filename, offset); + DwarfCUToModule root_handler(&file_context, &line_to_module, &reporter); + // Make a Dwarf2Handler that drives our DIEHandler. + dwarf2reader::DIEDispatcher die_dispatcher(&root_handler); + // Make a DWARF parser for the compilation unit at OFFSET. + dwarf2reader::CompilationUnit reader(file_context.section_map, + offset, + &byte_reader, + &die_dispatcher); + // Process the entire compilation unit; get the offset of the next. + offset += reader.Start(); + } + return true; } -static int FindFileByNameIdx(uint32_t name_index, - SourceFileInfoList &files) { - for (SourceFileInfoList::iterator it = files.begin(); - it != files.end(); it++) { - if (it->name_index == name_index) - return it->source_id; - } - - return -1; -} - -// Add included file information. -// Also fix the source id for the line info. -static void AddIncludedFiles(struct SymbolInfo *symbols, - const ElfW(Shdr) *stabstr_section) { - for (SourceFileInfoList::iterator source_file_it = - symbols->source_file_info.begin(); - source_file_it != symbols->source_file_info.end(); - ++source_file_it) { - struct SourceFileInfo &source_file = *source_file_it; - - for (FuncInfoList::iterator func_info_it = source_file.func_info.begin(); - func_info_it != source_file.func_info.end(); - ++func_info_it) { - struct FuncInfo &func_info = *func_info_it; - - for (LineInfoList::iterator line_info_it = func_info.line_info.begin(); - line_info_it != func_info.line_info.end(); ++line_info_it) { - struct LineInfo &line_info = *line_info_it; - - assert(line_info.source_name_index > 0); - assert(source_file.name_index > 0); - - // Check if the line belongs to the source file by comparing the - // name index into string table. - if (line_info.source_name_index != source_file.name_index) { - // This line is not from the current source file, check if this - // source file has been added before. - int found_source_id = FindFileByNameIdx(line_info.source_name_index, - symbols->source_file_info); - if (found_source_id < 0) { - // Got a new included file. - // Those included files don't have address or line information. - SourceFileInfo new_file; - new_file.name_index = line_info.source_name_index; - new_file.name = reinterpret_cast(new_file.name_index + - stabstr_section->sh_offset); - new_file.addr = 0; - new_file.source_id = symbols->next_source_id++; - line_info.source_id = new_file.source_id; - symbols->source_file_info.push_back(new_file); - } else { - // The file has been added. - line_info.source_id = found_source_id; - } - } else { - // The line belongs to the file. - line_info.source_id = source_file.source_id; - } - } // for each line. - } // for each function. - } // for each source file. - -} - -// Compute size and rva information based on symbols loaded from stab section. -static bool ComputeSizeAndRVA(ElfW(Addr) loading_addr, - struct SymbolInfo *symbols) { - std::vector sorted_files = - SortByAddress(&(symbols->source_file_info)); - for (size_t i = 0; i < sorted_files.size(); ++i) { - struct SourceFileInfo &source_file = *sorted_files[i]; - std::vector sorted_functions = - SortByAddress(&(source_file.func_info)); - for (size_t j = 0; j < sorted_functions.size(); ++j) { - struct FuncInfo &func_info = *sorted_functions[j]; - assert(func_info.addr >= loading_addr); - func_info.rva_to_base = func_info.addr - loading_addr; - func_info.size = 0; - ElfW(Addr) next_addr = NextAddress(&sorted_functions, - &sorted_files, - func_info); - // I've noticed functions with an address bigger than any other functions - // and source files modules, this is probably the last function in the - // module, due to limitions of Linux stab symbol, it is impossible to get - // the exact size of this kind of function, thus we give it a default - // very big value. This should be safe since this is the last function. - // But it is a ugly hack..... - // The following code can reproduce the case: - // template - // void Foo(T value) { - // } - // - // int main(void) { - // Foo(10); - // Foo(std::string("hello")); - // return 0; - // } - // TODO(liuli): Find a better solution. - static const int kDefaultSize = 0x10000000; - static int no_next_addr_count = 0; - if (next_addr != 0) { - func_info.size = next_addr - func_info.addr; - } else { - if (no_next_addr_count > 1) { - fprintf(stderr, "Got more than one funtion without the \ - following symbol. Igore this function.\n"); - fprintf(stderr, "The dumped symbol may not correct.\n"); - assert(!"This should not happen!\n"); - func_info.size = 0; - continue; - } - - no_next_addr_count++; - func_info.size = kDefaultSize; - } - // Compute line size. - for (LineInfoList::iterator line_info_it = func_info.line_info.begin(); - line_info_it != func_info.line_info.end(); line_info_it++) { - struct LineInfo &line_info = *line_info_it; - LineInfoList::iterator next_line_info_it = line_info_it; - next_line_info_it++; - line_info.size = 0; - if (next_line_info_it != func_info.line_info.end()) { - line_info.size = - next_line_info_it->rva_to_func - line_info.rva_to_func; - } else { - // The last line in the function. - // If we can find a function or source file symbol immediately - // following the line, we can get the size of the line by computing - // the difference of the next address to the starting address of this - // line. - // Otherwise, we need to set a default big enough value. This occurs - // mostly because the this function is the last one in the module. - if (next_addr != 0) { - ElfW(Off) next_addr_offset = next_addr - func_info.addr; - line_info.size = next_addr_offset - line_info.rva_to_func; - } else { - line_info.size = kDefaultSize; - } - } - line_info.rva_to_base = line_info.rva_to_func + func_info.rva_to_base; - } // for each line. - } // for each function. - } // for each source file. +// Fill REGISTER_NAMES with the register names appropriate to the +// machine architecture given in HEADER, indexed by the register +// numbers used in DWARF call frame information. Return true on +// success, or false if we don't recognize HEADER's machine +// architecture. +static bool DwarfCFIRegisterNames(const ElfW(Ehdr) *elf_header, + vector *register_names) +{ + static const char *const i386_names[] = { + "$eax", "$ecx", "$edx", "$ebx", "$esp", "$ebp", "$esi", "$edi", + "$eip", "$eflags", "$unused1", + "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", + "$unused2", "$unused3", + "$xmm0", "$xmm1", "$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", + "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", + "$fcw", "$fsw", "$mxcsr", + "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused4", "$unused5", + "$tr", "$ldtr", + NULL + }; + + static const char *const x86_64_names[] = { + "$rax", "$rdx", "$rcx", "$rbx", "$rsi", "$rdi", "$rbp", "$rsp", + "$r8", "$r9", "$r10", "$r11", "$r12", "$r13", "$r14", "$r15", + "$rip", + "$xmm0","$xmm1","$xmm2", "$xmm3", "$xmm4", "$xmm5", "$xmm6", "$xmm7", + "$xmm8","$xmm9","$xmm10","$xmm11","$xmm12","$xmm13","$xmm14","$xmm15", + "$st0", "$st1", "$st2", "$st3", "$st4", "$st5", "$st6", "$st7", + "$mm0", "$mm1", "$mm2", "$mm3", "$mm4", "$mm5", "$mm6", "$mm7", + "$rflags", + "$es", "$cs", "$ss", "$ds", "$fs", "$gs", "$unused1", "$unused2", + "$fs.base", "$gs.base", "$unused3", "$unused4", + "$tr", "$ldtr", + "$mxcsr", "$fcw", "$fsw", + NULL + }; + + static const char *const arm_names[] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "fps", "cpsr", + NULL + }; + + const char * const *name_table; + switch (elf_header->e_machine) { + case EM_386: name_table = i386_names; break; + case EM_ARM: name_table = arm_names; break; + case EM_X86_64: name_table = x86_64_names; break; + default: + return false; + } + + register_names->clear(); + for (int i = 0; name_table[i]; i++) + register_names->push_back(name_table[i]); return true; } -static bool LoadSymbols(const ElfW(Shdr) *stab_section, - const ElfW(Shdr) *stabstr_section, - ElfW(Addr) loading_addr, - struct SymbolInfo *symbols) { - if (stab_section == NULL || stabstr_section == NULL) - return false; - - struct nlist *lists = - reinterpret_cast(stab_section->sh_offset); - int nstab = stab_section->sh_size / sizeof(struct nlist); - // First pass, load all symbols from the object file. - for (int i = 0; i < nstab; ) { - int step = 1; - struct nlist *cur_list = lists + i; - if (cur_list->n_type == N_SO) { - // FUNC
    - struct SourceFileInfo source_file_info; - source_file_info.name_index = cur_list->n_un.n_strx; - source_file_info.name = reinterpret_cast(cur_list->n_un.n_strx + - stabstr_section->sh_offset); - source_file_info.addr = cur_list->n_value; - if (strchr(source_file_info.name, '.')) - source_file_info.source_id = symbols->next_source_id++; - else - source_file_info.source_id = -1; - step = LoadFuncSymbols(cur_list, lists + nstab, - stabstr_section, &source_file_info); - symbols->source_file_info.push_back(source_file_info); - } - i += step; +static bool LoadDwarfCFI(const string &dwarf_filename, + const ElfW(Ehdr) *elf_header, + const char *section_name, + const ElfW(Shdr) *section, + bool eh_frame, + const ElfW(Shdr) *got_section, + const ElfW(Shdr) *text_section, + Module *module) { + // Find the appropriate set of register names for this file's + // architecture. + vector register_names; + if (!DwarfCFIRegisterNames(elf_header, ®ister_names)) { + fprintf(stderr, "%s: unrecognized ELF machine architecture '%d';" + " cannot convert DWARF call frame information\n", + dwarf_filename.c_str(), elf_header->e_machine); + return false; + } + + // Figure out what endianness this file is. + dwarf2reader::Endianness endianness; + if (elf_header->e_ident[EI_DATA] == ELFDATA2LSB) + endianness = dwarf2reader::ENDIANNESS_LITTLE; + else if (elf_header->e_ident[EI_DATA] == ELFDATA2MSB) + endianness = dwarf2reader::ENDIANNESS_BIG; + else { + fprintf(stderr, "%s: bad data encoding in ELF header: %d\n", + dwarf_filename.c_str(), elf_header->e_ident[EI_DATA]); + return false; } - // Second pass, compute the size of functions and lines. - if (ComputeSizeAndRVA(loading_addr, symbols)) { - // Third pass, check for included source code, especially for header files. - // Until now, we only have compiling unit information, but they can - // have code from include files, add them here. - AddIncludedFiles(symbols, stabstr_section); - return true; + // Find the call frame information and its size. + const char *cfi = reinterpret_cast(section->sh_offset); + size_t cfi_size = section->sh_size; + + // Plug together the parser, handler, and their entourages. + DwarfCFIToModule::Reporter module_reporter(dwarf_filename, section_name); + DwarfCFIToModule handler(module, register_names, &module_reporter); + dwarf2reader::ByteReader byte_reader(endianness); + // Since we're using the ElfW macro, we're not actually capable of + // processing both ELF32 and ELF64 files with the same program; that + // would take a bit more work. But this will work out well enough. + if (elf_header->e_ident[EI_CLASS] == ELFCLASS32) + byte_reader.SetAddressSize(4); + else if (elf_header->e_ident[EI_CLASS] == ELFCLASS64) + byte_reader.SetAddressSize(8); + else { + fprintf(stderr, "%s: bad file class in ELF header: %d\n", + dwarf_filename.c_str(), elf_header->e_ident[EI_CLASS]); + return false; } - return false; + // Provide the base addresses for .eh_frame encoded pointers, if + // possible. + byte_reader.SetCFIDataBase(section->sh_addr, cfi); + if (got_section) + byte_reader.SetDataBase(got_section->sh_addr); + if (text_section) + byte_reader.SetTextBase(got_section->sh_addr); + + dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename, + section_name); + dwarf2reader::CallFrameInfo parser(cfi, cfi_size, + &byte_reader, &handler, &dwarf_reporter, + eh_frame); + parser.Start(); + return true; } -static bool LoadSymbols(ElfW(Ehdr) *elf_header, struct SymbolInfo *symbols) { +static bool LoadSymbols(const std::string &obj_file, ElfW(Ehdr) *elf_header, + Module *module) { // Translate all offsets in section headers into address. FixAddress(elf_header); ElfW(Addr) loading_addr = GetLoadingAddress( reinterpret_cast(elf_header->e_phoff), elf_header->e_phnum); + module->SetLoadAddress(loading_addr); const ElfW(Shdr) *sections = - reinterpret_cast(elf_header->e_shoff); - const ElfW(Shdr) *strtab = sections + elf_header->e_shstrndx; - const ElfW(Shdr) *stab_section = - FindSectionByName(kStabName, sections, strtab, elf_header->e_shnum); - if (stab_section == NULL) { - fprintf(stderr, "Stab section not found.\n"); - return false; - } - const ElfW(Shdr) *stabstr_section = stab_section->sh_link + sections; - - // Load symbols. - return LoadSymbols(stab_section, stabstr_section, loading_addr, symbols); -} - -static bool WriteModuleInfo(int fd, - ElfW(Half) arch, - const std::string &obj_file) { - const char *arch_name = NULL; - if (arch == EM_386) - arch_name = "x86"; - else if (arch == EM_X86_64) - arch_name = "x86_64"; - else - return false; - - unsigned char identifier[16]; - google_breakpad::FileID file_id(obj_file.c_str()); - if (file_id.ElfFileIdentifier(identifier)) { - char identifier_str[40]; - file_id.ConvertIdentifierToString(identifier, - identifier_str, sizeof(identifier_str)); - char id_no_dash[40]; - int id_no_dash_len = 0; - memset(id_no_dash, 0, sizeof(id_no_dash)); - for (int i = 0; identifier_str[i] != '\0'; ++i) - if (identifier_str[i] != '-') - id_no_dash[id_no_dash_len++] = identifier_str[i]; - // Add an extra "0" by the end. - id_no_dash[id_no_dash_len++] = '0'; - std::string filename = obj_file; - size_t slash_pos = obj_file.find_last_of("/"); - if (slash_pos != std::string::npos) - filename = obj_file.substr(slash_pos + 1); - return WriteFormat(fd, "MODULE Linux %s %s %s\n", arch_name, - id_no_dash, filename.c_str()); - } - return false; -} - -static bool WriteSourceFileInfo(int fd, const struct SymbolInfo &symbols) { - for (SourceFileInfoList::const_iterator it = - symbols.source_file_info.begin(); - it != symbols.source_file_info.end(); it++) { - if (it->source_id != -1) { - const char *name = it->name; - if (!WriteFormat(fd, "FILE %d %s\n", it->source_id, name)) - return false; + reinterpret_cast(elf_header->e_shoff); + const ElfW(Shdr) *section_names = sections + elf_header->e_shstrndx; + bool found_debug_info_section = false; + + // Look for STABS debugging information, and load it if present. + const ElfW(Shdr) *stab_section + = FindSectionByName(".stab", sections, section_names, + elf_header->e_shnum); + if (stab_section) { + const ElfW(Shdr) *stabstr_section = stab_section->sh_link + sections; + if (stabstr_section) { + found_debug_info_section = true; + if (!LoadStabs(stab_section, stabstr_section, module)) + fprintf(stderr, "\".stab\" section found, but failed to load STABS" + " debugging information\n"); } } - return true; -} - -static bool WriteOneFunction(int fd, - const struct FuncInfo &func_info){ - // Discard the ending part of the name. - std::string func_name(func_info.name); - std::string::size_type last_colon = func_name.find_last_of(':'); - if (last_colon != std::string::npos) - func_name = func_name.substr(0, last_colon); - func_name = Demangle(func_name.c_str()); - - if (func_info.size <= 0) - return true; - - if (WriteFormat(fd, "FUNC %lx %lx %d %s\n", - func_info.rva_to_base, - func_info.size, - func_info.stack_param_size, - func_name.c_str())) { - for (LineInfoList::const_iterator it = func_info.line_info.begin(); - it != func_info.line_info.end(); it++) { - const struct LineInfo &line_info = *it; - if (!WriteFormat(fd, "%lx %lx %d %d\n", - line_info.rva_to_base, - line_info.size, - line_info.line_num, - line_info.source_id)) - return false; - } - return true; - } - return false; -} -static bool WriteFunctionInfo(int fd, const struct SymbolInfo &symbols) { - for (SourceFileInfoList::const_iterator it = - symbols.source_file_info.begin(); - it != symbols.source_file_info.end(); it++) { - const struct SourceFileInfo &file_info = *it; - for (FuncInfoList::const_iterator fiIt = file_info.func_info.begin(); - fiIt != file_info.func_info.end(); fiIt++) { - const struct FuncInfo &func_info = *fiIt; - if (!WriteOneFunction(fd, func_info)) - return false; - } + // Look for DWARF debugging information, and load it if present. + const ElfW(Shdr) *dwarf_section + = FindSectionByName(".debug_info", sections, section_names, + elf_header->e_shnum); + if (dwarf_section) { + found_debug_info_section = true; + if (!LoadDwarf(obj_file, elf_header, module)) + fprintf(stderr, "\".debug_info\" section found, but failed to load " + "DWARF debugging information\n"); + } + + // Dwarf Call Frame Information (CFI) is actually independent from + // the other DWARF debugging information, and can be used alone. + const ElfW(Shdr) *dwarf_cfi_section = + FindSectionByName(".debug_frame", sections, section_names, + elf_header->e_shnum); + if (dwarf_cfi_section) { + // Ignore the return value of this function; even without call frame + // information, the other debugging information could be perfectly + // useful. + LoadDwarfCFI(obj_file, elf_header, ".debug_frame", + dwarf_cfi_section, false, 0, 0, module); + } + + // Linux C++ exception handling information can also provide + // unwinding data. + const ElfW(Shdr) *eh_frame_section = + FindSectionByName(".eh_frame", sections, section_names, + elf_header->e_shnum); + if (eh_frame_section) { + // Pointers in .eh_frame data may be relative to the base addresses of + // certain sections. Provide those sections if present. + const ElfW(Shdr) *got_section = + FindSectionByName(".got", sections, section_names, elf_header->e_shnum); + const ElfW(Shdr) *text_section = + FindSectionByName(".text", sections, section_names, + elf_header->e_shnum); + // As above, ignore the return value of this function. + LoadDwarfCFI(obj_file, elf_header, ".eh_frame", + eh_frame_section, true, got_section, text_section, module); + } + + if (!found_debug_info_section) { + fprintf(stderr, "file contains no debugging information" + " (no \".stab\" or \".debug_info\" sections)\n"); + return false; } return true; } -static bool DumpStabSymbols(int fd, const struct SymbolInfo &symbols) { - return WriteSourceFileInfo(fd, symbols) && - WriteFunctionInfo(fd, symbols); -} - // // FDWrapper // @@ -745,38 +475,111 @@ size_t size_; }; +// Return the breakpad symbol file identifier for the architecture of +// ELF_HEADER. +const char *ElfArchitecture(const ElfW(Ehdr) *elf_header) { + ElfW(Half) arch = elf_header->e_machine; + switch (arch) { + case EM_386: return "x86"; + case EM_ARM: return "arm"; + case EM_MIPS: return "mips"; + case EM_PPC64: return "ppc64"; + case EM_PPC: return "ppc"; + case EM_S390: return "s390"; + case EM_SPARC: return "sparc"; + case EM_SPARCV9: return "sparcv9"; + case EM_X86_64: return "x86_64"; + default: return NULL; + } +} + +// Format the Elf file identifier in IDENTIFIER as a UUID with the +// dashes removed. +std::string FormatIdentifier(unsigned char identifier[16]) { + char identifier_str[40]; + google_breakpad::FileID::ConvertIdentifierToString( + identifier, + identifier_str, + sizeof(identifier_str)); + std::string id_no_dash; + for (int i = 0; identifier_str[i] != '\0'; ++i) + if (identifier_str[i] != '-') + id_no_dash += identifier_str[i]; + // Add an extra "0" by the end. PDB files on Windows have an 'age' + // number appended to the end of the file identifier; this isn't + // really used or necessary on other platforms, but let's preserve + // the pattern. + id_no_dash += '0'; + return id_no_dash; +} + +// Return the non-directory portion of FILENAME: the portion after the +// last slash, or the whole filename if there are no slashes. +std::string BaseFileName(const std::string &filename) { + // Lots of copies! basename's behavior is less than ideal. + char *c_filename = strdup(filename.c_str()); + std::string base = basename(c_filename); + free(c_filename); + return base; +} + } // namespace namespace google_breakpad { -bool DumpSymbols::WriteSymbolFile(const std::string &obj_file, - int sym_fd) { +bool WriteSymbolFile(const std::string &obj_file, FILE *sym_file) { int obj_fd = open(obj_file.c_str(), O_RDONLY); - if (obj_fd < 0) + if (obj_fd < 0) { + fprintf(stderr, "Failed to open ELF file '%s': %s\n", + obj_file.c_str(), strerror(errno)); return false; + } FDWrapper obj_fd_wrapper(obj_fd); struct stat st; - if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) + if (fstat(obj_fd, &st) != 0 && st.st_size <= 0) { + fprintf(stderr, "Unable to fstat ELF file '%s': %s\n", + obj_file.c_str(), strerror(errno)); return false; + } void *obj_base = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, obj_fd, 0); - if (!obj_base) + if (obj_base == MAP_FAILED) { + fprintf(stderr, "Failed to mmap ELF file '%s': %s\n", + obj_file.c_str(), strerror(errno)); return false; + } MmapWrapper map_wrapper(obj_base, st.st_size); ElfW(Ehdr) *elf_header = reinterpret_cast(obj_base); - if (!IsValidElf(elf_header)) + if (!IsValidElf(elf_header)) { + fprintf(stderr, "Not a valid ELF file: %s\n", obj_file.c_str()); + return false; + } + + unsigned char identifier[16]; + google_breakpad::FileID file_id(obj_file.c_str()); + if (!file_id.ElfFileIdentifier(identifier)) { + fprintf(stderr, "Unable to generate file identifier\n"); + return false; + } + + const char *architecture = ElfArchitecture(elf_header); + if (!architecture) { + fprintf(stderr, "Unrecognized ELF machine architecture: %d\n", + elf_header->e_machine); return false; - struct SymbolInfo symbols; - symbols.next_source_id = 0; + } - if (!LoadSymbols(elf_header, &symbols)) - return false; - // Write to symbol file. - if (WriteModuleInfo(sym_fd, elf_header->e_machine, obj_file) && - DumpStabSymbols(sym_fd, symbols)) - return true; + std::string name = BaseFileName(obj_file); + std::string os = "Linux"; + std::string id = FormatIdentifier(identifier); - return false; + Module module(name, os, architecture, id); + if (!LoadSymbols(obj_file, elf_header, &module)) + return false; + if (!module.Write(sym_file)) + return false; + + return true; } } // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dump_symbols.h 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,6 @@ -// Copyright (c) 2006, Google Inc. +// -*- mode: c++ -*- + +// Copyright (c) 2010, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -26,9 +28,9 @@ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// -// dump_symbols.cc: Implements a linux stab debugging format dumper. -// + +// dump_symbols.h: Read debugging information from an ELF file, and write +// it out as a Breakpad symbol file. #ifndef COMMON_LINUX_DUMP_SYMBOLS_H__ #define COMMON_LINUX_DUMP_SYMBOLS_H__ @@ -38,11 +40,10 @@ namespace google_breakpad { -class DumpSymbols { - public: - bool WriteSymbolFile(const std::string &obj_file, - int sym_fd); -}; +// Find all the debugging information in OBJ_FILE, an ELF executable +// or shared library, and write it to SYM_FILE in the Breakpad symbol +// file format. +bool WriteSymbolFile(const std::string &obj_file, FILE *sym_file); } // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cfi_to_module.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cfi_to_module.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cfi_to_module.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cfi_to_module.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,185 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// Implementation of google_breakpad::DwarfCFIToModule. +// See dwarf_cfi_to_module.h for details. + +#include + +#include "common/linux/dwarf_cfi_to_module.h" + +namespace google_breakpad { + +using std::ostringstream; + +bool DwarfCFIToModule::Entry(size_t offset, uint64 address, uint64 length, + uint8 version, const string &augmentation, + unsigned return_address) { + assert(!entry_); + + // If dwarf2reader::CallFrameInfo can handle this version and + // augmentation, then we should be okay with that, so there's no + // need to check them here. + + // Get ready to collect entries. + entry_ = new Module::StackFrameEntry; + entry_->address = address; + entry_->size = length; + entry_offset_ = offset; + return_address_ = return_address; + + // Breakpad STACK CFI records must provide a .ra rule, but DWARF CFI + // may not establish any rule for .ra if the return address column + // is an ordinary register, and that register holds the return + // address on entry to the function. So establish an initial .ra + // rule citing the return address register. + if (return_address_ < register_names_.size()) + entry_->initial_rules[".ra"] = register_names_[return_address_]; + + return true; +} + +string DwarfCFIToModule::RegisterName(int i) { + assert(entry_); + if (i < 0) { + assert(i == kCFARegister); + return ".cfa"; + } + unsigned reg = i; + if (reg == return_address_) + return ".ra"; + + if (0 <= reg && reg < register_names_.size()) + return register_names_[reg]; + + reporter_->UnnamedRegister(entry_offset_, reg); + char buf[30]; + sprintf(buf, "unnamed_register%u", reg); + return buf; +} + +void DwarfCFIToModule::Record(Module::Address address, int reg, + const string &rule) { + assert(entry_); + // Is this one of this entry's initial rules? + if (address == entry_->address) + entry_->initial_rules[RegisterName(reg)] = rule; + // File it under the appropriate address. + else + entry_->rule_changes[address][RegisterName(reg)] = rule; +} + +bool DwarfCFIToModule::UndefinedRule(uint64 address, int reg) { + reporter_->UndefinedNotSupported(entry_offset_, RegisterName(reg)); + // Treat this as a non-fatal error. + return true; +} + +bool DwarfCFIToModule::SameValueRule(uint64 address, int reg) { + ostringstream s; + s << RegisterName(reg); + Record(address, reg, s.str()); + return true; +} + +bool DwarfCFIToModule::OffsetRule(uint64 address, int reg, + int base_register, long offset) { + ostringstream s; + s << RegisterName(base_register) << " " << offset << " + ^"; + Record(address, reg, s.str()); + return true; +} + +bool DwarfCFIToModule::ValOffsetRule(uint64 address, int reg, + int base_register, long offset) { + ostringstream s; + s << RegisterName(base_register) << " " << offset << " +"; + Record(address, reg, s.str()); + return true; +} + +bool DwarfCFIToModule::RegisterRule(uint64 address, int reg, + int base_register) { + ostringstream s; + s << RegisterName(base_register); + Record(address, reg, s.str()); + return true; +} + +bool DwarfCFIToModule::ExpressionRule(uint64 address, int reg, + const string &expression) { + reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg)); + // Treat this as a non-fatal error. + return true; +} + +bool DwarfCFIToModule::ValExpressionRule(uint64 address, int reg, + const string &expression) { + reporter_->ExpressionsNotSupported(entry_offset_, RegisterName(reg)); + // Treat this as a non-fatal error. + return true; +} + +bool DwarfCFIToModule::End() { + module_->AddStackFrameEntry(entry_); + entry_ = NULL; + return true; +} + +void DwarfCFIToModule::Reporter::UnnamedRegister(size_t offset, int reg) { + fprintf(stderr, "%s, section '%s': " + "the call frame entry at offset 0x%zx refers to register %d," + " whose name we don't know\n", + file_.c_str(), section_.c_str(), offset, reg); +} + +void DwarfCFIToModule::Reporter::UndefinedNotSupported(size_t offset, + const string ®) { + fprintf(stderr, "%s, section '%s': " + "the call frame entry at offset 0x%zx sets the rule for " + "register '%s' to 'undefined', but the Breakpad symbol file format" + " cannot express this\n", + file_.c_str(), section_.c_str(), offset, reg.c_str()); +} + +void DwarfCFIToModule::Reporter::ExpressionsNotSupported(size_t offset, + const string ®) { + fprintf(stderr, "%s, section '%s': " + "the call frame entry at offset 0x%zx uses a DWARF expression to" + " describe how to recover register '%s', " + " but this translator cannot yet translate DWARF expressions to" + " Breakpad postfix expressions\n", + file_.c_str(), section_.c_str(), offset, reg.c_str()); +} + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cfi_to_module.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cfi_to_module.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cfi_to_module.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cfi_to_module.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,154 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// dwarf_cfi_to_module.h: Define the DwarfCFIToModule class, which +// accepts parsed DWARF call frame info and adds it to a +// google_breakpad::Module object, which can write that information to +// a Breakpad symbol file. + +#ifndef COMMON_LINUX_DWARF_CFI_TO_MODULE_H +#define COMMON_LINUX_DWARF_CFI_TO_MODULE_H + +#include +#include +#include + +#include "common/linux/module.h" +#include "common/dwarf/dwarf2reader.h" + +namespace google_breakpad { + +using dwarf2reader::CallFrameInfo; +using google_breakpad::Module; +using std::string; +using std::vector; + +// A class that accepts parsed call frame information from the DWARF +// CFI parser and populates a google_breakpad::Module object with the +// contents. +class DwarfCFIToModule: public CallFrameInfo::Handler { + public: + + // DwarfCFIToModule uses an instance of this class to report errors + // detected while converting DWARF CFI to Breakpad STACK CFI records. + class Reporter { + public: + // Create a reporter that writes messages to the standard error + // stream. FILE is the name of the file we're processing, and + // SECTION is the name of the section within that file that we're + // looking at (.debug_frame, .eh_frame, etc.). + Reporter(const string &file, const string §ion) + : file_(file), section_(section) { } + virtual ~Reporter() { } + + // The DWARF CFI entry at OFFSET cites register REG, but REG is not + // covered by the vector of register names passed to the + // DwarfCFIToModule constructor, nor does it match the return + // address column number for this entry. + virtual void UnnamedRegister(size_t offset, int reg); + + // The DWARF CFI entry at OFFSET says that REG is undefined, but the + // Breakpad symbol file format cannot express this. + virtual void UndefinedNotSupported(size_t offset, const string ®); + + // The DWARF CFI entry at OFFSET says that REG uses a DWARF + // expression to find its value, but DwarfCFIToModule is not + // capable of translating DWARF expressions to Breakpad postfix + // expressions. + virtual void ExpressionsNotSupported(size_t offset, const string ®); + + protected: + string file_, section_; + }; + + // Create a handler for the dwarf2reader::CallFrameInfo parser that + // records the stack unwinding information it receives in MODULE. + // + // Use REGISTER_NAMES[I] as the name of register number I; *this + // keeps a reference to the vector, so the vector should remain + // alive for as long as the DwarfCFIToModule does. + // + // Use REPORTER for reporting problems encountered in the conversion + // process. + DwarfCFIToModule(Module *module, const vector ®ister_names, + Reporter *reporter) + : module_(module), register_names_(register_names), reporter_(reporter), + entry_(NULL), return_address_(-1) { } + virtual ~DwarfCFIToModule() { delete entry_; } + + virtual bool Entry(size_t offset, uint64 address, uint64 length, + uint8 version, const string &augmentation, + unsigned return_address); + virtual bool UndefinedRule(uint64 address, int reg); + virtual bool SameValueRule(uint64 address, int reg); + virtual bool OffsetRule(uint64 address, int reg, + int base_register, long offset); + virtual bool ValOffsetRule(uint64 address, int reg, + int base_register, long offset); + virtual bool RegisterRule(uint64 address, int reg, int base_register); + virtual bool ExpressionRule(uint64 address, int reg, + const string &expression); + virtual bool ValExpressionRule(uint64 address, int reg, + const string &expression); + virtual bool End(); + + private: + // Return the name to use for register REG. + string RegisterName(int i); + + // Record RULE for register REG at ADDRESS. + void Record(Module::Address address, int reg, const string &rule); + + // The module to which we should add entries. + Module *module_; + + // Map from register numbers to register names. + const vector ®ister_names_; + + // The reporter to use to report problems. + Reporter *reporter_; + + // The current entry we're constructing. + Module::StackFrameEntry *entry_; + + // The section offset of the current frame description entry, for + // use in error messages. + size_t entry_offset_; + + // The return address column for that entry. + unsigned return_address_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DWARF_CFI_TO_MODULE_H diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cfi_to_module_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cfi_to_module_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cfi_to_module_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cfi_to_module_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,260 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// dwarf_cfi_to_module_unittest.cc: Tests for google_breakpad::DwarfCFIToModule. + +#include "breakpad_googletest_includes.h" +#include "common/linux/dwarf_cfi_to_module.h" + +using google_breakpad::Module; +using google_breakpad::DwarfCFIToModule; +using testing::ContainerEq; +using testing::Test; +using testing::_; + +struct MockCFIReporter: public DwarfCFIToModule::Reporter { + MockCFIReporter(const string &file, const string §ion) + : Reporter(file, section) { } + MOCK_METHOD2(UnnamedRegister, void(size_t offset, int reg)); + MOCK_METHOD2(UndefinedNotSupported, void(size_t offset, const string ®)); + MOCK_METHOD2(ExpressionsNotSupported, void(size_t offset, const string ®)); +}; + +struct DwarfCFIToModuleFixture { + DwarfCFIToModuleFixture() + : module("module name", "module os", "module arch", "module id"), + reporter("reporter file", "reporter section"), + handler(&module, register_names, &reporter) { + register_names.push_back("reg0"); + register_names.push_back("reg1"); + register_names.push_back("reg2"); + register_names.push_back("reg3"); + register_names.push_back("reg4"); + register_names.push_back("reg5"); + register_names.push_back("reg6"); + register_names.push_back("reg7"); + register_names.push_back("sp"); + register_names.push_back("pc"); + + EXPECT_CALL(reporter, UnnamedRegister(_, _)).Times(0); + EXPECT_CALL(reporter, UndefinedNotSupported(_, _)).Times(0); + EXPECT_CALL(reporter, ExpressionsNotSupported(_, _)).Times(0); + } + + Module module; + vector register_names; + MockCFIReporter reporter; + DwarfCFIToModule handler; + vector entries; +}; + +class Entry: public DwarfCFIToModuleFixture, public Test { }; + +TEST_F(Entry, Accept) { + ASSERT_TRUE(handler.Entry(0x3b8961b8, 0xa21069698096fc98ULL, + 0xb440ce248169c8d6ULL, 3, "", 0xea93c106)); + ASSERT_TRUE(handler.End()); + module.GetStackFrameEntries(&entries); + EXPECT_EQ(1U, entries.size()); + EXPECT_EQ(0xa21069698096fc98ULL, entries[0]->address); + EXPECT_EQ(0xb440ce248169c8d6ULL, entries[0]->size); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Entry, AcceptOldVersion) { + ASSERT_TRUE(handler.Entry(0xeb60e0fc, 0x75b8806bb09eab78ULL, + 0xc771f44958d40bbcULL, 1, "", 0x093c945e)); + ASSERT_TRUE(handler.End()); + module.GetStackFrameEntries(&entries); + EXPECT_EQ(1U, entries.size()); + EXPECT_EQ(0x75b8806bb09eab78ULL, entries[0]->address); + EXPECT_EQ(0xc771f44958d40bbcULL, entries[0]->size); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +struct RuleFixture: public DwarfCFIToModuleFixture { + RuleFixture() : DwarfCFIToModuleFixture() { + entry_address = 0x89327ebf86b47492ULL; + entry_size = 0x2f8cd573072fe02aULL; + return_reg = 0x7886a346; + } + void StartEntry() { + ASSERT_TRUE(handler.Entry(0x4445c05c, entry_address, entry_size, + 3, "", return_reg)); + } + void CheckEntry() { + module.GetStackFrameEntries(&entries); + EXPECT_EQ(1U, entries.size()); + EXPECT_EQ(entry_address, entries[0]->address); + EXPECT_EQ(entry_size, entries[0]->size); + } + uint64 entry_address, entry_size; + unsigned return_reg; +}; + +class Rule: public RuleFixture, public Test { }; + +TEST_F(Rule, UndefinedRule) { + EXPECT_CALL(reporter, UndefinedNotSupported(_, "reg7")); + StartEntry(); + ASSERT_TRUE(handler.UndefinedRule(entry_address, 7)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, SameValueRule) { + StartEntry(); + ASSERT_TRUE(handler.SameValueRule(entry_address, 6)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + Module::RuleMap expected_initial; + expected_initial["reg6"] = "reg6"; + EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, OffsetRule) { + StartEntry(); + ASSERT_TRUE(handler.OffsetRule(entry_address + 1, return_reg, + DwarfCFIToModule::kCFARegister, + 16927065)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + Module::RuleChangeMap expected_changes; + expected_changes[entry_address + 1][".ra"] = ".cfa 16927065 + ^"; + EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); +} + +TEST_F(Rule, OffsetRuleNegative) { + StartEntry(); + ASSERT_TRUE(handler.OffsetRule(entry_address + 1, + DwarfCFIToModule::kCFARegister, 4, -34530721)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + Module::RuleChangeMap expected_changes; + expected_changes[entry_address + 1][".cfa"] = "reg4 -34530721 + ^"; + EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); +} + +TEST_F(Rule, ValOffsetRule) { + // Use an unnamed register number, to exercise that branch of RegisterName. + EXPECT_CALL(reporter, UnnamedRegister(_, 10)); + StartEntry(); + ASSERT_TRUE(handler.ValOffsetRule(entry_address + 0x5ab7, + DwarfCFIToModule::kCFARegister, + 10, 61812979)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + Module::RuleChangeMap expected_changes; + expected_changes[entry_address + 0x5ab7][".cfa"] = + "unnamed_register10 61812979 +"; + EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); +} + +TEST_F(Rule, RegisterRule) { + StartEntry(); + ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 3)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + Module::RuleMap expected_initial; + expected_initial[".ra"] = "reg3"; + EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, ExpressionRule) { + EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg2")); + StartEntry(); + ASSERT_TRUE(handler.ExpressionRule(entry_address + 0xf326, 2, + "it takes two to tango")); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, ValExpressionRule) { + EXPECT_CALL(reporter, ExpressionsNotSupported(_, "reg0")); + StartEntry(); + ASSERT_TRUE(handler.ValExpressionRule(entry_address + 0x6367, 0, + "bit off more than he could chew")); + ASSERT_TRUE(handler.End()); + CheckEntry(); + EXPECT_EQ(0U, entries[0]->initial_rules.size()); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, DefaultReturnAddressRule) { + return_reg = 2; + StartEntry(); + ASSERT_TRUE(handler.RegisterRule(entry_address, 0, 1)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + Module::RuleMap expected_initial; + expected_initial[".ra"] = "reg2"; + expected_initial["reg0"] = "reg1"; + EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, DefaultReturnAddressRuleOverride) { + return_reg = 2; + StartEntry(); + ASSERT_TRUE(handler.RegisterRule(entry_address, return_reg, 1)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + Module::RuleMap expected_initial; + expected_initial[".ra"] = "reg1"; + EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); + EXPECT_EQ(0U, entries[0]->rule_changes.size()); +} + +TEST_F(Rule, DefaultReturnAddressRuleLater) { + return_reg = 2; + StartEntry(); + ASSERT_TRUE(handler.RegisterRule(entry_address + 1, return_reg, 1)); + ASSERT_TRUE(handler.End()); + CheckEntry(); + Module::RuleMap expected_initial; + expected_initial[".ra"] = "reg2"; + EXPECT_THAT(entries[0]->initial_rules, ContainerEq(expected_initial)); + Module::RuleChangeMap expected_changes; + expected_changes[entry_address + 1][".ra"] = "reg1"; + EXPECT_THAT(entries[0]->rule_changes, ContainerEq(expected_changes)); +} + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cu_to_module.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cu_to_module.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cu_to_module.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cu_to_module.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,877 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// Implement the DwarfCUToModule class; see dwarf_cu_to_module.h. + +#include +#include + +#include "common/linux/dwarf_cu_to_module.h" +#include "common/linux/dwarf_line_to_module.h" + +namespace google_breakpad { + +using std::map; +using std::vector; + +// Data provided by a DWARF specification DIE. +// +// In DWARF, the DIE for a definition may contain a DW_AT_specification +// attribute giving the offset of the corresponding declaration DIE, and +// the definition DIE may omit information given in the declaration. For +// example, it's common for a function's address range to appear only in +// its definition DIE, but its name to appear only in its declaration +// DIE. +// +// The dumper needs to be able to follow DW_AT_specification links to +// bring all this information together in a FUNC record. Conveniently, +// DIEs that are the target of such links have a DW_AT_declaration flag +// set, so we can identify them when we first see them, and record their +// contents for later reference. +// +// A Specification holds information gathered from a declaration DIE that +// we may need if we find a DW_AT_specification link pointing to it. +struct DwarfCUToModule::Specification { + // The name of the enclosing scope, or the empty string if there is none. + string enclosing_name; + + // The name for the specification DIE itself, without any enclosing + // name components. + string unqualified_name; +}; + +// An abstract origin -- base definition of an inline function. +struct AbstractOrigin { + AbstractOrigin() : name() {} + AbstractOrigin(const string& name) : name(name) {} + + string name; +}; + +typedef map AbstractOriginByOffset; + +// Data global to the DWARF-bearing file that is private to the +// DWARF-to-Module process. +struct DwarfCUToModule::FilePrivate { + // A map from offsets of DIEs within the .debug_info section to + // Specifications describing those DIEs. Specification references can + // cross compilation unit boundaries. + SpecificationByOffset specifications; + + AbstractOriginByOffset origins; +}; + +DwarfCUToModule::FileContext::FileContext(const string &filename_arg, + Module *module_arg) + : filename(filename_arg), module(module_arg) { + file_private = new FilePrivate(); +} + +DwarfCUToModule::FileContext::~FileContext() { + delete file_private; +} + +// Information global to the particular compilation unit we're +// parsing. This is for data shared across the CU's entire DIE tree, +// and parameters from the code invoking the CU parser. +struct DwarfCUToModule::CUContext { + CUContext(FileContext *file_context_arg, WarningReporter *reporter_arg) + : file_context(file_context_arg), + reporter(reporter_arg), + language(Language::CPlusPlus) { } + ~CUContext() { + for (vector::iterator it = functions.begin(); + it != functions.end(); it++) + delete *it; + }; + + // The DWARF-bearing file into which this CU was incorporated. + FileContext *file_context; + + // For printing error messages. + WarningReporter *reporter; + + // The source language of this compilation unit. + const Language *language; + + // The functions defined in this compilation unit. We accumulate + // them here during parsing. Then, in DwarfCUToModule::Finish, we + // assign them lines and add them to file_context->module. + // + // Destroying this destroys all the functions this vector points to. + vector functions; +}; + +// Information about the context of a particular DIE. This is for +// information that changes as we descend the tree towards the leaves: +// the containing classes/namespaces, etc. +struct DwarfCUToModule::DIEContext { + // The fully-qualified name of the context. For example, for a + // tree like: + // + // DW_TAG_namespace Foo + // DW_TAG_class Bar + // DW_TAG_subprogram Baz + // + // in a C++ compilation unit, the DIEContext's name for the + // DW_TAG_subprogram DIE would be "Foo::Bar". The DIEContext's + // name for the DW_TAG_namespace DIE would be "". + string name; +}; + +// An abstract base class for all the dumper's DIE handlers. +class DwarfCUToModule::GenericDIEHandler: public dwarf2reader::DIEHandler { + public: + // Create a handler for the DIE at OFFSET whose compilation unit is + // described by CU_CONTEXT, and whose immediate context is described + // by PARENT_CONTEXT. + GenericDIEHandler(CUContext *cu_context, DIEContext *parent_context, + uint64 offset) + : cu_context_(cu_context), + parent_context_(parent_context), + offset_(offset), + declaration_(false), + specification_(NULL) { } + + // Derived classes' ProcessAttributeUnsigned can defer to this to + // handle DW_AT_declaration, or simply not override it. + void ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + + // Derived classes' ProcessAttributeReference can defer to this to + // handle DW_AT_specification, or simply not override it. + void ProcessAttributeReference(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + + // Derived classes' ProcessAttributeReference can defer to this to + // handle DW_AT_specification, or simply not override it. + void ProcessAttributeString(enum DwarfAttribute attr, + enum DwarfForm form, + const string &data); + + protected: + // Compute and return the fully-qualified name of the DIE. If this + // DIE is a declaration DIE, to be cited by other DIEs' + // DW_AT_specification attributes, record its enclosing name and + // unqualified name in the specification table. + // + // Use this from EndAttributes member functions, not ProcessAttribute* + // functions; only the former can be sure that all the DIE's attributes + // have been seen. + string ComputeQualifiedName(); + + CUContext *cu_context_; + DIEContext *parent_context_; + uint64 offset_; + + // If this DIE has a DW_AT_declaration attribute, this is its value. + // It is false on DIEs with no DW_AT_declaration attribute. + bool declaration_; + + // If this DIE has a DW_AT_specification attribute, this is the + // Specification structure for the DIE the attribute refers to. + // Otherwise, this is NULL. + Specification *specification_; + + // The value of the DW_AT_name attribute, or the empty string if the + // DIE has no such attribute. + string name_attribute_; +}; + +void DwarfCUToModule::GenericDIEHandler::ProcessAttributeUnsigned( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + switch (attr) { + case dwarf2reader::DW_AT_declaration: declaration_ = (data != 0); break; + default: break; + } +} + +void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + switch (attr) { + case dwarf2reader::DW_AT_specification: { + // Find the Specification to which this attribute refers, and + // set specification_ appropriately. We could do more processing + // here, but it's better to leave the real work to our + // EndAttribute member function, at which point we know we have + // seen all the DIE's attributes. + FileContext *file_context = cu_context_->file_context; + SpecificationByOffset *specifications + = &file_context->file_private->specifications; + SpecificationByOffset::iterator spec = specifications->find(data); + if (spec != specifications->end()) { + specification_ = &spec->second; + } else { + // Technically, there's no reason a DW_AT_specification + // couldn't be a forward reference, but supporting that would + // be a lot of work (changing to a two-pass structure), and I + // don't think any producers we care about ever emit such + // things. + cu_context_->reporter->UnknownSpecification(offset_, data); + } + break; + } + default: break; + } +} + +void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString( + enum DwarfAttribute attr, + enum DwarfForm form, + const string &data) { + switch (attr) { + case dwarf2reader::DW_AT_name: name_attribute_ = data; break; + default: break; + } +} + +string DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() { + // Find our unqualified name. If the DIE has its own DW_AT_name + // attribute, then use that; otherwise, check our specification. + const string *unqualified_name; + if (name_attribute_.empty() && specification_) + unqualified_name = &specification_->unqualified_name; + else + unqualified_name = &name_attribute_; + + // Find the name of our enclosing context. If we have a + // specification, it's the specification's enclosing context that + // counts; otherwise, use this DIE's context. + const string *enclosing_name; + if (specification_) + enclosing_name = &specification_->enclosing_name; + else + enclosing_name = &parent_context_->name; + + // If this DIE was marked as a declaration, record its names in the + // specification table. + if (declaration_) { + FileContext *file_context = cu_context_->file_context; + Specification spec; + spec.enclosing_name = *enclosing_name; + spec.unqualified_name = *unqualified_name; + file_context->file_private->specifications[offset_] = spec; + } + + // Combine the enclosing name and unqualified name to produce our + // own fully-qualified name. + return cu_context_->language->MakeQualifiedName(*enclosing_name, + *unqualified_name); +} + +// A handler class for DW_TAG_subprogram DIEs. +class DwarfCUToModule::FuncHandler: public GenericDIEHandler { + public: + FuncHandler(CUContext *cu_context, DIEContext *parent_context, + uint64 offset) + : GenericDIEHandler(cu_context, parent_context, offset), + low_pc_(0), high_pc_(0), abstract_origin_(NULL), inline_(false) { } + void ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + void ProcessAttributeSigned(enum DwarfAttribute attr, + enum DwarfForm form, + int64 data); + void ProcessAttributeReference(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + + bool EndAttributes(); + void Finish(); + + private: + // The fully-qualified name, as derived from name_attribute_, + // specification_, parent_context_. Computed in EndAttributes. + string name_; + uint64 low_pc_, high_pc_; // DW_AT_low_pc, DW_AT_high_pc + const AbstractOrigin* abstract_origin_; + bool inline_; +}; + +void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + switch (attr) { + // If this attribute is present at all --- even if its value is + // DW_INL_not_inlined --- then GCC may cite it as someone else's + // DW_AT_abstract_origin attribute. + case dwarf2reader::DW_AT_inline: inline_ = true; break; + + case dwarf2reader::DW_AT_low_pc: low_pc_ = data; break; + case dwarf2reader::DW_AT_high_pc: high_pc_ = data; break; + default: + GenericDIEHandler::ProcessAttributeUnsigned(attr, form, data); + break; + } +} + +void DwarfCUToModule::FuncHandler::ProcessAttributeSigned( + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { + switch (attr) { + // If this attribute is present at all --- even if its value is + // DW_INL_not_inlined --- then GCC may cite it as someone else's + // DW_AT_abstract_origin attribute. + case dwarf2reader::DW_AT_inline: inline_ = true; break; + + default: + break; + } +} + +void DwarfCUToModule::FuncHandler::ProcessAttributeReference( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + switch(attr) { + case dwarf2reader::DW_AT_abstract_origin: { + const AbstractOriginByOffset& origins = + cu_context_->file_context->file_private->origins; + AbstractOriginByOffset::const_iterator origin = origins.find(data); + if (origin != origins.end()) { + abstract_origin_ = &(origin->second); + } else { + cu_context_->reporter->UnknownAbstractOrigin(offset_, data); + } + break; + } + default: + GenericDIEHandler::ProcessAttributeReference(attr, form, data); + break; + } +} + +bool DwarfCUToModule::FuncHandler::EndAttributes() { + // Compute our name, and record a specification, if appropriate. + name_ = ComputeQualifiedName(); + if (name_.empty() && abstract_origin_) { + name_ = abstract_origin_->name; + } + return true; +} + +void DwarfCUToModule::FuncHandler::Finish() { + // Did we collect the information we need? Not all DWARF function + // entries have low and high addresses (for example, inlined + // functions that were never used), but all the ones we're + // interested in cover a non-empty range of bytes. + if (low_pc_ < high_pc_) { + // Create a Module::Function based on the data we've gathered, and + // add it to the functions_ list. + Module::Function *func = new Module::Function; + func->name = name_; + func->address = low_pc_; + func->size = high_pc_ - low_pc_; + func->parameter_size = 0; + cu_context_->functions.push_back(func); + } else if (inline_) { + AbstractOrigin origin(name_); + cu_context_->file_context->file_private->origins[offset_] = origin; + } +} + +// A handler for DIEs that contain functions and contribute a +// component to their names: namespaces, classes, etc. +class DwarfCUToModule::NamedScopeHandler: public GenericDIEHandler { + public: + NamedScopeHandler(CUContext *cu_context, DIEContext *parent_context, + uint64 offset) + : GenericDIEHandler(cu_context, parent_context, offset) { } + bool EndAttributes(); + DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag, + const AttributeList &attrs); + + private: + DIEContext child_context_; // A context for our children. +}; + +bool DwarfCUToModule::NamedScopeHandler::EndAttributes() { + child_context_.name = ComputeQualifiedName(); + return true; +} + +dwarf2reader::DIEHandler *DwarfCUToModule::NamedScopeHandler::FindChildHandler( + uint64 offset, + enum DwarfTag tag, + const AttributeList &attrs) { + switch (tag) { + case dwarf2reader::DW_TAG_subprogram: + return new FuncHandler(cu_context_, &child_context_, offset); + case dwarf2reader::DW_TAG_namespace: + case dwarf2reader::DW_TAG_class_type: + case dwarf2reader::DW_TAG_structure_type: + case dwarf2reader::DW_TAG_union_type: + return new NamedScopeHandler(cu_context_, &child_context_, offset); + default: + return NULL; + } +} + +void DwarfCUToModule::WarningReporter::CUHeading() { + if (printed_cu_header_) + return; + fprintf(stderr, "%s: in compilation unit '%s' (offset 0x%llx):\n", + filename_.c_str(), cu_name_.c_str(), cu_offset_); + printed_cu_header_ = true; +} + +void DwarfCUToModule::WarningReporter::UnknownSpecification(uint64 offset, + uint64 target) { + CUHeading(); + fprintf(stderr, "%s: the DIE at offset 0x%llx has a DW_AT_specification" + " attribute referring to the die at offset 0x%llx, which either" + " was not marked as a declaration, or comes later in the file\n", + filename_.c_str(), offset, target); +} + +void DwarfCUToModule::WarningReporter::UnknownAbstractOrigin(uint64 offset, + uint64 target) { + CUHeading(); + fprintf(stderr, "%s: the DIE at offset 0x%llx has a DW_AT_abstract_origin" + " attribute referring to the die at offset 0x%llx, which either" + " was not marked as an inline, or comes later in the file\n", + filename_.c_str(), offset, target); +} + +void DwarfCUToModule::WarningReporter::MissingSection(const string &name) { + CUHeading(); + fprintf(stderr, "%s: warning: couldn't find DWARF '%s' section\n", + filename_.c_str(), name.c_str()); +} + +void DwarfCUToModule::WarningReporter::BadLineInfoOffset(uint64 offset) { + CUHeading(); + fprintf(stderr, "%s: warning: line number data offset beyond end" + " of '.debug_line' section\n", + filename_.c_str()); +} + +void DwarfCUToModule::WarningReporter::UncoveredHeading() { + if (printed_unpaired_header_) + return; + CUHeading(); + fprintf(stderr, "%s: warning: skipping unpaired lines/functions:\n", + filename_.c_str()); + printed_unpaired_header_ = true; +} + +void DwarfCUToModule::WarningReporter::UncoveredFunction( + const Module::Function &function) { + UncoveredHeading(); + fprintf(stderr, " function%s: %s\n", + function.size == 0 ? " (zero-length)" : "", + function.name.c_str()); +} + +void DwarfCUToModule::WarningReporter::UncoveredLine(const Module::Line &line) { + UncoveredHeading(); + fprintf(stderr, " line%s: %s:%d at 0x%llx\n", + (line.size == 0 ? " (zero-length)" : ""), + line.file->name.c_str(), line.number, line.address); +} + +DwarfCUToModule::DwarfCUToModule(FileContext *file_context, + LineToModuleFunctor *line_reader, + WarningReporter *reporter) + : line_reader_(line_reader), has_source_line_info_(false) { + cu_context_ = new CUContext(file_context, reporter); + child_context_ = new DIEContext(); +} + +DwarfCUToModule::~DwarfCUToModule() { + delete cu_context_; + delete child_context_; +} + +void DwarfCUToModule::ProcessAttributeSigned(enum DwarfAttribute attr, + enum DwarfForm form, + int64 data) { + switch (attr) { + case dwarf2reader::DW_AT_language: // source language of this CU + SetLanguage(static_cast(data)); + break; + default: + break; + } +} + +void DwarfCUToModule::ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data) { + switch (attr) { + case dwarf2reader::DW_AT_stmt_list: // Line number information. + has_source_line_info_ = true; + source_line_offset_ = data; + break; + case dwarf2reader::DW_AT_language: // source language of this CU + SetLanguage(static_cast(data)); + break; + default: + break; + } +} + +void DwarfCUToModule::ProcessAttributeString(enum DwarfAttribute attr, + enum DwarfForm form, + const string &data) { + if (attr == dwarf2reader::DW_AT_name) + cu_context_->reporter->SetCUName(data); +} + +bool DwarfCUToModule::EndAttributes() { + return true; +} + +dwarf2reader::DIEHandler *DwarfCUToModule::FindChildHandler( + uint64 offset, + enum DwarfTag tag, + const AttributeList &attrs) { + switch (tag) { + case dwarf2reader::DW_TAG_subprogram: + return new FuncHandler(cu_context_, child_context_, offset); + case dwarf2reader::DW_TAG_namespace: + case dwarf2reader::DW_TAG_class_type: + case dwarf2reader::DW_TAG_structure_type: + case dwarf2reader::DW_TAG_union_type: + return new NamedScopeHandler(cu_context_, child_context_, offset); + default: + return NULL; + } +} + +void DwarfCUToModule::SetLanguage(DwarfLanguage language) { + switch (language) { + case dwarf2reader::DW_LANG_Java: + cu_context_->language = Language::Java; + break; + + // DWARF has no generic language code for assembly language; this is + // what the GNU toolchain uses. + case dwarf2reader::DW_LANG_Mips_Assembler: + cu_context_->language = Language::Assembler; + break; + + // C++ covers so many cases that it probably has some way to cope + // with whatever the other languages throw at us. So make it the + // default. + // + // Objective C and Objective C++ seem to create entries for + // methods whose DW_AT_name values are already fully-qualified: + // "-[Classname method:]". These appear at the top level. + // + // DWARF data for C should never include namespaces or functions + // nested in struct types, but if it ever does, then C++'s + // notation is probably not a bad choice for that. + default: + case dwarf2reader::DW_LANG_ObjC: + case dwarf2reader::DW_LANG_ObjC_plus_plus: + case dwarf2reader::DW_LANG_C: + case dwarf2reader::DW_LANG_C89: + case dwarf2reader::DW_LANG_C99: + case dwarf2reader::DW_LANG_C_plus_plus: + cu_context_->language = Language::CPlusPlus; + break; + } +} + +void DwarfCUToModule::ReadSourceLines(uint64 offset) { + const dwarf2reader::SectionMap §ion_map + = cu_context_->file_context->section_map; + dwarf2reader::SectionMap::const_iterator map_entry + = section_map.find(".debug_line"); + if (map_entry == section_map.end()) { + cu_context_->reporter->MissingSection(".debug_line"); + return; + } + const char *section_start = map_entry->second.first; + uint64 section_length = map_entry->second.second; + if (offset >= section_length) { + cu_context_->reporter->BadLineInfoOffset(offset); + return; + } + (*line_reader_)(section_start + offset, section_length - offset, + cu_context_->file_context->module, &lines_); +} + +namespace { +// Return true if ADDRESS falls within the range of ITEM. +template +inline bool within(const T &item, Module::Address address) { + // Because Module::Address is unsigned, and unsigned arithmetic + // wraps around, this will be false if ADDRESS falls before the + // start of ITEM, or if it falls after ITEM's end. + return address - item.address < item.size; +} +} + +void DwarfCUToModule::AssignLinesToFunctions() { + vector *functions = &cu_context_->functions; + WarningReporter *reporter = cu_context_->reporter; + + // This would be simpler if we assumed that source line entries + // don't cross function boundaries. However, there's no real reason + // to assume that (say) a series of function definitions on the same + // line wouldn't get coalesced into one line number entry. The + // DWARF spec certainly makes no such promises. + // + // So treat the functions and lines as peers, and take the trouble + // to compute their ranges' intersections precisely. In any case, + // the hair here is a constant factor for performance; the + // complexity from here on out is linear. + + // Put both our functions and lines in order by address. + sort(functions->begin(), functions->end(), + Module::Function::CompareByAddress); + sort(lines_.begin(), lines_.end(), Module::Line::CompareByAddress); + + // The last line that we used any piece of. We use this only for + // generating warnings. + const Module::Line *last_line_used = NULL; + + // The last function and line we warned about --- so we can avoid + // doing so more than once. + const Module::Function *last_function_cited = NULL; + const Module::Line *last_line_cited = NULL; + + // Make a single pass through both vectors from lower to higher + // addresses, populating each Function's lines vector with lines + // from our lines_ vector that fall within the function's address + // range. + vector::iterator func_it = functions->begin(); + vector::const_iterator line_it = lines_.begin(); + + Module::Address current; + + // Pointers to the referents of func_it and line_it, or NULL if the + // iterator is at the end of the sequence. + Module::Function *func; + const Module::Line *line; + + // Start current at the beginning of the first line or function, + // whichever is earlier. + if (func_it != functions->end() && line_it != lines_.end()) { + func = *func_it; + line = &*line_it; + current = std::min(func->address, line->address); + } else if (line_it != lines_.end()) { + func = NULL; + line = &*line_it; + current = line->address; + } else if (func_it != functions->end()) { + func = *func_it; + line = NULL; + current = (*func_it)->address; + } else { + return; + } + + while (func || line) { + // This loop has two invariants that hold at the top. + // + // First, at least one of the iterators is not at the end of its + // sequence, and those that are not refer to the earliest + // function or line that contains or starts after CURRENT. + // + // Note that every byte is in one of four states: it is covered + // or not covered by a function, and, independently, it is + // covered or not covered by a line. + // + // The second invariant is that CURRENT refers to a byte whose + // state is different from its predecessor, or it refers to the + // first byte in the address space. In other words, CURRENT is + // always the address of a transition. + // + // Note that, although each iteration advances CURRENT from one + // transition address to the next in each iteration, it might + // not advance the iterators. Suppose we have a function that + // starts with a line, has a gap, and then a second line, and + // suppose that we enter an iteration with CURRENT at the end of + // the first line. The next transition address is the start of + // the second line, after the gap, so the iteration should + // advance CURRENT to that point. At the head of that iteration, + // the invariants require that the line iterator be pointing at + // the second line. But this is also true at the head of the + // next. And clearly, the iteration must not change the function + // iterator. So neither iterator moves. + + // Assert the first invariant (see above). + assert(!func || current < func->address || within(*func, current)); + assert(!line || current < line->address || within(*line, current)); + + // The next transition after CURRENT. + Module::Address next_transition; + + // Figure out which state we're in, add lines or warn, and compute + // the next transition address. + if (func && current >= func->address) { + if (line && current >= line->address) { + // Covered by both a line and a function. + Module::Address func_left = func->size - (current - func->address); + Module::Address line_left = line->size - (current - line->address); + // This may overflow, but things work out. + next_transition = current + std::min(func_left, line_left); + Module::Line l = *line; + l.address = current; + l.size = next_transition - current; + func->lines.push_back(l); + last_line_used = line; + } else { + // Covered by a function, but no line. + if (func != last_function_cited) { + reporter->UncoveredFunction(*func); + last_function_cited = func; + } + if (line && within(*func, line->address)) + next_transition = line->address; + else + // If this overflows, we'll catch it below. + next_transition = func->address + func->size; + } + } else { + if (line && current >= line->address) { + // Covered by a line, but no function. + // + // If GCC emits padding after one function to align the start + // of the next, then it will attribute the padding + // instructions to the last source line of function (to reduce + // the size of the line number info), but omit it from the + // DW_AT_{low,high}_pc range given in .debug_info (since it + // costs nothing to be precise there). If we did use at least + // some of the line we're about to skip, and it ends at the + // start of the next function, then assume this is what + // happened, and don't warn. + if (line != last_line_cited + && !(func + && line == last_line_used + && func->address - line->address == line->size)) { + reporter->UncoveredLine(*line); + last_line_cited = line; + } + if (func && within(*line, func->address)) + next_transition = func->address; + else + // If this overflows, we'll catch it below. + next_transition = line->address + line->size; + } else { + // Covered by neither a function nor a line. By the invariant, + // both func and line begin after CURRENT. The next transition + // is the start of the next function or next line, whichever + // is earliest. + assert (func || line); + if (func && line) + next_transition = std::min(func->address, line->address); + else if (func) + next_transition = func->address; + else + next_transition = line->address; + } + } + + // If a function or line abuts the end of the address space, then + // next_transition may end up being zero, in which case we've completed + // our pass. Handle that here, instead of trying to deal with it in + // each place we compute next_transition. + if (!next_transition) + break; + + // Advance iterators as needed. If lines overlap or functions overlap, + // then we could go around more than once. We don't worry too much + // about what result we produce in that case, just as long as we don't + // hang or crash. + while (func_it != functions->end() + && current >= (*func_it)->address + && !within(**func_it, next_transition)) + func_it++; + func = (func_it != functions->end()) ? *func_it : NULL; + while (line_it != lines_.end() + && current >= line_it->address + && !within(*line_it, next_transition)) + line_it++; + line = (line_it != lines_.end()) ? &*line_it : NULL; + + // We must make progress. + assert(next_transition > current); + current = next_transition; + } +} + +void DwarfCUToModule::Finish() { + // Assembly language files have no function data, and that gives us + // no place to store our line numbers (even though the GNU toolchain + // will happily produce source line info for assembly language + // files). To avoid spurious warnings about lines we can't assign + // to functions, skip CUs in languages that lack functions. + if (!cu_context_->language->HasFunctions()) + return; + + // Read source line info, if we have any. + if (has_source_line_info_) + ReadSourceLines(source_line_offset_); + + vector *functions = &cu_context_->functions; + + // Dole out lines to the appropriate functions. + AssignLinesToFunctions(); + + // Add our functions, which now have source lines assigned to them, + // to module_. + cu_context_->file_context->module->AddFunctions(functions->begin(), + functions->end()); + + // Ownership of the function objects has shifted from cu_context to + // the Module. + functions->clear(); +} + +bool DwarfCUToModule::StartCompilationUnit(uint64 offset, + uint8 address_size, + uint8 offset_size, + uint64 cu_length, + uint8 dwarf_version) { + return dwarf_version >= 2; +} + +bool DwarfCUToModule::StartRootDIE(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs) { + // We don't deal with partial compilation units (the only other tag + // likely to be used for root DIE). + return tag == dwarf2reader::DW_TAG_compile_unit; +} + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cu_to_module.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cu_to_module.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cu_to_module.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cu_to_module.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,259 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// Add DWARF debugging information to a Breakpad symbol file. This +// file defines the DwarfCUToModule class, which accepts parsed DWARF +// data and populates a google_breakpad::Module with the results; the +// Module can then write its contents as a Breakpad symbol file. + +#ifndef COMMON_LINUX_DWARF_CU_TO_MODULE_H__ +#define COMMON_LINUX_DWARF_CU_TO_MODULE_H__ + +#include + +#include +#include +#include "common/linux/language.h" +#include "common/linux/module.h" +#include "common/dwarf/bytereader.h" +#include "common/dwarf/dwarf2diehandler.h" +#include "common/dwarf/dwarf2reader.h" + +namespace google_breakpad { + +using dwarf2reader::AttributeList; +using dwarf2reader::DwarfAttribute; +using dwarf2reader::DwarfForm; +using dwarf2reader::DwarfLanguage; +using dwarf2reader::DwarfTag; + +// Populate a google_breakpad::Module with DWARF debugging information. +// +// An instance of this class can be provided as a handler to a +// dwarf2reader::CompilationUnit DWARF parser. The handler uses the +// results of parsing to populate a google_breakpad::Module with +// source file, function, and source line information. +class DwarfCUToModule: public dwarf2reader::RootDIEHandler { + struct FilePrivate; + public: + + // Information global to the DWARF-bearing file we are processing, + // for use by DwarfCUToModule. Each DwarfCUToModule instance deals + // with a single compilation unit within the file, but information + // global to the whole file is held here. The client is responsible + // for filling it in appropriately (except for the 'file_private' + // field, which the constructor and destructor take care of), and + // then providing it to the DwarfCUToModule instance for each + // compilation unit we process in that file. + struct FileContext { + FileContext(const string &filename_arg, Module *module_arg); + ~FileContext(); + + // The name of this file, for use in error messages. + string filename; + + // A map of this file's sections, used for finding other DWARF + // sections that the .debug_info section may refer to. + dwarf2reader::SectionMap section_map; + + // The Module to which we're contributing definitions. + Module *module; + + // Inter-compilation unit data used internally by the handlers. + FilePrivate *file_private; + }; + + // An abstract base class for functors that handle DWARF line data + // for DwarfCUToModule. DwarfCUToModule could certainly just use + // dwarf2reader::LineInfo itself directly, but decoupling things + // this way makes unit testing a little easier. + class LineToModuleFunctor { + public: + LineToModuleFunctor() { } + virtual ~LineToModuleFunctor() { } + + // Populate MODULE and LINES with source file names and code/line + // mappings, given a pointer to some DWARF line number data + // PROGRAM, and an overestimate of its size. Add no zero-length + // lines to LINES. + virtual void operator()(const char *program, uint64 length, + Module *module, vector *lines) = 0; + }; + + // The interface DwarfCUToModule uses to report warnings. The member + // function definitions for this class write messages to stderr, but + // you can override them if you'd like to detect or report these + // conditions yourself. + class WarningReporter { + public: + // Warn about problems in the DWARF file FILENAME, in the + // compilation unit at OFFSET. + WarningReporter(const string &filename, uint64 cu_offset) + : filename_(filename), cu_offset_(cu_offset), printed_cu_header_(false), + printed_unpaired_header_(false) { } + virtual ~WarningReporter() { } + + // Set the name of the compilation unit we're processing to NAME. + virtual void SetCUName(const string &name) { cu_name_ = name; } + + // A DW_AT_specification in the DIE at OFFSET refers to a DIE we + // haven't processed yet, or that wasn't marked as a declaration, + // at TARGET. + virtual void UnknownSpecification(uint64 offset, uint64 target); + + // A DW_AT_abstract_origin in the DIE at OFFSET refers to a DIE we + // haven't processed yet, or that wasn't marked as inline, at TARGET. + virtual void UnknownAbstractOrigin(uint64 offset, uint64 target); + + // We were unable to find the DWARF section named SECTION_NAME. + virtual void MissingSection(const string §ion_name); + + // The CU's DW_AT_stmt_list offset OFFSET is bogus. + virtual void BadLineInfoOffset(uint64 offset); + + // FUNCTION includes code covered by no line number data. + virtual void UncoveredFunction(const Module::Function &function); + + // Line number NUMBER in LINE_FILE, of length LENGTH, includes code + // covered by no function. + virtual void UncoveredLine(const Module::Line &line); + + protected: + string filename_; + uint64 cu_offset_; + string cu_name_; + bool printed_cu_header_; + bool printed_unpaired_header_; + + private: + // Print a per-CU heading, once. + void CUHeading(); + // Print an unpaired function/line heading, once. + void UncoveredHeading(); + }; + + // Create a DWARF debugging info handler for a compilation unit + // within FILE_CONTEXT. This uses information received from the + // dwarf2reader::CompilationUnit DWARF parser to populate + // FILE_CONTEXT->module. Use LINE_READER to handle the compilation + // unit's line number data. Use REPORTER to report problems with the + // data we find. + DwarfCUToModule(FileContext *file_context, + LineToModuleFunctor *line_reader, + WarningReporter *reporter); + ~DwarfCUToModule(); + + void ProcessAttributeSigned(enum DwarfAttribute attr, + enum DwarfForm form, + int64 data); + void ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data); + void ProcessAttributeString(enum DwarfAttribute attr, + enum DwarfForm form, + const string &data); + bool EndAttributes(); + DIEHandler *FindChildHandler(uint64 offset, enum DwarfTag tag, + const AttributeList &attrs); + + // Assign all our source Lines to the Functions that cover their + // addresses, and then add them to module_. + void Finish(); + + bool StartCompilationUnit(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version); + bool StartRootDIE(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs); + + private: + + // Used internally by the handler. Full definitions are in + // dwarf_cu_to_module.cc. + struct FilePrivate; + struct Specification; + struct CUContext; + struct DIEContext; + class GenericDIEHandler; + class FuncHandler; + class NamedScopeHandler; + + // A map from section offsets to specifications. + typedef map SpecificationByOffset; + + // Set this compilation unit's source language to LANGUAGE. + void SetLanguage(DwarfLanguage language); + + // Read source line information at OFFSET in the .debug_line + // section. Record source files in module_, but record source lines + // in lines_; we apportion them to functions in + // AssignLinesToFunctions. + void ReadSourceLines(uint64 offset); + + // Assign the lines in lines_ to the individual line lists of the + // functions in functions_. (DWARF line information maps an entire + // compilation unit at a time, and gives no indication of which + // lines belong to which functions, beyond their addresses.) + void AssignLinesToFunctions(); + + // The only reason cu_context_ and child_context_ are pointers is + // that we want to keep their definitions private to + // dwarf_cu_to_module.cc, instead of listing them all here. They are + // owned by this DwarfCUToModule: the constructor sets them, and the + // destructor deletes them. + + // The functor to use to handle line number data. + LineToModuleFunctor *line_reader_; + + // This compilation unit's context. + CUContext *cu_context_; + + // A context for our children. + DIEContext *child_context_; + + // True if this compilation unit has source line information. + bool has_source_line_info_; + + // The offset of this compilation unit's line number information in + // the .debug_line section. + uint64 source_line_offset_; + + // The line numbers we have seen thus far. We accumulate these here + // during parsing. Then, in Finish, we call AssignLinesToFunctions + // to dole them out to the appropriate functions. + vector lines_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DWARF_CU_TO_MODULE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cu_to_module_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cu_to_module_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cu_to_module_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_cu_to_module_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,1637 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// dwarf_cu_to_module.cc: Unit tests for google_breakpad::DwarfCUToModule. + +#include + +#include "breakpad_googletest_includes.h" +#include "common/linux/dwarf_cu_to_module.h" + +using std::vector; + +using dwarf2reader::AttributeList; +using dwarf2reader::DIEHandler; +using dwarf2reader::DwarfTag; +using dwarf2reader::DwarfAttribute; +using dwarf2reader::DwarfForm; +using dwarf2reader::DwarfInline; +using dwarf2reader::RootDIEHandler; +using google_breakpad::DwarfCUToModule; +using google_breakpad::Module; + +using ::testing::_; +using ::testing::AtMost; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::Test; +using ::testing::TestWithParam; +using ::testing::Values; +using ::testing::ValuesIn; + +// Mock classes. + +class MockLineToModuleFunctor: public DwarfCUToModule::LineToModuleFunctor { + public: + MOCK_METHOD4(mock_apply, void(const char *program, uint64 length, + Module *module, vector *lines)); + void operator()(const char *program, uint64 length, + Module *module, vector *lines) { + mock_apply(program, length, module, lines); + } +}; + +class MockWarningReporter: public DwarfCUToModule::WarningReporter { + public: + MockWarningReporter(const string &filename, uint64 cu_offset) + : DwarfCUToModule::WarningReporter(filename, cu_offset) { } + MOCK_METHOD1(SetCUName, void(const string &name)); + MOCK_METHOD2(UnknownSpecification, void(uint64 offset, uint64 target)); + MOCK_METHOD2(UnknownAbstractOrigin, void(uint64 offset, uint64 target)); + MOCK_METHOD1(MissingSection, void(const string §ion_name)); + MOCK_METHOD1(BadLineInfoOffset, void(uint64 offset)); + MOCK_METHOD1(UncoveredFunction, void(const Module::Function &function)); + MOCK_METHOD1(UncoveredLine, void(const Module::Line &line)); +}; + +// A fixture class including all the objects needed to handle a +// compilation unit, and their entourage. It includes member functions +// for doing common kinds of setup and tests. +class CUFixtureBase { + public: + + // If we have: + // + // vector lines; + // AppendLinesFunctor appender(lines); + // + // then doing: + // + // appender(line_program, length, module, line_vector); + // + // will append lines to the end of line_vector. We can use this with + // MockLineToModuleFunctor like this: + // + // MockLineToModuleFunctor l2m; + // EXPECT_CALL(l2m, mock_apply(_,_,_,_)) + // .WillOnce(DoAll(Invoke(appender), Return())); + // + // in which case calling l2m with some line vector will append lines. + class AppendLinesFunctor { + public: + AppendLinesFunctor(const vector *lines) : lines_(lines) { } + void operator()(const char *program, uint64 length, + Module *module, vector *lines) { + lines->insert(lines->end(), lines_->begin(), lines_->end()); + } + private: + const vector *lines_; + }; + + CUFixtureBase() + : module_("module-name", "module-os", "module-arch", "module-id"), + file_context_("dwarf-filename", &module_), + language_(dwarf2reader::DW_LANG_none), + language_signed_(false), + appender_(&lines_), + reporter_("dwarf-filename", 0xcf8f9bb6443d29b5LL), + root_handler_(&file_context_, &line_reader_, &reporter_), + functions_filled_(false) { + // By default, expect no warnings to be reported, and expect the + // compilation unit's name to be provided. The test can override + // these expectations. + EXPECT_CALL(reporter_, SetCUName("compilation-unit-name")).Times(1); + EXPECT_CALL(reporter_, UnknownSpecification(_, _)).Times(0); + EXPECT_CALL(reporter_, UnknownAbstractOrigin(_, _)).Times(0); + EXPECT_CALL(reporter_, MissingSection(_)).Times(0); + EXPECT_CALL(reporter_, BadLineInfoOffset(_)).Times(0); + EXPECT_CALL(reporter_, UncoveredFunction(_)).Times(0); + EXPECT_CALL(reporter_, UncoveredLine(_)).Times(0); + + // By default, expect the line program reader not to be invoked. We + // may override this in StartCU. + EXPECT_CALL(line_reader_, mock_apply(_,_,_,_)).Times(0); + + // The handler will consult this section map to decide what to + // pass to our line reader. + file_context_.section_map[".debug_line"] = std::make_pair(dummy_line_program_, + dummy_line_size_); + } + + // Add a line with the given address, size, filename, and line + // number to the end of the statement list the handler will receive + // when it invokes its LineToModuleFunctor. Call this before calling + // StartCU. + void PushLine(Module::Address address, Module::Address size, + const string &filename, int line_number); + + // Use LANGUAGE for the compilation unit. More precisely, arrange + // for StartCU to pass the compilation unit's root DIE a + // DW_AT_language attribute whose value is LANGUAGE. + void SetLanguage(dwarf2reader::DwarfLanguage language) { + language_ = language; + } + + // If SIGNED true, have StartCU report DW_AT_language as a signed + // attribute; if false, have it report it as unsigned. + void SetLanguageSigned(bool is_signed) { language_signed_ = is_signed; } + + // Call the handler this.root_handler_'s StartCompilationUnit and + // StartRootDIE member functions, passing it appropriate attributes as + // determined by prior calls to PushLine and SetLanguage. Leave + // this.root_handler_ ready to hear about children: call + // this.root_handler_.EndAttributes, but not this.root_handler_.Finish. + void StartCU(); + + // Add some strange attributes/form pairs to the end of ATTRS. + void PushBackStrangeAttributes(dwarf2reader::AttributeList *attrs); + + // Have HANDLER process some strange attribute/form/value triples. + // These will match those promised by PushBackStrangeAttributes. + void ProcessStrangeAttributes(dwarf2reader::DIEHandler *handler); + + // Start a child DIE of PARENT with the given tag and name. Leave + // the handler ready to hear about children: call EndAttributes, but + // not Finish. + DIEHandler *StartNamedDIE(DIEHandler *parent, DwarfTag tag, + const string &name); + + // Start a child DIE of PARENT with the given tag and a + // DW_AT_specification attribute whose value is SPECIFICATION. Leave + // the handler ready to hear about children: call EndAttributes, but + // not Finish. If NAME is non-zero, use it as the DW_AT_name + // attribute. + DIEHandler *StartSpecifiedDIE(DIEHandler *parent, DwarfTag tag, + uint64 offset, const char *name = NULL); + + // Define a function as a child of PARENT with the given name, + // address, and size. Call EndAttributes and Finish; one cannot + // define children of the defined function's DIE. + void DefineFunction(DIEHandler *parent, const string &name, + Module::Address address, Module::Address size); + + // Create a declaration DIE as a child of PARENT with the given + // offset, tag and name. If NAME is the empty string, don't provide + // a DW_AT_name attribute. Call EndAttributes and Finish. + void DeclarationDIE(DIEHandler *parent, uint64 offset, + DwarfTag tag, const string &name); + + // Create a definition DIE as a child of PARENT with the given tag + // that refers to the declaration DIE at offset SPECIFICATION as its + // specification. If NAME is non-empty, pass it as the DW_AT_name + // attribute. If SIZE is non-zero, record ADDRESS and SIZE as + // low_pc/high_pc attributes. + void DefinitionDIE(DIEHandler *parent, DwarfTag tag, + uint64 specification, const string &name, + Module::Address address = 0, Module::Address size = 0); + + // Create an inline DW_TAG_subprogram DIE as a child of PARENT. If + // SPECIFICATION is non-zero, then the DIE refers to the declaration DIE at + // offset SPECIFICATION as its specification. If Name is non-empty, pass it + // as the DW_AT_name attribute. + void AbstractInstanceDIE(DIEHandler *parent, uint64 offset, + DwarfInline type, uint64 specification, + const string &name, + DwarfForm form = dwarf2reader::DW_FORM_data1); + + // Create a DW_TAG_subprogram DIE as a child of PARENT that refers to + // ORIGIN in its DW_AT_abstract_origin attribute. If NAME is the empty + // string, don't provide a DW_AT_name attribute. + void DefineInlineInstanceDIE(DIEHandler *parent, const string &name, + uint64 origin, Module::Address address, + Module::Address size); + + // The following Test* functions should be called after calling + // this.root_handler_.Finish. After that point, no further calls + // should be made on the handler. + + // Test that the number of functions defined in the module this.module_ is + // equal to EXPECTED. + void TestFunctionCount(size_t expected); + + // Test that the I'th function (ordered by address) in the module + // this.module_ has the given name, address, and size, and that its + // parameter size is zero. + void TestFunction(int i, const string &name, + Module::Address address, Module::Address size); + + // Test that the number of source lines owned by the I'th function + // in the module this.module_ is equal to EXPECTED. + void TestLineCount(int i, size_t expected); + + // Test that the J'th line (ordered by address) of the I'th function + // (again, by address) has the given address, size, filename, and + // line number. + void TestLine(int i, int j, Module::Address address, Module::Address size, + const string &filename, int number); + + // Actual objects under test. + Module module_; + DwarfCUToModule::FileContext file_context_; + + // If this is not DW_LANG_none, we'll pass it as a DW_AT_language + // attribute to the compilation unit. This defaults to DW_LANG_none. + dwarf2reader::DwarfLanguage language_; + + // If this is true, report DW_AT_language as a signed value; if false, + // report it as an unsigned value. + bool language_signed_; + + // If this is not empty, we'll give the CU a DW_AT_stmt_list + // attribute that, when passed to line_reader_, adds these lines to the + // provided lines array. + vector lines_; + + // Mock line program reader. + MockLineToModuleFunctor line_reader_; + AppendLinesFunctor appender_; + static const char dummy_line_program_[]; + static const size_t dummy_line_size_; + + MockWarningReporter reporter_; + DwarfCUToModule root_handler_; + + private: + // Fill functions_, if we haven't already. + void FillFunctions(); + + // If functions_filled_ is true, this is a table of functions we've + // extracted from module_, sorted by address. + vector functions_; + // True if we have filled the above vector with this.module_'s function list. + bool functions_filled_; +}; + +const char CUFixtureBase::dummy_line_program_[] = "lots of fun data"; +const size_t CUFixtureBase::dummy_line_size_ = + sizeof (CUFixtureBase::dummy_line_program_); + +void CUFixtureBase::PushLine(Module::Address address, Module::Address size, + const string &filename, int line_number) { + Module::Line l; + l.address = address; + l.size = size; + l.file = module_.FindFile(filename); + l.number = line_number; + lines_.push_back(l); +} + +void CUFixtureBase::StartCU() { + // If we have lines, make the line reader expect to be invoked at + // most once. (Hey, if the handler can pass its tests without + // bothering to read the line number data, that's great.) + // Have it add the lines passed to PushLine. Otherwise, leave the + // initial expectation (no calls) in force. + if (!lines_.empty()) + EXPECT_CALL(line_reader_, + mock_apply(&dummy_line_program_[0], dummy_line_size_, + &module_, _)) + .Times(AtMost(1)) + .WillOnce(DoAll(Invoke(appender_), Return())); + + ASSERT_TRUE(root_handler_ + .StartCompilationUnit(0x51182ec307610b51ULL, 0x81, 0x44, + 0x4241b4f33720dd5cULL, 3)); + { + dwarf2reader::AttributeList attrs; + attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + if (!lines_.empty()) + attrs.push_back(make_pair(dwarf2reader::DW_AT_stmt_list, + dwarf2reader::DW_FORM_ref4)); + if (language_ != dwarf2reader::DW_LANG_none) + attrs.push_back(make_pair(dwarf2reader::DW_AT_language, + language_signed_ + ? dwarf2reader::DW_FORM_sdata + : dwarf2reader::DW_FORM_udata)); + ASSERT_TRUE(root_handler_.StartRootDIE(0x02e56bfbda9e7337ULL, + dwarf2reader::DW_TAG_compile_unit, + attrs)); + } + root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + "compilation-unit-name"); + if (!lines_.empty()) + root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_stmt_list, + dwarf2reader::DW_FORM_ref4, + 0); + if (language_ != dwarf2reader::DW_LANG_none) { + if (language_signed_) + root_handler_.ProcessAttributeSigned(dwarf2reader::DW_AT_language, + dwarf2reader::DW_FORM_sdata, + language_); + else + root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_language, + dwarf2reader::DW_FORM_udata, + language_); + } + ASSERT_TRUE(root_handler_.EndAttributes()); +} + +void CUFixtureBase::PushBackStrangeAttributes( + dwarf2reader::AttributeList *attrs) { + attrs->push_back(make_pair((DwarfAttribute) 0xf560dead, + (DwarfForm) 0x4106e4db)); + attrs->push_back(make_pair((DwarfAttribute) 0x85380095, + (DwarfForm) 0x0f16fe87)); + attrs->push_back(make_pair((DwarfAttribute) 0xf7f7480f, + (DwarfForm) 0x829e038a)); + attrs->push_back(make_pair((DwarfAttribute) 0xa55ffb51, + (DwarfForm) 0x2f43b041)); + attrs->push_back(make_pair((DwarfAttribute) 0x2fde304a, + (DwarfForm) 0x895ffa23)); +} + +void CUFixtureBase::ProcessStrangeAttributes( + dwarf2reader::DIEHandler *handler) { + handler->ProcessAttributeUnsigned((DwarfAttribute) 0xf560dead, + (DwarfForm) 0x4106e4db, + 0xa592571997facda1ULL); + handler->ProcessAttributeSigned((DwarfAttribute) 0x85380095, + (DwarfForm) 0x0f16fe87, + 0x12602a4e3bf1f446LL); + handler->ProcessAttributeReference((DwarfAttribute) 0xf7f7480f, + (DwarfForm) 0x829e038a, + 0x50fddef44734fdecULL); + static const char buffer[10] = "frobynode"; + handler->ProcessAttributeBuffer((DwarfAttribute) 0xa55ffb51, + (DwarfForm) 0x2f43b041, + buffer, sizeof(buffer)); + handler->ProcessAttributeString((DwarfAttribute) 0x2f43b041, + (DwarfForm) 0x895ffa23, + "strange string"); +} + +DIEHandler *CUFixtureBase::StartNamedDIE(DIEHandler *parent, + DwarfTag tag, + const string &name) { + dwarf2reader::AttributeList attrs; + attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + PushBackStrangeAttributes(&attrs); + dwarf2reader::DIEHandler *handler + = parent->FindChildHandler(0x8f4c783c0467c989ULL, tag, attrs); + if (!handler) + return NULL; + handler->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + ProcessStrangeAttributes(handler); + if (!handler->EndAttributes()) { + handler->Finish(); + delete handler; + return NULL; + } + + return handler; +} + +DIEHandler *CUFixtureBase::StartSpecifiedDIE(DIEHandler *parent, + DwarfTag tag, + uint64 specification, + const char *name) { + dwarf2reader::AttributeList attrs; + if (name) + attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + attrs.push_back(make_pair(dwarf2reader::DW_AT_specification, + dwarf2reader::DW_FORM_ref4)); + dwarf2reader::DIEHandler *handler + = parent->FindChildHandler(0x8f4c783c0467c989ULL, tag, attrs); + if (!handler) + return NULL; + if (name) + handler->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + handler->ProcessAttributeReference(dwarf2reader::DW_AT_specification, + dwarf2reader::DW_FORM_ref4, + specification); + if (!handler->EndAttributes()) { + handler->Finish(); + delete handler; + return NULL; + } + + return handler; +} + +void CUFixtureBase::DefineFunction(dwarf2reader::DIEHandler *parent, + const string &name, Module::Address address, + Module::Address size) { + dwarf2reader::AttributeList func_attrs; + func_attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + func_attrs.push_back(make_pair(dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr)); + func_attrs.push_back(make_pair(dwarf2reader::DW_AT_high_pc, + dwarf2reader::DW_FORM_addr)); + PushBackStrangeAttributes(&func_attrs); + dwarf2reader::DIEHandler *func + = parent->FindChildHandler(0xe34797c7e68590a8LL, + dwarf2reader::DW_TAG_subprogram, + func_attrs); + ASSERT_TRUE(func != NULL); + func->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr, + address); + func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc, + dwarf2reader::DW_FORM_addr, + address + size); + ProcessStrangeAttributes(func); + EXPECT_TRUE(func->EndAttributes()); + func->Finish(); + delete func; +} + +void CUFixtureBase::DeclarationDIE(DIEHandler *parent, uint64 offset, + DwarfTag tag, + const string &name) { + dwarf2reader::AttributeList attrs; + if (!name.empty()) + attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + attrs.push_back(make_pair(dwarf2reader::DW_AT_declaration, + dwarf2reader::DW_FORM_flag)); + dwarf2reader::DIEHandler *die = parent->FindChildHandler(offset, tag, attrs); + ASSERT_TRUE(die != NULL); + if (!name.empty()) + die->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_declaration, + dwarf2reader::DW_FORM_flag, + 1); + EXPECT_TRUE(die->EndAttributes()); + die->Finish(); + delete die; +} + +void CUFixtureBase::DefinitionDIE(DIEHandler *parent, + DwarfTag tag, + uint64 specification, + const string &name, + Module::Address address, + Module::Address size) { + dwarf2reader::AttributeList attrs; + attrs.push_back(make_pair(dwarf2reader::DW_AT_specification, + dwarf2reader::DW_FORM_ref4)); + if (!name.empty()) + attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + if (size) { + attrs.push_back(make_pair(dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr)); + attrs.push_back(make_pair(dwarf2reader::DW_AT_high_pc, + dwarf2reader::DW_FORM_addr)); + } + dwarf2reader::DIEHandler *die + = parent->FindChildHandler(0x6ccfea031a9e6cc9ULL, tag, attrs); + ASSERT_TRUE(die != NULL); + die->ProcessAttributeReference(dwarf2reader::DW_AT_specification, + dwarf2reader::DW_FORM_ref4, + specification); + if (!name.empty()) + die->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + if (size) { + die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr, + address); + die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc, + dwarf2reader::DW_FORM_addr, + address + size); + } + EXPECT_TRUE(die->EndAttributes()); + die->Finish(); + delete die; +} + +void CUFixtureBase::AbstractInstanceDIE(DIEHandler *parent, + uint64 offset, + DwarfInline type, + uint64 specification, + const string &name, + DwarfForm form) { + dwarf2reader::AttributeList attrs; + if (specification != 0ULL) + attrs.push_back(make_pair(dwarf2reader::DW_AT_specification, + dwarf2reader::DW_FORM_ref4)); + attrs.push_back(make_pair(dwarf2reader::DW_AT_inline, form)); + if (!name.empty()) + attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + dwarf2reader::DIEHandler *die + = parent->FindChildHandler(offset, dwarf2reader::DW_TAG_subprogram, attrs); + ASSERT_TRUE(die != NULL); + if (specification != 0ULL) + die->ProcessAttributeReference(dwarf2reader::DW_AT_specification, + dwarf2reader::DW_FORM_ref4, + specification); + if (form == dwarf2reader::DW_FORM_sdata) { + die->ProcessAttributeSigned(dwarf2reader::DW_AT_inline, form, type); + } else { + die->ProcessAttributeUnsigned(dwarf2reader::DW_AT_inline, form, type); + } + if (!name.empty()) + die->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + + EXPECT_TRUE(die->EndAttributes()); + die->Finish(); + delete die; +} + +void CUFixtureBase::DefineInlineInstanceDIE(DIEHandler *parent, + const string &name, + uint64 origin, + Module::Address address, + Module::Address size) { + dwarf2reader::AttributeList func_attrs; + if (!name.empty()) + func_attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + func_attrs.push_back(make_pair(dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr)); + func_attrs.push_back(make_pair(dwarf2reader::DW_AT_high_pc, + dwarf2reader::DW_FORM_addr)); + func_attrs.push_back(make_pair(dwarf2reader::DW_AT_abstract_origin, + dwarf2reader::DW_FORM_ref4)); + PushBackStrangeAttributes(&func_attrs); + dwarf2reader::DIEHandler *func + = parent->FindChildHandler(0x11c70f94c6e87ccdLL, + dwarf2reader::DW_TAG_subprogram, + func_attrs); + ASSERT_TRUE(func != NULL); + if (!name.empty()) { + func->ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + name); + } + func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr, + address); + func->ProcessAttributeUnsigned(dwarf2reader::DW_AT_high_pc, + dwarf2reader::DW_FORM_addr, + address + size); + func->ProcessAttributeReference(dwarf2reader::DW_AT_abstract_origin, + dwarf2reader::DW_FORM_ref4, + origin); + ProcessStrangeAttributes(func); + EXPECT_TRUE(func->EndAttributes()); + func->Finish(); + delete func; +} + +void CUFixtureBase::FillFunctions() { + if (functions_filled_) + return; + module_.GetFunctions(&functions_, functions_.end()); + sort(functions_.begin(), functions_.end(), + Module::Function::CompareByAddress); + functions_filled_ = true; +} + +void CUFixtureBase::TestFunctionCount(size_t expected) { + FillFunctions(); + ASSERT_EQ(expected, functions_.size()); +} + +void CUFixtureBase::TestFunction(int i, const string &name, + Module::Address address, + Module::Address size) { + FillFunctions(); + ASSERT_LT((size_t) i, functions_.size()); + + Module::Function *function = functions_[i]; + EXPECT_EQ(name, function->name); + EXPECT_EQ(address, function->address); + EXPECT_EQ(size, function->size); + EXPECT_EQ(0U, function->parameter_size); +} + +void CUFixtureBase::TestLineCount(int i, size_t expected) { + FillFunctions(); + ASSERT_LT((size_t) i, functions_.size()); + + ASSERT_EQ(expected, functions_[i]->lines.size()); +} + +void CUFixtureBase::TestLine(int i, int j, + Module::Address address, Module::Address size, + const string &filename, int number) { + FillFunctions(); + ASSERT_LT((size_t) i, functions_.size()); + ASSERT_LT((size_t) j, functions_[i]->lines.size()); + + Module::Line *line = &functions_[i]->lines[j]; + EXPECT_EQ(address, line->address); + EXPECT_EQ(size, line->size); + EXPECT_EQ(filename, line->file->name.c_str()); + EXPECT_EQ(number, line->number); +} + +// Include caller locations for our test subroutines. +#define TRACE(call) do { SCOPED_TRACE("called from here"); call; } while (0) +#define PushLine(a,b,c,d) TRACE(PushLine((a),(b),(c),(d))) +#define SetLanguage(a) TRACE(SetLanguage(a)) +#define StartCU() TRACE(StartCU()) +#define DefineFunction(a,b,c,d) TRACE(DefineFunction((a),(b),(c),(d))) +#define DeclarationDIE(a,b,c,d) TRACE(DeclarationDIE((a),(b),(c),(d))) +#define DefinitionDIE(a,b,c,d,e,f) TRACE(DefinitionDIE((a),(b),(c),(d),(e),(f))) +#define TestFunctionCount(a) TRACE(TestFunctionCount(a)) +#define TestFunction(a,b,c,d) TRACE(TestFunction((a),(b),(c),(d))) +#define TestLineCount(a,b) TRACE(TestLineCount((a),(b))) +#define TestLine(a,b,c,d,e,f) TRACE(TestLine((a),(b),(c),(d),(e),(f))) + +class Simple: public CUFixtureBase, public Test { +}; + +TEST_F(Simple, OneFunc) { + PushLine(0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", 246571772); + + StartCU(); + DefineFunction(&root_handler_, "function1", + 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "function1", 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL); + TestLineCount(0, 1); + TestLine(0, 0, 0x938cf8c07def4d34ULL, 0x55592d727f6cd01fLL, "line-file", + 246571772); +} + +TEST_F(Simple, IrrelevantRootChildren) { + StartCU(); + dwarf2reader::AttributeList no_attrs; + EXPECT_FALSE(root_handler_ + .FindChildHandler(0x7db32bff4e2dcfb1ULL, + dwarf2reader::DW_TAG_lexical_block, no_attrs)); +} + +TEST_F(Simple, IrrelevantNamedScopeChildren) { + StartCU(); + dwarf2reader::AttributeList no_attrs; + DIEHandler *class_A_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "class_A"); + EXPECT_TRUE(class_A_handler != NULL); + EXPECT_FALSE(class_A_handler + ->FindChildHandler(0x02e55999b865e4e9ULL, + dwarf2reader::DW_TAG_lexical_block, + no_attrs)); + delete class_A_handler; +} + +// Verify that FileContexts can safely be deleted unused. +TEST_F(Simple, UnusedFileContext) { + Module m("module-name", "module-os", "module-arch", "module-id"); + DwarfCUToModule::FileContext fc("dwarf-filename", &m); + + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); +} + +TEST_F(Simple, InlineFunction) { + PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); + + StartCU(); + AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, + dwarf2reader::DW_INL_inlined, 0, "inline-name"); + DefineInlineInstanceDIE(&root_handler_, "", 0x1e8dac5d507ed7abULL, + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "inline-name", + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); +} + +TEST_F(Simple, InlineFunctionSignedAttribute) { + PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); + + StartCU(); + AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, + dwarf2reader::DW_INL_inlined, 0, "inline-name", + dwarf2reader::DW_FORM_sdata); + DefineInlineInstanceDIE(&root_handler_, "", 0x1e8dac5d507ed7abULL, + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "inline-name", + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); +} + +// Any DIE with an DW_AT_inline attribute can be cited by +// DW_AT_abstract_origin attributes --- even if the value of the +// DW_AT_inline attribute is DW_INL_not_inlined. +TEST_F(Simple, AbstractOriginNotInlined) { + PushLine(0x2805c4531be6ca0eULL, 0x686b52155a8d4d2cULL, "line-file", 6111581); + + StartCU(); + AbstractInstanceDIE(&root_handler_, 0x93e9cdad52826b39ULL, + dwarf2reader::DW_INL_not_inlined, 0, "abstract-instance"); + DefineInlineInstanceDIE(&root_handler_, "", 0x93e9cdad52826b39ULL, + 0x2805c4531be6ca0eULL, 0x686b52155a8d4d2cULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "abstract-instance", + 0x2805c4531be6ca0eULL, 0x686b52155a8d4d2cULL); +} + +TEST_F(Simple, UnknownAbstractOrigin) { + EXPECT_CALL(reporter_, UnknownAbstractOrigin(_, 1ULL)).WillOnce(Return()); + PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); + + StartCU(); + AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, + dwarf2reader::DW_INL_inlined, 0, "inline-name"); + DefineInlineInstanceDIE(&root_handler_, "", 1ULL, + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "", + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); +} + +// An address range. +struct Range { + Module::Address start, end; +}; + +// Test data for pairing functions and lines. +struct Situation { + // Two function intervals, and two line intervals. + Range functions[2], lines[2]; + + // The number of lines we expect to be assigned to each of the + // functions, and the address ranges. + int paired_count[2]; + Range paired[2][2]; + + // The number of functions that are not entirely covered by lines, + // and vice versa. + int uncovered_functions, uncovered_lines; +}; + +#define PAIRING(func1_start, func1_end, func2_start, func2_end, \ + line1_start, line1_end, line2_start, line2_end, \ + func1_num_lines, func2_num_lines, \ + func1_line1_start, func1_line1_end, \ + func1_line2_start, func1_line2_end, \ + func2_line1_start, func2_line1_end, \ + func2_line2_start, func2_line2_end, \ + uncovered_functions, uncovered_lines) \ + { { { func1_start, func1_end }, { func2_start, func2_end } }, \ + { { line1_start, line1_end }, { line2_start, line2_end } }, \ + { func1_num_lines, func2_num_lines }, \ + { { { func1_line1_start, func1_line1_end }, \ + { func1_line2_start, func1_line2_end } }, \ + { { func2_line1_start, func2_line1_end }, \ + { func2_line2_start, func2_line2_end } } }, \ + uncovered_functions, uncovered_lines }, + +Situation situations[] = { +#include "common/linux/testdata/func-line-pairing.h" +}; + +#undef PAIRING + +class FuncLinePairing: public CUFixtureBase, + public TestWithParam { }; + +INSTANTIATE_TEST_CASE_P(AllSituations, FuncLinePairing, + ValuesIn(situations)); + +TEST_P(FuncLinePairing, Pairing) { + const Situation &s = GetParam(); + PushLine(s.lines[0].start, + s.lines[0].end - s.lines[0].start, + "line-file", 67636963); + PushLine(s.lines[1].start, + s.lines[1].end - s.lines[1].start, + "line-file", 67636963); + if (s.uncovered_functions) + EXPECT_CALL(reporter_, UncoveredFunction(_)) + .Times(s.uncovered_functions) + .WillRepeatedly(Return()); + if (s.uncovered_lines) + EXPECT_CALL(reporter_, UncoveredLine(_)) + .Times(s.uncovered_lines) + .WillRepeatedly(Return()); + + StartCU(); + DefineFunction(&root_handler_, "function1", + s.functions[0].start, + s.functions[0].end - s.functions[0].start); + DefineFunction(&root_handler_, "function2", + s.functions[1].start, + s.functions[1].end - s.functions[1].start); + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "function1", + s.functions[0].start, + s.functions[0].end - s.functions[0].start); + TestLineCount(0, s.paired_count[0]); + for (int i = 0; i < s.paired_count[0]; i++) + TestLine(0, i, s.paired[0][i].start, + s.paired[0][i].end - s.paired[0][i].start, + "line-file", 67636963); + TestFunction(1, "function2", + s.functions[1].start, + s.functions[1].end - s.functions[1].start); + TestLineCount(1, s.paired_count[1]); + for (int i = 0; i < s.paired_count[1]; i++) + TestLine(1, i, s.paired[1][i].start, + s.paired[1][i].end - s.paired[1][i].start, + "line-file", 67636963); +} + +TEST_F(FuncLinePairing, EmptyCU) { + + StartCU(); + root_handler_.Finish(); + + TestFunctionCount(0); +} + +TEST_F(FuncLinePairing, LinesNoFuncs) { + PushLine(40, 2, "line-file", 82485646); + EXPECT_CALL(reporter_, UncoveredLine(_)).WillOnce(Return()); + + StartCU(); + root_handler_.Finish(); + + TestFunctionCount(0); +} + +TEST_F(FuncLinePairing, FuncsNoLines) { + EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); + + StartCU(); + DefineFunction(&root_handler_, "function1", 0x127da12ffcf5c51fULL, 0x1000U); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "function1", 0x127da12ffcf5c51fULL, 0x1000U); +} + +TEST_F(FuncLinePairing, GapThenFunction) { + PushLine(20, 2, "line-file-2", 174314698); + PushLine(10, 2, "line-file-1", 263008005); + + StartCU(); + DefineFunction(&root_handler_, "function1", 10, 2); + DefineFunction(&root_handler_, "function2", 20, 2); + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "function1", 10, 2); + TestLineCount(0, 1); + TestLine(0, 0, 10, 2, "line-file-1", 263008005); + TestFunction(1, "function2", 20, 2); + TestLineCount(1, 1); + TestLine(1, 0, 20, 2, "line-file-2", 174314698); +} + +// If GCC emits padding after one function to align the start of +// the next, then it will attribute the padding instructions to +// the last source line of function (to reduce the size of the +// line number info), but omit it from the DW_AT_{low,high}_pc +// range given in .debug_info (since it costs nothing to be +// precise there). If we did use at least some of the line +// we're about to skip, then assume this is what happened, and +// don't warn. +TEST_F(FuncLinePairing, GCCAlignmentStretch) { + PushLine(10, 10, "line-file", 63351048); + PushLine(20, 10, "line-file", 61661044); + + StartCU(); + DefineFunction(&root_handler_, "function1", 10, 5); + // five-byte gap between functions, covered by line 63351048. + // This should not elicit a warning. + DefineFunction(&root_handler_, "function2", 20, 10); + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "function1", 10, 5); + TestLineCount(0, 1); + TestLine(0, 0, 10, 5, "line-file", 63351048); + TestFunction(1, "function2", 20, 10); + TestLineCount(1, 1); + TestLine(1, 0, 20, 10, "line-file", 61661044); +} + +// Unfortunately, neither the DWARF parser's handler interface nor the +// DIEHandler interface is capable of expressing a function that abuts +// the end of the address space: the high_pc value looks like zero. + +TEST_F(FuncLinePairing, LineAtEndOfAddressSpace) { + PushLine(0xfffffffffffffff0ULL, 16, "line-file", 63351048); + EXPECT_CALL(reporter_, UncoveredLine(_)).WillOnce(Return()); + + StartCU(); + DefineFunction(&root_handler_, "function1", 0xfffffffffffffff0ULL, 6); + DefineFunction(&root_handler_, "function2", 0xfffffffffffffffaULL, 5); + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "function1", 0xfffffffffffffff0ULL, 6); + TestLineCount(0, 1); + TestLine(0, 0, 0xfffffffffffffff0ULL, 6, "line-file", 63351048); + TestFunction(1, "function2", 0xfffffffffffffffaULL, 5); + TestLineCount(1, 1); + TestLine(1, 0, 0xfffffffffffffffaULL, 5, "line-file", 63351048); +} + +// A function with more than one uncovered area should only be warned +// about once. +TEST_F(FuncLinePairing, WarnOnceFunc) { + PushLine(20, 1, "line-file-2", 262951329); + PushLine(11, 1, "line-file-1", 219964021); + EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); + + StartCU(); + DefineFunction(&root_handler_, "function", 10, 11); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "function", 10, 11); + TestLineCount(0, 2); + TestLine(0, 0, 11, 1, "line-file-1", 219964021); + TestLine(0, 1, 20, 1, "line-file-2", 262951329); +} + +// A line with more than one uncovered area should only be warned +// about once. +TEST_F(FuncLinePairing, WarnOnceLine) { + PushLine(10, 20, "filename1", 118581871); + EXPECT_CALL(reporter_, UncoveredLine(_)).WillOnce(Return()); + + StartCU(); + DefineFunction(&root_handler_, "function1", 11, 1); + DefineFunction(&root_handler_, "function2", 13, 1); + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "function1", 11, 1); + TestLineCount(0, 1); + TestLine(0, 0, 11, 1, "filename1", 118581871); + TestFunction(1, "function2", 13, 1); + TestLineCount(1, 1); + TestLine(1, 0, 13, 1, "filename1", 118581871); +} + +class CXXQualifiedNames: public CUFixtureBase, + public TestWithParam { }; + +INSTANTIATE_TEST_CASE_P(VersusEnclosures, CXXQualifiedNames, + Values(dwarf2reader::DW_TAG_class_type, + dwarf2reader::DW_TAG_structure_type, + dwarf2reader::DW_TAG_union_type, + dwarf2reader::DW_TAG_namespace)); + +TEST_P(CXXQualifiedNames, TwoFunctions) { + DwarfTag tag = GetParam(); + + SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); + PushLine(10, 1, "filename1", 69819327); + PushLine(20, 1, "filename2", 95115701); + + StartCU(); + DIEHandler *enclosure_handler = StartNamedDIE(&root_handler_, tag, + "Enclosure"); + EXPECT_TRUE(enclosure_handler != NULL); + DefineFunction(enclosure_handler, "func_B", 10, 1); + DefineFunction(enclosure_handler, "func_C", 20, 1); + enclosure_handler->Finish(); + delete enclosure_handler; + root_handler_.Finish(); + + TestFunctionCount(2); + TestFunction(0, "Enclosure::func_B", 10, 1); + TestFunction(1, "Enclosure::func_C", 20, 1); +} + +TEST_P(CXXQualifiedNames, FuncInEnclosureInNamespace) { + DwarfTag tag = GetParam(); + + SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); + PushLine(10, 1, "line-file", 69819327); + + StartCU(); + DIEHandler *namespace_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, + "Namespace"); + EXPECT_TRUE(namespace_handler != NULL); + DIEHandler *enclosure_handler = StartNamedDIE(namespace_handler, tag, + "Enclosure"); + EXPECT_TRUE(enclosure_handler != NULL); + DefineFunction(enclosure_handler, "function", 10, 1); + enclosure_handler->Finish(); + delete enclosure_handler; + namespace_handler->Finish(); + delete namespace_handler; + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "Namespace::Enclosure::function", 10, 1); +} + +TEST_F(CXXQualifiedNames, FunctionInClassInStructInNamespace) { + SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); + PushLine(10, 1, "filename1", 69819327); + + StartCU(); + DIEHandler *namespace_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, + "namespace_A"); + EXPECT_TRUE(namespace_handler != NULL); + DIEHandler *struct_handler + = StartNamedDIE(namespace_handler, dwarf2reader::DW_TAG_structure_type, + "struct_B"); + EXPECT_TRUE(struct_handler != NULL); + DIEHandler *class_handler + = StartNamedDIE(struct_handler, dwarf2reader::DW_TAG_class_type, + "class_C"); + DefineFunction(class_handler, "function_D", 10, 1); + class_handler->Finish(); + delete class_handler; + struct_handler->Finish(); + delete struct_handler; + namespace_handler->Finish(); + delete namespace_handler; + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "namespace_A::struct_B::class_C::function_D", 10, 1); +} + +struct LanguageAndQualifiedName { + dwarf2reader::DwarfLanguage language; + const char *name; +}; + +const LanguageAndQualifiedName LanguageAndQualifiedNameCases[] = { + { dwarf2reader::DW_LANG_none, "class_A::function_B" }, + { dwarf2reader::DW_LANG_C, "class_A::function_B" }, + { dwarf2reader::DW_LANG_C89, "class_A::function_B" }, + { dwarf2reader::DW_LANG_C99, "class_A::function_B" }, + { dwarf2reader::DW_LANG_C_plus_plus, "class_A::function_B" }, + { dwarf2reader::DW_LANG_Java, "class_A.function_B" }, + { dwarf2reader::DW_LANG_Cobol74, "class_A::function_B" }, + { dwarf2reader::DW_LANG_Mips_Assembler, NULL } +}; + +class QualifiedForLanguage: + public CUFixtureBase, + public TestWithParam { }; + +INSTANTIATE_TEST_CASE_P(LanguageAndQualifiedName, QualifiedForLanguage, + ValuesIn(LanguageAndQualifiedNameCases)); + +TEST_P(QualifiedForLanguage, MemberFunction) { + const LanguageAndQualifiedName ¶m = GetParam(); + + PushLine(10, 1, "line-file", 212966758); + SetLanguage(param.language); + + StartCU(); + DIEHandler *class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + "class_A"); + DefineFunction(class_handler, "function_B", 10, 1); + class_handler->Finish(); + delete class_handler; + root_handler_.Finish(); + + if (param.name) { + TestFunctionCount(1); + TestFunction(0, param.name, 10, 1); + } else { + TestFunctionCount(0); + } +} + +TEST_P(QualifiedForLanguage, MemberFunctionSignedLanguage) { + const LanguageAndQualifiedName ¶m = GetParam(); + + PushLine(10, 1, "line-file", 212966758); + SetLanguage(param.language); + SetLanguageSigned(true); + + StartCU(); + DIEHandler *class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + "class_A"); + DefineFunction(class_handler, "function_B", 10, 1); + class_handler->Finish(); + delete class_handler; + root_handler_.Finish(); + + if (param.name) { + TestFunctionCount(1); + TestFunction(0, param.name, 10, 1); + } else { + TestFunctionCount(0); + } +} + +class Specifications: public CUFixtureBase, public Test { }; + +TEST_F(Specifications, Function) { + PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661); + + StartCU(); + DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL, + dwarf2reader::DW_TAG_subprogram, "declaration-name"); + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0xcd3c51b946fb1eeeLL, "", + 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "declaration-name", + 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); +} + +TEST_F(Specifications, MemberFunction) { + PushLine(0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL, "line-file", 18116691); + + StartCU(); + DIEHandler *class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "class_A"); + DeclarationDIE(class_handler, 0x7d83028c431406e8ULL, + dwarf2reader::DW_TAG_subprogram, "declaration-name"); + class_handler->Finish(); + delete class_handler; + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0x7d83028c431406e8ULL, "", + 0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "class_A::declaration-name", + 0x3341a248634e7170ULL, 0x5f6938ee5553b953ULL); +} + +// This case should gather the name from both the definition and the +// declaration's parent. +TEST_F(Specifications, FunctionDeclarationParent) { + PushLine(0x463c9ddf405be227ULL, 0x6a47774af5049680ULL, "line-file", 70254922); + + StartCU(); + { + DIEHandler *class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + "class_A"); + ASSERT_TRUE(class_handler != NULL); + DeclarationDIE(class_handler, 0x0e0e877c8404544aULL, + dwarf2reader::DW_TAG_subprogram, "declaration-name"); + class_handler->Finish(); + delete class_handler; + } + + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0x0e0e877c8404544aULL, "definition-name", + 0x463c9ddf405be227ULL, 0x6a47774af5049680ULL); + + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "class_A::definition-name", + 0x463c9ddf405be227ULL, 0x6a47774af5049680ULL); +} + +// Named scopes should also gather enclosing name components from +// their declarations. +TEST_F(Specifications, NamedScopeDeclarationParent) { + PushLine(0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL, "line-file", 77392604); + + StartCU(); + { + DIEHandler *space_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, + "space_A"); + ASSERT_TRUE(space_handler != NULL); + DeclarationDIE(space_handler, 0x419bb1d12f9a73a2ULL, + dwarf2reader::DW_TAG_class_type, "class-declaration-name"); + space_handler->Finish(); + delete space_handler; + } + + { + DIEHandler *class_handler + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + 0x419bb1d12f9a73a2ULL, "class-definition-name"); + ASSERT_TRUE(class_handler != NULL); + DefineFunction(class_handler, "function", + 0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL); + class_handler->Finish(); + delete class_handler; + } + + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "space_A::class-definition-name::function", + 0x5d13433d0df13d00ULL, 0x48ebebe5ade2cab4ULL); +} + +// This test recreates bug 364. +TEST_F(Specifications, InlineFunction) { + PushLine(0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL, "line-file", 75173118); + + StartCU(); + DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL, + dwarf2reader::DW_TAG_subprogram, "inline-name"); + AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, + dwarf2reader::DW_INL_inlined, 0xcd3c51b946fb1eeeLL, ""); + DefineInlineInstanceDIE(&root_handler_, "", 0x1e8dac5d507ed7abULL, + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "inline-name", + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); +} + +// Check name construction for a long chain containing each combination of: +// - struct, union, class, namespace +// - direct and definition +TEST_F(Specifications, LongChain) { + PushLine(0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL, "line-file", 21192926); + SetLanguage(dwarf2reader::DW_LANG_C_plus_plus); + + StartCU(); + // The structure we're building here is: + // space_A full definition + // space_B declaration + // space_B definition + // struct_C full definition + // struct_D declaration + // struct_D definition + // union_E full definition + // union_F declaration + // union_F definition + // class_G full definition + // class_H declaration + // class_H definition + // func_I declaration + // func_I definition + // + // So: + // - space_A, struct_C, union_E, and class_G don't use specifications; + // - space_B, struct_D, union_F, and class_H do. + // - func_I uses a specification. + // + // The full name for func_I is thus: + // + // space_A::space_B::struct_C::struct_D::union_E::union_F:: + // class_G::class_H::func_I + { + DIEHandler *space_A_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, + "space_A"); + DeclarationDIE(space_A_handler, 0x2e111126496596e2ULL, + dwarf2reader::DW_TAG_namespace, "space_B"); + space_A_handler->Finish(); + delete space_A_handler; + } + + { + DIEHandler *space_B_handler + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_namespace, + 0x2e111126496596e2ULL); + DIEHandler *struct_C_handler + = StartNamedDIE(space_B_handler, dwarf2reader::DW_TAG_structure_type, + "struct_C"); + DeclarationDIE(struct_C_handler, 0x20cd423bf2a25a4cULL, + dwarf2reader::DW_TAG_structure_type, "struct_D"); + struct_C_handler->Finish(); + delete struct_C_handler; + space_B_handler->Finish(); + delete space_B_handler; + } + + { + DIEHandler *struct_D_handler + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_structure_type, + 0x20cd423bf2a25a4cULL); + DIEHandler *union_E_handler + = StartNamedDIE(struct_D_handler, dwarf2reader::DW_TAG_union_type, + "union_E"); + DeclarationDIE(union_E_handler, 0xe25c84805aa58c32ULL, + dwarf2reader::DW_TAG_union_type, "union_F"); + union_E_handler->Finish(); + delete union_E_handler; + struct_D_handler->Finish(); + delete struct_D_handler; + } + + { + DIEHandler *union_F_handler + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_union_type, + 0xe25c84805aa58c32ULL); + DIEHandler *class_G_handler + = StartNamedDIE(union_F_handler, dwarf2reader::DW_TAG_class_type, + "class_G"); + DeclarationDIE(class_G_handler, 0xb70d960dcc173b6eULL, + dwarf2reader::DW_TAG_class_type, "class_H"); + class_G_handler->Finish(); + delete class_G_handler; + union_F_handler->Finish(); + delete union_F_handler; + } + + { + DIEHandler *class_H_handler + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + 0xb70d960dcc173b6eULL); + DeclarationDIE(class_H_handler, 0x27ff829e3bf69f37ULL, + dwarf2reader::DW_TAG_subprogram, "func_I"); + class_H_handler->Finish(); + delete class_H_handler; + } + + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0x27ff829e3bf69f37ULL, "", + 0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "space_A::space_B::struct_C::struct_D::union_E::union_F" + "::class_G::class_H::func_I", + 0x5a0dd6bb85db754cULL, 0x3bccb213d08c7fd3ULL); +} + +TEST_F(Specifications, InterCU) { + Module m("module-name", "module-os", "module-arch", "module-id"); + DwarfCUToModule::FileContext fc("dwarf-filename", &m); + EXPECT_CALL(reporter_, UncoveredFunction(_)).WillOnce(Return()); + MockLineToModuleFunctor lr; + EXPECT_CALL(lr, mock_apply(_,_,_,_)).Times(0); + dwarf2reader::AttributeList no_attrs; + + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + // First CU. Declares class_A. + { + DwarfCUToModule root1_handler(&fc, &lr, &reporter_); + ASSERT_TRUE(root1_handler.StartCompilationUnit(0, 1, 2, 3, 3)); + dwarf2reader::AttributeList attrs; + PushBackStrangeAttributes(&attrs); + ASSERT_TRUE(root1_handler.StartRootDIE(1, dwarf2reader::DW_TAG_compile_unit, + attrs)); + ProcessStrangeAttributes(&root1_handler); + ASSERT_TRUE(root1_handler.EndAttributes()); + DeclarationDIE(&root1_handler, 0xb8fbfdd5f0b26fceULL, + dwarf2reader::DW_TAG_class_type, "class_A"); + root1_handler.Finish(); + } + + // Second CU. Defines class_A, declares member_func_B. + { + DwarfCUToModule root2_handler(&fc, &lr, &reporter_); + ASSERT_TRUE(root2_handler.StartCompilationUnit(0, 1, 2, 3, 3)); + ASSERT_TRUE(root2_handler.StartRootDIE(1, dwarf2reader::DW_TAG_compile_unit, + no_attrs)); + ASSERT_TRUE(root2_handler.EndAttributes()); + DIEHandler *class_A_handler + = StartSpecifiedDIE(&root2_handler, dwarf2reader::DW_TAG_class_type, + 0xb8fbfdd5f0b26fceULL); + DeclarationDIE(class_A_handler, 0xb01fef8b380bd1a2ULL, + dwarf2reader::DW_TAG_subprogram, "member_func_B"); + class_A_handler->Finish(); + delete class_A_handler; + root2_handler.Finish(); + } + + // Third CU. Defines member_func_B. + { + DwarfCUToModule root3_handler(&fc, &lr, &reporter_); + ASSERT_TRUE(root3_handler.StartCompilationUnit(0, 1, 2, 3, 3)); + ASSERT_TRUE(root3_handler.StartRootDIE(1, dwarf2reader::DW_TAG_compile_unit, + no_attrs)); + ASSERT_TRUE(root3_handler.EndAttributes()); + DefinitionDIE(&root3_handler, dwarf2reader::DW_TAG_subprogram, + 0xb01fef8b380bd1a2ULL, "", + 0x2618f00a1a711e53ULL, 0x4fd94b76d7c2caf5ULL); + root3_handler.Finish(); + } + + vector functions; + m.GetFunctions(&functions, functions.end()); + EXPECT_EQ(1U, functions.size()); + EXPECT_STREQ("class_A::member_func_B", functions[0]->name.c_str()); +} + +TEST_F(Specifications, BadOffset) { + PushLine(0xa0277efd7ce83771ULL, 0x149554a184c730c1ULL, "line-file", 56636272); + EXPECT_CALL(reporter_, UnknownSpecification(_, 0x2be953efa6f9a996ULL)) + .WillOnce(Return()); + + StartCU(); + DeclarationDIE(&root_handler_, 0xefd7f7752c27b7e4ULL, + dwarf2reader::DW_TAG_subprogram, "function"); + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0x2be953efa6f9a996ULL, "", + 0xa0277efd7ce83771ULL, 0x149554a184c730c1ULL); + root_handler_.Finish(); +} + +TEST_F(Specifications, FunctionDefinitionHasOwnName) { + PushLine(0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL, "line-file", 56792403); + + StartCU(); + DeclarationDIE(&root_handler_, 0xc34ff4786cae78bdULL, + dwarf2reader::DW_TAG_subprogram, "declaration-name"); + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0xc34ff4786cae78bdULL, "definition-name", + 0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "definition-name", + 0xced50b3eea81022cULL, 0x08dd4d301cc7a7d2ULL); +} + +TEST_F(Specifications, ClassDefinitionHasOwnName) { + PushLine(0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL, "line-file", 57119241); + + StartCU(); + DeclarationDIE(&root_handler_, 0xd0fe467ec2f1a58cULL, + dwarf2reader::DW_TAG_class_type, "class-declaration-name"); + + dwarf2reader::DIEHandler *class_definition + = StartSpecifiedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + 0xd0fe467ec2f1a58cULL, "class-definition-name"); + ASSERT_TRUE(class_definition); + DeclarationDIE(class_definition, 0x6d028229c15623dbULL, + dwarf2reader::DW_TAG_subprogram, + "function-declaration-name"); + class_definition->Finish(); + delete class_definition; + + DefinitionDIE(&root_handler_, dwarf2reader::DW_TAG_subprogram, + 0x6d028229c15623dbULL, "function-definition-name", + 0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL); + + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "class-definition-name::function-definition-name", + 0x1d0f5e0f6ce309bdULL, 0x654e1852ec3599e7ULL); +} + +// DIEs that cite a specification should prefer the specification's +// parents over their own when choosing qualified names. In this test, +// we take the name from our definition but the enclosing scope name +// from our declaration. I don't see why they'd ever be different, but +// we want to verify what DwarfCUToModule is looking at. +TEST_F(Specifications, PreferSpecificationParents) { + PushLine(0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL, "line-file", 79488694); + + StartCU(); + { + dwarf2reader::DIEHandler *declaration_class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, "declaration-class"); + DeclarationDIE(declaration_class_handler, 0x9ddb35517455ef7aULL, + dwarf2reader::DW_TAG_subprogram, "function-declaration"); + declaration_class_handler->Finish(); + delete declaration_class_handler; + } + { + dwarf2reader::DIEHandler *definition_class_handler + = StartNamedDIE(&root_handler_, dwarf2reader::DW_TAG_class_type, + "definition-class"); + DefinitionDIE(definition_class_handler, dwarf2reader::DW_TAG_subprogram, + 0x9ddb35517455ef7aULL, "function-definition", + 0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL); + definition_class_handler->Finish(); + delete definition_class_handler; + } + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "declaration-class::function-definition", + 0xbbd9d54dce3b95b7ULL, 0x39188b7b52b0899fULL); +} + +class Errors: public CUFixtureBase, public Test { }; + +TEST_F(Errors, BadStmtList) { + EXPECT_CALL(reporter_, BadLineInfoOffset(dummy_line_size_ + 10)).Times(1); + + ASSERT_TRUE(root_handler_ + .StartCompilationUnit(0xc591d5b037543d7cULL, 0x11, 0xcd, + 0x2d7d19546cf6590cULL, 3)); + dwarf2reader::AttributeList attrs; + attrs.push_back(make_pair(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp)); + attrs.push_back(make_pair(dwarf2reader::DW_AT_stmt_list, + dwarf2reader::DW_FORM_ref4)); + ASSERT_TRUE(root_handler_.StartRootDIE(0xae789dc102cfca54ULL, + dwarf2reader::DW_TAG_compile_unit, + attrs)); + root_handler_.ProcessAttributeString(dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_strp, + "compilation-unit-name"); + root_handler_.ProcessAttributeUnsigned(dwarf2reader::DW_AT_stmt_list, + dwarf2reader::DW_FORM_ref4, + dummy_line_size_ + 10); + root_handler_.EndAttributes(); + root_handler_.Finish(); +} + +TEST_F(Errors, NoLineSection) { + EXPECT_CALL(reporter_, MissingSection(".debug_line")).Times(1); + PushLine(0x88507fb678052611ULL, 0x42c8e9de6bbaa0faULL, "line-file", 64472290); + // Delete the entry for .debug_line added by the fixture class's constructor. + file_context_.section_map.clear(); + + StartCU(); + root_handler_.Finish(); +} + +TEST_F(Errors, BadDwarfVersion1) { + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + ASSERT_FALSE(root_handler_ + .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, + 0xc9de224ccb99ac3eULL, 1)); +} + +TEST_F(Errors, GoodDwarfVersion2) { + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + ASSERT_TRUE(root_handler_ + .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, + 0xc9de224ccb99ac3eULL, 2)); +} + +TEST_F(Errors, GoodDwarfVersion3) { + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + ASSERT_TRUE(root_handler_ + .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, + 0xc9de224ccb99ac3eULL, 3)); +} + +TEST_F(Errors, BadCURootDIETag) { + // Kludge: satisfy reporter_'s expectation. + reporter_.SetCUName("compilation-unit-name"); + + ASSERT_TRUE(root_handler_ + .StartCompilationUnit(0xadf6e0eb71e2b0d9ULL, 0x4d, 0x90, + 0xc9de224ccb99ac3eULL, 3)); + + dwarf2reader::AttributeList no_attrs; + ASSERT_FALSE(root_handler_.StartRootDIE(0x02e56bfbda9e7337ULL, + dwarf2reader::DW_TAG_subprogram, + no_attrs)); +} + +// Would be nice to also test: +// - overlapping lines, functions diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_line_to_module.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_line_to_module.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_line_to_module.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_line_to_module.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,132 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// dwarf_line_to_module.cc: Implementation of DwarfLineToModule class. +// See dwarf_line_to_module.h for details. + +#include "common/linux/dwarf_line_to_module.h" + +// Trying to support Windows paths in a reasonable way adds a lot of +// variations to test; it would be better to just put off dealing with +// it until we actually have to deal with DWARF on Windows. + +// Return true if PATH is an absolute path, false if it is relative. +static bool PathIsAbsolute(const string &path) { + return (path.size() >= 1 && path[0] == '/'); +} + +// If PATH is an absolute path, return PATH. If PATH is a relative path, +// treat it as relative to BASE and return the combined path. +static string ExpandPath(const string &path, const string &base) { + if (PathIsAbsolute(path)) + return path; + return base + "/" + path; +} + +namespace google_breakpad { + +void DwarfLineToModule::DefineDir(const string &name, uint32 dir_num) { + // Directory number zero is reserved to mean the compilation + // directory. Silently ignore attempts to redefine it. + if (dir_num != 0) + directories_[dir_num] = name; +} + +void DwarfLineToModule::DefineFile(const string &name, int32 file_num, + uint32 dir_num, uint64 mod_time, + uint64 length) { + if (file_num == -1) + file_num = ++highest_file_number_; + else if (file_num > highest_file_number_) + highest_file_number_ = file_num; + + std::string full_name; + if (dir_num != 0) { + DirectoryTable::const_iterator directory_it = directories_.find(dir_num); + if (directory_it != directories_.end()) { + full_name = ExpandPath(name, directory_it->second); + } else { + if (!warned_bad_directory_number_) { + fprintf(stderr, "warning: DWARF line number data refers to undefined" + " directory numbers\n"); + warned_bad_directory_number_ = true; + } + full_name = name; // just treat name as relative + } + } else { + // Directory number zero is the compilation directory; we just report + // relative paths in that case. + full_name = name; + } + + // Find a Module::File object of the given name, and add it to the + // file table. + files_[file_num] = module_->FindFile(full_name); +} + +void DwarfLineToModule::AddLine(uint64 address, uint64 length, + uint32 file_num, uint32 line_num, + uint32 column_num) { + if (length == 0) + return; + + // Clip lines not to extend beyond the end of the address space. + if (address + length < address) + length = -address; + + // Should we omit this line? (See the comments for omitted_line_end_.) + if (address == 0 || address == omitted_line_end_) { + omitted_line_end_ = address + length; + return; + } else { + omitted_line_end_ = 0; + } + + // Find the source file being referred to. + Module::File *file = files_[file_num]; + if (!file) { + if (!warned_bad_file_number_) { + fprintf(stderr, "warning: DWARF line number data refers to " + "undefined file numbers\n"); + warned_bad_file_number_ = true; + } + return; + } + Module::Line line; + line.address = address; + // We set the size when we get the next line or the EndSequence call. + line.size = length; + line.file = file; + line.number = line_num; + lines_->push_back(line); +} + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_line_to_module.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_line_to_module.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_line_to_module.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_line_to_module.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,179 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// The DwarfLineToModule class accepts line number information from a +// DWARF parser and adds it to a google_breakpad::Module. The Module +// can write that data out as a Breakpad symbol file. + +#ifndef COMMON_LINUX_DWARF_LINE_TO_MODULE_H +#define COMMON_LINUX_DWARF_LINE_TO_MODULE_H + +#include "common/linux/module.h" +#include "common/dwarf/dwarf2reader.h" + +namespace google_breakpad { + +// A class for producing a vector of google_breakpad::Module::Line +// instances from parsed DWARF line number data. +// +// An instance of this class can be provided as a handler to a +// dwarf2reader::LineInfo DWARF line number information parser. The +// handler accepts source location information from the parser and +// uses it to produce a vector of google_breakpad::Module::Line +// objects, referring to google_breakpad::Module::File objects added +// to a particular google_breakpad::Module. +// +// GNU toolchain omitted sections support: +// ====================================== +// +// Given the right options, the GNU toolchain will omit unreferenced +// functions from the final executable. Unfortunately, when it does so, it +// does not remove the associated portions of the DWARF line number +// program; instead, it gives the DW_LNE_set_address instructions referring +// to the now-deleted code addresses of zero. Given this input, the DWARF +// line parser will call AddLine with a series of lines starting at address +// zero. For example, here is the output from 'readelf -wl' for a program +// with four functions, the first three of which have been omitted: +// +// Line Number Statements: +// Extended opcode 2: set Address to 0x0 +// Advance Line by 14 to 15 +// Copy +// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 16 +// Special opcode 119: advance Address by 8 to 0xb and Line by 2 to 18 +// Advance PC by 2 to 0xd +// Extended opcode 1: End of Sequence +// +// Extended opcode 2: set Address to 0x0 +// Advance Line by 14 to 15 +// Copy +// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 16 +// Special opcode 119: advance Address by 8 to 0xb and Line by 2 to 18 +// Advance PC by 2 to 0xd +// Extended opcode 1: End of Sequence +// +// Extended opcode 2: set Address to 0x0 +// Advance Line by 19 to 20 +// Copy +// Special opcode 48: advance Address by 3 to 0x3 and Line by 1 to 21 +// Special opcode 76: advance Address by 5 to 0x8 and Line by 1 to 22 +// Advance PC by 2 to 0xa +// Extended opcode 1: End of Sequence +// +// Extended opcode 2: set Address to 0x80483a4 +// Advance Line by 23 to 24 +// Copy +// Special opcode 202: advance Address by 14 to 0x80483b2 and Line by 1 to 25 +// Special opcode 76: advance Address by 5 to 0x80483b7 and Line by 1 to 26 +// Advance PC by 6 to 0x80483bd +// Extended opcode 1: End of Sequence +// +// Instead of collecting runs of lines describing code that is not there, +// we try to recognize and drop them. Since the linker doesn't explicitly +// distinguish references to dropped sections from genuine references to +// code at address zero, we must use a heuristic. We have chosen: +// +// - If a line starts at address zero, omit it. (On the platforms +// breakpad targets, it is extremely unlikely that there will be code +// at address zero.) +// +// - If a line starts immediately after an omitted line, omit it too. +class DwarfLineToModule: public dwarf2reader::LineInfoHandler { + public: + // As the DWARF line info parser passes us line records, add source + // files to MODULE, and add all lines to the end of LINES. LINES + // need not be empty. If the parser hands us a zero-length line, we + // omit it. If the parser hands us a line that extends beyond the + // end of the address space, we clip it. It's up to our client to + // sort out which lines belong to which functions; we don't add them + // to any particular function in MODULE ourselves. + DwarfLineToModule(Module *module, vector *lines) + : module_(module), + lines_(lines), + highest_file_number_(-1), + omitted_line_end_(0), + warned_bad_file_number_(false), + warned_bad_directory_number_(false) { } + + ~DwarfLineToModule() { } + + void DefineDir(const std::string &name, uint32 dir_num); + void DefineFile(const std::string &name, int32 file_num, + uint32 dir_num, uint64 mod_time, + uint64 length); + void AddLine(uint64 address, uint64 length, + uint32 file_num, uint32 line_num, uint32 column_num); + + private: + + typedef std::map DirectoryTable; + typedef std::map FileTable; + + // The module we're contributing debugging info to. Owned by our + // client. + Module *module_; + + // The vector of lines we're accumulating. Owned by our client. + // + // In a Module, as in a breakpad symbol file, lines belong to + // specific functions, but DWARF simply assigns lines to addresses; + // one must infer the line/function relationship using the + // functions' beginning and ending addresses. So we can't add these + // to the appropriate function from module_ until we've read the + // function info as well. Instead, we accumulate lines here, and let + // whoever constructed this sort it all out. + vector *lines_; + + // A table mapping directory numbers to paths. + DirectoryTable directories_; + + // A table mapping file numbers to Module::File pointers. + FileTable files_; + + // The highest file number we've seen so far, or -1 if we've seen + // none. Used for dynamically defined file numbers. + int32 highest_file_number_; + + // This is the ending address of the last line we omitted, or zero if we + // didn't omit the previous line. It is zero before we have received any + // AddLine calls. + uint64 omitted_line_end_; + + // True if we've warned about: + bool warned_bad_file_number_; // bad file numbers + bool warned_bad_directory_number_; // bad directory numbers +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DWARF_LINE_TO_MODULE_H diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_line_to_module_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_line_to_module_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_line_to_module_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/dwarf_line_to_module_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,339 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// dwarf_line_to_module.cc: Unit tests for google_breakpad::DwarfLineToModule. + +#include "breakpad_googletest_includes.h" +#include "common/linux/dwarf_line_to_module.h" + +using google_breakpad::DwarfLineToModule; +using google_breakpad::Module; +using google_breakpad::Module; + +TEST(Simple, One) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineFile("file1", 0x30bf0f27, 0, 0, 0); + h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27, + 0x4c090cbf, 0x1cf9fe0d); + + vector files; + m.GetFiles(&files); + EXPECT_EQ(1U, files.size()); + EXPECT_STREQ("file1", files[0]->name.c_str()); + + EXPECT_EQ(1U, lines.size()); + EXPECT_EQ(0x6fd126fbf74f2680ULL, lines[0].address); + EXPECT_EQ(0x63c9a14cf556712bULL, lines[0].size); + EXPECT_TRUE(lines[0].file == files[0]); + EXPECT_EQ(0x4c090cbf, lines[0].number); +} + +TEST(Simple, Many) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineDir("directory1", 0x838299ab); + h.DefineDir("directory2", 0xf85de023); + h.DefineFile("file1", 0x2b80377a, 0x838299ab, 0, 0); + h.DefineFile("file1", 0x63beb4a4, 0xf85de023, 0, 0); + h.DefineFile("file2", 0x1d161d56, 0x838299ab, 0, 0); + h.DefineFile("file2", 0x1e7a667c, 0xf85de023, 0, 0); + h.AddLine(0x69900c5d553b7274ULL, 0x90fded183f0d0d3cULL, 0x2b80377a, + 0x15b0f0a9U, 0x3ff5abd6U); + h.AddLine(0x45811219a39b7101ULL, 0x25a5e6a924afc41fULL, 0x63beb4a4, + 0x4d259ce9U, 0x41c5ee32U); + h.AddLine(0xfa90514c1dc9704bULL, 0x0063efeabc02f313ULL, 0x1d161d56, + 0x1ee9fa4fU, 0xbf70e46aU); + h.AddLine(0x556b55fb6a647b10ULL, 0x3f3089ca2bfd80f5ULL, 0x1e7a667c, + 0x77fc280eU, 0x2c4a728cU); + h.DefineFile("file3", -1, 0, 0, 0); + h.AddLine(0xe2d72a37f8d9403aULL, 0x034dfab5b0d4d236ULL, 0x63beb4a5, + 0x75047044U, 0xb6a0016cU); + + vector files; + m.GetFiles(&files); + ASSERT_EQ(5U, files.size()); + EXPECT_STREQ("directory1/file1", files[0]->name.c_str()); + EXPECT_STREQ("directory1/file2", files[1]->name.c_str()); + EXPECT_STREQ("directory2/file1", files[2]->name.c_str()); + EXPECT_STREQ("directory2/file2", files[3]->name.c_str()); + EXPECT_STREQ("file3", files[4]->name.c_str()); + + ASSERT_EQ(5U, lines.size()); + + EXPECT_EQ(0x69900c5d553b7274ULL, lines[0].address); + EXPECT_EQ(0x90fded183f0d0d3cULL, lines[0].size); + EXPECT_TRUE(lines[0].file == files[0]); + EXPECT_EQ(0x15b0f0a9, lines[0].number); + + EXPECT_EQ(0x45811219a39b7101ULL, lines[1].address); + EXPECT_EQ(0x25a5e6a924afc41fULL, lines[1].size); + EXPECT_TRUE(lines[1].file == files[2]); + EXPECT_EQ(0x4d259ce9, lines[1].number); + + EXPECT_EQ(0xfa90514c1dc9704bULL, lines[2].address); + EXPECT_EQ(0x0063efeabc02f313ULL, lines[2].size); + EXPECT_TRUE(lines[2].file == files[1]); + EXPECT_EQ(0x1ee9fa4f, lines[2].number); + + EXPECT_EQ(0x556b55fb6a647b10ULL, lines[3].address); + EXPECT_EQ(0x3f3089ca2bfd80f5ULL, lines[3].size); + EXPECT_TRUE(lines[3].file == files[3]); + EXPECT_EQ(0x77fc280e, lines[3].number); + + EXPECT_EQ(0xe2d72a37f8d9403aULL, lines[4].address); + EXPECT_EQ(0x034dfab5b0d4d236ULL, lines[4].size); + EXPECT_TRUE(lines[4].file == files[4]); + EXPECT_EQ(0x75047044, lines[4].number); +} + +TEST(Filenames, Absolute) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineDir("directory1", 1); + h.DefineFile("/absolute", 1, 1, 0, 0); + + h.AddLine(1, 1, 1, 0, 0); + + vector files; + m.GetFiles(&files); + ASSERT_EQ(1U, files.size()); + EXPECT_STREQ("/absolute", files[0]->name.c_str()); + ASSERT_EQ(1U, lines.size()); + EXPECT_TRUE(lines[0].file == files[0]); +} + +TEST(Filenames, Relative) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineDir("directory1", 1); + h.DefineFile("relative", 1, 1, 0, 0); + + h.AddLine(1, 1, 1, 0, 0); + + vector files; + m.GetFiles(&files); + ASSERT_EQ(1U, files.size()); + EXPECT_STREQ("directory1/relative", files[0]->name.c_str()); + ASSERT_EQ(1U, lines.size()); + EXPECT_TRUE(lines[0].file == files[0]); +} + +TEST(Filenames, StrangeFile) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineDir("directory1", 1); + h.DefineFile("", 1, 1, 0, 0); + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("directory1/", lines[0].file->name.c_str()); +} + +TEST(Filenames, StrangeDirectory) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineDir("", 1); + h.DefineFile("file1", 1, 1, 0, 0); + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("/file1", lines[0].file->name.c_str()); +} + +TEST(Filenames, StrangeDirectoryAndFile) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineDir("", 1); + h.DefineFile("", 1, 1, 0, 0); + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("/", lines[0].file->name.c_str()); +} + +// We should silently ignore attempts to define directory number zero, +// since that is always the compilation directory. +TEST(Errors, DirectoryZero) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineDir("directory0", 0); // should be ignored + h.DefineFile("relative", 1, 0, 0, 0); + + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("relative", lines[0].file->name.c_str()); +} + +// We should refuse to add lines with bogus file numbers. We should +// produce only one warning, however. +TEST(Errors, BadFileNumber) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineFile("relative", 1, 0, 0, 0); + h.AddLine(1, 1, 2, 0, 0); // bad file number + h.AddLine(2, 1, 2, 0, 0); // bad file number (no duplicate warning) + + EXPECT_EQ(0U, lines.size()); +} + +// We should treat files with bogus directory numbers as relative to +// the compilation unit. +TEST(Errors, BadDirectoryNumber) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineDir("directory1", 1); + h.DefineFile("baddirnumber1", 1, 2, 0, 0); // bad directory number + h.DefineFile("baddirnumber2", 2, 2, 0, 0); // bad dir number (no warning) + h.AddLine(1, 1, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_STREQ("baddirnumber1", lines[0].file->name.c_str()); +} + +// We promise not to report empty lines. +TEST(Errors, EmptyLine) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(1, 0, 1, 0, 0); + + ASSERT_EQ(0U, lines.size()); +} + +// We are supposed to clip lines that extend beyond the end of the +// address space. +TEST(Errors, BigLine) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(0xffffffffffffffffULL, 2, 1, 0, 0); + + ASSERT_EQ(1U, lines.size()); + EXPECT_EQ(1U, lines[0].size); +} + +// The 'Omitted' tests verify that we correctly omit line information +// for code in sections that the linker has dropped. See "GNU +// toolchain omitted sections support" at the top of the +// DwarfLineToModule class. + +TEST(Omitted, DroppedThenGood) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(0, 10, 1, 83816211, 0); // should be omitted + h.AddLine(20, 10, 1, 13059195, 0); // should be recorded + + ASSERT_EQ(1U, lines.size()); + EXPECT_EQ(13059195, lines[0].number); +} + +TEST(Omitted, GoodThenDropped) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(0x9dd6a372, 10, 1, 41454594, 0); // should be recorded + h.AddLine(0, 10, 1, 44793413, 0); // should be omitted + + ASSERT_EQ(1U, lines.size()); + EXPECT_EQ(41454594, lines[0].number); +} + +TEST(Omitted, Mix1) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(0x679ed72f, 10, 1, 58932642, 0); // should be recorded + h.AddLine(0xdfb5a72d, 10, 1, 39847385, 0); // should be recorded + h.AddLine(0, 0x78, 1, 23053829, 0); // should be omitted + h.AddLine(0x78, 0x6a, 1, 65317783, 0); // should be omitted + h.AddLine(0x78 + 0x6a, 0x2a, 1, 77601423, 0); // should be omitted + h.AddLine(0x9fe0cea5, 10, 1, 91806582, 0); // should be recorded + h.AddLine(0x7e41a109, 10, 1, 56169221, 0); // should be recorded + + ASSERT_EQ(4U, lines.size()); + EXPECT_EQ(58932642, lines[0].number); + EXPECT_EQ(39847385, lines[1].number); + EXPECT_EQ(91806582, lines[2].number); + EXPECT_EQ(56169221, lines[3].number); +} + +TEST(Omitted, Mix2) { + Module m("name", "os", "architecture", "id"); + vector lines; + DwarfLineToModule h(&m, &lines); + + h.DefineFile("filename1", 1, 0, 0, 0); + h.AddLine(0, 0xf2, 1, 58802211, 0); // should be omitted + h.AddLine(0xf2, 0xb9, 1, 78958222, 0); // should be omitted + h.AddLine(0xf2 + 0xb9, 0xf7, 1, 64861892, 0); // should be omitted + h.AddLine(0x4e4d271e, 9, 1, 67355743, 0); // should be recorded + h.AddLine(0xdfb5a72d, 30, 1, 23365776, 0); // should be recorded + h.AddLine(0, 0x64, 1, 76196762, 0); // should be omitted + h.AddLine(0x64, 0x33, 1, 71066611, 0); // should be omitted + h.AddLine(0x64 + 0x33, 0xe3, 1, 61749337, 0); // should be omitted + + ASSERT_EQ(2U, lines.size()); + EXPECT_EQ(67355743, lines[0].number); + EXPECT_EQ(23365776, lines[1].number); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/eintr_wrapper.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/eintr_wrapper.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/eintr_wrapper.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/eintr_wrapper.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,47 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef COMMON_LINUX_EINTR_WRAPPER_H_ +#define COMMON_LINUX_EINTR_WRAPPER_H_ + +#include + +// This provides a wrapper around system calls which may be interrupted by a +// signal and return EINTR. See man 7 signal. +// + +#define HANDLE_EINTR(x) ({ \ + typeof(x) __eintr_result__; \ + do { \ + __eintr_result__ = x; \ + } while (__eintr_result__ == -1 && errno == EINTR); \ + __eintr_result__;\ +}) + +#endif // ifndef COMMON_LINUX_EINTR_WRAPPER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.cc 2010-04-16 17:32:47.000000000 +0100 @@ -32,104 +32,138 @@ // See file_id.h for documentation // -#include -#include +#include "common/linux/file_id.h" +#include "common/linux/linux_libc_support.h" +#include "common/linux/linux_syscall_support.h" + +#include #include #include #include -#include #include +#include #include -#include "common/linux/file_id.h" -#include "common/md5.h" +#include +#include +#include namespace google_breakpad { -static bool FindElfTextSection(const void *elf_mapped_base, - const void **text_start, - int *text_size) { +FileID::FileID(const char* path) { + strncpy(path_, path, sizeof(path_)); +} + +// These two functions are also used inside the crashed process, so be safe +// and use the syscall/libc wrappers instead of direct syscalls or libc. + static bool FindElfTextSection(const void *elf_mapped_base, + const void **text_start, + int *text_size) { assert(elf_mapped_base); assert(text_start); assert(text_size); - const unsigned char *elf_base = - static_cast(elf_mapped_base); - const ElfW(Ehdr) *elf_header = - reinterpret_cast(elf_base); - if (memcmp(elf_header, ELFMAG, SELFMAG) != 0) + const char* elf_base = + static_cast(elf_mapped_base); + const ElfW(Ehdr)* elf_header = + reinterpret_cast(elf_base); + if (my_strncmp(elf_base, ELFMAG, SELFMAG) != 0) + return false; +#if __ELF_NATIVE_CLASS == 32 +#define ELFCLASS ELFCLASS32 +#else +#define ELFCLASS ELFCLASS64 +#endif + //TODO: support dumping 32-bit binaries from a 64-bit dump_syms? + if (elf_header->e_ident[EI_CLASS] != ELFCLASS) return false; *text_start = NULL; *text_size = 0; - const ElfW(Shdr) *sections = - reinterpret_cast(elf_base + elf_header->e_shoff); - const char *text_section_name = ".text"; - int name_len = strlen(text_section_name); - const ElfW(Shdr) *string_section = sections + elf_header->e_shstrndx; - const ElfW(Shdr) *text_section = NULL; + const ElfW(Shdr)* sections = + reinterpret_cast(elf_base + elf_header->e_shoff); + const char* text_section_name = ".text"; + int name_len = my_strlen(text_section_name); + const ElfW(Shdr)* string_section = sections + elf_header->e_shstrndx; + const ElfW(Shdr)* text_section = NULL; for (int i = 0; i < elf_header->e_shnum; ++i) { if (sections[i].sh_type == SHT_PROGBITS) { - const char *section_name = (char*)(elf_base + + const char* section_name = (char*)(elf_base + string_section->sh_offset + sections[i].sh_name); - if (!strncmp(section_name, text_section_name, name_len)) { + if (!my_strncmp(section_name, text_section_name, name_len)) { text_section = §ions[i]; break; } } } if (text_section != NULL && text_section->sh_size > 0) { - int text_section_size = text_section->sh_size; *text_start = elf_base + text_section->sh_offset; - *text_size = text_section_size; + *text_size = text_section->sh_size; } return true; } -FileID::FileID(const char *path) { - strncpy(path_, path, sizeof(path_)); +// static +bool FileID::ElfFileIdentifierFromMappedFile(void* base, + uint8_t identifier[kMDGUIDSize]) +{ + const void* text_section = NULL; + int text_size = 0; + bool success = false; + if (FindElfTextSection(base, &text_section, &text_size) && (text_size > 0)) { + my_memset(identifier, 0, kMDGUIDSize); + const uint8_t* ptr = reinterpret_cast(text_section); + const uint8_t* ptr_end = ptr + std::min(text_size, 4096); + while (ptr < ptr_end) { + for (unsigned i = 0; i < kMDGUIDSize; i++) + identifier[i] ^= ptr[i]; + ptr += kMDGUIDSize; + } + success = true; + } + return success; } -bool FileID::ElfFileIdentifier(unsigned char identifier[16]) { +bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) { int fd = open(path_, O_RDONLY); if (fd < 0) return false; struct stat st; - if (fstat(fd, &st) != 0 && st.st_size <= 0) { + if (fstat(fd, &st) != 0) { close(fd); return false; } - void *base = mmap(NULL, st.st_size, + void* base = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); - if (!base) { - close(fd); + close(fd); + if (base == MAP_FAILED) return false; - } - bool success = false; - const void *text_section = NULL; - int text_size = 0; - if (FindElfTextSection(base, &text_section, &text_size) && (text_size > 0)) { - struct MD5Context md5; - MD5Init(&md5); - MD5Update(&md5, - static_cast(text_section), - text_size); - MD5Final(identifier, &md5); - success = true; - } - close(fd); + bool success = ElfFileIdentifierFromMappedFile(base, identifier); munmap(base, st.st_size); return success; } // static -void FileID::ConvertIdentifierToString(const unsigned char identifier[16], - char *buffer, int buffer_length) { +void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize], + char* buffer, int buffer_length) { + uint8_t identifier_swapped[kMDGUIDSize]; + + // Endian-ness swap to match dump processor expectation. + memcpy(identifier_swapped, identifier, kMDGUIDSize); + uint32_t* data1 = reinterpret_cast(identifier_swapped); + *data1 = htonl(*data1); + uint16_t* data2 = reinterpret_cast(identifier_swapped + 4); + *data2 = htons(*data2); + uint16_t* data3 = reinterpret_cast(identifier_swapped + 6); + *data3 = htons(*data3); + int buffer_idx = 0; - for (int idx = 0; (buffer_idx < buffer_length) && (idx < 16); ++idx) { - int hi = (identifier[idx] >> 4) & 0x0F; - int lo = (identifier[idx]) & 0x0F; + for (unsigned int idx = 0; + (buffer_idx < buffer_length) && (idx < kMDGUIDSize); + ++idx) { + int hi = (identifier_swapped[idx] >> 4) & 0x0F; + int lo = (identifier_swapped[idx]) & 0x0F; if (idx == 4 || idx == 6 || idx == 8 || idx == 10) buffer[buffer_idx++] = '-'; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/file_id.h 2010-04-16 17:32:47.000000000 +0100 @@ -35,25 +35,36 @@ #include +#include "common/linux/guid_creator.h" + namespace google_breakpad { +static const size_t kMDGUIDSize = sizeof(MDGUID); + class FileID { public: - FileID(const char *path); - ~FileID() {}; + explicit FileID(const char* path); + ~FileID() {} // Load the identifier for the elf file path specified in the constructor into // |identifier|. Return false if the identifier could not be created for the // file. - // The current implementation will return the MD5 hash of the file's bytes. - bool ElfFileIdentifier(unsigned char identifier[16]); + // The current implementation will XOR the first 4096 bytes of the + // .text section to generate an identifier. + bool ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]); + + // Load the identifier for the elf file mapped into memory at |base| into + // |identifier|. Return false if the identifier could not be created for the + // file. + static bool ElfFileIdentifierFromMappedFile(void* base, + uint8_t identifier[kMDGUIDSize]); // Convert the |identifier| data to a NULL terminated string. The string will // be formatted as a UUID (e.g., 22F065BB-FC9C-49F7-80FE-26A7CEBD7BCE). // The |buffer| should be at least 37 bytes long to receive all of the data // and termination. Shorter buffers will contain truncated data. - static void ConvertIdentifierToString(const unsigned char identifier[16], - char *buffer, int buffer_length); + static void ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize], + char* buffer, int buffer_length); private: // Storage for the path specified @@ -63,4 +74,3 @@ } // namespace google_breakpad #endif // COMMON_LINUX_FILE_ID_H__ - diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/file_id_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/file_id_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/file_id_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/file_id_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,76 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Unit tests for FileID + +#include + +#include "common/linux/file_id.h" +#include "breakpad_googletest_includes.h" + +using namespace google_breakpad; + + +namespace { +typedef testing::Test FileIDTest; +} + +TEST(FileIDTest, FileIDStrip) { + // Calculate the File ID of our binary using + // FileID::ElfFileIdentifier, then make a copy of our binary, + // strip it, and ensure that we still get the same result. + char exe_name[PATH_MAX]; + ssize_t len = readlink("/proc/self/exe", exe_name, PATH_MAX - 1); + ASSERT_NE(len, -1); + exe_name[len] = '\0'; + + // copy our binary to a temp file, and strip it + char templ[] = "/tmp/file-id-unittest-XXXXXX"; + mktemp(templ); + char cmdline[4096]; + sprintf(cmdline, "cp \"%s\" \"%s\"", exe_name, templ); + ASSERT_EQ(system(cmdline), 0); + sprintf(cmdline, "strip \"%s\"", templ); + ASSERT_EQ(system(cmdline), 0); + + uint8_t identifier1[sizeof(MDGUID)]; + uint8_t identifier2[sizeof(MDGUID)]; + FileID fileid1(exe_name); + EXPECT_TRUE(fileid1.ElfFileIdentifier(identifier1)); + FileID fileid2(templ); + EXPECT_TRUE(fileid2.ElfFileIdentifier(identifier2)); + char identifier_string1[37]; + char identifier_string2[37]; + FileID::ConvertIdentifierToString(identifier1, identifier_string1, + 37); + FileID::ConvertIdentifierToString(identifier2, identifier_string2, + 37); + EXPECT_STREQ(identifier_string1, identifier_string2); + unlink(templ); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,196 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +#include "common/linux/google_crashdump_uploader.h" +#include "common/linux/libcurl_wrapper.h" +#include "third_party/linux/include/glog/logging.h" + +#include +#include +#include + +namespace google_breakpad { + +GoogleCrashdumpUploader::GoogleCrashdumpUploader(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword) { + LibcurlWrapper* http_layer = new LibcurlWrapper(); + Init(product, + version, + guid, + ptime, + ctime, + email, + comments, + minidump_pathname, + crash_server, + proxy_host, + proxy_userpassword, + http_layer); +} + +GoogleCrashdumpUploader::GoogleCrashdumpUploader(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword, + LibcurlWrapper* http_layer) { + Init(product, + version, + guid, + ptime, + ctime, + email, + comments, + minidump_pathname, + crash_server, + proxy_host, + proxy_userpassword, + http_layer); +} + +void GoogleCrashdumpUploader::Init(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword, + LibcurlWrapper* http_layer) { + product_ = product; + version_ = version; + guid_ = guid; + ptime_ = ptime; + ctime_ = ctime; + email_ = email; + comments_ = comments; + http_layer_ = http_layer; + + crash_server_ = crash_server; + proxy_host_ = proxy_host; + proxy_userpassword_ = proxy_userpassword; + minidump_pathname_ = minidump_pathname; + LOG(INFO) << "Uploader initializing"; + LOG(INFO) << "\tProduct: " << product_; + LOG(INFO) << "\tVersion: " << version_; + LOG(INFO) << "\tGUID: " << guid_; + if (!ptime_.empty()) { + LOG(INFO) << "\tProcess uptime: " << ptime_; + } + if (!ctime_.empty()) { + LOG(INFO) << "\tCumulative Process uptime: " << ctime_; + } + if (!email_.empty()) { + LOG(INFO) << "\tEmail: " << email_; + } + if (!comments_.empty()) { + LOG(INFO) << "\tComments: " << comments_; + } +} + +bool GoogleCrashdumpUploader::CheckRequiredParametersArePresent() { + std::string error_text; + if (product_.empty()) { + error_text.append("\nProduct name must be specified."); + } + + if (version_.empty()) { + error_text.append("\nProduct version must be specified."); + } + + if (guid_.empty()) { + error_text.append("\nClient ID must be specified."); + } + + if (minidump_pathname_.empty()) { + error_text.append("\nMinidump pathname must be specified."); + } + + if (!error_text.empty()) { + LOG(ERROR) << error_text; + return false; + } + return true; + +} + +bool GoogleCrashdumpUploader::Upload() { + bool ok = http_layer_->Init(); + if (!ok) { + LOG(WARNING) << "http layer init failed"; + return ok; + } + + if (!CheckRequiredParametersArePresent()) { + return false; + } + + struct stat st; + int err = stat(minidump_pathname_.c_str(), &st); + if (err) { + LOG(WARNING) << minidump_pathname_ << " could not be found: " << errno; + return false; + } + + parameters_["prod"] = product_; + parameters_["ver"] = version_; + parameters_["guid"] = guid_; + parameters_["ptime"] = ptime_; + parameters_["ctime"] = ctime_; + parameters_["email"] = email_; + parameters_["comments_"] = comments_; + if (!http_layer_->AddFile(minidump_pathname_, + "upload_file_minidump")) { + return false; + } + LOG(INFO) << "Sending request to " << crash_server_; + return http_layer_->SendRequest(crash_server_, + parameters_, + NULL); +} +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,98 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +#include +#include + +namespace google_breakpad { + +class LibcurlWrapper; + +class GoogleCrashdumpUploader { + public: + GoogleCrashdumpUploader(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword); + + GoogleCrashdumpUploader(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword, + LibcurlWrapper* http_layer); + + void Init(const std::string& product, + const std::string& version, + const std::string& guid, + const std::string& ptime, + const std::string& ctime, + const std::string& email, + const std::string& comments, + const std::string& minidump_pathname, + const std::string& crash_server, + const std::string& proxy_host, + const std::string& proxy_userpassword, + LibcurlWrapper* http_layer); + bool Upload(); + + private: + bool CheckRequiredParametersArePresent(); + + LibcurlWrapper* http_layer_; + std::string product_; + std::string version_; + std::string guid_; + std::string ptime_; + std::string ctime_; + std::string email_; + std::string comments_; + std::string minidump_pathname_; + + std::string crash_server_; + std::string proxy_host_; + std::string proxy_userpassword_; + + std::map parameters_; +}; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/google_crashdump_uploader_test.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,166 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Unit test for crash dump uploader. + +#include "common/linux/google_crashdump_uploader.h" +#include "common/linux/libcurl_wrapper.h" +#include "breakpad_googletest_includes.h" + +namespace google_breakpad { + +using ::testing::Return; +using ::testing::_; + +class MockLibcurlWrapper : public LibcurlWrapper { + public: + MOCK_METHOD0(Init, bool()); + MOCK_METHOD2(SetProxy, bool(const std::string& proxy_host, + const std::string& proxy_userpwd)); + MOCK_METHOD2(AddFile, bool(const std::string& upload_file_path, + const std::string& basename)); + MOCK_METHOD3(SendRequest, + bool(const std::string& url, + const std::map& parameters, + std::string* server_response)); +}; + +class GoogleCrashdumpUploaderTest : public ::testing::Test { +}; + +TEST_F(GoogleCrashdumpUploaderTest, InitFailsCausesUploadFailure) { + MockLibcurlWrapper m; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(false)); + GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", + "1.0", + "AAA-BBB", + "", + "", + "test@test.com", + "none", + "/tmp/foo.dmp", + "http://foo.com", + "", + "", + &m); + ASSERT_FALSE(uploader->Upload()); +} + +TEST_F(GoogleCrashdumpUploaderTest, TestSendRequestHappensWithValidParameters) { + // Create a temp file + char tempfn[80] = "/tmp/googletest-upload-XXXXXX"; + int fd = mkstemp(tempfn); + ASSERT_NE(fd, -1); + close(fd); + + MockLibcurlWrapper m; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); + EXPECT_CALL(m, AddFile(tempfn, _)).WillOnce(Return(true)); + EXPECT_CALL(m, + SendRequest("http://foo.com",_,_)).Times(1).WillOnce(Return(true)); + GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", + "1.0", + "AAA-BBB", + "", + "", + "test@test.com", + "none", + tempfn, + "http://foo.com", + "", + "", + &m); + ASSERT_TRUE(uploader->Upload()); +} + + +TEST_F(GoogleCrashdumpUploaderTest, InvalidPathname) { + MockLibcurlWrapper m; + EXPECT_CALL(m, Init()).Times(1).WillOnce(Return(true)); + EXPECT_CALL(m, SendRequest(_,_,_)).Times(0); + GoogleCrashdumpUploader *uploader = new GoogleCrashdumpUploader("foobar", + "1.0", + "AAA-BBB", + "", + "", + "test@test.com", + "none", + "/tmp/foo.dmp", + "http://foo.com", + "", + "", + &m); + ASSERT_FALSE(uploader->Upload()); +} + +TEST_F(GoogleCrashdumpUploaderTest, TestRequiredParametersMustBePresent) { + // Test with empty product name. + GoogleCrashdumpUploader uploader("", + "1.0", + "AAA-BBB", + "", + "", + "test@test.com", + "none", + "/tmp/foo.dmp", + "http://foo.com", + "", + ""); + ASSERT_FALSE(uploader.Upload()); + + // Test with empty product version. + GoogleCrashdumpUploader uploader1("product", + "", + "AAA-BBB", + "", + "", + "", + "", + "/tmp/foo.dmp", + "", + "", + ""); + + ASSERT_FALSE(uploader1.Upload()); + + // Test with empty client GUID. + GoogleCrashdumpUploader uploader2("product", + "1.0", + "", + "", + "", + "", + "", + "/tmp/foo.dmp", + "", + "", + ""); + ASSERT_FALSE(uploader2.Upload()); +} +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/guid_creator.cc 2010-04-16 17:32:47.000000000 +0100 @@ -49,12 +49,26 @@ srandom(time(NULL)); } + static u_int32_t BytesToUInt32(const u_int8_t bytes[]) { + return ((u_int32_t) bytes[0] + | ((u_int32_t) bytes[1] << 8) + | ((u_int32_t) bytes[2] << 16) + | ((u_int32_t) bytes[3] << 24)); + } + + static void UInt32ToBytes(u_int8_t bytes[], u_int32_t n) { + bytes[0] = n & 0xff; + bytes[1] = (n >> 8) & 0xff; + bytes[2] = (n >> 16) & 0xff; + bytes[3] = (n >> 24) & 0xff; + } + bool CreateGUID(GUID *guid) const { guid->data1 = random(); guid->data2 = (u_int16_t)(random()); guid->data3 = (u_int16_t)(random()); - *reinterpret_cast(&guid->data4[0]) = random(); - *reinterpret_cast(&guid->data4[4]) = random(); + UInt32ToBytes(&guid->data4[0], random()); + UInt32ToBytes(&guid->data4[4], random()); return true; } }; @@ -72,8 +86,8 @@ assert(buf_len > kGUIDStringLength); int num = snprintf(buf, buf_len, kGUIDFormatString, guid->data1, guid->data2, guid->data3, - *reinterpret_cast(&(guid->data4[0])), - *reinterpret_cast(&(guid->data4[4]))); + GUIDGenerator::BytesToUInt32(&(guid->data4[0])), + GUIDGenerator::BytesToUInt32(&(guid->data4[4]))); if (num != kGUIDStringLength) return false; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/language.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/language.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/language.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/language.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,82 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// language.cc: Subclasses and singletons for google_breakpad::Language. +// See language.h for details. + +#include "common/linux/language.h" + +namespace google_breakpad { + +// C++ language-specific operations. +class CPPLanguage: public Language { + public: + string MakeQualifiedName(const string &parent_name, + const string &name) const { + if (parent_name.empty()) + return name; + else + return parent_name + "::" + name; + } +}; + +const CPPLanguage CPPLanguageSingleton; + +// Java language-specific operations. +class JavaLanguage: public Language { + public: + string MakeQualifiedName(const string &parent_name, + const string &name) const { + if (parent_name.empty()) + return name; + else + return parent_name + "." + name; + } +}; + +JavaLanguage JavaLanguageSingleton; + +// Assembler language-specific operations. +class AssemblerLanguage: public Language { + bool HasFunctions() const { return false; } + string MakeQualifiedName(const string &parent_name, + const string &name) const { + return name; + } +}; + +AssemblerLanguage AssemblerLanguageSingleton; + +const Language * const Language::CPlusPlus = &CPPLanguageSingleton; +const Language * const Language::Java = &JavaLanguageSingleton; +const Language * const Language::Assembler = &AssemblerLanguageSingleton; + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/language.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/language.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/language.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/language.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,84 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// language.h: Define google_breakpad::Language. Instances of +// subclasses of this class provide language-appropriate operations +// for the Breakpad symbol dumper. + +#ifndef COMMON_LINUX_LANGUAGE_H__ +#define COMMON_LINUX_LANGUAGE_H__ + +#include + +namespace google_breakpad { + +using std::string; + +// An abstract base class for language-specific operations. We choose +// an instance of a subclass of this when we find the CU's language. +// This class's definitions are appropriate for CUs with no specified +// language. +class Language { + public: + // Return true if this language has functions to which we can assign + // line numbers. (Debugging info for assembly language, for example, + // can have source location information, but does not have functions + // recorded using DW_TAG_subprogram DIEs.) + virtual bool HasFunctions() const { return true; } + + // Construct a fully-qualified, language-appropriate form of NAME, + // given that PARENT_NAME is the name of the construct enclosing + // NAME. If PARENT_NAME is the empty string, then NAME is a + // top-level name. + // + // This API sort of assumes that a fully-qualified name is always + // some simple textual composition of the unqualified name and its + // parent's name, and that we don't need to know anything else about + // the parent or the child (say, their DIEs' tags) to do the job. + // This is true for the languages we support at the moment, and + // keeps things concrete. Perhaps a more refined operation would + // take into account the parent and child DIE types, allow languages + // to use their own data type for complex parent names, etc. But if + // C++ doesn't need all that, who would? + virtual string MakeQualifiedName (const string &parent_name, + const string &name) const = 0; + + // Instances for specific languages. + static const Language * const CPlusPlus, + * const Java, + * const Assembler; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_LANGUAGE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,209 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include +#include +#include +#include + +#include + +#include "common/linux/libcurl_wrapper.h" +#include "third_party/linux/include/glog/logging.h" + +namespace google_breakpad { +LibcurlWrapper::LibcurlWrapper() + : init_ok_(false), + formpost_(NULL), + lastptr_(NULL), + headerlist_(NULL) { + curl_lib_ = dlopen("libcurl.so", RTLD_NOW); + if (!curl_lib_) { + curl_lib_ = dlopen("libcurl.so.4", RTLD_NOW); + } + if (!curl_lib_) { + curl_lib_ = dlopen("libcurl.so.3", RTLD_NOW); + } + if (!curl_lib_) { + LOG(WARNING) << "Could not find libcurl via dlopen"; + return; + } + LOG(INFO) << "LibcurlWrapper init succeeded"; + init_ok_ = true; + return; +} + +bool LibcurlWrapper::SetProxy(const std::string& proxy_host, + const std::string& proxy_userpwd) { + if (!init_ok_) { + return false; + } + // Set proxy information if necessary. + if (!proxy_host.empty()) { + (*easy_setopt_)(curl_, CURLOPT_PROXY, proxy_host.c_str()); + } else { + LOG(WARNING) << "SetProxy called with empty proxy host."; + return false; + } + if (!proxy_userpwd.empty()) { + (*easy_setopt_)(curl_, CURLOPT_PROXYUSERPWD, proxy_userpwd.c_str()); + } else { + LOG(WARNING) << "SetProxy called with empty proxy username/password."; + return false; + } + LOG(INFO) << "Set proxy host to " << proxy_host; + return true; +} + +bool LibcurlWrapper::AddFile(const std::string& upload_file_path, + const std::string& basename) { + if (!init_ok_) { + return false; + } + LOG(INFO) << "Adding " << upload_file_path << " to form upload."; + // Add form file. + (*formadd_)(&formpost_, &lastptr_, + CURLFORM_COPYNAME, basename.c_str(), + CURLFORM_FILE, upload_file_path.c_str(), + CURLFORM_END); + + return true; +} + +// Callback to get the response data from server. +static size_t WriteCallback(void *ptr, size_t size, + size_t nmemb, void *userp) { + if (!userp) + return 0; + + std::string *response = reinterpret_cast(userp); + size_t real_size = size * nmemb; + response->append(reinterpret_cast(ptr), real_size); + return real_size; +} + +bool LibcurlWrapper::SendRequest(const std::string& url, + const std::map& parameters, + std::string* server_response) { + (*easy_setopt_)(curl_, CURLOPT_URL, url.c_str()); + std::map::const_iterator iter = parameters.begin(); + for (; iter != parameters.end(); ++iter) + (*formadd_)(&formpost_, &lastptr_, + CURLFORM_COPYNAME, iter->first.c_str(), + CURLFORM_COPYCONTENTS, iter->second.c_str(), + CURLFORM_END); + + (*easy_setopt_)(curl_, CURLOPT_HTTPPOST, formpost_); + if (server_response != NULL) { + (*easy_setopt_)(curl_, CURLOPT_WRITEFUNCTION, WriteCallback); + (*easy_setopt_)(curl_, CURLOPT_WRITEDATA, + reinterpret_cast(server_response)); + } + + CURLcode err_code = CURLE_OK; + err_code = (*easy_perform_)(curl_); + *(void**) (&easy_strerror_) = dlsym(curl_lib_, "curl_easy_strerror"); +#ifndef NDEBUG + if (err_code != CURLE_OK) + fprintf(stderr, "Failed to send http request to %s, error: %s\n", + url.c_str(), + (*easy_strerror_)(err_code)); +#endif + if (headerlist_ != NULL) { + (*slist_free_all_)(headerlist_); + } + + (*easy_cleanup_)(curl_); + if (formpost_ != NULL) { + (*formfree_)(formpost_); + } + + return err_code == CURLE_OK; +} + +bool LibcurlWrapper::Init() { + if (!init_ok_) { + LOG(WARNING) << "Init_OK was not true in LibcurlWrapper::Init(), check earlier log messages"; + return false; + } + + if (!SetFunctionPointers()) { + LOG(WARNING) << "Could not find function pointers"; + init_ok_ = false; + return false; + } + + curl_ = (*easy_init_)(); + + last_curl_error_ = "No Error"; + + if (!curl_) { + dlclose(curl_lib_); + LOG(WARNING) << "Curl initialization failed"; + return false; + } + + // Disable 100-continue header. + char buf[] = "Expect:"; + + headerlist_ = (*slist_append_)(headerlist_, buf); + (*easy_setopt_)(curl_, CURLOPT_HTTPHEADER, headerlist_); + return true; +} + +#define SET_AND_CHECK_FUNCTION_POINTER(var, function_name) \ + *(void**) (&var) = dlsym(curl_lib_, function_name); \ + if (!var) { \ + LOG(WARNING) << "Could not find libcurl function " << function_name; \ + init_ok_ = false; \ + return false; \ + } + +bool LibcurlWrapper::SetFunctionPointers() { + + SET_AND_CHECK_FUNCTION_POINTER(easy_init_, + "curl_easy_init"); + SET_AND_CHECK_FUNCTION_POINTER(easy_setopt_, + "curl_easy_setopt"); + SET_AND_CHECK_FUNCTION_POINTER(formadd_, + "curl_formadd"); + SET_AND_CHECK_FUNCTION_POINTER(slist_append_, + "curl_slist_append"); + SET_AND_CHECK_FUNCTION_POINTER(easy_perform_, + "curl_easy_perform"); + SET_AND_CHECK_FUNCTION_POINTER(easy_cleanup_, + "curl_easy_cleanup"); + SET_AND_CHECK_FUNCTION_POINTER(slist_free_all_, + "curl_slist_free_all"); + SET_AND_CHECK_FUNCTION_POINTER(formfree_, + "curl_formfree"); + return true; +} + +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/libcurl_wrapper.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,82 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// A wrapper for libcurl to do HTTP Uploads, to support easy mocking +// and unit testing of the HTTPUpload class. + +#include +#include +#include + +namespace google_breakpad { +class LibcurlWrapper { + public: + LibcurlWrapper(); + virtual bool Init(); + virtual bool SetProxy(const std::string& proxy_host, + const std::string& proxy_userpwd); + virtual bool AddFile(const std::string& upload_file_path, + const std::string& basename); + virtual bool SendRequest(const std::string& url, + const std::map& parameters, + std::string* server_response); + private: + // This function initializes class state corresponding to function + // pointers into the CURL library. + bool SetFunctionPointers(); + + bool init_ok_; // Whether init succeeded + void* curl_lib_; // Pointer to result of dlopen() on + // curl library + std::string last_curl_error_; // The text of the last error when + // dealing + // with CURL. + + CURL *curl_; // Pointer for handle for CURL calls. + + CURL* (*easy_init_)(void); + + // Stateful pointers for calling into curl_formadd() + struct curl_httppost *formpost_; + struct curl_httppost *lastptr_; + struct curl_slist *headerlist_; + + // Function pointers into CURL library + CURLcode (*easy_setopt_)(CURL *, CURLoption, ...); + CURLFORMcode (*formadd_)(struct curl_httppost **, + struct curl_httppost **, ...); + struct curl_slist* (*slist_append_)(struct curl_slist *, const char *); + void (*slist_free_all_)(struct curl_slist *); + CURLcode (*easy_perform_)(CURL *); + const char* (*easy_strerror_)(CURLcode); + void (*easy_cleanup_)(CURL *); + void (*formfree_)(struct curl_httppost *); + +}; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,178 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This header provides replacements for libc functions that we need. We if +// call the libc functions directly we risk crashing in the dynamic linker as +// it tries to resolve uncached PLT entries. + +#ifndef CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_ +#define CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_ + +#include +#include +#include + +extern "C" { + +static inline size_t +my_strlen(const char* s) { + size_t len = 0; + while (*s++) len++; + return len; +} + +static inline int +my_strcmp(const char* a, const char* b) { + for (;;) { + if (*a < *b) + return -1; + else if (*a > *b) + return 1; + else if (*a == 0) + return 0; + a++; + b++; + } +} + +static inline int +my_strncmp(const char* a, const char* b, size_t len) { + for (size_t i = 0; i < len; ++i) { + if (*a < *b) + return -1; + else if (*a > *b) + return 1; + else if (*a == 0) + return 0; + a++; + b++; + } + + return 0; +} + +// Parse a non-negative integer. +// result: (output) the resulting non-negative integer +// s: a NUL terminated string +// Return true iff successful. +static inline bool +my_strtoui(int* result, const char* s) { + if (*s == 0) + return false; + int r = 0; + for (;; s++) { + if (*s == 0) + break; + const int old_r = r; + r *= 10; + if (*s < '0' || *s > '9') + return false; + r += *s - '0'; + if (r < old_r) + return false; + } + + *result = r; + return true; +} + +// Return the length of the given, non-negative integer when expressed in base +// 10. +static inline unsigned +my_int_len(int i) { + if (!i) + return 1; + + int len = 0; + while (i) { + len++; + i /= 10; + } + + return len; +} + +// Convert a non-negative integer to a string +// output: (output) the resulting string is written here. This buffer must be +// large enough to hold the resulting string. Call |my_int_len| to get the +// required length. +// i: the non-negative integer to serialise. +// i_len: the length of the integer in base 10 (see |my_int_len|). +static inline void +my_itos(char* output, int i, unsigned i_len) { + for (unsigned index = i_len; index; --index, i /= 10) + output[index - 1] = '0' + (i % 10); +} + +static inline const char* +my_strchr(const char* haystack, char needle) { + while (*haystack && *haystack != needle) + haystack++; + if (*haystack == needle) + return haystack; + return (const char*) 0; +} + +// Read a hex value +// result: (output) the resulting value +// s: a string +// Returns a pointer to the first invalid charactor. +static inline const char* +my_read_hex_ptr(uintptr_t* result, const char* s) { + uintptr_t r = 0; + + for (;; ++s) { + if (*s >= '0' && *s <= '9') { + r <<= 4; + r += *s - '0'; + } else if (*s >= 'a' && *s <= 'f') { + r <<= 4; + r += (*s - 'a') + 10; + } else if (*s >= 'A' && *s <= 'F') { + r <<= 4; + r += (*s - 'A') + 10; + } else { + break; + } + } + + *result = r; + return s; +} + +static inline void +my_memset(void* ip, char c, size_t len) { + char* p = (char *) ip; + while (len--) + *p++ = c; +} + +} // extern "C" + +#endif // CLIENT_LINUX_LINUX_LIBC_SUPPORT_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/linux_libc_support_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,153 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "common/linux/linux_libc_support.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +typedef testing::Test LinuxLibcSupportTest; +} + +TEST(LinuxLibcSupportTest, strlen) { + static const char* test_data[] = { "", "a", "aa", "aaa", "aabc", NULL }; + for (unsigned i = 0; ; ++i) { + if (!test_data[i]) + break; + ASSERT_EQ(strlen(test_data[i]), my_strlen(test_data[i])); + } +} + +TEST(LinuxLibcSupportTest, strcmp) { + static const char* test_data[] = { + "", "", + "a", "", + "", "a", + "a", "b", + "a", "a", + "ab", "aa", + "abc", "ab", + "abc", "abc", + NULL, + }; + + for (unsigned i = 0; ; ++i) { + if (!test_data[i*2]) + break; + ASSERT_EQ(my_strcmp(test_data[i*2], test_data[i*2 + 1]), + strcmp(test_data[i*2], test_data[i*2 + 1])); + } +} + +TEST(LinuxLibcSupportTest, strtoui) { + int result; + + ASSERT_FALSE(my_strtoui(&result, "")); + ASSERT_FALSE(my_strtoui(&result, "-1")); + ASSERT_FALSE(my_strtoui(&result, "-")); + ASSERT_FALSE(my_strtoui(&result, "a")); + ASSERT_FALSE(my_strtoui(&result, "23472893472938472987987398472398")); + + ASSERT_TRUE(my_strtoui(&result, "0")); + ASSERT_EQ(result, 0); + ASSERT_TRUE(my_strtoui(&result, "1")); + ASSERT_EQ(result, 1); + ASSERT_TRUE(my_strtoui(&result, "12")); + ASSERT_EQ(result, 12); + ASSERT_TRUE(my_strtoui(&result, "123")); + ASSERT_EQ(result, 123); + ASSERT_TRUE(my_strtoui(&result, "0123")); + ASSERT_EQ(result, 123); +} + +TEST(LinuxLibcSupportTest, int_len) { + ASSERT_EQ(my_int_len(0), 1); + ASSERT_EQ(my_int_len(2), 1); + ASSERT_EQ(my_int_len(5), 1); + ASSERT_EQ(my_int_len(9), 1); + ASSERT_EQ(my_int_len(10), 2); + ASSERT_EQ(my_int_len(99), 2); + ASSERT_EQ(my_int_len(100), 3); + ASSERT_EQ(my_int_len(101), 3); + ASSERT_EQ(my_int_len(1000), 4); +} + +TEST(LinuxLibcSupportTest, itos) { + char buf[10]; + + my_itos(buf, 0, 1); + ASSERT_EQ(0, memcmp(buf, "0", 1)); + + my_itos(buf, 1, 1); + ASSERT_EQ(0, memcmp(buf, "1", 1)); + + my_itos(buf, 10, 2); + ASSERT_EQ(0, memcmp(buf, "10", 2)); + + my_itos(buf, 63, 2); + ASSERT_EQ(0, memcmp(buf, "63", 2)); + + my_itos(buf, 101, 3); + ASSERT_EQ(0, memcmp(buf, "101", 2)); +} + +TEST(LinuxLibcSupportTest, strchr) { + ASSERT_EQ(NULL, my_strchr("abc", 'd')); + ASSERT_EQ(NULL, my_strchr("", 'd')); + ASSERT_EQ(NULL, my_strchr("efghi", 'd')); + + ASSERT_TRUE(my_strchr("a", 'a')); + ASSERT_TRUE(my_strchr("abc", 'a')); + ASSERT_TRUE(my_strchr("bcda", 'a')); + ASSERT_TRUE(my_strchr("sdfasdf", 'a')); +} + +TEST(LinuxLibcSupportTest, read_hex_ptr) { + uintptr_t result; + const char* last; + + last = my_read_hex_ptr(&result, ""); + ASSERT_EQ(result, 0); + ASSERT_EQ(*last, 0); + + last = my_read_hex_ptr(&result, "0"); + ASSERT_EQ(result, 0); + ASSERT_EQ(*last, 0); + + last = my_read_hex_ptr(&result, "0123"); + ASSERT_EQ(result, 0x123); + ASSERT_EQ(*last, 0); + + last = my_read_hex_ptr(&result, "0123a"); + ASSERT_EQ(result, 0x123a); + ASSERT_EQ(*last, 0); + + last = my_read_hex_ptr(&result, "0123a-"); + ASSERT_EQ(result, 0x123a); + ASSERT_EQ(*last, '-'); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/linux_syscall_support.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/linux_syscall_support.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/linux_syscall_support.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/linux_syscall_support.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,2947 @@ +/* Copyright (c) 2005-2008, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * --- + * Author: Markus Gutschke + */ + +/* This file includes Linux-specific support functions common to the + * coredumper and the thread lister; primarily, this is a collection + * of direct system calls, and a couple of symbols missing from + * standard header files. + * There are a few options that the including file can set to control + * the behavior of this file: + * + * SYS_CPLUSPLUS: + * The entire header file will normally be wrapped in 'extern "C" { }", + * making it suitable for compilation as both C and C++ source. If you + * do not want to do this, you can set the SYS_CPLUSPLUS macro to inhibit + * the wrapping. N.B. doing so will suppress inclusion of all prerequisite + * system header files, too. It is the caller's responsibility to provide + * the necessary definitions. + * + * SYS_ERRNO: + * All system calls will update "errno" unless overriden by setting the + * SYS_ERRNO macro prior to including this file. SYS_ERRNO should be + * an l-value. + * + * SYS_INLINE: + * New symbols will be defined "static inline", unless overridden by + * the SYS_INLINE macro. + * + * SYS_LINUX_SYSCALL_SUPPORT_H + * This macro is used to avoid multiple inclusions of this header file. + * If you need to include this file more than once, make sure to + * unset SYS_LINUX_SYSCALL_SUPPORT_H before each inclusion. + * + * SYS_PREFIX: + * New system calls will have a prefix of "sys_" unless overridden by + * the SYS_PREFIX macro. Valid values for this macro are [0..9] which + * results in prefixes "sys[0..9]_". It is also possible to set this + * macro to -1, which avoids all prefixes. + * + * This file defines a few internal symbols that all start with "LSS_". + * Do not access these symbols from outside this file. They are not part + * of the supported API. + */ +#ifndef SYS_LINUX_SYSCALL_SUPPORT_H +#define SYS_LINUX_SYSCALL_SUPPORT_H + +/* We currently only support x86-32, x86-64, ARM, MIPS, and PPC on Linux. + * Porting to other related platforms should not be difficult. + */ +#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \ + defined(__mips__) || defined(__PPC__) || defined(__ARM_EABI__)) \ + && defined(__linux) + +#ifndef SYS_CPLUSPLUS +#ifdef __cplusplus +/* Some system header files in older versions of gcc neglect to properly + * handle being included from C++. As it appears to be harmless to have + * multiple nested 'extern "C"' blocks, just add another one here. + */ +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __mips__ +/* Include definitions of the ABI currently in use. */ +#include +#endif +#endif + +/* As glibc often provides subtly incompatible data structures (and implicit + * wrapper functions that convert them), we provide our own kernel data + * structures for use by the system calls. + * These structures have been developed by using Linux 2.6.23 headers for + * reference. Note though, we do not care about exact API compatibility + * with the kernel, and in fact the kernel often does not have a single + * API that works across architectures. Instead, we try to mimic the glibc + * API where reasonable, and only guarantee ABI compatibility with the + * kernel headers. + * Most notably, here are a few changes that were made to the structures + * defined by kernel headers: + * + * - we only define structures, but not symbolic names for kernel data + * types. For the latter, we directly use the native C datatype + * (i.e. "unsigned" instead of "mode_t"). + * - in a few cases, it is possible to define identical structures for + * both 32bit (e.g. i386) and 64bit (e.g. x86-64) platforms by + * standardizing on the 64bit version of the data types. In particular, + * this means that we use "unsigned" where the 32bit headers say + * "unsigned long". + * - overall, we try to minimize the number of cases where we need to + * conditionally define different structures. + * - the "struct kernel_sigaction" class of structures have been + * modified to more closely mimic glibc's API by introducing an + * anonymous union for the function pointer. + * - a small number of field names had to have an underscore appended to + * them, because glibc defines a global macro by the same name. + */ + +/* include/linux/dirent.h */ +struct kernel_dirent64 { + unsigned long long d_ino; + long long d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +/* include/linux/dirent.h */ +struct kernel_dirent { + long d_ino; + long d_off; + unsigned short d_reclen; + char d_name[256]; +}; + +/* include/linux/uio.h */ +struct kernel_iovec { + void *iov_base; + unsigned long iov_len; +}; + +/* include/linux/socket.h */ +struct kernel_msghdr { + void *msg_name; + int msg_namelen; + struct kernel_iovec*msg_iov; + unsigned long msg_iovlen; + void *msg_control; + unsigned long msg_controllen; + unsigned msg_flags; +}; + +/* include/asm-generic/poll.h */ +struct kernel_pollfd { + int fd; + short events; + short revents; +}; + +/* include/linux/resource.h */ +struct kernel_rlimit { + unsigned long rlim_cur; + unsigned long rlim_max; +}; + +/* include/linux/time.h */ +struct kernel_timespec { + long tv_sec; + long tv_nsec; +}; + +/* include/linux/time.h */ +struct kernel_timeval { + long tv_sec; + long tv_usec; +}; + +/* include/linux/resource.h */ +struct kernel_rusage { + struct kernel_timeval ru_utime; + struct kernel_timeval ru_stime; + long ru_maxrss; + long ru_ixrss; + long ru_idrss; + long ru_isrss; + long ru_minflt; + long ru_majflt; + long ru_nswap; + long ru_inblock; + long ru_oublock; + long ru_msgsnd; + long ru_msgrcv; + long ru_nsignals; + long ru_nvcsw; + long ru_nivcsw; +}; + +struct siginfo; +#if defined(__i386__) || defined(__ARM_EABI__) || defined(__ARM_ARCH_3__) \ + || defined(__PPC__) + +/* include/asm-{arm,i386,mips,ppc}/signal.h */ +struct kernel_old_sigaction { + union { + void (*sa_handler_)(int); + void (*sa_sigaction_)(int, struct siginfo *, void *); + }; + unsigned long sa_mask; + unsigned long sa_flags; + void (*sa_restorer)(void); +} __attribute__((packed,aligned(4))); +#elif (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) + #define kernel_old_sigaction kernel_sigaction +#endif + +/* Some kernel functions (e.g. sigaction() in 2.6.23) require that the + * exactly match the size of the signal set, even though the API was + * intended to be extensible. We define our own KERNEL_NSIG to deal with + * this. + * Please note that glibc provides signals [1.._NSIG-1], whereas the + * kernel (and this header) provides the range [1..KERNEL_NSIG]. The + * actual number of signals is obviously the same, but the constants + * differ by one. + */ +#ifdef __mips__ +#define KERNEL_NSIG 128 +#else +#define KERNEL_NSIG 64 +#endif + +/* include/asm-{arm,i386,mips,x86_64}/signal.h */ +struct kernel_sigset_t { + unsigned long sig[(KERNEL_NSIG + 8*sizeof(unsigned long) - 1)/ + (8*sizeof(unsigned long))]; +}; + +/* include/asm-{arm,i386,mips,x86_64,ppc}/signal.h */ +struct kernel_sigaction { +#ifdef __mips__ + unsigned long sa_flags; + union { + void (*sa_handler_)(int); + void (*sa_sigaction_)(int, struct siginfo *, void *); + }; + struct kernel_sigset_t sa_mask; +#else + union { + void (*sa_handler_)(int); + void (*sa_sigaction_)(int, struct siginfo *, void *); + }; + unsigned long sa_flags; + void (*sa_restorer)(void); + struct kernel_sigset_t sa_mask; +#endif +}; + +/* include/linux/socket.h */ +struct kernel_sockaddr { + unsigned short sa_family; + char sa_data[14]; +}; + +/* include/asm-{arm,i386,mips,ppc}/stat.h */ +#ifdef __mips__ +#if _MIPS_SIM == _MIPS_SIM_ABI64 +struct kernel_stat { +#else +struct kernel_stat64 { +#endif + unsigned st_dev; + unsigned __pad0[3]; + unsigned long long st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned st_rdev; + unsigned __pad1[3]; + long long st_size; + unsigned st_atime_; + unsigned st_atime_nsec_; + unsigned st_mtime_; + unsigned st_mtime_nsec_; + unsigned st_ctime_; + unsigned st_ctime_nsec_; + unsigned st_blksize; + unsigned __pad2; + unsigned long long st_blocks; +}; +#elif defined __PPC__ +struct kernel_stat64 { + unsigned long long st_dev; + unsigned long long st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned long long st_rdev; + unsigned short int __pad2; + long long st_size; + long st_blksize; + long long st_blocks; + long st_atime_; + unsigned long st_atime_nsec_; + long st_mtime_; + unsigned long st_mtime_nsec_; + long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned long __unused4; + unsigned long __unused5; +}; +#else +struct kernel_stat64 { + unsigned long long st_dev; + unsigned char __pad0[4]; + unsigned __st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned long long st_rdev; + unsigned char __pad3[4]; + long long st_size; + unsigned st_blksize; + unsigned long long st_blocks; + unsigned st_atime_; + unsigned st_atime_nsec_; + unsigned st_mtime_; + unsigned st_mtime_nsec_; + unsigned st_ctime_; + unsigned st_ctime_nsec_; + unsigned long long st_ino; +}; +#endif + +/* include/asm-{arm,i386,mips,x86_64,ppc}/stat.h */ +#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) +struct kernel_stat { + /* The kernel headers suggest that st_dev and st_rdev should be 32bit + * quantities encoding 12bit major and 20bit minor numbers in an interleaved + * format. In reality, we do not see useful data in the top bits. So, + * we'll leave the padding in here, until we find a better solution. + */ + unsigned short st_dev; + short pad1; + unsigned st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + short pad2; + unsigned st_size; + unsigned st_blksize; + unsigned st_blocks; + unsigned st_atime_; + unsigned st_atime_nsec_; + unsigned st_mtime_; + unsigned st_mtime_nsec_; + unsigned st_ctime_; + unsigned st_ctime_nsec_; + unsigned __unused4; + unsigned __unused5; +}; +#elif defined(__x86_64__) +struct kernel_stat { + unsigned long st_dev; + unsigned long st_ino; + unsigned long st_nlink; + unsigned st_mode; + unsigned st_uid; + unsigned st_gid; + unsigned __pad0; + unsigned long st_rdev; + long st_size; + long st_blksize; + long st_blocks; + unsigned long st_atime_; + unsigned long st_atime_nsec_; + unsigned long st_mtime_; + unsigned long st_mtime_nsec_; + unsigned long st_ctime_; + unsigned long st_ctime_nsec_; + long __unused[3]; +}; +#elif defined(__PPC__) +struct kernel_stat { + unsigned st_dev; + unsigned long st_ino; // ino_t + unsigned long st_mode; // mode_t + unsigned short st_nlink; // nlink_t + unsigned st_uid; // uid_t + unsigned st_gid; // gid_t + unsigned st_rdev; + long st_size; // off_t + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime_; + unsigned long st_atime_nsec_; + unsigned long st_mtime_; + unsigned long st_mtime_nsec_; + unsigned long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned long __unused4; + unsigned long __unused5; +}; +#elif (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) +struct kernel_stat { + unsigned st_dev; + int st_pad1[3]; + unsigned st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned st_rdev; + int st_pad2[2]; + long st_size; + int st_pad3; + long st_atime_; + long st_atime_nsec_; + long st_mtime_; + long st_mtime_nsec_; + long st_ctime_; + long st_ctime_nsec_; + int st_blksize; + int st_blocks; + int st_pad4[14]; +}; +#endif + +/* include/asm-{arm,i386,mips,x86_64,ppc}/statfs.h */ +#ifdef __mips__ +#if _MIPS_SIM != _MIPS_SIM_ABI64 +struct kernel_statfs64 { + unsigned long f_type; + unsigned long f_bsize; + unsigned long f_frsize; + unsigned long __pad; + unsigned long long f_blocks; + unsigned long long f_bfree; + unsigned long long f_files; + unsigned long long f_ffree; + unsigned long long f_bavail; + struct { int val[2]; } f_fsid; + unsigned long f_namelen; + unsigned long f_spare[6]; +}; +#endif +#elif !defined(__x86_64__) +struct kernel_statfs64 { + unsigned long f_type; + unsigned long f_bsize; + unsigned long long f_blocks; + unsigned long long f_bfree; + unsigned long long f_bavail; + unsigned long long f_files; + unsigned long long f_ffree; + struct { int val[2]; } f_fsid; + unsigned long f_namelen; + unsigned long f_frsize; + unsigned long f_spare[5]; +}; +#endif + +/* include/asm-{arm,i386,mips,x86_64,ppc,generic}/statfs.h */ +#ifdef __mips__ +struct kernel_statfs { + long f_type; + long f_bsize; + long f_frsize; + long f_blocks; + long f_bfree; + long f_files; + long f_ffree; + long f_bavail; + struct { int val[2]; } f_fsid; + long f_namelen; + long f_spare[6]; +}; +#else +struct kernel_statfs { + /* x86_64 actually defines all these fields as signed, whereas all other */ + /* platforms define them as unsigned. Leaving them at unsigned should not */ + /* cause any problems. */ + unsigned long f_type; + unsigned long f_bsize; + unsigned long f_blocks; + unsigned long f_bfree; + unsigned long f_bavail; + unsigned long f_files; + unsigned long f_ffree; + struct { int val[2]; } f_fsid; + unsigned long f_namelen; + unsigned long f_frsize; + unsigned long f_spare[5]; +}; +#endif + + +/* Definitions missing from the standard header files */ +#ifndef O_DIRECTORY +#if defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) +#define O_DIRECTORY 0040000 +#else +#define O_DIRECTORY 0200000 +#endif +#endif +#ifndef NT_PRXFPREG +#define NT_PRXFPREG 0x46e62b7f +#endif +#ifndef PTRACE_GETFPXREGS +#define PTRACE_GETFPXREGS ((enum __ptrace_request)18) +#endif +#ifndef PR_GET_DUMPABLE +#define PR_GET_DUMPABLE 3 +#endif +#ifndef PR_SET_DUMPABLE +#define PR_SET_DUMPABLE 4 +#endif +#ifndef AT_FDCWD +#define AT_FDCWD (-100) +#endif +#ifndef AT_SYMLINK_NOFOLLOW +#define AT_SYMLINK_NOFOLLOW 0x100 +#endif +#ifndef AT_REMOVEDIR +#define AT_REMOVEDIR 0x200 +#endif +#ifndef MREMAP_FIXED +#define MREMAP_FIXED 2 +#endif +#ifndef SA_RESTORER +#define SA_RESTORER 0x04000000 +#endif + +#if defined(__i386__) +#ifndef __NR_setresuid +#define __NR_setresuid 164 +#define __NR_setresgid 170 +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigaction 174 +#define __NR_rt_sigprocmask 175 +#define __NR_rt_sigpending 176 +#define __NR_rt_sigsuspend 179 +#endif +#ifndef __NR_pread64 +#define __NR_pread64 180 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 181 +#endif +#ifndef __NR_ugetrlimit +#define __NR_ugetrlimit 191 +#endif +#ifndef __NR_stat64 +#define __NR_stat64 195 +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 197 +#endif +#ifndef __NR_setresuid32 +#define __NR_setresuid32 208 +#define __NR_setresgid32 210 +#endif +#ifndef __NR_setfsuid32 +#define __NR_setfsuid32 215 +#define __NR_setfsgid32 216 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 220 +#endif +#ifndef __NR_gettid +#define __NR_gettid 224 +#endif +#ifndef __NR_readahead +#define __NR_readahead 225 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 226 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 227 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 229 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 230 +#endif +#ifndef __NR_futex +#define __NR_futex 240 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 241 +#define __NR_sched_getaffinity 242 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 258 +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 268 +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 269 +#endif +#ifndef __NR_fadvise64_64 +#define __NR_fadvise64_64 272 +#endif +#ifndef __NR_openat +#define __NR_openat 295 +#endif +#ifndef __NR_fstatat64 +#define __NR_fstatat64 300 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 301 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 317 +#endif +/* End of i386 definitions */ +#elif defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_SYSCALL_BASE + 164) +#define __NR_setresgid (__NR_SYSCALL_BASE + 170) +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigaction (__NR_SYSCALL_BASE + 174) +#define __NR_rt_sigprocmask (__NR_SYSCALL_BASE + 175) +#define __NR_rt_sigpending (__NR_SYSCALL_BASE + 176) +#define __NR_rt_sigsuspend (__NR_SYSCALL_BASE + 179) +#endif +#ifndef __NR_pread64 +#define __NR_pread64 (__NR_SYSCALL_BASE + 180) +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 (__NR_SYSCALL_BASE + 181) +#endif +#ifndef __NR_ugetrlimit +#define __NR_ugetrlimit (__NR_SYSCALL_BASE + 191) +#endif +#ifndef __NR_stat64 +#define __NR_stat64 (__NR_SYSCALL_BASE + 195) +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 (__NR_SYSCALL_BASE + 197) +#endif +#ifndef __NR_setresuid32 +#define __NR_setresuid32 (__NR_SYSCALL_BASE + 208) +#define __NR_setresgid32 (__NR_SYSCALL_BASE + 210) +#endif +#ifndef __NR_setfsuid32 +#define __NR_setfsuid32 (__NR_SYSCALL_BASE + 215) +#define __NR_setfsgid32 (__NR_SYSCALL_BASE + 216) +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 (__NR_SYSCALL_BASE + 217) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_SYSCALL_BASE + 224) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_SYSCALL_BASE + 225) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_SYSCALL_BASE + 226) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_SYSCALL_BASE + 227) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_SYSCALL_BASE + 229) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_SYSCALL_BASE + 230) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_SYSCALL_BASE + 240) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_SYSCALL_BASE + 241) +#define __NR_sched_getaffinity (__NR_SYSCALL_BASE + 242) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_SYSCALL_BASE + 256) +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 (__NR_SYSCALL_BASE + 266) +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 (__NR_SYSCALL_BASE + 267) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_SYSCALL_BASE + 344) +#endif +/* End of ARM 3/EABI definitions */ +#elif defined(__x86_64__) +#ifndef __NR_setresuid +#define __NR_setresuid 117 +#define __NR_setresgid 119 +#endif +#ifndef __NR_gettid +#define __NR_gettid 186 +#endif +#ifndef __NR_readahead +#define __NR_readahead 187 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 188 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 189 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 191 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 192 +#endif +#ifndef __NR_futex +#define __NR_futex 202 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 203 +#define __NR_sched_getaffinity 204 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 217 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 218 +#endif +#ifndef __NR_fadvise64 +#define __NR_fadvise64 221 +#endif +#ifndef __NR_openat +#define __NR_openat 257 +#endif +#ifndef __NR_newfstatat +#define __NR_newfstatat 262 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 263 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 279 +#endif +/* End of x86-64 definitions */ +#elif defined(__mips__) +#if _MIPS_SIM == _MIPS_SIM_ABI32 +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_Linux + 185) +#define __NR_setresgid (__NR_Linux + 190) +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigaction (__NR_Linux + 194) +#define __NR_rt_sigprocmask (__NR_Linux + 195) +#define __NR_rt_sigpending (__NR_Linux + 196) +#define __NR_rt_sigsuspend (__NR_Linux + 199) +#endif +#ifndef __NR_pread64 +#define __NR_pread64 (__NR_Linux + 200) +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 (__NR_Linux + 201) +#endif +#ifndef __NR_stat64 +#define __NR_stat64 (__NR_Linux + 213) +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 (__NR_Linux + 215) +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 (__NR_Linux + 219) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_Linux + 222) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_Linux + 223) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_Linux + 224) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_Linux + 225) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_Linux + 227) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_Linux + 228) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_Linux + 238) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_Linux + 239) +#define __NR_sched_getaffinity (__NR_Linux + 240) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_Linux + 252) +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 (__NR_Linux + 255) +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 (__NR_Linux + 256) +#endif +#ifndef __NR_openat +#define __NR_openat (__NR_Linux + 288) +#endif +#ifndef __NR_fstatat +#define __NR_fstatat (__NR_Linux + 293) +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat (__NR_Linux + 294) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_Linux + 308) +#endif +/* End of MIPS (old 32bit API) definitions */ +#elif _MIPS_SIM == _MIPS_SIM_ABI64 +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_Linux + 115) +#define __NR_setresgid (__NR_Linux + 117) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_Linux + 178) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_Linux + 179) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_Linux + 180) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_Linux + 181) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_Linux + 183) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_Linux + 184) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_Linux + 194) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_Linux + 195) +#define __NR_sched_getaffinity (__NR_Linux + 196) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_Linux + 212) +#endif +#ifndef __NR_openat +#define __NR_openat (__NR_Linux + 247) +#endif +#ifndef __NR_fstatat +#define __NR_fstatat (__NR_Linux + 252) +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat (__NR_Linux + 253) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_Linux + 267) +#endif +/* End of MIPS (64bit API) definitions */ +#else +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_Linux + 115) +#define __NR_setresgid (__NR_Linux + 117) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_Linux + 178) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_Linux + 179) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_Linux + 180) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_Linux + 181) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_Linux + 183) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_Linux + 184) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_Linux + 194) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_Linux + 195) +#define __NR_sched_getaffinity (__NR_Linux + 196) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_Linux + 213) +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 (__NR_Linux + 217) +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 (__NR_Linux + 218) +#endif +#ifndef __NR_openat +#define __NR_openat (__NR_Linux + 251) +#endif +#ifndef __NR_fstatat +#define __NR_fstatat (__NR_Linux + 256) +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat (__NR_Linux + 257) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_Linux + 271) +#endif +/* End of MIPS (new 32bit API) definitions */ +#endif +/* End of MIPS definitions */ +#elif defined(__PPC__) +#ifndef __NR_setfsuid +#define __NR_setfsuid 138 +#define __NR_setfsgid 139 +#endif +#ifndef __NR_setresuid +#define __NR_setresuid 164 +#define __NR_setresgid 169 +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigaction 173 +#define __NR_rt_sigprocmask 174 +#define __NR_rt_sigpending 175 +#define __NR_rt_sigsuspend 178 +#endif +#ifndef __NR_pread64 +#define __NR_pread64 179 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 180 +#endif +#ifndef __NR_ugetrlimit +#define __NR_ugetrlimit 190 +#endif +#ifndef __NR_readahead +#define __NR_readahead 191 +#endif +#ifndef __NR_stat64 +#define __NR_stat64 195 +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 197 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 202 +#endif +#ifndef __NR_gettid +#define __NR_gettid 207 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 209 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 210 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 212 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 213 +#endif +#ifndef __NR_futex +#define __NR_futex 221 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 222 +#define __NR_sched_getaffinity 223 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 232 +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 252 +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 253 +#endif +#ifndef __NR_fadvise64_64 +#define __NR_fadvise64_64 254 +#endif +#ifndef __NR_openat +#define __NR_openat 286 +#endif +#ifndef __NR_fstatat64 +#define __NR_fstatat64 291 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 292 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 301 +#endif +/* End of powerpc defininitions */ +#endif + + +/* After forking, we must make sure to only call system calls. */ +#if __BOUNDED_POINTERS__ + #error "Need to port invocations of syscalls for bounded ptrs" +#else + /* The core dumper and the thread lister get executed after threads + * have been suspended. As a consequence, we cannot call any functions + * that acquire locks. Unfortunately, libc wraps most system calls + * (e.g. in order to implement pthread_atfork, and to make calls + * cancellable), which means we cannot call these functions. Instead, + * we have to call syscall() directly. + */ + #undef LSS_ERRNO + #ifdef SYS_ERRNO + /* Allow the including file to override the location of errno. This can + * be useful when using clone() with the CLONE_VM option. + */ + #define LSS_ERRNO SYS_ERRNO + #else + #define LSS_ERRNO errno + #endif + + #undef LSS_INLINE + #ifdef SYS_INLINE + #define LSS_INLINE SYS_INLINE + #else + #define LSS_INLINE static inline + #endif + + /* Allow the including file to override the prefix used for all new + * system calls. By default, it will be set to "sys_". + */ + #undef LSS_NAME + #ifndef SYS_PREFIX + #define LSS_NAME(name) sys_##name + #elif SYS_PREFIX < 0 + #define LSS_NAME(name) name + #elif SYS_PREFIX == 0 + #define LSS_NAME(name) sys0_##name + #elif SYS_PREFIX == 1 + #define LSS_NAME(name) sys1_##name + #elif SYS_PREFIX == 2 + #define LSS_NAME(name) sys2_##name + #elif SYS_PREFIX == 3 + #define LSS_NAME(name) sys3_##name + #elif SYS_PREFIX == 4 + #define LSS_NAME(name) sys4_##name + #elif SYS_PREFIX == 5 + #define LSS_NAME(name) sys5_##name + #elif SYS_PREFIX == 6 + #define LSS_NAME(name) sys6_##name + #elif SYS_PREFIX == 7 + #define LSS_NAME(name) sys7_##name + #elif SYS_PREFIX == 8 + #define LSS_NAME(name) sys8_##name + #elif SYS_PREFIX == 9 + #define LSS_NAME(name) sys9_##name + #endif + + #undef LSS_RETURN + #if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) \ + || defined(__ARM_EABI__)) + /* Failing system calls return a negative result in the range of + * -1..-4095. These are "errno" values with the sign inverted. + */ + #define LSS_RETURN(type, res) \ + do { \ + if ((unsigned long)(res) >= (unsigned long)(-4095)) { \ + LSS_ERRNO = -(res); \ + res = -1; \ + } \ + return (type) (res); \ + } while (0) + #elif defined(__mips__) + /* On MIPS, failing system calls return -1, and set errno in a + * separate CPU register. + */ + #define LSS_RETURN(type, res, err) \ + do { \ + if (err) { \ + LSS_ERRNO = (res); \ + res = -1; \ + } \ + return (type) (res); \ + } while (0) + #elif defined(__PPC__) + /* On PPC, failing system calls return -1, and set errno in a + * separate CPU register. See linux/unistd.h. + */ + #define LSS_RETURN(type, res, err) \ + do { \ + if (err & 0x10000000 ) { \ + LSS_ERRNO = (res); \ + res = -1; \ + } \ + return (type) (res); \ + } while (0) + #endif + #if defined(__i386__) + /* In PIC mode (e.g. when building shared libraries), gcc for i386 + * reserves ebx. Unfortunately, most distribution ship with implementations + * of _syscallX() which clobber ebx. + * Also, most definitions of _syscallX() neglect to mark "memory" as being + * clobbered. This causes problems with compilers, that do a better job + * at optimizing across __asm__ calls. + * So, we just have to redefine all of the _syscallX() macros. + */ + #undef LSS_BODY + #define LSS_BODY(type,args...) \ + long __res; \ + __asm__ __volatile__("push %%ebx\n" \ + "movl %2,%%ebx\n" \ + "int $0x80\n" \ + "pop %%ebx" \ + args \ + : "memory"); \ + LSS_RETURN(type,__res) + #undef _syscall0 + #define _syscall0(type,name) \ + type LSS_NAME(name)(void) { \ + long __res; \ + __asm__ volatile("int $0x80" \ + : "=a" (__res) \ + : "0" (__NR_##name) \ + : "memory"); \ + LSS_RETURN(type,__res); \ + } + #undef _syscall1 + #define _syscall1(type,name,type1,arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name), "ri" ((long)(arg1))); \ + } + #undef _syscall2 + #define _syscall2(type,name,type1,arg1,type2,arg2) \ + type LSS_NAME(name)(type1 arg1,type2 arg2) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name),"ri" ((long)(arg1)), "c" ((long)(arg2))); \ + } + #undef _syscall3 + #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ + type LSS_NAME(name)(type1 arg1,type2 arg2,type3 arg3) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name), "ri" ((long)(arg1)), "c" ((long)(arg2)), \ + "d" ((long)(arg3))); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name), "ri" ((long)(arg1)), "c" ((long)(arg2)), \ + "d" ((long)(arg3)),"S" ((long)(arg4))); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + long __res; \ + __asm__ __volatile__("push %%ebx\n" \ + "movl %2,%%ebx\n" \ + "movl %1,%%eax\n" \ + "int $0x80\n" \ + "pop %%ebx" \ + : "=a" (__res) \ + : "i" (__NR_##name), "ri" ((long)(arg1)), \ + "c" ((long)(arg2)), "d" ((long)(arg3)), \ + "S" ((long)(arg4)), "D" ((long)(arg5)) \ + : "memory"); \ + LSS_RETURN(type,__res); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + long __res; \ + struct { long __a1; long __a6; } __s = { (long)arg1, (long) arg6 }; \ + __asm__ __volatile__("push %%ebp\n" \ + "push %%ebx\n" \ + "movl 4(%2),%%ebp\n" \ + "movl 0(%2), %%ebx\n" \ + "movl %1,%%eax\n" \ + "int $0x80\n" \ + "pop %%ebx\n" \ + "pop %%ebp" \ + : "=a" (__res) \ + : "i" (__NR_##name), "0" ((long)(&__s)), \ + "c" ((long)(arg2)), "d" ((long)(arg3)), \ + "S" ((long)(arg4)), "D" ((long)(arg5)) \ + : "memory"); \ + LSS_RETURN(type,__res); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + __asm__ __volatile__(/* if (fn == NULL) + * return -EINVAL; + */ + "movl %3,%%ecx\n" + "jecxz 1f\n" + + /* if (child_stack == NULL) + * return -EINVAL; + */ + "movl %4,%%ecx\n" + "jecxz 1f\n" + + /* Set up alignment of the child stack: + * child_stack = (child_stack & ~0xF) - 20; + */ + "andl $-16,%%ecx\n" + "subl $20,%%ecx\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "movl %6,%%eax\n" + "movl %%eax,4(%%ecx)\n" + "movl %3,%%eax\n" + "movl %%eax,(%%ecx)\n" + + /* %eax = syscall(%eax = __NR_clone, + * %ebx = flags, + * %ecx = child_stack, + * %edx = parent_tidptr, + * %esi = newtls, + * %edi = child_tidptr) + * Also, make sure that %ebx gets preserved as it is + * used in PIC mode. + */ + "movl %8,%%esi\n" + "movl %7,%%edx\n" + "movl %5,%%eax\n" + "movl %9,%%edi\n" + "pushl %%ebx\n" + "movl %%eax,%%ebx\n" + "movl %2,%%eax\n" + "int $0x80\n" + + /* In the parent: restore %ebx + * In the child: move "fn" into %ebx + */ + "popl %%ebx\n" + + /* if (%eax != 0) + * return %eax; + */ + "test %%eax,%%eax\n" + "jnz 1f\n" + + /* In the child, now. Terminate frame pointer chain. + */ + "movl $0,%%ebp\n" + + /* Call "fn". "arg" is already on the stack. + */ + "call *%%ebx\n" + + /* Call _exit(%ebx). Unfortunately older versions + * of gcc restrict the number of arguments that can + * be passed to asm(). So, we need to hard-code the + * system call number. + */ + "movl %%eax,%%ebx\n" + "movl $1,%%eax\n" + "int $0x80\n" + + /* Return to parent. + */ + "1:\n" + : "=a" (__res) + : "0"(-EINVAL), "i"(__NR_clone), + "m"(fn), "m"(child_stack), "m"(flags), "m"(arg), + "m"(parent_tidptr), "m"(newtls), "m"(child_tidptr) + : "memory", "ecx", "edx", "esi", "edi"); + LSS_RETURN(int, __res); + } + + #define __NR__fadvise64_64 __NR_fadvise64_64 + LSS_INLINE _syscall6(int, _fadvise64_64, int, fd, + unsigned, offset_lo, unsigned, offset_hi, + unsigned, len_lo, unsigned, len_hi, + int, advice) + + LSS_INLINE int LSS_NAME(fadvise64)(int fd, loff_t offset, + loff_t len, int advice) { + return LSS_NAME(_fadvise64_64)(fd, + (unsigned)offset, (unsigned)(offset >>32), + (unsigned)len, (unsigned)(len >> 32), + advice); + } + + LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) { + /* On i386, the kernel does not know how to return from a signal + * handler. Instead, it relies on user space to provide a + * restorer function that calls the {rt_,}sigreturn() system call. + * Unfortunately, we cannot just reference the glibc version of this + * function, as glibc goes out of its way to make it inaccessible. + */ + void (*res)(void); + __asm__ __volatile__("call 2f\n" + "0:.align 16\n" + "1:movl %1,%%eax\n" + "int $0x80\n" + "2:popl %0\n" + "addl $(1b-0b),%0\n" + : "=a" (res) + : "i" (__NR_rt_sigreturn)); + return res; + } + LSS_INLINE void (*LSS_NAME(restore)(void))(void) { + /* On i386, the kernel does not know how to return from a signal + * handler. Instead, it relies on user space to provide a + * restorer function that calls the {rt_,}sigreturn() system call. + * Unfortunately, we cannot just reference the glibc version of this + * function, as glibc goes out of its way to make it inaccessible. + */ + void (*res)(void); + __asm__ __volatile__("call 2f\n" + "0:.align 16\n" + "1:pop %%eax\n" + "movl %1,%%eax\n" + "int $0x80\n" + "2:popl %0\n" + "addl $(1b-0b),%0\n" + : "=a" (res) + : "i" (__NR_sigreturn)); + return res; + } + #elif defined(__x86_64__) + /* There are no known problems with any of the _syscallX() macros + * currently shipping for x86_64, but we still need to be able to define + * our own version so that we can override the location of the errno + * location (e.g. when using the clone() system call with the CLONE_VM + * option). + */ + #undef LSS_BODY + #define LSS_BODY(type,name, ...) \ + long __res; \ + __asm__ __volatile__("syscall" : "=a" (__res) : "0" (__NR_##name), \ + ##__VA_ARGS__ : "r11", "rcx", "memory"); \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type,name) \ + type LSS_NAME(name)() { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type,name,type1,arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(type, name, "D" ((long)(arg1))); \ + } + #undef _syscall2 + #define _syscall2(type,name,type1,arg1,type2,arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_BODY(type, name, "D" ((long)(arg1)), "S" ((long)(arg2))); \ + } + #undef _syscall3 + #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_BODY(type, name, "D" ((long)(arg1)), "S" ((long)(arg2)), \ + "d" ((long)(arg3))); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + long __res; \ + __asm__ __volatile__("movq %5,%%r10; syscall" : \ + "=a" (__res) : "0" (__NR_##name), \ + "D" ((long)(arg1)), "S" ((long)(arg2)), "d" ((long)(arg3)), \ + "g" ((long)(arg4)) : "r10", "r11", "rcx", "memory"); \ + LSS_RETURN(type, __res); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + long __res; \ + __asm__ __volatile__("movq %5,%%r10; movq %6,%%r8; syscall" : \ + "=a" (__res) : "0" (__NR_##name), \ + "D" ((long)(arg1)), "S" ((long)(arg2)), "d" ((long)(arg3)), \ + "g" ((long)(arg4)), "g" ((long)(arg5)) : \ + "r8", "r10", "r11", "rcx", "memory"); \ + LSS_RETURN(type, __res); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + long __res; \ + __asm__ __volatile__("movq %5,%%r10; movq %6,%%r8; movq %7,%%r9;" \ + "syscall" : \ + "=a" (__res) : "0" (__NR_##name), \ + "D" ((long)(arg1)), "S" ((long)(arg2)), "d" ((long)(arg3)), \ + "g" ((long)(arg4)), "g" ((long)(arg5)), "g" ((long)(arg6)) : \ + "r8", "r9", "r10", "r11", "rcx", "memory"); \ + LSS_RETURN(type, __res); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + { + register void *__tls __asm__("r8") = newtls; + register int *__ctid __asm__("r10") = child_tidptr; + __asm__ __volatile__(/* if (fn == NULL) + * return -EINVAL; + */ + "testq %4,%4\n" + "jz 1f\n" + + /* if (child_stack == NULL) + * return -EINVAL; + */ + "testq %5,%5\n" + "jz 1f\n" + + /* childstack -= 2*sizeof(void *); + */ + "subq $16,%5\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "movq %7,8(%5)\n" + "movq %4,0(%5)\n" + + /* %rax = syscall(%rax = __NR_clone, + * %rdi = flags, + * %rsi = child_stack, + * %rdx = parent_tidptr, + * %r8 = new_tls, + * %r10 = child_tidptr) + */ + "movq %2,%%rax\n" + "syscall\n" + + /* if (%rax != 0) + * return; + */ + "testq %%rax,%%rax\n" + "jnz 1f\n" + + /* In the child. Terminate frame pointer chain. + */ + "xorq %%rbp,%%rbp\n" + + /* Call "fn(arg)". + */ + "popq %%rax\n" + "popq %%rdi\n" + "call *%%rax\n" + + /* Call _exit(%ebx). + */ + "movq %%rax,%%rdi\n" + "movq %3,%%rax\n" + "syscall\n" + + /* Return to parent. + */ + "1:\n" + : "=a" (__res) + : "0"(-EINVAL), "i"(__NR_clone), "i"(__NR_exit), + "r"(fn), "S"(child_stack), "D"(flags), "r"(arg), + "d"(parent_tidptr), "r"(__tls), "r"(__ctid) + : "memory", "r11", "rcx"); + } + LSS_RETURN(int, __res); + } + LSS_INLINE _syscall4(int, fadvise64, int, fd, loff_t, offset, loff_t, len, + int, advice) + + LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) { + /* On x86-64, the kernel does not know how to return from + * a signal handler. Instead, it relies on user space to provide a + * restorer function that calls the rt_sigreturn() system call. + * Unfortunately, we cannot just reference the glibc version of this + * function, as glibc goes out of its way to make it inaccessible. + */ + void (*res)(void); + __asm__ __volatile__("call 2f\n" + "0:.align 16\n" + "1:movq %1,%%rax\n" + "syscall\n" + "2:popq %0\n" + "addq $(1b-0b),%0\n" + : "=a" (res) + : "i" (__NR_rt_sigreturn)); + return res; + } + #elif defined(__ARM_ARCH_3__) + #undef LSS_REG + #define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a + #undef LSS_BODY + #define LSS_BODY(type,name,args...) \ + register long __res_r0 __asm__("r0"); \ + long __res; \ + __asm__ __volatile__ (__syscall(name) \ + : "=r"(__res_r0) : args : "lr", "memory"); \ + __res = __res_r0; \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)() { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4)); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4), "r"(__r5)); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + { + register int __flags __asm__("r0") = flags; + register void *__stack __asm__("r1") = child_stack; + register void *__ptid __asm__("r2") = parent_tidptr; + register void *__tls __asm__("r3") = newtls; + register int *__ctid __asm__("r4") = child_tidptr; + __asm__ __volatile__(/* if (fn == NULL || child_stack == NULL) + * return -EINVAL; + */ + "cmp %2,#0\n" + "cmpne %3,#0\n" + "moveq %0,%1\n" + "beq 1f\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "str %5,[%3,#-4]!\n" + "str %2,[%3,#-4]!\n" + + /* %r0 = syscall(%r0 = flags, + * %r1 = child_stack, + * %r2 = parent_tidptr, + * %r3 = newtls, + * %r4 = child_tidptr) + */ + __syscall(clone)"\n" + + /* if (%r0 != 0) + * return %r0; + */ + "movs %0,r0\n" + "bne 1f\n" + + /* In the child, now. Call "fn(arg)". + */ + "ldr r0,[sp, #4]\n" + "mov lr,pc\n" + "ldr pc,[sp]\n" + + /* Call _exit(%r0). + */ + __syscall(exit)"\n" + "1:\n" + : "=r" (__res) + : "i"(-EINVAL), + "r"(fn), "r"(__stack), "r"(__flags), "r"(arg), + "r"(__ptid), "r"(__tls), "r"(__ctid) + : "lr", "memory"); + } + LSS_RETURN(int, __res); + } + #elif defined(__ARM_EABI__) + /* Most definitions of _syscallX() neglect to mark "memory" as being + * clobbered. This causes problems with compilers, that do a better job + * at optimizing across __asm__ calls. + * So, we just have to redefine all fo the _syscallX() macros. + */ + #undef LSS_REG + #define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a + #undef LSS_BODY + #define LSS_BODY(type,name,args...) \ + register long __res_r0 __asm__("r0"); \ + long __res; \ + __asm__ __volatile__ ("push {r7}\n" \ + "mov r7, %1\n" \ + "swi 0x0\n" \ + "pop {r7}\n" \ + : "=r"(__res_r0) \ + : "i"(__NR_##name) , ## args \ + : "lr", "memory"); \ + __res = __res_r0; \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)() { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4)); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4), "r"(__r5)); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + { + register int __flags __asm__("r0") = flags; + register void *__stack __asm__("r1") = child_stack; + register void *__ptid __asm__("r2") = parent_tidptr; + register void *__tls __asm__("r3") = newtls; + register int *__ctid __asm__("r4") = child_tidptr; + __asm__ __volatile__(/* if (fn == NULL || child_stack == NULL) + * return -EINVAL; + */ + "cmp %2,#0\n" + "cmpne %3,#0\n" + "moveq %0,%1\n" + "beq 1f\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "str %5,[%3,#-4]!\n" + "str %2,[%3,#-4]!\n" + + /* %r0 = syscall(%r0 = flags, + * %r1 = child_stack, + * %r2 = parent_tidptr, + * %r3 = newtls, + * %r4 = child_tidptr) + */ + "mov r7, %9\n" + "swi 0x0\n" + + /* if (%r0 != 0) + * return %r0; + */ + "movs %0,r0\n" + "bne 1f\n" + + /* In the child, now. Call "fn(arg)". + */ + "ldr r0,[sp, #4]\n" + "mov lr,pc\n" + "ldr pc,[sp]\n" + + /* Call _exit(%r0). + */ + "mov r7, %10\n" + "swi 0x0\n" + "1:\n" + : "=r" (__res) + : "i"(-EINVAL), + "r"(fn), "r"(__stack), "r"(__flags), "r"(arg), + "r"(__ptid), "r"(__tls), "r"(__ctid), + "i"(__NR_clone), "i"(__NR_exit) + : "lr", "memory"); + } + LSS_RETURN(int, __res); + } + #elif defined(__mips__) + #undef LSS_REG + #define LSS_REG(r,a) register unsigned long __r##r __asm__("$"#r) = \ + (unsigned long)(a) + #undef LSS_BODY + #define LSS_BODY(type,name,r7,...) \ + register unsigned long __v0 __asm__("$2") = __NR_##name; \ + __asm__ __volatile__ ("syscall\n" \ + : "=&r"(__v0), r7 (__r7) \ + : "0"(__v0), ##__VA_ARGS__ \ + : "$8", "$9", "$10", "$11", "$12", \ + "$13", "$14", "$15", "$24", "memory"); \ + LSS_RETURN(type, __v0, __r7) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)() { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_BODY(type, name, "=r"); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_REG(4, arg1); LSS_BODY(type, name, "=r", "r"(__r4)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_REG(4, arg1); LSS_REG(5, arg2); \ + LSS_BODY(type, name, "=r", "r"(__r4), "r"(__r5)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_BODY(type, name, "=r", "r"(__r4), "r"(__r5), "r"(__r6)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); \ + LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6)); \ + } + #undef _syscall5 + #if _MIPS_SIM == _MIPS_SIM_ABI32 + /* The old 32bit MIPS system call API passes the fifth and sixth argument + * on the stack, whereas the new APIs use registers "r8" and "r9". + */ + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); \ + register unsigned long __v0 __asm__("$2"); \ + __asm__ __volatile__ (".set noreorder\n" \ + "lw $2, %6\n" \ + "subu $29, 32\n" \ + "sw $2, 16($29)\n" \ + "li $2, %2\n" \ + "syscall\n" \ + "addiu $29, 32\n" \ + ".set reorder\n" \ + : "=&r"(__v0), "+r" (__r7) \ + : "i" (__NR_##name), "r"(__r4), "r"(__r5), \ + "r"(__r6), "m" ((unsigned long)arg5) \ + : "$8", "$9", "$10", "$11", "$12", \ + "$13", "$14", "$15", "$24", "memory"); \ + LSS_RETURN(type, __v0, __r7); \ + } + #else + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); LSS_REG(8, arg5); \ + LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6), \ + "r"(__r8)); \ + } + #endif + #undef _syscall6 + #if _MIPS_SIM == _MIPS_SIM_ABI32 + /* The old 32bit MIPS system call API passes the fifth and sixth argument + * on the stack, whereas the new APIs use registers "r8" and "r9". + */ + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); \ + register unsigned long __v0 __asm__("$2"); \ + __asm__ __volatile__ (".set noreorder\n" \ + "lw $2, %6\n" \ + "lw $8, %7\n" \ + "subu $29, 32\n" \ + "sw $2, 16($29)\n" \ + "sw $8, 20($29)\n" \ + "li $2, %2\n" \ + "syscall\n" \ + "addiu $29, 32\n" \ + ".set reorder\n" \ + : "=&r"(__v0), "+r" (__r7) \ + : "i" (__NR_##name), "r"(__r4), "r"(__r5), \ + "r"(__r6), "r" ((unsigned long)arg5), \ + "r" ((unsigned long)arg6) \ + : "$8", "$9", "$10", "$11", "$12", \ + "$13", "$14", "$15", "$24", "memory"); \ + LSS_RETURN(type, __v0, __r7); \ + } + #else + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5,type6 arg6) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); LSS_REG(8, arg5); LSS_REG(9, arg6); \ + LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6), \ + "r"(__r8), "r"(__r9)); \ + } + #endif + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + register unsigned long __v0 __asm__("$2"); + register unsigned long __r7 __asm__("$7") = (unsigned long)newtls; + { + register int __flags __asm__("$4") = flags; + register void *__stack __asm__("$5") = child_stack; + register void *__ptid __asm__("$6") = parent_tidptr; + register int *__ctid __asm__("$8") = child_tidptr; + __asm__ __volatile__( + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "subu $29,24\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "sub $29,16\n" + #else + "dsubu $29,16\n" + #endif + + /* if (fn == NULL || child_stack == NULL) + * return -EINVAL; + */ + "li %0,%2\n" + "beqz %5,1f\n" + "beqz %6,1f\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "subu %6,32\n" + "sw %5,0(%6)\n" + "sw %8,4(%6)\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "sub %6,32\n" + "sw %5,0(%6)\n" + "sw %8,8(%6)\n" + #else + "dsubu %6,32\n" + "sd %5,0(%6)\n" + "sd %8,8(%6)\n" + #endif + + /* $7 = syscall($4 = flags, + * $5 = child_stack, + * $6 = parent_tidptr, + * $7 = newtls, + * $8 = child_tidptr) + */ + "li $2,%3\n" + "syscall\n" + + /* if ($7 != 0) + * return $2; + */ + "bnez $7,1f\n" + "bnez $2,1f\n" + + /* In the child, now. Call "fn(arg)". + */ + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "lw $25,0($29)\n" + "lw $4,4($29)\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "lw $25,0($29)\n" + "lw $4,8($29)\n" + #else + "ld $25,0($29)\n" + "ld $4,8($29)\n" + #endif + "jalr $25\n" + + /* Call _exit($2) + */ + "move $4,$2\n" + "li $2,%4\n" + "syscall\n" + + "1:\n" + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "addu $29, 24\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "add $29, 16\n" + #else + "daddu $29,16\n" + #endif + : "=&r" (__v0), "=r" (__r7) + : "i"(-EINVAL), "i"(__NR_clone), "i"(__NR_exit), + "r"(fn), "r"(__stack), "r"(__flags), "r"(arg), + "r"(__ptid), "r"(__r7), "r"(__ctid) + : "$9", "$10", "$11", "$12", "$13", "$14", "$15", + "$24", "memory"); + } + LSS_RETURN(int, __v0, __r7); + } + #elif defined (__PPC__) + #undef LSS_LOADARGS_0 + #define LSS_LOADARGS_0(name, dummy...) \ + __sc_0 = __NR_##name + #undef LSS_LOADARGS_1 + #define LSS_LOADARGS_1(name, arg1) \ + LSS_LOADARGS_0(name); \ + __sc_3 = (unsigned long) (arg1) + #undef LSS_LOADARGS_2 + #define LSS_LOADARGS_2(name, arg1, arg2) \ + LSS_LOADARGS_1(name, arg1); \ + __sc_4 = (unsigned long) (arg2) + #undef LSS_LOADARGS_3 + #define LSS_LOADARGS_3(name, arg1, arg2, arg3) \ + LSS_LOADARGS_2(name, arg1, arg2); \ + __sc_5 = (unsigned long) (arg3) + #undef LSS_LOADARGS_4 + #define LSS_LOADARGS_4(name, arg1, arg2, arg3, arg4) \ + LSS_LOADARGS_3(name, arg1, arg2, arg3); \ + __sc_6 = (unsigned long) (arg4) + #undef LSS_LOADARGS_5 + #define LSS_LOADARGS_5(name, arg1, arg2, arg3, arg4, arg5) \ + LSS_LOADARGS_4(name, arg1, arg2, arg3, arg4); \ + __sc_7 = (unsigned long) (arg5) + #undef LSS_LOADARGS_6 + #define LSS_LOADARGS_6(name, arg1, arg2, arg3, arg4, arg5, arg6) \ + LSS_LOADARGS_5(name, arg1, arg2, arg3, arg4, arg5); \ + __sc_8 = (unsigned long) (arg6) + #undef LSS_ASMINPUT_0 + #define LSS_ASMINPUT_0 "0" (__sc_0) + #undef LSS_ASMINPUT_1 + #define LSS_ASMINPUT_1 LSS_ASMINPUT_0, "1" (__sc_3) + #undef LSS_ASMINPUT_2 + #define LSS_ASMINPUT_2 LSS_ASMINPUT_1, "2" (__sc_4) + #undef LSS_ASMINPUT_3 + #define LSS_ASMINPUT_3 LSS_ASMINPUT_2, "3" (__sc_5) + #undef LSS_ASMINPUT_4 + #define LSS_ASMINPUT_4 LSS_ASMINPUT_3, "4" (__sc_6) + #undef LSS_ASMINPUT_5 + #define LSS_ASMINPUT_5 LSS_ASMINPUT_4, "5" (__sc_7) + #undef LSS_ASMINPUT_6 + #define LSS_ASMINPUT_6 LSS_ASMINPUT_5, "6" (__sc_8) + #undef LSS_BODY + #define LSS_BODY(nr, type, name, args...) \ + long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0"); \ + register unsigned long __sc_3 __asm__ ("r3"); \ + register unsigned long __sc_4 __asm__ ("r4"); \ + register unsigned long __sc_5 __asm__ ("r5"); \ + register unsigned long __sc_6 __asm__ ("r6"); \ + register unsigned long __sc_7 __asm__ ("r7"); \ + register unsigned long __sc_8 __asm__ ("r8"); \ + \ + LSS_LOADARGS_##nr(name, args); \ + __asm__ __volatile__ \ + ("sc\n\t" \ + "mfcr %0" \ + : "=&r" (__sc_0), \ + "=&r" (__sc_3), "=&r" (__sc_4), \ + "=&r" (__sc_5), "=&r" (__sc_6), \ + "=&r" (__sc_7), "=&r" (__sc_8) \ + : LSS_ASMINPUT_##nr \ + : "cr0", "ctr", "memory", \ + "r9", "r10", "r11", "r12"); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + LSS_RETURN(type, __sc_ret, __sc_err) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(0, type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(1, type, name, arg1); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_BODY(2, type, name, arg1, arg2); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_BODY(3, type, name, arg1, arg2, arg3); \ + } + #undef _syscall4 + #define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_BODY(4, type, name, arg1, arg2, arg3, arg4); \ + } + #undef _syscall5 + #define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_BODY(5, type, name, arg1, arg2, arg3, arg4, arg5); \ + } + #undef _syscall6 + #define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_BODY(6, type, name, arg1, arg2, arg3, arg4, arg5, arg6); \ + } + /* clone function adapted from glibc 2.3.6 clone.S */ + /* TODO(csilvers): consider wrapping some args up in a struct, like we + * do for i386's _syscall6, so we can compile successfully on gcc 2.95 + */ + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __ret, __err; + { + register int (*__fn)(void *) __asm__ ("r8") = fn; + register void *__cstack __asm__ ("r4") = child_stack; + register int __flags __asm__ ("r3") = flags; + register void * __arg __asm__ ("r9") = arg; + register int * __ptidptr __asm__ ("r5") = parent_tidptr; + register void * __newtls __asm__ ("r6") = newtls; + register int * __ctidptr __asm__ ("r7") = child_tidptr; + __asm__ __volatile__( + /* check for fn == NULL + * and child_stack == NULL + */ + "cmpwi cr0, %6, 0\n\t" + "cmpwi cr1, %7, 0\n\t" + "cror cr0*4+eq, cr1*4+eq, cr0*4+eq\n\t" + "beq- cr0, 1f\n\t" + + /* set up stack frame for child */ + "clrrwi %7, %7, 4\n\t" + "li 0, 0\n\t" + "stwu 0, -16(%7)\n\t" + + /* fn, arg, child_stack are saved across the syscall: r28-30 */ + "mr 28, %6\n\t" + "mr 29, %7\n\t" + "mr 27, %9\n\t" + + /* syscall */ + "li 0, %4\n\t" + /* flags already in r3 + * child_stack already in r4 + * ptidptr already in r5 + * newtls already in r6 + * ctidptr already in r7 + */ + "sc\n\t" + + /* Test if syscall was successful */ + "cmpwi cr1, 3, 0\n\t" + "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t" + "bne- cr1, 1f\n\t" + + /* Do the function call */ + "mtctr 28\n\t" + "mr 3, 27\n\t" + "bctrl\n\t" + + /* Call _exit(r3) */ + "li 0, %5\n\t" + "sc\n\t" + + /* Return to parent */ + "1:\n" + "mfcr %1\n\t" + "mr %0, 3\n\t" + : "=r" (__ret), "=r" (__err) + : "0" (-1), "1" (EINVAL), + "i" (__NR_clone), "i" (__NR_exit), + "r" (__fn), "r" (__cstack), "r" (__flags), + "r" (__arg), "r" (__ptidptr), "r" (__newtls), + "r" (__ctidptr) + : "cr0", "cr1", "memory", "ctr", + "r0", "r29", "r27", "r28"); + } + LSS_RETURN(int, __ret, __err); + } + #endif + #define __NR__exit __NR_exit + #define __NR__gettid __NR_gettid + #define __NR__mremap __NR_mremap + LSS_INLINE _syscall1(int, chdir, const char *,p) + LSS_INLINE _syscall1(int, close, int, f) + LSS_INLINE _syscall1(int, dup, int, f) + LSS_INLINE _syscall2(int, dup2, int, s, + int, d) + LSS_INLINE _syscall3(int, execve, const char*, f, + const char*const*,a,const char*const*, e) + LSS_INLINE _syscall1(int, _exit, int, e) + LSS_INLINE _syscall3(int, fcntl, int, f, + int, c, long, a) + LSS_INLINE _syscall0(pid_t, fork) + LSS_INLINE _syscall2(int, fstat, int, f, + struct kernel_stat*, b) + LSS_INLINE _syscall2(int, fstatfs, int, f, + struct kernel_statfs*, b) + LSS_INLINE _syscall4(int, futex, int*, a, + int, o, int, v, + struct kernel_timespec*, t) + LSS_INLINE _syscall3(int, getdents, int, f, + struct kernel_dirent*, d, int, c) + LSS_INLINE _syscall3(int, getdents64, int, f, + struct kernel_dirent64*, d, int, c) + LSS_INLINE _syscall0(gid_t, getegid) + LSS_INLINE _syscall0(uid_t, geteuid) + LSS_INLINE _syscall0(pid_t, getpgrp) + LSS_INLINE _syscall0(pid_t, getpid) + LSS_INLINE _syscall0(pid_t, getppid) + LSS_INLINE _syscall2(int, getpriority, int, a, + int, b) +#if !defined(__ARM_EABI__) + LSS_INLINE _syscall2(int, getrlimit, int, r, + struct kernel_rlimit*, l) +#endif + LSS_INLINE _syscall1(pid_t, getsid, pid_t, p) + LSS_INLINE _syscall0(pid_t, _gettid) + LSS_INLINE _syscall5(int, setxattr, const char *,p, + const char *, n, const void *,v, + size_t, s, int, f) + LSS_INLINE _syscall5(int, lsetxattr, const char *,p, + const char *, n, const void *,v, + size_t, s, int, f) + LSS_INLINE _syscall4(ssize_t, getxattr, const char *,p, + const char *, n, void *, v, size_t, s) + LSS_INLINE _syscall4(ssize_t, lgetxattr, const char *,p, + const char *, n, void *, v, size_t, s) + LSS_INLINE _syscall2(int, kill, pid_t, p, + int, s) + LSS_INLINE _syscall3(off_t, lseek, int, f, + off_t, o, int, w) + LSS_INLINE _syscall2(int, munmap, void*, s, + size_t, l) + LSS_INLINE _syscall6(long, move_pages, pid_t, p, + unsigned long, n, void **,g, int *, d, + int *, s, int, f) + LSS_INLINE _syscall5(void*, _mremap, void*, o, + size_t, os, size_t, ns, + unsigned long, f, void *, a) + LSS_INLINE _syscall3(int, open, const char*, p, + int, f, int, m) + LSS_INLINE _syscall3(int, poll, struct kernel_pollfd*, u, + unsigned int, n, int, t) + LSS_INLINE _syscall2(int, prctl, int, o, + long, a) + LSS_INLINE _syscall4(long, ptrace, int, r, + pid_t, p, void *, a, void *, d) + LSS_INLINE _syscall3(ssize_t, read, int, f, + void *, b, size_t, c) + LSS_INLINE _syscall3(int, readlink, const char*, p, + char*, b, size_t, s) + LSS_INLINE _syscall4(int, rt_sigaction, int, s, + const struct kernel_sigaction*, a, + struct kernel_sigaction*, o, size_t, c) + LSS_INLINE _syscall2(int, rt_sigpending, struct kernel_sigset_t *, s, + size_t, c) + LSS_INLINE _syscall4(int, rt_sigprocmask, int, h, + const struct kernel_sigset_t*, s, + struct kernel_sigset_t*, o, size_t, c) + LSS_INLINE _syscall2(int, rt_sigsuspend, + const struct kernel_sigset_t*, s, size_t, c) + LSS_INLINE _syscall3(int, sched_getaffinity,pid_t, p, + unsigned int, l, unsigned long *, m) + LSS_INLINE _syscall3(int, sched_setaffinity,pid_t, p, + unsigned int, l, unsigned long *, m) + LSS_INLINE _syscall0(int, sched_yield) + LSS_INLINE _syscall1(long, set_tid_address, int *, t) + LSS_INLINE _syscall1(int, setfsgid, gid_t, g) + LSS_INLINE _syscall1(int, setfsuid, uid_t, u) + LSS_INLINE _syscall2(int, setpgid, pid_t, p, + pid_t, g) + LSS_INLINE _syscall3(int, setpriority, int, a, + int, b, int, p) + LSS_INLINE _syscall3(int, setresgid, gid_t, r, + gid_t, e, gid_t, s) + LSS_INLINE _syscall3(int, setresuid, uid_t, r, + uid_t, e, uid_t, s) + LSS_INLINE _syscall2(int, setrlimit, int, r, + const struct kernel_rlimit*, l) + LSS_INLINE _syscall0(pid_t, setsid) + LSS_INLINE _syscall2(int, sigaltstack, const stack_t*, s, + const stack_t*, o) + LSS_INLINE _syscall2(int, stat, const char*, f, + struct kernel_stat*, b) + LSS_INLINE _syscall2(int, statfs, const char*, f, + struct kernel_statfs*, b) + LSS_INLINE _syscall1(int, unlink, const char*, f) + LSS_INLINE _syscall3(ssize_t, write, int, f, + const void *, b, size_t, c) + LSS_INLINE _syscall3(ssize_t, writev, int, f, + const struct kernel_iovec*, v, size_t, c) + #if defined(__x86_64__) || \ + (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32) + LSS_INLINE _syscall3(int, recvmsg, int, s, + struct kernel_msghdr*, m, int, f) + LSS_INLINE _syscall3(int, sendmsg, int, s, + const struct kernel_msghdr*, m, int, f) + LSS_INLINE _syscall6(int, sendto, int, s, + const void*, m, size_t, l, + int, f, + const struct kernel_sockaddr*, a, int, t) + LSS_INLINE _syscall2(int, shutdown, int, s, + int, h) + LSS_INLINE _syscall3(int, socket, int, d, + int, t, int, p) + LSS_INLINE _syscall4(int, socketpair, int, d, + int, t, int, p, int*, s) + #endif + #if defined(__x86_64__) + LSS_INLINE _syscall6(void*, mmap, void*, s, + size_t, l, int, p, + int, f, int, d, + __off64_t, o) + LSS_INLINE _syscall4(int, newfstatat, int, d, + const char *, p, + struct kernel_stat*, b, int, f) + + LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) { + return LSS_NAME(setfsgid)(gid); + } + + LSS_INLINE int LSS_NAME(setfsuid32)(uid_t uid) { + return LSS_NAME(setfsuid)(uid); + } + + LSS_INLINE int LSS_NAME(setresgid32)(gid_t rgid, gid_t egid, gid_t sgid) { + return LSS_NAME(setresgid)(rgid, egid, sgid); + } + + LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) { + return LSS_NAME(setresuid)(ruid, euid, suid); + } + + LSS_INLINE int LSS_NAME(sigaction)(int signum, + const struct kernel_sigaction *act, + struct kernel_sigaction *oldact) { + /* On x86_64, the kernel requires us to always set our own + * SA_RESTORER in order to be able to return from a signal handler. + * This function must have a "magic" signature that the "gdb" + * (and maybe the kernel?) can recognize. + */ + if (act != NULL && !(act->sa_flags & SA_RESTORER)) { + struct kernel_sigaction a = *act; + a.sa_flags |= SA_RESTORER; + a.sa_restorer = LSS_NAME(restore_rt)(); + return LSS_NAME(rt_sigaction)(signum, &a, oldact, + (KERNEL_NSIG+7)/8); + } else { + return LSS_NAME(rt_sigaction)(signum, act, oldact, + (KERNEL_NSIG+7)/8); + } + } + + LSS_INLINE int LSS_NAME(sigpending)(struct kernel_sigset_t *set) { + return LSS_NAME(rt_sigpending)(set, (KERNEL_NSIG+7)/8); + } + + LSS_INLINE int LSS_NAME(sigprocmask)(int how, + const struct kernel_sigset_t *set, + struct kernel_sigset_t *oldset) { + return LSS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG+7)/8); + } + + LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) { + return LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG+7)/8); + } + #endif + #if defined(__x86_64__) || defined(__ARM_ARCH_3__) || \ + defined(__ARM_EABI__) || \ + (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32) + LSS_INLINE _syscall4(pid_t, wait4, pid_t, p, + int*, s, int, o, + struct kernel_rusage*, r) + + LSS_INLINE pid_t LSS_NAME(waitpid)(pid_t pid, int *status, int options){ + return LSS_NAME(wait4)(pid, status, options, 0); + } + #endif + #if defined(__i386__) || defined(__x86_64__) + LSS_INLINE _syscall4(int, openat, int, d, const char *, p, int, f, int, m) + LSS_INLINE _syscall3(int, unlinkat, int, d, const char *, p, int, f) + #endif + #if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) + #define __NR__setfsgid32 __NR_setfsgid32 + #define __NR__setfsuid32 __NR_setfsuid32 + #define __NR__setresgid32 __NR_setresgid32 + #define __NR__setresuid32 __NR_setresuid32 +#if defined(__ARM_EABI__) + LSS_INLINE _syscall2(int, ugetrlimit, int, r, + struct kernel_rlimit*, l) +#endif + LSS_INLINE _syscall1(int, _setfsgid32, gid_t, f) + LSS_INLINE _syscall1(int, _setfsuid32, uid_t, f) + LSS_INLINE _syscall3(int, _setresgid32, gid_t, r, + gid_t, e, gid_t, s) + LSS_INLINE _syscall3(int, _setresuid32, uid_t, r, + uid_t, e, uid_t, s) + + LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) { + int rc; + if ((rc = LSS_NAME(_setfsgid32)(gid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)gid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setfsgid)(gid); + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(setfsuid32)(uid_t uid) { + int rc; + if ((rc = LSS_NAME(_setfsuid32)(uid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)uid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setfsuid)(uid); + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(setresgid32)(gid_t rgid, gid_t egid, gid_t sgid) { + int rc; + if ((rc = LSS_NAME(_setresgid32)(rgid, egid, sgid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)rgid & ~0xFFFFu || + (unsigned int)egid & ~0xFFFFu || + (unsigned int)sgid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setresgid)(rgid, egid, sgid); + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) { + int rc; + if ((rc = LSS_NAME(_setresuid32)(ruid, euid, suid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)ruid & ~0xFFFFu || + (unsigned int)euid & ~0xFFFFu || + (unsigned int)suid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setresuid)(ruid, euid, suid); + } + } + return rc; + } + #endif + LSS_INLINE int LSS_NAME(sigemptyset)(struct kernel_sigset_t *set) { + memset(&set->sig, 0, sizeof(set->sig)); + return 0; + } + + LSS_INLINE int LSS_NAME(sigfillset)(struct kernel_sigset_t *set) { + memset(&set->sig, -1, sizeof(set->sig)); + return 0; + } + + LSS_INLINE int LSS_NAME(sigaddset)(struct kernel_sigset_t *set, + int signum) { + if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { + LSS_ERRNO = EINVAL; + return -1; + } else { + set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] + |= 1UL << ((signum - 1) % (8*sizeof(set->sig[0]))); + return 0; + } + } + + LSS_INLINE int LSS_NAME(sigdelset)(struct kernel_sigset_t *set, + int signum) { + if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { + LSS_ERRNO = EINVAL; + return -1; + } else { + set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] + &= ~(1UL << ((signum - 1) % (8*sizeof(set->sig[0])))); + return 0; + } + } + + LSS_INLINE int LSS_NAME(sigismember)(struct kernel_sigset_t *set, + int signum) { + if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { + LSS_ERRNO = EINVAL; + return -1; + } else { + return !!(set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] & + (1UL << ((signum - 1) % (8*sizeof(set->sig[0]))))); + } + } + #if defined(__i386__) || defined(__ARM_ARCH_3__) || \ + defined(__ARM_EABI__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || defined(__PPC__) + #define __NR__sigaction __NR_sigaction + #define __NR__sigpending __NR_sigpending + #define __NR__sigprocmask __NR_sigprocmask + #define __NR__sigsuspend __NR_sigsuspend + #define __NR__socketcall __NR_socketcall + LSS_INLINE _syscall2(int, fstat64, int, f, + struct kernel_stat64 *, b) + LSS_INLINE _syscall5(int, _llseek, uint, fd, ulong, hi, ulong, lo, + loff_t *, res, uint, wh) +#if !defined(__ARM_EABI__) + LSS_INLINE _syscall1(void*, mmap, void*, a) +#endif + LSS_INLINE _syscall6(void*, mmap2, void*, s, + size_t, l, int, p, + int, f, int, d, + __off64_t, o) + LSS_INLINE _syscall3(int, _sigaction, int, s, + const struct kernel_old_sigaction*, a, + struct kernel_old_sigaction*, o) + LSS_INLINE _syscall1(int, _sigpending, unsigned long*, s) + LSS_INLINE _syscall3(int, _sigprocmask, int, h, + const unsigned long*, s, + unsigned long*, o) + #ifdef __PPC__ + LSS_INLINE _syscall1(int, _sigsuspend, unsigned long, s) + #else + LSS_INLINE _syscall3(int, _sigsuspend, const void*, a, + int, b, + unsigned long, s) + #endif + LSS_INLINE _syscall2(int, stat64, const char *, p, + struct kernel_stat64 *, b) + + LSS_INLINE int LSS_NAME(sigaction)(int signum, + const struct kernel_sigaction *act, + struct kernel_sigaction *oldact) { + int old_errno = LSS_ERRNO; + int rc; + struct kernel_sigaction a; + if (act != NULL) { + a = *act; + #ifdef __i386__ + /* On i386, the kernel requires us to always set our own + * SA_RESTORER when using realtime signals. Otherwise, it does not + * know how to return from a signal handler. This function must have + * a "magic" signature that the "gdb" (and maybe the kernel?) can + * recognize. + * Apparently, a SA_RESTORER is implicitly set by the kernel, when + * using non-realtime signals. + * + * TODO: Test whether ARM needs a restorer + */ + if (!(a.sa_flags & SA_RESTORER)) { + a.sa_flags |= SA_RESTORER; + a.sa_restorer = (a.sa_flags & SA_SIGINFO) + ? LSS_NAME(restore_rt)() : LSS_NAME(restore)(); + } + #endif + } + rc = LSS_NAME(rt_sigaction)(signum, act ? &a : act, oldact, + (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + struct kernel_old_sigaction oa, ooa, *ptr_a = &oa, *ptr_oa = &ooa; + if (!act) { + ptr_a = NULL; + } else { + oa.sa_handler_ = act->sa_handler_; + memcpy(&oa.sa_mask, &act->sa_mask, sizeof(oa.sa_mask)); + #ifndef __mips__ + oa.sa_restorer = act->sa_restorer; + #endif + oa.sa_flags = act->sa_flags; + } + if (!oldact) { + ptr_oa = NULL; + } + LSS_ERRNO = old_errno; + rc = LSS_NAME(_sigaction)(signum, ptr_a, ptr_oa); + if (rc == 0 && oldact) { + if (act) { + memcpy(oldact, act, sizeof(*act)); + } else { + memset(oldact, 0, sizeof(*oldact)); + } + oldact->sa_handler_ = ptr_oa->sa_handler_; + oldact->sa_flags = ptr_oa->sa_flags; + memcpy(&oldact->sa_mask, &ptr_oa->sa_mask, sizeof(ptr_oa->sa_mask)); + #ifndef __mips__ + oldact->sa_restorer = ptr_oa->sa_restorer; + #endif + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(sigpending)(struct kernel_sigset_t *set) { + int old_errno = LSS_ERRNO; + int rc = LSS_NAME(rt_sigpending)(set, (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + LSS_ERRNO = old_errno; + LSS_NAME(sigemptyset)(set); + rc = LSS_NAME(_sigpending)(&set->sig[0]); + } + return rc; + } + + LSS_INLINE int LSS_NAME(sigprocmask)(int how, + const struct kernel_sigset_t *set, + struct kernel_sigset_t *oldset) { + int olderrno = LSS_ERRNO; + int rc = LSS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + LSS_ERRNO = olderrno; + if (oldset) { + LSS_NAME(sigemptyset)(oldset); + } + rc = LSS_NAME(_sigprocmask)(how, + set ? &set->sig[0] : NULL, + oldset ? &oldset->sig[0] : NULL); + } + return rc; + } + + LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) { + int olderrno = LSS_ERRNO; + int rc = LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + LSS_ERRNO = olderrno; + rc = LSS_NAME(_sigsuspend)( + #ifndef __PPC__ + set, 0, + #endif + set->sig[0]); + } + return rc; + } + #endif + #if defined(__PPC__) + #undef LSS_SC_LOADARGS_0 + #define LSS_SC_LOADARGS_0(dummy...) + #undef LSS_SC_LOADARGS_1 + #define LSS_SC_LOADARGS_1(arg1) \ + __sc_4 = (unsigned long) (arg1) + #undef LSS_SC_LOADARGS_2 + #define LSS_SC_LOADARGS_2(arg1, arg2) \ + LSS_SC_LOADARGS_1(arg1); \ + __sc_5 = (unsigned long) (arg2) + #undef LSS_SC_LOADARGS_3 + #define LSS_SC_LOADARGS_3(arg1, arg2, arg3) \ + LSS_SC_LOADARGS_2(arg1, arg2); \ + __sc_6 = (unsigned long) (arg3) + #undef LSS_SC_LOADARGS_4 + #define LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4) \ + LSS_SC_LOADARGS_3(arg1, arg2, arg3); \ + __sc_7 = (unsigned long) (arg4) + #undef LSS_SC_LOADARGS_5 + #define LSS_SC_LOADARGS_5(arg1, arg2, arg3, arg4, arg5) \ + LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4); \ + __sc_8 = (unsigned long) (arg5) + #undef LSS_SC_BODY + #define LSS_SC_BODY(nr, type, opt, args...) \ + long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0") = __NR_socketcall; \ + register unsigned long __sc_3 __asm__ ("r3") = opt; \ + register unsigned long __sc_4 __asm__ ("r4"); \ + register unsigned long __sc_5 __asm__ ("r5"); \ + register unsigned long __sc_6 __asm__ ("r6"); \ + register unsigned long __sc_7 __asm__ ("r7"); \ + register unsigned long __sc_8 __asm__ ("r8"); \ + LSS_SC_LOADARGS_##nr(args); \ + __asm__ __volatile__ \ + ("stwu 1, -48(1)\n\t" \ + "stw 4, 20(1)\n\t" \ + "stw 5, 24(1)\n\t" \ + "stw 6, 28(1)\n\t" \ + "stw 7, 32(1)\n\t" \ + "stw 8, 36(1)\n\t" \ + "addi 4, 1, 20\n\t" \ + "sc\n\t" \ + "mfcr %0" \ + : "=&r" (__sc_0), \ + "=&r" (__sc_3), "=&r" (__sc_4), \ + "=&r" (__sc_5), "=&r" (__sc_6), \ + "=&r" (__sc_7), "=&r" (__sc_8) \ + : LSS_ASMINPUT_##nr \ + : "cr0", "ctr", "memory"); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + LSS_RETURN(type, __sc_ret, __sc_err) + + LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg, + int flags){ + LSS_SC_BODY(3, ssize_t, 17, s, msg, flags); + } + + LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s, + const struct kernel_msghdr *msg, + int flags) { + LSS_SC_BODY(3, ssize_t, 16, s, msg, flags); + } + + // TODO(csilvers): why is this ifdef'ed out? +#if 0 + LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len, + int flags, + const struct kernel_sockaddr *to, + unsigned int tolen) { + LSS_BODY(6, ssize_t, 11, s, buf, len, flags, to, tolen); + } +#endif + + LSS_INLINE int LSS_NAME(shutdown)(int s, int how) { + LSS_SC_BODY(2, int, 13, s, how); + } + + LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) { + LSS_SC_BODY(3, int, 1, domain, type, protocol); + } + + LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol, + int sv[2]) { + LSS_SC_BODY(4, int, 8, d, type, protocol, sv); + } + #endif + #if defined(__ARM_EABI__) + LSS_INLINE _syscall3(ssize_t, recvmsg, int, s, struct kernel_msghdr*, msg, + int, flags); + LSS_INLINE _syscall3(ssize_t, sendmsg, int, s, const struct kernel_msghdr*, + msg, int, flags); + LSS_INLINE _syscall6(ssize_t, sendto, int, s, const void*, buf, size_t, len, + int, falgs, const struct kernel_sockaddr*, to, + unsigned int, tolen); + LSS_INLINE _syscall2(int, shutdown, int, s, int, how); + LSS_INLINE _syscall3(int, socket, int, domain, int, type, int, protocol); + LSS_INLINE _syscall4(int, socketpair, int, d, int, type, int, protocol, + int*, sv); + #endif + #if defined(__i386__) || defined(__ARM_ARCH_3__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) + #define __NR__socketcall __NR_socketcall + LSS_INLINE _syscall2(int, _socketcall, int, c, + va_list, a) + LSS_INLINE int LSS_NAME(socketcall)(int op, ...) { + int rc; + va_list ap; + va_start(ap, op); + rc = LSS_NAME(_socketcall)(op, ap); + va_end(ap); + return rc; + } + LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg, + int flags){ + return (ssize_t)LSS_NAME(socketcall)(17, s, msg, flags); + } + LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s, + const struct kernel_msghdr *msg, + int flags) { + return (ssize_t)LSS_NAME(socketcall)(16, s, msg, flags); + } + LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len, + int flags, + const struct kernel_sockaddr *to, + unsigned int tolen) { + return (ssize_t)LSS_NAME(socketcall)(11, s, buf, len, flags, to, tolen); + } + LSS_INLINE int LSS_NAME(shutdown)(int s, int how) { + return LSS_NAME(socketcall)(13, s, how); + } + LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) { + return LSS_NAME(socketcall)(1, domain, type, protocol); + } + + LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol, + int sv[2]) { + return LSS_NAME(socketcall)(8, d, type, protocol, sv); + } + #endif + #if defined(__i386__) || defined(__PPC__) + LSS_INLINE _syscall4(int, fstatat64, int, d, + const char *, p, + struct kernel_stat64 *, b, int, f) + #endif + #if defined(__i386__) || defined(__PPC__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) + LSS_INLINE _syscall3(pid_t, waitpid, pid_t, p, + int*, s, int, o) + #endif + #if defined(__mips__) + /* sys_pipe() on MIPS has non-standard calling conventions, as it returns + * both file handles through CPU registers. + */ + LSS_INLINE int LSS_NAME(pipe)(int *p) { + register unsigned long __v0 __asm__("$2") = __NR_pipe; + register unsigned long __v1 __asm__("$3"); + register unsigned long __r7 __asm__("$7"); + __asm__ __volatile__ ("syscall\n" + : "=&r"(__v0), "=&r"(__v1), "+r" (__r7) + : "0"(__v0) + : "$8", "$9", "$10", "$11", "$12", + "$13", "$14", "$15", "$24", "memory"); + if (__r7) { + LSS_ERRNO = __v0; + return -1; + } else { + p[0] = __v0; + p[1] = __v1; + return 0; + } + } + #else + LSS_INLINE _syscall1(int, pipe, int *, p) + #endif + /* TODO(csilvers): see if ppc can/should support this as well */ + #if defined(__i386__) || defined(__ARM_ARCH_3__) || \ + defined(__ARM_EABI__) || \ + (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) + #define __NR__statfs64 __NR_statfs64 + #define __NR__fstatfs64 __NR_fstatfs64 + LSS_INLINE _syscall3(int, _statfs64, const char*, p, + size_t, s,struct kernel_statfs64*, b) + LSS_INLINE _syscall3(int, _fstatfs64, int, f, + size_t, s,struct kernel_statfs64*, b) + LSS_INLINE int LSS_NAME(statfs64)(const char *p, + struct kernel_statfs64 *b) { + return LSS_NAME(_statfs64)(p, sizeof(*b), b); + } + LSS_INLINE int LSS_NAME(fstatfs64)(int f,struct kernel_statfs64 *b) { + return LSS_NAME(_fstatfs64)(f, sizeof(*b), b); + } + #endif + + LSS_INLINE int LSS_NAME(execv)(const char *path, const char *const argv[]) { + extern char **environ; + return LSS_NAME(execve)(path, argv, (const char *const *)environ); + } + + LSS_INLINE pid_t LSS_NAME(gettid)() { + pid_t tid = LSS_NAME(_gettid)(); + if (tid != -1) { + return tid; + } + return LSS_NAME(getpid)(); + } + + LSS_INLINE void *LSS_NAME(mremap)(void *old_address, size_t old_size, + size_t new_size, int flags, ...) { + va_list ap; + void *new_address, *rc; + va_start(ap, flags); + new_address = va_arg(ap, void *); + rc = LSS_NAME(_mremap)(old_address, old_size, new_size, + flags, new_address); + va_end(ap); + return rc; + } + + LSS_INLINE int LSS_NAME(ptrace_detach)(pid_t pid) { + /* PTRACE_DETACH can sometimes forget to wake up the tracee and it + * then sends job control signals to the real parent, rather than to + * the tracer. We reduce the risk of this happening by starting a + * whole new time slice, and then quickly sending a SIGCONT signal + * right after detaching from the tracee. + */ + int rc, err; + LSS_NAME(sched_yield)(); + rc = LSS_NAME(ptrace)(PTRACE_DETACH, pid, (void *)0, (void *)0); + err = LSS_ERRNO; + LSS_NAME(kill)(pid, SIGCONT); + LSS_ERRNO = err; + return rc; + } + + LSS_INLINE int LSS_NAME(raise)(int sig) { + return LSS_NAME(kill)(LSS_NAME(getpid)(), sig); + } + + LSS_INLINE int LSS_NAME(setpgrp)() { + return LSS_NAME(setpgid)(0, 0); + } + + LSS_INLINE int LSS_NAME(sysconf)(int name) { + extern int __getpagesize(void); + switch (name) { + case _SC_OPEN_MAX: { + struct kernel_rlimit limit; +#if defined(__ARM_EABI__) + return LSS_NAME(ugetrlimit)(RLIMIT_NOFILE, &limit) < 0 + ? 8192 : limit.rlim_cur; +#else + return LSS_NAME(getrlimit)(RLIMIT_NOFILE, &limit) < 0 + ? 8192 : limit.rlim_cur; +#endif + } + case _SC_PAGESIZE: + return __getpagesize(); + default: + errno = ENOSYS; + return -1; + } + } + #if defined(__x86_64__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI64) + /* pread64() and pwrite64() do not exist on 64-bit systems... */ + LSS_INLINE _syscall3(int, readahead, int, f, + loff_t, o, unsigned, c) + #else + #define __NR__pread64 __NR_pread64 + #define __NR__pwrite64 __NR_pwrite64 + #define __NR__readahead __NR_readahead + LSS_INLINE _syscall5(ssize_t, _pread64, int, f, + void *, b, size_t, c, unsigned, o1, + unsigned, o2) + LSS_INLINE _syscall5(ssize_t, _pwrite64, int, f, + const void *, b, size_t, c, unsigned, o1, + long, o2) + LSS_INLINE _syscall4(int, _readahead, int, f, + unsigned, o1, unsigned, o2, size_t, c) + /* We force 64bit-wide parameters onto the stack, then access each + * 32-bit component individually. This guarantees that we build the + * correct parameters independent of the native byte-order of the + * underlying architecture. + */ + LSS_INLINE ssize_t LSS_NAME(pread64)(int fd, void *buf, size_t count, + loff_t off) { + union { loff_t off; unsigned arg[2]; } o = { off }; + return LSS_NAME(_pread64)(fd, buf, count, o.arg[0], o.arg[1]); + } + LSS_INLINE ssize_t LSS_NAME(pwrite64)(int fd, const void *buf, + size_t count, loff_t off) { + union { loff_t off; unsigned arg[2]; } o = { off }; + return LSS_NAME(_pwrite64)(fd, buf, count, o.arg[0], o.arg[1]); + } + LSS_INLINE int LSS_NAME(readahead)(int fd, loff_t off, int len) { + union { loff_t off; unsigned arg[2]; } o = { off }; + return LSS_NAME(_readahead)(fd, o.arg[0], o.arg[1], len); + } + #endif +#endif + +#if defined(__cplusplus) && !defined(SYS_CPLUSPLUS) +} +#endif + +#endif +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/Makefile.in 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/Makefile.in 2010-04-16 17:32:47.000000000 +0100 @@ -47,18 +47,23 @@ LOCAL_INCLUDES = -I$(srcdir)/../.. -# not compiling http_upload.cc currently -# since it depends on libcurl CPPSRCS = \ - dump_symbols.cc \ file_id.cc \ guid_creator.cc \ + http_upload.cc \ $(NULL) HOST_CPPSRCS = \ + dump_stabs.cc \ dump_symbols.cc \ + dwarf_cfi_to_module.cc \ + dwarf_cu_to_module.cc \ + dwarf_line_to_module.cc \ file_id.cc \ guid_creator.cc \ + language.cc \ + module.cc \ + stabs_reader.cc \ $(NULL) # need static lib diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/memory.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/memory.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/memory.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/memory.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,181 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef CLIENT_LINUX_HANDLER_MEMORY_H_ +#define CLIENT_LINUX_HANDLER_MEMORY_H_ + +#include +#include +#include +#include + +#include "common/linux/linux_syscall_support.h" + +namespace google_breakpad { + +// This is very simple allocator which fetches pages from the kernel directly. +// Thus, it can be used even when the heap may be corrupted. +// +// There is no free operation. The pages are only freed when the object is +// destroyed. +class PageAllocator { + public: + PageAllocator() + : page_size_(getpagesize()), + last_(NULL), + current_page_(NULL), + page_offset_(0) { + } + + ~PageAllocator() { + FreeAll(); + } + + void *Alloc(unsigned bytes) { + if (!bytes) + return NULL; + + if (current_page_ && page_size_ - page_offset_ >= bytes) { + uint8_t *const ret = current_page_ + page_offset_; + page_offset_ += bytes; + if (page_offset_ == page_size_) { + page_offset_ = 0; + current_page_ = NULL; + } + + return ret; + } + + const unsigned pages = + (bytes + sizeof(PageHeader) + page_size_ - 1) / page_size_; + uint8_t *const ret = GetNPages(pages); + if (!ret) + return NULL; + + page_offset_ = (page_size_ - (page_size_ * pages - (bytes + sizeof(PageHeader)))) % page_size_; + current_page_ = page_offset_ ? ret + page_size_ * (pages - 1) : NULL; + + return ret + sizeof(PageHeader); + } + + private: + uint8_t *GetNPages(unsigned num_pages) { +#ifdef __x86_64 + void *a = sys_mmap(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#else + void *a = sys_mmap2(NULL, page_size_ * num_pages, PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); +#endif + if (a == MAP_FAILED) + return NULL; + + struct PageHeader *header = reinterpret_cast(a); + header->next = last_; + header->num_pages = num_pages; + last_ = header; + + return reinterpret_cast(a); + } + + void FreeAll() { + PageHeader *next; + + for (PageHeader *cur = last_; cur; cur = next) { + next = cur->next; + sys_munmap(cur, cur->num_pages * page_size_); + } + } + + struct PageHeader { + PageHeader *next; // pointer to the start of the next set of pages. + unsigned num_pages; // the number of pages in this set. + }; + + const unsigned page_size_; + PageHeader *last_; + uint8_t *current_page_; + unsigned page_offset_; +}; + +// A wasteful vector is like a normal std::vector, except that it's very much +// simplier and it allocates memory from a PageAllocator. It's wasteful +// because, when resizing, it always allocates a whole new array since the +// PageAllocator doesn't support realloc. +template +class wasteful_vector { + public: + wasteful_vector(PageAllocator *allocator, unsigned size_hint = 16) + : allocator_(allocator), + a_((T*) allocator->Alloc(sizeof(T) * size_hint)), + allocated_(size_hint), + used_(0) { + } + + void push_back(const T& new_element) { + if (used_ == allocated_) + Realloc(allocated_ * 2); + a_[used_++] = new_element; + } + + size_t size() const { + return used_; + } + + T& operator[](size_t index) { + return a_[index]; + } + + const T& operator[](size_t index) const { + return a_[index]; + } + + private: + void Realloc(unsigned new_size) { + T *new_array = + reinterpret_cast(allocator_->Alloc(sizeof(T) * new_size)); + memcpy(new_array, a_, used_ * sizeof(T)); + a_ = new_array; + allocated_ = new_size; + } + + PageAllocator *const allocator_; + T *a_; // pointer to an array of |allocated_| elements. + unsigned allocated_; // size of |a_|, in elements. + unsigned used_; // number of used slots in |a_|. +}; + +} // namespace google_breakpad + +inline void* operator new(size_t nbytes, + google_breakpad::PageAllocator& allocator) { + return allocator.Alloc(nbytes); +} + +#endif // CLIENT_LINUX_HANDLER_MEMORY_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/memory_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/memory_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/memory_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/memory_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,84 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "common/linux/memory.h" +#include "testing/gtest/include/gtest/gtest.h" + +using namespace google_breakpad; + +namespace { +typedef testing::Test PageAllocatorTest; +} + +TEST(PageAllocatorTest, Setup) { + PageAllocator allocator; +} + +TEST(PageAllocatorTest, SmallObjects) { + PageAllocator allocator; + + for (unsigned i = 1; i < 1024; ++i) { + uint8_t *p = reinterpret_cast(allocator.Alloc(i)); + ASSERT_FALSE(p == NULL); + memset(p, 0, i); + } +} + +TEST(PageAllocatorTest, LargeObject) { + PageAllocator allocator; + + uint8_t *p = reinterpret_cast(allocator.Alloc(10000)); + ASSERT_FALSE(p == NULL); + for (unsigned i = 1; i < 10; ++i) { + uint8_t *p = reinterpret_cast(allocator.Alloc(i)); + ASSERT_FALSE(p == NULL); + memset(p, 0, i); + } +} + +namespace { +typedef testing::Test WastefulVectorTest; +} + +TEST(WastefulVectorTest, Setup) { + PageAllocator allocator_; + wasteful_vector v(&allocator_); + ASSERT_EQ(v.size(), 0u); +} + +TEST(WastefulVectorTest, Simple) { + PageAllocator allocator_; + wasteful_vector v(&allocator_); + + for (unsigned i = 0; i < 256; ++i) + v.push_back(i); + ASSERT_EQ(v.size(), 256u); + for (unsigned i = 0; i < 256; ++i) + ASSERT_EQ(v[i], i); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/module.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/module.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/module.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/module.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,235 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// module.cc: Implement google_breakpad::Module. See module.h. + +#include +#include + +#include "common/linux/module.h" + +namespace google_breakpad { + +Module::Module(const string &name, const string &os, + const string &architecture, const string &id) : + name_(name), + os_(os), + architecture_(architecture), + id_(id), + load_address_(0) { } + +Module::~Module() { + for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); it++) + delete it->second; + for (vector::iterator it = functions_.begin(); + it != functions_.end(); it++) + delete *it; + for (vector::iterator it = stack_frame_entries_.begin(); + it != stack_frame_entries_.end(); it++) + delete *it; +} + +void Module::SetLoadAddress(Address address) { + load_address_ = address; +} + +void Module::AddFunction(Function *function) { + functions_.push_back(function); +} + +void Module::AddFunctions(vector::iterator begin, + vector::iterator end) { + functions_.insert(functions_.end(), begin, end); +} + +void Module::AddStackFrameEntry(StackFrameEntry *stack_frame_entry) { + stack_frame_entries_.push_back(stack_frame_entry); +} + +void Module::GetFunctions(vector *vec, + vector::iterator i) { + vec->insert(i, functions_.begin(), functions_.end()); +} + +Module::File *Module::FindFile(const string &name) { + // A tricky bit here. The key of each map entry needs to be a + // pointer to the entry's File's name string. This means that we + // can't do the initial lookup with any operation that would create + // an empty entry for us if the name isn't found (like, say, + // operator[] or insert do), because such a created entry's key will + // be a pointer the string passed as our argument. Since the key of + // a map's value type is const, we can't fix it up once we've + // created our file. lower_bound does the lookup without doing an + // insertion, and returns a good hint iterator to pass to insert. + // Our "destiny" is where we belong, whether we're there or not now. + FileByNameMap::iterator destiny = files_.lower_bound(&name); + if (destiny == files_.end() + || *destiny->first != name) { // Repeated string comparison, boo hoo. + File *file = new File; + file->name = name; + file->source_id = -1; + destiny = files_.insert(destiny, + FileByNameMap::value_type(&file->name, file)); + } + return destiny->second; +} + +Module::File *Module::FindFile(const char *name) { + string name_string = name; + return FindFile(name_string); +} + +Module::File *Module::FindExistingFile(const string &name) { + FileByNameMap::iterator it = files_.find(&name); + return (it == files_.end()) ? NULL : it->second; +} + +void Module::GetFiles(vector *vec) { + vec->clear(); + for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); it++) + vec->push_back(it->second); +} + +void Module::GetStackFrameEntries(vector *vec) { + *vec = stack_frame_entries_; +} + +void Module::AssignSourceIds() { + // First, give every source file an id of -1. + for (FileByNameMap::iterator file_it = files_.begin(); + file_it != files_.end(); file_it++) + file_it->second->source_id = -1; + + // Next, mark all files actually cited by our functions' line number + // info, by setting each one's source id to zero. + for (vector::const_iterator func_it = functions_.begin(); + func_it != functions_.end(); func_it++) { + Function *func = *func_it; + for (vector::iterator line_it = func->lines.begin(); + line_it != func->lines.end(); line_it++) + line_it->file->source_id = 0; + } + + // Finally, assign source ids to those files that have been marked. + // We could have just assigned source id numbers while traversing + // the line numbers, but doing it this way numbers the files in + // lexicographical order by name, which is neat. + int next_source_id = 0; + for (FileByNameMap::iterator file_it = files_.begin(); + file_it != files_.end(); file_it++) + if (! file_it->second->source_id) + file_it->second->source_id = next_source_id++; +} + +bool Module::ReportError() { + fprintf(stderr, "error writing symbol file: %s\n", + strerror (errno)); + return false; +} + +bool Module::WriteRuleMap(const RuleMap &rule_map, FILE *stream) { + for (RuleMap::const_iterator it = rule_map.begin(); + it != rule_map.end(); it++) { + if (it != rule_map.begin() && + 0 > putc(' ', stream)) + return false; + if (0 > fprintf(stream, "%s: %s", it->first.c_str(), it->second.c_str())) + return false; + } + return true; +} + +bool Module::Write(FILE *stream) { + if (0 > fprintf(stream, "MODULE %s %s %s %s\n", + os_.c_str(), architecture_.c_str(), id_.c_str(), + name_.c_str())) + return ReportError(); + + AssignSourceIds(); + + // Write out files. + for (FileByNameMap::iterator file_it = files_.begin(); + file_it != files_.end(); file_it++) { + File *file = file_it->second; + if (file->source_id >= 0) { + if (0 > fprintf(stream, "FILE %d %s\n", + file->source_id, file->name.c_str())) + return ReportError(); + } + } + + // Write out functions and their lines. + for (vector::const_iterator func_it = functions_.begin(); + func_it != functions_.end(); func_it++) { + Function *func = *func_it; + if (0 > fprintf(stream, "FUNC %llx %llx %llx %s\n", + (unsigned long long) (func->address - load_address_), + (unsigned long long) func->size, + (unsigned long long) func->parameter_size, + func->name.c_str())) + return ReportError(); + for (vector::iterator line_it = func->lines.begin(); + line_it != func->lines.end(); line_it++) + if (0 > fprintf(stream, "%llx %llx %d %d\n", + (unsigned long long) (line_it->address - load_address_), + (unsigned long long) line_it->size, + line_it->number, + line_it->file->source_id)) + return ReportError(); + } + + // Write out 'STACK CFI INIT' and 'STACK CFI' records. + vector::const_iterator frame_it; + for (frame_it = stack_frame_entries_.begin(); + frame_it != stack_frame_entries_.end(); frame_it++) { + StackFrameEntry *entry = *frame_it; + if (0 > fprintf(stream, "STACK CFI INIT %llx %llx ", + (unsigned long long) entry->address - load_address_, + (unsigned long long) entry->size) + || !WriteRuleMap(entry->initial_rules, stream) + || 0 > putc('\n', stream)) + return ReportError(); + + // Write out this entry's delta rules as 'STACK CFI' records. + for (RuleChangeMap::const_iterator delta_it = entry->rule_changes.begin(); + delta_it != entry->rule_changes.end(); delta_it++) { + if (0 > fprintf(stream, "STACK CFI %llx ", + (unsigned long long) delta_it->first - load_address_) + || !WriteRuleMap(delta_it->second, stream) + || 0 > putc('\n', stream)) + return ReportError(); + } + } + + return true; +} + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/module.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/module.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/module.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/module.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,268 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// module.h: Define google_breakpad::Module. A Module holds debugging +// information, and can write that information out as a Breakpad +// symbol file. + +#ifndef COMMON_LINUX_MODULE_H__ +#define COMMON_LINUX_MODULE_H__ + +#include +#include +#include +#include + +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +using std::string; +using std::vector; +using std::map; + +// A Module represents the contents of a module, and supports methods +// for adding information produced by parsing STABS or DWARF data +// --- possibly both from the same file --- and then writing out the +// unified contents as a Breakpad-format symbol file. +class Module { + public: + // The type of addresses and sizes in a symbol table. + typedef u_int64_t Address; + struct File; + struct Function; + struct Line; + + // Addresses appearing in File, Function, and Line structures are + // absolute, not relative to the the module's load address. That + // is, if the module were loaded at its nominal load address, the + // addresses would be correct. + + // A source file. + struct File { + // The name of the source file. + string name; + + // The file's source id. The Write member function clears this + // field and assigns source ids a fresh, so any value placed here + // before calling Write will be lost. + int source_id; + }; + + // A function. + struct Function { + // For sorting by address. (Not style-guide compliant, but it's + // stupid not to put this in the struct.) + static bool CompareByAddress(const Function *x, const Function *y) { + return x->address < y->address; + } + + // The function's name. + string name; + + // The start address and length of the function's code. + Address address, size; + + // The function's parameter size. + Address parameter_size; + + // Source lines belonging to this function, sorted by increasing + // address. + vector lines; + }; + + // A source line. + struct Line { + // For sorting by address. (Not style-guide compliant, but it's + // stupid not to put this in the struct.) + static bool CompareByAddress(const Module::Line &x, const Module::Line &y) { + return x.address < y.address; + } + + Address address, size; // The address and size of the line's code. + File *file; // The source file. + int number; // The source line number. + }; + + // A map from register names to postfix expressions that recover + // their their values. This can represent a complete set of rules to + // follow at some address, or a set of changes to be applied to an + // extant set of rules. + typedef map RuleMap; + + // A map from addresses to RuleMaps, representing changes that take + // effect at given addresses. + typedef map RuleChangeMap; + + // A range of 'STACK CFI' stack walking information. An instance of + // this structure corresponds to a 'STACK CFI INIT' record and the + // subsequent 'STACK CFI' records that fall within its range. + struct StackFrameEntry { + // The starting address and number of bytes of machine code this + // entry covers. + Address address, size; + + // The initial register recovery rules, in force at the starting + // address. + RuleMap initial_rules; + + // A map from addresses to rule changes. To find the rules in + // force at a given address, start with initial_rules, and then + // apply the changes given in this map for all addresses up to and + // including the address you're interested in. + RuleChangeMap rule_changes; + }; + + // Create a new module with the given name, operating system, + // architecture, and ID string. + Module(const string &name, const string &os, const string &architecture, + const string &id); + ~Module(); + + // Set the module's load address to LOAD_ADDRESS; addresses given + // for functions and lines will be written to the Breakpad symbol + // file as offsets from this address. Construction initializes this + // module's load address to zero: addresses written to the symbol + // file will be the same as they appear in the File and Line + // structures. + void SetLoadAddress(Address load_address); + + // Add FUNCTION to the module. + // This module owns all Function objects added with this function: + // destroying the module destroys them as well. + void AddFunction(Function *function); + + // Add all the functions in [BEGIN,END) to the module. + // This module owns all Function objects added with this function: + // destroying the module destroys them as well. + void AddFunctions(vector::iterator begin, + vector::iterator end); + + // Add STACK_FRAME_ENTRY to the module. + // + // This module owns all StackFrameEntry objects added with this + // function: destroying the module destroys them as well. + void AddStackFrameEntry(StackFrameEntry *stack_frame_entry); + + // If this module has a file named NAME, return a pointer to it. If + // it has none, then create one and return a pointer to the new + // file. This module owns all File objects created using these + // functions; destroying the module destroys them as well. + File *FindFile(const string &name); + File *FindFile(const char *name); + + // If this module has a file named NAME, return a pointer to it. + // Otherwise, return NULL. + File *FindExistingFile(const string &name); + + // Insert pointers to the functions added to this module at I in + // VEC. The pointed-to Functions are still owned by this module. + // (Since this is effectively a copy of the function list, this is + // mostly useful for testing; other uses should probably get a more + // appropriate interface.) + void GetFunctions(vector *vec, vector::iterator i); + + // Clear VEC and fill it with pointers to the Files added to this + // module, sorted by name. The pointed-to Files are still owned by + // this module. (Since this is effectively a copy of the file list, + // this is mostly useful for testing; other uses should probably get + // a more appropriate interface.) + void GetFiles(vector *vec); + + // Clear VEC and fill it with pointers to the StackFrameEntry + // objects that have been added to this module. (Since this is + // effectively a copy of the stack frame entry list, this is mostly + // useful for testing; other uses should probably get + // a more appropriate interface.) + void GetStackFrameEntries(vector *vec); + + // Find those files in this module that are actually referred to by + // functions' line number data, and assign them source id numbers. + // Set the source id numbers for all other files --- unused by the + // source line data --- to -1. We do this before writing out the + // symbol file, at which point we omit any unused files. + void AssignSourceIds(); + + // Call AssignSourceIds, and write this module to STREAM in the + // breakpad symbol format. Return true if all goes well, or false if + // an error occurs. This method writes out: + // - a header based on the values given to the constructor, + // - the source files added via FindFile, and finally + // - the functions added via AddFunctions, each with its lines. + // Addresses in the output are all relative to the load address + // established by SetLoadAddress. + bool Write(FILE *stream); + +private: + + // Report an error that has occurred writing the symbol file, using + // errno to find the appropriate cause. Return false. + static bool ReportError(); + + // Write RULE_MAP to STREAM, in the form appropriate for 'STACK CFI' + // records, without a final newline. Return true if all goes well; + // if an error occurs, return false, and leave errno set. + static bool WriteRuleMap(const RuleMap &rule_map, FILE *stream); + + // Module header entries. + string name_, os_, architecture_, id_; + + // The module's nominal load address. Addresses for functions and + // lines are absolute, assuming the module is loaded at this + // address. + Address load_address_; + + // Relation for maps whose keys are strings shared with some other + // structure. + struct CompareStringPtrs { + bool operator()(const string *x, const string *y) { return *x < *y; }; + }; + + // A map from filenames to File structures. The map's keys are + // pointers to the Files' names. + typedef map FileByNameMap; + + // The module owns all the files and functions that have been added + // to it; destroying the module frees the Files and Functions these + // point to. + FileByNameMap files_; // This module's source files. + vector functions_; // This module's functions. + + // The module owns all the call frame info entries that have been + // added to it. + vector stack_frame_entries_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_MODULE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/module_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/module_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/module_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/module_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,396 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// module_unittest.cc: Unit tests for google_breakpad::Module. + +#include +#include +#include +#include + +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/linux/module.h" + +using google_breakpad::Module; +using std::string; +using std::vector; +using testing::ContainerEq; + +// Return a FILE * referring to a temporary file that will be deleted +// automatically when the stream is closed or the program exits. +FILE *checked_tmpfile() { + FILE *f = tmpfile(); + if (!f) { + fprintf(stderr, "error creating temporary file: %s\n", strerror(errno)); + exit(1); + } + return f; +} + +// Read from STREAM until end of file, and return the contents as a +// string. +string checked_read(FILE *stream) { + string contents; + int c; + while ((c = getc(stream)) != EOF) + contents.push_back(c); + if (ferror(stream)) { + fprintf(stderr, "error reading temporary file contents: %s\n", + strerror(errno)); + exit(1); + } + return contents; +} + +// Apply 'fflush' to STREAM, and check for errors. +void checked_fflush(FILE *stream) { + if (fflush(stream) == EOF) { + fprintf(stderr, "error flushing temporary file stream: %s\n", + strerror(errno)); + exit(1); + } +} + +// Apply 'fclose' to STREAM, and check for errors. +void checked_fclose(FILE *stream) { + if (fclose(stream) == EOF) { + fprintf(stderr, "error closing temporary file stream: %s\n", + strerror(errno)); + exit(1); + } +} + +#define MODULE_NAME "name with spaces" +#define MODULE_OS "os-name" +#define MODULE_ARCH "architecture" +#define MODULE_ID "id-string" + +TEST(Write, Header) { + FILE *f = checked_tmpfile(); + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + m.Write(f); + checked_fflush(f); + rewind(f); + string contents = checked_read(f); + checked_fclose(f); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n", + contents.c_str()); +} + +TEST(Write, OneLineFunc) { + FILE *f = checked_tmpfile(); + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + Module::File *file = m.FindFile("file_name.cc"); + Module::Function *function = new(Module::Function); + function->name = "function_name"; + function->address = 0xe165bf8023b9d9abLL; + function->size = 0x1e4bb0eb1cbf5b09LL; + function->parameter_size = 0x772beee89114358aLL; + Module::Line line = { 0xe165bf8023b9d9abLL, 0x1e4bb0eb1cbf5b09LL, + file, 67519080 }; + function->lines.push_back(line); + m.AddFunction(function); + + m.Write(f); + checked_fflush(f); + rewind(f); + string contents = checked_read(f); + checked_fclose(f); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FILE 0 file_name.cc\n" + "FUNC e165bf8023b9d9ab 1e4bb0eb1cbf5b09 772beee89114358a" + " function_name\n" + "e165bf8023b9d9ab 1e4bb0eb1cbf5b09 67519080 0\n", + contents.c_str()); +} + +TEST(Write, RelativeLoadAddress) { + FILE *f = checked_tmpfile(); + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + m.SetLoadAddress(0x2ab698b0b6407073LL); + + // Some source files. We will expect to see them in lexicographic order. + Module::File *file1 = m.FindFile("filename-b.cc"); + Module::File *file2 = m.FindFile("filename-a.cc"); + + // A function. + Module::Function *function = new(Module::Function); + function->name = "A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)"; + function->address = 0xbec774ea5dd935f3LL; + function->size = 0x2922088f98d3f6fcLL; + function->parameter_size = 0xe5e9aa008bd5f0d0LL; + + // Some source lines. The module should not sort these. + Module::Line line1 = { 0xbec774ea5dd935f3LL, 0x1c2be6d6c5af2611LL, + file1, 41676901 }; + Module::Line line2 = { 0xdaf35bc123885c04LL, 0xcf621b8d324d0ebLL, + file2, 67519080 }; + function->lines.push_back(line2); + function->lines.push_back(line1); + + m.AddFunction(function); + + // Some stack information. + Module::StackFrameEntry *entry = new Module::StackFrameEntry(); + entry->address = 0x30f9e5c83323973dULL; + entry->size = 0x49fc9ca7c7c13dc2ULL; + entry->initial_rules[".cfa"] = "he was a handsome man"; + entry->initial_rules["and"] = "what i want to know is"; + entry->rule_changes[0x30f9e5c83323973eULL]["how"] = + "do you like your blueeyed boy"; + entry->rule_changes[0x30f9e5c83323973eULL]["Mister"] = "Death"; + m.AddStackFrameEntry(entry); + + m.Write(f); + checked_fflush(f); + rewind(f); + string contents = checked_read(f); + checked_fclose(f); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FILE 0 filename-a.cc\n" + "FILE 1 filename-b.cc\n" + "FUNC 9410dc39a798c580 2922088f98d3f6fc e5e9aa008bd5f0d0" + " A_FLIBBERTIJIBBET::a_will_o_the_wisp(a clown)\n" + "b03cc3106d47eb91 cf621b8d324d0eb 67519080 0\n" + "9410dc39a798c580 1c2be6d6c5af2611 41676901 1\n" + "STACK CFI INIT 6434d177ce326ca 49fc9ca7c7c13dc2" + " .cfa: he was a handsome man" + " and: what i want to know is\n" + "STACK CFI 6434d177ce326cb" + " Mister: Death" + " how: do you like your blueeyed boy\n", + contents.c_str()); +} + +TEST(Write, OmitUnusedFiles) { + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Create some source files. + Module::File *file1 = m.FindFile("filename1"); + m.FindFile("filename2"); // not used by any line + Module::File *file3 = m.FindFile("filename3"); + + // Create a function. + Module::Function *function = new(Module::Function); + function->name = "function_name"; + function->address = 0x9b926d464f0b9384LL; + function->size = 0x4f524a4ba795e6a6LL; + function->parameter_size = 0xbbe8133a6641c9b7LL; + + // Source files that refer to some files, but not others. + Module::Line line1 = { 0x595fa44ebacc1086LL, 0x1e1e0191b066c5b3LL, + file1, 137850127 }; + Module::Line line2 = { 0x401ce8c8a12d25e3LL, 0x895751c41b8d2ce2LL, + file3, 28113549 }; + function->lines.push_back(line1); + function->lines.push_back(line2); + m.AddFunction(function); + + m.AssignSourceIds(); + + vector vec; + m.GetFiles(&vec); + EXPECT_EQ((size_t) 3, vec.size()); + EXPECT_STREQ("filename1", vec[0]->name.c_str()); + EXPECT_NE(-1, vec[0]->source_id); + // Expect filename2 not to be used. + EXPECT_STREQ("filename2", vec[1]->name.c_str()); + EXPECT_EQ(-1, vec[1]->source_id); + EXPECT_STREQ("filename3", vec[2]->name.c_str()); + EXPECT_NE(-1, vec[2]->source_id); + + FILE *f = checked_tmpfile(); + m.Write(f); + checked_fflush(f); + rewind(f); + string contents = checked_read(f); + checked_fclose(f); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FILE 0 filename1\n" + "FILE 1 filename3\n" + "FUNC 9b926d464f0b9384 4f524a4ba795e6a6 bbe8133a6641c9b7" + " function_name\n" + "595fa44ebacc1086 1e1e0191b066c5b3 137850127 0\n" + "401ce8c8a12d25e3 895751c41b8d2ce2 28113549 1\n", + contents.c_str()); +} + +TEST(Construct, AddFunctions) { + FILE *f = checked_tmpfile(); + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // Two functions. + Module::Function *function1 = new(Module::Function); + function1->name = "_without_form"; + function1->address = 0xd35024aa7ca7da5cLL; + function1->size = 0x200b26e605f99071LL; + function1->parameter_size = 0xf14ac4fed48c4a99LL; + + Module::Function *function2 = new(Module::Function); + function2->name = "_and_void"; + function2->address = 0x2987743d0b35b13fLL; + function2->size = 0xb369db048deb3010LL; + function2->parameter_size = 0x938e556cb5a79988LL; + + // Put them in a vector. + vector vec; + vec.push_back(function1); + vec.push_back(function2); + + m.AddFunctions(vec.begin(), vec.end()); + + m.Write(f); + checked_fflush(f); + rewind(f); + string contents = checked_read(f); + checked_fclose(f); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "FUNC d35024aa7ca7da5c 200b26e605f99071 f14ac4fed48c4a99" + " _without_form\n" + "FUNC 2987743d0b35b13f b369db048deb3010 938e556cb5a79988" + " _and_void\n", + contents.c_str()); + + // Check that m.GetFunctions returns the functions we expect. + vec.clear(); + m.GetFunctions(&vec, vec.end()); + EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function1)); + EXPECT_TRUE(vec.end() != find(vec.begin(), vec.end(), function2)); + EXPECT_EQ((size_t) 2, vec.size()); +} + +TEST(Construct, AddFrames) { + FILE *f = checked_tmpfile(); + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + + // First STACK CFI entry, with no initial rules or deltas. + Module::StackFrameEntry *entry1 = new Module::StackFrameEntry(); + entry1->address = 0xddb5f41285aa7757ULL; + entry1->size = 0x1486493370dc5073ULL; + m.AddStackFrameEntry(entry1); + + // Second STACK CFI entry, with initial rules but no deltas. + Module::StackFrameEntry *entry2 = new Module::StackFrameEntry(); + entry2->address = 0x8064f3af5e067e38ULL; + entry2->size = 0x0de2a5ee55509407ULL; + entry2->initial_rules[".cfa"] = "I think that I shall never see"; + entry2->initial_rules["stromboli"] = "a poem lovely as a tree"; + entry2->initial_rules["cannoli"] = "a tree whose hungry mouth is prest"; + m.AddStackFrameEntry(entry2); + + // Third STACK CFI entry, with initial rules and deltas. + Module::StackFrameEntry *entry3 = new Module::StackFrameEntry(); + entry3->address = 0x5e8d0db0a7075c6cULL; + entry3->size = 0x1c7edb12a7aea229ULL; + entry3->initial_rules[".cfa"] = "Whose woods are these"; + entry3->rule_changes[0x47ceb0f63c269d7fULL]["calzone"] = + "the village though"; + entry3->rule_changes[0x47ceb0f63c269d7fULL]["cannoli"] = + "he will not see me stopping here"; + entry3->rule_changes[0x36682fad3763ffffULL]["stromboli"] = + "his house is in"; + entry3->rule_changes[0x36682fad3763ffffULL][".cfa"] = + "I think I know"; + m.AddStackFrameEntry(entry3); + + // Check that Write writes STACK CFI records properly. + m.Write(f); + checked_fflush(f); + rewind(f); + string contents = checked_read(f); + checked_fclose(f); + EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n" + "STACK CFI INIT ddb5f41285aa7757 1486493370dc5073 \n" + "STACK CFI INIT 8064f3af5e067e38 de2a5ee55509407" + " .cfa: I think that I shall never see" + " cannoli: a tree whose hungry mouth is prest" + " stromboli: a poem lovely as a tree\n" + "STACK CFI INIT 5e8d0db0a7075c6c 1c7edb12a7aea229" + " .cfa: Whose woods are these\n" + "STACK CFI 36682fad3763ffff" + " .cfa: I think I know" + " stromboli: his house is in\n" + "STACK CFI 47ceb0f63c269d7f" + " calzone: the village though" + " cannoli: he will not see me stopping here\n", + contents.c_str()); + + // Check that GetStackFrameEntries works. + vector entries; + m.GetStackFrameEntries(&entries); + ASSERT_EQ(3U, entries.size()); + // Check first entry. + EXPECT_EQ(0xddb5f41285aa7757ULL, entries[0]->address); + EXPECT_EQ(0x1486493370dc5073ULL, entries[0]->size); + ASSERT_EQ(0U, entries[0]->initial_rules.size()); + ASSERT_EQ(0U, entries[0]->rule_changes.size()); + // Check second entry. + EXPECT_EQ(0x8064f3af5e067e38ULL, entries[1]->address); + EXPECT_EQ(0x0de2a5ee55509407ULL, entries[1]->size); + ASSERT_EQ(3U, entries[1]->initial_rules.size()); + Module::RuleMap entry2_initial; + entry2_initial[".cfa"] = "I think that I shall never see"; + entry2_initial["stromboli"] = "a poem lovely as a tree"; + entry2_initial["cannoli"] = "a tree whose hungry mouth is prest"; + EXPECT_THAT(entries[1]->initial_rules, ContainerEq(entry2_initial)); + ASSERT_EQ(0U, entries[1]->rule_changes.size()); + // Check third entry. + EXPECT_EQ(0x5e8d0db0a7075c6cULL, entries[2]->address); + EXPECT_EQ(0x1c7edb12a7aea229ULL, entries[2]->size); + Module::RuleMap entry3_initial; + entry3_initial[".cfa"] = "Whose woods are these"; + EXPECT_THAT(entries[2]->initial_rules, ContainerEq(entry3_initial)); + Module::RuleChangeMap entry3_changes; + entry3_changes[0x36682fad3763ffffULL][".cfa"] = "I think I know"; + entry3_changes[0x36682fad3763ffffULL]["stromboli"] = "his house is in"; + entry3_changes[0x47ceb0f63c269d7fULL]["calzone"] = "the village though"; + entry3_changes[0x47ceb0f63c269d7fULL]["cannoli"] = + "he will not see me stopping here"; + EXPECT_THAT(entries[2]->rule_changes, ContainerEq(entry3_changes)); +} + +TEST(Construct, UniqueFiles) { + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID); + Module::File *file1 = m.FindFile("foo"); + Module::File *file2 = m.FindFile(string("bar")); + Module::File *file3 = m.FindFile(string("foo")); + Module::File *file4 = m.FindFile("bar"); + EXPECT_NE(file1, file2); + EXPECT_EQ(file1, file3); + EXPECT_EQ(file2, file4); + EXPECT_EQ(file1, m.FindExistingFile("foo")); + EXPECT_TRUE(m.FindExistingFile("baz") == NULL); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,220 @@ +// Copyright (c) 2010 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// This file implements the google_breakpad::StabsReader class. +// See stabs_reader.h. + +#include +#include +#include +#include + +#include "common/linux/stabs_reader.h" + +namespace google_breakpad { + +StabsReader::StabsReader(const uint8_t *stab, size_t stab_size, + const uint8_t *stabstr, size_t stabstr_size, + StabsHandler *handler) : + stabstr_(stabstr), + stabstr_size_(stabstr_size), + handler_(handler), + string_offset_(0), + next_cu_string_offset_(0), + symbol_(NULL), + current_source_file_(NULL) { + symbols_ = reinterpret_cast(stab); + symbols_end_ = symbols_ + (stab_size / sizeof (*symbols_)); +} + +const char *StabsReader::SymbolString() { + ptrdiff_t offset = string_offset_ + symbol_->n_un.n_strx; + if (offset < 0 || (size_t) offset >= stabstr_size_) { + handler_->Warning("symbol %d: name offset outside the string section", + symbol_ - symbols_); + // Return our null string, to keep our promise about all names being + // taken from the string section. + offset = 0; + } + return reinterpret_cast(stabstr_ + offset); +} + +bool StabsReader::Process() { + symbol_ = symbols_; + while (symbol_ < symbols_end_) { + if (symbol_->n_type == N_SO) { + if (! ProcessCompilationUnit()) + return false; + } else if (symbol_->n_type == N_UNDF) { + // At the head of each compilation unit's entries there is an + // N_UNDF stab giving the number of symbols in the compilation + // unit, and the number of bytes that compilation unit's strings + // take up in the .stabstr section. Each CU's strings are + // separate; the n_strx values are offsets within the current + // CU's portion of the .stabstr section. + // + // As an optimization, the GNU linker combines all the + // compilation units into one, with a single N_UNDF at the + // beginning. However, other linkers, like Gold, do not perform + // this optimization. + string_offset_ = next_cu_string_offset_; + next_cu_string_offset_ = SymbolValue(); + symbol_++; + } else + symbol_++; + } + return true; +} + +bool StabsReader::ProcessCompilationUnit() { + assert(symbol_ < symbols_end_ && symbol_->n_type == N_SO); + + // There may be an N_SO entry whose name ends with a slash, + // indicating the directory in which the compilation occurred. + // The build directory defaults to NULL. + const char *build_directory = NULL; + { + const char *name = SymbolString(); + if (name[0] && name[strlen(name) - 1] == '/') { + build_directory = name; + symbol_++; + } + } + + // We expect to see an N_SO entry with a filename next, indicating + // the start of the compilation unit. + { + if (symbol_ >= symbols_end_ || symbol_->n_type != N_SO) + return true; + const char *name = SymbolString(); + if (name[0] == '\0') { + // This seems to be a stray end-of-compilation-unit marker; + // consume it, but don't report the end, since we didn't see a + // beginning. + symbol_++; + return true; + } + current_source_file_ = name; + } + + if (! handler_->StartCompilationUnit(current_source_file_, + SymbolValue(), + build_directory)) + return false; + + symbol_++; + + // The STABS documentation says that some compilers may emit + // additional N_SO entries with names immediately following the + // first, and that they should be ignored. However, the original + // Breakpad STABS reader doesn't ignore them, so we won't either. + + // Process the body of the compilation unit, up to the next N_SO. + while (symbol_ < symbols_end_ && symbol_->n_type != N_SO) { + if (symbol_->n_type == N_FUN) { + if (! ProcessFunction()) + return false; + } else + // Ignore anything else. + symbol_++; + } + + // An N_SO with an empty name indicates the end of the compilation + // unit. Default to zero. + uint64_t ending_address = 0; + if (symbol_ < symbols_end_) { + assert(symbol_->n_type == N_SO); + const char *name = SymbolString(); + if (name[0] == '\0') { + ending_address = SymbolValue(); + symbol_++; + } + } + + if (! handler_->EndCompilationUnit(ending_address)) + return false; + + return true; +} + +bool StabsReader::ProcessFunction() { + assert(symbol_ < symbols_end_ && symbol_->n_type == N_FUN); + + uint64_t function_address = SymbolValue(); + // The STABS string for an N_FUN entry is the name of the function, + // followed by a colon, followed by type information for the + // function. We want to pass the name alone to StartFunction. + const char *stab_string = SymbolString(); + const char *name_end = strchr(stab_string, ':'); + if (! name_end) + name_end = stab_string + strlen(stab_string); + std::string name(stab_string, name_end - stab_string); + if (! handler_->StartFunction(name, function_address)) + return false; + symbol_++; + + while (symbol_ < symbols_end_) { + if (symbol_->n_type == N_SO || symbol_->n_type == N_FUN) + break; + else if (symbol_->n_type == N_SLINE) { + // The value of an N_SLINE entry is the offset of the line from + // the function's start address. + uint64_t line_address = function_address + SymbolValue(); + // The n_desc of a N_SLINE entry is the line number. It's a + // signed 16-bit field; line numbers from 32768 to 65535 are + // stored as n-65536. + uint16_t line_number = symbol_->n_desc; + if (! handler_->Line(line_address, current_source_file_, line_number)) + return false; + symbol_++; + } else if (symbol_->n_type == N_SOL) { + current_source_file_ = SymbolString(); + symbol_++; + } else + // Ignore anything else. + symbol_++; + } + + // If there is a subsequent N_SO or N_FUN entry, its address is our + // end address. + uint64_t ending_address = 0; + if (symbol_ < symbols_end_) { + assert(symbol_->n_type == N_SO || symbol_->n_type == N_FUN); + ending_address = SymbolValue(); + // Note: we do not increment symbol_ here, since we haven't consumed it. + } + + if (! handler_->EndFunction(ending_address)) + return false; + + return true; +} + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,206 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// stabs_reader.h: Define StabsReader, a parser for STABS debugging +// information. A description of the STABS debugging format can be +// found at: +// +// http://sourceware.org/gdb/current/onlinedocs/stabs_toc.html +// +// The comments here assume you understand the format. +// +// This parser assumes that the system's and +// headers accurately describe the layout of the STABS data; this code +// will not parse STABS data for a system with a different address +// size or endianness. + +#ifndef COMMON_LINUX_STABS_READER_H__ +#define COMMON_LINUX_STABS_READER_H__ + +#include +#include +#include + +#include + +namespace google_breakpad { + +class StabsHandler; + +class StabsReader { + public: + // Create a reader for the STABS debug information whose .stab + // section is the STAB_SIZE bytes at STAB, and whose .stabstr + // section is the STABSTR_SIZE bytes at STABSTR. The reader will + // call the member functions of HANDLER to report the information it + // finds, when the reader's 'Process' member function is called. + // + // Note that, in ELF, the .stabstr section should be found using the + // 'sh_link' field of the .stab section header, not by name. + StabsReader(const uint8_t *stab, size_t stab_size, + const uint8_t *stabstr, size_t stabstr_size, + StabsHandler *handler); + + // Process the STABS data, calling the handler's member functions to + // report what we find. While the handler functions return true, + // continue to process until we reach the end of the section. If we + // processed the entire section and all handlers returned true, + // return true. If any handler returned false, return false. + bool Process(); + + private: + // Return the name of the current symbol. + const char *SymbolString(); + + // Return the value of the current symbol. + const uint64_t SymbolValue() { + return symbol_->n_value; + } + + // Process a compilation unit starting at symbol_. Return true + // to continue processing, or false to abort. + bool ProcessCompilationUnit(); + + // Process a function in current_source_file_ starting at symbol_. + // Return true to continue processing, or false to abort. + bool ProcessFunction(); + + // The debugging information we're reading. + const struct nlist *symbols_, *symbols_end_; + const uint8_t *stabstr_; + size_t stabstr_size_; + + StabsHandler *handler_; + + // The offset of the current compilation unit's strings within stabstr_. + size_t string_offset_; + + // The value string_offset_ should have for the next compilation unit, + // as established by N_UNDF entries. + size_t next_cu_string_offset_; + + // The current symbol we're processing. + const struct nlist *symbol_; + + // The current source file name. + const char *current_source_file_; +}; + +// Consumer-provided callback structure for the STABS reader. Clients +// of the STABS reader provide an instance of this structure. The +// reader then invokes the member functions of that instance to report +// the information it finds. +// +// The default definitions of the member functions do nothing, and return +// true so processing will continue. +class StabsHandler { + public: + StabsHandler() { } + virtual ~StabsHandler() { } + + // Some general notes about the handler callback functions: + + // Processing proceeds until the end of the .stabs section, or until + // one of these functions returns false. + + // The addresses given are as reported in the STABS info, without + // regard for whether the module may be loaded at different + // addresses at different times (a shared library, say). When + // processing STABS from an ELF shared library, the addresses given + // all assume the library is loaded at its nominal load address. + // They are *not* offsets from the nominal load address. If you + // want offsets, you must subtract off the library's nominal load + // address. + + // The arguments to these functions named FILENAME are all + // references to strings stored in the .stabstr section. Because + // both the Linux and Solaris linkers factor out duplicate strings + // from the .stabstr section, the consumer can assume that if two + // FILENAME values are different addresses, they represent different + // file names. + // + // Thus, it's safe to use (say) std::map, which does + // string address comparisons, not string content comparisons. + // Since all the strings are in same array of characters --- the + // .stabstr section --- comparing their addresses produces + // predictable, if not lexicographically meaningful, results. + + // Begin processing a compilation unit whose main source file is + // named FILENAME, and whose base address is ADDRESS. If + // BUILD_DIRECTORY is non-NULL, it is the name of the build + // directory in which the compilation occurred. + virtual bool StartCompilationUnit(const char *filename, uint64_t address, + const char *build_directory) { + return true; + } + + // Finish processing the compilation unit. If ADDRESS is non-zero, + // it is the ending address of the compilation unit. If ADDRESS is + // zero, then the compilation unit's ending address is not + // available, and the consumer must infer it by other means. + virtual bool EndCompilationUnit(uint64_t address) { return true; } + + // Begin processing a function named NAME, whose starting address is + // ADDRESS. This function belongs to the compilation unit that was + // most recently started but not ended. + // + // Note that, unlike filenames, NAME is not a pointer into the + // .stabstr section; this is because the name as it appears in the + // STABS data is followed by type information. The value passed to + // StartFunction is the function name alone. + // + // In languages that use name mangling, like C++, NAME is mangled. + virtual bool StartFunction(const std::string &name, uint64_t address) { + return true; + } + + // Finish processing the function. If ADDRESS is non-zero, it is + // the ending address for the function. If ADDRESS is zero, then + // the function's ending address is not available, and the consumer + // must infer it by other means. + virtual bool EndFunction(uint64_t address) { return true; } + + // Report that the code at ADDRESS is attributable to line NUMBER of + // the source file named FILENAME. The caller must infer the ending + // address of the line. + virtual bool Line(uint64_t address, const char *filename, int number) { + return true; + } + + // Report a warning. FORMAT is a printf-like format string, + // specifying how to format the subsequent arguments. + virtual void Warning(const char *format, ...) = 0; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_STABS_READER_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/stabs_reader_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,685 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// stabs_reader_unittest.cc: Unit tests for google_breakpad::StabsReader. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/linux/stabs_reader.h" + +using std::istream; +using std::istringstream; +using std::map; +using std::ostream; +using std::ostringstream; +using std::string; + +using ::testing::_; +using ::testing::Eq; +using ::testing::InSequence; +using ::testing::Return; +using ::testing::Sequence; +using ::testing::StrEq; + +using google_breakpad::StabsHandler; +using google_breakpad::StabsReader; + +namespace { + +// Mock stabs file parser +// +// In order to test StabsReader, we parse a human-readable input file +// describing STABS entries into in-memory .stab and .stabstr +// sections, and then pass those to StabsReader to look at. The +// human-readable file is called a "mock stabs file". +// +// Except for compilation unit boundary lines (described below), each +// line of a mock stabs file should have the following form: +// +// TYPE OTHER DESC VALUE NAME +// +// where all data is Latin-1 bytes and fields are separated by single +// space characters, except for NAME, which may contain spaces and +// continues to the end of the line. The fields have the following +// meanings: +// +// - TYPE: the name of the stabs symbol type; like SO or FUN. These are +// the names from /usr/include/bits/stab.def, without the leading N_. +// +// - OTHER, DESC, VALUE: numeric values for the n_other, n_desc, and +// n_value fields of the stab. These can be decimal or hex, +// using C++ notation (10, 0x10) +// +// - NAME: textual data for the entry. STABS packs all kinds of +// interesting data into entries' NAME fields, so calling it a NAME +// is misleading, but that's how it is. For SO, this may be a +// filename; for FUN, this is the function name, plus type data; and +// so on. +// +// A compilation unit boundary line has the form: +// +// cu-boundary FILENAME + +// I don't know if the whole parser/handler pattern is really worth +// the bureaucracy in this case. But just writing it out as +// old-fashioned functions wasn't astonishingly clear either, so it +// seemed worth a try. + +// A handler class for mock stabs data. +class MockStabsHandler { + public: + MockStabsHandler() { } + virtual ~MockStabsHandler() { } + // The mock stabs parser calls this member function for each entry + // it parses, passing it the contents of the entry. If this function + // returns true, the parser continues; if it returns false, the parser + // stops, and its Process member function returns false. + virtual bool Entry(enum __stab_debug_code type, char other, short desc, + unsigned long value, const string &name) { return true; } + // Report a compilation unit boundary whose filename is FILENAME. As + // for the Entry function, this should return true to continue + // parsing, or false to stop processing. + virtual bool CUBoundary(const string &filename) { return true; } + + // Report an error in parsing the mock stabs data. If this returns true, + // the parser continues; if it returns false, the parser stops and + // its Process member function returns false. + virtual bool Error(const char *format, ...) = 0; +}; + +// A class for parsing mock stabs files. +class MockStabsParser { + public: + // Create a parser reading input from STREAM and passing data to HANDLER. + // Use FILENAME when reporting errors. + MockStabsParser(const string &filename, istream *stream, + MockStabsHandler *handler); + // Parse data from the STREAM, invoking HANDLER->Entry for each + // entry we get. Return true if we parsed all the data succesfully, + // or false if we stopped early because Entry returned false, or if + // there were any errors during parsing. + bool Process(); + private: + // A type for maps from stab type names ("SO", "SLINE", etc.) to + // n_type values. + typedef map StabTypeNameTable; + + // Initialize the table mapping STAB type names to n_type values. + void InitializeTypeNames(); + + // Parse LINE, one line of input from a mock stabs file, and pass + // its contents to handler_->Entry and return the boolean value that + // returns. If we encounter an error parsing the line, report it + // using handler->Error. + bool ParseLine(const string &line); + + const string &filename_; + istream *stream_; + MockStabsHandler *handler_; + int line_number_; + StabTypeNameTable type_names_; +}; + +MockStabsParser::MockStabsParser(const string &filename, istream *stream, + MockStabsHandler *handler): + filename_(filename), stream_(stream), handler_(handler), + line_number_(0) { + InitializeTypeNames(); +} + +bool MockStabsParser::Process() { + // Iterate once per line, including a line at EOF without a + // terminating newline. + for(;;) { + string line; + getline(*stream_, line, '\n'); + if (line.empty() && stream_->eof()) + break; + line_number_++; + if (! ParseLine(line)) + return false; + } + return true; +} + +void MockStabsParser::InitializeTypeNames() { + // On GLIBC-based systems, is a file containing a + // call to an unspecified macro __define_stab for each stab type. + // uses it to define the __stab_debug_code enum type. We + // use it here to initialize our mapping from type names to enum + // values. + // + // This isn't portable to non-GLIBC systems. Feel free to just + // hard-code the values if this becomes a problem. +# define __define_stab(name, code, str) type_names_[string(str)] = code; +# include +# undef __define_stab +} + +bool MockStabsParser::ParseLine(const string &line) { + istringstream linestream(line); + // Allow "0x" prefix for hex, and so on. + linestream.unsetf(istringstream::basefield); + // Parse and validate the stabs type. + string typeName; + linestream >> typeName; + if (typeName == "cu-boundary") { + if (linestream.peek() == ' ') + linestream.get(); + string filename; + getline(linestream, filename, '\n'); + return handler_->CUBoundary(filename); + } else { + StabTypeNameTable::const_iterator typeIt = type_names_.find(typeName); + if (typeIt == type_names_.end()) + return handler_->Error("%s:%d: unrecognized stab type: %s\n", + filename_.c_str(), line_number_, typeName.c_str()); + // These are int, not char and unsigned char, to ensure they're parsed + // as decimal numbers, not characters. + int otherInt, descInt; + unsigned long value; + linestream >> otherInt >> descInt >> value; + if (linestream.fail()) + return handler_->Error("%s:%d: malformed mock stabs input line\n", + filename_.c_str(), line_number_); + if (linestream.peek() == ' ') + linestream.get(); + string name; + getline(linestream, name, '\n'); + return handler_->Entry(static_cast<__stab_debug_code>(typeIt->second), + otherInt, descInt, value, name); + } +} + +// A class for constructing .stab sections. +// +// A .stab section is an array of struct nlist entries. These +// entries' n_un.n_strx fields are indices into an accompanying +// .stabstr section. +class StabSection { + public: + StabSection(): used_(0), size_(1) { + entries_ = (struct nlist *) malloc(sizeof(*entries_) * size_); + } + ~StabSection() { free(entries_); } + + // Append a new 'struct nlist' entry to the end of the section, and + // return a pointer to it. This pointer is valid until the next + // call to Append. The caller should initialize the returned entry + // as needed. + struct nlist *Append(); + // Set the first entry's n_desc field to COUNT, and set its n_value field + // to STRING_SIZE. + void SetHeader(short count, unsigned long string_size); + // Set SECTION to the contents of a .stab section holding the + // accumulated list of entries added with Append. + void GetSection(string *section); + // Clear the array, and prepare this StabSection to accumulate a fresh + // set of entries. + void Clear(); + + private: + // The array of stabs entries, + struct nlist *entries_; + // The number of elements of entries_ that are used, and the allocated size + // of the array. + size_t used_, size_; +}; + +struct nlist *StabSection::Append() { + if (used_ == size_) { + size_ *= 2; + entries_ = (struct nlist *) realloc(entries_, sizeof(*entries_) * size_); + } + assert(used_ < size_); + return &entries_[used_++]; +} + +void StabSection::SetHeader(short count, unsigned long string_size) { + assert(used_ >= 1); + entries_[0].n_desc = count; + entries_[0].n_value = string_size; +} + +void StabSection::GetSection(string *section) { + section->assign(reinterpret_cast(entries_), + sizeof(*entries_) * used_); +} + +void StabSection::Clear() { + used_ = 0; + size_ = 1; + entries_ = (struct nlist *) realloc(entries_, sizeof(*entries_) * size_); +} + +// A class for building .stabstr sections. +// +// A .stabstr section is an array of characters containing a bunch of +// null-terminated strings. A string is identified by the index of +// its initial character in the array. The array always starts with a +// null byte, so that an index of zero refers to the empty string. +// +// This implementation also ensures that if two strings are equal, we +// assign them the same indices; most linkers do this, and some +// clients may rely upon it. (Note that this is not quite the same as +// ensuring that a string only appears once in the section; you could +// share space when one string is a suffix of another, but we don't.) +class StabstrSection { + public: + StabstrSection(): next_byte_(1) { string_indices_[""] = 0; } + // Ensure STR is present in the string section, and return its index. + size_t Insert(const string &str); + // Set SECTION to the contents of a .stabstr section in which the + // strings passed to Insert appear at the indices we promised. + void GetSection(string *section); + // Clear the contents of this StabstrSection, and prepare it to + // accumulate a new set of strings. + void Clear(); + private: + // Maps from strings to .stabstr indices and back. + typedef map StringToIndex; + typedef map IndexToString; + + // A map from strings to the indices we've assigned them. + StringToIndex string_indices_; + + // The next unused byte in the section. The next string we add + // will get this index. + size_t next_byte_; +}; + +size_t StabstrSection::Insert(const string &str) { + StringToIndex::iterator it = string_indices_.find(str); + size_t index; + if (it != string_indices_.end()) { + index = it->second; + } else { + // This is the first time we've seen STR; add it to the table. + string_indices_[str] = next_byte_; + index = next_byte_; + next_byte_ += str.size() + 1; + } + return index; +} + +void StabstrSection::GetSection(string *section) { + // First we have to invert the map. + IndexToString byIndex; + for (StringToIndex::const_iterator it = string_indices_.begin(); + it != string_indices_.end(); it++) + byIndex[it->second] = &it->first; + // Now we build the .stabstr section. + section->clear(); + for (IndexToString::const_iterator it = byIndex.begin(); + it != byIndex.end(); it++) { + // Make sure we're actually assigning it the index we claim to be. + assert(it->first == section->size()); + *section += *(it->second); + *section += '\0'; + } +} + +void StabstrSection::Clear() { + string_indices_.clear(); + string_indices_[""] = 0; + next_byte_ = 1; +} + +// A mock stabs parser handler class that builds .stab and .stabstr +// sections. +class StabsSectionsBuilder: public MockStabsHandler { + public: + // Construct a handler that will receive data from a MockStabsParser + // and construct .stab and .stabstr sections. FILENAME should be + // the name of the mock stabs input file; we use it in error + // messages. + StabsSectionsBuilder(const string &filename) + : filename_(filename), error_count_(0), has_header_(false), + entry_count_(0) { } + + // Overridden virtual member functions. + bool Entry(enum __stab_debug_code type, char other, short desc, + unsigned long value, const string &name); + bool CUBoundary(const string &filename); + bool Error(const char *format, ...); + + // Set SECTION to the contents of a .stab or .stabstr section + // reflecting the entries that have been passed to us via Entry. + void GetStab(string *section); + void GetStabstr(string *section); + + private: + // Finish a compilation unit. If there are any entries accumulated in + // stab_ and stabstr_, add them as a new compilation unit to + // finished_cu_stabs_ and finished_cu_stabstr_, and then clear stab_ and + // stabstr_. + void FinishCU(); + + const string &filename_; // input filename, for error messages + int error_count_; // number of errors we've seen so far + + // The following members accumulate the contents of a single compilation + // unit, possibly headed by an N_UNDF stab. + bool has_header_; // true if we have an N_UNDF header + int entry_count_; // the number of entries we've seen + StabSection stab_; // 'struct nlist' entries + StabstrSection stabstr_; // and the strings they love + + // Accumulated .stab and .stabstr content for all compilation units. + string finished_cu_stab_, finished_cu_stabstr_; +}; + +bool StabsSectionsBuilder::Entry(enum __stab_debug_code type, char other, + short desc, unsigned long value, + const string &name) { + struct nlist *entry = stab_.Append(); + entry->n_type = type; + entry->n_other = other; + entry->n_desc = desc; + entry->n_value = value; + entry->n_un.n_strx = stabstr_.Insert(name); + entry_count_++; + return true; +} + +bool StabsSectionsBuilder::CUBoundary(const string &filename) { + FinishCU(); + // Add a header for the compilation unit. + assert(!has_header_); + assert(entry_count_ == 0); + struct nlist *entry = stab_.Append(); + entry->n_type = N_UNDF; + entry->n_other = 0; + entry->n_desc = 0; // will be set to number of entries + entry->n_value = 0; // will be set to size of .stabstr data + entry->n_un.n_strx = stabstr_.Insert(filename); + has_header_ = true; + // The N_UNDF header isn't included in the symbol count, so we + // shouldn't bump entry_count_ here. + return true; +} + +void StabsSectionsBuilder::FinishCU() { + if (entry_count_ > 0) { + // Get the strings first, so we can record their size in the header. + string stabstr; + stabstr_.GetSection(&stabstr); + finished_cu_stabstr_ += stabstr; + + // Initialize our header, if we have one, and extract the .stab data. + if (has_header_) + stab_.SetHeader(entry_count_, stabstr.size()); + string stab; + stab_.GetSection(&stab); + finished_cu_stab_ += stab; + } + + stab_.Clear(); + stabstr_.Clear(); + has_header_ = false; + entry_count_ = 0; +} + +bool StabsSectionsBuilder::Error(const char *format, ...) { + va_list args; + va_start(args, format); + vfprintf(stderr, format, args); + va_end(args); + error_count_++; + if (error_count_ >= 20) { + fprintf(stderr, + "%s: lots of errors; is this really a mock stabs file?\n", + filename_.c_str()); + return false; + } + return true; +} + +void StabsSectionsBuilder::GetStab(string *section) { + FinishCU(); + *section = finished_cu_stab_; +} + +void StabsSectionsBuilder::GetStabstr(string *section) { + FinishCU(); + *section = finished_cu_stabstr_; +} + +class MockStabsReaderHandler: public StabsHandler { + public: + MOCK_METHOD3(StartCompilationUnit, + bool(const char *, uint64_t, const char *)); + MOCK_METHOD1(EndCompilationUnit, bool(uint64_t)); + MOCK_METHOD2(StartFunction, bool(const std::string &, uint64_t)); + MOCK_METHOD1(EndFunction, bool(uint64_t)); + MOCK_METHOD3(Line, bool(uint64_t, const char *, int)); + void Warning(const char *format, ...) { MockWarning(format); } + MOCK_METHOD1(MockWarning, void(const char *)); +}; + +// Create a StabsReader to parse the mock stabs data in INPUT_FILE, +// passing the parsed information to HANDLER. If all goes well, return +// the result of calling the reader's Process member function. +// Otherwise, return false. INPUT_FILE should be relative to the top +// of the source tree. +static bool ApplyHandlerToMockStabsData(StabsHandler *handler, + const string &input_file) { + string full_input_file + = string(getenv("srcdir") ? getenv("srcdir") : ".") + "/" + input_file; + + // Open the input file. + std::ifstream stream(full_input_file.c_str()); + if (stream.fail()) { + fprintf(stderr, "error opening mock stabs input file %s: %s\n", + full_input_file.c_str(), strerror(errno)); + return false; + } + + // Parse the mock stabs data, and produce stabs sections to use as + // test input to the reader. + StabsSectionsBuilder builder(full_input_file); + MockStabsParser mock_parser(full_input_file, &stream, &builder); + if (!mock_parser.Process()) + return false; + string stab, stabstr; + builder.GetStab(&stab); + builder.GetStabstr(&stabstr); + + // Run the parser on the test input, passing whatever we find to HANDLER. + StabsReader reader( + reinterpret_cast(stab.data()), stab.size(), + reinterpret_cast(stabstr.data()), stabstr.size(), + handler); + return reader.Process(); +} + +TEST(StabsReader, MockStabsInput) { + MockStabsReaderHandler mock_handler; + + { + InSequence s; + + EXPECT_CALL(mock_handler, StartCompilationUnit(StrEq("file1.c"), + 0x42, StrEq("builddir1/"))) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartFunction(StrEq("fun1"), 0x62)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, Line(0xe4, StrEq("file1.c"), 91)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, Line(0x164, StrEq("header.h"), 111)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0x112)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartFunction(StrEq("fun2"), 0x112)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, Line(0x234, StrEq("header.h"), 131)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, Line(0x254, StrEq("file1.c"), 151)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0x152)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0x152)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartCompilationUnit(StrEq("file3.c"), + 0x182, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0x192)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData( + &mock_handler, + "common/linux/testdata/stabs_reader_unittest.input1")); +} + +TEST(StabsReader, AbruptCU) { + MockStabsReaderHandler mock_handler; + + { + InSequence s; + + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("file2-1.c"), 0x12, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(NULL)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData( + &mock_handler, + "common/linux/testdata/stabs_reader_unittest.input2")); +} + +TEST(StabsReader, AbruptFunction) { + MockStabsReaderHandler mock_handler; + + { + InSequence s; + + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("file3-1.c"), 0x12, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartFunction(StrEq("fun3_1"), 0x22)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(NULL)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData( + &mock_handler, + "common/linux/testdata/stabs_reader_unittest.input3")); +} + +TEST(StabsReader, NoCU) { + MockStabsReaderHandler mock_handler; + + EXPECT_CALL(mock_handler, StartCompilationUnit(_, _, _)) + .Times(0); + EXPECT_CALL(mock_handler, StartFunction(_, _)) + .Times(0); + + ASSERT_TRUE(ApplyHandlerToMockStabsData( + &mock_handler, + "common/linux/testdata/stabs_reader_unittest.input4")); + +} + +TEST(StabsReader, NoCUEnd) { + MockStabsReaderHandler mock_handler; + + { + InSequence s; + + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("file5-1.c"), 0x12, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("file5-2.c"), 0x22, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(NULL)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData( + &mock_handler, + "common/linux/testdata/stabs_reader_unittest.input5")); + +} + +TEST(StabsReader, MultipleCUs) { + MockStabsReaderHandler mock_handler; + + { + InSequence s; + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("antimony"), 0x12, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartFunction(Eq("arsenic"), 0x22)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0x32)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0x32)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, + StartCompilationUnit(StrEq("aluminum"), 0x42, NULL)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, StartFunction(Eq("selenium"), 0x52)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndFunction(0x62)) + .WillOnce(Return(true)); + EXPECT_CALL(mock_handler, EndCompilationUnit(0x62)) + .WillOnce(Return(true)); + } + + ASSERT_TRUE(ApplyHandlerToMockStabsData( + &mock_handler, + "common/linux/testdata/stabs_reader_unittest.input6")); +} + +// name duplication + +} // anonymous namespace diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/func-line-pairing.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/func-line-pairing.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/func-line-pairing.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/func-line-pairing.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,676 @@ +// -*- mode: c++ -*- + +// Test data for pairing functions and lines. +// +// For a pair of functions that are adjacent (10,20),(20,25) and a +// pair that are not (10,15),(20,25), we include a test case for every +// possible arrangement of two lines relative to those functions. We +// include cases only for non-empty ranges, since empty functions and +// lines are dropped before we do any pairing. +// +// Each test case is represented by a macro call of the form: +// +// PAIRING(func1_start, func1_end, func2_start, func2_end, +// line1_start, line1_end, line2_start, line2_end, +// func1_num_lines, func2_num_lines, +// func1_line1_start, func1_line1_end, +// func1_line2_start, func1_line2_end, +// func2_line1_start, func2_line1_end, +// func2_line2_start, func2_line2_end, +// uncovered_funcs, uncovered_lines) +// +// where: +// - funcN_{start,end} is the range of the N'th function +// - lineN_{start,end} is the range of the N'th function +// - funcN_num_lines is the number of source lines that should be +// paired with the N'th function +// - funcN_lineM_{start,end} is the range of the M'th line +// paired with the N'th function, where 0,0 indicates that +// there should be no such line paired +// - uncovered_funcs is the number of functions with area that is +// uncovered by any line, and +// - uncovered_lines is the reverse. + +// func1 func2 line1 line2 num pairing1 pairing2 uncovered +PAIRING(10, 20, 20, 25, 6, 7, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #0 +PAIRING(10, 20, 20, 25, 6, 7, 7, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #1 +PAIRING(10, 20, 20, 25, 6, 7, 7, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #2 +PAIRING(10, 20, 20, 25, 6, 7, 7, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #3 +PAIRING(10, 20, 20, 25, 6, 7, 7, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #4 +PAIRING(10, 20, 20, 25, 6, 7, 7, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #5 +PAIRING(10, 20, 20, 25, 6, 7, 7, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #6 +PAIRING(10, 20, 20, 25, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #7 +PAIRING(10, 20, 20, 25, 6, 7, 8, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #8 +PAIRING(10, 20, 20, 25, 6, 7, 8, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #9 +PAIRING(10, 20, 20, 25, 6, 7, 8, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #10 +PAIRING(10, 20, 20, 25, 6, 7, 8, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #11 +PAIRING(10, 20, 20, 25, 6, 7, 8, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #12 +PAIRING(10, 20, 20, 25, 6, 7, 8, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #13 +PAIRING(10, 20, 20, 25, 6, 7, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #14 +PAIRING(10, 20, 20, 25, 6, 7, 10, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #15 +PAIRING(10, 20, 20, 25, 6, 7, 10, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #16 +PAIRING(10, 20, 20, 25, 6, 7, 10, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #17 +PAIRING(10, 20, 20, 25, 6, 7, 10, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #18 +PAIRING(10, 20, 20, 25, 6, 7, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #19 +PAIRING(10, 20, 20, 25, 6, 7, 11, 20, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #20 +PAIRING(10, 20, 20, 25, 6, 7, 11, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #21 +PAIRING(10, 20, 20, 25, 6, 7, 11, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #22 +PAIRING(10, 20, 20, 25, 6, 7, 11, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #23 +PAIRING(10, 20, 20, 25, 6, 7, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #24 +PAIRING(10, 20, 20, 25, 6, 7, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #25 +PAIRING(10, 20, 20, 25, 6, 7, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #26 +PAIRING(10, 20, 20, 25, 6, 7, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #27 +PAIRING(10, 20, 20, 25, 6, 7, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #28 +PAIRING(10, 20, 20, 25, 6, 7, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #29 +PAIRING(10, 20, 20, 25, 6, 7, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #30 +PAIRING(10, 20, 20, 25, 6, 7, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #31 +PAIRING(10, 20, 20, 25, 6, 10, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #32 +PAIRING(10, 20, 20, 25, 6, 10, 10, 20, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #33 +PAIRING(10, 20, 20, 25, 6, 10, 10, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #34 +PAIRING(10, 20, 20, 25, 6, 10, 10, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #35 +PAIRING(10, 20, 20, 25, 6, 10, 10, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #36 +PAIRING(10, 20, 20, 25, 6, 10, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #37 +PAIRING(10, 20, 20, 25, 6, 10, 11, 20, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #38 +PAIRING(10, 20, 20, 25, 6, 10, 11, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #39 +PAIRING(10, 20, 20, 25, 6, 10, 11, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #40 +PAIRING(10, 20, 20, 25, 6, 10, 11, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #41 +PAIRING(10, 20, 20, 25, 6, 10, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #42 +PAIRING(10, 20, 20, 25, 6, 10, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #43 +PAIRING(10, 20, 20, 25, 6, 10, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #44 +PAIRING(10, 20, 20, 25, 6, 10, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #45 +PAIRING(10, 20, 20, 25, 6, 10, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #46 +PAIRING(10, 20, 20, 25, 6, 10, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #47 +PAIRING(10, 20, 20, 25, 6, 10, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #48 +PAIRING(10, 20, 20, 25, 6, 10, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #49 +PAIRING(10, 20, 20, 25, 6, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 1) // #50 +PAIRING(10, 20, 20, 25, 6, 11, 11, 20, 2, 0, 10, 11, 11, 20, 0, 0, 0, 0, 1, 1) // #51 +PAIRING(10, 20, 20, 25, 6, 11, 11, 21, 2, 1, 10, 11, 11, 20, 20, 21, 0, 0, 1, 1) // #52 +PAIRING(10, 20, 20, 25, 6, 11, 11, 25, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 1) // #53 +PAIRING(10, 20, 20, 25, 6, 11, 11, 26, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 2) // #54 +PAIRING(10, 20, 20, 25, 6, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 1) // #55 +PAIRING(10, 20, 20, 25, 6, 11, 12, 20, 2, 0, 10, 11, 12, 20, 0, 0, 0, 0, 2, 1) // #56 +PAIRING(10, 20, 20, 25, 6, 11, 12, 21, 2, 1, 10, 11, 12, 20, 20, 21, 0, 0, 2, 1) // #57 +PAIRING(10, 20, 20, 25, 6, 11, 12, 25, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 1) // #58 +PAIRING(10, 20, 20, 25, 6, 11, 12, 26, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 2) // #59 +PAIRING(10, 20, 20, 25, 6, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #60 +PAIRING(10, 20, 20, 25, 6, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #61 +PAIRING(10, 20, 20, 25, 6, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #62 +PAIRING(10, 20, 20, 25, 6, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 1) // #63 +PAIRING(10, 20, 20, 25, 6, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #64 +PAIRING(10, 20, 20, 25, 6, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 2) // #65 +PAIRING(10, 20, 20, 25, 6, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #66 +PAIRING(10, 20, 20, 25, 6, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #67 +PAIRING(10, 20, 20, 25, 6, 20, 20, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #68 +PAIRING(10, 20, 20, 25, 6, 20, 20, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #69 +PAIRING(10, 20, 20, 25, 6, 20, 20, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #70 +PAIRING(10, 20, 20, 25, 6, 20, 21, 22, 1, 1, 10, 20, 0, 0, 21, 22, 0, 0, 1, 1) // #71 +PAIRING(10, 20, 20, 25, 6, 20, 21, 25, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 1) // #72 +PAIRING(10, 20, 20, 25, 6, 20, 21, 26, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 2) // #73 +PAIRING(10, 20, 20, 25, 6, 20, 25, 26, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #74 +PAIRING(10, 20, 20, 25, 6, 20, 26, 27, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 2) // #75 +PAIRING(10, 20, 20, 25, 6, 21, 21, 22, 1, 2, 10, 20, 0, 0, 20, 21, 21, 22, 1, 1) // #76 +PAIRING(10, 20, 20, 25, 6, 21, 21, 25, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 1) // #77 +PAIRING(10, 20, 20, 25, 6, 21, 21, 26, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 2) // #78 +PAIRING(10, 20, 20, 25, 6, 21, 22, 23, 1, 2, 10, 20, 0, 0, 20, 21, 22, 23, 1, 1) // #79 +PAIRING(10, 20, 20, 25, 6, 21, 22, 25, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 1) // #80 +PAIRING(10, 20, 20, 25, 6, 21, 22, 26, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 2) // #81 +PAIRING(10, 20, 20, 25, 6, 21, 25, 26, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #82 +PAIRING(10, 20, 20, 25, 6, 21, 26, 27, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 2) // #83 +PAIRING(10, 20, 20, 25, 6, 25, 25, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #84 +PAIRING(10, 20, 20, 25, 6, 25, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #85 +PAIRING(10, 20, 20, 25, 6, 26, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #86 +PAIRING(10, 20, 20, 25, 6, 26, 27, 28, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #87 +PAIRING(10, 20, 20, 25, 10, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 0) // #88 +PAIRING(10, 20, 20, 25, 10, 11, 11, 20, 2, 0, 10, 11, 11, 20, 0, 0, 0, 0, 1, 0) // #89 +PAIRING(10, 20, 20, 25, 10, 11, 11, 21, 2, 1, 10, 11, 11, 20, 20, 21, 0, 0, 1, 0) // #90 +PAIRING(10, 20, 20, 25, 10, 11, 11, 25, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 0) // #91 +PAIRING(10, 20, 20, 25, 10, 11, 11, 26, 2, 1, 10, 11, 11, 20, 20, 25, 0, 0, 0, 1) // #92 +PAIRING(10, 20, 20, 25, 10, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 0) // #93 +PAIRING(10, 20, 20, 25, 10, 11, 12, 20, 2, 0, 10, 11, 12, 20, 0, 0, 0, 0, 2, 0) // #94 +PAIRING(10, 20, 20, 25, 10, 11, 12, 21, 2, 1, 10, 11, 12, 20, 20, 21, 0, 0, 2, 0) // #95 +PAIRING(10, 20, 20, 25, 10, 11, 12, 25, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 0) // #96 +PAIRING(10, 20, 20, 25, 10, 11, 12, 26, 2, 1, 10, 11, 12, 20, 20, 25, 0, 0, 1, 1) // #97 +PAIRING(10, 20, 20, 25, 10, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 0) // #98 +PAIRING(10, 20, 20, 25, 10, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 0) // #99 +PAIRING(10, 20, 20, 25, 10, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #100 +PAIRING(10, 20, 20, 25, 10, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 0) // #101 +PAIRING(10, 20, 20, 25, 10, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 0) // #102 +PAIRING(10, 20, 20, 25, 10, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #103 +PAIRING(10, 20, 20, 25, 10, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #104 +PAIRING(10, 20, 20, 25, 10, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #105 +PAIRING(10, 20, 20, 25, 10, 20, 20, 21, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 0) // #106 +PAIRING(10, 20, 20, 25, 10, 20, 20, 25, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 0) // #107 +PAIRING(10, 20, 20, 25, 10, 20, 20, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #108 +PAIRING(10, 20, 20, 25, 10, 20, 21, 22, 1, 1, 10, 20, 0, 0, 21, 22, 0, 0, 1, 0) // #109 +PAIRING(10, 20, 20, 25, 10, 20, 21, 25, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 0) // #110 +PAIRING(10, 20, 20, 25, 10, 20, 21, 26, 1, 1, 10, 20, 0, 0, 21, 25, 0, 0, 1, 1) // #111 +PAIRING(10, 20, 20, 25, 10, 20, 25, 26, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #112 +PAIRING(10, 20, 20, 25, 10, 20, 26, 27, 1, 0, 10, 20, 0, 0, 0, 0, 0, 0, 1, 1) // #113 +PAIRING(10, 20, 20, 25, 10, 21, 21, 22, 1, 2, 10, 20, 0, 0, 20, 21, 21, 22, 1, 0) // #114 +PAIRING(10, 20, 20, 25, 10, 21, 21, 25, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 0) // #115 +PAIRING(10, 20, 20, 25, 10, 21, 21, 26, 1, 2, 10, 20, 0, 0, 20, 21, 21, 25, 0, 1) // #116 +PAIRING(10, 20, 20, 25, 10, 21, 22, 23, 1, 2, 10, 20, 0, 0, 20, 21, 22, 23, 1, 0) // #117 +PAIRING(10, 20, 20, 25, 10, 21, 22, 25, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 0) // #118 +PAIRING(10, 20, 20, 25, 10, 21, 22, 26, 1, 2, 10, 20, 0, 0, 20, 21, 22, 25, 1, 1) // #119 +PAIRING(10, 20, 20, 25, 10, 21, 25, 26, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #120 +PAIRING(10, 20, 20, 25, 10, 21, 26, 27, 1, 1, 10, 20, 0, 0, 20, 21, 0, 0, 1, 1) // #121 +PAIRING(10, 20, 20, 25, 10, 25, 25, 26, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #122 +PAIRING(10, 20, 20, 25, 10, 25, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 1) // #123 +PAIRING(10, 20, 20, 25, 10, 26, 26, 27, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #124 +PAIRING(10, 20, 20, 25, 10, 26, 27, 28, 1, 1, 10, 20, 0, 0, 20, 25, 0, 0, 0, 2) // #125 +PAIRING(10, 20, 20, 25, 11, 12, 12, 13, 2, 0, 11, 12, 12, 13, 0, 0, 0, 0, 2, 0) // #126 +PAIRING(10, 20, 20, 25, 11, 12, 12, 20, 2, 0, 11, 12, 12, 20, 0, 0, 0, 0, 2, 0) // #127 +PAIRING(10, 20, 20, 25, 11, 12, 12, 21, 2, 1, 11, 12, 12, 20, 20, 21, 0, 0, 2, 0) // #128 +PAIRING(10, 20, 20, 25, 11, 12, 12, 25, 2, 1, 11, 12, 12, 20, 20, 25, 0, 0, 1, 0) // #129 +PAIRING(10, 20, 20, 25, 11, 12, 12, 26, 2, 1, 11, 12, 12, 20, 20, 25, 0, 0, 1, 1) // #130 +PAIRING(10, 20, 20, 25, 11, 12, 13, 14, 2, 0, 11, 12, 13, 14, 0, 0, 0, 0, 2, 0) // #131 +PAIRING(10, 20, 20, 25, 11, 12, 13, 20, 2, 0, 11, 12, 13, 20, 0, 0, 0, 0, 2, 0) // #132 +PAIRING(10, 20, 20, 25, 11, 12, 13, 21, 2, 1, 11, 12, 13, 20, 20, 21, 0, 0, 2, 0) // #133 +PAIRING(10, 20, 20, 25, 11, 12, 13, 25, 2, 1, 11, 12, 13, 20, 20, 25, 0, 0, 1, 0) // #134 +PAIRING(10, 20, 20, 25, 11, 12, 13, 26, 2, 1, 11, 12, 13, 20, 20, 25, 0, 0, 1, 1) // #135 +PAIRING(10, 20, 20, 25, 11, 12, 20, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 0) // #136 +PAIRING(10, 20, 20, 25, 11, 12, 20, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 0) // #137 +PAIRING(10, 20, 20, 25, 11, 12, 20, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #138 +PAIRING(10, 20, 20, 25, 11, 12, 21, 22, 1, 1, 11, 12, 0, 0, 21, 22, 0, 0, 2, 0) // #139 +PAIRING(10, 20, 20, 25, 11, 12, 21, 25, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 0) // #140 +PAIRING(10, 20, 20, 25, 11, 12, 21, 26, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 1) // #141 +PAIRING(10, 20, 20, 25, 11, 12, 25, 26, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #142 +PAIRING(10, 20, 20, 25, 11, 12, 26, 27, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #143 +PAIRING(10, 20, 20, 25, 11, 20, 20, 21, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 0) // #144 +PAIRING(10, 20, 20, 25, 11, 20, 20, 25, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 0) // #145 +PAIRING(10, 20, 20, 25, 11, 20, 20, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #146 +PAIRING(10, 20, 20, 25, 11, 20, 21, 22, 1, 1, 11, 20, 0, 0, 21, 22, 0, 0, 2, 0) // #147 +PAIRING(10, 20, 20, 25, 11, 20, 21, 25, 1, 1, 11, 20, 0, 0, 21, 25, 0, 0, 2, 0) // #148 +PAIRING(10, 20, 20, 25, 11, 20, 21, 26, 1, 1, 11, 20, 0, 0, 21, 25, 0, 0, 2, 1) // #149 +PAIRING(10, 20, 20, 25, 11, 20, 25, 26, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #150 +PAIRING(10, 20, 20, 25, 11, 20, 26, 27, 1, 0, 11, 20, 0, 0, 0, 0, 0, 0, 2, 1) // #151 +PAIRING(10, 20, 20, 25, 11, 21, 21, 22, 1, 2, 11, 20, 0, 0, 20, 21, 21, 22, 2, 0) // #152 +PAIRING(10, 20, 20, 25, 11, 21, 21, 25, 1, 2, 11, 20, 0, 0, 20, 21, 21, 25, 1, 0) // #153 +PAIRING(10, 20, 20, 25, 11, 21, 21, 26, 1, 2, 11, 20, 0, 0, 20, 21, 21, 25, 1, 1) // #154 +PAIRING(10, 20, 20, 25, 11, 21, 22, 23, 1, 2, 11, 20, 0, 0, 20, 21, 22, 23, 2, 0) // #155 +PAIRING(10, 20, 20, 25, 11, 21, 22, 25, 1, 2, 11, 20, 0, 0, 20, 21, 22, 25, 2, 0) // #156 +PAIRING(10, 20, 20, 25, 11, 21, 22, 26, 1, 2, 11, 20, 0, 0, 20, 21, 22, 25, 2, 1) // #157 +PAIRING(10, 20, 20, 25, 11, 21, 25, 26, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #158 +PAIRING(10, 20, 20, 25, 11, 21, 26, 27, 1, 1, 11, 20, 0, 0, 20, 21, 0, 0, 2, 1) // #159 +PAIRING(10, 20, 20, 25, 11, 25, 25, 26, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #160 +PAIRING(10, 20, 20, 25, 11, 25, 26, 27, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 1) // #161 +PAIRING(10, 20, 20, 25, 11, 26, 26, 27, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #162 +PAIRING(10, 20, 20, 25, 11, 26, 27, 28, 1, 1, 11, 20, 0, 0, 20, 25, 0, 0, 1, 2) // #163 +PAIRING(10, 20, 20, 25, 20, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 0) // #164 +PAIRING(10, 20, 20, 25, 20, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 0) // #165 +PAIRING(10, 20, 20, 25, 20, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #166 +PAIRING(10, 20, 20, 25, 20, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 0) // #167 +PAIRING(10, 20, 20, 25, 20, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 0) // #168 +PAIRING(10, 20, 20, 25, 20, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #169 +PAIRING(10, 20, 20, 25, 20, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #170 +PAIRING(10, 20, 20, 25, 20, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #171 +PAIRING(10, 20, 20, 25, 20, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #172 +PAIRING(10, 20, 20, 25, 20, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #173 +PAIRING(10, 20, 20, 25, 20, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #174 +PAIRING(10, 20, 20, 25, 20, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #175 +PAIRING(10, 20, 20, 25, 21, 22, 22, 23, 0, 2, 0, 0, 0, 0, 21, 22, 22, 23, 2, 0) // #176 +PAIRING(10, 20, 20, 25, 21, 22, 22, 25, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 0) // #177 +PAIRING(10, 20, 20, 25, 21, 22, 22, 26, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 1) // #178 +PAIRING(10, 20, 20, 25, 21, 22, 23, 24, 0, 2, 0, 0, 0, 0, 21, 22, 23, 24, 2, 0) // #179 +PAIRING(10, 20, 20, 25, 21, 22, 23, 25, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 0) // #180 +PAIRING(10, 20, 20, 25, 21, 22, 23, 26, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 1) // #181 +PAIRING(10, 20, 20, 25, 21, 22, 25, 26, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #182 +PAIRING(10, 20, 20, 25, 21, 22, 26, 27, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #183 +PAIRING(10, 20, 20, 25, 21, 25, 25, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #184 +PAIRING(10, 20, 20, 25, 21, 25, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #185 +PAIRING(10, 20, 20, 25, 21, 26, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #186 +PAIRING(10, 20, 20, 25, 21, 26, 27, 28, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #187 +PAIRING(10, 20, 20, 25, 25, 26, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #188 +PAIRING(10, 20, 20, 25, 25, 26, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #189 +PAIRING(10, 20, 20, 25, 26, 27, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #190 +PAIRING(10, 20, 20, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #191 +PAIRING(10, 15, 20, 25, 6, 7, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #192 +PAIRING(10, 15, 20, 25, 6, 7, 7, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #193 +PAIRING(10, 15, 20, 25, 6, 7, 7, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #194 +PAIRING(10, 15, 20, 25, 6, 7, 7, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #195 +PAIRING(10, 15, 20, 25, 6, 7, 7, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #196 +PAIRING(10, 15, 20, 25, 6, 7, 7, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #197 +PAIRING(10, 15, 20, 25, 6, 7, 7, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #198 +PAIRING(10, 15, 20, 25, 6, 7, 7, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #199 +PAIRING(10, 15, 20, 25, 6, 7, 7, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #200 +PAIRING(10, 15, 20, 25, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #201 +PAIRING(10, 15, 20, 25, 6, 7, 8, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #202 +PAIRING(10, 15, 20, 25, 6, 7, 8, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #203 +PAIRING(10, 15, 20, 25, 6, 7, 8, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #204 +PAIRING(10, 15, 20, 25, 6, 7, 8, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #205 +PAIRING(10, 15, 20, 25, 6, 7, 8, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #206 +PAIRING(10, 15, 20, 25, 6, 7, 8, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #207 +PAIRING(10, 15, 20, 25, 6, 7, 8, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #208 +PAIRING(10, 15, 20, 25, 6, 7, 8, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #209 +PAIRING(10, 15, 20, 25, 6, 7, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #210 +PAIRING(10, 15, 20, 25, 6, 7, 10, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #211 +PAIRING(10, 15, 20, 25, 6, 7, 10, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #212 +PAIRING(10, 15, 20, 25, 6, 7, 10, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #213 +PAIRING(10, 15, 20, 25, 6, 7, 10, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #214 +PAIRING(10, 15, 20, 25, 6, 7, 10, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #215 +PAIRING(10, 15, 20, 25, 6, 7, 10, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #216 +PAIRING(10, 15, 20, 25, 6, 7, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #217 +PAIRING(10, 15, 20, 25, 6, 7, 11, 15, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #218 +PAIRING(10, 15, 20, 25, 6, 7, 11, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #219 +PAIRING(10, 15, 20, 25, 6, 7, 11, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #220 +PAIRING(10, 15, 20, 25, 6, 7, 11, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #221 +PAIRING(10, 15, 20, 25, 6, 7, 11, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #222 +PAIRING(10, 15, 20, 25, 6, 7, 11, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #223 +PAIRING(10, 15, 20, 25, 6, 7, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #224 +PAIRING(10, 15, 20, 25, 6, 7, 15, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #225 +PAIRING(10, 15, 20, 25, 6, 7, 15, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #226 +PAIRING(10, 15, 20, 25, 6, 7, 15, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #227 +PAIRING(10, 15, 20, 25, 6, 7, 15, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #228 +PAIRING(10, 15, 20, 25, 6, 7, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #229 +PAIRING(10, 15, 20, 25, 6, 7, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #230 +PAIRING(10, 15, 20, 25, 6, 7, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #231 +PAIRING(10, 15, 20, 25, 6, 7, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #232 +PAIRING(10, 15, 20, 25, 6, 7, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #233 +PAIRING(10, 15, 20, 25, 6, 7, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #234 +PAIRING(10, 15, 20, 25, 6, 7, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #235 +PAIRING(10, 15, 20, 25, 6, 7, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #236 +PAIRING(10, 15, 20, 25, 6, 7, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #237 +PAIRING(10, 15, 20, 25, 6, 7, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #238 +PAIRING(10, 15, 20, 25, 6, 7, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #239 +PAIRING(10, 15, 20, 25, 6, 7, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #240 +PAIRING(10, 15, 20, 25, 6, 7, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #241 +PAIRING(10, 15, 20, 25, 6, 10, 10, 11, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #242 +PAIRING(10, 15, 20, 25, 6, 10, 10, 15, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #243 +PAIRING(10, 15, 20, 25, 6, 10, 10, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #244 +PAIRING(10, 15, 20, 25, 6, 10, 10, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #245 +PAIRING(10, 15, 20, 25, 6, 10, 10, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #246 +PAIRING(10, 15, 20, 25, 6, 10, 10, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #247 +PAIRING(10, 15, 20, 25, 6, 10, 10, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #248 +PAIRING(10, 15, 20, 25, 6, 10, 11, 12, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #249 +PAIRING(10, 15, 20, 25, 6, 10, 11, 15, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #250 +PAIRING(10, 15, 20, 25, 6, 10, 11, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #251 +PAIRING(10, 15, 20, 25, 6, 10, 11, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #252 +PAIRING(10, 15, 20, 25, 6, 10, 11, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #253 +PAIRING(10, 15, 20, 25, 6, 10, 11, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #254 +PAIRING(10, 15, 20, 25, 6, 10, 11, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #255 +PAIRING(10, 15, 20, 25, 6, 10, 15, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #256 +PAIRING(10, 15, 20, 25, 6, 10, 15, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #257 +PAIRING(10, 15, 20, 25, 6, 10, 15, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #258 +PAIRING(10, 15, 20, 25, 6, 10, 15, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #259 +PAIRING(10, 15, 20, 25, 6, 10, 15, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #260 +PAIRING(10, 15, 20, 25, 6, 10, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #261 +PAIRING(10, 15, 20, 25, 6, 10, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #262 +PAIRING(10, 15, 20, 25, 6, 10, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #263 +PAIRING(10, 15, 20, 25, 6, 10, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #264 +PAIRING(10, 15, 20, 25, 6, 10, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #265 +PAIRING(10, 15, 20, 25, 6, 10, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #266 +PAIRING(10, 15, 20, 25, 6, 10, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #267 +PAIRING(10, 15, 20, 25, 6, 10, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #268 +PAIRING(10, 15, 20, 25, 6, 10, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #269 +PAIRING(10, 15, 20, 25, 6, 10, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #270 +PAIRING(10, 15, 20, 25, 6, 10, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #271 +PAIRING(10, 15, 20, 25, 6, 10, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #272 +PAIRING(10, 15, 20, 25, 6, 10, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #273 +PAIRING(10, 15, 20, 25, 6, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 1) // #274 +PAIRING(10, 15, 20, 25, 6, 11, 11, 15, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #275 +PAIRING(10, 15, 20, 25, 6, 11, 11, 16, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 2) // #276 +PAIRING(10, 15, 20, 25, 6, 11, 11, 20, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #277 +PAIRING(10, 15, 20, 25, 6, 11, 11, 21, 2, 1, 10, 11, 11, 15, 20, 21, 0, 0, 1, 2) // #278 +PAIRING(10, 15, 20, 25, 6, 11, 11, 25, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 2) // #279 +PAIRING(10, 15, 20, 25, 6, 11, 11, 26, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 2) // #280 +PAIRING(10, 15, 20, 25, 6, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 1) // #281 +PAIRING(10, 15, 20, 25, 6, 11, 12, 15, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #282 +PAIRING(10, 15, 20, 25, 6, 11, 12, 16, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 2) // #283 +PAIRING(10, 15, 20, 25, 6, 11, 12, 20, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #284 +PAIRING(10, 15, 20, 25, 6, 11, 12, 21, 2, 1, 10, 11, 12, 15, 20, 21, 0, 0, 2, 2) // #285 +PAIRING(10, 15, 20, 25, 6, 11, 12, 25, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 2) // #286 +PAIRING(10, 15, 20, 25, 6, 11, 12, 26, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 2) // #287 +PAIRING(10, 15, 20, 25, 6, 11, 15, 16, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #288 +PAIRING(10, 15, 20, 25, 6, 11, 15, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #289 +PAIRING(10, 15, 20, 25, 6, 11, 15, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 2) // #290 +PAIRING(10, 15, 20, 25, 6, 11, 15, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #291 +PAIRING(10, 15, 20, 25, 6, 11, 15, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #292 +PAIRING(10, 15, 20, 25, 6, 11, 16, 17, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #293 +PAIRING(10, 15, 20, 25, 6, 11, 16, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #294 +PAIRING(10, 15, 20, 25, 6, 11, 16, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 2) // #295 +PAIRING(10, 15, 20, 25, 6, 11, 16, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #296 +PAIRING(10, 15, 20, 25, 6, 11, 16, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #297 +PAIRING(10, 15, 20, 25, 6, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #298 +PAIRING(10, 15, 20, 25, 6, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #299 +PAIRING(10, 15, 20, 25, 6, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 2) // #300 +PAIRING(10, 15, 20, 25, 6, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 1) // #301 +PAIRING(10, 15, 20, 25, 6, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #302 +PAIRING(10, 15, 20, 25, 6, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 2) // #303 +PAIRING(10, 15, 20, 25, 6, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #304 +PAIRING(10, 15, 20, 25, 6, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 2) // #305 +PAIRING(10, 15, 20, 25, 6, 15, 15, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #306 +PAIRING(10, 15, 20, 25, 6, 15, 15, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #307 +PAIRING(10, 15, 20, 25, 6, 15, 15, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #308 +PAIRING(10, 15, 20, 25, 6, 15, 15, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #309 +PAIRING(10, 15, 20, 25, 6, 15, 15, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #310 +PAIRING(10, 15, 20, 25, 6, 15, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #311 +PAIRING(10, 15, 20, 25, 6, 15, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #312 +PAIRING(10, 15, 20, 25, 6, 15, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #313 +PAIRING(10, 15, 20, 25, 6, 15, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #314 +PAIRING(10, 15, 20, 25, 6, 15, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #315 +PAIRING(10, 15, 20, 25, 6, 15, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #316 +PAIRING(10, 15, 20, 25, 6, 15, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #317 +PAIRING(10, 15, 20, 25, 6, 15, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #318 +PAIRING(10, 15, 20, 25, 6, 15, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #319 +PAIRING(10, 15, 20, 25, 6, 15, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #320 +PAIRING(10, 15, 20, 25, 6, 15, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #321 +PAIRING(10, 15, 20, 25, 6, 15, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #322 +PAIRING(10, 15, 20, 25, 6, 15, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #323 +PAIRING(10, 15, 20, 25, 6, 16, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #324 +PAIRING(10, 15, 20, 25, 6, 16, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #325 +PAIRING(10, 15, 20, 25, 6, 16, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #326 +PAIRING(10, 15, 20, 25, 6, 16, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #327 +PAIRING(10, 15, 20, 25, 6, 16, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #328 +PAIRING(10, 15, 20, 25, 6, 16, 17, 18, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #329 +PAIRING(10, 15, 20, 25, 6, 16, 17, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #330 +PAIRING(10, 15, 20, 25, 6, 16, 17, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #331 +PAIRING(10, 15, 20, 25, 6, 16, 17, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #332 +PAIRING(10, 15, 20, 25, 6, 16, 17, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #333 +PAIRING(10, 15, 20, 25, 6, 16, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #334 +PAIRING(10, 15, 20, 25, 6, 16, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #335 +PAIRING(10, 15, 20, 25, 6, 16, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #336 +PAIRING(10, 15, 20, 25, 6, 16, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #337 +PAIRING(10, 15, 20, 25, 6, 16, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #338 +PAIRING(10, 15, 20, 25, 6, 16, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #339 +PAIRING(10, 15, 20, 25, 6, 16, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #340 +PAIRING(10, 15, 20, 25, 6, 16, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #341 +PAIRING(10, 15, 20, 25, 6, 20, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #342 +PAIRING(10, 15, 20, 25, 6, 20, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #343 +PAIRING(10, 15, 20, 25, 6, 20, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #344 +PAIRING(10, 15, 20, 25, 6, 20, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #345 +PAIRING(10, 15, 20, 25, 6, 20, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #346 +PAIRING(10, 15, 20, 25, 6, 20, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #347 +PAIRING(10, 15, 20, 25, 6, 20, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #348 +PAIRING(10, 15, 20, 25, 6, 20, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #349 +PAIRING(10, 15, 20, 25, 6, 21, 21, 22, 1, 2, 10, 15, 0, 0, 20, 21, 21, 22, 1, 1) // #350 +PAIRING(10, 15, 20, 25, 6, 21, 21, 25, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 1) // #351 +PAIRING(10, 15, 20, 25, 6, 21, 21, 26, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 2) // #352 +PAIRING(10, 15, 20, 25, 6, 21, 22, 23, 1, 2, 10, 15, 0, 0, 20, 21, 22, 23, 1, 1) // #353 +PAIRING(10, 15, 20, 25, 6, 21, 22, 25, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 1) // #354 +PAIRING(10, 15, 20, 25, 6, 21, 22, 26, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 2) // #355 +PAIRING(10, 15, 20, 25, 6, 21, 25, 26, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #356 +PAIRING(10, 15, 20, 25, 6, 21, 26, 27, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #357 +PAIRING(10, 15, 20, 25, 6, 25, 25, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #358 +PAIRING(10, 15, 20, 25, 6, 25, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #359 +PAIRING(10, 15, 20, 25, 6, 26, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #360 +PAIRING(10, 15, 20, 25, 6, 26, 27, 28, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #361 +PAIRING(10, 15, 20, 25, 10, 11, 11, 12, 2, 0, 10, 11, 11, 12, 0, 0, 0, 0, 2, 0) // #362 +PAIRING(10, 15, 20, 25, 10, 11, 11, 15, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 0) // #363 +PAIRING(10, 15, 20, 25, 10, 11, 11, 16, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 1) // #364 +PAIRING(10, 15, 20, 25, 10, 11, 11, 20, 2, 0, 10, 11, 11, 15, 0, 0, 0, 0, 1, 0) // #365 +PAIRING(10, 15, 20, 25, 10, 11, 11, 21, 2, 1, 10, 11, 11, 15, 20, 21, 0, 0, 1, 1) // #366 +PAIRING(10, 15, 20, 25, 10, 11, 11, 25, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 1) // #367 +PAIRING(10, 15, 20, 25, 10, 11, 11, 26, 2, 1, 10, 11, 11, 15, 20, 25, 0, 0, 0, 1) // #368 +PAIRING(10, 15, 20, 25, 10, 11, 12, 13, 2, 0, 10, 11, 12, 13, 0, 0, 0, 0, 2, 0) // #369 +PAIRING(10, 15, 20, 25, 10, 11, 12, 15, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 0) // #370 +PAIRING(10, 15, 20, 25, 10, 11, 12, 16, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 1) // #371 +PAIRING(10, 15, 20, 25, 10, 11, 12, 20, 2, 0, 10, 11, 12, 15, 0, 0, 0, 0, 2, 0) // #372 +PAIRING(10, 15, 20, 25, 10, 11, 12, 21, 2, 1, 10, 11, 12, 15, 20, 21, 0, 0, 2, 1) // #373 +PAIRING(10, 15, 20, 25, 10, 11, 12, 25, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 1) // #374 +PAIRING(10, 15, 20, 25, 10, 11, 12, 26, 2, 1, 10, 11, 12, 15, 20, 25, 0, 0, 1, 1) // #375 +PAIRING(10, 15, 20, 25, 10, 11, 15, 16, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #376 +PAIRING(10, 15, 20, 25, 10, 11, 15, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #377 +PAIRING(10, 15, 20, 25, 10, 11, 15, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #378 +PAIRING(10, 15, 20, 25, 10, 11, 15, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #379 +PAIRING(10, 15, 20, 25, 10, 11, 15, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #380 +PAIRING(10, 15, 20, 25, 10, 11, 16, 17, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #381 +PAIRING(10, 15, 20, 25, 10, 11, 16, 20, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #382 +PAIRING(10, 15, 20, 25, 10, 11, 16, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 1) // #383 +PAIRING(10, 15, 20, 25, 10, 11, 16, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #384 +PAIRING(10, 15, 20, 25, 10, 11, 16, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #385 +PAIRING(10, 15, 20, 25, 10, 11, 20, 21, 1, 1, 10, 11, 0, 0, 20, 21, 0, 0, 2, 0) // #386 +PAIRING(10, 15, 20, 25, 10, 11, 20, 25, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 0) // #387 +PAIRING(10, 15, 20, 25, 10, 11, 20, 26, 1, 1, 10, 11, 0, 0, 20, 25, 0, 0, 1, 1) // #388 +PAIRING(10, 15, 20, 25, 10, 11, 21, 22, 1, 1, 10, 11, 0, 0, 21, 22, 0, 0, 2, 0) // #389 +PAIRING(10, 15, 20, 25, 10, 11, 21, 25, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 0) // #390 +PAIRING(10, 15, 20, 25, 10, 11, 21, 26, 1, 1, 10, 11, 0, 0, 21, 25, 0, 0, 2, 1) // #391 +PAIRING(10, 15, 20, 25, 10, 11, 25, 26, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #392 +PAIRING(10, 15, 20, 25, 10, 11, 26, 27, 1, 0, 10, 11, 0, 0, 0, 0, 0, 0, 2, 1) // #393 +PAIRING(10, 15, 20, 25, 10, 15, 15, 16, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #394 +PAIRING(10, 15, 20, 25, 10, 15, 15, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #395 +PAIRING(10, 15, 20, 25, 10, 15, 15, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #396 +PAIRING(10, 15, 20, 25, 10, 15, 15, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #397 +PAIRING(10, 15, 20, 25, 10, 15, 15, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #398 +PAIRING(10, 15, 20, 25, 10, 15, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #399 +PAIRING(10, 15, 20, 25, 10, 15, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #400 +PAIRING(10, 15, 20, 25, 10, 15, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #401 +PAIRING(10, 15, 20, 25, 10, 15, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #402 +PAIRING(10, 15, 20, 25, 10, 15, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #403 +PAIRING(10, 15, 20, 25, 10, 15, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 0) // #404 +PAIRING(10, 15, 20, 25, 10, 15, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 0) // #405 +PAIRING(10, 15, 20, 25, 10, 15, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #406 +PAIRING(10, 15, 20, 25, 10, 15, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 0) // #407 +PAIRING(10, 15, 20, 25, 10, 15, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 0) // #408 +PAIRING(10, 15, 20, 25, 10, 15, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #409 +PAIRING(10, 15, 20, 25, 10, 15, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #410 +PAIRING(10, 15, 20, 25, 10, 15, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #411 +PAIRING(10, 15, 20, 25, 10, 16, 16, 17, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #412 +PAIRING(10, 15, 20, 25, 10, 16, 16, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #413 +PAIRING(10, 15, 20, 25, 10, 16, 16, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #414 +PAIRING(10, 15, 20, 25, 10, 16, 16, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #415 +PAIRING(10, 15, 20, 25, 10, 16, 16, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #416 +PAIRING(10, 15, 20, 25, 10, 16, 17, 18, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #417 +PAIRING(10, 15, 20, 25, 10, 16, 17, 20, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #418 +PAIRING(10, 15, 20, 25, 10, 16, 17, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #419 +PAIRING(10, 15, 20, 25, 10, 16, 17, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #420 +PAIRING(10, 15, 20, 25, 10, 16, 17, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #421 +PAIRING(10, 15, 20, 25, 10, 16, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 1) // #422 +PAIRING(10, 15, 20, 25, 10, 16, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #423 +PAIRING(10, 15, 20, 25, 10, 16, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #424 +PAIRING(10, 15, 20, 25, 10, 16, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 1) // #425 +PAIRING(10, 15, 20, 25, 10, 16, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #426 +PAIRING(10, 15, 20, 25, 10, 16, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 2) // #427 +PAIRING(10, 15, 20, 25, 10, 16, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #428 +PAIRING(10, 15, 20, 25, 10, 16, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 2) // #429 +PAIRING(10, 15, 20, 25, 10, 20, 20, 21, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 0) // #430 +PAIRING(10, 15, 20, 25, 10, 20, 20, 25, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 0) // #431 +PAIRING(10, 15, 20, 25, 10, 20, 20, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 1) // #432 +PAIRING(10, 15, 20, 25, 10, 20, 21, 22, 1, 1, 10, 15, 0, 0, 21, 22, 0, 0, 1, 0) // #433 +PAIRING(10, 15, 20, 25, 10, 20, 21, 25, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 0) // #434 +PAIRING(10, 15, 20, 25, 10, 20, 21, 26, 1, 1, 10, 15, 0, 0, 21, 25, 0, 0, 1, 1) // #435 +PAIRING(10, 15, 20, 25, 10, 20, 25, 26, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #436 +PAIRING(10, 15, 20, 25, 10, 20, 26, 27, 1, 0, 10, 15, 0, 0, 0, 0, 0, 0, 1, 1) // #437 +PAIRING(10, 15, 20, 25, 10, 21, 21, 22, 1, 2, 10, 15, 0, 0, 20, 21, 21, 22, 1, 1) // #438 +PAIRING(10, 15, 20, 25, 10, 21, 21, 25, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 1) // #439 +PAIRING(10, 15, 20, 25, 10, 21, 21, 26, 1, 2, 10, 15, 0, 0, 20, 21, 21, 25, 0, 2) // #440 +PAIRING(10, 15, 20, 25, 10, 21, 22, 23, 1, 2, 10, 15, 0, 0, 20, 21, 22, 23, 1, 1) // #441 +PAIRING(10, 15, 20, 25, 10, 21, 22, 25, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 1) // #442 +PAIRING(10, 15, 20, 25, 10, 21, 22, 26, 1, 2, 10, 15, 0, 0, 20, 21, 22, 25, 1, 2) // #443 +PAIRING(10, 15, 20, 25, 10, 21, 25, 26, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #444 +PAIRING(10, 15, 20, 25, 10, 21, 26, 27, 1, 1, 10, 15, 0, 0, 20, 21, 0, 0, 1, 2) // #445 +PAIRING(10, 15, 20, 25, 10, 25, 25, 26, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #446 +PAIRING(10, 15, 20, 25, 10, 25, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #447 +PAIRING(10, 15, 20, 25, 10, 26, 26, 27, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #448 +PAIRING(10, 15, 20, 25, 10, 26, 27, 28, 1, 1, 10, 15, 0, 0, 20, 25, 0, 0, 0, 2) // #449 +PAIRING(10, 15, 20, 25, 11, 12, 12, 13, 2, 0, 11, 12, 12, 13, 0, 0, 0, 0, 2, 0) // #450 +PAIRING(10, 15, 20, 25, 11, 12, 12, 15, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 0) // #451 +PAIRING(10, 15, 20, 25, 11, 12, 12, 16, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 1) // #452 +PAIRING(10, 15, 20, 25, 11, 12, 12, 20, 2, 0, 11, 12, 12, 15, 0, 0, 0, 0, 2, 0) // #453 +PAIRING(10, 15, 20, 25, 11, 12, 12, 21, 2, 1, 11, 12, 12, 15, 20, 21, 0, 0, 2, 1) // #454 +PAIRING(10, 15, 20, 25, 11, 12, 12, 25, 2, 1, 11, 12, 12, 15, 20, 25, 0, 0, 1, 1) // #455 +PAIRING(10, 15, 20, 25, 11, 12, 12, 26, 2, 1, 11, 12, 12, 15, 20, 25, 0, 0, 1, 1) // #456 +PAIRING(10, 15, 20, 25, 11, 12, 13, 14, 2, 0, 11, 12, 13, 14, 0, 0, 0, 0, 2, 0) // #457 +PAIRING(10, 15, 20, 25, 11, 12, 13, 15, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 0) // #458 +PAIRING(10, 15, 20, 25, 11, 12, 13, 16, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 1) // #459 +PAIRING(10, 15, 20, 25, 11, 12, 13, 20, 2, 0, 11, 12, 13, 15, 0, 0, 0, 0, 2, 0) // #460 +PAIRING(10, 15, 20, 25, 11, 12, 13, 21, 2, 1, 11, 12, 13, 15, 20, 21, 0, 0, 2, 1) // #461 +PAIRING(10, 15, 20, 25, 11, 12, 13, 25, 2, 1, 11, 12, 13, 15, 20, 25, 0, 0, 1, 1) // #462 +PAIRING(10, 15, 20, 25, 11, 12, 13, 26, 2, 1, 11, 12, 13, 15, 20, 25, 0, 0, 1, 1) // #463 +PAIRING(10, 15, 20, 25, 11, 12, 15, 16, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #464 +PAIRING(10, 15, 20, 25, 11, 12, 15, 20, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #465 +PAIRING(10, 15, 20, 25, 11, 12, 15, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 1) // #466 +PAIRING(10, 15, 20, 25, 11, 12, 15, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #467 +PAIRING(10, 15, 20, 25, 11, 12, 15, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #468 +PAIRING(10, 15, 20, 25, 11, 12, 16, 17, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #469 +PAIRING(10, 15, 20, 25, 11, 12, 16, 20, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #470 +PAIRING(10, 15, 20, 25, 11, 12, 16, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 1) // #471 +PAIRING(10, 15, 20, 25, 11, 12, 16, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #472 +PAIRING(10, 15, 20, 25, 11, 12, 16, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #473 +PAIRING(10, 15, 20, 25, 11, 12, 20, 21, 1, 1, 11, 12, 0, 0, 20, 21, 0, 0, 2, 0) // #474 +PAIRING(10, 15, 20, 25, 11, 12, 20, 25, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 0) // #475 +PAIRING(10, 15, 20, 25, 11, 12, 20, 26, 1, 1, 11, 12, 0, 0, 20, 25, 0, 0, 1, 1) // #476 +PAIRING(10, 15, 20, 25, 11, 12, 21, 22, 1, 1, 11, 12, 0, 0, 21, 22, 0, 0, 2, 0) // #477 +PAIRING(10, 15, 20, 25, 11, 12, 21, 25, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 0) // #478 +PAIRING(10, 15, 20, 25, 11, 12, 21, 26, 1, 1, 11, 12, 0, 0, 21, 25, 0, 0, 2, 1) // #479 +PAIRING(10, 15, 20, 25, 11, 12, 25, 26, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #480 +PAIRING(10, 15, 20, 25, 11, 12, 26, 27, 1, 0, 11, 12, 0, 0, 0, 0, 0, 0, 2, 1) // #481 +PAIRING(10, 15, 20, 25, 11, 15, 15, 16, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #482 +PAIRING(10, 15, 20, 25, 11, 15, 15, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #483 +PAIRING(10, 15, 20, 25, 11, 15, 15, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #484 +PAIRING(10, 15, 20, 25, 11, 15, 15, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #485 +PAIRING(10, 15, 20, 25, 11, 15, 15, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #486 +PAIRING(10, 15, 20, 25, 11, 15, 16, 17, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #487 +PAIRING(10, 15, 20, 25, 11, 15, 16, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #488 +PAIRING(10, 15, 20, 25, 11, 15, 16, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #489 +PAIRING(10, 15, 20, 25, 11, 15, 16, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #490 +PAIRING(10, 15, 20, 25, 11, 15, 16, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #491 +PAIRING(10, 15, 20, 25, 11, 15, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 0) // #492 +PAIRING(10, 15, 20, 25, 11, 15, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 0) // #493 +PAIRING(10, 15, 20, 25, 11, 15, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #494 +PAIRING(10, 15, 20, 25, 11, 15, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 0) // #495 +PAIRING(10, 15, 20, 25, 11, 15, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 0) // #496 +PAIRING(10, 15, 20, 25, 11, 15, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #497 +PAIRING(10, 15, 20, 25, 11, 15, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #498 +PAIRING(10, 15, 20, 25, 11, 15, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #499 +PAIRING(10, 15, 20, 25, 11, 16, 16, 17, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #500 +PAIRING(10, 15, 20, 25, 11, 16, 16, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #501 +PAIRING(10, 15, 20, 25, 11, 16, 16, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #502 +PAIRING(10, 15, 20, 25, 11, 16, 16, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #503 +PAIRING(10, 15, 20, 25, 11, 16, 16, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #504 +PAIRING(10, 15, 20, 25, 11, 16, 17, 18, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #505 +PAIRING(10, 15, 20, 25, 11, 16, 17, 20, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #506 +PAIRING(10, 15, 20, 25, 11, 16, 17, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #507 +PAIRING(10, 15, 20, 25, 11, 16, 17, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #508 +PAIRING(10, 15, 20, 25, 11, 16, 17, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #509 +PAIRING(10, 15, 20, 25, 11, 16, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 1) // #510 +PAIRING(10, 15, 20, 25, 11, 16, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #511 +PAIRING(10, 15, 20, 25, 11, 16, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #512 +PAIRING(10, 15, 20, 25, 11, 16, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 1) // #513 +PAIRING(10, 15, 20, 25, 11, 16, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #514 +PAIRING(10, 15, 20, 25, 11, 16, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 2) // #515 +PAIRING(10, 15, 20, 25, 11, 16, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #516 +PAIRING(10, 15, 20, 25, 11, 16, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 2) // #517 +PAIRING(10, 15, 20, 25, 11, 20, 20, 21, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 0) // #518 +PAIRING(10, 15, 20, 25, 11, 20, 20, 25, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 0) // #519 +PAIRING(10, 15, 20, 25, 11, 20, 20, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 1) // #520 +PAIRING(10, 15, 20, 25, 11, 20, 21, 22, 1, 1, 11, 15, 0, 0, 21, 22, 0, 0, 2, 0) // #521 +PAIRING(10, 15, 20, 25, 11, 20, 21, 25, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 0) // #522 +PAIRING(10, 15, 20, 25, 11, 20, 21, 26, 1, 1, 11, 15, 0, 0, 21, 25, 0, 0, 2, 1) // #523 +PAIRING(10, 15, 20, 25, 11, 20, 25, 26, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #524 +PAIRING(10, 15, 20, 25, 11, 20, 26, 27, 1, 0, 11, 15, 0, 0, 0, 0, 0, 0, 2, 1) // #525 +PAIRING(10, 15, 20, 25, 11, 21, 21, 22, 1, 2, 11, 15, 0, 0, 20, 21, 21, 22, 2, 1) // #526 +PAIRING(10, 15, 20, 25, 11, 21, 21, 25, 1, 2, 11, 15, 0, 0, 20, 21, 21, 25, 1, 1) // #527 +PAIRING(10, 15, 20, 25, 11, 21, 21, 26, 1, 2, 11, 15, 0, 0, 20, 21, 21, 25, 1, 2) // #528 +PAIRING(10, 15, 20, 25, 11, 21, 22, 23, 1, 2, 11, 15, 0, 0, 20, 21, 22, 23, 2, 1) // #529 +PAIRING(10, 15, 20, 25, 11, 21, 22, 25, 1, 2, 11, 15, 0, 0, 20, 21, 22, 25, 2, 1) // #530 +PAIRING(10, 15, 20, 25, 11, 21, 22, 26, 1, 2, 11, 15, 0, 0, 20, 21, 22, 25, 2, 2) // #531 +PAIRING(10, 15, 20, 25, 11, 21, 25, 26, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #532 +PAIRING(10, 15, 20, 25, 11, 21, 26, 27, 1, 1, 11, 15, 0, 0, 20, 21, 0, 0, 2, 2) // #533 +PAIRING(10, 15, 20, 25, 11, 25, 25, 26, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #534 +PAIRING(10, 15, 20, 25, 11, 25, 26, 27, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #535 +PAIRING(10, 15, 20, 25, 11, 26, 26, 27, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #536 +PAIRING(10, 15, 20, 25, 11, 26, 27, 28, 1, 1, 11, 15, 0, 0, 20, 25, 0, 0, 1, 2) // #537 +PAIRING(10, 15, 20, 25, 15, 16, 16, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #538 +PAIRING(10, 15, 20, 25, 15, 16, 16, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #539 +PAIRING(10, 15, 20, 25, 15, 16, 16, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #540 +PAIRING(10, 15, 20, 25, 15, 16, 16, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #541 +PAIRING(10, 15, 20, 25, 15, 16, 16, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #542 +PAIRING(10, 15, 20, 25, 15, 16, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #543 +PAIRING(10, 15, 20, 25, 15, 16, 17, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #544 +PAIRING(10, 15, 20, 25, 15, 16, 17, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #545 +PAIRING(10, 15, 20, 25, 15, 16, 17, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #546 +PAIRING(10, 15, 20, 25, 15, 16, 17, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #547 +PAIRING(10, 15, 20, 25, 15, 16, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #548 +PAIRING(10, 15, 20, 25, 15, 16, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #549 +PAIRING(10, 15, 20, 25, 15, 16, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #550 +PAIRING(10, 15, 20, 25, 15, 16, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #551 +PAIRING(10, 15, 20, 25, 15, 16, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #552 +PAIRING(10, 15, 20, 25, 15, 16, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #553 +PAIRING(10, 15, 20, 25, 15, 16, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #554 +PAIRING(10, 15, 20, 25, 15, 16, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #555 +PAIRING(10, 15, 20, 25, 15, 20, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #556 +PAIRING(10, 15, 20, 25, 15, 20, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #557 +PAIRING(10, 15, 20, 25, 15, 20, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #558 +PAIRING(10, 15, 20, 25, 15, 20, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #559 +PAIRING(10, 15, 20, 25, 15, 20, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #560 +PAIRING(10, 15, 20, 25, 15, 20, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #561 +PAIRING(10, 15, 20, 25, 15, 20, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #562 +PAIRING(10, 15, 20, 25, 15, 20, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #563 +PAIRING(10, 15, 20, 25, 15, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 1) // #564 +PAIRING(10, 15, 20, 25, 15, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #565 +PAIRING(10, 15, 20, 25, 15, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 2) // #566 +PAIRING(10, 15, 20, 25, 15, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 1) // #567 +PAIRING(10, 15, 20, 25, 15, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #568 +PAIRING(10, 15, 20, 25, 15, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 2) // #569 +PAIRING(10, 15, 20, 25, 15, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #570 +PAIRING(10, 15, 20, 25, 15, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #571 +PAIRING(10, 15, 20, 25, 15, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #572 +PAIRING(10, 15, 20, 25, 15, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #573 +PAIRING(10, 15, 20, 25, 15, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #574 +PAIRING(10, 15, 20, 25, 15, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #575 +PAIRING(10, 15, 20, 25, 16, 17, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #576 +PAIRING(10, 15, 20, 25, 16, 17, 17, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #577 +PAIRING(10, 15, 20, 25, 16, 17, 17, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #578 +PAIRING(10, 15, 20, 25, 16, 17, 17, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #579 +PAIRING(10, 15, 20, 25, 16, 17, 17, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #580 +PAIRING(10, 15, 20, 25, 16, 17, 18, 19, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #581 +PAIRING(10, 15, 20, 25, 16, 17, 18, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #582 +PAIRING(10, 15, 20, 25, 16, 17, 18, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #583 +PAIRING(10, 15, 20, 25, 16, 17, 18, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #584 +PAIRING(10, 15, 20, 25, 16, 17, 18, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #585 +PAIRING(10, 15, 20, 25, 16, 17, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #586 +PAIRING(10, 15, 20, 25, 16, 17, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #587 +PAIRING(10, 15, 20, 25, 16, 17, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #588 +PAIRING(10, 15, 20, 25, 16, 17, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #589 +PAIRING(10, 15, 20, 25, 16, 17, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #590 +PAIRING(10, 15, 20, 25, 16, 17, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #591 +PAIRING(10, 15, 20, 25, 16, 17, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #592 +PAIRING(10, 15, 20, 25, 16, 17, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #593 +PAIRING(10, 15, 20, 25, 16, 20, 20, 21, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #594 +PAIRING(10, 15, 20, 25, 16, 20, 20, 25, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #595 +PAIRING(10, 15, 20, 25, 16, 20, 20, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #596 +PAIRING(10, 15, 20, 25, 16, 20, 21, 22, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #597 +PAIRING(10, 15, 20, 25, 16, 20, 21, 25, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #598 +PAIRING(10, 15, 20, 25, 16, 20, 21, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #599 +PAIRING(10, 15, 20, 25, 16, 20, 25, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #600 +PAIRING(10, 15, 20, 25, 16, 20, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #601 +PAIRING(10, 15, 20, 25, 16, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 1) // #602 +PAIRING(10, 15, 20, 25, 16, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #603 +PAIRING(10, 15, 20, 25, 16, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 2) // #604 +PAIRING(10, 15, 20, 25, 16, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 1) // #605 +PAIRING(10, 15, 20, 25, 16, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #606 +PAIRING(10, 15, 20, 25, 16, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 2) // #607 +PAIRING(10, 15, 20, 25, 16, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #608 +PAIRING(10, 15, 20, 25, 16, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 2) // #609 +PAIRING(10, 15, 20, 25, 16, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #610 +PAIRING(10, 15, 20, 25, 16, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #611 +PAIRING(10, 15, 20, 25, 16, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #612 +PAIRING(10, 15, 20, 25, 16, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #613 +PAIRING(10, 15, 20, 25, 20, 21, 21, 22, 0, 2, 0, 0, 0, 0, 20, 21, 21, 22, 2, 0) // #614 +PAIRING(10, 15, 20, 25, 20, 21, 21, 25, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 0) // #615 +PAIRING(10, 15, 20, 25, 20, 21, 21, 26, 0, 2, 0, 0, 0, 0, 20, 21, 21, 25, 1, 1) // #616 +PAIRING(10, 15, 20, 25, 20, 21, 22, 23, 0, 2, 0, 0, 0, 0, 20, 21, 22, 23, 2, 0) // #617 +PAIRING(10, 15, 20, 25, 20, 21, 22, 25, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 0) // #618 +PAIRING(10, 15, 20, 25, 20, 21, 22, 26, 0, 2, 0, 0, 0, 0, 20, 21, 22, 25, 2, 1) // #619 +PAIRING(10, 15, 20, 25, 20, 21, 25, 26, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #620 +PAIRING(10, 15, 20, 25, 20, 21, 26, 27, 0, 1, 0, 0, 0, 0, 20, 21, 0, 0, 2, 1) // #621 +PAIRING(10, 15, 20, 25, 20, 25, 25, 26, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #622 +PAIRING(10, 15, 20, 25, 20, 25, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 1) // #623 +PAIRING(10, 15, 20, 25, 20, 26, 26, 27, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #624 +PAIRING(10, 15, 20, 25, 20, 26, 27, 28, 0, 1, 0, 0, 0, 0, 20, 25, 0, 0, 1, 2) // #625 +PAIRING(10, 15, 20, 25, 21, 22, 22, 23, 0, 2, 0, 0, 0, 0, 21, 22, 22, 23, 2, 0) // #626 +PAIRING(10, 15, 20, 25, 21, 22, 22, 25, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 0) // #627 +PAIRING(10, 15, 20, 25, 21, 22, 22, 26, 0, 2, 0, 0, 0, 0, 21, 22, 22, 25, 2, 1) // #628 +PAIRING(10, 15, 20, 25, 21, 22, 23, 24, 0, 2, 0, 0, 0, 0, 21, 22, 23, 24, 2, 0) // #629 +PAIRING(10, 15, 20, 25, 21, 22, 23, 25, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 0) // #630 +PAIRING(10, 15, 20, 25, 21, 22, 23, 26, 0, 2, 0, 0, 0, 0, 21, 22, 23, 25, 2, 1) // #631 +PAIRING(10, 15, 20, 25, 21, 22, 25, 26, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #632 +PAIRING(10, 15, 20, 25, 21, 22, 26, 27, 0, 1, 0, 0, 0, 0, 21, 22, 0, 0, 2, 1) // #633 +PAIRING(10, 15, 20, 25, 21, 25, 25, 26, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #634 +PAIRING(10, 15, 20, 25, 21, 25, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 1) // #635 +PAIRING(10, 15, 20, 25, 21, 26, 26, 27, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #636 +PAIRING(10, 15, 20, 25, 21, 26, 27, 28, 0, 1, 0, 0, 0, 0, 21, 25, 0, 0, 2, 2) // #637 +PAIRING(10, 15, 20, 25, 25, 26, 26, 27, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #638 +PAIRING(10, 15, 20, 25, 25, 26, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #639 +PAIRING(10, 15, 20, 25, 26, 27, 27, 28, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #640 +PAIRING(10, 15, 20, 25, 26, 27, 28, 29, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2) // #641 diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input1 firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input1 --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input1 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input1 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,19 @@ +SO 10 11 0x02 builddir/ +FUN 20 21 0x12 not the SO with source file name we expected +SO 30 31 0x22 +SO 40 41 0x32 builddir1/ +SO 50 51 0x42 file1.c +LSYM 60 61 0x52 not the FUN we're looking for +FUN 70 71 0x62 fun1 +BINCL 80 81 0x72 something to ignore in a FUN body +SLINE 90 91 0x82 +SOL 100 101 0x92 header.h +SLINE 110 111 0x102 +FUN 120 121 0x112 fun2:some stabs type info here, to trim from the name +SLINE 130 131 0x122 +SOL 140 141 0x132 file1.c +SLINE 150 151 0x142 +SO 160 161 0x152 +LSYM 170 171 0x162 +SO 180 181 0x182 file3.c +SO 190 191 0x192 diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input2 firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input2 --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input2 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input2 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1 @@ +SO 10 11 0x12 file2-1.c diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input3 firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input3 --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input3 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input3 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,2 @@ +SO 10 11 0x12 file3-1.c +FUN 20 21 0x22 fun3_1 diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input4 firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input4 --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input4 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input4 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1 @@ +SO 10 11 0x12 build-directory/ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input5 firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input5 --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input5 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input5 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,2 @@ +SO 10 11 0x12 file5-1.c +SO 20 21 0x22 file5-2.c diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input6 firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input6 --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input6 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/linux/testdata/stabs_reader_unittest.input6 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,8 @@ +cu-boundary antimony +SO 10 11 0x12 antimony +FUN 20 21 0x22 arsenic +SO 30 31 0x32 +cu-boundary aluminum +SO 40 41 0x42 aluminum +FUN 50 51 0x52 selenium +SO 60 61 0x62 diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.h 2010-04-16 17:32:47.000000000 +0100 @@ -33,11 +33,11 @@ #import #include -#include "common/mac/dwarf/dwarf2reader.h" +#include "common/dwarf/dwarf2reader.h" // This will map from an architecture string to a SectionMap, which // will contain the offsets for all the sections in the dictionary -typedef hash_map ArchSectionMap; +typedef map ArchSectionMap; @interface DumpSymbols : NSObject { @protected diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.mm firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.mm --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.mm 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dump_syms.mm 2010-04-16 17:32:47.000000000 +0100 @@ -47,9 +47,9 @@ #import "dump_syms.h" #import "common/mac/file_id.h" #import "common/mac/macho_utilities.h" -#import "common/mac/dwarf/dwarf2reader.h" -#import "common/mac/dwarf/functioninfo.h" -#import "common/mac/dwarf/bytereader.h" +#import "common/dwarf/dwarf2reader.h" +#import "common/dwarf/functioninfo.h" +#import "common/dwarf/bytereader.h" using google_breakpad::FileID; @@ -68,15 +68,6 @@ // for pruning out extraneous non-function symbols. static const int kTextSection = 1; -namespace __gnu_cxx { -template<> - struct hash { - size_t operator()(const std::string& k) const { - return hash< const char* >()( k.c_str() ); - } -}; -} - // Dump FunctionMap to stdout. Print address, function name, file // name, line number, lowpc, and highpc if available. void DumpFunctionMap(const dwarf2reader::FunctionMap function_map) { @@ -321,13 +312,16 @@ //============================================================================= - (BOOL)loadSymbolInfo:(void *)base offset:(uint32_t)offset { + BOOL loadedStabs = [self loadSTABSSymbolInfo:base offset:offset]; + NSMutableDictionary *archSections = [sectionData_ objectForKey:architecture_]; + BOOL loadedDWARF = NO; if ([archSections objectForKey:@"__DWARF__debug_info"]) { // Treat this this as debug information - return [self loadDWARFSymbolInfo:base offset:offset]; + loadedDWARF = [self loadDWARFSymbolInfo:base offset:offset]; } - return [self loadSTABSSymbolInfo:base offset:offset]; + return loadedDWARF || loadedStabs; } //============================================================================= @@ -342,11 +336,15 @@ section *dbgInfoSection = [[archSections objectForKey:@"__DWARF__debug_info"] sectionPointer]; uint32_t debugInfoSize = SwapLongIfNeeded(dbgInfoSection->size); - // i think this will break if run on a big-endian machine +#if __BIG_ENDIAN__ + dwarf2reader::ByteReader byte_reader(swap ? + dwarf2reader::ENDIANNESS_LITTLE : + dwarf2reader::ENDIANNESS_BIG); +#elif __LITTLE_ENDIAN__ dwarf2reader::ByteReader byte_reader(swap ? dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE); - +#endif uint64_t dbgOffset = 0; dwarf2reader::SectionMap* oneArchitectureSectionMap = [self getSectionMapForArchitecture:architecture_]; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.cc 1970-01-01 01:00:00.000000000 +0100 @@ -1,62 +0,0 @@ -// Copyright 2006 Google Inc. All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include "common/mac/dwarf/bytereader-inl.h" - -#include "common/mac/dwarf/bytereader.h" - -namespace dwarf2reader { - -ByteReader::ByteReader(enum Endianness endian) - :offset_reader_(NULL), address_reader_(NULL), endian_(endian), - address_size_(0), offset_size_(0) -{ } - -ByteReader::~ByteReader() { } - -void ByteReader::SetOffsetSize(uint8 size) { - offset_size_ = size; - assert(size == 4 || size == 8); - if (size == 4) { - this->offset_reader_ = &ByteReader::ReadFourBytes; - } else { - this->offset_reader_ = &ByteReader::ReadEightBytes; - } -} - -void ByteReader::SetAddressSize(uint8 size) { - address_size_ = size; - assert(size == 4 || size == 8); - if (size == 4) { - this->address_reader_ = &ByteReader::ReadFourBytes; - } else { - this->address_reader_ = &ByteReader::ReadEightBytes; - } -} - -} // namespace dwarf2reader diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,132 +0,0 @@ -// Copyright 2006 Google Inc. All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef COMMON_MAC_DWARF_BYTEREADER_H__ -#define COMMON_MAC_DWARF_BYTEREADER_H__ - -#include -#include "common/mac/dwarf/types.h" - -namespace dwarf2reader { - -// We can't use the obvious name of LITTLE_ENDIAN and BIG_ENDIAN -// because it conflicts with a macro -enum Endianness { - ENDIANNESS_BIG, - ENDIANNESS_LITTLE -}; - -// Class that knows how to read both big endian and little endian -// numbers, for use in DWARF2/3 reader. -// Takes an endianness argument. -// To read addresses and offsets, SetAddressSize and SetOffsetSize -// must be called first. -class ByteReader { - public: - explicit ByteReader(enum Endianness endian); - virtual ~ByteReader(); - - // Set the address size to SIZE, which sets up the ReadAddress member - // so that it works. - void SetAddressSize(uint8 size); - - // Set the offset size to SIZE, which sets up the ReadOffset member - // so that it works. - void SetOffsetSize(uint8 size); - - // Return the current offset size - uint8 OffsetSize() const { return offset_size_; } - - // Return the current address size - uint8 AddressSize() const { return address_size_; } - - // Read a single byte from BUFFER and return it as an unsigned 8 bit - // number. - uint8 ReadOneByte(const char* buffer) const; - - // Read two bytes from BUFFER and return it as an unsigned 16 bit - // number. - uint16 ReadTwoBytes(const char* buffer) const; - - // Read four bytes from BUFFER and return it as an unsigned 32 bit - // number. This function returns a uint64 so that it is compatible - // with ReadAddress and ReadOffset. The number it returns will - // never be outside the range of an unsigned 32 bit integer. - uint64 ReadFourBytes(const char* buffer) const; - - // Read eight bytes from BUFFER and return it as an unsigned 64 bit - // number - uint64 ReadEightBytes(const char* buffer) const; - - // Read an unsigned LEB128 (Little Endian Base 128) number from - // BUFFER and return it as an unsigned 64 bit integer. LEN is set - // to the length read. Everybody seems to reinvent LEB128 as a - // variable size integer encoding, DWARF has had it for a long time. - uint64 ReadUnsignedLEB128(const char* buffer, size_t* len) const; - - // Read a signed LEB128 number from BUFFER and return it as an - // signed 64 bit integer. LEN is set to the length read. - int64 ReadSignedLEB128(const char* buffer, size_t* len) const; - - // Read an offset from BUFFER and return it as an unsigned 64 bit - // integer. DWARF2/3 define offsets as either 4 or 8 bytes, - // generally depending on the amount of DWARF2/3 info present. - uint64 ReadOffset(const char* buffer) const; - - // Read an address from BUFFER and return it as an unsigned 64 bit - // integer. DWARF2/3 allow addresses to be any size from 0-255 - // bytes currently. Internally we support 4 and 8 byte addresses, - // and will CHECK on anything else. - uint64 ReadAddress(const char* buffer) const; - - private: - - // Function pointer type for our address and offset readers. - typedef uint64 (ByteReader::*AddressReader)(const char*) const; - - // Read an offset from BUFFER and return it as an unsigned 64 bit - // integer. DWARF2/3 define offsets as either 4 or 8 bytes, - // generally depending on the amount of DWARF2/3 info present. - // This function pointer gets set by SetOffsetSize. - AddressReader offset_reader_; - - // Read an address from BUFFER and return it as an unsigned 64 bit - // integer. DWARF2/3 allow addresses to be any size from 0-255 - // bytes currently. Internally we support 4 and 8 byte addresses, - // and will CHECK on anything else. - // This function pointer gets set by SetAddressSize. - AddressReader address_reader_; - - Endianness endian_; - uint8 address_size_; - uint8 offset_size_; -}; - -} // namespace dwarf2reader - -#endif // COMMON_MAC_DWARF_BYTEREADER_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader-inl.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader-inl.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader-inl.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/bytereader-inl.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,141 +0,0 @@ -// Copyright 2006 Google Inc. All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef UTIL_DEBUGINFO_BYTEREADER_INL_H__ -#define UTIL_DEBUGINFO_BYTEREADER_INL_H__ - -#include "common/mac/dwarf/bytereader.h" - -namespace dwarf2reader { - -inline uint8 ByteReader::ReadOneByte(const char* buffer) const { - return buffer[0]; -} - -inline uint16 ByteReader::ReadTwoBytes(const char* buffer) const { - const uint16 buffer0 = static_cast(buffer[0]); - const uint16 buffer1 = static_cast(buffer[1]); - if (endian_ == ENDIANNESS_LITTLE) { - return buffer0 | buffer1 << 8; - } else { - return buffer1 | buffer0 << 8; - } -} - -inline uint64 ByteReader::ReadFourBytes(const char* buffer) const { - const uint32 buffer0 = static_cast(buffer[0]); - const uint32 buffer1 = static_cast(buffer[1]); - const uint32 buffer2 = static_cast(buffer[2]); - const uint32 buffer3 = static_cast(buffer[3]); - if (endian_ == ENDIANNESS_LITTLE) { - return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24; - } else { - return buffer3 | buffer2 << 8 | buffer1 << 16 | buffer0 << 24; - } -} - -inline uint64 ByteReader::ReadEightBytes(const char* buffer) const { - const uint64 buffer0 = static_cast(buffer[0]); - const uint64 buffer1 = static_cast(buffer[1]); - const uint64 buffer2 = static_cast(buffer[2]); - const uint64 buffer3 = static_cast(buffer[3]); - const uint64 buffer4 = static_cast(buffer[4]); - const uint64 buffer5 = static_cast(buffer[5]); - const uint64 buffer6 = static_cast(buffer[6]); - const uint64 buffer7 = static_cast(buffer[7]); - if (endian_ == ENDIANNESS_LITTLE) { - return buffer0 | buffer1 << 8 | buffer2 << 16 | buffer3 << 24 | - buffer4 << 32 | buffer5 << 40 | buffer6 << 48 | buffer7 << 56; - } else { - return buffer7 | buffer6 << 8 | buffer5 << 16 | buffer4 << 24 | - buffer3 << 32 | buffer2 << 40 | buffer1 << 48 | buffer0 << 56; - } -} - -// Read an unsigned LEB128 number. Each byte contains 7 bits of -// information, plus one bit saying whether the number continues or -// not. - -inline uint64 ByteReader::ReadUnsignedLEB128(const char* buffer, - size_t* len) const { - uint64 result = 0; - size_t num_read = 0; - unsigned int shift = 0; - unsigned char byte; - - do { - byte = *buffer++; - num_read++; - - result |= (static_cast(byte & 0x7f)) << shift; - - shift += 7; - - } while (byte & 0x80); - - *len = num_read; - - return result; -} - -// Read a signed LEB128 number. These are like regular LEB128 -// numbers, except the last byte may have a sign bit set. - -inline int64 ByteReader::ReadSignedLEB128(const char* buffer, - size_t* len) const { - int64 result = 0; - unsigned int shift = 0; - size_t num_read = 0; - unsigned char byte; - - do { - byte = *buffer++; - num_read++; - result |= (static_cast(byte & 0x7f) << shift); - shift += 7; - } while (byte & 0x80); - - if ((shift < 8 * sizeof (result)) && (byte & 0x40)) - result |= -((static_cast(1)) << shift); - *len = num_read; - return result; -} - -inline uint64 ByteReader::ReadOffset(const char* buffer) const { - assert(this->offset_reader_); - return (this->*offset_reader_)(buffer); -} - -inline uint64 ByteReader::ReadAddress(const char* buffer) const { - assert(this->address_reader_); - return (this->*address_reader_)(buffer); -} - -} // namespace dwarf2reader - -#endif // UTIL_DEBUGINFO_BYTEREADER_INL_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2enums.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2enums.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2enums.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2enums.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,490 +0,0 @@ -// Copyright 2006 Google Inc. All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#ifndef COMMON_MAC_DWARF_DWARF2ENUMS_H__ -#define COMMON_MAC_DWARF_DWARF2ENUMS_H__ - -namespace dwarf2reader { - -// These enums do not follow the google3 style only because they are -// known universally (specs, other implementations) by the names in -// exactly this capitalization. -// Tag names and codes. -enum DwarfTag { - DW_TAG_padding = 0x00, - DW_TAG_array_type = 0x01, - DW_TAG_class_type = 0x02, - DW_TAG_entry_point = 0x03, - DW_TAG_enumeration_type = 0x04, - DW_TAG_formal_parameter = 0x05, - DW_TAG_imported_declaration = 0x08, - DW_TAG_label = 0x0a, - DW_TAG_lexical_block = 0x0b, - DW_TAG_member = 0x0d, - DW_TAG_pointer_type = 0x0f, - DW_TAG_reference_type = 0x10, - DW_TAG_compile_unit = 0x11, - DW_TAG_string_type = 0x12, - DW_TAG_structure_type = 0x13, - DW_TAG_subroutine_type = 0x15, - DW_TAG_typedef = 0x16, - DW_TAG_union_type = 0x17, - DW_TAG_unspecified_parameters = 0x18, - DW_TAG_variant = 0x19, - DW_TAG_common_block = 0x1a, - DW_TAG_common_inclusion = 0x1b, - DW_TAG_inheritance = 0x1c, - DW_TAG_inlined_subroutine = 0x1d, - DW_TAG_module = 0x1e, - DW_TAG_ptr_to_member_type = 0x1f, - DW_TAG_set_type = 0x20, - DW_TAG_subrange_type = 0x21, - DW_TAG_with_stmt = 0x22, - DW_TAG_access_declaration = 0x23, - DW_TAG_base_type = 0x24, - DW_TAG_catch_block = 0x25, - DW_TAG_const_type = 0x26, - DW_TAG_constant = 0x27, - DW_TAG_enumerator = 0x28, - DW_TAG_file_type = 0x29, - DW_TAG_friend = 0x2a, - DW_TAG_namelist = 0x2b, - DW_TAG_namelist_item = 0x2c, - DW_TAG_packed_type = 0x2d, - DW_TAG_subprogram = 0x2e, - DW_TAG_template_type_param = 0x2f, - DW_TAG_template_value_param = 0x30, - DW_TAG_thrown_type = 0x31, - DW_TAG_try_block = 0x32, - DW_TAG_variant_part = 0x33, - DW_TAG_variable = 0x34, - DW_TAG_volatile_type = 0x35, - // DWARF 3. - DW_TAG_dwarf_procedure = 0x36, - DW_TAG_restrict_type = 0x37, - DW_TAG_interface_type = 0x38, - DW_TAG_namespace = 0x39, - DW_TAG_imported_module = 0x3a, - DW_TAG_unspecified_type = 0x3b, - DW_TAG_partial_unit = 0x3c, - DW_TAG_imported_unit = 0x3d, - // SGI/MIPS Extensions. - DW_TAG_MIPS_loop = 0x4081, - // HP extensions. See: - // ftp://ftp.hp.com/pub/lang/tools/WDB/wdb-4.0.tar.gz - DW_TAG_HP_array_descriptor = 0x4090, - // GNU extensions. - DW_TAG_format_label = 0x4101, // For FORTRAN 77 and Fortran 90. - DW_TAG_function_template = 0x4102, // For C++. - DW_TAG_class_template = 0x4103, // For C++. - DW_TAG_GNU_BINCL = 0x4104, - DW_TAG_GNU_EINCL = 0x4105, - // Extensions for UPC. See: http://upc.gwu.edu/~upc. - DW_TAG_upc_shared_type = 0x8765, - DW_TAG_upc_strict_type = 0x8766, - DW_TAG_upc_relaxed_type = 0x8767, - // PGI (STMicroelectronics) extensions. No documentation available. - DW_TAG_PGI_kanji_type = 0xA000, - DW_TAG_PGI_interface_block = 0xA020 -}; - - -enum DwarfHasChild { - DW_children_no = 0, - DW_children_yes = 1 -}; - -// Form names and codes. -enum DwarfForm { - DW_FORM_addr = 0x01, - DW_FORM_block2 = 0x03, - DW_FORM_block4 = 0x04, - DW_FORM_data2 = 0x05, - DW_FORM_data4 = 0x06, - DW_FORM_data8 = 0x07, - DW_FORM_string = 0x08, - DW_FORM_block = 0x09, - DW_FORM_block1 = 0x0a, - DW_FORM_data1 = 0x0b, - DW_FORM_flag = 0x0c, - DW_FORM_sdata = 0x0d, - DW_FORM_strp = 0x0e, - DW_FORM_udata = 0x0f, - DW_FORM_ref_addr = 0x10, - DW_FORM_ref1 = 0x11, - DW_FORM_ref2 = 0x12, - DW_FORM_ref4 = 0x13, - DW_FORM_ref8 = 0x14, - DW_FORM_ref_udata = 0x15, - DW_FORM_indirect = 0x16 -}; - -// Attribute names and codes -enum DwarfAttribute { - DW_AT_sibling = 0x01, - DW_AT_location = 0x02, - DW_AT_name = 0x03, - DW_AT_ordering = 0x09, - DW_AT_subscr_data = 0x0a, - DW_AT_byte_size = 0x0b, - DW_AT_bit_offset = 0x0c, - DW_AT_bit_size = 0x0d, - DW_AT_element_list = 0x0f, - DW_AT_stmt_list = 0x10, - DW_AT_low_pc = 0x11, - DW_AT_high_pc = 0x12, - DW_AT_language = 0x13, - DW_AT_member = 0x14, - DW_AT_discr = 0x15, - DW_AT_discr_value = 0x16, - DW_AT_visibility = 0x17, - DW_AT_import = 0x18, - DW_AT_string_length = 0x19, - DW_AT_common_reference = 0x1a, - DW_AT_comp_dir = 0x1b, - DW_AT_const_value = 0x1c, - DW_AT_containing_type = 0x1d, - DW_AT_default_value = 0x1e, - DW_AT_inline = 0x20, - DW_AT_is_optional = 0x21, - DW_AT_lower_bound = 0x22, - DW_AT_producer = 0x25, - DW_AT_prototyped = 0x27, - DW_AT_return_addr = 0x2a, - DW_AT_start_scope = 0x2c, - DW_AT_stride_size = 0x2e, - DW_AT_upper_bound = 0x2f, - DW_AT_abstract_origin = 0x31, - DW_AT_accessibility = 0x32, - DW_AT_address_class = 0x33, - DW_AT_artificial = 0x34, - DW_AT_base_types = 0x35, - DW_AT_calling_convention = 0x36, - DW_AT_count = 0x37, - DW_AT_data_member_location = 0x38, - DW_AT_decl_column = 0x39, - DW_AT_decl_file = 0x3a, - DW_AT_decl_line = 0x3b, - DW_AT_declaration = 0x3c, - DW_AT_discr_list = 0x3d, - DW_AT_encoding = 0x3e, - DW_AT_external = 0x3f, - DW_AT_frame_base = 0x40, - DW_AT_friend = 0x41, - DW_AT_identifier_case = 0x42, - DW_AT_macro_info = 0x43, - DW_AT_namelist_items = 0x44, - DW_AT_priority = 0x45, - DW_AT_segment = 0x46, - DW_AT_specification = 0x47, - DW_AT_static_link = 0x48, - DW_AT_type = 0x49, - DW_AT_use_location = 0x4a, - DW_AT_variable_parameter = 0x4b, - DW_AT_virtuality = 0x4c, - DW_AT_vtable_elem_location = 0x4d, - // DWARF 3 values. - DW_AT_allocated = 0x4e, - DW_AT_associated = 0x4f, - DW_AT_data_location = 0x50, - DW_AT_stride = 0x51, - DW_AT_entry_pc = 0x52, - DW_AT_use_UTF8 = 0x53, - DW_AT_extension = 0x54, - DW_AT_ranges = 0x55, - DW_AT_trampoline = 0x56, - DW_AT_call_column = 0x57, - DW_AT_call_file = 0x58, - DW_AT_call_line = 0x59, - // SGI/MIPS extensions. - DW_AT_MIPS_fde = 0x2001, - DW_AT_MIPS_loop_begin = 0x2002, - DW_AT_MIPS_tail_loop_begin = 0x2003, - DW_AT_MIPS_epilog_begin = 0x2004, - DW_AT_MIPS_loop_unroll_factor = 0x2005, - DW_AT_MIPS_software_pipeline_depth = 0x2006, - DW_AT_MIPS_linkage_name = 0x2007, - DW_AT_MIPS_stride = 0x2008, - DW_AT_MIPS_abstract_name = 0x2009, - DW_AT_MIPS_clone_origin = 0x200a, - DW_AT_MIPS_has_inlines = 0x200b, - // HP extensions. - DW_AT_HP_block_index = 0x2000, - DW_AT_HP_unmodifiable = 0x2001, // Same as DW_AT_MIPS_fde. - DW_AT_HP_actuals_stmt_list = 0x2010, - DW_AT_HP_proc_per_section = 0x2011, - DW_AT_HP_raw_data_ptr = 0x2012, - DW_AT_HP_pass_by_reference = 0x2013, - DW_AT_HP_opt_level = 0x2014, - DW_AT_HP_prof_version_id = 0x2015, - DW_AT_HP_opt_flags = 0x2016, - DW_AT_HP_cold_region_low_pc = 0x2017, - DW_AT_HP_cold_region_high_pc = 0x2018, - DW_AT_HP_all_variables_modifiable = 0x2019, - DW_AT_HP_linkage_name = 0x201a, - DW_AT_HP_prof_flags = 0x201b, // In comp unit of procs_info for -g. - // GNU extensions. - DW_AT_sf_names = 0x2101, - DW_AT_src_info = 0x2102, - DW_AT_mac_info = 0x2103, - DW_AT_src_coords = 0x2104, - DW_AT_body_begin = 0x2105, - DW_AT_body_end = 0x2106, - DW_AT_GNU_vector = 0x2107, - // VMS extensions. - DW_AT_VMS_rtnbeg_pd_address = 0x2201, - // UPC extension. - DW_AT_upc_threads_scaled = 0x3210, - // PGI (STMicroelectronics) extensions. - DW_AT_PGI_lbase = 0x3a00, - DW_AT_PGI_soffset = 0x3a01, - DW_AT_PGI_lstride = 0x3a02 -}; - - -// Line number opcodes. -enum DwarfLineNumberOps { - DW_LNS_extended_op = 0, - DW_LNS_copy = 1, - DW_LNS_advance_pc = 2, - DW_LNS_advance_line = 3, - DW_LNS_set_file = 4, - DW_LNS_set_column = 5, - DW_LNS_negate_stmt = 6, - DW_LNS_set_basic_block = 7, - DW_LNS_const_add_pc = 8, - DW_LNS_fixed_advance_pc = 9, - // DWARF 3. - DW_LNS_set_prologue_end = 10, - DW_LNS_set_epilogue_begin = 11, - DW_LNS_set_isa = 12 -}; - -// Line number extended opcodes. -enum DwarfLineNumberExtendedOps { - DW_LNE_end_sequence = 1, - DW_LNE_set_address = 2, - DW_LNE_define_file = 3, - // HP extensions. - DW_LNE_HP_negate_is_UV_update = 0x11, - DW_LNE_HP_push_context = 0x12, - DW_LNE_HP_pop_context = 0x13, - DW_LNE_HP_set_file_line_column = 0x14, - DW_LNE_HP_set_routine_name = 0x15, - DW_LNE_HP_set_sequence = 0x16, - DW_LNE_HP_negate_post_semantics = 0x17, - DW_LNE_HP_negate_function_exit = 0x18, - DW_LNE_HP_negate_front_end_logical = 0x19, - DW_LNE_HP_define_proc = 0x20 -}; - -// Type encoding names and codes -enum DwarfEncoding { - DW_ATE_address =0x1, - DW_ATE_boolean =0x2, - DW_ATE_complex_float =0x3, - DW_ATE_float =0x4, - DW_ATE_signed =0x5, - DW_ATE_signed_char =0x6, - DW_ATE_unsigned =0x7, - DW_ATE_unsigned_char =0x8, - // DWARF3/DWARF3f - DW_ATE_imaginary_float =0x9, - DW_ATE_packed_decimal =0xa, - DW_ATE_numeric_string =0xb, - DW_ATE_edited =0xc, - DW_ATE_signed_fixed =0xd, - DW_ATE_unsigned_fixed =0xe, - DW_ATE_decimal_float =0xf, - DW_ATE_lo_user =0x80, - DW_ATE_hi_user =0xff -}; - -// Location virtual machine opcodes -enum DwarfOpcode { - DW_OP_addr =0x03, - DW_OP_deref =0x06, - DW_OP_const1u =0x08, - DW_OP_const1s =0x09, - DW_OP_const2u =0x0a, - DW_OP_const2s =0x0b, - DW_OP_const4u =0x0c, - DW_OP_const4s =0x0d, - DW_OP_const8u =0x0e, - DW_OP_const8s =0x0f, - DW_OP_constu =0x10, - DW_OP_consts =0x11, - DW_OP_dup =0x12, - DW_OP_drop =0x13, - DW_OP_over =0x14, - DW_OP_pick =0x15, - DW_OP_swap =0x16, - DW_OP_rot =0x17, - DW_OP_xderef =0x18, - DW_OP_abs =0x19, - DW_OP_and =0x1a, - DW_OP_div =0x1b, - DW_OP_minus =0x1c, - DW_OP_mod =0x1d, - DW_OP_mul =0x1e, - DW_OP_neg =0x1f, - DW_OP_not =0x20, - DW_OP_or =0x21, - DW_OP_plus =0x22, - DW_OP_plus_uconst =0x23, - DW_OP_shl =0x24, - DW_OP_shr =0x25, - DW_OP_shra =0x26, - DW_OP_xor =0x27, - DW_OP_bra =0x28, - DW_OP_eq =0x29, - DW_OP_ge =0x2a, - DW_OP_gt =0x2b, - DW_OP_le =0x2c, - DW_OP_lt =0x2d, - DW_OP_ne =0x2e, - DW_OP_skip =0x2f, - DW_OP_lit0 =0x30, - DW_OP_lit1 =0x31, - DW_OP_lit2 =0x32, - DW_OP_lit3 =0x33, - DW_OP_lit4 =0x34, - DW_OP_lit5 =0x35, - DW_OP_lit6 =0x36, - DW_OP_lit7 =0x37, - DW_OP_lit8 =0x38, - DW_OP_lit9 =0x39, - DW_OP_lit10 =0x3a, - DW_OP_lit11 =0x3b, - DW_OP_lit12 =0x3c, - DW_OP_lit13 =0x3d, - DW_OP_lit14 =0x3e, - DW_OP_lit15 =0x3f, - DW_OP_lit16 =0x40, - DW_OP_lit17 =0x41, - DW_OP_lit18 =0x42, - DW_OP_lit19 =0x43, - DW_OP_lit20 =0x44, - DW_OP_lit21 =0x45, - DW_OP_lit22 =0x46, - DW_OP_lit23 =0x47, - DW_OP_lit24 =0x48, - DW_OP_lit25 =0x49, - DW_OP_lit26 =0x4a, - DW_OP_lit27 =0x4b, - DW_OP_lit28 =0x4c, - DW_OP_lit29 =0x4d, - DW_OP_lit30 =0x4e, - DW_OP_lit31 =0x4f, - DW_OP_reg0 =0x50, - DW_OP_reg1 =0x51, - DW_OP_reg2 =0x52, - DW_OP_reg3 =0x53, - DW_OP_reg4 =0x54, - DW_OP_reg5 =0x55, - DW_OP_reg6 =0x56, - DW_OP_reg7 =0x57, - DW_OP_reg8 =0x58, - DW_OP_reg9 =0x59, - DW_OP_reg10 =0x5a, - DW_OP_reg11 =0x5b, - DW_OP_reg12 =0x5c, - DW_OP_reg13 =0x5d, - DW_OP_reg14 =0x5e, - DW_OP_reg15 =0x5f, - DW_OP_reg16 =0x60, - DW_OP_reg17 =0x61, - DW_OP_reg18 =0x62, - DW_OP_reg19 =0x63, - DW_OP_reg20 =0x64, - DW_OP_reg21 =0x65, - DW_OP_reg22 =0x66, - DW_OP_reg23 =0x67, - DW_OP_reg24 =0x68, - DW_OP_reg25 =0x69, - DW_OP_reg26 =0x6a, - DW_OP_reg27 =0x6b, - DW_OP_reg28 =0x6c, - DW_OP_reg29 =0x6d, - DW_OP_reg30 =0x6e, - DW_OP_reg31 =0x6f, - DW_OP_breg0 =0x70, - DW_OP_breg1 =0x71, - DW_OP_breg2 =0x72, - DW_OP_breg3 =0x73, - DW_OP_breg4 =0x74, - DW_OP_breg5 =0x75, - DW_OP_breg6 =0x76, - DW_OP_breg7 =0x77, - DW_OP_breg8 =0x78, - DW_OP_breg9 =0x79, - DW_OP_breg10 =0x7a, - DW_OP_breg11 =0x7b, - DW_OP_breg12 =0x7c, - DW_OP_breg13 =0x7d, - DW_OP_breg14 =0x7e, - DW_OP_breg15 =0x7f, - DW_OP_breg16 =0x80, - DW_OP_breg17 =0x81, - DW_OP_breg18 =0x82, - DW_OP_breg19 =0x83, - DW_OP_breg20 =0x84, - DW_OP_breg21 =0x85, - DW_OP_breg22 =0x86, - DW_OP_breg23 =0x87, - DW_OP_breg24 =0x88, - DW_OP_breg25 =0x89, - DW_OP_breg26 =0x8a, - DW_OP_breg27 =0x8b, - DW_OP_breg28 =0x8c, - DW_OP_breg29 =0x8d, - DW_OP_breg30 =0x8e, - DW_OP_breg31 =0x8f, - DW_OP_regX =0x90, - DW_OP_fbreg =0x91, - DW_OP_bregX =0x92, - DW_OP_piece =0x93, - DW_OP_deref_size =0x94, - DW_OP_xderef_size =0x95, - DW_OP_nop =0x96, - // DWARF3/DWARF3f - DW_OP_push_object_address =0x97, - DW_OP_call2 =0x98, - DW_OP_call4 =0x99, - DW_OP_call_ref =0x9a, - DW_OP_form_tls_address =0x9b, - DW_OP_call_frame_cfa =0x9c, - DW_OP_bit_piece =0x9d, - DW_OP_lo_user =0xe0, - DW_OP_hi_user =0xff, - // GNU extensions - DW_OP_GNU_push_tls_address =0xe0 -}; - -} // namespace dwarf2reader -#endif // COMMON_MAC_DWARF_DWARF2ENUMS_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.cc 1970-01-01 01:00:00.000000000 +0100 @@ -1,830 +0,0 @@ -// Copyright 2006 Google Inc. All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#include -#include -#include - -#include "common/mac/dwarf/bytereader-inl.h" -#include "common/mac/dwarf/dwarf2reader.h" -#include "common/mac/dwarf/bytereader.h" -#include "common/mac/dwarf/line_state_machine.h" - -namespace __gnu_cxx -{ - template<> struct hash< std::string > - { - size_t operator()( const std::string& x ) const - { - return hash< const char* >()( x.c_str() ); - } - }; -} - -namespace dwarf2reader { - -// Read a DWARF2/3 initial length field from START, using READER, and -// report the length in LEN. Return the actual initial length. - -static uint64 ReadInitialLength(const char* start, - ByteReader* reader, size_t* len) { - const uint64 initial_length = reader->ReadFourBytes(start); - start += 4; - - // In DWARF2/3, if the initial length is all 1 bits, then the offset - // size is 8 and we need to read the next 8 bytes for the real length. - if (initial_length == 0xffffffff) { - reader->SetOffsetSize(8); - *len = 12; - return reader->ReadOffset(start); - } else { - reader->SetOffsetSize(4); - *len = 4; - } - return initial_length; -} - -CompilationUnit::CompilationUnit(const SectionMap& sections, uint64 offset, - ByteReader* reader, Dwarf2Handler* handler) - : offset_from_section_start_(offset), reader_(reader), - sections_(sections), handler_(handler), abbrevs_(NULL), - string_buffer_(NULL), string_buffer_length_(0) {} - -// Read a DWARF2/3 abbreviation section. -// Each abbrev consists of a abbreviation number, a tag, a byte -// specifying whether the tag has children, and a list of -// attribute/form pairs. -// The list of forms is terminated by a 0 for the attribute, and a -// zero for the form. The entire abbreviation section is terminated -// by a zero for the code. - -void CompilationUnit::ReadAbbrevs() { - if (abbrevs_) - return; - - // First get the debug_abbrev section - SectionMap::const_iterator iter = sections_.find("__debug_abbrev"); - assert(iter != sections_.end()); - - abbrevs_ = new vector; - abbrevs_->resize(1); - - // The only way to check whether we are reading over the end of the - // buffer would be to first compute the size of the leb128 data by - // reading it, then go back and read it again. - const char* abbrev_start = iter->second.first + - header_.abbrev_offset; - const char* abbrevptr = abbrev_start; - const uint64 abbrev_length = iter->second.second - header_.abbrev_offset; - - while (1) { - CompilationUnit::Abbrev abbrev; - size_t len; - const uint32 number = reader_->ReadUnsignedLEB128(abbrevptr, &len); - - if (number == 0) - break; - abbrev.number = number; - abbrevptr += len; - - assert(abbrevptr < abbrev_start + abbrev_length); - const uint32 tag = reader_->ReadUnsignedLEB128(abbrevptr, &len); - abbrevptr += len; - abbrev.tag = static_cast(tag); - - assert(abbrevptr < abbrev_start + abbrev_length); - abbrev.has_children = reader_->ReadOneByte(abbrevptr); - abbrevptr += 1; - - assert(abbrevptr < abbrev_start + abbrev_length); - - while (1) { - const uint32 nametemp = reader_->ReadUnsignedLEB128(abbrevptr, &len); - abbrevptr += len; - - assert(abbrevptr < abbrev_start + abbrev_length); - const uint32 formtemp = reader_->ReadUnsignedLEB128(abbrevptr, &len); - abbrevptr += len; - if (nametemp == 0 && formtemp == 0) - break; - - const enum DwarfAttribute name = - static_cast(nametemp); - const enum DwarfForm form = static_cast(formtemp); - abbrev.attributes.push_back(make_pair(name, form)); - } - assert(abbrev.number == abbrevs_->size()); - abbrevs_->push_back(abbrev); - } -} - -// Skips a single DIE's attributes. -const char* CompilationUnit::SkipDIE(const char* start, - const Abbrev& abbrev) { - for (AttributeList::const_iterator i = abbrev.attributes.begin(); - i != abbrev.attributes.end(); - i++) { - start = SkipAttribute(start, i->second); - } - return start; -} - -// Skips a single attribute form's data. -const char* CompilationUnit::SkipAttribute(const char* start, - enum DwarfForm form) { - size_t len; - - switch (form) { - case DW_FORM_indirect: - form = static_cast(reader_->ReadUnsignedLEB128(start, - &len)); - start += len; - return SkipAttribute(start, form); - break; - - case DW_FORM_data1: - case DW_FORM_flag: - case DW_FORM_ref1: - return start + 1; - break; - case DW_FORM_ref2: - case DW_FORM_data2: - return start + 2; - break; - case DW_FORM_ref4: - case DW_FORM_data4: - return start + 4; - break; - case DW_FORM_ref8: - case DW_FORM_data8: - return start + 8; - break; - case DW_FORM_string: - return start + strlen(start) + 1; - break; - case DW_FORM_udata: - case DW_FORM_ref_udata: - reader_->ReadUnsignedLEB128(start, &len); - return start + len; - break; - - case DW_FORM_sdata: - reader_->ReadSignedLEB128(start, &len); - return start + len; - break; - case DW_FORM_addr: - return start + reader_->AddressSize(); - break; - case DW_FORM_ref_addr: - // DWARF2 and 3 differ on whether ref_addr is address size or - // offset size. - assert(header_.version == 2 || header_.version == 3); - if (header_.version == 2) { - return start + reader_->AddressSize(); - } else if (header_.version == 3) { - return start + reader_->OffsetSize(); - } - break; - - case DW_FORM_block1: - return start + 1 + reader_->ReadOneByte(start); - break; - case DW_FORM_block2: - return start + 2 + reader_->ReadTwoBytes(start); - break; - case DW_FORM_block4: - return start + 4 + reader_->ReadFourBytes(start); - break; - case DW_FORM_block: { - uint64 size = reader_->ReadUnsignedLEB128(start, &len); - return start + size + len; - } - break; - case DW_FORM_strp: - return start + reader_->OffsetSize(); - break; - default: - fprintf(stderr,"Unhandled form type"); - } - fprintf(stderr,"Unhandled form type"); - return NULL; -} - -// Read a DWARF2/3 header. -// The header is variable length in DWARF3 (and DWARF2 as extended by -// most compilers), and consists of an length field, a version number, -// the offset in the .debug_abbrev section for our abbrevs, and an -// address size. -void CompilationUnit::ReadHeader() { - const char* headerptr = buffer_; - size_t initial_length_size; - - assert(headerptr + 4 < buffer_ + buffer_length_); - const uint64 initial_length = ReadInitialLength(headerptr, reader_, - &initial_length_size); - headerptr += initial_length_size; - header_.length = initial_length; - - assert(headerptr + 2 < buffer_ + buffer_length_); - header_.version = reader_->ReadTwoBytes(headerptr); - headerptr += 2; - - assert(headerptr + reader_->OffsetSize() < buffer_ + buffer_length_); - header_.abbrev_offset = reader_->ReadOffset(headerptr); - headerptr += reader_->OffsetSize(); - - assert(headerptr + 1 < buffer_ + buffer_length_); - header_.address_size = reader_->ReadOneByte(headerptr); - reader_->SetAddressSize(header_.address_size); - headerptr += 1; - - after_header_ = headerptr; - - // This check ensures that we don't have to do checking during the - // reading of DIEs. header_.length does not include the size of the - // initial length. - assert(buffer_ + initial_length_size + header_.length <= - buffer_ + buffer_length_); -} - -uint64 CompilationUnit::Start() { - // First get the debug_info section - SectionMap::const_iterator iter = sections_.find("__debug_info"); - assert(iter != sections_.end()); - - // Set up our buffer - buffer_ = iter->second.first + offset_from_section_start_; - buffer_length_ = iter->second.second - offset_from_section_start_; - - // Read the header - ReadHeader(); - - // Figure out the real length from the end of the initial length to - // the end of the compilation unit, since that is the value we - // return. - uint64 ourlength = header_.length; - if (reader_->OffsetSize() == 8) - ourlength += 12; - else - ourlength += 4; - - // See if the user wants this compilation unit, and if not, just return. - if (!handler_->StartCompilationUnit(offset_from_section_start_, - reader_->AddressSize(), - reader_->OffsetSize(), - header_.length, - header_.version)) - return ourlength; - - // Otherwise, continue by reading our abbreviation entries. - ReadAbbrevs(); - - // Set the string section if we have one. - iter = sections_.find("__debug_str"); - if (iter != sections_.end()) { - string_buffer_ = iter->second.first; - string_buffer_length_ = iter->second.second; - } - - // Now that we have our abbreviations, start processing DIE's. - ProcessDIEs(); - - return ourlength; -} - -// If one really wanted, you could merge SkipAttribute and -// ProcessAttribute -// This is all boring data manipulation and calling of the handler. -const char* CompilationUnit::ProcessAttribute( - uint64 dieoffset, const char* start, enum DwarfAttribute attr, - enum DwarfForm form) { - size_t len; - - switch (form) { - // DW_FORM_indirect is never used because it is such a space - // waster. - case DW_FORM_indirect: - form = static_cast(reader_->ReadUnsignedLEB128(start, - &len)); - start += len; - return ProcessAttribute(dieoffset, start, attr, form); - break; - - case DW_FORM_data1: - case DW_FORM_flag: - case DW_FORM_ref1: - handler_->ProcessAttributeUnsigned(dieoffset, attr, form, - reader_->ReadOneByte(start)); - return start + 1; - break; - case DW_FORM_ref2: - case DW_FORM_data2: - handler_->ProcessAttributeUnsigned(dieoffset, attr, form, - reader_->ReadTwoBytes(start)); - return start + 2; - break; - case DW_FORM_ref4: - case DW_FORM_data4: - handler_->ProcessAttributeUnsigned(dieoffset, attr, form, - reader_->ReadFourBytes(start)); - return start + 4; - break; - case DW_FORM_ref8: - case DW_FORM_data8: - handler_->ProcessAttributeUnsigned(dieoffset, attr, form, - reader_->ReadEightBytes(start)); - return start + 8; - break; - case DW_FORM_string: { - const char* str = start; - handler_->ProcessAttributeString(dieoffset, attr, form, - str); - return start + strlen(str) + 1; - } - break; - case DW_FORM_udata: - case DW_FORM_ref_udata: - handler_->ProcessAttributeUnsigned(dieoffset, attr, form, - reader_->ReadUnsignedLEB128(start, - &len)); - return start + len; - break; - - case DW_FORM_sdata: - handler_->ProcessAttributeSigned(dieoffset, attr, form, - reader_->ReadSignedLEB128(start, &len)); - return start + len; - break; - case DW_FORM_addr: - handler_->ProcessAttributeUnsigned(dieoffset, attr, form, - reader_->ReadAddress(start)); - return start + reader_->AddressSize(); - break; - case DW_FORM_ref_addr: - // DWARF2 and 3 differ on whether ref_addr is address size or - // offset size. - assert(header_.version == 2 || header_.version == 3); - if (header_.version == 2) { - handler_->ProcessAttributeUnsigned(dieoffset, attr, form, - reader_->ReadAddress(start)); - return start + reader_->AddressSize(); - } else if (header_.version == 3) { - handler_->ProcessAttributeUnsigned(dieoffset, attr, form, - reader_->ReadOffset(start)); - return start + reader_->OffsetSize(); - } - break; - - case DW_FORM_block1: { - uint64 datalen = reader_->ReadOneByte(start); - handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 1, - datalen); - return start + 1 + datalen; - } - break; - case DW_FORM_block2: { - uint64 datalen = reader_->ReadTwoBytes(start); - handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 2, - datalen); - return start + 2 + datalen; - } - break; - case DW_FORM_block4: { - uint64 datalen = reader_->ReadFourBytes(start); - handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 4, - datalen); - return start + 4 + datalen; - } - break; - case DW_FORM_block: { - uint64 datalen = reader_->ReadUnsignedLEB128(start, &len); - handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + len, - datalen); - return start + datalen + len; - } - break; - case DW_FORM_strp: { - assert(string_buffer_ != NULL); - - const uint64 offset = reader_->ReadOffset(start); - assert(string_buffer_ + offset < string_buffer_ + string_buffer_length_); - - const char* str = string_buffer_ + offset; - handler_->ProcessAttributeString(dieoffset, attr, form, - str); - return start + reader_->OffsetSize(); - } - break; - default: - fprintf(stderr, "Unhandled form type"); - } - fprintf(stderr, "Unhandled form type"); - return NULL; -} - -const char* CompilationUnit::ProcessDIE(uint64 dieoffset, - const char* start, - const Abbrev& abbrev) { - for (AttributeList::const_iterator i = abbrev.attributes.begin(); - i != abbrev.attributes.end(); - i++) { - start = ProcessAttribute(dieoffset, start, i->first, i->second); - } - return start; -} - -void CompilationUnit::ProcessDIEs() { - const char* dieptr = after_header_; - size_t len; - - // lengthstart is the place the length field is based on. - // It is the point in the header after the initial length field - const char* lengthstart = buffer_; - - // In 64 bit dwarf, the initial length is 12 bytes, because of the - // 0xffffffff at the start. - if (reader_->OffsetSize() == 8) - lengthstart += 12; - else - lengthstart += 4; - - // we need semantics of boost scoped_ptr here - no intention of trasnferring - // ownership of the stack. use const, but then we limit ourselves to not - // ever being able to call .reset() on the smart pointer. - auto_ptr > const die_stack(new stack); - - while (dieptr < (lengthstart + header_.length)) { - // We give the user the absolute offset from the beginning of - // debug_info, since they need it to deal with ref_addr forms. - uint64 absolute_offset = (dieptr - buffer_) + offset_from_section_start_; - - uint64 abbrev_num = reader_->ReadUnsignedLEB128(dieptr, &len); - - dieptr += len; - - // Abbrev == 0 represents the end of a list of children. - if (abbrev_num == 0) { - const uint64 offset = die_stack->top(); - die_stack->pop(); - handler_->EndDIE(offset); - continue; - } - - const Abbrev& abbrev = abbrevs_->at(abbrev_num); - const enum DwarfTag tag = abbrev.tag; - if (!handler_->StartDIE(absolute_offset, tag, abbrev.attributes)) { - dieptr = SkipDIE(dieptr, abbrev); - } else { - dieptr = ProcessDIE(absolute_offset, dieptr, abbrev); - } - - if (abbrev.has_children) { - die_stack->push(absolute_offset); - } else { - handler_->EndDIE(absolute_offset); - } - } -} - -LineInfo::LineInfo(const char* buffer, uint64 buffer_length, - ByteReader* reader, LineInfoHandler* handler): - handler_(handler), reader_(reader), buffer_(buffer), - buffer_length_(buffer_length) { - header_.std_opcode_lengths = NULL; -} - -uint64 LineInfo::Start() { - ReadHeader(); - ReadLines(); - return after_header_ - buffer_; -} - -// The header for a debug_line section is mildly complicated, because -// the line info is very tightly encoded. -void LineInfo::ReadHeader() { - const char* lineptr = buffer_; - size_t initial_length_size; - - const uint64 initial_length = ReadInitialLength(lineptr, reader_, - &initial_length_size); - - lineptr += initial_length_size; - header_.total_length = initial_length; - assert(buffer_ + initial_length_size + header_.total_length <= - buffer_ + buffer_length_); - - // Address size *must* be set by CU ahead of time. - assert(reader_->AddressSize() != 0); - - header_.version = reader_->ReadTwoBytes(lineptr); - lineptr += 2; - - header_.prologue_length = reader_->ReadOffset(lineptr); - lineptr += reader_->OffsetSize(); - - header_.min_insn_length = reader_->ReadOneByte(lineptr); - lineptr += 1; - - header_.default_is_stmt = reader_->ReadOneByte(lineptr); - lineptr += 1; - - header_.line_base = *reinterpret_cast(lineptr); - lineptr += 1; - - header_.line_range = reader_->ReadOneByte(lineptr); - lineptr += 1; - - header_.opcode_base = reader_->ReadOneByte(lineptr); - lineptr += 1; - - header_.std_opcode_lengths = new vector; - header_.std_opcode_lengths->resize(header_.opcode_base + 1); - (*header_.std_opcode_lengths)[0] = 0; - for (int i = 1; i < header_.opcode_base; i++) { - (*header_.std_opcode_lengths)[i] = reader_->ReadOneByte(lineptr); - lineptr += 1; - } - - // It is legal for the directory entry table to be empty. - if (*lineptr) { - uint32 dirindex = 1; - while (*lineptr) { - const char* dirname = lineptr; - handler_->DefineDir(dirname, dirindex); - lineptr += strlen(dirname) + 1; - dirindex++; - } - } - lineptr++; - - // It is also legal for the file entry table to be empty. - if (*lineptr) { - uint32 fileindex = 1; - size_t len; - while (*lineptr) { - const char* filename = lineptr; - lineptr += strlen(filename) + 1; - - uint64 dirindex = reader_->ReadUnsignedLEB128(lineptr, &len); - lineptr += len; - - uint64 mod_time = reader_->ReadUnsignedLEB128(lineptr, &len); - lineptr += len; - - uint64 filelength = reader_->ReadUnsignedLEB128(lineptr, &len); - lineptr += len; - handler_->DefineFile(filename, fileindex, dirindex, mod_time, - filelength); - fileindex++; - } - } - lineptr++; - - after_header_ = lineptr; -} - -/* static */ -bool LineInfo::ProcessOneOpcode(ByteReader* reader, - LineInfoHandler* handler, - const struct LineInfoHeader &header, - const char* start, - struct LineStateMachine* lsm, - size_t* len, - uintptr_t pc, - bool *lsm_passes_pc) { - size_t oplen = 0; - size_t templen; - uint8 opcode = reader->ReadOneByte(start); - oplen++; - start++; - - // If the opcode is great than the opcode_base, it is a special - // opcode. Most line programs consist mainly of special opcodes. - if (opcode >= header.opcode_base) { - opcode -= header.opcode_base; - const int64 advance_address = (opcode / header.line_range) - * header.min_insn_length; - const int64 advance_line = (opcode % header.line_range) - + header.line_base; - - // Check if the lsm passes "pc". If so, mark it as passed. - if (lsm_passes_pc && - lsm->address <= pc && pc < lsm->address + advance_address) { - *lsm_passes_pc = true; - } - - lsm->address += advance_address; - lsm->line_num += advance_line; - lsm->basic_block = true; - *len = oplen; - return true; - } - - // Otherwise, we have the regular opcodes - switch (opcode) { - case DW_LNS_copy: { - lsm->basic_block = false; - *len = oplen; - return true; - } - - case DW_LNS_advance_pc: { - uint64 advance_address = reader->ReadUnsignedLEB128(start, &templen); - oplen += templen; - - // Check if the lsm passes "pc". If so, mark it as passed. - if (lsm_passes_pc && lsm->address <= pc && - pc < lsm->address + header.min_insn_length * advance_address) { - *lsm_passes_pc = true; - } - - lsm->address += header.min_insn_length * advance_address; - } - break; - case DW_LNS_advance_line: { - const int64 advance_line = reader->ReadSignedLEB128(start, &templen); - oplen += templen; - lsm->line_num += advance_line; - - // With gcc 4.2.1, we can get the line_no here for the first time - // since DW_LNS_advance_line is called after DW_LNE_set_address is - // called. So we check if the lsm passes "pc" here, not in - // DW_LNE_set_address. - if (lsm_passes_pc && lsm->address == pc) { - *lsm_passes_pc = true; - } - } - break; - case DW_LNS_set_file: { - const uint64 fileno = reader->ReadUnsignedLEB128(start, &templen); - oplen += templen; - lsm->file_num = fileno; - } - break; - case DW_LNS_set_column: { - const uint64 colno = reader->ReadUnsignedLEB128(start, &templen); - oplen += templen; - lsm->column_num = colno; - } - break; - case DW_LNS_negate_stmt: { - lsm->is_stmt = !lsm->is_stmt; - } - break; - case DW_LNS_set_basic_block: { - lsm->basic_block = true; - } - break; - case DW_LNS_fixed_advance_pc: { - const uint16 advance_address = reader->ReadTwoBytes(start); - oplen += 2; - - // Check if the lsm passes "pc". If so, mark it as passed. - if (lsm_passes_pc && - lsm->address <= pc && pc < lsm->address + advance_address) { - *lsm_passes_pc = true; - } - - lsm->address += advance_address; - } - break; - case DW_LNS_const_add_pc: { - const int64 advance_address = header.min_insn_length - * ((255 - header.opcode_base) - / header.line_range); - - // Check if the lsm passes "pc". If so, mark it as passed. - if (lsm_passes_pc && - lsm->address <= pc && pc < lsm->address + advance_address) { - *lsm_passes_pc = true; - } - - lsm->address += advance_address; - } - break; - case DW_LNS_extended_op: { - const size_t extended_op_len = reader->ReadUnsignedLEB128(start, - &templen); - start += templen; - oplen += templen + extended_op_len; - - const uint64 extended_op = reader->ReadOneByte(start); - start++; - - switch (extended_op) { - case DW_LNE_end_sequence: { - lsm->end_sequence = true; - *len = oplen; - return true; - } - break; - case DW_LNE_set_address: { - // With gcc 4.2.1, we cannot tell the line_no here since - // DW_LNE_set_address is called before DW_LNS_advance_line is - // called. So we do not check if the lsm passes "pc" here. See - // also the comment in DW_LNS_advance_line. - uint64 address = reader->ReadAddress(start); - lsm->address = address; - } - break; - case DW_LNE_define_file: { - const char* filename = start; - - templen = strlen(filename) + 1; - start += templen; - - uint64 dirindex = reader->ReadUnsignedLEB128(start, &templen); - oplen += templen; - - const uint64 mod_time = reader->ReadUnsignedLEB128(start, - &templen); - oplen += templen; - - const uint64 filelength = reader->ReadUnsignedLEB128(start, - &templen); - oplen += templen; - - if (handler) { - handler->DefineFile(filename, -1, dirindex, mod_time, - filelength); - } - } - break; - } - } - break; - - default: { - // Ignore unknown opcode silently - if (header.std_opcode_lengths) { - for (int i = 0; i < (*header.std_opcode_lengths)[opcode]; i++) { - size_t templen; - reader->ReadUnsignedLEB128(start, &templen); - start += templen; - oplen += templen; - } - } - } - break; - } - *len = oplen; - return false; -} - -void LineInfo::ReadLines() { - struct LineStateMachine lsm; - - // lengthstart is the place the length field is based on. - // It is the point in the header after the initial length field - const char* lengthstart = buffer_; - - // In 64 bit dwarf, the initial length is 12 bytes, because of the - // 0xffffffff at the start. - if (reader_->OffsetSize() == 8) - lengthstart += 12; - else - lengthstart += 4; - - const char* lineptr = after_header_; - while (lineptr < lengthstart + header_.total_length) { - lsm.Reset(header_.default_is_stmt); - while (!lsm.end_sequence) { - size_t oplength; - bool add_line = ProcessOneOpcode(reader_, handler_, header_, - lineptr, &lsm, &oplength, (uintptr_t)-1, NULL); - if (add_line) - handler_->AddLine(lsm.address, lsm.file_num, lsm.line_num, - lsm.column_num); - lineptr += oplength; - } - } - - after_header_ = lengthstart + header_.total_length; -} - -} // namespace dwarf2reader diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/dwarf2reader.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,393 +0,0 @@ -// Copyright 2006 Google Inc. All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// This file contains definitions related to the DWARF2/3 reader and -// it's handler interfaces. -// The DWARF2/3 specification can be found at -// http://dwarf.freestandards.org and should be considered required -// reading if you wish to modify the implementation. -// Only a cursory attempt is made to explain terminology that is -// used here, as it is much better explained in the standard documents -#ifndef COMMON_MAC_DWARF_DWARF2READER_H__ -#define COMMON_MAC_DWARF_DWARF2READER_H__ - -#include -#include -#include -#include -#include - -#include "common/mac/dwarf/dwarf2enums.h" -#include "common/mac/dwarf/types.h" - -using namespace std; -using namespace __gnu_cxx; - -namespace dwarf2reader { -struct LineStateMachine; -class ByteReader; -class Dwarf2Handler; -class LineInfoHandler; - -// This maps from a string naming a section to a pair containing a -// the data for the section, and the size of the section. -typedef hash_map > SectionMap; -typedef list > AttributeList; -typedef AttributeList::iterator AttributeIterator; -typedef AttributeList::const_iterator ConstAttributeIterator; - -struct LineInfoHeader { - uint64 total_length; - uint16 version; - uint64 prologue_length; - uint8 min_insn_length; // insn stands for instructin - bool default_is_stmt; // stmt stands for statement - int8 line_base; - uint8 line_range; - uint8 opcode_base; - // Use a pointer so that signalsafe_addr2line is able to use this structure - // without heap allocation problem. - vector *std_opcode_lengths; -}; - -class LineInfo { - public: - - // Initializes a .debug_line reader. Buffer and buffer length point - // to the beginning and length of the line information to read. - // Reader is a ByteReader class that has the endianness set - // properly. - LineInfo(const char* buffer_, uint64 buffer_length, - ByteReader* reader, LineInfoHandler* handler); - - virtual ~LineInfo() { - if (header_.std_opcode_lengths) { - delete header_.std_opcode_lengths; - } - } - - // Start processing line info, and calling callbacks in the handler. - // Consumes the line number information for a single compilation unit. - // Returns the number of bytes processed. - uint64 Start(); - - // Process a single line info opcode at START using the state - // machine at LSM. Return true if we should define a line using the - // current state of the line state machine. Place the length of the - // opcode in LEN. - // If LSM_PASSES_PC is non-NULL, this function also checks if the lsm - // passes the address of PC. In other words, LSM_PASSES_PC will be - // set to true, if the following condition is met. - // - // lsm's old address < PC <= lsm's new address - static bool ProcessOneOpcode(ByteReader* reader, - LineInfoHandler* handler, - const struct LineInfoHeader &header, - const char* start, - struct LineStateMachine* lsm, - size_t* len, - uintptr_t pc, - bool *lsm_passes_pc); - - private: - // Reads the DWARF2/3 header for this line info. - void ReadHeader(); - - // Reads the DWARF2/3 line information - void ReadLines(); - - // The associated handler to call processing functions in - LineInfoHandler* handler_; - - // The associated ByteReader that handles endianness issues for us - ByteReader* reader_; - - // A DWARF2/3 line info header. This is not the same size as - // in the actual file, as the one in the file may have a 32 bit or - // 64 bit lengths - - struct LineInfoHeader header_; - - // buffer is the buffer for our line info, starting at exactly where - // the line info to read is. after_header is the place right after - // the end of the line information header. - const char* buffer_; - uint64 buffer_length_; - const char* after_header_; -}; - -// This class is the main interface between the line info reader and -// the client. The virtual functions inside this get called for -// interesting events that happen during line info reading. The -// default implementation does nothing - -class LineInfoHandler { - public: - LineInfoHandler() { } - - virtual ~LineInfoHandler() { } - - // Called when we define a directory. NAME is the directory name, - // DIR_NUM is the directory number - virtual void DefineDir(const string& name, uint32 dir_num) { } - - // Called when we define a filename. NAME is the filename, FILE_NUM - // is the file number which is -1 if the file index is the next - // index after the last numbered index (this happens when files are - // dynamically defined by the line program), DIR_NUM is the - // directory index for the directory name of this file, MOD_TIME is - // the modification time of the file, and LENGTH is the length of - // the file - virtual void DefineFile(const string& name, int32 file_num, - uint32 dir_num, uint64 mod_time, - uint64 length) { } - - // Called when the line info reader has a new line, address pair - // ready for us. ADDRESS is the address of the code, FILE_NUM is - // the file number containing the code, LINE_NUM is the line number in - // that file for the code, and COLUMN_NUM is the column number the code - // starts at, if we know it (0 otherwise). - virtual void AddLine(uint64 address, uint32 file_num, uint32 line_num, - uint32 column_num) { } -}; - -// The base of DWARF2/3 debug info is a DIE (Debugging Information -// Entry. -// DWARF groups DIE's into a tree and calls the root of this tree a -// "compilation unit". Most of the time, their is one compilation -// unit in the .debug_info section for each file that had debug info -// generated. -// Each DIE consists of - -// 1. a tag specifying a thing that is being described (ie -// DW_TAG_subprogram for functions, DW_TAG_variable for variables, etc -// 2. attributes (such as DW_AT_location for location in memory, -// DW_AT_name for name), and data for each attribute. -// 3. A flag saying whether the DIE has children or not - -// In order to gain some amount of compression, the format of -// each DIE (tag name, attributes and data forms for the attributes) -// are stored in a separate table called the "abbreviation table". -// This is done because a large number of DIEs have the exact same tag -// and list of attributes, but different data for those attributes. -// As a result, the .debug_info section is just a stream of data, and -// requires reading of the .debug_abbrev section to say what the data -// means. - -// As a warning to the user, it should be noted that the reason for -// using absolute offsets from the beginning of .debug_info is that -// DWARF2/3 support referencing DIE's from other DIE's by their offset -// from either the current compilation unit start, *or* the beginning -// of the .debug_info section. This means it is possible to reference -// a DIE in one compilation unit from a DIE in another compilation -// unit. This style of reference is usually used to eliminate -// duplicated information that occurs across compilation -// units, such as base types, etc. GCC 3.4+ support this with -// -feliminate-dwarf2-dups. Other toolchains will sometimes do -// duplicate elimination in the linker. - -class CompilationUnit { - public: - - // Initialize a compilation unit. This requires a map of sections, - // the offset of this compilation unit in the debug_info section, a - // ByteReader, and a Dwarf2Handler class to call callbacks in. - CompilationUnit(const SectionMap& sections, uint64 offset, - ByteReader* reader, Dwarf2Handler* handler); - virtual ~CompilationUnit() { - if (abbrevs_) delete abbrevs_; - } - - // Begin reading a Dwarf2 compilation unit, and calling the - // callbacks in the Dwarf2Handler - // Return the offset of the end of the compilation unit - the passed - // in offset. - uint64 Start(); - - private: - - // This struct represents a single DWARF2/3 abbreviation - // The abbreviation tells how to read a DWARF2/3 DIE, and consist of a - // tag and a list of attributes, as well as the data form of each attribute. - struct Abbrev { - uint32 number; - enum DwarfTag tag; - bool has_children; - AttributeList attributes; - }; - - // A DWARF2/3 compilation unit header. This is not the same size as - // in the actual file, as the one in the file may have a 32 bit or - // 64 bit length. - struct CompilationUnitHeader { - uint64 length; - uint16 version; - uint64 abbrev_offset; - uint8 address_size; - } header_; - - // Reads the DWARF2/3 header for this compilation unit. - void ReadHeader(); - - // Reads the DWARF2/3 abbreviations for this compilation unit - void ReadAbbrevs(); - - // Processes a single DIE for this compilation unit and return a new - // pointer just past the end of it - const char* ProcessDIE(uint64 dieoffset, - const char* start, - const Abbrev& abbrev); - - // Processes a single attribute and return a new pointer just past the - // end of it - const char* ProcessAttribute(uint64 dieoffset, - const char* start, - enum DwarfAttribute attr, - enum DwarfForm form); - - // Processes all DIEs for this compilation unit - void ProcessDIEs(); - - // Skips the die with attributes specified in ABBREV starting at - // START, and return the new place to position the stream to. - const char* SkipDIE(const char* start, - const Abbrev& abbrev); - - // Skips the attribute starting at START, with FORM, and return the - // new place to position the stream to. - const char* SkipAttribute(const char* start, - enum DwarfForm form); - - // Offset from section start is the offset of this compilation unit - // from the beginning of the .debug_info section. - uint64 offset_from_section_start_; - - // buffer is the buffer for our CU, starting at .debug_info + offset - // passed in from constructor. - // after_header points to right after the compilation unit header. - const char* buffer_; - uint64 buffer_length_; - const char* after_header_; - - // The associated ByteReader that handles endianness issues for us - ByteReader* reader_; - - // The map of sections in our file to buffers containing their data - const SectionMap& sections_; - - // The associated handler to call processing functions in - Dwarf2Handler* handler_; - - // Set of DWARF2/3 abbreviations for this compilation unit. Indexed - // by abbreviation number, which means that abbrevs_[0] is not - // valid. - vector* abbrevs_; - - // String section buffer and length, if we have a string section. - // This is here to avoid doing a section lookup for strings in - // ProcessAttribute, which is in the hot path for DWARF2 reading. - const char* string_buffer_; - uint64 string_buffer_length_; -}; - -// This class is the main interface between the reader and the -// client. The virtual functions inside this get called for -// interesting events that happen during DWARF2 reading. -// The default implementation skips everything. - -class Dwarf2Handler { - public: - Dwarf2Handler() { } - - virtual ~Dwarf2Handler() { } - - // Start to process a compilation unit at OFFSET from the beginning of the - // debug_info section. Return false if you would like - // to skip this compilation unit. - virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, - uint8 offset_size, uint64 cu_length, - uint8 dwarf_version) { return false; } - - // Start to process a DIE at OFFSET from the beginning of the - // debug_info section. Return false if you would like to skip this - // DIE. - virtual bool StartDIE(uint64 offset, enum DwarfTag tag, - const AttributeList& attrs) { return false; } - - // Called when we have an attribute with unsigned data to give to - // our handler. The attribute is for the DIE at OFFSET from the - // beginning of compilation unit, has a name of ATTR, a form of - // FORM, and the actual data of the attribute is in DATA. - virtual void ProcessAttributeUnsigned(uint64 offset, - enum DwarfAttribute attr, - enum DwarfForm form, - uint64 data) { } - - // Called when we have an attribute with signed data to give to - // our handler. The attribute is for the DIE at OFFSET from the - // beginning of compilation unit, has a name of ATTR, a form of - // FORM, and the actual data of the attribute is in DATA. - virtual void ProcessAttributeSigned(uint64 offset, - enum DwarfAttribute attr, - enum DwarfForm form, - int64 data) { } - - // Called when we have an attribute with a buffer of data to give to - // our handler. The attribute is for the DIE at OFFSET from the - // beginning of compilation unit, has a name of ATTR, a form of - // FORM, and the actual data of the attribute is in DATA, and the - // length of the buffer is LENGTH. The buffer is owned by the - // caller, not the callee, and may not persist for very long. If - // you want the data to be available later, it needs to be copied. - virtual void ProcessAttributeBuffer(uint64 offset, - enum DwarfAttribute attr, - enum DwarfForm form, - const char* data, - uint64 len) { } - - // Called when we have an attribute with string data to give to - // our handler. The attribute is for the DIE at OFFSET from the - // beginning of compilation unit, has a name of ATTR, a form of - // FORM, and the actual data of the attribute is in DATA. - virtual void ProcessAttributeString(uint64 offset, - enum DwarfAttribute attr, - enum DwarfForm form, - const string& data) { } - - // Called when finished processing the DIE at OFFSET. - // Because DWARF2/3 specifies a tree of DIEs, you may get starts - // before ends of the previous DIE, as we process children before - // ending the parent. - virtual void EndDIE(uint64 offset) { } - -}; - - -} // namespace dwarf2reader - -#endif // UTIL_DEBUGINFO_DWARF2READER_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.cc 1970-01-01 01:00:00.000000000 +0100 @@ -1,240 +0,0 @@ -// Copyright 2006 Google Inc. All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// This is a client for the dwarf2reader to extract function and line -// information from the debug info. - -#include -#include -#include - - -#include "common/mac/dwarf/functioninfo.h" - -#include "common/mac/dwarf/bytereader.h" - - -namespace __gnu_cxx -{ - template<> - struct hash - { - size_t operator()(const std::string& k) const; - }; -} - - -namespace dwarf2reader { - -// Given an offset value, its form, and the base offset of the -// compilation unit containing this value, return an absolute offset -// within the .debug_info section. -uint64 GetAbsoluteOffset(uint64 offset, - enum DwarfForm form, - uint64 compilation_unit_base) { - switch (form) { - case DW_FORM_ref1: - case DW_FORM_ref2: - case DW_FORM_ref4: - case DW_FORM_ref8: - case DW_FORM_ref_udata: - return offset + compilation_unit_base; - case DW_FORM_ref_addr: - default: - return offset; - } -} - -CULineInfoHandler::CULineInfoHandler(vector* files, - vector* dirs, - LineMap* linemap):linemap_(linemap), - files_(files), - dirs_(dirs) { - // The dirs and files are 1 indexed, so just make sure we put - // nothing in the 0 vector. - assert(dirs->size() == 0); - assert(files->size() == 0); - dirs->push_back(""); - SourceFileInfo s; - s.name = ""; - s.lowpc = ULLONG_MAX; - files->push_back(s); -} - -void CULineInfoHandler::DefineDir(const string& name, uint32 dir_num) { - // These should never come out of order, actually - assert(dir_num == dirs_->size()); - dirs_->push_back(name); -} - -void CULineInfoHandler::DefineFile(const string& name, - int32 file_num, uint32 dir_num, - uint64 mod_time, uint64 length) { - assert(dir_num >= 0); - assert(dir_num < dirs_->size()); - - // These should never come out of order, actually. - if (file_num == (int32)files_->size() || file_num == -1) { - string dir = dirs_->at(dir_num); - - SourceFileInfo s; - s.lowpc = ULLONG_MAX; - - if (dir == "") { - s.name = name; - } else { - s.name = dir + "/" + name; - } - - files_->push_back(s); - } else { - fprintf(stderr, "error in DefineFile"); - } -} - -void CULineInfoHandler::AddLine(uint64 address, uint32 file_num, - uint32 line_num, uint32 column_num) { - if (file_num < files_->size()) { - linemap_->insert(make_pair(address, make_pair(files_->at(file_num).name.c_str(), - line_num))); - - if(address < files_->at(file_num).lowpc) { - files_->at(file_num).lowpc = address; - } - } else { - fprintf(stderr,"error in AddLine"); - } -} - -bool CUFunctionInfoHandler::StartCompilationUnit(uint64 offset, - uint8 address_size, - uint8 offset_size, - uint64 cu_length, - uint8 dwarf_version) { - current_compilation_unit_offset_ = offset; - return true; -} - - -// For function info, we only care about subprograms and inlined -// subroutines. For line info, the DW_AT_stmt_list lives in the -// compile unit tag. - -bool CUFunctionInfoHandler::StartDIE(uint64 offset, enum DwarfTag tag, - const AttributeList& attrs) { - switch (tag) { - case DW_TAG_subprogram: - case DW_TAG_inlined_subroutine: { - current_function_info_ = new FunctionInfo; - current_function_info_->lowpc = current_function_info_->highpc = 0; - current_function_info_->name = ""; - current_function_info_->line = 0; - current_function_info_->file = ""; - offset_to_funcinfo_->insert(make_pair(offset, current_function_info_)); - }; - // FALLTHROUGH - case DW_TAG_compile_unit: - return true; - default: - return false; - } - return false; -} - -// Only care about the name attribute for functions - -void CUFunctionInfoHandler::ProcessAttributeString(uint64 offset, - enum DwarfAttribute attr, - enum DwarfForm form, - const string &data) { - if (current_function_info_) { - if (attr == DW_AT_name) - current_function_info_->name = data; - else if(attr == DW_AT_MIPS_linkage_name) - current_function_info_->mangled_name = data; - } -} - -void CUFunctionInfoHandler::ProcessAttributeUnsigned(uint64 offset, - enum DwarfAttribute attr, - enum DwarfForm form, - uint64 data) { - if (attr == DW_AT_stmt_list) { - SectionMap::const_iterator iter = sections_.find("__debug_line"); - assert(iter != sections_.end()); - - // this should be a scoped_ptr but we dont' use boost :-( - auto_ptr lireader(new LineInfo(iter->second.first + data, - iter->second.second - data, - reader_, linehandler_)); - lireader->Start(); - } else if (current_function_info_) { - switch (attr) { - case DW_AT_low_pc: - current_function_info_->lowpc = data; - break; - case DW_AT_high_pc: - current_function_info_->highpc = data; - break; - case DW_AT_decl_line: - current_function_info_->line = data; - break; - case DW_AT_decl_file: - current_function_info_->file = files_->at(data).name; - break; - case DW_AT_specification: { - // Some functions have a "specification" attribute - // which means they were defined elsewhere. The name - // attribute is not repeated, and must be taken from - // the specification DIE. Here we'll assume that - // any DIE referenced in this manner will already have - // been seen, but that's not really required by the spec. - uint64 abs_offset = GetAbsoluteOffset(data, form, current_compilation_unit_offset_); - FunctionMap::iterator iter = offset_to_funcinfo_->find(abs_offset); - if (iter != offset_to_funcinfo_->end()) { - current_function_info_->name = iter->second->name; - current_function_info_->mangled_name = iter->second->mangled_name; - } else { - // If you hit this, this code probably needs to be rewritten. - fprintf(stderr, "Error: DW_AT_specification was seen before the referenced DIE! (Looking for DIE at offset %08llx, in DIE at offset %08llx)\n", abs_offset, offset); - } - break; - } - default: - break; - } - } -} - -void CUFunctionInfoHandler::EndDIE(uint64 offset) { - if (current_function_info_ && current_function_info_->lowpc) - address_to_funcinfo_->insert(make_pair(current_function_info_->lowpc, - current_function_info_)); -} - -} // namespace dwarf2reader diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/functioninfo.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,178 +0,0 @@ -// Copyright 2006 Google Inc. All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -// This file contains the definitions for a DWARF2/3 information -// collector that uses the DWARF2/3 reader interface to build a mapping -// of addresses to files, lines, and functions. - -#ifndef COMMON_MAC_DWARF_FUNCTIONINFO_H__ -#define COMMON_MAC_DWARF_FUNCTIONINFO_H__ - -#include -#include -#include -#include - -#include "common/mac/dwarf/dwarf2reader.h" - - -namespace dwarf2reader { - -struct FunctionInfo { - // Name of the function - string name; - // Mangled name of the function - string mangled_name; - // File containing this function - string file; - // Line number for start of function. - uint32 line; - // Beginning address for this function - uint64 lowpc; - // End address for this function. - uint64 highpc; -}; - -struct SourceFileInfo { - // Name of the source file name - string name; - // Low address of source file name - uint64 lowpc; -}; - -typedef map FunctionMap; -typedef map > LineMap; - -// This class is a basic line info handler that fills in the dirs, -// file, and linemap passed into it with the data produced from the -// LineInfoHandler. -class CULineInfoHandler: public LineInfoHandler { - public: - - // - CULineInfoHandler(vector* files, - vector* dirs, - LineMap* linemap); - virtual ~CULineInfoHandler() { } - - // Called when we define a directory. We just place NAME into dirs_ - // at position DIR_NUM. - virtual void DefineDir(const string& name, uint32 dir_num); - - // Called when we define a filename. We just place - // concat(dirs_[DIR_NUM], NAME) into files_ at position FILE_NUM. - virtual void DefineFile(const string& name, int32 file_num, - uint32 dir_num, uint64 mod_time, uint64 length); - - - // Called when the line info reader has a new line, address pair - // ready for us. ADDRESS is the address of the code, FILE_NUM is - // the file number containing the code, LINE_NUM is the line number - // in that file for the code, and COLUMN_NUM is the column number - // the code starts at, if we know it (0 otherwise). - virtual void AddLine(uint64 address, uint32 file_num, uint32 line_num, - uint32 column_num); - - - private: - LineMap* linemap_; - vector* files_; - vector* dirs_; -}; - -class CUFunctionInfoHandler: public Dwarf2Handler { - public: - CUFunctionInfoHandler(vector* files, - vector* dirs, - LineMap* linemap, - FunctionMap* offset_to_funcinfo, - FunctionMap* address_to_funcinfo, - CULineInfoHandler* linehandler, - const SectionMap& sections, - ByteReader* reader) - : files_(files), dirs_(dirs), linemap_(linemap), - offset_to_funcinfo_(offset_to_funcinfo), - address_to_funcinfo_(address_to_funcinfo), - linehandler_(linehandler), sections_(sections), - reader_(reader), current_function_info_(NULL) { } - - virtual ~CUFunctionInfoHandler() { } - - // Start to process a compilation unit at OFFSET from the beginning of the - // debug_info section. We want to see all compilation units, so we - // always return true. - - virtual bool StartCompilationUnit(uint64 offset, uint8 address_size, - uint8 offset_size, uint64 cu_length, - uint8 dwarf_version); - - // Start to process a DIE at OFFSET from the beginning of the - // debug_info section. We only care about function related DIE's. - virtual bool StartDIE(uint64 offset, enum DwarfTag tag, - const AttributeList& attrs); - - // Called when we have an attribute with unsigned data to give to - // our handler. The attribute is for the DIE at OFFSET from the - // beginning of compilation unit, has a name of ATTR, a form of - // FORM, and the actual data of the attribute is in DATA. - virtual void ProcessAttributeUnsigned(uint64 offset, - enum DwarfAttribute attr, - enum DwarfForm form, - uint64 data); - - // Called when we have an attribute with string data to give to - // our handler. The attribute is for the DIE at OFFSET from the - // beginning of compilation unit, has a name of ATTR, a form of - // FORM, and the actual data of the attribute is in DATA. - virtual void ProcessAttributeString(uint64 offset, - enum DwarfAttribute attr, - enum DwarfForm form, - const string& data); - - // Called when finished processing the DIE at OFFSET. - // Because DWARF2/3 specifies a tree of DIEs, you may get starts - // before ends of the previous DIE, as we process children before - // ending the parent. - virtual void EndDIE(uint64 offset); - - private: - vector* files_; - vector* dirs_; - LineMap* linemap_; - FunctionMap* offset_to_funcinfo_; - FunctionMap* address_to_funcinfo_; - CULineInfoHandler* linehandler_; - const SectionMap& sections_; - ByteReader* reader_; - FunctionInfo* current_function_info_; - uint64 current_compilation_unit_offset_; -}; - -} // namespace dwarf2reader -#endif // COMMON_MAC_DWARF_FUNCTIONINFO_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/line_state_machine.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/line_state_machine.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/line_state_machine.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/line_state_machine.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,61 +0,0 @@ -// Copyright 2008 Google Inc. All Rights Reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -#ifndef COMMON_MAC_DWARF_LINE_STATE_MACHINE_H__ -#define COMMON_MAC_DWARF_LINE_STATE_MACHINE_H__ - -namespace dwarf2reader { - -// This is the format of a DWARF2/3 line state machine that we process -// opcodes using. There is no need for anything outside the lineinfo -// processor to know how this works. -struct LineStateMachine { - void Reset(bool default_is_stmt) { - file_num = 1; - address = 0; - line_num = 1; - column_num = 0; - is_stmt = default_is_stmt; - basic_block = false; - end_sequence = false; - } - - uint32 file_num; - uint64 address; - uint64 line_num; - uint32 column_num; - bool is_stmt; // stmt means statement. - bool basic_block; - bool end_sequence; -}; - -} // namespace dwarf2reader - - -#endif // COMMON_MAC_DWARF_LINE_STATE_MACHINE_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/Makefile.in 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/Makefile.in 1970-01-01 01:00:00.000000000 +0100 @@ -1,61 +0,0 @@ -# ***** BEGIN LICENSE BLOCK ***** -# Version: MPL 1.1/GPL 2.0/LGPL 2.1 -# -# The contents of this file are subject to the Mozilla Public License Version -# 1.1 (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.mozilla.org/MPL/ -# -# Software distributed under the License is distributed on an "AS IS" basis, -# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -# for the specific language governing rights and limitations under the -# License. -# -# The Original Code is Mozilla Breakpad integration -# -# The Initial Developer of the Original Code is -# The Mozilla Foundation -# Portions created by the Initial Developer are Copyright (C) 2008 -# the Initial Developer. All Rights Reserved. -# -# Contributor(s): -# Ted Mielczarek -# -# Alternatively, the contents of this file may be used under the terms of -# either the GNU General Public License Version 2 or later (the "GPL"), or -# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -# in which case the provisions of the GPL or the LGPL are applicable instead -# of those above. If you wish to allow use of your version of this file only -# under the terms of either the GPL or the LGPL, and not to allow others to -# use your version of this file under the terms of the MPL, indicate your -# decision by deleting the provisions above and replace them with the notice -# and other provisions required by the GPL or the LGPL. If you do not delete -# the provisions above, a recipient may use your version of this file under -# the terms of any one of the MPL, the GPL or the LGPL. -# -# ***** END LICENSE BLOCK ***** - -DEPTH = ../../../../../../.. -topsrcdir = @top_srcdir@ -srcdir = @srcdir@ -VPATH = @srcdir@ - -include $(DEPTH)/config/autoconf.mk - -MODULE = breakpad_mac_dwarf -HOST_LIBRARY_NAME = host_breakpad_mac_dwarf_s - -LOCAL_INCLUDES = -I$(srcdir)/../../.. - -HOST_CPPSRCS = \ - bytereader.cc \ - dwarf2reader.cc \ - functioninfo.cc \ - $(NULL) - -HOST_CXXFLAGS += -funsigned-char - -# need static lib -FORCE_STATIC_LIB = 1 - -include $(topsrcdir)/config/rules.mk diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/types.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/types.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/types.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/types.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,46 +0,0 @@ -// Copyright 2008 Google, Inc. All Rights reserved -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -// This file contains some typedefs for basic types - - -#ifndef _COMMON_MAC_DWARF_TYPES_H__ -#define _COMMON_MAC_DWARF_TYPES_H__ - -typedef signed char int8; -typedef short int16; -typedef int int32; -typedef long long int64; - -typedef unsigned char uint8; -typedef unsigned short uint16; -typedef unsigned int uint32; -typedef unsigned long long uint64; - -#endif // _COMMON_MAC_DWARF_TYPES_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/GTMDefines.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/GTMDefines.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/GTMDefines.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/GTMDefines.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,241 @@ +// +// GTMDefines.h +// +// Copyright 2008 Google Inc. +// +// 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 +#include + +// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs +#ifndef MAC_OS_X_VERSION_10_5 + #define MAC_OS_X_VERSION_10_5 1050 +#endif +#ifndef MAC_OS_X_VERSION_10_6 + #define MAC_OS_X_VERSION_10_6 1060 +#endif + +// ---------------------------------------------------------------------------- +// CPP symbols that can be overridden in a prefix to control how the toolbox +// is compiled. +// ---------------------------------------------------------------------------- + + +// By setting the GTM_CONTAINERS_VALIDATION_FAILED_LOG and +// GTM_CONTAINERS_VALIDATION_FAILED_ASSERT macros you can control what happens +// when a validation fails. If you implement your own validators, you may want +// to control their internals using the same macros for consistency. +#ifndef GTM_CONTAINERS_VALIDATION_FAILED_ASSERT + #define GTM_CONTAINERS_VALIDATION_FAILED_ASSERT 0 +#endif + +// Give ourselves a consistent way to do inlines. Apple's macros even use +// a few different actual definitions, so we're based off of the foundation +// one. +#if !defined(GTM_INLINE) + #if defined (__GNUC__) && (__GNUC__ == 4) + #define GTM_INLINE static __inline__ __attribute__((always_inline)) + #else + #define GTM_INLINE static __inline__ + #endif +#endif + +// Give ourselves a consistent way of doing externs that links up nicely +// when mixing objc and objc++ +#if !defined (GTM_EXTERN) + #if defined __cplusplus + #define GTM_EXTERN extern "C" + #else + #define GTM_EXTERN extern + #endif +#endif + +// Give ourselves a consistent way of exporting things if we have visibility +// set to hidden. +#if !defined (GTM_EXPORT) + #define GTM_EXPORT __attribute__((visibility("default"))) +#endif + +// _GTMDevLog & _GTMDevAssert +// +// _GTMDevLog & _GTMDevAssert are meant to be a very lightweight shell for +// developer level errors. This implementation simply macros to NSLog/NSAssert. +// It is not intended to be a general logging/reporting system. +// +// Please see http://code.google.com/p/google-toolbox-for-mac/wiki/DevLogNAssert +// for a little more background on the usage of these macros. +// +// _GTMDevLog log some error/problem in debug builds +// _GTMDevAssert assert if conditon isn't met w/in a method/function +// in all builds. +// +// To replace this system, just provide different macro definitions in your +// prefix header. Remember, any implementation you provide *must* be thread +// safe since this could be called by anything in what ever situtation it has +// been placed in. +// + +// We only define the simple macros if nothing else has defined this. +#ifndef _GTMDevLog + +#ifdef DEBUG + #define _GTMDevLog(...) NSLog(__VA_ARGS__) +#else + #define _GTMDevLog(...) do { } while (0) +#endif + +#endif // _GTMDevLog + +// Declared here so that it can easily be used for logging tracking if +// necessary. See GTMUnitTestDevLog.h for details. +@class NSString; +GTM_EXTERN void _GTMUnitTestDevLog(NSString *format, ...); + +#ifndef _GTMDevAssert +// we directly invoke the NSAssert handler so we can pass on the varargs +// (NSAssert doesn't have a macro we can use that takes varargs) +#if !defined(NS_BLOCK_ASSERTIONS) + #define _GTMDevAssert(condition, ...) \ + do { \ + if (!(condition)) { \ + [[NSAssertionHandler currentHandler] \ + handleFailureInFunction:[NSString stringWithUTF8String:__PRETTY_FUNCTION__] \ + file:[NSString stringWithUTF8String:__FILE__] \ + lineNumber:__LINE__ \ + description:__VA_ARGS__]; \ + } \ + } while(0) +#else // !defined(NS_BLOCK_ASSERTIONS) + #define _GTMDevAssert(condition, ...) do { } while (0) +#endif // !defined(NS_BLOCK_ASSERTIONS) + +#endif // _GTMDevAssert + +// _GTMCompileAssert +// _GTMCompileAssert is an assert that is meant to fire at compile time if you +// want to check things at compile instead of runtime. For example if you +// want to check that a wchar is 4 bytes instead of 2 you would use +// _GTMCompileAssert(sizeof(wchar_t) == 4, wchar_t_is_4_bytes_on_OS_X) +// Note that the second "arg" is not in quotes, and must be a valid processor +// symbol in it's own right (no spaces, punctuation etc). + +// Wrapping this in an #ifndef allows external groups to define their own +// compile time assert scheme. +#ifndef _GTMCompileAssert + // We got this technique from here: + // http://unixjunkie.blogspot.com/2007/10/better-compile-time-asserts_29.html + + #define _GTMCompileAssertSymbolInner(line, msg) _GTMCOMPILEASSERT ## line ## __ ## msg + #define _GTMCompileAssertSymbol(line, msg) _GTMCompileAssertSymbolInner(line, msg) + #define _GTMCompileAssert(test, msg) \ + typedef char _GTMCompileAssertSymbol(__LINE__, msg) [ ((test) ? 1 : -1) ] +#endif // _GTMCompileAssert + +// Macro to allow fast enumeration when building for 10.5 or later, and +// reliance on NSEnumerator for 10.4. Remember, NSDictionary w/ FastEnumeration +// does keys, so pick the right thing, nothing is done on the FastEnumeration +// side to be sure you're getting what you wanted. +#ifndef GTM_FOREACH_OBJECT + #if TARGET_OS_IPHONE || (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) + #define GTM_FOREACH_OBJECT(element, collection) \ + for (element in collection) + #define GTM_FOREACH_KEY(element, collection) \ + for (element in collection) + #else + #define GTM_FOREACH_OBJECT(element, collection) \ + for (NSEnumerator * _ ## element ## _enum = [collection objectEnumerator]; \ + (element = [_ ## element ## _enum nextObject]) != nil; ) + #define GTM_FOREACH_KEY(element, collection) \ + for (NSEnumerator * _ ## element ## _enum = [collection keyEnumerator]; \ + (element = [_ ## element ## _enum nextObject]) != nil; ) + #endif +#endif + +// ============================================================================ + +// ---------------------------------------------------------------------------- +// CPP symbols defined based on the project settings so the GTM code has +// simple things to test against w/o scattering the knowledge of project +// setting through all the code. +// ---------------------------------------------------------------------------- + +// Provide a single constant CPP symbol that all of GTM uses for ifdefing +// iPhone code. +#if TARGET_OS_IPHONE // iPhone SDK + // For iPhone specific stuff + #define GTM_IPHONE_SDK 1 + #if TARGET_IPHONE_SIMULATOR + #define GTM_IPHONE_SIMULATOR 1 + #else + #define GTM_IPHONE_DEVICE 1 + #endif // TARGET_IPHONE_SIMULATOR +#else + // For MacOS specific stuff + #define GTM_MACOS_SDK 1 +#endif + +// Provide a symbol to include/exclude extra code for GC support. (This mainly +// just controls the inclusion of finalize methods). +#ifndef GTM_SUPPORT_GC + #if GTM_IPHONE_SDK + // iPhone never needs GC + #define GTM_SUPPORT_GC 0 + #else + // We can't find a symbol to tell if GC is supported/required, so best we + // do on Mac targets is include it if we're on 10.5 or later. + #if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 + #define GTM_SUPPORT_GC 0 + #else + #define GTM_SUPPORT_GC 1 + #endif + #endif +#endif + +// To simplify support for 64bit (and Leopard in general), we provide the type +// defines for non Leopard SDKs +#if MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 + // NSInteger/NSUInteger and Max/Mins + #ifndef NSINTEGER_DEFINED + #if __LP64__ || NS_BUILD_32_LIKE_64 + typedef long NSInteger; + typedef unsigned long NSUInteger; + #else + typedef int NSInteger; + typedef unsigned int NSUInteger; + #endif + #define NSIntegerMax LONG_MAX + #define NSIntegerMin LONG_MIN + #define NSUIntegerMax ULONG_MAX + #define NSINTEGER_DEFINED 1 + #endif // NSINTEGER_DEFINED + // CGFloat + #ifndef CGFLOAT_DEFINED + #if defined(__LP64__) && __LP64__ + // This really is an untested path (64bit on Tiger?) + typedef double CGFloat; + #define CGFLOAT_MIN DBL_MIN + #define CGFLOAT_MAX DBL_MAX + #define CGFLOAT_IS_DOUBLE 1 + #else /* !defined(__LP64__) || !__LP64__ */ + typedef float CGFloat; + #define CGFLOAT_MIN FLT_MIN + #define CGFLOAT_MAX FLT_MAX + #define CGFLOAT_IS_DOUBLE 0 + #endif /* !defined(__LP64__) || !__LP64__ */ + #define CGFLOAT_DEFINED 1 + #endif // CGFLOAT_DEFINED +#endif // MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_4 diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/GTMGarbageCollection.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/GTMGarbageCollection.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/GTMGarbageCollection.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/GTMGarbageCollection.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,72 @@ +// +// GTMGarbageCollection.h +// +// Copyright 2007-2008 Google Inc. +// +// 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. +// + +#import + +#import "GTMDefines.h" + +// This allows us to easily move our code from GC to non GC. +// They are no-ops unless we are require Leopard or above. +// See +// http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection/index.html +// and +// http://developer.apple.com/documentation/Cocoa/Conceptual/GarbageCollection/Articles/gcCoreFoundation.html#//apple_ref/doc/uid/TP40006687-SW1 +// for details. + +#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5) && !GTM_IPHONE_SDK +// General use would be to call this through GTMCFAutorelease +// but there may be a reason the you want to make something collectable +// but not autoreleased, especially in pure GC code where you don't +// want to bother with the nop autorelease. Done as a define instead of an +// inline so that tools like Clang's scan-build don't report code as leaking. +#define GTMNSMakeCollectable(cf) ((id)NSMakeCollectable(cf)) + +// GTMNSMakeUncollectable is for global maps, etc. that we don't +// want released ever. You should still retain these in non-gc code. +GTM_INLINE void GTMNSMakeUncollectable(id object) { + [[NSGarbageCollector defaultCollector] disableCollectorForPointer:object]; +} + +// Hopefully no code really needs this, but GTMIsGarbageCollectionEnabled is +// a common way to check at runtime if GC is on. +// There are some places where GC doesn't work w/ things w/in Apple's +// frameworks, so this is here so GTM unittests and detect it, and not run +// individual tests to work around bugs in Apple's frameworks. +GTM_INLINE BOOL GTMIsGarbageCollectionEnabled(void) { + return ([NSGarbageCollector defaultCollector] != nil); +} + +#else + +#define GTMNSMakeCollectable(cf) ((id)(cf)) + +GTM_INLINE void GTMNSMakeUncollectable(id object) { +} + +GTM_INLINE BOOL GTMIsGarbageCollectionEnabled(void) { + return NO; +} + +#endif + +// GTMCFAutorelease makes a CF object collectable in GC mode, or adds it +// to the autorelease pool in non-GC mode. Either way it is taken care +// of. Done as a define instead of an inline so that tools like Clang's +// scan-build don't report code as leaking. +#define GTMCFAutorelease(cf) ([GTMNSMakeCollectable(cf) autorelease]) + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/GTMLogger.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/GTMLogger.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/GTMLogger.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/GTMLogger.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,458 @@ +// +// GTMLogger.h +// +// Copyright 2007-2008 Google Inc. +// +// 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. +// + +// Key Abstractions +// ---------------- +// +// This file declares multiple classes and protocols that are used by the +// GTMLogger logging system. The 4 main abstractions used in this file are the +// following: +// +// * logger (GTMLogger) - The main logging class that users interact with. It +// has methods for logging at different levels and uses a log writer, a log +// formatter, and a log filter to get the job done. +// +// * log writer (GTMLogWriter) - Writes a given string to some log file, where +// a "log file" can be a physical file on disk, a POST over HTTP to some URL, +// or even some in-memory structure (e.g., a ring buffer). +// +// * log formatter (GTMLogFormatter) - Given a format string and arguments as +// a va_list, returns a single formatted NSString. A "formatted string" could +// be a string with the date prepended, a string with values in a CSV format, +// or even a string of XML. +// +// * log filter (GTMLogFilter) - Given a formatted log message as an NSString +// and the level at which the message is to be logged, this class will decide +// whether the given message should be logged or not. This is a flexible way +// to filter out messages logged at a certain level, messages that contain +// certain text, or filter nothing out at all. This gives the caller the +// flexibility to dynamically enable debug logging in Release builds. +// +// This file also declares some classes to handle the common log writer, log +// formatter, and log filter cases. Callers can also create their own writers, +// formatters, and filters and they can even build them on top of the ones +// declared here. Keep in mind that your custom writer/formatter/filter may be +// called from multiple threads, so it must be thread-safe. + +#import +#import "GTMDefines.h" + +// Predeclaration of used protocols that are declared later in this file. +@protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter; + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 +#define CHECK_FORMAT_NSSTRING(a, b) __attribute__((format(__NSString__, a, b))) +#else +#define CHECK_FORMAT_NSSTRING(a, b) +#endif + +// GTMLogger +// +// GTMLogger is the primary user-facing class for an object-oriented logging +// system. It is built on the concept of log formatters (GTMLogFormatter), log +// writers (GTMLogWriter), and log filters (GTMLogFilter). When a message is +// sent to a GTMLogger to log a message, the message is formatted using the log +// formatter, then the log filter is consulted to see if the message should be +// logged, and if so, the message is sent to the log writer to be written out. +// +// GTMLogger is intended to be a flexible and thread-safe logging solution. Its +// flexibility comes from the fact that GTMLogger instances can be customized +// with user defined formatters, filters, and writers. And these writers, +// filters, and formatters can be combined, stacked, and customized in arbitrary +// ways to suit the needs at hand. For example, multiple writers can be used at +// the same time, and a GTMLogger instance can even be used as another +// GTMLogger's writer. This allows for arbitrarily deep logging trees. +// +// A standard GTMLogger uses a writer that sends messages to standard out, a +// formatter that smacks a timestamp and a few other bits of interesting +// information on the message, and a filter that filters out debug messages from +// release builds. Using the standard log settings, a log message will look like +// the following: +// +// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] foo= +// +// The output contains the date and time of the log message, the name of the +// process followed by its process ID/thread ID, the log level at which the +// message was logged (in the previous example the level was 1: +// kGTMLoggerLevelDebug), and finally, the user-specified log message itself (in +// this case, the log message was @"foo=%@", foo). +// +// Multiple instances of GTMLogger can be created, each configured their own +// way. Though GTMLogger is not a singleton (in the GoF sense), it does provide +// access to a shared (i.e., globally accessible) GTMLogger instance. This makes +// it convenient for all code in a process to use the same GTMLogger instance. +// The shared GTMLogger instance can also be configured in an arbitrary, and +// these configuration changes will affect all code that logs through the shared +// instance. + +// +// Log Levels +// ---------- +// GTMLogger has 3 different log levels: Debug, Info, and Error. GTMLogger +// doesn't take any special action based on the log level; it simply forwards +// this information on to formatters, filters, and writers, each of which may +// optionally take action based on the level. Since log level filtering is +// performed at runtime, log messages are typically not filtered out at compile +// time. The exception to this rule is that calls to the GTMLoggerDebug() macro +// *ARE* filtered out of non-DEBUG builds. This is to be backwards compatible +// with behavior that many developers are currently used to. Note that this +// means that GTMLoggerDebug(@"hi") will be compiled out of Release builds, but +// [[GTMLogger sharedLogger] logDebug:@"hi"] will NOT be compiled out. +// +// Standard loggers are created with the GTMLogLevelFilter log filter, which +// filters out certain log messages based on log level, and some other settings. +// +// In addition to the -logDebug:, -logInfo:, and -logError: methods defined on +// GTMLogger itself, there are also C macros that make usage of the shared +// GTMLogger instance very convenient. These macros are: +// +// GTMLoggerDebug(...) +// GTMLoggerInfo(...) +// GTMLoggerError(...) +// +// Again, a notable feature of these macros is that GTMLogDebug() calls *will be +// compiled out of non-DEBUG builds*. +// +// Standard Loggers +// ---------------- +// GTMLogger has the concept of "standard loggers". A standard logger is simply +// a logger that is pre-configured with some standard/common writer, formatter, +// and filter combination. Standard loggers are created using the creation +// methods beginning with "standard". The alternative to a standard logger is a +// regular logger, which will send messages to stdout, with no special +// formatting, and no filtering. +// +// How do I use GTMLogger? +// ---------------------- +// The typical way you will want to use GTMLogger is to simply use the +// GTMLogger*() macros for logging from code. That way we can easily make +// changes to the GTMLogger class and simply update the macros accordingly. Only +// your application startup code (perhaps, somewhere in main()) should use the +// GTMLogger class directly in order to configure the shared logger, which all +// of the code using the macros will be using. Again, this is just the typical +// situation. +// +// To be complete, there are cases where you may want to use GTMLogger directly, +// or even create separate GTMLogger instances for some reason. That's fine, +// too. +// +// Examples +// -------- +// The following show some common GTMLogger use cases. +// +// 1. You want to log something as simply as possible. Also, this call will only +// appear in debug builds. In non-DEBUG builds it will be completely removed. +// +// GTMLoggerDebug(@"foo = %@", foo); +// +// 2. The previous example is similar to the following. The major difference is +// that the previous call (example 1) will be compiled out of Release builds +// but this statement will not be compiled out. +// +// [[GTMLogger sharedLogger] logDebug:@"foo = %@", foo]; +// +// 3. Send all logging output from the shared logger to a file. We do this by +// creating an NSFileHandle for writing associated with a file, and setting +// that file handle as the logger's writer. +// +// NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" +// create:YES]; +// [[GTMLogger sharedLogger] setWriter:f]; +// GTMLoggerError(@"hi"); // This will be sent to /tmp/f.log +// +// 4. Create a new GTMLogger that will log to a file. This example differs from +// the previous one because here we create a new GTMLogger that is different +// from the shared logger. +// +// GTMLogger *logger = [GTMLogger standardLoggerWithPath:@"/tmp/temp.log"]; +// [logger logInfo:@"hi temp log file"]; +// +// 5. Create a logger that writes to stdout and does NOT do any formatting to +// the log message. This might be useful, for example, when writing a help +// screen for a command-line tool to standard output. +// +// GTMLogger *logger = [GTMLogger logger]; +// [logger logInfo:@"%@ version 0.1 usage", progName]; +// +// 6. Send log output to stdout AND to a log file. The trick here is that +// NSArrays function as composite log writers, which means when an array is +// set as the log writer, it forwards all logging messages to all of its +// contained GTMLogWriters. +// +// // Create array of GTMLogWriters +// NSArray *writers = [NSArray arrayWithObjects: +// [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" create:YES], +// [NSFileHandle fileHandleWithStandardOutput], nil]; +// +// GTMLogger *logger = [GTMLogger standardLogger]; +// [logger setWriter:writers]; +// [logger logInfo:@"hi"]; // Output goes to stdout and /tmp/f.log +// +// For futher details on log writers, formatters, and filters, see the +// documentation below. +// +// NOTE: GTMLogger is application level logging. By default it does nothing +// with _GTMDevLog/_GTMDevAssert (see GTMDefines.h). An application can choose +// to bridge _GTMDevLog/_GTMDevAssert to GTMLogger by providing macro +// definitions in its prefix header (see GTMDefines.h for how one would do +// that). +// +@interface GTMLogger : NSObject { + @private + id writer_; + id formatter_; + id filter_; +} + +// +// Accessors for the shared logger instance +// + +// Returns a shared/global standard GTMLogger instance. Callers should typically +// use this method to get a GTMLogger instance, unless they explicitly want +// their own instance to configure for their own needs. This is the only method +// that returns a shared instance; all the rest return new GTMLogger instances. ++ (id)sharedLogger; + +// Sets the shared logger instance to |logger|. Future calls to +sharedLogger +// will return |logger| instead. ++ (void)setSharedLogger:(GTMLogger *)logger; + +// +// Creation methods +// + +// Returns a new autoreleased GTMLogger instance that will log to stdout, using +// the GTMLogStandardFormatter, and the GTMLogLevelFilter filter. ++ (id)standardLogger; + +// Same as +standardLogger, but logs to stderr. ++ (id)standardLoggerWithStderr; + +// Returns a new standard GTMLogger instance with a log writer that will +// write to the file at |path|, and will use the GTMLogStandardFormatter and +// GTMLogLevelFilter classes. If |path| does not exist, it will be created. ++ (id)standardLoggerWithPath:(NSString *)path; + +// Returns an autoreleased GTMLogger instance that will use the specified +// |writer|, |formatter|, and |filter|. ++ (id)loggerWithWriter:(id)writer + formatter:(id)formatter + filter:(id)filter; + +// Returns an autoreleased GTMLogger instance that logs to stdout, with the +// basic formatter, and no filter. The returned logger differs from the logger +// returned by +standardLogger because this one does not do any filtering and +// does not do any special log formatting; this is the difference between a +// "regular" logger and a "standard" logger. ++ (id)logger; + +// Designated initializer. This method returns a GTMLogger initialized with the +// specified |writer|, |formatter|, and |filter|. See the setter methods below +// for what values will be used if nil is passed for a parameter. +- (id)initWithWriter:(id)writer + formatter:(id)formatter + filter:(id)filter; + +// +// Logging methods +// + +// Logs a message at the debug level (kGTMLoggerLevelDebug). +- (void)logDebug:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2); +// Logs a message at the info level (kGTMLoggerLevelInfo). +- (void)logInfo:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2); +// Logs a message at the error level (kGTMLoggerLevelError). +- (void)logError:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2); +// Logs a message at the assert level (kGTMLoggerLevelAssert). +- (void)logAssert:(NSString *)fmt, ... CHECK_FORMAT_NSSTRING(1, 2); + + +// +// Accessors +// + +// Accessor methods for the log writer. If the log writer is set to nil, +// [NSFileHandle fileHandleWithStandardOutput] is used. +- (id)writer; +- (void)setWriter:(id)writer; + +// Accessor methods for the log formatter. If the log formatter is set to nil, +// GTMLogBasicFormatter is used. This formatter will format log messages in a +// plain printf style. +- (id)formatter; +- (void)setFormatter:(id)formatter; + +// Accessor methods for the log filter. If the log filter is set to nil, +// GTMLogNoFilter is used, which allows all log messages through. +- (id)filter; +- (void)setFilter:(id)filter; + +@end // GTMLogger + + +// Helper functions that are used by the convenience GTMLogger*() macros that +// enable the logging of function names. +@interface GTMLogger (GTMLoggerMacroHelpers) +- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... + CHECK_FORMAT_NSSTRING(2, 3); +- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... + CHECK_FORMAT_NSSTRING(2, 3); +- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... + CHECK_FORMAT_NSSTRING(2, 3); +- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... + CHECK_FORMAT_NSSTRING(2, 3); +@end // GTMLoggerMacroHelpers + + +// Convenience macros that log to the shared GTMLogger instance. These macros +// are how users should typically log to GTMLogger. Notice that GTMLoggerDebug() +// calls will be compiled out of non-Debug builds. +#define GTMLoggerDebug(...) \ + [[GTMLogger sharedLogger] logFuncDebug:__func__ msg:__VA_ARGS__] +#define GTMLoggerInfo(...) \ + [[GTMLogger sharedLogger] logFuncInfo:__func__ msg:__VA_ARGS__] +#define GTMLoggerError(...) \ + [[GTMLogger sharedLogger] logFuncError:__func__ msg:__VA_ARGS__] +#define GTMLoggerAssert(...) \ + [[GTMLogger sharedLogger] logFuncAssert:__func__ msg:__VA_ARGS__] + +// If we're not in a debug build, remove the GTMLoggerDebug statements. This +// makes calls to GTMLoggerDebug "compile out" of Release builds +#ifndef DEBUG +#undef GTMLoggerDebug +#define GTMLoggerDebug(...) do {} while(0) +#endif + +// Log levels. +typedef enum { + kGTMLoggerLevelUnknown, + kGTMLoggerLevelDebug, + kGTMLoggerLevelInfo, + kGTMLoggerLevelError, + kGTMLoggerLevelAssert, +} GTMLoggerLevel; + + +// +// Log Writers +// + +// Protocol to be implemented by a GTMLogWriter instance. +@protocol GTMLogWriter +// Writes the given log message to where the log writer is configured to write. +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level; +@end // GTMLogWriter + + +// Simple category on NSFileHandle that makes NSFileHandles valid log writers. +// This is convenient because something like, say, +fileHandleWithStandardError +// now becomes a valid log writer. Log messages are written to the file handle +// with a newline appended. +@interface NSFileHandle (GTMFileHandleLogWriter) +// Opens the file at |path| in append mode, and creates the file with |mode| +// if it didn't previously exist. ++ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode; +@end // NSFileHandle + + +// This category makes NSArray a GTMLogWriter that can be composed of other +// GTMLogWriters. This is the classic Composite GoF design pattern. When the +// GTMLogWriter -logMessage:level: message is sent to the array, the array +// forwards the message to all of its elements that implement the GTMLogWriter +// protocol. +// +// This is useful in situations where you would like to send log output to +// multiple log writers at the same time. Simply create an NSArray of the log +// writers you wish to use, then set the array as the "writer" for your +// GTMLogger instance. +@interface NSArray (GTMArrayCompositeLogWriter) +@end // GTMArrayCompositeLogWriter + + +// This category adapts the GTMLogger interface so that it can be used as a log +// writer; it's an "adapter" in the GoF Adapter pattern sense. +// +// This is useful when you want to configure a logger to log to a specific +// writer with a specific formatter and/or filter. But you want to also compose +// that with a different log writer that may have its own formatter and/or +// filter. +@interface GTMLogger (GTMLoggerLogWriter) +@end // GTMLoggerLogWriter + + +// +// Log Formatters +// + +// Protocol to be implemented by a GTMLogFormatter instance. +@protocol GTMLogFormatter +// Returns a formatted string using the format specified in |fmt| and the va +// args specified in |args|. +- (NSString *)stringForFunc:(NSString *)func + withFormat:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level; +@end // GTMLogFormatter + + +// A basic log formatter that formats a string the same way that NSLog (or +// printf) would. It does not do anything fancy, nor does it add any data of its +// own. +@interface GTMLogBasicFormatter : NSObject +@end // GTMLogBasicFormatter + + +// A log formatter that formats the log string like the basic formatter, but +// also prepends a timestamp and some basic process info to the message, as +// shown in the following sample output. +// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] log mesage here +@interface GTMLogStandardFormatter : GTMLogBasicFormatter { + @private + NSDateFormatter *dateFormatter_; // yyyy-MM-dd HH:mm:ss.SSS + NSString *pname_; + pid_t pid_; +} +@end // GTMLogStandardFormatter + + +// +// Log Filters +// + +// Protocol to be imlemented by a GTMLogFilter instance. +@protocol GTMLogFilter +// Returns YES if |msg| at |level| should be filtered out; NO otherwise. +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level; +@end // GTMLogFilter + + +// A log filter that filters messages at the kGTMLoggerLevelDebug level out of +// non-debug builds. Messages at the kGTMLoggerLevelInfo level are also filtered +// out of non-debug builds unless GTMVerboseLogging is set in the environment or +// the processes's defaults. Messages at the kGTMLoggerLevelError level are +// never filtered. +@interface GTMLogLevelFilter : NSObject +@end // GTMLogLevelFilter + + +// A simple log filter that does NOT filter anything out; +// -filterAllowsMessage:level will always return YES. This can be a convenient +// way to enable debug-level logging in release builds (if you so desire). +@interface GTMLogNoFilter : NSObject +@end // GTMLogNoFilter diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/GTMLogger.m firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/GTMLogger.m --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/GTMLogger.m 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/GTMLogger.m 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,445 @@ +// +// GTMLogger.m +// +// Copyright 2007-2008 Google Inc. +// +// 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. +// + +#import "GTMLogger.h" +#import "GTMGarbageCollection.h" +#import +#import +#import +#import + + +// Define a trivial assertion macro to avoid dependencies +#ifdef DEBUG + #define GTMLOGGER_ASSERT(expr) assert(expr) +#else + #define GTMLOGGER_ASSERT(expr) +#endif + + +@interface GTMLogger (PrivateMethods) + +- (void)logInternalFunc:(const char *)func + format:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level; + +@end + + +// Reference to the shared GTMLogger instance. This is not a singleton, it's +// just an easy reference to one shared instance. +static GTMLogger *gSharedLogger = nil; + + +@implementation GTMLogger + +// Returns a pointer to the shared logger instance. If none exists, a standard +// logger is created and returned. ++ (id)sharedLogger { + @synchronized(self) { + if (gSharedLogger == nil) { + gSharedLogger = [[self standardLogger] retain]; + } + GTMLOGGER_ASSERT(gSharedLogger != nil); + } + return [[gSharedLogger retain] autorelease]; +} + ++ (void)setSharedLogger:(GTMLogger *)logger { + @synchronized(self) { + [gSharedLogger autorelease]; + gSharedLogger = [logger retain]; + } +} + ++ (id)standardLogger { + id writer = [NSFileHandle fileHandleWithStandardOutput]; + id fr = [[[GTMLogStandardFormatter alloc] init] autorelease]; + id filter = [[[GTMLogLevelFilter alloc] init] autorelease]; + return [self loggerWithWriter:writer formatter:fr filter:filter]; +} + ++ (id)standardLoggerWithStderr { + id me = [self standardLogger]; + [me setWriter:[NSFileHandle fileHandleWithStandardError]]; + return me; +} + ++ (id)standardLoggerWithPath:(NSString *)path { + NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644]; + if (fh == nil) return nil; + id me = [self standardLogger]; + [me setWriter:fh]; + return me; +} + ++ (id)loggerWithWriter:(id)writer + formatter:(id)formatter + filter:(id)filter { + return [[[self alloc] initWithWriter:writer + formatter:formatter + filter:filter] autorelease]; +} + ++ (id)logger { + return [[[self alloc] init] autorelease]; +} + +- (id)init { + return [self initWithWriter:nil formatter:nil filter:nil]; +} + +- (id)initWithWriter:(id)writer + formatter:(id)formatter + filter:(id)filter { + if ((self = [super init])) { + [self setWriter:writer]; + [self setFormatter:formatter]; + [self setFilter:filter]; + GTMLOGGER_ASSERT(formatter_ != nil); + GTMLOGGER_ASSERT(filter_ != nil); + GTMLOGGER_ASSERT(writer_ != nil); + } + return self; +} + +- (void)dealloc { + GTMLOGGER_ASSERT(writer_ != nil); + GTMLOGGER_ASSERT(formatter_ != nil); + GTMLOGGER_ASSERT(filter_ != nil); + [writer_ release]; + [formatter_ release]; + [filter_ release]; + [super dealloc]; +} + +- (id)writer { + GTMLOGGER_ASSERT(writer_ != nil); + return [[writer_ retain] autorelease]; +} + +- (void)setWriter:(id)writer { + @synchronized(self) { + [writer_ autorelease]; + if (writer == nil) + writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain]; + else + writer_ = [writer retain]; + } + GTMLOGGER_ASSERT(writer_ != nil); +} + +- (id)formatter { + GTMLOGGER_ASSERT(formatter_ != nil); + return [[formatter_ retain] autorelease]; +} + +- (void)setFormatter:(id)formatter { + @synchronized(self) { + [formatter_ autorelease]; + if (formatter == nil) + formatter_ = [[GTMLogBasicFormatter alloc] init]; + else + formatter_ = [formatter retain]; + } + GTMLOGGER_ASSERT(formatter_ != nil); +} + +- (id)filter { + GTMLOGGER_ASSERT(filter_ != nil); + return [[filter_ retain] autorelease]; +} + +- (void)setFilter:(id)filter { + @synchronized(self) { + [filter_ autorelease]; + if (filter == nil) + filter_ = [[GTMLogNoFilter alloc] init]; + else + filter_ = [filter retain]; + } + GTMLOGGER_ASSERT(filter_ != nil); +} + +- (void)logDebug:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelDebug]; + va_end(args); +} + +- (void)logInfo:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelInfo]; + va_end(args); +} + +- (void)logError:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelError]; + va_end(args); +} + +- (void)logAssert:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelAssert]; + va_end(args); +} + +@end // GTMLogger + + +@implementation GTMLogger (GTMLoggerMacroHelpers) + +- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelDebug]; + va_end(args); +} + +- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelInfo]; + va_end(args); +} + +- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelError]; + va_end(args); +} + +- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... { + va_list args; + va_start(args, fmt); + [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelAssert]; + va_end(args); +} + +@end // GTMLoggerMacroHelpers + + +@implementation GTMLogger (PrivateMethods) + +- (void)logInternalFunc:(const char *)func + format:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level { + GTMLOGGER_ASSERT(formatter_ != nil); + GTMLOGGER_ASSERT(filter_ != nil); + GTMLOGGER_ASSERT(writer_ != nil); + + NSString *fname = func ? [NSString stringWithUTF8String:func] : nil; + NSString *msg = [formatter_ stringForFunc:fname + withFormat:fmt + valist:args + level:level]; + if (msg && [filter_ filterAllowsMessage:msg level:level]) + [writer_ logMessage:msg level:level]; +} + +@end // PrivateMethods + + +@implementation NSFileHandle (GTMFileHandleLogWriter) + ++ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode { + int fd = -1; + if (path) { + int flags = O_WRONLY | O_APPEND | O_CREAT; + fd = open([path fileSystemRepresentation], flags, mode); + } + if (fd == -1) return nil; + return [[[self alloc] initWithFileDescriptor:fd + closeOnDealloc:YES] autorelease]; +} + +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { + @synchronized(self) { + NSString *line = [NSString stringWithFormat:@"%@\n", msg]; + [self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]]; + } +} + +@end // GTMFileHandleLogWriter + + +@implementation NSArray (GTMArrayCompositeLogWriter) + +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { + @synchronized(self) { + id child = nil; + GTM_FOREACH_OBJECT(child, self) { + if ([child conformsToProtocol:@protocol(GTMLogWriter)]) + [child logMessage:msg level:level]; + } + } +} + +@end // GTMArrayCompositeLogWriter + + +@implementation GTMLogger (GTMLoggerLogWriter) + +- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { + switch (level) { + case kGTMLoggerLevelDebug: + [self logDebug:@"%@", msg]; + break; + case kGTMLoggerLevelInfo: + [self logInfo:@"%@", msg]; + break; + case kGTMLoggerLevelError: + [self logError:@"%@", msg]; + break; + case kGTMLoggerLevelAssert: + [self logAssert:@"%@", msg]; + break; + default: + // Ignore the message. + break; + } +} + +@end // GTMLoggerLogWriter + + +@implementation GTMLogBasicFormatter + +- (NSString *)stringForFunc:(NSString *)func + withFormat:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level { + // Performance note: since we always have to create a new NSString from the + // returned CFStringRef, we may want to do a quick check here to see if |fmt| + // contains a '%', and if not, simply return 'fmt'. + CFStringRef cfmsg = NULL; + cfmsg = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, + NULL, // format options + (CFStringRef)fmt, + args); + return GTMCFAutorelease(cfmsg); +} + +@end // GTMLogBasicFormatter + + +@implementation GTMLogStandardFormatter + +- (id)init { + if ((self = [super init])) { + dateFormatter_ = [[NSDateFormatter alloc] init]; + [dateFormatter_ setFormatterBehavior:NSDateFormatterBehavior10_4]; + [dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"]; + pname_ = [[[NSProcessInfo processInfo] processName] copy]; + pid_ = [[NSProcessInfo processInfo] processIdentifier]; + } + return self; +} + +- (void)dealloc { + [dateFormatter_ release]; + [pname_ release]; + [super dealloc]; +} + +- (NSString *)stringForFunc:(NSString *)func + withFormat:(NSString *)fmt + valist:(va_list)args + level:(GTMLoggerLevel)level { + GTMLOGGER_ASSERT(dateFormatter_ != nil); + NSString *tstamp = nil; + @synchronized (dateFormatter_) { + tstamp = [dateFormatter_ stringFromDate:[NSDate date]]; + } + return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@", + tstamp, pname_, pid_, pthread_self(), + level, (func ? func : @"(no func)"), + [super stringForFunc:func withFormat:fmt valist:args level:level]]; +} + +@end // GTMLogStandardFormatter + + +@implementation GTMLogLevelFilter + +// Check the environment and the user preferences for the GTMVerboseLogging key +// to see if verbose logging has been enabled. The environment variable will +// override the defaults setting, so check the environment first. +// COV_NF_START +static BOOL IsVerboseLoggingEnabled(void) { + static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging"; + static char *env = NULL; + if (env == NULL) + env = getenv([kVerboseLoggingKey UTF8String]); + + if (env && env[0]) { + return (strtol(env, NULL, 10) != 0); + } + + return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey]; +} +// COV_NF_END + +// In DEBUG builds, log everything. If we're not in a debug build we'll assume +// that we're in a Release build. +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { +#if DEBUG + return YES; +#endif + + BOOL allow = YES; + + switch (level) { + case kGTMLoggerLevelDebug: + allow = NO; + break; + case kGTMLoggerLevelInfo: + allow = (IsVerboseLoggingEnabled() == YES); + break; + case kGTMLoggerLevelError: + allow = YES; + break; + case kGTMLoggerLevelAssert: + allow = YES; + break; + default: + allow = YES; + break; + } + + return allow; +} + +@end // GTMLogLevelFilter + + +@implementation GTMLogNoFilter + +- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { + return YES; // Allow everything through +} + +@end // GTMLogNoFilter diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/MachIPC.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/MachIPC.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/MachIPC.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/MachIPC.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,304 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// MachIPC.h +// +// Some helpful wrappers for using Mach IPC calls + +#ifndef MACH_IPC_H__ +#define MACH_IPC_H__ + +#import +#import +#import +#import + +#import + +//============================================================================== +// DISCUSSION: +// +// The three main classes of interest are +// +// MachMessage: a wrapper for a mach message of the following form +// mach_msg_header_t +// mach_msg_body_t +// optional descriptors +// optional extra message data +// +// MachReceiveMessage and MachSendMessage subclass MachMessage +// and are used instead of MachMessage which is an abstract base class +// +// ReceivePort: +// Represents a mach port for which we have receive rights +// +// MachPortSender: +// Represents a mach port for which we have send rights +// +// Here's an example to receive a message on a server port: +// +// // This creates our named server port +// ReceivePort receivePort("com.Google.MyService"); +// +// MachReceiveMessage message; +// kern_return_t result = receivePort.WaitForMessage(&message, 0); +// +// if (result == KERN_SUCCESS && message.GetMessageID() == 57) { +// mach_port_t task = message.GetTranslatedPort(0); +// mach_port_t thread = message.GetTranslatedPort(1); +// +// char *messageString = message.GetData(); +// +// printf("message string = %s\n", messageString); +// } +// +// Here is an example of using these classes to send a message to this port: +// +// // send to already named port +// MachPortSender sender("com.Google.MyService"); +// MachSendMessage message(57); // our message ID is 57 +// +// // add some ports to be translated for us +// message.AddDescriptor(mach_task_self()); // our task +// message.AddDescriptor(mach_thread_self()); // this thread +// +// char messageString[] = "Hello server!\n"; +// message.SetData(messageString, strlen(messageString)+1); +// +// kern_return_t result = sender.SendMessage(message, 1000); // timeout 1000ms +// + +#define PRINT_MACH_RESULT(result_, message_) \ + printf(message_" %s (%d)\n", mach_error_string(result_), result_ ); + +//============================================================================== +// A wrapper class for mach_msg_port_descriptor_t (with same memory layout) +// with convenient constructors and accessors +class MachMsgPortDescriptor : public mach_msg_port_descriptor_t { + public: + // General-purpose constructor + MachMsgPortDescriptor(mach_port_t in_name, + mach_msg_type_name_t in_disposition) { + name = in_name; + pad1 = 0; + pad2 = 0; + disposition = in_disposition; + type = MACH_MSG_PORT_DESCRIPTOR; + } + + // For passing send rights to a port + MachMsgPortDescriptor(mach_port_t in_name) { + name = in_name; + pad1 = 0; + pad2 = 0; + disposition = MACH_MSG_TYPE_COPY_SEND; + type = MACH_MSG_PORT_DESCRIPTOR; + } + + // Copy constructor + MachMsgPortDescriptor(const MachMsgPortDescriptor& desc) { + name = desc.name; + pad1 = desc.pad1; + pad2 = desc.pad2; + disposition = desc.disposition; + type = desc.type; + } + + mach_port_t GetMachPort() const { + return name; + } + + mach_msg_type_name_t GetDisposition() const { + return disposition; + } + + // We're just a simple wrapper for mach_msg_port_descriptor_t + // and have the same memory layout + operator mach_msg_port_descriptor_t&() { + return *this; + } + + // For convenience + operator mach_port_t() const { + return GetMachPort(); + } +}; + +//============================================================================== +// MachMessage: a wrapper for a mach message +// (mach_msg_header_t, mach_msg_body_t, extra data) +// +// This considerably simplifies the construction of a message for sending +// and the getting at relevant data and descriptors for the receiver. +// +// Currently the combined size of the descriptors plus data must be +// less than 1024. But as a benefit no memory allocation is necessary. +// +// TODO: could consider adding malloc() support for very large messages +// +// A MachMessage object is used by ReceivePort::WaitForMessage +// and MachPortSender::SendMessage +// +class MachMessage { + public: + + // The receiver of the message can retrieve the raw data this way + u_int8_t *GetData() { + return GetDataLength() > 0 ? GetDataPacket()->data : NULL; + } + + u_int32_t GetDataLength() { + return EndianU32_LtoN(GetDataPacket()->data_length); + } + + // The message ID may be used as a code identifying the type of message + void SetMessageID(int32_t message_id) { + GetDataPacket()->id = EndianU32_NtoL(message_id); + } + + int32_t GetMessageID() { return EndianU32_LtoN(GetDataPacket()->id); } + + // Adds a descriptor (typically a mach port) to be translated + // returns true if successful, otherwise not enough space + bool AddDescriptor(const MachMsgPortDescriptor &desc); + + int GetDescriptorCount() const { return body.msgh_descriptor_count; } + MachMsgPortDescriptor *GetDescriptor(int n); + + // Convenience method which gets the mach port described by the descriptor + mach_port_t GetTranslatedPort(int n); + + // A simple message is one with no descriptors + bool IsSimpleMessage() const { return GetDescriptorCount() == 0; } + + // Sets raw data for the message (returns false if not enough space) + bool SetData(void *data, int32_t data_length); + + protected: + // Consider this an abstract base class - must create an actual instance + // of MachReceiveMessage or MachSendMessage + + MachMessage() { + memset(this, 0, sizeof(MachMessage)); + } + + friend class ReceivePort; + friend class MachPortSender; + + // Represents raw data in our message + struct MessageDataPacket { + int32_t id; // little-endian + int32_t data_length; // little-endian + u_int8_t data[1]; // actual size limited by sizeof(MachMessage) + }; + + MessageDataPacket* GetDataPacket(); + + void SetDescriptorCount(int n); + void SetDescriptor(int n, const MachMsgPortDescriptor &desc); + + // Returns total message size setting msgh_size in the header to this value + int CalculateSize(); + + mach_msg_header_t head; + mach_msg_body_t body; + u_int8_t padding[1024]; // descriptors and data may be embedded here +}; + +//============================================================================== +// MachReceiveMessage and MachSendMessage are useful to separate the idea +// of a mach message being sent and being received, and adds increased type +// safety: +// ReceivePort::WaitForMessage() only accepts a MachReceiveMessage +// MachPortSender::SendMessage() only accepts a MachSendMessage + +//============================================================================== +class MachReceiveMessage : public MachMessage { + public: + MachReceiveMessage() : MachMessage() {}; +}; + +//============================================================================== +class MachSendMessage : public MachMessage { + public: + MachSendMessage(int32_t message_id); +}; + +//============================================================================== +// Represents a mach port for which we have receive rights +class ReceivePort { + public: + // Creates a new mach port for receiving messages and registers a name for it + ReceivePort(const char *receive_port_name); + + // Given an already existing mach port, use it. We take ownership of the + // port and deallocate it in our destructor. + ReceivePort(mach_port_t receive_port); + + // Create a new mach port for receiving messages + ReceivePort(); + + ~ReceivePort(); + + // Waits on the mach port until message received or timeout + kern_return_t WaitForMessage(MachReceiveMessage *out_message, + mach_msg_timeout_t timeout); + + // The underlying mach port that we wrap + mach_port_t GetPort() const { return port_; } + + private: + ReceivePort(const ReceivePort&); // disable copy c-tor + + mach_port_t port_; + kern_return_t init_result_; +}; + +//============================================================================== +// Represents a mach port for which we have send rights +class MachPortSender { + public: + // get a port with send rights corresponding to a named registered service + MachPortSender(const char *receive_port_name); + + + // Given an already existing mach port, use it. + MachPortSender(mach_port_t send_port); + + kern_return_t SendMessage(MachSendMessage &message, + mach_msg_timeout_t timeout); + + private: + MachPortSender(const MachPortSender&); // disable copy c-tor + + mach_port_t send_port_; + kern_return_t init_result_; +}; + +#endif // MACH_IPC_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/MachIPC.mm firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/MachIPC.mm --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/MachIPC.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/MachIPC.mm 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,297 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// MachIPC.mm +// Wrapper for mach IPC calls + +#import +#import "MachIPC.h" + +//============================================================================== +MachSendMessage::MachSendMessage(int32_t message_id) : MachMessage() { + head.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); + + // head.msgh_remote_port = ...; // filled out in MachPortSender::SendMessage() + head.msgh_local_port = MACH_PORT_NULL; + head.msgh_reserved = 0; + head.msgh_id = 0; + + SetDescriptorCount(0); // start out with no descriptors + + SetMessageID(message_id); + SetData(NULL, 0); // client may add data later +} + +//============================================================================== +// returns true if successful +bool MachMessage::SetData(void *data, + int32_t data_length) { + // first check to make sure we have enough space + int size = CalculateSize(); + int new_size = size + data_length; + + if ((unsigned)new_size > sizeof(MachMessage)) { + return false; // not enough space + } + + GetDataPacket()->data_length = EndianU32_NtoL(data_length); + if (data) memcpy(GetDataPacket()->data, data, data_length); + + CalculateSize(); + + return true; +} + +//============================================================================== +// calculates and returns the total size of the message +// Currently, the entire message MUST fit inside of the MachMessage +// messsage size <= sizeof(MachMessage) +int MachMessage::CalculateSize() { + int size = sizeof(mach_msg_header_t) + sizeof(mach_msg_body_t); + + // add space for MessageDataPacket + int32_t alignedDataLength = (GetDataLength() + 3) & ~0x3; + size += 2*sizeof(int32_t) + alignedDataLength; + + // add space for descriptors + size += GetDescriptorCount() * sizeof(MachMsgPortDescriptor); + + head.msgh_size = size; + + return size; +} + +//============================================================================== +MachMessage::MessageDataPacket *MachMessage::GetDataPacket() { + int desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount(); + MessageDataPacket *packet = + reinterpret_cast(padding + desc_size); + + return packet; +} + +//============================================================================== +void MachMessage::SetDescriptor(int n, + const MachMsgPortDescriptor &desc) { + MachMsgPortDescriptor *desc_array = + reinterpret_cast(padding); + desc_array[n] = desc; +} + +//============================================================================== +// returns true if successful otherwise there was not enough space +bool MachMessage::AddDescriptor(const MachMsgPortDescriptor &desc) { + // first check to make sure we have enough space + int size = CalculateSize(); + int new_size = size + sizeof(MachMsgPortDescriptor); + + if ((unsigned)new_size > sizeof(MachMessage)) { + return false; // not enough space + } + + // unfortunately, we need to move the data to allow space for the + // new descriptor + u_int8_t *p = reinterpret_cast(GetDataPacket()); + bcopy(p, p+sizeof(MachMsgPortDescriptor), GetDataLength()+2*sizeof(int32_t)); + + SetDescriptor(GetDescriptorCount(), desc); + SetDescriptorCount(GetDescriptorCount() + 1); + + CalculateSize(); + + return true; +} + +//============================================================================== +void MachMessage::SetDescriptorCount(int n) { + body.msgh_descriptor_count = n; + + if (n > 0) { + head.msgh_bits |= MACH_MSGH_BITS_COMPLEX; + } else { + head.msgh_bits &= ~MACH_MSGH_BITS_COMPLEX; + } +} + +//============================================================================== +MachMsgPortDescriptor *MachMessage::GetDescriptor(int n) { + if (n < GetDescriptorCount()) { + MachMsgPortDescriptor *desc = + reinterpret_cast(padding); + return desc + n; + } + + return nil; +} + +//============================================================================== +mach_port_t MachMessage::GetTranslatedPort(int n) { + if (n < GetDescriptorCount()) { + return GetDescriptor(n)->GetMachPort(); + } + return MACH_PORT_NULL; +} + +#pragma mark - + +//============================================================================== +// create a new mach port for receiving messages and register a name for it +ReceivePort::ReceivePort(const char *receive_port_name) { + mach_port_t current_task = mach_task_self(); + + init_result_ = mach_port_allocate(current_task, + MACH_PORT_RIGHT_RECEIVE, + &port_); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = mach_port_insert_right(current_task, + port_, + port_, + MACH_MSG_TYPE_MAKE_SEND); + + if (init_result_ != KERN_SUCCESS) + return; + + mach_port_t bootstrap_port = 0; + init_result_ = task_get_bootstrap_port(current_task, &bootstrap_port); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = bootstrap_register(bootstrap_port, + const_cast(receive_port_name), + port_); +} + +//============================================================================== +// create a new mach port for receiving messages +ReceivePort::ReceivePort() { + mach_port_t current_task = mach_task_self(); + + init_result_ = mach_port_allocate(current_task, + MACH_PORT_RIGHT_RECEIVE, + &port_); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = mach_port_insert_right(current_task, + port_, + port_, + MACH_MSG_TYPE_MAKE_SEND); +} + +//============================================================================== +// Given an already existing mach port, use it. We take ownership of the +// port and deallocate it in our destructor. +ReceivePort::ReceivePort(mach_port_t receive_port) + : port_(receive_port), + init_result_(KERN_SUCCESS) { +} + +//============================================================================== +ReceivePort::~ReceivePort() { + if (init_result_ == KERN_SUCCESS) + mach_port_deallocate(mach_task_self(), port_); +} + +//============================================================================== +kern_return_t ReceivePort::WaitForMessage(MachReceiveMessage *out_message, + mach_msg_timeout_t timeout) { + if (!out_message) { + return KERN_INVALID_ARGUMENT; + } + + // return any error condition encountered in constructor + if (init_result_ != KERN_SUCCESS) + return init_result_; + + out_message->head.msgh_bits = 0; + out_message->head.msgh_local_port = port_; + out_message->head.msgh_remote_port = MACH_PORT_NULL; + out_message->head.msgh_reserved = 0; + out_message->head.msgh_id = 0; + + kern_return_t result = mach_msg(&out_message->head, + MACH_RCV_MSG | MACH_RCV_TIMEOUT, + 0, + sizeof(MachMessage), + port_, + timeout, // timeout in ms + MACH_PORT_NULL); + + return result; +} + +#pragma mark - + +//============================================================================== +// get a port with send rights corresponding to a named registered service +MachPortSender::MachPortSender(const char *receive_port_name) { + mach_port_t bootstrap_port = 0; + init_result_ = task_get_bootstrap_port(mach_task_self(), &bootstrap_port); + + if (init_result_ != KERN_SUCCESS) + return; + + init_result_ = bootstrap_look_up(bootstrap_port, + const_cast(receive_port_name), + &send_port_); +} + +//============================================================================== +MachPortSender::MachPortSender(mach_port_t send_port) + : send_port_(send_port), + init_result_(KERN_SUCCESS) { +} + +//============================================================================== +kern_return_t MachPortSender::SendMessage(MachSendMessage &message, + mach_msg_timeout_t timeout) { + if (message.head.msgh_size == 0) { + return KERN_INVALID_VALUE; // just for safety -- never should occur + }; + + if (init_result_ != KERN_SUCCESS) + return init_result_; + + message.head.msgh_remote_port = send_port_; + + kern_return_t result = mach_msg(&message.head, + MACH_SEND_MSG | MACH_SEND_TIMEOUT, + message.head.msgh_size, + 0, + MACH_PORT_NULL, + timeout, // timeout in ms + MACH_PORT_NULL); + + return result; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/Makefile.in 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/Makefile.in 2010-04-16 17:32:47.000000000 +0100 @@ -47,8 +47,6 @@ LOCAL_INCLUDES = -I$(srcdir)/../.. -DIRS = dwarf - # This is a little weird, but we're building a host and a target lib here. # The host lib is used for dump_syms, and the target lib for the # crash reporter client. Therefore, we don't need all the srcs in both. diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,195 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// SimpleStringDictionary.h +// + +#ifndef SimpleStringDictionary_H__ +#define SimpleStringDictionary_H__ + +#import +#import + +namespace google_breakpad { + +//============================================================================== +// SimpleStringDictionary (and associated class KeyValueEntry) implement a very +// basic dictionary container class. It has the property of not making any +// memory allocations when getting and setting values. But it is not very +// efficient, with calls to get and set values operating in linear time. +// It has the additional limitation of having a fairly small fixed capacity of +// SimpleStringDictionary::MAX_NUM_ENTRIES entries. An assert() will fire if +// the client attempts to set more than this number of key/value pairs. +// Ordinarilly a C++ programmer would use something like the std::map template +// class, or on the Macintosh would often choose CFDictionary or NSDictionary. +// But these dictionary classes may call malloc() during get and set operations. +// Google Breakpad requires that no memory allocations be made in code running +// in its exception handling thread, so it uses SimpleStringDictionary as the +// underlying implementation for the GoogleBreakpad.framework APIs: +// GoogleBreakpadSetKeyValue(), GoogleBreakpadKeyValue(), and +// GoogleBreakpadRemoveKeyValue() +// + +//============================================================================== +// KeyValueEntry +// +// A helper class used by SimpleStringDictionary representing a single +// storage cell for a key/value pair. Each key and value string are +// limited to MAX_STRING_STORAGE_SIZE-1 bytes (not glyphs). This class +// performs no memory allocations. It has methods for setting and getting +// key and value strings. +// +class KeyValueEntry { + public: + KeyValueEntry() { + Clear(); + } + + KeyValueEntry(const char *key, const char *value) { + SetKeyValue(key, value); + } + + void SetKeyValue(const char *key, const char *value) { + if (!key) { + key = ""; + } + if (!value) { + value = ""; + } + + strlcpy(key_, key, sizeof(key_)); + strlcpy(value_, value, sizeof(value_)); + } + + void SetValue(const char *value) { + if (!value) { + value = ""; + } + strlcpy(value_, value, sizeof(value_)); + }; + + // Removes the key/value + void Clear() { + memset(key_, 0, sizeof(key_)); + memset(value_, 0, sizeof(value_)); + } + + bool IsActive() const { return key_[0] != '\0'; } + const char *GetKey() const { return key_; } + const char *GetValue() const { return value_; } + + // Don't change this without considering the fixed size + // of MachMessage (in MachIPC.h) + // (see also struct KeyValueMessageData in Inspector.h) + enum {MAX_STRING_STORAGE_SIZE = 256}; + + private: + char key_[MAX_STRING_STORAGE_SIZE]; + char value_[MAX_STRING_STORAGE_SIZE]; +}; + +//============================================================================== +// This class is not an efficient dictionary, but for the purposes of breakpad +// will be just fine. We're just dealing with ten or so distinct +// key/value pairs. The idea is to avoid any malloc() or free() calls +// in certain important methods to be called when a process is in a +// crashed state. Each key and value string are limited to +// KeyValueEntry::MAX_STRING_STORAGE_SIZE-1 bytes (not glyphs). Strings passed +// in exceeding this length will be truncated. +// +class SimpleStringDictionary { + public: + SimpleStringDictionary() {}; // entries will all be cleared + + // Returns the number of active key/value pairs. The upper limit for this + // is MAX_NUM_ENTRIES. + int GetCount() const; + + // Given |key|, returns its corresponding |value|. + // If |key| is NULL, an assert will fire or NULL will be returned. If |key| + // is not found or is an empty string, NULL is returned. + const char *GetValueForKey(const char *key); + + // Stores a string |value| represented by |key|. If |key| is NULL or an empty + // string, this will assert (or do nothing). If |value| is NULL then + // the |key| will be removed. An empty string is OK for |value|. + void SetKeyValue(const char *key, const char *value); + + // Given |key|, removes any associated value. It will assert (or do nothing) + // if NULL is passed in. It will do nothing if |key| is not found. + void RemoveKey(const char *key); + + // This is the maximum number of key/value pairs which may be set in the + // dictionary. An assert may fire if more values than this are set. + // Don't change this without also changing comment in GoogleBreakpad.h + enum {MAX_NUM_ENTRIES = 64}; + + private: + friend class SimpleStringDictionaryIterator; + + const KeyValueEntry *GetEntry(int i) const; + + KeyValueEntry entries_[MAX_NUM_ENTRIES]; +}; + +//============================================================================== +class SimpleStringDictionaryIterator { + public: + SimpleStringDictionaryIterator(const SimpleStringDictionary &dict) + : dict_(dict), i_(0) { + } + + // Initializes iterator to the beginning (may later call Next() ) + void Start() { + i_ = 0; + } + + // like the nextObject method of NSEnumerator (in Cocoa) + // returns NULL when there are no more entries + // + const KeyValueEntry* Next() { + for (; i_ < SimpleStringDictionary::MAX_NUM_ENTRIES; ++i_) { + const KeyValueEntry *entry = dict_.GetEntry(i_); + if (entry->IsActive()) { + i_++; // move to next entry for next time + return entry; + } + } + + return NULL; // reached end of array + } + + private: + const SimpleStringDictionary& dict_; + int i_; +}; + +} // namespace google_breakpad + +#endif // SimpleStringDictionary_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.mm firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.mm --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.mm 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.mm 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,133 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// SimpleStringDictionary.mm +// Simple string dictionary that does not allocate memory +// + +#include + +#import "SimpleStringDictionary.h" + +namespace google_breakpad { + +//============================================================================== +const KeyValueEntry *SimpleStringDictionary::GetEntry(int i) const { + return (i >= 0 && i < MAX_NUM_ENTRIES) ? &entries_[i] : NULL; +} + +//============================================================================== +int SimpleStringDictionary::GetCount() const { + int count = 0; + for (int i = 0; i < MAX_NUM_ENTRIES; ++i) { + if (entries_[i].IsActive() ) { + ++count; + } + } + + return count; +} + +//============================================================================== +const char *SimpleStringDictionary::GetValueForKey(const char *key) { + assert(key); + if (!key) + return NULL; + + for (int i = 0; i < MAX_NUM_ENTRIES; ++i) { + KeyValueEntry &entry = entries_[i]; + if (entry.IsActive() && !strcmp(entry.GetKey(), key)) { + return entry.GetValue(); + } + } + + return NULL; +} + +//============================================================================== +void SimpleStringDictionary::SetKeyValue(const char *key, + const char *value) { + if (!value) { + RemoveKey(key); + return; + } + + // key must not be NULL + assert(key); + if (!key) + return; + + // key must not be empty string + assert(key[0] != '\0'); + if (key[0] == '\0') + return; + + int free_index = -1; + + // check if key already exists + for (int i = 0; i < MAX_NUM_ENTRIES; ++i) { + KeyValueEntry &entry = entries_[i]; + + if (entry.IsActive()) { + if (!strcmp(entry.GetKey(), key)) { + entry.SetValue(value); + return; + } + } else { + // Make a note of an empty slot + if (free_index == -1) { + free_index = i; + } + } + } + + // check if we've run out of space + assert(free_index != -1); + + // Put new key into an empty slot (if found) + if (free_index != -1) { + entries_[free_index].SetKeyValue(key, value); + } +} + +//============================================================================== +void SimpleStringDictionary::RemoveKey(const char *key) { + assert(key); + if (!key) + return; + + for (int i = 0; i < MAX_NUM_ENTRIES; ++i) { + if (!strcmp(entries_[i].GetKey(), key)) { + entries_[i].Clear(); + return; + } + } +} + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,1004 @@ +// +// GTMSenTestCase.h +// +// Copyright 2007-2008 Google Inc. +// +// 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. +// + +// Portions of this file fall under the following license, marked with +// SENTE_BEGIN - SENTE_END +// +// Copyright (c) 1997-2005, Sen:te (Sente SA). All rights reserved. +// +// Use of this source code is governed by the following license: +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// (1) Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// (2) Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +// IN NO EVENT SHALL Sente SA OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT +// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, +// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Note: this license is equivalent to the FreeBSD license. +// +// This notice may not be removed from this file. + +// Some extra test case macros that would have been convenient for SenTestingKit +// to provide. I didn't stick GTM in front of the Macro names, so that they would +// be easy to remember. + +#import "GTMDefines.h" + +#if (!GTM_IPHONE_SDK) +#import +#else +#import +NSString *STComposeString(NSString *, ...); +#endif + +// Generates a failure when a1 != noErr +// Args: +// a1: should be either an OSErr or an OSStatus +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNoErr(a1, description, ...) \ +do { \ + @try {\ + OSStatus a1value = (a1); \ + if (a1value != noErr) { \ + NSString *_expression = [NSString stringWithFormat:@"Expected noErr, got %ld for (%s)", a1value, #a1]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == noErr fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 != a2 +// Args: +// a1: received value. Should be either an OSErr or an OSStatus +// a2: expected value. Should be either an OSErr or an OSStatus +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertErr(a1, a2, description, ...) \ +do { \ + @try {\ + OSStatus a1value = (a1); \ + OSStatus a2value = (a2); \ + if (a1value != a2value) { \ + NSString *_expression = [NSString stringWithFormat:@"Expected %s(%ld) but got %ld for (%s)", #a2, a2value, a1value, #a1]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == (%s) fails", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + + +// Generates a failure when a1 is NULL +// Args: +// a1: should be a pointer (use STAssertNotNil for an object) +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotNULL(a1, description, ...) \ +do { \ + @try {\ + const void* a1value = (a1); \ + if (a1value == NULL) { \ + NSString *_expression = [NSString stringWithFormat:@"(%s) != NULL", #a1]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != NULL fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 is not NULL +// Args: +// a1: should be a pointer (use STAssertNil for an object) +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNULL(a1, description, ...) \ +do { \ + @try {\ + const void* a1value = (a1); \ + if (a1value != NULL) { \ + NSString *_expression = [NSString stringWithFormat:@"(%s) == NULL", #a1]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) == NULL fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 is equal to a2. This test is for C scalars, +// structs and unions. +// Args: +// a1: argument 1 +// a2: argument 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotEquals(a1, a2, description, ...) \ +do { \ + @try {\ + if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:[[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \ + } else { \ + __typeof__(a1) a1value = (a1); \ + __typeof__(a2) a2value = (a2); \ + NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \ + NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \ + if ([a1encoded isEqualToValue:a2encoded]) { \ + NSString *_expression = [NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat:@"(%s) != (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 is equal to a2. This test is for objects. +// Args: +// a1: argument 1. object. +// a2: argument 2. object. +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotEqualObjects(a1, a2, desc, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + id a2value = (a2); \ + if ( (@encode(__typeof__(a1value)) == @encode(id)) && \ + (@encode(__typeof__(a2value)) == @encode(id)) && \ + ![(id)a1value isEqual:(id)a2value] ) continue; \ + NSString *_expression = [NSString stringWithFormat:@"%s('%@') != %s('%@')", #a1, [a1 description], #a2, [a2 description]]; \ + if (desc) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(desc, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(desc, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 is not 'op' to a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertOperation(a1, a2, op, description, ...) \ +do { \ + @try {\ + if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:[[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \ + } else { \ + __typeof__(a1) a1value = (a1); \ + __typeof__(a2) a2value = (a2); \ + if (!(a1value op a2value)) { \ + double a1DoubleValue = a1value; \ + double a2DoubleValue = a2value; \ + NSString *_expression = [NSString stringWithFormat:@"%s (%lg) %s %s (%lg)", #a1, a1DoubleValue, #op, #a2, a2DoubleValue]; \ + if (description) { \ + _expression = [NSString stringWithFormat:@"%@: %@", _expression, STComposeString(description, ##__VA_ARGS__)]; \ + } \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:_expression]]; \ + } \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException \ + failureInRaise:[NSString stringWithFormat:@"(%s) %s (%s)", #a1, #op, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when a1 is not > a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertGreaterThan(a1, a2, description, ...) \ + STAssertOperation(a1, a2, >, description, ##__VA_ARGS__) + +// Generates a failure when a1 is not >= a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertGreaterThanOrEqual(a1, a2, description, ...) \ + STAssertOperation(a1, a2, >=, description, ##__VA_ARGS__) + +// Generates a failure when a1 is not < a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertLessThan(a1, a2, description, ...) \ + STAssertOperation(a1, a2, <, description, ##__VA_ARGS__) + +// Generates a failure when a1 is not <= a2. This test is for C scalars. +// Args: +// a1: argument 1 +// a2: argument 2 +// op: operation +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertLessThanOrEqual(a1, a2, description, ...) \ + STAssertOperation(a1, a2, <=, description, ##__VA_ARGS__) + +// Generates a failure when string a1 is not equal to string a2. This call +// differs from STAssertEqualObjects in that strings that are different in +// composition (precomposed vs decomposed) will compare equal if their final +// representation is equal. +// ex O + umlaut decomposed is the same as O + umlaut composed. +// Args: +// a1: string 1 +// a2: string 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertEqualStrings(a1, a2, description, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + id a2value = (a2); \ + if (a1value == a2value) continue; \ + if ([a1value isKindOfClass:[NSString class]] && \ + [a2value isKindOfClass:[NSString class]] && \ + [a1value compare:a2value options:0] == NSOrderedSame) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject: a1value \ + andObject: a2value \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when string a1 is equal to string a2. This call +// differs from STAssertEqualObjects in that strings that are different in +// composition (precomposed vs decomposed) will compare equal if their final +// representation is equal. +// ex O + umlaut decomposed is the same as O + umlaut composed. +// Args: +// a1: string 1 +// a2: string 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotEqualStrings(a1, a2, description, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + id a2value = (a2); \ + if ([a1value isKindOfClass:[NSString class]] && \ + [a2value isKindOfClass:[NSString class]] && \ + [a1value compare:a2value options:0] != NSOrderedSame) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject: a1value \ + andObject: a2value \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when c-string a1 is not equal to c-string a2. +// Args: +// a1: string 1 +// a2: string 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertEqualCStrings(a1, a2, description, ...) \ +do { \ + @try {\ + const char* a1value = (a1); \ + const char* a2value = (a2); \ + if (a1value == a2value) continue; \ + if (strcmp(a1value, a2value) == 0) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject: [NSString stringWithUTF8String:a1value] \ + andObject: [NSString stringWithUTF8String:a2value] \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +// Generates a failure when c-string a1 is equal to c-string a2. +// Args: +// a1: string 1 +// a2: string 2 +// description: A format string as in the printf() function. Can be nil or +// an empty string but must be present. +// ...: A variable number of arguments to the format string. Can be absent. +#define STAssertNotEqualCStrings(a1, a2, description, ...) \ +do { \ + @try {\ + const char* a1value = (a1); \ + const char* a2value = (a2); \ + if (strcmp(a1value, a2value) != 0) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject: [NSString stringWithUTF8String:a1value] \ + andObject: [NSString stringWithUTF8String:a2value] \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +#if GTM_IPHONE_SDK + +// SENTE_BEGIN +/*" Generates a failure when !{ [a1 isEqualTo:a2] } is false + (or one is nil and the other is not). + _{a1 The object on the left.} + _{a2 The object on the right.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertEqualObjects(a1, a2, description, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + id a2value = (a2); \ + if (a1value == a2value) continue; \ + if ( (@encode(__typeof__(a1value)) == @encode(id)) && \ + (@encode(__typeof__(a2value)) == @encode(id)) && \ + [(id)a1value isEqual: (id)a2value] ) continue; \ + [self failWithException:[NSException failureInEqualityBetweenObject: a1value \ + andObject: a2value \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + + +/*" Generates a failure when a1 is not equal to a2. This test is for + C scalars, structs and unions. + _{a1 The argument on the left.} + _{a2 The argument on the right.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertEquals(a1, a2, description, ...) \ +do { \ + @try {\ + if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \ + } else { \ + __typeof__(a1) a1value = (a1); \ + __typeof__(a2) a2value = (a2); \ + NSValue *a1encoded = [NSValue value:&a1value withObjCType: @encode(__typeof__(a1))]; \ + NSValue *a2encoded = [NSValue value:&a2value withObjCType: @encode(__typeof__(a2))]; \ + if (![a1encoded isEqualToValue:a2encoded]) { \ + [self failWithException:[NSException failureInEqualityBetweenValue: a1encoded \ + andValue: a2encoded \ + withAccuracy: nil \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + +#define STAbsoluteDifference(left,right) (MAX(left,right)-MIN(left,right)) + + +/*" Generates a failure when a1 is not equal to a2 within + or - accuracy is false. + This test is for scalars such as floats and doubles where small differences + could make these items not exactly equal, but also works for all scalars. + _{a1 The scalar on the left.} + _{a2 The scalar on the right.} + _{accuracy The maximum difference between a1 and a2 for these values to be + considered equal.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ + +#define STAssertEqualsWithAccuracy(a1, a2, accuracy, description, ...) \ +do { \ + @try {\ + if (@encode(__typeof__(a1)) != @encode(__typeof__(a2))) { \ + [self failWithException:[NSException failureInFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:[[[NSString stringWithFormat:@"Type mismatch (%@/%@) -- ",@encode(__typeof__(a1)),@encode(__typeof__(a2))] stringByAppendingString:STComposeString(description, ##__VA_ARGS__)]]]; \ + } else { \ + __typeof__(a1) a1value = (a1); \ + __typeof__(a2) a2value = (a2); \ + __typeof__(accuracy) accuracyvalue = (accuracy); \ + if (STAbsoluteDifference(a1value, a2value) > accuracyvalue) { \ + NSValue *a1encoded = [NSValue value:&a1value withObjCType:@encode(__typeof__(a1))]; \ + NSValue *a2encoded = [NSValue value:&a2value withObjCType:@encode(__typeof__(a2))]; \ + NSValue *accuracyencoded = [NSValue value:&accuracyvalue withObjCType:@encode(__typeof__(accuracy))]; \ + [self failWithException:[NSException failureInEqualityBetweenValue: a1encoded \ + andValue: a2encoded \ + withAccuracy: accuracyencoded \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == (%s)", #a1, #a2] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + + + +/*" Generates a failure unconditionally. + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STFail(description, ...) \ +[self failWithException:[NSException failureInFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]] + + + +/*" Generates a failure when a1 is not nil. + _{a1 An object.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertNil(a1, description, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + if (a1value != nil) { \ + NSString *_a1 = [NSString stringWithUTF8String: #a1]; \ + NSString *_expression = [NSString stringWithFormat:@"((%@) == nil)", _a1]; \ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: NO \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) == nil fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + + +/*" Generates a failure when a1 is nil. + _{a1 An object.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertNotNil(a1, description, ...) \ +do { \ + @try {\ + id a1value = (a1); \ + if (a1value == nil) { \ + NSString *_a1 = [NSString stringWithUTF8String: #a1]; \ + NSString *_expression = [NSString stringWithFormat:@"((%@) != nil)", _a1]; \ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: NO \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + }\ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) != nil fails", #a1] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while(0) + + +/*" Generates a failure when expression evaluates to false. + _{expr The expression that is tested.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertTrue(expr, description, ...) \ +do { \ + BOOL _evaluatedExpression = (expr);\ + if (!_evaluatedExpression) {\ + NSString *_expression = [NSString stringWithUTF8String: #expr];\ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: NO \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while (0) + + +/*" Generates a failure when expression evaluates to false and in addition will + generate error messages if an exception is encountered. + _{expr The expression that is tested.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertTrueNoThrow(expr, description, ...) \ +do { \ + @try {\ + BOOL _evaluatedExpression = (expr);\ + if (!_evaluatedExpression) {\ + NSString *_expression = [NSString stringWithUTF8String: #expr];\ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: NO \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"(%s) ", #expr] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while (0) + + +/*" Generates a failure when the expression evaluates to true. + _{expr The expression that is tested.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertFalse(expr, description, ...) \ +do { \ + BOOL _evaluatedExpression = (expr);\ + if (_evaluatedExpression) {\ + NSString *_expression = [NSString stringWithUTF8String: #expr];\ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: YES \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ +} while (0) + + +/*" Generates a failure when the expression evaluates to true and in addition + will generate error messages if an exception is encountered. + _{expr The expression that is tested.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertFalseNoThrow(expr, description, ...) \ +do { \ + @try {\ + BOOL _evaluatedExpression = (expr);\ + if (_evaluatedExpression) {\ + NSString *_expression = [NSString stringWithUTF8String: #expr];\ + [self failWithException:[NSException failureInCondition: _expression \ + isTrue: YES \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + } \ + } \ + @catch (id anException) {\ + [self failWithException:[NSException failureInRaise:[NSString stringWithFormat: @"!(%s) ", #expr] \ + exception:anException \ + inFile:[NSString stringWithUTF8String:__FILE__] \ + atLine:__LINE__ \ + withDescription:STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while (0) + + +/*" Generates a failure when expression does not throw an exception. + _{expression The expression that is evaluated.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent. +"*/ +#define STAssertThrows(expr, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (id anException) { \ + continue; \ + }\ + [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: nil \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ +} while (0) + + +/*" Generates a failure when expression does not throw an exception of a + specific class. + _{expression The expression that is evaluated.} + _{specificException The specified class of the exception.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertThrowsSpecific(expr, specificException, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (specificException *anException) { \ + continue; \ + }\ + @catch (id anException) {\ + NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\ + [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ + continue; \ + }\ + NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\ + [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: nil \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ +} while (0) + + +/*" Generates a failure when expression does not throw an exception of a + specific class with a specific name. Useful for those frameworks like + AppKit or Foundation that throw generic NSException w/specific names + (NSInvalidArgumentException, etc). + _{expression The expression that is evaluated.} + _{specificException The specified class of the exception.} + _{aName The name of the specified exception.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} + +"*/ +#define STAssertThrowsSpecificNamed(expr, specificException, aName, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (specificException *anException) { \ + if ([aName isEqualToString: [anException name]]) continue; \ + NSString *_descrip = STComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description);\ + [self failWithException: \ + [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ + continue; \ + }\ + @catch (id anException) {\ + NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\ + [self failWithException: \ + [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ + continue; \ + }\ + NSString *_descrip = STComposeString(@"(Expected exception: %@) %@", NSStringFromClass([specificException class]), description);\ + [self failWithException: \ + [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: nil \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ +} while (0) + + +/*" Generates a failure when expression does throw an exception. + _{expression The expression that is evaluated.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertNoThrow(expr, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (id anException) { \ + [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ +} while (0) + + +/*" Generates a failure when expression does throw an exception of the specitied + class. Any other exception is okay (i.e. does not generate a failure). + _{expression The expression that is evaluated.} + _{specificException The specified class of the exception.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} +"*/ +#define STAssertNoThrowSpecific(expr, specificException, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (specificException *anException) { \ + [self failWithException:[NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(description, ##__VA_ARGS__)]]; \ + }\ + @catch (id anythingElse) {\ + ; \ + }\ +} while (0) + + +/*" Generates a failure when expression does throw an exception of a + specific class with a specific name. Useful for those frameworks like + AppKit or Foundation that throw generic NSException w/specific names + (NSInvalidArgumentException, etc). + _{expression The expression that is evaluated.} + _{specificException The specified class of the exception.} + _{aName The name of the specified exception.} + _{description A format string as in the printf() function. Can be nil or + an empty string but must be present.} + _{... A variable number of arguments to the format string. Can be absent.} + +"*/ +#define STAssertNoThrowSpecificNamed(expr, specificException, aName, description, ...) \ +do { \ + @try { \ + (expr);\ + } \ + @catch (specificException *anException) { \ + if ([aName isEqualToString: [anException name]]) { \ + NSString *_descrip = STComposeString(@"(Expected exception: %@ (name: %@)) %@", NSStringFromClass([specificException class]), aName, description);\ + [self failWithException: \ + [NSException failureInRaise: [NSString stringWithUTF8String:#expr] \ + exception: anException \ + inFile: [NSString stringWithUTF8String:__FILE__] \ + atLine: __LINE__ \ + withDescription: STComposeString(_descrip, ##__VA_ARGS__)]]; \ + } \ + continue; \ + }\ + @catch (id anythingElse) {\ + ; \ + }\ +} while (0) + + + +@interface NSException (GTMSenTestAdditions) ++ (NSException *)failureInFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ...; ++ (NSException *)failureInCondition:(NSString *)condition + isTrue:(BOOL)isTrue + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ...; ++ (NSException *)failureInEqualityBetweenObject:(id)left + andObject:(id)right + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ...; ++ (NSException *)failureInEqualityBetweenValue:(NSValue *)left + andValue:(NSValue *)right + withAccuracy:(NSValue *)accuracy + inFile:(NSString *)filename + atLine:(int) ineNumber + withDescription:(NSString *)formatString, ...; ++ (NSException *)failureInRaise:(NSString *)expression + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ...; ++ (NSException *)failureInRaise:(NSString *)expression + exception:(NSException *)exception + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ...; +@end + +// SENTE_END + +@interface SenTestCase : NSObject { + SEL currentSelector_; +} + +- (void)setUp; +- (void)invokeTest; +- (void)tearDown; +- (void)performTest:(SEL)sel; +- (void)failWithException:(NSException*)exception; +@end + +GTM_EXTERN NSString *const SenTestFailureException; + +GTM_EXTERN NSString *const SenTestFilenameKey; +GTM_EXTERN NSString *const SenTestLineNumberKey; + +#endif // GTM_IPHONE_SDK + +// All unittest cases in GTM should inherit from GTMTestCase. It makes sure +// to set up our logging system correctly to verify logging calls. +// See GTMUnitTestDevLog.h for details +@interface GTMTestCase : SenTestCase +@end diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.m firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.m --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.m 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/mac/testing/GTMSenTestCase.m 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,366 @@ +// +// GTMSenTestCase.m +// +// Copyright 2007-2008 Google Inc. +// +// 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. +// + +#import "GTMSenTestCase.h" +#import + +#if !GTM_IPHONE_SDK +#import "GTMGarbageCollection.h" +#endif // !GTM_IPHONE_SDK + +#if GTM_IPHONE_SDK +#import + +@interface NSException (GTMSenTestPrivateAdditions) ++ (NSException *)failureInFile:(NSString *)filename + atLine:(int)lineNumber + reason:(NSString *)reason; +@end + +@implementation NSException (GTMSenTestPrivateAdditions) ++ (NSException *)failureInFile:(NSString *)filename + atLine:(int)lineNumber + reason:(NSString *)reason { + NSDictionary *userInfo = + [NSDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithInteger:lineNumber], SenTestLineNumberKey, + filename, SenTestFilenameKey, + nil]; + + return [self exceptionWithName:SenTestFailureException + reason:reason + userInfo:userInfo]; +} +@end + +@implementation NSException (GTMSenTestAdditions) + ++ (NSException *)failureInFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason = testDescription; + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInCondition:(NSString *)condition + isTrue:(BOOL)isTrue + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason = [NSString stringWithFormat:@"'%@' should be %s. %@", + condition, isTrue ? "TRUE" : "FALSE", testDescription]; + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInEqualityBetweenObject:(id)left + andObject:(id)right + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason = + [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@", + [left description], [right description], testDescription]; + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInEqualityBetweenValue:(NSValue *)left + andValue:(NSValue *)right + withAccuracy:(NSValue *)accuracy + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason; + if (accuracy) { + reason = + [NSString stringWithFormat:@"'%@' should be equal to '%@'. %@", + left, right, testDescription]; + } else { + reason = + [NSString stringWithFormat:@"'%@' should be equal to '%@' +/-'%@'. %@", + left, right, accuracy, testDescription]; + } + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInRaise:(NSString *)expression + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason = [NSString stringWithFormat:@"'%@' should raise. %@", + expression, testDescription]; + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + ++ (NSException *)failureInRaise:(NSString *)expression + exception:(NSException *)exception + inFile:(NSString *)filename + atLine:(int)lineNumber + withDescription:(NSString *)formatString, ... { + + NSString *testDescription = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + testDescription = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + + NSString *reason; + if ([[exception name] isEqualToString:SenTestFailureException]) { + // it's our exception, assume it has the right description on it. + reason = [exception reason]; + } else { + // not one of our exception, use the exceptions reason and our description + reason = [NSString stringWithFormat:@"'%@' raised '%@'. %@", + expression, [exception reason], testDescription]; + } + + return [self failureInFile:filename atLine:lineNumber reason:reason]; +} + +@end + +NSString *STComposeString(NSString *formatString, ...) { + NSString *reason = @""; + if (formatString) { + va_list vl; + va_start(vl, formatString); + reason = + [[[NSString alloc] initWithFormat:formatString arguments:vl] autorelease]; + va_end(vl); + } + return reason; +} + +NSString *const SenTestFailureException = @"SenTestFailureException"; +NSString *const SenTestFilenameKey = @"SenTestFilenameKey"; +NSString *const SenTestLineNumberKey = @"SenTestLineNumberKey"; + +@interface SenTestCase (SenTestCasePrivate) +// our method of logging errors ++ (void)printException:(NSException *)exception fromTestName:(NSString *)name; +@end + +@implementation SenTestCase +- (void)failWithException:(NSException*)exception { + [exception raise]; +} + +- (void)setUp { +} + +- (void)performTest:(SEL)sel { + currentSelector_ = sel; + @try { + [self invokeTest]; + } @catch (NSException *exception) { + [[self class] printException:exception + fromTestName:NSStringFromSelector(sel)]; + [exception raise]; + } +} + ++ (void)printException:(NSException *)exception fromTestName:(NSString *)name { + NSDictionary *userInfo = [exception userInfo]; + NSString *filename = [userInfo objectForKey:SenTestFilenameKey]; + NSNumber *lineNumber = [userInfo objectForKey:SenTestLineNumberKey]; + NSString *className = NSStringFromClass([self class]); + if ([filename length] == 0) { + filename = @"Unknown.m"; + } + fprintf(stderr, "%s:%ld: error: -[%s %s] : %s\n", + [filename UTF8String], + (long)[lineNumber integerValue], + [className UTF8String], + [name UTF8String], + [[exception reason] UTF8String]); + fflush(stderr); +} + +- (void)invokeTest { + NSException *e = nil; + @try { + // Wrap things in autorelease pools because they may + // have an STMacro in their dealloc which may get called + // when the pool is cleaned up + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + // We don't log exceptions here, instead we let the person that called + // this log the exception. This ensures they are only logged once but the + // outer layers get the exceptions to report counts, etc. + @try { + [self setUp]; + @try { + [self performSelector:currentSelector_]; + } @catch (NSException *exception) { + e = [exception retain]; + } + [self tearDown]; + } @catch (NSException *exception) { + e = [exception retain]; + } + [pool release]; + } @catch (NSException *exception) { + e = [exception retain]; + } + if (e) { + [e autorelease]; + [e raise]; + } +} + +- (void)tearDown { +} + +- (NSString *)description { + // This matches the description OCUnit would return to you + return [NSString stringWithFormat:@"-[%@ %@]", [self class], + NSStringFromSelector(currentSelector_)]; +} +@end + +#endif // GTM_IPHONE_SDK + +@implementation GTMTestCase : SenTestCase +- (void)invokeTest { + Class devLogClass = NSClassFromString(@"GTMUnitTestDevLog"); + if (devLogClass) { + [devLogClass performSelector:@selector(enableTracking)]; + [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)]; + + } + [super invokeTest]; + if (devLogClass) { + [devLogClass performSelector:@selector(verifyNoMoreLogsExpected)]; + [devLogClass performSelector:@selector(disableTracking)]; + } +} +@end + +// Leak detection +#if !GTM_IPHONE_DEVICE +// Don't want to get leaks on the iPhone Device as the device doesn't +// have 'leaks'. The simulator does though. + +// COV_NF_START +// We don't have leak checking on by default, so this won't be hit. +static void _GTMRunLeaks(void) { + // This is an atexit handler. It runs leaks for us to check if we are + // leaking anything in our tests. + const char* cExclusionsEnv = getenv("GTM_LEAKS_SYMBOLS_TO_IGNORE"); + NSMutableString *exclusions = [NSMutableString string]; + if (cExclusionsEnv) { + NSString *exclusionsEnv = [NSString stringWithUTF8String:cExclusionsEnv]; + NSArray *exclusionsArray = [exclusionsEnv componentsSeparatedByString:@","]; + NSString *exclusion; + NSCharacterSet *wcSet = [NSCharacterSet whitespaceCharacterSet]; + GTM_FOREACH_OBJECT(exclusion, exclusionsArray) { + exclusion = [exclusion stringByTrimmingCharactersInSet:wcSet]; + [exclusions appendFormat:@"-exclude \"%@\" ", exclusion]; + } + } + NSString *string + = [NSString stringWithFormat:@"/usr/bin/leaks %@%d" + @"| /usr/bin/sed -e 's/Leak: /Leaks:0: warning: Leak /'", + exclusions, getpid()]; + int ret = system([string UTF8String]); + if (ret) { + fprintf(stderr, "%s:%d: Error: Unable to run leaks. 'system' returned: %d", + __FILE__, __LINE__, ret); + fflush(stderr); + } +} +// COV_NF_END + +static __attribute__((constructor)) void _GTMInstallLeaks(void) { + BOOL checkLeaks = YES; +#if !GTM_IPHONE_SDK + checkLeaks = GTMIsGarbageCollectionEnabled() ? NO : YES; +#endif // !GTM_IPHONE_SDK + if (checkLeaks) { + checkLeaks = getenv("GTM_ENABLE_LEAKS") ? YES : NO; + if (checkLeaks) { + // COV_NF_START + // We don't have leak checking on by default, so this won't be hit. + fprintf(stderr, "Leak Checking Enabled\n"); + fflush(stderr); + int ret = atexit(&_GTMRunLeaks); + _GTMDevAssert(ret == 0, + @"Unable to install _GTMRunLeaks as an atexit handler (%d)", + errno); + // COV_NF_END + } + } +} + +#endif // !GTM_IPHONE_DEVICE diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/Makefile.in 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/Makefile.in 2010-04-16 17:32:47.000000000 +0100 @@ -47,6 +47,10 @@ LOCAL_INCLUDES = -I$(srcdir)/.. +ifeq (,$(filter-out Darwin Linux,$(OS_ARCH))) +DIRS = dwarf +endif + CPPSRCS = \ string_conversion.cc \ $(NULL) @@ -65,9 +69,3 @@ FORCE_USE_PIC = 1 include $(topsrcdir)/config/rules.mk - -# XXX, bug 417045, make -jN combines badly with -save-temps in -# CFLAGS/CXXFLAGS (for stabs symbols with XCode3) -ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) -.NOTPARALLEL: -endif diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/solaris/dump_symbols.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/solaris/dump_symbols.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/solaris/dump_symbols.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/solaris/dump_symbols.cc 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2007, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -176,7 +176,9 @@ // Demangle using demangle library on Solaris. std::string Demangle(const char *mangled) { int status = 0; + std::string str(mangled); char *demangled = (char *)malloc(demangleLen); + if (!demangled) { fprintf(stderr, "no enough memory.\n"); goto out; @@ -188,12 +190,11 @@ goto out; } - std::string str(demangled); + str = demangled; free(demangled); - return str; out: - return std::string(mangled); + return str; } bool WriteFormat(int fd, const char *fmt, ...) { @@ -650,7 +651,7 @@ return false; void *obj_base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, obj_fd, 0); - if (!obj_base) + if (obj_base == MAP_FAILED) return false; MmapWrapper map_wrapper(obj_base, st.st_size); GElf_Ehdr elf_header; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/solaris/file_id.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/solaris/file_id.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/solaris/file_id.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/solaris/file_id.cc 2010-04-16 17:32:47.000000000 +0100 @@ -151,7 +151,7 @@ return false; void *base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (!base) + if (base == MAP_FAILED) return false; bool success = false; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.cc 2010-04-16 17:32:47.000000000 +0100 @@ -30,7 +30,7 @@ #include // Disable exception handler warnings. -#pragma warning( disable : 4530 ) +#pragma warning( disable : 4530 ) #include @@ -66,12 +66,13 @@ const map ¶meters, const wstring &upload_file, const wstring &file_part_name, + int *timeout, wstring *response_body, int *response_code) { if (response_code) { *response_code = 0; } - + // TODO(bryner): support non-ASCII parameter names if (!CheckParameters(parameters)) { return false; @@ -146,6 +147,22 @@ return false; } + if (timeout) { + if (!InternetSetOption(request.get(), + INTERNET_OPTION_SEND_TIMEOUT, + timeout, + sizeof(timeout))) { + fwprintf(stderr, L"Could not unset send timeout, continuing...\n"); + } + + if (!InternetSetOption(request.get(), + INTERNET_OPTION_RECEIVE_TIMEOUT, + timeout, + sizeof(timeout))) { + fwprintf(stderr, L"Could not unset receive timeout, continuing...\n"); + } + } + if (!HttpSendRequest(request.get(), NULL, 0, const_cast(request_body.data()), static_cast(request_body.size()))) { @@ -194,17 +211,19 @@ DWORD bytes_available; DWORD total_read = 0; - bool return_code; + BOOL return_code; + + while (((return_code = InternetQueryDataAvailable(request, &bytes_available, + 0, 0)) != 0) && bytes_available > 0) { - while ((return_code = InternetQueryDataAvailable(request, &bytes_available, - 0, 0) != 0) && - bytes_available > 0) { vector response_buffer(bytes_available); DWORD size_read; - if ((return_code = InternetReadFile(request, &response_buffer[0], - bytes_available, &size_read) != 0) && - size_read > 0) { + return_code = InternetReadFile(request, + &response_buffer[0], + bytes_available, &size_read); + + if (return_code && size_read > 0) { total_read += size_read; response_body.append(&response_buffer[0], size_read); } else { @@ -316,7 +335,7 @@ #endif // _MSC_VER >= 1400 if (file.is_open()) { file.seekg(0, ios::end); - int length = file.tellg(); + std::streamoff length = file.tellg(); contents->resize(length); if (length != 0) { file.seekg(0, ios::beg); diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/windows/http_upload.h 2010-04-16 17:32:47.000000000 +0100 @@ -69,6 +69,7 @@ const map ¶meters, const wstring &upload_file, const wstring &file_part_name, + int *timeout, wstring *response_body, int *response_code); diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.cc 2010-04-16 17:32:47.000000000 +0100 @@ -118,11 +118,13 @@ return false; } - DWORD source_id; - if (FAILED(line->get_sourceFileId(&source_id))) { + DWORD dia_source_id; + if (FAILED(line->get_sourceFileId(&dia_source_id))) { fprintf(stderr, "failed to get line source file id\n"); return false; } + // duplicate file names are coalesced to share one ID + DWORD source_id = GetRealFileID(dia_source_id); DWORD line_num; if (FAILED(line->get_lineNumber(&line_num))) { @@ -136,17 +138,18 @@ return true; } -bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function) { +bool PDBSourceLineWriter::PrintFunction(IDiaSymbol *function, + IDiaSymbol *block) { // The function format is: // FUNC
    DWORD rva; - if (FAILED(function->get_relativeVirtualAddress(&rva))) { + if (FAILED(block->get_relativeVirtualAddress(&rva))) { fprintf(stderr, "couldn't get rva\n"); return false; } ULONGLONG length; - if (FAILED(function->get_length(&length))) { + if (FAILED(block->get_length(&length))) { fprintf(stderr, "failed to get function length\n"); return false; } @@ -215,7 +218,16 @@ return false; } - fwprintf(output_, L"FILE %d %s\n", file_id, file_name); + wstring file_name_string(file_name); + if (!FileIDIsCached(file_name_string)) { + // this is a new file name, cache it and output a FILE line. + CacheFileID(file_name_string, file_id); + fwprintf(output_, L"FILE %d %s\n", file_id, file_name); + } else { + // this file name has already been seen, just save this + // ID for later lookup. + StoreDuplicateFileID(file_name_string, file_id); + } file.Release(); } compiland.Release(); @@ -255,7 +267,7 @@ // that PDBSourceLineWriter will output either a FUNC or PUBLIC line, // but not both. if (tag == SymTagFunction) { - if (!PrintFunction(symbol)) { + if (!PrintFunction(symbol, symbol)) { return false; } } else if (tag == SymTagPublicSymbol) { @@ -266,6 +278,64 @@ symbol.Release(); } while (SUCCEEDED(symbols->Next(1, &symbol, &count)) && count == 1); + // When building with PGO, the compiler can split functions into + // "hot" and "cold" blocks, and move the "cold" blocks out to separate + // pages, so the function can be noncontiguous. To find these blocks, + // we have to iterate over all the compilands, and then find blocks + // that are children of them. We can then find the lexical parents + // of those blocks and print out an extra FUNC line for blocks + // that are not contained in their parent functions. + CComPtr global; + if (FAILED(session_->get_globalScope(&global))) { + fprintf(stderr, "get_globalScope failed\n"); + return false; + } + + CComPtr compilands; + if (FAILED(global->findChildren(SymTagCompiland, NULL, + nsNone, &compilands))) { + fprintf(stderr, "findChildren failed on the global\n"); + return false; + } + + CComPtr compiland; + while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) { + CComPtr blocks; + if (FAILED(compiland->findChildren(SymTagBlock, NULL, + nsNone, &blocks))) { + fprintf(stderr, "findChildren failed on a compiland\n"); + return false; + } + + CComPtr block; + while (SUCCEEDED(blocks->Next(1, &block, &count)) && count == 1) { + // find this block's lexical parent function + CComPtr parent; + DWORD tag; + if (SUCCEEDED(block->get_lexicalParent(&parent)) && + SUCCEEDED(parent->get_symTag(&tag)) && + tag == SymTagFunction) { + // now get the block's offset and the function's offset and size, + // and determine if the block is outside of the function + DWORD func_rva, block_rva; + ULONGLONG func_length; + if (SUCCEEDED(block->get_relativeVirtualAddress(&block_rva)) && + SUCCEEDED(parent->get_relativeVirtualAddress(&func_rva)) && + SUCCEEDED(parent->get_length(&func_length))) { + if (block_rva < func_rva || block_rva > (func_rva + func_length)) { + if (!PrintFunction(parent, block)) { + return false; + } + } + } + } + parent.Release(); + block.Release(); + } + blocks.Release(); + compiland.Release(); + } + return true; } diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/common/windows/pdb_source_line_writer.h 2010-04-16 17:32:47.000000000 +0100 @@ -35,6 +35,7 @@ #include +#include #include struct IDiaEnumLineNumbers; @@ -44,6 +45,7 @@ namespace google_breakpad { using std::wstring; +using stdext::hash_map; // A structure that carries information that identifies a pdb file. struct PDBModuleInfo { @@ -111,8 +113,11 @@ bool PrintLines(IDiaEnumLineNumbers *lines); // Outputs a function address and name, followed by its source line list. + // block can be the same object as function, or it can be a reference + // to a code block that is lexically part of this function, but + // resides at a separate address. // Returns true on success. - bool PrintFunction(IDiaSymbol *function); + bool PrintFunction(IDiaSymbol *function, IDiaSymbol *block); // Outputs all functions as described above. Returns true on success. bool PrintFunctions(); @@ -134,6 +139,37 @@ // its uuid and age. bool PrintPDBInfo(); + // Returns true if this filename has already been seen, + // and an ID is stored for it, or false if it has not. + bool FileIDIsCached(const wstring &file) { + return unique_files_.find(file) != unique_files_.end(); + }; + + // Cache this filename and ID for later reuse. + void CacheFileID(const wstring &file, DWORD id) { + unique_files_[file] = id; + }; + + // Store this ID in the cache as a duplicate for this filename. + void StoreDuplicateFileID(const wstring &file, DWORD id) { + hash_map::iterator iter = unique_files_.find(file); + if (iter != unique_files_.end()) { + // map this id to the previously seen one + file_ids_[id] = iter->second; + } + }; + + // Given a file's unique ID, return the ID that should be used to + // reference it. There may be multiple files with identical filenames + // but different unique IDs. The cache attempts to coalesce these into + // one ID per unique filename. + DWORD GetRealFileID(DWORD id) { + hash_map::iterator iter = file_ids_.find(id); + if (iter == file_ids_.end()) + return id; + return iter->second; + }; + // Returns the function name for a symbol. If possible, the name is // undecorated. If the symbol's decorated form indicates the size of // parameters on the stack, this information is returned in stack_param_size. @@ -153,6 +189,13 @@ // The current output file for this WriteMap invocation. FILE *output_; + // There may be many duplicate filenames with different IDs. + // This maps from the DIA "unique ID" to a single ID per unique + // filename. + hash_map file_ids_; + // This maps unique filenames to file IDs. + hash_map unique_files_; + // Disallow copy ctor and operator= PDBSourceLineWriter(const PDBSourceLineWriter&); void operator=(const PDBSourceLineWriter&); diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/config.h.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/config.h.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/config.h.in 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/config.h.in 2010-04-16 17:32:47.000000000 +0100 @@ -9,6 +9,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H +/* Define if you have POSIX threads libraries and header files. */ +#undef HAVE_PTHREAD + /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H @@ -30,6 +33,10 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H +/* Define to the sub-directory in which libtool stores uninstalled libraries. + */ +#undef LT_OBJDIR + /* Name of package */ #undef PACKAGE @@ -45,9 +52,16 @@ /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME +/* Define to the home page for this package. */ +#undef PACKAGE_URL + /* Define to the version of this package. */ #undef PACKAGE_VERSION +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +#undef PTHREAD_CREATE_JOINABLE + /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_cpu_arm.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_cpu_arm.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_cpu_arm.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_cpu_arm.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,140 @@ +/* Copyright (c) 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file contains the necessary definitions to read minidump files + * produced on ARM. These files may be read on any platform provided + * that the alignments of these structures on the processing system are + * identical to the alignments of these structures on the producing system. + * For this reason, precise-sized types are used. The structures defined + * by this file have been laid out to minimize alignment problems by + * ensuring that all members are aligned on their natural boundaries. + * In some cases, tail-padding may be significant when different ABIs specify + * different tail-padding behaviors. To avoid problems when reading or + * writing affected structures, MD_*_SIZE macros are provided where needed, + * containing the useful size of the structures without padding. + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. + * + * Author: Julian Seward + */ + +/* + * ARM support + */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_ARM_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_ARM_H__ + +#define MD_FLOATINGSAVEAREA_ARM_FPR_COUNT 32 +#define MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT 8 + +/* + * Note that these structures *do not* map directly to the CONTEXT + * structure defined in WinNT.h in the Windows Mobile SDK. That structure + * does not accomodate VFPv3, and I'm unsure if it was ever used in the + * wild anyway, as Windows CE only seems to produce "cedumps" which + * are not exactly minidumps. + */ +typedef struct { + u_int64_t fpscr; /* FPU status register */ + + /* 32 64-bit floating point registers, d0 .. d31. */ + u_int64_t regs[MD_FLOATINGSAVEAREA_ARM_FPR_COUNT]; + + /* Miscellaneous control words */ + u_int32_t extra[MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT]; +} MDFloatingSaveAreaARM; + +#define MD_CONTEXT_ARM_GPR_COUNT 16 + +typedef struct { + /* The next field determines the layout of the structure, and which parts + * of it are populated + */ + u_int32_t context_flags; + + /* 16 32-bit integer registers, r0 .. r15 + * Note the following fixed uses: + * r13 is the stack pointer + * r14 is the link register + * r15 is the program counter + */ + u_int32_t iregs[MD_CONTEXT_ARM_GPR_COUNT]; + + /* CPSR (flags, basically): 32 bits: + bit 31 - N (negative) + bit 30 - Z (zero) + bit 29 - C (carry) + bit 28 - V (overflow) + bit 27 - Q (saturation flag, sticky) + All other fields -- ignore */ + u_int32_t cpsr; + + /* The next field is included with MD_CONTEXT_ARM_FLOATING_POINT */ + MDFloatingSaveAreaARM float_save; + +} MDRawContextARM; + +/* Indices into iregs for registers with a dedicated or conventional + * purpose. + */ +enum MDARMRegisterNumbers { + MD_CONTEXT_ARM_REG_FP = 11, + MD_CONTEXT_ARM_REG_SP = 13, + MD_CONTEXT_ARM_REG_LR = 14, + MD_CONTEXT_ARM_REG_PC = 15 +}; + +/* For (MDRawContextARM).context_flags. These values indicate the type of + * context stored in the structure. */ +#define MD_CONTEXT_ARM_INTEGER (MD_CONTEXT_ARM | 0x00000002) +#define MD_CONTEXT_ARM_FLOATING_POINT (MD_CONTEXT_ARM | 0x00000004) + +#define MD_CONTEXT_ARM_FULL (MD_CONTEXT_ARM_INTEGER | \ + MD_CONTEXT_ARM_FLOATING_POINT) + +#define MD_CONTEXT_ARM_ALL (MD_CONTEXT_ARM_INTEGER | \ + MD_CONTEXT_ARM_FLOATING_POINT) + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_ARM_H__ */ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_cpu_ppc.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_cpu_ppc.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_cpu_ppc.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_cpu_ppc.h 2010-04-16 17:32:47.000000000 +0100 @@ -107,7 +107,11 @@ /* Use the same 32-bit alignment when accessing this structure from 64-bit code * as is used natively in 32-bit code. #pragma pack is a MSVC extension * supported by gcc. */ +#if defined(__SUNPRO_C) || defined(__SUNPRO_CC) +#pragma pack(4) +#else #pragma pack(push, 4) +#endif typedef struct { /* context_flags is not present in ppc_thread_state, but it aids @@ -136,7 +140,11 @@ MDVectorSaveAreaPPC vector_save; } MDRawContextPPC; /* Based on ppc_thread_state */ +#if defined(__SUNPRO_C) || defined(__SUNPRO_CC) +#pragma pack(0) +#else #pragma pack(pop) +#endif /* For (MDRawContextPPC).context_flags. These values indicate the type of * context stored in the structure. MD_CONTEXT_PPC is Breakpad-defined. Its diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_exception_win32.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_exception_win32.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_exception_win32.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_exception_win32.h 2010-04-16 17:32:47.000000000 +0100 @@ -94,8 +94,11 @@ /* EXCEPTION_PRIV_INSTRUCTION */ MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW = 0xc00000fd, /* EXCEPTION_STACK_OVERFLOW */ - MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK = 0xc0000194 + MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK = 0xc0000194, /* EXCEPTION_POSSIBLE_DEADLOCK */ + MD_EXCEPTION_CODE_WIN_UNHANDLED_CPP_EXCEPTION = 0xe06d7363 + /* Per http://support.microsoft.com/kb/185294, + generated by Visual C++ compiler */ } MDExceptionCodeWin; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_format.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_format.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_format.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/common/minidump_format.h 2010-04-16 17:32:47.000000000 +0100 @@ -112,12 +112,12 @@ u_int32_t context_flags; } MDRawContextBase; -#include "minidump_cpu_sparc.h" -#include "minidump_cpu_x86.h" +#include "minidump_cpu_amd64.h" +#include "minidump_cpu_arm.h" #include "minidump_cpu_ppc.h" #include "minidump_cpu_ppc64.h" -#include "minidump_cpu_amd64.h" - +#include "minidump_cpu_sparc.h" +#include "minidump_cpu_x86.h" /* * WinVer.h diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/basic_source_line_resolver.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/basic_source_line_resolver.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/basic_source_line_resolver.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/basic_source_line_resolver.h 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -33,28 +33,14 @@ #ifndef GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__ #define GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__ -// TODO: Platforms that have no hash_map can use map, at the likely cost of -// performance. -#ifdef __SUNPRO_CC -#define BSLR_NO_HASH_MAP -#endif // __SUNPRO_CC - -#ifdef BSLR_NO_HASH_MAP #include -#else // BSLR_NO_HASH_MAP -#include -#endif // BSLR_NO_HASH_MAP #include "google_breakpad/processor/source_line_resolver_interface.h" namespace google_breakpad { using std::string; -#ifdef BSLR_NO_HASH_MAP using std::map; -#else // BSLR_NO_HASH_MAP -using __gnu_cxx::hash_map; -#endif // BSLR_NO_HASH_MAP class BasicSourceLineResolver : public SourceLineResolverInterface { public: @@ -69,9 +55,15 @@ // retained until the BasicSourceLineResolver is destroyed. virtual bool LoadModule(const string &module_name, const string &map_file); - virtual bool HasModule(const string &module_name) const; + // Exactly the same as above, except the given map_buffer is used + // for symbols. + virtual bool LoadModuleUsingMapBuffer(const string &module_name, + const string &map_buffer); - virtual StackFrameInfo* FillSourceLineInfo(StackFrame *frame) const; + virtual bool HasModule(const string &module_name) const; + virtual void FillSourceLineInfo(StackFrame *frame) const; + virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame) const; + virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const; private: template class MemAddrMap; @@ -79,23 +71,13 @@ struct Function; struct PublicSymbol; struct File; -#ifdef BSLR_NO_HASH_MAP struct CompareString { bool operator()(const string &s1, const string &s2) const; }; -#else // BSLR_NO_HASH_MAP - struct HashString { - size_t operator()(const string &s) const; - }; -#endif // BSLR_NO_HASH_MAP class Module; // All of the modules we've loaded -#ifdef BSLR_NO_HASH_MAP typedef map ModuleMap; -#else // BSLR_NO_HASH_MAP - typedef hash_map ModuleMap; -#endif // BSLR_NO_HASH_MAP ModuleMap *modules_; // Disallow unwanted copy ctor and assignment operator diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/memory_region.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/memory_region.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/memory_region.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/memory_region.h 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -50,10 +50,10 @@ virtual ~MemoryRegion() {} // The base address of this memory region. - virtual u_int64_t GetBase() = 0; + virtual u_int64_t GetBase() const = 0; // The size of this memory region. - virtual u_int32_t GetSize() = 0; + virtual u_int32_t GetSize() const = 0; // Access to data of various sizes within the memory region. address // is a pointer to read, and it must lie within the memory region as @@ -63,10 +63,10 @@ // program. Returns true on success. Fails and returns false if address // is out of the region's bounds (after considering the width of value), // or for other types of errors. - virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value) = 0; - virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) = 0; - virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) = 0; - virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) = 0; + virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value) const =0; + virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) const =0; + virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) const =0; + virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) const =0; }; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump.h 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -81,6 +81,7 @@ #include +#include #include #include #include @@ -178,10 +179,11 @@ // Returns raw CPU-specific context data for the named CPU type. If the // context data does not match the CPU type or does not exist, returns // NULL. - const MDRawContextX86* GetContextX86() const; - const MDRawContextPPC* GetContextPPC() const; const MDRawContextAMD64* GetContextAMD64() const; + const MDRawContextARM* GetContextARM() const; + const MDRawContextPPC* GetContextPPC() const; const MDRawContextSPARC* GetContextSPARC() const; + const MDRawContextX86* GetContextX86() const; // Print a human-readable representation of the object to stdout. void Print(); @@ -215,7 +217,8 @@ MDRawContextAMD64* amd64; // on Solaris SPARC, sparc is defined as a numeric constant, // so variables can NOT be named as sparc - MDRawContextSPARC* ctx_sparc; + MDRawContextSPARC* ctx_sparc; + MDRawContextARM* arm; } context_; }; @@ -239,22 +242,22 @@ // Returns a pointer to the base of the memory region. Returns the // cached value if available, otherwise, reads the minidump file and // caches the memory region. - const u_int8_t* GetMemory(); + const u_int8_t* GetMemory() const; // The address of the base of the memory region. - u_int64_t GetBase(); + u_int64_t GetBase() const; // The size, in bytes, of the memory region. - u_int32_t GetSize(); + u_int32_t GetSize() const; // Frees the cached memory region, if cached. void FreeMemory(); // Obtains the value of memory at the pointer specified by address. - bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value); - bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value); - bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value); - bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value); + bool GetMemoryAtAddress(u_int64_t address, u_int8_t* value) const; + bool GetMemoryAtAddress(u_int64_t address, u_int16_t* value) const; + bool GetMemoryAtAddress(u_int64_t address, u_int32_t* value) const; + bool GetMemoryAtAddress(u_int64_t address, u_int64_t* value) const; // Print a human-readable representation of the object to stdout. void Print(); @@ -271,7 +274,7 @@ // Implementation for GetMemoryAtAddress template bool GetMemoryAtAddressInternal(u_int64_t address, - T* value); + T* value) const; // The largest memory region that will be read from a minidump. The // default is 1MB. @@ -282,7 +285,7 @@ MDMemoryDescriptor* descriptor_; // Cached memory. - vector* memory_; + mutable vector* memory_; }; @@ -336,7 +339,9 @@ } static u_int32_t max_threads() { return max_threads_; } - unsigned int thread_count() const { return valid_ ? thread_count_ : 0; } + unsigned int thread_count() const { + return valid_ ? thread_count_ : 0; + } // Sequential access to threads. MinidumpThread* GetThreadAtIndex(unsigned int index) const; @@ -637,6 +642,46 @@ MinidumpContext* context_; }; +// MinidumpAssertion wraps MDRawAssertionInfo, which contains information +// about an assertion that caused the minidump to be generated. +class MinidumpAssertion : public MinidumpStream { + public: + virtual ~MinidumpAssertion(); + + const MDRawAssertionInfo* assertion() const { + return valid_ ? &assertion_ : NULL; + } + + string expression() const { + return valid_ ? expression_ : ""; + } + + string function() const { + return valid_ ? function_ : ""; + } + + string file() const { + return valid_ ? file_ : ""; + } + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + + static const u_int32_t kStreamType = MD_ASSERTION_INFO_STREAM; + + explicit MinidumpAssertion(Minidump* minidump); + + bool Read(u_int32_t expected_size); + + MDRawAssertionInfo assertion_; + string expression_; + string function_; + string file_; +}; + // MinidumpSystemInfo wraps MDRawSystemInfo and provides information about // the system on which the minidump was generated. See also MinidumpMiscInfo. @@ -754,9 +799,17 @@ public: // path is the pathname of a file containing the minidump. explicit Minidump(const string& path); - - ~Minidump(); - + // input is an istream wrapping minidump data. Minidump holds a + // weak pointer to input, and the caller must ensure that the stream + // is valid as long as the Minidump object is. + explicit Minidump(std::istream& input); + + virtual ~Minidump(); + + // path may be empty if the minidump was not opened from a file + virtual string path() const { + return path_; + } static void set_max_streams(u_int32_t max_streams) { max_streams_ = max_streams; } @@ -767,22 +820,23 @@ } static u_int32_t max_string_length() { return max_string_length_; } - const MDRawHeader* header() const { return valid_ ? &header_ : NULL; } + virtual const MDRawHeader* header() const { return valid_ ? &header_ : NULL; } // Reads the minidump file's header and top-level stream directory. // The minidump is expected to be positioned at the beginning of the // header. Read() sets up the stream list and map, and validates the // Minidump object. - bool Read(); + virtual bool Read(); // The next set of methods are stubs that call GetStream. They exist to // force code generation of the templatized API within the module, and // to avoid exposing an ugly API (GetStream needs to accept a garbage // parameter). - MinidumpThreadList* GetThreadList(); + virtual MinidumpThreadList* GetThreadList(); MinidumpModuleList* GetModuleList(); MinidumpMemoryList* GetMemoryList(); MinidumpException* GetException(); + MinidumpAssertion* GetAssertion(); MinidumpSystemInfo* GetSystemInfo(); MinidumpMiscInfo* GetMiscInfo(); MinidumpBreakpadInfo* GetBreakpadInfo(); @@ -808,7 +862,7 @@ bool SeekSet(off_t offset); // Returns the current position of the minidump file. - off_t Tell() { return valid_ ? lseek(fd_, 0, SEEK_CUR) : (off_t)-1; } + off_t Tell(); // The next 2 methods are medium-level I/O routines. @@ -879,11 +933,12 @@ MinidumpStreamMap* stream_map_; // The pathname of the minidump file to process, set in the constructor. + // This may be empty if the minidump was opened directly from a stream. const string path_; - // The file descriptor for all file I/O. Used by ReadBytes and SeekSet. - // Set based on the |path_| member by Open, which is called by Read. - int fd_; + // The stream for all file I/O. Used by ReadBytes and SeekSet. + // Set based on the path in Open, or directly in the constructor. + std::istream* stream_; // swap_ is true if the minidump file should be byte-swapped. If the // minidump was produced by a CPU that is other-endian than the CPU diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump_processor.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump_processor.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump_processor.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/minidump_processor.h 2010-04-16 17:32:47.000000000 +0100 @@ -30,6 +30,7 @@ #ifndef GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_PROCESSOR_H__ #define GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_PROCESSOR_H__ +#include #include #include "google_breakpad/common/breakpad_types.h" @@ -42,16 +43,53 @@ class SourceLineResolverInterface; class SymbolSupplier; class SystemInfo; +// Return type for Process() +enum ProcessResult { + PROCESS_OK, // The minidump was + // processed + // successfully. + + PROCESS_ERROR_MINIDUMP_NOT_FOUND, // The minidump file + // was not found. + + PROCESS_ERROR_NO_MINIDUMP_HEADER, // The minidump file + // had no header + + PROCESS_ERROR_NO_THREAD_LIST, // The minidump file + // had no thread list. + + PROCESS_ERROR_GETTING_THREAD, // There was an error + // getting one + // thread's data from + // the minidump. + + PROCESS_ERROR_GETTING_THREAD_ID, // There was an error + // getting a thread id + // from the thread's + // data. + + PROCESS_ERROR_DUPLICATE_REQUESTING_THREADS, // There was more than + // one requesting + // thread. + + PROCESS_ERROR_NO_MEMORY_FOR_THREAD, // A thread had no + // memory region. + + PROCESS_ERROR_NO_STACKWALKER_FOR_THREAD, // We couldn't + // determine the + // StackWalker to walk + // the minidump's + // threads. + + PROCESS_SYMBOL_SUPPLIER_INTERRUPTED // The minidump + // processing was + // interrupted by the + // SymbolSupplier(not + // fatal) +}; class MinidumpProcessor { public: - // Return type for Process() - enum ProcessResult { - PROCESS_OK, // the minidump was processed successfully - PROCESS_ERROR, // there was an error processing the minidump - PROCESS_INTERRUPTED // processing was interrupted by the SymbolSupplier - }; - // Initializes this MinidumpProcessor. supplier should be an // implementation of the SymbolSupplier abstract base class. MinidumpProcessor(SymbolSupplier *supplier, @@ -62,6 +100,10 @@ ProcessResult Process(const string &minidump_file, ProcessState *process_state); + // Processes the minidump structure and fills process_state with the + // result. + ProcessResult Process(Minidump *minidump, + ProcessState *process_state); // Populates the cpu_* fields of the |info| parameter with textual // representations of the CPU type that the minidump in |dump| was // produced on. Returns false if this information is not available in @@ -84,6 +126,26 @@ // was caused by a memory access violation. static string GetCrashReason(Minidump *dump, u_int64_t *address); + // This function returns true if the passed-in error code is + // something unrecoverable(i.e. retry should not happen). For + // instance, if the minidump is corrupt, then it makes no sense to + // retry as we won't be able to glean additional information. + // However, as an example of the other case, the symbol supplier can + // return an error code indicating it was 'interrupted', which can + // happen of the symbols are fetched from a remote store, and a + // retry might be successful later on. + // You should not call this method with PROCESS_OK! Test for + // that separately before calling this. + static bool IsErrorUnrecoverable(ProcessResult p) { + assert(p != PROCESS_OK); + return (p != PROCESS_SYMBOL_SUPPLIER_INTERRUPTED); + } + + // Returns a textual representation of an assertion included + // in the minidump. Returns an empty string if this information + // does not exist or cannot be determined. + static string GetAssertion(Minidump *dump); + private: SymbolSupplier *supplier_; SourceLineResolverInterface *resolver_; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/process_state.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/process_state.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/process_state.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/process_state.h 2010-04-16 17:32:47.000000000 +0100 @@ -36,8 +36,9 @@ #include #include -#include "google_breakpad/processor/system_info.h" #include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/system_info.h" +#include "google_breakpad/processor/minidump.h" namespace google_breakpad { @@ -60,8 +61,12 @@ bool crashed() const { return crashed_; } string crash_reason() const { return crash_reason_; } u_int64_t crash_address() const { return crash_address_; } + string assertion() const { return assertion_; } int requesting_thread() const { return requesting_thread_; } const vector* threads() const { return &threads_; } + const vector* thread_memory_regions() const { + return &thread_memory_regions_; + } const SystemInfo* system_info() const { return &system_info_; } const CodeModules* modules() const { return modules_; } @@ -88,6 +93,11 @@ // this will be the address of the instruction that caused the fault. u_int64_t crash_address_; + // If there was an assertion that was hit, a textual representation + // of that assertion, possibly including the file and line at which + // it occurred. + string assertion_; + // The index of the thread that requested a dump be written in the // threads vector. If a dump was produced as a result of a crash, this // will point to the thread that crashed. If the dump was produced as @@ -101,6 +111,7 @@ // Stacks for each thread (except possibly the exception handler // thread) at the time of the crash. vector threads_; + vector thread_memory_regions_; // OS and CPU information. SystemInfo system_info_; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/source_line_resolver_interface.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/source_line_resolver_interface.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/source_line_resolver_interface.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/source_line_resolver_interface.h 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,6 @@ -// Copyright (c) 2006, Google Inc. +// -*- mode: C++ -*- + +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -40,7 +42,8 @@ using std::string; struct StackFrame; -struct StackFrameInfo; +struct WindowsFrameInfo; +struct CFIFrameInfo; class SourceLineResolverInterface { public: @@ -56,18 +59,31 @@ // map_file should contain line/address mappings for this module. virtual bool LoadModule(const string &module_name, const string &map_file) = 0; + // Same as above, but takes the contents of a pre-read map buffer + virtual bool LoadModuleUsingMapBuffer(const string &module_name, + const string &map_buffer) = 0; // Returns true if a module with the given name has been loaded. virtual bool HasModule(const string &module_name) const = 0; // Fills in the function_base, function_name, source_file_name, // and source_line fields of the StackFrame. The instruction and - // module_name fields must already be filled in. Additional debugging - // information, if available, is returned. If the information is not - // available, returns NULL. A NULL return value does not indicate an - // error. The caller takes ownership of any returned StackFrameInfo - // object. - virtual StackFrameInfo* FillSourceLineInfo(StackFrame *frame) const = 0; + // module_name fields must already be filled in. + virtual void FillSourceLineInfo(StackFrame *frame) const = 0; + + // If Windows stack walking information is available covering + // FRAME's instruction address, return a WindowsFrameInfo structure + // describing it. If the information is not available, returns NULL. + // A NULL return value does not indicate an error. The caller takes + // ownership of any returned WindowsFrameInfo object. + virtual WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame) + const = 0; + + // If CFI stack walking information is available covering ADDRESS, + // return a CFIFrameInfo structure describing it. If the information + // is not available, return NULL. The caller takes ownership of any + // returned CFIFrameInfo object. + virtual CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const = 0; protected: // SourceLineResolverInterface cannot be instantiated except by subclasses diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stack_frame_cpu.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stack_frame_cpu.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stack_frame_cpu.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stack_frame_cpu.h 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,6 @@ -// Copyright (c) 2006, Google Inc. +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -44,21 +46,50 @@ namespace google_breakpad { +struct WindowsFrameInfo; +struct CFIFrameInfo; + struct StackFrameX86 : public StackFrame { - // ContextValidity has one entry for each relevant hardware pointer register - // (%eip and %esp) and one entry for each nonvolatile (callee-save) register. + // ContextValidity has one entry for each relevant hardware pointer + // register (%eip and %esp) and one entry for each general-purpose + // register. It's worthwhile having validity flags for caller-saves + // registers: they are valid in the youngest frame, and such a frame + // might save a callee-saves register in a caller-saves register, but + // SimpleCFIWalker won't touch registers unless they're marked as valid. enum ContextValidity { CONTEXT_VALID_NONE = 0, CONTEXT_VALID_EIP = 1 << 0, CONTEXT_VALID_ESP = 1 << 1, CONTEXT_VALID_EBP = 1 << 2, - CONTEXT_VALID_EBX = 1 << 3, - CONTEXT_VALID_ESI = 1 << 4, - CONTEXT_VALID_EDI = 1 << 5, + CONTEXT_VALID_EAX = 1 << 3, + CONTEXT_VALID_EBX = 1 << 4, + CONTEXT_VALID_ECX = 1 << 5, + CONTEXT_VALID_EDX = 1 << 6, + CONTEXT_VALID_ESI = 1 << 7, + CONTEXT_VALID_EDI = 1 << 8, CONTEXT_VALID_ALL = -1 }; - StackFrameX86() : context(), context_validity(CONTEXT_VALID_NONE) {} + // Indicates how well we trust the instruction pointer we derived + // during stack walking. Since the stack walker can resort to + // stack scanning, we can wind up with dubious frames. + // In rough order of "trust metric". + enum FrameTrust { + FRAME_TRUST_NONE, // Unknown + FRAME_TRUST_SCAN, // Scanned the stack, found this + FRAME_TRUST_CFI_SCAN, // Scanned the stack using call frame info, found this + FRAME_TRUST_FP, // Derived from frame pointer + FRAME_TRUST_CFI, // Derived from call frame info + FRAME_TRUST_CONTEXT // Given as instruction pointer in a context + }; + + StackFrameX86() + : context(), + context_validity(CONTEXT_VALID_NONE), + trust(FRAME_TRUST_NONE), + windows_frame_info(NULL), + cfi_frame_info(NULL) {} + ~StackFrameX86(); // Register state. This is only fully valid for the topmost frame in a // stack. In other frames, the values of nonvolatile registers may be @@ -70,6 +101,15 @@ // the OR operator doesn't work well with enumerated types. This indicates // which fields in context are valid. int context_validity; + + // Amount of trust the stack walker has in the instruction pointer + // of this frame. + FrameTrust trust; + + // Any stack walking information we found describing this.instruction. + // These may be NULL if there is no such information for that address. + WindowsFrameInfo *windows_frame_info; + CFIFrameInfo *cfi_frame_info; }; struct StackFramePPC : public StackFrame { @@ -99,28 +139,44 @@ }; struct StackFrameAMD64 : public StackFrame { - // ContextValidity has one entry for each relevant hardware pointer register - // (%rip and %rsp) and one entry for each nonvolatile (callee-save) register. - //FIXME: validate this list + // ContextValidity has one entry for each register that we might be able + // to recover. enum ContextValidity { - CONTEXT_VALID_NONE = 0, - CONTEXT_VALID_RIP = 1 << 0, - CONTEXT_VALID_RSP = 1 << 1, - CONTEXT_VALID_RBP = 1 << 2, + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_RAX = 1 << 0, + CONTEXT_VALID_RDX = 1 << 1, + CONTEXT_VALID_RCX = 1 << 2, + CONTEXT_VALID_RBX = 1 << 3, + CONTEXT_VALID_RSI = 1 << 4, + CONTEXT_VALID_RDI = 1 << 5, + CONTEXT_VALID_RBP = 1 << 6, + CONTEXT_VALID_RSP = 1 << 7, + CONTEXT_VALID_R8 = 1 << 8, + CONTEXT_VALID_R9 = 1 << 9, + CONTEXT_VALID_R10 = 1 << 10, + CONTEXT_VALID_R11 = 1 << 11, + CONTEXT_VALID_R12 = 1 << 12, + CONTEXT_VALID_R13 = 1 << 13, + CONTEXT_VALID_R14 = 1 << 14, + CONTEXT_VALID_R15 = 1 << 15, + CONTEXT_VALID_RIP = 1 << 16, CONTEXT_VALID_ALL = -1 }; StackFrameAMD64() : context(), context_validity(CONTEXT_VALID_NONE) {} - // Register state. This is only fully valid for the topmost frame in a - // stack. In other frames, the values of nonvolatile registers may be - // present, given sufficient debugging information. Refer to - // context_validity. + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, which registers are present depends on what + // debugging information we had available. Refer to context_validity. MDRawContextAMD64 context; - // context_validity is actually ContextValidity, but int is used because - // the OR operator doesn't work well with enumerated types. This indicates - // which fields in context are valid. + // For each register in context whose value has been recovered, we set + // the corresponding CONTEXT_VALID_ bit in context_validity. + // + // context_validity's type should actually be ContextValidity, but + // we use int instead because the bitwise inclusive or operator + // yields an int when applied to enum values, and C++ doesn't + // silently convert from ints to enums. int context_validity; }; @@ -148,6 +204,58 @@ int context_validity; }; +struct StackFrameARM : public StackFrame { + // A flag for each register we might know. + enum ContextValidity { + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_R0 = 1 << 0, + CONTEXT_VALID_R1 = 1 << 1, + CONTEXT_VALID_R2 = 1 << 2, + CONTEXT_VALID_R3 = 1 << 3, + CONTEXT_VALID_R4 = 1 << 4, + CONTEXT_VALID_R5 = 1 << 5, + CONTEXT_VALID_R6 = 1 << 6, + CONTEXT_VALID_R7 = 1 << 7, + CONTEXT_VALID_R8 = 1 << 8, + CONTEXT_VALID_R9 = 1 << 9, + CONTEXT_VALID_R10 = 1 << 10, + CONTEXT_VALID_R11 = 1 << 11, + CONTEXT_VALID_R12 = 1 << 12, + CONTEXT_VALID_R13 = 1 << 13, + CONTEXT_VALID_R14 = 1 << 14, + CONTEXT_VALID_R15 = 1 << 15, + CONTEXT_VALID_ALL = ~CONTEXT_VALID_NONE, + + // Aliases for registers with dedicated or conventional roles. + CONTEXT_VALID_FP = CONTEXT_VALID_R11, + CONTEXT_VALID_SP = CONTEXT_VALID_R13, + CONTEXT_VALID_LR = CONTEXT_VALID_R14, + CONTEXT_VALID_PC = CONTEXT_VALID_R15 + }; + + StackFrameARM() : context(), context_validity(CONTEXT_VALID_NONE) {} + + // Return the ContextValidity flag for register rN. + static ContextValidity RegisterValidFlag(int n) { + return ContextValidity(1 << n); + } + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, the values of nonvolatile registers may be + // present, given sufficient debugging information. Refer to + // context_validity. + MDRawContextARM context; + + // For each register in context whose value has been recovered, we set + // the corresponding CONTEXT_VALID_ bit in context_validity. + // + // context_validity's type should actually be ContextValidity, but + // we use int instead because the bitwise inclusive or operator + // yields an int when applied to enum values, and C++ doesn't + // silently convert from ints to enums. + int context_validity; +}; + } // namespace google_breakpad #endif // GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_CPU_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stackwalker.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stackwalker.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stackwalker.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/stackwalker.h 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -41,22 +41,23 @@ #ifndef GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__ #define GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__ -#include +#include +#include +#include "google_breakpad/common/breakpad_types.h" namespace google_breakpad { class CallStack; +class CodeModule; class CodeModules; -template class linked_ptr; class MemoryRegion; class MinidumpContext; class SourceLineResolverInterface; struct StackFrame; -struct StackFrameInfo; class SymbolSupplier; class SystemInfo; -using std::vector; +using std::set; class Stackwalker { @@ -95,6 +96,16 @@ SymbolSupplier *supplier, SourceLineResolverInterface *resolver); + // This can be used to filter out potential return addresses when + // the stack walker resorts to stack scanning. + // Returns true if any of: + // * This address is within a loaded module, but we don't have symbols + // for that module. + // * This address is within a loaded module for which we have symbols, + // and falls inside a function in that module. + // Returns false otherwise. + bool InstructionAddressSeemsValid(u_int64_t address); + // Information about the system that produced the minidump. Subclasses // and the SymbolSupplier may find this information useful. const SystemInfo *system_info_; @@ -107,6 +118,10 @@ // This field is optional and may be NULL. const CodeModules *modules_; + protected: + // The SourceLineResolver implementation. + SourceLineResolverInterface *resolver_; + private: // Obtains the context frame, the innermost called procedure in a stack // trace. Returns NULL on failure. GetContextFrame allocates a new @@ -122,15 +137,15 @@ // the end of the stack has been reached). GetCallerFrame allocates a new // StackFrame (or StackFrame subclass), ownership of which is taken by // the caller. - virtual StackFrame* GetCallerFrame( - const CallStack *stack, - const vector< linked_ptr > &stack_frame_info) = 0; + virtual StackFrame* GetCallerFrame(const CallStack *stack) = 0; // The optional SymbolSupplier for resolving source line info. SymbolSupplier *supplier_; - // The SourceLineResolver implementation - SourceLineResolverInterface *resolver_; + // A list of modules that we haven't found symbols for. We track + // this in order to avoid repeatedly looking them up again within + // one minidump. + set no_symbol_modules_; }; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/symbol_supplier.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/symbol_supplier.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/symbol_supplier.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/google_breakpad/processor/symbol_supplier.h 2010-04-16 17:32:47.000000000 +0100 @@ -59,12 +59,22 @@ // Retrieves the symbol file for the given CodeModule, placing the // path in symbol_file if successful. system_info contains strings - // identifying the operating system and CPU; SymbolSupplier may use to help - // locate the symbol file. system_info may be NULL or its fields may be - // empty if these values are unknown. + // identifying the operating system and CPU; SymbolSupplier may use + // to help locate the symbol file. system_info may be NULL or its + // fields may be empty if these values are unknown. symbol_file + // must be a pointer to a valid string virtual SymbolResult GetSymbolFile(const CodeModule *module, const SystemInfo *system_info, string *symbol_file) = 0; + // Same as above, except also places symbol data into symbol_data. + // If symbol_data is NULL, the data is not returned. + // TODO(nealsid) Once we have symbol data caching behavior implemented + // investigate making all symbol suppliers implement all methods, + // and make this pure virtual + virtual SymbolResult GetSymbolFile(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data) = 0; }; } // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver.cc 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -29,6 +29,9 @@ #include #include +#include +#include +#include #include #include @@ -43,14 +46,12 @@ #include "google_breakpad/processor/stack_frame.h" #include "processor/linked_ptr.h" #include "processor/scoped_ptr.h" -#include "processor/stack_frame_info.h" +#include "processor/windows_frame_info.h" +#include "processor/cfi_frame_info.h" using std::map; using std::vector; using std::make_pair; -#ifndef BSLR_NO_HASH_MAP -using __gnu_cxx::hash; -#endif // BSLR_NO_HASH_MAP namespace google_breakpad { @@ -106,36 +107,46 @@ public: Module(const string &name) : name_(name) { } - // Loads the given map file, returning true on success. + // Loads the given map file, returning true on success. Reads the + // map file into memory and calls LoadMapFromBuffer bool LoadMap(const string &map_file); + // Loads a map from the given buffer, returning true on success + bool LoadMapFromBuffer(const string &map_buffer); + // Looks up the given relative address, and fills the StackFrame struct - // with the result. Additional debugging information, if available, is - // returned. If no additional information is available, returns NULL. - // A NULL return value is not an error. The caller takes ownership of - // any returned StackFrameInfo object. - StackFrameInfo* LookupAddress(StackFrame *frame) const; + // with the result. + void LookupAddress(StackFrame *frame) const; + + // If Windows stack walking information is available covering ADDRESS, + // return a WindowsFrameInfo structure describing it. If the information + // is not available, returns NULL. A NULL return value does not indicate + // an error. The caller takes ownership of any returned WindowsFrameInfo + // object. + WindowsFrameInfo *FindWindowsFrameInfo(const StackFrame *frame) const; + + // If CFI stack walking information is available covering ADDRESS, + // return a CFIFrameInfo structure describing it. If the information + // is not available, return NULL. The caller takes ownership of any + // returned CFIFrameInfo object. + CFIFrameInfo *FindCFIFrameInfo(const StackFrame *frame) const; private: friend class BasicSourceLineResolver; -#ifdef BSLR_NO_HASH_MAP typedef map FileMap; -#else // BSLR_NO_HASH_MAP - typedef hash_map FileMap; -#endif // BSLR_NO_HASH_MAP - // The types for stack_info_. This is equivalent to MS DIA's + // The types for windows_frame_info_. This is equivalent to MS DIA's // StackFrameTypeEnum. Each identifies a different type of frame // information, although all are represented in the symbol file in the - // same format. These are used as indices to the stack_info_ array. - enum StackInfoTypes { - STACK_INFO_FPO = 0, - STACK_INFO_TRAP, // not used here - STACK_INFO_TSS, // not used here - STACK_INFO_STANDARD, - STACK_INFO_FRAME_DATA, - STACK_INFO_LAST, // must be the last sequentially-numbered item - STACK_INFO_UNKNOWN = -1 + // same format. These are used as indices to the windows_frame_info_ array. + enum WindowsFrameInfoTypes { + WINDOWS_FRAME_INFO_FPO = 0, + WINDOWS_FRAME_INFO_TRAP, // not used here + WINDOWS_FRAME_INFO_TSS, // not used here + WINDOWS_FRAME_INFO_STANDARD, + WINDOWS_FRAME_INFO_FRAME_DATA, + WINDOWS_FRAME_INFO_LAST, // must be the last sequentially-numbered item + WINDOWS_FRAME_INFO_UNKNOWN = -1 }; // Splits line into at most max_tokens space-separated tokens, placing @@ -163,20 +174,49 @@ // Returns false if an error occurs. bool ParsePublicSymbol(char *public_line); - // Parses a stack frame info declaration, storing it in stack_info_. + // Parses a STACK WIN or STACK CFI frame info declaration, storing + // it in the appropriate table. bool ParseStackInfo(char *stack_info_line); + // Parses a STACK WIN record, storing it in windows_frame_info_. + bool ParseWindowsFrameInfo(char *stack_info_line); + + // Parses a STACK CFI record, storing it in cfi_frame_info_. + bool ParseCFIFrameInfo(char *stack_info_line); + + // Parse RULE_SET, a series of rules of the sort appearing in STACK + // CFI records, and store the given rules in FRAME_INFO. + bool ParseCFIRuleSet(const string &rule_set, CFIFrameInfo *frame_info) const; + string name_; FileMap files_; RangeMap< MemAddr, linked_ptr > functions_; AddressMap< MemAddr, linked_ptr > public_symbols_; - // Each element in the array is a ContainedRangeMap for a type listed in - // StackInfoTypes. These are split by type because there may be overlaps - // between maps of different types, but some information is only available - // as certain types. - ContainedRangeMap< MemAddr, linked_ptr > - stack_info_[STACK_INFO_LAST]; + // Each element in the array is a ContainedRangeMap for a type + // listed in WindowsFrameInfoTypes. These are split by type because + // there may be overlaps between maps of different types, but some + // information is only available as certain types. + ContainedRangeMap< MemAddr, linked_ptr > + windows_frame_info_[WINDOWS_FRAME_INFO_LAST]; + + // DWARF CFI stack walking data. The Module stores the initial rule sets + // and rule deltas as strings, just as they appear in the symbol file: + // although the file may contain hundreds of thousands of STACK CFI + // records, walking a stack will only ever use a few of them, so it's + // best to delay parsing a record until it's actually needed. + + // STACK CFI INIT records: for each range, an initial set of register + // recovery rules. The RangeMap's itself gives the starting and ending + // addresses. + RangeMap cfi_initial_rules_; + + // STACK CFI records: at a given address, the changes to the register + // recovery rules that take effect at that address. The map key is the + // starting address; the ending address is the key of the next entry in + // this map, or the end of the range as given by the cfi_initial_rules_ + // entry (which FindCFIFrameInfo looks up first). + map cfi_delta_rules_; }; BasicSourceLineResolver::BasicSourceLineResolver() : modules_(new ModuleMap) { @@ -211,16 +251,57 @@ return true; } +bool BasicSourceLineResolver::LoadModuleUsingMapBuffer( + const string &module_name, + const string &map_buffer) { + // Make sure we don't already have a module with the given name. + if (modules_->find(module_name) != modules_->end()) { + BPLOG(INFO) << "Symbols for module " << module_name << " already loaded"; + return false; + } + + BPLOG(INFO) << "Loading symbols for module " << module_name << " from buffer"; + + Module *module = new Module(module_name); + if (!module->LoadMapFromBuffer(map_buffer)) { + delete module; + return false; + } + + modules_->insert(make_pair(module_name, module)); + return true; +} + bool BasicSourceLineResolver::HasModule(const string &module_name) const { return modules_->find(module_name) != modules_->end(); } -StackFrameInfo* BasicSourceLineResolver::FillSourceLineInfo( - StackFrame *frame) const { +void BasicSourceLineResolver::FillSourceLineInfo(StackFrame *frame) const { + if (frame->module) { + ModuleMap::const_iterator it = modules_->find(frame->module->code_file()); + if (it != modules_->end()) { + it->second->LookupAddress(frame); + } + } +} + +WindowsFrameInfo *BasicSourceLineResolver::FindWindowsFrameInfo( + const StackFrame *frame) const { if (frame->module) { ModuleMap::const_iterator it = modules_->find(frame->module->code_file()); if (it != modules_->end()) { - return it->second->LookupAddress(frame); + return it->second->FindWindowsFrameInfo(frame); + } + } + return NULL; +} + +CFIFrameInfo *BasicSourceLineResolver::FindCFIFrameInfo( + const StackFrame *frame) const { + if (frame->module) { + ModuleMap::const_iterator it = modules_->find(frame->module->code_file()); + if (it != modules_->end()) { + return it->second->FindCFIFrameInfo(frame); } } return NULL; @@ -238,44 +319,61 @@ FILE *file_; }; -bool BasicSourceLineResolver::Module::LoadMap(const string &map_file) { - FILE *f = fopen(map_file.c_str(), "r"); - if (!f) { - string error_string; - int error_code = ErrnoString(&error_string); - BPLOG(ERROR) << "Could not open " << map_file << - ", error " << error_code << ": " << error_string; +bool BasicSourceLineResolver::Module::LoadMapFromBuffer( + const string &map_buffer) { + linked_ptr cur_func; + int line_number = 0; + const char *map_buffer_c_str = map_buffer.c_str(); + char *save_ptr; + + // set up our input buffer as a c-style string so we + // can we use strtok() + // have to copy because modifying the result of string::c_str is not + // permitted + size_t map_buffer_length = strlen(map_buffer_c_str); + + // If the length is 0, we can still pretend we have a symbol file. This is + // for scenarios that want to test symbol lookup, but don't necessarily care if + // certain modules do not have any information, like system libraries. + if (map_buffer_length == 0) { + return true; + } + + scoped_array map_buffer_chars(new char[map_buffer_length]); + if (map_buffer_chars == NULL) { + BPLOG(ERROR) << "Memory allocation of " << map_buffer_length << + " bytes failed"; return false; } - AutoFileCloser closer(f); + strncpy(map_buffer_chars.get(), map_buffer_c_str, map_buffer_length); - // TODO(mmentovai): this might not be large enough to handle really long - // lines, which might be present for FUNC lines of highly-templatized - // code. - char buffer[8192]; - linked_ptr cur_func; + if (map_buffer_chars[map_buffer_length - 1] == '\n') { + map_buffer_chars[map_buffer_length - 1] = '\0'; + } + char *buffer; + buffer = strtok_r(map_buffer_chars.get(), "\r\n", &save_ptr); - int line_number = 0; - while (fgets(buffer, sizeof(buffer), f)) { + while (buffer != NULL) { ++line_number; + if (strncmp(buffer, "FILE ", 5) == 0) { if (!ParseFile(buffer)) { - BPLOG(ERROR) << "ParseFile failed at " << - map_file << ":" << line_number; + BPLOG(ERROR) << "ParseFile on buffer failed at " << + ":" << line_number; return false; } } else if (strncmp(buffer, "STACK ", 6) == 0) { if (!ParseStackInfo(buffer)) { BPLOG(ERROR) << "ParseStackInfo failed at " << - map_file << ":" << line_number; + ":" << line_number; return false; } } else if (strncmp(buffer, "FUNC ", 5) == 0) { cur_func.reset(ParseFunction(buffer)); if (!cur_func.get()) { BPLOG(ERROR) << "ParseFunction failed at " << - map_file << ":" << line_number; + ":" << line_number; return false; } // StoreRange will fail if the function has an invalid address or size. @@ -288,7 +386,7 @@ if (!ParsePublicSymbol(buffer)) { BPLOG(ERROR) << "ParsePublicSymbol failed at " << - map_file << ":" << line_number; + ":" << line_number; return false; } } else if (strncmp(buffer, "MODULE ", 7) == 0) { @@ -301,59 +399,89 @@ } else { if (!cur_func.get()) { BPLOG(ERROR) << "Found source line data without a function at " << - map_file << ":" << line_number; + ":" << line_number; return false; } Line *line = ParseLine(buffer); if (!line) { - BPLOG(ERROR) << "ParseLine failed at " << - map_file << ":" << line_number; + BPLOG(ERROR) << "ParseLine failed at " << line_number << " for " << + buffer; return false; } cur_func->lines.StoreRange(line->address, line->size, linked_ptr(line)); } + + buffer = strtok_r(NULL, "\r\n", &save_ptr); } return true; } -StackFrameInfo* BasicSourceLineResolver::Module::LookupAddress( - StackFrame *frame) const { +bool BasicSourceLineResolver::Module::LoadMap(const string &map_file) { + struct stat buf; + int error_code = stat(map_file.c_str(), &buf); + if (error_code == -1) { + string error_string; + int error_code = ErrnoString(&error_string); + BPLOG(ERROR) << "Could not open " << map_file << + ", error " << error_code << ": " << error_string; + return false; + } + + off_t file_size = buf.st_size; + + // Allocate memory for file contents, plus a null terminator + // since we'll use strtok() on the contents. + char *file_buffer = new char[sizeof(char)*file_size + 1]; + + if (file_buffer == NULL) { + BPLOG(ERROR) << "Could not allocate memory for " << map_file; + return false; + } + + BPLOG(INFO) << "Opening " << map_file; + + FILE *f = fopen(map_file.c_str(), "rt"); + if (!f) { + string error_string; + int error_code = ErrnoString(&error_string); + BPLOG(ERROR) << "Could not open " << map_file << + ", error " << error_code << ": " << error_string; + delete [] file_buffer; + return false; + } + + AutoFileCloser closer(f); + + int items_read = 0; + + items_read = fread(file_buffer, 1, file_size, f); + + if (items_read != file_size) { + string error_string; + int error_code = ErrnoString(&error_string); + BPLOG(ERROR) << "Could not slurp " << map_file << + ", error " << error_code << ": " << error_string; + delete [] file_buffer; + return false; + } + file_buffer[file_size] = '\0'; + string map_buffer(file_buffer); + delete [] file_buffer; + + return LoadMapFromBuffer(map_buffer); +} + +void BasicSourceLineResolver::Module::LookupAddress(StackFrame *frame) const { MemAddr address = frame->instruction - frame->module->base_address(); - linked_ptr retrieved_info; - // Check for debugging info first, before any possible early returns. - // - // We only know about STACK_INFO_FRAME_DATA and STACK_INFO_FPO. Prefer - // them in this order. STACK_INFO_FRAME_DATA is the newer type that - // includes its own program string. STACK_INFO_FPO is the older type - // corresponding to the FPO_DATA struct. See stackwalker_x86.cc. - if (!stack_info_[STACK_INFO_FRAME_DATA].RetrieveRange(address, - &retrieved_info)) { - stack_info_[STACK_INFO_FPO].RetrieveRange(address, &retrieved_info); - } - - scoped_ptr frame_info; - if (retrieved_info.get()) { - frame_info.reset(new StackFrameInfo()); - frame_info->CopyFrom(*retrieved_info.get()); - } - - // First, look for a matching FUNC range. Use RetrieveNearestRange instead - // of RetrieveRange so that the nearest function can be compared to the - // nearest PUBLIC symbol if the address does not lie within the function. - // Having access to the highest function below address, even when address - // is outside of the function, is useful: if the function is higher than - // the nearest PUBLIC symbol, then it means that the PUBLIC symbols is not - // valid for the address, and no function information should be filled in. - // Using RetrieveNearestRange instead of RetrieveRange means that we need - // to verify that address is within the range before using a FUNC. - // - // If no FUNC containing the address is found, look for the nearest PUBLIC - // symbol, being careful not to use a public symbol at a lower address than - // the nearest FUNC. - int parameter_size = 0; + // First, look for a FUNC record that covers address. Use + // RetrieveNearestRange instead of RetrieveRange so that, if there + // is no such function, we can use the next function to bound the + // extent of the PUBLIC symbol we find, below. This does mean we + // need to check that address indeed falls within the function we + // find; do the range comparison in an overflow-friendly way. linked_ptr func; linked_ptr public_symbol; MemAddr function_base; @@ -361,9 +489,7 @@ MemAddr public_address; if (functions_.RetrieveNearestRange(address, &func, &function_base, &function_size) && - address >= function_base && address < function_base + function_size) { - parameter_size = func->parameter_size; - + address >= function_base && address - function_base < function_size) { frame->function_name = func->name; frame->function_base = frame->module->base_address() + function_base; @@ -379,27 +505,99 @@ } } else if (public_symbols_.Retrieve(address, &public_symbol, &public_address) && - (!func.get() || public_address > function_base + function_size)) { - parameter_size = public_symbol->parameter_size; - + (!func.get() || public_address > function_base)) { frame->function_name = public_symbol->name; frame->function_base = frame->module->base_address() + public_address; - } else { - // No FUNC or PUBLIC data available. - return frame_info.release(); } +} + +WindowsFrameInfo *BasicSourceLineResolver::Module::FindWindowsFrameInfo( + const StackFrame *frame) const { + MemAddr address = frame->instruction - frame->module->base_address(); + scoped_ptr result(new WindowsFrameInfo()); + + // We only know about WINDOWS_FRAME_INFO_FRAME_DATA and + // WINDOWS_FRAME_INFO_FPO. Prefer them in this order. + // WINDOWS_FRAME_INFO_FRAME_DATA is the newer type that includes its + // own program string. WINDOWS_FRAME_INFO_FPO is the older type + // corresponding to the FPO_DATA struct. See stackwalker_x86.cc. + linked_ptr frame_info; + if ((windows_frame_info_[WINDOWS_FRAME_INFO_FRAME_DATA] + .RetrieveRange(address, &frame_info)) + || (windows_frame_info_[WINDOWS_FRAME_INFO_FPO] + .RetrieveRange(address, &frame_info))) { + result->CopyFrom(*frame_info.get()); + return result.release(); + } + + // Even without a relevant STACK line, many functions contain + // information about how much space their parameters consume on the + // stack. Use RetrieveNearestRange instead of RetrieveRange, so that + // we can use the function to bound the extent of the PUBLIC symbol, + // below. However, this does mean we need to check that ADDRESS + // falls within the retrieved function's range; do the range + // comparison in an overflow-friendly way. + linked_ptr function; + MemAddr function_base, function_size; + if (functions_.RetrieveNearestRange(address, &function, + &function_base, &function_size) && + address >= function_base && address - function_base < function_size) { + result->parameter_size = function->parameter_size; + result->valid |= WindowsFrameInfo::VALID_PARAMETER_SIZE; + return result.release(); + } + + // PUBLIC symbols might have a parameter size. Use the function we + // found above to limit the range the public symbol covers. + linked_ptr public_symbol; + MemAddr public_address; + if (public_symbols_.Retrieve(address, &public_symbol, &public_address) && + (!function.get() || public_address > function_base)) { + result->parameter_size = public_symbol->parameter_size; + } + + return NULL; +} + +CFIFrameInfo *BasicSourceLineResolver::Module::FindCFIFrameInfo( + const StackFrame *frame) const { + MemAddr address = frame->instruction - frame->module->base_address(); + MemAddr initial_base, initial_size; + string initial_rules; + + // Find the initial rule whose range covers this address. That + // provides an initial set of register recovery rules. Then, walk + // forward from the initial rule's starting address to frame's + // instruction address, applying delta rules. + if (!cfi_initial_rules_.RetrieveRange(address, &initial_rules, + &initial_base, &initial_size)) { + return NULL; + } + + // Create a frame info structure, and populate it with the rules from + // the STACK CFI INIT record. + scoped_ptr rules(new CFIFrameInfo()); + if (!ParseCFIRuleSet(initial_rules, rules.get())) + return NULL; + + // Find the first delta rule that falls within the initial rule's range. + map::const_iterator delta = + cfi_delta_rules_.lower_bound(initial_base); - if (!frame_info.get()) { - // Even without a relevant STACK line, many functions contain information - // about how much space their parameters consume on the stack. Prefer - // the STACK stuff (above), but if it's not present, take the - // information from the FUNC or PUBLIC line. - frame_info.reset(new StackFrameInfo()); - frame_info->parameter_size = parameter_size; - frame_info->valid |= StackFrameInfo::VALID_PARAMETER_SIZE; + // Apply delta rules up to and including the frame's address. + while (delta != cfi_delta_rules_.end() && delta->first <= address) { + ParseCFIRuleSet(delta->second, rules.get()); + delta++; } - return frame_info.release(); + return rules.release(); +} + +bool BasicSourceLineResolver::Module::ParseCFIRuleSet( + const string &rule_set, CFIFrameInfo *frame_info) const { + CFIFrameInfoParseHandler handler(frame_info); + CFIRuleParser parser(&handler); + return parser.Parse(rule_set); } // static @@ -521,49 +719,61 @@ } bool BasicSourceLineResolver::Module::ParseStackInfo(char *stack_info_line) { - // STACK WIN - // - // - // - // If has_program_string is 1, the rest of the line is a program string. - // Otherwise, the final token tells whether the stack info indicates that - // a base pointer has been allocated. - // - // Expect has_program_string to be 1 when type is STACK_INFO_FRAME_DATA and - // 0 when type is STACK_INFO_FPO, but don't enforce this. - // Skip "STACK " prefix. stack_info_line += 6; - vector tokens; - if (!Tokenize(stack_info_line, 12, &tokens)) + // Find the token indicating what sort of stack frame walking + // information this is. + while (*stack_info_line == ' ') + stack_info_line++; + const char *platform = stack_info_line; + while (!strchr(" \r\n", *stack_info_line)) + stack_info_line++; + *stack_info_line++ = '\0'; + + // MSVC stack frame info. + if (strcmp(platform, "WIN") == 0) + return ParseWindowsFrameInfo(stack_info_line); + + // DWARF CFI stack frame info + else if (strcmp(platform, "CFI") == 0) + return ParseCFIFrameInfo(stack_info_line); + + // Something unrecognized. + else return false; +} - // Only MSVC stack frame info is understood for now. - const char *platform = tokens[0]; - if (strcmp(platform, "WIN") != 0) +bool BasicSourceLineResolver::Module::ParseWindowsFrameInfo( + char *stack_info_line) { + // The format of a STACK WIN record is documented at: + // + // http://code.google.com/p/google-breakpad/wiki/SymbolFiles + + vector tokens; + if (!Tokenize(stack_info_line, 11, &tokens)) return false; - int type = strtol(tokens[1], NULL, 16); - if (type < 0 || type > STACK_INFO_LAST - 1) + int type = strtol(tokens[0], NULL, 16); + if (type < 0 || type > WINDOWS_FRAME_INFO_LAST - 1) return false; - u_int64_t rva = strtoull(tokens[2], NULL, 16); - u_int64_t code_size = strtoull(tokens[3], NULL, 16); - u_int32_t prolog_size = strtoul(tokens[4], NULL, 16); - u_int32_t epilog_size = strtoul(tokens[5], NULL, 16); - u_int32_t parameter_size = strtoul(tokens[6], NULL, 16); - u_int32_t saved_register_size = strtoul(tokens[7], NULL, 16); - u_int32_t local_size = strtoul(tokens[8], NULL, 16); - u_int32_t max_stack_size = strtoul(tokens[9], NULL, 16); - int has_program_string = strtoul(tokens[10], NULL, 16); + u_int64_t rva = strtoull(tokens[1], NULL, 16); + u_int64_t code_size = strtoull(tokens[2], NULL, 16); + u_int32_t prolog_size = strtoul(tokens[3], NULL, 16); + u_int32_t epilog_size = strtoul(tokens[4], NULL, 16); + u_int32_t parameter_size = strtoul(tokens[5], NULL, 16); + u_int32_t saved_register_size = strtoul(tokens[6], NULL, 16); + u_int32_t local_size = strtoul(tokens[7], NULL, 16); + u_int32_t max_stack_size = strtoul(tokens[8], NULL, 16); + int has_program_string = strtoul(tokens[9], NULL, 16); const char *program_string = ""; int allocates_base_pointer = 0; if (has_program_string) { - program_string = tokens[11]; + program_string = tokens[10]; } else { - allocates_base_pointer = strtoul(tokens[11], NULL, 16); + allocates_base_pointer = strtoul(tokens[10], NULL, 16); } // TODO(mmentovai): I wanted to use StoreRange's return value as this @@ -586,8 +796,8 @@ // if ContainedRangeMap were modified to allow replacement of // already-stored values. - linked_ptr stack_frame_info( - new StackFrameInfo(prolog_size, + linked_ptr stack_frame_info( + new WindowsFrameInfo(prolog_size, epilog_size, parameter_size, saved_register_size, @@ -595,20 +805,49 @@ max_stack_size, allocates_base_pointer, program_string)); - stack_info_[type].StoreRange(rva, code_size, stack_frame_info); + windows_frame_info_[type].StoreRange(rva, code_size, stack_frame_info); + + return true; +} + +bool BasicSourceLineResolver::Module::ParseCFIFrameInfo( + char *stack_info_line) { + char *cursor; + + // Is this an INIT record or a delta record? + char *init_or_address = strtok_r(stack_info_line, " \r\n", &cursor); + if (!init_or_address) + return false; + if (strcmp(init_or_address, "INIT") == 0) { + // This record has the form "STACK INIT
    ". + char *address_field = strtok_r(NULL, " \r\n", &cursor); + if (!address_field) return false; + + char *size_field = strtok_r(NULL, " \r\n", &cursor); + if (!size_field) return false; + + char *initial_rules = strtok_r(NULL, "\r\n", &cursor); + if (!initial_rules) return false; + + MemAddr address = strtoul(address_field, NULL, 16); + MemAddr size = strtoul(size_field, NULL, 16); + cfi_initial_rules_.StoreRange(address, size, initial_rules); + return true; + } + + // This record has the form "STACK
    ". + char *address_field = init_or_address; + char *delta_rules = strtok_r(NULL, "\r\n", &cursor); + if (!delta_rules) return false; + MemAddr address = strtoul(address_field, NULL, 16); + cfi_delta_rules_[address] = delta_rules; return true; } -#ifdef BSLR_NO_HASH_MAP bool BasicSourceLineResolver::CompareString::operator()( const string &s1, const string &s2) const { return strcmp(s1.c_str(), s2.c_str()) < 0; } -#else // BSLR_NO_HASH_MAP -size_t BasicSourceLineResolver::HashString::operator()(const string &s) const { - return hash()(s.c_str()); -} -#endif // BSLR_NO_HASH_MAP } // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver_unittest.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/basic_source_line_resolver_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -32,10 +32,12 @@ #include "google_breakpad/processor/basic_source_line_resolver.h" #include "google_breakpad/processor/code_module.h" #include "google_breakpad/processor/stack_frame.h" +#include "google_breakpad/processor/memory_region.h" #include "processor/linked_ptr.h" #include "processor/logging.h" #include "processor/scoped_ptr.h" -#include "processor/stack_frame_info.h" +#include "processor/windows_frame_info.h" +#include "processor/cfi_frame_info.h" #define ASSERT_TRUE(cond) \ if (!(cond)) { \ @@ -51,11 +53,13 @@ using std::string; using google_breakpad::BasicSourceLineResolver; +using google_breakpad::CFIFrameInfo; using google_breakpad::CodeModule; +using google_breakpad::MemoryRegion; +using google_breakpad::StackFrame; +using google_breakpad::WindowsFrameInfo; using google_breakpad::linked_ptr; using google_breakpad::scoped_ptr; -using google_breakpad::StackFrame; -using google_breakpad::StackFrameInfo; class TestCodeModule : public CodeModule { public: @@ -63,7 +67,7 @@ virtual ~TestCodeModule() {} virtual u_int64_t base_address() const { return 0; } - virtual u_int64_t size() const { return 0x4000; } + virtual u_int64_t size() const { return 0xb000; } virtual string code_file() const { return code_file_; } virtual string code_identifier() const { return ""; } virtual string debug_file() const { return ""; } @@ -77,6 +81,70 @@ string code_file_; }; +// A mock memory region object, for use by the STACK CFI tests. +class MockMemoryRegion: public MemoryRegion { + u_int64_t GetBase() const { return 0x10000; } + u_int32_t GetSize() const { return 0x01000; } + bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const { + *value = address & 0xff; + return true; + } + bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const { + *value = address & 0xffff; + return true; + } + bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const { + switch (address) { + case 0x10008: *value = 0x98ecadc3; break; // saved %ebx + case 0x1000c: *value = 0x878f7524; break; // saved %esi + case 0x10010: *value = 0x6312f9a5; break; // saved %edi + case 0x10014: *value = 0x10038; break; // caller's %ebp + case 0x10018: *value = 0xf6438648; break; // return address + default: *value = 0xdeadbeef; break; // junk + } + return true; + } + bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const { + *value = address; + return true; + } +}; + +// Verify that, for every association in ACTUAL, EXPECTED has the same +// association. (That is, ACTUAL's associations should be a subset of +// EXPECTED's.) Also verify that ACTUAL has associations for ".ra" and +// ".cfa". +static bool VerifyRegisters( + const char *file, int line, + const CFIFrameInfo::RegisterValueMap &expected, + const CFIFrameInfo::RegisterValueMap &actual) { + CFIFrameInfo::RegisterValueMap::const_iterator a; + a = actual.find(".cfa"); + ASSERT_TRUE(a != actual.end()); + a = actual.find(".ra"); + ASSERT_TRUE(a != actual.end()); + for (a = actual.begin(); a != actual.end(); a++) { + CFIFrameInfo::RegisterValueMap::const_iterator e = + expected.find(a->first); + if (e == expected.end()) { + fprintf(stderr, "%s:%d: unexpected register '%s' recovered, value 0x%x\n", + file, line, a->first.c_str(), a->second); + return false; + } + if (e->second != a->second) { + fprintf(stderr, + "%s:%d: register '%s' recovered value was 0x%x, expected 0x%x\n", + file, line, a->first.c_str(), a->second, e->second); + return false; + } + // Don't complain if this doesn't recover all registers. Although + // the DWARF spec says that unmentioned registers are undefined, + // GCC uses omission to mean that they are unchanged. + } + return true; +} + + static bool VerifyEmpty(const StackFrame &frame) { ASSERT_TRUE(frame.function_name.empty()); ASSERT_TRUE(frame.source_file_name.empty()); @@ -104,9 +172,11 @@ TestCodeModule module1("module1"); StackFrame frame; + scoped_ptr windows_frame_info; + scoped_ptr cfi_frame_info; frame.instruction = 0x1000; frame.module = NULL; - scoped_ptr frame_info(resolver.FillSourceLineInfo(&frame)); + resolver.FillSourceLineInfo(&frame); ASSERT_FALSE(frame.module); ASSERT_TRUE(frame.function_name.empty()); ASSERT_EQ(frame.function_base, 0); @@ -115,7 +185,7 @@ ASSERT_EQ(frame.source_line_base, 0); frame.module = &module1; - frame_info.reset(resolver.FillSourceLineInfo(&frame)); + resolver.FillSourceLineInfo(&frame); ASSERT_EQ(frame.function_name, "Function1_1"); ASSERT_TRUE(frame.module); ASSERT_EQ(frame.module->code_file(), "module1"); @@ -123,45 +193,152 @@ ASSERT_EQ(frame.source_file_name, "file1_1.cc"); ASSERT_EQ(frame.source_line, 44); ASSERT_EQ(frame.source_line_base, 0x1000); - ASSERT_TRUE(frame_info.get()); - ASSERT_FALSE(frame_info->allocates_base_pointer); - ASSERT_EQ(frame_info->program_string, + windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); + ASSERT_TRUE(windows_frame_info.get()); + ASSERT_FALSE(windows_frame_info->allocates_base_pointer); + ASSERT_EQ(windows_frame_info->program_string, "$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ ="); ClearSourceLineInfo(&frame); frame.instruction = 0x800; frame.module = &module1; - frame_info.reset(resolver.FillSourceLineInfo(&frame)); + resolver.FillSourceLineInfo(&frame); ASSERT_TRUE(VerifyEmpty(frame)); - ASSERT_FALSE(frame_info.get()); + windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); + ASSERT_FALSE(windows_frame_info.get()); frame.instruction = 0x1280; - frame_info.reset(resolver.FillSourceLineInfo(&frame)); + resolver.FillSourceLineInfo(&frame); ASSERT_EQ(frame.function_name, "Function1_3"); ASSERT_TRUE(frame.source_file_name.empty()); ASSERT_EQ(frame.source_line, 0); - ASSERT_TRUE(frame_info.get()); - ASSERT_FALSE(frame_info->allocates_base_pointer); - ASSERT_TRUE(frame_info->program_string.empty()); + windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); + ASSERT_TRUE(windows_frame_info.get()); + ASSERT_FALSE(windows_frame_info->allocates_base_pointer); + ASSERT_TRUE(windows_frame_info->program_string.empty()); frame.instruction = 0x1380; - frame_info.reset(resolver.FillSourceLineInfo(&frame)); + resolver.FillSourceLineInfo(&frame); ASSERT_EQ(frame.function_name, "Function1_4"); ASSERT_TRUE(frame.source_file_name.empty()); ASSERT_EQ(frame.source_line, 0); - ASSERT_TRUE(frame_info.get()); - ASSERT_FALSE(frame_info->allocates_base_pointer); - ASSERT_FALSE(frame_info->program_string.empty()); + windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); + ASSERT_TRUE(windows_frame_info.get()); + ASSERT_FALSE(windows_frame_info->allocates_base_pointer); + ASSERT_FALSE(windows_frame_info->program_string.empty()); frame.instruction = 0x2000; - frame_info.reset(resolver.FillSourceLineInfo(&frame)); - ASSERT_FALSE(frame_info.get()); + windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); + ASSERT_FALSE(windows_frame_info.get()); + + // module1 has STACK CFI records covering 3d40..3def; + // module2 has STACK CFI records covering 3df0..3e9f; + // check that FindCFIFrameInfo doesn't claim to find any outside those ranges. + frame.instruction = 0x3d3f; + frame.module = &module1; + cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame)); + ASSERT_FALSE(cfi_frame_info.get()); + + frame.instruction = 0x3e9f; + frame.module = &module1; + cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame)); + ASSERT_FALSE(cfi_frame_info.get()); + + CFIFrameInfo::RegisterValueMap current_registers; + CFIFrameInfo::RegisterValueMap caller_registers; + CFIFrameInfo::RegisterValueMap expected_caller_registers; + MockMemoryRegion memory; + + // Regardless of which instruction evaluation takes place at, it + // should produce the same values for the caller's registers. + expected_caller_registers[".cfa"] = 0x1001c; + expected_caller_registers[".ra"] = 0xf6438648; + expected_caller_registers["$ebp"] = 0x10038; + expected_caller_registers["$ebx"] = 0x98ecadc3; + expected_caller_registers["$esi"] = 0x878f7524; + expected_caller_registers["$edi"] = 0x6312f9a5; + + frame.instruction = 0x3d40; + frame.module = &module1; + current_registers.clear(); + current_registers["$esp"] = 0x10018; + current_registers["$ebp"] = 0x10038; + current_registers["$ebx"] = 0x98ecadc3; + current_registers["$esi"] = 0x878f7524; + current_registers["$edi"] = 0x6312f9a5; + cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers); + + frame.instruction = 0x3d41; + current_registers["$esp"] = 0x10014; + cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers); + + frame.instruction = 0x3d43; + current_registers["$ebp"] = 0x10014; + cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers); + + frame.instruction = 0x3d54; + current_registers["$ebx"] = 0x6864f054U; + cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers); + + frame.instruction = 0x3d5a; + current_registers["$esi"] = 0x6285f79aU; + cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers); + + frame.instruction = 0x3d84; + current_registers["$edi"] = 0x64061449U; + cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame)); + ASSERT_TRUE(cfi_frame_info.get()); + ASSERT_TRUE(cfi_frame_info.get() + ->FindCallerRegs(current_registers, memory, + &caller_registers)); + VerifyRegisters(__FILE__, __LINE__, + expected_caller_registers, caller_registers); + + frame.instruction = 0x2900; + frame.module = &module1; + resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, string("PublicSymbol")); + + frame.instruction = 0x4000; + frame.module = &module1; + resolver.FillSourceLineInfo(&frame); + ASSERT_EQ(frame.function_name, string("LargeFunction")); TestCodeModule module2("module2"); frame.instruction = 0x2181; frame.module = &module2; - frame_info.reset(resolver.FillSourceLineInfo(&frame)); + resolver.FillSourceLineInfo(&frame); ASSERT_EQ(frame.function_name, "Function2_2"); ASSERT_EQ(frame.function_base, 0x2170); ASSERT_TRUE(frame.module); @@ -169,8 +346,9 @@ ASSERT_EQ(frame.source_file_name, "file2_2.cc"); ASSERT_EQ(frame.source_line, 21); ASSERT_EQ(frame.source_line_base, 0x2180); - ASSERT_TRUE(frame_info.get()); - ASSERT_EQ(frame_info->prolog_size, 1); + windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame)); + ASSERT_TRUE(windows_frame_info.get()); + ASSERT_EQ(windows_frame_info->prolog_size, 1); frame.instruction = 0x216f; resolver.FillSourceLineInfo(&frame); diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,157 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// cfi_frame_info.cc: Implementation of CFIFrameInfo class. +// See cfi_frame_info.h for details. + +#include + +#include "processor/cfi_frame_info.h" +#include "processor/postfix_evaluator-inl.h" +#include "processor/scoped_ptr.h" + +namespace google_breakpad { + +template +bool CFIFrameInfo::FindCallerRegs(const RegisterValueMap ®isters, + const MemoryRegion &memory, + RegisterValueMap *caller_registers) const { + // If there are not rules for both .ra and .cfa in effect at this address, + // don't use this CFI data for stack walking. + if (cfa_rule_.empty() || ra_rule_.empty()) + return false; + + RegisterValueMap working; + PostfixEvaluator evaluator(&working, &memory); + + caller_registers->clear(); + + // First, compute the CFA. + V cfa; + working = registers; + if (!evaluator.EvaluateForValue(cfa_rule_, &cfa)) + return false; + + // Then, compute the return address. + V ra; + working = registers; + working[".cfa"] = cfa; + if (!evaluator.EvaluateForValue(ra_rule_, &ra)) + return false; + + // Now, compute values for all the registers register_rules_ mentions. + for (RuleMap::const_iterator it = register_rules_.begin(); + it != register_rules_.end(); it++) { + V value; + working = registers; + working[".cfa"] = cfa; + if (!evaluator.EvaluateForValue(it->second, &value)) + return false; + (*caller_registers)[it->first] = value; + } + + (*caller_registers)[".ra"] = ra; + (*caller_registers)[".cfa"] = cfa; + + return true; +} + +// Explicit instantiations for 32-bit and 64-bit architectures. +template bool CFIFrameInfo::FindCallerRegs( + const RegisterValueMap ®isters, + const MemoryRegion &memory, + RegisterValueMap *caller_registers) const; +template bool CFIFrameInfo::FindCallerRegs( + const RegisterValueMap ®isters, + const MemoryRegion &memory, + RegisterValueMap *caller_registers) const; + +bool CFIRuleParser::Parse(const string &rule_set) { + size_t rule_set_len = rule_set.size(); + scoped_array working_copy(new char[rule_set_len + 1]); + memcpy(working_copy.get(), rule_set.data(), rule_set_len); + working_copy[rule_set_len] = '\0'; + + name_.clear(); + expression_.clear(); + + char *cursor; + static const char token_breaks[] = " \t\r\n"; + char *token = strtok_r(working_copy.get(), token_breaks, &cursor); + + for (;;) { + // End of rule set? + if (!token) return Report(); + + // Register/pseudoregister name? + size_t token_len = strlen(token); + if (token_len >= 1 && token[token_len - 1] == ':') { + // Names can't be empty. + if (token_len < 2) return false; + // If there is any pending content, report it. + if (!name_.empty() || !expression_.empty()) { + if (!Report()) return false; + } + name_.assign(token, token_len - 1); + expression_.clear(); + } else { + // Another expression component. + assert(token_len > 0); // strtok_r guarantees this, I think. + if (!expression_.empty()) + expression_ += ' '; + expression_ += token; + } + token = strtok_r(NULL, token_breaks, &cursor); + } +} + +bool CFIRuleParser::Report() { + if (name_.empty() || expression_.empty()) return false; + if (name_ == ".cfa") handler_->CFARule(expression_); + else if (name_ == ".ra") handler_->RARule(expression_); + else handler_->RegisterRule(name_, expression_); + return true; +} + +void CFIFrameInfoParseHandler::CFARule(const string &expression) { + frame_info_->SetCFARule(expression); +} + +void CFIFrameInfoParseHandler::RARule(const string &expression) { + frame_info_->SetRARule(expression); +} + +void CFIFrameInfoParseHandler::RegisterRule(const string &name, + const string &expression) { + frame_info_->SetRegisterRule(name, expression); +} + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,271 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// cfi_frame_info.h: Define the CFIFrameInfo class, which holds the +// set of 'STACK CFI'-derived register recovery rules that apply at a +// given instruction. + +#ifndef PROCESSOR_CFI_FRAME_INFO_H_ +#define PROCESSOR_CFI_FRAME_INFO_H_ + +#include +#include + +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +using std::map; +using std::string; + +class MemoryRegion; + +// A set of rules for recovering the calling frame's registers' +// values, when the PC is at a given address in the current frame's +// function. See the description of 'STACK CFI' records at: +// +// http://code.google.com/p/google-breakpad/wiki/SymbolFiles +// +// To prepare an instance of CFIFrameInfo for use at a given +// instruction, first populate it with the rules from the 'STACK CFI +// INIT' record that covers that instruction, and then apply the +// changes given by the 'STACK CFI' records up to our instruction's +// address. Then, use the FindCallerRegs member function to apply the +// rules to the callee frame's register values, yielding the caller +// frame's register values. +class CFIFrameInfo { + public: + // A map from register names onto values. + template class RegisterValueMap: + public map { }; + + // Set the expression for computing a call frame address, return + // address, or register's value. At least the CFA rule and the RA + // rule must be set before calling FindCallerRegs. + void SetCFARule(const string &expression) { cfa_rule_ = expression; } + void SetRARule(const string &expression) { ra_rule_ = expression; } + void SetRegisterRule(const string ®ister_name, const string &expression) { + register_rules_[register_name] = expression; + } + + // Compute the values of the calling frame's registers, according to + // this rule set. Use ValueType in expression evaluation; this + // should be u_int32_t on machines with 32-bit addresses, or + // u_int64_t on machines with 64-bit addresses. + // + // Return true on success, false otherwise. + // + // MEMORY provides access to the contents of the stack. REGISTERS is + // a dictionary mapping the names of registers whose values are + // known in the current frame to their values. CALLER_REGISTERS is + // populated with the values of the recoverable registers in the + // frame that called the current frame. + // + // In addition, CALLER_REGISTERS[".ra"] will be the return address, + // and CALLER_REGISTERS[".cfa"] will be the call frame address. + // These may be helpful in computing the caller's PC and stack + // pointer, if their values are not explicitly specified. + template + bool FindCallerRegs(const RegisterValueMap ®isters, + const MemoryRegion &memory, + RegisterValueMap *caller_registers) const; + + private: + + // A map from register names onto evaluation rules. + typedef map RuleMap; + + // In this type, a "postfix expression" is an expression of the sort + // interpreted by google_breakpad::PostfixEvaluator. + + // A postfix expression for computing the current frame's CFA (call + // frame address). The CFA is a reference address for the frame that + // remains unchanged throughout the frame's lifetime. You should + // evaluate this expression with a dictionary initially populated + // with the values of the current frame's known registers. + string cfa_rule_; + + // The following expressions should be evaluated with a dictionary + // initially populated with the values of the current frame's known + // registers, and with ".cfa" set to the result of evaluating the + // cfa_rule expression, above. + + // A postfix expression for computing the current frame's return + // address. + string ra_rule_; + + // For a register named REG, rules[REG] is a postfix expression + // which leaves the value of REG in the calling frame on the top of + // the stack. You should evaluate this expression + RuleMap register_rules_; +}; + +// A parser for STACK CFI-style rule sets. +// This may seem bureaucratic: there's no legitimate run-time reason +// to use a parser/handler pattern for this, as it's not a likely +// reuse boundary. But doing so makes finer-grained unit testing +// possible. +class CFIRuleParser { + public: + + class Handler { + public: + Handler() { } + virtual ~Handler() { } + + // The input specifies EXPRESSION as the CFA/RA computation rule. + virtual void CFARule(const string &expression) = 0; + virtual void RARule(const string &expression) = 0; + + // The input specifies EXPRESSION as the recovery rule for register NAME. + virtual void RegisterRule(const string &name, const string &expression) = 0; + }; + + // Construct a parser which feeds its results to HANDLER. + CFIRuleParser(Handler *handler) : handler_(handler) { } + + // Parse RULE_SET as a set of CFA computation and RA/register + // recovery rules, as appearing in STACK CFI records. Report the + // results of parsing by making the appropriate calls to handler_. + // Return true if parsing was successful, false otherwise. + bool Parse(const string &rule_set); + + private: + // Report any accumulated rule to handler_ + bool Report(); + + // The handler to which the parser reports its findings. + Handler *handler_; + + // Working data. + string name_, expression_; +}; + +// A handler for rule set parsing that populates a CFIFrameInfo with +// the results. +class CFIFrameInfoParseHandler: public CFIRuleParser::Handler { + public: + // Populate FRAME_INFO with the results of parsing. + CFIFrameInfoParseHandler(CFIFrameInfo *frame_info) + : frame_info_(frame_info) { } + + void CFARule(const string &expression); + void RARule(const string &expression); + void RegisterRule(const string &name, const string &expression); + + private: + CFIFrameInfo *frame_info_; +}; + +// A utility class template for simple 'STACK CFI'-driven stack walkers. +// Given a CFIFrameInfo instance, a table describing the architecture's +// register set, and a context holding the last frame's registers, an +// instance of this class can populate a new context with the caller's +// registers. +// +// This class template doesn't use any internal knowledge of CFIFrameInfo +// or the other stack walking structures; it just uses the public interface +// of CFIFrameInfo to do the usual things. But the logic it handles should +// be common to many different architectures' stack walkers, so wrapping it +// up in a class should allow the walkers to share code. +// +// RegisterType should be the type of this architecture's registers, either +// u_int32_t or u_int64_t. RawContextType should be the raw context +// structure type for this architecture. +template +class SimpleCFIWalker { + public: + // A structure describing one architecture register. + struct RegisterSet { + // The register name, as it appears in STACK CFI rules. + const char *name; + + // An alternate name that the register's value might be found + // under in a register value dictionary, or NULL. When generating + // names, prefer NAME to this value. It's common to list ".cfa" as + // an alternative name for the stack pointer, and ".ra" as an + // alternative name for the instruction pointer. + const char *alternate_name; + + // True if the callee is expected to preserve the value of this + // register. If this flag is true for some register R, and the STACK + // CFI records provide no rule to recover R, then SimpleCFIWalker + // assumes that the callee has not changed R's value, and the caller's + // value for R is that currently in the callee's context. + bool callee_saves; + + // The ContextValidity flag representing the register's presence. + int validity_flag; + + // A pointer to the RawContextType member that holds the + // register's value. + RegisterType RawContextType::*context_member; + }; + + // Create a simple CFI-based frame walker, given a description of the + // architecture's register set. REGISTER_MAP is an array of + // RegisterSet structures; MAP_SIZE is the number of elements in the + // array. + SimpleCFIWalker(const RegisterSet *register_map, size_t map_size) + : register_map_(register_map), map_size_(map_size) { } + + // Compute the calling frame's raw context given the callee's raw + // context. + // + // Given: + // + // - MEMORY, holding the stack's contents, + // - CFI_FRAME_INFO, describing the called function, + // - CALLEE_CONTEXT, holding the called frame's registers, and + // - CALLEE_VALIDITY, indicating which registers in CALLEE_CONTEXT are valid, + // + // fill in CALLER_CONTEXT with the caller's register values, and set + // CALLER_VALIDITY to indicate which registers are valid in + // CALLER_CONTEXT. Return true on success, or false on failure. + bool FindCallerRegisters(const MemoryRegion &memory, + const CFIFrameInfo &cfi_frame_info, + const RawContextType &callee_context, + int callee_validity, + RawContextType *caller_context, + int *caller_validity) const; + + private: + const RegisterSet *register_map_; + size_t map_size_; +}; + +} // namespace google_breakpad + +#include "cfi_frame_info-inl.h" + +#endif // PROCESSOR_CFI_FRAME_INFO_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info-inl.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info-inl.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info-inl.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info-inl.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,119 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// cfi_frame_info-inl.h: Definitions for cfi_frame_info.h inlined functions. + +#ifndef PROCESSOR_CFI_FRAME_INFO_INL_H_ +#define PROCESSOR_CFI_FRAME_INFO_INL_H_ + +#include + +namespace google_breakpad { + +template +bool SimpleCFIWalker::FindCallerRegisters( + const MemoryRegion &memory, + const CFIFrameInfo &cfi_frame_info, + const RawContextType &callee_context, + int callee_validity, + RawContextType *caller_context, + int *caller_validity) const { + typedef CFIFrameInfo::RegisterValueMap ValueMap; + ValueMap callee_registers; + ValueMap caller_registers; + // Just for brevity. + typename ValueMap::const_iterator caller_none = caller_registers.end(); + + // Populate callee_registers with register values from callee_context. + for (size_t i = 0; i < map_size_; i++) { + const RegisterSet &r = register_map_[i]; + if (callee_validity & r.validity_flag) + callee_registers[r.name] = callee_context.*r.context_member; + } + + // Apply the rules, and see what register values they yield. + if (!cfi_frame_info.FindCallerRegs(callee_registers, memory, + &caller_registers)) + return false; + + // Populate *caller_context with the values the rules placed in + // caller_registers. + memset(caller_context, 0xda, sizeof(caller_context)); + *caller_validity = 0; + for (size_t i = 0; i < map_size_; i++) { + const RegisterSet &r = register_map_[i]; + typename ValueMap::const_iterator caller_entry; + + // Did the rules provide a value for this register by its name? + caller_entry = caller_registers.find(r.name); + if (caller_entry != caller_none) { + caller_context->*r.context_member = caller_entry->second; + *caller_validity |= r.validity_flag; + continue; + } + + // Did the rules provide a value for this register under its + // alternate name? + if (r.alternate_name) { + caller_entry = caller_registers.find(r.alternate_name); + if (caller_entry != caller_none) { + caller_context->*r.context_member = caller_entry->second; + *caller_validity |= r.validity_flag; + continue; + } + } + + // Is this a callee-saves register? The walker assumes that these + // still hold the caller's value if the CFI doesn't mention them. + // + // Note that other frame walkers may fail to recover callee-saves + // registers; for example, the x86 "traditional" strategy only + // recovers %eip, %esp, and %ebp, even though %ebx, %esi, and %edi + // are callee-saves, too. It is not correct to blindly set the + // valid bit for all callee-saves registers, without first + // checking its validity bit in the callee. + if (r.callee_saves && (callee_validity & r.validity_flag) != 0) { + caller_context->*r.context_member = callee_context.*r.context_member; + *caller_validity |= r.validity_flag; + continue; + } + + // Otherwise, the register's value is unknown. + } + + return true; +} + +} // namespace google_breakpad + +#endif // PROCESSOR_CFI_FRAME_INFO_INL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/cfi_frame_info_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,531 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// cfi_frame_info_unittest.cc: Unit tests for CFIFrameInfo, +// CFIRuleParser, CFIFrameInfoParseHandler, and SimpleCFIWalker. + +#include + +#include "breakpad_googletest_includes.h" +#include "processor/cfi_frame_info.h" +#include "google_breakpad/processor/memory_region.h" + +using google_breakpad::CFIFrameInfo; +using google_breakpad::CFIFrameInfoParseHandler; +using google_breakpad::CFIRuleParser; +using google_breakpad::MemoryRegion; +using google_breakpad::SimpleCFIWalker; +using std::string; +using testing::_; +using testing::A; +using testing::AtMost; +using testing::DoAll; +using testing::Return; +using testing::SetArgumentPointee; +using testing::Test; + +class MockMemoryRegion: public MemoryRegion { + public: + MOCK_CONST_METHOD0(GetBase, u_int64_t()); + MOCK_CONST_METHOD0(GetSize, u_int32_t()); + MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int8_t *)); + MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int16_t *)); + MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int32_t *)); + MOCK_CONST_METHOD2(GetMemoryAtAddress, bool(u_int64_t, u_int64_t *)); +}; + +// Handy definitions for all tests. +struct CFIFixture { + + // Set up the mock memory object to expect no references. + void ExpectNoMemoryReferences() { + EXPECT_CALL(memory, GetBase()).Times(0); + EXPECT_CALL(memory, GetSize()).Times(0); + EXPECT_CALL(memory, GetMemoryAtAddress(_, A())).Times(0); + EXPECT_CALL(memory, GetMemoryAtAddress(_, A())).Times(0); + EXPECT_CALL(memory, GetMemoryAtAddress(_, A())).Times(0); + EXPECT_CALL(memory, GetMemoryAtAddress(_, A())).Times(0); + } + + CFIFrameInfo cfi; + MockMemoryRegion memory; + CFIFrameInfo::RegisterValueMap registers, caller_registers; +}; + +class Simple: public CFIFixture, public Test { }; + +// FindCallerRegs should fail if no .cfa rule is provided. +TEST_F(Simple, NoCFA) { + ExpectNoMemoryReferences(); + + cfi.SetRARule("0"); + ASSERT_FALSE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); +} + +// FindCallerRegs should fail if no .ra rule is provided. +TEST_F(Simple, NoRA) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("0"); + ASSERT_FALSE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); +} + +TEST_F(Simple, SetCFAAndRARule) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("330903416631436410"); + cfi.SetRARule("5870666104170902211"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(2U, caller_registers.size()); + ASSERT_EQ(330903416631436410ULL, caller_registers[".cfa"]); + ASSERT_EQ(5870666104170902211ULL, caller_registers[".ra"]); +} + +TEST_F(Simple, SetManyRules) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("$temp1 68737028 = $temp2 61072337 = $temp1 $temp2 -"); + cfi.SetRARule(".cfa 99804755 +"); + cfi.SetRegisterRule("register1", ".cfa 54370437 *"); + cfi.SetRegisterRule("vodkathumbscrewingly", "24076308 .cfa +"); + cfi.SetRegisterRule("pubvexingfjordschmaltzy", ".cfa 29801007 -"); + cfi.SetRegisterRule("uncopyrightables", "92642917 .cfa /"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(6U, caller_registers.size()); + ASSERT_EQ(7664691U, caller_registers[".cfa"]); + ASSERT_EQ(107469446U, caller_registers[".ra"]); + ASSERT_EQ(416732599139967ULL, caller_registers["register1"]); + ASSERT_EQ(31740999U, caller_registers["vodkathumbscrewingly"]); + ASSERT_EQ(-22136316ULL, caller_registers["pubvexingfjordschmaltzy"]); + ASSERT_EQ(12U, caller_registers["uncopyrightables"]); +} + +TEST_F(Simple, RulesOverride) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("330903416631436410"); + cfi.SetRARule("5870666104170902211"); + cfi.SetCFARule("2828089117179001"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(2U, caller_registers.size()); + ASSERT_EQ(2828089117179001ULL, caller_registers[".cfa"]); + ASSERT_EQ(5870666104170902211ULL, caller_registers[".ra"]); +} + +class Scope: public CFIFixture, public Test { }; + +// There should be no value for .cfa in scope when evaluating the CFA rule. +TEST_F(Scope, CFALacksCFA) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule(".cfa"); + cfi.SetRARule("0"); + ASSERT_FALSE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); +} + +// There should be no value for .ra in scope when evaluating the CFA rule. +TEST_F(Scope, CFALacksRA) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule(".ra"); + cfi.SetRARule("0"); + ASSERT_FALSE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); +} + +// The current frame's registers should be in scope when evaluating +// the CFA rule. +TEST_F(Scope, CFASeesCurrentRegs) { + ExpectNoMemoryReferences(); + + registers[".baraminology"] = 0x06a7bc63e4f13893ULL; + registers[".ornithorhynchus"] = 0x5e0bf850bafce9d2ULL; + cfi.SetCFARule(".baraminology .ornithorhynchus +"); + cfi.SetRARule("0"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(2U, caller_registers.size()); + ASSERT_EQ(0x06a7bc63e4f13893ULL + 0x5e0bf850bafce9d2ULL, + caller_registers[".cfa"]); +} + +// .cfa should be in scope in the return address expression. +TEST_F(Scope, RASeesCFA) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("48364076"); + cfi.SetRARule(".cfa"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(2U, caller_registers.size()); + ASSERT_EQ(48364076U, caller_registers[".ra"]); +} + +// There should be no value for .ra in scope when evaluating the CFA rule. +TEST_F(Scope, RALacksRA) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("0"); + cfi.SetRARule(".ra"); + ASSERT_FALSE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); +} + +// The current frame's registers should be in scope in the return +// address expression. +TEST_F(Scope, RASeesCurrentRegs) { + ExpectNoMemoryReferences(); + + registers["noachian"] = 0x54dc4a5d8e5eb503ULL; + cfi.SetCFARule("10359370"); + cfi.SetRARule("noachian"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(2U, caller_registers.size()); + ASSERT_EQ(0x54dc4a5d8e5eb503ULL, caller_registers[".ra"]); +} + +// .cfa should be in scope for register rules. +TEST_F(Scope, RegistersSeeCFA) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("6515179"); + cfi.SetRARule(".cfa"); + cfi.SetRegisterRule("rogerian", ".cfa"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(3U, caller_registers.size()); + ASSERT_EQ(6515179U, caller_registers["rogerian"]); +} + +// The return address should not be in scope for register rules. +TEST_F(Scope, RegsLackRA) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("42740329"); + cfi.SetRARule("27045204"); + cfi.SetRegisterRule("$r1", ".ra"); + ASSERT_FALSE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); +} + +// Register rules can see the current frame's register values. +TEST_F(Scope, RegsSeeRegs) { + ExpectNoMemoryReferences(); + + registers["$r1"] = 0x6ed3582c4bedb9adULL; + registers["$r2"] = 0xd27d9e742b8df6d0ULL; + cfi.SetCFARule("88239303"); + cfi.SetRARule("30503835"); + cfi.SetRegisterRule("$r1", "$r1 42175211 = $r2"); + cfi.SetRegisterRule("$r2", "$r2 21357221 = $r1"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(4U, caller_registers.size()); + ASSERT_EQ(0xd27d9e742b8df6d0ULL, caller_registers["$r1"]); + ASSERT_EQ(0x6ed3582c4bedb9adULL, caller_registers["$r2"]); +} + +// Each rule's temporaries are separate. +TEST_F(Scope, SeparateTempsRA) { + ExpectNoMemoryReferences(); + + cfi.SetCFARule("$temp1 76569129 = $temp1"); + cfi.SetRARule("0"); + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + + cfi.SetCFARule("$temp1 76569129 = $temp1"); + cfi.SetRARule("$temp1"); + ASSERT_FALSE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); +} + +class MockCFIRuleParserHandler: public CFIRuleParser::Handler { + public: + MOCK_METHOD1(CFARule, void(const string &)); + MOCK_METHOD1(RARule, void(const string &)); + MOCK_METHOD2(RegisterRule, void(const string &, const string &)); +}; + +// A fixture class for testing CFIRuleParser. +class CFIParserFixture { + public: + CFIParserFixture() : parser(&mock_handler) { + // Expect no parsing results to be reported to mock_handler. Individual + // tests can override this. + EXPECT_CALL(mock_handler, CFARule(_)).Times(0); + EXPECT_CALL(mock_handler, RARule(_)).Times(0); + EXPECT_CALL(mock_handler, RegisterRule(_, _)).Times(0); + } + + MockCFIRuleParserHandler mock_handler; + CFIRuleParser parser; +}; + +class Parser: public CFIParserFixture, public Test { }; + +TEST_F(Parser, Empty) { + EXPECT_FALSE(parser.Parse("")); +} + +TEST_F(Parser, LoneColon) { + EXPECT_FALSE(parser.Parse(":")); +} + +TEST_F(Parser, CFANoExpr) { + EXPECT_FALSE(parser.Parse(".cfa:")); +} + +TEST_F(Parser, CFANoColonNoExpr) { + EXPECT_FALSE(parser.Parse(".cfa")); +} + +TEST_F(Parser, RANoExpr) { + EXPECT_FALSE(parser.Parse(".ra:")); +} + +TEST_F(Parser, RANoColonNoExpr) { + EXPECT_FALSE(parser.Parse(".ra")); +} + +TEST_F(Parser, RegNoExpr) { + EXPECT_FALSE(parser.Parse("reg:")); +} + +TEST_F(Parser, NoName) { + EXPECT_FALSE(parser.Parse("expr")); +} + +TEST_F(Parser, NoNameTwo) { + EXPECT_FALSE(parser.Parse("expr1 expr2")); +} + +TEST_F(Parser, StartsWithExpr) { + EXPECT_FALSE(parser.Parse("expr1 reg: expr2")); +} + +TEST_F(Parser, CFA) { + EXPECT_CALL(mock_handler, CFARule("spleen")).WillOnce(Return()); + EXPECT_TRUE(parser.Parse(".cfa: spleen")); +} + +TEST_F(Parser, RA) { + EXPECT_CALL(mock_handler, RARule("notoriety")).WillOnce(Return()); + EXPECT_TRUE(parser.Parse(".ra: notoriety")); +} + +TEST_F(Parser, Reg) { + EXPECT_CALL(mock_handler, RegisterRule("nemo", "mellifluous")) + .WillOnce(Return()); + EXPECT_TRUE(parser.Parse("nemo: mellifluous")); +} + +TEST_F(Parser, CFARARegs) { + EXPECT_CALL(mock_handler, CFARule("cfa expression")).WillOnce(Return()); + EXPECT_CALL(mock_handler, RARule("ra expression")).WillOnce(Return()); + EXPECT_CALL(mock_handler, RegisterRule("galba", "praetorian")) + .WillOnce(Return()); + EXPECT_CALL(mock_handler, RegisterRule("otho", "vitellius")) + .WillOnce(Return()); + EXPECT_TRUE(parser.Parse(".cfa: cfa expression .ra: ra expression " + "galba: praetorian otho: vitellius")); +} + +TEST_F(Parser, Whitespace) { + EXPECT_CALL(mock_handler, RegisterRule("r1", "r1 expression")) + .WillOnce(Return()); + EXPECT_CALL(mock_handler, RegisterRule("r2", "r2 expression")) + .WillOnce(Return()); + EXPECT_TRUE(parser.Parse(" r1:\tr1\nexpression \tr2:\t\rr2\r\n " + "expression \n")); +} + +TEST_F(Parser, WhitespaceLoneColon) { + EXPECT_FALSE(parser.Parse(" \n:\t ")); +} + +TEST_F(Parser, EmptyName) { + EXPECT_CALL(mock_handler, RegisterRule("reg", _)) + .Times(AtMost(1)) + .WillRepeatedly(Return()); + EXPECT_FALSE(parser.Parse("reg: expr1 : expr2")); +} + +TEST_F(Parser, RuleLoneColon) { + EXPECT_CALL(mock_handler, RegisterRule("r1", "expr")) + .Times(AtMost(1)) + .WillRepeatedly(Return()); + EXPECT_FALSE(parser.Parse(" r1: expr :")); +} + +TEST_F(Parser, RegNoExprRule) { + EXPECT_CALL(mock_handler, RegisterRule("r1", "expr")) + .Times(AtMost(1)) + .WillRepeatedly(Return()); + EXPECT_FALSE(parser.Parse("r0: r1: expr")); +} + +class ParseHandlerFixture: public CFIFixture { + public: + ParseHandlerFixture() : CFIFixture(), handler(&cfi) { } + CFIFrameInfoParseHandler handler; +}; + +class ParseHandler: public ParseHandlerFixture, public Test { }; + +TEST_F(ParseHandler, CFARARule) { + handler.CFARule("reg-for-cfa"); + handler.RARule("reg-for-ra"); + registers["reg-for-cfa"] = 0x268a9a4a3821a797ULL; + registers["reg-for-ra"] = 0x6301b475b8b91c02ULL; + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(0x268a9a4a3821a797ULL, caller_registers[".cfa"]); + ASSERT_EQ(0x6301b475b8b91c02ULL, caller_registers[".ra"]); +} + +TEST_F(ParseHandler, RegisterRules) { + handler.CFARule("reg-for-cfa"); + handler.RARule("reg-for-ra"); + handler.RegisterRule("reg1", "reg-for-reg1"); + handler.RegisterRule("reg2", "reg-for-reg2"); + registers["reg-for-cfa"] = 0x268a9a4a3821a797ULL; + registers["reg-for-ra"] = 0x6301b475b8b91c02ULL; + registers["reg-for-reg1"] = 0x06cde8e2ff062481ULL; + registers["reg-for-reg2"] = 0xff0c4f76403173e2ULL; + ASSERT_TRUE(cfi.FindCallerRegs(registers, memory, + &caller_registers)); + ASSERT_EQ(0x268a9a4a3821a797ULL, caller_registers[".cfa"]); + ASSERT_EQ(0x6301b475b8b91c02ULL, caller_registers[".ra"]); + ASSERT_EQ(0x06cde8e2ff062481ULL, caller_registers["reg1"]); + ASSERT_EQ(0xff0c4f76403173e2ULL, caller_registers["reg2"]); +} + +struct SimpleCFIWalkerFixture { + struct RawContext { + u_int64_t r0, r1, r2, r3, r4, sp, pc; + }; + enum Validity { + R0_VALID = 0x01, + R1_VALID = 0x02, + R2_VALID = 0x04, + R3_VALID = 0x08, + R4_VALID = 0x10, + SP_VALID = 0x20, + PC_VALID = 0x40 + }; + typedef SimpleCFIWalker CFIWalker; + + SimpleCFIWalkerFixture() + : walker(register_map, + sizeof(register_map) / sizeof(register_map[0])) { } + + static CFIWalker::RegisterSet register_map[7]; + CFIFrameInfo call_frame_info; + CFIWalker walker; + MockMemoryRegion memory; + RawContext callee_context, caller_context; +}; + +SimpleCFIWalkerFixture::CFIWalker::RegisterSet +SimpleCFIWalkerFixture::register_map[7] = { + { "r0", NULL, true, R0_VALID, &RawContext::r0 }, + { "r1", NULL, true, R1_VALID, &RawContext::r1 }, + { "r2", NULL, false, R2_VALID, &RawContext::r2 }, + { "r3", NULL, false, R3_VALID, &RawContext::r3 }, + { "r4", NULL, true, R4_VALID, &RawContext::r4 }, + { "sp", ".cfa", true, SP_VALID, &RawContext::sp }, + { "pc", ".ra", true, PC_VALID, &RawContext::pc }, +}; + +class SimpleWalker: public SimpleCFIWalkerFixture, public Test { }; + +TEST_F(SimpleWalker, Walk) { + // Stack_top is the current stack pointer, pointing to the lowest + // address of a frame that looks like this (all 64-bit words): + // + // sp -> saved r0 + // garbage + // return address + // cfa -> + // + // r0 has been saved on the stack. + // r1 has been saved in r2. + // r2 and r3 are not recoverable. + // r4 is not recoverable, even though it is a callee-saves register. + // Some earlier frame's unwinder must have failed to recover it. + + u_int64_t stack_top = 0x83254944b20d5512ULL; + + // Saved r0. + EXPECT_CALL(memory, + GetMemoryAtAddress(stack_top, A())) + .WillRepeatedly(DoAll(SetArgumentPointee<1>(0xdc1975eba8602302ULL), + Return(true))); + // Saved return address. + EXPECT_CALL(memory, + GetMemoryAtAddress(stack_top + 16, A())) + .WillRepeatedly(DoAll(SetArgumentPointee<1>(0xba5ad6d9acce28deULL), + Return(true))); + + call_frame_info.SetCFARule("sp 24 +"); + call_frame_info.SetRARule(".cfa 8 - ^"); + call_frame_info.SetRegisterRule("r0", ".cfa 24 - ^"); + call_frame_info.SetRegisterRule("r1", "r2"); + + callee_context.r0 = 0x94e030ca79edd119ULL; + callee_context.r1 = 0x937b4d7e95ce52d9ULL; + callee_context.r2 = 0x5fe0027416b8b62aULL; // caller's r1 + // callee_context.r3 is not valid in callee. + // callee_context.r4 is not valid in callee. + callee_context.sp = stack_top; + callee_context.pc = 0x25b21b224311d280ULL; + int callee_validity = R0_VALID | R1_VALID | R2_VALID | SP_VALID | PC_VALID; + + memset(&caller_context, 0, sizeof(caller_context)); + + int caller_validity; + EXPECT_TRUE(walker.FindCallerRegisters(memory, call_frame_info, + callee_context, callee_validity, + &caller_context, &caller_validity)); + EXPECT_EQ(R0_VALID | R1_VALID | SP_VALID | PC_VALID, caller_validity); + EXPECT_EQ(0xdc1975eba8602302ULL, caller_context.r0); + EXPECT_EQ(0x5fe0027416b8b62aULL, caller_context.r1); + EXPECT_EQ(stack_top + 24, caller_context.sp); + EXPECT_EQ(0xba5ad6d9acce28deULL, caller_context.pc); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/contained_range_map-inl.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/contained_range_map-inl.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/contained_range_map-inl.h 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/contained_range_map-inl.h 2010-04-16 17:32:47.000000000 +0100 @@ -60,8 +60,12 @@ // Check for undersize or overflow. if (size <= 0 || high < base) { - BPLOG(INFO) << "StoreRange failed, " << HexString(base) << "+" << - HexString(size) << ", " << HexString(high); + //TODO(nealsid) We are commenting this out in order to prevent + // excessive logging. We plan to move to better logging as this + // failure happens quite often and is expected(see comment in + // basic_source_line_resolver.cc:671). + // BPLOG(INFO) << "StoreRange failed, " << HexString(base) << "+" + // << HexString(size) << ", " << HexString(high); return false; } @@ -81,8 +85,9 @@ // it must fail. iterator_base->first contains the key, which was the // containing child's high address. if (iterator_base->second->base_ == base && iterator_base->first == high) { - BPLOG(INFO) << "StoreRange failed, identical range is already " - "present: " << HexString(base) << "+" << HexString(size); + // TODO(nealsid): See the TODO above on why this is commented out. +// BPLOG(INFO) << "StoreRange failed, identical range is already " +// "present: " << HexString(base) << "+" << HexString(size); return false; } diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump.cc 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -50,6 +50,8 @@ #endif // _WIN32 #include +#include +#include #include #include #include @@ -66,6 +68,8 @@ namespace google_breakpad { +using std::istream; +using std::ifstream; using std::numeric_limits; using std::vector; @@ -243,6 +247,15 @@ return out.release(); } +// Return the smaller of the number of code units in the UTF-16 string, +// not including the terminating null word, or maxlen. +static size_t UTF16codeunits(const u_int16_t *string, size_t maxlen) { + size_t count = 0; + while (count < maxlen && string[count] != 0) + count++; + return count; +} + // // MinidumpObject @@ -607,9 +620,65 @@ break; } + case MD_CONTEXT_ARM: { + if (expected_size != sizeof(MDRawContextARM)) { + BPLOG(ERROR) << "MinidumpContext arm size mismatch, " << + expected_size << " != " << sizeof(MDRawContextARM); + return false; + } + + scoped_ptr context_arm(new MDRawContextARM()); + + // Set the context_flags member, which has already been read, and + // read the rest of the structure beginning with the first member + // after context_flags. + context_arm->context_flags = context_flags; + + size_t flags_size = sizeof(context_arm->context_flags); + u_int8_t* context_after_flags = + reinterpret_cast(context_arm.get()) + flags_size; + if (!minidump_->ReadBytes(context_after_flags, + sizeof(MDRawContextARM) - flags_size)) { + BPLOG(ERROR) << "MinidumpContext could not read arm context"; + return false; + } + + // Do this after reading the entire MDRawContext structure because + // GetSystemInfo may seek minidump to a new position. + if (!CheckAgainstSystemInfo(cpu_type)) { + BPLOG(ERROR) << "MinidumpContext arm does not match system info"; + return false; + } + + if (minidump_->swap()) { + // context_arm->context_flags was already swapped. + for (unsigned int ireg_index = 0; + ireg_index < MD_CONTEXT_ARM_GPR_COUNT; + ++ireg_index) { + Swap(&context_arm->iregs[ireg_index]); + } + Swap(&context_arm->cpsr); + Swap(&context_arm->float_save.fpscr); + for (unsigned int fpr_index = 0; + fpr_index < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT; + ++fpr_index) { + Swap(&context_arm->float_save.regs[fpr_index]); + } + for (unsigned int fpe_index = 0; + fpe_index < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT; + ++fpe_index) { + Swap(&context_arm->float_save.extra[fpe_index]); + } + } + context_.arm = context_arm.release(); + + break; + } + default: { - // Unknown context type - BPLOG(ERROR) << "MinidumpContext unknown context type " << + // Unknown context type - Don't log as an error yet. Let the + // caller work that out. + BPLOG(INFO) << "MinidumpContext unknown context type " << HexString(cpu_type); return false; break; @@ -671,6 +740,15 @@ return context_.ctx_sparc; } +const MDRawContextARM* MinidumpContext::GetContextARM() const { + if (GetContextCPU() != MD_CONTEXT_ARM) { + BPLOG(ERROR) << "MinidumpContext cannot get arm context"; + return NULL; + } + + return context_.arm; +} + void MinidumpContext::FreeContext() { switch (GetContextCPU()) { case MD_CONTEXT_X86: @@ -689,6 +767,10 @@ delete context_.ctx_sparc; break; + case MD_CONTEXT_ARM: + delete context_.arm; + break; + default: // There is no context record (valid_ is false) or there's a // context record for an unknown CPU (shouldn't happen, only known @@ -748,6 +830,11 @@ if (system_info_cpu_type == MD_CPU_ARCHITECTURE_SPARC) return_value = true; break; + + case MD_CONTEXT_ARM: + if (system_info_cpu_type == MD_CPU_ARCHITECTURE_ARM) + return_value = true; + break; } BPLOG_IF(ERROR, !return_value) << "MinidumpContext CPU " << @@ -953,6 +1040,36 @@ break; } + case MD_CONTEXT_ARM: { + const MDRawContextARM* context_arm = GetContextARM(); + printf("MDRawContextARM\n"); + printf(" context_flags = 0x%x\n", + context_arm->context_flags); + for (unsigned int ireg_index = 0; + ireg_index < MD_CONTEXT_ARM_GPR_COUNT; + ++ireg_index) { + printf(" iregs[%2d] = 0x%x\n", + ireg_index, context_arm->iregs[ireg_index]); + } + printf(" cpsr = 0x%x\n", context_arm->cpsr); + printf(" float_save.fpscr = 0x%" PRIx64 "\n", + context_arm->float_save.fpscr); + for (unsigned int fpr_index = 0; + fpr_index < MD_FLOATINGSAVEAREA_ARM_FPR_COUNT; + ++fpr_index) { + printf(" float_save.regs[%2d] = 0x%" PRIx64 "\n", + fpr_index, context_arm->float_save.regs[fpr_index]); + } + for (unsigned int fpe_index = 0; + fpe_index < MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT; + ++fpe_index) { + printf(" float_save.extra[%2d] = 0x%" PRIx32 "\n", + fpe_index, context_arm->float_save.extra[fpe_index]); + } + + break; + } + default: { break; } @@ -989,7 +1106,7 @@ } -const u_int8_t* MinidumpMemoryRegion::GetMemory() { +const u_int8_t* MinidumpMemoryRegion::GetMemory() const { if (!valid_) { BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetMemory"; return NULL; @@ -1028,7 +1145,7 @@ } -u_int64_t MinidumpMemoryRegion::GetBase() { +u_int64_t MinidumpMemoryRegion::GetBase() const { if (!valid_) { BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetBase"; return static_cast(-1); @@ -1038,7 +1155,7 @@ } -u_int32_t MinidumpMemoryRegion::GetSize() { +u_int32_t MinidumpMemoryRegion::GetSize() const { if (!valid_) { BPLOG(ERROR) << "Invalid MinidumpMemoryRegion for GetSize"; return 0; @@ -1056,7 +1173,7 @@ template bool MinidumpMemoryRegion::GetMemoryAtAddressInternal(u_int64_t address, - T* value) { + T* value) const { BPLOG_IF(ERROR, !value) << "MinidumpMemoryRegion::GetMemoryAtAddressInternal " "requires |value|"; assert(value); @@ -1098,25 +1215,25 @@ bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address, - u_int8_t* value) { + u_int8_t* value) const { return GetMemoryAtAddressInternal(address, value); } bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address, - u_int16_t* value) { + u_int16_t* value) const { return GetMemoryAtAddressInternal(address, value); } bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address, - u_int32_t* value) { + u_int32_t* value) const { return GetMemoryAtAddressInternal(address, value); } bool MinidumpMemoryRegion::GetMemoryAtAddress(u_int64_t address, - u_int64_t* value) { + u_int64_t* value) const { return GetMemoryAtAddressInternal(address, value); } @@ -1302,7 +1419,7 @@ // -u_int32_t MinidumpThreadList::max_threads_ = 256; +u_int32_t MinidumpThreadList::max_threads_ = 4096; MinidumpThreadList::MinidumpThreadList(Minidump* minidump) @@ -1464,8 +1581,8 @@ // -u_int32_t MinidumpModule::max_cv_bytes_ = 1024; -u_int32_t MinidumpModule::max_misc_bytes_ = 1024; +u_int32_t MinidumpModule::max_cv_bytes_ = 32768; +u_int32_t MinidumpModule::max_misc_bytes_ = 32768; MinidumpModule::MinidumpModule(Minidump* minidump) @@ -2424,7 +2541,7 @@ // -u_int32_t MinidumpMemoryList::max_regions_ = 256; +u_int32_t MinidumpMemoryList::max_regions_ = 4096; MinidumpMemoryList::MinidumpMemoryList(Minidump* minidump) @@ -2713,8 +2830,10 @@ scoped_ptr context(new MinidumpContext(minidump_)); + // Don't log as an error if we can still fall back on the thread's context + // (which must be possible if we got this far.) if (!context->Read(exception_.thread_context.data_size)) { - BPLOG(ERROR) << "MinidumpException cannot read context"; + BPLOG(INFO) << "MinidumpException cannot read context"; return NULL; } @@ -2765,6 +2884,109 @@ } } +// +// MinidumpAssertion +// + + +MinidumpAssertion::MinidumpAssertion(Minidump* minidump) + : MinidumpStream(minidump), + assertion_(), + expression_(), + function_(), + file_() { +} + + +MinidumpAssertion::~MinidumpAssertion() { +} + + +bool MinidumpAssertion::Read(u_int32_t expected_size) { + // Invalidate cached data. + valid_ = false; + + if (expected_size != sizeof(assertion_)) { + BPLOG(ERROR) << "MinidumpAssertion size mismatch, " << expected_size << + " != " << sizeof(assertion_); + return false; + } + + if (!minidump_->ReadBytes(&assertion_, sizeof(assertion_))) { + BPLOG(ERROR) << "MinidumpAssertion cannot read assertion"; + return false; + } + + // Each of {expression, function, file} is a UTF-16 string, + // we'll convert them to UTF-8 for ease of use. + // expression + // Since we don't have an explicit byte length for each string, + // we use UTF16codeunits to calculate word length, then derive byte + // length from that. + u_int32_t word_length = UTF16codeunits(assertion_.expression, + sizeof(assertion_.expression)); + if (word_length > 0) { + u_int32_t byte_length = word_length * 2; + vector expression_utf16(word_length); + memcpy(&expression_utf16[0], &assertion_.expression[0], byte_length); + + scoped_ptr new_expression(UTF16ToUTF8(expression_utf16, + minidump_->swap())); + expression_ = *new_expression; + } + + // assertion + word_length = UTF16codeunits(assertion_.function, + sizeof(assertion_.function)); + if (word_length) { + u_int32_t byte_length = word_length * 2; + vector function_utf16(word_length); + memcpy(&function_utf16[0], &assertion_.function[0], byte_length); + scoped_ptr new_function(UTF16ToUTF8(function_utf16, + minidump_->swap())); + function_ = *new_function; + } + + // file + word_length = UTF16codeunits(assertion_.file, + sizeof(assertion_.file)); + if (word_length > 0) { + u_int32_t byte_length = word_length * 2; + vector file_utf16(word_length); + memcpy(&file_utf16[0], &assertion_.file[0], byte_length); + scoped_ptr new_file(UTF16ToUTF8(file_utf16, + minidump_->swap())); + file_ = *new_file; + } + + if (minidump_->swap()) { + Swap(&assertion_.line); + Swap(&assertion_.type); + } + + valid_ = true; + return true; +} + +void MinidumpAssertion::Print() { + if (!valid_) { + BPLOG(ERROR) << "MinidumpAssertion cannot print invalid data"; + return; + } + + printf("MDAssertion\n"); + printf(" expression = %s\n", + expression_.c_str()); + printf(" function = %s\n", + function_.c_str()); + printf(" file = %s\n", + file_.c_str()); + printf(" line = %u\n", + assertion_.line); + printf(" type = %u\n", + assertion_.type); + printf("\n"); +} // // MinidumpSystemInfo @@ -2887,6 +3109,10 @@ cpu = "x86"; break; + case MD_CPU_ARCHITECTURE_AMD64: + cpu = "x86-64"; + break; + case MD_CPU_ARCHITECTURE_PPC: cpu = "ppc"; break; @@ -2895,6 +3121,10 @@ cpu = "sparc"; break; + case MD_CPU_ARCHITECTURE_ARM: + cpu = "arm"; + break; + default: BPLOG(ERROR) << "MinidumpSystemInfo unknown CPU for architecture " << HexString(system_info_.processor_architecture); @@ -3216,35 +3446,44 @@ directory_(NULL), stream_map_(new MinidumpStreamMap()), path_(path), - fd_(-1), + stream_(NULL), swap_(false), valid_(false) { } +Minidump::Minidump(istream& stream) + : header_(), + directory_(NULL), + stream_map_(new MinidumpStreamMap()), + path_(), + stream_(&stream), + swap_(false), + valid_(false) { +} Minidump::~Minidump() { + if (stream_) { + BPLOG(INFO) << "Minidump closing minidump"; + } + if (!path_.empty()) { + delete stream_; + } delete directory_; delete stream_map_; - if (fd_ != -1) { - BPLOG(INFO) << "Minidump closing minidump on fd " << fd_; - close(fd_); - } } bool Minidump::Open() { - if (fd_ != -1) { - BPLOG(INFO) << "Minidump reopening minidump " << path_ << " on fd " << fd_; + if (stream_ != NULL) { + BPLOG(INFO) << "Minidump reopening minidump " << path_; // The file is already open. Seek to the beginning, which is the position // the file would be at if it were opened anew. return SeekSet(0); } - // O_BINARY is useful (and defined) on Windows. On other platforms, it's - // useless, and because it's defined as 0 above, harmless. - fd_ = open(path_.c_str(), O_RDONLY | O_BINARY); - if (fd_ == -1) { + stream_ = new ifstream(path_.c_str(), std::ios::in | std::ios::binary); + if (!stream_ || !stream_->good()) { string error_string; int error_code = ErrnoString(&error_string); BPLOG(ERROR) << "Minidump could not open minidump " << path_ << @@ -3252,7 +3491,7 @@ return false; } - BPLOG(INFO) << "Minidump opened minidump " << path_ << " on fd " << fd_; + BPLOG(INFO) << "Minidump opened minidump " << path_; return true; } @@ -3412,6 +3651,11 @@ return GetStream(&exception); } +MinidumpAssertion* Minidump::GetAssertion() { + MinidumpAssertion* assertion; + return GetStream(&assertion); +} + MinidumpSystemInfo* Minidump::GetSystemInfo() { MinidumpSystemInfo* system_info; @@ -3497,12 +3741,14 @@ bool Minidump::ReadBytes(void* bytes, size_t count) { // Can't check valid_ because Read needs to call this method before - // validity can be determined. The only member that this method - // depends on is mFD, and an unset or invalid fd may generate an - // error but should not cause a crash. - ssize_t bytes_read = read(fd_, bytes, count); - if (static_cast(bytes_read) != count) { - if (bytes_read == -1) { + // validity can be determined. + if (!stream_) { + return false; + } + stream_->read(static_cast(bytes), count); + size_t bytes_read = stream_->gcount(); + if (bytes_read != count) { + if (bytes_read == size_t(-1)) { string error_string; int error_code = ErrnoString(&error_string); BPLOG(ERROR) << "ReadBytes: error " << error_code << ": " << error_string; @@ -3517,23 +3763,28 @@ bool Minidump::SeekSet(off_t offset) { // Can't check valid_ because Read needs to call this method before - // validity can be determined. The only member that this method - // depends on is mFD, and an unset or invalid fd may generate an - // error but should not cause a crash. - off_t sought = lseek(fd_, offset, SEEK_SET); - if (sought != offset) { - if (sought == -1) { - string error_string; - int error_code = ErrnoString(&error_string); - BPLOG(ERROR) << "SeekSet: error " << error_code << ": " << error_string; - } else { - BPLOG(ERROR) << "SeekSet: sought " << sought << "/" << offset; - } + // validity can be determined. + if (!stream_) { + return false; + } + stream_->seekg(offset, std::ios_base::beg); + if (!stream_->good()) { + string error_string; + int error_code = ErrnoString(&error_string); + BPLOG(ERROR) << "SeekSet: error " << error_code << ": " << error_string; return false; } return true; } +off_t Minidump::Tell() { + if (!valid_ || !stream_) { + return (off_t)-1; + } + + return stream_->tellg(); +} + string* Minidump::ReadString(off_t offset) { if (!valid_) { diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_dump.cc 2010-04-16 17:32:47.000000000 +0100 @@ -44,6 +44,7 @@ using google_breakpad::MinidumpModuleList; using google_breakpad::MinidumpMemoryList; using google_breakpad::MinidumpException; +using google_breakpad::MinidumpAssertion; using google_breakpad::MinidumpSystemInfo; using google_breakpad::MinidumpMiscInfo; using google_breakpad::MinidumpBreakpadInfo; @@ -89,6 +90,13 @@ exception->Print(); } + MinidumpAssertion *assertion = minidump.GetAssertion(); + if (!assertion) { + BPLOG(INFO) << "minidump.GetAssertion() failed"; + } else { + assertion->Print(); + } + MinidumpSystemInfo *system_info = minidump.GetSystemInfo(); if (!system_info) { ++errors; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor.cc 2010-04-16 17:32:47.000000000 +0100 @@ -28,6 +28,7 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include +#include #include "google_breakpad/processor/minidump_processor.h" #include "google_breakpad/processor/call_stack.h" @@ -47,48 +48,48 @@ MinidumpProcessor::~MinidumpProcessor() { } -MinidumpProcessor::ProcessResult MinidumpProcessor::Process( - const string &minidump_file, ProcessState *process_state) { - BPLOG(INFO) << "Processing minidump in file " << minidump_file; - - Minidump dump(minidump_file); - if (!dump.Read()) { - BPLOG(ERROR) << "Minidump " << minidump_file << " could not be read"; - return PROCESS_ERROR; - } +ProcessResult MinidumpProcessor::Process( + Minidump *dump, ProcessState *process_state) { + assert(dump); + assert(process_state); process_state->Clear(); - const MDRawHeader *header = dump.header(); - BPLOG_IF(ERROR, !header) << "Minidump " << minidump_file << " has no header"; - assert(header); + const MDRawHeader *header = dump->header(); + if (!header) { + BPLOG(ERROR) << "Minidump " << dump->path() << " has no header"; + return PROCESS_ERROR_NO_MINIDUMP_HEADER; + } process_state->time_date_stamp_ = header->time_date_stamp; - bool has_cpu_info = GetCPUInfo(&dump, &process_state->system_info_); - bool has_os_info = GetOSInfo(&dump, &process_state->system_info_); + bool has_cpu_info = GetCPUInfo(dump, &process_state->system_info_); + bool has_os_info = GetOSInfo(dump, &process_state->system_info_); u_int32_t dump_thread_id = 0; bool has_dump_thread = false; u_int32_t requesting_thread_id = 0; bool has_requesting_thread = false; - MinidumpBreakpadInfo *breakpad_info = dump.GetBreakpadInfo(); + MinidumpBreakpadInfo *breakpad_info = dump->GetBreakpadInfo(); if (breakpad_info) { has_dump_thread = breakpad_info->GetDumpThreadID(&dump_thread_id); has_requesting_thread = breakpad_info->GetRequestingThreadID(&requesting_thread_id); } - MinidumpException *exception = dump.GetException(); + MinidumpException *exception = dump->GetException(); if (exception) { process_state->crashed_ = true; has_requesting_thread = exception->GetThreadID(&requesting_thread_id); process_state->crash_reason_ = GetCrashReason( - &dump, &process_state->crash_address_); + dump, &process_state->crash_address_); } - MinidumpModuleList *module_list = dump.GetModuleList(); + // This will just return an empty string if it doesn't exist. + process_state->assertion_ = GetAssertion(dump); + + MinidumpModuleList *module_list = dump->GetModuleList(); // Put a copy of the module list into ProcessState object. This is not // necessarily a MinidumpModuleList, but it adheres to the CodeModules @@ -96,21 +97,21 @@ if (module_list) process_state->modules_ = module_list->Copy(); - MinidumpThreadList *threads = dump.GetThreadList(); + MinidumpThreadList *threads = dump->GetThreadList(); if (!threads) { - BPLOG(ERROR) << "Minidump " << minidump_file << " has no thread list"; - return PROCESS_ERROR; + BPLOG(ERROR) << "Minidump " << dump->path() << " has no thread list"; + return PROCESS_ERROR_NO_THREAD_LIST; } - BPLOG(INFO) << "Minidump " << minidump_file << " has " << - (has_cpu_info ? "" : "no ") << "CPU info, " << - (has_os_info ? "" : "no ") << "OS info, " << - (breakpad_info != NULL ? "" : "no ") << "Breakpad info, " << - (exception != NULL ? "" : "no ") << "exception, " << - (module_list != NULL ? "" : "no ") << "module list, " << - (threads != NULL ? "" : "no ") << "thread list, " << - (has_dump_thread ? "" : "no ") << "dump thread, and " << - (has_requesting_thread ? "" : "no ") << "requesting thread"; + BPLOG(INFO) << "Minidump " << dump->path() << " has " << + (has_cpu_info ? "" : "no ") << "CPU info, " << + (has_os_info ? "" : "no ") << "OS info, " << + (breakpad_info != NULL ? "" : "no ") << "Breakpad info, " << + (exception != NULL ? "" : "no ") << "exception, " << + (module_list != NULL ? "" : "no ") << "module list, " << + (threads != NULL ? "" : "no ") << "thread list, " << + (has_dump_thread ? "" : "no ") << "dump thread, and " << + (has_requesting_thread ? "" : "no ") << "requesting thread"; bool interrupted = false; bool found_requesting_thread = false; @@ -121,18 +122,18 @@ char thread_string_buffer[64]; snprintf(thread_string_buffer, sizeof(thread_string_buffer), "%d/%d", thread_index, thread_count); - string thread_string = minidump_file + ":" + thread_string_buffer; + string thread_string = dump->path() + ":" + thread_string_buffer; MinidumpThread *thread = threads->GetThreadAtIndex(thread_index); if (!thread) { BPLOG(ERROR) << "Could not get thread for " << thread_string; - return PROCESS_ERROR; + return PROCESS_ERROR_GETTING_THREAD; } u_int32_t thread_id; if (!thread->GetThreadID(&thread_id)) { BPLOG(ERROR) << "Could not get thread ID for " << thread_string; - return PROCESS_ERROR; + return PROCESS_ERROR_GETTING_THREAD_ID; } thread_string += " id " + HexString(thread_id); @@ -152,7 +153,7 @@ if (found_requesting_thread) { // There can't be more than one requesting thread. BPLOG(ERROR) << "Duplicate requesting thread: " << thread_string; - return PROCESS_ERROR; + return PROCESS_ERROR_DUPLICATE_REQUESTING_THREADS; } // Use processed_state->threads_.size() instead of thread_index. @@ -171,15 +172,17 @@ // of the thread's own context. For the crashed thread, the thread's // own context is the state inside the exception handler. Using it // would not result in the expected stack trace from the time of the - // crash. - context = exception->GetContext(); + // crash. If the exception context is invalid, however, we fall back + // on the thread context. + MinidumpContext *ctx = exception->GetContext(); + context = ctx ? ctx : thread->GetContext(); } } MinidumpMemoryRegion *thread_memory = thread->GetMemory(); if (!thread_memory) { BPLOG(ERROR) << "No memory region for " << thread_string; - return PROCESS_ERROR; + return PROCESS_ERROR_NO_MEMORY_FOR_THREAD; } // Use process_state->modules_ instead of module_list, because the @@ -199,36 +202,50 @@ resolver_)); if (!stackwalker.get()) { BPLOG(ERROR) << "No stackwalker for " << thread_string; - return PROCESS_ERROR; + return PROCESS_ERROR_NO_STACKWALKER_FOR_THREAD; } scoped_ptr stack(new CallStack()); if (!stackwalker->Walk(stack.get())) { BPLOG(INFO) << "Stackwalker interrupt (missing symbols?) at " << - thread_string; + thread_string; interrupted = true; } process_state->threads_.push_back(stack.release()); + process_state->thread_memory_regions_.push_back(thread_memory); } if (interrupted) { - BPLOG(INFO) << "Processing interrupted for " << minidump_file; - return PROCESS_INTERRUPTED; + BPLOG(INFO) << "Processing interrupted for " << dump->path(); + return PROCESS_SYMBOL_SUPPLIER_INTERRUPTED; } // If a requesting thread was indicated, it must be present. if (has_requesting_thread && !found_requesting_thread) { // Don't mark as an error, but invalidate the requesting thread BPLOG(ERROR) << "Minidump indicated requesting thread " << - HexString(requesting_thread_id) << ", not found in " << - minidump_file; + HexString(requesting_thread_id) << ", not found in " << + dump->path(); process_state->requesting_thread_ = -1; } - BPLOG(INFO) << "Processed " << minidump_file; + BPLOG(INFO) << "Processed " << dump->path(); return PROCESS_OK; } +ProcessResult MinidumpProcessor::Process( + const string &minidump_file, ProcessState *process_state) { + BPLOG(INFO) << "Processing minidump in file " << minidump_file; + + Minidump dump(minidump_file); + if (!dump.Read()) { + BPLOG(ERROR) << "Minidump " << dump.path() << " could not be read"; + return PROCESS_ERROR_MINIDUMP_NOT_FOUND; + } + + return Process(&dump, process_state); +} + // Returns the MDRawSystemInfo from a minidump, or NULL if system info is // not available from the minidump. If system_info is non-NULL, it is used // to pass back the MinidumpSystemInfo object. @@ -260,7 +277,7 @@ switch (raw_system_info->processor_architecture) { case MD_CPU_ARCHITECTURE_X86: case MD_CPU_ARCHITECTURE_AMD64: { - if (raw_system_info->processor_architecture == + if (raw_system_info->processor_architecture == MD_CPU_ARCHITECTURE_X86) info->cpu = "x86"; else @@ -291,6 +308,11 @@ break; } + case MD_CPU_ARCHITECTURE_ARM: { + info->cpu = "arm"; + break; + } + default: { // Assign the numeric architecture ID into the CPU string. char cpu_string[7]; @@ -742,6 +764,9 @@ case MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK: reason = "EXCEPTION_POSSIBLE_DEADLOCK"; break; + case MD_EXCEPTION_CODE_WIN_UNHANDLED_CPP_EXCEPTION: + reason = "Unhandled C++ Exception"; + break; default: BPLOG(INFO) << "Unknown exception reason " << reason; break; @@ -861,118 +886,118 @@ break; case MD_EXCEPTION_CODE_SOL_SIGQUIT: reason = "SIGQUIT"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGILL: reason = "SIGILL"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGTRAP: reason = "SIGTRAP"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGIOT: reason = "SIGIOT | SIGABRT"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGEMT: reason = "SIGEMT"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGFPE: reason = "SIGFPE"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGKILL: reason = "SIGKILL"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGBUS: reason = "SIGBUS"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGSEGV: reason = "SIGSEGV"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGSYS: reason = "SIGSYS"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGPIPE: reason = "SIGPIPE"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGALRM: reason = "SIGALRM"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGTERM: reason = "SIGTERM"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGUSR1: reason = "SIGUSR1"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGUSR2: reason = "SIGUSR2"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGCLD: reason = "SIGCLD | SIGCHLD"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGPWR: reason = "SIGPWR"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGWINCH: reason = "SIGWINCH"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGURG: reason = "SIGURG"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGPOLL: reason = "SIGPOLL | SIGIO"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGSTOP: reason = "SIGSTOP"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGTSTP: reason = "SIGTSTP"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGCONT: reason = "SIGCONT"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGTTIN: reason = "SIGTTIN"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGTTOU: reason = "SIGTTOU"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGVTALRM: reason = "SIGVTALRM"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGPROF: reason = "SIGPROF"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGXCPU: reason = "SIGXCPU"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGXFSZ: reason = "SIGXFSZ"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGWAITING: reason = "SIGWAITING"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGLWP: reason = "SIGLWP"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGFREEZE: reason = "SIGFREEZE"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGTHAW: reason = "SIGTHAW"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGCANCEL: reason = "SIGCANCEL"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGLOST: reason = "SIGLOST"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGXRES: reason = "SIGXRES"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGJVM1: reason = "SIGJVM1"; - break; + break; case MD_EXCEPTION_CODE_SOL_SIGJVM2: reason = "SIGJVM2"; - break; + break; default: BPLOG(INFO) << "Unknown exception reason " << reason; break; @@ -989,4 +1014,57 @@ return reason; } +// static +string MinidumpProcessor::GetAssertion(Minidump *dump) +{ + MinidumpAssertion *assertion = dump->GetAssertion(); + if (!assertion) + return ""; + + const MDRawAssertionInfo *raw_assertion = assertion->assertion(); + if (!raw_assertion) + return ""; + + string assertion_string; + switch (raw_assertion->type) { + case MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER: + assertion_string = "Invalid parameter passed to library function"; + break; + case MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL: + assertion_string = "Pure virtual function called"; + break; + default: { + char assertion_type[32]; + sprintf(assertion_type, "0x%08x", raw_assertion->type); + assertion_string = "Unknown assertion type "; + assertion_string += assertion_type; + break; + } + } + + string expression = assertion->expression(); + if (!expression.empty()) { + assertion_string.append(" " + expression); + } + + string function = assertion->function(); + if (!function.empty()) { + assertion_string.append(" in function " + function); + } + + string file = assertion->file(); + if (!file.empty()) { + assertion_string.append(", in file " + file); + } + + if (raw_assertion->line != 0) { + char assertion_line[32]; + sprintf(assertion_line, "%u", raw_assertion->line); + assertion_string.append(" at line "); + assertion_string.append(assertion_line); + } + + return assertion_string; +} + } // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor_unittest.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_processor_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -32,10 +32,15 @@ #include #include +#include +#include +#include +#include "breakpad_googletest_includes.h" #include "google_breakpad/processor/basic_source_line_resolver.h" #include "google_breakpad/processor/call_stack.h" #include "google_breakpad/processor/code_module.h" #include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/minidump.h" #include "google_breakpad/processor/minidump_processor.h" #include "google_breakpad/processor/process_state.h" #include "google_breakpad/processor/stack_frame.h" @@ -43,17 +48,40 @@ #include "processor/logging.h" #include "processor/scoped_ptr.h" +using std::map; + +namespace google_breakpad { +class MockMinidump : public Minidump { + public: + MockMinidump() : Minidump("") { + } + + MOCK_METHOD0(Read,bool()); + MOCK_CONST_METHOD0(path, string()); + MOCK_CONST_METHOD0(header,const MDRawHeader*()); + MOCK_METHOD0(GetThreadList,MinidumpThreadList*()); +}; +} + namespace { -using std::string; using google_breakpad::BasicSourceLineResolver; using google_breakpad::CallStack; using google_breakpad::CodeModule; using google_breakpad::MinidumpProcessor; +using google_breakpad::MinidumpThreadList; +using google_breakpad::MinidumpThread; +using google_breakpad::MockMinidump; using google_breakpad::ProcessState; using google_breakpad::scoped_ptr; using google_breakpad::SymbolSupplier; using google_breakpad::SystemInfo; +using std::string; +using ::testing::_; +using ::testing::Mock; +using ::testing::Ne; +using ::testing::Property; +using ::testing::Return; static const char *kSystemInfoOS = "Windows NT"; static const char *kSystemInfoOSShort = "windows"; @@ -62,17 +90,6 @@ static const char *kSystemInfoCPUInfo = "GenuineIntel family 6 model 13 stepping 8"; -#define ASSERT_TRUE(cond) \ - if (!(cond)) { \ - fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \ - return false; \ - } - -#define ASSERT_FALSE(cond) ASSERT_TRUE(!(cond)) - -#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2)) - -// Use ASSERT_*_ABORT in functions that can't return a boolean. #define ASSERT_TRUE_ABORT(cond) \ if (!(cond)) { \ fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \ @@ -89,6 +106,11 @@ const SystemInfo *system_info, string *symbol_file); + virtual SymbolResult GetSymbolFile(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data); + // When set to true, causes the SymbolSupplier to return INTERRUPT void set_interrupt(bool interrupt) { interrupt_ = interrupt; } @@ -123,7 +145,105 @@ return NOT_FOUND; } -static bool RunTests() { +SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile( + const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data) { + SymbolSupplier::SymbolResult s = GetSymbolFile(module, system_info, + symbol_file); + if (s == FOUND) { + std::ifstream in(symbol_file->c_str()); + std::getline(in, *symbol_data, std::string::traits_type::to_char_type( + std::string::traits_type::eof())); + in.close(); + } + + return s; +} + +// A mock symbol supplier that always returns NOT_FOUND; one current +// use for testing the processor's caching of symbol lookups. +class MockSymbolSupplier : public SymbolSupplier { + public: + MockSymbolSupplier() { } + MOCK_METHOD3(GetSymbolFile, SymbolResult(const CodeModule*, + const SystemInfo*, + string*)); + MOCK_METHOD4(GetSymbolFile, SymbolResult(const CodeModule*, + const SystemInfo*, + string*, + string*)); +}; + +class MinidumpProcessorTest : public ::testing::Test { + +}; + +TEST_F(MinidumpProcessorTest, TestCorruptMinidumps) { + MockMinidump dump; + TestSymbolSupplier supplier; + BasicSourceLineResolver resolver; + MinidumpProcessor processor(&supplier, &resolver); + ProcessState state; + + EXPECT_EQ(processor.Process("nonexistant minidump", &state), + google_breakpad::PROCESS_ERROR_MINIDUMP_NOT_FOUND); + + EXPECT_CALL(dump, path()).WillRepeatedly(Return("mock minidump")); + EXPECT_CALL(dump, Read()).WillRepeatedly(Return(true)); + + MDRawHeader fakeHeader; + fakeHeader.time_date_stamp = 0; + EXPECT_CALL(dump, header()).WillOnce(Return((MDRawHeader*)NULL)). + WillRepeatedly(Return(&fakeHeader)); + EXPECT_EQ(processor.Process(&dump, &state), + google_breakpad::PROCESS_ERROR_NO_MINIDUMP_HEADER); + + EXPECT_CALL(dump, GetThreadList()). + WillOnce(Return((MinidumpThreadList*)NULL)); + EXPECT_EQ(processor.Process(&dump, &state), + google_breakpad::PROCESS_ERROR_NO_THREAD_LIST); +} + +// This test case verifies that the symbol supplier is only consulted +// once per minidump per module. +TEST_F(MinidumpProcessorTest, TestSymbolSupplierLookupCounts) { + MockSymbolSupplier supplier; + BasicSourceLineResolver resolver; + MinidumpProcessor processor(&supplier, &resolver); + + string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/minidump2.dmp"; + ProcessState state; + EXPECT_CALL(supplier, GetSymbolFile( + Property(&google_breakpad::CodeModule::code_file, + "c:\\test_app.exe"), + _, _, _)).WillOnce(Return(SymbolSupplier::NOT_FOUND)); + EXPECT_CALL(supplier, GetSymbolFile( + Property(&google_breakpad::CodeModule::code_file, + Ne("c:\\test_app.exe")), + _, _, _)).WillRepeatedly(Return(SymbolSupplier::NOT_FOUND)); + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); + + ASSERT_TRUE(Mock::VerifyAndClearExpectations(&supplier)); + + // We need to verify that across minidumps, the processor will refetch + // symbol files, even with the same symbol supplier. + EXPECT_CALL(supplier, GetSymbolFile( + Property(&google_breakpad::CodeModule::code_file, + "c:\\test_app.exe"), + _, _, _)).WillOnce(Return(SymbolSupplier::NOT_FOUND)); + EXPECT_CALL(supplier, GetSymbolFile( + Property(&google_breakpad::CodeModule::code_file, + Ne("c:\\test_app.exe")), + _, _, _)).WillRepeatedly(Return(SymbolSupplier::NOT_FOUND)); + ASSERT_EQ(processor.Process(minidump_file, &state), + google_breakpad::PROCESS_OK); +} + +TEST_F(MinidumpProcessorTest, TestBasicProcessing) { TestSymbolSupplier supplier; BasicSourceLineResolver resolver; MinidumpProcessor processor(&supplier, &resolver); @@ -133,7 +253,7 @@ ProcessState state; ASSERT_EQ(processor.Process(minidump_file, &state), - MinidumpProcessor::PROCESS_OK); + google_breakpad::PROCESS_OK); ASSERT_EQ(state.system_info()->os, kSystemInfoOS); ASSERT_EQ(state.system_info()->os_short, kSystemInfoOSShort); ASSERT_EQ(state.system_info()->os_version, kSystemInfoOSVersion); @@ -141,16 +261,16 @@ ASSERT_EQ(state.system_info()->cpu_info, kSystemInfoCPUInfo); ASSERT_TRUE(state.crashed()); ASSERT_EQ(state.crash_reason(), "EXCEPTION_ACCESS_VIOLATION"); - ASSERT_EQ(state.crash_address(), 0x45); - ASSERT_EQ(state.threads()->size(), 1); + ASSERT_EQ(state.crash_address(), 0x45U); + ASSERT_EQ(state.threads()->size(), size_t(1)); ASSERT_EQ(state.requesting_thread(), 0); CallStack *stack = state.threads()->at(0); ASSERT_TRUE(stack); - ASSERT_EQ(stack->frames()->size(), 4); + ASSERT_EQ(stack->frames()->size(), 4U); ASSERT_TRUE(stack->frames()->at(0)->module); - ASSERT_EQ(stack->frames()->at(0)->module->base_address(), 0x400000); + ASSERT_EQ(stack->frames()->at(0)->module->base_address(), 0x400000U); ASSERT_EQ(stack->frames()->at(0)->module->code_file(), "c:\\test_app.exe"); ASSERT_EQ(stack->frames()->at(0)->function_name, "`anonymous namespace'::CrashFunction"); @@ -158,7 +278,7 @@ ASSERT_EQ(stack->frames()->at(0)->source_line, 58); ASSERT_TRUE(stack->frames()->at(1)->module); - ASSERT_EQ(stack->frames()->at(1)->module->base_address(), 0x400000); + ASSERT_EQ(stack->frames()->at(1)->module->base_address(), 0x400000U); ASSERT_EQ(stack->frames()->at(1)->module->code_file(), "c:\\test_app.exe"); ASSERT_EQ(stack->frames()->at(1)->function_name, "main"); ASSERT_EQ(stack->frames()->at(1)->source_file_name, "c:\\test_app.cc"); @@ -166,7 +286,7 @@ // This comes from the CRT ASSERT_TRUE(stack->frames()->at(2)->module); - ASSERT_EQ(stack->frames()->at(2)->module->base_address(), 0x400000); + ASSERT_EQ(stack->frames()->at(2)->module->base_address(), 0x400000U); ASSERT_EQ(stack->frames()->at(2)->module->code_file(), "c:\\test_app.exe"); ASSERT_EQ(stack->frames()->at(2)->function_name, "__tmainCRTStartup"); ASSERT_EQ(stack->frames()->at(2)->source_file_name, @@ -175,14 +295,14 @@ // No debug info available for kernel32.dll ASSERT_TRUE(stack->frames()->at(3)->module); - ASSERT_EQ(stack->frames()->at(3)->module->base_address(), 0x7c800000); + ASSERT_EQ(stack->frames()->at(3)->module->base_address(), 0x7c800000U); ASSERT_EQ(stack->frames()->at(3)->module->code_file(), "C:\\WINDOWS\\system32\\kernel32.dll"); ASSERT_TRUE(stack->frames()->at(3)->function_name.empty()); ASSERT_TRUE(stack->frames()->at(3)->source_file_name.empty()); ASSERT_EQ(stack->frames()->at(3)->source_line, 0); - ASSERT_EQ(state.modules()->module_count(), 13); + ASSERT_EQ(state.modules()->module_count(), 13U); ASSERT_TRUE(state.modules()->GetMainModule()); ASSERT_EQ(state.modules()->GetMainModule()->code_file(), "c:\\test_app.exe"); ASSERT_FALSE(state.modules()->GetModuleForAddress(0)); @@ -197,19 +317,12 @@ state.Clear(); supplier.set_interrupt(true); ASSERT_EQ(processor.Process(minidump_file, &state), - MinidumpProcessor::PROCESS_INTERRUPTED); - - return true; + google_breakpad::PROCESS_SYMBOL_SUPPLIER_INTERRUPTED + ); } - } // namespace int main(int argc, char *argv[]) { - BPLOG_INIT(&argc, &argv); - - if (!RunTests()) { - return 1; - } - - return 0; + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); } diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_stackwalk.cc 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -69,6 +70,7 @@ using google_breakpad::StackFrameSPARC; using google_breakpad::StackFrameX86; using google_breakpad::StackFrameAMD64; +using google_breakpad::StackFrameARM; // Separator character for machine readable output. static const char kOutputSeparator = '|'; @@ -159,6 +161,29 @@ sequence = PrintRegister("edx", frame_x86->context.edx, sequence); sequence = PrintRegister("efl", frame_x86->context.eflags, sequence); } + const char *trust_name; + switch (frame_x86->trust) { + default: + case StackFrameX86::FRAME_TRUST_NONE: + trust_name = "unknown"; + break; + case StackFrameX86::FRAME_TRUST_CONTEXT: + trust_name = "given as instruction pointer in context"; + break; + case StackFrameX86::FRAME_TRUST_CFI: + trust_name = "call frame info"; + break; + case StackFrameX86::FRAME_TRUST_CFI_SCAN: + trust_name = "call frame info with scanning"; + break; + case StackFrameX86::FRAME_TRUST_FP: + trust_name = "previous frame's frame pointer"; + break; + case StackFrameX86::FRAME_TRUST_SCAN: + trust_name = "stack scanning"; + break; + } + printf("\n Found by: %s", trust_name); } else if (cpu == "ppc") { const StackFramePPC *frame_ppc = reinterpret_cast(frame); @@ -171,6 +196,16 @@ const StackFrameAMD64 *frame_amd64 = reinterpret_cast(frame); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RBX) + sequence = PrintRegister("rbx", frame_amd64->context.rbx, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R12) + sequence = PrintRegister("r12", frame_amd64->context.r12, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R13) + sequence = PrintRegister("r13", frame_amd64->context.r13, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R14) + sequence = PrintRegister("r14", frame_amd64->context.r14, sequence); + if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_R15) + sequence = PrintRegister("r15", frame_amd64->context.r15, sequence); if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RIP) sequence = PrintRegister("rip", frame_amd64->context.rip, sequence); if (frame_amd64->context_validity & StackFrameAMD64::CONTEXT_VALID_RSP) @@ -187,6 +222,35 @@ sequence = PrintRegister("fp", frame_sparc->context.g_r[30], sequence); if (frame_sparc->context_validity & StackFrameSPARC::CONTEXT_VALID_PC) sequence = PrintRegister("pc", frame_sparc->context.pc, sequence); + } else if (cpu == "arm") { + const StackFrameARM *frame_arm = + reinterpret_cast(frame); + + // General-purpose callee-saves registers. + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R4) + sequence = PrintRegister("r4", frame_arm->context.iregs[4], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R5) + sequence = PrintRegister("r5", frame_arm->context.iregs[5], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R6) + sequence = PrintRegister("r6", frame_arm->context.iregs[6], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R7) + sequence = PrintRegister("r7", frame_arm->context.iregs[7], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R8) + sequence = PrintRegister("r8", frame_arm->context.iregs[8], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R9) + sequence = PrintRegister("r9", frame_arm->context.iregs[9], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_R10) + sequence = PrintRegister("r10", frame_arm->context.iregs[10], sequence); + + // Registers with a dedicated or conventional purpose. + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_FP) + sequence = PrintRegister("fp", frame_arm->context.iregs[11], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_SP) + sequence = PrintRegister("sp", frame_arm->context.iregs[13], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_LR) + sequence = PrintRegister("lr", frame_arm->context.iregs[14], sequence); + if (frame_arm->context_validity & StackFrameARM::CONTEXT_VALID_PC) + sequence = PrintRegister("pc", frame_arm->context.iregs[15], sequence); } printf("\n"); } @@ -338,6 +402,11 @@ printf("No crash\n"); } + string assertion = process_state.assertion(); + if (!assertion.empty()) { + printf("Assertion: %s\n", assertion.c_str()); + } + // If the thread that requested the dump is known, print it first. int requesting_thread = process_state.requesting_thread(); if (requesting_thread != -1) { @@ -390,7 +459,15 @@ StripSeparator(process_state.crash_reason()).c_str(), kOutputSeparator, process_state.crash_address(), kOutputSeparator); } else { - printf("No crash%c%c", kOutputSeparator, kOutputSeparator); + // print assertion info, if available, in place of crash reason, + // instead of the unhelpful "No crash" + string assertion = process_state.assertion(); + if (!assertion.empty()) { + printf("%s%c%c", StripSeparator(assertion).c_str(), + kOutputSeparator, kOutputSeparator); + } else { + printf("No crash%c%c", kOutputSeparator, kOutputSeparator); + } } if (requesting_thread != -1) { @@ -446,7 +523,7 @@ // Process the minidump. ProcessState process_state; if (minidump_processor.Process(minidump_file, &process_state) != - MinidumpProcessor::PROCESS_OK) { + google_breakpad::PROCESS_OK) { BPLOG(ERROR) << "MinidumpProcessor::Process failed"; return false; } diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/minidump_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,534 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Unit test for Minidump. Uses a pre-generated minidump and +// verifies that certain streams are correct. + +#include +#include +#include +#include +#include +#include +#include "breakpad_googletest_includes.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/minidump.h" +#include "processor/logging.h" +#include "processor/synth_minidump.h" + +namespace { + +using google_breakpad::Minidump; +using google_breakpad::MinidumpContext; +using google_breakpad::MinidumpMemoryList; +using google_breakpad::MinidumpMemoryRegion; +using google_breakpad::MinidumpModule; +using google_breakpad::MinidumpModuleList; +using google_breakpad::MinidumpSystemInfo; +using google_breakpad::MinidumpThread; +using google_breakpad::MinidumpThreadList; +using google_breakpad::SynthMinidump::Context; +using google_breakpad::SynthMinidump::Dump; +using google_breakpad::SynthMinidump::Memory; +using google_breakpad::SynthMinidump::Module; +using google_breakpad::SynthMinidump::Stream; +using google_breakpad::SynthMinidump::String; +using google_breakpad::SynthMinidump::SystemInfo; +using google_breakpad::SynthMinidump::Thread; +using google_breakpad::TestAssembler::kBigEndian; +using google_breakpad::TestAssembler::kLittleEndian; +using std::ifstream; +using std::istringstream; +using std::string; +using std::vector; +using ::testing::Return; + +class MinidumpTest : public ::testing::Test { +public: + void SetUp() { + minidump_file_ = string(getenv("srcdir") ? getenv("srcdir") : ".") + + "/src/processor/testdata/minidump2.dmp"; + } + string minidump_file_; +}; + +TEST_F(MinidumpTest, TestMinidumpFromFile) { + Minidump minidump(minidump_file_); + ASSERT_EQ(minidump.path(), minidump_file_); + ASSERT_TRUE(minidump.Read()); + const MDRawHeader* header = minidump.header(); + ASSERT_NE(header, (MDRawHeader*)NULL); + ASSERT_EQ(header->signature, u_int32_t(MD_HEADER_SIGNATURE)); + //TODO: add more checks here +} + +TEST_F(MinidumpTest, TestMinidumpFromStream) { + // read minidump contents into memory, construct a stringstream around them + ifstream file_stream(minidump_file_.c_str(), std::ios::in); + ASSERT_TRUE(file_stream.good()); + vector bytes; + file_stream.seekg(0, std::ios_base::end); + ASSERT_TRUE(file_stream.good()); + bytes.resize(file_stream.tellg()); + file_stream.seekg(0, std::ios_base::beg); + ASSERT_TRUE(file_stream.good()); + file_stream.read(&bytes[0], bytes.size()); + ASSERT_TRUE(file_stream.good()); + string str(&bytes[0], bytes.size()); + istringstream stream(str); + ASSERT_TRUE(stream.good()); + + // now read minidump from stringstream + Minidump minidump(stream); + ASSERT_EQ(minidump.path(), ""); + ASSERT_TRUE(minidump.Read()); + const MDRawHeader* header = minidump.header(); + ASSERT_NE(header, (MDRawHeader*)NULL); + ASSERT_EQ(header->signature, u_int32_t(MD_HEADER_SIGNATURE)); + //TODO: add more checks here +} + +TEST(Dump, ReadBackEmpty) { + Dump dump(0); + dump.Finish(); + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + istringstream stream(contents); + Minidump minidump(stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(0U, minidump.GetDirectoryEntryCount()); +} + +TEST(Dump, ReadBackEmptyBigEndian) { + Dump big_minidump(0, kBigEndian); + big_minidump.Finish(); + string contents; + ASSERT_TRUE(big_minidump.GetContents(&contents)); + istringstream stream(contents); + Minidump minidump(stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(0U, minidump.GetDirectoryEntryCount()); +} + +TEST(Dump, OneStream) { + Dump dump(0, kBigEndian); + Stream stream(dump, 0xfbb7fa2bU); + stream.Append("stream contents"); + dump.Add(&stream); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(1U, minidump.GetDirectoryEntryCount()); + + const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0); + ASSERT_TRUE(dir != NULL); + EXPECT_EQ(0xfbb7fa2bU, dir->stream_type); + + u_int32_t stream_length; + ASSERT_TRUE(minidump.SeekToStreamType(0xfbb7fa2bU, &stream_length)); + ASSERT_EQ(15U, stream_length); + char stream_contents[15]; + ASSERT_TRUE(minidump.ReadBytes(stream_contents, sizeof(stream_contents))); + EXPECT_EQ(string("stream contents"), + string(stream_contents, sizeof(stream_contents))); + + EXPECT_FALSE(minidump.GetThreadList()); + EXPECT_FALSE(minidump.GetModuleList()); + EXPECT_FALSE(minidump.GetMemoryList()); + EXPECT_FALSE(minidump.GetException()); + EXPECT_FALSE(minidump.GetAssertion()); + EXPECT_FALSE(minidump.GetSystemInfo()); + EXPECT_FALSE(minidump.GetMiscInfo()); + EXPECT_FALSE(minidump.GetBreakpadInfo()); +} + +TEST(Dump, OneMemory) { + Dump dump(0, kBigEndian); + Memory memory(dump, 0x309d68010bd21b2cULL); + memory.Append("memory contents"); + dump.Add(&memory); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(1U, minidump.GetDirectoryEntryCount()); + + const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0); + ASSERT_TRUE(dir != NULL); + EXPECT_EQ((u_int32_t) MD_MEMORY_LIST_STREAM, dir->stream_type); + + MinidumpMemoryList *memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(memory_list != NULL); + ASSERT_EQ(1U, memory_list->region_count()); + + MinidumpMemoryRegion *region1 = memory_list->GetMemoryRegionAtIndex(0); + ASSERT_EQ(0x309d68010bd21b2cULL, region1->GetBase()); + ASSERT_EQ(15U, region1->GetSize()); + const u_int8_t *region1_bytes = region1->GetMemory(); + ASSERT_TRUE(memcmp("memory contents", region1_bytes, 15) == 0); +} + +// One thread --- and its requisite entourage. +TEST(Dump, OneThread) { + Dump dump(0, kLittleEndian); + Memory stack(dump, 0x2326a0fa); + stack.Append("stack for thread"); + + MDRawContextX86 raw_context; + raw_context.context_flags = MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL; + raw_context.edi = 0x3ecba80d; + raw_context.esi = 0x382583b9; + raw_context.ebx = 0x7fccc03f; + raw_context.edx = 0xf62f8ec2; + raw_context.ecx = 0x46a6a6a8; + raw_context.eax = 0x6a5025e2; + raw_context.ebp = 0xd9fabb4a; + raw_context.eip = 0x6913f540; + raw_context.cs = 0xbffe6eda; + raw_context.eflags = 0xb2ce1e2d; + raw_context.esp = 0x659caaa4; + raw_context.ss = 0x2e951ef7; + Context context(dump, raw_context); + + Thread thread(dump, 0xa898f11b, stack, context, + 0x9e39439f, 0x4abfc15f, 0xe499898a, 0x0d43e939dcfd0372ULL); + + dump.Add(&stack); + dump.Add(&context); + dump.Add(&thread); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(2U, minidump.GetDirectoryEntryCount()); + + MinidumpMemoryList *md_memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(md_memory_list != NULL); + ASSERT_EQ(1U, md_memory_list->region_count()); + + MinidumpMemoryRegion *md_region = md_memory_list->GetMemoryRegionAtIndex(0); + ASSERT_EQ(0x2326a0faU, md_region->GetBase()); + ASSERT_EQ(16U, md_region->GetSize()); + const u_int8_t *region_bytes = md_region->GetMemory(); + ASSERT_TRUE(memcmp("stack for thread", region_bytes, 16) == 0); + + MinidumpThreadList *thread_list = minidump.GetThreadList(); + ASSERT_TRUE(thread_list != NULL); + ASSERT_EQ(1U, thread_list->thread_count()); + + MinidumpThread *md_thread = thread_list->GetThreadAtIndex(0); + ASSERT_TRUE(md_thread != NULL); + u_int32_t thread_id; + ASSERT_TRUE(md_thread->GetThreadID(&thread_id)); + ASSERT_EQ(0xa898f11bU, thread_id); + MinidumpMemoryRegion *md_stack = md_thread->GetMemory(); + ASSERT_TRUE(md_stack != NULL); + ASSERT_EQ(0x2326a0faU, md_stack->GetBase()); + ASSERT_EQ(16U, md_stack->GetSize()); + const u_int8_t *md_stack_bytes = md_stack->GetMemory(); + ASSERT_TRUE(memcmp("stack for thread", md_stack_bytes, 16) == 0); + + MinidumpContext *md_context = md_thread->GetContext(); + ASSERT_TRUE(md_context != NULL); + ASSERT_EQ((u_int32_t) MD_CONTEXT_X86, md_context->GetContextCPU()); + const MDRawContextX86 *md_raw_context = md_context->GetContextX86(); + ASSERT_TRUE(md_raw_context != NULL); + ASSERT_EQ((u_int32_t) (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL), + (md_raw_context->context_flags + & (MD_CONTEXT_X86_INTEGER | MD_CONTEXT_X86_CONTROL))); + EXPECT_EQ(0x3ecba80dU, raw_context.edi); + EXPECT_EQ(0x382583b9U, raw_context.esi); + EXPECT_EQ(0x7fccc03fU, raw_context.ebx); + EXPECT_EQ(0xf62f8ec2U, raw_context.edx); + EXPECT_EQ(0x46a6a6a8U, raw_context.ecx); + EXPECT_EQ(0x6a5025e2U, raw_context.eax); + EXPECT_EQ(0xd9fabb4aU, raw_context.ebp); + EXPECT_EQ(0x6913f540U, raw_context.eip); + EXPECT_EQ(0xbffe6edaU, raw_context.cs); + EXPECT_EQ(0xb2ce1e2dU, raw_context.eflags); + EXPECT_EQ(0x659caaa4U, raw_context.esp); + EXPECT_EQ(0x2e951ef7U, raw_context.ss); +} + +TEST(Dump, OneModule) { + static const MDVSFixedFileInfo fixed_file_info = { + 0xb2fba33a, // signature + 0x33d7a728, // struct_version + 0x31afcb20, // file_version_hi + 0xe51cdab1, // file_version_lo + 0xd1ea6907, // product_version_hi + 0x03032857, // product_version_lo + 0x11bf71d7, // file_flags_mask + 0x5fb8cdbf, // file_flags + 0xe45d0d5d, // file_os + 0x107d9562, // file_type + 0x5a8844d4, // file_subtype + 0xa8d30b20, // file_date_hi + 0x651c3e4e // file_date_lo + }; + + Dump dump(0, kBigEndian); + String module_name(dump, "single module"); + Module module(dump, 0xa90206ca83eb2852ULL, 0xada542bd, + module_name, + 0xb1054d2a, + 0x34571371, + fixed_file_info, // from synth_minidump_unittest_data.h + NULL, NULL); + + dump.Add(&module); + dump.Add(&module_name); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(1U, minidump.GetDirectoryEntryCount()); + + const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0); + ASSERT_TRUE(dir != NULL); + EXPECT_EQ((u_int32_t) MD_MODULE_LIST_STREAM, dir->stream_type); + + MinidumpModuleList *md_module_list = minidump.GetModuleList(); + ASSERT_TRUE(md_module_list != NULL); + ASSERT_EQ(1U, md_module_list->module_count()); + + const MinidumpModule *md_module = md_module_list->GetModuleAtIndex(0); + ASSERT_TRUE(md_module != NULL); + ASSERT_EQ(0xa90206ca83eb2852ULL, md_module->base_address()); + ASSERT_EQ(0xada542bd, md_module->size()); + ASSERT_EQ("single module", md_module->code_file()); + + const MDRawModule *md_raw_module = md_module->module(); + ASSERT_TRUE(md_raw_module != NULL); + ASSERT_EQ(0xb1054d2aU, md_raw_module->time_date_stamp); + ASSERT_EQ(0x34571371U, md_raw_module->checksum); + ASSERT_TRUE(memcmp(&md_raw_module->version_info, &fixed_file_info, + sizeof(fixed_file_info)) == 0); +} + +TEST(Dump, OneSystemInfo) { + Dump dump(0, kLittleEndian); + String csd_version(dump, "Petulant Pierogi"); + SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version); + + dump.Add(&system_info); + dump.Add(&csd_version); + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(1U, minidump.GetDirectoryEntryCount()); + + const MDRawDirectory *dir = minidump.GetDirectoryEntryAtIndex(0); + ASSERT_TRUE(dir != NULL); + EXPECT_EQ((u_int32_t) MD_SYSTEM_INFO_STREAM, dir->stream_type); + + MinidumpSystemInfo *md_system_info = minidump.GetSystemInfo(); + ASSERT_TRUE(md_system_info != NULL); + ASSERT_EQ("windows", md_system_info->GetOS()); + ASSERT_EQ("x86", md_system_info->GetCPU()); + ASSERT_EQ("Petulant Pierogi", *md_system_info->GetCSDVersion()); + ASSERT_EQ("GenuineIntel", *md_system_info->GetCPUVendor()); +} + +TEST(Dump, BigDump) { + Dump dump(0, kLittleEndian); + + // A SystemInfo stream. + String csd_version(dump, "Munificent Macaque"); + SystemInfo system_info(dump, SystemInfo::windows_x86, csd_version); + dump.Add(&csd_version); + dump.Add(&system_info); + + // Five threads! + Memory stack0(dump, 0x70b9ebfc); + stack0.Append("stack for thread zero"); + MDRawContextX86 raw_context0; + raw_context0.context_flags = MD_CONTEXT_X86_INTEGER; + raw_context0.eip = 0xaf0709e4; + Context context0(dump, raw_context0); + Thread thread0(dump, 0xbbef4432, stack0, context0, + 0xd0377e7b, 0xdb8eb0cf, 0xd73bc314, 0x09d357bac7f9a163ULL); + dump.Add(&stack0); + dump.Add(&context0); + dump.Add(&thread0); + + Memory stack1(dump, 0xf988cc45); + stack1.Append("stack for thread one"); + MDRawContextX86 raw_context1; + raw_context1.context_flags = MD_CONTEXT_X86_INTEGER; + raw_context1.eip = 0xe4f56f81; + Context context1(dump, raw_context1); + Thread thread1(dump, 0x657c3f58, stack1, context1, + 0xa68fa182, 0x6f3cf8dd, 0xe3a78ccf, 0x78cc84775e4534bbULL); + dump.Add(&stack1); + dump.Add(&context1); + dump.Add(&thread1); + + Memory stack2(dump, 0xc8a92e7c); + stack2.Append("stack for thread two"); + MDRawContextX86 raw_context2; + raw_context2.context_flags = MD_CONTEXT_X86_INTEGER; + raw_context2.eip = 0xb336a438; + Context context2(dump, raw_context2); + Thread thread2(dump, 0xdf4b8a71, stack2, context2, + 0x674c26b6, 0x445d7120, 0x7e700c56, 0xd89bf778e7793e17ULL); + dump.Add(&stack2); + dump.Add(&context2); + dump.Add(&thread2); + + Memory stack3(dump, 0x36d08e08); + stack3.Append("stack for thread three"); + MDRawContextX86 raw_context3; + raw_context3.context_flags = MD_CONTEXT_X86_INTEGER; + raw_context3.eip = 0xdf99a60c; + Context context3(dump, raw_context3); + Thread thread3(dump, 0x86e6c341, stack3, context3, + 0x32dc5c55, 0x17a2aba8, 0xe0cc75e7, 0xa46393994dae83aeULL); + dump.Add(&stack3); + dump.Add(&context3); + dump.Add(&thread3); + + Memory stack4(dump, 0x1e0ab4fa); + stack4.Append("stack for thread four"); + MDRawContextX86 raw_context4; + raw_context4.context_flags = MD_CONTEXT_X86_INTEGER; + raw_context4.eip = 0xaa646267; + Context context4(dump, raw_context4); + Thread thread4(dump, 0x261a28d4, stack4, context4, + 0x6ebd389e, 0xa0cd4759, 0x30168846, 0x164f650a0cf39d35ULL); + dump.Add(&stack4); + dump.Add(&context4); + dump.Add(&thread4); + + // Three modules! + String module1_name(dump, "module one"); + Module module1(dump, 0xeb77da57b5d4cbdaULL, 0x83cd5a37, module1_name); + dump.Add(&module1_name); + dump.Add(&module1); + + String module2_name(dump, "module two"); + Module module2(dump, 0x8675884adfe5ac90ULL, 0xb11e4ea3, module2_name); + dump.Add(&module2_name); + dump.Add(&module2); + + String module3_name(dump, "module three"); + Module module3(dump, 0x95fc1544da321b6cULL, 0x7c2bf081, module3_name); + dump.Add(&module3_name); + dump.Add(&module3); + + // Add one more memory region, on top of the five stacks. + Memory memory5(dump, 0x61979e828040e564ULL); + memory5.Append("contents of memory 5"); + dump.Add(&memory5); + + dump.Finish(); + + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + istringstream minidump_stream(contents); + Minidump minidump(minidump_stream); + ASSERT_TRUE(minidump.Read()); + ASSERT_EQ(4U, minidump.GetDirectoryEntryCount()); + + // Check the threads. + MinidumpThreadList *thread_list = minidump.GetThreadList(); + ASSERT_TRUE(thread_list != NULL); + ASSERT_EQ(5U, thread_list->thread_count()); + u_int32_t thread_id; + ASSERT_TRUE(thread_list->GetThreadAtIndex(0)->GetThreadID(&thread_id)); + ASSERT_EQ(0xbbef4432U, thread_id); + ASSERT_EQ(0x70b9ebfcU, + thread_list->GetThreadAtIndex(0)->GetMemory()->GetBase()); + ASSERT_EQ(0xaf0709e4U, + thread_list->GetThreadAtIndex(0)->GetContext()->GetContextX86() + ->eip); + + ASSERT_TRUE(thread_list->GetThreadAtIndex(1)->GetThreadID(&thread_id)); + ASSERT_EQ(0x657c3f58U, thread_id); + ASSERT_EQ(0xf988cc45U, + thread_list->GetThreadAtIndex(1)->GetMemory()->GetBase()); + ASSERT_EQ(0xe4f56f81U, + thread_list->GetThreadAtIndex(1)->GetContext()->GetContextX86() + ->eip); + + ASSERT_TRUE(thread_list->GetThreadAtIndex(2)->GetThreadID(&thread_id)); + ASSERT_EQ(0xdf4b8a71U, thread_id); + ASSERT_EQ(0xc8a92e7cU, + thread_list->GetThreadAtIndex(2)->GetMemory()->GetBase()); + ASSERT_EQ(0xb336a438U, + thread_list->GetThreadAtIndex(2)->GetContext()->GetContextX86() + ->eip); + + ASSERT_TRUE(thread_list->GetThreadAtIndex(3)->GetThreadID(&thread_id)); + ASSERT_EQ(0x86e6c341U, thread_id); + ASSERT_EQ(0x36d08e08U, + thread_list->GetThreadAtIndex(3)->GetMemory()->GetBase()); + ASSERT_EQ(0xdf99a60cU, + thread_list->GetThreadAtIndex(3)->GetContext()->GetContextX86() + ->eip); + + ASSERT_TRUE(thread_list->GetThreadAtIndex(4)->GetThreadID(&thread_id)); + ASSERT_EQ(0x261a28d4U, thread_id); + ASSERT_EQ(0x1e0ab4faU, + thread_list->GetThreadAtIndex(4)->GetMemory()->GetBase()); + ASSERT_EQ(0xaa646267U, + thread_list->GetThreadAtIndex(4)->GetContext()->GetContextX86() + ->eip); + + // Check the modules. + MinidumpModuleList *md_module_list = minidump.GetModuleList(); + ASSERT_TRUE(md_module_list != NULL); + ASSERT_EQ(3U, md_module_list->module_count()); + EXPECT_EQ(0xeb77da57b5d4cbdaULL, + md_module_list->GetModuleAtIndex(0)->base_address()); + EXPECT_EQ(0x8675884adfe5ac90ULL, + md_module_list->GetModuleAtIndex(1)->base_address()); + EXPECT_EQ(0x95fc1544da321b6cULL, + md_module_list->GetModuleAtIndex(2)->base_address()); +} + +} // namespace diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/pathname_stripper_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -27,6 +27,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include + #include "processor/pathname_stripper.h" #include "processor/logging.h" diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator.h 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator.h 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,6 @@ -// Copyright (c) 2006, Google Inc. +// -*- mode: C++ -*- + +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -90,18 +92,24 @@ // (^) will not be supported. |dictionary| may be NULL, but evaluation // will fail in that case unless set_dictionary is used before calling // Evaluate. - PostfixEvaluator(DictionaryType *dictionary, MemoryRegion *memory) + PostfixEvaluator(DictionaryType *dictionary, const MemoryRegion *memory) : dictionary_(dictionary), memory_(memory), stack_() {} - // Evaluate the expression. The results of execution will be stored - // in one (or more) variables in the dictionary. Returns false if any - // failures occure during execution, leaving variables in the dictionary - // in an indeterminate state. If assigned is non-NULL, any keys set in - // the dictionary as a result of evaluation will also be set to true in - // assigned, providing a way to determine if an expression modifies any - // of its input variables. + // Evaluate the expression, starting with an empty stack. The results of + // execution will be stored in one (or more) variables in the dictionary. + // Returns false if any failures occur during execution, leaving + // variables in the dictionary in an indeterminate state. If assigned is + // non-NULL, any keys set in the dictionary as a result of evaluation + // will also be set to true in assigned, providing a way to determine if + // an expression modifies any of its input variables. bool Evaluate(const string &expression, DictionaryValidityType *assigned); + // Like Evaluate, but provides the value left on the stack to the + // caller. If evaluation succeeds and leaves exactly one value on + // the stack, pop that value, store it in *result, and return true. + // Otherwise, return false. + bool EvaluateForValue(const string &expression, ValueType *result); + DictionaryType* dictionary() const { return dictionary_; } // Reset the dictionary. PostfixEvaluator does not take ownership. @@ -137,6 +145,12 @@ // Pushes a new value onto the stack. void PushValue(const ValueType &value); + // Evaluate expression, updating *assigned if it is non-zero. Return + // true if evaluation completes successfully. Do not clear the stack + // upon successful evaluation. + bool EvaluateInternal(const string &expression, + DictionaryValidityType *assigned); + // The dictionary mapping constant and variable identifiers (strings) to // values. Keys beginning with '$' are treated as variable names, and // PostfixEvaluator is free to create and modify these keys. Weak pointer. @@ -144,7 +158,7 @@ // If non-NULL, the MemoryRegion used for dereference (^) operations. // If NULL, dereferencing is unsupported and will fail. Weak pointer. - MemoryRegion *memory_; + const MemoryRegion *memory_; // The stack contains state information as execution progresses. Values // are pushed on to it as the expression string is read and as operations diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator-inl.h 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,6 @@ -// Copyright (c) 2006, Google Inc. +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -38,6 +40,7 @@ #define PROCESSOR_POSTFIX_EVALUATOR_INL_H__ +#include #include #include "processor/postfix_evaluator.h" @@ -63,11 +66,9 @@ template -bool PostfixEvaluator::Evaluate(const string &expression, - DictionaryValidityType *assigned) { - // Ensure that the stack is cleared before returning. - AutoStackClearer clearer(&stack_); - +bool PostfixEvaluator::EvaluateInternal( + const string &expression, + DictionaryValidityType *assigned) { // Tokenize, splitting on whitespace. istringstream stream(expression); string token; @@ -193,13 +194,46 @@ } } + return true; +} + +template +bool PostfixEvaluator::Evaluate(const string &expression, + DictionaryValidityType *assigned) { + // Ensure that the stack is cleared before returning. + AutoStackClearer clearer(&stack_); + + if (!EvaluateInternal(expression, assigned)) + return false; + // If there's anything left on the stack, it indicates incomplete execution. // This is a failure case. If the stack is empty, evalution was complete // and successful. - BPLOG_IF(ERROR, !stack_.empty()) << "Incomplete execution: " << expression; - return stack_.empty(); + if (stack_.empty()) + return true; + + BPLOG(ERROR) << "Incomplete execution: " << expression; + return false; } +template +bool PostfixEvaluator::EvaluateForValue(const string &expression, + ValueType *result) { + // Ensure that the stack is cleared before returning. + AutoStackClearer clearer(&stack_); + + if (!EvaluateInternal(expression, NULL)) + return false; + + // A successful execution should leave exactly one value on the stack. + if (stack_.size() != 1) { + BPLOG(ERROR) << "Expression yielded bad number of results: " + << "'" << expression << "'"; + return false; + } + + return PopValue(result); +} template typename PostfixEvaluator::PopResult @@ -212,16 +246,30 @@ string token = stack_.back(); stack_.pop_back(); - // First, try to treat the value as a literal. In order for this to - // succed, the entire string must be parseable as ValueType. If this - // isn't possible, it can't be a literal, so treat it as an identifier - // instead. + // First, try to treat the value as a literal. Literals may have leading + // '-' sign, and the entire remaining string must be parseable as + // ValueType. If this isn't possible, it can't be a literal, so treat it + // as an identifier instead. + // + // Some versions of the libstdc++, the GNU standard C++ library, have + // stream extractors for unsigned integer values that permit a leading + // '-' sign (6.0.13); others do not (6.0.9). Since we require it, we + // handle it explicitly here. istringstream token_stream(token); ValueType literal; + bool negative; + if (token_stream.peek() == '-') { + negative = true; + token_stream.get(); + } else { + negative = false; + } if (token_stream >> literal && token_stream.peek() == EOF) { if (value) { *value = literal; } + if (negative) + *value = -*value; return POP_RESULT_VALUE; } else { if (identifier) { diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator_unittest.cc 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/postfix_evaluator_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -56,21 +56,21 @@ // the value. class FakeMemoryRegion : public MemoryRegion { public: - virtual u_int64_t GetBase() { return 0; } - virtual u_int32_t GetSize() { return 0; } - virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) { + virtual u_int64_t GetBase() const { return 0; } + virtual u_int32_t GetSize() const { return 0; } + virtual bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const { *value = address + 1; return true; } - virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) { + virtual bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const { *value = address + 1; return true; } - virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) { + virtual bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const { *value = address + 1; return true; } - virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) { + virtual bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const { *value = address + 1; return true; } @@ -103,6 +103,18 @@ }; +struct EvaluateForValueTest { + // Expression passed to PostfixEvaluator::Evaluate. + const string expression; + + // True if the expression is expected to be evaluable, false if evaluation + // is expected to fail. + bool evaluable; + + // If evaluable, the value we expect it to yield. + unsigned int value; +}; + static bool RunTests() { // The first test set checks the basic operations and failure modes. PostfixEvaluator::DictionaryType dictionary_0; @@ -289,6 +301,84 @@ } } + // EvaluateForValue tests. + PostfixEvaluator::DictionaryType dictionary_2; + dictionary_2["$ebp"] = 0xbfff0010; + dictionary_2["$eip"] = 0x10000000; + dictionary_2["$esp"] = 0xbfff0000; + dictionary_2[".cbSavedRegs"] = 4; + dictionary_2[".cbParams"] = 4; + dictionary_2[".raSearchStart"] = 0xbfff0020; + const EvaluateForValueTest evaluate_for_value_tests_2[] = { + { "28907223", true, 28907223 }, // simple constant + { "89854293 40010015 +", true, 89854293 + 40010015 }, // arithmetic + { "-870245 8769343 +", true, 7899098 }, // negative constants + { "$ebp $esp - $eip +", true, 0x10000010 }, // variable references + { "18929794 34015074", false, 0 }, // too many values + { "$ebp $ebp 4 - =", false, 0 }, // too few values + { "$new $eip = $new", true, 0x10000000 }, // make new variable + { "$new 4 +", true, 0x10000004 }, // see prior assignments + { ".cfa 42 = 10", false, 0 } // can't set constants + }; + const int evaluate_for_value_tests_2_size + = (sizeof (evaluate_for_value_tests_2) + / sizeof (evaluate_for_value_tests_2[0])); + map validate_data_2; + validate_data_2["$eip"] = 0x10000000; + validate_data_2["$ebp"] = 0xbfff000c; + validate_data_2["$esp"] = 0xbfff0000; + validate_data_2["$new"] = 0x10000000; + validate_data_2[".cbSavedRegs"] = 4; + validate_data_2[".cbParams"] = 4; + validate_data_2[".raSearchStart"] = 0xbfff0020; + + postfix_evaluator.set_dictionary(&dictionary_2); + for (int i = 0; i < evaluate_for_value_tests_2_size; i++) { + const EvaluateForValueTest *test = &evaluate_for_value_tests_2[i]; + unsigned int result; + if (postfix_evaluator.EvaluateForValue(test->expression, &result) + != test->evaluable) { + fprintf(stderr, "FAIL: evaluate for value test %d, " + "expected evaluation to %s, but it %s\n", + i, test->evaluable ? "succeed" : "fail", + test->evaluable ? "failed" : "succeeded"); + return false; + } + if (test->evaluable && result != test->value) { + fprintf(stderr, "FAIL: evaluate for value test %d, " + "expected value to be 0x%x, but it was 0x%x\n", + i, test->value, result); + return false; + } + } + + for (map::iterator v = validate_data_2.begin(); + v != validate_data_2.end(); v++) { + map::iterator a = dictionary_2.find(v->first); + if (a == dictionary_2.end()) { + fprintf(stderr, "FAIL: evaluate for value dictionary check: " + "expected dict[\"%s\"] to be 0x%x, but it was unset\n", + v->first.c_str(), v->second); + return false; + } else if (a->second != v->second) { + fprintf(stderr, "FAIL: evaluate for value dictionary check: " + "expected dict[\"%s\"] to be 0x%x, but it was 0x%x\n", + v->first.c_str(), v->second, a->second); + return false; + } + dictionary_2.erase(a); + } + + map::iterator remaining = dictionary_2.begin(); + if (remaining != dictionary_2.end()) { + fprintf(stderr, "FAIL: evaluation of test expressions put unexpected " + "values in dictionary:\n"); + for (; remaining != dictionary_2.end(); remaining++) + fprintf(stderr, " dict[\"%s\"] == 0x%x\n", + remaining->first.c_str(), remaining->second); + return false; + } + return true; } diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/process_state.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/process_state.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/process_state.cc 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/process_state.cc 2010-04-16 17:32:47.000000000 +0100 @@ -48,6 +48,7 @@ crashed_ = false; crash_reason_.clear(); crash_address_ = 0; + assertion_.clear(); requesting_thread_ = -1; for (vector::const_iterator iterator = threads_.begin(); iterator != threads_.end(); diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/range_map-inl.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/range_map-inl.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/range_map-inl.h 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/range_map-inl.h 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -156,7 +156,7 @@ *entry = iterator->second.entry(); if (entry_base) - *entry_base = iterator->first; + *entry_base = iterator->second.base(); if (entry_size) *entry_size = iterator->first - iterator->second.base() + 1; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/range_map_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/range_map_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/range_map_unittest.cc 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/range_map_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -210,10 +210,11 @@ linked_ptr nearest_object; AddressType nearest_base; + AddressType nearest_size; bool retrieved_nearest = range_map->RetrieveNearestRange(address, &nearest_object, &nearest_base, - NULL); + &nearest_size); // When checking one greater than the high side, RetrieveNearestRange // should usually return the test range. When a different range begins @@ -237,6 +238,22 @@ observed_nearest ? "true" : "false"); return false; } + + // If a range was successfully retrieved, check that the returned + // bounds match the range as stored. + if (expected_nearest && + (nearest_base != range_test->address || + nearest_size != range_test->size)) { + fprintf(stderr, "FAILED: " + "RetrieveNearestRange id %d, side %d, offset %d, " + "expected base/size %d/%d, observed %d/%d\n", + range_test->id, + side, + offset, + range_test->address, range_test->size, + nearest_base, nearest_size); + return false; + } } } diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/simple_symbol_supplier.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/simple_symbol_supplier.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/simple_symbol_supplier.cc 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/simple_symbol_supplier.cc 2010-04-16 17:32:47.000000000 +0100 @@ -38,6 +38,8 @@ #include #include +#include +#include #include "processor/simple_symbol_supplier.h" #include "google_breakpad/processor/code_module.h" @@ -62,15 +64,35 @@ for (unsigned int path_index = 0; path_index < paths_.size(); ++path_index) { SymbolResult result; - if ((result = GetSymbolFileAtPath(module, system_info, paths_[path_index], - symbol_file)) != NOT_FOUND) { + if ((result = GetSymbolFileAtPathFromRoot(module, system_info, + paths_[path_index], + symbol_file)) != NOT_FOUND) { return result; } } return NOT_FOUND; } -SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFileAtPath( +SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFile( + const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data) { + assert(symbol_data); + symbol_data->clear(); + + SymbolSupplier::SymbolResult s = GetSymbolFile(module, system_info, symbol_file); + + if (s == FOUND) { + std::ifstream in(symbol_file->c_str()); + std::getline(in, *symbol_data, std::string::traits_type::to_char_type( + std::string::traits_type::eof())); + in.close(); + } + return s; +} + +SymbolSupplier::SymbolResult SimpleSymbolSupplier::GetSymbolFileAtPathFromRoot( const CodeModule *module, const SystemInfo *system_info, const string &root_path, string *symbol_file) { BPLOG_IF(ERROR, !symbol_file) << "SimpleSymbolSupplier::GetSymbolFileAtPath " diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/simple_symbol_supplier.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/simple_symbol_supplier.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/simple_symbol_supplier.h 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/simple_symbol_supplier.h 2010-04-16 17:32:47.000000000 +0100 @@ -102,15 +102,19 @@ // Returns the path to the symbol file for the given module. See the // description above. - SymbolResult GetSymbolFile(const CodeModule *module, - const SystemInfo *system_info, - string *symbol_file); + virtual SymbolResult GetSymbolFile(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file); + virtual SymbolResult GetSymbolFile(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data); protected: - SymbolResult GetSymbolFileAtPath(const CodeModule *module, - const SystemInfo *system_info, - const string &root_path, - string *symbol_file); + SymbolResult GetSymbolFileAtPathFromRoot(const CodeModule *module, + const SystemInfo *system_info, + const string &root_path, + string *symbol_file); private: vector paths_; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stack_frame_info.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stack_frame_info.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stack_frame_info.h 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stack_frame_info.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,126 +0,0 @@ -// Copyright (c) 2006, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// stack_frame_info.h: Holds debugging information about a stack frame. -// -// This structure is specific to Windows debugging information obtained -// from pdb files using the DIA API. -// -// Author: Mark Mentovai - - -#ifndef PROCESSOR_STACK_FRAME_INFO_H__ -#define PROCESSOR_STACK_FRAME_INFO_H__ - -#include - -#include "google_breakpad/common/breakpad_types.h" - -namespace google_breakpad { - -struct StackFrameInfo { - public: - enum Validity { - VALID_NONE = 0, - VALID_PARAMETER_SIZE = 1, - VALID_ALL = -1 - }; - - StackFrameInfo() : valid(VALID_NONE), - prolog_size(0), - epilog_size(0), - parameter_size(0), - saved_register_size(0), - local_size(0), - max_stack_size(0), - allocates_base_pointer(0), - program_string() {} - - StackFrameInfo(u_int32_t set_prolog_size, - u_int32_t set_epilog_size, - u_int32_t set_parameter_size, - u_int32_t set_saved_register_size, - u_int32_t set_local_size, - u_int32_t set_max_stack_size, - int set_allocates_base_pointer, - const std::string set_program_string) - : valid(VALID_ALL), - prolog_size(set_prolog_size), - epilog_size(set_epilog_size), - parameter_size(set_parameter_size), - saved_register_size(set_saved_register_size), - local_size(set_local_size), - max_stack_size(set_max_stack_size), - allocates_base_pointer(set_allocates_base_pointer), - program_string(set_program_string) {} - - // CopyFrom makes "this" StackFrameInfo object identical to "that". - void CopyFrom(const StackFrameInfo &that) { - valid = that.valid; - prolog_size = that.prolog_size; - epilog_size = that.epilog_size; - parameter_size = that.parameter_size; - saved_register_size = that.saved_register_size; - local_size = that.local_size; - max_stack_size = that.max_stack_size; - allocates_base_pointer = that.allocates_base_pointer; - program_string = that.program_string; - } - - // Clears the StackFrameInfo object so that users will see it as though - // it contains no information. - void Clear() { - valid = VALID_NONE; - program_string.erase(); - } - - // Identifies which fields in the structure are valid. This is of - // type Validity, but it is defined as an int because it's not - // possible to OR values into an enumerated type. Users must check - // this field before using any other. - int valid; - - // These values come from IDiaFrameData. - u_int32_t prolog_size; - u_int32_t epilog_size; - u_int32_t parameter_size; - u_int32_t saved_register_size; - u_int32_t local_size; - u_int32_t max_stack_size; - - // Only one of allocates_base_pointer or program_string will be valid. - // If program_string is empty, use allocates_base_pointer. - bool allocates_base_pointer; - std::string program_string; -}; - -} // namespace google_breakpad - - -#endif // PROCESSOR_STACK_FRAME_INFO_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64.cc 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64.cc 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2007, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -34,15 +34,61 @@ // Author: Mark Mentovai, Ted Mielczarek -#include "processor/stackwalker_amd64.h" #include "google_breakpad/processor/call_stack.h" #include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" #include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/cfi_frame_info.h" #include "processor/logging.h" +#include "processor/scoped_ptr.h" +#include "processor/stackwalker_amd64.h" namespace google_breakpad { +const StackwalkerAMD64::CFIWalker::RegisterSet +StackwalkerAMD64::cfi_register_map_[] = { + // It may seem like $rip and $rsp are callee-saves, because the callee is + // responsible for having them restored upon return. But the callee_saves + // flags here really means that the walker should assume they're + // unchanged if the CFI doesn't mention them --- clearly wrong for $rip + // and $rsp. + { "$rax", NULL, false, + StackFrameAMD64::CONTEXT_VALID_RAX, &MDRawContextAMD64::rax }, + { "$rdx", NULL, false, + StackFrameAMD64::CONTEXT_VALID_RDX, &MDRawContextAMD64::rdx }, + { "$rcx", NULL, false, + StackFrameAMD64::CONTEXT_VALID_RCX, &MDRawContextAMD64::rcx }, + { "$rbx", NULL, true, + StackFrameAMD64::CONTEXT_VALID_RBX, &MDRawContextAMD64::rbx }, + { "$rsi", NULL, false, + StackFrameAMD64::CONTEXT_VALID_RSI, &MDRawContextAMD64::rsi }, + { "$rdi", NULL, false, + StackFrameAMD64::CONTEXT_VALID_RDI, &MDRawContextAMD64::rdi }, + { "$rbp", NULL, true, + StackFrameAMD64::CONTEXT_VALID_RBP, &MDRawContextAMD64::rbp }, + { "$rsp", ".cfa", false, + StackFrameAMD64::CONTEXT_VALID_RSP, &MDRawContextAMD64::rsp }, + { "$r8", NULL, false, + StackFrameAMD64::CONTEXT_VALID_R8, &MDRawContextAMD64::r8 }, + { "$r9", NULL, false, + StackFrameAMD64::CONTEXT_VALID_R9, &MDRawContextAMD64::r9 }, + { "$r10", NULL, false, + StackFrameAMD64::CONTEXT_VALID_R10, &MDRawContextAMD64::r10 }, + { "$r11", NULL, false, + StackFrameAMD64::CONTEXT_VALID_R11, &MDRawContextAMD64::r11 }, + { "$r12", NULL, true, + StackFrameAMD64::CONTEXT_VALID_R12, &MDRawContextAMD64::r12 }, + { "$r13", NULL, true, + StackFrameAMD64::CONTEXT_VALID_R13, &MDRawContextAMD64::r13 }, + { "$r14", NULL, true, + StackFrameAMD64::CONTEXT_VALID_R14, &MDRawContextAMD64::r14 }, + { "$r15", NULL, true, + StackFrameAMD64::CONTEXT_VALID_R15, &MDRawContextAMD64::r15 }, + { "$rip", ".ra", false, + StackFrameAMD64::CONTEXT_VALID_RIP, &MDRawContextAMD64::rip }, +}; + StackwalkerAMD64::StackwalkerAMD64(const SystemInfo *system_info, const MDRawContextAMD64 *context, MemoryRegion *memory, @@ -50,7 +96,9 @@ SymbolSupplier *supplier, SourceLineResolverInterface *resolver) : Stackwalker(system_info, memory, modules, supplier, resolver), - context_(context) { + context_(context), + cfi_walker_(cfi_register_map_, + (sizeof(cfi_register_map_) / sizeof(cfi_register_map_[0]))) { } @@ -71,60 +119,70 @@ return frame; } +StackFrameAMD64 *StackwalkerAMD64::GetCallerByCFIFrameInfo( + const vector &frames, + CFIFrameInfo *cfi_frame_info) { + StackFrameAMD64 *last_frame = static_cast(frames.back()); + + scoped_ptr frame(new StackFrameAMD64()); + if (!cfi_walker_ + .FindCallerRegisters(*memory_, *cfi_frame_info, + last_frame->context, last_frame->context_validity, + &frame->context, &frame->context_validity)) + return NULL; + + // Make sure we recovered all the essentials. + static const int essentials = (StackFrameAMD64::CONTEXT_VALID_RIP + | StackFrameAMD64::CONTEXT_VALID_RSP); + if ((frame->context_validity & essentials) != essentials) + return NULL; + + return frame.release(); +} -StackFrame* StackwalkerAMD64::GetCallerFrame( - const CallStack *stack, - const vector< linked_ptr > &stack_frame_info) { +StackFrame* StackwalkerAMD64::GetCallerFrame(const CallStack *stack) { if (!memory_ || !stack) { BPLOG(ERROR) << "Can't get caller frame without memory or stack"; return NULL; } - StackFrameAMD64 *last_frame = static_cast( - stack->frames()->back()); - - //FIXME: this pretty much doesn't work at all due to FPO - // being enabled by default. - // Brain-dead stackwalking: - // %rip_new = *(%rbp_old + 8) - // %rsp_new = %rbp_old + 16 - // %rbp_new = *(%rbp_old) - - // A caller frame must reside higher in memory than its callee frames. - // Anything else is an error, or an indication that we've reached the - // end of the stack. - u_int64_t stack_pointer = last_frame->context.rbp + 16; - if (stack_pointer <= last_frame->context.rsp) { - return NULL; + const vector &frames = *stack->frames(); + StackFrameAMD64 *last_frame = static_cast(frames.back()); + scoped_ptr new_frame; + + // If we have DWARF CFI information, use it. + if (!new_frame.get()) { + scoped_ptr cfi_frame_info(resolver_ + ->FindCFIFrameInfo(last_frame)); + if (cfi_frame_info.get()) + new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get())); } - u_int64_t instruction; - if (!memory_->GetMemoryAtAddress(last_frame->context.rbp + 8, - &instruction) || - instruction <= 1) { + // If nothing worked, tell the caller. + if (!new_frame.get()) return NULL; - } - - u_int64_t stack_base; - if (!memory_->GetMemoryAtAddress(last_frame->context.rbp, - &stack_base) || - stack_base <= 1) { + + // Treat an instruction address of 0 as end-of-stack. + if (new_frame->context.rip == 0) return NULL; - } - StackFrameAMD64 *frame = new StackFrameAMD64(); - - frame->context = last_frame->context; - frame->context.rip = instruction; - frame->context.rsp = stack_pointer; - frame->context.rbp = stack_base; - frame->context_validity = StackFrameAMD64::CONTEXT_VALID_RIP | - StackFrameAMD64::CONTEXT_VALID_RSP | - StackFrameAMD64::CONTEXT_VALID_RBP; + // If the new stack pointer is at a lower address than the old, then + // that's clearly incorrect. Treat this as end-of-stack to enforce + // progress and avoid infinite loops. + if (new_frame->context.rsp <= last_frame->context.rsp) + return NULL; - frame->instruction = frame->context.rip - 1; + // new_frame->context.rip is the return address, which is one instruction + // past the CALL that caused us to arrive at the callee. Set + // new_frame->instruction to one less than that. This won't reference the + // beginning of the CALL instruction, but it's guaranteed to be within + // the CALL, which is sufficient to get the source line information to + // match up with the line that contains a function call. Callers that + // require the exact return address value may access the context.rip + // field of StackFrameAMD64. + new_frame->instruction = new_frame->context.rip - 1; - return frame; + return new_frame.release(); } diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64.h 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64.h 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2007, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -42,6 +42,8 @@ #include "google_breakpad/common/breakpad_types.h" #include "google_breakpad/common/minidump_format.h" #include "google_breakpad/processor/stackwalker.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/cfi_frame_info.h" namespace google_breakpad { @@ -61,16 +63,29 @@ SourceLineResolverInterface *resolver); private: + // A STACK CFI-driven frame walker for the AMD64 + typedef SimpleCFIWalker CFIWalker; + // Implementation of Stackwalker, using amd64 context (stack pointer in %rsp, // stack base in %rbp) and stack conventions (saved stack pointer at 0(%rbp)) virtual StackFrame* GetContextFrame(); - virtual StackFrame* GetCallerFrame( - const CallStack *stack, - const vector< linked_ptr > &stack_frame_info); + virtual StackFrame* GetCallerFrame(const CallStack *stack); + + // Use cfi_frame_info (derived from STACK CFI records) to construct + // the frame that called frames.back(). The caller takes ownership + // of the returned frame. Return NULL on failure. + StackFrameAMD64 *GetCallerByCFIFrameInfo(const vector &frames, + CFIFrameInfo *cfi_frame_info); // Stores the CPU context corresponding to the innermost stack frame to // be returned by GetContextFrame. const MDRawContextAMD64 *context_; + + // Our register map, for cfi_walker_. + static const CFIWalker::RegisterSet cfi_register_map_[]; + + // Our CFI frame walker. + const CFIWalker cfi_walker_; }; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_amd64_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,334 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// stackwalker_amd64_unittest.cc: Unit tests for StackwalkerAMD64 class. + +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/stackwalker_unittest_utils.h" +#include "processor/stackwalker_amd64.h" +#include "processor/test_assembler.h" + +using google_breakpad::BasicSourceLineResolver; +using google_breakpad::CallStack; +using google_breakpad::StackFrame; +using google_breakpad::StackFrameAMD64; +using google_breakpad::StackwalkerAMD64; +using google_breakpad::SystemInfo; +using google_breakpad::TestAssembler::kLittleEndian; +using google_breakpad::TestAssembler::Label; +using google_breakpad::TestAssembler::Section; +using std::string; +using std::vector; +using testing::_; +using testing::Return; +using testing::SetArgumentPointee; +using testing::Test; + +class StackwalkerAMD64Fixture { + public: + StackwalkerAMD64Fixture() + : stack_section(kLittleEndian), + // Give the two modules reasonable standard locations and names + // for tests to play with. + module1(0x40000000c0000000ULL, 0x10000, "module1", "version1"), + module2(0x50000000b0000000ULL, 0x10000, "module2", "version2") { + // Identify the system as a Linux system. + system_info.os = "Linux"; + system_info.os_short = "linux"; + system_info.os_version = "Horrendous Hippo"; + system_info.cpu = "x86"; + system_info.cpu_info = ""; + + // Put distinctive values in the raw CPU context. + BrandContext(&raw_context); + + // Create some modules with some stock debugging information. + modules.Add(&module1); + modules.Add(&module2); + + // By default, none of the modules have symbol info; call + // SetModuleSymbols to override this. + EXPECT_CALL(supplier, GetSymbolFile(_, _, _, _)) + .WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND)); + } + + // Set the Breakpad symbol information that supplier should return for + // MODULE to INFO. + void SetModuleSymbols(MockCodeModule *module, const string &info) { + EXPECT_CALL(supplier, GetSymbolFile(module, &system_info, _, _)) + .WillRepeatedly(DoAll(SetArgumentPointee<3>(info), + Return(MockSymbolSupplier::FOUND))); + } + + // Populate stack_region with the contents of stack_section. Use + // stack_section.start() as the region's starting address. + void RegionFromSection() { + string contents; + ASSERT_TRUE(stack_section.GetContents(&contents)); + stack_region.Init(stack_section.start().Value(), contents); + } + + // Fill RAW_CONTEXT with pseudo-random data, for round-trip checking. + void BrandContext(MDRawContextAMD64 *raw_context) { + u_int8_t x = 173; + for (size_t i = 0; i < sizeof(*raw_context); i++) + reinterpret_cast(raw_context)[i] = (x += 17); + } + + SystemInfo system_info; + MDRawContextAMD64 raw_context; + Section stack_section; + MockMemoryRegion stack_region; + MockCodeModule module1; + MockCodeModule module2; + MockCodeModules modules; + MockSymbolSupplier supplier; + BasicSourceLineResolver resolver; + CallStack call_stack; + const vector *frames; +}; + +class GetContextFrame: public StackwalkerAMD64Fixture, public Test { }; + +TEST_F(GetContextFrame, Simple) { + // There should be no references to the stack in this walk: we don't + // provide any call frame information, so trying to reconstruct the + // context frame's caller should fail. So there's no need for us to + // provide stack contents. + raw_context.rip = 0x40000000c0000200ULL; + raw_context.rbp = 0x8000000080000000ULL; + + StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_GE(1U, frames->size()); + StackFrameAMD64 *frame = static_cast(frames->at(0)); + // Check that the values from the original raw context made it + // through to the context in the stack frame. + EXPECT_TRUE(memcmp(&raw_context, &frame->context, sizeof(raw_context)) == 0); +} + +struct CFIFixture: public StackwalkerAMD64Fixture { + CFIFixture() { + // Provide a bunch of STACK CFI records; we'll walk to the caller + // from every point in this series, expecting to find the same set + // of register values. + SetModuleSymbols(&module1, + // The youngest frame's function. + "FUNC 4000 1000 10 enchiridion\n" + // Initially, just a return address. + "STACK CFI INIT 4000 100 .cfa: $rsp 8 + .ra: .cfa 8 - ^\n" + // Push %rbx. + "STACK CFI 4001 .cfa: $rsp 16 + $rbx: .cfa 16 - ^\n" + // Save %r12 in %rbx. Weird, but permitted. + "STACK CFI 4002 $r12: $rbx\n" + // Allocate frame space, and save %r13. + "STACK CFI 4003 .cfa: $rsp 40 + $r13: .cfa 32 - ^\n" + // Put the return address in %r13. + "STACK CFI 4005 .ra: $r13\n" + // Save %rbp, and use it as a frame pointer. + "STACK CFI 4006 .cfa: $rbp 16 + $rbp: .cfa 24 - ^\n" + + // The calling function. + "FUNC 5000 1000 10 epictetus\n" + // Mark it as end of stack. + "STACK CFI INIT 5000 1000 .cfa: $rsp .ra 0\n"); + + // Provide some distinctive values for the caller's registers. + expected.rsp = 0x8000000080000000ULL; + expected.rip = 0x40000000c0005510ULL; + expected.rbp = 0x68995b1de4700266ULL; + expected.rbx = 0x5a5beeb38de23be8ULL; + expected.r12 = 0xed1b02e8cc0fc79cULL; + expected.r13 = 0x1d20ad8acacbe930ULL; + expected.r14 = 0xe94cffc2f7adaa28ULL; + expected.r15 = 0xb638d17d8da413b5ULL; + + // By default, registers are unchanged. + raw_context = expected; + } + + // Walk the stack, using stack_section as the contents of the stack + // and raw_context as the current register values. (Set + // raw_context.rsp to the stack's starting address.) Expect two + // stack frames; in the older frame, expect the callee-saves + // registers to have values matching those in 'expected'. + void CheckWalk() { + RegionFromSection(); + raw_context.rsp = stack_section.start().Value(); + + StackwalkerAMD64 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameAMD64 *frame0 = static_cast(frames->at(0)); + ASSERT_EQ(StackFrameAMD64::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ("enchiridion", frame0->function_name); + EXPECT_EQ(0x40000000c0004000ULL, frame0->function_base); + + StackFrameAMD64 *frame1 = static_cast(frames->at(1)); + ASSERT_EQ((StackFrameAMD64::CONTEXT_VALID_RIP | + StackFrameAMD64::CONTEXT_VALID_RSP | + StackFrameAMD64::CONTEXT_VALID_RBP | + StackFrameAMD64::CONTEXT_VALID_RBX | + StackFrameAMD64::CONTEXT_VALID_R12 | + StackFrameAMD64::CONTEXT_VALID_R13 | + StackFrameAMD64::CONTEXT_VALID_R14 | + StackFrameAMD64::CONTEXT_VALID_R15), + frame1->context_validity); + EXPECT_EQ(expected.rip, frame1->context.rip); + EXPECT_EQ(expected.rsp, frame1->context.rsp); + EXPECT_EQ(expected.rbp, frame1->context.rbp); + EXPECT_EQ(expected.rbx, frame1->context.rbx); + EXPECT_EQ(expected.r12, frame1->context.r12); + EXPECT_EQ(expected.r13, frame1->context.r13); + EXPECT_EQ(expected.r14, frame1->context.r14); + EXPECT_EQ(expected.r15, frame1->context.r15); + EXPECT_EQ("epictetus", frame1->function_name); + } + + // The values we expect to find for the caller's registers. + MDRawContextAMD64 expected; +}; + +class CFI: public CFIFixture, public Test { }; + +TEST_F(CFI, At4000) { + Label frame1_rsp = expected.rsp; + stack_section + .D64(0x40000000c0005510ULL) // return address + .Mark(&frame1_rsp); // This effectively sets stack_section.start(). + raw_context.rip = 0x40000000c0004000ULL; + CheckWalk(); +} + +TEST_F(CFI, At4001) { + Label frame1_rsp = expected.rsp; + stack_section + .D64(0x5a5beeb38de23be8ULL) // saved %rbx + .D64(0x40000000c0005510ULL) // return address + .Mark(&frame1_rsp); // This effectively sets stack_section.start(). + raw_context.rip = 0x40000000c0004001ULL; + raw_context.rbx = 0xbe0487d2f9eafe29ULL; // callee's (distinct) %rbx value + CheckWalk(); +} + +TEST_F(CFI, At4002) { + Label frame1_rsp = expected.rsp; + stack_section + .D64(0x5a5beeb38de23be8ULL) // saved %rbx + .D64(0x40000000c0005510ULL) // return address + .Mark(&frame1_rsp); // This effectively sets stack_section.start(). + raw_context.rip = 0x40000000c0004002ULL; + raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12 + raw_context.r12 = 0xb0118de918a4bceaULL; // callee's (distinct) %r12 value + CheckWalk(); +} + +TEST_F(CFI, At4003) { + Label frame1_rsp = expected.rsp; + stack_section + .D64(0x0e023828dffd4d81ULL) // garbage + .D64(0x1d20ad8acacbe930ULL) // saved %r13 + .D64(0x319e68b49e3ace0fULL) // garbage + .D64(0x5a5beeb38de23be8ULL) // saved %rbx + .D64(0x40000000c0005510ULL) // return address + .Mark(&frame1_rsp); // This effectively sets stack_section.start(). + raw_context.rip = 0x40000000c0004003ULL; + raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12 + raw_context.r12 = 0x89d04fa804c87a43ULL; // callee's (distinct) %r12 + raw_context.r13 = 0x5118e02cbdb24b03ULL; // callee's (distinct) %r13 + CheckWalk(); +} + +// The results here should be the same as those at module offset 0x4003. +TEST_F(CFI, At4004) { + Label frame1_rsp = expected.rsp; + stack_section + .D64(0x0e023828dffd4d81ULL) // garbage + .D64(0x1d20ad8acacbe930ULL) // saved %r13 + .D64(0x319e68b49e3ace0fULL) // garbage + .D64(0x5a5beeb38de23be8ULL) // saved %rbx + .D64(0x40000000c0005510ULL) // return address + .Mark(&frame1_rsp); // This effectively sets stack_section.start(). + raw_context.rip = 0x40000000c0004004ULL; + raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12 + raw_context.r12 = 0x89d04fa804c87a43ULL; // callee's (distinct) %r12 + raw_context.r13 = 0x5118e02cbdb24b03ULL; // callee's (distinct) %r13 + CheckWalk(); +} + +TEST_F(CFI, At4005) { + Label frame1_rsp = expected.rsp; + stack_section + .D64(0x4b516dd035745953ULL) // garbage + .D64(0x1d20ad8acacbe930ULL) // saved %r13 + .D64(0xa6d445e16ae3d872ULL) // garbage + .D64(0x5a5beeb38de23be8ULL) // saved %rbx + .D64(0xaa95fa054aedfbaeULL) // garbage + .Mark(&frame1_rsp); // This effectively sets stack_section.start(). + raw_context.rip = 0x40000000c0004005ULL; + raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12 + raw_context.r12 = 0x46b1b8868891b34aULL; // callee's %r12 + raw_context.r13 = 0x40000000c0005510ULL; // return address + CheckWalk(); +} + +TEST_F(CFI, At4006) { + Label frame0_rbp; + Label frame1_rsp = expected.rsp; + stack_section + .D64(0x043c6dfceb91aa34ULL) // garbage + .D64(0x1d20ad8acacbe930ULL) // saved %r13 + .D64(0x68995b1de4700266ULL) // saved %rbp + .Mark(&frame0_rbp) // frame pointer points here + .D64(0x5a5beeb38de23be8ULL) // saved %rbx + .D64(0xf015ee516ad89eabULL) // garbage + .Mark(&frame1_rsp); // This effectively sets stack_section.start(). + raw_context.rip = 0x40000000c0004006ULL; + raw_context.rbp = frame0_rbp.Value(); + raw_context.rbx = 0xed1b02e8cc0fc79cULL; // saved %r12 + raw_context.r12 = 0x26e007b341acfebdULL; // callee's %r12 + raw_context.r13 = 0x40000000c0005510ULL; // return address + CheckWalk(); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,185 @@ +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// stackwalker_arm.cc: arm-specific stackwalker. +// +// See stackwalker_arm.h for documentation. +// +// Author: Mark Mentovai, Ted Mielczarek, Jim Blandy + + +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/cfi_frame_info.h" +#include "processor/logging.h" +#include "processor/scoped_ptr.h" +#include "processor/stackwalker_arm.h" + +namespace google_breakpad { + + +StackwalkerARM::StackwalkerARM(const SystemInfo *system_info, + const MDRawContextARM *context, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver) + : Stackwalker(system_info, memory, modules, supplier, resolver), + context_(context), + context_frame_validity_(StackFrameARM::CONTEXT_VALID_ALL) { } + + +StackFrame* StackwalkerARM::GetContextFrame() { + if (!context_ || !memory_) { + BPLOG(ERROR) << "Can't get context frame without context or memory"; + return NULL; + } + + StackFrameARM *frame = new StackFrameARM(); + + // The instruction pointer is stored directly in a register (r15), so pull it + // straight out of the CPU context structure. + frame->context = *context_; + frame->context_validity = context_frame_validity_; + frame->instruction = frame->context.iregs[15]; + + return frame; +} + + +StackFrame* StackwalkerARM::GetCallerFrame(const CallStack *stack) { + if (!memory_ || !stack) { + BPLOG(ERROR) << "Can't get caller frame without memory or stack"; + return NULL; + } + + const vector &frames = *stack->frames(); + StackFrameARM *last_frame = static_cast(frames.back()); + + // See if we have DWARF call frame information covering this address. + scoped_ptr cfi_frame_info(resolver_ + ->FindCFIFrameInfo(last_frame)); + if (cfi_frame_info == NULL) + // Unfortunately, CFI is our only option on the ARM for now. If we + // add a second strategy, we should put each one in its own function. + return NULL; + + static const char *register_names[] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "r12", "sp", "lr", "pc", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "fps", "cpsr", + NULL + }; + + // Populate a dictionary with the valid register values in last_frame. + CFIFrameInfo::RegisterValueMap callee_registers; + for (int i = 0; register_names[i]; i++) + if (last_frame->context_validity & StackFrameARM::RegisterValidFlag(i)) + callee_registers[register_names[i]] = last_frame->context.iregs[i]; + + // Use the STACK CFI data to recover the caller's register values. + CFIFrameInfo::RegisterValueMap caller_registers; + if (!cfi_frame_info->FindCallerRegs(callee_registers, *memory_, + &caller_registers)) + return NULL; + + // Construct a new stack frame given the values the CFI recovered. + scoped_ptr frame(new StackFrameARM()); + for (int i = 0; register_names[i]; i++) { + CFIFrameInfo::RegisterValueMap::iterator entry = + caller_registers.find(register_names[i]); + if (entry != caller_registers.end()) { + // We recovered the value of this register; fill the context with the + // value from caller_registers. + frame->context_validity |= StackFrameARM::RegisterValidFlag(i); + frame->context.iregs[i] = entry->second; + } else if (4 <= i && i <= 11 && (last_frame->context_validity & + StackFrameARM::RegisterValidFlag(i))) { + // If the STACK CFI data doesn't mention some callee-saves register, and + // it is valid in the callee, assume the callee has not yet changed it. + // Registers r4 through r11 are callee-saves, according to the Procedure + // Call Standard for the ARM Architecture, which the Linux ABI follows. + frame->context_validity |= StackFrameARM::RegisterValidFlag(i); + frame->context.iregs[i] = last_frame->context.iregs[i]; + } + } + // If the CFI doesn't recover the PC explicitly, then use .ra. + if (! (frame->context_validity & StackFrameARM::CONTEXT_VALID_PC)) { + CFIFrameInfo::RegisterValueMap::iterator entry = + caller_registers.find(".ra"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameARM::CONTEXT_VALID_PC; + frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = entry->second; + } + } + // If the CFI doesn't recover the SP explicitly, then use .cfa. + if (! (frame->context_validity & StackFrameARM::CONTEXT_VALID_SP)) { + CFIFrameInfo::RegisterValueMap::iterator entry = + caller_registers.find(".cfa"); + if (entry != caller_registers.end()) { + frame->context_validity |= StackFrameARM::CONTEXT_VALID_SP; + frame->context.iregs[MD_CONTEXT_ARM_REG_SP] = entry->second; + } + } + + // If we didn't recover the PC and the SP, then the frame isn't very useful. + static const int essentials = (StackFrameARM::CONTEXT_VALID_SP + | StackFrameARM::CONTEXT_VALID_PC); + if ((frame->context_validity & essentials) != essentials) + return NULL; + + // An instruction address of zero marks the end of the stack. + if (frame->context.iregs[MD_CONTEXT_ARM_REG_PC] == 0) + return NULL; + + // If the new stack pointer is at a lower address than the old, then + // that's clearly incorrect. Treat this as end-of-stack to enforce + // progress and avoid infinite loops. + if (frame->context.iregs[MD_CONTEXT_ARM_REG_SP] + < last_frame->context.iregs[MD_CONTEXT_ARM_REG_SP]) + return NULL; + + // The new frame's context's PC is the return address, which is one + // instruction past the instruction that caused us to arrive at the + // callee. Set new_frame->instruction to one less than the PC. This won't + // reference the beginning of the call instruction, but it's at least + // within it, which is sufficient to get the source line information to + // match up with the line that contains the function call. Callers that + // require the exact return address value may access + // frame->context.iregs[MD_CONTEXT_ARM_REG_PC]. + frame->instruction = frame->context.iregs[MD_CONTEXT_ARM_REG_PC] - 1; + + return frame.release(); +} + + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,90 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// stackwalker_arm.h: arm-specific stackwalker. +// +// Provides stack frames given arm register context and a memory region +// corresponding to an arm stack. +// +// Author: Mark Mentovai, Ted Mielczarek + + +#ifndef PROCESSOR_STACKWALKER_ARM_H__ +#define PROCESSOR_STACKWALKER_ARM_H__ + + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/stackwalker.h" + +namespace google_breakpad { + +class CodeModules; + +class StackwalkerARM : public Stackwalker { + public: + // context is an arm context object that gives access to arm-specific + // register state corresponding to the innermost called frame to be + // included in the stack. The other arguments are passed directly through + // to the base Stackwalker constructor. + StackwalkerARM(const SystemInfo *system_info, + const MDRawContextARM *context, + MemoryRegion *memory, + const CodeModules *modules, + SymbolSupplier *supplier, + SourceLineResolverInterface *resolver); + + // Change the context validity mask of the frame returned by + // GetContextFrame to VALID. This is only for use by unit tests; the + // default behavior is correct for all application code. + void SetContextFrameValidity(int valid) { context_frame_validity_ = valid; } + + private: + // Implementation of Stackwalker, using arm context and stack conventions. + // TODO: currently stubbed out, needs CFI symbol dumper support + virtual StackFrame* GetContextFrame(); + virtual StackFrame* GetCallerFrame(const CallStack *stack); + + // Stores the CPU context corresponding to the youngest stack frame, to + // be returned by GetContextFrame. + const MDRawContextARM *context_; + + // Validity mask for youngest stack frame. This is always + // CONTEXT_VALID_ALL in real use; it is only changeable for the sake of + // unit tests. + int context_frame_validity_; +}; + + +} // namespace google_breakpad + + +#endif // PROCESSOR_STACKWALKER_ARM_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_arm_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,440 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// stackwalker_arm_unittest.cc: Unit tests for StackwalkerARM class. + +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/stackwalker_unittest_utils.h" +#include "processor/stackwalker_arm.h" +#include "processor/test_assembler.h" +#include "processor/windows_frame_info.h" + +using google_breakpad::BasicSourceLineResolver; +using google_breakpad::CallStack; +using google_breakpad::StackFrame; +using google_breakpad::StackFrameARM; +using google_breakpad::StackwalkerARM; +using google_breakpad::SystemInfo; +using google_breakpad::WindowsFrameInfo; +using google_breakpad::TestAssembler::kLittleEndian; +using google_breakpad::TestAssembler::Label; +using google_breakpad::TestAssembler::Section; +using std::string; +using std::vector; +using testing::_; +using testing::Return; +using testing::SetArgumentPointee; +using testing::Test; + +class StackwalkerARMFixture { + public: + StackwalkerARMFixture() + : stack_section(kLittleEndian), + // Give the two modules reasonable standard locations and names + // for tests to play with. + module1(0x40000000, 0x10000, "module1", "version1"), + module2(0x50000000, 0x10000, "module2", "version2") { + // Identify the system as a Linux system. + system_info.os = "Linux"; + system_info.os_short = "linux"; + system_info.os_version = "Lugubrious Labrador"; + system_info.cpu = "arm"; + system_info.cpu_info = ""; + + // Put distinctive values in the raw CPU context. + BrandContext(&raw_context); + + // Create some modules with some stock debugging information. + modules.Add(&module1); + modules.Add(&module2); + + // By default, none of the modules have symbol info; call + // SetModuleSymbols to override this. + EXPECT_CALL(supplier, GetSymbolFile(_, _, _, _)) + .WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND)); + } + + // Set the Breakpad symbol information that supplier should return for + // MODULE to INFO. + void SetModuleSymbols(MockCodeModule *module, const string &info) { + EXPECT_CALL(supplier, GetSymbolFile(module, &system_info, _, _)) + .WillRepeatedly(DoAll(SetArgumentPointee<3>(info), + Return(MockSymbolSupplier::FOUND))); + } + + // Populate stack_region with the contents of stack_section. Use + // stack_section.start() as the region's starting address. + void RegionFromSection() { + string contents; + ASSERT_TRUE(stack_section.GetContents(&contents)); + stack_region.Init(stack_section.start().Value(), contents); + } + + // Fill RAW_CONTEXT with pseudo-random data, for round-trip checking. + void BrandContext(MDRawContextARM *raw_context) { + u_int8_t x = 173; + for (size_t i = 0; i < sizeof(*raw_context); i++) + reinterpret_cast(raw_context)[i] = (x += 17); + } + + SystemInfo system_info; + MDRawContextARM raw_context; + Section stack_section; + MockMemoryRegion stack_region; + MockCodeModule module1; + MockCodeModule module2; + MockCodeModules modules; + MockSymbolSupplier supplier; + BasicSourceLineResolver resolver; + CallStack call_stack; + const vector *frames; +}; + +class GetContextFrame: public StackwalkerARMFixture, public Test { }; + +TEST_F(GetContextFrame, Simple) { + // Since we have no call frame information, and all unwinding + // requires call frame information, the stack walk will end after + // the first frame. + StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(1U, frames->size()); + StackFrameARM *frame = static_cast(frames->at(0)); + // Check that the values from the original raw context made it + // through to the context in the stack frame. + EXPECT_TRUE(memcmp(&raw_context, &frame->context, sizeof(raw_context)) == 0); +} + +struct CFIFixture: public StackwalkerARMFixture { + CFIFixture() { + // Provide a bunch of STACK CFI records; we'll walk to the caller + // from every point in this series, expecting to find the same set + // of register values. + SetModuleSymbols(&module1, + // The youngest frame's function. + "FUNC 4000 1000 10 enchiridion\n" + // Initially, nothing has been pushed on the stack, + // and the return address is still in the link register. + "STACK CFI INIT 4000 100 .cfa: sp .ra: lr\n" + // Push r4, the frame pointer, and the link register. + "STACK CFI 4001 .cfa: sp 12 + r4: .cfa 12 - ^" + " r11: .cfa 8 - ^ .ra: .cfa 4 - ^\n" + // Save r4..r7 in r0..r3: verify that we populate + // the youngest frame with all the values we have. + "STACK CFI 4002 r4: r0 r5: r1 r6: r2 r7: r3\n" + // Restore r4..r7. Save the non-callee-saves register r1. + "STACK CFI 4003 .cfa: sp 16 + r1: .cfa 16 - ^" + " r4: r4 r5: r5 r6: r6 r7: r7\n" + // Move the .cfa back four bytes, to point at the return + // address, and restore the sp explicitly. + "STACK CFI 4005 .cfa: sp 12 + r1: .cfa 12 - ^" + " r11: .cfa 4 - ^ .ra: .cfa ^ sp: .cfa 4 +\n" + // Recover the PC explicitly from a new stack slot; + // provide garbage for the .ra. + "STACK CFI 4006 .cfa: sp 16 + pc: .cfa 16 - ^\n" + + // The calling function. + "FUNC 5000 1000 10 epictetus\n" + // Mark it as end of stack. + "STACK CFI INIT 5000 1000 .cfa: 0 .ra: 0\n" + + // A function whose CFI makes the stack pointer + // go backwards. + "FUNC 6000 1000 20 palinal\n" + "STACK CFI INIT 6000 1000 .cfa: sp 4 - .ra: lr\n" + + // A function with CFI expressions that can't be + // evaluated. + "FUNC 7000 1000 20 rhetorical\n" + "STACK CFI INIT 7000 1000 .cfa: moot .ra: ambiguous\n"); + + // Provide some distinctive values for the caller's registers. + expected.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40005510; + expected.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000; + expected.iregs[4] = 0xb5d55e68; + expected.iregs[5] = 0xebd134f3; + expected.iregs[6] = 0xa31e74bc; + expected.iregs[7] = 0x2dcb16b3; + expected.iregs[8] = 0x2ada2137; + expected.iregs[9] = 0xbbbb557d; + expected.iregs[10] = 0x48bf8ca7; + expected.iregs[MD_CONTEXT_ARM_REG_FP] = 0x8112e110; + + // Expect CFI to recover all callee-saves registers. Since CFI is the + // only stack frame construction technique we have, aside from the + // context frame itself, there's no way for us to have a set of valid + // registers smaller than this. + expected_validity = (StackFrameARM::CONTEXT_VALID_PC | + StackFrameARM::CONTEXT_VALID_SP | + StackFrameARM::CONTEXT_VALID_R4 | + StackFrameARM::CONTEXT_VALID_R5 | + StackFrameARM::CONTEXT_VALID_R6 | + StackFrameARM::CONTEXT_VALID_R7 | + StackFrameARM::CONTEXT_VALID_R8 | + StackFrameARM::CONTEXT_VALID_R9 | + StackFrameARM::CONTEXT_VALID_R10 | + StackFrameARM::CONTEXT_VALID_FP); + + // By default, context frames provide all registers, as normal. + context_frame_validity = StackFrameARM::CONTEXT_VALID_ALL; + + // By default, registers are unchanged. + raw_context = expected; + } + + // Walk the stack, using stack_section as the contents of the stack + // and raw_context as the current register values. (Set the stack + // pointer to the stack's starting address.) Expect two stack + // frames; in the older frame, expect the callee-saves registers to + // have values matching those in 'expected'. + void CheckWalk() { + RegionFromSection(); + raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = stack_section.start().Value(); + + StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + walker.SetContextFrameValidity(context_frame_validity); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameARM *frame0 = static_cast(frames->at(0)); + ASSERT_EQ(context_frame_validity, frame0->context_validity); + EXPECT_EQ("enchiridion", frame0->function_name); + EXPECT_EQ(0x40004000U, frame0->function_base); + + StackFrameARM *frame1 = static_cast(frames->at(1)); + ASSERT_EQ(expected_validity, frame1->context_validity); + if (expected_validity & StackFrameARM::CONTEXT_VALID_R1) + EXPECT_EQ(expected.iregs[1], frame1->context.iregs[1]); + if (expected_validity & StackFrameARM::CONTEXT_VALID_R4) + EXPECT_EQ(expected.iregs[4], frame1->context.iregs[4]); + if (expected_validity & StackFrameARM::CONTEXT_VALID_R5) + EXPECT_EQ(expected.iregs[5], frame1->context.iregs[5]); + if (expected_validity & StackFrameARM::CONTEXT_VALID_R6) + EXPECT_EQ(expected.iregs[6], frame1->context.iregs[6]); + if (expected_validity & StackFrameARM::CONTEXT_VALID_R7) + EXPECT_EQ(expected.iregs[7], frame1->context.iregs[7]); + if (expected_validity & StackFrameARM::CONTEXT_VALID_R8) + EXPECT_EQ(expected.iregs[8], frame1->context.iregs[8]); + if (expected_validity & StackFrameARM::CONTEXT_VALID_R9) + EXPECT_EQ(expected.iregs[9], frame1->context.iregs[9]); + if (expected_validity & StackFrameARM::CONTEXT_VALID_R10) + EXPECT_EQ(expected.iregs[10], frame1->context.iregs[10]); + if (expected_validity & StackFrameARM::CONTEXT_VALID_FP) + EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_FP], + frame1->context.iregs[MD_CONTEXT_ARM_REG_FP]); + + // We would never have gotten a frame in the first place if the SP + // and PC weren't valid or ->instruction weren't set. + EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_SP], + frame1->context.iregs[MD_CONTEXT_ARM_REG_SP]); + EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC], + frame1->context.iregs[MD_CONTEXT_ARM_REG_PC]); + EXPECT_EQ(expected.iregs[MD_CONTEXT_ARM_REG_PC], + frame1->instruction + 1); + EXPECT_EQ("epictetus", frame1->function_name); + } + + // The values we expect to find for the caller's registers. + MDRawContextARM expected; + + // The validity mask for expected. + int expected_validity; + + // The validity mask to impose on the context frame. + int context_frame_validity; +}; + +class CFI: public CFIFixture, public Test { }; + +TEST_F(CFI, At4000) { + stack_section.start() = expected.iregs[MD_CONTEXT_ARM_REG_SP]; + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004000; + raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510; + CheckWalk(); +} + +TEST_F(CFI, At4001) { + Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP]; + stack_section + .D32(0xb5d55e68) // saved r4 + .D32(0x8112e110) // saved fp + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004001; + raw_context.iregs[4] = 0x635adc9f; // distinct callee r4 + raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp + CheckWalk(); +} + +// As above, but unwind from a context that has only the PC and SP. +TEST_F(CFI, At4001LimitedValidity) { + context_frame_validity = + StackFrameARM::CONTEXT_VALID_PC | StackFrameARM::CONTEXT_VALID_SP; + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004001; + raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp + Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP]; + stack_section + .D32(0xb5d55e68) // saved r4 + .D32(0x8112e110) // saved fp + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + expected_validity = (StackFrameARM::CONTEXT_VALID_PC + | StackFrameARM::CONTEXT_VALID_SP + | StackFrameARM::CONTEXT_VALID_FP + | StackFrameARM::CONTEXT_VALID_R4); + CheckWalk(); +} + +TEST_F(CFI, At4002) { + Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP]; + stack_section + .D32(0xfb81ff3d) // no longer saved r4 + .D32(0x8112e110) // saved fp + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004002; + raw_context.iregs[0] = 0xb5d55e68; // saved r4 + raw_context.iregs[1] = 0xebd134f3; // saved r5 + raw_context.iregs[2] = 0xa31e74bc; // saved r6 + raw_context.iregs[3] = 0x2dcb16b3; // saved r7 + raw_context.iregs[4] = 0xfdd35466; // distinct callee r4 + raw_context.iregs[5] = 0xf18c946c; // distinct callee r5 + raw_context.iregs[6] = 0xac2079e8; // distinct callee r6 + raw_context.iregs[7] = 0xa449829f; // distinct callee r7 + raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0xbe145fc4; // distinct callee fp + CheckWalk(); +} + +TEST_F(CFI, At4003) { + Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP]; + stack_section + .D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves) + .D32(0xcb78040e) // no longer saved r4 + .D32(0x8112e110) // saved fp + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004003; + raw_context.iregs[1] = 0xfb756319; // distinct callee r1 + raw_context.iregs[MD_CONTEXT_ARM_REG_FP] = 0x0a2857ea; // distinct callee fp + expected.iregs[1] = 0x48c8dd5a; // caller's r1 + expected_validity |= StackFrameARM::CONTEXT_VALID_R1; + CheckWalk(); +} + +// We have no new rule at module offset 0x4004, so the results here should +// be the same as those at module offset 0x4003. +TEST_F(CFI, At4004) { + Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP]; + stack_section + .D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves) + .D32(0xcb78040e) // no longer saved r4 + .D32(0x8112e110) // saved fp + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004004; + raw_context.iregs[1] = 0xfb756319; // distinct callee r1 + expected.iregs[1] = 0x48c8dd5a; // caller's r1 + expected_validity |= StackFrameARM::CONTEXT_VALID_R1; + CheckWalk(); +} + +// Here we move the .cfa, but provide an explicit rule to recover the SP, +// so again there should be no change in the registers recovered. +TEST_F(CFI, At4005) { + Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP]; + stack_section + .D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves) + .D32(0xf013f841) // no longer saved r4 + .D32(0x8112e110) // saved fp + .D32(0x40005510) // return address + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004005; + raw_context.iregs[1] = 0xfb756319; // distinct callee r1 + expected.iregs[1] = 0x48c8dd5a; // caller's r1 + expected_validity |= StackFrameARM::CONTEXT_VALID_R1; + CheckWalk(); +} + +// Here we provide an explicit rule for the PC, and have the saved .ra be +// bogus. +TEST_F(CFI, At4006) { + Label frame1_sp = expected.iregs[MD_CONTEXT_ARM_REG_SP]; + stack_section + .D32(0x40005510) // saved pc + .D32(0x48c8dd5a) // saved r1 (even though it's not callee-saves) + .D32(0xf013f841) // no longer saved r4 + .D32(0x8112e110) // saved fp + .D32(0xf8d15783) // .ra rule recovers this, which is garbage + .Mark(&frame1_sp); // This effectively sets stack_section.start(). + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40004006; + raw_context.iregs[1] = 0xfb756319; // callee's r1, different from caller's + expected.iregs[1] = 0x48c8dd5a; // caller's r1 + expected_validity |= StackFrameARM::CONTEXT_VALID_R1; + CheckWalk(); +} + +// Check that we reject rules that would cause the stack pointer to +// move in the wrong direction. +TEST_F(CFI, RejectBackwards) { + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40006000; + raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000; + raw_context.iregs[MD_CONTEXT_ARM_REG_LR] = 0x40005510; + StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(1U, frames->size()); +} + +// Check that we reject rules whose expressions' evaluation fails. +TEST_F(CFI, RejectBadExpressions) { + raw_context.iregs[MD_CONTEXT_ARM_REG_PC] = 0x40007000; + raw_context.iregs[MD_CONTEXT_ARM_REG_SP] = 0x80000000; + StackwalkerARM walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(1U, frames->size()); +} + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker.cc 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -47,11 +47,11 @@ #include "processor/linked_ptr.h" #include "processor/logging.h" #include "processor/scoped_ptr.h" -#include "processor/stack_frame_info.h" #include "processor/stackwalker_ppc.h" #include "processor/stackwalker_sparc.h" #include "processor/stackwalker_x86.h" #include "processor/stackwalker_amd64.h" +#include "processor/stackwalker_arm.h" namespace google_breakpad { @@ -64,8 +64,8 @@ : system_info_(system_info), memory_(memory), modules_(modules), - supplier_(supplier), - resolver_(resolver) { + resolver_(resolver), + supplier_(supplier) { } @@ -74,11 +74,6 @@ assert(stack); stack->Clear(); - // stack_frame_info parallels the CallStack. The vector is passed to the - // GetCallerFrame function. It contains information that may be helpful - // for stackwalking. - vector< linked_ptr > stack_frame_info; - // Begin with the context frame, and keep getting callers until there are // no more. @@ -90,8 +85,6 @@ // frame_pointer fields. The frame structure comes from either the // context frame (above) or a caller frame (below). - linked_ptr frame_info; - // Resolve the module information, if a module map was provided. if (modules_) { const CodeModule *module = @@ -100,22 +93,27 @@ frame->module = module; if (resolver_ && !resolver_->HasModule(frame->module->code_file()) && + no_symbol_modules_.find( + module->code_file()) == no_symbol_modules_.end() && supplier_) { - string symbol_file; + string symbol_data, symbol_file; SymbolSupplier::SymbolResult symbol_result = - supplier_->GetSymbolFile(module, system_info_, &symbol_file); + supplier_->GetSymbolFile(module, system_info_, + &symbol_file, &symbol_data); switch (symbol_result) { case SymbolSupplier::FOUND: - resolver_->LoadModule(frame->module->code_file(), symbol_file); + resolver_->LoadModuleUsingMapBuffer(frame->module->code_file(), + symbol_data); break; case SymbolSupplier::NOT_FOUND: + no_symbol_modules_.insert(module->code_file()); break; // nothing to do case SymbolSupplier::INTERRUPT: return false; } } - frame_info.reset(resolver_->FillSourceLineInfo(frame.get())); + resolver_->FillSourceLineInfo(frame.get()); } } @@ -123,12 +121,8 @@ // over the frame, because the stack now owns it. stack->frames_.push_back(frame.release()); - // Add the frame info to the parallel stack. - stack_frame_info.push_back(frame_info); - frame_info.reset(NULL); - // Get the next frame and take ownership. - frame.reset(GetCallerFrame(stack, stack_frame_info)); + frame.reset(GetCallerFrame(stack)); } return true; @@ -179,6 +173,13 @@ memory, modules, supplier, resolver); break; + + case MD_CONTEXT_ARM: + cpu_stackwalker = new StackwalkerARM(system_info, + context->GetContextARM(), + memory, modules, supplier, + resolver); + break; } BPLOG_IF(ERROR, !cpu_stackwalker) << "Unknown CPU type " << HexString(cpu) << @@ -187,5 +188,39 @@ return cpu_stackwalker; } +bool Stackwalker::InstructionAddressSeemsValid(u_int64_t address) { + const CodeModule *module = modules_->GetModuleForAddress(address); + if (!module) { + // not inside any loaded module + return false; + } + + if (!resolver_ || !supplier_) { + // we don't have a resolver and or symbol supplier, + // but we're inside a known module + return true; + } + + if (!resolver_->HasModule(module->code_file())) { + string symbol_data, symbol_file; + SymbolSupplier::SymbolResult symbol_result = + supplier_->GetSymbolFile(module, system_info_, + &symbol_file, &symbol_data); + + if (symbol_result != SymbolSupplier::FOUND || + !resolver_->LoadModuleUsingMapBuffer(module->code_file(), + symbol_data)) { + // we don't have symbols, but we're inside a loaded module + return true; + } + } + + StackFrame frame; + frame.module = module; + frame.instruction = address; + resolver_->FillSourceLineInfo(&frame); + // we have symbols, so return true if inside a function + return !frame.function_name.empty(); +} } // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc.cc 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc.cc 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -81,9 +81,7 @@ } -StackFrame* StackwalkerPPC::GetCallerFrame( - const CallStack *stack, - const vector< linked_ptr > &stack_frame_info) { +StackFrame* StackwalkerPPC::GetCallerFrame(const CallStack *stack) { if (!memory_ || !stack) { BPLOG(ERROR) << "Can't get caller frame without memory or stack"; return NULL; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc.h 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_ppc.h 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -65,9 +65,7 @@ // saved program counter in %srr0) and stack conventions (saved stack // pointer at 0(%r1), return address at 8(0(%r1)). virtual StackFrame* GetContextFrame(); - virtual StackFrame* GetCallerFrame( - const CallStack *stack, - const vector< linked_ptr > &stack_frame_info); + virtual StackFrame* GetCallerFrame(const CallStack *stack); // Stores the CPU context corresponding to the innermost stack frame to // be returned by GetContextFrame. diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_sparc.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_sparc.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_sparc.cc 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_sparc.cc 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2007, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -72,9 +72,7 @@ } -StackFrame* StackwalkerSPARC::GetCallerFrame( - const CallStack *stack, - const vector< linked_ptr > &stack_frame_info) { +StackFrame* StackwalkerSPARC::GetCallerFrame(const CallStack *stack) { if (!memory_ || !stack) { BPLOG(ERROR) << "Can't get caller frame without memory or stack"; return NULL; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_sparc.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_sparc.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_sparc.h 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_sparc.h 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2007, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -61,18 +61,10 @@ SourceLineResolverInterface *resolver); private: - // Implementation of Stackwalker, using x86 context (%ebp, %esp, %eip) and - // stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp], or - // alternate conventions as guided by stack_frame_info_). - // Implementation of Stackwalker, using ppc context (stack pointer in %r1, - // saved program counter in %srr0) and stack conventions (saved stack - // pointer at 0(%r1), return address at 8(0(%r1)). // Implementation of Stackwalker, using sparc context (%fp, %sp, %pc) and - // stack conventions (saved %sp at) + // stack conventions virtual StackFrame* GetContextFrame(); - virtual StackFrame* GetCallerFrame( - const CallStack *stack, - const vector< linked_ptr > &stack_frame_info); + virtual StackFrame* GetCallerFrame(const CallStack *stack); // Stores the CPU context corresponding to the innermost stack frame to // be returned by GetContextFrame. diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_unittest_utils.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_unittest_utils.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_unittest_utils.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_unittest_utils.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,175 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// Mock classes for writing stackwalker tests, shared amongst architectures. + +#ifndef PROCESSOR_STACKWALKER_UNITTEST_UTILS_H_ +#define PROCESSOR_STACKWALKER_UNITTEST_UTILS_H_ + +#include +#include +#include + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/code_module.h" +#include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/symbol_supplier.h" +#include "google_breakpad/processor/system_info.h" + +class MockMemoryRegion: public google_breakpad::MemoryRegion { + public: + MockMemoryRegion(): base_address_(0) { } + + // Set this region's address and contents. If we have placed an + // instance of this class in a test fixture class, individual tests + // can use this to provide the region's contents. + void Init(u_int64_t base_address, const std::string &contents) { + base_address_ = base_address; + contents_ = contents; + } + + u_int64_t GetBase() const { return base_address_; } + u_int32_t GetSize() const { return contents_.size(); } + + bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const { + return GetMemoryLittleEndian(address, value); + } + bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const { + return GetMemoryLittleEndian(address, value); + } + bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const { + return GetMemoryLittleEndian(address, value); + } + bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const { + return GetMemoryLittleEndian(address, value); + } + + private: + // Fetch a little-endian value from ADDRESS in contents_ whose size + // is BYTES, and store it in *VALUE. Return true on success. + template + bool GetMemoryLittleEndian(u_int64_t address, ValueType *value) const { + if (address < base_address_ || + address - base_address_ + sizeof(ValueType) > contents_.size()) + return false; + ValueType v = 0; + int start = address - base_address_; + // The loop condition is odd, but it's correct for size_t. + for (size_t i = sizeof(ValueType) - 1; i < sizeof(ValueType); i--) + v = (v << 8) | static_cast(contents_[start + i]); + *value = v; + return true; + } + + u_int64_t base_address_; + std::string contents_; +}; + +class MockCodeModule: public google_breakpad::CodeModule { + public: + MockCodeModule(u_int64_t base_address, u_int64_t size, + const std::string &code_file, const std::string &version) + : base_address_(base_address), size_(size), code_file_(code_file) { } + + u_int64_t base_address() const { return base_address_; } + u_int64_t size() const { return size_; } + std::string code_file() const { return code_file_; } + std::string code_identifier() const { return code_file_; } + std::string debug_file() const { return code_file_; } + std::string debug_identifier() const { return code_file_; } + std::string version() const { return version_; } + const google_breakpad::CodeModule *Copy() const { + abort(); // Tests won't use this. + } + + private: + u_int64_t base_address_; + u_int64_t size_; + std::string code_file_; + std::string version_; +}; + +class MockCodeModules: public google_breakpad::CodeModules { + public: + typedef google_breakpad::CodeModule CodeModule; + typedef google_breakpad::CodeModules CodeModules; + + void Add(const MockCodeModule *module) { + modules_.push_back(module); + } + + unsigned int module_count() const { return modules_.size(); } + + const CodeModule *GetModuleForAddress(u_int64_t address) const { + for (ModuleVector::const_iterator i = modules_.begin(); + i != modules_.end(); i++) { + const MockCodeModule *module = *i; + if (module->base_address() <= address && + address - module->base_address() < module->size()) + return module; + } + return NULL; + }; + + const CodeModule *GetMainModule() const { return modules_[0]; } + + const CodeModule *GetModuleAtSequence(unsigned int sequence) const { + return modules_.at(sequence); + } + + const CodeModule *GetModuleAtIndex(unsigned int index) const { + return modules_.at(index); + } + + const CodeModules *Copy() const { abort(); } // Tests won't use this. + + private: + typedef std::vector ModuleVector; + ModuleVector modules_; +}; + +class MockSymbolSupplier: public google_breakpad::SymbolSupplier { + public: + typedef google_breakpad::CodeModule CodeModule; + typedef google_breakpad::SystemInfo SystemInfo; + MOCK_METHOD3(GetSymbolFile, SymbolResult(const CodeModule *module, + const SystemInfo *system_info, + std::string *symbol_file)); + MOCK_METHOD4(GetSymbolFile, SymbolResult(const CodeModule *module, + const SystemInfo *system_info, + std::string *symbol_file, + std::string *symbol_data)); +}; + +#endif // PROCESSOR_STACKWALKER_UNITTEST_UTILS_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.cc 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -36,18 +36,47 @@ #include "processor/postfix_evaluator-inl.h" -#include "processor/stackwalker_x86.h" #include "google_breakpad/processor/call_stack.h" #include "google_breakpad/processor/code_modules.h" #include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" #include "google_breakpad/processor/stack_frame_cpu.h" -#include "processor/linked_ptr.h" #include "processor/logging.h" -#include "processor/stack_frame_info.h" +#include "processor/scoped_ptr.h" +#include "processor/stackwalker_x86.h" +#include "processor/windows_frame_info.h" +#include "processor/cfi_frame_info.h" namespace google_breakpad { +const StackwalkerX86::CFIWalker::RegisterSet +StackwalkerX86::cfi_register_map_[] = { + // It may seem like $eip and $esp are callee-saves, because (with Unix or + // cdecl calling conventions) the callee is responsible for having them + // restored upon return. But the callee_saves flags here really means + // that the walker should assume they're unchanged if the CFI doesn't + // mention them, which is clearly wrong for $eip and $esp. + { "$eip", ".ra", false, + StackFrameX86::CONTEXT_VALID_EIP, &MDRawContextX86::eip }, + { "$esp", ".cfa", false, + StackFrameX86::CONTEXT_VALID_ESP, &MDRawContextX86::esp }, + { "$ebp", NULL, true, + StackFrameX86::CONTEXT_VALID_EBP, &MDRawContextX86::ebp }, + { "$eax", NULL, false, + StackFrameX86::CONTEXT_VALID_EAX, &MDRawContextX86::eax }, + { "$ebx", NULL, true, + StackFrameX86::CONTEXT_VALID_EBX, &MDRawContextX86::ebx }, + { "$ecx", NULL, false, + StackFrameX86::CONTEXT_VALID_ECX, &MDRawContextX86::ecx }, + { "$edx", NULL, false, + StackFrameX86::CONTEXT_VALID_EDX, &MDRawContextX86::edx }, + { "$esi", NULL, true, + StackFrameX86::CONTEXT_VALID_ESI, &MDRawContextX86::esi }, + { "$edi", NULL, true, + StackFrameX86::CONTEXT_VALID_EDI, &MDRawContextX86::edi }, +}; + StackwalkerX86::StackwalkerX86(const SystemInfo *system_info, const MDRawContextX86 *context, MemoryRegion *memory, @@ -55,7 +84,9 @@ SymbolSupplier *supplier, SourceLineResolverInterface *resolver) : Stackwalker(system_info, memory, modules, supplier, resolver), - context_(context) { + context_(context), + cfi_walker_(cfi_register_map_, + (sizeof(cfi_register_map_) / sizeof(cfi_register_map_[0]))) { if (memory_->GetBase() + memory_->GetSize() - 1 > 0xffffffff) { // The x86 is a 32-bit CPU, the limits of the supplied stack are invalid. // Mark memory_ = NULL, which will cause stackwalking to fail. @@ -66,8 +97,16 @@ } } +StackFrameX86::~StackFrameX86() { + if (windows_frame_info) + delete windows_frame_info; + windows_frame_info = NULL; + if (cfi_frame_info) + delete cfi_frame_info; + cfi_frame_info = NULL; +} -StackFrame* StackwalkerX86::GetContextFrame() { +StackFrame *StackwalkerX86::GetContextFrame() { if (!context_ || !memory_) { BPLOG(ERROR) << "Can't get context frame without context or memory"; return NULL; @@ -79,23 +118,28 @@ // straight out of the CPU context structure. frame->context = *context_; frame->context_validity = StackFrameX86::CONTEXT_VALID_ALL; + frame->trust = StackFrameX86::FRAME_TRUST_CONTEXT; frame->instruction = frame->context.eip; return frame; } - -StackFrame* StackwalkerX86::GetCallerFrame( - const CallStack *stack, - const vector< linked_ptr > &stack_frame_info) { - if (!memory_ || !stack) { - BPLOG(ERROR) << "Can't get caller frame without memory or stack"; +StackFrameX86 *StackwalkerX86::GetCallerByWindowsFrameInfo( + const vector &frames, + WindowsFrameInfo *last_frame_info) { + StackFrameX86::FrameTrust trust = StackFrameX86::FRAME_TRUST_NONE; + + StackFrameX86 *last_frame = static_cast(frames.back()); + + // Save the stack walking info we found, in case we need it later to + // find the callee of the frame we're constructing now. + last_frame->windows_frame_info = last_frame_info; + + // This function only covers the full STACK WIN case. If + // last_frame_info is VALID_PARAMETER_SIZE-only, then we should + // assume the traditional frame format or use some other strategy. + if (last_frame_info->valid != WindowsFrameInfo::VALID_ALL) return NULL; - } - - StackFrameX86 *last_frame = static_cast( - stack->frames()->back()); - StackFrameInfo *last_frame_info = stack_frame_info.back().get(); // This stackwalker sets each frame's %esp to its value immediately prior // to the CALL into the callee. This means that %esp points to the last @@ -129,13 +173,16 @@ // are unknown, 0 is also used in that case. When that happens, it should // be possible to walk to the next frame without reference to %esp. - int frames_already_walked = stack_frame_info.size(); u_int32_t last_frame_callee_parameter_size = 0; + int frames_already_walked = frames.size(); if (frames_already_walked >= 2) { - StackFrameInfo *last_frame_callee_info = - stack_frame_info[frames_already_walked - 2].get(); + const StackFrameX86 *last_frame_callee + = static_cast(frames[frames_already_walked - 2]); + WindowsFrameInfo *last_frame_callee_info + = last_frame_callee->windows_frame_info; if (last_frame_callee_info && - last_frame_callee_info->valid & StackFrameInfo::VALID_PARAMETER_SIZE) { + (last_frame_callee_info->valid + & WindowsFrameInfo::VALID_PARAMETER_SIZE)) { last_frame_callee_parameter_size = last_frame_callee_info->parameter_size; } @@ -143,146 +190,105 @@ // Set up the dictionary for the PostfixEvaluator. %ebp and %esp are used // in each program string, and their previous values are known, so set them - // here. .cbCalleeParams is a Breakpad extension that allows us to use - // the PostfixEvaluator engine when certain types of debugging information - // are present without having to write the constants into the program string - // as literals. + // here. PostfixEvaluator::DictionaryType dictionary; + // Provide the current register values. dictionary["$ebp"] = last_frame->context.ebp; dictionary["$esp"] = last_frame->context.esp; + // Provide constants from the debug info for last_frame and its callee. + // .cbCalleeParams is a Breakpad extension that allows us to use the + // PostfixEvaluator engine when certain types of debugging information + // are present without having to write the constants into the program + // string as literals. dictionary[".cbCalleeParams"] = last_frame_callee_parameter_size; + dictionary[".cbSavedRegs"] = last_frame_info->saved_register_size; + dictionary[".cbLocals"] = last_frame_info->local_size; + dictionary[".raSearchStart"] = last_frame->context.esp + + last_frame_callee_parameter_size + + last_frame_info->local_size + + last_frame_info->saved_register_size; + dictionary[".cbParams"] = last_frame_info->parameter_size; - if (last_frame_info && last_frame_info->valid == StackFrameInfo::VALID_ALL) { - // FPO debugging data is available. Initialize constants. - dictionary[".cbSavedRegs"] = last_frame_info->saved_register_size; - dictionary[".cbLocals"] = last_frame_info->local_size; - dictionary[".raSearchStart"] = last_frame->context.esp + - last_frame_callee_parameter_size + - last_frame_info->local_size + - last_frame_info->saved_register_size; - } - if (last_frame_info && - last_frame_info->valid & StackFrameInfo::VALID_PARAMETER_SIZE) { - // This is treated separately because it can either come from FPO data or - // from other debugging data. - dictionary[".cbParams"] = last_frame_info->parameter_size; - } - - // Decide what type of program string to use. The program string is in + // Decide what type of program string to use. The program string is in // postfix notation and will be passed to PostfixEvaluator::Evaluate. // Given the dictionary and the program string, it is possible to compute // the return address and the values of other registers in the calling - // function. When encountering a nontraditional frame (one which takes - // advantage of FPO), the stack may need to be scanned for these values. - // For traditional frames, simple deterministic dereferencing suffices - // without any need for scanning. The results of program string evaluation + // function. Because of bugs described below, the stack may need to be + // scanned for these values. The results of program string evaluation // will be used to determine whether to scan for better values. string program_string; - bool traditional_frame = true; bool recover_ebp = true; - if (last_frame_info && last_frame_info->valid == StackFrameInfo::VALID_ALL) { - // FPO data available. - traditional_frame = false; - if (!last_frame_info->program_string.empty()) { - // The FPO data has its own program string, which will tell us how to - // get to the caller frame, and may even fill in the values of - // nonvolatile registers and provide pointers to local variables and - // parameters. In some cases, particularly with program strings that use - // .raSearchStart, the stack may need to be scanned afterward. - program_string = last_frame_info->program_string; - } else if (last_frame_info->allocates_base_pointer) { - // The function corresponding to the last frame doesn't use the frame - // pointer for conventional purposes, but it does allocate a new - // frame pointer and use it for its own purposes. Its callee's - // information is still accessed relative to %esp, and the previous - // value of %ebp can be recovered from a location in its stack frame, - // within the saved-register area. - // - // Functions that fall into this category use the %ebp register for - // a purpose other than the frame pointer. They restore the caller's - // %ebp before returning. These functions create their stack frame - // after a CALL by decrementing the stack pointer in an amount - // sufficient to store local variables, and then PUSHing saved - // registers onto the stack. Arguments to a callee function, if any, - // are PUSHed after that. Walking up to the caller, therefore, - // can be done solely with calculations relative to the stack pointer - // (%esp). The return address is recovered from the memory location - // above the known sizes of the callee's parameters, saved registers, - // and locals. The caller's stack pointer (the value of %esp when - // the caller executed CALL) is the location immediately above the - // saved return address. The saved value of %ebp to be restored for - // the caller is at a known location in the saved-register area of - // the stack frame. - // - // For this type of frame, MSVC 14 (from Visual Studio 8/2005) in - // link-time code generation mode (/LTCG and /GL) can generate erroneous - // debugging data. The reported size of saved registers can be 0, - // which is clearly an error because these frames must, at the very - // least, save %ebp. For this reason, in addition to those given above - // about the use of .raSearchStart, the stack may need to be scanned - // for a better return address and a better frame pointer after the - // program string is evaluated. - // - // %eip_new = *(%esp_old + callee_params + saved_regs + locals) - // %ebp_new = *(%esp_old + callee_params + saved_regs - 8) - // %esp_new = %esp_old + callee_params + saved_regs + locals + 4 - program_string = "$eip .raSearchStart ^ = " - "$ebp $esp .cbCalleeParams + .cbSavedRegs + 8 - ^ = " - "$esp .raSearchStart 4 + ="; - } else { - // The function corresponding to the last frame doesn't use %ebp at - // all. The callee frame is located relative to %esp. - // - // The called procedure's instruction pointer and stack pointer are - // recovered in the same way as the case above, except that no - // frame pointer (%ebp) is used at all, so it is not saved anywhere - // in the callee's stack frame and does not need to be recovered. - // Because %ebp wasn't used in the callee, whatever value it has - // is the value that it had in the caller, so it can be carried - // straight through without bringing its validity into question. - // - // Because of the use of .raSearchStart, the stack will possibly be - // examined to locate a better return address after program string - // evaluation. The stack will not be examined to locate a saved - // %ebp value, because these frames do not save (or use) %ebp. - // - // %eip_new = *(%esp_old + callee_params + saved_regs + locals) - // %esp_new = %esp_old + callee_params + saved_regs + locals + 4 - // %ebp_new = %ebp_old - program_string = "$eip .raSearchStart ^ = " - "$esp .raSearchStart 4 + ="; - recover_ebp = false; - } + + trust = StackFrameX86::FRAME_TRUST_CFI; + if (!last_frame_info->program_string.empty()) { + // The FPO data has its own program string, which will tell us how to + // get to the caller frame, and may even fill in the values of + // nonvolatile registers and provide pointers to local variables and + // parameters. In some cases, particularly with program strings that use + // .raSearchStart, the stack may need to be scanned afterward. + program_string = last_frame_info->program_string; + } else if (last_frame_info->allocates_base_pointer) { + // The function corresponding to the last frame doesn't use the frame + // pointer for conventional purposes, but it does allocate a new + // frame pointer and use it for its own purposes. Its callee's + // information is still accessed relative to %esp, and the previous + // value of %ebp can be recovered from a location in its stack frame, + // within the saved-register area. + // + // Functions that fall into this category use the %ebp register for + // a purpose other than the frame pointer. They restore the caller's + // %ebp before returning. These functions create their stack frame + // after a CALL by decrementing the stack pointer in an amount + // sufficient to store local variables, and then PUSHing saved + // registers onto the stack. Arguments to a callee function, if any, + // are PUSHed after that. Walking up to the caller, therefore, + // can be done solely with calculations relative to the stack pointer + // (%esp). The return address is recovered from the memory location + // above the known sizes of the callee's parameters, saved registers, + // and locals. The caller's stack pointer (the value of %esp when + // the caller executed CALL) is the location immediately above the + // saved return address. The saved value of %ebp to be restored for + // the caller is at a known location in the saved-register area of + // the stack frame. + // + // For this type of frame, MSVC 14 (from Visual Studio 8/2005) in + // link-time code generation mode (/LTCG and /GL) can generate erroneous + // debugging data. The reported size of saved registers can be 0, + // which is clearly an error because these frames must, at the very + // least, save %ebp. For this reason, in addition to those given above + // about the use of .raSearchStart, the stack may need to be scanned + // for a better return address and a better frame pointer after the + // program string is evaluated. + // + // %eip_new = *(%esp_old + callee_params + saved_regs + locals) + // %ebp_new = *(%esp_old + callee_params + saved_regs - 8) + // %esp_new = %esp_old + callee_params + saved_regs + locals + 4 + program_string = "$eip .raSearchStart ^ = " + "$ebp $esp .cbCalleeParams + .cbSavedRegs + 8 - ^ = " + "$esp .raSearchStart 4 + ="; } else { - // No FPO information is available for the last frame. Assume that the - // standard %ebp-using x86 calling convention is in use. + // The function corresponding to the last frame doesn't use %ebp at + // all. The callee frame is located relative to %esp. // - // The typical x86 calling convention, when frame pointers are present, - // is for the calling procedure to use CALL, which pushes the return - // address onto the stack and sets the instruction pointer (%eip) to - // the entry point of the called routine. The called routine then - // PUSHes the calling routine's frame pointer (%ebp) onto the stack - // before copying the stack pointer (%esp) to the frame pointer (%ebp). - // Therefore, the calling procedure's frame pointer is always available - // by dereferencing the called procedure's frame pointer, and the return - // address is always available at the memory location immediately above - // the address pointed to by the called procedure's frame pointer. The - // calling procedure's stack pointer (%esp) is 8 higher than the value - // of the called procedure's frame pointer at the time the calling - // procedure made the CALL: 4 bytes for the return address pushed by the - // CALL itself, and 4 bytes for the callee's PUSH of the caller's frame - // pointer. + // The called procedure's instruction pointer and stack pointer are + // recovered in the same way as the case above, except that no + // frame pointer (%ebp) is used at all, so it is not saved anywhere + // in the callee's stack frame and does not need to be recovered. + // Because %ebp wasn't used in the callee, whatever value it has + // is the value that it had in the caller, so it can be carried + // straight through without bringing its validity into question. // - // Instruction and frame pointer recovery for these traditional frames is - // entirely deterministic, and the stack will not be scanned after - // recovering these values. + // Because of the use of .raSearchStart, the stack will possibly be + // examined to locate a better return address after program string + // evaluation. The stack will not be examined to locate a saved + // %ebp value, because these frames do not save (or use) %ebp. // - // %eip_new = *(%ebp_old + 4) - // %esp_new = %ebp_old + 8 - // %ebp_new = *(%ebp_old) - program_string = "$eip $ebp 4 + ^ = " - "$esp $ebp 8 + = " - "$ebp $ebp ^ ="; + // %eip_new = *(%esp_old + callee_params + saved_regs + locals) + // %esp_new = %esp_old + callee_params + saved_regs + locals + 4 + // %ebp_new = %ebp_old + program_string = "$eip .raSearchStart ^ = " + "$esp .raSearchStart 4 + ="; + recover_ebp = false; } // Now crank it out, making sure that the program string set at least the @@ -293,18 +299,36 @@ if (!evaluator.Evaluate(program_string, &dictionary_validity) || dictionary_validity.find("$eip") == dictionary_validity.end() || dictionary_validity.find("$esp") == dictionary_validity.end()) { - return NULL; + // Program string evaluation failed. It may be that %eip is not somewhere + // with stack frame info, and %ebp is pointing to non-stack memory, so + // our evaluation couldn't succeed. We'll scan the stack for a return + // address. This can happen if the stack is in a module for which + // we don't have symbols, and that module is compiled without a + // frame pointer. + u_int32_t location_start = last_frame->context.esp; + u_int32_t location, eip; + if (!ScanForReturnAddress(location_start, &location, &eip)) { + // if we can't find an instruction pointer even with stack scanning, + // give up. + return NULL; + } + + // This seems like a reasonable return address. Since program string + // evaluation failed, use it and set %esp to the location above the + // one where the return address was found. + dictionary["$eip"] = eip; + dictionary["$esp"] = location + 4; + trust = StackFrameX86::FRAME_TRUST_SCAN; } - // If this stack frame did not use %ebp in a traditional way, locating the - // return address isn't entirely deterministic. In that case, the stack - // can be scanned to locate the return address. + // Since this stack frame did not use %ebp in a traditional way, + // locating the return address isn't entirely deterministic. In that + // case, the stack can be scanned to locate the return address. // - // Even in nontraditional frames, if program string evaluation resulted in - // both %eip and %ebp values of 0, trust that the end of the stack has been + // However, if program string evaluation resulted in both %eip and + // %ebp values of 0, trust that the end of the stack has been // reached and don't scan for anything else. - if (!traditional_frame && - (dictionary["$eip"] != 0 || dictionary["$ebp"] != 0)) { + if (dictionary["$eip"] != 0 || dictionary["$ebp"] != 0) { int offset = 0; // This scan can only be done if a CodeModules object is available, to @@ -321,33 +345,18 @@ u_int32_t eip = dictionary["$eip"]; if (modules_ && !modules_->GetModuleForAddress(eip)) { - const int kRASearchWords = 15; - // The instruction pointer at .raSearchStart was invalid, so start // looking one 32-bit word above that location. u_int32_t location_start = dictionary[".raSearchStart"] + 4; - - for (u_int32_t location = location_start; - location <= location_start + kRASearchWords * 4; - location += 4) { - if (!memory_->GetMemoryAtAddress(location, &eip)) - break; - - if (modules_->GetModuleForAddress(eip)) { - // This is a better return address that what program string - // evaluation found. Use it, and set %esp to the location above the - // one where the return address was found. - // - // TODO(mmentovai): The return-address check can be made even - // stronger in modules for which debugging data is available. In - // that case, it's possible to check that the candidate return - // address is inside a known function. - - dictionary["$eip"] = eip; - dictionary["$esp"] = location + 4; - offset = location - location_start; - break; - } + u_int32_t location; + if (ScanForReturnAddress(location_start, &location, &eip)) { + // This is a better return address that what program string + // evaluation found. Use it, and set %esp to the location above the + // one where the return address was found. + dictionary["$eip"] = eip; + dictionary["$esp"] = location + 4; + offset = location - location_start; + trust = StackFrameX86::FRAME_TRUST_CFI_SCAN; } } @@ -381,17 +390,11 @@ } } - // Treat an instruction address of 0 as end-of-stack. Treat incorrect stack - // direction as end-of-stack to enforce progress and avoid infinite loops. - if (dictionary["$eip"] == 0 || - dictionary["$esp"] <= last_frame->context.esp) { - return NULL; - } - // Create a new stack frame (ownership will be transferred to the caller) // and fill it in. StackFrameX86 *frame = new StackFrameX86(); + frame->trust = trust; frame->context = last_frame->context; frame->context.eip = dictionary["$eip"]; frame->context.esp = dictionary["$esp"]; @@ -415,18 +418,182 @@ frame->context_validity |= StackFrameX86::CONTEXT_VALID_EDI; } - // frame->context.eip is the return address, which is one instruction - // past the CALL that caused us to arrive at the callee. Set - // frame->instruction to one less than that. This won't reference the - // beginning of the CALL instruction, but it's guaranteed to be within the - // CALL, which is sufficient to get the source line information to match up - // with the line that contains a function call. Callers that require the - // exact return address value may access the context.eip field of - // StackFrameX86. - frame->instruction = frame->context.eip - 1; + return frame; +} + +StackFrameX86 *StackwalkerX86::GetCallerByCFIFrameInfo( + const vector &frames, + CFIFrameInfo *cfi_frame_info) { + StackFrameX86 *last_frame = static_cast(frames.back()); + last_frame->cfi_frame_info = cfi_frame_info; + + scoped_ptr frame(new StackFrameX86()); + if (!cfi_walker_ + .FindCallerRegisters(*memory_, *cfi_frame_info, + last_frame->context, last_frame->context_validity, + &frame->context, &frame->context_validity)) + return NULL; + + // Make sure we recovered all the essentials. + static const int essentials = (StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP); + if ((frame->context_validity & essentials) != essentials) + return NULL; + + frame->trust = StackFrameX86::FRAME_TRUST_CFI; + + return frame.release(); +} + +StackFrameX86 *StackwalkerX86::GetCallerByEBPAtBase( + const vector &frames) { + StackFrameX86::FrameTrust trust; + StackFrameX86 *last_frame = static_cast(frames.back()); + u_int32_t last_esp = last_frame->context.esp; + u_int32_t last_ebp = last_frame->context.ebp; + + // Assume that the standard %ebp-using x86 calling convention is in + // use. + // + // The typical x86 calling convention, when frame pointers are present, + // is for the calling procedure to use CALL, which pushes the return + // address onto the stack and sets the instruction pointer (%eip) to + // the entry point of the called routine. The called routine then + // PUSHes the calling routine's frame pointer (%ebp) onto the stack + // before copying the stack pointer (%esp) to the frame pointer (%ebp). + // Therefore, the calling procedure's frame pointer is always available + // by dereferencing the called procedure's frame pointer, and the return + // address is always available at the memory location immediately above + // the address pointed to by the called procedure's frame pointer. The + // calling procedure's stack pointer (%esp) is 8 higher than the value + // of the called procedure's frame pointer at the time the calling + // procedure made the CALL: 4 bytes for the return address pushed by the + // CALL itself, and 4 bytes for the callee's PUSH of the caller's frame + // pointer. + // + // %eip_new = *(%ebp_old + 4) + // %esp_new = %ebp_old + 8 + // %ebp_new = *(%ebp_old) + + u_int32_t caller_eip, caller_esp, caller_ebp; + + if (memory_->GetMemoryAtAddress(last_ebp + 4, &caller_eip) && + memory_->GetMemoryAtAddress(last_ebp, &caller_ebp)) { + caller_esp = last_ebp + 8; + trust = StackFrameX86::FRAME_TRUST_FP; + } else { + // We couldn't read the memory %ebp refers to. It may be that %ebp + // is pointing to non-stack memory. We'll scan the stack for a + // return address. This can happen if last_frame is executing code + // for a module for which we don't have symbols, and that module + // is compiled without a frame pointer. + if (!ScanForReturnAddress(last_esp, &caller_esp, &caller_eip)) { + // if we can't find an instruction pointer even with stack scanning, + // give up. + return false; + } + + // ScanForReturnAddress found a reasonable return address. Advance + // %esp to the location above the one where the return address was + // found. Assume that %ebp is unchanged. + caller_esp += 4; + caller_ebp = last_ebp; + + trust = StackFrameX86::FRAME_TRUST_SCAN; + } + + // Create a new stack frame (ownership will be transferred to the caller) + // and fill it in. + StackFrameX86 *frame = new StackFrameX86(); + + frame->trust = trust; + frame->context = last_frame->context; + frame->context.eip = caller_eip; + frame->context.esp = caller_esp; + frame->context.ebp = caller_ebp; + frame->context_validity = StackFrameX86::CONTEXT_VALID_EIP | + StackFrameX86::CONTEXT_VALID_ESP | + StackFrameX86::CONTEXT_VALID_EBP; return frame; } +StackFrame *StackwalkerX86::GetCallerFrame(const CallStack *stack) { + if (!memory_ || !stack) { + BPLOG(ERROR) << "Can't get caller frame without memory or stack"; + return NULL; + } + + const vector &frames = *stack->frames(); + StackFrameX86 *last_frame = static_cast(frames.back()); + scoped_ptr new_frame; + + // If the resolver has Windows stack walking information, use that. + WindowsFrameInfo *windows_frame_info + = resolver_->FindWindowsFrameInfo(last_frame); + if (windows_frame_info) + new_frame.reset(GetCallerByWindowsFrameInfo(frames, windows_frame_info)); + + // If the resolver has DWARF CFI information, use that. + if (!new_frame.get()) { + CFIFrameInfo *cfi_frame_info = resolver_->FindCFIFrameInfo(last_frame); + if (cfi_frame_info) + new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info)); + } + + // Otherwise, hope that the program was using a traditional frame structure. + if (!new_frame.get()) + new_frame.reset(GetCallerByEBPAtBase(frames)); + + // If nothing worked, tell the caller. + if (!new_frame.get()) + return NULL; + + // Treat an instruction address of 0 as end-of-stack. + if (new_frame->context.eip == 0) + return NULL; + + // If the new stack pointer is at a lower address than the old, then + // that's clearly incorrect. Treat this as end-of-stack to enforce + // progress and avoid infinite loops. + if (new_frame->context.esp <= last_frame->context.esp) + return NULL; + + // new_frame->context.eip is the return address, which is one instruction + // past the CALL that caused us to arrive at the callee. Set + // new_frame->instruction to one less than that. This won't reference the + // beginning of the CALL instruction, but it's guaranteed to be within + // the CALL, which is sufficient to get the source line information to + // match up with the line that contains a function call. Callers that + // require the exact return address value may access the context.eip + // field of StackFrameX86. + new_frame->instruction = new_frame->context.eip - 1; + + return new_frame.release(); +} + +bool StackwalkerX86::ScanForReturnAddress(u_int32_t location_start, + u_int32_t *location_found, + u_int32_t *eip_found) { + const int kRASearchWords = 15; + for (u_int32_t location = location_start; + location <= location_start + kRASearchWords * 4; + location += 4) { + u_int32_t eip; + if (!memory_->GetMemoryAtAddress(location, &eip)) + break; + + if (modules_ && modules_->GetModuleForAddress(eip) && + InstructionAddressSeemsValid(eip)) { + + *eip_found = eip; + *location_found = location; + return true; + } + } + // nothing found + return false; +} } // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86.h 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,6 @@ -// Copyright (c) 2006, Google Inc. +// -*- mode: c++ -*- + +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -42,6 +44,8 @@ #include "google_breakpad/common/breakpad_types.h" #include "google_breakpad/common/minidump_format.h" #include "google_breakpad/processor/stackwalker.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "src/processor/cfi_frame_info.h" namespace google_breakpad { @@ -62,17 +66,58 @@ SourceLineResolverInterface *resolver); private: + // A STACK CFI-driven frame walker for the X86. + typedef SimpleCFIWalker CFIWalker; + // Implementation of Stackwalker, using x86 context (%ebp, %esp, %eip) and // stack conventions (saved %ebp at [%ebp], saved %eip at 4[%ebp], or - // alternate conventions as guided by stack_frame_info_). - virtual StackFrame* GetContextFrame(); - virtual StackFrame* GetCallerFrame( - const CallStack *stack, - const vector< linked_ptr > &stack_frame_info); + // alternate conventions as guided by any WindowsFrameInfo available for the + // code in question.). + virtual StackFrame *GetContextFrame(); + virtual StackFrame *GetCallerFrame(const CallStack *stack); + + // Use windows_frame_info (derived from STACK WIN and FUNC records) + // to construct the frame that called frames.back(). The caller + // takes ownership of the returned frame. Return NULL on failure. + StackFrameX86 *GetCallerByWindowsFrameInfo( + const vector &frames, + WindowsFrameInfo *windows_frame_info); + + // Use cfi_frame_info (derived from STACK CFI records) to construct + // the frame that called frames.back(). The caller takes ownership + // of the returned frame. Return NULL on failure. + StackFrameX86 *GetCallerByCFIFrameInfo(const vector &frames, + CFIFrameInfo *cfi_frame_info); + + // Assuming a traditional frame layout --- where the caller's %ebp + // has been pushed just after the return address and the callee's + // %ebp points to the saved %ebp --- construct the frame that called + // frames.back(). The caller takes ownership of the returned frame. + // Return NULL on failure. + StackFrameX86 *GetCallerByEBPAtBase(const vector &frames); + + // Scan the stack starting at location_start, looking for an address + // that looks like a valid instruction pointer. Addresses must + // 1) be contained in the current stack memory + // 2) pass the checks in Stackwalker::InstructionAddressSeemsValid + // + // Returns true if a valid-looking instruction pointer was found. + // When returning true, sets location_found to the address at which + // the value was found, and eip_found to the value contained at that + // location in memory. + bool ScanForReturnAddress(u_int32_t location_start, + u_int32_t *location_found, + u_int32_t *eip_found); // Stores the CPU context corresponding to the innermost stack frame to // be returned by GetContextFrame. const MDRawContextX86 *context_; + + // Our register map, for cfi_walker_. + static const CFIWalker::RegisterSet cfi_register_map_[]; + + // Our CFI frame walker. + const CFIWalker cfi_walker_; }; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/stackwalker_x86_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,893 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// stackwalker_x86_unittest.cc: Unit tests for StackwalkerX86 class. + +#include +#include + +#include "breakpad_googletest_includes.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" +#include "google_breakpad/processor/stack_frame_cpu.h" +#include "processor/stackwalker_unittest_utils.h" +#include "processor/stackwalker_x86.h" +#include "processor/test_assembler.h" +#include "processor/windows_frame_info.h" + +using google_breakpad::BasicSourceLineResolver; +using google_breakpad::CallStack; +using google_breakpad::StackFrame; +using google_breakpad::StackFrameX86; +using google_breakpad::StackwalkerX86; +using google_breakpad::SystemInfo; +using google_breakpad::WindowsFrameInfo; +using google_breakpad::TestAssembler::kLittleEndian; +using google_breakpad::TestAssembler::Label; +using google_breakpad::TestAssembler::Section; +using std::string; +using std::vector; +using testing::_; +using testing::Return; +using testing::SetArgumentPointee; +using testing::Test; + +class StackwalkerX86Fixture { + public: + StackwalkerX86Fixture() + : stack_section(kLittleEndian), + // Give the two modules reasonable standard locations and names + // for tests to play with. + module1(0x40000000, 0x10000, "module1", "version1"), + module2(0x50000000, 0x10000, "module2", "version2") { + // Identify the system as a Linux system. + system_info.os = "Linux"; + system_info.os_short = "linux"; + system_info.os_version = "Salacious Skink"; + system_info.cpu = "x86"; + system_info.cpu_info = ""; + + // Put distinctive values in the raw CPU context. + BrandContext(&raw_context); + + // Create some modules with some stock debugging information. + modules.Add(&module1); + modules.Add(&module2); + + // By default, none of the modules have symbol info; call + // SetModuleSymbols to override this. + EXPECT_CALL(supplier, GetSymbolFile(_, _, _, _)) + .WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND)); + } + + // Set the Breakpad symbol information that supplier should return for + // MODULE to INFO. + void SetModuleSymbols(MockCodeModule *module, const string &info) { + EXPECT_CALL(supplier, GetSymbolFile(module, &system_info, _, _)) + .WillRepeatedly(DoAll(SetArgumentPointee<3>(info), + Return(MockSymbolSupplier::FOUND))); + } + + // Populate stack_region with the contents of stack_section. Use + // stack_section.start() as the region's starting address. + void RegionFromSection() { + string contents; + ASSERT_TRUE(stack_section.GetContents(&contents)); + stack_region.Init(stack_section.start().Value(), contents); + } + + // Fill RAW_CONTEXT with pseudo-random data, for round-trip checking. + void BrandContext(MDRawContextX86 *raw_context) { + u_int8_t x = 173; + for (size_t i = 0; i < sizeof(*raw_context); i++) + reinterpret_cast(raw_context)[i] = (x += 17); + } + + SystemInfo system_info; + MDRawContextX86 raw_context; + Section stack_section; + MockMemoryRegion stack_region; + MockCodeModule module1; + MockCodeModule module2; + MockCodeModules modules; + MockSymbolSupplier supplier; + BasicSourceLineResolver resolver; + CallStack call_stack; + const vector *frames; +}; + +class GetContextFrame: public StackwalkerX86Fixture, public Test { }; + +TEST_F(GetContextFrame, Simple) { + stack_section.start() = 0x80000000; + stack_section.D32(0).D32(0); // end-of-stack marker + RegionFromSection(); + raw_context.eip = 0x40000200; + raw_context.ebp = 0x80000000; + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + StackFrameX86 *frame = static_cast(frames->at(0)); + // Check that the values from the original raw context made it + // through to the context in the stack frame. + EXPECT_TRUE(memcmp(&raw_context, &frame->context, sizeof(raw_context)) == 0); +} + +class GetCallerFrame: public StackwalkerX86Fixture, public Test { }; + +// Walk a traditional frame. A traditional frame saves the caller's +// %ebp just below the return address, and has its own %ebp pointing +// at the saved %ebp. +TEST_F(GetCallerFrame, Traditional) { + stack_section.start() = 0x80000000; + Label frame0_ebp, frame1_ebp; + stack_section + .Append(12, 0) // frame 0: space + .Mark(&frame0_ebp) // frame 0 %ebp points here + .D32(frame1_ebp) // frame 0: saved %ebp + .D32(0x40008679) // frame 0: return address + .Append(8, 0) // frame 1: space + .Mark(&frame1_ebp) // frame 1 %ebp points here + .D32(0) // frame 1: saved %ebp (stack end) + .D32(0); // frame 1: return address (stack end) + RegionFromSection(); + raw_context.eip = 0x4000c7a5; + raw_context.esp = stack_section.start().Value(); + raw_context.ebp = frame0_ebp.Value(); + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_CONTEXT, frame0->trust); + EXPECT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x4000c7a5U, frame0->instruction); + EXPECT_EQ(0x4000c7a5U, frame0->context.eip); + EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp); + EXPECT_EQ(NULL, frame0->windows_frame_info); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_FP, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x40008679U, frame1->instruction + 1); + EXPECT_EQ(0x40008679U, frame1->context.eip); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_EQ(NULL, frame1->windows_frame_info); +} + +// Walk a traditional frame, but use a bogus %ebp value, forcing a scan +// of the stack for something that looks like a return address. +TEST_F(GetCallerFrame, TraditionalScan) { + stack_section.start() = 0x80000000; + Label frame1_ebp; + stack_section + // frame 0 + .D32(0xf065dc76) // locals area: + .D32(0x46ee2167) // garbage that doesn't look like + .D32(0xbab023ec) // a return address + .D32(frame1_ebp) // saved %ebp (%ebp fails to point here, forcing scan) + .D32(0x4000129d) // return address + // frame 1 + .Append(8, 0) // space + .Mark(&frame1_ebp) // %ebp points here + .D32(0) // saved %ebp (stack end) + .D32(0); // return address (stack end) + + RegionFromSection(); + raw_context.eip = 0x4000f49d; + raw_context.esp = stack_section.start().Value(); + // Make the frame pointer bogus, to make the stackwalker scan the stack + // for something that looks like a return address. + raw_context.ebp = 0xd43eed6e; + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x4000f49dU, frame0->instruction); + EXPECT_EQ(0x4000f49dU, frame0->context.eip); + EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); + EXPECT_EQ(0xd43eed6eU, frame0->context.ebp); + EXPECT_EQ(NULL, frame0->windows_frame_info); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_SCAN, frame1->trust); + // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the + // walker does not actually fetch the EBP after a scan (forcing the + // next frame to be scanned as well). But let's grandfather the existing + // behavior in for now. + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x4000129dU, frame1->instruction + 1); + EXPECT_EQ(0x4000129dU, frame1->context.eip); + EXPECT_EQ(0x80000014U, frame1->context.esp); + EXPECT_EQ(0xd43eed6eU, frame1->context.ebp); + EXPECT_EQ(NULL, frame1->windows_frame_info); +} + +// Use Windows frame data (a "STACK WIN 4" record, from a +// FrameTypeFrameData DIA record) to walk a stack frame. +TEST_F(GetCallerFrame, WindowsFrameData) { + SetModuleSymbols(&module1, + "STACK WIN 4 aa85 176 0 0 4 10 4 0 1" + " $T2 $esp .cbSavedRegs + =" + " $T0 .raSearchStart =" + " $eip $T0 ^ =" + " $esp $T0 4 + =" + " $ebx $T2 4 - ^ =" + " $edi $T2 8 - ^ =" + " $esi $T2 12 - ^ =" + " $ebp $T2 16 - ^ =\n"); + Label frame1_esp, frame1_ebp; + stack_section.start() = 0x80000000; + stack_section + // frame 0 + .D32(frame1_ebp) // saved regs: %ebp + .D32(0xa7120d1a) // %esi + .D32(0x630891be) // %edi + .D32(0x9068a878) // %ebx + .D32(0xa08ea45f) // locals: unused + .D32(0x40001350) // return address + // frame 1 + .Mark(&frame1_esp) + .Append(12, 0) // empty space + .Mark(&frame1_ebp) + .D32(0) // saved %ebp (stack end) + .D32(0); // saved %eip (stack end) + + RegionFromSection(); + raw_context.eip = 0x4000aa85; + raw_context.esp = stack_section.start().Value(); + raw_context.ebp = 0xf052c1de; // should not be needed to walk frame + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x4000aa85U, frame0->instruction); + EXPECT_EQ(0x4000aa85U, frame0->context.eip); + EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); + EXPECT_EQ(0xf052c1deU, frame0->context.ebp); + EXPECT_TRUE(frame0->windows_frame_info != NULL); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP + | StackFrameX86::CONTEXT_VALID_EBX + | StackFrameX86::CONTEXT_VALID_ESI + | StackFrameX86::CONTEXT_VALID_EDI), + frame1->context_validity); + EXPECT_EQ(0x40001350U, frame1->instruction + 1); + EXPECT_EQ(0x40001350U, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_EQ(0x9068a878U, frame1->context.ebx); + EXPECT_EQ(0xa7120d1aU, frame1->context.esi); + EXPECT_EQ(0x630891beU, frame1->context.edi); + EXPECT_EQ(NULL, frame1->windows_frame_info); +} + +// Use Windows frame data (a "STACK WIN 4" record, from a +// FrameTypeFrameData DIA record) to walk a frame, and depend on the +// parameter size from the callee as well. +TEST_F(GetCallerFrame, WindowsFrameDataParameterSize) { + SetModuleSymbols(&module1, "FUNC 1000 100 c module1::wheedle\n"); + SetModuleSymbols(&module2, + // Note bogus parameter size in FUNC record; the stack walker + // should prefer the STACK WIN record, and see '4' below. + "FUNC aa85 176 beef module2::whine\n" + "STACK WIN 4 aa85 176 0 0 4 10 4 0 1" + " $T2 $esp .cbLocals + .cbSavedRegs + =" + " $T0 .raSearchStart =" + " $eip $T0 ^ =" + " $esp $T0 4 + =" + " $ebp $T0 20 - ^ =" + " $ebx $T0 8 - ^ =\n"); + Label frame0_esp, frame0_ebp; + Label frame1_esp; + Label frame2_esp, frame2_ebp; + stack_section.start() = 0x80000000; + stack_section + // frame 0, in module1::wheedle. Traditional frame. + .Mark(&frame0_esp) + .Append(16, 0) // frame space + .Mark(&frame0_ebp) + .D32(0x6fa902e0) // saved %ebp. Not a frame pointer. + .D32(0x5000aa95) // return address, in module2::whine + // frame 1, in module2::whine. FrameData frame. + .Mark(&frame1_esp) + .D32(0xbaa0cb7a) // argument 3 passed to module1::wheedle + .D32(0xbdc92f9f) // argument 2 + .D32(0x0b1d8442) // argument 1 + .D32(frame2_ebp) // saved %ebp + .D32(0xb1b90a15) // unused + .D32(0xf18e072d) // unused + .D32(0x2558c7f3) // saved %ebx + .D32(0x0365e25e) // unused + .D32(0x2a179e38) // return address; $T0 points here + // frame 2, in no module + .Mark(&frame2_esp) + .Append(12, 0) // empty space + .Mark(&frame2_ebp) + .D32(0) // saved %ebp (stack end) + .D32(0); // saved %eip (stack end) + + RegionFromSection(); + raw_context.eip = 0x40001004; // in module1::wheedle + raw_context.esp = stack_section.start().Value(); + raw_context.ebp = frame0_ebp.Value(); + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(3U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x40001004U, frame0->instruction); + EXPECT_EQ(0x40001004U, frame0->context.eip); + EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); + EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp); + EXPECT_EQ(&module1, frame0->module); + EXPECT_EQ("module1::wheedle", frame0->function_name); + EXPECT_EQ(0x40001000U, frame0->function_base); + // The FUNC record for module1::wheedle should have produced a + // WindowsFrameInfo structure with only the parameter size valid. + ASSERT_TRUE(frame0->windows_frame_info != NULL); + EXPECT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE, + frame0->windows_frame_info->valid); + EXPECT_EQ(12U, frame0->windows_frame_info->parameter_size); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_FP, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x5000aa95U, frame1->instruction + 1); + EXPECT_EQ(0x5000aa95U, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(0x6fa902e0U, frame1->context.ebp); + EXPECT_EQ(&module2, frame1->module); + EXPECT_EQ("module2::whine", frame1->function_name); + EXPECT_EQ(0x5000aa85U, frame1->function_base); + ASSERT_TRUE(frame1->windows_frame_info != NULL); + EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame1->windows_frame_info->valid); + // This should not see the 0xbeef parameter size from the FUNC + // record, but should instead see the STACK WIN record. + EXPECT_EQ(4U, frame1->windows_frame_info->parameter_size); + + StackFrameX86 *frame2 = static_cast(frames->at(2)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_CFI, frame2->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP + | StackFrameX86::CONTEXT_VALID_EBX), + frame2->context_validity); + EXPECT_EQ(0x2a179e38U, frame2->instruction + 1); + EXPECT_EQ(0x2a179e38U, frame2->context.eip); + EXPECT_EQ(frame2_esp.Value(), frame2->context.esp); + EXPECT_EQ(frame2_ebp.Value(), frame2->context.ebp); + EXPECT_EQ(0x2558c7f3U, frame2->context.ebx); + EXPECT_EQ(NULL, frame2->module); + EXPECT_EQ(NULL, frame2->windows_frame_info); +} + +// Use Windows frame data (a "STACK WIN 4" record, from a +// FrameTypeFrameData DIA record) to walk a stack frame, where the +// expression fails to yield both an $eip and an $ebp value, and the stack +// walker must scan. +TEST_F(GetCallerFrame, WindowsFrameDataScan) { + SetModuleSymbols(&module1, + "STACK WIN 4 c8c 111 0 0 4 10 4 0 1 bad program string\n"); + // Mark frame 1's PC as the end of the stack. + SetModuleSymbols(&module2, + "FUNC 7c38 accf 0 module2::function\n" + "STACK WIN 4 7c38 accf 0 0 4 10 4 0 1 $eip 0 = $ebp 0 =\n"); + Label frame1_esp; + stack_section.start() = 0x80000000; + stack_section + // frame 0 + .Append(16, 0x2a) // unused, garbage + .D32(0x50007ce9) // return address + // frame 1 + .Mark(&frame1_esp) + .Append(8, 0); // empty space + + RegionFromSection(); + raw_context.eip = 0x40000c9c; + raw_context.esp = stack_section.start().Value(); + raw_context.ebp = 0x2ae314cd; // should not be needed to walk frame + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x40000c9cU, frame0->instruction); + EXPECT_EQ(0x40000c9cU, frame0->context.eip); + EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); + EXPECT_EQ(0x2ae314cdU, frame0->context.ebp); + EXPECT_TRUE(frame0->windows_frame_info != NULL); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_SCAN, frame1->trust); + // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the walker + // does not actually fetch the EBP after a scan (forcing the next frame + // to be scanned as well). But let's grandfather the existing behavior in + // for now. + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x50007ce9U, frame1->instruction + 1); + EXPECT_EQ(0x50007ce9U, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_TRUE(frame1->windows_frame_info != NULL); +} + +// Use Windows frame data (a "STACK WIN 4" record, from a +// FrameTypeFrameData DIA record) to walk a stack frame, where the +// expression yields an $eip that falls outside of any module, and the +// stack walker must scan. +TEST_F(GetCallerFrame, WindowsFrameDataBadEIPScan) { + SetModuleSymbols(&module1, + "STACK WIN 4 6e6 e7 0 0 0 8 4 0 1" + // A traditional frame, actually. + " $eip $ebp 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =\n"); + // Mark frame 1's PC as the end of the stack. + SetModuleSymbols(&module2, + "FUNC cfdb 8406 0 module2::function\n" + "STACK WIN 4 cfdb 8406 0 0 0 0 0 0 1 $eip 0 = $ebp 0 =\n"); + stack_section.start() = 0x80000000; + + // In this stack, the context's %ebp is pointing at the wrong place, so + // the stack walker needs to scan to find the return address, and then + // scan again to find the caller's saved %ebp. + Label frame0_ebp, frame1_ebp, frame1_esp; + stack_section + // frame 0 + .Append(8, 0x2a) // garbage + .Mark(&frame0_ebp) // frame 0 %ebp points here, but should point + // at *** below + // The STACK WIN record says that the following two values are + // frame 1's saved %ebp and return address, but the %ebp is wrong; + // they're garbage. The stack walker will scan for the right values. + .D32(0x3d937b2b) // alleged to be frame 1's saved %ebp + .D32(0x17847f5b) // alleged to be frame 1's return address + .D32(frame1_ebp) // frame 1's real saved %ebp; scan will find + .D32(0x2b2b2b2b) // first word of realigned register save area + // *** frame 0 %ebp ought to be pointing here + .D32(0x2c2c2c2c) // realigned locals area + .D32(0x5000d000) // frame 1's real saved %eip; scan will find + // Frame 1, in module2::function. The STACK WIN record describes + // this as the oldest frame, without referring to its contents, so + // we needn't to provide any actual data here. + .Mark(&frame1_esp) + .Mark(&frame1_ebp) // frame 1 %ebp points here + // A dummy value for frame 1's %ebp to point at. The scan recognizes the + // saved %ebp because it points to a valid word in the stack memory region. + .D32(0x2d2d2d2d); + + RegionFromSection(); + raw_context.eip = 0x40000700; + raw_context.esp = stack_section.start().Value(); + raw_context.ebp = frame0_ebp.Value(); + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x40000700U, frame0->instruction); + EXPECT_EQ(0x40000700U, frame0->context.eip); + EXPECT_EQ(stack_section.start().Value(), frame0->context.esp); + EXPECT_EQ(frame0_ebp.Value(), frame0->context.ebp); + EXPECT_TRUE(frame0->windows_frame_info != NULL); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_CFI_SCAN, frame1->trust); + // I'd argue that CONTEXT_VALID_EBP shouldn't be here, since the + // walker does not actually fetch the EBP after a scan (forcing the + // next frame to be scanned as well). But let's grandfather the existing + // behavior in for now. + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x5000d000U, frame1->instruction + 1); + EXPECT_EQ(0x5000d000U, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_TRUE(frame1->windows_frame_info != NULL); +} + +// Use Windows FrameTypeFPO data to walk a stack frame for a function that +// does not modify %ebp from the value it had in the caller. +TEST_F(GetCallerFrame, WindowsFPOUnchangedEBP) { + SetModuleSymbols(&module1, + // Note bogus parameter size in FUNC record; the walker + // should prefer the STACK WIN record, and see the '8' below. + "FUNC e8a8 100 feeb module1::discombobulated\n" + "STACK WIN 0 e8a8 100 0 0 8 4 10 0 0 0\n"); + Label frame0_esp; + Label frame1_esp, frame1_ebp; + stack_section.start() = 0x80000000; + stack_section + // frame 0, in module1::wheedle. FrameTypeFPO (STACK WIN 0) frame. + .Mark(&frame0_esp) + // no outgoing parameters; this is the youngest frame. + .D32(0x7c521352) // four bytes of saved registers + .Append(0x10, 0x42) // local area + .D32(0x40009b5b) // return address, in module1, no function + // frame 1, in module1, no function. + .Mark(&frame1_esp) + .D32(0xf60ea7fc) // junk + .Mark(&frame1_ebp) + .D32(0) // saved %ebp (stack end) + .D32(0); // saved %eip (stack end) + + RegionFromSection(); + raw_context.eip = 0x4000e8b8; // in module1::whine + raw_context.esp = stack_section.start().Value(); + // Frame pointer unchanged from caller. + raw_context.ebp = frame1_ebp.Value(); + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x4000e8b8U, frame0->instruction); + EXPECT_EQ(0x4000e8b8U, frame0->context.eip); + EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame0->context.ebp); // unchanged from caller + EXPECT_EQ(&module1, frame0->module); + EXPECT_EQ("module1::discombobulated", frame0->function_name); + EXPECT_EQ(0x4000e8a8U, frame0->function_base); + // The STACK WIN record for module1::discombobulated should have + // produced a fully populated WindowsFrameInfo structure. + ASSERT_TRUE(frame0->windows_frame_info != NULL); + EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid); + EXPECT_EQ(0x10U, frame0->windows_frame_info->local_size); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x40009b5bU, frame1->instruction + 1); + EXPECT_EQ(0x40009b5bU, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_EQ(&module1, frame1->module); + EXPECT_EQ("", frame1->function_name); + EXPECT_EQ(NULL, frame1->windows_frame_info); +} + +// Use Windows FrameTypeFPO data to walk a stack frame for a function +// that uses %ebp for its own purposes, saving the value it had in the +// caller in the standard place in the saved register area. +TEST_F(GetCallerFrame, WindowsFPOUsedEBP) { + SetModuleSymbols(&module1, + // Note bogus parameter size in FUNC record; the walker + // should prefer the STACK WIN record, and see the '8' below. + "FUNC 9aa8 e6 abbe module1::RaisedByTheAliens\n" + "STACK WIN 0 9aa8 e6 a 0 10 8 4 0 0 1\n"); + Label frame0_esp; + Label frame1_esp, frame1_ebp; + stack_section.start() = 0x80000000; + stack_section + // frame 0, in module1::wheedle. FrameTypeFPO (STACK WIN 0) frame. + .Mark(&frame0_esp) + // no outgoing parameters; this is the youngest frame. + .D32(frame1_ebp) // saved register area: saved %ebp + .D32(0xb68bd5f9) // saved register area: something else + .D32(0xd25d05fc) // local area + .D32(0x4000debe) // return address, in module1, no function + // frame 1, in module1, no function. + .Mark(&frame1_esp) + .D32(0xf0c9a974) // junk + .Mark(&frame1_ebp) + .D32(0) // saved %ebp (stack end) + .D32(0); // saved %eip (stack end) + + RegionFromSection(); + raw_context.eip = 0x40009ab8; // in module1::RaisedByTheAliens + raw_context.esp = stack_section.start().Value(); + // RaisedByTheAliens uses %ebp for its own mysterious purposes. + raw_context.ebp = 0xecbdd1a5; + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ(0x40009ab8U, frame0->instruction); + EXPECT_EQ(0x40009ab8U, frame0->context.eip); + EXPECT_EQ(frame0_esp.Value(), frame0->context.esp); + EXPECT_EQ(0xecbdd1a5, frame0->context.ebp); + EXPECT_EQ(&module1, frame0->module); + EXPECT_EQ("module1::RaisedByTheAliens", frame0->function_name); + EXPECT_EQ(0x40009aa8U, frame0->function_base); + // The STACK WIN record for module1::RaisedByTheAliens should have + // produced a fully populated WindowsFrameInfo structure. + ASSERT_TRUE(frame0->windows_frame_info != NULL); + EXPECT_EQ(WindowsFrameInfo::VALID_ALL, frame0->windows_frame_info->valid); + EXPECT_EQ("", frame0->windows_frame_info->program_string); + EXPECT_TRUE(frame0->windows_frame_info->allocates_base_pointer); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP + | StackFrameX86::CONTEXT_VALID_ESP + | StackFrameX86::CONTEXT_VALID_EBP), + frame1->context_validity); + EXPECT_EQ(0x4000debeU, frame1->instruction + 1); + EXPECT_EQ(0x4000debeU, frame1->context.eip); + EXPECT_EQ(frame1_esp.Value(), frame1->context.esp); + EXPECT_EQ(frame1_ebp.Value(), frame1->context.ebp); + EXPECT_EQ(&module1, frame1->module); + EXPECT_EQ("", frame1->function_name); + EXPECT_EQ(NULL, frame1->windows_frame_info); +} + +struct CFIFixture: public StackwalkerX86Fixture { + CFIFixture() { + // Provide a bunch of STACK CFI records; individual tests walk to the + // caller from every point in this series, expecting to find the same + // set of register values. + SetModuleSymbols(&module1, + // The youngest frame's function. + "FUNC 4000 1000 10 enchiridion\n" + // Initially, just a return address. + "STACK CFI INIT 4000 100 .cfa: $esp 4 + .ra: .cfa 4 - ^\n" + // Push %ebx. + "STACK CFI 4001 .cfa: $esp 8 + $ebx: .cfa 8 - ^\n" + // Move %esi into %ebx. Weird, but permitted. + "STACK CFI 4002 $esi: $ebx\n" + // Allocate frame space, and save %edi. + "STACK CFI 4003 .cfa: $esp 20 + $edi: .cfa 16 - ^\n" + // Put the return address in %edi. + "STACK CFI 4005 .ra: $edi\n" + // Save %ebp, and use it as a frame pointer. + "STACK CFI 4006 .cfa: $ebp 8 + $ebp: .cfa 12 - ^\n" + + // The calling function. + "FUNC 5000 1000 10 epictetus\n" + // Mark it as end of stack. + "STACK CFI INIT 5000 1000 .cfa: $esp .ra 0\n"); + + // Provide some distinctive values for the caller's registers. + expected.esp = 0x80000000; + expected.eip = 0x40005510; + expected.ebp = 0xc0d4aab9; + expected.ebx = 0x60f20ce6; + expected.esi = 0x53d1379d; + expected.edi = 0xafbae234; + + // By default, registers are unchanged. + raw_context = expected; + } + + // Walk the stack, using stack_section as the contents of the stack + // and raw_context as the current register values. (Set + // raw_context.esp to the stack's starting address.) Expect two + // stack frames; in the older frame, expect the callee-saves + // registers to have values matching those in 'expected'. + void CheckWalk() { + RegionFromSection(); + raw_context.esp = stack_section.start().Value(); + + StackwalkerX86 walker(&system_info, &raw_context, &stack_region, &modules, + &supplier, &resolver); + ASSERT_TRUE(walker.Walk(&call_stack)); + frames = call_stack.frames(); + ASSERT_EQ(2U, frames->size()); + + StackFrameX86 *frame0 = static_cast(frames->at(0)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_CONTEXT, frame0->trust); + ASSERT_EQ(StackFrameX86::CONTEXT_VALID_ALL, frame0->context_validity); + EXPECT_EQ("enchiridion", frame0->function_name); + EXPECT_EQ(0x40004000U, frame0->function_base); + ASSERT_TRUE(frame0->windows_frame_info != NULL); + ASSERT_EQ(WindowsFrameInfo::VALID_PARAMETER_SIZE, + frame0->windows_frame_info->valid); + ASSERT_TRUE(frame0->cfi_frame_info != NULL); + + StackFrameX86 *frame1 = static_cast(frames->at(1)); + EXPECT_EQ(StackFrameX86::FRAME_TRUST_CFI, frame1->trust); + ASSERT_EQ((StackFrameX86::CONTEXT_VALID_EIP | + StackFrameX86::CONTEXT_VALID_ESP | + StackFrameX86::CONTEXT_VALID_EBP | + StackFrameX86::CONTEXT_VALID_EBX | + StackFrameX86::CONTEXT_VALID_ESI | + StackFrameX86::CONTEXT_VALID_EDI), + frame1->context_validity); + EXPECT_EQ(expected.eip, frame1->context.eip); + EXPECT_EQ(expected.esp, frame1->context.esp); + EXPECT_EQ(expected.ebp, frame1->context.ebp); + EXPECT_EQ(expected.ebx, frame1->context.ebx); + EXPECT_EQ(expected.esi, frame1->context.esi); + EXPECT_EQ(expected.edi, frame1->context.edi); + EXPECT_EQ("epictetus", frame1->function_name); + } + + // The values the stack walker should find for the caller's registers. + MDRawContextX86 expected; +}; + +class CFI: public CFIFixture, public Test { }; + +TEST_F(CFI, At4000) { + Label frame1_esp = expected.esp; + stack_section + .D32(0x40005510) // return address + .Mark(&frame1_esp); // This effectively sets stack_section.start(). + raw_context.eip = 0x40004000; + CheckWalk(); +} + +TEST_F(CFI, At4001) { + Label frame1_esp = expected.esp; + stack_section + .D32(0x60f20ce6) // saved %ebx + .D32(0x40005510) // return address + .Mark(&frame1_esp); // This effectively sets stack_section.start(). + raw_context.eip = 0x40004001; + raw_context.ebx = 0x91aa9a8b; // callee's %ebx value + CheckWalk(); +} + +TEST_F(CFI, At4002) { + Label frame1_esp = expected.esp; + stack_section + .D32(0x60f20ce6) // saved %ebx + .D32(0x40005510) // return address + .Mark(&frame1_esp); // This effectively sets stack_section.start(). + raw_context.eip = 0x40004002; + raw_context.ebx = 0x53d1379d; // saved %esi + raw_context.esi = 0xa5c790ed; // callee's %esi value + CheckWalk(); +} + +TEST_F(CFI, At4003) { + Label frame1_esp = expected.esp; + stack_section + .D32(0x56ec3db7) // garbage + .D32(0xafbae234) // saved %edi + .D32(0x53d67131) // garbage + .D32(0x60f20ce6) // saved %ebx + .D32(0x40005510) // return address + .Mark(&frame1_esp); // This effectively sets stack_section.start(). + raw_context.eip = 0x40004003; + raw_context.ebx = 0x53d1379d; // saved %esi + raw_context.esi = 0xa97f229d; // callee's %esi + raw_context.edi = 0xb05cc997; // callee's %edi + CheckWalk(); +} + +// The results here should be the same as those at module offset +// 0x4003. +TEST_F(CFI, At4004) { + Label frame1_esp = expected.esp; + stack_section + .D32(0xe29782c2) // garbage + .D32(0xafbae234) // saved %edi + .D32(0x5ba29ce9) // garbage + .D32(0x60f20ce6) // saved %ebx + .D32(0x40005510) // return address + .Mark(&frame1_esp); // This effectively sets stack_section.start(). + raw_context.eip = 0x40004004; + raw_context.ebx = 0x53d1379d; // saved %esi + raw_context.esi = 0x0fb7dc4e; // callee's %esi + raw_context.edi = 0x993b4280; // callee's %edi + CheckWalk(); +} + +TEST_F(CFI, At4005) { + Label frame1_esp = expected.esp; + stack_section + .D32(0xe29782c2) // garbage + .D32(0xafbae234) // saved %edi + .D32(0x5ba29ce9) // garbage + .D32(0x60f20ce6) // saved %ebx + .D32(0x8036cc02) // garbage + .Mark(&frame1_esp); // This effectively sets stack_section.start(). + raw_context.eip = 0x40004005; + raw_context.ebx = 0x53d1379d; // saved %esi + raw_context.esi = 0x0fb7dc4e; // callee's %esi + raw_context.edi = 0x40005510; // return address + CheckWalk(); +} + +TEST_F(CFI, At4006) { + Label frame0_ebp; + Label frame1_esp = expected.esp; + stack_section + .D32(0xdcdd25cd) // garbage + .D32(0xafbae234) // saved %edi + .D32(0xc0d4aab9) // saved %ebp + .Mark(&frame0_ebp) // frame pointer points here + .D32(0x60f20ce6) // saved %ebx + .D32(0x8036cc02) // garbage + .Mark(&frame1_esp); // This effectively sets stack_section.start(). + raw_context.eip = 0x40004006; + raw_context.ebp = frame0_ebp.Value(); + raw_context.ebx = 0x53d1379d; // saved %esi + raw_context.esi = 0x743833c9; // callee's %esi + raw_context.edi = 0x40005510; // return address + CheckWalk(); +} + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,309 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// synth_minidump.cc: Implementation of SynthMinidump. See synth_minidump.h + +#include "processor/synth_minidump.h" + +namespace google_breakpad { + +namespace SynthMinidump { + +Section::Section(const Dump &dump) + : TestAssembler::Section(dump.endianness()) { } + +void Section::CiteLocationIn(TestAssembler::Section *section) const { + if (this) + (*section).D32(size_).D32(file_offset_); + else + (*section).D32(0).D32(0); +} + +void Stream::CiteStreamIn(TestAssembler::Section *section) const { + section->D32(type_); + CiteLocationIn(section); +} + +SystemInfo::SystemInfo(const Dump &dump, + const MDRawSystemInfo &system_info, + const String &csd_version) + : Stream(dump, MD_SYSTEM_INFO_STREAM) { + D16(system_info.processor_architecture); + D16(system_info.processor_level); + D16(system_info.processor_revision); + D8(system_info.number_of_processors); + D8(system_info.product_type); + D32(system_info.major_version); + D32(system_info.minor_version); + D32(system_info.build_number); + D32(system_info.platform_id); + csd_version.CiteStringIn(this); + D16(system_info.suite_mask); + D16(system_info.reserved2); // Well, why not? + + // MDCPUInformation cpu; + if (system_info.processor_architecture == MD_CPU_ARCHITECTURE_X86) { + D32(system_info.cpu.x86_cpu_info.vendor_id[0]); + D32(system_info.cpu.x86_cpu_info.vendor_id[1]); + D32(system_info.cpu.x86_cpu_info.vendor_id[2]); + D32(system_info.cpu.x86_cpu_info.version_information); + D32(system_info.cpu.x86_cpu_info.feature_information); + D32(system_info.cpu.x86_cpu_info.amd_extended_cpu_features); + } else { + D64(system_info.cpu.other_cpu_info.processor_features[0]); + D64(system_info.cpu.other_cpu_info.processor_features[1]); + } +} + +const MDRawSystemInfo SystemInfo::windows_x86 = { + MD_CPU_ARCHITECTURE_X86, // processor_architecture + 6, // processor_level + 0xd08, // processor_revision + 1, // number_of_processors + 1, // product_type + 5, // major_version + 1, // minor_version + 2600, // build_number + 2, // platform_id + 0xdeadbeef, // csd_version_rva + 0x100, // suite_mask + 0, // reserved2 + { // cpu + { // x86_cpu_info + { 0x756e6547, 0x49656e69, 0x6c65746e }, // vendor_id + 0x6d8, // version_information + 0xafe9fbff, // feature_information + 0xffffffff // amd_extended_cpu_features + } + } +}; + +const string SystemInfo::windows_x86_csd_version = "Service Pack 2"; + +String::String(const Dump &dump, const string &contents) : Section(dump) { + D32(contents.size() * 2); + for (string::const_iterator i = contents.begin(); i != contents.end(); i++) + D16(*i); +} + +void String::CiteStringIn(TestAssembler::Section *section) const { + section->D32(file_offset_); +} + +void Memory::CiteMemoryIn(TestAssembler::Section *section) const { + section->D64(address_); + CiteLocationIn(section); +} + +Context::Context(const Dump &dump, const MDRawContextX86 &context) + : Section(dump) { + // The caller should have properly set the CPU type flag. + assert(context.context_flags & MD_CONTEXT_X86); + // It doesn't make sense to store x86 registers in big-endian form. + assert(dump.endianness() == kLittleEndian); + D32(context.context_flags); + D32(context.dr0); + D32(context.dr1); + D32(context.dr2); + D32(context.dr3); + D32(context.dr6); + D32(context.dr7); + D32(context.float_save.control_word); + D32(context.float_save.status_word); + D32(context.float_save.tag_word); + D32(context.float_save.error_offset); + D32(context.float_save.error_selector); + D32(context.float_save.data_offset); + D32(context.float_save.data_selector); + // context.float_save.register_area[] contains 8-bit quantities and + // does not need to be swapped. + Append(context.float_save.register_area, + sizeof(context.float_save.register_area)); + D32(context.float_save.cr0_npx_state); + D32(context.gs); + D32(context.fs); + D32(context.es); + D32(context.ds); + D32(context.edi); + D32(context.esi); + D32(context.ebx); + D32(context.edx); + D32(context.ecx); + D32(context.eax); + D32(context.ebp); + D32(context.eip); + D32(context.cs); + D32(context.eflags); + D32(context.esp); + D32(context.ss); + // context.extended_registers[] contains 8-bit quantities and does + // not need to be swapped. + Append(context.extended_registers, sizeof(context.extended_registers)); + assert(Size() == sizeof(MDRawContextX86)); +} + +Thread::Thread(const Dump &dump, + u_int32_t thread_id, const Memory &stack, const Context &context, + u_int32_t suspend_count, u_int32_t priority_class, + u_int32_t priority, u_int64_t teb) : Section(dump) { + D32(thread_id); + D32(suspend_count); + D32(priority_class); + D32(priority); + D64(teb); + stack.CiteMemoryIn(this); + context.CiteLocationIn(this); + assert(Size() == sizeof(MDRawThread)); +} + +Module::Module(const Dump &dump, + u_int64_t base_of_image, + u_int32_t size_of_image, + const String &name, + u_int32_t time_date_stamp, + u_int32_t checksum, + const MDVSFixedFileInfo &version_info, + const Section *cv_record, + const Section *misc_record) : Section(dump) { + D64(base_of_image); + D32(size_of_image); + D32(checksum); + D32(time_date_stamp); + name.CiteStringIn(this); + D32(version_info.signature); + D32(version_info.struct_version); + D32(version_info.file_version_hi); + D32(version_info.file_version_lo); + D32(version_info.product_version_hi); + D32(version_info.product_version_lo); + D32(version_info.file_flags_mask); + D32(version_info.file_flags); + D32(version_info.file_os); + D32(version_info.file_type); + D32(version_info.file_subtype); + D32(version_info.file_date_hi); + D32(version_info.file_date_lo); + cv_record->CiteLocationIn(this); + misc_record->CiteLocationIn(this); + D64(0).D64(0); +} + +const MDVSFixedFileInfo Module::stock_version_info = { + MD_VSFIXEDFILEINFO_SIGNATURE, // signature + MD_VSFIXEDFILEINFO_VERSION, // struct_version + 0x11111111, // file_version_hi + 0x22222222, // file_version_lo + 0x33333333, // product_version_hi + 0x44444444, // product_version_lo + MD_VSFIXEDFILEINFO_FILE_FLAGS_DEBUG, // file_flags_mask + MD_VSFIXEDFILEINFO_FILE_FLAGS_DEBUG, // file_flags + MD_VSFIXEDFILEINFO_FILE_OS_NT | MD_VSFIXEDFILEINFO_FILE_OS__WINDOWS32, + // file_os + MD_VSFIXEDFILEINFO_FILE_TYPE_APP, // file_type + MD_VSFIXEDFILEINFO_FILE_SUBTYPE_UNKNOWN, // file_subtype + 0, // file_date_hi + 0 // file_date_lo +}; + +Dump::Dump(u_int64_t flags, + Endianness endianness, + u_int32_t version, + u_int32_t date_time_stamp) + : TestAssembler::Section(endianness), + file_start_(0), + stream_directory_(*this), + stream_count_(0), + thread_list_(*this, MD_THREAD_LIST_STREAM), + module_list_(*this, MD_MODULE_LIST_STREAM), + memory_list_(*this, MD_MEMORY_LIST_STREAM) + { + D32(MD_HEADER_SIGNATURE); + D32(version); + D32(stream_count_label_); + D32(stream_directory_rva_); + D32(0); + D32(date_time_stamp); + D64(flags); + assert(Size() == sizeof(MDRawHeader)); +} + +Dump &Dump::Add(SynthMinidump::Section *section) { + section->Finish(file_start_ + Size()); + Append(*section); + return *this; +} + +Dump &Dump::Add(Stream *stream) { + Add(static_cast(stream)); + stream->CiteStreamIn(&stream_directory_); + stream_count_++; + return *this; +} + +Dump &Dump::Add(Memory *memory) { + // Add the memory contents themselves to the file. + Add(static_cast(memory)); + + // The memory list is a list of MDMemoryDescriptors, not of actual + // memory elements. Produce a descriptor, and add that to the list. + SynthMinidump::Section descriptor(*this); + memory->CiteMemoryIn(&descriptor); + memory_list_.Add(&descriptor); + return *this; +} + +Dump &Dump::Add(Thread *thread) { + thread_list_.Add(thread); + return *this; +} + +Dump &Dump::Add(Module *module) { + module_list_.Add(module); + return *this; +} + +void Dump::Finish() { + if (!thread_list_.Empty()) Add(&thread_list_); + if (!module_list_.Empty()) Add(&module_list_); + if (!memory_list_.Empty()) Add(&memory_list_); + + // Create the stream directory. We don't use + // stream_directory_.Finish here, because the stream directory isn't + // cited using a location descriptor; rather, the Minidump header + // has the stream count and MDRVA. + stream_count_label_ = stream_count_; + stream_directory_rva_ = file_start_ + Size(); + Append(static_cast(stream_directory_)); +} + +} // namespace SynthMinidump + +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,357 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// synth_minidump.h: Interface to SynthMinidump: fake minidump generator. +// +// We treat a minidump file as the concatenation of a bunch of +// TestAssembler::Sections. The file header, stream directory, +// streams, memory regions, strings, and so on --- each is a Section +// that eventually gets appended to the minidump. Dump, Memory, +// Context, Thread, and so on all inherit from TestAssembler::Section. +// For example: +// +// using google_breakpad::TestAssembler::kLittleEndian; +// using google_breakpad::SynthMinidump::Context; +// using google_breakpad::SynthMinidump::Dump; +// using google_breakpad::SynthMinidump::Memory; +// using google_breakpad::SynthMinidump::Thread; +// +// Dump minidump(MD_NORMAL, kLittleEndian); +// +// Memory stack1(minidump, 0x569eb0a9); +// ... build contents of stack1 with TestAssembler::Section functions ... +// +// MDRawContextX86 x86_context1; +// x86_context1.context_flags = MD_CONTEXT_X86; +// x86_context1.eip = 0x7c90eb94; +// x86_context1.esp = 0x569eb0a9; +// x86_context1.ebp = x86_context1.esp + something appropriate; +// Context context1(minidump, x86_context1); +// +// Thread thread1(minidump, 0xe4a4821d, stack1, context1); +// +// minidump.Add(&stack1); +// minidump.Add(&context1); +// minidump.Add(&thread1); +// minidump.Finish(); +// +// string contents; +// EXPECT_TRUE(minidump.GetContents(&contents)); +// // contents now holds the bytes of a minidump file +// +// Because the TestAssembler classes let us write Label references to +// sections before the Labels' values are known, this gives us +// flexibility in how we put the dump together: minidump pieces can +// hold the file offsets of other minidump pieces before the +// referents' positions have been decided. As long as everything has +// been placed by the time we call dump.GetContents to obtain the +// bytes, all the Labels' values will be known, and everything will +// get patched up appropriately. +// +// The dump.Add(thing) functions append THINGS's contents to the +// minidump, but they also do two other things: +// +// - dump.Add(thing) invokes thing->Finish, which tells *thing the +// offset within the file at which it was placed, and allows *thing +// to do any final content generation. +// +// - If THING is something which should receive an entry in some sort +// of list or directory, then dump.Add(THING) automatically creates +// the appropriate directory or list entry. Streams must appear in +// the stream directory; memory ranges should be listed in the +// memory list; threads should be placed in the thread list; and so +// on. +// +// By convention, Section subclass constructors that take references +// to other Sections do not take care of 'Add'ing their arguments to +// the dump. For example, although the Thread constructor takes +// references to a Memory and a Context, it does not add them to the +// dump on the caller's behalf. Rather, the caller is responsible for +// 'Add'ing every section they create. This allows Sections to be +// cited from more than one place; for example, Memory ranges are +// cited both from Thread objects (as their stack contents) and by the +// memory list stream. +// +// If you forget to Add some Section, the Dump::GetContents call will +// fail, as the TestAssembler::Labels used to cite the Section's +// contents from elsewhere will still be undefined. +#ifndef PROCESSOR_SYNTH_MINIDUMP_H_ +#define PROCESSOR_SYNTH_MINIDUMP_H_ + +#include +#include +#include + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/common/minidump_format.h" +#include "processor/test_assembler.h" + +namespace google_breakpad { + +namespace SynthMinidump { + +using std::string; +using TestAssembler::Endianness; +using TestAssembler::kBigEndian; +using TestAssembler::kLittleEndian; +using TestAssembler::kUnsetEndian; +using TestAssembler::Label; + +class Dump; +class Memory; +class String; + +// A TestAssembler::Section which will be appended to a minidump. +class Section: public TestAssembler::Section { + public: + explicit Section(const Dump &dump); + + // Append an MDLocationDescriptor referring to this section to SECTION. + // If 'this' is NULL, append a descriptor with a zero length and MDRVA. + // + // (I couldn't find the language in the C++ standard that says that + // invoking member functions of a NULL pointer to a class type is + // bad, if such language exists. Having this function handle NULL + // 'this' is convenient, but if it causes trouble, it's not hard to + // do differently.) + void CiteLocationIn(TestAssembler::Section *section) const; + + // Note that this section's contents are complete, and that it has + // been placed in the minidump file at OFFSET. The 'Add' member + // functions call the Finish member function of the object being + // added for you; if you are 'Add'ing this section, you needn't Finish it. + virtual void Finish(const Label &offset) { + file_offset_ = offset; size_ = Size(); + } + + protected: + // This section's size and offset within the minidump file. + Label file_offset_, size_; +}; + +// A stream within a minidump file. 'Add'ing a stream to a minidump +// creates an entry for it in the minidump's stream directory. +class Stream: public Section { + public: + // Create a stream of type TYPE. You can append whatever contents + // you like to this stream using the TestAssembler::Section methods. + Stream(const Dump &dump, u_int32_t type) : Section(dump), type_(type) { } + + // Append an MDRawDirectory referring to this stream to SECTION. + void CiteStreamIn(TestAssembler::Section *section) const; + + private: + // The type of this stream. + u_int32_t type_; +}; + +class SystemInfo: public Stream { + public: + // Create an MD_SYSTEM_INFO_STREAM stream belonging to DUMP holding + // an MDRawSystem info structure initialized with the values from + // SYSTEM_INFO, except that the csd_version field is replaced with + // the file offset of the string CSD_VERSION, which can be 'Add'ed + // to the dump at the desired location. + // + // Remember that you are still responsible for 'Add'ing CSD_VERSION + // to the dump yourself. + SystemInfo(const Dump &dump, + const MDRawSystemInfo &system_info, + const String &csd_version); + + // Stock MDRawSystemInfo information and associated strings, for + // writing tests. + static const MDRawSystemInfo windows_x86; + static const string windows_x86_csd_version; +}; + +// An MDString: a string predeced by a 32-bit length. +class String: public Section { + public: + String(const Dump &dump, const string &value); + + // Append an MDRVA referring to this string to SECTION. + void CiteStringIn(TestAssembler::Section *section) const; +}; + +// A range of memory contents. 'Add'ing a memory range to a minidump +// creates n entry for it in the minidump's memory list. By +// convention, the 'start', 'Here', and 'Mark' member functions refer +// to memory addresses. +class Memory: public Section { + public: + Memory(const Dump &dump, u_int64_t address) + : Section(dump), address_(address) { start() = address; } + + // Append an MDMemoryDescriptor referring to this memory range to SECTION. + void CiteMemoryIn(TestAssembler::Section *section) const; + + private: + // The process address from which these memory contents were taken. + // Shouldn't this be a Label? + u_int64_t address_; +}; + +class Context: public Section { + public: + // Create a context belonging to DUMP whose contents are a copy of CONTEXT. + Context(const Dump &dump, const MDRawContextX86 &context); + // Add constructors for other architectures here. Remember to byteswap. +}; + +class Thread: public Section { + public: + // Create a thread belonging to DUMP with the given values, citing + // STACK and CONTEXT (which you must Add to the dump separately). + Thread(const Dump &dump, + u_int32_t thread_id, + const Memory &stack, + const Context &context, + u_int32_t suspend_count = 0, + u_int32_t priority_class = 0, + u_int32_t priority = 0, + u_int64_t teb = 0); +}; + +class Module: public Section { + public: + // Create a module with the given values. Note that CV_RECORD and + // MISC_RECORD can be NULL, in which case the corresponding location + // descriptior in the minidump will have a length of zero. + Module(const Dump &dump, + u_int64_t base_of_image, + u_int32_t size_of_image, + const String &name, + u_int32_t time_date_stamp = 1262805309, + u_int32_t checksum = 0, + const MDVSFixedFileInfo &version_info = Module::stock_version_info, + const Section *cv_record = NULL, + const Section *misc_record = NULL); + + private: + // A standard MDVSFixedFileInfo structure to use as a default for + // minidumps. There's no reason to make users write out all this crap + // over and over. + static const MDVSFixedFileInfo stock_version_info; +}; + +// A list of entries starting with a 32-bit count, like a memory list +// or a thread list. +template +class List: public Stream { + public: + List(const Dump &dump, u_int32_t type) : Stream(dump, type), count_(0) { + D32(count_label_); + } + + // Add ELEMENT to this list. + void Add(Element *element) { + element->Finish(file_offset_ + Size()); + Append(*element); + count_++; + } + + // Return true if this List is empty, false otherwise. + bool Empty() { return count_ == 0; } + + // Finish up the contents of this section, mark it as having been + // placed at OFFSET. + virtual void Finish(const Label &offset) { + Stream::Finish(offset); + count_label_ = count_; + } + + private: + size_t count_; + Label count_label_; +}; + +class Dump: public TestAssembler::Section { + public: + + // Create a TestAssembler::Section containing a minidump file whose + // header uses the given values. ENDIANNESS determines the + // endianness of the signature; we set this section's default + // endianness by this. + Dump(u_int64_t flags, + Endianness endianness = kLittleEndian, + u_int32_t version = MD_HEADER_VERSION, + u_int32_t date_time_stamp = 1262805309); + + // The following functions call OBJECT->Finish(), and append the + // contents of OBJECT to this minidump. They also record OBJECT in + // whatever directory or list is appropriate for its type. The + // stream directory, memory list, thread list, and module list are + // accumulated this way. + Dump &Add(SynthMinidump::Section *object); // simply append data + Dump &Add(Stream *object); // append, record in stream directory + Dump &Add(Memory *object); // append, record in memory list + Dump &Add(Thread *object); // append, record in thread list + Dump &Add(Module *object); // append, record in module list + + // Complete the construction of the minidump, given the Add calls + // we've seen up to this point. After this call, this Dump's + // contents are complete, all labels should be defined if everything + // Cited has been Added, and you may call GetContents on it. + void Finish(); + + private: + // A label representing the start of the minidump file. + Label file_start_; + + // The stream directory. We construct this incrementally from + // Add(Stream *) calls. + SynthMinidump::Section stream_directory_; // The directory's contents. + size_t stream_count_; // The number of streams so far. + Label stream_count_label_; // Cited in file header. + Label stream_directory_rva_; // The directory's file offset. + + // This minidump's thread list. We construct this incrementally from + // Add(Thread *) calls. + List thread_list_; + + // This minidump's module list. We construct this incrementally from + // Add(Module *) calls. + List module_list_; + + // This minidump's memory list. We construct this incrementally from + // Add(Memory *) calls. This is actually a list of MDMemoryDescriptors, + // not memory ranges --- thus the odd type. + List memory_list_; +}; + +} // namespace SynthMinidump + +} // namespace google_breakpad + +#endif // PROCESSOR_SYNTH_MINIDUMP_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,280 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// test_assembler_unittest.cc: Unit tests for google_breakpad::TestAssembler. + +#include +#include + +#include "breakpad_googletest_includes.h" +#include "google_breakpad/common/minidump_format.h" +#include "processor/synth_minidump.h" +#include "processor/synth_minidump_unittest_data.h" + +using google_breakpad::SynthMinidump::Context; +using google_breakpad::SynthMinidump::Dump; +using google_breakpad::SynthMinidump::List; +using google_breakpad::SynthMinidump::Memory; +using google_breakpad::SynthMinidump::Module; +using google_breakpad::SynthMinidump::Section; +using google_breakpad::SynthMinidump::Stream; +using google_breakpad::SynthMinidump::String; +using google_breakpad::SynthMinidump::SystemInfo; +using google_breakpad::SynthMinidump::Thread; +using google_breakpad::TestAssembler::kBigEndian; +using google_breakpad::TestAssembler::kLittleEndian; +using google_breakpad::TestAssembler::Label; +using std::string; + +TEST(Section, Simple) { + Dump dump(0); + Section section(dump); + section.L32(0x12345678); + section.Finish(0); + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x78\x56\x34\x12", 4), contents); +} + +TEST(Section, CiteLocationIn) { + Dump dump(0, kBigEndian); + Section section1(dump), section2(dump); + section1.Append("order"); + section2.Append("mayhem"); + section2.Finish(0x32287ec2); + section2.CiteLocationIn(§ion1); + string contents; + ASSERT_TRUE(section1.GetContents(&contents)); + string expected("order\0\0\0\x06\x32\x28\x7e\xc2", 13); + EXPECT_EQ(expected, contents); +} + +TEST(Stream, CiteStreamIn) { + Dump dump(0, kLittleEndian); + Stream stream(dump, 0x40cae2b3); + Section section(dump); + stream.Append("stream contents"); + section.Append("section contents"); + stream.Finish(0x41424344); + stream.CiteStreamIn(§ion); + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + string expected("section contents" + "\xb3\xe2\xca\x40" + "\x0f\0\0\0" + "\x44\x43\x42\x41", + 16 + 4 + 4 + 4); + EXPECT_EQ(expected, contents); +} + +TEST(Memory, CiteMemoryIn) { + Dump dump(0, kBigEndian); + Memory memory(dump, 0x76d010874ab019f9ULL); + Section section(dump); + memory.Append("memory contents"); + section.Append("section contents"); + memory.Finish(0x51525354); + memory.CiteMemoryIn(§ion); + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + string expected("section contents" + "\x76\xd0\x10\x87\x4a\xb0\x19\xf9" + "\0\0\0\x0f" + "\x51\x52\x53\x54", + 16 + 8 + 4 + 4); + EXPECT_EQ(contents, expected); +} + +TEST(Memory, Here) { + Dump dump(0, kBigEndian); + Memory memory(dump, 0x89979731eb060ed4ULL); + memory.Append(1729, 42); + Label l = memory.Here(); + ASSERT_EQ(0x89979731eb060ed4ULL + 1729, l.Value()); +} + +TEST(Context, X86) { + Dump dump(0, kLittleEndian); + assert(x86_raw_context.context_flags & MD_CONTEXT_X86); + Context context(dump, x86_raw_context); + string contents; + ASSERT_TRUE(context.GetContents(&contents)); + EXPECT_EQ(sizeof(x86_expected_contents), contents.size()); + EXPECT_TRUE(memcmp(contents.data(), x86_expected_contents, contents.size()) + == 0); +} + +TEST(ContextDeathTest, X86BadFlags) { + Dump dump(0, kLittleEndian); + MDRawContextX86 raw; + raw.context_flags = 0; + ASSERT_DEATH(Context context(dump, raw);, + "context\\.context_flags & 0x[0-9a-f]+"); +} + +TEST(ContextDeathTest, X86BadEndianness) { + Dump dump(0, kBigEndian); + MDRawContextX86 raw; + raw.context_flags = MD_CONTEXT_X86; + ASSERT_DEATH(Context context(dump, raw);, + "dump\\.endianness\\(\\) == kLittleEndian"); +} + +TEST(Thread, Simple) { + Dump dump(0, kLittleEndian); + Context context(dump, x86_raw_context); + context.Finish(0x8665da0c); + Memory stack(dump, 0xaad55a93cc3c0efcULL); + stack.Append("stack contents"); + stack.Finish(0xe08cdbd1); + Thread thread(dump, 0x3d7ec360, stack, context, + 0x3593f44d, // suspend count + 0xab352b82, // priority class + 0x2753d838, // priority + 0xeb2de4be3f29e3e9ULL); // thread environment block + string contents; + ASSERT_TRUE(thread.GetContents(&contents)); + static const u_int8_t expected_bytes[] = { + 0x60, 0xc3, 0x7e, 0x3d, // thread id + 0x4d, 0xf4, 0x93, 0x35, // suspend count + 0x82, 0x2b, 0x35, 0xab, // priority class + 0x38, 0xd8, 0x53, 0x27, // priority + 0xe9, 0xe3, 0x29, 0x3f, 0xbe, 0xe4, 0x2d, 0xeb, // thread environment block + 0xfc, 0x0e, 0x3c, 0xcc, 0x93, 0x5a, 0xd5, 0xaa, // stack address + 0x0e, 0x00, 0x00, 0x00, // stack size + 0xd1, 0xdb, 0x8c, 0xe0, // stack MDRVA + 0xcc, 0x02, 0x00, 0x00, // context size + 0x0c, 0xda, 0x65, 0x86 // context MDRVA + }; + EXPECT_EQ(sizeof(expected_bytes), contents.size()); + EXPECT_TRUE(memcmp(contents.data(), expected_bytes, contents.size()) == 0); +} + +TEST(String, Simple) { + Dump dump(0, kBigEndian); + String s(dump, "All mimsy were the borogoves"); + string contents; + ASSERT_TRUE(s.GetContents(&contents)); + static const char expected[] = + "\x00\x00\x00\x38\0A\0l\0l\0 \0m\0i\0m\0s\0y\0 \0w\0e\0r\0e" + "\0 \0t\0h\0e\0 \0b\0o\0r\0o\0g\0o\0v\0e\0s"; + string expected_string(expected, sizeof(expected) - 1); + EXPECT_EQ(expected_string, contents); +} + +TEST(String, CiteStringIn) { + Dump dump(0, kLittleEndian); + String s(dump, "and the mome wraths outgrabe"); + Section section(dump); + section.Append("initial"); + s.CiteStringIn(§ion); + s.Finish(0xdc2bb469); + string contents; + ASSERT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("initial\x69\xb4\x2b\xdc", 7 + 4), contents); +} + +TEST(List, Empty) { + Dump dump(0, kBigEndian); + List
    list(dump, 0x2442779c); + EXPECT_TRUE(list.Empty()); + list.Finish(0x84e09808); + string contents; + ASSERT_TRUE(list.GetContents(&contents)); + EXPECT_EQ(string("\0\0\0\0", 4), contents); +} + +TEST(List, Two) { + Dump dump(0, kBigEndian); + List
    list(dump, 0x26c9f498); + Section section1(dump); + section1.Append("section one contents"); + EXPECT_TRUE(list.Empty()); + list.Add(§ion1); + EXPECT_FALSE(list.Empty()); + Section section2(dump); + section2.Append("section two contents"); + list.Add(§ion2); + list.Finish(0x1e5bb60e); + string contents; + ASSERT_TRUE(list.GetContents(&contents)); + EXPECT_EQ(string("\0\0\0\x02section one contentssection two contents", 44), + contents); +} + +TEST(Dump, Header) { + Dump dump(0x9f738b33685cc84cULL, kLittleEndian, 0xb3817faf, 0x2c741c0a); + dump.Finish(); + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + ASSERT_EQ(string("\x4d\x44\x4d\x50" // signature + "\xaf\x7f\x81\xb3" // version + "\0\0\0\0" // stream count + "\x20\0\0\0" // directory RVA (could be anything) + "\0\0\0\0" // checksum + "\x0a\x1c\x74\x2c" // time_date_stamp + "\x4c\xc8\x5c\x68\x33\x8b\x73\x9f", // flags + 32), + contents); +} + +TEST(Dump, HeaderBigEndian) { + Dump dump(0x206ce3cc6fb8e0f0ULL, kBigEndian, 0x161693e2, 0x35667744); + dump.Finish(); + string contents; + ASSERT_TRUE(dump.GetContents(&contents)); + ASSERT_EQ(string("\x50\x4d\x44\x4d" // signature + "\x16\x16\x93\xe2" // version + "\0\0\0\0" // stream count + "\0\0\0\x20" // directory RVA (could be anything) + "\0\0\0\0" // checksum + "\x35\x66\x77\x44" // time_date_stamp + "\x20\x6c\xe3\xcc\x6f\xb8\xe0\xf0", // flags + 32), + contents); +} + +TEST(Dump, OneSection) { + Dump dump(0, kLittleEndian); + Section section(dump); + section.Append("section contents"); + dump.Add(§ion); + dump.Finish(); + string dump_contents; + // Just check for undefined labels; don't worry about the contents. + ASSERT_TRUE(dump.GetContents(&dump_contents)); + + Section referencing_section(dump); + section.CiteLocationIn(&referencing_section); + string contents; + ASSERT_TRUE(referencing_section.GetContents(&contents)); + ASSERT_EQ(string("\x10\0\0\0\x20\0\0\0", 8), contents); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump_unittest_data.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump_unittest_data.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump_unittest_data.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/synth_minidump_unittest_data.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,246 @@ +// -*- mode: C++ -*- + +// Not copyrightable: random test data. +// synth_minidump_unittest_data.h: verbose test data for SynthMinidump tests. + +#ifndef PROCESSOR_SYNTH_MINIDUMP_UNITTEST_DATA_H_ +#define PROCESSOR_SYNTH_MINIDUMP_UNITTEST_DATA_H_ + +#include "google_breakpad/common/minidump_format.h" + +static const MDRawContextX86 x86_raw_context = { + 0xded5d71b, // context_flags + 0x9fdb432e, // dr0 + 0x26b7a81a, // dr1 + 0xcac7e348, // dr2 + 0xcf99ec09, // dr3 + 0x7dc8c2cd, // dr6 + 0x21deb880, // dr7 + + // float_save + { + 0x8a5d2bb0, // control_word + 0x0286c4c9, // status_word + 0xf1feea21, // tag_word + 0xb2d40576, // error_offset + 0x48146cde, // error_selector + 0x983f9b21, // data_offset + 0x475be12c, // data_selector + + // register_area + { + 0xd9, 0x04, 0x20, 0x6b, 0x88, 0x3a, 0x3f, 0xd5, + 0x59, 0x7a, 0xa9, 0xeb, 0xd0, 0x5c, 0xdf, 0xfe, + 0xad, 0xdd, 0x4a, 0x8b, 0x10, 0xcc, 0x9a, 0x33, + 0xcb, 0xb6, 0xf7, 0x86, 0xcd, 0x69, 0x25, 0xae, + 0x25, 0xe5, 0x7a, 0xa1, 0x8f, 0xb2, 0x84, 0xd9, + 0xf7, 0x2d, 0x8a, 0xa1, 0x80, 0x81, 0x7f, 0x67, + 0x07, 0xa8, 0x23, 0xf1, 0x8c, 0xdc, 0xd8, 0x04, + 0x8b, 0x9d, 0xb1, 0xcd, 0x61, 0x0c, 0x9c, 0x69, + 0xc7, 0x8d, 0x17, 0xb6, 0xe5, 0x0b, 0x94, 0xf7, + 0x78, 0x9b, 0x63, 0x49, 0xba, 0xfc, 0x08, 0x4d + }, + + 0x84c53a90, // cr0_npx_state + }, + + 0x79f71e76, // gs + 0x8107bd25, // fs + 0x452d2921, // es + 0x87ec2875, // ds + 0xf8bb73f5, // edi + 0xa63ebb88, // esi + 0x95d35ebe, // ebx + 0x17aa2456, // edx + 0x135fa208, // ecx + 0x500615e6, // eax + 0x66d14205, // ebp + 0x000719a5, // eip + 0x477b481b, // cs + 0x8684dfba, // eflags + 0xe33ccddf, // esp + 0xc0e65d33, // ss + + // extended_registers + { + 0x68, 0x63, 0xdf, 0x50, 0xf7, 0x3b, 0xe8, 0xe5, + 0xcb, 0xd6, 0x66, 0x60, 0xe5, 0xa3, 0x58, 0xb3, + 0x6f, 0x34, 0xca, 0x02, 0x9b, 0x5f, 0xd0, 0x41, + 0xbd, 0xc5, 0x2d, 0xf8, 0xff, 0x15, 0xa2, 0xd0, + 0xe3, 0x2b, 0x3b, 0x8a, 0x9f, 0xc3, 0x9e, 0x28, + 0x0a, 0xc2, 0xac, 0x3b, 0x67, 0x37, 0x01, 0xfd, + 0xc3, 0xaf, 0x60, 0xf6, 0x2c, 0x4f, 0xa9, 0x52, + 0x92, 0xe5, 0x28, 0xde, 0x34, 0xb6, 0x2e, 0x44, + 0x15, 0xa4, 0xb6, 0xe4, 0xc9, 0x1a, 0x14, 0xb9, + 0x51, 0x33, 0x3c, 0xe0, 0xc7, 0x94, 0xf0, 0xf7, + 0x78, 0xdd, 0xe5, 0xca, 0xb7, 0xa6, 0xe0, 0x14, + 0xa6, 0x03, 0xab, 0x77, 0xad, 0xbd, 0xd2, 0x53, + 0x3d, 0x07, 0xe7, 0xaf, 0x90, 0x44, 0x71, 0xbe, + 0x0c, 0xdf, 0x2b, 0x97, 0x40, 0x48, 0xd5, 0xf9, + 0x62, 0x03, 0x91, 0x84, 0xd6, 0xdd, 0x29, 0x97, + 0x35, 0x02, 0xfb, 0x59, 0x97, 0xb0, 0xec, 0xa9, + 0x39, 0x6f, 0x81, 0x71, 0x2a, 0xf0, 0xe7, 0x2c, + 0x4e, 0x93, 0x90, 0xcb, 0x67, 0x69, 0xde, 0xd7, + 0x68, 0x3b, 0x0f, 0x69, 0xa8, 0xf4, 0xa8, 0x83, + 0x42, 0x80, 0x47, 0x65, 0x7a, 0xc9, 0x19, 0x5d, + 0xcb, 0x43, 0xa5, 0xff, 0xf8, 0x9e, 0x62, 0xf4, + 0xe2, 0x6c, 0xcc, 0x17, 0x55, 0x7c, 0x0d, 0x5c, + 0x8d, 0x16, 0x01, 0xd7, 0x3a, 0x0c, 0xf4, 0x7f, + 0x71, 0xdc, 0x48, 0xe9, 0x4b, 0xfe, 0x1a, 0xd0, + 0x04, 0x15, 0x33, 0xec, 0x78, 0xc6, 0x7e, 0xde, + 0x7c, 0x23, 0x18, 0x8d, 0x8f, 0xc2, 0x74, 0xc1, + 0x48, 0xcd, 0x5d, 0xee, 0xee, 0x81, 0x9e, 0x49, + 0x47, 0x8a, 0xf8, 0x61, 0xa3, 0x9c, 0x81, 0x96, + 0xbe, 0x2b, 0x5e, 0xbc, 0xcd, 0x34, 0x0a, 0x2a, + 0x3b, 0x8b, 0x7d, 0xa1, 0xf2, 0x8d, 0xb4, 0x51, + 0x9e, 0x14, 0x78, 0xa3, 0x58, 0x65, 0x2d, 0xd6, + 0x50, 0x40, 0x36, 0x32, 0x31, 0xd4, 0x3e, 0xc2, + 0xe0, 0x87, 0x1c, 0x05, 0x95, 0x80, 0x84, 0x24, + 0x08, 0x6f, 0x5b, 0xc7, 0xe1, 0x1d, 0xd5, 0xa3, + 0x94, 0x44, 0xa1, 0x7c, 0xd8, 0x4b, 0x86, 0xd2, + 0xc6, 0xa9, 0xf3, 0xe2, 0x4d, 0x6e, 0x1f, 0x0e, + 0xf2, 0xf5, 0x71, 0xf9, 0x71, 0x05, 0x24, 0xc9, + 0xc1, 0xe8, 0x91, 0x42, 0x61, 0x86, 0x57, 0x68, + 0xd9, 0xc9, 0x1d, 0xd5, 0x5a, 0xe9, 0xba, 0xe6, + 0x15, 0x8f, 0x87, 0xbd, 0x62, 0x56, 0xed, 0xda, + 0xc2, 0xa5, 0xd5, 0x39, 0xac, 0x05, 0x10, 0x14, + 0x4a, 0xe7, 0xe7, 0x3c, 0x3f, 0xb7, 0xbb, 0xed, + 0x01, 0x6e, 0xcd, 0xee, 0x81, 0xb4, 0x62, 0xf4, + 0x62, 0x16, 0xff, 0x20, 0xb4, 0xf0, 0xbc, 0xff, + 0x7d, 0xd9, 0xcf, 0x95, 0x30, 0x27, 0xe0, 0x2f, + 0x98, 0x53, 0x80, 0x15, 0x13, 0xef, 0x44, 0x58, + 0x12, 0x16, 0xdb, 0x11, 0xef, 0x73, 0x51, 0xcd, + 0x42, 0x3f, 0x98, 0x6c, 0xc9, 0x68, 0xc3, 0xf4, + 0x5b, 0x0f, 0x5d, 0x77, 0xed, 0xdf, 0x0f, 0xff, + 0xb8, 0x69, 0x98, 0x50, 0x77, 0x7a, 0xe8, 0x90, + 0x27, 0x46, 0x10, 0xd2, 0xb5, 0x00, 0x3b, 0x36, + 0x43, 0x6d, 0x67, 0x41, 0x20, 0x3a, 0x32, 0xe0, + 0x2e, 0x5a, 0xfb, 0x4e, 0x4f, 0xa4, 0xf7, 0xc2, + 0xe6, 0x81, 0x1a, 0x51, 0xa8, 0x7c, 0xd4, 0x60, + 0x7c, 0x45, 0xe2, 0xba, 0x5b, 0x42, 0xf3, 0xbf, + 0x28, 0xaa, 0xf2, 0x90, 0xe4, 0x94, 0xdd, 0xaa, + 0x22, 0xd3, 0x71, 0x33, 0xa1, 0x01, 0x43, 0x0e, + 0xfa, 0x46, 0xd2, 0x6e, 0x55, 0x5e, 0x49, 0xeb, + 0x94, 0xf0, 0xb0, 0xb1, 0x2e, 0xf2, 0x3d, 0x6c, + 0x00, 0x5e, 0x01, 0x56, 0x3b, 0xfd, 0x5b, 0xa1, + 0x2f, 0x63, 0x1d, 0xbf, 0xf9, 0xd8, 0x13, 0xf7, + 0x4d, 0xb7, 0x1e, 0x3d, 0x98, 0xd2, 0xee, 0xb8, + 0x48, 0xc8, 0x5b, 0x91, 0x0f, 0x54, 0x9e, 0x26, + 0xb2, 0xc7, 0x3a, 0x6c, 0x8a, 0x35, 0xe1, 0xba + } +}; + +static const u_int8_t x86_expected_contents[] = { + 0x1b, 0xd7, 0xd5, 0xde, + 0x2e, 0x43, 0xdb, 0x9f, + 0x1a, 0xa8, 0xb7, 0x26, + 0x48, 0xe3, 0xc7, 0xca, + 0x09, 0xec, 0x99, 0xcf, + 0xcd, 0xc2, 0xc8, 0x7d, + 0x80, 0xb8, 0xde, 0x21, + 0xb0, 0x2b, 0x5d, 0x8a, + 0xc9, 0xc4, 0x86, 0x02, + 0x21, 0xea, 0xfe, 0xf1, + 0x76, 0x05, 0xd4, 0xb2, + 0xde, 0x6c, 0x14, 0x48, + 0x21, 0x9b, 0x3f, 0x98, + 0x2c, 0xe1, 0x5b, 0x47, + + // float_save.register_area --- unswapped + 0xd9, 0x04, 0x20, 0x6b, 0x88, 0x3a, 0x3f, 0xd5, + 0x59, 0x7a, 0xa9, 0xeb, 0xd0, 0x5c, 0xdf, 0xfe, + 0xad, 0xdd, 0x4a, 0x8b, 0x10, 0xcc, 0x9a, 0x33, + 0xcb, 0xb6, 0xf7, 0x86, 0xcd, 0x69, 0x25, 0xae, + 0x25, 0xe5, 0x7a, 0xa1, 0x8f, 0xb2, 0x84, 0xd9, + 0xf7, 0x2d, 0x8a, 0xa1, 0x80, 0x81, 0x7f, 0x67, + 0x07, 0xa8, 0x23, 0xf1, 0x8c, 0xdc, 0xd8, 0x04, + 0x8b, 0x9d, 0xb1, 0xcd, 0x61, 0x0c, 0x9c, 0x69, + 0xc7, 0x8d, 0x17, 0xb6, 0xe5, 0x0b, 0x94, 0xf7, + 0x78, 0x9b, 0x63, 0x49, 0xba, 0xfc, 0x08, 0x4d, + + 0x90, 0x3a, 0xc5, 0x84, + 0x76, 0x1e, 0xf7, 0x79, + 0x25, 0xbd, 0x07, 0x81, + 0x21, 0x29, 0x2d, 0x45, + 0x75, 0x28, 0xec, 0x87, + 0xf5, 0x73, 0xbb, 0xf8, + 0x88, 0xbb, 0x3e, 0xa6, + 0xbe, 0x5e, 0xd3, 0x95, + 0x56, 0x24, 0xaa, 0x17, + 0x08, 0xa2, 0x5f, 0x13, + 0xe6, 0x15, 0x06, 0x50, + 0x05, 0x42, 0xd1, 0x66, + 0xa5, 0x19, 0x07, 0x00, + 0x1b, 0x48, 0x7b, 0x47, + 0xba, 0xdf, 0x84, 0x86, + 0xdf, 0xcd, 0x3c, 0xe3, + 0x33, 0x5d, 0xe6, 0xc0, + + // extended_registers --- unswapped + 0x68, 0x63, 0xdf, 0x50, 0xf7, 0x3b, 0xe8, 0xe5, + 0xcb, 0xd6, 0x66, 0x60, 0xe5, 0xa3, 0x58, 0xb3, + 0x6f, 0x34, 0xca, 0x02, 0x9b, 0x5f, 0xd0, 0x41, + 0xbd, 0xc5, 0x2d, 0xf8, 0xff, 0x15, 0xa2, 0xd0, + 0xe3, 0x2b, 0x3b, 0x8a, 0x9f, 0xc3, 0x9e, 0x28, + 0x0a, 0xc2, 0xac, 0x3b, 0x67, 0x37, 0x01, 0xfd, + 0xc3, 0xaf, 0x60, 0xf6, 0x2c, 0x4f, 0xa9, 0x52, + 0x92, 0xe5, 0x28, 0xde, 0x34, 0xb6, 0x2e, 0x44, + 0x15, 0xa4, 0xb6, 0xe4, 0xc9, 0x1a, 0x14, 0xb9, + 0x51, 0x33, 0x3c, 0xe0, 0xc7, 0x94, 0xf0, 0xf7, + 0x78, 0xdd, 0xe5, 0xca, 0xb7, 0xa6, 0xe0, 0x14, + 0xa6, 0x03, 0xab, 0x77, 0xad, 0xbd, 0xd2, 0x53, + 0x3d, 0x07, 0xe7, 0xaf, 0x90, 0x44, 0x71, 0xbe, + 0x0c, 0xdf, 0x2b, 0x97, 0x40, 0x48, 0xd5, 0xf9, + 0x62, 0x03, 0x91, 0x84, 0xd6, 0xdd, 0x29, 0x97, + 0x35, 0x02, 0xfb, 0x59, 0x97, 0xb0, 0xec, 0xa9, + 0x39, 0x6f, 0x81, 0x71, 0x2a, 0xf0, 0xe7, 0x2c, + 0x4e, 0x93, 0x90, 0xcb, 0x67, 0x69, 0xde, 0xd7, + 0x68, 0x3b, 0x0f, 0x69, 0xa8, 0xf4, 0xa8, 0x83, + 0x42, 0x80, 0x47, 0x65, 0x7a, 0xc9, 0x19, 0x5d, + 0xcb, 0x43, 0xa5, 0xff, 0xf8, 0x9e, 0x62, 0xf4, + 0xe2, 0x6c, 0xcc, 0x17, 0x55, 0x7c, 0x0d, 0x5c, + 0x8d, 0x16, 0x01, 0xd7, 0x3a, 0x0c, 0xf4, 0x7f, + 0x71, 0xdc, 0x48, 0xe9, 0x4b, 0xfe, 0x1a, 0xd0, + 0x04, 0x15, 0x33, 0xec, 0x78, 0xc6, 0x7e, 0xde, + 0x7c, 0x23, 0x18, 0x8d, 0x8f, 0xc2, 0x74, 0xc1, + 0x48, 0xcd, 0x5d, 0xee, 0xee, 0x81, 0x9e, 0x49, + 0x47, 0x8a, 0xf8, 0x61, 0xa3, 0x9c, 0x81, 0x96, + 0xbe, 0x2b, 0x5e, 0xbc, 0xcd, 0x34, 0x0a, 0x2a, + 0x3b, 0x8b, 0x7d, 0xa1, 0xf2, 0x8d, 0xb4, 0x51, + 0x9e, 0x14, 0x78, 0xa3, 0x58, 0x65, 0x2d, 0xd6, + 0x50, 0x40, 0x36, 0x32, 0x31, 0xd4, 0x3e, 0xc2, + 0xe0, 0x87, 0x1c, 0x05, 0x95, 0x80, 0x84, 0x24, + 0x08, 0x6f, 0x5b, 0xc7, 0xe1, 0x1d, 0xd5, 0xa3, + 0x94, 0x44, 0xa1, 0x7c, 0xd8, 0x4b, 0x86, 0xd2, + 0xc6, 0xa9, 0xf3, 0xe2, 0x4d, 0x6e, 0x1f, 0x0e, + 0xf2, 0xf5, 0x71, 0xf9, 0x71, 0x05, 0x24, 0xc9, + 0xc1, 0xe8, 0x91, 0x42, 0x61, 0x86, 0x57, 0x68, + 0xd9, 0xc9, 0x1d, 0xd5, 0x5a, 0xe9, 0xba, 0xe6, + 0x15, 0x8f, 0x87, 0xbd, 0x62, 0x56, 0xed, 0xda, + 0xc2, 0xa5, 0xd5, 0x39, 0xac, 0x05, 0x10, 0x14, + 0x4a, 0xe7, 0xe7, 0x3c, 0x3f, 0xb7, 0xbb, 0xed, + 0x01, 0x6e, 0xcd, 0xee, 0x81, 0xb4, 0x62, 0xf4, + 0x62, 0x16, 0xff, 0x20, 0xb4, 0xf0, 0xbc, 0xff, + 0x7d, 0xd9, 0xcf, 0x95, 0x30, 0x27, 0xe0, 0x2f, + 0x98, 0x53, 0x80, 0x15, 0x13, 0xef, 0x44, 0x58, + 0x12, 0x16, 0xdb, 0x11, 0xef, 0x73, 0x51, 0xcd, + 0x42, 0x3f, 0x98, 0x6c, 0xc9, 0x68, 0xc3, 0xf4, + 0x5b, 0x0f, 0x5d, 0x77, 0xed, 0xdf, 0x0f, 0xff, + 0xb8, 0x69, 0x98, 0x50, 0x77, 0x7a, 0xe8, 0x90, + 0x27, 0x46, 0x10, 0xd2, 0xb5, 0x00, 0x3b, 0x36, + 0x43, 0x6d, 0x67, 0x41, 0x20, 0x3a, 0x32, 0xe0, + 0x2e, 0x5a, 0xfb, 0x4e, 0x4f, 0xa4, 0xf7, 0xc2, + 0xe6, 0x81, 0x1a, 0x51, 0xa8, 0x7c, 0xd4, 0x60, + 0x7c, 0x45, 0xe2, 0xba, 0x5b, 0x42, 0xf3, 0xbf, + 0x28, 0xaa, 0xf2, 0x90, 0xe4, 0x94, 0xdd, 0xaa, + 0x22, 0xd3, 0x71, 0x33, 0xa1, 0x01, 0x43, 0x0e, + 0xfa, 0x46, 0xd2, 0x6e, 0x55, 0x5e, 0x49, 0xeb, + 0x94, 0xf0, 0xb0, 0xb1, 0x2e, 0xf2, 0x3d, 0x6c, + 0x00, 0x5e, 0x01, 0x56, 0x3b, 0xfd, 0x5b, 0xa1, + 0x2f, 0x63, 0x1d, 0xbf, 0xf9, 0xd8, 0x13, 0xf7, + 0x4d, 0xb7, 0x1e, 0x3d, 0x98, 0xd2, 0xee, 0xb8, + 0x48, 0xc8, 0x5b, 0x91, 0x0f, 0x54, 0x9e, 0x26, + 0xb2, 0xc7, 0x3a, 0x6c, 0x8a, 0x35, 0xe1, 0xba +}; + +#endif // PROCESSOR_SYNTH_MINIDUMP_UNITTEST_DATA_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/test_assembler.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/test_assembler.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/test_assembler.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/test_assembler.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,357 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// test_assembler.cc: Implementation of google_breakpad::TestAssembler. +// See test_assembler.h for details. + +#include +#include +#include + +#include "processor/test_assembler.h" + +namespace google_breakpad { +namespace TestAssembler { + +using std::back_insert_iterator; + +Label::Label() : value_(new Binding()) { } +Label::Label(u_int64_t value) : value_(new Binding(value)) { } +Label::Label(const Label &label) { + value_ = label.value_; + value_->Acquire(); +} +Label::~Label() { + if (value_->Release()) delete value_; +} + +Label &Label::operator=(u_int64_t value) { + value_->Set(NULL, value); + return *this; +} + +Label &Label::operator=(const Label &label) { + value_->Set(label.value_, 0); + return *this; +} + +Label Label::operator+(u_int64_t addend) const { + Label l; + l.value_->Set(this->value_, addend); + return l; +} + +Label Label::operator-(u_int64_t subtrahend) const { + Label l; + l.value_->Set(this->value_, -subtrahend); + return l; +} + +// When NDEBUG is #defined, assert doesn't evaluate its argument. This +// means you can't simply use assert to check the return value of a +// function with necessary side effects. +// +// ALWAYS_EVALUATE_AND_ASSERT(x) evaluates x regardless of whether +// NDEBUG is #defined; when NDEBUG is not #defined, it further asserts +// that x is true. +#ifdef NDEBUG +#define ALWAYS_EVALUATE_AND_ASSERT(x) x +#else +#define ALWAYS_EVALUATE_AND_ASSERT(x) assert(x) +#endif + +u_int64_t Label::operator-(const Label &label) const { + u_int64_t offset; + ALWAYS_EVALUATE_AND_ASSERT(IsKnownOffsetFrom(label, &offset)); + return offset; +} + +u_int64_t Label::Value() const { + u_int64_t v; + ALWAYS_EVALUATE_AND_ASSERT(IsKnownConstant(&v)); + return v; +}; + +bool Label::IsKnownConstant(u_int64_t *value_p) const { + Binding *base; + u_int64_t addend; + value_->Get(&base, &addend); + if (base != NULL) return false; + if (value_p) *value_p = addend; + return true; +} + +bool Label::IsKnownOffsetFrom(const Label &label, u_int64_t *offset_p) const +{ + Binding *label_base, *this_base; + u_int64_t label_addend, this_addend; + label.value_->Get(&label_base, &label_addend); + value_->Get(&this_base, &this_addend); + // If this and label are related, Get will find their final + // common ancestor, regardless of how indirect the relation is. This + // comparison also handles the constant vs. constant case. + if (this_base != label_base) return false; + if (offset_p) *offset_p = this_addend - label_addend; + return true; +} + +Label::Binding::Binding() : base_(this), addend_(), reference_count_(1) { } + +Label::Binding::Binding(u_int64_t addend) + : base_(NULL), addend_(addend), reference_count_(1) { } + +Label::Binding::~Binding() { + assert(reference_count_ == 0); + if (base_ && base_ != this && base_->Release()) + delete base_; +} + +void Label::Binding::Set(Binding *binding, u_int64_t addend) { + if (!base_ && !binding) { + // We're equating two constants. This could be okay. + assert(addend_ == addend); + } else if (!base_) { + // We are a known constant, but BINDING may not be, so turn the + // tables and try to set BINDING's value instead. + binding->Set(NULL, addend_ - addend); + } else { + if (binding) { + // Find binding's final value. Since the final value is always either + // completely unconstrained or a constant, never a reference to + // another variable (otherwise, it wouldn't be final), this + // guarantees we won't create cycles here, even for code like this: + // l = m, m = n, n = l; + u_int64_t binding_addend; + binding->Get(&binding, &binding_addend); + addend += binding_addend; + } + + // It seems likely that setting a binding to itself is a bug + // (although I can imagine this might turn out to be helpful to + // permit). + assert(binding != this); + + if (base_ != this) { + // Set the other bindings on our chain as well. Note that this + // is sufficient even though binding relationships form trees: + // All binding operations traverse their chains to the end, and + // all bindings related to us share some tail of our chain, so + // they will see the changes we make here. + base_->Set(binding, addend - addend_); + // We're not going to use base_ any more. + if (base_->Release()) delete base_; + } + + // Adopt BINDING as our base. Note that it should be correct to + // acquire here, after the release above, even though the usual + // reference-counting rules call for acquiring first, and then + // releasing: the self-reference assertion above should have + // complained if BINDING were 'this' or anywhere along our chain, + // so we didn't release BINDING. + if (binding) binding->Acquire(); + base_ = binding; + addend_ = addend; + } +} + +void Label::Binding::Get(Binding **base, u_int64_t *addend) { + if (base_ && base_ != this) { + // Recurse to find the end of our reference chain (the root of our + // tree), and then rewrite every binding along the chain to refer + // to it directly, adjusting addends appropriately. (This is why + // this member function isn't this-const.) + Binding *final_base; + u_int64_t final_addend; + base_->Get(&final_base, &final_addend); + if (final_base) final_base->Acquire(); + if (base_->Release()) delete base_; + base_ = final_base; + addend_ += final_addend; + } + *base = base_; + *addend = addend_; +} + +template +static inline void InsertEndian(TestAssembler::Endianness endianness, + size_t size, u_int64_t number, Inserter dest) { + if (endianness == kLittleEndian) { + for (size_t i = 0; i < size; i++) { + *dest++ = (char) (number & 0xff); + number >>= 8; + } + } else { + assert(endianness == kBigEndian); + // The loop condition is odd, but it's correct for size_t. + for (size_t i = size - 1; i < size; i--) + *dest++ = (char) ((number >> (i * 8)) & 0xff); + } +} + +Section &Section::Append(Endianness endianness, size_t size, u_int64_t number) { + InsertEndian(endianness, size, number, + back_insert_iterator(contents_)); + return *this; +} + +Section &Section::Append(Endianness endianness, size_t size, + const Label &label) { + // If this label's value is known, there's no reason to waste an + // entry in references_ on it. + u_int64_t value; + if (label.IsKnownConstant(&value)) + return Append(endianness, size, value); + + // This will get caught when the references are resolved, but it's + // nicer to find out earlier. + assert(endianness != kUnsetEndian); + + references_.push_back(Reference(contents_.size(), endianness, size, label)); + contents_.append(size, 0); + return *this; +} + +#define ENDIANNESS_L kLittleEndian +#define ENDIANNESS_B kBigEndian +#define ENDIANNESS(e) ENDIANNESS_ ## e + +#define DEFINE_SHORT_APPEND_NUMBER_ENDIAN(e, bits) \ + Section &Section::e ## bits(u_int ## bits ## _t v) { \ + InsertEndian(ENDIANNESS(e), bits / 8, v, \ + back_insert_iterator(contents_)); \ + return *this; \ + } + +#define DEFINE_SHORT_APPEND_LABEL_ENDIAN(e, bits) \ + Section &Section::e ## bits(const Label &v) { \ + return Append(ENDIANNESS(e), bits / 8, v); \ + } + +// Define L16, B32, and friends. +#define DEFINE_SHORT_APPEND_ENDIAN(e, bits) \ + DEFINE_SHORT_APPEND_NUMBER_ENDIAN(e, bits) \ + DEFINE_SHORT_APPEND_LABEL_ENDIAN(e, bits) + +DEFINE_SHORT_APPEND_LABEL_ENDIAN(L, 8); +DEFINE_SHORT_APPEND_LABEL_ENDIAN(B, 8); +DEFINE_SHORT_APPEND_ENDIAN(L, 16); +DEFINE_SHORT_APPEND_ENDIAN(L, 32); +DEFINE_SHORT_APPEND_ENDIAN(L, 64); +DEFINE_SHORT_APPEND_ENDIAN(B, 16); +DEFINE_SHORT_APPEND_ENDIAN(B, 32); +DEFINE_SHORT_APPEND_ENDIAN(B, 64); + +#define DEFINE_SHORT_APPEND_NUMBER_DEFAULT(bits) \ + Section &Section::D ## bits(u_int ## bits ## _t v) { \ + InsertEndian(endianness_, bits / 8, v, \ + back_insert_iterator(contents_)); \ + return *this; \ + } +#define DEFINE_SHORT_APPEND_LABEL_DEFAULT(bits) \ + Section &Section::D ## bits(const Label &v) { \ + return Append(endianness_, bits / 8, v); \ + } +#define DEFINE_SHORT_APPEND_DEFAULT(bits) \ + DEFINE_SHORT_APPEND_NUMBER_DEFAULT(bits) \ + DEFINE_SHORT_APPEND_LABEL_DEFAULT(bits) + +DEFINE_SHORT_APPEND_LABEL_DEFAULT(8) +DEFINE_SHORT_APPEND_DEFAULT(16); +DEFINE_SHORT_APPEND_DEFAULT(32); +DEFINE_SHORT_APPEND_DEFAULT(64); + +Section &Section::Append(const Section §ion) { + size_t base = contents_.size(); + contents_.append(section.contents_); + for (vector::const_iterator it = section.references_.begin(); + it != section.references_.end(); it++) + references_.push_back(Reference(base + it->offset, it->endianness, + it->size, it->label)); + return *this; +} + +Section &Section::LEB128(long long value) { + while (value < -0x40 || 0x3f < value) { + contents_ += (value & 0x7f) | 0x80; + if (value < 0) + value = (value >> 7) | ~(((unsigned long long) -1) >> 7); + else + value = (value >> 7); + } + contents_ += value & 0x7f; + return *this; +} + +Section &Section::ULEB128(u_int64_t value) { + while (value > 0x7f) { + contents_ += (value & 0x7f) | 0x80; + value = (value >> 7); + } + contents_ += value; + return *this; +} + +Section &Section::Align(size_t alignment, u_int8_t pad_byte) { + // ALIGNMENT must be a power of two. + assert(((alignment - 1) & alignment) == 0); + size_t new_size = (contents_.size() + alignment - 1) & ~(alignment - 1); + contents_.append(new_size - contents_.size(), pad_byte); + assert((contents_.size() & (alignment - 1)) == 0); + return *this; +} + +void Section::Clear() { + contents_.clear(); + references_.clear(); +} + +bool Section::GetContents(string *contents) { + // For each label reference, find the label's value, and patch it into + // the section's contents. + for (size_t i = 0; i < references_.size(); i++) { + Reference &r = references_[i]; + u_int64_t value; + if (!r.label.IsKnownConstant(&value)) { + fprintf(stderr, "Undefined label #%d at offset 0x%x\n", i, r.offset); + return false; + } + assert(r.offset < contents_.size()); + assert(contents_.size() - r.offset >= r.size); + InsertEndian(r.endianness, r.size, value, contents_.begin() + r.offset); + } + contents->clear(); + std::swap(contents_, *contents); + references_.clear(); + return true; +} + +} // namespace TestAssembler +} // namespace google_breakpad diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/test_assembler.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/test_assembler.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/test_assembler.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/test_assembler.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,472 @@ +// -*- mode: C++ -*- + +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// test-assembler.h: interface to class for building complex binary streams. + +// To test the Breakpad symbol dumper and processor thoroughly, for +// all combinations of host system and minidump processor +// architecture, we need to be able to easily generate complex test +// data like debugging information and minidump files. +// +// For example, if we want our unit tests to provide full code +// coverage for stack walking, it may be difficult to persuade the +// compiler to generate every possible sort of stack walking +// information that we want to support; there are probably DWARF CFI +// opcodes that GCC never emits. Similarly, if we want to test our +// error handling, we will need to generate damaged minidumps or +// debugging information that (we hope) the client or compiler will +// never produce on its own. +// +// google_breakpad::TestAssembler provides a predictable and +// (relatively) simple way to generate complex formatted data streams +// like minidumps and CFI. Furthermore, because TestAssembler is +// portable, developers without access to (say) Visual Studio or a +// SPARC assembler can still work on test data for those targets. + +#ifndef PROCESSOR_TEST_ASSEMBLER_H_ +#define PROCESSOR_TEST_ASSEMBLER_H_ + +#include +#include +#include + +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +using std::list; +using std::string; +using std::vector; + +namespace TestAssembler { + +// A Label represents a value not yet known that we need to store in a +// section. As long as all the labels a section refers to are defined +// by the time we retrieve its contents as bytes, we can use undefined +// labels freely in that section's construction. +// +// A label can be in one of three states: +// - undefined, +// - defined as the sum of some other label and a constant, or +// - a constant. +// +// A label's value never changes, but it can accumulate constraints. +// Adding labels and integers is permitted, and yields a label. +// Subtracting a constant from a label is permitted, and also yields a +// label. Subtracting two labels that have some relationship to each +// other is permitted, and yields a constant. +// +// For example: +// +// Label a; // a's value is undefined +// Label b; // b's value is undefined +// { +// Label c = a + 4; // okay, even though a's value is unknown +// b = c + 4; // also okay; b is now a+8 +// } +// Label d = b - 2; // okay; d == a+6, even though c is gone +// d.Value(); // error: d's value is not yet known +// d - a; // is 6, even though their values are not known +// a = 12; // now b == 20, and d == 18 +// d.Value(); // 18: no longer an error +// b.Value(); // 20 +// d = 10; // error: d is already defined. +// +// Label objects' lifetimes are unconstrained: notice that, in the +// above example, even though a and b are only related through c, and +// c goes out of scope, the assignment to a sets b's value as well. In +// particular, it's not necessary to ensure that a Label lives beyond +// Sections that refer to it. +class Label { + public: + Label(); // An undefined label. + Label(u_int64_t value); // A label with a fixed value + Label(const Label &value); // A label equal to another. + ~Label(); + + // Return this label's value; it must be known. + // + // Providing this as a cast operator is nifty, but the conversions + // happen in unexpected places. In particular, ISO C++ says that + // Label + size_t becomes ambigious, because it can't decide whether + // to convert the Label to a u_int64_t and then to a size_t, or use + // the overloaded operator that returns a new label, even though the + // former could fail if the label is not yet defined and the latter won't. + u_int64_t Value() const; + + Label &operator=(u_int64_t value); + Label &operator=(const Label &value); + Label operator+(u_int64_t addend) const; + Label operator-(u_int64_t subtrahend) const; + u_int64_t operator-(const Label &subtrahend) const; + + // We could also provide == and != that work on undefined, but + // related, labels. + + // Return true if this label's value is known. If VALUE_P is given, + // set *VALUE_P to the known value if returning true. + bool IsKnownConstant(u_int64_t *value_p = NULL) const; + + // Return true if the offset from LABEL to this label is known. If + // OFFSET_P is given, set *OFFSET_P to the offset when returning true. + // + // You can think of l.KnownOffsetFrom(m, &d) as being like 'd = l-m', + // except that it also returns a value indicating whether the + // subtraction is possible given what we currently know of l and m. + // It can be possible even if we don't know l and m's values. For + // example: + // + // Label l, m; + // m = l + 10; + // l.IsKnownConstant(); // false + // m.IsKnownConstant(); // false + // u_int64_t d; + // l.IsKnownOffsetFrom(m, &d); // true, and sets d to -10. + // l-m // -10 + // m-l // 10 + // m.Value() // error: m's value is not known + bool IsKnownOffsetFrom(const Label &label, u_int64_t *offset_p = NULL) const; + + private: + // A label's value, or if that is not yet known, how the value is + // related to other labels' values. A binding may be: + // - a known constant, + // - constrained to be equal to some other binding plus a constant, or + // - unconstrained, and free to take on any value. + // + // Many labels may point to a single binding, and each binding may + // refer to another, so bindings and labels form trees whose leaves + // are labels, whose interior nodes (and roots) are bindings, and + // where links point from children to parents. Bindings are + // reference counted, allowing labels to be lightweight, copyable, + // assignable, placed in containers, and so on. + class Binding { + public: + Binding(); + Binding(u_int64_t addend); + ~Binding(); + + // Increment our reference count. + void Acquire() { reference_count_++; }; + // Decrement our reference count, and return true if it is zero. + bool Release() { return --reference_count_ == 0; } + + // Set this binding to be equal to BINDING + ADDEND. If BINDING is + // NULL, then set this binding to the known constant ADDEND. + // Update every binding on this binding's chain to point directly + // to BINDING, or to be a constant, with addends adjusted + // appropriately. + void Set(Binding *binding, u_int64_t value); + + // Return what we know about the value of this binding. + // - If this binding's value is a known constant, set BASE to + // NULL, and set ADDEND to its value. + // - If this binding is not a known constant but related to other + // bindings, set BASE to the binding at the end of the relation + // chain (which will always be unconstrained), and set ADDEND to the + // value to add to that binding's value to get this binding's + // value. + // - If this binding is unconstrained, set BASE to this, and leave + // ADDEND unchanged. + void Get(Binding **base, u_int64_t *addend); + + private: + // There are three cases: + // + // - A binding representing a known constant value has base_ NULL, + // and addend_ equal to the value. + // + // - A binding representing a completely unconstrained value has + // base_ pointing to this; addend_ is unused. + // + // - A binding whose value is related to some other binding's + // value has base_ pointing to that other binding, and addend_ + // set to the amount to add to that binding's value to get this + // binding's value. We only represent relationships of the form + // x = y+c. + // + // Thus, the bind_ links form a chain terminating in either a + // known constant value or a completely unconstrained value. Most + // operations on bindings do path compression: they change every + // binding on the chain to point directly to the final value, + // adjusting addends as appropriate. + Binding *base_; + u_int64_t addend_; + + // The number of Labels and Bindings pointing to this binding. + // (When a binding points to itself, indicating a completely + // unconstrained binding, that doesn't count as a reference.) + int reference_count_; + }; + + // This label's value. + Binding *value_; +}; + +inline Label operator+(u_int64_t a, const Label &l) { return l + a; } +// Note that int-Label isn't defined, as negating a Label is not an +// operation we support. + +// Conventions for representing larger numbers as sequences of bytes. +enum Endianness { + kBigEndian, // Big-endian: the most significant byte comes first. + kLittleEndian, // Little-endian: the least significant byte comes first. + kUnsetEndian, // used internally +}; + +// A section is a sequence of bytes, constructed by appending bytes +// to the end. Sections have a convenient and flexible set of member +// functions for appending data in various formats: big-endian and +// little-endian signed and unsigned values of different sizes; +// LEB128 and ULEB128 values (see below), and raw blocks of bytes. +// +// If you need to append a value to a section that is not convenient +// to compute immediately, you can create a label, append the +// label's value to the section, and then set the label's value +// later, when it's convenient to do so. Once a label's value is +// known, the section class takes care of updating all previously +// appended references to it. +// +// Once all the labels to which a section refers have had their +// values determined, you can get a copy of the section's contents +// as a string. +// +// Note that there is no specified "start of section" label. This is +// because there are typically several different meanings for "the +// start of a section": the offset of the section within an object +// file, the address in memory at which the section's content appear, +// and so on. It's up to the code that uses the Section class to +// keep track of these explicitly, as they depend on the application. +class Section { + public: + Section(Endianness endianness = kUnsetEndian) + : endianness_(endianness) { }; + ~Section() { }; + + // Set the default endianness of this section to ENDIANNESS. This + // sets the behavior of the D appending functions. If the + // assembler's default endianness was set, this is the + void set_endianness(Endianness endianness) { + endianness_ = endianness; + } + + // Return the default endianness of this section. + Endianness endianness() const { return endianness_; } + + // Append the SIZE bytes at DATA or the contents of STRING to the + // end of this section. Return a reference to this section. + Section &Append(const u_int8_t *data, size_t size) { + contents_.append(reinterpret_cast(data), size); + return *this; + }; + Section &Append(const string &data) { + contents_.append(data); + return *this; + }; + + // Append SIZE copies of BYTE to the end of this section. Return a + // reference to this section. + Section &Append(size_t size, u_int8_t byte) { + contents_.append(size, (char) byte); + return *this; + } + + // Append NUMBER to this section. ENDIANNESS is the endianness to + // use to write the number. SIZE is the length of the number in + // bytes. Return a reference to this section. + Section &Append(Endianness endianness, size_t size, u_int64_t number); + Section &Append(Endianness endianness, size_t size, const Label &label); + + // Append SECTION to the end of this section. The labels SECTION + // refers to need not be defined yet. + // + // Note that this has no effect on any Labels' values, or on + // SECTION. If placing SECTION within 'this' provides new + // constraints on existing labels' values, then it's up to the + // caller to fiddle with those labels as needed. + Section &Append(const Section §ion); + + // Append the contents of DATA as a series of bytes terminated by + // a NULL character. + Section &AppendCString(const string &data) { + Append(data); + contents_ += '\0'; + return *this; + } + + // Append VALUE or LABEL to this section, with the given bit width and + // endianness. Return a reference to this section. + // + // The names of these functions have the form : + // is either 'L' (little-endian, least significant byte first), + // 'B' (big-endian, most significant byte first), or + // 'D' (default, the section's default endianness) + // is 8, 16, 32, or 64. + // + // Since endianness doesn't matter for a single byte, all the + // =8 functions are equivalent. + // + // These can be used to write both signed and unsigned values, as + // the compiler will properly sign-extend a signed value before + // passing it to the function, at which point the function's + // behavior is the same either way. + Section &L8(u_int8_t value) { contents_ += value; return *this; } + Section &B8(u_int8_t value) { contents_ += value; return *this; } + Section &D8(u_int8_t value) { contents_ += value; return *this; } + Section &L16(u_int16_t), &L32(u_int32_t), &L64(u_int64_t), + &B16(u_int16_t), &B32(u_int32_t), &B64(u_int64_t), + &D16(u_int16_t), &D32(u_int32_t), &D64(u_int64_t); + Section &L8(const Label &label), &L16(const Label &label), + &L32(const Label &label), &L64(const Label &label), + &B8(const Label &label), &B16(const Label &label), + &B32(const Label &label), &B64(const Label &label), + &D8(const Label &label), &D16(const Label &label), + &D32(const Label &label), &D64(const Label &label); + + // Append VALUE in a signed LEB128 (Little-Endian Base 128) form. + // + // The signed LEB128 representation of an integer N is a variable + // number of bytes: + // + // - If N is between -0x40 and 0x3f, then its signed LEB128 + // representation is a single byte whose value is N. + // + // - Otherwise, its signed LEB128 representation is (N & 0x7f) | + // 0x80, followed by the signed LEB128 representation of N / 128, + // rounded towards negative infinity. + // + // In other words, we break VALUE into groups of seven bits, put + // them in little-endian order, and then write them as eight-bit + // bytes with the high bit on all but the last. + // + // Note that VALUE cannot be a Label (we would have to implement + // relaxation). + Section &LEB128(long long value); + + // Append VALUE in unsigned LEB128 (Little-Endian Base 128) form. + // + // The unsigned LEB128 representation of an integer N is a variable + // number of bytes: + // + // - If N is between 0 and 0x7f, then its unsigned LEB128 + // representation is a single byte whose value is N. + // + // - Otherwise, its unsigned LEB128 representation is (N & 0x7f) | + // 0x80, followed by the unsigned LEB128 representation of N / + // 128, rounded towards negative infinity. + // + // Note that VALUE cannot be a Label (we would have to implement + // relaxation). + Section &ULEB128(u_int64_t value); + + // Jump to the next location aligned on an ALIGNMENT-byte boundary, + // relative to the start of the section. Fill the gap with PAD_BYTE. + // ALIGNMENT must be a power of two. Return a reference to this + // section. + Section &Align(size_t alignment, u_int8_t pad_byte = 0); + + // Clear the contents of this section. + void Clear(); + + // Return the current size of the section. + size_t Size() const { return contents_.size(); } + + // Return a label representing the start of the section. + // + // It is up to the user whether this label represents the section's + // position in an object file, the section's address in memory, or + // what have you; some applications may need both, in which case + // this simple-minded interface won't be enough. This class only + // provides a single start label, for use with the Here and Mark + // member functions. + // + // Ideally, we'd provide this in a subclass that actually knows more + // about the application at hand and can provide an appropriate + // collection of start labels. But then the appending member + // functions like Append and D32 would return a reference to the + // base class, not the derived class, and the chaining won't work. + // Since the only value here is in pretty notation, that's a fatal + // flaw. + Label start() const { return start_; } + + // Return a label representing the point at which the next Appended + // item will appear in the section, relative to start(). + Label Here() const { return start_ + Size(); } + + // Set *LABEL to Here, and return a reference to this section. + Section &Mark(Label *label) { *label = Here(); return *this; } + + // If there are no undefined label references left in this + // section, set CONTENTS to the contents of this section, as a + // string, and clear this section. Return true on success, or false + // if there were still undefined labels. + bool GetContents(string *contents); + + private: + // Used internally. A reference to a label's value. + struct Reference { + Reference(size_t set_offset, Endianness set_endianness, size_t set_size, + const Label &set_label) + : offset(set_offset), endianness(set_endianness), size(set_size), + label(set_label) { } + + // The offset of the reference within the section. + size_t offset; + + // The endianness of the reference. + Endianness endianness; + + // The size of the reference. + size_t size; + + // The label to which this is a reference. + Label label; + }; + + // The default endianness of this section. + Endianness endianness_; + + // The contents of the section. + string contents_; + + // References to labels within those contents. + vector references_; + + // A label referring to the beginning of the section. + Label start_; +}; + +} // namespace TestAssembler +} // namespace google_breakpad + +#endif // PROCESSOR_TEST_ASSEMBLER_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/test_assembler_unittest.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/test_assembler_unittest.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/test_assembler_unittest.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/test_assembler_unittest.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,1644 @@ +// Copyright (c) 2010, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// test_assembler_unittest.cc: Unit tests for google_breakpad::TestAssembler. + +#include +#include + +#include "breakpad_googletest_includes.h" +#include "processor/test_assembler.h" + +using google_breakpad::TestAssembler::Label; +using google_breakpad::TestAssembler::Section; +using google_breakpad::TestAssembler::kBigEndian; +using google_breakpad::TestAssembler::kLittleEndian; +using std::string; +using testing::Test; + +TEST(ConstructLabel, Simple) { + Label l; +} + +TEST(ConstructLabel, Undefined) { + Label l; + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(ConstructLabelDeathTest, Undefined) { + Label l; + ASSERT_DEATH(l.Value(), "IsKnownConstant\\(&v\\)"); +} + +TEST(ConstructLabel, Constant) { + Label l(0x060b9f974eaf301eULL); + u_int64_t v; + EXPECT_TRUE(l.IsKnownConstant(&v)); + EXPECT_EQ(v, 0x060b9f974eaf301eULL); + EXPECT_EQ(l.Value(), 0x060b9f974eaf301eULL); +} + +TEST(ConstructLabel, Copy) { + Label l; + Label m(l); + u_int64_t v; + EXPECT_TRUE(l.IsKnownOffsetFrom(m, &v)); + EXPECT_EQ(0U, v); +} + +// The left-hand-side of a label assignment can be either +// unconstrained, related, or known. The right-hand-side can be any of +// those, or an integer. +TEST(Assignment, UnconstrainedToUnconstrained) { + Label l, m; + l = m; + EXPECT_EQ(0U, l-m); + EXPECT_TRUE(l.IsKnownOffsetFrom(m)); + u_int64_t d; + EXPECT_TRUE(l.IsKnownOffsetFrom(m, &d)); + EXPECT_EQ(0U, d); + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(Assignment, UnconstrainedToRelated) { + Label l, m, n; + l = n; + l = m; + EXPECT_EQ(0U, l-m); + EXPECT_TRUE(l.IsKnownOffsetFrom(m)); + u_int64_t d; + EXPECT_TRUE(l.IsKnownOffsetFrom(m, &d)); + EXPECT_EQ(0U, d); + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(Assignment, UnconstrainedToKnown) { + Label l, m; + l = 0x8fd16e55b20a39c1ULL; + l = m; + EXPECT_EQ(0U, l-m); + EXPECT_TRUE(l.IsKnownOffsetFrom(m)); + u_int64_t d; + EXPECT_TRUE(l.IsKnownOffsetFrom(m, &d)); + EXPECT_EQ(0U, d); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x8fd16e55b20a39c1ULL, m.Value()); +} + +TEST(Assignment, RelatedToUnconstrained) { + Label l, m, n; + m = n; + l = m; + EXPECT_EQ(0U, l-n); + EXPECT_TRUE(l.IsKnownOffsetFrom(n)); + u_int64_t d; + EXPECT_TRUE(l.IsKnownOffsetFrom(n, &d)); + EXPECT_EQ(0U, d); + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(Assignment, RelatedToRelated) { + Label l, m, n, o; + l = n; + m = o; + l = m; + EXPECT_EQ(0U, n-o); + EXPECT_TRUE(n.IsKnownOffsetFrom(o)); + u_int64_t d; + EXPECT_TRUE(n.IsKnownOffsetFrom(o, &d)); + EXPECT_EQ(0U, d); + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(Assignment, RelatedToKnown) { + Label l, m, n; + m = n; + l = 0xd2011f8c82ad56f2ULL; + l = m; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0xd2011f8c82ad56f2ULL, l.Value()); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0xd2011f8c82ad56f2ULL, m.Value()); + EXPECT_TRUE(n.IsKnownConstant()); + EXPECT_EQ(0xd2011f8c82ad56f2ULL, n.Value()); +} + +TEST(Assignment, KnownToUnconstrained) { + Label l, m; + m = 0x50b024c0d6073887ULL; + l = m; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0x50b024c0d6073887ULL, l.Value()); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x50b024c0d6073887ULL, m.Value()); +} + +TEST(Assignment, KnownToRelated) { + Label l, m, n; + l = n; + m = 0x5348883655c727e5ULL; + l = m; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0x5348883655c727e5ULL, l.Value()); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x5348883655c727e5ULL, m.Value()); + EXPECT_TRUE(n.IsKnownConstant()); + EXPECT_EQ(0x5348883655c727e5ULL, n.Value()); +} + +TEST(Assignment, KnownToKnown) { + Label l, m; + l = 0x36c209c20987564eULL; + m = 0x36c209c20987564eULL; + l = m; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0x36c209c20987564eULL, l.Value()); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x36c209c20987564eULL, m.Value()); +} + +TEST(Assignment, ConstantToUnconstrained) { + Label l; + l = 0xc02495f4d7f5a957ULL; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0xc02495f4d7f5a957ULL, l.Value()); +} + +TEST(Assignment, ConstantToRelated) { + Label l, m; + l = m; + l = 0x4577901cf275488dULL; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0x4577901cf275488dULL, l.Value()); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x4577901cf275488dULL, m.Value()); +} + +TEST(Assignment, ConstantToKnown) { + Label l; + l = 0xec0b9c369b7e8ea7ULL; + l = 0xec0b9c369b7e8ea7ULL; + EXPECT_TRUE(l.IsKnownConstant()); + EXPECT_EQ(0xec0b9c369b7e8ea7ULL, l.Value()); +} + +TEST(AssignmentDeathTest, Self) { + Label l; + ASSERT_DEATH(l = l, "binding != this"); +} + +TEST(AssignmentDeathTest, IndirectCycle) { + Label l, m, n; + l = m; + m = n; + ASSERT_DEATH(n = l, "binding != this"); +} + +TEST(AssignmentDeathTest, Cycle) { + Label l, m, n, o; + l = m; + m = n; + o = n; + ASSERT_DEATH(o = l, "binding != this"); +} + +TEST(Addition, LabelConstant) { + Label l, m; + m = l + 0x5248d93e8bbe9497ULL; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + u_int64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(0x5248d93e8bbe9497ULL, d); + EXPECT_FALSE(m.IsKnownConstant()); +} + +TEST(Addition, ConstantLabel) { + Label l, m; + m = 0xf51e94e00d6e3c84ULL + l; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + u_int64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(0xf51e94e00d6e3c84ULL, d); + EXPECT_FALSE(m.IsKnownConstant()); +} + +TEST(Addition, KnownLabelConstant) { + Label l, m; + l = 0x16286307042ce0d8ULL; + m = l + 0x3fdddd91306719d7ULL; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + u_int64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(0x3fdddd91306719d7ULL, d); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x16286307042ce0d8ULL + 0x3fdddd91306719d7ULL, m.Value()); +} + +TEST(Addition, ConstantKnownLabel) { + Label l, m; + l = 0x50f62d0cdd1031deULL; + m = 0x1b13462d8577c538ULL + l; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + u_int64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(0x1b13462d8577c538ULL, d); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x50f62d0cdd1031deULL + 0x1b13462d8577c538ULL, m.Value()); +} + +TEST(Subtraction, LabelConstant) { + Label l, m; + m = l - 0x0620884d21d3138eULL; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + u_int64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(-0x0620884d21d3138eULL, d); + EXPECT_FALSE(m.IsKnownConstant()); +} + +TEST(Subtraction, KnownLabelConstant) { + Label l, m; + l = 0x6237fbaf9ef7929eULL; + m = l - 0x317730995d2ab6eeULL; + EXPECT_TRUE(m.IsKnownOffsetFrom(l)); + u_int64_t d; + EXPECT_TRUE(m.IsKnownOffsetFrom(l, &d)); + EXPECT_EQ(-0x317730995d2ab6eeULL, d); + EXPECT_TRUE(m.IsKnownConstant()); + EXPECT_EQ(0x6237fbaf9ef7929eULL - 0x317730995d2ab6eeULL, m.Value()); +} + +TEST(SubtractionDeathTest, LabelLabel) { + Label l, m; + ASSERT_DEATH(l - m, "IsKnownOffsetFrom\\(label, &offset\\)"); +} + +TEST(Subtraction, LabelLabel) { + Label l, m; + l = m + 0x7fa77ec63e28a17aULL; + EXPECT_EQ(0x7fa77ec63e28a17aULL, l - m); + EXPECT_EQ(-0x7fa77ec63e28a17aULL, m - l); +} + +TEST(IsKnownConstant, Undefined) { + Label l; + EXPECT_FALSE(l.IsKnownConstant()); +} + +TEST(IsKnownConstant, RelatedLabel) { + Label l, m; + l = m; + EXPECT_FALSE(l.IsKnownConstant()); + EXPECT_FALSE(m.IsKnownConstant()); +} + +TEST(IsKnownConstant, Constant) { + Label l; + l = 0xf374b1bdd6a22576ULL; + EXPECT_TRUE(l.IsKnownConstant()); +} + +TEST(IsKnownOffsetFrom, Unrelated) { + Label l, m; + EXPECT_FALSE(l.IsKnownOffsetFrom(m)); +} + +TEST(IsKnownOffsetFrom, Related) { + Label l, m; + l = m; + EXPECT_TRUE(l.IsKnownOffsetFrom(m)); +} + +// Test the construction of chains of related labels, and the +// propagation of values through them. +// +// Although the relations between labels are supposed to behave +// symmetrically --- that is, 'a = b' should put a and b in +// indistinguishable states --- there's a distinction made internally +// between the target (a) and the source (b). +// +// So there are five test axes to cover: +// +// - Do we construct the chain with assignment ("Assign") or with constructors +// ("Construct")? +// +// - Do we set the value of the label at the start of the chain +// ("Start") or the label at the end ("End")? +// +// - Are we testing the propagation of a relationship between variable +// values ("Relation"), or the propagation of a known constant value +// ("Value")? +// +// - Do we set the value before building the chain ("Before") or after +// the chain has been built ("After")? +// +// - Do we add new relationships to the end of the existing chain +// ("Forward") or to the beginning ("Backward")? +// +// Of course, "Construct" and "Backward" can't be combined, which +// eliminates eight combinations, and "Construct", "End", and "Before" +// can't be combined, which eliminates two more, so there are are 22 +// combinations, not 32. + +TEST(LabelChain, AssignStartRelationBeforeForward) { + Label a, b, c, d; + Label x; + a = x; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, AssignStartRelationBeforeBackward) { + Label a, b, c, d; + Label x; + a = x; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, AssignStartRelationAfterForward) { + Label a, b, c, d; + Label x; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + a = x; + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, AssignStartRelationAfterBackward) { + Label a, b, c, d; + Label x; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + a = x; + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, AssignStartValueBeforeForward) { + Label a, b, c, d; + a = 0xa131200190546ac2ULL; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + EXPECT_EQ(0xa131200190546ac2ULL + 0x111U, d.Value()); + EXPECT_EQ(0xa131200190546ac2ULL + 0x11U, c.Value()); + EXPECT_EQ(0xa131200190546ac2ULL + 0x1U, b.Value()); + EXPECT_EQ(0xa131200190546ac2ULL + 0U, a.Value()); +} + +TEST(LabelChain, AssignStartValueBeforeBackward) { + Label a, b, c, d; + a = 0x8da17e1670ad4fa2ULL; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + EXPECT_EQ(0x8da17e1670ad4fa2ULL + 0x111U, d.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL + 0x11U, c.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL + 0x1U, b.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL + 0U, a.Value()); +} + +TEST(LabelChain, AssignStartValueAfterForward) { + Label a, b, c, d; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + a = 0x99b8f51bafd41adaULL; + EXPECT_EQ(0x99b8f51bafd41adaULL + 0x111U, d.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL + 0x11U, c.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL + 0x1U, b.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL + 0U, a.Value()); +} + +TEST(LabelChain, AssignStartValueAfterBackward) { + Label a, b, c, d; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + a = 0xc86ca1d97ab5df6eULL; + EXPECT_EQ(0xc86ca1d97ab5df6eULL + 0x111U, d.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL + 0x11U, c.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL + 0x1U, b.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL + 0U, a.Value()); +} + +TEST(LabelChain, AssignEndRelationBeforeForward) { + Label a, b, c, d; + Label x; + x = d; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + EXPECT_EQ(-(u_int64_t)0x111U, a-x); + EXPECT_EQ(-(u_int64_t)0x110U, b-x); + EXPECT_EQ(-(u_int64_t)0x100U, c-x); + EXPECT_EQ(-(u_int64_t)0U, d-x); +} + +TEST(LabelChain, AssignEndRelationBeforeBackward) { + Label a, b, c, d; + Label x; + x = d; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + EXPECT_EQ(-(u_int64_t)0x111U, a-x); + EXPECT_EQ(-(u_int64_t)0x110U, b-x); + EXPECT_EQ(-(u_int64_t)0x100U, c-x); + EXPECT_EQ(-(u_int64_t)0U, d-x); +} + +TEST(LabelChain, AssignEndRelationAfterForward) { + Label a, b, c, d; + Label x; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + x = d; + EXPECT_EQ(-(u_int64_t)0x111U, a-x); + EXPECT_EQ(-(u_int64_t)0x110U, b-x); + EXPECT_EQ(-(u_int64_t)0x100U, c-x); + EXPECT_EQ(-(u_int64_t)0x000U, d-x); +} + +TEST(LabelChain, AssignEndRelationAfterBackward) { + Label a, b, c, d; + Label x; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + x = d; + EXPECT_EQ(-(u_int64_t)0x111U, a-x); + EXPECT_EQ(-(u_int64_t)0x110U, b-x); + EXPECT_EQ(-(u_int64_t)0x100U, c-x); + EXPECT_EQ(-(u_int64_t)0x000U, d-x); +} + +TEST(LabelChain, AssignEndValueBeforeForward) { + Label a, b, c, d; + d = 0xa131200190546ac2ULL; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + EXPECT_EQ(0xa131200190546ac2ULL - 0x111, a.Value()); + EXPECT_EQ(0xa131200190546ac2ULL - 0x110, b.Value()); + EXPECT_EQ(0xa131200190546ac2ULL - 0x100, c.Value()); + EXPECT_EQ(0xa131200190546ac2ULL - 0x000, d.Value()); +} + +TEST(LabelChain, AssignEndValueBeforeBackward) { + Label a, b, c, d; + d = 0x8da17e1670ad4fa2ULL; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + EXPECT_EQ(0x8da17e1670ad4fa2ULL - 0x111, a.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL - 0x110, b.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL - 0x100, c.Value()); + EXPECT_EQ(0x8da17e1670ad4fa2ULL - 0x000, d.Value()); +} + +TEST(LabelChain, AssignEndValueAfterForward) { + Label a, b, c, d; + b = a + 0x1; + c = b + 0x10; + d = c + 0x100; + d = 0x99b8f51bafd41adaULL; + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x111, a.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x110, b.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x100, c.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x000, d.Value()); +} + +TEST(LabelChain, AssignEndValueAfterBackward) { + Label a, b, c, d; + d = c + 0x100; + c = b + 0x10; + b = a + 0x1; + d = 0xc86ca1d97ab5df6eULL; + EXPECT_EQ(0xc86ca1d97ab5df6eULL - 0x111, a.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL - 0x110, b.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL - 0x100, c.Value()); + EXPECT_EQ(0xc86ca1d97ab5df6eULL - 0x000, d.Value()); +} + +TEST(LabelChain, ConstructStartRelationBeforeForward) { + Label x; + Label a(x); + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, ConstructStartRelationAfterForward) { + Label x; + Label a; + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + a = x; + EXPECT_EQ(0x111U, d-x); + EXPECT_EQ(0x11U, c-x); + EXPECT_EQ(0x1U, b-x); + EXPECT_EQ(0U, a-x); +} + +TEST(LabelChain, ConstructStartValueBeforeForward) { + Label a(0x5d234d177d01ccc8ULL); + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + EXPECT_EQ(0x5d234d177d01ccc8ULL + 0x111U, d.Value()); + EXPECT_EQ(0x5d234d177d01ccc8ULL + 0x011U, c.Value()); + EXPECT_EQ(0x5d234d177d01ccc8ULL + 0x001U, b.Value()); + EXPECT_EQ(0x5d234d177d01ccc8ULL + 0x000U, a.Value()); +} + +TEST(LabelChain, ConstructStartValueAfterForward) { + Label a; + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + a = 0xded85d54586e84fcULL; + EXPECT_EQ(0xded85d54586e84fcULL + 0x111U, d.Value()); + EXPECT_EQ(0xded85d54586e84fcULL + 0x011U, c.Value()); + EXPECT_EQ(0xded85d54586e84fcULL + 0x001U, b.Value()); + EXPECT_EQ(0xded85d54586e84fcULL + 0x000U, a.Value()); +} + +TEST(LabelChain, ConstructEndRelationAfterForward) { + Label x; + Label a; + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + x = d; + EXPECT_EQ(-(u_int64_t)0x111U, a-x); + EXPECT_EQ(-(u_int64_t)0x110U, b-x); + EXPECT_EQ(-(u_int64_t)0x100U, c-x); + EXPECT_EQ(-(u_int64_t)0x000U, d-x); +} + +TEST(LabelChain, ConstructEndValueAfterForward) { + Label a; + Label b(a + 0x1); + Label c(b + 0x10); + Label d(c + 0x100); + d = 0x99b8f51bafd41adaULL; + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x111, a.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x110, b.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x100, c.Value()); + EXPECT_EQ(0x99b8f51bafd41adaULL - 0x000, d.Value()); +} + +TEST(LabelTree, KnownValue) { + Label l, m, n, o, p; + l = m; + m = n; + o = p; + p = n; + l = 0x536b5de3d468a1b5ULL; + EXPECT_EQ(0x536b5de3d468a1b5ULL, o.Value()); +} + +TEST(LabelTree, Related) { + Label l, m, n, o, p; + l = m - 1; + m = n - 10; + o = p + 100; + p = n + 1000; + EXPECT_EQ(1111U, o - l); +} + +TEST(EquationDeathTest, EqualConstants) { + Label m = 0x0d3962f280f07d24ULL; + Label n = 0x0d3962f280f07d24ULL; + m = n; // no death expected +} + +TEST(EquationDeathTest, EqualIndirectConstants) { + Label m = 0xa347f1e5238fe6a1ULL; + Label n; + Label o = n; + n = 0xa347f1e5238fe6a1ULL; + n = m; // no death expected +} + +TEST(EquationDeathTest, ConstantClash) { + Label m = 0xd4cc0f4f630ec741ULL; + Label n = 0x934cd2d8254fc3eaULL; + ASSERT_DEATH(m = n, "addend_ == addend"); +} + +TEST(EquationDeathTest, IndirectConstantClash) { + Label m = 0xd4cc0f4f630ec741ULL; + Label n, o; + n = o; + o = 0xcfbe3b83ac49ce86ULL; + ASSERT_DEATH(m = n, "addend_ == addend"); +} + +// Assigning to a related label may free the next Binding on its +// chain. This test always passes; it is interesting to memory +// checkers and coverage analysis. +TEST(LabelReferenceCount, AssignmentFree) { + Label l; + { + Label m; + l = m; + } + // This should free m's Binding. + l = 0xca8bae92f0376d4fULL; + ASSERT_EQ(0xca8bae92f0376d4fULL, l.Value()); +} + +// Finding the value of a label may free the Binding it refers to. This test +// always passes; it is interesting to memory checkers and coverage analysis. +TEST(LabelReferenceCount, FindValueFree) { + Label l; + { + Label m, n; + l = m; + m = n; + n = 0x7a0b0c576672daafULL; + // At this point, l's Binding refers to m's Binding, which refers + // to n's binding. + } + // Now, l is the only reference keeping the three Bindings alive. + // Resolving its value should free l's and m's original bindings. + ASSERT_EQ(0x7a0b0c576672daafULL, l.Value()); +} + +TEST(ConstructSection, Simple) { + Section s; +} + +TEST(ConstructSection, WithEndian) { + Section s(kBigEndian); +} + +// A fixture class for TestAssembler::Section tests. +class SectionFixture { + public: + Section section; + string contents; + static const u_int8_t data[]; + static const size_t data_size; +}; + +const u_int8_t SectionFixture::data[] = { + 0x87, 0x4f, 0x43, 0x67, 0x30, 0xd0, 0xd4, 0x0e +}; + +#define I0() +#define I1(a) { a } +#define I2(a,b) { a,b } +#define I3(a,b,c) { a,b,c } +#define I4(a,b,c,d) { a,b,c,d } +#define I5(a,b,c,d,e) { a,b,c,d,e } +#define I6(a,b,c,d,e,f) { a,b,c,d,e,f } +#define I7(a,b,c,d,e,f,g) { a,b,c,d,e,f,g } +#define I8(a,b,c,d,e,f,g,h) { a,b,c,d,e,f,g,h } +#define I9(a,b,c,d,e,f,g,h,i) { a,b,c,d,e,f,g,h,i } +#define ASSERT_BYTES(s, b) \ + do \ + { \ + static const u_int8_t expected_bytes[] = b; \ + ASSERT_EQ(sizeof(expected_bytes), s.size()); \ + ASSERT_TRUE(memcmp(s.data(), (const char *) expected_bytes, \ + sizeof(expected_bytes)) == 0); \ + } \ + while(0) + +class Append: public SectionFixture, public Test { }; + +TEST_F(Append, Bytes) { + section.Append(data, sizeof(data)); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_EQ(sizeof(data), contents.size()); + EXPECT_TRUE(0 == memcmp(contents.data(), (const char *) data, sizeof(data))); +} + +TEST_F(Append, BytesTwice) { + section.Append(data, sizeof(data)); + section.Append(data, sizeof(data)); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_EQ(2 * sizeof(data), contents.size()); + ASSERT_TRUE(0 == memcmp(contents.data(), (const char *) data, sizeof(data))); + ASSERT_TRUE(0 == memcmp(contents.data() + sizeof(data), + (const char *) data, sizeof(data))); +} + +TEST_F(Append, String) { + string s1 = "howdy "; + string s2 = "there"; + section.Append(s1); + section.Append(s2); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_STREQ(contents.c_str(), "howdy there"); +} + +TEST_F(Append, RepeatedBytes) { + section.Append((size_t) 10, '*'); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_STREQ(contents.c_str(), "**********"); +} + +TEST_F(Append, GeneralLE1) { + section.Append(kLittleEndian, 1, 42); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I1(42)); +} + +TEST_F(Append, GeneralLE2) { + section.Append(kLittleEndian, 2, 0x15a1); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0xa1, 0x15)); +} + +TEST_F(Append, GeneralLE3) { + section.Append(kLittleEndian, 3, 0x59ae8d); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x8d, 0xae, 0x59)); +} + +TEST_F(Append, GeneralLE4) { + section.Append(kLittleEndian, 4, 0x51603c56); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I4(0x56, 0x3c, 0x60, 0x51)); +} + +TEST_F(Append, GeneralLE5) { + section.Append(kLittleEndian, 5, 0x385e2803b4ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0xb4, 0x03, 0x28, 0x5e, 0x38)); +} + +TEST_F(Append, GeneralLE6) { + section.Append(kLittleEndian, 6, 0xc7db9534dd1fULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I6(0x1f, 0xdd, 0x34, 0x95, 0xdb, 0xc7)); +} + +TEST_F(Append, GeneralLE7) { + section.Append(kLittleEndian, 7, 0x1445c9f1b843e6ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I7(0xe6, 0x43, 0xb8, 0xf1, 0xc9, 0x45, 0x14)); +} + +TEST_F(Append, GeneralLE8) { + section.Append(kLittleEndian, 8, 0xaf48019dfe5c01e5ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I8(0xe5, 0x01, 0x5c, 0xfe, 0x9d, 0x01, 0x48, 0xaf)); +} + +TEST_F(Append, GeneralBE1) { + section.Append(kBigEndian, 1, 0xd0ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I1(0xd0)); +} + +TEST_F(Append, GeneralBE2) { + section.Append(kBigEndian, 2, 0x2e7eULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2e, 0x7e)); +} + +TEST_F(Append, GeneralBE3) { + section.Append(kBigEndian, 3, 0x37dad6ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x37, 0xda, 0xd6)); +} + +TEST_F(Append, GeneralBE4) { + section.Append(kBigEndian, 4, 0x715935c7ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I4(0x71, 0x59, 0x35, 0xc7)); +} + +TEST_F(Append, GeneralBE5) { + section.Append(kBigEndian, 5, 0x42baeb02b7ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x42, 0xba, 0xeb, 0x02, 0xb7)); +} + +TEST_F(Append, GeneralBE6) { + section.Append(kBigEndian, 6, 0xf1cdf10e7b18ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I6(0xf1, 0xcd, 0xf1, 0x0e, 0x7b, 0x18)); +} + +TEST_F(Append, GeneralBE7) { + section.Append(kBigEndian, 7, 0xf50a724f0b0d20ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I7(0xf5, 0x0a, 0x72, 0x4f, 0x0b, 0x0d, 0x20)); +} + +TEST_F(Append, GeneralBE8) { + section.Append(kBigEndian, 8, 0xa6b2cb5e98dc9c16ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I8(0xa6, 0xb2, 0xcb, 0x5e, 0x98, 0xdc, 0x9c, 0x16)); +} + +TEST_F(Append, GeneralLE1Label) { + Label l; + section.Append(kLittleEndian, 1, l); + l = 42; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I1(42)); +} + +TEST_F(Append, GeneralLE2Label) { + Label l; + section.Append(kLittleEndian, 2, l); + l = 0x15a1; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0xa1, 0x15)); +} + +TEST_F(Append, GeneralLE3Label) { + Label l; + section.Append(kLittleEndian, 3, l); + l = 0x59ae8d; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x8d, 0xae, 0x59)); +} + +TEST_F(Append, GeneralLE4Label) { + Label l; + section.Append(kLittleEndian, 4, l); + l = 0x51603c56; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I4(0x56, 0x3c, 0x60, 0x51)); +} + +TEST_F(Append, GeneralLE5Label) { + Label l; + section.Append(kLittleEndian, 5, l); + l = 0x385e2803b4ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0xb4, 0x03, 0x28, 0x5e, 0x38)); +} + +TEST_F(Append, GeneralLE6Label) { + Label l; + section.Append(kLittleEndian, 6, l); + l = 0xc7db9534dd1fULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I6(0x1f, 0xdd, 0x34, 0x95, 0xdb, 0xc7)); +} + +TEST_F(Append, GeneralLE7Label) { + Label l; + section.Append(kLittleEndian, 7, l); + l = 0x1445c9f1b843e6ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I7(0xe6, 0x43, 0xb8, 0xf1, 0xc9, 0x45, 0x14)); +} + +TEST_F(Append, GeneralLE8Label) { + Label l; + section.Append(kLittleEndian, 8, l); + l = 0xaf48019dfe5c01e5ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I8(0xe5, 0x01, 0x5c, 0xfe, 0x9d, 0x01, 0x48, 0xaf)); +} + +TEST_F(Append, GeneralBE1Label) { + Label l; + section.Append(kBigEndian, 1, l); + l = 0xd0ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I1(0xd0)); +} + +TEST_F(Append, GeneralBE2Label) { + Label l; + section.Append(kBigEndian, 2, l); + l = 0x2e7eULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2e, 0x7e)); +} + +TEST_F(Append, GeneralBE3Label) { + Label l; + section.Append(kBigEndian, 3, l); + l = 0x37dad6ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x37, 0xda, 0xd6)); +} + +TEST_F(Append, GeneralBE4Label) { + Label l; + section.Append(kBigEndian, 4, l); + l = 0x715935c7ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I4(0x71, 0x59, 0x35, 0xc7)); +} + +TEST_F(Append, GeneralBE5Label) { + Label l; + section.Append(kBigEndian, 5, l); + l = 0x42baeb02b7ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x42, 0xba, 0xeb, 0x02, 0xb7)); +} + +TEST_F(Append, GeneralBE6Label) { + Label l; + section.Append(kBigEndian, 6, l); + l = 0xf1cdf10e7b18ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I6(0xf1, 0xcd, 0xf1, 0x0e, 0x7b, 0x18)); +} + +TEST_F(Append, GeneralBE7Label) { + Label l; + section.Append(kBigEndian, 7, l); + l = 0xf50a724f0b0d20ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I7(0xf5, 0x0a, 0x72, 0x4f, 0x0b, 0x0d, 0x20)); +} + +TEST_F(Append, GeneralBE8Label) { + Label l; + section.Append(kBigEndian, 8, l); + l = 0xa6b2cb5e98dc9c16ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I8(0xa6, 0xb2, 0xcb, 0x5e, 0x98, 0xdc, 0x9c, 0x16)); +} + +TEST_F(Append, B8) { + section.Append(1, 0x2a); + section.B8(0xd3U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0xd3)); +} + +TEST_F(Append, B8Label) { + Label l; + section.Append(1, 0x2a); + section.B8(l); + l = 0x4bU; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0x4b)); +} + +TEST_F(Append, B16) { + section.Append(1, 0x2a); + section.B16(0x472aU); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x47, 0x2a)); +} + +TEST_F(Append, B16Label) { + Label l; + section.Append(1, 0x2a); + section.B16(l); + l = 0x55e8U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x55, 0xe8)); +} + +TEST_F(Append, B32) { + section.Append(1, 0x2a); + section.B32(0xbd412cbcU); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0xbd, 0x41, 0x2c, 0xbc)); +} + +TEST_F(Append, B32Label) { + Label l; + section.Append(1, 0x2a); + section.B32(l); + l = 0x208e37d5U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0x20, 0x8e, 0x37, 0xd5)); +} + +TEST_F(Append, B64) { + section.Append(1, 0x2a); + section.B64(0x3402a013111e68adULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0x34, 0x02, 0xa0, 0x13, 0x11, 0x1e, 0x68, 0xad)); +} + +TEST_F(Append, B64Label) { + Label l; + section.Append(1, 0x2a); + section.B64(l); + l = 0x355dbfbb4ac6d57fULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0x35, 0x5d, 0xbf, 0xbb, 0x4a, 0xc6, 0xd5, 0x7f)); +} + +TEST_F(Append, L8) { + section.Append(1, 0x2a); + section.L8(0x26U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0x26)); +} + +TEST_F(Append, L8Label) { + Label l; + section.Append(1, 0x2a); + section.L8(l); + l = 0xa8U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0xa8)); +} + +TEST_F(Append, L16) { + section.Append(1, 0x2a); + section.L16(0xca6dU); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x6d, 0xca)); +} + +TEST_F(Append, L16Label) { + Label l; + section.Append(1, 0x2a); + section.L16(l); + l = 0xd21fU; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x1f, 0xd2)); +} + +TEST_F(Append, L32) { + section.Append(1, 0x2a); + section.L32(0x558f6181U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0x81, 0x61, 0x8f, 0x55)); +} + +TEST_F(Append, L32Label) { + Label l; + section.Append(1, 0x2a); + section.L32(l); + l = 0x4b810f82U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0x82, 0x0f, 0x81, 0x4b)); +} + +TEST_F(Append, L64) { + section.Append(1, 0x2a); + section.L64(0x564384f7579515bfULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0xbf, 0x15, 0x95, 0x57, 0xf7, 0x84, 0x43, 0x56)); +} + +TEST_F(Append, L64Label) { + Label l; + section.Append(1, 0x2a); + section.L64(l); + l = 0x424b1d020667c8dbULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0xdb, 0xc8, 0x67, 0x06, 0x02, 0x1d, 0x4b, 0x42)); +} + +TEST_F(Append, D8Big) { + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D8(0xe6U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0xe6)); +} + +TEST_F(Append, D8BigLabel) { + Label l; + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D8(l); + l = 0xeeU; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0xee)); +} + +TEST_F(Append, D16Big) { + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D16(0x83b1U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x83, 0xb1)); +} + +TEST_F(Append, D16BigLabel) { + Label l; + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D16(l); + l = 0x5b55U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x5b, 0x55)); +} + +TEST_F(Append, D32Big) { + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D32(0xd0b0e431U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0xd0, 0xb0, 0xe4, 0x31)); +} + +TEST_F(Append, D32BigLabel) { + Label l; + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D32(l); + l = 0x312fb340U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0x31, 0x2f, 0xb3, 0x40)); +} + +TEST_F(Append, D64Big) { + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D64(0xb109843500dbcb16ULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0xb1, 0x09, 0x84, 0x35, 0x00, 0xdb, 0xcb, 0x16)); +} + +TEST_F(Append, D64BigLabel) { + Label l; + section.set_endianness(kBigEndian); + section.Append(1, 0x2a); + section.D64(l); + l = 0x9a0d61b70f671fd7ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0x9a, 0x0d, 0x61, 0xb7, 0x0f, 0x67, 0x1f, 0xd7)); +} + +TEST_F(Append, D8Little) { + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D8(0x42U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0x42)); +} + +TEST_F(Append, D8LittleLabel) { + Label l; + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D8(l); + l = 0x05U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I2(0x2a, 0x05)); +} + +TEST_F(Append, D16Little) { + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D16(0xc5c5U); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0xc5, 0xc5)); +} + +TEST_F(Append, D16LittleLabel) { + Label l; + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D16(l); + l = 0xb620U; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I3(0x2a, 0x20, 0xb6)); +} + +TEST_F(Append, D32Little) { + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D32(0x1a87d0feU); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0xfe, 0xd0, 0x87, 0x1a)); +} + +TEST_F(Append, D32LittleLabel) { + Label l; + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D32(l); + l = 0xb8012d6bU; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I5(0x2a, 0x6b, 0x2d, 0x01, 0xb8)); +} + +TEST_F(Append, D64Little) { + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D64(0x42de75c61375a1deULL); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0xde, 0xa1, 0x75, 0x13, 0xc6, 0x75, 0xde, 0x42)); +} + +TEST_F(Append, D64LittleLabel) { + Label l; + section.set_endianness(kLittleEndian); + section.Append(1, 0x2a); + section.D64(l); + l = 0x8b3bececf3fb5312ULL; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, + I9(0x2a, 0x12, 0x53, 0xfb, 0xf3, 0xec, 0xec, 0x3b, 0x8b)); +} + +TEST_F(Append, Variety) { + Label a, b, c, d, e, f, g, h; + section.Append(kBigEndian, 1, a) + .Append(kLittleEndian, 8, h) + .Append(kBigEndian, 1, 0x8bULL) + .Append(kLittleEndian, 8, 0x0ea56540448f4439ULL) + .Append(kBigEndian, 2, b) + .Append(kLittleEndian, 7, g) + .Append(kBigEndian, 2, 0xcf15ULL) + .Append(kLittleEndian, 7, 0x29694f04c5724aULL) + .Append(kBigEndian, 3, c) + .Append(kLittleEndian, 6, f) + .Append(kBigEndian, 3, 0x8c3ffdULL) + .Append(kLittleEndian, 6, 0x6f11ba80187aULL) + .Append(kBigEndian, 4, d) + .Append(kLittleEndian, 5, e) + .Append(kBigEndian, 4, 0x2fda2472ULL) + .Append(kLittleEndian, 5, 0x0aa02d423fULL) + .Append(kBigEndian, 5, e) + .Append(kLittleEndian, 4, d) + .Append(kBigEndian, 5, 0x53ba432138ULL) + .Append(kLittleEndian, 4, 0xf139ae60ULL) + .Append(kBigEndian, 6, f) + .Append(kLittleEndian, 3, c) + .Append(kBigEndian, 6, 0x168e436af716ULL) + .Append(kLittleEndian, 3, 0x3ef189ULL) + .Append(kBigEndian, 7, g) + .Append(kLittleEndian, 2, b) + .Append(kBigEndian, 7, 0xacd4ef233e47d9ULL) + .Append(kLittleEndian, 2, 0x5311ULL) + .Append(kBigEndian, 8, h) + .Append(kLittleEndian, 1, a) + .Append(kBigEndian, 8, 0x4668d5f1c93637a1ULL) + .Append(kLittleEndian, 1, 0x65ULL); + a = 0x79ac9bd8aa256b35ULL; + b = 0x22d13097ef86c91cULL; + c = 0xf204968b0a05862fULL; + d = 0x163177f15a0eb4ecULL; + e = 0xbd1b0f1d977f2246ULL; + f = 0x2b0842eee83c6461ULL; + g = 0x92f4b928a4bf875eULL; + h = 0x61a199a8f7286ba6ULL; + ASSERT_EQ(8 * 18U, section.Size()); + ASSERT_TRUE(section.GetContents(&contents)); + + static const u_int8_t expected[] = { + 0x35, 0xa6, 0x6b, 0x28, 0xf7, 0xa8, 0x99, 0xa1, 0x61, + 0x8b, 0x39, 0x44, 0x8f, 0x44, 0x40, 0x65, 0xa5, 0x0e, + 0xc9, 0x1c, 0x5e, 0x87, 0xbf, 0xa4, 0x28, 0xb9, 0xf4, + 0xcf, 0x15, 0x4a, 0x72, 0xc5, 0x04, 0x4f, 0x69, 0x29, + 0x05, 0x86, 0x2f, 0x61, 0x64, 0x3c, 0xe8, 0xee, 0x42, + 0x8c, 0x3f, 0xfd, 0x7a, 0x18, 0x80, 0xba, 0x11, 0x6f, + 0x5a, 0x0e, 0xb4, 0xec, 0x46, 0x22, 0x7f, 0x97, 0x1d, + 0x2f, 0xda, 0x24, 0x72, 0x3f, 0x42, 0x2d, 0xa0, 0x0a, + 0x1d, 0x97, 0x7f, 0x22, 0x46, 0xec, 0xb4, 0x0e, 0x5a, + 0x53, 0xba, 0x43, 0x21, 0x38, 0x60, 0xae, 0x39, 0xf1, + 0x42, 0xee, 0xe8, 0x3c, 0x64, 0x61, 0x2f, 0x86, 0x05, + 0x16, 0x8e, 0x43, 0x6a, 0xf7, 0x16, 0x89, 0xf1, 0x3e, + 0xf4, 0xb9, 0x28, 0xa4, 0xbf, 0x87, 0x5e, 0x1c, 0xc9, + 0xac, 0xd4, 0xef, 0x23, 0x3e, 0x47, 0xd9, 0x11, 0x53, + 0x61, 0xa1, 0x99, 0xa8, 0xf7, 0x28, 0x6b, 0xa6, 0x35, + 0x46, 0x68, 0xd5, 0xf1, 0xc9, 0x36, 0x37, 0xa1, 0x65, + }; + + ASSERT_TRUE(0 == memcmp(contents.data(), expected, sizeof(expected))); +} + +TEST_F(Append, Section) { + section.Append("murder"); + { + Section middle; + middle.Append(" she"); + section.Append(middle); + } + section.Append(" wrote"); + EXPECT_EQ(16U, section.Size()); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_STREQ(contents.c_str(), "murder she wrote"); +} + +TEST_F(Append, SectionRefs) { + section.Append("sugar "); + Label l; + { + Section middle; + Label m; + middle.B32(m); + section.Append(middle); + m = 0x66726565; + } + section.Append(" jazz"); + EXPECT_EQ(15U, section.Size()); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_STREQ(contents.c_str(), "sugar free jazz"); +} + +TEST_F(Append, LEB128_0) { + section.LEB128(0); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\0", 1), contents); +} + +TEST_F(Append, LEB128_0x3f) { + section.LEB128(0x3f); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x3f", 1), contents); +} + +TEST_F(Append, LEB128_0x40) { + section.LEB128(0x40); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xc0\x00", 2), contents); +} + +TEST_F(Append, LEB128_0x7f) { + section.LEB128(0x7f); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x00", 2), contents); +} + +TEST_F(Append, LEB128_0x80) { + section.LEB128(0x80); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x01", 2), contents); +} + +TEST_F(Append, LEB128_0xff) { + section.LEB128(0xff); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x01", 2), contents); +} + +TEST_F(Append, LEB128_0x1fff) { + section.LEB128(0x1fff); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x3f", 2), contents); +} + +TEST_F(Append, LEB128_0x2000) { + section.LEB128(0x2000); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\xc0\x00", 3), contents); +} + +TEST_F(Append, LEB128_n1) { + section.LEB128(-1); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x7f", 1), contents); +} + +TEST_F(Append, LEB128_n0x40) { + section.LEB128(-0x40); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x40", 1), contents); +} + +TEST_F(Append, LEB128_n0x41) { + section.LEB128(-0x41); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xbf\x7f", 2), contents); +} + +TEST_F(Append, LEB128_n0x7f) { + section.LEB128(-0x7f); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x81\x7f", 2), contents); +} + +TEST_F(Append, LEB128_n0x80) { + section.LEB128(-0x80); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x7f", 2), contents); +} + +TEST_F(Append, LEB128_n0x2000) { + section.LEB128(-0x2000); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x40", 2), contents); +} + +TEST_F(Append, LEB128_n0x2001) { + section.LEB128(-0x2001); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\xbf\x7f", 3), contents); +} + +TEST_F(Append,ULEB128_0) { + section.ULEB128(0); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\0", 1), contents); +} + +TEST_F(Append,ULEB128_1) { + section.ULEB128(1); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x01", 1), contents); +} + +TEST_F(Append,ULEB128_0x3f) { + section.ULEB128(0x3f); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x3f", 1), contents); +} + +TEST_F(Append,ULEB128_0x40) { + section.ULEB128(0x40); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x40", 1), contents); +} + +TEST_F(Append,ULEB128_0x7f) { + section.ULEB128(0x7f); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x7f", 1), contents); +} + +TEST_F(Append,ULEB128_0x80) { + section.ULEB128(0x80); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x01", 2), contents); +} + +TEST_F(Append,ULEB128_0xff) { + section.ULEB128(0xff); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x01", 2), contents); +} + +TEST_F(Append,ULEB128_0x100) { + section.ULEB128(0x100); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x02", 2), contents); +} + +TEST_F(Append,ULEB128_0x1fff) { + section.ULEB128(0x1fff); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x3f", 2), contents); +} + +TEST_F(Append,ULEB128_0x2000) { + section.ULEB128(0x2000); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x40", 2), contents); +} + +TEST_F(Append,ULEB128_0x3fff) { + section.ULEB128(0x3fff); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xff\x7f", 2), contents); +} + +TEST_F(Append,ULEB128_0x4000) { + section.ULEB128(0x4000); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x80\x01", 3), contents); +} + +TEST_F(Append,ULEB128_12857) { + section.ULEB128(12857); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\xb9\x64", 2), contents); +} + +TEST_F(Append, LEBChain) { + section.LEB128(-0x80).ULEB128(12857).Append("*"); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(string("\x80\x7f\xb9\x64*", 5), contents); +} + + +class GetContents: public SectionFixture, public Test { }; + +TEST_F(GetContents, Undefined) { + Label l; + section.Append(kLittleEndian, 8, l); + ASSERT_FALSE(section.GetContents(&contents)); +} + +TEST_F(GetContents, ClearsContents) { + section.Append((size_t) 10, '*'); + EXPECT_EQ(10U, section.Size()); + EXPECT_TRUE(section.GetContents(&contents)); + EXPECT_EQ(0U, section.Size()); +} + +TEST_F(GetContents, ClearsReferences) { + Label l; + section.Append(kBigEndian, 1, l); + l = 42; + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_BYTES(contents, I1(42)); + ASSERT_TRUE(section.GetContents(&contents)); // should not die +} + +class Miscellanea: public SectionFixture, public Test { }; + +TEST_F(Miscellanea, Clear) { + section.Append("howdy"); + Label l; + section.L32(l); + EXPECT_EQ(9U, section.Size()); + section.Clear(); + EXPECT_EQ(0U, section.Size()); + l = 0x8d231bf0U; + ASSERT_TRUE(section.GetContents(&contents)); // should not die +} + +TEST_F(Miscellanea, Align) { + section.Append("*"); + EXPECT_EQ(1U, section.Size()); + section.Align(4).Append("*"); + EXPECT_EQ(5U, section.Size()); + section.Append("*").Align(2); + EXPECT_EQ(6U, section.Size()); +} + +TEST_F(Miscellanea, AlignPad) { + section.Append("*"); + EXPECT_EQ(1U, section.Size()); + section.Align(4, ' ').Append("*"); + EXPECT_EQ(5U, section.Size()); + section.Append("*").Align(2, ' '); + EXPECT_EQ(6U, section.Size()); + ASSERT_TRUE(section.GetContents(&contents)); + ASSERT_EQ(string("* **"), contents); +} + +TEST_F(Miscellanea, StartHereMark) { + Label m; + section.Append(42, ' ').Mark(&m).Append(13, '+'); + EXPECT_EQ(42U, m - section.start()); + EXPECT_EQ(42U + 13U, section.Here() - section.start()); + EXPECT_FALSE(section.start().IsKnownConstant()); + EXPECT_FALSE(m.IsKnownConstant()); + EXPECT_FALSE(section.Here().IsKnownConstant()); +} + +TEST_F(Miscellanea, Endianness) { + section.set_endianness(kBigEndian); + EXPECT_EQ(kBigEndian, section.endianness()); + section.set_endianness(kLittleEndian); + EXPECT_EQ(kLittleEndian, section.endianness()); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/testdata/linux_test_app.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/testdata/linux_test_app.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/testdata/linux_test_app.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/testdata/linux_test_app.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,81 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Breakpad test application for Linux. When run, it generates one on-demand +// minidump and then crashes, which should generate an on-crash minidump. +// dump_syms can be used to extract symbol information for use in processing. + +// To build: +// g++ -g -o linux_test_app -I ../../ -L../../client/linux linux_test_app.cc \ +// -lbreakpad +// Add -m32 to build a 32-bit executable, or -m64 for a 64-bit one +// (assuming your environment supports it). Replace -g with -gstabs+ to +// generate an executable with STABS symbols (needs -m32), or -gdwarf-2 for one +// with DWARF symbols (32- or 64-bit) + +#include +#include +#include +#include + +#include "common/linux/linux_syscall_support.h" +#include "client/linux/handler/exception_handler.h" + +namespace { + +// google_breakpad::MinidumpCallback to invoke after minidump generation. +static bool callback(const char *dump_path, const char *id, + void *context, + bool succeeded) { + if (succeeded) { + printf("dump guid is %s\n", id); + } else { + printf("dump failed\n"); + } + fflush(stdout); + + return succeeded; +} + +static void CrashFunction() { + int *i = reinterpret_cast(0x45); + *i = 5; // crash! +} + +} // namespace + +int main(int argc, char **argv) { + google_breakpad::ExceptionHandler eh(".", NULL, callback, NULL, true); + if (!eh.WriteMinidump()) { + printf("Failed to generate on-demand minidump\n"); + } + CrashFunction(); + printf("did not crash?\n"); + return 0; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out 2010-04-16 17:32:47.000000000 +0100 @@ -12,12 +12,16 @@ eip = 0x0040429e esp = 0x0012fe84 ebp = 0x0012fe88 ebx = 0x7c80abc1 esi = 0x00000002 edi = 0x00000a28 eax = 0x00000045 ecx = 0x0012fe94 edx = 0x0042bc58 efl = 0x00010246 + Found by: given as instruction pointer in context 1 test_app.exe!main [test_app.cc : 65 + 0x4] eip = 0x00404200 esp = 0x0012fe90 ebp = 0x0012ff70 + Found by: call frame info 2 test_app.exe!__tmainCRTStartup [crt0.c : 327 + 0x11] eip = 0x004053ec esp = 0x0012ff78 ebp = 0x0012ffc0 + Found by: call frame info 3 kernel32.dll!BaseProcessStart + 0x22 eip = 0x7c816fd7 esp = 0x0012ffc8 ebp = 0x0012fff0 + Found by: call frame info Loaded modules: 0x00400000 - 0x0042cfff test_app.exe ??? (main) diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/testdata/module1.out firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/testdata/module1.out --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/testdata/module1.out 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/testdata/module1.out 2010-04-16 17:32:47.000000000 +0100 @@ -13,7 +13,16 @@ FUNC 1300 100 c Function1_4 FUNC 2000 0 0 Test_Zero_Size_Function_Is_Ignored 2000 4 88 2 +PUBLIC 2800 0 PublicSymbol +FUNC 3000 7000 42 LargeFunction +3000 7000 4098359 3 STACK WIN 4 1000 c 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ = STACK WIN 4 1100 8 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ = STACK WIN 4 1100 100 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ = STACK WIN 4 1300 100 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ = +STACK CFI INIT 3d40 af .cfa: $esp 4 + .ra: .cfa 4 - ^ +STACK CFI 3d41 .cfa: $esp 8 + +STACK CFI 3d43 .cfa: $ebp 8 + $ebp: .cfa 8 - ^ +STACK CFI 3d54 $ebx: .cfa 20 - ^ +STACK CFI 3d5a $esi: .cfa 16 - ^ +STACK CFI 3d84 $edi: .cfa 12 - ^ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/testdata/module2.out firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/testdata/module2.out --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/testdata/module2.out 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/testdata/module2.out 2010-04-16 17:32:47.000000000 +0100 @@ -15,3 +15,9 @@ PUBLIC 21a0 0 Public2_2 STACK WIN 4 2000 c 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ = STACK WIN 4 2170 14 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ = +STACK CFI INIT 3df0 af .cfa: $esp 4 + .ra: .cfa 4 - ^ +STACK CFI 3df1 .cfa: $esp 8 + +STACK CFI 3df3 .cfa: $ebp 8 + $ebp: .cfa 8 - ^ +STACK CFI 3e04 $ebx: .cfa 20 - ^ +STACK CFI 3e0a $esi: .cfa 16 - ^ +STACK CFI 3e34 $edi: .cfa 12 - ^ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/windows_frame_info.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/windows_frame_info.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/windows_frame_info.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/processor/windows_frame_info.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,126 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// windows_frame_info.h: Holds debugging information about a stack frame. +// +// This structure is specific to Windows debugging information obtained +// from pdb files using the DIA API. +// +// Author: Mark Mentovai + + +#ifndef PROCESSOR_WINDOWS_FRAME_INFO_H__ +#define PROCESSOR_WINDOWS_FRAME_INFO_H__ + +#include + +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +struct WindowsFrameInfo { + public: + enum Validity { + VALID_NONE = 0, + VALID_PARAMETER_SIZE = 1, + VALID_ALL = -1 + }; + + WindowsFrameInfo() : valid(VALID_NONE), + prolog_size(0), + epilog_size(0), + parameter_size(0), + saved_register_size(0), + local_size(0), + max_stack_size(0), + allocates_base_pointer(0), + program_string() {} + + WindowsFrameInfo(u_int32_t set_prolog_size, + u_int32_t set_epilog_size, + u_int32_t set_parameter_size, + u_int32_t set_saved_register_size, + u_int32_t set_local_size, + u_int32_t set_max_stack_size, + int set_allocates_base_pointer, + const std::string set_program_string) + : valid(VALID_ALL), + prolog_size(set_prolog_size), + epilog_size(set_epilog_size), + parameter_size(set_parameter_size), + saved_register_size(set_saved_register_size), + local_size(set_local_size), + max_stack_size(set_max_stack_size), + allocates_base_pointer(set_allocates_base_pointer), + program_string(set_program_string) {} + + // CopyFrom makes "this" WindowsFrameInfo object identical to "that". + void CopyFrom(const WindowsFrameInfo &that) { + valid = that.valid; + prolog_size = that.prolog_size; + epilog_size = that.epilog_size; + parameter_size = that.parameter_size; + saved_register_size = that.saved_register_size; + local_size = that.local_size; + max_stack_size = that.max_stack_size; + allocates_base_pointer = that.allocates_base_pointer; + program_string = that.program_string; + } + + // Clears the WindowsFrameInfo object so that users will see it as though + // it contains no information. + void Clear() { + valid = VALID_NONE; + program_string.erase(); + } + + // Identifies which fields in the structure are valid. This is of + // type Validity, but it is defined as an int because it's not + // possible to OR values into an enumerated type. Users must check + // this field before using any other. + int valid; + + // These values come from IDiaFrameData. + u_int32_t prolog_size; + u_int32_t epilog_size; + u_int32_t parameter_size; + u_int32_t saved_register_size; + u_int32_t local_size; + u_int32_t max_stack_size; + + // Only one of allocates_base_pointer or program_string will be valid. + // If program_string is empty, use allocates_base_pointer. + bool allocates_base_pointer; + std::string program_string; +}; + +} // namespace google_breakpad + + +#endif // PROCESSOR_WINDOWS_FRAME_INFO_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags_completions.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags_completions.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags_completions.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags_completions.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,121 @@ +// Copyright (c) 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// --- +// Author: Dave Nicponski +// +// Implement helpful bash-style command line flag completions +// +// ** Functional API: +// HandleCommandLineCompletions() should be called early during +// program startup, but after command line flag code has been +// initialized, such as the beginning of HandleCommandLineHelpFlags(). +// It checks the value of the flag --tab_completion_word. If this +// flag is empty, nothing happens here. If it contains a string, +// however, then HandleCommandLineCompletions() will hijack the +// process, attempting to identify the intention behind this +// completion. Regardless of the outcome of this deduction, the +// process will be terminated, similar to --helpshort flag +// handling. +// +// ** Overview of Bash completions: +// Bash can be told to programatically determine completions for the +// current 'cursor word'. It does this by (in this case) invoking a +// command with some additional arguments identifying the command +// being executed, the word being completed, and the previous word +// (if any). Bash then expects a sequence of output lines to be +// printed to stdout. If these lines all contain a common prefix +// longer than the cursor word, bash will replace the cursor word +// with that common prefix, and display nothing. If there isn't such +// a common prefix, bash will display the lines in pages using 'more'. +// +// ** Strategy taken for command line completions: +// If we can deduce either the exact flag intended, or a common flag +// prefix, we'll output exactly that. Otherwise, if information +// must be displayed to the user, we'll take the opportunity to add +// some helpful information beyond just the flag name (specifically, +// we'll include the default flag value and as much of the flag's +// description as can fit on a single terminal line width, as specified +// by the flag --tab_completion_columns). Furthermore, we'll try to +// make bash order the output such that the most useful or relevent +// flags are the most likely to be shown at the top. +// +// ** Additional features: +// To assist in finding that one really useful flag, substring matching +// was implemented. Before pressing a to get completion for the +// current word, you can append one or more '?' to the flag to do +// substring matching. Here's the semantics: +// --foo Show me all flags with names prefixed by 'foo' +// --foo? Show me all flags with 'foo' somewhere in the name +// --foo?? Same as prior case, but also search in module +// definition path for 'foo' +// --foo??? Same as prior case, but also search in flag +// descriptions for 'foo' +// Finally, we'll trim the output to a relatively small number of +// flags to keep bash quiet about the verbosity of output. If one +// really wanted to see all possible matches, appending a '+' to the +// search word will force the exhaustive list of matches to be printed. +// +// ** How to have bash accept completions from a binary: +// Bash requires that it be informed about each command that programmatic +// completion should be enabled for. Example addition to a .bashrc +// file would be (your path to gflags_completions.sh file may differ): + +/* +$ complete -o bashdefault -o default -o nospace -C \ + '/usr/local/bin/gflags_completions.sh --tab_completion_columns $COLUMNS' \ + time env binary_name another_binary [...] +*/ + +// This would allow the following to work: +// $ /path/to/binary_name --vmodule +// Or: +// $ ./bin/path/another_binary --gfs_u +// (etc) +// +// Sadly, it appears that bash gives no easy way to force this behavior for +// all commands. That's where the "time" in the above example comes in. +// If you haven't specifically added a command to the list of completion +// supported commands, you can still get completions by prefixing the +// entire command with "env". +// $ env /some/brand/new/binary --vmod +// Assuming that "binary" is a newly compiled binary, this should still +// produce the expected completion output. + + +#ifndef GOOGLE_GFLAGS_COMPLETIONS_H_ +#define GOOGLE_GFLAGS_COMPLETIONS_H_ + +namespace google { + +void HandleCommandLineCompletions(void); + +} + +#endif // GOOGLE_GFLAGS_COMPLETIONS_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/gflags/gflags.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,533 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// --- +// Author: Ray Sidney +// Revamped and reorganized by Craig Silverstein +// +// This is the file that should be included by any file which declares +// or defines a command line flag or wants to parse command line flags +// or print a program usage message (which will include information about +// flags). Executive summary, in the form of an example foo.cc file: +// +// #include "foo.h" // foo.h has a line "DECLARE_int32(start);" +// +// DEFINE_int32(end, 1000, "The last record to read"); +// DECLARE_bool(verbose); // some other file has a DEFINE_bool(verbose, ...) +// +// void MyFunc() { +// if (FLAGS_verbose) printf("Records %d-%d\n", FLAGS_start, FLAGS_end); +// } +// +// Then, at the command-line: +// ./foo --noverbose --start=5 --end=100 +// +// For more details, see +// doc/gflags.html +// +// --- A note about thread-safety: +// +// We describe many functions in this routine as being thread-hostile, +// thread-compatible, or thread-safe. Here are the meanings we use: +// +// thread-safe: it is safe for multiple threads to call this routine +// (or, when referring to a class, methods of this class) +// concurrently. +// thread-hostile: it is not safe for multiple threads to call this +// routine (or methods of this class) concurrently. In gflags, +// most thread-hostile routines are intended to be called early in, +// or even before, main() -- that is, before threads are spawned. +// thread-compatible: it is safe for multiple threads to read from +// this variable (when applied to variables), or to call const +// methods of this class (when applied to classes), as long as no +// other thread is writing to the variable or calling non-const +// methods of this class. + +#ifndef GOOGLE_GFLAGS_H_ +#define GOOGLE_GFLAGS_H_ + +#include +#include + +// We care a lot about number of bits things take up. Unfortunately, +// systems define their bit-specific ints in a lot of different ways. +// We use our own way, and have a typedef to get there. +// Note: these commands below may look like "#if 1" or "#if 0", but +// that's because they were constructed that way at ./configure time. +// Look at gflags.h.in to see how they're calculated (based on your config). +#if 1 +#include // the normal place uint16_t is defined +#endif +#if 1 +#include // the normal place u_int16_t is defined +#endif +#if 1 +#include // a third place for uint16_t or u_int16_t +#endif + +namespace google { + +#if 1 // the C99 format +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +#elif 1 // the BSD format +typedef int32_t int32; +typedef u_int32_t uint32; +typedef int64_t int64; +typedef u_int64_t uint64; +#elif 0 // the windows (vc7) format +typedef __int32 int32; +typedef unsigned __int32 uint32; +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +#error Do not know how to define a 32-bit integer quantity on your system +#endif + +// -------------------------------------------------------------------- +// To actually define a flag in a file, use DEFINE_bool, +// DEFINE_string, etc. at the bottom of this file. You may also find +// it useful to register a validator with the flag. This ensures that +// when the flag is parsed from the commandline, or is later set via +// SetCommandLineOption, we call the validation function. +// +// The validation function should return true if the flag value is valid, and +// false otherwise. If the function returns false for the new setting of the +// flag, the flag will retain its current value. If it returns false for the +// default value, InitGoogle will die. +// +// This function is safe to call at global construct time (as in the +// example below). +// +// Example use: +// static bool ValidatePort(const char* flagname, int32 value) { +// if (value > 0 && value < 32768) // value is ok +// return true; +// printf("Invalid value for --%s: %d\n", flagname, (int)value); +// return false; +// } +// DEFINE_int32(port, 0, "What port to listen on"); +// static bool dummy = RegisterFlagValidator(&FLAGS_port, &ValidatePort); + +// Returns true if successfully registered, false if not (because the +// first argument doesn't point to a command-line flag, or because a +// validator is already registered for this flag). +bool RegisterFlagValidator(const bool* flag, + bool (*validate_fn)(const char*, bool)); +bool RegisterFlagValidator(const int32* flag, + bool (*validate_fn)(const char*, int32)); +bool RegisterFlagValidator(const int64* flag, + bool (*validate_fn)(const char*, int64)); +bool RegisterFlagValidator(const uint64* flag, + bool (*validate_fn)(const char*, uint64)); +bool RegisterFlagValidator(const double* flag, + bool (*validate_fn)(const char*, double)); +bool RegisterFlagValidator(const std::string* flag, + bool (*validate_fn)(const char*, const std::string&)); + + +// -------------------------------------------------------------------- +// These methods are the best way to get access to info about the +// list of commandline flags. Note that these routines are pretty slow. +// GetAllFlags: mostly-complete info about the list, sorted by file. +// ShowUsageWithFlags: pretty-prints the list to stdout (what --help does) +// ShowUsageWithFlagsRestrict: limit to filenames with restrict as a substr +// +// In addition to accessing flags, you can also access argv[0] (the program +// name) and argv (the entire commandline), which we sock away a copy of. +// These variables are static, so you should only set them once. + +struct CommandLineFlagInfo { + std::string name; // the name of the flag + std::string type; // the type of the flag: int32, etc + std::string description; // the "help text" associated with the flag + std::string current_value; // the current value, as a string + std::string default_value; // the default value, as a string + std::string filename; // 'cleaned' version of filename holding the flag + bool has_validator_fn; // true if RegisterFlagValidator called on flag + bool is_default; // true if the flag has default value +}; + +extern void GetAllFlags(std::vector* OUTPUT); +// These two are actually defined in commandlineflags_reporting.cc. +extern void ShowUsageWithFlags(const char *argv0); // what --help does +extern void ShowUsageWithFlagsRestrict(const char *argv0, const char *restrict); + +// Create a descriptive string for a flag. +// Goes to some trouble to make pretty line breaks. +extern std::string DescribeOneFlag(const CommandLineFlagInfo& flag); + +// Thread-hostile; meant to be called before any threads are spawned. +extern void SetArgv(int argc, const char** argv); +// The following functions are thread-safe as long as SetArgv() is +// only called before any threads start. +extern const std::vector& GetArgvs(); // all of argv as a vector +extern const char* GetArgv(); // all of argv as a string +extern const char* GetArgv0(); // only argv0 +extern uint32 GetArgvSum(); // simple checksum of argv +extern const char* ProgramInvocationName(); // argv0, or "UNKNOWN" if not set +extern const char* ProgramInvocationShortName(); // basename(argv0) +// ProgramUsage() is thread-safe as long as SetUsageMessage() is only +// called before any threads start. +extern const char* ProgramUsage(); // string set by SetUsageMessage() + + +// -------------------------------------------------------------------- +// Normally you access commandline flags by just saying "if (FLAGS_foo)" +// or whatever, and set them by calling "FLAGS_foo = bar" (or, more +// commonly, via the DEFINE_foo macro). But if you need a bit more +// control, we have programmatic ways to get/set the flags as well. +// These programmatic ways to access flags are thread-safe, but direct +// access is only thread-compatible. + +// Return true iff the flagname was found. +// OUTPUT is set to the flag's value, or unchanged if we return false. +extern bool GetCommandLineOption(const char* name, std::string* OUTPUT); + +// Return true iff the flagname was found. OUTPUT is set to the flag's +// CommandLineFlagInfo or unchanged if we return false. +extern bool GetCommandLineFlagInfo(const char* name, + CommandLineFlagInfo* OUTPUT); + +// Return the CommandLineFlagInfo of the flagname. exit() if name not found. +// Example usage, to check if a flag's value is currently the default value: +// if (GetCommandLineFlagInfoOrDie("foo").is_default) ... +extern CommandLineFlagInfo GetCommandLineFlagInfoOrDie(const char* name); + +enum FlagSettingMode { + // update the flag's value (can call this multiple times). + SET_FLAGS_VALUE, + // update the flag's value, but *only if* it has not yet been updated + // with SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef". + SET_FLAG_IF_DEFAULT, + // set the flag's default value to this. If the flag has not yet updated + // yet (via SET_FLAGS_VALUE, SET_FLAG_IF_DEFAULT, or "FLAGS_xxx = nondef") + // change the flag's current value to the new default value as well. + SET_FLAGS_DEFAULT +}; + +// Set a particular flag ("command line option"). Returns a string +// describing the new value that the option has been set to. The +// return value API is not well-specified, so basically just depend on +// it to be empty if the setting failed for some reason -- the name is +// not a valid flag name, or the value is not a valid value -- and +// non-empty else. + +// SetCommandLineOption uses set_mode == SET_FLAGS_VALUE (the common case) +extern std::string SetCommandLineOption(const char* name, const char* value); +extern std::string SetCommandLineOptionWithMode(const char* name, const char* value, + FlagSettingMode set_mode); + + +// -------------------------------------------------------------------- +// Saves the states (value, default value, whether the user has set +// the flag, registered validators, etc) of all flags, and restores +// them when the FlagSaver is destroyed. This is very useful in +// tests, say, when you want to let your tests change the flags, but +// make sure that they get reverted to the original states when your +// test is complete. +// +// Example usage: +// void TestFoo() { +// FlagSaver s1; +// FLAG_foo = false; +// FLAG_bar = "some value"; +// +// // test happens here. You can return at any time +// // without worrying about restoring the FLAG values. +// } +// +// Note: This class is marked with __attribute__((unused)) because all the +// work is done in the constructor and destructor, so in the standard +// usage example above, the compiler would complain that it's an +// unused variable. +// +// This class is thread-safe. + +class FlagSaver { + public: + FlagSaver(); + ~FlagSaver(); + + private: + class FlagSaverImpl* impl_; // we use pimpl here to keep API steady + + FlagSaver(const FlagSaver&); // no copying! + void operator=(const FlagSaver&); +} __attribute__ ((unused)); + +// -------------------------------------------------------------------- +// Some deprecated or hopefully-soon-to-be-deprecated functions. + +// This is often used for logging. TODO(csilvers): figure out a better way +extern std::string CommandlineFlagsIntoString(); +// Usually where this is used, a FlagSaver should be used instead. +extern bool ReadFlagsFromString(const std::string& flagfilecontents, + const char* prog_name, + bool errors_are_fatal); // uses SET_FLAGS_VALUE + +// These let you manually implement --flagfile functionality. +// DEPRECATED. +extern bool AppendFlagsIntoFile(const std::string& filename, const char* prog_name); +extern bool SaveCommandFlags(); // actually defined in google.cc ! +extern bool ReadFromFlagsFile(const std::string& filename, const char* prog_name, + bool errors_are_fatal); // uses SET_FLAGS_VALUE + + +// -------------------------------------------------------------------- +// Useful routines for initializing flags from the environment. +// In each case, if 'varname' does not exist in the environment +// return defval. If 'varname' does exist but is not valid +// (e.g., not a number for an int32 flag), abort with an error. +// Otherwise, return the value. NOTE: for booleans, for true use +// 't' or 'T' or 'true' or '1', for false 'f' or 'F' or 'false' or '0'. + +extern bool BoolFromEnv(const char *varname, bool defval); +extern int32 Int32FromEnv(const char *varname, int32 defval); +extern int64 Int64FromEnv(const char *varname, int64 defval); +extern uint64 Uint64FromEnv(const char *varname, uint64 defval); +extern double DoubleFromEnv(const char *varname, double defval); +extern const char *StringFromEnv(const char *varname, const char *defval); + + +// -------------------------------------------------------------------- +// The next two functions parse commandlineflags from main(): + +// Set the "usage" message for this program. For example: +// string usage("This program does nothing. Sample usage:\n"); +// usage += argv[0] + " "; +// SetUsageMessage(usage); +// Do not include commandline flags in the usage: we do that for you! +// Thread-hostile; meant to be called before any threads are spawned. +extern void SetUsageMessage(const std::string& usage); + +// Looks for flags in argv and parses them. Rearranges argv to put +// flags first, or removes them entirely if remove_flags is true. +// If a flag is defined more than once in the command line or flag +// file, the last definition is used. +// See top-of-file for more details on this function. +#ifndef SWIG // In swig, use ParseCommandLineFlagsScript() instead. +extern uint32 ParseCommandLineFlags(int *argc, char*** argv, + bool remove_flags); +#endif + + +// Calls to ParseCommandLineNonHelpFlags and then to +// HandleCommandLineHelpFlags can be used instead of a call to +// ParseCommandLineFlags during initialization, in order to allow for +// changing default values for some FLAGS (via +// e.g. SetCommandLineOptionWithMode calls) between the time of +// command line parsing and the time of dumping help information for +// the flags as a result of command line parsing. +// If a flag is defined more than once in the command line or flag +// file, the last definition is used. +extern uint32 ParseCommandLineNonHelpFlags(int *argc, char*** argv, + bool remove_flags); +// This is actually defined in commandlineflags_reporting.cc. +// This function is misnamed (it also handles --version, etc.), but +// it's too late to change that now. :-( +extern void HandleCommandLineHelpFlags(); // in commandlineflags_reporting.cc + +// Allow command line reparsing. Disables the error normally +// generated when an unknown flag is found, since it may be found in a +// later parse. Thread-hostile; meant to be called before any threads +// are spawned. +extern void AllowCommandLineReparsing(); + +// Reparse the flags that have not yet been recognized. +// Only flags registered since the last parse will be recognized. +// Any flag value must be provided as part of the argument using "=", +// not as a separate command line argument that follows the flag argument. +// Intended for handling flags from dynamically loaded libraries, +// since their flags are not registered until they are loaded. +extern uint32 ReparseCommandLineNonHelpFlags(); + + +// -------------------------------------------------------------------- +// Now come the command line flag declaration/definition macros that +// will actually be used. They're kind of hairy. A major reason +// for this is initialization: we want people to be able to access +// variables in global constructors and have that not crash, even if +// their global constructor runs before the global constructor here. +// (Obviously, we can't guarantee the flags will have the correct +// default value in that case, but at least accessing them is safe.) +// The only way to do that is have flags point to a static buffer. +// So we make one, using a union to ensure proper alignment, and +// then use placement-new to actually set up the flag with the +// correct default value. In the same vein, we have to worry about +// flag access in global destructors, so FlagRegisterer has to be +// careful never to destroy the flag-values it constructs. +// +// Note that when we define a flag variable FLAGS_, we also +// preemptively define a junk variable, FLAGS_no. This is to +// cause a link-time error if someone tries to define 2 flags with +// names like "logging" and "nologging". We do this because a bool +// flag FLAG can be set from the command line to true with a "-FLAG" +// argument, and to false with a "-noFLAG" argument, and so this can +// potentially avert confusion. +// +// We also put flags into their own namespace. It is purposefully +// named in an opaque way that people should have trouble typing +// directly. The idea is that DEFINE puts the flag in the weird +// namespace, and DECLARE imports the flag from there into the current +// namespace. The net result is to force people to use DECLARE to get +// access to a flag, rather than saying "extern bool FLAGS_whatever;" +// or some such instead. We want this so we can put extra +// functionality (like sanity-checking) in DECLARE if we want, and +// make sure it is picked up everywhere. +// +// We also put the type of the variable in the namespace, so that +// people can't DECLARE_int32 something that they DEFINE_bool'd +// elsewhere. + +class FlagRegisterer { + public: + FlagRegisterer(const char* name, const char* type, + const char* help, const char* filename, + void* current_storage, void* defvalue_storage); +}; + +extern bool FlagsTypeWarn(const char *name); + +// If your application #defines STRIP_FLAG_HELP to a non-zero value +// before #including this file, we remove the help message from the +// binary file. This can reduce the size of the resulting binary +// somewhat, and may also be useful for security reasons. + +extern const char kStrippedFlagHelp[]; + +} + +#ifndef SWIG // In swig, ignore the main flag declarations + +#if defined(STRIP_FLAG_HELP) && STRIP_FLAG_HELP > 0 +// Need this construct to avoid the 'defined but not used' warning. +#define MAYBE_STRIPPED_HELP(txt) (false ? (txt) : kStrippedFlagHelp) +#else +#define MAYBE_STRIPPED_HELP(txt) txt +#endif + +// Each command-line flag has two variables associated with it: one +// with the current value, and one with the default value. However, +// we have a third variable, which is where value is assigned; it's a +// constant. This guarantees that FLAG_##value is initialized at +// static initialization time (e.g. before program-start) rather than +// than global construction time (which is after program-start but +// before main), at least when 'value' is a compile-time constant. We +// use a small trick for the "default value" variable, and call it +// FLAGS_no. This serves the second purpose of assuring a +// compile error if someone tries to define a flag named no +// which is illegal (--foo and --nofoo both affect the "foo" flag). +#define DEFINE_VARIABLE(type, shorttype, name, value, help) \ + namespace fL##shorttype { \ + static const type FLAGS_nono##name = value; \ + type FLAGS_##name = FLAGS_nono##name; \ + type FLAGS_no##name = FLAGS_nono##name; \ + static ::google::FlagRegisterer o_##name( \ + #name, #type, MAYBE_STRIPPED_HELP(help), __FILE__, \ + &FLAGS_##name, &FLAGS_no##name); \ + } \ + using fL##shorttype::FLAGS_##name + +#define DECLARE_VARIABLE(type, shorttype, name) \ + namespace fL##shorttype { \ + extern type FLAGS_##name; \ + } \ + using fL##shorttype::FLAGS_##name + +// For DEFINE_bool, we want to do the extra check that the passed-in +// value is actually a bool, and not a string or something that can be +// coerced to a bool. These declarations (no definition needed!) will +// help us do that, and never evaluate From, which is important. +// We'll use 'sizeof(IsBool(val))' to distinguish. This code requires +// that the compiler have different sizes for bool & double. Since +// this is not guaranteed by the standard, we check it with a +// compile-time assert (msg[-1] will give a compile-time error). +namespace fLB { +struct CompileAssert {}; +typedef CompileAssert expected_sizeof_double_neq_sizeof_bool[ + (sizeof(double) != sizeof(bool)) ? 1 : -1]; +template double IsBoolFlag(const From& from); +bool IsBoolFlag(bool from); +} // namespace fLB + +#define DECLARE_bool(name) DECLARE_VARIABLE(bool,B, name) +#define DEFINE_bool(name,val,txt) \ + namespace fLB { \ + typedef CompileAssert FLAG_##name##_value_is_not_a_bool[ \ + (sizeof(::fLB::IsBoolFlag(val)) != sizeof(double)) ? 1 : -1]; \ + } \ + DEFINE_VARIABLE(bool,B, name, val, txt) + +#define DECLARE_int32(name) DECLARE_VARIABLE(::google::int32,I, name) +#define DEFINE_int32(name,val,txt) DEFINE_VARIABLE(::google::int32,I, name, val, txt) + +#define DECLARE_int64(name) DECLARE_VARIABLE(::google::int64,I64, name) +#define DEFINE_int64(name,val,txt) DEFINE_VARIABLE(::google::int64,I64, name, val, txt) + +#define DECLARE_uint64(name) DECLARE_VARIABLE(::google::uint64,U64, name) +#define DEFINE_uint64(name,val,txt) DEFINE_VARIABLE(::google::uint64,U64, name, val, txt) + +#define DECLARE_double(name) DECLARE_VARIABLE(double,D, name) +#define DEFINE_double(name,val,txt) DEFINE_VARIABLE(double,D, name, val, txt) + +// Strings are trickier, because they're not a POD, so we can't +// construct them at static-initialization time (instead they get +// constructed at global-constructor time, which is much later). To +// try to avoid crashes in that case, we use a char buffer to store +// the string, which we can static-initialize, and then placement-new +// into it later. It's not perfect, but the best we can do. +#define DECLARE_string(name) namespace fLS { extern std::string& FLAGS_##name; } \ + using fLS::FLAGS_##name + +// We need to define a var named FLAGS_no##name so people don't define +// --string and --nostring. And we need a temporary place to put val +// so we don't have to evaluate it twice. Two great needs that go +// great together! +// The weird 'using' + 'extern' inside the fLS namespace is to work around +// an unknown compiler bug/issue with the gcc 4.2.1 on SUSE 10. See +// http://code.google.com/p/google-gflags/issues/detail?id=20 +#define DEFINE_string(name, val, txt) \ + namespace fLS { \ + static union { void* align; char s[sizeof(std::string)]; } s_##name[2]; \ + const std::string* const FLAGS_no##name = new (s_##name[0].s) std::string(val); \ + static ::google::FlagRegisterer o_##name( \ + #name, "string", MAYBE_STRIPPED_HELP(txt), __FILE__, \ + s_##name[0].s, new (s_##name[1].s) std::string(*FLAGS_no##name)); \ + extern std::string& FLAGS_##name; \ + using fLS::FLAGS_##name; \ + std::string& FLAGS_##name = *(reinterpret_cast(s_##name[0].s)); \ + } \ + using fLS::FLAGS_##name + +#endif // SWIG + +#endif // GOOGLE_GFLAGS_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/logging.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/logging.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/logging.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/logging.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,1499 @@ +// Copyright (c) 1999, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Ray Sidney +// +// This file contains #include information about logging-related stuff. +// Pretty much everybody needs to #include this file so that they can +// log various happenings. +// +#ifndef _LOGGING_H_ +#define _LOGGING_H_ + +#include +#include +#include +#include +#if 1 +# include +#endif +#ifdef __DEPRECATED +// Make GCC quiet. +# undef __DEPRECATED +# include +# define __DEPRECATED +#else +# include +#endif +#include + +// Annoying stuff for windows -- makes sure clients can import these functions +#ifndef GOOGLE_GLOG_DLL_DECL +# if defined(_WIN32) && !defined(__CYGWIN__) +# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) +# else +# define GOOGLE_GLOG_DLL_DECL +# endif +#endif + +// We care a lot about number of bits things take up. Unfortunately, +// systems define their bit-specific ints in a lot of different ways. +// We use our own way, and have a typedef to get there. +// Note: these commands below may look like "#if 1" or "#if 0", but +// that's because they were constructed that way at ./configure time. +// Look at logging.h.in to see how they're calculated (based on your config). +#if 1 +#include // the normal place uint16_t is defined +#endif +#if 1 +#include // the normal place u_int16_t is defined +#endif +#if 1 +#include // a third place for uint16_t or u_int16_t +#endif + +#if 0 +#include +#endif + +namespace google { + +#if 1 // the C99 format +typedef int32_t int32; +typedef uint32_t uint32; +typedef int64_t int64; +typedef uint64_t uint64; +#elif 1 // the BSD format +typedef int32_t int32; +typedef u_int32_t uint32; +typedef int64_t int64; +typedef u_int64_t uint64; +#elif 0 // the windows (vc7) format +typedef __int32 int32; +typedef unsigned __int32 uint32; +typedef __int64 int64; +typedef unsigned __int64 uint64; +#else +#error Do not know how to define a 32-bit integer quantity on your system +#endif + +} + +// The global value of GOOGLE_STRIP_LOG. All the messages logged to +// LOG(XXX) with severity less than GOOGLE_STRIP_LOG will not be displayed. +// If it can be determined at compile time that the message will not be +// printed, the statement will be compiled out. +// +// Example: to strip out all INFO and WARNING messages, use the value +// of 2 below. To make an exception for WARNING messages from a single +// file, add "#define GOOGLE_STRIP_LOG 1" to that file _before_ including +// base/logging.h +#ifndef GOOGLE_STRIP_LOG +#define GOOGLE_STRIP_LOG 0 +#endif + +// GCC can be told that a certain branch is not likely to be taken (for +// instance, a CHECK failure), and use that information in static analysis. +// Giving it this information can help it optimize for the common case in +// the absence of better information (ie. -fprofile-arcs). +// +#ifndef GOOGLE_PREDICT_BRANCH_NOT_TAKEN +#if 1 +#define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) (__builtin_expect(x, 0)) +#else +#define GOOGLE_PREDICT_BRANCH_NOT_TAKEN(x) x +#endif +#endif + +// Make a bunch of macros for logging. The way to log things is to stream +// things to LOG(). E.g., +// +// LOG(INFO) << "Found " << num_cookies << " cookies"; +// +// You can capture log messages in a string, rather than reporting them +// immediately: +// +// vector errors; +// LOG_STRING(ERROR, &errors) << "Couldn't parse cookie #" << cookie_num; +// +// This pushes back the new error onto 'errors'; if given a NULL pointer, +// it reports the error via LOG(ERROR). +// +// You can also do conditional logging: +// +// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// You can also do occasional logging (log every n'th occurrence of an +// event): +// +// LOG_EVERY_N(INFO, 10) << "Got the " << COUNTER << "th cookie"; +// +// The above will cause log messages to be output on the 1st, 11th, 21st, ... +// times it is executed. Note that the special COUNTER value is used to +// identify which repetition is happening. +// +// You can also do occasional conditional logging (log every n'th +// occurrence of an event, when condition is satisfied): +// +// LOG_IF_EVERY_N(INFO, (size > 1024), 10) << "Got the " << COUNTER +// << "th big cookie"; +// +// You can log messages the first N times your code executes a line. E.g. +// +// LOG_FIRST_N(INFO, 20) << "Got the " << COUNTER << "th cookie"; +// +// Outputs log messages for the first 20 times it is executed. +// +// Analogous SYSLOG, SYSLOG_IF, and SYSLOG_EVERY_N macros are available. +// These log to syslog as well as to the normal logs. If you use these at +// all, you need to be aware that syslog can drastically reduce performance, +// especially if it is configured for remote logging! Don't use these +// unless you fully understand this and have a concrete need to use them. +// Even then, try to minimize your use of them. +// +// There are also "debug mode" logging macros like the ones above: +// +// DLOG(INFO) << "Found cookies"; +// +// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies"; +// +// DLOG_EVERY_N(INFO, 10) << "Got the " << COUNTER << "th cookie"; +// +// All "debug mode" logging is compiled away to nothing for non-debug mode +// compiles. +// +// We also have +// +// LOG_ASSERT(assertion); +// DLOG_ASSERT(assertion); +// +// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion; +// +// There are "verbose level" logging macros. They look like +// +// VLOG(1) << "I'm printed when you run the program with --v=1 or more"; +// VLOG(2) << "I'm printed when you run the program with --v=2 or more"; +// +// These always log at the INFO log level (when they log at all). +// The verbose logging can also be turned on module-by-module. For instance, +// --vmodule=mapreduce=2,file=1,gfs*=3 --v=0 +// will cause: +// a. VLOG(2) and lower messages to be printed from mapreduce.{h,cc} +// b. VLOG(1) and lower messages to be printed from file.{h,cc} +// c. VLOG(3) and lower messages to be printed from files prefixed with "gfs" +// d. VLOG(0) and lower messages to be printed from elsewhere +// +// The wildcarding functionality shown by (c) supports both '*' (match +// 0 or more characters) and '?' (match any single character) wildcards. +// +// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as +// +// if (VLOG_IS_ON(2)) { +// // do some logging preparation and logging +// // that can't be accomplished with just VLOG(2) << ...; +// } +// +// There are also VLOG_IF, VLOG_EVERY_N and VLOG_IF_EVERY_N "verbose level" +// condition macros for sample cases, when some extra computation and +// preparation for logs is not needed. +// VLOG_IF(1, (size > 1024)) +// << "I'm printed when size is more than 1024 and when you run the " +// "program with --v=1 or more"; +// VLOG_EVERY_N(1, 10) +// << "I'm printed every 10th occurrence, and when you run the program " +// "with --v=1 or more. Present occurence is " << COUNTER; +// VLOG_IF_EVERY_N(1, (size > 1024), 10) +// << "I'm printed on every 10th occurence of case when size is more " +// " than 1024, when you run the program with --v=1 or more. "; +// "Present occurence is " << COUNTER; +// +// The supported severity levels for macros that allow you to specify one +// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL. +// Note that messages of a given severity are logged not only in the +// logfile for that severity, but also in all logfiles of lower severity. +// E.g., a message of severity FATAL will be logged to the logfiles of +// severity FATAL, ERROR, WARNING, and INFO. +// +// There is also the special severity of DFATAL, which logs FATAL in +// debug mode, ERROR in normal mode. +// +// Very important: logging a message at the FATAL severity level causes +// the program to terminate (after the message is logged). +// +// Unless otherwise specified, logs will be written to the filename +// "...log..", followed +// by the date, time, and pid (you can't prevent the date, time, and pid +// from being in the filename). +// +// The logging code takes two flags: +// --v=# set the verbose level +// --logtostderr log all the messages to stderr instead of to logfiles + +// LOG LINE PREFIX FORMAT +// +// Log lines have this form: +// +// Lmmdd hh:mm:ss.uuuuuu threadid file:line] msg... +// +// where the fields are defined as follows: +// +// L A single character, representing the log level +// (eg 'I' for INFO) +// mm The month (zero padded; ie May is '05') +// dd The day (zero padded) +// hh:mm:ss.uuuuuu Time in hours, minutes and fractional seconds +// threadid The space-padded thread ID as returned by GetTID() +// (this matches the PID on Linux) +// file The file name +// line The line number +// msg The user-supplied message +// +// Example: +// +// I1103 11:57:31.739339 24395 google.cc:2341] Command line: ./some_prog +// I1103 11:57:31.739403 24395 google.cc:2342] Process id 24395 +// +// NOTE: although the microseconds are useful for comparing events on +// a single machine, clocks on different machines may not be well +// synchronized. Hence, use caution when comparing the low bits of +// timestamps from different machines. + +#ifndef DECLARE_VARIABLE +#define MUST_UNDEF_GFLAGS_DECLARE_MACROS +#define DECLARE_VARIABLE(type, name, tn) \ + namespace FLAG__namespace_do_not_use_directly_use_DECLARE_##tn##_instead { \ + extern GOOGLE_GLOG_DLL_DECL type FLAGS_##name; \ + } \ + using FLAG__namespace_do_not_use_directly_use_DECLARE_##tn##_instead::FLAGS_##name + +// bool specialization +#define DECLARE_bool(name) \ + DECLARE_VARIABLE(bool, name, bool) + +// int32 specialization +#define DECLARE_int32(name) \ + DECLARE_VARIABLE(google::int32, name, int32) + +// Special case for string, because we have to specify the namespace +// std::string, which doesn't play nicely with our FLAG__namespace hackery. +#define DECLARE_string(name) \ + namespace FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead { \ + extern GOOGLE_GLOG_DLL_DECL std::string FLAGS_##name; \ + } \ + using FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead::FLAGS_##name +#endif + +// Set whether log messages go to stderr instead of logfiles +DECLARE_bool(logtostderr); + +// Set whether log messages go to stderr in addition to logfiles. +DECLARE_bool(alsologtostderr); + +// Log messages at a level >= this flag are automatically sent to +// stderr in addition to log files. +DECLARE_int32(stderrthreshold); + +// Set whether the log prefix should be prepended to each line of output. +DECLARE_bool(log_prefix); + +// Log messages at a level <= this flag are buffered. +// Log messages at a higher level are flushed immediately. +DECLARE_int32(logbuflevel); + +// Sets the maximum number of seconds which logs may be buffered for. +DECLARE_int32(logbufsecs); + +// Log suppression level: messages logged at a lower level than this +// are suppressed. +DECLARE_int32(minloglevel); + +// If specified, logfiles are written into this directory instead of the +// default logging directory. +DECLARE_string(log_dir); + +// Sets the path of the directory into which to put additional links +// to the log files. +DECLARE_string(log_link); + +DECLARE_int32(v); // in vlog_is_on.cc + +// Sets the maximum log file size (in MB). +DECLARE_int32(max_log_size); + +// Sets whether to avoid logging to the disk if the disk is full. +DECLARE_bool(stop_logging_if_full_disk); + +#ifdef MUST_UNDEF_GFLAGS_DECLARE_MACROS +#undef MUST_UNDEF_GFLAGS_DECLARE_MACROS +#undef DECLARE_VARIABLE +#undef DECLARE_bool +#undef DECLARE_int32 +#undef DECLARE_string +#endif + +// Log messages below the GOOGLE_STRIP_LOG level will be compiled away for +// security reasons. See LOG(severtiy) below. + +// A few definitions of macros that don't generate much code. Since +// LOG(INFO) and its ilk are used all over our code, it's +// better to have compact code for these operations. + +#if GOOGLE_STRIP_LOG == 0 +#define COMPACT_GOOGLE_LOG_INFO google::LogMessage( \ + __FILE__, __LINE__) +#define LOG_TO_STRING_INFO(message) google::LogMessage( \ + __FILE__, __LINE__, google::INFO, message) +#else +#define COMPACT_GOOGLE_LOG_INFO google::NullStream() +#define LOG_TO_STRING_INFO(message) google::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 1 +#define COMPACT_GOOGLE_LOG_WARNING google::LogMessage( \ + __FILE__, __LINE__, google::WARNING) +#define LOG_TO_STRING_WARNING(message) google::LogMessage( \ + __FILE__, __LINE__, google::WARNING, message) +#else +#define COMPACT_GOOGLE_LOG_WARNING google::NullStream() +#define LOG_TO_STRING_WARNING(message) google::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 2 +#define COMPACT_GOOGLE_LOG_ERROR google::LogMessage( \ + __FILE__, __LINE__, google::ERROR) +#define LOG_TO_STRING_ERROR(message) google::LogMessage( \ + __FILE__, __LINE__, google::ERROR, message) +#else +#define COMPACT_GOOGLE_LOG_ERROR google::NullStream() +#define LOG_TO_STRING_ERROR(message) google::NullStream() +#endif + +#if GOOGLE_STRIP_LOG <= 3 +#define COMPACT_GOOGLE_LOG_FATAL google::LogMessageFatal( \ + __FILE__, __LINE__) +#define LOG_TO_STRING_FATAL(message) google::LogMessage( \ + __FILE__, __LINE__, google::FATAL, message) +#else +#define COMPACT_GOOGLE_LOG_FATAL google::NullStreamFatal() +#define LOG_TO_STRING_FATAL(message) google::NullStreamFatal() +#endif + +// For DFATAL, we want to use LogMessage (as opposed to +// LogMessageFatal), to be consistent with the original behavior. +#ifdef NDEBUG +#define COMPACT_GOOGLE_LOG_DFATAL COMPACT_GOOGLE_LOG_ERROR +#elif GOOGLE_STRIP_LOG <= 3 +#define COMPACT_GOOGLE_LOG_DFATAL LogMessage( \ + __FILE__, __LINE__, google::FATAL) +#else +#define COMPACT_GOOGLE_LOG_DFATAL google::NullStreamFatal() +#endif + +#define GOOGLE_LOG_INFO(counter) google::LogMessage(__FILE__, __LINE__, google::INFO, counter, &google::LogMessage::SendToLog) +#define SYSLOG_INFO(counter) \ + google::LogMessage(__FILE__, __LINE__, google::INFO, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_WARNING(counter) \ + google::LogMessage(__FILE__, __LINE__, google::WARNING, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_WARNING(counter) \ + google::LogMessage(__FILE__, __LINE__, google::WARNING, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_ERROR(counter) \ + google::LogMessage(__FILE__, __LINE__, google::ERROR, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_ERROR(counter) \ + google::LogMessage(__FILE__, __LINE__, google::ERROR, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_FATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::FATAL, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_FATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::FATAL, counter, \ + &google::LogMessage::SendToSyslogAndLog) +#define GOOGLE_LOG_DFATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::DFATAL_LEVEL, counter, \ + &google::LogMessage::SendToLog) +#define SYSLOG_DFATAL(counter) \ + google::LogMessage(__FILE__, __LINE__, google::DFATAL_LEVEL, counter, \ + &google::LogMessage::SendToSyslogAndLog) + +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) || defined(__CYGWIN32__) +// A very useful logging macro to log windows errors: +#define LOG_SYSRESULT(result) \ + if (FAILED(result)) { \ + LPTSTR message = NULL; \ + LPTSTR msg = reinterpret_cast(&message); \ + DWORD message_length = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | \ + FORMAT_MESSAGE_FROM_SYSTEM, \ + 0, result, 0, msg, 100, NULL); \ + if (message_length > 0) { \ + google::LogMessage(__FILE__, __LINE__, ERROR, 0, \ + &google::LogMessage::SendToLog).stream() << message; \ + LocalFree(message); \ + } \ + } +#endif + +// We use the preprocessor's merging operator, "##", so that, e.g., +// LOG(INFO) becomes the token GOOGLE_LOG_INFO. There's some funny +// subtle difference between ostream member streaming functions (e.g., +// ostream::operator<<(int) and ostream non-member streaming functions +// (e.g., ::operator<<(ostream&, string&): it turns out that it's +// impossible to stream something like a string directly to an unnamed +// ostream. We employ a neat hack by calling the stream() member +// function of LogMessage which seems to avoid the problem. +#define LOG(severity) COMPACT_GOOGLE_LOG_ ## severity.stream() +#define SYSLOG(severity) SYSLOG_ ## severity(0).stream() + +namespace google { + +// They need the definitions of integer types. +#include "glog/log_severity.h" +#include "glog/vlog_is_on.h" + +// Initialize google's logging library. You will see the program name +// specified by argv0 in log outputs. +GOOGLE_GLOG_DLL_DECL void InitGoogleLogging(const char* argv0); + +// Install a function which will be called after LOG(FATAL). +GOOGLE_GLOG_DLL_DECL void InstallFailureFunction(void (*fail_func)()); + +class LogSink; // defined below + +// If a non-NULL sink pointer is given, we push this message to that sink. +// For LOG_TO_SINK we then do normal LOG(severity) logging as well. +// This is useful for capturing messages and passing/storing them +// somewhere more specific than the global log of the process. +// Argument types: +// LogSink* sink; +// LogSeverity severity; +// The cast is to disambiguate NULL arguments. +#define LOG_TO_SINK(sink, severity) \ + google::LogMessage( \ + __FILE__, __LINE__, \ + google::severity, \ + static_cast(sink), true).stream() +#define LOG_TO_SINK_BUT_NOT_TO_LOGFILE(sink, severity) \ + google::LogMessage( \ + __FILE__, __LINE__, \ + google::severity, \ + static_cast(sink), false).stream() + +// If a non-NULL string pointer is given, we write this message to that string. +// We then do normal LOG(severity) logging as well. +// This is useful for capturing messages and storing them somewhere more +// specific than the global log of the process. +// Argument types: +// string* message; +// LogSeverity severity; +// The cast is to disambiguate NULL arguments. +// NOTE: LOG(severity) expands to LogMessage().stream() for the specified +// severity. +#define LOG_TO_STRING(severity, message) \ + LOG_TO_STRING_##severity(static_cast(message)).stream() + +// If a non-NULL pointer is given, we push the message onto the end +// of a vector of strings; otherwise, we report it with LOG(severity). +// This is handy for capturing messages and perhaps passing them back +// to the caller, rather than reporting them immediately. +// Argument types: +// LogSeverity severity; +// vector *outvec; +// The cast is to disambiguate NULL arguments. +#define LOG_STRING(severity, outvec) \ + LOG_TO_STRING_##severity(static_cast*>(outvec)).stream() + +#define LOG_IF(severity, condition) \ + !(condition) ? (void) 0 : google::LogMessageVoidify() & LOG(severity) +#define SYSLOG_IF(severity, condition) \ + !(condition) ? (void) 0 : google::LogMessageVoidify() & SYSLOG(severity) + +#define LOG_ASSERT(condition) \ + LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition +#define SYSLOG_ASSERT(condition) \ + SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition + +// CHECK dies with a fatal error if condition is not true. It is *not* +// controlled by NDEBUG, so the check will be executed regardless of +// compilation mode. Therefore, it is safe to do things like: +// CHECK(fp->Write(x) == 4) +#define CHECK(condition) \ + LOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \ + << "Check failed: " #condition " " + +// A container for a string pointer which can be evaluated to a bool - +// true iff the pointer is NULL. +struct CheckOpString { + CheckOpString(std::string* str) : str_(str) { } + // No destructor: if str_ is non-NULL, we're about to LOG(FATAL), + // so there's no point in cleaning up str_. + operator bool() const { + return GOOGLE_PREDICT_BRANCH_NOT_TAKEN(str_ != NULL); + } + std::string* str_; +}; + +// Function is overloaded for integral types to allow static const +// integrals declared in classes and not defined to be used as arguments to +// CHECK* macros. It's not encouraged though. +template +inline const T& GetReferenceableValue(const T& t) { return t; } +inline char GetReferenceableValue(char t) { return t; } +inline unsigned char GetReferenceableValue(unsigned char t) { return t; } +inline signed char GetReferenceableValue(signed char t) { return t; } +inline short GetReferenceableValue(short t) { return t; } +inline unsigned short GetReferenceableValue(unsigned short t) { return t; } +inline int GetReferenceableValue(int t) { return t; } +inline unsigned int GetReferenceableValue(unsigned int t) { return t; } +inline long GetReferenceableValue(long t) { return t; } +inline unsigned long GetReferenceableValue(unsigned long t) { return t; } +inline long long GetReferenceableValue(long long t) { return t; } +inline unsigned long long GetReferenceableValue(unsigned long long t) { + return t; +} + +// This is a dummy class to define the following operator. +struct DummyClassToDefineOperator {}; + +} + +// Define global operator<< to declare using ::operator<<. +// This declaration will allow use to use CHECK macros for user +// defined classes which have operator<< (e.g., stl_logging.h). +inline std::ostream& operator<<( + std::ostream& out, const google::DummyClassToDefineOperator&) { + return out; +} + +namespace google { + +// Build the error message string. +template +std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) { + // It means that we cannot use stl_logging if compiler doesn't + // support using expression for operator. + // TODO(hamaji): Figure out a way to fix. +#if 1 + using ::operator<<; +#endif + std::strstream ss; + ss << names << " (" << v1 << " vs. " << v2 << ")"; + return new std::string(ss.str(), ss.pcount()); +} + +// Helper functions for CHECK_OP macro. +// The (int, int) specialization works around the issue that the compiler +// will not instantiate the template version of the function on values of +// unnamed enum type - see comment below. +#define DEFINE_CHECK_OP_IMPL(name, op) \ + template \ + inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \ + const char* names) { \ + if (v1 op v2) return NULL; \ + else return MakeCheckOpString(v1, v2, names); \ + } \ + inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \ + return Check##name##Impl(v1, v2, names); \ + } + +// Use _EQ, _NE, _LE, etc. in case the file including base/logging.h +// provides its own #defines for the simpler names EQ, NE, LE, etc. +// This happens if, for example, those are used as token names in a +// yacc grammar. +DEFINE_CHECK_OP_IMPL(_EQ, ==) +DEFINE_CHECK_OP_IMPL(_NE, !=) +DEFINE_CHECK_OP_IMPL(_LE, <=) +DEFINE_CHECK_OP_IMPL(_LT, < ) +DEFINE_CHECK_OP_IMPL(_GE, >=) +DEFINE_CHECK_OP_IMPL(_GT, > ) +#undef DEFINE_CHECK_OP_IMPL + +// Helper macro for binary operators. +// Don't use this macro directly in your code, use CHECK_EQ et al below. + +#if defined(STATIC_ANALYSIS) +// Only for static analysis tool to know that it is equivalent to assert +#define CHECK_OP_LOG(name, op, val1, val2, log) CHECK((val1) op (val2)) +#elif !defined(NDEBUG) +// In debug mode, avoid constructing CheckOpStrings if possible, +// to reduce the overhead of CHECK statments by 2x. +// Real DCHECK-heavy tests have seen 1.5x speedups. + +// The meaning of "string" might be different between now and +// when this macro gets invoked (e.g., if someone is experimenting +// with other string implementations that get defined after this +// file is included). Save the current meaning now and use it +// in the macro. +typedef std::string _Check_string; +#define CHECK_OP_LOG(name, op, val1, val2, log) \ + while (google::_Check_string* _result = \ + google::Check##name##Impl( \ + google::GetReferenceableValue(val1), \ + google::GetReferenceableValue(val2), \ + #val1 " " #op " " #val2)) \ + log(__FILE__, __LINE__, \ + google::CheckOpString(_result)).stream() +#else +// In optimized mode, use CheckOpString to hint to compiler that +// the while condition is unlikely. +#define CHECK_OP_LOG(name, op, val1, val2, log) \ + while (google::CheckOpString _result = \ + google::Check##name##Impl(GetReferenceableValue(val1), \ + GetReferenceableValue(val2), \ + #val1 " " #op " " #val2)) \ + log(__FILE__, __LINE__, _result).stream() +#endif // STATIC_ANALYSIS, !NDEBUG + +#if GOOGLE_STRIP_LOG <= 3 +#define CHECK_OP(name, op, val1, val2) \ + CHECK_OP_LOG(name, op, val1, val2, google::LogMessageFatal) +#else +#define CHECK_OP(name, op, val1, val2) \ + CHECK_OP_LOG(name, op, val1, val2, google::NullStreamFatal) +#endif // STRIP_LOG <= 3 + +// Equality/Inequality checks - compare two values, and log a FATAL message +// including the two values when the result is not as expected. The values +// must have operator<<(ostream, ...) defined. +// +// You may append to the error message like so: +// CHECK_NE(1, 2) << ": The world must be ending!"; +// +// We are very careful to ensure that each argument is evaluated exactly +// once, and that anything which is legal to pass as a function argument is +// legal here. In particular, the arguments may be temporary expressions +// which will end up being destroyed at the end of the apparent statement, +// for example: +// CHECK_EQ(string("abc")[1], 'b'); +// +// WARNING: These don't compile correctly if one of the arguments is a pointer +// and the other is NULL. To work around this, simply static_cast NULL to the +// type of the desired pointer. + +#define CHECK_EQ(val1, val2) CHECK_OP(_EQ, ==, val1, val2) +#define CHECK_NE(val1, val2) CHECK_OP(_NE, !=, val1, val2) +#define CHECK_LE(val1, val2) CHECK_OP(_LE, <=, val1, val2) +#define CHECK_LT(val1, val2) CHECK_OP(_LT, < , val1, val2) +#define CHECK_GE(val1, val2) CHECK_OP(_GE, >=, val1, val2) +#define CHECK_GT(val1, val2) CHECK_OP(_GT, > , val1, val2) + +// Check that the input is non NULL. This very useful in constructor +// initializer lists. + +#define CHECK_NOTNULL(val) \ + google::CheckNotNull(__FILE__, __LINE__, "'" #val "' Must be non NULL", (val)) + +// Helper functions for string comparisons. +// To avoid bloat, the definitions are in logging.cc. +#define DECLARE_CHECK_STROP_IMPL(func, expected) \ + GOOGLE_GLOG_DLL_DECL std::string* Check##func##expected##Impl( \ + const char* s1, const char* s2, const char* names); +DECLARE_CHECK_STROP_IMPL(strcmp, true) +DECLARE_CHECK_STROP_IMPL(strcmp, false) +DECLARE_CHECK_STROP_IMPL(strcasecmp, true) +DECLARE_CHECK_STROP_IMPL(strcasecmp, false) +#undef DECLARE_CHECK_STROP_IMPL + +// Helper macro for string comparisons. +// Don't use this macro directly in your code, use CHECK_STREQ et al below. +#define CHECK_STROP(func, op, expected, s1, s2) \ + while (google::CheckOpString _result = \ + google::Check##func##expected##Impl((s1), (s2), \ + #s1 " " #op " " #s2)) \ + LOG(FATAL) << *_result.str_ + + +// String (char*) equality/inequality checks. +// CASE versions are case-insensitive. +// +// Note that "s1" and "s2" may be temporary strings which are destroyed +// by the compiler at the end of the current "full expression" +// (e.g. CHECK_STREQ(Foo().c_str(), Bar().c_str())). + +#define CHECK_STREQ(s1, s2) CHECK_STROP(strcmp, ==, true, s1, s2) +#define CHECK_STRNE(s1, s2) CHECK_STROP(strcmp, !=, false, s1, s2) +#define CHECK_STRCASEEQ(s1, s2) CHECK_STROP(strcasecmp, ==, true, s1, s2) +#define CHECK_STRCASENE(s1, s2) CHECK_STROP(strcasecmp, !=, false, s1, s2) + +#define CHECK_INDEX(I,A) CHECK(I < (sizeof(A)/sizeof(A[0]))) +#define CHECK_BOUND(B,A) CHECK(B <= (sizeof(A)/sizeof(A[0]))) + +#define CHECK_DOUBLE_EQ(val1, val2) \ + do { \ + CHECK_LE((val1), (val2)+0.000000000000001L); \ + CHECK_GE((val1), (val2)-0.000000000000001L); \ + } while (0) + +#define CHECK_NEAR(val1, val2, margin) \ + do { \ + CHECK_LE((val1), (val2)+(margin)); \ + CHECK_GE((val1), (val2)-(margin)); \ + } while (0) + +// perror()..googly style! +// +// PLOG() and PLOG_IF() and PCHECK() behave exactly like their LOG* and +// CHECK equivalents with the addition that they postpend a description +// of the current state of errno to their output lines. + +#define PLOG(severity) GOOGLE_PLOG(severity, 0).stream() + +#define GOOGLE_PLOG(severity, counter) \ + google::ErrnoLogMessage( \ + __FILE__, __LINE__, google::severity, counter, \ + &google::LogMessage::SendToLog) + +#define PLOG_IF(severity, condition) \ + !(condition) ? (void) 0 : google::LogMessageVoidify() & PLOG(severity) + +// A CHECK() macro that postpends errno if the condition is false. E.g. +// +// if (poll(fds, nfds, timeout) == -1) { PCHECK(errno == EINTR); ... } +#define PCHECK(condition) \ + PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN(!(condition))) \ + << "Check failed: " #condition " " + +// A CHECK() macro that lets you assert the success of a function that +// returns -1 and sets errno in case of an error. E.g. +// +// CHECK_ERR(mkdir(path, 0700)); +// +// or +// +// int fd = open(filename, flags); CHECK_ERR(fd) << ": open " << filename; +#define CHECK_ERR(invocation) \ +PLOG_IF(FATAL, GOOGLE_PREDICT_BRANCH_NOT_TAKEN((invocation) == -1)) \ + << #invocation + +// Use macro expansion to create, for each use of LOG_EVERY_N(), static +// variables with the __LINE__ expansion as part of the variable name. +#define LOG_EVERY_N_VARNAME(base, line) LOG_EVERY_N_VARNAME_CONCAT(base, line) +#define LOG_EVERY_N_VARNAME_CONCAT(base, line) base ## line + +#define LOG_OCCURRENCES LOG_EVERY_N_VARNAME(occurrences_, __LINE__) +#define LOG_OCCURRENCES_MOD_N LOG_EVERY_N_VARNAME(occurrences_mod_n_, __LINE__) + +#define SOME_KIND_OF_LOG_EVERY_N(severity, n, what_to_do) \ + static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ + ++LOG_OCCURRENCES; \ + if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \ + if (LOG_OCCURRENCES_MOD_N == 1) \ + google::LogMessage( \ + __FILE__, __LINE__, google::severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +#define SOME_KIND_OF_LOG_IF_EVERY_N(severity, condition, n, what_to_do) \ + static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ + ++LOG_OCCURRENCES; \ + if (condition && \ + ((LOG_OCCURRENCES_MOD_N=(LOG_OCCURRENCES_MOD_N + 1) % n) == (1 % n))) \ + google::LogMessage( \ + __FILE__, __LINE__, google::severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +#define SOME_KIND_OF_PLOG_EVERY_N(severity, n, what_to_do) \ + static int LOG_OCCURRENCES = 0, LOG_OCCURRENCES_MOD_N = 0; \ + ++LOG_OCCURRENCES; \ + if (++LOG_OCCURRENCES_MOD_N > n) LOG_OCCURRENCES_MOD_N -= n; \ + if (LOG_OCCURRENCES_MOD_N == 1) \ + google::ErrnoLogMessage( \ + __FILE__, __LINE__, google::severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +#define SOME_KIND_OF_LOG_FIRST_N(severity, n, what_to_do) \ + static int LOG_OCCURRENCES = 0; \ + if (LOG_OCCURRENCES <= n) \ + ++LOG_OCCURRENCES; \ + if (LOG_OCCURRENCES <= n) \ + google::LogMessage( \ + __FILE__, __LINE__, google::severity, LOG_OCCURRENCES, \ + &what_to_do).stream() + +namespace glog_internal_namespace_ { +template +struct CompileAssert { +}; +struct CrashReason; +} // namespace glog_internal_namespace_ + +#define GOOGLE_GLOG_COMPILE_ASSERT(expr, msg) \ + typedef google::glog_internal_namespace_::CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] + +#define LOG_EVERY_N(severity, n) \ + GOOGLE_GLOG_COMPILE_ASSERT(google::severity < \ + google::NUM_SEVERITIES, \ + INVALID_REQUESTED_LOG_SEVERITY); \ + SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToLog) + +#define SYSLOG_EVERY_N(severity, n) \ + SOME_KIND_OF_LOG_EVERY_N(severity, (n), google::LogMessage::SendToSyslogAndLog) + +#define PLOG_EVERY_N(severity, n) \ + SOME_KIND_OF_PLOG_EVERY_N(severity, (n), google::LogMessage::SendToLog) + +#define LOG_FIRST_N(severity, n) \ + SOME_KIND_OF_LOG_FIRST_N(severity, (n), google::LogMessage::SendToLog) + +#define LOG_IF_EVERY_N(severity, condition, n) \ + SOME_KIND_OF_LOG_IF_EVERY_N(severity, (condition), (n), google::LogMessage::SendToLog) + +// We want the special COUNTER value available for LOG_EVERY_X()'ed messages +enum PRIVATE_Counter {COUNTER}; + + +// Plus some debug-logging macros that get compiled to nothing for production + +#ifndef NDEBUG + +#define DLOG(severity) LOG(severity) +#define DVLOG(verboselevel) VLOG(verboselevel) +#define DLOG_IF(severity, condition) LOG_IF(severity, condition) +#define DLOG_EVERY_N(severity, n) LOG_EVERY_N(severity, n) +#define DLOG_IF_EVERY_N(severity, condition, n) \ + LOG_IF_EVERY_N(severity, condition, n) +#define DLOG_ASSERT(condition) LOG_ASSERT(condition) + +// debug-only checking. not executed in NDEBUG mode. +#define DCHECK(condition) CHECK(condition) +#define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2) +#define DCHECK_NE(val1, val2) CHECK_NE(val1, val2) +#define DCHECK_LE(val1, val2) CHECK_LE(val1, val2) +#define DCHECK_LT(val1, val2) CHECK_LT(val1, val2) +#define DCHECK_GE(val1, val2) CHECK_GE(val1, val2) +#define DCHECK_GT(val1, val2) CHECK_GT(val1, val2) +#define DCHECK_STREQ(str1, str2) CHECK_STREQ(str1, str2) +#define DCHECK_STRCASEEQ(str1, str2) CHECK_STRCASEEQ(str1, str2) +#define DCHECK_STRNE(str1, str2) CHECK_STRNE(str1, str2) +#define DCHECK_STRCASENE(str1, str2) CHECK_STRCASENE(str1, str2) + +#else // NDEBUG + +#define DLOG(severity) \ + true ? (void) 0 : google::LogMessageVoidify() & LOG(severity) + +#define DVLOG(verboselevel) \ + (true || !VLOG_IS_ON(verboselevel)) ?\ + (void) 0 : google::LogMessageVoidify() & LOG(INFO) + +#define DLOG_IF(severity, condition) \ + (true || !(condition)) ? (void) 0 : google::LogMessageVoidify() & LOG(severity) + +#define DLOG_EVERY_N(severity, n) \ + true ? (void) 0 : google::LogMessageVoidify() & LOG(severity) + +#define DLOG_IF_EVERY_N(severity, condition, n) \ + (true || !(condition))? (void) 0 : google::LogMessageVoidify() & LOG(severity) + +#define DLOG_ASSERT(condition) \ + true ? (void) 0 : LOG_ASSERT(condition) + +#define DCHECK(condition) \ + while (false) \ + CHECK(condition) + +#define DCHECK_EQ(val1, val2) \ + while (false) \ + CHECK_EQ(val1, val2) + +#define DCHECK_NE(val1, val2) \ + while (false) \ + CHECK_NE(val1, val2) + +#define DCHECK_LE(val1, val2) \ + while (false) \ + CHECK_LE(val1, val2) + +#define DCHECK_LT(val1, val2) \ + while (false) \ + CHECK_LT(val1, val2) + +#define DCHECK_GE(val1, val2) \ + while (false) \ + CHECK_GE(val1, val2) + +#define DCHECK_GT(val1, val2) \ + while (false) \ + CHECK_GT(val1, val2) + +#define DCHECK_STREQ(str1, str2) \ + while (false) \ + CHECK_STREQ(str1, str2) + +#define DCHECK_STRCASEEQ(str1, str2) \ + while (false) \ + CHECK_STRCASEEQ(str1, str2) + +#define DCHECK_STRNE(str1, str2) \ + while (false) \ + CHECK_STRNE(str1, str2) + +#define DCHECK_STRCASENE(str1, str2) \ + while (false) \ + CHECK_STRCASENE(str1, str2) + + +#endif // NDEBUG + +// Log only in verbose mode. + +#define VLOG(verboselevel) LOG_IF(INFO, VLOG_IS_ON(verboselevel)) + +#define VLOG_IF(verboselevel, condition) \ + LOG_IF(INFO, (condition) && VLOG_IS_ON(verboselevel)) + +#define VLOG_EVERY_N(verboselevel, n) \ + LOG_IF_EVERY_N(INFO, VLOG_IS_ON(verboselevel), n) + +#define VLOG_IF_EVERY_N(verboselevel, condition, n) \ + LOG_IF_EVERY_N(INFO, (condition) && VLOG_IS_ON(verboselevel), n) + +// +// This class more or less represents a particular log message. You +// create an instance of LogMessage and then stream stuff to it. +// When you finish streaming to it, ~LogMessage is called and the +// full message gets streamed to the appropriate destination. +// +// You shouldn't actually use LogMessage's constructor to log things, +// though. You should use the LOG() macro (and variants thereof) +// above. +class GOOGLE_GLOG_DLL_DECL LogMessage { +public: + enum { + // Passing kNoLogPrefix for the line number disables the + // log-message prefix. Useful for using the LogMessage + // infrastructure as a printing utility. See also the --log_prefix + // flag for controlling the log-message prefix on an + // application-wide basis. + kNoLogPrefix = -1 + }; + + // LogStream inherit from non-DLL-exported class (std::ostrstream) + // and VC++ produces a warning for this situation. + // However, MSDN says "C4275 can be ignored in Microsoft Visual C++ + // 2005 if you are deriving from a type in the Standard C++ Library" + // http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx + // Let's just ignore the warning. +#ifdef _MSC_VER +# pragma warning(disable: 4275) +#endif + class GOOGLE_GLOG_DLL_DECL LogStream : public std::ostrstream { +#ifdef _MSC_VER +# pragma warning(default: 4275) +#endif + public: + LogStream(char *buf, int len, int ctr) + : ostrstream(buf, len), + ctr_(ctr) { + self_ = this; + } + + int ctr() const { return ctr_; } + void set_ctr(int ctr) { ctr_ = ctr; } + LogStream* self() const { return self_; } + + private: + int ctr_; // Counter hack (for the LOG_EVERY_X() macro) + LogStream *self_; // Consistency check hack + }; + +public: + // icc 8 requires this typedef to avoid an internal compiler error. + typedef void (LogMessage::*SendMethod)(); + + LogMessage(const char* file, int line, LogSeverity severity, int ctr, + SendMethod send_method); + + // Two special constructors that generate reduced amounts of code at + // LOG call sites for common cases. + + // Used for LOG(INFO): Implied are: + // severity = INFO, ctr = 0, send_method = &LogMessage::SendToLog. + // + // Using this constructor instead of the more complex constructor above + // saves 19 bytes per call site. + LogMessage(const char* file, int line); + + // Used for LOG(severity) where severity != INFO. Implied + // are: ctr = 0, send_method = &LogMessage::SendToLog + // + // Using this constructor instead of the more complex constructor above + // saves 17 bytes per call site. + LogMessage(const char* file, int line, LogSeverity severity); + + // Constructor to log this message to a specified sink (if not NULL). + // Implied are: ctr = 0, send_method = &LogMessage::SendToSinkAndLog if + // also_send_to_log is true, send_method = &LogMessage::SendToSink otherwise. + LogMessage(const char* file, int line, LogSeverity severity, LogSink* sink, + bool also_send_to_log); + + // Constructor where we also give a vector pointer + // for storing the messages (if the pointer is not NULL). + // Implied are: ctr = 0, send_method = &LogMessage::SaveOrSendToLog. + LogMessage(const char* file, int line, LogSeverity severity, + std::vector* outvec); + + // Constructor where we also give a string pointer for storing the + // message (if the pointer is not NULL). Implied are: ctr = 0, + // send_method = &LogMessage::WriteToStringAndLog. + LogMessage(const char* file, int line, LogSeverity severity, + std::string* message); + + // A special constructor used for check failures + LogMessage(const char* file, int line, const CheckOpString& result); + + ~LogMessage(); + + // Flush a buffered message to the sink set in the constructor. Always + // called by the destructor, it may also be called from elsewhere if + // needed. Only the first call is actioned; any later ones are ignored. + void Flush(); + + // An arbitrary limit on the length of a single log message. This + // is so that streaming can be done more efficiently. + static const size_t kMaxLogMessageLen; + + // Theses should not be called directly outside of logging.*, + // only passed as SendMethod arguments to other LogMessage methods: + void SendToLog(); // Actually dispatch to the logs + void SendToSyslogAndLog(); // Actually dispatch to syslog and the logs + + // Call abort() or similar to perform LOG(FATAL) crash. + static void Fail() __attribute__ ((noreturn)); + + std::ostream& stream() { return *(data_->stream_); } + + int preserved_errno() const { return data_->preserved_errno_; } + + // Must be called without the log_mutex held. (L < log_mutex) + static int64 num_messages(int severity); + +private: + // Fully internal SendMethod cases: + void SendToSinkAndLog(); // Send to sink if provided and dispatch to the logs + void SendToSink(); // Send to sink if provided, do nothing otherwise. + + // Write to string if provided and dispatch to the logs. + void WriteToStringAndLog(); + + void SaveOrSendToLog(); // Save to stringvec if provided, else to logs + + void Init(const char* file, int line, LogSeverity severity, + void (LogMessage::*send_method)()); + + // Used to fill in crash information during LOG(FATAL) failures. + void RecordCrashReason(glog_internal_namespace_::CrashReason* reason); + + // Counts of messages sent at each priority: + static int64 num_messages_[NUM_SEVERITIES]; // under log_mutex + + // We keep the data in a separate struct so that each instance of + // LogMessage uses less stack space. + struct GOOGLE_GLOG_DLL_DECL LogMessageData { + LogMessageData() {}; + + int preserved_errno_; // preserved errno + char* buf_; + char* message_text_; // Complete message text (points to selected buffer) + LogStream* stream_alloc_; + LogStream* stream_; + char severity_; // What level is this LogMessage logged at? + int line_; // line number where logging call is. + void (LogMessage::*send_method_)(); // Call this in destructor to send + union { // At most one of these is used: union to keep the size low. + LogSink* sink_; // NULL or sink to send message to + std::vector* outvec_; // NULL or vector to push message onto + std::string* message_; // NULL or string to write message into + }; + time_t timestamp_; // Time of creation of LogMessage + struct ::tm tm_time_; // Time of creation of LogMessage + size_t num_prefix_chars_; // # of chars of prefix in this message + size_t num_chars_to_log_; // # of chars of msg to send to log + size_t num_chars_to_syslog_; // # of chars of msg to send to syslog + const char* basename_; // basename of file that called LOG + const char* fullname_; // fullname of file that called LOG + bool has_been_flushed_; // false => data has not been flushed + bool first_fatal_; // true => this was first fatal msg + + ~LogMessageData(); + private: + LogMessageData(const LogMessageData&); + void operator=(const LogMessageData&); + }; + + static LogMessageData fatal_msg_data_exclusive_; + static LogMessageData fatal_msg_data_shared_; + + LogMessageData* allocated_; + LogMessageData* data_; + + friend class LogDestination; + + LogMessage(const LogMessage&); + void operator=(const LogMessage&); +}; + +// This class happens to be thread-hostile because all instances share +// a single data buffer, but since it can only be created just before +// the process dies, we don't worry so much. +class GOOGLE_GLOG_DLL_DECL LogMessageFatal : public LogMessage { + public: + LogMessageFatal(const char* file, int line); + LogMessageFatal(const char* file, int line, const CheckOpString& result); + ~LogMessageFatal() __attribute__ ((noreturn)); +}; + +// A non-macro interface to the log facility; (useful +// when the logging level is not a compile-time constant). +inline void LogAtLevel(int const severity, std::string const &msg) { + LogMessage(__FILE__, __LINE__, severity).stream() << msg; +} + +// A macro alternative of LogAtLevel. New code may want to use this +// version since there are two advantages: 1. this version outputs the +// file name and the line number where this macro is put like other +// LOG macros, 2. this macro can be used as C++ stream. +#define LOG_AT_LEVEL(severity) LogMessage(__FILE__, __LINE__, severity).stream() + +// A small helper for CHECK_NOTNULL(). +template +T* CheckNotNull(const char *file, int line, const char *names, T* t) { + if (t == NULL) { + LogMessageFatal(file, line, new std::string(names)); + } + return t; +} + +// Allow folks to put a counter in the LOG_EVERY_X()'ed messages. This +// only works if ostream is a LogStream. If the ostream is not a +// LogStream you'll get an assert saying as much at runtime. +GOOGLE_GLOG_DLL_DECL std::ostream& operator<<(std::ostream &os, + const PRIVATE_Counter&); + + +// Derived class for PLOG*() above. +class GOOGLE_GLOG_DLL_DECL ErrnoLogMessage : public LogMessage { + public: + + ErrnoLogMessage(const char* file, int line, LogSeverity severity, int ctr, + void (LogMessage::*send_method)()); + + // Postpends ": strerror(errno) [errno]". + ~ErrnoLogMessage(); + + private: + ErrnoLogMessage(const ErrnoLogMessage&); + void operator=(const ErrnoLogMessage&); +}; + + +// This class is used to explicitly ignore values in the conditional +// logging macros. This avoids compiler warnings like "value computed +// is not used" and "statement has no effect". + +class GOOGLE_GLOG_DLL_DECL LogMessageVoidify { + public: + LogMessageVoidify() { } + // This has to be an operator with a precedence lower than << but + // higher than ?: + void operator&(std::ostream&) { } +}; + + +// Flushes all log files that contains messages that are at least of +// the specified severity level. Thread-safe. +GOOGLE_GLOG_DLL_DECL void FlushLogFiles(LogSeverity min_severity); + +// Flushes all log files that contains messages that are at least of +// the specified severity level. Thread-hostile because it ignores +// locking -- used for catastrophic failures. +GOOGLE_GLOG_DLL_DECL void FlushLogFilesUnsafe(LogSeverity min_severity); + +// +// Set the destination to which a particular severity level of log +// messages is sent. If base_filename is "", it means "don't log this +// severity". Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetLogDestination(LogSeverity severity, + const char* base_filename); + +// +// Set the basename of the symlink to the latest log file at a given +// severity. If symlink_basename is empty, do not make a symlink. If +// you don't call this function, the symlink basename is the +// invocation name of the program. Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetLogSymlink(LogSeverity severity, + const char* symlink_basename); + +// +// Used to send logs to some other kind of destination +// Users should subclass LogSink and override send to do whatever they want. +// Implementations must be thread-safe because a shared instance will +// be called from whichever thread ran the LOG(XXX) line. +class GOOGLE_GLOG_DLL_DECL LogSink { + public: + virtual ~LogSink(); + + // Sink's logging logic (message_len is such as to exclude '\n' at the end). + // This method can't use LOG() or CHECK() as logging system mutex(s) are held + // during this call. + virtual void send(LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len) = 0; + + // Redefine this to implement waiting for + // the sink's logging logic to complete. + // It will be called after each send() returns, + // but before that LogMessage exits or crashes. + // By default this function does nothing. + // Using this function one can implement complex logic for send() + // that itself involves logging; and do all this w/o causing deadlocks and + // inconsistent rearrangement of log messages. + // E.g. if a LogSink has thread-specific actions, the send() method + // can simply add the message to a queue and wake up another thread that + // handles real logging while itself making some LOG() calls; + // WaitTillSent() can be implemented to wait for that logic to complete. + // See our unittest for an example. + virtual void WaitTillSent(); + + // Returns the normal text output of the log message. + // Can be useful to implement send(). + static std::string ToString(LogSeverity severity, const char* file, int line, + const struct ::tm* tm_time, + const char* message, size_t message_len); +}; + +// Add or remove a LogSink as a consumer of logging data. Thread-safe. +GOOGLE_GLOG_DLL_DECL void AddLogSink(LogSink *destination); +GOOGLE_GLOG_DLL_DECL void RemoveLogSink(LogSink *destination); + +// +// Specify an "extension" added to the filename specified via +// SetLogDestination. This applies to all severity levels. It's +// often used to append the port we're listening on to the logfile +// name. Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetLogFilenameExtension( + const char* filename_extension); + +// +// Make it so that all log messages of at least a particular severity +// are logged to stderr (in addition to logging to the usual log +// file(s)). Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetStderrLogging(LogSeverity min_severity); + +// +// Make it so that all log messages go only to stderr. Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void LogToStderr(); + +// +// Make it so that all log messages of at least a particular severity are +// logged via email to a list of addresses (in addition to logging to the +// usual log file(s)). The list of addresses is just a string containing +// the email addresses to send to (separated by spaces, say). Thread-safe. +// +GOOGLE_GLOG_DLL_DECL void SetEmailLogging(LogSeverity min_severity, + const char* addresses); + +// A simple function that sends email. dest is a commma-separated +// list of addressess. Thread-safe. +GOOGLE_GLOG_DLL_DECL bool SendEmail(const char *dest, + const char *subject, const char *body); + +GOOGLE_GLOG_DLL_DECL const std::vector& GetLoggingDirectories(); + +// For tests only: Clear the internal [cached] list of logging directories to +// force a refresh the next time GetLoggingDirectories is called. +// Thread-hostile. +void TestOnly_ClearLoggingDirectoriesList(); + +// Returns a set of existing temporary directories, which will be a +// subset of the directories returned by GetLogginDirectories(). +// Thread-safe. +GOOGLE_GLOG_DLL_DECL void GetExistingTempDirectories( + std::vector* list); + +// Print any fatal message again -- useful to call from signal handler +// so that the last thing in the output is the fatal message. +// Thread-hostile, but a race is unlikely. +GOOGLE_GLOG_DLL_DECL void ReprintFatalMessage(); + +// Truncate a log file that may be the append-only output of multiple +// processes and hence can't simply be renamed/reopened (typically a +// stdout/stderr). If the file "path" is > "limit" bytes, copy the +// last "keep" bytes to offset 0 and truncate the rest. Since we could +// be racing with other writers, this approach has the potential to +// lose very small amounts of data. For security, only follow symlinks +// if the path is /proc/self/fd/* +GOOGLE_GLOG_DLL_DECL void TruncateLogFile(const char *path, + int64 limit, int64 keep); + +// Truncate stdout and stderr if they are over the value specified by +// --max_log_size; keep the final 1MB. This function has the same +// race condition as TruncateLogFile. +GOOGLE_GLOG_DLL_DECL void TruncateStdoutStderr(); + +// Return the string representation of the provided LogSeverity level. +// Thread-safe. +GOOGLE_GLOG_DLL_DECL const char* GetLogSeverityName(LogSeverity severity); + +// --------------------------------------------------------------------- +// Implementation details that are not useful to most clients +// --------------------------------------------------------------------- + +// A Logger is the interface used by logging modules to emit entries +// to a log. A typical implementation will dump formatted data to a +// sequence of files. We also provide interfaces that will forward +// the data to another thread so that the invoker never blocks. +// Implementations should be thread-safe since the logging system +// will write to them from multiple threads. + +namespace base { + +class GOOGLE_GLOG_DLL_DECL Logger { + public: + virtual ~Logger(); + + // Writes "message[0,message_len-1]" corresponding to an event that + // occurred at "timestamp". If "force_flush" is true, the log file + // is flushed immediately. + // + // The input message has already been formatted as deemed + // appropriate by the higher level logging facility. For example, + // textual log messages already contain timestamps, and the + // file:linenumber header. + virtual void Write(bool force_flush, + time_t timestamp, + const char* message, + int message_len) = 0; + + // Flush any buffered messages + virtual void Flush() = 0; + + // Get the current LOG file size. + // The returned value is approximate since some + // logged data may not have been flushed to disk yet. + virtual uint32 LogSize() = 0; +}; + +// Get the logger for the specified severity level. The logger +// remains the property of the logging module and should not be +// deleted by the caller. Thread-safe. +extern GOOGLE_GLOG_DLL_DECL Logger* GetLogger(LogSeverity level); + +// Set the logger for the specified severity level. The logger +// becomes the property of the logging module and should not +// be deleted by the caller. Thread-safe. +extern GOOGLE_GLOG_DLL_DECL void SetLogger(LogSeverity level, Logger* logger); + +} + +// glibc has traditionally implemented two incompatible versions of +// strerror_r(). There is a poorly defined convention for picking the +// version that we want, but it is not clear whether it even works with +// all versions of glibc. +// So, instead, we provide this wrapper that automatically detects the +// version that is in use, and then implements POSIX semantics. +// N.B. In addition to what POSIX says, we also guarantee that "buf" will +// be set to an empty string, if this function failed. This means, in most +// cases, you do not need to check the error code and you can directly +// use the value of "buf". It will never have an undefined value. +GOOGLE_GLOG_DLL_DECL int posix_strerror_r(int err, char *buf, size_t len); + + +// A class for which we define operator<<, which does nothing. +class GOOGLE_GLOG_DLL_DECL NullStream : public LogMessage::LogStream { + public: + // Initialize the LogStream so the messages can be written somewhere + // (they'll never be actually displayed). This will be needed if a + // NullStream& is implicitly converted to LogStream&, in which case + // the overloaded NullStream::operator<< will not be invoked. + NullStream() : LogMessage::LogStream(message_buffer_, 1, 0) { } + NullStream(const char* /*file*/, int /*line*/, + const CheckOpString& /*result*/) : + LogMessage::LogStream(message_buffer_, 1, 0) { } + NullStream &stream() { return *this; } + private: + // A very short buffer for messages (which we discard anyway). This + // will be needed if NullStream& converted to LogStream& (e.g. as a + // result of a conditional expression). + char message_buffer_[2]; +}; + +// Do nothing. This operator is inline, allowing the message to be +// compiled away. The message will not be compiled away if we do +// something like (flag ? LOG(INFO) : LOG(ERROR)) << message; when +// SKIP_LOG=WARNING. In those cases, NullStream will be implicitly +// converted to LogStream and the message will be computed and then +// quietly discarded. +template +inline NullStream& operator<<(NullStream &str, const T &value) { return str; } + +// Similar to NullStream, but aborts the program (without stack +// trace), like LogMessageFatal. +class GOOGLE_GLOG_DLL_DECL NullStreamFatal : public NullStream { + public: + NullStreamFatal() { } + NullStreamFatal(const char* file, int line, const CheckOpString& result) : + NullStream(file, line, result) { } + __attribute__ ((noreturn)) ~NullStreamFatal() { _exit(1); } +}; + +// Install a signal handler that will dump signal information and a stack +// trace when the program crashes on certain signals. We'll install the +// signal handler for the following signals. +// +// SIGSEGV, SIGILL, SIGFPE, SIGABRT, SIGBUS, and SIGTERM. +// +// By default, the signal handler will write the failure dump to the +// standard error. You can customize the destination by installing your +// own writer function by InstallFailureWriter() below. +// +// Note on threading: +// +// The function should be called before threads are created, if you want +// to use the failure signal handler for all threads. The stack trace +// will be shown only for the thread that receives the signal. In other +// words, stack traces of other threads won't be shown. +GOOGLE_GLOG_DLL_DECL void InstallFailureSignalHandler(); + +// Installs a function that is used for writing the failure dump. "data" +// is the pointer to the beginning of a message to be written, and "size" +// is the size of the message. You should not expect the data is +// terminated with '\0'. +GOOGLE_GLOG_DLL_DECL void InstallFailureWriter( + void (*writer)(const char* data, int size)); + +} + +#endif // _LOGGING_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/log_severity.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/log_severity.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/log_severity.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/log_severity.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,84 @@ +// Copyright (c) 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef BASE_LOG_SEVERITY_H__ +#define BASE_LOG_SEVERITY_H__ + +// Annoying stuff for windows -- makes sure clients can import these functions +#ifndef GOOGLE_GLOG_DLL_DECL +# if defined(_WIN32) && !defined(__CYGWIN__) +# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) +# else +# define GOOGLE_GLOG_DLL_DECL +# endif +#endif + +// Variables of type LogSeverity are widely taken to lie in the range +// [0, NUM_SEVERITIES-1]. Be careful to preserve this assumption if +// you ever need to change their values or add a new severity. +typedef int LogSeverity; + +const int INFO = 0, WARNING = 1, ERROR = 2, FATAL = 3, NUM_SEVERITIES = 4; + +// DFATAL is FATAL in debug mode, ERROR in normal mode +#ifdef NDEBUG +#define DFATAL_LEVEL ERROR +#else +#define DFATAL_LEVEL FATAL +#endif + +extern GOOGLE_GLOG_DLL_DECL const char* const LogSeverityNames[NUM_SEVERITIES]; + +// NDEBUG usage helpers related to (RAW_)DCHECK: +// +// DEBUG_MODE is for small !NDEBUG uses like +// if (DEBUG_MODE) foo.CheckThatFoo(); +// instead of substantially more verbose +// #ifndef NDEBUG +// foo.CheckThatFoo(); +// #endif +// +// IF_DEBUG_MODE is for small !NDEBUG uses like +// IF_DEBUG_MODE( string error; ) +// DCHECK(Foo(&error)) << error; +// instead of substantially more verbose +// #ifndef NDEBUG +// string error; +// DCHECK(Foo(&error)) << error; +// #endif +// +#ifdef NDEBUG +enum { DEBUG_MODE = 0 }; +#define IF_DEBUG_MODE(x) +#else +enum { DEBUG_MODE = 1 }; +#define IF_DEBUG_MODE(x) x +#endif + +#endif // BASE_LOG_SEVERITY_H__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/raw_logging.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/raw_logging.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/raw_logging.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/raw_logging.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,185 @@ +// Copyright (c) 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Maxim Lifantsev +// +// Thread-safe logging routines that do not allocate any memory or +// acquire any locks, and can therefore be used by low-level memory +// allocation and synchronization code. + +#ifndef BASE_RAW_LOGGING_H_ +#define BASE_RAW_LOGGING_H_ + +#include + +namespace google { + +#include "glog/log_severity.h" +#include "glog/vlog_is_on.h" + +// Annoying stuff for windows -- makes sure clients can import these functions +#ifndef GOOGLE_GLOG_DLL_DECL +# if defined(_WIN32) && !defined(__CYGWIN__) +# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) +# else +# define GOOGLE_GLOG_DLL_DECL +# endif +#endif + +// This is similar to LOG(severity) << format... and VLOG(level) << format.., +// but +// * it is to be used ONLY by low-level modules that can't use normal LOG() +// * it is desiged to be a low-level logger that does not allocate any +// memory and does not need any locks, hence: +// * it logs straight and ONLY to STDERR w/o buffering +// * it uses an explicit format and arguments list +// * it will silently chop off really long message strings +// Usage example: +// RAW_LOG(ERROR, "Failed foo with %i: %s", status, error); +// RAW_VLOG(3, "status is %i", status); +// These will print an almost standard log lines like this to stderr only: +// E0821 211317 file.cc:123] RAW: Failed foo with 22: bad_file +// I0821 211317 file.cc:142] RAW: status is 20 +#define RAW_LOG(severity, ...) \ + do { \ + switch (google::severity) { \ + case 0: \ + RAW_LOG_INFO(__VA_ARGS__); \ + break; \ + case 1: \ + RAW_LOG_WARNING(__VA_ARGS__); \ + break; \ + case 2: \ + RAW_LOG_ERROR(__VA_ARGS__); \ + break; \ + case 3: \ + RAW_LOG_FATAL(__VA_ARGS__); \ + break; \ + default: \ + break; \ + } \ + } while (0) + +// The following STRIP_LOG testing is performed in the header file so that it's +// possible to completely compile out the logging code and the log messages. +#if STRIP_LOG == 0 +#define RAW_VLOG(verboselevel, ...) \ + do { \ + if (VLOG_IS_ON(verboselevel)) { \ + RAW_LOG_INFO(__VA_ARGS__); \ + } \ + } while (0) +#else +#define RAW_VLOG(verboselevel, ...) RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG == 0 + +#if STRIP_LOG == 0 +#define RAW_LOG_INFO(...) google::RawLog__(google::INFO, \ + __FILE__, __LINE__, __VA_ARGS__) +#else +#define RAW_LOG_INFO(...) google::RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG == 0 + +#if STRIP_LOG <= 1 +#define RAW_LOG_WARNING(...) google::RawLog__(google::WARNING, \ + __FILE__, __LINE__, __VA_ARGS__) +#else +#define RAW_LOG_WARNING(...) google::RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG <= 1 + +#if STRIP_LOG <= 2 +#define RAW_LOG_ERROR(...) google::RawLog__(google::ERROR, \ + __FILE__, __LINE__, __VA_ARGS__) +#else +#define RAW_LOG_ERROR(...) google::RawLogStub__(0, __VA_ARGS__) +#endif // STRIP_LOG <= 2 + +#if STRIP_LOG <= 3 +#define RAW_LOG_FATAL(...) google::RawLog__(google::FATAL, \ + __FILE__, __LINE__, __VA_ARGS__) +#else +#define RAW_LOG_FATAL(...) \ + do { \ + google::RawLogStub__(0, __VA_ARGS__); \ + exit(1); \ + } while (0) +#endif // STRIP_LOG <= 3 + +// Similar to CHECK(condition) << message, +// but for low-level modules: we use only RAW_LOG that does not allocate memory. +// We do not want to provide args list here to encourage this usage: +// if (!cond) RAW_LOG(FATAL, "foo ...", hard_to_compute_args); +// so that the args are not computed when not needed. +#define RAW_CHECK(condition, message) \ + do { \ + if (!(condition)) { \ + RAW_LOG(FATAL, "Check %s failed: %s", #condition, message); \ + } \ + } while (0) + +// Debug versions of RAW_LOG and RAW_CHECK +#ifndef NDEBUG + +#define RAW_DLOG(severity, ...) RAW_LOG(severity, __VA_ARGS__) +#define RAW_DCHECK(condition, message) RAW_CHECK(condition, message) + +#else // NDEBUG + +#define RAW_DLOG(severity, ...) \ + while (false) \ + RAW_LOG(severity, __VA_ARGS__) +#define RAW_DCHECK(condition, message) \ + while (false) \ + RAW_CHECK(condition, message) + +#endif // NDEBUG + +// Stub log function used to work around for unused variable warnings when +// building with STRIP_LOG > 0. +static inline void RawLogStub__(int ignored, ...) { +} + +// Helper function to implement RAW_LOG and RAW_VLOG +// Logs format... at "severity" level, reporting it +// as called from file:line. +// This does not allocate memory or acquire locks. +GOOGLE_GLOG_DLL_DECL void RawLog__(LogSeverity severity, + const char* file, + int line, + const char* format, ...) + __attribute__((__format__ (__printf__, 4, 5))); + +// Hack to propagate time information into this module so that +// this module does not have to directly call localtime_r(), +// which could allocate memory. +GOOGLE_GLOG_DLL_DECL void RawLog__SetLastTime(const struct tm& t, int usecs); + +} + +#endif // BASE_RAW_LOGGING_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/stl_logging.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/stl_logging.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/stl_logging.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/stl_logging.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,154 @@ +// Copyright (c) 2003, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Stream output operators for STL containers; to be used for logging *only*. +// Inclusion of this file lets you do: +// +// list x; +// LOG(INFO) << "data: " << x; +// vector v1, v2; +// CHECK_EQ(v1, v2); +// +// Note that if you want to use these operators from the non-global namespace, +// you may get an error since they are not in namespace std (and they are not +// in namespace std since that would result in undefined behavior). You may +// need to write +// +// using ::operator<<; +// +// to fix these errors. + +#ifndef UTIL_GTL_STL_LOGGING_INL_H_ +#define UTIL_GTL_STL_LOGGING_INL_H_ + +#if !1 +# error We do not support stl_logging for this compiler +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +# include +# include +# include +#endif + +template +inline std::ostream& operator<<(std::ostream& out, + const std::pair& p) { + out << '(' << p.first << ", " << p.second << ')'; + return out; +} + +namespace google { + +template +inline void PrintSequence(std::ostream& out, Iter begin, Iter end) { + using ::operator<<; + // Output at most 100 elements -- appropriate if used for logging. + for (int i = 0; begin != end && i < 100; ++i, ++begin) { + if (i > 0) out << ' '; + out << *begin; + } + if (begin != end) { + out << " ..."; + } +} + +} + +#define OUTPUT_TWO_ARG_CONTAINER(Sequence) \ +template \ +inline std::ostream& operator<<(std::ostream& out, \ + const Sequence& seq) { \ + google::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ +} + +OUTPUT_TWO_ARG_CONTAINER(std::vector) +OUTPUT_TWO_ARG_CONTAINER(std::deque) +OUTPUT_TWO_ARG_CONTAINER(std::list) +#ifdef __GNUC__ +OUTPUT_TWO_ARG_CONTAINER(__gnu_cxx::slist) +#endif + +#undef OUTPUT_TWO_ARG_CONTAINER + +#define OUTPUT_THREE_ARG_CONTAINER(Sequence) \ +template \ +inline std::ostream& operator<<(std::ostream& out, \ + const Sequence& seq) { \ + google::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ +} + +OUTPUT_THREE_ARG_CONTAINER(std::set) +OUTPUT_THREE_ARG_CONTAINER(std::multiset) + +#undef OUTPUT_THREE_ARG_CONTAINER + +#define OUTPUT_FOUR_ARG_CONTAINER(Sequence) \ +template \ +inline std::ostream& operator<<(std::ostream& out, \ + const Sequence& seq) { \ + google::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ +} + +OUTPUT_FOUR_ARG_CONTAINER(std::map) +OUTPUT_FOUR_ARG_CONTAINER(std::multimap) +#ifdef __GNUC__ +OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_set) +OUTPUT_FOUR_ARG_CONTAINER(__gnu_cxx::hash_multiset) +#endif + +#undef OUTPUT_FOUR_ARG_CONTAINER + +#define OUTPUT_FIVE_ARG_CONTAINER(Sequence) \ +template \ +inline std::ostream& operator<<(std::ostream& out, \ + const Sequence& seq) { \ + google::PrintSequence(out, seq.begin(), seq.end()); \ + return out; \ +} + +#ifdef __GNUC__ +OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_map) +OUTPUT_FIVE_ARG_CONTAINER(__gnu_cxx::hash_multimap) +#endif + +#undef OUTPUT_FIVE_ARG_CONTAINER + +#endif // UTIL_GTL_STL_LOGGING_INL_H_ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/vlog_is_on.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/vlog_is_on.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/vlog_is_on.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/include/glog/vlog_is_on.h 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,128 @@ +// Copyright (c) 1999, 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: Ray Sidney and many others +// +// Defines the VLOG_IS_ON macro that controls the variable-verbosity +// conditional logging. +// +// It's used by VLOG and VLOG_IF in logging.h +// and by RAW_VLOG in raw_logging.h to trigger the logging. +// +// It can also be used directly e.g. like this: +// if (VLOG_IS_ON(2)) { +// // do some logging preparation and logging +// // that can't be accomplished e.g. via just VLOG(2) << ...; +// } +// +// The truth value that VLOG_IS_ON(level) returns is determined by +// the three verbosity level flags: +// --v= Gives the default maximal active V-logging level; +// 0 is the default. +// Normally positive values are used for V-logging levels. +// --vmodule= Gives the per-module maximal V-logging levels to override +// the value given by --v. +// E.g. "my_module=2,foo*=3" would change the logging level +// for all code in source files "my_module.*" and "foo*.*" +// ("-inl" suffixes are also disregarded for this matching). +// +// SetVLOGLevel helper function is provided to do limited dynamic control over +// V-logging by overriding the per-module settings given via --vmodule flag. +// +// CAVEAT: --vmodule functionality is not available in non gcc compilers. +// + +#ifndef BASE_VLOG_IS_ON_H_ +#define BASE_VLOG_IS_ON_H_ + +#include "glog/log_severity.h" + +// Annoying stuff for windows -- makes sure clients can import these functions +#ifndef GOOGLE_GLOG_DLL_DECL +# if defined(_WIN32) && !defined(__CYGWIN__) +# define GOOGLE_GLOG_DLL_DECL __declspec(dllimport) +# else +# define GOOGLE_GLOG_DLL_DECL +# endif +#endif + +#if defined(__GNUC__) +// We emit an anonymous static int* variable at every VLOG_IS_ON(n) site. +// (Normally) the first time every VLOG_IS_ON(n) site is hit, +// we determine what variable will dynamically control logging at this site: +// it's either FLAGS_v or an appropriate internal variable +// matching the current source file that represents results of +// parsing of --vmodule flag and/or SetVLOGLevel calls. +#define VLOG_IS_ON(verboselevel) \ + ({ static google::int32* vlocal__ = &google::kLogSiteUninitialized; \ + google::int32 verbose_level__ = (verboselevel); \ + (*vlocal__ >= verbose_level__) && \ + ((vlocal__ != &google::kLogSiteUninitialized) || \ + (google::InitVLOG3__(&vlocal__, &FLAGS_v, \ + __FILE__, verbose_level__))); }) +#else +// GNU extensions not available, so we do not support --vmodule. +// Dynamic value of FLAGS_v always controls the logging level. +#define VLOG_IS_ON(verboselevel) (FLAGS_v >= (verboselevel)) +#endif + +// Set VLOG(_IS_ON) level for module_pattern to log_level. +// This lets us dynamically control what is normally set by the --vmodule flag. +// Returns the level that previously applied to module_pattern. +// NOTE: To change the log level for VLOG(_IS_ON) sites +// that have already executed after/during InitGoogleLogging, +// one needs to supply the exact --vmodule pattern that applied to them. +// (If no --vmodule pattern applied to them +// the value of FLAGS_v will continue to control them.) +extern GOOGLE_GLOG_DLL_DECL int SetVLOGLevel(const char* module_pattern, + int log_level); + +// Various declarations needed for VLOG_IS_ON above: ========================= + +// Special value used to indicate that a VLOG_IS_ON site has not been +// initialized. We make this a large value, so the common-case check +// of "*vlocal__ >= verbose_level__" in VLOG_IS_ON definition +// passes in such cases and InitVLOG3__ is then triggered. +extern google::int32 kLogSiteUninitialized; + +// Helper routine which determines the logging info for a particalur VLOG site. +// site_flag is the address of the site-local pointer to the controlling +// verbosity level +// site_default is the default to use for *site_flag +// fname is the current source file name +// verbose_level is the argument to VLOG_IS_ON +// We will return the return value for VLOG_IS_ON +// and if possible set *site_flag appropriately. +extern GOOGLE_GLOG_DLL_DECL bool InitVLOG3__( + google::int32** site_flag, + google::int32* site_default, + const char* fname, + google::int32 verbose_level); + +#endif // BASE_VLOG_IS_ON_H_ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/gflags/libgflags.a and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/gflags/libgflags.a differ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/glog/libglog.a and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/third_party/linux/lib/glog/libglog.a differ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/linux/dump_syms/dump_syms.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/linux/dump_syms/dump_syms.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/linux/dump_syms/dump_syms.cc 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/linux/dump_syms/dump_syms.cc 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -36,14 +36,13 @@ int main(int argc, char **argv) { if (argc != 2) { - fprintf(stderr, "Usage: %s \n", argv[0]); + fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } const char *binary = argv[1]; - DumpSymbols dumper; - if (!dumper.WriteSymbolFile(binary, fileno(stdout))) { + if (!WriteSymbolFile(binary, stdout)) { fprintf(stderr, "Failed to write symbol file.\n"); return 1; } diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/linux/dump_syms/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/linux/dump_syms/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/linux/dump_syms/Makefile.in 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/linux/dump_syms/Makefile.in 2010-04-16 17:32:47.000000000 +0100 @@ -55,10 +55,15 @@ HOST_LIBS += \ $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/linux/$(LIB_PREFIX)host_breakpad_linux_common_s.$(LIB_SUFFIX) \ $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/$(LIB_PREFIX)host_breakpad_common_s.$(LIB_SUFFIX) \ + $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/dwarf/$(LIB_PREFIX)host_breakpad_dwarf_s.$(LIB_SUFFIX) \ $(NULL) # force C++ linking CPP_PROG_LINK = 1 FORCE_USE_PIC = 1 +#XXX: bug 554854 causes us to be unable to run binaries on the build slaves +# due to them having an older libstdc++ +HOST_LDFLAGS += -static + include $(topsrcdir)/config/rules.mk diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/linux/md2core/minidump-2-core.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/linux/md2core/minidump-2-core.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/linux/md2core/minidump-2-core.cc 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/linux/md2core/minidump-2-core.cc 2010-04-16 17:32:47.000000000 +0100 @@ -0,0 +1,603 @@ +// Copyright (c) 2009, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Converts a minidump file to a core file which gdb can read. +// Large parts lifted from the userspace core dumper: +// http://code.google.com/p/google-coredumper/ +// +// Usage: minidump-2-core 1234.dmp > core + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/common/minidump_cpu_x86.h" +#include "common/linux/linux_syscall_support.h" +#include "common/linux/minidump_format_linux.h" + +#if __WORDSIZE == 64 + #define ELF_CLASS ELFCLASS64 + #define Ehdr Elf64_Ehdr + #define Phdr Elf64_Phdr + #define Shdr Elf64_Shdr + #define Nhdr Elf64_Nhdr + #define auxv_t Elf64_auxv_t +#else + #define ELF_CLASS ELFCLASS32 + #define Ehdr Elf32_Ehdr + #define Phdr Elf32_Phdr + #define Shdr Elf32_Shdr + #define Nhdr Elf32_Nhdr + #define auxv_t Elf32_auxv_t +#endif + + +#if defined(__x86_64__) + #define ELF_ARCH EM_X86_64 +#elif defined(__i386__) + #define ELF_ARCH EM_386 +#elif defined(__ARM_ARCH_3__) + #define ELF_ARCH EM_ARM +#elif defined(__mips__) + #define ELF_ARCH EM_MIPS +#endif + +static int usage(const char* argv0) { + fprintf(stderr, "Usage: %s \n", argv0); + return 1; +} + +// Write all of the given buffer, handling short writes and EINTR. Return true +// iff successful. +static bool +writea(int fd, const void* idata, size_t length) { + const uint8_t* data = (const uint8_t*) idata; + + size_t done = 0; + while (done < length) { + ssize_t r; + do { + r = write(fd, data + done, length - done); + } while (r == -1 && errno == EINTR); + + if (r < 1) + return false; + done += r; + } + + return true; +} + +// A range of a mmaped file. +class MMappedRange { + public: + MMappedRange(const void* data, size_t length) + : data_(reinterpret_cast(data)), + length_(length) { + } + + // Get an object of |length| bytes at |offset| and return a pointer to it + // unless it's out of bounds. + const void* GetObject(size_t offset, size_t length) { + if (offset + length < offset) + return NULL; + if (offset + length > length_) + return NULL; + return data_ + offset; + } + + // Get element |index| of an array of objects of length |length| starting at + // |offset| bytes. Return NULL if out of bounds. + const void* GetArrayElement(size_t offset, size_t length, unsigned index) { + const size_t element_offset = offset + index * length; + return GetObject(element_offset, length); + } + + // Return a new range which is a subset of this range. + MMappedRange Subrange(const MDLocationDescriptor& location) const { + if (location.rva > length_ || + location.rva + location.data_size < location.rva || + location.rva + location.data_size > length_) { + return MMappedRange(NULL, 0); + } + + return MMappedRange(data_ + location.rva, location.data_size); + } + + const uint8_t* data() const { return data_; } + size_t length() const { return length_; } + + private: + const uint8_t* const data_; + const size_t length_; +}; + +/* Dynamically determines the byte sex of the system. Returns non-zero + * for big-endian machines. + */ +static inline int sex() { + int probe = 1; + return !*(char *)&probe; +} + +typedef struct elf_timeval { /* Time value with microsecond resolution */ + long tv_sec; /* Seconds */ + long tv_usec; /* Microseconds */ +} elf_timeval; + +typedef struct elf_siginfo { /* Information about signal (unused) */ + int32_t si_signo; /* Signal number */ + int32_t si_code; /* Extra code */ + int32_t si_errno; /* Errno */ +} elf_siginfo; + +typedef struct prstatus { /* Information about thread; includes CPU reg*/ + elf_siginfo pr_info; /* Info associated with signal */ + uint16_t pr_cursig; /* Current signal */ + unsigned long pr_sigpend; /* Set of pending signals */ + unsigned long pr_sighold; /* Set of held signals */ + pid_t pr_pid; /* Process ID */ + pid_t pr_ppid; /* Parent's process ID */ + pid_t pr_pgrp; /* Group ID */ + pid_t pr_sid; /* Session ID */ + elf_timeval pr_utime; /* User time */ + elf_timeval pr_stime; /* System time */ + elf_timeval pr_cutime; /* Cumulative user time */ + elf_timeval pr_cstime; /* Cumulative system time */ + user_regs_struct pr_reg; /* CPU registers */ + uint32_t pr_fpvalid; /* True if math co-processor being used */ +} prstatus; + +typedef struct prpsinfo { /* Information about process */ + unsigned char pr_state; /* Numeric process state */ + char pr_sname; /* Char for pr_state */ + unsigned char pr_zomb; /* Zombie */ + signed char pr_nice; /* Nice val */ + unsigned long pr_flag; /* Flags */ +#if defined(__x86_64__) || defined(__mips__) + uint32_t pr_uid; /* User ID */ + uint32_t pr_gid; /* Group ID */ +#else + uint16_t pr_uid; /* User ID */ + uint16_t pr_gid; /* Group ID */ +#endif + pid_t pr_pid; /* Process ID */ + pid_t pr_ppid; /* Parent's process ID */ + pid_t pr_pgrp; /* Group ID */ + pid_t pr_sid; /* Session ID */ + char pr_fname[16]; /* Filename of executable */ + char pr_psargs[80]; /* Initial part of arg list */ +} prpsinfo; + +// We parse the minidump file and keep the parsed information in this structure. +struct CrashedProcess { + CrashedProcess() + : crashing_tid(-1), + auxv(NULL), + auxv_length(0) { + memset(&prps, 0, sizeof(prps)); + prps.pr_sname = 'R'; + } + + struct Mapping { + uint64_t start_address, end_address; + }; + std::vector mappings; + + pid_t crashing_tid; + int fatal_signal; + + struct Thread { + pid_t tid; + user_regs_struct regs; + user_fpregs_struct fpregs; + user_fpxregs_struct fpxregs; + uintptr_t stack_addr; + const uint8_t* stack; + size_t stack_length; + }; + std::vector threads; + + const uint8_t* auxv; + size_t auxv_length; + + prpsinfo prps; +}; + +static uint32_t +U32(const uint8_t* data) { + uint32_t v; + memcpy(&v, data, sizeof(v)); + return v; +} + +static uint16_t +U16(const uint8_t* data) { + uint16_t v; + memcpy(&v, data, sizeof(v)); + return v; +} + +#if defined(__i386__) +static void +ParseThreadRegisters(CrashedProcess::Thread* thread, MMappedRange range) { + const MDRawContextX86* rawregs = + (const MDRawContextX86*) range.GetObject(0, sizeof(MDRawContextX86)); + + thread->regs.ebx = rawregs->ebx; + thread->regs.ecx = rawregs->ecx; + thread->regs.edx = rawregs->edx; + thread->regs.esi = rawregs->esi; + thread->regs.edi = rawregs->edi; + thread->regs.ebp = rawregs->ebp; + thread->regs.eax = rawregs->eax; + thread->regs.xds = rawregs->ds; + thread->regs.xes = rawregs->es; + thread->regs.xfs = rawregs->fs; + thread->regs.xgs = rawregs->gs; + thread->regs.orig_eax = rawregs->eax; + thread->regs.eip = rawregs->eip; + thread->regs.xcs = rawregs->cs; + thread->regs.eflags = rawregs->eflags; + thread->regs.esp = rawregs->esp; + thread->regs.xss = rawregs->ss; + + thread->fpregs.cwd = rawregs->float_save.control_word; + thread->fpregs.swd = rawregs->float_save.status_word; + thread->fpregs.twd = rawregs->float_save.tag_word; + thread->fpregs.fip = rawregs->float_save.error_offset; + thread->fpregs.fcs = rawregs->float_save.error_selector; + thread->fpregs.foo = rawregs->float_save.data_offset; + thread->fpregs.fos = rawregs->float_save.data_selector; + memcpy(thread->fpregs.st_space, rawregs->float_save.register_area, + 10 * 8); + + thread->fpxregs.cwd = rawregs->float_save.control_word; + thread->fpxregs.swd = rawregs->float_save.status_word; + thread->fpxregs.twd = rawregs->float_save.tag_word; + thread->fpxregs.fop = U16(rawregs->extended_registers + 6); + thread->fpxregs.fip = U16(rawregs->extended_registers + 8); + thread->fpxregs.fcs = U16(rawregs->extended_registers + 12); + thread->fpxregs.foo = U16(rawregs->extended_registers + 16); + thread->fpxregs.fos = U16(rawregs->extended_registers + 20); + thread->fpxregs.mxcsr = U32(rawregs->extended_registers + 24); + memcpy(thread->fpxregs.st_space, rawregs->extended_registers + 32, 128); + memcpy(thread->fpxregs.xmm_space, rawregs->extended_registers + 160, 128); +} +#else +#error "This code has not been ported to your platform yet" +#endif + +static void +ParseThreadList(CrashedProcess* crashinfo, MMappedRange range, + const MMappedRange& full_file) { + const uint32_t num_threads = + *(const uint32_t*) range.GetObject(0, sizeof(uint32_t)); + for (unsigned i = 0; i < num_threads; ++i) { + CrashedProcess::Thread thread; + memset(&thread, 0, sizeof(thread)); + const MDRawThread* rawthread = + (MDRawThread*) range.GetArrayElement(sizeof(uint32_t), + sizeof(MDRawThread), i); + thread.tid = rawthread->thread_id; + thread.stack_addr = rawthread->stack.start_of_memory_range; + MMappedRange stack_range = full_file.Subrange(rawthread->stack.memory); + thread.stack = stack_range.data(); + thread.stack_length = rawthread->stack.memory.data_size; + + ParseThreadRegisters(&thread, + full_file.Subrange(rawthread->thread_context)); + + crashinfo->threads.push_back(thread); + } +} + +static void +ParseAuxVector(CrashedProcess* crashinfo, MMappedRange range) { + crashinfo->auxv = range.data(); + crashinfo->auxv_length = range.length(); +} + +static void +ParseCmdLine(CrashedProcess* crashinfo, MMappedRange range) { + const char* cmdline = (const char*) range.data(); + for (size_t i = 0; i < range.length(); ++i) { + if (cmdline[i] == 0) { + static const size_t fname_len = sizeof(crashinfo->prps.pr_fname) - 1; + static const size_t args_len = sizeof(crashinfo->prps.pr_psargs) - 1; + memset(crashinfo->prps.pr_fname, 0, fname_len + 1); + memset(crashinfo->prps.pr_psargs, 0, args_len + 1); + const char* binary_name = strrchr(cmdline, '/'); + if (binary_name) { + binary_name++; + const unsigned len = strlen(binary_name); + memcpy(crashinfo->prps.pr_fname, binary_name, + len > fname_len ? fname_len : len); + } else { + memcpy(crashinfo->prps.pr_fname, cmdline, + i > fname_len ? fname_len : i); + } + + const unsigned len = range.length() > args_len ? + args_len : range.length(); + memcpy(crashinfo->prps.pr_psargs, cmdline, len); + for (unsigned i = 0; i < len; ++i) { + if (crashinfo->prps.pr_psargs[i] == 0) + crashinfo->prps.pr_psargs[i] = ' '; + } + } + } +} + +static void +ParseExceptionStream(CrashedProcess* crashinfo, MMappedRange range) { + const MDRawExceptionStream* exp = + (MDRawExceptionStream*) range.GetObject(0, sizeof(MDRawExceptionStream)); + crashinfo->crashing_tid = exp->thread_id; + crashinfo->fatal_signal = (int) exp->exception_record.exception_code; +} + +static bool +WriteThread(const CrashedProcess::Thread& thread, int fatal_signal) { + struct prstatus pr; + memset(&pr, 0, sizeof(pr)); + + pr.pr_info.si_signo = fatal_signal; + pr.pr_cursig = fatal_signal; + pr.pr_pid = thread.tid; + memcpy(&pr.pr_reg, &thread.regs, sizeof(user_regs_struct)); + + Nhdr nhdr; + memset(&nhdr, 0, sizeof(nhdr)); + nhdr.n_namesz = 5; + nhdr.n_descsz = sizeof(struct prstatus); + nhdr.n_type = NT_PRSTATUS; + if (!writea(1, &nhdr, sizeof(nhdr)) || + !writea(1, "CORE\0\0\0\0", 8) || + !writea(1, &pr, sizeof(struct prstatus))) { + return false; + } + + nhdr.n_descsz = sizeof(user_fpregs_struct); + nhdr.n_type = NT_FPREGSET; + if (!writea(1, &nhdr, sizeof(nhdr)) || + !writea(1, "CORE\0\0\0\0", 8) || + !writea(1, &thread.fpregs, sizeof(user_fpregs_struct))) { + return false; + } + + nhdr.n_descsz = sizeof(user_fpxregs_struct); + nhdr.n_type = NT_PRXFPREG; + if (!writea(1, &nhdr, sizeof(nhdr)) || + !writea(1, "LINUX\0\0\0", 8) || + !writea(1, &thread.fpxregs, sizeof(user_fpxregs_struct))) { + return false; + } + + return true; +} + +static void +ParseModuleStream(CrashedProcess* crashinfo, MMappedRange range) { + const uint32_t num_mappings = + *(const uint32_t*) range.GetObject(0, sizeof(uint32_t)); + for (unsigned i = 0; i < num_mappings; ++i) { + CrashedProcess::Mapping mapping; + const MDRawModule* rawmodule = + (MDRawModule*) range.GetArrayElement(sizeof(uint32_t), + MD_MODULE_SIZE, i); + mapping.start_address = rawmodule->base_of_image; + mapping.end_address = rawmodule->size_of_image + rawmodule->base_of_image; + + crashinfo->mappings.push_back(mapping); + } +} + +int +main(int argc, char** argv) { + if (argc != 2) + return usage(argv[0]); + + const int fd = open(argv[1], O_RDONLY); + if (fd < 0) + return usage(argv[0]); + + struct stat st; + fstat(fd, &st); + + const void* bytes = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0); + close(fd); + if (bytes == MAP_FAILED) { + perror("Failed to mmap dump file"); + return 1; + } + + MMappedRange dump(bytes, st.st_size); + + const MDRawHeader* header = + (const MDRawHeader*) dump.GetObject(0, sizeof(MDRawHeader)); + + CrashedProcess crashinfo; + + for (unsigned i = 0; i < header->stream_count; ++i) { + const MDRawDirectory* dirent = + (const MDRawDirectory*) dump.GetArrayElement( + header->stream_directory_rva, sizeof(MDRawDirectory), i); + switch (dirent->stream_type) { + case MD_THREAD_LIST_STREAM: + ParseThreadList(&crashinfo, dump.Subrange(dirent->location), dump); + break; + case MD_LINUX_AUXV: + ParseAuxVector(&crashinfo, dump.Subrange(dirent->location)); + break; + case MD_LINUX_CMD_LINE: + ParseCmdLine(&crashinfo, dump.Subrange(dirent->location)); + break; + case MD_EXCEPTION_STREAM: + ParseExceptionStream(&crashinfo, dump.Subrange(dirent->location)); + break; + case MD_MODULE_LIST_STREAM: + ParseModuleStream(&crashinfo, dump.Subrange(dirent->location)); + default: + fprintf(stderr, "Skipping %x\n", dirent->stream_type); + } + } + + // Write the ELF header. The file will look like: + // ELF header + // Phdr for the PT_NOTE + // Phdr for each of the thread stacks + // PT_NOTE + // each of the thread stacks + Ehdr ehdr; + memset(&ehdr, 0, sizeof(Ehdr)); + ehdr.e_ident[0] = ELFMAG0; + ehdr.e_ident[1] = ELFMAG1; + ehdr.e_ident[2] = ELFMAG2; + ehdr.e_ident[3] = ELFMAG3; + ehdr.e_ident[4] = ELF_CLASS; + ehdr.e_ident[5] = sex() ? ELFDATA2MSB : ELFDATA2LSB; + ehdr.e_ident[6] = EV_CURRENT; + ehdr.e_type = ET_CORE; + ehdr.e_machine = ELF_ARCH; + ehdr.e_version = EV_CURRENT; + ehdr.e_phoff = sizeof(Ehdr); + ehdr.e_ehsize = sizeof(Ehdr); + ehdr.e_phentsize= sizeof(Phdr); + ehdr.e_phnum = 1 + crashinfo.threads.size() + crashinfo.mappings.size(); + ehdr.e_shentsize= sizeof(Shdr); + if (!writea(1, &ehdr, sizeof(Ehdr))) + return 1; + + size_t offset = sizeof(Ehdr) + + (1 + crashinfo.threads.size() + + crashinfo.mappings.size()) * sizeof(Phdr); + size_t filesz = sizeof(Nhdr) + 8 + sizeof(prpsinfo) + + // sizeof(Nhdr) + 8 + sizeof(user) + + sizeof(Nhdr) + 8 + crashinfo.auxv_length + + crashinfo.threads.size() * ( + (sizeof(Nhdr) + 8 + sizeof(prstatus)) + + sizeof(Nhdr) + 8 + sizeof(user_fpregs_struct) + + sizeof(Nhdr) + 8 + sizeof(user_fpxregs_struct)); + + Phdr phdr; + memset(&phdr, 0, sizeof(Phdr)); + phdr.p_type = PT_NOTE; + phdr.p_offset = offset; + phdr.p_filesz = filesz; + if (!writea(1, &phdr, sizeof(phdr))) + return 1; + + phdr.p_type = PT_LOAD; + phdr.p_align = getpagesize(); + size_t note_align = phdr.p_align - ((offset+filesz) % phdr.p_align); + if (note_align == phdr.p_align) + note_align = 0; + offset += note_align; + + for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { + const CrashedProcess::Thread& thread = crashinfo.threads[i]; + offset += filesz; + filesz = thread.stack_length; + phdr.p_offset = offset; + phdr.p_vaddr = thread.stack_addr; + phdr.p_filesz = phdr.p_memsz = filesz; + phdr.p_flags = PF_R | PF_W; + if (!writea(1, &phdr, sizeof(phdr))) + return 1; + } + + for (unsigned i = 0; i < crashinfo.mappings.size(); ++i) { + const CrashedProcess::Mapping& mapping = crashinfo.mappings[i]; + phdr.p_offset = 0; + phdr.p_vaddr = mapping.start_address; + phdr.p_filesz = 0; + phdr.p_flags = PF_R; + phdr.p_memsz = mapping.end_address - mapping.start_address; + if (!writea(1, &phdr, sizeof(phdr))) + return 1; + } + + Nhdr nhdr; + memset(&nhdr, 0, sizeof(nhdr)); + nhdr.n_namesz = 5; + nhdr.n_descsz = sizeof(prpsinfo); + nhdr.n_type = NT_PRPSINFO; + if (!writea(1, &nhdr, sizeof(nhdr)) || + !writea(1, "CORE\0\0\0\0", 8) || + !writea(1, &crashinfo.prps, sizeof(prpsinfo))) { + return 1; + } + + nhdr.n_descsz = crashinfo.auxv_length; + nhdr.n_type = NT_AUXV; + if (!writea(1, &nhdr, sizeof(nhdr)) || + !writea(1, "CORE\0\0\0\0", 8) || + !writea(1, &crashinfo.auxv, crashinfo.auxv_length)) { + return 1; + } + + for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { + if (crashinfo.threads[i].tid == crashinfo.crashing_tid) { + WriteThread(crashinfo.threads[i], crashinfo.fatal_signal); + break; + } + } + + for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { + if (crashinfo.threads[i].tid != crashinfo.crashing_tid) + WriteThread(crashinfo.threads[i], 0); + } + + if (note_align) { + char scratch[note_align]; + memset(scratch, 0, sizeof(scratch)); + if (!writea(1, scratch, sizeof(scratch))) + return 1; + } + + for (unsigned i = 0; i < crashinfo.threads.size(); ++i) { + const CrashedProcess::Thread& thread = crashinfo.threads[i]; + if (!writea(1, thread.stack, thread.stack_length)) + return 1; + } + + munmap(const_cast(bytes), st.st_size); + + return 0; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.mm firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.mm --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.mm 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.mm 2010-04-16 17:32:47.000000000 +0100 @@ -1,4 +1,4 @@ -// Copyright (c) 2006, Google Inc. +// Copyright (c) 2010 Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -59,6 +59,7 @@ using google_breakpad::CallStack; using google_breakpad::CodeModule; using google_breakpad::CodeModules; +using google_breakpad::Minidump; using google_breakpad::MinidumpProcessor; using google_breakpad::OnDemandSymbolSupplier; using google_breakpad::PathnameStripper; @@ -73,6 +74,7 @@ NSString *minidumpPath; NSString *searchDir; NSString *symbolSearchDir; + BOOL printThreadMemory; } Options; //============================================================================= @@ -190,10 +192,38 @@ printf("\n"); } -//============================================================================= -static void Start(Options *options) { - string minidump_file([options->minidumpPath fileSystemRepresentation]); +static void PrintModules(const CodeModules *modules) { + if (!modules) + return; + + printf("\n"); + printf("Loaded modules:\n"); + + u_int64_t main_address = 0; + const CodeModule *main_module = modules->GetMainModule(); + if (main_module) { + main_address = main_module->base_address(); + } + + unsigned int module_count = modules->module_count(); + for (unsigned int module_sequence = 0; + module_sequence < module_count; + ++module_sequence) { + const CodeModule *module = modules->GetModuleAtSequence(module_sequence); + assert(module); + u_int64_t base_address = module->base_address(); + printf("0x%08llx - 0x%08llx %s %s%s %s\n", + base_address, base_address + module->size() - 1, + PathnameStripper::File(module->code_file()).c_str(), + module->version().empty() ? "???" : module->version().c_str(), + main_module != NULL && base_address == main_address ? + " (main)" : "", + module->code_file().c_str()); + } +} +static void ProcessSingleReport(Options *options, NSString *file_path) { + string minidump_file([file_path fileSystemRepresentation]); BasicSourceLineResolver resolver; string search_dir = options->searchDir ? [options->searchDir fileSystemRepresentation] : ""; @@ -204,8 +234,14 @@ scoped_ptr minidump_processor(new MinidumpProcessor(symbol_supplier.get(), &resolver)); ProcessState process_state; - if (minidump_processor->Process(minidump_file, &process_state) != - MinidumpProcessor::PROCESS_OK) { + scoped_ptr dump(new google_breakpad::Minidump(minidump_file)); + + if (!dump->Read()) { + fprintf(stderr, "Minidump %s could not be read\n", dump->path().c_str()); + return; + } + if (minidump_processor->Process(dump.get(), &process_state) != + google_breakpad::PROCESS_OK) { fprintf(stderr, "MinidumpProcessor::Process failed\n"); return; } @@ -244,12 +280,20 @@ // Print all of the threads in the dump. int thread_count = process_state.threads()->size(); + const std::vector + *thread_memory_regions = process_state.thread_memory_regions(); + for (int thread_index = 0; thread_index < thread_count; ++thread_index) { if (thread_index != requesting_thread) { // Don't print the crash thread again, it was already printed. printf("\n"); printf("Thread %d\n", thread_index); PrintStack(process_state.threads()->at(thread_index), cpu); + google_breakpad::MinidumpMemoryRegion *thread_stack_bytes = + thread_memory_regions->at(thread_index); + if (options->printThreadMemory) { + thread_stack_bytes->Print(); + } } } @@ -258,6 +302,36 @@ printf("\nThread %d:", requesting_thread); PrintRegisters(process_state.threads()->at(requesting_thread), cpu); } + + // Print information about modules + PrintModules(process_state.modules()); +} + +//============================================================================= +static void Start(Options *options) { + NSFileManager *manager = [NSFileManager defaultManager]; + NSString *minidump_path = options->minidumpPath; + BOOL is_dir = NO; + BOOL file_exists = [manager fileExistsAtPath:minidump_path + isDirectory:&is_dir]; + if (file_exists && is_dir) { + NSDirectoryEnumerator *enumerator = + [manager enumeratorAtPath:minidump_path]; + NSString *current_file = nil; + while ((current_file = [enumerator nextObject])) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + if ([[current_file pathExtension] isEqualTo:@"dmp"]) { + printf("Attempting to process report: %s\n", + [current_file cStringUsingEncoding:NSASCIIStringEncoding]); + NSString *full_path = + [minidump_path stringByAppendingPathComponent:current_file]; + ProcessSingleReport(options, full_path); + } + [pool release]; + } + } else if (file_exists) { + ProcessSingleReport(options, minidump_path); + } } //============================================================================= @@ -270,9 +344,11 @@ "If modules cannot be found at the paths stored in the " "minidump file, they will be searched for at " "/.\n"); - fprintf(stderr, "Usage: %s [-s module-search-dir] [-S symbol-file-search-dir] minidump-file\n", argv[0]); + fprintf(stderr, "Usage: %s [-s module-search-dir] [-S symbol-file-search-dir] " + "minidump-file\n", argv[0]); fprintf(stderr, "\t-s: Specify a search directory to use for missing modules\n" - "\t-S: Specify a search directory to use for symbol files\n" + "\t-S: Specify a search directory to use for symbol files\n" + "\t-t: Print thread stack memory in hex\n" "\t-h: Usage\n" "\t-?: Usage\n"); } @@ -282,7 +358,7 @@ extern int optind; char ch; - while ((ch = getopt(argc, (char * const *)argv, "S:s:h?")) != -1) { + while ((ch = getopt(argc, (char * const *)argv, "S:s:ht?")) != -1) { switch (ch) { case 's': options->searchDir = [[NSFileManager defaultManager] @@ -296,6 +372,9 @@ length:strlen(optarg)]; break; + case 't': + options->printThreadMemory = YES; + break; case 'h': case '?': Usage(argc, argv); diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.xcodeproj/project.pbxproj firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.xcodeproj/project.pbxproj --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.xcodeproj/project.pbxproj 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/crash_report.xcodeproj/project.pbxproj 2010-04-16 17:32:47.000000000 +0100 @@ -46,6 +46,7 @@ F9C7ECE50E8ABCA600E953AD /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9C7ECE20E8ABCA600E953AD /* bytereader.cc */; }; F9C7ECE60E8ABCA600E953AD /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9C7ECE30E8ABCA600E953AD /* dwarf2reader.cc */; }; F9C7ECE70E8ABCA600E953AD /* functioninfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9C7ECE40E8ABCA600E953AD /* functioninfo.cc */; }; + F9F0706710FBC02D0037B88B /* stackwalker_arm.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9F0706510FBC02D0037B88B /* stackwalker_arm.cc */; }; FD6625CD0CF4D45C004AC844 /* stackwalker_amd64.cc in Sources */ = {isa = PBXBuildFile; fileRef = FD6625C40CF4D438004AC844 /* stackwalker_amd64.cc */; }; FD8EDEAE0CADDAD400A5EDF1 /* stackwalker_sparc.cc in Sources */ = {isa = PBXBuildFile; fileRef = FD8EDEAC0CADDAD400A5EDF1 /* stackwalker_sparc.cc */; }; FD8EDEAF0CADDAD400A5EDF1 /* stackwalker_sparc.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD8EDEAD0CADDAD400A5EDF1 /* stackwalker_sparc.h */; }; @@ -130,9 +131,11 @@ 9BE650AF0B52FE3000611104 /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_id.h; path = ../../../common/mac/macho_id.h; sourceTree = SOURCE_ROOT; }; 9BE650B00B52FE3000611104 /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_walker.cc; path = ../../../common/mac/macho_walker.cc; sourceTree = SOURCE_ROOT; }; 9BE650B10B52FE3000611104 /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_walker.h; path = ../../../common/mac/macho_walker.h; sourceTree = SOURCE_ROOT; }; - F9C7ECE20E8ABCA600E953AD /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/mac/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; }; - F9C7ECE30E8ABCA600E953AD /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/mac/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; }; - F9C7ECE40E8ABCA600E953AD /* functioninfo.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = functioninfo.cc; path = ../../../common/mac/dwarf/functioninfo.cc; sourceTree = SOURCE_ROOT; }; + F9C7ECE20E8ABCA600E953AD /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; }; + F9C7ECE30E8ABCA600E953AD /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; }; + F9C7ECE40E8ABCA600E953AD /* functioninfo.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = functioninfo.cc; path = ../../../common/dwarf/functioninfo.cc; sourceTree = SOURCE_ROOT; }; + F9F0706510FBC02D0037B88B /* stackwalker_arm.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = stackwalker_arm.cc; path = ../../../processor/stackwalker_arm.cc; sourceTree = SOURCE_ROOT; }; + F9F0706610FBC02D0037B88B /* stackwalker_arm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = stackwalker_arm.h; path = ../../../processor/stackwalker_arm.h; sourceTree = SOURCE_ROOT; }; FD6625C40CF4D438004AC844 /* stackwalker_amd64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = stackwalker_amd64.cc; path = ../../../processor/stackwalker_amd64.cc; sourceTree = SOURCE_ROOT; }; FD6625C50CF4D438004AC844 /* stackwalker_amd64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = stackwalker_amd64.h; path = ../../../processor/stackwalker_amd64.h; sourceTree = SOURCE_ROOT; }; FD8EDEAC0CADDAD400A5EDF1 /* stackwalker_sparc.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = stackwalker_sparc.cc; path = ../../../processor/stackwalker_sparc.cc; sourceTree = SOURCE_ROOT; }; @@ -238,6 +241,8 @@ 9BDF17280B1B8B0200F8391B /* processor */ = { isa = PBXGroup; children = ( + F9F0706510FBC02D0037B88B /* stackwalker_arm.cc */, + F9F0706610FBC02D0037B88B /* stackwalker_arm.h */, 9B3904980B2E52FD0059FABE /* basic_source_line_resolver.cc */, 9BDF1AFA0B1BEB6300F8391B /* address_map-inl.h */, 9BDF1AFB0B1BEB6300F8391B /* address_map.h */, @@ -352,6 +357,7 @@ F9C7ECE50E8ABCA600E953AD /* bytereader.cc in Sources */, F9C7ECE60E8ABCA600E953AD /* dwarf2reader.cc in Sources */, F9C7ECE70E8ABCA600E953AD /* functioninfo.cc in Sources */, + F9F0706710FBC02D0037B88B /* stackwalker_arm.cc in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/on_demand_symbol_supplier.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/on_demand_symbol_supplier.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/on_demand_symbol_supplier.h 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/on_demand_symbol_supplier.h 2010-04-16 17:32:47.000000000 +0100 @@ -47,7 +47,7 @@ public: // |search_dir| is the directory to search for alternative symbols with // the same name as the module in the minidump - OnDemandSymbolSupplier(const string &search_dir, + OnDemandSymbolSupplier(const string &search_dir, const string &symbol_search_dir); virtual ~OnDemandSymbolSupplier() {} @@ -56,11 +56,16 @@ const SystemInfo *system_info, string *symbol_file); + // Returns the path to the symbol file for the given module. + virtual SymbolResult GetSymbolFile(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data); protected: // Search directory string search_dir_; string symbol_search_dir_; - + // When we create a symbol file for a module, save the name of the module // and the path to that module's symbol file. map module_file_map_; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/on_demand_symbol_supplier.mm firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/on_demand_symbol_supplier.mm --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/on_demand_symbol_supplier.mm 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/crash_report/on_demand_symbol_supplier.mm 2010-04-16 17:32:47.000000000 +0100 @@ -30,6 +30,8 @@ #include #include #include +#include +#include #include "google_breakpad/processor/basic_source_line_resolver.h" #include "google_breakpad/processor/minidump.h" @@ -136,6 +138,26 @@ return FOUND; } +SymbolSupplier::SymbolResult +OnDemandSymbolSupplier::GetSymbolFile(const CodeModule *module, + const SystemInfo *system_info, + string *symbol_file, + string *symbol_data) { + SymbolSupplier::SymbolResult s = GetSymbolFile(module, + system_info, + symbol_file); + + + if (s == FOUND) { + ifstream in(symbol_file->c_str()); + getline(in, *symbol_data, std::string::traits_type::to_char_type( + std::string::traits_type::eof())); + in.close(); + } + + return s; +} + string OnDemandSymbolSupplier::GetLocalModulePath(const CodeModule *module) { NSFileManager *mgr = [NSFileManager defaultManager]; const char *moduleStr = module->code_file().c_str(); diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj 2010-04-16 17:32:47.000000000 +0100 @@ -45,15 +45,15 @@ 9BE650440B52F6D800611104 /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_id.h; path = ../../../common/mac/macho_id.h; sourceTree = SOURCE_ROOT; }; 9BE650450B52F6D800611104 /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_walker.cc; path = ../../../common/mac/macho_walker.cc; sourceTree = SOURCE_ROOT; }; 9BE650460B52F6D800611104 /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_walker.h; path = ../../../common/mac/macho_walker.h; sourceTree = SOURCE_ROOT; }; - F95B422B0E0E22D100DBDE83 /* bytereader-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "bytereader-inl.h"; path = "../../../common/mac/dwarf/bytereader-inl.h"; sourceTree = SOURCE_ROOT; }; - F95B422C0E0E22D100DBDE83 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/mac/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; }; - F95B422D0E0E22D100DBDE83 /* bytereader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bytereader.h; path = ../../../common/mac/dwarf/bytereader.h; sourceTree = SOURCE_ROOT; }; - F95B422E0E0E22D100DBDE83 /* dwarf2enums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dwarf2enums.h; path = ../../../common/mac/dwarf/dwarf2enums.h; sourceTree = SOURCE_ROOT; }; - F95B422F0E0E22D100DBDE83 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/mac/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; }; - F95B42300E0E22D100DBDE83 /* dwarf2reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dwarf2reader.h; path = ../../../common/mac/dwarf/dwarf2reader.h; sourceTree = SOURCE_ROOT; }; - F95B42310E0E22D100DBDE83 /* line_state_machine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = line_state_machine.h; path = ../../../common/mac/dwarf/line_state_machine.h; sourceTree = SOURCE_ROOT; }; - F9C7ED420E8AD93000E953AD /* functioninfo.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = functioninfo.cc; path = ../../../common/mac/dwarf/functioninfo.cc; sourceTree = SOURCE_ROOT; }; - F9F5344D0E7C902C0012363F /* functioninfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = functioninfo.h; path = ../../../common/mac/dwarf/functioninfo.h; sourceTree = SOURCE_ROOT; }; + F95B422B0E0E22D100DBDE83 /* bytereader-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "bytereader-inl.h"; path = "../../../common/dwarf/bytereader-inl.h"; sourceTree = SOURCE_ROOT; }; + F95B422C0E0E22D100DBDE83 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; }; + F95B422D0E0E22D100DBDE83 /* bytereader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bytereader.h; path = ../../../common/dwarf/bytereader.h; sourceTree = SOURCE_ROOT; }; + F95B422E0E0E22D100DBDE83 /* dwarf2enums.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dwarf2enums.h; path = ../../../common/dwarf/dwarf2enums.h; sourceTree = SOURCE_ROOT; }; + F95B422F0E0E22D100DBDE83 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; }; + F95B42300E0E22D100DBDE83 /* dwarf2reader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dwarf2reader.h; path = ../../../common/dwarf/dwarf2reader.h; sourceTree = SOURCE_ROOT; }; + F95B42310E0E22D100DBDE83 /* line_state_machine.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = line_state_machine.h; path = ../../../common/dwarf/line_state_machine.h; sourceTree = SOURCE_ROOT; }; + F9C7ED420E8AD93000E953AD /* functioninfo.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = functioninfo.cc; path = ../../../common/dwarf/functioninfo.cc; sourceTree = SOURCE_ROOT; }; + F9F5344D0E7C902C0012363F /* functioninfo.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = functioninfo.h; path = ../../../common/dwarf/functioninfo.h; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/dump_syms/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/dump_syms/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/dump_syms/Makefile.in 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/mac/dump_syms/Makefile.in 2010-04-16 17:32:47.000000000 +0100 @@ -53,8 +53,8 @@ $(NULL) HOST_LIBS += \ + $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/dwarf/$(LIB_PREFIX)host_breakpad_dwarf_s.$(LIB_SUFFIX) \ $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/mac/$(LIB_PREFIX)host_breakpad_mac_common_s.$(LIB_SUFFIX) \ - $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/mac/dwarf/$(LIB_PREFIX)host_breakpad_mac_dwarf_s.$(LIB_SUFFIX) \ $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/$(LIB_PREFIX)host_breakpad_common_s.$(LIB_SUFFIX) \ $(NULL) diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/windows/symupload/symupload.cc firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/windows/symupload/symupload.cc --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/windows/symupload/symupload.cc 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/google-breakpad/src/tools/windows/symupload/symupload.cc 2010-04-16 17:32:48.000000000 +0100 @@ -131,12 +131,13 @@ FILE *temp_file = NULL; #if _MSC_VER >= 1400 // MSVC 2005/8 - if (_wfopen_s(&temp_file, temp_filename, L"w") != 0) { + if (_wfopen_s(&temp_file, temp_filename, L"w") != 0) #else // _MSC_VER >= 1400 // _wfopen_s was introduced in MSVC8. Use _wfopen for earlier environments. // Don't use it with MSVC8 and later, because it's deprecated. - if (!(temp_file = _wfopen(temp_filename, L"w"))) { + if (!(temp_file = _wfopen(temp_filename, L"w"))) #endif // _MSC_VER >= 1400 + { return false; } @@ -152,12 +153,33 @@ return writer.GetModuleInfo(pdb_info); } +void printUsageAndExit() { + wprintf(L"Usage: symupload [--timeout NN] \n\n"); + wprintf(L"Timeout is in milliseconds, or can be 0 to be unlimited\n\n"); + wprintf(L"Example:\n\n\tsymupload.exe --timeout 0 chrome.dll http://no.free.symbol.server.for.you\n"); + exit(0); +} int wmain(int argc, wchar_t *argv[]) { - if (argc < 3) { - wprintf(L"Usage: %s \n", argv[0]); - return 0; + if ((argc != 3) && + (argc != 5)) { + printUsageAndExit(); + } + + const wchar_t *module, *url; + int timeout = -1; + if (argc == 3) { + module = argv[1]; + url = argv[2]; + } else { + // check for timeout flag + if (!wcscmp(L"--timeout", argv[1])) { + timeout = _wtoi(argv[2]); + module = argv[3]; + url = argv[4]; + } else { + printUsageAndExit(); + } } - const wchar_t *module = argv[1], *url = argv[2]; wstring symbol_file; PDBModuleInfo pdb_info; @@ -186,6 +208,7 @@ bool success = HTTPUpload::SendRequest(url, parameters, symbol_file, L"symbol_file", + timeout == -1 ? NULL : &timeout, NULL, NULL); _wunlink(symbol_file.c_str()); diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/Makefile.in 2010-04-02 16:59:12.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/Makefile.in 2010-04-16 17:32:46.000000000 +0100 @@ -50,9 +50,6 @@ string \ $(NULL) -DIRS = \ - $(NULL) - ifeq ($(OS_ARCH),WINNT) DIRS += \ google-breakpad/src/common/windows \ @@ -81,7 +78,9 @@ google-breakpad/src/common \ google-breakpad/src/common/linux \ google-breakpad/src/client \ + google-breakpad/src/client/linux/crash_generation \ google-breakpad/src/client/linux/handler \ + google-breakpad/src/client/linux/minidump_writer \ google-breakpad/src/tools/linux/dump_syms \ $(NULL) endif @@ -113,8 +112,12 @@ FORCE_STATIC_LIB = 1 +EXTRA_JS_MODULES = \ + CrashSubmit.jsm \ + $(NULL) + ifdef ENABLE_TESTS -DIRS += test +TOOL_DIRS = test endif include $(topsrcdir)/config/rules.mk diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/nsExceptionHandler.cpp firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/nsExceptionHandler.cpp --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/nsExceptionHandler.cpp 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/nsExceptionHandler.cpp 2010-04-16 17:32:48.000000000 +0100 @@ -21,6 +21,7 @@ * * Contributor(s): * Josh Aas + * Justin Dolske * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -43,7 +44,12 @@ #undef WIN32_LEAN_AND_MEAN #endif +#include "nsIWindowsRegKey.h" +#if defined(MOZ_IPC) +# include "client/windows/crash_generation/crash_generation_server.h" +#endif #include "client/windows/handler/exception_handler.h" +#include #include #elif defined(XP_MACOSX) #include "client/mac/handler/exception_handler.h" @@ -54,6 +60,14 @@ #include #include "mac_utils.h" #elif defined(XP_LINUX) +#include "nsDirectoryServiceUtils.h" +#include "nsDirectoryServiceDefs.h" +#include "nsIINIParser.h" +#if defined(MOZ_IPC) +# include "common/linux/linux_syscall_support.h" +# include "client/linux/crash_generation/client_info.h" +# include "client/linux/crash_generation/crash_generation_server.h" +#endif #include "client/linux/handler/exception_handler.h" #include #include @@ -72,15 +86,30 @@ #include #include #include +#include "mozilla/Mutex.h" #include "nsDebug.h" #include "nsCRT.h" #include "nsILocalFile.h" -#include "nsDataHashtable.h" +#include "nsIFileStreams.h" +#include "nsInterfaceHashtable.h" +#include "prprf.h" +#include "nsIXULAppInfo.h" + +#if defined(MOZ_IPC) +#include "nsIUUIDGenerator.h" + +using google_breakpad::CrashGenerationServer; +using google_breakpad::ClientInfo; + +using mozilla::Mutex; +using mozilla::MutexAutoLock; +#endif namespace CrashReporter { #ifdef XP_WIN32 typedef wchar_t XP_CHAR; +typedef std::wstring xpstring; #define CONVERT_UTF16_TO_XP_CHAR(x) x #define CONVERT_XP_CHAR_TO_UTF16(x) x #define XP_STRLEN(x) wcslen(x) @@ -98,6 +127,7 @@ #endif #else typedef char XP_CHAR; +typedef std::string xpstring; #define CONVERT_UTF16_TO_XP_CHAR(x) NS_ConvertUTF16toUTF8(x) #define CONVERT_XP_CHAR_TO_UTF16(x) NS_ConvertUTF8toUTF16(x) #define XP_STRLEN(x) strlen(x) @@ -137,10 +167,57 @@ sizeof(kTimeSinceLastCrashParameter)-1; // this holds additional data sent via the API -static nsDataHashtable* crashReporterAPIData_Hash; +static AnnotationTable* crashReporterAPIData_Hash; static nsCString* crashReporterAPIData = nsnull; static nsCString* notesField = nsnull; +#if defined(MOZ_IPC) +// OOP crash reporting +static CrashGenerationServer* crashServer; // chrome process has this + +# if defined(XP_WIN) +// If crash reporting is disabled, we hand out this "null" pipe to the +// child process and don't attempt to connect to a parent server. +static const char kNullNotifyPipe[] = "-"; +static char* childCrashNotifyPipe; + +# elif defined(XP_LINUX) +static int serverSocketFd = -1; +static int clientSocketFd = -1; +static const int kMagicChildCrashReportFd = 42; +# endif + +// |dumpMapLock| must protect all access to |pidToMinidump|. +static Mutex* dumpMapLock; +typedef nsInterfaceHashtable ChildMinidumpMap; +static ChildMinidumpMap* pidToMinidump; + +// Crashreporter annotations that we don't send along in subprocess +// reports +static const char* kSubprocessBlacklist[] = { + "FramePoisonBase", + "FramePoisonSize", + "StartupTime", + "URL" +}; + + +#endif // MOZ_IPC + +#ifdef XP_WIN +static void +CreateFileFromPath(const xpstring& path, nsILocalFile** file) +{ + NS_NewLocalFile(nsDependentString(path.c_str()), PR_FALSE, file); +} +#else +static void +CreateFileFromPath(const xpstring& path, nsILocalFile** file) +{ + NS_NewNativeLocalFile(nsDependentCString(path.c_str()), PR_FALSE, file); +} +#endif + static XP_CHAR* Concat(XP_CHAR* str, const XP_CHAR* toAppend, int* size) { @@ -213,7 +290,8 @@ O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd != -1) { - write(fd, crashTimeString, crashTimeStringLen); + ssize_t ignored = write(fd, crashTimeString, crashTimeStringLen); + (void)ignored; close(fd); } #endif @@ -281,14 +359,17 @@ if (fd != -1) { // not much we can do in case of error - write(fd, crashReporterAPIData->get(), crashReporterAPIData->Length()); - write(fd, kCrashTimeParameter, kCrashTimeParameterLen); - write(fd, crashTimeString, crashTimeStringLen); - write(fd, "\n", 1); + ssize_t ignored = write(fd, crashReporterAPIData->get(), + crashReporterAPIData->Length()); + ignored = write(fd, kCrashTimeParameter, kCrashTimeParameterLen); + ignored = write(fd, crashTimeString, crashTimeStringLen); + ignored = write(fd, "\n", 1); if (timeSinceLastCrash != 0) { - write(fd, kTimeSinceLastCrashParameter,kTimeSinceLastCrashParameterLen); - write(fd, timeSinceLastCrashString, timeSinceLastCrashStringLen); - write(fd, "\n", 1); + ignored = write(fd, kTimeSinceLastCrashParameter, + kTimeSinceLastCrashParameterLen); + ignored = write(fd, timeSinceLastCrashString, + timeSinceLastCrashStringLen); + ignored = write(fd, "\n", 1); } close (fd); } @@ -340,6 +421,14 @@ } #endif // XP_WIN +static bool ShouldReport() +{ + // this environment variable prevents us from launching + // the crash reporter client + const char *envvar = PR_GetEnv("MOZ_CRASHREPORTER_NO_REPORT"); + return !(envvar && *envvar); +} + nsresult SetExceptionHandler(nsILocalFile* aXREDirectory, bool force/*=false*/) { @@ -354,9 +443,7 @@ // this environment variable prevents us from launching // the crash reporter client - envvar = PR_GetEnv("MOZ_CRASHREPORTER_NO_REPORT"); - if (envvar && *envvar) - doReport = false; + doReport = ShouldReport(); // allocate our strings crashReporterAPIData = new nsCString(); @@ -683,6 +770,8 @@ return NS_OK; } +static void OOPDeinit(); + nsresult UnsetExceptionHandler() { delete gExceptionHandler; @@ -714,6 +803,10 @@ gExceptionHandler = nsnull; +#ifdef MOZ_IPC + OOPDeinit(); +#endif + return NS_OK; } @@ -909,4 +1002,730 @@ } #endif +/* + * Combined code to get/set the crash reporter submission pref on + * different platforms. + */ +static nsresult PrefSubmitReports(PRBool* aSubmitReports, bool writePref) +{ + nsresult rv; +#if defined(XP_WIN32) + /* + * NOTE! This needs to stay in sync with the preference checking code + * in toolkit/crashreporter/client/crashreporter_win.cpp + */ + nsCOMPtr appinfo = + do_GetService("@mozilla.org/xre/app-info;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCAutoString appVendor, appName; + rv = appinfo->GetVendor(appVendor); + NS_ENSURE_SUCCESS(rv, rv); + rv = appinfo->GetName(appName); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr regKey + (do_CreateInstance("@mozilla.org/windows-registry-key;1", &rv)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCAutoString regPath; + + regPath.AppendLiteral("Software\\"); + if(!appVendor.IsEmpty()) { + regPath.Append(appVendor); + regPath.AppendLiteral("\\"); + } + regPath.Append(appName); + regPath.AppendLiteral("\\Crash Reporter"); + + // If we're saving the pref value, just write it to ROOT_KEY_CURRENT_USER + // and we're done. + if (writePref) { + rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, + NS_ConvertUTF8toUTF16(regPath), + nsIWindowsRegKey::ACCESS_SET_VALUE); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 value = *aSubmitReports ? 1 : 0; + rv = regKey->WriteIntValue(NS_LITERAL_STRING("SubmitCrashReport"), value); + regKey->Close(); + return rv; + } + + // We're reading the pref value, so we need to first look under + // ROOT_KEY_LOCAL_MACHINE to see if it's set there, and then fall back to + // ROOT_KEY_CURRENT_USER. If it's not set in either place, the pref defaults + // to "true". + PRUint32 value; + rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE, + NS_ConvertUTF8toUTF16(regPath), + nsIWindowsRegKey::ACCESS_QUERY_VALUE); + if (NS_SUCCEEDED(rv)) { + rv = regKey->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value); + regKey->Close(); + if (NS_SUCCEEDED(rv)) { + *aSubmitReports = !!value; + return NS_OK; + } + } + + rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, + NS_ConvertUTF8toUTF16(regPath), + nsIWindowsRegKey::ACCESS_QUERY_VALUE); + if (NS_FAILED(rv)) { + *aSubmitReports = PR_TRUE; + return NS_OK; + } + + rv = regKey->ReadIntValue(NS_LITERAL_STRING("SubmitCrashReport"), &value); + // default to true on failure + if (NS_FAILED(rv)) { + value = 1; + rv = NS_OK; + } + regKey->Close(); + + *aSubmitReports = !!value; + return NS_OK; +#elif defined(XP_MACOSX) + // TODO: Implement for OSX (bug 542379) + return NS_ERROR_NOT_IMPLEMENTED; +#elif defined(XP_UNIX) + /* + * NOTE! This needs to stay in sync with the preference checking code + * in toolkit/crashreporter/client/crashreporter_linux.cpp + */ + nsCOMPtr reporterINI; + rv = NS_GetSpecialDirectory("UAppData", getter_AddRefs(reporterINI)); + NS_ENSURE_SUCCESS(rv, rv); + reporterINI->AppendNative(NS_LITERAL_CSTRING("Crash Reports")); + reporterINI->AppendNative(NS_LITERAL_CSTRING("crashreporter.ini")); + + PRBool exists; + rv = reporterINI->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + if (!exists) { + if (!writePref) { + // If reading the pref, default to true if .ini doesn't exist. + *aSubmitReports = PR_TRUE; + return NS_OK; + } + // Create the file so the INI processor can write to it. + rv = reporterINI->Create(nsIFile::NORMAL_FILE_TYPE, 0600); + NS_ENSURE_SUCCESS(rv, rv); + } + + nsCOMPtr iniFactory = + do_GetService("@mozilla.org/xpcom/ini-processor-factory;1", &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr localFile = do_QueryInterface(reporterINI); + NS_ENSURE_TRUE(localFile, NS_ERROR_FAILURE); + nsCOMPtr iniParser; + rv = iniFactory->CreateINIParser(localFile, + getter_AddRefs(iniParser)); + NS_ENSURE_SUCCESS(rv, rv); + + // If we're writing the pref, just set and we're done. + if (writePref) { + nsCOMPtr iniWriter = do_QueryInterface(iniParser); + NS_ENSURE_TRUE(iniWriter, NS_ERROR_FAILURE); + + rv = iniWriter->SetString(NS_LITERAL_CSTRING("Crash Reporter"), + NS_LITERAL_CSTRING("SubmitReport"), + *aSubmitReports ? NS_LITERAL_CSTRING("1") : + NS_LITERAL_CSTRING("0")); + NS_ENSURE_SUCCESS(rv, rv); + rv = iniWriter->WriteFile(NULL); + return rv; + } + + nsCAutoString submitReportValue; + rv = iniParser->GetString(NS_LITERAL_CSTRING("Crash Reporter"), + NS_LITERAL_CSTRING("SubmitReport"), + submitReportValue); + + // Default to "true" if the pref can't be found. + if (NS_FAILED(rv)) + *aSubmitReports = PR_TRUE; + else if (submitReportValue.EqualsASCII("0")) + *aSubmitReports = PR_FALSE; + else + *aSubmitReports = PR_TRUE; + + return NS_OK; +#else + return NS_ERROR_NOT_IMPLEMENTED; +#endif +} + +nsresult GetSubmitReports(PRBool* aSubmitReports) +{ + return PrefSubmitReports(aSubmitReports, false); +} + +nsresult SetSubmitReports(PRBool aSubmitReports) +{ + return PrefSubmitReports(&aSubmitReports, true); +} + +// The "pending" dir is Crash Reports/pending, from which minidumps +// can be submitted +static bool +GetPendingDir(nsILocalFile** dir) +{ + nsCOMPtr dirSvc = + do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); + if (!dirSvc) + return false; + nsCOMPtr pendingDir; + if (NS_FAILED(dirSvc->Get("UAppData", + NS_GET_IID(nsILocalFile), + getter_AddRefs(pendingDir))) || + NS_FAILED(pendingDir->Append(NS_LITERAL_STRING("Crash Reports"))) || + NS_FAILED(pendingDir->Append(NS_LITERAL_STRING("pending")))) + return false; + *dir = NULL; + pendingDir.swap(*dir); + return true; +} + +// The "limbo" dir is where minidumps go to wait for something else to +// use them. If we're |ShouldReport()|, then the "something else" is +// a minidump submitter, and they're coming from the +// Crash Reports/pending/ dir. Otherwise, we don't know what the +// "somthing else" is, but the minidumps stay in [profile]/minidumps/ +// limbo. +static bool +GetMinidumpLimboDir(nsILocalFile** dir) +{ + if (ShouldReport()) { + return GetPendingDir(dir); + } + else { + CreateFileFromPath(gExceptionHandler->dump_path(), dir); + return NULL != *dir; + } +} + +bool +GetMinidumpForID(const nsAString& id, nsILocalFile** minidump) +{ + if (!GetMinidumpLimboDir(minidump)) + return false; + (*minidump)->Append(id + NS_LITERAL_STRING(".dmp")); + return true; +} + +bool +GetIDFromMinidump(nsILocalFile* minidump, nsAString& id) +{ + if (NS_SUCCEEDED(minidump->GetLeafName(id))) { + id.Replace(id.Length() - 4, 4, NS_LITERAL_STRING("")); + return true; + } + return false; +} + +bool +GetExtraFileForID(const nsAString& id, nsILocalFile** extraFile) +{ + if (!GetMinidumpLimboDir(extraFile)) + return false; + (*extraFile)->Append(id + NS_LITERAL_STRING(".extra")); + return true; +} + +bool +GetExtraFileForMinidump(nsILocalFile* minidump, nsILocalFile** extraFile) +{ + nsAutoString leafName; + nsresult rv = minidump->GetLeafName(leafName); + if (NS_FAILED(rv)) + return false; + + nsCOMPtr extraF; + rv = minidump->Clone(getter_AddRefs(extraF)); + if (NS_FAILED(rv)) + return false; + + nsCOMPtr extra = do_QueryInterface(extraF); + if (!extra) + return false; + + leafName.Replace(leafName.Length() - 3, 3, + NS_LITERAL_STRING("extra")); + rv = extra->SetLeafName(leafName); + if (NS_FAILED(rv)) + return false; + + *extraFile = NULL; + extra.swap(*extraFile); + return true; +} + +bool +AppendExtraData(const nsAString& id, const AnnotationTable& data) +{ + nsCOMPtr extraFile; + if (!GetExtraFileForID(id, getter_AddRefs(extraFile))) + return false; + return AppendExtraData(extraFile, data); +} + +//----------------------------------------------------------------------------- +// Helpers for AppendExtraData() +// +struct Blacklist { + Blacklist() : mItems(NULL), mLen(0) { } + Blacklist(const char** items, int len) : mItems(items), mLen(len) { } + + bool Contains(const nsACString& key) const { + for (int i = 0; i < mLen; ++i) + if (key.EqualsASCII(mItems[i])) + return true; + return false; + } + + const char** mItems; + const int mLen; +}; + +struct EnumerateAnnotationsContext { + const Blacklist& blacklist; + PRFileDesc* fd; +}; + +static void +WriteAnnotation(PRFileDesc* fd, const nsACString& key, const nsACString& value) +{ + PR_Write(fd, key.BeginReading(), key.Length()); + PR_Write(fd, "=", 1); + PR_Write(fd, value.BeginReading(), value.Length()); + PR_Write(fd, "\n", 1); +} + +static PLDHashOperator +EnumerateAnnotations(const nsACString& key, + nsCString entry, + void* userData) +{ + EnumerateAnnotationsContext* ctx = + static_cast(userData); + const Blacklist& blacklist = ctx->blacklist; + + // skip entries in the blacklist + if (blacklist.Contains(key)) + return PL_DHASH_NEXT; + + WriteAnnotation(ctx->fd, key, entry); + + return PL_DHASH_NEXT; +} + +static bool +WriteExtraData(nsILocalFile* extraFile, + const AnnotationTable& data, + const Blacklist& blacklist, + bool writeCrashTime=false, + bool truncate=false) +{ + PRFileDesc* fd; + PRIntn truncOrAppend = truncate ? PR_TRUNCATE : PR_APPEND; + nsresult rv = + extraFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | truncOrAppend, + 0600, &fd); + if (NS_FAILED(rv)) + return false; + + EnumerateAnnotationsContext ctx = { blacklist, fd }; + data.EnumerateRead(EnumerateAnnotations, &ctx); + + if (writeCrashTime) { + time_t crashTime = time(NULL); + char crashTimeString[32]; + XP_TTOA(crashTime, crashTimeString, 10); + + WriteAnnotation(fd, + nsDependentCString("CrashTime"), + nsDependentCString(crashTimeString)); + } + + PR_Close(fd); + return true; +} + +bool +AppendExtraData(nsILocalFile* extraFile, const AnnotationTable& data) +{ + return WriteExtraData(extraFile, data, Blacklist()); +} + + +#if defined(MOZ_IPC) + +static bool +WriteExtraForMinidump(nsILocalFile* minidump, + const Blacklist& blacklist, + nsILocalFile** extraFile) +{ + nsCOMPtr extra; + if (!GetExtraFileForMinidump(minidump, getter_AddRefs(extra))) + return false; + + if (!WriteExtraData(extra, *crashReporterAPIData_Hash, + blacklist, + true /*write crash time*/, + true /*truncate*/)) + return false; + + *extraFile = NULL; + extra.swap(*extraFile); + + return true; +} + +// It really only makes sense to call this function when +// ShouldReport() is true. +static bool +MoveToPending(nsIFile* dumpFile, nsIFile* extraFile) +{ + nsCOMPtr pendingDir; + if (!GetPendingDir(getter_AddRefs(pendingDir))) + return false; + + return NS_SUCCEEDED(dumpFile->MoveTo(pendingDir, EmptyString())) && + NS_SUCCEEDED(extraFile->MoveTo(pendingDir, EmptyString())); +} + +static void +OnChildProcessDumpRequested(void* aContext, + const ClientInfo* aClientInfo, + const xpstring* aFilePath) +{ + nsCOMPtr minidump; + nsCOMPtr extraFile; + + CreateFileFromPath(*aFilePath, getter_AddRefs(minidump)); + + if (!WriteExtraForMinidump(minidump, + Blacklist(kSubprocessBlacklist, + NS_ARRAY_LENGTH(kSubprocessBlacklist)), + getter_AddRefs(extraFile))) + return; + + if (ShouldReport()) + MoveToPending(minidump, extraFile); + + { + PRUint32 pid = aClientInfo->pid(); + + MutexAutoLock lock(*dumpMapLock); + pidToMinidump->Put(pid, minidump); + } +} + +static bool +OOPInitialized() +{ + return crashServer != NULL; +} + +static void +OOPInit() +{ + NS_ABORT_IF_FALSE(!OOPInitialized(), + "OOP crash reporter initialized more than once!"); + NS_ABORT_IF_FALSE(gExceptionHandler != NULL, + "attempt to initialize OOP crash reporter before in-process crashreporter!"); + +#if defined(XP_WIN) + childCrashNotifyPipe = + PR_smprintf("\\\\.\\pipe\\gecko-crash-server-pipe.%i", + static_cast(::GetCurrentProcessId())); + + const std::wstring dumpPath = gExceptionHandler->dump_path(); + crashServer = new CrashGenerationServer( + NS_ConvertASCIItoUTF16(childCrashNotifyPipe).get(), + NULL, // default security attributes + NULL, NULL, // we don't care about process connect here + OnChildProcessDumpRequested, NULL, + NULL, NULL, // we don't care about process exit here + true, // automatically generate dumps + &dumpPath); + +#elif defined(XP_LINUX) + if (!CrashGenerationServer::CreateReportChannel(&serverSocketFd, + &clientSocketFd)) + NS_RUNTIMEABORT("can't create crash reporter socketpair()"); + + const std::string dumpPath = gExceptionHandler->dump_path(); + crashServer = new CrashGenerationServer( + serverSocketFd, + OnChildProcessDumpRequested, NULL, + NULL, NULL, // we don't care about process exit here + true, // automatically generate dumps + &dumpPath); +#endif + + if (!crashServer->Start()) + NS_RUNTIMEABORT("can't start crash reporter server()"); + + pidToMinidump = new ChildMinidumpMap(); + pidToMinidump->Init(); + + dumpMapLock = new Mutex("CrashReporter::dumpMapLock"); +} + +static void +OOPDeinit() +{ + if (!OOPInitialized()) { + NS_WARNING("OOPDeinit() without successful OOPInit()"); + return; + } + + delete crashServer; + crashServer = NULL; + + delete dumpMapLock; + dumpMapLock = NULL; + + delete pidToMinidump; + pidToMinidump = NULL; + +#if defined(XP_WIN) + PR_Free(childCrashNotifyPipe); + childCrashNotifyPipe = NULL; +#endif +} + +#if defined(XP_WIN) +// Parent-side API for children +const char* +GetChildNotificationPipe() +{ + if (!GetEnabled()) + return kNullNotifyPipe; + + if (!OOPInitialized()) + OOPInit(); + + return childCrashNotifyPipe; +} + +// Child-side API +bool +SetRemoteExceptionHandler(const nsACString& crashPipe) +{ + // crash reporting is disabled + if (crashPipe.Equals(kNullNotifyPipe)) + return true; + + NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd"); + + gExceptionHandler = new google_breakpad:: + ExceptionHandler(L"", + NULL, // no filter callback + NULL, // no minidump callback + NULL, // no callback context + google_breakpad::ExceptionHandler::HANDLER_ALL, + MiniDumpNormal, + NS_ConvertASCIItoUTF16(crashPipe).BeginReading(), + NULL); + + // we either do remote or nothing, no fallback to regular crash reporting + return gExceptionHandler->IsOutOfProcess(); +} + +//-------------------------------------------------- +#elif defined(XP_UNIX) + +// Parent-side API for children +bool +CreateNotificationPipeForChild(int* childCrashFd, int* childCrashRemapFd) +{ + if (!GetEnabled()) { + *childCrashFd = -1; + *childCrashRemapFd = -1; + return true; + } + + if (!OOPInitialized()) + OOPInit(); + + *childCrashFd = clientSocketFd; + *childCrashRemapFd = kMagicChildCrashReportFd; + + return true; +} + +// Child-side API +bool +SetRemoteExceptionHandler() +{ + NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd"); + + gExceptionHandler = new google_breakpad:: + ExceptionHandler("", + NULL, // no filter callback + NULL, // no minidump callback + NULL, // no callback context + true, // install signal handlers + kMagicChildCrashReportFd); + + // we either do remote or nothing, no fallback to regular crash reporting + return gExceptionHandler->IsOutOfProcess(); +} + +#endif // XP_WIN + + +bool +TakeMinidumpForChild(PRUint32 childPid, nsILocalFile** dump) +{ + if (!GetEnabled()) + return false; + + MutexAutoLock lock(*dumpMapLock); + + nsCOMPtr d; + bool found = pidToMinidump->Get(childPid, getter_AddRefs(d)); + if (found) + pidToMinidump->Remove(childPid); + + *dump = NULL; + d.swap(*dump); + + return found; +} + +//----------------------------------------------------------------------------- +// CreatePairedMinidumps() and helpers +// +struct PairedDumpContext { + nsCOMPtr* minidump; + nsCOMPtr* extra; + const Blacklist& blacklist; +}; + +static bool +PairedDumpCallback(const XP_CHAR* dump_path, + const XP_CHAR* minidump_id, + void* context, +#ifdef XP_WIN32 + EXCEPTION_POINTERS* /*unused*/, + MDRawAssertionInfo* /*unused*/, +#endif + bool succeeded) +{ + PairedDumpContext* ctx = static_cast(context); + nsCOMPtr& minidump = *ctx->minidump; + nsCOMPtr& extra = *ctx->extra; + const Blacklist& blacklist = ctx->blacklist; + + xpstring dump(dump_path); + dump += XP_PATH_SEPARATOR; + dump += minidump_id; + dump += dumpFileExtension; + + CreateFileFromPath(dump, getter_AddRefs(minidump)); + return WriteExtraForMinidump(minidump, blacklist, getter_AddRefs(extra)); +} + +ThreadId +CurrentThreadId() +{ +#if defined(XP_WIN) + return ::GetCurrentThreadId(); +#elif defined(XP_LINUX) + return sys_gettid(); +#elif defined(XP_MACOSX) + return -1; +#else +# error "Unsupported platform" +#endif +} + +bool +CreatePairedMinidumps(ProcessHandle childPid, + ThreadId childBlamedThread, + nsAString* pairGUID, + nsILocalFile** childDump, + nsILocalFile** parentDump) +{ + if (!GetEnabled()) + return false; + + // create the UUID for the hang dump as a pair + nsresult rv; + nsCOMPtr uuidgen = + do_GetService("@mozilla.org/uuid-generator;1", &rv); + NS_ENSURE_SUCCESS(rv, false); + + nsID id; + rv = uuidgen->GenerateUUIDInPlace(&id); + NS_ENSURE_SUCCESS(rv, false); + + char chars[NSID_LENGTH]; + id.ToProvidedString(chars); + CopyASCIItoUTF16(chars, *pairGUID); + + // trim off braces + pairGUID->Cut(0, 1); + pairGUID->Cut(pairGUID->Length()-1, 1); + + // dump the child + nsCOMPtr childMinidump; + nsCOMPtr childExtra; + Blacklist childBlacklist(kSubprocessBlacklist, + NS_ARRAY_LENGTH(kSubprocessBlacklist)); + PairedDumpContext childCtx = + { &childMinidump, &childExtra, childBlacklist }; + if (!google_breakpad::ExceptionHandler::WriteMinidumpForChild( + childPid, + childBlamedThread, + gExceptionHandler->dump_path(), + PairedDumpCallback, + &childCtx)) + return false; + + // dump the parent + nsCOMPtr parentMinidump; + nsCOMPtr parentExtra; + // nothing's blacklisted for this process + Blacklist parentBlacklist; + PairedDumpContext parentCtx = + { &parentMinidump, &parentExtra, parentBlacklist }; + if (!google_breakpad::ExceptionHandler::WriteMinidump( + gExceptionHandler->dump_path(), + true, // write exception stream + PairedDumpCallback, + &parentCtx)) + return false; + + // success + if (ShouldReport()) { + MoveToPending(childMinidump, childExtra); + MoveToPending(parentMinidump, parentExtra); + } + + *childDump = NULL; + *parentDump = NULL; + childMinidump.swap(*childDump); + parentMinidump.swap(*parentDump); + + return true; +} + +bool +UnsetRemoteExceptionHandler() +{ + delete gExceptionHandler; + gExceptionHandler = NULL; + return true; +} + +#endif // MOZ_IPC + } // namespace CrashReporter diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/nsExceptionHandler.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/nsExceptionHandler.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/nsExceptionHandler.h 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/nsExceptionHandler.h 2010-04-16 17:32:48.000000000 +0100 @@ -39,9 +39,12 @@ #define nsExceptionHandler_h__ #include "nscore.h" +#include "nsDataHashtable.h" #include "nsXPCOM.h" #include "nsStringGlue.h" +#include "nsIFile.h" + #if defined(XP_WIN32) #ifdef WIN32_LEAN_AND_MEAN #undef WIN32_LEAN_AND_MEAN @@ -62,12 +65,90 @@ nsresult SetRestartArgs(int argc, char** argv); nsresult SetupExtraData(nsILocalFile* aAppDataDirectory, const nsACString& aBuildID); + +// Functions for working with minidumps and .extras +typedef nsDataHashtable AnnotationTable; + +bool GetMinidumpForID(const nsAString& id, nsILocalFile** minidump); +bool GetIDFromMinidump(nsILocalFile* minidump, nsAString& id); +bool GetExtraFileForID(const nsAString& id, nsILocalFile** extraFile); +bool GetExtraFileForMinidump(nsILocalFile* minidump, nsILocalFile** extraFile); +bool AppendExtraData(const nsAString& id, const AnnotationTable& data); +bool AppendExtraData(nsILocalFile* extraFile, const AnnotationTable& data); + #ifdef XP_WIN32 nsresult WriteMinidumpForException(EXCEPTION_POINTERS* aExceptionInfo); #endif #ifdef XP_MACOSX nsresult AppendObjCExceptionInfoToAppNotes(void *inException); #endif +nsresult GetSubmitReports(PRBool* aSubmitReport); +nsresult SetSubmitReports(PRBool aSubmitReport); + +#ifdef MOZ_IPC +// Out-of-process crash reporter API. + +// Return true iff a dump was found for |childPid|, and return the +// path in |dump|. The caller owns the last reference to |dump| if it +// is non-NULL. +bool TakeMinidumpForChild(PRUint32 childPid, + nsILocalFile** dump NS_OUTPARAM); + +#ifdef XP_WIN +typedef HANDLE ProcessHandle; +typedef DWORD ThreadId; +#else +typedef int ProcessHandle; +typedef int ThreadId; +#endif + +// Return the current thread's ID. +// +// XXX: this is a somewhat out-of-place interface to expose through +// crashreporter, but it takes significant work to call sys_gettid() +// correctly on Linux and breakpad has already jumped through those +// hoops for us. +ThreadId CurrentThreadId(); + +// Create new minidumps that are snapshots of the state of this parent +// process and |childPid|. Return true on success along with the +// minidumps and a new UUID that can be used to correlate the dumps. +// +// If this function fails, it's the caller's responsibility to clean +// up |childDump| and |parentDump|. Either or both can be created and +// returned non-null on failure. +bool CreatePairedMinidumps(ProcessHandle childPid, + ThreadId childBlamedThread, + nsAString* pairGUID NS_OUTPARAM, + nsILocalFile** childDump NS_OUTPARAM, + nsILocalFile** parentDump NS_OUTPARAM); + +# if defined(XP_WIN32) +// Parent-side API for children +const char* GetChildNotificationPipe(); + +// Child-side API +bool SetRemoteExceptionHandler(const nsACString& crashPipe); + +# elif defined(XP_LINUX) +// Parent-side API for children + +// Set the outparams for crash reporter server's fd (|childCrashFd|) +// and the magic fd number it should be remapped to +// (|childCrashRemapFd|) before exec() in the child process. +// |SetRemoteExceptionHandler()| in the child process expects to find +// the server at |childCrashRemapFd|. Return true iff successful. +// +// If crash reporting is disabled, both outparams will be set to -1 +// and |true| will be returned. +bool CreateNotificationPipeForChild(int* childCrashFd, int* childCrashRemapFd); + +// Child-side API +bool SetRemoteExceptionHandler(); +#endif // XP_WIN32 + +bool UnsetRemoteExceptionHandler(); +#endif // MOZ_IPC } #endif /* nsExceptionHandler_h__ */ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/test/browser/browser_aboutCrashesResubmit.js firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/test/browser/browser_aboutCrashesResubmit.js --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/test/browser/browser_aboutCrashesResubmit.js 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/test/browser/browser_aboutCrashesResubmit.js 2010-04-16 17:32:48.000000000 +0100 @@ -89,6 +89,12 @@ }); } } + function csp_fail() { + browser.removeEventListener("CrashSubmitFailed", csp_fail, true); + ok(false, "failed to submit crash report!"); + cleanup_and_finish(); + } + browser.addEventListener("CrashSubmitFailed", csp_fail, true); browser.addEventListener("load", csp_onload, true); function csp_pageshow() { browser.removeEventListener("pageshow", csp_pageshow, true); diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/test/browser/crashreport.sjs firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/test/browser/crashreport.sjs --- firefox-3.6.3+nobinonly/mozilla/toolkit/crashreporter/test/browser/crashreport.sjs 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/crashreporter/test/browser/crashreport.sjs 2010-04-16 17:32:48.000000000 +0100 @@ -145,7 +145,7 @@ else if (request.method == "POST") { let formData = parseMultipartForm(request); - if ('upload_file_minidump' in formData) { + if (formData && 'upload_file_minidump' in formData) { response.setHeader("Content-Type", "text/plain", false); let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"] @@ -161,6 +161,7 @@ response.write("CrashID=" + uuid + "\n"); } else { + dump('*** crashreport.sjs: Malformed request?\n'); response.setStatusLine(request.httpVersion, 400, "Bad Request"); response.write("Missing minidump file"); } diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/library/dlldeps-xul.cpp firefox-3.6.4+build1+nobinonly/mozilla/toolkit/library/dlldeps-xul.cpp --- firefox-3.6.3+nobinonly/mozilla/toolkit/library/dlldeps-xul.cpp 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/library/dlldeps-xul.cpp 2010-04-16 17:32:48.000000000 +0100 @@ -49,4 +49,13 @@ XRE_CreateAppData(nsnull, nsnull); XRE_ParseAppData(nsnull, nsnull); XRE_FreeAppData(nsnull); + XRE_ChildProcessTypeToString(GeckoProcessType_Default); + XRE_StringToChildProcessType(""); + XRE_GetProcessType(); +#ifdef MOZ_IPC + XRE_InitChildProcess(0, nsnull, GeckoProcessType_Default); + XRE_InitParentProcess(0, nsnull, nsnull, nsnull); + XRE_RunAppShell(); + XRE_ShutdownChildProcess(); +#endif } diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/library/libxul-config.mk firefox-3.6.4+build1+nobinonly/mozilla/toolkit/library/libxul-config.mk --- firefox-3.6.3+nobinonly/mozilla/toolkit/library/libxul-config.mk 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/library/libxul-config.mk 2010-04-16 17:32:48.000000000 +0100 @@ -91,6 +91,27 @@ endif # dependent libraries +ifdef MOZ_IPC +STATIC_LIBS += \ + domplugins_s \ + mozipc_s \ + mozipdlgen_s \ + chromium_s \ + gfxipc_s \ + $(NULL) + +ifdef MOZ_IPDL_TESTS +STATIC_LIBS += ipdlunittest_s +endif + +ifeq (Linux,$(OS_ARCH)) +OS_LIBS += -lrt +endif +ifeq (WINNT,$(OS_ARCH)) +OS_LIBS += psapi.lib dbghelp.lib +endif +endif + STATIC_LIBS += \ xpcom_core \ ucvutil_s \ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/locales/en-US/chrome/mozapps/plugins/plugins.dtd firefox-3.6.4+build1+nobinonly/mozilla/toolkit/locales/en-US/chrome/mozapps/plugins/plugins.dtd --- firefox-3.6.3+nobinonly/mozilla/toolkit/locales/en-US/chrome/mozapps/plugins/plugins.dtd 2010-04-02 16:59:32.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/locales/en-US/chrome/mozapps/plugins/plugins.dtd 2010-04-16 17:32:48.000000000 +0100 @@ -23,3 +23,16 @@ + + + + + + + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/locales/filter.py firefox-3.6.4+build1+nobinonly/mozilla/toolkit/locales/filter.py --- firefox-3.6.3+nobinonly/mozilla/toolkit/locales/filter.py 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/locales/filter.py 2010-04-16 17:32:48.000000000 +0100 @@ -0,0 +1,13 @@ +def test(mod, path, entity = None): + import re + # ignore anything but toolkit + if mod not in ("netwerk", "dom", "toolkit", "security/manager", + "extensions/spellcheck"): + return None + + # "report" Lorentz strings, don't "error" + if mod == "toolkit" and path == "chrome/mozapps/plugins/plugins.dtd": + if entity.startswith('reloadPlugin.'): return "report" + if entity.startswith('report.'): return "report" + + return "error" diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/downloads/src/DownloadLastDir.jsm firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/downloads/src/DownloadLastDir.jsm --- firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/downloads/src/DownloadLastDir.jsm 2010-04-02 16:59:32.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/downloads/src/DownloadLastDir.jsm 2010-04-16 17:32:48.000000000 +0100 @@ -34,8 +34,13 @@ * * ***** END LICENSE BLOCK ***** */ +const LAST_DIR_PREF = "browser.download.lastDir"; + var EXPORTED_SYMBOLS = [ "gDownloadLastDir" ]; +let prefSvc = Components.classes["@mozilla.org/preferences-service;1"] + .getService(Components.interfaces.nsIPrefBranch); + let observer = { QueryInterface: function (aIID) { if (aIID.equals(Components.interfaces.nsIObserver) || @@ -45,13 +50,23 @@ throw Components.results.NS_NOINTERFACE; }, observe: function (aSubject, aTopic, aData) { - gDownloadLastDirFile = null; + switch (aTopic) { + case "private-browsing": + gDownloadLastDirFile = null; + break; + case "browser:purge-session-history": + gDownloadLastDirFile = null; + if (prefSvc.prefHasUserValue(LAST_DIR_PREF)) + prefSvc.clearUserPref(LAST_DIR_PREF); + break; + } } }; -Components.classes["@mozilla.org/observer-service;1"] - .getService(Components.interfaces.nsIObserverService) - .addObserver(observer, "private-browsing", true); +let os = Components.classes["@mozilla.org/observer-service;1"] + .getService(Components.interfaces.nsIObserverService); +os.addObserver(observer, "private-browsing", true); +os.addObserver(observer, "browser:purge-session-history", true); let gDownloadLastDirFile = null; let gDownloadLastDir = { diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/downloads/tests/chrome/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/downloads/tests/chrome/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/downloads/tests/chrome/Makefile.in 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/downloads/tests/chrome/Makefile.in 2010-04-16 17:32:48.000000000 +0100 @@ -66,7 +66,7 @@ test_space_key_pauses_resumes.xul \ test_privatebrowsing_title.xul \ test_ui_stays_open_on_alert_clickback.xul \ - test_unkownContentType_dialog_layout.xul \ + test_unknownContentType_dialog_layout.xul \ test_bug_412360.xul \ test_bug_429247.xul \ unknownContentType_dialog_layout_data.txt \ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/downloads/tests/chrome/test_unknownContentType_dialog_layout.xul firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/downloads/tests/chrome/test_unknownContentType_dialog_layout.xul --- firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/downloads/tests/chrome/test_unknownContentType_dialog_layout.xul 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/downloads/tests/chrome/test_unknownContentType_dialog_layout.xul 2010-04-16 17:32:48.000000000 +0100 @@ -0,0 +1,144 @@ + + + + + + + + +

    + +
    
    +  
    +
    +  
    +
    diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/downloads/tests/chrome/test_unkownContentType_dialog_layout.xul firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/downloads/tests/chrome/test_unkownContentType_dialog_layout.xul --- firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/downloads/tests/chrome/test_unkownContentType_dialog_layout.xul 2010-04-02 16:59:13.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/downloads/tests/chrome/test_unkownContentType_dialog_layout.xul 1970-01-01 01:00:00.000000000 +0100 @@ -1,144 +0,0 @@ - - - - - - - - -

    - -
    
    -  
    -
    -  
    -
    diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/downloads/tests/unit/test_DownloadLastDir.js firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/downloads/tests/unit/test_DownloadLastDir.js --- firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/downloads/tests/unit/test_DownloadLastDir.js 2010-04-02 16:59:32.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/downloads/tests/unit/test_DownloadLastDir.js 2010-04-16 17:32:48.000000000 +0100 @@ -41,6 +41,13 @@ let Cu = Components.utils; Cu.import("resource://gre/modules/DownloadLastDir.jsm"); + function clearHistory() { + // simulate clearing the private data + Cc["@mozilla.org/observer-service;1"]. + getService(Ci.nsIObserverService). + notifyObservers(null, "browser:purge-session-history", ""); + } + do_check_eq(typeof gDownloadLastDir, "object"); do_check_eq(gDownloadLastDir.file, null); @@ -56,6 +63,10 @@ do_check_eq(gDownloadLastDir.file, null); gDownloadLastDir.file = tmpDir; + clearHistory(); + do_check_eq(gDownloadLastDir.file, null); + gDownloadLastDir.file = tmpDir; + let pb; try { pb = Cc["@mozilla.org/privatebrowsing;1"]. @@ -82,5 +93,15 @@ pb.privateBrowsingEnabled = false; do_check_eq(gDownloadLastDir.file, null); + + pb.privateBrowsingEnabled = true; + gDownloadLastDir.file = tmpDir; + do_check_neq(gDownloadLastDir.file, null); + clearHistory(); + do_check_eq(gDownloadLastDir.file, null); + + pb.privateBrowsingEnabled = false; + do_check_eq(gDownloadLastDir.file, null); + prefs.clearUserPref("browser.privatebrowsing.keep_current_session"); } diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/installer/package-name.mk firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/installer/package-name.mk --- firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/installer/package-name.mk 2010-04-02 16:59:14.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/installer/package-name.mk 2010-04-16 17:32:48.000000000 +0100 @@ -138,6 +138,10 @@ endif endif PKG_PATH = $(MOZ_PKG_PLATFORM)/$(AB_CD)/ +ifeq ($(MOZ_APP_NAME),xulrunner) +PKG_PATH = runtimes/ +PKG_BASENAME = $(MOZ_APP_NAME)-$(MOZ_PKG_VERSION).$(AB_CD).$(MOZ_PKG_PLATFORM) +endif PKG_INST_PATH = $(PKG_PATH) PKG_UPDATE_BASENAME = $(MOZ_PKG_APPNAME_LC)-$(MOZ_PKG_VERSION) PKG_UPDATE_PATH = update/$(PKG_PATH) @@ -159,9 +163,9 @@ TEST_PACKAGE = $(PKG_BASENAME).tests.tar.bz2 ifneq (,$(wildcard $(DIST)/bin/application.ini)) -BUILDID = $(shell $(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(DIST)/bin/application.ini App BuildID) +BUILDID = $(shell $(PYTHON) $(MOZILLA_DIR)/config/printconfigsetting.py $(DIST)/bin/application.ini App BuildID) else -BUILDID = $(shell $(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(DIST)/bin/platform.ini Build BuildID) +BUILDID = $(shell $(PYTHON) $(MOZILLA_DIR)/config/printconfigsetting.py $(DIST)/bin/platform.ini Build BuildID) endif MOZ_SOURCE_STAMP = $(shell hg -R $(topsrcdir) parent --template="{node|short}\n" 2>/dev/null) diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/installer/packager.mk firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/installer/packager.mk --- firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/installer/packager.mk 2010-04-02 16:59:14.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/installer/packager.mk 2010-04-16 17:32:48.000000000 +0100 @@ -78,8 +78,12 @@ # By default, the SDK uses the same packaging type as the main bundle, # but on mac it is a .tar.bz2 +SDK_PATH = $(PKG_PATH) +ifeq ($(MOZ_APP_NAME),xulrunner) +SDK_PATH = sdk/ +endif SDK_SUFFIX = $(PKG_SUFFIX) -SDK = $(PKG_PATH)$(PKG_BASENAME).sdk$(SDK_SUFFIX) +SDK = $(SDK_PATH)$(PKG_BASENAME).sdk$(SDK_SUFFIX) MAKE_PACKAGE = $(error What is a $(MOZ_PKG_FORMAT) package format?); MAKE_CAB = $(error Don't know how to make a CAB!); @@ -92,7 +96,7 @@ CABARGS += -faststart endif VSINSTALLDIR ?= $(error VSINSTALLDIR not set, must be set to the Visual Studio install directory) -MAKE_CAB = $(PYTHON) $(topsrcdir)/build/package/wince/make_wince_cab.py \ +MAKE_CAB = $(PYTHON) $(MOZILLA_DIR)/build/package/wince/make_wince_cab.py \ $(CABARGS) "$(VSINSTALLDIR)/SmartDevices/SDK/SDKTools/cabwiz.exe" \ "$(MOZ_PKG_DIR)" "$(MOZ_APP_DISPLAYNAME)" "$(PKG_PATH)$(PKG_BASENAME).cab" endif @@ -193,6 +197,9 @@ # individual dmg and are created by hdiutil. SDK_SUFFIX = .tar.bz2 SDK = $(MOZ_PKG_APPNAME)-$(MOZ_PKG_VERSION).$(AB_CD).mac-$(TARGET_CPU).sdk$(SDK_SUFFIX) +ifeq ($(MOZ_APP_NAME),xulrunner) +SDK = $(SDK_PATH)$(MOZ_APP_NAME)-$(MOZ_PKG_VERSION).$(AB_CD).mac-$(TARGET_CPU).sdk$(SDK_SUFFIX) +endif MAKE_SDK = $(CREATE_FINAL_TAR) - $(MOZ_APP_NAME)-sdk | bzip2 -vf > $(SDK) endif @@ -499,6 +506,7 @@ # sdk/lib is the same as sdk/sdk/lib (cd $(DIST)/sdk/lib && tar $(TAR_CREATE_FLAGS) - .) | \ (cd $(DIST)/$(MOZ_APP_NAME)-sdk/lib && tar -xf -) + $(NSINSTALL) -D $(DIST)/$(SDK_PATH) cd $(DIST) && $(MAKE_SDK) ifeq ($(OS_TARGET), WINNT) diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/jar.mn firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/jar.mn --- firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/jar.mn 2010-04-02 16:59:14.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/jar.mn 2010-04-16 17:32:48.000000000 +0100 @@ -19,8 +19,16 @@ content/mozapps/plugins/pluginInstallerWizard.css (plugins/content/pluginInstallerWizard.css) content/mozapps/plugins/pluginInstallerDatasource.js (plugins/content/pluginInstallerDatasource.js) content/mozapps/plugins/pluginInstallerService.js (plugins/content/pluginInstallerService.js) - content/mozapps/plugins/missingPlugin.xml (plugins/content/missingPlugin.xml) - content/mozapps/plugins/missingPluginBinding.css (plugins/content/missingPluginBinding.css) + content/mozapps/plugins/pluginProblem.xml (plugins/content/pluginProblem.xml) + content/mozapps/plugins/pluginProblemContent.css (plugins/content/pluginProblemContent.css) + content/mozapps/plugins/pluginProblemBinding.css (plugins/content/pluginProblemBinding.css) + content/mozapps/plugins/pluginFinderBinding.css (plugins/content/pluginFinderBinding.css) + content/mozapps/plugins/pluginHelp-16.png (plugins/content/pluginHelp-16.png) + content/mozapps/plugins/pluginBlocked.png (plugins/content/pluginBlocked.png) + content/mozapps/plugins/pluginCrashed.png (plugins/content/pluginCrashed.png) + content/mozapps/plugins/pluginDisabled.png (plugins/content/pluginDisabled.png) + content/mozapps/plugins/pluginDownload.png (plugins/content/pluginDownload.png) + content/mozapps/plugins/pluginProblemLorentz.css (plugins/content/pluginProblemLorentz.css) content/mozapps/handling/handler.css (handling/content/handler.css) content/mozapps/handling/handler.xml (handling/content/handler.xml) content/mozapps/handling/dialog.xul (handling/content/dialog.xul) diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/missingPluginBinding.css firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/missingPluginBinding.css --- firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/missingPluginBinding.css 2010-04-02 16:59:14.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/missingPluginBinding.css 1970-01-01 01:00:00.000000000 +0100 @@ -1,52 +0,0 @@ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (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.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the stylesheet for the pluginfinder XBL binding. - * - * The Initial Developer of the Original Code is - * Christian Biesinger . - * Portions created by the Initial Developer are Copyright (C) 2005 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -@namespace url(http://www.w3.org/1999/xhtml); /* set default namespace to HTML */ - -embed:-moz-type-unsupported, applet:-moz-type-unsupported, -object:-moz-has-handlerref:-moz-type-unsupported { - -moz-binding: url('chrome://mozapps/content/plugins/missingPlugin.xml#missingPlugin') !important; -} - -embed:-moz-handler-disabled, applet:-moz-handler-disabled, -object:-moz-has-handlerref:-moz-handler-disabled { - -moz-binding: url('chrome://mozapps/content/plugins/missingPlugin.xml#disabledPlugin') !important; -} - -embed:-moz-handler-blocked, applet:-moz-handler-blocked, -object:-moz-has-handlerref:-moz-handler-blocked { - -moz-binding: url('chrome://mozapps/content/plugins/missingPlugin.xml#blockedPlugin') !important; -} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/missingPlugin.xml firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/missingPlugin.xml --- firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/missingPlugin.xml 2010-04-02 16:59:14.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/missingPlugin.xml 1970-01-01 01:00:00.000000000 +0100 @@ -1,130 +0,0 @@ - - - - - - - - - - - - - - - &missingPlugin.label; - - - - - - - - - - - - - - - - - - - - - - - - - &disabledPlugin.label; - - - - - - - - - - - - - - - - - - - - - - - - &blockedPlugin.label; - - - - - - - - - Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginBlocked.png and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginBlocked.png differ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginCrashed.png and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginCrashed.png differ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginDisabled.png and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginDisabled.png differ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginDownload.png and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginDownload.png differ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginFinderBinding.css firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginFinderBinding.css --- firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginFinderBinding.css 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginFinderBinding.css 2010-04-16 17:32:48.000000000 +0100 @@ -0,0 +1,49 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the stylesheet for the pluginfinder XBL binding. + * + * The Initial Developer of the Original Code is + * Christian Biesinger . + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Justin Dolske + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +@namespace url(http://www.w3.org/1999/xhtml); /* set default namespace to HTML */ + +/* This binding is specified separately from the others so that Linux distros can + override the default Mozilla plugin finder service with their own mechanism. */ +embed:-moz-type-unsupported, +applet:-moz-type-unsupported, +object:-moz-has-handlerref:-moz-type-unsupported { + display: inline-block; + overflow: hidden; + -moz-binding: url('chrome://mozapps/content/plugins/pluginProblem.xml#pluginProblem') !important; +} Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginHelp-16.png and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginHelp-16.png differ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginProblemBinding.css firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginProblemBinding.css --- firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginProblemBinding.css 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginProblemBinding.css 2010-04-16 17:32:48.000000000 +0100 @@ -0,0 +1,53 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the stylesheet for the pluginfinder XBL binding. + * + * The Initial Developer of the Original Code is + * Christian Biesinger . + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Justin Dolske + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +@namespace url(http://www.w3.org/1999/xhtml); /* set default namespace to HTML */ + +embed:-moz-handler-disabled, +embed:-moz-handler-blocked, +embed:-moz-handler-crashed, +applet:-moz-handler-disabled, +applet:-moz-handler-blocked, +applet:-moz-handler-crashed, +object:-moz-has-handlerref:-moz-handler-disabled, +object:-moz-has-handlerref:-moz-handler-blocked, +object:-moz-handler-crashed { + display: inline-block; + overflow: hidden; + -moz-binding: url('chrome://mozapps/content/plugins/pluginProblem.xml#pluginProblem') !important; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginProblemContent.css firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginProblemContent.css --- firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginProblemContent.css 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginProblemContent.css 2010-04-16 17:32:48.000000000 +0100 @@ -0,0 +1,70 @@ +@namespace html url(http://www.w3.org/1999/xhtml); + +html|object:not([width]), html|object[width=""], +html|embed:not([width]), html|embed[width=""], +html|applet:not([width]), html|applet[width=""] { + width: 240px; +} + +html|object:not([height]), html|object[height=""], +html|embed:not([height]), html|embed[height=""], +html|applet:not([height]), html|applet[height=""] { + height: 200px; +} + +:-moz-type-unsupported .mainBox { + cursor: pointer; +} + +:-moz-type-unsupported .mainBox, +:-moz-handler-disabled .mainBox, +:-moz-handler-blocked .mainBox { + -moz-user-focus: normal; +} +:-moz-type-unsupported .mainBox:focus, +:-moz-handler-disabled .mainBox:focus, +:-moz-handler-blocked .mainBox:focus { + outline: 1px dotted; +} + +.mainBox { + width: inherit; + height: inherit; + overflow: hidden; + direction: ltr; + unicode-bidi: embed; +} + +.mainBox[chromedir="rtl"] { + direction: rtl; +} + +.msg { + display: none; +} + +:-moz-type-unsupported .msgUnsupported, +:-moz-handler-disabled .msgDisabled, +:-moz-handler-blocked .msgBlocked, +:-moz-handler-crashed .msgCrashed { + display: block; +} + +.submitStatus[status="noReport"] .msgNoCrashReport, +.submitStatus[status="please"] .msgPleaseSubmit, +.submitStatus[status="noSubmit"] .msgNotSubmitted, +.submitStatus[status="submitting"] .msgSubmitting, +.submitStatus[status="success"] .msgSubmitted, +.submitStatus[status="failed"] .msgSubmitFailed, +.submitStatus[status]:not([status="please"]) .msgReload { + display: block; +} +.submitStatus[status="please"] .msgReload { + /* Take up space when invisible, so stuff doesn't shift upon reveal. */ + display: block; + visibility: hidden; +} + +.helpIcon { + cursor: pointer; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginProblemLorentz.css firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginProblemLorentz.css --- firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginProblemLorentz.css 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginProblemLorentz.css 2010-04-16 17:32:48.000000000 +0100 @@ -0,0 +1,72 @@ +@namespace html url(http://www.w3.org/1999/xhtml); + +/* These styles affect only the bound element, not other page content. */ + +.mainBox { + text-align: center; + background-image: -moz-repeating-linear-gradient(-45deg, + rgba(65, 65, 65, 0.8), + rgba(65, 65, 65, 0.8) 20px, + rgba(69, 69, 69, 0.8) 20px, + rgba(69, 69, 69, 0.8) 40px); + color: white; + -moz-border-radius: 12px; + /* recessed effect with dark inner shadow and lightened bottom */ + -moz-box-shadow: inset 0 1px 5px rgba(0,0,0,0.8), + 0 1px 0 rgba(255,255,255,0.2); + padding: 5px; + -moz-user-select: none; +} + +html|a { + color: white; +} + +.icon { + min-width: 48px; + min-height: 48px; + background-position: center; + background-repeat: no-repeat; +} +:-moz-type-unsupported .icon { + background-image: url(chrome://mozapps/content/plugins/pluginDownload.png); +} +:-moz-handler-disabled .icon { + background-image: url(chrome://mozapps/content/plugins/pluginDisabled.png); +} +:-moz-handler-blocked .icon { + background-image: url(chrome://mozapps/content/plugins/pluginBlocked.png); +} +:-moz-handler-crashed .icon { + background-image: url(chrome://mozapps/content/plugins/pluginCrashed.png); +} + +.throbber { + padding-left: 16px; /* width of the background image */ + background: url(chrome://global/skin/icons/loading_16.png) no-repeat; + margin-left: 5px; +} + +.msg { + font: message-box; + font-size: 12px; + cursor: default; + text-shadow: rgba(0,0,0,0.8) 0 0 5px; +} + +.submitStatus div { + min-height: 19px; /* height of biggest line (with throbber) */ +} + +.msgBottomLinks { + padding-left: 2px; + padding-right: 2px; +} + +.helpIcon { + float: left; + display: inline-block; + min-width: 16px; + min-height: 16px; + background: url(chrome://mozapps/content/plugins/pluginHelp-16.png) no-repeat; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginProblem.xml firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginProblem.xml --- firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginProblem.xml 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/content/pluginProblem.xml 2010-04-16 17:32:48.000000000 +0100 @@ -0,0 +1,93 @@ + + + + +%bindingsDTD; +%globalDTD; + + + + + + + + + +]> + + + + + + + + + + + + + + &missingPlugin.label; + &disabledPlugin.label; + &blockedPlugin.label; + + + + &report.please; + &report.submitting; + &report.submitted; + &report.disabled; + &report.failed; + &report.unavailable; + + &reloadPlugin.pre;&reloadPlugin.middle;&reloadPlugin.post; + + + + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/pluginGlue.js firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/pluginGlue.js --- firefox-3.6.3+nobinonly/mozilla/toolkit/mozapps/plugins/pluginGlue.js 2010-04-02 16:59:14.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/mozapps/plugins/pluginGlue.js 2010-04-16 17:32:48.000000000 +0100 @@ -20,6 +20,8 @@ * * Contributor(s): * + * Justin Dolske + * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), @@ -34,45 +36,24 @@ * * ***** END LICENSE BLOCK ***** */ -/** - * @file - * This file contains the agent style sheet registration code for the - * pluginfinder XBL binding. It's not a real module, it only exists so that we - * can call addCategoryEntry / deleteCategoryEntry. - */ - -var module = { - categoryEntry: "pluginfinder xbl binding", - categoryValue: "chrome://mozapps/content/plugins/missingPluginBinding.css", - - // registerSelf: Register this component. - registerSelf: function (compMgr, fileSpec, location, type) { - var catman = Components.classes['@mozilla.org/categorymanager;1'] - .getService(Components.interfaces.nsICategoryManager); - catman.addCategoryEntry("agent-style-sheets", this.categoryEntry, - this.categoryValue, true, true); +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); - }, +function pluginBindings() { } - // unregisterSelf: Unregister this component. - unregisterSelf: function (aCompMgr, aLocation, aLoaderStr) { - catman.deleteCategoryEntry("agent-style-sheets", this.categoryEntry, - true); - }, - - // getClassObject: Return this component's factory object. - getClassObject: function (compMgr, cid, iid) { - throw Components.results.NS_ERROR_NOT_IMPLEMENTED; - }, - - // canUnload: n/a (returns true) - canUnload: function(compMgr) { - return true; - } +pluginBindings.prototype = { + // This isn't a real component, we're just using categories to + // automatically add our stylesheets during module registration. + classDescription: "plugin bindings", + classID: Components.ID("12663f3a-a311-4606-83eb-b6b9108dcc36"), + contractID: "@mozilla.org/plugin-bindings;1", + QueryInterface: XPCOMUtils.generateQI([]), + + _xpcom_categories: [{ category: "agent-style-sheets", + entry: "pluginfinder xbl binding", + value: "chrome://mozapps/content/plugins/pluginFinderBinding.css"}, + { category: "agent-style-sheets", + entry: "pluginproblem xbl binding", + value: "chrome://mozapps/content/plugins/pluginProblemBinding.css"}] }; -// NSGetModule: Return the nsIModule object. -function NSGetModule(compMgr, fileSpec) { - return module; -} - +var NSGetModule = XPCOMUtils.generateNSGetModule([pluginBindings]); diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/themes/pinstripe/global/button.css firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/pinstripe/global/button.css --- firefox-3.6.3+nobinonly/mozilla/toolkit/themes/pinstripe/global/button.css 2010-04-02 16:59:15.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/pinstripe/global/button.css 2010-04-16 17:32:49.000000000 +0100 @@ -103,3 +103,40 @@ margin: 0 !important; padding: 0 !important; } + +/* ::::: help button ::::: */ + +button[dlgtype="help"] { + min-width: 1px; + padding: 0; + -moz-appearance: none; + -moz-box-align: start; + height: 24px; + width: 24px; + margin: 3px 4px; +} + +button[dlgtype="help"][disabled] { + opacity: 0.5; +} + +button[dlgtype="help"]:focus { + outline: 2px solid -moz-mac-focusring; + outline-offset: -2px; + -moz-outline-radius: 100%; +} + +button[dlgtype="help"] > .button-box > .button-icon { + list-style-image: url("chrome://global/skin/icons/question-mark.png"); + -moz-image-region: rect(0 24px 24px 0); + padding: 0; + margin: 0; +} + +button[dlgtype="help"]:active > .button-box > .button-icon { + -moz-image-region: rect(0 48px 24px 24px); +} + +button[dlgtype="help"] > .button-box > .button-text { + display: none; +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/themes/pinstripe/global/dialog.css firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/pinstripe/global/dialog.css --- firefox-3.6.3+nobinonly/mozilla/toolkit/themes/pinstripe/global/dialog.css 2010-04-02 16:59:15.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/pinstripe/global/dialog.css 2010-04-16 17:32:49.000000000 +0100 @@ -63,41 +63,6 @@ font: menu; } -.dialog-button[dlgtype="help"] { - min-width: 1px; - padding: 0; - -moz-appearance: none; - -moz-box-align: start; - height: 24px; - width: 24px; - margin: 3px 4px; -} - -.dialog-button[dlgtype="help"][disabled] { - opacity: 0.5; -} - -.dialog-button[dlgtype="help"]:focus { - outline: 2px solid -moz-mac-focusring; - outline-offset: -2px; - -moz-outline-radius: 100%; -} - -.dialog-button[dlgtype="help"] > .button-box > .button-icon { - list-style-image: url("chrome://global/skin/icons/question-mark.png"); - -moz-image-region: rect(0 24px 24px 0); - padding: 0; - margin: 0; -} - -.dialog-button[dlgtype="help"]:active > .button-box > .button-icon { - -moz-image-region: rect(0 48px 24px 24px); -} - -.dialog-button[dlgtype="help"] > .button-box > .button-text { - display: none; -} - /* ::::: dialog header ::::: */ dialogheader { diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/themes/pinstripe/mozapps/jar.mn firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/pinstripe/mozapps/jar.mn --- firefox-3.6.3+nobinonly/mozilla/toolkit/themes/pinstripe/mozapps/jar.mn 2010-04-02 16:59:15.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/pinstripe/mozapps/jar.mn 2010-04-16 17:32:49.000000000 +0100 @@ -19,7 +19,7 @@ skin/classic/mozapps/extensions/eula.css (extensions/eula.css) skin/classic/mozapps/extensions/blocklist.css (extensions/blocklist.css) skin/classic/mozapps/passwordmgr/key.png (passwordmgr/key.png) - skin/classic/mozapps/plugins/missingPlugin.css (plugins/missingPlugin.css) + skin/classic/mozapps/plugins/pluginProblem.css (plugins/pluginProblem.css) skin/classic/mozapps/plugins/pluginGeneric.png (plugins/pluginGeneric.png) skin/classic/mozapps/plugins/pluginDisabled.png (plugins/pluginDisabled.png) skin/classic/mozapps/plugins/pluginBlocked.png (plugins/pluginBlocked.png) diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/themes/pinstripe/mozapps/plugins/missingPlugin.css firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/pinstripe/mozapps/plugins/missingPlugin.css --- firefox-3.6.3+nobinonly/mozilla/toolkit/themes/pinstripe/mozapps/plugins/missingPlugin.css 2010-04-02 16:59:15.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/pinstripe/mozapps/plugins/missingPlugin.css 1970-01-01 01:00:00.000000000 +0100 @@ -1,68 +0,0 @@ -/* - * This file's style only applies to broken objects, not the rest - * of the page. - */ - -@namespace html url(http://www.w3.org/1999/xhtml); - -html|object:not([width]), html|object[width=""], -html|embed:not([width]), html|embed[width=""], -html|applet:not([width]), html|applet[width=""] { - width: 240px; -} - -html|object:not([height]), html|object[height=""], -html|embed:not([height]), html|embed[height=""], -html|applet:not([height]), html|applet[height=""] { - height: 200px; -} - -html|object > *|*, -html|embed > *|*, -html|applet > *|* { - width: inherit; - height: inherit; -} - -html|a, html|div { - display: inline-block; - /* overflow:hidden makes baseline vertical-alignment act like plugins */ - overflow: hidden; - vertical-align: inherit; - border: 1px outset; - padding: 5px; - font-size: 12px; - font-family: sans-serif; - background: white; - -moz-user-select: none; - text-decoration: none; - color: black; -} - -vbox { - width: inherit; - height: inherit; - -moz-box-align: center; - -moz-box-pack: center; -} - -#missingPluginPlaceholder { - list-style-image: url(chrome://mozapps/skin/plugins/pluginGeneric.png); -} - -#disabledPluginPlaceholder { - list-style-image: url(chrome://mozapps/skin/plugins/pluginDisabled.png); -} - -#blockedPluginPlaceholder { - list-style-image: url(chrome://mozapps/skin/plugins/pluginBlocked.png); -} - -#missingPluginPlaceholder, -#disabledPluginPlaceholder, -#blockedPluginPlaceholder { - display: block; - border: 0px; - width: 32px; - height: 32px; -} Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/themes/pinstripe/mozapps/plugins/pluginCrashed.png and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/pinstripe/mozapps/plugins/pluginCrashed.png differ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/themes/pinstripe/mozapps/plugins/pluginHelp-16.png and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/pinstripe/mozapps/plugins/pluginHelp-16.png differ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/themes/pinstripe/mozapps/plugins/pluginProblem.css firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/pinstripe/mozapps/plugins/pluginProblem.css --- firefox-3.6.3+nobinonly/mozilla/toolkit/themes/pinstripe/mozapps/plugins/pluginProblem.css 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/pinstripe/mozapps/plugins/pluginProblem.css 2010-04-16 17:32:49.000000000 +0100 @@ -0,0 +1,16 @@ +/* + * Note to themers: + * + * This CSS (and associated XBL/XUL) was added/changed for the landing of + * the new UI for crashed plugins in Gecko 1.9.2.3 (aka Firefox 3.6.3). To + * avoid the new UI from breaking when using existing themes, we've shipped + * the new CSS and images in /content instead of /skin. + * + * If you wish to theme this UI, you should add your CSS to this file and + * override the rules set by the /content CSS: + * chrome://mozapps/content/plugins/pluginProblemLorentz.css. + * + * The next major release (Gecko 1.9.3.x) will not have the /content + * pluginProblemLorentz.css; it's contents will be in this file, in /skin + * along with the images it references. + */ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/jar.mn firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/jar.mn --- firefox-3.6.3+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/jar.mn 2010-04-02 16:59:16.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/jar.mn 2010-04-16 17:32:49.000000000 +0100 @@ -25,7 +25,7 @@ skin/classic/mozapps/places/defaultFavicon.png (places/defaultFavicon.png) skin/classic/mozapps/places/tagContainerIcon.png (places/tagContainerIcon.png) #endif - skin/classic/mozapps/plugins/missingPlugin.css (plugins/missingPlugin.css) + skin/classic/mozapps/plugins/pluginProblem.css (plugins/pluginProblem.css) skin/classic/mozapps/plugins/pluginGeneric.png (plugins/pluginGeneric.png) skin/classic/mozapps/plugins/pluginDisabled.png (plugins/pluginDisabled.png) skin/classic/mozapps/plugins/pluginBlocked.png (plugins/pluginBlocked.png) @@ -67,7 +67,7 @@ skin/classic/aero/mozapps/places/defaultFavicon.png (places/defaultFavicon-aero.png) skin/classic/aero/mozapps/places/tagContainerIcon.png (places/tagContainerIcon-aero.png) #endif - skin/classic/aero/mozapps/plugins/missingPlugin.css (plugins/missingPlugin.css) + skin/classic/aero/mozapps/plugins/pluginProblem.css (plugins/pluginProblem.css) skin/classic/aero/mozapps/plugins/pluginGeneric.png (plugins/pluginGeneric-aero.png) skin/classic/aero/mozapps/plugins/pluginDisabled.png (plugins/pluginDisabled-aero.png) skin/classic/aero/mozapps/plugins/pluginBlocked.png (plugins/pluginBlocked-aero.png) diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/plugins/missingPlugin.css firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/plugins/missingPlugin.css --- firefox-3.6.3+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/plugins/missingPlugin.css 2010-04-02 16:59:16.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/plugins/missingPlugin.css 1970-01-01 01:00:00.000000000 +0100 @@ -1,68 +0,0 @@ -/* - * This file's style only applies to broken objects, not the rest - * of the page. - */ - -@namespace html url(http://www.w3.org/1999/xhtml); - -html|object:not([width]), html|object[width=""], -html|embed:not([width]), html|embed[width=""], -html|applet:not([width]), html|applet[width=""] { - width: 240px; -} - -html|object:not([height]), html|object[height=""], -html|embed:not([height]), html|embed[height=""], -html|applet:not([height]), html|applet[height=""] { - height: 200px; -} - -html|object > *|*, -html|embed > *|*, -html|applet > *|* { - width: inherit; - height: inherit; -} - -html|a, html|div { - display: inline-block; - /* overflow:hidden makes baseline vertical-alignment act like plugins */ - overflow: hidden; - vertical-align: inherit; - border: 1px outset; - padding: 5px; - font-size: 12px; - font-family: sans-serif; - background: white; - -moz-user-select: none; - text-decoration: none; - color: black; -} - -vbox { - width: inherit; - height: inherit; - -moz-box-align: center; - -moz-box-pack: center; -} - -#missingPluginPlaceholder { - list-style-image: url(chrome://mozapps/skin/plugins/pluginGeneric.png); -} - -#disabledPluginPlaceholder { - list-style-image: url(chrome://mozapps/skin/plugins/pluginDisabled.png); -} - -#blockedPluginPlaceholder { - list-style-image: url(chrome://mozapps/skin/plugins/pluginBlocked.png); -} - -#missingPluginPlaceholder, -#disabledPluginPlaceholder, -#blockedPluginPlaceholder { - display: block; - border: 0px; - width: 32px; - height: 32px; -} Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/plugins/pluginCrashed-aero.png and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/plugins/pluginCrashed-aero.png differ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/plugins/pluginCrashed.png and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/plugins/pluginCrashed.png differ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/plugins/pluginHelp-16-aero.png and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/plugins/pluginHelp-16-aero.png differ Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/plugins/pluginHelp-16.png and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/plugins/pluginHelp-16.png differ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/plugins/pluginProblem.css firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/plugins/pluginProblem.css --- firefox-3.6.3+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/plugins/pluginProblem.css 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/themes/winstripe/mozapps/plugins/pluginProblem.css 2010-04-16 17:32:49.000000000 +0100 @@ -0,0 +1,16 @@ +/* + * Note to themers: + * + * This CSS (and associated XBL/XUL) was added/changed for the landing of + * the new UI for crashed plugins in Gecko 1.9.2.3 (aka Firefox 3.6.3). To + * avoid the new UI from breaking when using existing themes, we've shipped + * the new CSS and images in /content instead of /skin. + * + * If you wish to theme this UI, you should add your CSS to this file and + * override the rules set by the /content CSS: + * chrome://mozapps/content/plugins/pluginProblemLorentz.css. + * + * The next major release (Gecko 1.9.3.x) will not have the /content + * pluginProblemLorentz.css; it's contents will be in this file, in /skin + * along with the images it references. + */ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/toolkit-makefiles.sh firefox-3.6.4+build1+nobinonly/mozilla/toolkit/toolkit-makefiles.sh --- firefox-3.6.3+nobinonly/mozilla/toolkit/toolkit-makefiles.sh 2010-04-02 16:59:16.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/toolkit-makefiles.sh 2010-04-16 17:32:49.000000000 +0100 @@ -48,6 +48,11 @@ " MAKEFILES_dom=" + ipc/Makefile + ipc/chromium/Makefile + ipc/glue/Makefile + ipc/ipdl/Makefile + ipc/testshell/Makefile dom/Makefile dom/public/coreEvents/Makefile dom/interfaces/base/Makefile @@ -80,6 +85,8 @@ dom/src/storage/Makefile dom/src/threads/Makefile dom/locales/Makefile + dom/plugins/Makefile + dom/ipc/Makefile " MAKEFILES_editor=" @@ -695,6 +702,7 @@ toolkit/crashreporter/client/Makefile toolkit/crashreporter/google-breakpad/src/client/Makefile toolkit/crashreporter/google-breakpad/src/client/linux/handler/Makefile + toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/Makefile toolkit/crashreporter/google-breakpad/src/client/mac/handler/Makefile toolkit/crashreporter/google-breakpad/src/client/solaris/handler/Makefile toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/Makefile diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/toolkit-tiers.mk firefox-3.6.4+build1+nobinonly/mozilla/toolkit/toolkit-tiers.mk --- firefox-3.6.3+nobinonly/mozilla/toolkit/toolkit-tiers.mk 2010-04-02 16:59:16.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/toolkit-tiers.mk 2010-04-16 17:32:49.000000000 +0100 @@ -81,6 +81,10 @@ # tier "gecko" - core components # +ifdef MOZ_IPC +tier_gecko_dirs += ipc +endif + tier_gecko_dirs += \ js/src/xpconnect \ js/ctypes \ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/xre/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/toolkit/xre/Makefile.in 2010-04-02 16:59:16.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/Makefile.in 2010-04-16 17:32:49.000000000 +0100 @@ -126,6 +126,7 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),windows) CPPSRCS += nsNativeAppSupportWin.cpp DEFINES += -DWIN32_LEAN_AND_MEAN -DUNICODE -D_UNICODE +EXPORTS = nsWindowsDllInterceptor.h else ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa) CMMSRCS = nsNativeAppSupportCocoa.mm @@ -155,6 +156,10 @@ CMMSRCS += MacApplicationDelegate.mm endif +ifdef MOZ_X11 +CPPSRCS += nsX11ErrorHandler.cpp +endif + SHARED_LIBRARY_LIBS += ../profile/src/$(LIB_PREFIX)profile_s.$(LIB_SUFFIX) ifdef MOZ_ENABLE_XREMOTE @@ -181,7 +186,9 @@ ifeq ($(OS_ARCH),Linux) SHARED_LIBRARY_LIBS += \ + $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/$(LIB_PREFIX)crash_generation_s.$(LIB_SUFFIX) \ $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/linux/handler/$(LIB_PREFIX)exception_handler_s.$(LIB_SUFFIX) \ + $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/$(LIB_PREFIX)minidump_writer_s.$(LIB_SUFFIX) \ $(DEPTH)/toolkit/crashreporter/google-breakpad/src/client/$(LIB_PREFIX)minidump_file_writer_s.$(LIB_SUFFIX) \ $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/$(LIB_PREFIX)breakpad_common_s.$(LIB_SUFFIX) \ $(DEPTH)/toolkit/crashreporter/google-breakpad/src/common/linux/$(LIB_PREFIX)breakpad_linux_common_s.$(LIB_SUFFIX) \ @@ -202,8 +209,15 @@ DIRS += test endif +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk include $(topsrcdir)/config/rules.mk +LOCAL_INCLUDES += \ + -I$(topsrcdir)/dom/ipc \ + -I$(topsrcdir)/toolkit/crashreporter \ + $(NULL) + ifdef BUILD_STATIC_LIBS export:: @$(PERL) -I$(MOZILLA_DIR)/config $(MOZILLA_DIR)/config/build-list.pl $(FINAL_LINK_COMP_NAMES) Apprunner diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsAppRunner.cpp firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsAppRunner.cpp --- firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsAppRunner.cpp 2010-04-02 16:59:16.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsAppRunner.cpp 2010-04-16 17:32:49.000000000 +0100 @@ -208,6 +208,10 @@ #include "nsIPrefService.h" #endif +#ifdef MOZ_IPC +#include "base/command_line.h" +#endif + #ifdef WINCE class WindowsMutex { public: @@ -297,6 +301,7 @@ #endif extern void InstallSignalHandlers(const char *ProgramName); +#include "nsX11ErrorHandler.h" int gArgc; char **gArgv; @@ -629,7 +634,7 @@ public nsIWinAppHelper, #endif #ifdef MOZ_CRASHREPORTER - public nsICrashReporter, + public nsICrashReporter_MOZILLA_1_9_2_BRANCH, #endif public nsIXULRuntime @@ -640,6 +645,7 @@ NS_DECL_NSIXULRUNTIME #ifdef MOZ_CRASHREPORTER NS_DECL_NSICRASHREPORTER + NS_DECL_NSICRASHREPORTER_MOZILLA_1_9_2_BRANCH #endif #ifdef XP_WIN NS_DECL_NSIWINAPPHELPER @@ -654,6 +660,7 @@ #endif #ifdef MOZ_CRASHREPORTER NS_INTERFACE_MAP_ENTRY(nsICrashReporter) + NS_INTERFACE_MAP_ENTRY(nsICrashReporter_MOZILLA_1_9_2_BRANCH) #endif NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIXULAppInfo, gAppData) NS_INTERFACE_MAP_END @@ -965,6 +972,19 @@ return NS_ERROR_NOT_IMPLEMENTED; #endif } + +NS_IMETHODIMP +nsXULAppInfo::GetSubmitReports(PRBool* aEnabled) +{ + return CrashReporter::GetSubmitReports(aEnabled); +} + +NS_IMETHODIMP +nsXULAppInfo::SetSubmitReports(PRBool aEnabled) +{ + return CrashReporter::SetSubmitReports(aEnabled); +} + #endif static const nsXULAppInfo kAppInfo; @@ -2619,20 +2639,6 @@ nsSplashScreen *splashScreen = nsnull; #endif -#ifdef XP_WIN - /* On Windows XPSP3 and Windows Vista if DEP is configured off-by-default - we still want DEP protection: enable it explicitly and programmatically. - - This function is not available on WinXPSP2 so we dynamically load it. - */ - - HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll"); - SetProcessDEPPolicyFunc _SetProcessDEPPolicy = - (SetProcessDEPPolicyFunc) GetProcAddress(kernel32, "SetProcessDEPPolicy"); - if (_SetProcessDEPPolicy) - _SetProcessDEPPolicy(PROCESS_DEP_ENABLE); -#endif - nsresult rv; ArgResult ar; NS_TIMELINE_MARK("enter main"); @@ -2642,31 +2648,7 @@ NS_BREAK(); #endif -#if defined (XP_WIN32) && !defined (WINCE) - // Suppress the "DLL Foo could not be found" dialog, such that if dependent - // libraries (such as GDI+) are not preset, we gracefully fail to load those - // XPCOM components, instead of being ungraceful. - UINT realMode = SetErrorMode(0); - realMode |= SEM_FAILCRITICALERRORS; - // If XRE_NO_WINDOWS_CRASH_DIALOG is set, suppress displaying the "This - // application has crashed" dialog box. This is mainly useful for - // automated testing environments, e.g. tinderbox, where there's no need - // for a dozen of the dialog boxes to litter the console - if (getenv("XRE_NO_WINDOWS_CRASH_DIALOG")) - realMode |= SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX; - - SetErrorMode(realMode); - -#ifdef DEBUG - // Disable small heap allocator to get heapwalk() giving us - // accurate heap numbers. Win2k non-debug does not use small heap allocator. - // Win2k debug seems to be still using it. - // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__set_sbh_threshold.asp - _set_sbh_threshold(0); -#endif -#endif - - InstallSignalHandlers(argv[0]); + SetupErrorHandling(argv[0]); #ifdef MOZ_ACCESSIBILITY_ATK // Reset GTK_MODULES, strip atk-bridge if exists @@ -2685,18 +2667,6 @@ PR_SetEnv("NO_AT_BRIDGE=1"); #endif -#ifndef WINCE - // Unbuffer stdout, needed for tinderbox tests. - setbuf(stdout, 0); -#endif - -#if defined(FREEBSD) - // Disable all SIGFPE's on FreeBSD, as it has non-IEEE-conformant fp - // trap behavior that trips up on floating-point tests performed by - // the JS engine. See bugzilla bug 9967 details. - fpsetmask(0); -#endif - gArgc = argc; gArgv = argv; @@ -3033,6 +3003,9 @@ MOZ_SPLASHSCREEN_UPDATE(20); + rv = XRE_InitCommandLine(gArgc, gArgv); + NS_ENSURE_SUCCESS(rv, 1); + { nsXREDirProvider dirProvider; rv = dirProvider.Initialize(gAppData->directory, gAppData->xreDirectory); @@ -3157,6 +3130,10 @@ gtk_widget_set_default_colormap(gdk_rgb_get_colormap()); #endif /* MOZ_WIDGET_GTK2 */ +#ifdef MOZ_X11 + // Do this after initializing GDK, or GDK will install its own handler. + InstallX11ErrorHandler(); +#endif // Call the code to install our handler #ifdef MOZ_JPROF @@ -3372,13 +3349,6 @@ nsCOMPtr cmdLine; -#if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK2) - nsRefPtr toolkit = GetGTKToolkit(); - if (toolkit && !desktopStartupID.IsEmpty()) { - toolkit->SetDesktopStartupID(desktopStartupID); - } -#endif - nsCOMPtr workingDir; rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(workingDir)); NS_ENSURE_SUCCESS(rv, 1); @@ -3406,6 +3376,13 @@ MOZ_SPLASHSCREEN_UPDATE(50); +#if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK2) + nsRefPtr toolkit = GetGTKToolkit(); + if (toolkit && !desktopStartupID.IsEmpty()) { + toolkit->SetDesktopStartupID(desktopStartupID); + } +#endif + // Extension Compatibility Checking and Startup if (gAppData->flags & NS_XRE_ENABLE_EXTENSION_MANAGER) { nsCOMPtr em(do_GetService("@mozilla.org/extensions/manager;1")); @@ -3501,13 +3478,14 @@ } MOZ_SPLASHSCREEN_UPDATE(90); - - NS_TIMELINE_ENTER("appStartup->Run"); - rv = appStartup->Run(); - NS_TIMELINE_LEAVE("appStartup->Run"); - if (NS_FAILED(rv)) { - NS_ERROR("failed to run appstartup"); - gLogConsoleErrors = PR_TRUE; + { + NS_TIMELINE_ENTER("appStartup->Run"); + rv = appStartup->Run(); + NS_TIMELINE_LEAVE("appStartup->Run"); + if (NS_FAILED(rv)) { + NS_ERROR("failed to run appstartup"); + gLogConsoleErrors = PR_TRUE; + } } // Check for an application initiated restart. This is one that @@ -3583,20 +3561,13 @@ } #endif -// XXXkt s/MOZ_TOOLKIT_GTK2/MOZ_WIDGET_GTK2/? -// but the hidden window has been destroyed so toolkit is NULL anyway. -#if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_TOOLKIT_GTK2) - nsGTKToolkit* toolkit = GetGTKToolkit(); - if (toolkit) { - nsCAutoString currentDesktopStartupID; - toolkit->GetDesktopStartupID(¤tDesktopStartupID); - if (!currentDesktopStartupID.IsEmpty()) { - nsCAutoString desktopStartupEnv; - desktopStartupEnv.AssignLiteral("DESKTOP_STARTUP_ID="); - desktopStartupEnv.Append(currentDesktopStartupID); - // Leak it with extreme prejudice! - PR_SetEnv(ToNewCString(desktopStartupEnv)); - } +#if defined(HAVE_DESKTOP_STARTUP_ID) && defined(MOZ_WIDGET_GTK2) + if (!desktopStartupID.IsEmpty()) { + nsCAutoString desktopStartupEnv; + desktopStartupEnv.AssignLiteral("DESKTOP_STARTUP_ID="); + desktopStartupEnv.Append(desktopStartupID); + // Leak it with extreme prejudice! + PR_SetEnv(ToNewCString(desktopStartupEnv)); } #endif @@ -3626,5 +3597,128 @@ CrashReporter::UnsetExceptionHandler(); #endif + XRE_DeinitCommandLine(); + return NS_FAILED(rv) ? 1 : 0; } + +nsresult +XRE_InitCommandLine(int aArgc, char* aArgv[]) +{ + nsresult rv = NS_OK; + +#if defined(MOZ_IPC) + +#if defined(OS_WIN) + CommandLine::Init(aArgc, aArgv); +#else + // these leak on error, but that's OK: we'll just exit() + char** canonArgs = new char*[aArgc]; + + // get the canonical version of the binary's path + nsCOMPtr binFile; + rv = XRE_GetBinaryPath(aArgv[0], getter_AddRefs(binFile)); + if (NS_FAILED(rv)) + return NS_ERROR_FAILURE; + + nsCAutoString canonBinPath; + rv = binFile->GetNativePath(canonBinPath); + if (NS_FAILED(rv)) + return NS_ERROR_FAILURE; + + canonArgs[0] = strdup(canonBinPath.get()); + + for (int i = 1; i < aArgc; ++i) { + if (aArgv[i]) { + canonArgs[i] = strdup(aArgv[i]); + } + } + + NS_ASSERTION(!CommandLine::IsInitialized(), "Bad news!"); + CommandLine::Init(aArgc, canonArgs); + + for (int i = 0; i < aArgc; ++i) + free(canonArgs[i]); + delete[] canonArgs; +#endif +#endif + return rv; +} + +nsresult +XRE_DeinitCommandLine() +{ + nsresult rv = NS_OK; + +#if defined(MOZ_IPC) + CommandLine::Terminate(); +#endif + + return rv; +} + +GeckoProcessType +XRE_GetProcessType() +{ +#ifdef MOZ_IPC + return mozilla::startup::sChildProcessType; +#else + return GeckoProcessType_Default; +#endif +} + +void +SetupErrorHandling(const char* progname) +{ +#ifdef XP_WIN + /* On Windows XPSP3 and Windows Vista if DEP is configured off-by-default + we still want DEP protection: enable it explicitly and programmatically. + + This function is not available on WinXPSP2 so we dynamically load it. + */ + + HMODULE kernel32 = GetModuleHandleW(L"kernel32.dll"); + SetProcessDEPPolicyFunc _SetProcessDEPPolicy = + (SetProcessDEPPolicyFunc) GetProcAddress(kernel32, "SetProcessDEPPolicy"); + if (_SetProcessDEPPolicy) + _SetProcessDEPPolicy(PROCESS_DEP_ENABLE); +#endif + +#if defined (XP_WIN32) && !defined (WINCE) + // Suppress the "DLL Foo could not be found" dialog, such that if dependent + // libraries (such as GDI+) are not preset, we gracefully fail to load those + // XPCOM components, instead of being ungraceful. + UINT realMode = SetErrorMode(0); + realMode |= SEM_FAILCRITICALERRORS; + // If XRE_NO_WINDOWS_CRASH_DIALOG is set, suppress displaying the "This + // application has crashed" dialog box. This is mainly useful for + // automated testing environments, e.g. tinderbox, where there's no need + // for a dozen of the dialog boxes to litter the console + if (getenv("XRE_NO_WINDOWS_CRASH_DIALOG")) + realMode |= SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX; + + SetErrorMode(realMode); + +#ifdef DEBUG + // Disable small heap allocator to get heapwalk() giving us + // accurate heap numbers. Win2k non-debug does not use small heap allocator. + // Win2k debug seems to be still using it. + // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__set_sbh_threshold.asp + _set_sbh_threshold(0); +#endif +#endif + + InstallSignalHandlers(progname); + +#ifndef WINCE + // Unbuffer stdout, needed for tinderbox tests. + setbuf(stdout, 0); +#endif + +#if defined(FREEBSD) + // Disable all SIGFPE's on FreeBSD, as it has non-IEEE-conformant fp + // trap behavior that trips up on floating-point tests performed by + // the JS engine. See bugzilla bug 9967 details. + fpsetmask(0); +#endif +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsAppRunner.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsAppRunner.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsAppRunner.h 2010-04-02 16:59:16.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsAppRunner.h 2010-04-16 17:32:49.000000000 +0100 @@ -182,4 +182,18 @@ NS_IF_ADDREF(ptr); } +#ifdef MOZ_IPC +namespace mozilla { +namespace startup { +extern GeckoProcessType sChildProcessType; +} +} +#endif + +/** + * Set up platform specific error handling such as suppressing DLL load dialog + * and the JIT debugger on Windows, and install unix signal handlers. + */ +void SetupErrorHandling(const char* progname); + #endif // nsAppRunner_h__ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsConsoleWriter.cpp firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsConsoleWriter.cpp --- firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsConsoleWriter.cpp 2010-04-02 16:59:16.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsConsoleWriter.cpp 2010-04-16 17:32:49.000000000 +0100 @@ -34,6 +34,10 @@ * * ***** END LICENSE BLOCK ***** */ +#ifdef NO_NSPR_10_SUPPORT +#undef NO_NSPR_10_SUPPORT +#endif + #include "nsAppRunner.h" #include "prio.h" diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsEmbedFunctions.cpp firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsEmbedFunctions.cpp --- firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsEmbedFunctions.cpp 2010-04-02 16:59:16.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsEmbedFunctions.cpp 2010-04-16 17:32:49.000000000 +0100 @@ -35,22 +35,82 @@ * * ***** END LICENSE BLOCK ***** */ +#ifdef MOZ_IPC +#include "base/basictypes.h" +#endif + #include "nsXULAppAPI.h" #include +#if defined(MOZ_WIDGET_GTK2) +#include +#endif + +#include "prenv.h" +#include "nsIAppShell.h" #include "nsIAppStartupNotifier.h" #include "nsIDirectoryService.h" #include "nsILocalFile.h" #include "nsIToolkitChromeRegistry.h" +#include "nsIToolkitProfile.h" + +#if defined(OS_LINUX) +# define XP_LINUX +#endif + +#ifdef XP_WIN +#include +#endif #include "nsAppDirectoryServiceDefs.h" #include "nsAppRunner.h" +#include "nsAutoRef.h" #include "nsDirectoryServiceDefs.h" +#include "nsExceptionHandler.h" #include "nsStaticComponents.h" #include "nsString.h" +#include "nsThreadUtils.h" +#include "nsWidgetsCID.h" +#include "nsXPFEComponentsCID.h" #include "nsXREDirProvider.h" -#include "nsIToolkitProfile.h" + +#ifdef MOZ_IPC +#include "nsX11ErrorHandler.h" +#include "base/at_exit.h" +#include "base/command_line.h" +#include "base/message_loop.h" +#include "base/process_util.h" +#include "chrome/common/child_process.h" + +#include "mozilla/ipc/GeckoChildProcessHost.h" +#include "mozilla/ipc/BrowserProcessSubThread.h" +#include "ScopedXREEmbed.h" + +#include "mozilla/plugins/PluginThreadChild.h" + +#include "mozilla/Monitor.h" + +#ifdef MOZ_IPDL_TESTS +#include "mozilla/_ipdltest/IPDLUnitTests.h" +#include "mozilla/_ipdltest/IPDLUnitTestThreadChild.h" + +using mozilla::_ipdltest::IPDLUnitTestThreadChild; +#endif // ifdef MOZ_IPDL_TESTS + +using mozilla::ipc::GeckoChildProcessHost; +using mozilla::ipc::BrowserProcessSubThread; +using mozilla::ipc::ScopedXREEmbed; + +using mozilla::plugins::PluginThreadChild; + +using mozilla::Monitor; +using mozilla::MonitorAutoEnter; + +using mozilla::startup::sChildProcessType; +#endif + +static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); void XRE_GetStaticComponents(nsStaticModuleInfo const **aStaticComponents, @@ -161,3 +221,280 @@ delete [] sCombined; delete gDirServiceProvider; } + +const char* +XRE_ChildProcessTypeToString(GeckoProcessType aProcessType) +{ + return (aProcessType < GeckoProcessType_End) ? + kGeckoProcessTypeString[aProcessType] : nsnull; +} + +GeckoProcessType +XRE_StringToChildProcessType(const char* aProcessTypeString) +{ + for (int i = 0; + i < (int) NS_ARRAY_LENGTH(kGeckoProcessTypeString); + ++i) { + if (!strcmp(kGeckoProcessTypeString[i], aProcessTypeString)) { + return static_cast(i); + } + } + return GeckoProcessType_Invalid; +} + +#ifdef MOZ_IPC +namespace mozilla { +namespace startup { +GeckoProcessType sChildProcessType = GeckoProcessType_Default; +} +} + +static MessageLoop* sIOMessageLoop; + +#if defined(MOZ_CRASHREPORTER) +// FIXME/bug 539522: this out-of-place function is stuck here because +// IPDL wants access to this crashreporter interface, and +// crashreporter is built in such a way to make that awkward +PRBool +XRE_TakeMinidumpForChild(PRUint32 aChildPid, nsILocalFile** aDump) +{ + return CrashReporter::TakeMinidumpForChild(aChildPid, aDump); +} + +PRBool +XRE_SetRemoteExceptionHandler(const char* aPipe/*= 0*/) +{ +#if defined(XP_WIN) + return CrashReporter::SetRemoteExceptionHandler(nsDependentCString(aPipe)); +#elif defined(OS_LINUX) + return CrashReporter::SetRemoteExceptionHandler(); +#else +# error "OOP crash reporter unsupported on this platform" +#endif +} +#endif // if defined(MOZ_CRASHREPORTER) + +nsresult +XRE_InitChildProcess(int aArgc, + char* aArgv[], + GeckoProcessType aProcess) +{ + NS_ENSURE_ARG_MIN(aArgc, 2); + NS_ENSURE_ARG_POINTER(aArgv); + NS_ENSURE_ARG_POINTER(aArgv[0]); + + SetupErrorHandling(aArgv[0]); + + sChildProcessType = aProcess; + +#if defined(MOZ_WIDGET_GTK2) + g_thread_init(NULL); +#endif + + if (PR_GetEnv("MOZ_DEBUG_CHILD_PROCESS")) { +#ifdef OS_POSIX + printf("\n\nCHILDCHILDCHILDCHILD\n debug me @%d\n\n", getpid()); + sleep(30); +#elif defined(OS_WIN) + printf("\n\nCHILDCHILDCHILDCHILD\n debug me @%d\n\n", _getpid()); + Sleep(30000); +#endif + } + + // child processes launched by GeckoChildProcessHost get this magic + // argument appended to their command lines + const char* const parentPIDString = aArgv[aArgc-1]; + NS_ABORT_IF_FALSE(parentPIDString, "NULL parent PID"); + --aArgc; + + char* end = 0; + base::ProcessId parentPID = strtol(parentPIDString, &end, 10); + NS_ABORT_IF_FALSE(!*end, "invalid parent PID"); + + base::ProcessHandle parentHandle; + bool ok = base::OpenProcessHandle(parentPID, &parentHandle); + NS_ABORT_IF_FALSE(ok, "can't open handle to parent"); + + base::AtExitManager exitManager; + + NS_LogInit(); + + int rv = XRE_InitCommandLine(aArgc, aArgv); + if (NS_FAILED(rv)) { + NS_LogTerm(); + return NS_ERROR_FAILURE; + } + + MessageLoopForIO mainMessageLoop; + + { + ChildThread* mainThread; + + switch (aProcess) { + case GeckoProcessType_Default: + NS_RUNTIMEABORT("This makes no sense"); + break; + + case GeckoProcessType_Plugin: + mainThread = new PluginThreadChild(parentHandle); + break; + + case GeckoProcessType_IPDLUnitTest: +#ifdef MOZ_IPDL_TESTS + mainThread = new IPDLUnitTestThreadChild(parentHandle); +#else + NS_RUNTIMEABORT("rebuild with --enable-ipdl-tests"); +#endif + break; + + default: + NS_RUNTIMEABORT("Unknown main thread class"); + } + + ChildProcess process(mainThread); + + // Do IPC event loop + sIOMessageLoop = MessageLoop::current(); + + sIOMessageLoop->Run(); + + sIOMessageLoop = nsnull; + } + + NS_LogTerm(); + return XRE_DeinitCommandLine(); +} + +MessageLoop* +XRE_GetIOMessageLoop() +{ + if (sChildProcessType == GeckoProcessType_Default) { + NS_ASSERTION(!sIOMessageLoop, "Shouldn't be set on parent process!"); + return BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO); + } + return sIOMessageLoop; +} + +namespace { + +class MainFunctionRunnable : public nsRunnable +{ +public: + NS_DECL_NSIRUNNABLE + + MainFunctionRunnable(MainFunction aFunction, + void* aData) + : mFunction(aFunction), + mData(aData) + { + NS_ASSERTION(aFunction, "Don't give me a null pointer!"); + } + +private: + MainFunction mFunction; + void* mData; +}; + +} /* anonymous namespace */ + +NS_IMETHODIMP +MainFunctionRunnable::Run() +{ + mFunction(mData); + return NS_OK; +} + +nsresult +XRE_InitParentProcess(int aArgc, + char* aArgv[], + MainFunction aMainFunction, + void* aMainFunctionData) +{ + NS_ENSURE_ARG_MIN(aArgc, 1); + NS_ENSURE_ARG_POINTER(aArgv); + NS_ENSURE_ARG_POINTER(aArgv[0]); + + int rv = XRE_InitCommandLine(aArgc, aArgv); + if (NS_FAILED(rv)) + return NS_ERROR_FAILURE; + + ScopedXREEmbed embed; + + { + embed.Start(); + + nsCOMPtr appShell(do_GetService(kAppShellCID)); + NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE); + + if (aMainFunction) { + nsCOMPtr runnable = + new MainFunctionRunnable(aMainFunction, aMainFunctionData); + NS_ENSURE_TRUE(runnable, NS_ERROR_OUT_OF_MEMORY); + + nsresult rv = NS_DispatchToCurrentThread(runnable); + NS_ENSURE_SUCCESS(rv, rv); + } + + // Do event loop + if (NS_FAILED(appShell->Run())) { + NS_WARNING("Failed to run appshell"); + return NS_ERROR_FAILURE; + } + } + + return XRE_DeinitCommandLine(); +} + +#ifdef MOZ_IPDL_TESTS +//----------------------------------------------------------------------------- +// IPDL unit test + +int +XRE_RunIPDLTest(int aArgc, char** aArgv) +{ + if (aArgc < 2) { + fprintf(stderr, "TEST-UNEXPECTED-FAIL | <---> | insufficient #args, need at least 2\n"); + return 1; + } + + void* data = reinterpret_cast(aArgv[aArgc-1]); + + nsresult rv = + XRE_InitParentProcess( + --aArgc, aArgv, mozilla::_ipdltest::IPDLUnitTestMain, data); + NS_ENSURE_SUCCESS(rv, 1); + + return 0; +} +#endif // ifdef MOZ_IPDL_TESTS + +nsresult +XRE_RunAppShell() +{ + nsCOMPtr appShell(do_GetService(kAppShellCID)); + NS_ENSURE_TRUE(appShell, NS_ERROR_FAILURE); + + return appShell->Run(); +} + +void +XRE_ShutdownChildProcess() +{ + NS_ABORT_IF_FALSE(MessageLoopForUI::current(), "Wrong thread!"); + + MessageLoop* ioLoop = XRE_GetIOMessageLoop(); + NS_ABORT_IF_FALSE(!!ioLoop, "Bad shutdown order"); + + ioLoop->PostTask(FROM_HERE, new MessageLoop::QuitTask()); +} + +#ifdef MOZ_X11 +void +XRE_InstallX11ErrorHandler() +{ + InstallX11ErrorHandler(); +} +#endif + +#endif // MOZ_IPC + diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsNativeAppSupportUnix.cpp firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsNativeAppSupportUnix.cpp --- firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsNativeAppSupportUnix.cpp 2010-04-02 16:59:32.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsNativeAppSupportUnix.cpp 2010-04-16 17:32:49.000000000 +0100 @@ -53,18 +53,37 @@ #include "nsICommandLineRunner.h" #include "nsIWindowMediator.h" #include "nsIDOMWindowInternal.h" +#include "nsPIDOMWindow.h" +#include "nsIDocShell.h" +#include "nsIBaseWindow.h" +#include "nsIWidget.h" +#include "nsIWritablePropertyBag2.h" +#include "nsIPrefService.h" #include #include #include #include +#ifdef MOZ_X11 +#include +#include +#endif + #ifdef MOZ_PLATFORM_MAEMO struct DBusMessage; /* libosso.h references internals of dbus */ #include #include #include + +// These come from (maemo sdk 5+) +#define MCE_SERVICE "com.nokia.mce" +#define MCE_REQUEST_IF "com.nokia.mce.request" +#define MCE_REQUEST_PATH "/com/nokia/mce/request" +#define MCE_SIGNAL_IF "com.nokia.mce.signal" +#define MCE_DEVICE_ORIENTATION_SIG "sig_device_orientation_ind" +#define MCE_MATCH_RULE "type='signal',interface='" MCE_SIGNAL_IF "',member='" MCE_DEVICE_ORIENTATION_SIG "'" #endif #define MIN_GTK_MAJOR_VERSION 2 @@ -210,6 +229,7 @@ public: NS_IMETHOD Start(PRBool* aRetVal); NS_IMETHOD Stop(PRBool *aResult); + NS_IMETHOD Enable(); private: #ifdef MOZ_PLATFORM_MAEMO @@ -224,17 +244,127 @@ }; #ifdef MOZ_PLATFORM_MAEMO +static nsresult +GetMostRecentWindow(const PRUnichar* aType, nsIDOMWindowInternal** aWindow) +{ + nsCOMPtr wm = do_GetService("@mozilla.org/appshell/window-mediator;1"); + if (wm) + return wm->GetMostRecentWindow(aType, aWindow); + return NS_ERROR_FAILURE; +} + +static GtkWidget* +WidgetForDOMWindow(nsISupports *aWindow) +{ + nsCOMPtr domWindow(do_QueryInterface(aWindow)); + if (!domWindow) + return NULL; + + nsCOMPtr baseWindow = do_QueryInterface(domWindow->GetDocShell()); + if (!baseWindow) + return NULL; + + nsCOMPtr widget; + baseWindow->GetMainWidget(getter_AddRefs(widget)); + if (!widget) + return NULL; + + return (GtkWidget*)(widget->GetNativeData(NS_NATIVE_SHELLWIDGET)); +} + +static void +OssoSetWindowOrientation(PRBool aPortrait) +{ + // If we locked the screen, ignore any orientation changes + PRBool lockScreen = PR_FALSE; + nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); + if (prefs) + prefs->GetBoolPref("toolkit.screen.lock", &lockScreen); + + if (lockScreen) + return; + + // Tell Hildon desktop to force our window to be either portrait or landscape, + // depending on the current rotation + // NOTE: We only update the most recent top-level window so this is only + // suitable for apps with only one window. + nsCOMPtr window; + GetMostRecentWindow(NS_LITERAL_STRING("").get(), getter_AddRefs(window)); + GtkWidget* widget = WidgetForDOMWindow(window); + if (widget && widget->window) { + GdkWindow *gdk = widget->window; + GdkAtom request = gdk_atom_intern("_HILDON_PORTRAIT_MODE_REQUEST", FALSE); + + if (aPortrait) { + gulong portrait_set = 1; + gdk_property_change(gdk, request, gdk_x11_xatom_to_atom(XA_CARDINAL), + 32, GDK_PROP_MODE_REPLACE, (const guchar *) &portrait_set, 1); + } + else { + gdk_property_delete(gdk, request); + } + } + + // Update the system info property + nsCOMPtr info = do_GetService("@mozilla.org/system-info;1"); + if (info) { + info->SetPropertyAsAString(NS_LITERAL_STRING("screen-orientation"), + aPortrait ? NS_LITERAL_STRING("portrait") : NS_LITERAL_STRING("landscape")); + } +} + +static PRBool OssoIsScreenOn(osso_context_t* ctx) +{ + osso_return_t rv; + osso_rpc_t ret; + PRBool result = PR_FALSE; + + rv = osso_rpc_run_system(ctx, MCE_SERVICE, MCE_REQUEST_PATH, MCE_REQUEST_IF, + "get_display_status", &ret, DBUS_TYPE_INVALID); + if (rv == OSSO_OK) { + if (strcmp(ret.value.s, "on") == 0) + result = PR_TRUE; + + osso_rpc_free_val(&ret); + } + return result; +} + +static void OssoRequestAccelerometer(osso_context_t *ctx, PRBool aEnabled) +{ + osso_return_t rv; + osso_rpc_t ret; + + rv = osso_rpc_run_system(ctx, + MCE_SERVICE, + MCE_REQUEST_PATH, MCE_REQUEST_IF, + aEnabled ? "req_accelerometer_enable" : "req_accelerometer_disable", + aEnabled ? &ret : NULL, + DBUS_TYPE_INVALID); + + // Orientation might changed while the accelerometer was off, so let's update + // the window's orientation + if (rv == OSSO_OK && aEnabled) { + OssoSetWindowOrientation(strcmp(ret.value.s, "portrait") == 0); + osso_rpc_free_val(&ret); + } +} static void OssoDisplayCallback(osso_display_state_t state, gpointer data) { nsCOMPtr os = do_GetService("@mozilla.org/observer-service;1"); if (!os) return; - - if (state == OSSO_DISPLAY_ON) + + osso_context_t* context = (osso_context_t*) data; + + if (state == OSSO_DISPLAY_ON) { os->NotifyObservers(nsnull, "system-display-on", nsnull); - else + OssoRequestAccelerometer(context, PR_TRUE); + } else { os->NotifyObservers(nsnull, "system-display-dimmed-or-off", nsnull); + OssoRequestAccelerometer(context, PR_FALSE); + } } static void OssoHardwareCallback(osso_hw_state_t *state, gpointer data) @@ -281,13 +411,11 @@ // The "top_application" method just wants us to focus the top-most window. if (!strcmp("top_application", method)) { - nsCOMPtr wm = do_GetService("@mozilla.org/appshell/window-mediator;1"); - nsCOMPtr window; - wm->GetMostRecentWindow(NS_LITERAL_STRING("").get(), getter_AddRefs(window)); - if (window) { + GetMostRecentWindow(NS_LITERAL_STRING("").get(), getter_AddRefs(window)); + if (window) window->Focus(); - } + return OSSO_OK; } @@ -340,6 +468,21 @@ return OSSO_OK; } +static DBusHandlerResult +OssoModeControlCallback(DBusConnection *con, DBusMessage *msg, gpointer data) +{ + if (dbus_message_is_signal(msg, MCE_SIGNAL_IF, MCE_DEVICE_ORIENTATION_SIG)) { + DBusMessageIter iter; + if (dbus_message_iter_init(msg, &iter)) { + const gchar *mode = NULL; + dbus_message_iter_get_basic(&iter, &mode); + + OssoSetWindowOrientation(strcmp(mode, "portrait") == 0); + } + } + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + #endif NS_IMETHODIMP @@ -378,7 +521,7 @@ system will happily kill your process. */ nsCAutoString applicationName; - if(gAppData->vendor) { + if (gAppData->vendor) { applicationName.Append(gAppData->vendor); applicationName.Append("."); } @@ -396,8 +539,13 @@ } osso_hw_set_event_cb(m_osso_context, nsnull, OssoHardwareCallback, &m_hw_state); - osso_hw_set_display_event_cb(m_osso_context, OssoDisplayCallback, nsnull); + osso_hw_set_display_event_cb(m_osso_context, OssoDisplayCallback, m_osso_context); osso_rpc_set_default_cb_f(m_osso_context, OssoDbusCallback, nsnull); + + // Setup an MCE callback to monitor orientation + DBusConnection *connnection = (DBusConnection*)osso_get_sys_dbus_connection(m_osso_context); + dbus_bus_add_match(connnection, MCE_MATCH_RULE, nsnull); + dbus_connection_add_filter(connnection, OssoModeControlCallback, nsnull, nsnull); #endif *aRetVal = PR_TRUE; @@ -477,6 +625,13 @@ #ifdef MOZ_PLATFORM_MAEMO if (m_osso_context) { + // Disable the accelerometer when closing + OssoRequestAccelerometer(m_osso_context, PR_FALSE); + + // Remove the MCE callback filter + DBusConnection *connnection = (DBusConnection*)osso_get_sys_dbus_connection(m_osso_context); + dbus_connection_remove_filter(connnection, OssoModeControlCallback, nsnull); + osso_hw_unset_event_cb(m_osso_context, nsnull); osso_rpc_unset_default_cb_f(m_osso_context, OssoDbusCallback, nsnull); osso_deinitialize(m_osso_context); @@ -486,6 +641,17 @@ return NS_OK; } +NS_IMETHODIMP +nsNativeAppSupportUnix::Enable() +{ +#ifdef MOZ_PLATFORM_MAEMO + // Enable the accelerometer for orientation support + if (OssoIsScreenOn(m_osso_context)) + OssoRequestAccelerometer(m_osso_context, PR_TRUE); +#endif + return NS_OK; +} + nsresult NS_CreateNativeAppSupport(nsINativeAppSupport **aResult) { diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsSigHandlers.cpp firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsSigHandlers.cpp --- firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsSigHandlers.cpp 2010-04-02 16:59:16.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsSigHandlers.cpp 2010-04-16 17:32:49.000000000 +0100 @@ -284,9 +284,11 @@ signal(SIGABRT, abnormal_exit_handler); #elif defined(CRAWL_STACK_ON_SIGSEGV) - signal(SIGSEGV, ah_crap_handler); - signal(SIGILL, ah_crap_handler); - signal(SIGABRT, ah_crap_handler); + if (!getenv("XRE_NO_WINDOWS_CRASH_DIALOG")) { + signal(SIGSEGV, ah_crap_handler); + signal(SIGILL, ah_crap_handler); + signal(SIGABRT, ah_crap_handler); + } #endif // CRAWL_STACK_ON_SIGSEGV /* Install a handler for floating point exceptions and disable them if they occur. */ diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsWindowsDllInterceptor.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsWindowsDllInterceptor.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsWindowsDllInterceptor.h 2010-04-02 16:59:16.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsWindowsDllInterceptor.h 2010-04-16 17:32:49.000000000 +0100 @@ -76,7 +76,7 @@ if (mModule) return; - mModule = LoadLibraryEx(modulename, NULL, 0); + mModule = LoadLibraryExA(modulename, NULL, 0); if (!mModule) { //printf("LoadLibraryEx for '%s' failed\n", modulename); return; diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsX11ErrorHandler.cpp firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsX11ErrorHandler.cpp --- firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsX11ErrorHandler.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsX11ErrorHandler.cpp 2010-04-16 17:32:49.000000000 +0100 @@ -0,0 +1,198 @@ +/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Karl Tomlinson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsX11ErrorHandler.h" + +#ifdef MOZ_IPC +#include "mozilla/plugins/PluginThreadChild.h" +using mozilla::plugins::PluginThreadChild; +#endif + +#include "prenv.h" +#include "nsXULAppAPI.h" +#include "nsExceptionHandler.h" +#include "nsDebug.h" + +#include +#ifdef MOZ_WIDGET_GTK2 +#include +#endif + +#define BUFSIZE 2048 // What Xlib uses with XGetErrorDatabaseText + +extern "C" { +static int +IgnoreError(Display *display, XErrorEvent *event) { + return 0; // This return value is ignored. +} + +static int +X11Error(Display *display, XErrorEvent *event) { + nsCAutoString notes; + char buffer[BUFSIZE]; + + // Get an indication of how long ago the request that caused the error was + // made. Do this before querying extensions etc below. + unsigned long age = NextRequest(display) - event->serial; + + // Ignore subsequent errors, which may get processed during the extension + // queries below for example. + XSetErrorHandler(IgnoreError); + + // Get a string to represent the request that caused the error. + nsCAutoString message; + if (event->request_code < 128) { + // Core protocol request + message.AppendInt(event->request_code); + } else { + // Extension request + int nExts; + char** extNames = XListExtensions(display, &nExts); + if (extNames) { + for (int i = 0; i < nExts; ++i) { + int major_opcode, first_event, first_error; + if (XQueryExtension(display, extNames[i], + &major_opcode, &first_event, &first_error) + && major_opcode == event->request_code) { + message.Append(extNames[i]); + message.Append('.'); + message.AppendInt(event->minor_code); + break; + } + } + + XFreeExtensionList(extNames); + } + } + + if (message.IsEmpty()) { + buffer[0] = '\0'; + } else { + XGetErrorDatabaseText(display, "XRequest", message.get(), "", + buffer, sizeof(buffer)); + } + + if (buffer[0]) { + notes.Append(buffer); + } else { + notes.Append("Request "); + notes.AppendInt(event->request_code); + notes.Append('.'); + notes.AppendInt(event->minor_code); + } + + notes.Append(": "); + + // Get a string to describe the error. + XGetErrorText(display, event->error_code, buffer, sizeof(buffer)); + notes.Append(buffer); + + // For requests where Xlib gets the reply synchronously, |age| will be 1 + // and the stack will include the function making the request. For + // asynchronous requests, the current stack will often be unrelated to the + // point of making the request, even if |age| is 1, but sometimes this may + // help us count back to the point of the request. With XSynchronize on, + // the stack will include the function making the request, even though + // |age| will be 2 for asynchronous requests because XSynchronize is + // implemented by an empty request from an XSync, which has not yet been + // processed. + if (age > 1) { + // XSynchronize returns the previous "after function". If a second + // XSynchronize call returns the same function after an enable call then + // synchronization must have already been enabled. + if (XSynchronize(display, True) == XSynchronize(display, False)) { + notes.Append("; sync"); + } else { + notes.Append("; "); + notes.AppendInt(PRUint32(age)); + notes.Append(" requests ago"); + } + } + +#ifdef MOZ_CRASHREPORTER + switch (XRE_GetProcessType()) { + case GeckoProcessType_Default: + CrashReporter::AppendAppNotesToCrashReport(notes); + break; +#ifdef MOZ_IPC + case GeckoProcessType_Plugin: + if (CrashReporter::GetEnabled()) { + // This is assuming that X operations are performed on the plugin + // thread. If plugins are using X on another thread, then we'll need to + // handle that differently. + PluginThreadChild::AppendNotesToCrashReport(notes); + } + break; +#endif + default: + ; // crash report notes not supported. + } +#endif + +#ifdef DEBUG + // The resource id is unlikely to be useful in a crash report without + // context of other ids, but add it to the debug console output. + notes.Append("; id=0x"); + notes.AppendInt(PRUint32(event->resourceid), 16); +#ifdef MOZ_WIDGET_GTK2 + // Actually, for requests where Xlib gets the reply synchronously, + // MOZ_X_SYNC=1 will not be necessary, but we don't have a table to tell us + // which requests get a synchronous reply. + if (!PR_GetEnv("MOZ_X_SYNC")) { + notes.Append("\nRe-running with MOZ_X_SYNC=1 in the environment may give a more helpful backtrace."); + } +#endif +#endif + + NS_RUNTIMEABORT(notes.get()); + return 0; // not reached +} +} + +void +InstallX11ErrorHandler() +{ + XSetErrorHandler(X11Error); + +#ifdef MOZ_WIDGET_GTK2 + NS_ASSERTION(GDK_DISPLAY(), "No GDK display"); + if (PR_GetEnv("MOZ_X_SYNC")) { + XSynchronize(GDK_DISPLAY(), True); + } +#endif +} diff -Nru firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsX11ErrorHandler.h firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsX11ErrorHandler.h --- firefox-3.6.3+nobinonly/mozilla/toolkit/xre/nsX11ErrorHandler.h 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/toolkit/xre/nsX11ErrorHandler.h 2010-04-16 17:32:49.000000000 +0100 @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Karl Tomlinson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifdef MOZ_X11 +void InstallX11ErrorHandler(); +#endif diff -Nru firefox-3.6.3+nobinonly/mozilla/tools/rb/fix_macosx_stack.py firefox-3.6.4+build1+nobinonly/mozilla/tools/rb/fix_macosx_stack.py --- firefox-3.6.3+nobinonly/mozilla/tools/rb/fix_macosx_stack.py 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/tools/rb/fix_macosx_stack.py 2010-04-16 17:32:50.000000000 +0100 @@ -0,0 +1,170 @@ +#!/usr/bin/python +# vim:sw=4:ts=4:et: +# ***** BEGIN LICENSE BLOCK ***** +# Version: MPL 1.1/GPL 2.0/LGPL 2.1 +# +# The contents of this file are subject to the Mozilla Public License Version +# 1.1 (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.mozilla.org/MPL/ +# +# Software distributed under the License is distributed on an "AS IS" basis, +# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License +# for the specific language governing rights and limitations under the +# License. +# +# The Original Code is fix-linux-stack.pl. +# +# The Initial Developer of the Original Code is L. David Baron. +# Portions created by the Initial Developer are Copyright (C) 2003 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# L. David Baron (original author) +# +# Alternatively, the contents of this file may be used under the terms of +# either the GNU General Public License Version 2 or later (the "GPL"), or +# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), +# in which case the provisions of the GPL or the LGPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of either the GPL or the LGPL, and not to allow others to +# use your version of this file under the terms of the MPL, indicate your +# decision by deleting the provisions above and replace them with the notice +# and other provisions required by the GPL or the LGPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the MPL, the GPL or the LGPL. +# +# ***** END LICENSE BLOCK ***** + +# This script uses atos to process the output of nsTraceRefcnt's Mac OS +# X stack walking code. This is useful for two things: +# (1) Getting line number information out of +# |nsTraceRefcntImpl::WalkTheStack|'s output in debug builds. +# (2) Getting function names out of |nsTraceRefcntImpl::WalkTheStack|'s +# output on all builds (where it mostly prints UNKNOWN because only +# a handful of symbols are exported from component libraries). +# +# Use the script by piping output containing stacks (such as raw stacks +# or make-tree.pl balance trees) through this script. + +import subprocess +import sys +import re +import os +import pty +import termios + +class unbufferedLineConverter: + """ + Wrap a child process that responds to each line of input with one line of + output. Uses pty to trick the child into providing unbuffered output. + """ + def __init__(self, command, args = []): + pid, fd = pty.fork() + if pid == 0: + # We're the child. Transfer control to command. + os.execvp(command, [command] + args) + else: + # Disable echoing. + attr = termios.tcgetattr(fd) + attr[3] = attr[3] & ~termios.ECHO + termios.tcsetattr(fd, termios.TCSANOW, attr) + # Set up a file()-like interface to the child process + self.r = os.fdopen(fd, "r", 1) + self.w = os.fdopen(os.dup(fd), "w", 1) + def convert(self, line): + self.w.write(line + "\n") + return self.r.readline().rstrip("\r\n") + @staticmethod + def test(): + assert unbufferedLineConverter("rev").convert("123") == "321" + assert unbufferedLineConverter("cut", ["-c3"]).convert("abcde") == "c" + print "Pass" + +def separate_debug_file_for(file): + return None + +address_adjustments = {} +def address_adjustment(file): + if not file in address_adjustments: + result = None + otool = subprocess.Popen(["otool", "-l", file], stdout=subprocess.PIPE) + while True: + line = otool.stdout.readline() + if line == "": + break + if line == " segname __TEXT\n": + line = otool.stdout.readline() + if not line.startswith(" vmaddr "): + raise StandardError("unexpected otool output") + result = int(line[10:], 16) + break + otool.stdout.close() + + if result is None: + raise StandardError("unexpected otool output") + + address_adjustments[file] = result + + return address_adjustments[file] + +atoses = {} +def addressToSymbol(file, address): + converter = None + if not file in atoses: + debug_file = separate_debug_file_for(file) or file + converter = unbufferedLineConverter('/usr/bin/atos', ['-o', debug_file]) + atoses[file] = converter + else: + converter = atoses[file] + return converter.convert("0x%X" % address) + +cxxfilt_proc = None +def cxxfilt(sym): + if cxxfilt_proc is None: + globals()["cxxfilt_proc"] = subprocess.Popen(['c++filt', + '--no-strip-underscores', + '--format', 'gnu-v3'], + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) + # strip underscores ourselves (works better than c++filt's + # --strip-underscores) + cxxfilt_proc.stdin.write(sym[1:] + "\n") + return cxxfilt_proc.stdout.readline().rstrip("\n") + +line_re = re.compile("^([ \|0-9-]*)(.*) ?\[([^ ]*) \+(0x[0-9A-F]{1,8})\](.*)$") +atos_sym_re = re.compile("^(\S+) \(in ([^)]+)\) \((.+)\)$") + +def fixSymbols(line): + result = line_re.match(line) + if result is not None: + # before allows preservation of balance trees + # after allows preservation of counts + (before, badsymbol, file, address, after) = result.groups() + address = int(address, 16) + + if os.path.exists(file) and os.path.isfile(file): + address += address_adjustment(file) + info = addressToSymbol(file, address) + + # atos output seems to have three forms: + # address + # address (in foo.dylib) + # symbol (in foo.dylib) (file:line) + symresult = atos_sym_re.match(info) + if symresult is not None: + # Print the first two forms as-is, and transform the third + (symbol, library, fileline) = symresult.groups() + symbol = cxxfilt(symbol) + info = "%s (%s, in %s)" % (symbol, fileline, library) + + return before + info + after + "\n" + else: + sys.stderr.write("Warning: File \"" + file + "\" does not exist.\n") + return line + else: + return line + +if __name__ == "__main__": + for line in sys.stdin: + sys.stdout.write(fixSymbols(line)) diff -Nru firefox-3.6.3+nobinonly/mozilla/uriloader/base/nsURILoader.cpp firefox-3.6.4+build1+nobinonly/mozilla/uriloader/base/nsURILoader.cpp --- firefox-3.6.3+nobinonly/mozilla/uriloader/base/nsURILoader.cpp 2010-04-02 16:59:17.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/uriloader/base/nsURILoader.cpp 2010-04-16 17:32:50.000000000 +0100 @@ -437,7 +437,8 @@ return NS_OK; } - // If we aren't allowed to try other listeners, we're done here. + // If we aren't allowed to try other listeners, just skip through to + // trying to convert the data. if (!(mFlags & nsIURILoader::DONT_RETARGET)) { // @@ -516,44 +517,39 @@ return rv; } } + } else { + LOG((" DONT_RETARGET flag set, so skipped over random other content " + "listeners and content handlers")); + } + + // + // Fifth step: If no listener prefers this type, see if any stream + // converters exist to transform this content type into + // some other. + // + // Don't do this if the server sent us a MIME type of "*/*" because they saw + // it in our Accept header and got confused. + // XXXbz have to be careful here; may end up in some sort of bizarre infinite + // decoding loop. + if (mContentType != anyType) { + rv = ConvertData(request, m_contentListener, mContentType, anyType); + if (NS_FAILED(rv)) { + m_targetStreamListener = nsnull; + } else if (m_targetStreamListener) { + // We found a converter for this MIME type. We'll just pump data into it + // and let the downstream nsDocumentOpenInfo handle things. + LOG((" Converter taking over now")); + return NS_OK; + } } - } else if (mFlags & nsIURILoader::DONT_RETARGET) { - // External handling was forced, but we must not retarget - // -> abort - LOG((" External handling forced, but not allowed to retarget -> aborting")); - return NS_ERROR_WONT_HANDLE_CONTENT; } NS_ASSERTION(!m_targetStreamListener, "If we found a listener, why are we not using it?"); - // - // Fifth step: If no listener prefers this type, see if any stream - // converters exist to transform this content type into - // some other. - // - - // We always want to do this, since even content being forced to - // be handled externally may need decoding (eg via the unknown - // content decoder). - // Don't do this if the server sent us a MIME type of "*/*" because they saw - // it in our Accept header and got confused. - // XXXbz have to be careful here; may end up in some sort of bizarre infinite - // decoding loop. - if (mContentType != anyType) { - rv = ConvertData(request, m_contentListener, mContentType, anyType); - if (NS_FAILED(rv)) { - m_targetStreamListener = nsnull; - } else if (m_targetStreamListener) { - // We found a converter for this MIME type. We'll just pump data into it - // and let the downstream nsDocumentOpenInfo handle things. - LOG((" Converter taking over now")); - return NS_OK; - } - } - if (mFlags & nsIURILoader::DONT_RETARGET) { - LOG((" Listener not interested and no stream converter exists, and retargeting disallowed -> aborting")); + LOG((" External handling forced or (listener not interested and no " + "stream converter exists), and retargeting disallowed -> aborting")); return NS_ERROR_WONT_HANDLE_CONTENT; } diff -Nru firefox-3.6.3+nobinonly/mozilla/uriloader/exthandler/mac/nsOSHelperAppService.mm firefox-3.6.4+build1+nobinonly/mozilla/uriloader/exthandler/mac/nsOSHelperAppService.mm --- firefox-3.6.3+nobinonly/mozilla/uriloader/exthandler/mac/nsOSHelperAppService.mm 2010-04-02 16:59:17.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/uriloader/exthandler/mac/nsOSHelperAppService.mm 2010-04-16 17:32:50.000000000 +0100 @@ -257,6 +257,8 @@ return nsnull; NS_ADDREF(mimeInfoMac); + NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init]; + OSStatus err; PRBool haveAppForType = PR_FALSE; PRBool haveAppForExt = PR_FALSE; @@ -349,6 +351,7 @@ nsCOMPtr app(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID)); if (!app) { NS_RELEASE(mimeInfoMac); + [localPool release]; return nsnull; } @@ -413,6 +416,7 @@ PR_LOG(mLog, PR_LOG_DEBUG, ("OS gave us: type '%s' found '%i'\n", mimeType.get(), *aFound)); + [localPool release]; return mimeInfoMac; NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL; diff -Nru firefox-3.6.3+nobinonly/mozilla/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp firefox-3.6.4+build1+nobinonly/mozilla/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp --- firefox-3.6.3+nobinonly/mozilla/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp 2010-04-02 16:59:32.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/uriloader/exthandler/unix/nsMIMEInfoUnix.cpp 2010-04-16 17:32:50.000000000 +0100 @@ -103,7 +103,7 @@ nsCAutoString nativePath; aFile->GetNativePath(nativePath); -#ifdef MOZ_PLATFORM_HILDON +#if (MOZ_PLATFORM_MAEMO == 5) && defined (MOZ_ENABLE_GNOMEVFS) if(NS_SUCCEEDED(LaunchDefaultWithDBus(PromiseFlatCString(nativePath).get()))) return NS_OK; #endif diff -Nru firefox-3.6.3+nobinonly/mozilla/view/src/nsScrollPortView.cpp firefox-3.6.4+build1+nobinonly/mozilla/view/src/nsScrollPortView.cpp --- firefox-3.6.3+nobinonly/mozilla/view/src/nsScrollPortView.cpp 2010-04-02 16:59:17.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/view/src/nsScrollPortView.cpp 2010-04-16 17:32:50.000000000 +0100 @@ -577,91 +577,6 @@ aRepaintRegion->Or(*aRepaintRegion, repaint); } -/** - * An nsTArray comparator that lets us sort nsIntRects by their right edge. - */ -class RightEdgeComparator { -public: - /** @return True if the elements are equals; false otherwise. */ - PRBool Equals(const nsIntRect& aA, const nsIntRect& aB) const - { - return aA.XMost() == aB.XMost(); - } - /** @return True if (a < b); false otherwise. */ - PRBool LessThan(const nsIntRect& aA, const nsIntRect& aB) const - { - return aA.XMost() < aB.XMost(); - } -}; - -// If aPixDelta has a negative component, flip aRect across the -// axis in that direction. We do this so we can assume all scrolling is -// down and to the right to simplify SortBlitRectsForCopy -static nsIntRect -FlipRect(const nsIntRect& aRect, nsIntPoint aPixDelta) -{ - nsIntRect r = aRect; - if (aPixDelta.x < 0) { - r.x = -r.XMost(); - } - if (aPixDelta.y < 0) { - r.y = -r.YMost(); - } - return r; -} - -// Sort aRects so that moving rectangle aRects[i] - aPixDelta to aRects[i] -// will not cause the rectangle to overlap any rectangles that haven't -// moved yet. -// See http://weblogs.mozillazine.org/roc/archives/2009/08/homework_answer.html -static void -SortBlitRectsForCopy(nsIntPoint aPixDelta, nsTArray* aRects) -{ - nsTArray rects; - - for (PRUint32 i = 0; i < aRects->Length(); ++i) { - nsIntRect* r = &aRects->ElementAt(i); - nsIntRect rect = - FlipRect(nsIntRect(r->x, r->y, r->width, r->height), aPixDelta); - rects.AppendElement(rect); - } - rects.Sort(RightEdgeComparator()); - - aRects->Clear(); - // This could probably be improved a bit for some worst-case scenarios. - // But in common cases this should be very fast, and we shouldn't - // make it more complex unless we really need to. - while (!rects.IsEmpty()) { - PRInt32 i = rects.Length() - 1; - PRBool overlappedBelow; - do { - overlappedBelow = PR_FALSE; - const nsIntRect& rectI = rects[i]; - // see if any rectangle < i overlaps rectI horizontally and is below - // rectI - for (PRInt32 j = i - 1; j >= 0; --j) { - if (rects[j].XMost() <= rectI.x) { - // No rectangle with index <= j can overlap rectI horizontally - break; - } - // Rectangle j overlaps rectI horizontally. - if (rects[j].y >= rectI.y) { - // Rectangle j is below rectangle i. This is the rightmost such - // rectangle, so set i to this rectangle and continue. - i = j; - overlappedBelow = PR_TRUE; - break; - } - } - } while (overlappedBelow); - - // Rectangle i has no rectangles to the right or below. - // Flip it back before saving the result. - aRects->AppendElement(FlipRect(rects[i], aPixDelta)); - rects.RemoveElementAt(i); - } -} - void nsScrollPortView::Scroll(nsView *aScrolledView, nsPoint aTwipsDelta, nsIntPoint aPixDelta, PRInt32 aP2A, const nsTArray& aConfigurations) @@ -708,7 +623,6 @@ nsRegion blitRectsRegion; ConvertBlitRegionToPixelRects(blitRegion, aP2A, &blitRects, &repaintRegion, &blitRectsRegion); - SortBlitRectsForCopy(aPixDelta, &blitRects); nearestWidget->Scroll(aPixDelta, blitRects, aConfigurations); AdjustChildWidgets(aScrolledView, nearestWidgetOffset, aP2A, PR_TRUE); diff -Nru firefox-3.6.3+nobinonly/mozilla/view/src/nsViewManager.cpp firefox-3.6.4+build1+nobinonly/mozilla/view/src/nsViewManager.cpp --- firefox-3.6.3+nobinonly/mozilla/view/src/nsViewManager.cpp 2010-04-02 16:59:18.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/view/src/nsViewManager.cpp 2010-04-16 17:32:50.000000000 +0100 @@ -1091,7 +1091,7 @@ default: { if ((NS_IS_MOUSE_EVENT(aEvent) && - // Ignore moves that we synthesize. + // Ignore mouse events that we synthesize. static_cast(aEvent)->reason == nsMouseEvent::eReal && // Ignore mouse exit and enter (we'll get moves if the user diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/public/nsIWidget.h firefox-3.6.4+build1+nobinonly/mozilla/widget/public/nsIWidget.h --- firefox-3.6.3+nobinonly/mozilla/widget/public/nsIWidget.h 2010-04-02 16:59:18.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/public/nsIWidget.h 2010-04-16 17:32:50.000000000 +0100 @@ -696,11 +696,8 @@ * operation fails to blit because part of the window is unavailable * (e.g. partially offscreen). * - * The caller guarantees that the rectangles in aDestRects are ordered - * so that copying from aDestRects[i] - aDelta to aDestRects[i] does - * not alter anything in aDestRects[j] - aDelta for j > i. That is, - * it's safe to just copy the rectangles in the order given in - * aDestRects. + * The caller guarantees that the rectangles in aDestRects are + * non-intersecting. * * @param aDelta amount to scroll (device pixels) * @param aDestRects rectangles to copy into diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/cocoa/nsChildView.mm firefox-3.6.4+build1+nobinonly/mozilla/widget/src/cocoa/nsChildView.mm --- firefox-3.6.3+nobinonly/mozilla/widget/src/cocoa/nsChildView.mm 2010-04-02 16:59:18.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/cocoa/nsChildView.mm 2010-04-16 17:32:51.000000000 +0100 @@ -1734,9 +1734,11 @@ if (mVisible && !aDestRects.IsEmpty()) { viewWasDirty = [mView needsDisplay]; - for (PRUint32 i = 0; i < aDestRects.Length(); ++i) { + nsIntRect destRect; // keep the last rect + for (BlitRectIter iter(aDelta, aDestRects); !iter.IsDone(); ++iter) { + destRect = iter.Rect(); NSRect rect; - GeckoRectToNSRect(aDestRects[i] - aDelta, rect); + GeckoRectToNSRect(destRect - aDelta, rect); NSSize scrollVector = {aDelta.x, aDelta.y}; [mView scrollRect:rect by:scrollVector]; } @@ -1748,7 +1750,7 @@ // So let's invalidate one pixel. We'll pick a pixel on the trailing edge // of the last destination rectangle, since in most situations that's going // to be invalidated anyway. - nsIntRect lastRect = aDestRects[aDestRects.Length() - 1] + aDelta; + nsIntRect lastRect = destRect + aDelta; nsIntPoint pointToInvalidate( PickValueForSign(aDelta.x, lastRect.XMost(), lastRect.x, lastRect.x - 1), PickValueForSign(aDelta.y, lastRect.YMost(), lastRect.y, lastRect.y - 1)); diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/cocoa/nsMenuX.mm firefox-3.6.4+build1+nobinonly/mozilla/widget/src/cocoa/nsMenuX.mm --- firefox-3.6.3+nobinonly/mozilla/widget/src/cocoa/nsMenuX.mm 2010-04-02 16:59:18.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/cocoa/nsMenuX.mm 2010-04-16 17:32:51.000000000 +0100 @@ -914,13 +914,11 @@ // don't request a menu item that doesn't exist or we crash // this might happen just due to some random quirks in the event system - PRUint32 itemCount; - targetMenu->GetVisibleItemCount(itemCount); - if (aPos >= itemCount) + nsMenuObjectX* target = targetMenu->GetVisibleItemAt((PRUint32)aPos); + if (!target) return eventNotHandledErr; // Send DOM event if we're over a menu item - nsMenuObjectX* target = targetMenu->GetVisibleItemAt((PRUint32)aPos); if (target->MenuObjectType() == eMenuItemObjectType) { nsMenuItemX* targetMenuItem = static_cast(target); PRBool handlerCalledPreventDefault; // but we don't actually care diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/gtk2/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/widget/src/gtk2/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/widget/src/gtk2/Makefile.in 2010-04-02 16:59:18.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/gtk2/Makefile.in 2010-04-16 17:32:51.000000000 +0100 @@ -132,13 +132,14 @@ EXTRA_DSO_LDOPTS += \ $(MOZ_COMPONENT_LIBS) \ -lgkgfx \ + -lthebes \ + $(MOZ_CAIRO_LIBS) \ $(MOZ_STARTUP_NOTIFICATION_LIBS) \ $(XLDFLAGS) \ $(XLIBS) \ $(XEXT_LIBS) \ $(XCOMPOSITE_LIBS) \ $(MOZ_GTK2_LIBS) \ - -lthebes \ $(QCMS_LIBS) \ $(NULL) @@ -167,7 +168,7 @@ include $(topsrcdir)/config/rules.mk CFLAGS += $(MOZ_GTK2_CFLAGS) $(MOZ_STARTUP_NOTIFICATION_CFLAGS) -CXXFLAGS += $(MOZ_GTK2_CFLAGS) $(MOZ_STARTUP_NOTIFICATION_CFLAGS) +CXXFLAGS += $(MOZ_CAIRO_CFLAGS) $(MOZ_GTK2_CFLAGS) $(MOZ_STARTUP_NOTIFICATION_CFLAGS) ifdef MOZ_PLATFORM_MAEMO ifdef MOZ_ENABLE_GCONF diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/gtk2/nsDragService.cpp firefox-3.6.4+build1+nobinonly/mozilla/widget/src/gtk2/nsDragService.cpp --- firefox-3.6.3+nobinonly/mozilla/widget/src/gtk2/nsDragService.cpp 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/gtk2/nsDragService.cpp 2010-04-16 17:32:51.000000000 +0100 @@ -225,7 +225,7 @@ memset(&event, 0, sizeof(GdkEvent)); event.type = GDK_BUTTON_PRESS; event.button.window = mHiddenWidget->window; - event.button.time = nsWindow::mLastButtonPressTime; + event.button.time = nsWindow::sLastButtonPressTime; // start our drag. GdkDragContext *context = gtk_drag_begin(mHiddenWidget, diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/gtk2/nsWindow.cpp firefox-3.6.4+build1+nobinonly/mozilla/widget/src/gtk2/nsWindow.cpp --- firefox-3.6.3+nobinonly/mozilla/widget/src/gtk2/nsWindow.cpp 2010-04-02 16:59:32.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/gtk2/nsWindow.cpp 2010-04-16 17:32:51.000000000 +0100 @@ -39,6 +39,7 @@ * ***** END LICENSE BLOCK ***** */ #ifdef MOZ_PLATFORM_MAEMO +// needed to include hildon parts in gtk.h #define MAEMO_CHANGES #endif @@ -63,7 +64,14 @@ #include #ifdef MOZ_X11 #include +#include + +#ifdef AIX +#include +#else #include +#endif + #include "gtk2xtbin.h" #endif /* MOZ_X11 */ #include @@ -84,6 +92,7 @@ #include "nsIObserverService.h" #include "nsIdleService.h" +#include "nsIPropertyBag2.h" #ifdef ACCESSIBILITY #include "nsIAccessibilityService.h" @@ -114,6 +123,9 @@ #include "nsIInterfaceRequestorUtils.h" #include "nsAutoPtr.h" +extern "C" { +#include "pixman.h" +} #include "gfxPlatformGtk.h" #include "gfxContext.h" #include "gfxImageSurface.h" @@ -274,10 +286,10 @@ // This is the time of the last button press event. The drag service // uses it as the time to start drags. -guint32 nsWindow::mLastButtonPressTime = 0; +guint32 nsWindow::sLastButtonPressTime = 0; // Time of the last button release event. We use it to detect when the // drag ended before we could properly setup drag and drop. -guint32 nsWindow::mLastButtonReleaseTime = 0; +guint32 nsWindow::sLastButtonReleaseTime = 0; static NS_DEFINE_IID(kCDragServiceCID, NS_DRAGSERVICE_CID); @@ -364,9 +376,13 @@ static GtkWidget *gInvisibleContainer = NULL; +// Sometimes this actually also includes the state of the modifier keys, but +// only the button state bits are used. +static guint gButtonState; + // Some gobject functions expect functions for gpointer arguments. // gpointer is void* but C++ doesn't like casting functions to void*. -template gpointer +template static inline gpointer FuncToGpointer(T aFunction) { return reinterpret_cast @@ -375,6 +391,25 @@ (reinterpret_cast(aFunction))); } +// nsAutoRef uses nsSimpleRef<> to know how to automatically +// destroy regions. +template <> +class nsSimpleRef : public pixman_region32 { +protected: + typedef pixman_region32 RawRef; + + nsSimpleRef() { data = nsnull; } + nsSimpleRef(const RawRef &aRawRef) : pixman_region32(aRawRef) { } + + static void Release(pixman_region32& region) { + pixman_region32_fini(®ion); + } + // Whether this needs to be released: + PRBool HaveResource() const { return data != nsnull; } + + pixman_region32& get() { return *this; } +}; + nsWindow::nsWindow() { mIsTopLevel = PR_FALSE; @@ -976,7 +1011,7 @@ NS_IMETHODIMP nsWindow::Show(PRBool aState) { -#ifndef MOZ_PLATFORM_HILDON +#ifndef MOZ_PLATFORM_MAEMO // XXX Bug 534981: work around to fix a large initial paint delay in Fennec if (aState == mIsShown) { return NS_OK; @@ -1738,9 +1773,134 @@ LOGDRAW(("Update [%p] %p\n", this, mGdkWindow)); gdk_window_process_updates(mGdkWindow, FALSE); + // Send the updates to the server. + gdk_display_flush(gdk_drawable_get_display(GDK_DRAWABLE(mGdkWindow))); return NS_OK; } +static pixman_box32 +ToPixmanBox(const nsIntRect& aRect) +{ + pixman_box32_t result; + result.x1 = aRect.x; + result.y1 = aRect.y; + result.x2 = aRect.XMost(); + result.y2 = aRect.YMost(); + return result; +} + +static nsIntRect +ToIntRect(const pixman_box32& aBox) +{ + nsIntRect result; + result.x = aBox.x1; + result.y = aBox.y1; + result.width = aBox.x2 - aBox.x1; + result.height = aBox.y2 - aBox.y1; + return result; +} + +static void +InitRegion(pixman_region32* aRegion, + const nsTArray& aRects) +{ + nsAutoTArray rects; + rects.SetCapacity(aRects.Length()); + for (PRUint32 i = 0; i < aRects.Length (); ++i) { + if (!aRects[i].IsEmpty()) { + rects.AppendElement(ToPixmanBox(aRects[i])); + } + } + + pixman_region32_init_rects(aRegion, + rects.Elements(), rects.Length()); +} + +static void +GetIntRects(pixman_region32& aRegion, nsTArray* aRects) +{ + int nRects; + pixman_box32* boxes = pixman_region32_rectangles(&aRegion, &nRects); + aRects->SetCapacity(aRects->Length() + nRects); + for (int i = 0; i < nRects; ++i) { + aRects->AppendElement(ToIntRect(boxes[i])); + } +} + +/** + * ScrollItemIter uses ScrollRectIterBase to order blit rectangles and + * rectangular child clip regions in a way such that moving the items in this + * order will avoid conflicts of blit rectangles. Conflicts with child + * windows are also avoided in situations with simple child window + * arrangements. + * + * The blit rectangles must not intersect with any other rectangles (of either + * blits or children). Note that child clip regions are not guaranteed to be + * exclusive of other child clip regions, so ScrollItemIter may not + * necessarily provide an optimal order (if a child rectangle intersects + * another child rectangle). + */ +class ScrollItemIter : public ScrollRectIterBase { +public: + // Each aChildRects[i] corresponds to + ScrollItemIter(const nsIntPoint& aDelta, + const nsTArray& aBlitRects, + const nsTArrayaChildConfs, + const nsTArray& aChildSubRects); + + PRBool IsBlit() const { return !Configuration(); }; + +private: + struct ScrollItem : public ScrollRect { + ScrollItem(const nsIntRect& aIntRect) : ScrollRect(aIntRect) {} + + const nsIWidget::Configuration *mChildConf; + }; + +public: + const nsIWidget::Configuration* Configuration() const + { + return static_cast(Rect()).mChildConf; + } + +private: + // Copying is not supported. + ScrollItemIter(const ScrollItemIter&); + void operator=(const ScrollItemIter&); + + nsTArray mRects; +}; + +ScrollItemIter::ScrollItemIter(const nsIntPoint& aDelta, + const nsTArray& aBlitRects, + const nsTArrayaChildConfs, + const nsTArray& aChildSubRects) + : mRects(aBlitRects.Length() + aChildConfs.Length()) +{ + for (PRUint32 i = 0; i < aBlitRects.Length(); ++i) { + if (ScrollItem* item = mRects.AppendElement(aBlitRects[i])) { + item->mChildConf = nsnull; + } + } + + PRUint32 numChildren = + NS_MIN(aChildConfs.Length(), aChildSubRects.Length()); + for (PRUint32 i = 0; i < numChildren; ++i) { + if (ScrollItem* item = mRects.AppendElement(aChildSubRects[i])) { + item->mChildConf = aChildConfs[i]; + } + } + + // Link items into a chain. + ScrollRect *next = nsnull; + for (PRUint32 i = mRects.Length(); i--; ) { + mRects[i].mNext = next; + next = &mRects[i]; + } + + BaseInit(aDelta, next); +} + void nsWindow::Scroll(const nsIntPoint& aDelta, const nsTArray& aDestRects, @@ -1751,87 +1911,158 @@ return; } - nsAutoTArray windowsToShow; - // Hide any widgets that are becoming invisible or that are moving. - // Moving widgets are hidden for the duration of the scroll so that - // the XCopyArea treats their drawn pixels as part of the window - // that should be scrolled. This works well when the widgets are - // moving because they're being scrolled, which is normally true. + // Empty Xlib's request buffer to reduce the likelihood of it getting + // emptied mid way through the scroll, in the hope that the server gets + // all the requests at once. + gdk_display_flush(gdk_drawable_get_display(GDK_DRAWABLE(mGdkWindow))); + + // Collect the destination positions of moving child windows where they + // will eventually obscure their parent. + nsTArray movingChildren; + nsTArray movingChildSubRects; + for (PRUint32 i = 0; i < aConfigurations.Length(); ++i) { - const Configuration& configuration = aConfigurations[i]; - nsWindow* w = static_cast(configuration.mChild); + const Configuration* conf = &aConfigurations[i]; + nsWindow* w = static_cast(conf->mChild); NS_ASSERTION(w->GetParent() == this, "Configured widget is not a child"); - if (w->mIsShown && - (configuration.mClipRegion.IsEmpty() || - configuration.mBounds != w->mBounds)) { - w->NativeShow(PR_FALSE); - windowsToShow.AppendElement(w); - } - } - // The parts of source regions not covered by their destination get marked - // invalid (by GDK). This is necessary (until covered by another blit) - // because GDK translates any pending expose events to the destination, - // and so doesn't know whether an expose event might have been due on the - // source. + if (!w->mIsShown) + continue; + + // Set the clip region of all visible windows to the intersection of + // the current and new region. This reduces the conflict area with + // other objects (including stationary objects). + w->SetWindowClipRegion(conf->mClipRegion, PR_TRUE); + + if (conf->mBounds.TopLeft() == w->mBounds.TopLeft()) + continue; // window is not moving + + nsAutoTArray rects; // of clip region intersection + w->GetWindowClipRegion(&rects); + + // ScrollItemIter is designed only for rectangular scroll items. + // + // It is not suitable to use the bounding rectangle of complex child + // clip regions because that rectangle may intersect with blit + // rectangles and ScrollItemIter would then not necessarily provide + // the correct order for any such blit rectangles. If child windows + // are not moved in the optimal order there will be some flicker + // during the scroll, which will be corrected through invalidations, + // but, if blit rectangles were moved in the wrong order, then some + // parts would get moved twice, which would not be corrected through + // invalidations. + // + // Choosing a sub-rectangle for the scroll item would make some parts + // of the child window scroll nicely but not others. This can + // actually look worse than the whole window being moved out of order, + // so moving of child windows with complex clip regions is simply + // delayed until after blitting. + if (rects.Length() != 1) + continue; // no moving content or non-rectangular clip-region + + movingChildren.AppendElement(conf); + + // Destination position wrt mGdkWindow top left. + nsIntRect subRect = rects[0] + conf->mBounds.TopLeft(); + movingChildSubRects.AppendElement(subRect); + } + + nsAutoRef blitRegion; + InitRegion(&blitRegion, aDestRects); + + // Remove some parts of the moving parent region that will be covered by + // moving child widgets. These parts won't need drawing anyway, and it + // breaks up the blit rectangles so that we have a chance of moving them + // without conflicts with child rectangles. // - // However, GDK 2.18 does not subtract the invalid regions at the - // destinations from the update_area, so the seams between different moves - // remain invalid. GDK 2.18 also delays and queues move operations. If - // gdk_window_process_updates is called before the moves are flushed, GDK - // 2.18 removes the invalid seams from the move regions, so the seams are - // left with their old content until they get redrawn. Therefore, the - // subtraction of destination invalid regions is performed here. - GdkRegion* updateArea = gdk_window_get_update_area(mGdkWindow); - if (!updateArea) { - updateArea = gdk_region_new(); // Aborts on OOM. - } - - // gdk_window_move_region, up to GDK 2.16, has a ghastly bug where it - // doesn't restrict blitting to the given region, and blits its full - // bounding box. So we have to work around that by blitting one rectangle - // at a time. - for (PRUint32 i = 0; i < aDestRects.Length(); ++i) { - const nsIntRect& r = aDestRects[i]; - GdkRectangle gdkSource = - { r.x - aDelta.x, r.y - aDelta.y, r.width, r.height }; - GdkRegion* rectRegion = gdk_region_rectangle(&gdkSource); - gdk_window_move_region(GDK_WINDOW(mGdkWindow), rectRegion, - aDelta.x, aDelta.y); - - // The part of the old invalid region that is moving. - GdkRegion* updateChanges = gdk_region_copy(rectRegion); - gdk_region_intersect(updateChanges, updateArea); - gdk_region_offset(updateChanges, aDelta.x, aDelta.y); - - // Make |rectRegion| the destination - gdk_region_offset(rectRegion, aDelta.x, aDelta.y); - // Remove any old invalid areas covered at the destination. - gdk_region_subtract(updateArea, rectRegion); - gdk_region_destroy(rectRegion); - - // The update_area from the move_region contains: - // 1. The part of the source region not covered by the destination. - // 2. Any destination regions for which the source was obscured by - // parent window clips or child windows. - GdkRegion* newUpdates = gdk_window_get_update_area(mGdkWindow); - if (newUpdates) { - gdk_region_union(updateChanges, newUpdates); - gdk_region_destroy(newUpdates); + // Also, subtracting the child sub-rectangles from the blit region ensures + // that the blit rectangles will not overlap with any blit or child + // rectangles, so ScrollItemIter will ensure that blit rectangles do not + // conflict with each other. + { + nsAutoRef childRegion; + InitRegion(&childRegion, movingChildSubRects); + + pixman_region32_subtract(&blitRegion, &blitRegion, &childRegion); + } + + nsTArray blitRects; + GetIntRects(blitRegion, &blitRects); + + GdkRegion* updateArea = gdk_region_new(); // aborts on OOM + + for (ScrollItemIter iter(aDelta, blitRects, + movingChildren, movingChildSubRects); + !iter.IsDone(); ++iter) { + if (iter.IsBlit()) { + // The parts of source regions not covered by their destination + // get marked invalid by gdk_window_move_region. This is + // necessary (until covered by another blit) because GDK + // translates any pending expose events to the destination, and so + // doesn't know whether an expose event might have been due on the + // source. + // + // However, GDK 2.18 does not subtract the invalid regions at the + // destinations from the update_area, so the seams between + // different moves remain invalid. GDK 2.18 also delays and + // queues move operations. If gdk_window_process_updates is + // called before the moves are flushed, GDK 2.18 removes the + // invalid seams from the move regions, so the seams are left with + // their old content until they get redrawn. Therefore, the + // subtraction of destination invalid regions is performed here. + GdkRegion* recentUpdates = gdk_window_get_update_area(mGdkWindow); + if (recentUpdates) { + gdk_region_union(updateArea, recentUpdates); + gdk_region_destroy(recentUpdates); + } + + // We don't attempt to collect rects into regions because + // gdk_window_move_region, up to GDK 2.16, has a bug where it + // doesn't restrict blitting to the given region, and blits its + // full bounding box. + nsIntRect source = iter.Rect() - aDelta; + GdkRectangle gdkSource = + { source.x, source.y, source.width, source.height }; + GdkRegion* rectRegion = gdk_region_rectangle(&gdkSource); + gdk_window_move_region(mGdkWindow, rectRegion, + aDelta.x, aDelta.y); + + // The update_area on mGdkWindow from the move_region contains + // invalidations from the move: + // 1. The part of the source region not covered by the destination. + // 2. Any destination regions for which the source was obscured by + // parent window clips or child windows. + // + // Our copy of the old invalid region needs adjusting. + + // The part of the old invalid region that is moving. + GdkRegion* updateChanges = gdk_region_copy(rectRegion); + gdk_region_intersect(updateChanges, updateArea); + gdk_region_offset(updateChanges, aDelta.x, aDelta.y); + + // Make |rectRegion| the destination + gdk_region_offset(rectRegion, aDelta.x, aDelta.y); + // Remove any old invalid areas covered at the destination. + gdk_region_subtract(updateArea, rectRegion); + gdk_region_union(updateArea, updateChanges); + + gdk_region_destroy(updateChanges); + gdk_region_destroy(rectRegion); + } else { + const Configuration *conf = iter.Configuration(); + nsWindow* w = static_cast(conf->mChild); + const nsIntRect& newBounds = conf->mBounds; + // (This move will modify the invalid_area on mGdkWindow to + // include areas that are uncovered when the child moves.) + w->Move(newBounds.x, newBounds.y); } - gdk_region_union(updateArea, updateChanges); - gdk_region_destroy(updateChanges); } gdk_window_invalidate_region(mGdkWindow, updateArea, FALSE); gdk_region_destroy(updateArea); ConfigureChildren(aConfigurations); - - for (PRUint32 i = 0; i < windowsToShow.Length(); ++i) { - windowsToShow[i]->NativeShow(PR_TRUE); - } } void* @@ -2533,10 +2764,21 @@ void nsWindow::OnEnterNotifyEvent(GtkWidget *aWidget, GdkEventCrossing *aEvent) { - // XXXldb Is this the right test for embedding cases? + // This skips NotifyVirtual and NotifyNonlinearVirtual enter notify events + // when the pointer enters a child window. If the destination window is a + // Gecko window then we'll catch the corresponding event on that window, + // but we won't notice when the pointer directly enters a foreign (plugin) + // child window without passing over a visible portion of a Gecko window. if (aEvent->subwindow != NULL) return; + // Check before is_parent_ungrab_enter() as the button state may have + // changed while a non-Gecko ancestor window had a pointer grab. + DispatchMissedButtonReleases(aEvent); + + if (is_parent_ungrab_enter(aEvent)) + return; + nsMouseEvent event(PR_TRUE, NS_MOUSE_ENTER, this, nsMouseEvent::eReal); event.refPoint.x = nscoord(aEvent->x); @@ -2550,6 +2792,7 @@ DispatchEvent(&event, status); } +// XXX Is this the right test for embedding cases? static PRBool is_top_level_mouse_exit(GdkWindow* aWindow, GdkEventCrossing *aEvent) { @@ -2567,7 +2810,14 @@ void nsWindow::OnLeaveNotifyEvent(GtkWidget *aWidget, GdkEventCrossing *aEvent) { - // XXXldb Is this the right test for embedding cases? + // This ignores NotifyVirtual and NotifyNonlinearVirtual leave notify + // events when the pointer leaves a child window. If the destination + // window is a Gecko window then we'll catch the corresponding event on + // that window. + // + // XXXkt However, we will miss toplevel exits when the pointer directly + // leaves a foreign (plugin) child window without passing over a visible + // portion of a Gecko window. if (aEvent->subwindow != NULL) return; @@ -2743,6 +2993,61 @@ } #endif +// If the automatic pointer grab on ButtonPress has deactivated before +// ButtonRelease, and the mouse button is released while the pointer is not +// over any a Gecko window, then the ButtonRelease event will not be received. +// (A similar situation exists when the pointer is grabbed with owner_events +// True as the ButtonRelease may be received on a foreign [plugin] window). +// Use this method to check for released buttons when the pointer returns to a +// Gecko window. +void +nsWindow::DispatchMissedButtonReleases(GdkEventCrossing *aGdkEvent) +{ + guint changed = aGdkEvent->state ^ gButtonState; + // Only consider button releases. + // (Ignore button presses that occurred outside Gecko.) + guint released = changed & gButtonState; + gButtonState = aGdkEvent->state; + + // Loop over each button, excluding mouse wheel buttons 4 and 5 for which + // GDK ignores releases. + for (guint buttonMask = GDK_BUTTON1_MASK; + buttonMask <= GDK_BUTTON3_MASK; + buttonMask <<= 1) { + + if (released & buttonMask) { + PRInt16 buttonType; + switch (buttonMask) { + case GDK_BUTTON1_MASK: + buttonType = nsMouseEvent::eLeftButton; + break; + case GDK_BUTTON2_MASK: + buttonType = nsMouseEvent::eMiddleButton; + break; + default: + NS_ASSERTION(buttonMask == GDK_BUTTON3_MASK, + "Unexpected button mask"); + buttonType = nsMouseEvent::eRightButton; + } + + LOG(("Synthesized button %u release on %p\n", + guint(buttonType + 1), (void *)this)); + + // Dispatch a synthesized button up event to tell Gecko about the + // change in state. This event is marked as synthesized so that + // it is not dispatched as a DOM event, because we don't know the + // position, widget, modifiers, or time/order. + nsMouseEvent synthEvent(PR_TRUE, NS_MOUSE_BUTTON_UP, this, + nsMouseEvent::eSynthesized); + synthEvent.button = buttonType; + nsEventStatus status; + DispatchEvent(&synthEvent, status); + + sLastButtonReleaseTime = aGdkEvent->time; + } + } +} + void nsWindow::InitButtonEvent(nsMouseEvent &aEvent, GdkEventButton *aGdkEvent) @@ -2776,13 +3081,20 @@ } } +static guint ButtonMaskFromGDKButton(guint button) +{ + return GDK_BUTTON1_MASK << (button - 1); +} + void nsWindow::OnButtonPressEvent(GtkWidget *aWidget, GdkEventButton *aEvent) { + LOG(("Button %u press on %p\n", aEvent->button, (void *)this)); + nsEventStatus status; - // If you double click in GDK, it will actually generate a single - // click event before sending the double click event, and this is + // If you double click in GDK, it will actually generate a second + // GDK_BUTTON_PRESS before sending the GDK_2BUTTON_PRESS, and this is // different than the DOM spec. GDK puts this in the queue // programatically, so it's safe to assume that if there's a // double click in the queue, it was generated so we can just drop @@ -2796,8 +3108,8 @@ } // Always save the time of this event - mLastButtonPressTime = aEvent->time; - mLastButtonReleaseTime = 0; + sLastButtonPressTime = aEvent->time; + sLastButtonReleaseTime = 0; nsWindow *containerWindow = GetContainerWindow(); if (!gFocusWindow && containerWindow) { @@ -2860,6 +3172,8 @@ return; } + gButtonState |= ButtonMaskFromGDKButton(aEvent->button); + nsMouseEvent event(PR_TRUE, NS_MOUSE_BUTTON_DOWN, this, nsMouseEvent::eReal); event.button = domButton; InitButtonEvent(event, aEvent); @@ -2881,8 +3195,10 @@ void nsWindow::OnButtonReleaseEvent(GtkWidget *aWidget, GdkEventButton *aEvent) { + LOG(("Button %u release on %p\n", aEvent->button, (void *)this)); + PRUint16 domButton; - mLastButtonReleaseTime = aEvent->time; + sLastButtonReleaseTime = aEvent->time; switch (aEvent->button) { case 1: @@ -2898,6 +3214,8 @@ return; } + gButtonState &= ~ButtonMaskFromGDKButton(aEvent->button); + nsMouseEvent event(PR_TRUE, NS_MOUSE_BUTTON_UP, this, nsMouseEvent::eReal); event.button = domButton; InitButtonEvent(event, aEvent); @@ -3101,6 +3419,7 @@ } #ifdef MOZ_X11 +#if ! defined AIX // no XFree86 on AIX 5L // Look for specialized app-command keys switch (aEvent->keyval) { case XF86XK_Back: @@ -3118,6 +3437,7 @@ case XF86XK_HomePage: return DispatchCommandEvent(nsWidgetAtoms::Home); } +#endif /* ! AIX */ #endif /* MOZ_X11 */ nsKeyEvent event(PR_TRUE, NS_KEY_PRESS, this); @@ -3450,7 +3770,7 @@ { LOGDRAG(("nsWindow::OnDragMotionSignal\n")); - if (mLastButtonReleaseTime) { + if (sLastButtonReleaseTime) { // The drag ended before it was even setup to handle the end of the drag // So, we fake the button getting released again to release the drag GtkWidget *widget = gtk_grab_get_current(); @@ -3458,9 +3778,9 @@ gboolean retval; memset(&event, 0, sizeof(event)); event.type = GDK_BUTTON_RELEASE; - event.button.time = mLastButtonReleaseTime; + event.button.time = sLastButtonReleaseTime; event.button.button = 1; - mLastButtonReleaseTime = 0; + sLastButtonReleaseTime = 0; if (widget) { g_signal_emit_by_name(widget, "button_release_event", &event, &retval); return TRUE; @@ -4065,6 +4385,25 @@ g_signal_connect_after(default_settings, "notify::gtk-font-name", G_CALLBACK(theme_changed_cb), this); + +#ifdef MOZ_PLATFORM_MAEMO + if (mWindowType == eWindowType_toplevel) { + GdkWindow *gdkwin = mShell->window; + + // Tell the Hildon desktop that we support being rotated + gulong portrait_set = 1; + GdkAtom support = gdk_atom_intern("_HILDON_PORTRAIT_MODE_SUPPORT", FALSE); + gdk_property_change(gdkwin, support, gdk_x11_xatom_to_atom(XA_CARDINAL), + 32, GDK_PROP_MODE_REPLACE, + (const guchar *) &portrait_set, 1); + + // Tell maemo-status-volume daemon to ungrab keys + gulong volume_set = 1; + GdkAtom keys = gdk_atom_intern("_HILDON_ZOOM_KEY_ATOM", FALSE); + gdk_property_change(gdkwin, keys, gdk_x11_xatom_to_atom(XA_INTEGER), + 32, GDK_PROP_MODE_REPLACE, (const guchar *) &volume_set, 1); + } +#endif } if (mContainer) { @@ -4526,8 +4865,7 @@ nsWindow* w = static_cast(configuration.mChild); NS_ASSERTION(w->GetParent() == this, "Configured widget is not a child"); - nsresult rv = w->SetWindowClipRegion(configuration.mClipRegion); - NS_ENSURE_SUCCESS(rv, rv); + w->SetWindowClipRegion(configuration.mClipRegion, PR_TRUE); if (w->mBounds.Size() != configuration.mBounds.Size()) { w->Resize(configuration.mBounds.x, configuration.mBounds.y, configuration.mBounds.width, configuration.mBounds.height, @@ -4535,24 +4873,49 @@ } else if (w->mBounds.TopLeft() != configuration.mBounds.TopLeft()) { w->Move(configuration.mBounds.x, configuration.mBounds.y); } + w->SetWindowClipRegion(configuration.mClipRegion, PR_FALSE); } return NS_OK; } -nsresult -nsWindow::SetWindowClipRegion(const nsTArray& aRects) +void +nsWindow::SetWindowClipRegion(const nsTArray& aRects, + PRBool aIntersectWithExisting) { - if (!StoreWindowClipRegion(aRects)) - return NS_OK; + const nsTArray* newRects = &aRects; + + nsAutoTArray intersectRects; + if (aIntersectWithExisting) { + nsAutoTArray existingRects; + GetWindowClipRegion(&existingRects); + + nsAutoRef existingRegion; + InitRegion(&existingRegion, existingRects); + nsAutoRef newRegion; + InitRegion(&newRegion, aRects); + nsAutoRef intersectRegion; + pixman_region32_init(&intersectRegion); + pixman_region32_intersect(&intersectRegion, + &newRegion, &existingRegion); + + if (pixman_region32_equal(&intersectRegion, &existingRegion)) + return; + + if (!pixman_region32_equal(&intersectRegion, &newRegion)) { + GetIntRects(intersectRegion, &intersectRects); + newRects = &intersectRects; + } + } + + if (!StoreWindowClipRegion(*newRects)) + return; if (!mGdkWindow) - return NS_OK; + return; - GdkRegion *region = gdk_region_new(); - if (!region) - return NS_ERROR_OUT_OF_MEMORY; - for (PRUint32 i = 0; i < aRects.Length(); ++i) { - const nsIntRect& r = aRects[i]; + GdkRegion *region = gdk_region_new(); // aborts on OOM + for (PRUint32 i = 0; i < newRects->Length(); ++i) { + const nsIntRect& r = newRects->ElementAt(i); GdkRectangle rect = { r.x, r.y, r.width, r.height }; gdk_region_union_with_rect(region, &rect); } @@ -4560,7 +4923,7 @@ gdk_window_shape_combine_region(mGdkWindow, region, 0, 0); gdk_region_destroy(region); - return NS_OK; + return; } void @@ -5518,10 +5881,6 @@ enter_notify_event_cb(GtkWidget *widget, GdkEventCrossing *event) { - if (is_parent_ungrab_enter(event)) { - return TRUE; - } - nsRefPtr window = get_window_for_gdk_window(event->window); if (!window) return TRUE; diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/gtk2/nsWindow.h firefox-3.6.4+build1+nobinonly/mozilla/widget/src/gtk2/nsWindow.h --- firefox-3.6.3+nobinonly/mozilla/widget/src/gtk2/nsWindow.h 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/gtk2/nsWindow.h 2010-04-16 17:32:51.000000000 +0100 @@ -301,8 +301,8 @@ Window mOldFocusWindow; #endif /* MOZ_X11 */ - static guint32 mLastButtonPressTime; - static guint32 mLastButtonReleaseTime; + static guint32 sLastButtonPressTime; + static guint32 sLastButtonReleaseTime; NS_IMETHOD BeginResizeDrag (nsGUIEvent* aEvent, PRInt32 aHorizontal, PRInt32 aVertical); @@ -444,7 +444,8 @@ void SetDefaultIcon(void); void InitButtonEvent(nsMouseEvent &aEvent, GdkEventButton *aGdkEvent); PRBool DispatchCommandEvent(nsIAtom* aCommand); - nsresult SetWindowClipRegion(const nsTArray& aRects); + void SetWindowClipRegion(const nsTArray& aRects, + PRBool aIntersectWithExisting); GtkWidget *mShell; MozContainer *mContainer; @@ -564,6 +565,7 @@ *flag &= ~mask; } + void DispatchMissedButtonReleases(GdkEventCrossing *aGdkEvent); }; class nsChildWindow : public nsWindow { diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/os2/nsWindow.cpp firefox-3.6.4+build1+nobinonly/mozilla/widget/src/os2/nsWindow.cpp --- firefox-3.6.3+nobinonly/mozilla/widget/src/os2/nsWindow.cpp 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/os2/nsWindow.cpp 2010-04-16 17:32:51.000000000 +0100 @@ -2152,9 +2152,9 @@ CheckDragStatus(ACTION_SCROLL, &hps); // Step through each rectangle to be scrolled. - for (PRUint32 i = 0; i < aDestRects.Length(); ++i) { + for (BlitRectIter iter(aDelta, aDestRects); !iter.IsDone(); ++iter) { nsIntRect affectedRect; - affectedRect.UnionRect(aDestRects[i], aDestRects[i] - aDelta); + affectedRect.UnionRect(iter.Rect(), iter.Rect() - aDelta); ULONG flags = SW_INVALIDATERGN; diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/windows/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/widget/src/windows/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/widget/src/windows/Makefile.in 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/windows/Makefile.in 2010-04-16 17:32:51.000000000 +0100 @@ -143,6 +143,7 @@ -I$(srcdir)/../xpwidgets \ -I$(srcdir) \ -I$(topsrcdir)/toolkit/xre \ + -I$(topsrcdir)/toolkit/crashreporter \ $(NULL) FORCE_STATIC_LIB = 1 @@ -155,6 +156,11 @@ ENABLE_CXX_EXCEPTIONS = 1 endif +ifdef MOZ_IPC +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk +endif + include $(topsrcdir)/config/rules.mk CXXFLAGS += $(MOZ_CAIRO_CFLAGS) diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/windows/nsAccelerometerWin.cpp firefox-3.6.4+build1+nobinonly/mozilla/widget/src/windows/nsAccelerometerWin.cpp --- firefox-3.6.3+nobinonly/mozilla/widget/src/windows/nsAccelerometerWin.cpp 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/windows/nsAccelerometerWin.cpp 2010-04-16 17:32:51.000000000 +0100 @@ -410,7 +410,7 @@ PRBool ThinkPadSensor::Startup() { - mLibrary = LoadLibrary("sensor.dll"); + mLibrary = LoadLibraryW(L"sensor.dll"); if (!mLibrary) return PR_FALSE; diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/windows/nsDeviceContextSpecWin.cpp firefox-3.6.4+build1+nobinonly/mozilla/widget/src/windows/nsDeviceContextSpecWin.cpp --- firefox-3.6.3+nobinonly/mozilla/widget/src/windows/nsDeviceContextSpecWin.cpp 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/windows/nsDeviceContextSpecWin.cpp 2010-04-16 17:32:51.000000000 +0100 @@ -95,9 +95,9 @@ void FreeGlobalPrinters(); PRBool PrintersAreAllocated() { return mPrinters != nsnull; } - LPTSTR GetItemFromList(PRInt32 aInx) { return mPrinters?mPrinters->ElementAt(aInx):nsnull; } + LPWSTR GetItemFromList(PRInt32 aInx) { return mPrinters?mPrinters->ElementAt(aInx):nsnull; } nsresult EnumeratePrinterList(); - void GetDefaultPrinterName(LPTSTR& aDefaultPrinterName); + void GetDefaultPrinterName(nsString& aDefaultPrinterName); PRInt32 GetNumPrinters() { return mPrinters?mPrinters->Length():0; } protected: @@ -106,12 +106,12 @@ void ReallocatePrinters(); static GlobalPrinters mGlobalPrinters; - static nsTArray* mPrinters; + static nsTArray* mPrinters; }; //--------------- // static members GlobalPrinters GlobalPrinters::mGlobalPrinters; -nsTArray* GlobalPrinters::mPrinters = nsnull; +nsTArray* GlobalPrinters::mPrinters = nsnull; //****************************************************** @@ -208,14 +208,9 @@ // helper static PRUnichar * GetDefaultPrinterNameFromGlobalPrinters() { - PRUnichar * printerName; - LPTSTR lpPrtName; - GlobalPrinters::GetInstance()->GetDefaultPrinterName(lpPrtName); - nsAutoString str; - NS_CopyNativeToUnicode(nsDependentCString((char *)lpPrtName), str); - printerName = ToNewUnicode(str); - free(lpPrtName); - return printerName; + nsAutoString printerName; + GlobalPrinters::GetInstance()->GetDefaultPrinterName(printerName); + return ToNewUnicode(printerName); } //---------------------------------------------------------------- @@ -230,22 +225,23 @@ LPPRINTER_INFO_2W lpInfo = NULL; // Get buffer size - if (::EnumPrinters ( aWhichPrinters, NULL, 2, NULL, 0, &dwSizeNeeded, &dwNumItems )) { + if (::EnumPrintersW(aWhichPrinters, NULL, 2, NULL, 0, &dwSizeNeeded, + &dwNumItems)) { return NS_ERROR_FAILURE; } // allocate memory - lpInfo = (LPPRINTER_INFO_2W)HeapAlloc ( GetProcessHeap (), HEAP_ZERO_MEMORY, dwSizeNeeded ); - if ( lpInfo == NULL ) { + lpInfo = (LPPRINTER_INFO_2W) malloc(dwSizeNeeded); + if (!lpInfo) { return NS_ERROR_OUT_OF_MEMORY; } - if (::EnumPrinters ( PRINTER_ENUM_LOCAL, NULL, 2, (LPBYTE)lpInfo, dwSizeNeeded, &dwSizeNeeded, &dwNumItems) == 0 ) { - ::HeapFree(GetProcessHeap (), 0, lpInfo); + if (::EnumPrintersW(PRINTER_ENUM_LOCAL, NULL, 2, (LPBYTE)lpInfo, + dwSizeNeeded, &dwSizeNeeded, &dwNumItems) == 0) { + free(lpInfo); return NS_OK; } - for (DWORD i = 0; i < dwNumItems; i++ ) { if (wcscmp(lpInfo[i].pPrinterName, aPrinterName) == 0) { aIsFound = PR_TRUE; @@ -254,7 +250,7 @@ } } - ::HeapFree(GetProcessHeap (), 0, lpInfo); + free(lpInfo); #endif return NS_OK; } @@ -675,29 +671,7 @@ } -#if defined(DEBUG_rods) || defined(DEBUG_dcone) -static void DisplayLastError() -{ - LPVOID lpMsgBuf; - DWORD errCode = GetLastError(); - - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - GetLastError(), - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language - (LPTSTR) &lpMsgBuf, - 0, - NULL - ); - - // Display the string. - MessageBox( NULL, (const char *)lpMsgBuf, "GetLastError", MB_OK|MB_ICONINFORMATION ); -} -#define DISPLAY_LAST_ERROR DisplayLastError(); -#else #define DISPLAY_LAST_ERROR -#endif //---------------------------------------------------------------------------------- // Setup the object's data member with the selected printer's data @@ -945,14 +919,8 @@ PRInt32 printerInx = 0; while( printerInx < numPrinters ) { - LPTSTR name = GlobalPrinters::GetInstance()->GetItemFromList(printerInx++); -#ifdef UNICODE - nsDependentString newName(name); -#else - nsAutoString newName; - NS_CopyNativeToUnicode(nsDependentCString(name), newName); -#endif - printers->AppendElement(newName); + LPWSTR name = GlobalPrinters::GetInstance()->GetItemFromList(printerInx++); + printers->AppendElement(nsDependentString(name)); } return NS_NewAdoptingStringEnumerator(aPrinterNameList, printers); @@ -978,7 +946,7 @@ if (PrintersAreAllocated()) { FreeGlobalPrinters(); } - mPrinters = new nsTArray(); + mPrinters = new nsTArray(); NS_ASSERTION(mPrinters, "Printers Array is NULL!"); } @@ -1004,16 +972,18 @@ PR_PL(("-----------------------\n")); PR_PL(("EnumerateNativePrinters\n")); - TCHAR szDefaultPrinterName[1024]; - DWORD status = GetProfileString("devices", 0, ",", szDefaultPrinterName, sizeof(szDefaultPrinterName)/sizeof(TCHAR)); + WCHAR szDefaultPrinterName[1024]; + DWORD status = GetProfileStringW(L"devices", 0, L",", + szDefaultPrinterName, + NS_ARRAY_LENGTH(szDefaultPrinterName)); if (status > 0) { DWORD count = 0; - LPTSTR sPtr = (LPTSTR)szDefaultPrinterName; - LPTSTR ePtr = (LPTSTR)(szDefaultPrinterName+(status*sizeof(TCHAR))); - LPTSTR prvPtr = sPtr; + LPWSTR sPtr = szDefaultPrinterName; + LPWSTR ePtr = szDefaultPrinterName + status; + LPWSTR prvPtr = sPtr; while (sPtr < ePtr) { if (*sPtr == NULL) { - LPTSTR name = _tcsdup(prvPtr); + LPWSTR name = wcsdup(prvPtr); mPrinters->AppendElement(name); PR_PL(("Printer Name: %s\n", prvPtr)); prvPtr = sPtr+1; @@ -1031,28 +1001,30 @@ //------------------------------------------------------------------ // Uses the GetProfileString to get the default printer from the registry void -GlobalPrinters::GetDefaultPrinterName(LPTSTR& aDefaultPrinterName) +GlobalPrinters::GetDefaultPrinterName(nsString& aDefaultPrinterName) { #ifndef WINCE - aDefaultPrinterName = nsnull; - TCHAR szDefaultPrinterName[1024]; - DWORD status = GetProfileString("windows", "device", 0, szDefaultPrinterName, sizeof(szDefaultPrinterName)/sizeof(TCHAR)); + aDefaultPrinterName.Truncate(); + WCHAR szDefaultPrinterName[1024]; + DWORD status = GetProfileStringW(L"windows", L"device", 0, + szDefaultPrinterName, + NS_ARRAY_LENGTH(szDefaultPrinterName)); if (status > 0) { - TCHAR comma = (TCHAR)','; - LPTSTR sPtr = (LPTSTR)szDefaultPrinterName; + WCHAR comma = ','; + LPWSTR sPtr = szDefaultPrinterName; while (*sPtr != comma && *sPtr != NULL) sPtr++; if (*sPtr == comma) { *sPtr = NULL; } - aDefaultPrinterName = _tcsdup(szDefaultPrinterName); + aDefaultPrinterName = szDefaultPrinterName; } else { - aDefaultPrinterName = _tcsdup(""); + aDefaultPrinterName = NS_LITERAL_STRING(""); } PR_PL(("DEFAULT PRINTER [%s]\n", aDefaultPrinterName)); #else - aDefaultPrinterName = TEXT("UNKNOWN"); + aDefaultPrinterName = NS_LITERAL_STRING("UNKNOWN"); #endif } @@ -1072,23 +1044,22 @@ if (NS_FAILED(rv)) return rv; // get the name of the default printer - LPTSTR defPrinterName; + nsAutoString defPrinterName; GetDefaultPrinterName(defPrinterName); // put the default printer at the beginning of list - if (defPrinterName != nsnull) { + if (!defPrinterName.IsEmpty()) { for (PRInt32 i=0;iLength();i++) { - LPTSTR name = mPrinters->ElementAt(i); - if (!_tcscmp(name, defPrinterName)) { + LPWSTR name = mPrinters->ElementAt(i); + if (defPrinterName.Equals(name)) { if (i > 0) { - LPTSTR ptr = mPrinters->ElementAt(0); + LPWSTR ptr = mPrinters->ElementAt(0); mPrinters->ElementAt(0) = name; mPrinters->ElementAt(i) = ptr; } break; } } - free(defPrinterName); } // make sure we at least tried to get the printers diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/windows/nsWindow.cpp firefox-3.6.4+build1+nobinonly/mozilla/widget/src/windows/nsWindow.cpp --- firefox-3.6.3+nobinonly/mozilla/widget/src/windows/nsWindow.cpp 2010-04-02 16:59:32.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/windows/nsWindow.cpp 2010-04-16 17:32:51.000000000 +0100 @@ -104,6 +104,10 @@ ************************************************************** **************************************************************/ +#ifdef MOZ_IPC +#include "mozilla/ipc/RPCChannel.h" +#endif + #include "nsWindow.h" #include @@ -205,10 +209,6 @@ #include "nsGfxCIID.h" #endif -// A magic APP message that can be sent to quit, sort of like a QUERYENDSESSION/ENDSESSION, -// but without the query. -#define MOZ_WM_APP_QUIT (WM_APP+0x0300) - /************************************************************** ************************************************************** ** @@ -277,6 +277,13 @@ nsWindow::sLresultFromObject = 0; #endif // ACCESSIBILITY +#ifdef MOZ_IPC +// Used in OOPP plugin focus processing. +const PRUnichar* kOOPPPluginFocusEventId = L"OOPP Plugin Focus Widget Event"; +PRUint32 nsWindow::sOOPPPluginFocusEvent = + RegisterWindowMessageW(kOOPPPluginFocusEventId); +#endif + /************************************************************** * * SECTION: globals variables @@ -325,7 +332,6 @@ is->IdleTimeWasModified(); } - // Global user preference for disabling native theme. Used // in NativeWindowTheme. PRBool gDisableNativeTheme = PR_FALSE; @@ -2250,8 +2256,8 @@ DWORD ourThreadID = GetWindowThreadProcessId(mWnd, NULL); - for (PRUint32 i = 0; i < aDestRects.Length(); ++i) { - const nsIntRect& destRect = aDestRects[i]; + for (BlitRectIter iter(aDelta, aDestRects); !iter.IsDone(); ++iter) { + const nsIntRect& destRect = iter.Rect(); nsIntRect affectedRect; affectedRect.UnionRect(destRect, destRect - aDelta); UINT flags = SW_SCROLLCHILDREN; @@ -2265,8 +2271,8 @@ if (entry) { // It's supposed to be scrolled, so we can still use // SW_SCROLLCHILDREN. But don't allow SW_SCROLLCHILDREN to be - // used on it again by a later rectangle in aDestRects, we - // don't want it to move twice! + // used on it again by a later rectangle; we don't want it to + // move twice! scrolledWidgets.RawRemoveEntry(entry); nsIntPoint screenOffset = WidgetToScreenOffset(); @@ -3106,9 +3112,8 @@ // its one of our windows so check to see if it has a // invalidated rect. If it does. Dispatch a synchronous // paint. - if (GetUpdateRect(aWnd, NULL, FALSE)) { + if (GetUpdateRect(aWnd, NULL, FALSE)) VERIFY(::UpdateWindow(aWnd)); - } } return TRUE; } @@ -3337,6 +3342,9 @@ case NS_MOUSE_MOVE: pluginEvent.event = WM_MOUSEMOVE; break; + case NS_MOUSE_EXIT: + pluginEvent.event = WM_MOUSELEAVE; + break; default: pluginEvent.event = WM_NULL; break; @@ -3546,6 +3554,101 @@ } /************************************************************** + * + * SECTION: IPC + * + * IPC related helpers. + * + **************************************************************/ + +#ifdef MOZ_IPC + +// static +bool +nsWindow::IsAsyncResponseEvent(UINT aMsg, LRESULT& aResult) +{ + switch(aMsg) { + case WM_SETFOCUS: + case WM_KILLFOCUS: + case WM_ENABLE: + case WM_WINDOWPOSCHANGING: + case WM_WINDOWPOSCHANGED: + case WM_PARENTNOTIFY: + case WM_ACTIVATEAPP: + case WM_NCACTIVATE: + case WM_ACTIVATE: + case WM_CHILDACTIVATE: + case WM_IME_SETCONTEXT: + case WM_IME_NOTIFY: + case WM_SHOWWINDOW: + case WM_CANCELMODE: + case WM_MOUSEACTIVATE: + case WM_CONTEXTMENU: + aResult = 0; + return true; + + case WM_SETTINGCHANGE: + case WM_SETCURSOR: + return false; + } + +#ifdef DEBUG + char szBuf[200]; + sprintf(szBuf, + "An unhandled ISMEX_SEND message was received during spin loop! (%X)", aMsg); + NS_WARNING(szBuf); +#endif + + return false; +} + +void +nsWindow::IPCWindowProcHandler(UINT& msg, WPARAM& wParam, LPARAM& lParam) +{ + NS_ASSERTION(!mozilla::ipc::SyncChannel::IsPumpingMessages(), + "Failed to prevent a nonqueued message from running!"); + + // Modal UI being displayed in windowless plugins. + if (mozilla::ipc::RPCChannel::IsSpinLoopActive() && + (InSendMessageEx(NULL)&(ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND) { + LRESULT res; + if (IsAsyncResponseEvent(msg, res)) { + ReplyMessage(res); + } + return; + } + + // Handle certain sync plugin events sent to the parent which + // trigger ipc calls that result in deadlocks. + + // Plugins taking focus triggering WM_SETFOCUS app messages. + if (msg == WM_SETFOCUS && + (InSendMessageEx(NULL)&(ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND) { + ReplyMessage(0); + return; + } + + // Windowless flash sending WM_ACTIVATE events to the main window + // via calls to ShowWindow. + if (msg == WM_ACTIVATE && lParam != 0 && + LOWORD(wParam) == WA_ACTIVE && IsWindow((HWND)lParam) && + (InSendMessageEx(NULL)&(ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND) { + ReplyMessage(0); + return; + } + + // Windowed plugins that pass sys key events to defwndproc generate + // WM_SYSCOMMAND events to the main window. + if (msg == WM_SYSCOMMAND && + (InSendMessageEx(NULL)&(ISMEX_REPLIED|ISMEX_SEND)) == ISMEX_SEND) { + ReplyMessage(0); + return; + } +} + +#endif // MOZ_IPC + +/************************************************************** ************************************************************** ** ** BLOCK: Native events @@ -3568,17 +3671,22 @@ // The WndProc procedure for all nsWindows in this toolkit LRESULT CALLBACK nsWindow::WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { + // Get the window which caused the event and ask it to process the message + nsWindow *someWindow = GetNSWindowPtr(hWnd); + +#ifdef MOZ_IPC + if (someWindow) + someWindow->IPCWindowProcHandler(msg, wParam, lParam); +#endif + // create this here so that we store the last rolled up popup until after // the event has been processed. nsAutoRollup autoRollup; LRESULT popupHandlingResult; - if ( DealWithPopups(hWnd, msg, wParam, lParam, &popupHandlingResult) ) + if (DealWithPopups(hWnd, msg, wParam, lParam, &popupHandlingResult)) return popupHandlingResult; - // Get the window which caused the event and ask it to process the message - nsWindow *someWindow = GetNSWindowPtr(hWnd); - // XXX This fixes 50208 and we are leaving 51174 open to further investigate // why we are hitting this assert if (nsnull == someWindow) { @@ -3603,15 +3711,15 @@ } // Call ProcessMessage - if (nsnull != someWindow) { - LRESULT retValue; - if (PR_TRUE == someWindow->ProcessMessage(msg, wParam, lParam, &retValue)) { - return retValue; - } + LRESULT retValue; + if (PR_TRUE == someWindow->ProcessMessage(msg, wParam, lParam, &retValue)) { + return retValue; } - return ::CallWindowProcW(someWindow->GetPrevWindowProc(), - hWnd, msg, wParam, lParam); + LRESULT res = ::CallWindowProcW(someWindow->GetPrevWindowProc(), + hWnd, msg, wParam, lParam); + + return res; } // The main windows message processing method for plugins. @@ -3698,7 +3806,13 @@ // (Large blocks of code should be broken out into OnEvent handlers.) if (mWindowHook.Notify(mWnd, msg, wParam, lParam, aRetValue)) return PR_TRUE; - + +#if defined(EVENT_DEBUG_OUTPUT) + // First param shows all events, second param indicates whether + // to show mouse move events. See nsWindowDbg for details. + PrintEvent(msg, SHOW_REPEAT_EVENTS, SHOW_MOUSEMOVE_EVENTS); +#endif + PRBool eatMessage; if (nsIMM32Handler::ProcessMessage(this, msg, wParam, lParam, aRetValue, eatMessage)) { @@ -3719,12 +3833,6 @@ static PRBool getWheelInfo = PR_TRUE; -#if defined(EVENT_DEBUG_OUTPUT) - // First param shows all events, second param indicates whether - // to show mouse move events. See nsWindowDbg for details. - PrintEvent(msg, SHOW_REPEAT_EVENTS, SHOW_MOUSEMOVE_EVENTS); -#endif - switch (msg) { case WM_COMMAND: { @@ -4561,6 +4669,15 @@ if (msg == nsAppShell::GetTaskbarButtonCreatedMessage()) SetHasTaskbarIconBeenCreated(); #endif +#ifdef MOZ_IPC + if (msg == sOOPPPluginFocusEvent) { + // With OOPP, the plugin window exists in another process and is a child of + // this window. This window is a placeholder plugin window for the dom. We + // receive this event when the child window receives focus. (sent from + // PluginInstanceParent.cpp) + ::SendMessage(mWnd, WM_MOUSEACTIVATE, 0, 0); // See nsPluginNativeWindowWin.cpp + } +#endif } break; } @@ -4694,7 +4811,7 @@ } if (!nsIMM32Handler::IsComposing(this) && - (aMsg.message != WM_KEYUP || aMsg.message != VK_MENU)) { + (aMsg.message != WM_KEYUP || aMsg.wParam != VK_MENU)) { // Ignore VK_MENU if it's not a system key release, so that the menu bar does not trigger // This helps avoid triggering the menu bar for ALT key accelerators used in // assistive technologies such as Window-Eyes and ZoomText, and when using Alt+Tab @@ -5250,8 +5367,18 @@ } } - if (!scrollEvent.delta) + if (!scrollEvent.delta) { + // We store the wheel delta, and it will be used next wheel message, so, + // we consume this message actually. We shouldn't call next wndproc. + result = PR_TRUE; return PR_FALSE; // break + } + +#ifdef MOZ_IPC + // The event may go to a plug-in which already dispatched this message. + // Then, the event can cause deadlock. We should unlock the sender here. + ::ReplyMessage(isVertical ? 0 : TRUE); +#endif scrollEvent.isShift = IS_VK_DOWN(NS_VK_SHIFT); scrollEvent.isControl = IS_VK_DOWN(NS_VK_CONTROL); @@ -5948,6 +6075,23 @@ nsWindowGfx::OnSettingsChangeGfx(wParam); } +static PRBool IsOurProcessWindow(HWND aHWND) +{ + DWORD processId = 0; + ::GetWindowThreadProcessId(aHWND, &processId); + return processId == ::GetCurrentProcessId(); +} + +static HWND FindOurProcessWindow(HWND aHWND) +{ + for (HWND wnd = ::GetParent(aHWND); wnd; wnd = ::GetParent(wnd)) { + if (IsOurProcessWindow(wnd)) { + return wnd; + } + } + return nsnull; +} + // Scrolling helper function for handling plugins. // Return value indicates whether the calling function should handle this // aHandled indicates whether this was handled at all @@ -6006,15 +6150,32 @@ // No window is under the pointer return PR_FALSE; // break, but continue processing } - // We don't care about windows belonging to other processes. - DWORD processId = 0; - GetWindowThreadProcessId(destWnd, &processId); - if (processId != GetCurrentProcessId()) - { - // Somebody elses window - return PR_FALSE; // break, but continue processing + + nsWindow* destWindow; + + // We don't handle the message if the found window belongs to another + // process's top window. If it belongs window, that is a plug-in's window. + // Then, we need to send the message to the plug-in window. + if (!IsOurProcessWindow(destWnd)) { + HWND ourPluginWnd = FindOurProcessWindow(destWnd); + if (!ourPluginWnd) { + // Somebody elses window + return PR_FALSE; // break, but continue processing + } + destWindow = GetNSWindowPtr(ourPluginWnd); + } else { + destWindow = GetNSWindowPtr(destWnd); } - nsWindow* destWindow = GetNSWindowPtr(destWnd); + + if (destWindow == this && mWindowType == eWindowType_plugin) { + // If this is plug-in window, the message came from the plug-in window. + // Then, the message should be processed on the parent window. + destWindow = static_cast(GetParent()); + NS_ENSURE_TRUE(destWindow, PR_FALSE); // break, but continue processing + destWnd = destWindow->mWnd; + NS_ENSURE_TRUE(destWnd, PR_FALSE); // break, but continue processing + } + if (!destWindow || destWindow->mWindowType == eWindowType_plugin) { // Some other app, or a plugin window. // Windows directs scrolling messages to the focused window. @@ -6035,15 +6196,24 @@ // others will call DefWndProc, which itself still forwards back to us. // So if we have sent it once, we need to handle it ourself. +#ifdef MOZ_IPC + // XXX The message shouldn't come from the plugin window at here. + // But the message might come from it due to some bugs. If it happens, + // SendMessage causes deadlock. For safety, we should unlock the + // sender here. + ::ReplyMessage(aMsg == WM_MOUSEHWHEEL ? TRUE : 0); +#endif + // First time we have seen this message. // Call the child - either it will consume it, or // it will wind it's way back to us,triggering the destWnd case above // either way,when the call returns,we are all done with the message, sIsProcessing = PR_TRUE; - if (0 == ::SendMessageW(destWnd, aMsg, aWParam, aLParam)) - aHandled = PR_TRUE; + ::SendMessageW(destWnd, aMsg, aWParam, aLParam); sIsProcessing = PR_FALSE; - return PR_FALSE; // break, but continue processing + aHandled = PR_TRUE; + aQuitProcessing = PR_TRUE; + return PR_FALSE; // break, and stop processing } parentWnd = ::GetParent(parentWnd); } // while parentWnd @@ -6097,6 +6267,11 @@ default: return PR_FALSE; } +#ifdef MOZ_IPC + // The event may go to a plug-in which already dispatched this message. + // Then, the event can cause deadlock. We should unlock the sender here. + ::ReplyMessage(0); +#endif scrollevent.isShift = IS_VK_DOWN(NS_VK_SHIFT); scrollevent.isControl = IS_VK_DOWN(NS_VK_CONTROL); scrollevent.isMeta = PR_FALSE; @@ -6972,6 +7147,7 @@ const WCHAR wstrKeys[][40] = {L"Software\\Lenovo\\TrackPoint", L"Software\\Lenovo\\UltraNav", L"Software\\Alps\\Apoint\\TrackPoint", + L"Software\\Synaptics\\SynTPEnh\\UltraNavUSB", L"Software\\Synaptics\\SynTPEnh\\UltraNavPS2"}; // If anything fails turn the hack off sTrackPointHack = false; diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/windows/nsWindowDefs.h firefox-3.6.4+build1+nobinonly/mozilla/widget/src/windows/nsWindowDefs.h --- firefox-3.6.3+nobinonly/mozilla/widget/src/windows/nsWindowDefs.h 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/windows/nsWindowDefs.h 2010-04-16 17:32:51.000000000 +0100 @@ -53,6 +53,10 @@ * **************************************************************/ +// A magic APP message that can be sent to quit, sort of like a QUERYENDSESSION/ENDSESSION, +// but without the query. +#define MOZ_WM_APP_QUIT (WM_APP+0x0300) + // GetWindowsVersion constants #define WIN2K_VERSION 0x500 #define WINXP_VERSION 0x501 @@ -74,6 +78,10 @@ #define WM_MOUSEHWHEEL 0x020E #endif +#ifndef WM_MOUSELEAVE +#define WM_MOUSELEAVE 0x02A3 +#endif + #ifndef SPI_GETWHEELSCROLLCHARS #define SPI_GETWHEELSCROLLCHARS 0x006C #endif diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/windows/nsWindowGfx.cpp firefox-3.6.4+build1+nobinonly/mozilla/widget/src/windows/nsWindowGfx.cpp --- firefox-3.6.3+nobinonly/mozilla/widget/src/windows/nsWindowGfx.cpp 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/windows/nsWindowGfx.cpp 2010-04-16 17:32:51.000000000 +0100 @@ -53,6 +53,11 @@ ************************************************************** **************************************************************/ +#ifdef MOZ_IPC +#include "mozilla/plugins/PluginInstanceParent.h" +using mozilla::plugins::PluginInstanceParent; +#endif + #include "nsWindowGfx.h" #include #include "nsIRegion.h" @@ -325,6 +330,44 @@ PRBool nsWindow::OnPaint(HDC aDC) { +#ifdef MOZ_IPC + if (mWindowType == eWindowType_plugin) { + + /** + * After we CallUpdateWindow to the child, occasionally a WM_PAINT message + * is posted to the parent event loop with an empty update rect. Do a + * dummy paint so that Windows stops dispatching WM_PAINT in an inifinite + * loop. See bug 543788. + */ + RECT updateRect; + if (!GetUpdateRect(mWnd, &updateRect, FALSE) || + (updateRect.left == updateRect.right && + updateRect.top == updateRect.bottom)) { + PAINTSTRUCT ps; + BeginPaint(mWnd, &ps); + EndPaint(mWnd, &ps); + return PR_TRUE; + } + + PluginInstanceParent* instance = reinterpret_cast( + ::GetPropW(mWnd, L"PluginInstanceParentProperty")); + if (instance) { + instance->CallUpdateWindow(); + ValidateRect(mWnd, NULL); + return PR_TRUE; + } + } +#endif + +#ifdef MOZ_IPC + // We never have reentrant paint events, except when we're running our RPC + // windows event spin loop. If we don't trap for this, we'll try to paint, + // but view manager will refuse to paint the surface, resulting is black + // flashes on the plugin rendering surface. + if (mozilla::ipc::RPCChannel::IsSpinLoopActive() && mPainting) + return PR_FALSE; +#endif + nsPaintEvent willPaintEvent(PR_TRUE, NS_WILL_PAINT, this); DispatchWindowEvent(&willPaintEvent); diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/windows/nsWindow.h firefox-3.6.4+build1+nobinonly/mozilla/widget/src/windows/nsWindow.h --- firefox-3.6.3+nobinonly/mozilla/widget/src/windows/nsWindow.h 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/windows/nsWindow.h 2010-04-16 17:32:51.000000000 +0100 @@ -385,6 +385,11 @@ protected: #endif // MOZ_XUL +#ifdef MOZ_IPC + static bool IsAsyncResponseEvent(UINT aMsg, LRESULT& aResult); + void IPCWindowProcHandler(UINT& msg, WPARAM& wParam, LPARAM& lParam); +#endif // MOZ_IPC + /** * Misc. */ @@ -442,6 +447,9 @@ static PRBool sJustGotActivate; static int sTrimOnMinimize; static PRBool sTrackPointHack; +#ifdef MOZ_IPC + static PRUint32 sOOPPPluginFocusEvent; +#endif // Hook Data Memebers for Dropdowns. sProcessHook Tells the // hook methods whether they should be processing the hook diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/windows/WindowHook.cpp firefox-3.6.4+build1+nobinonly/mozilla/widget/src/windows/WindowHook.cpp --- firefox-3.6.3+nobinonly/mozilla/widget/src/windows/WindowHook.cpp 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/windows/WindowHook.cpp 2010-04-16 17:32:51.000000000 +0100 @@ -120,8 +120,10 @@ void WindowHook::DeleteIfEmpty(MessageData *data) { - if (data->hook || data->monitors.IsEmpty()) + // Never remove a MessageData that has still a hook or monitor entries. + if (data->hook || !data->monitors.IsEmpty()) return; + MessageDataArray::index_type idx; idx = data - mMessageData.Elements(); NS_ASSERTION(idx >= 0 && idx < mMessageData.Length(), "Attempted to delete MessageData that doesn't belong to this array!"); diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/xpwidgets/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/widget/src/xpwidgets/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/widget/src/xpwidgets/Makefile.in 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/xpwidgets/Makefile.in 2010-04-16 17:32:51.000000000 +0100 @@ -65,7 +65,11 @@ thebes \ $(NULL) -DEFINES += -D_IMPL_NS_WIDGET -DUSE_TLS_FOR_TOOLKIT +DEFINES += \ + -D_IMPL_NS_WIDGET \ + -DUSE_TLS_FOR_TOOLKIT \ + -DNO_NSPR_10_SUPPORT=1 \ + $(NULL) CPPSRCS = \ nsBaseAppShell.cpp \ @@ -98,7 +102,7 @@ CPPSRCS += nsNativeTheme.cpp endif -LOCAL_INCLUDES = \ +LOCAL_INCLUDES += \ -I$(srcdir)/../$(MOZ_WIDGET_TOOLKIT) \ -I$(srcdir) \ $(NULL) @@ -106,7 +110,8 @@ # we don't want the shared lib, but we want to force the creation of a static lib. FORCE_STATIC_LIB = 1 +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk include $(topsrcdir)/config/rules.mk CXXFLAGS += $(TK_CFLAGS) - diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/xpwidgets/nsAccelerometer.cpp firefox-3.6.4+build1+nobinonly/mozilla/widget/src/xpwidgets/nsAccelerometer.cpp --- firefox-3.6.3+nobinonly/mozilla/widget/src/xpwidgets/nsAccelerometer.cpp 2010-04-02 16:59:32.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/xpwidgets/nsAccelerometer.cpp 2010-04-16 17:32:51.000000000 +0100 @@ -104,15 +104,21 @@ mLastY(10), mLastZ(10), mStarted(PR_FALSE), + mEnabled(PR_TRUE), mNewListener(PR_FALSE), mUpdateInterval(50) /* default to 50 ms */ { nsCOMPtr prefSrv = do_GetService(NS_PREFSERVICE_CONTRACTID); - PRInt32 value; if (prefSrv) { + PRInt32 value; nsresult rv = prefSrv->GetIntPref("accelerometer.update.interval", &value); if (NS_SUCCEEDED(rv)) mUpdateInterval = value; + + PRBool bvalue; + rv = prefSrv->GetBoolPref("accelerometer.enabled", &bvalue); + if (NS_SUCCEEDED(rv) && bvalue == PR_FALSE) + mEnabled = PR_FALSE; } } @@ -197,6 +203,9 @@ void nsAccelerometer::AccelerationChanged(double x, double y, double z) { + if (!mEnabled) + return; + if (x > 1) x = 1; if (y > 1) diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/xpwidgets/nsAccelerometer.h firefox-3.6.4+build1+nobinonly/mozilla/widget/src/xpwidgets/nsAccelerometer.h --- firefox-3.6.3+nobinonly/mozilla/widget/src/xpwidgets/nsAccelerometer.h 2010-04-02 16:59:32.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/xpwidgets/nsAccelerometer.h 2010-04-16 17:32:51.000000000 +0100 @@ -76,6 +76,7 @@ protected: PRUint32 mUpdateInterval; + PRBool mEnabled; virtual void Startup() = 0; virtual void Shutdown() = 0; diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/xpwidgets/nsBaseAppShell.cpp firefox-3.6.4+build1+nobinonly/mozilla/widget/src/xpwidgets/nsBaseAppShell.cpp --- firefox-3.6.3+nobinonly/mozilla/widget/src/xpwidgets/nsBaseAppShell.cpp 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/xpwidgets/nsBaseAppShell.cpp 2010-04-16 17:32:51.000000000 +0100 @@ -41,6 +41,10 @@ #include "nsIObserverService.h" #include "nsServiceManagerUtils.h" +#ifdef MOZ_IPC +#include "base/message_loop.h" +#endif + // When processing the next thread event, the appshell may process native // events (if not in performance mode), which can result in suppressing the // next thread event for at most this many ticks: @@ -59,7 +63,7 @@ , mSwitchTime(0) , mLastNativeEventTime(0) , mEventloopNestingState(eEventloopNone) - , mRunWasCalled(PR_FALSE) + , mRunning(PR_FALSE) , mExiting(PR_FALSE) , mBlockNativeEvent(PR_FALSE) { @@ -161,21 +165,32 @@ NS_IMETHODIMP nsBaseAppShell::Run(void) { - nsIThread *thread = NS_GetCurrentThread(); + NS_ENSURE_STATE(!mRunning); // should not call Run twice + mRunning = PR_TRUE; - NS_ENSURE_STATE(!mRunWasCalled); // should not call Run twice - mRunWasCalled = PR_TRUE; + nsIThread *thread = NS_GetCurrentThread(); +#ifdef MOZ_IPC + MessageLoop::current()->Run(); +#else while (!mExiting) NS_ProcessNextEvent(thread); +#endif NS_ProcessPendingEvents(thread); + + mRunning = PR_FALSE; return NS_OK; } NS_IMETHODIMP nsBaseAppShell::Exit(void) { +#ifdef MOZ_IPC + if (mRunning && !mExiting) { + MessageLoop::current()->Quit(); + } +#endif mExiting = PR_TRUE; return NS_OK; } diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/xpwidgets/nsBaseAppShell.h firefox-3.6.4+build1+nobinonly/mozilla/widget/src/xpwidgets/nsBaseAppShell.h --- firefox-3.6.3+nobinonly/mozilla/widget/src/xpwidgets/nsBaseAppShell.h 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/xpwidgets/nsBaseAppShell.h 2010-04-16 17:32:51.000000000 +0100 @@ -119,7 +119,7 @@ eEventloopOther // innermost native event loop is a native library/plugin etc }; EventloopNestingState mEventloopNestingState; - PRPackedBool mRunWasCalled; + PRPackedBool mRunning; PRPackedBool mExiting; /** * mBlockNativeEvent blocks the appshell from processing native events. diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/xpwidgets/nsBaseWidget.cpp firefox-3.6.4+build1+nobinonly/mozilla/widget/src/xpwidgets/nsBaseWidget.cpp --- firefox-3.6.3+nobinonly/mozilla/widget/src/xpwidgets/nsBaseWidget.cpp 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/xpwidgets/nsBaseWidget.cpp 2010-04-16 17:32:51.000000000 +0100 @@ -962,6 +962,117 @@ return NS_ERROR_NOT_IMPLEMENTED; } +////////////////////////////////////////////////////////////// +// +// Code to sort rectangles for scrolling. +// +// The algorithm used here is similar to that described at +// http://weblogs.mozillazine.org/roc/archives/2009/08/homework_answer.html +// +////////////////////////////////////////////////////////////// + +void +ScrollRectIterBase::BaseInit(const nsIntPoint& aDelta, ScrollRect* aHead) +{ + mHead = aHead; + // Reflect the coordinate system of the rectangles so that we can assume + // that rectangles are moving in the direction of decreasing x and y. + Flip(aDelta); + + // Do an initial sort of the rectangles by y and then reverse-x. + // nsRegion does not guarantee yx-banded rectangles but still tends to + // prefer breaking up rectangles vertically and joining horizontally, so + // tends to have fewer rectangles across x than down y, making this + // algorithm more efficient for rectangles from nsRegion when y is the + // primary sort parameter. + ScrollRect* unmovedHead; // chain of unmoved rectangles + { + nsTArray array; + for (ScrollRect* r = mHead; r; r = r->mNext) { + array.AppendElement(r); + } + array.Sort(InitialSortComparator()); + + ScrollRect *next = nsnull; + for (PRUint32 i = array.Length(); i--; ) { + array[i]->mNext = next; + next = array[i]; + } + unmovedHead = next; + // mHead becomes the start of the moved chain. + mHead = nsnull; + } + + // Try to move each rect from an unmoved chain to the moved chain. + mTailLink = &mHead; + while (unmovedHead) { + // Move() will check for other rectangles that might need to be moved first + // and move them also. + Move(&unmovedHead); + } + + // Reflect back to the original coordinate system. + Flip(aDelta); +} + +void ScrollRectIterBase::Move(ScrollRect** aUnmovedLink) +{ + ScrollRect* rect = *aUnmovedLink; + // Remove rect from the unmoved chain. + *aUnmovedLink = rect->mNext; + rect->mNext = nsnull; + + // Check subsequent rectangles that overlap vertically to see whether they + // might need to be moved first. + // + // The overlapping subsequent rectangles that are not moved this time get + // checked for each of their preceding unmoved overlapping rectangles, + // which adds an O(n^2) cost to this algorithm (where n is the number of + // rectangles across x). The reverse-x ordering from InitialSortComparator + // avoids this for the case when rectangles are aligned in y. + for (ScrollRect** nextLink = aUnmovedLink; *nextLink; ) { + ScrollRect* otherRect = *nextLink; + NS_ASSERTION(otherRect->y >= rect->y, "Scroll rectangles out of order"); + if (otherRect->y >= rect->YMost()) // doesn't overlap vertically + break; + + // This only moves the other rectangle first if it is entirely to the + // left. No promises are made regarding intersecting rectangles. Moving + // another intersecting rectangle with merely x < rect->x (but XMost() > + // rect->x) can cause more conflicts between rectangles that do not + // intersect each other. + if (otherRect->XMost() <= rect->x) { + Move(nextLink); + // *nextLink now points to a subsequent rectangle. + } else { + // Step over otherRect for now. + nextLink = &otherRect->mNext; + } + } + + // Add rect to the moved chain. + *mTailLink = rect; + mTailLink = &rect->mNext; +} + +BlitRectIter::BlitRectIter(const nsIntPoint& aDelta, + const nsTArray& aRects) + : mRects(aRects.Length()) +{ + for (PRUint32 i = 0; i < aRects.Length(); ++i) { + mRects.AppendElement(aRects[i]); + } + + // Link rectangles into a chain. + ScrollRect *next = nsnull; + for (PRUint32 i = mRects.Length(); i--; ) { + mRects[i].mNext = next; + next = &mRects[i]; + } + + BaseInit(aDelta, next); +} + #ifdef DEBUG ////////////////////////////////////////////////////////////// // diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/src/xpwidgets/nsBaseWidget.h firefox-3.6.4+build1+nobinonly/mozilla/widget/src/xpwidgets/nsBaseWidget.h --- firefox-3.6.3+nobinonly/mozilla/widget/src/xpwidgets/nsBaseWidget.h 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/src/xpwidgets/nsBaseWidget.h 2010-04-16 17:32:51.000000000 +0100 @@ -190,7 +190,7 @@ nsIntRect* mOriginalBounds; // When this pointer is null, the widget is not clipped nsAutoArrayPtr mClipRects; - PRInt32 mClipRectCount; + PRUint32 mClipRectCount; PRInt32 mZIndex; nsSizeMode mSizeMode; @@ -248,4 +248,89 @@ ~nsAutoRollup(); }; +/** + * BlitRectIter and/or ScrollRectIterBase are classes used in + * nsIWidget::Scroll() implementations. They provide sorting of rectangles + * such that copying from rects[i] - aDelta to rects[i] does not alter + * anything in rects[j] for each j > i when rect[i] and rect[j] do not + * intersect each other nor any other rectangle. That is, it is safe to just + * copy non-intersecting rectangles in the order provided. + * + * ScrollRectIterBase is only instantiated within derived classes. It expects + * to be initialized through BaseInit() with a linked list of rectangles. + * + * BlitRectIter provides a simple constructor from an array of nsIntRects. + */ + +class ScrollRectIterBase { +public: + PRBool IsDone() { return mHead == nsnull; } + void operator++() { mHead = mHead->mNext; } + const nsIntRect& Rect() const { return *mHead; } + +protected: + ScrollRectIterBase() {} + + struct ScrollRect : public nsIntRect { + ScrollRect(const nsIntRect& aIntRect) : nsIntRect(aIntRect) {} + + // Flip the coordinate system so that we can assume that the rectangles + // are moving in the direction of decreasing x and y (left and up). + // This function is its own inverse. + void Flip(const nsIntPoint& aDelta) + { + if (aDelta.x > 0) x = -XMost(); + if (aDelta.y > 0) y = -YMost(); + } + + ScrollRect* mNext; + }; + + void BaseInit(const nsIntPoint& aDelta, ScrollRect* aHead); + +private: + void Flip(const nsIntPoint& aDelta) + { + for (ScrollRect* r = mHead; r; r = r->mNext) { + r->Flip(aDelta); + } + } + + /** + * Comparator for an initial sort of the rectangles. The rectangles are + * primarily sorted in increasing y, which is required for the algorithm. + * The secondary sort is in decreasing x, chosen to make Move() more + * efficient for rows of rectangles with equal y. + */ + class InitialSortComparator { + public: + PRBool Equals(const ScrollRect* a, const ScrollRect* b) const + { + return a->y == b->y && a->x == b->x; + } + PRBool LessThan(const ScrollRect* a, const ScrollRect* b) const + { + return a->y < b->y || (a->y == b->y && a->x > b->x); + } + }; + + void Move(ScrollRect** aUnmovedLink); + + // Linked list of rectangles; these are assumed owned by the derived class + ScrollRect* mHead; + // Used in sorting to point to the last mNext link in the moved chain. + ScrollRect** mTailLink; +}; + +class BlitRectIter : public ScrollRectIterBase { +public: + BlitRectIter(const nsIntPoint& aDelta, const nsTArray& aRects); +private: + // Copying is not supported. + BlitRectIter(const BlitRectIter&); + void operator=(const BlitRectIter&); + + nsTArray mRects; +}; + #endif // nsBaseWidget_h__ diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/tests/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/widget/tests/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/widget/tests/Makefile.in 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/tests/Makefile.in 2010-04-16 17:33:04.000000000 +0100 @@ -57,7 +57,9 @@ include $(topsrcdir)/config/rules.mk -_TEST_FILES = test_bug343416.xul \ +_TEST-FILES = + +_CHROME_FILES = test_bug343416.xul \ test_bug429954.xul \ window_bug429954.xul \ test_bug444800.xul \ @@ -72,14 +74,8 @@ test_plugin_scroll_consistency.html \ $(NULL) -ifeq ($(OS_ARCH),WINNT) -_MOCHITEST_FILES += \ - test_windowed_invalidate.html \ - $(NULL) -endif - ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa) -_TEST_FILES += native_menus_window.xul \ +_CHROME_FILES += native_menus_window.xul \ test_native_menus.xul \ test_bug485118.xul \ test_bug428405.xul \ @@ -89,12 +85,23 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),windows) ifneq ($(OS_ARCH), WINCE) -_TEST_FILES += taskbar_previews.xul \ +_CHROME_FILES += taskbar_previews.xul \ window_state_windows.xul \ taskbar_progress.xul \ $(NULL) endif endif +ifeq ($(MOZ_WIDGET_TOOLKIT),gtk2) +_TEST_FILES += plugin_scroll_invalidation.html \ + test_plugin_scroll_invalidation.html \ + $(NULL) +endif + +ifdef _TEST_FILES libs:: $(_TEST_FILES) + $(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) +endif + +libs:: $(_CHROME_FILES) $(INSTALL) $^ $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir) diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/tests/plugin_scroll_invalidation.html firefox-3.6.4+build1+nobinonly/mozilla/widget/tests/plugin_scroll_invalidation.html --- firefox-3.6.3+nobinonly/mozilla/widget/tests/plugin_scroll_invalidation.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/tests/plugin_scroll_invalidation.html 2010-04-16 17:32:52.000000000 +0100 @@ -0,0 +1,60 @@ + + + + Test helper for plugin child widgets not being invalidated by scrolling + + + + + + + + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/widget/tests/test_plugin_scroll_invalidation.html firefox-3.6.4+build1+nobinonly/mozilla/widget/tests/test_plugin_scroll_invalidation.html --- firefox-3.6.3+nobinonly/mozilla/widget/tests/test_plugin_scroll_invalidation.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/widget/tests/test_plugin_scroll_invalidation.html 2010-04-16 17:32:52.000000000 +0100 @@ -0,0 +1,105 @@ + + + + Test for plugin child widgets not being invalidated by scrolling + + + + + +

    + +

    + +
    +
    + + + + diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/analysis/must-override.js firefox-3.6.4+build1+nobinonly/mozilla/xpcom/analysis/must-override.js --- firefox-3.6.3+nobinonly/mozilla/xpcom/analysis/must-override.js 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/analysis/must-override.js 2010-04-16 17:32:52.000000000 +0100 @@ -0,0 +1,48 @@ +/* + * Detect classes that should have overridden members of their parent + * classes but didn't. + * + * Example: + * + * struct S { + * virtual NS_MUST_OVERRIDE void f(); + * virtual void g(); + * }; + * + * struct A : S { virtual void f(); }; // ok + * struct B : S { virtual NS_MUST_OVERRIDE void f(); }; // also ok + * + * struct C : S { virtual void g(); }; // ERROR: must override f() + * struct D : S { virtual void f(int); }; // ERROR: different overload + * struct E : A { }; // ok: A's definition of f() is good for subclasses + * struct F : B { }; // ERROR: B's definition of f() is still must-override + * + * We don't care if you define the method or not. + */ + +function get_must_overrides(cls) +{ + let mos = {}; + for each (let base in cls.bases) + for each (let m in base.type.members) + if (hasAttribute(m, 'NS_must_override')) + mos[m.shortName] = m; + + return mos; +} + +function process_type(t) +{ + if (t.isIncomplete || (t.kind != 'class' && t.kind != 'struct')) + return; + + let mos = get_must_overrides(t); + for each (let m in t.members) { + let mos_m = mos[m.shortName] + if (mos_m && signaturesMatch(mos_m, m)) + delete mos[m.shortName]; + } + + for each (let u in mos) + error(t.kind + " " + t.name + " must override " + u.name, t.loc); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/analysis/override.js firefox-3.6.4+build1+nobinonly/mozilla/xpcom/analysis/override.js --- firefox-3.6.3+nobinonly/mozilla/xpcom/analysis/override.js 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/analysis/override.js 2010-04-16 17:32:52.000000000 +0100 @@ -0,0 +1,42 @@ +/** + * NS_OVERRIDE may be marked on class methods which are intended to override + * a method in a base class. If the method is removed or altered in the base + * class, the compiler will force all subclass overrides to be modified. + */ + +/** + * Generate all the base classes recursively of class `c`. + */ +function all_bases(c) +{ + for each (let b in c.bases) { + yield b.type; + for (let bb in all_bases(b.type)) + yield bb; + } +} + +function process_decl(d) +{ + if (!hasAttribute(d, 'NS_override')) + return; + + if (!d.memberOf || !d.isFunction) { + error("%s is marked NS_OVERRIDE but is not a class function.".format(d.name), d.loc); + return; + } + + if (d.isStatic) { + error("Marking NS_OVERRIDE on static function %s is meaningless.".format(d.name), d.loc); + return; + } + + for (let base in all_bases(d.memberOf)) { + for each (let m in base.members) { + if (m.shortName == d.shortName && signaturesMatch(m, d)) + return; + } + } + + error("NS_OVERRIDE function %s does not override a base class method with the same name and signature".format(d.name), d.loc); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/analysis/type-printer.js firefox-3.6.4+build1+nobinonly/mozilla/xpcom/analysis/type-printer.js --- firefox-3.6.3+nobinonly/mozilla/xpcom/analysis/type-printer.js 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/analysis/type-printer.js 2010-04-16 17:32:52.000000000 +0100 @@ -74,21 +74,6 @@ } } -function signaturesMatch(m1, m2) -{ - let p1 = m1.type.parameters; - let p2 = m2.type.parameters; - - if (p1.length != p2.length) - return false; - - for (let i = 0; i < p1.length; ++i) - if (p1[i] !== p2[i]) - return false; - - return true; -} - /** * Get the short name of a decl name. E.g. turn * "MyNamespace::MyClass::Method(int j) const" into diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/base/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/xpcom/base/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/xpcom/base/Makefile.in 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/base/Makefile.in 2010-04-16 17:32:52.000000000 +0100 @@ -47,6 +47,7 @@ LIBRARY_NAME = xpcombase_s GRE_MODULE = 1 MOZILLA_INTERNAL_API =1 +LIBXUL_LIBRARY = 1 REQUIRES = string \ $(NULL) @@ -154,3 +155,4 @@ CXXFLAGS += $(MOZ_GTK2_CFLAGS) endif +LOCAL_INCLUDES += -I$(srcdir)/../build diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/base/nscore.h firefox-3.6.4+build1+nobinonly/mozilla/xpcom/base/nscore.h --- firefox-3.6.3+nobinonly/mozilla/xpcom/base/nscore.h 2010-04-02 16:59:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/base/nscore.h 2010-04-16 17:32:52.000000000 +0100 @@ -509,11 +509,13 @@ # define NS_INPARAM __attribute__((user("NS_inparam"))) # define NS_OUTPARAM __attribute__((user("NS_outparam"))) # define NS_INOUTPARAM __attribute__((user("NS_inoutparam"))) +# define NS_OVERRIDE __attribute__((user("NS_override"))) #else # define NS_SCRIPTABLE # define NS_INPARAM # define NS_OUTPARAM # define NS_INOUTPARAM +# define NS_OVERRIDE #endif #endif /* nscore_h___ */ diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/base/nsDebugImpl.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpcom/base/nsDebugImpl.cpp --- firefox-3.6.3+nobinonly/mozilla/xpcom/base/nsDebugImpl.cpp 2010-04-02 16:59:19.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/base/nsDebugImpl.cpp 2010-04-16 17:32:52.000000000 +0100 @@ -76,6 +76,9 @@ Abort(const char *aMsg); static void +RealBreak(); + +static void Break(const char *aMsg); #if defined(XP_OS2) @@ -317,6 +320,9 @@ return; case NS_DEBUG_ABORT: +#ifdef DEBUG + RealBreak(); +#endif nsTraceRefcntImpl::WalkTheStack(stderr); Abort(buf.buffer); return; @@ -356,9 +362,18 @@ } static void +TouchBadMemory() +{ + // XXX this should use the frame poisoning code + gAssertionCount += *((PRInt32 *) 0); // TODO annotation saying we know + // this is crazy +} + +static void Abort(const char *aMsg) { #if defined(_WIN32) + TouchBadMemory(); #ifndef WINCE //This should exit us @@ -380,14 +395,31 @@ #endif // Still haven't aborted? Try dereferencing null. - // (Written this way to lessen the likelihood of it being optimized away.) - gAssertionCount += *((PRInt32 *) 0); // TODO annotation saying we know - // this is crazy + TouchBadMemory(); // Still haven't aborted? Try _exit(). PR_ProcessExit(127); } +static void +RealBreak() +{ +#if defined(_WIN32) +#ifndef WINCE + ::DebugBreak(); +#endif +#elif defined(XP_OS2) + asm("int $3"); +#elif defined(XP_BEOS) +#elif defined(XP_MACOSX) + raise(SIGTRAP); +#elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__)) + asm("int $3"); +#else + // don't know how to break on this platform +#endif +} + // Abort() calls this function, don't call it! static void Break(const char *aMsg) @@ -448,8 +480,7 @@ } } - ::DebugBreak(); - + RealBreak(); #endif // WINCE #elif defined(XP_OS2) char msg[1200]; @@ -472,17 +503,18 @@ if (( code == MBID_ENTER ) || (code == MBID_ERROR)) return; - asm("int $3"); + RealBreak(); #elif defined(XP_BEOS) DEBUGGER(aMsg); + RealBreak(); #elif defined(XP_MACOSX) /* Note that we put this Mac OS X test above the GNUC/x86 test because the * GNUC/x86 test is also true on Intel Mac OS X and we want the PPC/x86 * impls to be the same. */ - raise(SIGTRAP); + RealBreak(); #elif defined(__GNUC__) && (defined(__i386__) || defined(__i386) || defined(__x86_64__)) - asm("int $3"); + RealBreak(); #else // don't know how to break on this platform #endif diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/base/nsTraceRefcntImpl.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpcom/base/nsTraceRefcntImpl.cpp --- firefox-3.6.3+nobinonly/mozilla/xpcom/base/nsTraceRefcntImpl.cpp 2010-04-02 16:59:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/base/nsTraceRefcntImpl.cpp 2010-04-16 17:32:52.000000000 +0100 @@ -37,6 +37,7 @@ * ***** END LICENSE BLOCK ***** */ #include "nsTraceRefcntImpl.h" +#include "nsXPCOMPrivate.h" #include "nscore.h" #include "nsISupports.h" #include "nsTArray.h" @@ -49,6 +50,19 @@ #include "nsCRT.h" #include #include "nsStackWalk.h" +#include "nsString.h" + +#ifdef MOZ_IPC +#include "nsXULAppAPI.h" +#ifdef XP_WIN +#include +#define getpid _getpid +#else +#include +#endif +#endif + +#include "mozilla/BlockingResourceBase.h" #ifdef HAVE_LIBDL #include @@ -318,8 +332,12 @@ } PRBool PrintDumpHeader(FILE* out, const char* msg, nsTraceRefcntImpl::StatisticsType type) { +#ifdef MOZ_IPC + fprintf(out, "\n== BloatView: %s, %s process %d\n", msg, + XRE_ChildProcessTypeToString(XRE_GetProcessType()), getpid()); +#else fprintf(out, "\n== BloatView: %s\n", msg); - +#endif nsTraceRefcntStats& stats = (type == nsTraceRefcntImpl::NEW_STATS) ? mNewStats : mAllStats; if (gLogLeaksOnly && !HaveLeaks(&stats)) @@ -617,6 +635,12 @@ return nsnull != PL_HashTableLookup(gObjectsToLog, (const void*)(aSerialNumber)); } +#ifdef XP_WIN +#define FOPEN_NO_INHERIT "N" +#else +#define FOPEN_NO_INHERIT +#endif + static PRBool InitLog(const char* envVar, const char* msg, FILE* *result) { const char* value = getenv(envVar); @@ -634,18 +658,33 @@ return PR_TRUE; } else { - FILE *stream = ::fopen(value, "w"); + FILE *stream; + nsCAutoString fname(value); +#ifdef MOZ_IPC + if (XRE_GetProcessType() != GeckoProcessType_Default) { + bool hasLogExtension = + fname.RFind(".log", PR_TRUE, -1, 4) == kNotFound ? false : true; + if (hasLogExtension) + fname.Cut(fname.Length() - 4, 4); + fname.AppendLiteral("_"); + fname.Append((char*)XRE_ChildProcessTypeToString(XRE_GetProcessType())); + fname.AppendLiteral("_pid"); + fname.AppendInt((PRUint32)getpid()); + if (hasLogExtension) + fname.AppendLiteral(".log"); + } +#endif + stream = ::fopen(fname.get(), "w" FOPEN_NO_INHERIT); if (stream != NULL) { *result = stream; fprintf(stdout, "### %s defined -- logging %s to %s\n", - envVar, msg, value); - return PR_TRUE; + envVar, msg, fname.get()); } else { fprintf(stdout, "### %s defined -- unable to log %s to %s\n", - envVar, msg, value); - return PR_FALSE; + envVar, msg, fname.get()); } + return stream != NULL; } } return PR_FALSE; @@ -891,10 +930,33 @@ EXPORT_XPCOM_API(void) NS_LogTerm() { + mozilla::LogTerm(); +} + +namespace mozilla { +void +LogTerm() +{ NS_ASSERTION(gInitCount > 0, "NS_LogTerm without matching NS_LogInit"); if (--gInitCount == 0) { +#ifdef DEBUG + /* FIXME bug 491977: This is only going to operate on the + * BlockingResourceBase which is compiled into + * libxul/libxpcom_core.so. Anyone using external linkage will + * have their own copy of BlockingResourceBase statics which will + * not be freed by this method. + * + * It sounds like what we really want is to be able to register a + * callback function to call at XPCOM shutdown. Note that with + * this solution, however, we need to guarantee that + * BlockingResourceBase::Shutdown() runs after all other shutdown + * functions. + */ + BlockingResourceBase::Shutdown(); +#endif + if (gInitialized) { nsTraceRefcntImpl::DumpStatistics(); nsTraceRefcntImpl::ResetStatistics(); @@ -907,6 +969,8 @@ } } +} // namespace mozilla + EXPORT_XPCOM_API(void) NS_LogAddRef(void* aPtr, nsrefcnt aRefcnt, const char* aClazz, PRUint32 classSize) diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/build/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/xpcom/build/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/xpcom/build/Makefile.in 2010-04-02 16:59:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/build/Makefile.in 2010-04-16 17:32:52.000000000 +0100 @@ -160,6 +160,8 @@ GARBAGE += $(XPCOM_GLUE_SRC_LCSRCS) $(XPCOM_GLUE_SRC_LCPPSRCS) $(XPCOM_GLUENS_SRC_LCPPSRCS) $(wildcard *.$(OBJ_SUFFIX)) +include $(topsrcdir)/config/config.mk +include $(topsrcdir)/ipc/chromium/chromium-config.mk include $(topsrcdir)/config/rules.mk DEFINES += \ diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/build/nsXPComInit.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpcom/build/nsXPComInit.cpp --- firefox-3.6.3+nobinonly/mozilla/xpcom/build/nsXPComInit.cpp 2010-04-02 16:59:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/build/nsXPComInit.cpp 2010-04-16 17:32:52.000000000 +0100 @@ -37,7 +37,12 @@ * * ***** END LICENSE BLOCK ***** */ +#ifdef MOZ_IPC +#include "base/basictypes.h" +#endif + #include "mozilla/XPCOM.h" +#include "nsXULAppAPI.h" #include "nsXPCOMPrivate.h" #include "nsXPCOMCIDInternal.h" @@ -75,10 +80,6 @@ #include "nsThreadManager.h" #include "nsThreadPool.h" -#ifdef DEBUG -#include "BlockingResourceBase.h" -#endif // ifdef DEBUG - #include "nsIProxyObjectManager.h" #include "nsProxyEventPrivate.h" // access to the impl of nsProxyObjectManager for the generic factory registration. @@ -145,6 +146,26 @@ #include +#ifdef MOZ_IPC +#include "base/at_exit.h" +#include "base/command_line.h" +#include "base/message_loop.h" + +#include "mozilla/ipc/BrowserProcessSubThread.h" + +using base::AtExitManager; +using mozilla::ipc::BrowserProcessSubThread; + +namespace { + +static AtExitManager* sExitManager; +static MessageLoop* sMessageLoop; +static bool sCommandLineWasInitialized; +static BrowserProcessSubThread* sIOThread; + +} /* anonymous namespace */ +#endif + using mozilla::TimeStamp; // Registry Factory creation function defined in nsRegistry.cpp @@ -549,6 +570,34 @@ // We are not shutting down gXPCOMShuttingDown = PR_FALSE; +#ifdef MOZ_IPC + // Set up chromium libs + NS_ASSERTION(!sExitManager && !sMessageLoop, "Bad logic!"); + + if (!AtExitManager::AlreadyRegistered()) { + sExitManager = new AtExitManager(); + NS_ENSURE_STATE(sExitManager); + } + + if (!MessageLoop::current()) { + sMessageLoop = new MessageLoopForUI(MessageLoop::TYPE_MOZILLA_UI); + NS_ENSURE_STATE(sMessageLoop); + } + + if (XRE_GetProcessType() == GeckoProcessType_Default && + !BrowserProcessSubThread::GetMessageLoop(BrowserProcessSubThread::IO)) { + scoped_ptr ioThread( + new BrowserProcessSubThread(BrowserProcessSubThread::IO)); + NS_ENSURE_TRUE(ioThread.get(), NS_ERROR_OUT_OF_MEMORY); + + base::Thread::Options options; + options.message_loop_type = MessageLoop::TYPE_IO; + NS_ENSURE_TRUE(ioThread->StartWithOptions(options), NS_ERROR_FAILURE); + + sIOThread = ioThread.release(); + } +#endif + NS_LogInit(); // Set up TimeStamp @@ -609,6 +658,30 @@ if (NS_FAILED(rv)) return rv; } +#ifdef MOZ_IPC + if ((sCommandLineWasInitialized = !CommandLine::IsInitialized())) { +#ifdef OS_WIN + CommandLine::Init(0, nsnull); +#else + nsCOMPtr binaryFile; + nsDirectoryService::gService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, + NS_GET_IID(nsIFile), + getter_AddRefs(binaryFile)); + NS_ENSURE_STATE(binaryFile); + + rv = binaryFile->AppendNative(NS_LITERAL_CSTRING("nonexistent-executable")); + NS_ENSURE_SUCCESS(rv, rv); + + nsCString binaryPath; + rv = binaryFile->GetNativePath(binaryPath); + NS_ENSURE_SUCCESS(rv, rv); + + static char const *const argv = { strdup(binaryPath.get()) }; + CommandLine::Init(1, &argv); +#endif + } +#endif + NS_ASSERTION(nsComponentManagerImpl::gComponentManager == NULL, "CompMgr not null at init"); // Create the Component/Service Manager @@ -899,24 +972,27 @@ TimeStamp::Shutdown(); -#ifdef DEBUG - /* FIXME bug 491977: This is only going to operate on the - * BlockingResourceBase which is compiled into - * libxul/libxpcom_core.so. Anyone using external linkage will - * have their own copy of BlockingResourceBase statics which will - * not be freed by this method. - * - * It sounds like what we really want is to be able to register a - * callback function to call at XPCOM shutdown. Note that with - * this solution, however, we need to guarantee that - * BlockingResourceBase::Shutdown() runs after all other shutdown - * functions. - */ - BlockingResourceBase::Shutdown(); -#endif - NS_LogTerm(); +#ifdef MOZ_IPC + if (sIOThread) { + delete sIOThread; + sIOThread = nsnull; + } + if (sMessageLoop) { + delete sMessageLoop; + sMessageLoop = nsnull; + } + if (sCommandLineWasInitialized) { + CommandLine::Terminate(); + sCommandLineWasInitialized = false; + } + if (sExitManager) { + delete sExitManager; + sExitManager = nsnull; + } +#endif + #ifdef GC_LEAK_DETECTOR // Shutdown the Leak detector. NS_ShutdownLeakDetector(); diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/build/nsXPCOMPrivate.h firefox-3.6.4+build1+nobinonly/mozilla/xpcom/build/nsXPCOMPrivate.h --- firefox-3.6.3+nobinonly/mozilla/xpcom/build/nsXPCOMPrivate.h 2010-04-02 16:59:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/build/nsXPCOMPrivate.h 2010-04-16 17:32:52.000000000 +0100 @@ -224,6 +224,11 @@ nsresult ShutdownXPCOM(nsIServiceManager* servMgr); +/** + * C++ namespaced version of NS_LogTerm. + */ +void LogTerm(); + } // namespace mozilla diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/build/nsXULAppAPI.h firefox-3.6.4+build1+nobinonly/mozilla/xpcom/build/nsXULAppAPI.h --- firefox-3.6.3+nobinonly/mozilla/xpcom/build/nsXULAppAPI.h 2010-04-02 16:59:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/build/nsXULAppAPI.h 2010-04-16 17:32:52.000000000 +0100 @@ -45,6 +45,7 @@ #include "xrecore.h" #include "nsXPCOM.h" #include "nsISupports.h" +#include "prlog.h" /** * Application-specific data needed to start the apprunner. @@ -418,4 +419,93 @@ XRE_API(void, XRE_FreeAppData, (nsXREAppData *aAppData)) +enum GeckoProcessType { + GeckoProcessType_Default = 0, + + GeckoProcessType_Plugin, + GeckoProcessType_Content, + + GeckoProcessType_IPDLUnitTest, + + GeckoProcessType_End, + GeckoProcessType_Invalid = GeckoProcessType_End +}; + +static const char* const kGeckoProcessTypeString[] = { + "default", + "plugin", + "tab", + "ipdlunittest" +}; + +PR_STATIC_ASSERT(sizeof(kGeckoProcessTypeString) / + sizeof(kGeckoProcessTypeString[0]) == + GeckoProcessType_End); + + +XRE_API(const char*, + XRE_ChildProcessTypeToString, (GeckoProcessType aProcessType)) + +XRE_API(GeckoProcessType, + XRE_StringToChildProcessType, (const char* aProcessTypeString)) + +#if defined(MOZ_CRASHREPORTER) +// Used in the "master" parent process hosting the crash server +XRE_API(PRBool, + XRE_TakeMinidumpForChild, (PRUint32 aChildPid, nsILocalFile** aDump)) + +// Used in child processes. +XRE_API(PRBool, + XRE_SetRemoteExceptionHandler, (const char* aPipe)) +#endif + +XRE_API(nsresult, + XRE_InitChildProcess, (int aArgc, + char* aArgv[], + GeckoProcessType aProcess)) + +XRE_API(GeckoProcessType, + XRE_GetProcessType, ()) + +typedef void (*MainFunction)(void* aData); + +XRE_API(nsresult, + XRE_InitParentProcess, (int aArgc, + char* aArgv[], + MainFunction aMainFunction, + void* aMainFunctionExtraData)) + +XRE_API(int, + XRE_RunIPDLTest, (int aArgc, + char* aArgv[])) + +XRE_API(nsresult, + XRE_RunAppShell, ()) + +XRE_API(nsresult, + XRE_InitCommandLine, (int aArgc, char* aArgv[])) + +XRE_API(nsresult, + XRE_DeinitCommandLine, ()) + +class MessageLoop; + +XRE_API(void, + XRE_ShutdownChildProcess, ()) + +XRE_API(MessageLoop*, + XRE_GetIOMessageLoop, ()) + +struct JSContext; +struct JSString; + +XRE_API(bool, + XRE_SendTestShellCommand, (JSContext* aCx, + JSString* aCommand, + void* aCallback)) +XRE_API(bool, + XRE_ShutdownTestShell, ()) + +XRE_API(void, + XRE_InstallX11ErrorHandler, ()) #endif // _nsXULAppAPI_h__ diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/build.mk firefox-3.6.4+build1+nobinonly/mozilla/xpcom/build.mk --- firefox-3.6.3+nobinonly/mozilla/xpcom/build.mk 2010-04-02 16:59:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/build.mk 2010-04-16 17:32:52.000000000 +0100 @@ -42,3 +42,7 @@ endif tier_xpcom_dirs += xpcom + +ifdef MOZ_IPC +tier_xpcom_dirs += ipc/chromium ipc/glue +endif diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/ds/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/xpcom/ds/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/xpcom/ds/Makefile.in 2010-04-02 16:59:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/ds/Makefile.in 2010-04-16 17:32:52.000000000 +0100 @@ -152,6 +152,10 @@ EXPORTS += nsWindowsRegKey.h endif +EXTRA_COMPONENTS = \ + nsINIProcessor.js \ + $(NULL) + # we don't want the shared lib, but we want to force the creation of a static lib. FORCE_STATIC_LIB = 1 diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/ds/nsIINIParser.idl firefox-3.6.4+build1+nobinonly/mozilla/xpcom/ds/nsIINIParser.idl --- firefox-3.6.3+nobinonly/mozilla/xpcom/ds/nsIINIParser.idl 2010-04-02 16:59:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/ds/nsIINIParser.idl 2010-04-16 17:32:52.000000000 +0100 @@ -59,6 +59,20 @@ AUTF8String getString(in AUTF8String aSection, in AUTF8String aKey); }; +[scriptable, uuid(712dc5da-8d09-45d0-ba2e-de27eb384c4c)] +interface nsIINIParserWriter : nsISupports +{ + /** + * Set the value of a string for a particular section and key. + */ + void setString(in AUTF8String aSection, in AUTF8String aKey, in AUTF8String aValue); + + /** + * Write to the INI file. + */ + void writeFile([optional] in nsILocalFile aINIFile); +}; + [scriptable, uuid(ccae7ea5-1218-4b51-aecb-c2d8ecd46af9)] interface nsIINIParserFactory : nsISupports { diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/ds/nsINIProcessor.js firefox-3.6.4+build1+nobinonly/mozilla/xpcom/ds/nsINIProcessor.js --- firefox-3.6.3+nobinonly/mozilla/xpcom/ds/nsINIProcessor.js 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/ds/nsINIProcessor.js 2010-04-16 17:32:52.000000000 +0100 @@ -0,0 +1,209 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Justin Dolske (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cr = Components.results; +const Cu = Components.utils; + +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); + +function INIProcessorFactory() { +} + +INIProcessorFactory.prototype = { + classDescription: "INIProcessorFactory", + contractID: "@mozilla.org/xpcom/ini-processor-factory;1", + classID: Components.ID("{6ec5f479-8e13-4403-b6ca-fe4c2dca14fd}"), + QueryInterface : XPCOMUtils.generateQI([Ci.nsIINIParserFactory]), + + createINIParser : function (aINIFile) { + return new INIProcessor(aINIFile); + } + +}; // end of INIProcessorFactory implementation + +const MODE_WRONLY = 0x02; +const MODE_CREATE = 0x08; +const MODE_TRUNCATE = 0x20; + +// nsIINIParser implementation +function INIProcessor(aFile) { + this._iniFile = aFile; + this._iniData = {}; + this._readFile(); +} + +INIProcessor.prototype = { + QueryInterface : XPCOMUtils.generateQI([Ci.nsIINIParser, Ci.nsIINIParserWriter]), + + __utfConverter : null, // UCS2 <--> UTF8 string conversion + get _utfConverter() { + if (!this.__utfConverter) { + this.__utfConverter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]. + createInstance(Ci.nsIScriptableUnicodeConverter); + this.__utfConverter.charset = "UTF-8"; + } + return this.__utfConverter; + }, + + _utfConverterReset : function() { + this.__utfConverter = null; + }, + + _iniFile : null, + _iniData : null, + + /* + * Reads the INI file and stores the data internally. + */ + _readFile : function() { + // If file doesn't exist, there's nothing to do. + if (!this._iniFile.exists() || 0 == this._iniFile.fileSize) + return; + + let iniParser = Cc["@mozilla.org/xpcom/ini-parser-factory;1"] + .getService(Ci.nsIINIParserFactory).createINIParser(this._iniFile); + for (let section in XPCOMUtils.IterStringEnumerator(iniParser.getSections())) { + this._iniData[section] = {}; + for (let key in XPCOMUtils.IterStringEnumerator(iniParser.getKeys(section))) { + this._iniData[section][key] = iniParser.getString(section, key); + } + } + }, + + // nsIINIParser + + getSections : function() { + let sections = []; + for (let section in this._iniData) + sections.push(section); + return new stringEnumerator(sections); + }, + + getKeys : function(aSection) { + let keys = []; + if (aSection in this._iniData) + for (let key in this._iniData[aSection]) + keys.push(key); + return new stringEnumerator(keys); + }, + + getString : function(aSection, aKey) { + if (!(aSection in this._iniData)) + throw Cr.NS_ERROR_FAILURE; + if (!(aKey in this._iniData[aSection])) + throw Cr.NS_ERROR_FAILURE; + return this._iniData[aSection][aKey]; + }, + + + // nsIINIParserWriter + + setString : function(aSection, aKey, aValue) { + const isSectionIllegal = /[\0\r\n\[\]]/; + const isKeyValIllegal = /[\0\r\n=]/; + + if (isSectionIllegal.test(aSection)) + throw Components.Exception("bad character in section name", + Cr.ERROR_ILLEGAL_VALUE); + if (isKeyValIllegal.test(aKey) || isKeyValIllegal.test(aValue)) + throw Components.Exception("bad character in key/value", + Cr.ERROR_ILLEGAL_VALUE); + + if (!(aSection in this._iniData)) + this._iniData[aSection] = {}; + + this._iniData[aSection][aKey] = aValue; + }, + + writeFile : function(aFile) { + + let converter = this._utfConverter; + function writeLine(data) { + data = converter.ConvertFromUnicode(data); + data += converter.Finish(); + data += "\n"; + outputStream.write(data, data.length); + } + + if (!aFile) + aFile = this._iniFile; + + let safeStream = Cc["@mozilla.org/network/safe-file-output-stream;1"]. + createInstance(Ci.nsIFileOutputStream); + safeStream.init(aFile, MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE, + 0600, null); + + var outputStream = Cc["@mozilla.org/network/buffered-output-stream;1"]. + createInstance(Ci.nsIBufferedOutputStream); + outputStream.init(safeStream, 8192); + outputStream.QueryInterface(Ci.nsISafeOutputStream); // for .finish() + + for (let section in this._iniData) { + writeLine("[" + section + "]"); + for (let key in this._iniData[section]) { + writeLine(key + "=" + this._iniData[section][key]); + } + } + + outputStream.finish(); + } +}; + +function stringEnumerator(stringArray) { + this._strings = stringArray; +} +stringEnumerator.prototype = { + QueryInterface : XPCOMUtils.generateQI([Ci.nsIUTF8StringEnumerator]), + + _strings : null, + _enumIndex: 0, + + hasMore : function() { + return (this._enumIndex < this._strings.length); + }, + + getNext : function() { + return this._strings[this._enumIndex++]; + } +}; + +let component = [INIProcessorFactory]; +function NSGetModule (compMgr, fileSpec) { + return XPCOMUtils.generateModule(component); +} diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/glue/BlockingResourceBase.h firefox-3.6.4+build1+nobinonly/mozilla/xpcom/glue/BlockingResourceBase.h --- firefox-3.6.3+nobinonly/mozilla/xpcom/glue/BlockingResourceBase.h 2010-04-02 16:59:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/glue/BlockingResourceBase.h 2010-04-16 17:32:52.000000000 +0100 @@ -55,6 +55,7 @@ #include "nsStringGlue.h" #include "mozilla/DeadlockDetector.h" +#include "nsXPCOM.h" #endif // @@ -355,7 +356,7 @@ # ifdef MOZILLA_INTERNAL_API // so it can call BlockingResourceBase::Shutdown() - friend nsresult ShutdownXPCOM(nsIServiceManager*); + friend void LogTerm(); # endif // ifdef MOZILLA_INTERNAL_API #else // non-DEBUG implementation diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/glue/nsINIParser.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpcom/glue/nsINIParser.cpp --- firefox-3.6.3+nobinonly/mozilla/xpcom/glue/nsINIParser.cpp 2010-04-02 16:59:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/glue/nsINIParser.cpp 2010-04-16 17:32:52.000000000 +0100 @@ -163,7 +163,6 @@ char *buffer = mFileContents; char *currSection = nsnull; - INIValue *last = nsnull; // outer loop tokenizes into lines while (char *token = NS_strtok(kNL, &buffer)) { @@ -177,7 +176,6 @@ if (token[0] == '[') { // section header! ++token; currSection = token; - last = nsnull; char *rb = NS_strtok(kRBracket, &token); if (!rb || NS_strtok(kWhitespace, &token)) { @@ -199,31 +197,35 @@ char *key = token; char *e = NS_strtok(kEquals, &token); - if (!e) + if (!e || !token) continue; - INIValue *val = new INIValue(key, token); - if (!val) - return NS_ERROR_OUT_OF_MEMORY; + INIValue *v; + if (!mSections.Get(currSection, &v)) { + v = new INIValue(key, token); + if (!v) + return NS_ERROR_OUT_OF_MEMORY; - // If we haven't already added something to this section, "last" will - // be null. - if (!last) { - mSections.Get(currSection, &last); - while (last && last->next) - last = last->next; - } - - if (last) { - // Add this element on to the tail of the existing list - - last->next = val; - last = val; + mSections.Put(currSection, v); continue; } - // We've never encountered this section before, add it to the head - mSections.Put(currSection, val); + // Check whether this key has already been specified; overwrite + // if so, or append if not. + while (v) { + if (!strcmp(key, v->key)) { + v->value = token; + break; + } + if (!v->next) { + v->next = new INIValue(key, token); + if (!v->next) + return NS_ERROR_OUT_OF_MEMORY; + break; + } + v = v->next; + } + NS_ASSERTION(v, "v should never be null coming out of this loop"); } return NS_OK; diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/io/nsLocalFileUnix.h firefox-3.6.4+build1+nobinonly/mozilla/xpcom/io/nsLocalFileUnix.h --- firefox-3.6.3+nobinonly/mozilla/xpcom/io/nsLocalFileUnix.h 2010-04-02 16:59:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/io/nsLocalFileUnix.h 2010-04-16 17:32:52.000000000 +0100 @@ -87,6 +87,11 @@ #endif #if defined(HAVE_STAT64) && defined(HAVE_LSTAT64) + #if defined (AIX) + #if defined STAT + #undef STAT + #endif + #endif #define STAT stat64 #define LSTAT lstat64 #define HAVE_STATS64 1 diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/system/nsICrashReporter.idl firefox-3.6.4+build1+nobinonly/mozilla/xpcom/system/nsICrashReporter.idl --- firefox-3.6.3+nobinonly/mozilla/xpcom/system/nsICrashReporter.idl 2010-04-02 16:59:21.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/system/nsICrashReporter.idl 2010-04-16 17:32:53.000000000 +0100 @@ -118,3 +118,12 @@ */ [noscript] void appendObjCExceptionInfoToAppNotes(in voidPtr aException); }; + +[scriptable, uuid(e8fe590f-7c08-4128-a746-57eb6b427d8f)] +interface nsICrashReporter_MOZILLA_1_9_2_BRANCH : nsICrashReporter +{ + /** + * User preference for submitting crash reports. + */ + attribute boolean submitReports; +}; diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/Makefile.in 2010-04-02 16:59:21.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/Makefile.in 2010-04-16 17:32:53.000000000 +0100 @@ -112,9 +112,34 @@ FLOW_FAILURE_TESTCASES = \ flow_through_fail.cpp +MUST_OVERRIDE_PASS_TESTCASES = \ + OverrideOK1.cpp \ + OverrideOK2.cpp \ + $(NULL) + +MUST_OVERRIDE_FAILURE_TESTCASES = \ + OverrideFail1.cpp \ + OverrideFail2.cpp \ + OverrideFail3.cpp \ + OverrideFail4.cpp \ + $(NULL) + +OVERRIDE_PASS_TESTCASES = \ + override-pass.cpp \ + $(NULL) + +OVERRIDE_FAILURE_TESTCASES = \ + override-global.cpp \ + override-signature.cpp \ + override-static.cpp \ + override-virtual.cpp \ + $(NULL) + STATIC_FAILURE_TESTCASES = \ $(FINAL_FAILURE_TESTCASES) \ - $(FLOW_FAILURE_TESTCASES) + $(FLOW_FAILURE_TESTCASES) \ + $(MUST_OVERRIDE_FAILURE_TESTCASES) \ + $(OVERRIDE_FAILURE_TESTCASES) \ $(NULL) STATIC_WARNING_TESTCASES = \ @@ -127,6 +152,8 @@ $(OUTPARAMS_PASS_TESTCASES) \ $(STACK_PASS_TESTCASES) \ $(FLOW_PASS_TESTCASES) \ + $(MUST_OVERRIDE_PASS_TESTCASES) \ + $(OVERRIDE_PASS_TESTCASES) \ $(NULL) REQUIRES = xpcom diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/OverrideFail1.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/OverrideFail1.cpp --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/OverrideFail1.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/OverrideFail1.cpp 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,7 @@ +#include "nscore.h" + +struct S { + virtual NS_MUST_OVERRIDE void f(); + virtual void g(); +}; +struct C : S { virtual void g(); }; // ERROR: must override f() diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/OverrideFail2.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/OverrideFail2.cpp --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/OverrideFail2.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/OverrideFail2.cpp 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,8 @@ +#include "nscore.h" + +struct S { + virtual NS_MUST_OVERRIDE void f(); + virtual void g(); +}; + +struct D : S { virtual void f(int); }; // ERROR: different overload diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/OverrideFail3.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/OverrideFail3.cpp --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/OverrideFail3.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/OverrideFail3.cpp 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,10 @@ +#include "nscore.h" + +struct S { + virtual NS_MUST_OVERRIDE void f(); + virtual void g(); +}; + +struct B : S { virtual NS_MUST_OVERRIDE void f(); }; // also ok +struct F : B { }; // ERROR: B's definition of f() is still must-override + diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/OverrideFail4.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/OverrideFail4.cpp --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/OverrideFail4.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/OverrideFail4.cpp 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,13 @@ +#include "nscore.h" + +struct Base { + NS_MUST_OVERRIDE void f(); +}; + +struct Intermediate : Base { + NS_MUST_OVERRIDE void f(); +}; + +struct Derived : Intermediate { + // error: must override Intermediate's f() +}; diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/override-global.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/override-global.cpp --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/override-global.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/override-global.cpp 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1 @@ +__attribute__((user("NS_override"))) int m(); diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/OverrideOK1.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/OverrideOK1.cpp --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/OverrideOK1.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/OverrideOK1.cpp 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,10 @@ +#include "nscore.h" + +struct S { + virtual NS_MUST_OVERRIDE void f(); + virtual void g(); +}; + +struct A : S { virtual void f(); }; // ok +struct B : S { virtual NS_MUST_OVERRIDE void f(); }; // also ok +struct E : A { }; // ok: A's definition of f() is good for subclasses diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/OverrideOK2.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/OverrideOK2.cpp --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/OverrideOK2.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/OverrideOK2.cpp 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,24 @@ +#include "nscore.h" + +struct Base { + NS_MUST_OVERRIDE virtual void f(); // normal case + NS_MUST_OVERRIDE void g(); // virtual not required + NS_MUST_OVERRIDE static void h(); // can even be static +}; + +void Base::f() {} // can be defined, or not, don't care + +struct Derived1 : Base { // propagates override annotation + NS_MUST_OVERRIDE virtual void f(); + NS_MUST_OVERRIDE void g(); + NS_MUST_OVERRIDE static void h(); +}; + +struct Derived2 : Derived1 { // doesn't propagate override annotation + virtual void f(); + void g(); + static void h(); +}; + +struct Derived3 : Derived2 { // doesn't have to override anything +}; diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/override-pass.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/override-pass.cpp --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/override-pass.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/override-pass.cpp 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,15 @@ +class A +{ + int a(int, char*); + void c(char); +}; + +class B : A +{ + __attribute__((user("NS_override"))) int a(int, char*); +}; + +class C : B +{ + __attribute__((user("NS_override"))) void c(char); +}; diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/override-signature.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/override-signature.cpp --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/override-signature.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/override-signature.cpp 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,9 @@ +class A +{ + int m(int); +}; + +class B : A +{ + __attribute__((user("NS_override"))) int m(void*); +}; diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/override-static.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/override-static.cpp --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/override-static.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/override-static.cpp 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,9 @@ +class A +{ + static int m(); +}; + +class B : A +{ + __attribute__((user("NS_override"))) static int m(); +}; diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/override-virtual.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/override-virtual.cpp --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/static-checker/override-virtual.cpp 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/static-checker/override-virtual.cpp 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,9 @@ +class A +{ + int m(); +}; + +class B : A +{ + __attribute__((user("NS_override"))) virtual int m(); +}; diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser02.ini firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser02.ini --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser02.ini 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser02.ini 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1 @@ + diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser03.ini firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser03.ini --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser03.ini 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser03.ini 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1 @@ +[] diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser04.ini firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser04.ini --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser04.ini 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser04.ini 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1 @@ +[section1] diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser05.ini firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser05.ini --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser05.ini 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser05.ini 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1 @@ +[section1]junk diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser06.ini firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser06.ini --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser06.ini 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser06.ini 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,2 @@ +[section1] + diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser07.ini firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser07.ini --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser07.ini 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser07.ini 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,2 @@ +[section1] +name1 diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser08.ini firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser08.ini --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser08.ini 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser08.ini 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,2 @@ +[section1] +name1= diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser09.ini firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser09.ini --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser09.ini 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser09.ini 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,2 @@ +[section1] +name1=value1 diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser10.ini firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser10.ini --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser10.ini 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser10.ini 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,3 @@ + +[section1] +name1=value1 diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser11.ini firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser11.ini --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser11.ini 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser11.ini 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,3 @@ +# comment +[section1] +name1=value1 diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser12.ini firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser12.ini --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser12.ini 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser12.ini 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,3 @@ +[section1] +# [sectionBAD] +name1=value1 diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser13.ini firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser13.ini --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser13.ini 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser13.ini 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,3 @@ +[section1] +name1=value1 +# nameBAD=valueBAD diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser14.ini firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser14.ini --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser14.ini 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser14.ini 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,6 @@ +[section1] +name1=value1 +name2=value2 +[section2] +name1=value1 +name2=foopy diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser15.ini firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser15.ini --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/data/iniparser15.ini 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/data/iniparser15.ini 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,6 @@ +[section1] +name1=value1 +[section2] +name1=foopy +[section1] +name1=newValue1 diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/test_iniProcessor.js firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/test_iniProcessor.js --- firefox-3.6.3+nobinonly/mozilla/xpcom/tests/unit/test_iniProcessor.js 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/tests/unit/test_iniProcessor.js 2010-04-16 17:32:53.000000000 +0100 @@ -0,0 +1,211 @@ +const Ci = Components.interfaces; +const Cc = Components.classes; +const Cr = Components.results; + +let testnum = 0; +let factory; + +function parserForFile(filename) { + let parser = null; + try { + let file = do_get_file(filename); + do_check_true(!!file); + parser = factory.createINIParser(file); + do_check_true(!!parser); + } catch(e) { + dump("INFO | caught error: " + e); + // checkParserOutput will handle a null parser when it's expected. + } + return parser; + +} + +function checkParserOutput(parser, expected) { + // If the expected output is null, we expect the parser to have + // failed (and vice-versa). + if (!parser || !expected) { + do_check_eq(parser, null); + do_check_eq(expected, null); + return; + } + + let output = getParserOutput(parser); + for (let section in expected) { + do_check_true(section in output); + for (let key in expected[section]) { + do_check_true(key in output[section]); + do_check_eq(output[section][key], expected[section][key]); + delete output[section][key]; + } + for (let key in output[section]) + do_check_eq(key, "wasn't expecting this key!"); + delete output[section]; + } + for (let section in output) + do_check_eq(section, "wasn't expecting this section!"); +} + +function getParserOutput(parser) { + let output = {}; + + let sections = parser.getSections(); + do_check_true(!!sections); + while (sections.hasMore()) { + let section = sections.getNext(); + do_check_false(section in output); // catch dupes + output[section] = {}; + + let keys = parser.getKeys(section); + do_check_true(!!keys); + while (keys.hasMore()) { + let key = keys.getNext(); + do_check_false(key in output[section]); // catch dupes + let value = parser.getString(section, key); + output[section][key] = value; + } + } + return output; +} + +function run_test() { +try { + +let testdata = [ + { filename: "data/iniparser01.ini", reference: {} }, + { filename: "data/iniparser02.ini", reference: {} }, + { filename: "data/iniparser03.ini", reference: {} }, + { filename: "data/iniparser04.ini", reference: {} }, + { filename: "data/iniparser05.ini", reference: {} }, + { filename: "data/iniparser06.ini", reference: {} }, + { filename: "data/iniparser07.ini", reference: {} }, + { filename: "data/iniparser08.ini", reference: { section1: { name1: "" }} }, + { filename: "data/iniparser09.ini", reference: { section1: { name1: "value1" } } }, + { filename: "data/iniparser10.ini", reference: { section1: { name1: "value1" } } }, + { filename: "data/iniparser11.ini", reference: { section1: { name1: "value1" } } }, + { filename: "data/iniparser12.ini", reference: { section1: { name1: "value1" } } }, + { filename: "data/iniparser13.ini", reference: { section1: { name1: "value1" } } }, + { filename: "data/iniparser14.ini", reference: + { section1: { name1: "value1", name2: "value2" }, + section2: { name1: "value1", name2: "foopy" }} }, + { filename: "data/iniparser15.ini", reference: + { section1: { name1: "newValue1" }, + section2: { name1: "foopy" }} }, + ]; + +/* ========== 0 ========== */ +factory = Cc["@mozilla.org/xpcom/ini-processor-factory;1"]. + getService(Ci.nsIINIParserFactory); +do_check_true(!!factory); + +/* ========== 1 - 15 ========== */ + +// Test reading from a variety of files. While we're at it, write out each one +// and read it back to ensure that nothing changed. +for (testnum = 1; testnum <= 15; testnum++) { + let filename = testdata[testnum -1].filename; + dump("INFO | test #" + testnum + ", filename " + filename + "\n"); + let parser = parserForFile(filename); + checkParserOutput(parser, testdata[testnum - 1].reference); + if (!parser) + continue; + do_check_true(parser instanceof Ci.nsIINIParserWriter); + // write contents out to a new file + let newfilename = filename + ".new"; + let newfile = do_get_file(filename); + newfile.leafName += ".new"; + parser.writeFile(newfile); + // read new file and make sure the contents are the same. + parser = parserForFile(newfilename); + checkParserOutput(parser, testdata[testnum - 1].reference); +} + +/* ========== 16 ========== */ + +// test writing to a new file. +let newfile = do_get_file("data/"); +newfile.append("non-existant-file.ini"); +if (newfile.exists()) + newfile.remove(false); +do_check_false(newfile.exists()); + +let parser = factory.createINIParser(newfile); +do_check_true(!!parser); +do_check_true(parser instanceof Ci.nsIINIParserWriter); +checkParserOutput(parser, {}); +parser.writeFile(); +do_check_true(newfile.exists()); + +// test adding a new section and new key +parser.setString("section", "key", "value"); +parser.writeFile(); +do_check_true(newfile.exists()); +checkParserOutput(parser, {section: {key: "value"} }); +// read it in again, check for same data. +parser = parserForFile("data/non-existant-file.ini"); +checkParserOutput(parser, {section: {key: "value"} }); + +/* ========== 17 ========== */ + +// test modifying a existing key's value (in an existing section) +parser = parserForFile("data/iniparser09.ini"); +checkParserOutput(parser, {section1: {name1: "value1"} }); + +do_check_true(parser instanceof Ci.nsIINIParserWriter); +parser.setString("section1", "name1", "value2"); +checkParserOutput(parser, {section1: {name1: "value2"} }); + +/* ========== 18 ========== */ + +// test trying to set illegal characters +let caughtError; +caughtError = false; +checkParserOutput(parser, {section1: {name1: "value2"} }); + +// Bad characters in section name +try { parser.SetString("bad\0", "ok", "ok"); } catch (e) { caughtError = true; } +do_check_true(caughtError); +caughtError = false; +try { parser.SetString("bad\r", "ok", "ok"); } catch (e) { caughtError = true; } +do_check_true(caughtError); +caughtError = false; +try { parser.SetString("bad\n", "ok", "ok"); } catch (e) { caughtError = true; } +do_check_true(caughtError); +caughtError = false; +try { parser.SetString("bad[", "ok", "ok"); } catch (e) { caughtError = true; } +do_check_true(caughtError); +caughtError = false; +try { parser.SetString("bad]", "ok", "ok"); } catch (e) { caughtError = true; } +do_check_true(caughtError); + +// Bad characters in key name +caughtError = false; +try { parser.SetString("ok", "bad\0", "ok"); } catch (e) { caughtError = true; } +do_check_true(caughtError); +caughtError = false; +try { parser.SetString("ok", "bad\r", "ok"); } catch (e) { caughtError = true; } +do_check_true(caughtError); +caughtError = false; +try { parser.SetString("ok", "bad\n", "ok"); } catch (e) { caughtError = true; } +do_check_true(caughtError); +caughtError = false; +try { parser.SetString("ok", "bad=", "ok"); } catch (e) { caughtError = true; } +do_check_true(caughtError); + +// Bad characters in value +caughtError = false; +try { parser.SetString("ok", "ok", "bad\0"); } catch (e) { caughtError = true; } +do_check_true(caughtError); +caughtError = false; +try { parser.SetString("ok", "ok", "bad\r"); } catch (e) { caughtError = true; } +do_check_true(caughtError); +caughtError = false; +try { parser.SetString("ok", "ok", "bad\n"); } catch (e) { caughtError = true; } +do_check_true(caughtError); +caughtError = false; +try { parser.SetString("ok", "ok", "bad="); } catch (e) { caughtError = true; } +do_check_true(caughtError); + +} catch(e) { + throw "FAILED in test #" + testnum + " -- " + e; +} +} diff -Nru firefox-3.6.3+nobinonly/mozilla/xpcom/threads/nsEnvironment.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpcom/threads/nsEnvironment.cpp --- firefox-3.6.3+nobinonly/mozilla/xpcom/threads/nsEnvironment.cpp 2010-04-02 16:59:21.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpcom/threads/nsEnvironment.cpp 2010-04-16 17:32:53.000000000 +0100 @@ -137,7 +137,7 @@ * vars. */ -typedef nsBaseHashtableET EnvEntryType; +typedef nsBaseHashtableET EnvEntryType; typedef nsTHashtable EnvHashType; static EnvHashType *gEnvHash = nsnull; @@ -178,7 +178,7 @@ return NS_ERROR_UNEXPECTED; } - EnvEntryType* entry = gEnvHash->PutEntry(nativeName); + EnvEntryType* entry = gEnvHash->PutEntry(nativeName.get()); if (!entry) { return NS_ERROR_OUT_OF_MEMORY; } diff -Nru firefox-3.6.3+nobinonly/mozilla/xpfe/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/xpfe/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/xpfe/Makefile.in 2010-04-02 16:59:22.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpfe/Makefile.in 2010-04-16 17:32:53.000000000 +0100 @@ -32,7 +32,7 @@ # and other provisions required by the GPL or the LGPL. If you do not delete # the provisions above, a recipient may use your version of this file under # the terms of any one of the MPL, the GPL or the LGPL. -# +# # ***** END LICENSE BLOCK ***** DEPTH = .. diff -Nru firefox-3.6.3+nobinonly/mozilla/xpinstall/src/nsJSInstallTriggerGlobal.cpp firefox-3.6.4+build1+nobinonly/mozilla/xpinstall/src/nsJSInstallTriggerGlobal.cpp --- firefox-3.6.3+nobinonly/mozilla/xpinstall/src/nsJSInstallTriggerGlobal.cpp 2010-04-02 16:59:22.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpinstall/src/nsJSInstallTriggerGlobal.cpp 2010-04-16 17:32:54.000000000 +0100 @@ -121,6 +121,36 @@ } } +static JSBool CreateNativeObject(JSContext *cx, JSObject *obj, nsIDOMInstallTriggerGlobal **aResult) +{ + nsresult result; + nsIScriptObjectOwner *owner = nsnull; + nsIDOMInstallTriggerGlobal *nativeThis; + + static NS_DEFINE_CID(kInstallTrigger_CID, + NS_SoftwareUpdateInstallTrigger_CID); + + result = CallCreateInstance(kInstallTrigger_CID, &nativeThis); + if (NS_FAILED(result)) return JS_FALSE; + + result = nativeThis->QueryInterface(NS_GET_IID(nsIScriptObjectOwner), + (void **)&owner); + + if (NS_OK != result) + { + NS_RELEASE(nativeThis); + return JS_FALSE; + } + + owner->SetScriptObject((void *)obj); + JS_SetPrivate(cx, obj, nativeThis); + + *aResult = nativeThis; + + NS_RELEASE(nativeThis); // we only want one refcnt. JSUtils cleans us up. + return JS_TRUE; +} + // // Helper function for URI verification // @@ -163,7 +193,12 @@ if (!JS_InstanceOf(cx, obj, &InstallTriggerGlobalClass, nsnull)) return nsnull; - return (nsIDOMInstallTriggerGlobal*)JS_GetPrivate(cx, obj); + nsIDOMInstallTriggerGlobal *native = (nsIDOMInstallTriggerGlobal*)JS_GetPrivate(cx, obj); + if (!native) { + // xpinstall script contexts delay creation of the native. + CreateNativeObject(cx, obj, &native); + } + return native; } // diff -Nru firefox-3.6.3+nobinonly/mozilla/xpinstall/tests/browser_bug540558.js firefox-3.6.4+build1+nobinonly/mozilla/xpinstall/tests/browser_bug540558.js --- firefox-3.6.3+nobinonly/mozilla/xpinstall/tests/browser_bug540558.js 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpinstall/tests/browser_bug540558.js 2010-04-16 17:32:54.000000000 +0100 @@ -0,0 +1,37 @@ +// Load in the test harness +var scriptLoader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"] + .getService(Components.interfaces.mozIJSSubScriptLoader); +scriptLoader.loadSubScript("chrome://mochikit/content/browser/xpinstall/tests/harness.js", this); + +// ---------------------------------------------------------------------------- +// Tests that calling InstallTrigger.installChrome works +function test() { + Harness.installEndedCallback = check_xpi_install; + Harness.installsCompletedCallback = finish_test; + Harness.setup(); + + var pm = Components.classes["@mozilla.org/permissionmanager;1"] + .getService(Components.interfaces.nsIPermissionManager); + pm.add(makeURI("http://example.com/"), "install", pm.ALLOW_ACTION); + + gBrowser.selectedTab = gBrowser.addTab(); + gBrowser.loadURI(TESTROOT + "bug540558.html"); +} + +function check_xpi_install(addon, status) { + is(status, 0, "Install should succeed"); +} + +function finish_test() { + var pm = Components.classes["@mozilla.org/permissionmanager;1"] + .getService(Components.interfaces.nsIPermissionManager); + pm.remove("example.com", "install"); + + var em = Components.classes["@mozilla.org/extensions/manager;1"] + .getService(Components.interfaces.nsIExtensionManager); + em.cancelInstallItem("unsigned-xpi@tests.mozilla.org"); + + gBrowser.removeCurrentTab(); + Harness.finish(); +} +// ---------------------------------------------------------------------------- diff -Nru firefox-3.6.3+nobinonly/mozilla/xpinstall/tests/bug540558.html firefox-3.6.4+build1+nobinonly/mozilla/xpinstall/tests/bug540558.html --- firefox-3.6.3+nobinonly/mozilla/xpinstall/tests/bug540558.html 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpinstall/tests/bug540558.html 2010-04-16 17:32:54.000000000 +0100 @@ -0,0 +1,23 @@ + + + + + + + +InstallTrigger tests + + + +

    InstallTrigger tests

    +

    +

    + + diff -Nru firefox-3.6.3+nobinonly/mozilla/xpinstall/tests/Makefile.in firefox-3.6.4+build1+nobinonly/mozilla/xpinstall/tests/Makefile.in --- firefox-3.6.3+nobinonly/mozilla/xpinstall/tests/Makefile.in 2010-04-02 16:59:22.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla/xpinstall/tests/Makefile.in 2010-04-16 17:32:54.000000000 +0100 @@ -83,6 +83,7 @@ browser_cancel.js \ browser_navigateaway.js \ browser_navigateaway2.js \ + browser_bug540558.js \ unsigned.xpi \ signed.xpi \ signed2.xpi \ @@ -98,6 +99,7 @@ installchrome.html \ authRedirect.sjs \ cookieRedirect.sjs \ + bug540558.html \ $(NULL) libs:: $(_BROWSER_FILES) Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla-1.9.2-3.6.3-source.tar.bz2 and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla-1.9.2-3.6.3-source.tar.bz2 differ diff -Nru firefox-3.6.3+nobinonly/mozilla-1.9.2-3.6.3-source.tar.bz2.cdbs-config_list firefox-3.6.4+build1+nobinonly/mozilla-1.9.2-3.6.3-source.tar.bz2.cdbs-config_list --- firefox-3.6.3+nobinonly/mozilla-1.9.2-3.6.3-source.tar.bz2.cdbs-config_list 2010-04-21 01:12:20.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla-1.9.2-3.6.3-source.tar.bz2.cdbs-config_list 1970-01-01 01:00:00.000000000 +0100 @@ -1,89 +0,0 @@ -mozilla/config/config.mk -mozilla/modules/freetype2/builds/unix/config.guess -mozilla/modules/freetype2/builds/unix/config.sub -mozilla/nsprpub/config/config.mk -mozilla/nsprpub/build/autoconf/config.guess -mozilla/nsprpub/build/autoconf/config.sub -mozilla/toolkit/crashreporter/google-breakpad/autotools/config.guess -mozilla/toolkit/crashreporter/google-breakpad/autotools/config.sub -mozilla/toolkit/crashreporter/google-breakpad/src/config.h.in -mozilla/toolkit/themes/winstripe/global/config.css -mozilla/toolkit/themes/pinstripe/global/config.css -mozilla/toolkit/components/viewconfig/content/config.xul -mozilla/toolkit/components/viewconfig/content/config.js -mozilla/toolkit/locales/en-US/chrome/global/config.dtd -mozilla/toolkit/locales/en-US/chrome/global/config.properties -mozilla/tools/leaky/config.h -mozilla/tools/jprof/stub/config.h -mozilla/security/nss/tests/pkcs11/netscape/trivial/config.h.in -mozilla/security/nss/tests/pkcs11/netscape/suites/config.mk -mozilla/security/nss/tests/pkcs11/netscape/suites/security/pkcs11/config.mk -mozilla/security/nss/tests/pkcs11/netscape/suites/security/config.mk -mozilla/security/nss/tests/pkcs11/netscape/suites/security/ssl/config.mk -mozilla/security/nss/lib/sqlite/config.mk -mozilla/security/nss/lib/jar/config.mk -mozilla/security/nss/lib/pki/config.mk -mozilla/security/nss/lib/pkcs7/config.mk -mozilla/security/nss/lib/nss/config.mk -mozilla/security/nss/lib/pk11wrap/config.mk -mozilla/security/nss/lib/util/config.mk -mozilla/security/nss/lib/pkcs12/config.mk -mozilla/security/nss/lib/freebl/config.mk -mozilla/security/nss/lib/zlib/config.mk -mozilla/security/nss/lib/crmf/config.mk -mozilla/security/nss/lib/cryptohi/config.mk -mozilla/security/nss/lib/ckfw/capi/config.mk -mozilla/security/nss/lib/ckfw/builtins/config.mk -mozilla/security/nss/lib/ckfw/dbm/config.mk -mozilla/security/nss/lib/ckfw/config.mk -mozilla/security/nss/lib/ckfw/nssmkey/config.mk -mozilla/security/nss/lib/certhigh/config.mk -mozilla/security/nss/lib/dev/config.mk -mozilla/security/nss/lib/base/config.mk -mozilla/security/nss/lib/pki1/config.mk -mozilla/security/nss/lib/softoken/config.mk -mozilla/security/nss/lib/softoken/legacydb/config.mk -mozilla/security/nss/lib/ssl/config.mk -mozilla/security/nss/lib/smime/config.mk -mozilla/security/nss/lib/libpkix/pkix_pl_nss/pki/config.mk -mozilla/security/nss/lib/libpkix/pkix_pl_nss/module/config.mk -mozilla/security/nss/lib/libpkix/pkix_pl_nss/config.mk -mozilla/security/nss/lib/libpkix/pkix_pl_nss/system/config.mk -mozilla/security/nss/lib/libpkix/pkix/checker/config.mk -mozilla/security/nss/lib/libpkix/pkix/certsel/config.mk -mozilla/security/nss/lib/libpkix/pkix/results/config.mk -mozilla/security/nss/lib/libpkix/pkix/util/config.mk -mozilla/security/nss/lib/libpkix/pkix/store/config.mk -mozilla/security/nss/lib/libpkix/pkix/crlsel/config.mk -mozilla/security/nss/lib/libpkix/pkix/config.mk -mozilla/security/nss/lib/libpkix/pkix/params/config.mk -mozilla/security/nss/lib/libpkix/pkix/top/config.mk -mozilla/security/nss/lib/libpkix/include/config.mk -mozilla/security/nss/lib/libpkix/config.mk -mozilla/security/nss/lib/sysinit/config.mk -mozilla/security/nss/lib/certdb/config.mk -mozilla/security/nss/cmd/crmf-cgi/config.mk -mozilla/security/nss/cmd/crmftest/config.mk -mozilla/security/nss/cmd/lib/config.mk -mozilla/security/nss/cmd/libpkix/testutil/config.mk -mozilla/security/nss/cmd/libpkix/config.mk -mozilla/security/dbm/config/config.mk -mozilla/security/dbm/src/config.mk -mozilla/security/coreconf/config.mk -mozilla/media/liboggplay/src/liboggplay/config.h -mozilla/media/libfishsound/src/libfishsound/config.h -mozilla/media/libfishsound/include/fishsound/config.h -mozilla/media/liboggz/include/oggz/config.h -mozilla/media/libtheora/include/theora/config.h -mozilla/media/libtheora/lib/config.h -mozilla/js/src/tracevis/config.py -mozilla/js/src/config/config.mk -mozilla/js/src/tests/config.sh -mozilla/js/src/tests/config.mk -mozilla/js/src/config.mk -mozilla/js/src/build/autoconf/config.guess -mozilla/js/src/build/autoconf/config.sub -mozilla/js/ctypes/libffi/config.guess -mozilla/js/ctypes/libffi/config.sub -mozilla/build/autoconf/config.guess -mozilla/build/autoconf/config.sub Binary files /tmp/FgQiTzwYv7/firefox-3.6.3+nobinonly/mozilla-1.9.2-3.6.4+build1-source.tar.bz2 and /tmp/lA0KqPjFg2/firefox-3.6.4+build1+nobinonly/mozilla-1.9.2-3.6.4+build1-source.tar.bz2 differ diff -Nru firefox-3.6.3+nobinonly/mozilla-1.9.2-3.6.4+build1-source.tar.bz2.cdbs-config_list firefox-3.6.4+build1+nobinonly/mozilla-1.9.2-3.6.4+build1-source.tar.bz2.cdbs-config_list --- firefox-3.6.3+nobinonly/mozilla-1.9.2-3.6.4+build1-source.tar.bz2.cdbs-config_list 1970-01-01 01:00:00.000000000 +0100 +++ firefox-3.6.4+build1+nobinonly/mozilla-1.9.2-3.6.4+build1-source.tar.bz2.cdbs-config_list 2010-04-21 01:12:45.000000000 +0100 @@ -0,0 +1,94 @@ +mozilla/config/config.mk +mozilla/modules/freetype2/builds/unix/config.guess +mozilla/modules/freetype2/builds/unix/config.sub +mozilla/nsprpub/config/config.mk +mozilla/nsprpub/build/autoconf/config.guess +mozilla/nsprpub/build/autoconf/config.sub +mozilla/toolkit/crashreporter/google-breakpad/autotools/config.guess +mozilla/toolkit/crashreporter/google-breakpad/autotools/config.sub +mozilla/toolkit/crashreporter/google-breakpad/src/config.h.in +mozilla/toolkit/themes/winstripe/global/config.css +mozilla/toolkit/themes/pinstripe/global/config.css +mozilla/toolkit/components/viewconfig/content/config.xul +mozilla/toolkit/components/viewconfig/content/config.js +mozilla/toolkit/locales/en-US/chrome/global/config.dtd +mozilla/toolkit/locales/en-US/chrome/global/config.properties +mozilla/tools/leaky/config.h +mozilla/tools/jprof/stub/config.h +mozilla/ipc/chromium/src/third_party/libevent/config.h.in +mozilla/ipc/chromium/src/third_party/libevent/config.guess +mozilla/ipc/chromium/src/third_party/libevent/linux/config.h +mozilla/ipc/chromium/src/third_party/libevent/mac/config.h +mozilla/ipc/chromium/src/third_party/libevent/config.sub +mozilla/security/nss/tests/pkcs11/netscape/trivial/config.h.in +mozilla/security/nss/tests/pkcs11/netscape/suites/config.mk +mozilla/security/nss/tests/pkcs11/netscape/suites/security/pkcs11/config.mk +mozilla/security/nss/tests/pkcs11/netscape/suites/security/config.mk +mozilla/security/nss/tests/pkcs11/netscape/suites/security/ssl/config.mk +mozilla/security/nss/lib/sqlite/config.mk +mozilla/security/nss/lib/jar/config.mk +mozilla/security/nss/lib/pki/config.mk +mozilla/security/nss/lib/pkcs7/config.mk +mozilla/security/nss/lib/nss/config.mk +mozilla/security/nss/lib/pk11wrap/config.mk +mozilla/security/nss/lib/util/config.mk +mozilla/security/nss/lib/pkcs12/config.mk +mozilla/security/nss/lib/freebl/config.mk +mozilla/security/nss/lib/zlib/config.mk +mozilla/security/nss/lib/crmf/config.mk +mozilla/security/nss/lib/cryptohi/config.mk +mozilla/security/nss/lib/ckfw/capi/config.mk +mozilla/security/nss/lib/ckfw/builtins/config.mk +mozilla/security/nss/lib/ckfw/dbm/config.mk +mozilla/security/nss/lib/ckfw/config.mk +mozilla/security/nss/lib/ckfw/nssmkey/config.mk +mozilla/security/nss/lib/certhigh/config.mk +mozilla/security/nss/lib/dev/config.mk +mozilla/security/nss/lib/base/config.mk +mozilla/security/nss/lib/pki1/config.mk +mozilla/security/nss/lib/softoken/config.mk +mozilla/security/nss/lib/softoken/legacydb/config.mk +mozilla/security/nss/lib/ssl/config.mk +mozilla/security/nss/lib/smime/config.mk +mozilla/security/nss/lib/libpkix/pkix_pl_nss/pki/config.mk +mozilla/security/nss/lib/libpkix/pkix_pl_nss/module/config.mk +mozilla/security/nss/lib/libpkix/pkix_pl_nss/config.mk +mozilla/security/nss/lib/libpkix/pkix_pl_nss/system/config.mk +mozilla/security/nss/lib/libpkix/pkix/checker/config.mk +mozilla/security/nss/lib/libpkix/pkix/certsel/config.mk +mozilla/security/nss/lib/libpkix/pkix/results/config.mk +mozilla/security/nss/lib/libpkix/pkix/util/config.mk +mozilla/security/nss/lib/libpkix/pkix/store/config.mk +mozilla/security/nss/lib/libpkix/pkix/crlsel/config.mk +mozilla/security/nss/lib/libpkix/pkix/config.mk +mozilla/security/nss/lib/libpkix/pkix/params/config.mk +mozilla/security/nss/lib/libpkix/pkix/top/config.mk +mozilla/security/nss/lib/libpkix/include/config.mk +mozilla/security/nss/lib/libpkix/config.mk +mozilla/security/nss/lib/sysinit/config.mk +mozilla/security/nss/lib/certdb/config.mk +mozilla/security/nss/cmd/crmf-cgi/config.mk +mozilla/security/nss/cmd/crmftest/config.mk +mozilla/security/nss/cmd/lib/config.mk +mozilla/security/nss/cmd/libpkix/testutil/config.mk +mozilla/security/nss/cmd/libpkix/config.mk +mozilla/security/dbm/config/config.mk +mozilla/security/dbm/src/config.mk +mozilla/security/coreconf/config.mk +mozilla/media/liboggplay/src/liboggplay/config.h +mozilla/media/libfishsound/src/libfishsound/config.h +mozilla/media/libfishsound/include/fishsound/config.h +mozilla/media/liboggz/include/oggz/config.h +mozilla/media/libtheora/include/theora/config.h +mozilla/media/libtheora/lib/config.h +mozilla/js/src/tracevis/config.py +mozilla/js/src/config/config.mk +mozilla/js/src/tests/config.sh +mozilla/js/src/tests/config.mk +mozilla/js/src/config.mk +mozilla/js/src/build/autoconf/config.guess +mozilla/js/src/build/autoconf/config.sub +mozilla/js/ctypes/libffi/config.guess +mozilla/js/ctypes/libffi/config.sub +mozilla/build/autoconf/config.guess +mozilla/build/autoconf/config.sub